Intervention / image

PHP Image Processing
https://image.intervention.io
MIT License
13.88k stars 1.5k forks source link

without mask method, how to create circle/round image with V3 version? #1338

Closed hetao29 closed 5 months ago

hetao29 commented 5 months ago

Describe the feature you'd like Create circle/round image with V3 version

Create Image in V2

$width=$height=100;
$mask = $img_manger->canvas($width, $height);
$mask->circle($width, $width / 2, $height / 2, function ($draw){
    $draw->background('#fff');
});
$img_thumb = $img_manger->make("https://...")->fit($width,$height);
$img_thumb->mask($mask, false);
$img->insert($img_thumb,"center",32,10);

How in V3

?
olivervogel commented 5 months ago

Unfortunately, this is currently not possible in version 3, as the mask function is no longer part of the library. My impression was that the method was rarely used and was implemented very resource-intensively with GD. For this reason, I have decided not to include this feature in the new version. I don't want to rule out the mask method becoming part of Intervention Image again at some point, but this is not a priority for me at the moment.

hetao29 commented 5 months ago

Thanks, I use the place methd to fixed it like below:

$width_logo=120;
$handle=fopen("https://...","rb");
$img = ....

$img_thumb = new Imagick();
$img_thumb->readImageFile($handle);
$img_thumb->scaleImage($width_logo, $height_logo);
$img_thumb->roundCorners($width_logo/2,$height_logo/2);
$img_thumb->setImageFormat("png");

$img->place($img_thumb,"bottom-left",32,10);
fclose($handle);
ursoforte commented 2 months ago

My impression was that the method was rarely used and was implemented very resource-intensively with GD.

I use the mask() method to crop a circular profile photo. It was not a good choice to remove this method. Please, back with the method in the next release. :pray:

RejownAhmed commented 2 months ago

I was searching the same thing but not a single solution helped, also the top answer is not very appealing. Therefore, I have made a custom class to do that, pardon my commenting texts, as I wrote casually. Here's the code:-

<?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);