Skip to content

Repository files navigation

Slika - simple image handling for PHP

This is a library that covers only the bare basics you need when handling images:

  • resizing
  • cropping
  • rotation

It can use either PHP's libGD or a locally installed ImageMagick binary.

Installation

Use composer

composer require splitbrain/slika

Usage

Simply get an Adapter from the Slika factory, run some operations on it and call save.

Operations can be chained together. Consider the chain to be one command. Do not reuse the adapter returned by run(), it is a single use object. All operations can potentially throw a \splitbrain\slika\Exception.

Options (see below) can be passed as a second parameter to the run factory.

use \splitbrain\slika\Slika;
use \splitbrain\slika\Exception;
$options = [
'quality' => 75
];
try {
Slika::run('input.png', $options)
->resize(500,500)
->rotate(Slika::ROTATE_CCW
->save('output.jpg', 'jpg');
} catch (Exception$e) {
// conversion went wrong, handle it
}

Please also check the API Docs for details.

Operations

resize

All resize operations will keep the original aspect ratio of the image. There will be no distortion.

Keeping either width or height at zero will auto calculate the other value for you.

# fit the image into a bounding box of 500x500 pixels
Slika::run('input.jpg')->resize(500,500)->save('output.png', 'png');
# adjust the image to a maximum width of 500 pixels 
Slika::run('input.jpg')->resize(500,0)->save('output.png', 'png');
# adjust the image to a maximum height of 500 pixels 
Slika::run('input.jpg')->resize(0,500)->save('output.png', 'png');

By default, images smaller than the given dimensions are enlarged. That is not always desirable because the quality will suffer. Pass false as the third parameter to disable upscaling. The image is then never grown beyond its original size (it is returned unchanged when it already fits into the bounding box).

# fit into 500x500 but never enlarge a smaller image
Slika::run('input.jpg')->resize(500,500,false)->save('output.png', 'png');

crop

Similar to resizing, but this time the image will be cropped to fit the new aspect ratio.

Slika::run('input.jpg')->crop(500,500)->save('output.png', 'png');

Cropping also accepts the $upscale parameter. When set to false an image that is smaller than the requested crop area is never enlarged: dimensions larger than the image are cropped, smaller ones are left untouched, and an image that fits entirely into the area is returned as is. This means that the output image may be smaller than the requested crop area and might have a different aspect ratio than requested.

# crop to 500x500 but never enlarge a smaller image
Slika::run('input.jpg')->crop(500,500,false)->save('output.png', 'png');

rotate

Rotates the image. The parameter passed is one of the EXIF orientation flags:

orientation flags

For your convenience there are three Constants defined:

  • Slika::ROTATE_CCW counter clockwise rotation
  • Slika::ROTATE_CW clockwise rotation
  • Slika::ROTATE_TOPDOWN full 180 degree rotation
Slika::run('input.jpg')->rotate(Slika::ROTATE_CW)->save('output.png', 'png');

autorotate

Rotates the image according to the EXIF rotation tag if found.

Slika::run('input.jpg')->autorotate()->save('output.png', 'png');

Inspecting images without processing them

Sometimes you need to know what dimensions an image would have after a chain of operations without actually decoding pixels or calling ImageMagick — for example, to emit <img width="…" height="…"> attributes on a page that references a resize URL.

\splitbrain\slika\ImageInfo mirrors the Adapter's fluent API at the dimension level. It reads only getimagesize() and the EXIF orientation tag, never touching the pixel data.

use \splitbrain\slika\ImageInfo;
$info = newImageInfo('input.jpg');
// on-disk state (stable regardless of chain operations)$info->getRawWidth(); // e.g. 4000$info->getRawHeight(); // e.g. 3000$info->getExtension(); // 'jpeg'$info->getOrientation(); // EXIF orientation 1..8// the fluent chain simulates autorotate/rotate/resize/crop// and returns the final tracked dimensionslist($w, $h) = (newImageInfo('input.jpg'))
->autorotate()
->resize(500, 500)
->getDimensions();

This lets you predict the output of Slika::run(...)->autorotate()->resize(500,500) without doing any actual image work.

Options

Options can be passed as associatiave array as the second parameter in Slika::run.

The following options are availble currently:

OptionDefaultDescription
imconvert/usr/bin/convertThe path to ImageMagick's convert binary
quality92The quality when writing JPEG images
imlimitssee belowImageMagick resource limits, as an associative array

Resource limits

A small image file can still decode into an enormous amount of pixels. ImageMagick keeps the decoded image in a pixel cache, so a 3 MB JPEG holding 170 megapixels needs roughly 2 GB of memory to resize — enough to exhaust a web server.

The imlimits option caps that. Its entries are passed to ImageMagick as -limit arguments and default to:

$options = ['imlimits' => [
'memory' => '256MiB',
'map' => '512MiB',
'disk' => '1GiB',
]];

With these, memory use stays around 50 to 150 MB no matter how large the input is: once the pixel cache outgrows memory it spills to a memory mapped file, and once it outgrows map it spills to a temporary file on disk.

disk bounds that temporary file and therefore also acts as an upper bound on the image size Slika will process at all. An image needing more cache than disk allows makes ImageMagick fail, which Slika reports as an Exception. How many pixels fit into the default 1 GB depends on how ImageMagick was compiled — about 90 megapixels for a Q16-HDRI build, more for Q8.

Raise the limit to process larger images, at the cost of more temporary disk usage:

Slika::run('huge.jpg', ['imlimits' => ['disk' => '4GiB']])
->resize(500, 500)
->save('output.jpg', 'jpg');

Entries are merged into the defaults individually, so the above keeps the default memory and map values. Setting an entry to null drops that limit and restores ImageMagick's own default, which is usually unlimited:

# no disk limit at all
Slika::run('huge.jpg', ['imlimits' => ['disk' => null]])

Any limit ImageMagick understands can be used as a key. width and height reject images exceeding the given pixel dimensions outright, which is cheaper than letting the pixel cache fill up first, and time aborts processing after the given number of seconds:

$options = ['imlimits' => [
'memory' => '256MiB',
'map' => '512MiB',
'disk' => '1GiB',
'width' => 20000,
'height' => 20000,
'time' => 30,
]];

These limits apply to the ImageMagick adapter only. The GD adapter is bound by PHP's own memory_limit instead.

Releases

Used by

Contributors

Languages