Manipulating an image in PHP results in striped pattern - php

Edit: Just ended up using Image Magick and that fixed it.
Long story short, I'm attempting to pull some basic information about a series of schools from Wikipedia using a combination of PHP and jQuery. Part of that information is the school's emblem or logo, which is easy enough to find in the elements list.
The problem lies in trying to do some tweaking on the image in PHP. I know the image exists at the target URL (which is on a different domain, if that helps any) and that it's the one I want, but certain images come out looking like this:
This is the original image:
Others, of all file types, come out perfectly fine.
The code for that part is as follows:
$ext = end(explode('.', $image));
if($ext == 'png') {
$img = imagecreatefrompng($image);
}
else if($ext == 'jpeg' || $ext == 'jpg') {
$img = imagecreatefromjpeg($image);
}
else if($ext == 'gif') {
$img = imagecreatefromgif($image);
}
else $img = false;
if($img) {
$raw_x = imagesx($img);
$raw_y = imagesy($img);
if($raw_x > $raw_y && $raw_x > 500)
{
$y = (500 / $raw_x) * $raw_y;
$tmp_img = imagecreatetruecolor(500, $y);
$white = imagecolorallocate($tmp_img, 255, 255, 255);
imagefill($tmp_img, 0, 0, $white);
imagecopyresampled($tmp_img, $img, 0, 0, 0, 0, 500, $y, $raw_x, $raw_y);
$img = $tmp_img;
}
else if($raw_y > 500)
{
$x = (500 / $raw_y) * $raw_x;
$tmp_img = imagecreatetruecolor($x, 500);
$white = imagecolorallocate($tmp_img, 255, 255, 255);
imagefill($tmp_img, 0, 0, $white);
imagecopyresampled($tmp_img, $img, 0, 0, 0, 0, $x, 500, $raw_x, $raw_y);
$img = $tmp_img;
}
if(!file_exists("../images/schools/" . $id)) mkdir("../images/schools/" . $id, 0755, true);
imagejpeg($img, "../images/schools/" . $id . "/photo.jpg", 100);
}
I've been at this for a couple days and I can't figure out what's wrong, I'm hoping a fresh set of eyes might be able to see something I don't

My guess is that your conditional logic around raw image size is causing you problems. You are not handling the case where you image equal in width and height and you are not handling cases where the images are less than 500 pixels (not sure if this is intentional, but could lead to problems with HTML layout if you specify specific heights/widths for your img elements). You should do all your math to calculate expected scaled image size up front and then do image creation/resizing in one place (not in if conditionals).
I am assuming you want to always scale your image to be either 500px in height (if image is portrait) or 500px in width if image is landscape. You would calculate expected dimensions as follows:
$target_width = 500;
$target_height = 500;
if($raw_x >= $raw_y) { // set scaling factor based on x dimension
$scaling_factor = 500.0 / $raw_x;
$target_height = intval(500 * $scaling_factor);
} else { // set scaling factor based on y dimension
$scaling_factor = 500.0 / $raw_y;
$target_width = intval(500 * $scaling_factor)
}
And then only have one code block for setting new temp image and resizing original into it:
$tmp_img = imagecreatetruecolor($target_width, $target_height);
$white = imagecolorallocate($tmp_img, 255, 255, 255);
imagefill($tmp_img, 0, 0, $white);
imagecopyresampled($tmp_img, $img, 0, 0, 0, 0, $target_width, $target_height, $raw_x, $raw_y);
$img = $tmp_img;

Related

Resizing/cropping images in PHP results in images with black border on one side

