php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #22103 imagefilledellipse() is incorrect with alpha blending
Submitted: 2003-02-06 22:18 UTC Modified: 2003-02-08 02:56 UTC
From: ljpersson at hotmail dot com Assigned: pajoye (profile)
Status: Closed Package: GD related
PHP Version: 4.3.0 OS: Linux
Private report: No CVE-ID: None
 [2003-02-06 22:18 UTC] ljpersson at hotmail dot com
(Using built-in GD 2.x in PHP 4.3.0)

imagefilledellipse() creates moire' patterns inside the filled ellipse when alpha blending is enabled.

The problem seems to be that the fill algorithm strokes the same point within the ellipse several times and due to the alpha blending of single pixels creates several color shades within the ellipse.

This is fine as long as a solid fill is used but does not work together with alpha blending. 


Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2003-02-07 06:36 UTC] sniper@php.net
Please try using this CVS snapshot:

  http://snaps.php.net/php4-STABLE-latest.tar.gz
 
For Windows:
 
  http://snaps.php.net/win32/php4-win32-STABLE-latest.zip


There were couple of fixes in GD in CVS.
If it still fails, please provide a short example script.

 [2003-02-07 09:44 UTC] ljpersson at hotmail dot com
The problem still exist in snapshot 200302071430. 

I haven't had time to look at the GD code but this is most likely a design flaw and not a straight bug. Fixing this will probably require a new fill algorithm for ellipses that can guarantee that the same pixel inside the ellipse will only be painted exactly one time.

The script below demonstrates this bug
and the resulting image can also be viewed at
http://www.aditus.nu/jpgraph/bbimages/filledellipsebug.png


<?php
header("Content-type: image/png");

// Create a truecolor image with alpha blending
$im = imagecreatetruecolor(200,200);
imagealphablending ($im,true);

// Background color
$bkg = imagecolorresolvealpha($im, 255,255,255,0);

// Red with ~70% alpha blending (90/127)
$red = imagecolorresolvealpha($im, 255,0,0,90);

// White background
imagefilledrectangle($im,0,0,200,200,$bkg);

// Filled circle (with moire' patterns)
imagefilledellipse($im,100,100,120,120,$red);

imagepng($im);
imagedestroy($im);
?>
 [2003-02-07 11:06 UTC] ljpersson at hotmail dot com
I had a look in the source in GD. 
The problem is the way the gdImageFilledArc() is implemented. 

The algorithm there is flawed for alpha-blending. It fills the arc by drawing a series of filled polygons (triangles) centerd in the middle of the ellipse and out to the edge of the ellipse with 1 degree separation between the end points. This will make the interior points overlap and create the moire pattern.

Unfortunately there isn't a simple solution to this. Doing a floodfill to a specific color wan't work since we don't know the canvas we are drawing on.

Since I'm doing a lot of graphic stuff I'll see if I can come up with a new algorithm to do this. I'm afraid however that any correct algortihm will be quite CPU expensive.
 [2003-02-07 20:16 UTC] pajoye@php.net
Hello,

Thank's for the report.

<Since I'm doing a lot of graphic stuff I'll see if I can come up with a new algorithm to do this. I'm afraid however that any correct algortihm will be quite CPU expensive.>

I have already solved this problem with a quit nice and fast algorithm. I have to check if I can add it to the current 4_3 HEAD.

