1
/* Copyright (C) 2001-2006 Artifex Software, Inc.
4
This software is provided AS-IS with no warranty, either express or
7
This software is distributed under license and may not be copied, modified
8
or distributed except as expressly authorized under the terms of that
9
license. Refer to licensing information at http://www.artifex.com/
10
or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134,
11
San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information.
14
/* $Id: sidscale.c 8784 2008-05-28 18:16:26Z giles $ */
15
/* Special Image downsample scaling filters for dithered devices */
20
#include "gxfixed.h" /* for gxdda.h */
26
/* Temporary intermediate values */
27
typedef byte PixelTmp;
30
#define maxPixelTmp 255
31
#define unitPixelTmp 255
33
/* Max of all pixel sizes */
34
#define maxSizeofPixel 2
36
/* Auxiliary structures. */
38
/* ImageSpecialDownScaleEncode / ImageSpecialDownScaleDecode */
39
typedef struct stream_ISpecialDownScale_state_s {
40
/* The client sets the params values before initialization. */
41
stream_image_scale_state_common; /* = state_common + params */
42
/* The init procedure sets the following. */
43
int sizeofPixelIn; /* bytes per input value, 1 or 2 */
44
int sizeofPixelOut; /* bytes per output value, 1 or 2 */
45
void /*PixelIn */ *src;
46
void /*PixelOut */ *dst;
47
void /*PixelIn */ *tmp;
48
gx_dda_int_t dda_x_init; /* initial setting of dda_x */
49
/* The following are updated dynamically. */
51
uint dst_offset, dst_size;
52
gx_dda_int_t dda_x; /* DDA for dest X in current scan line */
54
uint src_offset, src_size;
56
gx_dda_int_t dda_y; /* DDA for dest Y */
57
} stream_ISpecialDownScale_state;
59
gs_private_st_ptrs3(st_ISpecialDownScale_state, stream_ISpecialDownScale_state,
60
"ImageSpecialDownScaleEncode/Decode state",
61
isdscale_state_enum_ptrs, isdscale_state_reloc_ptrs,
64
/* Apply filter to downscale horizontally from src to tmp. */
66
idownscale_x(void /* PixelIn */ * tmp, const void /* PixelIn */ *src, stream_ISpecialDownScale_state *const ss)
69
int Colors = ss->params.Colors;
70
int WidthIn = ss->params.WidthIn;
71
int prev_y = dda_previous(ss->dda_y);
72
int cur_y = dda_next(ss->dda_y);
73
bool firstline = prev_y != cur_y; /* at the start of a new group of lines */
74
bool polarity_additive = ss->params.ColorPolarityAdditive;
76
/* The following could be a macro, BUT macro's with control */
77
/* are not good style and hard to debug */
78
if (ss->sizeofPixelIn == 1) {
79
for (c = 0; c < Colors; ++c) {
80
byte *tp = (byte *)tmp + c; /* destination */
81
const byte *pp = (const byte *)src + c;
83
ss->dda_x = ss->dda_x_init;
84
if_debug1('W', "[W]idownscale_x color %d:", c);
86
for ( i = 0; i < WidthIn; tp += Colors) {
87
int endx = dda_next(ss->dda_x);
91
if ((polarity_additive && (*pp < *tp)) ||
92
(!polarity_additive && (*pp > *tp)) )
101
if_debug1('W', " %d", *tp);
103
if_debug0('W', "\n");
105
} else { /* sizeofPixelIn == 2 */
106
for (c = 0; c < Colors; ++c) {
107
bits16 *tp = (bits16 *)tmp + c; /* destination */
108
const bits16 *pp = (const bits16 *)src + c;
110
ss->dda_x = ss->dda_x_init;
111
if_debug1('W', "[W]idownscale_x color %d:", c);
113
for ( i = 0; i < WidthIn; tp += Colors) {
114
int endx = dda_next(ss->dda_x);
118
if ((polarity_additive && (*pp < *tp)) ||
119
(!polarity_additive && (*pp > *tp)) )
128
if_debug1('W', " %d", *tp);
130
if_debug0('W', "\n");
137
* Copy from tmp to dst, taking into account PixelOut vs. PixelIn sizes
138
* We do the conversion from PixelIn to PixelOut here (if needed)
139
* since if the Y is scaled down we will convert less often.
140
* This is simpler because we can treat all columns identically
141
* without regard to the number of samples per pixel.
144
idownscale_y(void /*PixelOut */ *dst, const void /* PixelIn */ *tmp,
145
stream_ISpecialDownScale_state *const ss)
147
int kn = ss->params.WidthOut * ss->params.Colors;
150
if_debug0('W', "[W]idownscale_y: ");
152
if (ss->sizeofPixelOut == 1) {
153
if (ss->sizeofPixelIn == 1) {
154
const byte *pp = (byte *)tmp;
156
for ( kc = 0; kc < kn; ++kc, pp++ ) {
157
if_debug1('W', " %d", *pp);
158
((byte *)dst)[kc] = *pp;
160
} else { /* sizeofPixelIn == 2 */
161
const bits16 *pp = (bits16 *)tmp;
163
for ( kc = 0; kc < kn; ++kc, pp++ ) {
164
if_debug1('W', " %d", *pp);
165
((byte *)dst)[kc] = frac2byte(*pp);
168
} else { /* sizeofPixelOut == 2 */
169
if (ss->sizeofPixelIn == 1) {
170
const byte *pp = (byte *)tmp;
172
for ( kc = 0; kc < kn; ++kc, pp++ ) {
173
if_debug1('W', " %d", *pp);
174
((bits16 *)dst)[kc] = byte2frac(*pp);
176
} else { /* sizeofPixelIn == 2 */
177
const bits16 *pp = (bits16 *)tmp;
179
for ( kc = 0; kc < kn; ++kc, pp++ ) {
180
if_debug1('W', " %d", *pp);
181
((bits16 *)dst)[kc] = *pp;
188
/* ------ Stream implementation ------ */
191
/* Forward references */
192
static void s_ISpecialDownScale_release(stream_state * st);
194
/* Set default parameter values (actually, just clear pointers). */
196
s_ISpecialDownScale_set_defaults(stream_state * st)
198
stream_ISpecialDownScale_state *const ss = (stream_ISpecialDownScale_state *) st;
205
/* Initialize the filter. */
207
s_ISpecialDownScale_init(stream_state * st)
209
stream_ISpecialDownScale_state *const ss = (stream_ISpecialDownScale_state *) st;
210
gs_memory_t *mem = ss->memory;
212
ss->sizeofPixelIn = ss->params.BitsPerComponentIn / 8;
213
ss->sizeofPixelOut = ss->params.BitsPerComponentOut / 8;
215
ss->src_size = ss->params.WidthIn * ss->sizeofPixelIn * ss->params.Colors;
216
ss->dst_size = ss->params.WidthOut * ss->sizeofPixelOut * ss->params.Colors;
218
/* Initialize destination DDAs. */
220
ss->src_offset = ss->dst_offset = 0;
221
dda_init(ss->dda_x, 0, ss->params.WidthIn, ss->params.WidthOut);
222
ss->dda_x_init = ss->dda_x;
223
ss->src_y = ss->dst_y = 0;
224
dda_init(ss->dda_y, 0, ss->params.HeightOut, ss->params.HeightIn);
227
/* create intermediate image to hold horizontal zoom */
228
ss->tmp = gs_alloc_byte_array(mem, ss->params.WidthOut * ss->params.Colors,
229
ss->sizeofPixelIn, "image_scale tmp");
230
/* Allocate buffers for 1 row of source and destination. */
231
ss->dst = gs_alloc_byte_array(mem, ss->params.WidthOut * ss->params.Colors,
232
ss->sizeofPixelOut, "image_scale dst");
233
ss->src = gs_alloc_byte_array(mem, ss->params.WidthIn * ss->params.Colors,
234
ss->sizeofPixelIn, "image_scale src");
235
if (ss->tmp == 0 || ss->dst == 0 || ss->src == 0) {
236
s_ISpecialDownScale_release(st);
238
/****** WRONG ******/
245
/* Process a buffer. Note that this handles Encode and Decode identically. */
247
s_ISpecialDownScale_process(stream_state * st, stream_cursor_read * pr,
248
stream_cursor_write * pw, bool last)
250
stream_ISpecialDownScale_state *const ss = (stream_ISpecialDownScale_state *) st;
251
uint cur_y = dda_current(ss->dda_y);
253
/* Check whether we need to deliver any output. */
256
if (cur_y > ss->dst_y) {
257
/* Deliver some or all of the current scaled row. */
258
/* to generate a vertically scaled output row. */
259
uint wleft = pw->limit - pw->ptr;
261
if (ss->dst_y == ss->params.HeightOut)
265
if (ss->dst_offset == 0) {
268
if (wleft >= ss->dst_size) { /* We can scale the row directly into the output. */
270
pw->ptr += ss->dst_size;
271
} else { /* We'll have to buffer the row. */
274
/* Apply filter to zoom vertically from tmp to dst. */
275
idownscale_y(row, ss->tmp, ss);
276
/* Idiotic C coercion rules allow T* and void* to be */
277
/* inter-assigned freely, but not compared! */
278
if ((void *)row != ss->dst) /* no buffering */
280
} { /* We're delivering a buffered output row. */
281
uint wcount = ss->dst_size - ss->dst_offset;
282
uint ncopy = min(wleft, wcount);
284
memcpy(pw->ptr + 1, (byte *) ss->dst + ss->dst_offset, ncopy);
286
ss->dst_offset += ncopy;
291
/* Advance to the next output row. */
295
/* Read input data and scale horizontally into tmp. */
298
uint rleft = pr->limit - pr->ptr;
299
uint rcount = ss->src_size - ss->src_offset;
302
return 0; /* need more input */
303
if (ss->src_y >= ss->params.HeightIn)
305
if (rleft >= rcount) { /* We're going to fill up a row. */
308
if (ss->src_offset == 0) { /* We have a complete row. Read the data */
309
/* directly from the input. */
311
} else { /* We're buffering a row in src. */
313
memcpy((byte *) ss->src + ss->src_offset, pr->ptr + 1,
317
/* Apply filter to zoom horizontally from src to tmp. */
318
if_debug2('w', "[w]idownscale_x y = %d to tmp row %d\n",
319
ss->src_y, (ss->src_y % MAX_ISCALE_SUPPORT));
320
idownscale_x(ss->tmp, row, ss);
323
cur_y = dda_next(ss->dda_y);
325
} else { /* We don't have a complete row. Copy data to src buffer. */
326
memcpy((byte *) ss->src + ss->src_offset, pr->ptr + 1, rleft);
327
ss->src_offset += rleft;
334
/* Release the filter's storage. */
336
s_ISpecialDownScale_release(stream_state * st)
338
stream_ISpecialDownScale_state *const ss = (stream_ISpecialDownScale_state *) st;
339
gs_memory_t *mem = ss->memory;
341
gs_free_object(mem, (void *)ss->src, "image_scale src"); /* no longer const */
343
gs_free_object(mem, ss->dst, "image_scale dst");
345
gs_free_object(mem, ss->tmp, "image_scale tmp");
349
/* Stream template */
350
const stream_template s_ISpecialDownScale_template = {
351
&st_ISpecialDownScale_state, s_ISpecialDownScale_init, s_ISpecialDownScale_process, 1, 1,
352
s_ISpecialDownScale_release, s_ISpecialDownScale_set_defaults