31

I'm having a little bit of a problem scaling my images to a properly predefined size. I was wondering - since it is purely mathematics, if there's some sort of common logical algorithm that works in every language (PHP, ActionScript, Javascript etc.) to scale images proportionally.

I'm using this at the moment:

var maxHeight   = 300;
var maxWidth    = 300;

var ratio:Number    =   height / width;

if (height > maxHeight) {
    height = maxHeight;
    width = Math.round(height / ratio);
} 

else if(width > maxWidth) {
    width = maxWidth;
    height = Math.round(width * ratio);
}

But it doesn't work properly. The images scales proportionately, sure enough, but the size isn't set at 300 (either in width or in height). It kind of makes sense, but I was wondering if there's a fool-proof, easy way to scale images proportionally.

Perception
  • 79,279
  • 19
  • 185
  • 195
Hot Pixel
  • 313
  • 1
  • 3
  • 5

6 Answers6

65
ratio = MIN( maxWidth / width, maxHeight/ height );
width = ratio * width;
height = ratio * height;

Make sure all divides are floating-point.

Dark Shikari
  • 7,941
  • 4
  • 26
  • 38
4

Dark Shikari has it. Your solution as stated in the question fails because you aren't first establishing which dimenson's size-to-maxsize ratio is greater and then reducing both dimensions by that greater ratio.

Your current solution's use of a serial, conditional analysis of one potential dimensional violation and then the other won't work.

Note also that if you want to upscale images, your current solution won't fly, and Dark Shikari's again will.

Josh Millard
  • 1,093
  • 6
  • 8
2

I'd recommend not writing this code yourself; there are myriads of pixel-level details that take a serious while to get right. Use ImageMagick, it's the best graphics library out there.

Alex Weinstein
  • 9,823
  • 9
  • 42
  • 59
2

Here is how I do it:

+ (NSSize) scaleHeight:(NSSize)origSize 
             newHeight:(CGFloat)height {

    NSSize newSize = NSZeroSize;
    if ( origSize.height == 0 ) return newSize;

    newSize.height = height;
    CGFloat factor = ( height / origSize.height );
    newSize.width  = (origSize.width * factor );

    return newSize;
}

+ (NSSize) scaleWidth:(NSSize)origSize 
             newWidth:(CGFloat)width {

    NSSize newSize = NSZeroSize;
    if ( origSize.width == 0 ) return newSize;

    newSize.width  = width;
    CGFloat factor = ( width / origSize.width );
    newSize.height = (origSize.height * factor );

    return newSize;
}
Arvin
  • 2,516
  • 27
  • 30
0

Here's a function I've developed for my site, you might want to use. It's based on your answer above.

It does other things not only the image processing - please remove everything which is unnecessary.

<?php

$thumb_width    = 500;
$thumb_height   = 500;

if ($handle = opendir('to-do')) {
    echo "Directory handle: $handle<br />";
    echo "Files:<br /><br />";

    /* This is the correct way to loop over the directory. */
    while (false !== ($file = readdir($handle))) {

        if ( ($file != ".") && ($file != "..") ){
            echo "$file";

            $original_path = "to-do/" . $file;

            $source_image = ImageCreateFromJPEG( $original_path );
            $thumb_width = $thumb_width;
            $thumb_height = $thumb_height;

            // Create the image, of the required size
            $thumbnail = imagecreatetruecolor($thumb_width, $thumb_height);
            if($thumbnail === false) {
                //creation failed -- probably not enough memory
                return null;
            }

            // Fill the image with a white color (this will be visible in the padding around the image,
            // if the aspect ratios of the image and the thumbnail do not match)
            // Replace this with any color you want, or comment it out for black.
            // I used grey for testing =)
            $fill = imagecolorallocate($thumbnail, 255, 255, 255);
            imagefill($thumbnail, 0, 0, $fill);

            // Compute resize ratio
            $hratio = $thumb_height / imagesy($source_image);
            $wratio = $thumb_width / imagesx($source_image);
            $ratio = min($hratio, $wratio);

            // If the source is smaller than the thumbnail size, 
            // Don't resize -- add a margin instead
            // (that is, dont magnify images)
            if ($ratio > 1.0)
                $ratio = 1.0;

            // Compute sizes
            $sy = floor(imagesy($source_image) * $ratio);
            $sx = floor(imagesx($source_image) * $ratio);

            // Compute margins
            // Using these margins centers the image in the thumbnail.
            // If you always want the image to the top left, set both of these to 0
            $m_y = floor(($thumb_height - $sy) / 2);
            $m_x = floor(($thumb_width - $sx) / 2);

            // Copy the image data, and resample
            // If you want a fast and ugly thumbnail, replace imagecopyresampled with imagecopyresized
            if (!imagecopyresampled($thumbnail, $source_image,
                $m_x, $m_y, //dest x, y (margins)
                0, 0, //src x, y (0,0 means top left)
                $sx, $sy,//dest w, h (resample to this size (computed above)
                imagesx($source_image), imagesy($source_image)) //src w, h (the full size of the original)
            ) {
                //copy failed
                imagedestroy($thumbnail);
                return null;
            }

            /* Set the new file name */
            $thumbnail_file_name = $file;

            /* Apply changes on the original image and write the result on the disk */
            ImageJPEG( $thumbnail, $complete_path . "done/" . $thumbnail_file_name );
            unset($source_image);
            unset($thumbnail);
            unset($original_path);
            unset($targeted_image_size);

            echo " done<br />";

        }

    }

    closedir($handle);
}

?>
Flexo
  • 87,323
  • 22
  • 191
  • 272
Bogdan Ciocoiu
  • 313
  • 4
  • 13
0

well I made this function to scale proportional, it uses a given width, height, and optionally the max width/height u want (depends on the given width and height)

   function scaleProportional($img_w,$img_h,$max=50)
   {
       $w = 0;
       $h = 0;

       $img_w > $img_h ? $w = $img_w / $img_h : $w = 1;
       $img_h > $img_w ? $h = $img_h / $img_w : $h = 1;

       $ws = $w > $h ? $ws = ($w / $w) * $max : $ws = (1 / $h) * $max;
       $hs = $h > $w ? $hs = ($h / $h) * $max : $hs = (1 / $w) * $max;

       return array(
           'width'=>$ws,
           'height'=>$hs
       );
   }

usage:

            $getScale = scaleProportional(600,200,500);
            $targ_w = $getScale['width']; //returns 500
            $targ_h = $getScale['height']; //returns 16,6666667
Nicholas
  • 5,430
  • 3
  • 21
  • 21