123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459 |
- <?php
- /**
- * @link https://www.yiiframework.com/
- * @copyright Copyright (c) 2008 Yii Software LLC
- * @license https://www.yiiframework.com/license/
- */
- namespace yii\imagine;
- use Yii;
- use Imagine\Image\Box;
- use Imagine\Image\BoxInterface;
- use Imagine\Image\ImageInterface;
- use Imagine\Image\ImagineInterface;
- use Imagine\Image\ManipulatorInterface;
- use Imagine\Image\Point;
- use Imagine\Image\Palette\RGB;
- use Imagine\Filter\Basic\Autorotate;
- 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 ImagineInterface instance.
- */
- private static $_imagine;
- /**
- * @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 string background color to use when creating thumbnails in `ImageInterface::THUMBNAIL_INSET` mode with
- * both width and height specified. Default is white.
- *
- * @since 2.0.4
- */
- public static $thumbnailBackgroundColor = 'FFF';
- /**
- * @var string background alpha (transparency) to use when creating thumbnails in `ImageInterface::THUMBNAIL_INSET`
- * mode with both width and height specified. Default is solid.
- *
- * @since 2.0.4
- */
- public static $thumbnailBackgroundAlpha = 100;
- /**
- * Returns the `Imagine` object that supports various image manipulations.
- * @return ImagineInterface the `Imagine` object
- */
- public static function getImagine()
- {
- if (self::$_imagine === null) {
- self::$_imagine = static::createImagine();
- }
- return self::$_imagine;
- }
- /**
- * @param ImagineInterface $imagine the `Imagine` object.
- */
- public static function setImagine($imagine)
- {
- self::$_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));
- }
- /**
- * Takes either file path or ImageInterface. In case of file path, creates an instance of ImageInterface from it.
- *
- * @param string|resource|ImageInterface $image
- * @return ImageInterface
- * @throws \yii\base\InvalidParamException
- * @since 2.1.0
- */
- protected static function ensureImageInterfaceInstance($image)
- {
- if ($image instanceof ImageInterface) {
- return $image;
- }
- if (is_resource($image)) {
- return static::getImagine()->read($image);
- }
- if (is_string($image)) {
- return static::getImagine()->open(Yii::getAlias($image));
- }
- throw new InvalidParamException('File should be either ImageInterface, resource or a string containing file path.');
- }
- /**
- * Crops an image.
- *
- * For example:
- *
- * ```php
- * $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|resource|ImageInterface $image either ImageInterface, resource or a string containing file path
- * @param int $width the crop width
- * @param int $height the crop height
- * @param array $start the starting point. This must be an array with two elements representing `x` and `y` coordinates.
- * @return ImageInterface
- * @throws InvalidParamException if the `$start` parameter is invalid
- */
- public static function crop($image, $width, $height, array $start = [0, 0])
- {
- if (!isset($start[0], $start[1])) {
- throw new InvalidParamException('$start must be an array of two elements.');
- }
- return static::ensureImageInterfaceInstance($image)
- ->copy()
- ->crop(new Point($start[0], $start[1]), new Box($width, $height));
- }
- /**
- * Rotates an image automatically based on EXIF information.
- *
- * @param string|resource|ImageInterface $image either ImageInterface, resource or a string containing file path
- * @param string $color
- * @return \Imagine\Image\ImageInterface
- * @since 2.1.0
- */
- public static function autorotate($image, $color = '000000')
- {
- return (new Autorotate($color))->apply(static::ensureImageInterfaceInstance($image));
- }
- /**
- * Creates a thumbnail image.
- *
- * If one of thumbnail dimensions is set to `null`, another one is calculated automatically based on aspect ratio of
- * original image. Note that calculated thumbnail dimension may vary depending on the source image in this case.
- *
- * If both dimensions are specified, resulting thumbnail would be exactly the width and height specified. How it's
- * achieved depends on the mode defined via settings parameter.
- *
- * If `ImageInterface::THUMBNAIL_OUTBOUND` mode is used, which is default, then the thumbnail is scaled so that
- * its smallest side equals the length of the corresponding side in the original image. Any excess outside of
- * the scaled thumbnail’s area will be cropped, and the returned thumbnail will have the exact width and height
- * specified.
- *
- * If thumbnail mode is `ImageInterface::THUMBNAIL_INSET`, the original image is scaled down so it is fully
- * contained within the thumbnail dimensions. The rest is filled with background that could be configured via
- * [[Image::$thumbnailBackgroundColor]] and [[Image::$thumbnailBackgroundAlpha]].
- *
- * @param string|resource|ImageInterface $image either ImageInterface, resource or a string containing file path
- * @param int $width the width in pixels to create the thumbnail
- * @param int $height the height in pixels to create the thumbnail
- * @param int $settings settings for resizing original image, one or more of the ManipulatorInterface::THUMBNAIL_ flags (joined with |)
- * @return ImageInterface
- */
- public static function thumbnail($image, $width, $height, $settings = ManipulatorInterface::THUMBNAIL_OUTBOUND)
- {
- $img = self::ensureImageInterfaceInstance($image);
- /** @var BoxInterface $sourceBox */
- $sourceBox = $img->getSize();
- $thumbnailBox = static::getThumbnailBox($sourceBox, $width, $height);
- $allowUpscale = (bool) ($settings & ImageInterface::THUMBNAIL_FLAG_UPSCALE);
- if (self::isUpscaling($sourceBox, $thumbnailBox) && !$allowUpscale) {
- return $img->copy();
- }
- $img = $img->thumbnail($thumbnailBox, $settings);
- if ($settings & ImageInterface::THUMBNAIL_OUTBOUND) {
- return $img;
- }
- $size = $img->getSize();
- if ($size->getWidth() == $width && $size->getHeight() == $height) {
- return $img;
- }
- $palette = new RGB();
- $color = $palette->color(static::$thumbnailBackgroundColor, static::$thumbnailBackgroundAlpha);
- // create empty image to preserve aspect ratio of thumbnail
- $thumb = static::getImagine()->create($thumbnailBox, $color);
- // calculate points
- $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;
- }
- /**
- * Resizes an image.
- *
- * If one of the dimensions is set to `null`, another one is calculated automatically based on aspect ratio of
- * original image.
- *
- * If both of the dimensions are set then new dimensions are calculated so that image keeps aspect ratio.
- *
- * You can set $keepAspectRatio to false if you want to force fixed width and height.
- *
- * @param string|resource|ImageInterface $image either ImageInterface, resource or a string containing file path
- * @param int $width the width in pixels
- * @param int $height the height in pixels
- * @param bool $keepAspectRatio should the image keep aspect ratio
- * @param bool $allowUpscaling should the image be upscaled if needed
- * @return ImageInterface
- *
- * @since 2.1.1
- */
- public static function resize($image, $width, $height, $keepAspectRatio = true, $allowUpscaling = false)
- {
- $img = self::ensureImageInterfaceInstance($image)->copy();
- /** @var BoxInterface $sourceBox */
- $sourceBox = $img->getSize();
- $destinationBox = static::getBox($sourceBox, $width, $height, $keepAspectRatio);
- if ($allowUpscaling === false && self::isUpscaling($sourceBox, $destinationBox)) {
- return $img;
- }
- return $img->resize($destinationBox);
- }
- /**
- * Adds a watermark to an existing image.
- * @param string|resource|ImageInterface $image either ImageInterface, resource or a string containing file path
- * @param string|resource|ImageInterface $watermarkImage either ImageInterface, resource or a string containing watermark file path
- * @param array $start the starting point. This must be an array with two elements representing `x` and `y` coordinates.
- * @return ImageInterface
- * @throws InvalidParamException if `$start` is invalid
- */
- public static function watermark($image, $watermarkImage, array $start = [0, 0])
- {
- if (!isset($start[0], $start[1])) {
- throw new InvalidParamException('$start must be an array of two elements.');
- }
- $img = self::ensureImageInterfaceInstance($image);
- $watermark = self::ensureImageInterfaceInstance($watermarkImage);
- $img->paste($watermark, new Point($start[0], $start[1]));
- return $img;
- }
- /**
- * Draws a text string on an existing image.
- * @param string|resource|ImageInterface $image either ImageInterface, resource or a string containing file path
- * @param string $text the text to write to the image
- * @param string $fontFile the file path or path alias
- * @param array $start the starting position of the text. This must be an array with two elements representing `x` and `y` coordinates.
- * @param array $fontOptions the font options. The following options may be specified:
- *
- * - color: The font color. Defaults to "fff".
- * - size: The font size. Defaults to 12.
- * - angle: The angle to use to write the text. Defaults to 0.
- *
- * @return ImageInterface
- * @throws InvalidParamException if `$fontOptions` is invalid
- */
- public static function text($image, $text, $fontFile, array $start = [0, 0], array $fontOptions = [])
- {
- if (!isset($start[0], $start[1])) {
- throw new InvalidParamException('$start must be an array of two elements.');
- }
- $fontSize = ArrayHelper::getValue($fontOptions, 'size', 12);
- $fontColor = ArrayHelper::getValue($fontOptions, 'color', 'fff');
- $fontAngle = ArrayHelper::getValue($fontOptions, 'angle', 0);
- $palette = new RGB();
- $color = $palette->color($fontColor);
- $img = self::ensureImageInterfaceInstance($image);
- $font = static::getImagine()->font(Yii::getAlias($fontFile), $fontSize, $color);
- $img->draw()->text($text, $font, new Point($start[0], $start[1]), $fontAngle);
- return $img;
- }
- /**
- * Adds a frame around of the image. Please note that the image size will increase by `$margin` x 2.
- * @param string|resource|ImageInterface $image either ImageInterface, resource or a string containing file path
- * @param int $margin the frame size to add around the image
- * @param string $color the frame color
- * @param int $alpha the alpha value of the frame.
- * @return ImageInterface
- */
- public static function frame($image, $margin = 20, $color = '666', $alpha = 100)
- {
- $img = self::ensureImageInterfaceInstance($image);
- $size = $img->getSize();
- $pasteTo = new Point($margin, $margin);
- $palette = new RGB();
- $color = $palette->color($color, $alpha);
- $box = new Box($size->getWidth() + ceil($margin * 2), $size->getHeight() + ceil($margin * 2));
- $finalImage = static::getImagine()->create($box, $color);
- $finalImage->paste($img, $pasteTo);
- return $finalImage;
- }
- /**
- * Returns box for a thumbnail to be created. If one of the dimensions is set to `null`, another one is calculated
- * automatically based on width to height ratio of original image box.
- *
- * @param BoxInterface $sourceBox original image box
- * @param int $width thumbnail width
- * @param int $height thumbnail height
- * @return BoxInterface thumbnail box
- *
- * @since 2.0.4
- */
- public static function getThumbnailBox(BoxInterface $sourceBox, $width, $height)
- {
- if ($width !== null && $height !== null) {
- return new Box($width, $height);
- }
- return self::getBox($sourceBox, $width, $height, false);
- }
- /**
- * Returns box for an image to be created.
- *
- * If one of the dimensions is set to `null`, another one is calculated automatically based on width to height ratio
- * of original image box.
- *
- * If both of the dimensions are set then new dimensions are calculated so that image keeps aspect ratio.
- *
- * You can set $keepAspectRatio to false if you want to force fixed width and height.
- *
- * @param BoxInterface $sourceBox original image box
- * @param int $width new image width
- * @param int $height new image height
- * @param bool $keepAspectRatio should we keep aspect ratio even if both with and height are set
- * @return BoxInterface new image box
- *
- * @since 2.1.1
- */
- public static function getBox(BoxInterface $sourceBox, $width, $height, $keepAspectRatio = true)
- {
- if ($width === null && $height === null) {
- throw new InvalidParamException('Width and height cannot be null at same time.');
- }
- $ratio = $sourceBox->getWidth() / $sourceBox->getHeight();
- if ($keepAspectRatio === false) {
- if ($height === null) {
- $height = ceil($width / $ratio);
- } elseif ($width === null) {
- $width = ceil($height * $ratio);
- }
- } else {
- if ($height === null) {
- $height = ceil($width / $ratio);
- } elseif ($width === null) {
- $width = ceil($height * $ratio);
- } elseif ($width / $height > $ratio) {
- $width = $height * $ratio;
- } else {
- $height = $width / $ratio;
- }
- }
- return new Box($width, $height);
- }
- /**
- * Checks if upscaling is going to happen
- *
- * @param BoxInterface $sourceBox
- * @param BoxInterface $destinationBox
- * @return bool
- */
- public static function isUpscaling(BoxInterface $sourceBox, BoxInterface $destinationBox)
- {
- return ($sourceBox->getWidth() <= $destinationBox->getWidth() && $sourceBox->getHeight() <= $destinationBox->getHeight()) || (!$destinationBox->getWidth() && !$destinationBox->getHeight());
- }
- }
|