crop center and resize image using cwebp respecting aspect ratio (php)
If you want to use cwebp and resize image to exact width and height you will have no luck, as it doesn’t have powerfull functions like imagemagick. Luckily, we can create our class that will calculate how much do we need to crop center so we can resize image after with respect of aspect ratio.
<?php
class ConvertToWebp
{
//simply resize without proportion
public static function resize(string $srcFilePath, string $newFilePath, int $width, int $height, int $quality = 50): ?array
{
$imageSize = getimagesize($srcFilePath);
if ($width > $imageSize[0] || $height > $imageSize[1]) {
return self::convert($srcFilePath, $newFilePath, $quality);
}
$command = "cwebp -q $quality -resize $width $height $srcFilePath -o $newFilePath > /dev/null 2>&1";
exec($command);
$imageSize = @getimagesize($newFilePath);
//check if cwebp did it's job. If not use imagemagick
if (!isset($imageSize[0]) || !$imageSize[0]) {
$s = ($width?:'').'x'.($height?:'');
$command = "convert $srcFilePath -strip -resize $s -format webp -quality $quality {$newFilePath}";
exec($command);
$imageSize = @getimagesize($newFilePath);
if (!isset($imageSize[0]) || !$imageSize[0]) {
return null;
}
}
return [
'width' => $imageSize[0],
'height' => $imageSize[1],
'size' => filesize($newFilePath)
];
}
//simply convert
public static function convert(string $srcFilePath, string $newFilePath, int $quality = 50): ?array
{
$command = "cwebp -q $quality $srcFilePath -o $newFilePath > /dev/null 2>&1";
exec($command);
$imageSize = @getimagesize($newFilePath);
//check if cwebp did it's job. If not use imagemagick
if (!isset($imageSize[0]) || !$imageSize[0]) {
$command = "convert $srcFilePath -strip -format webp -quality $quality {$newFilePath}";
exec($command);
$imageSize = @getimagesize($newFilePath);
if (!isset($imageSize[0]) || !$imageSize[0]) {
return null;
}
}
return [
'width' => $imageSize[0],
'height' => $imageSize[1],
'size' => filesize($newFilePath)
];
}
//crop center and resize respecting proportion
public static function cropResize(string $srcFilePath, string $newFilePath, int $width, int $height, int $quality = 50): ?array
{
$imageSize = getimagesize($srcFilePath);
if (self::isTooBadProportion($imageSize[0], $imageSize[1], $width, $height)) {
if ($imageSize[0] <= $width) {
$command = self::cropSmallWidth($srcFilePath, $newFilePath, $width, $height, $quality, $imageSize);
} elseif ($imageSize[1] < $height) {
$command = self::cropSmallHeight($srcFilePath, $newFilePath, $width, $height, $quality, $imageSize);
} elseif ($width >= $height) {
$command = self::cropResizeWidth($srcFilePath, $newFilePath, $width, $height, $quality, $imageSize);
} elseif ($height > $width) {
$command = self::cropResizeHeight($srcFilePath, $newFilePath, $width, $height, $quality, $imageSize);
}
} else {
$command = "cwebp -q $quality -resize $width $height $srcFilePath -o $newFilePath > /dev/null 2>&1";
}
exec($command);
$imageSize = @getimagesize($newFilePath);
//imagemagick fallback if cwebp failed
if (!isset($imageSize[0]) || !$imageSize[0]) {
$imagePixels = $imageSize[0] * $imageSize[1];
$wantPixels = $width * $height;
$s = ($width?:'').'x'.($height?:'');
if ($imagePixels < $wantPixels) {
//we want bigger image, so we don't use strict fit and just optimize image
$command = "convert $srcFilePath -strip -resize {$width}x{$height}\> -format webp -quality $quality {$newFilePath}";
} else {
$command = "convert $srcFilePath -strip -resize $s^ -gravity center -extent {$width}x{$height} -format webp -quality $quality {$newFilePath}";
}
exec($command);
$imageSize = @getimagesize($newFilePath);
if (!isset($imageSize[0]) || !$imageSize[0]) {
return null;
}
}
return [
'width' => $imageSize[0],
'height' => $imageSize[1],
'size' => filesize($newFilePath)
];
}
public static function isTooBadProportion($currentWidth, $currentHeight, $wantWidth, $wantHeight): bool
{
$proportionCurrent = intval($currentHeight/$currentWidth*100);
$proportionWant = intval($wantHeight/$wantWidth*100);
if (abs($proportionCurrent-$proportionWant) >= 20) {
return true;
}
return false;
}
private static function cropSmallWidth(string $srcFilePath, string $newFilePath, int $width, int $height, int $quality, array $imageSize): string
{
$percentage = $height / $width;
$width = $imageSize[0];
$height = intval($width * $percentage);
if ($imageSize[1] < $height) {
return self::cropSmallHeight($srcFilePath, $newFilePath, $width, $height, $quality, $imageSize);
}
$cropOffsetX = abs(intval(($imageSize[0] - $width)/2));
$cropOffsetY = abs(intval(($imageSize[1] - $height)/2));
$crop = "-crop $cropOffsetX $cropOffsetY $width $height";
return "cwebp $crop -q $quality $srcFilePath -o $newFilePath > /dev/null 2>&1";
}
private static function cropSmallHeight(string $srcFilePath, string $newFilePath, int $width, int $height, int $quality, array $imageSize): string
{
$percentage = $width / $height;
$height = $imageSize[1];
$width = intval($height * $percentage);
if ($imageSize[0] < $width) {
return self::cropSmallWidth($srcFilePath, $newFilePath, $width, $height, $quality, $imageSize);
}
$cropOffsetX = abs(intval(($imageSize[0] - $width)/2));
$cropOffsetY = abs(intval(($imageSize[1] - $height)/2));
$crop = "-crop $cropOffsetX $cropOffsetY $width $height";
return "cwebp $crop -q $quality $srcFilePath -o $newFilePath > /dev/null 2>&1";
}
private static function cropResizeWidth(string $srcFilePath, string $newFilePath, int $width, int $height, int $quality, array $imageSize): string
{
$cropW = $imageSize[0];
$cropH = intval($cropW * ($height / $width));
if ($imageSize[1] < $cropH) {
return self::cropResizeHeight($srcFilePath, $newFilePath, $width, $height, $quality, $imageSize);
}
$cropOffsetX = abs(intval(($imageSize[0] - $cropW)/2));
$cropOffsetY = abs(intval(($imageSize[1] - $cropH)/2));
$crop = "-crop $cropOffsetX $cropOffsetY $cropW $cropH";
return "cwebp $crop -resize $width $height -q $quality $srcFilePath -o $newFilePath > /dev/null 2>&1";
}
private static function cropResizeHeight(string $srcFilePath, string $newFilePath, int $width, int $height, int $quality, array $imageSize): string
{
$cropH = $imageSize[1];
$cropW = intval($cropH * ($width / $height));
if ($imageSize[0] < $cropW) {
return self::cropResizeWidth($srcFilePath, $newFilePath, $width, $height, $quality, $imageSize);
}
$cropOffsetX = abs(intval(($imageSize[0] - $cropW)/2));
$cropOffsetY = abs(intval(($imageSize[1] - $cropH)/2));
$crop = "-crop $cropOffsetX $cropOffsetY $cropW $cropH";
return "cwebp $crop -resize $width $height -q $quality $srcFilePath -o $newFilePath > /dev/null 2>&1";
}
}