PHP imagecopyresampled() produces image border on one side - php

I have a resampling script that centers resampled images on a square canvas.
For horizontally-oriented images that are centered vertically on a white background a line is added along the BOTTOM edge of the resampled image.
For vertically-oriented images that are centered horizontally the line appears on the RIGHT edge of the resampled image.
$thumb = imagecreatetruecolor($th_width, $th_height);
imagecopyresampled($thumb, $source, $th_x, $th_y, 0, 0, $th_width, $th_height, $src_width, $src_height);
$bgcolor = imagecolorallocate($thumb, 255, 255, 255);
imagefill($thumb, 0, 0, $bgcolor);
The line is there regardless of background fill, it just shows up most on white.
What causes this? No amount of adjusting parameter values will get rid of it (they just offset the resampled image on the background or distort it).

I am providing my own workaround for this issue.
It appears the image border artifact is a COMBINATION result of 'imagecopyresampled()' AND centering offsets. If I just resample the image as-is THEN center and fill the image, the border is avoided. Here is the workaround:
1) RESAMPLE your image as-is (same aspect ratio) then SAVE to retain changes:
$thumb = imagecreatetruecolor($res_width, $res_height);
imagecopyresampled($thumb, $source, 0, 0, 0, 0, $res_width, $res_height, $src_width, $src_height);
// SAVE new image - set quality to lossless since reusing it:
imagejpeg($thumb, "resampled/output_temp.jpg", 100);
2) Retrieve the temporary image:
$file = "resampled/output_temp.jpg";
$image = file_get_contents($file);
$source = imagecreatefromstring($image);
// Get the RESAMPLED image dimensions
list($src_width, $src_height) = getimagesize($file);
3) NOW apply CENTERING and FILL:
$thumb = imagecreatetruecolor($th_width, $th_height);
// COPY the image
imagecopy($thumb, $source, $th_x, $th_y, 0, 0, $src_width, $src_height);
$bgcolor = imagecolorallocate($thumb, 255, 255, 255);
// Replace default black background - this must be placed AFTER imagecopy()
imagefill($thumb, 0, 0, $bgcolor);
// Save image (uses default quality setting 75)
imagejpeg($thumb, "resampled/" . $newfile);
// Optionally free up memory:
imagedestroy($thumb);
The result is a clean image.
For correcting incomplete imagefill() on the background when centering vertically-oriented images see this post:
imagecopyresampled() results in split color background imagefill()

Related

How to make any "extra space" white when using imagecopy in PHP

I am using imagecopy to crop a PNG image to a user specification.
Currently, if the crop area is bigger than the image, any "extra space" becomes black, but I would like it to be white.
Have searched around a bunch, I have discovered that you can use imagefill or imagefilledrectangle to make the background white, however if this is done before imagecopy, then it has no effect and if it is done after imagecopy, it also makes any black parts of the image white.
My code currently looks like this and suffers from black parts of the original image being turned white as well as the extra space:
// Input and output files
$infile = "[Image]";
$outfile = "[Output path for image]"
// Make the image
$orig =imagecreatefromjpeg($infile);
$width = imagesx($orig);
$height = imagesy($orig);
$new = imagecreatetruecolor($width, $height);
// Crop the image
imagecopy($new, $orig, 0, 0, -100, 100, $width, $height);
// Try and make the extra space white
$white = imagecolorallocate($new, 255,255,255);
imagefill($new, 0, 0, $white);
// Save the file
imagepng($new, $outfile);
How can I make that extra space white without affecting the original image? I have no control over what image users might upload, so I can't really pick a transparent color as that color might be part of their original image.
EDIT: This scenario arises when a user chooses a crop size outside of the original image dimensions, something that I do want to be a valid option. The crop is to force a square image, but if the user uploads, say, a landscape rectangle and wants all of their image in the final crop, then the crop will be outside of the image on the top and bottom (which is where I want it to be white instead of black)
This happens because you are supplying 'invalid' values to imagecopy() (that is, the crop coordinates are outside the bounds of the source image). GD simply fills in the out of bounds area with black pixels. It would be lovely if it instead used transparent (or any colour) pixels but unfortunately that's not an option.
I don't completely understand what you are trying to do (your source doesn't seem to match your stated goal) but a possible solution involves restricting the crop to the bounds of the image:
$src = imagecreatefromjpeg('JPEG FILE'); // 100x100 image in my test.
$src_w = imagesx($src);
$src_h = imagesy($src);
$user_crop = [
'x' => -50,
'y' => -50,
'width' => 150,
'height' => 150
];
if ($user_crop['x'] < 0) {
$user_crop['x'] = 0;
}
if ($user_crop['y'] < 0) {
$user_crop['y'] = 0;
}
if ($user_crop['x'] + $user_crop['width'] > $src_w) {
$user_crop['width'] = $src_w - $user_crop['x'];
}
if ($user_crop['y'] + $user_crop['height'] > $src_h) {
$user_crop['height'] = $src_h - $user_crop['y'];
}
$dest = imagecreatetruecolor($src_w, $src_h);
imagefill($dest, 0, 0, 0x00ffffff); // opaque white.
imagecopy(
$dest,
$src,
$user_crop['x'],
$user_crop['y'],
$user_crop['x'],
$user_crop['y'],
$user_crop['width'],
$user_crop['height']
);
header('Content-type: image/png;');
imagepng($dest);
imagedestroy($src);
imagedestroy($dest);
exit;
Note that I've made a few assumptions in this code about placement of the cropped image.

