xref: /plan9/sys/src/cmd/gs/src/gxiscale.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
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