xref: /plan9/sys/src/cmd/gs/src/gximag3x.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 2000 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: gximag3x.c,v 1.20 2004/09/16 08:03:56 igor Exp $ */
18 /* ImageType 3x image implementation */
19 /****** THE REAL WORK IS NYI ******/
20 #include "math_.h"		/* for ceil, floor */
21 #include "memory_.h"
22 #include "gx.h"
23 #include "gserrors.h"
24 #include "gsbitops.h"
25 #include "gscspace.h"
26 #include "gscpixel.h"
27 #include "gsstruct.h"
28 #include "gxdevice.h"
29 #include "gxdevmem.h"
30 #include "gximag3x.h"
31 #include "gxistate.h"
32 #include "gdevbbox.h"
33 
34 extern_st(st_color_space);
35 
36 /* Forward references */
37 private dev_proc_begin_typed_image(gx_begin_image3x);
38 private image_enum_proc_plane_data(gx_image3x_plane_data);
39 private image_enum_proc_end_image(gx_image3x_end_image);
40 private image_enum_proc_flush(gx_image3x_flush);
41 private image_enum_proc_planes_wanted(gx_image3x_planes_wanted);
42 
43 /* GC descriptor */
44 private_st_gs_image3x();
45 
46 /* Define the image type for ImageType 3x images. */
47 const gx_image_type_t gs_image_type_3x = {
48     &st_gs_image3x, gx_begin_image3x, gx_data_image_source_size,
49     gx_image_no_sput, gx_image_no_sget, gx_image_default_release,
50     IMAGE3X_IMAGETYPE
51 };
52 private const gx_image_enum_procs_t image3x_enum_procs = {
53     gx_image3x_plane_data, gx_image3x_end_image,
54     gx_image3x_flush, gx_image3x_planes_wanted
55 };
56 
57 /* Initialize an ImageType 3x image. */
58 private void
gs_image3x_mask_init(gs_image3x_mask_t * pimm)59 gs_image3x_mask_init(gs_image3x_mask_t *pimm)
60 {
61     pimm->InterleaveType = 0;	/* not a valid type */
62     pimm->has_Matte = false;
63     gs_data_image_t_init(&pimm->MaskDict, 1);
64     pimm->MaskDict.BitsPerComponent = 0;	/* not supplied */
65 }
66 void
gs_image3x_t_init(gs_image3x_t * pim,const gs_color_space * color_space)67 gs_image3x_t_init(gs_image3x_t * pim, const gs_color_space * color_space)
68 {
69     gs_pixel_image_t_init((gs_pixel_image_t *) pim, color_space);
70     pim->type = &gs_image_type_3x;
71     gs_image3x_mask_init(&pim->Opacity);
72     gs_image3x_mask_init(&pim->Shape);
73 }
74 
75 /*
76  * We implement ImageType 3 images by interposing a mask clipper in
77  * front of an ordinary ImageType 1 image.  Note that we build up the
78  * mask row-by-row as we are processing the image.
79  *
80  * We export a generalized form of the begin_image procedure for use by
81  * the PDF and PostScript writers.
82  */
83 
84 typedef struct image3x_channel_state_s {
85     gx_image_enum_common_t *info;
86     gx_device *mdev;		/* gx_device_memory in default impl. */
87 				/* (only for masks) */
88     gs_image3_interleave_type_t InterleaveType;
89     int width, height, full_height, depth;
90     byte *data;			/* (if chunky) */
91     /* Only the following change dynamically. */
92     int y;
93     int skip;			/* only for masks, # of rows to skip, */
94 				/* see below */
95 } image3x_channel_state_t;
96 typedef struct gx_image3x_enum_s {
97     gx_image_enum_common;
98     gx_device *pcdev;		/* gx_device_mask_clip in default impl. */
99     int num_components;		/* (not counting masks) */
100     int bpc;			/* pixel BitsPerComponent */
101     gs_memory_t *memory;
102 #define NUM_MASKS 2		/* opacity, shape */
103     image3x_channel_state_t mask[NUM_MASKS], pixel;
104 } gx_image3x_enum_t;
105 
106 extern_st(st_gx_image_enum_common);
107 gs_private_st_suffix_add9(st_image3x_enum, gx_image3x_enum_t,
108   "gx_image3x_enum_t", image3x_enum_enum_ptrs, image3x_enum_reloc_ptrs,
109   st_gx_image_enum_common, pcdev, mask[0].info, mask[0].mdev, mask[0].data,
110   mask[1].info, mask[1].mdev, mask[1].data, pixel.info, pixel.data);
111 
112 /*
113  * Begin a generic ImageType 3x image, with client handling the creation of
114  * the mask image and mask clip devices.
115  */
116 typedef struct image3x_channel_values_s {
117     gs_matrix matrix;
118     gs_point corner;
119     gs_int_rect rect;
120     gs_image_t image;
121 } image3x_channel_values_t;
122 private int check_image3x_mask(const gs_image3x_t *pim,
123 			       const gs_image3x_mask_t *pimm,
124 			       const image3x_channel_values_t *ppcv,
125 			       image3x_channel_values_t *pmcv,
126 			       image3x_channel_state_t *pmcs,
127 			       gs_memory_t *mem);
128 int
gx_begin_image3x_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,image3x_make_mid_proc_t make_mid,image3x_make_mcde_proc_t make_mcde,gx_image_enum_common_t ** pinfo)129 gx_begin_image3x_generic(gx_device * dev,
130 			const gs_imager_state *pis, const gs_matrix *pmat,
131 			const gs_image_common_t *pic, const gs_int_rect *prect,
132 			const gx_drawing_color *pdcolor,
133 			const gx_clip_path *pcpath, gs_memory_t *mem,
134 			image3x_make_mid_proc_t make_mid,
135 			image3x_make_mcde_proc_t make_mcde,
136 			gx_image_enum_common_t **pinfo)
137 {
138     const gs_image3x_t *pim = (const gs_image3x_t *)pic;
139     gx_image3x_enum_t *penum;
140     gx_device *pcdev = 0;
141     image3x_channel_values_t mask[2], pixel;
142     gs_matrix mat;
143     gx_device *midev[2];
144     gx_image_enum_common_t *minfo[2];
145     gs_int_point origin[2];
146     int code;
147     int i;
148 
149     /* Validate the parameters. */
150     if (pim->Height <= 0)
151 	return_error(gs_error_rangecheck);
152     penum = gs_alloc_struct(mem, gx_image3x_enum_t, &st_image3x_enum,
153 			    "gx_begin_image3x");
154     if (penum == 0)
155 	return_error(gs_error_VMerror);
156     /* Initialize pointers now in case we bail out. */
157     penum->mask[0].info = 0, penum->mask[0].mdev = 0, penum->mask[0].data = 0;
158     penum->mask[1].info = 0, penum->mask[1].mdev = 0, penum->mask[1].data = 0;
159     penum->pixel.info = 0, penum->pixel.data = 0;
160     if (prect)
161 	pixel.rect = *prect;
162     else {
163 	pixel.rect.p.x = pixel.rect.p.y = 0;
164 	pixel.rect.q.x = pim->Width;
165 	pixel.rect.q.y = pim->Height;
166     }
167     if ((code = gs_matrix_invert(&pim->ImageMatrix, &pixel.matrix)) < 0 ||
168 	(code = gs_point_transform(pim->Width, pim->Height, &pixel.matrix,
169 				   &pixel.corner)) < 0 ||
170 	(code = check_image3x_mask(pim, &pim->Opacity, &pixel, &mask[0],
171 				   &penum->mask[0], mem)) < 0 ||
172 	(code = check_image3x_mask(pim, &pim->Shape, &pixel, &mask[1],
173 				   &penum->mask[1], mem)) < 0
174 	) {
175 	goto out0;
176     }
177     penum->num_components =
178 	gs_color_space_num_components(pim->ColorSpace);
179     gx_image_enum_common_init((gx_image_enum_common_t *) penum,
180 			      (const gs_data_image_t *)pim,
181 			      &image3x_enum_procs, dev,
182 			      1 + penum->num_components,
183 			      pim->format);
184     penum->pixel.width = pixel.rect.q.x - pixel.rect.p.x;
185     penum->pixel.height = pixel.rect.q.y - pixel.rect.p.y;
186     penum->pixel.full_height = pim->Height;
187     penum->pixel.y = 0;
188     if (penum->mask[0].data || penum->mask[1].data) {
189 	/* Also allocate a row buffer for the pixel data. */
190 	penum->pixel.data =
191 	    gs_alloc_bytes(mem,
192 			   (penum->pixel.width * pim->BitsPerComponent *
193 			    penum->num_components + 7) >> 3,
194 			   "gx_begin_image3x(pixel.data)");
195 	if (penum->pixel.data == 0) {
196 	    code = gs_note_error(gs_error_VMerror);
197 	    goto out1;
198 	}
199     }
200     penum->bpc = pim->BitsPerComponent;
201     penum->memory = mem;
202     if (pmat == 0)
203 	pmat = &ctm_only(pis);
204     for (i = 0; i < NUM_MASKS; ++i) {
205 	gs_rect mrect;
206 	gx_device *mdev;
207 	/*
208 	 * The mask data has to be defined in a DevicePixel color space
209 	 * of the correct depth so that no color mapping will occur.
210 	 */
211 	/****** FREE COLOR SPACE ON ERROR OR AT END ******/
212 	gs_color_space *pmcs;
213 
214 	if (penum->mask[i].depth == 0) {	/* mask not supplied */
215 	    midev[i] = 0;
216 	    minfo[i] = 0;
217 	    continue;
218 	}
219 	pmcs =  gs_alloc_struct(mem, gs_color_space, &st_color_space,
220 				"gx_begin_image3x_generic");
221 	if (pmcs == 0)
222 	    return_error(gs_error_VMerror);
223 	gs_cspace_init_DevicePixel(mem, pmcs, penum->mask[i].depth);
224 	mrect.p.x = mrect.p.y = 0;
225 	mrect.q.x = penum->mask[i].width;
226 	mrect.q.y = penum->mask[i].height;
227 	if ((code = gs_matrix_multiply(&mask[i].matrix, pmat, &mat)) < 0 ||
228 	    (code = gs_bbox_transform(&mrect, &mat, &mrect)) < 0
229 	    )
230 	    return code;
231 	origin[i].x = (int)floor(mrect.p.x);
232 	origin[i].y = (int)floor(mrect.p.y);
233 	code = make_mid(&mdev, dev,
234 			(int)ceil(mrect.q.x) - origin[i].x,
235 			(int)ceil(mrect.q.y) - origin[i].y,
236 			penum->mask[i].depth, mem);
237 	if (code < 0)
238 	    goto out1;
239 	penum->mask[i].mdev = mdev;
240         gs_image_t_init(&mask[i].image, pmcs);
241 	mask[i].image.ColorSpace = pmcs;
242 	mask[i].image.adjust = false;
243 	{
244 	    const gx_image_type_t *type1 = mask[i].image.type;
245 	    const gs_image3x_mask_t *pixm =
246 		(i == 0 ? &pim->Opacity : &pim->Shape);
247 
248 	    *(gs_data_image_t *)&mask[i].image = pixm->MaskDict;
249 	    mask[i].image.type = type1;
250 	    mask[i].image.BitsPerComponent = pixm->MaskDict.BitsPerComponent;
251 	}
252 	{
253 	    gs_matrix m_mat;
254 
255 	    /*
256 	     * Adjust the translation for rendering the mask to include a
257 	     * negative translation by origin.{x,y} in device space.
258 	     */
259 	    m_mat = *pmat;
260 	    m_mat.tx -= origin[i].x;
261 	    m_mat.ty -= origin[i].y;
262 	    /*
263 	     * Note that pis = NULL here, since we don't want to have to
264 	     * create another imager state with default log_op, etc.
265 	     * dcolor = NULL is OK because this is an opaque image with
266 	     * CombineWithColor = false.
267 	     */
268 	    code = gx_device_begin_typed_image(mdev, NULL, &m_mat,
269 			       (const gs_image_common_t *)&mask[i].image,
270 					       &mask[i].rect, NULL, NULL,
271 					       mem, &penum->mask[i].info);
272 	    if (code < 0)
273 		goto out2;
274 	}
275 	midev[i] = mdev;
276 	minfo[i] = penum->mask[i].info;
277     }
278     gs_image_t_init(&pixel.image, pim->ColorSpace);
279     {
280 	const gx_image_type_t *type1 = pixel.image.type;
281 
282 	*(gs_pixel_image_t *)&pixel.image = *(const gs_pixel_image_t *)pim;
283 	pixel.image.type = type1;
284     }
285     code = make_mcde(dev, pis, pmat, (const gs_image_common_t *)&pixel.image,
286 		     prect, pdcolor, pcpath, mem, &penum->pixel.info,
287 		     &pcdev, midev, minfo, origin, pim);
288     if (code < 0)
289 	goto out3;
290     penum->pcdev = pcdev;
291     /*
292      * Set num_planes, plane_widths, and plane_depths from the values in the
293      * enumerators for the mask(s) and the image data.
294      */
295     {
296 	int added_depth = 0;
297 	int pi = 0;
298 
299 	for (i = 0; i < NUM_MASKS; ++i) {
300 	    if (penum->mask[i].depth == 0)	/* no mask */
301 		continue;
302 	    switch (penum->mask[i].InterleaveType) {
303 	    case interleave_chunky:
304 		/* Add the mask data to the depth of the image data. */
305 		added_depth += pim->BitsPerComponent;
306 		break;
307 	    case interleave_separate_source:
308 		/* Insert the mask as a separate plane. */
309 		penum->plane_widths[pi] = penum->mask[i].width;
310 		penum->plane_depths[pi] = penum->mask[i].depth;
311 		++pi;
312 		break;
313 	    default:		/* can't happen */
314 		code = gs_note_error(gs_error_Fatal);
315 		goto out3;
316 	    }
317 	}
318 	memcpy(&penum->plane_widths[pi], &penum->pixel.info->plane_widths[0],
319 	       penum->pixel.info->num_planes * sizeof(penum->plane_widths[0]));
320 	memcpy(&penum->plane_depths[pi], &penum->pixel.info->plane_depths[0],
321 	       penum->pixel.info->num_planes * sizeof(penum->plane_depths[0]));
322 	penum->plane_depths[pi] += added_depth;
323 	penum->num_planes = pi + penum->pixel.info->num_planes;
324     }
325     if (midev[0])
326 	gx_device_retain(midev[0], true); /* will free explicitly */
327     if (midev[1])
328 	gx_device_retain(midev[1], true); /* ditto */
329     gx_device_retain(pcdev, true); /* ditto */
330     *pinfo = (gx_image_enum_common_t *) penum;
331     return 0;
332   out3:
333     if (penum->mask[1].info)
334 	gx_image_end(penum->mask[1].info, false);
335     if (penum->mask[0].info)
336 	gx_image_end(penum->mask[0].info, false);
337   out2:
338     if (penum->mask[1].mdev) {
339 	gs_closedevice(penum->mask[1].mdev);
340 	gs_free_object(mem, penum->mask[1].mdev,
341 		       "gx_begin_image3x(mask[1].mdev)");
342     }
343     if (penum->mask[0].mdev) {
344 	gs_closedevice(penum->mask[0].mdev);
345 	gs_free_object(mem, penum->mask[0].mdev,
346 		       "gx_begin_image3x(mask[0].mdev)");
347     }
348   out1:
349     gs_free_object(mem, penum->mask[0].data, "gx_begin_image3x(mask[0].data)");
350     gs_free_object(mem, penum->mask[1].data, "gx_begin_image3x(mask[1].data)");
351     gs_free_object(mem, penum->pixel.data, "gx_begin_image3x(pixel.data)");
352   out0:
353     gs_free_object(mem, penum, "gx_begin_image3x");
354     return code;
355 }
356 private bool
check_image3x_extent(floatp mask_coeff,floatp data_coeff)357 check_image3x_extent(floatp mask_coeff, floatp data_coeff)
358 {
359     if (mask_coeff == 0)
360 	return data_coeff == 0;
361     if (data_coeff == 0 || (mask_coeff > 0) != (data_coeff > 0))
362 	return false;
363     return true;
364 }
365 /*
366  * Check mask parameters.
367  * Reads ppcv->{matrix,corner,rect}, sets pmcv->{matrix,corner,rect} and
368  * pmcs->{InterleaveType,width,height,full_height,depth,data,y,skip}.
369  * If the mask is omitted, sets pmcs->depth = 0 and returns normally.
370  */
371 private bool
check_image3x_mask(const gs_image3x_t * pim,const gs_image3x_mask_t * pimm,const image3x_channel_values_t * ppcv,image3x_channel_values_t * pmcv,image3x_channel_state_t * pmcs,gs_memory_t * mem)372 check_image3x_mask(const gs_image3x_t *pim, const gs_image3x_mask_t *pimm,
373 		   const image3x_channel_values_t *ppcv,
374 		   image3x_channel_values_t *pmcv,
375 		   image3x_channel_state_t *pmcs, gs_memory_t *mem)
376 {
377     int mask_width = pimm->MaskDict.Width, mask_height = pimm->MaskDict.Height;
378     int code;
379 
380     if (pimm->MaskDict.BitsPerComponent == 0) { /* mask missing */
381 	pmcs->depth = 0;
382         pmcs->InterleaveType = 0;	/* not a valid type */
383 	return 0;
384     }
385     if (mask_height <= 0)
386 	return_error(gs_error_rangecheck);
387     switch (pimm->InterleaveType) {
388 	/*case interleave_scan_lines:*/	/* not supported */
389 	default:
390 	    return_error(gs_error_rangecheck);
391 	case interleave_chunky:
392 	    if (mask_width != pim->Width ||
393 		mask_height != pim->Height ||
394 		pimm->MaskDict.BitsPerComponent != pim->BitsPerComponent ||
395 		pim->format != gs_image_format_chunky
396 		)
397 		return_error(gs_error_rangecheck);
398 	    break;
399 	case interleave_separate_source:
400 	    switch (pimm->MaskDict.BitsPerComponent) {
401 	    case 1: case 2: case 4: case 8:
402 		break;
403 	    default:
404 		return_error(gs_error_rangecheck);
405 	    }
406     }
407     if (!check_image3x_extent(pim->ImageMatrix.xx,
408 			      pimm->MaskDict.ImageMatrix.xx) ||
409 	!check_image3x_extent(pim->ImageMatrix.xy,
410 			      pimm->MaskDict.ImageMatrix.xy) ||
411 	!check_image3x_extent(pim->ImageMatrix.yx,
412 			      pimm->MaskDict.ImageMatrix.yx) ||
413 	!check_image3x_extent(pim->ImageMatrix.yy,
414 			      pimm->MaskDict.ImageMatrix.yy)
415 	)
416 	return_error(gs_error_rangecheck);
417     if ((code = gs_matrix_invert(&pimm->MaskDict.ImageMatrix, &pmcv->matrix)) < 0 ||
418 	(code = gs_point_transform(mask_width, mask_height,
419 				   &pmcv->matrix, &pmcv->corner)) < 0
420 	)
421 	return code;
422     if (fabs(ppcv->matrix.tx - pmcv->matrix.tx) >= 0.5 ||
423 	fabs(ppcv->matrix.ty - pmcv->matrix.ty) >= 0.5 ||
424 	fabs(ppcv->corner.x - pmcv->corner.x) >= 0.5 ||
425 	fabs(ppcv->corner.y - pmcv->corner.y) >= 0.5
426 	)
427 	return_error(gs_error_rangecheck);
428     pmcv->rect.p.x = ppcv->rect.p.x * mask_width / pim->Width;
429     pmcv->rect.p.y = ppcv->rect.p.y * mask_height / pim->Height;
430     pmcv->rect.q.x = (ppcv->rect.q.x * mask_width + pim->Width - 1) /
431 	pim->Width;
432     pmcv->rect.q.y = (ppcv->rect.q.y * mask_height + pim->Height - 1) /
433 	pim->Height;
434     /* Initialize the channel state in the enumerator. */
435     pmcs->InterleaveType = pimm->InterleaveType;
436     pmcs->width = pmcv->rect.q.x - pmcv->rect.p.x;
437     pmcs->height = pmcv->rect.q.y - pmcv->rect.p.y;
438     pmcs->full_height = pimm->MaskDict.Height;
439     pmcs->depth = pimm->MaskDict.BitsPerComponent;
440     if (pmcs->InterleaveType == interleave_chunky) {
441 	/* Allocate a buffer for the data. */
442 	pmcs->data =
443 	    gs_alloc_bytes(mem,
444 			   (pmcs->width * pimm->MaskDict.BitsPerComponent + 7) >> 3,
445 			   "gx_begin_image3x(mask data)");
446 	if (pmcs->data == 0)
447 	    return_error(gs_error_VMerror);
448     }
449     pmcs->y = pmcs->skip = 0;
450     return 0;
451 }
452 
453 /*
454  * Return > 0 if we want more data from channel 1 now, < 0 if we want more
455  * from channel 2 now, 0 if we want both.
456  */
457 private int
channel_next(const image3x_channel_state_t * pics1,const image3x_channel_state_t * pics2)458 channel_next(const image3x_channel_state_t *pics1,
459 	     const image3x_channel_state_t *pics2)
460 {
461     /*
462      * The invariant we need to maintain is that we always have at least as
463      * much channel N as channel N+1 data, where N = 0 = opacity, 1 = shape,
464      * and 2 = pixel.  I.e., for any two consecutive channels c1 and c2, we
465      * require c1.y / c1.full_height >= c2.y / c2.full_height, or, to avoid
466      * floating point, c1.y * c2.full_height >= c2.y * c1.full_height.  We
467      * know this condition is true now; return a value that indicates how to
468      * maintain it.
469      */
470     int h1 = pics1->full_height;
471     int h2 = pics2->full_height;
472     long current = pics1->y * (long)h2 - pics2->y * (long)h1;
473 
474 #ifdef DEBUG
475     if (current < 0)
476 	lprintf4("channel_next invariant fails: %d/%d < %d/%d\n",
477 		 pics1->y, pics1->full_height,
478 		 pics2->y, pics2->full_height);
479 #endif
480     return ((current -= h1) >= 0 ? -1 :
481 	    current + h2 >= 0 ? 0 : 1);
482 }
483 
484 /* Define the default implementation of ImageType 3 processing. */
485 private IMAGE3X_MAKE_MID_PROC(make_midx_default); /* check prototype */
486 private int
make_midx_default(gx_device ** pmidev,gx_device * dev,int width,int height,int depth,gs_memory_t * mem)487 make_midx_default(gx_device **pmidev, gx_device *dev, int width, int height,
488 		 int depth, gs_memory_t *mem)
489 {
490     const gx_device_memory *mdproto = gdev_mem_device_for_bits(depth);
491     gx_device_memory *midev;
492     int code;
493 
494     if (mdproto == 0)
495 	return_error(gs_error_rangecheck);
496     midev = gs_alloc_struct(mem, gx_device_memory, &st_device_memory,
497 			    "make_mid_default");
498     if (midev == 0)
499 	return_error(gs_error_VMerror);
500     gs_make_mem_device(midev, mdproto, mem, 0, NULL);
501     midev->bitmap_memory = mem;
502     midev->width = width;
503     midev->height = height;
504     check_device_separable((gx_device *)midev);
505     gx_device_fill_in_procs((gx_device *)midev);
506     code = dev_proc(midev, open_device)((gx_device *)midev);
507     if (code < 0) {
508 	gs_free_object(mem, midev, "make_midx_default");
509 	return code;
510     }
511     midev->is_open = true;
512     dev_proc(midev, fill_rectangle)
513 	((gx_device *)midev, 0, 0, width, height, (gx_color_index)0);
514     *pmidev = (gx_device *)midev;
515     return 0;
516 }
517 private IMAGE3X_MAKE_MCDE_PROC(make_mcdex_default);  /* check prototype */
518 private int
make_mcdex_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[2],gx_image_enum_common_t * pminfo[2],const gs_int_point origin[2],const gs_image3x_t * pim)519 make_mcdex_default(gx_device *dev, const gs_imager_state *pis,
520 		   const gs_matrix *pmat, const gs_image_common_t *pic,
521 		   const gs_int_rect *prect, const gx_drawing_color *pdcolor,
522 		   const gx_clip_path *pcpath, gs_memory_t *mem,
523 		   gx_image_enum_common_t **pinfo,
524 		   gx_device **pmcdev, gx_device *midev[2],
525 		   gx_image_enum_common_t *pminfo[2],
526 		   const gs_int_point origin[2],
527 		   const gs_image3x_t *pim)
528 {
529     /**************** NYI ****************/
530     /*
531      * There is no soft-mask analogue of make_mcde_default, because
532      * soft-mask clipping is a more complicated operation, implemented
533      * by the general transparency code.  As a default, we simply ignore
534      * the soft mask.  However, we have to create an intermediate device
535      * that can be freed at the end and that simply forwards all calls.
536      * The most convenient device for this purpose is the bbox device.
537      */
538     gx_device_bbox *bbdev =
539 	gs_alloc_struct_immovable(mem, gx_device_bbox, &st_device_bbox,
540 				  "make_mcdex_default");
541     int code;
542 
543     if (bbdev == 0)
544 	return_error(gs_error_VMerror);
545     gx_device_bbox_init(bbdev, dev, mem);
546     gx_device_bbox_fwd_open_close(bbdev, false);
547     code = dev_proc(bbdev, begin_typed_image)
548 	((gx_device *)bbdev, pis, pmat, pic, prect, pdcolor, pcpath, mem,
549 	 pinfo);
550     if (code < 0) {
551 	gs_free_object(mem, bbdev, "make_mcdex_default");
552 	return code;
553     }
554     *pmcdev = (gx_device *)bbdev;
555     return 0;
556 }
557 private int
gx_begin_image3x(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)558 gx_begin_image3x(gx_device * dev,
559 		const gs_imager_state * pis, const gs_matrix * pmat,
560 		const gs_image_common_t * pic, const gs_int_rect * prect,
561 		const gx_drawing_color * pdcolor, const gx_clip_path * pcpath,
562 		gs_memory_t * mem, gx_image_enum_common_t ** pinfo)
563 {
564     return gx_begin_image3x_generic(dev, pis, pmat, pic, prect, pdcolor,
565 				    pcpath, mem, make_midx_default,
566 				    make_mcdex_default, pinfo);
567 }
568 
569 /* Process the next piece of an ImageType 3 image. */
570 private int
gx_image3x_plane_data(gx_image_enum_common_t * info,const gx_image_plane_t * planes,int height,int * rows_used)571 gx_image3x_plane_data(gx_image_enum_common_t * info,
572 		     const gx_image_plane_t * planes, int height,
573 		     int *rows_used)
574 {
575     gx_image3x_enum_t *penum = (gx_image3x_enum_t *) info;
576     int pixel_height = penum->pixel.height;
577     int pixel_used = 0;
578     int mask_height[2];
579     int mask_used[2];
580     int h1 = pixel_height - penum->pixel.y;
581     int h;
582     const gx_image_plane_t *pixel_planes;
583     gx_image_plane_t pixel_plane, mask_plane[2];
584     int code = 0;
585     int i, pi = 0;
586     int num_chunky = 0;
587 
588     for (i = 0; i < NUM_MASKS; ++i) {
589 	int mh = mask_height[i] = penum->mask[i].height;
590 
591 	mask_plane[i].data = 0;
592 	mask_used[i] = 0;
593 	if (!penum->mask[i].depth)
594 	    continue;
595 	h1 = min(h1, mh - penum->mask[i].y);
596 	if (penum->mask[i].InterleaveType == interleave_chunky)
597 	    ++num_chunky;
598     }
599     h = min(height, h1);
600     /* Initialized rows_used in case we get an error. */
601     *rows_used = 0;
602     if (h <= 0)
603 	return 0;
604 
605     /* Handle masks from separate sources. */
606     for (i = 0; i < NUM_MASKS; ++i)
607 	if (penum->mask[i].InterleaveType == interleave_separate_source) {
608 	    /*
609 	     * In order to be able to recover from interruptions, we must
610 	     * limit separate-source processing to 1 scan line at a time.
611 	     */
612 	    if (h > 1)
613 		h = 1;
614 	    mask_plane[i] = planes[pi++];
615 	}
616     pixel_planes = &planes[pi];
617 
618     /* Handle chunky masks. */
619     if (num_chunky) {
620 	int bpc = penum->bpc;
621 	int num_components = penum->num_components;
622 	int width = penum->pixel.width;
623 	/* Pull apart the source data and the mask data. */
624 	/* We do this in the simplest (not fastest) way for now. */
625 	uint bit_x = bpc * (num_components + num_chunky) * planes[pi].data_x;
626 	sample_load_declare_setup(sptr, sbit, planes[0].data + (bit_x >> 3),
627 				  bit_x & 7, bpc);
628 	sample_store_declare_setup(pptr, pbit, pbbyte,
629 				   penum->pixel.data, 0, bpc);
630 	sample_store_declare(dptr[NUM_MASKS], dbit[NUM_MASKS],
631 			     dbbyte[NUM_MASKS]);
632 	int depth[NUM_MASKS];
633 	int x;
634 
635 	if (h > 1) {
636 	    /* Do the operation one row at a time. */
637 	    h = 1;
638 	}
639 	for (i = 0; i < NUM_MASKS; ++i)
640 	    if (penum->mask[i].data) {
641 		depth[i] = penum->mask[i].depth;
642 		mask_plane[i].data = dptr[i] = penum->mask[i].data;
643 		mask_plane[i].data_x = 0;
644 		/* raster doesn't matter */
645 		sample_store_setup(dbit[i], 0, depth[i]);
646 		sample_store_preload(dbbyte[i], dptr[i], 0, depth[i]);
647 	    } else
648 		depth[i] = 0;
649 	pixel_plane.data = pptr;
650 	pixel_plane.data_x = 0;
651 	/* raster doesn't matter */
652 	pixel_planes = &pixel_plane;
653 	for (x = 0; x < width; ++x) {
654 	    uint value;
655 
656 	    for (i = 0; i < NUM_MASKS; ++i)
657 		if (depth[i]) {
658 		    sample_load_next12(value, sptr, sbit, bpc);
659 		    sample_store_next12(value, dptr[i], dbit[i], depth[i],
660 					dbbyte[i]);
661 		}
662 	    for (i = 0; i < num_components; ++i) {
663 		sample_load_next12(value, sptr, sbit, bpc);
664 		sample_store_next12(value, pptr, pbit, bpc, pbbyte);
665 	    }
666 	}
667 	for (i = 0; i < NUM_MASKS; ++i)
668 	    if (penum->mask[i].data)
669 		sample_store_flush(dptr[i], dbit[i], depth[i], dbbyte[i]);
670 	sample_store_flush(pptr, pbit, bpc, pbbyte);
671 	}
672     /*
673      * Process the mask data first, so it will set up the mask
674      * device for clipping the pixel data.
675      */
676     for (i = 0; i < NUM_MASKS; ++i)
677 	if (mask_plane[i].data) {
678 	    /*
679 	     * If, on the last call, we processed some mask rows
680 	     * successfully but processing the pixel rows was interrupted,
681 	     * we set rows_used to indicate the number of pixel rows
682 	     * processed (since there is no way to return two rows_used
683 	     * values).  If this happened, some mask rows may get presented
684 	     * again.  We must skip over them rather than processing them
685 	     * again.
686 	     */
687 	    int skip = penum->mask[i].skip;
688 
689 	    if (skip >= h) {
690 		penum->mask[i].skip = skip - (mask_used[i] = h);
691 	    } else {
692 		int mask_h = h - skip;
693 
694 		mask_plane[i].data += skip * mask_plane[i].raster;
695 		penum->mask[i].skip = 0;
696 		code = gx_image_plane_data_rows(penum->mask[i].info,
697 						&mask_plane[i],
698 						mask_h, &mask_used[i]);
699 		mask_used[i] += skip;
700 	    }
701 	    *rows_used = mask_used[i];
702 	    penum->mask[i].y += mask_used[i];
703 	    if (code < 0)
704 		return code;
705 	}
706     if (pixel_planes[0].data) {
707 	/*
708 	 * If necessary, flush any buffered mask data to the mask clipping
709 	 * device.
710 	 */
711 	for (i = 0; i < NUM_MASKS; ++i)
712 	    if (penum->mask[i].info)
713 		gx_image_flush(penum->mask[i].info);
714 	code = gx_image_plane_data_rows(penum->pixel.info, pixel_planes, h,
715 					&pixel_used);
716 	/*
717 	 * There isn't any way to set rows_used if different amounts of
718 	 * the mask and pixel data were used.  Fake it.
719 	 */
720 	*rows_used = pixel_used;
721 	/*
722 	 * Don't return code yet: we must account for the fact that
723 	 * some mask data may have been processed.
724 	 */
725 	penum->pixel.y += pixel_used;
726 	if (code < 0) {
727 	    /*
728 	     * We must prevent the mask data from being processed again.
729 	     * We rely on the fact that h > 1 is only possible if the
730 	     * mask and pixel data have the same Y scaling.
731 	     */
732 	    for (i = 0; i < NUM_MASKS; ++i)
733 		if (mask_used[i] > pixel_used) {
734 		    int skip = mask_used[i] - pixel_used;
735 
736 		    penum->mask[i].skip = skip;
737 		    penum->mask[i].y -= skip;
738 		    mask_used[i] = pixel_used;
739 		}
740 	}
741     }
742     if_debug7('b', "[b]image3x h=%d %sopacity.y=%d %sopacity.y=%d %spixel.y=%d\n",
743 	      h, (mask_plane[0].data ? "+" : ""), penum->mask[0].y,
744 	      (mask_plane[1].data ? "+" : ""), penum->mask[1].y,
745 	      (pixel_planes[0].data ? "+" : ""), penum->pixel.y);
746     if (penum->mask[0].y >= penum->mask[0].height &&
747 	penum->mask[1].y >= penum->mask[1].height &&
748 	penum->pixel.y >= penum->pixel.height)
749 	return 1;
750     /*
751      * The mask may be complete (gx_image_plane_data_rows returned 1),
752      * but there may still be pixel rows to go, so don't return 1 here.
753      */
754     return (code < 0 ? code : 0);
755 }
756 
757 /* Flush buffered data. */
758 private int
gx_image3x_flush(gx_image_enum_common_t * info)759 gx_image3x_flush(gx_image_enum_common_t * info)
760 {
761     gx_image3x_enum_t * const penum = (gx_image3x_enum_t *) info;
762     int code = gx_image_flush(penum->mask[0].info);
763 
764     if (code >= 0)
765 	code = gx_image_flush(penum->mask[1].info);
766     if (code >= 0)
767 	code = gx_image_flush(penum->pixel.info);
768     return code;
769 }
770 
771 /* Determine which data planes are wanted. */
772 private bool
gx_image3x_planes_wanted(const gx_image_enum_common_t * info,byte * wanted)773 gx_image3x_planes_wanted(const gx_image_enum_common_t * info, byte *wanted)
774 {
775     const gx_image3x_enum_t * const penum = (const gx_image3x_enum_t *) info;
776     /*
777      * We always want at least as much of the mask(s) to be filled as the
778      * pixel data.
779      */
780     bool
781 	sso = penum->mask[0].InterleaveType == interleave_separate_source,
782 	sss = penum->mask[1].InterleaveType == interleave_separate_source;
783 
784     if (sso & sss) {
785 	/* Both masks have separate sources. */
786 	int mask_next = channel_next(&penum->mask[1], &penum->pixel);
787 
788 	memset(wanted + 2, (mask_next <= 0 ? 0xff : 0), info->num_planes - 2);
789 	wanted[1] = (mask_next >= 0 ? 0xff : 0);
790 	if (wanted[1]) {
791 	    mask_next = channel_next(&penum->mask[0], &penum->mask[1]);
792 	    wanted[0] = mask_next >= 0;
793 	} else
794 	    wanted[0] = 0;
795 	return false;		/* see below */
796     } else if (sso | sss) {
797 	/* Only one separate source. */
798 	const image3x_channel_state_t *pics =
799 	    (sso ? &penum->mask[0] : &penum->mask[1]);
800 	int mask_next = channel_next(pics, &penum->pixel);
801 
802 	wanted[0] = (mask_next >= 0 ? 0xff : 0);
803 	memset(wanted + 1, (mask_next <= 0 ? 0xff : 0), info->num_planes - 1);
804 	/*
805 	 * In principle, wanted will always be true for both mask and pixel
806 	 * data if the full_heights are equal.  Unfortunately, even in this
807 	 * case, processing may be interrupted after a mask row has been
808 	 * passed to the underlying image processor but before the data row
809 	 * has been passed, in which case pixel data will be 'wanted', but
810 	 * not mask data, for the next call.  Therefore, we must return
811 	 * false.
812 	 */
813 	return false
814 	    /*(next == 0 &&
815 	      pics->full_height == penum->pixel.full_height)*/;
816     } else {
817 	/* Everything is chunky, only 1 plane. */
818 	wanted[0] = 0xff;
819 	return true;
820     }
821 }
822 
823 /* Clean up after processing an ImageType 3x image. */
824 private int
gx_image3x_end_image(gx_image_enum_common_t * info,bool draw_last)825 gx_image3x_end_image(gx_image_enum_common_t * info, bool draw_last)
826 {
827     gx_image3x_enum_t *penum = (gx_image3x_enum_t *) info;
828     gs_memory_t *mem = penum->memory;
829     gx_device *mdev0 = penum->mask[0].mdev;
830     int ocode =
831 	(penum->mask[0].info ? gx_image_end(penum->mask[0].info, draw_last) :
832 	 0);
833     gx_device *mdev1 = penum->mask[1].mdev;
834     int scode =
835 	(penum->mask[1].info ? gx_image_end(penum->mask[1].info, draw_last) :
836 	 0);
837     gx_device *pcdev = penum->pcdev;
838     int pcode = gx_image_end(penum->pixel.info, draw_last);
839 
840     gs_closedevice(pcdev);
841     if (mdev0)
842 	gs_closedevice(mdev0);
843     if (mdev1)
844 	gs_closedevice(mdev1);
845     gs_free_object(mem, penum->mask[0].data,
846 		   "gx_image3x_end_image(mask[0].data)");
847     gs_free_object(mem, penum->mask[1].data,
848 		   "gx_image3x_end_image(mask[1].data)");
849     gs_free_object(mem, penum->pixel.data,
850 		   "gx_image3x_end_image(pixel.data)");
851     gs_free_object(mem, pcdev, "gx_image3x_end_image(pcdev)");
852     gs_free_object(mem, mdev0, "gx_image3x_end_image(mask[0].mdev)");
853     gs_free_object(mem, mdev1, "gx_image3x_end_image(mask[1].mdev)");
854     gs_free_object(mem, penum, "gx_image3x_end_image");
855     return (pcode < 0 ? pcode : scode < 0 ? scode : ocode);
856 }
857