thephpleague / glide

Wonderfully easy on-demand image manipulation library with an HTTP based API.
http://glide.thephpleague.com
MIT License
2.55k stars 199 forks source link

Have crop not increase the size of the input image if it is smaller than the output size (or have a new fit option that does this) #293

Open PatrikCo opened 3 years ago

PatrikCo commented 3 years ago

Love the project!

I'm using it inside a CMS, so i cannot control what the input images are. Most of them are bigger than the output size, so Glide and its crop option do exactly what is needed.

But once in a while an image gets uploaded by the users that is actually a bit smaller than the output size. The glide crop feature then proceeds to enlarge the image, therefore transferring more pixels to the customer than were originally there.

I think this step of increasing the resolution should be done by the visitors browser instead (we specified the image resolution in the CSS as well, so the browser will do it). By not unnecessarily increasing the resolution we would also save bandwidth.

So what I wish is that crop in this case crops the image to the target aspect ratio, and not unnecessarily increases the resolution, and deliver that image.

Or if you wish to keep the crop behaviour the same, I wish that there is a new fit option, probably called crop-max.

Many thanks, Patrik

mlazze commented 3 years ago

Facing the same problem. We also require that when we need a bigger image, void space should be filled with white.

So if i say crop to 400x400 the resulting image will be exactly 400x400, without upscaling, cropping unneeded parts and adding white background if needed.

Here's my cheapish solution, supporting crop-top-left and similar crops too. crop-x-y is not supported.

Instead of using the default League\Glide\Manipulators\Size manipulator, i'm using this:

use Intervention\Image\Image;
use League\Glide\Manipulators\Size;

class CropAddsBackgroundIfNeededSize extends Size {
    /**
     * This crops as League\Glide\Manipulators\Size if no upscale needed, but instead of upscaling it adds white space
     * @inheritDoc
     */
    public function runCropResize(Image $image, $width, $height) {
        [$resize_width, $resize_height] = $this->resolveCropResizeDimensions($image, $width, $height);

        $zoom = $this->getCrop()[2];

        $image->resize(
            $resize_width * $zoom,
            $resize_height * $zoom,
            function ($constraint) {
                $constraint->aspectRatio();
                $constraint->upsize();
            }
        );

        $resultIsWider  = $width > $image->width();
        $resultIsTaller = $height > $image->height();
        if (! $resultIsWider && ! $resultIsTaller) {
            [$offset_x, $offset_y] = $this->resolveCropOffset($image, $width, $height);

            return $image->crop($width, $height, $offset_x, $offset_y);
        }
        if ($resultIsWider && $resultIsTaller) {
            $anchor = 'center';
        } elseif ($resultIsWider) {
            $cropMethods = [
                'crop-top-left'     => 'top',
                'crop-top'          => 'top',
                'crop-top-right'    => 'top',
                'crop-left'         => 'center',
                'crop-center'       => 'center',
                'crop-right'        => 'center',
                'crop-bottom-left'  => 'bottom',
                'crop-bottom'       => 'bottom',
                'crop-bottom-right' => 'bottom',
            ];
            $anchor      = $cropMethods[$this->fit] ?? 'center';
        } else {
            $cropMethods = [
                'crop-top-left'     => 'left',
                'crop-top'          => 'center',
                'crop-top-right'    => 'right',
                'crop-left'         => 'left',
                'crop-center'       => 'center',
                'crop-right'        => 'right',
                'crop-bottom-left'  => 'left',
                'crop-bottom'       => 'center',
                'crop-bottom-right' => 'right',
            ];
            $anchor      = $cropMethods[$this->fit] ?? 'center';
        }

        return $image->resizeCanvas($width, $height, $anchor);
    }
}