Prva verzija - gui interfejs

This commit is contained in:
Senad Uka
2020-06-07 09:02:34 +02:00
parent 6aa8dc863b
commit 2a091f1f8a
170 changed files with 15194 additions and 1 deletions

View File

@@ -1,3 +1,7 @@
# Uokvirivac
uokvirivac slika
Okvirovi idu u `okvirovi` folder.
Moraju biti uspravni sa transparentnom radnom povrsinom.
Ime mora biti dodano u `$okvirovi` a velicina mora biti dodana u `$radne_velicine`.

0
cache/.keep vendored Normal file
View File

5
composer.json Normal file
View File

@@ -0,0 +1,5 @@
{
"require": {
"imagine/imagine": "~1.2.3"
}
}

77
composer.lock generated Normal file
View File

@@ -0,0 +1,77 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "523332b5ac51b610cbd632ce713d9247",
"packages": [
{
"name": "imagine/imagine",
"version": "1.2.3",
"source": {
"type": "git",
"url": "https://github.com/avalanche123/Imagine.git",
"reference": "cb2361e5bb4410b681462d8e4f912bc5dabf84ab"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/avalanche123/Imagine/zipball/cb2361e5bb4410b681462d8e4f912bc5dabf84ab",
"reference": "cb2361e5bb4410b681462d8e4f912bc5dabf84ab",
"shasum": ""
},
"require": {
"php": ">=5.3.2"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "2.2.*",
"phpunit/phpunit": "^4.8 || ^5.7 || ^6.5 || ^7.5 || ^8.4"
},
"suggest": {
"ext-gd": "to use the GD implementation",
"ext-gmagick": "to use the Gmagick implementation",
"ext-imagick": "to use the Imagick implementation"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-develop": "0.7-dev"
}
},
"autoload": {
"psr-4": {
"Imagine\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Bulat Shakirzyanov",
"email": "mallluhuct@gmail.com",
"homepage": "http://avalanche123.com"
}
],
"description": "Image processing for PHP 5.3",
"homepage": "http://imagine.readthedocs.org/",
"keywords": [
"drawing",
"graphics",
"image manipulation",
"image processing"
],
"time": "2019-12-04T09:55:33+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": [],
"plugin-api-version": "1.1.0"
}

BIN
composer.phar Executable file

Binary file not shown.

67
image.php Normal file
View File

@@ -0,0 +1,67 @@
<?php
require __DIR__ . '/vendor/autoload.php';
require __DIR__ . '/okvirovi.php';
$FOLDER_SLIKE= __DIR__ . '/slike/';
$okvir_id = $_GET['okvir'] * 1 ?? 0;
$okvir = $okvirovi[$okvir_id];
$slika = $_GET['slika'] ?? 'slika';
// samo propusti slova i brojeve za naziv
$slika = preg_replace('/[^A-Za-z0-9]+/', '', $slika);
$ekstenzija = $_GET['ekstenzija'] ?? 'jpg';
// samo propusti slova za ekstenziju
$ekstenzija = preg_replace('/[^A-Za-z]+/', '', $ekstenzija);
$ekstenzija = substr($ekstenzija,0, 4);
preg_match('/(\d+)[xX](\d+)cm/',$okvir,$rezultat);
$sirina = $rezultat[1] * 1;
$duzina = $rezultat[2] * 1;
$imagine = new Imagine\Gd\Imagine();
$okvir_image = $imagine->open(__DIR__ . '/okvirovi/' . $okvir);
$slika_image = $imagine->open(__DIR__ . '/slike/' . $slika . ".$ekstenzija");
$okvir_size = $okvir_image->getSize();
$unutrasnji_size = $radne_velicine[$okvir_id];
$slika_size = $slika_image->getSize();
$finalna_dimenzija = $okvir_size->getHeight();
// smanji sliku da moze stati u okvir
$slika_vodoravna = $slika_size->getWidth() > $slika_size->getHeight();
if ($slika_vodoravna) {
$okvir_image->rotate(90);
$okvir_size = $okvir_image->getSize();
$unutrasnji_size = rotiraj_za_90($unutrasnji_size);
}
$slika_size = $slika_size->scale( best_fit_odnos($unutrasnji_size, $slika_size) );
$slika_image = $slika_image->thumbnail($slika_size, \Imagine\Image\ImageInterface::THUMBNAIL_OUTBOUND );
$slika_size = $slika_image->getSize();
// napravi bijelo platno velicine okvira
//
$palette = $slika_image->palette();
$color = $palette->color('#FFF', 0);
$gotova_slika = $imagine->create($okvir_size, $color);
// zalijepi sliku na centar platna
$pocetak_s_lijeva = ($okvir_size->getWidth() - $slika_size->getWidth()) / 2;
$pocetak_odozgo = ($okvir_size->getHeight() - $slika_size->getHeight()) / 2;
$gotova_slika->paste($slika_image, new Imagine\Image\Point($pocetak_s_lijeva,$pocetak_odozgo));
// uokviri platno
$gotova_slika->paste($okvir_image, new Imagine\Image\Point(0,0));
// snimi i prikazi
// $gotova_slika->save(__DIR__ . "/cache/tip" . $okvir_id . "slika" . $slika . ".jpg")->show("jpg");
// koristiti CDN za kes

3
index.php Normal file
View File

@@ -0,0 +1,3 @@
<?php

66
okvirovi.php Normal file
View File

@@ -0,0 +1,66 @@
<?php
require __DIR__ . '/vendor/autoload.php';
$okvirovi = array (
0 => 'Okvir-za-slike-BERGUR-18x24cm-srebrna.png',
1 => 'Okvir-za-slike-CHAYKA-13x16cm-s-ružama.png',
2 => 'Okvir-za-slike-ERLAND-18x23cm-roza.png',
3 => 'Okvir-za-slike-OSCAR-13x18cm-bijela.png',
4 => 'Okvir-za-slike-OSCAR-13x18cm-crna.png',
5 => 'Okvir-za-slike-OSCAR-21x30cm-bijela.png',
6 => 'Okvir-za-slike-OSCAR-21x30cm-crna.png',
7 => 'Okvir-za-slike-OSCAR-30x40cm-bijela.png',
8 => 'Okvir-za-slike-OSCAR-30x40cm-crna.png',
9 => 'Okvir-za-slike-OSCAR-60x90cm-crna.png',
10 => 'Okvir-za-slike-TORD-13x18-cm-drvo.png',
11 => 'Okvir-za-slike-TORD-21x30cm-drvo.png',
12 => 'Okvir-za-slike-TORD-30x40cm-drvo.png',
13 => 'Okvir-za-slike-TORD-50x70cm-drvo.png',
14 => 'Okvir-za-slike-VAGN-13x18cm-sivi..png',
15 => 'Okvir-za-slike-VAGN-18x24cm-zeleni.png',
16 => 'Okvir-za-slike-VALTER-10x15cm-bijela.png',
17 => 'Okvir-za-slike-VALTER-10x15cm-crna.png',
18 => 'Okvir-za-slike-VALTER-13x18cm-bijela.png',
19 => 'Okvir-za-slike-VALTER-15x21cm-crna-SDP.png',
20 => 'Okvir-za-slike-VALTER-18x24cm-bijela.png',
21 => 'Okvir-za-slike-VALTER-30x40cm-bijela.png',
22 => 'Okvir-za-slike-VALTER-40x50cm-crna.png',
23 => 'Okvir-za-slike-VALTER-50x70cm-crna.png',
);
$radne_velicine = array(
0 => new Imagine\Image\Box(273, 362),
1 => new Imagine\Image\Box(241, 354),
2 => new Imagine\Image\Box(213, 317),
3 => new Imagine\Image\Box(291, 404),
4 => new Imagine\Image\Box(277, 389),
5 => new Imagine\Image\Box(293, 426),
6 => new Imagine\Image\Box(297, 427),
7 => new Imagine\Image\Box(328, 437),
8 => new Imagine\Image\Box(324, 440),
9 => new Imagine\Image\Box(280, 418),
10 => new Imagine\Image\Box(256, 362),
11 => new Imagine\Image\Box(297, 425),
12 => new Imagine\Image\Box(322, 430),
13 => new Imagine\Image\Box(318, 448),
14 => new Imagine\Image\Box(179, 247),
15 => new Imagine\Image\Box(213, 288),
16 => new Imagine\Image\Box(253, 387),
17 => new Imagine\Image\Box(259, 395),
18 => new Imagine\Image\Box(281, 402),
19 => new Imagine\Image\Box(328, 433),
20 => new Imagine\Image\Box(316, 423),
21 => new Imagine\Image\Box(341, 458),
22 => new Imagine\Image\Box(351, 439),
23 => new Imagine\Image\Box(320, 454),
);
function rotiraj_za_90($box) {
return new Imagine\Image\Box($box->getHeight(), $box->getWidth());
}
function best_fit_odnos($box1, $box2) {
$odnos_sirina = $box1->getWidth() / $box2->getWidth();
$odnos_duzina = $box1->getHeight() / $box2->getHeight();
return min($odnos_sirina, $odnos_duzina);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

0
slike/.keep Normal file
View File

44
util_radne_povrsine.php Normal file
View File

@@ -0,0 +1,44 @@
<?php
(PHP_SAPI !== 'cli' || isset($_SERVER['HTTP_USER_AGENT'])) && die('cli only');
require __DIR__ . '/vendor/autoload.php';
require __DIR__ . '/okvirovi.php';
/*
* pronadje prvi unutrasnji transparentni piksel za svaki okvir
* vrati listu boxova koji su aproksimacija unutrasnje transparentne
* povrsine okvira (gdje se stavlja slika)
* listu treba pasteovati u "okvirovi.php"
* probleme popraviti rucno mjerenjem u photoshopu / gimpu i upisivanjem u okvirovi.php
*/
$okvirid = 0;
// za svaki okvir
foreach ($okvirovi as $okvir) {
$imagine = new Imagine\Gd\Imagine();
$okvir_image = $imagine->open(__DIR__ . '/okvirovi/' . $okvir);
$okvir_size = $okvir_image->getSize();
$pronasao_transparentnost = false;
// za svaki piksel
for ($i = 2; $i < $okvir_size->getWidth() ; $i++) {
for ($j = 2; $j < $okvir_size->getHeight() ; $j++) {
// dobavi boju
$boja = $okvir_image->getColorAt(new Imagine\Image\Point($i, $j));
// provjeri jel transparentna
if($boja->getAlpha() < 90) {
$sirina = $okvir_size->getWidth() - (2 * $i);
$duzina = $okvir_size->getHeight() - (2 * $j);
// vrati velicinu povrsine
// pod pretpostavkom simetricnosti okvira
echo "$okvirid => new Imagine\Image\Box($sirina, $duzina),\n";
$pronasao_transparentnost = true;
break;
}
}
if ($pronasao_transparentnost) {
break;
}
}
$okvirid++;
}

7
vendor/autoload.php vendored Normal file
View File

@@ -0,0 +1,7 @@
<?php
// autoload.php @generated by Composer
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInitfc8948f56a15b1d34cbda7dcc8cc2710::getLoader();

445
vendor/composer/ClassLoader.php vendored Normal file
View File

@@ -0,0 +1,445 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Autoload;
/**
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component', __DIR__.'/component');
* $loader->add('Symfony', __DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see http://www.php-fig.org/psr/psr-0/
* @see http://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
// PSR-4
private $prefixLengthsPsr4 = array();
private $prefixDirsPsr4 = array();
private $fallbackDirsPsr4 = array();
// PSR-0
private $prefixesPsr0 = array();
private $fallbackDirsPsr0 = array();
private $useIncludePath = false;
private $classMap = array();
private $classMapAuthoritative = false;
private $missingClasses = array();
private $apcuPrefix;
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', $this->prefixesPsr0);
}
return array();
}
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
public function getClassMap()
{
return $this->classMap;
}
/**
* @param array $classMap Class to filename map
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*/
public function add($prefix, $paths, $prepend = false)
{
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
(array) $paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
(array) $paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
(array) $paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
(array) $paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
(array) $paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
(array) $paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 base directories
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
/**
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
*
* @param string|null $apcuPrefix
*/
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
}
/**
* The APCu prefix in use, or null if APCu caching is not enabled.
*
* @return string|null
*/
public function getApcuPrefix()
{
return $this->apcuPrefix;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
}
/**
* Unregisters this instance as an autoloader.
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return bool|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
return true;
}
}
/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
return false;
}
if (null !== $this->apcuPrefix) {
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
if ($hit) {
return $file;
}
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if (false === $file && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if (null !== $this->apcuPrefix) {
apcu_add($this->apcuPrefix.$class, $file);
}
if (false === $file) {
// Remember that this class does not exist.
$this->missingClasses[$class] = true;
}
return $file;
}
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
$subPath = $class;
while (false !== $lastPos = strrpos($subPath, '\\')) {
$subPath = substr($subPath, 0, $lastPos);
$search = $subPath . '\\';
if (isset($this->prefixDirsPsr4[$search])) {
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
foreach ($this->prefixDirsPsr4[$search] as $dir) {
if (file_exists($file = $dir . $pathEnd)) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
// PSR-0 include paths.
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
return false;
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*/
function includeFile($file)
{
include $file;
}

21
vendor/composer/LICENSE vendored Normal file
View File

@@ -0,0 +1,21 @@
Copyright (c) Nils Adermann, Jordi Boggiano
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

9
vendor/composer/autoload_classmap.php vendored Normal file
View File

@@ -0,0 +1,9 @@
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);

View File

@@ -0,0 +1,9 @@
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);

10
vendor/composer/autoload_psr4.php vendored Normal file
View File

@@ -0,0 +1,10 @@
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Imagine\\' => array($vendorDir . '/imagine/imagine/src'),
);

55
vendor/composer/autoload_real.php vendored Normal file
View File

@@ -0,0 +1,55 @@
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInitfc8948f56a15b1d34cbda7dcc8cc2710
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
/**
* @return \Composer\Autoload\ClassLoader
*/
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInitfc8948f56a15b1d34cbda7dcc8cc2710', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInitfc8948f56a15b1d34cbda7dcc8cc2710', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require_once __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInitfc8948f56a15b1d34cbda7dcc8cc2710::getInitializer($loader));
} else {
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
}
$loader->register(true);
return $loader;
}
}

31
vendor/composer/autoload_static.php vendored Normal file
View File

@@ -0,0 +1,31 @@
<?php
// autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInitfc8948f56a15b1d34cbda7dcc8cc2710
{
public static $prefixLengthsPsr4 = array (
'I' =>
array (
'Imagine\\' => 8,
),
);
public static $prefixDirsPsr4 = array (
'Imagine\\' =>
array (
0 => __DIR__ . '/..' . '/imagine/imagine/src',
),
);
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInitfc8948f56a15b1d34cbda7dcc8cc2710::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInitfc8948f56a15b1d34cbda7dcc8cc2710::$prefixDirsPsr4;
}, null, ClassLoader::class);
}
}

62
vendor/composer/installed.json vendored Normal file
View File

@@ -0,0 +1,62 @@
[
{
"name": "imagine/imagine",
"version": "1.2.3",
"version_normalized": "1.2.3.0",
"source": {
"type": "git",
"url": "https://github.com/avalanche123/Imagine.git",
"reference": "cb2361e5bb4410b681462d8e4f912bc5dabf84ab"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/avalanche123/Imagine/zipball/cb2361e5bb4410b681462d8e4f912bc5dabf84ab",
"reference": "cb2361e5bb4410b681462d8e4f912bc5dabf84ab",
"shasum": ""
},
"require": {
"php": ">=5.3.2"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "2.2.*",
"phpunit/phpunit": "^4.8 || ^5.7 || ^6.5 || ^7.5 || ^8.4"
},
"suggest": {
"ext-gd": "to use the GD implementation",
"ext-gmagick": "to use the Gmagick implementation",
"ext-imagick": "to use the Imagick implementation"
},
"time": "2019-12-04T09:55:33+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-develop": "0.7-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Imagine\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Bulat Shakirzyanov",
"email": "mallluhuct@gmail.com",
"homepage": "http://avalanche123.com"
}
],
"description": "Image processing for PHP 5.3",
"homepage": "http://imagine.readthedocs.org/",
"keywords": [
"drawing",
"graphics",
"image manipulation",
"image processing"
]
}
]

253
vendor/imagine/imagine/CHANGELOG.md vendored Normal file
View File

