PHP Imagick: How to select parts of an image multiple times? - php

To select different parts of an image, I now load the image over and over again and crop to the part I want.
This is most probably not the most efficient way to do this, for I think I should be able to load the image one time in memory and then use it multiple times.
My current code:
$src = New Imagick();
for ($i = 0; $i < 4; $i++)
{
switch ($i)
{
case 0: $pos = [0,0]; break;
//...
}
$src->readImage('image.jpg');
$src->cropImage(100, 100, $pos[0], $pos[1]);
//...
}
Anyone knows a better way?

Related

What is better, if else within loop, or two different loops within if else, with a function call, or same code repeated within if-else?

So I am trying to optimize a piece of php code that basically runs the same operations on two different datasets, based on user input. What would be a better and more optimized approach?
//$input = //user input
//$a = [1,2,3 .....];
//$b = [a,b,c .....];//both are same length - n
case 1 :
for($i =0; $i<n; $i++) {
if($input == 'a')
//doSomething with $a[i] - code here
else
//doSomething with $b[i] - code here
}
case 2 :
if($input == 'a') {
for($i =0; $i<n; $i++) {
//doSomething with $a[i] - code here
}
}
else {
for($i =0; $i<n; $i++) {
//doSomething with $b[i] - code here
}
}
case 3 :
if($input == 'a') {
for($i =0; $i<n; $i++) {
doSomething($a[i]);
}
}
else {
for($i =0; $i<n; $i++) {
doSomething($b[i]);
}
}
the operation is same in all cases
Better is always difficult to quantify, but if you want to do exactly the same processing on the inputs, just picking the one dataset depending on the input, you may be better off just setting an input array and just processing that...
if($input == 'a')
$dataset = $a;
else
$dataset = $b;
foreach ( $dataset as $dataItem ) {
//doSomething data code here
}
This might not really be the answer you're looking for, but honestly, I would just go for whatever conveys the actual use case the best and not try to optimize too much.
Performance-wise, unless the operation that checks the $input takes a lot of time (orders of magnitude more than a simple comparison), or if the operation on $dataset is very short (comparable to the input check), it simply won't matter.
Assuming $input does not change while you're processing your dataset, I'd go for case three. You're making it clear that it is the same operation, and the only difference between the different if branches is the dataset you're working with. If you're familiar with ternary operators, I'd even use this:
for($i = 0; $i < n; $i++) {
doSomething(($input == $a) ? $a[i] : $b[i]);
}
I'd take code readability over micro-optimizations any day, as long as you don't actually have performance issues with this piece of code.
As a bonus, have a read at this question about optimization: https://softwareengineering.stackexchange.com/questions/80084/is-premature-optimization-really-the-root-of-all-evil

FPDF - Determining height of MultiCell before placing?

