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