Skip to content

Commit

Permalink
add fill
Browse files Browse the repository at this point in the history
  • Loading branch information
freekmurze committed Sep 5, 2023
1 parent f057b4f commit 8f23fe1
Show file tree
Hide file tree
Showing 20 changed files with 1,363 additions and 30 deletions.
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
"ext-gd": "*"
},
"require-dev": {
"pestphp/pest": "^1.0",
"pestphp/pest": "^2.0",
"pestphp/pest-plugin-drift": "^2.4",
"spatie/ray": "^1.37",
"symfony/var-dumper": "^4.0|^5.0|^6.0"
},
Expand Down
1 change: 1 addition & 0 deletions src/Actions/CalculateFitSizeAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public function execute(

return match ($fit) {
Fit::Contain => $size->resize($desiredWidth, $desiredHeight, [Constraint::PreserveAspectRatio]),
Fit::Fill => $size->resize($desiredWidth, $desiredHeight, [Constraint::PreserveAspectRatio, Constraint::Upsize]),
};
}
}
106 changes: 106 additions & 0 deletions src/Drivers/BaseColor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<?php

namespace Spatie\Image\Drivers;

use ImagickPixel;
use Spatie\Image\Exceptions\InvalidColor;

abstract class BaseColor
{
abstract public function initFromInteger(int $value);

abstract public function initFromArray(array $value);

abstract public function initFromString(string $value);

abstract public function initFromObject(ImagickPixel $value);

abstract public function initFromRgb(int $red, int $green, int $blue);

abstract public function initFromRgba(int $red, int $green, int $blue, float $alpha);

abstract public function getInt(): int;

abstract public function getHex(string $prefix): string;

abstract public function getArray(): array;

abstract public function getRgba(): string;

abstract public function differs(self $color, int $tolerance = 0): bool;

public function __construct(mixed $value = null)
{
$this->parse($value);
}

public function parse(mixed $colorValue): self
{
match (true) {
is_string($colorValue) => $this->initFromString($colorValue),
is_int($colorValue) => $this->initFromInteger($colorValue),
is_array($colorValue) => $this->initFromArray($colorValue),
is_object($colorValue) => $this->initFromObject($colorValue),
is_null($colorValue) => $this->initFromArray([255, 255, 255, 0]),
default => throw InvalidColor::make($colorValue),
};

return $this;
}

/**
* Formats current color instance into given format
*
* @param string $type
*
* @return mixed
*/
public function format(ColorFormat $colorFormat): mixed
{
return match ($colorFormat) {
ColorFormat::RGBA => $this->getRgba(),
ColorFormat::HEX => $this->getHex('#'),
ColorFormat::INT => $this->getInt(),
ColorFormat::ARRAY => $this->getArray(),
ColorFormat::OBJECT => $this,
};
}

protected function rgbaFromString(string $colorValue): array
{
$result = false;

// parse color string in hexidecimal format like #cccccc or cccccc or ccc
$hexPattern = '/^#?([a-f0-9]{1,2})([a-f0-9]{1,2})([a-f0-9]{1,2})$/i';

// parse color string in format rgb(140, 140, 140)
$rgbPattern = '/^rgb ?\(([0-9]{1,3}), ?([0-9]{1,3}), ?([0-9]{1,3})\)$/i';

// parse color string in format rgba(255, 0, 0, 0.5)
$rgbaPattern = '/^rgba ?\(([0-9]{1,3}), ?([0-9]{1,3}), ?([0-9]{1,3}), ?([0-9.]{1,4})\)$/i';

if (preg_match($hexPattern, $colorValue, $matches)) {
$result = [];
$result[0] = strlen($matches[1]) == '1' ? hexdec($matches[1] . $matches[1]) : hexdec($matches[1]);
$result[1] = strlen($matches[2]) == '1' ? hexdec($matches[2] . $matches[2]) : hexdec($matches[2]);
$result[2] = strlen($matches[3]) == '1' ? hexdec($matches[3] . $matches[3]) : hexdec($matches[3]);
$result[3] = 1;
} elseif (preg_match($rgbPattern, $colorValue, $matches)) {
$result = [];
$result[0] = ($matches[1] >= 0 && $matches[1] <= 255) ? intval($matches[1]) : 0;
$result[1] = ($matches[2] >= 0 && $matches[2] <= 255) ? intval($matches[2]) : 0;
$result[2] = ($matches[3] >= 0 && $matches[3] <= 255) ? intval($matches[3]) : 0;
$result[3] = 1;
} elseif (preg_match($rgbaPattern, $colorValue, $matches)) {
$result = [];
$result[0] = ($matches[1] >= 0 && $matches[1] <= 255) ? intval($matches[1]) : 0;
$result[1] = ($matches[2] >= 0 && $matches[2] <= 255) ? intval($matches[2]) : 0;
$result[2] = ($matches[3] >= 0 && $matches[3] <= 255) ? intval($matches[3]) : 0;
$result[3] = ($matches[4] >= 0 && $matches[4] <= 1) ? $matches[4] : 0;
} else {
throw InvalidColor::make($colorValue);
}

return $result;
}
}
12 changes: 12 additions & 0 deletions src/Drivers/ColorFormat.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

