diff --git a/core/lib/Drupal/Core/Image/Image.php b/core/lib/Drupal/Core/Image/Image.php
index e170991..fe146ee 100644
--- a/core/lib/Drupal/Core/Image/Image.php
+++ b/core/lib/Drupal/Core/Image/Image.php
@@ -179,8 +179,8 @@ public function rotate($degrees, $background = NULL) {
   /**
    * {@inheritdoc}
    */
-  public function scaleAndCrop($width, $height) {
-    return $this->apply('scale_and_crop', array('width' => $width, 'height' => $height));
+  public function scaleAndCrop($width, $height, $upscale = TRUE) {
+    return $this->apply('scale_and_crop', array('width' => $width, 'height' => $height, 'upscale' => $upscale));
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Image/ImageInterface.php b/core/lib/Drupal/Core/Image/ImageInterface.php
index 02db478..63ad482 100644
--- a/core/lib/Drupal/Core/Image/ImageInterface.php
+++ b/core/lib/Drupal/Core/Image/ImageInterface.php
@@ -142,11 +142,14 @@ public function scale($width, $height = NULL, $upscale = FALSE);
    *   The target width, in pixels.
    * @param int $height
    *   The target height, in pixels.
+   * @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 scaleAndCrop($width, $height);
+  public function scaleAndCrop($width, $height, $upscale = TRUE);
 
   /**
    * Crops an image to a rectangle specified by the given dimensions.
diff --git a/core/modules/image/config/schema/image.schema.yml b/core/modules/image/config/schema/image.schema.yml
index 4f1fc79..2c8ca4f 100644
--- a/core/modules/image/config/schema/image.schema.yml
+++ b/core/modules/image/config/schema/image.schema.yml
@@ -58,6 +58,10 @@ image.effect.image_scale:
 image.effect.image_scale_and_crop:
   type: image_size
   label: 'Image scale and crop'
+  mapping:
+      upscale:
+        type: boolean
+        label: 'Upscale'
 
 image.settings:
   type: mapping
diff --git a/core/modules/image/image.module b/core/modules/image/image.module
index a6ce171..3209427 100644
--- a/core/modules/image/image.module
+++ b/core/modules/image/image.module
@@ -133,6 +133,10 @@ function image_theme() {
       'variables' => array('data' => NULL, 'effect' => array()),
       'template' => 'image-crop-summary',
     ),
+    'image_scale_and_crop_summary' => array(
+      'variables' => array('data' => NULL, 'effect' => array()),
+      'template' => 'image-scale-and-crop-summary',
+    ),
     'image_rotate_summary' => array(
       'variables' => array('data' => NULL, 'effect' => array()),
       'template' => 'image-rotate-summary',
diff --git a/core/modules/image/src/Plugin/ImageEffect/ScaleAndCropImageEffect.php b/core/modules/image/src/Plugin/ImageEffect/ScaleAndCropImageEffect.php
index ca9479f..31a004a 100644
--- a/core/modules/image/src/Plugin/ImageEffect/ScaleAndCropImageEffect.php
+++ b/core/modules/image/src/Plugin/ImageEffect/ScaleAndCropImageEffect.php
@@ -24,11 +24,74 @@ class ScaleAndCropImageEffect extends ResizeImageEffect {
    * {@inheritdoc}
    */
   public function applyEffect(ImageInterface $image) {
-    if (!$image->scaleAndCrop($this->configuration['width'], $this->configuration['height'])) {
+    if (!$image->scaleAndCrop($this->configuration['width'], $this->configuration['height'], $this->configuration['upscale'])) {
       $this->logger->error('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()));
       return FALSE;
     }
     return TRUE;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function transformDimensions(array &$dimensions) {
+    $width = $this->configuration['width'];
+    $height = $this->configuration['height'];
+    $image_width = $dimensions['width'];
+    $image_height = $dimensions['height'];
+    $upscale = $this->configuration['upscale'];
+    $scale = max($width / $image_width, $height / $image_height);
+    if (!$upscale && $scale >= 1) {
+      // When upscale is false and scale > 1, image will have original
+      // dimensions.
+      return TRUE;
+    }
+    // The new image will have the exact dimensions defined for the effect.
+    $dimensions['width'] = $this->configuration['width'];
+    $dimensions['height'] = $this->configuration['height'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getSummary() {
+    $summary = array(
+      '#theme' => 'image_scale_and_crop_summary',
+      '#data' => $this->configuration,
+    );
+    $summary += parent::getSummary();
+    return $summary;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultConfiguration() {
+    return parent::defaultConfiguration() + array(
+      'upscale' => TRUE,
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationForm(array $form, array &$form_state) {
+    $form = parent::buildConfigurationForm($form, $form_state);
+    $form['upscale'] = array(
+      '#type' => 'checkbox',
+      '#default_value' => $this->configuration['upscale'],
+      '#title' => t('Allow Upscaling'),
+      '#description' => t('Let scale make images larger than their original size'),
+    );
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitConfigurationForm(array &$form, array &$form_state) {
+    parent::submitConfigurationForm($form, $form_state);
+    $this->configuration['upscale'] = $form_state['values']['upscale'];
+  }
+
 }
diff --git a/core/modules/image/src/Tests/ImageAdminStylesTest.php b/core/modules/image/src/Tests/ImageAdminStylesTest.php
index d998883..a9093c0 100644
--- a/core/modules/image/src/Tests/ImageAdminStylesTest.php
+++ b/core/modules/image/src/Tests/ImageAdminStylesTest.php
@@ -80,6 +80,7 @@ function testStyle() {
       'image_scale_and_crop' => array(
         'data[width]' => 120,
         'data[height]' => 121,
+        'data[upscale]' => 1,
       ),
       'image_crop' => array(
         'data[width]' => 130,
diff --git a/core/modules/image/src/Tests/ImageEffectsTest.php b/core/modules/image/src/Tests/ImageEffectsTest.php
index 95fd952..40e99ff 100644
--- a/core/modules/image/src/Tests/ImageEffectsTest.php
+++ b/core/modules/image/src/Tests/ImageEffectsTest.php
@@ -55,10 +55,10 @@ function testResizeEffect() {
    * Test the image_scale_effect() function.
    */
   function testScaleEffect() {
-    // @todo: need to test upscaling.
     $this->assertImageEffect('image_scale', array(
       'width' => 10,
       'height' => 10,
+      'upscale' => TRUE,
     ));
     $this->assertToolkitOperationsCalled(array('scale'));
 
@@ -66,6 +66,7 @@ function testScaleEffect() {
     $calls = $this->imageTestGetAllCalls();
     $this->assertEqual($calls['scale'][0][0], 10, 'Width was passed correctly');
     $this->assertEqual($calls['scale'][0][1], 10, 'Height was based off aspect ratio and passed correctly');
+    $this->assertTrue($calls['scale'][0][2], 'Upscale was passed correctly');
   }
 
   /**
@@ -95,6 +96,7 @@ function testScaleAndCropEffect() {
     $this->assertImageEffect('image_scale_and_crop', array(
       'width' => 5,
       'height' => 10,
+      'upscale' => TRUE,
     ));
     $this->assertToolkitOperationsCalled(array('scale_and_crop'));
 
@@ -102,6 +104,7 @@ function testScaleAndCropEffect() {
     $calls = $this->imageTestGetAllCalls();
     $this->assertEqual($calls['scale_and_crop'][0][0], 5, 'Width was computed and passed correctly');
     $this->assertEqual($calls['scale_and_crop'][0][1], 10, 'Height was computed and passed correctly');
+    $this->assertTrue($calls['scale_and_crop'][0][2], 'Upscale was passed correctly');
   }
 
   /**
diff --git a/core/modules/node/node.api.php b/core/modules/node/node.api.php
index 6fb311c..4949f3e 100644
--- a/core/modules/node/node.api.php
+++ b/core/modules/node/node.api.php
@@ -204,7 +204,7 @@ function hook_node_access_records(\Drupal\node\NodeInterface $node) {
  *
  * A module may deny all access to a node by setting $grants to an empty array.
  *
- * @param $grants
+ * @param array $grants
  *   The $grants array returned by hook_node_access_records().
  * @param \Drupal\node\NodeInterface $node
  *   The node for which the grants were acquired.
@@ -354,7 +354,7 @@ function hook_node_access(\Drupal\node\NodeInterface $node, $op, \Drupal\Core\Se
  *
  * @param \Drupal\node\NodeInterface $node
  *   The node being displayed in a search result.
- * @param $langcode
+ * @param object $langcode
  *   Language code of result being displayed.
  *
  * @return array
@@ -381,7 +381,7 @@ function hook_node_search_result(\Drupal\node\NodeInterface $node, $langcode) {
  *
  * @param \Drupal\node\NodeInterface $node
  *   The node being indexed.
- * @param $langcode
+ * @param object $langcode
  *   Language code of the variant of the node being indexed.
  *
  * @return string
@@ -414,9 +414,9 @@ function hook_node_update_index(\Drupal\node\NodeInterface $node, $langcode) {
  *
  * @param \Drupal\node\NodeInterface $node
  *   The node being validated.
- * @param $form
+ * @param array $form
  *   The form being used to edit the node.
- * @param $form_state
+ * @param \Drupal\Core\Form\FormStateInterface $form_state
  *   The current state of the form.
  *
  * @ingroup entity_crud
@@ -441,9 +441,9 @@ function hook_node_validate(\Drupal\node\NodeInterface $node, $form, \Drupal\Cor
  *
  * @param \Drupal\node\NodeInterface $node
  *   The node entity being updated in response to a form submission.
- * @param $form
+ * @param array $form
  *   The form being used to edit the node.
- * @param $form_state
+ * @param \Drupal\Core\Form\FormStateInterface $form_state
  *   The current state of the form.
  *
  * @ingroup entity_crud
