107 lines
3.3 KiB
PHP
107 lines
3.3 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use Illuminate\Http\UploadedFile;
|
|
use Illuminate\Support\Facades\Storage;
|
|
|
|
class ImageOptimizer
|
|
{
|
|
/**
|
|
* Configuracion por carpeta. Una unica fuente de verdad — usada por el
|
|
* comando optimize:images y por los uploads de los controllers admin.
|
|
*/
|
|
public const FOLDERS = [
|
|
'carousel' => ['maxWidth' => 1600, 'quality' => 82],
|
|
'noticias' => ['maxWidth' => 1200, 'quality' => 82],
|
|
'promos' => ['maxWidth' => 1200, 'quality' => 82],
|
|
'clubes' => ['maxWidth' => 512, 'quality' => 85],
|
|
'sponsors' => ['maxWidth' => 600, 'quality' => 85],
|
|
'qr' => ['maxWidth' => 800, 'quality' => 85],
|
|
];
|
|
|
|
private const MIN_RECOMPRESS_BYTES = 100 * 1024;
|
|
|
|
/**
|
|
* Sube el archivo al disk 'public' y lo optimiza in-place.
|
|
* Retorna el path relativo (ej: "carousel/abc123.jpg").
|
|
*/
|
|
public function storeAndOptimize(UploadedFile $file, string $folder): string
|
|
{
|
|
$path = $file->store($folder, 'public');
|
|
|
|
if (!extension_loaded('gd')) {
|
|
return $path;
|
|
}
|
|
|
|
$cfg = self::FOLDERS[$folder] ?? null;
|
|
if (!$cfg) {
|
|
return $path;
|
|
}
|
|
|
|
$abs = Storage::disk('public')->path($path);
|
|
$this->optimizeFile($abs, $cfg['maxWidth'], $cfg['quality']);
|
|
|
|
return $path;
|
|
}
|
|
|
|
/**
|
|
* Optimiza un archivo en disco (resize + recompress).
|
|
* Devuelve [origSize, newSize, newW, newH] o null si no se modifico.
|
|
*/
|
|
public function optimizeFile(string $file, int $maxWidth, int $quality): ?array
|
|
{
|
|
if (!file_exists($file)) return null;
|
|
|
|
$origSize = filesize($file);
|
|
$info = @getimagesize($file);
|
|
if (!$info) return null;
|
|
|
|
[$w, $h] = $info;
|
|
$needsResize = $w > $maxWidth;
|
|
$needsRecomp = $origSize > self::MIN_RECOMPRESS_BYTES;
|
|
if (!$needsResize && !$needsRecomp) return null;
|
|
|
|
$ext = strtolower(pathinfo($file, PATHINFO_EXTENSION));
|
|
$img = match ($ext) {
|
|
'jpg', 'jpeg' => @imagecreatefromjpeg($file),
|
|
'png' => @imagecreatefrompng($file),
|
|
'webp' => @imagecreatefromwebp($file),
|
|
default => null,
|
|
};
|
|
if (!$img) return null;
|
|
|
|
$newW = $w; $newH = $h;
|
|
if ($w > $maxWidth) {
|
|
$newW = $maxWidth;
|
|
$newH = (int) round($h * ($maxWidth / $w));
|
|
$resized = imagecreatetruecolor($newW, $newH);
|
|
|
|
if (in_array($ext, ['png', 'webp'])) {
|
|
imagealphablending($resized, false);
|
|
imagesavealpha($resized, true);
|
|
$transparent = imagecolorallocatealpha($resized, 0, 0, 0, 127);
|
|
imagefilledrectangle($resized, 0, 0, $newW, $newH, $transparent);
|
|
}
|
|
|
|
imagecopyresampled($resized, $img, 0, 0, 0, 0, $newW, $newH, $w, $h);
|
|
$img = $resized;
|
|
}
|
|
|
|
ob_start();
|
|
match ($ext) {
|
|
'jpg', 'jpeg' => imagejpeg($img, null, $quality),
|
|
'png' => imagepng($img, null, 9),
|
|
'webp' => imagewebp($img, null, $quality),
|
|
};
|
|
$bytes = ob_get_clean();
|
|
|
|
if (strlen($bytes) >= $origSize) {
|
|
return null;
|
|
}
|
|
|
|
file_put_contents($file, $bytes);
|
|
return [$origSize, strlen($bytes), $newW, $newH];
|
|
}
|
|
}
|