SPAN_DECLARE(int) image_translate_row(image_translate_state_t *s, uint8_t buf[], size_t len) { int x; int y; int i; int j; int limit; int old_pixel; int new_pixel; int quant_error; uint8_t *p; uint8_t xx; if (s->output_row < 0) return 0; 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++) { 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->bytes_per_pixel) != s->output_width*s->bytes_per_pixel) s->output_row = -1; } else { if (get_and_scrunch_row(s, s->pixel_row[1], s->output_width*s->bytes_per_pixel) != s->output_width*s->bytes_per_pixel) 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); for ( ; x > 0; x--) { 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); for ( ; x < s->output_width - 1; x++) { 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; }
int main(int argc, char *argv[]) { printf("Testing 16 bit saturation\n"); if (saturate16(10000) != 10000 || saturate16(-10000) != -10000 || saturate16(32767) != 32767 || saturate16(-32768) != -32768 || saturate16(32768) != 32767 || saturate16(-32769) != -32768) { printf("Test failed.\n"); exit(2); } printf("Testing 15 bit saturation\n"); if (saturate15(10000) != 10000 || saturate15(-10000) != -10000 || saturate15(16383) != 16383 || saturate15(-16384) != -16384 || saturate15(16384) != 16383 || saturate15(-16385) != -16384) { printf("Test failed.\n"); exit(2); } printf("Testing 16 bit unsigned saturation\n"); if (saturateu16(10000) != 10000 || saturateu16(32767) != 32767 || saturateu16(65535) != 65535 || saturateu16(65536) != 65535) { printf("Test failed.\n"); exit(2); } printf("Testing 8 bit unsigned saturation\n"); if (saturateu8(100) != 100 || saturateu8(127) != 127 || saturateu8(255) != 255 || saturateu8(256) != 255) { printf("Test failed.\n"); exit(2); } printf("Testing 16 bit saturation from float\n"); if (fsaturatef(10000.0f) != 10000 || fsaturatef(-10000.0f) != -10000 || fsaturatef(32767.0f) != 32767 || fsaturatef(-32768.0f) != -32768 || fsaturatef(32768.0f) != 32767 || fsaturatef(-32769.0f) != -32768) { printf("Test failed.\n"); exit(2); } printf("Testing 16 bit saturation from double\n"); if (fsaturate(10000.0) != 10000 || fsaturate(-10000.0) != -10000 || fsaturate(32767.0) != 32767 || fsaturate(-32768.0) != -32768 || fsaturate(32768.0) != 32767 || fsaturate(-32769.0) != -32768) { printf("Test failed.\n"); exit(2); } printf("Testing 16 bit fast saturation from float\n"); if (ffastsaturatef(10000.0f) != 10000 || ffastsaturatef(-10000.0f) != -10000 || ffastsaturatef(32767.0f) != 32767 || ffastsaturatef(-32768.0f) != -32768 || ffastsaturatef(32768.0f) != 32767 || ffastsaturatef(-32769.0f) != -32768) { printf("Test failed.\n"); exit(2); } printf("Testing 16 bit fast saturation from double\n"); if (ffastsaturate(10000.0) != 10000 || ffastsaturate(-10000.0) != -10000 || ffastsaturate(32767.0) != 32767 || ffastsaturate(-32768.0) != -32768 || ffastsaturate(32768.0) != 32767 || ffastsaturate(-32769.0) != -32768) { printf("Test failed.\n"); exit(2); } printf("Testing 16 bit float saturation from float\n"); if (ffsaturatef(10000.0f) != 10000.0f || ffsaturatef(-10000.0f) != -10000.0f || ffsaturatef(32767.0f) != 32767.0f || ffsaturatef(-32768.0f) != -32768.0f || ffsaturatef(32768.0f) != 32767.0f || ffsaturatef(-32769.0f) != -32768.0f) { printf("Test failed.\n"); exit(2); } printf("Testing 16 bit double saturation from double\n"); if (ffsaturate(10000.0) != 10000.0 || ffsaturate(-10000.0) != -10000.0 || ffsaturate(32767.0) != 32767.0 || ffsaturate(-32768.0) != -32768.0 || ffsaturate(32768.0) != 32767.0 || ffsaturate(-32769.0) != -32768.0) { printf("Test failed.\n"); exit(2); } printf("Testing 16 bit add\n"); if (saturated_add16(10000, 10000) != 20000 || saturated_add16(10000, -10000) != 0 || saturated_add16(-10000, 10000) != 0 || saturated_add16(-10000, -10000) != -20000 || saturated_add16(-30000, -30000) != INT16_MIN || saturated_add16(30000, 30000) != INT16_MAX) { printf("Test failed.\n"); exit(2); } printf("Testing 32 bit add\n"); if (saturated_add32(10000, 10000) != 20000 || saturated_add32(10000, -10000) != 0 || saturated_add32(-10000, 10000) != 0 || saturated_add32(-10000, -10000) != -20000 || saturated_add32(-2000000000, -2000000000) != INT32_MIN || saturated_add32(2000000000, 2000000000) != INT32_MAX) { printf("Test failed.\n"); exit(2); } printf("Testing 16 bit subtract\n"); if (saturated_sub16(10000, 10000) != 0 || saturated_sub16(10000, -10000) != 20000 || saturated_sub16(-10000, 10000) != -20000 || saturated_sub16(-10000, -10000) != 0 || saturated_sub16(-30000, 30000) != INT16_MIN || saturated_sub16(30000, -30000) != INT16_MAX) { printf("Test failed.\n"); exit(2); } printf("Testing 32 bit subtract\n"); if (saturated_sub32(10000, 10000) != 0 || saturated_sub32(10000, -10000) != 20000 || saturated_sub32(-10000, 10000) != -20000 || saturated_sub32(-10000, -10000) != 0 || saturated_sub32(-2000000000, 2000000000) != INT32_MIN || saturated_sub32(2000000000, -2000000000) != INT32_MAX) { printf("Test failed.\n"); exit(2); } printf("Testing 16 x 16 => 16 bit multiply\n"); if (saturated_mul16(100, 100) != 0 || saturated_mul16(255, 255) != 1 || saturated_mul16(32767, -32768) != -32767 || saturated_mul16(-32768, 32767) != -32767 || saturated_mul16(32767, 32767) != 32766 || saturated_mul16(-32768, -32768) != 32767) { printf("Test failed.\n"); exit(2); } printf("Testing 16 x 16 => 32 bit multiply\n"); if (saturated_mul16_32(100, 100) != 20000 || saturated_mul16_32(-100, 100) != -20000 || saturated_mul16_32(32767, -32768) != -2147418112 || saturated_mul16_32(-32768, 32767) != -2147418112 || saturated_mul16_32(32767, 32767) != 2147352578 || saturated_mul16_32(-32768, -32768) != -2147483648) { printf("Test failed.\n"); exit(2); } printf("Testing 16 bit absolute\n"); if (saturated_abs16(10000) != 10000 || saturated_abs16(-10000) != 10000 || saturated_abs16(32767) != 32767 || saturated_abs16(-32768) != 32767) { printf("Test failed.\n"); exit(2); } printf("Tests passed.\n"); return 0; }
static int image_resize_row(image_translate_state_t *s, uint8_t buf[], size_t len) { int i; int output_width; int output_length; int input_width; int input_length; int x; double c1; double c2; #if defined(SPANDSP_USE_FIXED_POINT) int frac_row; int frac_col; #else double int_part; double frac_row; double frac_col; #endif 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; if (skip >= s->raw_input_row) { skip++; while (skip >= s->raw_input_row) { if (s->raw_input_row >= s->input_length) { s->raw_output_row = -1; break; } row_len = get_and_scrunch_row(s, s->raw_pixel_row[0], s->input_width*s->bytes_per_pixel); if (row_len != s->input_width*s->bytes_per_pixel) { 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*input_length/output_length; frac_row = s->raw_output_row*input_length - frac_row*output_length; for (i = 0; i < output_width; i++) { x = i*input_width/output_width; frac_col = x - x*output_width; c1 = s->raw_pixel_row[0][x] + (s->raw_pixel_row[0][x + 1] - s->raw_pixel_row[0][x])*frac_col; c2 = s->raw_pixel_row[1][x] + (s->raw_pixel_row[1][x + 1] - s->raw_pixel_row[1][x])*frac_col; buf[i] = saturateu8(c1 + (c2 - c1)*frac_row); } #else frac_row = modf((double) s->raw_output_row*input_length/output_length, &int_part); for (i = 0; i < output_width; i++) { frac_col = modf((double) i*input_width/output_width, &int_part); x = int_part; c1 = s->raw_pixel_row[0][x] + (s->raw_pixel_row[0][x + 1] - s->raw_pixel_row[0][x])*frac_col; c2 = s->raw_pixel_row[1][x] + (s->raw_pixel_row[1][x + 1] - s->raw_pixel_row[1][x])*frac_col; buf[i] = saturateu8(c1 + (c2 - c1)*frac_row); } #endif if (++s->raw_output_row >= s->output_length) s->raw_output_row = -1; return len; }