$im = imagecreatefrompng('./test.png');
$white = imagecolorallocate($im, 255, 255, 255);
imagecolortransparent($im, $white);
This code is fine for removing pure white from an image, but I what I want to do is convert all colors to alpha percentages. For example medium gray would be 50% transparent black. So an image that was medium gray would show the image behind it, if placed on top.
Is this possible using PHP or an extension?
I just wrote and tested this script for you.
$imfile = './test.png';
$origim = imagecreatefrompng($imfile);
$im_size = getimagesize($imfile);
$im = imagecreatetruecolor($im_size[0], $im_size[1]);
imagealphablending($im, false);
imagesavealpha($im, true);
for ($x = 0; $x < $im_size[0]; ++$x){
for ($y = 0; $y < $im_size[1]; ++$y){
$rgb = imagecolorat($origim,- $x, $y);
$r = ($rgb >> 16) & 0xFF;
$g = ($rgb >> 8) & 0xFF;
$b = $rgb & 0xFF;
$color = imagecolorallocatealpha($im, 0, 0, 0, intval(($r+$g+$b)/3/255*127));
imagesetpixel($im, $x, $y, $color);
}
}
It may be a bit slow on large images, but it works just like you want it to. =)
Hope this answer is Acceptable. Good luck.
Related
I'm trying to re-create a script I made in JavaScript; it bends an image by changing each pixels positions. I'm currently trying to port this to PHP, except when retrieving pixels and setting them, it doesn't appear correctly (I currently dont change the pixel locations). Below is my attempt:
<?php
header ('Content-Type: image/png');
$url = "https://www.google.co.uk/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png";
list($width, $height, $type, $attr) = getimagesize($url);
$original = imagecreatefrompng($url); //Google's logo
$image = imagecreate($width, $height); // our new image
//loop through all pixels..
for ($x = 0;$x<$width;$x++){
for ($y = 0;$y<$height;$y++){
$rgb = imagecolorat($original, $x,$y); //get colour data from $x $y coordinates
$r = ($rgb >> 16) & 0xFF;
$g = ($rgb >> 8) & 0xFF;
$b = $rgb & 0xFF;
imagesetpixel($image,$x,$y,imagecolorallocate($image,$r,$g,$b));
//set pixel data on our new image at the same location with the same data
}
}
//retain a PNG's transparency
imagecolortransparent($image, imagecolorallocatealpha($image, 0, 0, 0, 127));
imagealphablending($image, false);
imagesavealpha($image, true);
imagepng($image);
imagedestroy($image);
imagedestroy($original);
?>
Here you can see (for testing purposes) I am using Google's logo, and im attempting to re-draw it pixel by pixel.. Below is the result. For some reason it only generates a very small portion of the image.. Any ideas?
Edit:
Here's the solution. As there isn't support for transparency using imagecreatetruecolor() I instead fill the image first with a white rectangle (if there is a solution for transparency, please create an answer).
<?php
header ('Content-Type: image/png');
$url = "https://www.google.co.uk/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png";
list($width, $height, $type, $attr) = getimagesize($url);
$source = imagecreatefrompng($url);
$image = imagecreatetruecolor($width, $height);
imagefilledrectangle($image, 0, 0, $width-1, $height-1, imagecolorallocatealpha($image, 255,255,255,0));
//fill with white rectangle to look transparent, but note that it isn't. Changing alpha channel merely shows
//a black background.
for ($x = 0;$x<$width;$x++){
for ($y = 0;$y<$height;$y++){
$rgb = imagecolorat($source, $x,$y);
$r = ($rgb >> 16) & 0xFF;
$g = ($rgb >> 8) & 0xFF;
$b = $rgb & 0xFF;
$colors = imagecolorsforindex($image, $rgb);
imagesetpixel($image,$x,$y,imagecolorallocatealpha($image,$colors["red"],$colors["green"],$colors["blue"],$colors["alpha"]));
//getting alpha from pixels for a real 'copy'
}
}
imagepng($image);
imagedestroy($image);
imagedestroy($source);
?>
I am losing color information, seemingly the blue channel, from an image after using GD to read from the WebP version and output a JPEG. Why does this happen and how can I fix it?
Original Image
Code
$pic = imagecreatefromwebp('https://lh4.ggpht.com/uaLB-tbci94IubJJhDZ4n6vJwGF4i9MpFLXl28LBHjVzLIy-K6fhoSILbM4yJcKqq9I=h900-rw');
imagejpeg($pic, './example.jpg', 80);
imagedestroy($pic);
Resulting Image
pic here http://savepic.ru/7812459.png
This looks like PHP bug #70102, "imagecreatefromwebm() shifts colors".
It is fixed in PHP >= 5.6.12 (release notes).
Your script works correctly for me (there is no yellow tint) in PHP 5.6.13
Seems like there's couple of bits too many...
function fixBlue($im)
{
$f_im = imagecreatetruecolor(imagesx($im),imagesy($im));
$c = imagecolorallocate($f_im, 255, 255, 255);
imagefill($f_im, 0, 0, $c);
for($y=0;$y<imagesy($im);$y++)
{
for($x=0;$x<imagesx($im);$x++)
{
$rgb_old = imagecolorat($im, $x, $y);
$r = ($rgb >> 24) & 0xFF;
$g = ($rgb >> 16) & 0xFF;
$b = ($rgb >> 8) & 0xFF;
$pixelcolor = imagecolorallocate($f_im, $r, $g, $b);
imagesetpixel($f_im, $x, $y, $pixelcolor);
}
}
return $f_im;
}
and then just:
$img = imagecreatefromwebp('https://lh4.ggpht.com/uaLB-tbci94IubJJhDZ4n6vJwGF4i9MpFLXl28LBHjVzLIy-K6fhoSILbM4yJcKqq9I=h900-rw');
$img_f = fixBlue($img, 60);
ob_end_clean();
header('Content-type: image/jpeg');
imagejpeg($img_f, null, 80);
imagedestroy($img);
imagedestroy($img_f);
I am trying to get my PNGs converted into grayscale and it almost works properly with this code:
$image = imagecreatefromstring(file_get_contents($this->image_dest."".$this->file_name));
imagefilter($image, IMG_FILTER_GRAYSCALE);
imagepng($image, $this->image_dest."".$this->file_name);
The problem is, when the image has some transparency, the transparent pixels are being rendered as black. I see there are others who have had the same question in part of their question, but it was never answered about this issue specifically.
I hope someone can help out with this!
If it helps, I was previously using this snippet to convert to greyscale, but it has the same problem with the transparent pixels in pngs being converted to black and I am not sure
how to detect the transparency and convert it using the imagecolorat function.
//Creates the 256 color palette
for ($c=0;$c<256;$c++){
$palette[$c] = imagecolorallocate($new,$c,$c,$c);
}
//Creates yiq function
function yiq($r,$g,$b){
return (($r*0.299)+($g*0.587)+($b*0.114));
}
//Reads the origonal colors pixel by pixel
for ($y=0;$y<$h;$y++) {
for ($x=0;$x<$w;$x++) {
$rgb = imagecolorat($new,$x,$y);
$r = ($rgb >> 16) & 0xFF;
$g = ($rgb >> 8) & 0xFF;
$b = $rgb & 0xFF;
//This is where we actually use yiq to modify our rbg values, and then convert them to our grayscale palette
$gs = yiq($r,$g,$b);
imagesetpixel($new,$x,$y,$palette[$gs]);
}
}
Okay, this was mostly borrowed.. Don't quite remember where, but it should work:
//$im is your image with the transparent background
$width = imagesx($im);
$height = imagesy($im);
//Make your white background to overlay the original image on ($im)
$bg = imagecreatetruecolor($width, $height);
$white = imagecolorallocate($bg, 255, 255, 255);
//Fill it with white
imagefill($bg, 0, 0, $white);
//Merge the two together
imagecopyresampled($bg, $im, 0, 0, 0, 0, $width, $height, $width, $height);
//Convert to gray-scale
imagefilter($bg, IMG_FILTER_GRAYSCALE);
Hope that helps!
So I'm using a method for re-sizing images that I came across on here the other day and it works great for the initial image it processes but I use the process a second time to create a smaller image to use for a thumb nail. When this second thumb nail is created it comes out with a black background where it should be transparent. I know this is a common problem as I have seen plenty of threads on here with similar complaints. When I was looking last night in the php error logs it was saying that imagecolorat was trying to hit pixels which were out of bounds.....
PHP Notice: imagecolorat(): 0,28 is out of bounds in C:\inetpub\
At least I know whats happening but what I need to know now is how to fix it. Its really strange that for the larger copy of the same image I dont get this error at all and it works just fine producing a nice copy of the larger uploaded copy. Heres the code that Im using to resize everything:
$image_source = imagecreatefromstring($markericon); //original image
$imgHead = "image/jpeg"; //tag for image to be pulled later. images go in are png's
//code to make sure image is never larger than certain size
$image_width = imagesx($image_source);
$image_height = imagesy($image_source);
if($image_width>$max_upload_large_side || $image_height >$max_upload_large_side){
$proportionslarge = $image_width/$image_height;
if($image_width>$image_height){
$new_large_width = $max_upload_large_side;
$new_large_height = round($max_upload_large_side/$proportionslarge);
}
else{
$new_large_height = $max_upload_large_side;
$new_large_width = round($max_upload_large_side*$proportionslarge);
}
$new_large_image = imagecreatetruecolor($new_large_width , $new_large_height);
//code used to retain image transparency
//over write alpha chanel of image destination
imagealphablending($new_large_image, false); // Overwrite alpha
imagesavealpha($new_large_image, true);
// Create a separate alpha channel to blend images with
$alpha_image = imagecreatetruecolor($image_width, $image_height);
imagealphablending($alpha_image, false); // Overwrite alpha
imagesavealpha($alpha_image, true);
//copy data at every pixel in image
for ($x = 0; $x < $image_width; $x++) {
for ($y = 0; $y < $image_height; $y++) {
$alpha = (imagecolorat($image_source, $x, $y) >> 24) & 0xFF;
$color = imagecolorallocatealpha($alpha_image, 0, 0, 0, $alpha);
imagesetpixel($alpha_image, $x, $y, $color);
}
}
// Resize image to destination, using gamma correction
imagegammacorrect($image_source, 2.2, 1.0);
imagecopyresampled($new_large_image, $image_source, 0, 0, 0, 0, $new_large_width, $new_large_height, $image_width, $image_height);
imagegammacorrect($new_large_image, 1.0, 2.2);
// Resize alpha channel
$alpha_resized_image = imagecreatetruecolor($new_large_width, $new_large_height);
imagealphablending($alpha_resized_image, false);
imagesavealpha($alpha_resized_image, true);
imagecopyresampled($alpha_resized_image, $alpha_image, 0, 0, 0, 0, $new_large_width, $new_large_height, $image_width, $image_height);
// Copy alpha channel back to resized image
for ($x = 0; $x < $new_large_width; $x++) {
for ($y = 0; $y < $new_large_height; $y++) {
$alpha = (imagecolorat($alpha_resized_image, $x, $y) >> 24) & 0xFF;
$rgb = imagecolorat($new_large_image, $x, $y);
$r = ($rgb >> 16 ) & 0xFF;
$g = ($rgb >> 8 ) & 0xFF;
$b = $rgb & 0xFF;
$color = imagecolorallocatealpha($new_large_image, $r, $g, $b, $alpha);
imagesetpixel($new_large_image, $x, $y, $color);
}
}
// end of first run//
ob_start(); // Start capturing stdout.
imagePNG($new_large_image);
$lBinaryThumbnail = ob_get_contents(); // the raw jpeg image data.
ob_end_clean(); // Dump the stdout so it does not screw other output.
$lBinaryThumbnail = addslashes($lBinaryThumbnail);
imagedestroy($new_large_image);
} else $lBinaryThumbnail = $imgData;
//start of second image run
if($image_width>$max_upload_small_side || $image_height >$max_upload_small_side){
$proportionssmall = $image_width/$image_height;
if($image_width>$image_height){
$new_small_width = $max_upload_small_side;
$new_small_height = round($max_upload_small_side/$proportionssmall);
}
else{
$new_small_height = $max_upload_small_side;
$new_small_width = round($max_upload_small_side*$proportionssmall);
}
$new_small_image = imagecreatetruecolor($new_small_width , $new_small_height);
//////////////////////////////////////////////////////////////////////////////////
//over write alpha chanel of image destination
imagealphablending($new_small_image, false); // Overwrite alpha
imagesavealpha($new_small_image, true);
// Create a separate alpha channel to blend images with
$alpha_image = imagecreatetruecolor($image_width, $image_height);
imagealphablending($alpha_image, false); // Overwrite alpha
imagesavealpha($alpha_image, true);
//copy data at every pixel in image
for ($x = 0; $x < $image_width; $x++) {
for ($y = 0; $y < $image_height; $y++) {
$alpha = (imagecolorat($image_source, $x, $y) >> 24) & 0xFF;
$color = imagecolorallocatealpha($alpha_image, 0, 0, 0, $alpha);
imagesetpixel($alpha_image, $x, $y, $color);
}
}
// Resize image to destination, using gamma correction
imagegammacorrect($image_source, 2.2, 1.0);
imagecopyresampled($new_small_image, $image_source, 0, 0, 0, 0, $new_small_width , $new_small_height, $image_width, $image_height);
imagegammacorrect($new_small_image, 1.0, 2.2);
// Resize alpha channel
$alpha_resized_image = imagecreatetruecolor( $image_width, $image_height);
imagealphablending($alpha_resized_image, false);
imagesavealpha($alpha_resized_image, true);
imagecopyresampled($alpha_resized_image, $alpha_image, 0, 0, 0, 0, $new_small_width ,$new_small_height, $image_width, $image_height);
// Copy alpha channel back to resized image
for ($x = 0; $x < $new_small_width; $x++) {
for ($y = 0; $y < $new_small_height; $y++) {
$alpha = (imagecolorat($alpha_resized_image, $x, $y) >> 24) & 0xFF;
$rgb = imagecolorat($new_small_image, $x, $y); //this is the line that throws the error first and gives a out of bounds related error.
$r = ($rgb >> 16 ) & 0xFF;
$g = ($rgb >> 8 ) & 0xFF;
$b = $rgb & 0xFF;
$color = imagecolorallocatealpha($new_small_image, $r, $g, $b, $alpha);
imagesetpixel($new_small_image, $x, $y, $color);
}
}
// end of new code //
//imagecopyresampled($new_small_image, $image_source, 0, 0, 0, 0, $new_small_width, $new_small_height, $image_width, $image_height);
ob_start(); // Start capturing stdout.
imagePNG($new_small_image);
$sBinaryThumbnail = ob_get_contents(); // the raw jpeg image data.
ob_end_clean(); // Dump the stdout so it does not screw other output.
$sBinaryThumbnail = addslashes($sBinaryThumbnail);
imagedestroy($new_small_image);
} else $sBinaryThumbnail = $lBinaryThumbnail;
imagedestroy($image_source);
Like I said everything works fine for the first run but for some reason on the second run it farts and throws a out of bounds error. it starts at 0,28 and goes on until the end of the loop process at 50,50
I figured it out :) I took the code I had up above that created the alpha correct copy and turned it all into its own function and now it works just fine.
Is there a reasonably straightforward way to copy a circular area from one image resource to another? Something like imagecopymerge except with circles or ovals etc?
If possible, I want to avoid having to use pre-created image files (any oval shape should be possible), and if there's transparency colours involved they should naturally leave the rest of the image alone.
Reason I'm asking, I have a few classes that allow to apply image operations inside a "selected area" of an image, which works by first deleting that area from a copy of the image, then overlaying the copy back on the original. But what if you want to select a rectangle, and then inside that deselect a circle, and have the operations only affect the area that's left?
You could try this:
Start with original image - $img
Copy that image to a png - $copy
Create a mask png image of the area you want in the circle/ellipse (a 'magicpink' image with a black shape on it, with black set to the colour of alpha transparency) - $mask
Copy $mask over the top of $copy maintaining the Alpha transparency
Change what you need to on $copy
Copy $copy back over $img maintaining the Alpha transparency
// 1. Start with the original image
$img = imagecreatefromjpeg("./original.jpg");
$img_magicpink = imagecolorallocatealpha($img, 255, 0, 255, 127);
//imagecolortransparent($img, $img_magicpink);
// (Get its dimensions for copying)
list($w, $h) = getimagesize("./original.jpg");
// 2. Create the first copy
$copy = imagecreatefromjpeg("./original.jpg");
imagealphablending($copy, true);
$copy_magicpink = imagecolorallocate($copy, 255, 0, 255);
imagecolortransparent($copy, $copy_magicpink);
// 3. Create the mask
$mask = imagecreatetruecolor($w, $h);
imagealphablending($mask, true);
// 3-1. Set the masking colours
$mask_black = imagecolorallocate($mask, 0, 0, 0);
$mask_magicpink = imagecolorallocate($mask, 255, 0, 255);
imagecolortransparent($mask, $mask_black);
imagefill($mask, 0, 0, $mask_magicpink);
// 3-2. Draw the circle for the mask
$circle_x = $w/2;
$circle_y = $h/2;
$circle_w = 150;
$circle_h = 150;
imagefilledellipse($mask, $circle_x, $circle_y, $circle_w, $circle_h, $mask_black);
// 4. Copy the mask over the top of the copied image, and apply the mask as an alpha layer
imagecopymerge($copy, $mask, 0, 0, 0, 0, $w, $h, 100);
// 5. Do what you need to do to the image area
// My example is turning the original image gray and leaving the masked area as colour
$x = imagesx($img);
$y = imagesy($img);
$gray = imagecreatetruecolor($x, $y);
imagecolorallocate($gray, 0, 0, 0);
for ($i = 0; $i > 16) & 0xFF;
$g = ($rgb >> 8) & 0xFF;
$b = $rgb & 0xFF;
//for gray mode $r = $g = $b
$color = max(array($r, $g, $b));
$gray_color = imagecolorexact($img, $color, $color, $color);
imagesetpixel($gray, $i, $j, $gray_color);
}
}
// 6. Merge the copy with the origianl - maintaining alpha
imagecopymergegray($gray, $copy, 0, 0, 0, 0, $w, $h, 100);
imagealphablending($gray, true);
imagecolortransparent($gray, $mask_magicpink);
header('Content-Type: image/png');
imagepng($gray);
imagedestroy($gray);