diff --git a/core/core.services.yml b/core/core.services.yml index b1fbd4f..3c63d42 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -603,13 +603,9 @@ services: image.toolkit.manager: class: Drupal\Core\ImageToolkit\ImageToolkitManager arguments: ['@container.namespaces', '@cache.cache', '@language_manager', '@config.factory'] - image.toolkit: - class: Drupal\Core\ImageToolkit\ImageToolkitInterface - factory_method: getDefaultToolkit - factory_service: image.toolkit.manager image.factory: class: Drupal\Core\Image\ImageFactory - arguments: ['@image.toolkit'] + arguments: ['@image.toolkit.manager'] breadcrumb: class: Drupal\Core\Breadcrumb\BreadcrumbManager arguments: ['@module_handler'] diff --git a/core/lib/Drupal/Core/Image/Image.php b/core/lib/Drupal/Core/Image/Image.php index 136c81e..6d4da44 100644 --- a/core/lib/Drupal/Core/Image/Image.php +++ b/core/lib/Drupal/Core/Image/Image.php @@ -34,13 +34,6 @@ class Image implements ImageInterface { protected $toolkit; /** - * An image file handle. - * - * @var resource - */ - protected $resource; - - /** * Height, in pixels. * * @var int @@ -112,6 +105,16 @@ public function isSupported() { /** * {@inheritdoc} */ + public function isExisting() { + if (!$this->processed) { + $this->processInfo(); + } + return $this->processed; + } + + /** + * {@inheritdoc} + */ public function getExtension() { $this->processInfo(); return $this->extension; @@ -176,32 +179,6 @@ public function getMimeType() { /** * {@inheritdoc} */ - public function setResource($resource) { - $this->resource = $resource; - return $this; - } - - /** - * {@inheritdoc} - */ - public function hasResource() { - return (bool) $this->resource; - } - - /** - * {@inheritdoc} - */ - public function getResource() { - if (!$this->hasResource()) { - $this->processInfo(); - $this->toolkit->load($this); - } - return $this->resource; - } - - /** - * {@inheritdoc} - */ public function setSource($source) { $this->source = $source; return $this; @@ -224,6 +201,14 @@ public function getToolkitId() { /** * {@inheritdoc} */ + public function getToolkit() { + $this->processInfo(); + return $this->toolkit; + } + + /** + * {@inheritdoc} + */ public function save($destination = NULL) { if (empty($destination)) { $destination = $this->getSource(); diff --git a/core/lib/Drupal/Core/Image/ImageFactory.php b/core/lib/Drupal/Core/Image/ImageFactory.php index 27773d8..09ac0d2 100644 --- a/core/lib/Drupal/Core/Image/ImageFactory.php +++ b/core/lib/Drupal/Core/Image/ImageFactory.php @@ -8,6 +8,7 @@ namespace Drupal\Core\Image; use Drupal\Core\ImageToolkit\ImageToolkitInterface; +use Drupal\Core\ImageToolkit\ImageToolkitManager; /** * Provides a factory for image objects. @@ -15,6 +16,13 @@ class ImageFactory { /** + * The image toolkit plugin manager. + * + * @var \Drupal\Core\ImageToolkit\ImageToolkitManager + */ + protected $toolkitManager; + + /** * The image toolkit to use for this factory. * * @var \Drupal\Core\ImageToolkit\ImageToolkitInterface @@ -24,11 +32,11 @@ class ImageFactory { /** * Constructs a new ImageFactory object. * - * @param \Drupal\Core\ImageToolkit\ImageToolkitInterface $toolkit - * The image toolkit to use for this image factory. + * @param \Drupal\Core\ImageToolkit\ImageToolkitManager $toolkit_manager + * The image toolkit plugin manager. */ - public function __construct(ImageToolkitInterface $toolkit) { - $this->toolkit = $toolkit; + public function __construct(ImageToolkitManager $toolkit_manager) { + $this->toolkitManager = $toolkit_manager; } /** @@ -55,7 +63,10 @@ public function setToolkit(ImageToolkitInterface $toolkit) { * The new Image object. */ public function get($source) { - return new Image($source, $this->toolkit); + if (!$this->toolkit) { + $this->toolkit = $this->toolkitManager->getDefaultToolkit(); + } + return new Image($source, $this->toolkitManager->createInstance($this->toolkit->getPluginId())); } } diff --git a/core/lib/Drupal/Core/Image/ImageInterface.php b/core/lib/Drupal/Core/Image/ImageInterface.php index 411d39a..ab07113 100644 --- a/core/lib/Drupal/Core/Image/ImageInterface.php +++ b/core/lib/Drupal/Core/Image/ImageInterface.php @@ -21,6 +21,14 @@ public function isSupported(); /** + * Checks if the image is existing. + * + * @return bool + * TRUE if the image exists, FALSE if it is invalid. + */ + public function isExisting(); + + /** * Returns the extension of the image file. * * @return string @@ -90,33 +98,6 @@ public function getType(); public function getMimeType(); /** - * Sets the image file resource. - * - * @param resource $resource - * The image file handle. - * - * @return self - * Returns this image file. - */ - public function setResource($resource); - - /** - * Determines if this image file has a resource set. - * - * @return bool - * TRUE if this image file has a resource set, FALSE otherwise. - */ - public function hasResource(); - - /** - * Retrieves the image file resource. - * - * @return resource - * The image file handle. - */ - public function getResource(); - - /** * Sets the source path of the image file. * * @param string $source @@ -136,6 +117,14 @@ public function setSource($source); public function getSource(); /** + * Returns the image toolkit used for this image file. + * + * @return string + * The image toolkit. + */ + public function getToolkit(); + + /** * Returns the ID of the image toolkit used for this image file. * * @return string diff --git a/core/lib/Drupal/Core/ImageToolkit/ImageToolkitInterface.php b/core/lib/Drupal/Core/ImageToolkit/ImageToolkitInterface.php index a5f96f3..8e5aa45 100644 --- a/core/lib/Drupal/Core/ImageToolkit/ImageToolkitInterface.php +++ b/core/lib/Drupal/Core/ImageToolkit/ImageToolkitInterface.php @@ -134,13 +134,15 @@ public function desaturate(ImageInterface $image); /** * Creates an image resource from a file. * - * @param \Drupal\Core\Image\ImageInterface $image - * An image object. The $image->resource value will populated by this call. + * @param string $source + * String specifying the path of the image file. + * @param string $details + * An array of image details. * * @return bool * TRUE or FALSE, based on success. */ - public function load(ImageInterface $image); + public function load($source, array $details); /** * Writes an image resource to a destination file. diff --git a/core/modules/file/file.module b/core/modules/file/file.module index f4e0f62..c76a27b 100644 --- a/core/modules/file/file.module +++ b/core/modules/file/file.module @@ -406,7 +406,7 @@ function file_validate_is_image(File $file) { $image = \Drupal::service('image.factory')->get($file->getFileUri()); if (!$image->isSupported()) { - $toolkit = \Drupal::service('image.toolkit'); + $toolkit = \Drupal::service('image.toolkit.manager')->getDefaultToolkit(); $extensions = array(); foreach ($toolkit->supportedTypes() as $image_type) { $extensions[] = Unicode::strtoupper(image_type_to_extension($image_type)); @@ -453,7 +453,7 @@ function file_validate_image_resolution(File $file, $maximum_dimensions = 0, $mi if ($image->getWidth() > $width || $image->getHeight() > $height) { // Try to resize the image to fit the dimensions. $image = $image_factory->get($file->getFileUri()); - if ($image->getResource()) { + if ($image->isExisting()) { $image->scale($width, $height); $image->save(); $file->filesize = $image->getFileSize(); diff --git a/core/modules/file/lib/Drupal/file/Tests/ValidatorTest.php b/core/modules/file/lib/Drupal/file/Tests/ValidatorTest.php index 76abd8a..d6b4326 100644 --- a/core/modules/file/lib/Drupal/file/Tests/ValidatorTest.php +++ b/core/modules/file/lib/Drupal/file/Tests/ValidatorTest.php @@ -79,7 +79,7 @@ function testFileValidateImageResolution() { $this->assertEqual(count($errors), 1, 'Small images report an error.', 'File'); // Maximum size. - if ($this->container->has('image.toolkit')) { + if ($this->container->has('image.toolkit.manager')) { // Copy the image so that the original doesn't get resized. copy('core/misc/druplicon.png', 'temporary://druplicon.png'); $this->image->setFileUri('temporary://druplicon.png'); diff --git a/core/modules/image/lib/Drupal/image/Entity/ImageStyle.php b/core/modules/image/lib/Drupal/image/Entity/ImageStyle.php index a6b36fb..037cb3a 100644 --- a/core/modules/image/lib/Drupal/image/Entity/ImageStyle.php +++ b/core/modules/image/lib/Drupal/image/Entity/ImageStyle.php @@ -283,7 +283,7 @@ public function createDerivative($original_uri, $derivative_uri) { } $image = \Drupal::service('image.factory')->get($original_uri); - if (!$image->getResource()) { + if (!$image->isExisting()) { return FALSE; } diff --git a/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/GDToolkit.php b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/GDToolkit.php index e5c5528..96ca774 100644 --- a/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/GDToolkit.php +++ b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/GDToolkit.php @@ -22,6 +22,37 @@ class GDToolkit extends ImageToolkitBase { /** + * An image file handle. + * + * @var resource + */ + protected $resource; + + /** + * Sets the image file resource. + * + * @param resource $resource + * The image file handle. + * + * @return self + * Returns this image file. + */ + public function setResource($resource) { + $this->resource = $resource; + return $this; + } + + /** + * Retrieves the image file resource. + * + * @return resource + * The image file handle. + */ + public function getResource() { + return $this->resource; + } + + /** * {@inheritdoc} */ public function settingsForm() { @@ -55,16 +86,17 @@ public function resize(ImageInterface $image, $width, $height) { $width = (int) round($width); $height = (int) round($height); - $res = $this->createTmp($image, $width, $height); + $res = $this->createTmp($image->getType(), $width, $height); - if (!imagecopyresampled($res, $image->getResource(), 0, 0, 0, 0, $width, $height, $image->getWidth(), $image->getHeight())) { + if (!imagecopyresampled($res, $this->getResource(), 0, 0, 0, 0, $width, $height, $image->getWidth(), $image->getHeight())) { return FALSE; } - imagedestroy($image->getResource()); + imagedestroy($this->getResource()); // Update image object. + $this + ->setResource($res); $image - ->setResource($res) ->setWidth($width) ->setHeight($height); return TRUE; @@ -86,39 +118,39 @@ public function rotate(ImageInterface $image, $degrees, $background = NULL) { for ($i = 16; $i >= 0; $i -= 8) { $rgb[] = (($background >> $i) & 0xFF); } - $background = imagecolorallocatealpha($image->getResource(), $rgb[0], $rgb[1], $rgb[2], 0); + $background = imagecolorallocatealpha($this->getResource(), $rgb[0], $rgb[1], $rgb[2], 0); } // Set the background color as transparent if $background is NULL. else { // Get the current transparent color. - $background = imagecolortransparent($image->getResource()); + $background = imagecolortransparent($this->getResource()); // If no transparent colors, use white. if ($background == 0) { - $background = imagecolorallocatealpha($image->getResource(), 255, 255, 255, 0); + $background = imagecolorallocatealpha($this->getResource(), 255, 255, 255, 0); } } // Images are assigned a new color palette when rotating, removing any // transparency flags. For GIF images, keep a record of the transparent color. if ($image->getType() == IMAGETYPE_GIF) { - $transparent_index = imagecolortransparent($image->getResource()); + $transparent_index = imagecolortransparent($this->getResource()); if ($transparent_index != 0) { - $transparent_gif_color = imagecolorsforindex($image->getResource(), $transparent_index); + $transparent_gif_color = imagecolorsforindex($this->getResource(), $transparent_index); } } - $image->setResource(imagerotate($image->getResource(), 360 - $degrees, $background)); + $this->setResource(imagerotate($this->getResource(), 360 - $degrees, $background)); // GIFs need to reassign the transparent color after performing the rotate. if (isset($transparent_gif_color)) { - $background = imagecolorexactalpha($image->getResource(), $transparent_gif_color['red'], $transparent_gif_color['green'], $transparent_gif_color['blue'], $transparent_gif_color['alpha']); - imagecolortransparent($image->getResource(), $background); + $background = imagecolorexactalpha($this->getResource(), $transparent_gif_color['red'], $transparent_gif_color['green'], $transparent_gif_color['blue'], $transparent_gif_color['alpha']); + imagecolortransparent($this->getResource(), $background); } $image - ->setWidth(imagesx($image->getResource())) - ->setHeight(imagesy($image->getResource())); + ->setWidth(imagesx($this->getResource())) + ->setHeight(imagesy($this->getResource())); return TRUE; } @@ -134,16 +166,17 @@ public function crop(ImageInterface $image, $x, $y, $width, $height) { $width = (int) round($width); $height = (int) round($height); - $res = $this->createTmp($image, $width, $height); + $res = $this->createTmp($image->getType(), $width, $height); - if (!imagecopyresampled($res, $image->getResource(), 0, 0, $x, $y, $width, $height, $width, $height)) { + if (!imagecopyresampled($res, $this->getResource(), 0, 0, $x, $y, $width, $height, $width, $height)) { return FALSE; } // Destroy the original image and return the modified image. - imagedestroy($image->getResource()); + imagedestroy($this->getResource()); + $this + ->setResource($res); $image - ->setResource($res) ->setWidth($width) ->setHeight($height); return TRUE; @@ -159,7 +192,7 @@ public function desaturate(ImageInterface $image) { return FALSE; } - return imagefilter($image->getResource(), IMG_FILTER_GRAYSCALE); + return imagefilter($this->getResource(), IMG_FILTER_GRAYSCALE); } /** @@ -201,19 +234,19 @@ public function scaleAndCrop(ImageInterface $image, $width, $height) { /** * {@inheritdoc} */ - public function load(ImageInterface $image) { - $function = 'imagecreatefrom' . image_type_to_extension($image->getType(), FALSE); - if (function_exists($function) && $resource = $function($image->getSource())) { - $image->setResource($resource); + public function load($source, array $details) { + $function = 'imagecreatefrom' . image_type_to_extension($details['type'], FALSE); + if (function_exists($function) && $resource = $function($source)) { + $this->resource = $resource; if (!imageistruecolor($resource)) { // Convert indexed images to true color, so that filters work // correctly and don't result in unnecessary dither. - $new_image = $this->createTmp($image, $image->getWidth(), $image->getHeight()); - imagecopy($new_image, $resource, 0, 0, 0, 0, $image->getWidth(), $image->getHeight()); + $new_image = $this->createTmp($details['type'], $details['width'], $details['height']); + imagecopy($new_image, $resource, 0, 0, 0, 0, $details['width'], $details['height']); imagedestroy($resource); - $image->setResource($new_image); + $this->resource = $new_image; } - return (bool) $image->getResource(); + return (bool) $this->resource; } return FALSE; @@ -241,15 +274,15 @@ public function save(ImageInterface $image, $destination) { return FALSE; } if ($image->getType() == IMAGETYPE_JPEG) { - $success = $function($image->getResource(), $destination, \Drupal::config('system.image.gd')->get('jpeg_quality')); + $success = $function($this->getResource(), $destination, \Drupal::config('system.image.gd')->get('jpeg_quality')); } else { // Always save PNG images with full transparency. if ($image->getType() == IMAGETYPE_PNG) { - imagealphablending($image->getResource(), FALSE); - imagesavealpha($image->getResource(), TRUE); + imagealphablending($this->getResource(), FALSE); + imagesavealpha($this->getResource(), TRUE); } - $success = $function($image->getResource(), $destination); + $success = $function($this->getResource(), $destination); } // Move temporary local file to remote destination. if (isset($permanent_destination) && $success) { @@ -274,14 +307,18 @@ public function getInfo(ImageInterface $image) { ); } + if ($details) { + $this->load($image->getSource(), $details); + } return $details; } /** * Creates a truecolor image preserving transparency from a provided image. * - * @param \Drupal\Core\Image\ImageInterface $image - * An image object. + * @param string $type + * An image type represented by a PHP IMAGETYPE_* constant (e.g. + * IMAGETYPE_JPEG, IMAGETYPE_PNG, etc.). * @param int $width * The new width of the new image, in pixels. * @param int $height @@ -290,16 +327,16 @@ public function getInfo(ImageInterface $image) { * @return resource * A GD image handle. */ - public function createTmp(ImageInterface $image, $width, $height) { + public function createTmp($type, $width, $height) { $res = imagecreatetruecolor($width, $height); - if ($image->getType() == IMAGETYPE_GIF) { + if ($type == IMAGETYPE_GIF) { // Grab transparent color index from image resource. - $transparent = imagecolortransparent($image->getResource()); + $transparent = imagecolortransparent($this->getResource()); if ($transparent >= 0) { // The original must have a transparent color, allocate to the new image. - $transparent_color = imagecolorsforindex($image->getResource(), $transparent); + $transparent_color = imagecolorsforindex($this->getResource(), $transparent); $transparent = imagecolorallocate($res, $transparent_color['red'], $transparent_color['green'], $transparent_color['blue']); // Flood with our new transparent color. @@ -307,7 +344,7 @@ public function createTmp(ImageInterface $image, $width, $height) { imagecolortransparent($res, $transparent); } } - elseif ($image->getType() == IMAGETYPE_PNG) { + elseif ($type == IMAGETYPE_PNG) { imagealphablending($res, FALSE); $transparency = imagecolorallocatealpha($res, 0, 0, 0, 127); imagefill($res, 0, 0, $transparency); diff --git a/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitGdTest.php b/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitGdTest.php index 805ee22..d683b70 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitGdTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitGdTest.php @@ -75,14 +75,15 @@ function colorsAreEqual($color_a, $color_b) { * Function for finding a pixel's RGBa values. */ function getPixelColor(ImageInterface $image, $x, $y) { - $color_index = imagecolorat($image->getResource(), $x, $y); + $toolkit = $image->getToolkit(); + $color_index = imagecolorat($toolkit->getResource(), $x, $y); - $transparent_index = imagecolortransparent($image->getResource()); + $transparent_index = imagecolortransparent($toolkit->getResource()); if ($color_index == $transparent_index) { return array(0, 0, 0, 127); } - return array_values(imagecolorsforindex($image->getResource(), $color_index)); + return array_values(imagecolorsforindex($toolkit->getResource(), $color_index)); } /** @@ -210,19 +211,19 @@ function testManipulations() { ); } - $toolkit = $this->container->get('image.toolkit.manager')->createInstance('gd'); - $image_factory = $this->container->get('image.factory')->setToolkit($toolkit); + $image_factory = $this->container->get('image.factory')->setToolkitId('gd'); foreach ($files as $file) { foreach ($operations as $op => $values) { // Load up a fresh image. $image = $image_factory->get(drupal_get_path('module', 'simpletest') . '/files/' . $file); + $toolkit = $image->getToolkit(); if (!$image) { $this->fail(String::format('Could not load image %file.', array('%file' => $file))); continue 2; } // All images should be converted to truecolor when loaded. - $image_truecolor = imageistruecolor($image->getResource()); + $image_truecolor = imageistruecolor($toolkit->getResource()); $this->assertTrue($image_truecolor, String::format('Image %file after load is a truecolor image.', array('%file' => $file))); if ($image->getType() == IMAGETYPE_GIF) { @@ -241,7 +242,7 @@ function testManipulations() { $correct_dimensions_object = TRUE; // Check the real dimensions of the image first. - if (imagesy($image->getResource()) != $values['height'] || imagesx($image->getResource()) != $values['width']) { + if (imagesy($toolkit->getResource()) != $values['height'] || imagesx($toolkit->getResource()) != $values['width']) { $correct_dimensions_real = FALSE; } diff --git a/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitTestBase.php index ce328aa..5f9d3ca 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitTestBase.php +++ b/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitTestBase.php @@ -71,7 +71,7 @@ protected function getImage() { $image = $this->container->get('image.factory') ->setToolkit($this->toolkit) ->get($this->file); - $image->getResource(); + $image->isExisting(); return $image; } diff --git a/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/Plugin/ImageToolkit/TestToolkit.php b/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/Plugin/ImageToolkit/TestToolkit.php index 5d62a82..7897e47 100644 --- a/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/Plugin/ImageToolkit/TestToolkit.php +++ b/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/Plugin/ImageToolkit/TestToolkit.php @@ -63,15 +63,18 @@ public function getInfo(ImageInterface $image) { ); } + if ($details) { + $this->load($image->getSource(), $details); + } return $details; } /** * {@inheritdoc} */ - public function load(ImageInterface $image) { - $this->logCall('load', array($image)); - return $image; + public function load($source, array $details) { + $this->logCall('load', array($source, $details)); + return TRUE; } /** diff --git a/core/tests/Drupal/Tests/Core/Image/ImageTest.php b/core/tests/Drupal/Tests/Core/Image/ImageTest.php index 728af5c..3f1b7fb 100644 --- a/core/tests/Drupal/Tests/Core/Image/ImageTest.php +++ b/core/tests/Drupal/Tests/Core/Image/ImageTest.php @@ -138,26 +138,12 @@ public function testGetMimeType() { } /** - * Tests \Drupal\Core\Image\Image::setResource(). + * Tests \Drupal\Core\Image\Image::isExisting(). */ - public function testSetResource() { + public function testIsExisting() { + $this->assertTrue($this->image->isExisting()); $resource = fopen($this->image->getSource(), 'r'); - $this->image->setResource($resource); - $this->assertEquals($this->image->getResource(), $resource); - - // Force \Drupal\Core\Image\Image::hasResource() to return FALSE. - $this->image->setResource(FALSE); - $this->assertNotNull($this->image->getResource()); - } - - /** - * Tests \Drupal\Core\Image\Image::hasResource(). - */ - public function testHasResource() { - $this->assertFalse($this->image->hasResource()); - $resource = fopen($this->image->getSource(), 'r'); - $this->image->setResource($resource); - $this->assertTrue($this->image->hasResource()); + $this->assertTrue(!empty($resource)); } /**