From 6b9f3b66c12b342fdf86001189e9f14178451882 Mon Sep 17 00:00:00 2001 From: Sam Hocevar Date: Sat, 21 Jun 2014 20:05:18 +0000 Subject: [PATCH] image: add YUV filter, median filter, and image cropping. --- TODO | 4 - src/Makefile.am | 2 + src/image/crop.cpp | 97 ++++++++-------- src/image/dither.cpp | 93 --------------- src/image/filter/median.cpp | 219 ++++++++++++++++++++---------------- src/image/filter/yuv.cpp | 148 ++++++------------------ src/lol/image/color.h | 39 +++++++ src/lol/image/image.h | 8 +- src/lolcore.vcxproj | 3 + src/lolcore.vcxproj.filters | 18 +++ 10 files changed, 275 insertions(+), 356 deletions(-) delete mode 100644 src/image/dither.cpp diff --git a/TODO b/TODO index 55b77af1..75a0b8b4 100644 --- a/TODO +++ b/TODO @@ -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 diff --git a/src/Makefile.am b/src/Makefile.am index d20a508a..5b9fbe51 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 \ diff --git a/src/image/crop.cpp b/src/image/crop.cpp index 3306c4b4..4334102f 100644 --- a/src/image/crop.cpp +++ b/src/image/crop.cpp @@ -1,68 +1,69 @@ -/* - * libpipi Pathetic image processing interface library - * Copyright (c) 2004-2009 Sam Hocevar - * 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 +// 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 -#include +/* + * 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 */ + diff --git a/src/image/dither.cpp b/src/image/dither.cpp deleted file mode 100644 index 2199a685..00000000 --- a/src/image/dither.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* - * libpipi Pathetic image processing interface library - * Copyright (c) 2004-2008 Sam Hocevar - * 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 -#include -#include - -#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); -} - diff --git a/src/image/filter/median.cpp b/src/image/filter/median.cpp index f11dfcb1..3c1d4c26 100644 --- a/src/image/filter/median.cpp +++ b/src/image/filter/median.cpp @@ -1,134 +1,157 @@ -/* - * libpipi Pathetic image processing interface library - * Copyright (c) 2004-2008 Sam Hocevar - * 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 +// 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 -#include -#include -#include +/* 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 list(lsize); + + float *srcp = tmp.Lock(); + float *dstp = ret.Lock(); - 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 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(); + vec4 *dstp = ret.Lock(); - 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 */ + diff --git a/src/image/filter/yuv.cpp b/src/image/filter/yuv.cpp index a60056fd..812bff03 100644 --- a/src/image/filter/yuv.cpp +++ b/src/image/filter/yuv.cpp @@ -1,127 +1,51 @@ -/* - * libpipi Pathetic image processing interface library - * Copyright (c) 2004-2008 Sam Hocevar - * 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 +// 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 -#include -#include -#include - -#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(); + 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(); + 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 */ + diff --git a/src/lol/image/color.h b/src/lol/image/color.h index 59742890..e7780078 100644 --- a/src/lol/image/color.h +++ b/src/lol/image/color.h @@ -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 */ diff --git a/src/lol/image/image.h b/src/lol/image/image.h index 75dcdb1d..9f7253e8 100644 --- a/src/lol/image/image.h +++ b/src/lol/image/image.h @@ -95,7 +95,9 @@ public: static Array2D HalftoneKernel(ivec2 size); static Array2D EdiffKernel(EdiffAlgorithm algorithm); static Array2D NormalizeKernel(Array2D const &kernel); - static Array2D GaussianKernel(vec2 radius, float angle, vec2 delta); + static Array2D 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 const &kernel); + Image Crop(ibox2 box) const; + Image Median(ivec2 radii) const; + Image YUVToRGB() const; + Image RGBToYUV() const; Image DitherRandom() const; Image DitherEdiff(Array2D const &kernel, diff --git a/src/lolcore.vcxproj b/src/lolcore.vcxproj index 05c4dc00..2376f5d2 100644 --- a/src/lolcore.vcxproj +++ b/src/lolcore.vcxproj @@ -151,10 +151,13 @@ + + + diff --git a/src/lolcore.vcxproj.filters b/src/lolcore.vcxproj.filters index 3977e36f..f1607321 100644 --- a/src/lolcore.vcxproj.filters +++ b/src/lolcore.vcxproj.filters @@ -88,11 +88,17 @@ {63e63eea-c96e-4d37-81f6-f3f17e18b751} + + {3f420a7d-0538-463a-925b-3f8968bf628e} + {23655fca-56e5-48ec-8cf0-a71322f4cc89} + + image + image @@ -355,6 +361,18 @@ sys + + image\filter + + + image\filter + + + image\filter + + + image\filter + image\dither