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