Merge branch '2.0'
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / system / image / adapter / GDImageAdapter.class.php
CommitLineData
b86ca09c 1<?php
64a820cf 2namespace wcf\system\image\adapter;
a3399fc5 3use wcf\system\exception\SystemException;
a17de04e 4use wcf\util\StringUtil;
64a820cf
AE
5
6/**
7 * Image adapter for bundled GD imaging library.
8 *
9f959ced 9 * @author Alexander Ebert
2b6cb5c2 10 * @copyright 2001-2015 WoltLab GmbH
64a820cf
AE
11 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
12 * @package com.woltlab.wcf
13 * @subpackage system.image.adapter
9f959ced 14 * @category Community Framework
64a820cf
AE
15 */
16class GDImageAdapter implements IImageAdapter {
17 /**
18 * active color
9f959ced
MS
19 * @var integer
20 */
64a820cf
AE
21 protected $color = null;
22
cfdb4fa4
MS
23 /**
24 * red, green, blue data of the active color
25 * @var array
26 */
27 protected $colorData = array();
28
b86ca09c
AE
29 /**
30 * image height
31 * @var integer
a17de04e 32 */
b86ca09c
AE
33 protected $height = 0;
34
35 /**
36 * loaded image
37 * @var resource
a17de04e 38 */
b86ca09c
AE
39 protected $image = null;
40
41 /**
42 * image type
43 * @var integer
44 */
45 protected $type = 0;
46
47 /**
48 * image width
49 * @var integer
a17de04e 50 */
b86ca09c
AE
51 protected $width = 0;
52
53 /**
0ad90fc3 54 * @see \wcf\system\image\adapter\IImageAdapter::load()
b86ca09c
AE
55 */
56 public function load($image, $type = '') {
57 if (!is_resource($image)) {
58 throw new SystemException("Image resource is invalid.");
59 }
60
61 if (empty($type)) {
62 throw new SystemException("Image type is missing.");
63 }
64
65 $this->image = $image;
66 $this->type = $type;
67
68 $this->height = imageSY($this->image);
69 $this->width = imageSX($this->image);
70 }
71
72 /**
0ad90fc3 73 * @see \wcf\system\image\adapter\IImageAdapter::loadFile()
a17de04e 74 */
b86ca09c
AE
75 public function loadFile($file) {
76 list($this->width, $this->height, $this->type) = getImageSize($file);
77
78 switch ($this->type) {
79 case IMAGETYPE_GIF:
80 $this->image = imageCreateFromGif($file);
81 break;
82
83 case IMAGETYPE_JPEG:
84 $this->image = imageCreateFromJpeg($file);
85 break;
86
87 case IMAGETYPE_PNG:
88 $this->image = imageCreateFromPng($file);
89 break;
90
91 default:
92 throw new SystemException("Could not read image '".$file."', format is not recognized.");
93 break;
94 }
95 }
96
45140a75 97 /**
0ad90fc3 98 * @see \wcf\system\image\adapter\IImageAdapter::createEmptyImage()
45140a75
TD
99 */
100 public function createEmptyImage($width, $height) {
101 $this->image = imageCreate($width, $height);
102 $this->type = IMAGETYPE_PNG;
103 $this->setColor(0xFF, 0xFF, 0xFF);
104 $this->color = null;
105 }
106
b86ca09c 107 /**
0ad90fc3 108 * @see \wcf\system\image\adapter\IImageAdapter::createThumbnail()
a17de04e 109 */
b86ca09c 110 public function createThumbnail($maxWidth, $maxHeight, $obtainDimensions = true) {
b86ca09c 111 $width = $height = $x = $y = 0;
32f6cd95
MW
112 $sourceWidth = $this->width;
113 $sourceHeight = $this->height;
b86ca09c
AE
114
115 if ($obtainDimensions) {
a3399fc5
AE
116 if ($maxWidth / $this->width < $maxHeight / $this->height) {
117 $width = $maxWidth;
118 $height = round($this->height * ($width / $this->width));
b86ca09c
AE
119 }
120 else {
a3399fc5
AE
121 $height = $maxHeight;
122 $width = round($this->width * ($height / $this->height));
b86ca09c
AE
123 }
124 }
125 else {
f7ac2ad1
MW
126 $width = $maxWidth;
127 $height = $maxHeight;
a3399fc5 128
f7ac2ad1
MW
129 if ($maxWidth / $this->width < $maxHeight / $this->height) {
130 $cut = (($sourceWidth * ($maxHeight / $this->height)) - $maxWidth) / ($maxHeight / $this->height);
131 $x = ceil($cut / 2);
132 $sourceWidth = $sourceWidth - $x * 2;
b86ca09c
AE
133 }
134 else {
f7ac2ad1
MW
135 $cut = (($sourceHeight * ($maxWidth / $this->width)) - $maxHeight) / ($maxWidth / $this->width);
136 $y = ceil($cut / 2);
137 $sourceHeight = $sourceHeight - $y * 2;
b86ca09c
AE
138 }
139 }
140
141 // resize image
142 $image = imageCreateTrueColor($width, $height);
143 imageAlphaBlending($image, false);
32f6cd95 144 imageCopyResampled($image, $this->image, 0, 0, $x, $y, $width, $height, $sourceWidth, $sourceHeight);
b86ca09c
AE
145 imageSaveAlpha($image, true);
146
147 return $image;
148 }
149
150 /**
0ad90fc3 151 * @see \wcf\system\image\adapter\IImageAdapter::clip()
b86ca09c
AE
152 */
153 public function clip($originX, $originY, $width, $height) {
b86ca09c
AE
154 $image = imageCreateTrueColor($width, $height);
155 imageAlphaBlending($image, false);
156
157 imageCopy($image, $this->image, 0, 0, $originX, $originY, $width, $height);
158 imageSaveAlpha($image, true);
159
c3844f55 160 // reload image to update image resource, width and height
08c8e6f8 161 $this->load($image, $this->type);
b86ca09c
AE
162 }
163
164 /**
0ad90fc3 165 * @see \wcf\system\image\adapter\IImageAdapter::resize()
b86ca09c 166 */
6eb4648f 167 public function resize($originX, $originY, $originWidth, $originHeight, $targetWidth = 0, $targetHeight = 0) {
b86ca09c
AE
168 $image = imageCreateTrueColor($targetWidth, $targetHeight);
169 imageAlphaBlending($image, false);
170
6eb4648f 171 imageCopyResampled($image, $this->image, 0, 0, $originX, $originY, $targetWidth, $targetHeight, $originWidth, $originHeight);
b86ca09c
AE
172 imageSaveAlpha($image, true);
173
c3844f55 174 // reload image to update image resource, width and height
08c8e6f8 175 $this->load($image, $this->type);
b86ca09c
AE
176 }
177
178 /**
0ad90fc3 179 * @see \wcf\system\image\adapter\IImageAdapter::drawRectangle()
b86ca09c 180 */
64a820cf
AE
181 public function drawRectangle($startX, $startY, $endX, $endY) {
182 imageFilledRectangle($this->image, $startX, $startY, $endX, $endY, $this->color);
b86ca09c
AE
183 }
184
185 /**
0ad90fc3 186 * @see \wcf\system\image\adapter\IImageAdapter::drawText()
b86ca09c 187 */
46ce632b 188 public function drawText($text, $x, $y, $font, $size, $opacity = 1) {
cfdb4fa4
MS
189 // set opacity
190 $color = imagecolorallocatealpha($this->image, $this->colorData['red'], $this->colorData['green'], $this->colorData['blue'], (1 - $opacity) * 127);
d3e0ca88 191
46ce632b
MW
192 // draw text
193 imagettftext($this->image, $size, 0, $x, $y, $color, $font, $text);
cfdb4fa4
MS
194 }
195
196 /**
197 * @see \wcf\system\image\adapter\IImageAdapter::drawTextRelative()
198 */
46ce632b
MW
199 public function drawTextRelative($text, $position, $margin, $offsetX, $offsetY, $font, $size, $opacity = 1) {
200 // split text into multiple lines
201 $lines = explode("\n", StringUtil::unifyNewlines($text));
cfdb4fa4 202
46ce632b
MW
203 // calc text width, height and first line height
204 $box = imagettfbbox($size, 0, $font, $text);
205 $firstLineBox = imagettfbbox($size, 0, $font, $lines[0]);
206 $textWidth = abs($box[0] - $box[2]);
207 $textHeight = abs($box[7] - $box[1]);
208 $firstLineHeight = abs($firstLineBox[7] - $firstLineBox[1]);
cfdb4fa4 209
46ce632b
MW
210 // calculate x coordinate
211 $x = 0;
212 switch ($position) {
213 case 'topLeft':
214 case 'middleLeft':
215 case 'bottomLeft':
216 $x = $margin;
8c56908c
MS
217 break;
218
46ce632b
MW
219 case 'topCenter':
220 case 'middleCenter':
221 case 'bottomCenter':
222 $x = floor(($this->getWidth() - $textWidth) / 2);
8c56908c
MS
223 break;
224
46ce632b
MW
225 case 'topRight':
226 case 'middleRight':
227 case 'bottomRight':
228 $x = $this->getWidth() - $textWidth - $margin;
8c56908c 229 break;
46ce632b 230 }
8c56908c 231
46ce632b
MW
232 // calculate y coordinate
233 $y = 0;
234 switch ($position) {
235 case 'topLeft':
236 case 'topCenter':
237 case 'topRight':
238 $y = $margin + $firstLineHeight;
8c56908c
MS
239 break;
240
46ce632b
MW
241 case 'middleLeft':
242 case 'middleCenter':
243 case 'middleRight':
244 $y = floor(($this->getHeight() - $textHeight) / 2) + $firstLineHeight;
8c56908c
MS
245 break;
246
46ce632b
MW
247 case 'bottomLeft':
248 case 'bottomCenter':
249 case 'bottomRight':
250 $y = $this->getHeight() - $textHeight + $firstLineHeight - $margin;
8c56908c 251 break;
cfdb4fa4 252 }
46ce632b
MW
253
254 $this->drawText($text, $x + $offsetX, $y + $offsetY, $font, $size, $opacity);
b86ca09c
AE
255 }
256
8c56908c
MS
257 /**
258 * @see \wcf\system\image\adapter\IImageAdapter::textFitsImage()
259 */
260 public function textFitsImage($text, $margin, $font, $size) {
261 $box = imagettfbbox($size, 0, $font, $text);
262
263 $textWidth = abs($box[0] - $box[2]);
264 $textHeight = abs($box[7] - $box[1]);
265
266 return ($textWidth + 2 * $margin <= $this->getWidth() && $textHeight + 2 * $margin <= $this->getHeight());
267 }
268
269 /**
270 * @see \wcf\system\image\adapter\IImageAdapter::adjustFontSize()
271 */
272 public function adjustFontSize($text, $margin, $font, $size) {
273 // does nothing
274 }
275
b86ca09c 276 /**
0ad90fc3 277 * @see \wcf\system\image\adapter\IImageAdapter::setColor()
d726f13d 278 */
64a820cf
AE
279 public function setColor($red, $green, $blue) {
280 $this->color = imageColorAllocate($this->image, $red, $green, $blue);
cfdb4fa4
MS
281
282 // save data of the color
283 $this->colorData = array(
284 'red' => $red,
285 'green' => $green,
286 'blue' => $blue
287 );
b86ca09c
AE
288 }
289
290 /**
0ad90fc3 291 * @see \wcf\system\image\adapter\IImageAdapter::hasColor()
64a820cf
AE
292 */
293 public function hasColor() {
294 return ($this->color !== null);
295 }
296
04e3288d 297 /**
0ad90fc3 298 * @see \wcf\system\image\adapter\IImageAdapter::setTransparentColor()
04e3288d
TS
299 */
300 public function setTransparentColor($red, $green, $blue) {
301 if ($this->type == IMAGETYPE_PNG) {
302 $color = imagecolorallocate($this->image, $red, $green, $blue);
303 imageColorTransparent($this->image, $color);
304 }
305 }
306
64a820cf 307 /**
0ad90fc3 308 * @see \wcf\system\image\adapter\IImageAdapter::writeImage()
64a820cf 309 */
b86ca09c 310 public function writeImage($image, $filename) {
a3399fc5
AE
311 if (!is_resource($image)) {
312 throw new SystemException("Given image is not a valid image resource.");
313 }
314
b86ca09c
AE
315 ob_start();
316
64a820cf 317 if ($this->type == IMAGETYPE_GIF) {
b86ca09c
AE
318 imageGIF($image);
319 }
64a820cf 320 else if ($this->type == IMAGETYPE_PNG) {
b86ca09c
AE
321 imagePNG($image);
322 }
323 else if (function_exists('imageJPEG')) {
6da905f4 324 imageJPEG($image, null, 90);
b86ca09c
AE
325 }
326
45140a75 327 $stream = ob_get_contents();
b86ca09c
AE
328 ob_end_clean();
329
45140a75 330 file_put_contents($filename, $stream);
b86ca09c
AE
331 }
332
a3399fc5 333 /**
0ad90fc3 334 * @see \wcf\system\image\adapter\IImageAdapter::getWidth()
d726f13d 335 */
a3399fc5
AE
336 public function getWidth() {
337 return $this->width;
338 }
339
340 /**
0ad90fc3 341 * @see \wcf\system\image\adapter\IImageAdapter::getHeight()
a3399fc5
AE
342 */
343 public function getHeight() {
344 return $this->height;
345 }
346
aa6ceb28
MW
347 /**
348 * @see \wcf\system\image\adapter\IImageAdapter::getType()
349 */
350 public function getType() {
351 return $this->type;
352 }
353
b86ca09c 354 /**
0ad90fc3 355 * @see \wcf\system\image\adapter\IImageAdapter::getImage()
b86ca09c
AE
356 */
357 public function getImage() {
358 return $this->image;
359 }
87646f44 360
f268d97a
MW
361 /**
362 * @see \wcf\system\image\adapter\IImageAdapter::rotate()
363 */
364 public function rotate($degrees) {
a8e8435c 365 // imagerotate interpretes degrees as counter-clockwise
f268d97a
MW
366 return imagerotate($this->image, (360.0 - $degrees), ($this->color ?: 0));
367 }
368
a7c0248c
MS
369 /**
370 * @see \wcf\system\image\adapter\IImageAdapter::overlayImage()
371 */
372 public function overlayImage($file, $x, $y, $opacity) {
373 $overlayImage = new self();
374 $overlayImage->loadFile($file);
375
376 // fix PNG alpha channel handling
377 // see http://php.net/manual/en/function.imagecopymerge.php#92787
378 $cut = imagecreatetruecolor($overlayImage->getWidth(), $overlayImage->getHeight());
379 imagecopy($cut, $this->image, 0, 0, $x, $y, $overlayImage->getWidth(), $overlayImage->getHeight());
380 imagecopy($cut, $overlayImage->image, 0, 0, 0, 0, $overlayImage->getWidth(), $overlayImage->getHeight());
381 imagecopymerge($this->image, $cut, $x, $y, 0, 0, $overlayImage->getWidth(), $overlayImage->getHeight(), $opacity * 100);
382 }
383
384 /**
385 * @see \wcf\system\image\adapter\IImageAdapter::overlayImageRelative()
386 */
387 public function overlayImageRelative($file, $position, $margin, $opacity) {
388 // does nothing
389 }
390
87646f44 391 /**
0ad90fc3 392 * @see \wcf\system\image\adapter\IImageAdapter::isSupported()
d726f13d 393 */
87646f44
AE
394 public static function isSupported() {
395 return true;
396 }
b86ca09c 397}