mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-26 16:52:41 +01:00
Improve image thumbnailing and increase the size of Macro thumbnails
Summary: Alternate proposal for D3635. - Works better with small images. - Produces a predictable thumbnail size. - Somewhat reasonable output on 3000x10 images. - Increase the size of Macro thumbnails to 240px. Test Plan: {F20497} Reviewers: vrana, chad Reviewed By: vrana CC: aran Differential Revision: https://secure.phabricator.com/D3638
This commit is contained in:
parent
74644d5210
commit
85d6f7a66e
6 changed files with 81 additions and 28 deletions
|
@ -47,6 +47,20 @@ final class PhabricatorImageTransformer {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function executePreviewTransform(
|
||||||
|
PhabricatorFile $file,
|
||||||
|
$size) {
|
||||||
|
|
||||||
|
$image = $this->generatePreview($file, $size);
|
||||||
|
|
||||||
|
return PhabricatorFile::newFromFileData(
|
||||||
|
$image,
|
||||||
|
array(
|
||||||
|
'name' => 'preview-'.$file->getName(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private function crudelyCropTo(PhabricatorFile $file, $x, $min_y, $max_y) {
|
private function crudelyCropTo(PhabricatorFile $file, $x, $min_y, $max_y) {
|
||||||
$data = $file->loadFileData();
|
$data = $file->loadFileData();
|
||||||
$img = imagecreatefromstring($data);
|
$img = imagecreatefromstring($data);
|
||||||
|
@ -86,27 +100,56 @@ final class PhabricatorImageTransformer {
|
||||||
$x = imagesx($src);
|
$x = imagesx($src);
|
||||||
$y = imagesy($src);
|
$y = imagesy($src);
|
||||||
|
|
||||||
$scale = min($x / $dx, $y / $dy);
|
$scale = min(($dx / $x), ($dy / $y), 1);
|
||||||
|
|
||||||
$dst = imagecreatetruecolor($dx, $dy);
|
$dst = imagecreatetruecolor($dx, $dy);
|
||||||
imagesavealpha($dst, true);
|
imagesavealpha($dst, true);
|
||||||
imagefill($dst, 0, 0, imagecolorallocatealpha($dst, 0, 0, 0, 127));
|
imagefill($dst, 0, 0, imagecolorallocatealpha($dst, 255, 255, 255, 127));
|
||||||
|
|
||||||
// If we need to chop off some pixels, chop them off from the sides instead
|
$sdx = $scale * $x;
|
||||||
// of scaling in on <0, 0>.
|
$sdy = $scale * $y;
|
||||||
$sdx = $scale * $dx;
|
|
||||||
$sdy = $scale * $dy;
|
|
||||||
|
|
||||||
imagecopyresampled(
|
imagecopyresampled(
|
||||||
$dst,
|
$dst,
|
||||||
$src,
|
$src,
|
||||||
|
($dx - $sdx) / 2, ($dy - $sdy) / 2,
|
||||||
0, 0,
|
0, 0,
|
||||||
($x - $sdx) / 2, ($y - $sdy) / 2,
|
$sdx, $sdy,
|
||||||
$dx, $dy,
|
$x, $y);
|
||||||
$sdx, $sdy);
|
|
||||||
|
|
||||||
return $dst;
|
return $dst;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function generatePreview(PhabricatorFile $file, $size) {
|
||||||
|
$data = $file->loadFileData();
|
||||||
|
$src = imagecreatefromstring($data);
|
||||||
|
|
||||||
|
$x = imagesx($src);
|
||||||
|
$y = imagesy($src);
|
||||||
|
|
||||||
|
$scale = min($size / $x, $size / $y, 1);
|
||||||
|
|
||||||
|
$dx = max($size / 4, $scale * $x);
|
||||||
|
$dy = max($size / 4, $scale * $y);
|
||||||
|
|
||||||
|
$dst = imagecreatetruecolor($dx, $dy);
|
||||||
|
imagesavealpha($dst, true);
|
||||||
|
imagefill($dst, 0, 0, imagecolorallocatealpha($dst, 255, 255, 255, 127));
|
||||||
|
|
||||||
|
$sdx = $scale * $x;
|
||||||
|
$sdy = $scale * $y;
|
||||||
|
|
||||||
|
imagecopyresampled(
|
||||||
|
$dst,
|
||||||
|
$src,
|
||||||
|
($dx - $sdx) / 2, ($dy - $sdy) / 2,
|
||||||
|
0, 0,
|
||||||
|
$sdx, $sdy,
|
||||||
|
$x, $y);
|
||||||
|
|
||||||
|
return $this->saveImageDataInAnyFormat($dst, $file->getMimeType());
|
||||||
|
}
|
||||||
|
|
||||||
private function saveImageDataInAnyFormat($data, $preferred_mime = '') {
|
private function saveImageDataInAnyFormat($data, $preferred_mime = '') {
|
||||||
switch ($preferred_mime) {
|
switch ($preferred_mime) {
|
||||||
case 'image/gif': // GIF doesn't support true color.
|
case 'image/gif': // GIF doesn't support true color.
|
||||||
|
|
|
@ -55,6 +55,12 @@ final class PhabricatorFileTransformController
|
||||||
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
|
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
|
||||||
|
|
||||||
switch ($this->transform) {
|
switch ($this->transform) {
|
||||||
|
case 'thumb-220x165':
|
||||||
|
$xformed_file = $this->executeThumbTransform($file, 220, 165);
|
||||||
|
break;
|
||||||
|
case 'preview-220':
|
||||||
|
$xformed_file = $this->executePreviewTransform($file, 220);
|
||||||
|
break;
|
||||||
case 'thumb-160x120':
|
case 'thumb-160x120':
|
||||||
$xformed_file = $this->executeThumbTransform($file, 160, 120);
|
$xformed_file = $this->executeThumbTransform($file, 160, 120);
|
||||||
break;
|
break;
|
||||||
|
@ -133,6 +139,11 @@ final class PhabricatorFileTransformController
|
||||||
return id(new AphrontRedirectResponse())->setURI($uri);
|
return id(new AphrontRedirectResponse())->setURI($uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function executePreviewTransform(PhabricatorFile $file, $size) {
|
||||||
|
$xformer = new PhabricatorImageTransformer();
|
||||||
|
return $xformer->executePreviewTransform($file, $size);
|
||||||
|
}
|
||||||
|
|
||||||
private function executeThumbTransform(PhabricatorFile $file, $x, $y) {
|
private function executeThumbTransform(PhabricatorFile $file, $x, $y) {
|
||||||
$xformer = new PhabricatorImageTransformer();
|
$xformer = new PhabricatorImageTransformer();
|
||||||
return $xformer->executeThumbTransform($file, $x, $y);
|
return $xformer->executeThumbTransform($file, $x, $y);
|
||||||
|
|
|
@ -315,6 +315,13 @@ final class PhabricatorFile extends PhabricatorFileDAO {
|
||||||
return '/file/xform/thumb-160x120/'.$this->getPHID().'/';
|
return '/file/xform/thumb-160x120/'.$this->getPHID().'/';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getPreview220URI() {
|
||||||
|
return '/file/xform/preview-220/'.$this->getPHID().'/';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getThumb220x165URI() {
|
||||||
|
return '/file/xform/thumb-220x165/'.$this->getPHID().'/';
|
||||||
|
}
|
||||||
|
|
||||||
public function isViewableInBrowser() {
|
public function isViewableInBrowser() {
|
||||||
return ($this->getViewableMimeType() !== null);
|
return ($this->getViewableMimeType() !== null);
|
||||||
|
|
|
@ -99,8 +99,8 @@ final class PhabricatorMacroListController
|
||||||
|
|
||||||
$item = new PhabricatorPinboardItemView();
|
$item = new PhabricatorPinboardItemView();
|
||||||
if ($file) {
|
if ($file) {
|
||||||
$item->setImageURI($file->getThumb160x120URI());
|
$item->setImageURI($file->getThumb220x165URI());
|
||||||
$item->setImageSize(160, 120);
|
$item->setImageSize(220, 165);
|
||||||
if ($file->getAuthorPHID()) {
|
if ($file->getAuthorPHID()) {
|
||||||
$author_handle = $this->getHandle($file->getAuthorPHID());
|
$author_handle = $this->getHandle($file->getAuthorPHID());
|
||||||
$item->appendChild(
|
$item->appendChild(
|
||||||
|
@ -112,7 +112,6 @@ final class PhabricatorMacroListController
|
||||||
'div',
|
'div',
|
||||||
array(),
|
array(),
|
||||||
'Created on '.$datetime));
|
'Created on '.$datetime));
|
||||||
|
|
||||||
}
|
}
|
||||||
$item->setURI($this->getApplicationURI('/edit/'.$macro->getID().'/'));
|
$item->setURI($this->getApplicationURI('/edit/'.$macro->getID().'/'));
|
||||||
$item->setHeader($macro->getName());
|
$item->setHeader($macro->getName());
|
||||||
|
|
|
@ -68,9 +68,7 @@ final class PhabricatorRemarkupRuleEmbedFile
|
||||||
return $this->getEngine()->storeText($link);
|
return $this->getEngine()->storeText($link);
|
||||||
}
|
}
|
||||||
|
|
||||||
$attrs = array(
|
$attrs = array();
|
||||||
'class' => 'phabricator-remarkup-embed-image',
|
|
||||||
);
|
|
||||||
|
|
||||||
switch ($options['size']) {
|
switch ($options['size']) {
|
||||||
case 'full':
|
case 'full':
|
||||||
|
@ -79,9 +77,7 @@ final class PhabricatorRemarkupRuleEmbedFile
|
||||||
break;
|
break;
|
||||||
case 'thumb':
|
case 'thumb':
|
||||||
default:
|
default:
|
||||||
$attrs['src'] = $file->getThumb160x120URI();
|
$attrs['src'] = $file->getPreview220URI();
|
||||||
$attrs['width'] = 160;
|
|
||||||
$attrs['height'] = 120;
|
|
||||||
$link = $file->getBestURI();
|
$link = $file->getBestURI();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -93,6 +89,7 @@ final class PhabricatorRemarkupRuleEmbedFile
|
||||||
'a',
|
'a',
|
||||||
array(
|
array(
|
||||||
'href' => $link,
|
'href' => $link,
|
||||||
|
'class' => 'phabricator-remarkup-embed-image',
|
||||||
'target' => '_blank',
|
'target' => '_blank',
|
||||||
),
|
),
|
||||||
$embed);
|
$embed);
|
||||||
|
|
|
@ -219,14 +219,10 @@
|
||||||
margin: .5em 1em 0;
|
margin: .5em 1em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
img.phabricator-remarkup-embed-image {
|
.phabricator-remarkup-embed-image {
|
||||||
display: inline;
|
display: inline-block;
|
||||||
border: 2px solid white;
|
border: 3px solid white;
|
||||||
background: white;
|
box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.20);
|
||||||
|
|
||||||
box-shadow: 1px 1px 6px rgba(0,0,0,.5);
|
|
||||||
-moz-box-shadow: 1px 1px 6px rgba(0,0,0,.5);
|
|
||||||
-webkit-box-shadow: 1px 1px 6px rgba(0,0,0,.5);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.phabricator-remarkup table.remarkup-table {
|
.phabricator-remarkup table.remarkup-table {
|
||||||
|
|
Loading…
Reference in a new issue