diff --git includes/image.inc includes/image.inc index b04943b..1f22907 100644 --- includes/image.inc +++ includes/image.inc @@ -178,12 +178,14 @@ function image_scale_and_crop(stdClass $image, $width, $height) { } /** - * Scales an image to the given width and height while maintaining aspect ratio. + * Scales dimensions to the given width and height while maintaining aspect + * ratio. * - * The resulting image can be smaller for one or both target dimensions. + * The resulting dimensions can be smaller for one or both target dimensions. * - * @param $image - * An image object returned by image_load(). + * @param $dimensions + * Dimensions to be modified - an array with components width and height, in + * pixels. * @param $width * The target width, in pixels. This value is omitted then the scaling will * based only on the height value. @@ -194,14 +196,10 @@ function image_scale_and_crop(stdClass $image, $width, $height) { * Boolean indicating that files smaller than the dimensions will be scaled * up. This generally results in a low quality image. * - * @return - * TRUE or FALSE, based on success. - * - * @see image_load() - * @see image_scale_and_crop() + * @see image_scale() */ -function image_scale(stdClass $image, $width = NULL, $height = NULL, $upscale = FALSE) { - $aspect = $image->info['height'] / $image->info['width']; +function image_dimensions_scale(&$dimensions, $width = NULL, $height = NULL, $upscale = FALSE) { + $aspect = $dimensions['height'] / $dimensions['width']; if ($upscale) { // Set width/height according to aspect ratio if either is empty. @@ -214,19 +212,52 @@ function image_scale(stdClass $image, $width = NULL, $height = NULL, $upscale = $height = !empty($height) ? $height : 9999999; // Don't scale up. - if (round($width) >= $image->info['width'] && round($height) >= $image->info['height']) { - return TRUE; + if (round($width) >= $dimensions['width'] && round($height) >= $dimensions['height']) { + return; } } if ($aspect < $height / $width) { - $height = $width * $aspect; + $dimensions['width'] = $width; + $dimensions['height'] = (int) round($width * $aspect); } else { - $width = $height / $aspect; + $dimensions['width'] = (int) round($height / $aspect); + $dimensions['height'] = $height; } +} + +/** + * Scales an image to the given width and height while maintaining aspect + * ratio. + * + * The resulting image can be smaller for one or both target dimensions. + * + * @param $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 + * Boolean indicating that files smaller than the dimensions will be scaled + * up. This generally results in a low quality image. + * + * @return + * TRUE or FALSE, based on success. + * + * @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; + image_dimensions_scale($dimensions, $width, $height, $upscale); - return image_resize($image, $width, $height); + return image_resize($image, $dimensions['width'], $dimensions['height']); } /** diff --git modules/image/image.api.php modules/image/image.api.php index 5b635ec..f23fcbb 100644 --- modules/image/image.api.php +++ modules/image/image.api.php @@ -22,6 +22,10 @@ * following items: * - "label": The human-readable name of the effect. * - "effect callback": The function to call to perform this image effect. + * - "dimensions passthrough": (optional) Set this item if the effect doesn't + * change the dimensions of the image. + * - "dimensions callback": (optional) The function to call to transform + * dimensions for this effect. * - "help": (optional) A brief description of the effect that will be shown * when adding or configuring this image effect. * - "form callback": (optional) The name of a function that will return a @@ -37,7 +41,8 @@ function hook_image_effect_info() { $effects['mymodule_resize'] = array( 'label' => t('Resize'), 'help' => t('Resize an image to an exact set of dimensions, ignoring aspect ratio.'), - 'effect callback' => 'mymodule_resize_image', + 'effect callback' => 'mymodule_resize_effect', + 'dimensions callback' => 'mymodule_resize_dimensions', 'form callback' => 'mymodule_resize_form', 'summary theme' => 'mymodule_resize_summary', ); @@ -56,6 +61,7 @@ function hook_image_effect_info() { function hook_image_effect_info_alter(&$effects) { // Override the Image module's crop effect with more options. $effect['image_crop']['effect callback'] = 'mymodule_crop_effect'; + $effect['image_crop']['dimensions callback'] = 'mymodule_crop_dimensions'; $effect['image_crop']['form callback'] = 'mymodule_crop_form'; } diff --git modules/image/image.effects.inc modules/image/image.effects.inc index 122af6c..4317d1d 100644 --- modules/image/image.effects.inc +++ modules/image/image.effects.inc @@ -14,6 +14,7 @@ function image_image_effect_info() { 'label' => t('Resize'), 'help' => t('Resizing will make images an exact set of dimensions. This may cause images to be stretched or shrunk disproportionately.'), 'effect callback' => 'image_resize_effect', + 'dimensions callback' => 'image_resize_dimensions', 'form callback' => 'image_resize_form', 'summary theme' => 'image_resize_summary', ), @@ -21,6 +22,7 @@ function image_image_effect_info() { 'label' => t('Scale'), 'help' => t('Scaling will maintain the aspect-ratio of the original image. If only a single dimension is specified, the other dimension will be calculated.'), 'effect callback' => 'image_scale_effect', + 'dimensions callback' => 'image_scale_dimensions', 'form callback' => 'image_scale_form', 'summary theme' => 'image_scale_summary', ), @@ -28,6 +30,7 @@ function image_image_effect_info() { 'label' => t('Scale and crop'), 'help' => t('Scale and crop will maintain the aspect-ratio of the original image, then crop the larger dimension. This is most useful for creating perfectly square thumbnails without stretching the image.'), 'effect callback' => 'image_scale_and_crop_effect', + 'dimensions callback' => 'image_resize_dimensions', 'form callback' => 'image_resize_form', 'summary theme' => 'image_resize_summary', ), @@ -35,6 +38,7 @@ function image_image_effect_info() { 'label' => t('Crop'), 'help' => t('Cropping will remove portions of an image to make it the specified dimensions.'), 'effect callback' => 'image_crop_effect', + 'dimensions callback' => 'image_resize_dimensions', 'form callback' => 'image_crop_form', 'summary theme' => 'image_crop_summary', ), @@ -42,6 +46,7 @@ function image_image_effect_info() { 'label' => t('Desaturate'), 'help' => t('Desaturate converts an image to grayscale.'), 'effect callback' => 'image_desaturate_effect', + 'dimensions passthrough' => TRUE, ), 'image_rotate' => array( 'label' => t('Rotate'), @@ -80,6 +85,24 @@ function image_resize_effect(&$image, $data) { } /** + * Image dimensions callback; Resize. + * + * @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 resize effect with the + * following items: + * - "width": An integer representing the desired width in pixels. + * - "height": An integer representing the desired height in pixels. + */ +function image_resize_dimensions(&$dimensions, $data) { + + $dimensions['width'] = $data['width']; + $dimensions['height'] = $data['height']; +} + +/** * Image effect callback; Scale an image resource. * * @param $image @@ -115,6 +138,25 @@ 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 upscalled if + * the dimensions are larger than the original image. + */ +function image_scale_dimensions(&$dimensions, $data) { + + image_dimensions_scale($dimensions, $data['width'], $data['height'], $data['upscale']); +} + +/** * Image effect callback; Crop an image resource. * * @param $image diff --git modules/image/image.field.inc modules/image/image.field.inc index 07cc1e0..e2dd57f 100644 --- modules/image/image.field.inc +++ modules/image/image.field.inc @@ -206,6 +206,16 @@ function image_field_prepare_view($entity_type, $entities, $field, $instances, $ */ function image_field_presave($entity_type, $entity, $field, $instance, $langcode, &$items) { file_field_presave($entity_type, $entity, $field, $instance, $langcode, $items); + + // Determine image dimensions. + foreach ($items as &$item) { + $info = image_get_info(file_load($item['fid'])->uri); + + if (is_array($info)) { + $item['width'] = $info['width']; + $item['height'] = $info['height']; + } + } } /** @@ -344,9 +354,28 @@ function image_field_widget_process($element, &$form_state, $form) { // Add the image preview. if ($element['#file'] && $widget_settings['preview_image_style']) { + + $variables = array( + 'style_name' => $widget_settings['preview_image_style'], + 'path' => $element['#file']->uri, + ); + + if (isset($element['#value']['width']) && isset($element['#value']['height'])) { + $variables['width'] = $element['#value']['width']; + $variables['height'] = $element['#value']['height']; + } + else { + $info = image_get_info($element['#file']->uri); + + if (is_array($info)) { + $variables['width'] = $info['width']; + $variables['height'] = $info['height']; + } + } + $element['preview'] = array( '#type' => 'markup', - '#markup' => theme('image_style', array('style_name' => $widget_settings['preview_image_style'], 'path' => $element['#file']->uri)), + '#markup' => theme('image_style', $variables), ); } @@ -532,6 +561,12 @@ function theme_image_formatter($variables) { 'path' => $item['uri'], 'alt' => $item['alt'], ); + + if (isset($item['width']) && isset($item['height'])) { + $image['width'] = $item['width']; + $image['height'] = $item['height']; + } + // Do not output an empty 'title' attribute. if (drupal_strlen($item['title']) > 0) { $image['title'] = $item['title']; diff --git modules/image/image.install modules/image/image.install index 121e8c7..0fdded3 100644 --- modules/image/image.install +++ modules/image/image.install @@ -130,6 +130,18 @@ function image_field_schema($field) { 'length' => 128, 'not null' => FALSE, ), + 'width' => array( + 'description' => 'The width of the image in pixels.', + 'type' => 'int', + 'not null' => FALSE, + 'unsigned' => TRUE, + ), + 'height' => array( + 'description' => 'The height of the image in pixels.', + 'type' => 'int', + 'not null' => FALSE, + 'unsigned' => TRUE, + ), ), 'indexes' => array( 'fid' => array('fid'), diff --git modules/image/image.module modules/image/image.module index d2d081c..bcfb439 100644 --- modules/image/image.module +++ modules/image/image.module @@ -1093,6 +1093,26 @@ function image_effect_apply($image, $effect) { * @ingroup themeable */ function theme_image_style($variables) { + + // Determine the dimensions of the styled image. + if (isset($variables['width']) && isset($variables['height'])) { + module_load_include('inc', 'image', 'image.effects'); + $style = image_style_load($variables['style_name']); + + foreach ($style['effects'] as $effect) { + if (isset($effect['dimensions passthrough'])) { + continue; + } + + if (!isset($effect['dimensions callback'])) { + unset($variables['width'], $variables['height']); + break; + } + + $effect['dimensions callback']($variables, $effect['data']); + } + } + $variables['path'] = image_style_url($variables['style_name'], $variables['path']); return theme('image', $variables); } diff --git modules/image/image.test modules/image/image.test index eaa6ee0..ad5aa62 100644 --- modules/image/image.test +++ modules/image/image.test @@ -667,6 +667,8 @@ class ImageFieldDisplayTestCase extends ImageFieldTestCase { $image_uri = $node->{$field_name}[LANGUAGE_NONE][0]['uri']; $image_info = array( 'path' => $image_uri, + 'width' => 40, + 'height' => 20, ); $default_output = theme('image', $image_info); $this->assertRaw($default_output, t('Default formatter displaying correctly on full node view.')); @@ -712,6 +714,8 @@ class ImageFieldDisplayTestCase extends ImageFieldTestCase { // image style callback paths. $this->drupalGet(image_style_url('thumbnail', $image_uri)); $image_info['path'] = image_style_path('thumbnail', $image_uri); + $image_info['width'] = 100; + $image_info['height'] = 50; $default_output = theme('image', $image_info); $this->drupalGet('node/' . $nid); $this->assertRaw($default_output, t('Image style thumbnail formatter displaying correctly on full node view.')); @@ -761,6 +765,8 @@ class ImageFieldDisplayTestCase extends ImageFieldTestCase { $node = node_load($nid, NULL, TRUE); $image_info = array( 'path' => image_style_url('medium', $node->{$field_name}[LANGUAGE_NONE][0]['uri']), + 'width' => 220, + 'height' => 110, ); $default_output = theme('image', $image_info); $this->assertRaw($default_output, t("Preview image is displayed using 'medium' style.")); @@ -770,6 +776,8 @@ class ImageFieldDisplayTestCase extends ImageFieldTestCase { 'path' => $node->{$field_name}[LANGUAGE_NONE][0]['uri'], 'alt' => $this->randomName(), 'title' => $this->randomName(), + 'width' => 40, + 'height' => 20, ); $edit = array( $field_name . '[' . LANGUAGE_NONE . '][0][alt]' => $image_info['alt'], @@ -816,6 +824,8 @@ class ImageFieldDisplayTestCase extends ImageFieldTestCase { $node = node_load($nid, NULL, TRUE); $image_info = array( 'path' => $node->{$field_name}[LANGUAGE_NONE][0]['uri'], + 'width' => 40, + 'height' => 20, ); $image_output = theme('image', $image_info); $this->drupalGet('node/' . $nid);