Main Page   Modules   Alphabetical List   Data Structures   File List   Data Fields   Globals  

src/main/dither-main.c

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

Generated on Wed May 12 20:21:28 2004 for libgimpprint API Reference by doxygen1.2.17