libgimpprint API Reference: src/main/dither-main.c Source File

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_8c-source.html,v 1.1.1.2 2004/12/22 23:48:58 jlovell 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 }