xref: /plan9-contrib/sys/src/cmd/gs/src/gdevp14.c (revision d46c239f8612929b7dbade67d0d071633df3a15d)
1 /*
2   Copyright (C) 2001 artofcode LLC.
3 
4   This file is part of AFPL Ghostscript.
5 
6   AFPL Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author or
7   distributor accepts any responsibility for the consequences of using it, or
8   for whether it serves any particular purpose or works at all, unless he or
9   she says so in writing.  Refer to the Aladdin Free Public License (the
10   "License") for full details.
11 
12   Every copy of AFPL Ghostscript must include a copy of the License, normally
13   in a plain ASCII text file named PUBLIC.  The License grants you the right
14   to copy, modify and redistribute AFPL Ghostscript, but only under certain
15   conditions described in the License.  Among other things, the License
16   requires that the copyright notice and this notice be preserved on all
17   copies.
18 
19   Author: Raph Levien <raph@artofcode.com>
20 */
21 /*$Id: gdevp14.c,v 1.4 2001/10/17 17:28:10 raph Exp $ */
22 /* Device filter implementing PDF 1.4 imaging model */
23 
24 #include "math_.h"
25 #include "memory_.h"
26 #include "gx.h"
27 #include "gserrors.h"
28 #include "gscdefs.h"
29 #include "gxdevice.h"
30 #include "gsdevice.h"
31 #include "gsstruct.h"
32 #include "gxistate.h"
33 #include "gxdcolor.h"
34 #include "gxiparam.h"
35 #include "gstparam.h"
36 #include "gxblend.h"
37 #include "gxtext.h"
38 #include "gsdfilt.h"
39 #include "gsimage.h"
40 #include "gzstate.h"
41 #include "gdevp14.h"
42 
43 # define INCR(v) DO_NOTHING
44 
45 /* Buffer stack data structure */
46 
47 #define PDF14_MAX_PLANES 16
48 
49 typedef struct pdf14_buf_s pdf14_buf;
50 typedef struct pdf14_ctx_s pdf14_ctx;
51 
52 struct pdf14_buf_s {
53     pdf14_buf *saved;
54 
55     bool isolated;
56     bool knockout;
57     byte alpha;
58     byte shape;
59     gs_blend_mode_t blend_mode;
60 
61     bool has_alpha_g;
62     bool has_shape;
63 
64     gs_int_rect rect;
65     /* Note: the traditional GS name for rowstride is "raster" */
66 
67     /* Data is stored in planar format. Order of planes is: pixel values,
68        alpha, shape if present, alpha_g if present. */
69 
70     int rowstride;
71     int planestride;
72     int n_chan; /* number of pixel planes including alpha */
73     int n_planes; /* total number of planes including alpha, shape, alpha_g */
74     byte *data;
75 };
76 
77 struct pdf14_ctx_s {
78     pdf14_buf *stack;
79     gs_memory_t *memory;
80     gs_int_rect rect;
81     int n_chan;
82 };
83 
84 /* GC procedures for buffer stack */
85 
86 private
87 ENUM_PTRS_WITH(pdf14_buf_enum_ptrs, pdf14_buf *buf)
88     return 0;
89     case 0: return ENUM_OBJ(buf->saved);
90     case 1: return ENUM_OBJ(buf->data);
91 ENUM_PTRS_END
92 
93 private
94 RELOC_PTRS_WITH(pdf14_buf_reloc_ptrs, pdf14_buf *buf)
95 {
96     RELOC_VAR(buf->saved);
97     RELOC_VAR(buf->data);
98 }
99 RELOC_PTRS_END
100 
101 gs_private_st_composite(st_pdf14_buf, pdf14_buf, "pdf14_buf",
102 			pdf14_buf_enum_ptrs, pdf14_buf_reloc_ptrs);
103 
104 gs_private_st_ptrs1(st_pdf14_ctx, pdf14_ctx, "pdf14_ctx",
105 		    pdf14_ctx_enum_ptrs, pdf14_ctx_reloc_ptrs,
106 		    stack);
107 
108 /* ------ The device descriptors ------ */
109 
110 /*
111  * Default X and Y resolution.
112  */
113 #define X_DPI 72
114 #define Y_DPI 72
115 
116 private int pdf14_open(gx_device * pdev);
117 private dev_proc_close_device(pdf14_close);
118 private int pdf14_output_page(gx_device * pdev, int num_copies, int flush);
119 private dev_proc_fill_rectangle(pdf14_fill_rectangle);
120 private dev_proc_fill_path(pdf14_fill_path);
121 private dev_proc_stroke_path(pdf14_stroke_path);
122 private dev_proc_begin_typed_image(pdf14_begin_typed_image);
123 private dev_proc_text_begin(pdf14_text_begin);
124 private dev_proc_begin_transparency_group(pdf14_begin_transparency_group);
125 private dev_proc_end_transparency_group(pdf14_end_transparency_group);
126 
127 #define XSIZE (8.5 * X_DPI)	/* 8.5 x 11 inch page, by default */
128 #define YSIZE (11 * Y_DPI)
129 
130 /* 24-bit color. */
131 
132 private const gx_device_procs pdf14_procs =
133 {
134 	pdf14_open,			/* open */
135 	NULL,	/* get_initial_matrix */
136 	NULL,	/* sync_output */
137 	pdf14_output_page,		/* output_page */
138 	pdf14_close,			/* close */
139 	gx_default_rgb_map_rgb_color,
140 	gx_default_rgb_map_color_rgb,
141 	pdf14_fill_rectangle,	/* fill_rectangle */
142 	NULL,	/* tile_rectangle */
143 	NULL,	/* copy_mono */
144 	NULL,	/* copy_color */
145 	NULL,	/* draw_line */
146 	NULL,	/* get_bits */
147 	NULL,   /* get_params */
148 	NULL,   /* put_params */
149 	NULL,	/* map_cmyk_color */
150 	NULL,	/* get_xfont_procs */
151 	NULL,	/* get_xfont_device */
152 	NULL,	/* map_rgb_alpha_color */
153 #if 0
154 	gx_page_device_get_page_device,	/* get_page_device */
155 #else
156 	NULL,   /* get_page_device */
157 #endif
158 	NULL,	/* get_alpha_bits */
159 	NULL,	/* copy_alpha */
160 	NULL,	/* get_band */
161 	NULL,	/* copy_rop */
162 	pdf14_fill_path,		/* fill_path */
163 	pdf14_stroke_path,	/* stroke_path */
164 	NULL,	/* fill_mask */
165 	NULL,	/* fill_trapezoid */
166 	NULL,	/* fill_parallelogram */
167 	NULL,	/* fill_triangle */
168 	NULL,	/* draw_thin_line */
169 	NULL,	/* begin_image */
170 	NULL,	/* image_data */
171 	NULL,	/* end_image */
172 	NULL,	/* strip_tile_rectangle */
173 	NULL,	/* strip_copy_rop, */
174 	NULL,	/* get_clipping_box */
175 	pdf14_begin_typed_image,	/* begin_typed_image */
176 	NULL,	/* get_bits_rectangle */
177 	NULL,	/* map_color_rgb_alpha */
178 	NULL,	/* create_compositor */
179 	NULL,	/* get_hardware_params */
180 	pdf14_text_begin,	/* text_begin */
181 	NULL,	/* finish_copydevice */
182 	pdf14_begin_transparency_group,
183 	pdf14_end_transparency_group
184 };
185 
186 typedef struct pdf14_device_s {
187     gx_device_common;
188 
189     pdf14_ctx *ctx;
190     gx_device *target;
191 
192 } pdf14_device;
193 
194 gs_private_st_composite_use_final(st_pdf14_device, pdf14_device, "pdf14_device",
195 				  pdf14_device_enum_ptrs, pdf14_device_reloc_ptrs,
196 			  gx_device_finalize);
197 
198 const pdf14_device gs_pdf14_device = {
199     std_device_color_stype_body(pdf14_device, &pdf14_procs, "pdf14",
200 				&st_pdf14_device,
201 				XSIZE, YSIZE, X_DPI, Y_DPI, 24, 255, 0),
202     { 0 }
203 };
204 
205 /* GC procedures */
206 private
207 ENUM_PTRS_WITH(pdf14_device_enum_ptrs, pdf14_device *pdev) return 0;
208 case 0: return ENUM_OBJ(pdev->ctx);
209 case 1: ENUM_RETURN(gx_device_enum_ptr(pdev->target));
210 ENUM_PTRS_END
211 private RELOC_PTRS_WITH(pdf14_device_reloc_ptrs, pdf14_device *pdev)
212 {
213     RELOC_VAR(pdev->ctx);
214     pdev->target = gx_device_reloc_ptr(pdev->target, gcst);
215 }
216 RELOC_PTRS_END
217 
218 /* ------ The device descriptors for the marking device ------ */
219 
220 private dev_proc_fill_rectangle(pdf14_mark_fill_rectangle);
221 private dev_proc_fill_rectangle(pdf14_mark_fill_rectangle_ko_simple);
222 
223 private const gx_device_procs pdf14_mark_procs =
224 {
225 	NULL,	/* open */
226 	NULL,	/* get_initial_matrix */
227 	NULL,	/* sync_output */
228 	NULL,	/* output_page */
229 	NULL,	/* close */
230 	gx_default_rgb_map_rgb_color,
231 	gx_default_rgb_map_color_rgb,
232 	NULL,	/* fill_rectangle */
233 	NULL,	/* tile_rectangle */
234 	NULL,	/* copy_mono */
235 	NULL,	/* copy_color */
236 	NULL,	/* draw_line */
237 	NULL,	/* get_bits */
238 	NULL,   /* get_params */
239 	NULL,   /* put_params */
240 	NULL,	/* map_cmyk_color */
241 	NULL,	/* get_xfont_procs */
242 	NULL,	/* get_xfont_device */
243 	NULL,	/* map_rgb_alpha_color */
244 #if 0
245 	gx_page_device_get_page_device,	/* get_page_device */
246 #else
247 	NULL,   /* get_page_device */
248 #endif
249 	NULL,	/* get_alpha_bits */
250 	NULL,	/* copy_alpha */
251 	NULL,	/* get_band */
252 	NULL,	/* copy_rop */
253 	NULL,	/* fill_path */
254 	NULL,	/* stroke_path */
255 	NULL,	/* fill_mask */
256 	NULL,	/* fill_trapezoid */
257 	NULL,	/* fill_parallelogram */
258 	NULL,	/* fill_triangle */
259 	NULL,	/* draw_thin_line */
260 	NULL,	/* begin_image */
261 	NULL,	/* image_data */
262 	NULL,	/* end_image */
263 	NULL,	/* strip_tile_rectangle */
264 	NULL,	/* strip_copy_rop, */
265 	NULL,	/* get_clipping_box */
266 	NULL,	/* begin_typed_image */
267 	NULL,	/* get_bits_rectangle */
268 	NULL,	/* map_color_rgb_alpha */
269 	NULL,	/* create_compositor */
270 	NULL,	/* get_hardware_params */
271 	NULL,	/* text_begin */
272 	NULL	/* finish_copydevice */
273 };
274 
275 typedef struct pdf14_mark_device_s {
276     gx_device_common;
277 
278     pdf14_device *pdf14_dev;
279     float opacity;
280     float shape;
281     float alpha; /* alpha = opacity * shape */
282     gs_blend_mode_t blend_mode;
283 } pdf14_mark_device;
284 
285 gs_private_st_simple_final(st_pdf14_mark_device, pdf14_mark_device,
286 			   "pdf14_mark_device", gx_device_finalize);
287 
288 const pdf14_mark_device gs_pdf14_mark_device = {
289     std_device_color_stype_body(pdf14_mark_device, &pdf14_mark_procs,
290 				"pdf14_mark",
291 				&st_pdf14_mark_device,
292 				XSIZE, YSIZE, X_DPI, Y_DPI, 24, 255, 0),
293     { 0 }
294 };
295 
296 typedef struct pdf14_text_enum_s {
297     gs_text_enum_common;
298     gs_text_enum_t *target_enum;
299 } pdf14_text_enum_t;
300 extern_st(st_gs_text_enum);
301 gs_private_st_suffix_add1(st_pdf14_text_enum, pdf14_text_enum_t,
302 			  "pdf14_text_enum_t", pdf14_text_enum_enum_ptrs,
303 			  pdf14_text_enum_reloc_ptrs, st_gs_text_enum,
304 			  target_enum);
305 
306 /* ------ Private definitions ------ */
307 
308 /**
309  * pdf14_buf_new: Allocate a new PDF 1.4 buffer.
310  * @n_chan: Number of pixel channels including alpha.
311  *
312  * Return value: Newly allocated buffer, or NULL on failure.
313  **/
314 private pdf14_buf *
315 pdf14_buf_new(gs_int_rect *rect, bool has_alpha_g, bool has_shape,
316 	       int n_chan,
317 	       gs_memory_t *memory)
318 {
319     pdf14_buf *result;
320     int rowstride = (rect->q.x - rect->p.x + 3) & -4;
321     int planestride = rowstride * (rect->q.y - rect->p.y);
322     int n_planes = n_chan + (has_shape ? 1 : 0) + (has_alpha_g ? 1 : 0);
323 
324     result = gs_alloc_struct(memory, pdf14_buf, &st_pdf14_buf,
325 			     "pdf14_buf_new");
326     if (result == NULL)
327 	return result;
328 
329     result->isolated = false;
330     result->knockout = false;
331     result->has_alpha_g = has_alpha_g;
332     result->has_shape = has_shape;
333     result->rect = *rect;
334     result->n_chan = n_chan;
335     result->n_planes = n_planes;
336     result->rowstride = rowstride;
337     result->planestride = planestride;
338     result->data = gs_alloc_bytes(memory, planestride * n_planes,
339 				  "pdf14_buf_new");
340     if (result->data == NULL) {
341 	gs_free_object(memory, result, "pdf_buf_new");
342 	return NULL;
343     }
344     if (has_alpha_g) {
345 	int alpha_g_plane = n_chan + (has_shape ? 1 : 0);
346 	memset (result->data + alpha_g_plane * planestride, 0, planestride);
347     }
348     return result;
349 }
350 
351 private void
352 pdf14_buf_free(pdf14_buf *buf, gs_memory_t *memory)
353 {
354     gs_free_object(memory, buf->data, "pdf14_buf_free");
355     gs_free_object(memory, buf, "pdf14_buf_free");
356 }
357 
358 private pdf14_ctx *
359 pdf14_ctx_new(gs_int_rect *rect, int n_chan, gs_memory_t *memory)
360 {
361     pdf14_ctx *result;
362     pdf14_buf *buf;
363 
364     result = gs_alloc_struct(memory, pdf14_ctx, &st_pdf14_ctx,
365 			     "pdf14_ctx_new");
366 
367     buf = pdf14_buf_new(rect, false, false, n_chan, memory);
368     if (buf == NULL) {
369 	gs_free_object(memory, result, "pdf14_ctx_new");
370 	return NULL;
371     }
372     buf->saved = NULL;
373     result->stack = buf;
374     result->n_chan = n_chan;
375     result->memory = memory;
376     result->rect = *rect;
377     if (result == NULL)
378 	return result;
379     return result;
380 }
381 
382 private void
383 pdf14_ctx_free(pdf14_ctx *ctx)
384 {
385     pdf14_buf *buf, *next;
386 
387     for (buf = ctx->stack; buf != NULL; buf = next) {
388 	next = buf->saved;
389 	pdf14_buf_free(buf, ctx->memory);
390     }
391     gs_free_object (ctx->memory, ctx, "pdf14_ctx_free");
392 }
393 
394 /**
395  * pdf14_find_backdrop_buf: Find backdrop buffer.
396  *
397  * Return value: Backdrop buffer for current group operation, or NULL
398  * if backdrop is fully transparent.
399  **/
400 private pdf14_buf *
401 pdf14_find_backdrop_buf(pdf14_ctx *ctx)
402 {
403     pdf14_buf *buf = ctx->stack;
404 
405     while (buf != NULL) {
406 	if (buf->isolated) return NULL;
407 	if (!buf->knockout) return buf->saved;
408 	buf = buf->saved;
409     }
410     /* this really shouldn't happen, as bottom-most buf should be
411        non-knockout */
412     return NULL;
413 }
414 
415 private int
416 pdf14_push_transparency_group(pdf14_ctx *ctx, gs_int_rect *rect,
417 			      bool isolated, bool knockout,
418 			      byte alpha, byte shape,
419 			      gs_blend_mode_t blend_mode)
420 {
421     pdf14_buf *tos = ctx->stack;
422     pdf14_buf *buf, *backdrop;
423     bool has_shape;
424 
425     /* todo: fix this hack, which makes all knockout groups isolated.
426        For the vast majority of files, there won't be any visible
427        effects, but it still isn't correct. The pixel compositing code
428        for non-isolated knockout groups gets pretty hairy, which is
429        why this is here. */
430     if (knockout) isolated = true;
431 
432     has_shape = tos->has_shape || tos->knockout;
433 
434     buf = pdf14_buf_new(rect, !isolated, has_shape, ctx->n_chan, ctx->memory);
435     if (buf == NULL)
436 	return_error(gs_error_VMerror);
437     buf->isolated = isolated;
438     buf->knockout = knockout;
439     buf->alpha = alpha;
440     buf->shape = shape;
441     buf->blend_mode = blend_mode;
442 
443     buf->saved = tos;
444     ctx->stack = buf;
445 
446     backdrop = pdf14_find_backdrop_buf(ctx);
447     if (backdrop == NULL) {
448 	memset(buf->data, 0, buf->planestride * (buf->n_chan +
449 						 (buf->has_shape ? 1 : 0)));
450     } else {
451 	/* make copy of backdrop for compositing */
452 	byte *buf_plane = buf->data;
453 	byte *tos_plane = tos->data + buf->rect.p.x - tos->rect.p.x +
454 	    (buf->rect.p.y - tos->rect.p.y) * tos->rowstride;
455 	int width = buf->rect.q.x - buf->rect.p.x;
456 	int y0 = buf->rect.p.y;
457 	int y1 = buf->rect.q.y;
458 	int i;
459 	int n_chan_copy = buf->n_chan + (tos->has_shape ? 1 : 0);
460 
461 	for (i = 0; i < n_chan_copy; i++) {
462 	    byte *buf_ptr = buf_plane;
463 	    byte *tos_ptr = tos_plane;
464 	    int y;
465 
466 	    for (y = y0; y < y1; ++y) {
467 		memcpy (buf_ptr, tos_ptr, width);
468 		buf_ptr += buf->rowstride;
469 		tos_ptr += tos->rowstride;
470 	    }
471 	    buf_plane += buf->planestride;
472 	    tos_plane += tos->planestride;
473 	}
474 	if (has_shape && !tos->has_shape)
475 	    memset (buf_plane, 0, buf->planestride);
476     }
477 
478     return 0;
479 }
480 
481 private int
482 pdf14_pop_transparency_group(pdf14_ctx *ctx)
483 {
484     pdf14_buf *tos = ctx->stack;
485     pdf14_buf *nos = tos->saved;
486     int n_chan = ctx->n_chan;
487     byte alpha = tos->alpha;
488     byte shape = tos->shape;
489     byte blend_mode = tos->blend_mode;
490     int x0 = tos->rect.p.x;
491     int y0 = tos->rect.p.y;
492     int x1 = tos->rect.q.x;
493     int y1 = tos->rect.q.y;
494     byte *tos_ptr = tos->data;
495     byte *nos_ptr = nos->data + x0 - nos->rect.p.x +
496 	(y0 - nos->rect.p.y) * nos->rowstride;
497     int tos_planestride = tos->planestride;
498     int nos_planestride = nos->planestride;
499     int width = x1 - x0;
500     int x, y;
501     int i;
502     byte tos_pixel[PDF14_MAX_PLANES];
503     byte nos_pixel[PDF14_MAX_PLANES];
504     bool tos_isolated = tos->isolated;
505     bool nos_knockout = nos->knockout;
506     byte *nos_alpha_g_ptr;
507     int tos_shape_offset = n_chan * tos_planestride;
508     int tos_alpha_g_offset = tos_shape_offset +
509 	(tos->has_shape ? tos_planestride : 0);
510     int nos_shape_offset = n_chan * nos_planestride;
511     bool nos_has_shape = nos->has_shape;
512 
513     if (nos == NULL)
514 	return_error(gs_error_rangecheck);
515 
516     /* for now, only simple non-knockout */
517 
518     if (nos->has_alpha_g)
519 	nos_alpha_g_ptr = nos_ptr + n_chan * nos_planestride;
520     else
521 	nos_alpha_g_ptr = NULL;
522 
523     for (y = y0; y < y1; ++y) {
524 	for (x = 0; x < width; ++x) {
525 	    for (i = 0; i < n_chan; ++i) {
526 		tos_pixel[i] = tos_ptr[x + i * tos_planestride];
527 		nos_pixel[i] = nos_ptr[x + i * nos_planestride];
528 	    }
529 
530 	    if (nos_knockout) {
531 		byte *nos_shape_ptr = nos_has_shape ?
532 		    &nos_ptr[x + nos_shape_offset] : NULL;
533 		byte tos_shape = tos_ptr[x + tos_shape_offset];
534 
535 #if 1
536 		art_pdf_composite_knockout_isolated_8 (nos_pixel,
537 						       nos_shape_ptr,
538 						       tos_pixel,
539 						       n_chan - 1,
540 						       tos_shape,
541 						       alpha, shape);
542 #else
543 		tos_pixel[3] = tos_ptr[x + tos_shape_offset];
544 		art_pdf_composite_group_8(nos_pixel, nos_alpha_g_ptr,
545 					  tos_pixel,
546 					  n_chan - 1,
547 					  alpha, blend_mode);
548 #endif
549 	    } else if (tos_isolated) {
550 		art_pdf_composite_group_8(nos_pixel, nos_alpha_g_ptr,
551 					  tos_pixel,
552 					  n_chan - 1,
553 					  alpha, blend_mode);
554 	    } else {
555 		byte tos_alpha_g = tos_ptr[x + tos_alpha_g_offset];
556 		art_pdf_recomposite_group_8(nos_pixel, nos_alpha_g_ptr,
557 					    tos_pixel, tos_alpha_g,
558 					    n_chan - 1,
559 					    alpha, blend_mode);
560 	    }
561 	    if (nos_has_shape) {
562 		nos_ptr[x + nos_shape_offset] =
563 		    art_pdf_union_mul_8 (nos_ptr[x + nos_shape_offset],
564 					 tos_ptr[x + tos_shape_offset],
565 					 shape);
566 	    }
567 	    /* todo: knockout cases */
568 
569 	    for (i = 0; i < n_chan; ++i) {
570 		nos_ptr[x + i * nos_planestride] = nos_pixel[i];
571 	    }
572 	    if (nos_alpha_g_ptr != NULL)
573 		++nos_alpha_g_ptr;
574 	}
575 	tos_ptr += tos->rowstride;
576 	nos_ptr += nos->rowstride;
577 	if (nos_alpha_g_ptr != NULL)
578 	    nos_alpha_g_ptr += nos->rowstride - width;
579     }
580 
581     ctx->stack = nos;
582     pdf14_buf_free(tos, ctx->memory);
583     return 0;
584 }
585 
586 private int
587 pdf14_open(gx_device *dev)
588 {
589     pdf14_device *pdev = (pdf14_device *)dev;
590     gs_int_rect rect;
591 
592     if_debug2('v', "[v]pdf14_open: width = %d, height = %d\n",
593 	     dev->width, dev->height);
594 
595     rect.p.x = 0;
596     rect.p.y = 0;
597     rect.q.x = dev->width;
598     rect.q.y = dev->height;
599     pdev->ctx = pdf14_ctx_new(&rect, 4, dev->memory);
600     if (pdev->ctx == NULL)
601 	return_error(gs_error_VMerror);
602 
603     return 0;
604 }
605 
606 /**
607  * pdf14_put_image: Put rendered image to target device.
608  * @pdev: The PDF 1.4 rendering device.
609  * @pgs: State for image draw operation.
610  * @target: The target device.
611  *
612  * Puts the rendered image in @pdev's buffer to @target. This is called
613  * as part of the sequence of popping the PDF 1.4 device filter.
614  *
615  * Return code: negative on error.
616  **/
617 private int
618 pdf14_put_image(pdf14_device *pdev, gs_state *pgs, gx_device *target)
619 {
620     int code;
621     gs_image1_t image;
622     gs_matrix pmat;
623     gx_image_enum_common_t *info;
624     int width = pdev->width;
625     int height = pdev->height;
626     gs_imager_state *pis = (gs_imager_state *)pgs;
627     int y;
628     pdf14_buf *buf = pdev->ctx->stack;
629 
630     int planestride = buf->planestride;
631     byte *buf_ptr = buf->data;
632     byte *linebuf;
633 
634 #ifdef TEST_CODE
635     code = dev_proc(target, fill_rectangle) (target, 10, 10, 100, 100, 0);
636 #endif
637 
638     gx_set_dev_color(pgs);
639     gs_image_t_init_adjust(&image, pis->shared->device_color_spaces.named.RGB,
640 			   false);
641     image.ImageMatrix.xx = width;
642     image.ImageMatrix.yy = height;
643     image.Width = width;
644     image.Height = height;
645     image.BitsPerComponent = 8;
646     pmat.xx = width;
647     pmat.xy = 0;
648     pmat.yx = 0;
649     pmat.yy = height;
650     pmat.tx = 0;
651     pmat.ty = 0;
652     code = dev_proc(target, begin_typed_image) (target,
653 						pis, &pmat,
654 						(gs_image_common_t *)&image,
655 						NULL, NULL, NULL,
656 						pgs->memory, &info);
657     if (code < 0)
658 	return code;
659 
660     linebuf = gs_alloc_bytes(pdev->memory, width * 3, "pdf14_put_image");
661     for (y = 0; y < height; y++) {
662 	gx_image_plane_t planes;
663 	int x;
664 	int rows_used;
665 
666 	for (x = 0; x < width; x++) {
667 	    const byte bg_r = 0xff, bg_g = 0xff, bg_b = 0xff;
668 	    byte r, g, b, a;
669 	    int tmp;
670 
671 	    /* composite RGBA pixel with over solid background */
672 	    a = buf_ptr[x + planestride * 3];
673 
674 	    if ((a + 1) & 0xfe) {
675 		r = buf_ptr[x];
676 		g = buf_ptr[x + planestride];
677 		b = buf_ptr[x + planestride * 2];
678 		a ^= 0xff;
679 
680 		tmp = ((bg_r - r) * a) + 0x80;
681 		r += (tmp + (tmp >> 8)) >> 8;
682 		linebuf[x * 3] = r;
683 
684 		tmp = ((bg_g - g) * a) + 0x80;
685 		g += (tmp + (tmp >> 8)) >> 8;
686 		linebuf[x * 3 + 1] = g;
687 
688 		tmp = ((bg_g - b) * a) + 0x80;
689 		b += (tmp + (tmp >> 8)) >> 8;
690 		linebuf[x * 3 + 2] = b;
691 	    } else if (a == 0) {
692 		linebuf[x * 3] = bg_r;
693 		linebuf[x * 3 + 1] = bg_g;
694 		linebuf[x * 3 + 2] = bg_b;
695 	    } else {
696 		r = buf_ptr[x];
697 		g = buf_ptr[x + planestride];
698 		b = buf_ptr[x + planestride * 2];
699 		linebuf[x * 3 + 0] = r;
700 		linebuf[x * 3 + 1] = g;
701 		linebuf[x * 3 + 2] = b;
702 	    }
703 	}
704 
705 	planes.data = linebuf;
706 	planes.data_x = 0;
707 	planes.raster = width * 3;
708 	info->procs->plane_data(info, &planes, 1, &rows_used);
709 	/* todo: check return value */
710 
711 	buf_ptr += buf->rowstride;
712     }
713     gs_free_object(pdev->memory, linebuf, "pdf14_put_image");
714 
715     info->procs->end_image(info, true);
716     return code;
717 }
718 
719 private int
720 pdf14_close(gx_device *dev)
721 {
722     pdf14_device *pdev = (pdf14_device *)dev;
723 
724     if (pdev->ctx)
725 	pdf14_ctx_free(pdev->ctx);
726     return 0;
727 }
728 
729 private int
730 pdf14_output_page(gx_device *dev, int num_copies, int flush)
731 {
732     /* todo: actually forward page */
733 
734     /* Hmm, actually I think the filter should be popped before the
735        output_page operation. In that case, this should probably
736        rangecheck. */
737     return 0;
738 }
739 
740 private void
741 pdf14_finalize(gx_device *dev)
742 {
743     if_debug1('v', "[v]finalizing %lx\n", dev);
744 }
745 
746 #define COPY_PARAM(p) dev->p = target->p
747 #define COPY_ARRAY_PARAM(p) memcpy(dev->p, target->p, sizeof(dev->p))
748 
749 /*
750  * Copy device parameters back from a target.  This copies all standard
751  * parameters related to page size and resolution, but not any of the
752  * color-related parameters, as the pdf14 device retains its own color
753  * handling. This routine is parallel to gx_device_copy_params().
754  */
755 private void
756 gs_pdf14_device_copy_params(gx_device *dev, const gx_device *target)
757 {
758 	COPY_PARAM(width);
759 	COPY_PARAM(height);
760 	COPY_ARRAY_PARAM(MediaSize);
761 	COPY_ARRAY_PARAM(ImagingBBox);
762 	COPY_PARAM(ImagingBBox_set);
763 	COPY_ARRAY_PARAM(HWResolution);
764 	COPY_ARRAY_PARAM(MarginsHWResolution);
765 	COPY_ARRAY_PARAM(Margins);
766 	COPY_ARRAY_PARAM(HWMargins);
767 	COPY_PARAM(PageCount);
768 #undef COPY_ARRAY_PARAM
769 #undef COPY_PARAM
770 }
771 
772 /**
773  * pdf14_get_marking_device: Obtain a marking device.
774  * @dev: Original device.
775  * @pis: Imager state.
776  *
777  * The current implementation creates a marking device each time this
778  * routine is called. A potential optimization is to cache a single
779  * instance in the original device.
780  *
781  * Return value: Marking device, or NULL on error.
782  **/
783 private gx_device *
784 pdf14_get_marking_device(gx_device *dev, const gs_imager_state *pis)
785 {
786     pdf14_device *pdev = (pdf14_device *)dev;
787     pdf14_buf *buf = pdev->ctx->stack;
788     pdf14_mark_device *mdev;
789     int code = gs_copydevice((gx_device **)&mdev,
790 			     (const gx_device *)&gs_pdf14_mark_device,
791 			     dev->memory);
792 
793     if (code < 0)
794 	return NULL;
795 
796     gx_device_fill_in_procs((gx_device *)mdev);
797     mdev->pdf14_dev = pdev;
798     mdev->opacity = pis->opacity.alpha;
799     mdev->shape = pis->shape.alpha;
800     mdev->alpha = pis->opacity.alpha * pis->shape.alpha;
801     mdev->blend_mode = pis->blend_mode;
802 
803     if (buf->knockout) {
804 	fill_dev_proc((gx_device *)mdev, fill_rectangle,
805 		      pdf14_mark_fill_rectangle_ko_simple);
806     } else {
807 	fill_dev_proc((gx_device *)mdev, fill_rectangle,
808 		      pdf14_mark_fill_rectangle);
809     }
810 
811     if_debug1('v', "[v]creating %lx\n", mdev);
812     gs_pdf14_device_copy_params((gx_device *)mdev, dev);
813     mdev->finalize = pdf14_finalize;
814     return (gx_device *)mdev;
815 }
816 
817 private void
818 pdf14_release_marking_device(gx_device *marking_dev)
819 {
820     rc_decrement_only(marking_dev, "pdf14_release_marking_device");
821 }
822 
823 private int
824 pdf14_fill_path(gx_device *dev, const gs_imager_state *pis,
825 			   gx_path *ppath, const gx_fill_params *params,
826 			   const gx_drawing_color *pdcolor,
827 			   const gx_clip_path *pcpath)
828 {
829     int code;
830     gx_device *mdev = pdf14_get_marking_device(dev, pis);
831     gs_imager_state new_is = *pis;
832 
833     if (mdev == 0)
834 	return_error(gs_error_VMerror);
835     new_is.log_op |= lop_pdf14;
836     code = gx_default_fill_path(mdev, &new_is, ppath, params, pdcolor, pcpath);
837     pdf14_release_marking_device(mdev);
838     return code;
839 }
840 
841 private int
842 pdf14_stroke_path(gx_device *dev, const gs_imager_state *pis,
843 			     gx_path *ppath, const gx_stroke_params *params,
844 			     const gx_drawing_color *pdcolor,
845 			     const gx_clip_path *pcpath)
846 {
847     int code;
848     gx_device *mdev = pdf14_get_marking_device(dev, pis);
849     gs_imager_state new_is = *pis;
850 
851     if (mdev == 0)
852 	return_error(gs_error_VMerror);
853     new_is.log_op |= lop_pdf14;
854     code = gx_default_stroke_path(mdev, &new_is, ppath, params, pdcolor,
855 				  pcpath);
856     pdf14_release_marking_device(mdev);
857     return code;
858 }
859 
860 private int
861 pdf14_begin_typed_image(gx_device * dev, const gs_imager_state * pis,
862 			   const gs_matrix *pmat, const gs_image_common_t *pic,
863 			   const gs_int_rect * prect,
864 			   const gx_drawing_color * pdcolor,
865 			   const gx_clip_path * pcpath, gs_memory_t * mem,
866 			   gx_image_enum_common_t ** pinfo)
867 {
868     gx_device *mdev = pdf14_get_marking_device(dev, pis);
869     int code;
870 
871     if (mdev == 0)
872 	return_error(gs_error_VMerror);
873 
874     code = gx_default_begin_typed_image(mdev, pis, pmat, pic, prect, pdcolor,
875 					pcpath, mem, pinfo);
876 
877     /* We need to free the marking device on end of image. This probably
878        means implementing our own image enum, which primarily forwards
879        requests, but also frees the marking device on end_image. For
880        now, we'll just leak this - it will get cleaned up by the GC. */
881 #if 0
882     pdf14_release_marking_device(mdev);
883 #endif
884 
885     return code;
886 }
887 
888 private int
889 pdf14_text_resync(gs_text_enum_t *pte, const gs_text_enum_t *pfrom)
890 {
891     pdf14_text_enum_t *const penum = (pdf14_text_enum_t *)pte;
892 
893     if ((pte->text.operation ^ pfrom->text.operation) & ~TEXT_FROM_ANY)
894 	return_error(gs_error_rangecheck);
895     if (penum->target_enum) {
896 	int code = gs_text_resync(penum->target_enum, pfrom);
897 
898 	if (code < 0)
899 	    return code;
900     }
901     pte->text = pfrom->text;
902     gs_text_enum_copy_dynamic(pte, pfrom, false);
903     return 0;
904 }
905 
906 private int
907 pdf14_text_process(gs_text_enum_t *pte)
908 {
909     pdf14_text_enum_t *const penum = (pdf14_text_enum_t *)pte;
910     int code;
911 
912     code = gs_text_process(penum->target_enum);
913     gs_text_enum_copy_dynamic(pte, penum->target_enum, true);
914     return code;
915 }
916 
917 private bool
918 pdf14_text_is_width_only(const gs_text_enum_t *pte)
919 {
920     const pdf14_text_enum_t *const penum = (const pdf14_text_enum_t *)pte;
921 
922     if (penum->target_enum)
923 	return gs_text_is_width_only(penum->target_enum);
924     return false;
925 }
926 
927 private int
928 pdf14_text_current_width(const gs_text_enum_t *pte, gs_point *pwidth)
929 {
930     const pdf14_text_enum_t *const penum = (const pdf14_text_enum_t *)pte;
931 
932     if (penum->target_enum)
933 	return gs_text_current_width(penum->target_enum, pwidth);
934     return_error(gs_error_rangecheck); /* can't happen */
935 }
936 
937 private int
938 pdf14_text_set_cache(gs_text_enum_t *pte, const double *pw,
939 		   gs_text_cache_control_t control)
940 {
941     pdf14_text_enum_t *const penum = (pdf14_text_enum_t *)pte;
942 
943     if (penum->target_enum)
944 	return gs_text_set_cache(penum->target_enum, pw, control);
945     return_error(gs_error_rangecheck); /* can't happen */
946 }
947 
948 private int
949 pdf14_text_retry(gs_text_enum_t *pte)
950 {
951     pdf14_text_enum_t *const penum = (pdf14_text_enum_t *)pte;
952 
953     if (penum->target_enum)
954 	return gs_text_retry(penum->target_enum);
955     return_error(gs_error_rangecheck); /* can't happen */
956 }
957 
958 private void
959 pdf14_text_release(gs_text_enum_t *pte, client_name_t cname)
960 {
961     pdf14_text_enum_t *const penum = (pdf14_text_enum_t *)pte;
962 
963     if (penum->target_enum) {
964 	gs_text_release(penum->target_enum, cname);
965 	penum->target_enum = 0;
966     }
967     gx_default_text_release(pte, cname);
968 }
969 
970 private const gs_text_enum_procs_t pdf14_text_procs = {
971     pdf14_text_resync, pdf14_text_process,
972     pdf14_text_is_width_only, pdf14_text_current_width,
973     pdf14_text_set_cache, pdf14_text_retry,
974     pdf14_text_release
975 };
976 
977 private int
978 pdf14_text_begin(gx_device * dev, gs_imager_state * pis,
979 		 const gs_text_params_t * text, gs_font * font,
980 		 gx_path * path, const gx_device_color * pdcolor,
981 		 const gx_clip_path * pcpath, gs_memory_t * memory,
982 		 gs_text_enum_t ** ppenum)
983 {
984     int code;
985     gx_device *mdev = pdf14_get_marking_device(dev, pis);
986     pdf14_text_enum_t *penum;
987     gs_text_enum_t *target_enum;
988 
989     if (mdev == 0)
990 	return_error(gs_error_VMerror);
991     if_debug0('v', "[v]pdf14_text_begin\n");
992     code = gx_default_text_begin(mdev, pis, text, font, path, pdcolor, pcpath,
993 				 memory, &target_enum);
994 
995     rc_alloc_struct_1(penum, pdf14_text_enum_t, &st_pdf14_text_enum, memory,
996 		      return_error(gs_error_VMerror), "pdf14_text_begin");
997     penum->rc.free = rc_free_text_enum;
998     penum->target_enum = target_enum;
999     code = gs_text_enum_init((gs_text_enum_t *)penum, &pdf14_text_procs,
1000 			     dev, pis, text, font, path, pdcolor, pcpath,
1001 			     memory);
1002     if (code < 0) {
1003 	gs_free_object(memory, penum, "pdf14_text_begin");
1004 	return code;
1005     }
1006     *ppenum = (gs_text_enum_t *)penum;
1007     rc_decrement_only(mdev, "pdf14_text_begin");
1008     return code;
1009 }
1010 
1011 private int
1012 pdf14_fill_rectangle(gx_device * dev,
1013 		    int x, int y, int w, int h, gx_color_index color)
1014 {
1015     if_debug4('v', "[v]pdf14_fill_rectangle, (%d, %d), %d x %d\n", x, y, w, h);
1016     return 0;
1017 }
1018 
1019 
1020 private int
1021 pdf14_begin_transparency_group(gx_device *dev,
1022 			      const gs_transparency_group_params_t *ptgp,
1023 			      const gs_rect *pbbox,
1024 			      gs_imager_state *pis,
1025 			      gs_transparency_state_t **ppts,
1026 			      gs_memory_t *mem)
1027 {
1028     pdf14_device *pdev = (pdf14_device *)dev;
1029     double alpha = pis->opacity.alpha * pis->shape.alpha;
1030     int code;
1031 
1032     if_debug4('v', "[v]begin_transparency_group, I = %d, K = %d, alpha = %g, bm = %d\n",
1033 	      ptgp->Isolated, ptgp->Knockout, alpha, pis->blend_mode);
1034 
1035     code = pdf14_push_transparency_group(pdev->ctx, &pdev->ctx->rect,
1036 					 ptgp->Isolated, ptgp->Knockout,
1037 					 floor (255 * alpha + 0.5),
1038 					 floor (255 * pis->shape.alpha + 0.5),
1039 					 pis->blend_mode);
1040     return code;
1041 }
1042 
1043 private int
1044 pdf14_end_transparency_group(gx_device *dev,
1045 			      gs_imager_state *pis,
1046 			      gs_transparency_state_t **ppts)
1047 {
1048     pdf14_device *pdev = (pdf14_device *)dev;
1049     int code;
1050 
1051     if_debug0('v', "[v]end_transparency_group\n");
1052     code = pdf14_pop_transparency_group(pdev->ctx);
1053     return code;
1054 }
1055 
1056 private int
1057 pdf14_mark_fill_rectangle(gx_device * dev,
1058 			 int x, int y, int w, int h, gx_color_index color)
1059 {
1060     pdf14_mark_device *mdev = (pdf14_mark_device *)dev;
1061     pdf14_device *pdev = (pdf14_device *)mdev->pdf14_dev;
1062     pdf14_buf *buf = pdev->ctx->stack;
1063     int i, j, k;
1064     byte *line, *dst_ptr;
1065     byte src[PDF14_MAX_PLANES];
1066     byte dst[PDF14_MAX_PLANES];
1067     gs_blend_mode_t blend_mode = mdev->blend_mode;
1068     int rowstride = buf->rowstride;
1069     int planestride = buf->planestride;
1070     bool has_alpha_g = buf->has_alpha_g;
1071     bool has_shape = buf->has_shape;
1072     int shape_off = buf->n_chan * planestride;
1073     int alpha_g_off = shape_off + (has_shape ? planestride : 0);
1074     byte shape;
1075 
1076     src[0] = color >> 16;
1077     src[1] = (color >> 8) & 0xff;
1078     src[2] = color & 0xff;
1079     src[3] = floor (255 * mdev->alpha + 0.5);
1080     if (has_shape)
1081 	shape = floor (255 * mdev->shape + 0.5);
1082 
1083     if (x < buf->rect.p.x) x = buf->rect.p.x;
1084     if (y < buf->rect.p.x) y = buf->rect.p.y;
1085     if (x + w > buf->rect.q.x) w = buf->rect.q.x - x;
1086     if (y + h > buf->rect.q.y) h = buf->rect.q.y - y;
1087 
1088     line = buf->data + (x - buf->rect.p.x) + (y - buf->rect.p.y) * rowstride;
1089 
1090     for (j = 0; j < h; ++j) {
1091 	dst_ptr = line;
1092 	for (i = 0; i < w; ++i) {
1093 	    for (k = 0; k < 4; ++k)
1094 		dst[k] = dst_ptr[k * planestride];
1095 	    art_pdf_composite_pixel_alpha_8(dst, src, 3, blend_mode);
1096 	    for (k = 0; k < 4; ++k)
1097 		dst_ptr[k * planestride] = dst[k];
1098 	    if (has_alpha_g) {
1099 		int tmp = (255 - dst_ptr[alpha_g_off]) * (255 - src[3]) + 0x80;
1100 		dst_ptr[alpha_g_off] = 255 - ((tmp + (tmp >> 8)) >> 8);
1101 	    }
1102 	    if (has_shape) {
1103 		int tmp = (255 - dst_ptr[shape_off]) * (255 - shape) + 0x80;
1104 		dst_ptr[shape_off] = 255 - ((tmp + (tmp >> 8)) >> 8);
1105 	    }
1106 	    ++dst_ptr;
1107 	}
1108 	line += rowstride;
1109     }
1110     return 0;
1111 }
1112 
1113 private int
1114 pdf14_mark_fill_rectangle_ko_simple(gx_device * dev,
1115 				   int x, int y, int w, int h, gx_color_index color)
1116 {
1117     pdf14_mark_device *mdev = (pdf14_mark_device *)dev;
1118     pdf14_device *pdev = (pdf14_device *)mdev->pdf14_dev;
1119     pdf14_buf *buf = pdev->ctx->stack;
1120     int i, j, k;
1121     byte *line, *dst_ptr;
1122     byte src[PDF14_MAX_PLANES];
1123     byte dst[PDF14_MAX_PLANES];
1124     int rowstride = buf->rowstride;
1125     int planestride = buf->planestride;
1126     int shape_off = buf->n_chan * planestride;
1127     bool has_shape = buf->has_shape;
1128     byte opacity;
1129 
1130     src[0] = color >> 16;
1131     src[1] = (color >> 8) & 0xff;
1132     src[2] = color & 0xff;
1133     src[3] = floor (255 * mdev->shape + 0.5);
1134     opacity = floor (255 * mdev->opacity + 0.5);
1135 
1136     if (x < buf->rect.p.x) x = buf->rect.p.x;
1137     if (y < buf->rect.p.x) y = buf->rect.p.y;
1138     if (x + w > buf->rect.q.x) w = buf->rect.q.x - x;
1139     if (y + h > buf->rect.q.y) h = buf->rect.q.y - y;
1140 
1141     line = buf->data + (x - buf->rect.p.x) + (y - buf->rect.p.y) * rowstride;
1142 
1143     for (j = 0; j < h; ++j) {
1144 	dst_ptr = line;
1145 	for (i = 0; i < w; ++i) {
1146 	    for (k = 0; k < 4; ++k)
1147 		dst[k] = dst_ptr[k * planestride];
1148 	    art_pdf_composite_knockout_simple_8(dst, has_shape ? dst_ptr + shape_off : NULL,
1149 						src, 3, opacity);
1150 	    for (k = 0; k < 4; ++k)
1151 		dst_ptr[k * planestride] = dst[k];
1152 	    ++dst_ptr;
1153 	}
1154 	line += rowstride;
1155     }
1156     return 0;
1157 }
1158 
1159 private int
1160 gs_pdf14_device_filter_push(gs_device_filter_t *self, gs_memory_t *mem,
1161 			   gx_device **pdev, gx_device *target)
1162 {
1163     pdf14_device *p14dev;
1164     int code;
1165 
1166     code = gs_copydevice((gx_device **) &p14dev,
1167 			 (const gx_device *) &gs_pdf14_device,
1168 			 mem);
1169     if (code < 0)
1170 	return code;
1171 
1172     gx_device_fill_in_procs((gx_device *)p14dev);
1173 
1174     gs_pdf14_device_copy_params((gx_device *)p14dev, target);
1175 
1176     rc_assign(p14dev->target, target, "gs_pdf14_device_filter_push");
1177 
1178     dev_proc((gx_device *) p14dev, open_device) ((gx_device *) p14dev);
1179     *pdev = (gx_device *) p14dev;
1180     return 0;
1181 }
1182 
1183 private int
1184 gs_pdf14_device_filter_pop(gs_device_filter_t *self, gs_memory_t *mem,
1185 			   gs_state *pgs, gx_device *dev)
1186 {
1187     gx_device *target = ((pdf14_device *)dev)->target;
1188     int code;
1189 
1190     code = pdf14_put_image((pdf14_device *)dev, pgs, target);
1191     if (code < 0)
1192 	return code;
1193 
1194     code = dev_proc(dev, close_device) (dev);
1195     if (code < 0)
1196 	return code;
1197 
1198     ((pdf14_device *)dev)->target = 0;
1199     rc_decrement_only(target, "gs_pdf14_device_filter_pop");
1200 
1201     gs_free_object(mem, self, "gs_pdf14_device_filter_pop");
1202     return 0;
1203 }
1204 
1205 int
1206 gs_pdf14_device_filter(gs_device_filter_t **pdf, int depth, gs_memory_t *mem)
1207 {
1208     gs_device_filter_t *df;
1209 
1210     df = gs_alloc_struct(mem, gs_device_filter_t,
1211 			 &st_gs_device_filter, "gs_pdf14_device_filter");
1212     if (df == 0)
1213 	return_error(gs_error_VMerror);
1214     df->push = gs_pdf14_device_filter_push;
1215     df->pop = gs_pdf14_device_filter_pop;
1216     *pdf = df;
1217     return 0;
1218 }
1219