diff options
Diffstat (limited to 'ext/gd')
-rw-r--r-- | ext/gd/config.m4 | 3 | ||||
-rw-r--r-- | ext/gd/config.w32 | 5 | ||||
-rw-r--r-- | ext/gd/gd.c | 179 | ||||
-rw-r--r-- | ext/gd/gdcache.c | 5 | ||||
-rw-r--r-- | ext/gd/libgd/gd.h | 59 | ||||
-rw-r--r-- | ext/gd/libgd/gd_crop.c | 356 | ||||
-rw-r--r-- | ext/gd/libgd/gd_png.c | 2 | ||||
-rw-r--r-- | ext/gd/libgd/gd_transform.c | 73 | ||||
-rw-r--r-- | ext/gd/php_gd.h | 3 | ||||
-rw-r--r-- | ext/gd/tests/imagecrop_auto.phpt | 82 | ||||
-rw-r--r-- | ext/gd/tests/imageflip.phpt | 30 | ||||
-rw-r--r-- | ext/gd/tests/logo_noise.png | bin | 0 -> 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 Binary files differnew file mode 100644 index 000000000..c9bde52c3 --- /dev/null +++ b/ext/gd/tests/logo_noise.png |