Doing something better with ellipse then what GD currently does is not very difficult (actually it's nothing else than a filled polygon with many connected lines, kind of ugly things to draw a simple ellipse).

pierre

 [2003-02-07 20:36 UTC] ljpersson at hotmail dot com
I wrote a drop in replacement for the GD gdImageFilledArc() which works with alpha-blending. Replace the gdImageFilledArc() in gd.c with the following function. Which is reasonable fast.


void 
gdImageFilledArc (gdImagePtr im, int cx, int cy, int w, int h, int s, int e, int color, int style)
{
    /* 
       This replacement for the original gdImageFilledArc() completely mimics
       the original behavior. This includes the behaviour I would consider a bug 
       where a style of IMG_ARC_CHORD is the same as IMG_ARC_PIE for filled arcs. 

       The benefit of this implementation is that this will also work for
       colors where alpha-blending is used.
       
       This algorithm uses GD standard sin & cos tables for quick lookup. 
       However, this also limits the accuracy to 1 degree. This means that
       very very large arc's will look "squarish". However for any normal
       sizes, say < 2000 pixels, this is not really a problem in practice.
    */

    gdPoint p[362];

    /* Sanity check */
    if( w < 0 || h < 0 ) return;

    /* Make sure angles are positive and e > s */
    while( s < 0 ) s += 360;
    while( e < 0 ) e += 360;
    while( e < s ) e += 360;

    s %= 360; 
    
    if( e > 360 ) 
	e %= 360;

    /* In the algorithm we need to ue the radius */
    w /= 2;
    h /= 2;

    /* We handle the chord and pie cases separately */
    if( style & gdChord ) {
	int x1,y1,x2,y2;
	x1 = cx + ((long) gdCosT[s] * (long) w / 1024 );
	y1 = cy + ((long) gdSinT[s] * (long) h / 1024 );
	x2 = cx + ((long) gdCosT[e] * (long) w / 1024 );
	y2 = cy + ((long) gdSinT[e] * (long) h / 1024 );
	if( style & gdNoFill ) {
	    if( style & gdEdged ) {
		p[0].x = cx; p[0].y = cy;
		p[1].x = x1; p[1].y = y1;
		p[2].x = x2; p[2].y = y2;
		p[3].x = cx; p[3].y = cy;
		gdImagePolygon (im, p, 4, color);	    
	    }
	    else {
		gdImageLine (im, x1, y1, x2, y2, color);	
	    }
	}
	else {
	    p[0].x = cx; p[0].y = cy;
	    p[1].x = x1; p[1].y = y1;
	    p[2].x = x2; p[2].y = y2;
	    p[3].x = cx; p[3].y = cy;
	    gdImageFilledPolygon (im, p, 4, color);	    
	}
    }
    else {
	/* style must be gdPie */
	int i=0, a=s;
	if( style & gdEdged || ! (style & gdNoFill) ) {
	    p[0].x = cx; p[0].y = cy;
	    i=1;
	}

	while( a <= e ) {
	    p[i].x = cx + ((long) gdCosT[a] * (long) w / 1024 );
	    p[i].y = cy + ((long) gdSinT[a] * (long) h / 1024 );
	    ++i;
	    ++a;
	}

	if( style & gdEdged || ! (style & gdNoFill) ) {
	    p[i].x = cx; p[i].y = cy;
	    ++i;
	}

	if( style & gdNoFill ) {
	    gdImagePolygon (im, p, i, color);	    
	}
	else {
	    gdImageFilledPolygon (im, p,i , color);	    
	}
    }
}
 [2003-02-07 20:42 UTC] ljpersson at hotmail dot com
Hm.. I didn't see that ou already had made a fix in the head for this. I haven't looked at your solution but I guess it's probably the same as mine. When I turned on my brain the solution is of course obvious once you know that FilledPolygon() actually does a correct fill.

BTW Do you agree with me that the behavior that
a filled style with IMG_ARC_CHORD should be the same as
IMG_ARC_PIE ? (The current behaviour.) I cosider this a bug since if you set the CHORD style it should fill just the CHORD and not complete the slice to the center. Just a minor detail.


I would rather consider this a bug
 [2003-02-07 21:26 UTC] pajoye@php.net
<I guess it's probably the same as mine.>

I thank you for your function, but hopefully there is a far better way to draw ellipse or circle.

As I said, I have to check if I can commit new functions to the head. As far as it's OK, I'll commit.

hth

pierre
 [2003-02-08 02:56 UTC] pajoye@php.net
This bug has been fixed in CVS.

In case this was a PHP problem, snapshots of the sources are packaged
every three hours; this change will be in the next snapshot. You can
grab the snapshot at http://snaps.php.net/.
 
In case this was a documentation problem, the fix will show up soon at
http://www.php.net/manual/.

In case this was a PHP.net website problem, the change will show
up on the PHP.net site and on the mirror sites in short time.
 
Thank you for the report, and for helping us make PHP better.


 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Mon Dec 02 13:01:29 2024 UTC