I dispatch php gd in my controller, here's the code
class getSmallImageController extends Controller
{
public function getImg(request $request)
{
// Get the image from local file. The image must be 300*200 size.
//change $img to your own path
$img_path = '/home/jonnyy/PhpstormProjects/zcapt/app/repository/Image/1.jpg';
$img = imagecreatefromjpeg($img_path);
$img_size = getimagesize($img_path);
if ($img_size[0] != 300 || $img_size[1] != 200)
die("image size must be 300*200");
//get value of authID from init.php
$response = app('App\Http\Controllers\initController')->index($request);
$authID = $response->getOriginalContent()['authID'];
// Calculate the diagonal coordinate for that square
$position_x_1 = DB::table('inits')
->select('x')
->where('authID', '=', $authID)
->inRandomOrder()
->first();
$position_y_1 = DB::table('inits')
->select('y')
->where('authID', '=', $authID)
->inRandomOrder()
->first();
// Create a small image with height 50 and width 50
$img_small = imagecreatetruecolor(50, 50);
// Copy one part of the large image (the image with size 300*200) to small part of image
imagecopy($img_small, $img, 0, 0, current($position_x_1), current($position_y_1), 50, 50);
// Change brightness of the picture
imagefilter($img_small, IMG_FILTER_BRIGHTNESS, 50);
$position_1 = 0;
$position_2 = 50;
// Adding some blur in to small picture
for ($i = 50; $i < 120; $i = $i + 6) {
imagerectangle($img_small, $position_1, $position_1, $position_2, $position_2, imagecolorallocatealpha($img_small, 255, 255, 255, $i));
$position_1 = $position_1 + 1;
$position_2 = $position_2 - 1;
}
// Set header in type jpg
;
// Generate image
header("Content-type: image/jpg");
// Generate image
imagejpeg($img_small);
// Release memory
imagedestroy($img_small);
;
imagedestroy($img);
}
}
Got this garbled
But When I dd() the last line of my code,
dd(imagedestroy($img))
return the picture I want.
By the way, the GD library works in another controller which I dispatched for.
That isn't the way the Laravel with MVC should work. As a dirty solution I believe that put exit; after imagedestroy() could work. Another option would be to save the image to disk and display to the user.
The following option is to save in memory and then send to the user. If your page/application isn't accessed too frequently, it can work well for you.
ob_start();
imagejpeg($img_small);
imagedestroy($img_small);
$image_data = ob_get_contents();
ob_end_clean();
$response = \Response::make($image_data, 200);
$response->header("Content-Type", "image/jpeg");
return $response;
Related
This script will load an image (jpg, gif or png) and then save a PNG local copy for caching.
I'm trying to find a way to resize the image to 300x300 before saving it as a PNG.
I tried to use the function imagecopyresampled() but the image is still not resized.
2 problems now :
The script saves a resized PNG image in the correct folder, but the image is empty (it's all black)
The first time i will load the image, i will get an error (image cannot be displayed because it contains error) but the image will still be saved as PNG in the cache folder. Second time i load the image, it will be displayed correctly (using the cached version) but it isn't resized.
Here's the full code of my page. The first part is used to cache the image, the second part is used to display the non-cached image (it reads an image from a ZIP file and output the content without extracting anything)
if (empty($_GET['display'])) {
header('Content-Type: image/png');
$imgpochette = $_GET['i'];
$ENABLE_CACHE = true;
$CACHE_TIME_HOURS = 744;
$CACHE_FILE_PATH = "pochette_album/$imgpochette.png";
if($ENABLE_CACHE && file_exists($CACHE_FILE_PATH) && (time() - filemtime($CACHE_FILE_PATH) < ($CACHE_TIME_HOURS * 60 * 60))) {
echo #file_get_contents($CACHE_FILE_PATH);
} else {
// Load the requested image
$imgdisplay = "http://www.pirate-punk.com/pochette.php?i=$imgpochette&display=1";
$image = imagecreatefromstring(file_get_contents($imgdisplay));
$width = "30";
$height = "30";
list($originalWidth, $originalHeight) = getimagesize($CACHE_FILE_PATH);
$new_image = imagecreatetruecolor($width, $height);
imagecopyresampled($new_image, $image, 0, 0, 0, 0, $width, $height, $originalWidth, $originalHeight);
// Send the image
imagepng($new_image, $CACHE_FILE_PATH);
exit();
#file_put_contents($CACHE_FILE_PATH, $output);
echo $output;
}
}
if (!empty($_GET['display'])) {
function showimage($zip_file, $file_name) {
$z = new ZipArchive();
if ($z->open($zip_file) !== true) {
echo "File not found.";
return false;
}
$stat = $z->statName($file_name);
$fp = $z->getStream($file_name);
// search for a path/to/file matching file, returning the index of it
$index = $z->locateName($file_name, ZipArchive::FL_NOCASE|ZipArchive::FL_NODIR);
// get the name of the file based on the index
$full_file_name = $z->getNameIndex($index);
// now get the stream
$fp = $z->getStream($full_file_name);
if(!$fp) {
echo "Could not load image.";
return false;
}
header('Content-Type: image/jpeg');
header('Content-Length: ' . $stat['size']);
fpassthru($fp);
return true;
}
$imgsrcencoded = $_GET['i'];
$imagesrc = base64_decode($imgsrcencoded);
$explodez = explode("#",$imagesrc);
$imgg = utf8_encode($explodez[1]);
$dirnfile = $explodez[0];
$zipp = end((explode('/', $dirnfile)));
$dirr = str_replace($zipp,"",$dirnfile);
$dirr = rtrim($dirr,"/");
$imgg = rtrim($imgg);
chdir($dirr);
if (empty($_GET['debug'])) {
echo showimage($zipp,$imgg);
}
}
Get the solution for .png images
Is there any working example how to read an animated gif, change color of any pixel in any frame of it using GD library or ImageMagick and save it as an animated gif again?
I have been googling for it for 2 days and I did not found answer anywhere.
Edit:
I have almost perfect solution. But some transparent (animated) gifs do not retain its transparency correctly (only part of the transparency is corrupted).
Source:
One of the wrong images: http://commons.wikimedia.org/wiki/File:Animation30.gif
GifFrameExtractor class: https://github.com/Sybio/GifFrameExtractor
GIFEncoder class: http://www.phpclasses.org/package/3163-PHP-Generate-GIF-animations-from-a-set-of-GIF-images.html
require_once('GIFEncoder.class.php');
require_once('GifFrameExtractor-master/src/GifFrameExtractor/GifFrameExtractor.php');
$gifFilePath = "input.gif";
// check this is an animated GIF
if (!\GifFrameExtractor\GifFrameExtractor::isAnimatedGif($gifFilePath)) {
\die('not an namiated image');
}
$gfe = new \GifFrameExtractor\GifFrameExtractor();
$gfe->extract($gifFilePath);
// frames as a GD source
$frameImages = $gfe->getFrameImages();
$frameDurations = $gfe->getFrameDurations();
// color used as a transparent color, if any
$tColor = array('red' => -1,'green'=>-1,'blue'=>-1);
foreach($frameImages as $i => $gdSource) {
// do something with gdSource
colorize($gdSource);
if($i == 0) {
//get the color used as transparent
// should be for any frame the same
$tColor = getTransparentColor($gdSource);
}
//GIFEncoder works only with binary data or files
//It cant work with gd source
//Therfore i output the gd source to stdout, catch it
//and use it as binary data.
//It could be memory consuming, so you might write the
//frames as a file and then use them instead of binary data.
\ob_start(); // catch output
\imagegif($gdSource); // write binary data to output
$frameImages[$i] = \ob_get_contents(); //replace GD source by the binary data
\ob_end_clean();
}
// create animated gif
$gif = new \GIFEncoder(
$frameImages, //binary data (or path to images on file system)
$frameDurations,
0, // infinite loop
2, // dont know what is this for (number 0-3 or -1 to ignore)
$tColor['red'], $tColor['green'], $tColor['blue'],
'bin' // or 'url' if $frameImages is array of paths to images on file system
);
// write image to file system
\FWrite ( \FOpen ( "output.gif", "wb" ), $gif->GetAnimation ( ) );
// send image to browser
//Header ( 'Content-type:image/gif' );
//echo $gif->GetAnimation ( );
//helpful function
/**
* Make grayscale color
* #param $color array('red'=>,'green'=>,'blue'=>);
* #return array('red'=>,'green'=>,'blue'=>);
*/
function transformColor($color) {
$gst = $color['red']*0.15+$color['green']*0.5+$color['blue']*0.35;
$color['red'] = $color['green'] = $color['blue'] = $gst;
return $color;
}
//helpful function
/**
* Get the transparent color from GD resource.
* If no tranpsarent color, all returned RGB values are -1
* #param $rs GD resource
* #return array('red'=>,'green'=>,'blue'=>)
*/
function getTransparentColor($rs) {
$transparentIndex = \ImageColorTransparent($rs);
$transparentColor = NULL;
if ($transparentIndex >= 0 && $transparentIndex < \ImageColorsTotal($rs)) {
$transparentColor = #\ImageColorsForIndex($rs, $transparentIndex);
}
return $transparentColor ? $transparentColor : array('red' => -1,'green'=>-1,'blue'=>-1);
}
// transform the GD resource with transparency
// there is probably problem,because if I save frames alone, they have the problem with transparency described above
function colorize($rs) {
assert('gd' === \get_resource_type($rs));
// you might want to create copy of the source here
// (instead of resource alias)
$bild = $rs;
//width
$x = \imagesx($bild);
// height
$y = \imagesy($bild);
$transparentIndex = \ImageColorTransparent($rs);
$transparentColor = NULL;
if ($transparentIndex >= 0 && $transparentIndex < \ImageColorsTotal($bild)) {
$transparentColor = #\ImageColorsForIndex($rs, $transparentIndex);
}
\ImageSaveAlpha($bild, TRUE);
\ImageAlphaBlending($bild, FALSE);
$transparentNewIndex = NULL;
$transparentNewColor = NULL;
if (!empty($transparentColor)) {
$transparentIndexNew = \ImageColorAllocate($bild, 255, 0, 255);
\ImageColorTransparent($bild, $transparentIndexNew);
$transparentColorNew = #ImageColorsForIndex($bild, transparentIndexNew);
}
// transform pixels
for ($i = 0; $i < $y; $i++) {
for ($j = 0; $j < $x; $j++) {
$index = \imagecolorat($rs, $j, $i);
$color = \imagecolorsforindex($rs, $index);
if ($index === $transparentIndex) {
$col = $transparentIndexNew;
} else {
$rgb = transformColor($color);
//TODO: we have a problem here, if $rgb ~= $transparentColorNew,
// then this pixel will be transparent and not the desired color
$col = \imagecolorresolvealpha($bild, $rgb['red'], $rgb['green'], $rgb['blue'], intval($color['alpha']));
if($col == $transparentIndexNew) {
//TODO: fix the color not to be transparent
// it do not die with the example gif
//die('This might be a problem, but not the problem describlet above this example');
}
}
\imagesetpixel($bild, $j, $i, $col);
}
}
return $bild;
}
I believe that an animated GIF has multiple frames, so the algorithm for this would be to read the gif as multiple frames, change the pixel colours in each frame, and then save it again. Are you able to load an animated gif in GD?
Perhaps this library can help you to write the GIF image back: http://www.phpclasses.org/package/3163-PHP-Generate-GIF-animations-from-a-set-of-GIF-images.html
$file = "yourImage.gif";
$im = imagecreatefromgif ($file);
$index = imagecolorclosest ( $im, 255,255,255 ); // get your color (rgb)
imagecolorset($im,$index,92,92,92); // set new color (rgb)
$file= "yourResult.gif";
imagegif($im, $file); // save image as gif
imagedestroy($im);
I'm not sure if this works with a animated gif.
I use this piece of code to make thumbnails of an uploaded image.
$file = randString().'.jpg';
$uniPath = 'i/u'.$login;
$completePath = $uniPath.'/a/'.$file;
$thumbPath = $uniPath.'/b/'.$file;
if(copy($_FILES['filename']['tmp_name'], $completePath)){
function convertPic($w_dst, $h_dst, $n_img){
$wxh = $w_dst.'x'.$h_dst;
switch($wxh){
case '150x150': $letter = 'b'; break;
case '50x50': $letter = 'c'; break;
default: $letter = 'z';
}
$dbPath = '/i/u1/'.$letter.'/'.$n_img;
$new_img = $_SERVER['DOCUMENT_ROOT'].$dbPath;
$file_src = "img.jpg"; // temporary safe image storage
$img_src = $file_src;
unlink($file_src);
move_uploaded_file($_FILES['filename']['tmp_name'], $file_src);
list($w_src, $h_src, $type) = getimagesize($file_src); // create new dimensions, keeping aspect ratio
$ratio = $w_src/$h_src;
$h_ratio = ($h_dst / $h_src);
$w_ratio = ($w_dst / $w_src);
if($w_src > $h_src){ //landscape
$w_crop = round($w_src * $h_ratio);
$h_crop = $h_dst;
$src_x = ceil(($w_src - $h_src)/2);
$src_y = 0;
}
elseif($w_src < $h_src){ // portrait
$h_crop = round($h_src * $w_ratio);
$w_crop = $w_dst;
$src_y = ceil(($h_src - $w_src)/2);
$src_x = 0;
}
else { //square
$w_crop = $w_dst;
$h_crop = $h_dst;
$src_x = 0;
$src_y = 0;
}
switch ($type)
{case 1: // gif -> jpg
$img_src = imagecreatefromgif($file_src);
break;
case 2: // jpeg -> jpg
$img_src = imagecreatefromjpeg($file_src);
break;
case 3: // png -> jpg
$img_src = imagecreatefrompng($file_src);
break;
}
$img_dst = imagecreatetruecolor($w_dst, $h_dst); // resample
imagecolorallocate($img_dst, 255, 255, 255) or die("fail imagecolorallocate");
imagecopyresampled($img_dst, $img_src, 0, 0, $src_x, $src_y, $w_crop, $h_crop, $w_src, $h_src) or die("imagecopyresampled($img_dst, $img_src, 0, 0, $src_x, $src_y, $w_crop, $h_crop, $w_src, $h_src)");
imagejpeg($img_dst, $new_img); // save new image
unlink($file_src); // clean up image storage
imagedestroy($img_src);
imagedestroy($img_dst);
return $db_path;
}
convertPic(150, 150, $file);
convertPic(250, 250, $file);
But for some reason the convertPic function does not work if called twice as in the example above. If it is called once everything works fine. I've put an alert if imagecopyresampled fails and it outputs
imagecopyresampled(Resource id #17,
img.jpg, 0, 0, 0, 0, 250, 250, , )
I think the problem is with the temporary image storing but not sure. Please help.
It would appear that you're processing an uploaded image. As part of the processing function, you move the uploaded file from its temporary directory, and then do some work on it.
When you call the function again the second time, the uploaded file is not longer in the temporary directory, and things blow up.
function convertPic(...) {
....
move_uploaded_file($_FILES['filename']['tmp_name'], $file_src);
....
unlink($file_src);
....
}
Basically the first call to convertPic processes and then deletes the "source", which means it's no longer available for the second call immediately afterwards.
I guess problem is with unlink($file_src)...
First, you call convertPic() function and it works OK because your img.jpg is still here, then you remove your image with unlink() and try to call again same routine that actually needs this file to work properly.
Also you can't move same file twice, so you have to move it outside the function, do your job as many times as needed, and after that, unlink image. Unlink should be after double-call of convertPic(), and move_uploaded_file() should be before function convertPic().
Well, that's what I think on first sight.
This one might be a little confusing. I'm using AMCharts with rails. Amcharts comes with a PHP script to export images called "export.php"
I'm trying to figure out how to take the code in export.php and put it into a controller.
Here is the code:
<?php
// amcharts.com export to image utility
// set image type (gif/png/jpeg)
$imgtype = 'jpeg';
// set image quality (from 0 to 100, not applicable to gif)
$imgquality = 100;
// get data from $_POST or $_GET ?
$data = &$_POST;
// get image dimensions
$width = (int) $data['width'];
$height = (int) $data['height'];
// create image object
$img = imagecreatetruecolor($width, $height);
// populate image with pixels
for ($y = 0; $y < $height; $y++) {
// innitialize
$x = 0;
// get row data
$row = explode(',', $data['r'.$y]);
// place row pixels
$cnt = sizeof($row);
for ($r = 0; $r < $cnt; $r++) {
// get pixel(s) data
$pixel = explode(':', $row[$r]);
// get color
$pixel[0] = str_pad($pixel[0], 6, '0', STR_PAD_LEFT);
$cr = hexdec(substr($pixel[0], 0, 2));
$cg = hexdec(substr($pixel[0], 2, 2));
$cb = hexdec(substr($pixel[0], 4, 2));
// allocate color
$color = imagecolorallocate($img, $cr, $cg, $cb);
// place repeating pixels
$repeat = isset($pixel[1]) ? (int) $pixel[1] : 1;
for ($c = 0; $c < $repeat; $c++) {
// place pixel
imagesetpixel($img, $x, $y, $color);
// iterate column
$x++;
}
}
}
// set proper content type
header('Content-type: image/'.$imgtype);
header('Content-Disposition: attachment; filename="chart.'.$imgtype.'"');
// stream image
$function = 'image'.$imgtype;
if ($imgtype == 'gif') {
$function($img);
}
else {
$function($img, null, $imgquality);
}
// destroy
imagedestroy($img);
?>
There are some versions in existence in a thread I found here: http://www.amcharts.com/forum/viewtopic.php?id=341
But I have a feeling the PHP code above has changed since then - because neither implementation worked for me.
What this code more or less dose is grabs the informations, that were sent to the script (POST).
The informations include the height and width of the picture and the RGB values of every pixel. The script draws every pixel and sends the images at the end to the client.
You can use Rmagick's method to draw a pixel. This will give you the same result.
The incomming post data looks like this:
height = number -> cast to int
width = number -> cast to int
// first row with a repeating part of R:G:B,R:G:B,... (n = width)
r0 = 255:0:0,150:120:0,77:88:99,...
r1 = ...
.
.
r100 = ... -> the row count is the height - 1
Actually, I found a discussion about speeding up pixel by pixel drawing.
So apparently I was running into other errors which made me think the already existing code didnt work. However, the code on the thread I linked to in the original question does in fact work!
I am trying to build a script that retrieves a list of thumbnail images from an external link, much like Facebook does when you share a link and can choose the thumbnail image that is associated with that post.
My script currently works like this:
file_get_contents on the URL
preg_match_all to match any <img src="" in the contents
Works out the full URL to each image and stores it in an array
If there are < 10 images it loops through and uses getimagesize to find width and height
If there are > 10 images it loops through and uses fread and imagecreatefromstring to find width and height (for speed)
Once all width and heights are worked out it loops through and only adds the images to a new array that have a minimum width and height (so only larger images are shown, smaller images are less likely to be descriptive of the URL)
Each image has its new dimensions worked out (scaled down proportionally) and are returned...
<img src="'.$image[0].'" width="'.$image[1].'" height="'.$image[2].'"><br><br>
At the moment this works fine, but there are a number of problems I can potentially have:
SPEED! If the URL has many images on the page it will take considerably longer to process
MEMORY! Using getimagesize or fread & imagecreatefromstring will store the whole image in memory, any large images on the page could eat up the server's memory and kill my script (and server)
One solution I have found is being able to retrieve the image width and height from the header of the image without having to download the whole image, though I have only found some code to do this for JPG's (it would need to support GIF & PNG).
Can anyone make any suggestions to help me with either problem mentioned above, or perhaps you can suggest another way of doing this I am open to ideas... Thanks!
** Edit: Code below:
// Example images array
$images = array('http://blah.com/1.jpg', 'http://blah.com/2.jpg');
// Find the image sizes
$image_sizes = $this->image_sizes($images);
// Find the images that meet the minimum size
for ($i = 0; $i < count($image_sizes); $i++) {
if ($image_sizes[$i][0] >= $min || $image_sizes[$i][1] >= $min) {
// Scale down the original image size
$dimensions = $this->resize_dimensions($scale_width, $scale_height, $image_sizes[$i][0], $image_sizes[$i][1]);
$img[] = array($images[$i], $dimensions['width'], $dimensions['height']);
}
}
// Output the images
foreach ($img as $image) echo '<img src="'.$image[0].'" width="'.$image[1].'" height="'.$image[2].'"><br><br>';
/**
* Retrieves the image sizes
* Uses the getimagesize() function or the filesystem for speed increases
*/
public function image_sizes($images) {
$out = array();
if (count($images) < 10) {
foreach ($images as $image) {
list($width, $height) = #getimagesize($image);
if (is_numeric($width) && is_numeric($height)) {
$out[] = array($width, $height);
}
else {
$out[] = array(0, 0);
}
}
}
else {
foreach ($images as $image) {
$handle = #fopen($image, "rb");
$contents = "";
if ($handle) {
while(true) {
$data = fread($handle, 8192);
if (strlen($data) == 0) break;
$contents .= $data;
}
fclose($handle);
$im = #imagecreatefromstring($contents);
if ($im) {
$out[] = array(imagesx($im), imagesy($im));
}
else {
$out[] = array(0, 0);
}
#imagedestroy($im);
}
else {
$out[] = array(0, 0);
}
}
}
return $out;
}
/**
* Calculates restricted dimensions with a maximum of $goal_width by $goal_height
*/
public function resize_dimensions($goal_width, $goal_height, $width, $height) {
$return = array('width' => $width, 'height' => $height);
// If the ratio > goal ratio and the width > goal width resize down to goal width
if ($width/$height > $goal_width/$goal_height && $width > $goal_width) {
$return['width'] = floor($goal_width);
$return['height'] = floor($goal_width/$width * $height);
}
// Otherwise, if the height > goal, resize down to goal height
else if ($height > $goal_height) {
$return['width'] = floor($goal_height/$height * $width);
$return['height'] = floor($goal_height);
}
return $return;
}
getimagesize reads only header, but imagecreatefromstring reads whole image. Image read by GD, ImageMagick or GraphicsMagic is stored as bitmap so it consumes widthheight(3 or 4) bytes, and there's nothing you can do about it.
The best possible solution for your problem is to make curl multi-request (see http://ru.php.net/manual/en/function.curl-multi-select.php ), and then one by one process recieved images with GD or any other library. And to make memory consumption a bit lower, you can store image files on disk, not in memory.
The only idea that comes to mind for your current approach (which is impressive) is to check the HTML for existing width and height attributes and skip the file read process altogether if they exist.