Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion phpstan.dist.neon
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ parameters:
- app/Views/errors/html/*
- system/Commands/Generators/Views/*
- system/Debug/Toolbar/Views/toolbar.tpl.php
- system/Images/Handlers/GDHandler.php
- system/Test/Mock/MockCommon.php
- system/ThirdParty/*
- system/Validation/Views/single.php
Expand Down
28 changes: 8 additions & 20 deletions system/Images/Handlers/BaseHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
use Config\Images;

/**
* Base image handling implementation
* Base image handling implementation.
*
* @template T of object
*/
abstract class BaseHandler implements ImageHandlerInterface
{
Expand Down Expand Up @@ -90,7 +92,7 @@ abstract class BaseHandler implements ImageHandlerInterface
/**
* Default options for text watermarking.
*
* @var array
* @var array<string, mixed>
*/
protected $textDefaults = [
'fontPath' => null,
Expand All @@ -110,7 +112,7 @@ abstract class BaseHandler implements ImageHandlerInterface
/**
* Image types with support for transparency.
*
* @var array
* @var list<int>
*/
protected $supportTransparency = [
IMAGETYPE_PNG,
Expand All @@ -120,7 +122,7 @@ abstract class BaseHandler implements ImageHandlerInterface
/**
* Temporary image used by the different engines.
*
* @var resource|null
* @var T|null
*/
protected $resource;

Expand Down Expand Up @@ -193,7 +195,6 @@ protected function image(): Image
throw ImageException::forMissingImage();
}

// Verify the loaded image is an Image instance
if (! $this->image instanceof Image) {
throw ImageException::forInvalidPath();
}
Expand All @@ -203,7 +204,6 @@ protected function image(): Image
throw ImageException::forFileNotSupported();
}

// Note that the image has been verified
$this->verified = true;

return $this->image;
Expand All @@ -214,7 +214,7 @@ protected function image(): Image
* Good for extending the system or doing things this library
* is not intended to do.
*
* @return resource
* @return T
*/
public function getResource()
{
Expand Down Expand Up @@ -246,7 +246,6 @@ public function withResource()
*/
public function resize(int $width, int $height, bool $maintainRatio = false, string $masterDim = 'auto')
{
// If the target width/height match the source, then we have nothing to do here.
if ($this->image()->origWidth === $width && $this->image()->origHeight === $height) {
return $this;
}
Expand Down Expand Up @@ -316,28 +315,20 @@ public function convert(int $imageType)
*/
public function rotate(float $angle)
{
// Allowed rotation values
$degs = [
90.0,
180.0,
270.0,
];
$degs = [90.0, 180.0, 270.0];

if (! in_array($angle, $degs, true)) {
throw ImageException::forMissingAngle();
}

// cast angle as an int, for our use
$angle = (int) $angle;

// Reassign the width and height
if ($angle === 90 || $angle === 270) {
$temp = $this->height;
$this->width = $this->height;
$this->height = $temp;
}

// Call the Handler-specific version.
$this->_rotate($angle);

return $this;
Expand Down Expand Up @@ -568,8 +559,6 @@ protected function calcAspectRatio($width, $height = null, $origWidth = 0, $orig
throw new InvalidArgumentException('You must supply the parameters: origWidth, origHeight.');
}

// If $height is null, then we have it easy.
// Calc based on full image size and be done.
if ($height === null) {
$height = ($width / $origWidth) * $origHeight;

Expand Down Expand Up @@ -724,7 +713,6 @@ protected function reproportion()
return;
}

// Sanitize
$this->width = (int) $this->width;
$this->height = (int) $this->height;

Expand Down
79 changes: 34 additions & 45 deletions system/Images/Handlers/GDHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,16 @@

use CodeIgniter\Images\Exceptions\ImageException;
use Config\Images;
use GdImage;

/**
* Image handler for GD package
*
* @extends BaseHandler<GdImage>
*/
class GDHandler extends BaseHandler
{
/**
* Constructor.
*
* @param Images|null $config
*
* @throws ImageException
Expand All @@ -43,17 +44,13 @@ public function __construct($config = null)
*/
protected function _rotate(int $angle): bool
{
// Create the image handle
$srcImg = $this->createImage();

// Set the background color
// This won't work with transparent PNG files so we are
// going to have to figure out how to determine the color
// of the alpha channel in a future release.

$white = imagecolorallocate($srcImg, 255, 255, 255);

// Rotate it!
$destImg = imagerotate($srcImg, $angle, $white);

$this->resource = $destImg;
Expand Down Expand Up @@ -110,23 +107,23 @@ protected function _flip(string $direction)
/**
* Get GD version
*
* @return mixed
* @return string
*/
public function getVersion()
{
if (function_exists('gd_info')) {
$gdVersion = @gd_info();

return preg_replace('/\D/', '', $gdVersion['GD Version']);
if (! function_exists('gd_info')) {
return ''; // @codeCoverageIgnore
}

return false;
$gdVersion = @gd_info();

return preg_replace('/\D/', '', $gdVersion['GD Version']) ?? '';
}

/**
* Resizes the image.
*
* @return GDHandler
* @return $this
*/
public function _resize(bool $maintainRatio = false)
{
Expand All @@ -136,7 +133,7 @@ public function _resize(bool $maintainRatio = false)
/**
* Crops the image.
*
* @return GDHandler
* @return $this
*/
public function _crop()
{
Expand All @@ -154,7 +151,6 @@ protected function process(string $action)
$origHeight = $this->image()->origHeight;

if ($action === 'crop') {
// Reassign the source width/height if cropping
$origWidth = $this->width;
$origHeight = $this->height;

Expand All @@ -165,7 +161,6 @@ protected function process(string $action)
$this->image()->origWidth = $this->width;
}

// Create the image handle
$src = $this->createImage();

if (function_exists('imagecreatetruecolor')) {
Expand All @@ -178,7 +173,6 @@ protected function process(string $action)

$dest = $create($this->width, $this->height);

// for png and webp we can actually preserve transparency
if (in_array($this->image()->imageType, $this->supportTransparency, true)) {
imagealphablending($dest, false);
imagesavealpha($dest, true);
Expand Down Expand Up @@ -209,7 +203,7 @@ public function save(?string $target = null, int $quality = 90): bool

// If no new resource has been created, then we're
// simply copy the existing one.
if (empty($this->resource) && $quality === 100) {
if ($this->resource === null && $quality === 100) {
if ($original === null) {
return true;
}
Expand All @@ -222,7 +216,6 @@ public function save(?string $target = null, int $quality = 90): bool

$this->ensureResource();

// for png and webp we can actually preserve transparency
if (in_array($this->image()->imageType, $this->supportTransparency, true)) {
imagepalettetotruecolor($this->resource);
imagealphablending($this->resource, false);
Expand Down Expand Up @@ -284,10 +277,7 @@ public function save(?string $target = null, int $quality = 90): bool
/**
* Create Image Resource
*
* This simply creates an image resource handle
* based on the type of image being processed
*
* @return bool|resource
* @return GdImage
*/
protected function createImage(string $path = '', string $imageType = '')
{
Expand All @@ -312,7 +302,6 @@ protected function createImage(string $path = '', string $imageType = '')
protected function ensureResource()
{
if ($this->resource === null) {
// if valid image type, make corresponding image resource
$this->resource = $this->getImageResource(
$this->image()->getPathname(),
$this->image()->imageType,
Expand All @@ -326,7 +315,7 @@ protected function ensureResource()
* @param string $path Image path
* @param int $imageType Image type
*
* @return bool|resource
* @return GdImage
*
* @throws ImageException
*/
Expand All @@ -338,32 +327,42 @@ protected function getImageResource(string $path, int $imageType)
throw ImageException::forInvalidImageCreate(lang('Images.gifNotSupported'));
}

return imagecreatefromgif($path);
$resource = imagecreatefromgif($path);
break;

case IMAGETYPE_JPEG:
if (! function_exists('imagecreatefromjpeg')) {
throw ImageException::forInvalidImageCreate(lang('Images.jpgNotSupported'));
}

return imagecreatefromjpeg($path);
$resource = imagecreatefromjpeg($path);
break;

case IMAGETYPE_PNG:
if (! function_exists('imagecreatefrompng')) {
throw ImageException::forInvalidImageCreate(lang('Images.pngNotSupported'));
}

return @imagecreatefrompng($path);
$resource = @imagecreatefrompng($path);
break;

case IMAGETYPE_WEBP:
if (! function_exists('imagecreatefromwebp')) {
throw ImageException::forInvalidImageCreate(lang('Images.webpNotSupported'));
}

return imagecreatefromwebp($path);
$resource = imagecreatefromwebp($path);
break;

default:
throw ImageException::forInvalidImageCreate('Ima');
}

if ($resource === false) {
throw ImageException::forInvalidImageCreate($path); // @codeCoverageIgnore
}

return $resource;
}

/**
Expand All @@ -386,10 +385,7 @@ protected function _text(string $text, array $options = [])
$options['hOffset'] *= -1;
}

// Set font width and height
// These are calculated differently depending on
// whether we are using the true type font or not
if (! empty($options['fontPath'])) {
if (($options['fontPath'] ?? '') !== '') {
if (function_exists('imagettfbbox')) {
$temp = imagettfbbox($options['fontSize'], 0, $options['fontPath'], $text);
$temp = $temp[2] - $temp[0];
Expand All @@ -408,19 +404,16 @@ protected function _text(string $text, array $options = [])
$options['fontheight'] = $fontheight;
$options['fontwidth'] = $fontwidth;

// Set base X and Y axis values
$xAxis = $options['hOffset'] + $options['padding'];
$yAxis = $options['vOffset'] + $options['padding'];

// Set vertical alignment
if ($options['vAlign'] === 'middle') {
// Don't apply padding when you're in the middle of the image.
$yAxis += ($this->image()->origHeight / 2) + ($fontheight / 2) - $options['padding'] - $fontheight - $options['shadowOffset'];
} elseif ($options['vAlign'] === 'bottom') {
$yAxis = ($this->image()->origHeight - $fontheight - $options['shadowOffset'] - ($fontheight / 2)) - $yAxis;
}

// Set horizontal alignment
if ($options['hAlign'] === 'right') {
$xAxis += ($this->image()->origWidth - ($fontwidth * strlen($text)) - $options['shadowOffset']) - (2 * $options['padding']);
} elseif ($options['hAlign'] === 'center') {
Expand All @@ -431,7 +424,6 @@ protected function _text(string $text, array $options = [])
$options['yAxis'] = $yAxis;

if ($options['withShadow']) {
// Offset from text
$options['xShadow'] = $xAxis + $options['shadowOffset'];
$options['yShadow'] = $yAxis + $options['shadowOffset'];

Expand All @@ -444,17 +436,15 @@ protected function _text(string $text, array $options = [])
/**
* Handler-specific method for overlaying text on an image.
*
* @param bool $isShadow Whether we are drawing the dropshadow or actual text
* @param array<string, mixed> $options
* @param bool $isShadow Whether we are drawing the dropshadow or actual text
*
* @return void
*/
protected function textOverlay(string $text, array $options = [], bool $isShadow = false)
{
$src = $this->createImage();

/* Set RGB values for shadow
*
* Get the rest of the string and split it into 2-length
* hex values:
*/
$opacity = (int) ($options['opacity'] * 127);

// Allow opacity to be applied to the text
Expand All @@ -473,8 +463,7 @@ protected function textOverlay(string $text, array $options = [], bool $isShadow
$xAxis = $isShadow ? $options['xShadow'] : $options['xAxis'];
$yAxis = $isShadow ? $options['yShadow'] : $options['yAxis'];

// Add the shadow to the source image
if (! empty($options['fontPath'])) {
if (($options['fontPath'] ?? '') !== '') {
// We have to add fontheight because imagettftext locates the bottom left corner, not top-left corner.
imagettftext($src, $options['fontSize'], 0, (int) $xAxis, (int) ($yAxis + $options['fontheight']), $color, $options['fontPath'], $text);
} else {
Expand Down
Loading
Loading