Merging two PNG images with the smaller image behind with GD - php

I am attempting to merge two png images by placing a smaller png behind an image with a "hole" in the center with transparency.
The "Front" image is $src in this example
The "Back" image is $dest in the example
So far, i've gotten it to work in reverse (by putting the $dest image / smaller image in front) using the following code:
imagecopymerge($src, $dest, 300, 150, 0, 0, 150, 150, 100);
However, i'm not sure how to do it with the smaller image "Behind" the bigger image so that it fits perfectly in the hole.
Do I need to recreate the image ($dest) as a larger image (500 x 500) to "paste" the $src image over top of with 0 offset? This stuff is confusing :S

Figured it out.
First I merged the smaller image onto a blank image below that matched the larger image.
Then, I merged the image with the hole onto the new image created above. See as follows:
// Get size of larger image
$sz = getimagesize("larger.jpg");
// Create resources
$backing = imagecreatetruecolor($sz[0],$sz[1]);
$img1 = imagecreatefrompng("larger.jpg");
$img2 = imagecreatefrompng("smaller.jpg");
// Merge backing
imagecopymerge($backing, $img2, 300, 150, 0, 0, 150, 150, 100);
// Merge main
imagecopymerge($backing,$img1, 0, 0, 0, 0, $sz[0], $sz[1], 100);
// Save new image
imagepng($backing,$save);
// Destroy resources
imagedestroy($backing);
imagedestroy($img1);
imagedestroy($img2);
Hope this helps someone!

Related

Crop and Resize image from X and Y Position

I'm trying to crop then resize an image on PHP v5.4, I've read these resources
Put PNG over a JPG in PHP
http://php.net/manual/en/function.imagecopy.php
http://php.net/manual/en/function.imagecopyresampled.php
http://php.net/manual/en/function.call-user-func-array.phPP
PHP watermarking
http://php.net/manual/en/function.imagecreatetruecolor.php
My code is based off the answer from Cropping image in PHP (the dimensions between these images vary alot).
I want to resize this image from 1151x768 to 200x82 and crop the background section at x: 0, y: 686
I'd prefer not bloating the question with the entire 600 lines in this question, $output refers to setwidth1200nzpioneerthursday08398 image
<?php
$output = imagecreatefromjpeg("setwidth1200nzpioneerthursday08398.jpg");
$source_crop_image = imagecreatetruecolor(200, 82);
if(!is_resource($source_crop_image)) {
return $source_crop_image;
}
imagealphablending($output, true);
$source_copy_result = imagecopy($output, $source_crop_image, 0, 0, 0, 686, 200, 82);
$source_copy_result = (bool) $source_copy_result;
if(!$source_copy_result) {
return false;
}
$source_image_result = imagejpeg($source_crop_image, "images/mynewimage.jpg");
$source_image_result = (bool) $source_image_result;
?>
My Image setwidth1200nzpioneerthursday08398
Ideally I'm trying to get it crop the RED SECTION, while keeping the scale intact then resizing to 200x82
My Result
My Expected Result (I created this image using GIMP).
I have no idea why my resulting image is a black box ..
You have imagecopy() arguments in wrong order.
The right one is $source_copy_result = imagecopy($source_crop_image, $output, 0, 0, 0, 686, 200, 82);

Calculate nearest possible dimension value PHP (keeping ratio)

I am trying to implement API for image resizing. It is created not exactly for image processing, this is only one part/feature of API.
What I want to implement.
I have url for retrieving image from server it looks like
mywebpage.com/api/product/42/image
This url will return URL to full image of product with id 42.
Everything is ok.
We can specify desired size with GET parameters like this
mywebpage.com/api/product/42/image?width=200&height=300
It also looks fine
But my question if following.
As we can have different images on server with different dimension and aspect ratio, I need to keep this ratio while resizing.
For example I need image to fit 200x300 container but I have 1024x576 (16:9) image on the server. I need to resize this image but keep initial aspect ratio(16:9) but to fit desired container.
How can I efficiently calculate new image size to return depending on incoming desired dimension and current image aspect ratio.
I want to thank everyone in advance for any help or advises.
Here is a script I used to make similar thing. Quite old , so may be not up to date.
<?php
if( isset($_GET["width"]) && is_numeric($_GET["width"]))
$target_width = intval($_GET["width"]);
else
$target_width= 200;//default value
if( isset($_GET["height"]) && is_numeric($_GET["height"]))
$target_height = intval($_GET["width"]);
else
$target_height= 300;//default value
if( isset($_GET["id"]) && is_numeric($_GET["id"]))//prevent any unwanted filesystem access
$original_image_path = "img/products/$id.jpg";
else
$original_image_path = "placeholder.png"
//http://php.net/manual/fr/function.getimagesize.php
$image_size = getimagesize($original_image_path);
//get the ratio of the original image
$image_ratio= $image_size[1]/ $image_size[0];
$original_image = imagecreatefromjpeg($original_image_path);
$new_image = imagecreatetruecolor($target_width, $image_ratio * $target_width);
//paints the image in white
//http://php.net/manual/en/function.imagefill.php
//http://php.net/manual/en/function.imagecolorallocatealpha.php
imagefill( $new_image, 0, 0, imagecolorallocatealpha($new_image, 255,255,255,127) );
imagesavealpha($new_image, TRUE);
/*
Copies the original to the new, preserving the ratio.
The original image fills all the width of the new,
and is placed on the top of the new.
http://php.net/manual/en/function.imagecopyresized.php
*/
imagecopyresized(
$new_image,$original_image,
0,0,
0,0,
$target_width,$image_ratio * $target_width,
$image_size[0],$image_size[1]
);
//image is returned in response to the request
header ("Content-type: image/png");
imagepng( $new_image );
?>
Well if you need to always fit a container of 200x300 (or what ever is passed through the URL), you may not because able to simply resize it because as you are aware it will affect the images aspect ratio.
If this is the case what you can do is resize the image to the closest size then crop the remainder of the image.
I assume you will be using imagemagick for this. Have you checked out the documentation? The cropThumbnailImage method does what I just explained.
Example usage:
/* Read the image */
$im = new imagick( "test.png" );
/* create the thumbnail */
$im->cropThumbnailImage( 80, 80 );
/* Write to a file */
$im->writeImage( "th_80x80_test.png" );
http://php.net/manual/en/imagick.cropthumbnailimage.php

