@@ -32,18 +32,14 @@ Image: | |||
· combine/rgb.cpp | |||
· combine/subadd.cpp | |||
· context.cpp | |||
· crop.cpp | |||
· dither.cpp | |||
· dither/dbs.cpp | |||
· filter/blur.cpp | |||
· filter/color.cpp | |||
· filter/dilate.cpp | |||
· filter/median.cpp | |||
· filter/rotate.cpp | |||
· filter/sharpen.cpp | |||
· filter/transform.cpp | |||
· filter/wave.cpp | |||
· filter/yuv.cpp | |||
· paint/bezier.cpp | |||
· paint/floodfill.cpp | |||
· paint/line.cpp | |||
@@ -111,6 +111,7 @@ liblolcore_sources = \ | |||
sys/thread.cpp sys/threadbase.h \ | |||
\ | |||
image/image.cpp image/image-private.h image/stock.cpp image/pixels.cpp \ | |||
image/crop.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 \ | |||
@@ -119,6 +120,7 @@ liblolcore_sources = \ | |||
image/dither/random.cpp image/dither/ediff.cpp \ | |||
image/dither/ostromoukhov.cpp image/dither/ordered.cpp \ | |||
image/filter/autocontrast.cpp image/filter/convolution.cpp \ | |||
image/filter/median.cpp image/filter/yuv.cpp \ | |||
image/render/noise.cpp \ | |||
\ | |||
loldebug.h \ | |||
@@ -1,68 +1,69 @@ | |||
/* | |||
* 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. | |||
*/ | |||
// | |||
// 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. | |||
// | |||
/* | |||
* crop.c: image cropping functions | |||
*/ | |||
#if defined HAVE_CONFIG_H | |||
# include "config.h" | |||
#endif | |||
#include "config.h" | |||
#include "core.h" | |||
#include "image-private.h" | |||
#include <stdlib.h> | |||
#include <string.h> | |||
/* | |||
* Image cropping functions | |||
*/ | |||
#include "pipi.h" | |||
#include "pipi-internals.h" | |||
namespace lol | |||
{ | |||
pipi_image_t *pipi_crop(pipi_image_t *src, int w, int h, int dx, int dy) | |||
Image Image::Crop(ibox2 box) const | |||
{ | |||
float *srcdata, *dstdata; | |||
pipi_image_t *dst; | |||
pipi_pixels_t *srcp, *dstp; | |||
int y, off, len; | |||
ivec2 const srcsize = GetSize(); | |||
ivec2 const dstsize = box.B - box.A; | |||
srcp = pipi_get_pixels(src, PIPI_PIXELS_RGBA_F32); | |||
srcdata = (float *)srcp->pixels; | |||
Image dst(dstsize); | |||
PixelFormat format = GetFormat(); | |||
dst = pipi_new(w, h); | |||
dstp = pipi_get_pixels(dst, PIPI_PIXELS_RGBA_F32); | |||
dstdata = (float *)dstp->pixels; | |||
if (format != PixelFormat::Unknown) | |||
{ | |||
dst.SetFormat(format); | |||
uint8_t const *srcp = (uint8_t const *)m_data->m_pixels[(int)format]; | |||
uint8_t *dstp = (uint8_t *)dst.m_data->m_pixels[(int)format]; | |||
uint8_t bpp = BytesPerPixel(format); | |||
off = dx; | |||
len = w; | |||
int len = dstsize.x; | |||
if(dx < 0) | |||
{ | |||
len += dx; | |||
dx = 0; | |||
} | |||
if (box.A.x < 0) | |||
{ | |||
len += box.A.x; | |||
box.A.x = 0; | |||
} | |||
if(dx + len > srcp->w) | |||
len = srcp->w - dx; | |||
if (box.A.x + len > srcsize.x) | |||
len = srcsize.x - box.A.x; | |||
if(len > 0) | |||
{ | |||
for(y = 0; y < h; y++) | |||
if (len > 0) | |||
{ | |||
if(y + dy < 0 || y + dy >= srcp->h) | |||
continue; | |||
for (int y = 0; y < dstsize.y; y++) | |||
{ | |||
if (y + box.A.y < 0 || y + box.A.y >= srcsize.y) | |||
continue; | |||
memcpy(dstdata + y * w * 4, | |||
srcdata + ((y + dy) * srcp->w + dx) * 4, | |||
len * 4 * sizeof(float)); | |||
memcpy(dstp + y * dstsize.x * bpp, | |||
srcp + ((y + box.A.y) * srcsize.x + box.A.x) * bpp, | |||
len * bpp); | |||
} | |||
} | |||
} | |||
return dst; | |||
} | |||
} /* namespace lol */ | |||
@@ -1,93 +0,0 @@ | |||
/* | |||
* 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. | |||
*/ | |||
/* | |||
* dither.c: dithering functions | |||
*/ | |||
#include "config.h" | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include "pipi.h" | |||
#include "pipi-internals.h" | |||
/* FIXME: this is not the right place for this... see pixels.c instead */ | |||
void pipi_dither_24to16(pipi_image_t *img) | |||
{ | |||
int *error, *nexterror; | |||
pipi_pixels_t *p; | |||
uint32_t *p32; | |||
int x, y; | |||
error = malloc(sizeof(int) * 3 * (img->w + 2)); | |||
nexterror = malloc(sizeof(int) * 3 * (img->w + 2)); | |||
p = pipi_get_pixels(img, PIPI_PIXELS_RGBA_U8); | |||
p32 = (uint32_t *)p->pixels; | |||
memset(error, 0, sizeof(int) * 3 * (img->w + 2)); | |||
for(y = 0; y < img->h; y++) | |||
{ | |||
int er = 0, eg = 0, eb = 0; | |||
memset(nexterror, 0, sizeof(int) * 3 * (img->w + 2)); | |||
for(x = 0; x < img->w; x++) | |||
{ | |||
int r, g, b, a, r2, g2, b2; | |||
r = p32[y * img->w + x] & 0xff; | |||
g = (p32[y * img->w + x] >> 8) & 0xff; | |||
b = (p32[y * img->w + x] >> 16) & 0xff; | |||
a = (p32[y * img->w + x] >> 24) & 0xff; | |||
r += er + error[x * 3 + 3]; | |||
g += eg + error[x * 3 + 4]; | |||
b += eb + error[x * 3 + 5]; | |||
r2 = r / 8 * 8; g2 = g / 4 * 4; b2 = b / 8 * 8; | |||
if(r2 < 0) r2 = 0; if(r2 > 0xf8) r2 = 0xf8; | |||
if(g2 < 0) g2 = 0; if(g2 > 0xfc) g2 = 0xfc; | |||
if(b2 < 0) b2 = 0; if(b2 > 0xf8) b2 = 0xf8; | |||
/* hack */ | |||
if(r2 == 0x88 && g2 == 0x88 && b2 == 0x88) g2 = 0x84; | |||
/* hack */ | |||
p32[y * img->w + x] = (a << 24) | (b2 << 16) | (g2 << 8) | r2; | |||
er = r - (r2 / 8 * 255 / 31); | |||
eg = g - (g2 / 4 * 255 / 63); | |||
eb = b - (b2 / 8 * 255 / 31); | |||
nexterror[x * 3 + 0] += er * 3 / 8; | |||
nexterror[x * 3 + 1] += eg * 3 / 8; | |||
nexterror[x * 3 + 2] += eb * 3 / 8; | |||
nexterror[x * 3 + 3] += er * 5 / 8; | |||
nexterror[x * 3 + 4] += eg * 5 / 8; | |||
nexterror[x * 3 + 5] += eb * 5 / 8; | |||
nexterror[x * 3 + 6] += er * 1 / 8; | |||
nexterror[x * 3 + 7] += eg * 1 / 8; | |||
nexterror[x * 3 + 8] += eb * 1 / 8; | |||
er -= er * 3 / 8 + er * 7 / 8 + er * 1 / 8; | |||
eg -= eg * 3 / 8 + eg * 7 / 8 + eg * 1 / 8; | |||
eb -= eb * 3 / 8 + eb * 7 / 8 + eb * 1 / 8; | |||
} | |||
memcpy(error, nexterror, sizeof(int) * 3 * (img->w + 2)); | |||
} | |||
pipi_release_pixels(img, p); | |||
free(error); | |||
free(nexterror); | |||
} | |||
@@ -1,134 +1,157 @@ | |||
/* | |||
* 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" | |||
/* | |||
* median.c: median filter functions | |||
* Median filter functions | |||
*/ | |||
#include "config.h" | |||
#include <stdlib.h> | |||
#include <stdio.h> | |||
#include <string.h> | |||
#include <math.h> | |||
/* FIXME: this is in desperate want of optimisation. Here is what could | |||
* be done to improve the performance: | |||
* - prefetch the neighbourhood; most neighbours are the same as the | |||
* previous pixels. | |||
* - use a better sort algorithm; bubble sort is ridiculous | |||
* - even better, use state-of-the art median selection, ie. O(3n), for | |||
* most common combinations (9, 25, 49, 81). */ | |||
#include "pipi.h" | |||
#include "pipi-internals.h" | |||
namespace lol | |||
{ | |||
static int cmpdouble(void const *i1, void const *i2) | |||
static int cmpfloat(void const *i1, void const *i2) | |||
{ | |||
double a = *(double const *)i1; | |||
double b = *(double const *)i2; | |||
float a = *(float const *)i1; | |||
float b = *(float const *)i2; | |||
return (a > b) - (a < b); | |||
} | |||
pipi_image_t *pipi_median(pipi_image_t *src, int radius) | |||
Image Image::Median(ivec2 ksize) const | |||
{ | |||
return pipi_median_ext(src, radius, radius); | |||
} | |||
ivec2 const size = GetSize(); | |||
Image tmp = *this; | |||
Image ret(size); | |||
/* FIXME: this is in desperate want of optimisation. Here is what could | |||
* be done to improve the performance: | |||
* - prefetch the neighbourhood; most neighbours are the same as the | |||
* previous pixels. | |||
* - use a better sort algorithm; bubble sort is ridiculous | |||
* - even better, use state-of-the art median selection, ie. O(3n), for | |||
* most common combinations (9, 25, 49, 81). */ | |||
pipi_image_t *pipi_median_ext(pipi_image_t *src, int rx, int ry) | |||
{ | |||
pipi_image_t *dst; | |||
pipi_pixels_t *srcp, *dstp; | |||
float *srcdata, *dstdata; | |||
double *list; | |||
int x, y, w, h, i, j, size, gray; | |||
if (GetFormat() == PixelFormat::Y_8 || GetFormat() == PixelFormat::Y_F32) | |||
{ | |||
ivec2 const lsize = 2 * ksize + ivec2(1); | |||
Array2D<float> list(lsize); | |||
float *srcp = tmp.Lock<PixelFormat::Y_F32>(); | |||
float *dstp = ret.Lock<PixelFormat::Y_F32>(); | |||
w = src->w; | |||
h = src->h; | |||
size = (2 * rx + 1) * (2 * ry + 1); | |||
for (int y = 0; y < size.y; y++) | |||
{ | |||
for (int x = 0; x < size.x; x++) | |||
{ | |||
/* Make a list of neighbours */ | |||
for (int j = -ksize.y; j <= ksize.y; j++) | |||
{ | |||
int y2 = y + j; | |||
if (y2 < 0) y2 = size.y - 1 - ((-y2 - 1) % size.y); | |||
else if (y2 > 0) y2 = y2 % size.y; | |||
gray = (src->last_modified == PIPI_PIXELS_Y_F32); | |||
for (int i = -ksize.x; i <= ksize.x; i++) | |||
{ | |||
int x2 = x + i; | |||
if (x2 < 0) x2 = size.x - 1 - ((-x2 - 1) % size.x); | |||
else if (x2 > 0) x2 = x2 % size.x; | |||
srcp = gray ? pipi_get_pixels(src, PIPI_PIXELS_Y_F32) | |||
: pipi_get_pixels(src, PIPI_PIXELS_RGBA_F32); | |||
srcdata = (float *)srcp->pixels; | |||
list[i + ksize.x][j + ksize.y] = srcp[y2 * size.x + x2]; | |||
} | |||
} | |||
dst = pipi_new(w, h); | |||
dstp = gray ? pipi_get_pixels(dst, PIPI_PIXELS_Y_F32) | |||
: pipi_get_pixels(dst, PIPI_PIXELS_RGBA_F32); | |||
dstdata = (float *)dstp->pixels; | |||
/* Sort the list */ | |||
qsort(&list[0][0], lsize.x * lsize.y, sizeof(float), cmpfloat); | |||
list = malloc(size * (gray ? 1 : 4) * sizeof(double)); | |||
/* Store the median value */ | |||
dstp[y * size.x + x] = *(&list[0][0] + lsize.x * lsize.y / 2); | |||
} | |||
} | |||
for(y = 0; y < h; y++) | |||
tmp.Unlock(srcp); | |||
ret.Unlock(dstp); | |||
} | |||
else | |||
{ | |||
for(x = 0; x < w; x++) | |||
{ | |||
double *parser = list; | |||
ivec2 const lsize = 2 * ksize + ivec2(1); | |||
Array2D<vec3> list(lsize); | |||
/* Make a list of neighbours */ | |||
for(j = -ry; j <= ry; j++) | |||
{ | |||
int y2 = y + j; | |||
if(y2 < 0) y2 = h - 1 - ((-y2 - 1) % h); | |||
else if(y2 > 0) y2 = y2 % h; | |||
vec4 *srcp = tmp.Lock<PixelFormat::RGBA_F32>(); | |||
vec4 *dstp = ret.Lock<PixelFormat::RGBA_F32>(); | |||
for(i = -rx; i <= rx; i++) | |||
for (int y = 0; y < size.y; y++) | |||
{ | |||
for (int x = 0; x < size.x; x++) | |||
{ | |||
/* Make a list of neighbours */ | |||
for (int j = -ksize.y; j <= ksize.y; j++) | |||
{ | |||
int x2 = x + i; | |||
if(x2 < 0) x2 = w - 1 - ((-x2 - 1) % w); | |||
else if(x2 > 0) x2 = x2 % w; | |||
int y2 = y + j; | |||
if (y2 < 0) y2 = size.y - 1 - ((-y2 - 1) % size.y); | |||
else if (y2 > 0) y2 = y2 % size.y; | |||
if(gray) | |||
for (int i = -ksize.x; i <= ksize.x; i++) | |||
{ | |||
*parser++ = srcdata[y2 * w + x2]; | |||
int x2 = x + i; | |||
if (x2 < 0) x2 = size.x - 1 - ((-x2 - 1) % size.x); | |||
else if (x2 > 0) x2 = x2 % size.x; | |||
list[i + ksize.x][j + ksize.y] = srcp[y2 * size.x + x2].rgb; | |||
} | |||
else | |||
} | |||
/* Algorithm constants, empirically chosen */ | |||
int const N = 5; | |||
float const K = 1.5f; | |||
/* Iterate using Weiszfeld’s algorithm */ | |||
vec3 oldmed(0.f), median(0.f); | |||
for (int iter = 0; ; ++iter) | |||
{ | |||
oldmed = median; | |||
vec3 s1(0.f); | |||
float s2 = 0.f; | |||
for (int j = 0; j < lsize.y; ++j) | |||
for (int i = 0; i < lsize.x; ++i) | |||
{ | |||
float d = 1.0f / (1e-10f + distance(median, list[i][j])); | |||
s1 += list[i][j] * d; | |||
s2 += d; | |||
} | |||
median = s1 / s2; | |||
if (iter > 1 && iter < N) | |||
{ | |||
parser[0] = srcdata[4 * (y2 * w + x2)]; | |||
parser[size * 1] = srcdata[4 * (y2 * w + x2) + 1]; | |||
parser[size * 2] = srcdata[4 * (y2 * w + x2) + 2]; | |||
parser[size * 3] = srcdata[4 * (y2 * w + x2) + 3]; | |||
parser++; | |||
median += K * (median - oldmed); | |||
} | |||
} | |||
} | |||
/* Sort the list */ | |||
qsort(list, size, sizeof(double), cmpdouble); | |||
if(!gray) | |||
{ | |||
qsort(list + size, size, sizeof(double), cmpdouble); | |||
qsort(list + 2 * size, size, sizeof(double), cmpdouble); | |||
qsort(list + 3 * size, size, sizeof(double), cmpdouble); | |||
} | |||
if (iter > 3 && distance(oldmed, median) < 1.e-5f) | |||
break; | |||
} | |||
/* Store the median value */ | |||
if(gray) | |||
{ | |||
dstdata[y * w + x] = list[size / 2]; | |||
} | |||
else | |||
{ | |||
dstdata[4 * (y * w + x)] = list[size / 2]; | |||
dstdata[4 * (y * w + x) + 1] = list[size / 2 + size * 1]; | |||
dstdata[4 * (y * w + x) + 2] = list[size / 2 + size * 2]; | |||
dstdata[4 * (y * w + x) + 3] = list[size / 2 + size * 3]; | |||
/* Store the median value */ | |||
dstp[y * size.x + x] = vec4(median, srcp[y * size.x + x].a); | |||
} | |||
} | |||
tmp.Unlock(srcp); | |||
ret.Unlock(dstp); | |||
} | |||
return dst; | |||
return ret; | |||
} | |||
} /* namespace lol */ | |||
@@ -1,127 +1,51 @@ | |||
/* | |||
* 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" | |||
/* | |||
* yuv.c: YUV conversion functions | |||
* YUV conversion functions | |||
*/ | |||
#include "config.h" | |||
#include <stdlib.h> | |||
#include <stdio.h> | |||
#include <string.h> | |||
#include <math.h> | |||
#include "pipi.h" | |||
#include "pipi-internals.h" | |||
pipi_image_t *pipi_rgb2yuv(pipi_image_t *src) | |||
namespace lol | |||
{ | |||
pipi_image_t *dst; | |||
pipi_pixels_t *srcp, *dstp; | |||
float *srcdata, *dstdata; | |||
int x, y, w, h; | |||
w = src->w; | |||
h = src->h; | |||
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; | |||
for(y = 0; y < h; y++) | |||
{ | |||
for(x = 0; x < w; x++) | |||
{ | |||
double r, g, b, a, yp, u, v; | |||
int d = 4 * (y * w + x); | |||
r = srcdata[d]; | |||
g = srcdata[d + 1]; | |||
b = srcdata[d + 2]; | |||
a = srcdata[d + 3]; | |||
yp = 0.299 * r + 0.587 * g + 0.114 * b; | |||
yp = (yp * 220.0 + 16.0) / 255.0; | |||
u = 0.5 - 0.14713 * r - 0.28886 * g + 0.436 * b; | |||
if (u < 0.0) u = 0.0; | |||
if (u > 1.0) u = 1.0; | |||
v = 0.5 + 0.615 * r - 0.51499 * g - 0.10001 * b; | |||
if (v < 0.0) v = 0.0; | |||
if (v > 1.0) v = 1.0; | |||
Image Image::YUVToRGB() const | |||
{ | |||
Image ret = *this; | |||
int count = GetSize().x * GetSize().y; | |||
dstdata[d] = yp; | |||
dstdata[d + 1] = u; | |||
dstdata[d + 2] = v; | |||
dstdata[d + 3] = a; | |||
} | |||
} | |||
vec4 *pixels = ret.Lock<PixelFormat::RGBA_F32>(); | |||
for (int n = 0; n < count; ++n) | |||
pixels[n] = Color::YUVToRGB(pixels[n]); | |||
ret.Unlock(pixels); | |||
return dst; | |||
return ret; | |||
} | |||
pipi_image_t *pipi_yuv2rgb(pipi_image_t *src) | |||
Image Image::RGBToYUV() const | |||
{ | |||
pipi_image_t *dst; | |||
pipi_pixels_t *srcp, *dstp; | |||
float *srcdata, *dstdata; | |||
int x, y, w, h; | |||
w = src->w; | |||
h = src->h; | |||
srcp = pipi_get_pixels(src, PIPI_PIXELS_RGBA_F32); | |||
srcdata = (float *)srcp->pixels; | |||
Image ret = *this; | |||
int count = GetSize().x * GetSize().y; | |||
dst = pipi_new(w, h); | |||
dstp = pipi_get_pixels(dst, PIPI_PIXELS_RGBA_F32); | |||
dstdata = (float *)dstp->pixels; | |||
vec4 *pixels = ret.Lock<PixelFormat::RGBA_F32>(); | |||
for (int n = 0; n < count; ++n) | |||
pixels[n] = Color::RGBToYUV(pixels[n]); | |||
ret.Unlock(pixels); | |||
for(y = 0; y < h; y++) | |||
{ | |||
for(x = 0; x < w; x++) | |||
{ | |||
double r, g, b, a, yp, u, v; | |||
int d = 4 * (y * w + x); | |||
yp = (srcdata[d] * 255.0 - 16.0) / 220.0; | |||
u = srcdata[d + 1] - 0.5; | |||
v = srcdata[d + 2] - 0.5; | |||
a = srcdata[d + 3]; | |||
r = yp + 1.13983 * v; | |||
if (r < 0.0) r = 0.0; | |||
if (r > 1.0) r = 1.0; | |||
g = yp - 0.39465 * u - 0.58060 * v; | |||
if (g < 0.0) g = 0.0; | |||
if (g > 1.0) g = 1.0; | |||
b = yp + 2.03211 * u; | |||
if (b < 0.0) b = 0.0; | |||
if (b > 1.0) b = 1.0; | |||
dstdata[d] = r; | |||
dstdata[d + 1] = g; | |||
dstdata[d + 2] = b; | |||
dstdata[d + 3] = a; | |||
} | |||
} | |||
return dst; | |||
return ret; | |||
} | |||
} /* namespace lol */ | |||
@@ -65,6 +65,45 @@ public: | |||
return vec4(sRGBToLinearRGB(src.rgb), src.a); | |||
} | |||
/* | |||
* Convert linear RGB to YUV | |||
*/ | |||
static vec3 RGBToYUV(vec3 src) | |||
{ | |||
mat3 m(vec3(0.299f, -0.14713f, 0.615f), | |||
vec3(0.587f, -0.28886f, -0.51499f), | |||
vec3(0.114f, 0.436f, -0.10001f)); | |||
vec3 tmp = m * src; | |||
tmp.r = (tmp.r * 220.f + 16.f) / 255.f; | |||
tmp.g += 0.5f; | |||
tmp.b += 0.5f; | |||
return clamp(tmp, 0.f, 1.f); | |||
} | |||
static vec4 RGBToYUV(vec4 src) | |||
{ | |||
return vec4(RGBToYUV(src.rgb), src.a); | |||
} | |||
/* | |||
* Convert YUV to linear RGB | |||
*/ | |||
static vec3 YUVToRGB(vec3 src) | |||
{ | |||
src.r = (src.r * 255.f - 16.f) / 220.f; | |||
src.g -= 0.5f; | |||
src.b -= 0.5f; | |||
mat3 m(vec3(1.f, 1.f, 1.f), | |||
vec3(0.f, -0.39465f, 2.03211f), | |||
vec3(1.13983f, -0.58060f, 0.f)); | |||
return clamp(m * src, 0.f, 1.f); | |||
} | |||
static vec4 YUVToRGB(vec4 src) | |||
{ | |||
return vec4(YUVToRGB(src.rgb), src.a); | |||
} | |||
/* | |||
* Convert linear HSV to linear RGB | |||
*/ | |||
@@ -95,7 +95,9 @@ public: | |||
static Array2D<float> HalftoneKernel(ivec2 size); | |||
static Array2D<float> EdiffKernel(EdiffAlgorithm algorithm); | |||
static Array2D<float> NormalizeKernel(Array2D<float> const &kernel); | |||
static Array2D<float> GaussianKernel(vec2 radius, float angle, vec2 delta); | |||
static Array2D<float> GaussianKernel(vec2 radius, | |||
float angle = 0.f, | |||
vec2 delta = vec2(0.f, 0.f)); | |||
/* Rendering */ | |||
bool Stock(char const *desc); | |||
@@ -104,6 +106,10 @@ public: | |||
/* Image processing */ | |||
Image AutoContrast() const; | |||
Image Convolution(Array2D<float> const &kernel); | |||
Image Crop(ibox2 box) const; | |||
Image Median(ivec2 radii) const; | |||
Image YUVToRGB() const; | |||
Image RGBToYUV() const; | |||
Image DitherRandom() const; | |||
Image DitherEdiff(Array2D<float> const &kernel, | |||
@@ -151,10 +151,13 @@ | |||
<ClCompile Include="image\color\color.cpp" /> | |||
<ClCompile Include="image\filter\autocontrast.cpp" /> | |||
<ClCompile Include="image\filter\convolution.cpp" /> | |||
<ClCompile Include="image\filter\median.cpp" /> | |||
<ClCompile Include="image\filter\yuv.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\crop.cpp" /> | |||
<ClCompile Include="image\image.cpp" /> | |||
<ClCompile Include="image\pixels.cpp" /> | |||
<ClCompile Include="image\render\noise.cpp" /> | |||
@@ -88,11 +88,17 @@ | |||
<Filter Include="image\dither"> | |||
<UniqueIdentifier>{63e63eea-c96e-4d37-81f6-f3f17e18b751}</UniqueIdentifier> | |||
</Filter> | |||
<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"> | |||
<Filter>image</Filter> | |||
</ClCompile> | |||
<ClCompile Include="image\image.cpp"> | |||
<Filter>image</Filter> | |||
</ClCompile> | |||
@@ -355,6 +361,18 @@ | |||
<ClCompile Include="sys\thread.cpp"> | |||
<Filter>sys</Filter> | |||
</ClCompile> | |||
<ClCompile Include="image\filter\autocontrast.cpp"> | |||
<Filter>image\filter</Filter> | |||
</ClCompile> | |||
<ClCompile Include="image\filter\convolution.cpp"> | |||
<Filter>image\filter</Filter> | |||
</ClCompile> | |||
<ClCompile Include="image\filter\median.cpp"> | |||
<Filter>image\filter</Filter> | |||
</ClCompile> | |||
<ClCompile Include="image\filter\yuv.cpp"> | |||
<Filter>image\filter</Filter> | |||
</ClCompile> | |||
<ClCompile Include="image\dither\ediff.cpp"> | |||
<Filter>image\dither</Filter> | |||
</ClCompile> | |||