ID:               23815
 Comment by:       andre at webkr dot de
 Reported By:      bjorn at smokingmedia dot com
 Status:           Assigned
 Bug Type:         Feature/Change Request
 Operating System: Linux pluto 2.4.18lvm-r1
 PHP Version:      5.2.9
 Assigned To:      pajoye
 New Comment:

So what does the "it implements alpha transparency for true colour
images" in "When pct  = 0, no action is taken, when 100 this function
behaves identically to imagecopy() for pallete images, while it
implements alpha transparency for true colour images." mean anyway?


Previous Comments:
------------------------------------------------------------------------

[2009-07-20 12:10:43] steve at redmonkey dot org

Thanks, understood. Although, I do think it would be a useful feature,
perhaps there's scope for an 'imagecopymergealpha' type function in the
future?

------------------------------------------------------------------------

[2009-07-20 08:43:04] paj...@php.net

imagecopymerge was not meant to support the alpha channel but to
emulate it via pct. It was also not meant to use both the alpha or the
pct value to blend an image over another.

------------------------------------------------------------------------

[2009-07-20 05:44:49] steve at redmonkey dot org

To make life a little easier I've put the notes and examples together
on a simple web page at http://www.redmonkey.org/php-bug-23815/

After investigating the code base a little further I've realised my
patch solution can be made more efficient as there is no need to make a
copy of the source or pass the image over to gdImageCopy once the new
alpha level has been set as we've already done the all the work and can
simply set the pixels RGBA index within the second image scan.

The revised patch (which is also available from a link on the web page)
is as follows....

--- php-5.3.0/ext/gd/libgd/gd.c 2009-05-27 08:17:54.000000000 +0100
+++ php-5.3.0-build/ext/gd/libgd/gd.c   2009-07-20 05:54:21.709936176
+0100
@@ -2255,6 +2255,67 @@
        int ncR, ncG, ncB;
        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;
