Browse Source

image: ordered dithering is now complete.

undefined
Sam Hocevar 10 years ago
parent
commit
e064118dc1
7 changed files with 106 additions and 148 deletions
  1. +0
    -1
      TODO
  2. +1
    -1
      src/Makefile.am
  3. +48
    -118
      src/image/dither/ordered.cpp
  4. +50
    -28
      src/image/stock.cpp
  5. +3
    -0
      src/lol/image/image.h
  6. +1
    -0
      src/lolcore.vcxproj
  7. +3
    -0
      src/lolcore.vcxproj.filters

+ 0
- 1
TODO View File

@@ -35,7 +35,6 @@ Image:
· crop.cpp
· dither.cpp
· dither/dbs.cpp
· dither/ordered.cpp
· filter/blur.cpp
· filter/color.cpp
· filter/dilate.cpp


+ 1
- 1
src/Makefile.am View File

@@ -117,7 +117,7 @@ liblolcore_sources = \
image/codec/dummy-image.cpp \
image/color/cie1931.cpp image/color/color.cpp \
image/dither/random.cpp image/dither/ediff.cpp \
image/dither/ostromoukhov.cpp \
image/dither/ostromoukhov.cpp image/dither/ordered.cpp \
image/filter/autocontrast.cpp image/filter/convolution.cpp \
image/render/noise.cpp \
\


+ 48
- 118
src/image/dither/ordered.cpp View File

@@ -1,144 +1,74 @@
/*
* libpipi Pathetic image processing interface library
* Copyright (c) 2004-2008 Sam Hocevar <sam@zoy.org>
* 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.
*/
//
// 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"

/*
* ordered.c: Bayer ordered dithering functions
* Bayer ordered dithering functions
*/

#include "config.h"

#include <stdlib.h>
#include <math.h>
#ifndef M_PI
# define M_PI 3.14159265358979323846
#endif

#include "pipi.h"
#include "pipi-internals.h"

