PHP: combining images as layers, casting a shadow? - php

I'm creating a program to create a product image based on choices made by the customer. The product is men's tights, with or without codpiece, with or without feet, in three different styles.
To do this, I have seventeen PNG images: background, a pair of actual feet, torso and arms above the waist (all in color); leg options all in grayscale: right leg, left leg, right codpiece, left codpiece, etc., colorized as they're laid in.
Here is the specific issue I'm trying to work out: every image is transparent (except the background) and they layer up just fine. However, the codpiece casts a shadow on the leg: right now that shadow is a black gradient fading to transparent. Combined with the picture below it, the shadow becomes a large stain (see below).
I have google-d like crazy and tried everything I can think of, but I can't find the right combination of functions. Is what I want to do possible, and if so, how?
tl;dr: How do I get one image to cast a shadow on another?
// image paths are stored in an array, built based on customer choices
$dest = imagecreatefrompng("./layers/{$layers[0][0]}.png");
for($i = 0; $i < count($layers); $i++){
imagealphablending($dest, false);
imagesavealpha($dest, true);
$src = imagecreatefrompng("./layers/{$layers[$i][0]}.png");
// apply color to the current image as appropriate
$color = $colors[$layers[$i][1]] ?? '';
if($color != ''){
colorize($src, $color);
}
// lay the current image onto the image we're building
imagecopymerge_alpha($dest, $src, 0, 0, 0, 0, 4000, 4000, 100);
}

Related

Combine two images in Php - one top of another

I'm trying to create a photo collage in php by combining two images, a background and an image.
Certain part of my background is transparent, I want to put the image into that part of the background and merge them. The transparent part could be a square, circle, oval rectangle or any other shape. I tried the following code, but it doesn't work
$image_1 = imagecreatefrompng('images/background.png');
$image_2 = imagecreatefrompng('images/image.png');
imagealphablending($image_1, true);
imagesavealpha($image_1, true);
imagecopy($image_1, $image_2, 0, 0, 0, 0, 100, 100);
imagepng($image_1, 'images/3.png');
The code above combines the two images, but with one of them being very small, and the other take up the whole screen which is not what I wanted. I need to have the the image fit into the transparent part of the background of the newly combined image.

PHP GD and transparent images sometimes work

While reading up on the issues merging transparent images in PHP, it seems that everyone is in the "it works" or "it doesn't work" camps. I have found a way to demonstrate each case.
I wish to take a PNG, cut an ALPHA hole in it and then merge it on top of another image. In this case, I cut a hole in an image from google maps, and paste it over a red block. I have two images from google maps. One is of Manhattan and one is of my house. One works, and one does not. The only difference is one was "saved" via a browser.
If you run the code below, you will see the difference in outputs. Does anyone know why the same code would treat two PNG files completely differently? It must be the difference in the PNG files themselves, but then what would it be?
<?php
sometimesworks("https://maps.googleapis.com/maps/api/staticmap?center=40.714728,-73.998672&zoom=12&size=400x400&maptype=hybrid", "google");
sometimesworks("https://lh4.googleusercontent.com/UQvV_dpBa3_rVf25pvLXKD3OwzF4FtPnHBHkzdWqjCQ5mlFqcFfId9echIgDMv_xYRRYzLaKEXphw7g=w2447-h1106", "myhouse");
function sometimesworks($p_image, $p_prefix)
{
$image_top =imagecreatefrompng($p_image);
$brush = imagecolorallocate($image_top, 0, 0, 0); //create a black brush
imagefilledpolygon($image_top, explode(",", "10,10, 120,22, 80,280, 200, 191"), 4, $brush); //fill a polygon
imagecolortransparent($image_top, $brush); //turn the black to be transparent
imagepng($image_top, './'.$p_prefix.'.transparent.png'); //save the file to confirm that it is working.
//create a big red square
$image_bottom = imagecreatetruecolor(imagesx($image_top), imagesy($image_top));
$red = imagecolorallocate($image_bottom, 255, 0, 0);
imagefill($image_bottom, 0, 0, $red);
imagepng($image_bottom, './'.$p_prefix.'.red.png');
//merge the top onto the bottom.
$out = imagecreatetruecolor(imagesx($image_top), imagesy($image_top));
imagecopy($out, $image_bottom, 0, 0, 0, 0, imagesx($image_top), imagesy($image_top));
imagecopy($out, $image_top, 0, 0, 0, 0, imagesx($image_top), imagesy($image_top));
imagepng($out, './'.$p_prefix.'.out.png');
}
The issue with the above code is that the PNG files were of different type. One was a truecolor image and the other was a palette. The code to merge files differs depending on what is being done.
This is a good reference for the merging of "palette" based PNG files with transparency.

PHP Determining and Removing Background Color of Image

I've been looking at CustomInk's design lab and I am very curious as to how they decide what color is the background color. For example, If I upload the Facebook logo, they decide to remove the blue from the image.
But if I upload a picture of an apple with a white background, then they remove the white in a similar fashion. (even though the white background is not the dominant color)
Using ImageMagick in PHP, how could I also achieve this task?
I've found a solution to finding a background color in an image and removing it with PHP and Imagick. I, instead of using the edges, decided to use the corners to figure out which colors to remove, which seemed to be working most of the time. Below are some results.
Which I was able to get to turn into this
(the background image is a texture of a white woven material I found on Google). But this seemed to handle gradient backgrounds pretty decently as well.
This is a picture of a Vette with the original first, then changing all the corners' colors to transparent, and the final is one using fill starting from the corners.
$Image = new Imagick('vette.jpg');
$BackgroundColors = array(
'TopLeft' => array(1, 1),
'TopRight' => array($Image->getimagewidth(), 1),
'BottomLeft' => array(1, $Image->getimageheight()),
'BottomRight' => array($Image->getimagewidth(), $Image->getimageheight())
);
foreach ($BackgroundColors as $Key => $BG) {
$pixel = $Image->getImagePixelColor($BG[0], $BG[1]);
$colors = $pixel->getColor();
$ExcludedColors[] = sprintf("%6X",array_values($colors));
$Image->floodfillPaintImage('none', 9000, $pixel, $BG[0] - 1, $BG[1] - 1, false);
//Comment the line above and uncomment the below line to achieve the effects of the second Vette
//$Image->transparentPaintImage($pixel, 0, 9000, false);
}
$Image->writeImage("vette-no_background.png");

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

text colour in GB image not correct

I have the following code which creates an image based on whats in a mySQL db based on what has been uploaded via a form
if($bg_img){
list($img_width, $img_height, $img_type, $img_attr) = getimagesize('./images/'.$bg_img);
}
// Use image in background
$im = imagecreatefrompng(IS_DIR."/images/".$bg_img);
$fn = rgb2array($font_color);
$font_color = imagecolorallocate($im, $fn[0], $fn[1], $fn[2]);
This creates the image then assigns a font colour, and later in the code, text is added etc. All that works fine.
I have a strange issue though. I have two 8 bit PNG's. One of them is just a plain grey colour, the other one is an actual graphical image with a logo on it. If I use the basic one, the text uses the colour I have defined in $font_color (which comes from DB) without issues. If I use the more graphical one, the colour is incorrect and the imagecollorallocate doesn't seem to return a set of RGB values.
Is there something that needs to be done with certain types of PNG?

Categories