+               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;
+                       }
+               }
+
+               /* src has no transparency? set to use full alpha range */
+               mt = mt == gdAlphaOpaque ? 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);
+
+                               int nc = gdImageColorAllocateAlpha(src, cr, cg, 
cb, (int)na);
+                               if (nc == -1) {
+                                       gdImageColorClosestAlpha(src, cr, cg, 
cb, (int)na);
+                               }
+
+                               gdImageSetPixel (dst, dstX + x, dstY + y, nc);
+                       }
+               }
+
+               return;
+       }
+
        for (y = srcY; y < (srcY + h); y++) {
                tox = dstX;
                for (x = srcX; x < (srcX + w); x++) {

------------------------------------------------------------------------

[2009-07-20 01:52:43] steve dot denim at redmonkey dot org

I have run into the same problem and can reproduce the example from
checat at yandex dot ru.

I have also provided additional examples and a patch for the solution I
have come with.

For the examples I've used two image files 'tux.png' for the background
(destination) image and 'ff-logo-sm.png' for the overlay (source) image,
these can be found at www.redmonkey.org/php-bug-23815/tux.png and
www.redmonkey.org/php-bug-23815/ff-logo-sm.png respectively.

If I run these images through imagecopy() with the following code..

$bg  = imagecreatefrompng('tux.png');
$over = imagecreatefrompng('ff-logo-sm.png');
imagealphablending($bg, true);
imagesavealpha($bg, true);
imagecopy($bg, $over, 276, 300, 0, 0, 123, 119);
imagepng($bg, 'tux-fox-imagecopy.png');

The alpha channels of both images seem to be handleed and
merged/blended in a way that I think most users would expect, the
resulting image can be found at
www.redmonkey.org/php-bug-23815/tux-fox-imagecopy.png

However, if I run the two images through imagecopymerge with the
following code..

$bg  = imagecreatefrompng('tux.png');
$over = imagecreatefrompng('ff-logo-sm.png');
imagealphablending($bg, true);
imagesavealpha($bg, true);
imagecopymerge($bg, $over, 276, 300, 0, 0, 123, 119, 100);
imagepng($bg, 'tux-fox-imagecopymerge-100-without-patch.png');

The resulting image is not what I would expect, in this case, it seems
that the alpha channel of the destination image is maintained but the
alpha channel of the source image is completely ignored, the resulting
image can be found at
www.redmonkey.org/php-bug-23815/tux-fox-imagecopymerge-100-without-patch.png

Applying a 50% reduction in opacity..

$bg  = imagecreatefrompng('tux.png');
$over = imagecreatefrompng('ff-logo-sm.png');
imagealphablending($bg, true);
imagesavealpha($bg, true);
imagecopymerge($bg, $over, 276, 300, 0, 0, 123, 119, 50);
imagepng($bg, 'tux-fox-imagecopymerge-50-without-patch.png');

Also has the same issue, the resulting image can be found at
www.redmonkey.org/php-bug-23815/tux-fox-imagecopymerge-50-without-patch.png

After applying my patch the results from imagcopymerge are more inline
with what I persoanlly would expect in that the alpha channels of both
images are maintained during the copy/merge process.

With the following code..

$bg  = imagecreatefrompng('tux.png');
$over = imagecreatefrompng('ff-logo-sm.png');
imagealphablending($bg, true);
imagesavealpha($bg, true);
imagecopymerge($bg, $over, 276, 300, 0, 0, 123, 119, 100);
imagepng($bg, 'tux-fox-imagecopymerge-100-with-patch.png');

The resulting image can be viewed at
www.redmonkey.org/php-bug-23815/tux-fox-imagecopymerge-100-with-patch.png

And with the following code..

$bg  = imagecreatefrompng('tux.png');
$over = imagecreatefrompng('ff-logo-sm.png');
imagealphablending($bg, true);
imagesavealpha($bg, true);
imagecopymerge($bg, $over, 276, 300, 0, 0, 123, 119, 50);
imagepng($bg, 'tux-fox-imagecopymerge-50-with-patch.png');

The resulting image can be viewed at
www.redmonkey.org/php-bug-23815/tux-fox-imagecopymerge-50-with-patch.png

The patch as applied to ext/gd/libgd/gd.c is as follows.....


--- php-5.3.0/ext/gd/libgd/gd.c 2009-05-27 08:17:54.000000000 +0100
+++ php-5.3.0-build/ext/gd/libgd/gd.c   2009-07-19 22:28:37.312702552
+0100
@@ -2255,6 +2255,83 @@
        int ncR, ncG, ncB;
        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.
+                */
+               gdImagePtr  srcback;
+               int         ca, cr, cg, cb;
+               float       na;
+               float       ac;
+
+               /* we adjust the alpha levels on a copy of the source image, the
copy
+                * only needs to be as large as the crop area if there is one
+                */
+               srcback = gdImageCreateTrueColor (w, h);
+               if (srcback==NULL) {
+                       return;
+               }
+
+               gdImageAlphaBlending(srcback, 0);
+               gdImageSaveAlpha(srcback, 1);
+               gdImageCopy(srcback, src, 0, 0, srcX, srcY, w, h);
+
+               /* 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 (srcback, x, y);
+                               ca = gdImageAlpha(srcback, c);
+
+                               mt = ca > mt ? ca : mt;
+                       }
+               }
+
+               /* src has no transparency? set to use full alpha range */
+               mt = mt == gdAlphaOpaque ? 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 (srcback, x, y);
+                               ca = gdImageAlpha(srcback, c);
+                               cr = gdImageRed(srcback, c);
+                               cg = gdImageGreen(srcback, c);
+                               cb = gdImageBlue(srcback, c);
+
+                               na = (ca + gdAlphaMax - (gdAlphaMax * 
((float)pct / 100))) * ac;
+                               na = (na > 127.0f)? 127.0f : ((na < 0.0f)? 
0.0f: na);
+
+                               int nc = gdImageColorAllocateAlpha(srcback, cr, 
cg, cb, (int)na);
+                               if (nc == -1) {
+                                       gdImageColorClosestAlpha(srcback, cr, 
cg, cb, (int)na);
+                               }
+
+                               gdImageSetPixel (srcback, x, y, nc);
+                       }
+               }
+
+               /* finally dispatch to gdImageCopy to do the actual copying */
+               gdImageCopy(dst, srcback, dstX, dstY, 0, 0, w, h);
+               gdImageDestroy(srcback);
+               return;
+       }
+
        for (y = srcY; y < (srcY + h); y++) {
                tox = dstX;
                for (x = srcX; x < (srcX + w); x++) {

------------------------------------------------------------------------

[2009-05-04 12:14:15] checat at yandex dot ru

Please try again: site with images is up and running.

I've also uploaded files to alternate location, get them from
http://checat.narod.ru/php-bug-23815/

------------------------------------------------------------------------

The remainder of the comments for this report are too long. To view
the rest of the comments, please view the bug report online at
    http://bugs.php.net/23815

-- 
Edit this bug report at http://bugs.php.net/?id=23815&edit=1

Reply via email to