1 /* Copyright (C) 1997, 1998, 1999, 2000 artofcode LLC.  All rights reserved.
2 
3   This program is free software; you can redistribute it and/or modify it
4   under the terms of the GNU General Public License as published by the
5   Free Software Foundation; either version 2 of the License, or (at your
6   option) any later version.
7 
8   This program is distributed in the hope that it will be useful, but
9   WITHOUT ANY WARRANTY; without even the implied warranty of
10   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11   General Public License for more details.
12 
13   You should have received a copy of the GNU General Public License along
14   with this program; if not, write to the Free Software Foundation, Inc.,
15   59 Temple Place, Suite 330, Boston, MA, 02111-1307.
16 
17 */
18 
19 /*$Id: gximage3.c,v 1.6.6.1.2.1 2003/01/17 00:49:04 giles Exp $ */
20 /* ImageType 3 image implementation */
21 #include "math_.h"		/* for ceil, floor */
22 #include "memory_.h"
23 #include "gx.h"
24 #include "gserrors.h"
25 #include "gsbitops.h"
26 #include "gscspace.h"
27 #include "gsstruct.h"
28 #include "gxdevice.h"
29 #include "gxdevmem.h"
30 #include "gxclipm.h"
31 #include "gximage3.h"
32 #include "gxistate.h"
33 
34 /* Forward references */
35 private dev_proc_begin_typed_image(gx_begin_image3);
36 private image_enum_proc_plane_data(gx_image3_plane_data);
37 private image_enum_proc_end_image(gx_image3_end_image);
38 private image_enum_proc_flush(gx_image3_flush);
39 private image_enum_proc_planes_wanted(gx_image3_planes_wanted);
40 
41 /* GC descriptor */
42 private_st_gs_image3();
43 
44 /* Define the image type for ImageType 3 images. */
45 const gx_image_type_t gs_image_type_3 = {
46     &st_gs_image3, gx_begin_image3, gx_data_image_source_size,
47     gx_image_no_sput, gx_image_no_sget, gx_image_default_release, 3
48 };
49 private const gx_image_enum_procs_t image3_enum_procs = {
50     gx_image3_plane_data, gx_image3_end_image,
51     gx_image3_flush, gx_image3_planes_wanted
52 };
53 
54 /* Initialize an ImageType 3 image. */
55 void
gs_image3_t_init(gs_image3_t * pim,const gs_color_space * color_space,gs_image3_interleave_type_t interleave_type)56 gs_image3_t_init(gs_image3_t * pim, const gs_color_space * color_space,
57 		 gs_image3_interleave_type_t interleave_type)
58 {
59     gs_pixel_image_t_init((gs_pixel_image_t *) pim, color_space);
60     pim->type = &gs_image_type_3;
61     pim->InterleaveType = interleave_type;
62     gs_data_image_t_init(&pim->MaskDict, -1);
63 }
64 
65 /*
66  * We implement ImageType 3 images by interposing a mask clipper in
67  * front of an ordinary ImageType 1 image.  Note that we build up the
68  * mask row-by-row as we are processing the image.
69  *
70  * We export a generalized form of the begin_image procedure for use by
71  * the PDF and PostScript writers.
72  */
73 typedef struct gx_image3_enum_s {
74     gx_image_enum_common;
75     gx_device *mdev;		/* gx_device_memory in default impl. */
76     gx_device *pcdev;		/* gx_device_mask_clip in default impl. */
77     gx_image_enum_common_t *mask_info;
78     gx_image_enum_common_t *pixel_info;
79     gs_image3_interleave_type_t InterleaveType;
80     int num_components;		/* (not counting mask) */
81     int bpc;			/* BitsPerComponent */
82     gs_memory_t *memory;
83     int mask_width, mask_height, mask_full_height;
84     int pixel_width, pixel_height, pixel_full_height;
85     byte *mask_data;		/* (if chunky) */
86     byte *pixel_data;		/* (if chunky) */
87     /* The following are the only members that change dynamically. */
88     int mask_y;
89     int pixel_y;
90     int mask_skip;		/* # of mask rows to skip, see below */
91 } gx_image3_enum_t;
92 
93 extern_st(st_gx_image_enum_common);
94 gs_private_st_suffix_add6(st_image3_enum, gx_image3_enum_t, "gx_image3_enum_t",
95   image3_enum_enum_ptrs, image3_enum_reloc_ptrs, st_gx_image_enum_common,
96   mdev, pcdev, pixel_info, mask_info, pixel_data, mask_data);
97 
98 /* Define the default implementation of ImageType 3 processing. */
99 private IMAGE3_MAKE_MID_PROC(make_mid_default); /* check prototype */
100 private int
make_mid_default(gx_device ** pmidev,gx_device * dev,int width,int height,gs_memory_t * mem)101 make_mid_default(gx_device **pmidev, gx_device *dev, int width, int height,
102 		 gs_memory_t *mem)
103 {
104     gx_device_memory *midev =
105 	gs_alloc_struct(mem, gx_device_memory, &st_device_memory,
106 			"make_mid_default");
107     int code;
108 
109     if (midev == 0)
110 	return_error(gs_error_VMerror);
111     gs_make_mem_mono_device(midev, mem, NULL);
112     midev->bitmap_memory = mem;
113     midev->width = width;
114     midev->height = height;
115     gx_device_fill_in_procs((gx_device *)midev);
116     code = dev_proc(midev, open_device)((gx_device *)midev);
117     if (code < 0) {
118 	gs_free_object(mem, midev, "make_mid_default");
119 	return code;
120     }
121     midev->is_open = true;
122     dev_proc(midev, fill_rectangle)
123 	((gx_device *)midev, 0, 0, width, height, (gx_color_index)0);
124     *pmidev = (gx_device *)midev;
125     return 0;
126 }
127 private IMAGE3_MAKE_MCDE_PROC(make_mcde_default);  /* check prototype */
128 private int
make_mcde_default(gx_device * dev,const gs_imager_state * pis,const gs_matrix * pmat,const gs_image_common_t * pic,const gs_int_rect * prect,const gx_drawing_color * pdcolor,const gx_clip_path * pcpath,gs_memory_t * mem,gx_image_enum_common_t ** pinfo,gx_device ** pmcdev,gx_device * midev,gx_image_enum_common_t * pminfo,const gs_int_point * origin)129 make_mcde_default(gx_device *dev, const gs_imager_state *pis,
130 		  const gs_matrix *pmat, const gs_image_common_t *pic,
131 		  const gs_int_rect *prect, const gx_drawing_color *pdcolor,
132 		  const gx_clip_path *pcpath, gs_memory_t *mem,
133 		  gx_image_enum_common_t **pinfo,
134 		  gx_device **pmcdev, gx_device *midev,
135 		  gx_image_enum_common_t *pminfo,
136 		  const gs_int_point *origin)
137 {
138     gx_device_memory *const mdev = (gx_device_memory *)midev;
139     gx_device_mask_clip *mcdev =
140 	gs_alloc_struct(mem, gx_device_mask_clip, &st_device_mask_clip,
141 			"make_mcde_default");
142     gx_strip_bitmap bits;	/* only gx_bitmap */
143     int code;
144 
145     if (mcdev == 0)
146 	return_error(gs_error_VMerror);
147     bits.data = mdev->base;
148     bits.raster = mdev->raster;
149     bits.size.x = mdev->width;
150     bits.size.y = mdev->height;
151     bits.id = gx_no_bitmap_id;
152     code = gx_mask_clip_initialize(mcdev, &gs_mask_clip_device,
153 				   (const gx_bitmap *)&bits, dev,
154 				   origin->x, origin->y, mem);
155     if (code < 0) {
156 	gs_free_object(mem, mcdev, "make_mcde_default");
157 	return code;
158     }
159     mcdev->tiles = bits;
160     code = dev_proc(mcdev, begin_typed_image)
161 	((gx_device *)mcdev, pis, pmat, pic, prect, pdcolor, pcpath, mem,
162 	 pinfo);
163     if (code < 0) {
164 	gs_free_object(mem, mcdev, "make_mcde_default");
165 	return code;
166     }
167     *pmcdev = (gx_device *)mcdev;
168     return 0;
169 }
170 private int
gx_begin_image3(gx_device * dev,const gs_imager_state * pis,const gs_matrix * pmat,const gs_image_common_t * pic,const gs_int_rect * prect,const gx_drawing_color * pdcolor,const gx_clip_path * pcpath,gs_memory_t * mem,gx_image_enum_common_t ** pinfo)171 gx_begin_image3(gx_device * dev,
172 		const gs_imager_state * pis, const gs_matrix * pmat,
173 		const gs_image_common_t * pic, const gs_int_rect * prect,
174 		const gx_drawing_color * pdcolor, const gx_clip_path * pcpath,
175 		gs_memory_t * mem, gx_image_enum_common_t ** pinfo)
176 {
177     return gx_begin_image3_generic(dev, pis, pmat, pic, prect, pdcolor,
178 				   pcpath, mem, make_mid_default,
179 				   make_mcde_default, pinfo);
180 }
181 
182 /*
183  * Begin a generic ImageType 3 image, with client handling the creation of
184  * the mask image and mask clip devices.
185  */
186 private bool check_image3_extent(P2(floatp mask_coeff, floatp data_coeff));
187 int
gx_begin_image3_generic(gx_device * dev,const gs_imager_state * pis,const gs_matrix * pmat,const gs_image_common_t * pic,const gs_int_rect * prect,const gx_drawing_color * pdcolor,const gx_clip_path * pcpath,gs_memory_t * mem,image3_make_mid_proc_t make_mid,image3_make_mcde_proc_t make_mcde,gx_image_enum_common_t ** pinfo)188 gx_begin_image3_generic(gx_device * dev,
189 			const gs_imager_state *pis, const gs_matrix *pmat,
190 			const gs_image_common_t *pic, const gs_int_rect *prect,
191 			const gx_drawing_color *pdcolor,
192 			const gx_clip_path *pcpath, gs_memory_t *mem,
193 			image3_make_mid_proc_t make_mid,
194 			image3_make_mcde_proc_t make_mcde,
195 			gx_image_enum_common_t **pinfo)
196 {
197     const gs_image3_t *pim = (const gs_image3_t *)pic;
198     gx_image3_enum_t *penum;
199     gs_int_rect mask_rect, data_rect;
200     gx_device *mdev = 0;
201     gx_device *pcdev = 0;
202     gs_image_t i_pixel, i_mask;
203     gs_matrix mi_pixel, mi_mask, mat;
204     gs_rect mrect;
205     gs_int_point origin;
206     int code;
207 
208     /* Validate the parameters. */
209     if (pim->Height <= 0 || pim->MaskDict.Height <= 0)
210 	return_error(gs_error_rangecheck);
211     switch (pim->InterleaveType) {
212 	default:
213 	    return_error(gs_error_rangecheck);
214 	case interleave_chunky:
215 	    if (pim->MaskDict.Width != pim->Width ||
216 		pim->MaskDict.Height != pim->Height ||
217 		pim->MaskDict.BitsPerComponent != pim->BitsPerComponent ||
218 		pim->format != gs_image_format_chunky
219 		)
220 		return_error(gs_error_rangecheck);
221 	    break;
222 	case interleave_scan_lines:
223 	    if (pim->MaskDict.Height % pim->Height != 0 &&
224 		pim->Height % pim->MaskDict.Height != 0
225 		)
226 		return_error(gs_error_rangecheck);
227 	    /* falls through */
228 	case interleave_separate_source:
229 	    if (pim->MaskDict.BitsPerComponent != 1)
230 		return_error(gs_error_rangecheck);
231     }
232     if (!check_image3_extent(pim->ImageMatrix.xx,
233 			     pim->MaskDict.ImageMatrix.xx) ||
234 	!check_image3_extent(pim->ImageMatrix.xy,
235 			     pim->MaskDict.ImageMatrix.xy) ||
236 	!check_image3_extent(pim->ImageMatrix.yx,
237 			     pim->MaskDict.ImageMatrix.yx) ||
238 	!check_image3_extent(pim->ImageMatrix.yy,
239 			     pim->MaskDict.ImageMatrix.yy)
240 	)
241 	return_error(gs_error_rangecheck);
242     if ((code = gs_matrix_invert(&pim->ImageMatrix, &mi_pixel)) < 0 ||
243 	(code = gs_matrix_invert(&pim->MaskDict.ImageMatrix, &mi_mask)) < 0
244 	)
245 	return code;
246     if (fabs(mi_pixel.tx - mi_mask.tx) >= 0.5 ||
247 	fabs(mi_pixel.ty - mi_mask.ty) >= 0.5
248 	)
249 	return_error(gs_error_rangecheck);
250     {
251 	gs_point ep, em;
252 
253 	if ((code = gs_point_transform(pim->Width, pim->Height, &mi_pixel,
254 				       &ep)) < 0 ||
255 	    (code = gs_point_transform(pim->MaskDict.Width,
256 				       pim->MaskDict.Height, &mi_mask,
257 				       &em)) < 0
258 	    )
259 	    return code;
260 	if (fabs(ep.x - em.x) >= 0.5 || fabs(ep.y - em.y) >= 0.5)
261 	    return_error(gs_error_rangecheck);
262     }
263     penum = gs_alloc_struct(mem, gx_image3_enum_t, &st_image3_enum,
264 			    "gx_begin_image3");
265     if (penum == 0)
266 	return_error(gs_error_VMerror);
267     penum->num_components =
268 	gs_color_space_num_components(pim->ColorSpace);
269     gx_image_enum_common_init((gx_image_enum_common_t *) penum,
270 			      (const gs_data_image_t *)pim,
271 			      &image3_enum_procs, dev,
272 			      1 + penum->num_components,
273 			      pim->format);
274     /* Initialize pointers now in case we bail out. */
275     penum->mask_data = 0;
276     penum->pixel_data = 0;
277     if (prect) {
278 	long lmw = pim->MaskDict.Width, lmh = pim->MaskDict.Height;
279 
280 	data_rect = *prect;
281 	mask_rect.p.x = (int)(data_rect.p.x * lmw / pim->Width);
282 	mask_rect.p.y = (int)(data_rect.p.y * lmh / pim->Height);
283 	mask_rect.q.x = (int)((data_rect.q.x + pim->Width - 1) * lmw /
284 			      pim->Width);
285 	mask_rect.q.y = (int)((data_rect.q.y + pim->Height - 1) * lmh /
286 			      pim->Height);
287     } else {
288 	mask_rect.p.x = mask_rect.p.y = 0;
289 	mask_rect.q.x = pim->MaskDict.Width;
290 	mask_rect.q.y = pim->MaskDict.Height;
291 	data_rect.p.x = data_rect.p.y = 0;
292 	data_rect.q.x = pim->Width;
293 	data_rect.q.y = pim->Height;
294     }
295     penum->mask_width = mask_rect.q.x - mask_rect.p.x;
296     penum->mask_height = mask_rect.q.y - mask_rect.p.y;
297     penum->mask_full_height = pim->MaskDict.Height;
298     penum->mask_y = 0;
299     penum->mask_skip = 0;
300     penum->pixel_width = data_rect.q.x - data_rect.p.x;
301     penum->pixel_height = data_rect.q.y - data_rect.p.y;
302     penum->pixel_full_height = pim->Height;
303     penum->pixel_y = 0;
304     penum->mask_info = 0;
305     penum->pixel_info = 0;
306     if (pim->InterleaveType == interleave_chunky) {
307 	/* Allocate row buffers for the mask and pixel data. */
308 	penum->pixel_data =
309 	    gs_alloc_bytes(mem,
310 			   (penum->pixel_width * pim->BitsPerComponent *
311 			    penum->num_components + 7) >> 3,
312 			   "gx_begin_image3(pixel_data)");
313 	penum->mask_data =
314 	    gs_alloc_bytes(mem, (penum->mask_width + 7) >> 3,
315 			   "gx_begin_image3(mask_data)");
316 	if (penum->pixel_data == 0 || penum->mask_data == 0) {
317 	    code = gs_note_error(gs_error_VMerror);
318 	    goto out1;
319 	}
320     }
321     penum->InterleaveType = pim->InterleaveType;
322     penum->bpc = pim->BitsPerComponent;
323     penum->memory = mem;
324     mrect.p.x = mrect.p.y = 0;
325     mrect.q.x = pim->MaskDict.Width;
326     mrect.q.y = pim->MaskDict.Height;
327     if (pmat == 0)
328 	pmat = &ctm_only(pis);
329     if ((code = gs_matrix_multiply(&mi_mask, pmat, &mat)) < 0 ||
330 	(code = gs_bbox_transform(&mrect, &mat, &mrect)) < 0
331 	)
332 	return code;
333     origin.x = floor(mrect.p.x);
334     origin.y = floor(mrect.p.y);
335     code = make_mid(&mdev, dev, (int)ceil(mrect.q.x) - origin.x,
336 		    (int)ceil(mrect.q.y) - origin.y, mem);
337     if (code < 0)
338 	goto out1;
339     penum->mdev = mdev;
340     gs_image_t_init_mask(&i_mask, false);
341     i_mask.adjust = false;
342     {
343 	const gx_image_type_t *type1 = i_mask.type;
344 
345 	*(gs_data_image_t *)&i_mask = pim->MaskDict;
346 	i_mask.type = type1;
347 	i_mask.BitsPerComponent = 1;
348     }
349     {
350 	gx_drawing_color dcolor;
351 	gs_matrix m_mat;
352 
353 	color_set_pure(&dcolor, 1);
354 	/*
355 	 * Adjust the translation for rendering the mask to include a
356 	 * negative translation by origin.{x,y} in device space.
357 	 */
358 	m_mat = *pmat;
359 	m_mat.tx -= origin.x;
360 	m_mat.ty -= origin.y;
361 	/*
362 	 * Note that pis = NULL here, since we don't want to have to
363 	 * create another imager state with default log_op, etc.
364 	 */
365 	code = gx_device_begin_typed_image(mdev, NULL, &m_mat,
366 					   (const gs_image_common_t *)&i_mask,
367 					   &mask_rect, &dcolor, NULL, mem,
368 					   &penum->mask_info);
369 	if (code < 0)
370 	    goto out2;
371     }
372     gs_image_t_init(&i_pixel, pim->ColorSpace);
373     {
374 	const gx_image_type_t *type1 = i_pixel.type;
375 
376 	*(gs_pixel_image_t *)&i_pixel = *(const gs_pixel_image_t *)pim;
377 	i_pixel.type = type1;
378     }
379     code = make_mcde(dev, pis, pmat, (const gs_image_common_t *)&i_pixel,
380 		     prect, pdcolor, pcpath, mem, &penum->pixel_info,
381 		     &pcdev, mdev, penum->mask_info, &origin);
382     if (code < 0)
383 	goto out3;
384     penum->pcdev = pcdev;
385     /*
386      * Set num_planes, plane_widths, and plane_depths from the values in the
387      * enumerators for the mask and the image data.
388      */
389     switch (pim->InterleaveType) {
390     case interleave_chunky:
391 	/* Add the mask data to the depth of the image data. */
392 	penum->num_planes = 1;
393 	penum->plane_widths[0] = pim->Width;
394 	penum->plane_depths[0] =
395 	    penum->pixel_info->plane_depths[0] *
396 	    (penum->num_components + 1) / penum->num_components;
397 	break;
398     case interleave_scan_lines:
399 	/*
400 	 * There is only 1 plane, with dynamically changing width & depth.
401 	 * Initialize it for the mask data, since that is what will be
402 	 * read first.
403 	 */
404 	penum->num_planes = 1;
405 	penum->plane_depths[0] = 1;
406 	penum->plane_widths[0] = pim->MaskDict.Width;
407 	break;
408     case interleave_separate_source:
409 	/* Insert the mask data as a separate plane before the image data. */
410 	penum->num_planes = penum->pixel_info->num_planes + 1;
411 	penum->plane_widths[0] = pim->MaskDict.Width;
412 	penum->plane_depths[0] = 1;
413 	memcpy(&penum->plane_widths[1], &penum->pixel_info->plane_widths[0],
414 	       (penum->num_planes - 1) * sizeof(penum->plane_widths[0]));
415 	memcpy(&penum->plane_depths[1], &penum->pixel_info->plane_depths[0],
416 	       (penum->num_planes - 1) * sizeof(penum->plane_depths[0]));
417 	break;
418     }
419     gx_device_retain(mdev, true); /* will free explicitly */
420     gx_device_retain(pcdev, true); /* ditto */
421     *pinfo = (gx_image_enum_common_t *) penum;
422     return 0;
423   out3:
424     gx_image_end(penum->mask_info, false);
425   out2:
426     gs_closedevice(mdev);
427     gs_free_object(mem, mdev, "gx_begin_image3(mdev)");
428   out1:
429     gs_free_object(mem, penum->mask_data, "gx_begin_image3(mask_data)");
430     gs_free_object(mem, penum->pixel_data, "gx_begin_image3(pixel_data)");
431     gs_free_object(mem, penum, "gx_begin_image3");
432     return code;
433 }
434 private bool
check_image3_extent(floatp mask_coeff,floatp data_coeff)435 check_image3_extent(floatp mask_coeff, floatp data_coeff)
436 {
437     if (mask_coeff == 0)
438 	return data_coeff == 0;
439     if (data_coeff == 0 || (mask_coeff > 0) != (data_coeff > 0))
440 	return false;
441     return true;
442 }
443 
444 /*
445  * Return > 0 if we want more mask now, < 0 if we want more data now,
446  * 0 if we want both.
447  */
448 private int
planes_next(const gx_image3_enum_t * penum)449 planes_next(const gx_image3_enum_t *penum)
450 {
451     /*
452      * The invariant we need to maintain is that we always have at least as
453      * much mask as pixel data, i.e., mask_y / mask_full_height >=
454      * pixel_y / pixel_full_height, or, to avoid floating point,
455      * mask_y * pixel_full_height >= pixel_y * mask_full_height.
456      * We know this condition is true now;
457      * return a value that indicates how to maintain it.
458      */
459     int mask_h = penum->mask_full_height;
460     int pixel_h = penum->pixel_full_height;
461     long current = penum->pixel_y * (long)mask_h -
462 	penum->mask_y * (long)pixel_h;
463 
464 #ifdef DEBUG
465     if (current > 0)
466 	lprintf4("planes_next invariant fails: %d/%d > %d/%d\n",
467 		 penum->pixel_y, penum->pixel_full_height,
468 		 penum->mask_y, penum->mask_full_height);
469 #endif
470     return ((current += mask_h) <= 0 ? -1 :
471 	    current - pixel_h <= 0 ? 0 : 1);
472 }
473 
474 /* Process the next piece of an ImageType 3 image. */
475 private int
gx_image3_plane_data(gx_image_enum_common_t * info,const gx_image_plane_t * planes,int height,int * rows_used)476 gx_image3_plane_data(gx_image_enum_common_t * info,
477 		     const gx_image_plane_t * planes, int height,
478 		     int *rows_used)
479 {
480     gx_image3_enum_t *penum = (gx_image3_enum_t *) info;
481     int pixel_height = penum->pixel_height;
482     int pixel_used = 0;
483     int mask_height = penum->mask_height;
484     int mask_used = 0;
485     int h1 = max(pixel_height - penum->pixel_y, mask_height - penum->mask_y);
486     int h = min(height, h1);
487     const gx_image_plane_t *pixel_planes;
488     gx_image_plane_t pixel_plane, mask_plane;
489     int code = 0;
490 
491     /* Initialized rows_used in case we get an error. */
492     *rows_used = 0;
493     switch (penum->InterleaveType) {
494 	case interleave_chunky:
495 	    if (h <= 0)
496 		return 0;
497 	    if (h > 1) {
498 		/* Do the operation one row at a time. */
499 		int h_orig = h;
500 
501 		mask_plane = planes[0];
502 		do {
503 		    code = gx_image3_plane_data(info, &mask_plane, 1,
504 						rows_used);
505 		    h -= *rows_used;
506 		    if (code)
507 			break;
508 		    mask_plane.data += mask_plane.raster;
509 		} while (h);
510 		*rows_used = h_orig - h;
511 		return code;
512 	    } {
513 		/* Pull apart the source data and the mask data. */
514 		int bpc = penum->bpc;
515 		int num_components = penum->num_components;
516 		int width = penum->pixel_width;
517 
518 		/* We do this in the simplest (not fastest) way for now. */
519 		uint bit_x = bpc * (num_components + 1) * planes[0].data_x;
520 
521 		sample_load_declare_setup(sptr, sbit,
522 					  planes[0].data + (bit_x >> 3),
523 					  bit_x & 7, bpc);
524 		sample_store_declare_setup(mptr, mbit, mbbyte,
525 					   penum->mask_data, 0, 1);
526 		sample_store_declare_setup(pptr, pbit, pbbyte,
527 					   penum->pixel_data, 0, bpc);
528 		int x;
529 
530 		mask_plane.data = mptr;
531 		mask_plane.data_x = 0;
532 		/* raster doesn't matter */
533 		pixel_plane.data = pptr;
534 		pixel_plane.data_x = 0;
535 		/* raster doesn't matter */
536 		pixel_planes = &pixel_plane;
537 		for (x = 0; x < width; ++x) {
538 		    uint value;
539 		    int i;
540 
541 		    sample_load_next12(value, sptr, sbit, bpc);
542 		    sample_store_next12(value != 0, mptr, mbit, 1, mbbyte);
543 		    for (i = 0; i < num_components; ++i) {
544 			sample_load_next12(value, sptr, sbit, bpc);
545 			sample_store_next12(value, pptr, pbit, bpc, pbbyte);
546 		    }
547 		}
548 		sample_store_flush(mptr, mbit, 1, mbbyte);
549 		sample_store_flush(pptr, pbit, bpc, pbbyte);
550 	    }
551 	    break;
552 	case interleave_scan_lines:
553 	    if (planes_next(penum) >= 0) {
554 		/* This is mask data. */
555 		mask_plane = planes[0];
556 		pixel_planes = &pixel_plane;
557 		pixel_plane.data = 0;
558 	    } else {
559 		/* This is pixel data. */
560 		mask_plane.data = 0;
561 		pixel_planes = planes;
562 	    }
563 	    break;
564 	case interleave_separate_source:
565 	    /*
566 	     * In order to be able to recover from interruptions, we must
567 	     * limit separate-source processing to 1 scan line at a time.
568 	     */
569 	    if (h > 1)
570 		h = 1;
571 	    mask_plane = planes[0];
572 	    pixel_planes = planes + 1;
573 	    break;
574 	default:		/* not possible */
575 	    return_error(gs_error_rangecheck);
576     }
577     /*
578      * Process the mask data first, so it will set up the mask
579      * device for clipping the pixel data.
580      */
581     if (mask_plane.data) {
582 	/*
583 	 * If, on the last call, we processed some mask rows successfully
584 	 * but processing the pixel rows was interrupted, we set rows_used
585 	 * to indicate the number of pixel rows processed (since there is
586 	 * no way to return two rows_used values).  If this happened, some
587 	 * mask rows may get presented again.  We must skip over them
588 	 * rather than processing them again.
589 	 */
590 	int skip = penum->mask_skip;
591 
592 	if (skip >= h) {
593 	    penum->mask_skip = skip - (mask_used = h);
594 	} else {
595 	    int mask_h = h - skip;
596 
597 	    mask_plane.data += skip * mask_plane.raster;
598 	    penum->mask_skip = 0;
599 	    code = gx_image_plane_data_rows(penum->mask_info, &mask_plane,
600 					    mask_h, &mask_used);
601 	    mask_used += skip;
602 	}
603 	*rows_used = mask_used;
604 	penum->mask_y += mask_used;
605 	if (code < 0)
606 	    return code;
607     }
608     if (pixel_planes[0].data) {
609 	/*
610 	 * If necessary, flush any buffered mask data to the mask clipping
611 	 * device.
612 	 */
613 	gx_image_flush(penum->mask_info);
614 	code = gx_image_plane_data_rows(penum->pixel_info, pixel_planes, h,
615 					&pixel_used);
616 	/*
617 	 * There isn't any way to set rows_used if different amounts of
618 	 * the mask and pixel data were used.  Fake it.
619 	 */
620 	*rows_used = pixel_used;
621 	/*
622 	 * Don't return code yet: we must account for the fact that
623 	 * some mask data may have been processed.
624 	 */
625 	penum->pixel_y += pixel_used;
626 	if (code < 0) {
627 	    /*
628 	     * We must prevent the mask data from being processed again.
629 	     * We rely on the fact that h > 1 is only possible if the
630 	     * mask and pixel data have the same Y scaling.
631 	     */
632 	    if (mask_used > pixel_used) {
633 		int skip = mask_used - pixel_used;
634 
635 		penum->mask_skip = skip;
636 		penum->mask_y -= skip;
637 		mask_used = pixel_used;
638 	    }
639 	}
640     }
641     if_debug5('b', "[b]image3 h=%d %smask_y=%d %spixel_y=%d\n",
642 	      h, (mask_plane.data ? "+" : ""), penum->mask_y,
643 	      (pixel_planes[0].data ? "+" : ""), penum->pixel_y);
644     if (penum->mask_y >= penum->mask_height &&
645 	penum->pixel_y >= penum->pixel_height)
646 	return 1;
647     if (penum->InterleaveType == interleave_scan_lines) {
648 	/* Update the width and depth in the enumerator. */
649 	if (planes_next(penum) >= 0) {  /* want mask data next */
650 	    penum->plane_widths[0] = penum->mask_width;
651 	    penum->plane_depths[0] = 1;
652 	} else {		/* want pixel data next */
653 	    penum->plane_widths[0] = penum->pixel_width;
654 	    penum->plane_depths[0] = penum->pixel_info->plane_depths[0];
655 	}
656     }
657     /*
658      * The mask may be complete (gx_image_plane_data_rows returned 1),
659      * but there may still be pixel rows to go, so don't return 1 here.
660      */
661     return (code < 0 ? code : 0);
662 }
663 
664 /* Flush buffered data. */
665 private int
gx_image3_flush(gx_image_enum_common_t * info)666 gx_image3_flush(gx_image_enum_common_t * info)
667 {
668     gx_image3_enum_t * const penum = (gx_image3_enum_t *) info;
669     int code = gx_image_flush(penum->mask_info);
670 
671     if (code >= 0)
672 	code = gx_image_flush(penum->pixel_info);
673     return code;
674 }
675 
676 /* Determine which data planes are wanted. */
677 private bool
gx_image3_planes_wanted(const gx_image_enum_common_t * info,byte * wanted)678 gx_image3_planes_wanted(const gx_image_enum_common_t * info, byte *wanted)
679 {
680     const gx_image3_enum_t * const penum = (const gx_image3_enum_t *) info;
681 
682     switch (penum->InterleaveType) {
683     case interleave_chunky:	/* only 1 plane */
684 	wanted[0] = 0xff;
685 	return true;
686     case interleave_scan_lines:	/* only 1 plane, but varying width/depth */
687 	wanted[0] = 0xff;
688 	return false;
689     case interleave_separate_source: {
690 	/*
691 	 * We always want at least as much of the mask to be filled as the
692 	 * pixel data.  next > 0 iff we've processed more data than mask.
693 	 * Plane 0 is the mask, planes [1 .. num_planes - 1] are pixel data.
694 	 */
695 	int next = planes_next(penum);
696 
697 	wanted[0] = (next >= 0 ? 0xff : 0);
698 	memset(wanted + 1, (next <= 0 ? 0xff : 0), info->num_planes - 1);
699 	/*
700 	 * In principle, wanted will always be true for both mask and pixel
701 	 * data if the full_heights are equal.  Unfortunately, even in this
702 	 * case, processing may be interrupted after a mask row has been
703 	 * passed to the underlying image processor but before the data row
704 	 * has been passed, in which case pixel data will be 'wanted', but
705 	 * not mask data, for the next call.  Therefore, we must return
706 	 * false.
707 	 */
708 	return false
709 	    /*(next == 0 &&
710 	      penum->mask_full_height == penum->pixel_full_height)*/;
711     }
712     default:			/* can't happen */
713 	memset(wanted, 0, info->num_planes);
714 	return false;
715     }
716 }
717 
718 /* Clean up after processing an ImageType 3 image. */
719 private int
gx_image3_end_image(gx_image_enum_common_t * info,bool draw_last)720 gx_image3_end_image(gx_image_enum_common_t * info, bool draw_last)
721 {
722     gx_image3_enum_t *penum = (gx_image3_enum_t *) info;
723     gs_memory_t *mem = penum->memory;
724     gx_device *mdev = penum->mdev;
725     int mcode = gx_image_end(penum->mask_info, draw_last);
726     gx_device *pcdev = penum->pcdev;
727     int pcode = gx_image_end(penum->pixel_info, draw_last);
728 
729     gs_closedevice(pcdev);
730     gs_closedevice(mdev);
731     gs_free_object(mem, penum->mask_data,
732 		   "gx_image3_end_image(mask_data)");
733     gs_free_object(mem, penum->pixel_data,
734 		   "gx_image3_end_image(pixel_data)");
735     gs_free_object(mem, pcdev, "gx_image3_end_image(pcdev)");
736     gs_free_object(mem, mdev, "gx_image3_end_image(mdev)");
737     gs_free_object(mem, penum, "gx_image3_end_image");
738     return (pcode < 0 ? pcode : mcode);
739 }
740