#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 <math.h>
#include <limits.h>
#include "dither-impl.h"
#include "dither-inlined-functions.h"
typedef struct
{
int dx;
int dy;
int r_sq;
} distance_t;
typedef struct
{
int d2x;
int d2y;
distance_t d_sq;
int aspect;
int unitone_aspect;
int physical_aspect;
int diff_factor;
stpi_dither_channel_t *dummy_channel;
} eventone_t;
typedef struct shade_segment
{
distance_t dis;
distance_t *et_dis;
stpi_ink_defn_t lower;
stpi_ink_defn_t upper;
int share_this_channel;
} shade_distance_t;
#define EVEN_C1 256
#define EVEN_C2 (EVEN_C1 * sqrt(3.0) / 2.0)
#define UNITONE_C1 16384
#define UNITONE_C2 (UNITONE_C1 * sqrt(3.0) / 2.0)
static void
free_eventone_data(stpi_dither_t *d)
{
int i;
eventone_t *et = (eventone_t *) (d->aux_data);
for (i = 0; i < CHANNEL_COUNT(d); i++)
{
if (CHANNEL(d, i).aux_data)
{
shade_distance_t *shade = (shade_distance_t *) CHANNEL(d,i).aux_data;
STP_SAFE_FREE(shade->et_dis);
STP_SAFE_FREE(CHANNEL(d, i).aux_data);
}
}
if (et->dummy_channel) {
stpi_dither_channel_t *dc = et->dummy_channel;
shade_distance_t *shade = (shade_distance_t *) dc->aux_data;
STP_SAFE_FREE(shade->et_dis);
STP_SAFE_FREE(dc->aux_data);
stpi_dither_channel_destroy(dc);
STP_SAFE_FREE(et->dummy_channel);
}
STP_SAFE_FREE(et);
}
static void
et_setup(stpi_dither_t *d)
{
int size = 2 * MAX_SPREAD + ((d->dst_width + 7) & ~7);
static const int diff_factors[] = {1, 10, 16, 23, 32};
eventone_t *et = stp_zalloc(sizeof(eventone_t));
int xa, ya;
int i;
for (i = 0; i < CHANNEL_COUNT(d); i++) {
CHANNEL(d, i).error_rows = 1;
CHANNEL(d, i).errs = stp_zalloc(1 * sizeof(int *));
CHANNEL(d, i).errs[0] = stp_zalloc(size * sizeof(int));
}
if (d->stpi_dither_type & D_UNITONE) {
stpi_dither_channel_t *dc = stp_zalloc(sizeof(stpi_dither_channel_t));
stp_dither_matrix_clone(&(d->dither_matrix), &(dc->dithermat), 0, 0);
stp_dither_matrix_clone(&(d->transition_matrix), &(dc->pick), 0, 0);
dc->error_rows = 1;
dc->errs = stp_zalloc(1 * sizeof(int *));
dc->errs[0] = stp_zalloc(size * sizeof(int));
et->dummy_channel = dc;
}
xa = d->x_aspect / d->y_aspect;
if (xa == 0)
xa = 1;
et->d_sq.dx = xa * xa;
et->d2x = 2 * et->d_sq.dx;
ya = d->y_aspect / d->x_aspect;
if (ya == 0)
ya = 1;
et->d_sq.dy = ya * ya;
et->d2y = 2 * et->d_sq.dy;
et->aspect = EVEN_C2 / (xa * ya);
et->unitone_aspect = UNITONE_C2 / (xa * ya);
et->d_sq.r_sq = 0;
for (i = 0; i < CHANNEL_COUNT(d); i++) {
int x;
shade_distance_t *shade = stp_zalloc(sizeof(shade_distance_t));
shade->dis = et->d_sq;
shade->et_dis = stp_malloc(sizeof(distance_t) * d->dst_width);
if (CHANNEL(d, i).darkness > .1)
shade->share_this_channel = 1;
else
shade->share_this_channel = 0;
for (x = 0; x < d->dst_width; x++) {
shade->et_dis[x] = et->d_sq;
}
CHANNEL(d, i).aux_data = shade;
}
if (et->dummy_channel) {
int x;
shade_distance_t *shade = stp_zalloc(sizeof(shade_distance_t));
shade->dis = et->d_sq;
shade->et_dis = stp_malloc(sizeof(distance_t) * d->dst_width);
for (x = 0; x < d->dst_width; x++) {
shade->et_dis[x] = et->d_sq;
}
et->dummy_channel->aux_data = shade;
}
et->physical_aspect = d->y_aspect / d->x_aspect;
if (et->physical_aspect >= 4)
et->physical_aspect = 4;
else if (et->physical_aspect >= 2)
et->physical_aspect = 2;
else et->physical_aspect = 1;
et->diff_factor = diff_factors[et->physical_aspect];
d->aux_data = et;
d->aux_freefunc = free_eventone_data;
}
static int
et_initializer(stpi_dither_t *d, int duplicate_line, int zero_mask)
{
int i;
eventone_t *et;
if (!d->aux_data)
et_setup(d);
et = (eventone_t *) (d->aux_data);
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) {
if (et->dummy_channel) {
memset(et->dummy_channel->errs[0], 0, d->dst_width * sizeof(int));
}
for (i = 0; i < CHANNEL_COUNT(d); i++)
memset(CHANNEL(d, i).errs[0], 0, d->dst_width * sizeof(int));
return 0;
}
for (i = 0; i < CHANNEL_COUNT(d); i++)
CHANNEL(d, i).v = 0;
if (et->dummy_channel)
et->dummy_channel->v = 0;
return 1;
}
static inline void
advance_eventone_pre(shade_distance_t *sp, eventone_t *et, int x)
{
distance_t *etd = &sp->et_dis[x];
int t = sp->dis.r_sq + sp->dis.dx;
if (t <= etd->r_sq) {
sp->dis.r_sq = t;
sp->dis.dx += et->d2x;
} else {
sp->dis = *etd;
}
}
static inline void
eventone_update(stpi_dither_channel_t *dc, eventone_t *et,
int x, int direction)
{
shade_distance_t *sp = (shade_distance_t *) dc->aux_data;
distance_t *etd = &sp->et_dis[x];
int t = etd->r_sq + etd->dy;
int u = sp->dis.r_sq + sp->dis.dy;
if (u < t) {
t = u;
etd->dx = sp->dis.dx;
etd->dy = sp->dis.dy;
}
etd->dy += et->d2y;
if (t > 65535) {
t = 65535;
}
etd->r_sq = t;
}
static inline void
diffuse_error(stpi_dither_channel_t *dc, eventone_t *et, int x, int direction)
{
#if 0
int frac_2 = dc->v + dc->v;
int frac_3 = frac_2 + dc->v;
dc->errs[0][x + MAX_SPREAD] = frac_3;
dc->errs[0][x + MAX_SPREAD - direction] += frac_2;
dc->v -= (frac_2 + frac_3) / 16;
#else
dc->errs[0][x + MAX_SPREAD] = dc->v * 3;
dc->errs[0][x + MAX_SPREAD - direction] += dc->v * 5;
dc->errs[0][x + MAX_SPREAD - (direction * 2)] += dc->v * 1;
dc->v -= dc->v * 9 / 16;
#endif
}
static inline int
eventone_adjust(stpi_dither_channel_t *dc, eventone_t *et, int dither_point,
unsigned int desired)
{
if (dither_point <= 0)
return 0;
else if (dither_point >= 65535)
return 65535;
if (desired == 0) {
dither_point = 0;
} else {
shade_distance_t *shade = (shade_distance_t *) dc->aux_data;
dither_point += shade->dis.r_sq * et->aspect - (EVEN_C1 * 65535) / desired;
if (dither_point > 65535)
dither_point = 65535;
else if (dither_point < 0)
dither_point = 0;
}
return dither_point;
}
static inline int
unitone_adjust(stpi_dither_channel_t *dc, eventone_t *et,
int dither_point, unsigned int desired)
{
if (dither_point <= 0)
return INT_MIN;
else if (dither_point >= 65535)
return dither_point;
if (desired == 0) {
dither_point = INT_MIN;
} else {
shade_distance_t *shade = (shade_distance_t *) dc->aux_data;
dither_point += shade->dis.r_sq * et->unitone_aspect -
(UNITONE_C1 * 65535u) / desired;
}
return dither_point;
}
static inline void
find_segment(stpi_dither_channel_t *dc, unsigned inkval,
stpi_ink_defn_t *lower, stpi_ink_defn_t *upper)
{
lower->range = 0;
lower->bits = 0;
if (dc->nlevels == 1)
{
upper->bits = dc->ink_list[1].bits;
upper->range = dc->ink_list[1].value;
}
else
{
int i;
stpi_ink_defn_t *ip;
for (i=0, ip = dc->ink_list; i < dc->nlevels - 1; i++, ip++) {
if (ip->value > inkval)
break;
lower->bits = ip->bits;
lower->range = ip->value;
}
upper->bits = ip->bits;
upper->range = ip->value;
}
}
static inline int
find_segment_and_ditherpoint(stpi_dither_channel_t *dc, unsigned inkval,
stpi_ink_defn_t *lower, stpi_ink_defn_t *upper)
{
find_segment(dc, inkval, lower, upper);
if (inkval <= lower->range)
return 0;
else if (inkval >= upper->range)
return 65535;
else
return (65535u * (inkval - lower->range)) / (upper->range - lower->range);
}
static inline void
print_ink(stpi_dither_t *d, unsigned char *tptr, const stpi_ink_defn_t *ink,
unsigned char bit, int length)
{
int j;
if (tptr != 0)
{
tptr += d->ptr_offset;
switch(ink->bits)
{
case 1:
tptr[0] |= bit;
return;
case 2:
tptr[length] |= bit;
return;
case 3:
tptr[0] |= bit;
tptr[length] |= bit;
return;
default:
for (j=1; j <= ink->bits; j+=j, tptr += length) {
if (j & ink->bits)
*tptr |= bit;
}
return;
}
}
}
void
stpi_dither_et(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");
eventone_t *et;
int x,
length;
unsigned char bit;
int i;
int terminate;
int direction;
int xerror, xstep, xmod;
int channel_count = CHANNEL_COUNT(d);
if (!et_initializer(d, duplicate_line, zero_mask))
return;
et = (eventone_t *) d->aux_data;
length = (d->dst_width + 7) / 8;
if (row & 1) {
direction = 1;
x = 0;
terminate = d->dst_width;
d->ptr_offset = 0;
} else {
direction = -1;
x = d->dst_width - 1;
terminate = -1;
d->ptr_offset = length - 1;
raw += channel_count * (d->src_width - 1);
}
bit = 1 << (7 - (x & 7));
xstep = channel_count * (d->src_width / d->dst_width);
xmod = d->src_width % d->dst_width;
xerror = (xmod * x) % d->dst_width;
for (; x != terminate; x += direction) {
int point_error = 0;
int comparison = 32768;
if (d->stpi_dither_type & D_ORDERED_BASE)
comparison += (ditherpoint(d, &(d->dither_matrix), x) / 16) - 2048;
for (i=0; i < channel_count; i++) {
if (CHANNEL(d, i).ptr)
{
int inkspot;
int range_point;
stpi_dither_channel_t *dc = &CHANNEL(d, i);
shade_distance_t *sp = (shade_distance_t *) dc->aux_data;
stpi_ink_defn_t *inkp;
stpi_ink_defn_t lower, upper;
advance_eventone_pre(sp, et, x);
range_point =
find_segment_and_ditherpoint(dc, raw[i], &lower, &upper);
dc->v += 2 * range_point + (dc->errs[0][x + MAX_SPREAD] + 8) / 16;
inkspot = dc->v - range_point;
point_error += eventone_adjust(dc, et, inkspot, range_point);
inkp = &lower;
if (point_error >= comparison) {
point_error -= 65535;
inkp = &upper;
dc->v -= 131070;
sp->dis = et->d_sq;
}
if (inkp->bits) {
if (!mask || (*(mask + d->ptr_offset) & bit)) {
set_row_ends(dc, x);
print_ink(d, dc->ptr, inkp, bit, length);
}
}
eventone_update(dc, et, x, direction);
diffuse_error(dc, et, x, direction);
}
}
if (direction == 1)
ADVANCE_UNIDIRECTIONAL(d, bit, raw, channel_count, xerror, xstep, xmod);
else
ADVANCE_REVERSE(d, bit, raw, channel_count, xerror, xstep, xmod);
}
if (direction == -1)
stpi_dither_reverse_row_ends(d);
}
void
stpi_dither_ut(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");
eventone_t *et;
int x,
length;
unsigned char bit;
int i;
int terminate;
int direction;
int xerror, xstep, xmod;
int channel_count = CHANNEL_COUNT(d);
stpi_dither_channel_t *ddc;
if (channel_count == 1) {
stpi_dither_et(v, row, raw, duplicate_line, zero_mask, mask);
return;
}
if (!et_initializer(d, duplicate_line, zero_mask))
return;
et = (eventone_t *) d->aux_data;
ddc = et->dummy_channel;
length = (d->dst_width + 7) / 8;
if (row & 1) {
direction = 1;
x = 0;
terminate = d->dst_width;
d->ptr_offset = 0;
} else {
direction = -1;
x = d->dst_width - 1;
terminate = -1;
d->ptr_offset = length - 1;
raw += channel_count * (d->src_width - 1);
}
bit = 1 << (7 - (x & 7));
xstep = channel_count * (d->src_width / d->dst_width);
xmod = d->src_width % d->dst_width;
xerror = (xmod * x) % d->dst_width;
for (; x != terminate; x += direction) {
shade_distance_t *ssp = (shade_distance_t *) ddc->aux_data;
int point_error = 0;
int total_error = 0;
int channels_to_print = 0;
int print_all_channels = 0;
int maximum_value = 0;
int comparison = 32768;
stpi_dither_channel_t *best_channel = NULL;
stpi_dither_channel_t *second_best_channel = NULL;
int best_channel_value = INT_MIN;
int second_best_channel_value = INT_MIN;
int random_value = ditherpoint(d, &(d->dither_matrix), x);
if (d->stpi_dither_type & D_ORDERED_BASE)
comparison += (random_value / 16) - 2048;
ddc->b = 0;
advance_eventone_pre(ssp, et, x);
for (i=0; i < channel_count; i++) {
stpi_dither_channel_t *dc = &CHANNEL(d, i);
if (dc->ptr) {
shade_distance_t *sp = (shade_distance_t *) dc->aux_data;
advance_eventone_pre(sp, et, x);
dc->b = find_segment_and_ditherpoint(dc, raw[i],
&(sp->lower), &(sp->upper));
if (sp->share_this_channel) {
if (dc->b > maximum_value)
maximum_value = dc->b;
ddc->b += dc->b;
}
dc->v += 2 * dc->b + (dc->errs[0][x + MAX_SPREAD] + 8) / 16;
dc->o = unitone_adjust(dc, et, dc->v - dc->b, dc->b);
}
}
#if 0
if ((2 * (ddc->b - maximum_value)) < (3 * maximum_value))
print_all_channels = 1;
#endif
if (ddc->b > 131070)
print_all_channels = 1;
else if (ddc->b > 65535) {
ddc->b -= 65535;
channels_to_print = 1;
}
if (ddc->b > 65535) {
ddc->b = 65535;
}
ddc->v += 2 * ddc->b + (ddc->errs[0][x + MAX_SPREAD] + 8) / 16;
total_error += eventone_adjust(ddc, et, ddc->v - ddc->b, ddc->b);
if (total_error >= comparison) {
channels_to_print += 1;
}
if (!print_all_channels) {
for (i=0; i < channel_count; i++) {
stpi_dither_channel_t *dc = &CHANNEL(d, i);
shade_distance_t *sp = (shade_distance_t *) dc->aux_data;
if (dc->ptr) {
if (sp->share_this_channel) {
if (dc->o > best_channel_value) {
second_best_channel = best_channel;
best_channel = dc;
second_best_channel_value = best_channel_value;
if (dc->o >= 32768)
best_channel_value = INT_MAX;
else
best_channel_value = dc->o;
} else if (dc->o > second_best_channel_value) {
second_best_channel = dc;
if (dc->o >= 32768)
second_best_channel_value = INT_MAX;
else
second_best_channel_value = dc->o;
}
}
}
}
}
for (i=0; i < channel_count; i++) {
stpi_dither_channel_t *dc = &CHANNEL(d, i);
if (dc->ptr) {
shade_distance_t *sp = (shade_distance_t *) dc->aux_data;
stpi_ink_defn_t *inkp = &(sp->lower);
if (dc->o < 0)
dc->o = 0;
else if (dc->o > 65535)
dc->o = 65535;
if (print_all_channels || !sp->share_this_channel) {
point_error += dc->o;
if (point_error >= comparison) {
point_error -= 65535;
inkp = &(sp->upper);
dc->v -= 131070;
sp->dis = et->d_sq;
}
} else if ((channels_to_print >= 1 && best_channel == dc) ||
(channels_to_print >= 2 && second_best_channel == dc)) {
inkp = &(sp->upper);
dc->v -= 131070;
sp->dis = et->d_sq;
}
if (inkp->bits) {
if (!mask || (*(mask + d->ptr_offset) & bit)) {
set_row_ends(dc, x);
print_ink(d, dc->ptr, inkp, bit, length);
}
}
}
}
if (total_error >= comparison) {
ddc->v -= 131070;
total_error -= 65535;
ssp->dis = et->d_sq;
}
eventone_update(ddc, et, x, direction);
diffuse_error(ddc, et, x, direction);
for (i=0; i < channel_count; i++) {
stpi_dither_channel_t *dc = &CHANNEL(d, i);
if (dc->ptr) {
eventone_update(dc, et, x, direction);
diffuse_error(dc, et, x, direction);
}
}
if (direction == 1)
ADVANCE_UNIDIRECTIONAL(d, bit, raw, channel_count, xerror, xstep, xmod);
else
ADVANCE_REVERSE(d, bit, raw, channel_count, xerror, xstep, xmod);
}
if (direction == -1)
stpi_dither_reverse_row_ends(d);
}