diff --git a/core/includes/common.inc b/core/includes/common.inc
index a4805c0..5726d32 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -3083,7 +3083,6 @@ function _drupal_bootstrap_code() {
   require_once __DIR__ . '/tablesort.inc';
   require_once __DIR__ . '/file.inc';
   require_once __DIR__ . '/unicode.inc';
-  require_once __DIR__ . '/image.inc';
   require_once __DIR__ . '/form.inc';
   require_once __DIR__ . '/mail.inc';
   require_once __DIR__ . '/ajax.inc';
diff --git a/core/includes/image.inc b/core/includes/image.inc
deleted file mode 100644
index 9e2d06f..0000000
--- a/core/includes/image.inc
+++ /dev/null
@@ -1,332 +0,0 @@
-<?php
-
-/**
- * @file
- * API for manipulating images.
- */
-
-use Drupal\system\Plugin\ImageToolkitInterface;
-use Drupal\Component\Image\Image;
-
-/**
- * @defgroup image Image toolkits
- * @{
- * Functions for image file manipulations.
- *
- * Drupal's image toolkits provide an abstraction layer for common image file
- * manipulations like scaling, cropping, and rotating. The abstraction frees
- * module authors from the need to support multiple image libraries, and it
- * allows site administrators to choose the library that's best for them.
- *
- * PHP includes the GD library by default so a GD toolkit is installed with
- * Drupal. Other toolkits like ImageMagick are available from contrib modules.
- * GD works well for small images, but using it with larger files may cause PHP
- * to run out of memory. In contrast the ImageMagick library does not suffer
- * from this problem, but it requires the ISP to have installed additional
- * software.
- *
- * Image toolkits are discovered using the Plugin system using
- * \Drupal\system\Plugin\ImageToolkitManager. The toolkit must then be enabled
- * using the admin/config/media/image-toolkit form.
- *
- * Only one toolkit may be selected at a time. If a module author wishes to call
- * a specific toolkit they can check that it is installed by calling
- * \Drupal\system\Plugin\ImageToolkitManager::getAvailableToolkits(), and then
- * calling its functions directly.
- */
-
-/**
- * Gets details about an 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 $filepath
- *   String specifying the path of the image file.
- * @param \Drupal\system\Plugin\ImageToolkitInterface $toolkit
- *   (optional) An image toolkit object to override the default.
- *
- * @return array
- *   FALSE, if the file could not be found or is not an image. Otherwise, a
- *   keyed array containing information about the image:
- *   - "width": Width, in pixels.
- *   - "height": Height, in pixels.
- *   - "extension": Commonly used file extension for the image.
- *   - "mime_type": MIME type ('image/jpeg', 'image/gif', 'image/png').
- *   - "file_size": File size in bytes.
- */
-function image_get_info($filepath, ImageToolkitInterface $toolkit = NULL) {
-  $details = FALSE;
-  if (!is_file($filepath) && !is_uploaded_file($filepath)) {
-    return $details;
-  }
-
-  if ($toolkit === NULL) {
-    $toolkit = Drupal::service('image.toolkit');
-  }
-  if ($toolkit) {
-    $image = new stdClass();
-    $image->source = $filepath;
-    $image->toolkit = $toolkit;
-    $details = $toolkit->getInfo($image);
-    if (isset($details) && is_array($details)) {
-      $details['file_size'] = filesize($filepath);
-    }
-  }
-
-  return $details;
-}
-
-/**
- * Scales an image to the exact width and height given.
- *
- * This function achieves the target aspect ratio by cropping the original image
- * equally on both sides, or equally on the top and bottom. This function is
- * useful to create uniform sized avatars from larger images.
- *
- * The resulting image always has the exact target dimensions.
- *
- * @param object $image
- *   An image object returned by image_load().
- * @param int $width
- *   The target width, in pixels.
- * @param int $height
- *   The target height, in pixels.
- *
- * @return bool
- *   TRUE on success, FALSE on failure.
- *
- * @see image_load()
- * @see image_resize()
- * @see image_crop()
- */
-function image_scale_and_crop($image, $width, $height) {
-  $scale = max($width / $image->info['width'], $height / $image->info['height']);
-  $x = ($image->info['width'] * $scale - $width) / 2;
-  $y = ($image->info['height'] * $scale - $height) / 2;
-
-  if (image_resize($image, $image->info['width'] * $scale, $image->info['height'] * $scale)) {
-    return image_crop($image, $x, $y, $width, $height);
-  }
-  return FALSE;
-}
-
-/**
- * Scales image dimensions while maintaining aspect ratio.
- *
- * @deprecated as of Drupal 8.0. Use
- *   \Drupal\Component\Image\Image::scaleDimensions() directly instead.
- *
- * @see image_scale()
- */
-function image_dimensions_scale(array &$dimensions, $width = NULL, $height = NULL, $upscale = FALSE) {
-  return Image::scaleDimensions($dimensions, $width, $height, $upscale);
-}
-
-/**
- * Scales an image while maintaining aspect ratio.
- *
- * The resulting image can be smaller for one or both target dimensions.
- *
- * @param object $image
- *   An image object returned by image_load().
- * @param int $width
- *   (optional) The target width, in pixels. This value is omitted then the
- *   scaling will based only on the height value.
- * @param int $height
- *   (optional) The target height, in pixels. This value is omitted then the
- *   scaling will based only on the width value.
- * @param bool $upscale
- *   (optional) Boolean indicating that files smaller than the dimensions will
- *   be scaled up. This generally results in a low quality image.
- *
- * @return bool
- *   TRUE on success, FALSE on failure.
- *
- * @see image_dimensions_scale()
- * @see image_load()
- * @see image_scale_and_crop()
- */
-function image_scale($image, $width = NULL, $height = NULL, $upscale = FALSE) {
-  $dimensions = $image->info;
-
-  // Scale the dimensions - if they don't change then just return success.
-  if (!image_dimensions_scale($dimensions, $width, $height, $upscale)) {
-    return TRUE;
-  }
-
-  return image_resize($image, $dimensions['width'], $dimensions['height']);
-}
-
-/**
- * Resizes an image to the given dimensions (ignoring aspect ratio).
- *
- * @param object $image
- *   An image object returned by image_load().
- * @param int $width
- *   The target width, in pixels.
- * @param int $height
- *   The target height, in pixels.
- *
- * @return bool
- *   TRUE on success, FALSE on failure.
- *
- * @see image_load()
- * @see \Drupal\system\Plugin\ImageToolkitInterface::resize()
- */
-function image_resize($image, $width, $height) {
-  $width = (int) round($width);
-  $height = (int) round($height);
-
-  return $image->toolkit->resize($image, $width, $height);
-}
-
-/**
- * Rotates an image by the given number of degrees.
- *
- * @param $image
- *   An image object returned by image_load().
- * @param int $degrees
- *   The number of (clockwise) degrees to rotate the image.
- * @param string $background
- *   (optional) An hexadecimal integer specifying the background color to use
- *   for the uncovered area of the image after the rotation. E.g. 0x000000 for
- *   black, 0xff00ff for magenta, and 0xffffff for white. For images that
- *   support transparency, this will default to transparent. Otherwise it will
- *   be white.
- *
- * @return bool
- *   TRUE on success, FALSE on failure.
- *
- * @see image_load()
- * @see \Drupal\system\Plugin\ImageToolkitInterface::rotate()
- */
-function image_rotate($image, $degrees, $background = NULL) {
-  return $image->toolkit->rotate($image, $degrees, $background);
-}
-
-/**
- * Crops an image to a rectangle specified by the given dimensions.
- *
- * @param $image
- *   An image object returned by image_load().
- * @param int $x
- *   The top left coordinate, in pixels, of the crop area (x axis value).
- * @param int $y
- *   The top left coordinate, in pixels, of the crop area (y axis value).
- * @param int $width
- *   The target width, in pixels.
- * @param int $height
- *   The target height, in pixels.
- *
- * @return bool
- *   TRUE on success, FALSE on failure.
- *
- * @see image_load()
- * @see image_scale_and_crop()
- * @see \Drupal\system\Plugin\ImageToolkitInterface::crop()
- */
-function image_crop($image, $x, $y, $width, $height) {
-  $aspect = $image->info['height'] / $image->info['width'];
-  if (empty($height)) $height = $width / $aspect;
-  if (empty($width)) $width = $height * $aspect;
-
-  $width = (int) round($width);
-  $height = (int) round($height);
-
-  return $image->toolkit->crop($image, $x, $y, $width, $height);
-}
-
-/**
- * Converts an image to grayscale.
- *
- * @param $image
- *   An image object returned by image_load().
- *
- * @return bool
- *   TRUE on success, FALSE on failure.
- *
- * @see image_load()
- * @see \Drupal\system\Plugin\ImageToolkitInterface::desaturate()
- */
-function image_desaturate($image) {
-  return $image->toolkit->desaturate($image);
-}
-
-/**
- * Loads an image file and returns an image object.
- *
- * Any changes to the file are not saved until image_save() is called.
- *
- * @param string $file
- *   Path to an image file.
- * @param \Drupal\system\Plugin\ImageToolkitInterface $toolkit
- *   (optional) Image toolkit object to override the default.
- *
- * @return object
- *   An image object or FALSE if there was a problem loading the file. The
- *   image object has the following properties:
- *    - 'source' - The original file path.
- *    - 'info' - The array of information returned by image_get_info()
- *    - 'toolkit' - The name of the image toolkit requested when the image was
- *      loaded.
- *   Image toolkits may add additional properties. The caller is advised not to
- *   monkey about with them.
- *
- * @see image_save()
- * @see image_get_info()
- */
-function image_load($file, ImageToolkitInterface $toolkit = NULL) {
-  if ($toolkit === NULL) {
-    $toolkit = Drupal::service('image.toolkit');
-  }
-  if ($toolkit) {
-    $image = new stdClass();
-    $image->source = $file;
-    $image->info = image_get_info($file, $toolkit);
-    if (isset($image->info) && is_array($image->info)) {
-      $image->toolkit = $toolkit;
-      if ($toolkit->load($image)) {
-        return $image;
-      }
-    }
-  }
-  return FALSE;
-}
-
-/**
- * Closes the image and saves the changes to a file.
- *
- * @param object $image
- *   An image object returned by image_load(). The object's 'info' property
- *   will be updated if the file is saved successfully.
- * @param $destination
- *   (optional) Destination path where the image should be saved. If it is empty
- *   the original image file will be overwritten.
- *
- * @return bool
- *   TRUE on success, FALSE on failure.
- *
- * @see image_load()
- * @see \Drupal\system\Plugin\ImageToolkitInterface::save()
- */
-function image_save($image, $destination = NULL) {
-  if (empty($destination)) {
-    $destination = $image->source;
-  }
-  if ($return = $image->toolkit->save($image, $destination)) {
-    // Clear the cached file size and refresh the image information.
-    clearstatcache(TRUE, $destination);
-    $image->info = image_get_info($destination, $image->toolkit);
-
-    if (drupal_chmod($destination)) {
-      return $return;
-    }
-  }
-  return FALSE;
-}
-
-/**
- * @} End of "defgroup image".
- */
diff --git a/core/lib/Drupal/Core/Image/ImageFile.php b/core/lib/Drupal/Core/Image/ImageFile.php
new file mode 100644
index 0000000..6ae5393
--- /dev/null
+++ b/core/lib/Drupal/Core/Image/ImageFile.php
@@ -0,0 +1,481 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Image\ImageFile.
+ */
+
+namespace Drupal\Core\Image;
+
+use Drupal\Component\Image\Image;
+use Drupal\system\Plugin\ImageToolkitInterface;
+
+/**
+ * Defines an image object to represent an image file.
+ *
+ * @see \Drupal\system\Plugin\ImageToolkitInterface
+ * @see \Drupal\image\ImageEffectInterface
+ *
+ * @ingroup image
+ */
+class ImageFile {
+
+  /**
+   * String specifying the path of the image file.
+   *
+   * @var string
+   */
+  protected $source;
+
+  /**
+   * An image toolkit object.
+   *
+   * @var \Drupal\system\Plugin\ImageToolkitInterface
+   */
+  protected $toolkit;
+
+  /**
+   * An image file handle.
+   *
+   * @var resource
+   */
+  protected $resource;
+
+  /**
+   * Height, in pixels.
+   *
+   * @var int
+   */
+  protected $height = 0;
+
+  /**
+   * Width, in pixels.
+   *
+   * @var int
+   */
+  protected $width = 0;
+
+  /**
+   * Commonly used file extension for the image.
+   *
+   * @var string
+   */
+  protected $extension = '';
+
+  /**
+   * MIME type ('image/jpeg', 'image/gif', 'image/png').
+   *
+   * @var string
+   */
+  protected $mimeType = '';
+
+  /**
+   * File size in bytes.
+   *
+   * @var int
+   */
+  protected $fileSize = 0;
+
+  /**
+   * If this image file has been processed.
+   *
+   * @var bool
+   */
+  protected $processed = FALSE;
+
+  /**
+   * Constructs a new ImageFile object.
+   *
+   * @param string $source
+   *   The path to an image file.
+   */
+  public function __construct($source) {
+    $this->source = $source;
+  }
+
+  /**
+   * Returns the extension of the image file.
+   *
+   * @return string
+   *   The extension of the file, or an empty string if the file is invalid.
+   */
+  public function getExtension() {
+    $this->processInfo();
+    return $this->extension;
+  }
+
+  /**
+   * Returns the height of the image file.
+   *
+   * @return int
+   *   The height of the file, or 0 if the file is invalid.
+   */
+  public function getHeight() {
+    $this->processInfo();
+    return $this->height;
+  }
+
+  /**
+   * Sets the height of the image file.
+   *
+   * @param int $height
+   *
+   * @return self
+   *   Returns this image file.
+   */
+  public function setHeight($height) {
+    $this->height = $height;
+    return $this;
+  }
+
+  /**
+   * Returns the width of the image file.
+   *
+   * @return int
+   *   The width of the file, or 0 if the file is invalid.
+   */
+  public function getWidth() {
+    $this->processInfo();
+    return $this->width;
+  }
+
+  /**
+   * Sets the width of the image file.
+   *
+   * @param int $width
+   *
+   * @return self
+   *   Returns this image file.
+   */
+  public function setWidth($width) {
+    $this->width = $width;
+    return $this;
+  }
+
+  /**
+   * Returns the size of the image file.
+   *
+   * @return int
+   *   The size of the file in bytes, or 0 if the file is invalid.
+   */
+  public function getFileSize() {
+    $this->processInfo();
+    return $this->fileSize;
+  }
+
+  /**
+   * Returns the MIME type of the image file.
+   *
+   * @return string
+   *   The MIME type of the file, or an empty string if the file is invalid.
+   */
+  public function getMimeType() {
+    $this->processInfo();
+    return $this->mimeType;
+  }
+
+  /**
+   * 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;
+  }
+
+  /**
+   * 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() {
+    return (bool) $this->resource;
+  }
+
+  /**
+   * Retrieves the image file resource.
+   *
+   * @return resource
+   *   The image file handle.
+   */
+  public function getResource() {
+    if (!$this->hasResource()) {
+      $this->processInfo();
+      $this->getToolkit()->load($this);
+    }
+    return $this->resource;
+  }
+
+  /**
+   * 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) {
+    $this->source = $source;
+    return $this;
+  }
+
+  /**
+   * Retrieves the source path of the image file.
+   *
+   * @return string
+   *   The source path of the image file.
+   */
+  public function getSource() {
+    return $this->source;
+  }
+
+  /**
+   * Sets a custom image toolkit.
+   *
+   * @param \Drupal\system\Plugin\ImageToolkitInterface $toolkit
+   *   The image toolkit to use for this image file.
+   *
+   * @return self
+   *   Returns this image file.
+   */
+  public function setToolkit(ImageToolkitInterface $toolkit) {
+    $this->toolkit = $toolkit;
+    return $this;
+  }
+
+  /**
+   * Returns the image toolkit being used for this image file.
+   *
+   * If a custom toolkit was not specified, this will fallback to the default.
+   *
+   * @return \Drupal\system\Plugin\ImageToolkitInterface
+   *   The image toolkit used for this image file.
+   */
+  protected function getToolkit() {
+    if (!$this->toolkit) {
+      $this->toolkit = \Drupal::service('image.toolkit');
+    }
+    return $this->toolkit;
+  }
+
+  /**
+   * Returns the ID of the image toolkit used for this image file.
+   *
+   * @return string
+   *   The ID of the image toolkit.
+   */
+  public function getToolkitId() {
+    return $this->getToolkit()->getPluginId();
+  }
+
+  /**
+   * Closes the image and saves the changes to a file.
+   *
+   * @param string|null $destination
+   *   (optional) Destination path where the image should be saved. If it is empty
+   *   the original image file will be overwritten.
+   *
+   * @return bool
+   *   TRUE on success, FALSE on failure.
+   *
+   * @see \Drupal\system\Plugin\ImageToolkitInterface::save()
+   */
+  public function save($destination = NULL) {
+    if (empty($destination)) {
+      $destination = $this->getSource();
+    }
+    if ($return = $this->getToolkit()->save($this, $destination)) {
+      // Clear the cached file size and refresh the image information.
+      clearstatcache(TRUE, $destination);
+      $this->setSource($destination);
+      $this->processInfo();
+
+      if (drupal_chmod($destination)) {
+        return $return;
+      }
+    }
+    return FALSE;
+  }
+
+  /**
+   * Prepares the image information.
+   *
+   * Drupal supports GIF, JPG and PNG file formats when used with the GD
+   * toolkit, and may support others, depending on which toolkits are
+   * installed.
+   *
+   * @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)) {
+      return FALSE;
+    }
+
+    if ($details = $this->getToolkit()->getInfo($this)) {
+      $this->height = $details['height'];
+      $this->width = $details['width'];
+      $this->extension = $details['extension'];
+      $this->mimeType = $details['mime_type'];
+      $this->fileSize = filesize($destination);
+      $this->processed = TRUE;
+    }
+    return TRUE;
+  }
+
+  /**
+   * Scales an image while maintaining aspect ratio.
+   *
+   * The resulting image can be smaller for one or both target dimensions.
+   *
+   * @param int $width
+   *   (optional) The target width, in pixels. This value is omitted then the
+   *   scaling will based only on the height value.
+   * @param int $height
+   *   (optional) The target height, in pixels. This value is omitted then the
+   *   scaling will based only on the width value.
+   * @param bool $upscale
+   *   (optional) Boolean indicating that files smaller than the dimensions will
+   *   be scaled up. This generally results in a low quality image.
+   *
+   * @return bool
+   *   TRUE on success, FALSE on failure.
+   */
+  public function scale($width = NULL, $height = NULL, $upscale = FALSE) {
+    $dimensions = array(
+      'width' => $this->getWidth(),
+      'height' => $this->getHeight(),
+    );
+
+    // Scale the dimensions - if they don't change then just return success.
+    if (!Image::scaleDimensions($dimensions, $width, $height, $upscale)) {
+      return TRUE;
+    }
+
+    return $this->resize($dimensions['width'], $dimensions['height']);
+
+  }
+
+  /**
+   * Scales an image to the exact width and height given.
+   *
+   * This function achieves the target aspect ratio by cropping the original image
+   * equally on both sides, or equally on the top and bottom. This function is
+   * useful to create uniform sized avatars from larger images.
+   *
+   * The resulting image always has the exact target dimensions.
+   *
+   * @param int $width
+   *   The target width, in pixels.
+   * @param int $height
+   *   The target height, in pixels.
+   *
+   * @return bool
+   *   TRUE on success, FALSE on failure.
+   */
+  public function scaleAndCrop($width, $height) {
+    $scale = max($width / $this->getWidth(), $height / $this->getHeight());
+    $x = ($this->getWidth() * $scale - $width) / 2;
+    $y = ($this->getHeight() * $scale - $height) / 2;
+
+    if ($this->resize($this->getWidth() * $scale, $this->getHeight() * $scale)) {
+      return $this->crop($x, $y, $width, $height);
+    }
+    return FALSE;
+  }
+
+  /**
+   * Crops an image to a rectangle specified by the given dimensions.
+   *
+   * @param int $x
+   *   The top left coordinate, in pixels, of the crop area (x axis value).
+   * @param int $y
+   *   The top left coordinate, in pixels, of the crop area (y axis value).
+   * @param int $width
+   *   The target width, in pixels.
+   * @param int $height
+   *   The target height, in pixels.
+   *
+   * @return bool
+   *   TRUE on success, FALSE on failure.
+   *
+   * @see \Drupal\system\Plugin\ImageToolkitInterface::crop()
+   */
+  function crop($x, $y, $width, $height) {
+    $aspect = $this->getHeight() / $this->getWidth();
+    if (empty($height)) $height = $width / $aspect;
+    if (empty($width)) $width = $height * $aspect;
+
+    $width = (int) round($width);
+    $height = (int) round($height);
+
+    return $this->getToolkit()->crop($this, $x, $y, $width, $height);
+  }
+
+  /**
+   * Resizes an image to the given dimensions (ignoring aspect ratio).
+   *
+   * @param int $width
+   *   The target width, in pixels.
+   * @param int $height
+   *   The target height, in pixels.
+   *
+   * @return bool
+   *   TRUE on success, FALSE on failure.
+   *
+   * @see \Drupal\system\Plugin\ImageToolkitInterface::resize()
+   */
+  public function resize($width, $height) {
+    $width = (int) round($width);
+    $height = (int) round($height);
+
+    return $this->getToolkit()->resize($this, $width, $height);
+  }
+
+  /**
+   * Converts an image to grayscale.
+   *
+   * @return bool
+   *   TRUE on success, FALSE on failure.
+   *
+   * @see \Drupal\system\Plugin\ImageToolkitInterface::desaturate()
+   */
+  public function desaturate() {
+    return $this->getToolkit()->desaturate($this);
+  }
+
+  /**
+   * Rotates an image by the given number of degrees.
+   *
+   * @param int $degrees
+   *   The number of (clockwise) degrees to rotate the image.
+   * @param string $background
+   *   (optional) An hexadecimal integer specifying the background color to use
+   *   for the uncovered area of the image after the rotation. E.g. 0x000000 for
+   *   black, 0xff00ff for magenta, and 0xffffff for white. For images that
+   *   support transparency, this will default to transparent. Otherwise it will
+   *   be white.
+   *
+   * @return bool
+   *   TRUE on success, FALSE on failure.
+   *
+   * @see \Drupal\system\Plugin\ImageToolkitInterface::rotate()
+   */
+  public function rotate($degrees, $background = NULL) {
+    return $this->getToolkit()->rotate($this, $degrees, $background);
+  }
+
+}
diff --git a/core/modules/file/file.module b/core/modules/file/file.module
index 70dfdb4..ae2f2f7 100644
--- a/core/modules/file/file.module
+++ b/core/modules/file/file.module
@@ -5,11 +5,11 @@
  * Defines a "managed_file" Form API field and a "file" field for Field module.
  */
 
+use Drupal\Core\Image\ImageFile;
 use Drupal\file\Plugin\Core\Entity\File;
 use Drupal\Component\Utility\NestedArray;
 use Drupal\Core\Template\Attribute;
 use Symfony\Component\HttpFoundation\JsonResponse;
-use Drupal\file\FileUsage\DatabaseFileUsageBackend;
 use Drupal\file\FileUsage\FileUsageInterface;
 use Drupal\Core\Ajax\AjaxResponse;
 use Drupal\Core\Ajax\ReplaceCommand;
@@ -408,7 +408,7 @@ function file_validate_size(File $file, $file_limit = 0, $user_limit = 0) {
 }
 
 /**
- * Checks that the file is recognized by image_get_info() as an image.
+ * Checks that the file is recognized by ImageFile::getInfo() as an image.
  *
  * @param Drupal\file\File $file
  *   A file entity.
@@ -421,8 +421,8 @@ function file_validate_size(File $file, $file_limit = 0, $user_limit = 0) {
 function file_validate_is_image(File $file) {
   $errors = array();
 
-  $info = image_get_info($file->getFileUri());
-  if (!$info || empty($info['extension'])) {
+  $image = new ImageFile($file->getFileUri());
+  if (!$image->getExtension()) {
     $errors[] = t('Only JPEG, PNG and GIF images are allowed.');
   }
 
@@ -456,16 +456,18 @@ function file_validate_image_resolution(File $file, $maximum_dimensions = 0, $mi
   $errors = array();
 
   // Check first that the file is an image.
-  if ($info = image_get_info($file->getFileUri())) {
+  $image = new ImageFile($file->getFileUri());
+  if ($image->getExtension()) {
     if ($maximum_dimensions) {
       // Check that it is smaller than the given dimensions.
       list($width, $height) = explode('x', $maximum_dimensions);
-      if ($info['width'] > $width || $info['height'] > $height) {
+      if ($image->getWidth() > $width || $image->getHeight() > $height) {
         // Try to resize the image to fit the dimensions.
-        if ($image = image_load($file->getFileUri())) {
-          image_scale($image, $width, $height);
-          image_save($image);
-          $file->filesize = $image->info['file_size'];
+        $image = new ImageFile($file->getFileUri());
+        if ($image->getResource()) {
+          $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 {
@@ -477,7 +479,7 @@ function file_validate_image_resolution(File $file, $maximum_dimensions = 0, $mi
     if ($minimum_dimensions) {
       // Check that it is larger than the given dimensions.
       list($width, $height) = explode('x', $minimum_dimensions);
-      if ($info['width'] < $width || $info['height'] < $height) {
+      if ($image->getWidth() < $width || $image->getHeight() < $height) {
         $errors[] = t('The image is too small; the minimum dimensions are %dimensions pixels.', array('%dimensions' => $minimum_dimensions));
       }
     }
diff --git a/core/modules/file/lib/Drupal/file/Tests/ValidatorTest.php b/core/modules/file/lib/Drupal/file/Tests/ValidatorTest.php
index 8012c4d..a4be16b 100644
--- a/core/modules/file/lib/Drupal/file/Tests/ValidatorTest.php
+++ b/core/modules/file/lib/Drupal/file/Tests/ValidatorTest.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\file\Tests;
 
+use Drupal\Core\Image\ImageFile;
+
 /**
  *  This will run tests against the file validation functions (file_validate_*).
  */
@@ -87,9 +89,9 @@ function testFileValidateImageResolution() {
       $errors = file_validate_image_resolution($this->image, '10x5');
       $this->assertEqual(count($errors), 0, 'No errors should be reported when an oversized image can be scaled down.', 'File');
 
-      $info = image_get_info($this->image->getFileUri());
-      $this->assertTrue($info['width'] <= 10, 'Image scaled to correct width.', 'File');
-      $this->assertTrue($info['height'] <= 5, 'Image scaled to correct height.', 'File');
+      $image = new ImageFile($this->image->getFileUri());
+      $this->assertTrue($image->getWidth() <= 10, 'Image scaled to correct width.', 'File');
+      $this->assertTrue($image->getHeight() <= 5, 'Image scaled to correct height.', 'File');
 
       drupal_unlink('temporary://druplicon.png');
     }
diff --git a/core/modules/image/image.admin.inc b/core/modules/image/image.admin.inc
index 1e2646f..fa34ecd 100644
--- a/core/modules/image/image.admin.inc
+++ b/core/modules/image/image.admin.inc
@@ -6,9 +6,9 @@
  */
 
 use Drupal\Component\Utility\String;
+use Drupal\Core\Image\ImageFile;
 use Drupal\image\ConfigurableImageEffectInterface;
 use Drupal\image\ImageStyleInterface;
-use Symfony\Component\HttpFoundation\RedirectResponse;
 
 /**
  * Menu callback; Listing of all current image styles.
@@ -380,7 +380,11 @@ function theme_image_style_preview($variables) {
 
   // Set up original file information.
   $original_path = $sample_image;
-  $original_image = image_get_info($original_path);
+  $original_image = new ImageFile($original_path);
+  $original_image = array(
+    'width' => $original_image->getWidth(),
+    'height' => $original_image->getHeight(),
+  );
   if ($original_image['width'] > $original_image['height']) {
     $original_width = min($original_image['width'], $sample_width);
     $original_height = round($original_width / $original_image['width'] * $original_image['height']);
@@ -389,15 +393,18 @@ function theme_image_style_preview($variables) {
     $original_height = min($original_image['height'], $sample_height);
     $original_width = round($original_height / $original_image['height'] * $original_image['width']);
   }
-  $original_attributes = array_intersect_key($original_image, array('width' => '', 'height' => ''));
-  $original_attributes['style'] = 'width: ' . $original_width . 'px; height: ' . $original_height . 'px;';
+  $original_image['style'] = 'width: ' . $original_width . 'px; height: ' . $original_height . 'px;';
 
   // Set up preview file information.
   $preview_file = $style->buildUri($original_path);
   if (!file_exists($preview_file)) {
     $style->createDerivative($original_path, $preview_file);
   }
-  $preview_image = image_get_info($preview_file);
+  $preview_image = new ImageFile($preview_file);
+  $preview_image = array(
+    'width' => $preview_image->getWidth(),
+    'height' => $preview_image->getHeight(),
+  );
   if ($preview_image['width'] > $preview_image['height']) {
     $preview_width = min($preview_image['width'], $sample_width);
     $preview_height = round($preview_width / $preview_image['width'] * $preview_image['height']);
@@ -406,8 +413,7 @@ function theme_image_style_preview($variables) {
     $preview_height = min($preview_image['height'], $sample_height);
     $preview_width = round($preview_height / $preview_image['height'] * $preview_image['width']);
   }
-  $preview_attributes = array_intersect_key($preview_image, array('width' => '', 'height' => ''));
-  $preview_attributes['style'] = 'width: ' . $preview_width . 'px; height: ' . $preview_height . 'px;';
+  $preview_image['style'] = 'width: ' . $preview_width . 'px; height: ' . $preview_height . 'px;';
 
   // In the previews, timestamps are added to prevent caching of images.
   $output = '<div class="image-style-preview preview clearfix">';
@@ -416,8 +422,8 @@ function theme_image_style_preview($variables) {
   $original_url = file_create_url($original_path);
   $output .= '<div class="preview-image-wrapper">';
   $output .= t('original') . ' (' . l(t('view actual size'), $original_url) . ')';
-  $output .= '<div class="preview-image original-image" style="' . $original_attributes['style'] . '">';
-  $output .= '<a href="' . $original_url . '">' . theme('image', array('uri' => $original_path, 'alt' => t('Sample original image'), 'title' => '', 'attributes' => $original_attributes)) . '</a>';
+  $output .= '<div class="preview-image original-image" style="' . $original_image['style'] . '">';
+  $output .= '<a href="' . $original_url . '">' . theme('image', array('uri' => $original_path, 'alt' => t('Sample original image'), 'title' => '', 'attributes' => $original_image)) . '</a>';
   $output .= '<div class="height" style="height: ' . $original_height . 'px"><span>' . $original_image['height'] . 'px</span></div>';
   $output .= '<div class="width" style="width: ' . $original_width . 'px"><span>' . $original_image['width'] . 'px</span></div>';
   $output .= '</div>'; // End preview-image.
@@ -427,8 +433,8 @@ function theme_image_style_preview($variables) {
   $preview_url = file_create_url($preview_file) . '?cache_bypass=' . REQUEST_TIME;
   $output .= '<div class="preview-image-wrapper">';
   $output .= check_plain($style->label()) . ' (' . l(t('view actual size'), file_create_url($preview_file) . '?' . time()) . ')';
-  $output .= '<div class="preview-image modified-image" style="' . $preview_attributes['style'] . '">';
-  $output .= '<a href="' . file_create_url($preview_file) . '?' . time() . '">' . theme('image', array('uri' => $preview_url, 'alt' => t('Sample modified image'), 'title' => '', 'attributes' => $preview_attributes)) . '</a>';
+  $output .= '<div class="preview-image modified-image" style="' . $preview_image['style'] . '">';
+  $output .= '<a href="' . file_create_url($preview_file) . '?' . time() . '">' . theme('image', array('uri' => $preview_url, 'alt' => t('Sample modified image'), 'title' => '', 'attributes' => $preview_image)) . '</a>';
   $output .= '<div class="height" style="height: ' . $preview_height . 'px"><span>' . $preview_image['height'] . 'px</span></div>';
   $output .= '<div class="width" style="width: ' . $preview_width . 'px"><span>' . $preview_image['width'] . 'px</span></div>';
   $output .= '</div>'; // End preview-image.
diff --git a/core/modules/image/image.field.inc b/core/modules/image/image.field.inc
index 824f14c..b1c5331 100644
--- a/core/modules/image/image.field.inc
+++ b/core/modules/image/image.field.inc
@@ -7,6 +7,7 @@
 
 use Drupal\Component\Utility\NestedArray;
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Image\ImageFile;
 
 /**
  * Implements hook_field_info().
@@ -226,15 +227,13 @@ function _image_field_resolution_validate($element, &$form_state) {
  * Implements hook_field_presave().
  */
 function image_field_presave(EntityInterface $entity, $field, $instance, $langcode, &$items) {
-
   // Determine the dimensions if necessary.
   foreach ($items as &$item) {
     if (!isset($item['width']) || !isset($item['height'])) {
-      $info = image_get_info(file_load($item['target_id'])->getFileUri());
-
-      if (is_array($info)) {
-        $item['width'] = $info['width'];
-        $item['height'] = $info['height'];
+      $image = new ImageFile(file_load($item['target_id'])->getFileUri());
+      if ($image->getExtension()) {
+        $item['width'] = $image->getWidth();
+        $item['height'] = $image->getHeight();
       }
     }
   }
@@ -301,11 +300,10 @@ function image_field_widget_process($element, &$form_state, $form) {
       $variables['height'] = $element['#value']['height'];
     }
     else {
-      $info = image_get_info($file->getFileUri());
-
-      if (is_array($info)) {
-        $variables['width'] = $info['width'];
-        $variables['height'] = $info['height'];
+      $image = new ImageFile($file->getFileUri());
+      if ($image->getExtension()) {
+        $variables['width'] = $image->getWidth();
+        $variables['height'] = $image->getHeight();
       }
       else {
         $variables['width'] = $variables['height'] = NULL;
diff --git a/core/modules/image/image.install b/core/modules/image/image.install
index 1617e8c..58cf110 100644
--- a/core/modules/image/image.install
+++ b/core/modules/image/image.install
@@ -6,7 +6,6 @@
  */
 
 use Drupal\Component\Uuid\Uuid;
-use Drupal\field\Plugin\Core\Entity\Field;
 
 /**
  * Implements hook_install().
diff --git a/core/modules/image/image.module b/core/modules/image/image.module
index fefbf00..9c75bd4 100644
--- a/core/modules/image/image.module
+++ b/core/modules/image/image.module
@@ -6,6 +6,7 @@
  */
 
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Image\ImageFile;
 use Drupal\field\Plugin\Core\Entity\Field;
 use Drupal\field\Plugin\Core\Entity\FieldInstance;
 use Drupal\file\Plugin\Core\Entity\File;
@@ -276,15 +277,16 @@ function image_file_download($uri) {
     $original_uri = file_uri_scheme($uri) . '://' . implode('/', $args);
 
     // Check that the file exists and is an image.
-    if ($info = image_get_info($uri)) {
+    $image = new ImageFile($uri);
+    if ($image->getExtension()) {
       // Check the permissions of the original to grant access to this image.
       $headers = module_invoke_all('file_download', $original_uri);
       // Confirm there's at least one module granting access and none denying access.
       if (!empty($headers) && !in_array(-1, $headers)) {
         return array(
           // Send headers describing the image's size, and MIME-type...
-          'Content-Type' => $info['mime_type'],
-          'Content-Length' => $info['file_size'],
+          'Content-Type' => $image->getMimeType(),
+          'Content-Length' => $image->getFileSize(),
           // By not explicitly setting them here, this uses normal Drupal
           // Expires, Cache-Control and ETag headers to prevent proxy or
           // browser caching of private images.
diff --git a/core/modules/image/lib/Drupal/image/Controller/ImageStyleDownloadController.php b/core/modules/image/lib/Drupal/image/Controller/ImageStyleDownloadController.php
index 34146c2..d06bffa 100644
--- a/core/modules/image/lib/Drupal/image/Controller/ImageStyleDownloadController.php
+++ b/core/modules/image/lib/Drupal/image/Controller/ImageStyleDownloadController.php
@@ -11,6 +11,7 @@
 use Drupal\Core\Config\ConfigFactory;
 use Drupal\Core\Controller\ControllerInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Image\ImageFile;
 use Drupal\Core\Lock\LockBackendInterface;
 use Drupal\Core\StringTranslation\Translator\TranslatorInterface;
 use Drupal\image\ImageStyleInterface;
@@ -159,11 +160,11 @@ public function deliver(Request $request, $scheme, ImageStyleInterface $image_st
     }
 
     if ($success) {
-      $image = image_load($derivative_uri);
-      $uri = $image->source;
+      $image = new ImageFile($derivative_uri);
+      $uri = $image->getSource();
       $headers += array(
-        'Content-Type' => $image->info['mime_type'],
-        'Content-Length' => $image->info['file_size'],
+        'Content-Type' => $image->getMimeType(),
+        'Content-Length' => $image->getFileSize(),
       );
       return new BinaryFileResponse($uri, 200, $headers);
     }
diff --git a/core/modules/image/lib/Drupal/image/ImageEffectInterface.php b/core/modules/image/lib/Drupal/image/ImageEffectInterface.php
index 05d5efd..2d4a5af 100644
--- a/core/modules/image/lib/Drupal/image/ImageEffectInterface.php
+++ b/core/modules/image/lib/Drupal/image/ImageEffectInterface.php
@@ -8,6 +8,7 @@
 namespace Drupal\image;
 
 use Drupal\Component\Plugin\PluginInspectionInterface;
+use Drupal\Core\Image\ImageFile;
 
 /**
  * Defines the interface for image effects.
@@ -17,13 +18,13 @@
   /**
    * Applies an image effect to the image object.
    *
-   * @param \stdClass $image
-   *   An image object returned by image_load().
+   * @param \Drupal\Core\Image\ImageFile $image
+   *   An image file object.
    *
    * @return bool
    *   TRUE on success. FALSE if unable to perform the image effect on the image.
    */
-  public function applyEffect($image);
+  public function applyEffect(ImageFile $image);
 
   /**
    * Determines the dimensions of the styled image.
diff --git a/core/modules/image/lib/Drupal/image/Plugin/Core/Entity/ImageStyle.php b/core/modules/image/lib/Drupal/image/Plugin/Core/Entity/ImageStyle.php
index ed17dee..a07f060 100644
--- a/core/modules/image/lib/Drupal/image/Plugin/Core/Entity/ImageStyle.php
+++ b/core/modules/image/lib/Drupal/image/Plugin/Core/Entity/ImageStyle.php
@@ -11,6 +11,7 @@
 use Drupal\Core\Entity\Annotation\EntityType;
 use Drupal\Core\Annotation\Translation;
 use Drupal\Core\Entity\EntityStorageControllerInterface;
+use Drupal\Core\Image\ImageFile;
 use Drupal\image\ImageEffectBag;
 use Drupal\image\ImageEffectInterface;
 use Drupal\image\ImageStyleInterface;
@@ -277,7 +278,8 @@ public function createDerivative($original_uri, $derivative_uri) {
       return FALSE;
     }
 
-    if (!$image = image_load($original_uri)) {
+    $image = new ImageFile($original_uri);
+    if (!$image->getResource()) {
       return FALSE;
     }
 
@@ -285,7 +287,7 @@ public function createDerivative($original_uri, $derivative_uri) {
       $effect->applyEffect($image);
     }
 
-    if (!image_save($image, $derivative_uri)) {
+    if (!$image->save($derivative_uri)) {
       if (file_exists($derivative_uri)) {
         watchdog('image', 'Cached image file %destination already exists. There may be an issue with your rewrite configuration.', array('%destination' => $derivative_uri), WATCHDOG_ERROR);
       }
diff --git a/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/CropImageEffect.php b/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/CropImageEffect.php
index 11f228e..35f4afa 100644
--- a/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/CropImageEffect.php
+++ b/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/CropImageEffect.php
@@ -8,6 +8,7 @@
 namespace Drupal\image\Plugin\ImageEffect;
 
 use Drupal\Core\Annotation\Translation;
+use Drupal\Core\Image\ImageFile;
 use Drupal\image\Annotation\ImageEffect;
 
 /**
@@ -24,17 +25,17 @@ class CropImageEffect extends ResizeImageEffect {
   /**
    * {@inheritdoc}
    */
-  public function applyEffect($image) {
+  public function applyEffect(ImageFile $image) {
     // Set sane default values.
     $this->configuration += array(
       'anchor' => 'center-center',
     );
 
     list($x, $y) = explode('-', $this->configuration['anchor']);
-    $x = image_filter_keyword($x, $image->info['width'], $this->configuration['width']);
-    $y = image_filter_keyword($y, $image->info['height'], $this->configuration['height']);
-    if (!image_crop($image, $x, $y, $this->configuration['width'], $this->configuration['height'])) {
-      watchdog('image', 'Image crop failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', array('%toolkit' => $image->toolkit->getPluginId(), '%path' => $image->source, '%mimetype' => $image->info['mime_type'], '%dimensions' => $image->info['width'] . 'x' . $image->info['height']), WATCHDOG_ERROR);
+    $x = image_filter_keyword($x, $image->getWidth(), $this->configuration['width']);
+    $y = image_filter_keyword($y, $image->getHeight(), $this->configuration['height']);
+    if (!$image->crop($x, $y, $this->configuration['width'], $this->configuration['height'])) {
+      watchdog('image', 'Image crop failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', array('%toolkit' => $image->getToolkitId(), '%path' => $image->getSource(), '%mimetype' => $image->getMimeType(), '%dimensions' => $image->getWidth() . 'x' . $image->getHeight()), WATCHDOG_ERROR);
       return FALSE;
     }
     return TRUE;
diff --git a/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/DesaturateImageEffect.php b/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/DesaturateImageEffect.php
index 816ed3e..c75606a 100644
--- a/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/DesaturateImageEffect.php
+++ b/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/DesaturateImageEffect.php
@@ -8,6 +8,7 @@
 namespace Drupal\image\Plugin\ImageEffect;
 
 use Drupal\Core\Annotation\Translation;
+use Drupal\Core\Image\ImageFile;
 use Drupal\image\Annotation\ImageEffect;
 use Drupal\image\ImageEffectBase;
 
@@ -31,9 +32,9 @@ public function transformDimensions(array &$dimensions) {
   /**
    * {@inheritdoc}
    */
-  public function applyEffect($image) {
-    if (!image_desaturate($image)) {
-      watchdog('image', 'Image desaturate failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', array('%toolkit' => $image->toolkit->getPluginId(), '%path' => $image->source, '%mimetype' => $image->info['mime_type'], '%dimensions' => $image->info['width'] . 'x' . $image->info['height']), WATCHDOG_ERROR);
+  public function applyEffect(ImageFile $image) {
+    if (!$image->desaturate()) {
+      watchdog('image', 'Image desaturate failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', array('%toolkit' => $image->getToolkitId(), '%path' => $image->getSource(), '%mimetype' => $image->getMimeType(), '%dimensions' => $image->getWidth() . 'x' . $image->getHeight()), WATCHDOG_ERROR);
       return FALSE;
     }
     return TRUE;
diff --git a/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/ResizeImageEffect.php b/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/ResizeImageEffect.php
index 484e6a0..7e92a28 100644
--- a/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/ResizeImageEffect.php
+++ b/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/ResizeImageEffect.php
@@ -8,6 +8,7 @@
 namespace Drupal\image\Plugin\ImageEffect;
 
 use Drupal\Core\Annotation\Translation;
+use Drupal\Core\Image\ImageFile;
 use Drupal\image\Annotation\ImageEffect;
 use Drupal\image\ConfigurableImageEffectInterface;
 use Drupal\image\ImageEffectBase;
@@ -26,9 +27,9 @@ class ResizeImageEffect extends ImageEffectBase implements ConfigurableImageEffe
   /**
    * {@inheritdoc}
    */
-  public function applyEffect($image) {
-    if (!image_resize($image, $this->configuration['width'], $this->configuration['height'])) {
-      watchdog('image', 'Image resize failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', array('%toolkit' => $image->toolkit->getPluginId(), '%path' => $image->source, '%mimetype' => $image->info['mime_type'], '%dimensions' => $image->info['width'] . 'x' . $image->info['height']), WATCHDOG_ERROR);
+  public function applyEffect(ImageFile $image) {
+    if (!$image->resize($this->configuration['width'], $this->configuration['height'])) {
+      watchdog('image', 'Image resize failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', array('%toolkit' => $image->getToolkitId(), '%path' => $image->getSource(), '%mimetype' => $image->getMimeType(), '%dimensions' => $image->getWidth() . 'x' . $image->getHeight()), WATCHDOG_ERROR);
       return FALSE;
     }
     return TRUE;
diff --git a/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/RotateImageEffect.php b/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/RotateImageEffect.php
index dee537e..8169ed9 100644
--- a/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/RotateImageEffect.php
+++ b/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/RotateImageEffect.php
@@ -8,6 +8,7 @@
 namespace Drupal\image\Plugin\ImageEffect;
 
 use Drupal\Core\Annotation\Translation;
+use Drupal\Core\Image\ImageFile;
 use Drupal\image\Annotation\ImageEffect;
 use Drupal\image\ConfigurableImageEffectInterface;
 use Drupal\image\ImageEffectBase;
@@ -26,7 +27,7 @@ class RotateImageEffect extends ImageEffectBase implements ConfigurableImageEffe
   /**
    * {@inheritdoc}
    */
-  public function applyEffect($image) {
+  public function applyEffect(ImageFile $image) {
     // Set sane default values.
     $this->configuration += array(
       'degrees' => 0,
@@ -53,8 +54,8 @@ public function applyEffect($image) {
       $this->configuration['degrees'] = rand(-1 * $degrees, $degrees);
     }
 
-    if (!image_rotate($image, $this->configuration['degrees'], $this->configuration['bgcolor'])) {
-      watchdog('image', 'Image rotate failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', array('%toolkit' => $image->toolkit->getPluginId(), '%path' => $image->source, '%mimetype' => $image->info['mime_type'], '%dimensions' => $image->info['width'] . 'x' . $image->info['height']), WATCHDOG_ERROR);
+    if (!$image->rotate($this->configuration['degrees'], $this->configuration['bgcolor'])) {
+      watchdog('image', 'Image rotate failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', array('%toolkit' => $image->getToolkitId(), '%path' => $image->getSource(), '%mimetype' => $image->getMimeType(), '%dimensions' => $image->getWidth() . 'x' . $image->getHeight()), WATCHDOG_ERROR);
       return FALSE;
     }
     return TRUE;
diff --git a/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/ScaleAndCropImageEffect.php b/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/ScaleAndCropImageEffect.php
index dfa5955..8dab4b8 100644
--- a/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/ScaleAndCropImageEffect.php
+++ b/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/ScaleAndCropImageEffect.php
@@ -8,6 +8,7 @@
 namespace Drupal\image\Plugin\ImageEffect;
 
 use Drupal\Core\Annotation\Translation;
+use Drupal\Core\Image\ImageFile;
 use Drupal\image\Annotation\ImageEffect;
 
 /**
@@ -24,9 +25,9 @@ class ScaleAndCropImageEffect extends ResizeImageEffect {
   /**
    * {@inheritdoc}
    */
-  public function applyEffect($image) {
-    if (!image_scale_and_crop($image, $this->configuration['width'], $this->configuration['height'])) {
-      watchdog('image', 'Image scale and crop failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', array('%toolkit' => $image->toolkit->getPluginId(), '%path' => $image->source, '%mimetype' => $image->info['mime_type'], '%dimensions' => $image->info['width'] . 'x' . $image->info['height']), WATCHDOG_ERROR);
+  public function applyEffect(ImageFile $image) {
+    if (!$image->scaleAndCrop($this->configuration['width'], $this->configuration['height'])) {
+      watchdog('image', 'Image scale and crop failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', array('%toolkit' => $image->getToolkitId(), '%path' => $image->getSource(), '%mimetype' => $image->getMimeType(), '%dimensions' => $image->getWidth() . 'x' . $image->getHeight()), WATCHDOG_ERROR);
       return FALSE;
     }
     return TRUE;
diff --git a/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/ScaleImageEffect.php b/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/ScaleImageEffect.php
index 47ada5a..452df8d 100644
--- a/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/ScaleImageEffect.php
+++ b/core/modules/image/lib/Drupal/image/Plugin/ImageEffect/ScaleImageEffect.php
@@ -9,6 +9,7 @@
 
 use Drupal\Component\Image\Image;
 use Drupal\Core\Annotation\Translation;
+use Drupal\Core\Image\ImageFile;
 use Drupal\image\Annotation\ImageEffect;
 
 /**
@@ -25,7 +26,7 @@ class ScaleImageEffect extends ResizeImageEffect {
   /**
    * {@inheritdoc}
    */
-  public function applyEffect($image) {
+  public function applyEffect(ImageFile $image) {
     // Set sane default values.
     $this->configuration += array(
       'width' => NULL,
@@ -33,8 +34,8 @@ public function applyEffect($image) {
       'upscale' => FALSE,
     );
 
-    if (!image_scale($image, $this->configuration['width'], $this->configuration['height'], $this->configuration['upscale'])) {
-      watchdog('image', 'Image scale failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', array('%toolkit' => $image->toolkit->getPluginId(), '%path' => $image->source, '%mimetype' => $image->info['mime_type'], '%dimensions' => $image->info['width'] . 'x' . $image->info['height']), WATCHDOG_ERROR);
+    if (!$image->scale($this->configuration['width'], $this->configuration['height'], $this->configuration['upscale'])) {
+      watchdog('image', 'Image scale failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', array('%toolkit' => $image->getToolkitId(), '%path' => $image->getSource(), '%mimetype' => $image->getMimeType(), '%dimensions' => $image->getWidth() . 'x' . $image->getHeight()), WATCHDOG_ERROR);
       return FALSE;
     }
     return TRUE;
diff --git a/core/modules/image/lib/Drupal/image/Tests/ImageDimensionsTest.php b/core/modules/image/lib/Drupal/image/Tests/ImageDimensionsTest.php
index 0ff6738..d661552 100644
--- a/core/modules/image/lib/Drupal/image/Tests/ImageDimensionsTest.php
+++ b/core/modules/image/lib/Drupal/image/Tests/ImageDimensionsTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\image\Tests;
 
+use Drupal\Core\Image\ImageFile;
 use Drupal\simpletest\WebTestBase;
 
 /**
@@ -53,9 +54,9 @@ function testImageDimensions() {
       'height' => 20,
     );
     // Verify that the original image matches the hard-coded values.
-    $image_info = image_get_info($original_uri);
-    $this->assertEqual($image_info['width'], $variables['width']);
-    $this->assertEqual($image_info['height'], $variables['height']);
+    $image_file = new ImageFile($original_uri);
+    $this->assertEqual($image_file->getWidth(), $variables['width']);
+    $this->assertEqual($image_file->getHeight(), $variables['height']);
 
     // Scale an image that is wider than it is high.
     $effect = array(
@@ -75,9 +76,9 @@ function testImageDimensions() {
     $this->drupalGet($url);
     $this->assertResponse(200, 'Image was generated at the URL.');
     $this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.');
-    $image_info = image_get_info($generated_uri);
-    $this->assertEqual($image_info['width'], 120);
-    $this->assertEqual($image_info['height'], 60);
+    $image_file = new ImageFile($generated_uri);
+    $this->assertEqual($image_file->getWidth(), 120);
+    $this->assertEqual($image_file->getHeight(), 60);
 
     // Rotate 90 degrees anticlockwise.
     $effect = array(
@@ -96,9 +97,9 @@ function testImageDimensions() {
     $this->drupalGet($url);
     $this->assertResponse(200, 'Image was generated at the URL.');
     $this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.');
-    $image_info = image_get_info($generated_uri);
-    $this->assertEqual($image_info['width'], 60);
-    $this->assertEqual($image_info['height'], 120);
+    $image_file = new ImageFile($generated_uri);
+    $this->assertEqual($image_file->getWidth(), 60);
+    $this->assertEqual($image_file->getHeight(), 120);
 
     // Scale an image that is higher than it is wide (rotated by previous effect).
     $effect = array(
@@ -118,9 +119,9 @@ function testImageDimensions() {
     $this->drupalGet($url);
     $this->assertResponse(200, 'Image was generated at the URL.');
     $this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.');
-    $image_info = image_get_info($generated_uri);
-    $this->assertEqual($image_info['width'], 45);
-    $this->assertEqual($image_info['height'], 90);
+    $image_file = new ImageFile($generated_uri);
+    $this->assertEqual($image_file->getWidth(), 45);
+    $this->assertEqual($image_file->getHeight(), 90);
 
     // Test upscale disabled.
     $effect = array(
@@ -140,9 +141,9 @@ function testImageDimensions() {
     $this->drupalGet($url);
     $this->assertResponse(200, 'Image was generated at the URL.');
     $this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.');
-    $image_info = image_get_info($generated_uri);
-    $this->assertEqual($image_info['width'], 45);
-    $this->assertEqual($image_info['height'], 90);
+    $image_file = new ImageFile($generated_uri);
+    $this->assertEqual($image_file->getWidth(), 45);
+    $this->assertEqual($image_file->getHeight(), 90);
 
     // Add a desaturate effect.
     $effect = array(
@@ -158,9 +159,9 @@ function testImageDimensions() {
     $this->drupalGet($url);
     $this->assertResponse(200, 'Image was generated at the URL.');
     $this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.');
-    $image_info = image_get_info($generated_uri);
-    $this->assertEqual($image_info['width'], 45);
-    $this->assertEqual($image_info['height'], 90);
+    $image_file = new ImageFile($generated_uri);
+    $this->assertEqual($image_file->getWidth(), 45);
+    $this->assertEqual($image_file->getHeight(), 90);
 
     // Add a random rotate effect.
     $effect = array(
@@ -199,9 +200,9 @@ function testImageDimensions() {
     $this->drupalGet($url);
     $this->assertResponse(200, 'Image was generated at the URL.');
     $this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.');
-    $image_info = image_get_info($generated_uri);
-    $this->assertEqual($image_info['width'], 30);
-    $this->assertEqual($image_info['height'], 30);
+    $image_file = new ImageFile($generated_uri);
+    $this->assertEqual($image_file->getWidth(), 30);
+    $this->assertEqual($image_file->getHeight(), 30);
 
     // Rotate to a non-multiple of 90 degrees.
     $effect = array(
diff --git a/core/modules/image/lib/Drupal/image/Tests/ImageFieldValidateTest.php b/core/modules/image/lib/Drupal/image/Tests/ImageFieldValidateTest.php
index 1cd0ce9..c3f14f9 100644
--- a/core/modules/image/lib/Drupal/image/Tests/ImageFieldValidateTest.php
+++ b/core/modules/image/lib/Drupal/image/Tests/ImageFieldValidateTest.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\image\Tests;
 
+use Drupal\Core\Image\ImageFile;
+
 /**
  * Test class to check for various validations.
  */
@@ -37,11 +39,11 @@ function testResolution() {
     $image_that_is_too_big = FALSE;
     $image_that_is_too_small = FALSE;
     foreach ($this->drupalGetTestFiles('image') as $image) {
-      $info = image_get_info($image->uri);
-      if ($info['width'] > $max_resolution) {
+      $image_file = new ImageFile($image->uri);
+      if ($image_file->getWidth() > $max_resolution) {
         $image_that_is_too_big = $image;
       }
-      if ($info['width'] < $min_resolution) {
+      if ($image_file->getWidth() < $min_resolution) {
         $image_that_is_too_small = $image;
       }
       if ($image_that_is_too_small && $image_that_is_too_big) {
diff --git a/core/modules/image/lib/Drupal/image/Tests/ImageItemTest.php b/core/modules/image/lib/Drupal/image/Tests/ImageItemTest.php
index 5e7472f..9574a28 100644
--- a/core/modules/image/lib/Drupal/image/Tests/ImageItemTest.php
+++ b/core/modules/image/lib/Drupal/image/Tests/ImageItemTest.php
@@ -9,6 +9,7 @@
 
 use Drupal\Core\Entity\Field\FieldInterface;
 use Drupal\Core\Entity\Field\FieldItemInterface;
+use Drupal\Core\Image\ImageFile;
 use Drupal\field\Tests\FieldUnitTestBase;
 
 /**
@@ -78,9 +79,9 @@ public function testImageItem() {
     $this->assertEqual($entity->image_test->target_id, $this->image->id());
     $this->assertEqual($entity->image_test->alt, $alt);
     $this->assertEqual($entity->image_test->title, $title);
-    $info = image_get_info('public://example.jpg');
-    $this->assertEqual($entity->image_test->width, $info['width']);
-    $this->assertEqual($entity->image_test->height, $info['height']);
+    $image = new ImageFile('public://example.jpg');
+    $this->assertEqual($entity->image_test->width, $image->getWidth());
+    $this->assertEqual($entity->image_test->height, $image->getHeight());
     $this->assertEqual($entity->image_test->entity->id(), $this->image->id());
     $this->assertEqual($entity->image_test->entity->uuid(), $this->image->uuid());
 
@@ -98,9 +99,9 @@ public function testImageItem() {
     $entity->save();
     $this->assertEqual($entity->image_test->entity->id(), $image2->id());
     $this->assertEqual($entity->image_test->entity->getFileUri(), $image2->getFileUri());
-    $info = image_get_info('public://example-2.jpg');
-    $this->assertEqual($entity->image_test->width, $info['width']);
-    $this->assertEqual($entity->image_test->height, $info['height']);
+    $image = new ImageFile('public://example-2.jpg');
+    $this->assertEqual($entity->image_test->width, $image->getWidth());
+    $this->assertEqual($entity->image_test->height, $image->getHeight());
     $this->assertEqual($entity->image_test->alt, $new_alt);
 
     // Check that the image item can be set to the referenced file directly.
diff --git a/core/modules/image/lib/Drupal/image/Tests/ImageStylesPathAndUrlTest.php b/core/modules/image/lib/Drupal/image/Tests/ImageStylesPathAndUrlTest.php
index cae45d7..11c6cbf 100644
--- a/core/modules/image/lib/Drupal/image/Tests/ImageStylesPathAndUrlTest.php
+++ b/core/modules/image/lib/Drupal/image/Tests/ImageStylesPathAndUrlTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\image\Tests;
 
+use Drupal\Core\Image\ImageFile;
 use Drupal\simpletest\WebTestBase;
 use Symfony\Component\HttpFoundation\Request;
 
@@ -153,9 +154,9 @@ function doImageStyleUrlAndPathTests($scheme, $clean_url = TRUE, $extra_slash =
     $this->assertResponse(200, 'Image was generated at the URL.');
     $this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.');
     $this->assertRaw(file_get_contents($generated_uri), 'URL returns expected file.');
-    $generated_image_info = image_get_info($generated_uri);
-    $this->assertEqual($this->drupalGetHeader('Content-Type'), $generated_image_info['mime_type'], 'Expected Content-Type was reported.');
-    $this->assertEqual($this->drupalGetHeader('Content-Length'), $generated_image_info['file_size'], 'Expected Content-Length was reported.');
+    $image = new ImageFile($generated_uri);
+    $this->assertEqual($this->drupalGetHeader('Content-Type'), $image->getMimeType(), 'Expected Content-Type was reported.');
+    $this->assertEqual($this->drupalGetHeader('Content-Length'), $image->getFileSize(), 'Expected Content-Length was reported.');
     if ($scheme == 'private') {
       $this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', 'Expires header was sent.');
       $this->assertNotEqual(strpos($this->drupalGetHeader('Cache-Control'), 'no-cache'), FALSE, 'Cache-Control header contains \'no-cache\' to prevent caching.');
diff --git a/core/modules/image/tests/modules/image_module_test/lib/Drupal/image_module_test/Plugin/ImageEffect/NullTestImageEffect.php b/core/modules/image/tests/modules/image_module_test/lib/Drupal/image_module_test/Plugin/ImageEffect/NullTestImageEffect.php
index 23b1931..f847361 100644
--- a/core/modules/image/tests/modules/image_module_test/lib/Drupal/image_module_test/Plugin/ImageEffect/NullTestImageEffect.php
+++ b/core/modules/image/tests/modules/image_module_test/lib/Drupal/image_module_test/Plugin/ImageEffect/NullTestImageEffect.php
@@ -8,6 +8,7 @@
 namespace Drupal\image_module_test\Plugin\ImageEffect;
 
 use Drupal\Core\Annotation\Translation;
+use Drupal\Core\Image\ImageFile;
 use Drupal\image\Annotation\ImageEffect;
 use Drupal\image\ImageEffectBase;
 
@@ -24,7 +25,7 @@ class NullTestImageEffect extends ImageEffectBase {
   /**
    * {@inheritdoc}
    */
-  public function applyEffect($image) {
+  public function applyEffect(ImageFile $image) {
     return TRUE;
   }
 
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 06cb4a6..e6f197d 100644
--- a/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/GDToolkit.php
+++ b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkit/GDToolkit.php
@@ -10,6 +10,7 @@
 use Drupal\Component\Plugin\PluginBase;
 use Drupal\Component\Annotation\Plugin;
 use Drupal\Core\Annotation\Translation;
+use Drupal\Core\Image\ImageFile;
 use Drupal\system\Plugin\ImageToolkitInterface;
 
 /**
@@ -50,33 +51,34 @@ public function settingsFormSubmit($form, &$form_state) {
   /**
    * Implements \Drupal\system\Plugin\ImageToolkitInterface::resize().
    */
-  public function resize($image, $width, $height) {
+  public function resize(ImageFile $image, $width, $height) {
     $res = $this->createTmp($image, $width, $height);
 
-    if (!imagecopyresampled($res, $image->resource, 0, 0, 0, 0, $width, $height, $image->info['width'], $image->info['height'])) {
+    if (!imagecopyresampled($res, $image->getResource(), 0, 0, 0, 0, $width, $height, $image->getWidth(), $image->getHeight())) {
       return FALSE;
     }
 
-    imagedestroy($image->resource);
+    imagedestroy($image->getResource());
     // Update image object.
-    $image->resource = $res;
-    $image->info['width'] = $width;
-    $image->info['height'] = $height;
+    $image
+      ->setResource($res)
+      ->setWidth($width)
+      ->setHeight($height);
     return TRUE;
   }
 
   /**
    * Implements \Drupal\system\Plugin\ImageToolkitInterface::rotate().
    */
-  public function rotate($image, $degrees, $background = NULL) {
+  public function rotate(ImageFile $image, $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->source));
+      watchdog('image', 'The image %file could not be rotated because the imagerotate() function is not available in this PHP installation.', array('%file' => $image->getSource()));
       return FALSE;
     }
 
-    $width = $image->info['width'];
-    $height = $image->info['height'];
+    $width = $image->getWidth();
+    $height = $image->getHeight();
 
     // Convert the hexadecimal background value to a color index value.
     if (isset($background)) {
@@ -84,88 +86,91 @@ public function rotate($image, $degrees, $background = NULL) {
       for ($i = 16; $i >= 0; $i -= 8) {
         $rgb[] = (($background >> $i) & 0xFF);
       }
-      $background = imagecolorallocatealpha($image->resource, $rgb[0], $rgb[1], $rgb[2], 0);
+      $background = imagecolorallocatealpha($image->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->resource);
+      $background = imagecolortransparent($image->getResource());
 
       // If no transparent colors, use white.
       if ($background == 0) {
-        $background = imagecolorallocatealpha($image->resource, 255, 255, 255, 0);
+        $background = imagecolorallocatealpha($image->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->info['extension'] == 'gif') {
-      $transparent_index = imagecolortransparent($image->resource);
+    if ($image->getExtension() == 'gif') {
+      $transparent_index = imagecolortransparent($image->getResource());
       if ($transparent_index != 0) {
-        $transparent_gif_color = imagecolorsforindex($image->resource, $transparent_index);
+        $transparent_gif_color = imagecolorsforindex($image->getResource(), $transparent_index);
       }
     }
 
-    $image->resource = imagerotate($image->resource, 360 - $degrees, $background);
+    $image->setResource(imagerotate($image->getResource(), 360 - $degrees, $background));
 
     // GIFs need to reassign the transparent color after performing the rotate.
     if (isset($transparent_gif_color)) {
-      $background = imagecolorexactalpha($image->resource, $transparent_gif_color['red'], $transparent_gif_color['green'], $transparent_gif_color['blue'], $transparent_gif_color['alpha']);
-      imagecolortransparent($image->resource, $background);
+      $background = imagecolorexactalpha($image->getResource(), $transparent_gif_color['red'], $transparent_gif_color['green'], $transparent_gif_color['blue'], $transparent_gif_color['alpha']);
+      imagecolortransparent($image->getResource(), $background);
     }
 
-    $image->info['width'] = imagesx($image->resource);
-    $image->info['height'] = imagesy($image->resource);
+    $image
+      ->setWidth(imagesx($image->getResource()))
+      ->setHeight(imagesy($image->getResource()));
     return TRUE;
   }
 
   /**
    * Implements \Drupal\system\Plugin\ImageToolkitInterface::crop().
    */
-  public function crop($image, $x, $y, $width, $height) {
+  public function crop(ImageFile $image, $x, $y, $width, $height) {
     $res = $this->createTmp($image, $width, $height);
 
-    if (!imagecopyresampled($res, $image->resource, 0, 0, $x, $y, $width, $height, $width, $height)) {
+    if (!imagecopyresampled($res, $image->getResource(), 0, 0, $x, $y, $width, $height, $width, $height)) {
       return FALSE;
     }
 
     // Destroy the original image and return the modified image.
-    imagedestroy($image->resource);
-    $image->resource = $res;
-    $image->info['width'] = $width;
-    $image->info['height'] = $height;
+    imagedestroy($image->getResource());
+    $image
+      ->setResource($res)
+      ->setWidth($width)
+      ->setHeight($height);
     return TRUE;
   }
 
   /**
    * Implements \Drupal\system\Plugin\ImageToolkitInterface::desaturate().
    */
-  public function desaturate($image) {
+  public function desaturate(ImageFile $image) {
     // 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->source));
+      watchdog('image', 'The image %file could not be desaturated because the imagefilter() function is not available in this PHP installation.', array('%file' => $image->getSource()));
       return FALSE;
     }
 
-    return imagefilter($image->resource, IMG_FILTER_GRAYSCALE);
+    return imagefilter($image->getResource(), IMG_FILTER_GRAYSCALE);
   }
 
   /**
    * Implements \Drupal\system\Plugin\ImageToolkitInterface::load().
    */
-  public function load($image) {
-    $extension = str_replace('jpg', 'jpeg', $image->info['extension']);
+  public function load(ImageFile $image) {
+    $extension = str_replace('jpg', 'jpeg', $image->getExtension());
     $function = 'imagecreatefrom' . $extension;
-    if (function_exists($function) && $image->resource = $function($image->source)) {
-      if (!imageistruecolor($image->resource)) {
+    if (function_exists($function) && $resource = $function($image->getSource())) {
+      $image->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($image, $image->info['width'], $image->info['height']);
-        imagecopy($new_image, $image->resource, 0, 0, 0, 0, $image->info['width'], $image->info['height']);
-        imagedestroy($image->resource);
-        $image->resource = $new_image;
+        $new_image = $this->createTmp($image, $image->getWidth(), $image->getHeight());
+        imagecopy($new_image, $resource, 0, 0, 0, 0, $image->getWidth(), $image->getHeight());
+        imagedestroy($resource);
+        $image->setResource($new_image);
       }
-      return (bool) $image->resource;
+      return (bool) $image->getResource();
     }
 
     return FALSE;
@@ -174,7 +179,7 @@ public function load($image) {
   /**
    * Implements \Drupal\system\Plugin\ImageToolkitInterface::save().
    */
-  public function save($image, $destination) {
+  public function save(ImageFile $image, $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)) {
@@ -188,21 +193,21 @@ public function save($image, $destination) {
       $destination = drupal_realpath($destination);
     }
 
-    $extension = str_replace('jpg', 'jpeg', $image->info['extension']);
+    $extension = str_replace('jpg', 'jpeg', $image->getExtension());
     $function = 'image' . $extension;
     if (!function_exists($function)) {
       return FALSE;
     }
     if ($extension == 'jpeg') {
-      $success = $function($image->resource, $destination, config('system.image.gd')->get('jpeg_quality'));
+      $success = $function($image->getResource(), $destination, config('system.image.gd')->get('jpeg_quality'));
     }
     else {
       // Always save PNG images with full transparency.
       if ($extension == 'png') {
-        imagealphablending($image->resource, FALSE);
-        imagesavealpha($image->resource, TRUE);
+        imagealphablending($image->getResource(), FALSE);
+        imagesavealpha($image->getResource(), TRUE);
       }
-      $success = $function($image->resource, $destination);
+      $success = $function($image->getResource(), $destination);
     }
     // Move temporary local file to remote destination.
     if (isset($permanent_destination) && $success) {
@@ -214,9 +219,9 @@ public function save($image, $destination) {
   /**
    * Implements \Drupal\system\Plugin\ImageToolkitInterface::getInfo().
    */
-  public function getInfo($image) {
+  public function getInfo(ImageFile $image) {
     $details = FALSE;
-    $data = getimagesize($image->source);
+    $data = getimagesize($image->getSource());
 
     if (isset($data) && is_array($data)) {
       $extensions = array('1' => 'gif', '2' => 'jpg', '3' => 'png');
@@ -235,7 +240,7 @@ public function getInfo($image) {
   /**
    * Creates a truecolor image preserving transparency from a provided image.
    *
-   * @param object $image
+   * @param \Drupal\Core\Image\ImageFile $image
    *   An image object.
    * @param int $width
    *   The new width of the new image, in pixels.
@@ -245,16 +250,16 @@ public function getInfo($image) {
    * @return resource
    *   A GD image handle.
    */
-  public function createTmp($image, $width, $height) {
+  public function createTmp(ImageFile $image, $width, $height) {
     $res = imagecreatetruecolor($width, $height);
 
-    if ($image->info['extension'] == 'gif') {
+    if ($image->getExtension() == 'gif') {
       // Grab transparent color index from image resource.
-      $transparent = imagecolortransparent($image->resource);
+      $transparent = imagecolortransparent($image->getResource());
 
       if ($transparent >= 0) {
         // The original must have a transparent color, allocate to the new image.
-        $transparent_color = imagecolorsforindex($image->resource, $transparent);
+        $transparent_color = imagecolorsforindex($image->getResource(), $transparent);
         $transparent = imagecolorallocate($res, $transparent_color['red'], $transparent_color['green'], $transparent_color['blue']);
 
         // Flood with our new transparent color.
@@ -262,7 +267,7 @@ public function createTmp($image, $width, $height) {
         imagecolortransparent($res, $transparent);
       }
     }
-    elseif ($image->info['extension'] == 'png') {
+    elseif ($image->getExtension() == '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/Plugin/ImageToolkitInterface.php b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkitInterface.php
index 0b22f3c..f0a497b 100644
--- a/core/modules/system/lib/Drupal/system/Plugin/ImageToolkitInterface.php
+++ b/core/modules/system/lib/Drupal/system/Plugin/ImageToolkitInterface.php
@@ -7,13 +7,43 @@
 
 namespace Drupal\system\Plugin;
 
+use Drupal\Component\Plugin\PluginInspectionInterface;
+use Drupal\Core\Image\ImageFile;
+
+/**
+ * @defgroup image Image toolkits
+ * @{
+ * Functions for image file manipulations.
+ *
+ * Drupal's image toolkits provide an abstraction layer for common image file
+ * manipulations like scaling, cropping, and rotating. The abstraction frees
+ * module authors from the need to support multiple image libraries, and it
+ * allows site administrators to choose the library that's best for them.
+ *
+ * PHP includes the GD library by default so a GD toolkit is installed with
+ * Drupal. Other toolkits like ImageMagick are available from contrib modules.
+ * GD works well for small images, but using it with larger files may cause PHP
+ * to run out of memory. In contrast the ImageMagick library does not suffer
+ * from this problem, but it requires the ISP to have installed additional
+ * software.
+ *
+ * Image toolkits are discovered using the Plugin system using
+ * \Drupal\system\Plugin\ImageToolkitManager. The toolkit must then be enabled
+ * using the admin/config/media/image-toolkit form.
+ *
+ * Only one toolkit may be selected at a time. If a module author wishes to call
+ * a specific toolkit they can check that it is installed by calling
+ * \Drupal\system\Plugin\ImageToolkitManager::getAvailableToolkits(), and then
+ * calling its functions directly.
+ */
+
 /**
  * Defines an interface for image toolkits.
  *
  * An image toolkit provides common image file manipulations like scaling,
  * cropping, and rotating.
  */
-interface ImageToolkitInterface {
+interface ImageToolkitInterface extends PluginInspectionInterface {
 
   /**
    * Retrieves toolkit's settings form.
@@ -32,7 +62,7 @@ function settingsFormSubmit($form, &$form_state);
   /**
    * Scales an image to the specified size.
    *
-   * @param object $image
+   * @param \Drupal\Core\Image\ImageFile $image
    *   An image object. The $image->resource, $image->info['width'], and
    *   $image->info['height'] values will be modified by this call.
    * @param int $width
@@ -42,15 +72,13 @@ function settingsFormSubmit($form, &$form_state);
    *
    * @return bool
    *   TRUE or FALSE, based on success.
-   *
-   * @see image_resize()
    */
-  function resize($image, $width, $height);
+  function resize(ImageFile $image, $width, $height);
 
   /**
    * Rotates an image the given number of degrees.
    *
-   * @param object $image
+   * @param \Drupal\Core\Image\ImageFile $image
    *   An image object. The $image->resource, $image->info['width'], and
    *   $image->info['height'] values will be modified by this call.
    * @param int $degrees
@@ -64,15 +92,13 @@ function resize($image, $width, $height);
    *
    * @return bool
    *   TRUE or FALSE, based on success.
-   *
-   * @see image_rotate()
    */
-  function rotate($image, $degrees, $background = NULL);
+  function rotate(ImageFile $image, $degrees, $background = NULL);
 
   /**
    * Crops an image.
    *
-   * @param object $image
+   * @param \Drupal\Core\Image\ImageFile $image
    *   An image object. The $image->resource, $image->info['width'], and
    *   $image->info['height'] values will be modified by this call.
    * @param int $x
@@ -89,56 +115,50 @@ function rotate($image, $degrees, $background = NULL);
    *
    * @see image_crop()
    */
-  function crop($image, $x, $y, $width, $height);
+  function crop(ImageFile $image, $x, $y, $width, $height);
 
   /**
    * Converts an image resource to grayscale.
    *
    * Note that transparent GIFs loose transparency when desaturated.
    *
-   * @param object $image
+   * @param \Drupal\Core\Image\ImageFile $image
    *   An image object. The $image->resource value will be modified by this
    *   call.
    *
    * @return bool
    *   TRUE or FALSE, based on success.
-   *
-   * @see image_desaturate()
    */
-  function desaturate($image);
+  function desaturate(ImageFile $image);
 
   /**
    * Creates an image resource from a file.
    *
-   * @param object $image
+   * @param \Drupal\Core\Image\ImageFile $image
    *   An image object. The $image->resource value will populated by this call.
    *
    * @return bool
    *   TRUE or FALSE, based on success.
-   *
-   * @see image_load()
    */
-  function load($image);
+  function load(ImageFile $image);
 
   /**
    * Writes an image resource to a destination file.
    *
-   * @param object $image
+   * @param \Drupal\Core\Image\ImageFile $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.
-   *
-   * @see image_save()
    */
-  function save($image, $destination);
+  function save(ImageFile $image, $destination);
 
   /**
    * Gets details about an image.
    *
-   * @param object $image
+   * @param \Drupal\Core\Image\ImageFile $image
    *   An image object.
    *
    * @return array
@@ -149,9 +169,9 @@ function save($image, $destination);
    *   - "extension": Commonly used file extension for the image.
    *   - "mime_type": MIME type ('image/jpeg', 'image/gif', 'image/png').
    *
-   * @see image_get_info()
+   * @see \Drupal\Core\Image\ImageFile::processInfo()
    */
-  function getInfo($image);
+  function getInfo(ImageFile $image);
 
   /**
    * Verifies Image Toolkit is set up correctly.
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 624dc79..3304656 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitGdTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitGdTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\system\Tests\Image;
 
+use Drupal\Core\Image\ImageFile;
 use Drupal\simpletest\DrupalUnitTestBase;
 use Drupal\system\Plugin\ImageToolkitManager;
 
@@ -79,15 +80,15 @@ function colorsAreEqual($color_a, $color_b) {
   /**
    * Function for finding a pixel's RGBa values.
    */
-  function getPixelColor($image, $x, $y) {
-    $color_index = imagecolorat($image->resource, $x, $y);
+  function getPixelColor(ImageFile $image, $x, $y) {
+    $color_index = imagecolorat($image->getResource(), $x, $y);
 
-    $transparent_index = imagecolortransparent($image->resource);
+    $transparent_index = imagecolortransparent($image->getResource());
     if ($color_index == $transparent_index) {
       return array(0, 0, 0, 127);
     }
 
-    return array_values(imagecolorsforindex($image->resource, $color_index));
+    return array_values(imagecolorsforindex($image->getResource(), $color_index));
   }
 
   /**
@@ -152,7 +153,7 @@ function testManipulations() {
         'corners' => array_fill(0, 4, $this->white),
       ),
       'scale_and_crop' => array(
-        'function' => 'scale_and_crop',
+        'function' => 'scaleAndCrop',
         'arguments' => array(10, 8),
         'width' => 10,
         'height' => 8,
@@ -219,17 +220,17 @@ function testManipulations() {
     foreach ($files as $file) {
       foreach ($operations as $op => $values) {
         // Load up a fresh image.
-        $image = image_load(drupal_get_path('module', 'simpletest') . '/files/' . $file, $manager->createInstance('gd'));
+        $image = new ImageFile(drupal_get_path('module', 'simpletest') . '/files/' . $file, $manager->createInstance('gd'));
         if (!$image) {
           $this->fail(t('Could not load image %file.', array('%file' => $file)));
           continue 2;
         }
 
         // All images should be converted to truecolor when loaded.
-        $image_truecolor = imageistruecolor($image->resource);
+        $image_truecolor = imageistruecolor($image->getResource());
         $this->assertTrue($image_truecolor, format_string('Image %file after load is a truecolor image.', array('%file' => $file)));
 
-        if ($image->info['extension'] == 'gif') {
+        if ($image->getExtension() == 'gif') {
           if ($op == 'desaturate') {
             // Transparent GIFs and the imagefilter function don't work together.
             $values['corners'][3][3] = 0;
@@ -237,11 +238,7 @@ function testManipulations() {
         }
 
         // Perform our operation.
-        $function = 'image_' . $values['function'];
-        $arguments = array();
-        $arguments[] = &$image;
-        $arguments = array_merge($arguments, $values['arguments']);
-        call_user_func_array($function, $arguments);
+        call_user_func_array(array($image, $values['function']), $values['arguments']);
 
         // To keep from flooding the test with assert values, make a general
         // value for whether each group of values fail.
@@ -250,24 +247,24 @@ function testManipulations() {
         $correct_colors = TRUE;
 
         // Check the real dimensions of the image first.
-        if (imagesy($image->resource) != $values['height'] || imagesx($image->resource) != $values['width']) {
+        if (imagesy($image->getResource()) != $values['height'] || imagesx($image->getResource()) != $values['width']) {
           $correct_dimensions_real = FALSE;
         }
 
         // Check that the image object has an accurate record of the dimensions.
-        if ($image->info['width'] != $values['width'] || $image->info['height'] != $values['height']) {
+        if ($image->getWidth() != $values['width'] || $image->getHeight() != $values['height']) {
           $correct_dimensions_object = FALSE;
         }
 
         $directory = $this->public_files_directory .'/imagetest';
         file_prepare_directory($directory, FILE_CREATE_DIRECTORY);
-        image_save($image, $directory . '/' . $op . '.' . $image->info['extension']);
+        $image->save($directory . '/' . $op . '.' . $image->getExtension());
 
         $this->assertTrue($correct_dimensions_real, format_string('Image %file after %action action has proper dimensions.', array('%file' => $file, '%action' => $op)));
         $this->assertTrue($correct_dimensions_object, format_string('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->info['extension'] != 'jpg') {
+        if ($image->getExtension() != 'jpg') {
           // 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 14a84e9..daad04c 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitTest.php
@@ -7,10 +7,11 @@
 
 namespace Drupal\system\Tests\Image;
 
+use Drupal\Core\Image\ImageFile;
 use Drupal\system\Plugin\ImageToolkitManager;
 
 /**
- * Test that the functions in image.inc correctly pass data to the toolkit.
+ * Tests that the methods in ImageFile correctly pass data to the toolkit.
  */
 class ToolkitTest extends ToolkitTestBase {
   public static function getInfo() {
@@ -34,12 +35,13 @@ function testGetAvailableToolkits() {
   }
 
   /**
-   * Test the image_load() function.
+   * Tests ImageFile's methods.
    */
   function testLoad() {
-    $image = image_load($this->file, $this->toolkit);
+    $image = new ImageFile($this->file);
+    $image->setToolkit($this->toolkit)->getResource();
     $this->assertTrue(is_object($image), 'Returned an object.');
-    $this->assertEqual($this->toolkit, $image->toolkit, 'Image had toolkit set.');
+    $this->assertEqual($this->toolkit->getPluginId(), $image->getToolkitId(), 'Image had toolkit set.');
     $this->assertToolkitOperationsCalled(array('load', 'get_info'));
   }
 
@@ -47,7 +49,7 @@ function testLoad() {
    * Test the image_save() function.
    */
   function testSave() {
-    $this->assertFalse(image_save($this->image), 'Function returned the expected value.');
+    $this->assertFalse($this->image->save(), 'Function returned the expected value.');
     $this->assertToolkitOperationsCalled(array('save'));
   }
 
@@ -55,7 +57,7 @@ function testSave() {
    * Test the image_resize() function.
    */
   function testResize() {
-    $this->assertTrue(image_resize($this->image, 1, 2), 'Function returned the expected value.');
+    $this->assertTrue($this->image->resize(1, 2), 'Function returned the expected value.');
     $this->assertToolkitOperationsCalled(array('resize'));
 
     // Check the parameters.
@@ -69,7 +71,7 @@ function testResize() {
    */
   function testScale() {
 // TODO: need to test upscaling
-    $this->assertTrue(image_scale($this->image, 10, 10), 'Function returned the expected value.');
+    $this->assertTrue($this->image->scale(10, 10), 'Function returned the expected value.');
     $this->assertToolkitOperationsCalled(array('resize'));
 
     // Check the parameters.
@@ -82,7 +84,7 @@ function testScale() {
    * Test the image_scale_and_crop() function.
    */
   function testScaleAndCrop() {
-    $this->assertTrue(image_scale_and_crop($this->image, 5, 10), 'Function returned the expected value.');
+    $this->assertTrue($this->image->scaleAndCrop(5, 10), 'Function returned the expected value.');
     $this->assertToolkitOperationsCalled(array('resize', 'crop'));
 
     // Check the parameters.
@@ -98,7 +100,7 @@ function testScaleAndCrop() {
    * Test the image_rotate() function.
    */
   function testRotate() {
-    $this->assertTrue(image_rotate($this->image, 90, 1), 'Function returned the expected value.');
+    $this->assertTrue($this->image->rotate(90, 1), 'Function returned the expected value.');
     $this->assertToolkitOperationsCalled(array('rotate'));
 
     // Check the parameters.
@@ -111,7 +113,7 @@ function testRotate() {
    * Test the image_crop() function.
    */
   function testCrop() {
-    $this->assertTrue(image_crop($this->image, 1, 2, 3, 4), 'Function returned the expected value.');
+    $this->assertTrue($this->image->crop(1, 2, 3, 4), 'Function returned the expected value.');
     $this->assertToolkitOperationsCalled(array('crop'));
 
     // Check the parameters.
@@ -126,7 +128,7 @@ function testCrop() {
    * Test the image_desaturate() function.
    */
   function testDesaturate() {
-    $this->assertTrue(image_desaturate($this->image), 'Function returned the expected value.');
+    $this->assertTrue($this->image->desaturate(), 'Function returned the expected value.');
     $this->assertToolkitOperationsCalled(array('desaturate'));
 
     // Check the parameters.
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 80b1588..9918553 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitTestBase.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitTestBase.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\system\Tests\Image;
 
+use Drupal\Core\Image\ImageFile;
 use Drupal\simpletest\WebTestBase;
 use Drupal\system\Plugin\ImageToolkitManager;
 use stdClass;
@@ -23,8 +24,19 @@
    */
   public static $modules = array('image_test');
 
+  /**
+   * @var \Drupal\system\Plugin\ImageToolkitInterface
+   */
   protected $toolkit;
+
+  /**
+   * @var string
+   */
   protected $file;
+
+  /**
+   * @var \Drupal\Core\Image\ImageFile
+   */
   protected $image;
 
   function setUp() {
@@ -38,12 +50,12 @@ function setUp() {
     $file = current($this->drupalGetTestFiles('image'));
     $this->file = $file->uri;
 
-    // Setup a dummy image to work with, this replicate image_load() so we
-    // can avoid calling it.
-    $this->image = new stdClass();
-    $this->image->source = $this->file;
-    $this->image->info = image_get_info($this->file);
-    $this->image->toolkit = $this->toolkit;
+    // Setup a dummy image to work with.
+    $this->image = new ImageFile($this->file);
+    // Trigger processing with the default toolkit before setting a custom one.
+    $this->image->getExtension();
+    // Set the custom toolkit.
+    $this->image->setToolkit($this->toolkit);
 
     // Clear out any hook calls.
     $this->imageTestReset();
diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php
index d395462..6d2b310 100644
--- a/core/modules/system/system.api.php
+++ b/core/modules/system/system.api.php
@@ -2149,8 +2149,8 @@ function hook_file_download($uri) {
       return -1;
     }
     else {
-      $info = image_get_info($uri);
-      return array('Content-Type' => $info['mime_type']);
+      $image = new \Drupal\Core\Image\ImageFile($uri);
+      return array('Content-Type' => $image->getMimeType());
     }
   }
 }
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 12ebae7..8203b18 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
@@ -10,6 +10,7 @@
 use Drupal\Component\Plugin\PluginBase;
 use Drupal\Component\Annotation\Plugin;
 use Drupal\Core\Annotation\Translation;
+use Drupal\Core\Image\ImageFile;
 use Drupal\system\Plugin\ImageToolkitInterface;
 
 /**
@@ -38,7 +39,7 @@ public function settingsFormSubmit($form, &$form_state) {}
   /**
    * Implements \Drupal\system\Plugin\ImageToolkitInterface::getInfo().
    */
-  public function getInfo($image) {
+  public function getInfo(ImageFile $image) {
     $this->logCall('get_info', array($image));
     return array();
   }
@@ -46,7 +47,7 @@ public function getInfo($image) {
   /**
    * Implements \Drupal\system\Plugin\ImageToolkitInterface::load().
    */
-  public function load($image) {
+  public function load(ImageFile $image) {
     $this->logCall('load', array($image));
     return $image;
   }
@@ -54,7 +55,7 @@ public function load($image) {
   /**
    * Implements \Drupal\system\Plugin\ImageToolkitInterface::save().
    */
-  public function save($image, $destination) {
+  public function save(ImageFile $image, $destination) {
     $this->logCall('save', array($image, $destination));
     // Return false so that image_save() doesn't try to chmod the destination
     // file that we didn't bother to create.
@@ -64,7 +65,7 @@ public function save($image, $destination) {
   /**
    * Implements \Drupal\system\Plugin\ImageToolkitInterface::crop().
    */
-  public function crop($image, $x, $y, $width, $height) {
+  public function crop(ImageFile $image, $x, $y, $width, $height) {
     $this->logCall('crop', array($image, $x, $y, $width, $height));
     return TRUE;
   }
@@ -72,7 +73,7 @@ public function crop($image, $x, $y, $width, $height) {
   /**
    * Implements \Drupal\system\Plugin\ImageToolkitInterface::resize().
    */
-  public function resize($image, $width, $height) {
+  public function resize(ImageFile $image, $width, $height) {
     $this->logCall('resize', array($image, $width, $height));
     return TRUE;
   }
@@ -80,7 +81,7 @@ public function resize($image, $width, $height) {
   /**
    * Implements \Drupal\system\Plugin\ImageToolkitInterface::rotate().
    */
-  public function rotate($image, $degrees, $background = NULL) {
+  public function rotate(ImageFile $image, $degrees, $background = NULL) {
     $this->logCall('rotate', array($image, $degrees, $background));
     return TRUE;
   }
@@ -88,7 +89,7 @@ public function rotate($image, $degrees, $background = NULL) {
   /**
    * Implements \Drupal\system\Plugin\ImageToolkitInterface::desaturate().
    */
-  public function desaturate($image) {
+  public function desaturate(ImageFile $image) {
     $this->logCall('desaturate', array($image));
     return TRUE;
   }
