diff --git a/core/includes/image.inc b/core/includes/image.inc
index f6ae7f1..d7668fc 100644
--- a/core/includes/image.inc
+++ b/core/includes/image.inc
@@ -178,86 +178,132 @@ function image_scale_and_crop(stdClass $image, $width, $height) {
}
/**
- * Scales image dimensions while maintaining aspect ratio.
- *
- * The resulting dimensions can be smaller for one or both target dimensions.
- *
- * @param $dimensions
- * Dimensions to be modified - an array with components width and height, in
- * pixels.
- * @param $width
- * The target width, in pixels. If this value is NULL then the scaling will be
- * based only on the height value.
- * @param $height
- * The target height, in pixels. If this value is NULL then the scaling will
- * be based only on the width value.
- * @param $upscale
- * Boolean indicating that images smaller than the target dimensions will be
- * scaled up. This generally results in a low quality image.
- *
- * @return
- * TRUE if $dimensions was modified, FALSE otherwise.
+ * Image dimensions callback; Scale.
+ *
+ * @param array $dimensions
+ * Dimensions to be modified - an array with components width and height in
+ * pixels (int), or 0 or null if unknown.
+ * @param array $data
+ * An array of attributes to use when performing the scale effect with the
+ * following items:
+ * - "width": An integer representing the desired width in pixels, or 0 or
+ * null if the scaling should only be based on the height value.
+ * - "height": An integer representing the desired height in pixels, or 0 or
+ * null if the scaling should only be based on the height value.
+ * - "upscale": A boolean indicating that the image should be upscaled if the
+ * dimensions are larger than the original image.
*
* @see image_scale()
*/
-function image_dimensions_scale(array &$dimensions, $width = NULL, $height = NULL, $upscale = FALSE) {
- $aspect = $dimensions['height'] / $dimensions['width'];
-
- // Calculate one of the dimensions from the other target dimension,
- // ensuring the same aspect ratio as the source dimensions. If one of the
- // target dimensions is missing, that is the one that is calculated. If both
- // are specified then the dimension calculated is the one that would not be
- // calculated to be bigger than its target.
- if (($width && !$height) || ($width && $height && $aspect < $height / $width)) {
- $height = (int) round($width * $aspect);
+function image_scale_dimensions(array &$dimensions, array $data) {
+ $width = $data['width'];
+ $height = $data['height'];
+ $upscale = $data['upscale'];
+
+ // We can only calculate the new dimensions if we know the current dimensions.
+ if ($dimensions['width'] > 0 && $dimensions['height'] > 0) {
+ if (!empty($width) && !empty($height)) {
+ // Scale within bounding box.
+ $scale_factor_x = $width / $dimensions['width'];
+ $scale_factor_y = $height / $dimensions['height'];
+ if ($scale_factor_x <= $scale_factor_y) {
+ // The width is more restricting, use that factor.
+ $scale_factor = $scale_factor_x;
+ $height = $scale_factor * $dimensions['height'];
+ }
+ else {
+ // The height is more restricting, use that factor.
+ $scale_factor = $scale_factor_y;
+ $width = $scale_factor * $dimensions['width'];
+ }
+ }
+ else if (!empty($width)) {
+ // Scale to given width.
+ $scale_factor = $width / $dimensions['width'];
+ $height = $scale_factor * $dimensions['height'];
+ }
+ else if (!empty($height)) {
+ // Scale to given height.
+ $scale_factor = $height / $dimensions['height'];
+ $width = $scale_factor * $dimensions['width'];
+ }
+ else {
+ // No new dimensions passed in, so we cannot scale.
+ $scale_factor = NULL;
+ $dimensions['width'] = NULL;
+ $dimensions['height'] = NULL;
+ }
+ // The scale effect will only be executed if we are scaling down or if
+ // $upscale is set. Otherwise the dimensions remain unchanged.
+ if ($upscale || (isset($scale_factor) && $scale_factor <= 1)) {
+ $dimensions['width'] = $width;
+ $dimensions['height'] = $height;
+ }
}
else {
- $width = (int) round($height / $aspect);
- }
-
- // Don't upscale if the option isn't enabled.
- if (!$upscale && ($width >= $dimensions['width'] || $height >= $dimensions['height'])) {
- return FALSE;
+ // Set the new dimensions to null.
+ $dimensions['width'] = NULL;
+ $dimensions['height'] = NULL;
+ // However, we might know one of the new dimensions.
+ if (!empty($width) && empty($height)) {
+ // We might know the width if we are scaling unconditionally ($upscale is
+ // set), or if the new width is smaller then the current width. For the
+ // latter the current width must be known to be able to determine this:
+ // we check by testing if a convert to int gives a positive result.
+ if ($upscale || $width <= (int) $dimensions['width']) {
+ $dimensions['width'] = $width;
+ }
+ }
+ else if (empty($width) && !empty($height)) {
+ // See comment about width a few lines above.
+ if ($upscale || $height <= (int) $dimensions['height']) {
+ $dimensions['height'] = $height;
+ }
+ }
}
-
- $dimensions['width'] = $width;
- $dimensions['height'] = $height;
- return TRUE;
}
/**
* Scales an image while maintaining aspect ratio.
*
- * The resulting image can be smaller for one or both target dimensions.
+ * If only one of the $width and $height parameters is specified, the image is
+ * scaled such that the resulting width or height equals the given width or
+ * height. If both width and height are specified, the image is scaled (while
+ * maintaining aspect ratio) within the bounding box defined by the given width
+ * and height.
*
- * @param $image
+ * @param object $image
* An image object returned by image_load().
- * @param $width
- * The target width, in pixels. This value is omitted then the scaling will
- * based only on the height value.
- * @param $height
- * The target height, in pixels. This value is omitted then the scaling will
- * based only on the width value.
- * @param $upscale
+ * @param int|null $width
+ * The target width, in pixels. If this value is 0 or null, then the scaling
+ * will be based only on the height value.
+ * @param int|null $height
+ * The target height, in pixels. If this value is 0 or null, then the scaling
+ * will be based only on the width value.
+ * @param bool $upscale
* Boolean indicating that files smaller than the dimensions will be scaled
* up. This generally results in a low quality image.
*
- * @return
- * TRUE on success, FALSE on failure.
+ * @return bool
+ * true on success, false on failure.
*
* @see image_dimensions_scale()
* @see image_load()
* @see image_scale_and_crop()
*/
-function image_scale(stdClass $image, $width = NULL, $height = NULL, $upscale = FALSE) {
- $dimensions = $image->info;
+function image_scale(stdClass $image, $width, $height, $upscale = FALSE) {
+ $width = (int) round($width);
+ $height = (int) round($height);
- // Scale the dimensions - if they don't change then just return success.
- if (!image_dimensions_scale($dimensions, $width, $height, $upscale)) {
- return TRUE;
+ if (empty($width) && empty($height)) {
+ // We won't be able to scale if none of the dimensions are known.
+ return FALSE;
}
- return image_resize($image, $dimensions['width'], $dimensions['height']);
+ // We defer any calculations to the toolkit specific implementation. This
+ // because we can't be sure that both the current width and the current height
+ // are known, nor that they are needed for the current toolkit.
+ return image_toolkit_invoke('scale', $image, array($width, $height, $upscale));
}
/**
diff --git a/core/modules/image/image.effects.inc b/core/modules/image/image.effects.inc
index 35a6a74..1b1c2f2 100644
--- a/core/modules/image/image.effects.inc
+++ b/core/modules/image/image.effects.inc
@@ -137,26 +137,6 @@ function image_scale_effect(&$image, $data) {
}
/**
- * Image dimensions callback; Scale.
- *
- * @param $dimensions
- * Dimensions to be modified - an array with components width and height, in
- * pixels.
- * @param $data
- * An array of attributes to use when performing the scale effect with the
- * following items:
- * - "width": An integer representing the desired width in pixels.
- * - "height": An integer representing the desired height in pixels.
- * - "upscale": A boolean indicating that the image should be upscaled if the
- * dimensions are larger than the original image.
- */
-function image_scale_dimensions(array &$dimensions, array $data) {
- if ($dimensions['width'] && $dimensions['height']) {
- image_dimensions_scale($dimensions, $data['width'], $data['height'], $data['upscale']);
- }
-}
-
-/**
* Image effect callback; Crop an image resource.
*
* @param $image
diff --git a/core/modules/image/image.test b/core/modules/image/image.test
index 2c422a7..212e75e 100644
--- a/core/modules/image/image.test
+++ b/core/modules/image/image.test
@@ -262,14 +262,14 @@ class ImageEffectsUnitTest extends ImageToolkitTestCase {
* Test the image_scale_effect() function.
*/
function testScaleEffect() {
- // @todo: need to test upscaling.
- $this->assertTrue(image_scale_effect($this->image, array('width' => 10, 'height' => 10)), t('Function returned the expected value.'));
- $this->assertToolkitOperationsCalled(array('resize'));
+ $this->assertTrue(image_scale_effect($this->image, array('width' => 10, 'height' => 20, 'upscale' => TRUE)), t('Function returned the expected value.'));
+ $this->assertToolkitOperationsCalled(array('scale'));
// Check the parameters.
$calls = image_test_get_all_calls();
- $this->assertEqual($calls['resize'][0][1], 10, t('Width was passed correctly'));
- $this->assertEqual($calls['resize'][0][2], 5, t('Height was based off aspect ratio and passed correctly'));
+ $this->assertEqual($calls['scale'][0][1], 10, t('Width was passed correctly'));
+ $this->assertEqual($calls['scale'][0][2], 20, t('Height was passed correctly'));
+ $this->assertEqual($calls['scale'][0][3], TRUE, t('Upscale was passed correctly'));
}
/**
@@ -934,7 +934,7 @@ class ImageDimensionsUnitTest extends DrupalWebTestCase {
image_effect_save('test', $effect);
$img_tag = theme_image_style($variables);
- $this->assertEqual($img_tag, '');
+ $this->assertTrue(strpos($img_tag, 'width="120"') !== FALSE && strpos($img_tag, 'height="60"') !== FALSE, t('Image tag contains width and height attributes with correct values.'));
$this->assertFalse(file_exists($generated_uri), t('Generated file does not exist.'));
$this->drupalGet($url);
$this->assertResponse(200, t('Image was generated at the URL.'));
@@ -955,7 +955,7 @@ class ImageDimensionsUnitTest extends DrupalWebTestCase {
image_effect_save('test', $effect);
$img_tag = theme_image_style($variables);
- $this->assertEqual($img_tag, '');
+ $this->assertTrue(strpos($img_tag, 'width="60"') !== FALSE && strpos($img_tag, 'height="120"') !== FALSE, t('Image tag contains width and height attributes with correct values.'));
$this->assertFalse(file_exists($generated_uri), t('Generated file does not exist.'));
$this->drupalGet($url);
$this->assertResponse(200, t('Image was generated at the URL.'));
@@ -977,7 +977,7 @@ class ImageDimensionsUnitTest extends DrupalWebTestCase {
image_effect_save('test', $effect);
$img_tag = theme_image_style($variables);
- $this->assertEqual($img_tag, '');
+ $this->assertTrue(strpos($img_tag, 'width="45"') !== FALSE && strpos($img_tag, 'height="90"') !== FALSE, t('Image tag contains width and height attributes with correct values.'));
$this->assertFalse(file_exists($generated_uri), t('Generated file does not exist.'));
$this->drupalGet($url);
$this->assertResponse(200, t('Image was generated at the URL.'));
@@ -999,7 +999,7 @@ class ImageDimensionsUnitTest extends DrupalWebTestCase {
image_effect_save('test', $effect);
$img_tag = theme_image_style($variables);
- $this->assertEqual($img_tag, '');
+ $this->assertTrue(strpos($img_tag, 'width="45"') !== FALSE && strpos($img_tag, 'height="90"') !== FALSE, t('Image tag contains width and height attributes with correct values.'));
$this->assertFalse(file_exists($generated_uri), t('Generated file does not exist.'));
$this->drupalGet($url);
$this->assertResponse(200, t('Image was generated at the URL.'));
@@ -1017,7 +1017,7 @@ class ImageDimensionsUnitTest extends DrupalWebTestCase {
image_effect_save('test', $effect);
$img_tag = theme_image_style($variables);
- $this->assertEqual($img_tag, '');
+ $this->assertTrue(strpos($img_tag, 'width="45"') !== FALSE && strpos($img_tag, 'height="90"') !== FALSE, t('Image tag contains width and height attributes with correct values.'));
$this->assertFalse(file_exists($generated_uri), t('Generated file does not exist.'));
$this->drupalGet($url);
$this->assertResponse(200, t('Image was generated at the URL.'));
@@ -1038,7 +1038,7 @@ class ImageDimensionsUnitTest extends DrupalWebTestCase {
image_effect_save('test', $effect);
$img_tag = theme_image_style($variables);
- $this->assertEqual($img_tag, '');
+ $this->assertTrue(strpos($img_tag, 'width="') === FALSE && strpos($img_tag, 'height="') === FALSE, t('Image tag does not contain width and height attributes.'));
$this->assertFalse(file_exists($generated_uri), t('Generated file does not exist.'));
$this->drupalGet($url);
$this->assertResponse(200, t('Image was generated at the URL.'));
@@ -1058,7 +1058,7 @@ class ImageDimensionsUnitTest extends DrupalWebTestCase {
image_effect_save('test', $effect);
$img_tag = theme_image_style($variables);
- $this->assertEqual($img_tag, '');
+ $this->assertTrue(strpos($img_tag, 'width="30"') !== FALSE && strpos($img_tag, 'height="30"') !== FALSE, t('Image tag contains width and height attributes with correct values.'));
$this->assertFalse(file_exists($generated_uri), t('Generated file does not exist.'));
$this->drupalGet($url);
$this->assertResponse(200, t('Image was generated at the URL.'));
@@ -1079,7 +1079,7 @@ class ImageDimensionsUnitTest extends DrupalWebTestCase {
$effect = image_effect_save('test', $effect);
$img_tag = theme_image_style($variables);
- $this->assertEqual($img_tag, '');
+ $this->assertTrue(strpos($img_tag, 'width="') === FALSE && strpos($img_tag, 'height="') === FALSE, t('Image tag does not contain width and height attributes.'));
$this->assertFalse(file_exists($generated_uri), t('Generated file does not exist.'));
$this->drupalGet($url);
$this->assertResponse(200, t('Image was generated at the URL.'));
@@ -1097,26 +1097,26 @@ class ImageDimensionsUnitTest extends DrupalWebTestCase {
image_effect_save('test', $effect);
$img_tag = theme_image_style($variables);
- $this->assertEqual($img_tag, '');
+ $this->assertTrue(strpos($img_tag, 'width="') === FALSE && strpos($img_tag, 'height="') === FALSE, t('Image tag does not contain width and height attributes.'));
}
}
/**
- * Tests image_dimensions_scale().
+ * Tests image_scale_dimensions().
*/
-class ImageDimensionsScaleTestCase extends DrupalUnitTestCase {
+class ImageScaleDimensionsTestCase extends DrupalUnitTestCase {
public static function getInfo() {
return array(
- 'name' => 'image_dimensions_scale()',
- 'description' => 'Tests all control flow branches in image_dimensions_scale().',
+ 'name' => 'image_scale_dimensions()',
+ 'description' => 'Tests all control flow branches in image_scale_dimensions().',
'group' => 'Image',
);
}
/**
- * Tests all control flow branches in image_dimensions_scale().
+ * Tests all control flow branches in image_scale_dimensions().
*/
- function testImageDimensionsScale() {
+ function testImagescaleDimensions() {
// Define input / output datasets to test different branch conditions.
$test = array();
@@ -1129,16 +1129,17 @@ class ImageDimensionsScaleTestCase extends DrupalUnitTestCase {
'width' => 1000,
'height' => 2000,
),
- 'width' => 200,
- 'height' => NULL,
- 'upscale' => TRUE,
+ 'data' => array(
+ 'width' => 200,
+ 'height' => NULL,
+ 'upscale' => TRUE,
+ ),
),
'output' => array(
'dimensions' => array(
'width' => 200,
'height' => 400,
),
- 'return_value' => TRUE,
),
);
@@ -1151,16 +1152,17 @@ class ImageDimensionsScaleTestCase extends DrupalUnitTestCase {
'width' => 1000,
'height' => 800,
),
- 'width' => NULL,
- 'height' => 140,
- 'upscale' => FALSE,
+ 'data' => array(
+ 'width' => NULL,
+ 'height' => 140,
+ 'upscale' => FALSE,
+ ),
),
'output' => array(
'dimensions' => array(
'width' => 175,
'height' => 140,
),
- 'return_value' => TRUE,
),
);
@@ -1173,16 +1175,17 @@ class ImageDimensionsScaleTestCase extends DrupalUnitTestCase {
'width' => 8,
'height' => 20,
),
- 'width' => 200,
- 'height' => 140,
- 'upscale' => TRUE,
+ 'data' => array(
+ 'width' => 200,
+ 'height' => 140,
+ 'upscale' => TRUE,
+ ),
),
'output' => array(
'dimensions' => array(
'width' => 56,
'height' => 140,
),
- 'return_value' => TRUE,
),
);
@@ -1193,16 +1196,17 @@ class ImageDimensionsScaleTestCase extends DrupalUnitTestCase {
'width' => 2000,
'height' => 800,
),
- 'width' => 200,
- 'height' => 140,
- 'upscale' => FALSE,
+ 'data' => array(
+ 'width' => 200,
+ 'height' => 140,
+ 'upscale' => FALSE,
+ ),
),
'output' => array(
'dimensions' => array(
'width' => 200,
'height' => 80,
),
- 'return_value' => TRUE,
),
);
@@ -1213,31 +1217,29 @@ class ImageDimensionsScaleTestCase extends DrupalUnitTestCase {
'width' => 100,
'height' => 50,
),
- 'width' => 200,
- 'height' => 140,
- 'upscale' => FALSE,
+ 'data' => array(
+ 'width' => 200,
+ 'height' => 140,
+ 'upscale' => FALSE,
+ ),
),
'output' => array(
'dimensions' => array(
'width' => 100,
'height' => 50,
),
- 'return_value' => FALSE,
),
);
foreach ($tests as $test) {
// Process the test dataset.
- $return_value = image_dimensions_scale($test['input']['dimensions'], $test['input']['width'], $test['input']['height'], $test['input']['upscale']);
+ image_scale_dimensions($test['input']['dimensions'], $test['input']['data']);
// Check the width.
$this->assertEqual($test['output']['dimensions']['width'], $test['input']['dimensions']['width'], t('Computed width (@computed_width) equals expected width (@expected_width)', array('@computed_width' => $test['output']['dimensions']['width'], '@expected_width' => $test['input']['dimensions']['width'])));
// Check the height.
$this->assertEqual($test['output']['dimensions']['height'], $test['input']['dimensions']['height'], t('Computed height (@computed_height) equals expected height (@expected_height)', array('@computed_height' => $test['output']['dimensions']['height'], '@expected_height' => $test['input']['dimensions']['height'])));
-
- // Check the return value.
- $this->assertEqual($test['output']['return_value'], $return_value, t('Correct return value.'));
}
}
}
diff --git a/core/modules/simpletest/tests/image.test b/core/modules/simpletest/tests/image.test
index deead57..e7ed5cf 100644
--- a/core/modules/simpletest/tests/image.test
+++ b/core/modules/simpletest/tests/image.test
@@ -124,14 +124,14 @@ class ImageToolkitUnitTest extends ImageToolkitTestCase {
* Test the image_scale() function.
*/
function testScale() {
-// TODO: need to test upscaling
- $this->assertTrue(image_scale($this->image, 10, 10), t('Function returned the expected value.'));
- $this->assertToolkitOperationsCalled(array('resize'));
+ $this->assertTrue(image_scale($this->image, 10, 20, TRUE), t('Function returned the expected value.'));
+ $this->assertToolkitOperationsCalled(array('scale'));
// Check the parameters.
$calls = image_test_get_all_calls();
- $this->assertEqual($calls['resize'][0][1], 10, t('Width was passed correctly'));
- $this->assertEqual($calls['resize'][0][2], 5, t('Height was based off aspect ratio and passed correctly'));
+ $this->assertEqual($calls['scale'][0][1], 10, t('Width was passed correctly'));
+ $this->assertEqual($calls['scale'][0][2], 20, t('Height was passed correctly'));
+ $this->assertEqual($calls['scale'][0][3], TRUE, t('Upscale was passed correctly'));
}
/**
diff --git a/core/modules/simpletest/tests/image_test.module b/core/modules/simpletest/tests/image_test.module
index de640f0..7ddd69e 100644
--- a/core/modules/simpletest/tests/image_test.module
+++ b/core/modules/simpletest/tests/image_test.module
@@ -33,6 +33,7 @@ function image_test_reset() {
'save' => array(),
'settings' => array(),
'resize' => array(),
+ 'scale' => array(),
'rotate' => array(),
'crop' => array(),
'desaturate' => array(),
@@ -46,8 +47,8 @@ function image_test_reset() {
*
* @return
* An array keyed by operation name ('load', 'save', 'settings', 'resize',
- * 'rotate', 'crop', 'desaturate') with values being arrays of parameters
- * passed to each call.
+ * 'scale', 'rotate', 'crop', 'desaturate') with values being arrays of
+ * parameters passed to each call.
*/
function image_test_get_all_calls() {
return variable_get('image_test_results', array());
@@ -58,7 +59,7 @@ function image_test_get_all_calls() {
*
* @param $op
* One of the image toolkit operations: 'get_info', 'load', 'save',
- * 'settings', 'resize', 'rotate', 'crop', 'desaturate'.
+ * 'settings', 'resize', 'scale', 'rotate', 'crop', 'desaturate'.
* @param $args
* Values passed to hook.
*
@@ -122,6 +123,14 @@ function image_test_resize(stdClass $image, $width, $height) {
}
/**
+ * Image tookit's resize operation.
+ */
+function image_test_scale(stdClass $image, $width, $height, $upscale) {
+ _image_test_log_call('scale', array($image, $width, $height, $upscale));
+ return TRUE;
+}
+
+/**
* Image tookit's rotate operation.
*/
function image_test_rotate(stdClass $image, $degrees, $background = NULL) {
diff --git a/core/modules/system/image.gd.inc b/core/modules/system/image.gd.inc
index 39f86dc..551b90c 100644
--- a/core/modules/system/image.gd.inc
+++ b/core/modules/system/image.gd.inc
@@ -96,6 +96,46 @@ function image_gd_resize(stdClass $image, $width, $height) {
}
/**
+ * Implements the image scale effect for the GD toolkit.
+ *
+ * See the documentation of image_scale() for more details.
+ *
+ * @param object $image
+ * An image object returned by image_load().
+ * @param int|null $width
+ * The target width, in pixels. If this value is null, then the scaling will
+ * be based only on the height value.
+ * @param int|null $height
+ * The target height, in pixels. If this value is null, then the scaling will
+ * be based only on the width value.
+ * @param bool $upscale
+ * 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_scale()
+ * @see image_scale_dimensions()
+ */
+function image_gd_scale(stdClass $image, $width, $height, $upscale) {
+ // GD does not natively support a scale operation. So we calculate the new
+ // dimension ourselves and then resize the image.
+ // We use the image_scale_dimensions() function for the calculation.
+ $dimensions = array(
+ 'width' => $image->info['width'],
+ 'height' => $image->info['height']
+ );
+ image_scale_dimensions($dimensions, array('width' => $width, 'height' => $height, 'upscale' => $upscale));
+
+ if ($dimensions['width'] !== $image->info['width'] || $dimensions['height'] !== $image->info['height']) {
+ // Dimensions will change, let resize do the real job.
+ return image_gd_resize($image, $dimensions['width'], $dimensions['height']);
+ }
+ return TRUE;
+}
+
+/**
* Rotate an image the given number of degrees.
*
* @param $image