summaryrefslogtreecommitdiff
path: root/ext/gd
diff options
context:
space:
mode:
Diffstat (limited to 'ext/gd')
-rw-r--r--ext/gd/config.m43
-rw-r--r--ext/gd/config.w325
-rw-r--r--ext/gd/gd.c179
-rw-r--r--ext/gd/gdcache.c5
-rw-r--r--ext/gd/libgd/gd.h59
-rw-r--r--ext/gd/libgd/gd_crop.c356
-rw-r--r--ext/gd/libgd/gd_png.c2
-rw-r--r--ext/gd/libgd/gd_transform.c73
-rw-r--r--ext/gd/php_gd.h3
-rw-r--r--ext/gd/tests/imagecrop_auto.phpt82
-rw-r--r--ext/gd/tests/imageflip.phpt30
-rw-r--r--ext/gd/tests/logo_noise.pngbin0 -> 25182 bytes
12 files changed, 792 insertions, 5 deletions
diff --git a/ext/gd/config.m4 b/ext/gd/config.m4
index 79a74e140..2f7170586 100644
--- a/ext/gd/config.m4
+++ b/ext/gd/config.m4
@@ -297,7 +297,8 @@ if test "$PHP_GD" = "yes"; then
libgd/gdfontmb.c libgd/gdfontl.c libgd/gdfontg.c libgd/gdtables.c libgd/gdft.c \
libgd/gdcache.c libgd/gdkanji.c libgd/wbmp.c libgd/gd_wbmp.c libgd/gdhelpers.c \
libgd/gd_topal.c libgd/gd_gif_in.c libgd/xbm.c libgd/gd_gif_out.c libgd/gd_security.c \
- libgd/gd_filter.c libgd/gd_pixelate.c libgd/gd_arc.c libgd/gd_rotate.c libgd/gd_color.c"
+ libgd/gd_filter.c libgd/gd_pixelate.c libgd/gd_arc.c libgd/gd_rotate.c libgd/gd_color.c \
+ libgd/gd_transform.c libgd/gd_crop.c"
dnl check for fabsf and floorf which are available since C99
AC_CHECK_FUNCS(fabsf floorf)
diff --git a/ext/gd/config.w32 b/ext/gd/config.w32
index b9c208382..b25a0e2d9 100644
--- a/ext/gd/config.w32
+++ b/ext/gd/config.w32
@@ -46,8 +46,9 @@ if (PHP_GD != "no") {
gdcache.c gdfontg.c gdfontl.c gdfontmb.c gdfonts.c gdfontt.c \
gdft.c gd_gd2.c gd_gd.c gd_gif_in.c gd_gif_out.c gdhelpers.c gd_io.c gd_io_dp.c \
gd_io_file.c gd_io_ss.c gd_jpeg.c gdkanji.c gd_png.c gd_ss.c \
- gdtables.c gd_topal.c gd_wbmp.c gdxpm.c wbmp.c xbm.c gd_security.c \
- gd_filter.c gd_pixelate.c gd_arc.c gd_rotate.c gd_color.c webpimg.c gd_webp.c ", "gd");
+ gdtables.c gd_topal.c gd_wbmp.c gdxpm.c wbmp.c xbm.c gd_security.c gd_transform.c \
+ gd_filter.c gd_pixelate.c gd_arc.c gd_rotate.c gd_color.c webpimg.c gd_webp.c \
+ gd_crop.c", "gd");
AC_DEFINE('HAVE_LIBGD', 1, 'GD support');
ADD_FLAG("CFLAGS_GD", " \
/D HAVE_GD_DYNAMIC_CTX_EX=1 \
diff --git a/ext/gd/gd.c b/ext/gd/gd.c
index 72abbba2b..be9501e55 100644
--- a/ext/gd/gd.c
+++ b/ext/gd/gd.c
@@ -885,6 +885,23 @@ ZEND_BEGIN_ARG_INFO(arginfo_imageantialias, 0)
ZEND_ARG_INFO(0, im)
ZEND_ARG_INFO(0, on)
ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_imageflip, 0)
+ ZEND_ARG_INFO(0, im)
+ ZEND_ARG_INFO(0, mode)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_imagecrop, 0)
+ ZEND_ARG_INFO(0, im)
+ ZEND_ARG_INFO(0, rect)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_imagecropauto, 0)
+ ZEND_ARG_INFO(0, im)
+ ZEND_ARG_INFO(0, mode)
+ ZEND_ARG_INFO(0, threshold)
+ ZEND_ARG_INFO(0, color)
+ZEND_END_ARG_INFO()
#endif
/* }}} */
@@ -944,6 +961,9 @@ const zend_function_entry gd_functions[] = {
#ifdef HAVE_GD_BUNDLED
PHP_FE(imageantialias, arginfo_imageantialias)
+ PHP_FE(imageflip, arginfo_imageflip)
+ PHP_FE(imagecrop, arginfo_imagecrop)
+ PHP_FE(imagecropauto, arginfo_imagecropauto)
#endif
#if HAVE_GD_IMAGESETTILE
@@ -1194,6 +1214,17 @@ PHP_MINIT_FUNCTION(gd)
REGISTER_LONG_CONSTANT("IMG_EFFECT_NORMAL", gdEffectNormal, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("IMG_EFFECT_OVERLAY", gdEffectOverlay, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("GD_BUNDLED", 1, CONST_CS | CONST_PERSISTENT);
+
+ REGISTER_LONG_CONSTANT("IMG_FLIP_HORIZONTAL", GD_FLIP_HORINZONTAL, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("IMG_FLIP_VERTICAL", GD_FLIP_VERTICAL, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("IMG_FLIP_BOTH", GD_FLIP_BOTH, CONST_CS | CONST_PERSISTENT);
+
+ REGISTER_LONG_CONSTANT("IMG_CROP_DEFAULT", GD_CROP_DEFAULT, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("IMG_CROP_TRANSPARENT", GD_CROP_TRANSPARENT, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("IMG_CROP_BLACK", GD_CROP_BLACK, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("IMG_CROP_WHITE", GD_CROP_WHITE, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("IMG_CROP_SIDES", GD_CROP_SIDES, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("IMG_CROP_THRESHOLD", GD_CROP_THRESHOLD, CONST_CS | CONST_PERSISTENT);
#else
REGISTER_LONG_CONSTANT("GD_BUNDLED", 0, CONST_CS | CONST_PERSISTENT);
#endif
@@ -5041,7 +5072,7 @@ PHP_FUNCTION(imageconvolution)
if (zend_hash_index_find(Z_ARRVAL_PP(var), (j), (void **) &var2) == SUCCESS) {
SEPARATE_ZVAL(var2);
convert_to_double(*var2);
- matrix[i][j] = Z_DVAL_PP(var2);
+ matrix[i][j] = (float)Z_DVAL_PP(var2);
} else {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "You must have a 3x3 matrix");
RETURN_FALSE;
@@ -5049,7 +5080,7 @@ PHP_FUNCTION(imageconvolution)
}
}
}
- res = gdImageConvolution(im_src, matrix, div, offset);
+ res = gdImageConvolution(im_src, matrix, (float)div, (float)offset);
if (res) {
RETURN_TRUE;
@@ -5078,8 +5109,152 @@ PHP_FUNCTION(imageantialias)
RETURN_TRUE;
}
/* }}} */
+
+
+/* {{{ proto void imageflip(resource im, int mode)
+ Flip an image (in place) horizontally, vertically or both directions. */
+PHP_FUNCTION(imageflip)
+{
+ zval *IM;
+ long mode;
+ gdImagePtr im;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &IM, &mode) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(im, gdImagePtr, &IM, -1, "Image", le_gd);
+
+ switch (mode) {
+ case GD_FLIP_VERTICAL:
+ gdImageFlipHorizontal(im);
+ break;
+
+ case GD_FLIP_HORINZONTAL:
+ gdImageFlipVertical(im);
+ break;
+
+ case GD_FLIP_BOTH:
+ gdImageFlipBoth(im);
+ break;
+
+ default:
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown flip mode");
+ RETURN_FALSE;
+ }
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto void imagecrop(resource im, array rect)
+ Crop an image using the given coordinates and size, x, y, width and height. */
+PHP_FUNCTION(imagecrop)
+{
+ zval *IM;
+ long mode = -1;
+ long color = -1;
+ double threshold = 0.5f;
+ gdImagePtr im;
+ gdImagePtr im_crop;
+ gdRect rect;
+ zval *z_rect;
+ zval **tmp;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|a", &IM, &z_rect) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(im, gdImagePtr, &IM, -1, "Image", le_gd);
+
+ if (zend_hash_find(HASH_OF(z_rect), "x", sizeof("x"), (void **)&tmp) != FAILURE) {
+ rect.x = Z_LVAL_PP(tmp);
+ } else {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Missing x position");
+ RETURN_FALSE;
+ }
+
+ if (zend_hash_find(HASH_OF(z_rect), "y", sizeof("x"), (void **)&tmp) != FAILURE) {
+ rect.y = Z_LVAL_PP(tmp);
+ } else {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Missing y position");
+ RETURN_FALSE;
+ }
+
+ if (zend_hash_find(HASH_OF(z_rect), "width", sizeof("width"), (void **)&tmp) != FAILURE) {
+ rect.width = Z_LVAL_PP(tmp);
+ } else {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Missing width");
+ RETURN_FALSE;
+ }
+
+ if (zend_hash_find(HASH_OF(z_rect), "height", sizeof("height"), (void **)&tmp) != FAILURE) {
+ rect.height = Z_LVAL_PP(tmp);
+ } else {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Missing height");
+ RETURN_FALSE;
+ }
+
+ im_crop = gdImageCrop(im, &rect);
+
+ if (im_crop == NULL) {
+ RETURN_FALSE;
+ } else {
+ ZEND_REGISTER_RESOURCE(return_value, im_crop, le_gd);
+ }
+}
+/* }}} */
+
+/* {{{ proto void imagecropauto(resource im [, int mode [, threshold [, color]]])
+ Crop an image automatically using one of the available modes. */
+PHP_FUNCTION(imagecropauto)
+{
+ zval *IM;
+ long mode = -1;
+ long color = -1;
+ double threshold = 0.5f;
+ gdImagePtr im;
+ gdImagePtr im_crop;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|ldl", &IM, &mode, &threshold, &color) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(im, gdImagePtr, &IM, -1, "Image", le_gd);
+
+ switch (mode) {
+ case -1:
+ mode = GD_CROP_DEFAULT;
+ case GD_CROP_DEFAULT:
+ case GD_CROP_TRANSPARENT:
+ case GD_CROP_BLACK:
+ case GD_CROP_WHITE:
+ case GD_CROP_SIDES:
+ im_crop = gdImageCropAuto(im, mode);
+ break;
+
+ case GD_CROP_THRESHOLD:
+ if (color < 0) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Color argument missing with threshold mode");
+ RETURN_FALSE;
+ }
+ im_crop = gdImageCropThreshold(im, color, (float) threshold);
+ break;
+
+ default:
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown flip mode");
+ RETURN_FALSE;
+ }
+ if (im_crop == NULL) {
+ RETURN_FALSE;
+ } else {
+ ZEND_REGISTER_RESOURCE(return_value, im_crop, le_gd);
+ }
+}
+/* }}} */
#endif
+
/*
* Local variables:
* tab-width: 4
diff --git a/ext/gd/gdcache.c b/ext/gd/gdcache.c
index 2349e38b9..231a1f791 100644
--- a/ext/gd/gdcache.c
+++ b/ext/gd/gdcache.c
@@ -95,6 +95,11 @@ gdCacheGet( gdCache_head_t *head, void *keydata )
void *userdata;
elem = head->mru;
+ if (elem == NULL) {
+ return NULL;
+
+ }
+
while(elem) {
if ((*(head->gdCacheTest))(elem->userdata, keydata)) {
if (i) { /* if not already most-recently-used */
diff --git a/ext/gd/libgd/gd.h b/ext/gd/libgd/gd.h
index 8aedc2c38..8d9df2a49 100644
--- a/ext/gd/libgd/gd.h
+++ b/ext/gd/libgd/gd.h
@@ -209,6 +209,31 @@ typedef struct {
/* Text functions take these. */
typedef gdFont *gdFontPtr;
+
+/**
+ * Group: Types
+ *
+ * typedef: gdRect
+ * Defines a rectilinear region.
+ *
+ * x - left position
+ * y - right position
+ * width - Rectangle width
+ * height - Rectangle height
+ *
+ * typedef: gdRectPtr
+ * Pointer to a <gdRect>
+ *
+ * See also:
+ * <gdSetInterpolationMethod>
+ **/
+typedef struct
+{
+ int x, y;
+ int width, height;
+}
+gdRect, *gdRectPtr;
+
/* For backwards compatibility only. Use gdImageSetStyle()
for MUCH more flexible line drawing. Also see
gdImageSetBrush(). */
@@ -682,6 +707,40 @@ int gdImageSmooth(gdImagePtr im, float weight);
/* Image comparison definitions */
int gdImageCompare(gdImagePtr im1, gdImagePtr im2);
+void gdImageFlipHorizontal(gdImagePtr im);
+void gdImageFlipVertical(gdImagePtr im);
+void gdImageFlipBoth(gdImagePtr im);
+
+#define GD_FLIP_HORINZONTAL 1
+#define GD_FLIP_VERTICAL 2
+#define GD_FLIP_BOTH 3
+
+/**
+ * Group: Crop
+ *
+ * Constants: gdCropMode
+ * GD_CROP_DEFAULT - Default crop mode (4 corners or background)
+ * GD_CROP_TRANSPARENT - Crop using the transparent color
+ * GD_CROP_BLACK - Crop black borders
+ * GD_CROP_WHITE - Crop white borders
+ * GD_CROP_SIDES - Crop using colors of the 4 corners
+ *
+ * See also:
+ * <gdImageAutoCrop>
+ **/
+enum gdCropMode {
+ GD_CROP_DEFAULT = 0,
+ GD_CROP_TRANSPARENT,
+ GD_CROP_BLACK,
+ GD_CROP_WHITE,
+ GD_CROP_SIDES,
+ GD_CROP_THRESHOLD
+};
+
+gdImagePtr gdImageCrop(gdImagePtr src, const gdRectPtr crop);
+gdImagePtr gdImageCropAuto(gdImagePtr im, const unsigned int mode);
+gdImagePtr gdImageCropThreshold(gdImagePtr im, const unsigned int color, const float threshold);
+
#define GD_CMP_IMAGE 1 /* Actual image IS different */
#define GD_CMP_NUM_COLORS 2 /* Number of Colours in pallette differ */
#define GD_CMP_COLOR 4 /* Image colours differ */
diff --git a/ext/gd/libgd/gd_crop.c b/ext/gd/libgd/gd_crop.c
new file mode 100644
index 000000000..9ce48273b
--- /dev/null
+++ b/ext/gd/libgd/gd_crop.c
@@ -0,0 +1,356 @@
+/**
+ * Title: Crop
+ *
+ * A couple of functions to crop images, automatically (auto detection of
+ * the borders color), using a given color (with or without tolerance)
+ * or using a selection.
+ *
+ * The threshold method works relatively well but it can be improved.
+ * Maybe L*a*b* and Delta-E will give better results (and a better
+ * granularity).
+ *
+ * Example:
+ * (start code)
+ * im2 = gdImageAutoCrop(im, GD_CROP_SIDES);
+ * if (im2) {
+
+ * }
+ * gdImageDestroy(im2);
+ * (end code)
+ **/
+
+#include <gd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+static int gdGuessBackgroundColorFromCorners(gdImagePtr im, int *color);
+static int gdColorMatch(gdImagePtr im, int col1, int col2, float threshold);
+
+/**
+ * Function: gdImageCrop
+ * Crops the src image using the area defined by the <crop> rectangle.
+ * The result is returned as a new image.
+ *
+ *
+ * Parameters:
+ * src - Source image
+ * crop - Rectangular region to crop
+ *
+ * Returns:
+ * <gdImagePtr> on success or NULL
+ */
+gdImagePtr gdImageCrop(gdImagePtr src, const gdRectPtr crop)
+{
+ gdImagePtr dst;
+
+ if (src->trueColor) {
+ dst = gdImageCreateTrueColor(crop->width, crop->height);
+ gdImageSaveAlpha(dst, 1);
+ } else {
+ dst = gdImageCreate(crop->width, crop->height);
+ gdImagePaletteCopy(dst, src);
+ }
+ dst->transparent = src->transparent;
+
+ if (src->sx < (crop->x + crop->width -1)) {
+ crop->width = src->sx - crop->x + 1;
+ }
+ if (src->sy < (crop->y + crop->height -1)) {
+ crop->height = src->sy - crop->y + 1;
+ }
+#if 0
+printf("rect->x: %i\nrect->y: %i\nrect->width: %i\nrect->height: %i\n", crop->x, crop->y, crop->width, crop->height);
+#endif
+ if (dst == NULL) {
+ return NULL;
+ } else {
+ int y = crop->y;
+ if (src->trueColor) {
+ unsigned int dst_y = 0;
+ while (y < (crop->y + (crop->height - 1))) {
+ /* TODO: replace 4 w/byte per channel||pitch once avaiable */
+ memcpy(dst->tpixels[dst_y++], src->tpixels[y++] + crop->x, crop->width * 4);
+ }
+ } else {
+ int x;
+ for (y = crop->y; y < (crop->y + (crop->height - 1)); y++) {
+ for (x = crop->x; x < (crop->x + (crop->width - 1)); x++) {
+ dst->pixels[y - crop->y][x - crop->x] = src->pixels[y][x];
+ }
+ }
+ }
+ return dst;
+ }
+}
+
+/**
+ * Function: gdImageAutoCrop
+ * Automatic croping of the src image using the given mode
+ * (see <gdCropMode>)
+ *
+ *
+ * Parameters:
+ * im - Source image
+ * mode - crop mode
+ *
+ * Returns:
+ * <gdImagePtr> on success or NULL
+ *
+ * See also:
+ * <gdCropMode>
+ */
+gdImagePtr gdImageCropAuto(gdImagePtr im, const unsigned int mode)
+{
+ const int width = gdImageSX(im);
+ const int height = gdImageSY(im);
+
+ int x,y;
+ int color, corners, match;
+ gdRect crop;
+
+ crop.x = 0;
+ crop.y = 0;
+ crop.width = 0;
+ crop.height = 0;
+
+ switch (mode) {
+ case GD_CROP_TRANSPARENT:
+ color = gdImageGetTransparent(im);
+ break;
+
+ case GD_CROP_BLACK:
+ color = gdImageColorClosestAlpha(im, 0, 0, 0, 0);
+ break;
+
+ case GD_CROP_WHITE:
+ color = gdImageColorClosestAlpha(im, 255, 255, 255, 0);
+ break;
+
+ case GD_CROP_SIDES:
+ corners = gdGuessBackgroundColorFromCorners(im, &color);
+ break;
+
+ case GD_CROP_DEFAULT:
+ default:
+ color = gdImageGetTransparent(im);
+ if (color == -1) {
+ corners = gdGuessBackgroundColorFromCorners(im, &color);
+ }
+ break;
+ }
+
+ /* TODO: Add gdImageGetRowPtr and works with ptr at the row level
+ * for the true color and palette images
+ * new formats will simply work with ptr
+ */
+ match = 1;
+ for (y = 0; match && y < height; y++) {
+ for (x = 0; match && x < width; x++) {
+ int c2 = gdImageGetPixel(im, x, y);
+ match = (color == c2);
+ }
+ }
+
+ /* Nothing to do > bye
+ * Duplicate the image?
+ */
+ if (y == height - 1) {
+ return NULL;
+ }
+
+ crop.y = y -1;
+ match = 1;
+ for (y = height - 1; match && y >= 0; y--) {
+ for (x = 0; match && x < width; x++) {
+ match = (color == gdImageGetPixel(im, x,y));
+ }
+ }
+
+ if (y == 0) {
+ crop.height = height - crop.y + 1;
+ } else {
+ crop.height = y - crop.y + 2;
+ }
+
+ match = 1;
+ for (x = 0; match && x < width; x++) {
+ for (y = 0; match && y < crop.y + crop.height - 1; y++) {
+ match = (color == gdImageGetPixel(im, x,y));
+ }
+ }
+ crop.x = x - 1;
+
+ match = 1;
+ for (x = width - 1; match && x >= 0; x--) {
+ for (y = 0; match && y < crop.y + crop.height - 1; y++) {
+ match = (color == gdImageGetPixel(im, x,y));
+ }
+ }
+ crop.width = x - crop.x + 2;
+ if (crop.x <= 0 || crop.y <= 0 || crop.width <= 0 || crop.height <= 0) {
+ return NULL;
+ }
+ return gdImageCrop(im, &crop);
+}
+/*TODOs: Implement DeltaE instead, way better perceptual differences */
+/**
+ * Function: gdImageThresholdCrop
+ * Crop an image using a given color. The threshold argument defines
+ * the tolerance to be used while comparing the image color and the
+ * color to crop. The method used to calculate the color difference
+ * is based on the color distance in the RGB(a) cube.
+ *
+ *
+ * Parameters:
+ * im - Source image
+ * color - color to crop
+ * threshold - tolerance (0..100)
+ *
+ * Returns:
+ * <gdImagePtr> on success or NULL
+ *
+ * See also:
+ * <gdCropMode>, <gdImageAutoCrop> or <gdImageCrop>
+ */
+gdImagePtr gdImageCropThreshold(gdImagePtr im, const unsigned int color, const float threshold)
+{
+ const int width = gdImageSX(im);
+ const int height = gdImageSY(im);
+
+ int x,y;
+ int match;
+ gdRect crop;
+
+ crop.x = 0;
+ crop.y = 0;
+ crop.width = 0;
+ crop.height = 0;
+
+ /* Pierre: crop everything sounds bad */
+ if (threshold > 1.0) {
+ return NULL;
+ }
+
+ /* TODO: Add gdImageGetRowPtr and works with ptr at the row level
+ * for the true color and palette images
+ * new formats will simply work with ptr
+ */
+ match = 1;
+ for (y = 0; match && y < height; y++) {
+ for (x = 0; match && x < width; x++) {
+ match = (gdColorMatch(im, color, gdImageGetPixel(im, x,y), threshold)) > 0;
+ }
+ }
+
+ /* Pierre
+ * Nothing to do > bye
+ * Duplicate the image?
+ */
+ if (y == height - 1) {
+ return NULL;
+ }
+
+ crop.y = y -1;
+ match = 1;
+ for (y = height - 1; match && y >= 0; y--) {
+ for (x = 0; match && x < width; x++) {
+ match = (gdColorMatch(im, color, gdImageGetPixel(im, x, y), threshold)) > 0;
+ }
+ }
+
+ if (y == 0) {
+ crop.height = height - crop.y + 1;
+ } else {
+ crop.height = y - crop.y + 2;
+ }
+
+ match = 1;
+ for (x = 0; match && x < width; x++) {
+ for (y = 0; match && y < crop.y + crop.height - 1; y++) {
+ match = (gdColorMatch(im, color, gdImageGetPixel(im, x,y), threshold)) > 0;
+ }
+ }
+ crop.x = x - 1;
+
+ match = 1;
+ for (x = width - 1; match && x >= 0; x--) {
+ for (y = 0; match && y < crop.y + crop.height - 1; y++) {
+ match = (gdColorMatch(im, color, gdImageGetPixel(im, x,y), threshold)) > 0;
+ }
+ }
+ crop.width = x - crop.x + 2;
+
+ return gdImageCrop(im, &crop);
+}
+
+/* This algorithm comes from pnmcrop (http://netpbm.sourceforge.net/)
+ * Three steps:
+ * - if 3 corners are equal.
+ * - if two are equal.
+ * - Last solution: average the colors
+ */
+static int gdGuessBackgroundColorFromCorners(gdImagePtr im, int *color)
+{
+ const int tl = gdImageGetPixel(im, 0, 0);
+ const int tr = gdImageGetPixel(im, gdImageSX(im) - 1, 0);
+ const int bl = gdImageGetPixel(im, 0, gdImageSY(im) -1);
+ const int br = gdImageGetPixel(im, gdImageSX(im) - 1, gdImageSY(im) -1);
+
+ if (tr == bl && tr == br) {
+ *color = tr;
+ return 3;
+ } else if (tl == bl && tl == br) {
+ *color = tl;
+ return 3;
+ } else if (tl == tr && tl == br) {
+ *color = tl;
+ return 3;
+ } else if (tl == tr && tl == bl) {
+ *color = tl;
+ return 3;
+ } else if (tl == tr || tl == bl || tl == br) {
+ *color = tl;
+ return 2;
+ } else if (tr == bl) {
+ *color = tr;
+ return 2;
+ } else if (br == bl) {
+ *color = bl;
+ return 2;
+ } else {
+ register int r,b,g,a;
+
+ r = (int)(0.5f + (gdImageRed(im, tl) + gdImageRed(im, tr) + gdImageRed(im, bl) + gdImageRed(im, br)) / 4);
+ g = (int)(0.5f + (gdImageGreen(im, tl) + gdImageGreen(im, tr) + gdImageGreen(im, bl) + gdImageGreen(im, br)) / 4);
+ b = (int)(0.5f + (gdImageBlue(im, tl) + gdImageBlue(im, tr) + gdImageBlue(im, bl) + gdImageBlue(im, br)) / 4);
+ a = (int)(0.5f + (gdImageAlpha(im, tl) + gdImageAlpha(im, tr) + gdImageAlpha(im, bl) + gdImageAlpha(im, br)) / 4);
+ *color = gdImageColorClosestAlpha(im, r, g, b, a);
+ return 0;
+ }
+}
+
+static int gdColorMatch(gdImagePtr im, int col1, int col2, float threshold)
+{
+ const int dr = gdImageRed(im, col1) - gdImageRed(im, col2);
+ const int dg = gdImageGreen(im, col1) - gdImageGreen(im, col2);
+ const int db = gdImageBlue(im, col1) - gdImageBlue(im, col2);
+ const int da = gdImageAlpha(im, col1) - gdImageAlpha(im, col2);
+ const double dist = sqrt(dr * dr + dg * dg + db * db + da * da);
+ const double dist_perc = sqrt(dist / (255^2 + 255^2 + 255^2));
+ return (dist_perc <= threshold);
+ //return (100.0 * dist / 195075) < threshold;
+}
+
+/*
+ * To be implemented when we have more image formats.
+ * Buffer like gray8 gray16 or rgb8 will require some tweak
+ * and can be done in this function (called from the autocrop
+ * function. (Pierre)
+ */
+#if 0
+static int colors_equal (const int col1, const in col2)
+{
+
+}
+#endif
diff --git a/ext/gd/libgd/gd_png.c b/ext/gd/libgd/gd_png.c
index bdbb7ee7d..a012cc63b 100644
--- a/ext/gd/libgd/gd_png.c
+++ b/ext/gd/libgd/gd_png.c
@@ -134,6 +134,7 @@ gdImagePtr gdImageCreateFromPngCtx (gdIOCtx * infile)
volatile int transparent = -1;
volatile int palette_allocated = FALSE;
+
/* Make sure the signature can't match by dumb luck -- TBB */
/* GRR: isn't sizeof(infile) equal to the size of the pointer? */
memset (sig, 0, sizeof(sig));
@@ -345,6 +346,7 @@ gdImagePtr gdImageCreateFromPngCtx (gdIOCtx * infile)
open[i] = 1;
}
}
+
/* 2.0.12: Slaven Rezic: palette images are not the only images
* with a simple transparent color setting.
*/
diff --git a/ext/gd/libgd/gd_transform.c b/ext/gd/libgd/gd_transform.c
new file mode 100644
index 000000000..9051525ee
--- /dev/null
+++ b/ext/gd/libgd/gd_transform.c
@@ -0,0 +1,73 @@
+#include "gd.h"
+
+void gdImageFlipVertical(gdImagePtr im)
+{
+ register int x, y;
+
+ if (im->trueColor) {
+ for (y = 0; y < im->sy / 2; y++) {
+ int *row_dst = im->tpixels[y];
+ int *row_src = im->tpixels[im->sy - 1 - y];
+ for (x = 0; x < im->sx; x++) {
+ register int p;
+ p = row_dst[x];
+ row_dst[x] = im->tpixels[im->sy - 1 - y][x];
+ row_src[x] = p;
+ }
+ }
+ } else {
+ unsigned char p;
+ for (y = 0; y < im->sy / 2; y++) {
+ for (x = 0; x < im->sx; x++) {
+ p = im->pixels[y][x];
+ im->pixels[y][x] = im->pixels[im->sy - 1 - y][x];
+ im->pixels[im->sy - 1 - y][x] = p;
+ }
+ }
+ }
+ return;
+}
+
+void gdImageFlipHorizontal(gdImagePtr im)
+{
+
+ int x, y;
+
+ if (im->trueColor) {
+ int *px1, *px2, tmp;
+
+ for (y = 0; y < im->sy; y++) {
+ px1 = im->tpixels[y];
+ px2 = im->tpixels[y] + im->sx - 1;
+ for (x = 0; x < (im->sx >> 1); x++) {
+ tmp = *px1;
+ *px1 = *px2;
+ *px2 = tmp;
+ px1++;
+ px2--;
+ }
+ }
+ } else {
+ unsigned char *px1, *px2, tmp;
+
+ for (y = 0; y < im->sy; y++) {
+ px1 = im->pixels[y];
+ px2 = im->pixels[y] + im->sx - 1;
+ for (x = 0; x < (im->sx >> 1); x++) {
+ tmp = *px1;
+ *px1 = *px2;
+ *px2 = tmp;
+ px1++;
+ px2--;
+ }
+ }
+ }
+}
+
+void gdImageFlipBoth(gdImagePtr im)
+{
+ gdImageFlipVertical(im);
+ gdImageFlipHorizontal(im);
+}
+
+
diff --git a/ext/gd/php_gd.h b/ext/gd/php_gd.h
index 45193768e..90ebd6526 100644
--- a/ext/gd/php_gd.h
+++ b/ext/gd/php_gd.h
@@ -124,6 +124,9 @@ PHP_FUNCTION(imagerotate);
#ifdef HAVE_GD_BUNDLED
PHP_FUNCTION(imageantialias);
+PHP_FUNCTION(imageflip);
+PHP_FUNCTION(imagecrop);
+PHP_FUNCTION(imagecropauto);
#endif
PHP_FUNCTION(imagesetthickness);
diff --git a/ext/gd/tests/imagecrop_auto.phpt b/ext/gd/tests/imagecrop_auto.phpt
new file mode 100644
index 000000000..c2b5177fb
--- /dev/null
+++ b/ext/gd/tests/imagecrop_auto.phpt
@@ -0,0 +1,82 @@
+--TEST--
+Testing imagecropauto()
+--SKIPIF--
+<?php
+if ( ! extension_loaded('gd') || !function_exists('imagecrop')) die( 'skip GD imagecropauto not present; skipping test' );
+?>
+--FILE--
+<?php
+
+echo "TC IMG_CROP_DEFAULT\n";
+$im = imagecreatetruecolor(99, 99);
+imagefilledrectangle($im, 20, 20, 30, 30, 0xff);
+$im_crop = imagecropauto($im, IMG_CROP_DEFAULT);
+var_dump(imagesx($im_crop));
+var_dump(imagesy($im_crop));
+
+echo "Palette IMG_CROP_DEFAULT\n";
+$im = imagecreate(99, 99);
+imagefilledrectangle($im, 20, 20, 30, 30, 0xff);
+$im_crop = imagecropauto($im, IMG_CROP_DEFAULT);
+var_dump(imagesx($im_crop));
+var_dump(imagesy($im_crop));
+
+echo "TC IMG_CROP_SIDES\n";
+$im = imagecreatetruecolor(99, 99);
+imagefilledrectangle($im, 20, 20, 30, 30, 0xff);
+$im_crop = imagecropauto($im, IMG_CROP_SIDES);
+var_dump(imagesx($im_crop));
+var_dump(imagesy($im_crop));
+
+echo "Palette IMG_CROP_SIDES\n";
+$im = imagecreate(99, 99);
+imagefilledrectangle($im, 20, 20, 30, 30, 0xff);
+$im_crop = imagecropauto($im, IMG_CROP_SIDES);
+var_dump(imagesx($im_crop));
+var_dump(imagesy($im_crop));
+
+echo "TC IMG_CROP_BLACK\n";
+$im = imagecreatetruecolor(50, 50);
+imagefilledrectangle($im, 20, 20, 30, 30, 0xff);
+$im_crop = imagecropauto($im, IMG_CROP_BLACK);
+var_dump(imagesx($im_crop));
+var_dump(imagesy($im_crop));
+
+echo "Palette IMG_CROP_BLACK\n";
+$im = imagecreate(50, 50);
+$bgd = imagecolorallocate($im, 0, 0, 0);
+$b = imagecolorallocate($im, 0, 0, 255);
+imagefilledrectangle($im, 20, 20, 30, 30, 0xff);
+$im_crop = imagecropauto($im, IMG_CROP_BLACK);
+var_dump(imagesx($im_crop));
+var_dump(imagesy($im_crop));
+
+echo "IMG_CROP_THRESHOLD\n";
+$im = imagecreatefrompng(__DIR__ . "/logo_noise.png");
+$im_crop = imagecropauto($im, IMG_CROP_THRESHOLD, 0.1, 0x0);
+imagepng($im_crop, __DIR__ . "/crop_threshold.png");
+var_dump(imagesx($im_crop));
+var_dump(imagesy($im_crop));
+?>
+--EXPECT--
+TC IMG_CROP_DEFAULT
+int(11)
+int(11)
+Palette IMG_CROP_DEFAULT
+int(11)
+int(11)
+TC IMG_CROP_SIDES
+int(11)
+int(11)
+Palette IMG_CROP_SIDES
+int(11)
+int(11)
+TC IMG_CROP_BLACK
+int(11)
+int(11)
+Palette IMG_CROP_BLACK
+int(11)
+int(11)
+IMG_CROP_THRESHOLD
+int(240)
+int(134)
diff --git a/ext/gd/tests/imageflip.phpt b/ext/gd/tests/imageflip.phpt
new file mode 100644
index 000000000..a326e0a2b
--- /dev/null
+++ b/ext/gd/tests/imageflip.phpt
@@ -0,0 +1,30 @@
+--TEST--
+Testing imageflip() of GD library
+--SKIPIF--
+<?php
+if ( ! extension_loaded('gd') || !function_exists('imageflip')) die( 'skip GD not present; skipping test' );
+?>
+--FILE--
+<?php
+
+$im = imagecreatetruecolor( 99, 99 );
+
+imagesetpixel($im, 0, 0, 0xFF);
+imagesetpixel($im, 0, 98, 0x00FF00);
+imagesetpixel($im, 98, 0, 0xFF0000);
+imagesetpixel($im, 98, 98, 0x0000FF);
+
+imageflip($im, IMG_FLIP_HORIZONTAL);
+imageflip($im, IMG_FLIP_VERTICAL);
+imageflip($im, IMG_FLIP_BOTH);
+
+var_dump(dechex(imagecolorat($im, 0, 0)));
+var_dump(dechex(imagecolorat($im, 0, 98)));
+var_dump(dechex(imagecolorat($im, 98, 0)));
+var_dump(dechex(imagecolorat($im, 98, 98)));
+?>
+--EXPECT--
+string(2) "ff"
+string(4) "ff00"
+string(6) "ff0000"
+string(2) "ff" \ No newline at end of file
diff --git a/ext/gd/tests/logo_noise.png b/ext/gd/tests/logo_noise.png
new file mode 100644
index 000000000..c9bde52c3
--- /dev/null
+++ b/ext/gd/tests/logo_noise.png
Binary files differ