Skip to content

Commit aeccf1b

Browse files
committed
doc
1 parent dd20410 commit aeccf1b

15 files changed

+280
-190
lines changed

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ phpunit.phar
205205
```
206206
207207
## Dependencies / support
208-
Tested against [PHP 5.6](http://php.net/) and [PHP 7.1](http://php.net/).
208+
Tested against [PHP 7.1](http://php.net/).
209209
210210
Current implementation uses [Imagine](https://github.com/avalanche123/Imagine).
211211
@@ -214,9 +214,12 @@ You will need either `php-gd`, `php-gmagick` or `php-imagick`.
214214
Wrappers for external optimizer:
215215
- [jpegtran](http://jpegclub.org/jpegtran/)
216216
- [pngcrush](https://pmt.sourceforge.io/pngcrush/)
217+
- [gifsicle](https://pmt.sourceforge.io/pngcrush/)
217218
218219
## History
219220
221+
- 1.5: add animated GIFs support in thumbnail and watermark (Imagick only)
222+
- 1.4: add optimize gifsicle
220223
- 1.3: allow backend to have image manipulator
221224
- 1.2: add watermark
222225
- 1.1: add path rules

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44
"keywords": ["image"],
55
"license": "MIT",
66
"require": {
7-
"imagine/Imagine": "^0.6",
7+
"imagine/imagine": "^0.6",
88
"guzzlehttp/guzzle": "^6.2",
99
"doctrine/cache": "^1.6"
1010
},
1111
"require-dev": {
1212
"ext-gd": "*",
13+
"ext-imagick": "*",
1314
"phpunit/phpunit": "^5.0"
1415
},
1516
"autoload": {

src/ImageStack/Image.php

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,10 @@ public function deprecateBinaryContent()
4949
* @param string $binaryContent
5050
* @param string $mimeType
5151
*/
52-
public function __construct($binaryContent, $mimeType = null, ImagineInterface $imagine = null)
52+
public function __construct($binaryContent, $mimeType = null, ImagineInterface $imagine = null, array $imagineOptions = [])
5353
{
5454
$this->setBinaryContent($binaryContent, $mimeType);
55-
$this->setImagine($imagine);
55+
if ($imagine) $this->setImagine($imagine, $imagineOptions);
5656
}
5757

5858
/**
@@ -68,6 +68,8 @@ public function setBinaryContent($binaryContent, $mimeType = null)
6868
$this->mimeType = $mimeType;
6969
$this->binaryContentDirty = false;
7070
$this->imagineImage = null;
71+
// we reset one shot dump options
72+
$this->ephemeralImagineOptions = [];
7173
}
7274

7375
/**
@@ -78,9 +80,11 @@ public function getBinaryContent()
7880
{
7981
if ($this->binaryContentDirty) {
8082
$mimeType = $this->getMimeType();
83+
// we consider that
84+
$options = array_replace($this->ephemeralImagineOptions, $this->imagineOptions);
8185
$binaryContent = $this->getImagineImage()->get(
8286
self::get_type_from_mime_type($mimeType),
83-
$this->imagineOptions);
87+
$options);
8488
$this->setBinaryContent($binaryContent, $mimeType);
8589
}
8690
return $this->binaryContent;
@@ -106,15 +110,15 @@ public function getMimeType()
106110
/**
107111
* @var array
108112
*/
109-
protected $imagineOptions = [];
113+
protected $ephemeralImagineOptions = [];
110114

111115
/**
112116
* Set imagine options for binary content generation.
113117
* @param array $imagineOptions
114118
*/
115-
public function setImagineOptions(array $imagineOptions)
119+
public function setEphemeralImagineOptions(array $imagineOptions)
116120
{
117-
$this->imagineOptions = array_replace($this->imagineOptions, $imagineOptions);
121+
$this->ephemeralImagineOptions = array_replace($this->ephemeralImagineOptions, $imagineOptions);
118122
}
119123

120124
/**

src/ImageStack/ImageBackend/PathRuleImageBackend.php

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,8 @@ class PathRuleImageBackend implements ImageBackendInterface
2727
/**
2828
* Cache image backend constructor.
2929
* @param ImageBackendInterface $imageBackend
30-
* @param Cache $cache
30+
* @param array $rules
3131
* @param array $options
32-
* Options:
33-
* - cache_prefix : prefix for the cache (default: stack prefix)
34-
* - cache_lifetime : cache lifetime (default: 0 is forever)
35-
* - cache_id_sanitizer : callback to sanitize the cache ID (default: none)
3632
*/
3733
public function __construct(ImageBackendInterface $imageBackend, array $rules = [], $options = array())
3834
{
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace ImageStack\ImageManipulator;
4+
5+
use ImageStack\ImageWithImagineInterface;
6+
7+
trait AnimatedGifAwareImageManipulatorTrait
8+
{
9+
protected function handleAnimatedGif(ImageWithImagineInterface $image, callable $initCallback = null, callable $frameCallback = null, callable $completeCallback = null)
10+
{
11+
if ($image->getMimeType() != 'image/gif') return false;
12+
if (! $image->getImagine() instanceof \Imagine\Imagick\Imagine) return false;
13+
if (empty($image->getImagineImage()->layers())) return false;
14+
if ($image->getImagineImage()->layers()->count() <= 1) return false;
15+
16+
$imagineImage = $image->getImagineImage();
17+
18+
if ($initCallback) call_user_func($initCallback, $image->getImagineImage());
19+
20+
if ($frameCallback) {
21+
$imagineImage->layers()->coalesce();
22+
foreach ($imagineImage->layers() as $frame) {
23+
call_user_func($frameCallback, $frame);
24+
}
25+
}
26+
27+
if ($completeCallback) call_user_func($completeCallback, $imagineImage);
28+
}
29+
}

src/ImageStack/ImageManipulator/ConverterImageManipulator.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<?php
2+
23
namespace ImageStack\ImageManipulator;
34

45
use ImageStack\Api\ImageInterface;
@@ -29,7 +30,8 @@ class ConverterImageManipulator implements ImageManipulatorInterface, ImagineAwa
2930
*/
3031
public function __construct(ImagineInterface $imagine, array $conversions = [], array $options = [])
3132
{
32-
$this->setImagine($imagine);
33+
$this->setImagine($imagine, $options['imagine_options'] ?? []);
34+
unset($options['imagine_options']);
3335
foreach ($conversions as $sourceMimeType => $destinationMimeType) {
3436
$this->addConversion($sourceMimeType, $destinationMimeType);
3537
}
@@ -70,9 +72,8 @@ protected function _manipulateImage(ImageWithImagineInterface $image, ImagePathI
7072
{
7173
if (isset($this->convertMap[$image->getMimeType()])) {
7274
if (!$image->getImagine()) {
73-
$image->setImagine($this->getImagine());
75+
$image->setImagine($this->getImagine(), $this->getImagineOptions());
7476
}
75-
$image->setImagineOptions($this->getOption('imagine_options', []));
7677
// trigger imagine image creation
7778
$image->getImagineImage();
7879
$image->setMimeType($this->convertMap[$image->getMimeType()]);

src/ImageStack/ImageManipulator/ThumbnailRule/PatternThumbnailRule.php

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
use ImageStack\ImageWithImagineInterface;
99
use ImageStack\Api\Exception\ImageNotFoundException;
1010
use ImageStack\ImageManipulator\ThumbnailRule\Exception\ThumbnailRuleException;
11+
use ImageStack\Api\Exception\ImageException;
12+
use Imagine\Imagick\Image as IImage;
13+
use ImageStack\ImageManipulator\AnimatedGifAwareImageManipulatorTrait;
1114

1215
/**
1316
* Pattern thumbnail rule.
@@ -16,6 +19,7 @@
1619
class PatternThumbnailRule implements ThumbnailRuleInterface, ImagineAwareInterface
1720
{
1821
use ImagineAwareTrait;
22+
use AnimatedGifAwareImageManipulatorTrait;
1923

2024
/**
2125
* A preg_match() pattern.
@@ -99,10 +103,35 @@ protected function _thumbnailImage(ImageWithImagineInterface $image, ImagePathIn
99103
}
100104
// not very LSP but handy
101105
if (!$image->getImagine()) {
102-
$image->setImagine($this->getImagine());
106+
$image->setImagine($this->getImagine(), $this->getImagineOptions());
103107
}
104-
$thumbnail = $image->getImagineImage()->thumbnail($size, $mode);
105-
$image->setImagineImage($thumbnail);
108+
109+
/** @var IImage $animated */
110+
$animated = null;
111+
$imagine = $image->getImagine();
112+
if ($this->handleAnimatedGif($image, function (IImage $iimage) use ($imagine, $size, &$animated, $image) {
113+
$animated = $imagine->create($size);
114+
$animated->layers()->remove(0);
115+
// we take first frame conf
116+
/** @var IImage $iframe */
117+
$iframe = $iimage->layers()[0];
118+
$options = [
119+
'flatten' => false,
120+
'animated' => true,
121+
'animated.delay' => $iframe->getImagick()->getImageDelay() * 10,
122+
'animated.loop' => $iframe->getImagick()->getImageIterations(),
123+
];
124+
// put those options for next binary dump only
125+
$image->setEphemeralImagineOptions($options);
126+
}, function (IImage $iframe) use ($size, $mode, &$animated) {
127+
$animated->layers()->add($iframe->thumbnail($size, $mode));
128+
}, function (IImage $iimage) {
129+
// nothing
130+
})) {
131+
$image->setImagineImage($animated);
132+
return true;
133+
}
134+
$image->setImagineImage($image->getImagineImage()->thumbnail($size, $mode));
106135
return true;
107136
}
108137

src/ImageStack/ImageManipulator/ThumbnailerImageManipulator.php

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use ImageStack\ImageManipulator\ThumbnailRule\ThumbnailRuleInterface;
1111
use ImageStack\ImagineAwareInterface;
1212
use ImageStack\ImageManipulator\Exception\ImageManipulatorException;
13+
use ImageStack\OptionnableTrait;
1314

1415
/**
1516
* Thumbnailer image manipulator.
@@ -19,14 +20,24 @@
1920
*
2021
*/
2122
class ThumbnailerImageManipulator implements ImageManipulatorInterface {
23+
use OptionnableTrait;
2224
use ImagineAwareTrait;
2325

24-
public function __construct(ImagineInterface $imagine, array $thumbnailRules = [])
26+
/**
27+
* Converter image manipulator constructor.
28+
* @param ImagineInterface $imagine
29+
* @param array $conversions associative array [ <current_mime> => <new_mime> ]
30+
* @param array $options
31+
* - imagine_options : array of options to pass to imagine (jpeg_quality, png_compression_level, etc)
32+
*/
33+
public function __construct(ImagineInterface $imagine, array $thumbnailRules = [], array $options = [])
2534
{
26-
$this->setImagine($imagine);
35+
$this->setImagine($imagine, $options['imagine_options'] ?? []);
36+
unset($options['imagine_options']);
2737
foreach ($thumbnailRules as $thumbnailRule) {
2838
$this->addThumbnailRule($thumbnailRule);
2939
}
40+
$this->setOptions($options);
3041
}
3142

3243
/**
@@ -43,7 +54,7 @@ public function addThumbnailRule(ThumbnailRuleInterface $thumbnailRule)
4354
if ($thumbnailRule instanceof ImagineAwareInterface) {
4455
// not very LSP but handy
4556
if (!$thumbnailRule->getImagine()) {
46-
$thumbnailRule->setImagine($this->getImagine());
57+
$thumbnailRule->setImagine($this->getImagine(), $this->getImagineOptions());
4758
}
4859
}
4960
$this->thumbnailRules[] = $thumbnailRule;

src/ImageStack/ImageManipulator/WatermarkImageManipulator.php

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use Imagine\Image\Point;
1414
use Imagine\Image\Box;
1515
use Imagine\Image\Palette\RGB;
16+
use Imagine\Imagick\Image as IImage;
1617

1718
/**
1819
* Watermark image manipulator.
@@ -23,6 +24,7 @@
2324
class WatermarkImageManipulator implements ImageManipulatorInterface, ImagineAwareInterface {
2425
use OptionnableTrait;
2526
use ImagineAwareTrait;
27+
use AnimatedGifAwareImageManipulatorTrait;
2628

2729
const ANCHOR_LEFT = 0x01;
2830
const ANCHOR_CENTER = 0x02;
@@ -52,7 +54,8 @@ class WatermarkImageManipulator implements ImageManipulatorInterface, ImagineAwa
5254
*/
5355
public function __construct(ImagineInterface $imagine, $watermark, array $options = [])
5456
{
55-
$this->setImagine($imagine);
57+
$this->setImagine($imagine, $options['imagine_options'] ?? []);
58+
unset($options['imagine_options']);
5659
$this->watermark = $watermark;
5760
$this->setOptions($options);
5861
}
@@ -84,9 +87,8 @@ protected function isBit($value, $mask)
8487
protected function _manipulateImage(ImageWithImagineInterface $image, ImagePathInterface $path)
8588
{
8689
if (!$image->getImagine()) {
87-
$image->setImagine($this->getImagine());
90+
$image->setImagine($this->getImagine(), $this->getImagineOptions());
8891
}
89-
$image->setImagineOptions($this->getOption('imagine_options', []));
9092

9193
$reduce = $this->getOption('reduce', static::REDUCE_NONE);
9294
$anchor = $this->getOption('anchor', static::ANCHOR_CENTER | static::ANCHOR_MIDDLE);
@@ -210,7 +212,31 @@ protected function _manipulateImage(ImageWithImagineInterface $image, ImagePathI
210212
if ($repeatY) $deltaY = 0;
211213
}
212214

213-
$iImage->paste($watermark, new Point($deltaX, $deltaY));
215+
$deltaP = new Point($deltaX, $deltaY);
216+
217+
if ($this->handleAnimatedGif($image, function (IImage $iimage) use ($image) {
218+
// we take first frame conf
219+
/** @var IImage $iframe */
220+
$iframe = $iimage->layers()[0];
221+
$options = [
222+
'flatten' => false,
223+
'animated' => true,
224+
'animated.delay' => $iframe->getImagick()->getImageDelay() * 10,
225+
'animated.loop' => $iframe->getImagick()->getImageIterations(),
226+
];
227+
// put those options for next binary dump only
228+
$image->setEphemeralImagineOptions($options);
229+
230+
}, function (IImage $iframe) use ($watermark, $deltaP) {
231+
$iframe->paste($watermark, $deltaP);
232+
}, function (IImage $iimage) {
233+
// nothing
234+
})) {
235+
236+
$image->deprecateBinaryContent();
237+
return;
238+
}
239+
$iImage->paste($watermark, $deltaP);
214240
$image->deprecateBinaryContent();
215241
}
216242

src/ImageStack/ImageWithImagineInterface.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
use ImageStack\Api\ImageInterface;
55
use Imagine\Image\ImageInterface as ImagineImageInterface;
6+
use ImageStack\Api\Exception\ImageException;
67

78
/**
89
* Image that can handle imagine image interface.
@@ -11,11 +12,17 @@
1112
*/
1213
interface ImageWithImagineInterface extends ImageInterface, ImagineAwareInterface
1314
{
15+
// /**
16+
// * Set permanet imagine options for binary content generation.
17+
// * @param array $imagineOptions
18+
// */
19+
// public function setImagineOptions(array $imagineOptions);
20+
1421
/**
15-
* Set imagine options for binary content generation.
22+
* Set ephemeral imagine options for binary content generation. Will be reste after generation.
1623
* @param array $imagineOptions
1724
*/
18-
public function setImagineOptions(array $imagineOptions);
25+
public function setEphemeralImagineOptions(array $imagineOptions);
1926

2027
/**
2128
* Return an imagine image object from the image binary content.

0 commit comments

Comments
 (0)