Reputation: 1179
Is there any working example how to read an animated gif, change color of any pixel in any frame of it using GD library or ImageMagick and save it as an animated gif again?
I have been googling for it for 2 days and I did not found answer anywhere.
Edit: I have almost perfect solution. But some transparent (animated) gifs do not retain its transparency correctly (only part of the transparency is corrupted).
Source:
GIFEncoder class: http://www.phpclasses.org/package/3163-PHP-Generate-GIF-animations-from-a-set-of-GIF-images.html
require_once('GIFEncoder.class.php');
require_once('GifFrameExtractor-master/src/GifFrameExtractor/GifFrameExtractor.php');
$gifFilePath = "input.gif";
// check this is an animated GIF
if (!\GifFrameExtractor\GifFrameExtractor::isAnimatedGif($gifFilePath)) {
\die('not an namiated image');
}
$gfe = new \GifFrameExtractor\GifFrameExtractor();
$gfe->extract($gifFilePath);
// frames as a GD source
$frameImages = $gfe->getFrameImages();
$frameDurations = $gfe->getFrameDurations();
// color used as a transparent color, if any
$tColor = array('red' => -1,'green'=>-1,'blue'=>-1);
foreach($frameImages as $i => $gdSource) {
// do something with gdSource
colorize($gdSource);
if($i == 0) {
//get the color used as transparent
// should be for any frame the same
$tColor = getTransparentColor($gdSource);
}
//GIFEncoder works only with binary data or files
//It cant work with gd source
//Therfore i output the gd source to stdout, catch it
//and use it as binary data.
//It could be memory consuming, so you might write the
//frames as a file and then use them instead of binary data.
\ob_start(); // catch output
\imagegif($gdSource); // write binary data to output
$frameImages[$i] = \ob_get_contents(); //replace GD source by the binary data
\ob_end_clean();
}
// create animated gif
$gif = new \GIFEncoder(
$frameImages, //binary data (or path to images on file system)
$frameDurations,
0, // infinite loop
2, // dont know what is this for (number 0-3 or -1 to ignore)
$tColor['red'], $tColor['green'], $tColor['blue'],
'bin' // or 'url' if $frameImages is array of paths to images on file system
);
// write image to file system
\FWrite ( \FOpen ( "output.gif", "wb" ), $gif->GetAnimation ( ) );
// send image to browser
//Header ( 'Content-type:image/gif' );
//echo $gif->GetAnimation ( );
//helpful function
/**
* Make grayscale color
* @param $color array('red'=>,'green'=>,'blue'=>);
* @return array('red'=>,'green'=>,'blue'=>);
*/
function transformColor($color) {
$gst = $color['red']*0.15+$color['green']*0.5+$color['blue']*0.35;
$color['red'] = $color['green'] = $color['blue'] = $gst;
return $color;
}
//helpful function
/**
* Get the transparent color from GD resource.
* If no tranpsarent color, all returned RGB values are -1
* @param $rs GD resource
* @return array('red'=>,'green'=>,'blue'=>)
*/
function getTransparentColor($rs) {
$transparentIndex = \ImageColorTransparent($rs);
$transparentColor = NULL;
if ($transparentIndex >= 0 && $transparentIndex < \ImageColorsTotal($rs)) {
$transparentColor = @\ImageColorsForIndex($rs, $transparentIndex);
}
return $transparentColor ? $transparentColor : array('red' => -1,'green'=>-1,'blue'=>-1);
}
// transform the GD resource with transparency // there is probably problem,because if I save frames alone, they have the problem with transparency described above
function colorize($rs) {
assert('gd' === \get_resource_type($rs));
// you might want to create copy of the source here
// (instead of resource alias)
$bild = $rs;
//width
$x = \imagesx($bild);
// height
$y = \imagesy($bild);
$transparentIndex = \ImageColorTransparent($rs);
$transparentColor = NULL;
if ($transparentIndex >= 0 && $transparentIndex < \ImageColorsTotal($bild)) {
$transparentColor = @\ImageColorsForIndex($rs, $transparentIndex);
}
\ImageSaveAlpha($bild, TRUE);
\ImageAlphaBlending($bild, FALSE);
$transparentNewIndex = NULL;
$transparentNewColor = NULL;
if (!empty($transparentColor)) {
$transparentIndexNew = \ImageColorAllocate($bild, 255, 0, 255);
\ImageColorTransparent($bild, $transparentIndexNew);
$transparentColorNew = @ImageColorsForIndex($bild, transparentIndexNew);
}
// transform pixels
for ($i = 0; $i < $y; $i++) {
for ($j = 0; $j < $x; $j++) {
$index = \imagecolorat($rs, $j, $i);
$color = \imagecolorsforindex($rs, $index);
if ($index === $transparentIndex) {
$col = $transparentIndexNew;
} else {
$rgb = transformColor($color);
//TODO: we have a problem here, if $rgb ~= $transparentColorNew,
// then this pixel will be transparent and not the desired color
$col = \imagecolorresolvealpha($bild, $rgb['red'], $rgb['green'], $rgb['blue'], intval($color['alpha']));
if($col == $transparentIndexNew) {
//TODO: fix the color not to be transparent
// it do not die with the example gif
//die('This might be a problem, but not the problem describlet above this example');
}
}
\imagesetpixel($bild, $j, $i, $col);
}
}
return $bild;
}
Upvotes: 2
Views: 1733
Reputation: 4725
$file = "yourImage.gif";
$im = imagecreatefromgif ($file);
$index = imagecolorclosest ( $im, 255,255,255 ); // get your color (rgb)
imagecolorset($im,$index,92,92,92); // set new color (rgb)
$file= "yourResult.gif";
imagegif($im, $file); // save image as gif
imagedestroy($im);
I'm not sure if this works with a animated gif.
Upvotes: 1
Reputation: 6909
I believe that an animated GIF has multiple frames, so the algorithm for this would be to read the gif as multiple frames, change the pixel colours in each frame, and then save it again. Are you able to load an animated gif in GD?
Perhaps this library can help you to write the GIF image back: http://www.phpclasses.org/package/3163-PHP-Generate-GIF-animations-from-a-set-of-GIF-images.html
Upvotes: 1