diff options
author | Robin Watts <robin.watts@artifex.com> | 2018-04-19 12:45:14 +0100 |
---|---|---|
committer | Robin Watts <Robin.Watts@artifex.com> | 2019-06-25 10:37:09 +0100 |
commit | 140a9359f3cc6739c7caf0796430bc3112cb3cd5 (patch) | |
tree | 91ef3f7e59c06fe13ecfff2d7d2461ebd8da1c2b /base/siscale_cal.c | |
parent | dd75f619d0ae98e21e30ad89d2e7d2cd4277cc2e (diff) | |
download | ghostpdl-140a9359f3cc6739c7caf0796430bc3112cb3cd5.tar.gz |
Initial import of CAL.
Diffstat (limited to 'base/siscale_cal.c')
-rw-r--r-- | base/siscale_cal.c | 393 |
1 files changed, 393 insertions, 0 deletions
diff --git a/base/siscale_cal.c b/base/siscale_cal.c new file mode 100644 index 000000000..c8323aa0b --- /dev/null +++ b/base/siscale_cal.c @@ -0,0 +1,393 @@ +/* Copyright (C) 2001-2017 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 the license contained in the file LICENSE in this distribution. + + 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. +*/ + +/* Image scaling filters */ +#include "math_.h" +#include "memory_.h" +#include "stdio_.h" +#include "stdint_.h" +#include "gdebug.h" +#include "strimpl.h" +#include "siscale.h" +#include "gxfrac.h" +#include "cal.h" +#include "assert_.h" + +/* ImageScaleEncode / ImageScaleDecode */ +typedef struct stream_IScale_cal_state_s { + /* The client sets the params values before initialization. */ + stream_image_scale_state_common; /* = state_common + params */ + /* The init procedure sets the following. */ + cal_context *context; + cal_rescaler *rescaler; + uint8_t *src; + uint8_t *dst; + byte *tmp; + int pre_scan_bytes; + int post_scan_bytes; + /* The following are updated dynamically. */ + int src_y; + uint src_offset, src_size; + int dst_y; + uint dst_offset, dst_size; +} stream_IScale_cal_state; + +/* FIXME: */ +gs_private_st_ptrs2(st_IScale_cal_state, stream_IScale_cal_state, + "ImageScaleEncode/Decode state", + iscale_state_enum_ptrs, iscale_state_reloc_ptrs, + dst, src); + +/* ------ Stream implementation ------ */ + +/* Forward references */ +static void s_IScale_cal_release(stream_state * st); + +/* Set default parameter values (actually, just clear pointers) */ +static void +s_IScale_cal_set_defaults(stream_state * st) +{ + stream_IScale_cal_state *const ss = (stream_IScale_cal_state *) st; + + ss->rescaler = NULL; +} + +static void * +cal_do_malloc(void *opaque, size_t size) +{ + gs_memory_t *mem = (gs_memory_t *)opaque; + return gs_alloc_bytes(mem, size, "cal_do_malloc"); +} + +static void * +cal_do_realloc(void *opaque, void *ptr, size_t newsize) +{ + gs_memory_t *mem = (gs_memory_t *)opaque; + return gs_resize_object(mem, ptr, newsize, "cal_do_malloc"); +} + +static void +cal_do_free(void *opaque, void *ptr) +{ + gs_memory_t *mem = (gs_memory_t *)opaque; + gs_free_object(mem, ptr, "cal_do_malloc"); +} + +static cal_allocators cal_allocs = +{ + cal_do_malloc, + cal_do_realloc, + cal_do_free +}; + +/* + + Some notes: + + (ss->params.XXXX is shown as XXXX in the following for sanity) + + Conceptually we are scaling a bitmap that was EntireWidthIn x EntireHeightIn + in size, to be EntireWidthOut x EntireHeightOut in size. + + But, we only actually care about a sub rectangle of this in the destination, + given by (LeftMarginOut, TopMarginOut) + (PatchWidthOut, PatchHeightOut). + Anything else is clipped away. There are times when this sub rectangle can + be very "sub" indeed, so the ability to avoid rescaling all the data we + don't care about is a vital one. + + To confuse this further, we don't get fed scanlines of EntireWidthIn pixels, + instead we get WidthIn pixels. Similarly we don't feed scanlines out of + EntireWidthOut pixels, but rather of WidthOut pixels. Width{In,Out} are not + (always) the same as PatchWidth{In,Out} either. + + Accordingly there may be padding before and after the active region. We make + the effort to ensure these bytes are set to zero. +*/ + + +static int +s_IScale_cal_init(stream_state * st) +{ + stream_IScale_cal_state *const ss = (stream_IScale_cal_state *) st; + gs_memory_t *mem = ss->memory; + int abs_interp_limit = ss->params.abs_interp_limit; + int limited_WidthOut = (ss->params.WidthOut + abs_interp_limit - 1) / abs_interp_limit; + int limited_PatchWidthOut = (ss->params.PatchWidthOut + abs_interp_limit - 1) / abs_interp_limit; + int limited_PatchHeightOut = (ss->params.PatchHeightOut2 + abs_interp_limit - 1) / abs_interp_limit; + int limited_EntireWidthOut = (ss->params.EntireWidthOut + abs_interp_limit - 1) / abs_interp_limit; + int limited_EntireHeightOut = (ss->params.EntireHeightOut + abs_interp_limit - 1) / abs_interp_limit; + int limited_LeftMarginOut = (ss->params.LeftMarginOut) / abs_interp_limit; + int limited_TopMarginOut = (ss->params.TopMarginOut2) / abs_interp_limit; + int limited_PadY = (ss->params.pad_y + abs_interp_limit/2 ) / abs_interp_limit; + int dst_bytes_per_pixel = ss->params.BitsPerComponentOut / 8; + int src_bytes_per_pixel = ss->params.BitsPerComponentIn / 8; + + ss->context = cal_init(&cal_allocs, mem->non_gc_memory); + if (ss->context == NULL) + return ERRC; + + ss->src_offset = 0; + ss->src_size = ss->params.WidthIn * ss->params.spp_interp * src_bytes_per_pixel; + ss->dst_offset = 0; + ss->dst_size = limited_WidthOut * ss->params.spp_interp * dst_bytes_per_pixel; + ss->dst_y = -limited_PadY; + ss->pre_scan_bytes = limited_LeftMarginOut * ss->params.spp_interp * dst_bytes_per_pixel; + ss->post_scan_bytes = (limited_WidthOut - limited_PatchWidthOut - limited_LeftMarginOut) * ss->params.spp_interp * dst_bytes_per_pixel; + + ss->dst = gs_alloc_byte_array(mem, ss->dst_size, 1, "image_scale dst"); + if (ss->dst == NULL) + goto fail; + + ss->src = gs_alloc_byte_array(mem, ss->params.EntireWidthIn * ss->params.spp_interp, + src_bytes_per_pixel, "image_scale dst"); + if (ss->src == NULL) + goto fail; + + ss->rescaler = cal_rescaler_init(ss->context, + mem->non_gc_memory, + ss->params.EntireWidthIn, + ss->params.EntireHeightIn, + 0, + ss->params.src_y_offset, + ss->params.WidthIn, + ss->params.HeightIn, + limited_EntireWidthOut, + limited_EntireHeightOut, + limited_LeftMarginOut, + limited_TopMarginOut, + limited_PatchWidthOut, + limited_PatchHeightOut, + CAL_MITCHELL, + src_bytes_per_pixel, + dst_bytes_per_pixel, + ss->params.spp_interp, + ss->params.MaxValueIn, + ss->params.MaxValueOut); + if (ss->rescaler == NULL) + goto fail; + + if (ss->pre_scan_bytes) + memset(ss->dst, 0, ss->pre_scan_bytes); + if (ss->post_scan_bytes) + memset(ss->dst + ss->dst_size - ss->post_scan_bytes, 0, ss->post_scan_bytes); + + return 0; + +fail: + if (ss->rescaler) + cal_rescaler_fin(ss->rescaler, mem->non_gc_memory); + if (ss->context != NULL) + cal_fin(ss->context, mem->non_gc_memory); + gs_free_object(mem, ss->src, "image_scale src"); + gs_free_object(mem, ss->dst, "image_scale dst"); + return ERRC; +} + +/* Process a buffer. Note that this handles Encode and Decode identically. */ +static int +s_IScale_cal_process(stream_state * st, stream_cursor_read * pr, + stream_cursor_write * pw, bool last) +{ + stream_IScale_cal_state *const ss = (stream_IScale_cal_state *) st; + gs_memory_t *mem = ss->memory; + int abs_interp_limit = ss->params.abs_interp_limit; + int limited_HeightOut = (ss->params.HeightOut + abs_interp_limit - 1) / abs_interp_limit; + uint wleft; + uint rleft; + int any_output = 0; + const byte *input = NULL; + + /* If we have no more data to pull out, we're done. */ + if (ss->dst_y == limited_HeightOut) + return EOFC; + + /* How much room do we have left in the output buffer? */ + wleft = pw->limit - pw->ptr; + + /* If no room left, exit */ + if (wleft == 0) + return 1; + + /* If we need to send some padding at the top, do so */ + if (ss->dst_y < 0) + { + uint wcount = ss->dst_size - ss->dst_offset; + uint ncopy = wcount; + + if (ncopy > wleft) + ncopy = wleft; + memset(pw->ptr + 1, 0, ncopy); + pw->ptr += ncopy; + wcount -= ncopy; + if (wcount == 0) + { + ss->dst_offset = 0; + ss->dst_y++; + } + else + ss->dst_offset += ncopy; + wleft -= ncopy; + /* Unless we can get a whole new line out, pass out what we have */ + if (wleft < ss->dst_size) + return 1; + any_output = 1; + } + + /* Pass out any buffered data we have */ + if (ss->dst_offset != 0) + { + uint wcount = ss->dst_size - ss->dst_offset; + uint ncopy = wcount; + + if (ncopy > wleft) + ncopy = wleft; + memcpy(pw->ptr + 1, (byte *) ss->dst + ss->dst_offset, ncopy); + pw->ptr += ncopy; + wcount -= ncopy; + if (wcount == 0) + { + ss->dst_offset = 0; + ss->dst_y++; + } + else + ss->dst_offset += ncopy; + wleft -= ncopy; + /* Unless we can get a whole new line out, pass out what we have */ + if (wleft < ss->dst_size) + return 1; + any_output = 1; + } + + /* How much data do we have in the incoming buffer? */ + rleft = pr->limit - pr->ptr; + if (rleft > 0 && ss->src_offset > 0) { + /* We have part of a line buffered. Let's fill that out. */ + uint ncopy = ss->src_size - ss->src_offset; + if (ncopy > rleft) + ncopy = rleft; + memcpy(ss->src + ss->src_offset, pr->ptr + 1, ncopy); + pr->ptr += ncopy; + rleft -= ncopy; + ss->src_offset += ncopy; + if (ss->src_offset == ss->src_size) + { + ss->src_offset = 0; + input = ss->src; + } + } + else if (rleft >= ss->src_size) + input = pr->ptr+1; + + /* If we can extract a whole extra output line, then try for that. + * Try anyway if we haven't managed to output anything. */ + while (wleft >= ss->dst_size || any_output == 0) + { + uint8_t *row; + int ret; + + if (wleft >= ss->dst_size) { + /* We can scale the row directly into the output. */ + row = pw->ptr + 1; + } else { + /* We'll have to buffer the row. */ + row = ss->dst; + } + ret = cal_rescaler_process(ss->rescaler, mem, input, row + ss->pre_scan_bytes); + if (ret & 1) + { + /* Input consumed */ + if (input != ss->src) + { + pr->ptr += ss->src_size; + rleft -= ss->src_size; + } + input = (rleft >= ss->src_size) ? pr->ptr+1 : NULL; + ss->src_y++; + } + if (ret & 2) + { + /* Output given */ + if (row == ss->dst) + { + /* Copy as much of the the buffered data out as + * possible - we can't manage a whole line. */ + memcpy(pw->ptr+1, ss->dst, wleft); + pw->ptr += wleft; + ss->dst_offset = wleft; + return 1; + } + else + { + /* We are scaling direct into the output. Clear any pre and + * post sections for neatness. */ + if (ss->pre_scan_bytes != 0) + memset(row, 0, ss->pre_scan_bytes); + if (ss->post_scan_bytes != 0) + memset(row + ss->dst_size - ss->post_scan_bytes, 0, ss->post_scan_bytes); + } + pw->ptr += ss->dst_size; + ss->dst_y++; + wleft -= ss->dst_size; + any_output = 1; + } + /* If nothing happened, nothing to be gained from calling again */ + if (ret == 0) + break; + } + + if (any_output == 0 && rleft > 0) + { + /* We've not managed to output anything, so the rescaler + * must be waiting for input data. If we had a whole line of + * data, we'd have tried to pass it in above. So we must + * have only the start of a line. */ + assert(rleft < ss->src_size && ss->dst_offset == 0); + memcpy(ss->src, pr->ptr + 1, rleft); + ss->src_offset += rleft; + pr->ptr += rleft; + return 1; + } + + return any_output; +} + +/* Release the filter's storage. */ +static void +s_IScale_cal_release(stream_state * st) +{ + stream_IScale_cal_state *const ss = (stream_IScale_cal_state *) st; + gs_memory_t *mem = ss->memory; + + if (ss->rescaler) + { + cal_rescaler_fin(ss->rescaler, mem->non_gc_memory); + ss->rescaler = NULL; + } + if (ss->context != NULL) + { + cal_fin(ss->context, mem->non_gc_memory); + ss->context = NULL; + } + gs_free_object(mem, ss->src, "image_scale src"); + ss->src = NULL; + gs_free_object(mem, ss->dst, "image_scale dst"); + ss->dst = NULL; +} + +/* Stream template */ +const stream_template s_IScale_template = { + &st_IScale_cal_state, s_IScale_cal_init, s_IScale_cal_process, 1, 1, + s_IScale_cal_release, s_IScale_cal_set_defaults +}; |