diff --git a/ext/gd/gd.c b/ext/gd/gd.c index 8fa0adec921a2..56084300c8d3e 100644 --- a/ext/gd/gd.c +++ b/ext/gd/gd.c @@ -704,7 +704,17 @@ ZEND_BEGIN_ARG_INFO(arginfo_imagecopymerge, 0) ZEND_ARG_INFO(0, src_h) ZEND_ARG_INFO(0, pct) ZEND_END_ARG_INFO() - +ZEND_BEGIN_ARG_INFO(arginfo_imagecopymergealpha, 0) + ZEND_ARG_INFO(0, src_im) + ZEND_ARG_INFO(0, dst_im) + ZEND_ARG_INFO(0, dst_x) + ZEND_ARG_INFO(0, dst_y) + ZEND_ARG_INFO(0, src_x) + ZEND_ARG_INFO(0, src_y) + ZEND_ARG_INFO(0, src_w) + ZEND_ARG_INFO(0, src_h) + ZEND_ARG_INFO(0, pct) +ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arginfo_imagecopymergegray, 0) ZEND_ARG_INFO(0, src_im) ZEND_ARG_INFO(0, dst_im) @@ -917,6 +927,7 @@ const zend_function_entry gd_functions[] = { PHP_FE(imagecopy, arginfo_imagecopy) #if HAVE_LIBGD15 PHP_FE(imagecopymerge, arginfo_imagecopymerge) + PHP_FE(imagecopymergealpha, arginfo_imagecopymergealpha) PHP_FE(imagecopymergegray, arginfo_imagecopymergegray) #endif PHP_FE(imagecopyresized, arginfo_imagecopyresized) @@ -3779,6 +3790,35 @@ PHP_FUNCTION(imagecopymerge) } /* }}} */ +/* {{{ proto bool imagecopymergealpha(resource src_im, resource dst_im, int dst_x, int dst_y, int src_x, int src_y, int src_w, int src_h, int pct) + Merge one part of an image with another -while preserving Alpha channel*/ +PHP_FUNCTION(imagecopymergealpha) +{ + zval *SIM, *DIM; + long SX, SY, SW, SH, DX, DY, PCT; + gdImagePtr im_dst, im_src; + int srcH, srcW, srcY, srcX, dstY, dstX, pct; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rrlllllll", &DIM, &SIM, &DX, &DY, &SX, &SY, &SW, &SH, &PCT) == FAILURE) { + return; + } + + ZEND_FETCH_RESOURCE(im_src, gdImagePtr, &SIM, -1, "Image", le_gd); + ZEND_FETCH_RESOURCE(im_dst, gdImagePtr, &DIM, -1, "Image", le_gd); + + srcX = SX; + srcY = SY; + srcH = SH; + srcW = SW; + dstX = DX; + dstY = DY; + pct = PCT; + + gdImageCopyMergeAlpha(im_dst, im_src, dstX, dstY, srcX, srcY, srcW, srcH, pct); + RETURN_TRUE; +} +/* }}} */ + /* {{{ proto bool imagecopymergegray(resource src_im, resource dst_im, int dst_x, int dst_y, int src_x, int src_y, int src_w, int src_h, int pct) Merge one part of an image with another */ PHP_FUNCTION(imagecopymergegray) diff --git a/ext/gd/libgd/gd.c b/ext/gd/libgd/gd.c index fa75898ddb336..611997bfc3c36 100644 --- a/ext/gd/libgd/gd.c +++ b/ext/gd/libgd/gd.c @@ -2285,6 +2285,82 @@ void gdImageCopyMerge (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int s } } +/* This function is a real alpha channel operations, + but it has restrictions. */ +void gdImageCopyMergeAlpha (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h, int pct) +{ + int c; + int x, y; + int toy; + toy = dstY; + + if (pct == 100) { + /* no opacity adjustment required pass through to gdImageCopy() */ + gdImageCopy(dst, src, dstX, dstY, srcX, srcY, w, h); + return; + } + + if (pct == 0) { + /* 0% opacity? nothing needs to be done */ + return; + } + + if (src->trueColor && dst->trueColor) { + /* support for maintaining the alpha (transparency) of both source and + * destination images (assuming they are true colour) while opacity blending. + */ + int ca, cr, cg, cb, nc; + float na; + float ac; + + /* we need to loop through the src image to get the max transparency level */ + int mt = 0; + + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + c = gdImageGetTrueColorPixel (src, srcX + x, srcY + y); + ca = gdImageAlpha(src, c); + + mt = ca > mt ? ca : mt; + } + } + + if (mt < (gdAlphaMax / 2)) { + mt = gdAlphaMax - mt; + } + + /* alpha correction factor */ + ac = (float)mt / gdAlphaMax; + + /* loop through the image again and set/adjust alpha channel level */ + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + c = gdImageGetTrueColorPixel (src, srcX + x, srcY + y); + ca = gdImageAlpha(src, c); + cr = gdImageRed(src, c); + cg = gdImageGreen(src, c); + cb = gdImageBlue(src, c); + + na = (ca + gdAlphaMax - (gdAlphaMax * ((float)pct / 100))) * ac; + na = (na > gdAlphaMax) ? gdAlphaMax : ((na < gdAlphaOpaque) ? gdAlphaOpaque: na); + + nc = gdImageColorAllocateAlpha(dst, cr, cg, cb, (int)na); + if (nc == -1) { + gdImageColorClosestAlpha(dst, cr, cg, cb, (int)na); + } + + /* set pixel on destination image */ + gdImageSetPixel (dst, dstX + x, dstY + y, nc); + } + } + + return; + } + + /* falback */ + gdImageCopyMerge(dst, src, dstX, dstY, srcX, srcY, w, h, pct); +} + /* This function is a substitute for real alpha channel operations, so it doesn't pay attention to the alpha channel. */ void gdImageCopyMergeGray (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h, int pct) diff --git a/ext/gd/libgd/gdtest.c b/ext/gd/libgd/gdtest.c index 24b750386424d..90ae69f25c78b 100644 --- a/ext/gd/libgd/gdtest.c +++ b/ext/gd/libgd/gdtest.c @@ -250,6 +250,9 @@ main (int argc, char **argv) gdImageCopyMerge (im2, im3, 150, 200, 10, 10, 90, 50, 50); gdImageCopyMerge (im2, im3, 180, 70, 10, 10, 90, 50, 50); + gdImageCopyMergeAlpha (im2, im3, 150, 200, 10, 10, 90, 50, 50); + gdImageCopyMergeAlpha (im2, im3, 180, 70, 10, 10, 90, 50, 50); + gdImageCopyMergeGray (im2, im3, 250, 160, 10, 10, 90, 50, 50); gdImageCopyMergeGray (im2, im3, 80, 70, 10, 10, 90, 50, 50); diff --git a/ext/gd/php_gd.h b/ext/gd/php_gd.h index f8433d609f57e..f871f221e538e 100644 --- a/ext/gd/php_gd.h +++ b/ext/gd/php_gd.h @@ -96,6 +96,7 @@ PHP_FUNCTION(imagecolorsforindex); PHP_FUNCTION(imagecolortransparent); PHP_FUNCTION(imagecopy); PHP_FUNCTION(imagecopymerge); +PHP_FUNCTION(imagecopymergealpha); PHP_FUNCTION(imagecopyresized); PHP_FUNCTION(imagetypes); PHP_FUNCTION(imagecreate); diff --git a/ext/gd/tests/ff-logo-sm.png b/ext/gd/tests/ff-logo-sm.png new file mode 100644 index 0000000000000..769af13f8542e Binary files /dev/null and b/ext/gd/tests/ff-logo-sm.png differ diff --git a/ext/gd/tests/imagecopymergealpha_basic.phpt b/ext/gd/tests/imagecopymergealpha_basic.phpt new file mode 100644 index 0000000000000..686dee07bad85 --- /dev/null +++ b/ext/gd/tests/imagecopymergealpha_basic.phpt @@ -0,0 +1,40 @@ +--TEST-- +Testing imagecopymergealpha() of GD library +--CREDITS-- +Matt Clegg +Orignally by: redmonkey +--SKIPIF-- + +--FILE-- +1, + 'imagecopymergealpha'=>2 +); + +foreach ($images as $f=>$md5){ + +$bg = imagecreatefrompng('tux.png'); +$over = imagecreatefrompng('ff-logo-sm.png'); +imagealphablending($bg, true); +imagesavealpha($bg, true); +$f($bg, $over, 276, 300, 0, 0, 123, 119, 50); + +ob_start(); +imagepng($bg); +$dump = "$f: ".md5(ob_get_contents())."\n"; +ob_end_clean(); + +echo $dump; + +} + + +?> +--EXPECTF-- +imagecopymerge: 8e5cf51d6f59e9cea8572bfac47b5668 +imagecopymergealpha: f685c4a30c4255de4290c928ab4ac05e + diff --git a/ext/gd/tests/tux.png b/ext/gd/tests/tux.png new file mode 100644 index 0000000000000..df25ec4e45762 Binary files /dev/null and b/ext/gd/tests/tux.png differ