diff --git a/core/includes/image.inc b/core/includes/image.inc
index f6ae7f1..6f0195b 100644
--- a/core/includes/image.inc
+++ b/core/includes/image.inc
@@ -1,5 +1,6 @@
 <?php
 
+use Drupal\Core\Image\GDToolkit;
 /**
  * @file
  * API for manipulating images.
@@ -39,15 +40,15 @@
  * @return
  *   An array with the toolkit names as keys and the descriptions as values.
  */
-function image_get_available_toolkits() {
+function image_get_available_toolkits($key = NULL) {
   // hook_image_toolkits returns an array of toolkit names.
   $toolkits = module_invoke_all('image_toolkits');
 
   $output = array();
-  foreach ($toolkits as $name => $info) {
+  foreach ($toolkits as $class) {
     // Only allow modules that aren't marked as unavailable.
-    if ($info['available']) {
-      $output[$name] = $info['title'];
+    if (call_user_func($class . '::checkAvailable')) {
+      $output[$class] = call_user_func($class . '::getDescription');
     }
   }
 
@@ -58,46 +59,31 @@ function image_get_available_toolkits() {
  * Retrieve the name of the currently used toolkit.
  *
  * @return
- *   String containing the name of the selected toolkit, or FALSE on error.
+ *   Object of the default toolkit, or FALSE on error.
  */
-function image_get_toolkit() {
-  static $toolkit;
+function image_get_toolkit($toolkit_class = NULL) {
+  static $toolkit = array();
 
-  if (!isset($toolkit)) {
+  if (empty($toolkit_class)) {
+    $toolkit_class = config('system.image')->get('toolkit');
+  }
+
+  if (!isset($toolkit[$toolkit_class])) {
     $toolkits = image_get_available_toolkits();
-    $toolkit = variable_get('image_toolkit', 'gd');
-    if (!isset($toolkits[$toolkit]) || !function_exists('image_' . $toolkit . '_load')) {
+
+    if (!isset($toolkits[$toolkit_class]) || !class_exists($toolkit_class)) {
       // The selected toolkit isn't available so return the first one found. If
       // none are available this will return FALSE.
       reset($toolkits);
-      $toolkit = key($toolkits);
+      $toolkit_class = key($toolkits);
     }
-  }
 
-  return $toolkit;
-}
-
-/**
- * Invokes the given method using the currently selected toolkit.
- *
- * @param $method
- *   A string containing the method to invoke.
- * @param $image
- *   An image object returned by image_load().
- * @param $params
- *   An optional array of parameters to pass to the toolkit method.
- *
- * @return
- *   Mixed values (typically Boolean indicating successful operation).
- */
-function image_toolkit_invoke($method, stdClass $image, array $params = array()) {
-  $function = 'image_' . $image->toolkit . '_' . $method;
-  if (function_exists($function)) {
-    array_unshift($params, $image);
-    return call_user_func_array($function, $params);
+    if ($toolkit_class) {
+      $toolkit[$toolkit_class] = new $toolkit_class();
+    }
   }
-  watchdog('image', 'The selected image handling toolkit %toolkit can not correctly process %function.', array('%toolkit' => $image->toolkit, '%function' => $function), WATCHDOG_ERROR);
-  return FALSE;
+
+  return $toolkit[$toolkit_class];
 }
 
 /**
@@ -110,7 +96,7 @@ function image_toolkit_invoke($method, stdClass $image, array $params = array())
  * @param $filepath
  *   String specifying the path of the image file.
  * @param $toolkit
- *   An optional image toolkit name to override the default.
+ *   An optional image toolkit object to override the default.
  *
  * @return
  *   FALSE, if the file could not be found or is not an image. Otherwise, a
@@ -133,8 +119,8 @@ function image_get_info($filepath, $toolkit = FALSE) {
   if ($toolkit) {
     $image = new stdClass();
     $image->source = $filepath;
-    $image->toolkit = $toolkit;
-    $details = image_toolkit_invoke('get_info', $image);
+    $image->toolkit = get_class($toolkit);
+    $details = $toolkit->getInfo($image);
     if (isset($details) && is_array($details)) {
       $details['file_size'] = filesize($filepath);
     }
@@ -277,10 +263,11 @@ function image_scale(stdClass $image, $width = NULL, $height = NULL, $upscale =
  * @see image_gd_resize()
  */
 function image_resize(stdClass $image, $width, $height) {
-  $width = (int) round($width);
-  $height = (int) round($height);
-
-  return image_toolkit_invoke('resize', $image, array($width, $height));
+  $toolkit = image_get_toolkit($image->toolkit);
+  if ($toolkit) {
+    return $toolkit->resize($image, (int) round($width), (int) round($height));
+  }
+  return FALSE;
 }
 
 /**
@@ -304,7 +291,11 @@ function image_resize(stdClass $image, $width, $height) {
  * @see image_gd_rotate()
  */
 function image_rotate(stdClass $image, $degrees, $background = NULL) {
-  return image_toolkit_invoke('rotate', $image, array($degrees, $background));
+  $toolkit = image_get_toolkit($image->toolkit);
+  if ($toolkit) {
+    return $toolkit->rotate($image, $degrees, $background);
+  }
+  return FALSE;
 }
 
 /**
@@ -333,10 +324,11 @@ function image_crop(stdClass $image, $x, $y, $width, $height) {
   if (empty($height)) $height = $width / $aspect;
   if (empty($width)) $width = $height * $aspect;
 
-  $width = (int) round($width);
-  $height = (int) round($height);
-
-  return image_toolkit_invoke('crop', $image, array($x, $y, $width, $height));
+  $toolkit = image_get_toolkit($image->toolkit);
+  if ($toolkit) {
+    return $toolkit->crop($image, $x, $y, (int) round($width), (int) round($height));
+  }
+  return FALSE;
 }
 
 /**
@@ -352,7 +344,11 @@ function image_crop(stdClass $image, $x, $y, $width, $height) {
  * @see image_gd_desaturate()
  */
 function image_desaturate(stdClass $image) {
-  return image_toolkit_invoke('desaturate', $image);
+  $toolkit = image_get_toolkit($image->toolkit);
+  if ($toolkit) {
+    return $toolkit->desaturate($image);
+  }
+  return FALSE;
 }
 
 
@@ -390,8 +386,8 @@ function image_load($file, $toolkit = FALSE) {
     $image->source = $file;
     $image->info = image_get_info($file, $toolkit);
     if (isset($image->info) && is_array($image->info)) {
-      $image->toolkit = $toolkit;
-      if (image_toolkit_invoke('load', $image)) {
+      $image->toolkit = get_class($toolkit);
+      if ($toolkit->load($image)) {
         return $image;
       }
     }
@@ -419,13 +415,16 @@ function image_save(stdClass $image, $destination = NULL) {
   if (empty($destination)) {
     $destination = $image->source;
   }
-  if ($return = image_toolkit_invoke('save', $image, array($destination))) {
-    // Clear the cached file size and refresh the image information.
-    clearstatcache();
-    $image->info = image_get_info($destination, $image->toolkit);
+  $toolkit = image_get_toolkit($image->toolkit);
+  if ($toolkit) {
+    if ($return = $toolkit->save($image, $destination)) {
+      // Clear the cached file size and refresh the image information.
+      clearstatcache();
+      $image->info = image_get_info($destination, $toolkit);
 
-    if (drupal_chmod($destination)) {
-      return $return;
+      if (drupal_chmod($destination)) {
+        return $return;
+      }
     }
   }
   return FALSE;
diff --git a/core/lib/Drupal/Core/Image/GDToolkit.php b/core/lib/Drupal/Core/Image/GDToolkit.php
new file mode 100644
index 0000000..76b1ba3
--- /dev/null
+++ b/core/lib/Drupal/Core/Image/GDToolkit.php
@@ -0,0 +1,300 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Image\GDToolkit.
+ */
+
+namespace Drupal\Core\Image;
+use \stdClass;
+
+/**
+ * Defines the GD2 toolkit for image manipulation within Drupal.
+ *
+ * @addtogroup image
+ */
+class GDToolkit implements ToolkitInterface {
+
+  /**
+   * Implements Drupal\Core\Image\ToolkitInterface::settingsForm().
+   */
+  function settingsForm() {
+    if ($this->checkAvailable()) {
+      $form['status'] = array(
+        '#markup' => t('The GD toolkit is installed and working properly.')
+      );
+
+      $form['image_jpeg_quality'] = array(
+        '#type' => 'number',
+        '#title' => t('JPEG quality'),
+        '#description' => t('Define the image quality for JPEG manipulations. Ranges from 0 to 100. Higher values mean better image quality but bigger files.'),
+        '#min' => 0,
+        '#max' => 100,
+        '#default_value' => config('system.image.gd')->get('jpeg_quality'),
+        '#field_suffix' => t('%'),
+      );
+      return $form;
+    }
+    else {
+      form_set_error('image_toolkit', t('The GD image toolkit requires that the GD module for PHP be installed and configured properly. For more information see <a href="@url">PHP\'s image documentation</a>.', array('@url' => 'http://php.net/image')));
+      return FALSE;
+    }
+  }
+
+  /**
+   * Implements Drupal\Core\Image\ToolkitInterface::settingsFormSubmit().
+   */
+  function settingsFormSubmit($form, &$form_state) {
+    config('system.image.gd')
+      ->set('jpeg_quality', $form_state['values']['Drupal\Core\Image\GDToolkit']['image_jpeg_quality'])
+      ->save();
+  }
+
+  /**
+   * Implements Drupal\Core\Image\ToolkitInterface::resize().
+   */
+  function resize(stdClass $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'])) {
+      return FALSE;
+    }
+
+    imagedestroy($image->resource);
+    // Update image object.
+    $image->resource = $res;
+    $image->info['width'] = $width;
+    $image->info['height'] = $height;
+    return TRUE;
+  }
+
+  /**
+   * Implements Drupal\Core\Image\ToolkitInterface::rotate().
+   */
+  function rotate(stdClass $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));
+      return FALSE;
+    }
+
+    $width = $image->info['width'];
+    $height = $image->info['height'];
+
+    // Convert the hexadecimal background value to a color index value.
+    if (isset($background)) {
+      $rgb = array();
+      for ($i = 16; $i >= 0; $i -= 8) {
+        $rgb[] = (($background >> $i) & 0xFF);
+      }
+      $background = imagecolorallocatealpha($image->resource, $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);
+
+      // If no transparent colors, use white.
+      if ($background == 0) {
+        $background = imagecolorallocatealpha($image->resource, 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 ($transparent_index != 0) {
+        $transparent_gif_color = imagecolorsforindex($image->resource, $transparent_index);
+      }
+    }
+
+    $image->resource = imagerotate($image->resource, 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);
+    }
+
+    $image->info['width'] = imagesx($image->resource);
+    $image->info['height'] = imagesy($image->resource);
+    return TRUE;
+  }
+
+  /**
+   * Implements Drupal\Core\Image\ToolkitInterface::crop().
+   */
+  function crop(stdClass $image, $x, $y, $width, $height) {
+    $res = $this->createTmp($image, $width, $height);
+
+    if (!imagecopyresampled($res, $image->resource, 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;
+    return TRUE;
+  }
+
+  /**
+   * Implements Drupal\Core\Image\ToolkitInterface::desaturate().
+   */
+  function desaturate(stdClass $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));
+      return FALSE;
+    }
+
+    return imagefilter($image->resource, IMG_FILTER_GRAYSCALE);
+  }
+
+  /**
+   * Implements Drupal\Core\Image\ToolkitInterface::load().
+   */
+  function load(stdClass $image) {
+    $extension = str_replace('jpg', 'jpeg', $image->info['extension']);
+    $function = 'imagecreatefrom' . $extension;
+    if (function_exists($function) && $image->resource = $function($image->source)) {
+      if (!imageistruecolor($image->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;
+      }
+      return (bool) $image->resource;
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * Implements Drupal\Core\Image\ToolkitInterface::save().
+   */
+  function save(stdClass $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)) {
+      // If destination is not local, save image to temporary local file.
+      $local_wrappers = file_get_stream_wrappers(STREAM_WRAPPERS_LOCAL);
+      if (!isset($local_wrappers[$scheme])) {
+        $permanent_destination = $destination;
+        $destination = drupal_tempnam('temporary://', 'gd_');
+      }
+      // Convert stream wrapper URI to normal path.
+      $destination = drupal_realpath($destination);
+    }
+
+    $extension = str_replace('jpg', 'jpeg', $image->info['extension']);
+    $function = 'image' . $extension;
+    if (!function_exists($function)) {
+      return FALSE;
+    }
+    if ($extension == 'jpeg') {
+      $success = $function($image->resource, $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);
+      }
+      $success = $function($image->resource, $destination);
+    }
+    // Move temporary local file to remote destination.
+    if (isset($permanent_destination) && $success) {
+      return (bool) file_unmanaged_move($destination, $permanent_destination, FILE_EXISTS_REPLACE);
+    }
+    return $success;
+  }
+
+  /**
+   * Implements Drupal\Core\Image\ToolkitInterface::getInfo().
+   */
+  function getInfo(stdClass $image) {
+    $details = FALSE;
+    $data = getimagesize($image->source);
+
+    if (isset($data) && is_array($data)) {
+      $extensions = array('1' => 'gif', '2' => 'jpg', '3' => 'png');
+      $extension = isset($extensions[$data[2]]) ?  $extensions[$data[2]] : '';
+      $details = array(
+        'width'     => $data[0],
+        'height'    => $data[1],
+        'extension' => $extension,
+        'mime_type' => $data['mime'],
+      );
+    }
+
+    return $details;
+  }
+
+  /**
+   * Create a truecolor image preserving transparency from a provided image.
+   *
+   * @param $image
+   *   An image object.
+   * @param $width
+   *   The new width of the new image, in pixels.
+   * @param $height
+   *   The new height of the new image, in pixels.
+   * @return
+   *   A GD image handle.
+   */
+  function createTmp(stdClass $image, $width, $height) {
+    $res = imagecreatetruecolor($width, $height);
+
+    if ($image->info['extension'] == 'gif') {
+      // Grab transparent color index from image resource.
+      $transparent = imagecolortransparent($image->resource);
+
+      if ($transparent >= 0) {
+        // The original must have a transparent color, allocate to the new image.
+        $transparent_color = imagecolorsforindex($image->resource, $transparent);
+        $transparent = imagecolorallocate($res, $transparent_color['red'], $transparent_color['green'], $transparent_color['blue']);
+
+        // Flood with our new transparent color.
+        imagefill($res, 0, 0, $transparent);
+        imagecolortransparent($res, $transparent);
+      }
+    }
+    elseif ($image->info['extension'] == 'png') {
+      imagealphablending($res, FALSE);
+      $transparency = imagecolorallocatealpha($res, 0, 0, 0, 127);
+      imagefill($res, 0, 0, $transparency);
+      imagealphablending($res, TRUE);
+      imagesavealpha($res, TRUE);
+    }
+    else {
+      imagefill($res, 0, 0, imagecolorallocate($res, 255, 255, 255));
+    }
+
+    return $res;
+  }
+
+  /**
+   * Implements Drupal\Core\Image\ToolkitInterface::checkAvailable().
+   */
+  public static function checkAvailable() {
+    if ($check = get_extension_funcs('gd')) {
+      if (in_array('imagegd2', $check)) {
+        // GD2 support is available.
+        return TRUE;
+      }
+    }
+    return FALSE;
+  }
+
+  /**
+   * Implements Drupal\Core\Image\ToolkitInterface::getDescription().
+   */
+  public static function getDescription() {
+    return t('GD2 image manipulation toolkit');
+  }
+}
diff --git a/core/lib/Drupal/Core/Image/ToolkitInterface.php b/core/lib/Drupal/Core/Image/ToolkitInterface.php
new file mode 100644
index 0000000..af1c563
--- /dev/null
+++ b/core/lib/Drupal/Core/Image/ToolkitInterface.php
@@ -0,0 +1,163 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Image\ToolkitInterface.
+ */
+
+namespace Drupal\Core\Image;
+use \stdClass;
+
+/**
+ * Defines an interface for image toolkits.
+ *
+ * Classes implementing this interface allow...
+ */
+interface ToolkitInterface {
+
+  /**
+   * Retrieve toolkit's settings form.
+   *
+   * @see system_image_toolkit_settings()
+   */
+  function settingsForm();
+
+  /**
+   * Form submission handler for toolkit's settings form.
+   *
+   * @see system_image_toolkit_settings_submit()
+   */
+  function settingsFormSubmit($form, &$form_state);
+
+  /**
+   * Scale an image to the specified size.
+   *
+   * @param $image
+   *   An image object. The $image->resource, $image->info['width'], and
+   *   $image->info['height'] values will be modified by this call.
+   * @param $width
+   *   The new width of the resized image, in pixels.
+   * @param $height
+   *   The new height of the resized image, in pixels.
+   * @return
+   *   TRUE or FALSE, based on success.
+   *
+   * @see image_resize()
+   */
+  function resize(stdClass $image, $width, $height);
+
+  /**
+   * Rotate an image the given number of degrees.
+   *
+   * @param $image
+   *   An image object. The $image->resource, $image->info['width'], and
+   *   $image->info['height'] values will be modified by this call.
+   * @param $degrees
+   *   The number of (clockwise) degrees to rotate the image.
+   * @param $background
+   *   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
+   *   TRUE or FALSE, based on success.
+   *
+   * @see image_rotate()
+   */
+  function rotate(stdClass $image, $degrees, $background = NULL);
+
+  /**
+   * Crop an image.
+   *
+   * @param $image
+   *   An image object. The $image->resource, $image->info['width'], and
+   *   $image->info['height'] values will be modified by this call.
+   * @param $x
+   *   The starting x offset at which to start the crop, in pixels.
+   * @param $y
+   *   The starting y offset at which to start the crop, in pixels.
+   * @param $width
+   *   The width of the cropped area, in pixels.
+   * @param $height
+   *   The height of the cropped area, in pixels.
+   * @return
+   *   TRUE or FALSE, based on success.
+   *
+   * @see image_crop()
+   */
+  function crop(stdClass $image, $x, $y, $width, $height);
+
+  /**
+   * Convert an image resource to grayscale.
+   *
+   * Note that transparent GIFs loose transparency when desaturated.
+   *
+   * @param $image
+   *   An image object. The $image->resource value will be modified by this call.
+   * @return
+   *   TRUE or FALSE, based on success.
+   *
+   * @see image_desaturate()
+   */
+  function desaturate(stdClass $image);
+
+  /**
+   * Create an image resource from a file.
+   *
+   * @param $image
+   *   An image object. The $image->resource value will populated by this call.
+   * @return
+   *   TRUE or FALSE, based on success.
+   *
+   * @see image_load()
+   */
+  function load(stdClass $image);
+
+  /**
+   * Write an image resource to a destination file.
+   *
+   * @param $image
+   *   An image object.
+   * @param $destination
+   *   A string file URI or path where the image should be saved.
+   * @return
+   *   TRUE or FALSE, based on success.
+   *
+   * @see image_save()
+   */
+  function save(stdClass $image, $destination);
+
+  /**
+   * Get details about an image.
+   *
+   * @param $image
+   *   An image object.
+   * @return
+   *   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').
+   *
+   * @see image_get_info()
+   */
+  function getInfo(stdClass $image);
+
+  /**
+   * Verify Image Toolkit is set up correctly.
+   *
+   * @return
+   *   A boolean indicating if the GD toolkit is available on this machine.
+   */
+  static function checkAvailable();
+
+  /**
+   * Get a one-line description of the Image Toolkit
+   *
+   * @return
+   *   A transalted string describing the Image Toolkit.
+   */
+  static function getDescription();
+}
diff --git a/core/modules/image/lib/Drupal/image/Tests/ImageEffectsTest.php b/core/modules/image/lib/Drupal/image/Tests/ImageEffectsTest.php
index 0456162..69a1e09 100644
--- a/core/modules/image/lib/Drupal/image/Tests/ImageEffectsTest.php
+++ b/core/modules/image/lib/Drupal/image/Tests/ImageEffectsTest.php
@@ -23,7 +23,7 @@ class ImageEffectsTest extends ToolkitTestBase {
   }
 
   function setUp() {
-    parent::setUp('image_test');
+    parent::setUp();
     module_load_include('inc', 'image', 'image.effects');
   }
 
diff --git a/core/modules/system/config/system.image.gd.yml b/core/modules/system/config/system.image.gd.yml
new file mode 100644
index 0000000..fbc379f
--- /dev/null
+++ b/core/modules/system/config/system.image.gd.yml
@@ -0,0 +1 @@
+jpeg_quality: '75'
diff --git a/core/modules/system/config/system.image.yml b/core/modules/system/config/system.image.yml
new file mode 100644
index 0000000..fb8ff43
--- /dev/null
+++ b/core/modules/system/config/system.image.yml
@@ -0,0 +1 @@
+toolkit: Drupal\Core\Image\GDToolkit
diff --git a/core/modules/system/image.gd.inc b/core/modules/system/image.gd.inc
deleted file mode 100644
index f6f12ae..0000000
--- a/core/modules/system/image.gd.inc
+++ /dev/null
@@ -1,367 +0,0 @@
-<?php
-
-/**
- * @file
- * GD2 toolkit for image manipulation within Drupal.
- */
-
-/**
- * @addtogroup image
- * @{
- */
-
-/**
- * Retrieve settings for the GD2 toolkit.
- */
-function image_gd_settings() {
-  if (image_gd_check_settings()) {
-    $form['status'] = array(
-      '#markup' => t('The GD toolkit is installed and working properly.')
-    );
-
-    $form['image_jpeg_quality'] = array(
-      '#type' => 'number',
-      '#title' => t('JPEG quality'),
-      '#description' => t('Define the image quality for JPEG manipulations. Ranges from 0 to 100. Higher values mean better image quality but bigger files.'),
-      '#min' => 0,
-      '#max' => 100,
-      '#default_value' => variable_get('image_jpeg_quality', 75),
-      '#field_suffix' => t('%'),
-    );
-
-    return $form;
-  }
-  else {
-    form_set_error('image_toolkit', t('The GD image toolkit requires that the GD module for PHP be installed and configured properly. For more information see <a href="@url">PHP\'s image documentation</a>.', array('@url' => 'http://php.net/image')));
-    return FALSE;
-  }
-}
-
-/**
- * Verify GD2 settings (that the right version is actually installed).
- *
- * @return
- *   A boolean indicating if the GD toolkit is available on this machine.
- */
-function image_gd_check_settings() {
-  if ($check = get_extension_funcs('gd')) {
-    if (in_array('imagegd2', $check)) {
-      // GD2 support is available.
-      return TRUE;
-    }
-  }
-  return FALSE;
-}
-
-/**
- * Scale an image to the specified size using GD.
- *
- * @param $image
- *   An image object. The $image->resource, $image->info['width'], and
- *   $image->info['height'] values will be modified by this call.
- * @param $width
- *   The new width of the resized image, in pixels.
- * @param $height
- *   The new height of the resized image, in pixels.
- * @return
- *   TRUE or FALSE, based on success.
- *
- * @see image_resize()
- */
-function image_gd_resize(stdClass $image, $width, $height) {
-  $res = image_gd_create_tmp($image, $width, $height);
-
-  if (!imagecopyresampled($res, $image->resource, 0, 0, 0, 0, $width, $height, $image->info['width'], $image->info['height'])) {
-    return FALSE;
-  }
-
-  imagedestroy($image->resource);
-  // Update image object.
-  $image->resource = $res;
-  $image->info['width'] = $width;
-  $image->info['height'] = $height;
-  return TRUE;
-}
-
-/**
- * Rotate an image the given number of degrees.
- *
- * @param $image
- *   An image object. The $image->resource, $image->info['width'], and
- *   $image->info['height'] values will be modified by this call.
- * @param $degrees
- *   The number of (clockwise) degrees to rotate the image.
- * @param $background
- *   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
- *   TRUE or FALSE, based on success.
- *
- * @see image_rotate()
- */
-function image_gd_rotate(stdClass $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));
-    return FALSE;
-  }
-
-  $width = $image->info['width'];
-  $height = $image->info['height'];
-
-  // Convert the hexadecimal background value to a color index value.
-  if (isset($background)) {
-    $rgb = array();
-    for ($i = 16; $i >= 0; $i -= 8) {
-      $rgb[] = (($background >> $i) & 0xFF);
-    }
-    $background = imagecolorallocatealpha($image->resource, $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);
-
-    // If no transparent colors, use white.
-    if ($background == 0) {
-      $background = imagecolorallocatealpha($image->resource, 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 ($transparent_index != 0) {
-      $transparent_gif_color = imagecolorsforindex($image->resource, $transparent_index);
-    }
-  }
-
-  $image->resource = imagerotate($image->resource, 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);
-  }
-
-  $image->info['width'] = imagesx($image->resource);
-  $image->info['height'] = imagesy($image->resource);
-  return TRUE;
-}
-
-/**
- * Crop an image using the GD toolkit.
- *
- * @param $image
- *   An image object. The $image->resource, $image->info['width'], and
- *   $image->info['height'] values will be modified by this call.
- * @param $x
- *   The starting x offset at which to start the crop, in pixels.
- * @param $y
- *   The starting y offset at which to start the crop, in pixels.
- * @param $width
- *   The width of the cropped area, in pixels.
- * @param $height
- *   The height of the cropped area, in pixels.
- * @return
- *   TRUE or FALSE, based on success.
- *
- * @see image_crop()
- */
-function image_gd_crop(stdClass $image, $x, $y, $width, $height) {
-  $res = image_gd_create_tmp($image, $width, $height);
-
-  if (!imagecopyresampled($res, $image->resource, 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;
-  return TRUE;
-}
-
-/**
- * Convert an image resource to grayscale.
- *
- * Note that transparent GIFs loose transparency when desaturated.
- *
- * @param $image
- *   An image object. The $image->resource value will be modified by this call.
- * @return
- *   TRUE or FALSE, based on success.
- *
- * @see image_desaturate()
- */
-function image_gd_desaturate(stdClass $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));
-    return FALSE;
-  }
-
-  return imagefilter($image->resource, IMG_FILTER_GRAYSCALE);
-}
-
-/**
- * GD helper function to create an image resource from a file.
- *
- * @param $image
- *   An image object. The $image->resource value will populated by this call.
- * @return
- *   TRUE or FALSE, based on success.
- *
- * @see image_load()
- */
-function image_gd_load(stdClass $image) {
-  $extension = str_replace('jpg', 'jpeg', $image->info['extension']);
-  $function = 'imagecreatefrom' . $extension;
-  if (function_exists($function) && $image->resource = $function($image->source)) {
-    if (!imageistruecolor($image->resource)) {
-      // Convert indexed images to true color, so that filters work
-      // correctly and don't result in unnecessary dither.
-      $new_image = image_gd_create_tmp($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;
-    }
-    return (bool) $image->resource;
-  }
-
-  return FALSE;
-}
-
-/**
- * GD helper to write an image resource to a destination file.
- *
- * @param $image
- *   An image object.
- * @param $destination
- *   A string file URI or path where the image should be saved.
- * @return
- *   TRUE or FALSE, based on success.
- *
- * @see image_save()
- */
-function image_gd_save(stdClass $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)) {
-    // If destination is not local, save image to temporary local file.
-    $local_wrappers = file_get_stream_wrappers(STREAM_WRAPPERS_LOCAL);
-    if (!isset($local_wrappers[$scheme])) {
-      $permanent_destination = $destination;
-      $destination = drupal_tempnam('temporary://', 'gd_');
-    }
-    // Convert stream wrapper URI to normal path.
-    $destination = drupal_realpath($destination);
-  }
-
-  $extension = str_replace('jpg', 'jpeg', $image->info['extension']);
-  $function = 'image' . $extension;
-  if (!function_exists($function)) {
-    return FALSE;
-  }
-  if ($extension == 'jpeg') {
-    $success = $function($image->resource, $destination, variable_get('image_jpeg_quality', 75));
-  }
-  else {
-    // Always save PNG images with full transparency.
-    if ($extension == 'png') {
-      imagealphablending($image->resource, FALSE);
-      imagesavealpha($image->resource, TRUE);
-    }
-    $success = $function($image->resource, $destination);
-  }
-  // Move temporary local file to remote destination.
-  if (isset($permanent_destination) && $success) {
-    return (bool) file_unmanaged_move($destination, $permanent_destination, FILE_EXISTS_REPLACE);
-  }
-  return $success;
-}
-
-/**
- * Create a truecolor image preserving transparency from a provided image.
- *
- * @param $image
- *   An image object.
- * @param $width
- *   The new width of the new image, in pixels.
- * @param $height
- *   The new height of the new image, in pixels.
- * @return
- *   A GD image handle.
- */
-function image_gd_create_tmp(stdClass $image, $width, $height) {
-  $res = imagecreatetruecolor($width, $height);
-
-  if ($image->info['extension'] == 'gif') {
-    // Grab transparent color index from image resource.
-    $transparent = imagecolortransparent($image->resource);
-
-    if ($transparent >= 0) {
-      // The original must have a transparent color, allocate to the new image.
-      $transparent_color = imagecolorsforindex($image->resource, $transparent);
-      $transparent = imagecolorallocate($res, $transparent_color['red'], $transparent_color['green'], $transparent_color['blue']);
-
-      // Flood with our new transparent color.
-      imagefill($res, 0, 0, $transparent);
-      imagecolortransparent($res, $transparent);
-    }
-  }
-  elseif ($image->info['extension'] == 'png') {
-    imagealphablending($res, FALSE);
-    $transparency = imagecolorallocatealpha($res, 0, 0, 0, 127);
-    imagefill($res, 0, 0, $transparency);
-    imagealphablending($res, TRUE);
-    imagesavealpha($res, TRUE);
-  }
-  else {
-    imagefill($res, 0, 0, imagecolorallocate($res, 255, 255, 255));
-  }
-
-  return $res;
-}
-
-/**
- * Get details about an image.
- *
- * @param $image
- *   An image object.
- * @return
- *   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').
- *
- * @see image_get_info()
- */
-function image_gd_get_info(stdClass $image) {
-  $details = FALSE;
-  $data = getimagesize($image->source);
-
-  if (isset($data) && is_array($data)) {
-    $extensions = array('1' => 'gif', '2' => 'jpg', '3' => 'png');
-    $extension = isset($extensions[$data[2]]) ?  $extensions[$data[2]] : '';
-    $details = array(
-      'width'     => $data[0],
-      'height'    => $data[1],
-      'extension' => $extension,
-      'mime_type' => $data['mime'],
-    );
-  }
-
-  return $details;
-}
-
-/**
- * @} End of "addtogroup image".
- */
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 611126d..756105f 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitGdTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitGdTest.php
@@ -8,7 +8,7 @@
 namespace Drupal\system\Tests\Image;
 
 use Drupal\simpletest\WebTestBase;
-
+use Drupal\Core\Image\GDToolkit;
 /**
  * Test the core GD image manipulation functions.
  */
@@ -35,8 +35,7 @@ class ToolkitGdTest extends WebTestBase {
   }
 
   protected function checkRequirements() {
-    image_get_available_toolkits();
-    if (!function_exists('image_gd_check_settings') || !image_gd_check_settings()) {
+    if (!GDToolkit::checkAvailable()) {
       return array(
         'Image manipulations for the GD toolkit cannot run because the GD toolkit is not available.',
       );
@@ -204,7 +203,7 @@ class ToolkitGdTest extends WebTestBase {
     foreach ($files as $file) {
       foreach ($operations as $op => $values) {
         // Load up a fresh image.
-        $image = image_load(drupal_get_path('module', 'simpletest') . '/files/' . $file, 'gd');
+        $image = image_load(drupal_get_path('module', 'simpletest') . '/files/' . $file, new GDToolkit());
         if (!$image) {
           $this->fail(t('Could not load image %file.', array('%file' => $file)));
           continue 2;
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 091fc5a..1c0fc5f 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitTest.php
@@ -25,8 +25,8 @@ class ToolkitTest extends ToolkitTestBase {
    */
   function testGetAvailableToolkits() {
     $toolkits = image_get_available_toolkits();
-    $this->assertTrue(isset($toolkits['test']), t('The working toolkit was returned.'));
-    $this->assertFalse(isset($toolkits['broken']), t('The toolkit marked unavailable was not returned'));
+    $this->assertTrue(isset($toolkits['Drupal\image_test\TestToolkit']), t('The working toolkit was returned.'));
+    $this->assertFalse(isset($toolkits['Drupal\image_test\BrokenToolkit']), t('The toolkit marked unavailable was not returned'));
     $this->assertToolkitOperationsCalled(array());
   }
 
@@ -34,9 +34,10 @@ class ToolkitTest extends ToolkitTestBase {
    * Test the image_load() function.
    */
   function testLoad() {
-    $image = image_load($this->file, $this->toolkit);
+    $toolkit = image_get_toolkit($this->toolkit);
+    $image = image_load($this->file, $toolkit);
     $this->assertTrue(is_object($image), t('Returned an object.'));
-    $this->assertEqual($this->toolkit, $image->toolkit, t('Image had toolkit set.'));
+    $this->assertEqual($this->toolkit, $image->toolkit);
     $this->assertToolkitOperationsCalled(array('load', 'get_info'));
   }
 
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 477475b..0fdf55d 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitTestBase.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Image/ToolkitTestBase.php
@@ -19,10 +19,10 @@ abstract class ToolkitTestBase extends WebTestBase {
   protected $image;
 
   function setUp() {
-    parent::setUp('image_test');
+    parent::setUp(array('image', 'image_test'));
 
     // Use the image_test.module's test toolkit.
-    $this->toolkit = 'test';
+    $this->toolkit = 'Drupal\image_test\TestToolkit';
 
     // Pick a file for testing.
     $file = current($this->drupalGetTestFiles('image'));
diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc
index 2947245..858c7f1 100644
--- a/core/modules/system/system.admin.inc
+++ b/core/modules/system/system.admin.inc
@@ -1854,39 +1854,56 @@ function system_file_system_settings() {
  * Form builder; Configure site image toolkit usage.
  *
  * @ingroup forms
- * @see system_settings_form()
+ * @see system_image_toolkit_settings_submit()
  */
-function system_image_toolkit_settings() {
-  $toolkits_available = image_get_available_toolkits();
-  $current_toolkit = image_get_toolkit();
+function system_image_toolkit_settings($form, &$form_state) {
+  $config = config('system.image');
+  $toolkits_available = image_get_available_toolkits('title');
 
-  if (count($toolkits_available) == 0) {
-    variable_del('image_toolkit');
-    $form['image_toolkit_help'] = array(
-      '#markup' => t("No image toolkits were detected. Drupal includes support for <a href='!gd-link'>PHP's built-in image processing functions</a> but they were not detected on this system. You should consult your system administrator to have them enabled, or try using a third party toolkit.", array('gd-link' => url('http://php.net/gd'))),
-    );
-    return $form;
-  }
+  $form['image_toolkit'] = array(
+    '#type' => 'radios',
+    '#title' => t('Select an image processing toolkit'),
+    '#default_value' => $config->get('toolkit'),
+    '#options' => $toolkits_available,
+  );
 
-  if (count($toolkits_available) > 1) {
-    $form['image_toolkit'] = array(
-      '#type' => 'radios',
-      '#title' => t('Select an image processing toolkit'),
-      '#default_value' => variable_get('image_toolkit', $current_toolkit),
-      '#options' => $toolkits_available
-    );
+  // Get the toolkit settings forms.
+  foreach ($toolkits_available as $toolkit_class => $toolkit_title) {
+    $toolkit = new $toolkit_class();
+    if ($toolkit instanceof Drupal\Core\Image\ToolkitInterface) {
+      $form['image_toolkit_settings'][$toolkit_class] = array(
+        '#type' => 'fieldset',
+        '#title' => t('@toolkit settings', array('@toolkit' => $toolkit_title)),
+        '#collapsible' => TRUE,
+        '#collapsed' => TRUE,
+        '#tree' => TRUE,
+      );
+      $form['image_toolkit_settings'][$toolkit_class] += $toolkit->settingsForm();
+    }
   }
-  else {
-    variable_set('image_toolkit', key($toolkits_available));
+
+  $current_toolkit = get_class(image_get_toolkit());
+  if (isset($form['image_toolkit_settings'][$current_toolkit])) {
+    $form['image_toolkit_settings'][$current_toolkit]['#collapsed'] = FALSE;
   }
+  return system_config_form($form, $form_state);
+}
 
-  // Get the toolkit's settings form.
-  $function = 'image_' . $current_toolkit . '_settings';
-  if (function_exists($function)) {
-    $form['image_toolkit_settings'] = $function();
+/**
+ * Form submission handler for system_image_toolkit_settings().
+ */
+function system_image_toolkit_settings_submit($form, &$form_state) {
+  config('system.image')
+    ->set('toolkit', $form_state['values']['image_toolkit'])
+    ->save();
+
+  // Call the form submit handler for the current toolkit. Calling
+  // image_get_toolkit() does the necessary includes.
+  $current_toolkit = image_get_toolkit();
+  if ($current_toolkit instanceof \Drupal\Core\Image\ToolkitInterface) {
+    $current_toolkit->settingsFormSubmit($form, $form_state);
   }
 
-  return system_settings_form($form);
 }
 
 /**
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index 96c02c3..ec71725 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -1997,6 +1997,21 @@ function system_update_8014() {
   $config->save();
 }
 
+
+/**
+ * Moves image toolkit settings from variable to config.
+ *
+ * @ingroup config_upgrade
+ */
+function system_update_8015() {
+  update_variables_to_config('system.image', array(
+    'image_toolkit' => 'toolkit',
+  ));
+  update_variables_to_config('system.image.gd', array(
+    'image_jpeg_quality' => 'jpeg_quality',
+  ));
+}
+
 /**
  * @} End of "defgroup updates-7.x-to-8.x".
  * The next series of updates should start at 9000.
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index a530c30..166c5dc 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -3728,13 +3728,10 @@ function theme_system_compact_link() {
  * Implements hook_image_toolkits().
  */
 function system_image_toolkits() {
-  include_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'system') . '/' . 'image.gd.inc';
   return array(
-    'gd' => array(
-      'title' => t('GD2 image manipulation toolkit'),
-      'available' => function_exists('image_gd_check_settings') && image_gd_check_settings(),
-    ),
+    'Drupal\Core\Image\GDToolkit',
   );
+
 }
 
 /**
diff --git a/core/modules/system/tests/modules/image_test/image_test.module b/core/modules/system/tests/modules/image_test/image_test.module
index de640f0..4ad1850 100644
--- a/core/modules/system/tests/modules/image_test/image_test.module
+++ b/core/modules/system/tests/modules/image_test/image_test.module
@@ -10,14 +10,8 @@
  */
 function image_test_image_toolkits() {
   return array(
-    'test' => array(
-      'title' => t('A dummy toolkit that works'),
-      'available' => TRUE,
-    ),
-    'broken' => array(
-      'title' => t('A dummy toolkit that is "broken"'),
-      'available' => FALSE,
-    ),
+    'Drupal\image_test\TestToolkit',
+    'Drupal\image_test\BrokenToolkit',
   );
 }
 
diff --git a/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/BrokenToolkit.php b/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/BrokenToolkit.php
new file mode 100644
index 0000000..090a50a
--- /dev/null
+++ b/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/BrokenToolkit.php
@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\image_test\BrokenToolkit.
+ */
+
+namespace Drupal\image_test;
+
+/**
+ * Defines a Test toolkit for image manipulation within Drupal.
+ */
+class BrokenToolkit extends TestToolkit {
+  /**
+   * Implements Drupal\Core\Image\ToolkitInterface::checkAvailable().
+   */
+  static function checkAvailable() {
+    return FALSE;
+  }
+
+  /**
+   * Implements Drupal\Core\Image\ToolkitInterface::getDescription().
+   */
+  static function getDescription() {
+    return  t('A dummy toolkit that is "broken"');
+  }
+}
diff --git a/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/TestToolkit.php b/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/TestToolkit.php
new file mode 100644
index 0000000..09716a2
--- /dev/null
+++ b/core/modules/system/tests/modules/image_test/lib/Drupal/image_test/TestToolkit.php
@@ -0,0 +1,119 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\image_test\TestToolkit.
+ */
+
+namespace Drupal\image_test;
+use \stdClass;
+
+Use Drupal\Core\Image\ToolkitInterface;
+/**
+ * Defines a Test toolkit for image manipulation within Drupal.
+ */
+class TestToolkit implements ToolkitInterface {
+
+  /**
+   * Implements Drupal\Core\Image\ToolkitInterface::settingsForm().
+   */
+  function settingsForm() {
+    $this->_logCall('settings', array());
+    return array();
+  }
+
+  /**
+   * Implements Drupal\Core\Image\ToolkitInterface::settingsFormSubmit().
+   */
+  function settingsFormSubmit($form, &$form_state) {}
+
+  /**
+   * Implements Drupal\Core\Image\ToolkitInterface::getInfo().
+   */
+  function getInfo(stdClass $image) {
+    $this->_logCall('get_info', array($image));
+    return array();
+  }
+
+  /**
+   * Implements Drupal\Core\Image\ToolkitInterface::load().
+   */
+  function load(stdClass $image) {
+    $this->_logCall('load', array($image));
+    return $image;
+  }
+
+  /**
+   * Implements Drupal\Core\Image\ToolkitInterface::save().
+   */
+  function save(stdClass $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.
+    return FALSE;
+  }
+
+  /**
+   * Implements Drupal\Core\Image\ToolkitInterface::crop().
+   */
+  function crop(stdClass $image, $x, $y, $width, $height) {
+    $this->_logCall('crop', array($image, $x, $y, $width, $height));
+    return TRUE;
+  }
+
+  /**
+   * Implements Drupal\Core\Image\ToolkitInterface::resize().
+   */
+  function resize(stdClass $image, $width, $height) {
+    $this->_logCall('resize', array($image, $width, $height));
+    return TRUE;
+  }
+
+  /**
+   * Implements Drupal\Core\Image\ToolkitInterface::rotate().
+   */
+  function rotate(stdClass $image, $degrees, $background = NULL) {
+    $this->_logCall('rotate', array($image, $degrees, $background));
+    return TRUE;
+  }
+
+  /**
+   * Implements Drupal\Core\Image\ToolkitInterface::desaturate().
+   */
+  function desaturate(stdClass $image) {
+    $this->_logCall('desaturate', array($image));
+    return TRUE;
+  }
+
+  /**
+   * Store the values passed to a toolkit call.
+   *
+   * @param $op
+   *   One of the image toolkit operations: 'get_info', 'load', 'save',
+   *   'settings', 'resize', 'rotate', 'crop', 'desaturate'.
+   * @param $args
+   *   Values passed to hook.
+   *
+   * @see image_test_get_all_calls()
+   * @see image_test_reset()
+   */
+  function _logCall($op, $args) {
+    $results = variable_get('image_test_results', array());
+    $results[$op][] = $args;
+    variable_set('image_test_results', $results);
+  }
+
+  /**
+   * Implements Drupal\Core\Image\ToolkitInterface::checkAvailable().
+   */
+  static function checkAvailable() {
+    return TRUE;
+  }
+
+  /**
+   * Implements Drupal\Core\Image\ToolkitInterface::getDescription().
+   */
+  static function getDescription() {
+    return  t('A dummy toolkit that works');
+  }
+}
