123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828 |
- /* Copyright (C) 1997, 1998, 1999 Aladdin Enterprises. All rights reserved.
-
- This file is part of AFPL Ghostscript.
-
- AFPL Ghostscript is distributed with NO WARRANTY OF ANY KIND. No author or
- distributor accepts any responsibility for the consequences of using it, or
- for whether it serves any particular purpose or works at all, unless he or
- she says so in writing. Refer to the Aladdin Free Public License (the
- "License") for full details.
-
- Every copy of AFPL Ghostscript must include a copy of the License, normally
- in a plain ASCII text file named PUBLIC. The License grants you the right
- to copy, modify and redistribute AFPL Ghostscript, but only under certain
- conditions described in the License. Among other things, the License
- requires that the copyright notice and this notice be preserved on all
- copies.
- */
- /*$Id: gsalphac.c,v 1.2 2000/09/19 19:00:25 lpd Exp $ */
- /* Alpha-compositing implementation */
- #include "memory_.h"
- #include "gx.h"
- #include "gserrors.h"
- #include "gsalphac.h"
- #include "gsiparam.h" /* for gs_image_alpha_t */
- #include "gsutil.h" /* for gs_next_ids */
- #include "gxalpha.h"
- #include "gxcomp.h"
- #include "gxdevice.h"
- #include "gxgetbit.h"
- #include "gxlum.h"
- /* ---------------- Internal definitions ---------------- */
- /* Define the parameters for a compositing operation. */
- typedef struct gs_composite_params_s {
- gs_composite_op_t cop;
- float delta; /* only for dissolve */
- uint source_alpha; /* only if !psource->alpha */
- uint source_values[4]; /* only if !psource->data */
- } gs_composite_params_t;
- /* Define the source or destination for a compositing operation. */
- #define pixel_row_fields(elt_type)\
- elt_type *data;\
- int bits_per_value; /* 1, 2, 4, 8, 12, 16 */\
- int initial_x;\
- gs_image_alpha_t alpha
- typedef struct pixel_row_s {
- pixel_row_fields(byte);
- } pixel_row_t;
- typedef struct const_pixel_row_s {
- pixel_row_fields(const byte);
- } const_pixel_row_t;
- /*
- * Composite two arrays of (premultiplied) pixel values. Legal values of
- * values_per_pixel are 1-4, not including alpha. Note that if pdest->alpha
- * is "none", the alpha value for all destination pixels will be taken as
- * unity, and any operation that could generate alpha values other than
- * unity will return an error. "Could generate" means that there are
- * possible values of the source and destination alpha values for which the
- * result has non-unity alpha: the error check does not scan the actual
- * alpha data to test whether there are any actual values that would
- * generate a non-unity alpha result.
- */
- int composite_values(P5(const pixel_row_t * pdest,
- const const_pixel_row_t * psource,
- int values_per_pixel, uint num_pixels,
- const gs_composite_params_t * pcp));
- /* ---------------- Alpha-compositing objects ---------------- */
- /*
- * Define which operations can generate non-unity alpha values in 3 of the 4
- * cases of source and destination not having unity alphas. (This is always
- * possible in the fourth case, both S & D non-unity, except for CLEAR.) We
- * do this with a bit mask indexed by the operation, counting from the LSB.
- * The name indicates whether S and/or D has non-unity alphas.
- */
- #define alpha_out_notS_notD\
- (1<<composite_Dissolve)
- #define _alpha_out_either\
- (alpha_out_notS_notD|(1<<composite_Satop)|(1<<composite_Datop)|\
- (1<<composite_Xor)|(1<<composite_PlusD)|(1<<composite_PlusL))
- #define alpha_out_S_notD\
- (_alpha_out_either|(1<<composite_Copy)|(1<<composite_Sover)|\
- (1<<composite_Din)|(1<<composite_Dout))
- #define alpha_out_notS_D\
- (_alpha_out_either|(1<<composite_Sin)|(1<<composite_Sout)|\
- (1<<composite_Dover)|(1<<composite_Highlight))
- /* ------ Object definition and creation ------ */
- /* Define alpha-compositing objects. */
- private composite_create_default_compositor_proc(c_alpha_create_default_compositor);
- private composite_equal_proc(c_alpha_equal);
- private composite_write_proc(c_alpha_write);
- private composite_read_proc(c_alpha_read);
- private const gs_composite_type_t gs_composite_alpha_type =
- {
- {
- c_alpha_create_default_compositor,
- c_alpha_equal,
- c_alpha_write,
- c_alpha_read
- }
- };
- typedef struct gs_composite_alpha_s {
- gs_composite_common;
- gs_composite_alpha_params_t params;
- } gs_composite_alpha_t;
- gs_private_st_simple(st_composite_alpha, gs_composite_alpha_t,
- "gs_composite_alpha_t");
- /* Create an alpha-compositing object. */
- int
- gs_create_composite_alpha(gs_composite_t ** ppcte,
- const gs_composite_alpha_params_t * params, gs_memory_t * mem)
- {
- gs_composite_alpha_t *pcte;
- rc_alloc_struct_0(pcte, gs_composite_alpha_t, &st_composite_alpha,
- mem, return_error(gs_error_VMerror),
- "gs_create_composite_alpha");
- pcte->type = &gs_composite_alpha_type;
- pcte->id = gs_next_ids(1);
- pcte->params = *params;
- *ppcte = (gs_composite_t *) pcte;
- return 0;
- }
- /* ------ Object implementation ------ */
- #define pacte ((const gs_composite_alpha_t *)pcte)
- private bool
- c_alpha_equal(const gs_composite_t * pcte, const gs_composite_t * pcte2)
- {
- return (pcte2->type == pcte->type &&
- #define pacte2 ((const gs_composite_alpha_t *)pcte2)
- pacte2->params.op == pacte->params.op &&
- (pacte->params.op != composite_Dissolve ||
- pacte2->params.delta == pacte->params.delta));
- #undef pacte2
- }
- private int
- c_alpha_write(const gs_composite_t * pcte, byte * data, uint * psize)
- {
- uint size = *psize;
- uint used;
- if (pacte->params.op == composite_Dissolve) {
- used = 1 + sizeof(pacte->params.delta);
- if (size < used) {
- *psize = used;
- return_error(gs_error_rangecheck);
- }
- memcpy(data + 1, &pacte->params.delta, sizeof(pacte->params.delta));
- } else {
- used = 1;
- if (size < used) {
- *psize = used;
- return_error(gs_error_rangecheck);
- }
- }
- *data = (byte) pacte->params.op;
- *psize = used;
- return 0;
- }
- private int
- c_alpha_read(gs_composite_t ** ppcte, const byte * data, uint size,
- gs_memory_t * mem)
- {
- gs_composite_alpha_params_t params;
- if (size < 1 || *data > composite_op_last)
- return_error(gs_error_rangecheck);
- params.op = *data;
- if (params.op == composite_Dissolve) {
- if (size != 1 + sizeof(params.delta))
- return_error(gs_error_rangecheck);
- memcpy(¶ms.delta, data + 1, sizeof(params.delta));
- } else {
- if (size != 1)
- return_error(gs_error_rangecheck);
- }
- return gs_create_composite_alpha(ppcte, ¶ms, mem);
- }
- /* ---------------- Alpha-compositing device ---------------- */
- /* Define the default alpha-compositing device. */
- typedef struct gx_device_composite_alpha_s {
- gx_device_forward_common;
- gs_composite_alpha_params_t params;
- } gx_device_composite_alpha;
- gs_private_st_suffix_add0_final(st_device_composite_alpha,
- gx_device_composite_alpha, "gx_device_composite_alpha",
- device_c_alpha_enum_ptrs, device_c_alpha_reloc_ptrs, gx_device_finalize,
- st_device_forward);
- /* The device descriptor. */
- private dev_proc_close_device(dca_close);
- private dev_proc_fill_rectangle(dca_fill_rectangle);
- private dev_proc_map_rgb_color(dca_map_rgb_color);
- private dev_proc_map_color_rgb(dca_map_color_rgb);
- private dev_proc_copy_mono(dca_copy_mono);
- private dev_proc_copy_color(dca_copy_color);
- private dev_proc_map_rgb_alpha_color(dca_map_rgb_alpha_color);
- private dev_proc_map_color_rgb_alpha(dca_map_color_rgb_alpha);
- private dev_proc_copy_alpha(dca_copy_alpha);
- private const gx_device_composite_alpha gs_composite_alpha_device =
- {std_device_std_body_open(gx_device_composite_alpha, 0,
- "alpha compositor", 0, 0, 1, 1),
- {gx_default_open_device,
- gx_forward_get_initial_matrix,
- gx_default_sync_output,
- gx_default_output_page,
- dca_close,
- dca_map_rgb_color,
- dca_map_color_rgb,
- dca_fill_rectangle,
- gx_default_tile_rectangle,
- dca_copy_mono,
- dca_copy_color,
- gx_default_draw_line,
- gx_default_get_bits,
- gx_forward_get_params,
- gx_forward_put_params,
- gx_default_cmyk_map_cmyk_color, /* only called for CMYK */
- gx_forward_get_xfont_procs,
- gx_forward_get_xfont_device,
- dca_map_rgb_alpha_color,
- gx_forward_get_page_device,
- gx_forward_get_alpha_bits,
- dca_copy_alpha,
- gx_forward_get_band,
- gx_default_copy_rop,
- gx_default_fill_path,
- gx_default_stroke_path,
- 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,
- gx_forward_get_bits_rectangle,
- dca_map_color_rgb_alpha,
- gx_no_create_compositor
- }
- };
- /* Create an alpha compositor. */
- private int
- c_alpha_create_default_compositor(const gs_composite_t * pcte,
- gx_device ** pcdev, gx_device * dev, const gs_imager_state * pis,
- gs_memory_t * mem)
- {
- gx_device_composite_alpha *cdev;
- if (pacte->params.op == composite_Copy) {
- /* Just use the original device. */
- *pcdev = dev;
- return 0;
- }
- cdev =
- gs_alloc_struct_immovable(mem, gx_device_composite_alpha,
- &st_device_composite_alpha,
- "create default alpha compositor");
- *pcdev = (gx_device *)cdev;
- if (cdev == 0)
- return_error(gs_error_VMerror);
- gx_device_init((gx_device *)cdev,
- (const gx_device *)&gs_composite_alpha_device, mem, true);
- gx_device_copy_params((gx_device *)cdev, dev);
- /*
- * Set the color_info and depth to be compatible with the target,
- * but using standard chunky color storage, including alpha.
- ****** CURRENTLY ALWAYS USE 8-BIT COLOR ******
- */
- cdev->color_info.depth =
- (dev->color_info.num_components == 4 ? 32 /* CMYK, no alpha */ :
- (dev->color_info.num_components + 1) * 8);
- cdev->color_info.max_gray = cdev->color_info.max_color = 255;
- /* No halftoning will occur, but we fill these in anyway.... */
- cdev->color_info.dither_grays = cdev->color_info.dither_colors = 256;
- /*
- * We could speed things up a little by tailoring the procedures in
- * the device to the specific num_components, but for simplicity,
- * we'll defer considering that until there is a demonstrated need.
- */
- gx_device_set_target((gx_device_forward *)cdev, dev);
- cdev->params = pacte->params;
- return 0;
- }
- /* Close the device and free its storage. */
- private int
- dca_close(gx_device * dev)
- { /*
- * Finalization will call close again: avoid a recursion loop.
- */
- set_dev_proc(dev, close_device, gx_default_close_device);
- gs_free_object(dev->memory, dev, "dca_close");
- return 0;
- }
- /* ------ (RGB) color mapping ------ */
- private gx_color_index
- dca_map_rgb_color(gx_device * dev,
- gx_color_value r, gx_color_value g, gx_color_value b)
- {
- return dca_map_rgb_alpha_color(dev, r, g, b, gx_max_color_value);
- }
- private gx_color_index
- dca_map_rgb_alpha_color(gx_device * dev,
- gx_color_value red, gx_color_value green, gx_color_value blue,
- gx_color_value alpha)
- { /*
- * We work exclusively with premultiplied color values, so we
- * have to premultiply the color components by alpha here.
- */
- byte a = gx_color_value_to_byte(alpha);
- #define premult_(c)\
- (((c) * a + gx_max_color_value / 2) / gx_max_color_value)
- #ifdef PREMULTIPLY_TOWARDS_WHITE
- byte bias = ~a;
- # define premult(c) (premult_(c) + bias)
- #else
- # define premult(c) premult_(c)
- #endif
- gx_color_index color;
- if (dev->color_info.num_components == 1) {
- uint lum =
- (red * lum_red_weight + green * lum_green_weight +
- blue * lum_blue_weight + lum_all_weights / 2) /
- lum_all_weights;
- if (a == 0xff)
- color = gx_color_value_to_byte(lum);
- else /* Premultiplication is necessary. */
- color = premult(lum);
- } else {
- if (a == 0xff)
- color =
- ((uint) gx_color_value_to_byte(red) << 16) +
- ((uint) gx_color_value_to_byte(green) << 8) +
- gx_color_value_to_byte(blue);
- else /* Premultiplication is necessary. */
- color =
- (premult(red) << 16) + (premult(green) << 8) + premult(blue);
- }
- #undef premult
- return (color << 8) + a;
- }
- private int
- dca_map_color_rgb(gx_device * dev, gx_color_index color,
- gx_color_value prgb[3])
- {
- gx_color_value red = gx_color_value_from_byte((byte) (color >> 24));
- byte a = (byte) color;
- #define postdiv_(c)\
- (((c) * 0xff + a / 2) / a)
- #ifdef PREMULTIPLY_TOWARDS_WHITE
- byte bias = ~a;
- # define postdiv(c) postdiv_(c - bias)
- #else
- # define postdiv(c) postdiv_(c)
- #endif
- if (dev->color_info.num_components == 1) {
- if (a != 0xff) {
- /* Undo premultiplication. */
- if (a == 0)
- red = 0;
- else
- red = postdiv(red);
- }
- prgb[0] = prgb[1] = prgb[2] = red;
- } else {
- gx_color_value
- green = gx_color_value_from_byte((byte) (color >> 16)),
- blue = gx_color_value_from_byte((byte) (color >> 8));
- if (a != 0xff) {
- /* Undo premultiplication. */
- /****** WHAT TO DO ABOUT BIG LOSS OF PRECISION? ******/
- if (a == 0)
- red = green = blue = 0;
- else {
- red = postdiv(red);
- green = postdiv(green);
- blue = postdiv(blue);
- }
- }
- prgb[0] = red, prgb[1] = green, prgb[2] = blue;
- }
- #undef postdiv
- return 0;
- }
- private int
- dca_map_color_rgb_alpha(gx_device * dev, gx_color_index color,
- gx_color_value prgba[4])
- {
- prgba[3] = gx_color_value_from_byte((byte) color);
- return dca_map_color_rgb(dev, color, prgba);
- }
- /* ------ Imaging ------ */
- private int
- dca_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
- gx_color_index color)
- { /* This is where all the real work gets done! */
- gx_device_composite_alpha *adev = (gx_device_composite_alpha *) dev;
- gx_device *target = adev->target;
- byte *std_row;
- byte *native_row;
- gs_int_rect rect;
- gs_get_bits_params_t std_params, native_params;
- int code = 0;
- int yi;
- gs_composite_params_t cp;
- const_pixel_row_t source;
- pixel_row_t dest;
- fit_fill(dev, x, y, w, h);
- std_row = gs_alloc_bytes(dev->memory,
- (dev->color_info.depth * w + 7) >> 3,
- "dca_fill_rectangle(std)");
- native_row = gs_alloc_bytes(dev->memory,
- (target->color_info.depth * w + 7) >> 3,
- "dca_fill_rectangle(native)");
- if (std_row == 0 || native_row == 0) {
- code = gs_note_error(gs_error_VMerror);
- goto out;
- }
- rect.p.x = x, rect.q.x = x + w;
- std_params.options =
- gb_colors_for_device(dev) |
- (GB_ALPHA_LAST | GB_DEPTH_8 | GB_PACKING_CHUNKY |
- GB_RETURN_COPY | GB_RETURN_POINTER | GB_ALIGN_ANY |
- GB_OFFSET_0 | GB_OFFSET_ANY | GB_RASTER_STANDARD |
- GB_RASTER_ANY);
- cp.cop = adev->params.op;
- if (cp.cop == composite_Dissolve)
- cp.delta = adev->params.delta;
- {
- gx_color_value rgba[4];
- /****** DOESN'T HANDLE CMYK ******/
- (*dev_proc(dev, map_color_rgb_alpha)) (dev, color, rgba);
- cp.source_values[0] = gx_color_value_to_byte(rgba[0]);
- cp.source_values[1] = gx_color_value_to_byte(rgba[1]);
- cp.source_values[2] = gx_color_value_to_byte(rgba[2]);
- cp.source_alpha = gx_color_value_to_byte(rgba[3]);
- }
- source.data = 0;
- source.bits_per_value = 8;
- source.alpha = gs_image_alpha_none;
- for (yi = y; yi < y + h; ++yi) {
- /* Read a row in standard representation. */
- rect.p.y = yi, rect.q.y = yi + 1;
- std_params.data[0] = std_row;
- code = (*dev_proc(target, get_bits_rectangle))
- (target, &rect, &std_params, NULL);
- if (code < 0)
- break;
- /* Do the work. */
- dest.data = std_params.data[0];
- dest.bits_per_value = 8;
- dest.initial_x =
- (std_params.options & GB_OFFSET_ANY ? std_params.x_offset : 0);
- dest.alpha =
- (std_params.options & GB_ALPHA_FIRST ? gs_image_alpha_first :
- std_params.options & GB_ALPHA_LAST ? gs_image_alpha_last :
- gs_image_alpha_none);
- code = composite_values(&dest, &source,
- dev->color_info.num_components, w, &cp);
- if (code < 0)
- break;
- if (std_params.data[0] == std_row) {
- /* Convert the row back to native representation. */
- /* (Otherwise, we had a direct pointer to device data.) */
- native_params.options =
- (GB_COLORS_NATIVE | GB_PACKING_CHUNKY | GB_RETURN_COPY |
- GB_OFFSET_0 | GB_RASTER_ALL | GB_ALIGN_STANDARD);
- native_params.data[0] = native_row;
- code = gx_get_bits_copy(target, 0, w, 1, &native_params,
- &std_params, std_row,
- 0 /* raster is irrelevant */ );
- if (code < 0)
- break;
- code = (*dev_proc(target, copy_color))
- (target, native_row, 0, 0 /* raster is irrelevant */ ,
- gx_no_bitmap_id, x, yi, w, 1);
- if (code < 0)
- break;
- }
- }
- out:gs_free_object(dev->memory, native_row, "dca_fill_rectangle(native)");
- gs_free_object(dev->memory, std_row, "dca_fill_rectangle(std)");
- return code;
- }
- private int
- dca_copy_mono(gx_device * dev, const byte * data,
- int dx, int raster, gx_bitmap_id id, int x, int y, int w, int h,
- gx_color_index zero, gx_color_index one)
- {
- /****** TEMPORARY ******/
- return gx_default_copy_mono(dev, data, dx, raster, id, x, y, w, h,
- zero, one);
- }
- private int
- dca_copy_color(gx_device * dev, const byte * data,
- int dx, int raster, gx_bitmap_id id,
- int x, int y, int w, int h)
- {
- /****** TEMPORARY ******/
- return gx_default_copy_color(dev, data, dx, raster, id, x, y, w, h);
- }
- private int
- dca_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)
- {
- /****** TEMPORARY ******/
- return gx_default_copy_alpha(dev, data, data_x, raster, id, x, y,
- width, height, color, depth);
- }
- /*
- * Composite two arrays of (premultiplied) pixel values.
- * See gsdpnext.h for the specification.
- *
- * The current implementation is simple but inefficient. We'll speed it up
- * later if necessary.
- */
- int
- composite_values(const pixel_row_t * pdest, const const_pixel_row_t * psource,
- int values_per_pixel, uint num_pixels, const gs_composite_params_t * pcp)
- {
- int dest_bpv = pdest->bits_per_value;
- int source_bpv = psource->bits_per_value;
- /*
- * source_alpha_j gives the source component index for the alpha value,
- * if the source has alpha.
- */
- int source_alpha_j =
- (psource->alpha == gs_image_alpha_last ? values_per_pixel :
- psource->alpha == gs_image_alpha_first ? 0 : -1);
- /* dest_alpha_j does the same for the destination. */
- int dest_alpha_j =
- (pdest->alpha == gs_image_alpha_last ? values_per_pixel :
- pdest->alpha == gs_image_alpha_first ? 0 : -1);
- /* dest_vpp is the number of stored destination values. */
- int dest_vpp = values_per_pixel + (dest_alpha_j >= 0);
- /* source_vpp is the number of stored source values. */
- int source_vpp = values_per_pixel + (source_alpha_j >= 0);
- bool constant_colors = psource->data == 0;
- uint highlight_value = (1 << dest_bpv) - 1;
- sample_load_declare(sptr, sbit);
- sample_store_declare(dptr, dbit, dbyte);
- {
- uint xbit = pdest->initial_x * dest_bpv * dest_vpp;
- sample_store_setup(dbit, xbit & 7, dest_bpv);
- dptr = pdest->data + (xbit >> 3);
- }
- {
- uint xbit = psource->initial_x * source_bpv * source_vpp;
- sbit = xbit & 7;
- sptr = psource->data + (xbit >> 3);
- }
- {
- uint source_max = (1 << source_bpv) - 1;
- uint dest_max = (1 << dest_bpv) - 1;
- /*
- * We could save a little work by only setting up source_delta
- * and dest_delta if the operation is Dissolve.
- */
- float source_delta = pcp->delta * dest_max / source_max;
- float dest_delta = 1.0 - pcp->delta;
- uint source_alpha = pcp->source_alpha;
- uint dest_alpha = dest_max;
- #ifdef PREMULTIPLY_TOWARDS_WHITE
- uint source_bias = source_max - source_alpha;
- uint dest_bias = 0;
- uint result_bias = 0;
- #endif
- uint x;
- if (!pdest->alpha) {
- uint mask =
- (psource->alpha || source_alpha != source_max ?
- alpha_out_S_notD : alpha_out_notS_notD);
- if ((mask >> pcp->cop) & 1) {
- /*
- * The operation could produce non-unity alpha values, but
- * the destination can't store them. Return an error.
- */
- return_error(gs_error_rangecheck);
- }
- }
- /* Preload the output byte buffer if necessary. */
- sample_store_preload(dbyte, dptr, dbit, dest_bpv);
- for (x = 0; x < num_pixels; ++x) {
- int j;
- uint result_alpha = dest_alpha;
- /* get_value does not increment the source pointer. */
- #define get_value(v, ptr, bit, bpv, vmax)\
- sample_load16(v, ptr, bit, bpv)
- /* put_value increments the destination pointer. */
- #define put_value(v, ptr, bit, bpv, bbyte)\
- sample_store_next16(v, ptr, bit, bpv, bbyte)
- #define advance(ptr, bit, bpv)\
- sample_next(ptr, bit, bpv)
- /* Get destination alpha value. */
- if (dest_alpha_j >= 0) {
- int dabit = dbit + dest_bpv * dest_alpha_j;
- const byte *daptr = dptr + (dabit >> 3);
- get_value(dest_alpha, daptr, dabit & 7, dest_bpv, dest_max);
- #ifdef PREMULTIPLY_TOWARDS_WHITE
- dest_bias = dest_max - dest_alpha;
- #endif
- }
- /* Get source alpha value. */
- if (source_alpha_j >= 0) {
- int sabit = sbit;
- const byte *saptr = sptr;
- if (source_alpha_j == 0)
- advance(sptr, sbit, source_bpv);
- else
- advance(saptr, sabit, source_bpv * source_alpha_j);
- get_value(source_alpha, saptr, sabit, source_bpv, source_max);
- #ifdef PREMULTIPLY_TOWARDS_WHITE
- source_bias = source_max - source_alpha;
- #endif
- }
- /*
- * We are always multiplying a dest value by a source value to compute a
- * dest value, so the denominator is always source_max. (Dissolve is the
- * one exception.)
- */
- #define fr(v, a) ((v) * (a) / source_max)
- #define nfr(v, a, maxv) ((v) * (maxv - (a)) / source_max)
- /*
- * Iterate over the components of a single pixel.
- * j = 0 for alpha, 1 .. values_per_pixel for color
- * components, regardless of the actual storage order;
- * we arrange things so that sptr/sbit and dptr/dbit
- * always point to the right place.
- */
- for (j = 0; j <= values_per_pixel; ++j) {
- uint dest_v, source_v, result;
- #define set_clamped(r, v)\
- BEGIN if ( (r = (v)) > dest_max ) r = dest_max; END
- if (j == 0) {
- source_v = source_alpha;
- dest_v = dest_alpha;
- } else {
- if (constant_colors)
- source_v = pcp->source_values[j - 1];
- else {
- get_value(source_v, sptr, sbit, source_bpv, source_max);
- advance(sptr, sbit, source_bpv);
- }
- get_value(dest_v, dptr, dbit, dest_bpv, dest_max);
- #ifdef PREMULTIPLY_TOWARDS_WHITE
- source_v -= source_bias;
- dest_v -= dest_bias;
- #endif
- }
- switch (pcp->cop) {
- case composite_Clear:
- /*
- * The NeXT documentation doesn't say this, but the CLEAR
- * operation sets not only alpha but also all the color
- * values to 0.
- */
- result = 0;
- break;
- case composite_Copy:
- result = source_v;
- break;
- case composite_PlusD:
- /*
- * This is the only case where we have to worry about
- * clamping a possibly negative result.
- */
- result = source_v + dest_v;
- result = (result < dest_max ? 0 : result - dest_max);
- break;
- case composite_PlusL:
- set_clamped(result, source_v + dest_v);
- break;
- case composite_Sover:
- set_clamped(result, source_v + nfr(dest_v, source_alpha, source_max));
- break;
- case composite_Dover:
- set_clamped(result, nfr(source_v, dest_alpha, dest_max) + dest_v);
- break;
- case composite_Sin:
- result = fr(source_v, dest_alpha);
- break;
- case composite_Din:
- result = fr(dest_v, source_alpha);
- break;
- case composite_Sout:
- result = nfr(source_v, dest_alpha, dest_max);
- break;
- case composite_Dout:
- result = nfr(dest_v, source_alpha, source_max);
- break;
- case composite_Satop:
- set_clamped(result, fr(source_v, dest_alpha) +
- nfr(dest_v, source_alpha, source_max));
- break;
- case composite_Datop:
- set_clamped(result, nfr(source_v, dest_alpha, dest_max) +
- fr(dest_v, source_alpha));
- break;
- case composite_Xor:
- set_clamped(result, nfr(source_v, dest_alpha, dest_max) +
- nfr(dest_v, source_alpha, source_max));
- break;
- case composite_Highlight:
- /*
- * Bizarre but true: this operation converts white and
- * light gray into each other, and leaves all other values
- * unchanged. We only implement it properly for gray-scale
- * devices.
- */
- if (j != 0 && !((source_v ^ highlight_value) & ~1))
- result = source_v ^ 1;
- else
- result = source_v;
- break;
- case composite_Dissolve:
- /*
- * In this case, and only this case, we need to worry about
- * source and dest having different bpv values. For the
- * moment, we wimp out and do everything in floating point.
- */
- result = (uint) (source_v * source_delta + dest_v * dest_delta);
- break;
- default:
- return_error(gs_error_rangecheck);
- }
- /*
- * Store the result. We don't have to worry about
- * destinations that don't store alpha, because we don't
- * even compute an alpha value in that case.
- */
- #ifdef PREMULTIPLY_TOWARDS_WHITE
- if (j == 0) {
- result_alpha = result;
- result_bias = dest_max - result_alpha;
- if (dest_alpha_j != 0)
- continue;
- } else {
- result += result_bias;
- }
- #else
- if (j == 0 && dest_alpha_j != 0) {
- result_alpha = result;
- continue;
- }
- #endif
- put_value(result, dptr, dbit, dest_bpv, dbyte);
- }
- /* Skip a trailing source alpha value. */
- if (source_alpha_j > 0)
- advance(sptr, sbit, source_bpv);
- /* Store a trailing destination alpha value. */
- if (dest_alpha_j > 0)
- put_value(result_alpha, dptr, dbit, dest_bpv, dbyte);
- #undef get_value
- #undef put_value
- #undef advance
- }
- /* Store any partial output byte. */
- sample_store_flush(dptr, dbit, dest_bpv, dbyte);
- }
- return 0;
- }
|