Main Page | Modules | Alphabetical List | Data Structures | File List | Data Fields | Globals | Related Pages

src/main/dither-main.c

Go to the documentation of this file.
00001 /*
00002  * "$Id: dither-main.c,v 1.47 2004/07/24 02:22:30 rlk Exp $"
00003  *
00004  *   Dither routine entrypoints
00005  *
00006  *   Copyright 1997-2003 Michael Sweet (mike@easysw.com) and
00007  *      Robert Krawitz (rlk@alum.mit.edu)
00008  *
00009  *   This program is free software; you can redistribute it and/or modify it
00010  *   under the terms of the GNU General Public License as published by the Free
00011  *   Software Foundation; either version 2 of the License, or (at your option)
00012  *   any later version.
00013  *
00014  *   This program is distributed in the hope that it will be useful, but
00015  *   WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
00016  *   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
00017  *   for more details.
00018  *
00019  *   You should have received a copy of the GNU General Public License
00020  *   along with this program; if not, write to the Free Software
00021  *   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00022  *
00023  * Revision History:
00024  *
00025  *   See ChangeLog
00026  */
00027 
00028 #ifdef HAVE_CONFIG_H
00029 #include <config.h>
00030 #endif
00031 #include <gimp-print/gimp-print.h>
00032 #include "gimp-print-internal.h"
00033 #include <gimp-print/gimp-print-intl-internal.h>
00034 #ifdef HAVE_LIMITS_H
00035 #include <limits.h>
00036 #endif
00037 #include <math.h>
00038 #include <string.h>
00039 #include "dither-impl.h"
00040 #include "generic-options.h"
00041 
00042 static const stpi_dither_algorithm_t dither_algos[] =
00043 {
00044   /* Note to translators: "EvenTone" is the proper name, rather than a */
00045   /* descriptive name, of this algorithm. */
00046   { "None",           N_ ("Default"),                -1 },
00047   { "EvenTone",       N_ ("EvenTone"),               D_EVENTONE },
00048   { "HybridEvenTone", N_ ("Hybrid EvenTone"),        D_HYBRID_EVENTONE },
00049   { "UniTone",        N_ ("UniTone"),                D_UNITONE },
00050   { "HybridUniTone",  N_ ("Hybrid UniTone"),         D_HYBRID_UNITONE },
00051   { "Adaptive",       N_ ("Adaptive Hybrid"),        D_ADAPTIVE_HYBRID },
00052   { "Ordered",        N_ ("Ordered"),                D_ORDERED },
00053   { "Fast",           N_ ("Fast"),                   D_FAST },
00054   { "VeryFast",       N_ ("Very Fast"),              D_VERY_FAST },
00055   { "Floyd",          N_ ("Hybrid Floyd-Steinberg"), D_FLOYD_HYBRID },
00056   { "Predithered",    N_ ("Predithered Input"),      D_PREDITHERED }
00057 };
00058 
00059 static const int num_dither_algos = sizeof(dither_algos)/sizeof(stpi_dither_algorithm_t);
00060 
00061 
00062 /*
00063  * Bayer's dither matrix using Judice, Jarvis, and Ninke recurrence relation
00064  * http://www.cs.rit.edu/~sxc7922/Project/CRT.htm
00065  */
00066 
00067 static const unsigned sq2[] =
00068 {
00069   0, 2,
00070   3, 1
00071 };
00072 
00073 static const stp_parameter_t dither_parameters[] =
00074 {
00075   {
00076     "Density", N_("Density"), N_("Output Level Adjustment"),
00077     N_("Adjust the density (amount of ink) of the print. "
00078        "Reduce the density if the ink bleeds through the "
00079        "paper or smears; increase the density if black "
00080        "regions are not solid."),
00081     STP_PARAMETER_TYPE_DOUBLE, STP_PARAMETER_CLASS_OUTPUT,
00082     STP_PARAMETER_LEVEL_ADVANCED, 0, 1, -1, 1, 0
00083   },
00084   {
00085     "DitherAlgorithm", N_("Dither Algorithm"), N_("Screening Adjustment"),
00086     N_("Choose the dither algorithm to be used.\n"
00087        "Adaptive Hybrid usually produces the best all-around quality.\n"
00088        "EvenTone is a new, experimental algorithm that often produces excellent results.\n"
00089        "Ordered is faster and produces almost as good quality on photographs.\n"
00090        "Fast and Very Fast are considerably faster, and work well for text and line art.\n"
00091        "Hybrid Floyd-Steinberg generally produces inferior output."),
00092     STP_PARAMETER_TYPE_STRING_LIST, STP_PARAMETER_CLASS_OUTPUT,
00093     STP_PARAMETER_LEVEL_ADVANCED, 1, 1, -1, 1, 0
00094   },
00095 };
00096 
00097 static const int dither_parameter_count =
00098 sizeof(dither_parameters) / sizeof(const stp_parameter_t);
00099 
00100 stp_parameter_list_t
00101 stp_dither_list_parameters(const stp_vars_t *v)
00102 {
00103   stp_parameter_list_t *ret = stp_parameter_list_create();
00104   int i;
00105   for (i = 0; i < dither_parameter_count; i++)
00106     stp_parameter_list_add_param(ret, &(dither_parameters[i]));
00107   return ret;
00108 }
00109 
00110 void
00111 stp_dither_describe_parameter(const stp_vars_t *v, const char *name,
00112                               stp_parameter_t *description)
00113 {
00114   int i;
00115   description->p_type = STP_PARAMETER_TYPE_INVALID;
00116   if (name == NULL)
00117     return;
00118   description->deflt.str = NULL;
00119   if (strcmp(name, "Density") == 0)
00120     {
00121       stp_fill_parameter_settings(description, &(dither_parameters[0]));
00122       description->bounds.dbl.upper = 8.0;
00123       description->bounds.dbl.lower = 0.1;
00124       description->deflt.dbl = 1.0;
00125     }
00126   else if (strcmp(name, "DitherAlgorithm") == 0)
00127     {
00128       stp_fill_parameter_settings(description, &(dither_parameters[1]));
00129       if (stp_check_string_parameter(v, "Quality", STP_PARAMETER_ACTIVE) &&
00130           stpi_get_quality_by_name(stp_get_string_parameter(v, "Quality")))
00131         description->is_active = 0;
00132       else
00133         {
00134           description->bounds.str = stp_string_list_create();
00135           for (i = 0; i < num_dither_algos; i++)
00136             {
00137               const stpi_dither_algorithm_t *dt = &dither_algos[i];
00138               stp_string_list_add_string(description->bounds.str,
00139                                          dt->name, dt->text);
00140             }
00141           description->deflt.str =
00142             stp_string_list_param(description->bounds.str, 0)->name;
00143         }
00144     }
00145   else
00146     return;
00147   if (stp_check_string_parameter(v, "Quality", STP_PARAMETER_ACTIVE) &&
00148       stpi_get_quality_by_name(stp_get_string_parameter(v, "Quality")))
00149     description->is_active = 0;
00150   else if (stp_check_string_parameter(v, "ImageType", STP_PARAMETER_ACTIVE) &&
00151            strcmp(stp_get_string_parameter(v, "ImageType"), "None") != 0 &&
00152            description->p_level > STP_PARAMETER_LEVEL_BASIC)
00153     description->is_active = 0;
00154 }
00155 
00156 #define RETURN_DITHERFUNC(func, v)                                      \
00157 do                                                                      \
00158 {                                                                       \
00159   stp_dprintf(STP_DBG_COLORFUNC, v, "ditherfunc %s\n", #func);  \
00160   return (func);                                                        \
00161 } while (0)
00162 
00163 static stpi_ditherfunc_t *
00164 stpi_set_dither_function(stp_vars_t *v)
00165 {
00166   const stpi_quality_t *quality = NULL;
00167   const char *image_type = stp_get_string_parameter(v, "ImageType");
00168   const char *color_correction = stp_get_string_parameter(v,"ColorCorrection");
00169   stpi_dither_t *d = (stpi_dither_t *) stp_get_component_data(v, "Dither");
00170   int i;
00171   const char *algorithm = stp_get_string_parameter(v, "DitherAlgorithm");
00172   d->stpi_dither_type = -1;
00173   if (stp_check_string_parameter(v, "Quality", STP_PARAMETER_ACTIVE))
00174     quality = stpi_get_quality_by_name(stp_get_string_parameter(v, "Quality"));
00175 
00176   if (color_correction)
00177     {
00178       if (strcmp(color_correction, "Predithered") == 0)
00179         d->stpi_dither_type = D_PREDITHERED;
00180     }
00181   if (image_type && d->stpi_dither_type == -1)
00182     {
00183       if (strcmp(image_type, "Text") == 0)
00184         d->stpi_dither_type = D_VERY_FAST;
00185     }
00186   if (quality && d->stpi_dither_type == -1)
00187     {
00188       switch (quality->quality_level)
00189         {
00190         case 0:
00191         case 1:
00192           d->stpi_dither_type = D_VERY_FAST;
00193           break;
00194         case 2:
00195         case 3:
00196           if (image_type && strcmp(image_type, "LineArt") == 0)
00197             d->stpi_dither_type = D_VERY_FAST;
00198           else
00199             d->stpi_dither_type = D_FAST;
00200           break;
00201         case 4:
00202           if (image_type &&
00203               (strcmp(image_type, "LineArt") == 0 ||
00204                strcmp(image_type, "TextGraphics") == 0))
00205             d->stpi_dither_type = D_ADAPTIVE_HYBRID;
00206           else
00207             d->stpi_dither_type = D_ORDERED;
00208           break;
00209         case 5:
00210           if (image_type &&
00211               (strcmp(image_type, "LineArt") == 0 ||
00212                strcmp(image_type, "TextGraphics") == 0))
00213             d->stpi_dither_type = D_HYBRID_EVENTONE;
00214           else if (image_type && (strcmp(image_type, "Photo") == 0))
00215             d->stpi_dither_type = D_EVENTONE;
00216           else
00217             d->stpi_dither_type = D_ORDERED;
00218           break;
00219         case 6:
00220         case 7:
00221         case 8:
00222         case 9:
00223         case 10:
00224         default:
00225           if (image_type &&
00226               (strcmp(image_type, "LineArt") == 0 ||
00227                strcmp(image_type, "TextGraphics") == 0))
00228             d->stpi_dither_type = D_HYBRID_EVENTONE;
00229           else
00230             d->stpi_dither_type = D_EVENTONE;
00231           break;
00232         }
00233       /* EvenTone performs poorly if the aspect ratio is greater than 2 */
00234       if ((d->stpi_dither_type & (D_EVENTONE | D_UNITONE)) &&
00235           (d->x_aspect > 2 || d->y_aspect > 2))
00236         d->stpi_dither_type = D_ADAPTIVE_HYBRID;
00237     }
00238   else if (algorithm)
00239     {
00240       for (i = 0; i < num_dither_algos; i++)
00241         {
00242           if (!strcmp(algorithm, _(dither_algos[i].name)))
00243             {
00244               d->stpi_dither_type = dither_algos[i].id;
00245               break;
00246             }
00247         }
00248       if (d->stpi_dither_type == -1)
00249         {
00250           d->stpi_dither_type = D_EVENTONE;
00251           /* EvenTone performs poorly if the aspect ratio is greater than 2 */
00252           if ((d->stpi_dither_type & (D_EVENTONE | D_UNITONE)) &&
00253               (d->x_aspect > 2 || d->y_aspect > 2))
00254             d->stpi_dither_type = D_ADAPTIVE_HYBRID;
00255         }       
00256     }
00257   switch (d->stpi_dither_type)
00258     {
00259     case D_PREDITHERED:
00260       RETURN_DITHERFUNC(stpi_dither_predithered, v);
00261     case D_VERY_FAST:
00262       RETURN_DITHERFUNC(stpi_dither_very_fast, v);
00263     case D_ORDERED:
00264     case D_FAST:
00265       RETURN_DITHERFUNC(stpi_dither_ordered, v);
00266     case D_HYBRID_EVENTONE:
00267     case D_EVENTONE:
00268       RETURN_DITHERFUNC(stpi_dither_et, v);
00269     case D_HYBRID_UNITONE:
00270     case D_UNITONE:
00271       RETURN_DITHERFUNC(stpi_dither_ut, v);
00272     default:
00273       RETURN_DITHERFUNC(stpi_dither_ed, v);
00274     }
00275 }
00276 
00277 void
00278 stp_dither_set_adaptive_limit(stp_vars_t *v, double limit)
00279 {
00280   stpi_dither_t *d = (stpi_dither_t *) stp_get_component_data(v, "Dither");
00281   d->adaptive_limit = limit;
00282 }
00283 
00284 void
00285 stp_dither_set_ink_spread(stp_vars_t *v, int spread)
00286 {
00287   stpi_dither_t *d = (stpi_dither_t *) stp_get_component_data(v, "Dither");
00288   STP_SAFE_FREE(d->offset0_table);
00289   STP_SAFE_FREE(d->offset1_table);
00290   if (spread >= 16)
00291     {
00292       d->spread = 16;
00293     }
00294   else
00295     {
00296       int max_offset;
00297       int i;
00298       d->spread = spread;
00299       max_offset = (1 << (16 - spread)) + 1;
00300       d->offset0_table = stp_malloc(sizeof(int) * max_offset);
00301       d->offset1_table = stp_malloc(sizeof(int) * max_offset);
00302       for (i = 0; i < max_offset; i++)
00303         {
00304           d->offset0_table[i] = (i + 1) * (i + 1);
00305           d->offset1_table[i] = ((i + 1) * i) / 2;
00306         }
00307     }
00308   d->spread_mask = (1 << d->spread) - 1;
00309 }
00310 
00311 void
00312 stp_dither_set_randomizer(stp_vars_t *v, int i, double val)
00313 {
00314   stpi_dither_t *d = (stpi_dither_t *) stp_get_component_data(v, "Dither");
00315   if (i < 0 || i >= CHANNEL_COUNT(d))
00316     return;
00317   CHANNEL(d, i).randomizer = val * 65535;
00318 }
00319 
00320 static void
00321 stpi_dither_free(void *vd)
00322 {
00323   stpi_dither_t *d = (stpi_dither_t *) vd;
00324   int j;
00325   if (d->aux_freefunc)
00326     (d->aux_freefunc)(d);
00327   for (j = 0; j < CHANNEL_COUNT(d); j++)
00328     stpi_dither_channel_destroy(&(CHANNEL(d, j)));
00329   STP_SAFE_FREE(d->offset0_table);
00330   STP_SAFE_FREE(d->offset1_table);
00331   stp_dither_matrix_destroy(&(d->dither_matrix));
00332   stp_dither_matrix_destroy(&(d->transition_matrix));
00333   stp_free(d->channel);
00334   stp_free(d->channel_index);
00335   stp_free(d->subchannel_count);
00336   stp_free(d);
00337 }
00338 
00339 void
00340 stp_dither_init(stp_vars_t *v, stp_image_t *image, int out_width,
00341                 int xdpi, int ydpi)
00342 {
00343   int in_width = stp_image_width(image);
00344   stpi_dither_t *d = stp_zalloc(sizeof(stpi_dither_t));
00345 
00346   stp_allocate_component_data(v, "Dither", NULL, stpi_dither_free, d);
00347 
00348   d->finalized = 0;
00349   d->error_rows = ERROR_ROWS;
00350   d->d_cutoff = 4096;
00351 
00352   d->offset0_table = NULL;
00353   d->offset1_table = NULL;
00354   if (xdpi > ydpi)
00355     {
00356       d->x_aspect = 1;
00357       d->y_aspect = xdpi / ydpi;
00358     }
00359   else
00360     {
00361       d->x_aspect = ydpi / xdpi;
00362       d->y_aspect = 1;
00363     }
00364   d->ditherfunc = stpi_set_dither_function(v);
00365   d->transition = 1.0;
00366   d->adaptive_limit = .75 * 65535;
00367 
00368   /*
00369    * For hybrid EvenTone we want to use the good matrix.  For regular
00370    * EvenTone, we don't need to pay the cost.
00371    */
00372   
00373   if (d->stpi_dither_type == D_VERY_FAST || d->stpi_dither_type ==D_EVENTONE ||
00374       d->stpi_dither_type == D_FAST || d->stpi_dither_type == D_PREDITHERED)
00375     {
00376       if (stp_check_int_parameter(v, "DitherVeryFastSteps",
00377                                   STP_PARAMETER_ACTIVE))
00378         stp_dither_set_iterated_matrix
00379           (v, 2, stp_get_int_parameter(v, "DitherVeryFastSteps"), sq2, 0, 2,4);
00380       else
00381         stp_dither_set_iterated_matrix(v, 2, DITHER_FAST_STEPS, sq2, 0, 2, 4);
00382     }
00383   else if (stp_check_array_parameter(v, "DitherMatrix",
00384                                      STP_PARAMETER_ACTIVE) &&
00385            (stp_dither_matrix_validate_array
00386             (stp_get_array_parameter(v, "DitherMatrix"))))
00387     {
00388       stp_dither_set_matrix_from_dither_array
00389         (v, stp_get_array_parameter(v, "DitherMatrix"), 0);
00390     }
00391   else
00392     {
00393       stp_array_t *array;
00394       int transposed;
00395         array = stp_find_standard_dither_array(d->y_aspect, d->x_aspect);
00396       transposed = d->y_aspect < d->x_aspect ? 1 : 0;
00397       if (array)
00398         {
00399           stp_dither_set_matrix_from_dither_array(v, array, transposed);
00400           stp_array_destroy(array);
00401         }
00402       else
00403         {
00404           stp_eprintf(v, "Cannot find dither matrix file!  Aborting.\n");
00405           stp_abort();
00406         }
00407     }
00408   stp_dither_set_transition(v, 0.7);
00409 
00410   d->src_width = in_width;
00411   d->dst_width = out_width;
00412 
00413   stp_dither_set_ink_spread(v, 13);
00414   d->channel_count = 0;
00415 }
00416 
00417 void
00418 stpi_dither_reverse_row_ends(stpi_dither_t *d)
00419 {
00420   int i;
00421   for (i = 0; i < CHANNEL_COUNT(d); i++)
00422     {
00423       int tmp = CHANNEL(d, i).row_ends[0];
00424       CHANNEL(d, i).row_ends[0] =
00425         CHANNEL(d, i).row_ends[1];
00426       CHANNEL(d, i).row_ends[1] = tmp;
00427     }
00428 }
00429 
00430 int
00431 stp_dither_get_first_position(stp_vars_t *v, int color, int subchannel)
00432 {
00433   stpi_dither_t *d = (stpi_dither_t *) stp_get_component_data(v, "Dither");
00434   int channel = stpi_dither_translate_channel(v, color, subchannel);
00435   if (channel < 0)
00436     return -1;
00437   return CHANNEL(d, channel).row_ends[0];
00438 }
00439 
00440 int
00441 stp_dither_get_last_position(stp_vars_t *v, int color, int subchannel)
00442 {
00443   stpi_dither_t *d = (stpi_dither_t *) stp_get_component_data(v, "Dither");
00444   int channel = stpi_dither_translate_channel(v, color, subchannel);
00445   if (channel < 0)
00446     return -1;
00447   return CHANNEL(d, channel).row_ends[1];
00448 }
00449 
00450 int *
00451 stpi_dither_get_errline(stpi_dither_t *d, int row, int color)
00452 {
00453   stpi_dither_channel_t *dc;
00454   if (row < 0 || color < 0 || color >= CHANNEL_COUNT(d))
00455     return NULL;
00456   dc = &(CHANNEL(d, color));
00457   if (!dc->errs)
00458     dc->errs = stp_zalloc(d->error_rows * sizeof(int *));
00459   if (!dc->errs[row % dc->error_rows])
00460     {
00461       int size = 2 * MAX_SPREAD + (16 * ((d->dst_width + 7) / 8));
00462       dc->errs[row % dc->error_rows] = stp_zalloc(size * sizeof(int));
00463     }
00464   return dc->errs[row % dc->error_rows] + MAX_SPREAD;
00465 }
00466 
00467 void
00468 stp_dither_internal(stp_vars_t *v, int row, const unsigned short *input,
00469                     int duplicate_line, int zero_mask,
00470                     const unsigned char *mask)
00471 {
00472   int i;
00473   stpi_dither_t *d = (stpi_dither_t *) stp_get_component_data(v, "Dither");
00474   stpi_dither_finalize(v);
00475   stp_dither_matrix_set_row(&(d->dither_matrix), row);
00476   stp_dither_matrix_set_row(&(d->transition_matrix), row);
00477   for (i = 0; i < CHANNEL_COUNT(d); i++)
00478     {
00479       CHANNEL(d, i).ptr = CHANNEL(d, i).ptr;
00480       if (CHANNEL(d, i).ptr)
00481           memset(CHANNEL(d, i).ptr, 0,
00482                  (d->dst_width + 7) / 8 * CHANNEL(d, i).signif_bits);
00483       CHANNEL(d, i).row_ends[0] = -1;
00484       CHANNEL(d, i).row_ends[1] = -1;
00485 
00486       stp_dither_matrix_set_row(&(CHANNEL(d, i).dithermat), row);
00487       stp_dither_matrix_set_row(&(CHANNEL(d, i).pick), row);
00488     }
00489   d->ptr_offset = 0;
00490   (d->ditherfunc)(v, row, input, duplicate_line, zero_mask, mask);
00491 }
00492 
00493 void
00494 stp_dither(stp_vars_t *v, int row, int duplicate_line, int zero_mask,
00495            const unsigned char *mask)
00496 {
00497   const unsigned short *input = stp_channel_get_output(v);
00498   stp_dither_internal(v, row, input, duplicate_line, zero_mask, mask);
00499 }

Generated on Wed Aug 25 07:56:13 2004 for libgimpprint API Reference by doxygen 1.3.6