Browse Source

image: bicubic and Bresenham resampling.

undefined
Sam Hocevar 10 years ago
parent
commit
967ce8141b
9 changed files with 230 additions and 278 deletions
  1. +0
    -2
      TODO
  2. +1
    -2
      src/Makefile.am
  3. +0
    -0
      src/image/noise.cpp
  4. +212
    -0
      src/image/resample.cpp
  5. +0
    -136
      src/image/resample/bicubic.cpp
  6. +0
    -131
      src/image/resample/bresenham.cpp
  7. +10
    -1
      src/lol/image/image.h
  8. +2
    -1
      src/lolcore.vcxproj
  9. +5
    -5
      src/lolcore.vcxproj.filters

+ 0
- 2
TODO View File

@@ -46,7 +46,5 @@ Image:
· pipi-internals.h
· pipi-template.h
· quantize/reduce.cpp
· resample/bicubic.cpp
· resample/bresenham.cpp
· sequence.cpp


+ 1
- 2
src/Makefile.am View File

@@ -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 \


src/image/render/noise.cpp → src/image/noise.cpp View File


+ 212
- 0
src/image/resample.cpp View File

@@ -0,0 +1,212 @@
//
// Lol Engine
//
// Copyright: (c) 2004-2014 Sam Hocevar <sam@hocevar.net>
// 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<PixelFormat::RGBA_F32>();
vec4 *dstp = dst.Lock<PixelFormat::RGBA_F32>();

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<PixelFormat::RGBA_F32>();
vec4 *dstp = dst.Lock<PixelFormat::RGBA_F32>();

Array<vec4> 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 */


+ 0
- 136
src/image/resample/bicubic.cpp View File

@@ -1,136 +0,0 @@
/*
* libpipi Pathetic image processing interface library
* Copyright (c) 2004-2010 Sam Hocevar <sam@hocevar.net>
* 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 <stdlib.h>
#include <string.h>

#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;
}


+ 0
- 131
src/image/resample/bresenham.cpp View File

@@ -1,131 +0,0 @@
/*
* libpipi Pathetic image processing interface library
* Copyright (c) 2004-2009 Sam Hocevar <sam@hocevar.net>
* 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 <stdlib.h>
#include <string.h>

#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;
}


+ 10
- 1
src/lol/image/image.h View File

@@ -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<float> const &kernel);
Image Crop(ibox2 box) const;
Image Dilate();
Image Erode();
Image Invert() const;


+ 2
- 1
src/lolcore.vcxproj View File

@@ -161,8 +161,9 @@
<ClCompile Include="image\crop.cpp" />
<ClCompile Include="image\image.cpp" />
<ClCompile Include="image\kernel.cpp" />
<ClCompile Include="image\noise.cpp" />
<ClCompile Include="image\pixels.cpp" />
<ClCompile Include="image\render\noise.cpp" />
<ClCompile Include="image\resample.cpp" />
<ClCompile Include="input\controller.cpp" />
<ClCompile Include="input\input.cpp" />
<ClCompile Include="light.cpp" />


+ 5
- 5
src/lolcore.vcxproj.filters View File

@@ -91,9 +91,6 @@
<Filter Include="image\filter">
<UniqueIdentifier>{3f420a7d-0538-463a-925b-3f8968bf628e}</UniqueIdentifier>
</Filter>
<Filter Include="image\render">
<UniqueIdentifier>{23655fca-56e5-48ec-8cf0-a71322f4cc89}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="image\crop.cpp">
@@ -391,8 +388,11 @@
<ClCompile Include="image\dither\random.cpp">
<Filter>image\dither</Filter>
</ClCompile>
<ClCompile Include="image\render\noise.cpp">
<Filter>image\render</Filter>
<ClCompile Include="image\noise.cpp">
<Filter>image</Filter>
</ClCompile>
<ClCompile Include="image\resample.cpp">
<Filter>image</Filter>
</ClCompile>
<ClCompile Include="image\pixels.cpp">
<Filter>image</Filter>


Loading…
Cancel
Save