/* Copyright (C) 1997, 1999 artofcode LLC. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA, 02111-1307. */ /*$Id: gdevalph.c,v 1.2.6.1.2.1 2003/01/17 00:49:00 giles Exp $ */ /* Alpha-channel storage device */ #include "memory_.h" #include "gx.h" #include "gserrors.h" #include "gsdcolor.h" #include "gxdevice.h" #include "gxdevmem.h" #include "gxgetbit.h" /* * Very few devices (actually, none right now) actually store an alpha * channel. Here we provide a device that acts as a "wrapper" around * another device, and adds alpha channel storage. */ /* Define the device. */ typedef struct gx_device_store_alpha_s { gx_device_forward_common; bool open_close_target; /* if true, open/close target when opening/closing */ int alpha_depth; /* # of bits of stored alpha */ gx_device_memory *adev; /* stores the alpha values -- created when needed */ } gx_device_store_alpha; gs_private_st_suffix_add1_final(st_device_store_alpha, gx_device_store_alpha, "gx_device_store_alpha", device_store_alpha_enum_ptrs, device_store_alpha_reloc_ptrs, gx_device_finalize, st_device_forward, adev); /* The device descriptor. */ private dev_proc_open_device(dsa_open); private dev_proc_close_device(dsa_close); private dev_proc_fill_rectangle(dsa_fill_rectangle); private dev_proc_map_rgb_color(dsa_map_rgb_color); private dev_proc_map_color_rgb(dsa_map_color_rgb); private dev_proc_copy_mono(dsa_copy_mono); private dev_proc_copy_color(dsa_copy_color); private dev_proc_put_params(dsa_put_params); private dev_proc_map_rgb_alpha_color(dsa_map_rgb_alpha_color); private dev_proc_map_color_rgb_alpha(dsa_map_color_rgb_alpha); private dev_proc_copy_alpha(dsa_copy_alpha); private dev_proc_fill_path(dsa_fill_path); private dev_proc_stroke_path(dsa_stroke_path); private dev_proc_get_bits_rectangle(dsa_get_bits_rectangle); private const gx_device_store_alpha gs_store_alpha_device = {std_device_std_body_open(gx_device_store_alpha, 0, "alpha storage", 0, 0, 1, 1), {dsa_open, gx_forward_get_initial_matrix, gx_forward_sync_output, gx_forward_output_page, dsa_close, dsa_map_rgb_color, dsa_map_color_rgb, dsa_fill_rectangle, gx_default_tile_rectangle, dsa_copy_mono, dsa_copy_color, gx_default_draw_line, gx_default_get_bits, gx_forward_get_params, dsa_put_params, gx_default_cmyk_map_cmyk_color, /* only called for CMYK */ gx_forward_get_xfont_procs, gx_forward_get_xfont_device, dsa_map_rgb_alpha_color, gx_forward_get_page_device, gx_forward_get_alpha_bits, dsa_copy_alpha, gx_forward_get_band, gx_default_copy_rop, dsa_fill_path, dsa_stroke_path, /* * We should do the same thing for the "mid-level" * drawing operations that we do for fill_path and * stroke_path, but we don't bother to do this yet. */ gx_default_fill_mask, gx_default_fill_trapezoid, gx_default_fill_parallelogram, gx_default_fill_triangle, gx_default_draw_thin_line, gx_default_begin_image, gx_default_image_data, gx_default_end_image, gx_default_strip_tile_rectangle, gx_default_strip_copy_rop, gx_forward_get_clipping_box, gx_default_begin_typed_image, dsa_get_bits_rectangle, dsa_map_color_rgb_alpha, gx_default_create_compositor } }; /* Forward references */ private int alloc_alpha_storage(P1(gx_device_store_alpha * sadev)); #define sadev ((gx_device_store_alpha *)dev) /* ------ Open/close ------ */ private int dsa_open(gx_device * dev) { gx_device *tdev = sadev->target; int max_value = max(tdev->color_info.max_gray, tdev->color_info.max_color); int adepth = (max_value <= 3 ? 2 : max_value <= 15 ? 4 : 8); int depth = tdev->color_info.depth + adepth; int code; if (depth > 32) return_error(gs_error_rangecheck); code = (sadev->open_close_target ? (*dev_proc(tdev, open_device)) (tdev) : 0); if (code < 0) return code; sadev->width = tdev->width; sadev->height = tdev->height; sadev->color_info = tdev->color_info; sadev->color_info.depth = (depth <= 4 ? 4 : ROUND_UP(depth, 8)); sadev->alpha_depth = adepth; sadev->adev = 0; return 0; } private int dsa_close(gx_device * dev) { gx_device *adev = (gx_device *) sadev->adev; if (adev) { (*dev_proc(adev, close_device)) (adev); gs_free_object(dev->memory, adev, "dsa_close(adev)"); sadev->adev = 0; } if (sadev->open_close_target) { gx_device *tdev = sadev->target; return (*dev_proc(tdev, close_device)) (tdev); } return 0; } /* ------ Color mapping ------ */ private gx_color_index dsa_map_rgb_color(gx_device * dev, gx_color_value red, gx_color_value green, gx_color_value blue) { return dsa_map_rgb_alpha_color(dev, red, green, blue, gx_max_color_value); } private int dsa_map_color_rgb(gx_device * dev, gx_color_index color, gx_color_value rgb[3]) { gx_device *tdev = sadev->target; return (*dev_proc(tdev, map_color_rgb)) (tdev, color >> sadev->alpha_depth, rgb); } private gx_color_index dsa_map_rgb_alpha_color(gx_device * dev, gx_color_value red, gx_color_value green, gx_color_value blue, gx_color_value alpha) { gx_device *tdev = sadev->target; int adepth = sadev->alpha_depth; return ((*dev_proc(tdev, map_rgb_color)) (tdev, red, green, blue) << adepth) + (alpha * ((1 << adepth) - 1) / gx_max_color_value); } private int dsa_map_color_rgb_alpha(gx_device * dev, gx_color_index color, gx_color_value rgba[4]) { int code = dsa_map_color_rgb(dev, color, rgba); int amask = (1 << sadev->alpha_depth) - 1; if (code < 0) return code; rgba[3] = (color & amask) * gx_max_color_value / amask; return code; } /* ------ Low-level drawing ------ */ private int dsa_fill_rectangle(gx_device * dev, int x, int y, int width, int height, gx_color_index color) { gx_device *tdev = sadev->target; gx_device *adev = (gx_device *) sadev->adev; int adepth = sadev->alpha_depth; uint amask = (1 << adepth) - 1; if ((~color & amask) && !adev) { int code = alloc_alpha_storage(sadev); if (code < 0) return code; adev = (gx_device *) sadev->adev; } if (adev) (*dev_proc(adev, fill_rectangle)) (adev, x, y, width, height, color & amask); return (*dev_proc(tdev, fill_rectangle)) (tdev, x, y, width, height, color >> adepth); } private int dsa_copy_mono(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 color0, gx_color_index color1) { gx_device *tdev = sadev->target; gx_device *adev = (gx_device *) sadev->adev; int adepth = sadev->alpha_depth; uint amask = (1 << adepth) - 1; if ((((color0 != gx_no_color_index) && (~color0 & amask)) || ((color1 != gx_no_color_index) && (~color1 & amask))) && !adev ) { int code = alloc_alpha_storage(sadev); if (code < 0) return code; adev = (gx_device *) sadev->adev; } if (adev) (*dev_proc(adev, copy_mono)) (adev, data, data_x, raster, id, x, y, width, height, (color0 == gx_no_color_index ? color0 : color0 & amask), (color1 == gx_no_color_index ? color1 : color1 & amask)); return (*dev_proc(tdev, copy_mono)) (tdev, data, data_x, raster, id, x, y, width, height, (color0 == gx_no_color_index ? color0 : color0 >> adepth), (color1 == gx_no_color_index ? color1 : color1 >> adepth)); } private int dsa_copy_color(gx_device * dev, const byte * data, int data_x, int raster, gx_bitmap_id id, int x, int y, int width, int height) { int sdepth = dev->color_info.depth; gx_device *tdev = sadev->target; int tdepth = tdev->color_info.depth; gx_device *adev = (gx_device *) sadev->adev; int adepth = sadev->alpha_depth; /* = adev->color_info.depth */ uint amask = (1 << adepth) - 1; gs_memory_t *mem = dev->memory; byte *color_row; byte *alpha_row; int code = 0; int xbit, yi; fit_copy(dev, data, data_x, raster, id, x, y, width, height); color_row = gs_alloc_bytes(mem, bitmap_raster(width * tdepth), "dsa_copy_color(color)"); /* * If we aren't already storing alpha, alpha_row remains 0 until we * encounter a source value with a non-unity alpha. */ alpha_row = (adev ? gs_alloc_bytes(mem, bitmap_raster(width * adepth), "dsa_copy_color(color)") : 0); if (color_row == 0 || (adev && alpha_row == 0)) { code = gs_note_error(gs_error_VMerror); goto out; } xbit = data_x * dev->color_info.depth; for (yi = y; yi < y + height; ++yi) { const byte *source_row = data + (yi - y) * raster; sample_load_declare_setup(sptr, sbit, source_row + (xbit >> 3), xbit & 7, sdepth); sample_store_declare_setup(dcptr, dcbit, dcbyte, color_row, 0, tdepth); sample_store_declare_setup(daptr, dabit, dabyte, alpha_row, 0, adepth); int xi; for (xi = 0; xi < width; ++xi) { gx_color_index source; sample_load_next32(source, sptr, sbit, sdepth); { gx_color_index dest_color = source >> adepth; sample_store_next32(dest_color, dcptr, dcbit, tdepth, dcbyte); } { uint dest_alpha = source & amask; if (dest_alpha != amask && !alpha_row) { /* * Create an alpha row (and, if necessary, alpha storage). */ int axbit = (xi - x) * adepth; if (!adev) { code = alloc_alpha_storage(sadev); if (code < 0) return code; adev = (gx_device *) sadev->adev; } alpha_row = gs_alloc_bytes(mem, bitmap_raster(width * adepth), "dsa_copy_color(color)"); if (alpha_row == 0) { code = gs_note_error(gs_error_VMerror); goto out; } /* * All the alpha values up to this point are unity, so we * can fill them into the alpha row quickly. */ memset(alpha_row, 0xff, (axbit + 7) >> 3); daptr = alpha_row + (axbit >> 3); sample_store_setup(dabit, axbit & 7, adepth); sample_store_preload(dabyte, daptr, dabit, adepth); } if (alpha_row) sample_store_next12(dest_alpha, daptr, dabit, adepth, dabyte); } } if (alpha_row) { sample_store_flush(daptr, dabit, adepth, dabyte); (*dev_proc(adev, copy_color)) (adev, alpha_row, 0, 0 /* irrelevant */ , gx_no_bitmap_id, x, yi, width, 1); } sample_store_flush(dcptr, dcbit, adepth, dcbyte); (*dev_proc(tdev, copy_color)) (tdev, color_row, 0, 0 /* irrelevant */ , gx_no_bitmap_id, x, yi, width, 1); } out:gs_free_object(mem, alpha_row, "dsa_copy_color(alpha)"); gs_free_object(mem, color_row, "dsa_copy_color(color)"); return code; } private int dsa_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) { gx_device *tdev = sadev->target; gx_device *adev = (gx_device *) sadev->adev; int adepth = sadev->alpha_depth; uint amask = (1 << adepth) - 1; if ((~color & amask) && !adev) { int code = alloc_alpha_storage(sadev); if (code < 0) return code; adev = (gx_device *) sadev->adev; } if (adev) (*dev_proc(adev, copy_alpha)) (adev, data, data_x, raster, id, x, y, width, height, color & amask, depth); return (*dev_proc(tdev, copy_alpha)) (tdev, data, data_x, raster, id, x, y, width, height, color >> adepth, depth); } /* ------ High-level drawing ------ */ private int dsa_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) { if (!color_is_pure(pdcolor)) return gx_default_fill_path(dev, pis, ppath, params, pdcolor, pcpath); { gx_color_index color = gx_dc_pure_color(pdcolor); gx_drawing_color dcolor; gx_device *tdev = sadev->target; gx_device *adev = (gx_device *) sadev->adev; int adepth = sadev->alpha_depth; uint amask = (1 << adepth) - 1; int code; if ((~color & amask) && !adev) { code = alloc_alpha_storage(sadev); if (code < 0) return code; adev = (gx_device *) sadev->adev; } if (adev) { color_set_pure(&dcolor, color & amask); code = (*dev_proc(adev, fill_path)) (adev, pis, ppath, params, &dcolor, pcpath); if (code < 0) return code; } color_set_pure(&dcolor, color >> adepth); return (*dev_proc(tdev, fill_path)) (tdev, pis, ppath, params, &dcolor, pcpath); } } private int dsa_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) { if (!color_is_pure(pdcolor)) return gx_default_stroke_path(dev, pis, ppath, params, pdcolor, pcpath); { gx_color_index color = gx_dc_pure_color(pdcolor); gx_drawing_color dcolor; gx_device *tdev = sadev->target; gx_device *adev = (gx_device *) sadev->adev; int adepth = sadev->alpha_depth; uint amask = (1 << adepth) - 1; int code; if ((~color & amask) && !adev) { code = alloc_alpha_storage(sadev); if (code < 0) return code; adev = (gx_device *) sadev->adev; } if (adev) { color_set_pure(&dcolor, color & amask); code = (*dev_proc(adev, stroke_path)) (adev, pis, ppath, params, &dcolor, pcpath); if (code < 0) return code; } color_set_pure(&dcolor, color >> adepth); return (*dev_proc(tdev, stroke_path)) (tdev, pis, ppath, params, &dcolor, pcpath); } } /* ------ Reading bits ------ */ private int dsa_get_bits_rectangle(gx_device * dev, const gs_int_rect * prect, gs_get_bits_params_t * params, gs_int_rect ** unread) { int ddepth = dev->color_info.depth; gx_device *tdev = sadev->target; int tdepth = tdev->color_info.depth; gx_device *adev = (gx_device *) sadev->adev; int adepth = sadev->alpha_depth; /* = adev->color_info.depth */ uint alpha = (1 << adepth) - 1; gs_get_bits_options_t options = params->options; int x = prect->p.x, w = prect->q.x - x, y = prect->p.y, h = prect->q.y - y; int yi; uint raster = (options & gb_raster_standard ? bitmap_raster(w * ddepth) : params->raster); if ((~options & (gb_colors_native | gb_alpha_last | gb_packing_chunky | gb_return_copy | gb_offset_0)) || !(options & (gb_raster_standard | gb_raster_specified)) ) { if (options == 0) { params->options = gb_colors_native | gb_alpha_last | gb_packing_chunky | gb_return_copy | gb_offset_0 | (gb_raster_standard | gb_raster_specified); return_error(gs_error_rangecheck); } } if ((w <= 0) | (h <= 0)) { if ((w | h) < 0) return_error(gs_error_rangecheck); return 0; } if (x < 0 || w > dev->width - x || y < 0 || h > dev->height - y ) return_error(gs_error_rangecheck); for (yi = y; yi < y + h; ++yi) { sample_load_declare(scptr, scbit); sample_load_declare(saptr, sabit); sample_store_declare(dptr, dbit, dbbyte); byte *data = params->data[0] + (yi - y) * raster; gs_get_bits_params_t color_params; gs_int_rect rect; int code, xi; rect.p.x = x, rect.p.y = yi; rect.q.x = x + w, rect.q.y = yi + 1; color_params.options = gb_colors_native | gb_alpha_none | gb_packing_chunky | (gb_return_copy | gb_return_pointer) | gb_offset_specified | gb_raster_all /*irrelevant */ | gb_align_all /*irrelevant */ ; color_params.x_offset = w; code = (*dev_proc(tdev, get_bits_rectangle)) (tdev, &rect, &color_params, NULL); if (code < 0) return code; dptr = data; sample_store_setup(dbit, 0, ddepth); sample_store_preload(dbbyte, dptr, dbit, ddepth); { int cxbit = (color_params.data[0] == data ? w * tdepth : 0); scptr = data + (cxbit >> 3); sample_load_setup(scbit, cxbit & 7, tdepth); } if (adev) { int axbit = x * adepth; saptr = data + (axbit >> 3); sample_load_setup(sabit, axbit & 7, adepth); } for (xi = 0; xi < w; ++xi) { gx_color_index colors; sample_load_next32(colors, scptr, scbit, tdepth); if (adev) sample_load_next12(alpha, saptr, sabit, adepth); colors = (colors << adepth) + alpha; sample_store_next32(colors, dptr, dbit, ddepth, dbbyte); } sample_store_flush(dptr, dbit, ddepth, dbbyte); } return 0; } /* ------ Setting parameters ------ */ private int dsa_put_params(gx_device * dev, gs_param_list * plist) { gx_device *tdev = sadev->target; int code = (*dev_proc(tdev, put_params)) (tdev, plist); if (code < 0) return code; gx_device_copy_params(dev, tdev); return code; } #undef sadev /* ------ Internal utilities ------ */ /* Allocate the alpha storage memory device. */ private int alloc_alpha_storage(gx_device_store_alpha * sadev) { int adepth = sadev->alpha_depth; const gx_device_memory *mdproto = gdev_mem_device_for_bits(adepth); gs_memory_t *mem = sadev->memory; gx_device_memory *mdev = gs_alloc_struct(mem, gx_device_memory, &st_device_memory, "alloc_alpha_storage(adev)"); int code; if (mdev == 0) return_error(gs_error_VMerror); gs_make_mem_device(mdev, mdproto, mem, 0, NULL); mdev->width = sadev->width; mdev->height = sadev->height; mdev->bitmap_memory = mem; code = (*dev_proc(mdev, open_device)) ((gx_device *) mdev); if (code < 0) { gs_free_object(mem, mdev, "alloc_alpha_storage(adev)"); return_error(gs_error_VMerror); } sadev->adev = mdev; return (*dev_proc(mdev, fill_rectangle)) ((gx_device *) mdev, 0, 0, mdev->width, mdev->height, (gx_color_index) ((1 << adepth) - 1)); }