From 3f31901de244c1dc1d0d8adcb6809a8c013f0b2d Mon Sep 17 00:00:00 2001 From: sam Date: Sun, 17 Oct 2010 00:57:27 +0000 Subject: [PATCH] Implement bicubic resampling. Lacks some blurring in the pre-pass, maybe. git-svn-id: file:///srv/caca.zoy.org/var/lib/svn/libpipi/trunk@4696 92316355-f0b4-4df1-b90c-862c8a59935f --- genethumb/genethumb.c | 2 +- pipi-php/php_pipi.c | 20 +++++- pipi/Makefile.am | 3 +- pipi/codec/oric.c | 2 +- pipi/context.c | 4 +- pipi/pipi.h | 3 +- pipi/resample/bicubic.c | 136 ++++++++++++++++++++++++++++++++++++++ pipi/resample/bresenham.c | 2 +- 8 files changed, 162 insertions(+), 10 deletions(-) create mode 100644 pipi/resample/bicubic.c diff --git a/genethumb/genethumb.c b/genethumb/genethumb.c index 1c76257..c58212c 100644 --- a/genethumb/genethumb.c +++ b/genethumb/genethumb.c @@ -95,7 +95,7 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - dst = pipi_resize(src, w, h); + dst = pipi_resize_bicubic(src, w, h); if(bpp == 16) pipi_dither_24to16(dst); pipi_save(dst, dstname); diff --git a/pipi-php/php_pipi.c b/pipi-php/php_pipi.c index dc7f0b4..16c93f2 100644 --- a/pipi-php/php_pipi.c +++ b/pipi-php/php_pipi.c @@ -37,7 +37,8 @@ static function_entry pipi_functions[] = { PHP_FE(pipi_get_format_name, NULL) PHP_FE(pipi_measure_msd, NULL) PHP_FE(pipi_measure_rmsd, NULL) - PHP_FE(pipi_resize, NULL) + PHP_FE(pipi_resize_bicubic, NULL) + PHP_FE(pipi_resize_bresenham, NULL) PHP_FE(pipi_render_random, NULL) PHP_FE(pipi_render_bayer, NULL) PHP_FE(pipi_render_halftone, NULL) @@ -339,7 +340,7 @@ PHP_FUNCTION(pipi_measure_rmsd) { RETURN_LONG(pipi_measure_rmsd(img1, img2)); } -PHP_FUNCTION(pipi_resize) { +PHP_FUNCTION(pipi_resize_bicubic) { zval *_zval; long width, height = 0; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rll", &_zval, &width, &height) == FAILURE) { @@ -348,7 +349,20 @@ PHP_FUNCTION(pipi_resize) { pipi_image_t *src, *result; ZEND_FETCH_RESOURCE(src, pipi_image_t*, &_zval, -1, PHP_PIPI_IMAGE_RES_NAME, le_pipi_image); - result = pipi_resize(src, width, height); + result = pipi_resize_bicubic(src, width, height); + ZEND_REGISTER_RESOURCE(return_value, result, le_pipi_image); +} + +PHP_FUNCTION(pipi_resize_bresenham) { + zval *_zval; + long width, height = 0; + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rll", &_zval, &width, &height) == FAILURE) { + RETURN_FALSE; + } + pipi_image_t *src, *result; + ZEND_FETCH_RESOURCE(src, pipi_image_t*, &_zval, -1, PHP_PIPI_IMAGE_RES_NAME, le_pipi_image); + + result = pipi_resize_bresenham(src, width, height); ZEND_REGISTER_RESOURCE(return_value, result, le_pipi_image); } diff --git a/pipi/Makefile.am b/pipi/Makefile.am index 4f8d985..d1bce5b 100644 --- a/pipi/Makefile.am +++ b/pipi/Makefile.am @@ -51,7 +51,8 @@ codec_sources = \ codec/oric.c resample_sources = \ - resample/bresenham.c + resample/bresenham.c \ + resample/bicubic.c paint_sources = \ paint/floodfill.c \ diff --git a/pipi/codec/oric.c b/pipi/codec/oric.c index 7298338..8a1c1b1 100644 --- a/pipi/codec/oric.c +++ b/pipi/codec/oric.c @@ -141,7 +141,7 @@ int pipi_save_oric(pipi_image_t *img, char const *name) fwrite("\x00", 1, 1, fp); if(img->w != WIDTH || img->h != HEIGHT) - tmp = pipi_resize(img, WIDTH, HEIGHT); + tmp = pipi_resize_bicubic(img, WIDTH, HEIGHT); else tmp = img; p = pipi_get_pixels(tmp, PIPI_PIXELS_RGBA_F32); diff --git a/pipi/context.c b/pipi/context.c index c24a318..2f07fe2 100644 --- a/pipi/context.c +++ b/pipi/context.c @@ -295,7 +295,7 @@ int pipi_command(pipi_context_t *ctx, char const *cmd, ...) src = ctx->images[ctx->nimages - 1]; switch(cmd[0]) { - case 'g': dst = pipi_resize(src, w, h); break; + case 'g': dst = pipi_resize_bicubic(src, w, h); break; case 't': dst = pipi_tile(src, w, h); break; } if(dst == NULL) @@ -322,7 +322,7 @@ int pipi_command(pipi_context_t *ctx, char const *cmd, ...) h = (int)(scale * src->h + 0.5); if(w <= 0 || h <= 0) return -1; - dst = pipi_resize(src, w, h); + dst = pipi_resize_bicubic(src, w, h); if(dst == NULL) return -1; pipi_free(src); diff --git a/pipi/pipi.h b/pipi/pipi.h index be24ad7..25881d2 100644 --- a/pipi/pipi.h +++ b/pipi/pipi.h @@ -159,7 +159,8 @@ __extern const char* pipi_get_format_name(int format); __extern double pipi_measure_msd(pipi_image_t *, pipi_image_t *); __extern double pipi_measure_rmsd(pipi_image_t *, pipi_image_t *); -__extern pipi_image_t *pipi_resize(pipi_image_t *, int, int); +__extern pipi_image_t *pipi_resize_bresenham(pipi_image_t *, int, int); +__extern pipi_image_t *pipi_resize_bicubic(pipi_image_t *, int, int); __extern pipi_image_t *pipi_crop(pipi_image_t *, int, int, int, int); __extern pipi_image_t *pipi_render_random(int, int); diff --git a/pipi/resample/bicubic.c b/pipi/resample/bicubic.c new file mode 100644 index 0000000..559e8f2 --- /dev/null +++ b/pipi/resample/bicubic.c @@ -0,0 +1,136 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2010 Sam Hocevar + * All Rights Reserved + * + * $Id$ + * + * This library is free software. It comes without any warranty, to + * the extent permitted by applicable law. You can redistribute it + * and/or modify it under the terms of the Do What The Fuck You Want + * To Public License, Version 2, as published by Sam Hocevar. See + * http://sam.zoy.org/wtfpl/COPYING for more details. + */ + +/* + * bicubic.c: Bicubic image resizing functions + */ + +#include "config.h" + +#include +#include + +#include "pipi.h" +#include "pipi_internals.h" + +static inline int min_int(int a, int b) { return a < b ? a : b; } +static inline int max_int(int a, int b) { return a > b ? a : b; } + +pipi_image_t *pipi_resize_bicubic(pipi_image_t *src, int w, int h) +{ + float *srcdata, *dstdata, *p0, *p1, *p2, *p3; + pipi_image_t *dst; + pipi_pixels_t *srcp, *dstp; + int x, y, i, sw, dw, sh, dh, i0, i1, i2, i3; + float scalex, scaley; + + srcp = pipi_get_pixels(src, PIPI_PIXELS_RGBA_F32); + srcdata = (float *)srcp->pixels; + + dst = pipi_new(w, h); + dstp = pipi_get_pixels(dst, PIPI_PIXELS_RGBA_F32); + dstdata = (float *)dstp->pixels; + + sw = src->w; sh = src->h; + dw = dst->w; dh = dst->h; + + scalex = dw > 1 ? (float)(sw - 1) / (dw - 1) : 1.0f; + scaley = dh > 1 ? (float)(sh - 1) / (dh - 1) : 1.0f; + + for(y = 0; y < dh; y++) + { + float yfloat = scaley * y; + int yint = (int)yfloat; + float y1 = yfloat - yint; + + p0 = srcdata + 4 * sw * min_int(max_int(0, yint - 1), sh - 1); + p1 = srcdata + 4 * sw * min_int(max_int(0, yint ), sh - 1); + p2 = srcdata + 4 * sw * min_int(max_int(0, yint + 1), sh - 1); + p3 = srcdata + 4 * sw * min_int(max_int(0, yint + 2), sh - 1); + + for (x = 0; x < dw; x++) + { + float xfloat = scalex * x; + int xint = (int)xfloat; + float x1 = xfloat - xint; + + i0 = 4 * min_int(max_int(0, xint - 1), sw - 1); + i1 = 4 * min_int(max_int(0, xint ), sw - 1); + i2 = 4 * min_int(max_int(0, xint + 1), sw - 1); + i3 = 4 * min_int(max_int(0, xint + 2), sw - 1); + + for (i = 0; i < 4; i++, i0++, i1++, i2++, i3++) + { + float a00 = p1[i1]; + float a01 = .5f * (p2[i1] - p0[i1]); + float a02 = p0[i1] - 2.5f * p1[i1] + + 2.f * p2[i1] - .5f * p3[i1]; + float a03 = .5f * (p3[i1] - p0[i1]) + 1.5f * (p1[i1] - p2[i1]); + + float a10 = .5f * (p1[i2] - p1[i0]); + float a11 = .25f * (p0[i0] - p2[i0] - p0[i2] + p2[i2]); + float a12 = .5f * (p0[i2] - p0[i0]) + 1.25f * (p1[i0] - p1[i2]) + + .25f * (p3[i0] - p3[i2]) + p2[i2] - p2[i0]; + float a13 = .25f * (p0[i0] - p3[i0] - p0[i2] + p3[i2]) + + .75f * (p2[i0] - p1[i0] + p1[i2] - p2[i2]); + + float a20 = p1[i0] - 2.5f * p1[i1] + + 2.f * p1[i2] - .5f * p1[i3]; + float a21 = .5f * (p2[i0] - p0[i0]) + 1.25f * (p0[i1] - p2[i1]) + + .25f * (p0[i3] - p2[i3]) - p0[i2] + p2[i2]; + float a22 = p0[i0] - p3[i2] - 2.5f * (p1[i0] + p0[i1]) + + 2.f * (p2[i0] + p0[i2]) - .5f * (p3[i0] + p0[i3]) + + 6.25f * p1[i1] - 5.f * (p2[i1] + p1[i2]) + + 1.25f * (p3[i1] + p1[i3]) + + 4.f * p2[i2] - p2[i3] + .25f * p3[i3]; + float a23 = 1.5f * (p1[i0] - p2[i0]) + .5f * (p3[i0] - p0[i0]) + + 1.25f * (p0[i1] - p3[i1]) + + 3.75f * (p2[i1] - p1[i1]) + p3[i2] - p0[i2] + + 3.f * (p1[i2] - p2[i2]) + .25f * (p0[i3] - p3[i3]) + + .75f * (p2[i3] - p1[i3]); + + float a30 = .5f * (p1[i3] - p1[i0]) + 1.5f * (p1[i1] - p1[i2]); + float a31 = .25f * (p0[i0] - p2[i0]) + .25f * (p2[i3] - p0[i3]) + + .75f * (p2[i1] - p0[i1] + p0[i2] - p2[i2]); + float a32 = -.5f * p0[i0] + 1.25f * p1[i0] - p2[i0] + + .25f * p3[i0] + 1.5f * p0[i1] - 3.75f * p1[i1] + + 3.f * p2[i1] - .75f * p3[i1] - 1.5f * p0[i2] + + 3.75f * p1[i2] - 3.f * p2[i2] + .75f * p3[i2] + + .5f * p0[i3] - 1.25f * p1[i3] + p2[i3] + - .25f * p3[i3]; + float a33 = .25f * p0[i0] - .75f * p1[i0] + .75f * p2[i0] + - .25f * p3[i0] - .75f * p0[i1] + 2.25f * p1[i1] + - 2.25f * p2[i1] + .75f * p3[i1] + .75f * p0[i2] + - 2.25f * p1[i2] + 2.25f * p2[i2] - .75f * p3[i2] + - .25f * p0[i3] + .75f * p1[i3] - .75f * p2[i3] + + .25f * p3[i3]; + + float y2 = y1 * y1; float y3 = y2 * y1; + float x2 = x1 * x1; float x3 = x2 * x1; + + float p = a00 + a01 * y1 + a02 * y2 + a03 * y3 + + + a10 * x1 + a11 * x1 * y1 + a12 * x1 * y2 + a13 * x1 * y3 + + a20 * x2 + a21 * x2 * y1 + a22 * x2 * y2 + a23 * x2 * y3 + + a30 * x3 + a31 * x3 * y1 + a32 * x3 * y2 + a33 * x3 * y3; + if (p < 0.0f) p = 0.0f; + if (p > 1.0f) p = 1.0f; + + dstdata[(y * dw + x) * 4 + i] = p; + } + } + } + + return dst; +} + diff --git a/pipi/resample/bresenham.c b/pipi/resample/bresenham.c index 4601411..bd9afa1 100644 --- a/pipi/resample/bresenham.c +++ b/pipi/resample/bresenham.c @@ -31,7 +31,7 @@ /* FIXME: the algorithm does not handle alpha components properly. Resulting * alpha should be the mean alpha value of the neightbouring pixels, but * the colour components should be weighted with the alpha value. */ -pipi_image_t *pipi_resize(pipi_image_t *src, int w, int h) +pipi_image_t *pipi_resize_bresenham(pipi_image_t *src, int w, int h) { float *srcdata, *dstdata, *aline, *line; pipi_image_t *dst;