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