xref: /dokuwiki/vendor/splitbrain/slika/src/GdAdapter.php (revision 2cadabe7de17786c9119ff71ba98edc20715e781)
192a8473aSAndreas Gohr<?php /** @noinspection PhpComposerExtensionStubsInspection */
292a8473aSAndreas Gohr
392a8473aSAndreas Gohr
492a8473aSAndreas Gohrnamespace splitbrain\slika;
592a8473aSAndreas Gohr
65c25a071SAndreas Gohr/**
75c25a071SAndreas Gohr * Image processing adapter for PHP's libGD
85c25a071SAndreas Gohr */
992a8473aSAndreas Gohrclass GdAdapter extends Adapter
1092a8473aSAndreas Gohr{
1192a8473aSAndreas Gohr    /** @var resource libGD image */
1292a8473aSAndreas Gohr    protected $image;
1392a8473aSAndreas Gohr    /** @var int width of the current image */
1492a8473aSAndreas Gohr    protected $width = 0;
1592a8473aSAndreas Gohr    /** @var int height of the current image */
1692a8473aSAndreas Gohr    protected $height = 0;
1792a8473aSAndreas Gohr    /** @var string the extension of the file we're working with */
1892a8473aSAndreas Gohr    protected $extension;
1992a8473aSAndreas Gohr
2092a8473aSAndreas Gohr
2192a8473aSAndreas Gohr    /** @inheritDoc */
2292a8473aSAndreas Gohr    public function __construct($imagepath, $options = [])
2392a8473aSAndreas Gohr    {
2492a8473aSAndreas Gohr        parent::__construct($imagepath, $options);
2592a8473aSAndreas Gohr        $this->image = $this->loadImage($imagepath);
2692a8473aSAndreas Gohr    }
2792a8473aSAndreas Gohr
2892a8473aSAndreas Gohr    /**
2992a8473aSAndreas Gohr     * Clean up
3092a8473aSAndreas Gohr     */
3192a8473aSAndreas Gohr    public function __destruct()
3292a8473aSAndreas Gohr    {
3392a8473aSAndreas Gohr        if (is_resource($this->image)) {
3492a8473aSAndreas Gohr            imagedestroy($this->image);
3592a8473aSAndreas Gohr        }
3692a8473aSAndreas Gohr    }
3792a8473aSAndreas Gohr
3892a8473aSAndreas Gohr    /** @inheritDoc
3992a8473aSAndreas Gohr     * @throws Exception
4092a8473aSAndreas Gohr     * @link https://gist.github.com/EionRobb/8e0c76178522bc963c75caa6a77d3d37#file-imagecreatefromstring_autorotate-php-L15
4192a8473aSAndreas Gohr     */
4292a8473aSAndreas Gohr    public function autorotate()
4392a8473aSAndreas Gohr    {
4492a8473aSAndreas Gohr        if ($this->extension !== 'jpeg') {
4592a8473aSAndreas Gohr            return $this;
4692a8473aSAndreas Gohr        }
4792a8473aSAndreas Gohr
4892a8473aSAndreas Gohr        $orientation = 1;
4992a8473aSAndreas Gohr
5092a8473aSAndreas Gohr        if (function_exists('exif_read_data')) {
5192a8473aSAndreas Gohr            // use PHP's exif capablities
5292a8473aSAndreas Gohr            $exif = exif_read_data($this->imagepath);
5392a8473aSAndreas Gohr            if (!empty($exif['Orientation'])) {
5492a8473aSAndreas Gohr                $orientation = $exif['Orientation'];
5592a8473aSAndreas Gohr            }
5692a8473aSAndreas Gohr        } else {
5792a8473aSAndreas Gohr            // grep the exif info from the raw contents
5892a8473aSAndreas Gohr            // we read only the first 70k bytes
5992a8473aSAndreas Gohr            $data = file_get_contents($this->imagepath, false, null, 0, 70000);
6092a8473aSAndreas Gohr            if (preg_match('@\x12\x01\x03\x00\x01\x00\x00\x00(.)\x00\x00\x00@', $data, $matches)) {
6192a8473aSAndreas Gohr                // Little endian EXIF
6292a8473aSAndreas Gohr                $orientation = ord($matches[1]);
6392a8473aSAndreas Gohr            } else if (preg_match('@\x01\x12\x00\x03\x00\x00\x00\x01\x00(.)\x00\x00@', $data, $matches)) {
6492a8473aSAndreas Gohr                // Big endian EXIF
6592a8473aSAndreas Gohr                $orientation = ord($matches[1]);
6692a8473aSAndreas Gohr            }
6792a8473aSAndreas Gohr        }
6892a8473aSAndreas Gohr
6992a8473aSAndreas Gohr        return $this->rotate($orientation);
7092a8473aSAndreas Gohr    }
7192a8473aSAndreas Gohr
7292a8473aSAndreas Gohr    /**
7392a8473aSAndreas Gohr     * @inheritDoc
7492a8473aSAndreas Gohr     * @throws Exception
7592a8473aSAndreas Gohr     */
7692a8473aSAndreas Gohr    public function rotate($orientation)
7792a8473aSAndreas Gohr    {
7892a8473aSAndreas Gohr        $orientation = (int)$orientation;
7992a8473aSAndreas Gohr        if ($orientation < 0 || $orientation > 8) {
8092a8473aSAndreas Gohr            throw new Exception('Unknown rotation given');
8192a8473aSAndreas Gohr        }
8292a8473aSAndreas Gohr
8392a8473aSAndreas Gohr        if ($orientation <= 1) {
8492a8473aSAndreas Gohr            // no rotation wanted
8592a8473aSAndreas Gohr            return $this;
8692a8473aSAndreas Gohr        }
8792a8473aSAndreas Gohr
8892a8473aSAndreas Gohr        // fill color
8992a8473aSAndreas Gohr        $transparency = imagecolorallocatealpha($this->image, 0, 0, 0, 127);
9092a8473aSAndreas Gohr
9192a8473aSAndreas Gohr        // rotate
9292a8473aSAndreas Gohr        if (in_array($orientation, [3, 4])) {
9392a8473aSAndreas Gohr            $image = imagerotate($this->image, 180, $transparency, 1);
9492a8473aSAndreas Gohr        }
9592a8473aSAndreas Gohr        if (in_array($orientation, [5, 6])) {
9692a8473aSAndreas Gohr            $image = imagerotate($this->image, -90, $transparency, 1);
9792a8473aSAndreas Gohr            list($this->width, $this->height) = [$this->height, $this->width];
9892a8473aSAndreas Gohr        } elseif (in_array($orientation, [7, 8])) {
9992a8473aSAndreas Gohr            $image = imagerotate($this->image, 90, $transparency, 1);
10092a8473aSAndreas Gohr            list($this->width, $this->height) = [$this->height, $this->width];
10192a8473aSAndreas Gohr        }
10292a8473aSAndreas Gohr        /** @var resource $image is now defined */
10392a8473aSAndreas Gohr
10492a8473aSAndreas Gohr        // additionally flip
10592a8473aSAndreas Gohr        if (in_array($orientation, [2, 5, 7, 4])) {
10692a8473aSAndreas Gohr            imageflip($image, IMG_FLIP_HORIZONTAL);
10792a8473aSAndreas Gohr        }
10892a8473aSAndreas Gohr
10992a8473aSAndreas Gohr        imagedestroy($this->image);
11092a8473aSAndreas Gohr        $this->image = $image;
11192a8473aSAndreas Gohr
11292a8473aSAndreas Gohr        //keep png alpha channel if possible
11392a8473aSAndreas Gohr        if ($this->extension == 'png' && function_exists('imagesavealpha')) {
11492a8473aSAndreas Gohr            imagealphablending($this->image, false);
11592a8473aSAndreas Gohr            imagesavealpha($this->image, true);
11692a8473aSAndreas Gohr        }
11792a8473aSAndreas Gohr
11892a8473aSAndreas Gohr        return $this;
11992a8473aSAndreas Gohr    }
12092a8473aSAndreas Gohr
12192a8473aSAndreas Gohr    /**
12292a8473aSAndreas Gohr     * @inheritDoc
12392a8473aSAndreas Gohr     * @throws Exception
12492a8473aSAndreas Gohr     */
12592a8473aSAndreas Gohr    public function resize($width, $height)
12692a8473aSAndreas Gohr    {
12792a8473aSAndreas Gohr        list($width, $height) = $this->boundingBox($width, $height);
12892a8473aSAndreas Gohr        $this->resizeOperation($width, $height);
12992a8473aSAndreas Gohr        return $this;
13092a8473aSAndreas Gohr    }
13192a8473aSAndreas Gohr
13292a8473aSAndreas Gohr    /**
13392a8473aSAndreas Gohr     * @inheritDoc
13492a8473aSAndreas Gohr     * @throws Exception
13592a8473aSAndreas Gohr     */
13692a8473aSAndreas Gohr    public function crop($width, $height)
13792a8473aSAndreas Gohr    {
13892a8473aSAndreas Gohr        list($this->width, $this->height, $offsetX, $offsetY) = $this->cropPosition($width, $height);
13992a8473aSAndreas Gohr        $this->resizeOperation($width, $height, $offsetX, $offsetY);
14092a8473aSAndreas Gohr        return $this;
14192a8473aSAndreas Gohr    }
14292a8473aSAndreas Gohr
14392a8473aSAndreas Gohr    /**
14492a8473aSAndreas Gohr     * @inheritDoc
14592a8473aSAndreas Gohr     * @throws Exception
14692a8473aSAndreas Gohr     */
14792a8473aSAndreas Gohr    public function save($path, $extension = '')
14892a8473aSAndreas Gohr    {
14992a8473aSAndreas Gohr        if ($extension === 'jpg') {
15092a8473aSAndreas Gohr            $extension = 'jpeg';
15192a8473aSAndreas Gohr        }
15292a8473aSAndreas Gohr        if ($extension === '') {
15392a8473aSAndreas Gohr            $extension = $this->extension;
15492a8473aSAndreas Gohr        }
15592a8473aSAndreas Gohr        $saver = 'image' . $extension;
15692a8473aSAndreas Gohr        if (!function_exists($saver)) {
15792a8473aSAndreas Gohr            throw new Exception('Can not save image format ' . $extension);
15892a8473aSAndreas Gohr        }
15992a8473aSAndreas Gohr
16092a8473aSAndreas Gohr        if ($extension == 'jpeg') {
16192a8473aSAndreas Gohr            imagejpeg($this->image, $path, $this->options['quality']);
16292a8473aSAndreas Gohr        } else {
16392a8473aSAndreas Gohr            $saver($this->image, $path);
16492a8473aSAndreas Gohr        }
16592a8473aSAndreas Gohr
16692a8473aSAndreas Gohr        imagedestroy($this->image);
16792a8473aSAndreas Gohr    }
16892a8473aSAndreas Gohr
16992a8473aSAndreas Gohr    /**
17092a8473aSAndreas Gohr     * Initialize libGD on the given image
17192a8473aSAndreas Gohr     *
17292a8473aSAndreas Gohr     * @param string $path
17392a8473aSAndreas Gohr     * @return resource
17492a8473aSAndreas Gohr     * @throws Exception
17592a8473aSAndreas Gohr     */
17692a8473aSAndreas Gohr    protected function loadImage($path)
17792a8473aSAndreas Gohr    {
17892a8473aSAndreas Gohr        // Figure out the file info
17992a8473aSAndreas Gohr        $info = getimagesize($path);
18092a8473aSAndreas Gohr        if ($info === false) {
18192a8473aSAndreas Gohr            throw new Exception('Failed to read image information');
18292a8473aSAndreas Gohr        }
18392a8473aSAndreas Gohr        $this->width = $info[0];
18492a8473aSAndreas Gohr        $this->height = $info[1];
18592a8473aSAndreas Gohr
18692a8473aSAndreas Gohr        // what type of image is it?
18792a8473aSAndreas Gohr        $this->extension = image_type_to_extension($info[2], false);
18892a8473aSAndreas Gohr        $creator = 'imagecreatefrom' . $this->extension;
18992a8473aSAndreas Gohr        if (!function_exists($creator)) {
19092a8473aSAndreas Gohr            throw new Exception('Can not work with image format ' . $this->extension);
19192a8473aSAndreas Gohr        }
19292a8473aSAndreas Gohr
19392a8473aSAndreas Gohr        // create the GD instance
19492a8473aSAndreas Gohr        $image = @$creator($path);
19592a8473aSAndreas Gohr
19692a8473aSAndreas Gohr        if ($image === false) {
19792a8473aSAndreas Gohr            throw new Exception('Failed to load image wiht libGD');
19892a8473aSAndreas Gohr        }
19992a8473aSAndreas Gohr
20092a8473aSAndreas Gohr        return $image;
20192a8473aSAndreas Gohr    }
20292a8473aSAndreas Gohr
20392a8473aSAndreas Gohr    /**
20492a8473aSAndreas Gohr     * Creates a new blank image to which we can copy
20592a8473aSAndreas Gohr     *
20692a8473aSAndreas Gohr     * Tries to set up alpha/transparency stuff correctly
20792a8473aSAndreas Gohr     *
20892a8473aSAndreas Gohr     * @param int $width
20992a8473aSAndreas Gohr     * @param int $height
21092a8473aSAndreas Gohr     * @return resource
21192a8473aSAndreas Gohr     * @throws Exception
21292a8473aSAndreas Gohr     */
21392a8473aSAndreas Gohr    protected function createImage($width, $height)
21492a8473aSAndreas Gohr    {
21592a8473aSAndreas Gohr        // create a canvas to copy to, use truecolor if possible (except for gif)
21692a8473aSAndreas Gohr        $canvas = false;
21792a8473aSAndreas Gohr        if (function_exists('imagecreatetruecolor') && $this->extension != 'gif') {
21892a8473aSAndreas Gohr            $canvas = @imagecreatetruecolor($width, $height);
21992a8473aSAndreas Gohr        }
22092a8473aSAndreas Gohr        if (!$canvas) {
22192a8473aSAndreas Gohr            $canvas = @imagecreate($width, $height);
22292a8473aSAndreas Gohr        }
22392a8473aSAndreas Gohr        if (!$canvas) {
22492a8473aSAndreas Gohr            throw new Exception('Failed to create new canvas');
22592a8473aSAndreas Gohr        }
22692a8473aSAndreas Gohr
22792a8473aSAndreas Gohr        //keep png alpha channel if possible
22892a8473aSAndreas Gohr        if ($this->extension == 'png' && function_exists('imagesavealpha')) {
22992a8473aSAndreas Gohr            imagealphablending($canvas, false);
23092a8473aSAndreas Gohr            imagesavealpha($canvas, true);
23192a8473aSAndreas Gohr        }
23292a8473aSAndreas Gohr
23392a8473aSAndreas Gohr        //keep gif transparent color if possible
234*2cadabe7SAndreas Gohr        if ($this->extension == 'gif') {
235*2cadabe7SAndreas Gohr            $this->keepGifTransparency($this->image, $canvas);
236*2cadabe7SAndreas Gohr        }
237*2cadabe7SAndreas Gohr
238*2cadabe7SAndreas Gohr        return $canvas;
239*2cadabe7SAndreas Gohr    }
240*2cadabe7SAndreas Gohr
241*2cadabe7SAndreas Gohr    /**
242*2cadabe7SAndreas Gohr     * Copy transparency from gif to gif
243*2cadabe7SAndreas Gohr     *
244*2cadabe7SAndreas Gohr     * If no transparency is found or the PHP does not support it, the canvas is filled with white
245*2cadabe7SAndreas Gohr     *
246*2cadabe7SAndreas Gohr     * @param resource $image Original image
247*2cadabe7SAndreas Gohr     * @param resource $canvas New, empty image
248*2cadabe7SAndreas Gohr     * @return void
249*2cadabe7SAndreas Gohr     */
250*2cadabe7SAndreas Gohr    protected function keepGifTransparency($image, $canvas)
251*2cadabe7SAndreas Gohr    {
252*2cadabe7SAndreas Gohr        if (!function_exists('imagefill') || !function_exists('imagecolorallocate')) {
253*2cadabe7SAndreas Gohr            return;
254*2cadabe7SAndreas Gohr        }
255*2cadabe7SAndreas Gohr
256*2cadabe7SAndreas Gohr        try {
257*2cadabe7SAndreas Gohr            if (!function_exists('imagecolorsforindex') || !function_exists('imagecolortransparent')) {
258*2cadabe7SAndreas Gohr                throw new \Exception('missing alpha methods');
259*2cadabe7SAndreas Gohr            }
260*2cadabe7SAndreas Gohr
261*2cadabe7SAndreas Gohr            $transcolorindex = @imagecolortransparent($image);
262*2cadabe7SAndreas Gohr            $transcolor = @imagecolorsforindex($image, $transcolorindex);
263*2cadabe7SAndreas Gohr            if (!$transcolor) {
264*2cadabe7SAndreas Gohr                // pre-PHP8 false is returned, in PHP8 an exception is thrown
265*2cadabe7SAndreas Gohr                throw new \ValueError('no valid alpha color');
266*2cadabe7SAndreas Gohr            }
267*2cadabe7SAndreas Gohr
26892a8473aSAndreas Gohr            $transcolorindex = @imagecolorallocate(
26992a8473aSAndreas Gohr                $canvas,
27092a8473aSAndreas Gohr                $transcolor['red'],
27192a8473aSAndreas Gohr                $transcolor['green'],
27292a8473aSAndreas Gohr                $transcolor['blue']
27392a8473aSAndreas Gohr            );
27492a8473aSAndreas Gohr            @imagefill($canvas, 0, 0, $transcolorindex);
27592a8473aSAndreas Gohr            @imagecolortransparent($canvas, $transcolorindex);
27692a8473aSAndreas Gohr
277*2cadabe7SAndreas Gohr        } catch (\Throwable $ignored) {
278*2cadabe7SAndreas Gohr            //filling with white
279*2cadabe7SAndreas Gohr            $whitecolorindex = @imagecolorallocate($canvas, 255, 255, 255);
280*2cadabe7SAndreas Gohr            @imagefill($canvas, 0, 0, $whitecolorindex);
281*2cadabe7SAndreas Gohr        }
28292a8473aSAndreas Gohr    }
28392a8473aSAndreas Gohr
28492a8473aSAndreas Gohr    /**
28592a8473aSAndreas Gohr     * Calculate new size
28692a8473aSAndreas Gohr     *
28792a8473aSAndreas Gohr     * If widht and height are given, the new size will be fit within this bounding box.
28892a8473aSAndreas Gohr     * If only one value is given the other is adjusted to match according to the aspect ratio
28992a8473aSAndreas Gohr     *
29092a8473aSAndreas Gohr     * @param int $width width of the bounding box
29192a8473aSAndreas Gohr     * @param int $height height of the bounding box
29292a8473aSAndreas Gohr     * @return array (width, height)
29392a8473aSAndreas Gohr     * @throws Exception
29492a8473aSAndreas Gohr     */
29592a8473aSAndreas Gohr    protected function boundingBox($width, $height)
29692a8473aSAndreas Gohr    {
2976cb05674SAndreas Gohr        $width = $this->cleanDimension($width, $this->width);
2986cb05674SAndreas Gohr        $height = $this->cleanDimension($height, $this->height);
2996cb05674SAndreas Gohr
30092a8473aSAndreas Gohr        if ($width == 0 && $height == 0) {
30192a8473aSAndreas Gohr            throw new Exception('You can not resize to 0x0');
30292a8473aSAndreas Gohr        }
30392a8473aSAndreas Gohr
30492a8473aSAndreas Gohr        if (!$height) {
30592a8473aSAndreas Gohr            // adjust to match width
30692a8473aSAndreas Gohr            $height = round(($width * $this->height) / $this->width);
30792a8473aSAndreas Gohr        } else if (!$width) {
30892a8473aSAndreas Gohr            // adjust to match height
30992a8473aSAndreas Gohr            $width = round(($height * $this->width) / $this->height);
31092a8473aSAndreas Gohr        } else {
31192a8473aSAndreas Gohr            // fit into bounding box
31292a8473aSAndreas Gohr            $scale = min($width / $this->width, $height / $this->height);
31392a8473aSAndreas Gohr            $width = $this->width * $scale;
31492a8473aSAndreas Gohr            $height = $this->height * $scale;
31592a8473aSAndreas Gohr        }
31692a8473aSAndreas Gohr
31792a8473aSAndreas Gohr        return [$width, $height];
31892a8473aSAndreas Gohr    }
31992a8473aSAndreas Gohr
32092a8473aSAndreas Gohr    /**
3216cb05674SAndreas Gohr     * Ensure the given Dimension is a proper pixel value
3226cb05674SAndreas Gohr     *
3236cb05674SAndreas Gohr     * When a percentage is given, the value is calculated based on the given original dimension
3246cb05674SAndreas Gohr     *
3256cb05674SAndreas Gohr     * @param int|string $dim New Dimension
3266cb05674SAndreas Gohr     * @param int $orig Original dimension
3276cb05674SAndreas Gohr     * @return int
3286cb05674SAndreas Gohr     */
3296cb05674SAndreas Gohr    protected function cleanDimension($dim, $orig)
3306cb05674SAndreas Gohr    {
3316cb05674SAndreas Gohr        if ($dim && substr($dim, -1) == '%') {
3326cb05674SAndreas Gohr            $dim = round($orig * ((float)$dim / 100));
3336cb05674SAndreas Gohr        } else {
3346cb05674SAndreas Gohr            $dim = (int)$dim;
3356cb05674SAndreas Gohr        }
3366cb05674SAndreas Gohr
3376cb05674SAndreas Gohr        return $dim;
3386cb05674SAndreas Gohr    }
3396cb05674SAndreas Gohr
3406cb05674SAndreas Gohr    /**
34192a8473aSAndreas Gohr     * Calculates crop position
34292a8473aSAndreas Gohr     *
34392a8473aSAndreas Gohr     * Given the wanted final size, this calculates which exact area needs to be cut
34492a8473aSAndreas Gohr     * from the original image to be then resized to the wanted dimensions.
34592a8473aSAndreas Gohr     *
34692a8473aSAndreas Gohr     * @param int $width
34792a8473aSAndreas Gohr     * @param int $height
34892a8473aSAndreas Gohr     * @return array (cropWidth, cropHeight, offsetX, offsetY)
34992a8473aSAndreas Gohr     * @throws Exception
35092a8473aSAndreas Gohr     */
35192a8473aSAndreas Gohr    protected function cropPosition($width, $height)
35292a8473aSAndreas Gohr    {
35392a8473aSAndreas Gohr        if ($width == 0 && $height == 0) {
35492a8473aSAndreas Gohr            throw new Exception('You can not crop to 0x0');
35592a8473aSAndreas Gohr        }
35692a8473aSAndreas Gohr
35792a8473aSAndreas Gohr        if (!$height) {
35892a8473aSAndreas Gohr            $height = $width;
35992a8473aSAndreas Gohr        }
36092a8473aSAndreas Gohr
36192a8473aSAndreas Gohr        if (!$width) {
36292a8473aSAndreas Gohr            $width = $height;
36392a8473aSAndreas Gohr        }
36492a8473aSAndreas Gohr
36592a8473aSAndreas Gohr        // calculate ratios
36692a8473aSAndreas Gohr        $oldRatio = $this->width / $this->height;
36792a8473aSAndreas Gohr        $newRatio = $width / $height;
36892a8473aSAndreas Gohr
36992a8473aSAndreas Gohr        // calulate new size
37092a8473aSAndreas Gohr        if ($newRatio >= 1) {
37192a8473aSAndreas Gohr            if ($newRatio > $oldRatio) {
37292a8473aSAndreas Gohr                $cropWidth = $this->width;
37392a8473aSAndreas Gohr                $cropHeight = (int)($this->width / $newRatio);
37492a8473aSAndreas Gohr            } else {
37592a8473aSAndreas Gohr                $cropWidth = (int)($this->height * $newRatio);
37692a8473aSAndreas Gohr                $cropHeight = $this->height;
37792a8473aSAndreas Gohr            }
37892a8473aSAndreas Gohr        } else {
37992a8473aSAndreas Gohr            if ($newRatio < $oldRatio) {
38092a8473aSAndreas Gohr                $cropWidth = (int)($this->height * $newRatio);
38192a8473aSAndreas Gohr                $cropHeight = $this->height;
38292a8473aSAndreas Gohr            } else {
38392a8473aSAndreas Gohr                $cropWidth = $this->width;
38492a8473aSAndreas Gohr                $cropHeight = (int)($this->width / $newRatio);
38592a8473aSAndreas Gohr            }
38692a8473aSAndreas Gohr        }
38792a8473aSAndreas Gohr
38892a8473aSAndreas Gohr        // calculate crop offset
38992a8473aSAndreas Gohr        $offsetX = (int)(($this->width - $cropWidth) / 2);
39092a8473aSAndreas Gohr        $offsetY = (int)(($this->height - $cropHeight) / 2);
39192a8473aSAndreas Gohr
39292a8473aSAndreas Gohr        return [$cropWidth, $cropHeight, $offsetX, $offsetY];
39392a8473aSAndreas Gohr    }
39492a8473aSAndreas Gohr
39592a8473aSAndreas Gohr    /**
39692a8473aSAndreas Gohr     * resize or crop images using PHP's libGD support
39792a8473aSAndreas Gohr     *
39892a8473aSAndreas Gohr     * @param int $toWidth desired width
39992a8473aSAndreas Gohr     * @param int $toHeight desired height
40092a8473aSAndreas Gohr     * @param int $offsetX offset of crop centre
40192a8473aSAndreas Gohr     * @param int $offsetY offset of crop centre
40292a8473aSAndreas Gohr     * @throws Exception
40392a8473aSAndreas Gohr     */
40492a8473aSAndreas Gohr    protected function resizeOperation($toWidth, $toHeight, $offsetX = 0, $offsetY = 0)
40592a8473aSAndreas Gohr    {
40692a8473aSAndreas Gohr        $newimg = $this->createImage($toWidth, $toHeight);
40792a8473aSAndreas Gohr
40892a8473aSAndreas Gohr        //try resampling first, fall back to resizing
40992a8473aSAndreas Gohr        if (
41092a8473aSAndreas Gohr            !function_exists('imagecopyresampled') ||
41192a8473aSAndreas Gohr            !@imagecopyresampled(
41292a8473aSAndreas Gohr                $newimg,
41392a8473aSAndreas Gohr                $this->image,
41492a8473aSAndreas Gohr                0,
41592a8473aSAndreas Gohr                0,
41692a8473aSAndreas Gohr                $offsetX,
41792a8473aSAndreas Gohr                $offsetY,
41892a8473aSAndreas Gohr                $toWidth,
41992a8473aSAndreas Gohr                $toHeight,
42092a8473aSAndreas Gohr                $this->width,
42192a8473aSAndreas Gohr                $this->height
42292a8473aSAndreas Gohr            )
42392a8473aSAndreas Gohr        ) {
42492a8473aSAndreas Gohr            imagecopyresized(
42592a8473aSAndreas Gohr                $newimg,
42692a8473aSAndreas Gohr                $this->image,
42792a8473aSAndreas Gohr                0,
42892a8473aSAndreas Gohr                0,
42992a8473aSAndreas Gohr                $offsetX,
43092a8473aSAndreas Gohr                $offsetY,
43192a8473aSAndreas Gohr                $toWidth,
43292a8473aSAndreas Gohr                $toHeight,
43392a8473aSAndreas Gohr                $this->width,
43492a8473aSAndreas Gohr                $this->height
43592a8473aSAndreas Gohr            );
43692a8473aSAndreas Gohr        }
43792a8473aSAndreas Gohr
43892a8473aSAndreas Gohr        // destroy original GD image ressource and replace with new one
43992a8473aSAndreas Gohr        imagedestroy($this->image);
44092a8473aSAndreas Gohr        $this->image = $newimg;
44192a8473aSAndreas Gohr        $this->width = $toWidth;
44292a8473aSAndreas Gohr        $this->height = $toHeight;
44392a8473aSAndreas Gohr    }
44492a8473aSAndreas Gohr
44592a8473aSAndreas Gohr}
446