Making Images Fit Into Pre-Determined Hole

Square Peg, Round Hole?! :blush:

I am adding User Profiles to my website and allowing Users to upload their pictures (just like on SitePoint).

Let’s say that my HTML/CSS has a “hole” for the Profile Picture of 50px by 70x… (Same dimensions as a 5" x 7" photo)

Now let’s say people upload the following dimensions…

50px by 50px
50px by 100 px
60px by 90px
120px by 30px

How do I handle Pictures that can’t be properly resized to fit into a pre-determined slot? :-/

(BTW, I am trying to avoid having to use Image Editing software to resize things on-the-fly because that sounds like a lot of PHP coding…)

Thanks,

Debbie

Hm, sounds like a programming issue to me. You might be able to install something like CKEditor, an editor that allows for some level of image adjustment.

You can go at this from different angles.

  1. Use CSS to center the image in the frame, as was the task in the latest CSS quiz: http://www.sitepoint.com/forums/showthread.php?778366-CSS-Test-Your-CSS-Skills-Number-40-Dead-Centre

  2. Use PHP to crop the image according to some standards (like, crop from the center)

  3. Use PHP in combination with something like jCrop to let people crop their image on the fly.

  4. Don’t do any processing and let your users figure it out for themselves.

Sencha io.src

  • The first line may read like it is only for mobile but that is merely branding gibberish.

How difficult are #1 through #3?

If I just shrink - via HTML - say the Width of the User’s Photo, and take the corresponding Height as-is, maybe that is the easiest solution?

Unless vertical alignment is crucial, that should take care of most issues as long as I limit the “Max Width” and “Max Height”, right?

Debbie

I like to use imagesize from PHP’s GD module for uploaded avatars – but if they are stored off-site (which usually I don’t allow) a great approach is to just set min-width on it, and overflow on it’s container for browsers that know not the min-width. I usually don’t set the height apart from perhaps a max-height on the parent container, because if the layout can’t handle different heights, there’s something wrong with the layout. (Again, see why much of what your PSD jockeys call “web design” I call “not viable for web deployment”)

Really though, if all you’re doing is letting them upload, instead of just copying the temp file during the upload, load it into PHP, check the dimensions, resize as necessary and save it back out as a new file. This has the added bonus that you will KNOW the resulting file is actually an image, and it gives you control over the file format and encoding.

I’ve been working on and off for a about a year now on a drop-in 404 handler to automatically make thumbs for larger images, the code from that for preserving aspect ratio while resizing the width could work well for you. Lemme see if I can extract just that part of the code.

