Merfed, When you upload an Avatar, three files are generated. Full size, mini & thumb views. These are made using the standard GD package and unfortunately will only copy the first frame of an animated gif. You have two different ways to solve your problem. 1. Manually resize the gifs & overwrite your original avatars (quick but just for you). 2. Rewrite the routines that upload & resize the avatars to utilise ImageMagick (lot of work but all users will benefit). Hope this helps.
Merfed, Just an update. A variation to choice 2 is to test for animated gifs and copy without resizing if so. Found this code which tests such on http://www.php.net/manual/en/function.imagecreatefromgif.php#88005
<?php
function is_ani($filename) {
if(!($fh = @fopen($filename, 'rb')))
return false;
$count = 0;
//an animated gif contains multiple "frames", with each frame having a
//header made up of:
// * a static 4-byte sequence (\x00\x21\xF9\x04)
// * 4 variable bytes
// * a static 2-byte sequence (\x00\x2C) (some variants may use \x00\x21 ?)
// We read through the file til we reach the end of the file, or we've found
// at least 2 frame headers
while(!feof($fh) && $count < 2) {
$chunk = fread($fh, 1024 * 100); //read 100kb at a time
$count += preg_match_all('#\x00\x21\xF9\x04.{4}\x00(\x2C|\x21)#s', $chunk, $matches);
}
fclose($fh);
return $count > 1;
}
?>
Psycocandy - I had another look and it gets a bit more involved as the download is re-scaled to three different sizes (thumb, preview & profile) and it is this re-scaling that wipes out the animation. If the scaling was performed via css (possibly/idealy within your theme) as opposed to fixed sizing then an animated gif between 43 - 50 pixels square could be uploaded and work.
Hey after a lot of parking logging statements in code I have finally solved the animated avatar problem! Three files plus maybe your style.css need be changed. Vanilla Animated Gifs
File: vanilla/applications/dashboard/controllers/class.profilecontroller.php
function picture()
change:
$UploadImage = new Gdn_UploadImage();
try
{
// Validate the upload
$TmpImage = $UploadImage->ValidateUpload('Picture');
// Generate the target image name.
$TargetImage = $UploadImage->GenerateTargetName(PATH_LOCAL_UPLOADS);
$ImageBaseName = pathinfo($TargetImage, PATHINFO_BASENAME);
to:
$UploadImage = new Gdn_UploadImage();
try
{
// Validate the upload
$TmpImage = $UploadImage->ValidateUpload('Picture');
list($Width, $Height, $Type, $Attributes) = getimagesize($TmpImage);
$OutputTypes = array(1 => 'gif', 2 => 'jpeg', 3 => 'png');
$FileType = $OutputTypes[$Type];
// Generate the target image name.
$TargetImage = $UploadImage->GenerateTargetName(PATH_LOCAL_UPLOADS);
$pos = strpos($TargetImage,'.');
if ($pos != 0)
$TargetImage = substr($TargetImage,0,$pos);
$TargetImage .= ".$FileType";
$ImageBaseName = pathinfo($TargetImage, PATHINFO_BASENAME);
Reason: Even though $TmpImage is the same filetype as what you are uploading it
carries the file extent of '.tmp' so we are forcing the $TargetImage to carry
the same file type.
File: vanilla/library/core/functions.general.php
add a new function (in alphabetical order):
public function is_ani($filename)
{
if (!($fh = @fopen($filename, 'rb')))
return false;
$count = 0;
while(!feof($fh) && $count < 2)
{
$chunk = fread($fh, 1024 * 100);
$count += preg_match_all('#\x00\x21\xF9\x04.{4}\x00(\x2C|\x21)#s', $chunk, $matches);
}
fclose($fh);
return $count > 1;
}
Reason: Function required to assertain if a GIF is animated or not.
File: vanilla/library/core/class.uploadimage.php
function SaveImageAs()
change:
$TargetPath = PATH_LOCAL_UPLOADS.'/'.ltrim($TargetParsed['Name'], '/');
if (!file_exists(dirname($TargetPath)))
mkdir(dirname($TargetPath), 0777, TRUE);
// Don't resize if the source dimensions are smaller than the target dimensions
$XCoord = GetValue('SourceX', $Options, 0);
to:
$TargetPath = PATH_LOCAL_UPLOADS.'/'.ltrim($TargetParsed['Name'], '/');
$pos = strpos($TargetPath,'.');
if ($pos != 0)
$TargetPath = substr($TargetPath,0,$pos);
$TargetPath .= ".$OutputType";
if (!file_exists(dirname($TargetPath)))
mkdir(dirname($TargetPath), 0777, TRUE);
// if gif then check for animated - dont re-size or crop animated gif, just copy as is
if ($OutputType == 1 && is_ani($Source))
copy($Source, $Target);
else
{
// Don't resize if the source dimensions are smaller than the target dimensions
$XCoord = GetValue('SourceX', $Options, 0);
and change:
if ($OutputType == 'gif')
imagegif($TargetImage, $TargetPath);
elseif ($OutputType == 'png')
imagepng($TargetImage, $TargetPath, (int)($ImageQuality/10));
else
imagejpeg($TargetImage, $TargetPath, $ImageQuality);
// Allow a plugin to move the file to a differnt location.
$Sender = new stdClass();
$Sender->EventArguments = array();
to:
if ($OutputType == 'gif')
imagegif($TargetImage, $TargetPath);
elseif ($OutputType == 'png')
imagepng($TargetImage, $TargetPath, (int)($ImageQuality/10));
else
imagejpeg($TargetImage, $TargetPath, $ImageQuality);
}
// Allow a plugin to move the file to a differnt location.
$Sender = new stdClass();
$Sender->EventArguments = array();
Reason: The first change tests for animated gifs and performs a straight copy without
resizing and the second is the closing brace to complete it.
These changes work on 2.0.18b Note: animated avatars don't get resized when uploading so don't have them bigger than 50x50 pixels. They will be resized by css on display.
Now this isn't a cure-all. I'm sure the developers could do better (hint here).
Just an update: change the height/width statements within your style.css for the thumbs / images to be height: expression(this.height > 50 ? 50: true); width: expression(this.width > 50 ? 50: true);
Answers
When you upload an Avatar, three files are generated. Full size, mini & thumb views. These are made using the standard GD package and unfortunately will only copy the first frame of an animated gif. You have two different ways to solve your problem.
1. Manually resize the gifs & overwrite your original avatars (quick but just for you).
2. Rewrite the routines that upload & resize the avatars to utilise ImageMagick (lot of work but all users will benefit).
Hope this helps.
Just an update. A variation to choice 2 is to test for animated gifs and copy without resizing if so. Found this code which tests such on http://www.php.net/manual/en/function.imagecreatefromgif.php#88005
<?php function is_ani($filename) { if(!($fh = @fopen($filename, 'rb'))) return false; $count = 0; //an animated gif contains multiple "frames", with each frame having a //header made up of: // * a static 4-byte sequence (\x00\x21\xF9\x04) // * 4 variable bytes // * a static 2-byte sequence (\x00\x2C) (some variants may use \x00\x21 ?) // We read through the file til we reach the end of the file, or we've found // at least 2 frame headers while(!feof($fh) && $count < 2) { $chunk = fread($fh, 1024 * 100); //read 100kb at a time $count += preg_match_all('#\x00\x21\xF9\x04.{4}\x00(\x2C|\x21)#s', $chunk, $matches); } fclose($fh); return $count > 1; } ?>
Where in the code did I have to test if it is an animated gif? Could you guys help me?
Thanks in advance!
Hey after a lot of parking logging statements in code I have finally solved the animated avatar problem! Three files plus maybe your style.css need be changed.
Vanilla Animated Gifs
File: vanilla/applications/dashboard/controllers/class.profilecontroller.php function picture() change: $UploadImage = new Gdn_UploadImage(); try { // Validate the upload $TmpImage = $UploadImage->ValidateUpload('Picture'); // Generate the target image name. $TargetImage = $UploadImage->GenerateTargetName(PATH_LOCAL_UPLOADS); $ImageBaseName = pathinfo($TargetImage, PATHINFO_BASENAME); to: $UploadImage = new Gdn_UploadImage(); try { // Validate the upload $TmpImage = $UploadImage->ValidateUpload('Picture'); list($Width, $Height, $Type, $Attributes) = getimagesize($TmpImage); $OutputTypes = array(1 => 'gif', 2 => 'jpeg', 3 => 'png'); $FileType = $OutputTypes[$Type]; // Generate the target image name. $TargetImage = $UploadImage->GenerateTargetName(PATH_LOCAL_UPLOADS); $pos = strpos($TargetImage,'.'); if ($pos != 0) $TargetImage = substr($TargetImage,0,$pos); $TargetImage .= ".$FileType"; $ImageBaseName = pathinfo($TargetImage, PATHINFO_BASENAME); Reason: Even though $TmpImage is the same filetype as what you are uploading it carries the file extent of '.tmp' so we are forcing the $TargetImage to carry the same file type. File: vanilla/library/core/functions.general.php add a new function (in alphabetical order): public function is_ani($filename) { if (!($fh = @fopen($filename, 'rb'))) return false; $count = 0; while(!feof($fh) && $count < 2) { $chunk = fread($fh, 1024 * 100); $count += preg_match_all('#\x00\x21\xF9\x04.{4}\x00(\x2C|\x21)#s', $chunk, $matches); } fclose($fh); return $count > 1; } Reason: Function required to assertain if a GIF is animated or not. File: vanilla/library/core/class.uploadimage.php function SaveImageAs() change: $TargetPath = PATH_LOCAL_UPLOADS.'/'.ltrim($TargetParsed['Name'], '/'); if (!file_exists(dirname($TargetPath))) mkdir(dirname($TargetPath), 0777, TRUE); // Don't resize if the source dimensions are smaller than the target dimensions $XCoord = GetValue('SourceX', $Options, 0); to: $TargetPath = PATH_LOCAL_UPLOADS.'/'.ltrim($TargetParsed['Name'], '/'); $pos = strpos($TargetPath,'.'); if ($pos != 0) $TargetPath = substr($TargetPath,0,$pos); $TargetPath .= ".$OutputType"; if (!file_exists(dirname($TargetPath))) mkdir(dirname($TargetPath), 0777, TRUE); // if gif then check for animated - dont re-size or crop animated gif, just copy as is if ($OutputType == 1 && is_ani($Source)) copy($Source, $Target); else { // Don't resize if the source dimensions are smaller than the target dimensions $XCoord = GetValue('SourceX', $Options, 0); and change: if ($OutputType == 'gif') imagegif($TargetImage, $TargetPath); elseif ($OutputType == 'png') imagepng($TargetImage, $TargetPath, (int)($ImageQuality/10)); else imagejpeg($TargetImage, $TargetPath, $ImageQuality); // Allow a plugin to move the file to a differnt location. $Sender = new stdClass(); $Sender->EventArguments = array(); to: if ($OutputType == 'gif') imagegif($TargetImage, $TargetPath); elseif ($OutputType == 'png') imagepng($TargetImage, $TargetPath, (int)($ImageQuality/10)); else imagejpeg($TargetImage, $TargetPath, $ImageQuality); } // Allow a plugin to move the file to a differnt location. $Sender = new stdClass(); $Sender->EventArguments = array(); Reason: The first change tests for animated gifs and performs a straight copy without resizing and the second is the closing brace to complete it.
These changes work on 2.0.18b
Note: animated avatars don't get resized when uploading so don't have them bigger than 50x50 pixels. They will be resized by css on display.
Now this isn't a cure-all. I'm sure the developers could do better (hint here).
height: expression(this.height > 50 ? 50: true);
width: expression(this.width > 50 ? 50: true);
or whatever maximums you wish.