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 <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
00045
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
00064
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
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
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
00370
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 }