namespace Spatie\Image\Drivers;

enum ColorFormat: string
{
case RGBA = 'rgba';
case HEX = 'hex';
case INT = 'int';
case OBJECT = 'object';
case ARRAY = 'array';
}
147 changes: 147 additions & 0 deletions src/Drivers/GdColor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
<?php

namespace Spatie\Image\Drivers;


use ImagickPixel;
use Spatie\Image\Exceptions\InvalidColor;

class GdColor extends BaseColor
{
public int $red;
public int $green;
public int $blue;
public float $alpha;

public function initFromInteger(int $value): self
{
$this->alpha = ($value >> 24) & 0xFF;
$this->red = ($value >> 16) & 0xFF;
$this->green = ($value >> 8) & 0xFF;
$this->blue = $value & 0xFF;

return $this;
}

public function initFromArray(array $value): self
{
$value = array_values($value);

if (count($value) == 4) {

[$red, $green, $blue, $alpha] = $value;
$this->alpha = $this->alpha2gd($alpha);

} elseif (count($value) == 3) {

[$red, $green, $blue] = $value;
$this->alpha = 0;

}

$this->red = $red;
$this->green = $green;
$this->blue = $blue;

return $this;
}

public function initFromString(string $value): self
{
if ($color = $this->rgbaFromString($value)) {
$this->red = $color[0];
$this->green = $color[1];
$this->blue = $color[2];
$this->alpha = $this->alpha2gd($color[3]);
}

return $this;
}

public function initFromRgb(int $red, int $green, int $blue): self
{
$this->red = $red;
$this->green = $green;
$this->blue = $blue;
$this->alpha = 0;

return $this;
}

public function initFromRgba(int $red, int $green, int $blue, float $alpha = 1): self
{
$this->red = $red;
$this->green = $green;
$this->blue = $blue;
$this->alpha = $this->alpha2gd($alpha);

return $this;
}

public function initFromObject(ImagickPixel $value): never
{
throw InvalidColor::cannotConvertImagickColorToGd();
}

public function getInt(): int
{
return ($this->alpha << 24) + ($this->red << 16) + ($this->green << 8) + $this->blue;
}

public function getHex(string $prefix = ''): string
{
return sprintf('%s%02x%02x%02x', $prefix, $this->red, $this->green, $this->blue);
}


public function getArray(): array
{
return [
$this->red,
$this->green,
$this->blue,
round(1 - $this->alpha / 127, 2)
];
}

public function getRgba(): string
{
return sprintf('rgba(%d, %d, %d, %.2F)',
$this->red,
$this->green,
$this->blue,
round(1 - $this->alpha / 127, 2)
);
}

public function differs(BaseColor $color, int $tolerance = 0): bool
{
$color_tolerance = round($tolerance * 2.55);
$alpha_tolerance = round($tolerance * 1.27);

$delta = [
'r' => abs($color->red - $this->red),
'g' => abs($color->green - $this->green),
'b' => abs($color->blue - $this->blue),
'a' => abs($color->alpha - $this->alpha)
];

return (
$delta['r'] > $color_tolerance ||
$delta['g'] > $color_tolerance ||
$delta['b'] > $color_tolerance ||
$delta['a'] > $alpha_tolerance
);
}

private function alpha2gd(float $input): int
{
$oldMin = 0;
$oldMax = 1;

$newMin = 127;
$newMax = 0;

return ceil(((($input- $oldMin) * ($newMax - $newMin)) / ($oldMax - $oldMin)) + $newMin);
}
}
Loading

0 comments on commit 8f23fe1

Please sign in to comment.