175 lines
4.6 KiB
PHP
175 lines
4.6 KiB
PHP
<?php
|
|
/**
|
|
* This file is part of the ForkBB <https://github.com/forkbb>.
|
|
*
|
|
* @copyright (c) Visman <mio.visman@yandex.ru, https://github.com/MioVisman>
|
|
* @license The MIT License (MIT)
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace ForkBB\Core\Image;
|
|
|
|
use ForkBB\Core\Files;
|
|
use ForkBB\Core\Image\DefaultDriver;
|
|
use ForkBB\Core\Exceptions\FileException;
|
|
|
|
class GDDriver extends DefaultDriver
|
|
{
|
|
const DEFAULT = false;
|
|
|
|
public function __construct(Files $files)
|
|
{
|
|
parent::__construct($files);
|
|
|
|
$this->ready = \extension_loaded('gd') && \function_exists('\\imagecreatetruecolor');
|
|
}
|
|
|
|
public function readFromStr(string $data): mixed
|
|
{
|
|
if ($this->isBadData(\substr($data, 0, 64))) {
|
|
return false;
|
|
} else {
|
|
return $this->tuning($this->ready ? \imagecreatefromstring($data) : false);
|
|
}
|
|
}
|
|
|
|
public function readFromPath(string $path): mixed
|
|
{
|
|
if (
|
|
! $this->ready
|
|
|| $this->files->isBadPath($path)
|
|
|| $this->isBadData(\file_get_contents($path, false, null, 0, 64))
|
|
) {
|
|
return false;
|
|
} else {
|
|
return $this->tuning(\imagecreatefromstring(\file_get_contents($path)));
|
|
}
|
|
}
|
|
|
|
protected function isBadData(string $data): bool
|
|
{
|
|
if (
|
|
8 === \strpos($data, 'WEBP')
|
|
&& (
|
|
\strpos($data, 'ANIM')
|
|
|| \strpos($data, 'ANMF')
|
|
)
|
|
) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
protected function tuning(mixed $image): mixed
|
|
{
|
|
if (
|
|
false !== $image
|
|
&& (
|
|
false === \imagealphablending($image, false)
|
|
|| false === \imagesavealpha($image, true)
|
|
)
|
|
) {
|
|
throw new FileException('Failed to adjust image');
|
|
} else {
|
|
return $image;
|
|
}
|
|
}
|
|
|
|
public function writeToPath(mixed $image, string $path, int $quality): ?bool
|
|
{
|
|
$args = [$image, $path];
|
|
$type = \pathinfo($path, \PATHINFO_EXTENSION);
|
|
|
|
switch ($type) {
|
|
case 'gif':
|
|
break;
|
|
case 'png':
|
|
$args[] = 9; //(int) \floor((100 - $quality) / 11);
|
|
break;
|
|
case 'jpg':
|
|
$type = 'jpeg';
|
|
$args[] = $quality;
|
|
break;
|
|
case 'webp':
|
|
case 'avif':
|
|
if (\imagecolorstotal($image) > 0) {
|
|
\imagepalettetotruecolor($image);
|
|
|
|
$args = [$this->tuning($image), $path];
|
|
}
|
|
|
|
$args[] = $quality;
|
|
break;
|
|
default:
|
|
return null;
|
|
}
|
|
|
|
$function = '\\image' . $type;
|
|
|
|
if (\function_exists($function)) {
|
|
return $function(...$args);
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public function resize(mixed $image, int $maxW, int $maxH): mixed
|
|
{
|
|
if (! $this->ready) {
|
|
throw new FileException('GD library not enabled');
|
|
}
|
|
|
|
$oldW = \imagesx($image);
|
|
$oldH = \imagesy($image);
|
|
$wr = $maxW < 16 ? 1 : $maxW / $oldW;
|
|
$hr = $maxH < 16 ? 1 : $maxH / $oldH;
|
|
$r = \min($wr, $hr, 1);
|
|
|
|
if (1 == $r) {
|
|
return $image;
|
|
}
|
|
|
|
$width = (int) \round($oldW * $r);
|
|
$height = (int) \round($oldH * $r);
|
|
|
|
if (false === ($result = \imagecreatetruecolor($width, $height))) {
|
|
throw new FileException('Failed to create new truecolor image');
|
|
}
|
|
if (false === ($color = \imagecolorallocatealpha($result, 255, 255, 255, 127))) {
|
|
throw new FileException('Failed to create color for image');
|
|
}
|
|
if (false === \imagefill($result, 0, 0, $color)) {
|
|
throw new FileException('Failed to fill image with color');
|
|
}
|
|
|
|
\imagecolortransparent($result, $color);
|
|
$palette = \imagecolorstotal($image);
|
|
|
|
if (
|
|
$palette > 0
|
|
&& ! \imagetruecolortopalette($result, true, $palette)
|
|
) {
|
|
throw new FileException('Failed to convert image to palette');
|
|
}
|
|
|
|
$result = $this->tuning($result);
|
|
|
|
if (false === \imagecopyresampled($result, $image, 0, 0, 0, 0, $width, $height, $oldW, $oldH)) {
|
|
throw new FileException('Failed to resize image');
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
public function width(mixed $image): int
|
|
{
|
|
return \imagesx($image);
|
|
}
|
|
|
|
public function height(mixed $image): int
|
|
{
|
|
return \imagesy($image);
|
|
}
|
|
}
|