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