Skip to content

Commit bcd11a1

Browse files
committed
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.
1 parent ec518ae commit bcd11a1

File tree

4 files changed

+175
-2
lines changed

4 files changed

+175
-2
lines changed

ext/gd/gd.c

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,8 @@ int overflow2(int a, int b);
112112
#define IMAGE_FILTER_MEAN_REMOVAL 9
113113
#define IMAGE_FILTER_SMOOTH 10
114114
#define IMAGE_FILTER_PIXELATE 11
115-
#define IMAGE_FILTER_MAX 11
115+
#define IMAGE_FILTER_SCATTER 12
116+
#define IMAGE_FILTER_MAX 12
116117
#define IMAGE_FILTER_MAX_ARGS 6
117118
static void php_image_filter_negate(INTERNAL_FUNCTION_PARAMETERS);
118119
static void php_image_filter_grayscale(INTERNAL_FUNCTION_PARAMETERS);
@@ -126,6 +127,7 @@ static void php_image_filter_selective_blur(INTERNAL_FUNCTION_PARAMETERS);
126127
static void php_image_filter_mean_removal(INTERNAL_FUNCTION_PARAMETERS);
127128
static void php_image_filter_smooth(INTERNAL_FUNCTION_PARAMETERS);
128129
static void php_image_filter_pixelate(INTERNAL_FUNCTION_PARAMETERS);
130+
static void php_image_filter_scatter(INTERNAL_FUNCTION_PARAMETERS);
129131

130132
/* End Section filters declarations */
131133
static gdImagePtr _php_image_create_from_string (zval *Data, char *tn, gdImagePtr (*ioctx_func_p)());
@@ -1191,6 +1193,7 @@ PHP_MINIT_FUNCTION(gd)
11911193
REGISTER_LONG_CONSTANT("IMG_FILTER_MEAN_REMOVAL", IMAGE_FILTER_MEAN_REMOVAL, CONST_CS | CONST_PERSISTENT);
11921194
REGISTER_LONG_CONSTANT("IMG_FILTER_SMOOTH", IMAGE_FILTER_SMOOTH, CONST_CS | CONST_PERSISTENT);
11931195
REGISTER_LONG_CONSTANT("IMG_FILTER_PIXELATE", IMAGE_FILTER_PIXELATE, CONST_CS | CONST_PERSISTENT);
1196+
REGISTER_LONG_CONSTANT("IMG_FILTER_SCATTER", IMAGE_FILTER_SCATTER, CONST_CS | CONST_PERSISTENT);
11941197
/* End Section Filters */
11951198

11961199
#ifdef GD_VERSION_STRING
@@ -4455,6 +4458,50 @@ static void php_image_filter_pixelate(INTERNAL_FUNCTION_PARAMETERS)
44554458
RETURN_FALSE;
44564459
}
44574460

4461+
static void php_image_filter_scatter(INTERNAL_FUNCTION_PARAMETERS)
4462+
{
4463+
zval *IM;
4464+
zval *hash_colors = NULL;
4465+
gdImagePtr im;
4466+
zend_long tmp;
4467+
zend_long scatter_sub, scatter_plus;
4468+
4469+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "rlll|a", &IM, &tmp, &scatter_sub, &scatter_plus, &hash_colors) == FAILURE) {
4470+
RETURN_FALSE;
4471+
}
4472+
4473+
if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) {
4474+
RETURN_FALSE;
4475+
}
4476+
4477+
if (hash_colors) {
4478+
uint32_t i = 0;
4479+
uint32_t num_colors = zend_hash_num_elements(Z_ARRVAL_P(hash_colors));
4480+
zval *color;
4481+
int *colors;
4482+
4483+
if (num_colors == 0) {
4484+
RETURN_BOOL(gdImageScatter(im, (int)scatter_sub, (int)scatter_plus));
4485+
}
4486+
4487+
colors = emalloc(num_colors * sizeof(int));
4488+
4489+
zend_hash_internal_pointer_reset(Z_ARRVAL_P(hash_colors));
4490+
4491+
while ((color = zend_hash_get_current_data(Z_ARRVAL_P(hash_colors))) != NULL) {
4492+
zend_hash_move_forward(Z_ARRVAL_P(hash_colors));
4493+
4494+
*(colors + i++) = (int) zval_get_long(color);
4495+
}
4496+
4497+
RETVAL_BOOL(gdImageScatterColor(im, (int)scatter_sub, (int)scatter_plus, colors, num_colors));
4498+
4499+
efree(colors);
4500+
} else {
4501+
RETURN_BOOL(gdImageScatter(im, (int) scatter_sub, (int) scatter_plus))
4502+
}
4503+
}
4504+
44584505
/* {{{ proto bool imagefilter(resource src_im, int filtertype[, int arg1 [, int arg2 [, int arg3 [, int arg4 ]]]] )
44594506
Applies Filter an image using a custom angle */
44604507
PHP_FUNCTION(imagefilter)
@@ -4476,7 +4523,8 @@ PHP_FUNCTION(imagefilter)
44764523
php_image_filter_selective_blur,
44774524
php_image_filter_mean_removal,
44784525
php_image_filter_smooth,
4479-
php_image_filter_pixelate
4526+
php_image_filter_pixelate,
4527+
php_image_filter_scatter
44804528
};
44814529

