refactor: susun semula struktur folder — Laravel source ke src/
This commit is contained in:
42
vendor/intervention/gif/src/AbstractEntity.php
vendored
Normal file
42
vendor/intervention/gif/src/AbstractEntity.php
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif;
|
||||
|
||||
use Intervention\Gif\Exceptions\EncoderException;
|
||||
use Intervention\Gif\Traits\CanDecode;
|
||||
use Intervention\Gif\Traits\CanEncode;
|
||||
use ReflectionClass;
|
||||
use ReflectionException;
|
||||
use Stringable;
|
||||
|
||||
abstract class AbstractEntity implements Stringable
|
||||
{
|
||||
use CanEncode;
|
||||
use CanDecode;
|
||||
|
||||
public const TERMINATOR = "\x00";
|
||||
|
||||
/**
|
||||
* Get short classname of current instance.
|
||||
*/
|
||||
public static function shortClassname(): ?string
|
||||
{
|
||||
try {
|
||||
return (new ReflectionClass(static::class))->getShortName();
|
||||
} catch (ReflectionException) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cast object to string.
|
||||
*
|
||||
* @throws EncoderException
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->encode();
|
||||
}
|
||||
}
|
||||
10
vendor/intervention/gif/src/AbstractExtension.php
vendored
Normal file
10
vendor/intervention/gif/src/AbstractExtension.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif;
|
||||
|
||||
abstract class AbstractExtension extends AbstractEntity
|
||||
{
|
||||
public const MARKER = "\x21";
|
||||
}
|
||||
97
vendor/intervention/gif/src/Blocks/ApplicationExtension.php
vendored
Normal file
97
vendor/intervention/gif/src/Blocks/ApplicationExtension.php
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Blocks;
|
||||
|
||||
use Intervention\Gif\AbstractExtension;
|
||||
use Intervention\Gif\Exceptions\StateException;
|
||||
|
||||
class ApplicationExtension extends AbstractExtension
|
||||
{
|
||||
public const LABEL = "\xFF";
|
||||
|
||||
/**
|
||||
* Application Identifier & Auth Code.
|
||||
*/
|
||||
protected string $application = '';
|
||||
|
||||
/**
|
||||
* Data Sub Blocks.
|
||||
*
|
||||
* @var array<DataSubBlock>
|
||||
*/
|
||||
protected array $blocks = [];
|
||||
|
||||
/**
|
||||
* Get size of block.
|
||||
*/
|
||||
public function blockSize(): int
|
||||
{
|
||||
return strlen($this->application);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set application name.
|
||||
*/
|
||||
public function setApplication(string $value): self
|
||||
{
|
||||
$this->application = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get application name.
|
||||
*/
|
||||
public function application(): string
|
||||
{
|
||||
return $this->application;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add block to application extension.
|
||||
*/
|
||||
public function addBlock(DataSubBlock $block): self
|
||||
{
|
||||
$this->blocks[] = $block;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set data sub blocks of instance.
|
||||
*
|
||||
* @param array<DataSubBlock> $blocks
|
||||
*/
|
||||
public function setBlocks(array $blocks): self
|
||||
{
|
||||
$this->blocks = $blocks;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get blocks of ApplicationExtension.
|
||||
*
|
||||
* @return array<DataSubBlock>
|
||||
*/
|
||||
public function blocks(): array
|
||||
{
|
||||
return $this->blocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get first block of ApplicationExtension.
|
||||
*
|
||||
* @throws StateException
|
||||
*/
|
||||
public function firstBlock(): DataSubBlock
|
||||
{
|
||||
if (!array_key_exists(0, $this->blocks)) {
|
||||
throw new StateException('Failed to retrieve data sub block');
|
||||
}
|
||||
|
||||
return $this->blocks[0];
|
||||
}
|
||||
}
|
||||
96
vendor/intervention/gif/src/Blocks/Color.php
vendored
Normal file
96
vendor/intervention/gif/src/Blocks/Color.php
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Blocks;
|
||||
|
||||
use Intervention\Gif\AbstractEntity;
|
||||
use Intervention\Gif\Exceptions\InvalidArgumentException;
|
||||
|
||||
class Color extends AbstractEntity
|
||||
{
|
||||
/**
|
||||
* Create new instance.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function __construct(
|
||||
protected int $r = 0,
|
||||
protected int $g = 0,
|
||||
protected int $b = 0
|
||||
) {
|
||||
if ($r < 0 || $r > 255) {
|
||||
throw new InvalidArgumentException('Color channel red must be in range 0 to 255');
|
||||
}
|
||||
|
||||
if ($g < 0 || $g > 255) {
|
||||
throw new InvalidArgumentException('Color channel green must be in range 0 to 255');
|
||||
}
|
||||
|
||||
if ($b < 0 || $b > 255) {
|
||||
throw new InvalidArgumentException('Color channel blue must be in range 0 to 255');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get red value.
|
||||
*/
|
||||
public function red(): int
|
||||
{
|
||||
return $this->r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set red value.
|
||||
*/
|
||||
public function setRed(int $value): self
|
||||
{
|
||||
$this->r = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get green value.
|
||||
*/
|
||||
public function green(): int
|
||||
{
|
||||
return $this->g;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set green value.
|
||||
*/
|
||||
public function setGreen(int $value): self
|
||||
{
|
||||
$this->g = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get blue value.
|
||||
*/
|
||||
public function blue(): int
|
||||
{
|
||||
return $this->b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set blue value.
|
||||
*/
|
||||
public function setBlue(int $value): self
|
||||
{
|
||||
$this->b = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return hash value of current color.
|
||||
*/
|
||||
public function hash(): string
|
||||
{
|
||||
return md5(strval($this->r . $this->g . $this->b));
|
||||
}
|
||||
}
|
||||
123
vendor/intervention/gif/src/Blocks/ColorTable.php
vendored
Normal file
123
vendor/intervention/gif/src/Blocks/ColorTable.php
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Blocks;
|
||||
|
||||
use Intervention\Gif\AbstractEntity;
|
||||
use Intervention\Gif\Exceptions\InvalidArgumentException;
|
||||
|
||||
class ColorTable extends AbstractEntity
|
||||
{
|
||||
/**
|
||||
* Create new instance.
|
||||
*
|
||||
* @param array<Color> $colors
|
||||
*/
|
||||
public function __construct(protected array $colors = [])
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Return array of current colors.
|
||||
*
|
||||
* @return array<Color>
|
||||
*/
|
||||
public function colors(): array
|
||||
{
|
||||
return array_values($this->colors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add color to table.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function addRgb(int $r, int $g, int $b): self
|
||||
{
|
||||
$this->addColor(new Color($r, $g, $b));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add color to table.
|
||||
*/
|
||||
public function addColor(Color $color): self
|
||||
{
|
||||
$this->colors[] = $color;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset colors to array of color objects.
|
||||
*
|
||||
* @param array<Color> $colors
|
||||
*/
|
||||
public function setColors(array $colors): self
|
||||
{
|
||||
$this->empty();
|
||||
foreach ($colors as $color) {
|
||||
$this->addColor($color);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count colors of current instance.
|
||||
*/
|
||||
public function countColors(): int
|
||||
{
|
||||
return count($this->colors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if any colors are present on the current table
|
||||
*/
|
||||
public function hasColors(): bool
|
||||
{
|
||||
return $this->countColors() >= 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Empty color table.
|
||||
*/
|
||||
public function empty(): self
|
||||
{
|
||||
$this->colors = [];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get size of color table in logical screen descriptor.
|
||||
*/
|
||||
public function logicalSize(): int
|
||||
{
|
||||
return match ($this->countColors()) {
|
||||
4 => 1,
|
||||
8 => 2,
|
||||
16 => 3,
|
||||
32 => 4,
|
||||
64 => 5,
|
||||
128 => 6,
|
||||
256 => 7,
|
||||
default => 0,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the number of bytes contained by the current table.
|
||||
*/
|
||||
public function byteSize(): int
|
||||
{
|
||||
if (!$this->hasColors()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 3 * pow(2, $this->logicalSize() + 1);
|
||||
}
|
||||
}
|
||||
47
vendor/intervention/gif/src/Blocks/CommentExtension.php
vendored
Normal file
47
vendor/intervention/gif/src/Blocks/CommentExtension.php
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Blocks;
|
||||
|
||||
use Intervention\Gif\AbstractExtension;
|
||||
|
||||
class CommentExtension extends AbstractExtension
|
||||
{
|
||||
public const LABEL = "\xFE";
|
||||
|
||||
/**
|
||||
* Comment blocks.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected array $comments = [];
|
||||
|
||||
/**
|
||||
* Get all or one comment.
|
||||
*
|
||||
* @return array<string>
|
||||
*/
|
||||
public function comments(): array
|
||||
{
|
||||
return $this->comments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get one comment by key.
|
||||
*/
|
||||
public function comment(int $key): mixed
|
||||
{
|
||||
return $this->comments[$key] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set comment text.
|
||||
*/
|
||||
public function addComment(string $value): self
|
||||
{
|
||||
$this->comments[] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
41
vendor/intervention/gif/src/Blocks/DataSubBlock.php
vendored
Normal file
41
vendor/intervention/gif/src/Blocks/DataSubBlock.php
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Blocks;
|
||||
|
||||
use Intervention\Gif\AbstractEntity;
|
||||
use Intervention\Gif\Exceptions\InvalidArgumentException;
|
||||
|
||||
class DataSubBlock extends AbstractEntity
|
||||
{
|
||||
/**
|
||||
* Create new instance.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function __construct(protected string $value)
|
||||
{
|
||||
if ($this->size() > 255) {
|
||||
throw new InvalidArgumentException(
|
||||
'Data Sub-Block can not have a block size larger than 255 bytes'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return size of current block.
|
||||
*/
|
||||
public function size(): int
|
||||
{
|
||||
return strlen($this->value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return block value.
|
||||
*/
|
||||
public function value(): string
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
}
|
||||
254
vendor/intervention/gif/src/Blocks/FrameBlock.php
vendored
Normal file
254
vendor/intervention/gif/src/Blocks/FrameBlock.php
vendored
Normal file
@@ -0,0 +1,254 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Blocks;
|
||||
|
||||
use Intervention\Gif\AbstractEntity;
|
||||
|
||||
/**
|
||||
* The GIF files that can be found on the Internet come in a wide variety
|
||||
* of forms. Some strictly adhere to the original specification, others do
|
||||
* not and differ in the actual sequence of blocks or their number.
|
||||
*
|
||||
* For this reason, this libary has this (kind of "virtual") FrameBlock,
|
||||
* which can contain all possible blocks in different order that occur in
|
||||
* a GIF animation.
|
||||
*
|
||||
* - Image Description
|
||||
* - Local Color Table
|
||||
* - Image Data Block
|
||||
* - Plain Text Extension
|
||||
* - Application Extension
|
||||
* - Comment Extension
|
||||
*
|
||||
* The TableBasedImage block, which is a chain of ImageDescriptor, (Local
|
||||
* Color Table) and ImageData, is used as a marker for terminating a
|
||||
* FrameBlock.
|
||||
*
|
||||
* So far I have only seen GIF files that follow this scheme. However, there are
|
||||
* examples which have one (or more) comment extensions added before the end. So
|
||||
* there can be additional "global comments" that are not part of the FrameBlock
|
||||
* and are appended to the GifDataStream afterwards.
|
||||
*/
|
||||
class FrameBlock extends AbstractEntity
|
||||
{
|
||||
protected ?GraphicControlExtension $graphicControlExtension = null;
|
||||
|
||||
protected ?ColorTable $colorTable = null;
|
||||
|
||||
protected ?PlainTextExtension $plainTextExtension = null;
|
||||
|
||||
/**
|
||||
* @var array<ApplicationExtension> $applicationExtensions
|
||||
*/
|
||||
protected array $applicationExtensions = [];
|
||||
|
||||
/**
|
||||
* @var array<CommentExtension> $commentExtensions
|
||||
*/
|
||||
protected array $commentExtensions = [];
|
||||
|
||||
public function __construct(
|
||||
protected ImageDescriptor $imageDescriptor = new ImageDescriptor(),
|
||||
protected ImageData $imageData = new ImageData()
|
||||
) {
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Add entity to block.
|
||||
*/
|
||||
public function addEntity(AbstractEntity $entity): self
|
||||
{
|
||||
return match (true) {
|
||||
$entity instanceof TableBasedImage => $this->setTableBasedImage($entity),
|
||||
$entity instanceof GraphicControlExtension => $this->setGraphicControlExtension($entity),
|
||||
$entity instanceof ImageDescriptor => $this->setImageDescriptor($entity),
|
||||
$entity instanceof ColorTable => $this->setColorTable($entity),
|
||||
$entity instanceof ImageData => $this->setImageData($entity),
|
||||
$entity instanceof PlainTextExtension => $this->setPlainTextExtension($entity),
|
||||
$entity instanceof NetscapeApplicationExtension,
|
||||
$entity instanceof ApplicationExtension => $this->addApplicationExtension($entity),
|
||||
$entity instanceof CommentExtension => $this->addCommentExtension($entity),
|
||||
default => $this,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Return application extensions of current frame block.
|
||||
*
|
||||
* @return array<ApplicationExtension>
|
||||
*/
|
||||
public function applicationExtensions(): array
|
||||
{
|
||||
return $this->applicationExtensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return comment extensions of current frame block.
|
||||
*
|
||||
* @return array<CommentExtension>
|
||||
*/
|
||||
public function commentExtensions(): array
|
||||
{
|
||||
return $this->commentExtensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the graphic control extension.
|
||||
*/
|
||||
public function setGraphicControlExtension(GraphicControlExtension $extension): self
|
||||
{
|
||||
$this->graphicControlExtension = $extension;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the graphic control extension of the current frame block.
|
||||
*/
|
||||
public function graphicControlExtension(): ?GraphicControlExtension
|
||||
{
|
||||
return $this->graphicControlExtension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the image descriptor.
|
||||
*/
|
||||
public function setImageDescriptor(ImageDescriptor $descriptor): self
|
||||
{
|
||||
$this->imageDescriptor = $descriptor;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the image descriptor of the frame block.
|
||||
*/
|
||||
public function imageDescriptor(): ImageDescriptor
|
||||
{
|
||||
return $this->imageDescriptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the color table of the current frame block.
|
||||
*/
|
||||
public function setColorTable(ColorTable $table): self
|
||||
{
|
||||
$this->colorTable = $table;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get color table.
|
||||
*/
|
||||
public function colorTable(): ?ColorTable
|
||||
{
|
||||
return $this->colorTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if frame block has color table.
|
||||
*/
|
||||
public function hasColorTable(): bool
|
||||
{
|
||||
return !is_null($this->colorTable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set image data of frame block.
|
||||
*/
|
||||
public function setImageData(ImageData $data): self
|
||||
{
|
||||
$this->imageData = $data;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get image data of current frame block.
|
||||
*/
|
||||
public function imageData(): ImageData
|
||||
{
|
||||
return $this->imageData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set plain text extension.
|
||||
*/
|
||||
public function setPlainTextExtension(PlainTextExtension $extension): self
|
||||
{
|
||||
$this->plainTextExtension = $extension;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get plain text extension.
|
||||
*/
|
||||
public function plainTextExtension(): ?PlainTextExtension
|
||||
{
|
||||
return $this->plainTextExtension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add given application extension to the current frame block.
|
||||
*/
|
||||
public function addApplicationExtension(ApplicationExtension $extension): self
|
||||
{
|
||||
$this->applicationExtensions[] = $extension;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all application extensions from the current frame block.
|
||||
*/
|
||||
public function clearApplicationExtensions(): self
|
||||
{
|
||||
$this->applicationExtensions = [];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add given comment extension to the current frame block
|
||||
*/
|
||||
public function addCommentExtension(CommentExtension $extension): self
|
||||
{
|
||||
$this->commentExtensions[] = $extension;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return netscape extension of the frame block if available.
|
||||
*/
|
||||
public function netscapeExtension(): ?NetscapeApplicationExtension
|
||||
{
|
||||
$extensions = array_filter(
|
||||
$this->applicationExtensions,
|
||||
fn(ApplicationExtension $extension): bool => $extension instanceof NetscapeApplicationExtension,
|
||||
);
|
||||
|
||||
return count($extensions) > 0 ? reset($extensions) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the table based image of the current frame block.
|
||||
*/
|
||||
public function setTableBasedImage(TableBasedImage $tableBasedImage): self
|
||||
{
|
||||
$this->setImageDescriptor($tableBasedImage->imageDescriptor());
|
||||
|
||||
$colorTable = $tableBasedImage->colorTable();
|
||||
if ($colorTable !== null) {
|
||||
$this->setColorTable($colorTable);
|
||||
}
|
||||
|
||||
$this->setImageData($tableBasedImage->imageData());
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
129
vendor/intervention/gif/src/Blocks/GraphicControlExtension.php
vendored
Normal file
129
vendor/intervention/gif/src/Blocks/GraphicControlExtension.php
vendored
Normal file
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Blocks;
|
||||
|
||||
use Intervention\Gif\AbstractExtension;
|
||||
use Intervention\Gif\DisposalMethod;
|
||||
|
||||
class GraphicControlExtension extends AbstractExtension
|
||||
{
|
||||
public const LABEL = "\xF9";
|
||||
public const BLOCKSIZE = "\x04";
|
||||
|
||||
/**
|
||||
* Existance flag of transparent color.
|
||||
*/
|
||||
protected bool $transparentColorExistance = false;
|
||||
|
||||
/**
|
||||
* Transparent color index.
|
||||
*/
|
||||
protected int $transparentColorIndex = 0;
|
||||
|
||||
/**
|
||||
* User input flag.
|
||||
*/
|
||||
protected bool $userInput = false;
|
||||
|
||||
/**
|
||||
* Create new instance.
|
||||
*/
|
||||
public function __construct(
|
||||
protected int $delay = 0,
|
||||
protected DisposalMethod $disposalMethod = DisposalMethod::UNDEFINED,
|
||||
) {
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Set delay time (1/100 second).
|
||||
*/
|
||||
public function setDelay(int $value): self
|
||||
{
|
||||
$this->delay = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return delay time (1/100 second).
|
||||
*/
|
||||
public function delay(): int
|
||||
{
|
||||
return $this->delay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set disposal method.
|
||||
*/
|
||||
public function setDisposalMethod(DisposalMethod $method): self
|
||||
{
|
||||
$this->disposalMethod = $method;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get disposal method.
|
||||
*/
|
||||
public function disposalMethod(): DisposalMethod
|
||||
{
|
||||
return $this->disposalMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get transparent color index.
|
||||
*/
|
||||
public function transparentColorIndex(): int
|
||||
{
|
||||
return $this->transparentColorIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set transparent color index.
|
||||
*/
|
||||
public function setTransparentColorIndex(int $index): self
|
||||
{
|
||||
$this->transparentColorIndex = $index;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current transparent color existance.
|
||||
*/
|
||||
public function transparentColorExistance(): bool
|
||||
{
|
||||
return $this->transparentColorExistance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set existance flag of transparent color.
|
||||
*/
|
||||
public function setTransparentColorExistance(bool $existance = true): self
|
||||
{
|
||||
$this->transparentColorExistance = $existance;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user input flag.
|
||||
*/
|
||||
public function userInput(): bool
|
||||
{
|
||||
return $this->userInput;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set user input flag.
|
||||
*/
|
||||
public function setUserInput(bool $value = true): self
|
||||
{
|
||||
$this->userInput = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
38
vendor/intervention/gif/src/Blocks/Header.php
vendored
Normal file
38
vendor/intervention/gif/src/Blocks/Header.php
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Blocks;
|
||||
|
||||
use Intervention\Gif\AbstractEntity;
|
||||
|
||||
class Header extends AbstractEntity
|
||||
{
|
||||
/**
|
||||
* Header signature.
|
||||
*/
|
||||
public const SIGNATURE = 'GIF';
|
||||
|
||||
/**
|
||||
* Current GIF version.
|
||||
*/
|
||||
protected string $version = '89a';
|
||||
|
||||
/**
|
||||
* Set GIF version.
|
||||
*/
|
||||
public function setVersion(string $value): self
|
||||
{
|
||||
$this->version = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return current version.
|
||||
*/
|
||||
public function version(): string
|
||||
{
|
||||
return $this->version;
|
||||
}
|
||||
}
|
||||
68
vendor/intervention/gif/src/Blocks/ImageData.php
vendored
Normal file
68
vendor/intervention/gif/src/Blocks/ImageData.php
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Blocks;
|
||||
|
||||
use Intervention\Gif\AbstractEntity;
|
||||
|
||||
class ImageData extends AbstractEntity
|
||||
{
|
||||
/**
|
||||
* LZW min. code size.
|
||||
*/
|
||||
protected int $lzwMinCodeSize;
|
||||
|
||||
/**
|
||||
* Sub blocks.
|
||||
*
|
||||
* @var array<DataSubBlock>
|
||||
*/
|
||||
protected array $blocks = [];
|
||||
|
||||
/**
|
||||
* Get LZW min. code size.
|
||||
*/
|
||||
public function lzwMinCodeSize(): int
|
||||
{
|
||||
return $this->lzwMinCodeSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set lzw min. code size.
|
||||
*/
|
||||
public function setLzwMinCodeSize(int $size): self
|
||||
{
|
||||
$this->lzwMinCodeSize = $size;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current data sub blocks.
|
||||
*
|
||||
* @return array<DataSubBlock>
|
||||
*/
|
||||
public function blocks(): array
|
||||
{
|
||||
return $this->blocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Addd sub block.
|
||||
*/
|
||||
public function addBlock(DataSubBlock $block): self
|
||||
{
|
||||
$this->blocks[] = $block;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if data sub blocks are present.
|
||||
*/
|
||||
public function hasBlocks(): bool
|
||||
{
|
||||
return count($this->blocks) >= 1;
|
||||
}
|
||||
}
|
||||
205
vendor/intervention/gif/src/Blocks/ImageDescriptor.php
vendored
Normal file
205
vendor/intervention/gif/src/Blocks/ImageDescriptor.php
vendored
Normal file
@@ -0,0 +1,205 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Blocks;
|
||||
|
||||
use Intervention\Gif\AbstractEntity;
|
||||
use Intervention\Gif\Exceptions\InvalidArgumentException;
|
||||
|
||||
class ImageDescriptor extends AbstractEntity
|
||||
{
|
||||
public const SEPARATOR = "\x2C";
|
||||
|
||||
/**
|
||||
* Width of frame.
|
||||
*/
|
||||
protected int $width = 0;
|
||||
|
||||
/**
|
||||
* Height of frame.
|
||||
*/
|
||||
protected int $height = 0;
|
||||
|
||||
/**
|
||||
* Left position of frame.
|
||||
*/
|
||||
protected int $left = 0;
|
||||
|
||||
/**
|
||||
* Top position of frame.
|
||||
*/
|
||||
protected int $top = 0;
|
||||
|
||||
/**
|
||||
* Determine if frame is interlaced.
|
||||
*/
|
||||
protected bool $interlaced = false;
|
||||
|
||||
/**
|
||||
* Local color table flag.
|
||||
*/
|
||||
protected bool $localColorTableExistance = false;
|
||||
|
||||
/**
|
||||
* Sort flag of local color table.
|
||||
*/
|
||||
protected bool $localColorTableSorted = false;
|
||||
|
||||
/**
|
||||
* Size of local color table.
|
||||
*/
|
||||
protected int $localColorTableSize = 0;
|
||||
|
||||
/**
|
||||
* Get current width.
|
||||
*/
|
||||
public function width(): int
|
||||
{
|
||||
return $this->width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current width.
|
||||
*/
|
||||
public function height(): int
|
||||
{
|
||||
return $this->height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current Top.
|
||||
*/
|
||||
public function top(): int
|
||||
{
|
||||
return $this->top;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current Left.
|
||||
*/
|
||||
public function left(): int
|
||||
{
|
||||
return $this->left;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set size of current instance.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function setSize(int $width, int $height): self
|
||||
{
|
||||
if ($width <= 0) {
|
||||
throw new InvalidArgumentException('Width in ' . $this::class . ' must be larger than 0');
|
||||
}
|
||||
|
||||
if ($height <= 0) {
|
||||
throw new InvalidArgumentException('Height in ' . $this::class . ' must be larger than 0');
|
||||
}
|
||||
|
||||
$this->width = $width;
|
||||
$this->height = $height;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set position of current instance.
|
||||
*/
|
||||
public function setPosition(int $left, int $top): self
|
||||
{
|
||||
$this->left = $left;
|
||||
$this->top = $top;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if frame is interlaced.
|
||||
*/
|
||||
public function isInterlaced(): bool
|
||||
{
|
||||
return $this->interlaced;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set or unset interlaced value.
|
||||
*/
|
||||
public function setInterlaced(bool $value = true): self
|
||||
{
|
||||
$this->interlaced = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if local color table is present.
|
||||
*/
|
||||
public function localColorTableExistance(): bool
|
||||
{
|
||||
return $this->localColorTableExistance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for localColorTableExistance.
|
||||
*/
|
||||
public function hasLocalColorTable(): bool
|
||||
{
|
||||
return $this->localColorTableExistance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set local color table flag.
|
||||
*/
|
||||
public function setLocalColorTableExistance(bool $existance = true): self
|
||||
{
|
||||
$this->localColorTableExistance = $existance;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get local color table sorted flag.
|
||||
*/
|
||||
public function localColorTableSorted(): bool
|
||||
{
|
||||
return $this->localColorTableSorted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set local color table sorted flag.
|
||||
*/
|
||||
public function setLocalColorTableSorted(bool $sorted = true): self
|
||||
{
|
||||
$this->localColorTableSorted = $sorted;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get size of local color table.
|
||||
*/
|
||||
public function localColorTableSize(): int
|
||||
{
|
||||
return $this->localColorTableSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get byte size of global color table.
|
||||
*/
|
||||
public function localColorTableByteSize(): int
|
||||
{
|
||||
return 3 * pow(2, $this->localColorTableSize() + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set size of local color table.
|
||||
*/
|
||||
public function setLocalColorTableSize(int $size): self
|
||||
{
|
||||
$this->localColorTableSize = $size;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
212
vendor/intervention/gif/src/Blocks/LogicalScreenDescriptor.php
vendored
Normal file
212
vendor/intervention/gif/src/Blocks/LogicalScreenDescriptor.php
vendored
Normal file
@@ -0,0 +1,212 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Blocks;
|
||||
|
||||
use Intervention\Gif\AbstractEntity;
|
||||
use Intervention\Gif\Exceptions\InvalidArgumentException;
|
||||
|
||||
class LogicalScreenDescriptor extends AbstractEntity
|
||||
{
|
||||
/**
|
||||
* Width.
|
||||
*/
|
||||
protected int $width;
|
||||
|
||||
/**
|
||||
* Height.
|
||||
*/
|
||||
protected int $height;
|
||||
|
||||
/**
|
||||
* Global color table flag.
|
||||
*/
|
||||
protected bool $globalColorTableExistance = false;
|
||||
|
||||
/**
|
||||
* Sort flag of global color table.
|
||||
*/
|
||||
protected bool $globalColorTableSorted = false;
|
||||
|
||||
/**
|
||||
* Size of global color table.
|
||||
*/
|
||||
protected int $globalColorTableSize = 0;
|
||||
|
||||
/**
|
||||
* Background color index.
|
||||
*/
|
||||
protected int $backgroundColorIndex = 0;
|
||||
|
||||
/**
|
||||
* Color resolution.
|
||||
*/
|
||||
protected int $bitsPerPixel = 8;
|
||||
|
||||
/**
|
||||
* Pixel aspect ration.
|
||||
*/
|
||||
protected int $pixelAspectRatio = 0;
|
||||
|
||||
/**
|
||||
* Set size.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function setSize(int $width, int $height): self
|
||||
{
|
||||
if ($width <= 0) {
|
||||
throw new InvalidArgumentException('Width in ' . $this::class . ' must be larger than 0');
|
||||
}
|
||||
|
||||
if ($height <= 0) {
|
||||
throw new InvalidArgumentException('Height in ' . $this::class . ' must be larger than 0');
|
||||
}
|
||||
|
||||
$this->width = $width;
|
||||
$this->height = $height;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get width of current instance.
|
||||
*/
|
||||
public function width(): int
|
||||
{
|
||||
return $this->width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get height of current instance.
|
||||
*/
|
||||
public function height(): int
|
||||
{
|
||||
return $this->height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if global color table is present.
|
||||
*/
|
||||
public function globalColorTableExistance(): bool
|
||||
{
|
||||
return $this->globalColorTableExistance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias of globalColorTableExistance.
|
||||
*/
|
||||
public function hasGlobalColorTable(): bool
|
||||
{
|
||||
return $this->globalColorTableExistance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set global color table flag.
|
||||
*/
|
||||
public function setGlobalColorTableExistance(bool $existance = true): self
|
||||
{
|
||||
$this->globalColorTableExistance = $existance;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get global color table sorted flag.
|
||||
*/
|
||||
public function globalColorTableSorted(): bool
|
||||
{
|
||||
return $this->globalColorTableSorted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set global color table sorted flag.
|
||||
*/
|
||||
public function setGlobalColorTableSorted(bool $sorted = true): self
|
||||
{
|
||||
$this->globalColorTableSorted = $sorted;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get size of global color table.
|
||||
*/
|
||||
public function globalColorTableSize(): int
|
||||
{
|
||||
return $this->globalColorTableSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get byte size of global color table.
|
||||
*/
|
||||
public function globalColorTableByteSize(): int
|
||||
{
|
||||
return 3 * pow(2, $this->globalColorTableSize() + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set size of global color table.
|
||||
*/
|
||||
public function setGlobalColorTableSize(int $size): self
|
||||
{
|
||||
$this->globalColorTableSize = $size;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get background color index.
|
||||
*/
|
||||
public function backgroundColorIndex(): int
|
||||
{
|
||||
return $this->backgroundColorIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set background color index.
|
||||
*/
|
||||
public function setBackgroundColorIndex(int $index): self
|
||||
{
|
||||
$this->backgroundColorIndex = $index;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current pixel aspect ration.
|
||||
*/
|
||||
public function pixelAspectRatio(): int
|
||||
{
|
||||
return $this->pixelAspectRatio;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set pixel aspect ratio.
|
||||
*/
|
||||
public function setPixelAspectRatio(int $ratio): self
|
||||
{
|
||||
$this->pixelAspectRatio = $ratio;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get color resolution.
|
||||
*/
|
||||
public function bitsPerPixel(): int
|
||||
{
|
||||
return $this->bitsPerPixel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set color resolution.
|
||||
*/
|
||||
public function setBitsPerPixel(int $value): self
|
||||
{
|
||||
$this->bitsPerPixel = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
66
vendor/intervention/gif/src/Blocks/NetscapeApplicationExtension.php
vendored
Normal file
66
vendor/intervention/gif/src/Blocks/NetscapeApplicationExtension.php
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Blocks;
|
||||
|
||||
use Intervention\Gif\Exceptions\DecoderException;
|
||||
use Intervention\Gif\Exceptions\InvalidArgumentException;
|
||||
use Intervention\Gif\Exceptions\StateException;
|
||||
|
||||
class NetscapeApplicationExtension extends ApplicationExtension
|
||||
{
|
||||
public const IDENTIFIER = "NETSCAPE";
|
||||
public const AUTH_CODE = "2.0";
|
||||
public const SUB_BLOCK_PREFIX = "\x01";
|
||||
|
||||
/**
|
||||
* Create new instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
try {
|
||||
$this->setApplication(self::IDENTIFIER . self::AUTH_CODE);
|
||||
$this->setBlocks([new DataSubBlock(self::SUB_BLOCK_PREFIX . "\x00\x00")]);
|
||||
} catch (InvalidArgumentException) {
|
||||
// ignore exception because of hard coded input
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get number of loops.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
public function loops(): int
|
||||
{
|
||||
try {
|
||||
$unpacked = unpack('v*', substr($this->firstBlock()->value(), 1));
|
||||
} catch (StateException $e) {
|
||||
throw new DecoderException(
|
||||
'Failed to decode loop count of netscape extension',
|
||||
previous: $e
|
||||
);
|
||||
}
|
||||
|
||||
if ($unpacked === false || !array_key_exists(1, $unpacked)) {
|
||||
throw new DecoderException('Failed to calculate loop count');
|
||||
}
|
||||
|
||||
return $unpacked[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set number of loops.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function setLoops(int $loops): self
|
||||
{
|
||||
$this->setBlocks([
|
||||
new DataSubBlock(self::SUB_BLOCK_PREFIX . pack('v*', $loops))
|
||||
]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
59
vendor/intervention/gif/src/Blocks/PlainTextExtension.php
vendored
Normal file
59
vendor/intervention/gif/src/Blocks/PlainTextExtension.php
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Blocks;
|
||||
|
||||
use Intervention\Gif\AbstractExtension;
|
||||
|
||||
class PlainTextExtension extends AbstractExtension
|
||||
{
|
||||
public const LABEL = "\x01";
|
||||
|
||||
/**
|
||||
* Array of text.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected array $text = [];
|
||||
|
||||
/**
|
||||
* Get current text.
|
||||
*
|
||||
* @return array<string>
|
||||
*/
|
||||
public function text(): array
|
||||
{
|
||||
return $this->text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add text.
|
||||
*/
|
||||
public function addText(string $text): self
|
||||
{
|
||||
$this->text[] = $text;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text array of extension.
|
||||
*
|
||||
* @param array<string> $text
|
||||
*/
|
||||
public function setText(array $text): self
|
||||
{
|
||||
$this->text = $text;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if any text is present.
|
||||
*/
|
||||
public function hasText(): bool
|
||||
{
|
||||
return $this->text !== [];
|
||||
}
|
||||
}
|
||||
68
vendor/intervention/gif/src/Blocks/TableBasedImage.php
vendored
Normal file
68
vendor/intervention/gif/src/Blocks/TableBasedImage.php
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Blocks;
|
||||
|
||||
use Intervention\Gif\AbstractEntity;
|
||||
|
||||
class TableBasedImage extends AbstractEntity
|
||||
{
|
||||
protected ImageDescriptor $imageDescriptor;
|
||||
protected ?ColorTable $colorTable = null;
|
||||
protected ImageData $imageData;
|
||||
|
||||
/**
|
||||
* Get image descriptor.
|
||||
*/
|
||||
public function imageDescriptor(): ImageDescriptor
|
||||
{
|
||||
return $this->imageDescriptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set image descriptor for current instance.
|
||||
*/
|
||||
public function setImageDescriptor(ImageDescriptor $descriptor): self
|
||||
{
|
||||
$this->imageDescriptor = $descriptor;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get image data.
|
||||
*/
|
||||
public function imageData(): ImageData
|
||||
{
|
||||
return $this->imageData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set image data for current instance.
|
||||
*/
|
||||
public function setImageData(ImageData $data): self
|
||||
{
|
||||
$this->imageData = $data;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current color table or null of table based based image has none.
|
||||
*/
|
||||
public function colorTable(): ?ColorTable
|
||||
{
|
||||
return $this->colorTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set color table.
|
||||
*/
|
||||
public function setColorTable(ColorTable $table): self
|
||||
{
|
||||
$this->colorTable = $table;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
12
vendor/intervention/gif/src/Blocks/Trailer.php
vendored
Normal file
12
vendor/intervention/gif/src/Blocks/Trailer.php
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Blocks;
|
||||
|
||||
use Intervention\Gif\AbstractEntity;
|
||||
|
||||
class Trailer extends AbstractEntity
|
||||
{
|
||||
public const MARKER = "\x3b";
|
||||
}
|
||||
233
vendor/intervention/gif/src/Builder.php
vendored
Normal file
233
vendor/intervention/gif/src/Builder.php
vendored
Normal file
@@ -0,0 +1,233 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif;
|
||||
|
||||
use Intervention\Gif\Blocks\FrameBlock;
|
||||
use Intervention\Gif\Blocks\GraphicControlExtension;
|
||||
use Intervention\Gif\Blocks\ImageDescriptor;
|
||||
use Intervention\Gif\Blocks\NetscapeApplicationExtension;
|
||||
use Intervention\Gif\Blocks\TableBasedImage;
|
||||
use Intervention\Gif\Exceptions\DecoderException;
|
||||
use Intervention\Gif\Exceptions\EncoderException;
|
||||
use Intervention\Gif\Exceptions\StreamException;
|
||||
use Intervention\Gif\Exceptions\InvalidArgumentException;
|
||||
use Intervention\Gif\Exceptions\StateException;
|
||||
use Intervention\Gif\Traits\CanHandleFiles;
|
||||
|
||||
class Builder
|
||||
{
|
||||
use CanHandleFiles;
|
||||
|
||||
/**
|
||||
* Create new instance.
|
||||
*/
|
||||
public function __construct(protected GifDataStream $gif = new GifDataStream())
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new canvas.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public static function canvas(int $width, int $height): self
|
||||
{
|
||||
return (new self())->setSize($width, $height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get GifDataStream object we're currently building.
|
||||
*/
|
||||
public function gifDataStream(): GifDataStream
|
||||
{
|
||||
return $this->gif;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set canvas size of gif.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function setSize(int $width, int $height): self
|
||||
{
|
||||
$this->gif->logicalScreenDescriptor()->setSize($width, $height);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set loop count.
|
||||
*
|
||||
* @throws StateException
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function setLoops(int $loops): self
|
||||
{
|
||||
if ($loops < 0) {
|
||||
throw new InvalidArgumentException('The loop count must be equal to or greater than 0');
|
||||
}
|
||||
|
||||
if ($this->gif->frames() === []) {
|
||||
throw new StateException('Add at least one frame before setting the loop count');
|
||||
}
|
||||
|
||||
// with one single loop the netscape extension must be removed otherwise the
|
||||
// gif is looped twice because the first repetition always takes place
|
||||
if ($loops === 1) {
|
||||
$this->gif->firstFrame()?->clearApplicationExtensions();
|
||||
return $this;
|
||||
}
|
||||
|
||||
// make sure a netscape extension is present to store the loop count
|
||||
if ($this->gif->firstFrame()?->netscapeExtension() === null) {
|
||||
$this->gif->firstFrame()?->addApplicationExtension(
|
||||
new NetscapeApplicationExtension()
|
||||
);
|
||||
}
|
||||
|
||||
// the loop count is reduced by one because what is referred to here as
|
||||
// the “loop count” actually means repetitions in GIF format, and thus
|
||||
// the first repetition always takes place. A loop count of 0 howerver
|
||||
// means infinite repetitions and remains unaltered.
|
||||
$loops = $loops === 0 ? $loops : $loops - 1;
|
||||
|
||||
// add loop count to netscape extension on first frame
|
||||
$this->gif->firstFrame()?->netscapeExtension()?->setLoops($loops);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new animation frame from given source which can be path to a file or GIF image data.
|
||||
*
|
||||
* @throws DecoderException
|
||||
* @throws StreamException
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function addFrame(
|
||||
mixed $source,
|
||||
float $delay = 0,
|
||||
int $left = 0,
|
||||
int $top = 0,
|
||||
bool $interlaced = false
|
||||
): self {
|
||||
$frame = new FrameBlock();
|
||||
$source = Decoder::decode($source);
|
||||
|
||||
// store delay
|
||||
$frame->setGraphicControlExtension(
|
||||
$this->buildGraphicControlExtension(
|
||||
$source,
|
||||
intval($delay * 100)
|
||||
)
|
||||
);
|
||||
|
||||
// store image
|
||||
$frame->setTableBasedImage(
|
||||
$this->buildTableBasedImage($source, $left, $top, $interlaced)
|
||||
);
|
||||
|
||||
// add frame
|
||||
$this->gif->addFrame($frame);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build new graphic control extension with given delay & disposal method
|
||||
*/
|
||||
protected function buildGraphicControlExtension(
|
||||
GifDataStream $source,
|
||||
int $delay,
|
||||
DisposalMethod $disposalMethod = DisposalMethod::BACKGROUND
|
||||
): GraphicControlExtension {
|
||||
// create extension
|
||||
$extension = new GraphicControlExtension($delay, $disposalMethod);
|
||||
|
||||
// set transparency index
|
||||
$control = $source->firstFrame()?->graphicControlExtension();
|
||||
if ($control !== null && $control->transparentColorExistance()) {
|
||||
$extension->setTransparentColorExistance();
|
||||
$extension->setTransparentColorIndex(
|
||||
$control->transparentColorIndex()
|
||||
);
|
||||
}
|
||||
|
||||
return $extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build table based image object from given source.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
protected function buildTableBasedImage(
|
||||
GifDataStream $source,
|
||||
int $left,
|
||||
int $top,
|
||||
bool $interlaced
|
||||
): TableBasedImage {
|
||||
$block = new TableBasedImage();
|
||||
$block->setImageDescriptor(new ImageDescriptor());
|
||||
|
||||
// set global color table from source as local color table
|
||||
$block->imageDescriptor()->setLocalColorTableExistance();
|
||||
$globalColorTable = $source->globalColorTable();
|
||||
|
||||
if ($globalColorTable === null) {
|
||||
throw new DecoderException(
|
||||
'Failed to build table based image. Unable to find global color table in gif data stream',
|
||||
);
|
||||
}
|
||||
|
||||
$block->setColorTable($globalColorTable);
|
||||
|
||||
$block->imageDescriptor()->setLocalColorTableSorted(
|
||||
$source->logicalScreenDescriptor()->globalColorTableSorted()
|
||||
);
|
||||
|
||||
try {
|
||||
$block->imageDescriptor()->setLocalColorTableSize(
|
||||
$source->logicalScreenDescriptor()->globalColorTableSize()
|
||||
);
|
||||
|
||||
$block->imageDescriptor()->setSize(
|
||||
$source->logicalScreenDescriptor()->width(),
|
||||
$source->logicalScreenDescriptor()->height()
|
||||
);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
throw new DecoderException(
|
||||
'Failed to decode image source',
|
||||
previous: $e
|
||||
);
|
||||
}
|
||||
|
||||
// set position
|
||||
$block->imageDescriptor()->setPosition($left, $top);
|
||||
|
||||
// set interlaced flag
|
||||
$block->imageDescriptor()->setInterlaced($interlaced);
|
||||
|
||||
// add image data from source
|
||||
$block->setImageData(
|
||||
$source->firstFrame()?->imageData() ?: throw new DecoderException(
|
||||
'Failed to build table based image. Unable to find image data',
|
||||
)
|
||||
);
|
||||
|
||||
return $block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode the current build.
|
||||
*
|
||||
* @throws EncoderException
|
||||
*/
|
||||
public function encode(): string
|
||||
{
|
||||
return $this->gif->encode();
|
||||
}
|
||||
}
|
||||
50
vendor/intervention/gif/src/Decoder.php
vendored
Normal file
50
vendor/intervention/gif/src/Decoder.php
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif;
|
||||
|
||||
use Intervention\Gif\Exceptions\DecoderException;
|
||||
use Intervention\Gif\Exceptions\StreamException;
|
||||
use Intervention\Gif\Exceptions\InvalidArgumentException;
|
||||
use Intervention\Gif\Traits\CanHandleFiles;
|
||||
|
||||
class Decoder
|
||||
{
|
||||
use CanHandleFiles;
|
||||
|
||||
/**
|
||||
* Decode given input.
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @throws StreamException
|
||||
* @throws DecoderException
|
||||
*/
|
||||
public static function decode(mixed $input): GifDataStream
|
||||
{
|
||||
$stream = match (true) {
|
||||
self::isFilePath($input) => self::streamFromFilePath($input),
|
||||
is_string($input) => self::streamFromData($input),
|
||||
self::isStream($input) => $input,
|
||||
default => throw new InvalidArgumentException(
|
||||
'Decoder input must be either file path, stream resource or binary data'
|
||||
)
|
||||
};
|
||||
|
||||
$result = rewind($stream);
|
||||
|
||||
if ($result === false) {
|
||||
throw new StreamException('Failed to rewind stream');
|
||||
}
|
||||
|
||||
return GifDataStream::decode($stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if input is stream resource.
|
||||
*/
|
||||
private static function isStream(mixed $input): bool
|
||||
{
|
||||
return is_resource($input) && get_resource_type($input) === 'stream';
|
||||
}
|
||||
}
|
||||
168
vendor/intervention/gif/src/Decoders/AbstractDecoder.php
vendored
Normal file
168
vendor/intervention/gif/src/Decoders/AbstractDecoder.php
vendored
Normal file
@@ -0,0 +1,168 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Decoders;
|
||||
|
||||
use Intervention\Gif\AbstractEntity;
|
||||
use Intervention\Gif\Exceptions\DecoderException;
|
||||
|
||||
abstract class AbstractDecoder
|
||||
{
|
||||
/**
|
||||
* Decode current source.
|
||||
*/
|
||||
abstract public function decode(): AbstractEntity;
|
||||
|
||||
/**
|
||||
* Create new instance.
|
||||
*/
|
||||
public function __construct(protected mixed $stream, protected ?int $length = null)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Set source to decode.
|
||||
*/
|
||||
public function setStream(mixed $stream): self
|
||||
{
|
||||
$this->stream = $stream;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read given number of bytes and move stream position.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
protected function nextBytesOrFail(int $length): string
|
||||
{
|
||||
if ($length < 1) {
|
||||
throw new DecoderException('The length of the next byte chain must be at least one byte');
|
||||
}
|
||||
|
||||
$bytes = fread($this->stream, $length);
|
||||
if ($bytes === false || strlen($bytes) !== $length) {
|
||||
throw new DecoderException('Unexpected end of file');
|
||||
}
|
||||
|
||||
return $bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read given number of bytes and move stream position back to previous position.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
protected function viewNextBytesOrFail(int $length): string
|
||||
{
|
||||
$bytes = $this->nextBytesOrFail($length);
|
||||
$this->moveStreamPosition($length * -1);
|
||||
|
||||
return $bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read next byte and move stream position back to previous position.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
protected function viewNextByteOrFail(): string
|
||||
{
|
||||
return $this->viewNextBytesOrFail(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read all remaining bytes from stream.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
protected function remainingBytes(): string
|
||||
{
|
||||
$contents = stream_get_contents($this->stream);
|
||||
|
||||
if ($contents === false) {
|
||||
throw new DecoderException('Failed to read remaining bytes from stream');
|
||||
}
|
||||
|
||||
return $contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get next byte in stream and move stream position.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
protected function nextByteOrFail(): string
|
||||
{
|
||||
return $this->nextBytesOrFail(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move stream position by given offset.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
protected function moveStreamPosition(int $offset): self
|
||||
{
|
||||
$result = fseek($this->stream, $offset, SEEK_CUR);
|
||||
|
||||
if ($result !== 0) {
|
||||
throw new DecoderException('Failed to move stream position by offset ' . $offset);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode multi byte value.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
protected function decodeMultiByte(string $bytes): int
|
||||
{
|
||||
$unpacked = unpack('v*', $bytes);
|
||||
|
||||
if ($unpacked === false || !array_key_exists(1, $unpacked)) {
|
||||
throw new DecoderException('Failed to decode given bytes');
|
||||
}
|
||||
|
||||
return $unpacked[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set length.
|
||||
*/
|
||||
public function setLength(int $length): self
|
||||
{
|
||||
$this->length = $length;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get length.
|
||||
*/
|
||||
public function length(): ?int
|
||||
{
|
||||
return $this->length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current stream position.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
public function position(): int
|
||||
{
|
||||
$position = ftell($this->stream);
|
||||
|
||||
if ($position === false) {
|
||||
throw new DecoderException('Failed to read current position from stream');
|
||||
}
|
||||
|
||||
return $position;
|
||||
}
|
||||
}
|
||||
48
vendor/intervention/gif/src/Decoders/AbstractPackedBitDecoder.php
vendored
Normal file
48
vendor/intervention/gif/src/Decoders/AbstractPackedBitDecoder.php
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Decoders;
|
||||
|
||||
use Intervention\Gif\Exceptions\DecoderException;
|
||||
|
||||
abstract class AbstractPackedBitDecoder extends AbstractDecoder
|
||||
{
|
||||
/**
|
||||
* Decode packed byte.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
protected function decodePackedByte(string $byte): int
|
||||
{
|
||||
$unpacked = unpack('C', $byte);
|
||||
|
||||
if ($unpacked === false || !array_key_exists(1, $unpacked)) {
|
||||
throw new DecoderException('Failed to decode packed info block size');
|
||||
}
|
||||
|
||||
return intval($unpacked[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if packed bit is set.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
protected function hasPackedBit(string $byte, int $num): bool
|
||||
{
|
||||
return (bool) $this->packedBits($byte)[$num];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get packed bits.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
protected function packedBits(string $byte, int $start = 0, int $length = 8): string
|
||||
{
|
||||
$bits = str_pad(decbin($this->decodePackedByte($byte)), 8, '0', STR_PAD_LEFT);
|
||||
|
||||
return substr($bits, $start, $length);
|
||||
}
|
||||
}
|
||||
87
vendor/intervention/gif/src/Decoders/ApplicationExtensionDecoder.php
vendored
Normal file
87
vendor/intervention/gif/src/Decoders/ApplicationExtensionDecoder.php
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Decoders;
|
||||
|
||||
use Intervention\Gif\Blocks\ApplicationExtension;
|
||||
use Intervention\Gif\Blocks\DataSubBlock;
|
||||
use Intervention\Gif\Blocks\NetscapeApplicationExtension;
|
||||
use Intervention\Gif\Exceptions\DecoderException;
|
||||
use Intervention\Gif\Exceptions\InvalidArgumentException;
|
||||
|
||||
class ApplicationExtensionDecoder extends AbstractDecoder
|
||||
{
|
||||
/**
|
||||
* Decode current source.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
public function decode(): ApplicationExtension
|
||||
{
|
||||
$result = new ApplicationExtension();
|
||||
|
||||
$this->nextByteOrFail(); // marker
|
||||
$this->nextByteOrFail(); // label
|
||||
$blocksize = $this->decodeBlockSize($this->nextByteOrFail());
|
||||
$application = $this->nextBytesOrFail($blocksize);
|
||||
|
||||
if ($application === NetscapeApplicationExtension::IDENTIFIER . NetscapeApplicationExtension::AUTH_CODE) {
|
||||
$result = new NetscapeApplicationExtension();
|
||||
|
||||
// skip length
|
||||
$this->nextByteOrFail();
|
||||
|
||||
try {
|
||||
$result->setBlocks([
|
||||
new DataSubBlock($this->nextBytesOrFail(3))
|
||||
]);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
throw new DecoderException(
|
||||
'Failed to decode image data sub block of image data',
|
||||
previous: $e
|
||||
);
|
||||
}
|
||||
|
||||
// skip terminator
|
||||
$this->nextByteOrFail();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
$result->setApplication($application);
|
||||
|
||||
// decode data sub blocks
|
||||
$blocksize = $this->decodeBlockSize($this->nextByteOrFail());
|
||||
while ($blocksize > 0) {
|
||||
try {
|
||||
$result->addBlock(new DataSubBlock($this->nextBytesOrFail($blocksize)));
|
||||
} catch (InvalidArgumentException $e) {
|
||||
throw new DecoderException(
|
||||
'Failed to decode image data sub block of image data',
|
||||
previous: $e
|
||||
);
|
||||
}
|
||||
|
||||
$blocksize = $this->decodeBlockSize($this->nextByteOrFail());
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode block size of ApplicationExtension from given byte.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
protected function decodeBlockSize(string $byte): int
|
||||
{
|
||||
$unpacked = @unpack('C', $byte);
|
||||
|
||||
if ($unpacked === false || !array_key_exists(1, $unpacked)) {
|
||||
throw new DecoderException('Failed to decode block size of application extension');
|
||||
}
|
||||
|
||||
return intval($unpacked[1]);
|
||||
}
|
||||
}
|
||||
48
vendor/intervention/gif/src/Decoders/ColorDecoder.php
vendored
Normal file
48
vendor/intervention/gif/src/Decoders/ColorDecoder.php
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Decoders;
|
||||
|
||||
use Intervention\Gif\Blocks\Color;
|
||||
use Intervention\Gif\Exceptions\DecoderException;
|
||||
use Intervention\Gif\Exceptions\InvalidArgumentException;
|
||||
|
||||
class ColorDecoder extends AbstractDecoder
|
||||
{
|
||||
/**
|
||||
* Decode current source to Color.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
public function decode(): Color
|
||||
{
|
||||
try {
|
||||
return new Color(
|
||||
$this->decodeColorValue($this->nextByteOrFail()),
|
||||
$this->decodeColorValue($this->nextByteOrFail()),
|
||||
$this->decodeColorValue($this->nextByteOrFail()),
|
||||
);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
throw new DecoderException(
|
||||
'Failed to decode color channel values',
|
||||
previous: $e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode color value from source.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
protected function decodeColorValue(string $byte): int
|
||||
{
|
||||
$unpacked = unpack('C', $byte);
|
||||
if ($unpacked === false || !array_key_exists(1, $unpacked)) {
|
||||
throw new DecoderException('Failed to decode color value');
|
||||
}
|
||||
|
||||
return $unpacked[1];
|
||||
}
|
||||
}
|
||||
29
vendor/intervention/gif/src/Decoders/ColorTableDecoder.php
vendored
Normal file
29
vendor/intervention/gif/src/Decoders/ColorTableDecoder.php
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Decoders;
|
||||
|
||||
use Intervention\Gif\Blocks\Color;
|
||||
use Intervention\Gif\Blocks\ColorTable;
|
||||
use Intervention\Gif\Exceptions\DecoderException;
|
||||
|
||||
class ColorTableDecoder extends AbstractDecoder
|
||||
{
|
||||
/**
|
||||
* Decode given string to ColorTable.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
public function decode(): ColorTable
|
||||
{
|
||||
$table = new ColorTable();
|
||||
$length = $this->length() !== null ? $this->length() : 0;
|
||||
|
||||
for ($i = 0; $i < ($length / 3); $i++) {
|
||||
$table->addColor(Color::decode($this->stream));
|
||||
}
|
||||
|
||||
return $table;
|
||||
}
|
||||
}
|
||||
65
vendor/intervention/gif/src/Decoders/CommentExtensionDecoder.php
vendored
Normal file
65
vendor/intervention/gif/src/Decoders/CommentExtensionDecoder.php
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Decoders;
|
||||
|
||||
use Intervention\Gif\Blocks\CommentExtension;
|
||||
use Intervention\Gif\Exceptions\DecoderException;
|
||||
|
||||
class CommentExtensionDecoder extends AbstractDecoder
|
||||
{
|
||||
/**
|
||||
* Decode current source.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
public function decode(): CommentExtension
|
||||
{
|
||||
$this->nextBytesOrFail(2); // skip marker & label
|
||||
|
||||
$extension = new CommentExtension();
|
||||
foreach ($this->decodeComments() as $comment) {
|
||||
$extension->addComment($comment);
|
||||
}
|
||||
|
||||
return $extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode comment from current source.
|
||||
*
|
||||
* @throws DecoderException
|
||||
* @return array<string>
|
||||
*/
|
||||
protected function decodeComments(): array
|
||||
{
|
||||
$comments = [];
|
||||
|
||||
do {
|
||||
$byte = $this->nextByteOrFail();
|
||||
$size = $this->decodeBlocksize($byte);
|
||||
if ($size > 0) {
|
||||
$comments[] = $this->nextBytesOrFail($size);
|
||||
}
|
||||
} while ($byte !== CommentExtension::TERMINATOR);
|
||||
|
||||
return $comments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode blocksize of following comment.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
protected function decodeBlocksize(string $byte): int
|
||||
{
|
||||
$unpacked = @unpack('C', $byte);
|
||||
|
||||
if ($unpacked === false || !array_key_exists(1, $unpacked)) {
|
||||
throw new DecoderException('Failed to decode block size of comment extension');
|
||||
}
|
||||
|
||||
return intval($unpacked[1]);
|
||||
}
|
||||
}
|
||||
38
vendor/intervention/gif/src/Decoders/DataSubBlockDecoder.php
vendored
Normal file
38
vendor/intervention/gif/src/Decoders/DataSubBlockDecoder.php
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Decoders;
|
||||
|
||||
use Intervention\Gif\Blocks\DataSubBlock;
|
||||
use Intervention\Gif\Exceptions\DecoderException;
|
||||
use Intervention\Gif\Exceptions\InvalidArgumentException;
|
||||
|
||||
class DataSubBlockDecoder extends AbstractDecoder
|
||||
{
|
||||
/**
|
||||
* Decode current source.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
public function decode(): DataSubBlock
|
||||
{
|
||||
$char = $this->nextByteOrFail();
|
||||
$unpacked = unpack('C', $char);
|
||||
|
||||
if ($unpacked === false || !array_key_exists(1, $unpacked)) {
|
||||
throw new DecoderException('Failed to decode data sub block');
|
||||
}
|
||||
|
||||
$size = (int) $unpacked[1];
|
||||
|
||||
try {
|
||||
return new DataSubBlock($this->nextBytesOrFail($size));
|
||||
} catch (InvalidArgumentException $e) {
|
||||
throw new DecoderException(
|
||||
'Failed to decode image data sub block of image data',
|
||||
previous: $e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
52
vendor/intervention/gif/src/Decoders/FrameBlockDecoder.php
vendored
Normal file
52
vendor/intervention/gif/src/Decoders/FrameBlockDecoder.php
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Decoders;
|
||||
|
||||
use Intervention\Gif\AbstractExtension;
|
||||
use Intervention\Gif\Blocks\ApplicationExtension;
|
||||
use Intervention\Gif\Blocks\CommentExtension;
|
||||
use Intervention\Gif\Blocks\FrameBlock;
|
||||
use Intervention\Gif\Blocks\GraphicControlExtension;
|
||||
use Intervention\Gif\Blocks\ImageDescriptor;
|
||||
use Intervention\Gif\Blocks\NetscapeApplicationExtension;
|
||||
use Intervention\Gif\Blocks\PlainTextExtension;
|
||||
use Intervention\Gif\Blocks\TableBasedImage;
|
||||
use Intervention\Gif\Exceptions\DecoderException;
|
||||
|
||||
class FrameBlockDecoder extends AbstractDecoder
|
||||
{
|
||||
/**
|
||||
* Decode FrameBlock.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
public function decode(): FrameBlock
|
||||
{
|
||||
$frame = new FrameBlock();
|
||||
|
||||
do {
|
||||
$block = match ($this->viewNextBytesOrFail(2)) {
|
||||
AbstractExtension::MARKER . GraphicControlExtension::LABEL
|
||||
=> GraphicControlExtension::decode($this->stream),
|
||||
AbstractExtension::MARKER . NetscapeApplicationExtension::LABEL
|
||||
=> NetscapeApplicationExtension::decode($this->stream),
|
||||
AbstractExtension::MARKER . ApplicationExtension::LABEL
|
||||
=> ApplicationExtension::decode($this->stream),
|
||||
AbstractExtension::MARKER . PlainTextExtension::LABEL
|
||||
=> PlainTextExtension::decode($this->stream),
|
||||
AbstractExtension::MARKER . CommentExtension::LABEL
|
||||
=> CommentExtension::decode($this->stream),
|
||||
default => match ($this->viewNextByteOrFail()) {
|
||||
ImageDescriptor::SEPARATOR => TableBasedImage::decode($this->stream),
|
||||
default => throw new DecoderException('Failed to decode data block'),
|
||||
}
|
||||
};
|
||||
|
||||
$frame->addEntity($block);
|
||||
} while (!($block instanceof TableBasedImage));
|
||||
|
||||
return $frame;
|
||||
}
|
||||
}
|
||||
58
vendor/intervention/gif/src/Decoders/GifDataStreamDecoder.php
vendored
Normal file
58
vendor/intervention/gif/src/Decoders/GifDataStreamDecoder.php
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Decoders;
|
||||
|
||||
use Intervention\Gif\AbstractExtension;
|
||||
use Intervention\Gif\Blocks\ColorTable;
|
||||
use Intervention\Gif\Blocks\CommentExtension;
|
||||
use Intervention\Gif\Blocks\FrameBlock;
|
||||
use Intervention\Gif\Blocks\Header;
|
||||
use Intervention\Gif\Blocks\LogicalScreenDescriptor;
|
||||
use Intervention\Gif\Blocks\Trailer;
|
||||
use Intervention\Gif\Exceptions\DecoderException;
|
||||
use Intervention\Gif\GifDataStream;
|
||||
|
||||
class GifDataStreamDecoder extends AbstractDecoder
|
||||
{
|
||||
/**
|
||||
* Decode current source to GifDataStream.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
public function decode(): GifDataStream
|
||||
{
|
||||
$gif = new GifDataStream();
|
||||
|
||||
$gif->setHeader(
|
||||
Header::decode($this->stream),
|
||||
);
|
||||
|
||||
$gif->setLogicalScreenDescriptor(
|
||||
LogicalScreenDescriptor::decode($this->stream),
|
||||
);
|
||||
|
||||
if ($gif->logicalScreenDescriptor()->hasGlobalColorTable()) {
|
||||
$length = $gif->logicalScreenDescriptor()->globalColorTableByteSize();
|
||||
$gif->setGlobalColorTable(
|
||||
ColorTable::decode($this->stream, $length)
|
||||
);
|
||||
}
|
||||
|
||||
while ($this->viewNextByteOrFail() !== Trailer::MARKER) {
|
||||
match ($this->viewNextBytesOrFail(2)) {
|
||||
// handle trailing "global" comment blocks which are not part of "FrameBlock"
|
||||
AbstractExtension::MARKER . CommentExtension::LABEL
|
||||
=> $gif->addComment(
|
||||
CommentExtension::decode($this->stream)
|
||||
),
|
||||
default => $gif->addFrame(
|
||||
FrameBlock::decode($this->stream)
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
return $gif;
|
||||
}
|
||||
}
|
||||
117
vendor/intervention/gif/src/Decoders/GraphicControlExtensionDecoder.php
vendored
Normal file
117
vendor/intervention/gif/src/Decoders/GraphicControlExtensionDecoder.php
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Decoders;
|
||||
|
||||
use Intervention\Gif\Blocks\GraphicControlExtension;
|
||||
use Intervention\Gif\DisposalMethod;
|
||||
use Intervention\Gif\Exceptions\DecoderException;
|
||||
use TypeError;
|
||||
use ValueError;
|
||||
|
||||
class GraphicControlExtensionDecoder extends AbstractPackedBitDecoder
|
||||
{
|
||||
/**
|
||||
* Decode given string to current instance.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
public function decode(): GraphicControlExtension
|
||||
{
|
||||
$result = new GraphicControlExtension();
|
||||
|
||||
// bytes 1-3
|
||||
$this->nextBytesOrFail(3); // skip marker, label & bytesize
|
||||
|
||||
// byte #4
|
||||
$packedField = $this->nextByteOrFail();
|
||||
$result->setDisposalMethod($this->decodeDisposalMethod($packedField));
|
||||
$result->setUserInput($this->decodeUserInput($packedField));
|
||||
$result->setTransparentColorExistance($this->decodeTransparentColorExistance($packedField));
|
||||
|
||||
// bytes 5-6
|
||||
$result->setDelay($this->decodeDelay($this->nextBytesOrFail(2)));
|
||||
|
||||
// byte #7
|
||||
$result->setTransparentColorIndex($this->decodeTransparentColorIndex(
|
||||
$this->nextByteOrFail()
|
||||
));
|
||||
|
||||
// byte #8 (terminator)
|
||||
$this->nextByteOrFail();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode disposal method
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
protected function decodeDisposalMethod(string $byte): DisposalMethod
|
||||
{
|
||||
try {
|
||||
return DisposalMethod::from(
|
||||
intval(bindec($this->packedBits($byte, 3, 3)))
|
||||
);
|
||||
} catch (TypeError | ValueError $e) {
|
||||
throw new DecoderException(
|
||||
'Failed to decode disposal method in graphic control extension',
|
||||
previous: $e,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode user input flag.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
private function decodeUserInput(string $byte): bool
|
||||
{
|
||||
return $this->hasPackedBit($byte, 6);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode transparent color existance.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
private function decodeTransparentColorExistance(string $byte): bool
|
||||
{
|
||||
return $this->hasPackedBit($byte, 7);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode delay value.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
private function decodeDelay(string $bytes): int
|
||||
{
|
||||
$unpacked = unpack('v*', $bytes);
|
||||
|
||||
if ($unpacked === false || !array_key_exists(1, $unpacked)) {
|
||||
throw new DecoderException('Failed to decode animation delay in graphic control extension');
|
||||
}
|
||||
|
||||
return $unpacked[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode transparent color index.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
private function decodeTransparentColorIndex(string $byte): int
|
||||
{
|
||||
$unpacked = unpack('C', $byte);
|
||||
|
||||
if ($unpacked === false || !array_key_exists(1, $unpacked)) {
|
||||
throw new DecoderException('Failed to decode transparent color index in graphic control extension');
|
||||
}
|
||||
|
||||
return $unpacked[1];
|
||||
}
|
||||
}
|
||||
40
vendor/intervention/gif/src/Decoders/HeaderDecoder.php
vendored
Normal file
40
vendor/intervention/gif/src/Decoders/HeaderDecoder.php
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Decoders;
|
||||
|
||||
use Intervention\Gif\Blocks\Header;
|
||||
use Intervention\Gif\Exceptions\DecoderException;
|
||||
|
||||
class HeaderDecoder extends AbstractDecoder
|
||||
{
|
||||
/**
|
||||
* Decode current source.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
public function decode(): Header
|
||||
{
|
||||
$header = new Header();
|
||||
$header->setVersion($this->decodeVersion());
|
||||
|
||||
return $header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode version string.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
private function decodeVersion(): string
|
||||
{
|
||||
$parsed = (bool) preg_match("/^GIF(?P<version>[0-9]{2}[a-z])$/", $this->nextBytesOrFail(6), $matches);
|
||||
|
||||
if ($parsed === false) {
|
||||
throw new DecoderException('Failed to parse GIF file header');
|
||||
}
|
||||
|
||||
return $matches['version'];
|
||||
}
|
||||
}
|
||||
57
vendor/intervention/gif/src/Decoders/ImageDataDecoder.php
vendored
Normal file
57
vendor/intervention/gif/src/Decoders/ImageDataDecoder.php
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Decoders;
|
||||
|
||||
use Intervention\Gif\AbstractEntity;
|
||||
use Intervention\Gif\Blocks\DataSubBlock;
|
||||
use Intervention\Gif\Blocks\ImageData;
|
||||
use Intervention\Gif\Exceptions\DecoderException;
|
||||
use Intervention\Gif\Exceptions\InvalidArgumentException;
|
||||
|
||||
class ImageDataDecoder extends AbstractDecoder
|
||||
{
|
||||
/**
|
||||
* Decode current source.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
public function decode(): ImageData
|
||||
{
|
||||
$data = new ImageData();
|
||||
|
||||
// LZW min. code size
|
||||
$char = $this->nextByteOrFail();
|
||||
$unpacked = unpack('C', $char);
|
||||
if ($unpacked === false || !array_key_exists(1, $unpacked)) {
|
||||
throw new DecoderException('Failed to decode lzw min. code size of image data');
|
||||
}
|
||||
|
||||
$data->setLzwMinCodeSize(intval($unpacked[1]));
|
||||
|
||||
do {
|
||||
// decode sub blocks
|
||||
$char = $this->nextByteOrFail();
|
||||
$unpacked = unpack('C', $char);
|
||||
if ($unpacked === false || !array_key_exists(1, $unpacked)) {
|
||||
throw new DecoderException('Failed to decode image data sub block of image data');
|
||||
}
|
||||
|
||||
$size = intval($unpacked[1]);
|
||||
|
||||
if ($size > 0) {
|
||||
try {
|
||||
$data->addBlock(new DataSubBlock($this->nextBytesOrFail($size)));
|
||||
} catch (InvalidArgumentException $e) {
|
||||
throw new DecoderException(
|
||||
'Failed to decode image data sub block of image data',
|
||||
previous: $e
|
||||
);
|
||||
}
|
||||
}
|
||||
} while ($char !== AbstractEntity::TERMINATOR);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
98
vendor/intervention/gif/src/Decoders/ImageDescriptorDecoder.php
vendored
Normal file
98
vendor/intervention/gif/src/Decoders/ImageDescriptorDecoder.php
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Decoders;
|
||||
|
||||
use Intervention\Gif\Blocks\ImageDescriptor;
|
||||
use Intervention\Gif\Exceptions\DecoderException;
|
||||
use Intervention\Gif\Exceptions\InvalidArgumentException;
|
||||
|
||||
class ImageDescriptorDecoder extends AbstractPackedBitDecoder
|
||||
{
|
||||
/**
|
||||
* Decode given string to current instance.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
public function decode(): ImageDescriptor
|
||||
{
|
||||
$descriptor = new ImageDescriptor();
|
||||
|
||||
$this->nextByteOrFail(); // skip separator
|
||||
|
||||
$descriptor->setPosition(
|
||||
$this->decodeMultiByte($this->nextBytesOrFail(2)),
|
||||
$this->decodeMultiByte($this->nextBytesOrFail(2))
|
||||
);
|
||||
|
||||
try {
|
||||
$descriptor->setSize(
|
||||
$this->decodeMultiByte($this->nextBytesOrFail(2)),
|
||||
$this->decodeMultiByte($this->nextBytesOrFail(2))
|
||||
);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
throw new DecoderException('Failed to decode image size of image descriptor', previous: $e);
|
||||
}
|
||||
|
||||
$packedField = $this->nextByteOrFail();
|
||||
|
||||
$descriptor->setLocalColorTableExistance(
|
||||
$this->decodeLocalColorTableExistance($packedField)
|
||||
);
|
||||
|
||||
$descriptor->setLocalColorTableSorted(
|
||||
$this->decodeLocalColorTableSorted($packedField)
|
||||
);
|
||||
|
||||
$descriptor->setLocalColorTableSize(
|
||||
$this->decodeLocalColorTableSize($packedField)
|
||||
);
|
||||
|
||||
$descriptor->setInterlaced(
|
||||
$this->decodeInterlaced($packedField)
|
||||
);
|
||||
|
||||
return $descriptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode local color table existance.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
private function decodeLocalColorTableExistance(string $byte): bool
|
||||
{
|
||||
return $this->hasPackedBit($byte, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode local color table sort method.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
private function decodeLocalColorTableSorted(string $byte): bool
|
||||
{
|
||||
return $this->hasPackedBit($byte, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode local color table size.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
private function decodeLocalColorTableSize(string $byte): int
|
||||
{
|
||||
return (int) bindec($this->packedBits($byte, 5, 3));
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode interlaced flag.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
private function decodeInterlaced(string $byte): bool
|
||||
{
|
||||
return $this->hasPackedBit($byte, 1);
|
||||
}
|
||||
}
|
||||
167
vendor/intervention/gif/src/Decoders/LogicalScreenDescriptorDecoder.php
vendored
Normal file
167
vendor/intervention/gif/src/Decoders/LogicalScreenDescriptorDecoder.php
vendored
Normal file
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Decoders;
|
||||
|
||||
use Intervention\Gif\Blocks\LogicalScreenDescriptor;
|
||||
use Intervention\Gif\Exceptions\DecoderException;
|
||||
use Intervention\Gif\Exceptions\InvalidArgumentException;
|
||||
|
||||
class LogicalScreenDescriptorDecoder extends AbstractPackedBitDecoder
|
||||
{
|
||||
/**
|
||||
* Decode given string to current instance.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
public function decode(): LogicalScreenDescriptor
|
||||
{
|
||||
$logicalScreenDescriptor = new LogicalScreenDescriptor();
|
||||
|
||||
// bytes 1-4
|
||||
try {
|
||||
$logicalScreenDescriptor->setSize(
|
||||
$this->decodeWidth($this->nextBytesOrFail(2)),
|
||||
$this->decodeHeight($this->nextBytesOrFail(2))
|
||||
);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
throw new DecoderException('Failed to decode image size of logical screen descriptor', previous: $e);
|
||||
}
|
||||
|
||||
// byte 5
|
||||
$packedField = $this->nextByteOrFail();
|
||||
|
||||
$logicalScreenDescriptor->setGlobalColorTableExistance(
|
||||
$this->decodeGlobalColorTableExistance($packedField)
|
||||
);
|
||||
|
||||
$logicalScreenDescriptor->setBitsPerPixel(
|
||||
$this->decodeBitsPerPixel($packedField)
|
||||
);
|
||||
|
||||
$logicalScreenDescriptor->setGlobalColorTableSorted(
|
||||
$this->decodeGlobalColorTableSorted($packedField)
|
||||
);
|
||||
|
||||
$logicalScreenDescriptor->setGlobalColorTableSize(
|
||||
$this->decodeGlobalColorTableSize($packedField)
|
||||
);
|
||||
|
||||
// byte 6
|
||||
$logicalScreenDescriptor->setBackgroundColorIndex(
|
||||
$this->decodeBackgroundColorIndex($this->nextByteOrFail())
|
||||
);
|
||||
|
||||
// byte 7
|
||||
$logicalScreenDescriptor->setPixelAspectRatio(
|
||||
$this->decodePixelAspectRatio($this->nextByteOrFail())
|
||||
);
|
||||
|
||||
return $logicalScreenDescriptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode width.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
private function decodeWidth(string $source): int
|
||||
{
|
||||
$unpacked = unpack('v*', $source);
|
||||
|
||||
if ($unpacked === false || !array_key_exists(1, $unpacked)) {
|
||||
throw new DecoderException('Failed to decode width in logical screen descriptor');
|
||||
}
|
||||
|
||||
return $unpacked[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode height.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
private function decodeHeight(string $source): int
|
||||
{
|
||||
$unpacked = unpack('v*', $source);
|
||||
|
||||
if ($unpacked === false || !array_key_exists(1, $unpacked)) {
|
||||
throw new DecoderException('Failed to decode height in logical screen descriptor');
|
||||
}
|
||||
|
||||
return $unpacked[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode existance of global color table.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
private function decodeGlobalColorTableExistance(string $byte): bool
|
||||
{
|
||||
return $this->hasPackedBit($byte, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode color resolution in bits per pixel.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
private function decodeBitsPerPixel(string $byte): int
|
||||
{
|
||||
return intval(bindec($this->packedBits($byte, 1, 3))) + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode global color table sorted status.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
private function decodeGlobalColorTableSorted(string $byte): bool
|
||||
{
|
||||
return $this->hasPackedBit($byte, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode size of global color table.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
private function decodeGlobalColorTableSize(string $byte): int
|
||||
{
|
||||
return intval(bindec($this->packedBits($byte, 5, 3)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode background color index.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
private function decodeBackgroundColorIndex(string $source): int
|
||||
{
|
||||
$unpacked = unpack('C', $source);
|
||||
|
||||
if ($unpacked === false || !array_key_exists(1, $unpacked)) {
|
||||
throw new DecoderException('Failed to decode background color index in logical screen descriptor');
|
||||
}
|
||||
|
||||
return $unpacked[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode pixel aspect ratio.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
private function decodePixelAspectRatio(string $source): int
|
||||
{
|
||||
$unpacked = unpack('C', $source);
|
||||
|
||||
if ($unpacked === false || !array_key_exists(1, $unpacked)) {
|
||||
throw new DecoderException('Failed to decode pixel aspect ratio in logical screen descriptor');
|
||||
}
|
||||
|
||||
return $unpacked[1];
|
||||
}
|
||||
}
|
||||
10
vendor/intervention/gif/src/Decoders/NetscapeApplicationExtensionDecoder.php
vendored
Normal file
10
vendor/intervention/gif/src/Decoders/NetscapeApplicationExtensionDecoder.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Decoders;
|
||||
|
||||
class NetscapeApplicationExtensionDecoder extends ApplicationExtensionDecoder
|
||||
{
|
||||
//
|
||||
}
|
||||
75
vendor/intervention/gif/src/Decoders/PlainTextExtensionDecoder.php
vendored
Normal file
75
vendor/intervention/gif/src/Decoders/PlainTextExtensionDecoder.php
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Decoders;
|
||||
|
||||
use Intervention\Gif\Blocks\PlainTextExtension;
|
||||
use Intervention\Gif\Exceptions\DecoderException;
|
||||
|
||||
class PlainTextExtensionDecoder extends AbstractDecoder
|
||||
{
|
||||
/**
|
||||
* Decode current source.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
public function decode(): PlainTextExtension
|
||||
{
|
||||
$extension = new PlainTextExtension();
|
||||
|
||||
// skip marker & label
|
||||
$this->nextBytesOrFail(2);
|
||||
|
||||
// skip info block
|
||||
$this->nextBytesOrFail($this->infoBlockSize());
|
||||
|
||||
// text blocks
|
||||
$extension->setText($this->decodeTextBlocks());
|
||||
|
||||
return $extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get number of bytes in header block.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
private function infoBlockSize(): int
|
||||
{
|
||||
$unpacked = unpack('C', $this->nextByteOrFail());
|
||||
|
||||
if ($unpacked === false || !array_key_exists(1, $unpacked)) {
|
||||
throw new DecoderException('Failed to decode info block size of plain text extension');
|
||||
}
|
||||
|
||||
return $unpacked[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode text sub blocks.
|
||||
*
|
||||
* @throws DecoderException
|
||||
* @return array<string>
|
||||
*/
|
||||
private function decodeTextBlocks(): array
|
||||
{
|
||||
$blocks = [];
|
||||
|
||||
do {
|
||||
$char = $this->nextByteOrFail();
|
||||
$unpacked = unpack('C', $char);
|
||||
if ($unpacked === false || !array_key_exists(1, $unpacked)) {
|
||||
throw new DecoderException('Failed to decode text blocks in plain text extension');
|
||||
}
|
||||
|
||||
$size = (int) $unpacked[1];
|
||||
|
||||
if ($size > 0) {
|
||||
$blocks[] = $this->nextBytesOrFail($size);
|
||||
}
|
||||
} while ($char !== PlainTextExtension::TERMINATOR);
|
||||
|
||||
return $blocks;
|
||||
}
|
||||
}
|
||||
41
vendor/intervention/gif/src/Decoders/TableBasedImageDecoder.php
vendored
Normal file
41
vendor/intervention/gif/src/Decoders/TableBasedImageDecoder.php
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Decoders;
|
||||
|
||||
use Intervention\Gif\Blocks\ColorTable;
|
||||
use Intervention\Gif\Blocks\ImageData;
|
||||
use Intervention\Gif\Blocks\ImageDescriptor;
|
||||
use Intervention\Gif\Blocks\TableBasedImage;
|
||||
use Intervention\Gif\Exceptions\DecoderException;
|
||||
|
||||
class TableBasedImageDecoder extends AbstractDecoder
|
||||
{
|
||||
/**
|
||||
* Decode TableBasedImage.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
public function decode(): TableBasedImage
|
||||
{
|
||||
$block = new TableBasedImage();
|
||||
|
||||
$block->setImageDescriptor(ImageDescriptor::decode($this->stream));
|
||||
|
||||
if ($block->imageDescriptor()->hasLocalColorTable()) {
|
||||
$block->setColorTable(
|
||||
ColorTable::decode(
|
||||
$this->stream,
|
||||
$block->imageDescriptor()->localColorTableByteSize()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$block->setImageData(
|
||||
ImageData::decode($this->stream)
|
||||
);
|
||||
|
||||
return $block;
|
||||
}
|
||||
}
|
||||
13
vendor/intervention/gif/src/DisposalMethod.php
vendored
Normal file
13
vendor/intervention/gif/src/DisposalMethod.php
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif;
|
||||
|
||||
enum DisposalMethod: int
|
||||
{
|
||||
case UNDEFINED = 0;
|
||||
case NONE = 1; // overlay each frame in sequence
|
||||
case BACKGROUND = 2; // clear to background (as indicated by the logical screen descriptor)
|
||||
case PREVIOUS = 3; // restore the canvas to its previous state
|
||||
}
|
||||
21
vendor/intervention/gif/src/Encoders/AbstractEncoder.php
vendored
Normal file
21
vendor/intervention/gif/src/Encoders/AbstractEncoder.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Encoders;
|
||||
|
||||
abstract class AbstractEncoder
|
||||
{
|
||||
/**
|
||||
* Encode current entity.
|
||||
*/
|
||||
abstract public function encode(): string;
|
||||
|
||||
/**
|
||||
* Create new instance.
|
||||
*/
|
||||
public function __construct(protected mixed $entity)
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
37
vendor/intervention/gif/src/Encoders/ApplicationExtensionEncoder.php
vendored
Normal file
37
vendor/intervention/gif/src/Encoders/ApplicationExtensionEncoder.php
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Encoders;
|
||||
|
||||
use Intervention\Gif\Blocks\ApplicationExtension;
|
||||
use Intervention\Gif\Blocks\DataSubBlock;
|
||||
use Intervention\Gif\Exceptions\EncoderException;
|
||||
|
||||
class ApplicationExtensionEncoder extends AbstractEncoder
|
||||
{
|
||||
/**
|
||||
* Create new decoder instance.
|
||||
*/
|
||||
public function __construct(ApplicationExtension $entity)
|
||||
{
|
||||
parent::__construct($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode current entity.
|
||||
*
|
||||
* @throws EncoderException
|
||||
*/
|
||||
public function encode(): string
|
||||
{
|
||||
return implode('', [
|
||||
ApplicationExtension::MARKER,
|
||||
ApplicationExtension::LABEL,
|
||||
pack('C', $this->entity->blockSize()),
|
||||
$this->entity->application(),
|
||||
implode('', array_map(fn(DataSubBlock $block): string => $block->encode(), $this->entity->blocks())),
|
||||
ApplicationExtension::TERMINATOR,
|
||||
]);
|
||||
}
|
||||
}
|
||||
38
vendor/intervention/gif/src/Encoders/ColorEncoder.php
vendored
Normal file
38
vendor/intervention/gif/src/Encoders/ColorEncoder.php
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Encoders;
|
||||
|
||||
use Intervention\Gif\Blocks\Color;
|
||||
|
||||
class ColorEncoder extends AbstractEncoder
|
||||
{
|
||||
/**
|
||||
* Create new instance.
|
||||
*/
|
||||
public function __construct(Color $entity)
|
||||
{
|
||||
parent::__construct($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode current entity.
|
||||
*/
|
||||
public function encode(): string
|
||||
{
|
||||
return implode('', [
|
||||
$this->encodeColorValue($this->entity->red()),
|
||||
$this->encodeColorValue($this->entity->green()),
|
||||
$this->encodeColorValue($this->entity->blue()),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode color value.
|
||||
*/
|
||||
private function encodeColorValue(int $value): string
|
||||
{
|
||||
return pack('C', $value);
|
||||
}
|
||||
}
|
||||
33
vendor/intervention/gif/src/Encoders/ColorTableEncoder.php
vendored
Normal file
33
vendor/intervention/gif/src/Encoders/ColorTableEncoder.php
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Encoders;
|
||||
|
||||
use Intervention\Gif\Blocks\Color;
|
||||
use Intervention\Gif\Blocks\ColorTable;
|
||||
use Intervention\Gif\Exceptions\EncoderException;
|
||||
|
||||
class ColorTableEncoder extends AbstractEncoder
|
||||
{
|
||||
/**
|
||||
* Create new instance.
|
||||
*/
|
||||
public function __construct(ColorTable $entity)
|
||||
{
|
||||
parent::__construct($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode current entity.
|
||||
*
|
||||
* @throws EncoderException
|
||||
*/
|
||||
public function encode(): string
|
||||
{
|
||||
return implode('', array_map(
|
||||
fn(Color $color): string => $color->encode(),
|
||||
$this->entity->colors(),
|
||||
));
|
||||
}
|
||||
}
|
||||
41
vendor/intervention/gif/src/Encoders/CommentExtensionEncoder.php
vendored
Normal file
41
vendor/intervention/gif/src/Encoders/CommentExtensionEncoder.php
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Encoders;
|
||||
|
||||
use Intervention\Gif\Blocks\CommentExtension;
|
||||
|
||||
class CommentExtensionEncoder extends AbstractEncoder
|
||||
{
|
||||
/**
|
||||
* Create new decoder instance.
|
||||
*/
|
||||
public function __construct(CommentExtension $entity)
|
||||
{
|
||||
parent::__construct($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode current entity.
|
||||
*/
|
||||
public function encode(): string
|
||||
{
|
||||
return implode('', [
|
||||
CommentExtension::MARKER,
|
||||
CommentExtension::LABEL,
|
||||
$this->encodeComments(),
|
||||
CommentExtension::TERMINATOR,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode comment blocks.
|
||||
*/
|
||||
private function encodeComments(): string
|
||||
{
|
||||
return implode('', array_map(function (string $comment): string {
|
||||
return pack('C', strlen($comment)) . $comment;
|
||||
}, $this->entity->comments()));
|
||||
}
|
||||
}
|
||||
26
vendor/intervention/gif/src/Encoders/DataSubBlockEncoder.php
vendored
Normal file
26
vendor/intervention/gif/src/Encoders/DataSubBlockEncoder.php
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Encoders;
|
||||
|
||||
use Intervention\Gif\Blocks\DataSubBlock;
|
||||
|
||||
class DataSubBlockEncoder extends AbstractEncoder
|
||||
{
|
||||
/**
|
||||
* Create new instance.
|
||||
*/
|
||||
public function __construct(DataSubBlock $entity)
|
||||
{
|
||||
parent::__construct($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode current entity.
|
||||
*/
|
||||
public function encode(): string
|
||||
{
|
||||
return pack('C', $this->entity->size()) . $this->entity->value();
|
||||
}
|
||||
}
|
||||
49
vendor/intervention/gif/src/Encoders/FrameBlockEncoder.php
vendored
Normal file
49
vendor/intervention/gif/src/Encoders/FrameBlockEncoder.php
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Encoders;
|
||||
|
||||
use Intervention\Gif\Blocks\ApplicationExtension;
|
||||
use Intervention\Gif\Blocks\CommentExtension;
|
||||
use Intervention\Gif\Blocks\FrameBlock;
|
||||
use Intervention\Gif\Exceptions\EncoderException;
|
||||
|
||||
class FrameBlockEncoder extends AbstractEncoder
|
||||
{
|
||||
/**
|
||||
* Create new decoder instance.
|
||||
*/
|
||||
public function __construct(FrameBlock $entity)
|
||||
{
|
||||
parent::__construct($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode current entity.
|
||||
*
|
||||
* @throws EncoderException
|
||||
*/
|
||||
public function encode(): string
|
||||
{
|
||||
$graphicControlExtension = $this->entity->graphicControlExtension();
|
||||
$colorTable = $this->entity->colorTable();
|
||||
$plainTextExtension = $this->entity->plainTextExtension();
|
||||
|
||||
return implode('', [
|
||||
implode('', array_map(
|
||||
fn(ApplicationExtension $extension): string => $extension->encode(),
|
||||
$this->entity->applicationExtensions(),
|
||||
)),
|
||||
implode('', array_map(
|
||||
fn(CommentExtension $extension): string => $extension->encode(),
|
||||
$this->entity->commentExtensions(),
|
||||
)),
|
||||
$plainTextExtension ? $plainTextExtension->encode() : '',
|
||||
$graphicControlExtension ? $graphicControlExtension->encode() : '',
|
||||
$this->entity->imageDescriptor()->encode(),
|
||||
$colorTable ? $colorTable->encode() : '',
|
||||
$this->entity->imageData()->encode(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
76
vendor/intervention/gif/src/Encoders/GifDataStreamEncoder.php
vendored
Normal file
76
vendor/intervention/gif/src/Encoders/GifDataStreamEncoder.php
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Encoders;
|
||||
|
||||
use Intervention\Gif\Blocks\CommentExtension;
|
||||
use Intervention\Gif\Blocks\FrameBlock;
|
||||
use Intervention\Gif\Exceptions\EncoderException;
|
||||
use Intervention\Gif\GifDataStream;
|
||||
|
||||
class GifDataStreamEncoder extends AbstractEncoder
|
||||
{
|
||||
/**
|
||||
* Create new instance.
|
||||
*/
|
||||
public function __construct(GifDataStream $entity)
|
||||
{
|
||||
parent::__construct($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode current entity.
|
||||
*
|
||||
* @throws EncoderException
|
||||
*/
|
||||
public function encode(): string
|
||||
{
|
||||
return implode('', [
|
||||
$this->entity->header()->encode(),
|
||||
$this->entity->logicalScreenDescriptor()->encode(),
|
||||
$this->maybeEncodeGlobalColorTable(),
|
||||
$this->encodeFrames(),
|
||||
$this->encodeComments(),
|
||||
$this->entity->trailer()->encode(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode global color table if present in gif data.
|
||||
*/
|
||||
private function maybeEncodeGlobalColorTable(): string
|
||||
{
|
||||
if (!$this->entity->hasGlobalColorTable()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->entity->globalColorTable()->encode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode data blocks of source.
|
||||
*
|
||||
* @throws EncoderException
|
||||
*/
|
||||
private function encodeFrames(): string
|
||||
{
|
||||
return implode('', array_map(
|
||||
fn(FrameBlock $frame): string => $frame->encode(),
|
||||
$this->entity->frames(),
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode comment extension blocks of source.
|
||||
*
|
||||
* @throws EncoderException
|
||||
*/
|
||||
private function encodeComments(): string
|
||||
{
|
||||
return implode('', array_map(
|
||||
fn(CommentExtension $commentExtension): string => $commentExtension->encode(),
|
||||
$this->entity->comments()
|
||||
));
|
||||
}
|
||||
}
|
||||
63
vendor/intervention/gif/src/Encoders/GraphicControlExtensionEncoder.php
vendored
Normal file
63
vendor/intervention/gif/src/Encoders/GraphicControlExtensionEncoder.php
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Encoders;
|
||||
|
||||
use Intervention\Gif\Blocks\GraphicControlExtension;
|
||||
|
||||
class GraphicControlExtensionEncoder extends AbstractEncoder
|
||||
{
|
||||
/**
|
||||
* Create new instance.
|
||||
*/
|
||||
public function __construct(GraphicControlExtension $entity)
|
||||
{
|
||||
parent::__construct($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode current entity.
|
||||
*/
|
||||
public function encode(): string
|
||||
{
|
||||
return implode('', [
|
||||
GraphicControlExtension::MARKER,
|
||||
GraphicControlExtension::LABEL,
|
||||
GraphicControlExtension::BLOCKSIZE,
|
||||
$this->encodePackedField(),
|
||||
$this->encodeDelay(),
|
||||
$this->encodeTransparentColorIndex(),
|
||||
GraphicControlExtension::TERMINATOR,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode delay time.
|
||||
*/
|
||||
private function encodeDelay(): string
|
||||
{
|
||||
return pack('v*', $this->entity->delay());
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode transparent color index.
|
||||
*/
|
||||
private function encodeTransparentColorIndex(): string
|
||||
{
|
||||
return pack('C', $this->entity->transparentColorIndex());
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode packed field.
|
||||
*/
|
||||
private function encodePackedField(): string
|
||||
{
|
||||
return pack('C', bindec(implode('', [
|
||||
str_pad('0', 3, '0', STR_PAD_LEFT),
|
||||
str_pad(decbin($this->entity->disposalMethod()->value), 3, '0', STR_PAD_LEFT),
|
||||
(int) $this->entity->userInput(),
|
||||
(int) $this->entity->transparentColorExistance(),
|
||||
])));
|
||||
}
|
||||
}
|
||||
26
vendor/intervention/gif/src/Encoders/HeaderEncoder.php
vendored
Normal file
26
vendor/intervention/gif/src/Encoders/HeaderEncoder.php
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Encoders;
|
||||
|
||||
use Intervention\Gif\Blocks\Header;
|
||||
|
||||
class HeaderEncoder extends AbstractEncoder
|
||||
{
|
||||
/**
|
||||
* Create new instance.
|
||||
*/
|
||||
public function __construct(Header $entity)
|
||||
{
|
||||
parent::__construct($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode current entity.
|
||||
*/
|
||||
public function encode(): string
|
||||
{
|
||||
return Header::SIGNATURE . $this->entity->version();
|
||||
}
|
||||
}
|
||||
44
vendor/intervention/gif/src/Encoders/ImageDataEncoder.php
vendored
Normal file
44
vendor/intervention/gif/src/Encoders/ImageDataEncoder.php
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Encoders;
|
||||
|
||||
use Intervention\Gif\AbstractEntity;
|
||||
use Intervention\Gif\Blocks\DataSubBlock;
|
||||
use Intervention\Gif\Blocks\ImageData;
|
||||
use Intervention\Gif\Exceptions\EncoderException;
|
||||
use Intervention\Gif\Exceptions\StateException;
|
||||
|
||||
class ImageDataEncoder extends AbstractEncoder
|
||||
{
|
||||
/**
|
||||
* Create new instance.
|
||||
*/
|
||||
public function __construct(ImageData $entity)
|
||||
{
|
||||
parent::__construct($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode current entity.
|
||||
*
|
||||
* @throws EncoderException
|
||||
* @throws StateException
|
||||
*/
|
||||
public function encode(): string
|
||||
{
|
||||
if (!$this->entity->hasBlocks()) {
|
||||
throw new StateException('No data blocks in image data');
|
||||
}
|
||||
|
||||
return implode('', [
|
||||
pack('C', $this->entity->lzwMinCodeSize()),
|
||||
implode('', array_map(
|
||||
fn(DataSubBlock $block): string => $block->encode(),
|
||||
$this->entity->blocks(),
|
||||
)),
|
||||
AbstractEntity::TERMINATOR,
|
||||
]);
|
||||
}
|
||||
}
|
||||
95
vendor/intervention/gif/src/Encoders/ImageDescriptorEncoder.php
vendored
Normal file
95
vendor/intervention/gif/src/Encoders/ImageDescriptorEncoder.php
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Encoders;
|
||||
|
||||
use Intervention\Gif\Blocks\ImageDescriptor;
|
||||
|
||||
class ImageDescriptorEncoder extends AbstractEncoder
|
||||
{
|
||||
/**
|
||||
* Create new instance.
|
||||
*/
|
||||
public function __construct(ImageDescriptor $entity)
|
||||
{
|
||||
parent::__construct($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode current entity.
|
||||
*/
|
||||
public function encode(): string
|
||||
{
|
||||
return implode('', [
|
||||
ImageDescriptor::SEPARATOR,
|
||||
$this->encodeLeft(),
|
||||
$this->encodeTop(),
|
||||
$this->encodeWidth(),
|
||||
$this->encodeHeight(),
|
||||
$this->encodePackedField(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode left value.
|
||||
*/
|
||||
private function encodeLeft(): string
|
||||
{
|
||||
return pack('v*', $this->entity->left());
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode top value.
|
||||
*/
|
||||
private function encodeTop(): string
|
||||
{
|
||||
return pack('v*', $this->entity->top());
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode width value.
|
||||
*/
|
||||
private function encodeWidth(): string
|
||||
{
|
||||
return pack('v*', $this->entity->width());
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode height value.
|
||||
*/
|
||||
private function encodeHeight(): string
|
||||
{
|
||||
return pack('v*', $this->entity->height());
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode size of local color table.
|
||||
*/
|
||||
private function encodeLocalColorTableSize(): string
|
||||
{
|
||||
return str_pad(decbin($this->entity->localColorTableSize()), 3, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode reserved field.
|
||||
*/
|
||||
private function encodeReservedField(): string
|
||||
{
|
||||
return str_pad('0', 2, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode packed field.
|
||||
*/
|
||||
private function encodePackedField(): string
|
||||
{
|
||||
return pack('C', bindec(implode('', [
|
||||
(int) $this->entity->localColorTableExistance(),
|
||||
(int) $this->entity->isInterlaced(),
|
||||
(int) $this->entity->localColorTableSorted(),
|
||||
$this->encodeReservedField(),
|
||||
$this->encodeLocalColorTableSize(),
|
||||
])));
|
||||
}
|
||||
}
|
||||
93
vendor/intervention/gif/src/Encoders/LogicalScreenDescriptorEncoder.php
vendored
Normal file
93
vendor/intervention/gif/src/Encoders/LogicalScreenDescriptorEncoder.php
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Encoders;
|
||||
|
||||
use Intervention\Gif\Blocks\LogicalScreenDescriptor;
|
||||
|
||||
class LogicalScreenDescriptorEncoder extends AbstractEncoder
|
||||
{
|
||||
/**
|
||||
* Create new instance.
|
||||
*/
|
||||
public function __construct(LogicalScreenDescriptor $entity)
|
||||
{
|
||||
parent::__construct($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode current entity.
|
||||
*/
|
||||
public function encode(): string
|
||||
{
|
||||
return implode('', [
|
||||
$this->encodeWidth(),
|
||||
$this->encodeHeight(),
|
||||
$this->encodePackedField(),
|
||||
$this->encodeBackgroundColorIndex(),
|
||||
$this->encodePixelAspectRatio(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode width of current instance.
|
||||
*/
|
||||
private function encodeWidth(): string
|
||||
{
|
||||
return pack('v*', $this->entity->width());
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode height of current instance.
|
||||
*/
|
||||
private function encodeHeight(): string
|
||||
{
|
||||
return pack('v*', $this->entity->height());
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode background color index of global color table.
|
||||
*/
|
||||
private function encodeBackgroundColorIndex(): string
|
||||
{
|
||||
return pack('C', $this->entity->backgroundColorIndex());
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode pixel aspect ratio.
|
||||
*/
|
||||
private function encodePixelAspectRatio(): string
|
||||
{
|
||||
return pack('C', $this->entity->pixelAspectRatio());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return color resolution for encoding.
|
||||
*/
|
||||
private function encodeColorResolution(): string
|
||||
{
|
||||
return str_pad(decbin($this->entity->bitsPerPixel() - 1), 3, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode size of global color table.
|
||||
*/
|
||||
private function encodeGlobalColorTableSize(): string
|
||||
{
|
||||
return str_pad(decbin($this->entity->globalColorTableSize()), 3, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode packed field of current instance.
|
||||
*/
|
||||
private function encodePackedField(): string
|
||||
{
|
||||
return pack('C', bindec(implode('', [
|
||||
(int) $this->entity->globalColorTableExistance(),
|
||||
$this->encodeColorResolution(),
|
||||
(int) $this->entity->globalColorTableSorted(),
|
||||
$this->encodeGlobalColorTableSize(),
|
||||
])));
|
||||
}
|
||||
}
|
||||
38
vendor/intervention/gif/src/Encoders/NetscapeApplicationExtensionEncoder.php
vendored
Normal file
38
vendor/intervention/gif/src/Encoders/NetscapeApplicationExtensionEncoder.php
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Encoders;
|
||||
|
||||
use Intervention\Gif\Blocks\ApplicationExtension;
|
||||
use Intervention\Gif\Blocks\DataSubBlock;
|
||||
use Intervention\Gif\Blocks\NetscapeApplicationExtension;
|
||||
use Intervention\Gif\Exceptions\EncoderException;
|
||||
|
||||
class NetscapeApplicationExtensionEncoder extends ApplicationExtensionEncoder
|
||||
{
|
||||
/**
|
||||
* Create new decoder instance.
|
||||
*/
|
||||
public function __construct(NetscapeApplicationExtension $entity)
|
||||
{
|
||||
parent::__construct($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode current source.
|
||||
*
|
||||
* @throws EncoderException
|
||||
*/
|
||||
public function encode(): string
|
||||
{
|
||||
return implode('', [
|
||||
ApplicationExtension::MARKER,
|
||||
ApplicationExtension::LABEL,
|
||||
pack('C', $this->entity->blockSize()),
|
||||
$this->entity->application(),
|
||||
implode('', array_map(fn(DataSubBlock $block): string => $block->encode(), $this->entity->blocks())),
|
||||
ApplicationExtension::TERMINATOR,
|
||||
]);
|
||||
}
|
||||
}
|
||||
55
vendor/intervention/gif/src/Encoders/PlainTextExtensionEncoder.php
vendored
Normal file
55
vendor/intervention/gif/src/Encoders/PlainTextExtensionEncoder.php
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Encoders;
|
||||
|
||||
use Intervention\Gif\Blocks\PlainTextExtension;
|
||||
|
||||
class PlainTextExtensionEncoder extends AbstractEncoder
|
||||
{
|
||||
/**
|
||||
* Create new instance.
|
||||
*/
|
||||
public function __construct(PlainTextExtension $entity)
|
||||
{
|
||||
parent::__construct($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode current entity.
|
||||
*/
|
||||
public function encode(): string
|
||||
{
|
||||
if (!$this->entity->hasText()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return implode('', [
|
||||
PlainTextExtension::MARKER,
|
||||
PlainTextExtension::LABEL,
|
||||
$this->encodeHead(),
|
||||
$this->encodeTexts(),
|
||||
PlainTextExtension::TERMINATOR,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode head block.
|
||||
*/
|
||||
private function encodeHead(): string
|
||||
{
|
||||
return "\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode text chunks.
|
||||
*/
|
||||
private function encodeTexts(): string
|
||||
{
|
||||
return implode('', array_map(
|
||||
fn(string $text): string => pack('C', strlen($text)) . $text,
|
||||
$this->entity->text(),
|
||||
));
|
||||
}
|
||||
}
|
||||
30
vendor/intervention/gif/src/Encoders/TableBasedImageEncoder.php
vendored
Normal file
30
vendor/intervention/gif/src/Encoders/TableBasedImageEncoder.php
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Encoders;
|
||||
|
||||
use Intervention\Gif\Blocks\TableBasedImage;
|
||||
|
||||
class TableBasedImageEncoder extends AbstractEncoder
|
||||
{
|
||||
/**
|
||||
* Create new instance.
|
||||
*/
|
||||
public function __construct(TableBasedImage $entity)
|
||||
{
|
||||
parent::__construct($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode current entity.
|
||||
*/
|
||||
public function encode(): string
|
||||
{
|
||||
return implode('', [
|
||||
$this->entity->imageDescriptor()->encode(),
|
||||
$this->entity->colorTable() ? $this->entity->colorTable()->encode() : '',
|
||||
$this->entity->imageData()->encode(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
26
vendor/intervention/gif/src/Encoders/TrailerEncoder.php
vendored
Normal file
26
vendor/intervention/gif/src/Encoders/TrailerEncoder.php
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Encoders;
|
||||
|
||||
use Intervention\Gif\Blocks\Trailer;
|
||||
|
||||
class TrailerEncoder extends AbstractEncoder
|
||||
{
|
||||
/**
|
||||
* Create new instance.
|
||||
*/
|
||||
public function __construct(Trailer $entity)
|
||||
{
|
||||
parent::__construct($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode current entity.
|
||||
*/
|
||||
public function encode(): string
|
||||
{
|
||||
return Trailer::MARKER;
|
||||
}
|
||||
}
|
||||
10
vendor/intervention/gif/src/Exceptions/ArgumentException.php
vendored
Normal file
10
vendor/intervention/gif/src/Exceptions/ArgumentException.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Exceptions;
|
||||
|
||||
class ArgumentException extends LogicException
|
||||
{
|
||||
//
|
||||
}
|
||||
10
vendor/intervention/gif/src/Exceptions/CoreException.php
vendored
Normal file
10
vendor/intervention/gif/src/Exceptions/CoreException.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Exceptions;
|
||||
|
||||
class CoreException extends RuntimeException
|
||||
{
|
||||
//
|
||||
}
|
||||
10
vendor/intervention/gif/src/Exceptions/DecoderException.php
vendored
Normal file
10
vendor/intervention/gif/src/Exceptions/DecoderException.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Exceptions;
|
||||
|
||||
class DecoderException extends RuntimeException
|
||||
{
|
||||
//
|
||||
}
|
||||
10
vendor/intervention/gif/src/Exceptions/EncoderException.php
vendored
Normal file
10
vendor/intervention/gif/src/Exceptions/EncoderException.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Exceptions;
|
||||
|
||||
class EncoderException extends RuntimeException
|
||||
{
|
||||
//
|
||||
}
|
||||
10
vendor/intervention/gif/src/Exceptions/FilesystemException.php
vendored
Normal file
10
vendor/intervention/gif/src/Exceptions/FilesystemException.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Exceptions;
|
||||
|
||||
class FilesystemException extends RuntimeException
|
||||
{
|
||||
//
|
||||
}
|
||||
12
vendor/intervention/gif/src/Exceptions/GifException.php
vendored
Normal file
12
vendor/intervention/gif/src/Exceptions/GifException.php
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class GifException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
||||
10
vendor/intervention/gif/src/Exceptions/InvalidArgumentException.php
vendored
Normal file
10
vendor/intervention/gif/src/Exceptions/InvalidArgumentException.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Exceptions;
|
||||
|
||||
class InvalidArgumentException extends ArgumentException
|
||||
{
|
||||
//
|
||||
}
|
||||
10
vendor/intervention/gif/src/Exceptions/LogicException.php
vendored
Normal file
10
vendor/intervention/gif/src/Exceptions/LogicException.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Exceptions;
|
||||
|
||||
class LogicException extends GifException
|
||||
{
|
||||
//
|
||||
}
|
||||
10
vendor/intervention/gif/src/Exceptions/RuntimeException.php
vendored
Normal file
10
vendor/intervention/gif/src/Exceptions/RuntimeException.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Exceptions;
|
||||
|
||||
class RuntimeException extends GifException
|
||||
{
|
||||
//
|
||||
}
|
||||
10
vendor/intervention/gif/src/Exceptions/SplitterException.php
vendored
Normal file
10
vendor/intervention/gif/src/Exceptions/SplitterException.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Exceptions;
|
||||
|
||||
class SplitterException extends RuntimeException
|
||||
{
|
||||
//
|
||||
}
|
||||
10
vendor/intervention/gif/src/Exceptions/StateException.php
vendored
Normal file
10
vendor/intervention/gif/src/Exceptions/StateException.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Exceptions;
|
||||
|
||||
class StateException extends LogicException
|
||||
{
|
||||
//
|
||||
}
|
||||
10
vendor/intervention/gif/src/Exceptions/StreamException.php
vendored
Normal file
10
vendor/intervention/gif/src/Exceptions/StreamException.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Exceptions;
|
||||
|
||||
class StreamException extends FilesystemException
|
||||
{
|
||||
//
|
||||
}
|
||||
197
vendor/intervention/gif/src/GifDataStream.php
vendored
Normal file
197
vendor/intervention/gif/src/GifDataStream.php
vendored
Normal file
@@ -0,0 +1,197 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif;
|
||||
|
||||
use Intervention\Gif\Blocks\ColorTable;
|
||||
use Intervention\Gif\Blocks\CommentExtension;
|
||||
use Intervention\Gif\Blocks\FrameBlock;
|
||||
use Intervention\Gif\Blocks\Header;
|
||||
use Intervention\Gif\Blocks\LogicalScreenDescriptor;
|
||||
use Intervention\Gif\Blocks\NetscapeApplicationExtension;
|
||||
use Intervention\Gif\Blocks\Trailer;
|
||||
|
||||
class GifDataStream extends AbstractEntity
|
||||
{
|
||||
/**
|
||||
* Create new instance.
|
||||
*
|
||||
* @param array<FrameBlock> $frames
|
||||
* @param array<CommentExtension> $comments
|
||||
*/
|
||||
public function __construct(
|
||||
protected Header $header = new Header(),
|
||||
protected LogicalScreenDescriptor $logicalScreenDescriptor = new LogicalScreenDescriptor(),
|
||||
protected ?ColorTable $globalColorTable = null,
|
||||
protected array $frames = [],
|
||||
protected array $comments = []
|
||||
) {
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Get header.
|
||||
*/
|
||||
public function header(): Header
|
||||
{
|
||||
return $this->header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set header.
|
||||
*/
|
||||
public function setHeader(Header $header): self
|
||||
{
|
||||
$this->header = $header;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get logical screen descriptor.
|
||||
*/
|
||||
public function logicalScreenDescriptor(): LogicalScreenDescriptor
|
||||
{
|
||||
return $this->logicalScreenDescriptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set logical screen descriptor.
|
||||
*/
|
||||
public function setLogicalScreenDescriptor(LogicalScreenDescriptor $descriptor): self
|
||||
{
|
||||
$this->logicalScreenDescriptor = $descriptor;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return global color table if available else null.
|
||||
*/
|
||||
public function globalColorTable(): ?ColorTable
|
||||
{
|
||||
return $this->globalColorTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set global color table.
|
||||
*/
|
||||
public function setGlobalColorTable(ColorTable $table): self
|
||||
{
|
||||
$this->globalColorTable = $table;
|
||||
$this->logicalScreenDescriptor->setGlobalColorTableExistance(true);
|
||||
$this->logicalScreenDescriptor->setGlobalColorTableSize(
|
||||
$table->logicalSize()
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get main graphic control extension.
|
||||
*/
|
||||
public function mainApplicationExtension(): ?NetscapeApplicationExtension
|
||||
{
|
||||
foreach ($this->frames as $frame) {
|
||||
$extension = $frame->netscapeExtension();
|
||||
if ($extension !== null) {
|
||||
return $extension;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get array of frames.
|
||||
*
|
||||
* @return array<FrameBlock>
|
||||
*/
|
||||
public function frames(): array
|
||||
{
|
||||
return $this->frames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return array of "global" comments.
|
||||
*
|
||||
* @return array<CommentExtension>
|
||||
*/
|
||||
public function comments(): array
|
||||
{
|
||||
return $this->comments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return first frame.
|
||||
*/
|
||||
public function firstFrame(): ?FrameBlock
|
||||
{
|
||||
if ($this->frames === []) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!array_key_exists(0, $this->frames)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->frames[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Add frame.
|
||||
*/
|
||||
public function addFrame(FrameBlock $frame): self
|
||||
{
|
||||
$this->frames[] = $frame;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add comment extension.
|
||||
*/
|
||||
public function addComment(CommentExtension $comment): self
|
||||
{
|
||||
$this->comments[] = $comment;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current data.
|
||||
*
|
||||
* @param array<FrameBlock> $frames
|
||||
*/
|
||||
public function setFrames(array $frames): self
|
||||
{
|
||||
$this->frames = $frames;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get trailer.
|
||||
*/
|
||||
public function trailer(): Trailer
|
||||
{
|
||||
return new Trailer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if gif is animated.
|
||||
*/
|
||||
public function isAnimated(): bool
|
||||
{
|
||||
return count($this->frames()) > 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if global color table is set.
|
||||
*/
|
||||
public function hasGlobalColorTable(): bool
|
||||
{
|
||||
return !is_null($this->globalColorTable);
|
||||
}
|
||||
}
|
||||
394
vendor/intervention/gif/src/Splitter.php
vendored
Normal file
394
vendor/intervention/gif/src/Splitter.php
vendored
Normal file
@@ -0,0 +1,394 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif;
|
||||
|
||||
use ArrayIterator;
|
||||
use GdImage;
|
||||
use Intervention\Gif\Exceptions\CoreException;
|
||||
use Intervention\Gif\Exceptions\DecoderException;
|
||||
use Intervention\Gif\Exceptions\EncoderException;
|
||||
use Intervention\Gif\Exceptions\StreamException;
|
||||
use Intervention\Gif\Exceptions\InvalidArgumentException;
|
||||
use Intervention\Gif\Exceptions\SplitterException;
|
||||
use IteratorAggregate;
|
||||
use Traversable;
|
||||
|
||||
/**
|
||||
* @implements IteratorAggregate<GifDataStream|GdImage>
|
||||
*/
|
||||
class Splitter implements IteratorAggregate
|
||||
{
|
||||
/**
|
||||
* Single frames resolved from main GifDataStream.
|
||||
*
|
||||
* @var array<GifDataStream|GdImage>
|
||||
*/
|
||||
protected array $frames = [];
|
||||
|
||||
/**
|
||||
* Delays of each frame resolved from main GifDataStream.
|
||||
*
|
||||
* @var array<int>
|
||||
*/
|
||||
protected array $delays = [];
|
||||
|
||||
/**
|
||||
* Loop count of main GifDataStream.
|
||||
*/
|
||||
protected int $loops;
|
||||
|
||||
/**
|
||||
* Create new instance.
|
||||
*
|
||||
* @throws SplitterException
|
||||
*/
|
||||
public function __construct(protected GifDataStream $gif)
|
||||
{
|
||||
try {
|
||||
$this->loops = $gif->mainApplicationExtension()?->loops() ?: 0;
|
||||
} catch (DecoderException $e) {
|
||||
throw new SplitterException('Failed to create instance from ' . GifDataStream::class, previous: $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create splitter instance from gif data stream object.
|
||||
*
|
||||
* @throws SplitterException
|
||||
*/
|
||||
public static function create(GifDataStream $stream): self
|
||||
{
|
||||
return new self($stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create splitter instance from raw binary gif image data.
|
||||
*
|
||||
* @throws SplitterException
|
||||
* @throws InvalidArgumentException
|
||||
* @throws StreamException
|
||||
* @throws DecoderException
|
||||
*/
|
||||
public static function decode(mixed $input): self
|
||||
{
|
||||
return new self(Decoder::decode($input));
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over the frames and pass each frame to a closure.
|
||||
*/
|
||||
public function each(callable $callback): self
|
||||
{
|
||||
array_map($callback, $this->frames, $this->delays);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set stream of instance.
|
||||
*/
|
||||
public function setStream(GifDataStream $stream): self
|
||||
{
|
||||
$this->gif = $stream;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build iterator.
|
||||
*/
|
||||
public function getIterator(): Traversable
|
||||
{
|
||||
return new ArrayIterator($this->frames);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get frames.
|
||||
*
|
||||
* @return array<GifDataStream|GdImage>
|
||||
*/
|
||||
public function frames(): array
|
||||
{
|
||||
return $this->frames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get delays.
|
||||
*
|
||||
* @return array<int>
|
||||
*/
|
||||
public function delays(): array
|
||||
{
|
||||
return $this->delays;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get loop count of currently handled gif data.
|
||||
*/
|
||||
public function loops(): int
|
||||
{
|
||||
return $this->loops;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split current stream into array of seperate gif data stream objects for each frame.
|
||||
*
|
||||
* @throws SplitterException
|
||||
*/
|
||||
public function split(): self
|
||||
{
|
||||
$this->frames = [];
|
||||
|
||||
foreach ($this->gif->frames() as $frame) {
|
||||
// create separate stream for each frame
|
||||
try {
|
||||
$gif = Builder::canvas(
|
||||
$this->gif->logicalScreenDescriptor()->width(),
|
||||
$this->gif->logicalScreenDescriptor()->height()
|
||||
)->gifDataStream();
|
||||
} catch (InvalidArgumentException $e) {
|
||||
throw new SplitterException('Failed to create separate stream resource for each frame', previous: $e);
|
||||
}
|
||||
|
||||
// check if working stream has global color table
|
||||
$table = $this->gif->globalColorTable();
|
||||
if ($table !== null) {
|
||||
$gif->setGlobalColorTable($table);
|
||||
$gif->logicalScreenDescriptor()->setGlobalColorTableExistance(true);
|
||||
|
||||
$gif->logicalScreenDescriptor()->setGlobalColorTableSorted(
|
||||
$this->gif->logicalScreenDescriptor()->globalColorTableSorted()
|
||||
);
|
||||
|
||||
$gif->logicalScreenDescriptor()->setGlobalColorTableSize(
|
||||
$this->gif->logicalScreenDescriptor()->globalColorTableSize()
|
||||
);
|
||||
|
||||
$gif->logicalScreenDescriptor()->setBackgroundColorIndex(
|
||||
$this->gif->logicalScreenDescriptor()->backgroundColorIndex()
|
||||
);
|
||||
|
||||
$gif->logicalScreenDescriptor()->setPixelAspectRatio(
|
||||
$this->gif->logicalScreenDescriptor()->pixelAspectRatio()
|
||||
);
|
||||
|
||||
$gif->logicalScreenDescriptor()->setBitsPerPixel(
|
||||
$this->gif->logicalScreenDescriptor()->bitsPerPixel()
|
||||
);
|
||||
}
|
||||
|
||||
// copy original frame
|
||||
$gif->addFrame($frame);
|
||||
|
||||
$this->frames[] = $gif;
|
||||
$this->delays[] = match (is_object($frame->graphicControlExtension())) {
|
||||
true => $frame->graphicControlExtension()->delay(),
|
||||
default => 0,
|
||||
};
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform current frames to an a rray of transparency flattened GdImage objects for each frame.
|
||||
*
|
||||
* @throws SplitterException
|
||||
* @throws CoreException
|
||||
*/
|
||||
public function flatten(): self
|
||||
{
|
||||
$frames = $this->unprocessedFramesOrFail();
|
||||
$gdImages = $this->extractFrames();
|
||||
|
||||
// non-animated gif files don't need to be flattened
|
||||
// just replace frames with extracted
|
||||
if (count($frames) === 1) {
|
||||
$this->frames = $gdImages;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
// get main image size
|
||||
$width = imagesx($gdImages[0]);
|
||||
$height = imagesy($gdImages[0]);
|
||||
$transparent = imagecolortransparent($gdImages[0]);
|
||||
|
||||
foreach ($gdImages as $key => $gdImage) {
|
||||
// get meta data of frame
|
||||
$gif = $frames[$key];
|
||||
$descriptor = $gif->firstFrame()?->imageDescriptor();
|
||||
$offsetX = $descriptor?->left() ?: 0;
|
||||
$offsetY = $descriptor?->top() ?: 0;
|
||||
$w = $descriptor?->width() ?: 0;
|
||||
$h = $descriptor?->height() ?: 0;
|
||||
|
||||
if (in_array($this->disposalMethod($gif), [DisposalMethod::NONE, DisposalMethod::PREVIOUS])) {
|
||||
if ($key >= 1) {
|
||||
// create normalized gd image
|
||||
$canvas = imagecreatetruecolor($width, $height);
|
||||
|
||||
if ($canvas === false) {
|
||||
throw new CoreException('Failed to create new image instance for animation frame #' . $key);
|
||||
}
|
||||
|
||||
if (imagecolortransparent($gdImage) !== -1) {
|
||||
$transparent = imagecolortransparent($gdImage);
|
||||
} else {
|
||||
$transparent = imagecolorallocatealpha($gdImage, 255, 0, 255, 127);
|
||||
}
|
||||
|
||||
if (!is_int($transparent)) {
|
||||
throw new CoreException(
|
||||
'Failed to allocate transparent color in animation frame #' . $key,
|
||||
);
|
||||
}
|
||||
|
||||
// fill with transparent
|
||||
imagefill($canvas, 0, 0, $transparent);
|
||||
imagecolortransparent($canvas, $transparent);
|
||||
imagealphablending($canvas, true);
|
||||
|
||||
// insert last as base
|
||||
imagecopy(
|
||||
$canvas,
|
||||
$gdImages[$key - 1],
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
$width,
|
||||
$height
|
||||
);
|
||||
|
||||
// insert gd image
|
||||
imagecopy(
|
||||
$canvas,
|
||||
$gdImage,
|
||||
$offsetX,
|
||||
$offsetY,
|
||||
0,
|
||||
0,
|
||||
$w,
|
||||
$h
|
||||
);
|
||||
} else {
|
||||
imagealphablending($gdImage, true);
|
||||
$canvas = $gdImage;
|
||||
}
|
||||
} else {
|
||||
// create normalized gd image
|
||||
$canvas = imagecreatetruecolor($width, $height);
|
||||
if ($canvas === false) {
|
||||
throw new CoreException('Failed to create new image instance for animation frame #' . $key);
|
||||
}
|
||||
|
||||
if (imagecolortransparent($gdImage) !== -1) {
|
||||
$transparent = imagecolortransparent($gdImage);
|
||||
} else {
|
||||
$transparent = imagecolorallocatealpha($gdImage, 255, 0, 255, 127);
|
||||
}
|
||||
|
||||
if (!is_int($transparent)) {
|
||||
throw new CoreException('Animation frames cannot be converted into GdImage objects');
|
||||
}
|
||||
|
||||
// fill with transparent
|
||||
imagefill($canvas, 0, 0, $transparent);
|
||||
imagecolortransparent($canvas, $transparent);
|
||||
imagealphablending($canvas, true);
|
||||
|
||||
// insert frame gd image
|
||||
imagecopy(
|
||||
$canvas,
|
||||
$gdImage,
|
||||
$offsetX,
|
||||
$offsetY,
|
||||
0,
|
||||
0,
|
||||
$w,
|
||||
$h
|
||||
);
|
||||
}
|
||||
|
||||
$gdImages[$key] = $canvas;
|
||||
}
|
||||
|
||||
$this->frames = $gdImages;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return array of GdImage objects for each frame.
|
||||
*
|
||||
* @throws CoreException
|
||||
* @throws SplitterException
|
||||
* @return array<GdImage>
|
||||
*/
|
||||
private function extractFrames(): array
|
||||
{
|
||||
$gdImages = [];
|
||||
|
||||
foreach ($this->unprocessedFramesOrFail() as $frame) {
|
||||
try {
|
||||
$gdImage = imagecreatefromstring($frame->encode());
|
||||
} catch (EncoderException) {
|
||||
throw new CoreException('Failed to extract animation frame to GdImage object');
|
||||
}
|
||||
|
||||
if ($gdImage === false) {
|
||||
throw new CoreException('Failed to extract animation frame to GdImage object');
|
||||
}
|
||||
|
||||
imagepalettetotruecolor($gdImage);
|
||||
imagesavealpha($gdImage, true);
|
||||
|
||||
$gdImages[] = $gdImage;
|
||||
}
|
||||
|
||||
return $gdImages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find and return disposal method of given gif data stream.
|
||||
*
|
||||
* @throws SplitterException
|
||||
*/
|
||||
private function disposalMethod(GifDataStream $gif): DisposalMethod
|
||||
{
|
||||
$disposalMethod = $gif->firstFrame()?->graphicControlExtension()?->disposalMethod();
|
||||
|
||||
return $disposalMethod ?: throw new SplitterException('Failed to find disposal method in gif data stream');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return array of unprocessed frames or throw exception if frames are already processed.
|
||||
*
|
||||
* @throws SplitterException
|
||||
* @return array<GifDataStream>
|
||||
*/
|
||||
private function unprocessedFramesOrFail(): array
|
||||
{
|
||||
if (count($this->frames) === 0) {
|
||||
throw new SplitterException('No frames available. Run ' . $this::class . '::split() first');
|
||||
}
|
||||
|
||||
// if any frame is instanceof GdImage, frame was already flattened
|
||||
$processed = count(array_filter(
|
||||
$this->frames,
|
||||
fn(GdImage|GifDataStream $frame): bool => $frame instanceof GdImage,
|
||||
)) > 0;
|
||||
|
||||
if ($processed) {
|
||||
throw new SplitterException('Frames have already been flattened');
|
||||
}
|
||||
|
||||
return array_filter(
|
||||
$this->frames,
|
||||
fn(GifDataStream|GdImage $frame): bool => $frame instanceof GifDataStream,
|
||||
);
|
||||
}
|
||||
}
|
||||
43
vendor/intervention/gif/src/Traits/CanDecode.php
vendored
Normal file
43
vendor/intervention/gif/src/Traits/CanDecode.php
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Traits;
|
||||
|
||||
use Intervention\Gif\Decoders\AbstractDecoder;
|
||||
use Intervention\Gif\Exceptions\DecoderException;
|
||||
|
||||
trait CanDecode
|
||||
{
|
||||
/**
|
||||
* Decode current instance.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
public static function decode(mixed $source, ?int $length = null): mixed
|
||||
{
|
||||
return self::decoder($source, $length)->decode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get decoder for current instance.
|
||||
*
|
||||
* @throws DecoderException
|
||||
*/
|
||||
protected static function decoder(mixed $source, ?int $length = null): AbstractDecoder
|
||||
{
|
||||
$classname = sprintf('Intervention\Gif\Decoders\%sDecoder', self::shortClassname());
|
||||
|
||||
if (!class_exists($classname)) {
|
||||
throw new DecoderException('Decoder for "' . static::class . '" not found');
|
||||
}
|
||||
|
||||
$decoder = new $classname($source, $length);
|
||||
|
||||
if (!($decoder instanceof AbstractDecoder)) {
|
||||
throw new DecoderException('Decoder for "' . static::class . '" not found');
|
||||
}
|
||||
|
||||
return $decoder;
|
||||
}
|
||||
}
|
||||
43
vendor/intervention/gif/src/Traits/CanEncode.php
vendored
Normal file
43
vendor/intervention/gif/src/Traits/CanEncode.php
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Traits;
|
||||
|
||||
use Intervention\Gif\Encoders\AbstractEncoder;
|
||||
use Intervention\Gif\Exceptions\EncoderException;
|
||||
|
||||
trait CanEncode
|
||||
{
|
||||
/**
|
||||
* Encode current entity.
|
||||
*
|
||||
* @throws EncoderException
|
||||
*/
|
||||
public function encode(): string
|
||||
{
|
||||
return $this->encoder()->encode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get encoder object for current entity.
|
||||
*
|
||||
* @throws EncoderException
|
||||
*/
|
||||
protected function encoder(): AbstractEncoder
|
||||
{
|
||||
$classname = sprintf('Intervention\Gif\Encoders\%sEncoder', self::shortClassname());
|
||||
|
||||
if (!class_exists($classname)) {
|
||||
throw new EncoderException('Encoder for "' . $this::class . '" not found');
|
||||
}
|
||||
|
||||
$encoder = new $classname($this);
|
||||
|
||||
if (!($encoder instanceof AbstractEncoder)) {
|
||||
throw new EncoderException('Encoder for "' . $this::class . '" not found');
|
||||
}
|
||||
|
||||
return $encoder;
|
||||
}
|
||||
}
|
||||
70
vendor/intervention/gif/src/Traits/CanHandleFiles.php
vendored
Normal file
70
vendor/intervention/gif/src/Traits/CanHandleFiles.php
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Intervention\Gif\Traits;
|
||||
|
||||
use Intervention\Gif\Exceptions\StreamException;
|
||||
|
||||
trait CanHandleFiles
|
||||
{
|
||||
/**
|
||||
* Determines if input is file path.
|
||||
*/
|
||||
private static function isFilePath(mixed $input): bool
|
||||
{
|
||||
return is_string($input) && !self::hasNullBytes($input) && @is_file($input) === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if given string contains null bytes.
|
||||
*/
|
||||
private static function hasNullBytes(string $string): bool
|
||||
{
|
||||
return str_contains($string, chr(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create stream resource from given gif image data.
|
||||
*
|
||||
* @throws StreamException
|
||||
*/
|
||||
private static function streamFromData(string $data): mixed
|
||||
{
|
||||
$stream = fopen('php://temp', 'r+');
|
||||
|
||||
if ($stream === false) {
|
||||
throw new StreamException('Failed to create tempory stream resource');
|
||||
}
|
||||
|
||||
$result = fwrite($stream, $data);
|
||||
if ($result === false) {
|
||||
fclose($stream);
|
||||
throw new StreamException('Failed to write tempory stream resource');
|
||||
}
|
||||
|
||||
$result = rewind($stream);
|
||||
if ($result === false) {
|
||||
fclose($stream);
|
||||
throw new StreamException('Failed to rewind tempory stream resource');
|
||||
}
|
||||
|
||||
return $stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create stream resource from given file path.
|
||||
*
|
||||
* @throws StreamException
|
||||
*/
|
||||
private static function streamFromFilePath(string $path): mixed
|
||||
{
|
||||
$stream = fopen($path, 'rb');
|
||||
|
||||
if ($stream === false) {
|
||||
throw new StreamException('Failed to create stream resource from path');
|
||||
}
|
||||
|
||||
return $stream;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user