Please see this question: PHP GD Use one image to mask another image, including transparency - The rules around here say for users to create new questions rather than revisit old ones and ask for support
I've been working with this script to enable transparent masking - the (possible) difference being that the source image has transparency, but it seems like the code below only works if the input PNGs have no transparency. Can someone have a look and see if I'm doing anything wrong?
What I'm trying to do below:
1. Grab a $source image
2. Resize it and save it locally as pjg.png, maintaining transparency (this works ok)
3. Mask the resultant image with another PNG.
Info:
image.png has transparency.
mask1.png is a white oval on black background, no transparency
The image saved at the very end has black on it, when it should maintain transparency throughout.
<?php
$data = file_get_contents('assets/img/image.png');
$source = imagecreatefromstring( $data);
// Set the percentage resize
$percent = 0.5;
// Get new dimensions
list($width, $height) = getimagesize('assets/img/image.png');
$new_width = $width * $percent;
$new_height = $height * $percent;
$image_p = imagecreatetruecolor($new_width, $new_height);
imagealphablending($image_p, false);
imagesavealpha( $image_p, true );
imagecopyresampled($image_p, $source, 0, 0, 0, 0, $new_width, $new_height, $width, $height);
imagepng($image_p, "assets/img/pjg.png");
$mask_id = 1;
create_mask( $image_p, $mask_id );
function create_mask( &$picture, $mask_id) {
// Image masking using PHP
// https://stackoverflow.com/questions/7203160/php-gd-use-one-image-to-mask-another-image-including-transparency
$mask = imagecreatefrompng( 'assets/img/masks/mask'.$mask_id.'.png' ); // The mask is a white-on-black png
// Get sizes and set up new picture
$xSize = imagesx( $picture );
$ySize = imagesy( $picture );
$newPicture = imagecreatetruecolor( $xSize, $ySize );
imagesavealpha($newPicture, true);
imagefill( $newPicture, 0, 0, imagecolorallocatealpha( $newPicture, 0, 0, 0, 127 ) );
// Resize mask if necessary
if( $xSize != imagesx( $mask ) || $ySize != imagesy( $mask ) ) {
$tempPic = imagecreatetruecolor( $xSize, $ySize );
imagecopyresampled( $tempPic, $mask, 0, 0, 0, 0, $xSize, $ySize, imagesx( $mask ), imagesy( $mask ) );
imagedestroy( $mask );
$mask = $tempPic;
}
// Perform pixel-based alpha map application
for( $x = 0; $x < $xSize; $x++ ) {
for( $y = 0; $y < $ySize; $y++ ) {
$alpha = imagecolorsforindex( $mask, imagecolorat( $mask, $x, $y ) );
$alpha = 127 - floor( $alpha[ 'red' ] / 2 );
$color = imagecolorsforindex( $picture, imagecolorat( $picture, $x, $y ) );
imagesetpixel( $newPicture, $x, $y, imagecolorallocatealpha( $newPicture, $color[ 'red' ], $color[ 'green' ], $color[ 'blue' ], $alpha ) );
}
}
$salt = random_string('alnum', 8); // Another function generating a string, not important
$now = time();
$new_filename = $now."_".$salt .".png";
// Save it Locally using a unique name
imagepng($newPicture, "assets/img/uploads/cropped/".$new_filename);
// Copy back to original picture
imagedestroy( $picture );
$picture = $newPicture;
}
If anyone could point out why the output image is not keeping its transparency, there'd be a nice cold beer in it for you.
Thanks!
PJG
I've worked it out. The original script was not checking for the transparency of the source image. The below script checks the source image for pixel transparency, and acts accordingly. The below script preforms a shap mask on a PNG image, and maintains the source image's transparency.
<?php
$data = file_get_contents('assets/img/image.png');
$source = imagecreatefromstring( $data);
// Set the percentage resize
$percent = 0.5;
// Get new dimensions
list($width, $height) = getimagesize('assets/img/image.png');
$new_width = $width * $percent;
$new_height = $height * $percent;
$image_p = imagecreatetruecolor($new_width, $new_height);
imagealphablending($image_p, false);
imagesavealpha( $image_p, true );
imagecopyresampled($image_p, $source, 0, 0, 0, 0, $new_width, $new_height, $width, $height);
imagepng($image_p, "assets/img/pjg.png");
$mask_id = 1;
create_mask( $image_p, $mask_id );
function create_mask( &$picture, $mask_id) {
// Image masking using PHP
// http://stackoverflow.com/questions/7203160/php-gd-use-one-image-to-mask-another-image-including-transparency
$mask = imagecreatefrompng( 'assets/img/masks/mask'.$mask_id.'.png' ); // The mask is a white-on-black png
// Get sizes and set up new picture
$xSize = imagesx( $picture );
$ySize = imagesy( $picture );
$newPicture = imagecreatetruecolor( $xSize, $ySize );
imagesavealpha($newPicture, true);
imagefill( $newPicture, 0, 0, imagecolorallocatealpha( $newPicture, 0, 0, 0, 127 ) );
// Resize mask if necessary
if( $xSize != imagesx( $mask ) || $ySize != imagesy( $mask ) ) {
$tempPic = imagecreatetruecolor( $xSize, $ySize );
imagecopyresampled( $tempPic, $mask, 0, 0, 0, 0, $xSize, $ySize, imagesx( $mask ), imagesy( $mask ) );
imagedestroy( $mask );
$mask = $tempPic;
}
// Perform pixel-based alpha map application
for( $x = 0; $x < $xSize; $x++ ) {
for( $y = 0; $y < $ySize; $y++ ) {
$alpha = imagecolorsforindex( $mask, imagecolorat( $mask, $x, $y ) );
if(($alpha['red'] == 0) && ($alpha['green'] == 0) && ($alpha['blue'] == 0) && ($alpha['alpha'] == 0))
{
// It's a black part of the mask
imagesetpixel( $newPicture, $x, $y, imagecolorallocatealpha( $newPicture, 0, 0, 0, 127 ) ); // Stick a black, but totally transparent, pixel in.
}
else
{
// Check the alpha state of the corresponding pixel of the image we're dealing with.
$alphaSource = imagecolorsforindex( $picture, imagecolorat( $picture, $x, $y ) );
if(($alphaSource['alpha'] == 127))
{
imagesetpixel( $newPicture, $x, $y, imagecolorallocatealpha( $newPicture, 0, 0, 0, 127 ) ); // Stick a black, but totally transparent, pixel in.
}
else
{
$color = imagecolorsforindex( $picture, imagecolorat( $picture, $x, $y ) );
imagesetpixel( $newPicture, $x, $y, imagecolorallocatealpha( $newPicture, $color[ 'red' ], $color[ 'green' ], $color[ 'blue' ], $color['alpha'] ) ); // Stick the pixel from the source image in
}
}
}
}
$salt = random_string('alnum', 8); // Another function generating a string, not important
$now = time();
$new_filename = $now."_".$salt .".png";
// Save it Locally using a unique name
imagepng($newPicture, "assets/img/uploads/cropped/".$new_filename);
// Copy back to original picture
imagedestroy( $picture );
$picture = $newPicture;
}
?>
Cheers for reading - looks like I'll be buying myself a beer later :)
PG
Related
I am trying to put an png overlay over an underlying png file.
The script works just fine, but instead of giving me the overlay colored as it should be, it is outputting it in gray color.
But only the overlay image is gray (bg-color of the overlay, which is basically a border for the underlying image: RGB 20,114,158). And it's 24-bit from PS.
The transparency part (white) works just fine.
<?php
$im = imagecreatefrompng($sourceFile);
$overlay = imagecreatefrompng($overlayFile);
$white = imagecolorallocate($overlay, 255, 255, 255);
imagecolortransparent($overlay, $white);
imagecopymerge($im, $overlay, 0, 0, 0, 0, 173, 173,100);
header('Content-Type: image/png');
imagepng($im);
?>
Any help is much appreciated!
Cheers
Chris
You should just using imagecopy() (you use 100 for the last argument ($pct) of imagecopymerge()).
pct
The two images will be merged according to pct which can range from 0 to 100.
When pct = 0, no action is taken, when 100 this function behaves identically to
imagecopy() for pallete images, except for ignoring alpha components, while it
implements alpha transparency for true colour images.
And don't forget to enable imagealphablending() on both images.
Can you try this (not my code):
<?php
/**
* PNG ALPHA CHANNEL SUPPORT for imagecopymerge();
* This is a function like imagecopymerge but it handle alpha channel well!!!
**/
// A fix to get a function like imagecopymerge WITH ALPHA SUPPORT
// Main script by aiden dot mail at freemail dot hu
// Transformed to imagecopymerge_alpha() by rodrigo dot polo at gmail dot com
function imagecopymerge_alpha($dst_im, $src_im, $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h, $pct){
if(!isset($pct)){
return false;
}
$pct /= 100;
// Get image width and height
$w = imagesx( $src_im );
$h = imagesy( $src_im );
// Turn alpha blending off
imagealphablending( $src_im, false );
// Find the most opaque pixel in the image (the one with the smallest alpha value)
$minalpha = 127;
for( $x = 0; $x < $w; $x++ )
for( $y = 0; $y < $h; $y++ ){
$alpha = ( imagecolorat( $src_im, $x, $y ) >> 24 ) & 0xFF;
if( $alpha < $minalpha ){
$minalpha = $alpha;
}
}
//loop through image pixels and modify alpha for each
for( $x = 0; $x < $w; $x++ ){
for( $y = 0; $y < $h; $y++ ){
//get current alpha value (represents the TANSPARENCY!)
$colorxy = imagecolorat( $src_im, $x, $y );
$alpha = ( $colorxy >> 24 ) & 0xFF;
//calculate new alpha
if( $minalpha !== 127 ){
$alpha = 127 + 127 * $pct * ( $alpha - 127 ) / ( 127 - $minalpha );
} else {
$alpha += 127 * $pct;
}
//get the color index with new alpha
$alphacolorxy = imagecolorallocatealpha( $src_im, ( $colorxy >> 16 ) & 0xFF, ( $colorxy >> 8 ) & 0xFF, $colorxy & 0xFF, $alpha );
//set pixel with the new color + opacity
if( !imagesetpixel( $src_im, $x, $y, $alphacolorxy ) ){
return false;
}
}
}
// The image copy
imagecopy($dst_im, $src_im, $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h);
}
// USAGE EXAMPLE:
$img_a = imagecreatefrompng('image1.png');
$img_b = imagecreatefrompng('wm2.png');
// SAME COMMANDS:
imagecopymerge_alpha($img_a, $img_b, 10, 10, 0, 0, imagesx($img_b), imagesy($img_b),50);
// OUTPUT IMAGE:
header("Content-Type: image/png");
imagesavealpha($img_a, true);
imagepng($img_a, NULL);
?>
Src: http://www.php.net/manual/en/function.imagecopymerge.php#88456
I'm having some major issues getting PNG transparency on a PNG that is being rotated.
$filename = 'bird_up.png';
$source = imagecreatefrompng($filename) or die('Error opening file '.$filename);
imagealphablending($source, false);
imagesavealpha($source, true);
$rotation = imagerotate($source, $degrees, imageColorAllocateAlpha($source, 0, 0, 0, 127));
imagealphablending($source, false);
imagesavealpha($source, true);
header('Content-type: image/png');
imagepng($rotation);
imagedestroy($source);
imagedestroy($rotation);
I've added a working commented version below
<?php
// this file writes the image into the http response,
// so we cant have any output other than headers and the file data
ob_start();
$filename = 'tibia.png';
$degrees = 45;
// open the image file
$im = imagecreatefrompng( $filename );
// create a transparent "color" for the areas which will be new after rotation
// r=0,b=0,g=0 ( black ), 127 = 100% transparency - we choose "invisible black"
$transparency = imagecolorallocatealpha( $im,0,0,0,127 );
// rotate, last parameter preserves alpha when true
$rotated = imagerotate( $im, $degrees, $transparency, 1);
// disable blendmode, we want real transparency
imagealphablending( $rotated, false );
// set the flag to save full alpha channel information
imagesavealpha( $rotated, true );
// now we want to start our output
ob_end_clean();
// we send image/png
header( 'Content-Type: image/png' );
imagepng( $rotated );
// clean up the garbage
imagedestroy( $im );
imagedestroy( $rotated );
Demo
Original image from Wikipedia
rotated -45 degrees, new areas with ~50% opacity for a demo
$transparency = imagecolorallocatealpha( $im,0,0,0,55 );
rotated -45 degress, new areas with 100% opacity
$transparency = imagecolorallocatealpha( $im,0,0,0,127 );
<?php
$text ="New"; $font = "fonts/AARDV.ttf"; $size = 100;
function html2rgb()
{
$color='FF0000';
if(strlen($color)==6)
list($r, $g, $b)=array($color[0].$color[1],$color[2].$color[3],$color[4].$color[5]);
elseif (strlen($color) == 3)
list($r, $g, $b) = array($color[0].$color[0], $color[1].$color[1], $color[2].$color[2]);
else
return false;
$r = hexdec($r);
$g = hexdec($g);
$b = hexdec($b);
return array($r, $g, $b);
}
$c=html2rgb();
$bbox = imagettfbbox($size, 0, $font, $text);
$width = abs($bbox[2] - $bbox[0]);
$height = abs($bbox[7] - $bbox[1]);
$image = imagecreatetruecolor($width, $height);
$bgcolor = imagecolorallocate($image, 1, 1,0);
imagecolortransparent($image, $bgcolor);
$color = imagecolorallocate($image, $c[0],$c[1],$c[2]);
$x = $bbox[0] + ($width / 2) - ($bbox[4] / 2);
$y = $bbox[1] + ($height / 2) - ($bbox[5] / 2);
imagefilledrectangle($image, 0, 0, $width - 1, $height - 1, $bgcolor);
imagettftext($image, $size, 0, $x, $y, $color, $font, $text);
$last_pixel= imagecolorat($image, 0, 0);
for ($j = 0; $j < $height; $j++)
{
for ($i = 0; $i < $width; $i++)
{
if (isset($blank_left) && $i >= $blank_left)
{
break;
}
if (imagecolorat($image, $i, $j) !== $last_pixel)
{
if (!isset($blank_top))
{
$blank_top=$j;
}
$blank_left=$i;break;
}
$last_pixel=imagecolorat($image, $i, $j);
}
}
$x -= $blank_left;
$y -= $blank_top;
imagefilledrectangle($image, 0, 0, $width - 1, $height - 1, $bgcolor);
imagettftext($image, $size, 0, $x, $y, $color, $font, $text);
header('Content-type: image/png');
$transparency = imagecolorallocatealpha( $image,0,0,0,127 );
$image=imagerotate($image, 10, $transparency, 1);
imagealphablending( $image, false );
imagesavealpha( $image, true );
ob_end_clean();
imagepng($image);
imagedestroy($image);
?>
This one not work..Black color
any one please answer this..
enter code here
I am trying to layer a partially transparent PNG on top of another opaque PNG.
There are many example of how to do this across this site and the net, however with every version I try I seem to not be able to maintain the foreground image's transparency.
Currently the code looks like this:
$image = imagecreatefrompng($_GET['fg']);
$frame = imagecreatefrompng($_GET['bg']);
imagealphablending($frame,true);
imagecopymerge($image, $frame, 0, 0, 0, 0, 0, 100, 100);
# Save the image to a file
$output_file = 'preview-' . time() . '.png';
imagepng( $image, $_SERVER['DOCUMENT_ROOT'] . '/share/' . $output_file );
Which produces an image made up of the foreground image with the transparent parts as white (or black).
Also I have tried this as seen in the image thumbnail generator, TimThumb, which produces the same output:
$canvas= imagecreatefrompng($_GET['bg']);
$overlay_gd_image = imagecreatefrompng( $_GET['fg'] );
$overlay_width = imagesx( $overlay_gd_image );
$overlay_height = imagesy( $overlay_gd_image );
imagealphablending($canvas, true );
imagecopy( $canvas, $overlay_gd_image, 0, 0, 0, 0, $overlay_width, $overlay_height);
imagealphablending($canvas, false );
imagesavealpha($canvas , true);
imagepng($canvas, 'new.png');
I am running out of things to try and would be grateful if anyone could shed light on the problem.
The native imagecopymerge doesn't really work so well when it comes to transparent images. If you want to merge two PNG's together and not have the alpha channel get messed up, this function found on the manual page works like a charm for me and actually preserves transparency. I have been merging PNGs nonstop with this function and have not had a problem so I think it could solve yours.
(Posted by "rodrigo dot polo at gmail dot com", so all credits to him/her.)
<?php
/**
* PNG ALPHA CHANNEL SUPPORT for imagecopymerge();
* This is a function like imagecopymerge but it handle alpha channel well!!!
**/
// A fix to get a function like imagecopymerge WITH ALPHA SUPPORT
// Main script by aiden dot mail at freemail dot hu
// Transformed to imagecopymerge_alpha() by rodrigo dot polo at gmail dot com
function imagecopymerge_alpha($dst_im, $src_im, $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h, $pct){
if(!isset($pct)){
return false;
}
$pct /= 100;
// Get image width and height
$w = imagesx( $src_im );
$h = imagesy( $src_im );
// Turn alpha blending off
imagealphablending( $src_im, false );
// Find the most opaque pixel in the image (the one with the smallest alpha value)
$minalpha = 127;
for( $x = 0; $x < $w; $x++ )
for( $y = 0; $y < $h; $y++ ){
$alpha = ( imagecolorat( $src_im, $x, $y ) >> 24 ) & 0xFF;
if( $alpha < $minalpha ){
$minalpha = $alpha;
}
}
//loop through image pixels and modify alpha for each
for( $x = 0; $x < $w; $x++ ){
for( $y = 0; $y < $h; $y++ ){
//get current alpha value (represents the TANSPARENCY!)
$colorxy = imagecolorat( $src_im, $x, $y );
$alpha = ( $colorxy >> 24 ) & 0xFF;
//calculate new alpha
if( $minalpha !== 127 ){
$alpha = 127 + 127 * $pct * ( $alpha - 127 ) / ( 127 - $minalpha );
} else {
$alpha += 127 * $pct;
}
//get the color index with new alpha
$alphacolorxy = imagecolorallocatealpha( $src_im, ( $colorxy >> 16 ) & 0xFF, ( $colorxy >> 8 ) & 0xFF, $colorxy & 0xFF, $alpha );
//set pixel with the new color + opacity
if( !imagesetpixel( $src_im, $x, $y, $alphacolorxy ) ){
return false;
}
}
}
// The image copy
imagecopy($dst_im, $src_im, $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h);
}
// USAGE EXAMPLE:
$img_a = imagecreatefrompng('image1.png');
$img_b = imagecreatefrompng('wm2.png');
// SAME COMMANDS:
imagecopymerge_alpha($img_a, $img_b, 10, 10, 0, 0, imagesx($img_b), imagesy($img_b),50);
// OUTPUT IMAGE:
header("Content-Type: image/png");
imagesavealpha($img_a, true);
imagepng($img_a, NULL);
?>
If you use the example, you'll see that $img_a would be below $img_b and instead of having a weird black box, $img_b's transparency would be preserved.
I had exactly the same issue. The solution for me was to use imagecopyresampled instead of imagecopymerge. This will preserve transparency for both images.
be aware of the slightly different parameters to pass
I am trying to create a PHP script that takes an image:
http://i.stack.imgur.com/eNvlM.png
and then applies a PNG image:
http://i.stack.imgur.com/iJr2I.png
as a mask.
The end result needs to maintain transparency:
http://i.stack.imgur.com/u0l0I.png
If at all possible I want to do this in GD, ImageMagick is not really an option right now.
How would I go about this?
phalacee's post (in "PHP/GD, how to copy a circle from one image to another?") seems to be along the right lines but I specifically need to use an image as a mask, not a shape.
Matt,
If you make your png with the oval white fill on black background instead of black fill with transparent background the following function does it.
<?php
// Load source and mask
$source = imagecreatefrompng( '1.png' );
$mask = imagecreatefrompng( '2.png' );
// Apply mask to source
imagealphamask( $source, $mask );
// Output
header( "Content-type: image/png");
imagepng( $source );
function imagealphamask( &$picture, $mask ) {
// Get sizes and set up new picture
$xSize = imagesx( $picture );
$ySize = imagesy( $picture );
$newPicture = imagecreatetruecolor( $xSize, $ySize );
imagesavealpha( $newPicture, true );
imagefill( $newPicture, 0, 0, imagecolorallocatealpha( $newPicture, 0, 0, 0, 127 ) );
// Resize mask if necessary
if( $xSize != imagesx( $mask ) || $ySize != imagesy( $mask ) ) {
$tempPic = imagecreatetruecolor( $xSize, $ySize );
imagecopyresampled( $tempPic, $mask, 0, 0, 0, 0, $xSize, $ySize, imagesx( $mask ), imagesy( $mask ) );
imagedestroy( $mask );
$mask = $tempPic;
}
// Perform pixel-based alpha map application
for( $x = 0; $x < $xSize; $x++ ) {
for( $y = 0; $y < $ySize; $y++ ) {
$alpha = imagecolorsforindex( $mask, imagecolorat( $mask, $x, $y ) );
$alpha = 127 - floor( $alpha[ 'red' ] / 2 );
$color = imagecolorsforindex( $picture, imagecolorat( $picture, $x, $y ) );
imagesetpixel( $newPicture, $x, $y, imagecolorallocatealpha( $newPicture, $color[ 'red' ], $color[ 'green' ], $color[ 'blue' ], $alpha ) );
}
}
// Copy back to original picture
imagedestroy( $picture );
$picture = $newPicture;
}
?>
Here's a little upgrade to this script - I found that if the source image has transparency itself, the mask (using the script above) plots a back pixel instead of the source image's transparent pixel. The below extended script takes source image transparency into consideration, and preserves it.
// Load source and mask
$source = imagecreatefrompng( '1.png' );
$mask = imagecreatefrompng( '2.png' );
// Apply mask to source
imagealphamask( $source, $mask );
// Output
header( "Content-type: image/png");
imagepng( $source );
function imagealphamask( &$picture, $mask ) {
// Get sizes and set up new picture
$xSize = imagesx( $picture );
$ySize = imagesy( $picture );
$newPicture = imagecreatetruecolor( $xSize, $ySize );
imagesavealpha( $newPicture, true );
imagefill( $newPicture, 0, 0, imagecolorallocatealpha( $newPicture, 0, 0, 0, 127 ) );
// Resize mask if necessary
if( $xSize != imagesx( $mask ) || $ySize != imagesy( $mask ) ) {
$tempPic = imagecreatetruecolor( $xSize, $ySize );
imagecopyresampled( $tempPic, $mask, 0, 0, 0, 0, $xSize, $ySize, imagesx( $mask ), imagesy( $mask ) );
imagedestroy( $mask );
$mask = $tempPic;
}
// Perform pixel-based alpha map application
for( $x = 0; $x < $xSize; $x++ ) {
for( $y = 0; $y < $ySize; $y++ ) {
$alpha = imagecolorsforindex( $mask, imagecolorat( $mask, $x, $y ) );
if(($alpha['red'] == 0) && ($alpha['green'] == 0) && ($alpha['blue'] == 0) && ($alpha['alpha'] == 0))
{
// It's a black part of the mask
imagesetpixel( $newPicture, $x, $y, imagecolorallocatealpha( $newPicture, 0, 0, 0, 127 ) ); // Stick a black, but totally transparent, pixel in.
}
else
{
// Check the alpha state of the corresponding pixel of the image we're dealing with.
$alphaSource = imagecolorsforindex( $source, imagecolorat( $source, $x, $y ) );
if(($alphaSource['alpha'] == 127))
{
imagesetpixel( $newPicture, $x, $y, imagecolorallocatealpha( $newPicture, 0, 0, 0, 127 ) ); // Stick a black, but totally transparent, pixel in.
}
else
{
$color = imagecolorsforindex( $source, imagecolorat( $source, $x, $y ) );
imagesetpixel( $newPicture, $x, $y, imagecolorallocatealpha( $newPicture, $color[ 'red' ], $color[ 'green' ], $color[ 'blue' ], $color['alpha'] ) ); // Stick the pixel from the source image in
}
}
}
}
// Copy back to original picture
imagedestroy( $picture );
$picture = $newPicture;
}
I like your script, good idea to remove extra colour information when the pixel is totally transparent. I should point out just a small error (IMO) though if anyone wants to use this method.
$color = imagecolorsforindex( $source, imagecolorat( $source, $x, $y ) );
should be
$color = imagecolorsforindex( $picture, imagecolorat( $picture, $x, $y ) );
also I'm not 100% sure why you're checking rgb values here if the pixel is 100% transparent
if(($alpha['red'] == 0) && ($alpha['green'] == 0) && ($alpha['blue'] == 0) && ($alpha['alpha'] == 0))
...
and I'm not sure alpha blending from the mask file would work well with your method, as its only used when rgba values all equal 0.
Jules's script is pretty good too, though it expects the mask to be a grey scale representation of a mask (which is pretty common practice).
In Matt's query he was after a script that grabs just the alpha transparency from an existing image and applies it to another image. Here's a simple mod of Jules's script just to grab the alpha from the mask image, and preserve the alpha of the source image.
<?php
// Load source and mask
$source = imagecreatefrompng( '1.png' );
$mask = imagecreatefrompng( '2.png' );
// Apply mask to source
imagealphamask( $source, $mask );
// Output
header( "Content-type: image/png");
imagepng( $source );
function imagealphamask( &$picture, $mask ) {
// Get sizes and set up new picture
$xSize = imagesx( $picture );
$ySize = imagesy( $picture );
$newPicture = imagecreatetruecolor( $xSize, $ySize );
imagesavealpha( $newPicture, true );
imagefill( $newPicture, 0, 0, imagecolorallocatealpha( $newPicture, 0, 0, 0, 127 ) );
// Resize mask if necessary
if( $xSize != imagesx( $mask ) || $ySize != imagesy( $mask ) ) {
$tempPic = imagecreatetruecolor( $xSize, $ySize );
imagecopyresampled( $tempPic, $mask, 0, 0, 0, 0, $xSize, $ySize, imagesx( $mask ), imagesy( $mask ) );
imagedestroy( $mask );
$mask = $tempPic;
}
// Perform pixel-based alpha map application
for( $x = 0; $x < $xSize; $x++ ) {
for( $y = 0; $y < $ySize; $y++ ) {
$alpha = imagecolorsforindex( $mask, imagecolorat( $mask, $x, $y ) );
//small mod to extract alpha, if using a black(transparent) and white
//mask file instead change the following line back to Jules's original:
//$alpha = 127 - floor($alpha['red'] / 2);
//or a white(transparent) and black mask file:
//$alpha = floor($alpha['red'] / 2);
$alpha = $alpha['alpha'];
$color = imagecolorsforindex( $picture, imagecolorat( $picture, $x, $y ) );
//preserve alpha by comparing the two values
if ($color['alpha'] > $alpha)
$alpha = $color['alpha'];
//kill data for fully transparent pixels
if ($alpha == 127) {
$color['red'] = 0;
$color['blue'] = 0;
$color['green'] = 0;
}
imagesetpixel( $newPicture, $x, $y, imagecolorallocatealpha( $newPicture, $color[ 'red' ], $color[ 'green' ], $color[ 'blue' ], $alpha ) );
}
}
// Copy back to original picture
imagedestroy( $picture );
$picture = $newPicture;
}
?>
There's a library called WideImage that supports alpha masks http://wideimage.sourceforge.net/documentation/manipulating-images/
for ($y = 0; $y < $ySize; $y++) {
$alpha = imagecolorsforindex($mask, imagecolorat($mask, $x, $y));
$alpha = 127 - floor($alpha['red'] / 2);
if (127 == $alpha) {
continue;
}
$color = imagecolorsforindex($picture, imagecolorat($picture, $x, $y));
imagesetpixel($newPicture, $x, $y, imagecolorallocatealpha(
$newPicture, $color['red'], $color['green'], $color['blue'], $alpha));
}
Here is a little upgrade for the first function. As you already have a transparent image, you don't need to copy the masked pixels. This will help the execution a little.
A different way to get a similar effect is to paste the png file onto a new image with a unique background colour to temporarily remove transparency, and set the transparent colour of the png image to the black circle colour instead. Then when you place it over the jpeg image, you set the new transparent colour to the colour of the mask.
// Load the Black Circle PNG image
$png = imagecreatefrompng( 'mask.png' );
$width = imagesx( $png );
$height = imagesy( $png );
// Create a mask image
$mask = imagecreatetruecolor( $width, $height );
// We'll use Magenta as our new transparent colour - set it as the solid background colour.
$magenta = imagecolorallocate( $mask, 255, 0, 255 );
imagefill( $mask, 0, 0, $magenta );
// Copy the png image onto the mask. Destroy it to free up memory.
imagecopyresampled( $mask, $png, 0, 0, 0, 0, $width, $height, $width, $height );
imagedestroy( $png );
// Set the black portion of the mask to transparent.
$black = imagecolorallocate( $mask, 0, 0, 0 );
imagecolortransparent( $mask, $black );
// Load JPEG image.
$jpg = imagecreatefromjpeg( 'image.jpg' );
$j_width = imagesx( $jpg );
$j_height = imagesx( $jpg );
// Enable alpha blending and copy the png image
imagealphablending( $jpg, true );
imagecopyresampled( $jpg, $mask, 0, 0, 0, 0, $j_width, $j_height, $width, $height );
imagedestroy( $mask );
// Set the new transparent colour and output new image to browser as a png.
$magenta = imagecolorallocate( $jpg, 255, 0, 255 );
imagecolortransparent( $jpg, $magenta );
imagepng( $jpg );
If resampling or semi-transparent pixels are getting you down, instead of using a png as a mask, you can disable blending and draw a transparent shape onto $mask image instead.
// Load JPEG Image.
$jpg = imagecreatefromjpeg( 'image.jpg' );
$width = imagesx( $jpg );
$height = imagesx( $jpg );
// Create mask at same size with an opaque background.
$mask = imagecreatetruecolor( $width, $height );
$magenta = imagecolorallocate( $mask, 255, 0, 255 );
imagefill( $mask, 0, 0, $magenta );
// Disable alpha blending and draw a transparent shape onto the mask.
$transparent = imagecolorallocatealpha( $mask, 255, 255, 255, 127 );
imagealphablending( $mask, false );
imagefilledellipse( $mask, round( $width / 2 ), round( $height / 2 ), $width, $height, $transparent );
// Paste the mask onto the original image and set the new transparent colour.
imagealphablending( $jpg, true );
imagecopyresampled( $jpg, $mask, 0, 0, 0, 0, $width, $height, $width, $height );
imagedestroy( $mask );
$magenta = imagecolorallocate( $jpg, 255, 0, 255 );
imagecolortransparent( $jpg, $magenta );
// Output new image to browser as a png.
imagepng( $jpg );
Note: The above code is untested, but should hopefully do what you need it to.
I got the image like this (it's a graph):
(source: kitconet.com)
I want to change the colours, so the white is black, the graph line is light blue, etc.. is it possible to achieve with GD and PHP?
This will replace the white color with Gray
$imgname = "test.gif";
$im = imagecreatefromgif ($imgname);
$index = imagecolorclosest ( $im, 255,255,255 ); // get White COlor
imagecolorset($im,$index,92,92,92); // SET NEW COLOR
$imgname = "result.gif";
imagegif($im, $imgname ); // save image as gif
imagedestroy($im);
I had trouble making this solution work. The image cannot be a true color image. Convert it first with imagetruecolortopalette();
$imgname = "test.gif";
$im = imagecreatefromgif ($imgname);
imagetruecolortopalette($im,false, 255);
$index = imagecolorclosest ( $im, 255,255,255 ); // get White COlor
imagecolorset($im,$index,92,92,92); // SET NEW COLOR
$imgname = "result.gif";
imagegif($im, $imgname ); // save image as gif
imagedestroy($im);
I know this is late and after the fact, but I've put together a script that will do this on a slightly larger scale. Hopefully someone who comes across this post can use it. It takes an number of source images that are one-color layers (your choice). You provide it with a source color and the tint of each layer and the script returns a composite image (with full transparency) colored specifically to your provided hex code.
Check out the code below. A more detailed explanation can be found on my blog.
function hexLighter($hex, $factor = 30) {
$new_hex = '';
$base['R'] = hexdec($hex{0}.$hex{1});
$base['G'] = hexdec($hex{2}.$hex{3});
$base['B'] = hexdec($hex{4}.$hex{5});
foreach ($base as $k => $v) {
$amount = 255 - $v;
$amount = $amount / 100;
$amount = round($amount * $factor);
$new_decimal = $v + $amount;
$new_hex_component = dechex($new_decimal);
$new_hex .= sprintf('%02.2s', $new_hex_component);
}
return $new_hex;
}
// Sanitize/Validate provided color variable
if (!isset($_GET['color']) || strlen($_GET['color']) != 6) {
header($_SERVER['SERVER_PROTOCOL'] . ' 400 Bad Request', true, 400);
exit(0);
}
if (file_exists( "cache/{$_GET['color']}.png" )) {
header( 'Content-Type: image/png' );
readfile( "cache/{$_GET['color']}.png" );
exit(0);
}
// Desired final size of image
$n_width = 50;
$n_height = 50;
// Actual size of source images
$width = 125;
$height = 125;
$image = imagecreatetruecolor($width, $height);
imagesavealpha($image, true);
imagealphablending($image, false);
$n_image = imagecreatetruecolor($n_width, $n_height);
imagesavealpha($n_image, true);
imagealphablending($n_image, false);
$black = imagecolorallocate($image, 0, 0, 0);
$transparent = imagecolorallocatealpha($image, 255, 255, 255, 127);
imagefilledrectangle($image, 0, 0, $width, $height, $transparent);
$layers = array();
$layers_processed = array();
$layers[] = array( 'src' => 'layer01.gif', 'level' => 0 ); // Border
$layers[] = array( 'src' => 'layer02.gif', 'level' => 35 ); // Background
$layers[] = array( 'src' => 'layer03.gif', 'level' => 100 ); // White Quotes
foreach ($layers as $idx => $layer) {
$img = imagecreatefromgif( $layer['src'] );
$processed = imagecreatetruecolor($width, $height);
imagesavealpha($processed, true);
imagealphablending($processed, false);
imagefilledrectangle($processed, 0, 0, $width, $height, $transparent);
$color = hexLighter( $_GET['color'], $layer['level'] );
$color = imagecolorallocate($image,
hexdec( $color{0} . $color{1} ),
hexdec( $color{2} . $color{3} ),
hexdec( $color{4} . $color{5} )
);
for ($x = 0; $x < $width; $x++)
for ($y = 0; $y < $height; $y++)
if ($black === imagecolorat($img, $x, $y))
imagesetpixel($processed, $x, $y, $color);
imagecolortransparent($processed, $transparent);
imagealphablending($processed, true);
array_push($layers_processed, $processed);
imagedestroy( $img );
}
foreach ($layers_processed as $processed) {
imagecopymerge($image, $processed, 0, 0, 0, 0, $width, $height, 100);
imagedestroy( $processed );
}
imagealphablending($image, true);
imagecopyresampled($n_image, $image, 0, 0, 0, 0, $n_width, $n_height, $width, $height);
imagealphablending($n_image, true);
header( 'Content-Type: image/png' );
imagepng( $n_image, "cache/{$_GET['color']}.png" );
imagepng( $n_image );
// Free up memory
imagedestroy( $n_image );
imagedestroy( $image );
I haven't tried it myself but you can look at the function imagecolorset() in the GD library It does a color fill like effect, that could help with the white background.
You could try the imagefilter function http://lv.php.net/imagefilter - but that will not give your direct access to replace one color with another, just changing the r/g/b components.
A very low level solution could be implemented using imagesetpixel http://nl2.php.net/imagesetpixel to set the new pixel values.
IMG_FILTER_NEGATE: Reverses all colors of the image.
http://www.php.net/manual/en/function.imagefilter.php
Could that be a solution?
The slow but sure approach, iterating over every pixel.
function ReplaceColour($img, $r1, $g1, $b1, $r2, $g2, $b2)
{
if(!imageistruecolor($img))
imagepalettetotruecolor($img);
$col1 = (($r1 & 0xFF) << 16) + (($g1 & 0xFF) << 8) + ($b1 & 0xFF);
$col2 = (($r2 & 0xFF) << 16) + (($g2 & 0xFF) << 8) + ($b2 & 0xFF);
$width = imagesx($img);
$height = imagesy($img);
for($x=0; $x < $width; $x++)
for($y=0; $y < $height; $y++)
{
$colrgb = imagecolorat($img, $x, $y);
if($col1 !== $colrgb)
continue;
imagesetpixel ($img, $x , $y , $col2);
}
}
I'm not aware of any ready-made functions. But I suppose you could go trough every pixel of the image and change it's colour...