Change record status: 
Project: 
Introduced in branch: 
8.3.x
Introduced in version: 
Description: 

Summary

  • "Allow upscale" setting is now available for "Scale and crop" effect.
  • Processing functions changed to properly handle upscaling setting.
  • "Scale and crop" effect template indicating upscale option status created.
  • Tests now covering upscaling.

core/lib/Drupal/Core/Image/ImageInterface.php

Updated scale and scaleAndCrop functions declaration and documentation.

Before

  /**
   * ...
   * @param int|null $height
   *   (optional) The target height, in pixels. If this value is null then the
   *   scaling will be 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.
   * ...
   */
  public function scale($width, $height = NULL, $upscale = FALSE);

  /**
   * ...
   * @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);

After

  /**
   * ...
   * @param int|null $height
   *   (optional) The target height, in pixels. If this value is null then the
   *   scaling will be based only on the width value. Defaults to NULL.
   * @param bool $upscale
   *   (optional) Boolean indicating that files smaller than the dimensions will
   *   be scaled up. This generally results in a low quality image.
   *   Defaults to FALSE.
   * ...
   */
  public function scale($width, $height = NULL, $upscale = FALSE);

  /**
   * ...
   * @param int $width
   *   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.
   *   Defaults to FALSE.
   *
   * @return bool
   *   TRUE on success, FALSE on failure.
   */
  public function scaleAndCrop($width, $height, $upscale = FALSE);

core/lib/Drupal/Core/Image/Image.php

Before

  public function scaleAndCrop($width, $height) {
    return $this->apply('scale_and_crop', array('width' => $width, 'height' => $height));
  }

After

  public function scaleAndCrop($width, $height, $upscale = FALSE) {
    return $this->apply('scale_and_crop', array('width' => $width, 'height' => $height, 'upscale' => $upscale));
  }

core/modules/image/config/schema/image.schema.yml

Schema definition updated.

Before

image.effect.image_scale_and_crop:
  type: image_size
  label: 'Image scale and crop'

After

image.effect.image_scale_and_crop:
  type: image_size
  label: 'Image scale and crop'
  mapping:
    upscale:
      type: boolean
      label: 'Upscale'

core/modules/image/src/Plugin/ImageEffect/ScaleAndCropImageEffect.php

applyEffect function was updated to respect upscale setting and necessary inherited functions were overridden.

Before

public function applyEffect(ImageInterface $image) {
    if (!$image->scaleAndCrop($this->configuration['width'], $this->configuration['height'])) {
      $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;
  }

After

  public function applyEffect(ImageInterface $image) {
    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)', [
        '%toolkit' => $image->getToolkitId(),
        '%path' => $image->getSource(),
        '%mimetype' => $image->getMimeType(),
        '%dimensions' => $image->getWidth() . 'x' . $image->getHeight(),
      ]);
      return FALSE;
    }
    return TRUE;
  }

  public function transformDimensions(array &$dimensions, $uri) {
    ...
  }

  public function getSummary() {
    ...
  }

  public function defaultConfiguration() {
    ...
  }

  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
    ...
  }

  public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
    ...
  }

  public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
    ...
  }

core/modules/image/image.module

Theme implementation for image_scale_and_crop_summary added.

Before

function image_theme() {
  ...
    'image_scale_summary' => array(
      'variables' => array('data' => NULL, 'effect' => array()),
    ),
    'image_crop_summary' => array(
      'variables' => array('data' => NULL, 'effect' => array()),
    ),
  ...
}

After

function image_theme() {
  ...
    'image_scale_summary' => array(
      'variables' => array('data' => NULL, 'effect' => array()),
    ),
    'image_scale_and_crop_summary' => array(
      'variables' => array('data' => NULL, 'effect' => array()),
    ),
    'image_crop_summary' => array(
      'variables' => array('data' => NULL, 'effect' => array()),
    ),
  ...
}

Added templates:
core/modules/image/templates/image-scale-and-crop-summary.html.twig
core/themes/stable/templates/admin/image-scale-and-crop-summary.html.twig

Tests

Tests were updated correspondingly

core/modules/image/src/Tests/ImageAdminStylesTest.php

Before

  function testStyle() {
    ...
      'image_scale_and_crop' => array(
        'width' => 120,
        'height' => 121,
      ),
    ...
  }

After

  function testStyle() {
    ...
      'image_scale_and_crop' => array(
        'width' => 120,
        'height' => 121,
        'upscale' => 1,
      ),
    ...
  }

core/modules/image/src/Tests/ImageEffectsTest.php

Before

  function testScaleEffect() {
    // @todo: need to test upscaling.
    $this->assertImageEffect('image_scale', array(
      'width' => 10,
      'height' => 10,
    ));
    $this->assertToolkitOperationsCalled(array('scale'));

    // Check the parameters.
    $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');
  }

...

  function testScaleAndCropEffect() {
    $this->assertImageEffect('image_scale_and_crop', array(
      'width' => 5,
      'height' => 10,
    ));
    $this->assertToolkitOperationsCalled(array('scale_and_crop'));

    // Check the parameters.
    $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');
  }

After

  function testScaleEffect() {
    $this->assertImageEffect('image_scale', array(
      'width' => 10,
      'height' => 10,
      'upscale' => 1,
    ));
    $this->assertToolkitOperationsCalled(array('scale'));

    // Check the parameters.
    $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');
  }

...

  function testScaleAndCropEffect() {
    $this->assertImageEffect('image_scale_and_crop', array(
      'width' => 5,
      'height' => 10,
      'upscale' => 1,
    ));
    $this->assertToolkitOperationsCalled(array('scale_and_crop'));

    // Check the parameters.
    $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');
  }

core/modules/image/tests/src/Kernel/Migrate/d7/MigrateImageStylesTest.php

Before

  public function testImageStylesMigration() {
    $this->assertEntity('custom_image_style_1', "Custom image style 1", ['image_scale_and_crop', 'image_desaturate'], [['width' => 55, 'height' => 55], []]);
    ...
  }

  /**
   * ...
   * @param array $expected_effect_plugins
   *   An array of expected plugins attached to the image style entity
   * @param array $expected_effect_config
   *   An array of expected configuration for each effect in the image style
   */
  protected function assertEntity($id, $label, array $expected_effect_plugins, array $expected_effect_config) {
    ...
  }

After

  public function testImageStylesMigration() {
    $this->assertEntity('custom_image_style_1', "Custom image style 1", ['image_scale_and_crop', 'image_desaturate'], [['width' => 55, 'height' => 55, 'upscale' => FALSE], []]);
    ...
  }

  /**
   * ...
   * @param array $expected_effect_plugins
   *   An array of expected plugins attached to the image style entity.
   * @param array $expected_effect_config
   *   An array of expected configuration for each effect in the image style.
   */
  protected function assertEntity($id, $label, array $expected_effect_plugins, array $expected_effect_config) {
    ...
  }
Impacts: 
Site builders, administrators, editors
Themers
Updates Done (doc team, etc.)
Online documentation: 
Not done
Theming guide: 
Not done
Module developer documentation: 
Not done
Examples project: 
Not done
Coder Review: 
Not done
Coder Upgrade: 
Not done
Other: 
Other updates done
Details: 
Progress: