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