Index: ext/gd/libgd/gd_filter.c =================================================================== --- ext/gd/libgd/gd_filter.c (revision 284704) +++ ext/gd/libgd/gd_filter.c (working copy) @@ -458,4 +458,127 @@ return gdImageConvolution(im, filter, weight+8, 0); } + +int gdImageConvolution2(gdImagePtr src, float filter_x[3][3], float filter_y[3][3], float filter_div, float offset, const int negate) +{ + int x, y, i, j, new_a; + float sxr, sxg, sxb; + float syr, syg, syb; + float new_r, new_g, new_b; + int rgba; + gdImagePtr srcback; + typedef int (*FuncPtr)(gdImagePtr, int, int); + FuncPtr f; + + if (src==NULL) { + return 0; + } + + /* We need the orinal image with each safe neoghb. pixel */ + srcback = gdImageCreateTrueColor (src->sx, src->sy); + if (srcback==NULL) { + return 0; + } + + gdImageSaveAlpha(srcback, 1); + gdImageCopy(srcback, src, 0, 0, 0, 0, src->sx, src->sy); + + f = GET_PIXEL_FUNCTION(src); + + for ( y=0; ysy; y++) { + for(x=0; xsx; x++) { + sxr = sxg = sxb = 0; + syr = syg = syb = 0; + + rgba = f(srcback, x, y); + new_a = gdImageAlpha(srcback, rgba); + + for (j=0; j<3; j++) { + int yv = MIN(MAX(y - 1 + j, 0), src->sy - 1); + for (i=0; i<3; i++) { + rgba = f(srcback, MIN(MAX(x - 1 + i, 0), src->sx - 1), yv); + + sxr += (float)gdImageRed(srcback, rgba) * filter_x[j][i]; + sxg += (float)gdImageGreen(srcback, rgba) * filter_x[j][i]; + sxb += (float)gdImageBlue(srcback, rgba) * filter_x[j][i]; + + syr += (float)gdImageRed(srcback, rgba) * filter_y[j][i]; + syg += (float)gdImageGreen(srcback, rgba) * filter_y[j][i]; + syb += (float)gdImageBlue(srcback, rgba) * filter_y[j][i]; + } + } + + sxr = (sxr / filter_div) + offset; + sxg = (sxg / filter_div) + offset; + sxb = (sxb / filter_div) + offset; + + syr = (syr / filter_div) + offset; + syg = (syg / filter_div) + offset; + syb = (syb / filter_div) + offset; + + new_r = sqrt((sxr * sxr) + (syr * syr)); + new_g = sqrt((sxg * sxg) + (syg * syg)); + new_b = sqrt((sxb * sxb) + (syb * syb)); + + new_r = (new_r > 255.0f)? 255.0f : ((new_r < 0.0f)? 0.0f:new_r); + new_g = (new_g > 255.0f)? 255.0f : ((new_g < 0.0f)? 0.0f:new_g); + new_b = (new_b > 255.0f)? 255.0f : ((new_b < 0.0f)? 0.0f:new_b); + + if (negate > 0) { + new_r = 255 - new_r; + new_g = 255 - new_g; + new_b = 255 - new_b; + } + + rgba = gdImageColorAllocateAlpha(src, (int)new_r, (int)new_g, (int)new_b, new_a); + if (rgba == -1) { + rgba = gdImageColorClosestAlpha(src, (int)new_r, (int)new_g, (int)new_b, new_a); + } + gdImageSetPixel (src, x, y, rgba); + } + } + gdImageDestroy(srcback); + return 1; +} + +int gdImageEdgeDetectPrewitt(gdImagePtr src, const int negate) +{ + float filter_x[3][3] = {{-1.0, 0.0, 1.0}, + {-1.0, 0.0, 1.0}, + {-1.0, 0.0, 1.0}}; + + float filter_y[3][3] = {{-1.0, -1.0, -1.0}, + { 0.0, 0.0, 0.0}, + { 1.0, 1.0, 1.0}}; + + return gdImageConvolution2(src, filter_x, filter_y, 1, 0, negate); +} + +int gdImageEdgeDetectScharr(gdImagePtr src, const int negate) +{ + float filter_x[3][3] = {{ 3.0, 0.0, -3.0}, + {10.0, 0.0,-10.0}, + { 3.0, 0.0, -3.0}}; + + float filter_y[3][3] = {{ 3.0, 10.0, 3.0}, + { 0.0, 0.0, 0.0}, + {-3.0,-10.0, -3.0}}; + + return gdImageConvolution2(src, filter_x, filter_y, 1, 0, negate); +} + +int gdImageEdgeDetectSobel(gdImagePtr src, const int negate) +{ + float filter_x[3][3] = {{ 1.0, 0.0, -1.0}, + { 2.0, 0.0, -2.0}, + { 1.0, 0.0, -1.0}}; + + float filter_y[3][3] = {{ 1.0, 2.0, 1.0}, + { 0.0, 0.0, 0.0}, + {-1.0, -2.0, -1.0}}; + + return gdImageConvolution2(src, filter_x, filter_y, 1, 0, negate); +} + /* End filters function */ + Index: ext/gd/libgd/gd_compat.h =================================================================== --- ext/gd/libgd/gd_compat.h (revision 284704) +++ ext/gd/libgd/gd_compat.h (working copy) @@ -42,6 +42,11 @@ int gdImageEmboss(gdImagePtr im); int gdImageMeanRemoval(gdImagePtr im); int gdImageSmooth(gdImagePtr im, float weight); +/* Image convolution by two 3x3 custom matrices */ +int gdImageConvolution2(gdImagePtr src, float ft_x[3][3], float ft_y[3][3], float filter_div, float offset, const int negate); +int gdImageEdgeDetectPrewitt(gdImagePtr src, const int negate); +int gdImageEdgeDetectScharr(gdImagePtr src, const int negate); +int gdImageEdgeDetectSobel(gdImagePtr src, const int negate); enum gdPixelateMode { GD_PIXELATE_UPPERLEFT, GD_PIXELATE_AVERAGE Index: ext/gd/libgd/gd.h =================================================================== --- ext/gd/libgd/gd.h (revision 284704) +++ ext/gd/libgd/gd.h (working copy) @@ -670,6 +670,15 @@ int gdImageSmooth(gdImagePtr im, float weight); +/* Image convolution by two 3x3 custom matrices */ +int gdImageConvolution2(gdImagePtr src, float ft_x[3][3], float ft_y[3][3], float filter_div, float offset, const int negate); + +int gdImageEdgeDetectPrewitt(gdImagePtr src, const int negate); + +int gdImageEdgeDetectScharr(gdImagePtr src, const int negate); + +int gdImageEdgeDetectSobel(gdImagePtr src, const int negate); + /* Image comparison definitions */ int gdImageCompare(gdImagePtr im1, gdImagePtr im2); Index: ext/gd/tests/imagefilter.phpt =================================================================== --- ext/gd/tests/imagefilter.phpt (revision 284704) +++ ext/gd/tests/imagefilter.phpt (working copy) @@ -82,6 +82,30 @@ } else { echo "IMG_FILTER_PIXELATE failed\n"; } + + if (imagefilter($im, IMG_FILTER_PREWITT, false)) { + imagepng($im, $SAVE_DIR . "/IMG_FILTER_PREWITT.png"); + echo "IMG_FILTER_PREWITT success\n"; + unlink($SAVE_DIR . "/IMG_FILTER_PREWITT.png"); + } else { + echo "IMG_FILTER_PREWITT failed\n"; + } + + if (imagefilter($im, IMG_FILTER_SCHARR, false)) { + imagepng($im, $SAVE_DIR . "/IMG_FILTER_SCHARR.png"); + echo "IMG_FILTER_SCHARR success\n"; + unlink($SAVE_DIR . "/IMG_FILTER_SCHARR.png"); + } else { + echo "IMG_FILTER_SCHARR failed\n"; + } + + if (imagefilter($im, IMG_FILTER_SOBEL, false)) { + imagepng($im, $SAVE_DIR . "/IMG_FILTER_SOBEL.png"); + echo "IMG_FILTER_SOBEL success\n"; + unlink($SAVE_DIR . "/IMG_FILTER_SOBEL.png"); + } else { + echo "IMG_FILTER_SOBEL failed\n"; + } ?> --EXPECT-- IMG_FILTER_NEGATE success @@ -96,3 +120,6 @@ IMG_FILTER_CONTRAST success IMG_FILTER_BRIGHTNESS success IMG_FILTER_PIXELATE success +IMG_FILTER_PREWITT sucess +IMG_FILTER_SCHARR sucess +IMG_FILTER_SOBEL sucess Index: ext/gd/php_gd.h =================================================================== --- ext/gd/php_gd.h (revision 284704) +++ ext/gd/php_gd.h (working copy) @@ -197,6 +197,7 @@ PHP_FUNCTION(imagefilter); PHP_FUNCTION(imageconvolution); +PHP_FUNCTION(imageconvolution2); PHP_GD_API int phpi_get_le_gd(void); Index: ext/gd/gd.c =================================================================== --- ext/gd/gd.c (revision 284704) +++ ext/gd/gd.c (working copy) @@ -116,7 +116,10 @@ #define IMAGE_FILTER_MEAN_REMOVAL 9 #define IMAGE_FILTER_SMOOTH 10 #define IMAGE_FILTER_PIXELATE 11 -#define IMAGE_FILTER_MAX 11 +#define IMAGE_FILTER_PREWITT 12 +#define IMAGE_FILTER_SCHARR 13 +#define IMAGE_FILTER_SOBEL 14 +#define IMAGE_FILTER_MAX 14 #define IMAGE_FILTER_MAX_ARGS 6 static void php_image_filter_negate(INTERNAL_FUNCTION_PARAMETERS); static void php_image_filter_grayscale(INTERNAL_FUNCTION_PARAMETERS); @@ -130,6 +133,9 @@ static void php_image_filter_mean_removal(INTERNAL_FUNCTION_PARAMETERS); static void php_image_filter_smooth(INTERNAL_FUNCTION_PARAMETERS); static void php_image_filter_pixelate(INTERNAL_FUNCTION_PARAMETERS); +static void php_image_filter_edgedetect_prewitt(INTERNAL_FUNCTION_PARAMETERS); +static void php_image_filter_edgedetect_scharr(INTERNAL_FUNCTION_PARAMETERS); +static void php_image_filter_edgedetect_sobel(INTERNAL_FUNCTION_PARAMETERS); /* End Section filters declarations */ static gdImagePtr _php_image_create_from_string (zval **Data, char *tn, gdImagePtr (*ioctx_func_p)() TSRMLS_DC); @@ -822,6 +828,15 @@ ZEND_ARG_INFO(0, offset) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO(arginfo_imageconvolution2, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, matrix_x_3x3) + ZEND_ARG_INFO(0, matrix_y_3x3) + ZEND_ARG_INFO(0, div) + ZEND_ARG_INFO(0, offset) + ZEND_ARG_INFO(0, negate) +ZEND_END_ARG_INFO() + #ifdef HAVE_GD_BUNDLED ZEND_BEGIN_ARG_INFO(arginfo_imageantialias, 0) ZEND_ARG_INFO(0, im) @@ -986,6 +1001,7 @@ /* gd filters */ PHP_FE(imagefilter, arginfo_imagefilter) PHP_FE(imageconvolution, arginfo_imageconvolution) + PHP_FE(imageconvolution2, arginfo_imageconvolution2) {NULL, NULL, NULL} }; @@ -1132,6 +1148,9 @@ REGISTER_LONG_CONSTANT("IMG_FILTER_MEAN_REMOVAL", IMAGE_FILTER_MEAN_REMOVAL, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("IMG_FILTER_SMOOTH", IMAGE_FILTER_SMOOTH, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("IMG_FILTER_PIXELATE", IMAGE_FILTER_PIXELATE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_FILTER_PREWITT", IMAGE_FILTER_PREWITT, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_FILTER_SCHARR", IMAGE_FILTER_SCHARR, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_FILTER_SOBEL", IMAGE_FILTER_SOBEL, CONST_CS | CONST_PERSISTENT); /* End Section Filters */ #ifdef GD_VERSION_STRING @@ -4628,6 +4647,78 @@ RETURN_FALSE; } +static void php_image_filter_edgedetect_prewitt(INTERNAL_FUNCTION_PARAMETERS) +{ + zval *SIM; + gdImagePtr im_src; + long tmp; + zend_bool negate = 1; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl|b", &SIM, &tmp, &negate) == FAILURE) { + RETURN_FALSE; + } + + ZEND_FETCH_RESOURCE(im_src, gdImagePtr, &SIM, -1, "Image", le_gd); + + if (im_src == NULL) { + RETURN_FALSE; + } + + if (gdImageEdgeDetectPrewitt(im_src, negate) == 1) { + RETURN_TRUE; + } + + RETURN_FALSE; +} + +static void php_image_filter_edgedetect_scharr(INTERNAL_FUNCTION_PARAMETERS) +{ + zval *SIM; + gdImagePtr im_src; + long tmp; + zend_bool negate = 1; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl|b", &SIM, &tmp, &negate) == FAILURE) { + RETURN_FALSE; + } + + ZEND_FETCH_RESOURCE(im_src, gdImagePtr, &SIM, -1, "Image", le_gd); + + if (im_src == NULL) { + RETURN_FALSE; + } + + if (gdImageEdgeDetectScharr(im_src, negate) == 1) { + RETURN_TRUE; + } + + RETURN_FALSE; +} + +static void php_image_filter_edgedetect_sobel(INTERNAL_FUNCTION_PARAMETERS) +{ + zval *SIM; + gdImagePtr im_src; + long tmp; + zend_bool negate = 1; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl|b", &SIM, &tmp, &negate) == FAILURE) { + RETURN_FALSE; + } + + ZEND_FETCH_RESOURCE(im_src, gdImagePtr, &SIM, -1, "Image", le_gd); + + if (im_src == NULL) { + RETURN_FALSE; + } + + if (gdImageEdgeDetectSobel(im_src, negate) == 1) { + RETURN_TRUE; + } + + RETURN_FALSE; +} + /* {{{ proto bool imagefilter(resource src_im, int filtertype, [args] ) U Applies Filter an image using a custom angle */ PHP_FUNCTION(imagefilter) @@ -4649,7 +4740,10 @@ php_image_filter_selective_blur, php_image_filter_mean_removal, php_image_filter_smooth, - php_image_filter_pixelate + php_image_filter_pixelate, + php_image_filter_edgedetect_prewitt, + php_image_filter_edgedetect_scharr, + php_image_filter_edgedetect_sobel }; if (ZEND_NUM_ARGS() < 2 || ZEND_NUM_ARGS() > IMAGE_FILTER_MAX_ARGS) { @@ -4710,6 +4804,78 @@ RETURN_BOOL(gdImageConvolution(im_src, matrix, div, offset)); } /* }}} */ + +/* {{{ proto resource imageconvolution2(resource src_im, array matrix_x_3x3, array matrix_y_3x3, double div, double offset, bool negate) + Apply two 3x3 convolution matrices, using coefficient div and offset */ +PHP_FUNCTION(imageconvolution2) +{ + zval *SIM, *hash_matrix_x, *hash_matrix_y; + zval **var = NULL, **var2 = NULL; + gdImagePtr im_src = NULL; + double div, offset; + int mx_nelem, my_nelem, i, j, res; + zend_bool negate = 0; + float matrix_x[3][3] = {{0,0,0}, {0,0,0}, {0,0,0}}; + float matrix_y[3][3] = {{0,0,0}, {0,0,0}, {0,0,0}}; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "raadd|b", &SIM, &hash_matrix_x, &hash_matrix_y, &div, &offset, &negate) == FAILURE) { + RETURN_FALSE; + } + + ZEND_FETCH_RESOURCE(im_src, gdImagePtr, &SIM, -1, "Image", le_gd); + + mx_nelem = zend_hash_num_elements(Z_ARRVAL_P(hash_matrix_x)); + my_nelem = zend_hash_num_elements(Z_ARRVAL_P(hash_matrix_y)); + if (mx_nelem != 3 || my_nelem != 3) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Each matrix must be a 3x3 array"); + RETURN_FALSE; + } + + for (i=0; i<3; i++) { + if (zend_hash_index_find(Z_ARRVAL_P(hash_matrix_x), (i), (void **) &var) == SUCCESS && Z_TYPE_PP(var) == IS_ARRAY) { + if (Z_TYPE_PP(var) != IS_ARRAY || zend_hash_num_elements(Z_ARRVAL_PP(var)) != 3 ) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "x matrix must be a 3x3 array"); + RETURN_FALSE; + } + + for (j=0; j<3; j++) { + if (zend_hash_index_find(Z_ARRVAL_PP(var), (j), (void **) &var2) == SUCCESS) { + SEPARATE_ZVAL(var2); + convert_to_double(*var2); + matrix_x[i][j] = Z_DVAL_PP(var2); + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "x matrix must be 3x3"); + RETURN_FALSE; + } + } + } + if (zend_hash_index_find(Z_ARRVAL_P(hash_matrix_y), (i), (void **) &var) == SUCCESS && Z_TYPE_PP(var) == IS_ARRAY) { + if (Z_TYPE_PP(var) != IS_ARRAY || zend_hash_num_elements(Z_ARRVAL_PP(var)) != 3 ) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "y matrix must be a 3x3 array"); + RETURN_FALSE; + } + + for (j=0; j<3; j++) { + if (zend_hash_index_find(Z_ARRVAL_PP(var), (j), (void **) &var2) == SUCCESS) { + SEPARATE_ZVAL(var2); + convert_to_double(*var2); + matrix_y[i][j] = Z_DVAL_PP(var2); + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "y matrix must be 3x3"); + RETURN_FALSE; + } + } + } + } + res = gdImageConvolution2(im_src, matrix_x, matrix_y, div, offset, negate); + + if (res) { + RETURN_TRUE; + } else { + RETURN_FALSE; + } +} +/* }}} */ /* End section: Filters */ #ifdef HAVE_GD_BUNDLED Index: main/php_compat.h =================================================================== --- main/php_compat.h (revision 284704) +++ main/php_compat.h (working copy) @@ -220,6 +220,7 @@ #define gdImageCompare php_gd_gdImageCompare #define gdImageContrast php_gd_gdImageContrast #define gdImageConvolution php_gd_gdImageConvolution +#define gdImageConvolution2 php_gd_gdImageConvolution2 #define gdImageCopy php_gd_gdImageCopy #define gdImageCopyMerge php_gd_gdImageCopyMerge #define gdImageCopyMergeGray php_gd_gdImageCopyMergeGray @@ -253,7 +254,10 @@ #define gdImageCreateTrueColor php_gd_gdImageCreateTrueColor #define gdImageDashedLine php_gd_gdImageDashedLine #define gdImageDestroy php_gd_gdImageDestroy +#define gdImageEdgeDetectPrewitt php_gd_gdImageEdgeDetectPrewitt #define gdImageEdgeDetectQuick php_gd_gdImageEdgeDetectQuick +#define gdImageEdgeDetectScharr php_gd_gdImageEdgeDetectScharr +#define gdImageEdgeDetectSobel php_gd_gdImageEdgeDetectSobel #define gdImageEllipse php_gd_gdImageEllipse #define gdImageEmboss php_gd_gdImageEmboss #define gdImageFill php_gd_gdImageFill