Skip to content

Commit 9c19e9b

Browse files
afaquejamepriestley
authored andcommitted
Preserving the Animation of Gif Images
Summary: Preserving animation of GIF profile Pictures Test Plan: Uploaded Animated images as profile pictures to check if the animation of gif images is preserved and it does :) somewhat ! Reviewers: epriestley Reviewed By: epriestley CC: aran, Korvin Differential Revision: https://secure.phabricator.com/D4833
1 parent cc08482 commit 9c19e9b

File tree

6 files changed

+103
-9
lines changed

6 files changed

+103
-9
lines changed

conf/default.conf.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,10 @@
882882
'image/vnd.microsoft.icon' => true,
883883
),
884884

885+
// Configuration option for enabling imagemagick
886+
// to resize animated profile pictures (gif)
887+
'files.enable-imagemagick' => false,
888+
885889
// -- Storage --------------------------------------------------------------- //
886890

887891
// Phabricator allows users to upload files, and can keep them in various

src/__phutil_library_map__.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1261,6 +1261,7 @@
12611261
'PhabricatorSetupCheckExtraConfig' => 'applications/config/check/PhabricatorSetupCheckExtraConfig.php',
12621262
'PhabricatorSetupCheckFacebook' => 'applications/config/check/PhabricatorSetupCheckFacebook.php',
12631263
'PhabricatorSetupCheckGD' => 'applications/config/check/PhabricatorSetupCheckGD.php',
1264+
'PhabricatorSetupCheckImagemagick' => 'applications/config/check/PhabricatorSetupCheckImagemagick.php',
12641265
'PhabricatorSetupCheckInvalidConfig' => 'applications/config/check/PhabricatorSetupCheckInvalidConfig.php',
12651266
'PhabricatorSetupCheckMail' => 'applications/config/check/PhabricatorSetupCheckMail.php',
12661267
'PhabricatorSetupCheckMySQL' => 'applications/config/check/PhabricatorSetupCheckMySQL.php',
@@ -2677,6 +2678,7 @@
26772678
'PhabricatorSetupCheckExtraConfig' => 'PhabricatorSetupCheck',
26782679
'PhabricatorSetupCheckFacebook' => 'PhabricatorSetupCheck',
26792680
'PhabricatorSetupCheckGD' => 'PhabricatorSetupCheck',
2681+
'PhabricatorSetupCheckImagemagick' => 'PhabricatorSetupCheck',
26802682
'PhabricatorSetupCheckInvalidConfig' => 'PhabricatorSetupCheck',
26812683
'PhabricatorSetupCheckMail' => 'PhabricatorSetupCheck',
26822684
'PhabricatorSetupCheckMySQL' => 'PhabricatorSetupCheck',
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
final class PhabricatorSetupCheckImagemagick extends PhabricatorSetupCheck {
4+
5+
protected function executeChecks() {
6+
$imagemagick = PhabricatorEnv::getEnvConfig('files.enable-imagemagick');
7+
if ($imagemagick) {
8+
list($err) = exec_manual('which convert');
9+
if ($err) {
10+
$message = pht(
11+
'You have enabled Imagemagick in your config, but the \'convert\''.
12+
' binary is not in the webserver\'s $PATH. Disable imagemagick'.
13+
' or make it available to the webserver.');
14+
15+
$this->newIssue('files.enable-imagemagick')
16+
->setName(pht(
17+
"'convert' binary not found or Imagemagick is not installed."))
18+
->setMessage($message)
19+
->addPhabricatorConfig('files.enable-imagemagick')
20+
->addPhabricatorConfig('environment.append-paths');
21+
}
22+
}
23+
}
24+
}
25+

src/applications/config/option/PhabricatorCoreConfigOptions.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ public function getOptions() {
133133
->setDescription(
134134
pht('Array containing list of Uninstalled applications.')
135135
),
136-
);
136+
);
137137
}
138138

