Commit:    a991360344ed5bca7c20f74a10891d0fc52f0c9f
Author:    Pierre Joye <pierre....@gmail.com>         Thu, 28 Feb 2013 17:24:23 
+0100
Parents:   82765a07800fe39f662bb45fd18199d007e0dc23
Branches:  master

Link:       
http://git.php.net/?p=php-src.git;a=commitdiff;h=a991360344ed5bca7c20f74a10891d0fc52f0c9f

Log:
- add image crop support

Changed paths:
  M  ext/gd/config.m4
  M  ext/gd/config.w32
  M  ext/gd/gd.c
  M  ext/gd/gdcache.c
  M  ext/gd/libgd/gd.h
  A  ext/gd/libgd/gd_crop.c
  M  ext/gd/libgd/gd_png.c
  M  ext/gd/php_gd.h

diff --git a/ext/gd/config.m4 b/ext/gd/config.m4
index 00e7c68..2f71705 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_transform.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 ed3eab6..b25a0e2 100644
--- a/ext/gd/config.w32
+++ b/ext/gd/config.w32
@@ -47,7 +47,8 @@ if (PHP_GD != "no") {
                        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_transform.c \
-                       gd_filter.c gd_pixelate.c gd_arc.c gd_rotate.c 
gd_color.c webpimg.c gd_webp.c", "gd");
+                       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 2bd0a2d..83733d1 100644
--- a/ext/gd/gd.c
+++ b/ext/gd/gd.c
@@ -890,6 +890,11 @@ 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_imagecropauto, 0)
+       ZEND_ARG_INFO(0, im)
+       ZEND_ARG_INFO(0, mode)
+ZEND_END_ARG_INFO()
 #endif
 
 /* }}} */
@@ -950,6 +955,7 @@ const zend_function_entry gd_functions[] = {
 #ifdef HAVE_GD_BUNDLED
        PHP_FE(imageantialias,                                                  
arginfo_imageantialias)
        PHP_FE(imageflip,                                                       
        arginfo_imageflip)
+       PHP_FE(imagecropauto,                                                   
arginfo_imagecropauto)
 #endif
 
 #if HAVE_GD_IMAGESETTILE
@@ -1204,6 +1210,12 @@ PHP_MINIT_FUNCTION(gd)
        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);
 #else
        REGISTER_LONG_CONSTANT("GD_BUNDLED", 0, CONST_CS | CONST_PERSISTENT);
 #endif
@@ -5125,6 +5137,44 @@ PHP_FUNCTION(imageflip)
        RETURN_TRUE;
 }
 /* }}} */
+
+
+/* {{{ proto void imageflip(resource im, int mode)
+   Flip an image (in place) horizontally, vertically or both directions. */
+PHP_FUNCTION(imagecropauto)
+{
+       zval *IM;
+       long mode = -1;
+       gdImagePtr im;
+       gdImagePtr im_crop;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|l", &IM, &mode) 
== 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;
+               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
 
 
diff --git a/ext/gd/gdcache.c b/ext/gd/gdcache.c
index 2349e38..231a1f7 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 c4a47af..adef026 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(). */
@@ -690,6 +715,31 @@ void gdImageFlipBoth(gdImagePtr im);
 #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
+};
+
+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 0000000..d5fd762
--- /dev/null
+++ b/ext/gd/libgd/gd_crop.c
@@ -0,0 +1,355 @@
+/**
+ * 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>
+
+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;
+       }
+#ifdef 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;
+               unsigned int dst_y = 0;
+               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);
+}
+
+/**
+ * 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 int dist = dr * dr + dg * dg + db * db + da * da;
+
+       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 bdbb7ee..a012cc6 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/php_gd.h b/ext/gd/php_gd.h
index a50a631..537efc4 100644
--- a/ext/gd/php_gd.h
+++ b/ext/gd/php_gd.h
@@ -125,6 +125,7 @@ PHP_FUNCTION(imagerotate);
 #ifdef HAVE_GD_BUNDLED
 PHP_FUNCTION(imageantialias);
 PHP_FUNCTION(imageflip);
+PHP_FUNCTION(imagecropauto);
 #endif
 
 PHP_FUNCTION(imagesetthickness);
-- 
PHP CVS Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to