pipi_image_t *pipi_dither_halftone(pipi_image_t *img, double r, double angle)
namespace lol
{
#define PRECISION 4.
pipi_image_t *ret, *kernel;
int k = (r * PRECISION / 2. / sqrt(2.) + .5);

kernel = pipi_render_halftone(k, k);
ret = pipi_dither_ordered_ext(img, kernel, 1. / PRECISION, angle + 45.);
pipi_free(kernel);
static Image DitherHelper(Image const &image, Array2D<float> const &kernel,
float scale, float angle);

return ret;
Image Image::DitherOrdered(Array2D<float> const &kernel) const
{
return DitherHelper(*this, kernel, 1.0f, 0.0f);
}

pipi_image_t *pipi_dither_ordered(pipi_image_t *img, pipi_image_t *kernel)
Image Image::DitherHalftone(float radius, float angle) const
{
return pipi_dither_ordered_ext(img, kernel, 1.0, 0.0);
/* Increasing the precision is necessary or the rotation will look
* like crap. So we create a kernel PRECISION times larger, and ask
* the ditherer to scale it by 1/PRECISION. */
float const PRECISION = 4.f;
int k = (radius * PRECISION * lol::sqrt(2.f) + 0.5f);
Array2D<float> kernel = Image::HalftoneKernel(ivec2(k, k));

return DitherHelper(*this, kernel, 1.f / PRECISION, angle + F_PI / 4.f);
}

pipi_image_t *pipi_dither_ordered_ext(pipi_image_t *img, pipi_image_t *kernel,
double scale, double angle)
static Image DitherHelper(Image const &image, Array2D<float> const &kernel,
float scale, float angle)
{
double sint, cost;
pipi_image_t *dst;
pipi_pixels_t *dstp, *kernelp;
float *dstdata, *kerneldata;
int x, y, w, h, kx, ky, kw, kh;

w = img->w;
h = img->h;
kw = kernel->w;
kh = kernel->h;
ivec2 size = image.GetSize();
ivec2 ksize = kernel.GetSize();

cost = cos(angle * (M_PI / 180));
sint = sin(angle * (M_PI / 180));
float cost = lol::cos(angle);
float sint = lol::sin(angle);

dst = pipi_copy(img);
dstp = pipi_get_pixels(dst, PIPI_PIXELS_Y_F32);
dstdata = (float *)dstp->pixels;
Image ret = image;
float *dstp = ret.Lock<PixelFormat::Y_F32>();

kernelp = pipi_get_pixels(kernel, PIPI_PIXELS_Y_F32);
kerneldata = (float *)kernelp->pixels;

for(y = 0; y < h; y++)
for (int y = 0; y < size.y; y++)
{
for(x = 0; x < w; x++)
for (int x = 0; x < size.x; x++)
{
float p, q;

kx = (int)((cost * x - sint * y + 2 * w * h) / scale) % kw;
ky = (int)((cost * y + sint * x + 2 * w * h) / scale) % kh;
int kx = (int)((cost * x - sint * y + 2 * size.x * size.y) / scale) % ksize.x;
int ky = (int)((cost * y + sint * x + 2 * size.x * size.y) / scale) % ksize.y;

p = dstdata[y * w + x];
q = p > kerneldata[ky * kw + kx] ? 1. : 0.;
dstdata[y * w + x] = q;
float p = dstp[y * size.x + x];
dstp[y * size.x + x] = (p > kernel[kx][ky]) ? 1.f : 0.f;
}
}

return dst;
}

typedef struct
{
int x, y;
double val;
}
dot_t;
ret.Unlock(dstp);

static int cmpdot(const void *p1, const void *p2)
{
return ((dot_t const *)p1)->val > ((dot_t const *)p2)->val;
return ret;
}

pipi_image_t *pipi_order(pipi_image_t *src)
{
double epsilon;
pipi_image_t *dst;
pipi_pixels_t *dstp, *srcp;
float *dstdata, *srcdata;
dot_t *circle;
int x, y, w, h, n;

w = src->w;
h = src->h;
epsilon = 1. / (w * h + 1);

srcp = pipi_get_pixels(src, PIPI_PIXELS_Y_F32);
srcdata = (float *)srcp->pixels;

dst = pipi_new(w, h);
dstp = pipi_get_pixels(dst, PIPI_PIXELS_Y_F32);
dstdata = (float *)dstp->pixels;

circle = malloc(w * h * sizeof(dot_t));

for(y = 0; y < h; y++)
for(x = 0; x < w; x++)
{
circle[y * w + x].x = x;
circle[y * w + x].y = y;
circle[y * w + x].val = srcdata[y * w + x];
}
qsort(circle, w * h, sizeof(dot_t), cmpdot);

for(n = 0; n < w * h; n++)
{
x = circle[n].x;
y = circle[n].y;
dstdata[y * w + x] = (float)(n + 1) * epsilon;
}

free(circle);

return dst;
}
} /* namespace lol */


+ 50
- 28
src/image/stock.cpp View File

@@ -72,51 +72,73 @@ Array2D<float> Image::BayerKernel(ivec2 size)
return ret;
}

Array2D<float> Image::HalftoneKernel(ivec2 size)
{
Array2D<float> ret(size);

for (int y = 0; y < size.y; y++)
for (int x = 0; x < size.x; x++)
{
float dx = 2.f * x / size.x - 0.5f;
float dy = 2.f * (y + 0.07f) / size.y - 0.5f;
bool flip = false;
if (dx > 0.5f)
{
flip = !flip;
dx -= 1.0f;
}
if (dy > 0.5f)
{
flip = !flip;
dy -= 1.0f;
}
/* Using dx²+dy² here creates another interesting halftone. */
float r = - lol::cos(F_PI * (dx - dy)) - lol::cos(F_PI * (dx + dy));

ret[x][y] = flip ? 10.f - r : r;
}

return NormalizeKernel(ret);
}

struct Dot
{
int x, y;
float dist;
float val;
};

static int cmpdot(const void *p1, const void *p2)
{
return ((Dot const *)p1)->dist > ((Dot const *)p2)->dist;
return ((Dot const *)p1)->val > ((Dot const *)p2)->val;
}

