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