1 /* Copyright (C) 1995-2003, artofcode LLC. All rights reserved.
2
3 This program is free software; you can redistribute it and/or modify it
4 under the terms of the GNU General Public License as published by the
5 Free Software Foundation; either version 2 of the License, or (at your
6 option) any later version.
7
8 This program is distributed in the hope that it will be useful, but
9 WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along
14 with this program; if not, write to the Free Software Foundation, Inc.,
15 59 Temple Place, Suite 330, Boston, MA, 02111-1307.
16
17 */
18
19 /*$Id: gdevpng.c,v 1.4.2.1.2.3 2003/05/15 10:05:29 ghostgum Exp $ */
20 /* PNG (Portable Network Graphics) Format. Pronounced "ping". */
21 /* lpd 1999-09-24: changes PNG_NO_STDIO to PNG_NO_CONSOLE_IO for libpng
22 versions 1.0.3 and later. */
23 /* lpd 1999-07-01: replaced remaining uses of gs_malloc and gs_free with
24 gs_alloc_bytes and gs_free_object. */
25 /* lpd 1999-03-08: changed png.h to png_.h to allow compiling with only
26 headers in /usr/include, no source code. */
27 /* lpd 1997-07-20: changed from using gs_malloc/png_xxx_int to png_create_xxx
28 * for allocating structures, and from gs_free to png_write_destroy for
29 * freeing them. */
30 /* lpd 1997-5-7: added PNG_LIBPNG_VER conditional for operand types of
31 * dummy png_push_fill_buffer. */
32 /* lpd 1997-4-13: Added PNG_NO_STDIO to remove library access to stderr. */
33 /* lpd 1997-3-14: Added resolution (pHYs) to output. */
34 /* lpd 1996-6-24: Added #ifdef for compatibility with old libpng versions. */
35 /* lpd 1996-6-11: Edited to remove unnecessary color mapping code. */
36 /* lpd (L. Peter Deutsch) 1996-4-7: Modified for libpng 0.88. */
37 /* Original version by Russell Lang 1995-07-04 */
38
39 #include "gdevprn.h"
40 #include "gdevmem.h"
41 #include "gdevpccm.h"
42 #include "gscdefs.h"
43
44 /*
45 * Earlier libpng versions require disabling FILE * I/O altogether.
46 * This produces a compiler warning about no prototype for png_init_io.
47 * The right thing will happen at link time, since the library itself
48 * is compiled with stdio support. Unfortunately, we can't do this
49 * conditionally depending on PNG_LIBPNG_VER, because this is defined
50 * in png.h.
51 */
52 /*#define PNG_NO_STDIO*/
53 #include "png_.h"
54
55 /* ------ The device descriptors ------ */
56
57 /*
58 * Default X and Y resolution.
59 */
60 #define X_DPI 72
61 #define Y_DPI 72
62
63 private dev_proc_print_page(png_print_page);
64 private dev_proc_open_device(pngalpha_open);
65 private dev_proc_map_rgb_color(pngalpha_map_rgb_color);
66 private dev_proc_map_color_rgb(pngalpha_map_color_rgb);
67 private dev_proc_copy_alpha(pngalpha_copy_alpha);
68 private dev_proc_fill_rectangle(pngalpha_fill_rectangle);
69 private dev_proc_get_params(pngalpha_get_params);
70 private dev_proc_put_params(pngalpha_put_params);
71 private dev_proc_create_buf_device(pngalpha_create_buf_device);
72
73 /* Monochrome. */
74
75 const gx_device_printer gs_pngmono_device =
76 prn_device(prn_std_procs, "pngmono",
77 DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
78 X_DPI, Y_DPI,
79 0, 0, 0, 0, /* margins */
80 1, png_print_page);
81
82 /* 4-bit planar (EGA/VGA-style) color. */
83
84 private const gx_device_procs png16_procs =
85 prn_color_procs(gdev_prn_open, gdev_prn_output_page, gdev_prn_close,
86 pc_4bit_map_rgb_color, pc_4bit_map_color_rgb);
87 const gx_device_printer gs_png16_device = {
88 prn_device_body(gx_device_printer, png16_procs, "png16",
89 DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
90 X_DPI, Y_DPI,
91 0, 0, 0, 0, /* margins */
92 3, 4, 3, 2, 4, 3, png_print_page)
93 };
94
95 /* 8-bit (SuperVGA-style) color. */
96 /* (Uses a fixed palette of 3,3,2 bits.) */
97
98 private const gx_device_procs png256_procs =
99 prn_color_procs(gdev_prn_open, gdev_prn_output_page, gdev_prn_close,
100 pc_8bit_map_rgb_color, pc_8bit_map_color_rgb);
101 const gx_device_printer gs_png256_device = {
102 prn_device_body(gx_device_printer, png256_procs, "png256",
103 DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
104 X_DPI, Y_DPI,
105 0, 0, 0, 0, /* margins */
106 3, 8, 6, 6, 7, 7, png_print_page)
107 };
108
109 /* 8-bit gray */
110
111 private const gx_device_procs pnggray_procs =
112 prn_color_procs(gdev_prn_open, gdev_prn_output_page, gdev_prn_close,
113 gx_default_gray_map_rgb_color, gx_default_gray_map_color_rgb);
114 const gx_device_printer gs_pnggray_device =
115 {prn_device_body(gx_device_printer, pnggray_procs, "pnggray",
116 DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
117 X_DPI, Y_DPI,
118 0, 0, 0, 0, /* margins */
119 1, 8, 255, 0, 256, 0, png_print_page)
120 };
121
122 /* 24-bit color. */
123
124 private const gx_device_procs png16m_procs =
125 prn_color_procs(gdev_prn_open, gdev_prn_output_page, gdev_prn_close,
126 gx_default_rgb_map_rgb_color, gx_default_rgb_map_color_rgb);
127 const gx_device_printer gs_png16m_device =
128 prn_device(png16m_procs, "png16m",
129 DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
130 X_DPI, Y_DPI,
131 0, 0, 0, 0, /* margins */
132 24, png_print_page);
133
134 /* 32-bit RGBA */
135 /* pngalpha device is 32-bit RGBA, with the alpha channel
136 * indicating pixel coverage, not true transparency.
137 * Anti-aliasing is enabled by default.
138 * An erasepage will erase to transparent, not white.
139 * It is intended to be used for creating web graphics with
140 * a transparent background.
141 */
142 typedef struct gx_device_pngalpha_s gx_device_pngalpha;
143 struct gx_device_pngalpha_s {
144 gx_device_common;
145 gx_prn_device_common;
146 dev_t_proc_fill_rectangle((*orig_fill_rectangle), gx_device);
147 int background;
148 };
149 private const gx_device_procs pngalpha_procs =
150 {
151 pngalpha_open,
152 NULL, /* get_initial_matrix */
153 NULL, /* sync_output */
154 gdev_prn_output_page,
155 gdev_prn_close,
156 pngalpha_map_rgb_color, /* map_rgb_color */
157 pngalpha_map_color_rgb, /* map_color_rgb */
158 pngalpha_fill_rectangle,
159 NULL, /* tile_rectangle */
160 NULL, /* copy_mono */
161 NULL, /* copy_color */
162 NULL, /* draw_line */
163 NULL, /* get_bits */
164 pngalpha_get_params,
165 pngalpha_put_params,
166 NULL, /* map_cmyk_color */
167 NULL, /* get_xfont_procs */
168 NULL, /* get_xfont_device */
169 NULL, /* map_rgb_alpha_color */
170 gx_page_device_get_page_device,
171 NULL, /* get_alpha_bits */
172 pngalpha_copy_alpha,
173 NULL, /* get_band */
174 NULL, /* copy_rop */
175 NULL, /* fill_path */
176 NULL, /* stroke_path */
177 NULL, /* fill_mask */
178 NULL, /* fill_trapezoid */
179 NULL, /* fill_parallelogram */
180 NULL, /* fill_triangle */
181 NULL, /* draw_thin_line */
182 NULL, /* begin_image */
183 NULL, /* image_data */
184 NULL, /* end_image */
185 NULL, /* strip_tile_rectangle */
186 NULL, /* strip_copy_rop, */
187 NULL, /* get_clipping_box */
188 NULL, /* begin_typed_image */
189 NULL, /* get_bits_rectangle */
190 NULL, /* map_color_rgb_alpha */
191 NULL, /* create_compositor */
192 NULL, /* get_hardware_params */
193 NULL, /* text_begin */
194 NULL, /* finish_copydevice */
195 NULL, /* begin_transparency_group */
196 NULL, /* end_transparency_group */
197 NULL, /* begin_transparency_mask */
198 NULL, /* end_transparency_mask */
199 NULL /* discard_transparency_layer */
200 };
201
202 const gx_device_pngalpha gs_pngalpha_device = {
203 std_device_part1_(gx_device_pngalpha, &pngalpha_procs, "pngalpha",
204 &st_device_printer, open_init_closed),
205 /* color_info */
206 {3 /* number components */,
207 32 /* depth */,
208 255 /* max gray */,
209 255 /* max color */,
210 256 /* dither grays */,
211 256 /* dither colors */,
212 { 4, 4 } /* antialias info text, graphics */
213 },
214 std_device_part2_(
215 (int)((float)(DEFAULT_WIDTH_10THS) * (X_DPI) / 10 + 0.5),
216 (int)((float)(DEFAULT_HEIGHT_10THS) * (Y_DPI) / 10 + 0.5),
217 X_DPI, Y_DPI),
218 offset_margin_values(0, 0, 0, 0, 0, 0),
219 std_device_part3_(),
220 prn_device_body_rest_(png_print_page),
221 NULL,
222 0xffffff /* white background */
223 };
224
225
226 /* ------ Private definitions ------ */
227
228 /* Write out a page in PNG format. */
229 /* This routine is used for all formats. */
230 private int
png_print_page(gx_device_printer * pdev,FILE * file)231 png_print_page(gx_device_printer * pdev, FILE * file)
232 {
233 gs_memory_t *mem = pdev->memory;
234 int raster = gdev_prn_raster(pdev);
235
236 /* PNG structures */
237 byte *row = gs_alloc_bytes(mem, raster, "png raster buffer");
238 png_struct *png_ptr =
239 png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
240 png_info *info_ptr =
241 png_create_info_struct(png_ptr);
242 int height = pdev->height;
243 int depth = pdev->color_info.depth;
244 int y;
245 int code; /* return code */
246 char software_key[80];
247 char software_text[256];
248 png_text text_png;
249
250 if (row == 0 || png_ptr == 0 || info_ptr == 0) {
251 code = gs_note_error(gs_error_VMerror);
252 goto done;
253 }
254 /* set error handling */
255 if (setjmp(png_jmpbuf(png_ptr))) {
256 /* If we get here, we had a problem reading the file */
257 code = gs_note_error(gs_error_VMerror);
258 goto done;
259 }
260 code = 0; /* for normal path */
261 /* set up the output control */
262 png_init_io(png_ptr, file);
263
264 /* set the file information here */
265 switch (depth) {
266 case 32:
267 png_set_IHDR(png_ptr, info_ptr, pdev->width, pdev->height,
268 8, PNG_COLOR_TYPE_RGB_ALPHA,
269 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
270 PNG_FILTER_TYPE_DEFAULT);
271 png_set_invert_alpha(png_ptr);
272 { gx_device_pngalpha *ppdev = (gx_device_pngalpha *)pdev;
273 png_color_16 background;
274 background.index = 0;
275 background.red = (ppdev->background >> 16) & 0xff;
276 background.green = (ppdev->background >> 8) & 0xff;
277 background.blue = (ppdev->background) & 0xff;
278 background.gray = 0;
279 png_set_bKGD(png_ptr, info_ptr, &background);
280 }
281 break;
282 case 48:
283 png_set_IHDR(png_ptr, info_ptr, pdev->width, pdev->height,
284 16, PNG_COLOR_TYPE_RGB,
285 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
286 PNG_FILTER_TYPE_DEFAULT);
287 #if defined(ARCH_IS_BIG_ENDIAN) && (!ARCH_IS_BIG_ENDIAN)
288 png_set_swap(png_ptr);
289 #endif
290 break;
291 case 24:
292 png_set_IHDR(png_ptr, info_ptr, pdev->width, pdev->height,
293 8, PNG_COLOR_TYPE_RGB,
294 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
295 PNG_FILTER_TYPE_DEFAULT);
296 break;
297 case 8:
298 png_set_IHDR(png_ptr, info_ptr, pdev->width, pdev->height,
299 8, gx_device_has_color(pdev) ?
300 PNG_COLOR_TYPE_PALETTE : PNG_COLOR_TYPE_GRAY,
301 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
302 PNG_FILTER_TYPE_DEFAULT);
303 break;
304 case 4:
305 png_set_IHDR(png_ptr, info_ptr, pdev->width, pdev->height,
306 4, PNG_COLOR_TYPE_PALETTE,
307 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
308 PNG_FILTER_TYPE_DEFAULT);
309 break;
310 case 1:
311 png_set_IHDR(png_ptr, info_ptr, pdev->width, pdev->height,
312 1, PNG_COLOR_TYPE_PALETTE,
313 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
314 PNG_FILTER_TYPE_DEFAULT);
315 /* invert monocrome pixels */
316 png_set_invert_mono(png_ptr);
317 break;
318 }
319
320 /* resolution is in pixels per meter vs. dpi */
321 png_set_pHYs(png_ptr, info_ptr,
322 (png_uint_32) (pdev->HWResolution[0] * (100.0 / 2.54)),
323 (png_uint_32) (pdev->HWResolution[1] * (100.0 / 2.54)),
324 PNG_RESOLUTION_METER);
325
326 /* set the palette if there is one */
327 if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_PALETTE) {
328 int i;
329 int num_colors = 1 << depth;
330 gx_color_value rgb[3];
331 png_color palette[256];
332
333 if (num_colors > 256)
334 num_colors = 256;
335
336 for (i = 0; i < num_colors; i++) {
337 (*dev_proc(pdev, map_color_rgb)) ((gx_device *) pdev,
338 (gx_color_index) i, rgb);
339 palette[i].red = gx_color_value_to_byte(rgb[0]);
340 palette[i].green = gx_color_value_to_byte(rgb[1]);
341 palette[i].blue = gx_color_value_to_byte(rgb[2]);
342 }
343 png_set_PLTE(png_ptr, info_ptr, palette, num_colors);
344 }
345 /* add comment */
346 strncpy(software_key, "Software", sizeof(software_key));
347 sprintf(software_text, "%s %d.%02d", gs_product,
348 (int)(gs_revision / 100), (int)(gs_revision % 100));
349 text_png.compression = -1; /* uncompressed */
350 text_png.key = software_key;
351 text_png.text = software_text;
352 text_png.text_length = strlen(software_text);
353 png_set_text(png_ptr, info_ptr, &text_png, 1);
354
355 /* write the file information */
356 png_write_info(png_ptr, info_ptr);
357
358 /* don't write the comments twice */
359 #if 0
360 info_ptr->num_text = 0;
361 info_ptr->text = NULL;
362 #endif
363
364 /* Write the contents of the image. */
365 for (y = 0; y < height; y++) {
366 gdev_prn_copy_scan_lines(pdev, y, row, raster);
367 png_write_rows(png_ptr, &row, 1);
368 }
369
370 /* write the rest of the file */
371 png_write_end(png_ptr, info_ptr);
372
373 #if 0
374 /* if you alloced the palette, free it here */
375 gs_free_object(mem, info_ptr->palette, "png palette");
376 #endif
377
378 done:
379 /* free the structures */
380 png_destroy_write_struct(&png_ptr, &info_ptr);
381 gs_free_object(mem, row, "png raster buffer");
382
383 return code;
384 }
385
386 #if 0
387 /*
388 * Patch around a static reference to a never-used procedure.
389 * This could be avoided if we were willing to edit pngconf.h to
390 * #undef PNG_PROGRESSIVE_READ_SUPPORTED
391 */
392 #ifdef PNG_PROGRESSIVE_READ_SUPPORTED
393 # if PNG_LIBPNG_VER >= 95
394 # define PPFB_LENGTH_T png_size_t
395 # else
396 # define PPFB_LENGTH_T png_uint_32
397 # endif
398 void
399 png_push_fill_buffer(png_structp png_ptr, png_bytep buffer,
400 PPFB_LENGTH_T length)
401 {
402 }
403 #endif
404 #endif
405
406 private int
pngalpha_open(gx_device * pdev)407 pngalpha_open(gx_device * pdev)
408 {
409 gx_device_pngalpha *ppdev = (gx_device_pngalpha *)pdev;
410 int code;
411 /* We replace create_buf_device so we can replace copy_alpha
412 * for memory device, but not clist.
413 */
414 ppdev->printer_procs.buf_procs.create_buf_device =
415 pngalpha_create_buf_device;
416 code = gdev_prn_open(pdev);
417 /* We intercept fill_rectangle to translate "fill page with white"
418 * into "fill page with transparent". We then call the original
419 * implementation of fill_page.
420 */
421 if ((ppdev->procs.fill_rectangle != pngalpha_fill_rectangle) &&
422 (ppdev->procs.fill_rectangle != NULL)) {
423 ppdev->orig_fill_rectangle = ppdev->procs.fill_rectangle;
424 ppdev->procs.fill_rectangle = pngalpha_fill_rectangle;
425 }
426 return code;
427 }
428
429 private int
pngalpha_create_buf_device(gx_device ** pbdev,gx_device * target,const gx_render_plane_t * render_plane,gs_memory_t * mem,bool for_band)430 pngalpha_create_buf_device(gx_device **pbdev, gx_device *target,
431 const gx_render_plane_t *render_plane, gs_memory_t *mem,
432 bool for_band)
433 {
434 gx_device_printer *ptarget = (gx_device_printer *)target;
435 int code = gx_default_create_buf_device(pbdev, target,
436 render_plane, mem, for_band);
437 /* Now set copy_alpha to one that handles RGBA */
438 set_dev_proc(*pbdev, copy_alpha, ptarget->orig_procs.copy_alpha);
439 return code;
440 }
441
442 private int
pngalpha_put_params(gx_device * pdev,gs_param_list * plist)443 pngalpha_put_params(gx_device * pdev, gs_param_list * plist)
444 {
445 gx_device_pngalpha *ppdev = (gx_device_pngalpha *)pdev;
446 int background;
447 int code;
448
449 /* BackgroundColor in format 16#RRGGBB is used for bKGD chunk */
450 switch(code = param_read_int(plist, "BackgroundColor", &background)) {
451 case 0:
452 ppdev->background = background & 0xffffff;
453 break;
454 case 1: /* not found */
455 code = 0;
456 break;
457 default:
458 param_signal_error(plist, "BackgroundColor", code);
459 break;
460 }
461
462 if (code == 0) {
463 code = gdev_prn_put_params(pdev, plist);
464 if ((ppdev->procs.fill_rectangle != pngalpha_fill_rectangle) &&
465 (ppdev->procs.fill_rectangle != NULL)) {
466 /* Get current implementation of fill_rectangle and restore ours.
467 * Implementation is either clist or memory and can change
468 * during put_params.
469 */
470 ppdev->orig_fill_rectangle = ppdev->procs.fill_rectangle;
471 ppdev->procs.fill_rectangle = pngalpha_fill_rectangle;
472 }
473 }
474 return code;
475 }
476
477 /* Get device parameters */
478 private int
pngalpha_get_params(gx_device * pdev,gs_param_list * plist)479 pngalpha_get_params(gx_device * pdev, gs_param_list * plist)
480 {
481 gx_device_pngalpha *ppdev = (gx_device_pngalpha *)pdev;
482 int code = gdev_prn_get_params(pdev, plist);
483 if (code >= 0)
484 code = param_write_int(plist, "BackgroundColor",
485 &(ppdev->background));
486 return code;
487 }
488
489
490 /* RGB mapping for 32-bit RGBA color devices */
491
492 private gx_color_index
pngalpha_map_rgb_color(gx_device * dev,gx_color_value r,gx_color_value g,gx_color_value b)493 pngalpha_map_rgb_color(gx_device * dev,
494 gx_color_value r, gx_color_value g, gx_color_value b)
495 {
496 /* bits 0-7 are alpha, stored inverted to avoid white/opaque
497 * being 0xffffffff which is also gx_no_color_index.
498 * So 0xff is transparent and 0x00 is opaque.
499 * We always return opaque colors (bits 0-7 = 0).
500 * Return value is 0xRRGGBB00.
501 */
502 return
503 ((uint) gx_color_value_to_byte(b) << 8) +
504 ((ulong) gx_color_value_to_byte(g) << 16) +
505 ((ulong) gx_color_value_to_byte(r) << 24);
506 }
507
508 /* Map a color index to a r-g-b color. */
509 private int
pngalpha_map_color_rgb(gx_device * dev,gx_color_index color,gx_color_value prgb[3])510 pngalpha_map_color_rgb(gx_device * dev, gx_color_index color,
511 gx_color_value prgb[3])
512 {
513 prgb[0] = gx_color_value_from_byte((color >> 24) & 0xff);
514 prgb[1] = gx_color_value_from_byte((color >> 16) & 0xff);
515 prgb[2] = gx_color_value_from_byte((color >> 8) & 0xff);
516 return 0;
517 }
518
519 private int
pngalpha_fill_rectangle(gx_device * dev,int x,int y,int w,int h,gx_color_index color)520 pngalpha_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
521 gx_color_index color)
522 {
523 gx_device_pngalpha *pdev = (gx_device_pngalpha *)dev;
524 if ((color == 0xffffff00) && (x==0) && (y==0)
525 && (w==dev->width) && (h==dev->height)) {
526 /* If filling whole page with white, make it transparent */
527 return pdev->orig_fill_rectangle(dev, x, y, w, h, 0xfefefeff);
528 }
529 return pdev->orig_fill_rectangle(dev, x, y, w, h, color);
530 }
531
532 /* Implementation for 32-bit RGBA in a memory buffer */
533 /* Derived from gx_default_copy_alpha, but now maintains alpha channel. */
534 private int
pngalpha_copy_alpha(gx_device * dev,const byte * data,int data_x,int raster,gx_bitmap_id id,int x,int y,int width,int height,gx_color_index color,int depth)535 pngalpha_copy_alpha(gx_device * dev, const byte * data, int data_x,
536 int raster, gx_bitmap_id id, int x, int y, int width, int height,
537 gx_color_index color, int depth)
538 { /* This might be called with depth = 1.... */
539 if (depth == 1)
540 return (*dev_proc(dev, copy_mono)) (dev, data, data_x, raster, id,
541 x, y, width, height,
542 gx_no_color_index, color);
543 /*
544 * Simulate alpha by weighted averaging of RGB values.
545 * This is very slow, but functionally correct.
546 */
547 {
548 const byte *row;
549 gs_memory_t *mem = dev->memory;
550 int bpp = dev->color_info.depth;
551 int ncomps = dev->color_info.num_components;
552 uint in_size = gx_device_raster(dev, false);
553 byte *lin;
554 uint out_size;
555 byte *lout;
556 int code = 0;
557 gx_color_value color_cv[GX_DEVICE_COLOR_MAX_COMPONENTS];
558 int ry;
559
560 fit_copy(dev, data, data_x, raster, id, x, y, width, height);
561 row = data;
562 out_size = bitmap_raster(width * bpp);
563 lin = gs_alloc_bytes(mem, in_size, "copy_alpha(lin)");
564 lout = gs_alloc_bytes(mem, out_size, "copy_alpha(lout)");
565 if (lin == 0 || lout == 0) {
566 code = gs_note_error(gs_error_VMerror);
567 goto out;
568 }
569 (*dev_proc(dev, map_color_rgb)) (dev, color, color_cv);
570 for (ry = y; ry < y + height; row += raster, ++ry) {
571 byte *line;
572 int sx, rx;
573
574 DECLARE_LINE_ACCUM_COPY(lout, bpp, x);
575
576 code = (*dev_proc(dev, get_bits)) (dev, ry, lin, &line);
577 if (code < 0)
578 break;
579 for (sx = data_x, rx = x; sx < data_x + width; ++sx, ++rx) {
580 gx_color_index previous = gx_no_color_index;
581 gx_color_index composite;
582 int alpha2, alpha;
583
584 if (depth == 2) /* map 0 - 3 to 0 - 15 */
585 alpha = ((row[sx >> 2] >> ((3 - (sx & 3)) << 1)) & 3) * 5;
586 else
587 alpha2 = row[sx >> 1],
588 alpha = (sx & 1 ? alpha2 & 0xf : alpha2 >> 4);
589 if (alpha == 15) { /* Just write the new color. */
590 composite = color;
591 } else {
592 if (previous == gx_no_color_index) { /* Extract the old color. */
593 const byte *src = line + (rx * (bpp >> 3));
594 previous = 0;
595 previous += (gx_color_index) * src++ << 24;
596 previous += (gx_color_index) * src++ << 16;
597 previous += (gx_color_index) * src++ << 8;
598 previous += *src++;
599 }
600 if (alpha == 0) { /* Just write the old color. */
601 composite = previous;
602 } else { /* Blend values. */
603 gx_color_value cv[GX_DEVICE_COLOR_MAX_COMPONENTS];
604 int i;
605 int old_coverage;
606 int new_coverage;
607
608 (*dev_proc(dev, map_color_rgb)) (dev, previous, cv);
609 /* map_color_rgb doesn't give us coverage */
610 cv[3] = previous & 0xff;
611 old_coverage = 255 - cv[3];
612 new_coverage =
613 (255 * alpha + old_coverage * (15 - alpha)) / 15;
614 for (i=0; i<ncomps; i++)
615 cv[i] = min(((255 * alpha * color_cv[i]) +
616 (old_coverage * (15 - alpha ) * cv[i]))
617 / (new_coverage * 15), gx_max_color_value);
618 composite =
619 (*dev_proc(dev, map_rgb_color)) (dev,
620 cv[0], cv[1], cv[2]);
621 /* map_rgb_color doesn't include coverage */
622 composite |= (255 - new_coverage) & 0xff;
623
624 /* composite can never be gx_no_color_index
625 * because pixel is never completely transparent
626 * (low byte != 0xff).
627 */
628 }
629 }
630 LINE_ACCUM(composite, bpp);
631 }
632 LINE_ACCUM_COPY(dev, lout, bpp, x, rx, raster, ry);
633 }
634 out:gs_free_object(mem, lout, "copy_alpha(lout)");
635 gs_free_object(mem, lin, "copy_alpha(lin)");
636 return code;
637 }
638
639 /* shouldn't reach */
640 return_error(gs_error_unknownerror);
641 }
642
643