1 /* Copyright (C) 1996, 1997, 1998, 1999 Aladdin Enterprises. All rights reserved.
2
3 This software is provided AS-IS with no warranty, either express or
4 implied.
5
6 This software is distributed under license and may not be copied,
7 modified or distributed except as expressly authorized under the terms
8 of the license contained in the file LICENSE in this distribution.
9
10 For more information about licensing, please refer to
11 http://www.ghostscript.com/licensing/. For information on
12 commercial licensing, go to http://www.artifex.com/licensing/ or
13 contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14 San Rafael, CA 94903, U.S.A., +1(415)492-9861.
15 */
16
17 /* $Id: gxiscale.c,v 1.9 2005/06/20 08:59:23 igor Exp $ */
18 /* Interpolated image procedures */
19 #include "gx.h"
20 #include "math_.h"
21 #include "memory_.h"
22 #include "gpcheck.h"
23 #include "gserrors.h"
24 #include "gxfixed.h"
25 #include "gxfrac.h"
26 #include "gxarith.h"
27 #include "gxmatrix.h"
28 #include "gsccolor.h"
29 #include "gspaint.h"
30 #include "gxdevice.h"
31 #include "gxcmap.h"
32 #include "gxdcolor.h"
33 #include "gxistate.h"
34 #include "gxdevmem.h"
35 #include "gxcpath.h"
36 #include "gximage.h"
37 #include "stream.h" /* for s_alloc_state */
38 #include "siinterp.h" /* for spatial interpolation */
39 #include "siscale.h" /* for Mitchell filtering */
40
41 /*
42 * Define whether we are using Mitchell filtering or spatial
43 * interpolation to implement Interpolate. (The latter doesn't work yet.)
44 */
45 #define USE_MITCHELL_FILTER
46
47 /* ------ Strategy procedure ------ */
48
49 /* Check the prototype. */
50 iclass_proc(gs_image_class_0_interpolate);
51
52 /* If we're interpolating, use special logic. */
53 private irender_proc(image_render_interpolate);
54 irender_proc_t
gs_image_class_0_interpolate(gx_image_enum * penum)55 gs_image_class_0_interpolate(gx_image_enum * penum)
56 {
57 const gs_imager_state *pis = penum->pis;
58 gs_memory_t *mem = penum->memory;
59 stream_image_scale_params_t iss;
60 stream_image_scale_state *pss;
61 const stream_template *template;
62 byte *line;
63 const gs_color_space *pcs = penum->pcs;
64 const gs_color_space *pccs;
65 gs_point dst_xy;
66 uint in_size;
67
68 if (!penum->interpolate)
69 return 0;
70 if (penum->use_mask_color || penum->posture != image_portrait ||
71 penum->masked || penum->alpha ||
72 (penum->dev->color_info.num_components == 1 &&
73 penum->dev->color_info.max_gray < 15) ||
74 (penum->dev->color_info.num_components > 1 &&
75 penum->dev->color_info.max_color < 15)
76 ) {
77 /* We can't handle these cases yet. Punt. */
78 penum->interpolate = false;
79 return 0;
80 }
81 /*
82 * USE_CONSERVATIVE_INTERPOLATION_RULES is normally NOT defined since
83 * the MITCHELL digital filter seems OK as long as we are going out to
84 * a device that can produce > 15 shades.
85 */
86 #if defined(USE_MITCHELL_FILTER) && defined(USE_CONSERVATIVE_INTERPOLATION_RULES)
87 /*
88 * We interpolate using a digital filter, rather than Adobe's
89 * spatial interpolation algorithm: this produces very bad-looking
90 * results if the input resolution is close to the output resolution,
91 * especially if the input has low color resolution, so we resort to
92 * some hack tests on the input color resolution and scale to suppress
93 * interpolation if we think the result would look especially bad.
94 * If we used Adobe's spatial interpolation approach, we wouldn't need
95 * to do this, but the spatial interpolation filter doesn't work yet.
96 */
97 if (penum->bps < 4 || penum->bps * penum->spp < 8 ||
98 (fabs(penum->matrix.xx) <= 5 && fabs(penum->matrix.yy <= 5))
99 ) {
100 penum->interpolate = false;
101 return 0;
102 }
103 #endif
104 /* Non-ANSI compilers require the following casts: */
105 gs_distance_transform((float)penum->rect.w, (float)penum->rect.h,
106 &penum->matrix, &dst_xy);
107 iss.BitsPerComponentOut = sizeof(frac) * 8;
108 iss.MaxValueOut = frac_1;
109 iss.WidthOut = (int)ceil(fabs(dst_xy.x));
110 iss.HeightOut = (int)ceil(fabs(dst_xy.y));
111 iss.WidthIn = penum->rect.w;
112 iss.HeightIn = penum->rect.h;
113 pccs = cs_concrete_space(pcs, pis);
114 iss.Colors = cs_num_components(pccs);
115 if (penum->bps <= 8 && penum->device_color) {
116 iss.BitsPerComponentIn = 8;
117 iss.MaxValueIn = 0xff;
118 in_size =
119 (penum->matrix.xx < 0 ?
120 /* We need a buffer for reversing each scan line. */
121 iss.WidthIn * iss.Colors : 0);
122 } else {
123 iss.BitsPerComponentIn = sizeof(frac) * 8;
124 iss.MaxValueIn = frac_1;
125 in_size = round_up(iss.WidthIn * iss.Colors * sizeof(frac),
126 align_bitmap_mod);
127 }
128 /* Allocate a buffer for one source/destination line. */
129 {
130 uint out_size =
131 iss.WidthOut * max(iss.Colors * (iss.BitsPerComponentOut / 8),
132 arch_sizeof_color_index);
133
134 line = gs_alloc_bytes(mem, in_size + out_size,
135 "image scale src+dst line");
136 }
137 #ifdef USE_MITCHELL_FILTER
138 template = &s_IScale_template;
139 #else
140 template = &s_IIEncode_template;
141 #endif
142 pss = (stream_image_scale_state *)
143 s_alloc_state(mem, template->stype, "image scale state");
144 if (line == 0 || pss == 0 ||
145 (pss->params = iss, pss->template = template,
146 (*pss->template->init) ((stream_state *) pss) < 0)
147 ) {
148 gs_free_object(mem, pss, "image scale state");
149 gs_free_object(mem, line, "image scale src+dst line");
150 /* Try again without interpolation. */
151 penum->interpolate = false;
152 return 0;
153 }
154 penum->line = line;
155 penum->scaler = pss;
156 penum->line_xy = 0;
157 {
158 gx_dda_fixed x0;
159
160 x0 = penum->dda.pixel0.x;
161 if (penum->matrix.xx < 0)
162 dda_advance(x0, penum->rect.w);
163 penum->xyi.x = fixed2int_pixround(dda_current(x0));
164 }
165 penum->xyi.y = fixed2int_pixround(dda_current(penum->dda.pixel0.y));
166 if_debug0('b', "[b]render=interpolate\n");
167 return &image_render_interpolate;
168 }
169
170 /* ------ Rendering for interpolated images ------ */
171
172 private int
image_render_interpolate(gx_image_enum * penum,const byte * buffer,int data_x,uint iw,int h,gx_device * dev)173 image_render_interpolate(gx_image_enum * penum, const byte * buffer,
174 int data_x, uint iw, int h, gx_device * dev)
175 {
176 stream_image_scale_state *pss = penum->scaler;
177 const gs_imager_state *pis = penum->pis;
178 const gs_color_space *pcs = penum->pcs;
179 gs_logical_operation_t lop = penum->log_op;
180 int c = pss->params.Colors;
181 stream_cursor_read r;
182 stream_cursor_write w;
183 byte *out = penum->line;
184
185 if (h != 0) {
186 /* Convert the unpacked data to concrete values in */
187 /* the source buffer. */
188 int sizeofPixelIn = pss->params.BitsPerComponentIn / 8;
189 uint row_size = pss->params.WidthIn * c * sizeofPixelIn;
190 const byte *bdata = buffer + data_x * c * sizeofPixelIn;
191
192 if (sizeofPixelIn == 1) {
193 /* Easy case: 8-bit device color values. */
194 if (penum->matrix.xx >= 0) {
195 /* Use the input data directly. */
196 r.ptr = bdata - 1;
197 } else {
198 /* Mirror the data in X. */
199 const byte *p = bdata + row_size - c;
200 byte *q = out;
201 int i;
202
203 for (i = 0; i < pss->params.WidthIn; p -= c, q += c, ++i)
204 memcpy(q, p, c);
205 r.ptr = out - 1;
206 out = q;
207 }
208 } else {
209 /* Messy case: concretize each sample. */
210 int bps = penum->bps;
211 int dc = penum->spp;
212 const byte *pdata = bdata;
213 frac *psrc = (frac *) penum->line;
214 gs_client_color cc;
215 int i;
216
217 r.ptr = (byte *) psrc - 1;
218 if_debug0('B', "[B]Concrete row:\n[B]");
219 for (i = 0; i < pss->params.WidthIn; i++, psrc += c) {
220 int j;
221
222 if (bps <= 8)
223 for (j = 0; j < dc; ++pdata, ++j) {
224 decode_sample(*pdata, cc, j);
225 } else /* bps == 12 */
226 for (j = 0; j < dc; pdata += sizeof(frac), ++j) {
227 decode_frac(*(const frac *)pdata, cc, j);
228 }
229 (*pcs->type->concretize_color) (&cc, pcs, psrc, pis);
230 #ifdef DEBUG
231 if (gs_debug_c('B')) {
232 int ci;
233
234 for (ci = 0; ci < c; ++ci)
235 dprintf2("%c%04x", (ci == 0 ? ' ' : ','), psrc[ci]);
236 }
237 #endif
238 }
239 out += round_up(pss->params.WidthIn * c * sizeof(frac),
240 align_bitmap_mod);
241 if_debug0('B', "\n");
242 }
243 r.limit = r.ptr + row_size;
244 } else /* h == 0 */
245 r.ptr = 0, r.limit = 0;
246
247 /*
248 * Process input and/or collect output. By construction, the pixels are
249 * 1-for-1 with the device, but the Y coordinate might be inverted.
250 */
251
252 {
253 int xo = penum->xyi.x;
254 int yo = penum->xyi.y;
255 int width = pss->params.WidthOut;
256 int sizeofPixelOut = pss->params.BitsPerComponentOut / 8;
257 int dy;
258 const gs_color_space *pconcs = cs_concrete_space(pcs, pis);
259 int bpp = dev->color_info.depth;
260 uint raster = bitmap_raster(width * bpp);
261
262 if (penum->matrix.yy > 0)
263 dy = 1;
264 else
265 dy = -1, yo--;
266 for (;;) {
267 int ry = yo + penum->line_xy * dy;
268 int x;
269 const frac *psrc;
270 gx_device_color devc;
271 int status, code;
272
273 DECLARE_LINE_ACCUM_COPY(out, bpp, xo);
274
275 w.limit = out + width *
276 max(c * sizeofPixelOut, arch_sizeof_color_index) - 1;
277 w.ptr = w.limit - width * c * sizeofPixelOut;
278 psrc = (const frac *)(w.ptr + 1);
279 status = (*pss->template->process)
280 ((stream_state *) pss, &r, &w, h == 0);
281 if (status < 0 && status != EOFC)
282 return_error(gs_error_ioerror);
283 if (w.ptr == w.limit) {
284 int xe = xo + width;
285
286 if_debug1('B', "[B]Interpolated row %d:\n[B]",
287 penum->line_xy);
288 for (x = xo; x < xe;) {
289 #ifdef DEBUG
290 if (gs_debug_c('B')) {
291 int ci;
292
293 for (ci = 0; ci < c; ++ci)
294 dprintf2("%c%04x", (ci == 0 ? ' ' : ','),
295 psrc[ci]);
296 }
297 #endif
298 code = (*pconcs->type->remap_concrete_color)
299 (psrc, pcs, &devc, pis, dev, gs_color_select_source);
300 if (code < 0)
301 return code;
302 if (color_is_pure(&devc)) {
303 /* Just pack colors into a scan line. */
304 gx_color_index color = devc.colors.pure;
305
306 /* Skip RGB runs quickly. */
307 if (c == 3) {
308 do {
309 LINE_ACCUM(color, bpp);
310 x++, psrc += 3;
311 } while (x < xe && psrc[-3] == psrc[0] &&
312 psrc[-2] == psrc[1] &&
313 psrc[-1] == psrc[2]);
314 } else {
315 LINE_ACCUM(color, bpp);
316 x++, psrc += c;
317 }
318 } else {
319 int rcode;
320
321 LINE_ACCUM_COPY(dev, out, bpp, xo, x, raster, ry);
322 rcode = gx_fill_rectangle_device_rop(x, ry,
323 1, 1, &devc, dev, lop);
324 if (rcode < 0)
325 return rcode;
326 LINE_ACCUM_SKIP(bpp);
327 l_xprev = x + 1;
328 x++, psrc += c;
329 }
330 }
331 LINE_ACCUM_COPY(dev, out, bpp, xo, x, raster, ry);
332 penum->line_xy++;
333 if_debug0('B', "\n");
334 }
335 if ((status == 0 && r.ptr == r.limit) || status == EOFC)
336 break;
337 }
338 }
339
340 return (h == 0 ? 0 : 1);
341 }
342