3 namespace wcf\system\image\adapter
;
5 use wcf\system\exception\SystemException
;
9 * Wrapper for image adapters.
11 * @author Alexander Ebert
12 * @copyright 2001-2019 WoltLab GmbH
13 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
14 * @package WoltLabSuite\Core\System\Image\Adapter
16 class ImageAdapter
implements IImageAdapter
, IMemoryAwareImageAdapter
19 * IImageAdapter object
25 * supported relative positions
28 protected $relativePositions = [
41 * Creates a new ImageAdapter instance.
43 * @param string $adapterClassName
45 public function __construct($adapterClassName)
47 $this->adapter
= new $adapterClassName();
53 public function load($image, $type = 0)
55 $this->adapter
->load($image, $type);
61 public function loadFile($file)
63 if (!\file_exists
($file) ||
!\
is_readable($file)) {
64 throw new SystemException("Image '" . $file . "' is not readable or does not exists.");
67 $this->adapter
->loadFile($file);
73 public function createEmptyImage($width, $height)
75 $this->adapter
->createEmptyImage($width, $height);
81 public function createThumbnail($maxWidth, $maxHeight, $preserveAspectRatio = true)
83 if ($maxWidth > $this->getWidth() && $maxHeight > $this->getHeight()) {
84 throw new SystemException("Dimensions for thumbnail can not exceed image dimensions.");
87 $maxHeight = \
min($maxHeight, $this->getHeight());
88 $maxWidth = \
min($maxWidth, $this->getWidth());
90 return $this->adapter
->createThumbnail($maxWidth, $maxHeight, $preserveAspectRatio);
96 public function clip($originX, $originY, $width, $height)
98 // validate if coordinates and size are within bounds
99 if ($originX < 0 ||
$originY < 0) {
100 throw new SystemException("Clipping an image requires valid offsets, an offset below zero is invalid.");
102 if ($width <= 0 ||
$height <= 0) {
103 throw new SystemException(
104 "Clipping an image requires valid dimensions, width or height below or equal zero are invalid."
107 if ((($originX +
$width) > $this->getWidth()) ||
(($originY +
$height) > $this->getHeight())) {
108 throw new SystemException("Offset and dimension can not exceed image dimensions.");
111 $this->adapter
->clip($originX, $originY, $width, $height);
117 public function resize($originX, $originY, $originWidth, $originHeight, $targetWidth, $targetHeight)
119 // use origin dimensions if target dimensions are both zero
120 if ($targetWidth == 0 && $targetHeight == 0) {
121 $targetWidth = $originWidth;
122 $targetHeight = $originHeight;
125 $this->adapter
->resize($originX, $originY, $originWidth, $originHeight, $targetWidth, $targetHeight);
131 public function drawRectangle($startX, $startY, $endX, $endY)
133 if (!$this->adapter
->hasColor()) {
134 throw new SystemException("Cannot draw a rectangle unless a color has been specified with setColor().");
137 $this->adapter
->drawRectangle($startX, $startY, $endX, $endY);
143 public function drawText($text, $x, $y, $font, $size, $opacity = 1.0)
145 if (!$this->adapter
->hasColor()) {
146 throw new SystemException("Cannot draw text unless a color has been specified with setColor().");
150 if ($opacity < 0 ||
$opacity > 1) {
151 throw new SystemException("Invalid opacity value given.");
154 $this->adapter
->drawText($text, $x, $y, $font, $size, $opacity);
160 public function drawTextRelative($text, $position, $margin, $offsetX, $offsetY, $font, $size, $opacity = 1.0)
162 if (!$this->adapter
->hasColor()) {
163 throw new SystemException("Cannot draw text unless a color has been specified with setColor().");
167 if (!\
in_array($position, $this->relativePositions
)) {
168 throw new SystemException("Unknown relative position '" . $position . "'.");
172 if ($margin < 0 ||
$margin >= $this->getHeight() / 2 ||
$margin >= $this->getWidth() / 2) {
173 throw new SystemException("Margin has to be positive and respect image dimensions.");
177 if ($opacity < 0 ||
$opacity > 1) {
178 throw new SystemException("Invalid opacity value given.");
181 $this->adapter
->drawTextRelative($text, $position, $margin, $offsetX, $offsetY, $font, $size, $opacity);
187 public function textFitsImage($text, $margin, $font, $size)
189 return $this->adapter
->textFitsImage($text, $margin, $font, $size);
195 public function adjustFontSize($text, $margin, $font, $size)
198 while ($size && !$this->textFitsImage($text, $margin, $font, $size)) {
208 public function setColor($red, $green, $blue)
210 $this->adapter
->setColor($red, $green, $blue);
216 public function hasColor()
218 return $this->adapter
->hasColor();
224 public function setTransparentColor($red, $green, $blue)
226 $this->adapter
->setTransparentColor($red, $green, $blue);
232 public function writeImage($image, $filename = null)
234 if ($filename === null) {
236 $image = $this->adapter
->getImage();
239 $this->adapter
->writeImage($image, $filename);
245 public function getImage()
247 return $this->adapter
->getImage();
253 public function getWidth()
255 return $this->adapter
->getWidth();
261 public function getHeight()
263 return $this->adapter
->getHeight();
269 public function getType()
271 return $this->adapter
->getType();
277 public function rotate($degrees)
279 if ($degrees > 360.0 ||
$degrees < 0.0) {
280 throw new SystemException("Degrees must be a value between 0 and 360.");
283 return $this->adapter
->rotate($degrees);
289 public function overlayImage($file, $x, $y, $opacity)
292 if (!\file_exists
($file)) {
293 throw new SystemException("Image '" . $file . "' does not exist.");
297 if ($opacity < 0 ||
$opacity > 1) {
298 throw new SystemException("Invalid opacity value given.");
301 $this->adapter
->overlayImage($file, $x, $y, $opacity);
307 public function overlayImageRelative($file, $position, $margin, $opacity)
310 if (!\file_exists
($file)) {
311 throw new SystemException("Image '" . $file . "' does not exist.");
315 if (!\
in_array($position, $this->relativePositions
)) {
316 throw new SystemException("Unknown relative position '" . $position . "'.");
320 if ($margin < 0 ||
$margin >= $this->getHeight() / 2 ||
$margin >= $this->getWidth() / 2) {
321 throw new SystemException("Margin has to be positive and respect image dimensions.");
325 if ($opacity < 0 ||
$opacity > 1) {
326 throw new SystemException("Invalid opacity value given.");
329 $adapterClassName = \
get_class($this->adapter
);
331 /** @var IImageAdapter $overlayImage */
332 $overlayImage = new $adapterClassName();
333 $overlayImage->loadFile($file);
334 $overlayHeight = $overlayImage->getHeight();
335 $overlayWidth = $overlayImage->getWidth();
337 // calculate y coordinate
349 $x = \floor
(($this->getWidth() - $overlayWidth) / 2);
355 $x = $this->getWidth() - $overlayWidth - $margin;
359 // calculate y coordinate
371 $y = \floor
(($this->getHeight() - $overlayHeight) / 2);
377 $y = $this->getHeight() - $overlayHeight - $margin;
381 $this->overlayImage($file, $x, $y, $opacity);
387 public function checkMemoryLimit($width, $height, $mimeType)
389 if ($this->adapter
instanceof IMemoryAwareImageAdapter
) {
390 return $this->adapter
->checkMemoryLimit($width, $height, $mimeType);
393 $channels = $mimeType == 'image/png' ?
4 : 3;
395 return FileUtil
::checkMemoryLimit($width * $height * $channels * 2.1);
401 public function saveImageAs($image, string $filename, string $type, int $quality = 100): void
412 throw new \
InvalidArgumentException("Unsupported image format '{$type}'.");
415 if ($quality < 0 ||
$quality > 100) {
416 throw new \
InvalidArgumentException("The quality must be an integer between 0 and 100.");
419 $this->adapter
->saveImageAs($image, $filename, $type, $quality);
425 public static function isSupported()