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 /* Mask clipping device */
18 #include "memory_.h"
19 #include "gx.h"
20 #include "gsbittab.h"
21 #include "gxdevice.h"
22 #include "gxdevmem.h"
23 #include "gxclipm.h"
24 #include "gxdcolor.h"
25 
26 /* Device procedures */
27 static dev_proc_fill_rectangle(mask_clip_fill_rectangle);
28 static dev_proc_fill_rectangle_hl_color(mask_clip_fill_rectangle_hl_color);
29 static dev_proc_copy_mono(mask_clip_copy_mono);
30 static dev_proc_copy_color(mask_clip_copy_color);
31 static dev_proc_copy_alpha(mask_clip_copy_alpha);
32 static dev_proc_copy_alpha_hl_color(mask_clip_copy_alpha_hl_color);
33 static dev_proc_strip_tile_rectangle(mask_clip_strip_tile_rectangle);
34 static dev_proc_strip_tile_rect_devn(mask_clip_strip_tile_rect_devn);
35 static dev_proc_strip_copy_rop(mask_clip_strip_copy_rop);
36 static dev_proc_strip_copy_rop2(mask_clip_strip_copy_rop2);
37 static dev_proc_get_clipping_box(mask_clip_get_clipping_box);
38 
39 /* The device descriptor. */
40 const gx_device_mask_clip gs_mask_clip_device =
41 {std_device_std_body_open(gx_device_mask_clip, 0, "mask clipper",
42                           0, 0, 1, 1),
43  {gx_default_open_device,
44   gx_forward_get_initial_matrix,
45   gx_default_sync_output,
46   gx_default_output_page,
47   gx_default_close_device,
48   gx_forward_map_rgb_color,
49   gx_forward_map_color_rgb,
50   mask_clip_fill_rectangle,
51   gx_default_tile_rectangle,
52   mask_clip_copy_mono,
53   mask_clip_copy_color,
54   gx_default_draw_line,
55   gx_forward_get_bits,
56   gx_forward_get_params,
57   gx_forward_put_params,
58   gx_forward_map_cmyk_color,
59   gx_forward_get_xfont_procs,
60   gx_forward_get_xfont_device,
61   gx_forward_map_rgb_alpha_color,
62   gx_forward_get_page_device,
63   gx_forward_get_alpha_bits,
64   mask_clip_copy_alpha,
65   gx_forward_get_band,
66   gx_default_copy_rop,
67   gx_default_fill_path,
68   gx_default_stroke_path,
69   gx_default_fill_mask,
70   gx_default_fill_trapezoid,
71   gx_default_fill_parallelogram,
72   gx_default_fill_triangle,
73   gx_default_draw_thin_line,
74   gx_default_begin_image,
75   gx_default_image_data,
76   gx_default_end_image,
77   mask_clip_strip_tile_rectangle,
78   mask_clip_strip_copy_rop,
79   mask_clip_get_clipping_box,
80   gx_default_begin_typed_image,
81   gx_forward_get_bits_rectangle,
82   gx_forward_map_color_rgb_alpha,
83   gx_no_create_compositor,
84   gx_forward_get_hardware_params,
85   gx_default_text_begin,
86   gx_default_finish_copydevice,
87   NULL,			/* begin_transparency_group */
88   NULL,			/* end_transparency_group */
89   NULL,			/* begin_transparency_mask */
90   NULL,			/* end_transparency_mask */
91   NULL,			/* discard_transparency_layer */
92   gx_forward_get_color_mapping_procs,
93   gx_forward_get_color_comp_index,
94   gx_forward_encode_color,
95   gx_forward_decode_color,
96   NULL,                 /* pattern_manage */
97   mask_clip_fill_rectangle_hl_color,
98   gx_forward_include_color_space,
99   gx_forward_fill_linear_color_scanline,
100   gx_forward_fill_linear_color_trapezoid,
101   gx_forward_fill_linear_color_triangle,
102   gx_forward_update_spot_equivalent_colors,
103   gx_forward_ret_devn_params,
104   gx_forward_fillpage,
105   NULL,                      /* push_transparency_state */
106   NULL,                      /* pop_transparency_state */
107   NULL,                      /* put_image */
108   gx_forward_dev_spec_op,
109   NULL,
110   gx_forward_get_profile,
111   gx_forward_set_graphics_type_tag,
112   mask_clip_strip_copy_rop2,
113   mask_clip_strip_tile_rect_devn,
114   mask_clip_copy_alpha_hl_color
115  }
116 };
117 
118 /* Fill a rectangle with a hl color, painting through the mask */
119 static int
mask_clip_fill_rectangle_hl_color(gx_device * dev,const gs_fixed_rect * rect,const gs_imager_state * pis,const gx_drawing_color * pdcolor,const gx_clip_path * pcpath)120 mask_clip_fill_rectangle_hl_color(gx_device *dev,
121     const gs_fixed_rect *rect,
122     const gs_imager_state *pis, const gx_drawing_color *pdcolor,
123     const gx_clip_path *pcpath)
124 {
125     gx_device_mask_clip *cdev = (gx_device_mask_clip *) dev;
126     gx_device *tdev = cdev->target;
127     int x, y, w, h;
128     int mx0, mx1, my0, my1;
129 
130     x = rect->p.x;
131     y = rect->p.y;
132     w = rect->q.x - x;
133     h = rect->q.y - y;
134 
135     /* Clip the rectangle to the region covered by the mask. */
136     mx0 = x + cdev->phase.x;
137     my0 = y + cdev->phase.y;
138     mx1 = mx0 + w;
139     my1 = my0 + h;
140 
141     if (mx0 < 0)
142         mx0 = 0;
143     if (my0 < 0)
144         my0 = 0;
145     if (mx1 > cdev->tiles.size.x)
146         mx1 = cdev->tiles.size.x;
147     if (my1 > cdev->tiles.size.y)
148         my1 = cdev->tiles.size.y;
149     /* It would be nice to have a higher level way to do this operation
150        like a copy_mono_hl_color */
151     return (pdcolor->type->fill_masked)
152             (pdcolor, cdev->tiles.data + my0 * cdev->tiles.raster, mx0,
153              cdev->tiles.raster, cdev->tiles.id, mx0 - cdev->phase.x,
154              my0 - cdev->phase.y, mx1 - mx0, my1 - my0,
155              tdev, lop_default, false);
156 }
157 
158 /* Fill a rectangle by painting through the mask. */
159 static int
mask_clip_fill_rectangle(gx_device * dev,int x,int y,int w,int h,gx_color_index color)160 mask_clip_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
161                          gx_color_index color)
162 {
163     gx_device_mask_clip *cdev = (gx_device_mask_clip *) dev;
164     gx_device *tdev = cdev->target;
165 
166     /* Clip the rectangle to the region covered by the mask. */
167     int mx0 = x + cdev->phase.x, my0 = y + cdev->phase.y;
168     int mx1 = mx0 + w, my1 = my0 + h;
169 
170     if (mx0 < 0)
171         mx0 = 0;
172     if (my0 < 0)
173         my0 = 0;
174     if (mx1 > cdev->tiles.size.x)
175         mx1 = cdev->tiles.size.x;
176     if (my1 > cdev->tiles.size.y)
177         my1 = cdev->tiles.size.y;
178     return (*dev_proc(tdev, copy_mono))
179         (tdev, cdev->tiles.data + my0 * cdev->tiles.raster, mx0,
180          cdev->tiles.raster, cdev->tiles.id,
181          mx0 - cdev->phase.x, my0 - cdev->phase.y,
182          mx1 - mx0, my1 - my0, gx_no_color_index, color);
183 }
184 
185 /*
186  * Clip the rectangle for a copy operation.
187  * Sets m{x,y}{0,1} to the region in the mask coordinate system;
188  * subtract cdev->phase.{x,y} to get target coordinates.
189  * Sets sdata, sx to adjusted values of data, sourcex.
190  * References cdev, data, sourcex, raster, x, y, w, h.
191  */
192 #define DECLARE_MASK_COPY\
193         const byte *sdata;\
194         int sx, mx0, my0, mx1, my1
195 #define FIT_MASK_COPY(data, sourcex, raster, vx, vy, vw, vh)\
196         BEGIN\
197           sdata = data, sx = sourcex;\
198           mx0 = vx + cdev->phase.x, my0 = vy + cdev->phase.y;\
199           mx1 = mx0 + vw, my1 = my0 + vh;\
200           if ( mx0 < 0 )\
201             sx -= mx0, mx0 = 0;\
202           if ( my0 < 0 )\
203             sdata -= my0 * raster, my0 = 0;\
204           if ( mx1 > cdev->tiles.size.x )\
205             mx1 = cdev->tiles.size.x;\
206           if ( my1 > cdev->tiles.size.y )\
207             my1 = cdev->tiles.size.y;\
208         END
209 
210 /* Copy a monochrome bitmap by playing Boolean games. */
211 static int
mask_clip_copy_mono(gx_device * dev,const byte * data,int sourcex,int raster,gx_bitmap_id id,int x,int y,int w,int h,gx_color_index color0,gx_color_index color1)212 mask_clip_copy_mono(gx_device * dev,
213                 const byte * data, int sourcex, int raster, gx_bitmap_id id,
214                     int x, int y, int w, int h,
215                     gx_color_index color0, gx_color_index color1)
216 {
217     gx_device_mask_clip *cdev = (gx_device_mask_clip *) dev;
218     gx_device *tdev = cdev->target;
219     gx_color_index color, mcolor0, mcolor1;
220 
221     DECLARE_MASK_COPY;
222     int cy, ny;
223     int code;
224 
225     setup_mask_copy_mono(cdev, color, mcolor0, mcolor1);
226     FIT_MASK_COPY(data, sourcex, raster, x, y, w, h);
227     for (cy = my0; cy < my1; cy += ny) {
228         int ty = cy - cdev->phase.y;
229         int cx, nx;
230 
231         ny = my1 - cy;
232         if (ny > cdev->mdev.height)
233             ny = cdev->mdev.height;
234         for (cx = mx0; cx < mx1; cx += nx) {
235             int tx = cx - cdev->phase.x;
236 
237             nx = mx1 - cx;	/* also should be min */
238             /* Copy a tile slice to the memory device buffer. */
239             memcpy(cdev->buffer.bytes,
240                    cdev->tiles.data + cy * cdev->tiles.raster,
241                    cdev->tiles.raster * ny);
242             /* Intersect the tile with the source data. */
243             /* mcolor0 and mcolor1 invert the data if needed. */
244             /* This call can't fail. */
245             (*dev_proc(&cdev->mdev, copy_mono)) ((gx_device *) & cdev->mdev,
246                                      sdata + (ty - y) * raster, sx + tx - x,
247                                                  raster, gx_no_bitmap_id,
248                                            cx, 0, nx, ny, mcolor0, mcolor1);
249             /* Now copy the color through the double mask. */
250             code = (*dev_proc(tdev, copy_mono)) (cdev->target,
251                                  cdev->buffer.bytes, cx, cdev->tiles.raster,
252                                                  gx_no_bitmap_id,
253                                   tx, ty, nx, ny, gx_no_color_index, color);
254             if (code < 0)
255                 return code;
256         }
257     }
258     return 0;
259 }
260 
261 /*
262  * Define the run enumerator for the other copying operations.  We can't use
263  * the BitBlt tricks: we have to scan for runs of 1s.  There are obvious
264  * ways to speed this up; we'll implement some if we need to.
265  */
266 static int
clip_runs_enumerate(gx_device_mask_clip * cdev,int (* process)(clip_callback_data_t * pccd,int xc,int yc,int xec,int yec),clip_callback_data_t * pccd)267 clip_runs_enumerate(gx_device_mask_clip * cdev,
268                     int (*process) (clip_callback_data_t * pccd, int xc, int yc, int xec, int yec),
269                     clip_callback_data_t * pccd)
270 {
271     DECLARE_MASK_COPY;
272     int cy;
273     const byte *tile_row;
274     gs_int_rect prev;
275     int code;
276 
277     FIT_MASK_COPY(pccd->data, pccd->sourcex, pccd->raster,
278                   pccd->x, pccd->y, pccd->w, pccd->h);
279     tile_row = cdev->tiles.data + my0 * cdev->tiles.raster + (mx0 >> 3);
280     prev.p.x = 0;	/* arbitrary */
281     prev.q.x = prev.p.x - 1;	/* an impossible rectangle */
282     prev.p.y = prev.q.y = -1;	/* arbitrary */
283     for (cy = my0; cy < my1; cy++) {
284         int cx = mx0;
285         const byte *tp = tile_row;
286 
287         if_debug1('B', "[B]clip runs y=%d:", cy - cdev->phase.y);
288         while (cx < mx1) {
289             int len;
290             int tx1, tx, ty;
291 
292             /* Skip a run of 0s. */
293             len = byte_bit_run_length[cx & 7][*tp ^ 0xff];
294             if (len < 8) {
295                 cx += len;
296                 if (cx >= mx1)
297                     break;
298             } else {
299                 cx += len - 8;
300                 tp++;
301                 while (cx < mx1 && *tp == 0)
302                     cx += 8, tp++;
303                 if (cx >= mx1)
304                     break;
305                 cx += byte_bit_run_length_0[*tp ^ 0xff];
306                 if (cx >= mx1)
307                     break;
308             }
309             tx1 = cx - cdev->phase.x;
310             /* Scan a run of 1s. */
311             len = byte_bit_run_length[cx & 7][*tp];
312             if (len < 8) {
313                 cx += len;
314                 if (cx > mx1)
315                     cx = mx1;
316             } else {
317                 cx += len - 8;
318                 tp++;
319                 while (cx < mx1 && *tp == 0xff)
320                     cx += 8, tp++;
321                 if (cx > mx1)
322                     cx = mx1;
323                 else {
324                     cx += byte_bit_run_length_0[*tp];
325                     if (cx > mx1)
326                         cx = mx1;
327                 }
328             }
329             tx = cx - cdev->phase.x;
330             if_debug2('B', " %d-%d,", tx1, tx);
331             ty = cy - cdev->phase.y;
332             /* Detect vertical rectangles. */
333             if (prev.p.x == tx1 && prev.q.x == tx && prev.q.y == ty)
334                 prev.q.y = ty + 1;
335             else {
336                 if (prev.q.y > prev.p.y) {
337                     code = (*process)(pccd, prev.p.x, prev.p.y, prev.q.x, prev.q.y);
338                     if (code < 0)
339                         return code;
340                 }
341                 prev.p.x = tx1;
342                 prev.p.y = ty;
343                 prev.q.x = tx;
344                 prev.q.y = ty + 1;
345             }
346         }
347         if_debug0('B', "\n");
348         tile_row += cdev->tiles.raster;
349     }
350     if (prev.q.y > prev.p.y) {
351         code = (*process)(pccd, prev.p.x, prev.p.y, prev.q.x, prev.q.y);
352         if (code < 0)
353             return code;
354     }
355     return 0;
356 }
357 
358 /* Copy a color rectangle */
359 static int
mask_clip_copy_color(gx_device * dev,const byte * data,int sourcex,int raster,gx_bitmap_id id,int x,int y,int w,int h)360 mask_clip_copy_color(gx_device * dev,
361                 const byte * data, int sourcex, int raster, gx_bitmap_id id,
362                      int x, int y, int w, int h)
363 {
364     gx_device_mask_clip *cdev = (gx_device_mask_clip *) dev;
365     clip_callback_data_t ccdata;
366 
367     ccdata.tdev = cdev->target;
368     ccdata.data = data, ccdata.sourcex = sourcex, ccdata.raster = raster;
369     ccdata.x = x, ccdata.y = y, ccdata.w = w, ccdata.h = h;
370     return clip_runs_enumerate(cdev, clip_call_copy_color, &ccdata);
371 }
372 
373 /* Copy a rectangle with alpha */
374 static int
mask_clip_copy_alpha(gx_device * dev,const byte * data,int sourcex,int raster,gx_bitmap_id id,int x,int y,int w,int h,gx_color_index color,int depth)375 mask_clip_copy_alpha(gx_device * dev,
376                 const byte * data, int sourcex, int raster, gx_bitmap_id id,
377                 int x, int y, int w, int h, gx_color_index color, int depth)
378 {
379     gx_device_mask_clip *cdev = (gx_device_mask_clip *) dev;
380     clip_callback_data_t ccdata;
381 
382     ccdata.tdev = cdev->target;
383     ccdata.data = data, ccdata.sourcex = sourcex, ccdata.raster = raster;
384     ccdata.x = x, ccdata.y = y, ccdata.w = w, ccdata.h = h;
385     ccdata.color[0] = color, ccdata.depth = depth;
386     return clip_runs_enumerate(cdev, clip_call_copy_alpha, &ccdata);
387 }
388 
389 static int
mask_clip_copy_alpha_hl_color(gx_device * dev,const byte * data,int sourcex,int raster,gx_bitmap_id id,int x,int y,int w,int h,const gx_drawing_color * pdcolor,int depth)390 mask_clip_copy_alpha_hl_color(gx_device * dev,
391                 const byte * data, int sourcex, int raster, gx_bitmap_id id,
392                 int x, int y, int w, int h, const gx_drawing_color *pdcolor,
393                 int depth)
394 {
395     gx_device_mask_clip *cdev = (gx_device_mask_clip *) dev;
396     clip_callback_data_t ccdata;
397 
398     ccdata.tdev = cdev->target;
399     ccdata.data = data, ccdata.sourcex = sourcex, ccdata.raster = raster;
400     ccdata.x = x, ccdata.y = y, ccdata.w = w, ccdata.h = h;
401     ccdata.pdcolor = pdcolor, ccdata.depth = depth;
402     return clip_runs_enumerate(cdev, clip_call_copy_alpha_hl_color, &ccdata);
403 }
404 
405 static int
mask_clip_strip_tile_rectangle(gx_device * dev,const gx_strip_bitmap * tiles,int x,int y,int w,int h,gx_color_index color0,gx_color_index color1,int phase_x,int phase_y)406 mask_clip_strip_tile_rectangle(gx_device * dev, const gx_strip_bitmap * tiles,
407                                int x, int y, int w, int h,
408                                gx_color_index color0, gx_color_index color1,
409                                int phase_x, int phase_y)
410 {
411     gx_device_mask_clip *cdev = (gx_device_mask_clip *) dev;
412     clip_callback_data_t ccdata;
413 
414     ccdata.tdev = cdev->target;
415     ccdata.x = x, ccdata.y = y, ccdata.w = w, ccdata.h = h;
416     ccdata.tiles = tiles;
417     ccdata.color[0] = color0, ccdata.color[1] = color1;
418     ccdata.phase.x = phase_x, ccdata.phase.y = phase_y;
419     return clip_runs_enumerate(cdev, clip_call_strip_tile_rectangle, &ccdata);
420 }
421 
422 static int
mask_clip_strip_tile_rect_devn(gx_device * dev,const gx_strip_bitmap * tiles,int x,int y,int w,int h,const gx_drawing_color * pdcolor0,const gx_drawing_color * pdcolor1,int phase_x,int phase_y)423 mask_clip_strip_tile_rect_devn(gx_device * dev, const gx_strip_bitmap * tiles,
424                                int x, int y, int w, int h,
425                                const gx_drawing_color *pdcolor0,
426                                const gx_drawing_color *pdcolor1,
427                                int phase_x, int phase_y)
428 {
429     gx_device_mask_clip *cdev = (gx_device_mask_clip *) dev;
430     clip_callback_data_t ccdata;
431 
432     ccdata.tdev = cdev->target;
433     ccdata.x = x, ccdata.y = y, ccdata.w = w, ccdata.h = h;
434     ccdata.tiles = tiles;
435     ccdata.pdc[0] = pdcolor0, ccdata.pdc[1] = pdcolor1;
436     ccdata.phase.x = phase_x, ccdata.phase.y = phase_y;
437     return clip_runs_enumerate(cdev, clip_call_strip_tile_rect_devn, &ccdata);
438 }
439 
440 static int
mask_clip_strip_copy_rop(gx_device * dev,const byte * data,int sourcex,uint raster,gx_bitmap_id id,const gx_color_index * scolors,const gx_strip_bitmap * textures,const gx_color_index * tcolors,int x,int y,int w,int h,int phase_x,int phase_y,gs_logical_operation_t lop)441 mask_clip_strip_copy_rop(gx_device * dev,
442                const byte * data, int sourcex, uint raster, gx_bitmap_id id,
443                          const gx_color_index * scolors,
444            const gx_strip_bitmap * textures, const gx_color_index * tcolors,
445                          int x, int y, int w, int h,
446                        int phase_x, int phase_y, gs_logical_operation_t lop)
447 {
448     gx_device_mask_clip *cdev = (gx_device_mask_clip *) dev;
449     clip_callback_data_t ccdata;
450 
451     ccdata.tdev = cdev->target;
452     ccdata.x = x, ccdata.y = y, ccdata.w = w, ccdata.h = h;
453     ccdata.data = data, ccdata.sourcex = sourcex, ccdata.raster = raster;
454     ccdata.scolors = scolors, ccdata.textures = textures,
455         ccdata.tcolors = tcolors;
456     ccdata.phase.x = phase_x, ccdata.phase.y = phase_y, ccdata.lop = lop;
457     return clip_runs_enumerate(cdev, clip_call_strip_copy_rop, &ccdata);
458 }
459 
460 static int
mask_clip_strip_copy_rop2(gx_device * dev,const byte * data,int sourcex,uint raster,gx_bitmap_id id,const gx_color_index * scolors,const gx_strip_bitmap * textures,const gx_color_index * tcolors,int x,int y,int w,int h,int phase_x,int phase_y,gs_logical_operation_t lop,uint planar_height)461 mask_clip_strip_copy_rop2(gx_device * dev,
462                const byte * data, int sourcex, uint raster, gx_bitmap_id id,
463                          const gx_color_index * scolors,
464            const gx_strip_bitmap * textures, const gx_color_index * tcolors,
465                          int x, int y, int w, int h,
466                        int phase_x, int phase_y, gs_logical_operation_t lop,
467                        uint planar_height)
468 {
469     gx_device_mask_clip *cdev = (gx_device_mask_clip *) dev;
470     clip_callback_data_t ccdata;
471 
472     ccdata.tdev = cdev->target;
473     ccdata.x = x, ccdata.y = y, ccdata.w = w, ccdata.h = h;
474     ccdata.data = data, ccdata.sourcex = sourcex, ccdata.raster = raster;
475     ccdata.scolors = scolors, ccdata.textures = textures,
476         ccdata.tcolors = tcolors;
477     ccdata.phase.x = phase_x, ccdata.phase.y = phase_y, ccdata.lop = lop;
478     ccdata.plane_height = planar_height;
479     return clip_runs_enumerate(cdev, clip_call_strip_copy_rop2, &ccdata);
480 }
481 
482 static void
mask_clip_get_clipping_box(gx_device * dev,gs_fixed_rect * pbox)483 mask_clip_get_clipping_box(gx_device * dev, gs_fixed_rect * pbox)
484 {
485     gx_device_mask_clip *cdev = (gx_device_mask_clip *) dev;
486     gx_device *tdev = cdev->target;
487     gs_fixed_rect tbox;
488 
489     (*dev_proc(tdev, get_clipping_box)) (tdev, &tbox);
490     pbox->p.x = tbox.p.x - cdev->phase.x;
491     pbox->p.y = tbox.p.y - cdev->phase.y;
492     pbox->q.x = tbox.q.x - cdev->phase.x;
493     pbox->q.y = tbox.q.y - cdev->phase.y;
494 }
495