139139
protected function didValidateOption(

src/applications/files/PhabricatorImageTransformer.php

Lines changed: 61 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,14 @@ private function crudelyCropTo(PhabricatorFile $file, $x, $min_y, $max_y) {
9595
$scaled_y = $min_y;
9696
}
9797

98+
$cropped = $this->applyScaleWithImagemagick($file, $x, $scaled_y);
99+
100+
if ($cropped != null) {
101+
return $cropped;
102+
}
103+
98104
$img = $this->applyScaleTo(
99-
$img,
105+
$file,
100106
$x,
101107
$scaled_y);
102108

@@ -131,11 +137,13 @@ private function crasslyCropTo(PhabricatorFile $file, $top, $left, $w, $h) {
131137
* Very crudely scale an image up or down to an exact size.
132138
*/
133139
private function crudelyScaleTo(PhabricatorFile $file, $dx, $dy) {
134-
$data = $file->loadFileData();
135-
$src = imagecreatefromstring($data);
140+
$scaled = $this->applyScaleWithImagemagick($file, $dx, $dy);
136141

137-
$dst = $this->applyScaleTo($src, $dx, $dy);
142+
if ($scaled != null) {
143+
return $scaled;
144+
}
138145

146+
$dst = $this->applyScaleTo($file, $dx, $dy);
139147
return $this->saveImageDataInAnyFormat($dst, $file->getMimeType());
140148
}
141149

@@ -147,17 +155,22 @@ private function getBlankDestinationFile($dx, $dy) {
147155
return $dst;
148156
}
149157

150-
private function applyScaleTo($src, $dx, $dy) {
158+
private function applyScaleTo(PhabricatorFile $file, $dx, $dy) {
159+
$data = $file->loadFileData();
160+
$src = imagecreatefromstring($data);
161+
151162
$x = imagesx($src);
152163
$y = imagesy($src);
153164

154165
$scale = min(($dx / $x), ($dy / $y), 1);
155166

156-
$dst = $this->getBlankDestinationFile($dx, $dy);
157-
158167
$sdx = $scale * $x;
159168
$sdy = $scale * $y;
160169

170+
$dst = $this->getBlankDestinationFile($dx, $dy);
171+
imagesavealpha($dst, true);
172+
imagefill($dst, 0, 0, imagecolorallocatealpha($dst, 255, 255, 255, 127));
173+
161174
imagecopyresampled(
162175
$dst,
163176
$src,
@@ -167,6 +180,7 @@ private function applyScaleTo($src, $dx, $dy) {
167180
$x, $y);
168181

169182
return $dst;
183+
170184
}
171185

172186
public static function getPreviewDimensions(PhabricatorFile $file, $size) {
@@ -337,7 +351,7 @@ private function doesTextBoundingBoxFitInImage($img,
337351

338352
private function saveImageDataInAnyFormat($data, $preferred_mime = '') {
339353
switch ($preferred_mime) {
340-
case 'image/gif': // GIF doesn't support true color.
354+
case 'image/gif': // Gif doesn't support true color
341355
case 'image/png':
342356
if (function_exists('imagepng')) {
343357
ob_start();
@@ -368,4 +382,43 @@ private function saveImageDataInAnyFormat($data, $preferred_mime = '') {
368382
return $img;
369383
}
370384

385+
private function applyScaleWithImagemagick(PhabricatorFile $file, $dx, $dy) {
386+
387+
$img_type = $file->getMimeType();
388+
$imagemagick = PhabricatorEnv::getEnvConfig('files.enable-imagemagick');
389+
390+
if ($img_type != 'image/gif' || $imagemagick == false) {
391+
return null;
392+
}
393+
394+
$data = $file->loadFileData();
395+
$src = imagecreatefromstring($data);
396+
397+
$x = imagesx($src);
398+
$y = imagesy($src);
399+
400+
$scale = min(($dx / $x), ($dy / $y), 1);
401+
402+
$sdx = $scale * $x;
403+
$sdy = $scale * $y;
404+
405+
$input = new TempFile();
406+
Filesystem::writeFile($input, $data);
407+
408+
$resized = new TempFile();
409+
410+
list($err) = exec_manual(
411+
'convert %s -coalesce -resize %sX%s\! %s'
412+
, $input, $sdx, $sdy, $resized
413+
);
414+
415+
if (!$err) {
416+
$new_data = Filesystem::readFile($resized);
417+
return $new_data;
418+
} else {
419+
return null;
420+
}
421+
422+
}
423+
371424
}

src/applications/files/config/PhabricatorFilesConfigOptions.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,16 @@ public function getOptions() {
119119
"value and the UI will then reflect the actual configured ".
120120
"limit."))
121121
->addExample('10M', pht("Valid setting.")),
122+
$this->newOption('files.enable-imagemagick', 'bool', false)
123+
->setBoolOptions(
124+
array(
125+
pht('Enable'),
126+
pht('Disable')
127+
))->setDescription(
128+
pht("This option will enable animated gif images".
129+
"to be set as profile pictures. The \'convert\' binary ".
130+
"should be available to the webserver for this to work")),
131+
122132
);
123133
}
124134

0 commit comments

Comments
 (0)