diff --git a/core/lib/Drupal/Core/Image/Image.php b/core/lib/Drupal/Core/Image/Image.php
index d1edb6e..e079293 100644
--- a/core/lib/Drupal/Core/Image/Image.php
+++ b/core/lib/Drupal/Core/Image/Image.php
@@ -20,11 +20,11 @@
 class Image implements ImageInterface {
 
   /**
-   * String specifying the path of the image file.
+   * Path of the image file.
    *
    * @var string
    */
-  protected $source;
+  protected $source = '';
 
   /**
    * An image toolkit object.
@@ -34,25 +34,18 @@ class Image implements ImageInterface {
   protected $toolkit;
 
   /**
-   * Image type represented by a PHP IMAGETYPE_* constant (e.g. IMAGETYPE_JPEG).
-   *
-   * @var int
-   */
-  protected $type;
-
-  /**
    * File size in bytes.
    *
    * @var int
    */
-  protected $fileSize = 0;
+  protected $fileSize;
 
   /**
-   * If this image file has been processed.
+   * If this image object is valid.
    *
    * @var bool
    */
-  protected $processed = FALSE;
+  protected $valid = FALSE;
 
   /**
    * Constructs a new Image object.
@@ -66,72 +59,43 @@ class Image implements ImageInterface {
   public function __construct(ImageToolkitInterface $toolkit, $source = NULL) {
     $this->toolkit = $toolkit;
     if ($source) {
-      $this->source = $source;
-      $this->processInfo();
+      $this->parseFile($source);
     }
   }
 
   /**
    * {@inheritdoc}
    */
-  public function isSupported() {
-    return in_array($this->getType(), $this->toolkit->supportedTypes());
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function isExisting() {
-    $this->processInfo();
-    return $this->processed;
+  public function isValid() {
+    return $this->valid;
   }
 
   /**
    * {@inheritdoc}
    */
   public function getHeight() {
-    $this->processInfo();
-    return $this->toolkit->getHeight($this);
+    return $this->toolkit->getHeight();
   }
 
   /**
    * {@inheritdoc}
    */
   public function getWidth() {
-    $this->processInfo();
-    return $this->toolkit->getWidth($this);
+    return $this->toolkit->getWidth();
   }
 
   /**
    * {@inheritdoc}
    */
   public function getFileSize() {
-    $this->processInfo();
     return $this->fileSize;
   }
 
   /**
    * {@inheritdoc}
    */
-  public function getType() {
-    $this->processInfo();
-    return $this->type;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   public function getMimeType() {
-    $this->processInfo();
-    return $this->type ? image_type_to_mime_type($this->type) : '';
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setSource($source) {
-    $this->source = $source;
-    return $this;
+    return $this->toolkit->getMimeType();
   }
 
   /**
@@ -152,7 +116,6 @@ public function getToolkitId() {
    * {@inheritdoc}
    */
   public function getToolkit() {
-    $this->processInfo();
     return $this->toolkit;
   }
 
@@ -160,14 +123,17 @@ public function getToolkit() {
    * {@inheritdoc}
    */
   public function save($destination = NULL) {
-    if (empty($destination)) {
-      $destination = $this->getSource();
+    // Return immediately if the image is not valid.
+    if (!$this->isValid()) {
+      return FALSE;
     }
-    if ($return = $this->toolkit->save($this, $destination)) {
+
+    $destination = $destination ?: $this->getSource();
+    if ($return = $this->toolkit->save($destination)) {
       // Clear the cached file size and refresh the image information.
       clearstatcache(TRUE, $destination);
-      $this->setSource($destination);
-      $this->processInfo();
+      $this->fileSize = filesize($destination);
+      $this->source = $destination;
 
       // @todo Use File utility when https://drupal.org/node/2050759 is in.
       if ($this->chmod($destination)) {
@@ -178,33 +144,34 @@ public function save($destination = NULL) {
   }
 
   /**
-   * Prepares the image information.
+   * Determines if a file contains a valid image.
    *
    * Drupal supports GIF, JPG and PNG file formats when used with the GD
    * toolkit, and may support others, depending on which toolkits are
    * installed.
    *
+   * @param string $source
+   *   The path to an image file.
+   *
    * @return bool
    *   FALSE, if the file could not be found or is not an image. Otherwise, the
    *   image information is populated.
    */
-  protected function processInfo() {
-    if ($this->processed) {
-      return TRUE;
-    }
-
-    $destination = $this->getSource();
-    if (!is_file($destination) && !is_uploaded_file($destination)) {
+  protected function parseFile($source) {
+    // Check that file is valid.
+    if (!is_file($source) && !is_uploaded_file($source)) {
       return FALSE;
     }
 
-    if ($details = $this->toolkit->getInfo($this)) {
-      $this->type = $details['type'];
-      $this->fileSize = filesize($destination);
-
-      $this->processed = TRUE;
+    // Defer file parsing to toolkit.
+    if ($this->toolkit->parseFile($source)) {
+      $this->fileSize = filesize($source);
+      $this->source = $source;
+      $this->valid = TRUE;
+      return TRUE;
     }
-    return TRUE;
+
+    return FALSE;
   }
 
   /**
@@ -227,22 +194,22 @@ protected function processInfo() {
    */
   public function __call($method, $arguments) {
     // @todo Temporary to avoid that legacy GD setResource(), getResource(),
-    //  hasResource() methods moved to GD toolkit in #2103621, and setWidth(),
-    //  setHeight() methods moved to ImageToolkitInterface in #2196067 get
+    //  hasResource() methods moved to GD toolkit in #2103621, setWidth(),
+    //  setHeight() methods moved to ImageToolkitInterface in #2196067,
+    //  getType() method moved to GDToolkit in #2211227 get
     //  invoked from this class anyway through the magic __call. Will be
-    //  removed through https://drupal.org/node/2110499, when
+    //  removed through https://drupal.org/node/2073759, when
     //  call_user_func_array() will be replaced by
     //  $this->toolkit->apply($name, $this, $arguments).
-    if (in_array($method, array('setResource', 'getResource', 'hasResource', 'setWidth', 'setHeight'))) {
-      throw new \BadMethodCallException();
+    if (in_array($method, array('setResource', 'getResource', 'hasResource', 'setWidth', 'setHeight', 'getType'))) {
+      throw new \BadMethodCallException($method);
     }
     if (is_callable(array($this->toolkit, $method))) {
-      // @todo In https://drupal.org/node/2110499, call_user_func_array() will
+      // @todo In https://drupal.org/node/2073759, call_user_func_array() will
       //   be replaced by $this->toolkit->apply($name, $this, $arguments).
-      array_unshift($arguments, $this);
       return call_user_func_array(array($this->toolkit, $method), $arguments);
     }
-    throw new \BadMethodCallException();
+    throw new \BadMethodCallException($method);
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Image/ImageFactory.php b/core/lib/Drupal/Core/Image/ImageFactory.php
index c7adf4c..620cae2 100644
--- a/core/lib/Drupal/Core/Image/ImageFactory.php
+++ b/core/lib/Drupal/Core/Image/ImageFactory.php
@@ -88,6 +88,28 @@ public function getToolkitId() {
    */
   public function get($source = NULL, $toolkit_id = NULL) {
     $toolkit_id = $toolkit_id ?: $this->toolkitId;
-    return new Image($this->toolkitManager->createInstance($toolkit_id), $source);
+    $toolkit = $this->toolkitManager->createInstance($toolkit_id);
+    $image = new Image($toolkit, $source);
+    $toolkit->setImage($image);
+    return $image;
   }
+
+  /**
+   * Returns the image file extensions supported by the toolkit.
+   *
+   * @param string|null $toolkit_id
+   *   (optional) The ID of the image toolkit to use for checking, or NULL
+   *   to use the current toolkit.
+   *
+   * @return array
+   *   An array of supported image file extensions (e.g. png/jpeg/gif).
+   *
+   * @see \Drupal\Core\ImageToolkit\ImageToolkitInterface::getSupportedExtensions()
+   */
+  public function getSupportedExtensions($toolkit_id = NULL) {
+    $toolkit_id = $toolkit_id ?: $this->toolkitId;
+    $definition = $this->toolkitManager->getDefinition($toolkit_id);
+    return call_user_func($definition['class'] . '::getSupportedExtensions');
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Image/ImageInterface.php b/core/lib/Drupal/Core/Image/ImageInterface.php
index 7e0e1a5..efbd540 100644
--- a/core/lib/Drupal/Core/Image/ImageInterface.php
+++ b/core/lib/Drupal/Core/Image/ImageInterface.php
@@ -13,20 +13,12 @@
 interface ImageInterface {
 
   /**
-   * Checks if the image format is supported.
+   * Checks if the image is valid.
    *
    * @return bool
-   *   Returns TRUE if the image format is supported by the toolkit.
+   *   TRUE if the image object contains a valid image, FALSE otherwise.
    */
-  public function isSupported();
-
-  /**
-   * Checks if the image is existing.
-   *
-   * @return bool
-   *   TRUE if the image exists and is a valid image, FALSE otherwise.
-   */
-  public function isExisting();
+  public function isValid();
 
   /**
    * Returns the height of the image.
@@ -47,21 +39,12 @@ public function getWidth();
   /**
    * Returns the size of the image file.
    *
-   * @return int
-   *   The size of the file in bytes, or 0 if the file is invalid.
+   * @return int|null
+   *   The size of the file in bytes, or NULL if the image is invalid.
    */
   public function getFileSize();
 
   /**
-   * Returns the type of the image.
-   *
-   * @return int
-   *   The image type represented by a PHP IMAGETYPE_* constant (e.g.
-   *   IMAGETYPE_JPEG).
-   */
-  public function getType();
-
-  /**
    * Returns the MIME type of the image file.
    *
    * @return string
@@ -71,21 +54,11 @@ public function getType();
   public function getMimeType();
 
   /**
-   * Sets the source path of the image file.
-   *
-   * @param string $source
-   *   A string specifying the path of the image file.
-   *
-   * @return self
-   *   Returns this image file.
-   */
-  public function setSource($source);
-
-  /**
    * Retrieves the source path of the image file.
    *
    * @return string
-   *   The source path of the image file.
+   *   The source path of the image file. An empty string if the source is
+   *   not set or the image is invalid.
    */
   public function getSource();
 
diff --git a/core/lib/Drupal/Core/ImageToolkit/ImageToolkitBase.php b/core/lib/Drupal/Core/ImageToolkit/ImageToolkitBase.php
index 4f28a6e..988c85c 100644
--- a/core/lib/Drupal/Core/ImageToolkit/ImageToolkitBase.php
+++ b/core/lib/Drupal/Core/ImageToolkit/ImageToolkitBase.php
@@ -7,11 +7,29 @@
 
 namespace Drupal\Core\ImageToolkit;
 
+use Drupal\Core\Image\ImageInterface;
 use Drupal\Core\Plugin\PluginBase;
 
 abstract class ImageToolkitBase extends PluginBase implements ImageToolkitInterface {
 
   /**
+   * The parent Image object.
+   *
+   * @var \Drupal\Core\Image\ImageInterface
+   */
+  protected $image;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setImage(ImageInterface $image) {
+    if (!isset($this->image)) {
+      $this->image = $image;
+    }
+    return $this;
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function getRequirements() {
diff --git a/core/lib/Drupal/Core/ImageToolkit/ImageToolkitInterface.php b/core/lib/Drupal/Core/ImageToolkit/ImageToolkitInterface.php
index 177057b..d5fa925 100644
--- a/core/lib/Drupal/Core/ImageToolkit/ImageToolkitInterface.php
+++ b/core/lib/Drupal/Core/ImageToolkit/ImageToolkitInterface.php
@@ -62,8 +62,6 @@ public function settingsFormSubmit($form, &$form_state);
   /**
    * Scales an image to the specified size.
    *
-   * @param \Drupal\Core\Image\ImageInterface $image
-   *   An image object.
    * @param int $width
    *   The new width of the resized image, in pixels.
    * @param int $height
@@ -72,13 +70,11 @@ public function settingsFormSubmit($form, &$form_state);
    * @return bool
    *   TRUE or FALSE, based on success.
    */
-  public function resize(ImageInterface $image, $width, $height);
+  public function resize($width, $height);
 
   /**
    * Rotates an image the given number of degrees.
    *
-   * @param \Drupal\Core\Image\ImageInterface $image
-   *   An image object.
    * @param int $degrees
    *   The number of (clockwise) degrees to rotate the image.
    * @param string $background
@@ -91,13 +87,11 @@ public function resize(ImageInterface $image, $width, $height);
    * @return bool
    *   TRUE or FALSE, based on success.
    */
-  public function rotate(ImageInterface $image, $degrees, $background = NULL);
+  public function rotate($degrees, $background = NULL);
 
   /**
    * Crops an image.
    *
-   * @param \Drupal\Core\Image\ImageInterface $image
-   *   An image object.
    * @param int $x
    *   The starting x offset at which to start the crop, in pixels.
    * @param int $y
@@ -109,45 +103,35 @@ public function rotate(ImageInterface $image, $degrees, $background = NULL);
    *
    * @return bool
    *   TRUE or FALSE, based on success.
-   *
-   * @see image_crop()
    */
-  public function crop(ImageInterface $image, $x, $y, $width, $height);
+  public function crop($x, $y, $width, $height);
 
   /**
    * Converts an image resource to grayscale.
    *
    * Note that transparent GIFs loose transparency when desaturated.
    *
-   * @param \Drupal\Core\Image\ImageInterface $image
-   *   An image object. The $image->resource value will be modified by this
-   *   call.
-   *
    * @return bool
    *   TRUE or FALSE, based on success.
    */
-  public function desaturate(ImageInterface $image);
+  public function desaturate();
 
   /**
    * Writes an image resource to a destination file.
    *
-   * @param \Drupal\Core\Image\ImageInterface $image
-   *   An image object.
    * @param string $destination
    *   A string file URI or path where the image should be saved.
    *
    * @return bool
    *   TRUE or FALSE, based on success.
    */
-  public function save(ImageInterface $image, $destination);
+  public function save($destination);
 
   /**
    * Scales an image while maintaining aspect ratio.
    *
    * The resulting image can be smaller for one or both target dimensions.
    *
-   * @param \Drupal\Core\Image\ImageInterface $image
-   *   An image object.
    * @param int $width
    *   (optional) The target width, in pixels. This value is omitted then the
    *   scaling will based only on the height value.
@@ -161,7 +145,7 @@ public function save(ImageInterface $image, $destination);
    * @return bool
    *   TRUE on success, FALSE on failure.
    */
-  public function scale(ImageInterface $image, $width = NULL, $height = NULL, $upscale = FALSE);
+  public function scale($width = NULL, $height = NULL, $upscale = FALSE);
 
   /**
    * Scales an image to the exact width and height given.
@@ -172,8 +156,6 @@ public function scale(ImageInterface $image, $width = NULL, $height = NULL, $ups
    *
    * The resulting image always has the exact target dimensions.
    *
-   * @param \Drupal\Core\Image\ImageInterface $image
-   *   An image object.
    * @param int $width
    *   The target width, in pixels.
    * @param int $height
@@ -182,44 +164,55 @@ public function scale(ImageInterface $image, $width = NULL, $height = NULL, $ups
    * @return bool
    *   TRUE on success, FALSE on failure.
    */
-  public function scaleAndCrop(ImageInterface $image, $width, $height);
+  public function scaleAndCrop($width, $height);
 
   /**
-   * Gets details about an image.
+   * Injects the parent Image object in its toolkit instance.
+   *
+   * This allows toolkit code to access methods set on the Image object.
    *
    * @param \Drupal\Core\Image\ImageInterface $image
    *   An image object.
    *
-   * @return array
-   *   If the file could not be found or is not an image, an empty array;
-   *   otherwise, a keyed array containing information about the image:
-   *   - "type": Image type represented as an IMAGETYPE_* constant.
+   * @return this
+   */
+  public function setImage(ImageInterface $image);
+
+  /**
+   * Determines if a file contains a valid image.
    *
-   * @see \Drupal\Core\Image\ImageInterface::processInfo()
+   * @param string $source
+   *   A string file URI or path where the image should be found.
+   *
+   * @return bool
+   *   TRUE if the file could be found and is an image, FALSE otherwise.
    */
-  public function getInfo(ImageInterface $image);
+  public function parseFile($source);
 
   /**
    * Returns the height of the image.
    *
-   * @param \Drupal\Core\Image\ImageInterface $image
-   *   An image object.
-   *
    * @return int|null
    *   The height of the image, or NULL if the image is invalid.
    */
-  public function getHeight(ImageInterface $image);
+  public function getHeight();
 
   /**
    * Returns the width of the image.
    *
-   * @param \Drupal\Core\Image\ImageInterface $image
-   *   An image object.
-   *
    * @return int|null
    *   The width of the image, or NULL if the image is invalid.
    */
-  public function getWidth(ImageInterface $image);
+  public function getWidth();
+
+  /**
+   * Returns the MIME type of the image file.
+   *
+   * @return string
+   *   The MIME type of the image file, or an empty string if the image is
+   *   invalid.
+   */
+  public function getMimeType();
 
   /**
    * Gets toolkit requirements in a format suitable for hook_requirements().
@@ -244,12 +237,11 @@ public function getRequirements();
   public static function isAvailable();
 
   /**
-   * Returns a list of image types supported by the toolkit.
+   * Returns a list of image file extensions supported by the toolkit.
    *
    * @return array
-   *   An array of available image types. An image type is represented by a PHP
-   *   IMAGETYPE_* constant (e.g. IMAGETYPE_JPEG, IMAGETYPE_PNG, etc.).
+   *   An array of supported image file extensions (e.g. png/jpeg/gif).
    */
-  public static function supportedTypes();
+  public static function getSupportedExtensions();
 
 }
diff --git a/core/modules/file/file.module b/core/modules/file/file.module
index 9fb3c60..607618e 100644
--- a/core/modules/file/file.module
+++ b/core/modules/file/file.module
@@ -396,7 +396,7 @@ function file_validate_size(File $file, $file_limit = 0, $user_limit = 0) {
 }
 
 /**
- * Checks that the file is recognized by Image::getInfo() as an image.
+ * Checks that the file is recognized as a valid image.
  *
  * @param \Drupal\file\File $file
  *   A file entity.
@@ -409,13 +409,11 @@ function file_validate_size(File $file, $file_limit = 0, $user_limit = 0) {
 function file_validate_is_image(File $file) {
   $errors = array();
 
-  $image = \Drupal::service('image.factory')->get($file->getFileUri());
-  if (!$image->isSupported()) {
-    $extensions = array();
-    foreach ($image->getToolkit()->supportedTypes() as $image_type) {
-      $extensions[] = Unicode::strtoupper(image_type_to_extension($image_type));
-    }
-    $errors[] = t('Image type not supported. Allowed types: @types.', array('@types' => implode(', ', $extensions)));
+  $image_factory = \Drupal::service('image.factory');
+  $image = $image_factory->get($file->getFileUri());
+  if (!$image->isValid()) {
+    $supported_extensions = $image_factory->getSupportedExtensions();
+    $errors[] = t('Image type not supported. Allowed types: %types', array('%types' => implode(' ', $supported_extensions)));
   }
 
   return $errors;
@@ -450,21 +448,19 @@ function file_validate_image_resolution(File $file, $maximum_dimensions = 0, $mi
   // Check first that the file is an image.
   $image_factory = \Drupal::service('image.factory');
   $image = $image_factory->get($file->getFileUri());
-  if ($image->isSupported()) {
+  if ($image->isValid()) {
     if ($maximum_dimensions) {
       // Check that it is smaller than the given dimensions.
       list($width, $height) = explode('x', $maximum_dimensions);
       if ($image->getWidth() > $width || $image->getHeight() > $height) {
         // Try to resize the image to fit the dimensions.
-        $image = $image_factory->get($file->getFileUri());
-        if ($image->isExisting()) {
-          $image->scale($width, $height);
+        if ($image->scale($width, $height)) {
           $image->save();
           $file->filesize = $image->getFileSize();
           drupal_set_message(t('The image was resized to fit within the maximum allowed dimensions of %dimensions pixels.', array('%dimensions' => $maximum_dimensions)));
         }
         else {
-          $errors[] = t('The image is too large; the maximum dimensions are %dimensions pixels.', array('%dimensions' => $maximum_dimensions));
+          $errors[] = t('The image exceeds the maximum allowed dimensions and an attempt to resize it failed.');
         }
       }
     }
diff --git a/core/modules/file/lib/Drupal/file/Tests/ValidatorTest.php b/core/modules/file/lib/Drupal/file/Tests/ValidatorTest.php
index b7d9bf4..d7f3af3 100644
--- a/core/modules/file/lib/Drupal/file/Tests/ValidatorTest.php
+++ b/core/modules/file/lib/Drupal/file/Tests/ValidatorTest.php
@@ -91,6 +91,12 @@ function testFileValidateImageResolution() {
       $this->assertTrue($image->getWidth() <= 10, 'Image scaled to correct width.', 'File');
       $this->assertTrue($image->getHeight() <= 5, 'Image scaled to correct height.', 'File');
 
+      // Once again, now with negative width and height to force an error.
+      copy('core/misc/druplicon.png', 'temporary://druplicon.png');
+      $this->image->setFileUri('temporary://druplicon.png');
+      $errors = file_validate_image_resolution($this->image, '-10x-5');
+      $this->assertEqual(count($errors), 1, 'An error reported for an oversized image that can not be scaled down.', 'File');
+
       drupal_unlink('temporary://druplicon.png');
     }
     else {
diff --git a/core/modules/image/image.module b/core/modules/image/image.module
index 61b30b9..7f1ff65 100644
--- a/core/modules/image/image.module
+++ b/core/modules/image/image.module
@@ -187,7 +187,7 @@ function image_file_download($uri) {
 
     // Check that the file exists and is an image.
     $image = \Drupal::service('image.factory')->get($uri);
-    if ($image->isSupported()) {
+    if ($image->isValid()) {
       // Check the permissions of the original to grant access to this image.
       $headers = \Drupal::moduleHandler()->invokeAll('file_download', array($original_uri));
       // Confirm there's at least one module granting access and none denying access.
diff --git a/core/modules/image/lib/Drupal/image/Entity/ImageStyle.php b/core/modules/image/lib/Drupal/image/Entity/ImageStyle.php
index 4eaa2ec..985a501 100644
--- a/core/modules/image/lib/Drupal/image/Entity/ImageStyle.php
+++ b/core/modules/image/lib/Drupal/image/Entity/ImageStyle.php
@@ -282,7 +282,7 @@ public function createDerivative($original_uri, $derivative_uri) {
     }
 
     $image = \Drupal::service('image.factory')->get($original_uri);
-    if (!$image->isExisting()) {
+    if (!$image->isValid()) {
       return FALSE;
     }
 
diff --git a/core/modules/image/lib/Drupal/image/Plugin/Field/FieldType/ImageItem.php b/core/modules/image/lib/Drupal/image/Plugin/Field/FieldType/ImageItem.php
index 249933e..f94c51d 100644
--- a/core/modules/image/lib/Drupal/image/Plugin/Field/FieldType/ImageItem.php
+++ b/core/modules/image/lib/Drupal/image/Plugin/Field/FieldType/ImageItem.php
@@ -303,7 +303,7 @@ public function preSave() {
     // Determine the dimensions if necessary.
     if (empty($width) || empty($height)) {
       $image = \Drupal::service('image.factory')->get($this->entity->getFileUri());
-      if ($image->isSupported()) {
+      if ($image->isValid()) {
         $this->width = $image->getWidth();
         $this->height =$image->getHeight();
       }
diff --git a/core/modules/image/lib/Drupal/image/Plugin/Field/FieldWidget/ImageWidget.php b/core/modules/image/lib/Drupal/image/Plugin/Field/FieldWidget/ImageWidget.php
index 26d4dea..439a183 100644
--- a/core/modules/image/lib/Drupal/image/Plugin/Field/FieldWidget/ImageWidget.php
+++ b/core/modules/image/lib/Drupal/image/Plugin/Field/FieldWidget/ImageWidget.php
@@ -166,7 +166,7 @@ public static function process($element, &$form_state, $form) {
       }
       else {
         $image = \Drupal::service('image.factory')->get($file->getFileUri());
-        if ($image->isExisting()) {
+        if ($image->isValid()) {
           $variables['width'] = $image->getWidth();
           $variables['height'] = $image->getHeight();
         }
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 e067014..164abc1 100644
--- a/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/GDToolkit.php
+++ b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/GDToolkit.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\system\Plugin\ImageToolkit;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Image\ImageInterface;
 use Drupal\Core\ImageToolkit\ImageToolkitBase;
 use Drupal\Component\Utility\Image as ImageUtility;
@@ -29,6 +30,13 @@ class GDToolkit extends ImageToolkitBase {
   protected $resource;
 
   /**
+   * Image type represented by a PHP IMAGETYPE_* constant (e.g. IMAGETYPE_JPEG).
+   *
+   * @var int
+   */
+  protected $type;
+
+  /**
    * Sets the GD image resource.
    *
    * @param resource $resource
@@ -80,15 +88,19 @@ public function settingsFormSubmit($form, &$form_state) {
   /**
    * {@inheritdoc}
    */
-  public function resize(ImageInterface $image, $width, $height) {
+  public function resize($width, $height) {
     // @todo Dimensions computation will be moved into a dedicated functionality
     //   in https://drupal.org/node/2108307.
     $width = (int) round($width);
     $height = (int) round($height);
 
-    $res = $this->createTmp($image->getType(), $width, $height);
+    if ($width <= 0 || $height <= 0) {
+      return FALSE;
+    }
+
+    $res = $this->createTmp($this->getType(), $width, $height);
 
-    if (!imagecopyresampled($res, $this->getResource(), 0, 0, 0, 0, $width, $height, $this->getWidth($image), $this->getHeight($image))) {
+    if (!imagecopyresampled($res, $this->getResource(), 0, 0, 0, 0, $width, $height, $this->getWidth(), $this->getHeight())) {
       return FALSE;
     }
 
@@ -101,10 +113,10 @@ public function resize(ImageInterface $image, $width, $height) {
   /**
    * {@inheritdoc}
    */
-  public function rotate(ImageInterface $image, $degrees, $background = NULL) {
+  public function rotate($degrees, $background = NULL) {
     // PHP installations using non-bundled GD do not have imagerotate.
     if (!function_exists('imagerotate')) {
-      watchdog('image', 'The image %file could not be rotated because the imagerotate() function is not available in this PHP installation.', array('%file' => $image->getSource()));
+      watchdog('image', 'The image %file could not be rotated because the imagerotate() function is not available in this PHP installation.', array('%file' => $this->image->getSource()));
       return FALSE;
     }
 
@@ -129,7 +141,7 @@ public function rotate(ImageInterface $image, $degrees, $background = NULL) {
 
     // 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) {
+    if ($this->getType() == IMAGETYPE_GIF) {
       $transparent_index = imagecolortransparent($this->getResource());
       if ($transparent_index != 0) {
         $transparent_gif_color = imagecolorsforindex($this->getResource(), $transparent_index);
@@ -150,16 +162,20 @@ public function rotate(ImageInterface $image, $degrees, $background = NULL) {
   /**
    * {@inheritdoc}
    */
-  public function crop(ImageInterface $image, $x, $y, $width, $height) {
+  public function crop($x, $y, $width, $height) {
     // @todo Dimensions computation will be moved into a dedicated functionality
     //   in https://drupal.org/node/2108307.
-    $aspect = $this->getHeight($image) / $this->getWidth($image);
+    $aspect = $this->getHeight() / $this->getWidth();
     $height = empty($height) ? $width * $aspect : $height;
     $width = empty($width) ? $height / $aspect : $width;
     $width = (int) round($width);
     $height = (int) round($height);
 
-    $res = $this->createTmp($image->getType(), $width, $height);
+    if ($width <= 0 || $height <= 0) {
+      return FALSE;
+    }
+
+    $res = $this->createTmp($this->getType(), $width, $height);
 
     if (!imagecopyresampled($res, $this->getResource(), 0, 0, $x, $y, $width, $height, $width, $height)) {
       return FALSE;
@@ -174,10 +190,10 @@ public function crop(ImageInterface $image, $x, $y, $width, $height) {
   /**
    * {@inheritdoc}
    */
-  public function desaturate(ImageInterface $image) {
+  public function desaturate() {
     // PHP installations using non-bundled GD do not have imagefilter.
     if (!function_exists('imagefilter')) {
-      watchdog('image', 'The image %file could not be desaturated because the imagefilter() function is not available in this PHP installation.', array('%file' => $image->getSource()));
+      watchdog('image', 'The image %file could not be desaturated because the imagefilter() function is not available in this PHP installation.', array('%file' => $this->image->getSource()));
       return FALSE;
     }
 
@@ -187,12 +203,12 @@ public function desaturate(ImageInterface $image) {
   /**
    * {@inheritdoc}
    */
-  public function scale(ImageInterface $image, $width = NULL, $height = NULL, $upscale = FALSE) {
+  public function scale($width = NULL, $height = NULL, $upscale = FALSE) {
     // @todo Dimensions computation will be moved into a dedicated functionality
     //   in https://drupal.org/node/2108307.
     $dimensions = array(
-      'width' => $this->getWidth($image),
-      'height' => $this->getHeight($image),
+      'width' => $this->getWidth(),
+      'height' => $this->getHeight(),
     );
 
     // Scale the dimensions - if they don't change then just return success.
@@ -200,59 +216,56 @@ public function scale(ImageInterface $image, $width = NULL, $height = NULL, $ups
       return TRUE;
     }
 
-    return $this->resize($image, $dimensions['width'], $dimensions['height']);
+    return $this->resize($dimensions['width'], $dimensions['height']);
   }
 
   /**
    * {@inheritdoc}
    */
-  public function scaleAndCrop(ImageInterface $image, $width, $height) {
+  public function scaleAndCrop($width, $height) {
     // @todo Dimensions computation will be moved into a dedicated functionality
     //   in https://drupal.org/node/2108307.
-    $scale = max($width / $this->getWidth($image), $height / $this->getHeight($image));
-    $x = ($this->getWidth($image) * $scale - $width) / 2;
-    $y = ($this->getHeight($image) * $scale - $height) / 2;
+    $scale = max($width / $this->getWidth(), $height / $this->getHeight());
+    $x = ($this->getWidth() * $scale - $width) / 2;
+    $y = ($this->getHeight() * $scale - $height) / 2;
 
-    if ($this->resize($image, $this->getWidth($image) * $scale, $this->getHeight($image) * $scale)) {
-      return $this->crop($image, $x, $y, $width, $height);
+    if ($this->resize($this->getWidth() * $scale, $this->getHeight() * $scale)) {
+      return $this->crop($x, $y, $width, $height);
     }
 
     return FALSE;
   }
 
   /**
-   * Creates a resource from a file.
+   * Loads a GD resource from a file.
    *
    * @param string $source
    *   String specifying the path of the image file.
-   * @param array $details
-   *   An array of image details.
    *
    * @return bool
    *   TRUE or FALSE, based on success.
    */
-  protected function load($source, array $details) {
-    $function = 'imagecreatefrom' . image_type_to_extension($details['type'], FALSE);
+  protected function load($source) {
+    $function = 'imagecreatefrom' . image_type_to_extension($this->getType(), FALSE);
     if (function_exists($function) && $resource = $function($source)) {
       $this->setResource($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($details['type'], imagesx($resource), imagesy($resource));
+        $new_image = $this->createTmp($this->getType(), imagesx($resource), imagesy($resource));
         imagecopy($new_image, $resource, 0, 0, 0, 0, imagesx($resource), imagesy($resource));
         imagedestroy($resource);
         $this->setResource($new_image);
       }
       return (bool) $this->getResource();
     }
-
     return FALSE;
   }
 
   /**
    * {@inheritdoc}
    */
-  public function save(ImageInterface $image, $destination) {
+  public function save($destination) {
     $scheme = file_uri_scheme($destination);
     // Work around lack of stream wrapper support in imagejpeg() and imagepng().
     if ($scheme && file_stream_wrapper_valid_scheme($scheme)) {
@@ -266,16 +279,16 @@ public function save(ImageInterface $image, $destination) {
       $destination = drupal_realpath($destination);
     }
 
-    $function = 'image' . image_type_to_extension($image->getType(), FALSE);
+    $function = 'image' . image_type_to_extension($this->getType(), FALSE);
     if (!function_exists($function)) {
       return FALSE;
     }
-    if ($image->getType() == IMAGETYPE_JPEG) {
+    if ($this->getType() == IMAGETYPE_JPEG) {
       $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) {
+      if ($this->getType() == IMAGETYPE_PNG) {
         imagealphablending($this->getResource(), FALSE);
         imagesavealpha($this->getResource(), TRUE);
       }
@@ -291,15 +304,14 @@ public function save(ImageInterface $image, $destination) {
   /**
    * {@inheritdoc}
    */
-  public function getInfo(ImageInterface $image) {
-    $details = array();
-    $data = getimagesize($image->getSource());
-
-    if (isset($data) && is_array($data) && in_array($data[2], static::supportedTypes())) {
-      $details['type'] = $data[2];
-      $this->load($image->getSource(), $details);
+  public function parseFile($source) {
+    $data = getimagesize($source);
+    if ($data && is_array($data) && in_array($data[2], static::supportedTypes())) {
+      $this->setType($data[2]);
+      $this->load($source);
+      return (bool) $this->getResource();
     }
-    return $details;
+    return FALSE;
   }
 
   /**
@@ -350,18 +362,52 @@ public function createTmp($type, $width, $height) {
   /**
    * {@inheritdoc}
    */
-  public function getWidth(ImageInterface $image) {
+  public function getWidth() {
     return $this->getResource() ? imagesx($this->getResource()) : NULL;
   }
 
   /**
    * {@inheritdoc}
    */
-  public function getHeight(ImageInterface $image) {
+  public function getHeight() {
     return $this->getResource() ? imagesy($this->getResource()) : NULL;
   }
 
   /**
+   * Gets the PHP type of the image.
+   *
+   * @return int
+   *   The image type represented by a PHP IMAGETYPE_* constant (e.g.
+   *   IMAGETYPE_JPEG).
+   */
+  public function getType() {
+    return $this->type;
+  }
+
+  /**
+   * Sets the PHP type of the image.
+   *
+   * @param int $type
+   *   The image type represented by a PHP IMAGETYPE_* constant (e.g.
+   *   IMAGETYPE_JPEG).
+   *
+   * @return this
+   */
+  public function setType($type) {
+    if (in_array($type, static::supportedTypes())) {
+      $this->type = $type;
+    }
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getMimeType() {
+    return $this->getType() ? image_type_to_mime_type($this->getType()) : '';
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function getRequirements() {
@@ -393,7 +439,22 @@ public static function isAvailable() {
   /**
    * {@inheritdoc}
    */
-  public static function supportedTypes() {
+  public static function getSupportedExtensions() {
+    $extensions = array();
+    foreach (static::supportedTypes() as $image_type) {
+      $extensions[] = Unicode::strtolower(image_type_to_extension($image_type, FALSE));
+    }
+    return $extensions;
+  }
+
+  /**
+   * Returns a list of image types supported by the toolkit.
+   *
+   * @return array
+   *   An array of available image types. An image type is represented by a PHP
+   *   IMAGETYPE_* constant (e.g. IMAGETYPE_JPEG, IMAGETYPE_PNG, etc.).
+   */
+  protected static function supportedTypes() {
     return array(IMAGETYPE_PNG, IMAGETYPE_JPEG, IMAGETYPE_GIF);
   }
 }
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 4c9a668..c26a750 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitGdTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitGdTest.php
@@ -247,7 +247,7 @@ function testManipulations() {
         $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) {
+        if ($image->getToolkit()->getType() == IMAGETYPE_GIF) {
           if ($op == 'desaturate') {
             // Transparent GIFs and the imagefilter function don't work together.
             $values['corners'][3][3] = 0;
@@ -274,13 +274,13 @@ function testManipulations() {
 
         $directory = $this->public_files_directory .'/imagetest';
         file_prepare_directory($directory, FILE_CREATE_DIRECTORY);
-        $image->save($directory . '/' . $op . image_type_to_extension($image->getType()));
+        $image->save($directory . '/' . $op . image_type_to_extension($image->getToolkit()->getType()));
 
         $this->assertTrue($correct_dimensions_real, String::format('Image %file after %action action has proper dimensions.', array('%file' => $file, '%action' => $op)));
         $this->assertTrue($correct_dimensions_object, String::format('Image %file object after %action action is reporting the proper height and width values.', array('%file' => $file, '%action' => $op)));
 
         // JPEG colors will always be messed up due to compression.
-        if ($image->getType() != IMAGETYPE_JPEG) {
+        if ($image->getToolkit()->getType() != IMAGETYPE_JPEG) {
           // Now check each of the corners to ensure color correctness.
           foreach ($values['corners'] as $key => $corner) {
             // Get the location of the corner.
diff --git a/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitTest.php b/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitTest.php
index 34376ba..28907cf 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitTest.php
@@ -38,7 +38,7 @@ function testLoad() {
     $image = $this->getImage();
     $this->assertTrue(is_object($image), 'Returned an object.');
     $this->assertEqual($image->getToolkitId(), 'test', 'Image had toolkit set.');
-    $this->assertToolkitOperationsCalled(array('load', 'get_info'));
+    $this->assertToolkitOperationsCalled(array('parseFile'));
   }
 
   /**
@@ -58,8 +58,8 @@ function testResize() {
 
     // Check the parameters.
     $calls = $this->imageTestGetAllCalls();
-    $this->assertEqual($calls['resize'][0][1], 1, 'Width was passed correctly');
-    $this->assertEqual($calls['resize'][0][2], 2, 'Height was passed correctly');
+    $this->assertEqual($calls['resize'][0][0], 1, 'Width was passed correctly');
+    $this->assertEqual($calls['resize'][0][1], 2, 'Height was passed correctly');
   }
 
   /**
@@ -72,8 +72,8 @@ function testScale() {
 
     // Check the parameters.
     $calls = $this->imageTestGetAllCalls();
-    $this->assertEqual($calls['scale'][0][1], 10, 'Width was passed correctly');
-    $this->assertEqual($calls['scale'][0][2], 10, 'Height was based off aspect ratio and passed correctly');
+    $this->assertEqual($calls['scale'][0][0], 10, 'Width was passed correctly');
+    $this->assertEqual($calls['scale'][0][1], 10, 'Height was based off aspect ratio and passed correctly');
   }
 
   /**
@@ -86,8 +86,8 @@ function testScaleAndCrop() {
     // Check the parameters.
     $calls = $this->imageTestGetAllCalls();
 
-    $this->assertEqual($calls['scaleAndCrop'][0][1], 5, 'Width was computed and passed correctly');
-    $this->assertEqual($calls['scaleAndCrop'][0][2], 10, 'Height was computed and passed correctly');
+    $this->assertEqual($calls['scaleAndCrop'][0][0], 5, 'Width was computed and passed correctly');
+    $this->assertEqual($calls['scaleAndCrop'][0][1], 10, 'Height was computed and passed correctly');
   }
 
   /**
@@ -99,8 +99,8 @@ function testRotate() {
 
     // Check the parameters.
     $calls = $this->imageTestGetAllCalls();
-    $this->assertEqual($calls['rotate'][0][1], 90, 'Degrees were passed correctly');
-    $this->assertEqual($calls['rotate'][0][2], 1, 'Background color was passed correctly');
+    $this->assertEqual($calls['rotate'][0][0], 90, 'Degrees were passed correctly');
+    $this->assertEqual($calls['rotate'][0][1], 1, 'Background color was passed correctly');
   }
 
   /**
@@ -112,10 +112,10 @@ function testCrop() {
 
     // Check the parameters.
     $calls = $this->imageTestGetAllCalls();
-    $this->assertEqual($calls['crop'][0][1], 1, 'X was passed correctly');
-    $this->assertEqual($calls['crop'][0][2], 2, 'Y was passed correctly');
-    $this->assertEqual($calls['crop'][0][3], 3, 'Width was passed correctly');
-    $this->assertEqual($calls['crop'][0][4], 4, 'Height was passed correctly');
+    $this->assertEqual($calls['crop'][0][0], 1, 'X was passed correctly');
+    $this->assertEqual($calls['crop'][0][1], 2, 'Y was passed correctly');
+    $this->assertEqual($calls['crop'][0][2], 3, 'Width was passed correctly');
+    $this->assertEqual($calls['crop'][0][3], 4, 'Height was passed correctly');
   }
 
   /**
@@ -127,6 +127,6 @@ function testDesaturate() {
 
     // Check the parameters.
     $calls = $this->imageTestGetAllCalls();
-    $this->assertEqual(count($calls['desaturate'][0]), 1, 'Only the image was passed.');
+    $this->assertEqual(count($calls['desaturate'][0]), 0, 'No arguments were passed.');
   }
 }
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 846e6e5..c8b04e1 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitTestBase.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitTestBase.php
@@ -68,7 +68,7 @@ function setUp() {
    */
   protected function getImage() {
     $image = $this->imageFactory->get($this->file, 'test');
-    $this->assertTrue($image->isExisting(), 'Image was loaded.');
+    $this->assertTrue($image->isValid(), 'Image was loaded.');
     return $image;
   }
 
@@ -109,7 +109,7 @@ function assertToolkitOperationsCalled(array $expected) {
   function imageTestReset() {
     // Keep track of calls to these operations
     $results = array(
-      'load' => array(),
+      'parseFile' => array(),
       'save' => array(),
       'settings' => array(),
       'resize' => array(),
@@ -124,9 +124,9 @@ function imageTestReset() {
    * Gets an array of calls to the test toolkit.
    *
    * @return array
-   *   An array keyed by operation name ('load', 'save', 'settings', 'resize',
-   *   'rotate', 'crop', 'desaturate') with values being arrays of parameters
-   *   passed to each call.
+   *   An array keyed by operation name ('parseFile', 'save', 'settings',
+   *   'resize', 'rotate', 'crop', 'desaturate') with values being arrays of
+   *   parameters passed to each call.
    */
   function imageTestGetAllCalls() {
     return \Drupal::state()->get('image_test.results') ?: array();
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 11fa771..51fbe49 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
@@ -7,6 +7,7 @@
 
 namespace Drupal\image_test\Plugin\ImageToolkit;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Image\ImageInterface;
 use Drupal\Core\ImageToolkit\ImageToolkitBase;
 
@@ -21,6 +22,13 @@
 class TestToolkit extends ImageToolkitBase {
 
   /**
+   * Image type represented by a PHP IMAGETYPE_* constant (e.g. IMAGETYPE_JPEG).
+   *
+   * @var int
+   */
+  protected $type;
+
+  /**
    * The width of the image.
    *
    * @var int
@@ -62,42 +70,23 @@ public function settingsFormSubmit($form, &$form_state) {
   /**
    * {@inheritdoc}
    */
-  public function getInfo(ImageInterface $image) {
-    $this->logCall('get_info', array($image));
-
-    $details = array();
-    $data = getimagesize($image->getSource());
-
-    if (isset($data) && is_array($data) && in_array($data[2], static::supportedTypes())) {
-      $details['type'] = $data[2];
+  public function parseFile($source) {
+    $this->logCall('parseFile', array($source));
+    $data = getimagesize($source);
+    if ($data && is_array($data) && in_array($data[2], static::supportedTypes())) {
+      $this->setType($data[2]);
       $this->width = $data[0];
       $this->height = $data[1];
-      $this->load($image->getSource(), $details);
+      return TRUE;
     }
-    return $details;
-  }
-
-  /**
-   * Mimick loading the image from a file.
-   *
-   * @param string $source
-   *   String specifying the path of the image file.
-   * @param array $details
-   *   An array of image details.
-   *
-   * @return bool
-   *   TRUE or FALSE, based on success.
-   */
-  protected function load($source, array $details) {
-    $this->logCall('load', array($source, $details));
-    return TRUE;
+    return FALSE;
   }
 
   /**
    * {@inheritdoc}
    */
-  public function save(ImageInterface $image, $destination) {
-    $this->logCall('save', array($image, $destination));
+  public function save($destination) {
+    $this->logCall('save', array($destination));
     // Return false so that image_save() doesn't try to chmod the destination
     // file that we didn't bother to create.
     return FALSE;
@@ -106,48 +95,48 @@ public function save(ImageInterface $image, $destination) {
   /**
    * {@inheritdoc}
    */
-  public function crop(ImageInterface $image, $x, $y, $width, $height) {
-    $this->logCall('crop', array($image, $x, $y, $width, $height));
+  public function crop($x, $y, $width, $height) {
+    $this->logCall('crop', array($x, $y, $width, $height));
     return TRUE;
   }
 
   /**
    * {@inheritdoc}
    */
-  public function resize(ImageInterface $image, $width, $height) {
-    $this->logCall('resize', array($image, $width, $height));
+  public function resize($width, $height) {
+    $this->logCall('resize', array($width, $height));
     return TRUE;
   }
 
   /**
    * {@inheritdoc}
    */
-  public function rotate(ImageInterface $image, $degrees, $background = NULL) {
-    $this->logCall('rotate', array($image, $degrees, $background));
+  public function rotate($degrees, $background = NULL) {
+    $this->logCall('rotate', array($degrees, $background));
     return TRUE;
   }
 
   /**
    * {@inheritdoc}
    */
-  public function desaturate(ImageInterface $image) {
-    $this->logCall('desaturate', array($image));
+  public function desaturate() {
+    $this->logCall('desaturate', array());
     return TRUE;
   }
 
   /**
    * {@inheritdoc}
    */
-  public function scale(ImageInterface $image, $width = NULL, $height = NULL, $upscale = FALSE) {
-    $this->logCall('scale', array($image, $width, $height, $upscale));
+  public function scale($width = NULL, $height = NULL, $upscale = FALSE) {
+    $this->logCall('scale', array($width, $height, $upscale));
     return TRUE;
   }
 
   /**
    * {@inheritdoc}
    */
-  public function scaleAndCrop(ImageInterface $image, $width, $height) {
-    $this->logCall('scaleAndCrop', array($image, $width, $height));
+  public function scaleAndCrop($width, $height) {
+    $this->logCall('scaleAndCrop', array($width, $height));
     return TRUE;
   }
 
@@ -155,8 +144,8 @@ public function scaleAndCrop(ImageInterface $image, $width, $height) {
    * Stores the values passed to a toolkit call.
    *
    * @param string $op
-   *   One of the image toolkit operations: 'get_info', 'load', 'save',
-   *   'settings', 'resize', 'rotate', 'crop', 'desaturate'.
+   *   One of the image toolkit operations: 'parseFile', 'save', 'settings',
+   *   'resize', 'rotate', 'crop', 'desaturate'.
    * @param array $args
    *   Values passed to hook.
    *
@@ -172,18 +161,52 @@ protected function logCall($op, $args) {
   /**
    * {@inheritdoc}
    */
-  public function getWidth(ImageInterface $image) {
+  public function getWidth() {
     return $this->width;
   }
 
   /**
    * {@inheritdoc}
    */
-  public function getHeight(ImageInterface $image) {
+  public function getHeight() {
     return $this->height;
   }
 
   /**
+   * Returns the type of the image.
+   *
+   * @return int
+   *   The image type represented by a PHP IMAGETYPE_* constant (e.g.
+   *   IMAGETYPE_JPEG).
+   */
+  public function getType() {
+    return $this->type;
+  }
+
+  /**
+   * Sets the PHP type of the image.
+   *
+   * @param int $type
+   *   The image type represented by a PHP IMAGETYPE_* constant (e.g.
+   *   IMAGETYPE_JPEG).
+   *
+   * @return this
+   */
+  public function setType($type) {
+    if (in_array($type, static::supportedTypes())) {
+      $this->type = $type;
+    }
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getMimeType() {
+    return $this->getType() ? image_type_to_mime_type($this->getType()) : '';
+  }
+
+  /**
    * {@inheritdoc}
    */
   public static function isAvailable() {
@@ -193,7 +216,22 @@ public static function isAvailable() {
   /**
    * {@inheritdoc}
    */
-  public static function supportedTypes() {
+  public static function getSupportedExtensions() {
+    $extensions = array();
+    foreach (static::supportedTypes() as $image_type) {
+      $extensions[] = Unicode::strtolower(image_type_to_extension($image_type, FALSE));
+    }
+    return $extensions;
+  }
+
+  /**
+   * Returns a list of image types supported by the toolkit.
+   *
+   * @return array
+   *   An array of available image types. An image type is represented by a PHP
+   *   IMAGETYPE_* constant (e.g. IMAGETYPE_JPEG, IMAGETYPE_PNG, etc.).
+   */
+  protected static function supportedTypes() {
     return array(IMAGETYPE_PNG, IMAGETYPE_JPEG, IMAGETYPE_GIF);
   }
 
diff --git a/core/tests/Drupal/Tests/Core/Image/ImageTest.php b/core/tests/Drupal/Tests/Core/Image/ImageTest.php
index ea3fc1d..7e8bda1 100644
--- a/core/tests/Drupal/Tests/Core/Image/ImageTest.php
+++ b/core/tests/Drupal/Tests/Core/Image/ImageTest.php
@@ -47,6 +47,7 @@ protected function setUp() {
       ->will($this->returnValue('gd'));
 
     $this->image = new Image($this->toolkit, $this->source);
+    $this->toolkit->setImage($this->image);
   }
 
   /**
@@ -88,10 +89,10 @@ public function testGetFileSize() {
   }
 
   /**
-   * Tests \Drupal\Core\Image\Image::getType().
+   * Tests \Drupal\Core\Image\Image::getToolkit()->getType().
    */
   public function testGetType() {
-    $this->assertEquals($this->image->getType(), IMAGETYPE_PNG);
+    $this->assertEquals($this->image->getToolkit()->getType(), IMAGETYPE_PNG);
   }
 
   /**
@@ -102,23 +103,14 @@ public function testGetMimeType() {
   }
 
   /**
-   * Tests \Drupal\Core\Image\Image::isExisting().
+   * Tests \Drupal\Core\Image\Image::isValid().
    */
-  public function testIsExisting() {
-    $this->assertTrue($this->image->isExisting());
+  public function testIsValid() {
+    $this->assertTrue($this->image->isValid());
     $this->assertTrue(is_readable($this->image->getSource()));
   }
 
   /**
-   * Tests \Drupal\Core\Image\Image::setSource().
-   */
-  public function testSetSource() {
-    $source = __DIR__ . '/../../../../../misc/grippie.png';
-    $this->image->setSource($source);
-    $this->assertEquals($this->image->getSource(), $source);
-  }
-
-  /**
    * Tests \Drupal\Core\Image\Image::getToolkitId().
    */
   public function testGetToolkitId() {
@@ -139,6 +131,7 @@ public function testSave() {
       ->method('chmod')
       ->will($this->returnValue(TRUE));
 
+    $this->toolkit->setImage($image);
     $image->save();
   }
 
@@ -168,17 +161,20 @@ public function testChmodFails() {
       ->method('chmod')
       ->will($this->returnValue(FALSE));
 
+    $this->toolkit->setImage($image);
     $this->assertFalse($image->save());
   }
 
   /**
-   * Tests \Drupal\Core\Image\Image::processInfo().
+   * Tests \Drupal\Core\Image\Image::parseFile().
    */
-  public function testProcessInfoFails() {
+  public function testParseFileFails() {
     $toolkit = $this->getToolkitMock();
     $image = new Image($toolkit, 'magic-foobars.png');
+    $toolkit->setImage($image);
 
-    $this->assertFalse($image->isExisting());
+    $this->assertFalse($image->isValid());
+    $this->assertFalse($image->save());
   }
 
   /**
@@ -187,6 +183,7 @@ public function testProcessInfoFails() {
   public function testScaleWidth() {
     $toolkit = $this->getToolkitMock(array('resize'));
     $image = new Image($toolkit, $this->source);
+    $toolkit->setImage($image);
 
     $toolkit->expects($this->any())
       ->method('resize')
@@ -201,6 +198,7 @@ public function testScaleWidth() {
   public function testScaleHeight() {
     $toolkit = $this->getToolkitMock(array('resize'));
     $image = new Image($toolkit, $this->source);
+    $toolkit->setImage($image);
 
     $toolkit->expects($this->any())
       ->method('resize')
@@ -215,6 +213,7 @@ public function testScaleHeight() {
   public function testScaleSame() {
     $toolkit = $this->getToolkitMock(array('resize'));
     $image = new Image($toolkit, $this->source);
+    $toolkit->setImage($image);
 
     // Dimensions are the same, resize should not be called.
     $toolkit->expects($this->never())
@@ -231,6 +230,7 @@ public function testScaleSame() {
   public function testScaleAndCropWidth() {
     $toolkit = $this->getToolkitMock(array('resize', 'crop'));
     $image = new Image($toolkit, $this->source);
+    $toolkit->setImage($image);
 
     $toolkit->expects($this->once())
       ->method('resize')
@@ -250,6 +250,7 @@ public function testScaleAndCropWidth() {
   public function testScaleAndCropHeight() {
     $toolkit = $this->getToolkitMock(array('resize', 'crop'));
     $image = new Image($toolkit, $this->source);
+    $toolkit->setImage($image);
 
     $toolkit->expects($this->once())
       ->method('resize')
@@ -269,6 +270,7 @@ public function testScaleAndCropHeight() {
   public function testScaleAndCropFails() {
     $toolkit = $this->getToolkitMock(array('resize', 'crop'));
     $image = new Image($toolkit, $this->source);
+    $toolkit->setImage($image);
 
     $toolkit->expects($this->once())
       ->method('resize')
@@ -305,6 +307,7 @@ public function testScaleAndCropFails() {
   public function testCrop() {
     $toolkit = $this->getToolkitMock(array('crop'));
     $image = new Image($toolkit, $this->source);
+    $toolkit->setImage($image);
 
     $toolkit->expects($this->once())
       ->method('crop')
@@ -329,6 +332,7 @@ public function testCrop() {
   public function testDesaturate() {
     $toolkit = $this->getToolkitMock(array('desaturate'));
     $image = new Image($toolkit, $this->source);
+    $toolkit->setImage($image);
 
     $toolkit->expects($this->once())
       ->method('desaturate');
@@ -341,6 +345,7 @@ public function testDesaturate() {
   public function testRotate() {
     $toolkit = $this->getToolkitMock(array('rotate'));
     $image = new Image($toolkit, $this->source);
+    $toolkit->setImage($image);
 
     $toolkit->expects($this->once())
       ->method('rotate');