@@ -0,0 +1,253 @@
# CHANGELOG
### 1.2.3 (2019-12-04)
* Handle jfif extension in GD driver (#727, @sylvain-msl-talkspirit)
* Improve detection of unsupported Exit Metadata Reader (#729, @mlocati, @ausi)
### 1.2.2 (2019-07-09)
* The GD driver can now load WebP files (#711, #718, @lashus, @ausi)
* Avoid calling `imageantialias` if it's not available (#713, @ahukkanen)
### 1.2.1 (2019-06-03)
* Silence call to `\Imagick::setImageOpacity()` in order to prevent deprecation error with Imagick 3.4.4 and ImageMagick 6 (#715, @samdark, @mlocati)
### 1.2.0 (2018-12-07)
* `ExifMetadataReader` now returns all the available metadata, not only EXIF and IFD0 (#701, @mlocati)
### 1.1.0 (2018-10-25)
* New `ImageInterface::THUMBNAIL_FLAG_NOCLONE` flag for `thumbnail()` to let it modify the original image instance in order to save memory (@mlocati)
### 1.0.2 (2018-10-24)
* Check that the Imagick PHP extension is not compiled using ImageMagick version 7.0.7-32 because it does not work correctly (@mlocati)
### 1.0.1 (2018-09-27)
* `Box` now rounds the width/height it receives (previously it discarded the decimal points) (@mlocati)
### 1.0.0 (2018-09-25)
* New `FontInterface` method: `wrapText` - split a text into multiple lines, so that it fits a specific width (@mlocati)
**BREAKING CHANGE** if you have your own `FontInterface` implementation, it now must implement `wrapText`
* Drawer methods can now accept a thickness of zero (@mlocati)
* Fix drawing unfilled chords with GD driver (@mlocati)
* Fix thickness drawing of unfilled chords with Imagick and Gmagick drivers (@mlocati)
* Fix handling of radius in `circle` method implementations (@mlocati)
* The `dissolve` method of `ColorInterface` normalizes the final value of alpha (@mlocati)
**BREAKING CHANGE** `dissolve` doesn't throw a `Imagine\Exception\InvalidArgumentException` anymore
### 1.0.0-alpha2 (2018-09-08)
* The `coalesce` method of `LayerInterface` instances now returns the LayerInterface itself (@mlocati)
**BREAKING CHANGE** if you have your own `LayerInterface` implementation, it now must return `$this`
* The `__toString` method has been added to `ColorInterface` since all its implementations have it (@mlocati)
**BREAKING CHANGE** if you have your own `ColorInterface` implementation, it now must implement `__toString`
* New Imagick save option: `optimize` if set, the size of animated GIF files is optimized (@mlocati)
**NOTE** Imagick requires that the image frames have the same size
* The `paste` method now accepts images not fully included in the destination image (@mlocati)
**BREAKING CHANGE** the paste method doesn't throw an OutOfBoundsException anymore
* Fix handling of PNG compression in Imagick `save` method (@mlocati)
* New drawer methods: `rectangle` and `circle` (@mlocati)
**BREAKING CHANGE** if you have your own implementation of `DrawerInterface` you should add these two new methods
* The `getChannelsMaxValue` method has been added to `PaletteInterface` (@mlocati)
**BREAKING CHANGE** if you have your own `PaletteInterface` implementation, it now must implement this new method
### 1.0.0-alpha1 (2018-08-28)
* Imagine is now tested under Windows too (@mlocati)
* Add support to webp image format (@chregu, @antoligy, @alexander-schranz)
* Add `Imagine\File\LoaderInterface` that allows loading remote images with any imaging driver (@mlocati).
You can use your own `LoaderInterface` implementation so that you can for instance use curl or any other library.
* Fix some phpdoc issues (@mlocati)
* `flipHorizontally` and `flipVertically` methods of GD images is now much faster on PHP 5.5+ (@mlocati)
* Fix loading of PNG indexed images with GD (@mlocati)
* Loading indexed images with GD is now much faster on PHP 5.5+ (@mlocati)
* Add support to grayscale images with Gmagick (@mlocati)
* Add support to alpha channels of Gmagick images (@mlocati)
* Fix `getColorAt` method of Gmagick images (@mlocati)
* Add `getTransformations` to the `Autorotate` filter, so that you can get the list of transformations that should be applied to an image accordingly to the EXIF metadata (@mlocati)
* The metadata reader now doesn't throw exceptions or warnings (@lentex, @mlocati)
* Fix documentation (@ZhangChaoWN, @Mark-H, @mlocati)
* Fix pixel range issue with Gmagick image (@b-viguier)
* Fix `text` drawer method on Windows when using relative font file paths (@mlocati)
* Fix `box` font method on Windows when using relative font file paths (@mlocati)
* Fix crash on Windows when loading an image with Imagick (@mlocati)
* Fix generation of API documentation (@mlocati)
* Add `jpeg_sampling_factors` option when saving JPEG images (Gmagick/Imagick only) (@ausi)
* Add BMP as supported image format (@mlocati)
* Add support to new image type constants of Imagick (@ausi)
* Check that Imagick correctly supports profiles (@ausi)
* Add `setMetadataReader`/`getMetadataReader` to `ImagineInterface` (@mlocati)
**BREAKING CHANGE** if you have your own `ImagineInterface` implementation, it now must implement those two methods
* Fix creating Gmagick images with alpha colors when palette doesn't support alpha (@FractalizeR)
* Fix warning about deprecated clone method in copy method of Imagick images (@mlocati)
* Fix copy methods of Images (the original image and its new copy are now fully detached) (@mlocati)
* It's now possible to use `clone $image` as an alternative to `$image->copy()` (@mlocati)
* Add support to custom classes for `BoxInterface`, `MetadataReaderInterface`, `FontInterface`, `LoaderInterface`, `LayersInterface`, `ImageInterface` (@mlocati)
**BREAKING CHANGE** if you have your own `ImagineInterface` implementation, it now must implement the methods of `ClassFactoryAwareInterface`
* Add support for pasting with alpha for GD and Imagick (@AlloVince, @mlocati)
* Downscaling a `Box` until it reaches a dimension less than 1 returns a box with dimension of 1 instead of throwing an exception (@mlocati)
**BREAKING CHANGE** if you relied on `Box::scale` throwing an exception in this case
* New filters: `BlackWhite`, `BorderDetection`, `Negation`, `Neighborhood` (@rejinka)
* Minor optimization of filters based on `OnPixelBased` (@rejinka, @mlocati)
* Add flag to `thumbnail` to allow upscaling images (@vlakoff)
**BREAKING CHANGE** the `$mode` argument has been renamed to `$settings`, and it's now an integer (but old string values are accepted for backward compatibility). In this case the `ManipulatorInterface` constants `THUMBNAIL_INSET`, `THUMBNAIL_OUTBOUND` were changed from string values to integers.
* New filter: `brightness` (@lenybernard, @mlocati)
* New filter: `colvolve` available for all graphics libraries except gmagick with version prior to 2.0.1RC2 (@armatronic, @mlocati)
* Fix bug in Imagine\Image\Palette\RGB::blend() (@dmolineus, @mlocati)
* Autoload was moved from PSR-0 to PSR-4, and code files moved from `/lib/Imagine` to `/src` (@mlocati)
### 0.7.1 (2017-05-16)
* Remove Symfony PHPUnit bridge as dependency (@craue)
### 0.7.0 (2017-05-02)
* Fix memory usage on metadata reading (@Seldaek)
* PHP 7.1 support
* Latest Imagemagick compatibility (@jdewit)
### 0.6.3 (2015-09-19)
* Fix wrong array_merge when calling Transformation::getFilters without filters
* Add export-ignore git attribute (@Benoth)
* Fix docblocks (@Sm0ke0ut and @norkunas)
* Fix animated gif loop length options (@jygaulier)
* Multiple tweaks for the repository and travis builds (@localheinz, @vrkansagara and @dunzun)
* Fix metadata extraction from streams (@armatronic)
* Fix autorotation (@tarleb)
* Load exifmetadata reader whenever possible
* Add metadata getter
### 0.6.2 (2014-11-11)
* Stripping image containing an invalid ICC profile fails
* MetadataBag now implements \Countable
* Fix wrong array_merge in MetadataBag giving invalid results with HTTP resources (@javaguirre)
* Fix Imagick merge strategy (@GrahamCampbell)
* Fixed various alpha issues (@RadekDvorak)
* Fix Image cloning on HHVM (@RdeWilde)
* Fix exception on invalid file using GD driver (@vlakoff).
* Fix ImageInterface::getSize on animated GIFs (@sokac)
### 0.6.1 (2014-06-16)
* Fix invalid namespace usage (#336 @csanquer).
### 0.6.0 (2014-06-13)
* BC break: Colors are now provided through the PaletteInterface. Any call
to previous Imagine\Image\Color constructor must be removed and use the
palette provided by Imagine\Image\ImageInterface::getPalette to create
colors.
* BC break : Animated GIF default delay is no longer 800ms but null. This
avoids resettings a delay on animated image.
* Add support for ICC profiles
* Add support for CMYK and grayscale colorspace images.
* Add filter argument to ImageInterface::thumbnail method.
* Add priority to filters (@Richtermeister).
* Add blur effect (@Nokrosis).
* Rename "quality" option to "jpeg_quality" and apply it only to JPEG files (@vlakoff).
* Add "png_compression_level" option (@vlakoff).
* Rename "filters" option to "png_compression_filter" (@vlakoff).
* Deprecate `quality` and `filters` ManipulatorInterface::save options, use
`jpeg_quality`, `png_compression_level` and `png_compression_filter` instead.
* Add support for alpha blending in GD drawer (@salem).
* Add width parameter to Drawer::text (@salemgolemugoo).
* Add NotSupportedException when a driver does not support an operation (@rouffj).
* Add support for metadata.
* Fix #158: GD alpha detection + Color::isOpaque are broken.
* Fix color extraction for non-RGB palettes.
### 0.5.0 (2013-07-10)
* Add `Layers::coalesce`.
* Add filter option to `ImageInterface::resize`.
* Add sharpen effect.
* Add interlace support.
* `LayersInterface` now extends `ArrayAccess`, gives support for animated gifs.
* Remove Imagick and Gmagick flatten after composite.
* Fix pixel opacity reading in `Gmagick::histogram`.
* Deprecate pear channel installation.
* Deprecate phar installation.
### 0.4.1 (2012-12-13)
* Lazy-load GD layers.
### 0.4.0 (2012-12-10)
* Add support for image Layers.
* Add Colorize effect.
* Add documentation for the Grayscale effect.
* Port RelativeResize filter from JmikolaImagineBundle.
### 0.3.1 (2012-11-12)
* Add Grayscale effect.
* `Drawer::text` position fix.
### 0.3.0 (2012-07-28)
* Add configurable border thickness to drawer interface and implementations.
* Add `ImageInterface`::strip.
* Add Canvas filter.
* Add resolution option on image saving.
* Add Grayscale filter.
* Add sami API documentation.
* Add compression quality to Gmagick.
* Add effects API.
* Add method to get pixel at point in Gmagick.
* Ensure valid background color in rotations.
* Fill lines with color to prevent semi-transparency issues.
* Use `Imagick::resizeImage` instead of `Imagick::thumbnailImage` for resizing.
* Fix PNG transparency on save ; do not flatten if not necessary.
### 0.2.8 (2011-11-29)
* Add support for Travis CI.
### 0.2.7 (2011-11-17)
* Use composer for autoloading.
### 0.2.6 (2011-11-09)
* Documentation enhancements.
### 0.2.5 (2011-10-29)
* Add PEAR support.
* Documentation enhancements.
### 0.2.4 (2011-10-17)
* Add imagine.phar, phar and rake tasks.
* Add `ImagineInterface::read` to read from a stream resource.
* Documentation enhancements.
* Fix gifs transparency issues.
### 0.2.3 (2011-10-16)
* Documentation enhancements.
### 0.2.2 (2011-10-16)
* Documentation enhancements.
### 0.2.1 (2011-10-15)
* Add `PointInterface::move`.
* `BoxInterface::scale` can accept floats.
* Set antialias mode for GD images.
* Fix png compression.
### 0.2.0 (2011-10-06)
* Add `Imagine\Fill\Gradient\Linear::getStart`/`getEnd`.
* Add `Imagine\Image\Color::isOpaque`.
* Add Gmagick transparency exceptions.
* Add support for transparency for gif export.
* Add widen/heighten methods for easy scaling to target width/height.
* Add functionals tests to unit test thumbnails creation.
* Add the ability to use hexadecimal notation for `Imagine\Image\Color` construction.
* Implement fast linear gradient for Imagick.
* Remove lengthy image histogram comparisons.
* Extract `ManipulatorInterface` from `ImageInterface`.
* Switch methods to final.
* New method `ImageInterface::getColorAt`.
* Introduce `ImagineAware` abstract filter class.
### 0.1.5 (2011-05-18)
* Fix bug in GD rotate.
### 0.1.4 (2011-03-21)
* Add environment check to gracefuly skip test.
### 0.1.3 (2011-03-21)
* Improve api docs.
* Extract `FontInterface`.
### 0.1.2 (2011-03-21)
* Add check for GD.
### 0.1.1 (2011-03-21)
* Add rounding and fixed thumbnail logic.
### 0.1.0 (2011-03-14)
* First tagged version.

25
vendor/imagine/imagine/LICENSE vendored Normal file
View File

@@ -0,0 +1,25 @@
Copyright (c) 2004-2012 Bulat Shakirzyanov
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
This software embeds Adobe ICC Profiles, see license at
http://www.adobe.com/support/downloads/iccprofiles/icc_eula_mac_dist.html .
This software also embeds ICC Profile from colormanagement.org. Please
find information about their license at http://colormanagement.org/ .

58
vendor/imagine/imagine/README.md vendored Normal file
View File

@@ -0,0 +1,58 @@
# Imagine
[![Travis CI Build Status](https://travis-ci.org/avalanche123/Imagine.svg?branch=develop)](https://travis-ci.org/avalanche123/Imagine) [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/avalanche123/Imagine?branch=develop&svg=true)](https://ci.appveyor.com/project/avalanche123/Imagine)
Tweet about it using the [#php_imagine](https://twitter.com/search?q=%23php_imagine) hashtag.
Image manipulation library for PHP 5.3 inspired by Python's PIL and other image
libraries.
## Requirements
The Imagine library has the following requirements:
- PHP 5.3+
Depending on the chosen Image implementation, you may need one of the following PHP extensions:
- GD2
- Imagick (with ImageMagick version 6.2.9 or later, except version 7.0.7-32)
- Gmagick
### Installation using composer
`php composer.phar require imagine/imagine`
## Basic Principles
The main purpose of Imagine is to provide all the necessary functionality to bring all native low level image processing libraries in PHP to the same simple and intuitive OO API.
Several things are necessary to accomplish that:
* Image manipulation tools, such as resize, crop, etc.
* Drawing API - to create basic shapes and advanced charts, write text on the image
* Masking functionality - ability to apply black&white or grayscale images as masks, leading to semi-transparency or absolute transparency of the image the mask is being applied to
The above tools should be the basic foundation for a more powerful set of tools that are called ``Filters`` in Imagine.
Some of the ideas for upcoming filters:
* Charting and graphing filters - pie and bar charts, linear graphs with annotations
* Reflection - apple style
* Rounded corners - web 2.0
## Documentation ##
- [Hosted by Read The Docs](http://imagine.readthedocs.org/)
## Presentations ##
- [Introduction to Imagine](http://www.slideshare.net/avalanche123/introduction-toimagine)
- [How to Take Over the World with Lithium](http://speakerdeck.com/u/nateabele/p/how-to-take-over-the-world-with-lithium?slide=33)
## Articles ##
- [Image Processing with Imagine](http://www.phparch.com/2011/03/image-processing-with-imagine)
## Contributing ##
New pull requests should be based on the `develop` branch.
The `master` branch is the stable branch: it usually matches the latest a release but in can be a bit ahead.

62
vendor/imagine/imagine/composer.json vendored Normal file
View File

@@ -0,0 +1,62 @@
{
"name": "imagine/imagine",
"description": "Image processing for PHP 5.3",
"keywords": [
"image manipulation",
"image processing",
"drawing",
"graphics"
],
"homepage": "http://imagine.readthedocs.org/",
"license": "MIT",
"authors": [
{
"name": "Bulat Shakirzyanov",
"email": "mallluhuct@gmail.com",
"homepage": "http://avalanche123.com"
}
],
"require": {
"php": ">=5.3.2"
},
"require-dev": {
"phpunit/phpunit": "^4.8 || ^5.7 || ^6.5 || ^7.5 || ^8.4",
"friendsofphp/php-cs-fixer": "2.2.*"
},
"suggest": {
"ext-gd": "to use the GD implementation",
"ext-imagick": "to use the Imagick implementation",
"ext-gmagick": "to use the Gmagick implementation"
},
"autoload": {
"psr-4": {
"Imagine\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Imagine\\Test\\": "tests/tests/"
}
},
"extra": {
"branch-alias": {
"dev-develop": "0.7-dev"
}
},
"archive": {
"exclude": [
"/.*",
"/tests",
"/vendor",
"/bin",
"docs/_build",
"Imagine-*.tgz",
"imagine-*.phar",
"composer.phar"
]
},
"scripts": {
"test": "phpunit --verbose",
"codestyle": "php-cs-fixer fix --path-mode=intersection --config=.php_cs.dist"
}
}

View File

@@ -0,0 +1,175 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Draw;
use Imagine\Image\AbstractFont;
use Imagine\Image\BoxInterface;
use Imagine\Image\Palette\Color\ColorInterface;
use Imagine\Image\PointInterface;
/**
* Interface for the drawer.
*/
interface DrawerInterface
{
/**
* Draws an arc on a starting at a given x, y coordinates under a given
* start and end angles.
*
* @param \Imagine\Image\PointInterface $center
* @param \Imagine\Image\BoxInterface $size
* @param int $start
* @param int $end
* @param \Imagine\Image\Palette\Color\ColorInterface $color
* @param int $thickness
*
* @throws \Imagine\Exception\RuntimeException
*
* @return $this
*/
public function arc(PointInterface $center, BoxInterface $size, $start, $end, ColorInterface $color, $thickness = 1);
/**
* Same as arc, but also connects end points with a straight line.
*
* @param \Imagine\Image\PointInterface $center
* @param \Imagine\Image\BoxInterface $size
* @param int $start
* @param int $end
* @param \Imagine\Image\Palette\Color\ColorInterface $color
* @param bool $fill
* @param int $thickness
*
* @throws \Imagine\Exception\RuntimeException
*
* @return $this
*/
public function chord(PointInterface $center, BoxInterface $size, $start, $end, ColorInterface $color, $fill = false, $thickness = 1);
/**
* Draws and circle with center at the given x, y coordinates, and given radius.
*
* @param \Imagine\Image\PointInterface $center
* @param int $radius
* @param \Imagine\Image\Palette\Color\ColorInterface $color
* @param bool $fill
* @param int $thickness
*
* @throws \Imagine\Exception\RuntimeException
*
* @return $this
*/
public function circle(PointInterface $center, $radius, ColorInterface $color, $fill = false, $thickness = 1);
/**
* Draws and ellipse with center at the given x, y coordinates, and given width and height.
*
* @param \Imagine\Image\PointInterface $center
* @param \Imagine\Image\BoxInterface $size
* @param \Imagine\Image\Palette\Color\ColorInterface $color
* @param bool $fill
* @param int $thickness
*
* @throws \Imagine\Exception\RuntimeException
*
* @return $this
*/
public function ellipse(PointInterface $center, BoxInterface $size, ColorInterface $color, $fill = false, $thickness = 1);
/**
* Draws a line from start(x, y) to end(x, y) coordinates.
*
* @param \Imagine\Image\PointInterface $start
* @param \Imagine\Image\PointInterface $end
* @param \Imagine\Image\Palette\Color\ColorInterface $outline
* @param int $thickness
*
* @return $this
*/
public function line(PointInterface $start, PointInterface $end, ColorInterface $outline, $thickness = 1);
/**
* Same as arc, but connects end points and the center.
*
* @param \Imagine\Image\PointInterface $center
* @param \Imagine\Image\BoxInterface $size
* @param int $start
* @param int $end
* @param \Imagine\Image\Palette\Color\ColorInterface $color
* @param bool $fill
* @param int $thickness
*
* @throws \Imagine\Exception\RuntimeException
*
* @return $this
*/
public function pieSlice(PointInterface $center, BoxInterface $size, $start, $end, ColorInterface $color, $fill = false, $thickness = 1);
/**
* Places a one pixel point at specific coordinates and fills it with
* specified color.
*
* @param \Imagine\Image\PointInterface $position
* @param \Imagine\Image\Palette\Color\ColorInterface $color
*
* @throws \Imagine\Exception\RuntimeException
*
* @return $this
*/
public function dot(PointInterface $position, ColorInterface $color);
/**
* Draws a rectangle from left, top(x, y) to right, bottom(x, y) coordinates.
*
* @param \Imagine\Image\PointInterface $leftTop
* @param \Imagine\Image\PointInterface $rightBottom
* @param \Imagine\Image\Palette\Color\ColorInterface $color
* @param bool $fill
* @param int $thickness
*
* @throws \Imagine\Exception\RuntimeException
*
* @return $this
*/
public function rectangle(PointInterface $leftTop, PointInterface $rightBottom, ColorInterface $color, $fill = false, $thickness = 1);
/**
* Draws a polygon using array of x, y coordinates. Must contain at least three coordinates.
*
* @param \Imagine\Image\PointInterface[] $coordinates
* @param \Imagine\Image\Palette\Color\ColorInterface $color
* @param bool $fill
* @param int $thickness
*
* @throws \Imagine\Exception\RuntimeException
*
* @return $this
*/
public function polygon(array $coordinates, ColorInterface $color, $fill = false, $thickness = 1);
/**
* Annotates image with specified text at a given position starting on the top left of the final text box.
*
* The rotation is done CW
*
* @param string $string
* @param \Imagine\Image\AbstractFont $font
* @param \Imagine\Image\PointInterface $position
* @param int $angle
* @param int $width
*
* @throws \Imagine\Exception\RuntimeException
*
* @return $this
*/
public function text($string, AbstractFont $font, PointInterface $position, $angle = 0, $width = null);
}

View File

@@ -0,0 +1,103 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Effects;
use Imagine\Image\Palette\Color\ColorInterface;
use Imagine\Utils\Matrix;
/**
* Interface for the effects.
*/
interface EffectsInterface
{
/**
* Apply gamma correction.
*
* @param float $correction
*
* @throws \Imagine\Exception\RuntimeException
*
* @return $this
*/
public function gamma($correction);
/**
* Invert the colors of the image.
*
* @throws \Imagine\Exception\RuntimeException
*
* @return $this
*/
public function negative();
/**
* Grayscale the image.
*
* @throws \Imagine\Exception\RuntimeException
*
* @return $this
*/
public function grayscale();
/**
* Colorize the image.
*
* @param \Imagine\Image\Palette\Color\ColorInterface $color
*
* @throws \Imagine\Exception\RuntimeException
*
* @return $this
*/
public function colorize(ColorInterface $color);
/**
* Sharpens the image.
*
* @throws \Imagine\Exception\RuntimeException
*
* @return $this
*/
public function sharpen();
/**
* Blur the image.
*
* @param float|int $sigma
*
* @throws \Imagine\Exception\RuntimeException
*
* @return $this
*/
public function blur($sigma);
/**
* Changes the brightness of the image.
*
* @param int $brightness The level of brightness (-100 (black) to 100 (white))
*
* @throws \Imagine\Exception\RuntimeException
*
* @return $this
*/
public function brightness($brightness);
/**
* Convolves the image.
*
* @param \Imagine\Utils\Matrix $matrix The matrix from which derive the convolution kernel
*
* @throws \Imagine\Exception\RuntimeException
*
* @return $this
*/
public function convolve(Matrix $matrix);
}

View File

@@ -0,0 +1,19 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Exception;
/**
* Imagine-specific exception.
*/
interface Exception
{
}

View File

@@ -0,0 +1,19 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Exception;
/**
* Imagine-specific invalid argument exception.
*/
class InvalidArgumentException extends \InvalidArgumentException implements Exception
{
}

View File

@@ -0,0 +1,19 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Exception;
/**
* Should be used when a driver does not support an operation.
*/
class NotSupportedException extends RuntimeException implements Exception
{
}

View File

@@ -0,0 +1,19 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Exception;
/**
* Imagine-specific out of bounds exception.
*/
class OutOfBoundsException extends \OutOfBoundsException implements Exception
{
}

View File

@@ -0,0 +1,19 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Exception;
/**
* Imagine-specific runtime exception.
*/
class RuntimeException extends \RuntimeException implements Exception
{
}

View File

@@ -0,0 +1,189 @@
<?php
namespace Imagine\Factory;
use Imagine\Exception\InvalidArgumentException;
use Imagine\Exception\RuntimeException;
use Imagine\File\Loader;
use Imagine\Image\Box;
use Imagine\Image\ImageInterface;
use Imagine\Image\Metadata\DefaultMetadataReader;
use Imagine\Image\Metadata\ExifMetadataReader;
use Imagine\Image\Metadata\MetadataBag;
use Imagine\Image\Palette\Color\ColorInterface;
use Imagine\Image\Palette\PaletteInterface;
/**
* The default implementation of Imagine\Factory\ClassFactoryInterface.
*/
class ClassFactory implements ClassFactoryInterface
{
/**
* @var array|null
*/
private static $gdInfo;
/**
* {@inheritdoc}
*
* @see \Imagine\Factory\ClassFactoryInterface::createMetadataReader()
*/
public function createMetadataReader()
{
return $this->finalize(ExifMetadataReader::isSupported() ? new ExifMetadataReader() : new DefaultMetadataReader());
}
/**
* {@inheritdoc}
*
* @see \Imagine\Factory\ClassFactoryInterface::createBox()
*/
public function createBox($width, $height)
{
return $this->finalize(new Box($width, $height));
}
/**
* {@inheritdoc}
*
* @see \Imagine\Factory\ClassFactoryInterface::createFileLoader()
*/
public function createFileLoader($path)
{
return $this->finalize(new Loader($path));
}
/**
* {@inheritdoc}
*
* @see \Imagine\Factory\ClassFactoryInterface::createDrawer()
*/
public function createDrawer($handle, $resource)
{
switch ($handle) {
case self::HANDLE_GD:
return $this->finalize(new \Imagine\Gd\Drawer($resource));
case self::HANDLE_GMAGICK:
return $this->finalize(new \Imagine\Gmagick\Drawer($resource));
case self::HANDLE_IMAGICK:
return $this->finalize(new \Imagine\Imagick\Drawer($resource));
default:
throw new InvalidArgumentException(sprintf('Unrecognized handle %s in %s', $handle, __FUNCTION__));
}
}
/**
* {@inheritdoc}
*
* @see \Imagine\Factory\ClassFactoryInterface::createLayers()
*/
public function createLayers($handle, ImageInterface $image, $initialKey = null)
{
switch ($handle) {
case self::HANDLE_GD:
return $this->finalize(new \Imagine\Gd\Layers($image, $image->palette(), $image->getGdResource(), (int) $initialKey));
case self::HANDLE_GMAGICK:
return $this->finalize(new \Imagine\Gmagick\Layers($image, $image->palette(), $image->getGmagick(), (int) $initialKey));
case self::HANDLE_IMAGICK:
return $this->finalize(new \Imagine\Imagick\Layers($image, $image->palette(), $image->getImagick(), (int) $initialKey));
default:
throw new InvalidArgumentException(sprintf('Unrecognized handle %s in %s', $handle, __FUNCTION__));
}
}
/**
* {@inheritdoc}
*
* @see \Imagine\Factory\ClassFactoryInterface::createEffects()
*/
public function createEffects($handle, $resource)
{
switch ($handle) {
case self::HANDLE_GD:
return $this->finalize(new \Imagine\Gd\Effects($resource));
case self::HANDLE_GMAGICK:
return $this->finalize(new \Imagine\Gmagick\Effects($resource));
case self::HANDLE_IMAGICK:
return $this->finalize(new \Imagine\Imagick\Effects($resource));
default:
throw new InvalidArgumentException(sprintf('Unrecognized handle %s in %s', $handle, __FUNCTION__));
}
}
/**
* {@inheritdoc}
*
* @see \Imagine\Factory\ClassFactoryInterface::createImage()
*/
public function createImage($handle, $resource, PaletteInterface $palette, MetadataBag $metadata)
{
switch ($handle) {
case self::HANDLE_GD:
return $this->finalize(new \Imagine\Gd\Image($resource, $palette, $metadata));
case self::HANDLE_GMAGICK:
return $this->finalize(new \Imagine\Gmagick\Image($resource, $palette, $metadata));
case self::HANDLE_IMAGICK:
return $this->finalize(new \Imagine\Imagick\Image($resource, $palette, $metadata));
default:
throw new InvalidArgumentException(sprintf('Unrecognized handle %s in %s', $handle, __FUNCTION__));
}
}
/**
* {@inheritdoc}
*
* @see \Imagine\Factory\ClassFactoryInterface::createFont()
*/
public function createFont($handle, $file, $size, ColorInterface $color)
{
switch ($handle) {
case self::HANDLE_GD:
$gdInfo = static::getGDInfo();
if (!$gdInfo['FreeType Support']) {
throw new RuntimeException('GD is not compiled with FreeType support');
}
return $this->finalize(new \Imagine\Gd\Font($file, $size, $color));
case self::HANDLE_GMAGICK:
$gmagick = new \Gmagick();
$gmagick->newimage(1, 1, 'transparent');
return $this->finalize(new \Imagine\Gmagick\Font($gmagick, $file, $size, $color));
case self::HANDLE_IMAGICK:
return $this->finalize(new \Imagine\Imagick\Font(new \Imagick(), $file, $size, $color));
default:
throw new InvalidArgumentException(sprintf('Unrecognized handle %s in %s', $handle, __FUNCTION__));
}
}
/**
* Finalize the newly created object.
*
* @param object $object
*
* @return object
*/
protected function finalize($object)
{
if ($object instanceof ClassFactoryAwareInterface) {
$object->setClassFactory($this);
}
return $object;
}
/**
* @return array
*/
protected static function getGDInfo()
{
if (self::$gdInfo === null) {
if (!function_exists('gd_info')) {
throw new RuntimeException('GD is not installed');
}
self::$gdInfo = gd_info();
}
return self::$gdInfo;
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace Imagine\Factory;
/**
* An interface that classes that accepts a class factory should implement.
*/
interface ClassFactoryAwareInterface
{
/**
* Set the class factory instance to be used.
*
* @param \Imagine\Factory\ClassFactoryInterface $classFactory
*
* @return $this
*/
public function setClassFactory(ClassFactoryInterface $classFactory);
/**
* Get the class factory instance to be used.
*
* @return \Imagine\Factory\ClassFactoryInterface
*/
public function getClassFactory();
}

View File

@@ -0,0 +1,113 @@
<?php
namespace Imagine\Factory;
use Imagine\Image\Palette\Color\ColorInterface;
/**
* The interface that class factories must implement.
*/
interface ClassFactoryInterface
{
/**
* The handle to be used for the GD manipulation library.
*
* @var string
*/
const HANDLE_GD = 'gd';
/**
* The handle to be used for the Gmagick manipulation library.
*
* @var string
*/
const HANDLE_GMAGICK = 'gmagick';
/**
* The handle to be used for the Imagick manipulation library.
*
* @var string
*/
const HANDLE_IMAGICK = 'imagick';
/**
* Create a new instance of a metadata reader.
*
* @return \Imagine\Image\Metadata\MetadataReaderInterface
*/
public function createMetadataReader();
/**
* Create new BoxInterface instance.
*
* @param int $width The box width
* @param int $height The box height
*
* @return \Imagine\Image\BoxInterface
*/
public function createBox($width, $height);
/**
* Create new FontInterface instance.
*
* @param string $handle The handle that identifies the manipulation library (one of the HANDLE_... constants, or your own implementation).
* @param string $file
* @param int $size the font size in points (e.g. 10pt means 10)
* @param \Imagine\Image\Palette\Color\ColorInterface $color
*
* @return \Imagine\Image\FontInterface
*/
public function createFont($handle, $file, $size, ColorInterface $color);
/**
* Create a new instance of a file loader.
*
* @param string|mixed $path
*
* @return \Imagine\File\LoaderInterface
*/
public function createFileLoader($path);
/**
* Crate a new instance of a layers interface.
*
* @param string $handle The handle that identifies the manipulation library (one of the HANDLE_... constants, or your own implementation).
* @param \Imagine\Image\ImageInterface $image
* @param mixed|null $initialKey the key of the initially selected layer
*
* @return \Imagine\Image\LayersInterface
*/
public function createLayers($handle, \Imagine\Image\ImageInterface $image, $initialKey = null);
/**
* Create a new ImageInterface instance.
*
* @param string $handle The handle that identifies the manipulation library (one of the HANDLE_... constants, or your own implementation).
* @param mixed $resource
* @param \Imagine\Image\Palette\PaletteInterface $palette
* @param \Imagine\Image\Metadata\MetadataBag $metadata
*
* @return \Imagine\Image\ImageInterface
*/
public function createImage($handle, $resource, \Imagine\Image\Palette\PaletteInterface $palette, \Imagine\Image\Metadata\MetadataBag $metadata);
/**
* Create a new DrawerInterface instance.
*
* @param string $handle The handle that identifies the manipulation library (one of the HANDLE_... constants, or your own implementation).
* @param mixed $resource
*
* @return \Imagine\Draw\DrawerInterface
*/
public function createDrawer($handle, $resource);
/**
* Create a new EffectsInterface instance.
*
* @param string $handle The handle that identifies the manipulation library (one of the HANDLE_... constants, or your own implementation).
* @param mixed $resource
*
* @return \Imagine\Effects\EffectsInterface
*/
public function createEffects($handle, $resource);
}

View File

@@ -0,0 +1,300 @@
<?php
/*
* This file is part of the Imagine package.
*
* For the full copyright and license information, please view the LICENSE file that was distributed with this source code.
*/
namespace Imagine\File;
use Imagine\Exception\InvalidArgumentException;
use Imagine\Exception\RuntimeException;
/**
* Default implementation of Imagine\File\LoaderInterface.
*/
class Loader implements LoaderInterface
{
/**
* The mimimum supported version of curl.
*
* @var string
*/
const MINIMUM_CURL_VERSION = '7.34.0';
/**
* The file path.
*
* @var string
*/
protected $path;
/**
* Does $path contain an URL?
*
* @var bool
*/
protected $isUrl;
/**
* The loaded data.
*
* @var string|null
*/
protected $data;
/**
* Is curl available, with a decent version?
*
* @var bool|null
*/
protected $isCurlSupported;
/**
* Initialize the instance.
*
* @param string|mixed $path the file path (or an object whose string representation is the file path)
*
* @throws \Imagine\Exception\InvalidArgumentException throws an InvalidArgumentException is $path is an empty string, or is not an object that has a __toString method
*/
public function __construct($path)
{
if (is_object($path) && !method_exists($path, '__toString')) {
throw new InvalidArgumentException(sprintf('$path is an object of file %s which does not implement the __toString method', get_class($path)));
}
$this->path = (string) $path;
if ($this->path === '') {
throw new InvalidArgumentException('$path is empty');
}
$this->isUrl = filter_var($this->path, FILTER_VALIDATE_URL) !== false;
if (!$this->isUrl) {
$this->checkLocalFile();
}
}
/**
* {@inheritdoc}
*
* @see \Imagine\File\LoaderInterface::isLocalFile()
*/
public function isLocalFile()
{
return !$this->isUrl;
}
/**
* {@inheritdoc}
*
* @see \Imagine\File\LoaderInterface::getPath()
*/
public function getPath()
{
return $this->path;
}
/**
* {@inheritdoc}
*
* @see \Imagine\File\LoaderInterface::hasReadData()
*/
public function hasReadData()
{
return $this->data !== null;
}
/**
* {@inheritdoc}
*
* @throws \Imagine\Exception\InvalidArgumentException
* @throws \Imagine\Exception\RuntimeException
*
* @see \Imagine\File\LoaderInterface::getData()
*/
public function getData()
{
if (!$this->hasReadData()) {
if ($this->isLocalFile()) {
$this->data = $this->readLocalFile();
} else {
$this->data = $this->readRemoteFile();
}
}
return $this->data;
}
/**
* {@inheritdoc}
*
* @see \Imagine\File\LoaderInterface::__toString()
*/
public function __toString()
{
return $this->getPath();
}
/**
* Read a local file.
*
* @throws \Imagine\Exception\InvalidArgumentException
*
* @return string
*/
protected function readLocalFile()
{
$this->checkLocalFile();
$data = @file_get_contents($this->path);
if ($data === false) {
throw new InvalidArgumentException(sprintf('Failed to read from file %s.', $this->path));
}
return $data;
}
/**
* Check that the file exists and it's readable.
*
* @throws \Imagine\Exception\InvalidArgumentException
*/
protected function checkLocalFile()
{
if (!is_file($this->path)) {
throw new InvalidArgumentException(sprintf('File %s does not exist.', $this->path));
}
if (!is_readable($this->path)) {
throw new InvalidArgumentException(sprintf('File %s is not readable.', $this->path));
}
}
/**
* Read a remote file.
*
* @throws \Imagine\Exception\InvalidArgumentException
* @throws \Imagine\Exception\RuntimeException
*
* @return string
*/
protected function readRemoteFile()
{
if ($this->isCurlSupported()) {
return $this->readRemoteFileWithCurl();
}
return $this->readRemoteFileWithFileGetContents();
}
/**
* Check if curl is available and it's a decent version.
*
* @return bool
*/
protected function isCurlSupported()
{
if ($this->isCurlSupported === null) {
$isCurlSupported = false;
if (function_exists('curl_init') && function_exists('curl_version')) {
$curlVersion = curl_version();
if (is_array($curlVersion) && !empty($curlVersion['version'])) {
if (version_compare($curlVersion['version'], static::MINIMUM_CURL_VERSION) >= 0) {
$isCurlSupported = true;
}
}
}
$this->isCurlSupported = $isCurlSupported;
}
return $this->isCurlSupported;
}
/**
* Read a remote file using the cURL extension.
*
* @throws \Imagine\Exception\InvalidArgumentException
*
* @return string
*/
protected function readRemoteFileWithCurl()
{
$curl = @curl_init($this->path);
if ($curl === false) {
throw new RuntimeException('curl_init() failed.');
}
if (!@curl_setopt($curl, CURLOPT_RETURNTRANSFER, true)) {
throw new RuntimeException('curl_setopt(CURLOPT_RETURNTRANSFER) failed.');
}
$this->setCurlOptions($curl);
$response = @curl_exec($curl);
if ($response === false) {
$errorMessage = curl_error($curl);
if ($errorMessage === '') {
$errorMessage = 'curl_exec() failed.';
}
$errorCode = curl_errno($curl);
curl_close($curl);
throw new RuntimeException($errorMessage, $errorCode);
}
$responseInfo = curl_getinfo($curl);
curl_close($curl);
if ($responseInfo['http_code'] == 404) {
throw new InvalidArgumentException(sprintf('File %s does not exist.', $this->path));
}
if ($responseInfo['http_code'] < 200 || $responseInfo['http_code'] >= 300) {
throw new InvalidArgumentException(sprintf('Failed to download "%s": %s', $this->path, $responseInfo['http_code']));
}
return $response;
}
/**
* Set curl options.
*
*
* @param resource $curl
*
* @throws \Imagine\Exception\RuntimeException
*/
protected function setCurlOptions($curl)
{
if (!@curl_setopt($curl, CURLOPT_HTTPHEADER, array('Accept-Encoding: identity'))) {
throw new RuntimeException('curl_setopt(CURLOPT_HTTPHEADER) failed.');
}
if (!@curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true)) {
throw new RuntimeException('curl_setopt(CURLOPT_FOLLOWLOCATION) failed.');
}
if (defined('CURL_SSLVERSION_TLSv1_1')) {
if (!@curl_setopt($curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_1)) {
throw new RuntimeException('curl_setopt(CURLOPT_SSLVERSION) failed.');
}
} else {
// Manually checked that CURL_SSLVERSION_TLSv1_1 is 5 for any version of curl from 7.34.0 to 7.61.0
// See for example https://github.com/curl/curl/blob/curl-7_34_0/include/curl/curl.h#L1668
if (!@curl_setopt($curl, CURLOPT_SSLVERSION, 5)) {
throw new RuntimeException('curl_setopt(CURLOPT_SSLVERSION) failed.');
}
}
}
/**
* Read a remote file using the file_get_contents.
*
* @throws \Imagine\Exception\InvalidArgumentException
*
* @return string
*/
protected function readRemoteFileWithFileGetContents()
{
$http_response_header = null;
$data = @file_get_contents($this->path);
if ($data === false) {
if (is_array($http_response_header) && isset($http_response_header[0]) && preg_match('/^HTTP\/\d+(?:\.\d+)*\s+(\d+\s+\w.*)/i', $http_response_header[0], $matches)) {
throw new InvalidArgumentException(sprintf('Failed to read from URL %s: %s', $this->path, $matches[1]));
}
throw new InvalidArgumentException(sprintf('Failed to read from URL %s', $this->path));
}
return $data;
}
}

View File

@@ -0,0 +1,50 @@
<?php
/*
* This file is part of the Imagine package.
*
* For the full copyright and license information, please view the LICENSE file that was distributed with this source code.
*/
namespace Imagine\File;
/**
* Interface for classes that can load local or remote files.
*/
interface LoaderInterface
{
/**
* Is this a local file.
*
* @return bool
*/
public function isLocalFile();
/**
* Get the path of the file (local or remote).
*
* @return string
*/
public function getPath();
/**
* Is the binary content already loaded?
*
* @return bool
*/
public function hasReadData();
/**
* Get the file binary contents.
*
* @return string
*/
public function getData();
/**
* The string representation of this object must be the file path (local or remote).
*
* @return string
*/
public function __toString();
}

View File

@@ -0,0 +1,68 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Filter\Advanced;
use Imagine\Exception\InvalidArgumentException;
use Imagine\Filter\FilterInterface;
use Imagine\Image\ImageInterface;
use Imagine\Image\Palette\Color\ColorInterface;
use Imagine\Image\Palette\RGB;
use Imagine\Image\Point;
/**
* This filter calculates, for each pixel of an image, whether it is ligher or darker than a threshold.
* If the pixel is lighter than the thresold it will be black, otherwise it will be light.
* The result is an image with only black and white pixels (black pixels for ligher colors, white pixels for darker colors).
*/
class BlackWhite extends OnPixelBased implements FilterInterface
{
/**
* @var \Imagine\Filter\Advanced\Grayscale
*/
protected $grayScaleFilter;
/**
* Initialize this filter.
*
* @param int $threshold the dask/light threshold, from 0 (all black) to 255 (all white)
*
* @throws \Imagine\Exception\InvalidArgumentException
*/
public function __construct($threshold)
{
if (!(0 <= $threshold && $threshold <= 255)) {
throw new InvalidArgumentException('$threshold has to be between 0 and 255');
}
$this->grayScaleFilter = new Grayscale();
$rgb = new RGB();
parent::__construct(
function (ImageInterface $image, Point $point) use ($threshold, $rgb) {
$newRedValue = $image->getColorAt($point)->getValue(ColorInterface::COLOR_RED) < $threshold ? 255 : 0;
$image->draw()->dot($point, $rgb->color(array($newRedValue, $newRedValue, $newRedValue)));
}
);
}
/**
* {@inheritdoc}
*
* @see \Imagine\Filter\Advanced\OnPixelBased::apply()
*/
public function apply(ImageInterface $image)
{
$grayScaledImage = $this->grayScaleFilter->apply($image);
return parent::apply($grayScaledImage);
}
}

View File

@@ -0,0 +1,100 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Filter\Advanced;
use Imagine\Filter\FilterInterface;
use Imagine\Image\ImageInterface;
use Imagine\Image\Palette\Color\ColorInterface;
use Imagine\Image\Point;
/**
* A border filter.
*/
class Border implements FilterInterface
{
/**
* @var \Imagine\Image\Palette\Color\ColorInterface
*/
private $color;
/**
* @var int
*/
private $width;
/**
* @var int
*/
private $height;
/**
* Constructs Border filter with given color, width and height.
*
* @param \Imagine\Image\Palette\Color\ColorInterface $color
* @param int $width Width of the border on the left and right sides of the image
* @param int $height Height of the border on the top and bottom sides of the image
*/
public function __construct(ColorInterface $color, $width = 1, $height = 1)
{
$this->color = $color;
$this->width = $width;
$this->height = $height;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Filter\FilterInterface::apply()
*/
public function apply(ImageInterface $image)
{
$size = $image->getSize();
$width = $size->getWidth();
$height = $size->getHeight();
$draw = $image->draw();
// Draw top and bottom lines
$draw
->line(
new Point(0, 0),
new Point($width - 1, 0),
$this->color,
$this->height
)
->line(
new Point($width - 1, $height - 1),
new Point(0, $height - 1),
$this->color,
$this->height
)
;
// Draw sides
$draw
->line(
new Point(0, 0),
new Point(0, $height - 1),
$this->color,
$this->width
)
->line(
new Point($width - 1, 0),
new Point($width - 1, $height - 1),
$this->color,
$this->width
)
;
return $image;
}
}

View File

@@ -0,0 +1,91 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Filter\Advanced;
use Imagine\Exception\InvalidArgumentException;
use Imagine\Filter\FilterInterface;
use Imagine\Utils\Matrix;
/**
* BorderDetection based on Laplace-Operator. Three different variants are offered:.
* <code><pre>
* First Second Third
* 0, 1, 0 1, 1, 1, -1, 2, -1,
* 1, -4, 1 and 1, -8, 1, and 2, -4, 2,
* 0, 1, 0 1, 1, 1 -1, 2, -1
* </pre></code>.
*
* Consider to apply this filter on a grayscaled image.
*/
class BorderDetection extends Neighborhood implements FilterInterface
{
/**
* First variant of the detection matrix.
*
* @var int
*/
const VARIANT_ONE = 0;
/**
* Second variant of the detection matrix.
*
* @var int
*/
const VARIANT_TWO = 1;
/**
* Third variant of the detection matrix.
*
* @var int
*/
const VARIANT_THREE = 2;
/**
* Initialize this filter.
*
* @param int $variant One of the BorderDetection::VARIANT_... constants.
*
* @throws \Imagine\Exception\InvalidArgumentException throws an InvalidArgumentException if $variant is not valid
*/
public function __construct($variant = self::VARIANT_ONE)
{
$matrix = null;
switch ($variant) {
case self::VARIANT_ONE:
$matrix = new Matrix(3, 3, array(
0, 1, 0,
1, -4, 1,
0, 1, 0,
));
break;
case self::VARIANT_TWO:
$matrix = new Matrix(3, 3, array(
1, 1, 1,
1, -8, 1,
1, 1, 1,
));
break;
case self::VARIANT_THREE:
$matrix = new Matrix(3, 3, array(
-1, 2, -1,
2, -4, 2,
-1, 2, -1,
));
break;
default:
throw new InvalidArgumentException('Unknown variant ' . $variant);
}
parent::__construct($matrix);
}
}

View File

@@ -0,0 +1,75 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Filter\Advanced;
use Imagine\Filter\FilterInterface;
use Imagine\Image\BoxInterface;
use Imagine\Image\ImageInterface;
use Imagine\Image\ImagineInterface;
use Imagine\Image\Palette\Color\ColorInterface;
use Imagine\Image\Point;
use Imagine\Image\PointInterface;
/**
* A canvas filter.
*/
class Canvas implements FilterInterface
{
/**
* @var \Imagine\Image\BoxInterface
*/
private $size;
/**
* @var \Imagine\Image\PointInterface
*/
private $placement;
/**
* @var \Imagine\Image\Palette\Color\ColorInterface
*/
private $background;
/**
* @var \Imagine\Image\ImagineInterface
*/
private $imagine;
/**
* Constructs Canvas filter with given width and height and the placement of the current image inside the new canvas.
*
* @param \Imagine\Image\ImagineInterface $imagine
* @param \Imagine\Image\BoxInterface $size
* @param \Imagine\Image\PointInterface $placement
* @param \Imagine\Image\Palette\Color\ColorInterface $background
*/
public function __construct(ImagineInterface $imagine, BoxInterface $size, PointInterface $placement = null, ColorInterface $background = null)
{
$this->imagine = $imagine;
$this->size = $size;
$this->placement = $placement ?: new Point(0, 0);
$this->background = $background;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Filter\FilterInterface::apply()
*/
public function apply(ImageInterface $image)
{
$canvas = $this->imagine->create($this->size, $this->background);
$canvas->paste($image, $this->placement);
return $canvas;
}
}

View File

@@ -0,0 +1,30 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Filter\Advanced;
use Imagine\Filter\FilterInterface;
use Imagine\Image\ImageInterface;
use Imagine\Image\Point;
/**
* The Grayscale filter calculates the gray-value based on RGB.
*/
class Grayscale extends OnPixelBased implements FilterInterface
{
public function __construct()
{
parent::__construct(function (ImageInterface $image, Point $point) {
$color = $image->getColorAt($point);
$image->draw()->dot($point, $color->grayscale());
});
}
}

View File

@@ -0,0 +1,33 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Filter\Advanced;
use Imagine\Filter\FilterInterface;
use Imagine\Image\ImageInterface;
/**
* This filter negates every color of every pixel of an image.
*/
class Negation implements FilterInterface
{
/**
* {@inheritdoc}
*
* @see \Imagine\Filter\FilterInterface::apply()
*/
public function apply(ImageInterface $image)
{
$image->effects()->negative();
return $image;
}
}

View File

@@ -0,0 +1,100 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Filter\Advanced;
use Imagine\Filter\FilterInterface;
use Imagine\Image\ImageInterface;
use Imagine\Image\Point;
use Imagine\Utils\Matrix;
/**
* The Neighborhood filter takes a matrix and calculates the color current pixel based on its neighborhood.
*
* @example
* <code><pre>
* a, b, c
* Matrix = d, e, f
* g, h, i
* </pre></code>
*
* and color{i, j} the color of the pixel at position (i, j). It calculates the color of pixel (x, y) like that:
*
* <code><pre>
* color (x, y) = a * color(x - 1, y - 1) + b * color(x, y - 1) + c * color(x + 1, y - 1)
* + d * color(x - 1, y) + e * color(x, y) + f * color(x + 1, y)
* + g * color(x - 1, y + 1) + h * color(x, y + 1) + i * color(x + 1, y + 1)
* </pre></code>
*/
class Neighborhood implements FilterInterface
{
/**
* @var \Imagine\Utils\Matrix
*/
protected $matrix;
/**
* Initialize the instance.
*
* @param \Imagine\Utils\Matrix $matrix
*/
public function __construct(Matrix $matrix)
{
$this->matrix = $matrix;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Filter\FilterInterface::apply()
*/
public function apply(ImageInterface $image)
{
// We reduce the usage of methods on the image to dramatically increase the performance of this algorithm.
// Really... We need that performance...
// Therefore we first build a matrix, that holds the colors of the image.
$width = $image->getSize()->getWidth();
$height = $image->getSize()->getHeight();
$byteData = new Matrix($width, $height);
for ($y = 0; $y < $height; $y++) {
for ($x = 0; $x < $width; $x++) {
$byteData->setElementAt($x, $y, $image->getColorAt(new Point($x, $y)));
}
}
$dWidth = (int) (($this->matrix->getWidth() - 1) / 2);
$dHeight = (int) (($this->matrix->getHeight() - 1) / 2);
// foreach point, which has a big enough neighborhood
for ($y = $dHeight; $y < $height - $dHeight; $y++) {
for ($x = $dWidth; $x < $width - $dWidth; $x++) {
$currentColor = array_fill(0, count($image->palette()->pixelDefinition()), 0);
// calculate the new color based on the neighborhood
for ($boxY = $y - $dHeight, $matrixY = 0; $boxY <= $y + $dHeight; $boxY++, $matrixY++) {
for ($boxX = $x - $dWidth, $matrixX = 0; $boxX <= $x + $dWidth; $boxX++, $matrixX++) {
foreach ($image->palette()->pixelDefinition() as $index => $stream) {
$currentColor[$index] +=
$byteData->getElementAt($boxX, $boxY)->getValue($stream) *
$this->matrix->getElementAt($matrixX, $matrixY)
;
}
}
}
$image->draw()->dot(new Point($x, $y), $image->palette()->color($currentColor));
}
}
return $image;
}
}

View File

@@ -0,0 +1,64 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Filter\Advanced;
use Imagine\Exception\InvalidArgumentException;
use Imagine\Filter\FilterInterface;
use Imagine\Image\ImageInterface;
use Imagine\Image\Point;
/**
* The OnPixelBased takes a callable, and for each pixel, this callable is called with the
* image (\Imagine\Image\ImageInterface) and the current point (\Imagine\Image\Point).
*/
class OnPixelBased implements FilterInterface
{
/**
* @var callable
*/
protected $callback;
/**
* Initialize the instance.
*
* @param callable $callback
*
* @throws \Imagine\Exception\InvalidArgumentException
*/
public function __construct($callback)
{
if (!is_callable($callback)) {
throw new InvalidArgumentException('$callback has to be callable');
}
$this->callback = $callback;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Filter\FilterInterface::apply()
*/
public function apply(ImageInterface $image)
{
$size = $image->getSize();
$w = $size->getWidth();
$h = $size->getHeight();
for ($y = 0; $y < $h; $y++) {
for ($x = 0; $x < $w; $x++) {
call_user_func($this->callback, $image, new Point($x, $y));
}
}
return $image;
}
}

View File

@@ -0,0 +1,58 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Filter\Advanced;
use Imagine\Exception\InvalidArgumentException;
use Imagine\Filter\FilterInterface;
use Imagine\Image\ImageInterface;
/**
* The RelativeResize filter allows images to be resized relative to their existing dimensions.
*/
class RelativeResize implements FilterInterface
{
/**
* @var string
*/
private $method;
/**
* @var mixed
*/
private $parameter;
/**
* Constructs a RelativeResize filter with the given method and argument.
*
* @param string $method BoxInterface method
* @param mixed $parameter Parameter for BoxInterface method
*/
public function __construct($method, $parameter)
{
if (!in_array($method, array('heighten', 'increase', 'scale', 'widen'))) {
throw new InvalidArgumentException(sprintf('Unsupported method: ', $method));
}
$this->method = $method;
$this->parameter = $parameter;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Filter\FilterInterface::apply()
*/
public function apply(ImageInterface $image)
{
return $image->resize(call_user_func(array($image->getSize(), $this->method), $this->parameter));
}
}

View File

@@ -0,0 +1,46 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Filter\Basic;
use Imagine\Filter\FilterInterface;
use Imagine\Image\ImageInterface;
/**
* An apply mask filter.
*/
class ApplyMask implements FilterInterface
{
/**
* @var \Imagine\Image\ImageInterface
*/
private $mask;
/**
* Initialize the instance.
*
* @param \Imagine\Image\ImageInterface $mask
*/
public function __construct(ImageInterface $mask)
{
$this->mask = $mask;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Filter\FilterInterface::apply()
*/
public function apply(ImageInterface $image)
{
return $image->applyMask($this->mask);
}
}

View File

@@ -0,0 +1,133 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Filter\Basic;
use Imagine\Filter\FilterInterface;
use Imagine\Image\ImageInterface;
use Imagine\Image\Palette\Color\ColorInterface;
/**
* Rotates an image automatically based on exif information.
*
* Your attention please: This filter requires the use of the
* ExifMetadataReader to work.
*
* @see https://imagine.readthedocs.org/en/latest/usage/metadata.html
*/
class Autorotate implements FilterInterface
{
/**
* Image transformation: flip vertically.
*
* @var string
*/
const FLIP_VERTICALLY = 'V';
/**
* Image transformation: flip horizontally.
*
* @var string
*/
const FLIP_HORIZONTALLY = 'H';
/**
* @var string|array|\Imagine\Image\Palette\Color\ColorInterface
*/
private $color;
/**
* @param string|array|\Imagine\Image\Palette\Color\ColorInterface $color A color
*/
public function __construct($color = '000000')
{
$this->color = $color;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Filter\FilterInterface::apply()
*/
public function apply(ImageInterface $image)
{
foreach ($this->getTransformations($image) as $transformation) {
if ($transformation === self::FLIP_HORIZONTALLY) {
$image->flipHorizontally();
} elseif ($transformation === self::FLIP_VERTICALLY) {
$image->flipVertically();
} elseif (is_int($transformation)) {
$image->rotate($transformation, $this->getColor($image));
}
}
return $image;
}
/**
* Get the transformations.
*
* @param \Imagine\Image\ImageInterface $image
*
* @return array an array containing Autorotate::FLIP_VERTICALLY, Autorotate::FLIP_HORIZONTALLY, rotation degrees
*/
public function getTransformations(ImageInterface $image)
{
$transformations = array();
$metadata = $image->metadata();
switch (isset($metadata['ifd0.Orientation']) ? $metadata['ifd0.Orientation'] : null) {
case 1: // top-left
break;
case 2: // top-right
$transformations[] = self::FLIP_HORIZONTALLY;
break;
case 3: // bottom-right
$transformations[] = 180;
break;
case 4: // bottom-left
$transformations[] = self::FLIP_HORIZONTALLY;
$transformations[] = 180;
break;
case 5: // left-top
$transformations[] = self::FLIP_HORIZONTALLY;
$transformations[] = -90;
break;
case 6: // right-top
$transformations[] = 90;
break;
case 7: // right-bottom
$transformations[] = self::FLIP_HORIZONTALLY;
$transformations[] = 90;
break;
case 8: // left-bottom
$transformations[] = -90;
break;
default: // Invalid orientation
break;
}
return $transformations;
}
/**
* @param \Imagine\Image\ImageInterface $image
*
* @return \Imagine\Image\Palette\Color\ColorInterface
*/
private function getColor(ImageInterface $image)
{
if ($this->color instanceof ColorInterface) {
return $this->color;
}
return $image->palette()->color($this->color);
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Filter\Basic;
use Imagine\Filter\FilterInterface;
use Imagine\Image\ImageInterface;
/**
* A copy filter.
*/
class Copy implements FilterInterface
{
/**
* {@inheritdoc}
*
* @see \Imagine\Filter\FilterInterface::apply()
*/
public function apply(ImageInterface $image)
{
return $image->copy();
}
}

View File

@@ -0,0 +1,55 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Filter\Basic;
use Imagine\Filter\FilterInterface;
use Imagine\Image\BoxInterface;
use Imagine\Image\ImageInterface;
use Imagine\Image\PointInterface;
/**
* A crop filter.
*/
class Crop implements FilterInterface
{
/**
* @var \Imagine\Image\PointInterface
*/
private $start;
/**
* @var \Imagine\Image\BoxInterface
*/
private $size;
/**
* Constructs a Crop filter with given x, y, coordinates and crop width and height values.
*
* @param \Imagine\Image\PointInterface $start
* @param \Imagine\Image\BoxInterface $size
*/
public function __construct(PointInterface $start, BoxInterface $size)
{
$this->start = $start;
$this->size = $size;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Filter\FilterInterface::apply()
*/
public function apply(ImageInterface $image)
{
return $image->crop($this->start, $this->size);
}
}

View File

@@ -0,0 +1,45 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Filter\Basic;
use Imagine\Filter\FilterInterface;
use Imagine\Image\Fill\FillInterface;
use Imagine\Image\ImageInterface;
/**
* A fill filter.
*/
class Fill implements FilterInterface
{
/**
* @var \Imagine\Image\Fill\FillInterface
*/
private $fill;
/**
* @param \Imagine\Image\Fill\FillInterface $fill
*/
public function __construct(FillInterface $fill)
{
$this->fill = $fill;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Filter\FilterInterface::apply()
*/
public function apply(ImageInterface $image)
{
return $image->fill($this->fill);
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Filter\Basic;
use Imagine\Filter\FilterInterface;
use Imagine\Image\ImageInterface;
/**
* A "flip horizontally" filter.
*/
class FlipHorizontally implements FilterInterface
{
/**
* {@inheritdoc}
*
* @see \Imagine\Filter\FilterInterface::apply()
*/
public function apply(ImageInterface $image)
{
return $image->flipHorizontally();
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Filter\Basic;
use Imagine\Filter\FilterInterface;
use Imagine\Image\ImageInterface;
/**
* A "flip vertically" filter.
*/
class FlipVertically implements FilterInterface
{
/**
* {@inheritdoc}
*
* @see \Imagine\Filter\FilterInterface::apply()
*/
public function apply(ImageInterface $image)
{
return $image->flipVertically();
}
}

View File

@@ -0,0 +1,69 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Filter\Basic;
use Imagine\Exception\InvalidArgumentException;
use Imagine\Filter\FilterInterface;
use Imagine\Image\ImageInterface;
use Imagine\Image\PointInterface;
/**
* A paste filter.
*/
class Paste implements FilterInterface
{
/**
* @var \Imagine\Image\ImageInterface
*/
private $image;
/**
* @var \Imagine\Image\PointInterface
*/
private $start;
/**
* How to paste the image, from 0 (fully transparent) to 100 (fully opaque).
*
* @var int
*/
private $alpha;
/**
* Constructs a Paste filter with given ImageInterface to paste and x, y
* coordinates of target position.
*
* @param \Imagine\Image\ImageInterface $image
* @param \Imagine\Image\PointInterface $start
* @param int $alpha how to paste the image, from 0 (fully transparent) to 100 (fully opaque)
*/
public function __construct(ImageInterface $image, PointInterface $start, $alpha = 100)
{
$this->image = $image;
$this->start = $start;
$alpha = (int) round($alpha);
if ($alpha < 0 || $alpha > 100) {
throw new InvalidArgumentException(sprintf('The %1$s argument can range from %2$d to %3$d, but you specified %4$d.', '$alpha', 0, 100, $alpha));
}
$this->alpha = $alpha;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Filter\FilterInterface::apply()
*/
public function apply(ImageInterface $image)
{
return $image->paste($this->image, $this->start, $this->alpha);
}
}

View File

@@ -0,0 +1,54 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Filter\Basic;
use Imagine\Filter\FilterInterface;
use Imagine\Image\BoxInterface;
use Imagine\Image\ImageInterface;
/**
* A resize filter.
*/
class Resize implements FilterInterface
{
/**
* @var \Imagine\Image\BoxInterface
*/
private $size;
/**
* @var string
*/
private $filter;
/**
* Constructs Resize filter with given width and height.
*
* @param \Imagine\Image\BoxInterface $size
* @param string $filter
*/
public function __construct(BoxInterface $size, $filter = ImageInterface::FILTER_UNDEFINED)
{
$this->size = $size;
$this->filter = $filter;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Filter\FilterInterface::apply()
*/
public function apply(ImageInterface $image)
{
return $image->resize($this->size, $this->filter);
}
}

View File

@@ -0,0 +1,54 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Filter\Basic;
use Imagine\Filter\FilterInterface;
use Imagine\Image\ImageInterface;
use Imagine\Image\Palette\Color\ColorInterface;
/**
* A rotate filter.
*/
class Rotate implements FilterInterface
{
/**
* @var int
*/
private $angle;
/**
* @var \Imagine\Image\Palette\Color\ColorInterface
*/
private $background;
/**
* Constructs Rotate filter with given angle and background color.
*
* @param int $angle
* @param \Imagine\Image\Palette\Color\ColorInterface $background
*/
public function __construct($angle, ColorInterface $background = null)
{
$this->angle = $angle;
$this->background = $background;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Filter\FilterInterface::apply()
*/
public function apply(ImageInterface $image)
{
return $image->rotate($this->angle, $this->background);
}
}

View File

@@ -0,0 +1,53 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Filter\Basic;
use Imagine\Filter\FilterInterface;
use Imagine\Image\ImageInterface;
/**
* A save filter.
*/
class Save implements FilterInterface
{
/**
* @var string
*/
private $path;
/**
* @var array
*/
private $options;
/**
* Constructs Save filter with given path and options.
*
* @param string $path
* @param array $options
*/
public function __construct($path = null, array $options = array())
{
$this->path = $path;
$this->options = $options;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Filter\FilterInterface::apply()
*/
public function apply(ImageInterface $image)
{
return $image->save($this->path, $this->options);
}
}

View File

@@ -0,0 +1,53 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Filter\Basic;
use Imagine\Filter\FilterInterface;
use Imagine\Image\ImageInterface;
/**
* A show filter.
*/
class Show implements FilterInterface
{
/**
* @var string
*/
private $format;
/**
* @var array
*/
private $options;
/**
* Constructs the Show filter with given format and options.
*
* @param string $format
* @param array $options
*/
public function __construct($format, array $options = array())
{
$this->format = $format;
$this->options = $options;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Filter\FilterInterface::apply()
*/
public function apply(ImageInterface $image)
{
return $image->show($this->format, $this->options);
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Filter\Basic;
use Imagine\Filter\FilterInterface;
use Imagine\Image\ImageInterface;
/**
* A strip filter.
*/
class Strip implements FilterInterface
{
/**
* {@inheritdoc}
*
* @see \Imagine\Filter\FilterInterface::apply()
*/
public function apply(ImageInterface $image)
{
return $image->strip();
}
}

View File

@@ -0,0 +1,61 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Filter\Basic;
use Imagine\Filter\FilterInterface;
use Imagine\Image\BoxInterface;
use Imagine\Image\ImageInterface;
/**
* A thumbnail filter.
*/
class Thumbnail implements FilterInterface
{
/**
* @var \Imagine\Image\BoxInterface
*/
private $size;
/**
* @var int|string
*/
private $settings;
/**
* @var string
*/
private $filter;
/**
* Constructs the Thumbnail filter.
*
* @param \Imagine\Image\BoxInterface $size
* @param int|string $settings One or more of the ManipulatorInterface::THUMBNAIL_ flags (joined with |). It may be a string for backward compatibility with old constant values that were strings.
* @param string $filter See ImageInterface::FILTER_... constants
*/
public function __construct(BoxInterface $size, $settings = ImageInterface::THUMBNAIL_INSET, $filter = ImageInterface::FILTER_UNDEFINED)
{
$this->size = $size;
$this->settings = $settings;
$this->filter = $filter;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Filter\FilterInterface::apply()
*/
public function apply(ImageInterface $image)
{
return $image->thumbnail($this->size, $this->settings, $this->filter);
}
}

View File

@@ -0,0 +1,74 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Filter\Basic;
use Imagine\Filter\FilterInterface;
use Imagine\Image\ImageInterface;
use Imagine\Image\Palette\RGB;
/**
* A filter to render web-optimized images.
*/
class WebOptimization implements FilterInterface
{
/**
* @var \Imagine\Image\Palette\RGB
*/
private $palette;
/**
* @var string|callable|null
*/
private $path;
/**
* @var array
*/
private $options;
/**
* @param string|callable|null $path
* @param array $options
*/
public function __construct($path = null, array $options = array())
{
$this->path = $path;
$this->options = array_replace(array(
'resolution-units' => ImageInterface::RESOLUTION_PIXELSPERINCH,
'resolution-y' => 72,
'resolution-x' => 72,
), $options);
$this->palette = new RGB();
}
/**
* {@inheritdoc}
*
* @see \Imagine\Filter\FilterInterface::apply()
*/
public function apply(ImageInterface $image)
{
$image
->usePalette($this->palette)
->strip();
if (is_callable($this->path)) {
$path = call_user_func($this->path, $image);
} elseif (null !== $this->path) {
$path = $this->path;
} else {
return $image;
}
return $image->save($path, $this->options);
}
}

View File

@@ -0,0 +1,29 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Filter;
use Imagine\Image\ImageInterface;
/**
* Interface for imagine filters.
*/
interface FilterInterface
{
/**
* Applies scheduled transformation to an ImageInterface instance.
*
* @param \Imagine\Image\ImageInterface $image
*
* @return \Imagine\Image\ImageInterface returns the processed ImageInterface instance
*/
public function apply(ImageInterface $image);
}

View File

@@ -0,0 +1,54 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Filter;
use Imagine\Exception\InvalidArgumentException;
use Imagine\Image\ImagineInterface;
/**
* ImagineAware base class.
*/
abstract class ImagineAware implements FilterInterface
{
/**
* An ImagineInterface instance.
*
* @var \Imagine\Image\ImagineInterface
*/
private $imagine;
/**
* Set ImagineInterface instance.
*
* @param \Imagine\Image\ImagineInterface $imagine An ImagineInterface instance
*/
public function setImagine(ImagineInterface $imagine)
{
$this->imagine = $imagine;
}
/**
* Get ImagineInterface instance.
*
* @throws \Imagine\Exception\InvalidArgumentException
*
* @return \Imagine\Image\ImagineInterface
*/
public function getImagine()
{
if (!$this->imagine instanceof ImagineInterface) {
throw new InvalidArgumentException(sprintf('In order to use %s pass an Imagine\Image\ImagineInterface instance to filter constructor', get_class($this)));
}
return $this->imagine;
}
}

View File

@@ -0,0 +1,268 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Filter;
use Imagine\Exception\InvalidArgumentException;
use Imagine\Filter\Basic\ApplyMask;
use Imagine\Filter\Basic\Copy;
use Imagine\Filter\Basic\Crop;
use Imagine\Filter\Basic\Fill;
use Imagine\Filter\Basic\FlipHorizontally;
use Imagine\Filter\Basic\FlipVertically;
use Imagine\Filter\Basic\Paste;
use Imagine\Filter\Basic\Resize;
use Imagine\Filter\Basic\Rotate;
use Imagine\Filter\Basic\Save;
use Imagine\Filter\Basic\Show;
use Imagine\Filter\Basic\Strip;
use Imagine\Filter\Basic\Thumbnail;
use Imagine\Image\BoxInterface;
use Imagine\Image\Fill\FillInterface;
use Imagine\Image\ImageInterface;
use Imagine\Image\ImagineInterface;
use Imagine\Image\ManipulatorInterface;
use Imagine\Image\Palette\Color\ColorInterface;
use Imagine\Image\PointInterface;
/**
* A transformation filter.
*/
final class Transformation implements FilterInterface, ManipulatorInterface
{
/**
* @var array[\Imagine\Filter\FilterInterface[]]
*/
private $filters = array();
/**
* @var array[\Imagine\Filter\FilterInterface[]]|null
*/
private $sorted;
/**
* An ImagineInterface instance.
*
* @var \Imagine\Image\ImageInterface|null
*/
private $imagine;
/**
* Class constructor.
*
* @param \Imagine\Image\ImageInterface|null $imagine An ImagineInterface instance
*/
public function __construct(ImagineInterface $imagine = null)
{
$this->imagine = $imagine;
}
/**
* Applies a given FilterInterface onto given ImageInterface and returns modified ImageInterface.
*
* @param \Imagine\Image\ImageInterface $image
* @param \Imagine\Filter\FilterInterface $filter
*
* @throws \Imagine\Exception\InvalidArgumentException
*
* @return \Imagine\Image\ImageInterface
*/
public function applyFilter(ImageInterface $image, FilterInterface $filter)
{
if ($filter instanceof ImagineAware) {
if ($this->imagine === null) {
throw new InvalidArgumentException(sprintf('In order to use %s pass an Imagine\Image\ImagineInterface instance to Transformation constructor', get_class($filter)));
}
$filter->setImagine($this->imagine);
}
return $filter->apply($image);
}
/**
* Returns a list of filters sorted by their priority. Filters with same priority will be returned in the order they were added.
*
* @return array
*/
public function getFilters()
{
if (null === $this->sorted) {
if (!empty($this->filters)) {
ksort($this->filters);
$this->sorted = call_user_func_array('array_merge', $this->filters);
} else {
$this->sorted = array();
}
}
return $this->sorted;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Filter\FilterInterface::apply()
*/
public function apply(ImageInterface $image)
{
return array_reduce(
$this->getFilters(),
array($this, 'applyFilter'),
$image
);
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ManipulatorInterface::copy()
*/
public function copy()
{
return $this->add(new Copy());
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ManipulatorInterface::crop()
*/
public function crop(PointInterface $start, BoxInterface $size)
{
return $this->add(new Crop($start, $size));
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ManipulatorInterface::flipHorizontally()
*/
public function flipHorizontally()
{
return $this->add(new FlipHorizontally());
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ManipulatorInterface::flipVertically()
*/
public function flipVertically()
{
return $this->add(new FlipVertically());
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ManipulatorInterface::strip()
*/
public function strip()
{
return $this->add(new Strip());
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ManipulatorInterface::paste()
*/
public function paste(ImageInterface $image, PointInterface $start, $alpha = 100)
{
return $this->add(new Paste($image, $start, $alpha));
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ManipulatorInterface::applyMask()
*/
public function applyMask(ImageInterface $mask)
{
return $this->add(new ApplyMask($mask));
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ManipulatorInterface::fill()
*/
public function fill(FillInterface $fill)
{
return $this->add(new Fill($fill));
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ManipulatorInterface::resize()
*/
public function resize(BoxInterface $size, $filter = ImageInterface::FILTER_UNDEFINED)
{
return $this->add(new Resize($size, $filter));
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ManipulatorInterface::rotate()
*/
public function rotate($angle, ColorInterface $background = null)
{
return $this->add(new Rotate($angle, $background));
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ManipulatorInterface::save()
*/
public function save($path = null, array $options = array())
{
return $this->add(new Save($path, $options));
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ManipulatorInterface::show()
*/
public function show($format, array $options = array())
{
return $this->add(new Show($format, $options));
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ManipulatorInterface::thumbnail()
*/
public function thumbnail(BoxInterface $size, $settings = ImageInterface::THUMBNAIL_INSET, $filter = ImageInterface::FILTER_UNDEFINED)
{
return $this->add(new Thumbnail($size, $settings, $filter));
}
/**
* Registers a given FilterInterface in an internal array of filters for later application to an instance of ImageInterface.
*
* @param \Imagine\Filter\FilterInterface $filter
* @param int $priority
*
* @return $this
*/
public function add(FilterInterface $filter, $priority = 0)
{
$this->filters[$priority][] = $filter;
$this->sorted = null;
return $this;
}
}

422
vendor/imagine/imagine/src/Gd/Drawer.php vendored Normal file
View File

@@ -0,0 +1,422 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Gd;
use Imagine\Draw\DrawerInterface;
use Imagine\Exception\InvalidArgumentException;
use Imagine\Exception\RuntimeException;
use Imagine\Image\AbstractFont;
use Imagine\Image\Box;
use Imagine\Image\BoxInterface;
use Imagine\Image\Palette\Color\ColorInterface;
use Imagine\Image\Palette\Color\RGB as RGBColor;
use Imagine\Image\PointInterface;
/**
* Drawer implementation using the GD PHP extension.
*/
final class Drawer implements DrawerInterface
{
/**
* @var resource
*/
private $resource;
/**
* @var array
*/
private $info;
/**
* Constructs Drawer with a given gd image resource.
*
* @param resource $resource
*/
public function __construct($resource)
{
$this->loadGdInfo();
$this->resource = $resource;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Draw\DrawerInterface::arc()
*/
public function arc(PointInterface $center, BoxInterface $size, $start, $end, ColorInterface $color, $thickness = 1)
{
$thickness = max(0, (int) round($thickness));
if ($thickness === 0) {
return $this;
}
imagesetthickness($this->resource, $thickness);
if (false === imagealphablending($this->resource, true)) {
throw new RuntimeException('Draw arc operation failed');
}
if (false === imagearc($this->resource, $center->getX(), $center->getY(), $size->getWidth(), $size->getHeight(), $start, $end, $this->getColor($color))) {
imagealphablending($this->resource, false);
throw new RuntimeException('Draw arc operation failed');
}
if (false === imagealphablending($this->resource, false)) {
throw new RuntimeException('Draw arc operation failed');
}
return $this;
}
/**
* This function does not work properly because of a bug in GD.
*
* {@inheritdoc}
*
* @see \Imagine\Draw\DrawerInterface::chord()
*/
public function chord(PointInterface $center, BoxInterface $size, $start, $end, ColorInterface $color, $fill = false, $thickness = 1)
{
$thickness = max(0, (int) round($thickness));
if ($thickness === 0 && !$fill) {
return $this;
}
imagesetthickness($this->resource, $thickness);
if (false === imagealphablending($this->resource, true)) {
throw new RuntimeException('Draw chord operation failed');
}
if ($fill) {
$style = IMG_ARC_CHORD;
if (false === imagefilledarc($this->resource, $center->getX(), $center->getY(), $size->getWidth(), $size->getHeight(), $start, $end, $this->getColor($color), $style)) {
imagealphablending($this->resource, false);
throw new RuntimeException('Draw chord operation failed');
}
} else {
foreach (array(IMG_ARC_NOFILL, IMG_ARC_NOFILL | IMG_ARC_CHORD) as $style) {
if (false === imagefilledarc($this->resource, $center->getX(), $center->getY(), $size->getWidth(), $size->getHeight(), $start, $end, $this->getColor($color), $style)) {
imagealphablending($this->resource, false);
throw new RuntimeException('Draw chord operation failed');
}
}
}
if (false === imagealphablending($this->resource, false)) {
throw new RuntimeException('Draw chord operation failed');
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Draw\DrawerInterface::circle()
*/
public function circle(PointInterface $center, $radius, ColorInterface $color, $fill = false, $thickness = 1)
{
$diameter = $radius * 2;
return $this->ellipse($center, new Box($diameter, $diameter), $color, $fill, $thickness);
}
/**
* {@inheritdoc}
*
* @see \Imagine\Draw\DrawerInterface::ellipse()
*/
public function ellipse(PointInterface $center, BoxInterface $size, ColorInterface $color, $fill = false, $thickness = 1)
{
$thickness = max(0, (int) round($thickness));
if ($thickness === 0 && !$fill) {
return $this;
}
if (function_exists('imageantialias')) {
imageantialias($this->resource, true);
}
imagesetthickness($this->resource, $thickness);
if ($fill) {
$callback = 'imagefilledellipse';
} else {
$callback = 'imageellipse';
}
if (function_exists('imageantialias')) {
imageantialias($this->resource, true);
}
if (false === imagealphablending($this->resource, true)) {
throw new RuntimeException('Draw ellipse operation failed');
}
if (function_exists('imageantialias')) {
imageantialias($this->resource, true);
}
if (false === $callback($this->resource, $center->getX(), $center->getY(), $size->getWidth(), $size->getHeight(), $this->getColor($color))) {
imagealphablending($this->resource, false);
throw new RuntimeException('Draw ellipse operation failed');
}
if (false === imagealphablending($this->resource, false)) {
throw new RuntimeException('Draw ellipse operation failed');
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Draw\DrawerInterface::line()
*/
public function line(PointInterface $start, PointInterface $end, ColorInterface $color, $thickness = 1)
{
$thickness = max(0, (int) round($thickness));
if ($thickness === 0) {
return $this;
}
imagesetthickness($this->resource, $thickness);
if (false === imagealphablending($this->resource, true)) {
throw new RuntimeException('Draw line operation failed');
}
if (false === imageline($this->resource, $start->getX(), $start->getY(), $end->getX(), $end->getY(), $this->getColor($color))) {
imagealphablending($this->resource, false);
throw new RuntimeException('Draw line operation failed');
}
if (false === imagealphablending($this->resource, false)) {
throw new RuntimeException('Draw line operation failed');
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Draw\DrawerInterface::pieSlice()
*/
public function pieSlice(PointInterface $center, BoxInterface $size, $start, $end, ColorInterface $color, $fill = false, $thickness = 1)
{
$thickness = max(0, (int) round($thickness));
if ($thickness === 0 && !$fill) {
return $this;
}
imagesetthickness($this->resource, $thickness);
if ($fill) {
$style = IMG_ARC_EDGED;
} else {
$style = IMG_ARC_EDGED | IMG_ARC_NOFILL;
}
if (false === imagealphablending($this->resource, true)) {
throw new RuntimeException('Draw chord operation failed');
}
if (false === imagefilledarc($this->resource, $center->getX(), $center->getY(), $size->getWidth(), $size->getHeight(), $start, $end, $this->getColor($color), $style)) {
imagealphablending($this->resource, false);
throw new RuntimeException('Draw chord operation failed');
}
if (false === imagealphablending($this->resource, false)) {
throw new RuntimeException('Draw chord operation failed');
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Draw\DrawerInterface::dot()
*/
public function dot(PointInterface $position, ColorInterface $color)
{
if (false === imagealphablending($this->resource, true)) {
throw new RuntimeException('Draw point operation failed');
}
if (false === imagesetpixel($this->resource, $position->getX(), $position->getY(), $this->getColor($color))) {
imagealphablending($this->resource, false);
throw new RuntimeException('Draw point operation failed');
}
if (false === imagealphablending($this->resource, false)) {
throw new RuntimeException('Draw point operation failed');
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Draw\DrawerInterface::rectangle()
*/
public function rectangle(PointInterface $leftTop, PointInterface $rightBottom, ColorInterface $color, $fill = false, $thickness = 1)
{
$thickness = max(0, (int) round($thickness));
if ($thickness === 0 && !$fill) {
return $this;
}
imagesetthickness($this->resource, $thickness);
$minX = min($leftTop->getX(), $rightBottom->getX());
$maxX = max($leftTop->getX(), $rightBottom->getX());
$minY = min($leftTop->getY(), $rightBottom->getY());
$maxY = max($leftTop->getY(), $rightBottom->getY());
if ($fill) {
$callback = 'imagefilledrectangle';
} else {
$callback = 'imagerectangle';
}
if (false === imagealphablending($this->resource, true)) {
throw new RuntimeException('Draw polygon operation failed');
}
if (false === $callback($this->resource, $minX, $minY, $maxX, $maxY, $this->getColor($color))) {
imagealphablending($this->resource, false);
throw new RuntimeException('Draw polygon operation failed');
}
if (false === imagealphablending($this->resource, false)) {
throw new RuntimeException('Draw polygon operation failed');
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Draw\DrawerInterface::polygon()
*/
public function polygon(array $coordinates, ColorInterface $color, $fill = false, $thickness = 1)
{
$thickness = max(0, (int) round($thickness));
if ($thickness === 0 && !$fill) {
return $this;
}
imagesetthickness($this->resource, $thickness);
if (count($coordinates) < 3) {
throw new InvalidArgumentException(sprintf('A polygon must consist of at least 3 points, %d given', count($coordinates)));
}
$points = call_user_func_array('array_merge', array_map(function (PointInterface $p) {
return array($p->getX(), $p->getY());
}, $coordinates));
if ($fill) {
$callback = 'imagefilledpolygon';
} else {
$callback = 'imagepolygon';
}
if (false === imagealphablending($this->resource, true)) {
throw new RuntimeException('Draw polygon operation failed');
}
if (false === $callback($this->resource, $points, count($coordinates), $this->getColor($color))) {
imagealphablending($this->resource, false);
throw new RuntimeException('Draw polygon operation failed');
}
if (false === imagealphablending($this->resource, false)) {
throw new RuntimeException('Draw polygon operation failed');
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Draw\DrawerInterface::text()
*/
public function text($string, AbstractFont $font, PointInterface $position, $angle = 0, $width = null)
{
if (!$this->info['FreeType Support']) {
throw new RuntimeException('GD is not compiled with FreeType support');
}
$angle = -1 * $angle;
$fontsize = $font->getSize();
$fontfile = $font->getFile();
$x = $position->getX();
$y = $position->getY() + $fontsize;
if ($width !== null) {
$string = $font->wrapText($string, $width, $angle);
}
if (false === imagealphablending($this->resource, true)) {
throw new RuntimeException('Font mask operation failed');
}
if ($fontfile && DIRECTORY_SEPARATOR === '\\') {
// On Windows imagefttext() throws a "Could not find/open font" error if $fontfile is not an absolute path.
$fontfileRealpath = realpath($fontfile);
if ($fontfileRealpath !== false) {
$fontfile = $fontfileRealpath;
}
}
if (false === imagefttext($this->resource, $fontsize, $angle, $x, $y, $this->getColor($font->getColor()), $fontfile, $string)) {
imagealphablending($this->resource, false);
throw new RuntimeException('Font mask operation failed');
}
if (false === imagealphablending($this->resource, false)) {
throw new RuntimeException('Font mask operation failed');
}
return $this;
}
/**
* Generates a GD color from Color instance.
*
* @param \Imagine\Image\Palette\Color\ColorInterface $color
*
* @throws \Imagine\Exception\RuntimeException
* @throws \Imagine\Exception\InvalidArgumentException
*
* @return resource
*/
private function getColor(ColorInterface $color)
{
if (!$color instanceof RGBColor) {
throw new InvalidArgumentException('GD driver only supports RGB colors');
}
$gdColor = imagecolorallocatealpha($this->resource, $color->getRed(), $color->getGreen(), $color->getBlue(), (100 - $color->getAlpha()) * 127 / 100);
if (false === $gdColor) {
throw new RuntimeException(sprintf('Unable to allocate color "RGB(%s, %s, %s)" with transparency of %d percent', $color->getRed(), $color->getGreen(), $color->getBlue(), $color->getAlpha()));
}
return $gdColor;
}
private function loadGdInfo()
{
if (!function_exists('gd_info')) {
throw new RuntimeException('Gd not installed');
}
$this->info = gd_info();
}
}

View File

@@ -0,0 +1,166 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Gd;
use Imagine\Effects\EffectsInterface;
use Imagine\Exception\InvalidArgumentException;
use Imagine\Exception\RuntimeException;
use Imagine\Image\Palette\Color\ColorInterface;
use Imagine\Image\Palette\Color\RGB as RGBColor;
use Imagine\Utils\Matrix;
/**
* Effects implementation using the GD PHP extension.
*/
class Effects implements EffectsInterface
{
/**
* @var resource
*/
private $resource;
/**
* Initialize the instance.
*
* @param resource $resource
*/
public function __construct($resource)
{
$this->resource = $resource;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Effects\EffectsInterface::gamma()
*/
public function gamma($correction)
{
if (false === imagegammacorrect($this->resource, 1.0, $correction)) {
throw new RuntimeException('Failed to apply gamma correction to the image');
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Effects\EffectsInterface::negative()
*/
public function negative()
{
if (false === imagefilter($this->resource, IMG_FILTER_NEGATE)) {
throw new RuntimeException('Failed to negate the image');
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Effects\EffectsInterface::grayscale()
*/
public function grayscale()
{
if (false === imagefilter($this->resource, IMG_FILTER_GRAYSCALE)) {
throw new RuntimeException('Failed to grayscale the image');
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Effects\EffectsInterface::colorize()
*/
public function colorize(ColorInterface $color)
{
if (!$color instanceof RGBColor) {
throw new RuntimeException('Colorize effects only accepts RGB color in GD context');
}
if (false === imagefilter($this->resource, IMG_FILTER_COLORIZE, $color->getRed(), $color->getGreen(), $color->getBlue())) {
throw new RuntimeException('Failed to colorize the image');
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Effects\EffectsInterface::sharpen()
*/
public function sharpen()
{
$sharpenMatrix = array(array(-1, -1, -1), array(-1, 16, -1), array(-1, -1, -1));
$divisor = array_sum(array_map('array_sum', $sharpenMatrix));
if (false === imageconvolution($this->resource, $sharpenMatrix, $divisor, 0)) {
throw new RuntimeException('Failed to sharpen the image');
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Effects\EffectsInterface::blur()
*/
public function blur($sigma = 1)
{
if (false === imagefilter($this->resource, IMG_FILTER_GAUSSIAN_BLUR)) {
throw new RuntimeException('Failed to blur the image');
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Effects\EffectsInterface::brightness()
*/
public function brightness($brightness)
{
$gdBrightness = (int) round($brightness / 100 * 255);
if ($gdBrightness < -255 || $gdBrightness > 255) {
throw new InvalidArgumentException(sprintf('The %1$s argument can range from %2$d to %3$d, but you specified %4$d.', '$brightness', -100, 100, $brightness));
}
if (false === imagefilter($this->resource, IMG_FILTER_BRIGHTNESS, $gdBrightness)) {
throw new RuntimeException('Failed to brightness the image');
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Effects\EffectsInterface::convolve()
*/
public function convolve(Matrix $matrix)
{
if ($matrix->getWidth() !== 3 || $matrix->getHeight() !== 3) {
throw new InvalidArgumentException(sprintf('A convolution matrix must be 3x3 (%dx%d provided).', $matrix->getWidth(), $matrix->getHeight()));
}
if (false === imageconvolution($this->resource, $matrix->getMatrix(), 1, 0)) {
throw new RuntimeException('Failed to convolve the image');
}
return $this;
}
}

50
vendor/imagine/imagine/src/Gd/Font.php vendored Normal file
View File

@@ -0,0 +1,50 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Gd;
use Imagine\Exception\RuntimeException;
use Imagine\Image\AbstractFont;
/**
* Font implementation using the GD library.
*/
final class Font extends AbstractFont
{
/**
* {@inheritdoc}
*
* @see \Imagine\Image\FontInterface::box()
*/
public function box($string, $angle = 0)
{
if (!function_exists('imageftbbox')) {
throw new RuntimeException('GD must have been compiled with `--with-freetype-dir` option to use the Font feature.');
}
$fontfile = $this->file;
if ($fontfile && DIRECTORY_SEPARATOR === '\\') {
// On Windows imageftbbox() throws a "Could not find/open font" error if $fontfile is not an absolute path.
$fontfileRealpath = realpath($fontfile);
if ($fontfileRealpath !== false) {
$fontfile = $fontfileRealpath;
}
}
$angle = -1 * $angle;
$info = imageftbbox($this->size, $angle, $fontfile, $string);
$xs = array($info[0], $info[2], $info[4], $info[6]);
$ys = array($info[1], $info[3], $info[5], $info[7]);
$width = abs(max($xs) - min($xs));
$height = abs(max($ys) - min($ys));
return $this->getClassFactory()->createBox($width, $height);
}
}

818
vendor/imagine/imagine/src/Gd/Image.php vendored Normal file
View File

@@ -0,0 +1,818 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Gd;
use Imagine\Exception\InvalidArgumentException;
use Imagine\Exception\OutOfBoundsException;
use Imagine\Exception\RuntimeException;
use Imagine\Factory\ClassFactoryInterface;
use Imagine\Image\AbstractImage;
use Imagine\Image\BoxInterface;
use Imagine\Image\Fill\FillInterface;
use Imagine\Image\ImageInterface;
use Imagine\Image\Metadata\MetadataBag;
use Imagine\Image\Palette\Color\ColorInterface;
use Imagine\Image\Palette\Color\RGB as RGBColor;
use Imagine\Image\Palette\PaletteInterface;
use Imagine\Image\Palette\RGB;
use Imagine\Image\Point;
use Imagine\Image\PointInterface;
use Imagine\Image\ProfileInterface;
use Imagine\Utils\ErrorHandling;
/**
* Image implementation using the GD library.
*/
final class Image extends AbstractImage
{
/**
* @var resource
*/
private $resource;
/**
* @var \Imagine\Gd\Layers|null
*/
private $layers;
/**
* @var \Imagine\Image\Palette\PaletteInterface
*/
private $palette;
/**
* Constructs a new Image instance.
*
* @param resource $resource
* @param \Imagine\Image\Palette\PaletteInterface $palette
* @param \Imagine\Image\Metadata\MetadataBag $metadata
*/
public function __construct($resource, PaletteInterface $palette, MetadataBag $metadata)
{
$this->metadata = $metadata;
$this->palette = $palette;
$this->resource = $resource;
}
/**
* Makes sure the current image resource is destroyed.
*/
public function __destruct()
{
if (is_resource($this->resource) && 'gd' === get_resource_type($this->resource)) {
imagedestroy($this->resource);
}
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\AbstractImage::__clone()
*/
public function __clone()
{
parent::__clone();
$size = $this->getSize();
$copy = $this->createImage($size, 'copy');
if (false === imagecopy($copy, $this->resource, 0, 0, 0, 0, $size->getWidth(), $size->getHeight())) {
imagedestroy($copy);
throw new RuntimeException('Image copy operation failed');
}
$this->resource = $copy;
$this->palette = clone $this->palette;
if ($this->layers !== null) {
$this->layers = $this->getClassFactory()->createLayers(ClassFactoryInterface::HANDLE_GD, $this, $this->layers->key());
}
}
/**
* Returns Gd resource.
*
* @return resource
*/
public function getGdResource()
{
return $this->resource;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ManipulatorInterface::copy()
*/
final public function copy()
{
return clone $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ManipulatorInterface::crop()
*/
final public function crop(PointInterface $start, BoxInterface $size)
{
if (!$start->in($this->getSize())) {
throw new OutOfBoundsException('Crop coordinates must start at minimum 0, 0 position from top left corner, crop height and width must be positive integers and must not exceed the current image borders');
}
$width = $size->getWidth();
$height = $size->getHeight();
$dest = $this->createImage($size, 'crop');
if (false === imagecopy($dest, $this->resource, 0, 0, $start->getX(), $start->getY(), $width, $height)) {
imagedestroy($dest);
throw new RuntimeException('Image crop operation failed');
}
imagedestroy($this->resource);
$this->resource = $dest;
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ManipulatorInterface::paste()
*/
final public function paste(ImageInterface $image, PointInterface $start, $alpha = 100)
{
if (!$image instanceof self) {
throw new InvalidArgumentException(sprintf('Gd\Image can only paste() Gd\Image instances, %s given', get_class($image)));
}
$alpha = (int) round($alpha);
if ($alpha < 0 || $alpha > 100) {
throw new InvalidArgumentException(sprintf('The %1$s argument can range from %2$d to %3$d, but you specified %4$d.', '$alpha', 0, 100, $alpha));
}
$size = $image->getSize();
if ($alpha === 100) {
imagealphablending($this->resource, true);
imagealphablending($image->resource, true);
$success = imagecopy($this->resource, $image->resource, $start->getX(), $start->getY(), 0, 0, $size->getWidth(), $size->getHeight());
imagealphablending($this->resource, false);
imagealphablending($image->resource, false);
if ($success === false) {
throw new RuntimeException('Image paste operation failed');
}
} elseif ($alpha > 0) {
if (false === imagecopymerge(/*dst_im*/$this->resource, /*src_im*/$image->resource, /*dst_x*/$start->getX(), /*dst_y*/$start->getY(), /*src_x*/0, /*src_y*/0, /*src_w*/$size->getWidth(), /*src_h*/$size->getHeight(), /*pct*/$alpha)) {
throw new RuntimeException('Image paste operation failed');
}
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ManipulatorInterface::resize()
*/
final public function resize(BoxInterface $size, $filter = ImageInterface::FILTER_UNDEFINED)
{
if (ImageInterface::FILTER_UNDEFINED !== $filter) {
throw new InvalidArgumentException('Unsupported filter type, GD only supports ImageInterface::FILTER_UNDEFINED filter');
}
$width = $size->getWidth();
$height = $size->getHeight();
$dest = $this->createImage($size, 'resize');
imagealphablending($this->resource, true);
imagealphablending($dest, true);
$success = imagecopyresampled($dest, $this->resource, 0, 0, 0, 0, $width, $height, imagesx($this->resource), imagesy($this->resource));
imagealphablending($this->resource, false);
imagealphablending($dest, false);
if ($success === false) {
imagedestroy($dest);
throw new RuntimeException('Image resize operation failed');
}
imagedestroy($this->resource);
$this->resource = $dest;
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ManipulatorInterface::rotate()
*/
final public function rotate($angle, ColorInterface $background = null)
{
if ($background === null) {
$background = $this->palette->color('fff');
}
$color = $this->getColor($background);
$resource = imagerotate($this->resource, -1 * $angle, $color);
if (false === $resource) {
throw new RuntimeException('Image rotate operation failed');
}
imagedestroy($this->resource);
$this->resource = $resource;
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ManipulatorInterface::save()
*/
final public function save($path = null, array $options = array())
{
$path = null === $path ? (isset($this->metadata['filepath']) ? $this->metadata['filepath'] : $path) : $path;
if (null === $path) {
throw new RuntimeException('You can omit save path only if image has been open from a file');
}
if (isset($options['format'])) {
$format = $options['format'];
} elseif ('' !== $extension = pathinfo($path, \PATHINFO_EXTENSION)) {
$format = $extension;
} else {
$originalPath = isset($this->metadata['filepath']) ? $this->metadata['filepath'] : null;
$format = pathinfo($originalPath, \PATHINFO_EXTENSION);
}
$this->saveOrOutput($format, $options, $path);
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ManipulatorInterface::show()
*/
public function show($format, array $options = array())
{
header('Content-type: ' . $this->getMimeType($format));
$this->saveOrOutput($format, $options);
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ImageInterface::get()
*/
public function get($format, array $options = array())
{
ob_start();
$this->saveOrOutput($format, $options);
return ob_get_clean();
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ImageInterface::__toString()
*/
public function __toString()
{
return $this->get('png');
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ManipulatorInterface::flipHorizontally()
*/
final public function flipHorizontally()
{
if (function_exists('imageflip')) {
imageflip($this->resource, IMG_FLIP_HORIZONTAL);
} else {
$size = $this->getSize();
$width = $size->getWidth();
$height = $size->getHeight();
$dest = $this->createImage($size, 'flip');
for ($i = 0; $i < $width; $i++) {
if (false === imagecopy($dest, $this->resource, $i, 0, ($width - 1) - $i, 0, 1, $height)) {
imagedestroy($dest);
throw new RuntimeException('Horizontal flip operation failed');
}
}
imagedestroy($this->resource);
$this->resource = $dest;
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ManipulatorInterface::flipVertically()
*/
final public function flipVertically()
{
if (function_exists('imageflip')) {
imageflip($this->resource, IMG_FLIP_VERTICAL);
} else {
$size = $this->getSize();
$width = $size->getWidth();
$height = $size->getHeight();
$dest = $this->createImage($size, 'flip');
for ($i = 0; $i < $height; $i++) {
if (false === imagecopy($dest, $this->resource, 0, $i, 0, ($height - 1) - $i, $width, 1)) {
imagedestroy($dest);
throw new RuntimeException('Vertical flip operation failed');
}
}
imagedestroy($this->resource);
$this->resource = $dest;
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ManipulatorInterface::strip()
*/
final public function strip()
{
// GD strips profiles and comment, so there's nothing to do here
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ImageInterface::draw()
*/
public function draw()
{
return $this->getClassFactory()->createDrawer(ClassFactoryInterface::HANDLE_GD, $this->resource);
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ImageInterface::effects()
*/
public function effects()
{
return $this->getClassFactory()->createEffects(ClassFactoryInterface::HANDLE_GD, $this->resource);
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ImageInterface::getSize()
*/
public function getSize()
{
return $this->getClassFactory()->createBox(imagesx($this->resource), imagesy($this->resource));
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ManipulatorInterface::applyMask()
*/
public function applyMask(ImageInterface $mask)
{
if (!$mask instanceof self) {
throw new InvalidArgumentException('Cannot mask non-gd images');
}
$size = $this->getSize();
$maskSize = $mask->getSize();
if ($size != $maskSize) {
throw new InvalidArgumentException(sprintf('The given mask doesn\'t match current image\'s size, Current mask\'s dimensions are %s, while image\'s dimensions are %s', $maskSize, $size));
}
for ($x = 0, $width = $size->getWidth(); $x < $width; $x++) {
for ($y = 0, $height = $size->getHeight(); $y < $height; $y++) {
$position = new Point($x, $y);
$color = $this->getColorAt($position);
$maskColor = $mask->getColorAt($position);
$round = (int) round(max($color->getAlpha(), (100 - $color->getAlpha()) * $maskColor->getRed() / 255));
if (false === imagesetpixel($this->resource, $x, $y, $this->getColor($color->dissolve($round - $color->getAlpha())))) {
throw new RuntimeException('Apply mask operation failed');
}
}
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ManipulatorInterface::fill()
*/
public function fill(FillInterface $fill)
{
$size = $this->getSize();
for ($x = 0, $width = $size->getWidth(); $x < $width; $x++) {
for ($y = 0, $height = $size->getHeight(); $y < $height; $y++) {
if (false === imagesetpixel($this->resource, $x, $y, $this->getColor($fill->getColor(new Point($x, $y))))) {
throw new RuntimeException('Fill operation failed');
}
}
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ImageInterface::mask()
*/
public function mask()
{
$mask = $this->copy();
if (false === imagefilter($mask->resource, IMG_FILTER_GRAYSCALE)) {
throw new RuntimeException('Mask operation failed');
}
return $mask;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ImageInterface::histogram()
*/
public function histogram()
{
$size = $this->getSize();
$colors = array();
for ($x = 0, $width = $size->getWidth(); $x < $width; $x++) {
for ($y = 0, $height = $size->getHeight(); $y < $height; $y++) {
$colors[] = $this->getColorAt(new Point($x, $y));
}
}
return array_unique($colors);
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ImageInterface::getColorAt()
*/
public function getColorAt(PointInterface $point)
{
if (!$point->in($this->getSize())) {
throw new RuntimeException(sprintf('Error getting color at point [%s,%s]. The point must be inside the image of size [%s,%s]', $point->getX(), $point->getY(), $this->getSize()->getWidth(), $this->getSize()->getHeight()));
}
$index = imagecolorat($this->resource, $point->getX(), $point->getY());
$info = imagecolorsforindex($this->resource, $index);
return $this->palette->color(array($info['red'], $info['green'], $info['blue']), max(min(100 - (int) round($info['alpha'] / 127 * 100), 100), 0));
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ImageInterface::layers()
*/
public function layers()
{
if (null === $this->layers) {
$this->layers = $this->getClassFactory()->createLayers(ClassFactoryInterface::HANDLE_GD, $this);
}
return $this->layers;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ImageInterface::interlace()
*/
public function interlace($scheme)
{
static $supportedInterlaceSchemes = array(
ImageInterface::INTERLACE_NONE => 0,
ImageInterface::INTERLACE_LINE => 1,
ImageInterface::INTERLACE_PLANE => 1,
ImageInterface::INTERLACE_PARTITION => 1,
);
if (!array_key_exists($scheme, $supportedInterlaceSchemes)) {
throw new InvalidArgumentException('Unsupported interlace type');
}
imageinterlace($this->resource, $supportedInterlaceSchemes[$scheme]);
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ImageInterface::palette()
*/
public function palette()
{
return $this->palette;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ImageInterface::profile()
*/
public function profile(ProfileInterface $profile)
{
throw new RuntimeException('GD driver does not support color profiles');
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ImageInterface::usePalette()
*/
public function usePalette(PaletteInterface $palette)
{
if (!$palette instanceof RGB) {
throw new RuntimeException('GD driver only supports RGB palette');
}
$this->palette = $palette;
return $this;
}
/**
* Performs save or show operation using one of GD's image... functions.
*
* @param string $format
* @param array $options
* @param string $filename
*
* @throws \Imagine\Exception\InvalidArgumentException
* @throws \Imagine\Exception\RuntimeException
*/
private function saveOrOutput($format, array $options, $filename = null)
{
$format = $this->normalizeFormat($format);
if (!$this->supported($format)) {
throw new InvalidArgumentException(sprintf('Saving image in "%s" format is not supported, please use one of the following extensions: "%s"', $format, implode('", "', $this->supported())));
}
$save = 'image' . $format;
$args = array(&$this->resource, $filename);
switch ($format) {
case 'bmp':
if (isset($options['compressed'])) {
$args[] = (bool) $options['compressed'];
}
break;
case 'jpeg':
if (!isset($options['jpeg_quality'])) {
if (isset($options['quality'])) {
$options['jpeg_quality'] = $options['quality'];
}
}
if (isset($options['jpeg_quality'])) {
$args[] = $options['jpeg_quality'];
}
break;
case 'png':
if (!isset($options['png_compression_level'])) {
if (isset($options['quality'])) {
$options['png_compression_level'] = round((100 - $options['quality']) * 9 / 100);
}
}
if (isset($options['png_compression_level'])) {
if ($options['png_compression_level'] < 0 || $options['png_compression_level'] > 9) {
throw new InvalidArgumentException('png_compression_level option should be an integer from 0 to 9');
}
$args[] = $options['png_compression_level'];
} else {
$args[] = -1; // use default level
}
if (!isset($options['png_compression_filter'])) {
if (isset($options['filters'])) {
$options['png_compression_filter'] = $options['filters'];
}
}
if (isset($options['png_compression_filter'])) {
if (~PNG_ALL_FILTERS & $options['png_compression_filter']) {
throw new InvalidArgumentException('png_compression_filter option should be a combination of the PNG_FILTER_XXX constants');
}
$args[] = $options['png_compression_filter'];
}
break;
case 'wbmp':
case 'xbm':
if (isset($options['foreground'])) {
$args[] = $options['foreground'];
}
break;
case 'webp':
if (!isset($options['webp_quality'])) {
if (isset($options['quality'])) {
$options['webp_quality'] = $options['quality'];
}
}
if (isset($options['webp_quality'])) {
if ($options['webp_quality'] < 0 || $options['webp_quality'] > 100) {
throw new InvalidArgumentException('webp_quality option should be an integer from 0 to 100');
}
$args[] = $options['webp_quality'];
}
break;
}
ErrorHandling::throwingRuntimeException(E_WARNING | E_NOTICE, function () use ($save, $args) {
if (false === call_user_func_array($save, $args)) {
throw new RuntimeException('Save operation failed');
}
});
}
/**
* Generates a GD image.
*
* @param \Imagine\Image\BoxInterface $size
* @param string $operation the operation initiating the creation
*
* @throws \Imagine\Exception\RuntimeException
*
* @return resource
*/
private function createImage(BoxInterface $size, $operation)
{
$resource = imagecreatetruecolor($size->getWidth(), $size->getHeight());
if (false === $resource) {
throw new RuntimeException('Image ' . $operation . ' failed');
}
if (false === imagealphablending($resource, false) || false === imagesavealpha($resource, true)) {
throw new RuntimeException('Image ' . $operation . ' failed');
}
if (function_exists('imageantialias')) {
imageantialias($resource, true);
}
$transparent = imagecolorallocatealpha($resource, 255, 255, 255, 127);
imagefill($resource, 0, 0, $transparent);
imagecolortransparent($resource, $transparent);
return $resource;
}
/**
* Generates a GD color from Color instance.
*
* @param \Imagine\Image\Palette\Color\ColorInterface $color
*
* @throws \Imagine\Exception\RuntimeException
* @throws \Imagine\Exception\InvalidArgumentException
*
* @return int A color identifier
*/
private function getColor(ColorInterface $color)
{
if (!$color instanceof RGBColor) {
throw new InvalidArgumentException('GD driver only supports RGB colors');
}
$index = imagecolorallocatealpha($this->resource, $color->getRed(), $color->getGreen(), $color->getBlue(), round(127 * (100 - $color->getAlpha()) / 100));
if (false === $index) {
throw new RuntimeException(sprintf('Unable to allocate color "RGB(%s, %s, %s)" with transparency of %d percent', $color->getRed(), $color->getGreen(), $color->getBlue(), $color->getAlpha()));
}
return $index;
}
/**
* Normalizes a given format name.
*
* @param string $format
*
* @return string
*/
private function normalizeFormat($format)
{
$format = strtolower($format);
if ('jpg' === $format || 'pjpeg' === $format || 'jfif' === $format) {
$format = 'jpeg';
}
return $format;
}
/**
* Checks whether a given format is supported by GD library.
*
* @param string $format
*
* @return bool
*/
private function supported($format = null)
{
$formats = self::getSupportedFormats();
if (null === $format) {
return array_keys($formats);
}
return is_string($format) && isset($formats[$format]);
}
/**
* Get the mime type based on format.
*
* @param string $format
*
* @throws \Imagine\Exception\RuntimeException
*
* @return string mime-type
*/
private function getMimeType($format)
{
$format = $this->normalizeFormat($format);
$formats = self::getSupportedFormats();
if (!isset($formats[$format])) {
throw new RuntimeException('Invalid format');
}
return $formats[$format]['mimeType'];
}
/**
* @return array
*/
private static function getSupportedFormats()
{
static $supportedFormats;
if (!isset($supportedFormats)) {
$supportedFormats = array(
'gif' => array('mimeType' => 'image/gif'),
'jpeg' => array('mimeType' => 'image/jpeg'),
'png' => array('mimeType' => 'image/png'),
'wbmp' => array('mimeType' => 'image/vnd.wap.wbmp'),
'xbm' => array('mimeType' => 'image/xbm'),
);
if (function_exists('imagebmp')) {
$supportedFormats['bmp'] = array('mimeType' => 'image/bmp');
}
if (function_exists('imagewebp')) {
$supportedFormats['webp'] = array('mimeType' => 'image/webp');
}
ksort($supportedFormats);
}
return $supportedFormats;
}
}

View File

@@ -0,0 +1,250 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Gd;
use Imagine\Exception\InvalidArgumentException;
use Imagine\Exception\RuntimeException;
use Imagine\Factory\ClassFactoryInterface;
use Imagine\File\LoaderInterface;
use Imagine\Image\AbstractImagine;
use Imagine\Image\BoxInterface;
use Imagine\Image\Metadata\MetadataBag;
use Imagine\Image\Palette\Color\ColorInterface;
use Imagine\Image\Palette\Color\RGB as RGBColor;
use Imagine\Image\Palette\PaletteInterface;
use Imagine\Image\Palette\RGB;
use Imagine\Utils\ErrorHandling;
/**
* Imagine implementation using the GD library.
*/
final class Imagine extends AbstractImagine
{
/**
* Initialize the class.
*/
public function __construct()
{
$this->requireGdVersion('2.0.1');
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ImagineInterface::create()
*/
public function create(BoxInterface $size, ColorInterface $color = null)
{
$width = $size->getWidth();
$height = $size->getHeight();
$resource = imagecreatetruecolor($width, $height);
if (false === $resource) {
throw new RuntimeException('Create operation failed');
}
$palette = null !== $color ? $color->getPalette() : new RGB();
$color = $color ? $color : $palette->color('fff');
if (!$color instanceof RGBColor) {
throw new InvalidArgumentException('GD driver only supports RGB colors');
}
$index = imagecolorallocatealpha($resource, $color->getRed(), $color->getGreen(), $color->getBlue(), round(127 * (100 - $color->getAlpha()) / 100));
if (false === $index) {
throw new RuntimeException('Unable to allocate color');
}
if (false === imagefill($resource, 0, 0, $index)) {
throw new RuntimeException('Could not set background color fill');
}
if ($color->getAlpha() <= 5) {
imagecolortransparent($resource, $index);
}
return $this->wrap($resource, $palette, new MetadataBag());
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ImagineInterface::open()
*/
public function open($path)
{
$loader = $path instanceof LoaderInterface ? $path : $this->getClassFactory()->createFileLoader($path);
$path = $loader->getPath();
$data = $loader->getData();
$resource = $this->createImageFromString($data);
if (!\is_resource($resource)) {
throw new RuntimeException(sprintf('Unable to open image %s', $path));
}
return $this->wrap($resource, new RGB(), $this->getMetadataReader()->readFile($loader));
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ImagineInterface::load()
*/
public function load($string)
{
return $this->doLoad($string, $this->getMetadataReader()->readData($string));
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ImagineInterface::read()
*/
public function read($resource)
{
if (!\is_resource($resource)) {
throw new InvalidArgumentException('Variable does not contain a stream resource');
}
$content = stream_get_contents($resource);
if (false === $content) {
throw new InvalidArgumentException('Cannot read resource content');
}
return $this->doLoad($content, $this->getMetadataReader()->readData($content, $resource));
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ImagineInterface::font()
*/
public function font($file, $size, ColorInterface $color)
{
return $this->getClassFactory()->createFont(ClassFactoryInterface::HANDLE_GD, $file, $size, $color);
}
/**
* @param resource $resource
* @param \Imagine\Image\Palette\PaletteInterface $palette
* @param \Imagine\Image\Metadata\MetadataBag $metadata
*
* @throws \Imagine\Exception\RuntimeException
*
* @return \Imagine\Image\ImageInterface
*/
private function wrap($resource, PaletteInterface $palette, MetadataBag $metadata)
{
if (!imageistruecolor($resource)) {
if (\function_exists('imagepalettetotruecolor')) {
if (false === imagepalettetotruecolor($resource)) {
throw new RuntimeException('Could not convert a palette based image to true color');
}
} else {
list($width, $height) = array(imagesx($resource), imagesy($resource));
// create transparent truecolor canvas
$truecolor = imagecreatetruecolor($width, $height);
$transparent = imagecolorallocatealpha($truecolor, 255, 255, 255, 127);
imagealphablending($truecolor, false);
imagefilledrectangle($truecolor, 0, 0, $width, $height, $transparent);
imagealphablending($truecolor, false);
imagecopy($truecolor, $resource, 0, 0, 0, 0, $width, $height);
imagedestroy($resource);
$resource = $truecolor;
}
}
if (false === imagealphablending($resource, false) || false === imagesavealpha($resource, true)) {
throw new RuntimeException('Could not set alphablending, savealpha and antialias values');
}
if (\function_exists('imageantialias')) {
imageantialias($resource, true);
}
return $this->getClassFactory()->createImage(ClassFactoryInterface::HANDLE_GD, $resource, $palette, $metadata);
}
/**
* @param string $version
*
* @throws \Imagine\Exception\RuntimeException
*/
private function requireGdVersion($version)
{
if (!\function_exists('gd_info')) {
throw new RuntimeException('Gd not installed');
}
if (version_compare(GD_VERSION, $version, '<')) {
throw new RuntimeException(sprintf('GD2 version %s or higher is required, %s provided', $version, GD_VERSION));
}
}
/**
* @param string $string
* @param \Imagine\Image\Metadata\MetadataBag $metadata
*
* @throws \Imagine\Exception\RuntimeException
*
* @return \Imagine\Image\ImageInterface
*/
private function doLoad($string, MetadataBag $metadata)
{
$resource = $this->createImageFromString($string);
if (!\is_resource($resource)) {
throw new RuntimeException('An image could not be created from the given input');
}
return $this->wrap($resource, new RGB(), $metadata);
}
/**
* Check if the raw image data represents an image in WebP format.
*
* @param string $data
*
* @return bool
*/
private function isWebP(&$data)
{
return substr($data, 8, 7) === 'WEBPVP8';
}
/**
* Create an image resource starting from its raw daa.
*
* @param string $string
*
* @return resource|false
*/
private function createImageFromString(&$string)
{
return ErrorHandling::ignoring(-1, function () use (&$string) {
// imagecreatefromstring() does not support webp images before PHP 7.3.0
if (PHP_VERSION_ID < 70300 && function_exists('imagecreatefromwebp') && $this->isWebP($string)) {
return @imagecreatefromwebp('data:image/webp;base64,' . base64_encode($string));
}
return @imagecreatefromstring($string);
});
}
}

195
vendor/imagine/imagine/src/Gd/Layers.php vendored Normal file
View File

@@ -0,0 +1,195 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Gd;
use Imagine\Exception\NotSupportedException;
use Imagine\Exception\RuntimeException;
use Imagine\Factory\ClassFactoryInterface;
use Imagine\Image\AbstractLayers;
use Imagine\Image\Metadata\MetadataBag;
use Imagine\Image\Palette\PaletteInterface;
class Layers extends AbstractLayers
{
/**
* @var \Imagine\Gd\Image
*/
private $image;
/**
* @var int
*/
private $offset;
/**
* @var resource
*/
private $resource;
/**
* @var \Imagine\Image\Palette\PaletteInterface
*/
private $palette;
/**
* @param \Imagine\Gd\Image $image
* @param \Imagine\Image\Palette\PaletteInterface $palette
* @param resource $resource
* @param int $initialOffset
*
* @throws \Imagine\Exception\RuntimeException
*/
public function __construct(Image $image, PaletteInterface $palette, $resource, $initialOffset = 0)
{
if (!is_resource($resource)) {
throw new RuntimeException('Invalid Gd resource provided');
}
$this->image = $image;
$this->resource = $resource;
$this->offset = (int) $initialOffset;
$this->palette = $palette;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\LayersInterface::merge()
*/
public function merge()
{
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\LayersInterface::coalesce()
*/
public function coalesce()
{
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\LayersInterface::animate()
*/
public function animate($format, $delay, $loops)
{
return $this;
}
/**
* {@inheritdoc}
*
* @see \Iterator::current()
*/
public function current()
{
return $this->getClassFactory()->createImage(ClassFactoryInterface::HANDLE_GD, $this->resource, $this->palette, new MetadataBag());
}
/**
* {@inheritdoc}
*
* @see \Iterator::key()
*/
public function key()
{
return $this->offset;
}
/**
* {@inheritdoc}
*
* @see \Iterator::next()
*/
public function next()
{
++$this->offset;
}
/**
* {@inheritdoc}
*
* @see \Iterator::rewind()
*/
public function rewind()
{
$this->offset = 0;
}
/**
* {@inheritdoc}
*
* @see \Iterator::valid()
*/
public function valid()
{
return $this->offset < 1;
}
/**
* {@inheritdoc}
*
* @see \Countable::count()
*/
public function count()
{
return 1;
}
/**
* {@inheritdoc}
*
* @see \ArrayAccess::offsetExists()
*/
public function offsetExists($offset)
{
return 0 === $offset;
}
/**
* {@inheritdoc}
*
* @see \ArrayAccess::offsetGet()
*/
public function offsetGet($offset)
{
if (0 === $offset) {
return $this->getClassFactory()->createImage(ClassFactoryInterface::HANDLE_GD, $this->resource, $this->palette, new MetadataBag());
}
throw new RuntimeException('GD only supports one layer at offset 0');
}
/**
* {@inheritdoc}
*
* @see \ArrayAccess::offsetSet()
*/
public function offsetSet($offset, $value)
{
throw new NotSupportedException('GD does not support layer set');
}
/**
* {@inheritdoc}
*
* @see \ArrayAccess::offsetUnset()
*/
public function offsetUnset($offset)
{
throw new NotSupportedException('GD does not support layer unset');
}
}

View File

@@ -0,0 +1,445 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Gmagick;
use Imagine\Draw\DrawerInterface;
use Imagine\Exception\InvalidArgumentException;
use Imagine\Exception\RuntimeException;
use Imagine\Image\AbstractFont;
use Imagine\Image\Box;
use Imagine\Image\BoxInterface;
use Imagine\Image\Palette\Color\ColorInterface;
use Imagine\Image\Point;
use Imagine\Image\PointInterface;
/**
* Drawer implementation using the Gmagick PHP extension.
*/
final class Drawer implements DrawerInterface
{
/**
* @var \Gmagick
*/
private $gmagick;
/**
* @param \Gmagick $gmagick
*/
public function __construct(\Gmagick $gmagick)
{
$this->gmagick = $gmagick;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Draw\DrawerInterface::arc()
*/
public function arc(PointInterface $center, BoxInterface $size, $start, $end, ColorInterface $color, $thickness = 1)
{
$thickness = max(0, (int) round($thickness));
if ($thickness === 0) {
return $this;
}
$x = $center->getX();
$y = $center->getY();
$width = $size->getWidth();
$height = $size->getHeight();
try {
$pixel = $this->getColor($color);
$arc = new \GmagickDraw();
$arc->setstrokecolor($pixel);
$arc->setstrokewidth($thickness);
$arc->setfillcolor('transparent');
$arc->arc(
$x - $width / 2,
$y - $height / 2,
$x + $width / 2,
$y + $height / 2,
$start,
$end
);
$this->gmagick->drawImage($arc);
$pixel = null;
$arc = null;
} catch (\GmagickException $e) {
throw new RuntimeException('Draw arc operation failed', $e->getCode(), $e);
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Draw\DrawerInterface::chord()
*/
public function chord(PointInterface $center, BoxInterface $size, $start, $end, ColorInterface $color, $fill = false, $thickness = 1)
{
$thickness = max(0, (int) round($thickness));
if ($thickness === 0 && !$fill) {
return $this;
}
$x = $center->getX();
$y = $center->getY();
$width = $size->getWidth();
$height = $size->getHeight();
try {
$pixel = $this->getColor($color);
$chord = new \GmagickDraw();
$chord->setstrokecolor($pixel);
$chord->setstrokewidth($thickness);
if ($fill) {
$chord->setfillcolor($pixel);
} else {
$x1 = round($x + $width / 2 * cos(deg2rad($start)));
$y1 = round($y + $height / 2 * sin(deg2rad($start)));
$x2 = round($x + $width / 2 * cos(deg2rad($end)));
$y2 = round($y + $height / 2 * sin(deg2rad($end)));
$this->line(new Point($x1, $y1), new Point($x2, $y2), $color, $thickness);
$chord->setfillcolor('transparent');
}
$chord->arc($x - $width / 2, $y - $height / 2, $x + $width / 2, $y + $height / 2, $start, $end);
$this->gmagick->drawImage($chord);
$pixel = null;
$chord = null;
} catch (\GmagickException $e) {
throw new RuntimeException('Draw chord operation failed', $e->getCode(), $e);
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Draw\DrawerInterface::circle()
*/
public function circle(PointInterface $center, $radius, ColorInterface $color, $fill = false, $thickness = 1)
{
$diameter = $radius * 2;
return $this->ellipse($center, new Box($diameter, $diameter), $color, $fill, $thickness);
}
/**
* {@inheritdoc}
*
* @see \Imagine\Draw\DrawerInterface::ellipse()
*/
public function ellipse(PointInterface $center, BoxInterface $size, ColorInterface $color, $fill = false, $thickness = 1)
{
$thickness = max(0, (int) round($thickness));
if ($thickness === 0 && !$fill) {
return $this;
}
$width = $size->getWidth();
$height = $size->getHeight();
try {
$pixel = $this->getColor($color);
$ellipse = new \GmagickDraw();
$ellipse->setstrokecolor($pixel);
$ellipse->setstrokewidth($thickness);
if ($fill) {
$ellipse->setfillcolor($pixel);
} else {
$ellipse->setfillcolor('transparent');
}
$ellipse->ellipse(
$center->getX(),
$center->getY(),
$width / 2,
$height / 2,
0, 360
);
$this->gmagick->drawImage($ellipse);
$pixel = null;
$ellipse = null;
} catch (\GmagickException $e) {
throw new RuntimeException('Draw ellipse operation failed', $e->getCode(), $e);
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Draw\DrawerInterface::line()
*/
public function line(PointInterface $start, PointInterface $end, ColorInterface $color, $thickness = 1)
{
$thickness = max(0, (int) round($thickness));
if ($thickness === 0) {
return $this;
}
try {
$pixel = $this->getColor($color);
$line = new \GmagickDraw();
$line->setstrokecolor($pixel);
$line->setstrokewidth($thickness);
$line->setfillcolor($pixel);
$line->line(
$start->getX(),
$start->getY(),
$end->getX(),
$end->getY()
);
$this->gmagick->drawImage($line);
$pixel = null;
$line = null;
} catch (\GmagickException $e) {
throw new RuntimeException('Draw line operation failed', $e->getCode(), $e);
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Draw\DrawerInterface::pieSlice()
*/
public function pieSlice(PointInterface $center, BoxInterface $size, $start, $end, ColorInterface $color, $fill = false, $thickness = 1)
{
$thickness = max(0, (int) round($thickness));
if ($thickness === 0 && !$fill) {
return $this;
}
$width = $size->getWidth();
$height = $size->getHeight();
$x1 = round($center->getX() + $width / 2 * cos(deg2rad($start)));
$y1 = round($center->getY() + $height / 2 * sin(deg2rad($start)));
$x2 = round($center->getX() + $width / 2 * cos(deg2rad($end)));
$y2 = round($center->getY() + $height / 2 * sin(deg2rad($end)));
if ($fill) {
$this->chord($center, $size, $start, $end, $color, true, $thickness);
$this->polygon(
array(
$center,
new Point($x1, $y1),
new Point($x2, $y2),
),
$color,
true,
$thickness
);
} else {
$this->arc($center, $size, $start, $end, $color, $thickness);
$this->line($center, new Point($x1, $y1), $color, $thickness);
$this->line($center, new Point($x2, $y2), $color, $thickness);
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Draw\DrawerInterface::dot()
*/
public function dot(PointInterface $position, ColorInterface $color)
{
$x = $position->getX();
$y = $position->getY();
try {
$pixel = $this->getColor($color);
$point = new \GmagickDraw();
$point->setfillcolor($pixel);
$point->point($x, $y);
$this->gmagick->drawimage($point);
$pixel = null;
$point = null;
} catch (\GmagickException $e) {
throw new RuntimeException('Draw point operation failed', $e->getCode(), $e);
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Draw\DrawerInterface::rectangle()
*/
public function rectangle(PointInterface $leftTop, PointInterface $rightBottom, ColorInterface $color, $fill = false, $thickness = 1)
{
$thickness = max(0, (int) round($thickness));
if ($thickness === 0 && !$fill) {
return $this;
}
$minX = min($leftTop->getX(), $rightBottom->getX());
$maxX = max($leftTop->getX(), $rightBottom->getX());
$minY = min($leftTop->getY(), $rightBottom->getY());
$maxY = max($leftTop->getY(), $rightBottom->getY());
try {
$pixel = $this->getColor($color);
$rectangle = new \GmagickDraw();
$rectangle->setstrokecolor($pixel);
$rectangle->setstrokewidth($thickness);
if ($fill) {
$rectangle->setfillcolor($pixel);
} else {
$rectangle->setfillcolor('transparent');
}
$rectangle->rectangle($minX, $minY, $maxX, $maxY);
$this->gmagick->drawImage($rectangle);
} catch (\GmagickException $e) {
throw new RuntimeException('Draw polygon operation failed', $e->getCode(), $e);
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Draw\DrawerInterface::polygon()
*/
public function polygon(array $coordinates, ColorInterface $color, $fill = false, $thickness = 1)
{
if (count($coordinates) < 3) {
throw new InvalidArgumentException(sprintf('Polygon must consist of at least 3 coordinates, %d given', count($coordinates)));
}
$thickness = max(0, (int) round($thickness));
if ($thickness === 0 && !$fill) {
return $this;
}
$points = array_map(function (PointInterface $p) {
return array('x' => $p->getX(), 'y' => $p->getY());
}, $coordinates);
try {
$pixel = $this->getColor($color);
$polygon = new \GmagickDraw();
$polygon->setstrokecolor($pixel);
$polygon->setstrokewidth($thickness);
if ($fill) {
$polygon->setfillcolor($pixel);
} else {
$polygon->setfillcolor('transparent');
}
$polygon->polygon($points);
$this->gmagick->drawImage($polygon);
unset($pixel, $polygon);
} catch (\GmagickException $e) {
throw new RuntimeException('Draw polygon operation failed', $e->getCode(), $e);
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Draw\DrawerInterface::text()
*/
public function text($string, AbstractFont $font, PointInterface $position, $angle = 0, $width = null)
{
try {
$pixel = $this->getColor($font->getColor());
$text = new \GmagickDraw();
$text->setfont($font->getFile());
/*
* @see http://www.php.net/manual/en/imagick.queryfontmetrics.php#101027
*
* ensure font resolution is the same as GD's hard-coded 96
*/
$text->setfontsize((int) ($font->getSize() * (96 / 72)));
$text->setfillcolor($pixel);
if ($width !== null) {
$string = $font->wrapText($string, $width, $angle);
}
$info = $this->gmagick->queryfontmetrics($text, $string);
$rad = deg2rad($angle);
$cos = cos($rad);
$sin = sin($rad);
$x1 = round(0 * $cos - 0 * $sin);
$x2 = round($info['textWidth'] * $cos - $info['textHeight'] * $sin);
$y1 = round(0 * $sin + 0 * $cos);
$y2 = round($info['textWidth'] * $sin + $info['textHeight'] * $cos);
$xdiff = 0 - min($x1, $x2);
$ydiff = 0 - min($y1, $y2);
$this->gmagick->annotateimage($text, $position->getX() + $x1 + $xdiff, $position->getY() + $y2 + $ydiff, $angle, $string);
unset($pixel, $text);
} catch (\GmagickException $e) {
throw new RuntimeException('Draw text operation failed', $e->getCode(), $e);
}
return $this;
}
/**
* Gets specifically formatted color string from Color instance.
*
* @param \Imagine\Image\Palette\Color\ColorInterface $color
*
* @throws \Imagine\Exception\InvalidArgumentException In case a non-opaque color is passed
*
* @return \GmagickPixel
*/
private function getColor(ColorInterface $color)
{
if (!$color->isOpaque()) {
throw new InvalidArgumentException('Gmagick doesn\'t support transparency');
}
return new \GmagickPixel((string) $color);
}
}

View File

@@ -0,0 +1,177 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Gmagick;
use Imagine\Effects\EffectsInterface;
use Imagine\Exception\InvalidArgumentException;
use Imagine\Exception\NotSupportedException;
use Imagine\Exception\RuntimeException;
use Imagine\Image\Palette\Color\ColorInterface;
use Imagine\Utils\Matrix;
/**
* Effects implementation using the Gmagick PHP extension.
*/
class Effects implements EffectsInterface
{
/**
* @var \Gmagick
*/
private $gmagick;
/**
* Initialize the instance.
*
* @param \Gmagick $gmagick
*/
public function __construct(\Gmagick $gmagick)
{
$this->gmagick = $gmagick;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Effects\EffectsInterface::gamma()
*/
public function gamma($correction)
{
try {
$this->gmagick->gammaimage($correction);
} catch (\GmagickException $e) {
throw new RuntimeException('Failed to apply gamma correction to the image', $e->getCode(), $e);
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Effects\EffectsInterface::negative()
*/
public function negative()
{
if (!method_exists($this->gmagick, 'negateimage')) {
throw new NotSupportedException('Gmagick version 1.1.0 RC3 is required for negative effect');
}
try {
$this->gmagick->negateimage(false, \Gmagick::CHANNEL_ALL);
} catch (\GmagickException $e) {
throw new RuntimeException('Failed to negate the image', $e->getCode(), $e);
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Effects\EffectsInterface::grayscale()
*/
public function grayscale()
{
try {
$this->gmagick->setImageType(2);
} catch (\GmagickException $e) {
throw new RuntimeException('Failed to grayscale the image', $e->getCode(), $e);
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Effects\EffectsInterface::colorize()
*/
public function colorize(ColorInterface $color)
{
throw new NotSupportedException('Gmagick does not support colorize');
}
/**
* {@inheritdoc}
*
* @see \Imagine\Effects\EffectsInterface::sharpen()
*/
public function sharpen()
{
throw new NotSupportedException('Gmagick does not support sharpen yet');
}
/**
* {@inheritdoc}
*
* @see \Imagine\Effects\EffectsInterface::blur()
*/
public function blur($sigma = 1)
{
try {
$this->gmagick->blurImage(0, $sigma);
} catch (\GmagickException $e) {
throw new RuntimeException('Failed to blur the image', $e->getCode(), $e);
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Effects\EffectsInterface::brightness()
*/
public function brightness($brightness)
{
$brightness = (int) round($brightness);
if ($brightness < -100 || $brightness > 100) {
throw new InvalidArgumentException(sprintf('The %1$s argument can range from %2$d to %3$d, but you specified %4$d.', '$brightness', -100, 100, $brightness));
}
try {
// This *emulates* setting the brightness
$sign = $brightness < 0 ? -1 : 1;
$v = abs($brightness) / 100;
if ($sign > 0) {
$v = (2 / (sin(($v * .99999 * M_PI_2) + M_PI_2))) - 2;
}
$this->gmagick->modulateimage(100 + $sign * $v * 100, 100, 100);
} catch (\GmagickException $e) {
throw new RuntimeException('Failed to brightness the image');
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Effects\EffectsInterface::convolve()
*/
public function convolve(Matrix $matrix)
{
if (!method_exists($this->gmagick, 'convolveimage')) {
// convolveimage has been added in gmagick 2.0.1RC2
throw new NotSupportedException('The version of Gmagick extension is too old: it does not support convolve.');
}
if ($matrix->getWidth() !== 3 || $matrix->getHeight() !== 3) {
throw new InvalidArgumentException(sprintf('A convolution matrix must be 3x3 (%dx%d provided).', $matrix->getWidth(), $matrix->getHeight()));
}
try {
$this->gmagick->convolveimage($matrix->getValueList());
} catch (\ImagickException $e) {
throw new RuntimeException('Failed to convolve the image');
}
return $this;
}
}

View File

@@ -0,0 +1,64 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Gmagick;
use Imagine\Image\AbstractFont;
use Imagine\Image\Palette\Color\ColorInterface;
/**
* Font implementation using the Gmagick PHP extension.
*/
final class Font extends AbstractFont
{
/**
* @var \Gmagick
*/
private $gmagick;
/**
* @param \Gmagick $gmagick
* @param string $file
* @param int $size
* @param \Imagine\Image\Palette\Color\ColorInterface $color
*/
public function __construct(\Gmagick $gmagick, $file, $size, ColorInterface $color)
{
$this->gmagick = $gmagick;
parent::__construct($file, $size, $color);
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\FontInterface::box()
*/
public function box($string, $angle = 0)
{
$text = new \GmagickDraw();
$text->setfont($this->file);
/*
* @see http://www.php.net/manual/en/imagick.queryfontmetrics.php#101027
*
* ensure font resolution is the same as GD's hard-coded 96
*/
$text->setfontsize((int) ($this->size * (96 / 72)));
$text->setfontstyle(\Gmagick::STYLE_OBLIQUE);
$info = $this->gmagick->queryfontmetrics($text, $string);
$box = $this->getClassFactory()->createBox($info['textWidth'], $info['textHeight']);
return $box;
}
}

View File

@@ -0,0 +1,890 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Gmagick;
use Imagine\Exception\InvalidArgumentException;
use Imagine\Exception\NotSupportedException;
use Imagine\Exception\OutOfBoundsException;
use Imagine\Exception\RuntimeException;
use Imagine\Factory\ClassFactoryInterface;
use Imagine\Image\AbstractImage;
use Imagine\Image\BoxInterface;
use Imagine\Image\Fill\FillInterface;
use Imagine\Image\ImageInterface;
use Imagine\Image\Metadata\MetadataBag;
use Imagine\Image\Palette\Color\ColorInterface;
use Imagine\Image\Palette\PaletteInterface;
use Imagine\Image\Point;
use Imagine\Image\PointInterface;
use Imagine\Image\ProfileInterface;
/**
* Image implementation using the Gmagick PHP extension.
*/
final class Image extends AbstractImage
{
/**
* @var \Gmagick
*/
private $gmagick;
/**
* @var \Imagine\Gmagick\Layers|null
*/
private $layers;
/**
* @var \Imagine\Image\Palette\PaletteInterface
*/
private $palette;
/**
* @var array|null
*/
private static $colorspaceMapping = null;
/**
* Constructs a new Image instance.
*
* @param \Gmagick $gmagick
* @param \Imagine\Image\Palette\PaletteInterface $palette
* @param \Imagine\Image\Metadata\MetadataBag $metadata
*/
public function __construct(\Gmagick $gmagick, PaletteInterface $palette, MetadataBag $metadata)
{
$this->metadata = $metadata;
$this->gmagick = $gmagick;
$this->setColorspace($palette);
}
/**
* Destroys allocated gmagick resources.
*/
public function __destruct()
{
if ($this->gmagick instanceof \Gmagick) {
$this->gmagick->clear();
$this->gmagick->destroy();
}
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\AbstractImage::__clone()
*/
public function __clone()
{
parent::__clone();
$this->gmagick = clone $this->gmagick;
$this->palette = clone $this->palette;
if ($this->layers !== null) {
$this->layers = $this->getClassFactory()->createLayers(ClassFactoryInterface::HANDLE_GMAGICK, $this, $this->layers->key());
}
}
/**
* Returns gmagick instance.
*
* @return \Gmagick
*/
public function getGmagick()
{
return $this->gmagick;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ManipulatorInterface::copy()
*/
public function copy()
{
return clone $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ManipulatorInterface::crop()
*/
public function crop(PointInterface $start, BoxInterface $size)
{
if (!$start->in($this->getSize())) {
throw new OutOfBoundsException('Crop coordinates must start at minimum 0, 0 position from top left corner, crop height and width must be positive integers and must not exceed the current image borders');
}
try {
$this->gmagick->cropimage($size->getWidth(), $size->getHeight(), $start->getX(), $start->getY());
} catch (\GmagickException $e) {
throw new RuntimeException('Crop operation failed', $e->getCode(), $e);
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ManipulatorInterface::flipHorizontally()
*/
public function flipHorizontally()
{
try {
$this->gmagick->flopimage();
} catch (\GmagickException $e) {
throw new RuntimeException('Horizontal flip operation failed', $e->getCode(), $e);
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ManipulatorInterface::flipVertically()
*/
public function flipVertically()
{
try {
$this->gmagick->flipimage();
} catch (\GmagickException $e) {
throw new RuntimeException('Vertical flip operation failed', $e->getCode(), $e);
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ManipulatorInterface::strip()
*/
public function strip()
{
try {
try {
$this->profile($this->palette->profile());
} catch (\Exception $e) {
// here we discard setting the profile as the previous incorporated profile
// is corrupted, let's now strip the image
}
$this->gmagick->stripimage();
} catch (\GmagickException $e) {
throw new RuntimeException('Strip operation failed', $e->getCode(), $e);
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ManipulatorInterface::paste()
*/
public function paste(ImageInterface $image, PointInterface $start, $alpha = 100)
{
if (!$image instanceof self) {
throw new InvalidArgumentException(sprintf('Gmagick\Image can only paste() Gmagick\Image instances, %s given', get_class($image)));
}
$alpha = (int) round($alpha);
if ($alpha < 0 || $alpha > 100) {
throw new InvalidArgumentException(sprintf('The %1$s argument can range from %2$d to %3$d, but you specified %4$d.', '$alpha', 0, 100, $alpha));
}
if ($alpha === 100) {
try {
$this->gmagick->compositeimage($image->gmagick, \Gmagick::COMPOSITE_DEFAULT, $start->getX(), $start->getY());
} catch (\GmagickException $e) {
throw new RuntimeException('Paste operation failed', $e->getCode(), $e);
}
} elseif ($alpha > 0) {
throw new NotSupportedException('Gmagick doesn\'t support paste with alpha.', 1);
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ManipulatorInterface::resize()
*/
public function resize(BoxInterface $size, $filter = ImageInterface::FILTER_UNDEFINED)
{
static $supportedFilters = array(
ImageInterface::FILTER_UNDEFINED => \Gmagick::FILTER_UNDEFINED,
ImageInterface::FILTER_BESSEL => \Gmagick::FILTER_BESSEL,
ImageInterface::FILTER_BLACKMAN => \Gmagick::FILTER_BLACKMAN,
ImageInterface::FILTER_BOX => \Gmagick::FILTER_BOX,
ImageInterface::FILTER_CATROM => \Gmagick::FILTER_CATROM,
ImageInterface::FILTER_CUBIC => \Gmagick::FILTER_CUBIC,
ImageInterface::FILTER_GAUSSIAN => \Gmagick::FILTER_GAUSSIAN,
ImageInterface::FILTER_HANNING => \Gmagick::FILTER_HANNING,
ImageInterface::FILTER_HAMMING => \Gmagick::FILTER_HAMMING,
ImageInterface::FILTER_HERMITE => \Gmagick::FILTER_HERMITE,
ImageInterface::FILTER_LANCZOS => \Gmagick::FILTER_LANCZOS,
ImageInterface::FILTER_MITCHELL => \Gmagick::FILTER_MITCHELL,
ImageInterface::FILTER_POINT => \Gmagick::FILTER_POINT,
ImageInterface::FILTER_QUADRATIC => \Gmagick::FILTER_QUADRATIC,
ImageInterface::FILTER_SINC => \Gmagick::FILTER_SINC,
ImageInterface::FILTER_TRIANGLE => \Gmagick::FILTER_TRIANGLE,
);
if (!array_key_exists($filter, $supportedFilters)) {
throw new InvalidArgumentException('Unsupported filter type');
}
try {
$this->gmagick->resizeimage($size->getWidth(), $size->getHeight(), $supportedFilters[$filter], 1);
} catch (\GmagickException $e) {
throw new RuntimeException('Resize operation failed', $e->getCode(), $e);
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ManipulatorInterface::rotate()
*/
public function rotate($angle, ColorInterface $background = null)
{
try {
if ($background === null) {
$background = $this->palette->color('fff');
}
$pixel = $this->getColor($background);
$this->gmagick->rotateimage($pixel, $angle);
unset($pixel);
} catch (\GmagickException $e) {
throw new RuntimeException('Rotate operation failed', $e->getCode(), $e);
}
return $this;
}
/**
* Applies options before save or output.
*
* @param \Gmagick $image
* @param array $options
* @param string $path
*
* @throws \Imagine\Exception\InvalidArgumentException
*/
private function applyImageOptions(\Gmagick $image, array $options, $path)
{
if (isset($options['format'])) {
$format = $options['format'];
} elseif ('' !== $extension = pathinfo($path, \PATHINFO_EXTENSION)) {
$format = $extension;
} else {
$format = pathinfo($image->getImageFilename(), \PATHINFO_EXTENSION);
}
$format = strtolower($format);
switch ($format) {
case 'jpeg':
case 'jpg':
case 'pjpeg':
if (!isset($options['jpeg_quality'])) {
if (isset($options['quality'])) {
$options['jpeg_quality'] = $options['quality'];
}
}
if (isset($options['jpeg_quality'])) {
$image->setCompressionQuality($options['jpeg_quality']);
}
if (isset($options['jpeg_sampling_factors'])) {
if (!is_array($options['jpeg_sampling_factors']) || \count($options['jpeg_sampling_factors']) < 1) {
throw new InvalidArgumentException('jpeg_sampling_factors option should be an array of integers');
}
$image->setSamplingFactors(array_map(function ($factor) {
return (int) $factor;
}, $options['jpeg_sampling_factors']));
}
break;
case 'png':
if (!isset($options['png_compression_level'])) {
if (isset($options['quality'])) {
$options['png_compression_level'] = round((100 - $options['quality']) * 9 / 100);
}
}
if (isset($options['png_compression_level'])) {
if ($options['png_compression_level'] < 0 || $options['png_compression_level'] > 9) {
throw new InvalidArgumentException('png_compression_level option should be an integer from 0 to 9');
}
}
if (isset($options['png_compression_filter'])) {
if ($options['png_compression_filter'] < 0 || $options['png_compression_filter'] > 9) {
throw new InvalidArgumentException('png_compression_filter option should be an integer from 0 to 9');
}
}
if (isset($options['png_compression_level']) || isset($options['png_compression_filter'])) {
// first digit: compression level (default: 7)
$compression = isset($options['png_compression_level']) ? $options['png_compression_level'] * 10 : 70;
// second digit: compression filter (default: 5)
$compression += isset($options['png_compression_filter']) ? $options['png_compression_filter'] : 5;
$image->setCompressionQuality($compression);
}
break;
case 'webp':
if (!isset($options['webp_quality'])) {
if (isset($options['quality'])) {
$options['webp_quality'] = $options['quality'];
}
}
if (isset($options['webp_quality'])) {
$image->setCompressionQuality($options['webp_quality']);
}
break;
}
if (isset($options['resolution-units']) && isset($options['resolution-x']) && isset($options['resolution-y'])) {
switch ($options['resolution-units']) {
case ImageInterface::RESOLUTION_PIXELSPERCENTIMETER:
$image->setimageunits(\Gmagick::RESOLUTION_PIXELSPERCENTIMETER);
break;
case ImageInterface::RESOLUTION_PIXELSPERINCH:
$image->setimageunits(\Gmagick::RESOLUTION_PIXELSPERINCH);
break;
default:
throw new InvalidArgumentException('Unsupported image unit format');
}
$image->setimageresolution($options['resolution-x'], $options['resolution-y']);
}
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ManipulatorInterface::save()
*/
public function save($path = null, array $options = array())
{
$path = null === $path ? $this->gmagick->getImageFilename() : $path;
if ('' === trim($path)) {
throw new RuntimeException('You can omit save path only if image has been open from a file');
}
try {
$this->prepareOutput($options, $path);
$allFrames = !isset($options['animated']) || false === $options['animated'];
$this->gmagick->writeimage($path, $allFrames);
} catch (\GmagickException $e) {
throw new RuntimeException('Save operation failed', $e->getCode(), $e);
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ManipulatorInterface::show()
*/
public function show($format, array $options = array())
{
header('Content-type: ' . $this->getMimeType($format));
echo $this->get($format, $options);
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ImageInterface::get()
*/
public function get($format, array $options = array())
{
try {
$options['format'] = $format;
$this->prepareOutput($options);
} catch (\GmagickException $e) {
throw new RuntimeException('Get operation failed', $e->getCode(), $e);
}
return $this->gmagick->getimagesblob();
}
/**
* @param array $options
* @param string $path
*/
private function prepareOutput(array $options, $path = null)
{
if (isset($options['format'])) {
$this->gmagick->setimageformat($options['format']);
}
if (isset($options['animated']) && true === $options['animated']) {
$format = isset($options['format']) ? $options['format'] : 'gif';
$delay = isset($options['animated.delay']) ? $options['animated.delay'] : null;
$loops = isset($options['animated.loops']) ? $options['animated.loops'] : 0;
$options['flatten'] = false;
$this->layers()->animate($format, $delay, $loops);
} else {
$this->layers()->merge();
}
$this->applyImageOptions($this->gmagick, $options, $path);
// flatten only if image has multiple layers
if ((!isset($options['flatten']) || $options['flatten'] === true) && $this->layers()->count() > 1) {
$this->flatten();
}
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ImageInterface::__toString()
*/
public function __toString()
{
return $this->get('png');
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ImageInterface::draw()
*/
public function draw()
{
return $this->getClassFactory()->createDrawer(ClassFactoryInterface::HANDLE_GMAGICK, $this->gmagick);
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ImageInterface::effects()
*/
public function effects()
{
return $this->getClassFactory()->createEffects(ClassFactoryInterface::HANDLE_GMAGICK, $this->gmagick);
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ImageInterface::getSize()
*/
public function getSize()
{
try {
$i = $this->gmagick->getimageindex();
$this->gmagick->setimageindex(0); //rewind
$width = $this->gmagick->getimagewidth();
$height = $this->gmagick->getimageheight();
$this->gmagick->setimageindex($i);
} catch (\GmagickException $e) {
throw new RuntimeException('Get size operation failed', $e->getCode(), $e);
}
return $this->getClassFactory()->createBox($width, $height);
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ManipulatorInterface::applyMask()
*/
public function applyMask(ImageInterface $mask)
{
if (!$mask instanceof self) {
throw new InvalidArgumentException('Can only apply instances of Imagine\Gmagick\Image as masks');
}
$size = $this->getSize();
$maskSize = $mask->getSize();
if ($size != $maskSize) {
throw new InvalidArgumentException(sprintf('The given mask doesn\'t match current image\'s size, current mask\'s dimensions are %s, while image\'s dimensions are %s', $maskSize, $size));
}
try {
$mask = $mask->copy();
$this->gmagick->compositeimage($mask->gmagick, \Gmagick::COMPOSITE_DEFAULT, 0, 0);
} catch (\GmagickException $e) {
throw new RuntimeException('Apply mask operation failed', $e->getCode(), $e);
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ImageInterface::mask()
*/
public function mask()
{
$mask = $this->copy();
try {
$mask->gmagick->modulateimage(100, 0, 100);
} catch (\GmagickException $e) {
throw new RuntimeException('Mask operation failed', $e->getCode(), $e);
}
return $mask;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ManipulatorInterface::fill()
*/
public function fill(FillInterface $fill)
{
try {
$draw = new \GmagickDraw();
$size = $this->getSize();
$w = $size->getWidth();
$h = $size->getHeight();
for ($x = 0; $x < $w; $x++) {
for ($y = 0; $y < $h; $y++) {
$pixel = $this->getColor($fill->getColor(new Point($x, $y)));
$draw->setfillcolor($pixel);
$draw->point($x, $y);
$pixel = null;
}
}
$this->gmagick->drawimage($draw);
$draw = null;
} catch (\GmagickException $e) {
throw new RuntimeException('Fill operation failed', $e->getCode(), $e);
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ImageInterface::histogram()
*/
public function histogram()
{
try {
$pixels = $this->gmagick->getimagehistogram();
} catch (\GmagickException $e) {
throw new RuntimeException('Error while fetching histogram', $e->getCode(), $e);
}
$image = $this;
return array_map(function (\GmagickPixel $pixel) use ($image) {
return $image->pixelToColor($pixel);
}, $pixels);
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ImageInterface::getColorAt()
*/
public function getColorAt(PointInterface $point)
{
if (!$point->in($this->getSize())) {
throw new InvalidArgumentException(sprintf('Error getting color at point [%s,%s]. The point must be inside the image of size [%s,%s]', $point->getX(), $point->getY(), $this->getSize()->getWidth(), $this->getSize()->getHeight()));
}
try {
$cropped = clone $this->gmagick;
$histogram = $cropped
->rollimage(-$point->getX(), -$point->getY())
->cropImage(1, 1, 0, 0)
->getImageHistogram();
} catch (\GmagickException $e) {
throw new RuntimeException('Unable to get the pixel', $e->getCode(), $e);
}
$pixel = array_shift($histogram);
unset($histogram, $cropped);
return $this->pixelToColor($pixel);
}
/**
* Returns a color given a pixel, depending the Palette context.
*
* Note : this method is public for PHP 5.3 compatibility
*
* @param \GmagickPixel $pixel
*
* @throws \Imagine\Exception\InvalidArgumentException In case a unknown color is requested
*
* @return \Imagine\Image\Palette\Color\ColorInterface
*/
public function pixelToColor(\GmagickPixel $pixel)
{
static $colorMapping = array(
ColorInterface::COLOR_RED => \Gmagick::COLOR_RED,
ColorInterface::COLOR_GREEN => \Gmagick::COLOR_GREEN,
ColorInterface::COLOR_BLUE => \Gmagick::COLOR_BLUE,
ColorInterface::COLOR_CYAN => \Gmagick::COLOR_CYAN,
ColorInterface::COLOR_MAGENTA => \Gmagick::COLOR_MAGENTA,
ColorInterface::COLOR_YELLOW => \Gmagick::COLOR_YELLOW,
ColorInterface::COLOR_KEYLINE => \Gmagick::COLOR_BLACK,
// There is no gray component in \Gmagick, let's use one of the RGB comp
ColorInterface::COLOR_GRAY => \Gmagick::COLOR_RED,
);
$alpha = null;
if ($this->palette->supportsAlpha()) {
if ($alpha === null && defined('Gmagick::COLOR_ALPHA')) {
try {
$alpha = (int) round($pixel->getcolorvalue(\Gmagick::COLOR_ALPHA) * 100);
} catch (\GmagickPixelException $e) {
}
}
if ($alpha === null && defined('Gmagick::COLOR_OPACITY')) {
try {
$alpha = (int) round(100 - $pixel->getcolorvalue(\Gmagick::COLOR_OPACITY) * 100);
} catch (\GmagickPixelException $e) {
}
}
}
$multiplier = $this->palette()->getChannelsMaxValue();
return $this->palette->color(array_map(function ($color) use ($multiplier, $pixel, $colorMapping) {
if (!isset($colorMapping[$color])) {
throw new InvalidArgumentException(sprintf('Color %s is not mapped in Gmagick', $color));
}
return $pixel->getcolorvalue($colorMapping[$color]) * $multiplier;
}, $this->palette->pixelDefinition()), $alpha);
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ImageInterface::layers()
*/
public function layers()
{
if ($this->layers === null) {
$this->layers = $this->getClassFactory()->createLayers(ClassFactoryInterface::HANDLE_GMAGICK, $this);
}
return $this->layers;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ImageInterface::interlace()
*/
public function interlace($scheme)
{
static $supportedInterlaceSchemes = array(
ImageInterface::INTERLACE_NONE => \Gmagick::INTERLACE_NO,
ImageInterface::INTERLACE_LINE => \Gmagick::INTERLACE_LINE,
ImageInterface::INTERLACE_PLANE => \Gmagick::INTERLACE_PLANE,
ImageInterface::INTERLACE_PARTITION => \Gmagick::INTERLACE_PARTITION,
);
if (!array_key_exists($scheme, $supportedInterlaceSchemes)) {
throw new InvalidArgumentException('Unsupported interlace type');
}
$this->gmagick->setInterlaceScheme($supportedInterlaceSchemes[$scheme]);
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ImageInterface::usePalette()
*/
public function usePalette(PaletteInterface $palette)
{
$colorspaceMapping = self::getColorspaceMapping();
if (!isset($colorspaceMapping[$palette->name()])) {
throw new InvalidArgumentException(sprintf('The palette %s is not supported by Gmagick driver', $palette->name()));
}
if ($this->palette->name() === $palette->name()) {
return $this;
}
try {
try {
$hasICCProfile = (bool) $this->gmagick->getimageprofile('ICM');
} catch (\GmagickException $e) {
$hasICCProfile = false;
}
if (!$hasICCProfile) {
$this->profile($this->palette->profile());
}
$this->profile($palette->profile());
$this->setColorspace($palette);
} catch (\GmagickException $e) {
throw new RuntimeException('Failed to set colorspace', $e->getCode(), $e);
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ImageInterface::palette()
*/
public function palette()
{
return $this->palette;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ImageInterface::profile()
*/
public function profile(ProfileInterface $profile)
{
try {
$this->gmagick->profileimage('ICM', $profile->data());
} catch (\GmagickException $e) {
if (false !== strpos($e->getMessage(), 'LCMS encoding not enabled')) {
throw new RuntimeException(sprintf('Unable to add profile %s to image, be sue to compile graphicsmagick with `--with-lcms2` option', $profile->name()), $e->getCode(), $e);
}
throw new RuntimeException(sprintf('Unable to add profile %s to image', $profile->name()), $e->getCode(), $e);
}
return $this;
}
/**
* Flatten the image.
*/
private function flatten()
{
/*
* @see http://pecl.php.net/bugs/bug.php?id=22435
*/
if (method_exists($this->gmagick, 'flattenImages')) {
try {
$this->gmagick = $this->gmagick->flattenImages();
} catch (\GmagickException $e) {
throw new RuntimeException('Flatten operation failed', $e->getCode(), $e);
}
}
}
/**
* Gets specifically formatted color string from Color instance.
*
* @param \Imagine\Image\Palette\Color\ColorInterface $color
*
* @throws \Imagine\Exception\InvalidArgumentException
*
* @return \GmagickPixel
*/
private function getColor(ColorInterface $color)
{
if (!$color->isOpaque()) {
throw new InvalidArgumentException('Gmagick doesn\'t support transparency');
}
return new \GmagickPixel((string) $color);
}
/**
* Get the mime type based on format.
*
* @param string $format
*
* @throws \Imagine\Exception\InvalidArgumentException
*
* @return string mime-type
*/
private function getMimeType($format)
{
static $mimeTypes = array(
'jpeg' => 'image/jpeg',
'jpg' => 'image/jpeg',
'gif' => 'image/gif',
'png' => 'image/png',
'webp' => 'image/webp',
'wbmp' => 'image/vnd.wap.wbmp',
'xbm' => 'image/xbm',
'bmp' => 'image/bmp',
);
if (!isset($mimeTypes[$format])) {
throw new InvalidArgumentException(sprintf('Unsupported format given. Only %s are supported, %s given', implode(', ', array_keys($mimeTypes)), $format));
}
return $mimeTypes[$format];
}
/**
* Sets colorspace and image type, assigns the palette.
*
* @param \Imagine\Image\Palette\PaletteInterface $palette
*
* @throws \Imagine\Exception\InvalidArgumentException
*/
private function setColorspace(PaletteInterface $palette)
{
$colorspaceMapping = self::getColorspaceMapping();
if (!isset($colorspaceMapping[$palette->name()])) {
throw new InvalidArgumentException(sprintf('The palette %s is not supported by Gmagick driver', $palette->name()));
}
$this->gmagick->setimagecolorspace($colorspaceMapping[$palette->name()]);
$this->palette = $palette;
}
/**
* @return array
*/
private static function getColorspaceMapping()
{
if (self::$colorspaceMapping === null) {
$csm = array(
PaletteInterface::PALETTE_CMYK => \Gmagick::COLORSPACE_CMYK,
PaletteInterface::PALETTE_RGB => \Gmagick::COLORSPACE_RGB,
);
if (defined('Gmagick::COLORSPACE_GRAY')) {
$csm[PaletteInterface::PALETTE_GRAYSCALE] = \Gmagick::COLORSPACE_GRAY;
}
self::$colorspaceMapping = $csm;
}
return self::$colorspaceMapping;
}
}

View File

@@ -0,0 +1,194 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Gmagick;
use Imagine\Exception\InvalidArgumentException;
use Imagine\Exception\NotSupportedException;
use Imagine\Exception\RuntimeException;
use Imagine\Factory\ClassFactoryInterface;
use Imagine\File\LoaderInterface;
use Imagine\Image\AbstractImagine;
use Imagine\Image\BoxInterface;
use Imagine\Image\Metadata\MetadataBag;
use Imagine\Image\Palette\CMYK;
use Imagine\Image\Palette\Color\CMYK as CMYKColor;
use Imagine\Image\Palette\Color\ColorInterface;
use Imagine\Image\Palette\Grayscale;
use Imagine\Image\Palette\RGB;
/**
* Imagine implementation using the Gmagick PHP extension.
*/
class Imagine extends AbstractImagine
{
/**
* @throws \Imagine\Exception\RuntimeException
*/
public function __construct()
{
if (!class_exists('Gmagick')) {
throw new RuntimeException('Gmagick not installed');
}
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ImagineInterface::open()
*/
public function open($path)
{
$loader = $path instanceof LoaderInterface ? $path : $this->getClassFactory()->createFileLoader($path);
$path = $loader->getPath();
try {
if ($loader->isLocalFile()) {
$gmagick = new \Gmagick($path);
$image = $this->getClassFactory()->createImage(ClassFactoryInterface::HANDLE_GMAGICK, $gmagick, $this->createPalette($gmagick), $this->getMetadataReader()->readFile($loader));
} else {
$image = $this->doLoad($loader->getData(), $this->getMetadataReader()->readFile($loader));
}
} catch (\GmagickException $e) {
throw new RuntimeException(sprintf('Unable to open image %s', $path), $e->getCode(), $e);
}
return $image;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ImagineInterface::create()
*/
public function create(BoxInterface $size, ColorInterface $color = null)
{
$width = $size->getWidth();
$height = $size->getHeight();
$palette = null !== $color ? $color->getPalette() : new RGB();
$color = null !== $color ? $color : $palette->color('fff');
try {
$gmagick = new \Gmagick();
// Gmagick does not support creation of CMYK GmagickPixel
// see https://bugs.php.net/bug.php?id=64466
if ($color instanceof CMYKColor) {
$switchPalette = $palette;
$palette = new RGB();
$pixel = new \GmagickPixel($palette->color((string) $color));
} else {
$switchPalette = null;
$pixel = new \GmagickPixel((string) $color);
}
if (!$color->getPalette()->supportsAlpha() && $color->getAlpha() !== null && $color->getAlpha() < 100) {
throw new NotSupportedException('alpha transparency is not supported');
}
$gmagick->newimage($width, $height, $pixel->getcolor(false));
$gmagick->setimagecolorspace(\Gmagick::COLORSPACE_TRANSPARENT);
$gmagick->setimagebackgroundcolor($pixel);
$image = $this->getClassFactory()->createImage(ClassFactoryInterface::HANDLE_GMAGICK, $gmagick, $palette, new MetadataBag());
if ($switchPalette) {
$image->usePalette($switchPalette);
}
return $image;
} catch (\GmagickException $e) {
throw new RuntimeException('Could not create empty image', $e->getCode(), $e);
}
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ImagineInterface::load()
*/
public function load($string)
{
return $this->doLoad($string, $this->getMetadataReader()->readData($string));
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ImagineInterface::read()
*/
public function read($resource)
{
if (!is_resource($resource)) {
throw new InvalidArgumentException('Variable does not contain a stream resource');
}
$content = stream_get_contents($resource);
if (false === $content) {
throw new InvalidArgumentException('Couldn\'t read given resource');
}
return $this->doLoad($content, $this->getMetadataReader()->readData($content, $resource));
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ImagineInterface::font()
*/
public function font($file, $size, ColorInterface $color)
{
return $this->getClassFactory()->createFont(ClassFactoryInterface::HANDLE_GMAGICK, $file, $size, $color);
}
/**
* @param \Gmagick $gmagick
*
* @throws \Imagine\Exception\NotSupportedException
*
* @return \Imagine\Image\Palette\PaletteInterface
*/
private function createPalette(\Gmagick $gmagick)
{
switch ($gmagick->getimagecolorspace()) {
case \Gmagick::COLORSPACE_SRGB:
case \Gmagick::COLORSPACE_RGB:
return new RGB();
case \Gmagick::COLORSPACE_CMYK:
return new CMYK();
case \Gmagick::COLORSPACE_GRAY:
return new Grayscale();
default:
throw new NotSupportedException('Only RGB and CMYK colorspace are currently supported');
}
}
/**
* @param string $content
* @param \Imagine\Image\Metadata\MetadataBag $metadata
*
* @throws \Imagine\Exception\RuntimeException
*
* @return \Imagine\Image\ImageInterface
*/
private function doLoad($content, MetadataBag $metadata)
{
try {
$gmagick = new \Gmagick();
$gmagick->readimageblob($content);
} catch (\GmagickException $e) {
throw new RuntimeException('Could not load image from string', $e->getCode(), $e);
}
return $this->getClassFactory()->createImage(ClassFactoryInterface::HANDLE_GMAGICK, $gmagick, $this->createPalette($gmagick), $metadata);
}
}

View File

@@ -0,0 +1,308 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Gmagick;
use Imagine\Exception\InvalidArgumentException;
use Imagine\Exception\NotSupportedException;
use Imagine\Exception\OutOfBoundsException;
use Imagine\Exception\RuntimeException;
use Imagine\Factory\ClassFactoryInterface;
use Imagine\Image\AbstractLayers;
use Imagine\Image\Metadata\MetadataBag;
use Imagine\Image\Palette\PaletteInterface;
class Layers extends AbstractLayers
{
/**
* @var \Imagine\Gmagick\Image
*/
private $image;
/**
* @var \Gmagick
*/
private $resource;
/**
* @var int
*/
private $offset;
/**
* @var \Imagine\Gmagick\Image[]
*/
private $layers = array();
/**
* @var \Imagine\Image\Palette\PaletteInterface
*/
private $palette;
/**
* @param \Imagine\Gmagick\Image $image
* @param \Imagine\Image\Palette\PaletteInterface $palette
* @param \Gmagick $resource
* @param int $initialOffset
*/
public function __construct(Image $image, PaletteInterface $palette, \Gmagick $resource, $initialOffset = 0)
{
$this->image = $image;
$this->resource = $resource;
$this->palette = $palette;
$this->offset = (int) $initialOffset;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\LayersInterface::merge()
*/
public function merge()
{
foreach ($this->layers as $offset => $image) {
try {
$this->resource->setimageindex($offset);
$this->resource->setimage($image->getGmagick());
} catch (\GmagickException $e) {
throw new RuntimeException('Failed to substitute layer', $e->getCode(), $e);
}
}
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\LayersInterface::coalesce()
*/
public function coalesce()
{
throw new NotSupportedException('Gmagick does not support coalescing');
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\LayersInterface::animate()
*/
public function animate($format, $delay, $loops)
{
if ('gif' !== strtolower($format)) {
throw new NotSupportedException('Animated picture is currently only supported on gif');
}
if (!is_int($loops) || $loops < 0) {
throw new InvalidArgumentException('Loops must be a positive integer.');
}
if (null !== $delay && (!is_int($delay) || $delay < 0)) {
throw new InvalidArgumentException('Delay must be either null or a positive integer.');
}
try {
foreach ($this as $offset => $layer) {
$this->resource->setimageindex($offset);
$this->resource->setimageformat($format);
if (null !== $delay) {
$this->resource->setimagedelay($delay / 10);
}
$this->resource->setimageiterations($loops);
}
} catch (\GmagickException $e) {
throw new RuntimeException('Failed to animate layers', $e->getCode(), $e);
}
return $this;
}
/**
* {@inheritdoc}
*
* @see \Iterator::current()
*/
public function current()
{
return $this->extractAt($this->offset);
}
/**
* Tries to extract layer at given offset.
*
* @param int $offset
*
* @throws \Imagine\Exception\RuntimeException
*
* @return \Imagine\Gmagick\Image
*/
private function extractAt($offset)
{
if (!isset($this->layers[$offset])) {
try {
$this->resource->setimageindex($offset);
$this->layers[$offset] = $this->getClassFactory()->createImage(ClassFactoryInterface::HANDLE_GMAGICK, $this->resource->getimage(), $this->palette, new MetadataBag());
} catch (\GmagickException $e) {
throw new RuntimeException(sprintf('Failed to extract layer %d', $offset), $e->getCode(), $e);
}
}
return $this->layers[$offset];
}
/**
* {@inheritdoc}
*
* @see \Iterator::key()
*/
public function key()
{
return $this->offset;
}
/**
* {@inheritdoc}
*
* @see \Iterator::next()
*/
public function next()
{
++$this->offset;
}
/**
* {@inheritdoc}
*
* @see \Iterator::rewind()
*/
public function rewind()
{
$this->offset = 0;
}
/**
* {@inheritdoc}
*
* @see \Iterator::valid()
*/
public function valid()
{
return $this->offset < count($this);
}
/**
* {@inheritdoc}
*
* @see \Countable::count()
*/
public function count()
{
try {
return $this->resource->getnumberimages();
} catch (\GmagickException $e) {
throw new RuntimeException('Failed to count the number of layers', $e->getCode(), $e);
}
}
/**
* {@inheritdoc}
*
* @see \ArrayAccess::offsetExists()
*/
public function offsetExists($offset)
{
return is_int($offset) && $offset >= 0 && $offset < count($this);
}
/**
* {@inheritdoc}
*
* @see \ArrayAccess::offsetGet()
*/
public function offsetGet($offset)
{
return $this->extractAt($offset);
}
/**
* {@inheritdoc}
*
* @see \ArrayAccess::offsetSet()
*/
public function offsetSet($offset, $image)
{
if (!$image instanceof Image) {
throw new InvalidArgumentException('Only a Gmagick Image can be used as layer');
}
if (null === $offset) {
$offset = count($this) - 1;
} else {
if (!is_int($offset)) {
throw new InvalidArgumentException('Invalid offset for layer, it must be an integer');
}
if (count($this) < $offset || 0 > $offset) {
throw new OutOfBoundsException(sprintf('Invalid offset for layer, it must be a value between 0 and %d, %d given', count($this), $offset));
}
if (isset($this[$offset])) {
unset($this[$offset]);
$offset = $offset - 1;
}
}
$frame = $image->getGmagick();
try {
if (count($this) > 0) {
$this->resource->setimageindex($offset);
$this->resource->nextimage();
}
$this->resource->addimage($frame);
/*
* ugly hack to bypass issue https://bugs.php.net/bug.php?id=64623
*/
if (count($this) == 2) {
$this->resource->setimageindex($offset + 1);
$this->resource->nextimage();
$this->resource->addimage($frame);
unset($this[0]);
}
} catch (\GmagickException $e) {
throw new RuntimeException('Unable to set the layer', $e->getCode(), $e);
}
$this->layers = array();
}
/**
* {@inheritdoc}
*
* @see \ArrayAccess::offsetUnset()
*/
public function offsetUnset($offset)
{
try {
$this->extractAt($offset);
} catch (RuntimeException $e) {
return;
}
try {
$this->resource->setimageindex($offset);
$this->resource->removeimage();
} catch (\GmagickException $e) {
throw new RuntimeException('Unable to remove layer', $e->getCode(), $e);
}
}
}

View File

@@ -0,0 +1,155 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Image;
use Imagine\Exception\InvalidArgumentException;
use Imagine\Factory\ClassFactory;
use Imagine\Factory\ClassFactoryAwareInterface;
use Imagine\Factory\ClassFactoryInterface;
use Imagine\Image\Palette\Color\ColorInterface;
/**
* Abstract font base class.
*/
abstract class AbstractFont implements FontInterface, ClassFactoryAwareInterface
{
/**
* @var \Imagine\Factory\ClassFactoryInterface|null
*/
private $classFactory;
/**
* @var string
*/
protected $file;
/**
* @var int
*/
protected $size;
/**
* @var \Imagine\Image\Palette\Color\ColorInterface
*/
protected $color;
/**
* Constructs a font with specified $file, $size and $color.
*
* The font size is to be specified in points (e.g. 10pt means 10)
*
* @param string $file
* @param int $size
* @param \Imagine\Image\Palette\Color\ColorInterface $color
*/
public function __construct($file, $size, ColorInterface $color)
{
$this->file = $file;
$this->size = $size;
$this->color = $color;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\FontInterface::getFile()
*/
final public function getFile()
{
return $this->file;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\FontInterface::getSize()
*/
final public function getSize()
{
return $this->size;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\FontInterface::getColor()
*/
final public function getColor()
{
return $this->color;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\FontInterface::wrapText()
*/
public function wrapText($string, $maxWidth, $angle = 0)
{
$string = (string) $string;
if ($string === '') {
return $string;
}
$maxWidth = (int) round($maxWidth);
if ($maxWidth < 1) {
throw new InvalidArgumentException(sprintf('The $maxWidth parameter of wrapText must be greater than 0.'));
}
$words = explode(' ', $string);
$lines = array();
$currentLine = null;
foreach ($words as $word) {
if ($currentLine === null) {
$currentLine = $word;
} else {
$testLine = $currentLine . ' ' . $word;
$testbox = $this->box($testLine, $angle);
if ($testbox->getWidth() <= $maxWidth) {
$currentLine = $testLine;
} else {
$lines[] = $currentLine;
$currentLine = $word;
}
}
}
if ($currentLine !== null) {
$lines[] = $currentLine;
}
return implode("\n", $lines);
}
/**
* {@inheritdoc}
*
* @see \Imagine\Factory\ClassFactoryAwareInterface::getClassFactory()
*/
public function getClassFactory()
{
if ($this->classFactory === null) {
$this->classFactory = new ClassFactory();
}
return $this->classFactory;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Factory\ClassFactoryAwareInterface::setClassFactory()
*/
public function setClassFactory(ClassFactoryInterface $classFactory)
{
$this->classFactory = $classFactory;
return $this;
}
}

View File

@@ -0,0 +1,216 @@
<?php
/*
* This file is part of the Imagine package.
*
* (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Imagine\Image;
use Imagine\Exception\InvalidArgumentException;
use Imagine\Factory\ClassFactory;
use Imagine\Factory\ClassFactoryAwareInterface;
use Imagine\Factory\ClassFactoryInterface;
abstract class AbstractImage implements ImageInterface, ClassFactoryAwareInterface
{
/**
* @var \Imagine\Image\Metadata\MetadataBag
*/
protected $metadata;
/**
* @var \Imagine\Factory\ClassFactoryInterface|null
*/
private $classFactory;
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ManipulatorInterface::thumbnail()
*/
public function thumbnail(BoxInterface $size, $settings = ImageInterface::THUMBNAIL_INSET, $filter = ImageInterface::FILTER_UNDEFINED)
{
$settings = $this->checkThumbnailSettings($settings);
$mode = $settings & 0xffff;
$allowUpscale = (bool) ($settings & ImageInterface::THUMBNAIL_FLAG_UPSCALE);
$noClone = (bool) ($settings & ImageInterface::THUMBNAIL_FLAG_NOCLONE);
$imageSize = $this->getSize();
$palette = $this->palette();
$thumbnail = $noClone ? $this : $this->copy();
$thumbnail->usePalette($palette);
$thumbnail->strip();
if ($size->getWidth() === $imageSize->getWidth() && $size->getHeight() === $imageSize->getHeight()) {
// The thumbnail size is the same as the wanted size.
return $thumbnail;
}
if (!$allowUpscale && $size->contains($imageSize)) {
// Thumbnail is smaller than the image and we are not upscaling
return $thumbnail;
}
$ratios = array(
$size->getWidth() / $imageSize->getWidth(),
$size->getHeight() / $imageSize->getHeight(),
);
switch ($mode) {
case ImageInterface::THUMBNAIL_OUTBOUND:
// Crop the image so that it fits the wanted size
$ratio = max($ratios);
if ($imageSize->contains($size)) {
// Downscale the image
$imageSize = $imageSize->scale($ratio);
$thumbnail->resize($imageSize, $filter);
$thumbnailSize = $size;
} else {
if ($allowUpscale) {
// Upscale the image so that the max dimension will be the wanted one
$imageSize = $imageSize->scale($ratio);
$thumbnail->resize($imageSize, $filter);
}
$thumbnailSize = new Box(
min($imageSize->getWidth(), $size->getWidth()),
min($imageSize->getHeight(), $size->getHeight())
);
}
$thumbnail->crop(
new Point(
max(0, round(($imageSize->getWidth() - $size->getWidth()) / 2)),
max(0, round(($imageSize->getHeight() - $size->getHeight()) / 2))
),
$thumbnailSize
);
break;
case ImageInterface::THUMBNAIL_INSET:
default:
// Scale the image so that it fits the wanted size
$ratio = min($ratios);
$thumbnailSize = $imageSize->scale($ratio);
$thumbnail->resize($thumbnailSize, $filter);
break;
}
return $thumbnail;
}
/**
* Check the settings argument in thumbnail() method.
*
* @param int $settings
*/
private function checkThumbnailSettings($settings)
{
// Preserve BC until version 1.0
if (is_string($settings)) {
if ($settings === 'inset') {
$settings = ImageInterface::THUMBNAIL_INSET;
} elseif ($settings === 'outbound') {
$settings = ImageInterface::THUMBNAIL_OUTBOUND;
} elseif (is_numeric($settings)) {
$settings = (int) $settings;
}
}
if (!is_int($settings)) {
throw new InvalidArgumentException('Invalid setting specified');
}
$mode = $settings & 0xffff;
if ($mode === 0) {
$settings |= ImageInterface::THUMBNAIL_INSET;
} else {
if (!in_array($mode, $this->getAllThumbnailModes())) {
if (strlen(str_replace('0', '', decbin($mode))) === 1) {
throw new InvalidArgumentException('Invalid setting specified');
}
throw new InvalidArgumentException('Only one mode should be specified');
}
}
return $settings;
}
/**
* Get all the available thumbnail modes.
*
* @return int[]
*/
protected function getAllThumbnailModes()
{
return array(
ImageInterface::THUMBNAIL_INSET,
ImageInterface::THUMBNAIL_OUTBOUND,
);
}
/**
* Updates a given array of save options for backward compatibility with legacy names.
*
* @param array $options
*
* @return array
*/
protected function updateSaveOptions(array $options)
{
// Preserve BC until version 1.0
if (isset($options['quality']) && !isset($options['jpeg_quality'])) {
$options['jpeg_quality'] = $options['quality'];
}
return $options;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Image\ImageInterface::metadata()
*/
public function metadata()
{
return $this->metadata;
}
/**
* Clones all the resources associated to this instance.
*/
public function __clone()
{
if ($this->metadata !== null) {
$this->metadata = clone $this->metadata;
}
}
/**
* {@inheritdoc}
*
* @see \Imagine\Factory\ClassFactoryAwareInterface::getClassFactory()
*/
public function getClassFactory()
{
if ($this->classFactory === null) {
$this->classFactory = new ClassFactory();
}
return $this->classFactory;
}
/**
* {@inheritdoc}
*
* @see \Imagine\Factory\ClassFactoryAwareInterface::setClassFactory()
*/
public function setClassFactory(ClassFactoryInterface $classFactory)
{
$this->classFactory = $classFactory;
return $this;
}
}

Some files were not shown because too many files have changed in this diff Show More