diff options
author | Robin Watts <Robin.Watts@artifex.com> | 2019-02-19 16:38:26 +0000 |
---|---|---|
committer | Robin Watts <Robin.Watts@artifex.com> | 2019-04-23 12:40:40 +0100 |
commit | f0f2fa2a084758bcf99409ce19c2c1f93dc04d32 (patch) | |
tree | 1ecf538377e94a3d60e3a08379c4604b415f56e4 /base/gxblend.c | |
parent | ba8ff34e302ec3e65bd47b14796dcfaef157527f (diff) | |
download | ghostpdl-f0f2fa2a084758bcf99409ce19c2c1f93dc04d32.tar.gz |
Deep color transparency
Previously, all transparency blending has been done using the
pdf14 compisitor working in 8 bits. Here, we extend it to be
capable of working in 16 bits.
When the compositor is created, it looks at the underlying device;
if that device uses more than 8 bits per component (plus 8 bits
for tags, if tags are in use), then we use 'deep' (i.e. 16 bit
per component) pdf14 buffers.
Diffstat (limited to 'base/gxblend.c')
-rw-r--r-- | base/gxblend.c | 2889 |
1 files changed, 2794 insertions, 95 deletions
diff --git a/base/gxblend.c b/base/gxblend.c index 7c3d55be4..db8144b4c 100644 --- a/base/gxblend.c +++ b/base/gxblend.c @@ -66,11 +66,11 @@ blend_valid_for_spot(gs_blend_mode_t blend_mode) must be in CMYK, RGB or monochrome. */ /* Note, data is planar */ - -void -smask_luminosity_mapping(int num_rows, int num_cols, int n_chan, int row_stride, - int plane_stride, byte *gs_restrict src, const byte *gs_restrict dst, bool isadditive, - gs_transparency_mask_subtype_t SMask_SubType) +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) { int x,y; int mask_alpha_offset,mask_C_offset,mask_M_offset,mask_Y_offset,mask_K_offset; @@ -79,8 +79,8 @@ smask_luminosity_mapping(int num_rows, int num_cols, int n_chan, int row_stride, #if RAW_DUMP dump_raw_buffer(num_rows, row_stride, n_chan, - plane_stride, row_stride, - "Raw_Mask", src); + plane_stride, row_stride, + "Raw_Mask", src, 0); global_index++; #endif @@ -179,34 +179,187 @@ smask_luminosity_mapping(int num_rows, int num_cols, int n_chan, int 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) +{ + 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(num_rows, row_stride, n_chan, + plane_stride, row_stride, + "Raw_Mask", 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 (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); + else + do_smask_luminosity_mapping(num_rows, num_cols, n_chan, row_stride, + plane_stride, src, dst, isadditive, SMask_SubType); +} + /* 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) + int planestride, bool deep) { int x, y; int position; - byte comp, a; - int tmp; - 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; + 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; } - position+=1; } } } @@ -228,15 +381,15 @@ void smask_copy(int num_rows, int num_cols, int 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) + gsicc_link_t *icclink, bool deep) { gsicc_bufferdesc_t input_buff_desc; gsicc_bufferdesc_t output_buff_desc; #if RAW_DUMP - dump_raw_buffer(num_rows, row_stride, n_chan, - plane_stride, row_stride, - "Raw_Mask_ICC", src); + dump_raw_buffer(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 @@ -244,10 +397,10 @@ void smask_icc(gx_device *dev, int num_rows, int num_cols, int n_chan, We will just handle that here and let the CMM know nothing about it */ - gsicc_init_buffer(&input_buff_desc, n_chan-1, 1, + 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, + gsicc_init_buffer(&output_buff_desc, 1, 1<<deep, false, false, true, plane_stride, row_stride, num_rows, num_cols); /* Transform the data */ @@ -300,6 +453,51 @@ art_blend_luminosity_rgb_8(int n_chan, byte *gs_restrict dst, const byte *gs_res } 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; + int scale; + + /* FIXME: Check this! */ + 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 - 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_custom_8(int n_chan, byte *gs_restrict dst, const byte *gs_restrict backdrop, const byte *gs_restrict src) { @@ -352,6 +550,60 @@ art_blend_luminosity_custom_8(int n_chan, byte *gs_restrict dst, const byte *gs_ 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; + int scale; + + /* FIXME: Check this! */ + /* 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 - 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]; +} + /* * The PDF 1.4 spec. does not give the details of the math involved in the * luminosity blending. All we are given is: @@ -384,6 +636,18 @@ art_blend_luminosity_cmyk_8(int n_chan, byte *gs_restrict dst, const byte *gs_re } 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) { @@ -449,6 +713,72 @@ art_blend_saturation_rgb_8(int n_chan, byte *gs_restrict dst, const byte *gs_res } 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; + int scale; + int r, g, b; + + /* FIXME: Check this! */ + 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) & 0x10000) { + 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 > 65535) + scalemax = ((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) { @@ -528,6 +858,88 @@ art_blend_saturation_custom_8(int n_chan, byte *gs_restrict dst, const byte *gs_ 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, @@ -541,6 +953,18 @@ art_blend_saturation_cmyk_8(int n_chan, byte *gs_restrict dst, const byte *gs_re 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] = { @@ -815,6 +1239,209 @@ art_blend_pixel_8(byte *gs_restrict dst, const byte *gs_restrict backdrop, 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) { + int b2 = b + (b>>15); + dst[i] = b - (((0xffff - (s<<1)) * b2 * (0x10000 - b2))>>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)) / (unsigned int)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)) / (unsigned int)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->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; comps != 0; ++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) @@ -895,6 +1522,76 @@ art_pdf_knockout_composite_pixel_alpha_8(byte *gs_restrict backdrop, byte tos_sh 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; + + 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) + 0x8000; + dst[i] = c_b + (tmp >> 16); + } + } 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); + 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) + 0x8000; + c_mix = c_s + (tmp >> 16); + tmp = src_scale * (c_mix - c_b) + 0x8000; + dst[i] = c_b + (tmp >> 16); + } + } + 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, @@ -967,6 +1664,78 @@ art_pdf_composite_pixel_alpha_8(byte *gs_restrict dst, const byte *gs_restrict s } } +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; + 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 = ((a_s << 16) + (a_r >> 1)) / a_r; + + if (first_spot != 0) { + /* Do compositing with blending */ + uint16_t blend[ART_MAX_CHAN]; + + 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)) + 0x8000; + c_mix = c_s + (((tmp >> 16) + tmp) >> 16); + 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; + } +} + 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, @@ -1037,6 +1806,75 @@ art_pdf_composite_pixel_alpha_8_inline(byte *gs_restrict dst, byte *gs_restrict 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 = ((a_s << 16) + (a_r >> 1)) / a_r; + + 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); + 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) + 0x8000)>>16; + c_b += (src_scale * (c_s - c_b) + 0x8000)>>16; + 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) + 0x8000)>>16; + 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: @@ -1087,6 +1925,47 @@ art_pdf_composite_pixel_alpha_8_fast_mono(byte *gs_restrict dst, const byte *gs_ 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; + + /* 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) + 0x8000; + c_s += (tmp>>16); + dst[0] = c_b + ((src_scale * (c_s - c_b) + 0x8000)>>16); + } + dst[stride] = a_r; +} + /** * art_pdf_recomposite_group_8: Recomposite group pixel. * @dst: Where to store pixel, also initial backdrop of group. @@ -1177,6 +2056,75 @@ art_pdf_recomposite_group_8(byte *gs_restrict *dstp, byte *gs_restrict dst_alpha /* 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 = (dst_alpha * 65535 + (src_alpha_g>>1)) / src_alpha_g - + dst_alpha; + for (i = 0; i < n_chan; i++) { + int si, di; + + si = src[i]; + di = dst[i]; + tmp = (si - di) * scale + 0x8000; + tmp = si + (tmp >> 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. @@ -1230,6 +2178,44 @@ art_pdf_composite_knockout_group_8(byte *gs_restrict backdrop, byte tos_shape, b 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. @@ -1273,6 +2259,30 @@ art_pdf_composite_group_8(byte *gs_restrict dst, byte *gs_restrict dst_alpha_g, 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 @@ -1287,6 +2297,18 @@ art_pdf_knockoutisolated_group_8(byte *gs_restrict dst, const byte *gs_restrict 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. @@ -1401,19 +2423,92 @@ art_pdf_composite_knockout_8(byte *gs_restrict dst, } } +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) +{ + 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 == 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) + 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 * (65535 - src_shape) + + ((int)src[i]) * 65535 * src_shape + (result_alpha << 15); + dst[i] = tmp / (result_alpha * 65535); + } + 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; + + 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 */ + + c_s = src[i]; + c_b = dst[i]; + c_bl = blend[i]; + tmp = a_b * (c_bl - ((int)c_s)) + 0x8000; + c_mix = c_s + (((tmp >> 16) + tmp) >> 16); + tmp = (c_b << 16) + src_scale * (c_mix - c_b) + 0x8000; + dst[i] = tmp >> 16; + } + 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 */ -void -dump_raw_buffer(int num_rows, int width, int n_chan, +static void +do_dump_raw_buffer(int num_rows, int width, int n_chan, int plane_stride, int rowstride, - char filename[],byte *Buffer) + char filename[], const byte *Buffer, bool deep, bool be) { char full_file_name[50]; FILE *fid; int z,y; - byte *buff_ptr; + const byte *buff_ptr; int max_bands; /* clist_band_count is incremented at every pdf14putimage */ @@ -1425,62 +2520,109 @@ dump_raw_buffer(int num_rows, int width, int n_chan, /* 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(full_file_name,"wb"); - fprintf(fid, "P7\nWIDTH %d\nHEIGHT %d\nDEPTH 2\nMAXVAL 255\nTUPLTYPE GRAYSCALE_ALPHA\nENDHDR\n", - width, num_rows); - for(y=0; y<num_rows; y++) - for(x=0; x<width; x++) - for(z=0; z<2; z++) - fputc(Buffer[z*plane_stride + y*rowstride + x], fid); + 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. */ + fputc(Buffer[z*plane_stride + y*rowstride + x*2 + be^1], fid); + 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++) + fputc(Buffer[z*plane_stride + y*rowstride + x], fid); + } fclose(fid); if (n_chan == 3) { - gs_sprintf(full_file_name,"%02d)%s_shape.pam",global_index,filename); + 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(full_file_name,"wb"); - fprintf(fid, "P7\nWIDTH %d\nHEIGHT %d\nDEPTH 1\nMAXVAL 255\nTUPLTYPE GRAYSCALE\nENDHDR\n", - width, num_rows); - for(y=0; y<num_rows; y++) - for(x=0; x<width; x++) - fputc(Buffer[2*plane_stride + y*rowstride + x], fid); + 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. */ + fputc(Buffer[2*plane_stride + y*rowstride + x * 2 + be^1], fid); + 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++) + fputc(Buffer[2*plane_stride + y*rowstride + x], fid); + } 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(full_file_name,"wb"); - fprintf(fid, "P7\nWIDTH %d\nHEIGHT %d\nDEPTH 4\nMAXVAL 255\nTUPLTYPE RGB_ALPHA\nENDHDR\n", - width, num_rows); - for(y=0; y<num_rows; y++) - for(x=0; x<width; x++) - for(z=0; z<4; z++) - fputc(Buffer[z*plane_stride + y*rowstride + x], fid); + 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. */ + fputc(Buffer[z*plane_stride + y*rowstride + x*2 + be^1], fid); + 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++) + fputc(Buffer[z*plane_stride + y*rowstride + x], fid); + } fclose(fid); if (n_chan > 4) { - gs_sprintf(full_file_name,"%02d)%s_shape.pam",global_index,filename); + gs_sprintf(full_file_name,"%02d)%s_shape.pgm",global_index,filename); fid = gp_fopen(full_file_name,"wb"); - fprintf(fid, "P7\nWIDTH %d\nHEIGHT %d\nDEPTH 1\nMAXVAL 255\nTUPLTYPE GRAYSCALE\nENDHDR\n", - width, num_rows); - for(y=0; y<num_rows; y++) - for(x=0; x<width; x++) - fputc(Buffer[4*plane_stride + y*rowstride + x], fid); + 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. */ + fputc(Buffer[4*plane_stride + y*rowstride + x*2 + be^1], fid); + 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++) + fputc(Buffer[4*plane_stride + y*rowstride + x], fid); + } fclose(fid); } if (n_chan == 6) { - gs_sprintf(full_file_name,"%02d)%s_tags.pam",global_index,filename); + gs_sprintf(full_file_name,"%02d)%s_tags.pgm",global_index,filename); fid = gp_fopen(full_file_name,"wb"); - fprintf(fid, "P7\nWIDTH %d\nHEIGHT %d\nDEPTH 1\nMAXVAL 255\nTUPLTYPE GRAYSCALE\nENDHDR\n", - width, num_rows); - for(y=0; y<num_rows; y++) - for(x=0; x<width; x++) - fputc(Buffer[5*plane_stride + y*rowstride + x], fid); + 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++) + 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++) + fputc(Buffer[5*plane_stride + y*rowstride + x], fid); + } fclose(fid); } return; } #endif max_bands = ( n_chan < 57 ? n_chan : 56); /* Photoshop handles at most 56 bands */ - gs_sprintf(full_file_name,"%02d)%s_%dx%dx%d.raw",global_index,filename,width,num_rows,max_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(full_file_name,"wb"); for (z = 0; z < max_bands; ++z) { @@ -1488,14 +2630,42 @@ dump_raw_buffer(int num_rows, int width, int n_chan, buff_ptr = &(Buffer[z*plane_stride]); for ( y = 0; y < num_rows; y++ ) { /* write out each row */ - fwrite(buff_ptr,sizeof(unsigned char),width,fid); + fwrite(buff_ptr,sizeof(unsigned char),width<<deep,fid); buff_ptr += rowstride; } } fclose(fid); } + +void +dump_raw_buffer(int num_rows, int width, int n_chan, + int plane_stride, int rowstride, + char filename[],const byte *Buffer, bool deep) +{ + do_dump_raw_buffer(num_rows, width, n_chan, plane_stride, + rowstride, filename, Buffer, deep, 0); +} + +void +dump_raw_buffer_be(int num_rows, int width, int n_chan, + int plane_stride, int rowstride, + char filename[],const byte *Buffer, bool deep) +{ + do_dump_raw_buffer(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, 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, @@ -1567,8 +2737,9 @@ template_compose_group(byte *gs_restrict tos_ptr, bool tos_isolated, if (has_matte && matte_alpha != 0 && matte_alpha < 0xff) { for (i = 0; i < n_chan; i++) { /* undo */ - int val = tos_ptr[i * tos_planestride] - maskbuf->matte[i]; - int temp = ((((val * 0xff) << 8) / matte_alpha) >> 8) + maskbuf->matte[i]; + 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) @@ -2031,16 +3202,16 @@ compose_group_alphaless_nonknockout(byte *tos_ptr, bool tos_isolated, int tos_pl backdrop_ptr, /* has_matte */ false , n_chan, additive, num_spots, overprint, drawn_comps, x0, y0, x1, y1, pblend_procs, pdev, 0); } -void -pdf14_compose_group(pdf14_buf *tos, pdf14_buf *nos, pdf14_buf *maskbuf, +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; - byte shape = tos->shape; + 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; @@ -2111,7 +3282,7 @@ pdf14_compose_group(pdf14_buf *tos, pdf14_buf *nos, pdf14_buf *maskbuf, /* 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; + 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]; @@ -2122,14 +3293,14 @@ pdf14_compose_group(pdf14_buf *tos, pdf14_buf *nos, pdf14_buf *maskbuf, #if RAW_DUMP composed_ptr = nos_ptr; dump_raw_buffer(y1-y0, width, tos->n_planes, tos_planestride, tos->rowstride, - "bImageTOS",tos_ptr); + "bImageTOS", tos_ptr, tos->deep); dump_raw_buffer(y1-y0, width, nos->n_planes, nos_planestride, nos->rowstride, - "cImageNOS",nos_ptr); + "cImageNOS", nos_ptr, tos->deep); if (maskbuf !=NULL && maskbuf->data != NULL) { dump_raw_buffer(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->data, maskbuf->deep); } #endif @@ -2203,15 +3374,790 @@ pdf14_compose_group(pdf14_buf *tos, pdf14_buf *nos, pdf14_buf *maskbuf, #if RAW_DUMP dump_raw_buffer(y1-y0, width, nos->n_planes, nos_planestride, nos->rowstride, - "eComposed", composed_ptr); + "eComposed", composed_ptr, nos->deep); + global_index++; +#endif +} + + +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, byte *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, byte *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) + /* FIXME: Not ideal */ + matte_alpha = mask_tr_fn[*mask_curr_ptr >> 8] * 0x101; + } + } + + /* 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) { + /* FIXME: Not ideal */ + uint16_t mask = mask_tr_fn[(*mask_curr_ptr++)>>8] * 0x101; + 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, byte *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, byte *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, byte *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++) { + /* FIXME: Not ideal */ + int mask = mask_tr_fn[(*mask_curr_ptr++)>>8] * 0x101; + uint16_t src_alpha = tos_ptr[n_chan * tos_planestride]; + if (src_alpha != 0) { + uint16_t a_b; + 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; + + /* 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) + 0x8000) >> 16); + } + } + } + ++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, byte *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) { + /* FIXME: Not ideal */ + int mask = mask_tr_fn[(*mask_curr_ptr++)>>8] * 0x101; + 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; + + /* 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) + 0x8000) >> 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>>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, byte *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, byte *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, byte *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, byte *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, byte *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, byte *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); + byte *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) { + mask_tr_fn = 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 = mask_tr_fn[mask_bg_alpha>>8] * 0x101; + mask_bg_alpha += mask_bg_alpha>>8; + mask_bg_alpha = (alpha * mask_bg_alpha + 0x8000)>>16; + } + n_chan--; /* Now the true number of colorants (i.e. not including alpha)*/ +#if RAW_DUMP + composed_ptr = nos_ptr; + dump_raw_buffer(y1-y0, width, tos->n_planes, tos_planestride, tos->rowstride, + "bImageTOS", (byte *)tos_ptr, tos->deep); + dump_raw_buffer(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(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(y1-y0, width, nos->n_planes, nos_planestride<<1, 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) +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; @@ -2219,8 +4165,8 @@ pdf14_compose_alphaless_group(pdf14_buf *tos, pdf14_buf *nos, gx_color_index drawn_comps = pdev->drawn_comps; int n_chan = nos->n_chan; int num_spots = tos->num_spots; - byte alpha = tos->alpha; - byte shape = tos->shape; + 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; @@ -2280,9 +4226,9 @@ pdf14_compose_alphaless_group(pdf14_buf *tos, pdf14_buf *nos, #if RAW_DUMP composed_ptr = nos_ptr; dump_raw_buffer(y1-y0, width, tos->n_planes, tos_planestride, tos->rowstride, - "bImageTOS",tos_ptr); + "bImageTOS", tos_ptr, tos->deep); dump_raw_buffer(y1-y0, width, nos->n_planes, nos_planestride, nos->rowstride, - "cImageNOS",nos_ptr); + "cImageNOS", nos_ptr, nos->deep); /* maskbuf is NULL in here */ #endif @@ -2333,11 +4279,155 @@ pdf14_compose_alphaless_group(pdf14_buf *tos, pdf14_buf *nos, #if RAW_DUMP dump_raw_buffer(y1-y0, width, nos->n_planes, nos_planestride, nos->rowstride, - "eComposed", composed_ptr); + "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); + byte *mask_tr_fn = NULL; /* Quiet compiler. */ + 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(y1-y0, width, tos->n_planes, tos_planestride, tos->rowstride, + "bImageTOS", (byte *)tos_ptr, tos->deep); + dump_raw_buffer(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, 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(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, @@ -2741,10 +4831,10 @@ mark_fill_rect_add1_no_spots_fast(int w, int h, byte *gs_restrict dst_ptr, byte } } -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) +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; @@ -2884,18 +4974,627 @@ pdf14_mark_fill_rectangle(gx_device * dev, int x, int y, int w, int h, /* #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->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); + if(global_index/10.0 == (int) (global_index/10.0) ) + dump_raw_buffer(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); + + 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 && blend_mode == BLEND_MODE_Normal) { + 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; + + /* 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) + 0x8000; + dst_ptr[k * planestride] = 0xffff - c_b - (tmp >> 16); + } + } + ++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; + + /* 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) + 0x8000; + dst_ptr[k * planestride] = c_b + (tmp >> 16); + } + } + ++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_; + + 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 (blend_mode == BLEND_MODE_Normal && 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; + + /* Do simple compositing of source over backdrop */ + c_s = src[0]; + c_b = dst_ptr[0]; + tmp = src_scale * (c_s - c_b) + 0x8000; + dst_ptr[0] = c_b + (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 == 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; + + /* Do simple compositing of source over backdrop */ + c_s = src[0]; + c_b = dst_ptr[0]; + tmp = src_scale * (c_s - c_b) + 0x8000; + dst_ptr[0] = c_b + (tmp >> 16); + 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->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 |