imagecopyresampled() results in split color background imagefill()

I have a script that takes an image and when resampled centers the short dimension (width or height) on a square coloured background.
This works fine for images with a longer WIDTH but for some reason any image with a longer HEIGHT the result is a split background fill - the correct colour on the left but the default black on the right. If I play with the x-axis offset the background fill only extends to the right as far as the image placement.
The calculated values are as expected for the vertical images so I cannot figure out what is happening here. Note that 'imagecopy()' produces the exact same behaviour.
original image is 155 x 400px
adjusted source dimensions for square aspect ratio = 400 x 400px
resulting thumbnail to be 250 x 250px
Here is the code with static values for one example:
$thumb = imagecreatetruecolor(250, 250);
imagecopyresampled($thumb, $source, 77, 0, 0, 0, 250, 250, 400, 400);
$blue = imagecolorallocate($thumb, 0xDE, 0xE6, 0xF9);
imagefill($thumb, 0, 0, $blue);
Using the same image rotated 90 degrees (400 x 155 px) so it is longer horizontally DOES apply the full background fill:
imagecopyresampled($thumb, $source, 0, 77, 0, 0, 250, 250, 400, 400);
For the vertical image, my coordinate values (77, 0) place the image on the imagecreatetruecolor() canvas centered exactly where I want it but changing any of the other imagecopyresampled() values stretch or squeeze the resampled image or crop it.
Am I overlooking something simple? View the screenshots here:
http://i.stack.imgur.com/5CxHU.jpg (vertical issue) and
http://i.stack.imgur.com/wvhzP.jpg (OK horizontally)
This vertical issue must have something to do with PHP's resampling/imagefill algorithm (?) but here is a workaround that now works for centering all of my vertical images within my square canvas:
1) You need to first pad your image placeholder so the background fill will extend to the right edge in the resampled image by extending the thumbnail height with your x-axis offset (sounds odd but it works)...we will trim this off later:
$thadj_height = $th_height + $th_x;
$thumb = imagecreatetruecolor($th_width, $thadj_height);
2) Resample as usual with the background fill (note that the fill is applied AFTER the resampling statement, odd but just works that way)...remember that $thumb has more height than what $th_width, $th_height will occupy:
imagecopyresampled($thumb, $source, $th_x, $th_y, 0, 0, $th_width, $th_height, $src_width, $src_height);
imagefill($thumb, 0, 0, $bgcolor);
3) Temporarily save the image output so a new function can be applied to it next - set quality to lossless since we'll be reusing it:
imagejpeg($thumb, "resampled/output_temp.jpg", 100);
imagedestroy($thumb);
4) Retrieve the temporary file and grab the new dimensions (overwrite the previous variables):
$file = "resampled/output_temp.jpg";
$image = file_get_contents($file);
$source = imagecreatefromstring($image);
list($src_width, $src_height) = getimagesize($file);
5) Create a new image placeholder, square as originally intended in my case:
$thumb = imagecreatetruecolor($th_width, $th_height);
6) Now copy the temporary padded thumbnail into the square placeholder which will result in cropping off the padding:
imagecopy($thumb, $source, 0, 0, 0, 0, $src_width, $src_height);
header('Content-Type: image/jpeg');
echo imagejpeg($thumb);
imagedestroy($thumb);
Again, none of this is necessary for centering my horizontal images on a square canvas but this is a workaround that will work to eliminate the split fill background.

PHP GD : Can i just fill transparent part?

There is an image which has transparent area. (png image)
Now, while doing a imagecopy, can we just fill that transparent area?
Imagemagick can do this easily. Is that possible in php gd?
A layered approach via imagecopymerge() is one route. The concept is to merge your source image onto a new image, with a pre-set background image, which will show through the source image's transparency once merged.
//create main image - transparent, with opaque red square in middle
$img = imagecreate(60, 60);
$white = imagecolorallocate($img, 255, 255, 255);
imagecolortransparent($img, $white); //make background transparent
$red = imagecolorallocate($img, 255, 0, 0);
imagefilledrectangle($img, imagesx($img) / 4, imagesy($img) / 4, imagesx($img) - (imagesx($img) / 4), imagesy($img) - (imagesy($img) / 4), $red);
//create new image, with pre-filled background, then merge first image across
$img2 = imagecreate(60, 60);
$blue = imagecolorallocate($img2, 0, 0, 255);
imagecopymerge($img2, $img, 0, 0, 0, 0, imagesx($img), imagesy($img), 100);
//output
imagepng($img2);
So the first image creates a transparent image (the white) with a red square in the middle. The second image is simply a blue fill.Merge the two, and the blue shows through the transparent part of the first image, so our red square now sits on the blue fill. Effectively, we've filled the transparent part.
Here's the three states in sequence.
There is nothing to 'fill' where there is transparency. Transparency in a png (or gif) is not the absence of a color, but a single color specifically marked to be shown as transparent. Therefore you want to remove that marker.
Take a look at the php gds function 'imagecolortransparent':
Nope, you can't.
Instead that, you can try to create a copy from a colored rectangle. This code worked for me:
$input = imagecreatefrompng($file_input);
list($width, $height) = getimagesize($file_input);
$output = imagecreatetruecolor($width, $height);
$white = imagecolorallocate($output, 255, 255, 255);
imagefilledrectangle($output, 0, 0, $width, $height, $white);
imagecopy($output, $input, 0, 0, 0, 0, $width, $height);
imagepng($output, $file_output);

