/** * File: Image Filters */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "gd.h" #include "gdhelpers.h" #include "gd_intern.h" #ifdef _WIN32 # include #else # include #endif #include #include #include #undef NDEBUG /* Comment out this line to enable asserts. * TODO: This logic really belongs in cmake and configure. */ #define NDEBUG 1 #include 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 /* Function: gdImageScatter */ 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); } /* Function: gdImageScatterColor */ 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); } /* Function: gdImageScatterEx */ 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; } /* Function: gdImagePixelate */ 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; } /** * Function: gdImageNegate * * Invert an image * * Parameters: * src - The image. * * Returns: * Non-zero on success, zero on failure. */ 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; ysy; ++y) { for (x=0; xsx; ++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; } /** * Function: gdImageGrayScale * * Convert an image to grayscale * * The red, green and blue components of each pixel are replaced by their * weighted sum using the same coefficients as the REC.601 luma (Y') * calculation. The alpha components are retained. * * For palette images the result may differ due to palette limitations. * * Parameters: * src - The image. * * Returns: * Non-zero on success, zero on failure. */ BGD_DECLARE(int) gdImageGrayScale(gdImagePtr src) { int x, y; int r,g,b,a; int new_pxl, pxl; FuncPtr f; int alpha_blending; if (src==NULL) { return 0; } alpha_blending = src->alphaBlendingFlag; gdImageAlphaBlending(src, gdEffectReplace); f = GET_PIXEL_FUNCTION(src); for (y=0; ysy; ++y) { for (x=0; xsx; ++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); } } gdImageAlphaBlending(src, alpha_blending); return 1; } /** * Function: gdImageBrightness * * Change the brightness of an image * * Parameters: * src - The image. * brightness - The value to add to the color channels of all pixels. * * Returns: * Non-zero on success, zero on failure. * * See also: * - * - */ 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; ysy; ++y) { for (x=0; xsx; ++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; } /** * Function: gdImageContrast * * Change the contrast of an image * * Parameters: * src - The image. * contrast - The contrast adjustment value. Negative values increase, postive * values decrease the contrast. The larger the absolute value, the * stronger the effect. * * Returns: * Non-zero on success, zero on failure. * * See also: * - */ 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; ysy; ++y) { for (x=0; xsx; ++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; } /** * Function: gdImageColor * * Change channel values of an image * * Parameters: * src - The image. * red - The value to add to the red channel of all pixels. * green - The value to add to the green channel of all pixels. * blue - The value to add to the blue channel of all pixels. * alpha - The value to add to the alpha channel of all pixels. * * Returns: * Non-zero on success, zero on failure. * * See also: * - */ 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; ysy; ++y) { for (x=0; xsx; ++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; } /** * Function: gdImageConvolution * * Apply a convolution matrix to an image * * Depending on the matrix a wide range of effects can be accomplished, e.g. * blurring, sharpening, embossing and edge detection. * * Parameters: * src - The image. * filter - The 3x3 convolution matrix. * filter_div - The value to divide the convoluted channel values by. * offset - The value to add to the convoluted channel values. * * Returns: * Non-zero on success, zero on failure. * * See also: * - * - * - * - * - */ 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; ysy; y++) { for(x=0; xsx; 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; } /* Function: gdImageSelectiveBlur */ 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; ysy; y++) { for (x=0; xsx; 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; } /** * Function: gdImageEdgeDetectQuick * * Edge detection of an image * * (see edge_detect_quick.jpg) * * Parameters: * src - The image. * * Returns: * Non-zero on success, zero on failure. * * See also: * - * - */ 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 performs a Gaussian blur of radius 1 on the image. The image is modified in place. *NOTE:* You will almost certain want to use 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 , 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); } /** * Function: gdImageEmboss * * Emboss an image * * (see emboss.jpg) * * Parameters: * im - The image. * * Returns: * Non-zero on success, zero on failure. * * See also: * - */ 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); } /** * Function: gdImageMeanRemoval * * Mean removal of an image * * (see mean_removal.jpg) * * Parameters: * im - The image. * * Returns: * Non-zero on success, zero on failure. * * See also: * - * - */ 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); } /** * Function: gdImageSmooth * * Smooth an image * * (see smooth.jpg) * * Parameters: * im - The image. * weight - The strength of the smoothing. * * Returns: * Non-zero on success, zero on failure. * * See also: * - */ 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, 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, 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: (start code) FILE *in; gdImagePtr result, src; in = fopen("foo.png", "rb"); src = gdImageCreateFromPng(in); result = gdImageCopyGaussianBlurred(im, src->sx / 10, -1.0); (end code) */ /* 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*/