diff --git a/conf/default.conf.php b/conf/default.conf.php index b22e053827..0d13f5e895 100644 --- a/conf/default.conf.php +++ b/conf/default.conf.php @@ -882,6 +882,10 @@ return array( 'image/vnd.microsoft.icon' => true, ), + // Configuration option for enabling imagemagick + // to resize animated profile pictures (gif) + 'files.enable-imagemagick' => false, + // -- Storage --------------------------------------------------------------- // // Phabricator allows users to upload files, and can keep them in various diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 49650e27dd..8176961f8f 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1261,6 +1261,7 @@ phutil_register_library_map(array( 'PhabricatorSetupCheckExtraConfig' => 'applications/config/check/PhabricatorSetupCheckExtraConfig.php', 'PhabricatorSetupCheckFacebook' => 'applications/config/check/PhabricatorSetupCheckFacebook.php', 'PhabricatorSetupCheckGD' => 'applications/config/check/PhabricatorSetupCheckGD.php', + 'PhabricatorSetupCheckImagemagick' => 'applications/config/check/PhabricatorSetupCheckImagemagick.php', 'PhabricatorSetupCheckInvalidConfig' => 'applications/config/check/PhabricatorSetupCheckInvalidConfig.php', 'PhabricatorSetupCheckMail' => 'applications/config/check/PhabricatorSetupCheckMail.php', 'PhabricatorSetupCheckMySQL' => 'applications/config/check/PhabricatorSetupCheckMySQL.php', @@ -2677,6 +2678,7 @@ phutil_register_library_map(array( 'PhabricatorSetupCheckExtraConfig' => 'PhabricatorSetupCheck', 'PhabricatorSetupCheckFacebook' => 'PhabricatorSetupCheck', 'PhabricatorSetupCheckGD' => 'PhabricatorSetupCheck', + 'PhabricatorSetupCheckImagemagick' => 'PhabricatorSetupCheck', 'PhabricatorSetupCheckInvalidConfig' => 'PhabricatorSetupCheck', 'PhabricatorSetupCheckMail' => 'PhabricatorSetupCheck', 'PhabricatorSetupCheckMySQL' => 'PhabricatorSetupCheck', diff --git a/src/applications/config/check/PhabricatorSetupCheckImagemagick.php b/src/applications/config/check/PhabricatorSetupCheckImagemagick.php new file mode 100644 index 0000000000..efcd65fb35 --- /dev/null +++ b/src/applications/config/check/PhabricatorSetupCheckImagemagick.php @@ -0,0 +1,25 @@ +newIssue('files.enable-imagemagick') + ->setName(pht( + "'convert' binary not found or Imagemagick is not installed.")) + ->setMessage($message) + ->addPhabricatorConfig('files.enable-imagemagick') + ->addPhabricatorConfig('environment.append-paths'); + } + } + } +} + diff --git a/src/applications/config/option/PhabricatorCoreConfigOptions.php b/src/applications/config/option/PhabricatorCoreConfigOptions.php index 6dadb5c816..ebcf6401c9 100644 --- a/src/applications/config/option/PhabricatorCoreConfigOptions.php +++ b/src/applications/config/option/PhabricatorCoreConfigOptions.php @@ -133,7 +133,7 @@ final class PhabricatorCoreConfigOptions ->setDescription( pht('Array containing list of Uninstalled applications.') ), - ); + ); } protected function didValidateOption( diff --git a/src/applications/files/PhabricatorImageTransformer.php b/src/applications/files/PhabricatorImageTransformer.php index 8e651eba69..49991a9da4 100644 --- a/src/applications/files/PhabricatorImageTransformer.php +++ b/src/applications/files/PhabricatorImageTransformer.php @@ -95,8 +95,14 @@ final class PhabricatorImageTransformer { $scaled_y = $min_y; } + $cropped = $this->applyScaleWithImagemagick($file, $x, $scaled_y); + + if ($cropped != null) { + return $cropped; + } + $img = $this->applyScaleTo( - $img, + $file, $x, $scaled_y); @@ -131,11 +137,13 @@ final class PhabricatorImageTransformer { * Very crudely scale an image up or down to an exact size. */ private function crudelyScaleTo(PhabricatorFile $file, $dx, $dy) { - $data = $file->loadFileData(); - $src = imagecreatefromstring($data); + $scaled = $this->applyScaleWithImagemagick($file, $dx, $dy); - $dst = $this->applyScaleTo($src, $dx, $dy); + if ($scaled != null) { + return $scaled; + } + $dst = $this->applyScaleTo($file, $dx, $dy); return $this->saveImageDataInAnyFormat($dst, $file->getMimeType()); } @@ -147,17 +155,22 @@ final class PhabricatorImageTransformer { return $dst; } - private function applyScaleTo($src, $dx, $dy) { + private function applyScaleTo(PhabricatorFile $file, $dx, $dy) { + $data = $file->loadFileData(); + $src = imagecreatefromstring($data); + $x = imagesx($src); $y = imagesy($src); $scale = min(($dx / $x), ($dy / $y), 1); - $dst = $this->getBlankDestinationFile($dx, $dy); - $sdx = $scale * $x; $sdy = $scale * $y; + $dst = $this->getBlankDestinationFile($dx, $dy); + imagesavealpha($dst, true); + imagefill($dst, 0, 0, imagecolorallocatealpha($dst, 255, 255, 255, 127)); + imagecopyresampled( $dst, $src, @@ -167,6 +180,7 @@ final class PhabricatorImageTransformer { $x, $y); return $dst; + } public static function getPreviewDimensions(PhabricatorFile $file, $size) { @@ -337,7 +351,7 @@ final class PhabricatorImageTransformer { private function saveImageDataInAnyFormat($data, $preferred_mime = '') { switch ($preferred_mime) { - case 'image/gif': // GIF doesn't support true color. + case 'image/gif': // Gif doesn't support true color case 'image/png': if (function_exists('imagepng')) { ob_start(); @@ -368,4 +382,43 @@ final class PhabricatorImageTransformer { return $img; } + private function applyScaleWithImagemagick(PhabricatorFile $file, $dx, $dy) { + + $img_type = $file->getMimeType(); + $imagemagick = PhabricatorEnv::getEnvConfig('files.enable-imagemagick'); + + if ($img_type != 'image/gif' || $imagemagick == false) { + return null; + } + + $data = $file->loadFileData(); + $src = imagecreatefromstring($data); + + $x = imagesx($src); + $y = imagesy($src); + + $scale = min(($dx / $x), ($dy / $y), 1); + + $sdx = $scale * $x; + $sdy = $scale * $y; + + $input = new TempFile(); + Filesystem::writeFile($input, $data); + + $resized = new TempFile(); + + list($err) = exec_manual( + 'convert %s -coalesce -resize %sX%s\! %s' + , $input, $sdx, $sdy, $resized + ); + + if (!$err) { + $new_data = Filesystem::readFile($resized); + return $new_data; + } else { + return null; + } + + } + } diff --git a/src/applications/files/config/PhabricatorFilesConfigOptions.php b/src/applications/files/config/PhabricatorFilesConfigOptions.php index b960cd6ee5..45b7512597 100644 --- a/src/applications/files/config/PhabricatorFilesConfigOptions.php +++ b/src/applications/files/config/PhabricatorFilesConfigOptions.php @@ -119,6 +119,16 @@ final class PhabricatorFilesConfigOptions "value and the UI will then reflect the actual configured ". "limit.")) ->addExample('10M', pht("Valid setting.")), + $this->newOption('files.enable-imagemagick', 'bool', false) + ->setBoolOptions( + array( + pht('Enable'), + pht('Disable') + ))->setDescription( + pht("This option will enable animated gif images". + "to be set as profile pictures. The \'convert\' binary ". + "should be available to the webserver for this to work")), + ); }