Merge images with PHP

I have written a code for merge two images.
My code is:
$rnd = rand("99000", "99999");
$dst_path = "/home/maxioutl/public_html/images/urunler/";
$dst_file_name = "tresim-{$rnd}.jpg";
$dst_file = $dst_path.$dst_file_name;
$dst = imagecreatetruecolor(250, 375);
imagefill($dst, 0, 0, imagecolorallocate($dst, 255, 255, 255));
$src = imagecreatefromjpeg("http://www.goldstore.com.tr/upload/product/raw/3.72925.0332.JPG");
imagecopymerge($dst, $src, 40, 60, 0, 0, 250, 375, 100);
imagejpeg($dst, $dst_file, 90);
Result:
Black background. Where is it?
It's the imagecopymerge($dst, $src, 40, 60, 0, 0, 250, 375, 100); statement that's doing it.
You're passing it the dimensions 250x375, which isn't the watch's actual dimensions. Therefore the merge bounding box continues on and it uses black. You can see this easily if you comment it out because you'll get your white square from the fill like you were expecting.
you need to get the exact dimensions of your watch graphic (i.e. through getimagesize) and pass those to imagecopymerge so it cuts it exactly when it merges.
$arrSize = getimagesize($originalFile);
imagecopymerge($dst, $src, 40, 60, 0, 0, $arrSize[0], $arrSize[1], 100);
The code you wrote superimposes the images. It does not remove the white pixels from the image with the watch.
They are basically overlapped.
You should cycle through both image dimensions and replace each *white pixel with a black pixel or a transparent pixel.
*white pixel may not necessarily mean rgb(255,255,255) you can choose to treat all pixels with rgb(>235,>235,>235) as being "white".
Function imagecreatetruecolor creates completely black image
Taken from here :
imagecreatetruecolor() returns an image identifier representing a
black image of the specified size.

Merging Images using GD with PHP

i'm working on creating one PNG image from two others.
Image A and B have the same dimensions, they are both 200x400px. The final image the same.
I'm using the GD library with PHP.
So my idea was to create a PNG-24 from my original PNG-8, then use color transparency and finally copy the second image into
this new PNG-24. The problem appears in the first step anyway, when going from PNG-24 to PNG-8 with color transparency:
This is to get the original PNG-8 and it's dimensions:
$png8 = imagecreatefrompng($imageUrl);
$size = getimagesize($imageUrl);
Now i create a new PNG and fill it's background with a green color (not present in the images):
$png24 = imagecreatetruecolor($size[0], $size[1]);
$transparentIndex = imagecolorallocate($png24, 0x66, 0xff, 0x66);
imagefill($png24, 0, 0, $transparentIndex);
This is for making the green color transparent:
imagecolortransparent($png24, $transparentIndex);
Then i copy the png8 into the PNG-24:
imagecopy($png24, $png8, 0, 0, 0, 0, $size[0], $size[1]);
So here's the problem: the original PNG-8 looks good, but it has a green border surrounding the shape within the original image. It's difficult to explain really. Seems like some part of the green background is left in the remaining PNG.
What can i do?
thanks in advance
best regards,
Fernando
I had some problems with png transparency before and was able to solve them with this pattern:
// allocate original image to copy stuff to
$img = imagecreatetruecolor(200, 100);
// create second image
$bg = imagecreatefrompng('bg.png');
// copy image onto it using imagecopyresampled
imagecopyresampled($img, $bg, 0, 0, 0, 0, 200, 100, 200, 100);
imagedestroy($bg);
// create third image
// do same routine
$fg = imagecreatefrompng('fg.png');
imagecopyresampled($img, $fg, 50, 50, 0, 0, 50, 50, 50, 50);
imagedestroy($fg);
// output image
imagepng($img);
imagedestroy($img);
I think the only difference between mine and yours is imagecopy() vs. imagecopyresampled(). I seem to remember having problems with that though it was quite a while ago. You can see an example of an image I use this pattern on here: http://www.ipnow.org/images/1/bggrad/bg4/yes/TRANSIST.TTF/8B0000/custombrowserimage.jpg (I allocate a blank image, copy the background image in, copy the overlay with transparency in)

Categories