/* Copyright (C) 1997, 2000 artofcode LLC. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA, 02111-1307. */ /*$Id: gdevpsds.c,v 1.4.6.1.2.1 2003/01/17 00:49:01 giles Exp $ */ /* Image processing streams for PostScript and PDF writers */ #include "gx.h" #include "memory_.h" #include "gserrors.h" #include "gxdcconv.h" #include "gdevpsds.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. */ private 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; } private 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; } private 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; } private 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\ }\ } private 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" */ private 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;\ }\ } private 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). */ private void s_C2R_set_defaults(stream_state * st) { stream_C2R_state *const ss = (stream_C2R_state *) st; ss->pis = 0; } /* Process one buffer. */ private 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(); private 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 private 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. */ private 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. */ private 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. */ private 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); } private 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. */ private 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. */ private 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). */ private 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. */ private 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. */ private 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. */ private 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 };