If you upload a user picture that respects the limits in maximum size, width and height, it is processed anyway and the processing adds unecessary noise (quite visible) to the picture by compressing it. (It remains otherwise the same width and height, of course.)

Probably

  // Check that uploaded file is an image, with a maximum file size
  // and maximum height/width.
  $info = image_get_info($file->filepath);
  list($maxwidth, $maxheight) = explode('x', variable_get('user_picture_dimensions', '85x85'));

  if (!$info || !$info['extension']) {
    form_set_error('picture_upload', t('The uploaded file was not an image.'));
  }
  else if (image_get_toolkit()) {
    image_scale($file->filepath, $file->filepath, $maxwidth, $maxheight);
  }
  else if (filesize($file->filepath) > (variable_get('user_picture_file_size', '30') * 1000)) {
    form_set_error('picture_upload', t('The uploaded image is too large; the maximum file size is %size kB.', array('%size' => variable_get('user_picture_file_size', '30'))));
  }
  else if ($info['width'] > $maxwidth || $info['height'] > $maxheight) {
    form_set_error('picture_upload', t('The uploaded image is too large; the maximum dimensions are %dimensions pixels.', array('%dimensions' => variable_get('user_picture_dimensions', '85x85'))));
  }
Code
function image_scale($source, $destination, $width, $height) {
  $info = image_get_info($source);

  // don't scale up
  if ($width > $info['width'] && $height > $info['height']) {
    return FALSE;
  }

  $aspect = $info['height'] / $info['width'];
  if ($aspect < $height / $width) {
    $width = (int)min($width, $info['width']);
    $height = (int)round($width * $aspect);
  }
  else {
    $height = (int)min($height, $info['height']);
    $width = (int)round($height / $aspect);
  }

  return image_toolkit_invoke('resize', array($source, $destination, $width, $height));
} 
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Chill35’s picture

Title: User picture gets resized with loss even when it's the right size or smaller » User picture gets resized with loss even when it's the right size

In the image_scale function why not doing this (added the equal size ) :

// don't scale up
if ($width >= $info['width'] && $height >= $info['height']) {
return FALSE;
}

Chill35’s picture

Bigger or equal. I mean.

Chill35’s picture

One way to solve this is to change one line in the image_scale function, which is not in the user.module. (But I guess there are other ways...)

Change this :

 // don't scale up
if ($width > $info['width'] && $height > $info['height']) {
return FALSE;
}
 // don't scale up
if ($width >= $info['width'] && $height >= $info['height']) {
return FALSE;
}

The reason why I think this is a bug to fix is that scenario :

Joe Bloe reads the restrictions, for example that his picture cannot exceed 85 by 85 pixels, goes into Photoshop or another app and resizes his picture EXACTLY to 85 by 85 pixels, makes it look real nice using "Save For Web". Then he uploads it and finds all that noise on it.

Chill35’s picture

Is it just me or the order in which the "if" and "else if" conditions are found in the user module does not seem logical...? It is probably me.

if (image is not a picture) {
say that 'The uploaded file was not an image.'
}
else if (we got the GDI) {
scale down the picture if necessary -- and even when it's not ;)
}
else if (its's still too big in kBytes) {
say it is so too heavy
}
else if (if the picture is too big !!! How could it be if it has been resized ? LOL...) {
say it is too big in height and width
}

Chill35’s picture

I tested the upload user picture widget and indeed there is something wrong with that code, I think.

 if (!$info || !$info['extension']) {
form_set_error('picture_upload', t('The uploaded file was not an image.'));
}
else if (image_get_toolkit()) {
image_scale($file->filepath, $file->filepath, $maxwidth, $maxheight);
}
else if (filesize($file->filepath) > (variable_get('user_picture_file_size', '30') * 1000)) {
form_set_error('picture_upload', t('The uploaded image is too large; the maximum file size is %size kB.', array('%size' => variable_get('user_picture_file_size', '30'))));
}
else if ($info['width'] > $maxwidth || $info['height'] > $maxheight) {
form_set_error('picture_upload', t('The uploaded image is too large; the maximum dimensions are %dimensions pixels.', array('%dimensions' => variable_get('user_picture_dimensions', '85x85'))));
}

The 2 last conditions are never met.

  1. If I upload a picture that exceeds the width and height size, say a 200px x 400px picture, it uploads successfully and it is scaled down to 85px x 85px. This could be a feature, but then the last condition above is not useful (as it is never met).
  2. If I upload a picture that is too huge in weight (over 20Kbytes for example if I have set my limit to 20Kbytes), I get nor error message either. That could be a feature too, but again the before-last condition is useless.
Chill35’s picture

I tested with the change in image.inc :

FROM THIS (line 120) :

// don't scale up
if ($width > $info['width'] && $height > $info['height']) {
return FALSE;
}

TO THIS :

// don't scale up
if ($width >= $info['width'] && $height >= $info['height']) {
return FALSE;
}

And pictures who are the right size (on the limit) are no longer processed : they are exactly the same size (in Bytes, not smaller) and they have no JPEG noise added to them (loss in quality)...
so I guess it works.

webchick’s picture

Thanks for tracking this down! How about making a patch for this?

There are some instructions here: http://drupal.org/patch

There's also some specific stuff if you're on Windows, for example: http://drupal.org/node/60234

Chill35’s picture

Hi Webchick!

I have downloaded UnxUtils and will figure out how to create a patch using diff in the next days.

Thanks for the links.

webchick’s picture

No problem. :) Feel free to reply back if you get stuck.

Chill35’s picture

Assigned: Unassigned » Chill35
Priority: Minor » Normal
Status: Active » Needs review
FileSize
370 bytes

This should fix a bug in includes/image.inc : in a scaling operation, if the target width and height are equal to the width and height of the original image, the image is still processed. The bug as it is experienced now shows that the image is processed unnecessarily. The resulting image has the same dimensions (w and h) but it's smaller in Bytes, it has been compressed again, so there has been quality loss. Pictures that not require scaling and respect KBytes limits should be left alone.

In the user module, Drupal tells the user about certain restrictions concerning the width, height and weight of the picture he may download to represent himself. If the user knows that he must, for example, download a picture not bigger than 85 by 85 pixels, he may create in his favorite image app a picture with this exact dimension and may be puzzled that his image once uploaded shows noise. That’s only one example. With the user uploading pictures with dimensions restrictions, the problem will probably be the same.

I changed the image_scale function :

function image_scale($source, $destination, $width, $height) {
  $info = image_get_info($source);

  // don't scale up
  if ($width > $info['width'] && $height > $info['height']) {
    return FALSE;
  }

To that :

function image_scale($source, $destination, $width, $height) {
  $info = image_get_info($source);

  // don't scale up
  if ($width >= $info['width'] && $height >= $info['height']) {
    return FALSE;
  }</image>

The problem is the same in both 4.7.4 and 5.x

The patches attached are for review. They are identical as far as I can tell.

Chill35’s picture

That's for 5.x.

dvessel’s picture

Status: Needs review » Reviewed & tested by the community
FileSize
575 bytes

Nice find. Here's a proper patch. Works as advertised.

Chill35’s picture

Assigned: Chill35 » Unassigned
Steven’s picture

Status: Reviewed & tested by the community » Fixed

Good catch. Committed to HEAD and 4.7.

Anonymous’s picture

Status: Fixed » Closed (fixed)