I created a function that takes images, resizes them to fit at least one dimension perfectly onto a canvas and finally crops out excess image content.
Process:
Take a 640x400 image
Scale image fit into 450x450 canvas (image is now 450x281)
Crop out excess data if any
The problem I'm encountering currently involves images that are too small for the newly created thumbnail (such as the one provided in the example above). I expected the output to either have a white background(JPGs) or a transparent background(PNGs). However, despite my best efforts, this tends to be the resulting mess:
As can be observed, the image has a black background in place of a transparent/white background. I am completely stumped on how to correct this flaw.
A copy of my code:
function create_thumbnail($path, $saveto, $width, $height) {
ini_set('memory_limit', '128M'); //Unlocking more memory for download
$info = getimagesize($path);
$rate = $info[0]/$info[1];
// Determine image size/position
if ($info[0] < $width || $info[1] < $height) {
// Image is too small
if ($info[0] < $width && $info[1] < $height) {
// Both width and height too small
$nw = $info[0];
$nh = $info[1];
} else if ($info[0] < $width) {
// Width is too small
$nw = ($info[0]*$height)/$info[1];
$nh = $height;
} else if ($info[1] < $height) {
// Height is too small
$nw = $width;
$nh = ($info[1]*$width)/$info[0];
}
} else {
// Image fits
if (($width/$height) > $rate) {
$nw = $width;
$nh = $width/$rate;
} else {
$nw = $height*$rate;
$nh = $height;
}
}
$nw = round($nw);
$nh = round($nh);
$x_mid = round($nw/2);
$y_mid = round($nh/2);
switch($info[2]) {
case IMAGETYPE_PNG :
$src = imagecreatefrompng($path);
break;
case IMAGETYPE_JPEG :
$src = imagecreatefromjpeg($path);
break;
case IMAGETYPE_GIF :
$src = imagecreatefromgif($path);
break;
default :
return false;
}
// Create image
$proc = imagecreatetruecolor($nw, $nh);
$clr = imagecolorallocate($proc, 255, 255, 255);
imagefill($proc, 0, 0, $clr);
imagecopyresampled($proc, $src, 0, 0, 0, 0, $nw, $nh, $info[0], $info[1]);
$thmb = imagecreatetruecolor($width, $height);
$clr = imagecolorallocate($thmb, 255, 255, 255);
imagefill($thmb, 0, 0, $clr);
imagecopyresampled($thmb, $proc, 0, 0, ($x_mid-($width/2)), ($y_mid-($height/2)), $width, $height, $width, $height);
if ($info[2] == IMAGETYPE_PNG || $info[2] == IMAGETYPE_GIF) {
$trnprt_idx = imagecolortransparent($src);
if ($trnprt_idx >= 0) {
// Attempt to forcefully correct transparencies using original image's color index
$trnprt_clr = imagecolorsforindex($src, $trnprt_idx);
$trnprt_idx = imagecolorallocate($thmb, $trnprt_clr['red'], $trnprt_clr['green'], $trnprt_clr['blue']);
imagefill($thmb, 0, 0, $trnprt_idx);
imagecolortransparent($thmb, $trnprt_idx);
imagealphablending($thmb, false);
imagesavealpha($thmb, true);
} else if ($info[2] == IMAGETYPE_PNG) {
// Attempt to forcefully correct transparencies by shutting down blending
$clr = imagecolorallocatealpha($thmb, 0, 0, 0, 127);
imagefill($thmb, 0, 0, $clr);
imagealphablending($thmb, false);
imagesavealpha($thmb, true);
}
}
switch($info[2]) {
case IMAGETYPE_PNG :
imagepng($thmb, $saveto);
break;
case IMAGETYPE_JPEG :
imagejpeg($thmb, $saveto, 100);
break;
case IMAGETYPE_GIF :
imagegif($thmb, $saveto);
break;
default :
return false;
}
return true;
} //End of create_thumbnail()
I have attempted to correct the transparency/coloring (as visible in my code), but it only affects one side of the image. Everything I have tried has either resulting in one side having a transparent/white background or both sides being completely black.
After a long time spent playing around trying to figure out what exactly was going on and breaking, I have found the solution.
The problem is here:
$proc = imagecreatetruecolor($nw, $nh);
$clr = imagecolorallocate($proc, 255, 255, 255);
imagefill($proc, 0, 0, $clr);
imagecopyresampled($proc, $src, 0, 0, 0, 0, $nw, $nh, $info[0], $info[1]);
$thmb = imagecreatetruecolor($width, $height);
$clr = imagecolorallocate($thmb, 255, 255, 255);
imagefill($thmb, 0, 0, $clr);
imagecopyresampled($thmb, $proc, 0, 0, ($x_mid-($width/2)), ($y_mid-($height/2)), $width, $height, $width, $height);
The newly created image $proc was not transferring it's transparency over to $thmb when I was doing imagecopyresampled from one to the other. The solution I found was to either skip creating/using $thmb altogether, or to save $proc as a png/gif first, then use that saved image for imagecopyresample.

