diff options
author | Robin Watts <robin.watts@artifex.com> | 2012-05-22 13:35:31 +0100 |
---|---|---|
committer | Robin Watts <robin.watts@artifex.com> | 2012-05-22 19:42:37 +0100 |
commit | 15cc33536ada0b4cb105110a48df0132539c54db (patch) | |
tree | df9660d76eb72bf83156de928a367d8ccc1aaf28 | |
parent | 4677f28c2a0bf72a6bc89e785cc651d3deedc043 (diff) |
Add downscaler functionality to tiffsep.
Update tiffsep to call the downscaler. This means adding MinFeatureSize
and DownScaleFactor to tiffsep. Also add BitsPerComponent to allow us to
specify 8 (default) or 1 (monochrome). MinFeatureSize is ignored except
in monochrome mode.
This has meant slight reworking of the downscaler to cope with planar
buffers, and its use of get_bits_rectangle rather than get_bits.
Also updated docs, and fixed some leaks on memory allocation failures
within tiffsep.
-rw-r--r-- | gs/base/gdevtsep.c | 185 | ||||
-rw-r--r-- | gs/base/gxdownscale.c | 519 | ||||
-rw-r--r-- | gs/base/gxdownscale.h | 48 | ||||
-rw-r--r-- | gs/base/lib.mak | 2 | ||||
-rw-r--r-- | gs/doc/Devices.htm | 11 |
5 files changed, 367 insertions, 398 deletions
diff --git a/gs/base/gdevtsep.c b/gs/base/gdevtsep.c index 9f19d614d..0710c2093 100644 --- a/gs/base/gdevtsep.c +++ b/gs/base/gdevtsep.c @@ -36,6 +36,7 @@ #include "ctype_.h" #include "gxgetbit.h" #include "gdevppla.h" +#include "gxdownscale.h" /* * Some of the code in this module is based upon the gdevtfnx.c module. @@ -418,6 +419,9 @@ static dev_proc_output_page(tiffseps_output_page); TIFFTAG_COMPRESSION */\ bool close_files; \ long MaxStripSize;\ + long DownScaleFactor;\ + long MinFeatureSize;\ + long BitsPerComponent;\ gs_devn_params devn_params; /* DeviceN generated parameters */\ equivalent_cmyk_color_params equiv_cmyk_colors\ @@ -588,7 +592,10 @@ gs_private_st_composite_final(st_tiffsep_device, tiffsep_device, arch_is_big_endian /* true = big endian; false = little endian */,\ compr /* COMPRESSION_* */,\ true, /* close_files */ \ - TIFF_DEFAULT_STRIP_SIZE /* MaxStripSize */ + TIFF_DEFAULT_STRIP_SIZE,/* MaxStripSize */\ + 1, /* DownScaleFactor */\ + 0, /* MinFeatureSize */\ + 8 /* BitsPerComponent */ /* * Select the default number of components based upon the number of bits @@ -860,6 +867,12 @@ tiffsep_get_params(gx_device * pdev, gs_param_list * plist) ecode = code; if ((code = param_write_long(plist, "MaxStripSize", &pdevn->MaxStripSize)) < 0) ecode = code; + if ((code = param_write_long(plist, "DownScaleFactor", &pdevn->DownScaleFactor)) < 0) + ecode = code; + if ((code = param_write_long(plist, "MinFeatureSize", &pdevn->MinFeatureSize)) < 0) + ecode = code; + if ((code = param_write_long(plist, "BitsPerComponent", &pdevn->BitsPerComponent)) < 0) + ecode = code; return ecode; } @@ -873,6 +886,9 @@ tiffsep_put_params(gx_device * pdev, gs_param_list * plist) const char *param_name; gs_param_string comprstr; bool save_close_files = pdevn->close_files; + long downscale = pdevn->DownScaleFactor; + long mfs = pdevn->MinFeatureSize; + long bpc = pdevn->BitsPerComponent; /* Read BigEndian option as bool */ switch (code = param_read_bool(plist, (param_name = "BigEndian"), &pdevn->BigEndian)) { @@ -916,6 +932,45 @@ tiffsep_put_params(gx_device * pdev, gs_param_list * plist) case 1: break; } + switch (code = param_read_long(plist, + (param_name = "DownScaleFactor"), + &pdevn->DownScaleFactor)) { + case 0: + if (downscale <= 0) + downscale = 1; + break; + case 1: + break; + default: + param_signal_error(plist, param_name, code); + return code; + } + switch (code = param_read_long(plist, (param_name = "MinFeatureSize"), &mfs)) { + case 0: + if ((mfs >= 0) && (mfs <= 4)) { + pdevn->MinFeatureSize = mfs; + break; + } + code = gs_error_rangecheck; + case 1: + break; + default: + param_signal_error(plist, param_name, code); + return code; + } + switch (code = param_read_long(plist, (param_name = "BitsPerComponent"), &bpc)) { + case 0: + if ((bpc == 1) || (bpc == 8)) { + pdevn->BitsPerComponent = bpc; + break; + } + code = gs_error_rangecheck; + case 1: + break; + default: + param_signal_error(plist, param_name, code); + return code; + } pdevn->close_files = false; @@ -1714,8 +1769,13 @@ tiffsep_print_page(gx_device_printer * pdev, FILE * file) int save_numcomps = pdev->color_info.num_components; const char *fmt; gs_parsed_file_name_t parsed; - int non_encodable_count = 0; int plane_count = 0; /* quite compiler */ + int factor = tfdev->DownScaleFactor; + int mfs = tfdev->MinFeatureSize; + int dst_bpc = tfdev->BitsPerComponent; + gx_downscaler_t ds; + int width = tfdev->width / factor; + int height = tfdev->height / factor; /* Print the names of the spot colors */ if (num_order == 0) { @@ -1734,14 +1794,14 @@ tiffsep_print_page(gx_device_printer * pdev, FILE * file) strlen(tfdev->fname), pdev->memory); /* Write the page directory for the CMYK equivalent file. */ - pdev->color_info.depth = 32; /* Create directory for 32 bit cmyk */ - if (tfdev->Compression==COMPRESSION_NONE && - pdev->height > ((unsigned long) 0xFFFFFFFF - ftell(file))/(pdev->width*4)) { /* note width is never 0 in print_page */ - dprintf("CMYK composite file would be too large! Reduce resolution or enable compression.\n"); - return_error(gs_error_rangecheck); /* this will overflow 32 bits */ - } + if (dst_bpc == 8 && !tfdev->comp_file) { + pdev->color_info.depth = 32; /* Create directory for 32 bit cmyk */ + if (tfdev->Compression==COMPRESSION_NONE && + height > ((unsigned long) 0xFFFFFFFF - ftell(file))/(pdev->width*4)) { /* note width is never 0 in print_page */ + dprintf("CMYK composite file would be too large! Reduce resolution or enable compression.\n"); + return_error(gs_error_rangecheck); /* this will overflow 32 bits */ + } - if (!tfdev->comp_file) { code = gx_device_open_output_file((gx_device *)pdev, pdev->fname, true, true, &(tfdev->comp_file)); if (code < 0) return code; @@ -1751,11 +1811,20 @@ tiffsep_print_page(gx_device_printer * pdev, FILE * file) return_error(gs_error_invalidfileaccess); } - code = tiff_set_fields_for_printer(pdev, tfdev->tiff_comp, 1, 0); - if (tfdev->Compression==COMPRESSION_NONE || tfdev->Compression==COMPRESSION_LZW || tfdev->Compression==COMPRESSION_PACKBITS) - tiff_set_cmyk_fields(pdev, tfdev->tiff_comp, 8, tfdev->Compression, tfdev->MaxStripSize); - else - tiff_set_cmyk_fields(pdev, tfdev->tiff_comp, 8, COMPRESSION_LZW, tfdev->MaxStripSize); + if (dst_bpc == 8) { + code = tiff_set_fields_for_printer(pdev, tfdev->tiff_comp, factor, 0); + if (dst_bpc == 1 && (tfdev->Compression==COMPRESSION_NONE || + tfdev->Compression==COMPRESSION_CCITTRLE || + tfdev->Compression==COMPRESSION_CCITTFAX3 || + tfdev->Compression==COMPRESSION_CCITTFAX4)) + tiff_set_cmyk_fields(pdev, tfdev->tiff_comp, 1, tfdev->Compression, tfdev->MaxStripSize); + else if (dst_bpc == 8 && (tfdev->Compression==COMPRESSION_NONE || + tfdev->Compression==COMPRESSION_LZW || + tfdev->Compression==COMPRESSION_PACKBITS)) + tiff_set_cmyk_fields(pdev, tfdev->tiff_comp, 8, tfdev->Compression, tfdev->MaxStripSize); + else + tiff_set_cmyk_fields(pdev, tfdev->tiff_comp, 8, COMPRESSION_LZW, tfdev->MaxStripSize); +} pdev->color_info.depth = save_depth; if (code < 0) return code; @@ -1789,52 +1858,52 @@ tiffsep_print_page(gx_device_printer * pdev, FILE * file) return_error(gs_error_ioerror); } - pdev->color_info.depth = 8; /* Create files for 8 bit gray */ + pdev->color_info.depth = dst_bpc; /* Create files for 8 bit gray */ pdev->color_info.num_components = 1; if (tfdev->Compression==COMPRESSION_NONE && - pdev->height > ((unsigned long) 0xFFFFFFFF - ftell(file))/(pdev->width)) /* note width is never 0 in print_page */ + height*8/dst_bpc > ((unsigned long) 0xFFFFFFFF - ftell(file))/width) /* note width is never 0 in print_page */ return_error(gs_error_rangecheck); /* this will overflow 32 bits */ - code = tiff_set_fields_for_printer(pdev, tfdev->tiff[comp_num], 1, 0); - tiff_set_gray_fields(pdev, tfdev->tiff[comp_num], 8, tfdev->Compression, tfdev->MaxStripSize); + code = tiff_set_fields_for_printer(pdev, tfdev->tiff[comp_num], factor, 0); + tiff_set_gray_fields(pdev, tfdev->tiff[comp_num], dst_bpc, tfdev->Compression, tfdev->MaxStripSize); pdev->color_info.depth = save_depth; pdev->color_info.num_components = save_numcomps; if (code < 0) return code; } - build_cmyk_map(tfdev, num_comp, cmyk_map); + if (dst_bpc == 8) + build_cmyk_map(tfdev, num_comp, cmyk_map); { - int raster_plane = bitmap_raster(pdev->width * 8); + int raster_plane = bitmap_raster(width * 8); byte *planes[GS_CLIENT_COLOR_MAX_COMPONENTS]; - int width = tfdev->width; int cmyk_raster = width * NUM_CMYK_COMPONENTS; int pixel, y; byte * sep_line; - int plane_index, offset_plane; + int plane_index; + int offset_plane = 0; sep_line = gs_alloc_bytes(pdev->memory, cmyk_raster, "tiffsep_print_page"); for (comp_num = 0; comp_num < num_comp; comp_num++ ) TIFFCheckpointDirectory(tfdev->tiff[comp_num]); - TIFFCheckpointDirectory(tfdev->tiff_comp); + if (dst_bpc == 8) + TIFFCheckpointDirectory(tfdev->tiff_comp); /* Write the page data. */ { - gs_int_rect rect; gs_get_bits_params_t params; - rect.q.x = pdev->width; - rect.p.x = 0; - /* Return planar data */ + /* Return planar data */ params.options = (GB_RETURN_POINTER | GB_RETURN_COPY | GB_ALIGN_STANDARD | GB_OFFSET_0 | GB_RASTER_STANDARD | GB_PACKING_PLANAR | GB_COLORS_NATIVE | GB_ALPHA_NONE); params.x_offset = 0; - params.raster = bitmap_raster(pdev->width * pdev->color_info.depth); + params.raster = bitmap_raster(width * pdev->color_info.depth); + memset(planes, 0, sizeof(*planes) * plane_count); if (num_order > 0) { /* In this case, there was a specification for a separation color order, which indicates what colorants we will @@ -1844,10 +1913,12 @@ tiffsep_print_page(gx_device_printer * pdev, FILE * file) still create the composite CMYK output file. */ for (comp_num = 0; comp_num < num_std_colorants; comp_num++) { planes[comp_num] = gs_alloc_bytes(pdev->memory, raster_plane, - "tiffsep_print_page"); + "tiffsep_print_page"); params.data[comp_num] = planes[comp_num]; - if (params.data[comp_num] == NULL) - return_error(gs_error_VMerror); + if (params.data[comp_num] == NULL) { + code = gs_error_VMerror; + goto cleanup; + } } offset_plane = num_std_colorants; /* Now we need to make sure that we do not allocate extra @@ -1865,8 +1936,10 @@ tiffsep_print_page(gx_device_printer * pdev, FILE * file) "tiffsep_print_page"); /* Assign the new plane to the appropriate position */ params.data[plane_index] = planes[plane_count]; - if (params.data[plane_index] == NULL) - return_error(gs_error_VMerror); + if (params.data[plane_index] == NULL) { + code = gs_error_VMerror; + goto cleanup; + } plane_count += 1; } else { /* Assign params.data with the appropriate std. @@ -1882,16 +1955,20 @@ tiffsep_print_page(gx_device_printer * pdev, FILE * file) planes[comp_num] = gs_alloc_bytes(pdev->memory, raster_plane, "tiffsep_print_page"); params.data[comp_num] = planes[comp_num]; - if (params.data[comp_num] == NULL) - return_error(gs_error_VMerror); + if (params.data[comp_num] == NULL) { + code = gs_error_VMerror; + goto cleanup; + } } - offset_plane = 0; } - for (y = 0; y < pdev->height; ++y) { - rect.p.y = y; - rect.q.y = y + 1; - code = dev_proc(pdev, get_bits_rectangle) - ((gx_device *) pdev, &rect, ¶ms, NULL); + code = gx_downscaler_init_planar(&ds, (gx_device *)pdev, ¶ms, + num_comp, factor, mfs, dst_bpc); + if (code < 0) + goto cleanup; + for (y = 0; y < height; ++y) { + code = gx_downscaler_get_bits_rectangle(&ds, ¶ms, y); + if (code < 0) + goto cleanup; /* Write separation data (tiffgray format) */ for (comp_num = 0; comp_num < num_comp; comp_num++ ) { byte *src; @@ -1906,12 +1983,14 @@ tiffsep_print_page(gx_device_printer * pdev, FILE * file) TIFFWriteScanline(tfdev->tiff[comp_num], (tdata_t)sep_line, y, 0); } /* Write CMYK equivalent data (tiff32nc format) */ - build_cmyk_raster_line_fromplanar(params, sep_line, width, - num_comp, cmyk_map, num_order, - tfdev); - TIFFWriteScanline(tfdev->tiff_comp, (tdata_t)sep_line, y, 0); + if (dst_bpc == 8) { + build_cmyk_raster_line_fromplanar(params, sep_line, width, + num_comp, cmyk_map, num_order, + tfdev); + TIFFWriteScanline(tfdev->tiff_comp, (tdata_t)sep_line, y, 0); + } } - gs_free_object(pdev->memory, sep_line, "tiffsep_print_page"); +cleanup: if (num_order > 0) { /* Free up the standard colorants if num_order was set. In this process, we need to make sure that none of them @@ -1928,8 +2007,10 @@ tiffsep_print_page(gx_device_printer * pdev, FILE * file) "tiffsep_print_page"); } } + gx_downscaler_fin(&ds); + gs_free_object(pdev->memory, sep_line, "tiffsep_print_page"); } - code1 = 0; + code1 = code; for (comp_num = 0; comp_num < num_comp; comp_num++ ) { TIFFWriteDirectory(tfdev->tiff[comp_num]); if (fmt) { @@ -1947,7 +2028,8 @@ tiffsep_print_page(gx_device_printer * pdev, FILE * file) } } - TIFFWriteDirectory(tfdev->tiff_comp); + if (dst_bpc == 8) + TIFFWriteDirectory(tfdev->tiff_comp); if (fmt) { code = tiffsep_close_comp_file(tfdev, pdev->fname); } @@ -1961,13 +2043,6 @@ tiffsep_print_page(gx_device_printer * pdev, FILE * file) max(16, num_comp)); #endif - /* - * If we have any non encodable pixels then signal an error. - */ - if (non_encodable_count) { - dlprintf1("WARNING: Non encodable pixels = %d\n", non_encodable_count); - return_error(gs_error_rangecheck); - } /* After page is printed free up the separation list since we may have different spots on the next page */ free_separation_names(tfdev->memory, &(tfdev->devn_params.separations)); diff --git a/gs/base/gxdownscale.c b/gs/base/gxdownscale.c index 55badfc59..b80fe3ce8 100644 --- a/gs/base/gxdownscale.c +++ b/gs/base/gxdownscale.c @@ -37,7 +37,10 @@ enum { /* Mono downscale/error diffusion/min feature size code */ static void down_core(gx_downscaler_t *ds, byte *out_buffer, - int row) + byte *in_buffer, + int row, + int plane, + int span) { int x, xx, y, value; int mask; @@ -47,19 +50,21 @@ static void down_core(gx_downscaler_t *ds, int width = ds->width; int awidth = ds->awidth; int factor = ds->factor; - int span = ds->span; - int *errors = ds->errors; + int *errors = ds->errors + (awidth+3)*plane; byte *mfs_data = ds->mfs_data; const int threshold = factor*factor*128; const int max_value = factor*factor*255; + if (mfs_data) + mfs_data += (awidth+1)*plane; + pad_white = (awidth - width) * factor; if (pad_white < 0) pad_white = 0; if (pad_white) { - inp = ds->data + width*factor; + inp = in_buffer + width*factor; for (y = factor; y > 0; y--) { memset(inp, 0xFF, pad_white); @@ -67,7 +72,7 @@ static void down_core(gx_downscaler_t *ds, } } - inp = ds->data; + inp = in_buffer; if (mfs_data == NULL) { if ((row & 1) == 0) @@ -300,23 +305,20 @@ static void down_core(gx_downscaler_t *ds, } /* Grey downscale code */ -#ifdef DO_STUPID_ERROR_DIFFUSION static void down_core8(gx_downscaler_t *ds, byte *outp, - int row) + byte *in_buffer, + int row, + int plane, + int span) { - int x, xx, y, value; - int mask; - int e_downleft, e_down, e_forward = 0; - int pad_white; - byte *inp; - int width = ds->width; - int awidth = ds->awidth; - int factor = ds->factor; - int span = ds->span; - int *errors = ds->errors; - byte *mfs_data = ds->mfs_data; - int div = factor*factor; + int x, xx, y, value; + int pad_white; + byte *inp; + int width = ds->width; + int awidth = ds->awidth; + int factor = ds->factor; + int div = factor*factor; pad_white = (awidth - width) * factor; if (pad_white < 0) @@ -324,7 +326,7 @@ static void down_core8(gx_downscaler_t *ds, if (pad_white) { - inp = ds->data + width*factor; + inp = in_buffer + width*factor; for (y = factor; y > 0; y--) { memset(inp, 0xFF, pad_white); @@ -332,104 +334,10 @@ static void down_core8(gx_downscaler_t *ds, } } - inp = ds->data; - if ((row & 1) == 0) + inp = in_buffer; { /* Left to Right pass (no min feature size) */ const int back = span * factor -1; - errors += 2; - for (x = awidth; x > 0; x--) - { - int res; - value = e_forward + *errors; - for (xx = factor; xx > 0; xx--) - { - for (y = factor; y > 0; y--) - { - value += *inp; - inp += span; - } - inp -= back; - } - res = (value+(div>>1))/div; - *outp++ = res; - value -= res*div; - e_forward = value * 7/16; - e_downleft = value * 3/16; - e_down = value * 5/16; - value -= e_forward + e_downleft + e_down; - errors[-2] += e_downleft; - errors[-1] += e_down; - *errors++ = value; - } - } - else - { - /* Right to Left pass (no min feature size) */ - const int back = span * factor + 1; - errors += awidth; - inp += awidth*factor-1; - outp += awidth-1; - for (x = awidth; x > 0; x--) - { - int res; - value = e_forward + *errors; - for (xx = factor; xx > 0; xx--) - { - for (y = factor; y > 0; y--) - { - value += *inp; - inp += span; - } - inp -= back; - } - res = (value + (div>>1))/div; - *outp-- = res; - value -= res*div; - e_forward = value * 7/16; - e_downleft = value * 3/16; - e_down = value * 5/16; - value -= e_forward + e_downleft + e_down; - errors[2] += e_downleft; - errors[1] += e_down; - *errors-- = value; - } - } -} -#else -static void down_core8(gx_downscaler_t *ds, - byte *outp, - int row) -{ - int x, xx, y, value; - int pad_white; - byte *inp; - int width = ds->width; - int awidth = ds->awidth; - int factor = ds->factor; - int span = ds->span; - int *errors = ds->errors; - int div = factor*factor; - - pad_white = (awidth - width) * factor; - if (pad_white < 0) - pad_white = 0; - - if (pad_white) - { - inp = ds->data + width*factor; - for (y = factor; y > 0; y--) - { - memset(inp, 0xFF, pad_white); - inp += span; - } - } - - inp = ds->data; - { - /* Left to Right pass (no min feature size) */ - const int back = span * factor -1; - errors += 2; for (x = awidth; x > 0; x--) { value = 0; @@ -446,29 +354,23 @@ static void down_core8(gx_downscaler_t *ds, } } } -#endif -/* RGB downscale/error diffusion code */ -#ifdef DO_STUPID_ERROR_DIFFUSION +/* RGB downscale (no error diffusion) code */ + static void down_core24(gx_downscaler_t *ds, byte *outp, - int row) + byte *in_buffer, + int row, + int plane, + int span) { - int x, xx, y, value; - int mask; - int e_downleft, e_down; - int e_forward_r = 0; - int e_forward_g = 0; - int e_forward_b = 0; - int pad_white; - byte *inp; - int width = ds->width; - int awidth = ds->awidth; - int factor = ds->factor; - int span = ds->span; - int *errors = ds->errors; - byte *mfs_data = ds->mfs_data; - int div = factor*factor; + int x, xx, y, value; + int pad_white; + byte *inp; + int width = ds->width; + int awidth = ds->awidth; + int factor = ds->factor; + int div = factor*factor; pad_white = (awidth - width) * factor * 3; if (pad_white < 0) @@ -476,7 +378,7 @@ static void down_core24(gx_downscaler_t *ds, if (pad_white) { - inp = ds->data + width*factor*3; + inp = in_buffer + width*factor*3; for (y = factor; y > 0; y--) { memset(inp, 0xFF, pad_white); @@ -484,18 +386,15 @@ static void down_core24(gx_downscaler_t *ds, } } - inp = ds->data; - if ((row & 1) == 0) + inp = in_buffer; { /* Left to Right pass (no min feature size) */ const int back = span * factor - 3; const int back2 = factor * 3 - 1; - errors += 6; for (x = awidth; x > 0; x--) { - int res; /* R */ - value = e_forward_r + *errors; + value = 0; for (xx = factor; xx > 0; xx--) { for (y = factor; y > 0; y--) @@ -506,18 +405,9 @@ static void down_core24(gx_downscaler_t *ds, inp -= back; } inp -= back2; - res = (value+(div>>1))/div; - *outp++ = res; - value -= res*div; - e_forward_r = value * 7/16; - e_downleft = value * 3/16; - e_down = value * 5/16; - value -= e_forward_r + e_downleft + e_down; - errors[-2] += e_downleft; - errors[-1] += e_down; - *errors++ = value; + *outp++ = (value+(div>>1))/div; /* G */ - value = e_forward_g + *errors; + value = 0; for (xx = factor; xx > 0; xx--) { for (y = factor; y > 0; y--) @@ -528,18 +418,9 @@ static void down_core24(gx_downscaler_t *ds, inp -= back; } inp -= back2; - res = (value+(div>>1))/div; - *outp++ = res; - value -= res*div; - e_forward_g = value * 7/16; - e_downleft = value * 3/16; - e_down = value * 5/16; - value -= e_forward_g + e_downleft + e_down; - errors[-2] += e_downleft; - errors[-1] += e_down; - *errors++ = value; + *outp++ = (value+(div>>1))/div; /* B */ - value = e_forward_b + *errors; + value = 0; for (xx = factor; xx > 0; xx--) { for (y = factor; y > 0; y--) @@ -550,176 +431,80 @@ static void down_core24(gx_downscaler_t *ds, inp -= back; } inp -= 2; - res = (value+(div>>1))/div; - *outp++ = res; - value -= res*div; - e_forward_b = value * 7/16; - e_downleft = value * 3/16; - e_down = value * 5/16; - value -= e_forward_b + e_downleft + e_down; - errors[-2] += e_downleft; - errors[-1] += e_down; - *errors++ = value; - } - } - else - { - /* Right to Left pass (no min feature size) */ - const int back = span * factor + 3; - const int back2 = factor * 3 - 1; - errors += 3*awidth; - inp += 3*awidth*factor-1; - outp += 3*awidth - 1; - for (x = awidth; x > 0; x--) - { - int res; - /* B */ - value = e_forward_b + *errors; - for (xx = factor; xx > 0; xx--) - { - for (y = factor; y > 0; y--) - { - value += *inp; - inp += span; - } - inp -= back; - } - inp += back2; - res = (value + (div>>1))/div; - *outp-- = res; - value -= res*div; - e_forward_b = value * 7/16; - e_downleft = value * 3/16; - e_down = value * 5/16; - value -= e_forward_b + e_downleft + e_down; - errors[2] += e_downleft; - errors[1] += e_down; - *errors-- = value; - /* G */ - value = e_forward_g + *errors; - for (xx = factor; xx > 0; xx--) - { - for (y = factor; y > 0; y--) - { - value += *inp; - inp += span; - } - inp -= back; - } - inp += back2; - res = (value + (div>>1))/div; - *outp-- = res; - value -= res*div; - e_forward_g = value * 7/16; - e_downleft = value * 3/16; - e_down = value * 5/16; - value -= e_forward_g + e_downleft + e_down; - errors[2] += e_downleft; - errors[1] += e_down; - *errors-- = value; - /* R */ - value = e_forward_r + *errors; - for (xx = factor; xx > 0; xx--) - { - for (y = factor; y > 0; y--) - { - value += *inp; - inp += span; - } - inp -= back; - } - inp += 2; - res = (value + (div>>1))/div; - *outp-- = res; - value -= res*div; - e_forward_r = value * 7/16; - e_downleft = value * 3/16; - e_down = value * 5/16; - value -= e_forward_r + e_downleft + e_down; - errors[2] += e_downleft; - errors[1] += e_down; - *errors-- = value; + *outp++ = (value+(div>>1))/div; } } } -#else -static void down_core24(gx_downscaler_t *ds, - byte *outp, - int row) -{ - int x, xx, y, value; - int pad_white; - byte *inp; - int width = ds->width; - int awidth = ds->awidth; - int factor = ds->factor; - int span = ds->span; - int div = factor*factor; - pad_white = (awidth - width) * factor * 3; - if (pad_white < 0) - pad_white = 0; +int gx_downscaler_init_planar(gx_downscaler_t *ds, + gx_device *dev, + gs_get_bits_params_t *params, + int num_comps, + int factor, + int mfs, + int dst_bpc) +{ + int span = bitmap_raster(dev->width * 8); + int width = dev->width/factor; + int code; + gx_downscale_core *core; + int i; - if (pad_white) + memset(ds, 0, sizeof(*ds)); + ds->dev = dev; + ds->width = width; + ds->awidth = width; + ds->span = span; + ds->factor = factor; + ds->num_planes = num_comps; + + memcpy(&ds->params, params, sizeof(*params)); + ds->params.raster = span; + for (i = 0; i < num_comps; i++) { - inp = ds->data + width*factor*3; - for (y = factor; y > 0; y--) - { - memset(inp, 0xFF, pad_white); - inp += span; - } + ds->params.data[i] = gs_alloc_bytes(dev->memory, span * factor, + "gx_downscaler(planar_data)"); + if (ds->params.data[i] == NULL) + goto cleanup; } - inp = ds->data; + if (dst_bpc == 1) + core = &down_core; + else if (factor == 1) + core = NULL; + else + core = &down_core8; + ds->down_core = core; + + if (mfs > 1) { + ds->mfs_data = (byte *)gs_alloc_bytes(dev->memory, + (width+1) * num_comps, + "gx_downscaler(mfs)"); + if (ds->mfs_data == NULL) { + code = gs_note_error(gs_error_VMerror); + goto cleanup; + } + memset(ds->mfs_data, 0, (width+1) * num_comps); + } + if (dst_bpc == 1) { - /* Left to Right pass (no min feature size) */ - const int back = span * factor - 3; - const int back2 = factor * 3 - 1; - for (x = awidth; x > 0; x--) + ds->errors = (int *)gs_alloc_bytes(dev->memory, + num_comps*(width+3)*sizeof(int), + "gx_downscaler(errors)"); + if (ds->errors == NULL) { - /* R */ - value = 0; - for (xx = factor; xx > 0; xx--) - { - for (y = factor; y > 0; y--) - { - value += *inp; - inp += span; - } - inp -= back; - } - inp -= back2; - *outp++ = (value+(div>>1))/div; - /* G */ - value = 0; - for (xx = factor; xx > 0; xx--) - { - for (y = factor; y > 0; y--) - { - value += *inp; - inp += span; - } - inp -= back; - } - inp -= back2; - *outp++ = (value+(div>>1))/div; - /* B */ - value = 0; - for (xx = factor; xx > 0; xx--) - { - for (y = factor; y > 0; y--) - { - value += *inp; - inp += span; - } - inp -= back; - } - inp -= 2; - *outp++ = (value+(div>>1))/div; + code = gs_note_error(gs_error_VMerror); + goto cleanup; } + memset(ds->errors, 0, num_comps * (width+3) * sizeof(int)); } + + return 0; + + cleanup: + gx_downscaler_fin(ds); + return code; } -#endif int gx_downscaler_init(gx_downscaler_t *ds, gx_device *dev, @@ -729,7 +514,7 @@ int gx_downscaler_init(gx_downscaler_t *ds, int factor, int mfs, int (*adjust_width_proc)(int, int), - int adjust_width) + int adjust_width) { int size = gdev_mem_bytes_per_scan_line((gx_device *)dev); int span; @@ -747,11 +532,12 @@ int gx_downscaler_init(gx_downscaler_t *ds, span = size + pad_white*factor*num_comps + factor-1; memset(ds, 0, sizeof(*ds)); - ds->dev = dev; - ds->width = width; - ds->awidth = awidth; - ds->span = span; - ds->factor = factor; + ds->dev = dev; + ds->width = width; + ds->awidth = awidth; + ds->span = span; + ds->factor = factor; + ds->num_planes = 0; /* Choose an appropriate core */ if ((src_bpc == 8) && (dst_bpc == 1) && (num_comps == 1)) @@ -785,9 +571,7 @@ int gx_downscaler_init(gx_downscaler_t *ds, } memset(ds->mfs_data, 0, awidth+1); } -#ifndef DO_STUPID_ERROR_DIFFUSION if (dst_bpc == 1) -#endif { ds->errors = (int *)gs_alloc_bytes(dev->memory, num_comps*(awidth+3)*sizeof(int), @@ -810,6 +594,14 @@ int gx_downscaler_init(gx_downscaler_t *ds, void gx_downscaler_fin(gx_downscaler_t *ds) { + int plane; + for (plane=0; plane < ds->num_planes; plane++) + { + gs_free_object(ds->dev->memory, ds->params.data[plane], + "gx_downscaler(planar_data)"); + } + ds->num_planes = 0; + gs_free_object(ds->dev->memory, ds->mfs_data, "gx_downscaler(mfs)"); ds->mfs_data = NULL; gs_free_object(ds->dev->memory, ds->errors, "gx_downscaler(errors)"); @@ -843,7 +635,78 @@ int gx_downscaler_getbits(gx_downscaler_t *ds, y++; } while (y < y_end); - (ds->down_core)(ds, out_data, row); + (ds->down_core)(ds, out_data, ds->data, row, 0, ds->span); + + return code; +} + +int gx_downscaler_get_bits_rectangle(gx_downscaler_t *ds, + gs_get_bits_params_t *params, + int row) +{ + int code; + gs_int_rect rect; + int plane; + int factor = ds->factor; + gs_get_bits_params_t params2; + + + rect.p.x = 0; + rect.p.y = row * factor; + rect.q.x = ds->dev->width; + rect.q.y = (row + 1) * factor; + + /* Check for the simple case */ + if (ds->down_core == NULL) { + return (*dev_proc(ds->dev, get_bits_rectangle))(ds->dev, &rect, params, NULL); + } + + /* Copy the params, because get_bits_rectangle can helpfully overwrite + * them. */ + memcpy(¶ms2, &ds->params, sizeof(params2)); + /* Get factor rows worth of data */ + code = (*dev_proc(ds->dev, get_bits_rectangle))(ds->dev, &rect, ¶ms2, NULL); + if (code == gs_error_rangecheck) + { + int i, j; + int copy = ds->dev->width; /* Assumes 8bpp */ + /* At the bottom of a band, the get_bits_rectangle call can fail to be + * able to return us enough lines of data at the same time. We therefore + * drop back to reading them one at a time, and copying them into our + * own buffer. */ + for (i = 0; i < factor; i++) { + rect.q.y = rect.p.y+1; + if (rect.q.y > ds->dev->height) + break; + memcpy(¶ms2, &ds->params, sizeof(params2)); + code = (*dev_proc(ds->dev, get_bits_rectangle))(ds->dev, &rect, ¶ms2, NULL); + if (code < 0) + break; + for (j = 0; j < ds->num_planes; j++) { + memcpy(ds->params.data[j] + i*ds->span, params2.data[j], copy); + } + rect.p.y++; + } + if (i == 0) + return code; + /* If we still haven't got enough, we've hit the end of the page; just + * duplicate the last line we did get. */ + for (;i < factor; i++) { + for (j = 0; j < ds->num_planes; j++) { + memcpy(ds->params.data[j] + i*ds->span, ds->params.data[j] + (i-1)*ds->span, copy); + } + } + for (j = 0; j < ds->num_planes; j++) { + params2.data[j] = ds->params.data[j]; + } + } + if (code < 0) + return code; + + for (plane=0; plane < ds->num_planes; plane++) + { + (ds->down_core)(ds, params->data[plane], params2.data[plane], row, plane, params2.raster); + } return code; } diff --git a/gs/base/gxdownscale.h b/gs/base/gxdownscale.h index aa65bca34..70fe62e5f 100644 --- a/gs/base/gxdownscale.h +++ b/gs/base/gxdownscale.h @@ -18,6 +18,7 @@ #include "gsmemory.h" #include "gstypes.h" #include "gxdevcli.h" +#include "gxgetbit.h" /* The following structure definitions should really be considered * private, and are exposed here only because it enables us to define @@ -28,34 +29,41 @@ typedef struct gx_downscaler_s gx_downscaler_t; typedef void (gx_downscale_core)(gx_downscaler_t *ds, byte *out_buffer, - int row); + byte *in_buffer, + int row, + int plane, + int span); struct gx_downscaler_s { - gx_device *dev; /* Device */ - int width; /* Width (pixels) */ - int awidth; /* Adjusted width (pixels) */ - int span; /* Num bytes in downscale buffer scanline */ - int factor; /* Factor to downscale */ - byte *mfs_data; /* MinFeatureSize data */ - int *errors; /* Error diffusion table */ - byte *data; /* Downscaling buffer */ - gx_downscale_core *down_core; + gx_device *dev; /* Device */ + int width; /* Width (pixels) */ + int awidth; /* Adjusted width (pixels) */ + int span; /* Num bytes in downscale buffer scanline */ + int factor; /* Factor to downscale */ + byte *mfs_data; /* MinFeatureSize data */ + int *errors; /* Error diffusion table */ + byte *data; /* Downscaling buffer */ + gx_downscale_core *down_core; /* Core downscaling function */ + gs_get_bits_params_t params; /* Params if in planar mode */ + int num_planes;/* Number of planes if planar, 0 otherwise */ }; /* To use the downscaler: * * + define a gx_downscaler_t on the stack. - * + initialise it with gx_downscaler_init + * + initialise it with gx_downscaler_init or gx_downscaler_init_planar * + repeatedly call gx_downscaler_get_lines (or - * gx_downscaler_copy_scan_lines) + * gx_downscaler_copy_scan_lines) (for chunky mode) or + * gx_downscaler_get_bits_rectangle (for planar mode) * + finalise with gx_downscaler_fin */ -/* Currently only: +/* For chunky mode, currently only: * src_bpc == 8 && dst_bpc == 1 && num_comps == 1 * src_bpc == 8 && dst_bpc == 8 && num_comps == 1 * src_bpc == 8 && dst_bpc == 8 && num_comps == 3 * are supported. mfs is ignored for all except the first of these. + * For planar mode, currently only src_bpp && dst_bpp == 8 are supported. */ int gx_downscaler_init(gx_downscaler_t *ds, gx_device *dev, @@ -65,12 +73,24 @@ int gx_downscaler_init(gx_downscaler_t *ds, int factor, int mfs, int (*adjust_width_proc)(int, int), - int adjust_width); + int adjust_width); + +int gx_downscaler_init_planar(gx_downscaler_t *ds, + gx_device *dev, + gs_get_bits_params_t *params, + int num_comps, + int factor, + int mfs, + int dst_bpc); int gx_downscaler_getbits(gx_downscaler_t *ds, byte *out_data, int row); +int gx_downscaler_get_bits_rectangle(gx_downscaler_t *ds, + gs_get_bits_params_t *params, + int row); + /* Must only fin a device that has been inited (though you can safely * fin several times) */ void gx_downscaler_fin(gx_downscaler_t *ds); diff --git a/gs/base/lib.mak b/gs/base/lib.mak index b2fcd10e0..37240fdf0 100644 --- a/gs/base/lib.mak +++ b/gs/base/lib.mak @@ -1212,7 +1212,7 @@ $(GLOBJ)gdevemap.$(OBJ) : $(GLSRC)gdevemap.c $(AK) $(std_h) $(MAKEDIRS) # ----------- Downsampling routines ------------ # gxdownscale_h=$(GLSRC)gxdownscale.h $(gsmemory_h) $(gxdevcli_h) $(ctype__h) \ - $(gstypes_h) + $(gstypes_h) $(gxgetbit_h) downscale_=$(GLOBJ)gxdownscale.$(OBJ) $(GLOBJ)gxdownscale.$(OBJ) : $(GLSRC)gxdownscale.c $(AK) \ diff --git a/gs/doc/Devices.htm b/gs/doc/Devices.htm index ebab696d2..a6a915373 100644 --- a/gs/doc/Devices.htm +++ b/gs/doc/Devices.htm @@ -458,6 +458,17 @@ It is possible to create an overall CMYK composite for more than 4 colorants by then adding together (via an external application like imagemagick) the composite CMYK output files from the individual Ghostscript passes. +<p>The <code>tiffsep</code> device accepts a -dBitsPerComponent= +option, which may be set to 8 (the default) or 1. In 1bpp mode, the +device renders each component internally in 8 bits, but then converts +down to 1bpp with error diffusion before output as described below in +the <code>tiffscaled</code> device. No composite file is produced in +1bpp mode, only individual separations. + +<p>The device also accepts the -dDownScaleFactor= and -dMinFeatureSize= +parameters as described below in the tiffscaled device, but +MinFeatureSize is only honoured in 1bpp mode. + <a name="tiffsep1"></a><dt><code>tiffsep1</code> <dd> The tiffsep1 device creates multiple output files, one for each component |