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

dither-main.c

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

Generated on Thu Feb 10 19:29:30 2005 for libgutenprint API Reference by  doxygen 1.4.1