Merge two images with GD Library, have 1 repeat in background

I want to take an image that has transparency and then overlay that on top of a 60x60 (arbitrary size) image that repeats for the width and length of the first image...
So essentially use image 2 as a repeating background image that image 1 is on top of.
EDIT:
Okay, so I used One Trick Pony's solution, but tried to modify it to create a square image out of a rectangle if the width is less than the height, but not stretch the original image and instead center it. I was able to center the image but then the repeating background does not continue repeating after the overlay image stops.
Here is the code:
<?php
$overlay = imagecreatefrompng('../images/' . $_REQUEST['overlay']);
$repeating = '../images/' . $_REQUEST['repeating'];
$ext = explode('.', $_REQUEST['repeating']);
$ext = strtolower($ext[1]);
if ($ext == 'gif')
$repeating = imagecreatefromgif($repeating);
elseif ($ext == 'png')
$repeating = imagecreatefrompng($repeating);
elseif ($ext == 'jpg' || $ext == 'jpeg')
$repeating = imagecreatefromjpeg($repeating);
$w = imagesx($overlay);
$h = imagesy($overlay);
if ($w < $h)
$w = $h;
$output = imagecreatetruecolor($w, $h);
imagealphablending($output, true);
imagesettile($output, $repeating);
imagefill($output, 0, 0, IMG_COLOR_TILED);
imagedestroy($repeating);
$offsetx = ($w - imagesx($overlay)) / 2;
imagecopy($output, $overlay, $offsetx, 0, 0, 0, $w, $h);
imagedestroy($overlay);
header('Content-Type: image/png');
imagepng($output);
imagedestroy($output);
?>
EDIT 2:
Overlay: http://72.167.52.68/~viisi/ebaylist/images/back_test2.png
Repeating: http://72.167.52.68/~viisi/ebaylist/images/back_test.gif
Expected result (but continue the repeating across the whole image): http://72.167.52.68/~viisi/ebaylist/image/previewImage.php?overlay=back_test2.png&repeating=back_test.gif
$overlay = imagecreatefrompng('/path/to/transparent/image.png');
$repeating = imagecreatefrompng('/path/to/repeating/image.png');
// create a new image matching overlay size
$w = imagesx($overlay);
$h = imagesy($overlay);
$output = imagecreatetruecolor($w, $h);
imagealphablending($output, true);
imagesavealpha($output, true);
// tile repeating image on it
imagesettile($output, $repeating);
imagefill($output, 0, 0, IMG_COLOR_TILED);
imagedestroy($repeating);
// now add overlay on top
imagecopy($output, $overlay, 0, 0, 0, 0, $w, $h);
imagedestroy($overlay);
// send to screen
header('Content-Type: image/png');
imagepng($output);
imagedestroy($output);

Empty space getting filled with black after GD imagecopy but only in some scenarios

