@@ -32,18 +32,14 @@ Image: | |||||
· combine/rgb.cpp | · combine/rgb.cpp | ||||
· combine/subadd.cpp | · combine/subadd.cpp | ||||
· context.cpp | · context.cpp | ||||
· crop.cpp | |||||
· dither.cpp | |||||
· dither/dbs.cpp | · dither/dbs.cpp | ||||
· filter/blur.cpp | · filter/blur.cpp | ||||
· filter/color.cpp | · filter/color.cpp | ||||
· filter/dilate.cpp | · filter/dilate.cpp | ||||
· filter/median.cpp | |||||
· filter/rotate.cpp | · filter/rotate.cpp | ||||
· filter/sharpen.cpp | · filter/sharpen.cpp | ||||
· filter/transform.cpp | · filter/transform.cpp | ||||
· filter/wave.cpp | · filter/wave.cpp | ||||
· filter/yuv.cpp | |||||
· paint/bezier.cpp | · paint/bezier.cpp | ||||
· paint/floodfill.cpp | · paint/floodfill.cpp | ||||
· paint/line.cpp | · paint/line.cpp | ||||
@@ -111,6 +111,7 @@ liblolcore_sources = \ | |||||
sys/thread.cpp sys/threadbase.h \ | sys/thread.cpp sys/threadbase.h \ | ||||
\ | \ | ||||
image/image.cpp image/image-private.h image/stock.cpp image/pixels.cpp \ | 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/gdiplus-image.cpp image/codec/imlib2-image.cpp \ | ||||
image/codec/sdl-image.cpp image/codec/ios-image.cpp \ | image/codec/sdl-image.cpp image/codec/ios-image.cpp \ | ||||
image/codec/zed-image.cpp image/codec/zed-palette-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/random.cpp image/dither/ediff.cpp \ | ||||
image/dither/ostromoukhov.cpp image/dither/ordered.cpp \ | image/dither/ostromoukhov.cpp image/dither/ordered.cpp \ | ||||
image/filter/autocontrast.cpp image/filter/convolution.cpp \ | image/filter/autocontrast.cpp image/filter/convolution.cpp \ | ||||
image/filter/median.cpp image/filter/yuv.cpp \ | |||||
image/render/noise.cpp \ | image/render/noise.cpp \ | ||||
\ | \ | ||||
loldebug.h \ | 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; | 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); | 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); | 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 | * Convert linear HSV to linear RGB | ||||
*/ | */ | ||||
@@ -95,7 +95,9 @@ public: | |||||
static Array2D<float> HalftoneKernel(ivec2 size); | static Array2D<float> HalftoneKernel(ivec2 size); | ||||
static Array2D<float> EdiffKernel(EdiffAlgorithm algorithm); | static Array2D<float> EdiffKernel(EdiffAlgorithm algorithm); | ||||
static Array2D<float> NormalizeKernel(Array2D<float> const &kernel); | 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 */ | /* Rendering */ | ||||
bool Stock(char const *desc); | bool Stock(char const *desc); | ||||
@@ -104,6 +106,10 @@ public: | |||||
/* Image processing */ | /* Image processing */ | ||||
Image AutoContrast() const; | Image AutoContrast() const; | ||||
Image Convolution(Array2D<float> const &kernel); | 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 DitherRandom() const; | ||||
Image DitherEdiff(Array2D<float> const &kernel, | Image DitherEdiff(Array2D<float> const &kernel, | ||||
@@ -151,10 +151,13 @@ | |||||
<ClCompile Include="image\color\color.cpp" /> | <ClCompile Include="image\color\color.cpp" /> | ||||
<ClCompile Include="image\filter\autocontrast.cpp" /> | <ClCompile Include="image\filter\autocontrast.cpp" /> | ||||
<ClCompile Include="image\filter\convolution.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\ediff.cpp" /> | ||||
<ClCompile Include="image\dither\ordered.cpp" /> | <ClCompile Include="image\dither\ordered.cpp" /> | ||||
<ClCompile Include="image\dither\ostromoukhov.cpp" /> | <ClCompile Include="image\dither\ostromoukhov.cpp" /> | ||||
<ClCompile Include="image\dither\random.cpp" /> | <ClCompile Include="image\dither\random.cpp" /> | ||||
<ClCompile Include="image\crop.cpp" /> | |||||
<ClCompile Include="image\image.cpp" /> | <ClCompile Include="image\image.cpp" /> | ||||
<ClCompile Include="image\pixels.cpp" /> | <ClCompile Include="image\pixels.cpp" /> | ||||
<ClCompile Include="image\render\noise.cpp" /> | <ClCompile Include="image\render\noise.cpp" /> | ||||
@@ -88,11 +88,17 @@ | |||||
<Filter Include="image\dither"> | <Filter Include="image\dither"> | ||||
<UniqueIdentifier>{63e63eea-c96e-4d37-81f6-f3f17e18b751}</UniqueIdentifier> | <UniqueIdentifier>{63e63eea-c96e-4d37-81f6-f3f17e18b751}</UniqueIdentifier> | ||||
</Filter> | </Filter> | ||||
<Filter Include="image\filter"> | |||||
<UniqueIdentifier>{3f420a7d-0538-463a-925b-3f8968bf628e}</UniqueIdentifier> | |||||
</Filter> | |||||
<Filter Include="image\render"> | <Filter Include="image\render"> | ||||
<UniqueIdentifier>{23655fca-56e5-48ec-8cf0-a71322f4cc89}</UniqueIdentifier> | <UniqueIdentifier>{23655fca-56e5-48ec-8cf0-a71322f4cc89}</UniqueIdentifier> | ||||
</Filter> | </Filter> | ||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<ClCompile Include="image\crop.cpp"> | |||||
<Filter>image</Filter> | |||||
</ClCompile> | |||||
<ClCompile Include="image\image.cpp"> | <ClCompile Include="image\image.cpp"> | ||||
<Filter>image</Filter> | <Filter>image</Filter> | ||||
</ClCompile> | </ClCompile> | ||||
@@ -355,6 +361,18 @@ | |||||
<ClCompile Include="sys\thread.cpp"> | <ClCompile Include="sys\thread.cpp"> | ||||
<Filter>sys</Filter> | <Filter>sys</Filter> | ||||
</ClCompile> | </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"> | <ClCompile Include="image\dither\ediff.cpp"> | ||||
<Filter>image\dither</Filter> | <Filter>image\dither</Filter> | ||||
</ClCompile> | </ClCompile> | ||||