summaryrefslogtreecommitdiff
path: root/gs/base/sidscale.c
diff options
context:
space:
mode:
Diffstat (limited to 'gs/base/sidscale.c')
-rw-r--r--gs/base/sidscale.c353
1 files changed, 353 insertions, 0 deletions
diff --git a/gs/base/sidscale.c b/gs/base/sidscale.c
new file mode 100644
index 000000000..63eeb55be
--- /dev/null
+++ b/gs/base/sidscale.c
@@ -0,0 +1,353 @@
+/* Copyright (C) 2001-2006 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 that
+ license. Refer to licensing information at http://www.artifex.com/
+ or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134,
+ San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* $Id$ */
+/* Special Image downsample scaling filters for dithered devices */
+#include "math_.h"
+#include "memory_.h"
+#include "stdio_.h"
+#include "gdebug.h"
+#include "gxfixed.h" /* for gxdda.h */
+#include "gxdda.h"
+#include "gxfrac.h"
+#include "strimpl.h"
+#include "sidscale.h"
+
+/* Temporary intermediate values */
+typedef byte PixelTmp;
+
+#define minPixelTmp 0
+#define maxPixelTmp 255
+#define unitPixelTmp 255
+
+/* Max of all pixel sizes */
+#define maxSizeofPixel 2
+
+/* Auxiliary structures. */
+
+/* ImageSpecialDownScaleEncode / ImageSpecialDownScaleDecode */
+typedef struct stream_ISpecialDownScale_state_s {
+ /* The client sets the params values before initialization. */
+ stream_image_scale_state_common; /* = state_common + params */
+ /* The init procedure sets the following. */
+ int sizeofPixelIn; /* bytes per input value, 1 or 2 */
+ int sizeofPixelOut; /* bytes per output value, 1 or 2 */
+ void /*PixelIn */ *src;
+ void /*PixelOut */ *dst;
+ void /*PixelIn */ *tmp;
+ gx_dda_int_t dda_x_init; /* initial setting of dda_x */
+ /* The following are updated dynamically. */
+ int dst_x;
+ uint dst_offset, dst_size;
+ gx_dda_int_t dda_x; /* DDA for dest X in current scan line */
+ int src_y;
+ uint src_offset, src_size;
+ int dst_y;
+ gx_dda_int_t dda_y; /* DDA for dest Y */
+} stream_ISpecialDownScale_state;
+
+gs_private_st_ptrs3(st_ISpecialDownScale_state, stream_ISpecialDownScale_state,
+ "ImageSpecialDownScaleEncode/Decode state",
+ isdscale_state_enum_ptrs, isdscale_state_reloc_ptrs,
+ dst, src, tmp);
+
+/* Apply filter to downscale horizontally from src to tmp. */
+static void
+idownscale_x(void /* PixelIn */ * tmp, const void /* PixelIn */ *src, stream_ISpecialDownScale_state *const ss)
+{
+ int c, i;
+ int Colors = ss->params.Colors;
+ int WidthIn = ss->params.WidthIn;
+ int prev_y = dda_previous(ss->dda_y);
+ int cur_y = dda_next(ss->dda_y);
+ bool firstline = prev_y != cur_y; /* at the start of a new group of lines */
+ bool polarity_additive = ss->params.ColorPolarityAdditive;
+
+ /* The following could be a macro, BUT macro's with control */
+ /* are not good style and hard to debug */
+ if (ss->sizeofPixelIn == 1) {
+ for (c = 0; c < Colors; ++c) {
+ byte *tp = (byte *)tmp + c; /* destination */
+ const byte *pp = (const byte *)src + c;
+
+ ss->dda_x = ss->dda_x_init;
+ if_debug1('W', "[W]idownscale_x color %d:", c);
+
+ for ( i = 0; i < WidthIn; tp += Colors) {
+ int endx = dda_next(ss->dda_x);
+ if (firstline)
+ *tp = *pp;
+ else {
+ if ((polarity_additive && (*pp < *tp)) ||
+ (!polarity_additive && (*pp > *tp)) )
+ *tp = *pp;
+ }
+ i++; pp += Colors;
+ while (i < endx) {
+ if (*pp < *tp)
+ *tp = *pp;
+ i++; pp += Colors;
+ }
+ if_debug1('W', " %d", *tp);
+ }
+ if_debug0('W', "\n");
+ }
+ } else { /* sizeofPixelIn == 2 */
+ for (c = 0; c < Colors; ++c) {
+ bits16 *tp = (bits16 *)tmp + c; /* destination */
+ const bits16 *pp = (const bits16 *)src + c;
+
+ ss->dda_x = ss->dda_x_init;
+ if_debug1('W', "[W]idownscale_x color %d:", c);
+
+ for ( i = 0; i < WidthIn; tp += Colors) {
+ int endx = dda_next(ss->dda_x);
+ if (firstline)
+ *tp = *pp;
+ else {
+ if ((polarity_additive && (*pp < *tp)) ||
+ (!polarity_additive && (*pp > *tp)) )
+ *tp = *pp;
+ }
+ i++; pp += Colors;
+ while (i < endx) {
+ if (*pp < *tp)
+ *tp = *pp;
+ i++; pp += Colors;
+ }
+ if_debug1('W', " %d", *tp);
+ }
+ if_debug0('W', "\n");
+ }
+ }
+}
+
+
+/*
+ * Copy from tmp to dst, taking into account PixelOut vs. PixelIn sizes
+ * We do the conversion from PixelIn to PixelOut here (if needed)
+ * since if the Y is scaled down we will convert less often.
+ * This is simpler because we can treat all columns identically
+ * without regard to the number of samples per pixel.
+ */
+static void
+idownscale_y(void /*PixelOut */ *dst, const void /* PixelIn */ *tmp,
+ stream_ISpecialDownScale_state *const ss)
+{
+ int kn = ss->params.WidthOut * ss->params.Colors;
+ int kc;
+
+ if_debug0('W', "[W]idownscale_y: ");
+
+ if (ss->sizeofPixelOut == 1) {
+ if (ss->sizeofPixelIn == 1) {
+ const byte *pp = (byte *)tmp;
+
+ for ( kc = 0; kc < kn; ++kc, pp++ ) {
+ if_debug1('W', " %d", *pp);
+ ((byte *)dst)[kc] = *pp;
+ }
+ } else { /* sizeofPixelIn == 2 */
+ const bits16 *pp = (bits16 *)tmp;
+
+ for ( kc = 0; kc < kn; ++kc, pp++ ) {
+ if_debug1('W', " %d", *pp);
+ ((byte *)dst)[kc] = frac2byte(*pp);
+ }
+ }
+ } else { /* sizeofPixelOut == 2 */
+ if (ss->sizeofPixelIn == 1) {
+ const byte *pp = (byte *)tmp;
+
+ for ( kc = 0; kc < kn; ++kc, pp++ ) {
+ if_debug1('W', " %d", *pp);
+ ((bits16 *)dst)[kc] = byte2frac(*pp);
+ }
+ } else { /* sizeofPixelIn == 2 */
+ const bits16 *pp = (bits16 *)tmp;
+
+ for ( kc = 0; kc < kn; ++kc, pp++ ) {
+ if_debug1('W', " %d", *pp);
+ ((bits16 *)dst)[kc] = *pp;
+ }
+ }
+ }
+ if_debug0('W', "n");
+}
+
+/* ------ Stream implementation ------ */
+
+
+/* Forward references */
+static void s_ISpecialDownScale_release(stream_state * st);
+
+/* Set default parameter values (actually, just clear pointers). */
+static void
+s_ISpecialDownScale_set_defaults(stream_state * st)
+{
+ stream_ISpecialDownScale_state *const ss = (stream_ISpecialDownScale_state *) st;
+
+ ss->src = 0;
+ ss->dst = 0;
+ ss->tmp = 0;
+}
+
+/* Initialize the filter. */
+static int
+s_ISpecialDownScale_init(stream_state * st)
+{
+ stream_ISpecialDownScale_state *const ss = (stream_ISpecialDownScale_state *) st;
+ gs_memory_t *mem = ss->memory;
+
+ ss->sizeofPixelIn = ss->params.BitsPerComponentIn / 8;
+ ss->sizeofPixelOut = ss->params.BitsPerComponentOut / 8;
+
+ ss->src_size = ss->params.WidthIn * ss->sizeofPixelIn * ss->params.Colors;
+ ss->dst_size = ss->params.WidthOut * ss->sizeofPixelOut * ss->params.Colors;
+
+ /* Initialize destination DDAs. */
+ ss->dst_x = 0;
+ ss->src_offset = ss->dst_offset = 0;
+ dda_init(ss->dda_x, 0, ss->params.WidthIn, ss->params.WidthOut);
+ ss->dda_x_init = ss->dda_x;
+ ss->src_y = ss->dst_y = 0;
+ dda_init(ss->dda_y, 0, ss->params.HeightOut, ss->params.HeightIn);
+
+
+ /* create intermediate image to hold horizontal zoom */
+ ss->tmp = gs_alloc_byte_array(mem, ss->params.WidthOut * ss->params.Colors,
+ ss->sizeofPixelIn, "image_scale tmp");
+ /* Allocate buffers for 1 row of source and destination. */
+ ss->dst = gs_alloc_byte_array(mem, ss->params.WidthOut * ss->params.Colors,
+ ss->sizeofPixelOut, "image_scale dst");
+ ss->src = gs_alloc_byte_array(mem, ss->params.WidthIn * ss->params.Colors,
+ ss->sizeofPixelIn, "image_scale src");
+ if (ss->tmp == 0 || ss->dst == 0 || ss->src == 0) {
+ s_ISpecialDownScale_release(st);
+ return ERRC;
+/****** WRONG ******/
+ }
+
+ return 0;
+
+}
+
+/* Process a buffer. Note that this handles Encode and Decode identically. */
+static int
+s_ISpecialDownScale_process(stream_state * st, stream_cursor_read * pr,
+ stream_cursor_write * pw, bool last)
+{
+ stream_ISpecialDownScale_state *const ss = (stream_ISpecialDownScale_state *) st;
+ uint cur_y = dda_current(ss->dda_y);
+
+ /* Check whether we need to deliver any output. */
+
+top:
+ if (cur_y > ss->dst_y) {
+ /* Deliver some or all of the current scaled row. */
+ /* to generate a vertically scaled output row. */
+ uint wleft = pw->limit - pw->ptr;
+
+ if (ss->dst_y == ss->params.HeightOut)
+ return EOFC;
+ if (wleft == 0)
+ return 1;
+ if (ss->dst_offset == 0) {
+ byte *row;
+
+ if (wleft >= ss->dst_size) { /* We can scale the row directly into the output. */
+ row = pw->ptr + 1;
+ pw->ptr += ss->dst_size;
+ } else { /* We'll have to buffer the row. */
+ row = ss->dst;
+ }
+ /* Apply filter to zoom vertically from tmp to dst. */
+ idownscale_y(row, ss->tmp, ss);
+ /* Idiotic C coercion rules allow T* and void* to be */
+ /* inter-assigned freely, but not compared! */
+ if ((void *)row != ss->dst) /* no buffering */
+ goto adv;
+ } { /* We're delivering a buffered output row. */
+ uint wcount = ss->dst_size - ss->dst_offset;
+ uint ncopy = min(wleft, wcount);
+
+ memcpy(pw->ptr + 1, (byte *) ss->dst + ss->dst_offset, ncopy);
+ pw->ptr += ncopy;
+ ss->dst_offset += ncopy;
+ if (ncopy != wcount)
+ return 1;
+ ss->dst_offset = 0;
+ }
+ /* Advance to the next output row. */
+adv: ++(ss->dst_y);
+ }
+
+ /* Read input data and scale horizontally into tmp. */
+
+ {
+ uint rleft = pr->limit - pr->ptr;
+ uint rcount = ss->src_size - ss->src_offset;
+
+ if (rleft == 0)
+ return 0; /* need more input */
+ if (ss->src_y >= ss->params.HeightIn)
+ return ERRC;
+ if (rleft >= rcount) { /* We're going to fill up a row. */
+ const byte *row;
+
+ if (ss->src_offset == 0) { /* We have a complete row. Read the data */
+ /* directly from the input. */
+ row = pr->ptr + 1;
+ } else { /* We're buffering a row in src. */
+ row = ss->src;
+ memcpy((byte *) ss->src + ss->src_offset, pr->ptr + 1,
+ rcount);
+ ss->src_offset = 0;
+ }
+ /* Apply filter to zoom horizontally from src to tmp. */
+ if_debug2('w', "[w]idownscale_x y = %d to tmp row %d\n",
+ ss->src_y, (ss->src_y % MAX_ISCALE_SUPPORT));
+ idownscale_x(ss->tmp, row, ss);
+ pr->ptr += rcount;
+ ++(ss->src_y);
+ cur_y = dda_next(ss->dda_y);
+ goto top;
+ } else { /* We don't have a complete row. Copy data to src buffer. */
+ memcpy((byte *) ss->src + ss->src_offset, pr->ptr + 1, rleft);
+ ss->src_offset += rleft;
+ pr->ptr += rleft;
+ return 0;
+ }
+ }
+}
+
+/* Release the filter's storage. */
+static void
+s_ISpecialDownScale_release(stream_state * st)
+{
+ stream_ISpecialDownScale_state *const ss = (stream_ISpecialDownScale_state *) st;
+ gs_memory_t *mem = ss->memory;
+
+ gs_free_object(mem, (void *)ss->src, "image_scale src"); /* no longer const */
+ ss->src = 0;
+ gs_free_object(mem, ss->dst, "image_scale dst");
+ ss->dst = 0;
+ gs_free_object(mem, ss->tmp, "image_scale tmp");
+ ss->tmp = 0;
+}
+
+/* Stream template */
+const stream_template s_ISpecialDownScale_template = {
+ &st_ISpecialDownScale_state, s_ISpecialDownScale_init, s_ISpecialDownScale_process, 1, 1,
+ s_ISpecialDownScale_release, s_ISpecialDownScale_set_defaults
+};