I am experiencing an issue where I am allowing the user to resize images in a container and then need to create a resulting image that is the size of the container but with the image scaled and adjusted as per the users choices.
So for example say the container is 400 x 200 and the user wants to be able to put in a logo that is 600 x 100 they may wish to shrink the logo so it fits and leave space at the top and bottom. I need to be able to save that as an image that is 400x200 with the appropriate gaps at the top and bottom.
What I have found though is that if the image content (the logo in this example) extends beyond BOTH the top and the right of the container everything is fine OR if it DOESNT extend beyond either it is fine but if it extends beyond one and not the other then I get black fill- or something like that- see examples below...
Below are some examples of the results and this is the code I am using...
$cropped = wp_imagecreatetruecolor( $frame_w, $frame_h);
$backgroundColor = imagecolorallocatealpha($cropped, 0, 0, 0, 127);
//imageantialias( $cropped, true );
//if $img_y or $img_x are negative we need to apply the value to $img_x and $img_y
//if $img_y or $img_x are positive we need to apply the value to $dest_x and $dest_y
$dest_x = strstr($img_x,'-') ? 0 : abs($img_x);//if neg is true = 0 else offset inside
$dest_y = strstr($img_y,'-') ? 0 : abs($img_y);
$img_x = strstr($img_x,'-') ? abs($img_x) : 0;//if neg is true offset outside else 0
$img_y = strstr($img_y,'-') ? abs($img_y) : 0;
$img_w = $img_w > $frame_w ? $frame_w : $img_w;
$img_h = $img_h > $frame_h ? $frame_h : $img_h;
imagecopy( $cropped, $resized, $dest_x, $dest_y, $img_x, $img_y, $img_w, $img_h);
//imagecopymerge( $cropped, $resized, $dest_x, $dest_y, $img_x, $img_y, $img_w, $img_h,100);
//imagecopyresampled( $cropped, $resized, $dest_x, $dest_y, $img_x, $img_y, $frame_w, $frame_h, $img_w, $img_h );
imagefill($cropped, 0, 0, $backgroundColor);//putting this after the copy makes any black borders transparent again unless $resized does not extend beyond both dimensions
Examples
Image does not extend beyond top or beyond right (fine)
Image extends beyond bottom but not right (not fine)
Image extends beyond both (fine)
Image extends beyond right but not bottom (not fine)
Image Extends beyond neither (fine)
I have been literally tearing my hair out trying to fix this and tried every possible combination of imagesavealpha, imagecopymerged, imagecolorallocatealpha, imagealphablending etc I can think of but nothing seems to fix this...
So is this a bug/limitation of GD? Or can someone out there come to my rescue!
I don't know if this will help you, but I had an issue with this earlier today. The box expands but the area was black. Here's my code (which fixes it):
<?php
function createImage($text)
{
// Adds an extra space to fill underline
$text = " $text";
// Adds one line at the end
$text .= "\n";
// Wrap the text to fit the image
$text = wordwrap($text, 40, "\n");
// Count new lines
$newlines = substr_count($text, "\n");
// Count how long to expand
if($newlines == 0)
{
$height = 30;
}
else
{
$height = 30*$newlines-$newlines*5;
}
putenv('GDFONTPATH=' . realpath('.'));
header('Content-Type: image/png');
// Adding underline
$e = explode('<', $text);
for($i=0;$i<count($e);$i++)
{
$e[$i] = implode('̲', str_split($e[$i]));
}
// Creating image
$text = implode(' ', $e);
$im = imagecreatetruecolor(315, $height);
$white = imagecolorallocate($im, 255, 255, 255);
$grey = imagecolorallocate($im, 128, 128, 128);
$black = imagecolorallocate($im, 0, 0, 0);
$purple = imagecolorallocate($im, 97, 26, 139);
imagefilledrectangle($im, 0, 0, 399, $height, $white);
$font = 'arialbd.ttf';
imagettftext($im, 11, 0, 10, 20, $purple, $font, $text);
imagepng($im);
imagedestroy($im);
}
createImage("asbdasddsa");
?>
i think this will happen only for PNG check with other formats
I'm not sure if this is an actual answer since it is basically 'Use ImageMagick' but anyway for those for whom ImageMagick is an option the code below might help them achieve the same thing as I was trying to above... Basically ImageMagick seems far superior to GD, no borders around rotated images, no hassles with transparency, no onwanted black fill, clearer resizing if you are enlarging...
$img_x = -50; //left offset of the image within the frame
$img_y = 50; //top offset of the image within the frame
$img_w = 400; //width of the image to be put in the frame
$img_h = 200; // height of the image to be put in the frame
$angle = 45; //rotation to be applied to the image before it is put into the frame
$frame_w = 300; //width of the frame the image is going into
$frame_h = 300; //height of the frame the image is going into
$img_path = 'path/to/image/file.jpg';
$image = new Imagick( $img_path );
$size = $image->getImageGeometry();
$orig_w = $size['width']; $orig_h = $size['height'];
$image->scaleImage( $img_w, $img_h );
//rotate if necessary
if($angle)
{
$image->rotateImage( new ImagickPixel('none'), $angle );
$size = $image->getImageGeometry();
$img_w = $size['width']; $img_h = $size['height'];
}
//composite into frame
//in imagemagick we create an image that is the size of the frame and make it transparent
$frame = new Imagick();
$frame->newImage($frame_w, $frame_h, new ImagickPixel("none"));
//then we composite the image itself into this with the respective offset values
$frame->compositeImage( $image, Imagick::COMPOSITE_DEFAULT, $img_x, $img_y );
//save it
$destfilename = "{$dir}/{$name}-{$img_suffix}.{$ext}";
$frame->writeImage($destfilename);
$frame->clear();
$frame->destroy();
$image->clear();
$image->destroy();
The above code produces this...yay!

