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 };
00057
00058 static const int num_dither_algos = sizeof(dither_algos)/sizeof(stpi_dither_algorithm_t);
00059
00060
00061
00062
00063
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
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
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
00361
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 }