xref: /plan9/sys/src/cmd/gs/src/gxclipm.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1998, 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: gxclipm.c,v 1.13 2004/06/24 05:03:36 dan Exp $ */
18 /* Mask clipping device */
19 #include "memory_.h"
20 #include "gx.h"
21 #include "gsbittab.h"
22 #include "gxdevice.h"
23 #include "gxdevmem.h"
24 #include "gxclipm.h"
25 
26 /* Device procedures */
27 private dev_proc_fill_rectangle(mask_clip_fill_rectangle);
28 private dev_proc_copy_mono(mask_clip_copy_mono);
29 private dev_proc_copy_color(mask_clip_copy_color);
30 private dev_proc_copy_alpha(mask_clip_copy_alpha);
31 private dev_proc_strip_tile_rectangle(mask_clip_strip_tile_rectangle);
32 private dev_proc_strip_copy_rop(mask_clip_strip_copy_rop);
33 private dev_proc_get_clipping_box(mask_clip_get_clipping_box);
34 
35 /* The device descriptor. */
36 const gx_device_mask_clip gs_mask_clip_device =
37 {std_device_std_body_open(gx_device_mask_clip, 0, "mask clipper",
38 			  0, 0, 1, 1),
39  {gx_default_open_device,
40   gx_forward_get_initial_matrix,
41   gx_default_sync_output,
42   gx_default_output_page,
43   gx_default_close_device,
44   gx_forward_map_rgb_color,
45   gx_forward_map_color_rgb,
46   mask_clip_fill_rectangle,
47   gx_default_tile_rectangle,
48   mask_clip_copy_mono,
49   mask_clip_copy_color,
50   gx_default_draw_line,
51   gx_forward_get_bits,
52   gx_forward_get_params,
53   gx_forward_put_params,
54   gx_forward_map_cmyk_color,
55   gx_forward_get_xfont_procs,
56   gx_forward_get_xfont_device,
57   gx_forward_map_rgb_alpha_color,
58   gx_forward_get_page_device,
59   gx_forward_get_alpha_bits,
60   mask_clip_copy_alpha,
61   gx_forward_get_band,
62   gx_default_copy_rop,
63   gx_default_fill_path,
64   gx_default_stroke_path,
65   gx_default_fill_mask,
66   gx_default_fill_trapezoid,
67   gx_default_fill_parallelogram,
68   gx_default_fill_triangle,
69   gx_default_draw_thin_line,
70   gx_default_begin_image,
71   gx_default_image_data,
72   gx_default_end_image,
73   mask_clip_strip_tile_rectangle,
74   mask_clip_strip_copy_rop,
75   mask_clip_get_clipping_box,
76   gx_default_begin_typed_image,
77   gx_forward_get_bits_rectangle,
78   gx_forward_map_color_rgb_alpha,
79   gx_no_create_compositor,
80   gx_forward_get_hardware_params,
81   gx_default_text_begin,
82   gx_default_finish_copydevice,
83   NULL,			/* begin_transparency_group */
84   NULL,			/* end_transparency_group */
85   NULL,			/* begin_transparency_mask */
86   NULL,			/* end_transparency_mask */
87   NULL,			/* discard_transparency_layer */
88   gx_forward_get_color_mapping_procs,
89   gx_forward_get_color_comp_index,
90   gx_forward_encode_color,
91   gx_forward_decode_color,
92   gx_forward_pattern_manage,
93   gx_forward_fill_rectangle_hl_color,
94   gx_forward_include_color_space,
95   gx_forward_fill_linear_color_scanline,
96   gx_forward_fill_linear_color_trapezoid,
97   gx_forward_fill_linear_color_triangle,
98   gx_forward_update_spot_equivalent_colors
99  }
100 };
101 
102 /* Fill a rectangle by painting through the mask. */
103 private int
mask_clip_fill_rectangle(gx_device * dev,int x,int y,int w,int h,gx_color_index color)104 mask_clip_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
105 			 gx_color_index color)
106 {
107     gx_device_mask_clip *cdev = (gx_device_mask_clip *) dev;
108     gx_device *tdev = cdev->target;
109 
110     /* Clip the rectangle to the region covered by the mask. */
111     int mx0 = x + cdev->phase.x, my0 = y + cdev->phase.y;
112     int mx1 = mx0 + w, my1 = my0 + h;
113 
114     if (mx0 < 0)
115 	mx0 = 0;
116     if (my0 < 0)
117 	my0 = 0;
118     if (mx1 > cdev->tiles.size.x)
119 	mx1 = cdev->tiles.size.x;
120     if (my1 > cdev->tiles.size.y)
121 	my1 = cdev->tiles.size.y;
122     return (*dev_proc(tdev, copy_mono))
123 	(tdev, cdev->tiles.data + my0 * cdev->tiles.raster, mx0,
124 	 cdev->tiles.raster, cdev->tiles.id,
125 	 mx0 - cdev->phase.x, my0 - cdev->phase.y,
126 	 mx1 - mx0, my1 - my0, gx_no_color_index, color);
127 }
128 
129 /*
130  * Clip the rectangle for a copy operation.
131  * Sets m{x,y}{0,1} to the region in the mask coordinate system;
132  * subtract cdev->phase.{x,y} to get target coordinates.
133  * Sets sdata, sx to adjusted values of data, sourcex.
134  * References cdev, data, sourcex, raster, x, y, w, h.
135  */
136 #define DECLARE_MASK_COPY\
137 	const byte *sdata;\
138 	int sx, mx0, my0, mx1, my1
139 #define FIT_MASK_COPY(data, sourcex, raster, vx, vy, vw, vh)\
140 	BEGIN\
141 	  sdata = data, sx = sourcex;\
142 	  mx0 = vx + cdev->phase.x, my0 = vy + cdev->phase.y;\
143 	  mx1 = mx0 + vw, my1 = my0 + vh;\
144 	  if ( mx0 < 0 )\
145 	    sx -= mx0, mx0 = 0;\
146 	  if ( my0 < 0 )\
147 	    sdata -= my0 * raster, my0 = 0;\
148 	  if ( mx1 > cdev->tiles.size.x )\
149 	    mx1 = cdev->tiles.size.x;\
150 	  if ( my1 > cdev->tiles.size.y )\
151 	    my1 = cdev->tiles.size.y;\
152 	END
153 
154 /* Copy a monochrome bitmap by playing Boolean games. */
155 private 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)156 mask_clip_copy_mono(gx_device * dev,
157 		const byte * data, int sourcex, int raster, gx_bitmap_id id,
158 		    int x, int y, int w, int h,
159 		    gx_color_index color0, gx_color_index color1)
160 {
161     gx_device_mask_clip *cdev = (gx_device_mask_clip *) dev;
162     gx_device *tdev = cdev->target;
163     gx_color_index color, mcolor0, mcolor1;
164 
165     DECLARE_MASK_COPY;
166     int cy, ny;
167     int code;
168 
169     setup_mask_copy_mono(cdev, color, mcolor0, mcolor1);
170     FIT_MASK_COPY(data, sourcex, raster, x, y, w, h);
171     for (cy = my0; cy < my1; cy += ny) {
172 	int ty = cy - cdev->phase.y;
173 	int cx, nx;
174 
175 	ny = my1 - cy;
176 	if (ny > cdev->mdev.height)
177 	    ny = cdev->mdev.height;
178 	for (cx = mx0; cx < mx1; cx += nx) {
179 	    int tx = cx - cdev->phase.x;
180 
181 	    nx = mx1 - cx;	/* also should be min */
182 	    /* Copy a tile slice to the memory device buffer. */
183 	    memcpy(cdev->buffer.bytes,
184 		   cdev->tiles.data + cy * cdev->tiles.raster,
185 		   cdev->tiles.raster * ny);
186 	    /* Intersect the tile with the source data. */
187 	    /* mcolor0 and mcolor1 invert the data if needed. */
188 	    /* This call can't fail. */
189 	    (*dev_proc(&cdev->mdev, copy_mono)) ((gx_device *) & cdev->mdev,
190 				     sdata + (ty - y) * raster, sx + tx - x,
191 						 raster, gx_no_bitmap_id,
192 					   cx, 0, nx, ny, mcolor0, mcolor1);
193 	    /* Now copy the color through the double mask. */
194 	    code = (*dev_proc(tdev, copy_mono)) (cdev->target,
195 				 cdev->buffer.bytes, cx, cdev->tiles.raster,
196 						 gx_no_bitmap_id,
197 				  tx, ty, nx, ny, gx_no_color_index, color);
198 	    if (code < 0)
199 		return code;
200 	}
201     }
202     return 0;
203 }
204 
205 /*
206  * Define the run enumerator for the other copying operations.  We can't use
207  * the BitBlt tricks: we have to scan for runs of 1s.  There are obvious
208  * ways to speed this up; we'll implement some if we need to.
209  */
210 private 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)211 clip_runs_enumerate(gx_device_mask_clip * cdev,
212 		    int (*process) (clip_callback_data_t * pccd, int xc, int yc, int xec, int yec),
213 		    clip_callback_data_t * pccd)
214 {
215     DECLARE_MASK_COPY;
216     int cy;
217     const byte *tile_row;
218     gs_int_rect prev;
219     int code;
220 
221     FIT_MASK_COPY(pccd->data, pccd->sourcex, pccd->raster,
222 		  pccd->x, pccd->y, pccd->w, pccd->h);
223     tile_row = cdev->tiles.data + my0 * cdev->tiles.raster + (mx0 >> 3);
224     prev.p.x = 0;	/* arbitrary */
225     prev.q.x = prev.p.x - 1;	/* an impossible rectangle */
226     prev.p.y = prev.q.y = -1;	/* arbitrary */
227     for (cy = my0; cy < my1; cy++) {
228 	int cx = mx0;
229 	const byte *tp = tile_row;
230 
231 	if_debug1('B', "[B]clip runs y=%d:", cy - cdev->phase.y);
232 	while (cx < mx1) {
233 	    int len;
234 	    int tx1, tx, ty;
235 
236 	    /* Skip a run of 0s. */
237 	    len = byte_bit_run_length[cx & 7][*tp ^ 0xff];
238 	    if (len < 8) {
239 		cx += len;
240 		if (cx >= mx1)
241 		    break;
242 	    } else {
243 		cx += len - 8;
244 		tp++;
245 		while (cx < mx1 && *tp == 0)
246 		    cx += 8, tp++;
247 		if (cx >= mx1)
248 		    break;
249 		cx += byte_bit_run_length_0[*tp ^ 0xff];
250 		if (cx >= mx1)
251 		    break;
252 	    }
253 	    tx1 = cx - cdev->phase.x;
254 	    /* Scan a run of 1s. */
255 	    len = byte_bit_run_length[cx & 7][*tp];
256 	    if (len < 8) {
257 		cx += len;
258 		if (cx > mx1)
259 		    cx = mx1;
260 	    } else {
261 		cx += len - 8;
262 		tp++;
263 		while (cx < mx1 && *tp == 0xff)
264 		    cx += 8, tp++;
265 		if (cx > mx1)
266 		    cx = mx1;
267 		else {
268 		    cx += byte_bit_run_length_0[*tp];
269 		    if (cx > mx1)
270 			cx = mx1;
271 		}
272 	    }
273 	    tx = cx - cdev->phase.x;
274 	    if_debug2('B', " %d-%d,", tx1, tx);
275 	    ty = cy - cdev->phase.y;
276 	    /* Detect vertical rectangles. */
277 	    if (prev.p.x == tx1 && prev.q.x == tx && prev.q.y == ty)
278 		prev.q.y = ty + 1;
279 	    else {
280 		if (prev.q.y > prev.p.y) {
281 		    code = (*process)(pccd, tx1, ty, tx, ty + 1);
282 		    if (code < 0)
283 			return code;
284 		}
285 		prev.p.x = tx1;
286 		prev.p.y = ty;
287 		prev.q.x = tx;
288 		prev.q.y = ty + 1;
289 	    }
290 	}
291 	if_debug0('B', "\n");
292 	tile_row += cdev->tiles.raster;
293     }
294     if (prev.q.y > prev.p.y) {
295 	code = (*process)(pccd, prev.p.x, prev.p.y, prev.q.x, prev.q.y);
296 	if (code < 0)
297 	    return code;
298     }
299     return 0;
300 }
301 
302 /* Copy a color rectangle */
303 private 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)304 mask_clip_copy_color(gx_device * dev,
305 		const byte * data, int sourcex, int raster, gx_bitmap_id id,
306 		     int x, int y, int w, int h)
307 {
308     gx_device_mask_clip *cdev = (gx_device_mask_clip *) dev;
309     clip_callback_data_t ccdata;
310 
311     ccdata.tdev = cdev->target;
312     ccdata.data = data, ccdata.sourcex = sourcex, ccdata.raster = raster;
313     ccdata.x = x, ccdata.y = y, ccdata.w = w, ccdata.h = h;
314     return clip_runs_enumerate(cdev, clip_call_copy_color, &ccdata);
315 }
316 
317 /* Copy a rectangle with alpha */
318 private 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)319 mask_clip_copy_alpha(gx_device * dev,
320 		const byte * data, int sourcex, int raster, gx_bitmap_id id,
321 		int x, int y, int w, int h, gx_color_index color, int depth)
322 {
323     gx_device_mask_clip *cdev = (gx_device_mask_clip *) dev;
324     clip_callback_data_t ccdata;
325 
326     ccdata.tdev = cdev->target;
327     ccdata.data = data, ccdata.sourcex = sourcex, ccdata.raster = raster;
328     ccdata.x = x, ccdata.y = y, ccdata.w = w, ccdata.h = h;
329     ccdata.color[0] = color, ccdata.depth = depth;
330     return clip_runs_enumerate(cdev, clip_call_copy_alpha, &ccdata);
331 }
332 
333 private 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)334 mask_clip_strip_tile_rectangle(gx_device * dev, const gx_strip_bitmap * tiles,
335 			       int x, int y, int w, int h,
336 			       gx_color_index color0, gx_color_index color1,
337 			       int phase_x, int phase_y)
338 {
339     gx_device_mask_clip *cdev = (gx_device_mask_clip *) dev;
340     clip_callback_data_t ccdata;
341 
342     ccdata.tdev = cdev->target;
343     ccdata.x = x, ccdata.y = y, ccdata.w = w, ccdata.h = h;
344     ccdata.tiles = tiles;
345     ccdata.color[0] = color0, ccdata.color[1] = color1;
346     ccdata.phase.x = phase_x, ccdata.phase.y = phase_y;
347     return clip_runs_enumerate(cdev, clip_call_strip_tile_rectangle, &ccdata);
348 }
349 
350 private 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)351 mask_clip_strip_copy_rop(gx_device * dev,
352 	       const byte * data, int sourcex, uint raster, gx_bitmap_id id,
353 			 const gx_color_index * scolors,
354 	   const gx_strip_bitmap * textures, const gx_color_index * tcolors,
355 			 int x, int y, int w, int h,
356 		       int phase_x, int phase_y, gs_logical_operation_t lop)
357 {
358     gx_device_mask_clip *cdev = (gx_device_mask_clip *) dev;
359     clip_callback_data_t ccdata;
360 
361     ccdata.tdev = cdev->target;
362     ccdata.x = x, ccdata.y = y, ccdata.w = w, ccdata.h = h;
363     ccdata.data = data, ccdata.sourcex = sourcex, ccdata.raster = raster;
364     ccdata.scolors = scolors, ccdata.textures = textures,
365 	ccdata.tcolors = tcolors;
366     ccdata.phase.x = phase_x, ccdata.phase.y = phase_y, ccdata.lop = lop;
367     return clip_runs_enumerate(cdev, clip_call_strip_copy_rop, &ccdata);
368 }
369 
370 private void
mask_clip_get_clipping_box(gx_device * dev,gs_fixed_rect * pbox)371 mask_clip_get_clipping_box(gx_device * dev, gs_fixed_rect * pbox)
372 {
373     gx_device_mask_clip *cdev = (gx_device_mask_clip *) dev;
374     gx_device *tdev = cdev->target;
375     gs_fixed_rect tbox;
376 
377     (*dev_proc(tdev, get_clipping_box)) (tdev, &tbox);
378     pbox->p.x = tbox.p.x - cdev->phase.x;
379     pbox->p.y = tbox.p.y - cdev->phase.y;
380     pbox->q.x = tbox.q.x - cdev->phase.x;
381     pbox->q.y = tbox.q.y - cdev->phase.y;
382 }
383