@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
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}