xref: /plan9/sys/src/cmd/gs/src/gxclip2.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1993, 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: gxclip2.c,v 1.11 2004/06/24 05:03:36 dan Exp $ */
18 /* Mask clipping for patterns */
19 #include "memory_.h"
20 #include "gx.h"
21 #include "gserrors.h"
22 #include "gsstruct.h"
23 #include "gxdevice.h"
24 #include "gxdevmem.h"
25 #include "gxclip2.h"
26 
27 private_st_device_tile_clip();
28 
29 /* Device procedures */
30 private dev_proc_fill_rectangle(tile_clip_fill_rectangle);
31 private dev_proc_copy_mono(tile_clip_copy_mono);
32 private dev_proc_copy_color(tile_clip_copy_color);
33 private dev_proc_copy_alpha(tile_clip_copy_alpha);
34 private dev_proc_strip_copy_rop(tile_clip_strip_copy_rop);
35 
36 /* The device descriptor. */
37 private const gx_device_tile_clip gs_tile_clip_device =
38 {std_device_std_body_open(gx_device_tile_clip, 0, "tile clipper",
39 			  0, 0, 1, 1),
40  {gx_default_open_device,
41   gx_forward_get_initial_matrix,
42   gx_default_sync_output,
43   gx_default_output_page,
44   gx_default_close_device,
45   gx_forward_map_rgb_color,
46   gx_forward_map_color_rgb,
47   tile_clip_fill_rectangle,
48   gx_default_tile_rectangle,
49   tile_clip_copy_mono,
50   tile_clip_copy_color,
51   gx_default_draw_line,
52   gx_forward_get_bits,
53   gx_forward_get_params,
54   gx_forward_put_params,
55   gx_forward_map_cmyk_color,
56   gx_forward_get_xfont_procs,
57   gx_forward_get_xfont_device,
58   gx_forward_map_rgb_alpha_color,
59   gx_forward_get_page_device,
60   gx_forward_get_alpha_bits,
61   tile_clip_copy_alpha,
62   gx_forward_get_band,
63   gx_default_copy_rop,
64   gx_default_fill_path,
65   gx_default_stroke_path,
66   gx_default_fill_mask,
67   gx_default_fill_trapezoid,
68   gx_default_fill_parallelogram,
69   gx_default_fill_triangle,
70   gx_default_draw_thin_line,
71   gx_default_begin_image,
72   gx_default_image_data,
73   gx_default_end_image,
74   gx_default_strip_tile_rectangle,
75   tile_clip_strip_copy_rop,
76   gx_forward_get_clipping_box,
77   gx_default_begin_typed_image,
78   gx_forward_get_bits_rectangle,
79   gx_forward_map_color_rgb_alpha,
80   gx_no_create_compositor,
81   gx_forward_get_hardware_params,
82   gx_default_text_begin,
83   gx_default_finish_copydevice,
84   NULL,			/* begin_transparency_group */
85   NULL,			/* end_transparency_group */
86   NULL,			/* begin_transparency_mask */
87   NULL,			/* end_transparency_mask */
88   NULL,			/* discard_transparency_layer */
89   gx_forward_get_color_mapping_procs,
90   gx_forward_get_color_comp_index,
91   gx_forward_encode_color,
92   gx_forward_decode_color,
93   gx_forward_pattern_manage,
94   gx_forward_fill_rectangle_hl_color,
95   gx_forward_include_color_space,
96   gx_forward_fill_linear_color_scanline,
97   gx_forward_fill_linear_color_trapezoid,
98   gx_forward_fill_linear_color_triangle,
99   gx_forward_update_spot_equivalent_colors
100  }
101 };
102 
103 /* Initialize a tile clipping device from a mask. */
104 int
tile_clip_initialize(gx_device_tile_clip * cdev,const gx_strip_bitmap * tiles,gx_device * tdev,int px,int py,gs_memory_t * mem)105 tile_clip_initialize(gx_device_tile_clip * cdev, const gx_strip_bitmap * tiles,
106 		     gx_device * tdev, int px, int py, gs_memory_t *mem)
107 {
108     int code =
109     gx_mask_clip_initialize(cdev, &gs_tile_clip_device,
110 			    (const gx_bitmap *)tiles,
111 			    tdev, 0, 0, mem);	/* phase will be reset */
112 
113     if (code >= 0) {
114 	cdev->tiles = *tiles;
115 	tile_clip_set_phase(cdev, px, py);
116     }
117     return code;
118 }
119 
120 /* Set the phase of the tile. */
121 void
tile_clip_set_phase(gx_device_tile_clip * cdev,int px,int py)122 tile_clip_set_phase(gx_device_tile_clip * cdev, int px, int py)
123 {
124     cdev->phase.x = px;
125     cdev->phase.y = py;
126 }
127 
128 /* Fill a rectangle by tiling with the mask. */
129 private int
tile_clip_fill_rectangle(gx_device * dev,int x,int y,int w,int h,gx_color_index color)130 tile_clip_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
131 			 gx_color_index color)
132 {
133     gx_device_tile_clip *cdev = (gx_device_tile_clip *) dev;
134     gx_device *tdev = cdev->target;
135 
136     return (*dev_proc(tdev, strip_tile_rectangle)) (tdev, &cdev->tiles,
137 						    x, y, w, h,
138 		    gx_no_color_index, color, cdev->phase.x, cdev->phase.y);
139 }
140 
141 /* Calculate the X offset corresponding to a given Y, taking the phase */
142 /* and shift into account. */
143 #define x_offset(ty, cdev)\
144   ((cdev)->phase.x + (((ty) + (cdev)->phase.y) / (cdev)->tiles.rep_height) *\
145    (cdev)->tiles.rep_shift)
146 
147 /* Copy a monochrome bitmap.  We divide it up into maximal chunks */
148 /* that line up with a single tile, and then do the obvious Boolean */
149 /* combination of the tile mask and the source. */
150 private int
tile_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)151 tile_clip_copy_mono(gx_device * dev,
152 		const byte * data, int sourcex, int raster, gx_bitmap_id id,
153 		    int x, int y, int w, int h,
154 		    gx_color_index color0, gx_color_index color1)
155 {
156     gx_device_tile_clip *cdev = (gx_device_tile_clip *) dev;
157     gx_color_index color, mcolor0, mcolor1;
158     int ty, ny;
159     int code;
160 
161     setup_mask_copy_mono(cdev, color, mcolor0, mcolor1);
162     for (ty = y; ty < y + h; ty += ny) {
163 	int tx, nx;
164 	int cy = (ty + cdev->phase.y) % cdev->tiles.rep_height;
165 	int xoff = x_offset(ty, cdev);
166 
167 	ny = min(y + h - ty, cdev->tiles.size.y - cy);
168 	if (ny > cdev->mdev.height)
169 	    ny = cdev->mdev.height;
170 	for (tx = x; tx < x + w; tx += nx) {
171 	    int cx = (tx + xoff) % cdev->tiles.rep_width;
172 
173 	    nx = min(x + w - tx, cdev->tiles.size.x - cx);
174 	    /* Copy a tile slice to the memory device buffer. */
175 	    memcpy(cdev->buffer.bytes,
176 		   cdev->tiles.data + cy * cdev->tiles.raster,
177 		   cdev->tiles.raster * ny);
178 	    /* Intersect the tile with the source data. */
179 	    /* mcolor0 and mcolor1 invert the data if needed. */
180 	    /* This call can't fail. */
181 	    (*dev_proc(&cdev->mdev, copy_mono)) ((gx_device *) & cdev->mdev,
182 				 data + (ty - y) * raster, sourcex + tx - x,
183 						 raster, gx_no_bitmap_id,
184 					   cx, 0, nx, ny, mcolor0, mcolor1);
185 	    /* Now copy the color through the double mask. */
186 	    code = (*dev_proc(cdev->target, copy_mono)) (cdev->target,
187 				 cdev->buffer.bytes, cx, cdev->tiles.raster,
188 							 gx_no_bitmap_id,
189 				  tx, ty, nx, ny, gx_no_color_index, color);
190 	    if (code < 0)
191 		return code;
192 	}
193     }
194     return 0;
195 }
196 
197 /*
198  * Define the skeleton for the other copying operations.  We can't use the
199  * BitBlt tricks: we have to scan for runs of 1s.  There are many obvious
200  * ways to speed this up; we'll implement some if we need to.  The schema
201  * is:
202  *      FOR_RUNS(data_row, tx1, tx, ty) {
203  *        ... process the run ([tx1,tx),ty) ...
204  *      } END_FOR_RUNS();
205  * Free variables: cdev, data, sourcex, raster, x, y, w, h.
206  */
207 #define t_next(tx)\
208   BEGIN {\
209     if ( ++cx == cdev->tiles.size.x )\
210       cx = 0, tp = tile_row, tbit = 0x80;\
211     else if ( (tbit >>= 1) == 0 )\
212       tp++, tbit = 0x80;\
213     tx++;\
214   } END
215 #define FOR_RUNS(data_row, tx1, tx, ty)\
216 	const byte *data_row = data;\
217 	int cy = (y + cdev->phase.y) % cdev->tiles.rep_height;\
218 	const byte *tile_row = cdev->tiles.data + cy * cdev->tiles.raster;\
219 	int ty;\
220 \
221 	for ( ty = y; ty < y + h; ty++, data_row += raster ) {\
222 	  int cx = (x + x_offset(ty, cdev)) % cdev->tiles.rep_width;\
223 	  const byte *tp = tile_row + (cx >> 3);\
224 	  byte tbit = 0x80 >> (cx & 7);\
225 	  int tx;\
226 \
227 	  for ( tx = x; tx < x + w; ) {\
228 	    int tx1;\
229 \
230 	    /* Skip a run of 0s. */\
231 	    while ( tx < x + w && (*tp & tbit) == 0 )\
232 	      t_next(tx);\
233 	    if ( tx == x + w )\
234 	      break;\
235 	    /* Scan a run of 1s. */\
236 	    tx1 = tx;\
237 	    do {\
238 	      t_next(tx);\
239 	    } while ( tx < x + w && (*tp & tbit) != 0 );\
240 	    if_debug3('T', "[T]run x=(%d,%d), y=%d\n", tx1, tx, ty);
241 /* (body goes here) */
242 #define END_FOR_RUNS()\
243 	  }\
244 	  if ( ++cy == cdev->tiles.size.y )\
245 	    cy = 0, tile_row = cdev->tiles.data;\
246 	  else\
247 	    tile_row += cdev->tiles.raster;\
248 	}
249 
250 /* Copy a color rectangle. */
251 private int
tile_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)252 tile_clip_copy_color(gx_device * dev,
253 		const byte * data, int sourcex, int raster, gx_bitmap_id id,
254 		     int x, int y, int w, int h)
255 {
256     gx_device_tile_clip *cdev = (gx_device_tile_clip *) dev;
257 
258     FOR_RUNS(data_row, txrun, tx, ty) {
259 	/* Copy the run. */
260 	int code = (*dev_proc(cdev->target, copy_color))
261 	(cdev->target, data_row, sourcex + txrun - x, raster,
262 	 gx_no_bitmap_id, txrun, ty, tx - txrun, 1);
263 
264 	if (code < 0)
265 	    return code;
266     }
267     END_FOR_RUNS();
268     return 0;
269 }
270 
271 /* Copy an alpha rectangle similarly. */
272 private int
tile_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)273 tile_clip_copy_alpha(gx_device * dev,
274 		const byte * data, int sourcex, int raster, gx_bitmap_id id,
275 		int x, int y, int w, int h, gx_color_index color, int depth)
276 {
277     gx_device_tile_clip *cdev = (gx_device_tile_clip *) dev;
278 
279     FOR_RUNS(data_row, txrun, tx, ty) {
280 	/* Copy the run. */
281 	int code = (*dev_proc(cdev->target, copy_alpha))
282 	(cdev->target, data_row, sourcex + txrun - x, raster,
283 	 gx_no_bitmap_id, txrun, ty, tx - txrun, 1, color, depth);
284 
285 	if (code < 0)
286 	    return code;
287     }
288     END_FOR_RUNS();
289     return 0;
290 }
291 
292 /* Copy a RasterOp rectangle similarly. */
293 private int
tile_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)294 tile_clip_strip_copy_rop(gx_device * dev,
295 	       const byte * data, int sourcex, uint raster, gx_bitmap_id id,
296 			 const gx_color_index * scolors,
297 	   const gx_strip_bitmap * textures, const gx_color_index * tcolors,
298 			 int x, int y, int w, int h,
299 		       int phase_x, int phase_y, gs_logical_operation_t lop)
300 {
301     gx_device_tile_clip *cdev = (gx_device_tile_clip *) dev;
302 
303     FOR_RUNS(data_row, txrun, tx, ty) {
304 	/* Copy the run. */
305 	int code = (*dev_proc(cdev->target, strip_copy_rop))
306 	(cdev->target, data_row, sourcex + txrun - x, raster,
307 	 gx_no_bitmap_id, scolors, textures, tcolors,
308 	 txrun, ty, tx - txrun, 1, phase_x, phase_y, lop);
309 
310 	if (code < 0)
311 	    return code;
312     }
313     END_FOR_RUNS();
314     return 0;
315 }
316