xref: /plan9/sys/src/cmd/gs/src/gxclrect.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1997, 1998, 1999 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: gxclrect.c,v 1.7 2004/08/04 19:36:12 stefan Exp $ */
18 /* Rectangle-oriented command writing for command list */
19 #include "gx.h"
20 #include "gserrors.h"
21 #include "gsutil.h"		/* for gs_next_ids */
22 #include "gxdevice.h"
23 #include "gxdevmem.h"		/* must precede gxcldev.h */
24 #include "gxcldev.h"
25 
26 /* ---------------- Writing utilities ---------------- */
27 
28 #define cmd_set_rect(rect)\
29   ((rect).x = x, (rect).y = y,\
30    (rect).width = width, (rect).height = height)
31 
32 /* Write a rectangle. */
33 private int
cmd_size_rect(register const gx_cmd_rect * prect)34 cmd_size_rect(register const gx_cmd_rect * prect)
35 {
36     return
37 	cmd_sizew(prect->x) + cmd_sizew(prect->y) +
38 	cmd_sizew(prect->width) + cmd_sizew(prect->height);
39 }
40 private byte *
cmd_put_rect(register const gx_cmd_rect * prect,register byte * dp)41 cmd_put_rect(register const gx_cmd_rect * prect, register byte * dp)
42 {
43     cmd_putw(prect->x, dp);
44     cmd_putw(prect->y, dp);
45     cmd_putw(prect->width, dp);
46     cmd_putw(prect->height, dp);
47     return dp;
48 }
49 
50 int
cmd_write_rect_cmd(gx_device_clist_writer * cldev,gx_clist_state * pcls,int op,int x,int y,int width,int height)51 cmd_write_rect_cmd(gx_device_clist_writer * cldev, gx_clist_state * pcls,
52 		   int op, int x, int y, int width, int height)
53 {
54     int dx = x - pcls->rect.x;
55     int dy = y - pcls->rect.y;
56     int dwidth = width - pcls->rect.width;
57     int dheight = height - pcls->rect.height;
58     byte *dp;
59     int code;
60 
61 #define check_range_xy(rmin, rmax)\
62   ((unsigned)(dx - rmin) <= (rmax - rmin) &&\
63    (unsigned)(dy - rmin) <= (rmax - rmin))
64 #define check_range_w(rmin, rmax)\
65   ((unsigned)(dwidth - rmin) <= (rmax - rmin))
66 #define check_ranges(rmin, rmax)\
67   (check_range_xy(rmin, rmax) && check_range_w(rmin, rmax) &&\
68    (unsigned)(dheight - rmin) <= (rmax - rmin))
69     cmd_set_rect(pcls->rect);
70     if (dheight == 0 && check_range_w(cmd_min_dw_tiny, cmd_max_dw_tiny) &&
71 	check_range_xy(cmd_min_dxy_tiny, cmd_max_dxy_tiny)
72 	) {
73 	byte op_tiny = op + 0x20 + dwidth - cmd_min_dw_tiny;
74 
75 	if (dx == width - dwidth && dy == 0) {
76 	    code = set_cmd_put_op(dp, cldev, pcls, op_tiny + 8, 1);
77 	    if (code < 0)
78 		return code;
79 	} else {
80 	    code = set_cmd_put_op(dp, cldev, pcls, op_tiny, 2);
81 	    if (code < 0)
82 		return code;
83 	    dp[1] = (dx << 4) + dy - (cmd_min_dxy_tiny * 0x11);
84 	}
85     }
86 #define rmin cmd_min_short
87 #define rmax cmd_max_short
88     else if (check_ranges(rmin, rmax)) {
89 	int dh = dheight - cmd_min_dxy_tiny;
90 
91 	if ((unsigned)dh <= cmd_max_dxy_tiny - cmd_min_dxy_tiny &&
92 	    dh != 0 && dy == 0
93 	    ) {
94 	    op += dh;
95 	    code = set_cmd_put_op(dp, cldev, pcls, op + 0x10, 3);
96 	    if (code < 0)
97 		return code;
98 	    if_debug3('L', "    rs2:%d,%d,0,%d\n",
99 		      dx, dwidth, dheight);
100 	} else {
101 	    code = set_cmd_put_op(dp, cldev, pcls, op + 0x10, 5);
102 	    if (code < 0)
103 		return code;
104 	    if_debug4('L', "    rs4:%d,%d,%d,%d\n",
105 		      dx, dwidth, dy, dheight);
106 	    dp[3] = dy - rmin;
107 	    dp[4] = dheight - rmin;
108 	}
109 	dp[1] = dx - rmin;
110 	dp[2] = dwidth - rmin;
111     }
112 #undef rmin
113 #undef rmax
114     else if (dy >= -2 && dy <= 1 && dheight >= -2 && dheight <= 1 &&
115 	     (dy + dheight) != -4
116 	) {
117 	int rcsize = 1 + cmd_sizew(x) + cmd_sizew(width);
118 
119 	code = set_cmd_put_op(dp, cldev, pcls,
120 			      op + ((dy + 2) << 2) + dheight + 2, rcsize);
121 	if (code < 0)
122 	    return code;
123 	++dp;
124 	cmd_put2w(x, width, dp);
125     } else {
126 	int rcsize = 1 + cmd_size_rect(&pcls->rect);
127 
128 	code = set_cmd_put_op(dp, cldev, pcls, op, rcsize);
129 	if (code < 0)
130 	    return code;
131 	if_debug5('L', "    r%d:%d,%d,%d,%d\n",
132 		  rcsize - 1, dx, dwidth, dy, dheight);
133 	cmd_put_rect(&pcls->rect, dp + 1);
134     }
135     return 0;
136 }
137 
138 /* ---------------- Driver procedures ---------------- */
139 
140 int
clist_fill_rectangle(gx_device * dev,int x,int y,int width,int height,gx_color_index color)141 clist_fill_rectangle(gx_device * dev, int x, int y, int width, int height,
142 		     gx_color_index color)
143 {
144     gx_device_clist_writer * const cdev =
145 	&((gx_device_clist *)dev)->writer;
146     int code;
147 
148     fit_fill(dev, x, y, width, height);
149     FOR_RECTS {
150 	pcls->colors_used.or |= color;
151 	TRY_RECT {
152 	    code = cmd_disable_lop(cdev, pcls);
153 	    if (code >= 0 && color != pcls->colors[1])
154 		code = cmd_put_color(cdev, pcls, &clist_select_color1,
155 				     color, &pcls->colors[1]);
156 	    if (code >= 0)
157 		code = cmd_write_rect_cmd(cdev, pcls, cmd_op_fill_rect, x, y,
158 					  width, height);
159 	} HANDLE_RECT(code);
160     } END_RECTS;
161     return 0;
162 }
163 
164 int
clist_strip_tile_rectangle(gx_device * dev,const gx_strip_bitmap * tile,int x,int y,int width,int height,gx_color_index color0,gx_color_index color1,int px,int py)165 clist_strip_tile_rectangle(gx_device * dev, const gx_strip_bitmap * tile,
166 			   int x, int y, int width, int height,
167 	       gx_color_index color0, gx_color_index color1, int px, int py)
168 {
169     gx_device_clist_writer * const cdev =
170 	&((gx_device_clist *)dev)->writer;
171     int depth =
172 	(color1 == gx_no_color_index && color0 == gx_no_color_index ?
173 	 dev->color_info.depth : 1);
174     gx_color_index colors_used =
175 	(color1 == gx_no_color_index && color0 == gx_no_color_index ?
176 	 /* We can't know what colors will be used: assume the worst. */
177 	 ((gx_color_index)1 << depth) - 1 :
178 	 (color0 == gx_no_color_index ? 0 : color0) |
179 	 (color1 == gx_no_color_index ? 0 : color1));
180     int code;
181 
182     fit_fill(dev, x, y, width, height);
183     FOR_RECTS {
184 	ulong offset_temp;
185 
186 	pcls->colors_used.or |= colors_used;
187 	TRY_RECT {
188 	    code = cmd_disable_lop(cdev, pcls);
189 	} HANDLE_RECT(code);
190 	if (!cls_has_tile_id(cdev, pcls, tile->id, offset_temp)) {
191 	    code = 0;
192 	    if (tile->id != gx_no_bitmap_id) {
193 		TRY_RECT {
194 		    code = clist_change_tile(cdev, pcls, tile, depth);
195 		} HANDLE_RECT_UNLESS(code,
196 		    (code != gs_error_VMerror || !cdev->error_is_retryable));
197 	    }
198 	    if (code < 0) {
199 		/* ok if gx_default... does retries internally: */
200 		/* it's self-sufficient */
201 		code = gx_default_strip_tile_rectangle(dev, tile,
202 						       x, y, width, height,
203 						       color0, color1,
204 						       px, py);
205 		if (code < 0)
206 		    ERROR_RECT(code);
207 		goto endr;
208 	    }
209 	}
210 	TRY_RECT {
211 	    code = 0;
212 	    if (color0 != pcls->tile_colors[0] || color1 != pcls->tile_colors[1])
213 		code = cmd_set_tile_colors(cdev, pcls, color0, color1);
214 	    if (px != pcls->tile_phase.x || py != pcls->tile_phase.y) {
215 		if (code >= 0)
216 		    code = cmd_set_tile_phase(cdev, pcls, px, py);
217 	    }
218 	    if (code >= 0)
219 		code = cmd_write_rect_cmd(cdev, pcls, cmd_op_tile_rect, x, y,
220 					  width, height);
221 	} HANDLE_RECT(code);
222 endr:;
223     } END_RECTS;
224     return 0;
225 }
226 
227 int
clist_copy_mono(gx_device * dev,const byte * data,int data_x,int raster,gx_bitmap_id id,int x,int y,int width,int height,gx_color_index color0,gx_color_index color1)228 clist_copy_mono(gx_device * dev,
229 		const byte * data, int data_x, int raster, gx_bitmap_id id,
230 		int x, int y, int width, int height,
231 		gx_color_index color0, gx_color_index color1)
232 {
233     gx_device_clist_writer * const cdev =
234 	&((gx_device_clist *)dev)->writer;
235     int y0;
236     gx_bitmap_id orig_id = id;
237     gx_color_index colors_used =
238 	(color0 == gx_no_color_index ? 0 : color0) |
239 	(color1 == gx_no_color_index ? 0 : color1);
240 
241     fit_copy(dev, data, data_x, raster, id, x, y, width, height);
242     y0 = y;
243     FOR_RECTS {
244 	int dx = data_x & 7;
245 	int w1 = dx + width;
246 	const byte *row = data + (y - y0) * raster + (data_x >> 3);
247 	int code;
248 
249 	pcls->colors_used.or |= colors_used;
250 	TRY_RECT {
251 	    code = cmd_disable_lop(cdev, pcls);
252 	    if (code >= 0)
253 		code = cmd_disable_clip(cdev, pcls);
254 	    if (color0 != pcls->colors[0] && code >= 0)
255 		code = cmd_set_color0(cdev, pcls, color0);
256 	    if (color1 != pcls->colors[1] && code >= 0)
257 		code = cmd_set_color1(cdev, pcls, color1);
258 	} HANDLE_RECT(code);
259 	/* Don't bother to check for a possible cache hit: */
260 	/* tile_rectangle and fill_mask handle those cases. */
261 copy:{
262 	gx_cmd_rect rect;
263 	int rsize;
264 	byte op = (byte) cmd_op_copy_mono;
265 	byte *dp;
266 	uint csize;
267 	uint compress;
268 	int code;
269 
270 	rect.x = x, rect.y = y;
271 	rect.width = w1, rect.height = height;
272 	rsize = (dx ? 3 : 1) + cmd_size_rect(&rect);
273 	TRY_RECT {
274 	    code = cmd_put_bits(cdev, pcls, row, w1, height, raster,
275 				rsize, (orig_id == gx_no_bitmap_id ?
276 					1 << cmd_compress_rle :
277 					cmd_mask_compress_any),
278 				&dp, &csize);
279 	} HANDLE_RECT_UNLESS(code, code == gs_error_limitcheck);
280 	compress = (uint)code;
281 	if (code < 0) {
282 	    /* The bitmap was too large; split up the transfer. */
283 	    if (height > 1) {
284 		/*
285 		 * Split the transfer by reducing the height.
286 		 * See the comment above FOR_RECTS in gxcldev.h.
287 		 */
288 		height >>= 1;
289 		goto copy;
290 	    } else {
291 		/* Split a single (very long) row. */
292 		int w2 = w1 >> 1;
293 
294 		NEST_RECT {
295 		    code = clist_copy_mono(dev, row, dx,
296 					   raster, gx_no_bitmap_id, x, y,
297 					   w2, 1, color0, color1);
298 		    if (code >= 0)
299 			code = clist_copy_mono(dev, row, dx + w2,
300 					       raster, gx_no_bitmap_id,
301 					       x + w2, y,
302 					       w1 - w2, 1, color0, color1);
303 		} UNNEST_RECT;
304 		if (code < 0)
305 		    ERROR_RECT(code);
306 		continue;
307 	    }
308 	}
309 	op += compress;
310 	if (dx) {
311 	    *dp++ = cmd_count_op(cmd_opv_set_misc, 2);
312 	    *dp++ = cmd_set_misc_data_x + dx;
313 	}
314 	*dp++ = cmd_count_op(op, csize);
315 	cmd_put2w(x, y, dp);
316 	cmd_put2w(w1, height, dp);
317 	pcls->rect = rect;
318 	}
319     } END_RECTS;
320     return 0;
321 }
322 
323 int
clist_copy_color(gx_device * dev,const byte * data,int data_x,int raster,gx_bitmap_id id,int x,int y,int width,int height)324 clist_copy_color(gx_device * dev,
325 		 const byte * data, int data_x, int raster, gx_bitmap_id id,
326 		 int x, int y, int width, int height)
327 {
328     gx_device_clist_writer * const cdev =
329 	&((gx_device_clist *)dev)->writer;
330     int depth = dev->color_info.depth;
331     int y0;
332     int data_x_bit;
333     /* We can't know what colors will be used: assume the worst. */
334     gx_color_index colors_used = ((gx_color_index)1 << depth) - 1;
335 
336     fit_copy(dev, data, data_x, raster, id, x, y, width, height);
337     y0 = y;
338     data_x_bit = data_x * depth;
339     FOR_RECTS {
340 	int dx = (data_x_bit & 7) / depth;
341 	int w1 = dx + width;
342 	const byte *row = data + (y - y0) * raster + (data_x_bit >> 3);
343 	int code;
344 
345 	pcls->colors_used.or |= colors_used;
346 	TRY_RECT {
347 	    code = cmd_disable_lop(cdev, pcls);
348 	    if (code >= 0)
349 		code = cmd_disable_clip(cdev, pcls);
350 	} HANDLE_RECT(code);
351 	if (pcls->color_is_alpha) {
352 	    byte *dp;
353 
354 	    TRY_RECT {
355 		code =
356 		    set_cmd_put_op(dp, cdev, pcls, cmd_opv_set_copy_color, 1);
357 	    } HANDLE_RECT(code);
358 	    pcls->color_is_alpha = 0;
359 	}
360 copy:{
361 	    gx_cmd_rect rect;
362 	    int rsize;
363 	    byte op = (byte) cmd_op_copy_color_alpha;
364 	    byte *dp;
365 	    uint csize;
366 	    uint compress;
367 
368 	    rect.x = x, rect.y = y;
369 	    rect.width = w1, rect.height = height;
370 	    rsize = (dx ? 3 : 1) + cmd_size_rect(&rect);
371 	    TRY_RECT {
372 		code = cmd_put_bits(cdev, pcls, row, w1 * depth,
373 				    height, raster, rsize,
374 				    1 << cmd_compress_rle, &dp, &csize);
375 	    } HANDLE_RECT_UNLESS(code, code == gs_error_limitcheck);
376 	    compress = (uint)code;
377 	    if (code < 0) {
378 		/* The bitmap was too large; split up the transfer. */
379 		if (height > 1) {
380 		    /* Split the transfer by reducing the height.
381 		     * See the comment above FOR_RECTS in gxcldev.h.
382 		     */
383 		    height >>= 1;
384 		    goto copy;
385 		} else {
386 		    /* Split a single (very long) row. */
387 		    int w2 = w1 >> 1;
388 
389 		    NEST_RECT {
390 			code = clist_copy_color(dev, row, dx,
391 						raster, gx_no_bitmap_id,
392 						x, y, w2, 1);
393 			if (code >= 0)
394 			    code = clist_copy_color(dev, row, dx + w2,
395 						    raster, gx_no_bitmap_id,
396 						    x + w2, y, w1 - w2, 1);
397 		    } UNNEST_RECT;
398 		    if (code < 0)
399 			ERROR_RECT(code);
400 		    continue;
401 		}
402 	    }
403 	    op += compress;
404 	    if (dx) {
405 		*dp++ = cmd_count_op(cmd_opv_set_misc, 2);
406 		*dp++ = cmd_set_misc_data_x + dx;
407 	    }
408 	    *dp++ = cmd_count_op(op, csize);
409 	    cmd_put2w(x, y, dp);
410 	    cmd_put2w(w1, height, dp);
411 	    pcls->rect = rect;
412 	}
413     } END_RECTS;
414     return 0;
415 }
416 
417 int
clist_copy_alpha(gx_device * dev,const byte * data,int data_x,int raster,gx_bitmap_id id,int x,int y,int width,int height,gx_color_index color,int depth)418 clist_copy_alpha(gx_device * dev, const byte * data, int data_x,
419 	   int raster, gx_bitmap_id id, int x, int y, int width, int height,
420 		 gx_color_index color, int depth)
421 {
422     gx_device_clist_writer * const cdev =
423 	&((gx_device_clist *)dev)->writer;
424     /* I don't like copying the entire body of clist_copy_color */
425     /* just to change 2 arguments and 1 opcode, */
426     /* but I don't see any alternative that doesn't require */
427     /* another level of procedure call even in the common case. */
428     int log2_depth = ilog2(depth);
429     int y0;
430     int data_x_bit;
431 
432     /* If the target can't perform copy_alpha, exit now */
433     if (depth > 1 && (cdev->disable_mask & clist_disable_copy_alpha) != 0)
434 	return_error(gs_error_unknownerror);
435 
436     fit_copy(dev, data, data_x, raster, id, x, y, width, height);
437     y0 = y;
438     data_x_bit = data_x << log2_depth;
439     FOR_RECTS {
440 	int dx = (data_x_bit & 7) >> log2_depth;
441 	int w1 = dx + width;
442 	const byte *row = data + (y - y0) * raster + (data_x_bit >> 3);
443 	int code;
444 
445 	pcls->colors_used.or |= color;
446 	TRY_RECT {
447 	    code = cmd_disable_lop(cdev, pcls);
448 	    if (code >= 0)
449 		code = cmd_disable_clip(cdev, pcls);
450 	} HANDLE_RECT(code);
451 	if (!pcls->color_is_alpha) {
452 	    byte *dp;
453 
454 	    TRY_RECT {
455 		code =
456 		    set_cmd_put_op(dp, cdev, pcls, cmd_opv_set_copy_alpha, 1);
457 	    } HANDLE_RECT(code);
458 	    pcls->color_is_alpha = 1;
459 	}
460 	if (color != pcls->colors[1]) {
461 	    TRY_RECT {
462 		code = cmd_set_color1(cdev, pcls, color);
463 	    } HANDLE_RECT(code);
464 	}
465 copy:{
466 	    gx_cmd_rect rect;
467 	    int rsize;
468 	    byte op = (byte) cmd_op_copy_color_alpha;
469 	    byte *dp;
470 	    uint csize;
471 	    uint compress;
472 
473 	    rect.x = x, rect.y = y;
474 	    rect.width = w1, rect.height = height;
475 	    rsize = (dx ? 4 : 2) + cmd_size_rect(&rect);
476 	    TRY_RECT {
477 		code = cmd_put_bits(cdev, pcls, row, w1 << log2_depth,
478 				    height, raster, rsize,
479 				    1 << cmd_compress_rle, &dp, &csize);
480 	    } HANDLE_RECT_UNLESS(code, code == gs_error_limitcheck);
481 	    compress = (uint)code;
482 	    if (code < 0) {
483 		/* The bitmap was too large; split up the transfer. */
484 		if (height > 1) {
485 		    /* Split the transfer by reducing the height.
486 		     * See the comment above FOR_RECTS in gxcldev.h.
487 		     */
488 		    height >>= 1;
489 		    goto copy;
490 		} else {
491 		    /* Split a single (very long) row. */
492 		    int w2 = w1 >> 1;
493 
494 		    NEST_RECT {
495 			code = clist_copy_alpha(dev, row, dx,
496 						raster, gx_no_bitmap_id, x, y,
497 						w2, 1, color, depth);
498 			if (code >= 0)
499 			    code = clist_copy_alpha(dev, row, dx + w2,
500 						    raster, gx_no_bitmap_id,
501 						    x + w2, y, w1 - w2, 1,
502 						    color, depth);
503 		    } UNNEST_RECT;
504 		    if (code < 0)
505 			ERROR_RECT(code);
506 		    continue;
507 		}
508 	    }
509 	    op += compress;
510 	    if (dx) {
511 		*dp++ = cmd_count_op(cmd_opv_set_misc, 2);
512 		*dp++ = cmd_set_misc_data_x + dx;
513 	    }
514 	    *dp++ = cmd_count_op(op, csize);
515 	    *dp++ = depth;
516 	    cmd_put2w(x, y, dp);
517 	    cmd_put2w(w1, height, dp);
518 	    pcls->rect = rect;
519 	}
520     } END_RECTS;
521     return 0;
522 }
523 
524 int
clist_strip_copy_rop(gx_device * dev,const byte * sdata,int sourcex,uint sraster,gx_bitmap_id id,const gx_color_index * scolors,const gx_strip_bitmap * textures,const gx_color_index * tcolors,int x,int y,int width,int height,int phase_x,int phase_y,gs_logical_operation_t lop)525 clist_strip_copy_rop(gx_device * dev,
526 	     const byte * sdata, int sourcex, uint sraster, gx_bitmap_id id,
527 		     const gx_color_index * scolors,
528 	   const gx_strip_bitmap * textures, const gx_color_index * tcolors,
529 		     int x, int y, int width, int height,
530 		     int phase_x, int phase_y, gs_logical_operation_t lop)
531 {
532     gx_device_clist_writer * const cdev =
533 	&((gx_device_clist *)dev)->writer;
534     gs_rop3_t rop = lop_rop(lop);
535     gx_strip_bitmap tile_with_id;
536     const gx_strip_bitmap *tiles = textures;
537     int y0;
538     /* Compute the set of possible colors that this operation can generate. */
539     gx_color_index all = ((gx_color_index)1 << dev->color_info.depth) - 1;
540     bool subtractive = dev->color_info.num_components == 4; /****** HACK ******/
541     gx_color_index S =
542 	(scolors ? scolors[0] | scolors[1] : sdata ? all : 0);
543     gx_color_index T =
544 	(tcolors ? tcolors[0] | tcolors[1] : textures ? all : 0);
545     gs_rop3_t color_rop =
546 	(subtractive ? byte_reverse_bits[rop ^ 0xff] : rop);
547     bool slow_rop;
548 
549     if (scolors != 0 && scolors[0] != scolors[1]) {
550 	fit_fill(dev, x, y, width, height);
551     } else {
552 	fit_copy(dev, sdata, sourcex, sraster, id, x, y, width, height);
553     }
554     /*
555      * On CMYK devices, RasterOps must be executed with complete pixels
556      * if the operation involves the destination.
557      * This is because the black plane interacts with the other planes
558      * in the conversion between RGB and CMYK.  Check for this now.
559      */
560     {
561 	gs_rop3_t rop_used = rop;
562 
563 	if (scolors && (scolors[0] == scolors[1]))
564 	    rop_used = (scolors[0] == gx_device_black(dev) ?
565 			rop3_know_S_0(rop_used) :
566 			scolors[0] == gx_device_white(dev) ?
567 			rop3_know_S_1(rop_used) : rop_used);
568 	if (tcolors && (tcolors[0] == tcolors[1]))
569 	    rop_used = (tcolors[0] == gx_device_black(dev) ?
570 			rop3_know_T_0(rop_used) :
571 			tcolors[0] == gx_device_white(dev) ?
572 			rop3_know_T_1(rop_used) : rop_used);
573 	slow_rop = !(rop == rop3_0 || rop == rop3_1 ||
574 		     rop == rop3_D || rop == rop3_S || rop == rop3_T);
575     }
576     y0 = y;
577     /*
578      * We shouldn't need to put the logic below inside FOR/END_RECTS,
579      * but the lop_enabled flags are per-band.
580      */
581     FOR_RECTS {
582 	const byte *row = sdata + (y - y0) * sraster;
583 	gx_color_index D = pcls->colors_used.or;
584 	int code;
585 
586 	/* Reducing D, S, T to rop_operand (which apparently is 32 bit) appears safe
587 	   due to 'all' a has smaller snumber of significant bits. */
588 	pcls->colors_used.or =
589 	    ((rop_proc_table[color_rop])((rop_operand)D, (rop_operand)S, (rop_operand)T) & all) | D;
590 	pcls->colors_used.slow_rop |= slow_rop;
591 	if (rop3_uses_T(rop)) {
592 	    if (tcolors == 0 || tcolors[0] != tcolors[1]) {
593 		ulong offset_temp;
594 
595 		if (!cls_has_tile_id(cdev, pcls, tiles->id, offset_temp)) {
596 		    /* Change tile.  If there is no id, generate one. */
597 		    if (tiles->id == gx_no_bitmap_id) {
598 			tile_with_id = *tiles;
599 			tile_with_id.id = gs_next_ids(dev->memory, 1);
600 			tiles = &tile_with_id;
601 		    }
602 		    TRY_RECT {
603 			code = clist_change_tile(cdev, pcls, tiles,
604 						 (tcolors != 0 ? 1 :
605 						  dev->color_info.depth));
606 		    } HANDLE_RECT_UNLESS(code, code == gs_error_limitcheck);
607 		    if (code < 0) {
608 			/*
609 			 * The error is a limitcheck: we have a tile that
610 			 * is too big to fit in the command reading buffer.
611 			 * For now, just divide up the transfer into scan
612 			 * lines.  (If a single scan line won't fit, punt.)
613 			 * Eventually, we'll need a way to transfer the tile
614 			 * in pieces.
615 			 */
616 			uint rep_height = tiles->rep_height;
617 			gs_id ids;
618 			gx_strip_bitmap line_tile;
619 			int iy;
620 
621 			if (rep_height == 1 ||
622 			    /****** CAN'T HANDLE SHIFT YET ******/
623 			    tiles->rep_shift != 0
624 			    )
625 			    return code;
626 			/*
627 			 * Allocate enough fake IDs, since the inner call on
628 			 * clist_strip_copy_rop will need them anyway.
629 			 */
630 			ids = gs_next_ids(dev->memory, min(height, rep_height));
631 			line_tile = *tiles;
632 			line_tile.size.y = 1;
633 			line_tile.rep_height = 1;
634 			for (iy = 0; iy < height; ++iy) {
635 			    line_tile.data = tiles->data + line_tile.raster *
636 				((y + iy + phase_y) % rep_height);
637 			    line_tile.id = ids + (iy % rep_height);
638 			    /*
639 			     * Note that since we're only transferring
640 			     * a single scan line, phase_y is irrelevant;
641 			     * we may as well use the current tile phase
642 			     * so we don't have to write extra commands.
643 			     */
644 			    NEST_RECT {
645 				code = clist_strip_copy_rop(dev,
646 					(sdata == 0 ? 0 : row + iy * sraster),
647 					sourcex, sraster,
648 					gx_no_bitmap_id, scolors,
649 					&line_tile, tcolors,
650 					x, y + iy, width, 1,
651 					phase_x, pcls->tile_phase.y, lop);
652 			    } UNNEST_RECT;
653 			    if (code < 0)
654 				ERROR_RECT(code);
655 			}
656 			continue;
657 		    }
658 		    if (phase_x != pcls->tile_phase.x ||
659 			phase_y != pcls->tile_phase.y
660 			) {
661 			TRY_RECT {
662 			    code = cmd_set_tile_phase(cdev, pcls, phase_x,
663 						      phase_y);
664 			} HANDLE_RECT(code);
665 		    }
666 		}
667 	    }
668 	    /* Set the tile colors. */
669 	    TRY_RECT {
670 		code =
671 		    (tcolors != 0 ?
672 		     cmd_set_tile_colors(cdev, pcls, tcolors[0], tcolors[1]) :
673 		     cmd_set_tile_colors(cdev, pcls, gx_no_color_index,
674 					 gx_no_color_index));
675 	    } HANDLE_RECT(code);
676 	}
677 	TRY_RECT {
678 	    code = 0;
679 	    if (lop != pcls->lop)
680 		code = cmd_set_lop(cdev, pcls, lop);
681 	    if (code >= 0)
682 		code = cmd_enable_lop(cdev, pcls);
683 	} HANDLE_RECT(code);
684 
685 	/* Set lop_enabled to -1 so that fill_rectangle / copy_* */
686 	/* won't attempt to set it to 0. */
687 	pcls->lop_enabled = -1;
688 	NEST_RECT {
689 	    if (scolors != 0) {
690 		if (scolors[0] == scolors[1])
691 		    code = clist_fill_rectangle(dev, x, y, width, height,
692 						scolors[1]);
693 		else
694 		    code = clist_copy_mono(dev, row, sourcex, sraster, id,
695 					   x, y, width, height,
696 					   scolors[0], scolors[1]);
697 	    } else
698 		code = clist_copy_color(dev, row, sourcex, sraster, id,
699 					x, y, width, height);
700 	} UNNEST_RECT;
701 	pcls->lop_enabled = 1;
702 	if (code < 0)
703 	    ERROR_RECT(code);
704     } END_RECTS;
705     return 0;
706 }
707