/* * * Print plug-in driver utility functions for the GIMP. * * Copyright 1997-2000 Michael Sweet (mike@easysw.com) and * Robert Krawitz (rlk@alum.mit.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Revision History: * * See ChangeLog */ #ifdef HAVE_CONFIG_H #include #endif #include #include "gutenprint-internal.h" #include #ifdef HAVE_LIMITS_H #include #endif #include #include #include "dither-impl.h" int stpi_dither_translate_channel(stp_vars_t *v, unsigned channel, unsigned subchannel) { stpi_dither_t *d = (stpi_dither_t *) stp_get_component_data(v, "Dither"); unsigned chan_idx; if (!d) return -1; if (channel >= d->channel_count) return -1; if (subchannel >= d->subchannel_count[channel]) return -1; chan_idx = d->channel_index[channel]; return chan_idx + subchannel; } unsigned char * stp_dither_get_channel(stp_vars_t *v, unsigned channel, unsigned subchannel) { stpi_dither_t *d = (stpi_dither_t *) stp_get_component_data(v, "Dither"); int place = stpi_dither_translate_channel(v, channel, subchannel); if (place >= 0) return d->channel[place].ptr; else return NULL; } static void insert_channel(stp_vars_t *v, stpi_dither_t *d, int channel) { unsigned oc = d->channel_count; int i; d->channel_index = stp_realloc (d->channel_index, sizeof(unsigned) * (channel + 1)); d->subchannel_count = stp_realloc (d->subchannel_count, sizeof(unsigned) * (channel + 1)); for (i = oc; i < channel + 1; i++) { if (oc == 0) d->channel_index[i] = 0; else d->channel_index[i] = d->channel_index[oc - 1] + d->subchannel_count[oc - 1]; d->subchannel_count[i] = 0; } d->channel_count = channel + 1; } void stpi_dither_channel_destroy(stpi_dither_channel_t *channel) { int i; STP_SAFE_FREE(channel->ink_list); if (channel->errs) { for (i = 0; i < channel->error_rows; i++) STP_SAFE_FREE(channel->errs[i]); STP_SAFE_FREE(channel->errs); } STP_SAFE_FREE(channel->ranges); stp_dither_matrix_destroy(&(channel->pick)); stp_dither_matrix_destroy(&(channel->dithermat)); } static void initialize_channel(stp_vars_t *v, int channel, int subchannel) { stpi_dither_t *d = (stpi_dither_t *) stp_get_component_data(v, "Dither"); int idx = stpi_dither_translate_channel(v, channel, subchannel); stpi_dither_channel_t *dc = &(CHANNEL(d, idx)); stp_shade_t shade; stp_dotsize_t dot; STPI_ASSERT(idx >= 0, NULL); memset(dc, 0, sizeof(stpi_dither_channel_t)); stp_dither_matrix_clone(&(d->dither_matrix), &(dc->dithermat), 0, 0); shade.dot_sizes = ˙ shade.value = 1.0; shade.numsizes = 1; dot.bit_pattern = 1; dot.value = 1.0; stp_dither_set_inks_full(v, channel, 1, &shade, 1.0, 1.0); } static void insert_subchannel(stp_vars_t *v, stpi_dither_t *d, int channel, int subchannel) { int i; unsigned oc = d->subchannel_count[channel]; unsigned increment = subchannel - oc + 1; unsigned old_place = d->channel_index[channel] + oc; stpi_dither_channel_t *nc = stp_malloc(sizeof(stpi_dither_channel_t) * (d->total_channel_count + increment)); if (d->channel) { /* * Copy the old channels, including all subchannels of the current * channel that already existed. */ memcpy(nc, d->channel, sizeof(stpi_dither_channel_t) * old_place); if (old_place < d->total_channel_count) /* * If we're inserting a new subchannel in the middle somewhere, * we need to move everything else up */ memcpy(nc + old_place + increment, d->channel + old_place, (sizeof(stpi_dither_channel_t) * (d->total_channel_count - old_place))); stp_free(d->channel); } d->channel = nc; if (channel < d->channel_count - 1) /* Now fix up the subchannel offsets */ for (i = channel + 1; i < d->channel_count; i++) d->channel_index[i] += increment; d->subchannel_count[channel] = subchannel + 1; d->total_channel_count += increment; for (i = oc; i < oc + increment; i++) initialize_channel(v, channel, i); } void stpi_dither_finalize(stp_vars_t *v) { stpi_dither_t *d = (stpi_dither_t *) stp_get_component_data(v, "Dither"); if (!d->finalized) { int i; unsigned rc = 1 + (unsigned) ceil(sqrt(CHANNEL_COUNT(d))); unsigned x_n = d->dither_matrix.x_size / rc; unsigned y_n = d->dither_matrix.y_size / rc; for (i = 0; i < CHANNEL_COUNT(d); i++) { stpi_dither_channel_t *dc = &(CHANNEL(d, i)); stp_dither_matrix_clone(&(d->dither_matrix), &(dc->dithermat), x_n * (i % rc), y_n * (i / rc)); stp_dither_matrix_clone(&(d->dither_matrix), &(dc->pick), x_n * (i % rc), y_n * (i / rc)); } d->finalized = 1; } } void stp_dither_add_channel(stp_vars_t *v, unsigned char *data, unsigned channel, unsigned subchannel) { stpi_dither_t *d = (stpi_dither_t *) stp_get_component_data(v, "Dither"); int idx; if (channel >= d->channel_count) insert_channel(v, d, channel); if (subchannel >= d->subchannel_count[channel]) insert_subchannel(v, d, channel, subchannel); idx = stpi_dither_translate_channel(v, channel, subchannel); STPI_ASSERT(idx >= 0, NULL); d->channel[idx].ptr = data; } static void stpi_dither_finalize_ranges(stp_vars_t *v, stpi_dither_channel_t *dc) { stpi_dither_t *d = (stpi_dither_t *) stp_get_component_data(v, "Dither"); int i; unsigned lbit = dc->bit_max; dc->signif_bits = 0; while (lbit > 0) { dc->signif_bits++; lbit >>= 1; } for (i = 0; i < dc->nlevels; i++) { if (dc->ranges[i].lower->bits == dc->ranges[i].upper->bits) dc->ranges[i].is_same_ink = 1; else dc->ranges[i].is_same_ink = 0; if (dc->ranges[i].range_span > 0 && dc->ranges[i].value_span > 0) dc->ranges[i].is_equal = 0; else dc->ranges[i].is_equal = 1; stp_dprintf(STP_DBG_INK, v, " level %d value[0] %d value[1] %d range[0] %d range[1] %d\n", i, dc->ranges[i].lower->value, dc->ranges[i].upper->value, dc->ranges[i].lower->range, dc->ranges[i].upper->range); stp_dprintf(STP_DBG_INK, v, " bits[0] %d bits[1] %d\n", dc->ranges[i].lower->bits, dc->ranges[i].upper->bits); stp_dprintf(STP_DBG_INK, v, " rangespan %d valuespan %d same_ink %d equal %d\n", dc->ranges[i].range_span, dc->ranges[i].value_span, dc->ranges[i].is_same_ink, dc->ranges[i].is_equal); if (i > 0 && dc->ranges[i].lower->range >= d->adaptive_limit) { d->adaptive_limit = dc->ranges[i].lower->range + 1; if (d->adaptive_limit > 65535) d->adaptive_limit = 65535; stp_dprintf(STP_DBG_INK, v, "Setting adaptive limit to %d\n", d->adaptive_limit); } } for (i = 0; i <= dc->nlevels; i++) stp_dprintf(STP_DBG_INK, v, " ink_list[%d] range %d value %d bits %d\n", i, dc->ink_list[i].range, dc->ink_list[i].value, dc->ink_list[i].bits); if (dc->nlevels == 1 && dc->ranges[0].upper->bits == 1) dc->very_fast = 1; else dc->very_fast = 0; stp_dprintf(STP_DBG_INK, v, " bit_max %d signif_bits %d\n", dc->bit_max, dc->signif_bits); } static void stpi_dither_set_ranges(stp_vars_t *v, int color, const stp_shade_t *shade, double density, double darkness) { stpi_dither_t *d = (stpi_dither_t *) stp_get_component_data(v, "Dither"); stpi_dither_channel_t *dc = &(CHANNEL(d, color)); const stp_dotsize_t *ranges = shade->dot_sizes; int nlevels = shade->numsizes; int i; STP_SAFE_FREE(dc->ranges); STP_SAFE_FREE(dc->ink_list); dc->nlevels = nlevels > 1 ? nlevels + 1 : nlevels; dc->ranges = (stpi_dither_segment_t *) stp_zalloc(dc->nlevels * sizeof(stpi_dither_segment_t)); dc->ink_list = (stpi_ink_defn_t *) stp_zalloc((dc->nlevels + 1) * sizeof(stpi_ink_defn_t)); dc->bit_max = 0; dc->density = density * 65535; dc->darkness = darkness; stp_init_debug_messages(v); stp_dprintf(STP_DBG_INK, v, "stpi_dither_set_ranges channel %d nlevels %d density %f darkness %f\n", color, nlevels, density, darkness); for (i = 0; i < nlevels; i++) stp_dprintf(STP_DBG_INK, v, " level %d value %f pattern %x\n", i, ranges[i].value, ranges[i].bit_pattern); dc->ranges[0].lower = &dc->ink_list[0]; dc->ranges[0].upper = &dc->ink_list[1]; dc->ink_list[0].range = 0; dc->ink_list[0].value = 0; dc->ink_list[0].bits = 0; if (nlevels == 1) dc->ink_list[1].range = 65535; else dc->ink_list[1].range = ranges[0].value * 65535.0 * density; if (dc->ink_list[1].range > 65535) dc->ink_list[1].range = 65535; dc->ink_list[1].value = ranges[0].value * 65535.0; if (dc->ink_list[1].value > 65535) dc->ink_list[1].value = 65535; dc->ink_list[1].bits = ranges[0].bit_pattern; if (ranges[0].bit_pattern > dc->bit_max) dc->bit_max = ranges[0].bit_pattern; dc->ranges[0].range_span = dc->ranges[0].upper->range; dc->ranges[0].value_span = dc->ranges[0].upper->value; if (dc->nlevels > 1) { for (i = 1; i < nlevels; i++) { int l = i + 1; dc->ranges[i].lower = &dc->ink_list[i]; dc->ranges[i].upper = &dc->ink_list[l]; dc->ink_list[l].range = (ranges[i].value + ranges[i].value) * 32768.0 * density; if (dc->ink_list[l].range > 65535) dc->ink_list[l].range = 65535; dc->ink_list[l].value = ranges[i].value * 65535.0; if (dc->ink_list[l].value > 65535) dc->ink_list[l].value = 65535; dc->ink_list[l].bits = ranges[i].bit_pattern; if (ranges[i].bit_pattern > dc->bit_max) dc->bit_max = ranges[i].bit_pattern; dc->ranges[i].range_span = dc->ink_list[l].range - dc->ink_list[i].range; dc->ranges[i].value_span = dc->ink_list[l].value - dc->ink_list[i].value; } dc->ranges[i].lower = &dc->ink_list[i]; dc->ranges[i].upper = &dc->ink_list[i+1]; dc->ink_list[i+1] = dc->ink_list[i]; dc->ink_list[i+1].range = 65535; dc->ranges[i].range_span = dc->ink_list[i+1].range - dc->ink_list[i].range; dc->ranges[i].value_span = dc->ink_list[i+1].value - dc->ink_list[i].value; } stpi_dither_finalize_ranges(v, dc); stp_flush_debug_messages(v); } void stp_dither_set_inks_simple(stp_vars_t *v, int color, int nlevels, const double *levels, double density, double darkness) { stp_shade_t s; stp_dotsize_t *d = stp_malloc(nlevels * sizeof(stp_dotsize_t)); int i; s.dot_sizes = d; s.value = 65535.0; s.numsizes = nlevels; for (i = 0; i < nlevels; i++) { d[i].bit_pattern = i + 1; d[i].value = levels[i]; } stp_dither_set_inks_full(v, color, 1, &s, density, darkness); stp_free(d); } void stp_dither_set_inks_full(stp_vars_t *v, int color, int nshades, const stp_shade_t *shades, double density, double darkness) { int i; int idx; stp_channel_reset_channel(v, color); for (i = nshades - 1; i >= 0; i--) { int subchannel = nshades - i - 1; idx = stpi_dither_translate_channel(v, color, subchannel); STPI_ASSERT(idx >= 0, NULL); stp_channel_add(v, color, subchannel, shades[i].value); if (idx >= 0) stpi_dither_set_ranges(v, idx, &shades[i], density, shades[i].value * darkness); stp_dprintf(STP_DBG_INK, v, " shade %d value %f\n", i, shades[i].value); } } void stp_dither_set_inks(stp_vars_t *v, int color, double density, double darkness, int nshades, const double *svalues, int ndotsizes, const double *dvalues) { int i, j; stp_shade_t *shades = stp_malloc(sizeof(stp_shade_t) * nshades); stp_dotsize_t *dotsizes = stp_malloc(sizeof(stp_dotsize_t) * ndotsizes); j = 0; for (i = 0; i < ndotsizes; i++) { /* Skip over any zero-valued dot sizes */ if (dvalues[i] > 0) { dotsizes[j].value = dvalues[i]; dotsizes[j].bit_pattern = i + 1; j++; } } for (i = 0; i < nshades; i++) { shades[i].value = svalues[i]; shades[i].numsizes = j; shades[i].dot_sizes = dotsizes; } stp_dither_set_inks_full(v, color, nshades, shades, density, darkness); stp_free(dotsizes); stp_free(shades); }