From 967ce8141b711db95de769166cc0de8aa4abcd2b Mon Sep 17 00:00:00 2001 From: Sam Hocevar Date: Sun, 22 Jun 2014 15:03:23 +0000 Subject: [PATCH] image: bicubic and Bresenham resampling. --- TODO | 2 - src/Makefile.am | 3 +- src/image/{render => }/noise.cpp | 0 src/image/resample.cpp | 212 +++++++++++++++++++++++++++++++ src/image/resample/bicubic.cpp | 136 -------------------- src/image/resample/bresenham.cpp | 131 ------------------- src/lol/image/image.h | 11 +- src/lolcore.vcxproj | 3 +- src/lolcore.vcxproj.filters | 10 +- 9 files changed, 230 insertions(+), 278 deletions(-) rename src/image/{render => }/noise.cpp (100%) create mode 100644 src/image/resample.cpp delete mode 100644 src/image/resample/bicubic.cpp delete mode 100644 src/image/resample/bresenham.cpp diff --git a/TODO b/TODO index f2d45bb4..6013beb3 100644 --- a/TODO +++ b/TODO @@ -46,7 +46,5 @@ Image: · pipi-internals.h · pipi-template.h · quantize/reduce.cpp - · resample/bicubic.cpp - · resample/bresenham.cpp · sequence.cpp diff --git a/src/Makefile.am b/src/Makefile.am index 1b907ff8..8da3efdd 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -111,7 +111,7 @@ liblolcore_sources = \ sys/thread.cpp sys/threadbase.h \ \ image/image.cpp image/image-private.h image/kernel.cpp image/pixels.cpp \ - image/crop.cpp \ + image/crop.cpp image/resample.cpp image/noise.cpp \ image/codec/gdiplus-image.cpp image/codec/imlib2-image.cpp \ image/codec/sdl-image.cpp image/codec/ios-image.cpp \ image/codec/zed-image.cpp image/codec/zed-palette-image.cpp \ @@ -121,7 +121,6 @@ liblolcore_sources = \ image/dither/ostromoukhov.cpp image/dither/ordered.cpp \ image/filter/convolution.cpp image/filter/color.cpp \ image/filter/dilate.cpp image/filter/median.cpp image/filter/yuv.cpp \ - image/render/noise.cpp \ \ loldebug.h \ debug/fps.cpp debug/fps.h debug/lines.cpp \ diff --git a/src/image/render/noise.cpp b/src/image/noise.cpp similarity index 100% rename from src/image/render/noise.cpp rename to src/image/noise.cpp diff --git a/src/image/resample.cpp b/src/image/resample.cpp new file mode 100644 index 00000000..9db75847 --- /dev/null +++ b/src/image/resample.cpp @@ -0,0 +1,212 @@ +// +// Lol Engine +// +// Copyright: (c) 2004-2014 Sam Hocevar +// This program is free software; 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://www.wtfpl.net/ for more details. +// + +#if defined HAVE_CONFIG_H +# include "config.h" +#endif + +#include "core.h" + +/* + * Image resizing functions + */ + +namespace lol +{ + +static Image ResizeBicubic(Image &image, ivec2 size); +static Image ResizeBresenham(Image &image, ivec2 size); + +Image Image::Resize(ivec2 size, ResampleAlgorithm algorithm) +{ + switch (algorithm) + { + case ResampleAlgorithm::Bicubic: + return ResizeBicubic(*this, size); + case ResampleAlgorithm::Bresenham: + default: + return ResizeBresenham(*this, size); + } +} + +static Image ResizeBicubic(Image &image, ivec2 size) +{ + Image dst(size); + ivec2 const oldsize = image.GetSize(); + + vec4 const *srcp = image.Lock(); + vec4 *dstp = dst.Lock(); + + float scalex = size.x > 1 ? (oldsize.x - 1.f) / (size.x - 1) : 1.f; + float scaley = size.y > 1 ? (oldsize.y - 1.f) / (size.y - 1) : 1.f; + + for (int y = 0; y < size.y; ++y) + { + float yfloat = scaley * y; + int yint = (int)yfloat; + float y1 = yfloat - yint; + + vec4 const *p0 = srcp + oldsize.x * lol::min(lol::max(0, yint - 1), oldsize.y - 1); + vec4 const *p1 = srcp + oldsize.x * lol::min(lol::max(0, yint ), oldsize.y - 1); + vec4 const *p2 = srcp + oldsize.x * lol::min(lol::max(0, yint + 1), oldsize.y - 1); + vec4 const *p3 = srcp + oldsize.x * lol::min(lol::max(0, yint + 2), oldsize.y - 1); + + for (int x = 0; x < size.x; ++x) + { + float xfloat = scalex * x; + int xint = (int)xfloat; + float x1 = xfloat - xint; + + int const i0 = lol::min(lol::max(0, xint - 1), oldsize.x - 1); + int const i1 = lol::min(lol::max(0, xint ), oldsize.x - 1); + int const i2 = lol::min(lol::max(0, xint + 1), oldsize.x - 1); + int const i3 = lol::min(lol::max(0, xint + 2), oldsize.x - 1); + + vec4 a00 = p1[i1]; + vec4 a01 = .5f * (p2[i1] - p0[i1]); + vec4 a02 = p0[i1] - 2.5f * p1[i1] + + 2.f * p2[i1] - .5f * p3[i1]; + vec4 a03 = .5f * (p3[i1] - p0[i1]) + 1.5f * (p1[i1] - p2[i1]); + + vec4 a10 = .5f * (p1[i2] - p1[i0]); + vec4 a11 = .25f * (p0[i0] - p2[i0] - p0[i2] + p2[i2]); + vec4 a12 = .5f * (p0[i2] - p0[i0]) + 1.25f * (p1[i0] - p1[i2]) + + .25f * (p3[i0] - p3[i2]) + p2[i2] - p2[i0]; + vec4 a13 = .25f * (p0[i0] - p3[i0] - p0[i2] + p3[i2]) + + .75f * (p2[i0] - p1[i0] + p1[i2] - p2[i2]); + + vec4 a20 = p1[i0] - 2.5f * p1[i1] + + 2.f * p1[i2] - .5f * p1[i3]; + vec4 a21 = .5f * (p2[i0] - p0[i0]) + 1.25f * (p0[i1] - p2[i1]) + + .25f * (p0[i3] - p2[i3]) - p0[i2] + p2[i2]; + vec4 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]; + vec4 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]); + + vec4 a30 = .5f * (p1[i3] - p1[i0]) + 1.5f * (p1[i1] - p1[i2]); + vec4 a31 = .25f * (p0[i0] - p2[i0]) + .25f * (p2[i3] - p0[i3]) + + .75f * (p2[i1] - p0[i1] + p0[i2] - p2[i2]); + vec4 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]; + vec4 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; + + vec4 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; + + dstp[y * size.x + x] = lol::clamp(p, 0.f, 1.f); + } + } + + dst.Unlock(dstp); + image.Unlock(srcp); + + return dst; +} + +/* This is Bresenham resizing. I “rediscovered” it independently but + * it was actually first described in 1995 by Tim Kientzle in “Scaling + * Bitmaps with Bresenham”. */ + +/* 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. */ +static Image ResizeBresenham(Image &image, ivec2 size) +{ + Image dst(size); + ivec2 const oldsize = image.GetSize(); + float const invswsh = 1.0f / (oldsize.x * oldsize.y); + + vec4 const *srcp = image.Lock(); + vec4 *dstp = dst.Lock(); + + Array aline, line; + aline.Resize(size.x); + line.Resize(size.x); + memset(line.Data(), 0, line.Bytes()); + + int remy = 0; + + for (int y = 0, y0 = 0; y < size.y; y++) + { + memset(aline.Data(), 0, aline.Bytes()); + + for (int toty = 0; toty < oldsize.y; ) + { + if (remy == 0) + { + vec4 color(0.f); + int remx = 0; + + for (int x = 0, x0 = 0; x < size.x; x++) + { + vec4 acolor(0.f); + + for (int totx = 0; totx < oldsize.x; ) + { + if (remx == 0) + { + color = srcp[y0 * oldsize.x + x0]; + x0++; + remx = size.x; + } + + int nx = lol::min(remx, oldsize.x - totx); + acolor += (float)nx * color; + totx += nx; + remx -= nx; + } + + line[x] = acolor; + } + + y0++; + remy = size.y; + } + + int ny = lol::min(remy, oldsize.y - toty); + for (int x = 0; x < size.x; x++) + aline[x] += (float)ny * line[x]; + toty += ny; + remy -= ny; + } + + for (int x = 0; x < size.x; x++) + dstp[y * size.x + x] = aline[x] * invswsh; + } + + dst.Unlock(dstp); + image.Unlock(srcp); + + return dst; +} + +} /* namespace lol */ + diff --git a/src/image/resample/bicubic.cpp b/src/image/resample/bicubic.cpp deleted file mode 100644 index 6cf36658..00000000 --- a/src/image/resample/bicubic.cpp +++ /dev/null @@ -1,136 +0,0 @@ -/* - * 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/src/image/resample/bresenham.cpp b/src/image/resample/bresenham.cpp deleted file mode 100644 index 1611cfac..00000000 --- a/src/image/resample/bresenham.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/* - * libpipi Pathetic image processing interface library - * Copyright (c) 2004-2009 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. - */ - -/* - * bresenham.c: Bresenham image resizing functions - */ - -#include "config.h" - -#include -#include - -#include "pipi.h" -#include "pipi-internals.h" - -/* This is Bresenham resizing. I rediscovered it independently but it was - * actually first described in 1995 by Tim Kientzle in "Scaling Bitmaps - * with Bresenham". */ - -/* 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_bresenham(pipi_image_t *src, int w, int h) -{ - float *srcdata, *dstdata, *aline, *line; - pipi_image_t *dst; - pipi_pixels_t *srcp, *dstp; - int x, y, x0, y0, sw, dw, sh, dh, remy; - float invswsh; - - 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; - invswsh = 1.0f / (sw * sh); - - aline = malloc(4 * dw * sizeof(float)); - line = malloc(4 * dw * sizeof(float)); - - memset(line, 0, 4 * dw * sizeof(float)); - remy = 0; - - for(y = 0, y0 = 0; y < dh; y++) - { - int toty = 0, ny; - - memset(aline, 0, 4 * dw * sizeof(float)); - - while(toty < sh) - { - if(remy == 0) - { - float r = 0, g = 0, b = 0, a = 0; - int remx = 0; - - for(x = 0, x0 = 0; x < dw; x++) - { - float ar = 0, ag = 0, ab = 0, aa = 0; - int totx = 0, nx; - - while(totx < sw) - { - if(remx == 0) - { - r = srcdata[(y0 * sw + x0) * 4]; - g = srcdata[(y0 * sw + x0) * 4 + 1]; - b = srcdata[(y0 * sw + x0) * 4 + 2]; - a = srcdata[(y0 * sw + x0) * 4 + 3]; - x0++; - remx = dw; - } - - nx = (totx + remx <= sw) ? remx : sw - totx; - ar += nx * r; ag += nx * g; ab += nx * b; aa += nx * a; - totx += nx; - remx -= nx; - } - - line[4 * x] = ar; - line[4 * x + 1] = ag; - line[4 * x + 2] = ab; - line[4 * x + 3] = aa; - } - - y0++; - remy = dh; - } - - ny = (toty + remy <= sh) ? remy : sh - toty; - for(x = 0; x < dw; x++) - { - aline[4 * x] += ny * line[4 * x]; - aline[4 * x + 1] += ny * line[4 * x + 1]; - aline[4 * x + 2] += ny * line[4 * x + 2]; - aline[4 * x + 3] += ny * line[4 * x + 3]; - } - toty += ny; - remy -= ny; - } - - for(x = 0; x < dw; x++) - { - dstdata[(y * dw + x) * 4] = aline[4 * x] * invswsh; - dstdata[(y * dw + x) * 4 + 1] = aline[4 * x + 1] * invswsh; - dstdata[(y * dw + x) * 4 + 2] = aline[4 * x + 2] * invswsh; - dstdata[(y * dw + x) * 4 + 3] = aline[4 * x + 3] * invswsh; - } - } - - free(aline); - free(line); - - return dst; -} - diff --git a/src/lol/image/image.h b/src/lol/image/image.h index 0af48fba..71f74371 100644 --- a/src/lol/image/image.h +++ b/src/lol/image/image.h @@ -41,6 +41,12 @@ enum class ScanMode : uint8_t Serpentine, }; +enum class ResampleAlgorithm : uint8_t +{ + Bicubic, + Bresenham, +}; + enum class EdiffAlgorithm : uint8_t { FloydSteinberg, @@ -102,12 +108,15 @@ public: /* Rendering */ bool RenderRandom(ivec2 size); + /* Resize and crop */ + Image Resize(ivec2 size, ResampleAlgorithm algorithm); + Image Crop(ibox2 box) const; + /* Image processing */ Image AutoContrast() const; Image Brightness(float val) const; Image Contrast(float val) const; Image Convolution(Array2D const &kernel); - Image Crop(ibox2 box) const; Image Dilate(); Image Erode(); Image Invert() const; diff --git a/src/lolcore.vcxproj b/src/lolcore.vcxproj index d20b7c35..af2858bd 100644 --- a/src/lolcore.vcxproj +++ b/src/lolcore.vcxproj @@ -161,8 +161,9 @@ + - + diff --git a/src/lolcore.vcxproj.filters b/src/lolcore.vcxproj.filters index 33f861b3..1c3a4898 100644 --- a/src/lolcore.vcxproj.filters +++ b/src/lolcore.vcxproj.filters @@ -91,9 +91,6 @@ {3f420a7d-0538-463a-925b-3f8968bf628e} - - {23655fca-56e5-48ec-8cf0-a71322f4cc89} - @@ -391,8 +388,11 @@ image\dither - - image\render + + image + + + image image