44824530
if (ZEND_NUM_ARGS() < 2 || ZEND_NUM_ARGS() > IMAGE_FILTER_MAX_ARGS) {

ext/gd/libgd/gd.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,18 @@ enum gdPixelateMode {
717717

718718
int gdImagePixelate(gdImagePtr im, int block_size, const unsigned int mode);
719719

720+
typedef struct {
721+
int sub;
722+
int plus;
723+
unsigned int num_colors;
724+
int *colors;
725+
unsigned int seed;
726+
} gdScatter, *gdScatterPtr;
727+
728+
int gdImageScatter(gdImagePtr im, int sub, int plus);
729+
int gdImageScatterColor(gdImagePtr im, int sub, int plus, int colors[], unsigned int num_colors);
730+
int gdImageScatterEx(gdImagePtr im, gdScatterPtr s);
731+
720732
/* Macros to access information about images. */
721733

722734
/* Returns nonzero if the image is a truecolor image,

ext/gd/libgd/gd_filter.c

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,114 @@
66

77
#include "gd_intern.h"
88

9+
#ifdef _WIN32
10+
# include <windows.h>
11+
#else
12+
# include <unistd.h>
13+
#endif
14+
#include <stdlib.h>
15+
#include <time.h>
16+
917
/* Filters function added on 2003/12
1018
* by Pierre-Alain Joye (pierre@php.net)
19+
*
20+
* Scatter filter added in libgd 2.1.0
21+
* by Kalle Sommer Nielsen (kalle@php.net)
1122
**/
23+
1224
/* Begin filters function */
1325
#define GET_PIXEL_FUNCTION(src)(src->trueColor?gdImageGetTrueColorPixel:gdImageGetPixel)
1426

27+
#ifdef _WIN32
28+
# define GD_SCATTER_SEED() (unsigned int)(time(0) * GetCurrentProcessId())
29+
#else
30+
# define GD_SCATTER_SEED() (unsigned int)(time(0) * getpid())
31+
#endif
32+
33+
int gdImageScatter(gdImagePtr im, int sub, int plus)
34+
{
35+
gdScatter s;
36+
37+
s.sub = sub;
38+
s.plus = plus;
39+
s.num_colors = 0;
40+
s.seed = GD_SCATTER_SEED();
41+
return gdImageScatterEx(im, &s);
42+
}
43+
44+
int gdImageScatterColor(gdImagePtr im, int sub, int plus, int colors[], unsigned int num_colors)
45+
{
46+
gdScatter s;
47+
48+
s.sub = sub;
49+
s.plus = plus;
50+
s.colors = colors;
51+
s.num_colors = num_colors;
52+
s.seed = GD_SCATTER_SEED();
53+
return gdImageScatterEx(im, &s);
54+
}
55+
56+
int gdImageScatterEx(gdImagePtr im, gdScatterPtr scatter)
57+
{
58+
register int x, y;
59+
int dest_x, dest_y;
60+
int pxl, new_pxl;
61+
unsigned int n;
62+
int sub = scatter->sub, plus = scatter->plus;
63+
64+
if (plus == 0 && sub == 0) {
65+
return 1;
66+
}
67+
else if (sub >= plus) {
68+
return 0;
69+
}
70+
71+
(void)srand(scatter->seed);
72+
73+
if (scatter->num_colors) {
74+
for (y = 0; y < im->sy; y++) {
75+
for (x = 0; x < im->sx; x++) {
76+
dest_x = (int)(x + ((rand() % (plus - sub)) + sub));
77+
dest_y = (int)(y + ((rand() % (plus - sub)) + sub));
78+
79+
if (!gdImageBoundsSafe(im, dest_x, dest_y)) {
80+
continue;
81+
}
82+
83+
pxl = gdImageGetPixel(im, x, y);
84+
new_pxl = gdImageGetPixel(im, dest_x, dest_y);
85+
86+
for (n = 0; n < scatter->num_colors; n++) {
87+
if (pxl == scatter->colors[n]) {
88+
gdImageSetPixel(im, dest_x, dest_y, pxl);
89+
gdImageSetPixel(im, x, y, new_pxl);
90+
}
91+
}
92+
}
93+
}
94+
}
95+
else {
96+
for (y = 0; y < im->sy; y++) {
97+
for (x = 0; x < im->sx; x++) {
98+
dest_x = (int)(x + ((rand() % (plus - sub)) + sub));
99+
dest_y = (int)(y + ((rand() % (plus - sub)) + sub));
100+
101+
if (!gdImageBoundsSafe(im, dest_x, dest_y)) {
102+
continue;
103+
}
104+
105+
pxl = gdImageGetPixel(im, x, y);
106+
new_pxl = gdImageGetPixel(im, dest_x, dest_y);
107+
108+
gdImageSetPixel(im, dest_x, dest_y, pxl);
109+
gdImageSetPixel(im, x, y, new_pxl);
110+
}
111+
}
112+
}
113+
114+
return 1;
115+
}
116+
15117
/* invert src image */
16118
int gdImageNegate(gdImagePtr src)
17119
{

ext/gd/tests/imagefilter.phpt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,16 @@ $SOURCE_IMG = $SAVE_DIR . "/test.png";
8282
} else {
8383
echo "IMG_FILTER_PIXELATE failed\n";
8484
}
85+
86+
$im = imagecreatefrompng($SOURCE_IMG);
87+
88+
if (imagefilter($im, IMG_FILTER_SCATTER, 3, 5)) {
89+
imagepng($im, $SAVE_DIR . "/IMG_FILTER_SCATTER.png");
90+
echo "IMG_FILTER_SCATTER success\n";
91+
unlink($SAVE_DIR . "/IMG_FILTER_SCATTER.png");
92+
} else {
93+
echo "IMG_FILTER_SCATTER failed\n";
94+
}
8595
?>
8696
--EXPECT--
8797
IMG_FILTER_NEGATE success
@@ -96,3 +106,4 @@ IMG_FILTER_COLORIZE success
96106
IMG_FILTER_CONTRAST success
97107
IMG_FILTER_BRIGHTNESS success
98108
IMG_FILTER_PIXELATE success
109+
IMG_FILTER_SCATTER success

0 commit comments

Comments
 (0)