diff options
Diffstat (limited to 'gs/base/gdevpsds.c')
-rw-r--r-- | gs/base/gdevpsds.c | 1201 |
1 files changed, 1201 insertions, 0 deletions
diff --git a/gs/base/gdevpsds.c b/gs/base/gdevpsds.c new file mode 100644 index 000000000..399d7b89e --- /dev/null +++ b/gs/base/gdevpsds.c @@ -0,0 +1,1201 @@ +/* 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$ */ +/* Image processing streams for PostScript and PDF writers */ +#include "gx.h" +#include "memory_.h" +#include "gserrors.h" +#include "gxdcconv.h" +#include "gdevpsds.h" +#include "gxbitmap.h" +#include "gxcspace.h" +#include "gsdcolor.h" +#include "gscspace.h" +#include "gxdevcli.h" + +/* ---------------- Convert between 1/2/4/12 and 8 bits ---------------- */ + +gs_private_st_simple(st_1248_state, stream_1248_state, "stream_1248_state"); + +/* Initialize an expansion or reduction stream. */ +int +s_1248_init(stream_1248_state *ss, int Columns, int samples_per_pixel) +{ + ss->samples_per_row = Columns * samples_per_pixel; + return ss->template->init((stream_state *)ss); +} + +/* Initialize the state. */ +static int +s_1_init(stream_state * st) +{ + stream_1248_state *const ss = (stream_1248_state *) st; + + ss->left = ss->samples_per_row; + ss->bits_per_sample = 1; + return 0; +} +static int +s_2_init(stream_state * st) +{ + stream_1248_state *const ss = (stream_1248_state *) st; + + ss->left = ss->samples_per_row; + ss->bits_per_sample = 2; + return 0; +} +static int +s_4_init(stream_state * st) +{ + stream_1248_state *const ss = (stream_1248_state *) st; + + ss->left = ss->samples_per_row; + ss->bits_per_sample = 4; + return 0; +} +static int +s_12_init(stream_state * st) +{ + stream_1248_state *const ss = (stream_1248_state *) st; + + ss->left = ss->samples_per_row; + ss->bits_per_sample = 12; /* not needed */ + return 0; +} + +/* Process one buffer. */ +#define BEGIN_1248\ + stream_1248_state * const ss = (stream_1248_state *)st;\ + const byte *p = pr->ptr;\ + const byte *rlimit = pr->limit;\ + byte *q = pw->ptr;\ + byte *wlimit = pw->limit;\ + uint left = ss->left;\ + int status;\ + int n +#define END_1248\ + pr->ptr = p;\ + pw->ptr = q;\ + ss->left = left;\ + return status + +/* N-to-8 expansion */ +#define FOREACH_N_8(in, nout)\ + status = 0;\ + for ( ; p < rlimit; left -= n, q += n, ++p ) {\ + byte in = p[1];\ + n = min(left, nout);\ + if ( wlimit - q < n ) {\ + status = 1;\ + break;\ + }\ + switch ( n ) {\ + case 0: left = ss->samples_per_row; --p; continue; +#define END_FOREACH_N_8\ + }\ + } +static int +s_N_8_process(stream_state * st, stream_cursor_read * pr, + stream_cursor_write * pw, bool last) +{ + BEGIN_1248; + + switch (ss->bits_per_sample) { + + case 1:{ + FOREACH_N_8(in, 8) + case 8: + q[8] = (byte) - (in & 1); + case 7: + q[7] = (byte) - ((in >> 1) & 1); + case 6: + q[6] = (byte) - ((in >> 2) & 1); + case 5: + q[5] = (byte) - ((in >> 3) & 1); + case 4: + q[4] = (byte) - ((in >> 4) & 1); + case 3: + q[3] = (byte) - ((in >> 5) & 1); + case 2: + q[2] = (byte) - ((in >> 6) & 1); + case 1: + q[1] = (byte) - (in >> 7); + END_FOREACH_N_8; + } + break; + + case 2:{ + static const byte b2[4] = + {0x00, 0x55, 0xaa, 0xff}; + + FOREACH_N_8(in, 4) + case 4: + q[4] = b2[in & 3]; + case 3: + q[3] = b2[(in >> 2) & 3]; + case 2: + q[2] = b2[(in >> 4) & 3]; + case 1: + q[1] = b2[in >> 6]; + END_FOREACH_N_8; + } + break; + + case 4:{ + static const byte b4[16] = + { + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff + }; + + FOREACH_N_8(in, 2) + case 2: + q[2] = b4[in & 0xf]; + case 1: + q[1] = b4[in >> 4]; + END_FOREACH_N_8; + } + break; + + default: + return ERRC; + } + + END_1248; +} + +/* 12-to-8 "expansion" */ +static int +s_12_8_process(stream_state * st, stream_cursor_read * pr, + stream_cursor_write * pw, bool last) +{ + BEGIN_1248; + + n = ss->samples_per_row; /* misuse n to avoid a compiler warning */ + status = 0; + for (; rlimit - p >= 2; ++q) { + if (q >= wlimit) { + status = 1; + break; + } + if (left == 0) + left = n; + if ((n - left) & 1) { + q[1] = (byte)((p[1] << 4) | (p[2] >> 4)); + p += 2, --left; + } else { + q[1] = *++p; + if (!--left) + ++p; + } + } + + END_1248; +} + + +/* 8-to-N reduction */ +#define FOREACH_8_N(out, nin)\ + byte out;\ + status = 1;\ + for ( ; q < wlimit; left -= n, p += n, ++q ) {\ + n = min(left, nin);\ + if ( rlimit - p < n ) {\ + status = 0;\ + break;\ + }\ + out = 0;\ + switch ( n ) {\ + case 0: left = ss->samples_per_row; --q; continue; +#define END_FOREACH_8_N\ + q[1] = out;\ + }\ + } +static int +s_8_N_process(stream_state * st, stream_cursor_read * pr, + stream_cursor_write * pw, bool last) +{ + BEGIN_1248; + + switch (ss->bits_per_sample) { + + case 1:{ + FOREACH_8_N(out, 8) + case 8: + out = p[8] >> 7; + case 7: + out |= (p[7] >> 7) << 1; + case 6: + out |= (p[6] >> 7) << 2; + case 5: + out |= (p[5] >> 7) << 3; + case 4: + out |= (p[4] >> 7) << 4; + case 3: + out |= (p[3] >> 7) << 5; + case 2: + out |= (p[2] >> 7) << 6; + case 1: + out |= p[1] & 0x80; + END_FOREACH_8_N; + } + break; + + case 2:{ + FOREACH_8_N(out, 4) + case 4: + out |= p[4] >> 6; + case 3: + out |= (p[3] >> 6) << 2; + case 2: + out |= (p[2] >> 6) << 4; + case 1: + out |= p[1] & 0xc0; + END_FOREACH_8_N; + } + break; + + case 4:{ + FOREACH_8_N(out, 2) + case 2: + out |= p[2] >> 4; + case 1: + out |= p[1] & 0xf0; + END_FOREACH_8_N; + } + break; + + default: + return ERRC; + } + + END_1248; +} + +const stream_template s_1_8_template = { + &st_1248_state, s_1_init, s_N_8_process, 1, 8 +}; +const stream_template s_2_8_template = { + &st_1248_state, s_2_init, s_N_8_process, 1, 4 +}; +const stream_template s_4_8_template = { + &st_1248_state, s_4_init, s_N_8_process, 1, 2 +}; +const stream_template s_12_8_template = { + &st_1248_state, s_12_init, s_12_8_process, 1, 2 +}; + +const stream_template s_8_1_template = { + &st_1248_state, s_1_init, s_8_N_process, 8, 1 +}; +const stream_template s_8_2_template = { + &st_1248_state, s_2_init, s_8_N_process, 4, 1 +}; +const stream_template s_8_4_template = { + &st_1248_state, s_4_init, s_8_N_process, 2, 1 +}; + +/* ---------------- Color space conversion ---------------- */ + +/* ------ Convert CMYK to RGB ------ */ + +private_st_C2R_state(); + +/* Initialize a CMYK => RGB conversion stream. */ +int +s_C2R_init(stream_C2R_state *ss, const gs_imager_state *pis) +{ + ss->pis = pis; + return 0; +} + +/* Set default parameter values (actually, just clear pointers). */ +static void +s_C2R_set_defaults(stream_state * st) +{ + stream_C2R_state *const ss = (stream_C2R_state *) st; + + ss->pis = 0; +} + +/* Process one buffer. */ +static int +s_C2R_process(stream_state * st, stream_cursor_read * pr, + stream_cursor_write * pw, bool last) +{ + stream_C2R_state *const ss = (stream_C2R_state *) st; + const byte *p = pr->ptr; + const byte *rlimit = pr->limit; + byte *q = pw->ptr; + byte *wlimit = pw->limit; + + for (; rlimit - p >= 4 && wlimit - q >= 3; p += 4, q += 3) { + byte bc = p[1], bm = p[2], by = p[3], bk = p[4]; + frac rgb[3]; + + color_cmyk_to_rgb(byte2frac(bc), byte2frac(bm), byte2frac(by), + byte2frac(bk), ss->pis, rgb); + q[1] = frac2byte(rgb[0]); + q[2] = frac2byte(rgb[1]); + q[3] = frac2byte(rgb[2]); + } + pr->ptr = p; + pw->ptr = q; + return (rlimit - p < 4 ? 0 : 1); +} + +const stream_template s_C2R_template = { + &st_C2R_state, 0 /*NULL */ , s_C2R_process, 4, 3, 0, s_C2R_set_defaults +}; + +/* ------ Convert any color space to Indexed ------ */ + +private_st_IE_state(); +static +ENUM_PTRS_WITH(ie_state_enum_ptrs, stream_IE_state *st) return 0; +case 0: return ENUM_OBJ(st->Decode); +case 1: return ENUM_BYTESTRING(&st->Table); +ENUM_PTRS_END +static +RELOC_PTRS_WITH(ie_state_reloc_ptrs, stream_IE_state *st) +{ + RELOC_VAR(st->Decode); + RELOC_BYTESTRING_VAR(st->Table); +} +RELOC_PTRS_END + +/* Set defaults. */ +static void +s_IE_set_defaults(stream_state * st) +{ + stream_IE_state *const ss = (stream_IE_state *) st; + + ss->Decode = 0; /* clear pointers */ + gs_bytestring_from_string(&ss->Table, 0, 0); +} + +/* Initialize the state. */ +static int +s_IE_init(stream_state * st) +{ + stream_IE_state *const ss = (stream_IE_state *) st; + int key_index = (1 << ss->BitsPerIndex) * ss->NumComponents; + int i; + + if (ss->Table.data == 0 || ss->Table.size < key_index) + return ERRC; /****** WRONG ******/ + /* Initialize Table with default values. */ + memset(ss->Table.data, 0, ss->NumComponents); + ss->Table.data[ss->Table.size - 1] = 0; + for (i = 0; i < countof(ss->hash_table); ++i) + ss->hash_table[i] = key_index; + ss->next_index = 0; + ss->in_bits_left = 0; + ss->next_component = 0; + ss->byte_out = 1; + ss->x = 0; + return 0; +} + +/* Process a buffer. */ +static int +s_IE_process(stream_state * st, stream_cursor_read * pr, + stream_cursor_write * pw, bool last) +{ + stream_IE_state *const ss = (stream_IE_state *) st; + /* Constant values from the state */ + const int bpc = ss->BitsPerComponent; + const int num_components = ss->NumComponents; + const int end_index = (1 << ss->BitsPerIndex) * num_components; + byte *const table = ss->Table.data; + byte *const key = table + end_index; + /* Dynamic values from the state */ + uint byte_in = ss->byte_in; + int in_bits_left = ss->in_bits_left; + int next_component = ss->next_component; + uint byte_out = ss->byte_out; + /* Other dynamic values */ + const byte *p = pr->ptr; + const byte *rlimit = pr->limit; + byte *q = pw->ptr; + byte *wlimit = pw->limit; + int status = 0; + + for (;;) { + uint hash, reprobe; + int i, index; + + /* Check for a filled output byte. */ + if (byte_out >= 0x100) { + if (q >= wlimit) { + status = 1; + break; + } + *++q = (byte)byte_out; + byte_out = 1; + } + /* Acquire a complete input value. */ + while (next_component < num_components) { + const float *decode = &ss->Decode[next_component * 2]; + int sample; + + if (in_bits_left == 0) { + if (p >= rlimit) + goto out; + byte_in = *++p; + in_bits_left = 8; + } + /* An input sample can never span a byte boundary. */ + in_bits_left -= bpc; + sample = (byte_in >> in_bits_left) & ((1 << bpc) - 1); + /* Scale the sample according to Decode. */ + sample = (int)((decode[0] + + (sample / (float)((1 << bpc) - 1) * + (decode[1] - decode[0]))) * 255 + 0.5); + key[next_component++] = + (sample < 0 ? 0 : sample > 255 ? 255 : (byte)sample); + } + /* Look up the input value. */ + for (hash = 0, i = 0; i < num_components; ++i) + hash = hash + 23 * key[i]; /* adhoc */ + reprobe = (hash / countof(ss->hash_table)) | 137; /* adhoc */ + for (hash %= countof(ss->hash_table); + memcmp(table + ss->hash_table[hash], key, num_components); + hash = (hash + reprobe) % countof(ss->hash_table) + ) + DO_NOTHING; + index = ss->hash_table[hash]; + if (index == end_index) { + /* The match was on an empty entry. */ + if (ss->next_index == end_index) { + /* Too many different values. */ + status = ERRC; + break; + } + ss->hash_table[hash] = index = ss->next_index; + ss->next_index += num_components; + memcpy(table + index, key, num_components); + } + byte_out = (byte_out << ss->BitsPerIndex) + index / num_components; + next_component = 0; + if (++(ss->x) == ss->Width) { + /* Handle input and output padding. */ + in_bits_left = 0; + if (byte_out != 1) + while (byte_out < 0x100) + byte_out <<= 1; + ss->x = 0; + } + } +out: + pr->ptr = p; + pw->ptr = q; + ss->byte_in = byte_in; + ss->in_bits_left = in_bits_left; + ss->next_component = next_component; + ss->byte_out = byte_out; + /* For simplicity, always update the record of the table size. */ + ss->Table.data[ss->Table.size - 1] = + (ss->next_index == 0 ? 0 : + ss->next_index / ss->NumComponents - 1); + return status; +} + +const stream_template s_IE_template = { + &st_IE_state, s_IE_init, s_IE_process, 1, 1, + 0 /* NULL */, s_IE_set_defaults +}; + +#if 0 + +/* Test code */ +void +test_IE(void) +{ + const stream_template *const template = &s_IE_template; + stream_IE_state state; + stream_state *const ss = (stream_state *)&state; + static const float decode[6] = {1, 0, 1, 0, 1, 0}; + static const byte in[] = { + /* + * Each row is 3 pixels x 3 components x 4 bits. Processing the + * first two rows doesn't cause an error; processing all 3 rows + * does. + */ + 0x12, 0x35, 0x67, 0x9a, 0xb0, + 0x56, 0x7d, 0xef, 0x12, 0x30, + 0x88, 0x88, 0x88, 0x88, 0x80 + }; + byte table[3 * 5]; + int n; + + template->set_defaults(ss); + state.BitsPerComponent = 4; + state.NumComponents = 3; + state.Width = 3; + state.BitsPerIndex = 2; + state.Decode = decode; + gs_bytestring_from_bytes(&state.Table, table, 0, sizeof(table)); + for (n = 10; n <= 15; n += 5) { + stream_cursor_read r; + stream_cursor_write w; + byte out[100]; + int status; + + s_IE_init(ss); + r.ptr = in; --r.ptr; + r.limit = r.ptr + n; + w.ptr = out; --w.ptr; + w.limit = w.ptr + sizeof(out); + memset(table, 0xcc, sizeof(table)); + memset(out, 0xff, sizeof(out)); + dprintf1("processing %d bytes\n", n); + status = template->process(ss, &r, &w, true); + dprintf3("%d bytes read, %d bytes written, status = %d\n", + (int)(r.ptr + 1 - in), (int)(w.ptr + 1 - out), status); + debug_dump_bytes(table, table + sizeof(table), "table"); + debug_dump_bytes(out, w.ptr + 1, "out"); + } +} + +#endif + +/* ---------------- Downsampling ---------------- */ + +/* Return the number of samples after downsampling. */ +int +s_Downsample_size_out(int size_in, int factor, bool pad) +{ + return ((pad ? size_in + factor - 1 : size_in) / factor); +} + +static void +s_Downsample_set_defaults(register stream_state * st) +{ + stream_Downsample_state *const ss = (stream_Downsample_state *)st; + + s_Downsample_set_defaults_inline(ss); +} + +/* ------ Subsample ------ */ + +gs_private_st_simple(st_Subsample_state, stream_Subsample_state, + "stream_Subsample_state"); + +/* Initialize the state. */ +static int +s_Subsample_init(stream_state * st) +{ + stream_Subsample_state *const ss = (stream_Subsample_state *) st; + + ss->x = ss->y = 0; + return 0; +} + +/* Process one buffer. */ +static int +s_Subsample_process(stream_state * st, stream_cursor_read * pr, + stream_cursor_write * pw, bool last) +{ + stream_Subsample_state *const ss = (stream_Subsample_state *) st; + const byte *p = pr->ptr; + const byte *rlimit = pr->limit; + byte *q = pw->ptr; + byte *wlimit = pw->limit; + int spp = ss->Colors; + int width = ss->WidthIn, height = ss->HeightIn; + int xf = ss->XFactor, yf = ss->YFactor; + int xf2 = xf / 2, yf2 = yf / 2; + int xlimit = (width / xf) * xf, ylimit = (height / yf) * yf; + int xlast = + (ss->padX && xlimit < width ? xlimit + (width % xf) / 2 : -1); + int ylast = + (ss->padY && ylimit < height ? ylimit + (height % yf) / 2 : -1); + int x = ss->x, y = ss->y; + int status = 0; + + if_debug4('w', "[w]subsample: x=%d, y=%d, rcount=%ld, wcount=%ld\n", + x, y, (long)(rlimit - p), (long)(wlimit - q)); + for (; rlimit - p >= spp; p += spp) { + if (((y % yf == yf2 && y < ylimit) || y == ylast) && + ((x % xf == xf2 && x < xlimit) || x == xlast) + ) { + if (wlimit - q < spp) { + status = 1; + break; + } + memcpy(q + 1, p + 1, spp); + q += spp; + } + if (++x == width) + x = 0, ++y; + } + if_debug5('w', + "[w]subsample: x'=%d, y'=%d, read %ld, wrote %ld, status = %d\n", + x, y, (long)(p - pr->ptr), (long)(q - pw->ptr), status); + pr->ptr = p; + pw->ptr = q; + ss->x = x, ss->y = y; + return status; +} + +const stream_template s_Subsample_template = { + &st_Subsample_state, s_Subsample_init, s_Subsample_process, 4, 4, + 0 /* NULL */, s_Downsample_set_defaults +}; + +/* ------ Average ------ */ + +private_st_Average_state(); + +/* Set default parameter values (actually, just clear pointers). */ +static void +s_Average_set_defaults(stream_state * st) +{ + stream_Average_state *const ss = (stream_Average_state *) st; + + s_Downsample_set_defaults(st); + /* Clear pointers */ + ss->sums = 0; +} + +/* Initialize the state. */ +static int +s_Average_init(stream_state * st) +{ + stream_Average_state *const ss = (stream_Average_state *) st; + + ss->sum_size = + ss->Colors * ((ss->WidthIn + ss->XFactor - 1) / ss->XFactor); + ss->copy_size = ss->sum_size - + (ss->padX || (ss->WidthIn % ss->XFactor == 0) ? 0 : ss->Colors); + ss->sums = + (uint *)gs_alloc_byte_array(st->memory, ss->sum_size, + sizeof(uint), "Average sums"); + if (ss->sums == 0) + return ERRC; /****** WRONG ******/ + memset(ss->sums, 0, ss->sum_size * sizeof(uint)); + return s_Subsample_init(st); +} + +/* Release the state. */ +static void +s_Average_release(stream_state * st) +{ + stream_Average_state *const ss = (stream_Average_state *) st; + + gs_free_object(st->memory, ss->sums, "Average sums"); +} + +/* Process one buffer. */ +static int +s_Average_process(stream_state * st, stream_cursor_read * pr, + stream_cursor_write * pw, bool last) +{ + stream_Average_state *const ss = (stream_Average_state *) st; + const byte *p = pr->ptr; + const byte *rlimit = pr->limit; + byte *q = pw->ptr; + byte *wlimit = pw->limit; + int spp = ss->Colors; + int width = ss->WidthIn; + int xf = ss->XFactor, yf = ss->YFactor; + int x = ss->x, y = ss->y; + uint *sums = ss->sums; + int status = 0; + +top: + if (y == yf || (last && p >= rlimit && ss->padY && y != 0)) { + /* We're copying averaged values to the output. */ + int ncopy = min(ss->copy_size - x, wlimit - q); + + if (ncopy) { + int scale = xf * y; + + while (--ncopy >= 0) + *++q = (byte) (sums[x++] / scale); + } + if (x < ss->copy_size) { + status = 1; + goto out; + } + /* Done copying. */ + x = y = 0; + memset(sums, 0, ss->sum_size * sizeof(uint)); + } + while (rlimit - p >= spp) { + uint *bp = sums + x / xf * spp; + int i; + + for (i = spp; --i >= 0;) + *bp++ += *++p; + if (++x == width) { + x = 0; + ++y; + goto top; + } + } +out: + pr->ptr = p; + pw->ptr = q; + ss->x = x, ss->y = y; + return status; +} + +const stream_template s_Average_template = { + &st_Average_state, s_Average_init, s_Average_process, 4, 4, + s_Average_release, s_Average_set_defaults +}; + +/* ---------------- Image compression chooser ---------------- */ + +private_st_compr_chooser_state(); + +/* Initialize the state. */ +static int +s_compr_chooser_init(stream_state * st) +{ + stream_compr_chooser_state *const ss = (stream_compr_chooser_state *) st; + + ss->choice = 0; + ss->width = ss->height = ss->depth = ss->bits_per_sample = 0; + ss->sample = 0; + ss->samples_count = 0; + ss->bits_left = 0; + ss->packed_data = 0; + ss->lower_plateaus = ss->upper_plateaus = 0; + ss->gradients = 0; + return 0; +} + +/* Set image dimensions. */ +int +s_compr_chooser_set_dimensions(stream_compr_chooser_state * ss, int width, + int height, int depth, int bits_per_sample) +{ + ss->width = width; + ss->height = height; + ss->depth = depth; + ss->bits_per_sample = bits_per_sample; + ss->sample = gs_alloc_bytes(ss->memory, width * depth, "s_compr_chooser_set_dimensions"); + if (ss->sample == 0) + return_error(gs_error_VMerror); + return 0; +} + +/* Release state. */ +static void +s_compr_chooser_release(stream_state * st) +{ + stream_compr_chooser_state *const ss = (stream_compr_chooser_state *) st; + + gs_free_object(ss->memory, ss->sample, "s_compr_chooser_release"); +} + +/* Estimate a row for photo/lineart recognition. */ +static void +s_compr_chooser__estimate_row(stream_compr_chooser_state *const ss, byte *p) +{ + /* This function uses a statistical algorithm being not well defined. + + We compute areas covered by gradients, + separately with small width (line art) + and with big width (photo). + Making the choice based on the areas. + + Note that we deal with horizontal frequencies only. + Dealing with vertical ones would be too expensive. + */ + const int delta = 256 / 16; /* about 1/16 of the color range */ + const int max_lineart_boundary_width = 3; /* pixels */ + const int max_gradient_constant = 10; /* pixels */ + int i, j0 = 0, j1 = 0; + int w0 = p[0], w1 = p[0], v; + ulong plateau_count = 0, lower_plateaus = 0; + ulong upper_plateaus = 0, gradients = 0; + bool lower = false, upper = false; + + for (i = 1; i < ss->width; i++) { + v = p[i]; + if (!lower) { + if (w1 < v) { + if (!upper) + j1 = i - 1; + w1 = v; + upper = true; + } else if (w1 == v && j1 < i - max_gradient_constant) + j1 = i - max_gradient_constant; /* inner constant plateaw */ + else if (upper && w1 - delta > v) { + /* end of upper plateau at w1-delta...w1 */ + for (j0 = i - 1; j0 > j1 && w1 - delta <= p[j0]; j0--) DO_NOTHING; + /* upper plateau j0+1...i-1 */ + if(j0 > 0 && i < ss->width - 1) /* ignore sides */ + upper_plateaus += i - j0; + plateau_count ++; + if (j0 > j1) { + /* upgrade j1...j0 */ + if (j0 > j1 + max_lineart_boundary_width) + gradients += j0 - j1; + } + j1 = i; + upper = false; + w0 = w1; + continue; + } + } + if (!upper) { + if (w0 > v) { + if (!lower) + j1 = i - 1; + w0 = v; + lower = true; + } else if (w0 == v && j1 < i - max_gradient_constant) + j1 = i - max_gradient_constant; /* inner constant plateaw */ + else if (lower && w0 + delta < v) { + /* end of lower plateau at w0...w0+delta */ + for (j0 = i - 1; j0 > j1 && w0 + delta >= p[j0]; j0--) DO_NOTHING; + /* lower plateau j0+1...i-1 */ + if(j0 > 0 && i < ss->width - 1) /* ignore sides */ + lower_plateaus += i - j0; + plateau_count ++; + if (j0 > j1) { + /* downgrade j1...j0 */ + if (j0 > j1 + max_lineart_boundary_width) + gradients += j0 - j1; + } + j1 = i; + lower = false; + w1 = w0; + } + } + } + if (plateau_count > ss->width / 6) { + /* Possibly a dithering, can't recognize. + It would be better to estimate frequency histogram rather than + rough quantity, but we hope that the simpler test can work fine. + */ + } else if (!plateau_count) /* a pseudo-constant color through entire row */ + DO_NOTHING; /* ignore such lines */ + else { + int plateaus; + ss->lower_plateaus += lower_plateaus; + ss->upper_plateaus += upper_plateaus; + ss->gradients += gradients; + plateaus = min(ss->lower_plateaus, ss->upper_plateaus); /* (fore/back)ground */ + if (ss->gradients >= 10000 && ss->gradients > plateaus / 6) + ss->choice = 1; /* choice is made : photo */ + else if (plateaus >= 100000 && plateaus / 5000 >= ss->gradients) + ss->choice = 2; /* choice is made : lineart */ + } +} + +/* Recognize photo/lineart. */ +static void +s_compr_chooser__recognize(stream_compr_chooser_state * ss) +{ + int i; + byte *p = ss->sample; + + for (i = 0; i < ss->depth; i++, p += ss->width) + s_compr_chooser__estimate_row(ss, p); + /* todo: make decision */ +} + +/* Uppack data and recognize photo/lineart. */ +static void +s_compr_chooser__unpack_and_recognize(stream_compr_chooser_state *const ss, + const byte *data, int length) +{ + /* + * Input samples are packed ABCABCABC..., but the sample[] array of + * unpacked values is stored AAA...BBB...CCC. i counts samples within + * a pixel, multiplied by width; j counts pixels. + */ + uint i = (ss->samples_count % ss->depth) * ss->width; + uint j = ss->samples_count / ss->depth; + const byte *p = data; + int l = length; + + while (l) { + if (ss->bits_left < 8) { + uint k = (sizeof(ss->packed_data) * 8 - ss->bits_left) / 8; + + k = min(k, l); + for (; k; k--, l--, p++, ss->bits_left += 8) + ss->packed_data = (ss->packed_data << 8) + *p; + } + while (ss->bits_left >= ss->bits_per_sample) { + uint k = ss->bits_left - ss->bits_per_sample; + ulong v = ss->packed_data >> k; + + ss->packed_data -= (v << k); + ss->bits_left -= ss->bits_per_sample; + if (ss->bits_per_sample > 8) + v >>= ss->bits_per_sample - 8; + else + v <<= 8 - ss->bits_per_sample; + ss->sample[i + j] = (byte)v; /* scaled to 0...255 */ + i += ss->width; + if (i >= ss->width * ss->depth) + i = 0, j++; + ss->samples_count++; + if (ss->samples_count >= ss->width * ss->depth) { + s_compr_chooser__recognize(ss); + ss->packed_data = 0; + ss->bits_left = 0; + ss->samples_count = 0; + i = j = 0; + } + } + } +} + +/* Process a buffer. */ +static int +s_compr_chooser_process(stream_state * st, stream_cursor_read * pr, + stream_cursor_write * pw, bool last) +{ + stream_compr_chooser_state *const ss = (stream_compr_chooser_state *) st; + int l = pr->limit - pr->ptr; + + if (ss->width >= 3) /* Can't process narrow images. */ + s_compr_chooser__unpack_and_recognize(ss, pr->ptr + 1, l); + pr->ptr += l; + return 0; +} + +const stream_template s_compr_chooser_template = { + &st_compr_chooser_state, s_compr_chooser_init, s_compr_chooser_process, 1, 1, + s_compr_chooser_release, 0 /* NULL */ +}; + +/* Get choice */ +uint +s_compr_chooser__get_choice(stream_compr_chooser_state *ss, bool force) +{ + ulong plateaus = min(ss->lower_plateaus, ss->upper_plateaus); + + if (ss->choice) + return ss->choice; + if (force) { + if (ss->gradients > plateaus / 12) /* messenger16.pdf, page 3. */ + return 1; /* photo */ + else if (plateaus / 5000 >= ss->gradients) + return 2; /* lineart */ + } + return 0; +} + +/* ---------------- Am image color conversion filter ---------------- */ + +private_st_image_colors_state(); + +/* Initialize the state. */ +static int +s_image_colors_init(stream_state * st) +{ + stream_image_colors_state *const ss = (stream_image_colors_state *) st; + + ss->width = ss->height = ss->depth = ss->bits_per_sample = 0; + ss->output_bits_buffer = 0; + ss->output_bits_buffered = 0; + ss->output_depth = 1; + ss->output_component_index = ss->output_depth; + ss->output_bits_per_sample = 1; + ss->output_component_bits_written = 0; + ss->raster = 0; + ss->row_bits = 0; + ss->row_bits_passed = 0; + ss->row_alignment_bytes = 0; + ss->row_alignment_bytes_left = 0; + ss->input_component_index = 0; + ss->input_bits_buffer = 0; + ss->input_bits_buffered = 0; + ss->convert_color = 0; + ss->pcs = 0; + ss->pdev = 0; + ss->pis = 0; + return 0; +} + +static int +s_image_colors_convert_color_to_mask(stream_image_colors_state *ss) +{ + int i, ii; + + for (i = ii = 0; i < ss->depth; i++, ii += 2) + if (ss->input_color[i] < ss->MaskColor[ii] || + ss->input_color[i] > ss->MaskColor[ii + 1]) + break; + ss->output_color[0] = (i < ss->depth ? 1 : 0); + return 0; +} + +static int +s_image_colors_convert_to_device_color(stream_image_colors_state * ss) +{ + gs_client_color cc; + gx_device_color dc; + int i, code; + double v0 = (1 << ss->bits_per_sample) - 1; + double v1 = (1 << ss->output_bits_per_sample) - 1; + + for (i = 0; i < ss->depth; i++) + cc.paint.values[i] = ss->input_color[i] * + (ss->Decode[i * 2 + 1] - ss->Decode[i * 2]) / v0 + ss->Decode[i * 2]; + + code = ss->pcs->type->remap_color(&cc, ss->pcs, &dc, ss->pis, + ss->pdev, gs_color_select_texture); + if (code < 0) + return code; + for (i = 0; i < ss->output_depth; i++) { + uint m = (1 << ss->pdev->color_info.comp_bits[i]) - 1; + uint w = (dc.colors.pure >> ss->pdev->color_info.comp_shift[i]) & m; + + ss->output_color[i] = (uint)(v1 * w / m + 0.5); + } + return 0; +} + +/* Set masc colors dimensions. */ +void +s_image_colors_set_mask_colors(stream_image_colors_state * ss, uint *MaskColor) +{ + ss->convert_color = s_image_colors_convert_color_to_mask; + memcpy(ss->MaskColor, MaskColor, ss->depth * sizeof(MaskColor[0]) * 2); +} + +/* Set image dimensions. */ +void +s_image_colors_set_dimensions(stream_image_colors_state * ss, + int width, int height, int depth, int bits_per_sample) +{ + ss->width = width; + ss->height = height; + ss->depth = depth; + ss->bits_per_sample = bits_per_sample; + ss->row_bits = bits_per_sample * depth * width; + ss->raster = bitmap_raster(ss->row_bits); + ss->row_alignment_bytes = 0; /* (ss->raster * 8 - ss->row_bits) / 8) doesn't work. */ +} + +void +s_image_colors_set_color_space(stream_image_colors_state * ss, gx_device *pdev, + const gs_color_space *pcs, const gs_imager_state *pis, + float *Decode) +{ + ss->output_depth = pdev->color_info.num_components; + ss->output_component_index = ss->output_depth; + ss->output_bits_per_sample = pdev->color_info.comp_bits[0]; /* Same precision for all components. */ + ss->convert_color = s_image_colors_convert_to_device_color; + ss->pdev = pdev; + ss->pcs = pcs; + ss->pis = pis; + memcpy(ss->Decode, Decode, ss->depth * sizeof(Decode[0]) * 2); +} + + +/* Process a buffer. */ +static int +s_image_colors_process(stream_state * st, stream_cursor_read * pr, + stream_cursor_write * pw, bool last) +{ + stream_image_colors_state *const ss = (stream_image_colors_state *) st; + + for (;;) { + if (pw->ptr >= pw->limit) + return 1; + if (ss->row_bits_passed >= ss->row_bits) { + ss->row_alignment_bytes_left = ss->row_alignment_bytes; + ss->input_bits_buffered = 0; + ss->input_bits_buffer = 0; /* Just to simplify the debugging. */ + if (ss->output_bits_buffered) { + *(++pw->ptr) = ss->output_bits_buffer; + ss->output_bits_buffered = 0; + ss->output_bits_buffer = 0; + } + ss->row_bits_passed = 0; + continue; + } + if (ss->row_alignment_bytes_left) { + uint k = pr->limit - pr->ptr; + + if (k > ss->row_alignment_bytes_left) + k = ss->row_alignment_bytes_left; + pr->ptr += k; + ss->row_alignment_bytes_left -= k; + if (pr->ptr >= pr->limit) + return 0; + } + if (ss->output_component_index < ss->output_depth) { + for (;ss->output_component_index < ss->output_depth;) { + uint fitting = (uint)(8 - ss->output_bits_buffered); + uint v, w, u, n, m; + + if (pw->ptr >= pw->limit) + return 1; + v = ss->output_color[ss->output_component_index]; + n = ss->output_bits_per_sample - ss->output_component_bits_written; /* no. of bits left */ + w = v - ((v >> n) << n); /* the current component without written bits. */ + if (fitting > n) + fitting = n; /* no. of bits to write. */ + m = n - fitting; /* no. of bits will left. */ + u = w >> m; /* bits to write (near lsb). */ + ss->output_bits_buffer |= u << (8 - ss->output_bits_buffered - fitting); + ss->output_bits_buffered += fitting; + if (ss->output_bits_buffered >= 8) { + *(++pw->ptr) = ss->output_bits_buffer; + ss->output_bits_buffered = 0; + ss->output_bits_buffer = 0; + } + ss->output_component_bits_written += fitting; + if (ss->output_component_bits_written >= ss->output_bits_per_sample) { + ss->output_component_index++; + ss->output_component_bits_written = 0; + } + } + ss->row_bits_passed += ss->bits_per_sample * ss->depth; + continue; + } + if (ss->input_bits_buffered < ss->bits_per_sample) { + if (pr->ptr >= pr->limit) + return 0; + ss->input_bits_buffer = (ss->input_bits_buffer << 8) | *++pr->ptr; + ss->input_bits_buffered += 8; + /* fixme: delay shifting the input ptr until input_bits_buffer is cleaned. */ + } + if (ss->input_bits_buffered >= ss->bits_per_sample) { + uint w; + + ss->input_bits_buffered -= ss->bits_per_sample; + ss->input_color[ss->input_component_index] = w = ss->input_bits_buffer >> ss->input_bits_buffered; + ss->input_bits_buffer &= ~(w << ss->input_bits_buffered); + ss->input_component_index++; + if (ss->input_component_index >= ss->depth) { + int code = ss->convert_color(ss); + + if (code < 0) + return ERRC; + ss->output_component_index = 0; + ss->input_component_index = 0; + } + } + } +} + +const stream_template s__image_colors_template = { + &st_stream_image_colors_state, s_image_colors_init, s_image_colors_process, 1, 1, + NULL, NULL +}; + |