xref: /plan9/sys/src/cmd/gs/src/gdevpnga.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /*
2   Copyright (C) 2001-2004 artofcode LLC. All rights reserved.
3 
4   This software is provided AS-IS with no warranty, either express or
5   implied.
6 
7   This software is distributed under license and may not be copied,
8   modified or distributed except as expressly authorized under the terms
9   of the license contained in the file LICENSE in this distribution.
10 
11   For more information about licensing, please refer to
12   http://www.ghostscript.com/licensing/. For information on
13   commercial licensing, go to http://www.artifex.com/licensing/ or
14   contact Artifex Software, Inc., 101 Lucas Valley Road #110,
15   San Rafael, CA  94903, U.S.A., +1(415)492-9861.
16 
17   Author: Raph Levien <raph@artofcode.com>
18 */
19 
20 /* $Id: gdevpnga.c,v 1.9 2004/05/26 04:10:58 dan Exp $ */
21 /* Test driver for PDF 1.4 transparency stuff */
22 
23 #include "gdevprn.h"
24 #include "gdevpccm.h"
25 #include "gscdefs.h"
26 #include "gsdevice.h"
27 #include "gdevmem.h"
28 #include "gxblend.h"
29 #include "gxtext.h"
30 
31 # define INCR(v) DO_NOTHING
32 
33 #define PNG_INTERNAL
34 /*
35  * libpng versions 1.0.3 and later allow disabling access to the stdxxx
36  * files while retaining support for FILE * I/O.
37  */
38 #define PNG_NO_CONSOLE_IO
39 /*
40  * Earlier libpng versions require disabling FILE * I/O altogether.
41  * This produces a compiler warning about no prototype for png_init_io.
42  * The right thing will happen at link time, since the library itself
43  * is compiled with stdio support.  Unfortunately, we can't do this
44  * conditionally depending on PNG_LIBPNG_VER, because this is defined
45  * in png.h.
46  */
47 /*#define PNG_NO_STDIO*/
48 #include "png_.h"
49 
50 /* Buffer stack data structure */
51 
52 #define PDF14_MAX_PLANES 16
53 
54 typedef struct pdf14_buf_s pdf14_buf;
55 typedef struct pdf14_ctx_s pdf14_ctx;
56 
57 struct pdf14_buf_s {
58     pdf14_buf *saved;
59 
60     bool isolated;
61     bool knockout;
62     byte alpha;
63     byte shape;
64     gs_blend_mode_t blend_mode;
65 
66     bool has_alpha_g;
67     bool has_shape;
68 
69     gs_int_rect rect;
70     /* Note: the traditional GS name for rowstride is "raster" */
71 
72     /* Data is stored in planar format. Order of planes is: pixel values,
73        alpha, shape if present, alpha_g if present. */
74 
75     int rowstride;
76     int planestride;
77     int n_chan; /* number of pixel planes including alpha */
78     int n_planes; /* total number of planes including alpha, shape, alpha_g */
79     byte *data;
80 };
81 
82 struct pdf14_ctx_s {
83     pdf14_buf *stack;
84     gs_memory_t *memory;
85     gs_int_rect rect;
86     int n_chan;
87 };
88 
89 /* GC procedures for buffer stack */
90 
91 private
92 ENUM_PTRS_WITH(pdf14_buf_enum_ptrs, pdf14_buf *buf)
93     return 0;
94     case 0: return ENUM_OBJ(buf->saved);
95     case 1: return ENUM_OBJ(buf->data);
96 ENUM_PTRS_END
97 
98 private
RELOC_PTRS_WITH(pdf14_buf_reloc_ptrs,pdf14_buf * buf)99 RELOC_PTRS_WITH(pdf14_buf_reloc_ptrs, pdf14_buf *buf)
100 {
101     RELOC_VAR(buf->saved);
102     RELOC_VAR(buf->data);
103 }
104 RELOC_PTRS_END
105 
106 gs_private_st_composite(st_pdf14_buf, pdf14_buf, "pdf14_buf",
107 			pdf14_buf_enum_ptrs, pdf14_buf_reloc_ptrs);
108 
109 gs_private_st_ptrs1(st_pdf14_ctx, pdf14_ctx, "pdf14_ctx",
110 		    pdf14_ctx_enum_ptrs, pdf14_ctx_reloc_ptrs,
111 		    stack);
112 
113 /* ------ The device descriptors ------ */
114 
115 /*
116  * Default X and Y resolution.
117  */
118 #define X_DPI 72
119 #define Y_DPI 72
120 
121 private int pnga_open(gx_device * pdev);
122 private dev_proc_close_device(pnga_close);
123 private int pnga_output_page(gx_device * pdev, int num_copies, int flush);
124 private dev_proc_fill_rectangle(pnga_fill_rectangle);
125 private dev_proc_fill_path(pnga_fill_path);
126 private dev_proc_stroke_path(pnga_stroke_path);
127 private dev_proc_begin_typed_image(pnga_begin_typed_image);
128 private dev_proc_text_begin(pnga_text_begin);
129 private dev_proc_begin_transparency_group(pnga_begin_transparency_group);
130 private dev_proc_end_transparency_group(pnga_end_transparency_group);
131 
132 #define XSIZE (8.5 * X_DPI)	/* 8.5 x 11 inch page, by default */
133 #define YSIZE (11 * Y_DPI)
134 
135 /* 24-bit color. */
136 
137 private const gx_device_procs pnga_procs =
138 {
139 	pnga_open,			/* open */
140 	NULL,	/* get_initial_matrix */
141 	NULL,	/* sync_output */
142 	pnga_output_page,		/* output_page */
143 	pnga_close,			/* close */
144 	gx_default_rgb_map_rgb_color,
145 	gx_default_rgb_map_color_rgb,
146 	pnga_fill_rectangle,	/* fill_rectangle */
147 	NULL,	/* tile_rectangle */
148 	NULL,	/* copy_mono */
149 	NULL,	/* copy_color */
150 	NULL,	/* draw_line */
151 	NULL,	/* get_bits */
152 	NULL,   /* get_params */
153 	NULL,   /* put_params */
154 	NULL,	/* map_cmyk_color */
155 	NULL,	/* get_xfont_procs */
156 	NULL,	/* get_xfont_device */
157 	NULL,	/* map_rgb_alpha_color */
158 #if 0
159 	gx_page_device_get_page_device,	/* get_page_device */
160 #else
161 	NULL,   /* get_page_device */
162 #endif
163 	NULL,	/* get_alpha_bits */
164 	NULL,	/* copy_alpha */
165 	NULL,	/* get_band */
166 	NULL,	/* copy_rop */
167 	pnga_fill_path,		/* fill_path */
168 	pnga_stroke_path,	/* stroke_path */
169 	NULL,	/* fill_mask */
170 	NULL,	/* fill_trapezoid */
171 	NULL,	/* fill_parallelogram */
172 	NULL,	/* fill_triangle */
173 	NULL,	/* draw_thin_line */
174 	NULL,	/* begin_image */
175 	NULL,	/* image_data */
176 	NULL,	/* end_image */
177 	NULL,	/* strip_tile_rectangle */
178 	NULL,	/* strip_copy_rop, */
179 	NULL,	/* get_clipping_box */
180 	pnga_begin_typed_image,	/* begin_typed_image */
181 	NULL,	/* get_bits_rectangle */
182 	NULL,	/* map_color_rgb_alpha */
183 	NULL,	/* create_compositor */
184 	NULL,	/* get_hardware_params */
185 	pnga_text_begin,	/* text_begin */
186 	NULL,	/* finish_copydevice */
187 	pnga_begin_transparency_group,
188 	pnga_end_transparency_group
189 };
190 
191 typedef struct pnga_device_s {
192     gx_device_common;
193 
194     pdf14_ctx *ctx;
195 
196 } pnga_device;
197 
198 gs_private_st_composite_use_final(st_pnga_device, pnga_device, "pnga_device",
199 				  pnga_device_enum_ptrs, pnga_device_reloc_ptrs,
200 				  gx_device_finalize);
201 
202 const gx_device_printer gs_pnga_device = {
203     std_device_color_stype_body(pnga_device, &pnga_procs, "pnga",
204 				&st_pnga_device,
205 				XSIZE, YSIZE, X_DPI, Y_DPI, 24, 255, 0),
206     { 0 }
207 };
208 
209 #if 0
210 prn_device(pnga_procs, "pnga",
211 	   DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
212 	   X_DPI, Y_DPI,
213 	   0, 0, 0, 0,		/* margins */
214 	   24, pnga_print_page);
215 #endif
216 
217 /* GC procedures */
218 private
219 ENUM_PTRS_WITH(pnga_device_enum_ptrs, pnga_device *pdev) return 0;
220 case 0: return ENUM_OBJ(pdev->ctx);
221 ENUM_PTRS_END
RELOC_PTRS_WITH(pnga_device_reloc_ptrs,pnga_device * pdev)222 private RELOC_PTRS_WITH(pnga_device_reloc_ptrs, pnga_device *pdev)
223 {
224     RELOC_VAR(pdev->ctx);
225 }
226 RELOC_PTRS_END
227 
228 /* ------ The device descriptors for the marking device ------ */
229 
230 private dev_proc_fill_rectangle(pnga_mark_fill_rectangle);
231 private dev_proc_fill_rectangle(pnga_mark_fill_rectangle_ko_simple);
232 
233 private const gx_device_procs pnga_mark_procs =
234 {
235 	NULL,	/* open */
236 	NULL,	/* get_initial_matrix */
237 	NULL,	/* sync_output */
238 	NULL,	/* output_page */
239 	NULL,	/* close */
240 	gx_default_rgb_map_rgb_color,
241 	gx_default_rgb_map_color_rgb,
242 	NULL,	/* fill_rectangle */
243 	NULL,	/* tile_rectangle */
244 	NULL,	/* copy_mono */
245 	NULL,	/* copy_color */
246 	NULL,	/* draw_line */
247 	NULL,	/* get_bits */
248 	NULL,   /* get_params */
249 	NULL,   /* put_params */
250 	NULL,	/* map_cmyk_color */
251 	NULL,	/* get_xfont_procs */
252 	NULL,	/* get_xfont_device */
253 	NULL,	/* map_rgb_alpha_color */
254 #if 0
255 	gx_page_device_get_page_device,	/* get_page_device */
256 #else
257 	NULL,   /* get_page_device */
258 #endif
259 	NULL,	/* get_alpha_bits */
260 	NULL,	/* copy_alpha */
261 	NULL,	/* get_band */
262 	NULL,	/* copy_rop */
263 	NULL,	/* fill_path */
264 	NULL,	/* stroke_path */
265 	NULL,	/* fill_mask */
266 	NULL,	/* fill_trapezoid */
267 	NULL,	/* fill_parallelogram */
268 	NULL,	/* fill_triangle */
269 	NULL,	/* draw_thin_line */
270 	NULL,	/* begin_image */
271 	NULL,	/* image_data */
272 	NULL,	/* end_image */
273 	NULL,	/* strip_tile_rectangle */
274 	NULL,	/* strip_copy_rop, */
275 	NULL,	/* get_clipping_box */
276 	NULL,	/* begin_typed_image */
277 	NULL,	/* get_bits_rectangle */
278 	NULL,	/* map_color_rgb_alpha */
279 	NULL,	/* create_compositor */
280 	NULL,	/* get_hardware_params */
281 	NULL,	/* text_begin */
282 	NULL	/* finish_copydevice */
283 };
284 
285 typedef struct pnga_mark_device_s {
286     gx_device_common;
287 
288     pnga_device *pnga_dev;
289     float opacity;
290     float shape;
291     float alpha; /* alpha = opacity * shape */
292     gs_blend_mode_t blend_mode;
293 } pnga_mark_device;
294 
295 gs_private_st_simple_final(st_pnga_mark_device, pnga_mark_device,
296 			   "pnga_mark_device", gx_device_finalize);
297 
298 const gx_device_printer gs_pnga_mark_device = {
299     std_device_color_stype_body(pnga_mark_device, &pnga_mark_procs,
300 				"pnga_mark",
301 				&st_pnga_mark_device,
302 				XSIZE, YSIZE, X_DPI, Y_DPI, 24, 255, 0),
303     { 0 }
304 };
305 
306 typedef struct pnga_text_enum_s {
307     gs_text_enum_common;
308     gs_text_enum_t *target_enum;
309 } pnga_text_enum_t;
310 extern_st(st_gs_text_enum);
311 gs_private_st_suffix_add1(st_pnga_text_enum, pnga_text_enum_t,
312 			  "pnga_text_enum_t", pnga_text_enum_enum_ptrs,
313 			  pnga_text_enum_reloc_ptrs, st_gs_text_enum,
314 			  target_enum);
315 
316 /* ------ Private definitions ------ */
317 
318 /**
319  * pdf14_buf_new: Allocate a new PDF 1.4 buffer.
320  * @n_chan: Number of pixel channels including alpha.
321  *
322  * Return value: Newly allocated buffer, or NULL on failure.
323  **/
324 private pdf14_buf *
pdf14_buf_new(gs_int_rect * rect,bool has_alpha_g,bool has_shape,int n_chan,gs_memory_t * memory)325 pdf14_buf_new(gs_int_rect *rect, bool has_alpha_g, bool has_shape,
326 	       int n_chan,
327 	       gs_memory_t *memory)
328 {
329     pdf14_buf *result;
330     int rowstride = (rect->q.x - rect->p.x + 3) & -4;
331     int planestride = rowstride * (rect->q.y - rect->p.y);
332     int n_planes = n_chan + (has_shape ? 1 : 0) + (has_alpha_g ? 1 : 0);
333 
334     result = gs_alloc_struct(memory, pdf14_buf, &st_pdf14_buf,
335 			     "pdf14_buf_new");
336     if (result == NULL)
337 	return result;
338 
339     result->isolated = false;
340     result->knockout = false;
341     result->has_alpha_g = has_alpha_g;
342     result->has_shape = has_shape;
343     result->rect = *rect;
344     result->n_chan = n_chan;
345     result->n_planes = n_planes;
346     result->rowstride = rowstride;
347     result->planestride = planestride;
348     result->data = gs_alloc_bytes(memory, planestride * n_planes,
349 				  "pdf14_buf_new");
350     if (result->data == NULL) {
351 	gs_free_object(memory, result, "pdf_buf_new");
352 	return NULL;
353     }
354     if (has_alpha_g) {
355 	int alpha_g_plane = n_chan + (has_shape ? 1 : 0);
356 	memset (result->data + alpha_g_plane * planestride, 0, planestride);
357     }
358     return result;
359 }
360 
361 private void
pdf14_buf_free(pdf14_buf * buf,gs_memory_t * memory)362 pdf14_buf_free(pdf14_buf *buf, gs_memory_t *memory)
363 {
364     gs_free_object(memory, buf->data, "pdf14_buf_free");
365     gs_free_object(memory, buf, "pdf14_buf_free");
366 }
367 
368 private pdf14_ctx *
pdf14_ctx_new(gs_int_rect * rect,int n_chan,gs_memory_t * memory)369 pdf14_ctx_new(gs_int_rect *rect, int n_chan, gs_memory_t *memory)
370 {
371     pdf14_ctx *result;
372     pdf14_buf *buf;
373 
374     result = gs_alloc_struct(memory, pdf14_ctx, &st_pdf14_ctx,
375 			     "pdf14_ctx_new");
376 
377     buf = pdf14_buf_new(rect, false, false, n_chan, memory);
378     if (buf == NULL) {
379 	gs_free_object(memory, result, "pdf14_ctx_new");
380 	return NULL;
381     }
382     result->stack = buf;
383     result->n_chan = n_chan;
384     result->memory = memory;
385     result->rect = *rect;
386     if (result == NULL)
387 	return result;
388     return result;
389 }
390 
391 private void
pdf14_ctx_free(pdf14_ctx * ctx)392 pdf14_ctx_free(pdf14_ctx *ctx)
393 {
394     pdf14_buf *buf, *next;
395 
396     for (buf = ctx->stack; buf != NULL; buf = next) {
397 	next = buf->saved;
398 	pdf14_buf_free(buf, ctx->memory);
399     }
400     gs_free_object (ctx->memory, ctx, "pdf14_ctx_free");
401 }
402 
403 /**
404  * pdf14_find_backdrop_buf: Find backdrop buffer.
405  *
406  * Return value: Backdrop buffer for current group operation, or NULL
407  * if backdrop is fully transparent.
408  **/
409 private pdf14_buf *
pdf14_find_backdrop_buf(pdf14_ctx * ctx)410 pdf14_find_backdrop_buf(pdf14_ctx *ctx)
411 {
412     pdf14_buf *buf = ctx->stack;
413 
414     while (buf != NULL) {
415 	if (buf->isolated) return NULL;
416 	if (!buf->knockout) return buf->saved;
417 	buf = buf->saved;
418     }
419     /* this really shouldn't happen, as bottom-most buf should be
420        non-knockout */
421     return NULL;
422 }
423 
424 private int
pdf14_push_transparency_group(pdf14_ctx * ctx,gs_int_rect * rect,bool isolated,bool knockout,byte alpha,byte shape,gs_blend_mode_t blend_mode)425 pdf14_push_transparency_group(pdf14_ctx *ctx, gs_int_rect *rect,
426 			      bool isolated, bool knockout,
427 			      byte alpha, byte shape,
428 			      gs_blend_mode_t blend_mode)
429 {
430     pdf14_buf *tos = ctx->stack;
431     pdf14_buf *buf, *backdrop;
432     bool has_shape;
433 
434     /* todo: fix this hack, which makes all knockout groups isolated.
435        For the vast majority of files, there won't be any visible
436        effects, but it still isn't correct. The pixel compositing code
437        for non-isolated knockout groups gets pretty hairy, which is
438        why this is here. */
439     if (knockout) isolated = true;
440 
441     has_shape = tos->has_shape || tos->knockout;
442 
443     buf = pdf14_buf_new(rect, !isolated, has_shape, ctx->n_chan, ctx->memory);
444     if (buf == NULL)
445 	return_error(gs_error_VMerror);
446     buf->isolated = isolated;
447     buf->knockout = knockout;
448     buf->alpha = alpha;
449     buf->shape = shape;
450     buf->blend_mode = blend_mode;
451 
452     buf->saved = tos;
453     ctx->stack = buf;
454 
455     backdrop = pdf14_find_backdrop_buf(ctx);
456     if (backdrop == NULL) {
457 	memset(buf->data, 0, buf->planestride * (buf->n_chan +
458 						 (buf->has_shape ? 1 : 0)));
459     } else {
460 	/* make copy of backdrop for compositing */
461 	byte *buf_plane = buf->data;
462 	byte *tos_plane = tos->data + buf->rect.p.x - tos->rect.p.x +
463 	    (buf->rect.p.y - tos->rect.p.y) * tos->rowstride;
464 	int width = buf->rect.q.x - buf->rect.p.x;
465 	int y0 = buf->rect.p.y;
466 	int y1 = buf->rect.q.y;
467 	int i;
468 	int n_chan_copy = buf->n_chan + (tos->has_shape ? 1 : 0);
469 
470 	for (i = 0; i < n_chan_copy; i++) {
471 	    byte *buf_ptr = buf_plane;
472 	    byte *tos_ptr = tos_plane;
473 	    int y;
474 
475 	    for (y = y0; y < y1; ++y) {
476 		memcpy (buf_ptr, tos_ptr, width);
477 		buf_ptr += buf->rowstride;
478 		tos_ptr += tos->rowstride;
479 	    }
480 	    buf_plane += buf->planestride;
481 	    tos_plane += tos->planestride;
482 	}
483 	if (has_shape && !tos->has_shape)
484 	    memset (buf_plane, 0, buf->planestride);
485     }
486 
487     return 0;
488 }
489 
490 private int
pdf14_pop_transparency_group(pdf14_ctx * ctx)491 pdf14_pop_transparency_group(pdf14_ctx *ctx)
492 {
493     pdf14_buf *tos = ctx->stack;
494     pdf14_buf *nos = tos->saved;
495     int n_chan = ctx->n_chan;
496     byte alpha = tos->alpha;
497     byte shape = tos->shape;
498     byte blend_mode = tos->blend_mode;
499     int x0 = tos->rect.p.x;
500     int y0 = tos->rect.p.y;
501     int x1 = tos->rect.q.x;
502     int y1 = tos->rect.q.y;
503     byte *tos_ptr = tos->data;
504     byte *nos_ptr = nos->data + x0 - nos->rect.p.x +
505 	(y0 - nos->rect.p.y) * nos->rowstride;
506     int tos_planestride = tos->planestride;
507     int nos_planestride = nos->planestride;
508     int width = x1 - x0;
509     int x, y;
510     int i;
511     byte tos_pixel[PDF14_MAX_PLANES];
512     byte nos_pixel[PDF14_MAX_PLANES];
513     bool tos_isolated = tos->isolated;
514     bool nos_knockout = nos->knockout;
515     byte *nos_alpha_g_ptr;
516     int tos_shape_offset = n_chan * tos_planestride;
517     int tos_alpha_g_offset = tos_shape_offset +
518 	(tos->has_shape ? tos_planestride : 0);
519     int nos_shape_offset = n_chan * nos_planestride;
520     bool nos_has_shape = nos->has_shape;
521 
522     if (nos == NULL)
523 	return_error(gs_error_rangecheck);
524 
525     /* for now, only simple non-knockout */
526 
527     if (nos->has_alpha_g)
528 	nos_alpha_g_ptr = nos_ptr + n_chan * nos_planestride;
529     else
530 	nos_alpha_g_ptr = NULL;
531 
532     for (y = y0; y < y1; ++y) {
533 	for (x = 0; x < width; ++x) {
534 	    for (i = 0; i < n_chan; ++i) {
535 		tos_pixel[i] = tos_ptr[x + i * tos_planestride];
536 		nos_pixel[i] = nos_ptr[x + i * nos_planestride];
537 	    }
538 
539 	    if (nos_knockout) {
540 		byte *nos_shape_ptr = nos_has_shape ?
541 		    &nos_ptr[x + nos_shape_offset] : NULL;
542 		byte tos_shape = tos_ptr[x + tos_shape_offset];
543 
544 #if 1
545 		art_pdf_composite_knockout_isolated_8 (nos_pixel,
546 						       nos_shape_ptr,
547 						       tos_pixel,
548 						       n_chan - 1,
549 						       tos_shape,
550 						       alpha, shape);
551 #else
552 		tos_pixel[3] = tos_ptr[x + tos_shape_offset];
553 		art_pdf_composite_group_8(nos_pixel, nos_alpha_g_ptr,
554 					  tos_pixel,
555 					  n_chan - 1,
556 					  alpha, blend_mode);
557 #endif
558 	    } else if (tos_isolated) {
559 		art_pdf_composite_group_8(nos_pixel, nos_alpha_g_ptr,
560 					  tos_pixel,
561 					  n_chan - 1,
562 					  alpha, blend_mode);
563 	    } else {
564 		byte tos_alpha_g = tos_ptr[x + tos_alpha_g_offset];
565 		art_pdf_recomposite_group_8(nos_pixel, nos_alpha_g_ptr,
566 					    tos_pixel, tos_alpha_g,
567 					    n_chan - 1,
568 					    alpha, blend_mode);
569 	    }
570 	    if (nos_has_shape) {
571 		nos_ptr[x + nos_shape_offset] =
572 		    art_pdf_union_mul_8 (nos_ptr[x + nos_shape_offset],
573 					 tos_ptr[x + tos_shape_offset],
574 					 shape);
575 	    }
576 	    /* todo: knockout cases */
577 
578 	    for (i = 0; i < n_chan; ++i) {
579 		nos_ptr[x + i * nos_planestride] = nos_pixel[i];
580 	    }
581 	    if (nos_alpha_g_ptr != NULL)
582 		++nos_alpha_g_ptr;
583 	}
584 	tos_ptr += tos->rowstride;
585 	nos_ptr += nos->rowstride;
586 	if (nos_alpha_g_ptr != NULL)
587 	    nos_alpha_g_ptr += nos->rowstride - width;
588     }
589 
590     ctx->stack = nos;
591     pdf14_buf_free(tos, ctx->memory);
592     return 0;
593 }
594 
595 private int
pnga_open(gx_device * dev)596 pnga_open(gx_device *dev)
597 {
598     pnga_device *pdev = (pnga_device *)dev;
599     gs_int_rect rect;
600 
601     if_debug2('v', "[v]pnga_open: width = %d, height = %d\n",
602 	     dev->width, dev->height);
603 
604     rect.p.x = 0;
605     rect.p.y = 0;
606     rect.q.x = dev->width;
607     rect.q.y = dev->height;
608     pdev->ctx = pdf14_ctx_new(&rect, 4, dev->memory);
609     if (pdev->ctx == NULL)
610 	return_error(gs_error_VMerror);
611 
612     return 0;
613 }
614 
615 private int
pnga_close(gx_device * dev)616 pnga_close(gx_device *dev)
617 {
618     pnga_device *pdev = (pnga_device *)dev;
619 
620     if (pdev->ctx)
621 	pdf14_ctx_free(pdev->ctx);
622     return 0;
623 }
624 
625 private int
pnga_output_page(gx_device * dev,int num_copies,int flush)626 pnga_output_page(gx_device *dev, int num_copies, int flush)
627 {
628     pnga_device *pdev = (pnga_device *)dev;
629     gs_memory_t *mem = dev->memory;
630     int width = dev->width;
631     int height = dev->height;
632     int rowbytes = width << 2;
633 
634     /* PNG structures */
635     byte *row = gs_alloc_bytes(mem, rowbytes, "png raster buffer");
636     png_struct *png_ptr =
637     png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
638     png_info *info_ptr =
639     png_create_info_struct(png_ptr);
640     int depth = dev->color_info.depth;
641     int y;
642     int code;			/* return code */
643     const char *software_key = "Software";
644     char software_text[256];
645     png_text text_png;
646     char prefix[] = "pnga_png";
647     char fname[gp_file_name_sizeof];
648     FILE *file;
649 
650     pdf14_buf *buf = pdev->ctx->stack;
651     int planestride = buf->planestride;
652     byte *buf_ptr = buf->data;
653 
654     file = gp_open_scratch_file(prefix, fname, "wb");
655     if (file == NULL) {
656 	code = gs_note_error(gs_error_invalidfileaccess);
657 	goto done;
658     }
659     /* todo: suck from OutputFile instead */
660 
661     if_debug0('v', "[v]pnga_output_page\n");
662 
663     if (row == 0 || png_ptr == 0 || info_ptr == 0) {
664 	code = gs_note_error(gs_error_VMerror);
665 	goto done;
666     }
667     /* set error handling */
668     if (setjmp(png_ptr->jmpbuf)) {
669 	/* If we get here, we had a problem reading the file */
670 	code = gs_note_error(gs_error_VMerror);
671 	goto done;
672     }
673 
674     code = 0;			/* for normal path */
675     /* set up the output control */
676     png_init_io(png_ptr, file);
677 
678     /* set the file information here */
679     info_ptr->width = dev->width;
680     info_ptr->height = dev->height;
681     /* resolution is in pixels per meter vs. dpi */
682     info_ptr->x_pixels_per_unit =
683 	(png_uint_32) (dev->HWResolution[0] * (100.0 / 2.54));
684     info_ptr->y_pixels_per_unit =
685 	(png_uint_32) (dev->HWResolution[1] * (100.0 / 2.54));
686     info_ptr->phys_unit_type = PNG_RESOLUTION_METER;
687     info_ptr->valid |= PNG_INFO_pHYs;
688 
689     /* At present, only supporting 32-bit rgba */
690     info_ptr->bit_depth = 8;
691     info_ptr->color_type = PNG_COLOR_TYPE_RGB_ALPHA;
692 
693     /* add comment */
694     sprintf(software_text, "%s %d.%02d", gs_product,
695 	    (int)(gs_revision / 100), (int)(gs_revision % 100));
696     text_png.compression = -1;	/* uncompressed */
697     text_png.key = (char *)software_key;	/* not const, unfortunately */
698     text_png.text = software_text;
699     text_png.text_length = strlen(software_text);
700     info_ptr->text = &text_png;
701     info_ptr->num_text = 1;
702 
703     /* write the file information */
704     png_write_info(png_ptr, info_ptr);
705 
706     /* don't write the comments twice */
707     info_ptr->num_text = 0;
708     info_ptr->text = NULL;
709 
710     /* Write the contents of the image. */
711     for (y = 0; y < height; ++y) {
712 	int x;
713 
714 	for (x = 0; x < width; ++x) {
715 	    row[(x << 2)] = buf_ptr[x];
716 	    row[(x << 2) + 1] = buf_ptr[x + planestride];
717 	    row[(x << 2) + 2] = buf_ptr[x + planestride * 2];
718 	    row[(x << 2) + 3] = buf_ptr[x + planestride * 3];
719 	}
720 	png_write_row(png_ptr, row);
721 	buf_ptr += buf->rowstride;
722     }
723 
724     /* write the rest of the file */
725     png_write_end(png_ptr, info_ptr);
726 
727   done:
728     /* free the structures */
729     png_destroy_write_struct(&png_ptr, &info_ptr);
730     gs_free_object(mem, row, "png raster buffer");
731 
732     fclose (file);
733 
734     return code;
735 }
736 
737 private void
pnga_finalize(gx_device * dev)738 pnga_finalize(gx_device *dev)
739 {
740     if_debug1('v', "[v]finalizing %lx\n", dev);
741 }
742 
743 /**
744  * pnga_get_marking_device: Obtain a marking device.
745  * @dev: Original device.
746  * @pis: Imager state.
747  *
748  * The current implementation creates a marking device each time this
749  * routine is called. A potential optimization is to cache a single
750  * instance in the original device.
751  *
752  * Return value: Marking device, or NULL on error.
753  **/
754 private gx_device *
pnga_get_marking_device(gx_device * dev,const gs_imager_state * pis)755 pnga_get_marking_device(gx_device *dev, const gs_imager_state *pis)
756 {
757     pnga_device *pdev = (pnga_device *)dev;
758     pdf14_buf *buf = pdev->ctx->stack;
759     pnga_mark_device *mdev;
760     int code = gs_copydevice((gx_device **)&mdev,
761 			     (const gx_device *)&gs_pnga_mark_device,
762 			     dev->memory);
763 
764     if (code < 0)
765 	return NULL;
766 
767     check_device_separable((gx_device *)mdev);
768     gx_device_fill_in_procs((gx_device *)mdev);
769     mdev->pnga_dev = pdev;
770     mdev->opacity = pis->opacity.alpha;
771     mdev->shape = pis->shape.alpha;
772     mdev->alpha = pis->opacity.alpha * pis->shape.alpha;
773     mdev->blend_mode = pis->blend_mode;
774 
775     if (buf->knockout) {
776 	fill_dev_proc((gx_device *)mdev, fill_rectangle,
777 		      pnga_mark_fill_rectangle_ko_simple);
778     } else {
779 	fill_dev_proc((gx_device *)mdev, fill_rectangle,
780 		      pnga_mark_fill_rectangle);
781     }
782 
783     if_debug1('v', "[v]creating %lx\n", mdev);
784     mdev->finalize = pnga_finalize;
785     return (gx_device *)mdev;
786 }
787 
788 private void
pnga_release_marking_device(gx_device * marking_dev)789 pnga_release_marking_device(gx_device *marking_dev)
790 {
791     rc_decrement_only(marking_dev, "pnga_release_marking_device");
792 }
793 
794 private int
pnga_fill_path(gx_device * dev,const gs_imager_state * pis,gx_path * ppath,const gx_fill_params * params,const gx_drawing_color * pdcolor,const gx_clip_path * pcpath)795 pnga_fill_path(gx_device *dev, const gs_imager_state *pis,
796 			   gx_path *ppath, const gx_fill_params *params,
797 			   const gx_drawing_color *pdcolor,
798 			   const gx_clip_path *pcpath)
799 {
800     int code;
801     gx_device *mdev = pnga_get_marking_device(dev, pis);
802 
803     if (mdev == 0)
804 	return_error(gs_error_VMerror);
805     code = gx_default_fill_path(mdev, pis, ppath, params, pdcolor, pcpath);
806     pnga_release_marking_device(mdev);
807     return code;
808 }
809 
810 private int
pnga_stroke_path(gx_device * dev,const gs_imager_state * pis,gx_path * ppath,const gx_stroke_params * params,const gx_drawing_color * pdcolor,const gx_clip_path * pcpath)811 pnga_stroke_path(gx_device *dev, const gs_imager_state *pis,
812 			     gx_path *ppath, const gx_stroke_params *params,
813 			     const gx_drawing_color *pdcolor,
814 			     const gx_clip_path *pcpath)
815 {
816     int code;
817     gx_device *mdev = pnga_get_marking_device(dev, pis);
818 
819     if (mdev == 0)
820 	return_error(gs_error_VMerror);
821     code = gx_default_stroke_path(mdev, pis, ppath, params, pdcolor, pcpath);
822     pnga_release_marking_device(mdev);
823     return code;
824 }
825 
826 private int
pnga_begin_typed_image(gx_device * dev,const gs_imager_state * pis,const gs_matrix * pmat,const gs_image_common_t * pic,const gs_int_rect * prect,const gx_drawing_color * pdcolor,const gx_clip_path * pcpath,gs_memory_t * mem,gx_image_enum_common_t ** pinfo)827 pnga_begin_typed_image(gx_device * dev, const gs_imager_state * pis,
828 			   const gs_matrix *pmat, const gs_image_common_t *pic,
829 			   const gs_int_rect * prect,
830 			   const gx_drawing_color * pdcolor,
831 			   const gx_clip_path * pcpath, gs_memory_t * mem,
832 			   gx_image_enum_common_t ** pinfo)
833 {
834     gx_device *mdev = pnga_get_marking_device(dev, pis);
835     int code;
836 
837     if (mdev == 0)
838 	return_error(gs_error_VMerror);
839 
840     code = gx_default_begin_typed_image(mdev, pis, pmat, pic, prect, pdcolor,
841 					pcpath, mem, pinfo);
842 
843     rc_decrement_only(mdev, "pnga_begin_typed_image");
844 
845     return code;
846 }
847 
848 private int
pnga_text_resync(gs_text_enum_t * pte,const gs_text_enum_t * pfrom)849 pnga_text_resync(gs_text_enum_t *pte, const gs_text_enum_t *pfrom)
850 {
851     pnga_text_enum_t *const penum = (pnga_text_enum_t *)pte;
852 
853     if ((pte->text.operation ^ pfrom->text.operation) & ~TEXT_FROM_ANY)
854 	return_error(gs_error_rangecheck);
855     if (penum->target_enum) {
856 	int code = gs_text_resync(penum->target_enum, pfrom);
857 
858 	if (code < 0)
859 	    return code;
860     }
861     pte->text = pfrom->text;
862     gs_text_enum_copy_dynamic(pte, pfrom, false);
863     return 0;
864 }
865 
866 private int
pnga_text_process(gs_text_enum_t * pte)867 pnga_text_process(gs_text_enum_t *pte)
868 {
869     pnga_text_enum_t *const penum = (pnga_text_enum_t *)pte;
870     int code;
871 
872     code = gs_text_process(penum->target_enum);
873     gs_text_enum_copy_dynamic(pte, penum->target_enum, true);
874     return code;
875 }
876 
877 private bool
pnga_text_is_width_only(const gs_text_enum_t * pte)878 pnga_text_is_width_only(const gs_text_enum_t *pte)
879 {
880     const pnga_text_enum_t *const penum = (const pnga_text_enum_t *)pte;
881 
882     if (penum->target_enum)
883 	return gs_text_is_width_only(penum->target_enum);
884     return false;
885 }
886 
887 private int
pnga_text_current_width(const gs_text_enum_t * pte,gs_point * pwidth)888 pnga_text_current_width(const gs_text_enum_t *pte, gs_point *pwidth)
889 {
890     const pnga_text_enum_t *const penum = (const pnga_text_enum_t *)pte;
891 
892     if (penum->target_enum)
893 	return gs_text_current_width(penum->target_enum, pwidth);
894     return_error(gs_error_rangecheck); /* can't happen */
895 }
896 
897 private int
pnga_text_set_cache(gs_text_enum_t * pte,const double * pw,gs_text_cache_control_t control)898 pnga_text_set_cache(gs_text_enum_t *pte, const double *pw,
899 		   gs_text_cache_control_t control)
900 {
901     pnga_text_enum_t *const penum = (pnga_text_enum_t *)pte;
902 
903     if (penum->target_enum)
904 	return gs_text_set_cache(penum->target_enum, pw, control);
905     return_error(gs_error_rangecheck); /* can't happen */
906 }
907 
908 private int
pnga_text_retry(gs_text_enum_t * pte)909 pnga_text_retry(gs_text_enum_t *pte)
910 {
911     pnga_text_enum_t *const penum = (pnga_text_enum_t *)pte;
912 
913     if (penum->target_enum)
914 	return gs_text_retry(penum->target_enum);
915     return_error(gs_error_rangecheck); /* can't happen */
916 }
917 
918 private void
pnga_text_release(gs_text_enum_t * pte,client_name_t cname)919 pnga_text_release(gs_text_enum_t *pte, client_name_t cname)
920 {
921     pnga_text_enum_t *const penum = (pnga_text_enum_t *)pte;
922 
923     if (penum->target_enum) {
924 	gs_text_release(penum->target_enum, cname);
925 	penum->target_enum = 0;
926     }
927     gx_default_text_release(pte, cname);
928 }
929 
930 private const gs_text_enum_procs_t pnga_text_procs = {
931     pnga_text_resync, pnga_text_process,
932     pnga_text_is_width_only, pnga_text_current_width,
933     pnga_text_set_cache, pnga_text_retry,
934     pnga_text_release
935 };
936 
937 private int
pnga_text_begin(gx_device * dev,gs_imager_state * pis,const gs_text_params_t * text,gs_font * font,gx_path * path,const gx_device_color * pdcolor,const gx_clip_path * pcpath,gs_memory_t * memory,gs_text_enum_t ** ppenum)938 pnga_text_begin(gx_device * dev, gs_imager_state * pis,
939 		 const gs_text_params_t * text, gs_font * font,
940 		 gx_path * path, const gx_device_color * pdcolor,
941 		 const gx_clip_path * pcpath, gs_memory_t * memory,
942 		 gs_text_enum_t ** ppenum)
943 {
944     int code;
945     gx_device *mdev = pnga_get_marking_device(dev, pis);
946     pnga_text_enum_t *penum;
947     gs_text_enum_t *target_enum;
948 
949     if (mdev == 0)
950 	return_error(gs_error_VMerror);
951     if_debug0('v', "[v]pnga_text_begin\n");
952     code = gx_default_text_begin(mdev, pis, text, font, path, pdcolor, pcpath,
953 				 memory, &target_enum);
954 
955     rc_alloc_struct_1(penum, pnga_text_enum_t, &st_pnga_text_enum, memory,
956 		      return_error(gs_error_VMerror), "pnga_text_begin");
957     penum->rc.free = rc_free_text_enum;
958     penum->target_enum = target_enum;
959     code = gs_text_enum_init((gs_text_enum_t *)penum, &pnga_text_procs,
960 			     dev, pis, text, font, path, pdcolor, pcpath,
961 			     memory);
962     if (code < 0) {
963 	gs_free_object(memory, penum, "pnga_text_begin");
964 	return code;
965     }
966     *ppenum = (gs_text_enum_t *)penum;
967     rc_decrement_only(mdev, "pnga_text_begin");
968     return code;
969 }
970 
971 private int
pnga_fill_rectangle(gx_device * dev,int x,int y,int w,int h,gx_color_index color)972 pnga_fill_rectangle(gx_device * dev,
973 		    int x, int y, int w, int h, gx_color_index color)
974 {
975     if_debug4('v', "[v]pnga_fill_rectangle, (%d, %d), %d x %d\n", x, y, w, h);
976     return 0;
977 }
978 
979 
980 private int
pnga_begin_transparency_group(gx_device * dev,const gs_transparency_group_params_t * ptgp,const gs_rect * pbbox,gs_imager_state * pis,gs_transparency_state_t ** ppts,gs_memory_t * mem)981 pnga_begin_transparency_group(gx_device *dev,
982 			      const gs_transparency_group_params_t *ptgp,
983 			      const gs_rect *pbbox,
984 			      gs_imager_state *pis,
985 			      gs_transparency_state_t **ppts,
986 			      gs_memory_t *mem)
987 {
988     pnga_device *pdev = (pnga_device *)dev;
989     double alpha = pis->opacity.alpha * pis->shape.alpha;
990     int code;
991 
992     if_debug4('v', "[v]begin_transparency_group, I = %d, K = %d, alpha = %g, bm = %d\n",
993 	      ptgp->Isolated, ptgp->Knockout, alpha, pis->blend_mode);
994 
995     code = pdf14_push_transparency_group(pdev->ctx, &pdev->ctx->rect,
996 					 ptgp->Isolated, ptgp->Knockout,
997 					 floor (255 * alpha + 0.5),
998 					 floor (255 * pis->shape.alpha + 0.5),
999 					 pis->blend_mode);
1000     return code;
1001 }
1002 
1003 private int
pnga_end_transparency_group(gx_device * dev,gs_imager_state * pis,gs_transparency_state_t ** ppts)1004 pnga_end_transparency_group(gx_device *dev,
1005 			      gs_imager_state *pis,
1006 			      gs_transparency_state_t **ppts)
1007 {
1008     pnga_device *pdev = (pnga_device *)dev;
1009     int code;
1010 
1011     if_debug0('v', "[v]end_transparency_group\n");
1012     code = pdf14_pop_transparency_group(pdev->ctx);
1013     return code;
1014 }
1015 
1016 private int
pnga_mark_fill_rectangle(gx_device * dev,int x,int y,int w,int h,gx_color_index color)1017 pnga_mark_fill_rectangle(gx_device * dev,
1018 			 int x, int y, int w, int h, gx_color_index color)
1019 {
1020     pnga_mark_device *mdev = (pnga_mark_device *)dev;
1021     pnga_device *pdev = (pnga_device *)mdev->pnga_dev;
1022     pdf14_buf *buf = pdev->ctx->stack;
1023     int i, j, k;
1024     byte *line, *dst_ptr;
1025     byte src[PDF14_MAX_PLANES];
1026     byte dst[PDF14_MAX_PLANES];
1027     gs_blend_mode_t blend_mode = mdev->blend_mode;
1028     int rowstride = buf->rowstride;
1029     int planestride = buf->planestride;
1030     bool has_alpha_g = buf->has_alpha_g;
1031     bool has_shape = buf->has_shape;
1032     int shape_off = buf->n_chan * planestride;
1033     int alpha_g_off = shape_off + (has_shape ? planestride : 0);
1034     byte shape;
1035 
1036     src[0] = color >> 16;
1037     src[1] = (color >> 8) & 0xff;
1038     src[2] = color & 0xff;
1039     src[3] = floor (255 * mdev->alpha + 0.5);
1040     if (has_shape)
1041 	shape = floor (255 * mdev->shape + 0.5);
1042 
1043     if (x < buf->rect.p.x) x = buf->rect.p.x;
1044     if (y < buf->rect.p.x) y = buf->rect.p.y;
1045     if (x + w > buf->rect.q.x) w = buf->rect.q.x - x;
1046     if (y + h > buf->rect.q.y) h = buf->rect.q.y - y;
1047 
1048     line = buf->data + (x - buf->rect.p.x) + (y - buf->rect.p.y) * rowstride;
1049 
1050     for (j = 0; j < h; ++j) {
1051 	dst_ptr = line;
1052 	for (i = 0; i < w; ++i) {
1053 	    for (k = 0; k < 4; ++k)
1054 		dst[k] = dst_ptr[k * planestride];
1055 	    art_pdf_composite_pixel_alpha_8(dst, src, 3, blend_mode);
1056 	    for (k = 0; k < 4; ++k)
1057 		dst_ptr[k * planestride] = dst[k];
1058 	    if (has_alpha_g) {
1059 		int tmp = (255 - dst_ptr[alpha_g_off]) * (255 - src[3]) + 0x80;
1060 		dst_ptr[alpha_g_off] = 255 - ((tmp + (tmp >> 8)) >> 8);
1061 	    }
1062 	    if (has_shape) {
1063 		int tmp = (255 - dst_ptr[shape_off]) * (255 - shape) + 0x80;
1064 		dst_ptr[shape_off] = 255 - ((tmp + (tmp >> 8)) >> 8);
1065 	    }
1066 	    ++dst_ptr;
1067 	}
1068 	line += rowstride;
1069     }
1070     return 0;
1071 }
1072 
1073 private int
pnga_mark_fill_rectangle_ko_simple(gx_device * dev,int x,int y,int w,int h,gx_color_index color)1074 pnga_mark_fill_rectangle_ko_simple(gx_device * dev,
1075 				   int x, int y, int w, int h, gx_color_index color)
1076 {
1077     pnga_mark_device *mdev = (pnga_mark_device *)dev;
1078     pnga_device *pdev = (pnga_device *)mdev->pnga_dev;
1079     pdf14_buf *buf = pdev->ctx->stack;
1080     int i, j, k;
1081     byte *line, *dst_ptr;
1082     byte src[PDF14_MAX_PLANES];
1083     byte dst[PDF14_MAX_PLANES];
1084     int rowstride = buf->rowstride;
1085     int planestride = buf->planestride;
1086     int shape_off = buf->n_chan * planestride;
1087     bool has_shape = buf->has_shape;
1088     byte opacity;
1089 
1090     src[0] = color >> 16;
1091     src[1] = (color >> 8) & 0xff;
1092     src[2] = color & 0xff;
1093     src[3] = floor (255 * mdev->shape + 0.5);
1094     opacity = floor (255 * mdev->opacity + 0.5);
1095 
1096     if (x < buf->rect.p.x) x = buf->rect.p.x;
1097     if (y < buf->rect.p.x) y = buf->rect.p.y;
1098     if (x + w > buf->rect.q.x) w = buf->rect.q.x - x;
1099     if (y + h > buf->rect.q.y) h = buf->rect.q.y - y;
1100 
1101     line = buf->data + (x - buf->rect.p.x) + (y - buf->rect.p.y) * rowstride;
1102 
1103     for (j = 0; j < h; ++j) {
1104 	dst_ptr = line;
1105 	for (i = 0; i < w; ++i) {
1106 	    for (k = 0; k < 4; ++k)
1107 		dst[k] = dst_ptr[k * planestride];
1108 	    art_pdf_composite_knockout_simple_8(dst, has_shape ? dst_ptr + shape_off : NULL,
1109 						src, 3, opacity);
1110 	    for (k = 0; k < 4; ++k)
1111 		dst_ptr[k * planestride] = dst[k];
1112 	    ++dst_ptr;
1113 	}
1114 	line += rowstride;
1115     }
1116     return 0;
1117 }
1118