Commit 3cd7a744 by Qiang Xue

Refactored imagine extension.

parent df8d79ee
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\imagine;
use Yii;
use Imagine\Exception\InvalidArgumentException;
use Imagine\Image\Box;
use Imagine\Image\Color;
use Imagine\Image\ImageInterface;
use Imagine\Image\ImagineInterface;
use Imagine\Image\ManipulatorInterface;
use Imagine\Image\Point;
use yii\base\InvalidConfigException;
use yii\base\InvalidParamException;
use yii\helpers\ArrayHelper;
/**
* BaseImage provides concrete implementation for [[Image]].
*
* Do not use BaseImage. Use [[Image]] instead.
*
* @author Antonio Ramirez <amigo.cobos@gmail.com>
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class BaseImage
{
/**
* GD2 driver definition for Imagine implementation using the GD library.
*/
const DRIVER_GD2 = 'gd2';
/**
* imagick driver definition.
*/
const DRIVER_IMAGICK = 'imagick';
/**
* gmagick driver definition.
*/
const DRIVER_GMAGICK = 'gmagick';
/**
* @var array|string the driver to use. This can be either a single driver name or an array of driver names.
* If the latter, the first available driver will be used.
*/
public static $driver = [self::DRIVER_GMAGICK, self::DRIVER_IMAGICK, self::DRIVER_GD2];
/**
* @var ImagineInterface instance.
*/
private static $_imagine;
/**
* Returns the `Imagine` object that supports various image manipulations.
* @return ImagineInterface the `Imagine` object
*/
public static function getImagine()
{
if (static::$_imagine === null) {
static::$_imagine = static::createImagine();
}
return static::$_imagine;
}
/**
* @param Imagine\Image\ImagineInterface $imagine the `Imagine` object.
*/
public static function setImagine($imagine)
{
static::$_imagine = $imagine;
}
/**
* Creates an `Imagine` object based on the specified [[driver]].
* @return ImagineInterface the new `Imagine` object
* @throws InvalidConfigException if [[driver]] is unknown or the system doesn't support any [[driver]].
*/
protected static function createImagine()
{
foreach ((array)static::$driver as $driver) {
switch ($driver) {
case self::DRIVER_GMAGICK:
if (class_exists('Gmagick', false)) {
return new \Imagine\Gmagick\Imagine();
}
break;
case self::DRIVER_IMAGICK:
if (!class_exists('Imagick', false)) {
return new \Imagine\Imagick\Imagine();
}
break;
case self::DRIVER_GD2:
if (!function_exists('gd_info')) {
return new \Imagine\Gd\Imagine();
}
break;
default:
throw new InvalidConfigException("Unknown driver: $driver");
}
}
throw new InvalidConfigException("Your system does not support any of these drivers: " . implode(',', (array)static::$driver));
}
/**
* Crops an image.
*
* For example,
*
* ~~~
* $obj->crop('path\to\image.jpg', 200, 200, [5, 5]);
*
* $point = new \Imagine\Image\Point(5, 5);
* $obj->crop('path\to\image.jpg', 200, 200, $point);
* ~~~
*
* @param string $filename the image file path or path alias.
* @param integer $width the crop width
* @param integer $height the crop height
* @param array|Point $start the starting point. This can be either an array of `x` and `y` coordinates, or
* a `Point` object.
* @return ImageInterface
* @throws InvalidParamException if the `$start` parameter is invalid
*/
public static function crop($filename, $width, $height, $start = [0, 0])
{
if (is_array($start)) {
if (isset($start[0], $start[1])) {
$start = new Point($start[0], $start[1]);
} else {
throw new InvalidParamException('$start must be an array of two elements.');
}
}
if ($start instanceof Point) {
return static::getImagine()
->open(Yii::getAlias($filename))
->copy()
->crop($start, new Box($width, $height));
} else {
throw new InvalidParamException('$start must be either an array or an "Imagine\\Image\\Point" object.');
}
}
/**
* Creates a thumbnail image. The function differs from [[\Imagine\Image\ImageInterface::thumbnail()]] function that
* it keeps the aspect ratio of the image.
* @param string $filename the image file path or path alias.
* @param integer $width the width in pixels to create the thumbnail
* @param integer $height the height in pixels to create the thumbnail
* @param string $mode
* @return ImageInterface
*/
public static function thumbnail($filename, $width, $height, $mode = ManipulatorInterface::THUMBNAIL_OUTBOUND)
{
$box = new Box($width, $height);
$img = static::getImagine()->open(Yii::getAlias($filename));
if (($img->getSize()->getWidth() <= $box->getWidth() && $img->getSize()->getHeight() <= $box->getHeight()) || (!$box->getWidth() && !$box->getHeight())) {
return $img->copy();
}
$img = $img->thumbnail($box, $mode);
// create empty image to preserve aspect ratio of thumbnail
$thumb = static::getImagine()->create($box);
// calculate points
$size = $img->getSize();
$startX = 0;
$startY = 0;
if ($size->getWidth() < $width) {
$startX = ceil($width - $size->getWidth()) / 2;
}
if ($size->getHeight() < $height) {
$startY = ceil($height - $size->getHeight()) / 2;
}
$thumb->paste($img, new Point($startX, $startY));
return $thumb;
}
/**
* Adds a watermark to an existing image.
* @param string $filename the image file path or path alias.
* @param string $watermarkFilename the file path or path alias of the watermark image.
* @param array|Point $start the starting point. This can be either an array of `x` and `y` coordinates, or
* a `Point` object.
* @return ImageInterface
* @throws InvalidParamException if `$start` is invalid
*/
public static function watermark($filename, $watermarkFilename, $start = [0, 0])
{
if (is_array($start)) {
if (isset($start[0], $start[1])) {
$start = new Point($start[0], $start[1]);
} else {
throw new InvalidParamException('$start must be an array of two elements.');
}
}
if ($start instanceof Point) {
$img = static::getImagine()->open(Yii::getAlias($filename));
$watermark = static::getImagine()->open(Yii::getAlias($watermarkFilename));
return $img->paste($watermark, $start);
} else {
throw new InvalidParamException('$start must be either an array or an "Imagine\\Image\\Point" object.');
}
}
/**
* Draws a text string on an existing image.
* @param string $filename the image file path or path alias.
* @param string $text the text to write to the image
* @param array $fontOptions the font options. The following options may be specified:
*
* - font: The path to the font file to use to style the text. This option is required.
* - color: The font color. Defaults to "fff".
* - size: The font size. Defaults to 12.
* - x: The X position to write the text. Defaults to 5.
* - y: The Y position to write the text. Defaults to 5.
* - angle: The angle to use to write the text. Defaults to 0.
*
* @return ImageInterface
* @throws InvalidParamException if `$fontOptions` is invalid
*/
public static function text($filename, $text, array $fontOptions)
{
$font = ArrayHelper::getValue($fontOptions, 'font');
if ($font === null) {
throw new InvalidParamException('$fontOptions must contain a "font" key specifying which font file to use.');
}
$fontSize = ArrayHelper::getValue($fontOptions, 'size', 12);
$fontColor = ArrayHelper::getValue($fontOptions, 'color', 'fff');
$fontPosX = ArrayHelper::getValue($fontOptions, 'x', 5);
$fontPosY = ArrayHelper::getValue($fontOptions, 'y', 5);
$fontAngle = ArrayHelper::getValue($fontOptions, 'angle', 0);
$img = static::getImagine()->open(Yii::getAlias($filename));
$font = static::getImagine()->font(Yii::getAlias($font), $fontSize, new Color($fontColor));
return $img->draw()->text($text, $font, new Point($fontPosX, $fontPosY), $fontAngle);
}
/**
* Adds a frame around of the image. Please note that the image size will increase by `$margin` x 2.
* @param string $filename the full path to the image file
* @param integer $margin the frame size to add around the image
* @param string $color the frame color
* @param integer $alpha the alpha value of the frame.
* @return ImageInterface
*/
public static function frame($filename, $margin = 5, $color = '000', $alpha = 100)
{
$img = static::getImagine()->open(Yii::getAlias($filename));
$size = $img->getSize();
$pasteTo = new Point($margin, $margin);
$padColor = new Color($color, $alpha);
$box = new Box($size->getWidth() + ceil($margin * 2), $size->getHeight() + ceil($margin * 2));
$image = static::getImagine()->create($box, $padColor);
return $image->paste($img, $pasteTo);
}
}
Image Extension for Yii 2 Image Extension for Yii 2
============================== =========================
This extension adds most common image functions and also acts as a wrapper to [Imagine](http://imagine.readthedocs.org/) This extension adds most common image functions and also acts as a wrapper to [Imagine](http://imagine.readthedocs.org/)
image manipulation library. image manipulation library.
...@@ -27,44 +27,19 @@ to the `require` section of your composer.json. ...@@ -27,44 +27,19 @@ to the `require` section of your composer.json.
Usage & Documentation Usage & Documentation
--------------------- ---------------------
This extension is a wrapper to the [Imagine](http://imagine.readthedocs.org/) and also adds the most common methods This extension is a wrapper to the [Imagine](http://imagine.readthedocs.org/) and also adds the most commonly used
used for Image manipulation. image manipulation methods.
To use this extension, you can use it in to ways, whether you configure it on your application file or you use it The following example shows how to use this extension:
directly.
The following shows how to use it via application configuration file: ```php
```
// configuring on your application configuration file
'components' => [
'image' => [
'class' => 'yii\imagine\Image',
'driver' => \yii\imagine\Image::DRIVER_GD2,
]
...
]
// Once configured you can access to the extension like this:
$img = Yii::$app->image->thumb('path/to/image.jpg', 120, 120);
```
This is how to use it directly:
```
use yii\imagine\Image; use yii\imagine\Image;
$image = new Image();
$img = $image->thumb('path/to/image.jpg', 120, 120);
```
**About the methods**
Each method returns an instance to `\Imagine\Image\ManipulatorInterface`, that means that you can easily make use of the methods included in the `Imagine` library:
```
// frame, rotate and save an image // frame, rotate and save an image
Image::frame('path/to/image.jpg', 5, '666', 0)
Yii::$app->image->frame('path/to/image.jpg', 5, '666', 0)
->rotate(-8) ->rotate(-8)
->save('path/to/destination/image.jpg', ['quality' => 50]); ->save('path/to/destination/image.jpg', ['quality' => 50]);
``` ```
Note that each `Image` method returns an instance of `\Imagine\Image\ImageInterface`.
This means you can make use of the methods included in the `Imagine` library:
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
namespace yiiunit\extensions\imagine; namespace yiiunit\extensions\imagine;
use Yii; use Yii;
use yii\imagine\Image;
use Imagine\Image\Point; use Imagine\Image\Point;
use yiiunit\VendorTestCase; use yiiunit\VendorTestCase;
...@@ -9,10 +10,6 @@ Yii::setAlias('@yii/imagine', __DIR__ . '/../../../../extensions/yii/imagine'); ...@@ -9,10 +10,6 @@ Yii::setAlias('@yii/imagine', __DIR__ . '/../../../../extensions/yii/imagine');
abstract class AbstractImageTest extends VendorTestCase abstract class AbstractImageTest extends VendorTestCase
{ {
/**
* @var yii\imagine\Image
*/
protected $image;
protected $imageFile; protected $imageFile;
protected $watermarkFile; protected $watermarkFile;
protected $runtimeTextFile; protected $runtimeTextFile;
...@@ -33,14 +30,15 @@ abstract class AbstractImageTest extends VendorTestCase ...@@ -33,14 +30,15 @@ abstract class AbstractImageTest extends VendorTestCase
@unlink($this->runtimeWatermarkFile); @unlink($this->runtimeWatermarkFile);
} }
public function testText() { public function testText()
if(!$this->isFontTestSupported()) { {
if (!$this->isFontTestSupported()) {
$this->markTestSkipped('Skipping ImageGdTest Gd not installed'); $this->markTestSkipped('Skipping ImageGdTest Gd not installed');
} }
$fontFile = Yii::getAlias('@yiiunit/data/imagine/GothamRnd-Light') . '.otf'; $fontFile = Yii::getAlias('@yiiunit/data/imagine/GothamRnd-Light') . '.otf';
$img = $this->image->text($this->imageFile, 'Yii-2 Image', [ $img = Image::text($this->imageFile, 'Yii-2 Image', [
'font' => $fontFile, 'font' => $fontFile,
'size' => 12, 'size' => 12,
'color' => '000' 'color' => '000'
...@@ -53,14 +51,14 @@ abstract class AbstractImageTest extends VendorTestCase ...@@ -53,14 +51,14 @@ abstract class AbstractImageTest extends VendorTestCase
public function testCrop() public function testCrop()
{ {
$point = [20,20]; $point = [20, 20];
$img = $this->image->crop($this->imageFile, 100, 100, $point); $img = Image::crop($this->imageFile, 100, 100, $point);
$this->assertEquals(100, $img->getSize()->getWidth()); $this->assertEquals(100, $img->getSize()->getWidth());
$this->assertEquals(100, $img->getSize()->getHeight()); $this->assertEquals(100, $img->getSize()->getHeight());
$point = new Point(20, 20); $point = new Point(20, 20);
$img = $this->image->crop($this->imageFile, 100, 100, $point); $img = Image::crop($this->imageFile, 100, 100, $point);
$this->assertEquals(100, $img->getSize()->getWidth()); $this->assertEquals(100, $img->getSize()->getWidth());
$this->assertEquals(100, $img->getSize()->getHeight()); $this->assertEquals(100, $img->getSize()->getHeight());
...@@ -68,7 +66,7 @@ abstract class AbstractImageTest extends VendorTestCase ...@@ -68,7 +66,7 @@ abstract class AbstractImageTest extends VendorTestCase
public function testWatermark() public function testWatermark()
{ {
$img = $this->image->watermark($this->imageFile, $this->watermarkFile); $img = Image::watermark($this->imageFile, $this->watermarkFile);
$img->save($this->runtimeWatermarkFile); $img->save($this->runtimeWatermarkFile);
$this->assertTrue(file_exists($this->runtimeWatermarkFile)); $this->assertTrue(file_exists($this->runtimeWatermarkFile));
} }
...@@ -76,9 +74,9 @@ abstract class AbstractImageTest extends VendorTestCase ...@@ -76,9 +74,9 @@ abstract class AbstractImageTest extends VendorTestCase
public function testFrame() public function testFrame()
{ {
$frameSize = 5; $frameSize = 5;
$original = $this->image->getImagine()->open($this->imageFile); $original = Image::getImagine()->open($this->imageFile);
$originalSize = $original->getSize(); $originalSize = $original->getSize();
$img = $this->image->frame($this->imageFile, $frameSize, '666', 0); $img = Image::frame($this->imageFile, $frameSize, '666', 0);
$size = $img->getSize(); $size = $img->getSize();
$this->assertEquals($size->getWidth(), $originalSize->getWidth() + ($frameSize * 2)); $this->assertEquals($size->getWidth(), $originalSize->getWidth() + ($frameSize * 2));
...@@ -86,7 +84,7 @@ abstract class AbstractImageTest extends VendorTestCase ...@@ -86,7 +84,7 @@ abstract class AbstractImageTest extends VendorTestCase
public function testThumbnail() public function testThumbnail()
{ {
$img = $this->image->thumbnail($this->imageFile, 120, 120); $img = Image::thumbnail($this->imageFile, 120, 120);
$this->assertEquals(120, $img->getSize()->getWidth()); $this->assertEquals(120, $img->getSize()->getWidth());
$this->assertEquals(120, $img->getSize()->getHeight()); $this->assertEquals(120, $img->getSize()->getHeight());
...@@ -95,22 +93,26 @@ abstract class AbstractImageTest extends VendorTestCase ...@@ -95,22 +93,26 @@ abstract class AbstractImageTest extends VendorTestCase
/** /**
* @expectedException \yii\base\InvalidConfigException * @expectedException \yii\base\InvalidConfigException
*/ */
public function testShouldThrowExceptionOnDriverInvalidArgument() { public function testShouldThrowExceptionOnDriverInvalidArgument()
$this->image->setDriver('fake-driver'); {
Image::setImagine(null);
Image::$driver = 'fake-driver';
} }
/** /**
* @expectedException \InvalidArgumentException * @expectedException \InvalidArgumentException
*/ */
public function testShouldThrowExceptionOnCropInvalidArgument() { public function testShouldThrowExceptionOnCropInvalidArgument()
$this->image->crop($this->imageFile, 100, 100, new \stdClass()); {
Image::crop($this->imageFile, 100, 100, new \stdClass());
} }
/** /**
* @expectedException \InvalidArgumentException * @expectedException \InvalidArgumentException
*/ */
public function testShouldThrowExceptionOnWatermarkInvalidArgument() { public function testShouldThrowExceptionOnWatermarkInvalidArgument()
$this->image->watermark($this->imageFile, $this->watermarkFile, new \stdClass()); {
Image::watermark($this->imageFile, $this->watermarkFile, new \stdClass());
} }
......
...@@ -10,14 +10,13 @@ use yii\imagine\Image; ...@@ -10,14 +10,13 @@ use yii\imagine\Image;
*/ */
class ImageGdTest extends AbstractImageTest class ImageGdTest extends AbstractImageTest
{ {
protected function setUp() protected function setUp()
{ {
if (!function_exists('gd_info')) { if (!function_exists('gd_info')) {
$this->markTestSkipped('Skipping ImageGdTest, Gd not installed'); $this->markTestSkipped('Skipping ImageGdTest, Gd not installed');
} else { } else {
$this->image = new Image(); Image::setImagine(null);
$this->image->setDriver(Image::DRIVER_GD2); Image::$driver = Image::DRIVER_GD2;
parent::setUp(); parent::setUp();
} }
} }
......
...@@ -16,8 +16,8 @@ class ImageGmagickTest extends AbstractImageTest ...@@ -16,8 +16,8 @@ class ImageGmagickTest extends AbstractImageTest
if (!class_exists('Gmagick')) { if (!class_exists('Gmagick')) {
$this->markTestSkipped('Skipping ImageGmagickTest, Gmagick is not installed'); $this->markTestSkipped('Skipping ImageGmagickTest, Gmagick is not installed');
} else { } else {
$this->image = new Image(); Image::setImagine(null);
$this->image->setDriver(Image::DRIVER_GMAGICK); Image::$driver = Image::DRIVER_GMAGICK;
parent::setUp(); parent::setUp();
} }
} }
......
...@@ -16,8 +16,8 @@ class ImageImagickTest extends AbstractImageTest ...@@ -16,8 +16,8 @@ class ImageImagickTest extends AbstractImageTest
if (!class_exists('Imagick')) { if (!class_exists('Imagick')) {
$this->markTestSkipped('Skipping ImageImagickTest, Imagick is not installed'); $this->markTestSkipped('Skipping ImageImagickTest, Imagick is not installed');
} else { } else {
$this->image = new Image(); Image::setImagine(null);
$this->image->setDriver(Image::DRIVER_IMAGICK); Image::$driver = Image::DRIVER_IMAGICK;
parent::setUp(); parent::setUp();
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment