00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
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
00045
00046 { "None", N_ ("Default"), -1 },
00047 { "EvenTone", N_ ("EvenTone"), D_EVENTONE },
00048 { "HybridEvenTone", N_ ("Hybrid EvenTone"), D_HYBRID_EVENTONE },
00049
00050
00051
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
00070
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
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
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
00376
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 }