Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\Filesystem\Folder;
use Joomla\Filesystem\File;
use Joomla\CMS\Version;

class SpsimpleportfolioHelper
{
Expand Down Expand Up @@ -44,26 +47,56 @@ public static function addSubmenu($submenu)
// Create thumbs
public static function createThumbs($src, $sizes = array(), $folder = '', $base_name = '', $ext = '')
{
/**
* Creates resized thumbnail images for a given source image in various sizes.
*
* Supports multiple image formats using the GD library. If the image format is AVIF,
* it delegates processing to the handleAvifImage() method which supports both GD and Imagick.
*
* @param string $src Full path to the source image file.
* @param array $sizes Array of target sizes. Each element should be an array [width, height].
* @param string $folder Destination folder for storing generated thumbnails.
* @param string $base_name Base name for the generated thumbnail files (without extension).
* @param string $ext Extension/format of the image (e.g., 'jpg', 'png', 'avif', etc.)
*
* @return array|false Returns an associative array of thumbnail paths on success.
* Keys are the same as in the $sizes array. On failure, returns false.
*
* @throws Exception If an image cannot be created or resized due to an unsupported type or a GD/Imagick error.
*/


// Get params
$params = ComponentHelper::getParams('com_spsimpleportfolio');
$img_crop_position = $params->get('crop_position', 'center');

list($originalWidth, $originalHeight) = getimagesize($src);

if ($ext === 'avif') {
return self::handleAvifImage($src, $folder, $base_name, $ext, $sizes, $img_crop_position);
}

// GD fallback for other image types
switch ($ext) {
case 'bmp':$img = imagecreatefromwbmp($src);
case 'bmp':
$img = imagecreatefromwbmp($src);
break;
case 'gif':$img = imagecreatefromgif($src);
case 'gif':
$img = imagecreatefromgif($src);
break;
case 'jpg':$img = imagecreatefromjpeg($src);
case 'jpg':
case 'jpeg':
$img = imagecreatefromjpeg($src);
break;
case 'jpeg':$img = imagecreatefromjpeg($src);
case 'png':
$img = imagecreatefrompng($src);
break;
case 'png':$img = imagecreatefrompng($src);
case 'webp':
$img = imagecreatefromwebp($src);
break;
case 'webp':$img = imagecreatefromwebp($src);
break;
default:
Factory::getApplication()->enqueueMessage('Unsupported image type or no AVIF support in GD', 'error');
return false;
}

if (count($sizes)) {
Expand Down Expand Up @@ -122,18 +155,22 @@ public static function createThumbs($src, $sizes = array(), $folder = '', $base_
}

switch ($ext) {
case 'bmp':imagewbmp($new, $dest);
case 'bmp':
imagewbmp($new, $dest);
break;
case 'gif':imagegif($new, $dest);
case 'gif':
imagegif($new, $dest);
break;
case 'jpg':imagejpeg($new, $dest);
case 'jpg':
case 'jpeg':
imagejpeg($new, $dest);
break;
case 'jpeg':imagejpeg($new, $dest);
case 'png':
imagepng($new, $dest);
break;
case 'png':imagepng($new, $dest);
case 'webp':
imagewebp($new, $dest);
break;
case 'webp':imagewebp($new, $dest);
break;
}
}

Expand All @@ -143,6 +180,113 @@ public static function createThumbs($src, $sizes = array(), $folder = '', $base_
return false;
}

public static function handleAvifImage($src, $folder, $base_name, $ext, $sizes, $img_crop_position = 'center')
{
$output = [];

// Joomla's Version class
$version = new Version();
$joomlaMajorVersion = $version->getShortVersion();

if (version_compare($joomlaMajorVersion, '5.0.0', '>=') && class_exists('Imagick')) {
try {
$imagick = new Imagick($src);
$dimensions = $imagick->getImageGeometry();
$originalWidth = $dimensions['width'] ?? 0;
$originalHeight = $dimensions['height'] ?? 0;

if ($base_name) {
$output['original'] = $folder . '/' . $base_name . '.' . $ext;
}

foreach ($sizes as $key => $size) {
$targetWidth = $size[0];
$targetHeight = $size[1];

$ratio_thumb = $targetWidth / $targetHeight;
$ratio_original = $originalWidth / $originalHeight;

if ($ratio_original >= $ratio_thumb) {
$height = $originalHeight;
$width = ceil($height * $ratio_thumb);
switch ($img_crop_position) {
case 'topleft':
$x = 0;
break;
case 'topright':
$x = $originalWidth - $width;
break;
default:
$x = intval(($originalWidth - $width) / 2);
break;
}
$y = 0;
} else {
$width = $originalWidth;
$height = ceil($width / $ratio_thumb);
$x = 0;
$y = ($img_crop_position === 'topleft') ? 0 : intval(($originalHeight - $height) / 2);
}

$thumb = clone $imagick;
$thumb->cropImage($width, $height, $x, $y);
$thumb->resizeImage($targetWidth, $targetHeight, Imagick::FILTER_LANCZOS, 1, true);
$thumb->setImageFormat('avif');

$dest = $folder . '/' . ($base_name ? $base_name . '_' . $key : $key) . '.' . $ext;
$output[$key] = $dest;
$thumb->writeImage($dest);

$thumb->clear();
$thumb->destroy();
}

$imagick->clear();
$imagick->destroy();
return $output;
} catch (Exception $e) {
Factory::getApplication()->enqueueMessage('Imagick error: ' . $e->getMessage(), 'error');
return false;
}
} else {
// Fallback logic
$dimensions = @getimagesize($src);
$originalWidth = $dimensions ? $dimensions[0] : 0;
$originalHeight = $dimensions ? $dimensions[1] : 0;

if (!is_dir($folder)) {
Folder::create($folder, 0755);
}

if ($base_name) {
$originalDest = $folder . '/' . $base_name . '.' . $ext;
if (File::copy($src, $originalDest)) {
$output['original'] = $originalDest . '?width=' . $originalWidth . '&height=' . $originalHeight;
} else {
Factory::getApplication()->enqueueMessage('Failed to copy original AVIF file.', 'error');
return false;
}
}

foreach ($sizes as $key => $size) {
$thumbDest = $folder . '/' . $base_name . $key . '.' . $ext;

if (!is_dir(dirname($thumbDest))) {
Folder::create(dirname($thumbDest), 0755);
}

if (File::copy($src, $thumbDest)) {
$output[$key] = $thumbDest . '?width=' . $size[0] . '&height=' . $size[1];
} else {
Factory::getApplication()->enqueueMessage("Failed to copy AVIF thumbnail for size {$key}.", 'error');
}
}

return $output;
}
}


public static function isPageBuilderIntegrated($item)
{

Expand Down