@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
at upstream/main 231 lines 6.1 kB view raw
1<?php 2 3final class PhabricatorFileThumbnailTransform 4 extends PhabricatorFileImageTransform { 5 6 const TRANSFORM_PROFILE = 'profile'; 7 const TRANSFORM_PINBOARD = 'pinboard'; 8 const TRANSFORM_THUMBGRID = 'thumbgrid'; 9 const TRANSFORM_PREVIEW = 'preview'; 10 const TRANSFORM_WORKCARD = 'workcard'; 11 12 private $name; 13 private $key; 14 private $dstX; 15 private $dstY; 16 private $scaleUp; 17 18 public function setName($name) { 19 $this->name = $name; 20 return $this; 21 } 22 23 public function setKey($key) { 24 $this->key = $key; 25 return $this; 26 } 27 28 public function setDimensions($x, $y) { 29 $this->dstX = $x; 30 $this->dstY = $y; 31 return $this; 32 } 33 34 public function setScaleUp($scale) { 35 $this->scaleUp = $scale; 36 return $this; 37 } 38 39 public function getTransformName() { 40 return $this->name; 41 } 42 43 public function getTransformKey() { 44 return $this->key; 45 } 46 47 protected function getFileProperties() { 48 $properties = array(); 49 switch ($this->key) { 50 case self::TRANSFORM_PROFILE: 51 $properties['profile'] = true; 52 $properties['name'] = 'profile'; 53 break; 54 } 55 return $properties; 56 } 57 58 public function generateTransforms() { 59 return array( 60 id(new PhabricatorFileThumbnailTransform()) 61 ->setName(pht("Profile (400px \xC3\x97 400px) (Image will be Public)")) 62 ->setKey(self::TRANSFORM_PROFILE) 63 ->setDimensions(400, 400) 64 ->setScaleUp(true), 65 id(new PhabricatorFileThumbnailTransform()) 66 ->setName(pht("Pinboard (280px \xC3\x97 210px)")) 67 ->setKey(self::TRANSFORM_PINBOARD) 68 ->setDimensions(280, 210), 69 id(new PhabricatorFileThumbnailTransform()) 70 ->setName(pht('Thumbgrid (100px)')) 71 ->setKey(self::TRANSFORM_THUMBGRID) 72 ->setDimensions(100, null), 73 id(new PhabricatorFileThumbnailTransform()) 74 ->setName(pht('Preview (220px)')) 75 ->setKey(self::TRANSFORM_PREVIEW) 76 ->setDimensions(220, null), 77 id(new self()) 78 ->setName(pht('Workcard (526px)')) 79 ->setKey(self::TRANSFORM_WORKCARD) 80 ->setScaleUp(true) 81 ->setDimensions(526, null), 82 ); 83 } 84 85 public function applyTransform(PhabricatorFile $file) { 86 $this->willTransformFile($file); 87 88 list($src_x, $src_y) = $this->getImageDimensions(); 89 $dst_x = $this->dstX; 90 $dst_y = $this->dstY; 91 92 $dimensions = $this->computeDimensions( 93 $src_x, 94 $src_y, 95 $dst_x, 96 $dst_y); 97 98 $copy_x = $dimensions['copy_x']; 99 $copy_y = $dimensions['copy_y']; 100 $use_x = $dimensions['use_x']; 101 $use_y = $dimensions['use_y']; 102 $dst_x = $dimensions['dst_x']; 103 $dst_y = $dimensions['dst_y']; 104 105 return $this->applyCropAndScale( 106 $dst_x, 107 $dst_y, 108 ($src_x - $copy_x) / 2, 109 ($src_y - $copy_y) / 2, 110 $copy_x, 111 $copy_y, 112 $use_x, 113 $use_y, 114 $this->scaleUp); 115 } 116 117 118 public function getTransformedDimensions(PhabricatorFile $file) { 119 $dst_x = $this->dstX; 120 $dst_y = $this->dstY; 121 122 // If this is transform has fixed dimensions, we can trivially predict 123 // the dimensions of the transformed file. 124 if ($dst_y !== null) { 125 return array($dst_x, $dst_y); 126 } 127 128 $src_x = $file->getImageWidth(); 129 $src_y = $file->getImageHeight(); 130 131 if (!$src_x || !$src_y) { 132 return null; 133 } 134 135 $dimensions = $this->computeDimensions( 136 $src_x, 137 $src_y, 138 $dst_x, 139 $dst_y); 140 141 return array($dimensions['dst_x'], $dimensions['dst_y']); 142 } 143 144 145 private function computeDimensions($src_x, $src_y, $dst_x, $dst_y) { 146 if ($dst_y === null) { 147 // If we only have one dimension, it represents a maximum dimension. 148 // The other dimension of the transform is scaled appropriately, except 149 // that we never generate images with crazily extreme aspect ratios. 150 if ($src_x < $src_y) { 151 // This is a tall, narrow image. Use the maximum dimension for the 152 // height and scale the width. 153 $use_y = $dst_x; 154 $dst_y = $dst_x; 155 156 $use_x = $dst_y * ($src_x / $src_y); 157 $dst_x = max($dst_y / 4, $use_x); 158 } else { 159 // This is a short, wide image. Use the maximum dimension for the width 160 // and scale the height. 161 $use_x = $dst_x; 162 163 $use_y = $dst_x * ($src_y / $src_x); 164 $dst_y = max($dst_x / 4, $use_y); 165 } 166 167 // In this mode, we always copy the entire source image. We may generate 168 // margins in the output. 169 $copy_x = $src_x; 170 $copy_y = $src_y; 171 } else { 172 $scale_up = $this->scaleUp; 173 174 // Otherwise, both dimensions are fixed. Figure out how much we'd have to 175 // scale the image down along each dimension to get the entire thing to 176 // fit. 177 $scale_x = ($dst_x / $src_x); 178 $scale_y = ($dst_y / $src_y); 179 180 if (!$scale_up) { 181 $scale_x = min($scale_x, 1); 182 $scale_y = min($scale_y, 1); 183 } 184 185 if ($scale_x > $scale_y) { 186 // This image is relatively tall and narrow. We're going to crop off the 187 // top and bottom. 188 $scale = $scale_x; 189 } else { 190 // This image is relatively short and wide. We're going to crop off the 191 // left and right. 192 $scale = $scale_y; 193 } 194 195 $copy_x = $dst_x / $scale; 196 $copy_y = $dst_y / $scale; 197 198 if (!$scale_up) { 199 $copy_x = min($src_x, $copy_x); 200 $copy_y = min($src_y, $copy_y); 201 } 202 203 // In this mode, we always use the entire destination image. We may 204 // crop the source input. 205 $use_x = $dst_x; 206 $use_y = $dst_y; 207 } 208 209 return array( 210 'copy_x' => $copy_x, 211 'copy_y' => $copy_y, 212 'use_x' => $use_x, 213 'use_y' => $use_y, 214 'dst_x' => $dst_x, 215 'dst_y' => $dst_y, 216 ); 217 } 218 219 220 public function getDefaultTransform(PhabricatorFile $file) { 221 $x = (int)$this->dstX; 222 $y = (int)$this->dstY; 223 $name = 'image-'.$x.'x'.nonempty($y, $x).'.png'; 224 225 $root = dirname(phutil_get_library_root('phabricator')); 226 $data = Filesystem::readFile($root.'/resources/builtin/'.$name); 227 228 return $this->newFileFromData($data); 229 } 230 231}