How to create and to merge a 2D transparent barcode on top of a JPG image in PHP?

Could you please see the below code and let me know how to write the $png on top of the $jpg image? The code shows the bacode image in the browser, but it does not save it and merge it with $jpg image. thanks for taking time and help.
$jpg= imagejpeg($image, '/applications/AMPPS/www/files/final-image-2.jpg');
require_once(dirname(__FILE__).'/tcpdf/tcpdf_barcodes_2d.php');
// set the barcode content and type
$barcodeobj = new TCPDF2DBarcode('Hello-this-is-a-test', 'DATAMATRIX');
// output the barcode as PNG image
$barcodeobj->getBarcodePNG(6, 6, array(0,0,0));
// the code stops to work from here
$png= imagepng($barcodeobj, '/applications/AMPPS/www/certificates/barcode.jpg');
imagecopymerge($png, $jpg, 0, 0, 0, 0, 50, 50, 100);
imagedestroy($image);
imagedestroy($png);

PHP ImagickDraw with outlined text issues

I'm learning and practicing my Imagick skills.
I have issues with outlined text using Imagick stroke. I would like to achieve an effect visible on this image: a popular Internet meme:
Here's the code I have so far:
$draw = new \ImagickDraw();
$outputImage = new \Imagick('meme.jpg');
$draw->setFillColor('#fff');
$draw->setFont('impact.ttf');
$draw->setFontSize(40);
$draw->setGravity(\Imagick::GRAVITY_NORTH);
$draw->setStrokeColor('#000');
$draw->setStrokeWidth(1);
$draw->setStrokeAntialias(true);
$draw->setTextAntialias(true);
$outputImage->annotateImage($draw, 0, 5, 0, 'Sample text');
$outputImage->setFormat('png');
$outputImage->writeimage('tmp/meme.png');
The issue: text stroke does not look nice. I've found a tip on Imagick discussion board about annotating image second time, but without stroke. Does not work.
Before writing image:
$draw->setStrokeColor('transparent');
$outputImage->annotateImage($draw, 0, 5, 0, 'Sample text');
Can anybody give me a clue?
Update
To conclude, my generated image looks as following:
As you can see, I have some issues with 2px stroke while using different font size. On big fonts, it looks nice but with smaller font there are some issues with the stroke and font.
Version1: resizing
Version 2: composite over and resizing
Version 2 gives a much better result. See code below. Depending on the final size, you need to play around with font and stroke size, as the resizing may give unwanted effects. You may also try version 2 without resizing.
Version 1: resizing
$draw = new ImagickDraw();
$draw->setFillColor('#fff');
$draw->setFont('impact.ttf');
$draw->setFontSize(100); //use a large font-size
$draw->setStrokeColor('#000');
$draw->setStrokeWidth(4);
$draw->setStrokeAntialias(true); //try with and without
$draw->setTextAntialias(true); //try with and without
$outputImage = new Imagick();
$outputImage->newImage(1400,400, "transparent"); //transparent canvas
$outputImage->annotateImage($draw, 20, 100, 0, 'STOP ME FROM MEMEING');
$outputImage->trimImage(0); //Cut off transparent border
$outputImage->resizeImage(300,0, imagick::FILTER_CATROM, 0.9, false); //resize to final size
/*
Now you can compositve over the image
*/
//Clean up
$draw->clear();
$draw->destroy();
$outputImage->clear();
$outputImage->destroy();
Version 2: composite over and resizing
$draw = new ImagickDraw();
$draw->setFont('impact.ttf');
$draw->setFontSize(100); //use a large font-size
$draw->setStrokeAntialias(true); //try with and without
$draw->setTextAntialias(true); //try with and without
//Create text
$draw->setFillColor('#fff');
$textOnly = new Imagick();
$textOnly->newImage(1400,400, "transparent"); /transparent canvas
$textOnly->annotateImage($draw, 21, 101, 0, 'STOP ME FROM MEMEING'); //parameters depend of stroke and text size
//Create stroke
$draw->setFillColor('#000'); //same as stroke color
$draw->setStrokeColor('#000');
$draw->setStrokeWidth(8);
$strokeImage = new Imagick();
$strokeImage->newImage(1400,400, "transparent");
$strokeImage->annotateImage($draw, 20, 100, 0, 'STOP ME FROM MEMEING');
//Composite text over stroke
$strokeImage->compositeImage($textOnly, imagick::COMPOSITE_OVER, 0, 0, Imagick::CHANNEL_ALPHA );
$strokeImage->trimImage(0); //cut transparent border
$strokeImage->resizeImage(300,0, imagick::FILTER_CATROM, 0.9, false); //resize to final size
/*
Now you can compositve over the image
*/
//Clean up
$draw->clear();
$draw->destroy();
$strokeImage->clear();
$strokeImage->destroy();
$textOnly->clear();
$textOnly->destroy();
Can you post your result or be more specific what doesn't look fine to you?
A solution could be to create the text (with stroke) first on a transaprent background and then composite it over the image. You could create the text in bigger font size and resize to make it look smoother on the final image.

