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