summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'base/gxblend.c')
-rw-r--r--base/gxblend.c5797
1 files changed, 5797 insertions, 0 deletions
diff --git a/base/gxblend.c b/base/gxblend.c
new file mode 100644
index 00000000..59d0deaf
--- /dev/null
+++ b/base/gxblend.c
@@ -0,0 +1,5797 @@
+/* Copyright (C) 2001-2019 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato,
+ CA 94945, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* PDF 1.4 blending functions */
+
+#include "memory_.h"
+#include "gx.h"
+#include "gp.h"
+#include "gstparam.h"
+#include "gxblend.h"
+#include "gxcolor2.h"
+#include "gsicc_cache.h"
+#include "gsicc_manage.h"
+#include "gdevp14.h"
+#include "gsrect.h" /* for rect_merge */
+#include "math_.h" /* for ceil, floor */
+#ifdef WITH_CAL
+#include "cal.h"
+#endif
+
+typedef int art_s32;
+
+#if RAW_DUMP
+extern unsigned int global_index;
+extern unsigned int clist_band_count;
+#endif
+
+#undef TRACK_COMPOSE_GROUPS
+#ifdef TRACK_COMPOSE_GROUPS
+int compose_groups[1<<17];
+
+static int track_compose_groups = 0;
+
+static void dump_track_compose_groups(void);
+#endif
+
+
+/* For spot colors, blend modes must be white preserving and separable. The
+ * order of the blend modes should be reordered so this is a single compare */
+bool
+blend_valid_for_spot(gs_blend_mode_t blend_mode)
+{
+ if (blend_mode == BLEND_MODE_Difference ||
+ blend_mode == BLEND_MODE_Exclusion ||
+ blend_mode == BLEND_MODE_Hue ||
+ blend_mode == BLEND_MODE_Saturation ||
+ blend_mode == BLEND_MODE_Color ||
+ blend_mode == BLEND_MODE_Luminosity)
+ return false;
+ else
+ return true;
+}
+
+/* This function is used for mapping the SMask source to a
+ monochrome luminosity value which basically is the alpha value
+ Note, that separation colors are not allowed here. Everything
+ must be in CMYK, RGB or monochrome. */
+
+/* Note, data is planar */
+static void
+do_smask_luminosity_mapping(int num_rows, int num_cols, int n_chan, int row_stride,
+ int plane_stride, const byte *gs_restrict src,
+ byte *gs_restrict dst, bool isadditive,
+ gs_transparency_mask_subtype_t SMask_SubType
+#if RAW_DUMP
+ , const gs_memory_t *mem
+#endif
+ )
+{
+ int x,y;
+ int mask_alpha_offset,mask_C_offset,mask_M_offset,mask_Y_offset,mask_K_offset;
+ int mask_R_offset,mask_G_offset,mask_B_offset;
+ byte *dstptr;
+
+#if RAW_DUMP
+ dump_raw_buffer(mem, num_rows, row_stride, n_chan,
+ plane_stride, row_stride,
+ "Raw_Mask", src, 0);
+
+ global_index++;
+#endif
+ dstptr = (byte *)dst;
+ /* If subtype is Luminosity then we should just grab the Y channel */
+ if ( SMask_SubType == TRANSPARENCY_MASK_Luminosity ){
+ memcpy(dstptr, &(src[plane_stride]), plane_stride);
+ return;
+ }
+ /* If we are alpha type, then just grab that */
+ /* We need to optimize this so that we are only drawing alpha in the rect fills */
+ if ( SMask_SubType == TRANSPARENCY_MASK_Alpha ){
+ mask_alpha_offset = (n_chan - 1) * plane_stride;
+ memcpy(dstptr, &(src[mask_alpha_offset]), plane_stride);
+ return;
+ }
+ /* To avoid the if statement inside this loop,
+ decide on additive or subractive now */
+ if (isadditive || n_chan == 2) {
+ /* Now we need to split Gray from RGB */
+ if( n_chan == 2 ) {
+ /* Gray Scale case */
+ mask_alpha_offset = (n_chan - 1) * plane_stride;
+ mask_R_offset = 0;
+ for ( y = 0; y < num_rows; y++ ) {
+ for ( x = 0; x < num_cols; x++ ){
+ /* With the current design this will indicate if
+ we ever did a fill at this pixel. if not then move on.
+ This could have some serious optimization */
+ if (src[x + mask_alpha_offset] != 0x00) {
+ dstptr[x] = src[x + mask_R_offset];
+ }
+ }
+ dstptr += row_stride;
+ mask_alpha_offset += row_stride;
+ mask_R_offset += row_stride;
+ }
+ } else {
+ /* RGB case */
+ mask_R_offset = 0;
+ mask_G_offset = plane_stride;
+ mask_B_offset = 2 * plane_stride;
+ mask_alpha_offset = (n_chan - 1) * plane_stride;
+ for ( y = 0; y < num_rows; y++ ) {
+ for ( x = 0; x < num_cols; x++ ){
+ /* With the current design this will indicate if
+ we ever did a fill at this pixel. if not then move on */
+ if (src[x + mask_alpha_offset] != 0x00) {
+ /* Get luminosity of Device RGB value */
+ float temp;
+ temp = ( 0.30 * src[x + mask_R_offset] +
+ 0.59 * src[x + mask_G_offset] +
+ 0.11 * src[x + mask_B_offset] );
+ temp = temp * (1.0 / 255.0 ); /* May need to be optimized */
+ dstptr[x] = float_color_to_byte_color(temp);
+ }
+ }
+ dstptr += row_stride;
+ mask_alpha_offset += row_stride;
+ mask_R_offset += row_stride;
+ mask_G_offset += row_stride;
+ mask_B_offset += row_stride;
+ }
+ }
+ } else {
+ /* CMYK case */
+ mask_alpha_offset = (n_chan - 1) * plane_stride;
+ mask_C_offset = 0;
+ mask_M_offset = plane_stride;
+ mask_Y_offset = 2 * plane_stride;
+ mask_K_offset = 3 * plane_stride;
+ for ( y = 0; y < num_rows; y++ ){
+ for ( x = 0; x < num_cols; x++ ){
+ /* With the current design this will indicate if
+ we ever did a fill at this pixel. if not then move on */
+ if (src[x + mask_alpha_offset] != 0x00){
+ /* PDF spec says to use Y = 0.30 (1 - C)(1 - K) +
+ 0.59 (1 - M)(1 - K) + 0.11 (1 - Y)(1 - K) */
+ /* For device CMYK */
+ float temp;
+ temp = ( 0.30 * ( 0xff - src[x + mask_C_offset]) +
+ 0.59 * ( 0xff - src[x + mask_M_offset]) +
+ 0.11 * ( 0xff - src[x + mask_Y_offset]) ) *
+ ( 0xff - src[x + mask_K_offset]);
+ temp = temp * (1.0 / 65025.0 ); /* May need to be optimized */
+ dstptr[x] = float_color_to_byte_color(temp);
+ }
+ }
+ dstptr += row_stride;
+ mask_alpha_offset += row_stride;
+ mask_C_offset += row_stride;
+ mask_M_offset += row_stride;
+ mask_Y_offset += row_stride;
+ mask_K_offset += row_stride;
+ }
+ }
+}
+
+static void
+do_smask_luminosity_mapping_16(int num_rows, int num_cols, int n_chan, int row_stride,
+ int plane_stride, const uint16_t *gs_restrict src,
+ uint16_t *gs_restrict dst, bool isadditive,
+ gs_transparency_mask_subtype_t SMask_SubType
+#if RAW_DUMP
+ , const gs_memory_t *mem
+#endif
+ )
+{
+ int x,y;
+ int mask_alpha_offset,mask_C_offset,mask_M_offset,mask_Y_offset,mask_K_offset;
+ int mask_R_offset,mask_G_offset,mask_B_offset;
+ uint16_t *dstptr;
+
+#if RAW_DUMP
+ dump_raw_buffer_be(mem, num_rows, row_stride, n_chan,
+ plane_stride, row_stride,
+ "Raw_Mask", (const byte *)src, 0);
+
+ global_index++;
+#endif
+ dstptr = dst;
+ /* If subtype is Luminosity then we should just grab the Y channel */
+ if ( SMask_SubType == TRANSPARENCY_MASK_Luminosity ){
+ memcpy(dstptr, &(src[plane_stride]), plane_stride*2);
+ return;
+ }
+ /* If we are alpha type, then just grab that */
+ /* We need to optimize this so that we are only drawing alpha in the rect fills */
+ if ( SMask_SubType == TRANSPARENCY_MASK_Alpha ){
+ mask_alpha_offset = (n_chan - 1) * plane_stride;
+ memcpy(dstptr, &(src[mask_alpha_offset]), plane_stride*2);
+ return;
+ }
+ /* To avoid the if statement inside this loop,
+ decide on additive or subractive now */
+ if (isadditive || n_chan == 2) {
+ /* Now we need to split Gray from RGB */
+ if( n_chan == 2 ) {
+ /* Gray Scale case */
+ mask_alpha_offset = (n_chan - 1) * plane_stride;
+ mask_R_offset = 0;
+ for ( y = 0; y < num_rows; y++ ) {
+ for ( x = 0; x < num_cols; x++ ){
+ /* With the current design this will indicate if
+ we ever did a fill at this pixel. if not then move on.
+ This could have some serious optimization */
+ if (src[x + mask_alpha_offset] != 0x00) {
+ dstptr[x] = src[x + mask_R_offset];
+ }
+ }
+ dstptr += row_stride;
+ mask_alpha_offset += row_stride;
+ mask_R_offset += row_stride;
+ }
+ } else {
+ /* RGB case */
+ mask_R_offset = 0;
+ mask_G_offset = plane_stride;
+ mask_B_offset = 2 * plane_stride;
+ mask_alpha_offset = (n_chan - 1) * plane_stride;
+ for ( y = 0; y < num_rows; y++ ) {
+ for ( x = 0; x < num_cols; x++ ){
+ /* With the current design this will indicate if
+ we ever did a fill at this pixel. if not then move on */
+ if (src[x + mask_alpha_offset] != 0x00) {
+ /* Get luminosity of Device RGB value */
+ float temp;
+ temp = ( 0.30 * src[x + mask_R_offset] +
+ 0.59 * src[x + mask_G_offset] +
+ 0.11 * src[x + mask_B_offset] );
+ temp = temp * (1.0 / 65535.0 ); /* May need to be optimized */
+ dstptr[x] = float_color_to_color16(temp);
+ }
+ }
+ dstptr += row_stride;
+ mask_alpha_offset += row_stride;
+ mask_R_offset += row_stride;
+ mask_G_offset += row_stride;
+ mask_B_offset += row_stride;
+ }
+ }
+ } else {
+ /* CMYK case */
+ mask_alpha_offset = (n_chan - 1) * plane_stride;
+ mask_C_offset = 0;
+ mask_M_offset = plane_stride;
+ mask_Y_offset = 2 * plane_stride;
+ mask_K_offset = 3 * plane_stride;
+ for ( y = 0; y < num_rows; y++ ){
+ for ( x = 0; x < num_cols; x++ ){
+ /* With the current design this will indicate if
+ we ever did a fill at this pixel. if not then move on */
+ if (src[x + mask_alpha_offset] != 0x00){
+ /* PDF spec says to use Y = 0.30 (1 - C)(1 - K) +
+ 0.59 (1 - M)(1 - K) + 0.11 (1 - Y)(1 - K) */
+ /* For device CMYK */
+ float temp;
+ temp = ( 0.30 * ( 0xffff - src[x + mask_C_offset]) +
+ 0.59 * ( 0xffff - src[x + mask_M_offset]) +
+ 0.11 * ( 0xffff - src[x + mask_Y_offset]) ) *
+ ( 0xffff - src[x + mask_K_offset]);
+ temp = temp * (1.0 / (65535.0*65535.0) ); /* May need to be optimized */
+ dstptr[x] = float_color_to_color16(temp);
+ }
+ }
+ dstptr += row_stride;
+ mask_alpha_offset += row_stride;
+ mask_C_offset += row_stride;
+ mask_M_offset += row_stride;
+ mask_Y_offset += row_stride;
+ mask_K_offset += row_stride;
+ }
+ }
+}
+
+void
+smask_luminosity_mapping(int num_rows, int num_cols, int n_chan, int row_stride,
+ int plane_stride, const byte *gs_restrict src,
+ byte *gs_restrict dst, bool isadditive,
+ gs_transparency_mask_subtype_t SMask_SubType, bool deep
+#if RAW_DUMP
+ , const gs_memory_t *mem
+#endif
+ )
+{
+ if (deep)
+ do_smask_luminosity_mapping_16(num_rows, num_cols, n_chan, row_stride>>1,
+ plane_stride>>1, (const uint16_t *)(const void *)src,
+ (uint16_t *)(void *)dst, isadditive, SMask_SubType
+#if RAW_DUMP
+ , mem
+#endif
+ );
+ else
+ do_smask_luminosity_mapping(num_rows, num_cols, n_chan, row_stride,
+ plane_stride, src, dst, isadditive, SMask_SubType
+#if RAW_DUMP
+ , mem
+#endif
+ );
+}
+
+/* soft mask gray buffer should be blended with its transparency planar data
+ during the pop for a luminosity case if we have a soft mask within a soft
+ mask. This situation is detected in the code so that we only do this
+ blending in those rare situations */
+void
+smask_blend(byte *gs_restrict src, int width, int height, int rowstride,
+ int planestride, bool deep)
+{
+ int x, y;
+ int position;
+
+ if (deep) {
+ uint16_t comp, a;
+ const uint16_t bg = 0;
+ uint16_t *src16 = (uint16_t *)(void *)src;
+ rowstride >>= 1;
+ planestride >>= 1;
+ for (y = 0; y < height; y++) {
+ position = y * rowstride;
+ for (x = 0; x < width; x++) {
+ a = src16[position + planestride];
+ if (a == 0) {
+ src16[position] = 0;
+ } else if (a != 0xffff) {
+ a ^= 0xffff;
+ a += a>>15;
+ comp = src16[position];
+ comp += (((bg - comp) * a) + 0x8000)>>16;
+ /* Errors in bit 16 and above are ignored */
+ src16[position] = comp;
+ }
+ position+=1;
+ }
+ }
+ } else {
+ byte comp, a;
+ int tmp;
+ const byte bg = 0;
+ for (y = 0; y < height; y++) {
+ position = y * rowstride;
+ for (x = 0; x < width; x++) {
+ a = src[position + planestride];
+ if ((a + 1) & 0xfe) {
+ a ^= 0xff;
+ comp = src[position];
+ tmp = ((bg - comp) * a) + 0x80;
+ comp += (tmp + (tmp >> 8)) >> 8;
+ src[position] = comp;
+ } else if (a == 0) {
+ src[position] = 0;
+ }
+ position+=1;
+ }
+ }
+ }
+}
+
+void smask_copy(int num_rows, int num_cols, int row_stride,
+ byte *gs_restrict src, const byte *gs_restrict dst)
+{
+ int y;
+ byte *dstptr,*srcptr;
+
+ dstptr = (byte *)dst;
+ srcptr = src;
+ for ( y = 0; y < num_rows; y++ ) {
+ memcpy(dstptr,srcptr,num_cols);
+ dstptr += row_stride;
+ srcptr += row_stride;
+ }
+}
+
+void smask_icc(gx_device *dev, int num_rows, int num_cols, int n_chan,
+ int row_stride, int plane_stride, byte *gs_restrict src, const byte *gs_restrict dst,
+ gsicc_link_t *icclink, bool deep)
+{
+ gsicc_bufferdesc_t input_buff_desc;
+ gsicc_bufferdesc_t output_buff_desc;
+
+#if RAW_DUMP
+ dump_raw_buffer(dev->memory, num_rows, row_stride>>deep, n_chan,
+ plane_stride, row_stride,
+ "Raw_Mask_ICC", src, deep);
+ global_index++;
+#endif
+/* Set up the buffer descriptors. Note that pdf14 always has
+ the alpha channels at the back end (last planes).
+ We will just handle that here and let the CMM know
+ nothing about it */
+
+ gsicc_init_buffer(&input_buff_desc, n_chan-1, 1<<deep,
+ false, false, true, plane_stride, row_stride,
+ num_rows, num_cols);
+ gsicc_init_buffer(&output_buff_desc, 1, 1<<deep,
+ false, false, true, plane_stride,
+ row_stride, num_rows, num_cols);
+ /* Transform the data */
+ (icclink->procs.map_buffer)(dev, icclink, &input_buff_desc, &output_buff_desc,
+ (void*) src, (void*) dst);
+}
+
+void
+art_blend_luminosity_rgb_8(int n_chan, byte *gs_restrict dst, const byte *gs_restrict backdrop,
+ const byte *gs_restrict src)
+{
+ int rb = backdrop[0], gb = backdrop[1], bb = backdrop[2];
+ int rs = src[0], gs = src[1], bs = src[2];
+ int delta_y;
+ int r, g, b;
+
+ /*
+ * From section 7.4 of the PDF 1.5 specification, for RGB, the luminosity
+ * is: Y = 0.30 R + 0.59 G + 0.11 B)
+ */
+ delta_y = ((rs - rb) * 77 + (gs - gb) * 151 + (bs - bb) * 28 + 0x80) >> 8;
+ r = rb + delta_y;
+ g = gb + delta_y;
+ b = bb + delta_y;
+ if ((r | g | b) & 0x100) {
+ int y;
+ int scale;
+
+ y = (rs * 77 + gs * 151 + bs * 28 + 0x80) >> 8;
+ if (delta_y > 0) {
+ int max;
+
+ max = r > g ? r : g;
+ max = b > max ? b : max;
+ scale = ((255 - y) << 16) / (max - y);
+ } else {
+ int min;
+
+ min = r < g ? r : g;
+ min = b < min ? b : min;
+ scale = (y << 16) / (y - min);
+ }
+ r = y + (((r - y) * scale + 0x8000) >> 16);
+ g = y + (((g - y) * scale + 0x8000) >> 16);
+ b = y + (((b - y) * scale + 0x8000) >> 16);
+ }
+ dst[0] = r;
+ dst[1] = g;
+ dst[2] = b;
+}
+
+void
+art_blend_luminosity_rgb_16(int n_chan, uint16_t *gs_restrict dst, const uint16_t *gs_restrict backdrop,
+ const uint16_t *gs_restrict src)
+{
+ int rb = backdrop[0], gb = backdrop[1], bb = backdrop[2];
+ int rs = src[0], gs = src[1], bs = src[2];
+ int delta_y;
+ int r, g, b;
+
+ /*
+ * From section 7.4 of the PDF 1.5 specification, for RGB, the luminosity
+ * is: Y = 0.30 R + 0.59 G + 0.11 B)
+ */
+ delta_y = ((rs - rb) * 77 + (gs - gb) * 151 + (bs - bb) * 28 + 0x80) >> 8;
+ r = rb + delta_y;
+ g = gb + delta_y;
+ b = bb + delta_y;
+ if ((r | g | b) & 0x10000) {
+ int y;
+ int64_t scale;
+
+ /* Resort to 64 bit to avoid calculations with scale overflowing */
+ y = (rs * 77 + gs * 151 + bs * 28 + 0x80) >> 8;
+ if (delta_y > 0) {
+ int max;
+
+ max = r > g ? r : g;
+ max = b > max ? b : max;
+ scale = ((65535 - (int64_t)y) << 16) / (max - y);
+ } else {
+ int min;
+
+ min = r < g ? r : g;
+ min = b < min ? b : min;
+ scale = (((int64_t)y) << 16) / (y - min);
+ }
+ r = y + (((r - y) * scale + 0x8000) >> 16);
+ g = y + (((g - y) * scale + 0x8000) >> 16);
+ b = y + (((b - y) * scale + 0x8000) >> 16);
+ }
+ dst[0] = r;
+ dst[1] = g;
+ dst[2] = b;
+}
+
+void
+art_blend_luminosity_custom_8(int n_chan, byte *gs_restrict dst, const byte *gs_restrict backdrop,
+ const byte *gs_restrict src)
+{
+ int delta_y = 0, test = 0;
+ int r[ART_MAX_CHAN];
+ int i;
+
+ /*
+ * Since we do not know the details of the blending color space, we are
+ * simply using the average as the luminosity. First we need the
+ * delta luminosity values.
+ */
+ for (i = 0; i < n_chan; i++)
+ delta_y += src[i] - backdrop[i];
+ delta_y = (delta_y + n_chan / 2) / n_chan;
+ for (i = 0; i < n_chan; i++) {
+ r[i] = backdrop[i] + delta_y;
+ test |= r[i];
+ }
+
+ if (test & 0x100) {
+ int y;
+ int scale;
+
+ /* Assume that the luminosity is simply the average of the backdrop. */
+ y = src[0];
+ for (i = 1; i < n_chan; i++)
+ y += src[i];
+ y = (y + n_chan / 2) / n_chan;
+
+ if (delta_y > 0) {
+ int max;
+
+ max = r[0];
+ for (i = 1; i < n_chan; i++)
+ max = max(max, r[i]);
+ scale = ((255 - y) << 16) / (max - y);
+ } else {
+ int min;
+
+ min = r[0];
+ for (i = 1; i < n_chan; i++)
+ min = min(min, r[i]);
+ scale = (y << 16) / (y - min);
+ }
+ for (i = 0; i < n_chan; i++)
+ r[i] = y + (((r[i] - y) * scale + 0x8000) >> 16);
+ }
+ for (i = 0; i < n_chan; i++)
+ dst[i] = r[i];
+}
+
+void
+art_blend_luminosity_custom_16(int n_chan, uint16_t *gs_restrict dst, const uint16_t *gs_restrict backdrop,
+ const uint16_t *gs_restrict src)
+{
+ int delta_y = 0, test = 0;
+ int r[ART_MAX_CHAN];
+ int i;
+
+ /*
+ * Since we do not know the details of the blending color space, we are
+ * simply using the average as the luminosity. First we need the
+ * delta luminosity values.
+ */
+ for (i = 0; i < n_chan; i++)
+ delta_y += src[i] - backdrop[i];
+ delta_y = (delta_y + n_chan / 2) / n_chan;
+ for (i = 0; i < n_chan; i++) {
+ r[i] = backdrop[i] + delta_y;
+ test |= r[i];
+ }
+
+ if (test & 0x10000) {
+ int y;
+ int64_t scale;
+
+ /* Resort to 64bit to avoid calculations with scale overflowing */
+ /* Assume that the luminosity is simply the average of the backdrop. */
+ y = src[0];
+ for (i = 1; i < n_chan; i++)
+ y += src[i];
+ y = (y + n_chan / 2) / n_chan;
+
+ if (delta_y > 0) {
+ int max;
+
+ max = r[0];
+ for (i = 1; i < n_chan; i++)
+ max = max(max, r[i]);
+ scale = ((65535 - (int64_t)y) << 16) / (max - y);
+ } else {
+ int min;
+
+ min = r[0];
+ for (i = 1; i < n_chan; i++)
+ min = min(min, r[i]);
+ scale = (((int64_t)y) << 16) / (y - min);
+ }
+ for (i = 0; i < n_chan; i++)
+ r[i] = y + (((r[i] - y) * scale + 0x8000) >> 16);
+ }
+ for (i = 0; i < n_chan; i++)
+ dst[i] = r[i];
+}
+
+/*
+ * The PDF 1.4 spec. does not give the details of the math involved in the
+ * luminosity blending. All we are given is:
+ * "Creates a color with the luminance of the source color and the hue
+ * and saturation of the backdrop color. This produces an inverse
+ * effect to that of the Color mode."
+ * From section 7.4 of the PDF 1.5 specification, which is duscussing soft
+ * masks, we are given that, for CMYK, the luminosity is:
+ * Y = 0.30 (1 - C)(1 - K) + 0.59 (1 - M)(1 - K) + 0.11 (1 - Y)(1 - K)
+ * However the results of this equation do not match the results seen from
+ * Illustrator CS. Very different results are obtained if process gray
+ * (.5, .5, .5, 0) is blended over pure cyan, versus gray (0, 0, 0, .5) over
+ * the same pure cyan. The first gives a medium cyan while the later gives a
+ * medium gray. This routine seems to match Illustrator's actions. C, M and Y
+ * are treated similar to RGB in the previous routine and black is treated
+ * separately.
+ *
+ * Our component values have already been complemented, i.e. (1 - X).
+ */
+void
+art_blend_luminosity_cmyk_8(int n_chan, byte *gs_restrict dst, const byte *gs_restrict backdrop,
+ const byte *gs_restrict src)
+{
+ int i;
+
+ /* Treat CMY the same as RGB. */
+ art_blend_luminosity_rgb_8(3, dst, backdrop, src);
+ for (i = 3; i < n_chan; i++)
+ dst[i] = src[i];
+}
+
+void
+art_blend_luminosity_cmyk_16(int n_chan, uint16_t *gs_restrict dst, const uint16_t *gs_restrict backdrop,
+ const uint16_t *gs_restrict src)
+{
+ int i;
+
+ /* Treat CMY the same as RGB. */
+ art_blend_luminosity_rgb_16(3, dst, backdrop, src);
+ for (i = 3; i < n_chan; i++)
+ dst[i] = src[i];
+}
+
+void
+art_blend_saturation_rgb_8(int n_chan, byte *gs_restrict dst, const byte *gs_restrict backdrop,
+ const byte *gs_restrict src)
+{
+ int rb = backdrop[0], gb = backdrop[1], bb = backdrop[2];
+ int rs = src[0], gs = src[1], bs = src[2];
+ int minb, maxb;
+ int mins, maxs;
+ int y;
+ int scale;
+ int r, g, b;
+
+ minb = rb < gb ? rb : gb;
+ minb = minb < bb ? minb : bb;
+ maxb = rb > gb ? rb : gb;
+ maxb = maxb > bb ? maxb : bb;
+ if (minb == maxb) {
+ /* backdrop has zero saturation, avoid divide by 0 */
+ dst[0] = gb;
+ dst[1] = gb;
+ dst[2] = gb;
+ return;
+ }
+
+ mins = rs < gs ? rs : gs;
+ mins = mins < bs ? mins : bs;
+ maxs = rs > gs ? rs : gs;
+ maxs = maxs > bs ? maxs : bs;
+
+ scale = ((maxs - mins) << 16) / (maxb - minb);
+ y = (rb * 77 + gb * 151 + bb * 28 + 0x80) >> 8;
+ r = y + ((((rb - y) * scale) + 0x8000) >> 16);
+ g = y + ((((gb - y) * scale) + 0x8000) >> 16);
+ b = y + ((((bb - y) * scale) + 0x8000) >> 16);
+
+ if ((r | g | b) & 0x100) {
+ int scalemin, scalemax;
+ int min, max;
+
+ min = r < g ? r : g;
+ min = min < b ? min : b;
+ max = r > g ? r : g;
+ max = max > b ? max : b;
+
+ if (min < 0)
+ scalemin = (y << 16) / (y - min);
+ else
+ scalemin = 0x10000;
+
+ if (max > 255)
+ scalemax = ((255 - y) << 16) / (max - y);
+ else
+ scalemax = 0x10000;
+
+ scale = scalemin < scalemax ? scalemin : scalemax;
+ r = y + (((r - y) * scale + 0x8000) >> 16);
+ g = y + (((g - y) * scale + 0x8000) >> 16);
+ b = y + (((b - y) * scale + 0x8000) >> 16);
+ }
+
+ dst[0] = r;
+ dst[1] = g;
+ dst[2] = b;
+}
+
+void
+art_blend_saturation_rgb_16(int n_chan, uint16_t *gs_restrict dst, const uint16_t *gs_restrict backdrop,
+ const uint16_t *gs_restrict src)
+{
+ int rb = backdrop[0], gb = backdrop[1], bb = backdrop[2];
+ int rs = src[0], gs = src[1], bs = src[2];
+ int minb, maxb;
+ int mins, maxs;
+ int y;
+ int64_t scale;
+ int r, g, b;
+
+ minb = rb < gb ? rb : gb;
+ minb = minb < bb ? minb : bb;
+ maxb = rb > gb ? rb : gb;
+ maxb = maxb > bb ? maxb : bb;
+ if (minb == maxb) {
+ /* backdrop has zero saturation, avoid divide by 0 */
+ dst[0] = gb;
+ dst[1] = gb;
+ dst[2] = gb;
+ return;
+ }
+
+ mins = rs < gs ? rs : gs;
+ mins = mins < bs ? mins : bs;
+ maxs = rs > gs ? rs : gs;
+ maxs = maxs > bs ? maxs : bs;
+
+ scale = (((int64_t)(maxs - mins)) << 16) / (maxb - minb);
+ y = (rb * 77 + gb * 151 + bb * 28 + 0x80) >> 8;
+ r = y + ((((rb - y) * scale) + 0x8000) >> 16);
+ g = y + ((((gb - y) * scale) + 0x8000) >> 16);
+ b = y + ((((bb - y) * scale) + 0x8000) >> 16);
+
+ if ((r | g | b) & 0x10000) {
+ int64_t scalemin, scalemax;
+ int min, max;
+
+ min = r < g ? r : g;
+ min = min < b ? min : b;
+ max = r > g ? r : g;
+ max = max > b ? max : b;
+
+ if (min < 0)
+ scalemin = ((int64_t)(y << 16)) / (y - min);
+ else
+ scalemin = 0x10000;
+
+ if (max > 65535)
+ scalemax = (((int64_t)(65535 - y)) << 16) / (max - y);
+ else
+ scalemax = 0x10000;
+
+ scale = scalemin < scalemax ? scalemin : scalemax;
+ r = y + (((r - y) * scale + 0x8000) >> 16);
+ g = y + (((g - y) * scale + 0x8000) >> 16);
+ b = y + (((b - y) * scale + 0x8000) >> 16);
+ }
+
+ dst[0] = r;
+ dst[1] = g;
+ dst[2] = b;
+}
+
+void
+art_blend_saturation_custom_8(int n_chan, byte *gs_restrict dst, const byte *gs_restrict backdrop,
+ const byte *gs_restrict src)
+{
+ int minb, maxb;
+ int mins, maxs;
+ int y;
+ int scale;
+ int r[ART_MAX_CHAN];
+ int test = 0;
+ int temp, i;
+
+ /* Determine min and max of the backdrop */
+ minb = maxb = temp = backdrop[0];
+ for (i = 1; i < n_chan; i++) {
+ temp = backdrop[i];
+ minb = min(minb, temp);
+ maxb = max(maxb, temp);
+ }
+
+ if (minb == maxb) {
+ /* backdrop has zero saturation, avoid divide by 0 */
+ for (i = 0; i < n_chan; i++)
+ dst[i] = temp;
+ return;
+ }
+
+ /* Determine min and max of the source */
+ mins = maxs = src[0];
+ for (i = 1; i < n_chan; i++) {
+ temp = src[i];
+ mins = min(minb, temp);
+ maxs = max(minb, temp);
+ }
+
+ scale = ((maxs - mins) << 16) / (maxb - minb);
+
+ /* Assume that the saturation is simply the average of the backdrop. */
+ y = backdrop[0];
+ for (i = 1; i < n_chan; i++)
+ y += backdrop[i];
+ y = (y + n_chan / 2) / n_chan;
+
+ /* Calculate the saturated values */
+ for (i = 0; i < n_chan; i++) {
+ r[i] = y + ((((backdrop[i] - y) * scale) + 0x8000) >> 16);
+ test |= r[i];
+ }
+
+ if (test & 0x100) {
+ int scalemin, scalemax;
+ int min, max;
+
+ /* Determine min and max of our blended values */
+ min = max = temp = r[0];
+ for (i = 1; i < n_chan; i++) {
+ temp = src[i];
+ min = min(min, temp);
+ max = max(max, temp);
+ }
+
+ if (min < 0)
+ scalemin = (y << 16) / (y - min);
+ else
+ scalemin = 0x10000;
+
+ if (max > 255)
+ scalemax = ((255 - y) << 16) / (max - y);
+ else
+ scalemax = 0x10000;
+
+ scale = scalemin < scalemax ? scalemin : scalemax;
+ for (i = 0; i < n_chan; i++)
+ r[i] = y + (((r[i] - y) * scale + 0x8000) >> 16);
+ }
+
+ for (i = 0; i < n_chan; i++)
+ dst[i] = r[i];
+}
+
+void
+art_blend_saturation_custom_16(int n_chan, uint16_t *gs_restrict dst, const uint16_t *gs_restrict backdrop,
+ const uint16_t *gs_restrict src)
+{
+ int minb, maxb;
+ int mins, maxs;
+ int y;
+ int scale;
+ int r[ART_MAX_CHAN];
+ int test = 0;
+ int temp, i;
+
+ /* FIXME: Test this */
+
+ /* Determine min and max of the backdrop */
+ minb = maxb = temp = backdrop[0];
+ for (i = 1; i < n_chan; i++) {
+ temp = backdrop[i];
+ minb = min(minb, temp);
+ maxb = max(maxb, temp);
+ }
+
+ if (minb == maxb) {
+ /* backdrop has zero saturation, avoid divide by 0 */
+ for (i = 0; i < n_chan; i++)
+ dst[i] = temp;
+ return;
+ }
+
+ /* Determine min and max of the source */
+ mins = maxs = src[0];
+ for (i = 1; i < n_chan; i++) {
+ temp = src[i];
+ mins = min(minb, temp);
+ maxs = max(minb, temp);
+ }
+
+ scale = ((maxs - mins) << 16) / (maxb - minb);
+
+ /* Assume that the saturation is simply the average of the backdrop. */
+ y = backdrop[0];
+ for (i = 1; i < n_chan; i++)
+ y += backdrop[i];
+ y = (y + n_chan / 2) / n_chan;
+
+ /* Calculate the saturated values */
+ for (i = 0; i < n_chan; i++) {
+ r[i] = y + ((((backdrop[i] - y) * scale) + 0x8000) >> 16);
+ test |= r[i];
+ }
+
+ if (test & 0x10000) {
+ int scalemin, scalemax;
+ int min, max;
+
+ /* Determine min and max of our blended values */
+ min = max = temp = r[0];
+ for (i = 1; i < n_chan; i++) {
+ temp = src[i];
+ min = min(min, temp);
+ max = max(max, temp);
+ }
+
+ if (min < 0)
+ scalemin = (y << 16) / (y - min);
+ else
+ scalemin = 0x10000;
+
+ if (max > 65535)
+ scalemax = ((65535 - y) << 16) / (max - y);
+ else
+ scalemax = 0x10000;
+
+ scale = scalemin < scalemax ? scalemin : scalemax;
+ for (i = 0; i < n_chan; i++)
+ r[i] = y + (((r[i] - y) * scale + 0x8000) >> 16);
+ }
+
+ for (i = 0; i < n_chan; i++)
+ dst[i] = r[i];
+}
+
+/* Our component values have already been complemented, i.e. (1 - X). */
+void
+art_blend_saturation_cmyk_8(int n_chan, byte *gs_restrict dst, const byte *gs_restrict backdrop,
+ const byte *gs_restrict src)
+{
+ int i;
+
+ /* Treat CMY the same as RGB */
+ art_blend_saturation_rgb_8(3, dst, backdrop, src);
+ for (i = 3; i < n_chan; i++)
+ dst[i] = backdrop[i];
+}
+
+void
+art_blend_saturation_cmyk_16(int n_chan, uint16_t *gs_restrict dst, const uint16_t *gs_restrict backdrop,
+ const uint16_t *gs_restrict src)
+{
+ int i;
+
+ /* Treat CMY the same as RGB */
+ art_blend_saturation_rgb_16(3, dst, backdrop, src);
+ for (i = 3; i < n_chan; i++)
+ dst[i] = backdrop[i];
+}
+
+/* This array consists of floor ((x - x * x / 255.0) * 65536 / 255 +
+ 0.5) for x in [0..255]. */
+const unsigned int art_blend_sq_diff_8[256] = {
+ 0, 256, 510, 762, 1012, 1260, 1506, 1750, 1992, 2231, 2469, 2705,
+ 2939, 3171, 3401, 3628, 3854, 4078, 4300, 4519, 4737, 4953, 5166,
+ 5378, 5588, 5795, 6001, 6204, 6406, 6606, 6803, 6999, 7192, 7384,
+ 7573, 7761, 7946, 8129, 8311, 8490, 8668, 8843, 9016, 9188, 9357,
+ 9524, 9690, 9853, 10014, 10173, 10331, 10486, 10639, 10790, 10939,
+ 11086, 11232, 11375, 11516, 11655, 11792, 11927, 12060, 12191, 12320,
+ 12447, 12572, 12695, 12816, 12935, 13052, 13167, 13280, 13390, 13499,
+ 13606, 13711, 13814, 13914, 14013, 14110, 14205, 14297, 14388, 14477,
+ 14564, 14648, 14731, 14811, 14890, 14967, 15041, 15114, 15184, 15253,
+ 15319, 15384, 15446, 15507, 15565, 15622, 15676, 15729, 15779, 15827,
+ 15874, 15918, 15960, 16001, 16039, 16075, 16110, 16142, 16172, 16200,
+ 16227, 16251, 16273, 16293, 16311, 16327, 16341, 16354, 16364, 16372,
+ 16378, 16382, 16384, 16384, 16382, 16378, 16372, 16364, 16354, 16341,
+ 16327, 16311, 16293, 16273, 16251, 16227, 16200, 16172, 16142, 16110,
+ 16075, 16039, 16001, 15960, 15918, 15874, 15827, 15779, 15729, 15676,
+ 15622, 15565, 15507, 15446, 15384, 15319, 15253, 15184, 15114, 15041,
+ 14967, 14890, 14811, 14731, 14648, 14564, 14477, 14388, 14297, 14205,
+ 14110, 14013, 13914, 13814, 13711, 13606, 13499, 13390, 13280, 13167,
+ 13052, 12935, 12816, 12695, 12572, 12447, 12320, 12191, 12060, 11927,
+ 11792, 11655, 11516, 11375, 11232, 11086, 10939, 10790, 10639, 10486,
+ 10331, 10173, 10014, 9853, 9690, 9524, 9357, 9188, 9016, 8843, 8668,
+ 8490, 8311, 8129, 7946, 7761, 7573, 7384, 7192, 6999, 6803, 6606,
+ 6406, 6204, 6001, 5795, 5588, 5378, 5166, 4953, 4737, 4519, 4300,
+ 4078, 3854, 3628, 3401, 3171, 2939, 2705, 2469, 2231, 1992, 1750,
+ 1506, 1260, 1012, 762, 510, 256, 0
+};
+
+/* This array consists of SoftLight (x, 255) - x, for values of x in
+ the range [0..255] (normalized to [0..255 range). The original
+ values were directly sampled from Adobe Illustrator 9. I've fit a
+ quadratic spline to the SoftLight (x, 1) function as follows
+ (normalized to [0..1] range):
+
+ Anchor point (0, 0)
+ Control point (0.0755, 0.302)
+ Anchor point (0.18, 0.4245)
+ Control point (0.4263, 0.7131)
+ Anchor point (1, 1)
+
+ I don't believe this is _exactly_ the function that Adobe uses,
+ but it really should be close enough for all practical purposes. */
+const byte art_blend_soft_light_8[256] = {
+ 0, 3, 6, 9, 11, 14, 16, 19, 21, 23, 26, 28, 30, 32, 33, 35, 37, 39,
+ 40, 42, 43, 45, 46, 47, 48, 49, 51, 52, 53, 53, 54, 55, 56, 57, 57,
+ 58, 58, 59, 60, 60, 60, 61, 61, 62, 62, 62, 62, 63, 63, 63, 63, 63,
+ 63, 63, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 62, 62, 62,
+ 62, 62, 62, 62, 61, 61, 61, 61, 61, 61, 60, 60, 60, 60, 60, 59, 59,
+ 59, 59, 59, 58, 58, 58, 58, 57, 57, 57, 57, 56, 56, 56, 56, 55, 55,
+ 55, 55, 54, 54, 54, 54, 53, 53, 53, 52, 52, 52, 51, 51, 51, 51, 50,
+ 50, 50, 49, 49, 49, 48, 48, 48, 47, 47, 47, 46, 46, 46, 45, 45, 45,
+ 44, 44, 43, 43, 43, 42, 42, 42, 41, 41, 40, 40, 40, 39, 39, 39, 38,
+ 38, 37, 37, 37, 36, 36, 35, 35, 35, 34, 34, 33, 33, 33, 32, 32, 31,
+ 31, 31, 30, 30, 29, 29, 28, 28, 28, 27, 27, 26, 26, 25, 25, 25, 24,
+ 24, 23, 23, 22, 22, 21, 21, 21, 20, 20, 19, 19, 18, 18, 17, 17, 16,
+ 16, 15, 15, 15, 14, 14, 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 8, 8, 7,
+ 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0
+};
+
+static forceinline void
+art_blend_pixel_8_inline(byte *gs_restrict dst, const byte *gs_restrict backdrop,
+ const byte *gs_restrict src, int n_chan, gs_blend_mode_t blend_mode,
+ const pdf14_nonseparable_blending_procs_t * pblend_procs,
+ pdf14_device *p14dev)
+{
+ int i;
+ byte b, s;
+ bits32 t;
+
+ switch (blend_mode) {
+ case BLEND_MODE_Normal:
+ case BLEND_MODE_Compatible: /* todo */
+ memcpy(dst, src, n_chan);
+ break;
+ case BLEND_MODE_Multiply:
+ for (i = 0; i < n_chan; i++) {
+ t = ((bits32) backdrop[i]) * ((bits32) src[i]);
+ t += 0x80;
+ t += (t >> 8);
+ dst[i] = t >> 8;
+ }
+ break;
+ case BLEND_MODE_Screen:
+ for (i = 0; i < n_chan; i++) {
+ t =
+ ((bits32) (0xff - backdrop[i])) *
+ ((bits32) (0xff - src[i]));
+ t += 0x80;
+ t += (t >> 8);
+ dst[i] = 0xff - (t >> 8);
+ }
+ break;
+ case BLEND_MODE_Overlay:
+ for (i = 0; i < n_chan; i++) {
+ b = backdrop[i];
+ s = src[i];
+ if (b < 0x80)
+ t = 2 * ((bits32) b) * ((bits32) s);
+ else
+ t = 0xfe01 -
+ 2 * ((bits32) (0xff - b)) * ((bits32) (0xff - s));
+ t += 0x80;
+ t += (t >> 8);
+ dst[i] = t >> 8;
+ }
+ break;
+ case BLEND_MODE_SoftLight:
+ for (i = 0; i < n_chan; i++) {
+ b = backdrop[i];
+ s = src[i];
+ if (s < 0x80) {
+ t = (0xff - (s << 1)) * art_blend_sq_diff_8[b];
+ t += 0x8000;
+ dst[i] = b - (t >> 16);
+ } else {
+ t =
+ ((s << 1) -
+ 0xff) * ((bits32) (art_blend_soft_light_8[b]));
+ t += 0x80;
+ t += (t >> 8);
+ dst[i] = b + (t >> 8);
+ }
+ }
+ break;
+ case BLEND_MODE_HardLight:
+ for (i = 0; i < n_chan; i++) {
+ b = backdrop[i];
+ s = src[i];
+ if (s < 0x80)
+ t = 2 * ((bits32) b) * ((bits32) s);
+ else
+ t = 0xfe01 -
+ 2 * ((bits32) (0xff - b)) * ((bits32) (0xff - s));
+ t += 0x80;
+ t += (t >> 8);
+ dst[i] = t >> 8;
+ }
+ break;
+ case BLEND_MODE_ColorDodge:
+ for (i = 0; i < n_chan; i++) {
+ b = backdrop[i];
+ s = 0xff - src[i];
+ if (b == 0)
+ dst[i] = 0;
+ else if (b >= s)
+ dst[i] = 0xff;
+ else
+ dst[i] = (0x1fe * b + s) / (s << 1);
+ }
+ break;
+ case BLEND_MODE_ColorBurn:
+ for (i = 0; i < n_chan; i++) {
+ b = 0xff - backdrop[i];
+ s = src[i];
+ if (b == 0)
+ dst[i] = 0xff;
+ else if (b >= s)
+ dst[i] = 0;
+ else
+ dst[i] = 0xff - (0x1fe * b + s) / (s << 1);
+ }
+ break;
+ case BLEND_MODE_Darken:
+ for (i = 0; i < n_chan; i++) {
+ b = backdrop[i];
+ s = src[i];
+ dst[i] = b < s ? b : s;
+ }
+ break;
+ case BLEND_MODE_Lighten:
+ for (i = 0; i < n_chan; i++) {
+ b = backdrop[i];
+ s = src[i];
+ dst[i] = b > s ? b : s;
+ }
+ break;
+ case BLEND_MODE_Difference:
+ for (i = 0; i < n_chan; i++) {
+ art_s32 tmp;
+
+ tmp = ((art_s32) backdrop[i]) - ((art_s32) src[i]);
+ dst[i] = tmp < 0 ? -tmp : tmp;
+ }
+ break;
+ case BLEND_MODE_Exclusion:
+ for (i = 0; i < n_chan; i++) {
+ b = backdrop[i];
+ s = src[i];
+ t = ((bits32) (0xff - b)) * ((bits32) s) +
+ ((bits32) b) * ((bits32) (0xff - s));
+ t += 0x80;
+ t += (t >> 8);
+ dst[i] = t >> 8;
+ }
+ break;
+ case BLEND_MODE_Luminosity:
+ pblend_procs->blend_luminosity(n_chan, dst, backdrop, src);
+ break;
+ case BLEND_MODE_Color:
+ pblend_procs->blend_luminosity(n_chan, dst, src, backdrop);
+ break;
+ case BLEND_MODE_Saturation:
+ pblend_procs->blend_saturation(n_chan, dst, backdrop, src);
+ break;
+ case BLEND_MODE_Hue:
+ {
+ byte tmp[4];
+
+ pblend_procs->blend_luminosity(n_chan, tmp, src, backdrop);
+ pblend_procs->blend_saturation(n_chan, dst, tmp, backdrop);
+ }
+ break;
+ /* This mode requires information about the color space as
+ * well as the overprint mode. See Section 7.6.3 of
+ * PDF specification */
+ case BLEND_MODE_CompatibleOverprint:
+ {
+ gx_color_index drawn_comps = p14dev->drawn_comps;
+ gx_color_index comps;
+ /* If overprint mode is true and the current color space and
+ * the group color space are CMYK (or CMYK and spots), then
+ * B(cb, cs) = cs if cs is nonzero otherwise it is cb for CMYK.
+ * Spot colors are always set to cb. The nice thing about the PDF14
+ * compositor is that it always has CMYK + spots with spots after
+ * the CMYK colorants (see gx_put_blended_image_cmykspot).
+ * that way we don't have to worry about where the process colors
+ * are.
+
+ * Note: The spec claims the following:
+
+ If the overprint mode is 1 (nonzero overprint mode) and the
+ current color space and group color space are both DeviceCMYK,
+ then only process color components with nonzero values replace
+ the corresponding component values of the backdrop. All other
+ component values leave the existing backdrop value unchanged.
+ That is, the value of the blend function B(Cb,Cs) is the source
+ component cs for any process (DeviceCMYK) color component whose
+ (subtractive) color value is nonzero; otherwise it is the
+ backdrop component cb. For spot color components, the value is
+ always cb.
+
+ The equation for compositing is
+
+ ar*Cr = (1-as)*Cb + as*[(1-ab)*Cs+ab*B(Cb,Cs)]
+
+ Now if I simply set B(cb,cs) to cb for the case when the
+ DevieCMYK value (with opm true) is zero I get
+
+ ar*Cr = (1-as)*Cb + as*[(1-ab)*Cs+ab*Cb]
+
+ But what I am seeing with AR is
+ ar*Cr = (1-as)*Cb + as*[(1-ab)*Cb+ab*Cb] = (1-as)*Cb + as*Cb = Cb
+ which is what I think we want.
+
+ The description in the spec is confusing as it says
+ "then only process color components with nonzero values replace
+ the corresponding component values of the backdrop. All other
+ component values leave the existing backdrop value unchanged"
+
+ which makes sense for overprinting,
+
+ vs.
+
+ "That is, the value of the blend function B(Cb,Cs) is the source
+ component cs for any process (DeviceCMYK) color component whose
+ (subtractive) color value is nonzero; otherwise it is the
+ backdrop component cb."
+
+ Which is NOT the same thing as leaving the backdrop unchanged
+ with the compositing equation
+ ar*Cr = (1-as)*Cb + as*[(1-ab)*Cs+ab*B(Cb,Cs)]
+
+ For this to work, we need to carry out the operation during
+ the mixing of the source with the blend result. Essentially
+ replacing that mixing with the color we have here.
+ */
+ if (p14dev->effective_overprint_mode && p14dev->color_info.num_components > 3
+ && !(p14dev->ctx->additive)) {
+ for (i = 0; i < 4; i++) {
+ b = backdrop[i];
+ s = src[i];
+ dst[i] = s < 0xff ? s : b; /* Subtractive zero */
+ }
+ for (i = 4; i < n_chan; i++) {
+ dst[i] = backdrop[i];
+ }
+ } else {
+ /* Otherwise we have B(cb, cs)= cs if cs is specified in
+ * the current color space all other color should get cb.
+ * Essentially the standard overprint case. */
+ for (i = 0, comps = drawn_comps; i < n_chan; ++i, comps >>= 1) {
+ if ((comps & 0x1) != 0) {
+ dst[i] = src[i];
+ } else {
+ dst[i] = backdrop[i];
+ }
+ }
+ }
+ break;
+ }
+ default:
+#ifndef GS_THREADSAFE
+ dlprintf1("art_blend_pixel_8: blend mode %d not implemented\n",
+ blend_mode);
+#endif
+ memcpy(dst, src, n_chan);
+ break;
+ }
+}
+
+void
+art_blend_pixel_8(byte *gs_restrict dst, const byte *gs_restrict backdrop,
+ const byte *gs_restrict src, int n_chan, gs_blend_mode_t blend_mode,
+ const pdf14_nonseparable_blending_procs_t * pblend_procs,
+ pdf14_device *p14dev)
+{
+ art_blend_pixel_8_inline(dst, backdrop, src, n_chan, blend_mode,
+ pblend_procs, p14dev);
+}
+
+static forceinline void
+art_blend_pixel_16_inline(uint16_t *gs_restrict dst, const uint16_t *gs_restrict backdrop,
+ const uint16_t *gs_restrict src, int n_chan, gs_blend_mode_t blend_mode,
+ const pdf14_nonseparable_blending_procs_t * pblend_procs,
+ pdf14_device *p14dev)
+{
+ int i;
+ int b, s;
+ bits32 t;
+
+ switch (blend_mode) {
+ case BLEND_MODE_Normal:
+ case BLEND_MODE_Compatible: /* todo */
+ memcpy(dst, src, n_chan*2);
+ break;
+ case BLEND_MODE_Multiply:
+ for (i = 0; i < n_chan; i++) {
+ t = backdrop[i];
+ t += t >> 15;
+ t = t * src[i] + 0x8000;
+ dst[i] = t >> 16;
+ }
+ break;
+ case BLEND_MODE_Screen:
+ for (i = 0; i < n_chan; i++) {
+ t = backdrop[i];
+ t += t >> 15;
+ t = (0x10000-t) * (0xffff - src[i]) + 0x8000;
+ dst[i] = 0xffff - (t >> 16);
+ }
+ break;
+ case BLEND_MODE_Overlay:
+ for (i = 0; i < n_chan; i++) {
+ b = backdrop[i];
+ b += b >> 15;
+ s = src[i];
+ if (b < 0x8000)
+ t = (2 * b * s);
+ else
+ t = 0xffff0000 -
+ 2 * (0x10000 - b) * (0xffff - s);
+ t = (t+0x8000)>>16;
+ dst[i] = t;
+ }
+ break;
+ case BLEND_MODE_SoftLight:
+ for (i = 0; i < n_chan; i++) {
+ b = backdrop[i];
+ s = src[i];
+ if (s < 0x8000) {
+ unsigned int b2 = ((unsigned int)(b * (b + (b>>15))))>>16;
+ b2 = b - b2;
+ b2 += b2>>15;
+ t = ((0xffff - (s << 1)) * b2) + 0x8000;
+ dst[i] = b - (t >> 16);
+ } else {
+#define art_blend_soft_light_16(B) (art_blend_soft_light_8[(B)>>8]*0x101)
+ t = ((s << 1) - 0xffff) * art_blend_soft_light_16(b) + 0x8000;
+ dst[i] = b + (t >> 16);
+ }
+ }
+ break;
+ case BLEND_MODE_HardLight:
+ for (i = 0; i < n_chan; i++) {
+ b = backdrop[i];
+ b += b>>15;
+ s = src[i];
+ if (s < 0x8000)
+ t = 2 * b * s;
+ else
+ t = 0xffff0000 - 2 * (0x10000 - b) * (0xffff - s);
+ t += 0x8000;
+ dst[i] = t >> 16;
+ }
+ break;
+ case BLEND_MODE_ColorDodge:
+ for (i = 0; i < n_chan; i++) {
+ b = backdrop[i];
+ s = 0xffff - src[i];
+ if (b == 0)
+ dst[i] = 0;
+ else if (b >= s)
+ dst[i] = 0xffff;
+ else
+ dst[i] = ((unsigned int)(0xffff * b + (s>>1))) / s;
+ }
+ break;
+ case BLEND_MODE_ColorBurn:
+ for (i = 0; i < n_chan; i++) {
+ b = 0xffff - backdrop[i];
+ s = src[i];
+ if (b == 0)
+ dst[i] = 0xffff;
+ else if (b >= s)
+ dst[i] = 0;
+ else
+ dst[i] = 0xffff - ((unsigned int)(0xffff * b + (s>>1))) / s;
+ }
+ break;
+ case BLEND_MODE_Darken:
+ for (i = 0; i < n_chan; i++) {
+ b = backdrop[i];
+ s = src[i];
+ dst[i] = b < s ? b : s;
+ }
+ break;
+ case BLEND_MODE_Lighten:
+ for (i = 0; i < n_chan; i++) {
+ b = backdrop[i];
+ s = src[i];
+ dst[i] = b > s ? b : s;
+ }
+ break;
+ case BLEND_MODE_Difference:
+ for (i = 0; i < n_chan; i++) {
+ art_s32 tmp;
+
+ tmp = ((art_s32) backdrop[i]) - ((art_s32) src[i]);
+ dst[i] = tmp < 0 ? -tmp : tmp;
+ }
+ break;
+ case BLEND_MODE_Exclusion:
+ for (i = 0; i < n_chan; i++) {
+ b = backdrop[i];
+ b += b>>15;
+ s = src[i];
+ t = (0x10000 - b) * s + b * (0xffff - s) + 0x8000;
+ dst[i] = t >> 16;
+ }
+ break;
+ case BLEND_MODE_Luminosity:
+ pblend_procs->blend_luminosity16(n_chan, dst, backdrop, src);
+ break;
+ case BLEND_MODE_Color:
+ pblend_procs->blend_luminosity16(n_chan, dst, src, backdrop);
+ break;
+ case BLEND_MODE_Saturation:
+ pblend_procs->blend_saturation16(n_chan, dst, backdrop, src);
+ break;
+ case BLEND_MODE_Hue:
+ {
+ uint16_t tmp[4];
+
+ pblend_procs->blend_luminosity16(n_chan, tmp, src, backdrop);
+ pblend_procs->blend_saturation16(n_chan, dst, tmp, backdrop);
+ }
+ break;
+ /* This mode requires information about the color space as
+ * well as the overprint mode. See Section 7.6.3 of
+ * PDF specification */
+ case BLEND_MODE_CompatibleOverprint:
+ {
+ gx_color_index drawn_comps = p14dev->drawn_comps;
+ gx_color_index comps;
+ /* If overprint mode is true and the current color space and
+ * the group color space are CMYK (or CMYK and spots), then
+ * B(cb, cs) = cs if cs is nonzero otherwise it is cb for CMYK.
+ * Spot colors are always set to cb. The nice thing about the PDF14
+ * compositor is that it always has CMYK + spots with spots after
+ * the CMYK colorants (see gx_put_blended_image_cmykspot).
+ * that way we don't have to worry about where the process colors
+ * are. */
+ if (p14dev->effective_overprint_mode && p14dev->color_info.num_components > 3
+ && !(p14dev->ctx->additive)) {
+ for (i = 0; i < 4; i++) {
+ b = backdrop[i];
+ s = src[i];
+ dst[i] = s < 0xffff ? s : b; /* Subtractive zero */
+ }
+ for (i = 4; i < n_chan; i++) {
+ dst[i] = backdrop[i];
+ }
+ } else {
+ /* Otherwise we have B(cb, cs)= cs if cs is specified in
+ * the current color space all other color should get cb.
+ * Essentially the standard overprint case. */
+ for (i = 0, comps = drawn_comps; i < n_chan; ++i, comps >>= 1) {
+ if ((comps & 0x1) != 0) {
+ dst[i] = src[i];
+ } else {
+ dst[i] = backdrop[i];
+ }
+ }
+ }
+ break;
+ }
+ default:
+#ifndef GS_THREADSAFE
+ dlprintf1("art_blend_pixel_16: blend mode %d not implemented\n",
+ blend_mode);
+#endif
+ memcpy(dst, src, n_chan*2);
+ break;
+ }
+}
+
+void
+art_blend_pixel_16(uint16_t *gs_restrict dst, const uint16_t *gs_restrict backdrop,
+ const uint16_t *gs_restrict src, int n_chan, gs_blend_mode_t blend_mode,
+ const pdf14_nonseparable_blending_procs_t * pblend_procs,
+ pdf14_device *p14dev)
+{
+ art_blend_pixel_16_inline(dst, backdrop, src, n_chan, blend_mode,
+ pblend_procs, p14dev);
+}
+
+#ifdef UNUSED
+byte
+art_pdf_union_8(byte alpha1, byte alpha2)
+{
+ int tmp;
+
+ tmp = (0xff - alpha1) * (0xff - alpha2) + 0x80;
+ return 0xff - ((tmp + (tmp >> 8)) >> 8);
+}
+#endif
+
+static void
+art_pdf_knockout_composite_pixel_alpha_8(byte *gs_restrict backdrop, byte tos_shape, byte *gs_restrict dst,
+ const byte *gs_restrict src, int n_chan, gs_blend_mode_t blend_mode,
+ const pdf14_nonseparable_blending_procs_t * pblend_procs,
+ pdf14_device *p14dev)
+{
+ byte a_b, a_s;
+ unsigned int a_r;
+ int tmp;
+ int src_scale;
+ int c_b, c_s;
+ int i;
+
+ a_s = src[n_chan];
+ a_b = backdrop[n_chan];
+ if (a_s == 0) {
+ /* source alpha is zero, if we have a src shape value there then copy
+ the backdrop, else leave it alone */
+ if (tos_shape)
+ memcpy(dst, backdrop, n_chan + 1);
+ return;
+ }
+
+ /* In this case a_s is not zero */
+ if (a_b == 0) {
+ /* backdrop alpha is zero but not source alpha, just copy source pixels and
+ avoid computation. */
+ memcpy(dst, src, n_chan + 1);
+ return;
+ }
+
+ /* Result alpha is Union of backdrop and source alpha */
+ tmp = (0xff - a_b) * (0xff - a_s) + 0x80;
+ a_r = 0xff - (((tmp >> 8) + tmp) >> 8);
+ /* todo: verify that a_r is nonzero in all cases */
+
+ /* Compute a_s / a_r in 16.16 format */
+ src_scale = ((a_s << 16) + (a_r >> 1)) / a_r;
+
+ if (blend_mode == BLEND_MODE_Normal) {
+ /* Do simple compositing of source over backdrop */
+ for (i = 0; i < n_chan; i++) {
+ c_s = src[i];
+ c_b = backdrop[i];
+ tmp = (c_b << 16) + src_scale * (c_s - c_b) + 0x8000;
+ dst[i] = tmp >> 16;
+ }
+ } else {
+ /* Do compositing with blending */
+ byte blend[ART_MAX_CHAN];
+
+ art_blend_pixel_8(blend, backdrop, src, n_chan, blend_mode, pblend_procs,
+ p14dev);
+ for (i = 0; i < n_chan; i++) {
+ int c_bl; /* Result of blend function */
+ int c_mix; /* Blend result mixed with source color */
+
+ c_s = src[i];
+ c_b = backdrop[i];
+ c_bl = blend[i];
+ tmp = a_b * (c_bl - ((int)c_s)) + 0x80;
+ c_mix = c_s + (((tmp >> 8) + tmp) >> 8);
+ tmp = (c_b << 16) + src_scale * (c_mix - c_b) + 0x8000;
+ dst[i] = tmp >> 16;
+ }
+ }
+ dst[n_chan] = a_r;
+}
+
+static void
+art_pdf_knockout_composite_pixel_alpha_16(uint16_t *gs_restrict backdrop, uint16_t tos_shape, uint16_t *gs_restrict dst,
+ const uint16_t *gs_restrict src, int n_chan, gs_blend_mode_t blend_mode,
+ const pdf14_nonseparable_blending_procs_t * pblend_procs,
+ pdf14_device *p14dev)
+{
+ int a_b, a_s;
+ unsigned int a_r;
+ int tmp;
+ int src_scale;
+ int c_b, c_s;
+ int i;
+
+ a_s = src[n_chan];
+ a_b = backdrop[n_chan];
+ if (a_s == 0) {
+ /* source alpha is zero, if we have a src shape value there then copy
+ the backdrop, else leave it alone */
+ if (tos_shape)
+ memcpy(dst, backdrop, 2*(n_chan + 1));
+ return;
+ }
+
+ /* In this case a_s is not zero */
+ if (a_b == 0) {
+ /* backdrop alpha is zero but not source alpha, just copy source pixels and
+ avoid computation. */
+ memcpy(dst, src, 2*(n_chan + 1));
+ return;
+ }
+
+ /* Result alpha is Union of backdrop and source alpha */
+ a_b += a_b>>15;
+ tmp = (0x10000 - a_b) * (0xffff - a_s) + 0x8000;
+ a_r = 0xffff - (tmp >> 16);
+ /* todo: verify that a_r is nonzero in all cases */
+
+ /* Compute a_s / a_r in 16.16 format */
+ src_scale = ((a_s << 16) + (a_r >> 1)) / a_r;
+
+ src_scale >>= 1; /* Lose a bit to avoid overflow */
+ if (blend_mode == BLEND_MODE_Normal) {
+ /* Do simple compositing of source over backdrop */
+ for (i = 0; i < n_chan; i++) {
+ c_s = src[i];
+ c_b = backdrop[i];
+ tmp = src_scale * (c_s - c_b) + 0x4000;
+ dst[i] = c_b + (tmp >> 15);
+ }
+ } else {
+ /* Do compositing with blending */
+ uint16_t blend[ART_MAX_CHAN];
+
+ art_blend_pixel_16(blend, backdrop, src, n_chan, blend_mode, pblend_procs,
+ p14dev);
+ a_b >>= 1; /* Lose a bit to avoid overflow */
+ for (i = 0; i < n_chan; i++) {
+ int c_bl; /* Result of blend function */
+ int c_mix; /* Blend result mixed with source color */
+
+ c_s = src[i];
+ c_b = backdrop[i];
+ c_bl = blend[i];
+ tmp = a_b * (c_bl - c_s) + 0x4000;
+ c_mix = c_s + (tmp >> 15);
+ tmp = src_scale * (c_mix - c_b) + 0x4000;
+ dst[i] = c_b + (tmp >> 15);
+ }
+ }
+ dst[n_chan] = a_r;
+}
+
+void
+art_pdf_composite_pixel_alpha_8(byte *gs_restrict dst, const byte *gs_restrict src, int n_chan,
+ gs_blend_mode_t blend_mode, int first_spot,
+ const pdf14_nonseparable_blending_procs_t * pblend_procs, pdf14_device *p14dev)
+{
+ byte a_b, a_s;
+ unsigned int a_r;
+ int tmp;
+ int src_scale;
+ int c_b, c_s;
+ int i;
+
+ a_s = src[n_chan];
+ if (a_s == 0) {
+ /* source alpha is zero, avoid all computations and possible
+ divide by zero errors. */
+ return;
+ }
+
+ a_b = dst[n_chan];
+ if (a_b == 0) {
+ /* backdrop alpha is zero, just copy source pixels and avoid
+ computation. */
+
+ memcpy (dst, src, n_chan + 1);
+
+ return;
+ }
+
+ /* Result alpha is Union of backdrop and source alpha */
+ tmp = (0xff - a_b) * (0xff - a_s) + 0x80;
+ a_r = 0xff - (((tmp >> 8) + tmp) >> 8);
+ /* todo: verify that a_r is nonzero in all cases */
+
+ /* Compute a_s / a_r in 16.16 format */
+ src_scale = ((a_s << 16) + (a_r >> 1)) / a_r;
+
+ if (first_spot != 0) {
+ /* Do compositing with blending */
+ byte blend[ART_MAX_CHAN];
+
+ art_blend_pixel_8(blend, dst, src, first_spot, blend_mode, pblend_procs, p14dev);
+ for (i = 0; i < first_spot; i++) {
+ int c_bl; /* Result of blend function */
+ int c_mix; /* Blend result mixed with source color */
+
+ c_s = src[i];
+ c_b = dst[i];
+ c_bl = blend[i];
+ tmp = a_b * (c_bl - ((int)c_s)) + 0x80;
+ c_mix = c_s + (((tmp >> 8) + tmp) >> 8);
+ tmp = (c_b << 16) + src_scale * (c_mix - c_b) + 0x8000;
+ dst[i] = tmp >> 16;
+ }
+ }
+ dst[n_chan] = a_r;
+
+ dst += first_spot;
+ src += first_spot;
+ n_chan -= first_spot;
+ if (n_chan == 0)
+ return;
+
+ /* Do simple compositing of source over backdrop */
+ for (i = 0; i < n_chan; i++) {
+ c_s = src[i];
+ c_b = dst[i];
+ tmp = (c_b << 16) + src_scale * (c_s - c_b) + 0x8000;
+ dst[i] = tmp >> 16;
+ }
+}
+
+void
+art_pdf_composite_pixel_alpha_16(uint16_t *gs_restrict dst, const uint16_t *gs_restrict src, int n_chan,
+ gs_blend_mode_t blend_mode, int first_spot,
+ const pdf14_nonseparable_blending_procs_t * pblend_procs, pdf14_device *p14dev)
+{
+ int a_b, a_s;
+ unsigned int a_r;
+ unsigned int tmp;
+ int src_scale;
+ int c_b, c_s;
+ int i;
+
+ a_s = src[n_chan];
+ if (a_s == 0) {
+ /* source alpha is zero, avoid all computations and possible
+ divide by zero errors. */
+ return;
+ }
+
+ a_b = dst[n_chan];
+ if (a_b == 0) {
+ /* backdrop alpha is zero, just copy source pixels and avoid
+ computation. */
+
+ memcpy (dst, src, (n_chan + 1)*2);
+
+ return;
+ }
+
+ /* Result alpha is Union of backdrop and source alpha */
+ tmp = (0xffff - a_b) * (0xffff - a_s) + 0x8000;
+ a_r = 0xffff - (((tmp >> 16) + tmp) >> 16);
+ /* todo: verify that a_r is nonzero in all cases */
+
+ /* Compute a_s / a_r in 16.16 format */
+ src_scale = ((unsigned int)((a_s << 16) + (a_r >> 1))) / a_r;
+
+ src_scale >>= 1; /* Lose a bit to avoid overflow */
+ if (first_spot != 0) {
+ /* Do compositing with blending */
+ uint16_t blend[ART_MAX_CHAN];
+
+ a_b >>= 1; /* Lose a bit to avoid overflow */
+ art_blend_pixel_16(blend, dst, src, first_spot, blend_mode, pblend_procs, p14dev);
+ for (i = 0; i < first_spot; i++) {
+ int c_bl; /* Result of blend function */
+ int c_mix; /* Blend result mixed with source color */
+
+ c_s = src[i];
+ c_b = dst[i];
+ c_bl = blend[i];
+ tmp = a_b * (c_bl - ((int)c_s)) + 0x4000;
+ c_mix = c_s + (((tmp >> 16) + tmp) >> 15);
+ tmp = src_scale * (c_mix - c_b) + 0x4000;
+ dst[i] = c_b + (tmp >> 15);
+ }
+ }
+ dst[n_chan] = a_r;
+
+ dst += first_spot;
+ src += first_spot;
+ n_chan -= first_spot;
+ if (n_chan == 0)
+ return;
+
+ /* Do simple compositing of source over backdrop */
+ for (i = 0; i < n_chan; i++) {
+ c_s = src[i];
+ c_b = dst[i];
+ tmp = src_scale * (c_s - c_b) + 0x4000;
+ dst[i] = c_b + (tmp >> 15);
+ }
+}
+
+static forceinline byte *
+art_pdf_composite_pixel_alpha_8_inline(byte *gs_restrict dst, byte *gs_restrict src, int n_chan,
+ gs_blend_mode_t blend_mode, int first_spot,
+ const pdf14_nonseparable_blending_procs_t * pblend_procs, pdf14_device *p14dev)
+{
+ byte a_b, a_s;
+ unsigned int a_r;
+ int tmp;
+ int src_scale;
+ int c_b, c_s;
+ int i;
+
+ a_s = src[n_chan];
+ if (a_s == 0) {
+ /* source alpha is zero, avoid all computations and possible
+ divide by zero errors. */
+ return NULL; /* No change to destination at all! */
+ }
+
+ a_b = dst[n_chan];
+ if (a_b == 0) {
+ /* backdrop alpha is zero, just copy source pixels and avoid
+ computation. */
+ return src;
+ }
+
+ /* Result alpha is Union of backdrop and source alpha */
+ tmp = (0xff - a_b) * (0xff - a_s) + 0x80;
+ a_r = 0xff - (((tmp >> 8) + tmp) >> 8);
+ /* todo: verify that a_r is nonzero in all cases */
+
+ /* Compute a_s / a_r in 16.16 format */
+ src_scale = ((a_s << 16) + (a_r >> 1)) / a_r;
+
+ if (first_spot != 0) {
+ /* Do compositing with blending */
+ byte blend[ART_MAX_CHAN];
+
+ art_blend_pixel_8_inline(blend, dst, src, first_spot, blend_mode, pblend_procs, p14dev);
+
+ if (blend_mode == BLEND_MODE_CompatibleOverprint) {
+ for (i = 0; i < first_spot; i++) {
+ /* No mixing. Blend[i] is backdrop or src */
+ tmp = (dst[i] << 16) + src_scale * (blend[i] - dst[i]) + 0x8000;
+ dst[i] = tmp >> 16;
+ }
+ } else {
+ for (i = 0; i < first_spot; i++) {
+ int c_bl; /* Result of blend function */
+ int c_mix; /* Blend result mixed with source color */
+
+ c_s = src[i];
+ c_b = dst[i];
+ c_bl = blend[i];
+ tmp = a_b * (c_bl - ((int)c_s)) + 0x80;
+ c_mix = c_s + (((tmp >> 8) + tmp) >> 8);
+ tmp = (c_b << 16) + src_scale * (c_mix - c_b) + 0x8000;
+ dst[i] = tmp >> 16;
+ }
+ }
+ }
+ dst[n_chan] = a_r;
+
+ n_chan -= first_spot;
+ if (n_chan == 0)
+ return dst;
+ dst += first_spot;
+ src += first_spot;
+
+ /* Do simple compositing of source over backdrop */
+ for (i = 0; i < n_chan; i++) {
+ c_s = src[i];
+ c_b = dst[i];
+ tmp = (c_b << 16) + src_scale * (c_s - c_b) + 0x8000;
+ dst[i] = tmp >> 16;
+ }
+ return dst - first_spot;
+}
+
+static forceinline uint16_t *
+art_pdf_composite_pixel_alpha_16_inline(uint16_t *gs_restrict dst, uint16_t *gs_restrict src, int n_chan,
+ gs_blend_mode_t blend_mode, int first_spot,
+ const pdf14_nonseparable_blending_procs_t * pblend_procs, pdf14_device *p14dev)
+{
+ int a_b, a_s;
+ unsigned int a_r;
+ int tmp;
+ int src_scale;
+ int c_b, c_s;
+ int i;
+
+ a_s = src[n_chan];
+ if (a_s == 0) {
+ /* source alpha is zero, avoid all computations and possible
+ divide by zero errors. */
+ return NULL; /* No change to destination at all! */
+ }
+
+ a_b = dst[n_chan];
+ if (a_b == 0) {
+ /* backdrop alpha is zero, just copy source pixels and avoid
+ computation. */
+ return src;
+ }
+
+ /* Result alpha is Union of backdrop and source alpha */
+ a_b += a_b>>15; /* a_b in 0...0x10000 range */
+ tmp = (0x10000 - a_b) * (0xffff - a_s) + 0x8000;
+ a_r = 0xffff - (((unsigned int)tmp) >> 16); /* a_r in 0...0xffff range */
+ /* todo: verify that a_r is nonzero in all cases */
+
+ /* Compute a_s / a_r in 16.16 format */
+ src_scale = ((unsigned int)((a_s << 16) + (a_r >> 1))) / a_r;
+
+ src_scale >>= 1; /* Lose a bit to avoid overflow */
+ if (first_spot != 0) {
+ /* Do compositing with blending */
+ uint16_t blend[ART_MAX_CHAN];
+
+ art_blend_pixel_16_inline(blend, dst, src, first_spot, blend_mode, pblend_procs, p14dev);
+
+ if (blend_mode == BLEND_MODE_CompatibleOverprint) {
+ for (i = 0; i < first_spot; i++) {
+ /* No mixing. Blend[i] is backdrop or src */
+ dst[i] += (src_scale * (blend[i] - dst[i]) + 0x4000) >> 15;
+ }
+ } else {
+ a_b >>= 1; /* Lose a bit to avoid overflow */
+ for (i = 0; i < first_spot; i++) {
+ int c_bl; /* Result of blend function */
+
+ c_s = src[i];
+ c_b = dst[i];
+ c_bl = blend[i];
+
+ c_s += (a_b * (c_bl - c_s) + 0x4000) >> 15;
+ c_b += (src_scale * (c_s - c_b) + 0x4000) >> 15;
+ dst[i] = c_b;
+ }
+ }
+ }
+ dst[n_chan] = a_r;
+
+ n_chan -= first_spot;
+ if (n_chan == 0)
+ return dst;
+ dst += first_spot;
+ src += first_spot;
+
+ /* Do simple compositing of source over backdrop */
+ for (i = 0; i < n_chan; i++) {
+ c_s = src[i];
+ c_b = dst[i];
+ c_b += (src_scale * (c_s - c_b) + 0x4000)>>15;
+ dst[i] = c_b;
+ }
+ return dst - first_spot;
+}
+
+/**
+ * art_pdf_composite_pixel_alpha_8_fast_mono: Tweaked version of art_pdf_composite_pixel_alpha_8_fast.
+ * Same args, except n_chan, which is assumed to be 1:
+ * @stride: stride between dst pixel values.
+ * @p14dev: pdf14 device
+ * Dst data is therefore in dst[i * stride] for 0 <= i <= 1.
+ * Called with the guarantee that dst[stride] != 0, src[1] != 0, and that blend_mode != Normal
+ */
+static inline void
+art_pdf_composite_pixel_alpha_8_fast_mono(byte *gs_restrict dst, const byte *gs_restrict src,
+ gs_blend_mode_t blend_mode,
+ const pdf14_nonseparable_blending_procs_t * pblend_procs,
+ int stride, pdf14_device *p14dev)
+{
+ byte a_b, a_s;
+ unsigned int a_r;
+ int tmp;
+ int src_scale;
+ int c_b, c_s;
+ byte blend[ART_MAX_CHAN];
+
+ a_s = src[1];
+
+ a_b = dst[stride];
+
+ /* Result alpha is Union of backdrop and source alpha */
+ tmp = (0xff - a_b) * (0xff - a_s) + 0x80;
+ a_r = 0xff - (((tmp >> 8) + tmp) >> 8);
+ /* todo: verify that a_r is nonzero in all cases */
+
+ /* Compute a_s / a_r in 16.16 format */
+ src_scale = ((a_s << 16) + (a_r >> 1)) / a_r;
+
+ /* Do compositing with blending */
+ art_blend_pixel_8(blend, dst, src, 1, blend_mode, pblend_procs, p14dev);
+ {
+ int c_bl; /* Result of blend function */
+ int c_mix; /* Blend result mixed with source color */
+
+ c_s = src[0];
+ c_b = dst[0];
+ c_bl = blend[0];
+ tmp = a_b * (c_bl - ((int)c_s)) + 0x80;
+ c_mix = c_s + (((tmp >> 8) + tmp) >> 8);
+ tmp = (c_b << 16) + src_scale * (c_mix - c_b) + 0x8000;
+ dst[0] = tmp >> 16;
+ }
+ dst[stride] = a_r;
+}
+
+static inline void
+art_pdf_composite_pixel_alpha_16_fast_mono(uint16_t *gs_restrict dst, const uint16_t *gs_restrict src,
+ gs_blend_mode_t blend_mode,
+ const pdf14_nonseparable_blending_procs_t * pblend_procs,
+ int stride, pdf14_device *p14dev)
+{
+ uint16_t a_b, a_s;
+ unsigned int a_r;
+ int tmp;
+ int src_scale;
+ int c_b, c_s;
+ uint16_t blend[ART_MAX_CHAN];
+
+ a_s = src[1];
+
+ a_b = dst[stride];
+ a_b += a_b>>15;
+
+ /* Result alpha is Union of backdrop and source alpha */
+ tmp = (0x10000 - a_b) * (0xffff - a_s) + 0x8000;
+ a_r = 0xffff - (tmp >> 16);
+ /* todo: verify that a_r is nonzero in all cases */
+
+ /* Compute a_s / a_r in 16.16 format */
+ src_scale = ((a_s << 16) + (a_r >> 1)) / a_r;
+
+ src_scale >>= 1; /* Lose a bit to avoid overflow */
+ a_b >>= 1; /* Lose a bit to avoid overflow */
+ /* Do compositing with blending */
+ art_blend_pixel_16(blend, dst, src, 1, blend_mode, pblend_procs, p14dev);
+ {
+ int c_bl; /* Result of blend function */
+
+ c_s = src[0];
+ c_b = dst[0];
+ c_bl = blend[0];
+ tmp = a_b * (c_bl - c_s) + 0x4000;
+ c_s += (tmp>>15);
+ dst[0] = c_b + ((src_scale * (c_s - c_b) + 0x4000)>>15);
+ }
+ dst[stride] = a_r;
+}
+
+/**
+ * art_pdf_recomposite_group_8: Recomposite group pixel.
+ * @dst: Where to store pixel, also initial backdrop of group.
+ * @dst_alpha_g: Optional pointer to alpha g value associated with @dst.
+ * @alpha: Alpha mask value.
+ * @src_alpha_g: alpha_g value associated with @src.
+ * @blend_mode: Blend mode for compositing.
+ * @first_blend_spot: The first component for which Normal blending should be used.
+ * @pblend_procs: Procs for handling non separable blending modes.
+ * @p14dev: pdf14 device
+ *
+ * Note: this is only for non-isolated groups. This covers only the
+ * single-alpha case. A separate function is needed for dual-alpha,
+ * and that probably needs to treat knockout separately.
+ * Also note the need to know if the spot colorants should be blended
+ * normal. This occurs when we have spot colorants and the blending is set
+ * for non-separable or non-white preserving blend modes
+ * @src_alpha_g corresponds to $\alpha g_n$ in the Adobe notation.
+ *
+ * @alpha corresponds to $fk_i \cdot fm_i \cdot qk_i \cdot qm_i$.
+ *
+ * @NOTE: This function may corrupt src.
+ *
+ * Returns 1 if we need to call art_pdf_composite_pixel_alpha_8.
+ **/
+static forceinline int
+art_pdf_recomposite_group_8(byte *gs_restrict *dstp, byte *gs_restrict dst_alpha_g,
+ byte *gs_restrict src, byte src_alpha_g, int n_chan,
+ byte alpha, gs_blend_mode_t blend_mode, int first_blend_spot,
+ const pdf14_nonseparable_blending_procs_t * pblend_procs,
+ pdf14_device *p14dev)
+{
+ byte dst_alpha;
+ int i;
+ int tmp;
+ int scale;
+ byte *gs_restrict dst = *dstp;
+
+ if (src_alpha_g == 0)
+ return 0;
+
+ if (blend_mode == BLEND_MODE_Normal && alpha == 255) {
+ /* In this case, uncompositing and recompositing cancel each
+ other out. Note: if the reason that alpha == 255 is that
+ there is no constant mask and no soft mask, then this
+ operation should be optimized away at a higher level. */
+
+ if (dst_alpha_g != NULL) {
+ tmp = (255 - *dst_alpha_g) * (255 - src_alpha_g) + 0x80;
+ *dst_alpha_g = 255 - ((tmp + (tmp >> 8)) >> 8);
+ }
+ *dstp = src;
+ return 0;
+ } else {
+ /* "interesting" blend mode */
+ dst_alpha = dst[n_chan];
+ if (src_alpha_g != 255 && dst_alpha != 0) {
+ /* Uncomposite the color. In other words, solve
+ "src = (src, src_alpha_g) over dst" for src */
+ scale = (dst_alpha * 255 * 2 + src_alpha_g) / (src_alpha_g << 1) -
+ dst_alpha;
+ for (i = 0; i < n_chan; i++) {
+ int si, di;
+
+ si = src[i];
+ di = dst[i];
+ tmp = (si - di) * scale + 0x80;
+ tmp = si + ((tmp + (tmp >> 8)) >> 8);
+
+ /* todo: it should be possible to optimize these cond branches */
+ if (tmp < 0)
+ tmp = 0;
+ if (tmp > 255)
+ tmp = 255;
+ src[i] = tmp;
+ }
+ }
+
+ tmp = src_alpha_g * alpha + 0x80;
+ tmp = (tmp + (tmp >> 8)) >> 8;
+ src[n_chan] = tmp;
+ if (dst_alpha_g != NULL) {
+ tmp = (255 - *dst_alpha_g) * (255 - tmp) + 0x80;
+ *dst_alpha_g = 255 - ((tmp + (tmp >> 8)) >> 8);
+ }
+ }
+ return 1;
+ /* todo: optimize BLEND_MODE_Normal buf alpha != 255 case */
+}
+
+static forceinline int
+art_pdf_recomposite_group_16(uint16_t *gs_restrict *dstp, uint16_t *gs_restrict dst_alpha_g,
+ uint16_t *gs_restrict src, uint16_t src_alpha_g, int n_chan,
+ uint16_t alpha, gs_blend_mode_t blend_mode, int first_blend_spot,
+ const pdf14_nonseparable_blending_procs_t * pblend_procs,
+ pdf14_device *p14dev)
+{
+ uint16_t dst_alpha;
+ int i;
+ int tmp;
+ int scale;
+ uint16_t *gs_restrict dst = *dstp;
+
+ if (src_alpha_g == 0)
+ return 0;
+
+ if (blend_mode == BLEND_MODE_Normal && alpha == 65535) {
+ /* In this case, uncompositing and recompositing cancel each
+ other out. Note: if the reason that alpha == 65535 is that
+ there is no constant mask and no soft mask, then this
+ operation should be optimized away at a higher level. */
+
+ if (dst_alpha_g != NULL) {
+ int d = *dst_alpha_g;
+ d += d>>15;
+ tmp = (0x10000 - d) * (0xffff - src_alpha_g) + 0x8000;
+ *dst_alpha_g = 0xffff - (tmp>>16);
+ }
+ *dstp = src;
+ return 0;
+ } else {
+ /* "interesting" blend mode */
+ dst_alpha = dst[n_chan];
+ if (src_alpha_g != 65535 && dst_alpha != 0) {
+ /* Uncomposite the color. In other words, solve
+ "src = (src, src_alpha_g) over dst" for src */
+ scale = ((unsigned int)(dst_alpha * 65535 + (src_alpha_g>>1))) / src_alpha_g -
+ dst_alpha;
+ /* scale is NOT in 16.16 form here. I've seen values of 0xfefe01, for example. */
+ for (i = 0; i < n_chan; i++) {
+ int si, di;
+ int64_t tmp64;
+
+ si = src[i];
+ di = dst[i];
+ /* RJW: Nasty that we have to resort to 64bit here, but we'll live with it. */
+ tmp64 = (si - di) * (int64_t)scale + 0x8000;
+ tmp = si + (tmp64 >> 16);
+
+ /* todo: it should be possible to optimize these cond branches */
+ if (tmp < 0)
+ tmp = 0;
+ if (tmp > 65535)
+ tmp = 65535;
+ src[i] = tmp;
+ }
+ }
+
+ tmp = alpha + (alpha>>15);
+ tmp = (src_alpha_g * tmp + 0x8000)>>16;
+ src[n_chan] = tmp;
+ if (dst_alpha_g != NULL) {
+ int d = *dst_alpha_g;
+ d += d>>15;
+ tmp = (0x10000 - d) * (0xffff - tmp) + 0x8000;
+ *dst_alpha_g = 0xffff - (tmp >> 16);
+ }
+ }
+ return 1;
+ /* todo: optimize BLEND_MODE_Normal buf alpha != 255 case */
+}
+
+/**
+ * art_pdf_composite_knockout_group_8: Composite group pixel.
+ * @backdrop: Backdrop of original parent group.
+ * @tos_shape: So that we know to copy the backdrop or not even if a_s is zero
+ * @dst: Where to store pixel.
+ * @dst_alpha_g: Optional pointer to alpha g value.
+ * @alpha: Alpha mask value.
+ * @blend_mode: Blend mode for compositing.
+ * @pblend_procs: Procs for handling non separable blending modes.
+ * @p14dev: PDF14 device
+ * @has_mask: needed for knowing to pass back the soft mask value if shape = 0
+ *
+ * Note: this is only for knockout nonisolated groups.
+ *
+ * @alpha corresponds to $fk_i \cdot fm_i \cdot qk_i \cdot qm_i$.
+ *
+ * @NOTE: This function may corrupt src.
+ **/
+static forceinline void
+art_pdf_composite_knockout_group_8(byte *gs_restrict backdrop, byte tos_shape, byte *gs_restrict dst,
+ byte *gs_restrict dst_alpha_g, byte *gs_restrict src, int n_chan, byte alpha,
+ gs_blend_mode_t blend_mode,
+ const pdf14_nonseparable_blending_procs_t * pblend_procs,
+ pdf14_device *p14dev, bool has_mask)
+{
+ byte src_alpha; /* $\alpha g_n$ */
+ int tmp;
+
+ if (tos_shape == 0) {
+ /* If a softmask was present pass it along Bug 693548 */
+ if (has_mask)
+ dst[n_chan] = alpha;
+ return;
+ }
+
+ if (alpha != 255) {
+ if (tos_shape != 255) return;
+ src_alpha = src[n_chan];
+ if (src_alpha == 0)
+ return;
+ tmp = src_alpha * alpha + 0x80;
+ src[n_chan] = (tmp + (tmp >> 8)) >> 8;
+ }
+
+ if (dst_alpha_g != NULL) {
+ tmp = (255 - *dst_alpha_g) * (255 - src[n_chan]) + 0x80;
+ *dst_alpha_g = 255 - ((tmp + (tmp >> 8)) >> 8);
+ }
+ art_pdf_knockout_composite_pixel_alpha_8(backdrop, tos_shape, dst, src,
+ n_chan, blend_mode, pblend_procs,
+ p14dev);
+}
+
+static forceinline void
+art_pdf_composite_knockout_group_16(uint16_t *gs_restrict backdrop, uint16_t tos_shape, uint16_t *gs_restrict dst,
+ uint16_t *gs_restrict dst_alpha_g, uint16_t *gs_restrict src, int n_chan, uint16_t alpha,
+ gs_blend_mode_t blend_mode,
+ const pdf14_nonseparable_blending_procs_t * pblend_procs,
+ pdf14_device *p14dev, bool has_mask)
+{
+ int src_alpha; /* $\alpha g_n$ */
+ int tmp;
+
+ if (tos_shape == 0) {
+ /* If a softmask was present pass it along Bug 693548 */
+ if (has_mask)
+ dst[n_chan] = alpha;
+ return;
+ }
+
+ if (alpha != 65535) {
+ if (tos_shape != 65535) return;
+ src_alpha = src[n_chan];
+ if (src_alpha == 0)
+ return;
+ src_alpha += src_alpha>>15;
+ tmp = src_alpha * alpha + 0x8000;
+ src[n_chan] = tmp >> 16;
+ }
+
+ if (dst_alpha_g != NULL) {
+ tmp = *dst_alpha_g;
+ tmp += tmp>>15;
+ tmp = (0x10000 - tmp) * (0xffff - src[n_chan]) + 0x8000;
+ *dst_alpha_g = 0xffff - (tmp >> 16);
+ }
+ art_pdf_knockout_composite_pixel_alpha_16(backdrop, tos_shape, dst, src,
+ n_chan, blend_mode, pblend_procs,
+ p14dev);
+}
+
+/**
+ * art_pdf_composite_group_8: Composite group pixel.
+ * @dst: Where to store pixel, also initial backdrop of group.
+ * @dst_alpha_g: Optional pointer to alpha g value.
+ * @alpha: Alpha mask value.
+ * @blend_mode: Blend mode for compositing.
+ * @pblend_procs: Procs for handling non separable blending modes.
+ *
+ * Note: this is only for isolated groups. This covers only the
+ * single-alpha case. A separate function is needed for dual-alpha,
+ * and that probably needs to treat knockout separately.
+ *
+ * Components 0 to first_spot are blended with blend_mode.
+ * Components first_spot to n_chan are blended with BLEND_MODE_Normal.
+ *
+ * @alpha corresponds to $fk_i \cdot fm_i \cdot qk_i \cdot qm_i$.
+ *
+ * @NOTE: This function may corrupt src.
+ *
+ * Returns 1 if we need to call art_pdf_composite_pixel_alpha_8.
+ **/
+static forceinline int
+art_pdf_composite_group_8(byte *gs_restrict dst, byte *gs_restrict dst_alpha_g,
+ byte *gs_restrict src, int n_chan, byte alpha)
+{
+ byte src_alpha = src[n_chan]; /* $\alpha g_n$ */
+
+ if (src_alpha == 0)
+ return 0;
+
+ if (alpha != 255) {
+ int tmp = src_alpha * alpha + 0x80;
+ src[n_chan] = (tmp + (tmp >> 8)) >> 8;
+ }
+
+ if (dst_alpha_g != NULL) {
+ int tmp = (255 - *dst_alpha_g) * (255 - src[n_chan]) + 0x80;
+ *dst_alpha_g = 255 - ((tmp + (tmp >> 8)) >> 8);
+ }
+
+ return 1;
+}
+
+static forceinline int
+art_pdf_composite_group_16(uint16_t *gs_restrict dst, uint16_t *gs_restrict dst_alpha_g,
+ uint16_t *gs_restrict src, int n_chan, uint16_t alpha)
+{
+ uint16_t src_alpha = src[n_chan]; /* $\alpha g_n$ */
+
+ if (src_alpha == 0)
+ return 0;
+
+ if (alpha != 65535) {
+ int tmp = alpha + (alpha>>15);
+ src[n_chan] = (src_alpha * tmp + 0x8000)>>16;
+ }
+
+ if (dst_alpha_g != NULL) {
+ int tmp = *dst_alpha_g;
+ tmp += tmp>>15;
+ tmp = (0x10000 - tmp) * (0xffff - src[n_chan]) + 0x8000;
+ *dst_alpha_g = 0xffff - (tmp >> 16);
+ }
+
+ return 1;
+}
+
+/* A very simple case. Knockout isolated group going to a parent that is not
+ a knockout. Simply copy over everwhere where we have a non-zero alpha value */
+void
+art_pdf_knockoutisolated_group_8(byte *gs_restrict dst, const byte *gs_restrict src, int n_chan)
+{
+ byte src_alpha;
+
+ src_alpha = src[n_chan];
+ if (src_alpha == 0)
+ return;
+
+ memcpy (dst, src, n_chan + 1);
+}
+
+void
+art_pdf_knockoutisolated_group_16(uint16_t *gs_restrict dst, const uint16_t *gs_restrict src, int n_chan)
+{
+ uint16_t src_alpha;
+
+ src_alpha = src[n_chan];
+ if (src_alpha == 0)
+ return;
+
+ memcpy (dst, src, 2*(n_chan + 1));
+}
+
+/* An odd case where we have an alpha from the AA device and we have a current
+ source alpha. This is done only in the case where we are doing AA and a
+ stroke fill at the same time.
+ We have to first do a blend with the AA alpha if there
+ is something to blend with and then set the alpha to the source alpha.
+ In such a case an isolated knockout group was created and in the
+ copy_alpha code we end up here to handle the alpha from the AA code
+ differently from the other alpha. This ensures that the stroke and fill
+ end up blended with each other on the inside of the stroke path
+ but that the alpha from the AA does not end up getting blended with the
+ backdrop (unless the source alpha is not opaque) while the outside of the
+ stroke path ends up with the alpha for both the AA effect and source alpha */
+void
+art_pdf_knockoutisolated_group_aa_8(byte *gs_restrict dst, const byte *gs_restrict src, byte src_alpha,
+ byte aa_alpha, int n_chan, pdf14_device *p14dev)
+{
+ int dst_alpha = dst[n_chan];
+ byte temp_src[ART_MAX_CHAN + 1];
+ int i;
+
+ /* Note: src[n_chan] is a blend of the aa_alpha and src_alpha */
+ if (src[n_chan] == 0)
+ return;
+
+ /* Check what is at the destination. If nothing there then just copy */
+ if (dst_alpha == 0) {
+ memcpy(dst, src, n_chan + 1);
+ return;
+ }
+
+ /* Now the more complex case as something is there. First blend with the AA
+ alpha and then set our alpha to src_alpha so it will end up blending properly
+ with the backdrop if we had an global alpha for this fill/stroke */
+ for (i = 0; i < n_chan; i++)
+ temp_src[i] = src[i];
+ temp_src[n_chan] = aa_alpha;
+ art_pdf_composite_pixel_alpha_8(dst, temp_src, n_chan, BLEND_MODE_Normal,
+ n_chan, NULL, p14dev);
+ dst[n_chan] = src_alpha;
+}
+
+void
+art_pdf_composite_knockout_8(byte *gs_restrict dst,
+ const byte *gs_restrict src,
+ int n_chan,
+ gs_blend_mode_t blend_mode,
+ const pdf14_nonseparable_blending_procs_t * pblend_procs,
+ pdf14_device *p14dev)
+{
+ byte src_shape = src[n_chan];
+ int i, tmp;
+
+ if (blend_mode == BLEND_MODE_Normal) {
+ /* Do simple compositing of source over backdrop */
+ if (src_shape == 0)
+ return;
+ else if (src_shape == 255) {
+ memcpy (dst, src, n_chan + 1);
+ return;
+ } else {
+ /* Use src_shape to interpolate (in premultiplied alpha space)
+ between dst and (src, opacity). */
+ int dst_alpha = dst[n_chan];
+ byte result_alpha;
+
+ tmp = (255 - dst_alpha) * src_shape + 0x80;
+ result_alpha = dst_alpha + ((tmp + (tmp >> 8)) >> 8);
+
+ if (result_alpha != 0)
+ for (i = 0; i < n_chan; i++) {
+ /* todo: optimize this - can strength-reduce so that
+ inner loop is a single interpolation */
+ tmp = dst[i] * dst_alpha * (255 - src_shape) +
+ ((int)src[i]) * 255 * src_shape + (result_alpha << 7);
+ dst[i] = tmp / (result_alpha * 255);
+ }
+ dst[n_chan] = result_alpha;
+ }
+ } else {
+ /* Do compositing with blending */
+ byte blend[ART_MAX_CHAN];
+ byte a_b, a_s;
+ unsigned int a_r;
+ int src_scale;
+ int c_b, c_s;
+
+ a_s = src[n_chan];
+ a_b = dst[n_chan];
+
+ /* Result alpha is Union of backdrop and source alpha */
+ tmp = (0xff - a_b) * (0xff - a_s) + 0x80;
+ a_r = 0xff - (((tmp >> 8) + tmp) >> 8);
+ /* todo: verify that a_r is nonzero in all cases */
+
+ /* Compute a_s / a_r in 16.16 format */
+ src_scale = ((a_s << 16) + (a_r >> 1)) / a_r;
+
+ art_blend_pixel_8(blend, dst, src, n_chan, blend_mode, pblend_procs, p14dev);
+ for (i = 0; i < n_chan; i++) {
+ int c_bl; /* Result of blend function */
+ int c_mix; /* Blend result mixed with source color */
+
+ c_s = src[i];
+ c_b = dst[i];
+ c_bl = blend[i];
+ tmp = a_b * (c_bl - ((int)c_s)) + 0x80;
+ c_mix = c_s + (((tmp >> 8) + tmp) >> 8);
+ tmp = (c_b << 16) + src_scale * (c_mix - c_b) + 0x8000;
+ dst[i] = tmp >> 16;
+ }
+ dst[n_chan] = a_r;
+ }
+}
+
+void
+art_pdf_composite_knockout_16(uint16_t *gs_restrict dst,
+ const uint16_t *gs_restrict src,
+ int n_chan,
+ gs_blend_mode_t blend_mode,
+ const pdf14_nonseparable_blending_procs_t * pblend_procs,
+ pdf14_device *p14dev)
+{
+ uint16_t src_shape = src[n_chan];
+ int i;
+ unsigned int tmp;
+
+ if (blend_mode == BLEND_MODE_Normal) {
+ /* Do simple compositing of source over backdrop */
+ if (src_shape == 0)
+ return;
+ else if (src_shape == 65535) {
+ memcpy (dst, src, (n_chan + 1)*2);
+ return;
+ } else {
+ /* Use src_shape to interpolate (in premultiplied alpha space)
+ between dst and (src, opacity). */
+ int dst_alpha = dst[n_chan];
+ uint16_t result_alpha;
+
+ tmp = (65535 - dst_alpha) * src_shape + 0x8000;
+ result_alpha = dst_alpha + ((tmp + (tmp >> 16)) >> 16);
+
+ if (result_alpha != 0) {
+ dst_alpha += dst_alpha>>15;
+ for (i = 0; i < n_chan; i++) {
+ /* todo: optimize this - can strength-reduce so that
+ inner loop is a single interpolation */
+ tmp = dst[i] * dst_alpha;
+ tmp = (tmp>>16) * (65535 - src_shape) +
+ src[i] * src_shape + (result_alpha>>1);
+ dst[i] = tmp / result_alpha;
+ }
+ }
+ dst[n_chan] = result_alpha;
+ }
+ } else {
+ /* Do compositing with blending */
+ uint16_t blend[ART_MAX_CHAN];
+ uint16_t a_b, a_s;
+ unsigned int a_r;
+ int src_scale;
+ int c_b, c_s;
+
+ a_s = src[n_chan];
+ a_b = dst[n_chan];
+
+ /* Result alpha is Union of backdrop and source alpha */
+ tmp = (0xffff - a_b) * (0xffff - a_s) + 0x8000;
+ a_r = 0xffff - (((tmp >> 16) + tmp) >> 16);
+ /* todo: verify that a_r is nonzero in all cases */
+
+ /* Compute a_s / a_r in 16.16 format */
+ src_scale = ((a_s << 16) + (a_r >> 1)) / a_r;
+
+ src_scale >>= 1; /* Lose a bit to avoid overflow */
+ a_b >>= 1; /* Lose a bit to avoid overflow */
+ art_blend_pixel_16(blend, dst, src, n_chan, blend_mode, pblend_procs, p14dev);
+ for (i = 0; i < n_chan; i++) {
+ int c_bl; /* Result of blend function */
+ int c_mix; /* Blend result mixed with source color */
+ int stmp;
+
+ c_s = src[i];
+ c_b = dst[i];
+ c_bl = blend[i];
+ stmp = a_b * (c_bl - ((int)c_s)) + 0x4000;
+ c_mix = c_s + (((stmp >> 16) + stmp) >> 15);
+ tmp = src_scale * (c_mix - c_b) + 0x4000;
+ dst[i] = c_b + (tmp >> 15);
+ }
+ dst[n_chan] = a_r;
+ }
+}
+
+#if RAW_DUMP
+/* Debug dump of buffer data from pdf14 device. Saved in
+ planar form with global indexing and tag information in
+ file name */
+static void
+do_dump_raw_buffer(const gs_memory_t *mem, int num_rows, int width, int n_chan,
+ int plane_stride, int rowstride,
+ char filename[], const byte *Buffer, bool deep, bool be)
+{
+ char full_file_name[50];
+ gp_file *fid;
+ int z,y;
+ const byte *buff_ptr;
+ int max_bands;
+
+ /* clist_band_count is incremented at every pdf14putimage */
+ /* Useful for catching this thing and only dumping */
+ /* during a particular band if we have a large file */
+ /* if (clist_band_count != 65) return; */
+ buff_ptr = Buffer;
+#if RAW_DUMP_AS_PAM
+ /* FIXME: GRAY + ALPHA + SHAPE + TAGS will be interpreted as RGB + ALPHA */
+ if ((n_chan == 2) || (n_chan == 3)) {
+ int x;
+ dlprintf2("%02d)%s.pam\n",global_index,filename);dflush();
+ gs_sprintf(full_file_name,"%02d)%s.pam",global_index,filename);
+ fid = gp_fopen(mem,full_file_name,"wb");
+ fprintf(fid, "P7\nWIDTH %d\nHEIGHT %d\nDEPTH 2\nMAXVAL %d\nTUPLTYPE GRAYSCALE_ALPHA\nENDHDR\n",
+ width, num_rows, deep ? 65535 : 255);
+ if (deep) {
+ for(y=0; y<num_rows; y++)
+ for(x=0; x<width; x++)
+ for(z=0; z<2; z++) {
+ /* This assumes a little endian host. Sue me. */
+ gp_fputc(Buffer[z*plane_stride + y*rowstride + x*2 + be^1], fid);
+ gp_fputc(Buffer[z*plane_stride + y*rowstride + x*2 + be ], fid);
+ }
+ } else {
+ for(y=0; y<num_rows; y++)
+ for(x=0; x<width; x++)
+ for(z=0; z<2; z++)
+ gp_fputc(Buffer[z*plane_stride + y*rowstride + x], fid);
+ }
+ gp_fclose(fid);
+ if (n_chan == 3) {
+ dlprintf2("%02d)%s_shape.pgm\n",global_index,filename);dflush();
+ gs_sprintf(full_file_name,"%02d)%s_shape.pgm",global_index,filename);
+ fid = gp_fopen(mem,full_file_name,"wb");
+ fprintf(fid, "P5\n%d %d %d\n",
+ width, num_rows, deep ? 65535 : 255);
+ if (deep) {
+ for(y=0; y<num_rows; y++)
+ for(x=0; x<width; x++) {
+ /* This assumes a little endian host. Sue me. */
+ gp_fputc(Buffer[2*plane_stride + y*rowstride + x * 2 + be^1], fid);
+ gp_fputc(Buffer[2*plane_stride + y*rowstride + x * 2 + be ], fid);
+ }
+ } else {
+ for(y=0; y<num_rows; y++)
+ for(x=0; x<width; x++)
+ gp_fputc(Buffer[2*plane_stride + y*rowstride + x], fid);
+ }
+ gp_fclose(fid);
+ }
+ }
+ if ((n_chan == 4) || (n_chan == 5) || (n_chan == 6)) {
+ int x;
+ dprintf2("%02d)%s.pam\n",global_index,filename);dflush();
+ gs_sprintf(full_file_name,"%02d)%s.pam",global_index,filename);
+ fid = gp_fopen(mem,full_file_name,"wb");
+ fprintf(fid, "P7\nWIDTH %d\nHEIGHT %d\nDEPTH 4\nMAXVAL %d\nTUPLTYPE RGB_ALPHA\nENDHDR\n",
+ width, num_rows, deep ? 65535 : 255);
+ if (deep) {
+ for(y=0; y<num_rows; y++)
+ for(x=0; x<width; x++)
+ for(z=0; z<4; z++) {
+ /* This assumes a little endian host. Sue me. */
+ gp_fputc(Buffer[z*plane_stride + y*rowstride + x*2 + be^1], fid);
+ gp_fputc(Buffer[z*plane_stride + y*rowstride + x*2 + be ], fid);
+ }
+ } else {
+ for(y=0; y<num_rows; y++)
+ for(x=0; x<width; x++)
+ for(z=0; z<4; z++)
+ gp_fputc(Buffer[z*plane_stride + y*rowstride + x], fid);
+ }
+ gp_fclose(fid);
+ if (n_chan > 4) {
+ gs_sprintf(full_file_name,"%02d)%s_shape.pgm",global_index,filename);
+ fid = gp_fopen(mem,full_file_name,"wb");
+ fprintf(fid, "P5\n%d %d %d\n",
+ width, num_rows, deep ? 65535 : 255);
+ if (deep) {
+ for(y=0; y<num_rows; y++)
+ for(x=0; x<width; x++) {
+ /* This assumes a little endian host. Sue me. */
+ gp_fputc(Buffer[4*plane_stride + y*rowstride + x*2 + be^1], fid);
+ gp_fputc(Buffer[4*plane_stride + y*rowstride + x*2 + be ], fid);
+ }
+ } else {
+ for(y=0; y<num_rows; y++)
+ for(x=0; x<width; x++)
+ gp_fputc(Buffer[4*plane_stride + y*rowstride + x], fid);
+ }
+ gp_fclose(fid);
+ }
+ if (n_chan == 6) {
+ gs_sprintf(full_file_name,"%02d)%s_tags.pgm",global_index,filename);
+ fid = gp_fopen(mem, full_file_name,"wb");
+ fprintf(fid, "P5\n%d %d 255\n", width, num_rows);
+ if (deep) {
+ for(y=0; y<num_rows; y++)
+ for(x=0; x<width; x++)
+ gp_fputc(Buffer[5*plane_stride + y*rowstride + x*2 + be ], fid);
+ } else {
+ for(y=0; y<num_rows; y++)
+ for(x=0; x<width; x++)
+ gp_fputc(Buffer[5*plane_stride + y*rowstride + x], fid);
+ }
+ gp_fclose(fid);
+ }
+ return;
+ }
+#endif
+ max_bands = ( n_chan < 57 ? n_chan : 56); /* Photoshop handles at most 56 bands */
+ dlprintf6("%02d)%s_%dx%dx%dx%d.raw\n",global_index,filename,width,num_rows,deep ? 16 : 8,max_bands);dflush();
+ gs_sprintf(full_file_name,"%02d)%s_%dx%dx%dx%d.raw",global_index,filename,width,num_rows,deep ? 16 : 8,max_bands);
+ fid = gp_fopen(mem, full_file_name,"wb");
+
+ if (be && deep) {
+ for (z = 0; z < max_bands; ++z) {
+ /* grab pointer to the next plane */
+ buff_ptr = &(Buffer[z*plane_stride]);
+ for ( y = 0; y < num_rows; y++ ) {
+ /* write out each row */
+ int x;
+ for (x = 0; x < width; x++ ) {
+ gp_fputc(buff_ptr[x*2 + be^1], fid);
+ gp_fputc(buff_ptr[x*2 + be ], fid);
+ }
+ buff_ptr += rowstride;
+ }
+ }
+ } else {
+ for (z = 0; z < max_bands; ++z) {
+ /* grab pointer to the next plane */
+ buff_ptr = &(Buffer[z*plane_stride]);
+ for ( y = 0; y < num_rows; y++ ) {
+ /* write out each row */
+ gp_fwrite(buff_ptr,sizeof(unsigned char),width<<deep,fid);
+ buff_ptr += rowstride;
+ }
+ }
+ }
+ gp_fclose(fid);
+}
+
+void
+dump_raw_buffer(const gs_memory_t *mem, int num_rows, int width, int n_chan,
+ int plane_stride, int rowstride,
+ char filename[],const byte *Buffer, bool deep)
+{
+ do_dump_raw_buffer(mem, num_rows, width, n_chan, plane_stride,
+ rowstride, filename, Buffer, deep, 0);
+}
+
+void
+dump_raw_buffer_be(const gs_memory_t *mem, int num_rows, int width, int n_chan,
+ int plane_stride, int rowstride,
+ char filename[],const byte *Buffer, bool deep)
+{
+ do_dump_raw_buffer(mem, num_rows, width, n_chan, plane_stride,
+ rowstride, filename, Buffer, deep, 1);
+}
+#endif
+
+typedef void (*art_pdf_compose_group_fn)(byte *tos_ptr, bool tos_isolated, int tos_planestride, int tos_rowstride,
+ byte alpha, byte shape, gs_blend_mode_t blend_mode, bool tos_has_shape,
+ int tos_shape_offset, int tos_alpha_g_offset, int tos_tag_offset, bool tos_has_tag,
+ byte *nos_ptr, bool nos_isolated, int nos_planestride, int nos_rowstride,
+ byte *nos_alpha_g_ptr, bool nos_knockout, int nos_shape_offset, int nos_tag_offset,
+ byte *mask_row_ptr, int has_mask, pdf14_buf *maskbuf, byte mask_bg_alpha, const byte *mask_tr_fn,
+ byte *backdrop_ptr, bool has_matte, int n_chan, bool additive, int num_spots, bool overprint,
+ gx_color_index drawn_comps, int x0, int y0, int x1, int y1,
+ const pdf14_nonseparable_blending_procs_t *pblend_procs, pdf14_device *pdev);
+
+static forceinline void
+template_compose_group(byte *gs_restrict tos_ptr, bool tos_isolated,
+ int tos_planestride, int tos_rowstride,
+ byte alpha, byte shape, gs_blend_mode_t blend_mode,
+ bool tos_has_shape, int tos_shape_offset,
+ int tos_alpha_g_offset, int tos_tag_offset,
+ bool tos_has_tag, byte *gs_restrict nos_ptr,
+ bool nos_isolated, int nos_planestride,
+ int nos_rowstride, byte *gs_restrict nos_alpha_g_ptr,
+ bool nos_knockout, int nos_shape_offset,
+ int nos_tag_offset, byte *gs_restrict mask_row_ptr,
+ int has_mask, pdf14_buf *gs_restrict maskbuf,
+ byte mask_bg_alpha, const byte *gs_restrict mask_tr_fn,
+ byte *gs_restrict backdrop_ptr, bool has_matte,
+ int n_chan, bool additive, int num_spots,
+ bool overprint, gx_color_index drawn_comps,
+ int x0, int y0, int x1, int y1,
+ const pdf14_nonseparable_blending_procs_t *pblend_procs,
+ pdf14_device *pdev, int has_alpha)
+{
+ byte *gs_restrict mask_curr_ptr = NULL;
+ int width = x1 - x0;
+ int x, y;
+ int i;
+ byte tos_pixel[PDF14_MAX_PLANES];
+ byte nos_pixel[PDF14_MAX_PLANES];
+ byte back_drop[PDF14_MAX_PLANES];
+ gx_color_index comps;
+ bool in_mask_rect_y;
+ bool in_mask_rect;
+ byte pix_alpha;
+ byte matte_alpha = 0xff;
+ int first_spot = n_chan - num_spots;
+ int first_blend_spot = n_chan;
+ bool has_mask2 = has_mask;
+ byte *gs_restrict dst;
+ byte global_shape = (byte)(255 * pdev->shape + 0.5);
+
+ if (!nos_knockout && num_spots > 0 && !blend_valid_for_spot(blend_mode)) {
+ first_blend_spot = first_spot;
+ }
+ if (blend_mode == BLEND_MODE_Normal)
+ first_blend_spot = 0;
+ if (!nos_isolated && backdrop_ptr != NULL)
+ has_mask2 = false;
+
+ for (y = y1 - y0; y > 0; --y) {
+ mask_curr_ptr = mask_row_ptr;
+ in_mask_rect_y = (has_mask && y1 - y >= maskbuf->rect.p.y && y1 - y < maskbuf->rect.q.y);
+ for (x = 0; x < width; x++) {
+ in_mask_rect = (in_mask_rect_y && x0 + x >= maskbuf->rect.p.x && x0 + x < maskbuf->rect.q.x);
+ pix_alpha = alpha;
+ /* If we have a soft mask, then we have some special handling of the
+ group alpha value */
+ if (maskbuf != NULL) {
+ if (!in_mask_rect) {
+ /* Special case where we have a soft mask but are outside
+ the range of the soft mask and must use the background
+ alpha value */
+ pix_alpha = mask_bg_alpha;
+ matte_alpha = 0xff;
+ } else {
+ if (has_matte)
+ matte_alpha = mask_tr_fn[*mask_curr_ptr];
+ }
+ }
+
+ /* Matte present, need to undo premultiplied alpha prior to blend */
+ if (has_matte && matte_alpha != 0 && matte_alpha < 0xff) {
+ for (i = 0; i < n_chan; i++) {
+ /* undo */
+ byte matte = maskbuf->matte[i]>>8;
+ int val = tos_ptr[i * tos_planestride] - matte;
+ int temp = ((((val * 0xff) << 8) / matte_alpha) >> 8) + matte;
+
+ /* clip */
+ if (temp > 0xff)
+ tos_pixel[i] = 0xff;
+ else if (temp < 0)
+ tos_pixel[i] = 0;
+ else
+ tos_pixel[i] = temp;
+
+ if (!additive) {
+ /* Pure subtractive */
+ tos_pixel[i] = 255 - tos_pixel[i];
+ nos_pixel[i] = 255 - nos_ptr[i * nos_planestride];
+ } else {
+ /* additive or hybrid */
+ if (i >= first_spot)
+ nos_pixel[i] = 255 - nos_ptr[i * nos_planestride];
+ else
+ nos_pixel[i] = nos_ptr[i * nos_planestride];
+ }
+ }
+ } else {
+ /* No matte present */
+ if (!additive) {
+ /* Pure subtractive */
+ for (i = 0; i < n_chan; ++i) {
+ tos_pixel[i] = 255 - tos_ptr[i * tos_planestride];
+ nos_pixel[i] = 255 - nos_ptr[i * nos_planestride];
+ }
+ } else {
+ /* Additive or hybrid */
+ for (i = 0; i < first_spot; ++i) {
+ tos_pixel[i] = tos_ptr[i * tos_planestride];
+ nos_pixel[i] = nos_ptr[i * nos_planestride];
+ }
+ for (; i < n_chan; i++) {
+ tos_pixel[i] = 255 - tos_ptr[i * tos_planestride];
+ nos_pixel[i] = 255 - nos_ptr[i * nos_planestride];
+ }
+ }
+ }
+ /* alpha */
+ tos_pixel[n_chan] = has_alpha ? tos_ptr[n_chan * tos_planestride] : 255;
+ nos_pixel[n_chan] = has_alpha ? nos_ptr[n_chan * nos_planestride] : 255;
+
+ if (mask_curr_ptr != NULL) {
+ if (in_mask_rect) {
+ byte mask = mask_tr_fn[*mask_curr_ptr++];
+ int tmp = pix_alpha * mask + 0x80;
+ pix_alpha = (tmp + (tmp >> 8)) >> 8;
+ } else {
+ mask_curr_ptr++;
+ }
+ }
+
+ dst = nos_pixel;
+ if (nos_knockout) {
+ /* We need to be knocking out what ever is on the nos, but may
+ need to combine with it's backdrop */
+ byte tos_shape = 255;
+
+ if (tos_has_shape)
+ tos_shape = tos_ptr[tos_shape_offset];
+
+ if (nos_isolated || backdrop_ptr == NULL) {
+ /* We do not need to compose with the backdrop */
+ back_drop[n_chan] = 0;
+ /* FIXME: The blend here can be simplified */
+ } else {
+ /* Per the PDF spec, since the tos is not isolated and we are
+ going onto a knock out group, we do the composition with
+ the nos initial backdrop. */
+ if (additive) {
+ /* additive or hybrid */
+ for (i = 0; i < first_spot; ++i) {
+ back_drop[i] = backdrop_ptr[i * nos_planestride];
+ }
+ for (; i < n_chan; i++) {
+ back_drop[i] = 255 - backdrop_ptr[i * nos_planestride];
+ }
+ } else {
+ /* pure subtractive */
+ for (i = 0; i < n_chan; ++i) {
+ back_drop[i] = 255 - backdrop_ptr[i * nos_planestride];
+ }
+ }
+ /* alpha */
+ back_drop[n_chan] = backdrop_ptr[n_chan * nos_planestride];
+ }
+ art_pdf_composite_knockout_group_8(back_drop, tos_shape,
+ nos_pixel, nos_alpha_g_ptr,
+ tos_pixel, n_chan, pix_alpha,
+ blend_mode, pblend_procs,
+ pdev, has_mask2);
+ } else if (tos_isolated ?
+ art_pdf_composite_group_8(nos_pixel, nos_alpha_g_ptr,
+ tos_pixel, n_chan, pix_alpha) :
+ art_pdf_recomposite_group_8(&dst, nos_alpha_g_ptr,
+ tos_pixel, has_alpha ? tos_ptr[tos_alpha_g_offset] : 255, n_chan,
+ pix_alpha, blend_mode, first_blend_spot,
+ pblend_procs, pdev)) {
+ dst = art_pdf_composite_pixel_alpha_8_inline(nos_pixel, tos_pixel, n_chan,
+ blend_mode, first_blend_spot,
+ pblend_procs, pdev);
+ }
+ if (nos_shape_offset && pix_alpha != 0) {
+ nos_ptr[nos_shape_offset] =
+ art_pdf_union_mul_8 (nos_ptr[nos_shape_offset],
+ has_alpha ? tos_ptr[tos_shape_offset] : global_shape,
+ shape);
+ }
+ if (dst)
+ {
+ /* Complement the results for subtractive color spaces. Again,
+ * if we are in an additive blending color space, we are not
+ * going to be fooling with overprint of spot colors */
+ if (additive) {
+ /* additive or hybrid */
+ for (i = 0; i < first_spot; ++i) {
+ nos_ptr[i * nos_planestride] = dst[i];
+ }
+ for (; i < n_chan; i++) {
+ nos_ptr[i * nos_planestride] = 255 - dst[i];
+ }
+ } else {
+ /* Pure subtractive */
+ /* If we were running in the compatible overprint blend mode
+ * and popping the group, we don't need to fool with the
+ * drawn components as that should have already have been
+ * handled during the blending within our special non-isolated
+ * group. So in other words, if the blend mode is normal
+ * (or compatible) and we are doing overprint, the overprint
+ * has NOT been handled by compatible overprint mode and we
+ * need to take care of it now */
+ if (overprint) {
+ for (i = 0, comps = drawn_comps; comps != 0; ++i, comps >>= 1) {
+ if ((comps & 0x1) != 0) {
+ nos_ptr[i * nos_planestride] = 255 - dst[i];
+ }
+ }
+ } else {
+ for (i = 0; i < n_chan; ++i)
+ nos_ptr[i * nos_planestride] = 255 - dst[i];
+ }
+ }
+ /* alpha */
+ nos_ptr[n_chan * nos_planestride] = dst[n_chan];
+ }
+ /* tags */
+ if (nos_tag_offset && tos_has_tag) {
+ nos_ptr[nos_tag_offset] |= tos_ptr[tos_tag_offset];
+ }
+
+ if (nos_alpha_g_ptr != NULL)
+ ++nos_alpha_g_ptr;
+ if (backdrop_ptr != NULL)
+ ++backdrop_ptr;
+ ++tos_ptr;
+ ++nos_ptr;
+ }
+ tos_ptr += tos_rowstride - width;
+ nos_ptr += nos_rowstride - width;
+ if (nos_alpha_g_ptr != NULL)
+ nos_alpha_g_ptr += nos_rowstride - width;
+ if (mask_row_ptr != NULL)
+ mask_row_ptr += maskbuf->rowstride;
+ if (backdrop_ptr != NULL)
+ backdrop_ptr += nos_rowstride - width;
+ }
+}
+
+static void
+compose_group_knockout(byte *tos_ptr, bool tos_isolated, int tos_planestride, int tos_rowstride, byte alpha, byte shape, gs_blend_mode_t blend_mode, bool tos_has_shape,
+ int tos_shape_offset, int tos_alpha_g_offset, int tos_tag_offset, bool tos_has_tag,
+ byte *nos_ptr, bool nos_isolated, int nos_planestride, int nos_rowstride, byte *nos_alpha_g_ptr, bool nos_knockout,
+ int nos_shape_offset, int nos_tag_offset,
+ byte *mask_row_ptr, int has_mask, pdf14_buf *maskbuf, byte mask_bg_alpha, const byte *mask_tr_fn,
+ byte *backdrop_ptr,
+ bool has_matte, int n_chan, bool additive, int num_spots, bool overprint, gx_color_index drawn_comps, int x0, int y0, int x1, int y1,
+ const pdf14_nonseparable_blending_procs_t *pblend_procs, pdf14_device *pdev)
+{
+ template_compose_group(tos_ptr, tos_isolated, tos_planestride, tos_rowstride, alpha, shape, blend_mode, tos_has_shape,
+ tos_shape_offset, tos_alpha_g_offset, tos_tag_offset, tos_has_tag,
+ nos_ptr, nos_isolated, nos_planestride, nos_rowstride, nos_alpha_g_ptr, /* nos_knockout = */1,
+ nos_shape_offset, nos_tag_offset, mask_row_ptr, has_mask, maskbuf, mask_bg_alpha, mask_tr_fn,
+ backdrop_ptr, has_matte, n_chan, additive, num_spots, overprint, drawn_comps, x0, y0, x1, y1, pblend_procs, pdev, 1);
+}
+
+static void
+compose_group_nonknockout_blend(byte *tos_ptr, bool tos_isolated, int tos_planestride, int tos_rowstride, byte alpha, byte shape, gs_blend_mode_t blend_mode, bool tos_has_shape,
+ int tos_shape_offset, int tos_alpha_g_offset, int tos_tag_offset, bool tos_has_tag,
+ byte *nos_ptr, bool nos_isolated, int nos_planestride, int nos_rowstride, byte *nos_alpha_g_ptr, bool nos_knockout,
+ int nos_shape_offset, int nos_tag_offset,
+ byte *mask_row_ptr, int has_mask, pdf14_buf *maskbuf, byte mask_bg_alpha, const byte *mask_tr_fn,
+ byte *backdrop_ptr,
+ bool has_matte, int n_chan, bool additive, int num_spots, bool overprint, gx_color_index drawn_comps, int x0, int y0, int x1, int y1,
+ const pdf14_nonseparable_blending_procs_t *pblend_procs, pdf14_device *pdev)
+{
+ template_compose_group(tos_ptr, tos_isolated, tos_planestride, tos_rowstride, alpha, shape, blend_mode, tos_has_shape,
+ tos_shape_offset, tos_alpha_g_offset, tos_tag_offset, tos_has_tag,
+ nos_ptr, nos_isolated, nos_planestride, nos_rowstride, nos_alpha_g_ptr, /* nos_knockout = */0,
+ nos_shape_offset, nos_tag_offset, mask_row_ptr, has_mask, maskbuf, mask_bg_alpha, mask_tr_fn,
+ backdrop_ptr, has_matte, n_chan, additive, num_spots, overprint, drawn_comps, x0, y0, x1, y1, pblend_procs, pdev, 1);
+}
+
+static void
+compose_group_nonknockout_nonblend_isolated_allmask_common(byte *tos_ptr, bool tos_isolated, int tos_planestride, int tos_rowstride, byte alpha, byte shape, gs_blend_mode_t blend_mode, bool tos_has_shape,
+ int tos_shape_offset, int tos_alpha_g_offset, int tos_tag_offset, bool tos_has_tag,
+ byte *nos_ptr, bool nos_isolated, int nos_planestride, int nos_rowstride, byte *nos_alpha_g_ptr, bool nos_knockout,
+ int nos_shape_offset, int nos_tag_offset,
+ byte *mask_row_ptr, int has_mask, pdf14_buf *maskbuf, byte mask_bg_alpha, const byte *mask_tr_fn,
+ byte *backdrop_ptr,
+ bool has_matte, int n_chan, bool additive, int num_spots, bool overprint, gx_color_index drawn_comps, int x0, int y0, int x1, int y1,
+ const pdf14_nonseparable_blending_procs_t *pblend_procs, pdf14_device *pdev)
+{
+ int width = x1 - x0;
+ int x, y;
+ int i;
+
+ for (y = y1 - y0; y > 0; --y) {
+ byte *gs_restrict mask_curr_ptr = mask_row_ptr;
+ for (x = 0; x < width; x++) {
+ byte mask = mask_tr_fn[*mask_curr_ptr++];
+ byte src_alpha = tos_ptr[n_chan * tos_planestride];
+ if (src_alpha != 0) {
+ byte a_b;
+
+ int tmp = alpha * mask + 0x80;
+ byte pix_alpha = (tmp + (tmp >> 8)) >> 8;
+
+ if (pix_alpha != 255) {
+ int tmp = src_alpha * pix_alpha + 0x80;
+ src_alpha = (tmp + (tmp >> 8)) >> 8;
+ }
+
+ a_b = nos_ptr[n_chan * nos_planestride];
+ if (a_b == 0) {
+ /* Simple copy of colors plus alpha. */
+ for (i = 0; i < n_chan; i++) {
+ nos_ptr[i * nos_planestride] = tos_ptr[i * tos_planestride];
+ }
+ nos_ptr[i * nos_planestride] = src_alpha;
+ } else {
+ /* Result alpha is Union of backdrop and source alpha */
+ int tmp = (0xff - a_b) * (0xff - src_alpha) + 0x80;
+ unsigned int a_r = 0xff - (((tmp >> 8) + tmp) >> 8);
+
+ /* Compute src_alpha / a_r in 16.16 format */
+ int src_scale = ((src_alpha << 16) + (a_r >> 1)) / a_r;
+
+ nos_ptr[n_chan * nos_planestride] = a_r;
+
+ /* Do simple compositing of source over backdrop */
+ for (i = 0; i < n_chan; i++) {
+ int c_s = tos_ptr[i * tos_planestride];
+ int c_b = nos_ptr[i * nos_planestride];
+ tmp = src_scale * (c_s - c_b) + 0x8000;
+ nos_ptr[i * nos_planestride] = c_b + (tmp >> 16);
+ }
+ }
+ }
+ ++tos_ptr;
+ ++nos_ptr;
+ }
+ tos_ptr += tos_rowstride - width;
+ nos_ptr += nos_rowstride - width;
+ mask_row_ptr += maskbuf->rowstride;
+ }
+}
+
+static void
+compose_group_nonknockout_nonblend_isolated_mask_common(byte *tos_ptr, bool tos_isolated, int tos_planestride, int tos_rowstride, byte alpha, byte shape, gs_blend_mode_t blend_mode, bool tos_has_shape,
+ int tos_shape_offset, int tos_alpha_g_offset, int tos_tag_offset, bool tos_has_tag,
+ byte *nos_ptr, bool nos_isolated, int nos_planestride, int nos_rowstride, byte *nos_alpha_g_ptr, bool nos_knockout,
+ int nos_shape_offset, int nos_tag_offset,
+ byte *mask_row_ptr, int has_mask, pdf14_buf *maskbuf, byte mask_bg_alpha, const byte *mask_tr_fn,
+ byte *backdrop_ptr,
+ bool has_matte, int n_chan, bool additive, int num_spots, bool overprint, gx_color_index drawn_comps, int x0, int y0, int x1, int y1,
+ const pdf14_nonseparable_blending_procs_t *pblend_procs, pdf14_device *pdev)
+{
+ byte *gs_restrict mask_curr_ptr = NULL;
+ int width = x1 - x0;
+ int x, y;
+ int i;
+ bool in_mask_rect_y;
+ bool in_mask_rect;
+ byte pix_alpha, src_alpha;
+
+ for (y = y1 - y0; y > 0; --y) {
+ mask_curr_ptr = mask_row_ptr;
+ in_mask_rect_y = (has_mask && y1 - y >= maskbuf->rect.p.y && y1 - y < maskbuf->rect.q.y);
+ for (x = 0; x < width; x++) {
+ in_mask_rect = (in_mask_rect_y && has_mask && x0 + x >= maskbuf->rect.p.x && x0 + x < maskbuf->rect.q.x);
+ pix_alpha = alpha;
+ /* If we have a soft mask, then we have some special handling of the
+ group alpha value */
+ if (maskbuf != NULL) {
+ if (!in_mask_rect) {
+ /* Special case where we have a soft mask but are outside
+ the range of the soft mask and must use the background
+ alpha value */
+ pix_alpha = mask_bg_alpha;
+ }
+ }
+
+ if (mask_curr_ptr != NULL) {
+ if (in_mask_rect) {
+ byte mask = mask_tr_fn[*mask_curr_ptr++];
+ int tmp = pix_alpha * mask + 0x80;
+ pix_alpha = (tmp + (tmp >> 8)) >> 8;
+ } else {
+ mask_curr_ptr++;
+ }
+ }
+
+ src_alpha = tos_ptr[n_chan * tos_planestride];
+ if (src_alpha != 0) {
+ byte a_b;
+
+ if (pix_alpha != 255) {
+ int tmp = src_alpha * pix_alpha + 0x80;
+ src_alpha = (tmp + (tmp >> 8)) >> 8;
+ }
+
+ a_b = nos_ptr[n_chan * nos_planestride];
+ if (a_b == 0) {
+ /* Simple copy of colors plus alpha. */
+ for (i = 0; i < n_chan; i++) {
+ nos_ptr[i * nos_planestride] = tos_ptr[i * tos_planestride];
+ }
+ nos_ptr[i * nos_planestride] = src_alpha;
+ } else {
+ /* Result alpha is Union of backdrop and source alpha */
+ int tmp = (0xff - a_b) * (0xff - src_alpha) + 0x80;
+ unsigned int a_r = 0xff - (((tmp >> 8) + tmp) >> 8);
+
+ /* Compute src_alpha / a_r in 16.16 format */
+ int src_scale = ((src_alpha << 16) + (a_r >> 1)) / a_r;
+
+ nos_ptr[n_chan * nos_planestride] = a_r;
+
+ /* Do simple compositing of source over backdrop */
+ for (i = 0; i < n_chan; i++) {
+ int c_s = tos_ptr[i * tos_planestride];
+ int c_b = nos_ptr[i * nos_planestride];
+ tmp = (c_b << 16) + src_scale * (c_s - c_b) + 0x8000;
+ nos_ptr[i * nos_planestride] = tmp >> 16;
+ }
+ }
+ }
+ ++tos_ptr;
+ ++nos_ptr;
+ }
+ tos_ptr += tos_rowstride - width;
+ nos_ptr += nos_rowstride - width;
+ if (mask_row_ptr != NULL)
+ mask_row_ptr += maskbuf->rowstride;
+ }
+}
+
+static void
+compose_group_nonknockout_nonblend_isolated_nomask_common(byte *tos_ptr, bool tos_isolated, int tos_planestride, int tos_rowstride, byte alpha, byte shape, gs_blend_mode_t blend_mode, bool tos_has_shape,
+ int tos_shape_offset, int tos_alpha_g_offset, int tos_tag_offset, bool tos_has_tag,
+ byte *nos_ptr, bool nos_isolated, int nos_planestride, int nos_rowstride, byte *nos_alpha_g_ptr, bool nos_knockout,
+ int nos_shape_offset, int nos_tag_offset,
+ byte *mask_row_ptr, int has_mask, pdf14_buf *maskbuf, byte mask_bg_alpha, const byte *mask_tr_fn,
+ byte *backdrop_ptr,
+ bool has_matte, int n_chan, bool additive, int num_spots, bool overprint, gx_color_index drawn_comps, int x0, int y0, int x1, int y1,
+ const pdf14_nonseparable_blending_procs_t *pblend_procs, pdf14_device *pdev)
+{
+ template_compose_group(tos_ptr, /*tos_isolated*/1, tos_planestride, tos_rowstride, alpha, shape, BLEND_MODE_Normal, /*tos_has_shape*/0,
+ tos_shape_offset, tos_alpha_g_offset, tos_tag_offset, /*tos_has_tag*/0,
+ nos_ptr, /*nos_isolated*/0, nos_planestride, nos_rowstride, /*nos_alpha_g_ptr*/0, /* nos_knockout = */0,
+ /*nos_shape_offset*/0, /*nos_tag_offset*/0, mask_row_ptr, /*has_mask*/0, /*maskbuf*/NULL, mask_bg_alpha, mask_tr_fn,
+ backdrop_ptr, /*has_matte*/0, n_chan, /*additive*/1, /*num_spots*/0, /*overprint*/0, /*drawn_comps*/0, x0, y0, x1, y1, pblend_procs, pdev, 1);
+}
+
+static void
+compose_group_nonknockout_nonblend_nonisolated_mask_common(byte *tos_ptr, bool tos_isolated, int tos_planestride, int tos_rowstride, byte alpha, byte shape, gs_blend_mode_t blend_mode, bool tos_has_shape,
+ int tos_shape_offset, int tos_alpha_g_offset, int tos_tag_offset, bool tos_has_tag,
+ byte *nos_ptr, bool nos_isolated, int nos_planestride, int nos_rowstride, byte *nos_alpha_g_ptr, bool nos_knockout,
+ int nos_shape_offset, int nos_tag_offset,
+ byte *mask_row_ptr, int has_mask, pdf14_buf *maskbuf, byte mask_bg_alpha, const byte *mask_tr_fn,
+ byte *backdrop_ptr,
+ bool has_matte, int n_chan, bool additive, int num_spots, bool overprint, gx_color_index drawn_comps, int x0, int y0, int x1, int y1,
+ const pdf14_nonseparable_blending_procs_t *pblend_procs, pdf14_device *pdev)
+{
+ template_compose_group(tos_ptr, /*tos_isolated*/0, tos_planestride, tos_rowstride, alpha, shape, BLEND_MODE_Normal, /*tos_has_shape*/0,
+ tos_shape_offset, tos_alpha_g_offset, tos_tag_offset, /*tos_has_tag*/0,
+ nos_ptr, /*nos_isolated*/0, nos_planestride, nos_rowstride, /*nos_alpha_g_ptr*/0, /* nos_knockout = */0,
+ /*nos_shape_offset*/0, /*nos_tag_offset*/0, mask_row_ptr, has_mask, maskbuf, mask_bg_alpha, mask_tr_fn,
+ backdrop_ptr, /*has_matte*/0, n_chan, /*additive*/1, /*num_spots*/0, /*overprint*/0, /*drawn_comps*/0, x0, y0, x1, y1, pblend_procs, pdev, 1);
+}
+
+static void
+compose_group_nonknockout_nonblend_nonisolated_nomask_common(byte *tos_ptr, bool tos_isolated, int tos_planestride, int tos_rowstride, byte alpha, byte shape, gs_blend_mode_t blend_mode, bool tos_has_shape,
+ int tos_shape_offset, int tos_alpha_g_offset, int tos_tag_offset, bool tos_has_tag,
+ byte *nos_ptr, bool nos_isolated, int nos_planestride, int nos_rowstride, byte *nos_alpha_g_ptr, bool nos_knockout,
+ int nos_shape_offset, int nos_tag_offset,
+ byte *mask_row_ptr, int has_mask, pdf14_buf *maskbuf, byte mask_bg_alpha, const byte *mask_tr_fn,
+ byte *backdrop_ptr,
+ bool has_matte, int n_chan, bool additive, int num_spots, bool overprint, gx_color_index drawn_comps, int x0, int y0, int x1, int y1,
+ const pdf14_nonseparable_blending_procs_t *pblend_procs, pdf14_device *pdev)
+{
+ template_compose_group(tos_ptr, /*tos_isolated*/0, tos_planestride, tos_rowstride, alpha, shape, BLEND_MODE_Normal, /*tos_has_shape*/0,
+ tos_shape_offset, tos_alpha_g_offset, tos_tag_offset, /*tos_has_tag*/0,
+ nos_ptr, /*nos_isolated*/0, nos_planestride, nos_rowstride, /*nos_alpha_g_ptr*/0, /* nos_knockout = */0,
+ /*nos_shape_offset*/0, /*nos_tag_offset*/0, mask_row_ptr, /*has_mask*/0, /*maskbuf*/NULL, mask_bg_alpha, mask_tr_fn,
+ backdrop_ptr, /*has_matte*/0, n_chan, /*additive*/1, /*num_spots*/0, /*overprint*/0, /*drawn_comps*/0, x0, y0, x1, y1, pblend_procs, pdev, 1);
+}
+
+static void
+compose_group_nonknockout_noblend_general(byte *tos_ptr, bool tos_isolated, int tos_planestride, int tos_rowstride, byte alpha, byte shape, gs_blend_mode_t blend_mode, bool tos_has_shape,
+ int tos_shape_offset, int tos_alpha_g_offset, int tos_tag_offset, bool tos_has_tag,
+ byte *nos_ptr, bool nos_isolated, int nos_planestride, int nos_rowstride, byte *nos_alpha_g_ptr, bool nos_knockout,
+ int nos_shape_offset, int nos_tag_offset,
+ byte *mask_row_ptr, int has_mask, pdf14_buf *maskbuf, byte mask_bg_alpha, const byte *mask_tr_fn,
+ byte *backdrop_ptr,
+ bool has_matte, int n_chan, bool additive, int num_spots, bool overprint, gx_color_index drawn_comps, int x0, int y0, int x1, int y1,
+ const pdf14_nonseparable_blending_procs_t *pblend_procs, pdf14_device *pdev)
+{
+ template_compose_group(tos_ptr, tos_isolated, tos_planestride, tos_rowstride, alpha, shape, BLEND_MODE_Normal, tos_has_shape,
+ tos_shape_offset, tos_alpha_g_offset, tos_tag_offset, tos_has_tag,
+ nos_ptr, nos_isolated, nos_planestride, nos_rowstride, nos_alpha_g_ptr, /* nos_knockout = */0,
+ nos_shape_offset, nos_tag_offset, mask_row_ptr, has_mask, maskbuf, mask_bg_alpha, mask_tr_fn,
+ backdrop_ptr, has_matte, n_chan, additive, num_spots, overprint, drawn_comps, x0, y0, x1, y1, pblend_procs, pdev, 1);
+}
+
+static void
+compose_group_alphaless_knockout(byte *tos_ptr, bool tos_isolated, int tos_planestride, int tos_rowstride, byte alpha, byte shape, gs_blend_mode_t blend_mode, bool tos_has_shape,
+ int tos_shape_offset, int tos_alpha_g_offset, int tos_tag_offset, bool tos_has_tag,
+ byte *nos_ptr, bool nos_isolated, int nos_planestride, int nos_rowstride, byte *nos_alpha_g_ptr, bool nos_knockout,
+ int nos_shape_offset, int nos_tag_offset,
+ byte *mask_row_ptr, int has_mask, pdf14_buf *maskbuf, byte mask_bg_alpha, const byte *mask_tr_fn,
+ byte *backdrop_ptr,
+ bool has_matte, int n_chan, bool additive, int num_spots, bool overprint, gx_color_index drawn_comps, int x0, int y0, int x1, int y1,
+ const pdf14_nonseparable_blending_procs_t *pblend_procs, pdf14_device *pdev)
+{
+ template_compose_group(tos_ptr, tos_isolated, tos_planestride, tos_rowstride, alpha, shape, blend_mode, tos_has_shape,
+ tos_shape_offset, tos_alpha_g_offset, tos_tag_offset, tos_has_tag,
+ nos_ptr, nos_isolated, nos_planestride, nos_rowstride, nos_alpha_g_ptr, /* nos_knockout = */1,
+ nos_shape_offset, nos_tag_offset, /* mask_row_ptr */ NULL, /* has_mask */ 0, /* maskbuf */ NULL, mask_bg_alpha, /* mask_tr_fn */ NULL,
+ backdrop_ptr, /* has_matte */ false , n_chan, additive, num_spots, overprint, drawn_comps, x0, y0, x1, y1, pblend_procs, pdev, 0);
+}
+
+static void
+compose_group_alphaless_nonknockout(byte *tos_ptr, bool tos_isolated, int tos_planestride, int tos_rowstride, byte alpha, byte shape, gs_blend_mode_t blend_mode, bool tos_has_shape,
+ int tos_shape_offset, int tos_alpha_g_offset, int tos_tag_offset, bool tos_has_tag,
+ byte *nos_ptr, bool nos_isolated, int nos_planestride, int nos_rowstride, byte *nos_alpha_g_ptr, bool nos_knockout,
+ int nos_shape_offset, int nos_tag_offset,
+ byte *mask_row_ptr, int has_mask, pdf14_buf *maskbuf, byte mask_bg_alpha, const byte *mask_tr_fn,
+ byte *backdrop_ptr,
+ bool has_matte, int n_chan, bool additive, int num_spots, bool overprint, gx_color_index drawn_comps, int x0, int y0, int x1, int y1,
+ const pdf14_nonseparable_blending_procs_t *pblend_procs, pdf14_device *pdev)
+{
+ template_compose_group(tos_ptr, tos_isolated, tos_planestride, tos_rowstride, alpha, shape, blend_mode, tos_has_shape,
+ tos_shape_offset, tos_alpha_g_offset, tos_tag_offset, tos_has_tag,
+ nos_ptr, nos_isolated, nos_planestride, nos_rowstride, nos_alpha_g_ptr, /* nos_knockout = */0,
+ nos_shape_offset, nos_tag_offset, /* mask_row_ptr */ NULL, /* has_mask */ 0, /* maskbuf */ NULL, mask_bg_alpha, /* mask_tr_fn */ NULL,
+ backdrop_ptr, /* has_matte */ false , n_chan, additive, num_spots, overprint, drawn_comps, x0, y0, x1, y1, pblend_procs, pdev, 0);
+}
+
+static void
+do_compose_group(pdf14_buf *tos, pdf14_buf *nos, pdf14_buf *maskbuf,
+ int x0, int x1, int y0, int y1, int n_chan, bool additive,
+ const pdf14_nonseparable_blending_procs_t * pblend_procs,
+ bool has_matte, bool overprint, gx_color_index drawn_comps,
+ gs_memory_t *memory, gx_device *dev)
+{
+ int num_spots = tos->num_spots;
+ byte alpha = tos->alpha>>8;
+ byte shape = tos->shape>>8;
+ gs_blend_mode_t blend_mode = tos->blend_mode;
+ byte *tos_ptr = tos->data + x0 - tos->rect.p.x +
+ (y0 - tos->rect.p.y) * tos->rowstride;
+ byte *nos_ptr = nos->data + x0 - nos->rect.p.x +
+ (y0 - nos->rect.p.y) * nos->rowstride;
+ byte *mask_row_ptr = NULL;
+ int tos_planestride = tos->planestride;
+ int nos_planestride = nos->planestride;
+ byte mask_bg_alpha = 0; /* Quiet compiler. */
+ bool tos_isolated = tos->isolated;
+ bool nos_isolated = nos->isolated;
+ bool nos_knockout = nos->knockout;
+ byte *nos_alpha_g_ptr;
+ int tos_shape_offset = n_chan * tos_planestride;
+ int tos_alpha_g_offset = tos_shape_offset + (tos->has_shape ? tos_planestride : 0);
+ bool tos_has_tag = tos->has_tags;
+ int tos_tag_offset = tos_planestride * (tos->n_planes - 1);
+ int nos_shape_offset = n_chan * nos_planestride;
+ int nos_alpha_g_offset = nos_shape_offset + (nos->has_shape ? nos_planestride : 0);
+ int nos_tag_offset = nos_planestride * (nos->n_planes - 1);
+ byte *mask_tr_fn = NULL; /* Quiet compiler. */
+ bool is_ident = true;
+ bool has_mask = false;
+ byte *backdrop_ptr = NULL;
+ pdf14_device *pdev = (pdf14_device *)dev;
+
+
+#if RAW_DUMP
+ byte *composed_ptr = NULL;
+ int width = x1 - x0;
+#endif
+ art_pdf_compose_group_fn fn;
+
+ if ((tos->n_chan == 0) || (nos->n_chan == 0))
+ return;
+ rect_merge(nos->dirty, tos->dirty);
+ if (nos->has_tags)
+ if_debug7m('v', memory,
+ "pdf14_pop_transparency_group y0 = %d, y1 = %d, w = %d, alpha = %d, shape = %d, tag = %d, bm = %d\n",
+ y0, y1, x1 - x0, alpha, shape, dev->graphics_type_tag & ~GS_DEVICE_ENCODES_TAGS, blend_mode);
+ else
+ if_debug6m('v', memory,
+ "pdf14_pop_transparency_group y0 = %d, y1 = %d, w = %d, alpha = %d, shape = %d, bm = %d\n",
+ y0, y1, x1 - x0, alpha, shape, blend_mode);
+ if (!nos->has_shape)
+ nos_shape_offset = 0;
+ if (!nos->has_tags)
+ nos_tag_offset = 0;
+ if (nos->has_alpha_g) {
+ nos_alpha_g_ptr = nos_ptr + nos_alpha_g_offset;
+ } else
+ nos_alpha_g_ptr = NULL;
+ if (nos->backdrop != NULL) {
+ backdrop_ptr = nos->backdrop + x0 - nos->rect.p.x +
+ (y0 - nos->rect.p.y) * nos->rowstride;
+ }
+ if (blend_mode != BLEND_MODE_Compatible && blend_mode != BLEND_MODE_Normal)
+ overprint = false;
+
+ if (maskbuf != NULL) {
+ int tmp;
+
+ mask_tr_fn = maskbuf->transfer_fn;
+
+ is_ident = maskbuf->is_ident;
+ /* Make sure we are in the mask buffer */
+ if (maskbuf->data != NULL) {
+ mask_row_ptr = maskbuf->data + x0 - maskbuf->rect.p.x +
+ (y0 - maskbuf->rect.p.y) * maskbuf->rowstride;
+ has_mask = true;
+ }
+ /* We may have a case, where we are outside the maskbuf rect. */
+ /* We would have avoided creating the maskbuf->data */
+ /* In that case, we should use the background alpha value */
+ /* See discussion on the BC entry in the PDF spec. */
+ mask_bg_alpha = maskbuf->alpha>>8;
+ /* Adjust alpha by the mask background alpha. This is only used
+ if we are outside the soft mask rect during the filling operation */
+ mask_bg_alpha = mask_tr_fn[mask_bg_alpha];
+ tmp = alpha * mask_bg_alpha + 0x80;
+ mask_bg_alpha = (tmp + (tmp >> 8)) >> 8;
+ }
+ n_chan--; /* Now the true number of colorants (i.e. not including alpha)*/
+#if RAW_DUMP
+ composed_ptr = nos_ptr;
+ dump_raw_buffer(memory, y1-y0, width, tos->n_planes, tos_planestride, tos->rowstride,
+ "bImageTOS", tos_ptr, tos->deep);
+ dump_raw_buffer(memory, y1-y0, width, nos->n_planes, nos_planestride, nos->rowstride,
+ "cImageNOS", nos_ptr, tos->deep);
+ if (maskbuf !=NULL && maskbuf->data != NULL) {
+ dump_raw_buffer(memory, maskbuf->rect.q.y - maskbuf->rect.p.y,
+ maskbuf->rect.q.x - maskbuf->rect.p.x, maskbuf->n_planes,
+ maskbuf->planestride, maskbuf->rowstride, "dMask",
+ maskbuf->data, maskbuf->deep);
+ }
+#endif
+
+ /* You might hope that has_mask iff maskbuf != NULL, but this is
+ * not the case. Certainly we can see cases where maskbuf != NULL
+ * and has_mask = 0. What's more, treating such cases as being
+ * has_mask = 0 causes diffs. */
+#ifdef TRACK_COMPOSE_GROUPS
+ {
+ int code = 0;
+
+ code += !!nos_knockout;
+ code += (!!nos_isolated)<<1;
+ code += (!!tos_isolated)<<2;
+ code += (!!tos->has_shape)<<3;
+ code += (!!tos_has_tag)<<4;
+ code += (!!additive)<<5;
+ code += (!!overprint)<<6;
+ code += (!!has_mask || maskbuf != NULL)<<7;
+ code += (!!has_matte)<<8;
+ code += (backdrop_ptr != NULL)<<9;
+ code += (num_spots != 0)<<10;
+ code += blend_mode<<11;
+
+ if (track_compose_groups == 0)
+ {
+ atexit(dump_track_compose_groups);
+ track_compose_groups = 1;
+ }
+ compose_groups[code]++;
+ }
+#endif
+
+ /* We have tested the files on the cluster to see what percentage of
+ * files/devices hit the different options. */
+ if (nos_knockout)
+ fn = &compose_group_knockout; /* Small %ages, nothing more than 1.1% */
+ else if (blend_mode != 0)
+ fn = &compose_group_nonknockout_blend; /* Small %ages, nothing more than 2% */
+ else if (tos->has_shape == 0 && tos_has_tag == 0 && nos_isolated == 0 && nos_alpha_g_ptr == NULL &&
+ nos_shape_offset == 0 && nos_tag_offset == 0 && backdrop_ptr == NULL && has_matte == 0 && num_spots == 0 &&
+ overprint == 0) {
+ /* Additive vs Subtractive makes no difference in normal blend mode with no spots */
+ if (tos_isolated) {
+ if (has_mask || maskbuf) {/* 7% */
+ /* AirPrint test case hits this */
+ if (maskbuf && maskbuf->rect.p.x <= x0 && maskbuf->rect.p.y <= y0 &&
+ maskbuf->rect.q.x >= x1 && maskbuf->rect.q.y >= y1) {
+ /* AVX and SSE accelerations only valid if maskbuf transfer
+ function is identity and we have no matte color replacement */
+ if (is_ident && !has_matte) {
+ fn = compose_group_nonknockout_nonblend_isolated_allmask_common;
+#ifdef WITH_CAL
+ fn = (art_pdf_compose_group_fn)cal_get_compose_group(
+ memory->gs_lib_ctx->core->cal_ctx,
+ (cal_composer_proc_t *)fn,
+ tos->n_chan-1);
+#endif
+ } else {
+ fn = compose_group_nonknockout_nonblend_isolated_allmask_common;
+ }
+ } else
+ fn = &compose_group_nonknockout_nonblend_isolated_mask_common;
+ } else /* 14% */
+ fn = &compose_group_nonknockout_nonblend_isolated_nomask_common;
+ } else {
+ if (has_mask || maskbuf) /* 4% */
+ fn = &compose_group_nonknockout_nonblend_nonisolated_mask_common;
+ else /* 15% */
+ fn = &compose_group_nonknockout_nonblend_nonisolated_nomask_common;
+ }
+ } else
+ fn = compose_group_nonknockout_noblend_general;
+
+ fn(tos_ptr, tos_isolated, tos_planestride, tos->rowstride, alpha, shape,
+ blend_mode, tos->has_shape, tos_shape_offset, tos_alpha_g_offset,
+ tos_tag_offset, tos_has_tag, nos_ptr, nos_isolated, nos_planestride,
+ nos->rowstride, nos_alpha_g_ptr, nos_knockout, nos_shape_offset,
+ nos_tag_offset, mask_row_ptr, has_mask, maskbuf, mask_bg_alpha,
+ mask_tr_fn, backdrop_ptr, has_matte, n_chan, additive, num_spots,
+ overprint, drawn_comps, x0, y0, x1, y1, pblend_procs, pdev);
+
+#if RAW_DUMP
+ dump_raw_buffer(memory, y1-y0, width, nos->n_planes, nos_planestride, nos->rowstride,
+ "eComposed", composed_ptr, nos->deep);
+ global_index++;
+#endif
+}
+
+static inline uint16_t
+interp16(const uint16_t *table, uint16_t idx)
+{
+ byte top = idx>>8;
+ uint16_t a = table[top];
+ int b = table[top+1]-a;
+
+ return a + ((0x80 + b*(idx & 0xff))>>8);
+}
+
+typedef void (*art_pdf_compose_group16_fn)(uint16_t *tos_ptr, bool tos_isolated, int tos_planestride, int tos_rowstride,
+ uint16_t alpha, uint16_t shape, gs_blend_mode_t blend_mode, bool tos_has_shape,
+ int tos_shape_offset, int tos_alpha_g_offset, int tos_tag_offset, bool tos_has_tag,
+ uint16_t *nos_ptr, bool nos_isolated, int nos_planestride, int nos_rowstride,
+ uint16_t *nos_alpha_g_ptr, bool nos_knockout, int nos_shape_offset, int nos_tag_offset,
+ uint16_t *mask_row_ptr, int has_mask, pdf14_buf *maskbuf, uint16_t mask_bg_alpha, const uint16_t *mask_tr_fn,
+ uint16_t *backdrop_ptr, bool has_matte, int n_chan, bool additive, int num_spots, bool overprint,
+ gx_color_index drawn_comps, int x0, int y0, int x1, int y1,
+ const pdf14_nonseparable_blending_procs_t *pblend_procs, pdf14_device *pdev);
+
+static forceinline void
+template_compose_group16(uint16_t *gs_restrict tos_ptr, bool tos_isolated,
+ int tos_planestride, int tos_rowstride,
+ uint16_t alpha, uint16_t shape, gs_blend_mode_t blend_mode,
+ bool tos_has_shape, int tos_shape_offset,
+ int tos_alpha_g_offset, int tos_tag_offset,
+ bool tos_has_tag, uint16_t *gs_restrict nos_ptr,
+ bool nos_isolated, int nos_planestride,
+ int nos_rowstride, uint16_t *gs_restrict nos_alpha_g_ptr,
+ bool nos_knockout, int nos_shape_offset,
+ int nos_tag_offset, uint16_t *gs_restrict mask_row_ptr,
+ int has_mask, pdf14_buf *gs_restrict maskbuf,
+ uint16_t mask_bg_alpha, const uint16_t *gs_restrict mask_tr_fn,
+ uint16_t *gs_restrict backdrop_ptr, bool has_matte,
+ int n_chan, bool additive, int num_spots,
+ bool overprint, gx_color_index drawn_comps,
+ int x0, int y0, int x1, int y1,
+ const pdf14_nonseparable_blending_procs_t *pblend_procs,
+ pdf14_device *pdev, int has_alpha, bool tos_is_be)
+{
+ uint16_t *gs_restrict mask_curr_ptr = NULL;
+ int width = x1 - x0;
+ int x, y;
+ int i;
+ uint16_t tos_pixel[PDF14_MAX_PLANES];
+ uint16_t nos_pixel[PDF14_MAX_PLANES];
+ uint16_t back_drop[PDF14_MAX_PLANES];
+ gx_color_index comps;
+ bool in_mask_rect_y;
+ bool in_mask_rect;
+ uint16_t pix_alpha;
+ uint16_t matte_alpha = 0xffff;
+ int first_spot = n_chan - num_spots;
+ int first_blend_spot = n_chan;
+ bool has_mask2 = has_mask;
+ uint16_t *gs_restrict dst;
+ uint16_t global_shape = (uint16_t)(65535 * pdev->shape + 0.5);
+
+ if (!nos_knockout && num_spots > 0 && !blend_valid_for_spot(blend_mode)) {
+ first_blend_spot = first_spot;
+ }
+ if (blend_mode == BLEND_MODE_Normal)
+ first_blend_spot = 0;
+ if (!nos_isolated && backdrop_ptr != NULL)
+ has_mask2 = false;
+
+/* TOS data being passed to this routine is usually in native
+ * endian format (i.e. if it's from another pdf14 buffer). Occasionally,
+ * if it's being passed in from pdf_compose_alphaless_group16, it can be
+ * from memory produced by another memory device (such as a pattern
+ * cache device). That data is in big endian form. So we have a crufty
+ * macro to get 16 bits of data from either native or bigendian into
+ * a native value. This should resolve nicely at compile time. */
+#define GET16_2NATIVE(be, v) \
+ ((be) ? ((((byte *)&v)[0]<<8) | (((byte *)&v)[1])) : v)
+
+ for (y = y1 - y0; y > 0; --y) {
+ mask_curr_ptr = mask_row_ptr;
+ in_mask_rect_y = (has_mask && y1 - y >= maskbuf->rect.p.y && y1 - y < maskbuf->rect.q.y);
+ for (x = 0; x < width; x++) {
+ in_mask_rect = (in_mask_rect_y && x0 + x >= maskbuf->rect.p.x && x0 + x < maskbuf->rect.q.x);
+ pix_alpha = alpha;
+ /* If we have a soft mask, then we have some special handling of the
+ group alpha value */
+ if (maskbuf != NULL) {
+ if (!in_mask_rect) {
+ /* Special case where we have a soft mask but are outside
+ the range of the soft mask and must use the background
+ alpha value */
+ pix_alpha = mask_bg_alpha;
+ matte_alpha = 0xffff;
+ } else {
+ if (has_matte)
+ matte_alpha = interp16(mask_tr_fn, *mask_curr_ptr);
+ }
+ }
+
+ /* Matte present, need to undo premultiplied alpha prior to blend */
+ if (has_matte && matte_alpha != 0 && matte_alpha != 0xffff) {
+ for (i = 0; i < n_chan; i++) {
+ /* undo */
+ int val = GET16_2NATIVE(tos_is_be, tos_ptr[i * tos_planestride]) - maskbuf->matte[i];
+ int temp = (((unsigned int)(val * 0xffff)) / matte_alpha) + maskbuf->matte[i];
+
+ /* clip */
+ if (temp > 0xffff)
+ tos_pixel[i] = 0xffff;
+ else if (temp < 0)
+ tos_pixel[i] = 0;
+ else
+ tos_pixel[i] = temp;
+
+ if (!additive) {
+ /* Pure subtractive */
+ tos_pixel[i] = 65535 - tos_pixel[i];
+ nos_pixel[i] = 65535 - nos_ptr[i * nos_planestride];
+ } else {
+ /* additive or hybrid */
+ if (i >= first_spot)
+ nos_pixel[i] = 65535 - nos_ptr[i * nos_planestride];
+ else
+ nos_pixel[i] = nos_ptr[i * nos_planestride];
+ }
+ }
+ } else {
+ /* No matte present */
+ if (!additive) {
+ /* Pure subtractive */
+ for (i = 0; i < n_chan; ++i) {
+ tos_pixel[i] = 65535 - GET16_2NATIVE(tos_is_be, tos_ptr[i * tos_planestride]);
+ nos_pixel[i] = 65535 - nos_ptr[i * nos_planestride];
+ }
+ } else {
+ /* Additive or hybrid */
+ for (i = 0; i < first_spot; ++i) {
+ tos_pixel[i] = GET16_2NATIVE(tos_is_be, tos_ptr[i * tos_planestride]);
+ nos_pixel[i] = nos_ptr[i * nos_planestride];
+ }
+ for (; i < n_chan; i++) {
+ tos_pixel[i] = 65535 - GET16_2NATIVE(tos_is_be, tos_ptr[i * tos_planestride]);
+ nos_pixel[i] = 65535 - nos_ptr[i * nos_planestride];
+ }
+ }
+ }
+ /* alpha */
+ tos_pixel[n_chan] = has_alpha ? GET16_2NATIVE(tos_is_be, tos_ptr[n_chan * tos_planestride]) : 65535;
+ nos_pixel[n_chan] = has_alpha ? nos_ptr[n_chan * nos_planestride] : 65535;
+
+ if (mask_curr_ptr != NULL) {
+ if (in_mask_rect) {
+ uint16_t mask = interp16(mask_tr_fn, *mask_curr_ptr++);
+ int tmp = pix_alpha * (mask+(mask>>15)) + 0x8000;
+ pix_alpha = (tmp >> 16);
+ } else {
+ mask_curr_ptr++;
+ }
+ }
+
+ dst = nos_pixel;
+ if (nos_knockout) {
+ /* We need to be knocking out what ever is on the nos, but may
+ need to combine with it's backdrop */
+ uint16_t tos_shape = 65535;
+
+ if (tos_has_shape)
+ tos_shape = GET16_2NATIVE(tos_is_be, tos_ptr[tos_shape_offset]);
+
+ if (nos_isolated || backdrop_ptr == NULL) {
+ /* We do not need to compose with the backdrop */
+ back_drop[n_chan] = 0;
+ /* FIXME: The blend here can be simplified */
+ } else {
+ /* Per the PDF spec, since the tos is not isolated and we are
+ going onto a knock out group, we do the composition with
+ the nos initial backdrop. */
+ if (additive) {
+ /* additive or hybrid */
+ for (i = 0; i < first_spot; ++i) {
+ back_drop[i] = backdrop_ptr[i * nos_planestride];
+ }
+ for (; i < n_chan; i++) {
+ back_drop[i] = 65535 - backdrop_ptr[i * nos_planestride];
+ }
+ } else {
+ /* pure subtractive */
+ for (i = 0; i < n_chan; ++i) {
+ back_drop[i] = 65535 - backdrop_ptr[i * nos_planestride];
+ }
+ }
+ /* alpha */
+ back_drop[n_chan] = backdrop_ptr[n_chan * nos_planestride];
+ }
+ art_pdf_composite_knockout_group_16(back_drop, tos_shape,
+ nos_pixel, nos_alpha_g_ptr,
+ tos_pixel, n_chan, pix_alpha,
+ blend_mode, pblend_procs,
+ pdev, has_mask2);
+ }
+ else if (tos_isolated ?
+ art_pdf_composite_group_16(nos_pixel, nos_alpha_g_ptr,
+ tos_pixel, n_chan, pix_alpha) :
+ art_pdf_recomposite_group_16(&dst, nos_alpha_g_ptr,
+ tos_pixel,
+ has_alpha ? GET16_2NATIVE(tos_is_be, tos_ptr[tos_alpha_g_offset]) : 65535,
+ n_chan,
+ pix_alpha, blend_mode, first_blend_spot,
+ pblend_procs, pdev)) {
+ dst = art_pdf_composite_pixel_alpha_16_inline(nos_pixel, tos_pixel, n_chan,
+ blend_mode, first_blend_spot,
+ pblend_procs, pdev);
+ }
+ if (nos_shape_offset && pix_alpha != 0) {
+ nos_ptr[nos_shape_offset] =
+ art_pdf_union_mul_16(nos_ptr[nos_shape_offset],
+ has_alpha ? GET16_2NATIVE(tos_is_be, tos_ptr[tos_shape_offset]) : global_shape,
+ shape);
+ }
+ if (dst)
+ {
+ /* Complement the results for subtractive color spaces. Again,
+ * if we are in an additive blending color space, we are not
+ * going to be fooling with overprint of spot colors */
+ if (additive) {
+ /* additive or hybrid */
+ for (i = 0; i < first_spot; ++i) {
+ nos_ptr[i * nos_planestride] = dst[i];
+ }
+ for (; i < n_chan; i++) {
+ nos_ptr[i * nos_planestride] = 65535 - dst[i];
+ }
+ } else {
+ /* Pure subtractive */
+ /* If we were running in the compatible overprint blend mode
+ * and popping the group, we don't need to fool with the
+ * drawn components as that should have already have been
+ * handled during the blending within our special non-isolated
+ * group. So in other words, if the blend mode is normal
+ * (or compatible) and we are doing overprint, the overprint
+ * has NOT been handled by compatible overprint mode and we
+ * need to take care of it now */
+ if (overprint) {
+ for (i = 0, comps = drawn_comps; comps != 0; ++i, comps >>= 1) {
+ if ((comps & 0x1) != 0) {
+ nos_ptr[i * nos_planestride] = 65535 - dst[i];
+ }
+ }
+ } else {
+ for (i = 0; i < n_chan; ++i)
+ nos_ptr[i * nos_planestride] = 65535 - dst[i];
+ }
+ }
+ /* alpha */
+ nos_ptr[n_chan * nos_planestride] = dst[n_chan];
+ }
+ /* tags */
+ if (nos_tag_offset && tos_has_tag) {
+ nos_ptr[nos_tag_offset] |= tos_ptr[tos_tag_offset];
+ }
+
+ if (nos_alpha_g_ptr != NULL)
+ ++nos_alpha_g_ptr;
+ if (backdrop_ptr != NULL)
+ ++backdrop_ptr;
+ ++tos_ptr;
+ ++nos_ptr;
+ }
+ tos_ptr += tos_rowstride - width;
+ nos_ptr += nos_rowstride - width;
+ if (nos_alpha_g_ptr != NULL)
+ nos_alpha_g_ptr += nos_rowstride - width;
+ if (mask_row_ptr != NULL)
+ mask_row_ptr += maskbuf->rowstride>>1;
+ if (backdrop_ptr != NULL)
+ backdrop_ptr += nos_rowstride - width;
+ }
+}
+
+static void
+compose_group16_knockout(uint16_t *tos_ptr, bool tos_isolated, int tos_planestride, int tos_rowstride, uint16_t alpha, uint16_t shape, gs_blend_mode_t blend_mode,
+ bool tos_has_shape, int tos_shape_offset, int tos_alpha_g_offset, int tos_tag_offset, bool tos_has_tag,
+ uint16_t *nos_ptr, bool nos_isolated, int nos_planestride, int nos_rowstride, uint16_t *nos_alpha_g_ptr, bool nos_knockout,
+ int nos_shape_offset, int nos_tag_offset,
+ uint16_t *mask_row_ptr, int has_mask, pdf14_buf *maskbuf, uint16_t mask_bg_alpha, const uint16_t *mask_tr_fn,
+ uint16_t *backdrop_ptr,
+ bool has_matte, int n_chan, bool additive, int num_spots, bool overprint, gx_color_index drawn_comps, int x0, int y0, int x1, int y1,
+ const pdf14_nonseparable_blending_procs_t *pblend_procs, pdf14_device *pdev)
+{
+ template_compose_group16(tos_ptr, tos_isolated, tos_planestride, tos_rowstride, alpha, shape, blend_mode, tos_has_shape,
+ tos_shape_offset, tos_alpha_g_offset, tos_tag_offset, tos_has_tag,
+ nos_ptr, nos_isolated, nos_planestride, nos_rowstride, nos_alpha_g_ptr, /* nos_knockout = */1,
+ nos_shape_offset, nos_tag_offset, mask_row_ptr, has_mask, maskbuf, mask_bg_alpha, mask_tr_fn,
+ backdrop_ptr, has_matte, n_chan, additive, num_spots, overprint, drawn_comps, x0, y0, x1, y1, pblend_procs, pdev, 1, 0);
+}
+
+static void
+compose_group16_nonknockout_blend(uint16_t *tos_ptr, bool tos_isolated, int tos_planestride, int tos_rowstride, uint16_t alpha, uint16_t shape,
+ gs_blend_mode_t blend_mode, bool tos_has_shape, int tos_shape_offset, int tos_alpha_g_offset, int tos_tag_offset, bool tos_has_tag,
+ uint16_t *nos_ptr, bool nos_isolated, int nos_planestride, int nos_rowstride, uint16_t *nos_alpha_g_ptr, bool nos_knockout,
+ int nos_shape_offset, int nos_tag_offset,
+ uint16_t *mask_row_ptr, int has_mask, pdf14_buf *maskbuf, uint16_t mask_bg_alpha, const uint16_t *mask_tr_fn,
+ uint16_t *backdrop_ptr,
+ bool has_matte, int n_chan, bool additive, int num_spots, bool overprint, gx_color_index drawn_comps, int x0, int y0, int x1, int y1,
+ const pdf14_nonseparable_blending_procs_t *pblend_procs, pdf14_device *pdev)
+{
+ template_compose_group16(tos_ptr, tos_isolated, tos_planestride, tos_rowstride, alpha, shape, blend_mode, tos_has_shape,
+ tos_shape_offset, tos_alpha_g_offset, tos_tag_offset, tos_has_tag,
+ nos_ptr, nos_isolated, nos_planestride, nos_rowstride, nos_alpha_g_ptr, /* nos_knockout = */0,
+ nos_shape_offset, nos_tag_offset, mask_row_ptr, has_mask, maskbuf, mask_bg_alpha, mask_tr_fn,
+ backdrop_ptr, has_matte, n_chan, additive, num_spots, overprint, drawn_comps, x0, y0, x1, y1, pblend_procs, pdev, 1, 0);
+}
+
+static void
+compose_group16_nonknockout_nonblend_isolated_allmask_common(uint16_t *tos_ptr, bool tos_isolated, int tos_planestride, int tos_rowstride, uint16_t alpha,
+ uint16_t shape, gs_blend_mode_t blend_mode, bool tos_has_shape, int tos_shape_offset, int tos_alpha_g_offset, int tos_tag_offset, bool tos_has_tag,
+ uint16_t *nos_ptr, bool nos_isolated, int nos_planestride, int nos_rowstride, uint16_t *nos_alpha_g_ptr, bool nos_knockout,
+ int nos_shape_offset, int nos_tag_offset,
+ uint16_t *mask_row_ptr, int has_mask, pdf14_buf *maskbuf, uint16_t mask_bg_alpha, const uint16_t *mask_tr_fn,
+ uint16_t *backdrop_ptr,
+ bool has_matte, int n_chan, bool additive, int num_spots, bool overprint, gx_color_index drawn_comps, int x0, int y0, int x1, int y1,
+ const pdf14_nonseparable_blending_procs_t *pblend_procs, pdf14_device *pdev)
+{
+ int width = x1 - x0;
+ int x, y;
+ int i;
+
+ for (y = y1 - y0; y > 0; --y) {
+ uint16_t *gs_restrict mask_curr_ptr = mask_row_ptr;
+ for (x = 0; x < width; x++) {
+ unsigned int mask = interp16(mask_tr_fn, *mask_curr_ptr++);
+ uint16_t src_alpha = tos_ptr[n_chan * tos_planestride];
+ if (src_alpha != 0) {
+ uint16_t a_b;
+ unsigned int pix_alpha;
+
+ mask += mask>>15;
+ pix_alpha = (alpha * mask + 0x8000)>>16;
+
+ if (pix_alpha != 0xffff) {
+ pix_alpha += pix_alpha>>15;
+ src_alpha = (src_alpha * pix_alpha + 0x8000)>>16;
+ }
+
+ a_b = nos_ptr[n_chan * nos_planestride];
+ if (a_b == 0) {
+ /* Simple copy of colors plus alpha. */
+ for (i = 0; i < n_chan; i++) {
+ nos_ptr[i * nos_planestride] = tos_ptr[i * tos_planestride];
+ }
+ nos_ptr[i * nos_planestride] = src_alpha;
+ } else {
+ unsigned int a_r;
+ int src_scale;
+ unsigned int tmp;
+
+ /* Result alpha is Union of backdrop and source alpha */
+ tmp = (0xffff - a_b) * (0xffff - src_alpha) + 0x8000;
+ tmp += tmp>>16;
+ a_r = 0xffff - (tmp >> 16);
+
+ /* Compute src_alpha / a_r in 16.16 format */
+ src_scale = ((src_alpha << 16) + (a_r >> 1)) / a_r;
+
+ nos_ptr[n_chan * nos_planestride] = a_r;
+
+ src_scale >>= 1; /* Will overflow unless we lose a bit */
+ /* Do simple compositing of source over backdrop */
+ for (i = 0; i < n_chan; i++) {
+ int c_s = tos_ptr[i * tos_planestride];
+ int c_b = nos_ptr[i * nos_planestride];
+ nos_ptr[i * nos_planestride] = c_b + ((src_scale * (c_s - c_b) + 0x4000) >> 15);
+ }
+ }
+ }
+ ++tos_ptr;
+ ++nos_ptr;
+ }
+ tos_ptr += tos_rowstride - width;
+ nos_ptr += nos_rowstride - width;
+ mask_row_ptr += maskbuf->rowstride>>1;
+ }
+}
+
+static void
+compose_group16_nonknockout_nonblend_isolated_mask_common(uint16_t *tos_ptr, bool tos_isolated, int tos_planestride, int tos_rowstride, uint16_t alpha, uint16_t shape,
+ gs_blend_mode_t blend_mode, bool tos_has_shape, int tos_shape_offset, int tos_alpha_g_offset, int tos_tag_offset, bool tos_has_tag,
+ uint16_t *nos_ptr, bool nos_isolated, int nos_planestride, int nos_rowstride, uint16_t *nos_alpha_g_ptr, bool nos_knockout,
+ int nos_shape_offset, int nos_tag_offset,
+ uint16_t *mask_row_ptr, int has_mask, pdf14_buf *maskbuf, uint16_t mask_bg_alpha, const uint16_t *mask_tr_fn,
+ uint16_t *backdrop_ptr,
+ bool has_matte, int n_chan, bool additive, int num_spots, bool overprint, gx_color_index drawn_comps, int x0, int y0, int x1, int y1,
+ const pdf14_nonseparable_blending_procs_t *pblend_procs, pdf14_device *pdev)
+{
+ uint16_t *gs_restrict mask_curr_ptr = NULL;
+ int width = x1 - x0;
+ int x, y;
+ int i;
+ bool in_mask_rect_y;
+ bool in_mask_rect;
+ uint16_t pix_alpha, src_alpha;
+
+ for (y = y1 - y0; y > 0; --y) {
+ mask_curr_ptr = mask_row_ptr;
+ in_mask_rect_y = (has_mask && y1 - y >= maskbuf->rect.p.y && y1 - y < maskbuf->rect.q.y);
+ for (x = 0; x < width; x++) {
+ in_mask_rect = (in_mask_rect_y && has_mask && x0 + x >= maskbuf->rect.p.x && x0 + x < maskbuf->rect.q.x);
+ pix_alpha = alpha;
+ /* If we have a soft mask, then we have some special handling of the
+ group alpha value */
+ if (maskbuf != NULL) {
+ if (!in_mask_rect) {
+ /* Special case where we have a soft mask but are outside
+ the range of the soft mask and must use the background
+ alpha value */
+ pix_alpha = mask_bg_alpha;
+ }
+ }
+
+ if (mask_curr_ptr != NULL) {
+ if (in_mask_rect) {
+ unsigned int mask = interp16(mask_tr_fn, *mask_curr_ptr++);
+ mask += mask>>15;
+ pix_alpha = (pix_alpha * mask + 0x8000)>>16;
+ } else {
+ mask_curr_ptr++;
+ }
+ }
+
+ src_alpha = tos_ptr[n_chan * tos_planestride];
+ if (src_alpha != 0) {
+ uint16_t a_b;
+
+ if (pix_alpha != 65535) {
+ pix_alpha += pix_alpha>>15;
+ src_alpha = (src_alpha * pix_alpha + 0x8000)>>16;
+ }
+
+ a_b = nos_ptr[n_chan * nos_planestride];
+ if (a_b == 0) {
+ /* Simple copy of colors plus alpha. */
+ for (i = 0; i < n_chan; i++) {
+ nos_ptr[i * nos_planestride] = tos_ptr[i * tos_planestride];
+ }
+ nos_ptr[i * nos_planestride] = src_alpha;
+ } else {
+ unsigned int a_r;
+ int src_scale;
+ unsigned int tmp;
+
+ /* Result alpha is Union of backdrop and source alpha */
+ tmp = (0xffff - a_b) * (0xffff - src_alpha) + 0x8000;
+ tmp += tmp>>16;
+ a_r = 0xffff - (tmp >> 16);
+
+ /* Compute src_alpha / a_r in 16.16 format */
+ src_scale = ((src_alpha << 16) + (a_r >> 1)) / a_r;
+
+ nos_ptr[n_chan * nos_planestride] = a_r;
+
+ src_scale >>= 1; /* Need to lose a bit to avoid overflow */
+ /* Do simple compositing of source over backdrop */
+ for (i = 0; i < n_chan; i++) {
+ int c_s = tos_ptr[i * tos_planestride];
+ int c_b = nos_ptr[i * nos_planestride];
+ nos_ptr[i * nos_planestride] = c_b + ((src_scale * (c_s - c_b) + 0x4000) >> 15);
+ }
+ }
+ }
+ ++tos_ptr;
+ ++nos_ptr;
+ }
+ tos_ptr += tos_rowstride - width;
+ nos_ptr += nos_rowstride - width;
+ if (mask_row_ptr != NULL)
+ mask_row_ptr += maskbuf->rowstride>>1;
+ }
+}
+
+static void
+compose_group16_nonknockout_nonblend_isolated_nomask_common(uint16_t *tos_ptr, bool tos_isolated, int tos_planestride, int tos_rowstride, uint16_t alpha,
+ uint16_t shape, gs_blend_mode_t blend_mode, bool tos_has_shape, int tos_shape_offset, int tos_alpha_g_offset, int tos_tag_offset, bool tos_has_tag,
+ uint16_t *nos_ptr, bool nos_isolated, int nos_planestride, int nos_rowstride, uint16_t *nos_alpha_g_ptr, bool nos_knockout,
+ int nos_shape_offset, int nos_tag_offset,
+ uint16_t *mask_row_ptr, int has_mask, pdf14_buf *maskbuf, uint16_t mask_bg_alpha, const uint16_t *mask_tr_fn,
+ uint16_t *backdrop_ptr,
+ bool has_matte, int n_chan, bool additive, int num_spots, bool overprint, gx_color_index drawn_comps, int x0, int y0, int x1, int y1,
+ const pdf14_nonseparable_blending_procs_t *pblend_procs, pdf14_device *pdev)
+{
+ template_compose_group16(tos_ptr, /*tos_isolated*/1, tos_planestride, tos_rowstride, alpha, shape, BLEND_MODE_Normal, /*tos_has_shape*/0,
+ tos_shape_offset, tos_alpha_g_offset, tos_tag_offset, /*tos_has_tag*/0,
+ nos_ptr, /*nos_isolated*/0, nos_planestride, nos_rowstride, /*nos_alpha_g_ptr*/0, /* nos_knockout = */0,
+ /*nos_shape_offset*/0, /*nos_tag_offset*/0, mask_row_ptr, /*has_mask*/0, /*maskbuf*/NULL, mask_bg_alpha, mask_tr_fn,
+ backdrop_ptr, /*has_matte*/0, n_chan, /*additive*/1, /*num_spots*/0, /*overprint*/0, /*drawn_comps*/0, x0, y0, x1, y1, pblend_procs, pdev, 1, 0);
+}
+
+static void
+compose_group16_nonknockout_nonblend_nonisolated_mask_common(uint16_t *tos_ptr, bool tos_isolated, int tos_planestride, int tos_rowstride, uint16_t alpha,
+ uint16_t shape, gs_blend_mode_t blend_mode, bool tos_has_shape, int tos_shape_offset, int tos_alpha_g_offset, int tos_tag_offset, bool tos_has_tag,
+ uint16_t *nos_ptr, bool nos_isolated, int nos_planestride, int nos_rowstride, uint16_t *nos_alpha_g_ptr, bool nos_knockout,
+ int nos_shape_offset, int nos_tag_offset,
+ uint16_t *mask_row_ptr, int has_mask, pdf14_buf *maskbuf, uint16_t mask_bg_alpha, const uint16_t *mask_tr_fn,
+ uint16_t *backdrop_ptr,
+ bool has_matte, int n_chan, bool additive, int num_spots, bool overprint, gx_color_index drawn_comps, int x0, int y0, int x1, int y1,
+ const pdf14_nonseparable_blending_procs_t *pblend_procs, pdf14_device *pdev)
+{
+ template_compose_group16(tos_ptr, /*tos_isolated*/0, tos_planestride, tos_rowstride, alpha, shape, BLEND_MODE_Normal, /*tos_has_shape*/0,
+ tos_shape_offset, tos_alpha_g_offset, tos_tag_offset, /*tos_has_tag*/0,
+ nos_ptr, /*nos_isolated*/0, nos_planestride, nos_rowstride, /*nos_alpha_g_ptr*/0, /* nos_knockout = */0,
+ /*nos_shape_offset*/0, /*nos_tag_offset*/0, mask_row_ptr, has_mask, maskbuf, mask_bg_alpha, mask_tr_fn,
+ backdrop_ptr, /*has_matte*/0, n_chan, /*additive*/1, /*num_spots*/0, /*overprint*/0, /*drawn_comps*/0, x0, y0, x1, y1, pblend_procs, pdev, 1, 0);
+}
+
+static void
+compose_group16_nonknockout_nonblend_nonisolated_nomask_common(uint16_t *tos_ptr, bool tos_isolated, int tos_planestride, int tos_rowstride, uint16_t alpha,
+ uint16_t shape, gs_blend_mode_t blend_mode, bool tos_has_shape, int tos_shape_offset, int tos_alpha_g_offset, int tos_tag_offset, bool tos_has_tag,
+ uint16_t *nos_ptr, bool nos_isolated, int nos_planestride, int nos_rowstride, uint16_t *nos_alpha_g_ptr, bool nos_knockout,
+ int nos_shape_offset, int nos_tag_offset,
+ uint16_t *mask_row_ptr, int has_mask, pdf14_buf *maskbuf, uint16_t mask_bg_alpha, const uint16_t *mask_tr_fn,
+ uint16_t *backdrop_ptr,
+ bool has_matte, int n_chan, bool additive, int num_spots, bool overprint, gx_color_index drawn_comps, int x0, int y0, int x1, int y1,
+ const pdf14_nonseparable_blending_procs_t *pblend_procs, pdf14_device *pdev)
+{
+ template_compose_group16(tos_ptr, /*tos_isolated*/0, tos_planestride, tos_rowstride, alpha, shape, BLEND_MODE_Normal, /*tos_has_shape*/0,
+ tos_shape_offset, tos_alpha_g_offset, tos_tag_offset, /*tos_has_tag*/0,
+ nos_ptr, /*nos_isolated*/0, nos_planestride, nos_rowstride, /*nos_alpha_g_ptr*/0, /* nos_knockout = */0,
+ /*nos_shape_offset*/0, /*nos_tag_offset*/0, mask_row_ptr, /*has_mask*/0, /*maskbuf*/NULL, mask_bg_alpha, mask_tr_fn,
+ backdrop_ptr, /*has_matte*/0, n_chan, /*additive*/1, /*num_spots*/0, /*overprint*/0, /*drawn_comps*/0, x0, y0, x1, y1, pblend_procs, pdev, 1, 0);
+}
+
+static void
+compose_group16_nonknockout_noblend_general(uint16_t *tos_ptr, bool tos_isolated, int tos_planestride, int tos_rowstride, uint16_t alpha, uint16_t shape,
+ gs_blend_mode_t blend_mode, bool tos_has_shape, int tos_shape_offset, int tos_alpha_g_offset, int tos_tag_offset, bool tos_has_tag,
+ uint16_t *nos_ptr, bool nos_isolated, int nos_planestride, int nos_rowstride, uint16_t *nos_alpha_g_ptr, bool nos_knockout,
+ int nos_shape_offset, int nos_tag_offset,
+ uint16_t *mask_row_ptr, int has_mask, pdf14_buf *maskbuf, uint16_t mask_bg_alpha, const uint16_t *mask_tr_fn,
+ uint16_t *backdrop_ptr,
+ bool has_matte, int n_chan, bool additive, int num_spots, bool overprint, gx_color_index drawn_comps, int x0, int y0, int x1, int y1,
+ const pdf14_nonseparable_blending_procs_t *pblend_procs, pdf14_device *pdev)
+{
+ template_compose_group16(tos_ptr, tos_isolated, tos_planestride, tos_rowstride, alpha, shape, BLEND_MODE_Normal, tos_has_shape,
+ tos_shape_offset, tos_alpha_g_offset, tos_tag_offset, tos_has_tag,
+ nos_ptr, nos_isolated, nos_planestride, nos_rowstride, nos_alpha_g_ptr, /* nos_knockout = */0,
+ nos_shape_offset, nos_tag_offset, mask_row_ptr, has_mask, maskbuf, mask_bg_alpha, mask_tr_fn,
+ backdrop_ptr, has_matte, n_chan, additive, num_spots, overprint, drawn_comps, x0, y0, x1, y1, pblend_procs, pdev, 1, 0);
+}
+
+static void
+compose_group16_alphaless_knockout(uint16_t *tos_ptr, bool tos_isolated, int tos_planestride, int tos_rowstride, uint16_t alpha, uint16_t shape,
+ gs_blend_mode_t blend_mode, bool tos_has_shape, int tos_shape_offset, int tos_alpha_g_offset, int tos_tag_offset, bool tos_has_tag,
+ uint16_t *nos_ptr, bool nos_isolated, int nos_planestride, int nos_rowstride, uint16_t *nos_alpha_g_ptr, bool nos_knockout,
+ int nos_shape_offset, int nos_tag_offset,
+ uint16_t *mask_row_ptr, int has_mask, pdf14_buf *maskbuf, uint16_t mask_bg_alpha, const uint16_t *mask_tr_fn,
+ uint16_t *backdrop_ptr,
+ bool has_matte, int n_chan, bool additive, int num_spots, bool overprint, gx_color_index drawn_comps, int x0, int y0, int x1, int y1,
+ const pdf14_nonseparable_blending_procs_t *pblend_procs, pdf14_device *pdev)
+{
+ template_compose_group16(tos_ptr, tos_isolated, tos_planestride, tos_rowstride, alpha, shape, blend_mode, tos_has_shape,
+ tos_shape_offset, tos_alpha_g_offset, tos_tag_offset, tos_has_tag,
+ nos_ptr, nos_isolated, nos_planestride, nos_rowstride, nos_alpha_g_ptr, /* nos_knockout = */1,
+ nos_shape_offset, nos_tag_offset, /* mask_row_ptr */ NULL, /* has_mask */ 0, /* maskbuf */ NULL, mask_bg_alpha, /* mask_tr_fn */ NULL,
+ backdrop_ptr, /* has_matte */ false , n_chan, additive, num_spots, overprint, drawn_comps, x0, y0, x1, y1, pblend_procs, pdev, 0, 1);
+}
+
+static void
+compose_group16_alphaless_nonknockout(uint16_t *tos_ptr, bool tos_isolated, int tos_planestride, int tos_rowstride, uint16_t alpha, uint16_t shape,
+ gs_blend_mode_t blend_mode, bool tos_has_shape, int tos_shape_offset, int tos_alpha_g_offset, int tos_tag_offset, bool tos_has_tag,
+ uint16_t *nos_ptr, bool nos_isolated, int nos_planestride, int nos_rowstride, uint16_t *nos_alpha_g_ptr, bool nos_knockout,
+ int nos_shape_offset, int nos_tag_offset,
+ uint16_t *mask_row_ptr, int has_mask, pdf14_buf *maskbuf, uint16_t mask_bg_alpha, const uint16_t *mask_tr_fn,
+ uint16_t *backdrop_ptr,
+ bool has_matte, int n_chan, bool additive, int num_spots, bool overprint, gx_color_index drawn_comps, int x0, int y0, int x1, int y1,
+ const pdf14_nonseparable_blending_procs_t *pblend_procs, pdf14_device *pdev)
+{
+ template_compose_group16(tos_ptr, tos_isolated, tos_planestride, tos_rowstride, alpha, shape, blend_mode, tos_has_shape,
+ tos_shape_offset, tos_alpha_g_offset, tos_tag_offset, tos_has_tag,
+ nos_ptr, nos_isolated, nos_planestride, nos_rowstride, nos_alpha_g_ptr, /* nos_knockout = */0,
+ nos_shape_offset, nos_tag_offset, /* mask_row_ptr */ NULL, /* has_mask */ 0, /* maskbuf */ NULL, mask_bg_alpha, /* mask_tr_fn */ NULL,
+ backdrop_ptr, /* has_matte */ false , n_chan, additive, num_spots, overprint, drawn_comps, x0, y0, x1, y1, pblend_procs, pdev, 0, 1);
+}
+
+static void
+do_compose_group16(pdf14_buf *tos, pdf14_buf *nos, pdf14_buf *maskbuf,
+ int x0, int x1, int y0, int y1, int n_chan, bool additive,
+ const pdf14_nonseparable_blending_procs_t * pblend_procs,
+ bool has_matte, bool overprint, gx_color_index drawn_comps,
+ gs_memory_t *memory, gx_device *dev)
+{
+ int num_spots = tos->num_spots;
+ uint16_t alpha = tos->alpha;
+ uint16_t shape = tos->shape;
+ gs_blend_mode_t blend_mode = tos->blend_mode;
+ uint16_t *tos_ptr =
+ (uint16_t *)(void *)(tos->data + (x0 - tos->rect.p.x)*2 +
+ (y0 - tos->rect.p.y) * tos->rowstride);
+ uint16_t *nos_ptr =
+ (uint16_t *)(void *)(nos->data + (x0 - nos->rect.p.x)*2 +
+ (y0 - nos->rect.p.y) * nos->rowstride);
+ uint16_t *mask_row_ptr = NULL;
+ int tos_planestride = tos->planestride;
+ int nos_planestride = nos->planestride;
+ uint16_t mask_bg_alpha = 0; /* Quiet compiler. */
+ bool tos_isolated = tos->isolated;
+ bool nos_isolated = nos->isolated;
+ bool nos_knockout = nos->knockout;
+ uint16_t *nos_alpha_g_ptr;
+ int tos_shape_offset = n_chan * tos_planestride;
+ int tos_alpha_g_offset = tos_shape_offset + (tos->has_shape ? tos_planestride : 0);
+ bool tos_has_tag = tos->has_tags;
+ int tos_tag_offset = tos_planestride * (tos->n_planes - 1);
+ int nos_shape_offset = n_chan * nos_planestride;
+ int nos_alpha_g_offset = nos_shape_offset + (nos->has_shape ? nos_planestride : 0);
+ int nos_tag_offset = nos_planestride * (nos->n_planes - 1);
+ const uint16_t *mask_tr_fn = NULL; /* Quiet compiler. */
+ bool has_mask = false;
+ uint16_t *backdrop_ptr = NULL;
+ pdf14_device *pdev = (pdf14_device *)dev;
+#if RAW_DUMP
+ uint16_t *composed_ptr = NULL;
+ int width = x1 - x0;
+#endif
+ art_pdf_compose_group16_fn fn;
+
+ if ((tos->n_chan == 0) || (nos->n_chan == 0))
+ return;
+ rect_merge(nos->dirty, tos->dirty);
+ if (nos->has_tags)
+ if_debug7m('v', memory,
+ "pdf14_pop_transparency_group y0 = %d, y1 = %d, w = %d, alpha = %d, shape = %d, tag = %d, bm = %d\n",
+ y0, y1, x1 - x0, alpha, shape, dev->graphics_type_tag & ~GS_DEVICE_ENCODES_TAGS, blend_mode);
+ else
+ if_debug6m('v', memory,
+ "pdf14_pop_transparency_group y0 = %d, y1 = %d, w = %d, alpha = %d, shape = %d, bm = %d\n",
+ y0, y1, x1 - x0, alpha, shape, blend_mode);
+ if (!nos->has_shape)
+ nos_shape_offset = 0;
+ if (!nos->has_tags)
+ nos_tag_offset = 0;
+ if (nos->has_alpha_g) {
+ nos_alpha_g_ptr = nos_ptr + (nos_alpha_g_offset>>1);
+ } else
+ nos_alpha_g_ptr = NULL;
+ if (nos->backdrop != NULL) {
+ backdrop_ptr =
+ (uint16_t *)(void *)(nos->backdrop + (x0 - nos->rect.p.x)*2 +
+ (y0 - nos->rect.p.y) * nos->rowstride);
+ }
+ if (blend_mode != BLEND_MODE_Compatible && blend_mode != BLEND_MODE_Normal)
+ overprint = false;
+
+ if (maskbuf != NULL) {
+ unsigned int tmp;
+ mask_tr_fn = (uint16_t *)maskbuf->transfer_fn;
+ /* Make sure we are in the mask buffer */
+ if (maskbuf->data != NULL) {
+ mask_row_ptr =
+ (uint16_t *)(void *)(maskbuf->data + (x0 - maskbuf->rect.p.x)*2 +
+ (y0 - maskbuf->rect.p.y) * maskbuf->rowstride);
+ has_mask = true;
+ }
+ /* We may have a case, where we are outside the maskbuf rect. */
+ /* We would have avoided creating the maskbuf->data */
+ /* In that case, we should use the background alpha value */
+ /* See discussion on the BC entry in the PDF spec. */
+ mask_bg_alpha = maskbuf->alpha;
+ /* Adjust alpha by the mask background alpha. This is only used
+ if we are outside the soft mask rect during the filling operation */
+ mask_bg_alpha = interp16(mask_tr_fn, mask_bg_alpha);
+ tmp = alpha * mask_bg_alpha + 0x8000;
+ mask_bg_alpha = (tmp + (tmp >> 8)) >> 8;
+ }
+ n_chan--; /* Now the true number of colorants (i.e. not including alpha)*/
+#if RAW_DUMP
+ composed_ptr = nos_ptr;
+ dump_raw_buffer(memory, y1-y0, width, tos->n_planes, tos_planestride, tos->rowstride,
+ "bImageTOS", (byte *)tos_ptr, tos->deep);
+ dump_raw_buffer(memory, y1-y0, width, nos->n_planes, nos_planestride, nos->rowstride,
+ "cImageNOS", (byte *)nos_ptr, tos->deep);
+ if (maskbuf !=NULL && maskbuf->data != NULL) {
+ dump_raw_buffer(memory, maskbuf->rect.q.y - maskbuf->rect.p.y,
+ maskbuf->rect.q.x - maskbuf->rect.p.x, maskbuf->n_planes,
+ maskbuf->planestride, maskbuf->rowstride, "dMask",
+ maskbuf->data, maskbuf->deep);
+ }
+#endif
+
+ /* You might hope that has_mask iff maskbuf != NULL, but this is
+ * not the case. Certainly we can see cases where maskbuf != NULL
+ * and has_mask = 0. What's more, treating such cases as being
+ * has_mask = 0 causes diffs. */
+#ifdef TRACK_COMPOSE_GROUPS
+ {
+ int code = 0;
+
+ code += !!nos_knockout;
+ code += (!!nos_isolated)<<1;
+ code += (!!tos_isolated)<<2;
+ code += (!!tos->has_shape)<<3;
+ code += (!!tos_has_tag)<<4;
+ code += (!!additive)<<5;
+ code += (!!overprint)<<6;
+ code += (!!has_mask || maskbuf != NULL)<<7;
+ code += (!!has_matte)<<8;
+ code += (backdrop_ptr != NULL)<<9;
+ code += (num_spots != 0)<<10;
+ code += blend_mode<<11;
+
+ if (track_compose_groups == 0)
+ {
+ atexit(dump_track_compose_groups);
+ track_compose_groups = 1;
+ }
+ compose_groups[code]++;
+ }
+#endif
+
+ /* We have tested the files on the cluster to see what percentage of
+ * files/devices hit the different options. */
+ if (nos_knockout)
+ fn = &compose_group16_knockout; /* Small %ages, nothing more than 1.1% */
+ else if (blend_mode != 0)
+ fn = &compose_group16_nonknockout_blend; /* Small %ages, nothing more than 2% */
+ else if (tos->has_shape == 0 && tos_has_tag == 0 && nos_isolated == 0 && nos_alpha_g_ptr == NULL &&
+ nos_shape_offset == 0 && nos_tag_offset == 0 && backdrop_ptr == NULL && has_matte == 0 && num_spots == 0 &&
+ overprint == 0) {
+ /* Additive vs Subtractive makes no difference in normal blend mode with no spots */
+ if (tos_isolated) {
+ if (has_mask || maskbuf) {/* 7% */
+ /* AirPrint test case hits this */
+ if (maskbuf && maskbuf->rect.p.x <= x0 && maskbuf->rect.p.y <= y0 &&
+ maskbuf->rect.q.x >= x1 && maskbuf->rect.q.y >= y1)
+ fn = &compose_group16_nonknockout_nonblend_isolated_allmask_common;
+ else
+ fn = &compose_group16_nonknockout_nonblend_isolated_mask_common;
+ } else /* 14% */
+ fn = &compose_group16_nonknockout_nonblend_isolated_nomask_common;
+ } else {
+ if (has_mask || maskbuf) /* 4% */
+ fn = &compose_group16_nonknockout_nonblend_nonisolated_mask_common;
+ else /* 15% */
+ fn = &compose_group16_nonknockout_nonblend_nonisolated_nomask_common;
+ }
+ } else
+ fn = compose_group16_nonknockout_noblend_general;
+
+ tos_planestride >>= 1;
+ tos_shape_offset >>= 1;
+ tos_alpha_g_offset >>= 1;
+ tos_tag_offset >>= 1;
+ nos_planestride >>= 1;
+ nos_shape_offset >>= 1;
+ nos_tag_offset >>= 1;
+ fn(tos_ptr, tos_isolated, tos_planestride, tos->rowstride>>1, alpha, shape, blend_mode, tos->has_shape,
+ tos_shape_offset, tos_alpha_g_offset, tos_tag_offset, tos_has_tag,
+ nos_ptr, nos_isolated, nos_planestride, nos->rowstride>>1, nos_alpha_g_ptr, nos_knockout,
+ nos_shape_offset, nos_tag_offset,
+ mask_row_ptr, has_mask, maskbuf, mask_bg_alpha, mask_tr_fn,
+ backdrop_ptr,
+ has_matte, n_chan, additive, num_spots, overprint, drawn_comps, x0, y0, x1, y1,
+ pblend_procs, pdev);
+
+#if RAW_DUMP
+ dump_raw_buffer(memory, y1-y0, width, nos->n_planes, nos_planestride<<1, nos->rowstride,
+ "eComposed", (byte *)composed_ptr, nos->deep);
+ global_index++;
+#endif
+}
+
+void
+pdf14_compose_group(pdf14_buf *tos, pdf14_buf *nos, pdf14_buf *maskbuf,
+ int x0, int x1, int y0, int y1, int n_chan, bool additive,
+ const pdf14_nonseparable_blending_procs_t * pblend_procs,
+ bool has_matte, bool overprint, gx_color_index drawn_comps,
+ gs_memory_t *memory, gx_device *dev)
+{
+ if (tos->deep)
+ do_compose_group16(tos, nos, maskbuf, x0, x1, y0, y1, n_chan,
+ additive, pblend_procs, has_matte, overprint,
+ drawn_comps, memory, dev);
+ else
+ do_compose_group(tos, nos, maskbuf, x0, x1, y0, y1, n_chan,
+ additive, pblend_procs, has_matte, overprint,
+ drawn_comps, memory, dev);
+}
+
+static void
+do_compose_alphaless_group(pdf14_buf *tos, pdf14_buf *nos,
+ int x0, int x1, int y0, int y1,
+ gs_memory_t *memory, gx_device *dev)
+{
+ pdf14_device *pdev = (pdf14_device *)dev;
+ bool overprint = pdev->overprint;
+ bool additive = pdev->ctx->additive;
+ gx_color_index drawn_comps = pdev->drawn_comps;
+ int n_chan = nos->n_chan;
+ int num_spots = tos->num_spots;
+ byte alpha = tos->alpha>>8;
+ byte shape = tos->shape>>8;
+ gs_blend_mode_t blend_mode = tos->blend_mode;
+ byte *tos_ptr = tos->data + x0 - tos->rect.p.x +
+ (y0 - tos->rect.p.y) * tos->rowstride;
+ byte *nos_ptr = nos->data + x0 - nos->rect.p.x +
+ (y0 - nos->rect.p.y) * nos->rowstride;
+ byte *mask_row_ptr = NULL;
+ int tos_planestride = tos->planestride;
+ int nos_planestride = nos->planestride;
+ byte mask_bg_alpha = 0; /* Quiet compiler. */
+ bool tos_isolated = false;
+ bool nos_isolated = nos->isolated;
+ bool nos_knockout = nos->knockout;
+ byte *nos_alpha_g_ptr;
+ int tos_shape_offset = n_chan * tos_planestride;
+ int tos_alpha_g_offset = tos_shape_offset + (tos->has_shape ? tos_planestride : 0);
+ bool tos_has_tag = tos->has_tags;
+ int tos_tag_offset = tos_planestride * (tos->n_planes - 1);
+ int nos_shape_offset = n_chan * nos_planestride;
+ int nos_alpha_g_offset = nos_shape_offset + (nos->has_shape ? nos_planestride : 0);
+ int nos_tag_offset = nos_planestride * (nos->n_planes - 1);
+ const byte *mask_tr_fn = NULL; /* Quiet compiler. */
+ bool has_mask = false;
+ byte *backdrop_ptr = NULL;
+#if RAW_DUMP
+ byte *composed_ptr = NULL;
+ int width = x1 - x0;
+#endif
+ art_pdf_compose_group_fn fn;
+
+ if ((tos->n_chan == 0) || (nos->n_chan == 0))
+ return;
+ rect_merge(nos->dirty, tos->dirty);
+ if (nos->has_tags)
+ if_debug7m('v', memory,
+ "pdf14_pop_transparency_group y0 = %d, y1 = %d, w = %d, alpha = %d, shape = %d, tag = %d, bm = %d\n",
+ y0, y1, x1 - x0, alpha, shape, dev->graphics_type_tag & ~GS_DEVICE_ENCODES_TAGS, blend_mode);
+ else
+ if_debug6m('v', memory,
+ "pdf14_pop_transparency_group y0 = %d, y1 = %d, w = %d, alpha = %d, shape = %d, bm = %d\n",
+ y0, y1, x1 - x0, alpha, shape, blend_mode);
+ if (!nos->has_shape)
+ nos_shape_offset = 0;
+ if (!nos->has_tags)
+ nos_tag_offset = 0;
+ if (nos->has_alpha_g) {
+ nos_alpha_g_ptr = nos_ptr + nos_alpha_g_offset;
+ } else
+ nos_alpha_g_ptr = NULL;
+ if (nos->backdrop != NULL) {
+ backdrop_ptr = nos->backdrop + x0 - nos->rect.p.x +
+ (y0 - nos->rect.p.y) * nos->rowstride;
+ }
+ if (blend_mode != BLEND_MODE_Compatible && blend_mode != BLEND_MODE_Normal)
+ overprint = false;
+
+ n_chan--; /* Now the true number of colorants (i.e. not including alpha)*/
+#if RAW_DUMP
+ composed_ptr = nos_ptr;
+ dump_raw_buffer(memory, y1-y0, width, tos->n_planes, tos_planestride, tos->rowstride,
+ "bImageTOS", tos_ptr, tos->deep);
+ dump_raw_buffer(memory, y1-y0, width, nos->n_planes, nos_planestride, nos->rowstride,
+ "cImageNOS", nos_ptr, nos->deep);
+ /* maskbuf is NULL in here */
+#endif
+
+ /* You might hope that has_mask iff maskbuf != NULL, but this is
+ * not the case. Certainly we can see cases where maskbuf != NULL
+ * and has_mask = 0. What's more, treating such cases as being
+ * has_mask = 0 causes diffs. */
+#ifdef TRACK_COMPOSE_GROUPS
+ {
+ int code = 0;
+
+ code += !!nos_knockout;
+ code += (!!nos_isolated)<<1;
+ code += (!!tos_isolated)<<2;
+ code += (!!tos->has_shape)<<3;
+ code += (!!tos_has_tag)<<4;
+ code += (!!additive)<<5;
+ code += (!!overprint)<<6;
+ code += (!!has_mask)<<7;
+ code += (backdrop_ptr != NULL)<<9;
+ code += (num_spots != 0)<<10;
+ code += blend_mode<<11;
+
+ if (track_compose_groups == 0)
+ {
+ atexit(dump_track_compose_groups);
+ track_compose_groups = 1;
+ }
+ compose_groups[code]++;
+ }
+#endif
+
+ /* We have tested the files on the cluster to see what percentage of
+ * files/devices hit the different options. */
+ if (nos_knockout)
+ fn = &compose_group_alphaless_knockout;
+ else
+ fn = &compose_group_alphaless_nonknockout;
+
+ fn(tos_ptr, tos_isolated, tos_planestride, tos->rowstride, alpha, shape, blend_mode, tos->has_shape,
+ tos_shape_offset, tos_alpha_g_offset, tos_tag_offset, tos_has_tag,
+ nos_ptr, nos_isolated, nos_planestride, nos->rowstride, nos_alpha_g_ptr, nos_knockout,
+ nos_shape_offset, nos_tag_offset,
+ mask_row_ptr, has_mask, /* maskbuf */ NULL, mask_bg_alpha, mask_tr_fn,
+ backdrop_ptr,
+ /* has_matte */ 0, n_chan, additive, num_spots, overprint, drawn_comps, x0, y0, x1, y1,
+ pdev->blend_procs, pdev);
+
+#if RAW_DUMP
+ dump_raw_buffer(memory, y1-y0, width, nos->n_planes, nos_planestride, nos->rowstride,
+ "eComposed", composed_ptr, nos->deep);
+ global_index++;
+#endif
+}
+
+static void
+do_compose_alphaless_group16(pdf14_buf *tos, pdf14_buf *nos,
+ int x0, int x1, int y0, int y1,
+ gs_memory_t *memory, gx_device *dev)
+{
+ pdf14_device *pdev = (pdf14_device *)dev;
+ bool overprint = pdev->overprint;
+ bool additive = pdev->ctx->additive;
+ gx_color_index drawn_comps = pdev->drawn_comps;
+ int n_chan = nos->n_chan;
+ int num_spots = tos->num_spots;
+ uint16_t alpha = tos->alpha;
+ uint16_t shape = tos->shape;
+ gs_blend_mode_t blend_mode = tos->blend_mode;
+ uint16_t *tos_ptr =
+ (uint16_t *)(void *)(tos->data + (x0 - tos->rect.p.x)*2 +
+ (y0 - tos->rect.p.y) * tos->rowstride);
+ uint16_t *nos_ptr =
+ (uint16_t *)(void *)(nos->data + (x0 - nos->rect.p.x)*2 +
+ (y0 - nos->rect.p.y) * nos->rowstride);
+ uint16_t *mask_row_ptr = NULL;
+ int tos_planestride = tos->planestride;
+ int nos_planestride = nos->planestride;
+ uint16_t mask_bg_alpha = 0; /* Quiet compiler. */
+ bool tos_isolated = false;
+ bool nos_isolated = nos->isolated;
+ bool nos_knockout = nos->knockout;
+ uint16_t *nos_alpha_g_ptr;
+ int tos_shape_offset = n_chan * tos_planestride;
+ int tos_alpha_g_offset = tos_shape_offset + (tos->has_shape ? tos_planestride : 0);
+ bool tos_has_tag = tos->has_tags;
+ int tos_tag_offset = tos_planestride * (tos->n_planes - 1);
+ int nos_shape_offset = n_chan * nos_planestride;
+ int nos_alpha_g_offset = nos_shape_offset + (nos->has_shape ? nos_planestride : 0);
+ int nos_tag_offset = nos_planestride * (nos->n_planes - 1);
+ bool has_mask = false;
+ uint16_t *backdrop_ptr = NULL;
+#if RAW_DUMP
+ uint16_t *composed_ptr = NULL;
+ int width = x1 - x0;
+#endif
+ art_pdf_compose_group16_fn fn;
+
+ if ((tos->n_chan == 0) || (nos->n_chan == 0))
+ return;
+ rect_merge(nos->dirty, tos->dirty);
+ if (nos->has_tags)
+ if_debug7m('v', memory,
+ "pdf14_pop_transparency_group y0 = %d, y1 = %d, w = %d, alpha = %d, shape = %d, tag = %d, bm = %d\n",
+ y0, y1, x1 - x0, alpha, shape, dev->graphics_type_tag & ~GS_DEVICE_ENCODES_TAGS, blend_mode);
+ else
+ if_debug6m('v', memory,
+ "pdf14_pop_transparency_group y0 = %d, y1 = %d, w = %d, alpha = %d, shape = %d, bm = %d\n",
+ y0, y1, x1 - x0, alpha, shape, blend_mode);
+ if (!nos->has_shape)
+ nos_shape_offset = 0;
+ if (!nos->has_tags)
+ nos_tag_offset = 0;
+ if (nos->has_alpha_g) {
+ nos_alpha_g_ptr = nos_ptr + (nos_alpha_g_offset>>1);
+ } else
+ nos_alpha_g_ptr = NULL;
+ if (nos->backdrop != NULL) {
+ backdrop_ptr =
+ (uint16_t *)(void *)(nos->backdrop + (x0 - nos->rect.p.x)*2 +
+ (y0 - nos->rect.p.y) * nos->rowstride);
+ }
+ if (blend_mode != BLEND_MODE_Compatible && blend_mode != BLEND_MODE_Normal)
+ overprint = false;
+
+ n_chan--; /* Now the true number of colorants (i.e. not including alpha)*/
+#if RAW_DUMP
+ composed_ptr = nos_ptr;
+ dump_raw_buffer_be(memory, y1-y0, width, tos->n_planes, tos_planestride, tos->rowstride,
+ "bImageTOS", (byte *)tos_ptr, tos->deep);
+ dump_raw_buffer(memory, y1-y0, width, nos->n_planes, nos_planestride, nos->rowstride,
+ "cImageNOS", (byte *)nos_ptr, nos->deep);
+ /* maskbuf is NULL in here */
+#endif
+
+ /* You might hope that has_mask iff maskbuf != NULL, but this is
+ * not the case. Certainly we can see cases where maskbuf != NULL
+ * and has_mask = 0. What's more, treating such cases as being
+ * has_mask = 0 causes diffs. */
+#ifdef TRACK_COMPOSE_GROUPS
+ {
+ int code = 0;
+
+ code += !!nos_knockout;
+ code += (!!nos_isolated)<<1;
+ code += (!!tos_isolated)<<2;
+ code += (!!tos->has_shape)<<3;
+ code += (!!tos_has_tag)<<4;
+ code += (!!additive)<<5;
+ code += (!!overprint)<<6;
+ code += (!!has_mask)<<7;
+ code += (backdrop_ptr != NULL)<<9;
+ code += (num_spots != 0)<<10;
+ code += blend_mode<<11;
+
+ if (track_compose_groups == 0)
+ {
+ atexit(dump_track_compose_groups);
+ track_compose_groups = 1;
+ }
+ compose_groups[code]++;
+ }
+#endif
+
+ /* We have tested the files on the cluster to see what percentage of
+ * files/devices hit the different options. */
+ if (nos_knockout)
+ fn = &compose_group16_alphaless_knockout;
+ else
+ fn = &compose_group16_alphaless_nonknockout;
+
+ fn(tos_ptr, tos_isolated, tos_planestride>>1, tos->rowstride>>1, alpha, shape, blend_mode, tos->has_shape,
+ tos_shape_offset>>1, tos_alpha_g_offset>>1, tos_tag_offset>>1, tos_has_tag,
+ nos_ptr, nos_isolated, nos_planestride>>1, nos->rowstride>>1, nos_alpha_g_ptr, nos_knockout,
+ nos_shape_offset>>1, nos_tag_offset>>1,
+ mask_row_ptr, has_mask, /* maskbuf */ NULL, mask_bg_alpha, NULL,
+ backdrop_ptr,
+ /* has_matte */ 0, n_chan, additive, num_spots, overprint, drawn_comps, x0, y0, x1, y1,
+ pdev->blend_procs, pdev);
+
+#if RAW_DUMP
+ dump_raw_buffer(memory, y1-y0, width, nos->n_planes, nos_planestride, nos->rowstride,
+ "eComposed", (byte *)composed_ptr, nos->deep);
+ global_index++;
+#endif
+}
+
+void
+pdf14_compose_alphaless_group(pdf14_buf *tos, pdf14_buf *nos,
+ int x0, int x1, int y0, int y1,
+ gs_memory_t *memory, gx_device *dev)
+{
+ if (tos->deep)
+ do_compose_alphaless_group16(tos, nos, x0, x1, y0, y1, memory, dev);
+ else
+ do_compose_alphaless_group(tos, nos, x0, x1, y0, y1, memory, dev);
+}
+
+typedef void (*pdf14_mark_fill_rect_fn)(int w, int h, byte *gs_restrict dst_ptr, byte *gs_restrict src, int num_comp, int num_spots, int first_blend_spot,
+ byte src_alpha, int rowstride, int planestride, bool additive, pdf14_device *pdev, gs_blend_mode_t blend_mode,
+ bool overprint, gx_color_index drawn_comps, int tag_off, gs_graphics_type_tag_t curr_tag,
+ int alpha_g_off, int shape_off, byte shape, bool isolated);
+
+static forceinline void
+template_mark_fill_rect(int w, int h, byte *gs_restrict dst_ptr, byte *gs_restrict src, int num_comp, int num_spots, int first_blend_spot,
+ byte src_alpha, int rowstride, int planestride, bool additive, pdf14_device *pdev, gs_blend_mode_t blend_mode,
+ bool overprint, gx_color_index drawn_comps, int tag_off, gs_graphics_type_tag_t curr_tag,
+ int alpha_g_off, int shape_off, byte shape, bool isolated)
+{
+ int i, j, k;
+ gx_color_index comps;
+ byte dst[PDF14_MAX_PLANES] = { 0 };
+ byte dest_alpha;
+ bool tag_blend = blend_mode == BLEND_MODE_Normal ||
+ blend_mode == BLEND_MODE_Compatible ||
+ blend_mode == BLEND_MODE_CompatibleOverprint;
+
+ for (j = h; j > 0; --j) {
+ for (i = w; i > 0; --i) {
+ if ((blend_mode == BLEND_MODE_Normal && src[num_comp] == 0xff && !overprint) || dst_ptr[num_comp * planestride] == 0) {
+ /* dest alpha is zero (or normal, and solid src) just use source. */
+ if (additive) {
+ /* Hybrid case */
+ for (k = 0; k < (num_comp - num_spots); k++) {
+ dst_ptr[k * planestride] = src[k];
+ }
+ for (k = 0; k < num_spots; k++) {
+ dst_ptr[(k + num_comp - num_spots) * planestride] =
+ 255 - src[k + num_comp - num_spots];
+ }
+ } else {
+ /* Pure subtractive */
+ for (k = 0; k < num_comp; k++) {
+ dst_ptr[k * planestride] = 255 - src[k];
+ }
+ }
+ /* alpha */
+ dst_ptr[num_comp * planestride] = src[num_comp];
+ } else if (src[num_comp] != 0) {
+ byte *pdst;
+ /* Complement subtractive planes */
+ if (!additive) {
+ /* Pure subtractive */
+ for (k = 0; k < num_comp; ++k)
+ dst[k] = 255 - dst_ptr[k * planestride];
+ } else {
+ /* Hybrid case, additive with subtractive spots */
+ for (k = 0; k < (num_comp - num_spots); k++) {
+ dst[k] = dst_ptr[k * planestride];
+ }
+ for (k = 0; k < num_spots; k++) {
+ dst[k + num_comp - num_spots] =
+ 255 - dst_ptr[(k + num_comp - num_spots) * planestride];
+ }
+ }
+ dst[num_comp] = dst_ptr[num_comp * planestride];
+ dest_alpha = dst[num_comp];
+ pdst = art_pdf_composite_pixel_alpha_8_inline(dst, src, num_comp, blend_mode, first_blend_spot,
+ pdev->blend_procs, pdev);
+ /* Until I see otherwise in AR or the spec, do not fool
+ with spot overprinting while we are in an RGB or Gray
+ blend color space. */
+ if (!additive && overprint) {
+ /* If this is an overprint case, and alpha_r is different
+ than alpha_d then we will need to adjust
+ the colors of the non-drawn components here too */
+ for (k = 0, comps = drawn_comps; comps != 0; ++k, comps >>= 1) {
+ if ((comps & 0x1) != 0) {
+ dst_ptr[k * planestride] = 255 - pdst[k];
+ } else if (dest_alpha != pdst[num_comp]) {
+ /* We need val_new = (val_old * old_alpha) / new_alpha */
+ if (pdst[num_comp] != 0) {
+ int val = (int)floor(((float)dest_alpha / (float)pdst[num_comp]) * (255 - pdst[k]) + 0.5);
+ if (val < 0)
+ val = 0;
+ else if (val > 255)
+ val = 255;
+ dst_ptr[k * planestride] = val;
+ }
+ }
+ }
+ } else {
+ /* Post blend complement for subtractive */
+ if (!additive) {
+ /* Pure subtractive */
+ for (k = 0; k < num_comp; ++k)
+ dst_ptr[k * planestride] = 255 - pdst[k];
+
+ } else {
+ /* Hybrid case, additive with subtractive spots */
+ for (k = 0; k < (num_comp - num_spots); k++) {
+ dst_ptr[k * planestride] = pdst[k];
+ }
+ for (k = 0; k < num_spots; k++) {
+ dst_ptr[(k + num_comp - num_spots) * planestride] =
+ 255 - pdst[k + num_comp - num_spots];
+ }
+ }
+ }
+ /* The alpha channel */
+ dst_ptr[num_comp * planestride] = pdst[num_comp];
+ }
+ if (tag_off) {
+ /* If src alpha is 100% then set to curr_tag, else or */
+ /* other than Normal BM, we always OR */
+ if (src[num_comp] == 255 && tag_blend) {
+ dst_ptr[tag_off] = curr_tag;
+ } else {
+ dst_ptr[tag_off] |= curr_tag;
+ }
+ }
+ if (alpha_g_off) {
+ int tmp = (255 - dst_ptr[alpha_g_off]) * src_alpha + 0x80;
+ dst_ptr[alpha_g_off] = 255 - ((tmp + (tmp >> 8)) >> 8);
+ }
+ if (shape_off) {
+ int tmp = (255 - dst_ptr[shape_off]) * shape + 0x80;
+ dst_ptr[shape_off] = 255 - ((tmp + (tmp >> 8)) >> 8);
+ }
+ ++dst_ptr;
+ }
+ dst_ptr += rowstride;
+ }
+}
+
+static void
+mark_fill_rect_alpha0(int w, int h, byte *gs_restrict dst_ptr, byte *gs_restrict src, int num_comp, int num_spots, int first_blend_spot,
+ byte src_alpha, int rowstride, int planestride, bool additive, pdf14_device *pdev, gs_blend_mode_t blend_mode,
+ bool overprint, gx_color_index drawn_comps, int tag_off, gs_graphics_type_tag_t curr_tag,
+ int alpha_g_off, int shape_off, byte shape, bool isolated)
+{
+ int i, j;
+
+ for (j = h; j > 0; --j) {
+ for (i = w; i > 0; --i) {
+ if (alpha_g_off) {
+ int tmp = (255 - dst_ptr[alpha_g_off]) * src_alpha + 0x80;
+ dst_ptr[alpha_g_off] = 255 - ((tmp + (tmp >> 8)) >> 8);
+ }
+ if (shape_off) {
+ int tmp = (255 - dst_ptr[shape_off]) * shape + 0x80;
+ dst_ptr[shape_off] = 255 - ((tmp + (tmp >> 8)) >> 8);
+ }
+ ++dst_ptr;
+ }
+ dst_ptr += rowstride;
+ }
+}
+
+static void
+mark_fill_rect(int w, int h, byte *gs_restrict dst_ptr, byte *gs_restrict src, int num_comp, int num_spots, int first_blend_spot,
+ byte src_alpha, int rowstride, int planestride, bool additive, pdf14_device *pdev, gs_blend_mode_t blend_mode,
+ bool overprint, gx_color_index drawn_comps, int tag_off, gs_graphics_type_tag_t curr_tag,
+ int alpha_g_off, int shape_off, byte shape, bool isolated)
+{
+ template_mark_fill_rect(w, h, dst_ptr, src, num_comp, num_spots, first_blend_spot,
+ src_alpha, rowstride, planestride, additive, pdev, blend_mode,
+ overprint, drawn_comps, tag_off, curr_tag,
+ alpha_g_off, shape_off, shape, isolated);
+}
+
+static void
+mark_fill_rect_sub4_fast(int w, int h, byte *gs_restrict dst_ptr, byte *gs_restrict src, int num_comp, int num_spots, int first_blend_spot,
+ byte src_alpha, int rowstride, int planestride, bool additive, pdf14_device *pdev, gs_blend_mode_t blend_mode,
+ bool overprint, gx_color_index drawn_comps, int tag_off, gs_graphics_type_tag_t curr_tag,
+ int alpha_g_off, int shape_off, byte shape, bool isolated)
+{
+ int i, j, k;
+
+ for (j = h; j > 0; --j) {
+ for (i = w; i > 0; --i) {
+ byte a_s = src[4];
+ byte a_b = dst_ptr[4 * planestride];
+ if ((a_s == 0xff) || a_b == 0) {
+ /* dest alpha is zero (or normal, and solid src) just use source. */
+ dst_ptr[0 * planestride] = 255 - src[0];
+ dst_ptr[1 * planestride] = 255 - src[1];
+ dst_ptr[2 * planestride] = 255 - src[2];
+ dst_ptr[3 * planestride] = 255 - src[3];
+ /* alpha */
+ dst_ptr[4 * planestride] = a_s;
+ } else if (a_s != 0) {
+ /* Result alpha is Union of backdrop and source alpha */
+ int tmp = (0xff - a_b) * (0xff - a_s) + 0x80;
+ unsigned int a_r = 0xff - (((tmp >> 8) + tmp) >> 8);
+
+ /* Compute a_s / a_r in 16.16 format */
+ int src_scale = ((a_s << 16) + (a_r >> 1)) / a_r;
+
+ dst_ptr[4 * planestride] = a_r;
+
+ /* Do simple compositing of source over backdrop */
+ for (k = 0; k < 4; k++) {
+ int c_s = src[k];
+ int c_b = 255 - dst_ptr[k * planestride];
+ tmp = (c_b << 16) + src_scale * (c_s - c_b) + 0x8000;
+ dst_ptr[k * planestride] = 255 - (tmp >> 16);
+ }
+ }
+ ++dst_ptr;
+ }
+ dst_ptr += rowstride;
+ }
+}
+
+static void
+mark_fill_rect_add_nospots(int w, int h, byte *gs_restrict dst_ptr, byte *gs_restrict src, int num_comp, int num_spots, int first_blend_spot,
+ byte src_alpha, int rowstride, int planestride, bool additive, pdf14_device *pdev, gs_blend_mode_t blend_mode,
+ bool overprint, gx_color_index drawn_comps, int tag_off, gs_graphics_type_tag_t curr_tag,
+ int alpha_g_off, int shape_off, byte shape, bool isolated)
+{
+ template_mark_fill_rect(w, h, dst_ptr, src, num_comp, /*num_spots*/0, first_blend_spot,
+ src_alpha, rowstride, planestride, /*additive*/1, pdev, blend_mode,
+ /*overprint*/0, /*drawn_comps*/0, tag_off, curr_tag,
+ alpha_g_off, shape_off, shape, /*isolated*/ 0);
+}
+
+static void
+mark_fill_rect_add_nospots_common(int w, int h, byte *gs_restrict dst_ptr, byte *gs_restrict src, int num_comp, int num_spots, int first_blend_spot,
+ byte src_alpha, int rowstride, int planestride, bool additive, pdf14_device *pdev, gs_blend_mode_t blend_mode,
+ bool overprint, gx_color_index drawn_comps, int tag_off, gs_graphics_type_tag_t curr_tag,
+ int alpha_g_off, int shape_off, byte shape, bool isolated)
+{
+ template_mark_fill_rect(w, h, dst_ptr, src, num_comp, /*num_spots*/0, /*first_blend_spot*/0,
+ src_alpha, rowstride, planestride, /*additive*/1, pdev, /*blend_mode*/BLEND_MODE_Normal,
+ /*overprint*/0, /*drawn_comps*/0, /*tag_off*/0, curr_tag,
+ alpha_g_off, /*shape_off*/0, shape, /*isolated*/ 0);
+}
+
+static void
+mark_fill_rect_add_nospots_common_no_alpha_g(int w, int h, byte *gs_restrict dst_ptr, byte *gs_restrict src, int num_comp, int num_spots, int first_blend_spot,
+ byte src_alpha, int rowstride, int planestride, bool additive, pdf14_device *pdev, gs_blend_mode_t blend_mode,
+ bool overprint, gx_color_index drawn_comps, int tag_off, gs_graphics_type_tag_t curr_tag,
+ int alpha_g_off, int shape_off, byte shape, bool isolated)
+{
+ template_mark_fill_rect(w, h, dst_ptr, src, num_comp, /*num_spots*/0, /*first_blend_spot*/0,
+ src_alpha, rowstride, planestride, /*additive*/1, pdev, /*blend_mode*/BLEND_MODE_Normal,
+ /*overprint*/0, /*drawn_comps*/0, /*tag_off*/0, curr_tag,
+ /*alpha_g_off*/0, /*shape_off*/0, shape, /*isolated*/ 0);
+}
+
+static void
+mark_fill_rect_add3_common(int w, int h, byte *gs_restrict dst_ptr, byte *gs_restrict src, int num_comp, int num_spots, int first_blend_spot,
+ byte src_alpha, int rowstride, int planestride, bool additive, pdf14_device *pdev, gs_blend_mode_t blend_mode,
+ bool overprint, gx_color_index drawn_comps, int tag_off, gs_graphics_type_tag_t curr_tag,
+ int alpha_g_off, int shape_off, byte shape, bool isolated)
+{
+ int i, j, k;
+
+ for (j = h; j > 0; --j) {
+ for (i = w; i > 0; --i) {
+ byte a_s = src[3];
+ byte a_b = dst_ptr[3 * planestride];
+ if (a_s == 0xff || a_b == 0) {
+ /* dest alpha is zero (or solid source) just use source. */
+ dst_ptr[0 * planestride] = src[0];
+ dst_ptr[1 * planestride] = src[1];
+ dst_ptr[2 * planestride] = src[2];
+ /* alpha */
+ dst_ptr[3 * planestride] = a_s;
+ } else if (a_s != 0) {
+ /* Result alpha is Union of backdrop and source alpha */
+ int tmp = (0xff - a_b) * (0xff - a_s) + 0x80;
+ unsigned int a_r = 0xff - (((tmp >> 8) + tmp) >> 8);
+ /* todo: verify that a_r is nonzero in all cases */
+
+ /* Compute a_s / a_r in 16.16 format */
+ int src_scale = ((a_s << 16) + (a_r >> 1)) / a_r;
+
+ dst_ptr[3 * planestride] = a_r;
+
+ /* Do simple compositing of source over backdrop */
+ for (k = 0; k < 3; k++) {
+ int c_s = src[k];
+ int c_b = dst_ptr[k * planestride];
+ tmp = (c_b << 16) + src_scale * (c_s - c_b) + 0x8000;
+ dst_ptr[k * planestride] = tmp >> 16;
+ }
+ }
+ ++dst_ptr;
+ }
+ dst_ptr += rowstride;
+ }
+}
+
+static void
+mark_fill_rect_add1_no_spots(int w, int h, byte *gs_restrict dst_ptr, byte *gs_restrict src, int num_comp, int num_spots, int first_blend_spot,
+ byte src_alpha, int rowstride, int planestride, bool additive, pdf14_device *pdev, gs_blend_mode_t blend_mode,
+ bool overprint, gx_color_index drawn_comps, int tag_off, gs_graphics_type_tag_t curr_tag,
+ int alpha_g_off, int shape_off, byte shape, bool isolated)
+{
+ int i;
+ bool tag_blend = blend_mode == BLEND_MODE_Normal ||
+ blend_mode == BLEND_MODE_Compatible ||
+ blend_mode == BLEND_MODE_CompatibleOverprint;
+
+ for (; h > 0; --h) {
+ for (i = w; i > 0; --i) {
+ /* background empty, nothing to change, or solid source */
+ byte a_s = src[1];
+ if ((blend_mode == BLEND_MODE_Normal && a_s == 0xff) || dst_ptr[planestride] == 0) {
+ dst_ptr[0] = src[0];
+ dst_ptr[planestride] = a_s;
+ } else {
+ art_pdf_composite_pixel_alpha_8_fast_mono(dst_ptr, src,
+ blend_mode, pdev->blend_procs,
+ planestride, pdev);
+ }
+ if (tag_off) {
+ /* If src alpha is 100% then set to curr_tag, else or */
+ /* other than Normal BM, we always OR */
+ if (tag_blend && a_s == 255) {
+ dst_ptr[tag_off] = curr_tag;
+ } else {
+ dst_ptr[tag_off] |= curr_tag;
+ }
+ }
+ if (alpha_g_off) {
+ int tmp = (255 - dst_ptr[alpha_g_off]) * src_alpha + 0x80;
+ dst_ptr[alpha_g_off] = 255 - ((tmp + (tmp >> 8)) >> 8);
+ }
+ if (shape_off) {
+ int tmp = (255 - dst_ptr[shape_off]) * shape + 0x80;
+ dst_ptr[shape_off] = 255 - ((tmp + (tmp >> 8)) >> 8);
+ }
+ ++dst_ptr;
+ }
+ dst_ptr += rowstride;
+ }
+}
+
+static void
+mark_fill_rect_add1_no_spots_normal(int w, int h, byte *gs_restrict dst_ptr, byte *gs_restrict src, int num_comp, int num_spots, int first_blend_spot,
+ byte src_alpha, int rowstride, int planestride, bool additive, pdf14_device *pdev, gs_blend_mode_t blend_mode,
+ bool overprint, gx_color_index drawn_comps, int tag_off, gs_graphics_type_tag_t curr_tag,
+ int alpha_g_off, int shape_off, byte shape, bool isolated)
+{
+ int i;
+
+ for (; h > 0; --h) {
+ for (i = w; i > 0; --i) {
+ /* background empty, nothing to change, or solid source */
+ byte a_s = src[1];
+ byte a_b = dst_ptr[planestride];
+ if (a_s == 0xff || a_b == 0) {
+ dst_ptr[0] = src[0];
+ dst_ptr[planestride] = a_s;
+ } else {
+ /* Result alpha is Union of backdrop and source alpha */
+ int tmp = (0xff - a_b) * (0xff - a_s) + 0x80;
+ unsigned int a_r = 0xff - (((tmp >> 8) + tmp) >> 8);
+
+ /* Compute a_s / a_r in 16.16 format */
+ int src_scale = ((a_s << 16) + (a_r >> 1)) / a_r;
+
+ /* Do simple compositing of source over backdrop */
+ int c_s = src[0];
+ int c_b = dst_ptr[0];
+ tmp = (c_b << 16) + src_scale * (c_s - c_b) + 0x8000;
+ dst_ptr[0] = tmp >> 16;
+ dst_ptr[planestride] = a_r;
+ }
+ if (tag_off) {
+ /* If src alpha is 100% then set to curr_tag, else or */
+ /* other than Normal BM, we always OR */
+ if (a_s == 255) {
+ dst_ptr[tag_off] = curr_tag;
+ } else {
+ dst_ptr[tag_off] |= curr_tag;
+ }
+ }
+ if (alpha_g_off) {
+ int tmp = (255 - dst_ptr[alpha_g_off]) * src_alpha + 0x80;
+ dst_ptr[alpha_g_off] = 255 - ((tmp + (tmp >> 8)) >> 8);
+ }
+ if (shape_off) {
+ int tmp = (255 - dst_ptr[shape_off]) * shape + 0x80;
+ dst_ptr[shape_off] = 255 - ((tmp + (tmp >> 8)) >> 8);
+ }
+ ++dst_ptr;
+ }
+ dst_ptr += rowstride;
+ }
+}
+
+static void
+mark_fill_rect_add1_no_spots_fast(int w, int h, byte *gs_restrict dst_ptr, byte *gs_restrict src, int num_comp, int num_spots, int first_blend_spot,
+ byte src_alpha, int rowstride, int planestride, bool additive, pdf14_device *pdev, gs_blend_mode_t blend_mode,
+ bool overprint, gx_color_index drawn_comps, int tag_off, gs_graphics_type_tag_t curr_tag,
+ int alpha_g_off, int shape_off, byte shape, bool isolated)
+{
+ int i;
+
+ for (; h > 0; --h) {
+ for (i = w; i > 0; --i) {
+ /* background empty, nothing to change, or solid source */
+ byte a_s = src[1];
+ byte a_b = dst_ptr[planestride];
+ if (a_s == 0xff || a_b == 0) {
+ dst_ptr[0] = src[0];
+ dst_ptr[planestride] = a_s;
+ } else if (a_s != 0) {
+ /* Result alpha is Union of backdrop and source alpha */
+ int tmp = (0xff - a_b) * (0xff - a_s) + 0x80;
+ unsigned int a_r = 0xff - (((tmp >> 8) + tmp) >> 8);
+
+ /* Compute a_s / a_r in 16.16 format */
+ int src_scale = ((a_s << 16) + (a_r >> 1)) / a_r;
+
+ /* Do simple compositing of source over backdrop */
+ int c_s = src[0];
+ int c_b = dst_ptr[0];
+ tmp = (c_b << 16) + src_scale * (c_s - c_b) + 0x8000;
+ dst_ptr[0] = tmp >> 16;
+ dst_ptr[planestride] = a_r;
+ }
+ ++dst_ptr;
+ }
+ dst_ptr += rowstride;
+ }
+}
+
+static int
+do_mark_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
+ gx_color_index color, const gx_device_color *pdc,
+ bool devn)
+{
+ pdf14_device *pdev = (pdf14_device *)dev;
+ pdf14_buf *buf = pdev->ctx->stack;
+ int j;
+ byte *dst_ptr;
+ byte src[PDF14_MAX_PLANES];
+ gs_blend_mode_t blend_mode = pdev->blend_mode;
+ bool additive = pdev->ctx->additive;
+ int rowstride = buf->rowstride;
+ int planestride = buf->planestride;
+ gs_graphics_type_tag_t curr_tag = GS_UNKNOWN_TAG; /* Quite compiler */
+ bool has_alpha_g = buf->has_alpha_g;
+ bool has_shape = buf->has_shape;
+ bool has_tags = buf->has_tags;
+ int num_chan = buf->n_chan;
+ int num_comp = num_chan - 1;
+ int shape_off = num_chan * planestride;
+ int alpha_g_off = shape_off + (has_shape ? planestride : 0);
+ int tag_off = alpha_g_off + (has_alpha_g ? planestride : 0);
+ bool overprint = pdev->overprint;
+ gx_color_index drawn_comps = pdev->drawn_comps;
+ byte shape = 0; /* Quiet compiler. */
+ byte src_alpha;
+ const gx_color_index mask = ((gx_color_index)1 << 8) - 1;
+ const int shift = 8;
+ int num_spots = buf->num_spots;
+ int first_blend_spot = num_comp;
+ pdf14_mark_fill_rect_fn fn;
+ bool isolated = buf->isolated;
+
+ if (num_spots > 0 && !blend_valid_for_spot(blend_mode))
+ first_blend_spot = num_comp - num_spots;
+ if (blend_mode == BLEND_MODE_Normal)
+ first_blend_spot = 0;
+
+ if (buf->data == NULL)
+ return 0;
+ /* NB: gx_color_index is 4 or 8 bytes */
+#if 0
+ if (sizeof(color) <= sizeof(ulong))
+ if_debug8m('v', dev->memory,
+ "[v]pdf14_mark_fill_rectangle, (%d, %d), %d x %d color = %lx bm %d, nc %d, overprint %d\n",
+ x, y, w, h, (ulong)color, blend_mode, num_chan, overprint);
+ else
+ if_debug9m('v', dev->memory,
+ "[v]pdf14_mark_fill_rectangle, (%d, %d), %d x %d color = %08lx%08lx bm %d, nc %d, overprint %d\n",
+ x, y, w, h,
+ (ulong)(color >> 8*(sizeof(color) - sizeof(ulong))), (ulong)color,
+ blend_mode, num_chan, overprint);
+#endif
+ /*
+ * Unpack the gx_color_index values. Complement the components for subtractive
+ * color spaces.
+ */
+ if (has_tags) {
+ curr_tag = (color >> (num_comp*8)) & 0xff;
+ }
+ if (devn) {
+ if (additive) {
+ for (j = 0; j < (num_comp - num_spots); j++) {
+ src[j] = ((pdc->colors.devn.values[j]) >> shift & mask);
+ }
+ for (j = 0; j < num_spots; j++) {
+ src[j + num_comp - num_spots] =
+ 255 - ((pdc->colors.devn.values[j + num_comp - num_spots]) >> shift & mask);
+ }
+ } else {
+ for (j = 0; j < num_comp; j++) {
+ src[j] = 255 - ((pdc->colors.devn.values[j]) >> shift & mask);
+ }
+ }
+ } else
+ pdev->pdf14_procs->unpack_color(num_comp, color, pdev, src);
+ src_alpha = src[num_comp] = (byte)floor (255 * pdev->alpha + 0.5);
+ if (has_shape)
+ shape = (byte)floor (255 * pdev->shape + 0.5);
+ /* Fit the mark into the bounds of the buffer */
+ if (x < buf->rect.p.x) {
+ w += x - buf->rect.p.x;
+ x = buf->rect.p.x;
+ }
+ if (y < buf->rect.p.y) {
+ h += y - buf->rect.p.y;
+ y = buf->rect.p.y;
+ }
+ if (x + w > buf->rect.q.x) w = buf->rect.q.x - x;
+ if (y + h > buf->rect.q.y) h = buf->rect.q.y - y;
+ /* Update the dirty rectangle with the mark */
+ if (x < buf->dirty.p.x) buf->dirty.p.x = x;
+ if (y < buf->dirty.p.y) buf->dirty.p.y = y;
+ if (x + w > buf->dirty.q.x) buf->dirty.q.x = x + w;
+ if (y + h > buf->dirty.q.y) buf->dirty.q.y = y + h;
+ dst_ptr = buf->data + (x - buf->rect.p.x) + (y - buf->rect.p.y) * rowstride;
+ src_alpha = 255-src_alpha;
+ shape = 255-shape;
+ if (!has_alpha_g)
+ alpha_g_off = 0;
+ if (!has_shape)
+ shape_off = 0;
+ if (!has_tags)
+ tag_off = 0;
+ rowstride -= w;
+ /* The num_comp == 1 && additive case is very common (mono output
+ * devices no spot support), so we optimise that specifically here. */
+ if (src[num_comp] == 0)
+ fn = mark_fill_rect_alpha0;
+ else if (additive && num_spots == 0) {
+ if (num_comp == 1) {
+ if (blend_mode == BLEND_MODE_Normal) {
+ if (tag_off == 0 && shape_off == 0 && alpha_g_off == 0)
+ fn = mark_fill_rect_add1_no_spots_fast;
+ else
+ fn = mark_fill_rect_add1_no_spots_normal;
+ } else
+ fn = mark_fill_rect_add1_no_spots;
+ } else if (tag_off == 0 && shape_off == 0 && blend_mode == BLEND_MODE_Normal) {
+ if (alpha_g_off == 0) {
+ if (num_comp == 3)
+ fn = mark_fill_rect_add3_common;
+ else
+ fn = mark_fill_rect_add_nospots_common_no_alpha_g;
+ } else
+ fn = mark_fill_rect_add_nospots_common;
+ } else
+ fn = mark_fill_rect_add_nospots;
+ } else if (!additive && num_spots == 0 && num_comp == 4 && num_spots == 0 &&
+ first_blend_spot == 0 && blend_mode == BLEND_MODE_Normal &&
+ !overprint && tag_off == 0 && alpha_g_off == 0 && shape_off == 0)
+ fn = mark_fill_rect_sub4_fast;
+ else
+ fn = mark_fill_rect;
+
+ fn(w, h, dst_ptr, src, num_comp, num_spots, first_blend_spot, src_alpha,
+ rowstride, planestride, additive, pdev, blend_mode, overprint,
+ drawn_comps, tag_off, curr_tag, alpha_g_off, shape_off, shape, isolated);
+
+#if 0
+/* #if RAW_DUMP */
+ /* Dump the current buffer to see what we have. */
+
+ if(global_index/10.0 == (int) (global_index/10.0) )
+ dump_raw_buffer(pdev->ctx->mem,
+ pdev->ctx->stack->rect.q.y-pdev->ctx->stack->rect.p.y,
+ pdev->ctx->stack->rect.q.x-pdev->ctx->stack->rect.p.x,
+ pdev->ctx->stack->n_planes,
+ pdev->ctx->stack->planestride, pdev->ctx->stack->rowstride,
+ "Draw_Rect", pdev->ctx->stack->data, pdev->ctx->stack->deep);
+
+ global_index++;
+#endif
+ return 0;
+}
+
+typedef void (*pdf14_mark_fill_rect16_fn)(int w, int h, uint16_t *gs_restrict dst_ptr, uint16_t *gs_restrict src, int num_comp, int num_spots, int first_blend_spot,
+ uint16_t src_alpha, int rowstride, int planestride, bool additive, pdf14_device *pdev, gs_blend_mode_t blend_mode,
+ bool overprint, gx_color_index drawn_comps, int tag_off, gs_graphics_type_tag_t curr_tag,
+ int alpha_g_off, int shape_off, uint16_t shape);
+
+static forceinline void
+template_mark_fill_rect16(int w, int h, uint16_t *gs_restrict dst_ptr, uint16_t *gs_restrict src, int num_comp, int num_spots, int first_blend_spot,
+ uint16_t src_alpha_, int rowstride, int planestride, bool additive, pdf14_device *pdev, gs_blend_mode_t blend_mode,
+ bool overprint, gx_color_index drawn_comps, int tag_off, gs_graphics_type_tag_t curr_tag,
+ int alpha_g_off, int shape_off, uint16_t shape_)
+{
+ int i, j, k;
+ gx_color_index comps;
+ uint16_t dst[PDF14_MAX_PLANES] = { 0 };
+ /* Expand src_alpha and shape to be 0...0x10000 rather than 0...0xffff */
+ int src_alpha = src_alpha_ + (src_alpha_>>15);
+ int shape = shape_ + (shape_>>15);
+ bool tag_blend = blend_mode == BLEND_MODE_Normal ||
+ blend_mode == BLEND_MODE_Compatible ||
+ blend_mode == BLEND_MODE_CompatibleOverprint;
+
+ for (j = h; j > 0; --j) {
+ for (i = w; i > 0; --i) {
+ if ((blend_mode == BLEND_MODE_Normal && src[num_comp] == 0xffff && !overprint) || dst_ptr[num_comp * planestride] == 0) {
+ /* dest alpha is zero (or normal, and solid src) just use source. */
+ if (additive) {
+ /* Hybrid case */
+ for (k = 0; k < (num_comp - num_spots); k++) {
+ dst_ptr[k * planestride] = src[k];
+ }
+ for (k = 0; k < num_spots; k++) {
+ dst_ptr[(k + num_comp - num_spots) * planestride] =
+ 65535 - src[k + num_comp - num_spots];
+ }
+ } else {
+ /* Pure subtractive */
+ for (k = 0; k < num_comp; k++) {
+ dst_ptr[k * planestride] = 65535 - src[k];
+ }
+ }
+ /* alpha */
+ dst_ptr[num_comp * planestride] = src[num_comp];
+ } else if (src[num_comp] != 0) {
+ uint16_t *pdst;
+ /* Complement subtractive planes */
+ if (!additive) {
+ /* Pure subtractive */
+ for (k = 0; k < num_comp; ++k)
+ dst[k] = 65535 - dst_ptr[k * planestride];
+ } else {
+ /* Hybrid case, additive with subtractive spots */
+ for (k = 0; k < (num_comp - num_spots); k++) {
+ dst[k] = dst_ptr[k * planestride];
+ }
+ for (k = 0; k < num_spots; k++) {
+ dst[k + num_comp - num_spots] =
+ 65535 - dst_ptr[(k + num_comp - num_spots) * planestride];
+ }
+ }
+ dst[num_comp] = dst_ptr[num_comp * planestride];
+ pdst = art_pdf_composite_pixel_alpha_16_inline(dst, src, num_comp, blend_mode, first_blend_spot,
+ pdev->blend_procs, pdev);
+ /* Until I see otherwise in AR or the spec, do not fool
+ with spot overprinting while we are in an RGB or Gray
+ blend color space. */
+ if (!additive && overprint) {
+ for (k = 0, comps = drawn_comps; comps != 0; ++k, comps >>= 1) {
+ if ((comps & 0x1) != 0) {
+ dst_ptr[k * planestride] = 65535 - pdst[k];
+ }
+ }
+ } else {
+ /* Post blend complement for subtractive */
+ if (!additive) {
+ /* Pure subtractive */
+ for (k = 0; k < num_comp; ++k)
+ dst_ptr[k * planestride] = 65535 - pdst[k];
+
+ } else {
+ /* Hybrid case, additive with subtractive spots */
+ for (k = 0; k < (num_comp - num_spots); k++) {
+ dst_ptr[k * planestride] = pdst[k];
+ }
+ for (k = 0; k < num_spots; k++) {
+ dst_ptr[(k + num_comp - num_spots) * planestride] =
+ 65535 - pdst[k + num_comp - num_spots];
+ }
+ }
+ }
+ /* The alpha channel */
+ dst_ptr[num_comp * planestride] = pdst[num_comp];
+ }
+ if (tag_off) {
+ /* If src alpha is 100% then set to curr_tag, else or */
+ /* other than Normal BM, we always OR */
+ if (src[num_comp] == 65535 && tag_blend) {
+ dst_ptr[tag_off] = curr_tag;
+ } else {
+ dst_ptr[tag_off] |= curr_tag;
+ }
+ }
+ if (alpha_g_off) {
+ int tmp = (65535 - dst_ptr[alpha_g_off]) * src_alpha + 0x8000;
+ dst_ptr[alpha_g_off] = 65535 - (tmp >> 16);
+ }
+ if (shape_off) {
+ int tmp = (65535 - dst_ptr[shape_off]) * shape + 0x8000;
+ dst_ptr[shape_off] = 65535 - (tmp >> 16);
+ }
+ ++dst_ptr;
+ }
+ dst_ptr += rowstride;
+ }
+}
+
+static void
+mark_fill_rect16_alpha0(int w, int h, uint16_t *gs_restrict dst_ptr, uint16_t *gs_restrict src, int num_comp, int num_spots, int first_blend_spot,
+ uint16_t src_alpha_, int rowstride, int planestride, bool additive, pdf14_device *pdev, gs_blend_mode_t blend_mode,
+ bool overprint, gx_color_index drawn_comps, int tag_off, gs_graphics_type_tag_t curr_tag,
+ int alpha_g_off, int shape_off, uint16_t shape_)
+{
+ int i, j;
+ int src_alpha = src_alpha_;
+ int shape = shape_;
+
+ src_alpha += src_alpha>>15;
+ shape += shape>>15;
+ for (j = h; j > 0; --j) {
+ for (i = w; i > 0; --i) {
+ if (alpha_g_off) {
+ int tmp = (65535 - dst_ptr[alpha_g_off]) * src_alpha + 0x8000;
+ dst_ptr[alpha_g_off] = 65535 - (tmp >> 16);
+ }
+ if (shape_off) {
+ int tmp = (65535 - dst_ptr[shape_off]) * shape + 0x8000;
+ dst_ptr[shape_off] = 65535 - (tmp >> 16);
+ }
+ ++dst_ptr;
+ }
+ dst_ptr += rowstride;
+ }
+}
+
+static void
+mark_fill_rect16(int w, int h, uint16_t *gs_restrict dst_ptr, uint16_t *gs_restrict src, int num_comp, int num_spots, int first_blend_spot,
+ uint16_t src_alpha, int rowstride, int planestride, bool additive, pdf14_device *pdev, gs_blend_mode_t blend_mode,
+ bool overprint, gx_color_index drawn_comps, int tag_off, gs_graphics_type_tag_t curr_tag,
+ int alpha_g_off, int shape_off, uint16_t shape)
+{
+ template_mark_fill_rect16(w, h, dst_ptr, src, num_comp, num_spots, first_blend_spot,
+ src_alpha, rowstride, planestride, additive, pdev, blend_mode,
+ overprint, drawn_comps, tag_off, curr_tag,
+ alpha_g_off, shape_off, shape);
+}
+
+static void
+mark_fill_rect16_sub4_fast(int w, int h, uint16_t *gs_restrict dst_ptr, uint16_t *gs_restrict src, int num_comp, int num_spots, int first_blend_spot,
+ uint16_t src_alpha, int rowstride, int planestride, bool additive, pdf14_device *pdev, gs_blend_mode_t blend_mode,
+ bool overprint, gx_color_index drawn_comps, int tag_off, gs_graphics_type_tag_t curr_tag,
+ int alpha_g_off, int shape_off, uint16_t shape)
+{
+ int i, j, k;
+
+ for (j = h; j > 0; --j) {
+ for (i = w; i > 0; --i) {
+ uint16_t a_s = src[4];
+ int a_b = dst_ptr[4 * planestride];
+ if ((a_s == 0xffff) || a_b == 0) {
+ /* dest alpha is zero (or normal, and solid src) just use source. */
+ dst_ptr[0 * planestride] = 65535 - src[0];
+ dst_ptr[1 * planestride] = 65535 - src[1];
+ dst_ptr[2 * planestride] = 65535 - src[2];
+ dst_ptr[3 * planestride] = 65535 - src[3];
+ /* alpha */
+ dst_ptr[4 * planestride] = a_s;
+ } else if (a_s != 0) {
+ /* Result alpha is Union of backdrop and source alpha */
+ int tmp, src_scale;
+ unsigned int a_r;
+
+ a_b += a_b>>15;
+ tmp = (0x10000 - a_b) * (0xffff - a_s) + 0x8000;
+ a_r = 0xffff - (tmp >> 16);
+
+ /* Compute a_s / a_r in 16.16 format */
+ src_scale = ((a_s << 16) + (a_r >> 1)) / a_r;
+
+ dst_ptr[4 * planestride] = a_r;
+
+ src_scale >>= 1; /* Lose a bit to avoid overflow */
+ /* Do simple compositing of source over backdrop */
+ for (k = 0; k < 4; k++) {
+ int c_s = src[k];
+ int c_b = 65535 - dst_ptr[k * planestride];
+ tmp = src_scale * (c_s - c_b) + 0x4000;
+ dst_ptr[k * planestride] = 0xffff - c_b - (tmp >> 15);
+ }
+ }
+ ++dst_ptr;
+ }
+ dst_ptr += rowstride;
+ }
+}
+
+static void
+mark_fill_rect16_add_nospots(int w, int h, uint16_t *gs_restrict dst_ptr, uint16_t *gs_restrict src, int num_comp, int num_spots, int first_blend_spot,
+ uint16_t src_alpha, int rowstride, int planestride, bool additive, pdf14_device *pdev, gs_blend_mode_t blend_mode,
+ bool overprint, gx_color_index drawn_comps, int tag_off, gs_graphics_type_tag_t curr_tag,
+ int alpha_g_off, int shape_off, uint16_t shape)
+{
+ template_mark_fill_rect16(w, h, dst_ptr, src, num_comp, /*num_spots*/0, first_blend_spot,
+ src_alpha, rowstride, planestride, /*additive*/1, pdev, blend_mode,
+ /*overprint*/0, /*drawn_comps*/0, tag_off, curr_tag,
+ alpha_g_off, shape_off, shape);
+}
+
+static void
+mark_fill_rect16_add_nospots_common(int w, int h, uint16_t *gs_restrict dst_ptr, uint16_t *gs_restrict src, int num_comp, int num_spots, int first_blend_spot,
+ uint16_t src_alpha, int rowstride, int planestride, bool additive, pdf14_device *pdev, gs_blend_mode_t blend_mode,
+ bool overprint, gx_color_index drawn_comps, int tag_off, gs_graphics_type_tag_t curr_tag,
+ int alpha_g_off, int shape_off, uint16_t shape)
+{
+ template_mark_fill_rect16(w, h, dst_ptr, src, num_comp, /*num_spots*/0, /*first_blend_spot*/0,
+ src_alpha, rowstride, planestride, /*additive*/1, pdev, /*blend_mode*/BLEND_MODE_Normal,
+ /*overprint*/0, /*drawn_comps*/0, /*tag_off*/0, curr_tag,
+ alpha_g_off, /*shape_off*/0, shape);
+}
+
+static void
+mark_fill_rect16_add_nospots_common_no_alpha_g(int w, int h, uint16_t *gs_restrict dst_ptr, uint16_t *gs_restrict src, int num_comp, int num_spots, int first_blend_spot,
+ uint16_t src_alpha, int rowstride, int planestride, bool additive, pdf14_device *pdev, gs_blend_mode_t blend_mode,
+ bool overprint, gx_color_index drawn_comps, int tag_off, gs_graphics_type_tag_t curr_tag,
+ int alpha_g_off, int shape_off, uint16_t shape)
+{
+ template_mark_fill_rect16(w, h, dst_ptr, src, num_comp, /*num_spots*/0, /*first_blend_spot*/0,
+ src_alpha, rowstride, planestride, /*additive*/1, pdev, /*blend_mode*/BLEND_MODE_Normal,
+ /*overprint*/0, /*drawn_comps*/0, /*tag_off*/0, curr_tag,
+ /*alpha_g_off*/0, /*shape_off*/0, shape);
+}
+
+static void
+mark_fill_rect16_add3_common(int w, int h, uint16_t *gs_restrict dst_ptr, uint16_t *gs_restrict src, int num_comp, int num_spots, int first_blend_spot,
+ uint16_t src_alpha, int rowstride, int planestride, bool additive, pdf14_device *pdev, gs_blend_mode_t blend_mode,
+ bool overprint, gx_color_index drawn_comps, int tag_off, gs_graphics_type_tag_t curr_tag,
+ int alpha_g_off, int shape_off, uint16_t shape)
+{
+ int i, j, k;
+
+ for (j = h; j > 0; --j) {
+ for (i = w; i > 0; --i) {
+ uint16_t a_s = src[3];
+ int a_b = dst_ptr[3 * planestride];
+ if (a_s == 0xffff || a_b == 0) {
+ /* dest alpha is zero (or solid source) just use source. */
+ dst_ptr[0 * planestride] = src[0];
+ dst_ptr[1 * planestride] = src[1];
+ dst_ptr[2 * planestride] = src[2];
+ /* alpha */
+ dst_ptr[3 * planestride] = a_s;
+ } else if (a_s != 0) {
+ int tmp;
+ unsigned int a_r;
+ int src_scale;
+
+ a_b += a_b >> 15;
+ /* Result alpha is Union of backdrop and source alpha */
+ tmp = (0x10000 - a_b) * (0xffff - a_s) + 0x8000;
+ a_r = 0xffff - (tmp >> 16);
+ /* todo: verify that a_r is nonzero in all cases */
+
+ /* Compute a_s / a_r in 16.16 format */
+ src_scale = ((a_s << 16) + (a_r >> 1)) / a_r;
+
+ dst_ptr[3 * planestride] = a_r;
+
+ src_scale >>= 1; /* Lose a bit to avoid overflow */
+ /* Do simple compositing of source over backdrop */
+ for (k = 0; k < 3; k++) {
+ int c_s = src[k];
+ int c_b = dst_ptr[k * planestride];
+ tmp = src_scale * (c_s - c_b) + 0x4000;
+ dst_ptr[k * planestride] = c_b + (tmp >> 15);
+ }
+ }
+ ++dst_ptr;
+ }
+ dst_ptr += rowstride;
+ }
+}
+
+static void
+mark_fill_rect16_add1_no_spots(int w, int h, uint16_t *gs_restrict dst_ptr, uint16_t *gs_restrict src, int num_comp, int num_spots, int first_blend_spot,
+ uint16_t src_alpha_, int rowstride, int planestride, bool additive, pdf14_device *pdev, gs_blend_mode_t blend_mode,
+ bool overprint, gx_color_index drawn_comps, int tag_off, gs_graphics_type_tag_t curr_tag,
+ int alpha_g_off, int shape_off, uint16_t shape_)
+{
+ int i;
+ int src_alpha = src_alpha_;
+ int shape = shape_;
+ bool tag_blend = blend_mode == BLEND_MODE_Normal ||
+ blend_mode == BLEND_MODE_Compatible ||
+ blend_mode == BLEND_MODE_CompatibleOverprint;
+
+ src_alpha += src_alpha>>15;
+ shape += shape>>15;
+ for (; h > 0; --h) {
+ for (i = w; i > 0; --i) {
+ /* background empty, nothing to change, or solid source */
+ uint16_t a_s = src[1];
+ if ((blend_mode == BLEND_MODE_Normal && a_s == 0xffff) || dst_ptr[planestride] == 0) {
+ dst_ptr[0] = src[0];
+ dst_ptr[planestride] = a_s;
+ } else {
+ art_pdf_composite_pixel_alpha_16_fast_mono(dst_ptr, src,
+ blend_mode, pdev->blend_procs,
+ planestride, pdev);
+ }
+ if (tag_off) {
+ /* If src alpha is 100% then set to curr_tag, else or */
+ /* other than Normal BM, we always OR */
+ if (tag_blend && a_s == 65535) {
+ dst_ptr[tag_off] = curr_tag;
+ } else {
+ dst_ptr[tag_off] |= curr_tag;
+ }
+ }
+ if (alpha_g_off) {
+ int tmp = (65535 - dst_ptr[alpha_g_off]) * src_alpha + 0x8000;
+ dst_ptr[alpha_g_off] = 65535 - (tmp >> 16);
+ }
+ if (shape_off) {
+ int tmp = (65535 - dst_ptr[shape_off]) * shape + 0x8000;
+ dst_ptr[shape_off] = 65535 - (tmp >> 16);
+ }
+ ++dst_ptr;
+ }
+ dst_ptr += rowstride;
+ }
+}
+
+static void
+mark_fill_rect16_add1_no_spots_normal(int w, int h, uint16_t *gs_restrict dst_ptr, uint16_t *gs_restrict src, int num_comp, int num_spots, int first_blend_spot,
+ uint16_t src_alpha_, int rowstride, int planestride, bool additive, pdf14_device *pdev, gs_blend_mode_t blend_mode,
+ bool overprint, gx_color_index drawn_comps, int tag_off, gs_graphics_type_tag_t curr_tag,
+ int alpha_g_off, int shape_off, uint16_t shape_)
+{
+ int i;
+ int src_alpha = src_alpha_;
+ int shape = shape_;
+
+ src_alpha += src_alpha>>15;
+ shape += shape>>15;
+
+ for (; h > 0; --h) {
+ for (i = w; i > 0; --i) {
+ /* background empty, nothing to change, or solid source */
+ uint16_t a_s = src[1];
+ uint16_t a_b = dst_ptr[planestride];
+ if (a_s == 0xffff || a_b == 0) {
+ dst_ptr[0] = src[0];
+ dst_ptr[planestride] = a_s;
+ } else {
+ /* Result alpha is Union of backdrop and source alpha */
+ int tmp, src_scale, c_s, c_b;
+ unsigned int a_r;
+
+ a_b += a_b>>15;
+ tmp = (0x10000 - a_b) * (0xffff - a_s) + 0x8000;
+ a_r = 0xffff - (tmp >> 16);
+
+ /* Compute a_s / a_r in 16.16 format */
+ src_scale = ((a_s << 16) + (a_r >> 1)) / a_r;
+
+ src_scale >>= 1; /* Lose a bit to avoid overflow */
+ /* Do simple compositing of source over backdrop */
+ c_s = src[0];
+ c_b = dst_ptr[0];
+ tmp = src_scale * (c_s - c_b) + 0x4000;
+ dst_ptr[0] = c_b + (tmp >> 15);
+ dst_ptr[planestride] = a_r;
+ }
+ if (tag_off) {
+ /* If src alpha is 100% then set to curr_tag, else or */
+ /* other than Normal BM, we always OR */
+ if (a_s == 65535) {
+ dst_ptr[tag_off] = curr_tag;
+ } else {
+ dst_ptr[tag_off] |= curr_tag;
+ }
+ }
+ if (alpha_g_off) {
+ int tmp = (65535 - dst_ptr[alpha_g_off]) * src_alpha + 0x8000;
+ dst_ptr[alpha_g_off] = 65535 - (tmp >> 16);
+ }
+ if (shape_off) {
+ int tmp = (65535 - dst_ptr[shape_off]) * shape + 0x8000;
+ dst_ptr[shape_off] = 65535 - (tmp >> 16);
+ }
+ ++dst_ptr;
+ }
+ dst_ptr += rowstride;
+ }
+}
+
+static void
+mark_fill_rect16_add1_no_spots_fast(int w, int h, uint16_t *gs_restrict dst_ptr, uint16_t *gs_restrict src, int num_comp, int num_spots, int first_blend_spot,
+ uint16_t src_alpha, int rowstride, int planestride, bool additive, pdf14_device *pdev, gs_blend_mode_t blend_mode,
+ bool overprint, gx_color_index drawn_comps, int tag_off, gs_graphics_type_tag_t curr_tag,
+ int alpha_g_off, int shape_off, uint16_t shape)
+{
+ int i;
+
+ for (; h > 0; --h) {
+ for (i = w; i > 0; --i) {
+ /* background empty, nothing to change, or solid source */
+ uint16_t a_s = src[1];
+ int a_b = dst_ptr[planestride];
+ if (a_s == 0xffff || a_b == 0) {
+ dst_ptr[0] = src[0];
+ dst_ptr[planestride] = a_s;
+ } else if (a_s != 0) {
+ /* Result alpha is Union of backdrop and source alpha */
+ int tmp, src_scale, c_s, c_b;
+ unsigned int a_r;
+
+ a_b += a_b>>15;
+ tmp = (0x10000 - a_b) * (0xffff - a_s) + 0x8000;
+ a_r = 0xffff - (tmp >> 16);
+
+ /* Compute a_s / a_r in 16.16 format */
+ src_scale = ((a_s << 16) + (a_r >> 1)) / a_r;
+
+ src_scale >>= 1; /* Lose a bit to avoid overflow */
+ /* Do simple compositing of source over backdrop */
+ c_s = src[0];
+ c_b = dst_ptr[0];
+ tmp = src_scale * (c_s - c_b) + 0x4000;
+ dst_ptr[0] = c_b + (tmp >> 15);
+ dst_ptr[planestride] = a_r;
+ }
+ ++dst_ptr;
+ }
+ dst_ptr += rowstride;
+ }
+}
+
+static int
+do_mark_fill_rectangle16(gx_device * dev, int x, int y, int w, int h,
+ gx_color_index color, const gx_device_color *pdc,
+ bool devn)
+{
+ pdf14_device *pdev = (pdf14_device *)dev;
+ pdf14_buf *buf = pdev->ctx->stack;
+ int j;
+ uint16_t *dst_ptr;
+ uint16_t src[PDF14_MAX_PLANES];
+ gs_blend_mode_t blend_mode = pdev->blend_mode;
+ bool additive = pdev->ctx->additive;
+ int rowstride = buf->rowstride;
+ int planestride = buf->planestride;
+ gs_graphics_type_tag_t curr_tag = GS_UNKNOWN_TAG; /* Quite compiler */
+ bool has_alpha_g = buf->has_alpha_g;
+ bool has_shape = buf->has_shape;
+ bool has_tags = buf->has_tags;
+ int num_chan = buf->n_chan;
+ int num_comp = num_chan - 1;
+ int shape_off = num_chan * planestride;
+ int alpha_g_off = shape_off + (has_shape ? planestride : 0);
+ int tag_off = alpha_g_off + (has_alpha_g ? planestride : 0);
+ bool overprint = pdev->overprint;
+ gx_color_index drawn_comps = pdev->drawn_comps;
+ uint16_t shape = 0; /* Quiet compiler. */
+ uint16_t src_alpha;
+ int num_spots = buf->num_spots;
+ int first_blend_spot = num_comp;
+ pdf14_mark_fill_rect16_fn fn;
+
+ if (num_spots > 0 && !blend_valid_for_spot(blend_mode))
+ first_blend_spot = num_comp - num_spots;
+ if (blend_mode == BLEND_MODE_Normal)
+ first_blend_spot = 0;
+
+ if (buf->data == NULL)
+ return 0;
+ /* NB: gx_color_index is 4 or 8 bytes */
+#if 0
+ if (sizeof(color) <= sizeof(ulong))
+ if_debug8m('v', dev->memory,
+ "[v]pdf14_mark_fill_rectangle, (%d, %d), %d x %d color = %lx bm %d, nc %d, overprint %d\n",
+ x, y, w, h, (ulong)color, blend_mode, num_chan, overprint);
+ else
+ if_debug9m('v', dev->memory,
+ "[v]pdf14_mark_fill_rectangle, (%d, %d), %d x %d color = %08lx%08lx bm %d, nc %d, overprint %d\n",
+ x, y, w, h,
+ (ulong)(color >> 8*(sizeof(color) - sizeof(ulong))), (ulong)color,
+ blend_mode, num_chan, overprint);
+#endif
+ /*
+ * Unpack the gx_color_index values. Complement the components for subtractive
+ * color spaces.
+ */
+ if (has_tags) {
+ curr_tag = (color >> (num_comp*16)) & 0xff;
+ }
+ if (devn) {
+ if (additive) {
+ for (j = 0; j < (num_comp - num_spots); j++) {
+ src[j] = pdc->colors.devn.values[j];
+ }
+ for (j = 0; j < num_spots; j++) {
+ src[j + num_comp - num_spots] =
+ 65535 - pdc->colors.devn.values[j + num_comp - num_spots];
+ }
+ } else {
+ for (j = 0; j < num_comp; j++) {
+ src[j] = 65535 - pdc->colors.devn.values[j];
+ }
+ }
+ } else
+ pdev->pdf14_procs->unpack_color16(num_comp, color, pdev, src);
+ src_alpha = src[num_comp] = (uint16_t)floor (65535 * pdev->alpha + 0.5);
+ if (has_shape)
+ shape = (uint16_t)floor (65535 * pdev->shape + 0.5);
+ /* Fit the mark into the bounds of the buffer */
+ if (x < buf->rect.p.x) {
+ w += x - buf->rect.p.x;
+ x = buf->rect.p.x;
+ }
+ if (y < buf->rect.p.y) {
+ h += y - buf->rect.p.y;
+ y = buf->rect.p.y;
+ }
+ if (x + w > buf->rect.q.x) w = buf->rect.q.x - x;
+ if (y + h > buf->rect.q.y) h = buf->rect.q.y - y;
+ /* Update the dirty rectangle with the mark */
+ if (x < buf->dirty.p.x) buf->dirty.p.x = x;
+ if (y < buf->dirty.p.y) buf->dirty.p.y = y;
+ if (x + w > buf->dirty.q.x) buf->dirty.q.x = x + w;
+ if (y + h > buf->dirty.q.y) buf->dirty.q.y = y + h;
+ dst_ptr = (uint16_t *)(buf->data + (x - buf->rect.p.x) * 2 + (y - buf->rect.p.y) * rowstride);
+ src_alpha = 65535-src_alpha;
+ shape = 65535-shape;
+ if (!has_alpha_g)
+ alpha_g_off = 0;
+ if (!has_shape)
+ shape_off = 0;
+ if (!has_tags)
+ tag_off = 0;
+ rowstride -= w<<1;
+ /* The num_comp == 1 && additive case is very common (mono output
+ * devices no spot support), so we optimise that specifically here. */
+ if (src[num_comp] == 0)
+ fn = mark_fill_rect16_alpha0;
+ else if (additive && num_spots == 0) {
+ if (num_comp == 1) {
+ if (blend_mode == BLEND_MODE_Normal) {
+ if (tag_off == 0 && shape_off == 0 && alpha_g_off == 0)
+ fn = mark_fill_rect16_add1_no_spots_fast;
+ else
+ fn = mark_fill_rect16_add1_no_spots_normal;
+ } else
+ fn = mark_fill_rect16_add1_no_spots;
+ } else if (tag_off == 0 && shape_off == 0 && blend_mode == BLEND_MODE_Normal) {
+ if (alpha_g_off == 0) {
+ if (num_comp == 3)
+ fn = mark_fill_rect16_add3_common;
+ else
+ fn = mark_fill_rect16_add_nospots_common_no_alpha_g;
+ } else
+ fn = mark_fill_rect16_add_nospots_common;
+ } else
+ fn = mark_fill_rect16_add_nospots;
+ } else if (!additive && num_spots == 0 && num_comp == 4 && num_spots == 0 &&
+ first_blend_spot == 0 && blend_mode == BLEND_MODE_Normal &&
+ !overprint && tag_off == 0 && alpha_g_off == 0 && shape_off == 0)
+ fn = mark_fill_rect16_sub4_fast;
+ else
+ fn = mark_fill_rect16;
+
+ /* Pass values as array offsets, not byte diffs */
+ rowstride >>= 1;
+ planestride >>= 1;
+ tag_off >>= 1;
+ alpha_g_off >>= 1;
+ shape_off >>= 1;
+ fn(w, h, dst_ptr, src, num_comp, num_spots, first_blend_spot, src_alpha,
+ rowstride, planestride, additive, pdev, blend_mode, overprint,
+ drawn_comps, tag_off, curr_tag, alpha_g_off, shape_off, shape);
+
+#if 0
+/* #if RAW_DUMP */
+ /* Dump the current buffer to see what we have. */
+
+ if(global_index/10.0 == (int) (global_index/10.0) )
+ dump_raw_buffer(pdev->ctx->mem,
+ pdev->ctx->stack->rect.q.y-pdev->ctx->stack->rect.p.y,
+ pdev->ctx->stack->rect.q.x-pdev->ctx->stack->rect.p.x,
+ pdev->ctx->stack->n_planes,
+ pdev->ctx->stack->planestride, pdev->ctx->stack->rowstride,
+ "Draw_Rect", pdev->ctx->stack->data, pdev->ctx->stack->deep);
+
+ global_index++;
+#endif
+ return 0;
+}
+
+int
+pdf14_mark_fill_rectangle(gx_device * dev, int x, int y, int w, int h,
+ gx_color_index color, const gx_device_color *pdc,
+ bool devn)
+{
+ pdf14_device *pdev = (pdf14_device *)dev;
+ pdf14_buf *buf = pdev->ctx->stack;
+
+ if (buf->deep)
+ return do_mark_fill_rectangle16(dev, x, y, w, h, color, pdc, devn);
+ else
+ return do_mark_fill_rectangle(dev, x, y, w, h, color, pdc, devn);
+}
+
+/* Keep this at the end because of the #undef print */
+
+#ifdef TRACK_COMPOSE_GROUPS
+static void
+dump_track_compose_groups(void)
+{
+ int i;
+
+ for (i = 0; i < (1<<17); i++)
+ {
+ if (compose_groups[i] == 0)
+ continue;
+#undef printf
+ printf("COMPOSE_GROUPS: %04x:%d\n", i, compose_groups[i]);
+ }
+}
+#endif