PHP - Bug in polygon GD library? - php

I have run into some trouble with the gd library's imagefilledpolygon().
For some reason some of my lines were ending up 1px out of place so I decided to debug it using imagepixelset to set the colour of my shapes points to red.
alt text http://www.degreeshowcase.com/other/1.gif
if you look at the picture you can see some of the points are inside the shape ... some are outside....its very illogical.
(the picture has been scaled up to make it more visible)
Does anyone have a solution?
Update:
My points for the shape above were: 0,0 40,0 40,20 20,20 20,40 0,40
I require that the height and width of the shape produced should be a multiple of 20.... but for some reason some of the shape ends up 21 px high or wide.
I have made a script to work out what the points would be to get the shape I wanted but I can not work out why and so I can't work out a script to correct all my shapes.
<?php
// set up array of points for polygon
$values = array(0,0, 39,0, 39,20, 19,20, 19,39, 0,39);
//My original values were 0,0 40,0 40,20 20,20 20,40 0,40
//I do not understand why some values require minus 1 and others can remain as they were (a multiple of 20)
// create image
$image = imagecreatetruecolor(40, 40);
// allocate colors
$bg = imagecolorallocate($image, 200, 200, 200);
$blue = imagecolorallocate($image, 0, 0, 255);
// fill the background
imagefilledrectangle($image, 0, 0, 39, 39, $bg);
// draw a polygon
imagefilledpolygon($image, $values, 6, $blue);
// flush image
header('Content-type: image/png');
imagepng($image);
imagedestroy($image);
?>

My guess is that you're mixing up width with position.
For example a line from 0px to 9px is 10px long... if you used length as the second parameter instead of position, it would end up 11px long.
If I could see some code I could confirm this.

Normal polygon rendering ensures that each pixel can only be in one polygon, if the 2 polygons share an edge. If you imagine drawing 2 squares, next to each other, so they share a common edge, you don't want to render the pixels along that edge twice.
There is an explanation of determining which pixels on the edge of a polygon should be considered inside the polygon here: http://www.gameprogrammer.com/5-poly.html
A common solution is to say that "pixels on the left and top edges of a polygon belong to the polygon and pixels on the right and bottom edges don't". I am not 100% sure what solution GD uses, as I could not find any documentation on it, but I expect it is something similar to this.

I spoke to the guy who currently develops the GD library he explained that it follows the 'Winding number algorithm' - can be found here. Having looked at my example image, it does match how the 'winding number algorithm' works, however the function should take this into account and produce the shape that was input.
As far as I can see the only way to accurately (to the pixel) generate a concave polygon with this function is to write another function that also applies the winding rule to your coordinates and adjusts them accordingly and then put it into the function.

Related

PHP: How do I change a color in an 8 bit PNG (png has transparency) using GD Library

