<?php
/**
- * @file src/Object/Image.php
- * @brief This file contains the Image class for image processing
+ * @copyright Copyright (C) 2010-2022, the Friendica project
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
*/
+
namespace Friendica\Object;
use Exception;
-use Friendica\Core\Config;
-use Friendica\Core\System;
+use Friendica\DI;
use Friendica\Util\Images;
use Imagick;
use ImagickPixel;
private $types;
/**
- * @brief Constructor
- * @param string $data
- * @param boolean $type optional, default null
+ * Constructor
+ *
+ * @param string $data Image data
+ * @param string $type optional, default null
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
- public function __construct($data, $type = null)
+ public function __construct(string $data, string $type = null)
{
$this->imagick = class_exists('Imagick');
$this->types = Images::supportedTypes();
$this->type = $type;
if ($this->isImagick() && $this->loadData($data)) {
- return true;
+ return;
} else {
// Failed to load with Imagick, fallback
$this->imagick = false;
}
- return $this->loadData($data);
+ $this->loadData($data);
}
/**
- * @brief Destructor
+ * Destructor
+ *
* @return void
*/
public function __destruct()
}
/**
- * @param string $data data
- * @return boolean
+ * Loads image data into handler class
+ *
+ * @param string $data Image data
+ * @return boolean Success
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
- private function loadData($data)
+ private function loadData(string $data): bool
{
if ($this->isImagick()) {
$this->image = new Imagick();
$this->image->setFormat($format);
// Always coalesce, if it is not a multi-frame image it won't hurt anyway
- $this->image = $this->image->coalesceImages();
+ try {
+ $this->image = $this->image->coalesceImages();
+ } catch (Exception $e) {
+ return false;
+ }
/*
* setup the compression here, so we'll do it only once
*/
switch ($this->getType()) {
- case "image/png":
- $quality = Config::get('system', 'png_quality');
- if ((! $quality) || ($quality > 9)) {
- $quality = PNG_QUALITY;
- }
+ case 'image/png':
+ $quality = DI::config()->get('system', 'png_quality');
/*
* From http://www.imagemagick.org/script/command-line-options.php#quality:
*
$quality = $quality * 10;
$this->image->setCompressionQuality($quality);
break;
- case "image/jpeg":
- $quality = Config::get('system', 'jpeg_quality');
- if ((! $quality) || ($quality > 100)) {
- $quality = JPEG_QUALITY;
- }
+
+ case 'image/jpg':
+ case 'image/jpeg':
+ $quality = DI::config()->get('system', 'jpeg_quality');
$this->image->setCompressionQuality($quality);
}
}
$this->valid = false;
- $this->image = @imagecreatefromstring($data);
- if ($this->image !== false) {
- $this->width = imagesx($this->image);
- $this->height = imagesy($this->image);
- $this->valid = true;
- imagealphablending($this->image, false);
- imagesavealpha($this->image, true);
-
- return true;
+ try {
+ $this->image = @imagecreatefromstring($data);
+ if ($this->image !== false) {
+ $this->width = imagesx($this->image);
+ $this->height = imagesy($this->image);
+ $this->valid = true;
+ imagealphablending($this->image, false);
+ imagesavealpha($this->image, true);
+
+ return true;
+ }
+ } catch (\Throwable $error) {
+ /** @see https://github.com/php/doc-en/commit/d09a881a8e9059d11e756ee59d75bf404d6941ed */
+ if (strstr($error->getMessage(), "gd-webp cannot allocate temporary buffer")) {
+ DI::logger()->notice('Image is probably animated and therefore unsupported', ['error' => $error]);
+ } else {
+ DI::logger()->warning('Unexpected throwable.', ['error' => $error]);
+ }
}
return false;
/**
* @return boolean
*/
- public function isValid()
+ public function isValid(): bool
{
if ($this->isImagick()) {
return ($this->image !== false);
}
if ($this->isImagick()) {
- /* Clean it */
- $this->image = $this->image->deconstructImages();
- return $this->image;
+ try {
+ /* Clean it */
+ $this->image = $this->image->deconstructImages();
+ return $this->image;
+ } catch (Exception $e) {
+ return false;
+ }
}
return $this->image;
}
}
/**
+ * Scales image down
+ *
* @param integer $max max dimension
* @return mixed
*/
- public function scaleDown($max)
+ public function scaleDown(int $max)
{
if (!$this->isValid()) {
return false;
}
/**
+ * Rotates image
+ *
* @param integer $degrees degrees to rotate image
* @return mixed
*/
- public function rotate($degrees)
+ public function rotate(int $degrees)
{
if (!$this->isValid()) {
return false;
}
/**
+ * Flips image
+ *
* @param boolean $horiz optional, default true
* @param boolean $vert optional, default false
* @return mixed
*/
- public function flip($horiz = true, $vert = false)
+ public function flip(bool $horiz = true, bool $vert = false)
{
if (!$this->isValid()) {
return false;
}
/**
- * @param string $filename filename
+ * Fixes orientation and maybe returns EXIF data (?)
+ *
+ * @param string $filename Filename
* @return mixed
*/
- public function orient($filename)
+ public function orient(string $filename)
{
if ($this->isImagick()) {
// based off comment on http://php.net/manual/en/imagick.getimageorientation.php
break;
}
- // Logger::log('exif: ' . print_r($exif,true));
return $exif;
}
/**
- * @param integer $min minimum dimension
+ * Rescales image to minimum size
+ *
+ * @param integer $min Minimum dimension
* @return mixed
*/
- public function scaleUp($min)
+ public function scaleUp(int $min)
{
if (!$this->isValid()) {
return false;
}
/**
- * @param integer $dim dimension
+ * Scales image to square
+ *
+ * @param integer $dim Dimension
* @return mixed
*/
- public function scaleToSquare($dim)
+ public function scaleToSquare(int $dim)
{
if (!$this->isValid()) {
return false;
}
/**
- * @brief Scale image to target dimensions
+ * Scale image to target dimensions
*
- * @param int $dest_width
- * @param int $dest_height
- * @return boolean
+ * @param int $dest_width Destination width
+ * @param int $dest_height Destination height
+ * @return boolean Success
*/
- private function scale($dest_width, $dest_height)
+ private function scale(int $dest_width, int $dest_height): bool
{
if (!$this->isValid()) {
return false;
do {
// FIXME - implement horizontal bias for scaling as in following GD functions
// to allow very tall images to be constrained only horizontally.
- $this->image->scaleImage($dest_width, $dest_height);
+ try {
+ $this->image->scaleImage($dest_width, $dest_height);
+ } catch (Exception $e) {
+ // Imagick couldn't use the data
+ return false;
+ }
} while ($this->image->nextImage());
// These may not be necessary anymore
}
/**
+ * Convert a GIF to a PNG to make it static
+ *
+ * @return void
+ */
+ public function toStatic()
+ {
+ if ($this->type != 'image/gif') {
+ return;
+ }
+
+ if ($this->isImagick()) {
+ $this->type == 'image/png';
+ $this->image->setFormat('png');
+ }
+ }
+
+ /**
+ * Crops image
+ *
* @param integer $max maximum
* @param integer $x x coordinate
* @param integer $y y coordinate
* @param integer $h height
* @return mixed
*/
- public function crop($max, $x, $y, $w, $h)
+ public function crop(int $max, int $x, int $y, int $w, int $h)
{
if (!$this->isValid()) {
return false;
$this->image = $dest;
$this->width = imagesx($this->image);
$this->height = imagesy($this->image);
- }
-
- /**
- * @param string $path file path
- * @return mixed
- * @throws \Friendica\Network\HTTPException\InternalServerErrorException
- */
- public function saveToFilePath($path)
- {
- if (!$this->isValid()) {
- return false;
- }
-
- $string = $this->asString();
-
- $a = \get_app();
- $stamp1 = microtime(true);
- file_put_contents($path, $string);
- $a->getProfiler()->saveTimestamp($stamp1, "file", System::callstack());
+ // All successful
+ return true;
}
/**
- * @brief Magic method allowing string casting of an Image object
+ * Magic method allowing string casting of an Image object
*
* Ex: $data = $Image->asString();
* can be replaced by
* @return string
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
- public function __toString() {
- return $this->asString();
+ public function __toString(): string
+ {
+ return (string) $this->asString();
}
/**
+ * Returns image as string or false on failure
+ *
* @return mixed
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
}
if ($this->isImagick()) {
- /* Clean it */
- $this->image = $this->image->deconstructImages();
- $string = $this->image->getImagesBlob();
- return $string;
+ try {
+ /* Clean it */
+ $this->image = $this->image->deconstructImages();
+ $string = $this->image->getImagesBlob();
+ return $string;
+ } catch (Exception $e) {
+ return false;
+ }
}
ob_start();
imageinterlace($this->image, true);
switch ($this->getType()) {
- case "image/png":
- $quality = Config::get('system', 'png_quality');
- if ((!$quality) || ($quality > 9)) {
- $quality = PNG_QUALITY;
- }
+ case 'image/png':
+ $quality = DI::config()->get('system', 'png_quality');
imagepng($this->image, null, $quality);
break;
- case "image/jpeg":
- $quality = Config::get('system', 'jpeg_quality');
- if ((!$quality) || ($quality > 100)) {
- $quality = JPEG_QUALITY;
- }
+
+ case 'image/jpeg':
+ case 'image/jpg':
+ $quality = DI::config()->get('system', 'jpeg_quality');
imagejpeg($this->image, null, $quality);
+ break;
}
$string = ob_get_contents();
ob_end_clean();
return $string;
}
-
- /**
- * @brief supported mimetypes and corresponding file extensions
- * @return array
- * @deprecated in version 2019.12 please use Util\Images::supportedTypes() instead.
- */
- public static function supportedTypes()
- {
- return Images::supportedTypes();
- }
-
- /**
- * @brief Maps Mime types to Imagick formats
- * @return array With with image formats (mime type as key)
- * @deprecated in version 2019.12 please use Util\Images::getFormatsMap() instead.
- */
- public static function getFormatsMap()
- {
- return Images::getFormatsMap();
- }
-
- /**
- * Guess image mimetype from filename or from Content-Type header
- *
- * @param string $filename Image filename
- * @param boolean $fromcurl Check Content-Type header from curl request
- * @param string $header passed headers to take into account
- *
- * @return string|null
- * @throws Exception
- * @deprecated in version 2019.12 please use Util\Images::guessType() instead.
- */
- public static function guessType($filename, $fromcurl = false, $header = '')
- {
- return Images::guessType($filename, $fromcurl, $header);
- }
-
- /**
- * @param string $url url
- * @return array
- * @throws \Friendica\Network\HTTPException\InternalServerErrorException
- * @deprecated in version 2019.12 please use Util\Images::getInfoFromURLCached() instead.
- */
- public static function getInfoFromURL($url)
- {
- return Images::getInfoFromURLCached($url);
- }
-
- /**
- * @param integer $width width
- * @param integer $height height
- * @param integer $max max
- * @return array
- * @deprecated in version 2019.12 please use Util\Images::getScalingDimensions() instead.
- */
- public static function getScalingDimensions($width, $height, $max)
- {
- return Images::getScalingDimensions($width, $height, $max);
- }
}