This is simplified down greatly:


	$workImage=imagecreatefrompng(($localFileName);
	$sourceW=imagesx($workImage);
	$sourceH=imagesy($workImage);
	$destW=140;
	$destH=floor($sourceH*$destW/$sourceW);
	$outputImage=imagecreatetruecolor($destW,$destH);
	imagecopyresampled(
		$outputImage,$workImage,
		0,0,
		0,0,
		$destW,$destH,
		$sourceW,$sourceH
	);
	imagepng($outputImage,null,9);

… but shows the ‘basics’ of loading an image, determining the new dimensions, resizing it with a proper resample (higher quality than a flat resize), and writing it back out to a file. Naturally you’d want to add format detection, possibly play with the output sizes to see what format gives you the best new image, detect if the image is actually smaller so that you aren’t enlarging it (which this routine would actually do), etc, etc…

Off Topic:

Side note, GAH I feel dirty every time I deal with FLOOR and languages that don’t actually have integer divides

#1 pretty easy, just read through the thread I linked to

#2 also relatively easy, @deathshadow60; just supplied a working example

#3 quite a bit more involved. to do it correctly it involves uploading via ajax/iframes, which can get quite complicated.

Oh, here’s something a bit more useful – it’s my ‘fit’ method… put in your target width and height (targetResX,targetResY) and it calculates the best ‘fit’ for the image. I also tossed in the bits and pieces for setting the background-color for where it doesn’t ‘fit’.


$targetResX=140;
$targetResY=240;
$backgroundColor=array(128,128,128);

$destW=$targetResX;
$destH=$targetResY;

$sourceW=imagesx($workImage);
$sourceH=imagesy($workImage);

$scale=$sourceW/$destW;
$tempH=floor($sourceH/$scale);

if ($tempH>$destH) {
	$scale=$sourceH/$destH;
	$destW=floor($sourceW/$scale);
} else {
	$destH=$tempH;
}

$destX=floor(($targetResX-$destW)/2);
$destY=floor(($targetResY-$destH)/2);

$outputImage=imagecreatetruecolor($destW,$destH);

$bgColor=imagecolorallocate(
	$outputImage,
	$backgroundColor[0],
	$backgroundColor[1],
	$backgroundColor[2]
);

imagefilledrectangle(
	$outputImage,
	0,0,
	$targetResX,$targetResY,
	$bgColor
);

$copyFunction(
	$outputImage,$workImage,
	$destX,$destY,
	$sourceX,$sourceY,
	$destW,$destH,
	$sourceW,$sourceH
);

A bit more complex, but pretty powerful. Mind you, this still does a ‘upsize’, you’d want to add a if statement so as not to make any changes and just center it if the image is smaller than the target ‘window’.

Thanks for the ideas ScallioXTX and DeathShadow.

But DeathShadow, I think you missed my key question…

If my webpage is laid out to have a 50px by 70px slot, and people are uploading pictures that have different aspect ratios, then what do I do?

Just choose one side as the “dominant” side (e.g. width) and scale the image down to a width of 50px using simple HTML?

Or do I need to get into fancy cropping - to which some of your ideas might be helpful.

No, my layout is not so rigid that I couldn’t handle a 50px by 50px image or a 50px by 100px image, but obviously if I chose a “slot” of 50px by 70px there must have been a reason.

ALSO, DeathShadow, how do I check if I have GD as part of the build on both my Dev Laptop (i.e. MAMP) and my Production VPS (i.e. GoDaddy - don’t hold that against me?!) ??

Thanks,

Debbie

I’d lean towards scaling the image to fit… usually I used what you called the ‘dominant’ side approach, as it’s typically the best way to do it. Cropping I’d really advise against, as users tend to get annoyed if you ‘chop off’ part of their image.

You could even switch it up a bit to make a rescaling routine that will only scale if it’s larger than your ‘max width’ and a certain height, and instead of how mine fills in the ‘not-fit’ area with a solid color, just make a target image that is aspect-correct and smaller than the limits…

PHP has this wonderful function called ‘extension_loaded’… which returns a simple true/false… it can also help to figure out what capabilites are available using the gd_info function.


<?php
if (extension_loaded('gd')) {
	echo '<pre>',print_r(gd_info()),'</pre>';
} else echo 'GD Not Loaded!';
?>

For example, outputs this on my XAMPP install.


Array
(
    [GD Version] => 2.0.35
    [FreeType Support] => 1
    [FreeType Linkage] => with freetype
    [T1Lib Support] => 1
    [GIF Read Support] => 1
    [GIF Create Support] => 1
    [JPEG Support] => 1
    [PNG Support] => 1
    [WBMP Support] => 1
    [XPM Support] => 
    [XBM Support] => 1
    [JIS-mapped Japanese Font Support] => 
)
1

Not that hard a thing to check really – the various ‘supported’ formats are also very helpful; I suggest checking those BEFORE you start trying to call routines to read or write to those file formats.

… and remember WBMP == the useless “wireless Bitmap” monochrome format, and has absolutely nothing to do with windows BMP files (which GD has ZERO support for)… Just a word of warning about something that bit me in the backside when I started playing with GD. (ended up writing my own BMP reader).

DeathShadow,

I don’t have this one in my production environment…


   [T1Lib Support] => 

Is that a problem for what you suggested?

Debbie

Wow you are all making this so much more complex than it really needs to be…

And the answer is…

Debbie

The solution I recommended was here. You can save a lot of time and hassle by using that service. I know because I created my own custom solution for a framework I am building in my down time to handle all image resizing. Than I discovered Sencha io and was like what a waste. Though I still use my system because it has some neat little image transformations built in and permutation caching. Still I wish I would have known about Sencha io before I would have reinvented most of the wheel. I use it on just about every project at work now and it is great. It allows users to upload whatever crappy images they want and with ease fit them into an allocated space regardless of orientation or original dimensions.

You also have to realize that once proper error handling is added to what has been provided by the other members the complexity and length will increase. Than you go even further and consider resizing more images. It is unlikely that as a site grows the function only will be needed for user avatars. What that means is creating reusable logic that can be applied to all cases unless redundancy is the chosen in the approach. So it all really gets more complex as you start thinking about other scenarios or contexts which resizing may need to occur. I always like to anticipate these things ahead of time so that when I am building something new it can be reused in different contexts considering I absolutely hate repeating similar logic. Though it does take much more time to create reusable code than to spaghetti something together.

Sencha is cute when you know you can trust the source image, don’t mind the overhead of the source images size, and are certain it’s an image. We’re talking user uploads here – which means verify, resize if too large and copy – even if you’re going to use a resizing ‘service’.

At which point, spend the extra five to ten lines for a resize algorythm and cut out the middle-man… or perhaps copy the file to a temp web location, call sencha to get the resized version, and use the version they send you…

In any case, DD’s looking at dealing with a user upload – so checks will need to be run and it’s NOT as simple as saying “oh just send the image request to some other service” since she’ll still need to verify that what was uploaded is in fact an image and copy it to an avatars directory. The HANDFUL of lines needed to do a resize when copying isn’t “too complex” – bloating out the markup and making requests to some other server that recursively calls your own server is.

Even if she was to use Sencha, that’s only a tiny piece of the entire process.

DeathShadow brings up some interesting points.

I have wasted the last 2 1/2 days of my life trying to learn how to write a secure Image Upload module. (God is that a large and generally poorly documented area?!)

Being paranoid like I am, I think that it makes sense to not only resize images to make them fit into my page “slot”, but also for security reasons.

The code that DeathShadow posted doesn’t seem too onerous, although I’m sure I’ll come back with more questions. (Right now I am focused on writing “bullet-proof” PHP and learning Apache to make things safe. Not sure if/when that’ll ever happen…)

BTW, I couldn’t get that website to load, so I ditched it…

Thanks,

Debbie

Lemme guess, got javascript disabled? The sensha documentation pages are SO … I’ll skip the string of expletives for now… that all you get is grinding gears for their PLAINTEXT DOCUMENTATION PAGES.

Usually a good sign they’re not even worth trying to use… iframes, AJAX to pretend there’s framesets, and massive libraries – their documentation being a stellar example of EXACTLY what I mean by pointless idiotic bloat in site deployment — with it’s two MEGABYTES of javascript, 328k of CSS… to deliver 25k of plaintext.

… and lemme guess, they’re dumb enough to say “oh but it’s easier this way” – RIGHT.

Seriously, it’s plaintext documentation – Whiskey Tango Foxtrot!!! Poster child for everything WRONG with Internet development today.

The difference between them and you though is they are making sh*t loads of money and have a large developer following. While all you do is preach and make none. Of course you have the time to do everything perfectly… you don’t actually have to deal with a team, past mistakes and business requirements. Once you throw those all into the mix nothing is ever perfect. That is just the way it is when you have to deal with practical professional world not the debbie or deathshadow world of I have all the time in world to create something and no one to dictate anything nor dependencies that other people built to deal with.

The reason nothing works without JavaScript because they are using their javaScript framework. While I can agree to an extent that the documentation probably should be accessible I get they want to plug their work. Considering their framework really isn’t for building anything besides action intensive admin areas I can some what agree. Though that doesn’t take away from the power of io src in the least. It works perfectly fine without JS. I guess it is just a matter of opening yourself up to new things or you can stay closed off in your own little world. Everything is inherently flawed if you look close enough at it. Spending 3 days lets assume 8 x 3 = 24hrs of a clients money when you could use something like io src which would take an hour to understand is inherently flawed business.

@debbie

After that 24 hours of learning where exactly are you? From what I gather not much farther than you started. So would this have been paid work you you might end up spending an estimated week or more. When that could have been at the very least halved by using others tools and not reinventing the wheel for at least the resizing portion here.

Hey look everybody, it’s the “I can’t reasonably attack the position so I’ll attack the person” response. Hey, isn’t that one of the Lame excuses?!?

leading to the question "FOR WHAT?!?"7

By alienating potential users and showing what total rubbish it is? Yeah, that’ll work out great. Good luck with that…

Then why are they using it for DOCUMENTATION if it’s for ‘action intensive admin areas’ – PHP.net doesn’t need this nonsense and is a billion times more useable and accessible.

I agree it’s a cute service, and if all you need is resizing, it’ll get the job done… unfortunately for what’s being done that’s just a SMALL minute piece of the puzzle that could just as easily be handled by a handful of lines of fairly simple code… handling the upload itself is certainly MUCH more complex than just calling some stock off the shelf site or some simple code to resize it… that’s the easy part!

Which is why I suggested it might at least be worth looking at to save the image to a temp dir, call them, and then save their result… in either case you’d need to sanitize the upload locally – which is why I lean towards resizing it locally “while you’re already in there”… because to be frank… resizing with GD as needed is so simple you shouldn’t need a whole lot of time spent on that part of it. Check the image, load the image, run some simple divide/mult/calcs, make a target surface of the desired size, and write it out. It’s not rocket science or some massive “reinvention of the wheel”.

Reinventing the wheel would be making a RLE8 decoder for windows BMP support thanks to GD not supporting BMP’s…


function winRLE8Decode($data,$height,$rowSize) {
	$newData=str_repeat(chr(0),$height*$rowSize);
	$dataLength=strlen($data);
	$fromOffset=$toOffset=0;
	while ($fromOffset<$dataLength) {
		$count=ord($data[$fromOffset++]);
		if ($count==0) {
			$count=ord($data[$fromOffset++]);
			switch ($count) {
				case 0:	// end of scanline
					if (($tLeft=$toOffset % $rowSize)>0) {
						$toOffset+=($rowSize-$tLeft);
					}
				break;
				case 1: // end of data
					return $newData;
				case 2: // offset
					$toOffset+=(
						ord($data[$fromOffset++]) +
						ord($data[$fromOffset++])*$rowSize
					);
				break;
				default: // uncompressed run
					$padding=$count%2;
					while ($count-- > 0) $newData[$toOffset++]=$data[$fromOffset++];
					$fromOffset+=$padding;
			}
		} else { // compressed run
			$b=$data[$fromOffset++];
			while ($count-- > 0) $newData[$toOffset++]=$b;
		}
	}
	return $newData;
}

Uhm… yeah… ok, my bad…

The question also becomes one of does Debbie want to learn to WRITE tools like io.src, or does she want to just use them? The difference between a developer and a user.

… and it seems SO many developers don’t want to actually BE developers… or the most important thing that comes with it; being able to know how to fix it when it breaks. Off the shelf, that often becomes “wait around with your thumb up your backside until when/if the tool maker gets around to it”.

@ DD … To take a different slant on all this—and as one who always looks for the easy way out—have you considered not having avatars on your site? Are they really needed? What do they add, except mess, unnecessary bandwidth usage … and a lot of headaches that this thread details. For mine, the only “pre-determined hole” avatars belong in is unmentionable (along with most other decorative images on the web). If you need some kind of decoration, you could just have a set of your own decorative avatars that apply randomly to comments*, or perhaps have some other nice (and unique) CSS decoration for each post/comment. Just a thought, anyhow.

  • a la some popular feedback forums, which serve up attractive default avatars if users doesn’t add their own.