From bcd11a1dfbb520c6663d7737cf7e0fec93eb2d8a Mon Sep 17 00:00:00 2001 From: Kalle Sommer Nielsen Date: Fri, 10 May 2019 02:23:40 +0300 Subject: [PATCH] Implemented the gdScatter filter I wrote almost 10 years ago This filter is available as of libgd 2.1.0 which is our bare minimum for external compilation of ext/gd. The scatter filter works by iterating over all pixels in the image and shifting them randomly based on two modifier (`plus` and `sub`) values: dest_x = (int)(x + ((rand() % (plus - sub)) + sub)); dest_y = (int)(y + ((rand() % (plus - sub)) + sub)); Additionally the scatter filter also supports by only shifting pixels where the current pixel being iterated is one or more colors, allowing the scatter filter to only effect solid colors in part of an image. Note, due to the nature of randomness and implementation, pixels who were shifted ahead of iteration will be shifted once more and therefore the bottom right of an image may contain a slight scatter effect due to this. --- ext/gd/gd.c | 52 ++++++++++++++++- ext/gd/libgd/gd.h | 12 ++++ ext/gd/libgd/gd_filter.c | 102 ++++++++++++++++++++++++++++++++++ ext/gd/tests/imagefilter.phpt | 11 ++++ 4 files changed, 175 insertions(+), 2 deletions(-) diff --git a/ext/gd/gd.c b/ext/gd/gd.c index f9319cf830..786589d263 100644 --- a/ext/gd/gd.c +++ b/ext/gd/gd.c @@ -112,7 +112,8 @@ int overflow2(int a, int b); #define IMAGE_FILTER_MEAN_REMOVAL 9 #define IMAGE_FILTER_SMOOTH 10 #define IMAGE_FILTER_PIXELATE 11 -#define IMAGE_FILTER_MAX 11 +#define IMAGE_FILTER_SCATTER 12 +#define IMAGE_FILTER_MAX 12 #define IMAGE_FILTER_MAX_ARGS 6 static void php_image_filter_negate(INTERNAL_FUNCTION_PARAMETERS); static void php_image_filter_grayscale(INTERNAL_FUNCTION_PARAMETERS); @@ -126,6 +127,7 @@ static void php_image_filter_selective_blur(INTERNAL_FUNCTION_PARAMETERS); static void php_image_filter_mean_removal(INTERNAL_FUNCTION_PARAMETERS); static void php_image_filter_smooth(INTERNAL_FUNCTION_PARAMETERS); static void php_image_filter_pixelate(INTERNAL_FUNCTION_PARAMETERS); +static void php_image_filter_scatter(INTERNAL_FUNCTION_PARAMETERS); /* End Section filters declarations */ static gdImagePtr _php_image_create_from_string (zval *Data, char *tn, gdImagePtr (*ioctx_func_p)()); @@ -1191,6 +1193,7 @@ PHP_MINIT_FUNCTION(gd) REGISTER_LONG_CONSTANT("IMG_FILTER_MEAN_REMOVAL", IMAGE_FILTER_MEAN_REMOVAL, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("IMG_FILTER_SMOOTH", IMAGE_FILTER_SMOOTH, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("IMG_FILTER_PIXELATE", IMAGE_FILTER_PIXELATE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_FILTER_SCATTER", IMAGE_FILTER_SCATTER, CONST_CS | CONST_PERSISTENT); /* End Section Filters */ #ifdef GD_VERSION_STRING @@ -4455,6 +4458,50 @@ static void php_image_filter_pixelate(INTERNAL_FUNCTION_PARAMETERS) RETURN_FALSE; } +static void php_image_filter_scatter(INTERNAL_FUNCTION_PARAMETERS) +{ + zval *IM; + zval *hash_colors = NULL; + gdImagePtr im; + zend_long tmp; + zend_long scatter_sub, scatter_plus; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rlll|a", &IM, &tmp, &scatter_sub, &scatter_plus, &hash_colors) == FAILURE) { + RETURN_FALSE; + } + + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { + RETURN_FALSE; + } + + if (hash_colors) { + uint32_t i = 0; + uint32_t num_colors = zend_hash_num_elements(Z_ARRVAL_P(hash_colors)); + zval *color; + int *colors; + + if (num_colors == 0) { + RETURN_BOOL(gdImageScatter(im, (int)scatter_sub, (int)scatter_plus)); + } + + colors = emalloc(num_colors * sizeof(int)); + + zend_hash_internal_pointer_reset(Z_ARRVAL_P(hash_colors)); + + while ((color = zend_hash_get_current_data(Z_ARRVAL_P(hash_colors))) != NULL) { + zend_hash_move_forward(Z_ARRVAL_P(hash_colors)); + + *(colors + i++) = (int) zval_get_long(color); + } + + RETVAL_BOOL(gdImageScatterColor(im, (int)scatter_sub, (int)scatter_plus, colors, num_colors)); + + efree(colors); + } else { + RETURN_BOOL(gdImageScatter(im, (int) scatter_sub, (int) scatter_plus)) + } +} + /* {{{ proto bool imagefilter(resource src_im, int filtertype[, int arg1 [, int arg2 [, int arg3 [, int arg4 ]]]] ) Applies Filter an image using a custom angle */ PHP_FUNCTION(imagefilter) @@ -4476,7 +4523,8 @@ PHP_FUNCTION(imagefilter) php_image_filter_selective_blur, php_image_filter_mean_removal, php_image_filter_smooth, - php_image_filter_pixelate + php_image_filter_pixelate, + php_image_filter_scatter }; if (ZEND_NUM_ARGS() < 2 || ZEND_NUM_ARGS() > IMAGE_FILTER_MAX_ARGS) { diff --git a/ext/gd/libgd/gd.h b/ext/gd/libgd/gd.h index 89a64041a0..30f59b38f6 100644 --- a/ext/gd/libgd/gd.h +++ b/ext/gd/libgd/gd.h @@ -717,6 +717,18 @@ enum gdPixelateMode { int gdImagePixelate(gdImagePtr im, int block_size, const unsigned int mode); +typedef struct { + int sub; + int plus; + unsigned int num_colors; + int *colors; + unsigned int seed; +} gdScatter, *gdScatterPtr; + +int gdImageScatter(gdImagePtr im, int sub, int plus); +int gdImageScatterColor(gdImagePtr im, int sub, int plus, int colors[], unsigned int num_colors); +int gdImageScatterEx(gdImagePtr im, gdScatterPtr s); + /* Macros to access information about images. */ /* Returns nonzero if the image is a truecolor image, diff --git a/ext/gd/libgd/gd_filter.c b/ext/gd/libgd/gd_filter.c index fc48cd08de..cebaa8785a 100644 --- a/ext/gd/libgd/gd_filter.c +++ b/ext/gd/libgd/gd_filter.c @@ -6,12 +6,114 @@ #include "gd_intern.h" +#ifdef _WIN32 +# include +#else +# include +#endif +#include +#include + /* Filters function added on 2003/12 * by Pierre-Alain Joye (pierre@php.net) + * + * Scatter filter added in libgd 2.1.0 + * by Kalle Sommer Nielsen (kalle@php.net) **/ + /* Begin filters function */ #define GET_PIXEL_FUNCTION(src)(src->trueColor?gdImageGetTrueColorPixel:gdImageGetPixel) +#ifdef _WIN32 +# define GD_SCATTER_SEED() (unsigned int)(time(0) * GetCurrentProcessId()) +#else +# define GD_SCATTER_SEED() (unsigned int)(time(0) * getpid()) +#endif + +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); +} + +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); +} + +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; +} + /* invert src image */ int gdImageNegate(gdImagePtr src) { diff --git a/ext/gd/tests/imagefilter.phpt b/ext/gd/tests/imagefilter.phpt index 87efb1591e..a46cc1476e 100644 --- a/ext/gd/tests/imagefilter.phpt +++ b/ext/gd/tests/imagefilter.phpt @@ -82,6 +82,16 @@ $SOURCE_IMG = $SAVE_DIR . "/test.png"; } else { echo "IMG_FILTER_PIXELATE failed\n"; } + + $im = imagecreatefrompng($SOURCE_IMG); + + if (imagefilter($im, IMG_FILTER_SCATTER, 3, 5)) { + imagepng($im, $SAVE_DIR . "/IMG_FILTER_SCATTER.png"); + echo "IMG_FILTER_SCATTER success\n"; + unlink($SAVE_DIR . "/IMG_FILTER_SCATTER.png"); + } else { + echo "IMG_FILTER_SCATTER failed\n"; + } ?> --EXPECT-- IMG_FILTER_NEGATE success @@ -96,3 +106,4 @@ IMG_FILTER_COLORIZE success IMG_FILTER_CONTRAST success IMG_FILTER_BRIGHTNESS success IMG_FILTER_PIXELATE success +IMG_FILTER_SCATTER success -- 2.40.0