/*
*
* Ordered dither algorithm
*
* Copyright 1997-2003 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
#include "dither-impl.h"
#include "dither-inlined-functions.h"
typedef struct {
size_t channels;
double *drops;
unsigned short *lut;
} stpi_new_ordered_t;
typedef struct {
unsigned short shift;
unsigned short mask;
unsigned short x_mask;
stpi_new_ordered_t *ord_new;
} stpi_ordered_t;
static int
compare_channels(const stpi_dither_channel_t *dc1,
const stpi_dither_channel_t *dc2)
{
int i;
if (dc1->nlevels != dc2->nlevels)
return 0;
for (i = 0; i < dc1->nlevels; i++)
if (dc1->ranges[i].upper->value != dc2->ranges[i].upper->value)
return 0;
return 1;
}
const static double dp_fraction = 0.5;
static void
init_dither_channel_new(stpi_dither_channel_t *dc, stp_vars_t *v)
{
int i, j, k;
double bp = 0;
double lbp = 0;
double lower_bottom = 0;
double lower_middle = 0;
double lower_top = 0;
double upper_bottom = 0;
double upper_middle = 0;
double upper_top = 0;
double *breakpoints;
double *val;
unsigned short *data;
stpi_new_ordered_t *ord = stp_malloc(sizeof(stpi_new_ordered_t));
((stpi_ordered_t *) (dc->aux_data))->ord_new = ord;
ord->channels = dc->nlevels - 1;
ord->drops = stp_malloc(sizeof(double) * (ord->channels + 1));
breakpoints = stp_malloc(sizeof(double) * (ord->channels + 1));
val = stp_malloc(sizeof(double) * ord->channels);
data = stp_malloc(sizeof(unsigned short) * 65536 * ord->channels);
ord->lut = data;
for (j = 0; j < ord->channels; j++)
{
stpi_dither_segment_t *dd = &(dc->ranges[j]);
ord->drops[j] = (double) dd->upper->value / 65535.0;
}
ord->drops[ord->channels] = 1;
for (j = 0; j < ord->channels; j++)
{
if (j == 0)
breakpoints[j] = 65535 * ord->drops[j] * dp_fraction;
else
breakpoints[j] = 65535 * ((ord->drops[j] * dp_fraction) +
(ord->drops[j - 1] * (1.0 - dp_fraction)));
stp_dprintf(STP_DBG_INK, v, " size %.3f bp %5.0f\n",
ord->drops[j], breakpoints[j]);
}
breakpoints[ord->channels] = 65535;
j = 0;
for (i = 0; i <= ord->channels; i++)
{
lbp = bp;
bp = breakpoints[i];
lower_bottom = upper_middle;
upper_bottom = 0;
lower_middle = upper_top;
lower_top = 0;
if (i == ord->channels)
upper_top = 0;
else
upper_top = 65535 * dp_fraction;
if (i > 0)
upper_middle = 65535 - upper_top;
while (j <= bp)
{
double range_point = (j - lbp) / (bp - lbp);
double uv, mv, lv;
int total_ink = 0;
for (k = 0; k < ord->channels; k++)
val[k] = 0;
uv = lower_top + (upper_top - lower_top) * range_point;
mv = lower_middle + (upper_middle - lower_middle) * range_point;
lv = lower_bottom + (upper_bottom - lower_bottom) * range_point;
if (i < ord->channels)
val[i] = (unsigned short) uv;
if (i > 0)
val[i - 1] = (unsigned short) mv;
if (i > 1)
val[i - 2] = (unsigned short) lv;
for (k = ord->channels - 1; k >= 0; k--)
{
total_ink += val[k];
if (total_ink > 65535)
total_ink = 65535;
data[k] = total_ink;
}
if ((stp_get_debug_level() & STP_DBG_INK) && (j % 257 == 0))
{
stp_dprintf(STP_DBG_INK, v, " %5d:", j);
for (k = 0; k < ord->channels; k++)
stp_dprintf(STP_DBG_INK, v, " %9.3f", val[k]);
stp_dprintf(STP_DBG_INK, v, " ");
for (k = 0; k < ord->channels; k++)
stp_dprintf(STP_DBG_INK, v, " %9.3f", breakpoints[k]);
stp_dprintf(STP_DBG_INK, v, " ");
for (k = 0; k < ord->channels; k++)
stp_dprintf(STP_DBG_INK, v, " %5d", data[k]);
stp_dprintf(STP_DBG_INK, v, "\n");
}
data += ord->channels;
j++;
}
}
stp_free(breakpoints);
stp_free(val);
}
static inline void
print_color_ordered_new(const stpi_dither_t *d, stpi_dither_channel_t *dc,
int val, int x, int y, unsigned char bit, int length)
{
int i;
int j;
unsigned bits;
int levels = dc->nlevels - 1;
unsigned dpoint = ditherpoint(d, &(dc->dithermat), x);
const stpi_ordered_t *o = (const stpi_ordered_t *) dc->aux_data;
const stpi_new_ordered_t *ord = (const stpi_new_ordered_t *) o->ord_new;
unsigned short swhere = (unsigned short) val;
unsigned short *where = ord ? ord->lut + (val * levels) : &swhere;
/*
* Look for the appropriate range into which the input value falls.
*/
for (i = levels - 1; i >= 0; i--)
{
if (dpoint < where[i])
{
stpi_dither_segment_t *dd = &(dc->ranges[i]);
bits = dd->upper->bits;
if (bits)
{
unsigned char *tptr = dc->ptr + d->ptr_offset;
/*
* Lay down all of the bits in the pixel.
*/
set_row_ends(dc, x);
for (j = 1; j <= bits; j += j, tptr += length)
{
if (j & bits)
tptr[0] |= bit;
}
}
return;
}
}
}
static inline void
print_color_ordered(const stpi_dither_t *d, stpi_dither_channel_t *dc, int val,
int x, int y, unsigned char bit, int length)
{
int i;
int j;
unsigned bits;
int levels = dc->nlevels - 1;
/*
* Look for the appropriate range into which the input value falls.
*/
for (i = levels; i >= 0; i--)
{
stpi_dither_segment_t *dd = &(dc->ranges[i]);
if (val > dd->lower->value)
{
/*
* Where are we within the range.
*/
unsigned rangepoint = val - dd->lower->value;
if (dd->value_span < 65535)
rangepoint = rangepoint * 65535 / dd->value_span;
if (rangepoint >= ditherpoint(d, &(dc->dithermat), x))
bits = dd->upper->bits;
else
bits = dd->lower->bits;
if (bits)
{
unsigned char *tptr = dc->ptr + d->ptr_offset;
/*
* Lay down all of the bits in the pixel.
*/
set_row_ends(dc, x);
for (j = 1; j <= bits; j += j, tptr += length)
{
if (j & bits)
tptr[0] |= bit;
}
}
return;
}
}
}
static void
free_dither_ordered(stpi_dither_t *d)
{
int i;
stpi_dither_channel_t *dc0 = &CHANNEL(d, 0);
stpi_ordered_t *o0 = dc0->aux_data;
stpi_new_ordered_t *no0 = NULL;
if (o0)
no0 = o0->ord_new;
for (i = CHANNEL_COUNT(d) - 1; i >= 0 ; i--)
{
stpi_dither_channel_t *dc = &CHANNEL(d, i);
if (dc->aux_data)
{
stpi_ordered_t *ord = (stpi_ordered_t *) dc->aux_data;
if (ord->ord_new && (i == 0 || ord->ord_new != no0))
{
stpi_new_ordered_t *no = (stpi_new_ordered_t *) ord->ord_new;
if (no->drops)
stp_free(no->drops);
if (no->lut)
stp_free(no->lut);
stp_free(no);
}
stp_free(dc->aux_data);
dc->aux_data = NULL;
}
}
stp_free(d->aux_data);
}
static void
init_dither_ordered(stpi_dither_t *d, stp_vars_t *v)
{
int i;
d->aux_data = stp_malloc(1);
d->aux_freefunc = &free_dither_ordered;
stp_dprintf(STP_DBG_INK, v, "init_dither_ordered\n");
for (i = 0; i < CHANNEL_COUNT(d); i++)
{
stpi_dither_channel_t *dc = &CHANNEL(d, i);
stpi_ordered_t *s;
dc->aux_data = stp_malloc(sizeof(stpi_ordered_t));
s = (stpi_ordered_t *) dc->aux_data;
s->ord_new = NULL;
if (d->stpi_dither_type & D_ORDERED_SEGMENTED)
{
s->shift = 16 - dc->signif_bits;
s->mask = ((1 << dc->signif_bits) - 1) << s->shift;
s->x_mask = ~(s->mask);
stp_dprintf(STP_DBG_INK, v, " channel %d: shift %d mask 0x%x x_mask 0x%x\n",
i, s->shift, s->mask, s->x_mask);
}
if (d->stpi_dither_type & D_ORDERED_NEW)
{
if (dc->nlevels < 2)
stp_dprintf(STP_DBG_INK, v, " channel %d ignored\n", i);
else if (i == 0 || !compare_channels(&CHANNEL(d, 0), dc))
{
stp_dprintf(STP_DBG_INK, v, " channel %d\n", i);
init_dither_channel_new(dc, v);
}
else
{
stp_dprintf(STP_DBG_INK, v, " channel %d duplicated from channel 0\n", i);
s->ord_new = ((stpi_ordered_t *) (CHANNEL(d, 0).aux_data))->ord_new;
}
}
}
}
void
stpi_dither_ordered(stp_vars_t *v,
int row,
const unsigned short *raw,
int duplicate_line,
int zero_mask,
const unsigned char *mask)
{
stpi_dither_t *d = (stpi_dither_t *) stp_get_component_data(v, "Dither");
int x,
length;
unsigned char bit;
int i;
int one_bit_only = 1;
int one_level_only = 1;
int xerror, xstep, xmod;
if ((zero_mask & ((1 << CHANNEL_COUNT(d)) - 1)) ==
((1 << CHANNEL_COUNT(d)) - 1))
return;
length = (d->dst_width + 7) / 8;
bit = 128;
xstep = CHANNEL_COUNT(d) * (d->src_width / d->dst_width);
xmod = d->src_width % d->dst_width;
xerror = 0;
for (i = 0; i < CHANNEL_COUNT(d); i++)
{
stpi_dither_channel_t *dc = &(CHANNEL(d, i));
if (dc->nlevels != 1)
one_level_only = 0;
if (dc->nlevels != 1 || dc->ranges[0].upper->bits != 1)
one_bit_only = 0;
}
if (! one_bit_only && ! d->aux_data &&
(d->stpi_dither_type & (D_ORDERED_SEGMENTED | D_ORDERED_NEW)))
init_dither_ordered(d, v);
if (one_bit_only)
{
for (x = 0; x < d->dst_width; x ++)
{
if (!mask || (*(mask + d->ptr_offset) & bit))
{
for (i = 0; i < CHANNEL_COUNT(d); i++)
{
if (raw[i] &&
raw[i] >= ditherpoint(d, &(CHANNEL(d, i).dithermat), x))
{
set_row_ends(&(CHANNEL(d, i)), x);
CHANNEL(d, i).ptr[d->ptr_offset] |= bit;
}
}
}
ADVANCE_UNIDIRECTIONAL(d, bit, raw, CHANNEL_COUNT(d),
xerror, xstep, xmod);
}
}
else if (d->stpi_dither_type & D_ORDERED_SEGMENTED)
{
for (x = 0; x < d->dst_width; x ++)
{
if (!mask || (*(mask + d->ptr_offset) & bit))
{
for (i = 0; i < CHANNEL_COUNT(d); i++)
{
stpi_dither_channel_t *dc = &CHANNEL(d, i);
stpi_ordered_t *s = (stpi_ordered_t *) dc->aux_data;
unsigned short bits = raw[i] >> s->shift;
unsigned short val = raw[i] << dc->signif_bits;
val |= val >> s->shift;
if (bits)
{
if (val &&
val >= ditherpoint(d, &(CHANNEL(d, i).dithermat), x))
{
int j;
unsigned char *tptr = dc->ptr + d->ptr_offset;
set_row_ends(dc, x);
for (j = 1; j <= bits; j += j, tptr += length)
{
if (j & bits)
tptr[0] |= bit;
}
}
}
else if (CHANNEL(d, i).ptr && val)
{
if (d->stpi_dither_type & D_ORDERED_NEW)
print_color_ordered_new(d, &(CHANNEL(d, i)), val, x,
row, bit, length);
else
print_color_ordered(d, &(CHANNEL(d, i)), val, x,
row, bit, length);
}
}
}
ADVANCE_UNIDIRECTIONAL(d, bit, raw, CHANNEL_COUNT(d),
xerror, xstep, xmod);
}
}
else if (one_level_only || !(d->stpi_dither_type == D_ORDERED_NEW))
{
for (x = 0; x != d->dst_width; x ++)
{
if (!mask || (*(mask + d->ptr_offset) & bit))
{
for (i = 0; i < CHANNEL_COUNT(d); i++)
{
if (CHANNEL(d, i).ptr && raw[i])
print_color_ordered(d, &(CHANNEL(d, i)), raw[i], x, row,
bit, length);
}
}
ADVANCE_UNIDIRECTIONAL(d, bit, raw, CHANNEL_COUNT(d), xerror,
xstep, xmod);
}
}
else
{
for (x = 0; x != d->dst_width; x ++)
{
if (!mask || (*(mask + d->ptr_offset) & bit))
{
for (i = 0; i < CHANNEL_COUNT(d); i++)
{
if (CHANNEL(d, i).ptr && raw[i])
print_color_ordered_new(d, &(CHANNEL(d, i)), raw[i], x,
row, bit, length);
}
}
ADVANCE_UNIDIRECTIONAL(d, bit, raw, CHANNEL_COUNT(d), xerror,
xstep, xmod);
}
}
}