diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 236fa77590706..b0d3a911acef8 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -1638,6 +1638,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 e6035fd13b242..c7a2650c1fc14 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -1671,6 +1671,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 08b388b47a442..b2a3003975bcf 100644 --- a/lib/private/Collaboration/Reference/LinkReferenceProvider.php +++ b/lib/private/Collaboration/Reference/LinkReferenceProvider.php @@ -29,4 +29,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/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([ @@ -361,6 +362,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/private/legacy/OC_Image.php b/lib/private/legacy/OC_Image.php index 8807eef4b565a..cfcacf28231f5 100644 --- a/lib/private/legacy/OC_Image.php +++ b/lib/private/legacy/OC_Image.php @@ -55,6 +55,8 @@ class OC_Image implements \OCP\IImage { // Default quality for jpeg images protected const DEFAULT_JPEG_QUALITY = 80; + protected const DEFAULT_WEBP_QUALITY = 80; + protected const DEFAULT_AVIF_QUALITY = 50; // Default quality for webp images protected const DEFAULT_WEBP_QUALITY = 80; @@ -271,6 +273,12 @@ private function _output(?string $filePath = null, ?string $mimeType = null): bo case 'image/jpeg': $imageType = IMAGETYPE_JPEG; break; + case 'image/webp': + $imageType = IMAGETYPE_WEBP; + break; + case 'image/avif': + $imageType = IMAGETYPE_JPEG; + break; case 'image/png': $imageType = IMAGETYPE_PNG; break; @@ -289,6 +297,19 @@ private function _output(?string $filePath = null, ?string $mimeType = null): bo } } + 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); @@ -298,6 +319,16 @@ private function _output(?string $filePath = null, ?string $mimeType = null): bo imageinterlace($this->resource, (PHP_VERSION_ID >= 80000 ? true : 1)); $retVal = imagejpeg($this->resource, $filePath, $this->getJpegQuality()); break; + case "image/webp": + /** @psalm-suppress InvalidScalarArgument */ + imageinterlace($this->resource, (PHP_VERSION_ID >= 80000 ? true : 1)); + $retVal = imagewebp($this->resource, $filePath, $this->getWebpQuality()); + break; + case "image/avif": + /** @psalm-suppress InvalidScalarArgument */ + imageinterlace($this->resource, (PHP_VERSION_ID >= 80100 ? true : 1)); + $retVal = imageavif($this->resource, $filePath, $this->getAvifQuality(), 10); + break; case IMAGETYPE_PNG: $retVal = imagepng($this->resource, $filePath); break; @@ -353,9 +384,23 @@ 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/webp': + case 'image/avif': + return 'image/jpeg'; case 'image/gif': case 'image/webp': return $this->mimeType; @@ -372,7 +417,23 @@ public function data(): ?string { return null; } ob_start(); - switch ($this->mimeType) { + $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 ($imageType) { case "image/png": $res = imagepng($this->resource); break; @@ -381,6 +442,16 @@ public function data(): ?string { $quality = $this->getJpegQuality(); $res = imagejpeg($this->resource, null, $quality); break; + case "image/webp": + /** @psalm-suppress InvalidScalarArgument */ + imageinterlace($this->resource, (PHP_VERSION_ID >= 80000 ? true : 1)); + $res = imagewebp($this->resource, null, $this->getWebpQuality()); + break; + case "image/avif": + /** @psalm-suppress InvalidScalarArgument */ + imageinterlace($this->resource, (PHP_VERSION_ID >= 80100 ? true : 1)); + $res = imageavif($this->resource, null, $this->getAvifQuality(), 10); + break; case "image/gif": $res = imagegif($this->resource); break; @@ -417,6 +488,24 @@ protected function getJpegQuality(): int { return min(100, max(10, (int) $quality)); } + protected function getWebpQuality(): int { + $quality = $this->config->getAppValue('preview', 'webp_quality', (string) self::DEFAULT_WEBP_QUALITY); + // TODO: remove when getAppValue is type safe + if ($quality === null) { + $quality = self::DEFAULT_WEBP_QUALITY; + } + return min(100, max(10, (int) $quality)); + } + + protected function getAvifQuality(): int { + $quality = $this->config->getAppValue('preview', 'avif_quality', (string) self::DEFAULT_AVIF_QUALITY); + // TODO: remove when getAppValue is type safe + if ($quality === null) { + $quality = self::DEFAULT_AVIF_QUALITY; + } + return min(100, max(10, (int) $quality)); + } + /** * @return int */ @@ -722,6 +811,16 @@ public function loadFromFile($imagePath = false) { $this->logger->debug('OC_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); + } 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/public/Collaboration/Reference/LinkReferenceProvider.php b/lib/public/Collaboration/Reference/LinkReferenceProvider.php index d41c1160c7c07..14ab0b3db0859 100644 --- a/lib/public/Collaboration/Reference/LinkReferenceProvider.php +++ b/lib/public/Collaboration/Reference/LinkReferenceProvider.php @@ -60,7 +60,8 @@ class LinkReferenceProvider implements IReferenceProvider { '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 3ff4c3c78ad64..afddf2da9a081 100644 --- a/resources/config/mimetypemapping.dist.json +++ b/resources/config/mimetypemapping.dist.json @@ -214,6 +214,7 @@ "webloc": ["application/internet-shortcut"], "webm": ["video/webm"], "webp": ["image/webp"], + "avif": ["image/avif"], "wmv": ["video/x-ms-wmv"], "woff": ["application/font-woff"], "wpd": ["application/vnd.wordperfect"],