diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 670a719c9fb1b..6f6274a8a9b36 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -1844,6 +1844,7 @@ 'OC\\Preview\\Watcher' => $baseDir . '/lib/private/Preview/Watcher.php', 'OC\\Preview\\WatcherConnector' => $baseDir . '/lib/private/Preview/WatcherConnector.php', 'OC\\Preview\\WebP' => $baseDir . '/lib/private/Preview/WebP.php', + 'OC\\Preview\\AVIF' => $baseDir . '/lib/private/Preview/AVIF.php', 'OC\\Preview\\XBitmap' => $baseDir . '/lib/private/Preview/XBitmap.php', 'OC\\Profile\\Actions\\EmailAction' => $baseDir . '/lib/private/Profile/Actions/EmailAction.php', 'OC\\Profile\\Actions\\FediverseAction' => $baseDir . '/lib/private/Profile/Actions/FediverseAction.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 3e18f17caf60f..8bedd69829c4f 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -1893,6 +1893,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\Preview\\Watcher' => __DIR__ . '/../../..' . '/lib/private/Preview/Watcher.php', 'OC\\Preview\\WatcherConnector' => __DIR__ . '/../../..' . '/lib/private/Preview/WatcherConnector.php', 'OC\\Preview\\WebP' => __DIR__ . '/../../..' . '/lib/private/Preview/WebP.php', + 'OC\\Preview\\AVIF' => __DIR__ . '/../../..' . '/lib/private/Preview/AVIF.php', 'OC\\Preview\\XBitmap' => __DIR__ . '/../../..' . '/lib/private/Preview/XBitmap.php', 'OC\\Profile\\Actions\\EmailAction' => __DIR__ . '/../../..' . '/lib/private/Profile/Actions/EmailAction.php', 'OC\\Profile\\Actions\\FediverseAction' => __DIR__ . '/../../..' . '/lib/private/Profile/Actions/FediverseAction.php', diff --git a/lib/private/Collaboration/Reference/LinkReferenceProvider.php b/lib/private/Collaboration/Reference/LinkReferenceProvider.php index 5af23bf633d6d..a74f8c8fc5fcd 100644 --- a/lib/private/Collaboration/Reference/LinkReferenceProvider.php +++ b/lib/private/Collaboration/Reference/LinkReferenceProvider.php @@ -12,4 +12,4 @@ /** @deprecated 29.0.0 Use OCP\Collaboration\Reference\LinkReferenceProvider instead */ class LinkReferenceProvider extends OCPLinkReferenceProvider { -} +} \ No newline at end of file diff --git a/lib/private/Image.php b/lib/private/Image.php index 96699a0046b2b..f966ac32a2b04 100644 --- a/lib/private/Image.php +++ b/lib/private/Image.php @@ -31,6 +31,9 @@ class Image implements IImage { // Default quality for webp images protected const DEFAULT_WEBP_QUALITY = 80; + // Default quality for avif images + protected const DEFAULT_AVIF_QUALITY = 50; + // tmp resource. protected GdImage|false $resource = false; // Default to png if file type isn't evident. @@ -240,11 +243,27 @@ private function _output(?string $filePath = null, ?string $mimeType = null): bo case 'image/webp': $imageType = IMAGETYPE_WEBP; break; + case 'image/avif': + $imageType = IMAGETYPE_JPEG; + break; default: throw new \Exception('Image::_output(): "' . $mimeType . '" is not supported when forcing a specific output format'); } } + if ($this->mimeType !== 'image/gif') { + $preview_format = $this->config->getSystemValueString('preview_format', ''); + switch ($preview_format) { + case 'webp': + $imageType = IMAGETYPE_WEBP; + break; + case 'avif': + $imageType = IMAGETYPE_AVIF; + break; + default: + } + } + switch ($imageType) { case IMAGETYPE_GIF: $retVal = imagegif($this->resource, $filePath); @@ -273,6 +292,9 @@ private function _output(?string $filePath = null, ?string $mimeType = null): bo case IMAGETYPE_WEBP: $retVal = imagewebp($this->resource, null, $this->getWebpQuality()); break; + case IMAGETYPE_AVIF: + $retVal = imageavif($this->resource, $filePath, $this->getAvifQuality(), 10); + break; default: $retVal = imagepng($this->resource, $filePath); } @@ -308,12 +330,25 @@ public function dataMimeType(): ?string { return null; } + if ($this->mimeType !== 'image/gif') { + $preview_format = $this->config->getSystemValueString('preview_format', ''); + switch ($preview_format) { + case 'webp': + return 'image/webp'; + case 'avif': + return 'image/avif'; + default: + } + } + switch ($this->mimeType) { case 'image/png': case 'image/jpeg': case 'image/gif': case 'image/webp': return $this->mimeType; + case 'image/avif': + return 'image/jpeg'; default: return 'image/png'; } @@ -327,6 +362,22 @@ public function data(): ?string { return null; } ob_start(); + $imageType = $this->imageType; + if ($imageType == 'image/avif') { + $imageType = 'image/jpeg'; + } + if ($imageType !== 'image/gif') { + $preview_format = $this->config->getSystemValueString('preview_format', ''); + switch ($preview_format) { + case 'webp': + $imageType = 'image/webp'; + break; + case 'avif': + $imageType = 'image/avif'; + break; + default: + } + } switch ($this->mimeType) { case 'image/png': $res = imagepng($this->resource); @@ -342,6 +393,9 @@ public function data(): ?string { case 'image/webp': $res = imagewebp($this->resource, null, $this->getWebpQuality()); break; + case "image/avif": + $res = imageavif($this->resource, null, $this->getAvifQuality(), 10); + break; default: $res = imagepng($this->resource); $this->logger->info('Image->data. Could not guess mime-type, defaulting to png', ['app' => 'core']); @@ -370,6 +424,11 @@ protected function getJpegQuality(): int { return min(100, max(10, $quality)); } + protected function getAvifQuality(): int { + $quality = $this->config->getAppValue('preview', 'avif_quality', self::DEFAULT_AVIF_QUALITY); + return min(100, max(10, (int) $quality)); + } + protected function getWebpQuality(): int { $quality = $this->appConfig->getValueInt('preview', 'webp_quality', self::DEFAULT_WEBP_QUALITY); return min(100, max(10, $quality)); @@ -674,6 +733,43 @@ public function loadFromFile($imagePath = false) { $this->logger->debug('Image->loadFromFile, WBMP images not supported: ' . $imagePath, ['app' => 'core']); } break; + case IMAGETYPE_AVIF: + if (imagetypes() & IMG_AVIF) { + if (!$this->checkImageSize($imagePath)) { + return false; + } + + $this->resource = @imagecreatefromavif($imagePath); + + // Check for irot box and apply rotation if needed + if ($this->resource && function_exists('imagerotate')) { + $fp = fopen($imagePath, 'rb'); + if ($fp) { + $data = fread($fp, 512); + fclose($fp); + + if ($data !== false) { + $pos = strpos($data, 'irot'); + if ($pos !== false && ($pos + 4 < strlen($data))) { + $rotationByte = ord($data[$pos + 4]); + $rotation = match ($rotationByte) { + 1 => 90, + 2 => 180, + 3 => 270, + default => 0, + }; + if ($rotation !== 0) { + $this->resource = imagerotate($this->resource, $rotation, 0); + } + } + } + } + } + + } else { + $this->logger->debug('OC_Image->loadFromFile, AVIF images not supported: ' . $imagePath, ['app' => 'core']); + } + break; case IMAGETYPE_BMP: $this->resource = imagecreatefrombmp($imagePath); break; diff --git a/lib/private/Preview/AVIF.php b/lib/private/Preview/AVIF.php new file mode 100644 index 0000000000000..ea8abb9a4d353 --- /dev/null +++ b/lib/private/Preview/AVIF.php @@ -0,0 +1,20 @@ +defaultProviders = $this->config->getSystemValue('enabledPreviewProviders', array_merge([ @@ -342,6 +343,7 @@ protected function registerCoreProviders() { $this->registerCoreProvider(Preview\BMP::class, '/image\/bmp/'); $this->registerCoreProvider(Preview\XBitmap::class, '/image\/x-xbitmap/'); $this->registerCoreProvider(Preview\WebP::class, '/image\/webp/'); + $this->registerCoreProvider(Preview\AVIF::class, '/image\/avif/'); $this->registerCoreProvider(Preview\Krita::class, '/application\/x-krita/'); $this->registerCoreProvider(Preview\MP3::class, '/audio\/mpeg$/'); $this->registerCoreProvider(Preview\OpenDocument::class, '/application\/vnd.oasis.opendocument.*/'); diff --git a/lib/public/Collaboration/Reference/LinkReferenceProvider.php b/lib/public/Collaboration/Reference/LinkReferenceProvider.php index 65bdcecb57733..47a6f1433f9c0 100644 --- a/lib/public/Collaboration/Reference/LinkReferenceProvider.php +++ b/lib/public/Collaboration/Reference/LinkReferenceProvider.php @@ -42,7 +42,8 @@ class LinkReferenceProvider implements IReferenceProvider, IPublicReferenceProvi 'image/jpeg', 'image/gif', 'image/svg+xml', - 'image/webp' + 'image/webp', + 'image/avif' ]; /** diff --git a/resources/config/mimetypemapping.dist.json b/resources/config/mimetypemapping.dist.json index 1227741769d0c..b002adab2092d 100644 --- a/resources/config/mimetypemapping.dist.json +++ b/resources/config/mimetypemapping.dist.json @@ -216,6 +216,7 @@ "webloc": ["application/internet-shortcut"], "webm": ["video/webm"], "webp": ["image/webp"], + "avif": ["image/avif"], "whiteboard": ["application/vnd.excalidraw+json"], "wmv": ["video/x-ms-wmv"], "woff": ["application/font-woff"],