Intervention Image rounded corners upload
Asked Answered
L

2

7

I'm trying to upload my files as circles, but I can't make it work. I've seen some topics about applying a mask to the image, but when I apply the mask it takes way to long and the server shuts the request down.

I'm using the Intervention Image library for Laravel

My code is as follows:

$identifier = "{$this->loggedUser->id}" . str_random(9) . ".{$file->getClientOriginalExtension()}";
$mask = $this->createCircleMask(200, 200);
$thumbMask = $this->createCircleMask(40, 40);
Image::make($file->getRealPath())->mask($mask)->save(public_path("images/profile/{$identifier}"));
Image::make($file->getRealPath())->mask($thumbMask)->save(public_path("images/profile/thumbs/{$identifier}"));

The createCircleMask method looks like this:

public function createCircleMask($width, $height)
{
    $circle = Image::canvas($width, $height, '#000000');
    return $circle->circle($width - 1, $width / 2, $height / 2);
}
Lilybel answered 13/4, 2015 at 10:5 Comment(0)
M
13

Here is a function that works in my case. But ONLY if I use the imagick driver. The standard gd library is very very slow, at least on my test computer. You can have a look at vendor\intervention\image\src\Intervention\Image\Gd\Commands\MaskCommand.php to find out why.

public function upload() {

    $path = storage_path('app')."/";

    $image = \Image::make(\Input::file('image'));
    $image->encode('png');

    /* if you want to have a perfect and complete circle using the whole width and height the image
     must be shaped as as square. If your images are not guaranteed to be a square maybe you could
     use Intervention's fit() function */
    //  $image->fit(300,300);

    // create empty canvas
    $width = $image->getWidth();
    $height = $image->getHeight();
    $mask = \Image::canvas($width, $height);

    // draw a white circle
    $mask->circle($width, $width/2, $height/2, function ($draw) {
        $draw->background('#fff');
    });

    $image->mask($mask, false);
    $image->save($path."circled.png");

}
Mechanics answered 14/4, 2015 at 6:47 Comment(0)
A
0

For those who are using intervention/image v3 in Laravel since the mask method has been removed, we can use a custom class to do the job.

<?php

namespace App\Helpers\Image;

class CircleImage
{
    public $img;

    public $width;

    public $height;
    public $minSize;

    public function __construct($img = null)
    {
        if (!empty($img)) {
            $this->img = imagecreatefromstring($img);
            $this->width = imagesx($this->img); // Image original width
            $this->height = imagesy($this->img); // Image original height
            // Get the minimum size
            // This will help cut the image in a circular shape
            $this->minSize = min($this->width, $this->height);
        }
    }

    public function make(): string
    {
        $radius = $this->minSize / 2;
        // First we crop the image from center
        $cropped = imagecrop($this->img, [
            // Calculation summary:
            // here width/2 gives us the center point, suppose our image width is 200,
            // Then the center point is 100, now we want our cropping to start
            // at negative $radius from 100 and end at positive $radius from 100,
            // So that it crops from the center part into a square with the
            // Diameter same as minSize (Same for height)
            "x" => $this->width / 2 - $radius,
            "y" => $this->height / 2 - $radius,
            "width" => $this->minSize, // Diameter
            "height" => $this->minSize, // Diameter
        ]);

        if ($cropped !== false) { // in case a new image object was returned
            imagedestroy($this->img);    // we destroy the original image
            $this->img = $cropped;       // and assign the cropped image

        } else {
            throw new \Exception("Failed to crop the image!", 500);

        }

        // Now create the circular mask
        $mask = imagecreatetruecolor($this->minSize, $this->minSize);
        $black = imagecolorallocate($mask, 0, 0, 0);
        $magenta = imagecolorallocate($mask, 255, 0, 255);

        // Fill with magenta color
        imagefill($mask, 0, 0, $magenta);

        // Create a black circle in the center
        imagefilledellipse(
            $mask,
            $radius,
            $radius,
            $this->minSize,
            $this->minSize,
            $black
        );

        // Now make the black circle part transparent
        imagecolortransparent($mask, $black);

        // Merge the two images so that only the transparent
        // part shows the image and other parts become magenta solid
        imagecopymerge(
            $this->img,
            $mask,
            0,
            0,
            0,
            0,
            $this->minSize,
            $this->minSize,
            100
        );

        // Now make the magenta part transparent
        // It now only keeps the center transparent(prev=black) part
        // of the original image visible
        imagecolortransparent($this->img, $magenta);

        // Destroy the mask
        imagedestroy($mask);

        return $this->render();
    }

    public function render(): string
    {
        // Get the string content
        // Of the generated image & return
        ob_start();
        imagepng($this->img);
        $imagedata = ob_get_clean();

        return $imagedata;

    }
}

###Usage:-

// First we scale the image to a smaller size, otherwise the circular crop may time out and get the string value
$image = InterventionImage::read($image)->scale(300, null)->encodeByMediaType(type: "image/png", quality: 90)->toString();

// Now simply Crop the image as a circle with our helper class
$circleImage = new CircleImage($image);
$image = $circleImage->make();

// It returns the png string image content, either convert to base64 or simply put to the storage Like below:-
// Storage: Storage::put("picture/avatar.png", $image);
// base64_encode($image);
Adorne answered 24/7 at 21:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.