PHP - creating a PNG file with semi-transparent logo / watermark - php

I'm trying to place a logo into a white image and make it semi-transparent to be used as a watermark.
Here is my code...
// load the stamp and the photo to apply the watermark to
if (file_exists($logoPath)) {
$im = imagecreatefrompng($logoPath);
$size = getimagesize($logoPath);
$stamp = imagecreatetruecolor(612, 792);
imagefilledrectangle($stamp, 0, 0, 612-1, 792-1, 0xFFFFFF);
$sx = imagesx($stamp);
$sy = imagesy($stamp);
// center width and height
$centerX=$sx/2-$size[0]/2;
$centerY=$sy/2-$size[1]/2;
$res=imagecopymerge($stamp, $im, $centerX,$centerY, 0, 0, $sx, $sy, 15);
$waterPath = $watermark_path.$broker_id."_watermark.png";
// Save the image to file and free memory
imagepng($stamp, $waterPath);
imagedestroy($stamp);
imagedestroy($im);
}
It all looks good to me but when I run it I get this...
http://i43.tinypic.com/2cyft06.jpg
...as you can see, the lower right quad of the image is getting colored for some reason.

if you take a look at imagecopymerge() docs, the 7th and 8th arguments represent the source image width and height amount. You appear to pass the target image height (612, 792), so basically you're trying to copy a 612x792 slice from your logo image, which looks much smaller.
I'll try to better describe the arguments:
$res = imagecopymerge(
$stamp, // <- target image
$im, // <- source image, from where to copy (logo)
$centerX, // <- target x-position (where to place your logo),
$centerY, // <- target y-position
0, // <- source x-position (x-offset from where to start copy)
0, // <- source y-position
imagesx($im), // <- amount to copy from source (width)
imagesy($im), // <- amount... (height)
15 // <- i have no idea what this is :)
);

Related

Transparent image on transparent image PHP

I'll explain a bit of my situation.
We got an island with cities. These could be:
Owned by my alliance
Owned by an enemy
Owned by nobody (free)
So we got 3 "cities"-images and an island image, looks like this:
Now we want to put these cities images on the island image. For example, we put a city owned by nobody on the island like this:
<?php
// Get image
$im = imagecreatefrompng('island.png');
imagealphablending($im,true);
// Get our "Free-city-position" image
$stamp = imagecreatefrompng('free.png');
$pos_x = 190 - 15; // Position X = 190 - the half of the free.png image = 30 / 2 = 15
$pos_y = 225 - 15;// Position Y = 225 - the half of the free.png image = 30 / 2 = 15
imagealphablending($stamp,true);
imagecopy($im, $stamp, $pos_x, $pos_y, 0, 0, imagesx($stamp), imagesy($stamp));
// Output image
header('Content-type: image/png');
imagepng($im);
imagedestroy($im);
?>
And now there is our problem: The city image is not transparent on the island image! It looks like this:
I thought imagealphablending should do the trick, but unfortunately, it doesn't.
How can we get the transparant city image on the island?
Taken from PHP's comment on GD functions :
If you are trying to copy a transparant image on to another image, you
might assume that you should apply the ImageAlphaBlending function to
the image that has the transparancy, the source image. In reality, you
must apply the ImageAlphaBlending function to the destination image.
Basically it's saying, "make the specified image respect
transparancy".
Try this way:
#!/usr/bin/php -f
<?php
// Read island image and get dimensions
$island=imagecreatefrompng("island.png");
$w_island=imagesx($island);
$h_island=imagesy($island);
// Read city image and get dimensions
$city= imagecreatefrompng("city.png");
$w_city=imagesx($city);
$h_city=imagesy($city);
// Create output image
$result=imagecreatetruecolor($w_island,$h_island);
imagesavealpha($result,true);
$transparent=imagecolorallocatealpha($result, 0, 0, 0, 127);
imagefill($result, 0, 0, $transparent);
// Splat island onto transparent background
imagecopy($result, $island, 0, 0, 0, 0, $w_island, $h_island);
// Splat city ontop
imagecopy($result, $city, 100, 280, 0, 0, $w_city, $h_city);
imagepng($result,"result.png");
?>

PHP imagerotate is cropping image