Array2D<float> Image::HalftoneKernel(ivec2 size)
Array2D<float> Image::NormalizeKernel(Array2D<float> const &kernel)
{
Array2D<float> ret(size);
ivec2 size = kernel.GetSize();

ivec2 csize = (size + ivec2(1)) / 2;
Array<Dot> circle;
circle.Resize(csize.x * csize.y);
for (int y = 0; y < csize.y; y++)
for (int x = 0; x < csize.x; x++)
Array<Dot> tmp;
tmp.Resize(size.x * size.y);
for (int y = 0; y < size.y; y++)
for (int x = 0; x < size.x; x++)
{
float dy = ((float)y + 0.07f) / csize.y - 0.5f;
float dx = (float)x / csize.x - 0.5f;
/* Using dx²+dy² here creates another interesting halftone. */
float r = - lol::cos(F_PI * (dx - dy)) - lol::cos(F_PI * (dx + dy));
circle[y * csize.x + x].x = x;
circle[y * csize.x + x].y = y;
circle[y * csize.x + x].dist = r;
tmp[y * size.x + x].x = x;
tmp[y * size.x + x].y = y;
tmp[y * size.x + x].val = kernel[x][y];
}
/* FIXME: use std::sort here */
std::qsort(circle.Data(), csize.x * csize.y, sizeof(Dot), cmpdot);
std::qsort(tmp.Data(), size.x * size.y, sizeof(Dot), cmpdot);

float mul = 1.f / (csize.x * csize.y * 4 + 1);
for (int n = 0; n < csize.x * csize.y; n++)
{
int x = circle[n].x;
int y = circle[n].y;
Array2D<float> dst(size);

ret[x][y] = (float)(2 * n + 1) * mul;
ret[x + csize.x][y + csize.y] = (float)(2 * n + 2) * mul;
ret[x][y + csize.y] = 1.0f - (float)(2 * n + 1) * mul;
ret[x + csize.x][y] = 1.0f - (float)(2 * n + 2) * mul;
float epsilon = 1.f / (size.x * size.y + 1);
for (int n = 0; n < size.x * size.y; n++)
{
int x = tmp[n].x;
int y = tmp[n].y;
dst[x][y] = (float)(n + 1) * epsilon;
}

return ret;
return dst;
}

Array2D<float> Image::EdiffKernel(EdiffAlgorithm algorithm)


+ 3
- 0
src/lol/image/image.h View File

@@ -94,6 +94,7 @@ public:
static Array2D<float> BayerKernel(ivec2 size);
static Array2D<float> HalftoneKernel(ivec2 size);
static Array2D<float> EdiffKernel(EdiffAlgorithm algorithm);
static Array2D<float> NormalizeKernel(Array2D<float> const &kernel);

/* Rendering */
bool Stock(char const *desc);
@@ -107,6 +108,8 @@ public:
Image DitherEdiff(Array2D<float> const &kernel,
ScanMode scan = ScanMode::Raster) const;
Image DitherOstromoukhov(ScanMode scan = ScanMode::Raster) const;
Image DitherOrdered(Array2D<float> const &kernel) const;
Image DitherHalftone(float radius, float angle) const;

private:
class ImageData *m_data;


+ 1
- 0
src/lolcore.vcxproj View File

@@ -152,6 +152,7 @@
<ClCompile Include="image\filter\autocontrast.cpp" />
<ClCompile Include="image\filter\convolution.cpp" />
<ClCompile Include="image\dither\ediff.cpp" />
<ClCompile Include="image\dither\ordered.cpp" />
<ClCompile Include="image\dither\ostromoukhov.cpp" />
<ClCompile Include="image\dither\random.cpp" />
<ClCompile Include="image\image.cpp" />


+ 3
- 0
src/lolcore.vcxproj.filters View File

@@ -358,6 +358,9 @@
<ClCompile Include="image\dither\ediff.cpp">
<Filter>image\dither</Filter>
</ClCompile>
<ClCompile Include="image\dither\ordered.cpp">
<Filter>image\dither</Filter>
</ClCompile>
<ClCompile Include="image\dither\ostromoukhov.cpp">
<Filter>image\dither</Filter>
</ClCompile>


Loading…
Cancel
Save