vendor/imagine/imagine/src/Gd/Image.php line 778

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Imagine package.
  4.  *
  5.  * (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Imagine\Gd;
  11. use Imagine\Driver\InfoProvider;
  12. use Imagine\Exception\InvalidArgumentException;
  13. use Imagine\Exception\OutOfBoundsException;
  14. use Imagine\Exception\RuntimeException;
  15. use Imagine\Factory\ClassFactoryInterface;
  16. use Imagine\Image\AbstractImage;
  17. use Imagine\Image\BoxInterface;
  18. use Imagine\Image\Fill\FillInterface;
  19. use Imagine\Image\Format;
  20. use Imagine\Image\ImageInterface;
  21. use Imagine\Image\Metadata\MetadataBag;
  22. use Imagine\Image\Palette\Color\ColorInterface;
  23. use Imagine\Image\Palette\Color\RGB as RGBColor;
  24. use Imagine\Image\Palette\PaletteInterface;
  25. use Imagine\Image\Point;
  26. use Imagine\Image\PointInterface;
  27. use Imagine\Image\ProfileInterface;
  28. use Imagine\Utils\ErrorHandling;
  29. /**
  30.  * Image implementation using the GD library.
  31.  */
  32. final class Image extends AbstractImage implements InfoProvider
  33. {
  34.     /**
  35.      * @var resource|\GdImage
  36.      */
  37.     private $resource;
  38.     /**
  39.      * @var \Imagine\Gd\Layers|null
  40.      */
  41.     private $layers;
  42.     /**
  43.      * @var \Imagine\Image\Palette\PaletteInterface
  44.      */
  45.     private $palette;
  46.     /**
  47.      * Constructs a new Image instance.
  48.      *
  49.      * @param resource|\GdImage $resource
  50.      * @param \Imagine\Image\Palette\PaletteInterface $palette
  51.      * @param \Imagine\Image\Metadata\MetadataBag $metadata
  52.      */
  53.     public function __construct($resourcePaletteInterface $paletteMetadataBag $metadata)
  54.     {
  55.         $this->metadata $metadata;
  56.         $this->palette $palette;
  57.         $this->resource $resource;
  58.     }
  59.     /**
  60.      * Makes sure the current image resource is destroyed.
  61.      */
  62.     public function __destruct()
  63.     {
  64.         if ($this->resource) {
  65.             if (is_resource($this->resource) && get_resource_type($this->resource) === 'gd' || $this->resource instanceof \GdImage) {
  66.                 imagedestroy($this->resource);
  67.             }
  68.             $this->resource null;
  69.         }
  70.     }
  71.     /**
  72.      * {@inheritdoc}
  73.      *
  74.      * @see \Imagine\Image\AbstractImage::__clone()
  75.      */
  76.     public function __clone()
  77.     {
  78.         parent::__clone();
  79.         $size $this->getSize();
  80.         $copy $this->createImage($size'copy');
  81.         if (imagecopy($copy$this->resource0000$size->getWidth(), $size->getHeight()) === false) {
  82.             imagedestroy($copy);
  83.             throw new RuntimeException('Image copy operation failed');
  84.         }
  85.         $this->resource $copy;
  86.         $this->palette = clone $this->palette;
  87.         if ($this->layers !== null) {
  88.             $this->layers $this->getClassFactory()->createLayers(ClassFactoryInterface::HANDLE_GD$this$this->layers->key());
  89.         }
  90.     }
  91.     /**
  92.      * {@inheritdoc}
  93.      *
  94.      * @see \Imagine\Driver\InfoProvider::getDriverInfo()
  95.      * @since 1.3.0
  96.      */
  97.     public static function getDriverInfo($required true)
  98.     {
  99.         return DriverInfo::get($required);
  100.     }
  101.     /**
  102.      * Returns Gd resource.
  103.      *
  104.      * @return resource|\GdImage
  105.      */
  106.     public function getGdResource()
  107.     {
  108.         return $this->resource;
  109.     }
  110.     /**
  111.      * {@inheritdoc}
  112.      *
  113.      * @see \Imagine\Image\ManipulatorInterface::copy()
  114.      */
  115.     final public function copy()
  116.     {
  117.         return clone $this;
  118.     }
  119.     /**
  120.      * {@inheritdoc}
  121.      *
  122.      * @see \Imagine\Image\ManipulatorInterface::crop()
  123.      */
  124.     final public function crop(PointInterface $startBoxInterface $size)
  125.     {
  126.         if (!$start->in($this->getSize())) {
  127.             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');
  128.         }
  129.         $width $size->getWidth();
  130.         $height $size->getHeight();
  131.         $dest $this->createImage($size'crop');
  132.         if (imagecopy($dest$this->resource00$start->getX(), $start->getY(), $width$height) === false) {
  133.             imagedestroy($dest);
  134.             throw new RuntimeException('Image crop operation failed');
  135.         }
  136.         imagedestroy($this->resource);
  137.         $this->resource $dest;
  138.         return $this;
  139.     }
  140.     /**
  141.      * {@inheritdoc}
  142.      *
  143.      * @see \Imagine\Image\ManipulatorInterface::paste()
  144.      */
  145.     final public function paste(ImageInterface $imagePointInterface $start$alpha 100)
  146.     {
  147.         if (!$image instanceof self) {
  148.             throw new InvalidArgumentException(sprintf('Gd\Image can only paste() Gd\Image instances, %s given'get_class($image)));
  149.         }
  150.         $alpha = (int) round($alpha);
  151.         if ($alpha || $alpha 100) {
  152.             throw new InvalidArgumentException(sprintf('The %1$s argument can range from %2$d to %3$d, but you specified %4$d.''$alpha'0100$alpha));
  153.         }
  154.         $size $image->getSize();
  155.         if ($alpha === 100) {
  156.             imagealphablending($this->resourcetrue);
  157.             imagealphablending($image->resourcetrue);
  158.             $success imagecopy($this->resource$image->resource$start->getX(), $start->getY(), 00$size->getWidth(), $size->getHeight());
  159.             imagealphablending($this->resourcefalse);
  160.             imagealphablending($image->resourcefalse);
  161.             if ($success === false) {
  162.                 throw new RuntimeException('Image paste operation failed');
  163.             }
  164.         } elseif ($alpha 0) {
  165.             if (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) === false) {
  166.                 throw new RuntimeException('Image paste operation failed');
  167.             }
  168.         }
  169.         return $this;
  170.     }
  171.     /**
  172.      * {@inheritdoc}
  173.      *
  174.      * Please remark that GD doesn't support different filters, so the $filter argument is ignored.
  175.      *
  176.      * @see \Imagine\Image\ManipulatorInterface::resize()
  177.      */
  178.     final public function resize(BoxInterface $size$filter ImageInterface::FILTER_UNDEFINED)
  179.     {
  180.         if (!in_array($filter, static::getAllFilterValues(), true)) {
  181.             throw new InvalidArgumentException('Unsupported filter type');
  182.         }
  183.         $width $size->getWidth();
  184.         $height $size->getHeight();
  185.         $dest $this->createImage($size'resize');
  186.         imagealphablending($this->resourcetrue);
  187.         imagealphablending($desttrue);
  188.         $success imagecopyresampled($dest$this->resource0000$width$heightimagesx($this->resource), imagesy($this->resource));
  189.         imagealphablending($this->resourcefalse);
  190.         imagealphablending($destfalse);
  191.         if ($success === false) {
  192.             imagedestroy($dest);
  193.             throw new RuntimeException('Image resize operation failed');
  194.         }
  195.         imagedestroy($this->resource);
  196.         $this->resource $dest;
  197.         return $this;
  198.     }
  199.     /**
  200.      * {@inheritdoc}
  201.      *
  202.      * @see \Imagine\Image\ManipulatorInterface::rotate()
  203.      */
  204.     final public function rotate($angleColorInterface $background null)
  205.     {
  206.         if ($background === null) {
  207.             $background $this->palette->color('fff');
  208.         }
  209.         $color $this->getColor($background);
  210.         $resource imagerotate($this->resource, -$angle$color);
  211.         if ($resource === false) {
  212.             throw new RuntimeException('Image rotate operation failed');
  213.         }
  214.         imagedestroy($this->resource);
  215.         $this->resource $resource;
  216.         return $this;
  217.     }
  218.     /**
  219.      * {@inheritdoc}
  220.      *
  221.      * @see \Imagine\Image\ManipulatorInterface::save()
  222.      */
  223.     final public function save($path null, array $options = array())
  224.     {
  225.         $path $path === null ? (isset($this->metadata['filepath']) ? $this->metadata['filepath'] : $path) : $path;
  226.         if ($path === null) {
  227.             throw new RuntimeException('You can omit save path only if image has been open from a file');
  228.         }
  229.         if (isset($options['format'])) {
  230.             $format $options['format'];
  231.         } elseif ('' !== $extension pathinfo($path\PATHINFO_EXTENSION)) {
  232.             $format $extension;
  233.         } else {
  234.             $originalPath = isset($this->metadata['filepath']) ? $this->metadata['filepath'] : null;
  235.             $format pathinfo($originalPath\PATHINFO_EXTENSION);
  236.         }
  237.         $formatInfo = static::getDriverInfo()->getSupportedFormats()->find($format);
  238.         if ($formatInfo === null) {
  239.             throw new InvalidArgumentException(sprintf(
  240.                 'Saving image in "%s" format is not supported, please use one of the following extensions: "%s"',
  241.                 $format,
  242.                 implode('", "', static::getDriverInfo()->getSupportedFormats()->getAllIDs())
  243.             ));
  244.         }
  245.         $this->saveOrOutput($formatInfo$options$path);
  246.         return $this;
  247.     }
  248.     /**
  249.      * {@inheritdoc}
  250.      *
  251.      * @see \Imagine\Image\ManipulatorInterface::show()
  252.      */
  253.     public function show($format, array $options = array())
  254.     {
  255.         $formatInfo = static::getDriverInfo()->getSupportedFormats()->find($format);
  256.         if ($formatInfo === null) {
  257.             throw new InvalidArgumentException(sprintf(
  258.                 'Displaying an image in "%s" format is not supported, please use one of the following formats: "%s"',
  259.                 $format,
  260.                 implode('", "', static::getDriverInfo()->getSupportedFormats()->getAllIDs())
  261.             ));
  262.         }
  263.         header('Content-type: ' $formatInfo->getMimeType());
  264.         $this->saveOrOutput($formatInfo$options);
  265.         return $this;
  266.     }
  267.     /**
  268.      * {@inheritdoc}
  269.      *
  270.      * @see \Imagine\Image\ImageInterface::get()
  271.      */
  272.     public function get($format, array $options = array())
  273.     {
  274.         $formatInfo = static::getDriverInfo()->getSupportedFormats()->find($format);
  275.         if ($formatInfo === null) {
  276.             throw new InvalidArgumentException(sprintf(
  277.                 'Creating an image in "%s" format is not supported, please use one of the following formats: "%s"',
  278.                 $format,
  279.                 implode('", "', static::getDriverInfo()->getSupportedFormats()->getAllIDs())
  280.             ));
  281.         }
  282.         ob_start();
  283.         $this->saveOrOutput($formatInfo$options);
  284.         return ob_get_clean();
  285.     }
  286.     /**
  287.      * {@inheritdoc}
  288.      *
  289.      * @see \Imagine\Image\ImageInterface::__toString()
  290.      */
  291.     public function __toString()
  292.     {
  293.         return $this->get(Format::ID_PNG);
  294.     }
  295.     /**
  296.      * {@inheritdoc}
  297.      *
  298.      * @see \Imagine\Image\ManipulatorInterface::flipHorizontally()
  299.      */
  300.     final public function flipHorizontally()
  301.     {
  302.         if (function_exists('imageflip')) {
  303.             imageflip($this->resourceIMG_FLIP_HORIZONTAL);
  304.         } else {
  305.             $size $this->getSize();
  306.             $width $size->getWidth();
  307.             $height $size->getHeight();
  308.             $dest $this->createImage($size'flip');
  309.             for ($i 0$i $width$i++) {
  310.                 if (imagecopy($dest$this->resource$i0, ($width 1) - $i01$height) === false) {
  311.                     imagedestroy($dest);
  312.                     throw new RuntimeException('Horizontal flip operation failed');
  313.                 }
  314.             }
  315.             imagedestroy($this->resource);
  316.             $this->resource $dest;
  317.         }
  318.         return $this;
  319.     }
  320.     /**
  321.      * {@inheritdoc}
  322.      *
  323.      * @see \Imagine\Image\ManipulatorInterface::flipVertically()
  324.      */
  325.     final public function flipVertically()
  326.     {
  327.         if (function_exists('imageflip')) {
  328.             imageflip($this->resourceIMG_FLIP_VERTICAL);
  329.         } else {
  330.             $size $this->getSize();
  331.             $width $size->getWidth();
  332.             $height $size->getHeight();
  333.             $dest $this->createImage($size'flip');
  334.             for ($i 0$i $height$i++) {
  335.                 if (imagecopy($dest$this->resource0$i0, ($height 1) - $i$width1) === false) {
  336.                     imagedestroy($dest);
  337.                     throw new RuntimeException('Vertical flip operation failed');
  338.                 }
  339.             }
  340.             imagedestroy($this->resource);
  341.             $this->resource $dest;
  342.         }
  343.         return $this;
  344.     }
  345.     /**
  346.      * {@inheritdoc}
  347.      *
  348.      * @see \Imagine\Image\ManipulatorInterface::strip()
  349.      */
  350.     final public function strip()
  351.     {
  352.         // GD strips profiles and comment, so there's nothing to do here
  353.         return $this;
  354.     }
  355.     /**
  356.      * {@inheritdoc}
  357.      *
  358.      * @see \Imagine\Image\ImageInterface::draw()
  359.      */
  360.     public function draw()
  361.     {
  362.         return $this->getClassFactory()->createDrawer(ClassFactoryInterface::HANDLE_GD$this->resource);
  363.     }
  364.     /**
  365.      * {@inheritdoc}
  366.      *
  367.      * @see \Imagine\Image\ImageInterface::effects()
  368.      */
  369.     public function effects()
  370.     {
  371.         return $this->getClassFactory()->createEffects(ClassFactoryInterface::HANDLE_GD$this->resource);
  372.     }
  373.     /**
  374.      * {@inheritdoc}
  375.      *
  376.      * @see \Imagine\Image\ImageInterface::getSize()
  377.      */
  378.     public function getSize()
  379.     {
  380.         return $this->getClassFactory()->createBox(imagesx($this->resource), imagesy($this->resource));
  381.     }
  382.     /**
  383.      * {@inheritdoc}
  384.      *
  385.      * @see \Imagine\Image\ManipulatorInterface::applyMask()
  386.      */
  387.     public function applyMask(ImageInterface $mask)
  388.     {
  389.         if (!$mask instanceof self) {
  390.             throw new InvalidArgumentException('Cannot mask non-gd images');
  391.         }
  392.         $size $this->getSize();
  393.         $maskSize $mask->getSize();
  394.         if ($size != $maskSize) {
  395.             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));
  396.         }
  397.         for ($x 0$width $size->getWidth(); $x $width$x++) {
  398.             for ($y 0$height $size->getHeight(); $y $height$y++) {
  399.                 $position = new Point($x$y);
  400.                 $color $this->getColorAt($position);
  401.                 $maskColor $mask->getColorAt($position);
  402.                 $delta = (int) round($color->getAlpha() * $maskColor->getRed() / 255) * -1;
  403.                 if (imagesetpixel($this->resource$x$y$this->getColor($color->dissolve($delta))) === false) {
  404.                     throw new RuntimeException('Apply mask operation failed');
  405.                 }
  406.             }
  407.         }
  408.         return $this;
  409.     }
  410.     /**
  411.      * {@inheritdoc}
  412.      *
  413.      * @see \Imagine\Image\ManipulatorInterface::fill()
  414.      */
  415.     public function fill(FillInterface $fill)
  416.     {
  417.         $size $this->getSize();
  418.         for ($x 0$width $size->getWidth(); $x $width$x++) {
  419.             for ($y 0$height $size->getHeight(); $y $height$y++) {
  420.                 if (imagesetpixel($this->resource$x$y$this->getColor($fill->getColor(new Point($x$y)))) === false) {
  421.                     throw new RuntimeException('Fill operation failed');
  422.                 }
  423.             }
  424.         }
  425.         return $this;
  426.     }
  427.     /**
  428.      * {@inheritdoc}
  429.      *
  430.      * @see \Imagine\Image\ImageInterface::mask()
  431.      */
  432.     public function mask()
  433.     {
  434.         $mask $this->copy();
  435.         if (imagefilter($mask->resourceIMG_FILTER_GRAYSCALE) === false) {
  436.             throw new RuntimeException('Mask operation failed');
  437.         }
  438.         return $mask;
  439.     }
  440.     /**
  441.      * {@inheritdoc}
  442.      *
  443.      * @see \Imagine\Image\ImageInterface::histogram()
  444.      */
  445.     public function histogram()
  446.     {
  447.         $size $this->getSize();
  448.         $colors = array();
  449.         for ($x 0$width $size->getWidth(); $x $width$x++) {
  450.             for ($y 0$height $size->getHeight(); $y $height$y++) {
  451.                 $colors[] = $this->getColorAt(new Point($x$y));
  452.             }
  453.         }
  454.         return array_values(array_unique($colors));
  455.     }
  456.     /**
  457.      * {@inheritdoc}
  458.      *
  459.      * @see \Imagine\Image\ImageInterface::getColorAt()
  460.      */
  461.     public function getColorAt(PointInterface $point)
  462.     {
  463.         if (!$point->in($this->getSize())) {
  464.             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()));
  465.         }
  466.         $index imagecolorat($this->resource$point->getX(), $point->getY());
  467.         $info imagecolorsforindex($this->resource$index);
  468.         return $this->palette->color(array($info['red'], $info['green'], $info['blue']), max(min(100 - (int) round($info['alpha'] / 127 100), 100), 0));
  469.     }
  470.     /**
  471.      * {@inheritdoc}
  472.      *
  473.      * @see \Imagine\Image\ImageInterface::layers()
  474.      */
  475.     public function layers()
  476.     {
  477.         if ($this->layers === null) {
  478.             $this->layers $this->getClassFactory()->createLayers(ClassFactoryInterface::HANDLE_GD$this);
  479.         }
  480.         return $this->layers;
  481.     }
  482.     /**
  483.      * {@inheritdoc}
  484.      *
  485.      * @see \Imagine\Image\ImageInterface::interlace()
  486.      */
  487.     public function interlace($scheme)
  488.     {
  489.         static $supportedInterlaceSchemes = array(
  490.             ImageInterface::INTERLACE_NONE => 0,
  491.             ImageInterface::INTERLACE_LINE => 1,
  492.             ImageInterface::INTERLACE_PLANE => 1,
  493.             ImageInterface::INTERLACE_PARTITION => 1,
  494.         );
  495.         if (!array_key_exists($scheme$supportedInterlaceSchemes)) {
  496.             throw new InvalidArgumentException('Unsupported interlace type');
  497.         }
  498.         imageinterlace($this->resource$supportedInterlaceSchemes[$scheme]);
  499.         return $this;
  500.     }
  501.     /**
  502.      * {@inheritdoc}
  503.      *
  504.      * @see \Imagine\Image\ImageInterface::palette()
  505.      */
  506.     public function palette()
  507.     {
  508.         return $this->palette;
  509.     }
  510.     /**
  511.      * {@inheritdoc}
  512.      *
  513.      * @see \Imagine\Image\ImageInterface::profile()
  514.      */
  515.     public function profile(ProfileInterface $profile)
  516.     {
  517.         static::getDriverInfo()->requireFeature(DriverInfo::FEATURE_COLORPROFILES);
  518.     }
  519.     /**
  520.      * {@inheritdoc}
  521.      *
  522.      * @see \Imagine\Image\ImageInterface::usePalette()
  523.      */
  524.     public function usePalette(PaletteInterface $palette)
  525.     {
  526.         if ($this->palette->name() === $palette->name()) {
  527.             return $this;
  528.         }
  529.         static::getDriverInfo()->requirePaletteSupport($palette);
  530.         $this->palette $palette;
  531.         return $this;
  532.     }
  533.     /**
  534.      * Performs save or show operation using one of GD's image... functions.
  535.      *
  536.      * @param \Imagine\Image\Format $format
  537.      * @param array $options
  538.      * @param string $filename
  539.      *
  540.      * @throws \Imagine\Exception\InvalidArgumentException
  541.      * @throws \Imagine\Exception\RuntimeException
  542.      */
  543.     private function saveOrOutput(Format $format, array $options$filename null)
  544.     {
  545.         switch ($format->getID()) {
  546.             default:
  547.                 $saveFunction 'image' $format->getID();
  548.                 break;
  549.         }
  550.         $args array_merge(array(&$this->resource$filename), $this->finalizeOptions($format$options));
  551.         ErrorHandling::throwingRuntimeException(E_WARNING E_NOTICE, function () use ($saveFunction$args) {
  552.             if (call_user_func_array($saveFunction$args) === false) {
  553.                 throw new RuntimeException('Save operation failed');
  554.             }
  555.         });
  556.     }
  557.     /**
  558.      * @param \Imagine\Image\Format $format
  559.      * @param array $options
  560.      *
  561.      * @throws \Imagine\Exception\InvalidArgumentException
  562.      *
  563.      * @return array
  564.      */
  565.     private function finalizeOptions(Format $format, array $options)
  566.     {
  567.         $result = array();
  568.         switch ($format->getID()) {
  569.             case Format::ID_AVIF:
  570.                 // ranges from 0 (worst quality, smaller file) to 100 (best quality, larger file). If -1 is provided, the default value is used
  571.                 $quality = -1;
  572.                 // ranges from 0 (slow, smaller file) to 10 (fast, larger file). If -1 is provided, the default value is used
  573.                 $speed = -1;
  574.                 if (!empty($options['avif_lossless'])) {
  575.                     $quality 100;
  576.                 } else {
  577.                     if (!isset($options['avif_quality'])) {
  578.                         if (isset($options['quality'])) {
  579.                             $options['avif_quality'] = $options['quality'];
  580.                         }
  581.                     }
  582.                     if (isset($options['avif_quality'])) {
  583.                         $quality max(0min(100$options['avif_quality']));
  584.                     }
  585.                 }
  586.                 $result[] = $quality;
  587.                 $result[] = $speed;
  588.                 break;
  589.             case Format::ID_BMP:
  590.                 if (isset($options['compressed'])) {
  591.                     $result[] = (bool) $options['compressed'];
  592.                 }
  593.                 break;
  594.             case Format::ID_JPEG:
  595.                 if (!isset($options['jpeg_quality'])) {
  596.                     if (isset($options['quality'])) {
  597.                         $options['jpeg_quality'] = $options['quality'];
  598.                     }
  599.                 }
  600.                 if (isset($options['jpeg_quality'])) {
  601.                     $result[] = $options['jpeg_quality'];
  602.                 }
  603.                 break;
  604.             case Format::ID_PNG:
  605.                 if (!isset($options['png_compression_level'])) {
  606.                     if (isset($options['quality'])) {
  607.                         $options['png_compression_level'] = round((100 $options['quality']) * 100);
  608.                     }
  609.                 }
  610.                 if (isset($options['png_compression_level'])) {
  611.                     if ($options['png_compression_level'] < || $options['png_compression_level'] > 9) {
  612.                         throw new InvalidArgumentException('png_compression_level option should be an integer from 0 to 9');
  613.                     }
  614.                     $result[] = $options['png_compression_level'];
  615.                 } else {
  616.                     $result[] = -1// use default level
  617.                 }
  618.                 if (!isset($options['png_compression_filter'])) {
  619.                     if (isset($options['filters'])) {
  620.                         $options['png_compression_filter'] = $options['filters'];
  621.                     }
  622.                 }
  623.                 if (isset($options['png_compression_filter'])) {
  624.                     if (~PNG_ALL_FILTERS $options['png_compression_filter']) {
  625.                         throw new InvalidArgumentException('png_compression_filter option should be a combination of the PNG_FILTER_XXX constants');
  626.                     }
  627.                     $result[] = $options['png_compression_filter'];
  628.                 }
  629.                 break;
  630.             case Format::ID_WEBP:
  631.                 if (!isset($options['webp_quality'])) {
  632.                     if (isset($options['quality'])) {
  633.                         $options['webp_quality'] = $options['quality'];
  634.                     }
  635.                 }
  636.                 if (isset($options['webp_quality'])) {
  637.                     if ($options['webp_quality'] < || $options['webp_quality'] > 100) {
  638.                         throw new InvalidArgumentException('webp_quality option should be an integer from 0 to 100');
  639.                     }
  640.                     $result[] = $options['webp_quality'];
  641.                 }
  642.                 break;
  643.             case Format::ID_XBM:
  644.             case Format::ID_WBMP:
  645.                 if (isset($options['foreground'])) {
  646.                     $result[] = $options['foreground'];
  647.                 }
  648.                 break;
  649.         }
  650.         return $result;
  651.     }
  652.     /**
  653.      * Generates a GD image.
  654.      *
  655.      * @param \Imagine\Image\BoxInterface $size
  656.      * @param string $operation the operation initiating the creation
  657.      *
  658.      * @throws \Imagine\Exception\RuntimeException
  659.      *
  660.      * @return resource|\GdImage
  661.      */
  662.     private function createImage(BoxInterface $size$operation)
  663.     {
  664.         $resource imagecreatetruecolor($size->getWidth(), $size->getHeight());
  665.         if ($resource === false) {
  666.             throw new RuntimeException('Image ' $operation ' failed');
  667.         }
  668.         if (imagealphablending($resourcefalse) === false || imagesavealpha($resourcetrue) === false) {
  669.             throw new RuntimeException('Image ' $operation ' failed');
  670.         }
  671.         if (function_exists('imageantialias')) {
  672.             imageantialias($resourcetrue);
  673.         }
  674.         $transparent imagecolorallocatealpha($resource255255255127);
  675.         imagefill($resource00$transparent);
  676.         imagecolortransparent($resource$transparent);
  677.         return $resource;
  678.     }
  679.     /**
  680.      * Generates a GD color from Color instance.
  681.      *
  682.      * @param \Imagine\Image\Palette\Color\ColorInterface $color
  683.      *
  684.      * @throws \Imagine\Exception\RuntimeException
  685.      * @throws \Imagine\Exception\InvalidArgumentException
  686.      *
  687.      * @return int A color identifier
  688.      */
  689.     private function getColor(ColorInterface $color)
  690.     {
  691.         if (!$color instanceof RGBColor) {
  692.             throw new InvalidArgumentException('GD driver only supports RGB colors');
  693.         }
  694.         $index imagecolorallocatealpha($this->resource$color->getRed(), $color->getGreen(), $color->getBlue(), round(127 * (100 $color->getAlpha()) / 100));
  695.         if ($index === false) {
  696.             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()));
  697.         }
  698.         return $index;
  699.     }
  700. }