diff options
Diffstat (limited to 'gs/base/sdcparam.c')
-rw-r--r-- | gs/base/sdcparam.c | 614 |
1 files changed, 614 insertions, 0 deletions
diff --git a/gs/base/sdcparam.c b/gs/base/sdcparam.c new file mode 100644 index 000000000..3ab3a59b1 --- /dev/null +++ b/gs/base/sdcparam.c @@ -0,0 +1,614 @@ +/* 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$ */ +/* DCT filter parameter setting and reading */ +#include "memory_.h" +#include "jpeglib_.h" +#include "gserror.h" +#include "gserrors.h" +#include "gstypes.h" +#include "gsmemory.h" +#include "gsparam.h" +#include "strimpl.h" /* sdct.h requires this */ +#include "sdct.h" +#include "sdcparam.h" +#include "sjpeg.h" + +/* Define the DCT parameters. */ +#define dctp(key, type, stype, memb) { key, type, offset_of(stype, memb) } +static const gs_param_item_t s_DCT_param_items[] = +{ +dctp("ColorTransform", gs_param_type_int, stream_DCT_state, ColorTransform), + dctp("QFactor", gs_param_type_float, stream_DCT_state, QFactor), + gs_param_item_end +}; +static const gs_param_item_t jsd_param_items[] = +{ + dctp("Picky", gs_param_type_int, jpeg_stream_data, Picky), + dctp("Relax", gs_param_type_int, jpeg_stream_data, Relax), + gs_param_item_end +}; + +#undef dctp + +/* + * Adobe specifies the values to be supplied in zigzag order. + * For IJG versions newer than v6, we need to convert this order + * to natural array order. Older IJG versions want zigzag order. + */ +#if JPEG_LIB_VERSION >= 61 + /* natural array position of n'th element of JPEG zigzag order */ +static const byte natural_order[DCTSIZE2] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63 +}; + +#define jpeg_order(x) natural_order[x] + /* invert natural_order for getting parameters */ +static const byte inverse_natural_order[DCTSIZE2] = +{ + 0, 1, 5, 6, 14, 15, 27, 28, + 2, 4, 7, 13, 16, 26, 29, 42, + 3, 8, 12, 17, 25, 30, 41, 43, + 9, 11, 18, 24, 31, 40, 44, 53, + 10, 19, 23, 32, 39, 45, 52, 54, + 20, 22, 33, 38, 46, 51, 55, 60, + 21, 34, 37, 47, 50, 56, 59, 61, + 35, 36, 48, 49, 57, 58, 62, 63 +}; + +#define jpeg_inverse_order(x) inverse_natural_order[x] +#else +#define jpeg_order(x) (x) +#define jpeg_inverse_order(x) (x) +#endif + +/* ================ Get parameters ================ */ + +static int +quant_param_string(gs_param_string * pstr, int count, const UINT16 * pvals, + floatp QFactor, gs_memory_t * mem) +{ + byte *data; + int code = 0; + int i; + + data = gs_alloc_string(mem, count, "quant_param_string"); + if (data == 0) + return_error(gs_error_VMerror); + for (i = 0; i < count; ++i) { + floatp val = pvals[jpeg_inverse_order(i)] / QFactor; + + data[i] = + (val < 1 ? (code = 1) : val > 255 ? (code = 255) : (byte) val); + } + pstr->data = data; + pstr->size = count; + pstr->persistent = true; + return code & 1; +} + +static int +quant_param_array(gs_param_float_array * pfa, int count, const UINT16 * pvals, + floatp QFactor, gs_memory_t * mem) +{ + float *data; + int i; + + data = (float *)gs_alloc_byte_array(mem, count, sizeof(float), + "quant_param_array"); + + if (data == 0) + return_error(gs_error_VMerror); + for (i = 0; i < count; ++i) + data[i] = pvals[jpeg_inverse_order(i)] / QFactor; + pfa->data = data; + pfa->size = count; + pfa->persistent = true; + return 0; +} + +int +s_DCT_get_quantization_tables(gs_param_list * plist, + const stream_DCT_state * pdct, const stream_DCT_state * defaults, + bool is_encode) +{ + gs_memory_t *mem = pdct->memory; + jpeg_component_info d_comp_info[4]; + int num_in_tables; + const jpeg_component_info *comp_info; + const jpeg_component_info *default_comp_info; + JQUANT_TBL **table_ptrs; + JQUANT_TBL **default_table_ptrs; + gs_param_array quant_tables; + floatp QFactor = pdct->QFactor; + int i; + int code; + + if (is_encode) { + num_in_tables = pdct->data.compress->cinfo.num_components; + comp_info = pdct->data.compress->cinfo.comp_info; + table_ptrs = pdct->data.compress->cinfo.quant_tbl_ptrs; + if (defaults) { + default_comp_info = defaults->data.compress->cinfo.comp_info; + default_table_ptrs = defaults->data.compress->cinfo.quant_tbl_ptrs; + } + } else { + quant_tables.size = count_of(d_comp_info); + num_in_tables = quant_tables.size; + for (i = 0; i < num_in_tables; ++i) + d_comp_info[i].quant_tbl_no = i; + comp_info = d_comp_info; + table_ptrs = pdct->data.decompress->dinfo.quant_tbl_ptrs; + if (defaults) { + default_comp_info = d_comp_info; + default_table_ptrs = + defaults->data.decompress->dinfo.quant_tbl_ptrs; + } + } + + /* Check whether all tables match defaults. */ + if (defaults) { + bool match = true; + + for (i = 0; i < num_in_tables; ++i) { + JQUANT_TBL *tbl = table_ptrs[comp_info[i].quant_tbl_no]; + JQUANT_TBL *default_tbl = + (default_comp_info == 0 || default_table_ptrs == 0 ? 0 : + default_table_ptrs[default_comp_info[i].quant_tbl_no]); + + if (tbl == default_tbl) + continue; + if (tbl == 0 || default_tbl == 0 || + memcmp(tbl->quantval, default_tbl->quantval, + DCTSIZE2 * sizeof(UINT16)) + ) { + match = false; + break; + } + } + if (match) + return 0; + } + quant_tables.size = num_in_tables; + code = param_begin_write_collection(plist, "QuantTables", + &quant_tables, + gs_param_collection_array); + if (code < 0) + return code; + for (i = 0; i < num_in_tables; ++i) { + char key[3]; + gs_param_string str; + gs_param_float_array fa; + + sprintf(key, "%d", i); + if (QFactor == 1.0) { + code = quant_param_string(&str, DCTSIZE2, + table_ptrs[comp_info[i].quant_tbl_no]->quantval, + QFactor, mem); + switch (code) { + case 0: + code = param_write_string(quant_tables.list, key, &str); + if (code < 0) + return code; /* should dealloc */ + continue; + default: + return code; /* should dealloc */ + case 1: + break; + } + gs_free_const_string(mem, str.data, str.size, + "quant_param_string"); + } + code = quant_param_array(&fa, DCTSIZE2, + table_ptrs[comp_info[i].quant_tbl_no]->quantval, + QFactor, mem); + if (code < 0) + return code; /* should dealloc */ + code = param_write_float_array(quant_tables.list, key, &fa); + if (code < 0) + return code; /* should dealloc */ + } + return param_end_write_dict(plist, "QuantTables", &quant_tables); +} + +static int +pack_huff_table(gs_param_string * pstr, const JHUFF_TBL * table, + gs_memory_t * mem) +{ + int total; + int i; + byte *data; + + for (i = 1, total = 0; i <= 16; ++i) + total += table->bits[i]; + data = gs_alloc_string(mem, 16 + total, "pack_huff_table"); + if (data == 0) + return_error(gs_error_VMerror); + memcpy(data, table->bits + 1, 16); + memcpy(data + 16, table->huffval, total); + pstr->data = data; + pstr->size = 16 + total; + pstr->persistent = true; + return 0; +} + +int +s_DCT_get_huffman_tables(gs_param_list * plist, + const stream_DCT_state * pdct, const stream_DCT_state * defaults, + bool is_encode) +{ + gs_memory_t *mem = pdct->memory; + gs_param_string *huff_data; + gs_param_string_array hta; + int num_in_tables; + jpeg_component_info *comp_info; + JHUFF_TBL **dc_table_ptrs; + JHUFF_TBL **ac_table_ptrs; + int i; + int code = 0; + + if (is_encode) { + dc_table_ptrs = pdct->data.compress->cinfo.dc_huff_tbl_ptrs; + ac_table_ptrs = pdct->data.compress->cinfo.ac_huff_tbl_ptrs; + num_in_tables = pdct->data.compress->cinfo.input_components * 2; + comp_info = pdct->data.compress->cinfo.comp_info; + } else { + dc_table_ptrs = pdct->data.decompress->dinfo.dc_huff_tbl_ptrs; + ac_table_ptrs = pdct->data.decompress->dinfo.ac_huff_tbl_ptrs; + for (i = 2; i > 0; --i) + if (dc_table_ptrs[i - 1] || ac_table_ptrs[i - 1]) + break; + num_in_tables = i * 2; + comp_info = NULL; /* do not set for decompress case */ + } +/****** byte_array IS WRONG ******/ + huff_data = (gs_param_string *) + gs_alloc_byte_array(mem, num_in_tables, sizeof(gs_param_string), + "get huffman tables"); + if (huff_data == 0) + return_error(gs_error_VMerror); + for (i = 0; i < num_in_tables; i += 2) { + if ((code = pack_huff_table(huff_data + i, ac_table_ptrs[i >> 1], mem)) < 0 || + (code = pack_huff_table(huff_data + i + 1, dc_table_ptrs[i >> 1], mem)) + ) + break; + } + if (code < 0) + return code; + hta.data = huff_data; + hta.size = num_in_tables; + hta.persistent = true; + return param_write_string_array(plist, "HuffTables", &hta); +} + +int +s_DCT_get_params(gs_param_list * plist, const stream_DCT_state * ss, + const stream_DCT_state * defaults) +{ + int code = + gs_param_write_items(plist, ss, defaults, s_DCT_param_items); + + if (code >= 0) + code = gs_param_write_items(plist, ss->data.common, + (defaults ? defaults->data.common : + NULL), + jsd_param_items); + return code; +} + +/* ================ Put parameters ================ */ + +stream_state_proc_put_params(s_DCT_put_params, stream_DCT_state); /* check */ + +/* ---------------- Utilities ---------------- */ + +/* + * Get N byte-size values from an array or a string. + * Used for HuffTables, HSamples, VSamples. + */ +int +s_DCT_byte_params(gs_param_list * plist, gs_param_name key, int start, + int count, UINT8 * pvals) +{ + int i; + gs_param_string bytes; + gs_param_float_array floats; + int code = param_read_string(plist, key, &bytes); + + switch (code) { + case 0: + if (bytes.size < start + count) { + code = gs_note_error(gs_error_rangecheck); + break; + } + for (i = 0; i < count; ++i) + pvals[i] = (UINT8) bytes.data[start + i]; + return 0; + default: /* might be a float array */ + code = param_read_float_array(plist, key, &floats); + if (!code) { + if (floats.size < start + count) { + code = gs_note_error(gs_error_rangecheck); + break; + } + for (i = 0; i < count; ++i) { + float v = floats.data[start + i]; + + if (v < 0 || v > 255) { + code = gs_note_error(gs_error_rangecheck); + break; + } + pvals[i] = (UINT8) (v + 0.5); + } + } + } + if (code < 0) + param_signal_error(plist, key, code); + return code; +} + +/* Get N quantization values from an array or a string. */ +static int +quant_params(gs_param_list * plist, gs_param_name key, int count, + UINT16 * pvals, floatp QFactor) +{ + int i; + gs_param_string bytes; + gs_param_float_array floats; + int code = param_read_string(plist, key, &bytes); + + switch (code) { + case 0: + if (bytes.size != count) { + code = gs_note_error(gs_error_rangecheck); + break; + } + for (i = 0; i < count; ++i) { + double v = bytes.data[i] * QFactor; + + pvals[jpeg_order(i)] = + (UINT16) (v < 1 ? 1 : v > 255 ? 255 : v + 0.5); + } + return 0; + default: /* might be a float array */ + code = param_read_float_array(plist, key, &floats); + if (!code) { + if (floats.size != count) { + code = gs_note_error(gs_error_rangecheck); + break; + } + for (i = 0; i < count; ++i) { + double v = floats.data[i] * QFactor; + + pvals[jpeg_order(i)] = + (UINT16) (v < 1 ? 1 : v > 255 ? 255 : v + 0.5); + } + } + } + if (code < 0) + param_signal_error(plist, key, code); + return code; +#undef jpeg_order +} + +/* ---------------- Main procedures ---------------- */ + +/* Put common scalars. */ +int +s_DCT_put_params(gs_param_list * plist, stream_DCT_state * pdct) +{ + int code = + gs_param_read_items(plist, pdct, s_DCT_param_items); + + if (code < 0) + return code; + code = gs_param_read_items(plist, pdct->data.common, jsd_param_items); + if (code < 0) + return code; + if (pdct->data.common->Picky < 0 || pdct->data.common->Picky > 1 || + pdct->data.common->Relax < 0 || pdct->data.common->Relax > 1 || + pdct->ColorTransform < -1 || pdct->ColorTransform > 2 || + pdct->QFactor < 0.0 || pdct->QFactor > 1000000.0 + ) + return_error(gs_error_rangecheck); + return 0; +} + +/* Put quantization tables. */ +int +s_DCT_put_quantization_tables(gs_param_list * plist, stream_DCT_state * pdct, + bool is_encode) +{ + int code; + int i, j; + gs_param_array quant_tables; /* array of strings/arrays */ + int num_in_tables; + int num_out_tables; + jpeg_component_info *comp_info; + JQUANT_TBL **table_ptrs; + JQUANT_TBL *this_table; + + switch ((code = param_begin_read_dict(plist, "QuantTables", + &quant_tables, true)) + ) { + case 1: + return 1; + default: + return param_signal_error(plist, "QuantTables", code); + case 0: + ; + } + if (is_encode) { + num_in_tables = pdct->data.compress->cinfo.num_components; + if (quant_tables.size < num_in_tables) + return_error(gs_error_rangecheck); + comp_info = pdct->data.compress->cinfo.comp_info; + table_ptrs = pdct->data.compress->cinfo.quant_tbl_ptrs; + } else { + num_in_tables = quant_tables.size; + comp_info = NULL; /* do not set for decompress case */ + table_ptrs = pdct->data.decompress->dinfo.quant_tbl_ptrs; + } + num_out_tables = 0; + for (i = 0; i < num_in_tables; ++i) { + char istr[5]; /* i converted to string key */ + UINT16 values[DCTSIZE2]; + + sprintf(istr, "%d", i); + code = quant_params(quant_tables.list, istr, DCTSIZE2, values, + pdct->QFactor); + if (code < 0) + return code; + /* Check for duplicate tables. */ + for (j = 0; j < num_out_tables; j++) { + if (!memcmp(table_ptrs[j]->quantval, values, sizeof(values))) + break; + } + if (comp_info != NULL) + comp_info[i].quant_tbl_no = j; + if (j < num_out_tables) /* found a duplicate */ + continue; + if (++num_out_tables > NUM_QUANT_TBLS) + return_error(gs_error_rangecheck); + this_table = table_ptrs[j]; + if (this_table == NULL) { + this_table = gs_jpeg_alloc_quant_table(pdct); + if (this_table == NULL) + return_error(gs_error_VMerror); + table_ptrs[j] = this_table; + } + memcpy(this_table->quantval, values, sizeof(values)); + } + return 0; +} + +/* Put Huffman tables. */ +static int +find_huff_values(JHUFF_TBL ** table_ptrs, int num_tables, + const UINT8 counts[16], const UINT8 * values, int codes_size) +{ + int j; + + for (j = 0; j < num_tables; ++j) + if (!memcmp(table_ptrs[j]->bits, counts, sizeof(counts)) && + !memcmp(table_ptrs[j]->huffval, values, + codes_size * sizeof(values[0]))) + break; + return j; +} +int +s_DCT_put_huffman_tables(gs_param_list * plist, stream_DCT_state * pdct, + bool is_encode) +{ + int code; + int i, j; + gs_param_array huff_tables; + int num_in_tables; + int ndc, nac; + int codes_size; + jpeg_component_info *comp_info; + JHUFF_TBL **dc_table_ptrs; + JHUFF_TBL **ac_table_ptrs; + JHUFF_TBL **this_table_ptr; + JHUFF_TBL *this_table; + int max_tables = 2; /* baseline limit */ + + switch ((code = param_begin_read_dict(plist, "HuffTables", + &huff_tables, true)) + ) { + case 1: + return 0; + default: + return param_signal_error(plist, "HuffTables", code); + case 0: + ; + } + if (is_encode) { + num_in_tables = pdct->data.compress->cinfo.input_components * 2; + if (huff_tables.size < num_in_tables) + return_error(gs_error_rangecheck); + comp_info = pdct->data.compress->cinfo.comp_info; + dc_table_ptrs = pdct->data.compress->cinfo.dc_huff_tbl_ptrs; + ac_table_ptrs = pdct->data.compress->cinfo.ac_huff_tbl_ptrs; + if (pdct->data.common->Relax) + max_tables = max(pdct->data.compress->cinfo.input_components, 2); + } else { + num_in_tables = huff_tables.size; + comp_info = NULL; /* do not set for decompress case */ + dc_table_ptrs = pdct->data.decompress->dinfo.dc_huff_tbl_ptrs; + ac_table_ptrs = pdct->data.decompress->dinfo.ac_huff_tbl_ptrs; + if (pdct->data.common->Relax) + max_tables = NUM_HUFF_TBLS; + } + ndc = nac = 0; + for (i = 0; i < num_in_tables; ++i) { + char istr[5]; /* i converted to string key */ + UINT8 counts[16], values[256]; + + /* Collect the Huffman parameters. */ + sprintf(istr, "%d", i); + code = s_DCT_byte_params(huff_tables.list, istr, 0, 16, counts); + if (code < 0) + return code; + for (codes_size = 0, j = 0; j < 16; j++) + codes_size += counts[j]; + if (codes_size > 256 /*|| r_size(pa) != codes_size+16 */ ) + return_error(gs_error_rangecheck); + code = s_DCT_byte_params(huff_tables.list, istr, 16, codes_size, + values); + if (code < 0) + return code; + if (i & 1) { + j = find_huff_values(ac_table_ptrs, nac, counts, values, + codes_size); + if (comp_info != NULL) + comp_info[i >> 1].ac_tbl_no = j; + if (j < nac) + continue; + if (++nac > NUM_HUFF_TBLS) + return_error(gs_error_rangecheck); + this_table_ptr = ac_table_ptrs + j; + } else { + j = find_huff_values(dc_table_ptrs, ndc, counts, values, + codes_size); + if (comp_info != NULL) + comp_info[i >> 1].dc_tbl_no = j; + if (j < ndc) + continue; + if (++ndc > NUM_HUFF_TBLS) + return_error(gs_error_rangecheck); + this_table_ptr = dc_table_ptrs + j; + } + this_table = *this_table_ptr; + if (this_table == NULL) { + this_table = gs_jpeg_alloc_huff_table(pdct); + if (this_table == NULL) + return_error(gs_error_VMerror); + *this_table_ptr = this_table; + } + memcpy(this_table->bits, counts, sizeof(counts)); + memcpy(this_table->huffval, values, codes_size * sizeof(values[0])); + } + if (nac > max_tables || ndc > max_tables) + return_error(gs_error_rangecheck); + return 0; +} |