php imagick, how to make an area transparent

I want to make an area transparent within an Imagick object with a specific width, height and a top position.
For example I need a transparent area with 30px x 30px from the 15th px to the top but I can't find a way to do it.
$canvas1 = new Imagick();
$canvas1->newImage(30,60,'black','png');
Please help.
This may be a slightly simpler way of doing it. I recycled #AndreKR's setup code to get started:
$im = new Imagick();
$im->newImage(100,100, 'red');
$im->setImageAlphaChannel(Imagick::ALPHACHANNEL_ACTIVATE); // make sure it has an alpha channel
$box=$im->getImageRegion(30,30,15,15);
$box->setImageAlphaChannel(Imagick::ALPHACHANNEL_TRANSPARENT);
$im->compositeImage($box,Imagick::COMPOSITE_REPLACE,15,15);
While you can flood fill with transparency ink (not transparent ink) like this:
$im->floodFillPaintImage('#FF000000', 10, '#FFFFFF', 0, 0, false);
in this post, Anthony, apparently some important figure in the ImageMagick universe, says that you cannot draw with transparency.
So it seems you have to create a punch image and then use it to punch the transparent areas out in your actual image. To create the punch here I draw the rectangle opaque on a transparent brackground and then invert the whole image:
$punch = new Imagick();
$punch->newImage(100,100, 'transparent');
$drawing = new ImagickDraw();
$drawing->setFillColor(new ImagickPixel('black'));
$drawing->rectangle(15, 15, 45, 45);
$punch->drawImage($drawing);
$punch->negateImage(true, Imagick::CHANNEL_ALPHA);
Here's the actual image before the punching:
$im = new Imagick();
$im->newImage(100,100, 'red');
$im->setImageAlphaChannel(Imagick::ALPHACHANNEL_ACTIVATE); // make sure it has
// an alpha channel
Now we can copy over the alpha channel from our punch image. For a reason unknown to me the obvious way does not work:
// Copy over the alpha channel from one image to the other
// this does NOT work, the $channel parameter seems to be useless:
// $im->compositeImage($punch, Imagick::COMPOSITE_SRC, 0, 0, Imagick::CHANNEL_ALPHA);
However, these both work:
// Copy over the alpha channel from one image to the other
// $im->compositeImage($punch, Imagick::COMPOSITE_COPYOPACITY, 0, 0);
// $im->compositeImage($punch, Imagick::COMPOSITE_DSTIN, 0, 0);
(The light blue is the background of the Windows photo viewer, indicating transparent areas.)
You can set the opacity as follows
$image->setImageOpacity(0.0);
If you set it to 0.0 the image what you have crated will become transparent
for more information you can Set opacity in Imagick
if you want it for a particular area part then you need to change the approach by using GD library functions by doing some what like this
$img = imagecreatefrompng($imgPath); // load the image
list($width,$height) = getimagesize($imgPath); // get its size
$c = imagecolortransparent($img,imagecolorallocate($img,255,1,254)); // create transparent color, (255,1,254) is a color that won't likely occur in your image
$border = 10;
imagefilledrectangle($img, $border, $border, $width-$border, $height-$border, $c); // draw transparent box
imagepng($img,'after.png'); // save
I Could see a similar requirement which is posted in another forum here
Try
$canvas1->setImageOpacity(0);

Categories