/* Copyright (C) 2001-2023 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of the license contained in the file LICENSE in this distribution. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, CA 94129, USA, for further information. */ #include "gxdownscale.h" #include "gserrors.h" #include "string_.h" #include "gdevprn.h" #include "assert_.h" #include "gsicc_cache.h" #ifdef WITH_CAL #include "cal_ets.h" #else #include "ets.h" #endif /* Nasty inline declaration, as gxht_thresh.h requires penum */ void gx_ht_threshold_row_bit_sub(byte *contone, byte *threshold_strip, int contone_stride, byte *halftone, int dithered_stride, int width, int num_rows, int offset_bits); struct gx_downscale_liner_s { int (*get_line)(gx_downscale_liner *, void *, int); void (*drop)(gx_downscale_liner *, gs_memory_t *); }; enum { MAX_ETS_PLANES = 8 }; /* Error diffusion data is stored in errors block. * We have 1 empty entry at each end to avoid overflow. When * moving left to right we read from entries 2->width+1 (inclusive), and * write to 1->width. When moving right to left we read from width->1 and * write to width+1->2. * * Minimum feature size data is stored in the mfs_data block. * We have 1 extra entry at the end to avoid overflow. When moving left to * right we read from entries 1->width (inclusive), and write to 0->width-1. * When moving right to left we read from width-1->0 and write to width->1. */ enum { mfs_clear = 0, mfs_force_off = 1, mfs_above_is_0 = 2, mfs_above_left_is_0 = 4, }; /* Mono downscale/error diffusion/min feature size code */ /* Subsidiary function to pack the data from 8 bits to 1 */ static void pack_8to1(byte *outp, byte *inp, int w) { int mask = 128; int value = 0; for (; w > 0; w--) { if (*inp++) value |= mask; mask >>= 1; if (mask == 0) { mask = 128; *outp++= value; value = 0; } } if (mask != 128) { *outp++ = value; } } static void down_core(gx_downscaler_t *ds, byte *out_buffer, byte *in_buffer, int row, int plane, int span) { int x, xx, y, value; int e_downleft, e_down, e_forward = 0; int pad_white; byte *inp, *outp; int width = ds->width; int awidth = ds->awidth; int factor = ds->factor; int *errors = ds->errors + (awidth+3)*plane; const int threshold = factor*factor*128; const int max_value = factor*factor*255; pad_white = (awidth - width) * factor; if (pad_white < 0) pad_white = 0; if (pad_white) { inp = in_buffer + width*factor; for (y = factor; y > 0; y--) { memset(inp, 0xFF, pad_white); inp += span; } } inp = in_buffer; if ((row & 1) == 0) { /* Left to Right pass (no min feature size) */ const int back = span * factor - 1; errors += 2; outp = inp; for (x = awidth; x > 0; x--) { value = e_forward + *errors; for (xx = factor; xx > 0; xx--) { for (y = factor; y > 0; y--) { value += *inp; inp += span; } inp -= back; } if (value >= threshold) { *outp++ = 1; value -= max_value; } else { *outp++ = 0; } e_forward = value * 7/16; e_downleft = value * 3/16; e_down = value * 5/16; value -= e_forward + e_downleft + e_down; errors[-2] += e_downleft; errors[-1] += e_down; *errors++ = value; } outp -= awidth; } else { /* Right to Left pass (no min feature size) */ const int back = span * factor + 1; errors += awidth; inp += awidth*factor-1; outp = inp; for (x = awidth; x > 0; x--) { value = e_forward + *errors; for (xx = factor; xx > 0; xx--) { for (y = factor; y > 0; y--) { value += *inp; inp += span; } inp -= back; } if (value >= threshold) { *outp-- = 1; value -= max_value; } else { *outp-- = 0; } e_forward = value * 7/16; e_downleft = value * 3/16; e_down = value * 5/16; value -= e_forward + e_downleft + e_down; errors[2] += e_downleft; errors[1] += e_down; *errors-- = value; } outp++; } pack_8to1(out_buffer, outp, awidth); } static void down_core_ets_1(gx_downscaler_t *ds, byte *out_buffer, byte *in_buffer, int row, int plane, int span) { unsigned char *dest[MAX_ETS_PLANES]; ETS_SrcPixel *src[MAX_ETS_PLANES]; int pad_white, y; int factor = ds->factor; pad_white = (ds->awidth - ds->width) * factor * 4; if (pad_white < 0) pad_white = 0; if (pad_white) { unsigned char *inp = in_buffer + ds->width * factor * 4; for (y = factor; y > 0; y--) { memset(inp, 0xFF, pad_white); inp += span; } } if (ds->ets_downscale) ds->ets_downscale(ds, in_buffer, in_buffer, row, plane, span); src[0] = in_buffer; dest[0] = in_buffer; ets_line((ETS_Ctx *)ds->ets_config, dest, (const ETS_SrcPixel * const *)src); pack_8to1(out_buffer, in_buffer, ds->awidth); } static void down_core_1(gx_downscaler_t *ds, byte *out_buffer, byte *in_buffer, int row, int plane, int span) { int x, value; int e_downleft, e_down, e_forward = 0; int pad_white; byte *inp, *outp; int width = ds->width; int awidth = ds->awidth; int *errors = ds->errors + (awidth+3)*plane; const int threshold = 128; const int max_value = 255; pad_white = (awidth - width); if (pad_white < 0) pad_white = 0; if (pad_white) { memset(in_buffer + width, 0xFF, pad_white); } inp = in_buffer; if ((row & 1) == 0) { /* Left to Right pass (no min feature size) */ errors += 2; outp = inp; for (x = awidth; x > 0; x--) { value = e_forward + *errors + *inp++; if (value >= threshold) { *outp++ = 1; value -= max_value; } else { *outp++ = 0; } e_forward = value * 7/16; e_downleft = value * 3/16; e_down = value * 5/16; value -= e_forward + e_downleft + e_down; errors[-2] += e_downleft; errors[-1] += e_down; *errors++ = value; } outp -= awidth; } else { /* Right to Left pass (no min feature size) */ errors += awidth; inp += awidth-1; outp = inp; for (x = awidth; x > 0; x--) { value = e_forward + *errors + *inp--; if (value >= threshold) { *outp-- = 1; value -= max_value; } else { *outp-- = 0; } e_forward = value * 7/16; e_downleft = value * 3/16; e_down = value * 5/16; value -= e_forward + e_downleft + e_down; errors[2] += e_downleft; errors[1] += e_down; *errors-- = value; } outp++; } pack_8to1(out_buffer, outp, awidth); } static void down_core_2(gx_downscaler_t *ds, byte *out_buffer, byte *in_buffer, int row, int plane, int span) { int x, value; int e_downleft, e_down, e_forward = 0; int pad_white; byte *inp, *outp; int width = ds->width; int awidth = ds->awidth; int *errors = ds->errors + (awidth+3)*plane; const int threshold = 2*2*128; const int max_value = 2*2*255; pad_white = (awidth - width) * 2; if (pad_white < 0) pad_white = 0; if (pad_white) { inp = in_buffer + width*2; for (x = 2; x > 0; x--) { memset(inp, 0xFF, pad_white); inp += span; } } inp = in_buffer; if ((row & 1) == 0) { /* Left to Right pass (no min feature size) */ errors += 2; outp = inp; for (x = awidth; x > 0; x--) { value = e_forward + *errors + inp[0] + inp[1] + inp[span] + inp[span+1]; inp += 2; if (value >= threshold) { *outp++ = 1; value -= max_value; } else { *outp++ = 0; } e_forward = value * 7/16; e_downleft = value * 3/16; e_down = value * 5/16; value -= e_forward + e_downleft + e_down; errors[-2] += e_downleft; errors[-1] += e_down; *errors++ = value; } outp -= awidth; } else { /* Right to Left pass (no min feature size) */ errors += awidth; inp += (awidth-1)*2; outp = inp; for (x = awidth; x > 0; x--) { value = e_forward + *errors + inp[0] + inp[1] + inp[span] + inp[span+1]; inp -= 2; if (value >= threshold) { *outp-- = 1; value -= max_value; } else { *outp-- = 0; } e_forward = value * 7/16; e_downleft = value * 3/16; e_down = value * 5/16; value -= e_forward + e_downleft + e_down; errors[2] += e_downleft; errors[1] += e_down; *errors-- = value; } outp++; } pack_8to1(out_buffer, outp, awidth); } static void down_core_3(gx_downscaler_t *ds, byte *out_buffer, byte *in_buffer, int row, int plane, int span) { int x, value; int e_downleft, e_down, e_forward = 0; int pad_white; byte *inp, *outp; int width = ds->width; int awidth = ds->awidth; int *errors = ds->errors + (awidth+3)*plane; const int threshold = 3*3*128; const int max_value = 3*3*255; pad_white = (awidth - width) * 3; if (pad_white < 0) pad_white = 0; if (pad_white) { inp = in_buffer + width*3; for (x = 3; x > 0; x--) { memset(inp, 0xFF, pad_white); inp += span; } } inp = in_buffer; if ((row & 1) == 0) { /* Left to Right pass (no min feature size) */ errors += 2; outp = inp; for (x = awidth; x > 0; x--) { value = e_forward + *errors + inp[ 0] + inp[ 1] + inp[ 2] + inp[span ] + inp[span +1] + inp[span +2] + inp[span*2] + inp[span*2+1] + inp[span*2+2]; inp += 3; if (value >= threshold) { *outp++ = 1; value -= max_value; } else { *outp++ = 0; } e_forward = value * 7/16; e_downleft = value * 3/16; e_down = value * 5/16; value -= e_forward + e_downleft + e_down; errors[-2] += e_downleft; errors[-1] += e_down; *errors++ = value; } outp -= awidth; } else { /* Right to Left pass (no min feature size) */ errors += awidth; inp += (awidth-1)*3; outp = inp; for (x = awidth; x > 0; x--) { value = e_forward + *errors + inp[ 0] + inp[ 1] + inp[ 2] + inp[span ] + inp[span +1] + inp[span +2] + inp[span*2] + inp[span*2+1] + inp[span*2+2]; inp -= 3; if (value >= threshold) { *outp-- = 1; value -= max_value; } else { *outp-- = 0; } e_forward = value * 7/16; e_downleft = value * 3/16; e_down = value * 5/16; value -= e_forward + e_downleft + e_down; errors[2] += e_downleft; errors[1] += e_down; *errors-- = value; } outp++; } pack_8to1(out_buffer, outp, awidth); } static void down_core_4(gx_downscaler_t *ds, byte *out_buffer, byte *in_buffer, int row, int plane, int span) { int x, value; int e_downleft, e_down, e_forward = 0; int pad_white; byte *inp, *outp; int width = ds->width; int awidth = ds->awidth; int *errors = ds->errors + (awidth+3)*plane; const int threshold = 4*4*128; const int max_value = 4*4*255; pad_white = (awidth - width) * 4; if (pad_white < 0) pad_white = 0; if (pad_white) { inp = in_buffer + width*4; for (x = 4; x > 0; x--) { memset(inp, 0xFF, pad_white); inp += span; } } inp = in_buffer; if ((row & 1) == 0) { /* Left to Right pass (no min feature size) */ errors += 2; outp = inp; for (x = awidth; x > 0; x--) { value = e_forward + *errors + inp[ 0] + inp[ 1] + inp[ 2] + inp[3 ] + inp[span ] + inp[span +1] + inp[span +2] + inp[span +3] + inp[span*2] + inp[span*2+1] + inp[span*2+2] + inp[span*2+3] + inp[span*3] + inp[span*3+1] + inp[span*3+2] + inp[span*3+3]; inp += 4; if (value >= threshold) { *outp++ = 1; value -= max_value; } else { *outp++ = 0; } e_forward = value * 7/16; e_downleft = value * 3/16; e_down = value * 5/16; value -= e_forward + e_downleft + e_down; errors[-2] += e_downleft; errors[-1] += e_down; *errors++ = value; } outp -= awidth; } else { /* Right to Left pass (no min feature size) */ errors += awidth; inp += (awidth-1)*4; outp = inp; for (x = awidth; x > 0; x--) { value = e_forward + *errors + inp[ 0] + inp[ 1] + inp[ 2] + inp[3 ] + inp[span ] + inp[span +1] + inp[span +2] + inp[span +3] + inp[span*2] + inp[span*2+1] + inp[span*2+2] + inp[span*2+3] + inp[span*3] + inp[span*3+1] + inp[span*3+2] + inp[span*3+3]; inp -= 4; if (value >= threshold) { *outp-- = 1; value -= max_value; } else { *outp-- = 0; } e_forward = value * 7/16; e_downleft = value * 3/16; e_down = value * 5/16; value -= e_forward + e_downleft + e_down; errors[2] += e_downleft; errors[1] += e_down; *errors-- = value; } outp++; } pack_8to1(out_buffer, outp, awidth); } static void down_core_mfs(gx_downscaler_t *ds, byte *out_buffer, byte *in_buffer, int row, int plane, int span) { int x, xx, y, value; int e_downleft, e_down, e_forward = 0; int pad_white; byte *inp, *outp; int width = ds->width; int awidth = ds->awidth; int factor = ds->factor; int *errors = ds->errors + (awidth+3)*plane; byte *mfs_data = ds->mfs_data + (awidth+1)*plane; const int threshold = factor*factor*128; const int max_value = factor*factor*255; pad_white = (awidth - width) * factor; if (pad_white < 0) pad_white = 0; if (pad_white) { inp = in_buffer + width*factor; for (y = factor; y > 0; y--) { memset(inp, 0xFF, pad_white); inp += span; } } inp = in_buffer; if ((row & 1) == 0) { /* Left to Right pass (with min feature size = 2) */ const int back = span * factor -1; byte mfs, force_forward = 0; errors += 2; outp = inp; *mfs_data++ = mfs_clear; for (x = awidth; x > 0; x--) { value = e_forward + *errors; for (xx = factor; xx > 0; xx--) { for (y = factor; y > 0; y--) { value += *inp; inp += span; } inp -= back; } mfs = *mfs_data; *mfs_data++ = mfs_clear; if ((mfs & mfs_force_off) || force_forward) { /* We are being forced to be 0 */ *outp++ = 0; force_forward = 0; } else if (value < threshold) { /* We want to be 0 anyway */ *outp++ = 0; if ((mfs & (mfs_above_is_0 | mfs_above_left_is_0)) != (mfs_above_is_0 | mfs_above_left_is_0)) { /* We aren't in a group anyway, so must force other * pixels. */ mfs_data[-2] |= mfs_force_off; mfs_data[-1] |= mfs_force_off; force_forward = 1; } else { /* No forcing, but we need to tell other pixels that * we were 0. */ mfs_data[-2] |= mfs_above_is_0; mfs_data[-1] |= mfs_above_left_is_0; } } else { *outp++ = 1; value -= max_value; } e_forward = value * 7/16; e_downleft = value * 3/16; e_down = value * 5/16; value -= e_forward + e_downleft + e_down; errors[-2] += e_downleft; errors[-1] += e_down; *errors++ = value; } outp -= awidth; } else { /* Right to Left pass (with min feature size = 2) */ const int back = span * factor + 1; byte mfs, force_forward = 0; errors += awidth; mfs_data += awidth; inp += awidth*factor-1; outp = inp; *mfs_data-- = mfs_clear; for (x = awidth; x > 0; x--) { value = e_forward + *errors; for (xx = factor; xx > 0; xx--) { for (y = factor; y > 0; y--) { value += *inp; inp += span; } inp -= back; } mfs = *mfs_data; *mfs_data-- = mfs_clear; if ((mfs & mfs_force_off) || force_forward) { /* We are being forced to be 0 */ *outp-- = 0; force_forward = 0; } else if (value < threshold) { *outp-- = 0; if ((mfs & (mfs_above_is_0 | mfs_above_left_is_0)) != (mfs_above_is_0 | mfs_above_left_is_0)) { /* We aren't in a group anyway, so must force other * pixels. */ mfs_data[1] |= mfs_force_off; mfs_data[2] |= mfs_force_off; force_forward = 1; } else { /* No forcing, but we need to tell other pixels that * we were 0. */ mfs_data[1] |= mfs_above_is_0; mfs_data[2] |= mfs_above_left_is_0; } } else { *outp-- = 1; value -= max_value; } e_forward = value * 7/16; e_downleft = value * 3/16; e_down = value * 5/16; value -= e_forward + e_downleft + e_down; errors[2] += e_downleft; errors[1] += e_down; *errors-- = value; } outp++; } pack_8to1(out_buffer, outp, awidth); } /* CMYK 32 -> 4bit core */ static void down_core4(gx_downscaler_t *ds, byte *out_buffer, byte *in_buffer, int row, int plane /* unused */, int span) { int x, xx, y, value, comp; int e_downleft, e_down, e_forward = 0; int pad_white; byte *inp, *outp; int width = ds->width; int awidth = ds->awidth; int factor = ds->factor; int *errors = ds->errors; const int threshold = factor*factor*128; const int max_value = factor*factor*255; pad_white = (awidth - width) * factor * 4; if (pad_white < 0) pad_white = 0; if (pad_white) { inp = in_buffer + width*factor * 4; for (y = factor; y > 0; y--) { memset(inp, 0xFF, pad_white); inp += span; } } if ((row & 1) == 0) { /* Left to Right pass (no min feature size) */ const int back = span * factor - 4; for (comp = 0; comp < 4; comp++) { errors = ds->errors + (awidth+3)*comp + 2; inp = in_buffer + comp; outp = inp; for (x = awidth; x > 0; x--) { value = e_forward + *errors; for (xx = factor; xx > 0; xx--) { for (y = factor; y > 0; y--) { value += *inp; inp += span; } inp -= back; } if (value >= threshold) { *outp = 1; outp += 4; value -= max_value; } else { *outp = 0; outp += 4; } e_forward = value * 7/16; e_downleft = value * 3/16; e_down = value * 5/16; value -= e_forward + e_downleft + e_down; errors[-2] += e_downleft; errors[-1] += e_down; *errors++ = value; } } outp = in_buffer; } else { /* Right to Left pass (no min feature size) */ const int back = span * factor + 4; for (comp = 0; comp < 4; comp++) { errors = ds->errors + (awidth+3)*comp + awidth; inp = in_buffer + awidth*factor*4 - 4 + comp; outp = inp; for (x = awidth; x > 0; x--) { value = e_forward + *errors; for (xx = factor; xx > 0; xx--) { for (y = factor; y > 0; y--) { value += *inp; inp += span; } inp -= back; } if (value >= threshold) { *outp = 1; outp -= 4; value -= max_value; } else { *outp = 0; outp -= 4; } e_forward = value * 7/16; e_downleft = value * 3/16; e_down = value * 5/16; value -= e_forward + e_downleft + e_down; errors[2] += e_downleft; errors[1] += e_down; *errors-- = value; } } outp = in_buffer + awidth*factor*4 - (awidth*4); } pack_8to1(out_buffer, outp, awidth*4); } static void down_core4_ht(gx_downscaler_t *ds, byte *out_buffer, /* Guaranteed aligned */ byte *in_buffer, /* Not guaranteed aligned */ int row, int plane /* unused */, int span) { int pad_white, y; int factor = ds->factor; int i; int nc = ds->early_cm ? ds->post_cm_num_comps : ds->num_comps; byte *downscaled_data = ds->inbuf; pad_white = (ds->awidth - ds->width) * factor * 4; if (pad_white < 0) pad_white = 0; if (pad_white) { unsigned char *inp = in_buffer + ds->width * factor * 4; for (y = factor; y > 0; y--) { memset(inp, 0xFF, pad_white); inp += span; } } /* Color conversion has already happened. Do any downscale required. */ if (ds->ets_downscale) ds->ets_downscale(ds, downscaled_data, in_buffer, row, plane, span); else if ((31 & (intptr_t)in_buffer) == 0) downscaled_data = in_buffer; /* Already aligned! Yay! */ else memcpy(downscaled_data, in_buffer, (size_t)nc*ds->width); /* Copy to align */ /* Do the halftone */ for (i = 0; i < nc; i++) { /* Make the expanded threshold row */ byte *d = ds->htrow + i; int len = ds->width; const byte *srow = ds->ht[i].data + ds->ht[i].stride * ((row + ds->ht[i].y_phase) % ds->ht[i].h); { int o = ds->ht[i].x_phase; int run = ds->ht[i].w - o; const byte *s = &srow[o]; if (run > len) run = len; len -= run; do { *d = *s++; d += nc; } while (--run); } while (len) { const byte *s = srow; int run = ds->ht[i].w; if (run > len) run = len; len -= run; do { *d = *s++; d += nc; } while (--run); } } /* Do the halftone */ gx_ht_threshold_row_bit_sub(downscaled_data, ds->htrow, 0, out_buffer, 0, ds->width * nc, 1, 0); } static void down_core4_ets(gx_downscaler_t *ds, byte *out_buffer, byte *in_buffer, int row, int plane /* unused */, int span) { unsigned char *dest[MAX_ETS_PLANES]; ETS_SrcPixel *src[MAX_ETS_PLANES]; int pad_white, y; int factor = ds->factor; pad_white = (ds->awidth - ds->width) * factor * 4; if (pad_white < 0) pad_white = 0; if (pad_white) { unsigned char *inp = in_buffer + ds->width * factor * 4; for (y = factor; y > 0; y--) { memset(inp, 0xFF, pad_white); inp += span; } } if (ds->ets_downscale) ds->ets_downscale(ds, in_buffer, in_buffer, row, plane, span); src[0] = in_buffer+3; dest[0] = in_buffer+3; src[1] = in_buffer+1; dest[1] = in_buffer+1; src[2] = in_buffer+0; dest[2] = in_buffer+0; src[3] = in_buffer+2; dest[3] = in_buffer+2; ets_line((ETS_Ctx *)ds->ets_config, dest, (const ETS_SrcPixel * const *)src); pack_8to1(out_buffer, in_buffer, ds->awidth * 4); } static void down_core4_mfs(gx_downscaler_t *ds, byte *out_buffer, byte *in_buffer, int row, int plane /* unused */, int span) { int x, xx, y, value, comp; int e_downleft, e_down, e_forward = 0; int pad_white; byte *inp, *outp; int width = ds->width; int awidth = ds->awidth; int factor = ds->factor; int *errors; const int threshold = factor*factor*128; const int max_value = factor*factor*255; byte *mfs_data; pad_white = (awidth - width) * factor * 4; if (pad_white < 0) pad_white = 0; if (pad_white) { inp = in_buffer + width*factor*4; for (y = factor*4; y > 0; y--) { memset(inp, 0xFF, pad_white); inp += span; } } if ((row & 1) == 0) { /* Left to Right pass (with min feature size = 2) */ const int back = span * factor - 4; for (comp = 0; comp < 4; comp++) { byte mfs, force_forward = 0; errors = ds->errors + (awidth+3)*comp + 2; inp = in_buffer + comp; outp = inp; mfs_data = ds->mfs_data + (awidth+1)*comp; *mfs_data++ = mfs_clear; for (x = awidth; x > 0; x--) { value = e_forward + *errors; for (xx = factor; xx > 0; xx--) { for (y = factor; y > 0; y--) { value += *inp; inp += span; } inp -= back; } mfs = *mfs_data; *mfs_data++ = mfs_clear; if ((mfs & mfs_force_off) || force_forward) { /* We are being forced to be 0 */ *outp = 1; outp += 4; value -= max_value; force_forward = 0; } else if (value >= threshold) { /* We want to be 1 anyway */ *outp = 1; outp += 4; value -= max_value; if ((mfs & (mfs_above_is_0 | mfs_above_left_is_0)) != (mfs_above_is_0 | mfs_above_left_is_0)) { /* We aren't in a group anyway, so must force other * pixels. */ mfs_data[-2] |= mfs_force_off; mfs_data[-1] |= mfs_force_off; force_forward = 1; } else { /* No forcing, but we need to tell other pixels that * we were 1. */ mfs_data[-2] |= mfs_above_is_0; mfs_data[-1] |= mfs_above_left_is_0; } } else { *outp = 0; outp += 4; } e_forward = value * 7/16; e_downleft = value * 3/16; e_down = value * 5/16; value -= e_forward + e_downleft + e_down; errors[-2] += e_downleft; errors[-1] += e_down; *errors++ = value; } } outp = in_buffer; } else { /* Right to Left pass (with min feature size = 2) */ const int back = span * factor + 4; for (comp = 0; comp < 4; comp++) { byte mfs, force_forward = 0; errors = ds->errors + (awidth+3)*comp + awidth; inp = in_buffer + awidth*factor*4 - 4 + comp; outp = inp; mfs_data = ds->mfs_data + (awidth+1)*comp + awidth; *mfs_data-- = mfs_clear; for (x = awidth; x > 0; x--) { value = e_forward + *errors; for (xx = factor; xx > 0; xx--) { for (y = factor; y > 0; y--) { value += *inp; inp += span; } inp -= back; } mfs = *mfs_data; *mfs_data-- = mfs_clear; if ((mfs & mfs_force_off) || force_forward) { /* We are being forced to be 0 */ *outp = 1; outp -= 4; value -= max_value; force_forward = 0; } else if (value >= threshold) { *outp = 1; outp -= 4; value -= max_value; if ((mfs & (mfs_above_is_0 | mfs_above_left_is_0)) != (mfs_above_is_0 | mfs_above_left_is_0)) { /* We aren't in a group anyway, so must force other * pixels. */ mfs_data[1] |= mfs_force_off; mfs_data[2] |= mfs_force_off; force_forward = 1; } else { /* No forcing, but we need to tell other pixels that * we were 1. */ mfs_data[1] |= mfs_above_is_0; mfs_data[2] |= mfs_above_left_is_0; } } else { *outp = 0; outp -= 4; } e_forward = value * 7/16; e_downleft = value * 3/16; e_down = value * 5/16; value -= e_forward + e_downleft + e_down; errors[2] += e_downleft; errors[1] += e_down; *errors-- = value; } } outp = in_buffer + awidth*factor*4 - (awidth*4); } pack_8to1(out_buffer, outp, awidth*4); } /* Grey (or planar) downscale code */ static void down_core16(gx_downscaler_t *ds, byte *outp, byte *in_buffer, int row, int plane, int span) { int x, xx, y, value; int pad_white; byte *inp; int width = ds->width; int awidth = ds->awidth; int factor = ds->factor; int div = factor*factor; pad_white = (awidth - width) * factor; if (pad_white < 0) pad_white = 0; if (pad_white) { inp = in_buffer + width*2*factor; for (y = factor; y > 0; y--) { memset(inp, 0xFF, pad_white*2); inp += span; } } inp = in_buffer; { /* Left to Right pass (no min feature size) */ const int back = span * factor -2; for (x = awidth; x > 0; x--) { value = 0; for (xx = factor; xx > 0; xx--) { for (y = factor; y > 0; y--) { value += inp[0]<<8; value += inp[1]; inp += span; } inp -= back; } value = (value + (div>>1))/div; outp[0] = value>>8; outp[1] = value; outp += 2; } } } static void down_core8(gx_downscaler_t *ds, byte *outp, byte *in_buffer, int row, int plane, int span) { int x, xx, y, value; int pad_white; byte *inp; int width = ds->width; int awidth = ds->awidth; int factor = ds->factor; int div = factor*factor; pad_white = (awidth - width) * factor; if (pad_white < 0) pad_white = 0; if (pad_white) { inp = in_buffer + width*factor; for (y = factor; y > 0; y--) { memset(inp, 0xFF, pad_white); inp += span; } } inp = in_buffer; { /* Left to Right pass (no min feature size) */ const int back = span * factor -1; for (x = awidth; x > 0; x--) { value = 0; for (xx = factor; xx > 0; xx--) { for (y = factor; y > 0; y--) { value += *inp; inp += span; } inp -= back; } *outp++ = (value+(div>>1))/div; } } } static void down_core8_2(gx_downscaler_t *ds, byte *outp, byte *in_buffer, int row, int plane, int span) { int x; int pad_white; byte *inp; int width = ds->width; int awidth = ds->awidth; pad_white = (awidth - width) * 2; if (pad_white < 0) pad_white = 0; if (pad_white) { inp = in_buffer + width*2; for (x = 2; x > 0; x--) { memset(inp, 0xFF, pad_white); inp += span; } } inp = in_buffer; /* Left to Right pass (no min feature size) */ for (x = awidth; x > 0; x--) { *outp++ = (inp[0] + inp[1] + inp[span] + inp[span+1] + 2)>>2; inp += 2; } } static void down_core8_3(gx_downscaler_t *ds, byte *outp, byte *in_buffer, int row, int plane, int span) { int x; int pad_white; byte *inp; int width = ds->width; int awidth = ds->awidth; pad_white = (awidth - width) * 3; if (pad_white < 0) pad_white = 0; if (pad_white) { inp = in_buffer + width*3; for (x = 3; x > 0; x--) { memset(inp, 0xFF, pad_white); inp += span; } } inp = in_buffer; /* Left to Right pass (no min feature size) */ for (x = awidth; x > 0; x--) { *outp++ = (inp[0 ] + inp[ 1] + inp[ 2] + inp[span ] + inp[span +1] + inp[span +2] + inp[span*2] + inp[span*2+1] + inp[span*2+2] + 4)/9; inp += 3; } } static void down_core8_4(gx_downscaler_t *ds, byte *outp, byte *in_buffer, int row, int plane, int span) { int x; int pad_white; byte *inp; int width = ds->width; int awidth = ds->awidth; pad_white = (awidth - width) * 4; if (pad_white < 0) pad_white = 0; if (pad_white) { inp = in_buffer + width*4; for (x = 4; x > 0; x--) { memset(inp, 0xFF, pad_white); inp += span; } } inp = in_buffer; /* Left to Right pass (no min feature size) */ for (x = awidth; x > 0; x--) { *outp++ = (inp[0 ] + inp[ 1] + inp[ 2] + inp[ 3] + inp[span ] + inp[span +1] + inp[span +2] + inp[span +3] + inp[span*2] + inp[span*2+1] + inp[span*2+2] + inp[span*2+3] + inp[span*3] + inp[span*3+1] + inp[span*3+2] + inp[span*3+3] + 8)>>4; inp += 4; } } static void down_core8_3_2(gx_downscaler_t *ds, byte *outp, byte *in_buffer, int row, int plane, int span) { int x; int pad_white; byte *inp; int width = ds->width; int awidth = ds->awidth; int dspan = ds->scaled_span; pad_white = (awidth - width) * 3 / 2; if (pad_white < 0) pad_white = 0; if (pad_white) { inp = in_buffer + width*3/2; for (x = 2; x > 0; x--) { memset(inp, 0xFF, pad_white); inp += span; } } inp = in_buffer; /* Left to Right pass (no min feature size) */ for (x = awidth/2; x > 0; x--) { int a = inp[ 0]; int b = inp[ 1]; int c = inp[ 2]; int d = inp[ span+0]; int e = inp[ span+1]; int f = inp[ span+2]; int g = inp[2*span+0]; int h = inp[2*span+1]; int i = inp[2*span+2]; outp[0 ] = (4*a+2*b+2*d+e+4)/9; outp[1 ] = (4*c+2*b+2*f+e+4)/9; outp[dspan+0] = (4*g+2*h+2*d+e+4)/9; outp[dspan+1] = (4*i+2*h+2*f+e+4)/9; outp += 2; inp += 3; } } static void down_core8_3_4(gx_downscaler_t *ds, byte *outp, byte *in_buffer, int row, int plane, int span) { int x; int pad_white; byte *inp; int width = ds->width; int awidth = ds->awidth; int dspan = ds->scaled_span; pad_white = (awidth - width) * 3 / 4; if (pad_white < 0) pad_white = 0; if (pad_white) { inp = in_buffer + width*3/4; for (x = 4; x > 0; x--) { memset(inp, 0xFF, pad_white); inp += span; } } inp = in_buffer; /* Left to Right pass (no min feature size) */ for (x = awidth/4; x > 0; x--) { int a = inp[ 0]; int b = inp[ 1]; int c = inp[ 2]; int d = inp[ span+0]; int e = inp[ span+1]; int f = inp[ span+2]; int g = inp[2*span+0]; int h = inp[2*span+1]; int i = inp[2*span+2]; outp[ 0] = a; outp[ 1] = (a+2*b+1)/3; outp[ 2] = (c+2*b+1)/3; outp[ 3] = c; outp[ dspan+0] = (a+2*d+1)/3; outp[ dspan+1] = (a+2*b+2*d+4*e+3)/9; outp[ dspan+2] = (c+2*b+2*f+4*e+3)/9; outp[ dspan+3] = (c+2*f+1)/3; outp[2*dspan+0] = (g+2*d+1)/3; outp[2*dspan+1] = (g+2*h+2*d+4*e+3)/9; outp[2*dspan+2] = (i+2*h+2*f+4*e+3)/9; outp[2*dspan+3] = (i+2*f+1)/3; outp[3*dspan+0] = g; outp[3*dspan+1] = (g+2*h+1)/3; outp[3*dspan+2] = (i+2*h+1)/3; outp[3*dspan+3] = i; outp += 4; inp += 3; } } /* RGB downscale (no error diffusion) code */ static void down_core24(gx_downscaler_t *ds, byte *outp, byte *in_buffer, int row, int plane, int span) { int x, xx, y, value; int pad_white; byte *inp; int width = ds->width; int awidth = ds->awidth; int factor = ds->factor; int div = factor*factor; pad_white = (awidth - width) * factor * 3; if (pad_white < 0) pad_white = 0; if (pad_white) { inp = in_buffer + width*factor*3; for (y = factor; y > 0; y--) { memset(inp, 0xFF, pad_white); inp += span; } } inp = in_buffer; { /* Left to Right pass (no min feature size) */ const int back = span * factor - 3; const int back2 = factor * 3 - 1; for (x = awidth; x > 0; x--) { /* R */ value = 0; for (xx = factor; xx > 0; xx--) { for (y = factor; y > 0; y--) { value += *inp; inp += span; } inp -= back; } inp -= back2; *outp++ = (value+(div>>1))/div; /* G */ value = 0; for (xx = factor; xx > 0; xx--) { for (y = factor; y > 0; y--) { value += *inp; inp += span; } inp -= back; } inp -= back2; *outp++ = (value+(div>>1))/div; /* B */ value = 0; for (xx = factor; xx > 0; xx--) { for (y = factor; y > 0; y--) { value += *inp; inp += span; } inp -= back; } inp -= 2; *outp++ = (value+(div>>1))/div; } } } /* CMYK downscale (no error diffusion) code */ static void down_core32(gx_downscaler_t *ds, byte *outp, byte *in_buffer, int row, int plane, int span) { int x, xx, y, value; int pad_white; byte *inp; int width = ds->width; int awidth = ds->awidth; int factor = ds->factor; int div = factor*factor; pad_white = (awidth - width) * factor * 4; if (pad_white < 0) pad_white = 0; if (pad_white) { inp = in_buffer + width*factor*4; for (y = factor; y > 0; y--) { memset(inp, 0xFF, pad_white); inp += span; } } inp = in_buffer; { /* Left to Right pass (no min feature size) */ const int back = span * factor - 4; const int back2 = factor * 4 - 1; for (x = awidth; x > 0; x--) { /* C */ value = 0; for (xx = factor; xx > 0; xx--) { for (y = factor; y > 0; y--) { value += *inp; inp += span; } inp -= back; } inp -= back2; *outp++ = (value+(div>>1))/div; /* M */ value = 0; for (xx = factor; xx > 0; xx--) { for (y = factor; y > 0; y--) { value += *inp; inp += span; } inp -= back; } inp -= back2; *outp++ = (value+(div>>1))/div; /* Y */ value = 0; for (xx = factor; xx > 0; xx--) { for (y = factor; y > 0; y--) { value += *inp; inp += span; } inp -= back; } inp -= back2; *outp++ = (value+(div>>1))/div; /* K */ value = 0; for (xx = factor; xx > 0; xx--) { for (y = factor; y > 0; y--) { value += *inp; inp += span; } inp -= back; } inp -= 3; *outp++ = (value+(div>>1))/div; } } } void gx_downscaler_decode_factor(int factor, int *up, int *down) { if (factor == 32) *down = 3, *up = 2; else if (factor == 34) *down = 3, *up = 4; else *down = factor, *up = 1; } int gx_downscaler_scale(int width, int factor) { int up, down; gx_downscaler_decode_factor(factor, &up, &down); return (width*up)/down; } int gx_downscaler_adjust_bandheight(int factor, int band_height) { int up, down; gx_downscaler_decode_factor(factor, &up, &down); return (band_height/down)*down; } int gx_downscaler_scale_rounded(int width, int factor) { int up, down; gx_downscaler_decode_factor(factor, &up, &down); return (width*up + down-1)/down; } typedef struct { gx_downscale_liner base; ClapTrap *claptrap; int y; int width; int height; int num_comps; gs_get_bits_params_t *params; gx_downscale_liner *chain; } liner_claptrap_planar; static int claptrap_planar_line(gx_downscale_liner *liner_, void *buffer, int row) { liner_claptrap_planar *liner = (liner_claptrap_planar *)liner_; gs_get_bits_params_t *params = (gs_get_bits_params_t *)buffer; liner->params = params; return ClapTrap_GetLinePlanar(liner->claptrap, params->data); } static void claptrap_planar_drop(gx_downscale_liner *liner_, gs_memory_t *mem) { liner_claptrap_planar *liner = (liner_claptrap_planar *)liner_; gx_downscale_liner *next; if (!liner) return; ClapTrap_Fin(mem, liner->claptrap); next = liner->chain; gs_free_object(mem, liner, "liner_claptrap_planar"); if (next) next->drop(next, mem); } static int get_planar_line_for_trap(void *arg, unsigned char *buf) { liner_claptrap_planar *ct = (liner_claptrap_planar *)arg; gs_get_bits_params_t params; int nc = ct->num_comps; int i, code; unsigned char *buf2; params = *ct->params; buf2 = buf; for (i = 0; i < nc; i++) { params.data[i] = buf2; buf2 += ct->width; } code = ct->chain->get_line(ct->chain, ¶ms, ct->y++); /* Allow for devices (like psdcmyk) that make several passes through * the image. */ if (ct->y == ct->height) ct->y = 0; return code; } static int check_trapping(gs_memory_t *memory, int trap_w, int trap_h, int num_comps, const int *comp_order) { if (trap_w < 0 || trap_h < 0) { dmprintf(memory, "Trapping range must be >= 0"); return_error(gs_error_rangecheck); } if (trap_w > 0 || trap_h > 0) { if (comp_order == NULL) { emprintf(memory, "Trapping cannot be used without comp_order being defined"); return_error(gs_error_rangecheck); } /* Check that the comp_order we have been passed is sane */ { char comps[GS_CLIENT_COLOR_MAX_COMPONENTS] = { 0 }; int i; for (i = 0; i < num_comps; i++) { int n = comp_order[i]; if (n < 0 || n >= num_comps || comps[n] != 0) break; comps[n] = 1; } if (i != num_comps) { emprintf(memory, "Illegal component order passed to trapping"); return_error(gs_error_rangecheck); } } } return 0; } static void find_aspect_ratio(float *res, int *a, int *b) { float xres = res[0]; float yres = res[1]; float f; if (xres == yres) { *a = *b = 1; return; } else if (xres > yres) { xres /= yres; f = xres - (int)xres; if (f >= 0.2 && f < 0.3) xres *= 4, yres = 4; else if (f >= 0.3 && f < 0.4) xres *= 3, yres = 3; else if (f >= 0.4 && f < 0.6) xres *= 2, yres = 2; else if (f >= 0.6 && f < 0.7) xres *= 3, yres = 3; else if (f >= 0.7 && f < 0.8) xres *= 4, yres = 4; else yres = 1; *a = (int)(xres + 0.5); *b = (int)yres; } else { yres /= xres; f = yres - (int)yres; if (f >= 0.2 && f < 0.3) yres *= 4, xres = 4; else if (f >= 0.3 && f < 0.4) yres *= 3, xres = 3; else if (f >= 0.4 && f < 0.6) yres *= 2, xres = 2; else if (f >= 0.6 && f < 0.7) yres *= 3, xres = 3; else if (f >= 0.7 && f < 0.8) yres *= 4, xres = 4; else xres = 1; *a = (int)xres; *b = (int)(yres + 0.5); } } static int init_ets(gx_downscaler_t *ds, int num_planes, gx_downscale_core *downscale_core) { ETS_Params params = { 0 }; int strengths[MAX_ETS_PLANES] = { 128, 51, 51, 13, 13, 13, 13, 13 }; int lut[ETS_SRC_MAX+1]; int *luts[MAX_ETS_PLANES]; int rs_lut[ETS_SRC_MAX+1]; int *rs_luts[MAX_ETS_PLANES]; int i; int c1_scale[MAX_ETS_PLANES] = { 1, 1, 1, 1, 1, 1, 1, 1 }; ETS_POLARITY polarity = ETS_BLACK_IS_ONE; polarity = ETS_BLACK_IS_ONE; if (num_planes > MAX_ETS_PLANES) return gs_error_rangecheck; ds->ets_downscale = downscale_core; /* Setup a simple gamma scale */ { double scale = ETS_SRC_MAX; for (i = 0; i < (ETS_SRC_MAX+1); i++) lut[i] = (int)((1 << 24) * (pow (i / scale, 1.0))); } for (i = 0; i < (ETS_SRC_MAX+1); i++) rs_lut[i] = 2 << 16; for (i = 0; i < num_planes; i++) luts[i] = lut; for (i = 0; i < num_planes; i++) rs_luts[i] = rs_lut; #ifdef WITH_CAL params.context = ds->dev->memory->gs_lib_ctx->core->cal_ctx; #endif params.width = ds->width; params.n_planes = num_planes; params.levels = 2; params.luts = luts; params.distscale = 0; find_aspect_ratio(ds->dev->HWResolution, ¶ms.aspect_x, ¶ms.aspect_y); params.strengths = strengths; params.rand_scale = 0; params.c1_scale = c1_scale; params.ets_bias = ETS_BIAS_REDUCE_POSITIVE; params.r_style = ETS_RSTYLE_THRESHOLD; params.dump_file = NULL; params.dump_level = 0; params.rand_scale_luts = rs_luts; params.polarity = polarity; ds->ets_config = ets_create(ds->dev->memory, ¶ms); if (ds->ets_config == NULL) return gs_note_error(gs_error_VMerror); return 0; } static int init_ht(gx_downscaler_t *ds, int num_planes, gx_downscale_core *downscale_core) { int nc = ds->early_cm ? ds->post_cm_num_comps : ds->num_comps; ds->ets_downscale = downscale_core; /* Allocate us a row (with padding for alignment) so we can hold the * expanded threshold array. */ ds->htrow_alloc = gs_alloc_bytes(ds->dev->memory, ds->width * nc + 64, "gx_downscaler(htrow)"); if (ds->htrow_alloc == NULL) return gs_error_VMerror; /* Make an aligned version */ ds->htrow = ds->htrow_alloc + ((32-(intptr_t)ds->htrow_alloc) & 31); /* Allocate us a row (with padding for alignment) for the downscaled data. */ ds->inbuf_alloc = gs_alloc_bytes(ds->dev->memory, ds->width * nc + 64, "gx_downscaler(inbuf)"); if (ds->inbuf_alloc == NULL) { gs_free_object(ds->dev->memory, ds->htrow_alloc, "gx_downscaler(htrow)"); ds->htrow_alloc = ds->htrow = NULL; return gs_error_VMerror; } /* Make an aligned version */ ds->inbuf = ds->inbuf_alloc + ((32-(intptr_t)ds->inbuf_alloc) & 31); return 0; } int gx_downscaler_init_planar(gx_downscaler_t *ds, gx_device *dev, int src_bpc, int dst_bpc, int num_comps, const gx_downscaler_params *params, const gs_get_bits_params_t *gb_params) { return gx_downscaler_init_planar_cm(ds, dev, src_bpc, dst_bpc, num_comps, params, gb_params, NULL, NULL, num_comps); } typedef struct { gx_downscale_liner base; gx_device *dev; } liner_getbits_chunky; static int getbits_chunky_line(gx_downscale_liner *liner_, void *buffer, int row) { liner_getbits_chunky *liner = (liner_getbits_chunky *)liner_; gs_int_rect rect; gs_get_bits_params_t params; rect.p.x = 0; rect.p.y = row; rect.q.x = liner->dev->width; rect.q.y = row+1; params.x_offset = 0; params.raster = bitmap_raster(liner->dev->width * liner->dev->color_info.depth); params.options = (GB_ALIGN_ANY | GB_RETURN_COPY | GB_OFFSET_0 | GB_RASTER_STANDARD | GB_PACKING_CHUNKY | GB_COLORS_NATIVE | GB_ALPHA_NONE); params.data[0] = buffer; return (*dev_proc(liner->dev, get_bits_rectangle))(liner->dev, &rect, ¶ms); } static void getbits_chunky_drop(gx_downscale_liner *liner_, gs_memory_t *mem) { liner_getbits_chunky *liner = (liner_getbits_chunky *)liner_; gs_free_object(mem, liner, "liner_getbits_chunky"); } typedef struct { gx_downscale_liner base; gx_device *dev; int raster; int num_comps; } liner_getbits_planar; static int getbits_planar_line(gx_downscale_liner *liner_, void *output, int row) { liner_getbits_planar *liner = (liner_getbits_planar *)liner_; gs_get_bits_params_t *params = (gs_get_bits_params_t *)output; gs_get_bits_params_t params2; gs_int_rect rect; int i, code, n; rect.p.x = 0; rect.p.y = row; rect.q.x = liner->dev->width; rect.q.y = row+1; n = liner->dev->width; if (liner->dev->color_info.depth > liner->dev->color_info.num_components * 8 + 8) n *= 2; params2 = *params; code = (*dev_proc(liner->dev, get_bits_rectangle))(liner->dev, &rect, ¶ms2); /* If our caller can't accept a pointer, we need to do some work. */ if (params->options & GB_RETURN_POINTER) { for (i = 0; i < liner->num_comps; i++) params->data[i] = params2.data[i]; } else { /* get_bits_rectangle doesn't like doing planar copies, only return * pointers. This is a problem for us, so fudge it here. */ for (i = 0; i < liner->num_comps; i++) if (params->data[i] != params2.data[i]) memcpy(params->data[i], params2.data[i], n); } return code; } static void getbits_planar_drop(gx_downscale_liner *liner_, gs_memory_t *mem) { liner_getbits_planar *liner = (liner_getbits_planar *)liner_; gs_free_object(mem, liner, "liner_getbits_planar"); } typedef struct { gx_downscale_liner base; ClapTrap *claptrap; int y; int height; gx_downscale_liner *chain; } liner_claptrap; static int claptrap_line(gx_downscale_liner *liner_, void *buffer, int row) { liner_claptrap *liner = (liner_claptrap *)liner_; return ClapTrap_GetLine(liner->claptrap, buffer); } static void claptrap_drop(gx_downscale_liner *liner_, gs_memory_t *mem) { liner_claptrap *liner = (liner_claptrap *)liner_; gx_downscale_liner *next; if (!liner) return; ClapTrap_Fin(mem, liner->claptrap); next = liner->chain; gs_free_object(mem, liner, "liner_claptrap"); if (next) next->drop(next, mem); } #ifdef WITH_CAL static unsigned char bg0[GX_DEVICE_COLOR_MAX_COMPONENTS] = {0}; static unsigned char bg1[GX_DEVICE_COLOR_MAX_COMPONENTS] = { 0xFF, 0xFF, 0xFF, 0xFF }; typedef struct { gx_downscale_liner base; cal_deskewer *deskewer[GX_DEVICE_COLOR_MAX_COMPONENTS]; cal_deskewer_bander *bander[GX_DEVICE_COLOR_MAX_COMPONENTS]; int height; int get_row; int got_row; int num_planes; gx_downscale_liner *chain; } liner_skew; static int skew_line(gx_downscale_liner *liner_, void *buffer, int row) { liner_skew *liner = (liner_skew *)liner_; int code; if (row < liner->got_row) liner->get_row = 0; liner->got_row = row; while (1) { code = cal_deskewer_band_pull(liner->bander[0], buffer); if (code == 1) return 0; /* We got a line! */ code = liner->chain->get_line(liner->chain, buffer, liner->get_row++); if (code < 0) return code; code = cal_deskewer_band_push(liner->bander[0], buffer); if (code < 0) return code; } } static void skew_drop(gx_downscale_liner *liner_, gs_memory_t *mem) { liner_skew *liner = (liner_skew *)liner_; gx_downscale_liner *next; int i; if (!liner) return; for (i = 0; i < liner->num_planes; i++) { cal_deskewer_band_end(liner->bander[i], mem); cal_deskewer_fin(liner->deskewer[i], mem); } next = liner->chain; gs_free_object(mem, liner, "liner_skew"); if (next) next->drop(next, mem); } static int planar_skew_line(gx_downscale_liner *liner_, void *params_, int row) { liner_skew *liner = (liner_skew *)liner_; int code = 0; gs_get_bits_params_t *params = (gs_get_bits_params_t *)params_; int i; if (row < liner->got_row) liner->get_row = 0; liner->got_row = row; while (1) { for (i = 0; i < liner->num_planes; i++) { code = cal_deskewer_band_pull(liner->bander[i], params->data[i]); if (code < 0) return code; } if (code == 1) return 0; /* We got a line! */ code = liner->chain->get_line(liner->chain, params, liner->get_row++); if (code < 0) return code; for (i = 0; i < liner->num_planes; i++) { code = cal_deskewer_band_push(liner->bander[i], params->data[i]); if (code < 0) return code; } } } static void planar_skew_drop(gx_downscale_liner *liner_, gs_memory_t *mem) { liner_skew *liner = (liner_skew *)liner_; gx_downscale_liner *next; int i; if (!liner) return; for (i = 0; i < liner->num_planes; i++) { cal_deskewer_band_end(liner->bander[i], mem); cal_deskewer_fin(liner->deskewer[i], mem); } next = liner->chain; gs_free_object(mem, liner, "liner_skew"); if (next) next->drop(next, mem); } #endif #define alloc_liner(mem, type, get, drop, res) \ do_alloc_liner(mem, sizeof(type), #type, get, drop,\ (gx_downscale_liner **)res) static int do_alloc_liner(gs_memory_t *mem, size_t size, const char *type, int (*get_line)(gx_downscale_liner *, void *, int), void (*drop)(gx_downscale_liner *, gs_memory_t *), gx_downscale_liner **res) { gx_downscale_liner *liner; liner = (gx_downscale_liner *)gs_alloc_bytes(mem, size, type); *res = liner; if (liner == NULL) return_error(gs_error_VMerror); liner->get_line = get_line; liner->drop = drop; return 0; } int gx_downscaler_init_planar_cm(gx_downscaler_t *ds, gx_device *dev, int src_bpc, int dst_bpc, int num_comps, const gx_downscaler_params *params, const gs_get_bits_params_t *gb_params, gx_downscale_cm_fn *apply_cm, void *apply_cm_arg, int post_cm_num_comps) { int span = bitmap_raster(dev->width * src_bpc); int post_span = bitmap_raster(dev->width * src_bpc); int width; int code; gx_downscale_core *core; int i; int upfactor, downfactor; int factor = params->downscale_factor; int mfs = params->min_feature_size; gx_downscaler_decode_factor(factor, &upfactor, &downfactor); /* width = scaled width */ width = (dev->width*upfactor)/downfactor; memset(ds, 0, sizeof(*ds)); ds->dev = dev; ds->width = width; ds->awidth = width; ds->span = span; ds->factor = factor; ds->num_planes = num_comps; ds->src_bpc = src_bpc; ds->dst_bpc = dst_bpc; ds->scaled_data = NULL; ds->scaled_span = bitmap_raster((dst_bpc*dev->width*upfactor + downfactor-1)/downfactor); ds->apply_cm = apply_cm; ds->apply_cm_arg = apply_cm_arg; ds->early_cm = dst_bpc < src_bpc || (dst_bpc == src_bpc && post_cm_num_comps < num_comps); ds->post_cm_num_comps = post_cm_num_comps; ds->do_skew_detection = params->do_skew_detection; if (apply_cm) { ds->post_cm[0] = gs_alloc_bytes(dev->memory, (size_t)post_span * downfactor * post_cm_num_comps, "gx_downscaler(planar_data)"); if (ds->post_cm[0] == NULL) { code = gs_note_error(gs_error_VMerror); goto cleanup; } for (i = 1; i < post_cm_num_comps; i++) { ds->post_cm[i] = ds->post_cm[i-1] + (size_t)post_span * downfactor; } } /* The primary line source for planar comes always from * get_bits_rectangle. */ { liner_getbits_planar *gb_liner; code = alloc_liner(dev->memory, liner_getbits_planar, getbits_planar_line, getbits_planar_drop, &gb_liner); if (code < 0) goto cleanup; gb_liner->dev = dev; gb_liner->num_comps = num_comps; ds->liner = &gb_liner->base; } memcpy(&ds->params, gb_params, sizeof(*gb_params)); ds->params.raster = span; ds->pre_cm[0] = gs_alloc_bytes(dev->memory, (size_t)span * downfactor * num_comps, "gx_downscaler(planar_data)"); for (i = 1; i < num_comps; i++) { ds->pre_cm[i] = ds->pre_cm[i-1] + (size_t)span * downfactor; } #ifdef WITH_CAL if (ds->do_skew_detection) { /* Do a skew detection pass */ int j; int w = ds->dev->width; int h = ds->dev->height; cal_skew *skew; for (i = 0; i < num_comps; i++) { ds->params.data[i] = ds->pre_cm[i]; } skew = cal_skew_init(ds->dev->memory->gs_lib_ctx->core->cal_ctx, ds->dev->memory, w, h); if (skew == NULL) code = gs_error_VMerror; for (j = 0; code >= 0 && j < h; j++) { gs_get_bits_params_t params2 = ds->params; code = ds->liner->get_line(ds->liner, ¶ms2, j); /* Craply turn that into "greyscale" - this assumes 8 bit. */ if (num_comps > 1) { int i, k; byte *dst = ds->params.data[0]; for (i = 0; i < w; i++) { int v = 0; for (k = num_comps-1; k > 0; k--) v += ((unsigned char *)params2.data[k])[i]; *dst++ = (v+(num_comps>>1))/num_comps; } } code = cal_skew_process(skew, ds->dev->memory, ds->params.data[0]); } if (code >= 0) { ds->skew_angle = cal_skew_detect(skew, ds->dev->memory); if (ds->skew_angle < -45 || ds->skew_angle > 45) ds->skew_angle = 0; } cal_skew_fin(skew, ds->dev->memory); if (code < 0) goto cleanup; if (ds->skew_angle != 0) { liner_skew *sk_liner; unsigned int dw, dh; code = alloc_liner(dev->memory, liner_skew, planar_skew_line, planar_skew_drop, &sk_liner); if (code < 0) goto cleanup; sk_liner->chain = ds->liner; sk_liner->get_row = 0; sk_liner->got_row = 0; sk_liner->height = dev->height; sk_liner->num_planes = num_comps; ds->liner = &sk_liner->base; for (i = 0; i < num_comps; i++) { sk_liner->deskewer[i] = cal_deskewer_init( ds->dev->memory->gs_lib_ctx->core->cal_ctx, ds->dev->memory, ds->dev->width, ds->dev->height, &dw, &dh, ds->skew_angle, 1, /* Keep the page size constant */ 1.0, 1.0, 1.0, 1.0, bg0, 1); if (sk_liner->deskewer[i] == NULL) { emprintf(dev->memory, "Deskewer initialisation failed"); code = gs_note_error(gs_error_VMerror); goto cleanup; } sk_liner->bander[i] = cal_deskewer_band_begin(sk_liner->deskewer[i], ds->dev->memory, 0, 0); if (sk_liner->bander[i] == NULL) { emprintf(dev->memory, "Deskewer initialisation(2) failed"); code = gs_note_error(gs_error_VMerror); goto cleanup; } } } } #endif code = check_trapping(dev->memory, params->trap_w, params->trap_h, num_comps, params->trap_order); if (code < 0) return code; if (params->trap_w > 0 || params->trap_h > 0) { liner_claptrap_planar *ct_liner; code = alloc_liner(dev->memory, liner_claptrap_planar, claptrap_planar_line, claptrap_planar_drop, &ct_liner); if (code < 0) goto cleanup; ct_liner->chain = ds->liner; ct_liner->y = 0; ct_liner->height = dev->height; ct_liner->num_comps = ds->num_comps; ct_liner->width = dev->width; ds->liner = &ct_liner->base; ct_liner->claptrap = ClapTrap_Init(dev->memory, dev->width, dev->height, num_comps, params->trap_order, params->trap_w, params->trap_h, get_planar_line_for_trap, ct_liner); if (ct_liner->claptrap == NULL) { emprintf(dev->memory, "Trapping initialisation failed"); code = gs_note_error(gs_error_VMerror); goto cleanup; } } if (upfactor > 1) { ds->scaled_data = gs_alloc_bytes(dev->memory, (size_t)ds->scaled_span * upfactor * num_comps, "gx_downscaler(scaled_data)"); if (ds->scaled_data == NULL) { code = gs_note_error(gs_error_VMerror); goto cleanup; } } if ((src_bpc == 8) && (dst_bpc == 8) && (factor == 32)) { core = &down_core8_3_2; } else if ((src_bpc == 8) && (dst_bpc == 8) && (factor == 34)) { core = &down_core8_3_4; } else if (factor > 8) { code = gs_note_error(gs_error_rangecheck); goto cleanup; } else if (dst_bpc == 1) { if (src_bpc == dst_bpc) core = NULL; else if (mfs > 1) core = &down_core_mfs; else if (factor == 4) core = &down_core_4; else if (factor == 3) core = &down_core_3; else if (factor == 2) core = &down_core_2; else if (factor == 1) core = &down_core_1; else core = &down_core; } else if (factor == 1) core = NULL; else if (src_bpc == 16) core = &down_core16; else if (factor == 4) core = &down_core8_4; else if (factor == 3) core = &down_core8_3; else if (factor == 2) core = &down_core8_2; else core = &down_core8; ds->down_core = core; if (mfs > 1) { ds->mfs_data = (byte *)gs_alloc_bytes(dev->memory, (size_t)(width+1) * num_comps, "gx_downscaler(mfs)"); if (ds->mfs_data == NULL) { code = gs_note_error(gs_error_VMerror); goto cleanup; } memset(ds->mfs_data, 0, (size_t)num_comps * (width+1)); } if (dst_bpc == 1) { ds->errors = (int *)gs_alloc_bytes(dev->memory, (size_t)num_comps*(width+3)*sizeof(int), "gx_downscaler(errors)"); if (ds->errors == NULL) { code = gs_note_error(gs_error_VMerror); goto cleanup; } memset(ds->errors, 0, (size_t)num_comps * (width+3) * sizeof(int)); } return 0; cleanup: gx_downscaler_fin(ds); return code; } static int get_line_for_trap(void *arg, unsigned char *buf) { liner_claptrap *ct = (liner_claptrap *)arg; /* Allow for devices (like psdcmyk) that make several passes through * the image. This is a bit crap cos it assumes that we will pass * through strictly from top to bottom (possibly repeatedly). */ if (ct->y == ct->height) ct->y = 0; return ct->chain->get_line(ct->chain, buf, ct->y++); } int gx_downscaler_init(gx_downscaler_t *ds, gx_device *dev, int src_bpc, int dst_bpc, int num_comps, const gx_downscaler_params *params, int (*adjust_width_proc)(int, int), int adjust_width) { return gx_downscaler_init_cm(ds, dev, src_bpc, dst_bpc, num_comps, params, adjust_width_proc, adjust_width, NULL, NULL, 0); } static gx_downscaler_ht_t bogus_ets_halftone; int gx_downscaler_init_cm(gx_downscaler_t *ds, gx_device *dev, int src_bpc, int dst_bpc, int num_comps, const gx_downscaler_params *params, int (*adjust_width_proc)(int, int), int adjust_width, gx_downscale_cm_fn *apply_cm, void *apply_cm_arg, int post_cm_num_comps) { return gx_downscaler_init_cm_halftone(ds, dev, src_bpc, dst_bpc, num_comps, params, adjust_width_proc, adjust_width, apply_cm, apply_cm_arg, post_cm_num_comps, params->ets ? &bogus_ets_halftone : NULL); } static gx_downscale_core * select_8_to_8_core(int nc, int factor) { if (factor == 1) return NULL; /* No sense doing anything */ if (nc == 1) { if (factor == 4) return &down_core8_4; else if (factor == 3) return &down_core8_3; else if (factor == 2) return &down_core8_2; else return &down_core8; } else if (nc == 3) return &down_core24; else if (nc == 4) return &down_core32; return NULL; } int gx_downscaler_init_cm_halftone(gx_downscaler_t *ds, gx_device *dev, int src_bpc, int dst_bpc, int num_comps, const gx_downscaler_params *params, int (*adjust_width_proc)(int, int), int adjust_width, gx_downscale_cm_fn *apply_cm, void *apply_cm_arg, int post_cm_num_comps, gx_downscaler_ht_t *ht) { int size; int post_size; int span; int width; int awidth; int pad_white; int code = 0; gx_downscale_core *core; int upfactor; int downfactor; int nc; int factor = params->downscale_factor; int mfs = params->min_feature_size; size = gdev_mem_bytes_per_scan_line((gx_device *)dev); post_size = bitmap_raster(dev->width * src_bpc * post_cm_num_comps); gx_downscaler_decode_factor(factor, &upfactor, &downfactor); /* width = scaled width */ width = (dev->width * upfactor)/downfactor; awidth = width; if (adjust_width_proc != NULL) awidth = (*adjust_width_proc)(width, adjust_width); pad_white = awidth - width; if (pad_white < 0) pad_white = 0; /* size = unscaled size. span = unscaled size + padding */ span = size + pad_white*downfactor*num_comps/upfactor + downfactor-1; memset(ds, 0, sizeof(*ds)); ds->dev = dev; ds->width = width; ds->awidth = awidth; ds->span = span; ds->factor = factor; ds->num_planes = 0; ds->src_bpc = src_bpc; ds->apply_cm = apply_cm; ds->apply_cm_arg = apply_cm_arg; ds->early_cm = dst_bpc < src_bpc; ds->post_cm_num_comps = post_cm_num_comps; ds->ht = ht; ds->dst_bpc = dst_bpc; ds->num_comps = num_comps; ds->do_skew_detection = params->do_skew_detection; /* The primary line source comes always from getbits. */ { liner_getbits_chunky *gb_liner; code = alloc_liner(dev->memory, liner_getbits_chunky, getbits_chunky_line, getbits_chunky_drop, &gb_liner); if (code < 0) goto cleanup; gb_liner->dev = dev; ds->liner = &gb_liner->base; } #ifdef WITH_CAL if (ds->do_skew_detection) { /* Do a skew detection pass */ int j; int w = ds->dev->width; int h = ds->dev->height; int n = ds->dev->color_info.num_components; cal_skew *skew; byte *buffer = gs_alloc_bytes(ds->dev->memory, w*n, "skew_row"); if (buffer == NULL) return_error(gs_error_VMerror); skew = cal_skew_init(ds->dev->memory->gs_lib_ctx->core->cal_ctx, ds->dev->memory, w, h); if (skew == NULL) code = gs_error_VMerror; for (j = 0; code >= 0 && j < h; j++) { code = ds->liner->get_line(ds->liner, buffer, j); /* Craply turn that into "greyscale" */ if (n > 1) { int i, k; const byte *src = buffer; byte *dst = buffer; for (i = w; i > 0; i--) { int v = 0; for (k = n; k > 0; k--) v += *src++; *dst++ = (v+(n>>1))/n; } } code = cal_skew_process(skew, ds->dev->memory, buffer); } if (code >= 0) ds->skew_angle = cal_skew_detect(skew, ds->dev->memory); gs_free_object(ds->dev->memory, buffer, "skew_row"); cal_skew_fin(skew, ds->dev->memory); if (code < 0) goto cleanup; if (ds->skew_angle != 0) { liner_skew *sk_liner; unsigned int dw, dh; code = alloc_liner(dev->memory, liner_skew, skew_line, skew_drop, &sk_liner); if (code < 0) goto cleanup; sk_liner->chain = ds->liner; sk_liner->get_row = 0; sk_liner->got_row = 0; sk_liner->height = dev->height; sk_liner->num_planes = 1; ds->liner = &sk_liner->base; sk_liner->deskewer[0] = cal_deskewer_init( ds->dev->memory->gs_lib_ctx->core->cal_ctx, ds->dev->memory, ds->dev->width, ds->dev->height, &dw, &dh, ds->skew_angle, 1, /* Keep the page size constant */ 1.0, 1.0, 1.0, 1.0, (ds->num_comps <= 3 ? bg1 : bg0), ds->num_comps); if (sk_liner->deskewer[0] == NULL) { emprintf(dev->memory, "Deskewer initialisation failed"); code = gs_note_error(gs_error_VMerror); goto cleanup; } sk_liner->bander[0] = cal_deskewer_band_begin(sk_liner->deskewer[0], ds->dev->memory, 0, 0); if (sk_liner->bander[0] == NULL) { emprintf(dev->memory, "Deskewer initialisation(2) failed"); code = gs_note_error(gs_error_VMerror); goto cleanup; } } } #endif code = check_trapping(dev->memory, params->trap_w, params->trap_h, num_comps, params->trap_order); if (code < 0) return code; if (params->trap_w > 0 || params->trap_h > 0) { liner_claptrap *ct_liner; code = alloc_liner(dev->memory, liner_claptrap, claptrap_line, claptrap_drop, &ct_liner); if (code < 0) goto cleanup; ct_liner->chain = ds->liner; ct_liner->y = 0; ct_liner->height = dev->height; ds->liner = &ct_liner->base; ct_liner->claptrap = ClapTrap_Init(dev->memory, width, dev->height, num_comps, params->trap_order, params->trap_w, params->trap_h, get_line_for_trap, ct_liner); if (ct_liner->claptrap == NULL) { emprintf(dev->memory, "Trapping initialisation failed"); code = gs_note_error(gs_error_VMerror); goto cleanup; } } /* Choose an appropriate core. Try to honour our early_cm * choice, and fallback to late cm if we can't. */ core = NULL; while (1) { nc = ds->early_cm ? post_cm_num_comps : num_comps; if (factor > 8) { code = gs_note_error(gs_error_rangecheck); goto cleanup; } else if ((src_bpc == 16) && (dst_bpc == 16) && (nc == 1)) { core = &down_core16; } else if ((src_bpc == 8) && (dst_bpc == 1) && (nc == 4)) { if (mfs > 1) core = &down_core4_mfs; else if (ht == &bogus_ets_halftone) { code = init_ets(ds, 4, select_8_to_8_core(nc, factor)); if (code) goto cleanup; core = &down_core4_ets; } else if (ht != NULL) { code = init_ht(ds, 4, select_8_to_8_core(nc, factor)); if (code) goto cleanup; core = &down_core4_ht; } else core = &down_core4; } else if ((src_bpc == 8) && (dst_bpc == 1) && (nc == 1)) { if (mfs > 1) core = &down_core_mfs; else if (ht == &bogus_ets_halftone) { code = init_ets(ds, 1, select_8_to_8_core(nc, factor)); if (code) goto cleanup; core = &down_core_ets_1; } else if (factor == 4) core = &down_core_4; else if (factor == 3) core = &down_core_3; else if (factor == 2) core = &down_core_2; else if (factor == 1) core = &down_core_1; else core = &down_core; } else if ((factor == 1) && (src_bpc == dst_bpc)) break; else if (src_bpc == 8 && dst_bpc == 8) core = select_8_to_8_core(nc, factor); /* If we found one, or we have nothing to fallback to, exit */ if (core || !ds->early_cm) break; /* Fallback */ ds->early_cm = false; } if (factor == 1 && src_bpc == dst_bpc) { /* core can permissibly be NULL */ } else if (core == NULL) { code = gs_note_error(gs_error_rangecheck); goto cleanup; } ds->down_core = core; if (apply_cm) { ds->post_cm[0] = gs_alloc_bytes(dev->memory, (size_t)post_size * downfactor, "gx_downscaler(data)"); if (ds->post_cm[0] == NULL) { code = gs_note_error(gs_error_VMerror); goto cleanup; } } if (core != NULL || apply_cm) { ds->pre_cm[0] = gs_alloc_bytes(dev->memory, (size_t)span * downfactor, "gx_downscaler(data)"); if (ds->pre_cm[0] == NULL) { code = gs_note_error(gs_error_VMerror); goto cleanup; } } if (core != NULL) { if (mfs > 1) { ds->mfs_data = (byte *)gs_alloc_bytes(dev->memory, (size_t)(awidth+1)*nc, "gx_downscaler(mfs)"); if (ds->mfs_data == NULL) { code = gs_note_error(gs_error_VMerror); goto cleanup; } memset(ds->mfs_data, 0, (size_t)nc*(awidth+1)); } if (dst_bpc == 1) { ds->errors = (int *)gs_alloc_bytes(dev->memory, (size_t)nc*(awidth+3)*sizeof(int), "gx_downscaler(errors)"); if (ds->errors == NULL) { code = gs_note_error(gs_error_VMerror); goto cleanup; } memset(ds->errors, 0, (size_t)nc * (awidth+3) * sizeof(int)); } } return 0; cleanup: gx_downscaler_fin(ds); return code; } void gx_downscaler_fin(gx_downscaler_t *ds) { if (ds->dev == NULL) return; gs_free_object(ds->dev->memory, ds->pre_cm[0], "gx_downscaler(planar_data)"); gs_free_object(ds->dev->memory, ds->post_cm[0], "gx_downscaler(planar_data)"); ds->pre_cm[0] = NULL; ds->post_cm[0] = NULL; ds->num_planes = 0; gs_free_object(ds->dev->memory, ds->mfs_data, "gx_downscaler(mfs)"); ds->mfs_data = NULL; gs_free_object(ds->dev->memory, ds->errors, "gx_downscaler(errors)"); ds->errors = NULL; gs_free_object(ds->dev->memory, ds->scaled_data, "gx_downscaler(scaled_data)"); ds->scaled_data = NULL; gs_free_object(ds->dev->memory, ds->htrow_alloc, "gx_downscaler(htrow)"); ds->htrow = NULL; ds->htrow_alloc = NULL; if (ds->liner) ds->liner->drop(ds->liner, ds->dev->memory); ds->liner = NULL; if (ds->ets_config) ets_destroy(ds->dev->memory, ds->ets_config); ds->ets_config = NULL; } /* Chunky case */ int gx_downscaler_getbits(gx_downscaler_t *ds, byte *out_data, int row) { int code = 0; int y, y_end; byte *data_ptr; int upfactor, downfactor; gx_downscaler_decode_factor(ds->factor, &upfactor, &downfactor); /* Check for the simple case */ if (ds->down_core == NULL) { code = ds->liner->get_line(ds->liner, ds->apply_cm ? ds->pre_cm[0] : out_data, row); if (code < 0) return code; if (ds->apply_cm) { data_ptr = out_data; return ds->apply_cm(ds->apply_cm_arg, &data_ptr, ds->pre_cm, ds->width, 1, 0); } return 0; } /* Get factor rows worth of data */ y = row * downfactor; y_end = y + downfactor; data_ptr = ds->pre_cm[0]; do { code = ds->liner->get_line(ds->liner, data_ptr, y); if (code < 0) return code; data_ptr += ds->span; y++; } while (y < y_end); if (ds->apply_cm) { if (ds->early_cm) { code = ds->apply_cm(ds->apply_cm_arg, ds->post_cm, ds->pre_cm, ds->dev->width, 1, 0); if (code < 0) return code; (ds->down_core)(ds, out_data, ds->post_cm[0], row, 0, ds->span); } else { data_ptr = out_data; (ds->down_core)(ds, ds->post_cm[0], ds->pre_cm[0], row, 0, ds->span); code = ds->apply_cm(ds->apply_cm_arg, &out_data, ds->post_cm, ds->width, 1, 0); if (code < 0) return code; } } else (ds->down_core)(ds, out_data, ds->pre_cm[0], row, 0, ds->span); return code; } /* Planar case */ int gx_downscaler_get_bits_rectangle(gx_downscaler_t *ds, gs_get_bits_params_t *params, int row) { int code = 0; gs_int_rect rect; int plane; int factor = ds->factor; gs_get_bits_params_t params2; int upfactor, downfactor; int subrow; int copy = (ds->dev->width * ds->src_bpc + 7)>>3; int i, j, n; int num_planes_to_downscale; n = ds->dev->width; if (ds->dev->color_info.depth > ds->dev->color_info.num_components*8+8) n *= 2; n = (n*ds->src_bpc+7)/8; gx_downscaler_decode_factor(factor, &upfactor, &downfactor); subrow = row % upfactor; if (subrow) { /* Just copy a previous row from our stored buffer */ for (plane=0; plane < ds->num_planes; plane++) params->data[plane] = ds->scaled_data + (upfactor * plane + subrow) * ds->scaled_span; return 0; } rect.p.x = 0; rect.p.y = (row/upfactor) * downfactor; rect.q.x = ds->dev->width; rect.q.y = ((row/upfactor) + 1) * downfactor; /* Check for the simple case */ if (ds->down_core == NULL) { gs_get_bits_params_t saved; if (ds->apply_cm) { /* Always do the request giving our own workspace, * and be prepared to accept a pointer */ saved = *params; for (i = 0; i < ds->num_planes; i++) params->data[i] = ds->pre_cm[i]; params->options |= GB_RETURN_POINTER; } code = ds->liner->get_line(ds->liner, params, row); if (code < 0) return code; if (ds->apply_cm) { byte **buffer; if (saved.options & GB_RETURN_COPY) { /* They will accept a copy. Let's use the buffer they supplied */ params->options &= ~GB_RETURN_POINTER; buffer = saved.data; } else buffer = ds->post_cm; code = ds->apply_cm(ds->apply_cm_arg, buffer, params->data, ds->dev->width, rect.q.y - rect.p.y, params->raster); for (i = 0; i < ds->post_cm_num_comps; i++) params->data[i] = buffer[i]; } return code; } /* Copy the params, because get_bits_rectangle can helpfully overwrite * them. */ memcpy(¶ms2, &ds->params, sizeof(params2)); for (i = 0; i < ds->num_planes; i++) params2.data[i] = ds->pre_cm[i]; /* Get downfactor rows worth of data - we always work a line at a * time now. */ for (i = 0; i < downfactor; i++) { rect.q.y = rect.p.y+1; if (rect.q.y > ds->dev->height) break; memcpy(¶ms2, &ds->params, sizeof(params2)); for (j = 0; j < ds->num_planes; j++) params2.data[j] = ds->pre_cm[j] + i * ds->span; code = ds->liner->get_line(ds->liner, ¶ms2, rect.p.y); if (code < 0) break; for (j = 0; j < ds->num_planes; j++) { byte *tgt = ds->pre_cm[j] + i * ds->span; if (params2.data[j] != tgt) memcpy(tgt, params2.data[j], n); } rect.p.y++; } if (i == 0) return code; if (code < 0) return code; /* If we still haven't got enough, we've hit the end of the page; just * duplicate the last line we did get. */ for (; i < downfactor; i++) for (j = 0; j < ds->num_planes; j++) memcpy(ds->pre_cm[j] + i*ds->span, ds->pre_cm[j] + (i-1)*ds->span, copy); /* All the data is now in ds->pre_cm. Update params2.data so that this points to * it. From here on in, we will keep params2.data pointing to whereever the * latest processed version of the data is. */ for (j = 0; j < ds->num_planes; j++) params2.data[j] = ds->pre_cm[j]; num_planes_to_downscale = ds->num_planes; if (ds->early_cm && ds->apply_cm) { code = ds->apply_cm(ds->apply_cm_arg, ds->post_cm, params2.data, ds->dev->width, downfactor, ds->span); if (code < 0) return code; for (j = 0; j < ds->post_cm_num_comps; j++) params2.data[j] = ds->post_cm[j]; num_planes_to_downscale = ds->post_cm_num_comps; } if (upfactor > 1) { /* Downscale the block of lines into our output buffer */ for (plane=0; plane < num_planes_to_downscale; plane++) { byte *scaled = ds->scaled_data + upfactor * plane * ds->scaled_span; (ds->down_core)(ds, scaled, params2.data[plane], row, plane, params2.raster); params2.data[plane] = scaled; } } else if (ds->down_core != NULL) { /* Downscale direct into output buffer */ for (plane=0; plane < num_planes_to_downscale; plane++) { (ds->down_core)(ds, params->data[plane], params2.data[plane], row, plane, params2.raster); params2.data[plane] = params->data[plane]; } } else { /* Copy into output buffer */ /* No color management can be required here */ assert(!ds->early_cm || ds->apply_cm == NULL); for (plane=0; plane < num_planes_to_downscale; plane++) { memcpy(params->data[plane], params2.data[plane], params2.raster); params2.data[plane] = params->data[plane]; } } if (!ds->early_cm && ds->apply_cm) { code = ds->apply_cm(ds->apply_cm_arg, params->data, params2.data, ds->width, 1, params->raster); if (code < 0) return code; for (plane=0; plane < num_planes_to_downscale; plane++) params2.data[plane] = params->data[plane]; } for (plane=0; plane < num_planes_to_downscale; plane++) params->data[plane] = params2.data[plane]; return code; } typedef struct downscaler_process_page_arg_s { gx_process_page_options_t *orig_options; int upfactor; int downfactor; gx_downscaler_t ds; } downscaler_process_page_arg_t; typedef struct downscaler_process_page_buffer_s { gx_device *bdev; void *orig_buffer; } downscaler_process_page_buffer_t; static int downscaler_init_fn(void *arg_, gx_device *dev, gs_memory_t *memory, int w, int h, void **pbuffer) { downscaler_process_page_arg_t *arg = (downscaler_process_page_arg_t *)arg_; downscaler_process_page_buffer_t *buffer; int code = 0; buffer = (downscaler_process_page_buffer_t *)gs_alloc_bytes(memory, sizeof(*buffer), "downscaler process_page buffer"); if (buffer == NULL) return_error(gs_error_VMerror); memset(buffer, 0, sizeof(*buffer)); if (arg->upfactor > arg->downfactor) { code = gx_default_create_buf_device(&buffer->bdev, dev, (h*arg->upfactor + arg->downfactor-1)/arg->downfactor, NULL, memory, NULL); if (code < 0) { gs_free_object(memory, buffer, "downscaler process_page buffer"); return code; } } if (arg->orig_options && arg->orig_options->init_buffer_fn) { code = arg->orig_options->init_buffer_fn(arg->orig_options->arg, dev, memory, (w * arg->upfactor + arg->downfactor-1)/arg->downfactor, (h * arg->upfactor + arg->downfactor-1)/arg->downfactor, &buffer->orig_buffer); if (code < 0) { if (buffer->bdev) dev_proc(dev, close_device)(dev); gs_free_object(memory, buffer, "downscaler process_page buffer"); return code; } } *pbuffer = (void *)buffer; return code; } static int downscaler_process_fn(void *arg_, gx_device *dev, gx_device *bdev, const gs_int_rect *rect, void *buffer_) { downscaler_process_page_arg_t *arg = (downscaler_process_page_arg_t *)arg_; downscaler_process_page_buffer_t *buffer = (downscaler_process_page_buffer_t *)buffer_; int code, raster_in, raster_out; gs_get_bits_params_t params; gs_int_rect in_rect, out_rect; byte *in_ptr, *out_ptr; in_rect.p.x = 0; in_rect.p.y = 0; in_rect.q.x = rect->q.x - rect->p.x; in_rect.q.y = rect->q.y - rect->p.y; out_rect.p.x = 0; out_rect.p.y = 0; out_rect.q.x = (in_rect.q.x * arg->upfactor + arg->downfactor-1) / arg->downfactor; out_rect.q.y = (in_rect.q.y * arg->upfactor + arg->downfactor-1) / arg->downfactor; /* Where do we get the data from? */ params.options = GB_COLORS_NATIVE | GB_ALPHA_NONE | GB_PACKING_CHUNKY | GB_RETURN_POINTER | GB_ALIGN_ANY | GB_OFFSET_0 | GB_RASTER_ANY; code = dev_proc(bdev, get_bits_rectangle)(bdev, &in_rect, ¶ms); if (code < 0) return code; raster_in = params.raster; in_ptr = params.data[0]; /* Where do we write it to? */ if (buffer->bdev) { code = dev_proc(bdev, get_bits_rectangle)(buffer->bdev, &out_rect, ¶ms); if (code < 0) return code; raster_out = params.raster; out_ptr = params.data[0]; } else { raster_out = raster_in; out_ptr = params.data[0]; } /* Do the downscale */ if (arg->ds.down_core) { int y; for (y = rect->p.y; y < rect->q.y; y += arg->downfactor) { arg->ds.down_core(&arg->ds, out_ptr, in_ptr, y, 0, arg->ds.span); in_ptr += arg->ds.span * arg->downfactor; out_ptr += raster_out * arg->upfactor; } } /* Pass on to further processing */ if (arg->orig_options && arg->orig_options->process_fn) { out_rect.p.y = rect->p.y*arg->upfactor/arg->downfactor; out_rect.q.y += out_rect.p.y; code = arg->orig_options->process_fn(arg->orig_options->arg, dev, (buffer->bdev ? buffer->bdev : bdev), &out_rect, buffer->orig_buffer); } return code; } static void downscaler_free_fn(void *arg_, gx_device *dev, gs_memory_t *memory, void *buffer_) { downscaler_process_page_arg_t *arg = (downscaler_process_page_arg_t *)arg_; downscaler_process_page_buffer_t *buffer = (downscaler_process_page_buffer_t *)buffer_; arg->orig_options->free_buffer_fn(arg->orig_options->arg, dev, memory, buffer->orig_buffer); if (buffer->bdev) dev_proc(dev, close_device)(dev); gs_free_object(memory, buffer, "downscaler process_page buffer"); } static int downscaler_output_fn(void *arg_, gx_device *dev, void *buffer_) { downscaler_process_page_arg_t *arg = (downscaler_process_page_arg_t *)arg_; downscaler_process_page_buffer_t *buffer = (downscaler_process_page_buffer_t *)buffer_; return arg->orig_options->output_fn(arg->orig_options->arg, dev, buffer->orig_buffer); } /* No error diffusion with process_page as bands need to be handled * separately. */ int gx_downscaler_process_page(gx_device *dev, gx_process_page_options_t *options, int factor) { downscaler_process_page_arg_t arg = { 0 }; gx_process_page_options_t my_options = { 0 }; int num_comps = dev->color_info.num_components; int src_bpc = dev->color_info.comp_bits[0]; int scaled_w; gx_downscale_core *core; arg.orig_options = options; gx_downscaler_decode_factor(factor, &arg.upfactor, &arg.downfactor); arg.ds.dev = dev; arg.ds.width = (dev->width * arg.upfactor + arg.downfactor-1)/arg.downfactor; arg.ds.awidth = arg.ds.width; arg.ds.span = bitmap_raster(dev->width * num_comps * src_bpc); scaled_w = (dev->width * arg.upfactor + arg.downfactor-1)/arg.downfactor; arg.ds.factor = factor; arg.ds.src_bpc = src_bpc; arg.ds.scaled_span = bitmap_raster(scaled_w * num_comps * src_bpc); arg.ds.num_planes = 0; /* Choose an appropriate core */ if (factor > 8) { return gs_note_error(gs_error_rangecheck); } else if ((src_bpc == 16) && (num_comps == 1)) { core = &down_core16; } else if (factor == 1) core = NULL; else if ((src_bpc == 8) && (num_comps == 1)) { if (factor == 4) core = &down_core8_4; else if (factor == 3) core = &down_core8_3; else if (factor == 2) core = &down_core8_2; else core = &down_core8; } else if ((src_bpc == 8) && (num_comps == 3)) core = &down_core24; else if ((src_bpc == 8) && (num_comps == 4)) core = &down_core32; else { return gs_note_error(gs_error_rangecheck); } arg.ds.down_core = core; my_options.init_buffer_fn = downscaler_init_fn; my_options.process_fn = downscaler_process_fn; my_options.output_fn = downscaler_output_fn; my_options.free_buffer_fn = downscaler_free_fn; my_options.arg = &arg; return dev_proc(dev, process_page)(dev, &my_options); } int gx_downscaler_read_params(gs_param_list *plist, gx_downscaler_params *params, int features) { int code; int downscale, mfs, ets, deskew; int trap_w, trap_h; const char *param_name; gs_param_int_array trap_order; trap_order.data = NULL; switch (code = param_read_int(plist, (param_name = "DownScaleFactor"), &downscale)) { case 1: break; case 0: if (downscale >= 1) { params->downscale_factor = downscale; break; } code = gs_error_rangecheck; default: param_signal_error(plist, param_name, code); return code; } switch (code = param_read_bool(plist, (param_name = "Deskew"), &deskew)) { case 1: break; case 0: if (deskew >= 0) { params->do_skew_detection = deskew; break; } code = gs_error_rangecheck; default: param_signal_error(plist, param_name, code); return code; } if (features & GX_DOWNSCALER_PARAMS_MFS) { switch (code = param_read_int(plist, (param_name = "MinFeatureSize"), &mfs)) { case 1: break; case 0: if ((mfs >= 0) && (mfs <= 4)) { params->min_feature_size = mfs; break; } code = gs_error_rangecheck; default: param_signal_error(plist, param_name, code); return code; } } if (features & GX_DOWNSCALER_PARAMS_TRAP) { switch (code = param_read_int(plist, (param_name = "TrapX"), &trap_w)) { case 1: break; case 0: if (trap_w >= 0) { params->trap_w = trap_w; break; } code = gs_error_rangecheck; default: param_signal_error(plist, param_name, code); return code; } switch (code = param_read_int(plist, (param_name = "TrapY"), &trap_h)) { case 1: break; case 0: if (trap_h >= 0) { params->trap_h = trap_h; break; } code = gs_error_rangecheck; default: param_signal_error(plist, param_name, code); return code; } switch (code = param_read_int_array(plist, (param_name = "TrapOrder"), &trap_order)) { case 0: break; case 1: trap_order.data = 0; /* mark as not filled */ break; default: param_signal_error(plist, param_name, code); return code; } if (trap_order.data != NULL) { int i; int n = trap_order.size; if (n > GS_CLIENT_COLOR_MAX_COMPONENTS) n = GS_CLIENT_COLOR_MAX_COMPONENTS; for (i = 0; i < n; i++) { params->trap_order[i] = trap_order.data[i]; } for (; i < GS_CLIENT_COLOR_MAX_COMPONENTS; i++) { params->trap_order[i] = i; } } else { /* Set some sane defaults */ int i; params->trap_order[0] = 3; /* K */ params->trap_order[1] = 1; /* M */ params->trap_order[2] = 0; /* C */ params->trap_order[3] = 2; /* Y */ for (i = 4; i < GS_CLIENT_COLOR_MAX_COMPONENTS; i++) { params->trap_order[i] = i; } } } if (features & GX_DOWNSCALER_PARAMS_ETS) { switch (code = param_read_int(plist, (param_name = "DownScaleETS"), &ets)) { case 1: break; case 0: if (ets >= 0) { params->ets = ets; break; } code = gs_error_rangecheck; default: param_signal_error(plist, param_name, code); return code; } } return 0; } int gx_downscaler_write_params(gs_param_list *plist, gx_downscaler_params *params, int features) { int code; int ecode = 0; gs_param_int_array trap_order; trap_order.data = params->trap_order; trap_order.size = GS_CLIENT_COLOR_MAX_COMPONENTS; trap_order.persistent = false; if ((code = param_write_int(plist, "DownScaleFactor", ¶ms->downscale_factor)) < 0) ecode = code; if ((code = param_write_bool(plist, "Deskew", ¶ms->do_skew_detection)) < 0) ecode = code; if (features & GX_DOWNSCALER_PARAMS_MFS) { if ((code = param_write_int(plist, "MinFeatureSize", ¶ms->min_feature_size)) < 0) ecode = code; } if (features & GX_DOWNSCALER_PARAMS_TRAP) { if ((code = param_write_int(plist, "TrapX", ¶ms->trap_w)) < 0) ecode = code; if ((code = param_write_int(plist, "TrapY", ¶ms->trap_h)) < 0) ecode = code; if ((code = param_write_int_array(plist, "TrapOrder", &trap_order)) < 0) ecode = code; } if (features & GX_DOWNSCALER_PARAMS_ETS) { if ((code = param_write_int(plist, "DownScaleETS", ¶ms->ets)) < 0) ecode = code; } return ecode; } /* ETS relies on some malloc wrappers */ void *ets_malloc(void *malloc_arg, int size) { return gs_alloc_bytes((gs_memory_t *)malloc_arg, size, "ets_malloc"); } void *ets_calloc(void *malloc_arg, int count, int size) { void *p = ets_malloc(malloc_arg, (size_t)count * size); if (p) memset(p, 0, (size_t)count * size); return p; } void ets_free(void *malloc_arg, void *p) { if (!p) return; gs_free_object((gs_memory_t *)malloc_arg, p, "ets_malloc"); } int gx_downscaler_create_post_render_link(gx_device *dev, gsicc_link_t **link) { cmm_dev_profile_t *profile_struct; gsicc_rendering_param_t rendering_params; int code = dev_proc(dev, get_profile)(dev, &profile_struct); if (code < 0) return_error(gs_error_undefined); *link = NULL; if (profile_struct->postren_profile == NULL) return 0; rendering_params.black_point_comp = gsBLACKPTCOMP_ON; rendering_params.graphics_type_tag = GS_UNKNOWN_TAG; rendering_params.override_icc = false; rendering_params.preserve_black = gsBLACKPRESERVE_OFF; rendering_params.rendering_intent = gsRELATIVECOLORIMETRIC; rendering_params.cmm = gsCMM_DEFAULT; *link = gsicc_alloc_link_dev(dev->memory, profile_struct->device_profile[GS_DEFAULT_DEVICE_PROFILE], profile_struct->postren_profile, &rendering_params); if (*link == NULL) return_error(gs_error_VMerror); /* If it is identity, release it now and set link to NULL */ if ((*link)->is_identity) { gsicc_free_link_dev(*link); *link = NULL; } return 0; } int gx_downscaler_create_icc_link(gx_device *dev, gsicc_link_t **link, cmm_profile_t *icc_profile) { gsicc_rendering_param_t rendering_params; cmm_dev_profile_t *profile_struct; int code = dev_proc(dev, get_profile)(dev, &profile_struct); *link = NULL; if (code < 0) return code; if (icc_profile == NULL) return 0; /* Should be an error, maybe? */ rendering_params.black_point_comp = gsBLACKPTCOMP_ON; rendering_params.graphics_type_tag = GS_UNKNOWN_TAG; rendering_params.override_icc = false; rendering_params.preserve_black = gsBLACKPRESERVE_OFF; rendering_params.rendering_intent = gsRELATIVECOLORIMETRIC; rendering_params.cmm = gsCMM_DEFAULT; *link = gsicc_alloc_link_dev(dev->memory, profile_struct->device_profile[GS_DEFAULT_DEVICE_PROFILE], icc_profile, &rendering_params); if (*link == NULL) return_error(gs_error_VMerror); /* If it is identity, release it now and set link to NULL */ if ((*link)->is_identity) { gsicc_free_link_dev(*link); *link = NULL; } return 0; }