summaryrefslogtreecommitdiff
path: root/Build/source/libs/gd/libgd-src/src/gd_filter.c
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/libs/gd/libgd-src/src/gd_filter.c')
-rw-r--r--Build/source/libs/gd/libgd-src/src/gd_filter.c863
1 files changed, 863 insertions, 0 deletions
diff --git a/Build/source/libs/gd/libgd-src/src/gd_filter.c b/Build/source/libs/gd/libgd-src/src/gd_filter.c
new file mode 100644
index 00000000000..a3119f77f80
--- /dev/null
+++ b/Build/source/libs/gd/libgd-src/src/gd_filter.c
@@ -0,0 +1,863 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gd.h"
+#include "gdhelpers.h"
+#include "gd_intern.h"
+
+#ifdef _WIN32
+# include <windows.h>
+#else
+# include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <time.h>
+#include <math.h>
+
+#define NDEBUG /* Uncomment to enable assertions. */
+#include <assert.h>
+
+typedef int (BGD_STDCALL *FuncPtr)(gdImagePtr, int, int);
+
+#define GET_PIXEL_FUNCTION(src)(src->trueColor?gdImageGetTrueColorPixel:gdImageGetPixel)
+#define MIN(a,b) ((a)<(b)?(a):(b))
+#define MAX(a,b) ((a)<(b)?(b):(a))
+
+#ifdef _WIN32
+# define GD_SCATTER_SEED() (unsigned int)(time(0) * GetCurrentProcessId())
+#else
+# define GD_SCATTER_SEED() (unsigned int)(time(0) * getpid())
+#endif
+
+BGD_DECLARE(int) gdImageScatter(gdImagePtr im, int sub, int plus)
+{
+ gdScatter s;
+
+ s.sub = sub;
+ s.plus = plus;
+ s.num_colors = 0;
+ s.seed = GD_SCATTER_SEED();
+ return gdImageScatterEx(im, &s);
+}
+
+BGD_DECLARE(int) gdImageScatterColor(gdImagePtr im, int sub, int plus, int colors[], unsigned int num_colors)
+{
+ gdScatter s;
+
+ s.sub = sub;
+ s.plus = plus;
+ s.colors = colors;
+ s.num_colors = num_colors;
+ s.seed = GD_SCATTER_SEED();
+ return gdImageScatterEx(im, &s);
+}
+
+BGD_DECLARE(int) gdImageScatterEx(gdImagePtr im, gdScatterPtr scatter)
+{
+ register int x, y;
+ int dest_x, dest_y;
+ int pxl, new_pxl;
+ unsigned int n;
+ int sub = scatter->sub, plus = scatter->plus;
+
+ if (plus == 0 && sub == 0) {
+ return 1;
+ } else if (sub >= plus) {
+ return 0;
+ }
+
+ (void)srand(scatter->seed);
+
+ if (scatter->num_colors) {
+ for (y = 0; y < im->sy; y++) {
+ for (x = 0; x < im->sx; x++) {
+ dest_x = (int) (x + ((rand() % (plus - sub)) + sub));
+ dest_y = (int) (y + ((rand() % (plus - sub)) + sub));
+
+ if (!gdImageBoundsSafe(im, dest_x, dest_y)) {
+ continue;
+ }
+
+ pxl = gdImageGetPixel(im, x, y);
+ new_pxl = gdImageGetPixel(im, dest_x, dest_y);
+
+ for (n = 0; n < scatter->num_colors; n++) {
+ if (pxl == scatter->colors[n]) {
+ gdImageSetPixel(im, dest_x, dest_y, pxl);
+ gdImageSetPixel(im, x, y, new_pxl);
+ }
+ }
+ }
+ }
+ } else {
+ for (y = 0; y < im->sy; y++) {
+ for (x = 0; x < im->sx; x++) {
+ dest_x = (int) (x + ((rand() % (plus - sub)) + sub));
+ dest_y = (int) (y + ((rand() % (plus - sub)) + sub));
+
+ if (!gdImageBoundsSafe(im, dest_x, dest_y)) {
+ continue;
+ }
+
+ pxl = gdImageGetPixel(im, x, y);
+ new_pxl = gdImageGetPixel(im, dest_x, dest_y);
+
+ gdImageSetPixel(im, dest_x, dest_y, pxl);
+ gdImageSetPixel(im, x, y, new_pxl);
+ }
+ }
+ }
+
+ return 1;
+}
+
+BGD_DECLARE(int) gdImagePixelate(gdImagePtr im, int block_size, const unsigned int mode)
+{
+ int x, y;
+
+ if (block_size <= 0) {
+ return 0;
+ } else if (block_size == 1) {
+ return 1;
+ }
+ switch (mode) {
+ case GD_PIXELATE_UPPERLEFT:
+ for (y = 0; y < im->sy; y += block_size) {
+ for (x = 0; x < im->sx; x += block_size) {
+ if (gdImageBoundsSafe(im, x, y)) {
+ int c = gdImageGetPixel(im, x, y);
+ gdImageFilledRectangle(im, x, y, x + block_size - 1, y + block_size - 1, c);
+ }
+ }
+ }
+ break;
+ case GD_PIXELATE_AVERAGE:
+ for (y = 0; y < im->sy; y += block_size) {
+ for (x = 0; x < im->sx; x += block_size) {
+ int a, r, g, b, c;
+ int total;
+ int cx, cy;
+
+ a = r = g = b = c = total = 0;
+ /* sampling */
+ for (cy = 0; cy < block_size; cy++) {
+ for (cx = 0; cx < block_size; cx++) {
+ if (!gdImageBoundsSafe(im, x + cx, y + cy)) {
+ continue;
+ }
+ c = gdImageGetPixel(im, x + cx, y + cy);
+ a += gdImageAlpha(im, c);
+ r += gdImageRed(im, c);
+ g += gdImageGreen(im, c);
+ b += gdImageBlue(im, c);
+ total++;
+ }
+ }
+ /* drawing */
+ if (total > 0) {
+ c = gdImageColorResolveAlpha(im, r / total, g / total, b / total, a / total);
+ gdImageFilledRectangle(im, x, y, x + block_size - 1, y + block_size - 1, c);
+ }
+ }
+ }
+ break;
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+/* invert src image */
+BGD_DECLARE(int) gdImageNegate(gdImagePtr src)
+{
+ int x, y;
+ int r,g,b,a;
+ int new_pxl, pxl;
+ FuncPtr f;
+
+ if (src==NULL) {
+ return 0;
+ }
+
+ f = GET_PIXEL_FUNCTION(src);
+
+ for (y=0; y<src->sy; ++y) {
+ for (x=0; x<src->sx; ++x) {
+ pxl = f (src, x, y);
+ r = gdImageRed(src, pxl);
+ g = gdImageGreen(src, pxl);
+ b = gdImageBlue(src, pxl);
+ a = gdImageAlpha(src, pxl);
+
+ new_pxl = gdImageColorAllocateAlpha(src, 255-r, 255-g, 255-b, a);
+ if (new_pxl == -1) {
+ new_pxl = gdImageColorClosestAlpha(src, 255-r, 255-g, 255-b, a);
+ }
+ gdImageSetPixel (src, x, y, new_pxl);
+ }
+ }
+ return 1;
+}
+
+/* Convert the image src to a grayscale image */
+BGD_DECLARE(int) gdImageGrayScale(gdImagePtr src)
+{
+ int x, y;
+ int r,g,b,a;
+ int new_pxl, pxl;
+ FuncPtr f;
+
+ if (src==NULL) {
+ return 0;
+ }
+
+ f = GET_PIXEL_FUNCTION(src);
+
+ for (y=0; y<src->sy; ++y) {
+ for (x=0; x<src->sx; ++x) {
+ pxl = f (src, x, y);
+ r = gdImageRed(src, pxl);
+ g = gdImageGreen(src, pxl);
+ b = gdImageBlue(src, pxl);
+ a = gdImageAlpha(src, pxl);
+ r = g = b = (int) (.299 * r + .587 * g + .114 * b);
+
+ new_pxl = gdImageColorAllocateAlpha(src, r, g, b, a);
+ if (new_pxl == -1) {
+ new_pxl = gdImageColorClosestAlpha(src, r, g, b, a);
+ }
+ gdImageSetPixel (src, x, y, new_pxl);
+ }
+ }
+ return 1;
+}
+
+/* Set the brightness level <level> for the image src */
+BGD_DECLARE(int) gdImageBrightness(gdImagePtr src, int brightness)
+{
+ int x, y;
+ int r,g,b,a;
+ int new_pxl, pxl;
+ FuncPtr f;
+
+ if (src==NULL || (brightness < -255 || brightness > 255)) {
+ return 0;
+ }
+
+ if (brightness==0) {
+ return 1;
+ }
+
+ f = GET_PIXEL_FUNCTION(src);
+
+ for (y=0; y<src->sy; ++y) {
+ for (x=0; x<src->sx; ++x) {
+ pxl = f (src, x, y);
+
+ r = gdImageRed(src, pxl);
+ g = gdImageGreen(src, pxl);
+ b = gdImageBlue(src, pxl);
+ a = gdImageAlpha(src, pxl);
+
+ r = r + brightness;
+ g = g + brightness;
+ b = b + brightness;
+
+ r = (r > 255)? 255 : ((r < 0)? 0:r);
+ g = (g > 255)? 255 : ((g < 0)? 0:g);
+ b = (b > 255)? 255 : ((b < 0)? 0:b);
+
+ new_pxl = gdImageColorAllocateAlpha(src, (int)r, (int)g, (int)b, a);
+ if (new_pxl == -1) {
+ new_pxl = gdImageColorClosestAlpha(src, (int)r, (int)g, (int)b, a);
+ }
+ gdImageSetPixel (src, x, y, new_pxl);
+ }
+ }
+ return 1;
+}
+
+
+BGD_DECLARE(int) gdImageContrast(gdImagePtr src, double contrast)
+{
+ int x, y;
+ int r,g,b,a;
+ double rf,gf,bf;
+ int new_pxl, pxl;
+
+ FuncPtr f;
+
+ if (src==NULL) {
+ return 0;
+ }
+
+ f = GET_PIXEL_FUNCTION(src);
+
+ contrast = (double)(100.0-contrast)/100.0;
+ contrast = contrast*contrast;
+
+ for (y=0; y<src->sy; ++y) {
+ for (x=0; x<src->sx; ++x) {
+ pxl = f(src, x, y);
+
+ r = gdImageRed(src, pxl);
+ g = gdImageGreen(src, pxl);
+ b = gdImageBlue(src, pxl);
+ a = gdImageAlpha(src, pxl);
+
+ rf = (double)r/255.0;
+ rf = rf-0.5;
+ rf = rf*contrast;
+ rf = rf+0.5;
+ rf = rf*255.0;
+
+ bf = (double)b/255.0;
+ bf = bf-0.5;
+ bf = bf*contrast;
+ bf = bf+0.5;
+ bf = bf*255.0;
+
+ gf = (double)g/255.0;
+ gf = gf-0.5;
+ gf = gf*contrast;
+ gf = gf+0.5;
+ gf = gf*255.0;
+
+ rf = (rf > 255.0)? 255.0 : ((rf < 0.0)? 0.0:rf);
+ gf = (gf > 255.0)? 255.0 : ((gf < 0.0)? 0.0:gf);
+ bf = (bf > 255.0)? 255.0 : ((bf < 0.0)? 0.0:bf);
+
+ new_pxl = gdImageColorAllocateAlpha(src, (int)rf, (int)gf, (int)bf, a);
+ if (new_pxl == -1) {
+ new_pxl = gdImageColorClosestAlpha(src, (int)rf, (int)gf, (int)bf, a);
+ }
+ gdImageSetPixel (src, x, y, new_pxl);
+ }
+ }
+ return 1;
+}
+
+
+BGD_DECLARE(int) gdImageColor(gdImagePtr src, const int red, const int green, const int blue, const int alpha)
+{
+ int x, y;
+ int new_pxl, pxl;
+ FuncPtr f;
+
+ if (src == NULL) {
+ return 0;
+ }
+
+ f = GET_PIXEL_FUNCTION(src);
+
+ for (y=0; y<src->sy; ++y) {
+ for (x=0; x<src->sx; ++x) {
+ int r,g,b,a;
+
+ pxl = f(src, x, y);
+ r = gdImageRed(src, pxl);
+ g = gdImageGreen(src, pxl);
+ b = gdImageBlue(src, pxl);
+ a = gdImageAlpha(src, pxl);
+
+ r = r + red;
+ g = g + green;
+ b = b + blue;
+ a = a + alpha;
+
+ r = (r > 255)? 255 : ((r < 0)? 0 : r);
+ g = (g > 255)? 255 : ((g < 0)? 0 : g);
+ b = (b > 255)? 255 : ((b < 0)? 0 : b);
+ a = (a > 127)? 127 : ((a < 0)? 0 : a);
+
+ new_pxl = gdImageColorAllocateAlpha(src, r, g, b, a);
+ if (new_pxl == -1) {
+ new_pxl = gdImageColorClosestAlpha(src, r, g, b, a);
+ }
+ gdImageSetPixel (src, x, y, new_pxl);
+ }
+ }
+ return 1;
+}
+
+BGD_DECLARE(int) gdImageConvolution(gdImagePtr src, float filter[3][3], float filter_div, float offset)
+{
+ int x, y, i, j, new_a;
+ float new_r, new_g, new_b;
+ int new_pxl, pxl=0;
+ gdImagePtr srcback;
+ FuncPtr f;
+
+ if (src==NULL) {
+ return 0;
+ }
+
+ /* We need the orinal image with each safe neoghb. pixel */
+ srcback = gdImageCreateTrueColor (src->sx, src->sy);
+ if (srcback==NULL) {
+ return 0;
+ }
+
+ gdImageSaveAlpha(srcback, 1);
+ new_pxl = gdImageColorAllocateAlpha(srcback, 0, 0, 0, 127);
+ gdImageFill(srcback, 0, 0, new_pxl);
+
+ gdImageCopy(srcback, src,0,0,0,0,src->sx,src->sy);
+
+ f = GET_PIXEL_FUNCTION(src);
+
+ for ( y=0; y<src->sy; y++) {
+ for(x=0; x<src->sx; x++) {
+ new_r = new_g = new_b = 0;
+ new_a = gdImageAlpha(srcback, pxl);
+
+ for (j=0; j<3; j++) {
+ int yv = MIN(MAX(y - 1 + j, 0), src->sy - 1);
+ for (i=0; i<3; i++) {
+ pxl = f(srcback, MIN(MAX(x - 1 + i, 0), src->sx - 1), yv);
+ new_r += (float)gdImageRed(srcback, pxl) * filter[j][i];
+ new_g += (float)gdImageGreen(srcback, pxl) * filter[j][i];
+ new_b += (float)gdImageBlue(srcback, pxl) * filter[j][i];
+ }
+ }
+
+ new_r = (new_r/filter_div)+offset;
+ new_g = (new_g/filter_div)+offset;
+ new_b = (new_b/filter_div)+offset;
+
+ new_r = (new_r > 255.0f)? 255.0f : ((new_r < 0.0f)? 0.0f:new_r);
+ new_g = (new_g > 255.0f)? 255.0f : ((new_g < 0.0f)? 0.0f:new_g);
+ new_b = (new_b > 255.0f)? 255.0f : ((new_b < 0.0f)? 0.0f:new_b);
+
+ new_pxl = gdImageColorAllocateAlpha(src, (int)new_r, (int)new_g, (int)new_b, new_a);
+ if (new_pxl == -1) {
+ new_pxl = gdImageColorClosestAlpha(src, (int)new_r, (int)new_g, (int)new_b, new_a);
+ }
+ gdImageSetPixel (src, x, y, new_pxl);
+ }
+ }
+ gdImageDestroy(srcback);
+ return 1;
+}
+
+BGD_DECLARE(int) gdImageSelectiveBlur( gdImagePtr src)
+{
+ int x, y, i, j;
+ float new_r, new_g, new_b;
+ int new_pxl, cpxl, pxl, new_a=0;
+ float flt_r [3][3];
+ float flt_g [3][3];
+ float flt_b [3][3];
+ float flt_r_sum, flt_g_sum, flt_b_sum;
+
+ gdImagePtr srcback;
+ FuncPtr f;
+
+ if (src==NULL) {
+ return 0;
+ }
+
+ /* We need the orinal image with each safe neoghb. pixel */
+ srcback = gdImageCreateTrueColor (src->sx, src->sy);
+ if (srcback==NULL) {
+ return 0;
+ }
+ gdImageCopy(srcback, src,0,0,0,0,src->sx,src->sy);
+
+ f = GET_PIXEL_FUNCTION(src);
+
+ for(y = 0; y<src->sy; y++) {
+ for (x=0; x<src->sx; x++) {
+ flt_r_sum = flt_g_sum = flt_b_sum = 0.0;
+ cpxl = f(src, x, y);
+
+ for (j=0; j<3; j++) {
+ for (i=0; i<3; i++) {
+ if ((j == 1) && (i == 1)) {
+ flt_r[1][1] = flt_g[1][1] = flt_b[1][1] = 0.5;
+ } else {
+ pxl = f(src, x-(3>>1)+i, y-(3>>1)+j);
+ new_a = gdImageAlpha(srcback, pxl);
+
+ new_r = ((float)gdImageRed(srcback, cpxl)) - ((float)gdImageRed (srcback, pxl));
+
+ if (new_r < 0.0f) {
+ new_r = -new_r;
+ }
+ if (new_r != 0) {
+ flt_r[j][i] = 1.0f/new_r;
+ } else {
+ flt_r[j][i] = 1.0f;
+ }
+
+ new_g = ((float)gdImageGreen(srcback, cpxl)) - ((float)gdImageGreen(srcback, pxl));
+
+ if (new_g < 0.0f) {
+ new_g = -new_g;
+ }
+ if (new_g != 0) {
+ flt_g[j][i] = 1.0f/new_g;
+ } else {
+ flt_g[j][i] = 1.0f;
+ }
+
+ new_b = ((float)gdImageBlue(srcback, cpxl)) - ((float)gdImageBlue(srcback, pxl));
+
+ if (new_b < 0.0f) {
+ new_b = -new_b;
+ }
+ if (new_b != 0) {
+ flt_b[j][i] = 1.0f/new_b;
+ } else {
+ flt_b[j][i] = 1.0f;
+ }
+ }
+
+ flt_r_sum += flt_r[j][i];
+ flt_g_sum += flt_g[j][i];
+ flt_b_sum += flt_b [j][i];
+ }
+ }
+
+ for (j=0; j<3; j++) {
+ for (i=0; i<3; i++) {
+ if (flt_r_sum != 0.0) {
+ flt_r[j][i] /= flt_r_sum;
+ }
+ if (flt_g_sum != 0.0) {
+ flt_g[j][i] /= flt_g_sum;
+ }
+ if (flt_b_sum != 0.0) {
+ flt_b [j][i] /= flt_b_sum;
+ }
+ }
+ }
+
+ new_r = new_g = new_b = 0.0;
+
+ for (j=0; j<3; j++) {
+ for (i=0; i<3; i++) {
+ pxl = f(src, x-(3>>1)+i, y-(3>>1)+j);
+ new_r += (float)gdImageRed(srcback, pxl) * flt_r[j][i];
+ new_g += (float)gdImageGreen(srcback, pxl) * flt_g[j][i];
+ new_b += (float)gdImageBlue(srcback, pxl) * flt_b[j][i];
+ }
+ }
+
+ new_r = (new_r > 255.0f)? 255.0f : ((new_r < 0.0f)? 0.0f:new_r);
+ new_g = (new_g > 255.0f)? 255.0f : ((new_g < 0.0f)? 0.0f:new_g);
+ new_b = (new_b > 255.0f)? 255.0f : ((new_b < 0.0f)? 0.0f:new_b);
+ new_pxl = gdImageColorAllocateAlpha(src, (int)new_r, (int)new_g, (int)new_b, new_a);
+ if (new_pxl == -1) {
+ new_pxl = gdImageColorClosestAlpha(src, (int)new_r, (int)new_g, (int)new_b, new_a);
+ }
+ gdImageSetPixel (src, x, y, new_pxl);
+ }
+ }
+ gdImageDestroy(srcback);
+ return 1;
+}
+
+BGD_DECLARE(int) gdImageEdgeDetectQuick(gdImagePtr src)
+{
+ float filter[3][3] = {{-1.0,0.0,-1.0},
+ {0.0,4.0,0.0},
+ {-1.0,0.0,-1.0}};
+
+ return gdImageConvolution(src, filter, 1, 127);
+}
+
+/*
+ Function: gdImageGaussianBlur
+
+ <gdImageGaussianBlur> performs a Gaussian blur of radius 1 on the
+ image. The image is modified in place.
+
+ *NOTE:* You will almost certain want to use
+ <gdImageCopyGaussianBlurred> instead, as it allows you to change
+ your kernel size and sigma value. Future versions of this
+ function may fall back to calling it instead of
+ <gdImageConvolution>, causing subtle changes so be warned.
+
+ Parameters:
+ im - The image to blur
+
+ Returns:
+ GD_TRUE (1) on success, GD_FALSE (0) on failure.
+
+*/
+
+BGD_DECLARE(int) gdImageGaussianBlur(gdImagePtr im)
+{
+ float filter[3][3] = {
+ {1.0, 2.0, 1.0},
+ {2.0, 4.0, 2.0},
+ {1.0, 2.0, 1.0}
+ };
+
+ return gdImageConvolution(im, filter, 16, 0);
+}
+
+BGD_DECLARE(int) gdImageEmboss(gdImagePtr im)
+{
+/*
+ float filter[3][3] = {{1.0,1.0,1.0},
+ {0.0,0.0,0.0},
+ {-1.0,-1.0,-1.0}};
+*/
+ float filter[3][3] = {{ 1.5, 0.0, 0.0},
+ { 0.0, 0.0, 0.0},
+ { 0.0, 0.0,-1.5}};
+
+ return gdImageConvolution(im, filter, 1, 127);
+}
+
+BGD_DECLARE(int) gdImageMeanRemoval(gdImagePtr im)
+{
+ float filter[3][3] = {{-1.0,-1.0,-1.0},
+ {-1.0,9.0,-1.0},
+ {-1.0,-1.0,-1.0}};
+
+ return gdImageConvolution(im, filter, 1, 0);
+}
+
+BGD_DECLARE(int) gdImageSmooth(gdImagePtr im, float weight)
+{
+ float filter[3][3] = {{1.0,1.0,1.0},
+ {1.0,0.0,1.0},
+ {1.0,1.0,1.0}};
+
+ filter[1][1] = weight;
+
+ return gdImageConvolution(im, filter, weight+8, 0);
+}
+
+
+/* ======================== Gaussian Blur Code ======================== */
+
+/* Return an array of coefficients for 'radius' and 'sigma' (sigma >=
+ * 0 means compute it). Result length is 2*radius+1. */
+static double *
+gaussian_coeffs(int radius, double sigmaArg) {
+ const double sigma = (sigmaArg <= 0.0) ? (2.0/3.0)*radius : sigmaArg;
+ const double s = 2.0 * sigma * sigma;
+ double *result;
+ double sum = 0;
+ int x, n, count;
+
+ count = 2*radius + 1;
+
+ result = gdMalloc(sizeof(double) * count);
+ if (!result) {
+ return NULL;
+ }/* if */
+
+ for (x = -radius; x <= radius; x++) {
+ double coeff = exp(-(x*x)/s);
+
+ sum += coeff;
+ result[x + radius] = coeff;
+ }/* for */
+
+ for (n = 0; n < count; n++) {
+ result[n] /= sum;
+ }/* for */
+
+ return result;
+}/* gaussian_coeffs*/
+
+
+
+static inline int
+reflect(int max, int x)
+{
+ assert(x > -max && x < 2*max);
+
+ if(x < 0) return -x;
+ if(x >= max) return max - (x - max) - 1;
+ return x;
+}/* reflect*/
+
+
+
+static inline void
+applyCoeffsLine(gdImagePtr src, gdImagePtr dst, int line, int linelen,
+ double *coeffs, int radius, gdAxis axis)
+{
+ int ndx;
+
+ for (ndx = 0; ndx < linelen; ndx++) {
+ double r = 0, g = 0, b = 0, a = 0;
+ int cndx;
+ int *dest = (axis == HORIZONTAL) ?
+ &dst->tpixels[line][ndx] :
+ &dst->tpixels[ndx][line];
+
+ for (cndx = -radius; cndx <= radius; cndx++) {
+ const double coeff = coeffs[cndx + radius];
+ const int rndx = reflect(linelen, ndx + cndx);
+
+ const int srcpx = (axis == HORIZONTAL) ?
+ src->tpixels[line][rndx] :
+ src->tpixels[rndx][line];
+
+ r += coeff * (double)gdTrueColorGetRed(srcpx);
+ g += coeff * (double)gdTrueColorGetGreen(srcpx);
+ b += coeff * (double)gdTrueColorGetBlue(srcpx);
+ a += coeff * (double)gdTrueColorGetAlpha(srcpx);
+ }/* for */
+
+ *dest = gdTrueColorAlpha(uchar_clamp(r, 0xFF), uchar_clamp(g, 0xFF),
+ uchar_clamp(b, 0xFF), uchar_clamp(a, 0x7F));
+ }/* for */
+}/* applyCoeffsLine*/
+
+
+static void
+applyCoeffs(gdImagePtr src, gdImagePtr dst, double *coeffs, int radius,
+ gdAxis axis)
+{
+ int line, numlines, linelen;
+
+ if (axis == HORIZONTAL) {
+ numlines = src->sy;
+ linelen = src->sx;
+ } else {
+ numlines = src->sx;
+ linelen = src->sy;
+ }/* if .. else*/
+
+ for (line = 0; line < numlines; line++) {
+ applyCoeffsLine(src, dst, line, linelen, coeffs, radius, axis);
+ }/* for */
+}/* applyCoeffs*/
+
+/*
+ Function: gdImageCopyGaussianBlurred
+
+ Return a copy of the source image _src_ blurred according to the
+ parameters using the Gaussian Blur algorithm.
+
+ _radius_ is a radius, not a diameter so a radius of 2 (for
+ example) will blur across a region 5 pixels across (2 to the
+ center, 1 for the center itself and another 2 to the other edge).
+
+ _sigma_ represents the "fatness" of the curve (lower == fatter).
+ If _sigma_ is less than or equal to 0,
+ <gdImageCopyGaussianBlurred> ignores it and instead computes an
+ "optimal" value. Be warned that future versions of this function
+ may compute sigma differently.
+
+ The resulting image is always truecolor.
+
+ More Details:
+
+ A Gaussian Blur is generated by replacing each pixel's color
+ values with the average of the surrounding pixels' colors. This
+ region is a circle whose radius is given by argument _radius_.
+ Thus, a larger radius will yield a blurrier image.
+
+ This average is not a simple mean of the values. Instead, values
+ are weighted using the Gaussian function (roughly a bell curve
+ centered around the destination pixel) giving it much more
+ influence on the result than its neighbours. Thus, a fatter curve
+ will give the center pixel more weight and make the image less
+ blurry; lower _sigma_ values will yield flatter curves.
+
+ Currently, <gdImageCopyGaussianBlurred> computes the default sigma
+ as
+
+ (2/3)*radius
+
+ Note, however that we reserve the right to change this if we find
+ a better ratio. If you absolutely need the current sigma value,
+ you should set it yourself.
+
+ Parameters:
+
+ src - the source image
+ radius - the blur radius (*not* diameter--range is 2*radius + 1)
+ sigma - the sigma value or a value <= 0.0 to use the computed default
+
+ Returns:
+
+ The new image or NULL if an error occurred. The result is always
+ truecolor.
+
+ Example:
+
+ > FILE *in;
+ > gdImagePtr result, src;
+ >
+ > in = fopen("foo.png", "rb");
+ > src = gdImageCreateFromPng(in);
+ >
+ > result = gdImageCopyGaussianBlurred(im, src->sx / 10, -1.0);
+
+*/
+
+/* TODO: Look into turning this into a generic seperable filter
+ * function with Gaussian Blur being one special case. (At the
+ * moment, I can't find any other useful separable filter so for not,
+ * it's just blur.) */
+BGD_DECLARE(gdImagePtr)
+gdImageCopyGaussianBlurred(gdImagePtr src, int radius, double sigma)
+{
+ gdImagePtr tmp = NULL, result = NULL;
+ double *coeffs;
+ int freeSrc = 0;
+
+ if (radius < 1) {
+ return NULL;
+ }/* if */
+
+ /* Compute the coefficients. */
+ coeffs = gaussian_coeffs(radius, sigma);
+ if (!coeffs) {
+ return NULL;
+ }/* if */
+
+ /* If the image is not truecolor, we first make a truecolor
+ * scratch copy. */
+ if (!src->trueColor) {
+ int tcstat;
+
+ src = gdImageClone(src);
+ if (!src) {
+ gdFree(coeffs);
+ return NULL;
+ }
+
+ tcstat = gdImagePaletteToTrueColor(src);
+ if (!tcstat) {
+ gdImageDestroy(src);
+ gdFree(coeffs);
+ return NULL;
+ }/* if */
+
+ freeSrc = 1;
+ }/* if */
+
+ /* Apply the filter horizontally. */
+ tmp = gdImageCreateTrueColor(src->sx, src->sy);
+ if (!tmp) {
+ gdFree(coeffs);
+ return NULL;
+ }
+ applyCoeffs(src, tmp, coeffs, radius, HORIZONTAL);
+
+ /* Apply the filter vertically. */
+ result = gdImageCreateTrueColor(src->sx, src->sy);
+ if (result) {
+ applyCoeffs(tmp, result, coeffs, radius, VERTICAL);
+ }/* if */
+
+ gdImageDestroy(tmp);
+ gdFree(coeffs);
+
+ if (freeSrc) gdImageDestroy(src);
+
+ return result;
+}/* gdImageCopyGaussianBlurred*/
+