xref: /plan9/sys/src/cmd/gs/src/gdevplnx.c (revision ff8c3af2f44d95267f67219afa20ba82ff6cf7e4)
1 /* Copyright (C) 1998, 1999 Aladdin Enterprises.  All rights reserved.
2 
3   This file is part of AFPL Ghostscript.
4 
5   AFPL Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author or
6   distributor accepts any responsibility for the consequences of using it, or
7   for whether it serves any particular purpose or works at all, unless he or
8   she says so in writing.  Refer to the Aladdin Free Public License (the
9   "License") for full details.
10 
11   Every copy of AFPL Ghostscript must include a copy of the License, normally
12   in a plain ASCII text file named PUBLIC.  The License grants you the right
13   to copy, modify and redistribute AFPL Ghostscript, but only under certain
14   conditions described in the License.  Among other things, the License
15   requires that the copyright notice and this notice be preserved on all
16   copies.
17 */
18 
19 /*$Id: gdevplnx.c,v 1.2 2000/09/19 19:00:20 lpd Exp $*/
20 /* Plane extraction device */
21 #include "gx.h"
22 #include "gserrors.h"
23 #include "gsbitops.h"
24 #include "gsrop.h"		/* for logical op access */
25 #include "gsstruct.h"
26 #include "gsutil.h"
27 #include "gxdcolor.h"
28 #include "gxcmap.h"		/* requires gxdcolor.h */
29 #include "gxdevice.h"
30 #include "gxdevmem.h"
31 #include "gxdither.h"
32 #include "gxgetbit.h"
33 #include "gxiparam.h"
34 #include "gxistate.h"
35 #include "gdevplnx.h"
36 
37 /* Define the size of the locally allocated bitmap buffers. */
38 #define COPY_COLOR_BUF_SIZE 100
39 #define TILE_RECTANGLE_BUF_SIZE 100
40 #define COPY_ROP_SOURCE_BUF_SIZE 100
41 #define COPY_ROP_TEXTURE_BUF_SIZE 100
42 
43 /* GC procedures */
44 private
45 ENUM_PTRS_WITH(device_plane_extract_enum_ptrs, gx_device_plane_extract *edev)
46     ENUM_PREFIX(st_device_forward, 1);
47 case 0: ENUM_RETURN(gx_device_enum_ptr(edev->target));
48 ENUM_PTRS_END
49 private RELOC_PTRS_WITH(device_plane_extract_reloc_ptrs, gx_device_plane_extract *edev)
50 {
51     RELOC_PREFIX(st_device_forward);
52     edev->plane_dev = gx_device_reloc_ptr(edev->plane_dev, gcst);
53 }
54 RELOC_PTRS_END
55 public_st_device_plane_extract();
56 
57 /* Driver procedures */
58 private dev_proc_open_device(plane_open_device);
59 private dev_proc_fill_rectangle(plane_fill_rectangle);
60 private dev_proc_copy_mono(plane_copy_mono);
61 private dev_proc_copy_color(plane_copy_color);
62 private dev_proc_copy_alpha(plane_copy_alpha);
63 private dev_proc_fill_path(plane_fill_path);
64 private dev_proc_stroke_path(plane_stroke_path);
65 private dev_proc_fill_mask(plane_fill_mask);
66 private dev_proc_fill_parallelogram(plane_fill_parallelogram);
67 private dev_proc_fill_triangle(plane_fill_triangle);
68 private dev_proc_strip_tile_rectangle(plane_strip_tile_rectangle);
69 private dev_proc_strip_copy_rop(plane_strip_copy_rop);
70 private dev_proc_begin_typed_image(plane_begin_typed_image);
71 private dev_proc_get_bits_rectangle(plane_get_bits_rectangle);
72 
73 /* Device prototype */
74 private const gx_device_plane_extract gs_plane_extract_device = {
75     std_device_std_body(gx_device_plane_extract, 0, "plane_extract",
76 			0, 0, 72, 72),
77     {
78 	plane_open_device,
79 	NULL,
80 	NULL,
81 	NULL,
82 	gx_default_close_device,
83 	NULL,
84 	NULL,
85 	plane_fill_rectangle,
86 	gx_default_tile_rectangle,
87 	plane_copy_mono,
88 	plane_copy_color,
89 	gx_default_draw_line,
90 	gx_default_get_bits,
91 	NULL,
92 	NULL,
93 	NULL,
94 	NULL,
95 	NULL,
96 	NULL,
97 	NULL,
98 	NULL,
99 	plane_copy_alpha,
100 	NULL,
101 	gx_default_copy_rop,
102 	plane_fill_path,
103 	plane_stroke_path,
104 	plane_fill_mask,
105 	gx_default_fill_trapezoid,
106 	plane_fill_parallelogram,
107 	plane_fill_triangle,
108 	gx_default_draw_thin_line,
109 	gx_default_begin_image,
110 	gx_default_image_data,
111 	gx_default_end_image,
112 	plane_strip_tile_rectangle,
113 	plane_strip_copy_rop,
114 	NULL,
115 	plane_begin_typed_image,
116 	plane_get_bits_rectangle,
117 	NULL,
118 	gx_no_create_compositor, /* WRONG */
119 	NULL,
120 	gx_default_text_begin
121     },
122     /* device-specific members */
123     NULL,				/* target */
124     NULL,				/* plane_dev */
125     { 0 },				/* plane */
126     0,					/* plane_white */
127     0,					/* plane_mask */
128     0,					/* plane_dev_is_memory */
129     1 /*true*/				/* any_marks */
130 };
131 
132 /* ---------------- Utilities ---------------- */
133 
134 /* Extract the selected plane from a color (gx_color_index). */
135 #define COLOR_PIXEL(edev, color)\
136   ( ((color) >> (edev)->plane.shift) & (edev)->plane_mask )
137 /* Do the same if the color might be transparent. */
138 #define TRANS_COLOR_PIXEL(edev, color)\
139  ((color) == gx_no_color_index ? gx_no_color_index : COLOR_PIXEL(edev, color))
140 
141 /*
142  * Reduce the drawing color to one for the selected plane.
143  * All we care about is whether the drawing operation should be skipped.
144  */
145 typedef enum {
146     REDUCE_SKIP,
147     REDUCE_DRAW,
148     REDUCE_FAILED			/* couldn't reduce */
149 } reduced_color_t;
150 #define REDUCE_PURE(edev, pixel)\
151   ((pixel) == (edev)->plane_white && !(edev)->any_marks ?  REDUCE_SKIP :\
152    ((edev)->any_marks = true, REDUCE_DRAW))
153 private reduced_color_t
154 reduce_drawing_color(gx_device_color *ppdc, gx_device_plane_extract *edev,
155 		     const gx_drawing_color *pdevc,
156 		     gs_logical_operation_t *plop)
157 {
158     reduced_color_t reduced;
159 
160     if (gx_dc_is_pure(pdevc)) {
161 	gx_color_index pixel = COLOR_PIXEL(edev, gx_dc_pure_color(pdevc));
162 
163 	color_set_pure(ppdc, pixel);
164 	reduced = REDUCE_PURE(edev, pixel);
165     } else if (gx_dc_is_binary_halftone(pdevc)) {
166 	gx_color_index pixel0 =
167 	    TRANS_COLOR_PIXEL(edev, gx_dc_binary_color0(pdevc));
168 	gx_color_index pixel1 =
169 	    TRANS_COLOR_PIXEL(edev, gx_dc_binary_color1(pdevc));
170 
171 	if (pixel0 == pixel1) {
172 	    color_set_pure(ppdc, pixel0);
173 	    reduced = REDUCE_PURE(edev, pixel0);
174 	} else {
175 	    *ppdc = *pdevc;
176 	    ppdc->colors.binary.color[0] = pixel0;
177 	    ppdc->colors.binary.color[1] = pixel1;
178 	    edev->any_marks = true;
179 	    reduced = REDUCE_DRAW;
180 	}
181     } else if (color_is_colored_halftone(pdevc)) {
182 	int plane = edev->plane.index;
183 	int i;
184 
185 	*ppdc = *pdevc;
186 	for (i = 0; i < countof(ppdc->colors.colored.c_base); ++i)
187 	    if (i != edev->plane.index) {
188 		ppdc->colors.colored.c_base[i] = 0;
189 		ppdc->colors.colored.c_level[i] = 0;
190 	    }
191 	ppdc->colors.colored.plane_mask &= 1 << plane;
192 	if (ppdc->colors.colored.c_level[plane] == 0) {
193 	    gx_reduce_colored_halftone(ppdc, (gx_device *)edev, true);
194 	    ppdc->colors.pure = COLOR_PIXEL(edev, ppdc->colors.pure);
195 	    reduced = REDUCE_PURE(edev, gx_dc_pure_color(ppdc));
196 	} else if (ppdc->colors.colored.alpha != gx_max_color_value)
197 	    return REDUCE_FAILED; /* can't reduce */
198 	else {
199 	    gx_reduce_colored_halftone(ppdc, (gx_device *)edev, true);
200 	    ppdc->colors.binary.color[0] =
201 		COLOR_PIXEL(edev, ppdc->colors.binary.color[0]);
202 	    ppdc->colors.binary.color[1] =
203 		COLOR_PIXEL(edev, ppdc->colors.binary.color[1]);
204 	    gx_color_load(ppdc, NULL, (gx_device *)edev);
205 	    edev->any_marks = true;
206 	    reduced = REDUCE_DRAW;
207 	}
208     } else
209 	return REDUCE_FAILED;		/* can't handle it */
210     if (*plop & lop_T_transparent) {
211 	/*
212 	 * If the logical operation invokes transparency for the texture, we
213 	 * must do some extra work, since a color that was originally opaque
214 	 * may become transparent (white) if reduced to a single plane.  If
215 	 * RasterOp transparency were calculated before halftoning, life
216 	 * would be easy: we would simply turn off texture transparency in
217 	 * the logical operation iff the original (not reduced) color was
218 	 * not white.  Unfortunately, RasterOp transparency is calculated
219 	 * after halftoning.  (This is arguably wrong, but it's how we've
220 	 * defined it.)  Therefore, if transparency is involved with a
221 	 * white color or a halftone that can include white, we must keep
222 	 * the entire pixel together for the RasterOp.
223 	 */
224 	gx_color_index white = gx_device_white((gx_device *)edev);
225 
226 	/*
227 	 * Given that we haven't failed, the only possible colors at this
228 	 * point are pure or binary halftone.
229 	 */
230 	if (gx_dc_is_pure(ppdc)) {
231 	    if (gx_dc_pure_color(pdevc) != white)
232 		*plop &= ~lop_T_transparent;
233 	    else if (!gx_dc_is_pure(pdevc))
234 		return REDUCE_FAILED;
235 	} else {
236 	    if (gx_dc_binary_color0(pdevc) != white &&
237 		gx_dc_binary_color1(pdevc) != white) {
238 		*plop &= ~lop_T_transparent;
239 	    } else
240 		return REDUCE_FAILED;
241 	}
242     }
243     return reduced;
244 }
245 
246 /*
247  * Set up to create the plane-extracted bitmap corresponding to a
248  * source or halftone pixmap.  If the bitmap doesn't fit in the locally
249  * allocated buffer, we may either do the operation in pieces, or allocate
250  * a buffer on the heap.  The control structure is:
251  *	begin_tiling(&state, ...);
252  *	do {
253  *	    extract_partial_tile(&state);
254  *	    ... process tile in buffer ...
255  *	} while (next_tile(&state));
256  *	end_tiling(&state);
257  * If partial_ok is false, there is only a single tile, so the do ... while
258  * is not used.
259  */
260 typedef struct tiling_state_s {
261 	/* Save the original operands. */
262     const gx_device_plane_extract *edev;
263     const byte *data;
264     int data_x;
265     uint raster;
266     int width, height;
267     int dest_x;			/* only for copy_color, defaults to 0 */
268 	/* Define the (aligned) buffer for doing the operation. */
269     struct tsb_ {
270 	byte *data;
271 	uint size;
272 	uint raster;
273 	bool on_heap;
274     } buffer;
275 	/* Record the current tile available for processing. */
276 	/* The client may read these out. */
277     gs_int_point offset;
278     gs_int_point size;
279 	/* Record private tiling parameters. */
280     int per_tile_width;
281 } tiling_state_t;
282 
283 /*
284  * Extract the plane's data from one subrectangle of a source tile.
285  */
286 inline private int /* ignore the return value */
287 extract_partial_tile(const tiling_state_t *pts)
288 {
289     const gx_device_plane_extract * const edev = pts->edev;
290     bits_plane_t dest, source;
291 
292     dest.data.write = pts->buffer.data + pts->offset.y * pts->buffer.raster;
293     dest.raster = pts->buffer.raster;
294     dest.depth = edev->plane.depth;
295     dest.x = pts->dest_x;
296 
297     source.data.read = pts->data + pts->offset.y * pts->raster;
298     source.raster = pts->raster;
299     source.depth = edev->color_info.depth;
300     source.x = pts->data_x + pts->offset.x;
301 
302     bits_extract_plane(&dest, &source, edev->plane.shift,
303 		       pts->size.x, pts->size.y);
304     return 0;
305 }
306 
307 /*
308  * Set up to start (possibly) tiling.  Return 0 if the entire tile fit,
309  * 1 if a partial tile fit, or a negative error code.
310  */
311 private int
312 begin_tiling(tiling_state_t *pts, gx_device_plane_extract *edev,
313     const byte *data, int data_x, uint raster, int width, int height,
314     byte *local_buffer, uint buffer_size, bool partial_ok)
315 {
316     uint width_raster =
317 	bitmap_raster(width * edev->plane_dev->color_info.depth);
318     uint full_size = width_raster * height;
319 
320     pts->edev = edev;
321     pts->data = data, pts->data_x = data_x, pts->raster = raster;
322     pts->width = width, pts->height = height;
323     pts->dest_x = 0;
324     if (full_size <= buffer_size) {
325 	pts->buffer.data = local_buffer;
326 	pts->buffer.size = buffer_size;
327 	pts->buffer.raster = width_raster;
328 	pts->buffer.on_heap = false;
329 	pts->size.x = width, pts->size.y = height;
330     } else if (partial_ok) {
331 	pts->buffer.data = local_buffer;
332 	pts->buffer.size = buffer_size;
333 	pts->buffer.on_heap = false;
334 	if (buffer_size >= width_raster) {
335 	    pts->buffer.raster = width_raster;
336 	    pts->size.x = width;
337 	    pts->size.y = buffer_size / width_raster;
338 	} else {
339 	    pts->buffer.raster = buffer_size & -align_bitmap_mod;
340 	    pts->size.x =
341 		pts->buffer.raster * (8 / edev->plane_dev->color_info.depth);
342 	    pts->size.y = 1;
343 	}
344     } else {
345 	pts->buffer.data =
346 	    gs_alloc_bytes(edev->memory, full_size, "begin_tiling");
347 	if (!pts->buffer.data)
348 	    return_error(gs_error_VMerror);
349 	pts->buffer.size = full_size;
350 	pts->buffer.raster = width_raster;
351 	pts->buffer.on_heap = true;
352 	pts->size.x = width, pts->size.y = height;
353     }
354     pts->buffer.raster = width_raster;
355     pts->offset.x = pts->offset.y = 0;
356     pts->per_tile_width = pts->size.x;
357     return pts->buffer.size < full_size;
358 }
359 
360 /*
361  * Advance to the next tile.  Return true if there are more tiles to do.
362  */
363 private bool
364 next_tile(tiling_state_t *pts)
365 {
366     if ((pts->offset.x += pts->size.x) >= pts->width) {
367 	if ((pts->offset.y += pts->size.y) >= pts->height)
368 	    return false;
369 	pts->offset.x = 0;
370 	pts->size.x = pts->per_tile_width;
371 	if (pts->offset.y + pts->size.y >= pts->height)
372 	    pts->size.y = pts->height - pts->offset.y;
373     } else if (pts->offset.x + pts->size.x >= pts->width)
374 	pts->size.x = pts->width - pts->offset.x;
375     return true;
376 }
377 
378 /*
379  * Finish tiling by freeing the buffer if necessary.
380  */
381 private void
382 end_tiling(tiling_state_t *pts)
383 {
384     if (pts->buffer.on_heap)
385 	gs_free_object(pts->edev->memory, pts->buffer.data, "end_tiling");
386 }
387 
388 /* ---------------- Initialization ---------------- */
389 
390 int
391 plane_device_init(gx_device_plane_extract *edev, gx_device *target,
392     gx_device *plane_dev, const gx_render_plane_t *render_plane, bool clear)
393 {
394     /* Check for compatibility of the plane specification. */
395     if (render_plane->depth > plane_dev->color_info.depth)
396 	return_error(gs_error_rangecheck);
397     gx_device_init((gx_device *)edev,
398 		   (const gx_device *)&gs_plane_extract_device,
399 		   edev->memory, true);
400     gx_device_forward_fill_in_procs((gx_device_forward *)edev);
401     gx_device_set_target((gx_device_forward *)edev, target);
402     gx_device_copy_params((gx_device *)edev, target);
403     edev->plane_dev = plane_dev;
404     edev->plane = *render_plane;
405     plane_open_device((gx_device *)edev);
406     if (clear) {
407 	dev_proc(plane_dev, fill_rectangle)
408 	    (plane_dev, 0, 0, plane_dev->width, plane_dev->height,
409 	     edev->plane_white);
410 	edev->any_marks = false;
411     }
412     return 0;
413 }
414 
415 /* ---------------- Driver procedures ---------------- */
416 
417 private int
418 plane_open_device(gx_device *dev)
419 {
420     gx_device_plane_extract * const edev = (gx_device_plane_extract *)dev;
421     gx_device * const plane_dev = edev->plane_dev;
422     int plane_depth = plane_dev->color_info.depth;
423     const gx_device_memory * const mdproto =
424 	gdev_mem_device_for_bits(plane_depth);
425 
426     edev->plane_white = gx_device_white(plane_dev);
427     edev->plane_mask = (1 << plane_depth) - 1;
428     edev->plane_dev_is_memory = mdproto != 0 &&
429 	dev_proc(plane_dev, copy_color) == dev_proc(mdproto, copy_color);
430     /* We don't set or clear any_marks here: see ...init above. */
431     return 0;
432 }
433 
434 private int
435 plane_fill_rectangle(gx_device *dev,
436     int x, int y, int w, int h, gx_color_index color)
437 {
438     gx_device_plane_extract * const edev = (gx_device_plane_extract *)dev;
439     gx_device * const plane_dev = edev->plane_dev;
440     gx_color_index pixel = COLOR_PIXEL(edev, color);
441 
442     if (pixel != edev->plane_white)
443 	edev->any_marks = true;
444     else if (!edev->any_marks)
445 	return 0;
446     return dev_proc(plane_dev, fill_rectangle)
447 	(plane_dev, x, y, w, h, pixel);
448 }
449 
450 private int
451 plane_copy_mono(gx_device *dev,
452     const byte *data, int data_x, int raster, gx_bitmap_id id,
453     int x, int y, int w, int h,
454     gx_color_index color0, gx_color_index color1)
455 {
456     gx_device_plane_extract * const edev = (gx_device_plane_extract *)dev;
457     gx_device * const plane_dev = edev->plane_dev;
458     gx_color_index pixel0 = TRANS_COLOR_PIXEL(edev, color0);
459     gx_color_index pixel1 = TRANS_COLOR_PIXEL(edev, color1);
460 
461     if (pixel0 == pixel1)
462 	return plane_fill_rectangle(dev, x, y, w, h, color0);
463     if ((pixel0 == edev->plane_white || pixel0 == gx_no_color_index) &&
464 	(pixel1 == edev->plane_white || pixel1 == gx_no_color_index)) {
465 	/* This operation will only write white. */
466 	if (!edev->any_marks)
467 	    return 0;
468     } else
469 	edev->any_marks = true;
470     return dev_proc(plane_dev, copy_mono)
471 	(plane_dev, data, data_x, raster, id, x, y, w, h, pixel0, pixel1);
472 }
473 
474 private int
475 plane_copy_color(gx_device *dev,
476     const byte *data, int data_x, int raster, gx_bitmap_id id,
477     int x, int y, int w, int h)
478 {
479     gx_device_plane_extract * const edev = (gx_device_plane_extract *)dev;
480     gx_device * const plane_dev = edev->plane_dev;
481     tiling_state_t state;
482     long buf[COPY_COLOR_BUF_SIZE / sizeof(long)];
483     int code;
484 
485     if (edev->plane_dev_is_memory) {
486 	/* Reduce the source directly into the plane device. */
487 	gx_device_memory * const mdev = (gx_device_memory *)plane_dev;
488 
489 	fit_copy(edev, data, data_x, raster, id, x, y, w, h);
490 	code = begin_tiling(&state, edev, data, data_x, raster, w, h,
491 			    scan_line_base(mdev, y), max_uint, false);
492 	if (code < 0)
493 	    return code;
494 	state.dest_x = x;
495 	state.buffer.raster = mdev->raster;
496 	extract_partial_tile(&state);
497 	end_tiling(&state);
498 	edev->any_marks = true;
499 	return 0;
500     }
501     code = begin_tiling(&state, edev, data, data_x, raster,
502 			w, h, (byte *)buf, sizeof(buf), true);
503     if (code < 0)
504 	return code;
505     do {
506 	extract_partial_tile(&state);
507 	code = dev_proc(plane_dev, copy_color)
508 	    (plane_dev, state.buffer.data, 0, state.buffer.raster,
509 	     gx_no_bitmap_id, x + state.offset.x, y + state.offset.y,
510 	     state.size.x, state.size.y);
511     } while (code >= 0 && next_tile(&state));
512     end_tiling(&state);
513     edev->any_marks = true;
514     return code;
515 }
516 
517 private int
518 plane_copy_alpha(gx_device *dev, const byte *data, int data_x,
519     int raster, gx_bitmap_id id, int x, int y, int w, int h,
520     gx_color_index color, int depth)
521 {
522     gx_device_plane_extract * const edev = (gx_device_plane_extract *)dev;
523     gx_device * const plane_dev = edev->plane_dev;
524     gx_color_index pixel = COLOR_PIXEL(edev, color);
525 
526     if (pixel != edev->plane_white)
527 	edev->any_marks = true;
528     else if (!edev->any_marks)
529 	return 0;
530     return dev_proc(plane_dev, copy_alpha)
531 	(plane_dev, data, data_x, raster, id, x, y, w, h, pixel, depth);
532 }
533 
534 private int
535 plane_fill_path(gx_device *dev,
536     const gs_imager_state *pis, gx_path *ppath,
537     const gx_fill_params *params,
538     const gx_drawing_color *pdevc, const gx_clip_path *pcpath)
539 {
540     gx_device_plane_extract * const edev = (gx_device_plane_extract *)dev;
541     gx_device * const plane_dev = edev->plane_dev;
542     gs_logical_operation_t lop_orig =
543 	gs_current_logical_op((const gs_state *)pis);
544     gs_logical_operation_t lop = lop_orig;
545     gx_device_color dcolor;
546 
547     switch (reduce_drawing_color(&dcolor, edev, pdevc, &lop)) {
548     case REDUCE_SKIP:
549 	return 0;
550     case REDUCE_DRAW: {
551 	gs_imager_state lopis;
552 	const gs_imager_state *pis_draw = pis;
553 
554 	if (lop != lop_orig) {
555 	    lopis = *pis;
556 	    gs_set_logical_op((gs_state *)&lopis, lop);
557 	    pis_draw = &lopis;
558 	}
559 	return dev_proc(plane_dev, fill_path)
560 	    (plane_dev, pis_draw, ppath, params, &dcolor, pcpath);
561     }
562     default /*REDUCE_FAILED*/:
563 	return gx_default_fill_path(dev, pis, ppath, params, pdevc, pcpath);
564     }
565 }
566 
567 private int
568 plane_stroke_path(gx_device *dev,
569     const gs_imager_state *pis, gx_path *ppath,
570     const gx_stroke_params *params,
571     const gx_drawing_color *pdevc, const gx_clip_path *pcpath)
572 {
573     gx_device_plane_extract * const edev = (gx_device_plane_extract *)dev;
574     gx_device * const plane_dev = edev->plane_dev;
575     gs_logical_operation_t lop_orig =
576 	gs_current_logical_op((const gs_state *)pis);
577     gs_logical_operation_t lop = lop_orig;
578     gx_device_color dcolor;
579 
580     switch (reduce_drawing_color(&dcolor, edev, pdevc, &lop)) {
581     case REDUCE_SKIP:
582 	return 0;
583     case REDUCE_DRAW: {
584 	gs_imager_state lopis;
585 	const gs_imager_state *pis_draw = pis;
586 
587 	if (lop != lop_orig) {
588 	    lopis = *pis;
589 	    gs_set_logical_op((gs_state *)&lopis, lop);
590 	    pis_draw = &lopis;
591 	}
592 	return dev_proc(plane_dev, stroke_path)
593 	    (plane_dev, pis_draw, ppath, params, &dcolor, pcpath);
594     }
595     default /*REDUCE_FAILED*/:
596 	return gx_default_stroke_path(dev, pis, ppath, params, pdevc, pcpath);
597     }
598 }
599 
600 private int
601 plane_fill_mask(gx_device *dev,
602     const byte *data, int data_x, int raster, gx_bitmap_id id,
603     int x, int y, int w, int h,
604     const gx_drawing_color *pdcolor, int depth,
605     gs_logical_operation_t lop, const gx_clip_path *pcpath)
606 {
607     gx_device_plane_extract * const edev = (gx_device_plane_extract *)dev;
608     gx_device * const plane_dev = edev->plane_dev;
609     gx_device_color dcolor;
610 
611     switch (reduce_drawing_color(&dcolor, edev, pdcolor, &lop)) {
612     case REDUCE_SKIP:
613 	return 0;
614     case REDUCE_DRAW:
615 	return dev_proc(plane_dev, fill_mask)
616 	    (plane_dev, data, data_x, raster, gx_no_bitmap_id, x, y, w, h,
617 	     &dcolor, depth, lop, pcpath);
618     default /*REDUCE_FAILED*/:
619 	return gx_default_fill_mask(dev, data, data_x, raster, gx_no_bitmap_id,
620 				    x, y, w, h, &dcolor, depth, lop, pcpath);
621     }
622 }
623 
624 private int
625 plane_fill_parallelogram(gx_device * dev,
626     fixed px, fixed py, fixed ax, fixed ay, fixed bx, fixed by,
627     const gx_drawing_color * pdcolor, gs_logical_operation_t lop)
628 {
629     gx_device_plane_extract * const edev = (gx_device_plane_extract *)dev;
630     gx_device * const plane_dev = edev->plane_dev;
631     gx_device_color dcolor;
632 
633     switch (reduce_drawing_color(&dcolor, edev, pdcolor, &lop)) {
634     case REDUCE_SKIP:
635 	return 0;
636     case REDUCE_DRAW:
637 	return dev_proc(plane_dev, fill_parallelogram)
638 	    (plane_dev, px, py, ax, ay, bx, by, &dcolor, lop);
639     default /*REDUCE_FAILED*/:
640 	return gx_default_fill_parallelogram(dev, px, py, ax, ay, bx, by,
641 					     pdcolor, lop);
642     }
643 }
644 
645 private int
646 plane_fill_triangle(gx_device * dev,
647     fixed px, fixed py, fixed ax, fixed ay, fixed bx, fixed by,
648     const gx_drawing_color * pdcolor, gs_logical_operation_t lop)
649 {
650     gx_device_plane_extract * const edev = (gx_device_plane_extract *)dev;
651     gx_device * const plane_dev = edev->plane_dev;
652     gx_device_color dcolor;
653 
654     switch (reduce_drawing_color(&dcolor, edev, pdcolor, &lop)) {
655     case REDUCE_SKIP:
656 	return 0;
657     case REDUCE_DRAW:
658 	return dev_proc(plane_dev, fill_triangle)
659 	    (plane_dev, px, py, ax, ay, bx, by, &dcolor, lop);
660     default /*REDUCE_FAILED*/:
661 	return gx_default_fill_triangle(dev, px, py, ax, ay, bx, by,
662 					pdcolor, lop);
663     }
664 }
665 
666 private int
667 plane_strip_tile_rectangle(gx_device *dev,
668     const gx_strip_bitmap *tiles, int x, int y, int w, int h,
669     gx_color_index color0, gx_color_index color1,
670     int phase_x, int phase_y)
671 {
672     gx_device_plane_extract * const edev = (gx_device_plane_extract *)dev;
673     gx_device * const plane_dev = edev->plane_dev;
674     gx_color_index pixel0 = TRANS_COLOR_PIXEL(edev, color0);
675     gx_color_index pixel1 = TRANS_COLOR_PIXEL(edev, color1);
676 
677     if (pixel0 == pixel1) {
678 	if (pixel0 != gx_no_color_index)
679 	    return plane_fill_rectangle(dev, x, y, w, h, color0);
680 	/* The tile is a pixmap rather than a bitmap. */
681 	/* We should use the default implementation if it is small.... */
682 	{
683 	    gx_strip_bitmap plane_tile;
684 	    tiling_state_t state;
685 	    long buf[TILE_RECTANGLE_BUF_SIZE / sizeof(long)];
686 	    int code = begin_tiling(&state, edev, tiles->data, 0, tiles->raster,
687 			tiles->size.x, tiles->size.y,
688 				(byte *)buf, sizeof(buf), false);
689 
690 	    if (code < 0)
691 		return gx_default_strip_tile_rectangle(dev, tiles, x, y, w, h,
692 					color0, color1, phase_x, phase_y);
693 	    extract_partial_tile(&state);
694 	    plane_tile = *tiles;
695 	    plane_tile.data = state.buffer.data;
696 	    plane_tile.raster = state.buffer.raster;
697 	    plane_tile.id = gx_no_bitmap_id;
698 	    code = dev_proc(plane_dev, strip_tile_rectangle)
699 		(plane_dev, &plane_tile, x, y, w, h, pixel0, pixel1,
700 		 phase_x, phase_y);
701 	    end_tiling(&state);
702 	    edev->any_marks = true;
703 	    return code;
704 	}
705     }
706     if ((pixel0 == edev->plane_white || pixel0 == gx_no_color_index) &&
707 	(pixel1 == edev->plane_white || pixel1 == gx_no_color_index)) {
708 	/* This operation will only write white. */
709 	if (!edev->any_marks)
710 	    return 0;
711     } else
712 	edev->any_marks = true;
713     return dev_proc(plane_dev, strip_tile_rectangle)
714 	(plane_dev, tiles, x, y, w, h, pixel0, pixel1, phase_x, phase_y);
715 }
716 
717 private int
718 plane_strip_copy_rop(gx_device *dev,
719     const byte *sdata, int sourcex, uint sraster, gx_bitmap_id id,
720     const gx_color_index *scolors,
721     const gx_strip_bitmap *textures, const gx_color_index *tcolors,
722     int x, int y, int w, int h,
723     int phase_x, int phase_y, gs_logical_operation_t lop)
724 {
725     gx_device_plane_extract * const edev = (gx_device_plane_extract *)dev;
726     gx_device * const plane_dev = edev->plane_dev;
727     gs_rop3_t rop = lop_rop(lop);
728     struct crp_ {
729 	gx_color_index pixels[2];
730 	gx_color_index *colors;
731 	tiling_state_t state;
732     } source, texture;
733     long sbuf[COPY_ROP_SOURCE_BUF_SIZE / sizeof(long)];
734     long tbuf[COPY_ROP_TEXTURE_BUF_SIZE / sizeof(long)];
735     const byte *plane_source;
736     uint plane_raster;
737     gx_strip_bitmap plane_texture;
738     const gx_strip_bitmap *plane_textures;
739     int code;
740 
741     /* We should do better than this on transparency.... */
742     if (lop & (lop_S_transparent | lop_T_transparent))
743 	return gx_default_strip_copy_rop(dev, sdata, sourcex, sraster, id,
744 					 scolors, textures, tcolors,
745 					 x, y, w, h, phase_x, phase_y, lop);
746     if (!rop3_uses_S(rop)) {
747 	sdata = 0;
748 	source.colors = 0;
749     } else if (scolors) {
750 	source.pixels[0] = COLOR_PIXEL(edev, scolors[0]);
751 	source.pixels[1] = COLOR_PIXEL(edev, scolors[1]);
752 	if (source.pixels[0] == source.pixels[1])
753 	    sdata = 0;
754 	source.colors = source.pixels;
755     }
756     else
757 	source.colors = 0;
758     if (!rop3_uses_T(rop)) {
759 	textures = 0;
760 	texture.colors = 0;
761     } else if (tcolors) {
762 	texture.pixels[0] = COLOR_PIXEL(edev, tcolors[0]);
763 	texture.pixels[1] = COLOR_PIXEL(edev, tcolors[1]);
764 	if (texture.pixels[0] == texture.pixels[1])
765 	    textures = 0;
766 	texture.colors = texture.pixels;
767     }
768     else
769 	texture.colors = 0;
770     if (sdata) {
771 	code = begin_tiling(&source.state, edev, sdata, sourcex, sraster, w, y,
772 			    (byte *)sbuf, sizeof(sbuf), true);
773 	if (code < 0)
774 	    return gx_default_strip_copy_rop(dev, sdata, sourcex, sraster, id,
775 					     scolors, textures, tcolors,
776 					     x, y, w, h, phase_x, phase_y, lop);
777 	plane_source = source.state.buffer.data;
778 	plane_raster = source.state.buffer.raster;
779     } else
780 	plane_source = 0;
781     if (textures) {
782 	code = begin_tiling(&texture.state, edev, textures->data, 0,
783 			    textures->raster, textures->size.x,
784 			    textures->size.y, (byte *)tbuf, sizeof(tbuf),
785 			    false);
786 	if (code < 0) {
787 	    if (plane_source)
788 		end_tiling(&source.state);
789 	    return code;
790 	}
791 	plane_texture = *textures;
792 	plane_texture.data = texture.state.buffer.data;
793 	plane_texture.raster = texture.state.buffer.raster;
794 	plane_textures = &plane_texture;
795     }
796     if (textures)
797 	extract_partial_tile(&texture.state);
798     do {
799 	if (sdata)
800 	    extract_partial_tile(&source.state);
801 	code = dev_proc(plane_dev, strip_copy_rop)
802 	    (plane_dev, plane_source, sourcex, plane_raster, gx_no_bitmap_id,
803 	     source.colors, plane_textures, texture.colors,
804 	     x, y, w, h, phase_x, phase_y, lop);
805     } while (code >= 0 && sdata && next_tile(&source.state));
806     if (textures)
807 	end_tiling(&texture.state);
808     if (sdata)
809 	end_tiling(&source.state);
810     return code;
811 }
812 
813 /* ---------------- Images ---------------- */
814 
815 /* Define the state for image rendering. */
816 typedef struct plane_image_enum_s {
817     gx_image_enum_common;
818     gs_memory_t *memory;
819     gx_image_enum_common_t *info; /* plane device enumerator */
820     const gs_imager_state *pis;	/* original imager state */
821     gs_imager_state *pis_image;	/* modified imager state */
822 } plane_image_enum_t;
823 gs_private_st_suffix_add3(st_plane_image_enum, plane_image_enum_t,
824   "plane_image_enum_t", plane_image_enum_enum_ptrs,
825   plane_image_enum_reloc_ptrs, st_gx_image_enum_common, info, pis, pis_image);
826 
827 /*
828  * Reduce drawing colors returned by color mapping.  Note that these
829  * assume that the call of reduce_drawing_color will not fail:
830  * plane_begin_typed_image must ensure this.
831  *
832  * In the imager state passed to these procedures, the client data is
833  * the plane_image_enum_t.
834  */
835 
836 private void
837 plane_cmap_gray(frac gray, gx_device_color * pdc,
838     const gs_imager_state *pis_image, gx_device *dev, gs_color_select_t select)
839 {
840     const plane_image_enum_t *ppie =
841 	(const plane_image_enum_t *)pis_image->client_data;
842     gx_device_plane_extract * const edev =
843 	(gx_device_plane_extract *)ppie->dev;
844     gs_logical_operation_t lop = gs_current_logical_op_inline(pis_image);
845     gx_device_color dcolor;
846 
847     gx_remap_concrete_gray(gray, &dcolor, ppie->pis,
848 			   (gx_device *)edev, select);
849     reduce_drawing_color(pdc, edev, &dcolor, &lop);
850 }
851 private void
852 plane_cmap_rgb(frac r, frac g, frac b, gx_device_color * pdc,
853     const gs_imager_state *pis_image, gx_device *dev, gs_color_select_t select)
854 {
855     const plane_image_enum_t *ppie =
856 	(const plane_image_enum_t *)pis_image->client_data;
857     gx_device_plane_extract * const edev =
858 	(gx_device_plane_extract *)ppie->dev;
859     gs_logical_operation_t lop = gs_current_logical_op_inline(pis_image);
860     gx_device_color dcolor;
861 
862     gx_remap_concrete_rgb(r, g, b, &dcolor, ppie->pis,
863 			  (gx_device *)edev, select);
864     reduce_drawing_color(pdc, edev, &dcolor, &lop);
865 }
866 private void
867 plane_cmap_cmyk(frac c, frac m, frac y, frac k, gx_device_color * pdc,
868     const gs_imager_state *pis_image, gx_device *dev, gs_color_select_t select)
869 {
870     const plane_image_enum_t *ppie =
871 	(const plane_image_enum_t *)pis_image->client_data;
872     gx_device_plane_extract * const edev =
873 	(gx_device_plane_extract *)ppie->dev;
874     gs_logical_operation_t lop = gs_current_logical_op_inline(pis_image);
875     gx_device_color dcolor;
876 
877     gx_remap_concrete_cmyk(c, m, y, k, &dcolor, ppie->pis,
878 			   (gx_device *)edev, select);
879     reduce_drawing_color(pdc, edev, &dcolor, &lop);
880 }
881 private void
882 plane_cmap_rgb_alpha(frac r, frac g, frac b, frac alpha, gx_device_color * pdc,
883     const gs_imager_state *pis_image, gx_device *dev, gs_color_select_t select)
884 {
885     const plane_image_enum_t *ppie =
886 	(const plane_image_enum_t *)pis_image->client_data;
887     gx_device_plane_extract * const edev =
888 	(gx_device_plane_extract *)ppie->dev;
889     gs_logical_operation_t lop = gs_current_logical_op_inline(pis_image);
890     gx_device_color dcolor;
891 
892     gx_remap_concrete_rgb_alpha(r, g, b, alpha, &dcolor, ppie->pis,
893 				(gx_device *)edev, select);
894     reduce_drawing_color(pdc, edev, &dcolor, &lop);
895 }
896 private const gx_color_map_procs plane_color_map_procs = {
897     plane_cmap_gray, plane_cmap_rgb, plane_cmap_cmyk, plane_cmap_rgb_alpha
898 };
899 private const gx_color_map_procs *
900 plane_get_cmap_procs(const gs_imager_state *pis, const gx_device *dev)
901 {
902     return &plane_color_map_procs;
903 }
904 
905 /* Define the image processing procedures. */
906 private image_enum_proc_plane_data(plane_image_plane_data);
907 private image_enum_proc_end_image(plane_image_end_image);
908 private const gx_image_enum_procs_t plane_image_enum_procs = {
909     plane_image_plane_data, plane_image_end_image
910 };
911 
912 private int
913 plane_begin_typed_image(gx_device * dev,
914 			const gs_imager_state * pis, const gs_matrix * pmat,
915 		   const gs_image_common_t * pic, const gs_int_rect * prect,
916 	      const gx_drawing_color * pdcolor, const gx_clip_path * pcpath,
917 		      gs_memory_t * memory, gx_image_enum_common_t ** pinfo)
918 {
919     /*
920      * For images, we intercept the imager state's cmap_procs and apply
921      * reduce_drawing_color to the colors as they are returned to the image
922      * processing code.  For reasons explained above, we can't do this in
923      * some cases of RasterOp that include transparency.
924      */
925     gx_device_plane_extract * const edev = (gx_device_plane_extract *)dev;
926     gs_logical_operation_t lop = gs_current_logical_op((const gs_state *)pis);
927     const gs_pixel_image_t *pim;
928     plane_image_enum_t *info = 0;
929     gs_imager_state *pis_image = 0;
930     gx_device_color dcolor;
931     bool uses_color = false;
932     int code;
933 
934     /* We can only handle a limited set of image types. */
935     switch (pic->type->index) {
936     case 1: {
937 	const gs_image1_t * const pim1 = (const gs_image1_t *)pic;
938 
939 	if (pim1->Alpha != gs_image_alpha_none)
940 	    goto fail;
941 	uses_color = pim1->ImageMask;
942 	break;
943 	}
944     case 3:
945     case 4:
946 	break;
947     default:
948 	goto fail;
949     }
950     pim = (const gs_pixel_image_t *)pic;
951     if ((lop & lop_S_transparent) ||
952 	((uses_color || pim->CombineWithColor) && (lop & lop_T_transparent))
953 	)
954 	goto fail;
955     if (uses_color || (pim->CombineWithColor && lop_uses_T(lop))) {
956 	if (reduce_drawing_color(&dcolor, edev, pdcolor, &lop) ==
957 	    REDUCE_FAILED)
958 	    goto fail;
959     } else {
960 	/*
961 	 * The drawing color won't be used, but if RasterOp is involved,
962 	 * it may still be accessed in some anomalous cases.
963 	 */
964 	color_set_pure(&dcolor, (gx_color_index)0);
965     }
966     info = gs_alloc_struct(memory, plane_image_enum_t, &st_plane_image_enum,
967 			   "plane_image_begin_typed(info)");
968     pis_image = gs_imager_state_copy(pis, memory);
969     if (pis_image == 0 || info == 0)
970 	goto fail;
971     *pis_image = *pis;
972     pis_image->client_data = info;
973     pis_image->get_cmap_procs = plane_get_cmap_procs;
974     code = dev_proc(edev->plane_dev, begin_typed_image)
975 	(edev->plane_dev, pis_image, pmat, pic, prect,
976 	 &dcolor, pcpath, memory, &info->info);
977     if (code < 0)
978 	goto fail;
979     *((gx_image_enum_common_t *)info) = *info->info;
980     info->procs = &plane_image_enum_procs;
981     info->dev = (gx_device *)edev;
982     info->id = gs_next_ids(1);
983     info->memory = memory;
984     info->pis = pis;
985     info->pis_image = pis_image;
986     *pinfo = (gx_image_enum_common_t *)info;
987     return code;
988 fail:
989     gs_free_object(memory, pis_image, "plane_image_begin_typed(pis_image)");
990     gs_free_object(memory, info, "plane_image_begin_typed(info)");
991     return gx_default_begin_typed_image(dev, pis, pmat, pic, prect,
992 					pdcolor, pcpath, memory, pinfo);
993 }
994 
995 private int
996 plane_image_plane_data(gx_image_enum_common_t * info,
997 		       const gx_image_plane_t * planes, int height,
998 		       int *rows_used)
999 {
1000     plane_image_enum_t * const ppie = (plane_image_enum_t *)info;
1001 
1002     return gx_image_plane_data_rows(ppie->info, planes, height, rows_used);
1003 }
1004 
1005 private int
1006 plane_image_end_image(gx_image_enum_common_t * info, bool draw_last)
1007 {
1008     plane_image_enum_t * const ppie = (plane_image_enum_t *)info;
1009     int code = gx_image_end(ppie->info, draw_last);
1010 
1011     gs_free_object(ppie->memory, ppie->pis_image,
1012 		   "plane_image_end_image(pis_image)");
1013     gs_free_object(ppie->memory, info, "plane_image_end_image(info)");
1014     return code;
1015 }
1016 
1017 /* ---------------- Reading back bits ---------------- */
1018 
1019 private int
1020 plane_get_bits_rectangle(gx_device * dev, const gs_int_rect * prect,
1021 			 gs_get_bits_params_t * params, gs_int_rect ** unread)
1022 {
1023     gx_device_plane_extract * const edev = (gx_device_plane_extract *)dev;
1024     gx_device * const plane_dev = edev->plane_dev;
1025     int plane_index = edev->plane.index;
1026     gs_get_bits_options_t options = params->options;
1027     gs_get_bits_params_t plane_params;
1028     int plane;
1029     int code;
1030 
1031     /*
1032      * The only real option that this device supports is single-plane
1033      * retrieval.  However, for the default case of RasterOp, it must be
1034      * able to return chunky pixels in which the other components are
1035      * arbitrary (but might as well be zero).
1036      */
1037     if ((options & GB_PACKING_PLANAR) && (options & GB_SELECT_PLANES)) {
1038 	if (params->data[plane_index] == 0)
1039 	    return gx_default_get_bits_rectangle(dev, prect, params, unread);
1040 	/* If the caller wants any other plane(s), punt. */
1041 	for (plane = 0; plane < dev->color_info.num_components; ++plane)
1042 	    if (plane != plane_index && params->data[plane] != 0)
1043 		return gx_default_get_bits_rectangle(dev, prect, params, unread);
1044 	/* Pass the request on to the plane device. */
1045 	plane_params = *params;
1046 	plane_params.options =
1047 	    (options & ~(GB_PACKING_ALL | GB_SELECT_PLANES)) |
1048 	    GB_PACKING_CHUNKY;
1049 	plane_params.data[0] = params->data[plane_index];
1050 	code = dev_proc(plane_dev, get_bits_rectangle)
1051 	    (plane_dev, prect, &plane_params, unread);
1052 	if (code >= 0) {
1053 	    *params = plane_params;
1054 	    params->options = (params->options & ~GB_PACKING_ALL) |
1055 		(GB_PACKING_PLANAR | GB_SELECT_PLANES);
1056 	    params->data[plane_index] = params->data[0];
1057 	    for (plane = 0; plane < dev->color_info.num_components; ++plane)
1058 		if (plane != plane_index)
1059 		    params->data[plane] = 0;
1060 	}
1061     } else if (!(~options & (GB_COLORS_NATIVE | GB_ALPHA_NONE |
1062 			     GB_PACKING_CHUNKY | GB_RETURN_COPY |
1063 			     GB_ALIGN_STANDARD | GB_OFFSET_0 |
1064 			     GB_RASTER_STANDARD))) {
1065 	/* Expand the plane into chunky pixels. */
1066 	bits_plane_t dest, source;
1067 
1068 	dest.data.write = params->data[0];
1069 	dest.raster =
1070 	    bitmap_raster((prect->q.x - prect->p.x) * dev->color_info.depth);
1071 	dest.depth = edev->color_info.depth;
1072 	dest.x = 0;
1073 
1074 	/* not source.data, source.raster, source.x */
1075 	source.depth = plane_dev->color_info.depth;
1076 
1077 	plane_params = *params;
1078 	plane_params.options = options &=
1079 	    (~(GB_COLORS_ALL | GB_ALPHA_ALL | GB_PACKING_ALL |
1080 	       GB_RETURN_ALL | GB_ALIGN_ALL | GB_OFFSET_ALL | GB_RASTER_ALL) |
1081 	     GB_COLORS_NATIVE | GB_ALPHA_NONE | GB_PACKING_CHUNKY |
1082 	     /* Try for a pointer return the first time. */
1083 	     GB_RETURN_POINTER |
1084 	     GB_ALIGN_STANDARD |
1085 	     (GB_OFFSET_0 | GB_OFFSET_ANY) |
1086 	     (GB_RASTER_STANDARD | GB_RASTER_ANY));
1087 	plane_params.raster = gx_device_raster(plane_dev, true);
1088 	code = dev_proc(plane_dev, get_bits_rectangle)
1089 	    (plane_dev, prect, &plane_params, unread);
1090 	if (code >= 0) {
1091 	    /* Success, expand the plane into pixels. */
1092 	    source.data.read = plane_params.data[0];
1093 	    source.raster = plane_params.raster;
1094 	    source.x = params->x_offset;
1095 	    code = bits_expand_plane(&dest, &source, edev->plane.shift,
1096 				     prect->q.x - prect->p.x,
1097 				     prect->q.y - prect->p.y);
1098 	}
1099 	params->options = (options & ~GB_RETURN_POINTER) | GB_RETURN_COPY;
1100     } else
1101 	return gx_default_get_bits_rectangle(dev, prect, params, unread);
1102     return code;
1103 }
1104