The basic question: Is it possible to determine the height of a MultiCell before placing it in the document?
The reason: I've been tasked with creating a PDF version of a form. This form allows text input, with a resulting variable length. One person my not enter anything, another person may write a few paragraphs. "The Powers That Be" do not want this text breaking between pages.
Currently, after placing each block, I check the position on the page, and if I'm near the end, I create a new page.
if($this->getY() >= 250) {
$this->AddPage('P');
}
For the most part, this works. But there are the few sneaky ones that come in at, say 249, and then have boatloads of text. It seems it would make more sense to determine the height of a block before placing it to see if it actually fits on the page.
Surely there must be a way to do this, but Googling around hasn't proven very helpful.
Edit to clarify: The PDF is being generated after the form is submitted. Not an active, editable PDF form...
Earned the tumbleweed badge for this question. :( Anyway, in the off chance someone has the same issue, I figured I'd answer with what I did. I doubt this is the most efficient method, but it got the job done.
First, I create two FPDF objects; a temporary one that never gets output and the final, complete pdf that the end user will see.
I get my current Y coordinate in both objects. I then place the block of data using Cell() or MultiCell() (for this form, usually a combination of the two) into the temporary object, and then check the new Y coordinate. I can then do the math to determine the height of the block. -Note, the math can get a bit funky if the block breaks across pages. Remember to take your top and bottom margins into account.
Now that I know the height of a block, I can take the current Y coordinate of the destination object, subtract it from the total height of a page(minus bottom margin) to get my available space. If the block fits, place it, if not, add a page and place it at the top.
Repeat with each block.
Only caveat I see is if any single block is longer then an entire page, but with this form, that never happens. (famous last words...)
See also https://gist.github.com/johnballantyne/4089627
function GetMultiCellHeight($w, $h, $txt, $border=null, $align='J') {
// Calculate MultiCell with automatic or explicit line breaks height
// $border is un-used, but I kept it in the parameters to keep the call
// to this function consistent with MultiCell()
$cw = &$this->CurrentFont['cw'];
if($w==0)
$w = $this->w-$this->rMargin-$this->x;
$wmax = ($w-2*$this->cMargin)*1000/$this->FontSize;
$s = str_replace("\r",'',$txt);
$nb = strlen($s);
if($nb>0 && $s[$nb-1]=="\n")
$nb--;
$sep = -1;
$i = 0;
$j = 0;
$l = 0;
$ns = 0;
$height = 0;
while($i<$nb)
{
// Get next character
$c = $s[$i];
if($c=="\n")
{
// Explicit line break
if($this->ws>0)
{
$this->ws = 0;
$this->_out('0 Tw');
}
//Increase Height
$height += $h;
$i++;
$sep = -1;
$j = $i;
$l = 0;
$ns = 0;
continue;
}
if($c==' ')
{
$sep = $i;
$ls = $l;
$ns++;
}
$l += $cw[$c];
if($l>$wmax)
{
// Automatic line break
if($sep==-1)
{
if($i==$j)
$i++;
if($this->ws>0)
{
$this->ws = 0;
$this->_out('0 Tw');
}
//Increase Height
$height += $h;
}
else
{
if($align=='J')
{
$this->ws = ($ns>1) ? ($wmax-$ls)/1000*$this->FontSize/($ns-1) : 0;
$this->_out(sprintf('%.3F Tw',$this->ws*$this->k));
}
//Increase Height
$height += $h;
$i = $sep+1;
}
$sep = -1;
$j = $i;
$l = 0;
$ns = 0;
}
else
$i++;
}
// Last chunk
if($this->ws>0)
{
$this->ws = 0;
$this->_out('0 Tw');
}
//Increase Height
$height += $h;
return $height;
}
In addition to JLuc answer which is working as one would imagine it, too. Implementing this functionality however is not trivial and requires you to extend the main fpdf class.
First off if you have an extended fpdf already you could just copy JLuc's answer and skip this step. If not, go ahead like this:
create a File and Class that extends FPDF (name the file as your class name):
<?php
namespace YourNameSpace\Library;
class FpdfExtended extends \FPDF {
...
}
If you are not using namespaces you could just use require. Once that file is created insert the GetMultiCellHeight from the gist or this answer.
Now you'd need to instantiate the extended Class a simple constructor most likely suffices.
use YourNameSpace\Library\FpdfExtended
...
$pdf = new FpdfExtended();
...
now you could simply use the GetMultiCellHeight method to decide the next cells height like so:
$col1 = 'col 1 text without line break';
$col2 = 'col 2 text with line break \n and some more text';
// get next cell height
$nextHeight = $pdf->GetMultiCellHeight($width, $height, $col2);
$pdf->MultiCell($width, $nextHeight, $col1, 0, 'L', 1);
Good luck and happy coding you poor souls still using FPDF.
I have done it by this way. We are passing height in multicell. That is $h. Total height would be (number of lines * $h). Because $h is height of one line. And we want to know total height so total height is $totalLines * $h.

Border color of pattern image

I am working on image manipulation script.
I want to change outline color of the pattern image, not fill up pattern with specific color.
I have tried different method of GD library. I could fill up color of the pattern but could not set outline color of the pattern.
Please let me know if any one has solution for the same.
Thanks in advance.
Try something like this:
// Draw a border
function drawBorder(&$img, &$color, $thickness = 1)
{
$x1 = 0;
$y1 = 0;
$x2 = ImageSX($img) - 1;
$y2 = ImageSY($img) - 1;
for($i = 0; $i < $thickness; $i++)
{
ImageRectangle($img, $x1++, $y1++, $x2--, $y2--, $color);
}
}
Credit where due
You can read about imagerectangle() on the PHP man page. The key point here is that $color must be "A color identifier created with imagecolorallocate()."

pass files through php with rename

I have 1600 images, each 256px. These images have been sliced in photoshop from an image that is 10240px x 10240px in to the tiles. Problem is, photoshop has named them image_0001.png, image_0002.png...
I would like to rename these in to a usable file name such as image_x_y.png x being tile number in that row, y being tile number in that column...
And ideas how i can automate the renaming of these, or if not how i can pass these images through php, so that i can access image.php?x=2&y=1 ect...
thanks in advance
EDIT:
I have no permission to answer my own question but, used this as renaming would be required on every update. Not ideal...
<?php
$x=$_GET['x'];
$y=$_GET['y'];
$image=(($y-1)*40)+$x;
if ($image<10){
$image="0".$image;
}
$url="tiles/" . $image . ".jpg";
header("Location:" . $url);
?>
You could open the directory containing your files and then create a loop to access all images and rename them, like:
<?php
if ($handle = opendir('/path/to/image/directory')) {
while (false !== ($fileName = readdir($handle))) {
//do the renaming here
//$newName =
rename($fileName, $newName);
}
closedir($handle);
}
?>
Useful functions:
rename(),readdir(), readdir(), str_replace(), preg_replace()
Hope this helps!
You don't have to rename them, just calculate the "linear id" on every access.
so, assuming you have a 40 * 40 set of files, in image.php you'd have something like
$fileid = $x * 40 + y;
$filename = sprintf("image_%04d.png",$fileid);
// send the file with name $filename
What formula you need depends on how it was sliced, could as well be $y * 40 + x
The main advantage is that should your image be updated, it will be ready to use without the intermediate step of renaming the files.
Try this :
$dir = "your_dir";
$i = 0;
$j = 0;
$col = 5;
foreach(glob($dir . '/*') as $file)
{
rename($file, "image"."_".$j."_".$i);
$i++;
if($i % $col == 0)
{
$j++;
}
}
If you know for sure that the routine that converted your files always named the resulting images in the same order
i.e. top left = 0001 ...... Bottom right = 0016
Then it should be fairly simple to write a quick CLI script to go through and rename all your images.
Alternatively if you are going to use that same image converter again many times it may be simpler to make your image.php?x=1&y=2 script workout what file to serve then you wont need to do the renaming every time you get new images.
-read the soure folder with your images (http://php.net/manual/de/function.readdir.php)
-put the part of each imagename between "_" and "."
-parse it ($image_nr) as integer
-do the following:
$y = floor($image_nr/40);
$x = $image_nr%40;
finaly put each image in your destination directory with the new name
I Have not tested it but you could try using this:
$imagesInARow = 10240/256; //=> 40
$rows = 1600 / $imagesInARow; //=> 40
$imageIndex = 1;
for($i = 1; $i <= $rows; $i++) { // row iteration
for($j = 1; $j <= $imagesInARow; $j++) { // columns iteration
rename('image_'. str_pad($imageIndex, 4, '0', STR_PAD_LEFT).'.png',
"image_{$i}_{$j}.png");
$imageIndex ++;
}
}

Programatically select the largest image from an array of image URLS in PHP?

I've got an array with 4-5 local image urls in it.
I want to programatically return the URL of the largest image by image dimension in the array. How do I do that?
A very simplistic approach would be:
$files = array_combine($filenames,
array_map("array_sum", array_map("getimagesize", $filenames))
);
arsort($files);
print key($files); # largest image
This just adds $width+$height and checks for which file this adds up to the largest amount. Similar results to multiplying the two values. But in practice you might want to manually search for the max() value of width and height, if a 15x1000 should be treated as larger than 550x550.
getimagesize is what you want here. I've coded this in a very learn-friendly way. There are more advanced ways to do this, but they sometimes get obtuse.
$largest = -1;
$largest_image = null;
foreach ($images as $image)
{
$size = getimagesize($image);
$val = $size[0] * $size[1];
if ($val > $largest)
{
$largest_image = $image;
$largest = $val;
}
}
if ($largest_image != null)
{
// do magic
}

Categories