]> granicus.if.org Git - php/commitdiff
Implemented the gdScatter filter I wrote almost 10 years ago
authorKalle Sommer Nielsen <kalle@php.net>
Thu, 9 May 2019 23:23:40 +0000 (02:23 +0300)
committerKalle Sommer Nielsen <kalle@php.net>
Thu, 9 May 2019 23:23:40 +0000 (02:23 +0300)
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
ext/gd/libgd/gd.h
ext/gd/libgd/gd_filter.c
ext/gd/tests/imagefilter.phpt

index f9319cf830e3721b6392aacda210c228445642d8..786589d263b941156bab09922acadbab42659a27 100644 (file)
@@ -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) {
index 89a64041a0eaac91e1805fdd51eef68ca2de8a4d..30f59b38f64c6773ad88c95d1ef3fa17c78d4340 100644 (file)
@@ -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,
index fc48cd08de4d9da9503efda2c8e9a26bbd0e399d..cebaa8785aa712ab3a868ea0405afd9d4801a21a 100644 (file)
 
 #include "gd_intern.h"
 
+#ifdef _WIN32
+# include <windows.h>
+#else
+# include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <time.h>
+
 /* 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)
 {
index 87efb1591e993cc05666a25dad4bc0c5ed4a9e02..a46cc1476e317b0ac10db23973d8deaa50526b00 100644 (file)
@@ -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