I am writing a script that takes an arrow image and rotates it by a set number of degrees. Using the code below, when the angle is a multiple of 90 the image rotates and displays as expected.
The source image looks like this (74 x 74):
Images after rotating by 90:
Images after rotating by any other number (not a multiple of 90) eg 45:
As can be seen in the image, the tip of the arrow has been cropped out of the image. Could anyone please tell me why this is happening? Again, multiples of 90 are fine, it's just any other number where the unusual cropping occurs.
$props = ['w' => 74, 'h' => 74];
$angle = 360 - $_GET['angle'];
$final_img = imagecreatetruecolor($props['w'], $props['h']);
imagesavealpha($final_img, true);
$transColor = imagecolorallocatealpha($final_img, 0, 0, 0, 127);
imagefill($final_img, 0, 0, $transColor);
$rotate = imagecreatefrompng('arrow.png');
$src = imagerotate($rotate, $angle, $transColor); //rotated my image
$src_x = ImageSX($src); //find out new x width
$src_y = ImageSY($src); //find out new y height
$src_widthx = $src_x/2 - $props['w']/2; // divide each by 2 and then subtract desired end width from wider rotated width
$src_heighty = $src_y/2 - $props['h']/2; // and again for height
imagecopy($final_img, $src, 0, 0, $src_widthx, $src_heighty, $props['w'], $props['h']);
header('Content-Type: image/png');
imagepng($final_img);
When you rotate a square of nXm pixels by lets say 45 degrees you will get the diagonals(which are bigger than n or m and equal sqrt(n^2+m^2)) of the image be the new rotated image width and height.
The function crops the rotated image using the original dimensions of the image, namely n and m.
A way to fix the problem would be by crating a bigger blank image with the appropriate size, sqrt(width_original_image^2+height_original_image^2), and than copy the original image to the new image using imagecopy. After that you can use imagerotate on the new image
I installed and used the ImageMagick PHP library and the rotations show uncropped, no matter the degree of rotation.

GD adding border to image not working

So I have PHP class and I'm trying to add white border with spacing around the uploaded image:
private function __draw_white_border()
{
// get source image and dimensions.
$src = $this->file_data['im'];
$src_w = imagesx($src);
$src_h = imagesy($src);
// create destination image with dimensions increased from $src for borders.
$dest_w = $src_w + 10;
$dest_h = $src_h + 10;
$dest = imagecreatetruecolor($dest_w, $dest_h);
// draw white border (no need for black since new images default to that).
imagerectangle($dest, 1, 1, $dest_w - 2, $dest_h - 2, 0x00ffffff);
imagerectangle($dest, 0, 0, $dest_w - 1, $dest_h - 1, 0x00ffffff);
// copy source image into destination image.
imagecopy($dest, $src, 5, 5, 0, 0, $src_w, $src_h);
}
this function works good when not in the class but with this class its not working. It seems that this function don't save image or something, I dont know.. It should load image from $this->file_data['im'] i think and save to the same destination for further actions. Where is the problem here?
you need to create 2 images and layer the main image on top of the slightly larger second image. This way the larger image will be seen around the edge of the main image making it look like a border.
http://php.net/manual/en/function.imagecopy.php
I use this to layer the images correctly. If the destination image is larger lets say 100x100px. The source image is 80x80px. You will add 10 to the x and y of the source image which will place the source image in the center of the 100x100px image. If the 100x100px image is a plain black image it will generate a clean black border
You are not putting the amended image back anywhere where you can get to it again after this method is run. This code (see last line) puts it back where the original image came from.
private function __draw_white_border()
{
// get source image and dimensions.
$src = $this->file_data['im'];
$src_w = imagesx($src);
$src_h = imagesy($src);
// create destination image with dimensions increased from $src for borders.
$dest_w = $src_w + 10;
$dest_h = $src_h + 10;
$dest = imagecreatetruecolor($dest_w, $dest_h);
// draw white border (no need for black since new images default to that).
imagerectangle($dest, 1, 1, $dest_w - 2, $dest_h - 2, 0x00ffffff);
imagerectangle($dest, 0, 0, $dest_w - 1, $dest_h - 1, 0x00ffffff);
// copy source image into destination image.
imagecopy($dest, $src, 5, 5, 0, 0, $src_w, $src_h);
$this->file_data['im'] = $dest;
}

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.

Categories