diff --git a/core/lib/Drupal/Core/Image/Image.php b/core/lib/Drupal/Core/Image/Image.php
index e170991..9f9f8f2 100644
--- a/core/lib/Drupal/Core/Image/Image.php
+++ b/core/lib/Drupal/Core/Image/Image.php
@@ -151,6 +151,13 @@ public function apply($operation, array $arguments = array()) {
   /**
    * {@inheritdoc}
    */
+  public function createNew($width, $height, $mime_type = 'image/png', $transparent_color = '#FFFFFF') {
+    return $this->apply('create_new', array('width' => $width, 'height' => $height, 'mime_type' => $mime_type, 'transparent_color' => $transparent_color));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function crop($x, $y, $width, $height = NULL) {
     return $this->apply('crop', array('x' => $x, 'y' => $y, 'width' => $width, 'height' => $height));
   }
diff --git a/core/lib/Drupal/Core/Image/ImageInterface.php b/core/lib/Drupal/Core/Image/ImageInterface.php
index 02db478..f60aaa8 100644
--- a/core/lib/Drupal/Core/Image/ImageInterface.php
+++ b/core/lib/Drupal/Core/Image/ImageInterface.php
@@ -110,6 +110,25 @@ public function apply($operation, array $arguments = array());
   public function save($destination = NULL);
 
   /**
+   * Prepares a new image, without loading it from a file.
+   *
+   * @param int $width
+   *   The width of the new image, in pixels.
+   * @param int $height
+   *   The height of the new image, in pixels.
+   * @param string $mime_type
+   *   (Optional) The MIME type of the image (e.g. 'image/png', image/gif',
+   *   etc.). Defaults to 'image/png'.
+   * @param string $transparent_color
+   *   (Optional) The hexadecimal string representing the color to be used
+   *   for transparency, needed for GIF images. Defaults to '#FFFFFF' (white).
+   *
+   * @return bool
+   *   TRUE on success, FALSE on failure.
+   */
+  public function createNew($width, $height, $mime_type = 'image/png', $transparent_color = '#FFFFFF');
+
+  /**
    * Scales an image while maintaining aspect ratio.
    *
    * The resulting image can be smaller for one or both target dimensions.
diff --git a/core/lib/Drupal/Core/ImageToolkit/ImageToolkitOperationBase.php b/core/lib/Drupal/Core/ImageToolkit/ImageToolkitOperationBase.php
index 9bff2a4..e4e9b9f 100644
--- a/core/lib/Drupal/Core/ImageToolkit/ImageToolkitOperationBase.php
+++ b/core/lib/Drupal/Core/ImageToolkit/ImageToolkitOperationBase.php
@@ -59,10 +59,10 @@ public function __construct(array $configuration, $plugin_id, array $plugin_defi
   /**
    * Returns the image toolkit instance for this operation.
    *
-   * Image toolkit implementers should provide a trait that overrides this
-   * method to correctly document the return type of this getter. This provides
-   * better DX (code checking and code completion) for image toolkit operation
-   * developers.
+   * Image toolkit implementers should provide a toolkit operation base class
+   * that overrides this method to correctly document the return type of this
+   * getter. This provides better DX (code checking and code completion) for
+   * image toolkit operation developers.
    *
    * @return \Drupal\Core\ImageToolkit\ImageToolkitInterface
    */
diff --git a/core/modules/system/src/Plugin/ImageToolkit/GDToolkit.php b/core/modules/system/src/Plugin/ImageToolkit/GDToolkit.php
index 98ae863..8a5efd2 100644
--- a/core/modules/system/src/Plugin/ImageToolkit/GDToolkit.php
+++ b/core/modules/system/src/Plugin/ImageToolkit/GDToolkit.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\system\Plugin\ImageToolkit;
 
+use Drupal\Component\Utility\Color;
 use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\ImageToolkit\ImageToolkitBase;
@@ -136,16 +137,21 @@ protected function load() {
         return TRUE;
       }
       else {
-        // Convert indexed images to true color, so that filters work
-        // correctly and don't result in unnecessary dither.
-        $new_image = $this->createTmp($this->getType(), imagesx($resource), imagesy($resource));
-        if ($ret = (bool) $new_image) {
-          imagecopy($new_image, $resource, 0, 0, 0, 0, imagesx($resource), imagesy($resource));
+        // Convert indexed images to truecolor, copying the image to a new
+        // truecolor resource, so that filters work correctly and don't result
+        // in unnecessary dither.
+        $data = array(
+          'width' => imagesx($resource),
+          'height' => imagesy($resource),
+          'mime_type' => $this->getMimeType(),
+          'transparent_color' => $this->getTransparentColor(),
+        );
+        if ($this->apply('create_new', $data)) {
+          imagecopy($this->getResource(), $resource, 0, 0, 0, 0, imagesx($resource), imagesy($resource));
           imagedestroy($resource);
-          $this->setResource($new_image);
         }
-        return $ret;
       }
+      return (bool) $this->getResource();
     }
     return FALSE;
   }
@@ -210,58 +216,36 @@ public function parseFile() {
   }
 
   /**
-   * Creates a truecolor image preserving transparency from a provided image.
+   * Gets the color set for transparency in GIF images.
    *
-   * @param int $type
-   *   An image type represented by a PHP IMAGETYPE_* constant (e.g.
-   *   IMAGETYPE_JPEG, IMAGETYPE_PNG, etc.).
-   * @param int $width
-   *   The new width of the new image, in pixels.
-   * @param int $height
-   *   The new height of the new image, in pixels.
-   *
-   * @return resource
-   *   A GD image handle.
+   * @return string|null
+   *   A color string like '#rrggbb', or NULL if not set or not relevant.
    */
-  public function createTmp($type, $width, $height) {
-    $res = imagecreatetruecolor($width, $height);
-
-    if ($type == IMAGETYPE_GIF) {
-      // Find out if a transparent color is set, will return -1 if no
-      // transparent color has been defined in the image.
-      $transparent = imagecolortransparent($this->getResource());
-      if ($transparent >= 0) {
-        // Find out the number of colors in the image palette. It will be 0 for
-        // truecolor images.
-        $palette_size = imagecolorstotal($this->getResource());
-        if ($palette_size == 0 || $transparent < $palette_size) {
-          // Set the transparent color in the new resource, either if it is a
-          // truecolor image or if the transparent color is part of the palette.
-          // Since the index of the transparency color is a property of the
-          // image rather than of the palette, it is possible that an image
-          // could be created with this index set outside the palette size (see
-          // http://stackoverflow.com/a/3898007).
-          $transparent_color = imagecolorsforindex($this->getResource(), $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 ($type == IMAGETYPE_PNG) {
-      imagealphablending($res, FALSE);
-      $transparency = imagecolorallocatealpha($res, 0, 0, 0, 127);
-      imagefill($res, 0, 0, $transparency);
-      imagealphablending($res, TRUE);
-      imagesavealpha($res, TRUE);
+  public function getTransparentColor() {
+    if (!$this->getResource() || $this->getType() != IMAGETYPE_GIF) {
+      return NULL;
     }
-    else {
-      imagefill($res, 0, 0, imagecolorallocate($res, 255, 255, 255));
+    // Find out if a transparent color is set, will return -1 if no
+    // transparent color has been defined in the image.
+    $transparent = imagecolortransparent($this->getResource());
+    if ($transparent >= 0) {
+      // Find out the number of colors in the image palette. It will be 0 for
+      // truecolor images.
+      $palette_size = imagecolorstotal($this->getResource());
+      if ($palette_size == 0 || $transparent < $palette_size) {
+        // Return the transparent color, either if it is a truecolor image
+        // or if the transparent color is part of the palette.
+        // Since the index of the transparent color is a property of the
+        // image rather than of the palette, it is possible that an image
+        // could be created with this index set outside the palette size.
+        // (see http://stackoverflow.com/a/3898007).
+        $rgb = imagecolorsforindex($this->getResource(), $transparent);
+        unset($rgb['alpha']);
+        return Color::rgbToHex($rgb);
+      }
+      return NULL;
     }
-
-    return $res;
+    return NULL;
   }
 
   /**
diff --git a/core/modules/system/src/Plugin/ImageToolkit/Operation/gd/CreateNew.php b/core/modules/system/src/Plugin/ImageToolkit/Operation/gd/CreateNew.php
new file mode 100644
index 0000000..1692cbb
--- /dev/null
+++ b/core/modules/system/src/Plugin/ImageToolkit/Operation/gd/CreateNew.php
@@ -0,0 +1,94 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\Plugin\ImageToolkit\Operation\gd\CreateNew.
+ */
+
+namespace Drupal\system\Plugin\ImageToolkit\Operation\gd;
+
+use Drupal\Component\Utility\Color;
+
+/**
+ * Defines GD2 create_new image operation.
+ *
+ * @ImageToolkitOperation(
+ *   id = "gd_create_new",
+ *   toolkit = "gd",
+ *   operation = "create_new",
+ *   label = @Translation("Set a new image"),
+ *   description = @Translation("Creates a new transparent resource and sets it for the image.")
+ * )
+ */
+class CreateNew extends GDImageToolkitOperationBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function arguments() {
+    return array(
+      'width' => array(
+        'description' => 'The width of the image, in pixels',
+      ),
+      'height' => array(
+        'description' => 'The height of the image, in pixels',
+      ),
+      'mime_type' => array(
+        'description' => 'The MIME type of the image',
+        'required' => FALSE,
+        'default' => 'image/png',
+      ),
+      'transparent_color' => array(
+        'description' => 'The RGB hex color for GIF transparency',
+        'required' => FALSE,
+        'default' => '#FFFFFF',
+      ),
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function execute(array $arguments) {
+    if (!$res = imagecreatetruecolor($arguments['width'], $arguments['height'])) {
+      return FALSE;
+    }
+    switch ($arguments['mime_type']) {
+      case 'image/png':
+        imagealphablending($res, FALSE);
+        $transparency = imagecolorallocatealpha($res, 0, 0, 0, 127);
+        imagefill($res, 0, 0, $transparency);
+        imagealphablending($res, TRUE);
+        imagesavealpha($res, TRUE);
+        $this->getToolkit()->setType(IMAGETYPE_PNG);
+        break;
+
+      case 'image/gif':
+        if (empty($arguments['transparent_color'])) {
+          // No transparency color specified, fill white.
+          $fill_color = imagecolorallocate($res, 255, 255, 255);
+        }
+        else {
+          $fill_rgb = Color::hexToRgb($arguments['transparent_color']);
+          $fill_color = imagecolorallocate($res, $fill_rgb['red'], $fill_rgb['green'], $fill_rgb['blue']);
+          imagecolortransparent($res, $fill_color);
+        }
+        imagefill($res, 0, 0, $fill_color);
+        $this->getToolkit()->setType(IMAGETYPE_GIF);
+        break;
+
+      case 'image/jpeg':
+        imagefill($res, 0, 0, imagecolorallocate($res, 255, 255, 255));
+        $this->getToolkit()->setType(IMAGETYPE_JPEG);
+        break;
+
+      default:
+        return FALSE;
+
+    }
+
+    $this->getToolkit()->setResource($res);
+    return TRUE;
+  }
+
+}
diff --git a/core/modules/system/src/Plugin/ImageToolkit/Operation/gd/Crop.php b/core/modules/system/src/Plugin/ImageToolkit/Operation/gd/Crop.php
index 95d8fd7..5529129 100644
--- a/core/modules/system/src/Plugin/ImageToolkit/Operation/gd/Crop.php
+++ b/core/modules/system/src/Plugin/ImageToolkit/Operation/gd/Crop.php
@@ -80,17 +80,24 @@ protected function validateArguments(array $arguments) {
    * {@inheritdoc}
    */
   protected function execute(array $arguments) {
-    $res = $this->getToolkit()->createTmp($this->getToolkit()->getType(), $arguments['width'], $arguments['height']);
-
-    if (!imagecopyresampled($res, $this->getToolkit()->getResource(), 0, 0, $arguments['x'], $arguments['y'], $arguments['width'], $arguments['height'], $arguments['width'], $arguments['height'])) {
-      return FALSE;
+    // Create a new resource of the required dimensions, and copy and resize
+    // the original resource on it with resampling. Destroy the original
+    // resource upon success.
+    $original_resource = $this->getToolkit()->getResource();
+    $data = array(
+      'width' => $arguments['width'],
+      'height' => $arguments['height'],
+      'mime_type' => $this->getToolkit()->getMimeType(),
+      'transparent_color' => $this->getToolkit()->getTransparentColor()
+    );
+    if ($this->getToolkit()->apply('create_new', $data)) {
+      if (!imagecopyresampled($this->getToolkit()->getResource(), $original_resource, 0, 0, $arguments['x'], $arguments['y'], $arguments['width'], $arguments['height'], $arguments['width'], $arguments['height'])) {
+        return FALSE;
+      }
+      imagedestroy($original_resource);
+      return TRUE;
     }
-
-    // Destroy the original image and return the modified image.
-    imagedestroy($this->getToolkit()->getResource());
-    $this->getToolkit()->setResource($res);
-
-    return TRUE;
+    return FALSE;
   }
 
 }
diff --git a/core/modules/system/src/Plugin/ImageToolkit/Operation/gd/Resize.php b/core/modules/system/src/Plugin/ImageToolkit/Operation/gd/Resize.php
index bc192c7..4bf7e65 100644
--- a/core/modules/system/src/Plugin/ImageToolkit/Operation/gd/Resize.php
+++ b/core/modules/system/src/Plugin/ImageToolkit/Operation/gd/Resize.php
@@ -59,17 +59,24 @@ protected function validateArguments(array $arguments) {
    * {@inheritdoc}
    */
   protected function execute(array $arguments = array()) {
-    $res = $this->getToolkit()->createTmp($this->getToolkit()->getType(), $arguments['width'], $arguments['height']);
-
-    if (!imagecopyresampled($res, $this->getToolkit()->getResource(), 0, 0, 0, 0, $arguments['width'], $arguments['height'], $this->getToolkit()->getWidth(), $this->getToolkit()->getHeight())) {
-      return FALSE;
+    // Create a new resource of the required dimensions, and copy and resize
+    // the original resource on it with resampling. Destroy the original
+    // resource upon success.
+    $original_resource = $this->getToolkit()->getResource();
+    $data = array(
+      'width' => $arguments['width'],
+      'height' => $arguments['height'],
+      'mime_type' => $this->getToolkit()->getMimeType(),
+      'transparent_color' => $this->getToolkit()->getTransparentColor()
+    );
+    if ($this->getToolkit()->apply('create_new', $data)) {
+      if (!imagecopyresampled($this->getToolkit()->getResource(), $original_resource, 0, 0, 0, 0, $arguments['width'], $arguments['height'], imagesx($original_resource), imagesy($original_resource))) {
+        return FALSE;
+      }
+      imagedestroy($original_resource);
+      return TRUE;
     }
-
-    imagedestroy($this->getToolkit()->getResource());
-    // Update image object.
-    $this->getToolkit()->setResource($res);
-
-    return TRUE;
+    return FALSE;
   }
 
 }
diff --git a/core/modules/system/src/Tests/Image/ToolkitGdTest.php b/core/modules/system/src/Tests/Image/ToolkitGdTest.php
index c3f8e1c..c20c77c 100644
--- a/core/modules/system/src/Tests/Image/ToolkitGdTest.php
+++ b/core/modules/system/src/Tests/Image/ToolkitGdTest.php
@@ -125,6 +125,12 @@ function testManipulations() {
 
     // Setup a list of tests to perform on each type.
     $operations = array(
+      'create_new' => array(
+        'function' => 'create_new',
+        'arguments' => array('width' => 20, 'height' => 10, 'transparent_color' => '#ffff00'), // Yellow color for transparency of GIF file.
+        'width' => 20,
+        'height' => 10,
+      ),
       'resize' => array(
         'function' => 'resize',
         'arguments' => array('width' => 20, 'height' => 10),
@@ -231,6 +237,10 @@ function testManipulations() {
       );
     }
 
+    // Prepare a directory for test file results.
+    $directory = $this->public_files_directory .'/imagetest';
+    file_prepare_directory($directory, FILE_CREATE_DIRECTORY);
+
     foreach ($files as $file) {
       foreach ($operations as $op => $values) {
         // Load up a fresh image.
@@ -252,6 +262,18 @@ function testManipulations() {
           }
         }
 
+        // CreateNew operation requires specifying MIME type, and color at corners
+        // is dependent on that.
+        if ($op == 'create_new') {
+          $values['arguments']['mime_type'] = $image->getMimeType();
+          if ($image->getToolkit()->getType() != IMAGETYPE_JPEG) {
+            $values['corners'] = array_fill(0, 4, $this->transparent);
+          }
+          else {
+            $values['corners'] = array_fill(0, 4, $this->white);
+          }
+        }
+
         // Perform our operation.
         $image->apply($values['function'], $values['arguments']);
 
@@ -283,8 +305,6 @@ function testManipulations() {
           $correct_dimensions_object = FALSE;
         }
 
-        $directory = $this->public_files_directory .'/imagetest';
-        file_prepare_directory($directory, FILE_CREATE_DIRECTORY);
         $file_path = $directory . '/' . $op . image_type_to_extension($image->getToolkit()->getType());
         $image->save($file_path);
 
@@ -292,7 +312,7 @@ function testManipulations() {
         $this->assertTrue($correct_dimensions_object, String::format('Image %file object after %action action is reporting the proper height and width values.', array('%file' => $file, '%action' => $op)));
 
         // JPEG colors will always be messed up due to compression.
-        if ($image->getToolkit()->getType() != IMAGETYPE_JPEG) {
+        if ($image->getToolkit()->getType() != IMAGETYPE_JPEG  || $op == 'create_new') {
           // Now check each of the corners to ensure color correctness.
           foreach ($values['corners'] as $key => $corner) {
             // The test gif that does not have transparency has yellow where the
@@ -328,8 +348,43 @@ function testManipulations() {
         // Check that saved image reloads without raising PHP errors.
         $image_reloaded = $this->imageFactory->get($file_path);
         $resource = $image_reloaded->getToolkit()->getResource();
+
+        // Check that expected color (yellow) was saved as transparent
+        // in GIF file.
+        if ($image_reloaded->getToolkit()->getType() == IMAGETYPE_GIF  && $op == 'create_new') {
+          $this->assertEqual('#ffff00', $image_reloaded->getToolkit()->getTransparentColor(), String::format('Image file %file has the correct transparent color channel set.', array('%file' => $op . image_type_to_extension($image->getToolkit()->getType()))));
+        }
+      }
+    }
+
+    // Test creation of image from scratch, and saving to storage.
+    foreach (array(IMAGETYPE_PNG, IMAGETYPE_GIF, IMAGETYPE_JPEG) as $type) {
+      $image = $this->imageFactory->get();
+      $image->createNew(50, 20, image_type_to_mime_type($type), '#ffff00');
+      $file = 'from_null' . image_type_to_extension($type);
+      $file_path = $directory . '/' . $file ;
+      $this->assertEqual(50, $image->getWidth(), String::format('Image file %file has the correct width.', array('%file' => $file)));
+      $this->assertEqual(20, $image->getHeight(), String::format('Image file %file has the correct height.', array('%file' => $file)));
+      $this->assertEqual(image_type_to_mime_type($type), $image->getMimeType(), String::format('Image file %file has the correct MIME type.', array('%file' => $file)));
+      $this->assertTrue($image->save($file_path), String::format('Image %file created anew from a null image was saved.', array('%file' => $file)));
+
+      // Reload saved image.
+      $image_reloaded = $this->imageFactory->get($file_path);
+      if (!$image_reloaded->isValid()) {
+        $this->fail(String::format('Could not load image %file.', array('%file' => $file)));
+        continue;
+      }
+      $this->assertEqual(50, $image_reloaded->getWidth(), String::format('Image file %file has the correct width.', array('%file' => $file)));
+      $this->assertEqual(20, $image_reloaded->getHeight(), String::format('Image file %file has the correct height.', array('%file' => $file)));
+      $this->assertEqual(image_type_to_mime_type($type), $image_reloaded->getMimeType(), String::format('Image file %file has the correct MIME type.', array('%file' => $file)));
+      if ($image_reloaded->getToolkit()->getType() == IMAGETYPE_GIF) {
+        $this->assertEqual('#ffff00', $image_reloaded->getToolkit()->getTransparentColor(), String::format('Image file %file has the correct transparent color channel set.', array('%file' => $file)));
+      }
+      else  {
+        $this->assertEqual(NULL, $image_reloaded->getToolkit()->getTransparentColor(), String::format('Image file %file has no color channel set.', array('%file' => $file)));
       }
     }
+
   }
 
   /**
diff --git a/core/modules/system/src/Tests/Image/ToolkitTestBase.php b/core/modules/system/src/Tests/Image/ToolkitTestBase.php
index a04dbb3..9f9b0fc 100644
--- a/core/modules/system/src/Tests/Image/ToolkitTestBase.php
+++ b/core/modules/system/src/Tests/Image/ToolkitTestBase.php
@@ -88,6 +88,7 @@ function assertToolkitOperationsCalled(array $expected) {
       'rotate',
       'crop',
       'desaturate',
+      'create_new',
       'scale',
       'scale_and_crop',
       'my_operation',
@@ -134,6 +135,7 @@ function imageTestReset() {
       'rotate' => array(),
       'crop' => array(),
       'desaturate' => array(),
+      'create_new' => array(),
       'scale' => array(),
       'scale_and_crop' => array(),
     );