Resizing and cropping image with GD while retaining aspect ratio

I'm currently coding an uploader script based on Uploadify. Right now I resize the given image and watermark one of the sizes. It all works well, but I need the script to resize the height and then crop the width so that the aspect ratio does not get messed up.
This is my code so far:
if ($fileExtension == "jpg" ||
$fileExtension == "jpeg" ||
$fileExtension == "png" ||
$fileExtension == "gif"){
// GD variables:
list($width, $height, $type) = GetImageSize($uploadedFile['tmp_name']);
// Image sizes:
$bigImage = array(800, 453);
$mediumImage = array(410, 231);
$listImage = array(120, 68);
$thumbnail = array(90, 51);
$sourceAspect = $width / $height;
$bigAspect = $bigImage[0] / $bigImage[1];
$mediumAspect = $mediumImage[0] / $mediumImage[1];
$listAspect = $listImage[0] / $listImage[1];
$thumbnailAspect = $thumbnail[0] / $thumbnail[1];
// Image is PNG:
if ($type == IMAGETYPE_PNG){
$image = imagecreatefrompng($uploadedFile['tmp_name']);
$valid = true;
}
// Image is JPEG:
else if ($type == IMAGETYPE_JPEG){
$image = imagecreatefromjpeg($uploadedFile['tmp_name']);
$valid = true;
}
// Image is GIF:
else if ($type == IMAGETYPE_GIF){
$image = imagecreatefromgif($uploadedFile['tmp_name']);
$valid = true;
}
// Format not allowed:
else {
$valid = false;
}
// Start creating images:
if ($valid){
// Get size:
$imageSize = getimagesize($uploadedFile['tmp_name']);
// Generate canvas:
$bCanvas = imagecreatetruecolor($bigImage[0], $bigImage[1]);
$mCanvas = imagecreatetruecolor($mediumImage[0], $mediumImage[1]);
$lCanvas = imagecreatetruecolor($listImage[0], $listImage[1]);
$tCanvas = imagecreatetruecolor($thumbnail[0], $thumbnail[1]);
// Copy content:
imagecopyresampled($bCanvas, $image, 0, 0, 0, 0, ($bigImage[0] * $sourceAspect), ($bigImage[1] / $sourceAspect), $imageSize[0], $imageSize[1]);
imagecopyresampled($mCanvas, $image, 0, 0, 0, 0, $mediumImage[0], $mediumImage[1], $imageSize[0], $imageSize[1]);
imagecopyresampled($lCanvas, $image, 0, 0, 0, 0, $listImage[0], $listImage[1], $imageSize[0], $imageSize[1]);
imagecopyresampled($tCanvas, $image, 0, 0, 0, 0, $thumbnail[0], $thumbnail[1], $imageSize[0], $imageSize[1]);
// Save images:
$saveB = imagejpeg($bCanvas, $targetFile.'_big.jpg', 90);
$saveM = imagejpeg($mCanvas, $targetFile.'_medium.jpg', 90);
$saveT = imagejpeg($lCanvas, $targetFile.'_list.jpg', 90);
$saveT = imagejpeg($tCanvas, $targetFile.'_thumb.jpg', 90);
// Destroy images:
imagedestroy($image);
imagedestroy($bCanvas);
imagedestroy($mCanvas);
imagedestroy($lCanvas);
imagedestroy($tCanvas);
// Watermark images:
$mark = imagecreatefrompng("logo.png");
list($mwidth, $mheight) = getimagesize("logo.png");
$img = imagecreatefromjpeg($targetFile.'_big.jpg');
list($bwidth, $bheight) = getimagesize($targetFile.'_big.jpg');
imagecopy($img, $mark, $bwidth-$mwidth-25, $bheight-$mheight-25, 0, 0, $mwidth, $mheight);
imagejpeg($img, $targetFile.'_big.jpg', 100);
imagedestroy($img);
} else {
echo "0";
}
} else {
move_uploaded_file($tempFile,$targetFile.'.'.$fileExtension);
}
I would be really happy if someone could help me solve this. I've been trying several methods but none of them seemed to work properly. As you can see in the top I have already defined the canvas sizes that I want to use in the variables "bigImage", "mediumImage", "listImage" and "thumbnail".
Thanks in advance! //
Jonathan
There is more than one way to resize an image. I'll spell them out for you:
Stretch to fit -- the image is resized to the desired size ignoring aspect ratio
Scale to fit -- the image is resized so that one dimension (width or height) has the desired size while the other is same or shorter while maintaining aspect ratio (one extra step may be required to fill the shorter side with solid color)
Crop to fit -- the image is resized so that one dimension (width or height) has the desired size while the other is same or longer while maintaining aspect ratio (one extra step is required to trim the outside region)
PS: both articles were written by me.

