/* * SpanDSP - a series of DSP components for telephony * * image_translate.c - Image translation routines for reworking colour * and gray scale images to be colour, gray scale or * bi-level images of an appropriate size to be FAX * compatible. * * Written by Steve Underwood * * Copyright (C) 2009 Steve Underwood * * 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 version 2, as * published by the Free Software Foundation. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ /*! \file */ #if defined(HAVE_CONFIG_H) #include "config.h" #endif #include #include #include #include #include #include #include #include #include #if defined(HAVE_TGMATH_H) #include #endif #if defined(HAVE_MATH_H) #include #endif #if defined(HAVE_STDBOOL_H) #include #else #include "spandsp/stdbool.h" #endif #include "floating_fudge.h" #include #include #include "spandsp/telephony.h" #include "spandsp/alloc.h" #include "spandsp/fast_convert.h" #include "spandsp/logging.h" #include "spandsp/saturated.h" #include "spandsp/timezone.h" #include "spandsp/t4_rx.h" #include "spandsp/t4_tx.h" #include "spandsp/t81_t82_arith_coding.h" #include "spandsp/t85.h" #include "spandsp/t42.h" #include "spandsp/t43.h" #include "spandsp/t4_t6_decode.h" #include "spandsp/t4_t6_encode.h" #include "spandsp/image_translate.h" #include "spandsp/private/logging.h" #include "spandsp/private/t81_t82_arith_coding.h" #include "spandsp/private/t85.h" #include "spandsp/private/t42.h" #include "spandsp/private/t43.h" #include "spandsp/private/t4_t6_decode.h" #include "spandsp/private/t4_t6_encode.h" #include "spandsp/private/image_translate.h" #include "spandsp/private/t4_rx.h" #include "spandsp/private/t4_tx.h" static int image_colour16_to_colour8_row(uint8_t colour8[], uint16_t colour16[], int pixels) { int i; for (i = 0; i < 3*pixels; i++) colour8[i] = colour16[i] >> 8; return pixels; } /*- End of function --------------------------------------------------------*/ static int image_colour16_to_gray16_row(uint16_t gray16[], uint16_t colour16[], int pixels) { int i; uint32_t gray; for (i = 0; i < pixels; i++) { gray = colour16[3*i]*19595 + colour16[3*i + 1]*38469 + colour16[3*i + 2]*7472; gray16[i] = saturateu16(gray >> 16); } return pixels; } /*- End of function --------------------------------------------------------*/ static int image_colour16_to_gray8_row(uint8_t gray8[], uint16_t colour16[], int pixels) { int i; uint32_t gray; for (i = 0; i < pixels; i++) { gray = colour16[3*i]*19595 + colour16[3*i + 1]*38469 + colour16[3*i + 2]*7472; gray8[i] = saturateu8(gray >> 24); } return pixels; } /*- End of function --------------------------------------------------------*/ static int image_colour8_to_gray16_row(uint16_t gray16[], uint8_t colour8[], int pixels) { int i; uint32_t gray; for (i = 0; i < pixels; i++) { gray = colour8[3*i]*19595 + colour8[3*i + 1]*38469 + colour8[3*i + 2]*7472; gray16[i] = saturateu16(gray >> 8); } return pixels; } /*- End of function --------------------------------------------------------*/ static int image_colour8_to_gray8_row(uint8_t gray8[], uint8_t colour8[], int pixels) { int i; uint32_t gray; for (i = 0; i < pixels; i++) { gray = colour8[3*i]*19595 + colour8[3*i + 1]*38469 + colour8[3*i + 2]*7472; gray8[i] = saturateu8(gray >> 16); } return pixels; } /*- End of function --------------------------------------------------------*/ static int image_colour8_to_colour16_row(uint16_t colour16[], uint8_t colour8[], int pixels) { int i; for (i = 3*pixels - 1; i >= 0; i--) colour16[i] = colour8[i] << 8; return pixels; } /*- End of function --------------------------------------------------------*/ static int image_gray16_to_colour16_row(uint16_t colour16[], uint16_t gray16[], int pixels) { int i; for (i = pixels - 1; i >= 0; i--) { colour16[3*i] = saturateu16((gray16[i]*36532U) >> 15); colour16[3*i + 1] = saturateu16((gray16[i]*37216U) >> 16); colour16[3*i + 2] = saturateu16((gray16[i]*47900U) >> 14); } return pixels; } /*- End of function --------------------------------------------------------*/ static int image_gray16_to_colour8_row(uint8_t colour8[], uint16_t gray16[], int pixels) { int i; for (i = pixels - 1; i >= 0; i--) { colour8[3*i] = saturateu8((gray16[i]*36532U) >> 23); colour8[3*i + 1] = saturateu8((gray16[i]*37216U) >> 24); colour8[3*i + 2] = saturateu8((gray16[i]*47900U) >> 22); } return pixels; } /*- End of function --------------------------------------------------------*/ static int image_gray16_to_gray8_row(uint8_t gray8[], uint16_t gray16[], int pixels) { int i; for (i = 0; i < pixels; i++) gray8[i] = gray16[i] >> 8; return pixels; } /*- End of function --------------------------------------------------------*/ static int image_gray8_to_colour16_row(uint16_t colour16[], uint8_t gray8[], int pixels) { int i; for (i = pixels - 1; i >= 0; i--) { colour16[3*i] = saturateu16((gray8[i]*36532U) >> 7); colour16[3*i + 1] = saturateu16((gray8[i]*37216U) >> 8); colour16[3*i + 2] = saturateu16((gray8[i]*47900U) >> 6); } return pixels; } /*- End of function --------------------------------------------------------*/ static int image_gray8_to_colour8_row(uint8_t colour8[], uint8_t gray8[], int pixels) { int i; for (i = pixels - 1; i >= 0; i--) { colour8[3*i] = saturateu8((gray8[i]*36532U) >> 15); colour8[3*i + 1] = saturateu8((gray8[i]*37216U) >> 16); colour8[3*i + 2] = saturateu8((gray8[i]*47900U) >> 14); } return pixels; } /*- End of function --------------------------------------------------------*/ static int image_gray8_to_gray16_row(uint16_t gray16[], uint8_t gray8[], int pixels) { int i; for (i = pixels - 1; i >= 0; i--) gray16[i] = gray8[i] << 8; return pixels; } /*- End of function --------------------------------------------------------*/ static int get_and_scrunch_row(image_translate_state_t *s, uint8_t buf[]) { int input_row_len; input_row_len = (*s->row_read_handler)(s->row_read_user_data, buf, s->input_width*s->input_bytes_per_pixel); if (input_row_len != s->input_width*s->input_bytes_per_pixel) return 0; /* Scrunch colour down to gray, vice versa. Scrunch 16 bit pixels down to 8 bit pixels, or vice versa. */ switch (s->input_format) { case T4_IMAGE_TYPE_GRAY_12BIT: switch (s->output_format) { case T4_IMAGE_TYPE_BILEVEL: case T4_IMAGE_TYPE_GRAY_8BIT: image_gray16_to_gray8_row(buf, (uint16_t *) buf, s->input_width); break; case T4_IMAGE_TYPE_COLOUR_12BIT: image_gray16_to_colour16_row((uint16_t *) buf, (uint16_t *) buf, s->input_width); break; case T4_IMAGE_TYPE_COLOUR_BILEVEL: case T4_IMAGE_TYPE_COLOUR_8BIT: image_gray16_to_colour8_row(buf, (uint16_t *) buf, s->input_width); break; } break; case T4_IMAGE_TYPE_GRAY_8BIT: switch (s->output_format) { case T4_IMAGE_TYPE_GRAY_12BIT: image_gray8_to_gray16_row((uint16_t *) buf, buf, s->input_width); break; case T4_IMAGE_TYPE_COLOUR_12BIT: image_gray8_to_colour16_row((uint16_t *) buf, buf, s->input_width); break; case T4_IMAGE_TYPE_COLOUR_BILEVEL: case T4_IMAGE_TYPE_COLOUR_8BIT: image_gray8_to_colour8_row(buf, buf, s->input_width); break; } break; case T4_IMAGE_TYPE_COLOUR_12BIT: switch (s->output_format) { case T4_IMAGE_TYPE_GRAY_12BIT: image_colour16_to_gray16_row((uint16_t *) buf, (uint16_t *) buf, s->input_width); break; case T4_IMAGE_TYPE_BILEVEL: case T4_IMAGE_TYPE_GRAY_8BIT: image_colour16_to_gray8_row(buf, (uint16_t *) buf, s->input_width); break; case T4_IMAGE_TYPE_COLOUR_BILEVEL: case T4_IMAGE_TYPE_COLOUR_8BIT: image_colour16_to_colour8_row(buf, (uint16_t *) buf, s->input_width); break; } break; case T4_IMAGE_TYPE_COLOUR_BILEVEL: case T4_IMAGE_TYPE_COLOUR_8BIT: switch (s->output_format) { case T4_IMAGE_TYPE_GRAY_12BIT: image_colour8_to_gray16_row((uint16_t *) buf, buf, s->input_width); break; case T4_IMAGE_TYPE_BILEVEL: case T4_IMAGE_TYPE_GRAY_8BIT: image_colour8_to_gray8_row(buf, buf, s->input_width); break; case T4_IMAGE_TYPE_COLOUR_12BIT: image_colour8_to_colour16_row((uint16_t *) buf, buf, s->input_width); break; } break; } return s->output_width; } /*- End of function --------------------------------------------------------*/ static int image_resize_row(image_translate_state_t *s, uint8_t buf[]) { int i; int j; int output_width; int output_length; int input_width; int input_length; int x; #if defined(SPANDSP_USE_FIXED_POINT) int c1; int c2; int frac_row; int frac_col; #else double c1; double c2; double int_part; double frac_row; double frac_col; double width_scaling; #endif uint8_t *row8[2]; uint16_t *row16[2]; uint16_t *buf16; int row_len; int skip; uint8_t *p; if (s->raw_output_row < 0) return 0; output_width = s->output_width - 1; output_length = s->output_length - 1; input_width = s->input_width - 1; input_length = s->input_length - 1; skip = s->raw_output_row*input_length/output_length + 1; if (skip >= s->raw_input_row) { while (skip >= s->raw_input_row) { if (s->raw_input_row >= s->input_length) break; row_len = get_and_scrunch_row(s, s->raw_pixel_row[0]); if (row_len != s->output_width) { s->raw_output_row = -1; return 0; } s->raw_input_row++; p = s->raw_pixel_row[0]; s->raw_pixel_row[0] = s->raw_pixel_row[1]; s->raw_pixel_row[1] = p; } } #if defined(SPANDSP_USE_FIXED_POINT) frac_row = ((s->raw_output_row*256*input_length)/output_length) & 0xFF; #else frac_row = modf((double) s->raw_output_row*input_length/output_length, &int_part); width_scaling = (double) input_width/output_width; #endif switch (s->output_format) { case T4_IMAGE_TYPE_COLOUR_BILEVEL: case T4_IMAGE_TYPE_COLOUR_8BIT: row8[0] = s->raw_pixel_row[0]; row8[1] = s->raw_pixel_row[1]; for (i = 0; i < output_width; i++) { #if defined(SPANDSP_USE_FIXED_POINT) x = i*256*input_width/output_width; frac_col = x & 0xFF; x >>= 8; x = 3*x; for (j = 0; j < 3; j++) { c1 = row8[0][x + j] + (((row8[0][x + j + 3] - row8[0][x + j])*frac_col) >> 8); c2 = row8[1][x + j] + (((row8[1][x + j + 3] - row8[1][x + j])*frac_col) >> 8); buf[3*i + j] = saturateu8(c1 + (((c2 - c1)*frac_row) >> 8)); } #else frac_col = modf(width_scaling*i, &int_part); x = 3*int_part; for (j = 0; j < 3; j++) { c1 = row8[0][x + j] + (row8[0][x + j + 3] - row8[0][x + j])*frac_col; c2 = row8[1][x + j] + (row8[1][x + j + 3] - row8[1][x + j])*frac_col; buf[3*i + j] = saturateu8(c1 + (c2 - c1)*frac_row); } #endif } break; case T4_IMAGE_TYPE_COLOUR_12BIT: row16[0] = (uint16_t *) s->raw_pixel_row[0]; row16[1] = (uint16_t *) s->raw_pixel_row[1]; buf16 = (uint16_t *) buf; for (i = 0; i < output_width; i++) { #if defined(SPANDSP_USE_FIXED_POINT) x = i*256*input_width/output_width; frac_col = x & 0xFF; x >>= 8; x = 3*x; for (j = 0; j < 3; j++) { c1 = row16[0][x + j] + (((row16[0][x + j + 3] - row16[0][x + j])*frac_col) >> 8); c2 = row16[1][x + j] + (((row16[1][x + j + 3] - row16[1][x + j])*frac_col) >> 8); buf16[3*i + j] = saturateu16(c1 + (((c2 - c1)*frac_row) >> 8)); } #else frac_col = modf(width_scaling*i, &int_part); x = 3*int_part; for (j = 0; j < 3; j++) { c1 = row16[0][x + j] + (row16[0][x + j + 3] - row16[0][x + j])*frac_col; c2 = row16[1][x + j] + (row16[1][x + j + 3] - row16[1][x + j])*frac_col; buf16[3*i + j] = saturateu16(c1 + (c2 - c1)*frac_row); } #endif } break; case T4_IMAGE_TYPE_BILEVEL: case T4_IMAGE_TYPE_GRAY_8BIT: row8[0] = s->raw_pixel_row[0]; row8[1] = s->raw_pixel_row[1]; for (i = 0; i < output_width; i++) { #if defined(SPANDSP_USE_FIXED_POINT) x = i*256*input_width/output_width; frac_col = x & 0xFF; x >>= 8; c1 = row8[0][x] + (((row8[0][x + 1] - row8[0][x])*frac_col) >> 8); c2 = row8[1][x] + (((row8[1][x + 1] - row8[1][x])*frac_col) >> 8); buf[i] = saturateu8(c1 + (((c2 - c1)*frac_row) >> 8)); #else frac_col = modf(width_scaling*i, &int_part); x = int_part; c1 = row8[0][x] + (row8[0][x + 1] - row8[0][x])*frac_col; c2 = row8[1][x] + (row8[1][x + 1] - row8[1][x])*frac_col; buf[i] = saturateu8(c1 + (c2 - c1)*frac_row); #endif } break; case T4_IMAGE_TYPE_GRAY_12BIT: row16[0] = (uint16_t *) s->raw_pixel_row[0]; row16[1] = (uint16_t *) s->raw_pixel_row[1]; for (i = 0; i < output_width; i++) { #if defined(SPANDSP_USE_FIXED_POINT) x = i*256*input_width/output_width; frac_col = x & 0xFF; x >>= 8; c1 = row16[0][x] + (((row16[0][x + 1] - row16[0][x])*frac_col) >> 8); c2 = row16[1][x] + (((row16[1][x + 1] - row16[1][x])*frac_col) >> 8); buf[i] = saturateu8(c1 + (((c2 - c1)*frac_row) >> 8)); #else frac_col = modf(width_scaling*i, &int_part); x = int_part; c1 = row16[0][x] + (row16[0][x + 1] - row16[0][x])*frac_col; c2 = row16[1][x] + (row16[1][x + 1] - row16[1][x])*frac_col; buf[i] = saturateu8(c1 + (c2 - c1)*frac_row); #endif } break; } if (++s->raw_output_row >= s->output_length) s->raw_output_row = -1; return s->output_width; } /*- End of function --------------------------------------------------------*/ static __inline__ uint8_t find_closest_palette_color(int in) { return (in >= 128) ? 255 : 0; } /*- End of function --------------------------------------------------------*/ static int floyd_steinberg_dither_row(image_translate_state_t *s, uint8_t buf[]) { int x; int y; int i; int j; int limit; int old_pixel; int new_pixel; int quant_error; uint8_t xx; uint8_t *p; y = s->output_row++; /* This algorithm works over two rows, and outputs the earlier of the two. To make this work: - At row 0 we grab and scrunch two rows. - From row 1 up to the last row we grab one new additional row each time. - At the last row we dither and output, without getting an extra row in. */ for (i = (y == 0) ? 0 : 1; i < 2; i++) { /* Swap the row buffers */ p = s->pixel_row[0]; s->pixel_row[0] = s->pixel_row[1]; s->pixel_row[1] = p; /* If this is the end of the image just ignore that there is now rubbish in pixel_row[1]. Mark that the end has occurred. This row will be properly output, and the next one will fail, with the end of image condition (i.e. returning zero length) */ if (s->resize) { if (image_resize_row(s, s->pixel_row[1]) != s->output_width) s->output_row = -1; } else { if (get_and_scrunch_row(s, s->pixel_row[1]) != s->output_width) s->output_row = -1; } } /* Apply Floyd-Steinberg dithering to the 8 bit pixels, using a bustrophodontic scan, to reduce the grayscale image to pure black and white */ /* The first and last pixels in each row need special treatment, so we do not step outside the row. */ if ((y & 1)) { x = s->output_width - 1; old_pixel = s->pixel_row[0][x]; new_pixel = find_closest_palette_color(old_pixel); quant_error = old_pixel - new_pixel; s->pixel_row[0][x + 0] = new_pixel; s->pixel_row[0][x - 1] = saturateu8(s->pixel_row[0][x - 1] + (7*quant_error)/16); s->pixel_row[1][x + 0] = saturateu8(s->pixel_row[1][x + 0] + (5*quant_error)/16); s->pixel_row[1][x - 1] = saturateu8(s->pixel_row[1][x - 1] + (1*quant_error)/16); while (--x > 0) { old_pixel = s->pixel_row[0][x]; new_pixel = find_closest_palette_color(old_pixel); quant_error = old_pixel - new_pixel; s->pixel_row[0][x + 0] = new_pixel; s->pixel_row[0][x - 1] = saturateu8(s->pixel_row[0][x - 1] + (7*quant_error)/16); s->pixel_row[1][x + 1] = saturateu8(s->pixel_row[1][x + 1] + (3*quant_error)/16); s->pixel_row[1][x + 0] = saturateu8(s->pixel_row[1][x + 0] + (5*quant_error)/16); s->pixel_row[1][x - 1] = saturateu8(s->pixel_row[1][x - 1] + (1*quant_error)/16); } old_pixel = s->pixel_row[0][x]; new_pixel = find_closest_palette_color(old_pixel); quant_error = old_pixel - new_pixel; s->pixel_row[0][x + 0] = new_pixel; s->pixel_row[1][x + 1] = saturateu8(s->pixel_row[1][x + 1] + (3*quant_error)/16); s->pixel_row[1][x + 0] = saturateu8(s->pixel_row[1][x + 0] + (5*quant_error)/16); } else { x = 0; old_pixel = s->pixel_row[0][x]; new_pixel = find_closest_palette_color(old_pixel); quant_error = old_pixel - new_pixel; s->pixel_row[0][x + 0] = new_pixel; s->pixel_row[0][x + 1] = saturateu8(s->pixel_row[0][x + 1] + (7*quant_error)/16); s->pixel_row[1][x + 0] = saturateu8(s->pixel_row[1][x + 0] + (5*quant_error)/16); s->pixel_row[1][x + 1] = saturateu8(s->pixel_row[1][x + 1] + (1*quant_error)/16); while (++x < s->output_width - 1) { old_pixel = s->pixel_row[0][x]; new_pixel = find_closest_palette_color(old_pixel); quant_error = old_pixel - new_pixel; s->pixel_row[0][x + 0] = new_pixel; s->pixel_row[0][x + 1] = saturateu8(s->pixel_row[0][x + 1] + (7*quant_error)/16); s->pixel_row[1][x - 1] = saturateu8(s->pixel_row[1][x - 1] + (3*quant_error)/16); s->pixel_row[1][x + 0] = saturateu8(s->pixel_row[1][x + 0] + (5*quant_error)/16); s->pixel_row[1][x + 1] = saturateu8(s->pixel_row[1][x + 1] + (1*quant_error)/16); } old_pixel = s->pixel_row[0][x]; new_pixel = find_closest_palette_color(old_pixel); quant_error = old_pixel - new_pixel; s->pixel_row[0][x + 0] = new_pixel; s->pixel_row[1][x - 1] = saturateu8(s->pixel_row[1][x - 1] + (3*quant_error)/16); s->pixel_row[1][x + 0] = saturateu8(s->pixel_row[1][x + 0] + (5*quant_error)/16); } /* Now bit pack the pixel per byte row into a pixel per bit row. */ for (i = 0, x = 0; x < s->output_width; i++, x += 8) { xx = 0; /* Allow for the possibility that the width is not a multiple of 8 */ limit = (8 <= s->output_width - x) ? 8 : (s->output_width - x); for (j = 0; j < limit; j++) { if (s->pixel_row[0][x + j] <= 128) xx |= (1 << (7 - j)); } buf[i] = xx; } return i; } /*- End of function --------------------------------------------------------*/ SPAN_DECLARE(int) image_translate_row(image_translate_state_t *s, uint8_t buf[], size_t len) { int i; if (s->output_row < 0) return 0; switch (s->output_format) { case T4_IMAGE_TYPE_BILEVEL: case T4_IMAGE_TYPE_COLOUR_BILEVEL: case T4_IMAGE_TYPE_4COLOUR_BILEVEL: i = floyd_steinberg_dither_row(s, buf); break; default: s->output_row++; if (s->resize) { if (image_resize_row(s, buf) != s->output_width) s->output_row = -1; } else { if (get_and_scrunch_row(s, buf) != s->output_width) s->output_row = -1; } if (s->output_row < 0) return 0; i = s->output_width*s->output_bytes_per_pixel; break; } return i; } /*- End of function --------------------------------------------------------*/ SPAN_DECLARE(int) image_translate_get_output_width(image_translate_state_t *s) { return s->output_width; } /*- End of function --------------------------------------------------------*/ SPAN_DECLARE(int) image_translate_get_output_length(image_translate_state_t *s) { return s->output_length; } /*- End of function --------------------------------------------------------*/ SPAN_DECLARE(int) image_translate_set_row_read_handler(image_translate_state_t *s, t4_row_read_handler_t row_read_handler, void *row_read_user_data) { s->row_read_handler = row_read_handler; s->row_read_user_data = row_read_user_data; return 0; } /*- End of function --------------------------------------------------------*/ static int image_format_to_bytes_per_pixel(int image_format) { switch (image_format) { case T4_IMAGE_TYPE_BILEVEL: case T4_IMAGE_TYPE_GRAY_8BIT: return 1; case T4_IMAGE_TYPE_GRAY_12BIT: return 2; case T4_IMAGE_TYPE_COLOUR_BILEVEL: case T4_IMAGE_TYPE_COLOUR_8BIT: return 3; case T4_IMAGE_TYPE_4COLOUR_BILEVEL: case T4_IMAGE_TYPE_4COLOUR_8BIT: return 4; case T4_IMAGE_TYPE_COLOUR_12BIT: return 6; case T4_IMAGE_TYPE_4COLOUR_12BIT: return 8; } return 1; } /*- End of function --------------------------------------------------------*/ SPAN_DECLARE(int) image_translate_restart(image_translate_state_t *s, int input_length) { int i; int raw_row_size; int row_size; s->input_length = input_length; if (s->resize) s->output_length = (s->input_length*s->output_width)/s->input_width; else s->output_length = s->input_length; /* Allocate the two row buffers we need, using the space requirements we now have */ raw_row_size = s->input_width*s->input_bytes_per_pixel; row_size = s->output_width*s->output_bytes_per_pixel; if (raw_row_size < row_size) raw_row_size = row_size; if (s->resize) { for (i = 0; i < 2; i++) { if (s->raw_pixel_row[i] == NULL) { if ((s->raw_pixel_row[i] = (uint8_t *) span_alloc(raw_row_size)) == NULL) return -1; } memset(s->raw_pixel_row[i], 0, raw_row_size); } } switch (s->output_format) { case T4_IMAGE_TYPE_BILEVEL: case T4_IMAGE_TYPE_COLOUR_BILEVEL: case T4_IMAGE_TYPE_4COLOUR_BILEVEL: if (s->resize) raw_row_size = row_size; for (i = 0; i < 2; i++) { if (s->pixel_row[i] == NULL) { if ((s->pixel_row[i] = (uint8_t *) span_alloc(raw_row_size)) == NULL) return -1; } memset(s->pixel_row[i], 0, raw_row_size); } break; } s->raw_input_row = 0; s->raw_output_row = 0; s->output_row = 0; return 0; } /*- End of function --------------------------------------------------------*/ SPAN_DECLARE(image_translate_state_t *) image_translate_init(image_translate_state_t *s, int output_format, int output_width, int output_length, int input_format, int input_width, int input_length, t4_row_read_handler_t row_read_handler, void *row_read_user_data) { if (s == NULL) { if ((s = (image_translate_state_t *) span_alloc(sizeof(*s))) == NULL) return NULL; } memset(s, 0, sizeof(*s)); s->row_read_handler = row_read_handler; s->row_read_user_data = row_read_user_data; s->input_format = input_format; s->input_width = input_width; s->input_length = input_length; s->input_bytes_per_pixel = image_format_to_bytes_per_pixel(s->input_format); s->output_format = output_format; s->output_bytes_per_pixel = image_format_to_bytes_per_pixel(s->output_format); s->resize = (output_width > 0); s->output_width = (s->resize) ? output_width : s->input_width; if (image_translate_restart(s, input_length)) return NULL; return s; } /*- End of function --------------------------------------------------------*/ SPAN_DECLARE(int) image_translate_release(image_translate_state_t *s) { int i; for (i = 0; i < 2; i++) { if (s->raw_pixel_row[i]) { span_free(s->raw_pixel_row[i]); s->raw_pixel_row[i] = NULL; } if (s->pixel_row[i]) { span_free(s->pixel_row[i]); s->pixel_row[i] = NULL; } } return 0; } /*- End of function --------------------------------------------------------*/ SPAN_DECLARE(int) image_translate_free(image_translate_state_t *s) { int res; res = image_translate_release(s); span_free(s); return res; } /*- End of function --------------------------------------------------------*/ /*- End of file ------------------------------------------------------------*/