/* * * Test pattern generator for Gutenprint * * Copyright 2001 Robert Krawitz * * 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, see . */ /* * This sample program may be used to generate test patterns. It also * serves as an example of how to use the Gutenprint API. * * As the purpose of this program is to allow fine grained control over * the output, it uses the raw CMYK output type. This feeds 16 bits each * of CMYK to the driver. This mode performs no correction on the data; * it passes it directly to the dither engine, performing no color, * density, gamma, etc. correction. Most programs will use one of the * other modes (RGB, density and gamma corrected 8-bit CMYK, grayscale, or * black and white). */ #include #include #include #include #include #include "testpattern.h" #include #include #pragma GCC diagnostic ignored "-Wredundant-decls" extern int yyparse(void); static const char *Image_get_appname(stp_image_t *image); static void Image_conclude(stp_image_t *image); static void Image_init(stp_image_t *image); static stp_image_status_t Image_get_row(stp_image_t *image, unsigned char *data, size_t byte_limit, int row); static int Image_height(stp_image_t *image); static void Image_reset(stp_image_t *image); static int Image_width(stp_image_t *image); static int Image_is_valid = 0; static stp_image_t theImage = { Image_init, Image_reset, Image_width, Image_height, Image_get_row, Image_get_appname, Image_conclude, NULL }; stp_vars_t *global_vars = NULL; double global_levels[STP_CHANNEL_LIMIT]; double global_gammas[STP_CHANNEL_LIMIT]; double global_gamma; int global_steps; int global_halt_on_error = 0; double global_ink_limit; int global_noblackline; int global_colorline; int global_printer_width; int global_printer_height; int global_band_height; int global_n_testpatterns; char *global_printer = NULL; double global_density; double global_xtop; double global_xleft; size_mode_t global_size_mode; double global_hsize; double global_vsize; const char *global_image_type; int global_bit_depth; int global_channel_depth; int global_invert_data = 0; int global_use_raw_cmyk; int global_did_something; int global_noscale = 0; int global_suppress_output = 0; int global_quiet = 0; int global_fail_verify_ok = 0; int global_round_size = 0; char *global_output = NULL; FILE *output = NULL; int write_to_process = 0; int start_job = 0; int end_job = 0; int passes = 0; int failures = 0; int skipped = 0; size_t bytes_written = 0; static testpattern_t *static_testpatterns; static size_t c_strlen(const char *s) { return strlen(s); } static double roundto(double n) { if (global_round_size) return (double) ((int) n); else return n; } static char * c_strndup(const char *s, int n) { char *ret; if (!s || n < 0) { ret = stp_malloc(1); ret[0] = 0; return ret; } else { ret = stp_malloc(n + 1); memcpy(ret, s, n); ret[n] = 0; return ret; } } char * c_strdup(const char *s) { char *ret; if (!s) { ret = stp_malloc(1); ret[0] = 0; return ret; } else return c_strndup(s, c_strlen(s)); } static void clear_testpattern(testpattern_t *p) { int i; for (i = 0; i < STP_CHANNEL_LIMIT; i++) { p->d.pattern.mins[i] = 0; p->d.pattern.vals[i] = 0; p->d.pattern.gammas[i] = 1; p->d.pattern.levels[i] = 0; } } testpattern_t * get_next_testpattern(void) { static int internal_n_testpatterns = 0; if (global_n_testpatterns == -1) { static_testpatterns = stp_malloc(sizeof(testpattern_t)); global_n_testpatterns = 0; internal_n_testpatterns = 1; clear_testpattern(&(static_testpatterns[0])); return &(static_testpatterns[0]); } else if (global_n_testpatterns + 1 >= internal_n_testpatterns) { internal_n_testpatterns *= 2; static_testpatterns = stp_realloc(static_testpatterns, internal_n_testpatterns * sizeof(testpattern_t)); } global_n_testpatterns++; clear_testpattern(&(static_testpatterns[global_n_testpatterns])); return &(static_testpatterns[global_n_testpatterns]); } static void writefunc(void *file, const char *buf, size_t bytes) { FILE *prn = (FILE *)file; bytes_written += bytes; if (! file) return; if (file == stderr) { if (!global_quiet) fwrite(buf, 1, bytes, prn); } else if (!global_suppress_output) { fwrite(buf, 1, bytes, prn); } } void close_output(void) { if (output && output != stdout) { if (write_to_process) (void) pclose(output); else (void) fclose(output); output = NULL; } } static void initialize_global_parameters(void) { int i; for (i = 0; i < STP_CHANNEL_LIMIT; i++) { global_levels[i] = 1.0; global_gammas[i] = 1.0; } global_gamma = 1.0; global_steps = 256; global_ink_limit = 1.0; global_noblackline = 0; global_printer_width = 0; global_printer_height = 0; global_band_height = 0; global_n_testpatterns = -1; if (global_printer) free(global_printer); /* Allocated with strdup() */ global_printer = NULL; global_density = 1.0; global_xtop = 0; global_xleft = 0; global_hsize = 1.0; global_vsize = 1.0; global_bit_depth = 16; global_channel_depth = 0; global_image_type = "CMYK"; global_use_raw_cmyk = 0; global_did_something = 0; global_invert_data = 0; if (static_testpatterns) stp_free(static_testpatterns); static_testpatterns = NULL; if (global_vars) stp_vars_destroy(global_vars); global_vars = NULL; if (global_printer) free(global_printer); /* Allocated with strdup() */ global_printer = NULL; start_job = 0; end_job = 0; } static int do_print(void) { int status = 0; stp_vars_t *v; const stp_printer_t *the_printer; stp_dimension_t left, right, top, bottom; stp_resolution_t x, y; stp_dimension_t width, height; int retval; stp_parameter_list_t params; int count; int i; char tmp[32]; initialize_global_parameters(); global_vars = stp_vars_create(); stp_set_outfunc(global_vars, writefunc); stp_set_errfunc(global_vars, writefunc); stp_set_outdata(global_vars, stdout); stp_set_errdata(global_vars, stderr); setlocale(LC_ALL, "C"); retval = yyparse(); setlocale(LC_ALL, ""); if (retval) return retval + 1; if (!global_did_something) return 1; v = stp_vars_create(); the_printer = stp_get_printer_by_driver(global_printer); if (!the_printer) { int j; fprintf(stderr, "Unknown printer %s\nValid printers are:\n", global_printer); for (j = 0; j < stp_printer_model_count(); j++) { the_printer = stp_get_printer_by_index(j); fprintf(stderr, "%-16s%s\n", stp_printer_get_driver(the_printer), stp_printer_get_long_name(the_printer)); } return 2; } bytes_written = 0; if (global_output) { if (strcmp(global_output, "-") == 0) output = stdout; else if (strcmp(global_output, "") == 0) output = NULL; else if (global_output[0] == '|') { close_output(); write_to_process = 1; output = popen(global_output+1, "w"); if (! output) { fprintf(stderr, "popen '%s' failed: %s\n", global_output, strerror(errno)); output = NULL; } free(global_output); global_output = NULL; } else { close_output(); output = fopen(global_output, "wb"); if (! output) { fprintf(stderr, "Create %s failed: %s\n", global_output, strerror(errno)); output = NULL; } free(global_output); global_output = NULL; } } stp_set_printer_defaults(v, the_printer); stp_set_outfunc(v, writefunc); stp_set_errfunc(v, writefunc); stp_set_outdata(v, output); stp_set_errdata(v, stderr); stp_set_string_parameter(v, "InputImageType", global_image_type); sprintf(tmp, "%d", global_bit_depth); stp_set_string_parameter(v, "ChannelBitDepth", tmp); if (strcmp(global_image_type, "Raw") == 0) { sprintf(tmp, "%d", global_channel_depth); stp_set_string_parameter(v, "RawChannels", tmp); } stp_set_float_parameter(v, "Density", global_density); stp_set_string_parameter(v, "Quality", "None"); stp_set_string_parameter(v, "ImageType", "None"); params = stp_get_parameter_list(v); count = stp_parameter_list_count(params); for (i = 0; i < count; i++) { const stp_parameter_t *p = stp_parameter_list_param(params, i); if (p->p_type == STP_PARAMETER_TYPE_STRING_LIST) { const char *val = stp_get_string_parameter(global_vars, p->name); if (val && strlen(val) > 0) { stp_set_string_parameter(v, p->name, val); } } else if (p->p_type == STP_PARAMETER_TYPE_INT && stp_check_int_parameter(global_vars, p->name, STP_PARAMETER_ACTIVE)) { int val = stp_get_int_parameter(global_vars, p->name); stp_set_int_parameter(v, p->name, val); } else if (p->p_type == STP_PARAMETER_TYPE_BOOLEAN && stp_check_boolean_parameter(global_vars, p->name, STP_PARAMETER_ACTIVE)) { int val = stp_get_boolean_parameter(global_vars, p->name); stp_set_boolean_parameter(v, p->name, val); } else if (p->p_type == STP_PARAMETER_TYPE_CURVE && stp_check_curve_parameter(global_vars, p->name, STP_PARAMETER_ACTIVE)) { const stp_curve_t *val = stp_get_curve_parameter(global_vars, p->name); stp_set_curve_parameter(v, p->name, val); } else if (p->p_type == STP_PARAMETER_TYPE_DOUBLE && stp_check_float_parameter(global_vars, p->name, STP_PARAMETER_ACTIVE)) { double val = stp_get_float_parameter(global_vars, p->name); stp_set_float_parameter(v, p->name, val); } } stp_set_page_width(v, stp_get_page_width(global_vars)); stp_set_page_height(v, stp_get_page_height(global_vars)); stp_parameter_list_destroy(params); if (stp_check_string_parameter(v, "PageSize", STP_PARAMETER_ACTIVE) && !strcmp(stp_get_string_parameter(v, "PageSize"), "Auto")) { stp_parameter_t desc; stp_describe_parameter(v, "PageSize", &desc); if (desc.p_type == STP_PARAMETER_TYPE_STRING_LIST) stp_set_string_parameter(v, "PageSize", desc.deflt.str); stp_parameter_description_destroy(&desc); } stp_set_printer_defaults_soft(v, the_printer); stp_get_imageable_area(v, &left, &right, &bottom, &top); left = roundto(left); right = roundto(right); bottom = roundto(bottom); top = roundto(top); stp_describe_resolution(v, &x, &y); if (x < 0) x = 300; if (y < 0) y = 300; width = roundto(right - left); height = roundto(bottom - top); switch (global_size_mode) { case SIZE_PT: top += roundto((int) (global_xtop + .5)); left += roundto((int) (global_xleft + .5)); width = roundto((int) (global_hsize + .5)); height = roundto((int) (global_vsize + .5)); break; case SIZE_IN: top += roundto((int) ((global_xtop * 72) + .5)); left += roundto((int) ((global_xleft * 72) + .5)); width = roundto((int) ((global_hsize * 72) + .5)); height = roundto((int) ((global_vsize * 72) + .5)); break; case SIZE_MM: top += roundto((int) ((global_xtop * 72 / 25.4) + .5)); left += roundto((int) ((global_xleft * 72 / 25.4) + .5)); width = roundto((int) ((global_hsize * 72 / 25.4) + .5)); height = roundto((int) ((global_vsize * 72 / 25.4) + .5)); break; case SIZE_RELATIVE: default: top = roundto(top + (height * global_xtop)); left = roundto(left + (width * global_xleft)); width = roundto(width * global_hsize); height = roundto(height * global_vsize); break; } if (width < 1) width = 1; if (height < 1) height = 1; stp_set_width(v, width); stp_set_height(v, height); #if 0 width = (width / global_steps) * global_steps; height = (height / global_n_testpatterns) * global_n_testpatterns; #endif if (global_steps > width) global_steps = width; global_printer_width = width * x / 72; global_printer_height = height * y / 72; global_band_height = global_printer_height / global_n_testpatterns; if (global_band_height == 0) global_band_height = 1; stp_set_left(v, left); stp_set_top(v, top); stp_merge_printvars(v, stp_printer_get_defaults(the_printer)); if (stp_verify(v)) { bytes_written = 0; if (start_job) { stp_start_job(v, &theImage); start_job = 0; } if (stp_print(v, &theImage) != 1) { if (!global_quiet) fputs("FAILED", stderr); failures++; status = 2; if (global_halt_on_error) return status; } else if (bytes_written == 0) { if (!global_quiet) fputs("FAILED: No output", stderr); failures++; status = 2; if (global_halt_on_error) return status; } else passes++; if (end_job) { stp_end_job(v, &theImage); end_job = 0; } } else { if (! global_fail_verify_ok) { if (!global_quiet) fputs("FAILED", stderr); failures++; status = 2; if (global_halt_on_error) return status; } else { if (!global_quiet) fputs("(skipped)", stderr); skipped++; } } if (!global_quiet) fputc('\n', stderr); stp_vars_destroy(v); stp_free(static_testpatterns); static_testpatterns = NULL; return status; } int main(int argc, char **argv) { int c; int status; int global_status = 0; while (1) { c = getopt(argc, argv, "nqyH"); if (c == -1) break; switch (c) { case 'n': global_suppress_output = 1; break; case 'q': global_quiet = 1; break; case 'y': global_fail_verify_ok = 1; break; case 'H': global_halt_on_error = 1; break; default: break; } } stp_init(); output = stdout; while (1) { status = do_print(); if (status == 1) break; else if (status != 0) global_status = 1; } close_output(); if (passes + failures + skipped > 1) fprintf(stderr, "%d pass, %d fail, %d skipped\n", passes, failures, skipped); return global_status; } static void invert_data(unsigned char *data, size_t byte_depth) { int i; size_t total_bytes; total_bytes = global_printer_width * global_channel_depth * byte_depth; for (i = 0; i < total_bytes; i++) data[i] = 0xff ^ data[i]; } /* * Emulate templates with macros -- rlk 20031014 */ #define FILL_CHANNELS_FUNCTION(T, bits) \ static void \ fill_channels_##bits(unsigned char *data, size_t len, size_t scount) \ { \ int i; \ int c; \ scount = global_channel_depth; \ T *s_data = (T *) data; \ unsigned black_val = global_ink_limit * ((1 << bits) - 1); \ unsigned blocksize = len / scount; \ unsigned blocks = blocksize * scount; \ unsigned extra = len - blocks; \ memset(s_data, 0, sizeof(T) * len * scount); \ for (c = 0; c < scount; c++) \ { \ for (i = 0; i < blocksize; i++) \ { \ s_data[c] = black_val; \ s_data += global_channel_depth; \ } \ } \ memset(s_data, 0xff, sizeof(T) * extra * \ global_channel_depth); \ } FILL_CHANNELS_FUNCTION(unsigned short, 16) FILL_CHANNELS_FUNCTION(unsigned char, 8) static void fill_channels(unsigned char *data, size_t len, size_t scount, size_t bytes) { switch (bytes) { case 1: fill_channels_8(data, len, scount); break; case 2: fill_channels_16(data, len, scount); break; } if (global_invert_data) invert_data(data, bytes); } #define FILL_BLACK_FUNCTION(T, bits) \ static void \ fill_black_##bits(unsigned char *data, size_t len, size_t scount) \ { \ int i; \ T *s_data = (T *) data; \ unsigned black_val = global_ink_limit * ((1 << bits) - 1); \ unsigned blocks = (len / scount) * scount; \ unsigned extra = len - blocks; \ if (strcmp(global_image_type, "Raw") == 0) \ { \ for (i = 0; i < (len / scount) * scount; i++) \ { \ memset(s_data, 0, sizeof(T) * global_channel_depth); \ s_data[0] = black_val; \ if (global_channel_depth == 3) \ { \ s_data[1] = black_val; \ s_data[2] = black_val; \ } \ else if (global_channel_depth == 5) \ { \ s_data[2] = black_val; \ s_data[4] = black_val; \ } \ s_data += global_channel_depth; \ } \ memset(s_data, 0xff, sizeof(T) * extra * \ global_channel_depth); \ } \ else if (strcmp(global_image_type, "RGB") == 0) \ { \ for (i = 0; i < (len / scount) * scount; i++) \ { \ memset(s_data, 0, sizeof(T) * 3); \ s_data += 3; \ } \ memset(s_data, 0, sizeof(T) * extra * 3); \ } \ else if (strcmp(global_image_type, "CMY") == 0) \ { \ for (i = 0; i < (len / scount) * scount; i++) \ { \ s_data[0] = black_val; \ s_data[1] = black_val; \ s_data[2] = black_val; \ s_data += 3; \ } \ memset(s_data, 0, sizeof(T) * extra * 3); \ } \ else if (strcmp(global_image_type, "CMYK") == 0) \ { \ for (i = 0; i < (len / scount) * scount; i++) \ { \ memset(s_data, 0, sizeof(T) * 4); \ s_data[3] = black_val; \ s_data += 4; \ } \ memset(s_data, 0, sizeof(T) * extra * 4); \ } \ else if (strcmp(global_image_type, "KCMY") == 0) \ { \ for (i = 0; i < (len / scount) * scount; i++) \ { \ memset(s_data, 0, sizeof(T) * 4); \ s_data[0] = black_val; \ s_data += 4; \ } \ memset(s_data, 0, sizeof(T) * extra * 4); \ } \ else if (strcmp(global_image_type, "Grayscale") == 0) \ { \ for (i = 0; i < (len / scount) * scount; i++) \ { \ memset(s_data, 0, sizeof(T) * 1); \ s_data[0] = black_val; \ s_data += 1; \ } \ memset(s_data, 0, sizeof(T) * extra); \ } \ else if (strcmp(global_image_type, "Whitescale") == 0) \ { \ for (i = 0; i < (len / scount) * scount; i++) \ { \ memset(s_data, 0, sizeof(T) * 1); \ s_data += 1; \ } \ memset(s_data, 0xff, sizeof(T) * extra); \ } \ } FILL_BLACK_FUNCTION(unsigned short, 16) FILL_BLACK_FUNCTION(unsigned char, 8) static void fill_black(unsigned char *data, size_t len, size_t scount, size_t bytes) { if (global_colorline) fill_channels(data, len, global_channel_depth, bytes); else { switch (bytes) { case 1: fill_black_8(data, len, scount); break; case 2: fill_black_16(data, len, scount); break; } if (global_invert_data) invert_data(data, bytes); } } #define FILL_WHITE_FUNCTION(T, bits) \ static void \ fill_white_##bits(unsigned char *data, size_t len, size_t scount) \ { \ int i; \ T *s_data = (T *) data; \ if (strcmp(global_image_type, "Raw") == 0) \ { \ for (i = 0; i < len; i++) \ { \ memset(s_data, 0, sizeof(T) * global_channel_depth); \ s_data += global_channel_depth; \ } \ } \ else if (strcmp(global_image_type, "RGB") == 0) \ { \ for (i = 0; i < len; i++) \ { \ memset(s_data, 0xff, sizeof(T) * 3); \ s_data += 3; \ } \ } \ else if (strcmp(global_image_type, "CMY") == 0) \ { \ for (i = 0; i < len; i++) \ { \ memset(s_data, 0, sizeof(T) * 3); \ s_data += 3; \ } \ } \ else if (strcmp(global_image_type, "CMYK") == 0) \ { \ for (i = 0; i < len; i++) \ { \ memset(s_data, 0, sizeof(T) * 4); \ s_data += 4; \ } \ } \ else if (strcmp(global_image_type, "KCMY") == 0) \ { \ for (i = 0; i < len; i++) \ { \ memset(s_data, 0, sizeof(T) * 4); \ s_data += 4; \ } \ } \ else if (strcmp(global_image_type, "Grayscale") == 0) \ { \ for (i = 0; i < len; i++) \ { \ memset(s_data, 0, sizeof(T) * 1); \ s_data += 1; \ } \ } \ else if (strcmp(global_image_type, "Whitescale") == 0) \ { \ for (i = 0; i < len; i++) \ { \ memset(s_data, 0xff, sizeof(T) * 1); \ s_data += 1; \ } \ } \ } FILL_WHITE_FUNCTION(unsigned short, 16) FILL_WHITE_FUNCTION(unsigned char, 8) static void fill_white(unsigned char *data, size_t len, size_t scount, size_t bytes) { switch (bytes) { case 1: fill_white_8(data, len, scount); break; case 2: fill_white_16(data, len, scount); break; } if (global_invert_data) invert_data(data, bytes); } #define FILL_GRID_FUNCTION(T, bits) \ static void \ fill_grid_##bits(unsigned char *data, size_t len, size_t scount, \ testpattern_t *p) \ { \ int i; \ int xlen = (len / scount) * scount; \ int errdiv = (p->d.grid.ticks) / (xlen - 1); \ int errmod = (p->d.grid.ticks) % (xlen - 1); \ int errval = 0; \ int errlast = -1; \ int errline = 0; \ T *s_data = (T *) data; \ unsigned multiplier = (1 << bits) - 1; \ \ for (i = 0; i < xlen; i++) \ { \ if (errline != errlast) \ { \ errlast = errline; \ s_data[0] = multiplier; \ } \ else \ s_data[0] = 0; \ errval += errmod; \ errline += errdiv; \ if (errval >= xlen - 1) \ { \ errval -= xlen - 1; \ errline++; \ } \ s_data += global_channel_depth; \ } \ } FILL_GRID_FUNCTION(unsigned short, 16) FILL_GRID_FUNCTION(unsigned char, 8) static void fill_grid(unsigned char *data, size_t len, size_t scount, testpattern_t *p, size_t bytes) { switch (bytes) { case 1: fill_grid_8(data, len, scount, p); break; case 2: fill_grid_16(data, len, scount, p); break; } } #define FILL_COLORS_EXTENDED_FUNCTION(T, bits) \ static void \ fill_colors_extended_##bits(unsigned char *data, size_t len, \ size_t scount, testpattern_t *p) \ { \ double mins[STP_CHANNEL_LIMIT]; \ double vals[STP_CHANNEL_LIMIT]; \ double gammas[STP_CHANNEL_LIMIT]; \ int i; \ int j; \ int k; \ int pixels; \ int channel_limit = \ global_channel_depth <= 7 ? 7 : global_channel_depth; \ T *s_data = (T *) data; \ unsigned multiplier = global_noscale ? 1 : (1 << bits) - 1; \ \ for (j = 0; j < channel_limit; j++) \ { \ mins[j] = p->d.pattern.mins[j] == -2 ? \ global_levels[j] : p->d.pattern.mins[j]; \ vals[j] = p->d.pattern.vals[j] == -2 ? \ global_levels[j] : p->d.pattern.vals[j]; \ gammas[j] = \ p->d.pattern.gammas[j] * global_gamma * global_gammas[j]; \ vals[j] -= mins[j]; \ } \ if (scount > len) \ scount = len; \ pixels = len / scount; \ for (i = 0; i < scount; i++) \ { \ double where = (double) i / ((double) scount - 1); \ double val = where; \ double xvals[STP_CHANNEL_LIMIT]; \ \ for (j = 0; j < channel_limit; j++) \ { \ xvals[j] = mins[j] + val * vals[j]; \ xvals[j] = pow(xvals[j], gammas[j]); \ xvals[j] *= global_ink_limit * multiplier; \ xvals[j] += 0.9; \ } \ for (k = 0; k < pixels; k++) \ { \ switch (global_channel_depth) \ { \ case 1: \ s_data[0] = xvals[0]; \ break; \ case 2: \ s_data[0] = xvals[0]; \ s_data[1] = xvals[4]; \ break; \ case 3: \ s_data[0] = xvals[1]; \ s_data[1] = xvals[2]; \ s_data[2] = xvals[3]; \ break; \ case 4: \ s_data[0] = xvals[0]; \ s_data[1] = xvals[1]; \ s_data[2] = xvals[2]; \ s_data[3] = xvals[3]; \ break; \ case 5: \ s_data[0] = xvals[1]; \ s_data[1] = xvals[5]; \ s_data[2] = xvals[2]; \ s_data[3] = xvals[6]; \ s_data[4] = xvals[3]; \ break; \ case 6: \ s_data[0] = xvals[0]; \ s_data[1] = xvals[1]; \ s_data[2] = xvals[5]; \ s_data[3] = xvals[2]; \ s_data[4] = xvals[6]; \ s_data[5] = xvals[3]; \ break; \ case 7: \ s_data[0] = xvals[0]; \ s_data[1] = xvals[4]; \ s_data[2] = xvals[1]; \ s_data[3] = xvals[5]; \ s_data[4] = xvals[2]; \ s_data[5] = xvals[6]; \ s_data[6] = xvals[3]; \ break; \ default: \ for (j = 0; j < global_channel_depth; j++) \ s_data[j] = xvals[j]; \ } \ s_data += global_channel_depth; \ } \ } \ } FILL_COLORS_EXTENDED_FUNCTION(unsigned short, 16) FILL_COLORS_EXTENDED_FUNCTION(unsigned char, 8) static void fill_colors_extended(unsigned char *data, size_t len, size_t scount, testpattern_t *p, size_t bytes) { switch (bytes) { case 1: fill_colors_extended_8(data, len, scount, p); break; case 2: fill_colors_extended_16(data, len, scount, p); break; } } #define FILL_COLORS_FUNCTION(T, bits) \ static void \ fill_colors_##bits(unsigned char *data, size_t len, size_t scount, \ testpattern_t *p) \ { \ double mins[4]; \ double vals[4]; \ double gammas[4]; \ double levels[4]; \ double lower = p->d.pattern.lower; \ double upper = p->d.pattern.upper; \ int i; \ int j; \ int pixels; \ T *s_data = (T *) data; \ unsigned multiplier = global_noscale ? 1 : (1 << bits) - 1; \ \ vals[0] = p->d.pattern.vals[0]; \ mins[0] = p->d.pattern.mins[0]; \ \ for (j = 1; j < 4; j++) \ { \ vals[j] = p->d.pattern.vals[j] == -2 ? global_levels[j] : \ p->d.pattern.vals[j]; \ mins[j] = p->d.pattern.mins[j] == -2 ? global_levels[j] : \ p->d.pattern.mins[j]; \ levels[j] = p->d.pattern.levels[j] == \ -2 ? global_levels[j] : p->d.pattern.levels[j]; \ } \ for (j = 0; j < 4; j++) \ { \ gammas[j] = \ p->d.pattern.gammas[j] * global_gamma * global_gammas[j]; \ vals[j] -= mins[j]; \ } \ \ if (scount > len) \ scount = len; \ pixels = len / scount; \ for (i = 0; i < scount; i++) \ { \ int k; \ double where = (double) i / ((double) scount - 1); \ double cmyv; \ double kv; \ double val = where; \ double xvals[4]; \ for (j = 0; j < 4; j++) \ { \ if (j > 0) \ xvals[j] = mins[j] + val * vals[j]; \ else \ xvals[j] = mins[j] + vals[j]; \ xvals[j] = pow(xvals[j], gammas[j]); \ } \ \ if (where <= lower) \ kv = 0; \ else if (where > upper) \ kv = where; \ else \ kv = (where - lower) * upper / (upper - lower); \ cmyv = vals[0] * (where - kv); \ xvals[0] *= kv; \ for (j = 1; j < 4; j++) \ xvals[j] += cmyv * levels[j]; \ for (j = 0; j < 4; j++) \ { \ if (xvals[j] > 1) \ xvals[j] = 1; \ xvals[j] *= global_ink_limit * multiplier; \ xvals[j] += 0.9; \ } \ for (k = 0; k < pixels; k++) \ { \ switch (global_channel_depth) \ { \ case 0: \ for (j = 0; j < 4; j++) \ s_data[j] = xvals[(j + 1) % 4]; \ s_data += 4; \ break; \ case 1: \ s_data[0] = xvals[0]; \ break; \ case 2: \ s_data[0] = xvals[0]; \ s_data[1] = 0; \ break; \ case 3: \ for (j = 1; j < 4; j++) \ s_data[j - 1] = xvals[j]; \ break; \ case 4: \ for (j = 0; j < 4; j++) \ s_data[j] = xvals[j]; \ break; \ case 6: \ s_data[0] = xvals[0]; \ s_data[1] = xvals[1]; \ s_data[2] = 0; \ s_data[3] = xvals[2]; \ s_data[4] = 0; \ s_data[5] = xvals[3]; \ break; \ case 7: \ for (j = 0; j < 4; j++) \ s_data[j * 2] = xvals[j]; \ for (j = 1; j < 6; j += 2) \ s_data[j] = 0; \ break; \ } \ s_data += global_channel_depth; \ } \ } \ } FILL_COLORS_FUNCTION(unsigned short, 16) FILL_COLORS_FUNCTION(unsigned char, 8) static void fill_colors(unsigned char *data, size_t len, size_t scount, testpattern_t *p, size_t bytes) { if (strcmp(global_image_type, "RGB") == 0) fill_colors_extended(data, len, scount, p, bytes); else { switch (bytes) { case 1: fill_colors_8(data, len, scount, p); break; case 2: fill_colors_16(data, len, scount, p); break; } } } extern FILE *yyin; static void fill_pattern(testpattern_t *p, unsigned char *data, size_t width, size_t s_count, size_t image_depth, size_t byte_depth) { memset(data, 0, global_printer_width * image_depth * byte_depth); switch (p->type) { case E_PATTERN: fill_colors(data, width, s_count, p, byte_depth); break; case E_XPATTERN: fill_colors_extended(data, width, s_count, p, byte_depth); break; case E_GRID: fill_grid(data, width, s_count, p, byte_depth); break; default: break; } } static stp_image_status_t Image_get_row(stp_image_t *image, unsigned char *data, size_t byte_limit, int row) { int depth = global_channel_depth; if (! Image_is_valid) { fputs("Calling Image_get_row with invalid image!\n", stderr); abort(); } if (static_testpatterns[0].type == E_IMAGE) { testpattern_t *t = &(static_testpatterns[0]); int total_read = fread(data, 1, t->d.image.x * depth * global_bit_depth / 8, yyin); if (total_read != t->d.image.x * depth * global_bit_depth / 8) { fputs("Read failed!\n", stderr); return STP_IMAGE_STATUS_ABORT; } if (!global_quiet) fputc('.', stderr); } else { static int previous_band = -1; static int printed_blackline = 0; int band = row / global_band_height; if (row == global_printer_height - 1 && ! global_noblackline) fill_black(data, global_printer_width, global_steps, global_bit_depth / 8); else if (band >= global_n_testpatterns) fill_white(data, global_printer_width, global_steps, global_bit_depth / 8); else { if (band != previous_band) { if (! global_noblackline && printed_blackline == 0) { fill_black(data, global_printer_width, global_steps, global_bit_depth / 8); printed_blackline = 1; return STP_IMAGE_STATUS_OK; } else { previous_band = band; printed_blackline = 0; if (! global_quiet) fputc('.', stderr); } } fill_pattern(&(static_testpatterns[band]), data, global_printer_width, global_steps, depth, global_bit_depth / 8); } } return STP_IMAGE_STATUS_OK; } static void check_valid_image(const char *s) { if (! Image_is_valid) { fputs(s, stderr); abort(); } } static int Image_width(stp_image_t *image) { check_valid_image("Calling Image_width with invalid image!\n"); if (static_testpatterns[0].type == E_IMAGE) return static_testpatterns[0].d.image.x; else return global_printer_width; } static int Image_height(stp_image_t *image) { check_valid_image("Calling Image_height with invalid image!\n"); if (static_testpatterns[0].type == E_IMAGE) return static_testpatterns[0].d.image.y; else return global_printer_height; } static void Image_init(stp_image_t *image) { if (Image_is_valid) { fputs("Calling Image_init with already valid image!\n", stderr); abort(); } Image_is_valid = 1; /* dummy function */ } static void Image_reset(stp_image_t *image) { check_valid_image("Calling Image_reset with invalid image!\n"); /* dummy function */ } static void Image_conclude(stp_image_t *image) { check_valid_image("Calling Image_conclude with invalid image!\n"); Image_is_valid = 0; } static const char * Image_get_appname(stp_image_t *image) { check_valid_image("Calling Image_get_appname with invalid image!\n"); return "Test Pattern"; }