Link to home
Start Free TrialLog in
Avatar of andrewaiello
andrewaiello

asked on

Issue width JPG upload with conversion to PNG in php

I'm getting some odd behavior with an image upload function I have.  I can't seem to see where it is coming from and was hoping someone could take a look.

I have a function which I call to resize and convert images to png.  I have a parameter option to take the passed width and height and center/resize the passed image to that width and height width transparency to either the left and right or top and bottom.

So if I have an image (.jpg or .png) which is 400px X 200px and I pass the width and height of 200px X 200px.  I would end up with a 200px X 200px .png with the top 50px transparent and the bottom 50px transparent.

I am getting almost that exact behavior with exception to 2 end results.

1. If the image is wider then the destination width and height (ratio wise) then I get a 1px X 1px black dot in the 0px X 0px location with a black line just above my original image (save for the first pixel on the right which is transparent)

So input (820px X 520px all blue image with the function call
upload($location, 801, 543, 2)

Open in new window

)
output  (http://dansdomain.com/Test/result-wider.png)

2. If the image is taller then the destination width and height (ratio wise) then I get a .png image which is 100% as expected with exception to a black pixel at the location 1px X 1px.  

So input (790px X 600px all blue image with the function call
upload($location, 801, 543, 2)

Open in new window

)

output (http://dansdomain.com/Test/result-taller.png)

Here is my function:

if(!function_exists("upload")){
	//$image = $image file name
	//$width = intended width of resized image, if 0 it will proportion to height, overrides proportion
	//$height = intended width of resized image, if 0 it will proportion to width, overrides proportion
	//$proportion = 2,1,0;
	//------ 2 = Preserve proportions while adding a border to fill width and height  
	//------ 1 = retain proportion to fit within both given width and height
	//------ 0 = disregard proportions and resize to the exact width and height
	
	
	function upload($image, $width, $height, $proportion){
		// IS GD HERE?
		
		$gdv = get_gd_info();
		
		if (!$gdv){ 
			return FALSE;
		}
		
		// GET AN IMAGE THING
		
		$ext = trim(strtolower(end(explode('.', $image))));
		
		list($imageX, $imageY, $type) = getimagesize($image);	//gets information about new server image
				
		// GET THE LESSER OF THE RATIO OF THUMBNAIL H OR W DIMENSIONS
		$ratio_w = ($width / $imageX);
		$ratio_h = ($height / $imageY);
		$ratio   = ($ratio_w < $ratio_h) ? $ratio_w : $ratio_h;		
		
		if($width == 0){
			
			if($imageY > $height){
				$newHeight = $height;
				$newWidth = ($height/$imageY) * $imageX;	
			}else{
				$newHeight = $imageY;
				$newWidth = $imageX;	
			}
			
			$width = $newWidth;
			$height = $newHeight;
			
			// COMPUTE THUMBNAIL IMAGE CENTERING OFFSETS
			$fromMidX = 0;
			$fromMidY = 0;
			
		}elseif($height == 0){
			
			if ($imageX > $width){
				$newWidth = $width;
				$newHeight = ($width/$imageX) * $imageY;
			}else{
				$newHeight = $imageY;
				$newWidth = $imageX;	
			}
			
			$width = $newWidth;
			$height = $newHeight;
			
			// COMPUTE THUMBNAIL IMAGE CENTERING OFFSETS
			$fromMidX = 0;
			$fromMidY = 0;
			
		}elseif($proportion == 2){
			
			// COMPUTE THUMBNAIL IMAGE DIMENSIONS
			$newWidth = $imageX * $ratio;
			$newHeight = $imageY * $ratio;
			
			
			
			
			// COMPUTE THUMBNAIL IMAGE CENTERING OFFSETS
			$fromMidX = ($width - $newWidth) / 2.0;
			$fromMidY = ($height - $newHeight) / 2.0;
			
			
		}elseif($proportion == 1){
			
			if ($imageX > $width){
				$newHeight = ($width/$imageX) * $imageY;
				$newWidth = $width;
			}
			if ($imageY > $height){
				$newHeight = $height;
				$newWidth = ($height/$imageY) * $imageX;
			}
			
			$fromMidY = 0;
			$fromMidX = 0;
			
		}elseif($proportion == 0){
			
			$newWidth = $width;
			$newHeight = $height;
			
			$fromMidY = 0;
			$fromMidX = 0;			
		}
		
		switch($type)
		{
			case '2' :
				$source = imagecreatefromjpeg($image);
				break;
			case '3' :
				$source = imagecreatefrompng($image);
				break;
			default : die("UNKNOWN IMAGE TYPE: $image");
		}
		
		// WHICH FUNCTIONS CAN RESIZE / RESAMPLE THE IMAGE?
		if ($gdv >= 2)
		{

			// IF GD IS AT LEVEL 2 OR ABOVE
			$target = imagecreatetruecolor($width, $height);
			
			$color = imagecolorallocatealpha($target, 255, 255, 255, 127); 
			imagefill($target, 0, 0, $color);
			imagesavealpha($target, TRUE);
			$fromMidX = ceil($fromMidX);
			$fromMidY = ceil($fromMidY);
			imagecopyresampled ($target, $source, $fromMidX, $fromMidY, 0, 0, $newWidth, $newHeight, $imageX, $imageY);
			if($fromMidX > 0){
				imagefill($target, 0, 0, $color);
			}
			if($fromMidY > 0){ 
				imagefill($target, 0, 0, $color);
			}
			
		}
		else
		{
			// IF GD IS AT A LOWER REVISION LEVEL
		   	$target = imagecreate($width, $height);
		   	imagesavealpha($target, TRUE);
				$empty = imagecolorallocatealpha($thumb,0x00,0x00,0x00,127);
				imagefill($target, 0, 0, $empty);
		   	imagecopyresized($target, $source, $fromMidX, $fromMidY, 0, 0, $newWidth, $newHeight, $imageX, $imageY);
		}
		
		// SHARPEN THE PIC
		$sharpenMatrix = array
		( array( -1.2, -1, -1.2 )
		, array( -1,   20, -1 )
		, array( -1.2, -1, -1.2 )
		)
		;
		$divisor = array_sum(array_map('array_sum', $sharpenMatrix));
		$offset  = 0;
		imageconvolution($target, $sharpenMatrix, $divisor, $offset);
	
		if(imagepng($target, $image,9)){
			imagedestroy($target);
		}
		return $image;
	}
}

Open in new window


Any help would be appreciated, Dan
Avatar of Ray Paseur
Ray Paseur
Flag of United States of America image

Let me see if I can paraphrase... Given an image of some width and height, you want to proportionally resize the image, then you want to pad the image with transparent space to fit a specific output size.  Does that cover it?
Avatar of andrewaiello
andrewaiello

ASKER

Yes. And I actually think I got the basis of this function from something you posted in one of my previous questions on http://www.experts-exchange.com.  Thank you again.
I'm using your question as a teaching example in class today.  I'll have a good script for you after lunch, US Eastern time.  Best regards, ~Ray
I wanted to add to this question.  I was playing around with the function and notice that line 121 was not necessary, so I removed it.  

I then changed lines 126 to 131 to:

if($fromMidX > 0){
				$transparent = $width - 1;
				imagefill($target, 0, 0, $color);
				imagefill($target,$transparent , 0, $color); 
			}
			if($fromMidY > 0){ 
				$transparent = $height - 1;
				imagefill($target, 0, 0, $color);
				imagefill($target, 0, $transparent, $color);
			}

Open in new window


Also, despite the fact that I had #FFFFFF specified on line 120 and cleared my cache, I was still getting #000000 being used as a fill.

I played around with it and came to replace it with:

$color = imagecolorallocatealpha($target, 255, 255, 255, 123);

Open in new window


By changing the alpha channel it changed the color used to #FFFFFF and I was able to blend the transparent padding with my background enough so it is not noticeable.  But I am still getting a solid pixel in the top left corner and a solid line across the top of the non transparent part of the image.

I also changed the coordinates used in imagefill() to various spots other than 0,0 but those solid pixels remain in the same locations.

It is sufficient for what I need, and I can move on.  But I'm still curious about this behavior.
SOLUTION
Avatar of Ray Paseur
Ray Paseur
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I just tried the script you posted, and it does exactly what I was trying to do.  There are no stray pixels or anything.

I still have to compare it to what I have to see where I am going wrong. I also will have to go over the links in your comments at the top of the script.  

I just want to ask, why do you check the file extension when deciding how to process the image (imagecreatefromjpeg() or imagecreatefrompng()) and not by the mime type.

I just don't know if it is preference or if there is a specific reason.

Thank you for all your help once again, Dan.
No special reason.  I suppose there would be arguments that the mime type was somehow more "accurate" but I've used this method for years and I've never seen anybody send me an obscured file name.

If I get time, I'll add the sharpening back in and post a new version with better overwrite handling.

Best regards, ~Ray
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I accepted as a multiple solution because both posts answer my question accurately.  The 2nd one just has a few more features added to it.  Both posts are extremely detailed with links to the PHP manuals for all used functions along with reference docs related to the subject.  

Thank you Ray.