Remove the image resize ratio in OpenCart

I've look everywhere on how I could remove the image resizing in OpenCart but haven't find nothing about that.
I need that it resize but don't keep the ratio. I want the image to be just like I set it.
Here's the resize code in the system/library/image.php
public function resize($width = 0, $height = 0) {
if (!$this->info['width'] || !$this->info['height']) {
return;
}
$xpos = 0;
$ypos = 0;
$scale = min($width / $this->info['width'], $height / $this->info['height']);
if ($scale == 1) {
return;
}
$new_width = (int)($this->info['width'] * $scale);
$new_height = (int)($this->info['height'] * $scale);
$xpos = (int)(($width - $new_width) / 2);
$ypos = (int)(($height - $new_height) / 2);
$image_old = $this->image;
$this->image = imagecreatetruecolor($width, $height);
if (isset($this->info['mime']) && $this->info['mime'] == 'image/png') {
imagealphablending($this->image, false);
imagesavealpha($this->image, true);
$background = imagecolorallocatealpha($this->image, 255, 255, 255, 127);
imagecolortransparent($this->image, $background);
} else {
$background = imagecolorallocate($this->image, 255, 255, 255);
}
imagefilledrectangle($this->image, 0, 0, $width, $height, $background);
imagecopyresampled($this->image, $image_old, $xpos, $ypos, 0, 0, $new_width, $new_height, $this->info['width'], $this->info['height']);
imagedestroy($image_old);
$this->info['width'] = $width;
$this->info['height'] = $height;
}
What in that code could I remove so the image don't keep it's ratio on resize ?
First of all I would leave the default resizing tool as in some circumstances it might come in handy. What I did was add two more functions to resize images.
One function that crops an image so it fits the size set in the admin. Now empty white areas are added. This one is great for product lists. The second I added is a function that resizes an image so the biggest dimension will be set to the max size set in the admin. It will be scaled proportionally.
The new files are posted in an OpenCart forum thread.
I named the two extra functions cropsize and onesize. All you have to do is find the used resize functions in the controller and adjust this:
'thumb' => $this->model_tool_image
->resize($image,
$this->config->get('config_image_category_width'),
$this->config->get('config_image_category_height')));
to:
'thumb' => $this->model_tool_image
->cropsize($image,
$this->config->get('config_image_category_width'),
$this->config->get('config_image_category_height')));
The onesize function only needs one parameter so it's up to you but you could use something like this:
'popup' => $this->model_tool_image
->onesize($result['image'],
$this->config->get('config_image_popup_width'))
I hope this will help you getting better images.

Categories