I have an 8 bit PNG (original is here: https://i.ibb.co/s3024Y8/1000x1500-autumn-leaves.png) and I want to change the colour of the leaves.
I have a list of the hex colours that need to be changed and a list of the colours they need to be changed to - all to be done dynamically. I'm working towards a solution that has to work off those lists - but that can wait. For this question, being able to change the colour of just one leaf will do.
After changing the colour of the leaf I need the image to retain its transparent areas (so I can lay it on top of another image - that part I can do).
I am quite new to the GD library and am getting confused. I understand that an 8-bit PNG (as created in Photoshop) has full alpha - and that needs to be preserved.
That leads me to the start of my code:
$im = ImageCreateFromPNG('https://i.ibb.co/s3024Y8/1000x1500-autumn-leaves.png');
ImageAlphaBlending($im, false);
imagesavealpha($im, true);
Everything I've tried after that fails miserably. By that, I mean that the colour doesn't change. No error message, just no change in colour. It seems that the following has no effect when using that image:
$myColourToChange = imagecolorallocatealpha($im, 100, 24, 11, 0); // colour of dark brown leaf
imagecolorset($im, $myColourToChange, 255, 0, 0, 0); // try changing it to red, fully opaque
I've even tried (which gives 6559755)
$myColourToChange = imagecolorresolvealpha($im, 100, 24, 11, 0);
I think things are failing (ie the colour doesn't change) because the image I'm using isn't a paletted image in the way that GD needs it to be, in order for me to use imagecolorset
I've tried converting it to one using imagetruecolortopalette but that gives unpredictable results on the image, with all sorts of artifacts not visibly present in the original image - and the colour values change slightly.
Note that the following are representations if you want to experiment, use the image link above.
Original image before imagetruecolortopalette - dark brown leaf colour: #64180B
After imagetruecolortopalette - dark brown leaf colour: #631A0C
So how do I specify the colour that needs to be changed and then change it to the given colour? If it means I must use imagetruecolortopalette how do I deal with the artifacts and the colour change I get when using it?
(I won't know in advance the location of colours that need changing, so imagecolorat won't help)
Thanks in advance :)

How to add a transparent rectangle to PNG file in php WITHOUT a border

I can successfully add a transparent rectangle to an area of a saved PNG file, but it has a faint border around it, which can be seen on the final image. I want to remove this.
The code I use to add the rectangle is:
$dest=imagecreatefrompng("originalFilename.png");
$red = imagecolorallocate($dest, 255, 0, 0);
imagecolortransparent($dest, $red);
imagefilledrectangle($dest, 0, 0, 50, 25, $red);
imagepng($dest, "newFilename.png");
and the result looks like this:
Image showing rectangle outline
The transparency works fine, showing in this example a white background behind... but you can see that in the top left there's the faint outline of the transparent rectangle. This is what I want to remove.
I've since tried putting a box over the edge (it would be white to match the background of the image, but using green for display purposes here), but that box gets a slightly red huhe around it too!?!? Really strange.
Like this
red hue
Grateful for any clues. This is not the same as the suggested duplicate as it deals specifically with the hue that's left around the border of the transparent area and also any object made to go in that area.

Imagecolortransparent() for multiple colors

I use this code to make all white pixels transparent:
$img = imagecreatefromjpeg('test.jpg');
$remove = imagecolorallocate($img, 255, 255, 255);
imagecolortransparent($img, $remove);
imagepng($img, 'bla.png');
But I also want some "almost white" pixels to be transparent like 254, 255, 255 etc. How could I add that?
If your question is how to remove a contiguous area with similar color range; I must say that it's hard using PHP's GD library. You should take a look at ImageMagick which is a much more powerful image processing library and has PHP integration.
If you choose to use ImageMagick instead, you'll have access to a lot of third-party scripts that does amazing image processings. One of which is Fred Weinhaus' MagicWand. It does what you're looking for.
You seed it with an x and y coordinate, it can extract the color of that coordinate and make it transparent with a configurable color dissimilarity threshold (fuzz factor). Have a look how it can match a gradient of blue:
I had the same problem. With a black and white image. But white was not very white. So before to set transparency, I do a filter to increase the contrast. And I have an image with real black and real white. And when I use transparency on white color, it's works fine.
Filter's example :
imagefilter($src,IMG_FILTER_CONTRAST,-50);

PHP GD library - Unexpected white spots found on overlaying one gd semi-transparent image over the other

Ive been working on this project for days now and for some reason just not able to get rid of an unexpected 1px by 1px (approx) white spot that apears in each and every tile that is processed.
Summary:
I am using an original image ( say original.jpeg ) as referance to create a mosaic image ( say mosaic.jpeg which is approx 1000px by 1000px ) by merging totegher much much smaller jpeg images ( aprox 10px by 10px ).
I have a data set of approx 20,000 tile images to work with.
Process so far
I have mapped the original.jpeg image to cut it up into 5px by 5px tiles, then found the average color of each tile and saved it for further use.
I have scanned all the (10x10) tile images and stored their individual average colors as well.
i have calculated which tile image would fit closest to which tile of the original image by using Weighted ref : euclidean distance from the site mentioned.
I have managed to use PHP gd library to create a new truecolor image with all the matching tiles placed in the right position ( thus effectively creating a mosaic of the original.jpeg image )
The Problem
I am not getting a clear mosaic i had expected, for some reason the tiles do not match properly.
The Workaround
Now due to lack of time i an using a quick fix where in i take the original image, give it some 50% opacity and overlay it on top of each tile while it is being placed into the final mosaic.
NOTE: Although i am effectively overlaying the original image over the mosaic image, im NOT doing it in one go. The overlaying HAS to happen at each tile level.
So in short : Before placing each tile at the correct position of the final mosaic, i do the following
1. get that particular section of the original image ( 5x5px )
2. expand it to match the final tile size ( 10x10px )
3. set transparency for the section
4. place it over the tile that will be placed
5. merge this new tile over the final mosaic at the corresponding position.
Here is the function i have created to overlay part of the image along with transparency set to it.
public function overlay($dImg, $sImg, $opacity = null) {
// set default Opacity if not specified
$opacity = (is_null($opacity)) ? $this->opacity : $opacity;
// get width, height of sourceImage
$sWidth = imagesx($sImg);
$sHeight = imagesy($sImg);
// get width height of final image
$dWidth = imagesx($dImg);
$dHeight = imagesy($dImg);
$image = imagecreatetruecolor($dWidth, $dHeight);
imagecopyresampled($image, $sImg, 0, 0, 0, 0, $dWidth, $dHeight, $sWidth, $sHeight);
$background = imagecolorallocatealpha($image, 255, 255, 255, 127);
imagefill($image, 0, 0, $background);
imagealphablending($image, true);
imagecopymerge($dImg, $image, 0, 0, 0, 0, $dWidth, $dHeight, $opacity);
imagedestroy($image);
return $dImg;
}
THE REAL PROBLEM
in theory all of this seems perfectly fine. But the results have their own say in the matter.
I noticed an unusual almost 1x1px white patch at the start of every tile of the final mosaic.
This white patch only appears when the transparency technique above is applied. It does not happen otherwise.
I am clueless as to why this is happening, Due to this white patch the entire image looks like it has white noise all over it and degrades the overall quality immensely.
Please guide me in any direction as to why something like this could be happening.
your problem lies on these two lines:
$background = imagecolorallocatealpha($image, 255, 255, 255, 127);
imagefill($image, 0, 0, $background);
you don't need those, because imagefill is used to fill an area that have same/similar color with the color located on the supplied coordinate, in your case 0, 0 (top left), when there is no adjacent similar color, then it just change the color at the given coordinate.
you can use imagefilledrectangle instead, but i still think you don't need that, just comment out those 2 lines and see the result if it match your requirement, if not, then go ahead use imagefilledrectangle
imagefilledrectangle

Starting point in coordinate systems

I have the following php code:
<?php
$image = imagecreatefrompng("captcha_background.png");
$imgcolor = imagecolorallocate($image, 0, 0, 0);
imagesetthickness($image, 2);
imageline($image, 0, 25, 40, 90, $imgcolor);
?>
The method "imageline" draws a straight line on my image from the coordinates 0 (x) 25 (y) to 40 (x) 90 (y).
The result is the following image:
What I'm confused about is the reverse of the bottom and the top when using coordinate systems in php.
Normally 0 (The starting point) would be in the lower left corner, but when assigning coordinates in the method "imageline" the 0 (Starting point) is located in the upper left corner?
Expected result:
(The image is 300x100 pixels)
Could someone please explain why this is happening?
This is not a mathematical graph. The typical coordinate system used in development (as far as I know) is to have the first quadrant at the lower right. That is, 0x0 is at the top left. This applies to all html elements that have widths and heights (the elements drop down, they do not fall up).
The motivation appears to be the fact that it's hard to tell how much height you have to work with without knowing the absolute height of the image, which you may not know at any given time, and which may change frequently.
That's how the coordinates are defined in GD, nothing to worry about.
http://www.php.net/manual/en/function.imagedashedline.php :
y1: Upper left y coordinate 0, 0 is the top left corner of the image.
I believe this is the standard for the GD image library as they define the natural origin as the top-left corner.

Categories