#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <gimp-print/gimp-print.h>
#include "gimp-print-internal.h"
#include <gimp-print/gimp-print-intl-internal.h>
#include <string.h>
#include "dither-impl.h"
#include "dither-inlined-functions.h"
#define UPDATE_COLOR(color, dither) ( \
((dither) >= 0)? (color) + ((dither) >> 3) : (color) - ((-(dither)) >> 3))
static inline int
update_dither(stpi_dither_t *d, int channel, int width,
int direction, int *error0, int *error1)
{
int r = CHANNEL(d, channel).v;
int o = CHANNEL(d, channel).o;
int tmp = r;
int i, dist, dist1;
int delta, delta1;
int offset;
if (tmp == 0)
return error0[direction];
if (tmp > 65535)
tmp = 65535;
if (d->spread >= 16 || o >= 2048)
{
tmp += tmp;
tmp += tmp;
error1[0] += tmp;
return error0[direction] + tmp;
}
else
{
int tmpo = o << 5;
offset = ((65535 - tmpo) >> d->spread) +
((tmp & d->spread_mask) > (tmpo & d->spread_mask));
}
switch (offset)
{
case 0:
tmp += tmp;
tmp += tmp;
error1[0] += tmp;
return error0[direction] + tmp;
case 1:
error1[-1] += tmp;
error1[1] += tmp;
tmp += tmp;
error1[0] += tmp;
tmp += tmp;
return error0[direction] + tmp;
default:
tmp += tmp;
tmp += tmp;
dist = tmp / d->offset0_table[offset];
dist1 = tmp / d->offset1_table[offset];
delta = dist;
delta1 = dist1;
for (i = -offset; i; i++)
{
error1[i] += delta;
error1[-i] += delta;
error0[i] += delta1;
error0[-i] += delta1;
delta1 += dist1;
delta += dist;
}
error1[0] += delta;
return error0[direction];
}
}
static inline int
print_color(const stpi_dither_t *d, stpi_dither_channel_t *dc, int x, int y,
unsigned char bit, int length, int dontprint, int stpi_dither_type,
const unsigned char *mask)
{
int base = dc->b;
int density = dc->o;
int adjusted = dc->v;
unsigned randomizer = dc->randomizer;
stp_dither_matrix_impl_t *pick_matrix = &(dc->pick);
stp_dither_matrix_impl_t *dither_matrix = &(dc->dithermat);
unsigned rangepoint = 32768;
unsigned vmatrix;
int i;
int j;
unsigned char *tptr;
unsigned bits;
unsigned v;
int levels = dc->nlevels - 1;
int dither_value = adjusted;
stpi_ink_defn_t *lower;
stpi_ink_defn_t *upper;
if (base <= 0 || density <= 0 ||
(adjusted <= 0 && !(stpi_dither_type & D_ADAPTIVE_BASE)))
return adjusted;
if (density > 65535)
density = 65535;
for (i = levels; i >= 0; i--)
{
stpi_dither_segment_t *dd = &(dc->ranges[i]);
if (density <= dd->lower->range)
continue;
if (stpi_dither_type & D_ADAPTIVE_BASE)
{
stpi_dither_type -= D_ADAPTIVE_BASE;
if (i < levels || base <= d->adaptive_limit)
{
stpi_dither_type = D_ORDERED;
dither_value = base;
}
else if (adjusted <= 0)
return adjusted;
}
lower = dd->lower;
upper = dd->upper;
if (stpi_dither_type & D_ORDERED_BASE)
{
rangepoint = density - dd->lower->range;
if (dd->range_span < 65535)
rangepoint = rangepoint * 65535 / dd->range_span;
vmatrix = 0;
}
else
{
unsigned virtual_value;
rangepoint = density - dd->lower->range;
if (dd->range_span < 65535)
rangepoint = rangepoint * 65535 / dd->range_span;
if (dd->value_span == 0)
virtual_value = upper->value;
else
virtual_value = (upper->value + lower->value) / 2;
randomizer = 0;
if (randomizer > 0)
{
if (base > d->d_cutoff)
randomizer = 0;
else if (base > d->d_cutoff / 2)
randomizer = randomizer * 2 * (d->d_cutoff - base) / d->d_cutoff;
}
if (randomizer == 0)
vmatrix = virtual_value / 2;
else
{
vmatrix = ditherpoint(d, dither_matrix, x);
vmatrix = vmatrix * virtual_value / 65535;
if (randomizer != 65535)
{
unsigned vbase = virtual_value * (65535u - randomizer) /
131070u;
vmatrix = vmatrix * randomizer / 65535;
vmatrix += vbase;
}
}
}
if (dither_value > 0 && dither_value >= vmatrix)
{
stpi_ink_defn_t *subc;
if (dd->is_same_ink)
subc = upper;
else
{
if (rangepoint >= ditherpoint(d, pick_matrix, x))
subc = upper;
else
subc = lower;
}
v = subc->value;
if (!mask || (*(mask + d->ptr_offset) & bit))
{
if (dc->ptr)
{
tptr = dc->ptr + d->ptr_offset;
if (dontprint < v)
{
bits = subc->bits;
set_row_ends(dc, x);
for (j = 1; j <= bits; j += j, tptr += length)
{
if (j & bits)
tptr[0] |= bit;
}
}
}
}
if (stpi_dither_type & D_ORDERED_BASE)
{
double adj = -(int) v;
adj /= 2.0;
adjusted = adj;
}
else
{
double adj = v;
adjusted -= adj;
}
}
return adjusted;
}
return adjusted;
}
static int
shared_ed_initializer(stpi_dither_t *d,
int row,
int duplicate_line,
int zero_mask,
int length,
int direction,
int ****error,
int **ndither)
{
int i, j;
for (i = 0; i < CHANNEL_COUNT(d); i++)
CHANNEL(d, i).error_rows = 2;
if (!duplicate_line)
{
if ((zero_mask & ((1 << CHANNEL_COUNT(d)) - 1)) !=
((1 << CHANNEL_COUNT(d)) - 1))
d->last_line_was_empty = 0;
else
d->last_line_was_empty++;
}
else if (d->last_line_was_empty)
d->last_line_was_empty++;
if (d->last_line_was_empty >= 5)
return 0;
else if (d->last_line_was_empty == 4)
{
for (i = 0; i < CHANNEL_COUNT(d); i++)
for (j = 0; j < d->error_rows; j++)
memset(stpi_dither_get_errline(d, row + j, i), 0,
d->dst_width * sizeof(int));
return 0;
}
d->ptr_offset = (direction == 1) ? 0 : length - 1;
*error = stp_malloc(CHANNEL_COUNT(d) * sizeof(int **));
*ndither = stp_malloc(CHANNEL_COUNT(d) * sizeof(int));
for (i = 0; i < CHANNEL_COUNT(d); i++)
{
(*error)[i] = stp_malloc(d->error_rows * sizeof(int *));
for (j = 0; j < d->error_rows; j++)
{
(*error)[i][j] = stpi_dither_get_errline(d, row + j, i);
if (j == d->error_rows - 1)
memset((*error)[i][j], 0, d->dst_width * sizeof(int));
if (direction == -1)
(*error)[i][j] += d->dst_width - 1;
}
(*ndither)[i] = (*error)[i][0][0];
}
return 1;
}
static void
shared_ed_deinitializer(stpi_dither_t *d,
int ***error,
int *ndither)
{
int i;
for (i = 0; i < CHANNEL_COUNT(d); i++)
{
STP_SAFE_FREE(error[i]);
}
STP_SAFE_FREE(error);
STP_SAFE_FREE(ndither);
}
void
stpi_dither_ed(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 *ndither;
int ***error;
int terminate;
int direction = row & 1 ? 1 : -1;
int xerror, xstep, xmod;
length = (d->dst_width + 7) / 8;
if (d->stpi_dither_type & D_ADAPTIVE_BASE)
for (i = 0; i < CHANNEL_COUNT(d); i++)
if (CHANNEL(d, i).nlevels > 1)
{
stpi_dither_ordered(v, row, raw, duplicate_line, zero_mask, mask);
return;
}
if (!shared_ed_initializer(d, row, duplicate_line, zero_mask, length,
direction, &error, &ndither))
return;
x = (direction == 1) ? 0 : d->dst_width - 1;
bit = 1 << (7 - (x & 7));
xstep = CHANNEL_COUNT(d) * (d->src_width / d->dst_width);
xmod = d->src_width % d->dst_width;
xerror = (xmod * x) % d->dst_width;
terminate = (direction == 1) ? d->dst_width : -1;
if (direction == -1)
raw += (CHANNEL_COUNT(d) * (d->src_width - 1));
for (; x != terminate; x += direction)
{
for (i = 0; i < CHANNEL_COUNT(d); i++)
{
if (CHANNEL(d, i).ptr)
{
CHANNEL(d, i).v = raw[i];
CHANNEL(d, i).o = CHANNEL(d, i).v;
CHANNEL(d, i).b = CHANNEL(d, i).v;
CHANNEL(d, i).v = UPDATE_COLOR(CHANNEL(d, i).v, ndither[i]);
CHANNEL(d, i).v = print_color(d, &(CHANNEL(d, i)), x, row, bit,
length, 0, d->stpi_dither_type,
mask);
ndither[i] = update_dither(d, i, d->src_width,
direction, error[i][0], error[i][1]);
}
}
ADVANCE_BIDIRECTIONAL(d, bit, raw, direction, CHANNEL_COUNT(d), xerror,
xstep, xmod, error, d->error_rows);
}
shared_ed_deinitializer(d, error, ndither);
if (direction == -1)
stpi_dither_reverse_row_ends(d);
}