Skip to content
Draft
Show file tree
Hide file tree
Changes from 6 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
6 changes: 5 additions & 1 deletion apps/dav/lib/Avatars/AvatarNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ public function get() {
ob_start();
if ($this->ext === 'png') {
imagepng($res);
} elseif ($this->ext === 'jxl') {
imagejxl($res);

Check failure

Code scanning / Psalm

UndefinedFunction

Function OCA\DAV\Avatars\imagejxl does not exist
} else {
imagejpeg($res);
}
Expand All @@ -78,8 +80,10 @@ public function get() {
public function getContentType() {
if ($this->ext === 'png') {
return 'image/png';
} elseif ($this->ext === 'jxl') {
return 'image/jxl';
}
return 'image/jpeg';
return 'image/jpeg';
}

public function getETag() {
Expand Down
1 change: 1 addition & 0 deletions apps/dav/lib/CardDAV/PhotoCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ class PhotoCache {
public const ALLOWED_CONTENT_TYPES = [
'image/png' => 'png',
'image/jpeg' => 'jpg',
'image/jxl' => 'jxl',
'image/gif' => 'gif',
'image/vnd.microsoft.icon' => 'ico',
];
Expand Down
4 changes: 4 additions & 0 deletions apps/theming/lib/ImageManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,10 @@ public function updateImage(string $key, string $tmpFile): string {
if (!imagejpeg($outputImage, $newTmpFile, 90)) {
throw new \Exception('Could not recompress background image as JPEG');
}
} else if (str_contains($detectedMimeType, 'image/jxl')) {
if (!imagejxl($outputImage, $newTmpFile, 90)) {

Check failure

Code scanning / Psalm

UndefinedFunction

Function OCA\Theming\imagejxl does not exist
throw new \Exception('Could not recompress background image as JPEG XL');
}
} else {
if (!imagepng($outputImage, $newTmpFile, 8)) {
throw new \Exception('Could not recompress background image as PNG');
Expand Down
2 changes: 1 addition & 1 deletion apps/theming/src/components/admin/FileInputField.vue
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ export default {
return {
showLoading: false,
acceptMime: (allowedMimeTypes[this.name]
|| ['image/jpeg', 'image/png', 'image/gif', 'image/webp']).join(','),
|| ['image/jpeg', 'image/jxl', 'image/png', 'image/gif', 'image/webp']).join(','),
}
},

Expand Down
15 changes: 11 additions & 4 deletions build/integration/features/bootstrap/WebDav.php
Original file line number Diff line number Diff line change
Expand Up @@ -448,28 +448,35 @@ public function searchFile(string $user, ?string $properties = null, ?string $sc
</d:prop>
<d:literal>image/png</d:literal>
</d:eq>

<d:eq>
<d:prop>
<d:getcontenttype/>
</d:prop>
<d:literal>image/jpeg</d:literal>
</d:eq>


<d:eq>
<d:prop>
<d:getcontenttype/>
</d:prop>
<d:literal>image/jxl</d:literal>
</d:eq>

<d:eq>
<d:prop>
<d:getcontenttype/>
</d:prop>
<d:literal>image/heic</d:literal>
</d:eq>

<d:eq>
<d:prop>
<d:getcontenttype/>
</d:prop>
<d:literal>video/mp4</d:literal>
</d:eq>

<d:eq>
<d:prop>
<d:getcontenttype/>
Expand Down
3 changes: 2 additions & 1 deletion config/config.sample.php
Original file line number Diff line number Diff line change
Expand Up @@ -1304,6 +1304,7 @@
'OC\Preview\BMP',
'OC\Preview\GIF',
'OC\Preview\JPEG',
'OC\Preview\JXL',
'OC\Preview\Krita',
'OC\Preview\MarkDown',
'OC\Preview\MP3',
Expand Down Expand Up @@ -1945,7 +1946,7 @@
*
* Example for windows systems: ``array('?', '<', '>', ':', '*', '|', '"', chr(0), "\n", "\r")``
* see https://en.wikipedia.org/wiki/Comparison_of_file_systems#Limits
*
*
* Defaults to ``array()``
*/
'forbidden_chars' => [],
Expand Down
2 changes: 1 addition & 1 deletion core/Controller/AvatarController.php
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ public function postAvatar(?string $path = null): JSONResponse {

if ($image->valid()) {
$mimeType = $image->mimeType();
if ($mimeType !== 'image/jpeg' && $mimeType !== 'image/png') {
if ($mimeType !== 'image/jpeg' && $mimeType !== 'image/jxl' && $mimeType !== 'image/png') {
return new JSONResponse(
['data' => ['message' => $this->l10n->t('Unknown filetype')]],
Http::STATUS_OK
Expand Down
4 changes: 3 additions & 1 deletion core/js/mimetypelist.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ OC.MimeTypeList={
"application/font-sfnt": "font",
"application/font-woff": "font",
"application/gpx+xml": "location",
"application/gzip": "package/x-generic",
"application/illustrator": "image",
"application/javascript": "text/code",
"application/json": "text/code",
Expand Down Expand Up @@ -80,7 +81,7 @@ OC.MimeTypeList={
"application/x-fictionbook+xml": "text",
"application/x-font": "font",
"application/x-gimp": "image",
"application/x-gzip": "package/x-generic",
"application/x-gzip": "application/gzip",
"application/x-iwork-keynote-sffkey": "x-office/presentation",
"application/x-iwork-numbers-sffnumbers": "x-office/spreadsheet",
"application/x-iwork-pages-sffpages": "x-office/document",
Expand Down Expand Up @@ -111,6 +112,7 @@ OC.MimeTypeList={
"application/km": "mindmap",
"application/x-freemind": "mindmap",
"application/vnd.xmind.workbook": "mindmap",
"image/jxl": "image/jxl",
"image/targa": "image/tga",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document.oform": "x-office/form",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document.docxf": "x-office/form-template"
Expand Down
1 change: 1 addition & 0 deletions lib/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -1518,6 +1518,7 @@
'OC\\Preview\\Image' => $baseDir . '/lib/private/Preview/Image.php',
'OC\\Preview\\Imaginary' => $baseDir . '/lib/private/Preview/Imaginary.php',
'OC\\Preview\\JPEG' => $baseDir . '/lib/private/Preview/JPEG.php',
'OC\\Preview\\JXL' => $baseDir . '/lib/private/Preview/JXL.php',
'OC\\Preview\\Krita' => $baseDir . '/lib/private/Preview/Krita.php',
'OC\\Preview\\MP3' => $baseDir . '/lib/private/Preview/MP3.php',
'OC\\Preview\\MSOffice2003' => $baseDir . '/lib/private/Preview/MSOffice2003.php',
Expand Down
9 changes: 5 additions & 4 deletions lib/composer/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
);

public static $prefixLengthsPsr4 = array (
'O' =>
'O' =>
array (
'OC\\Core\\' => 8,
'OC\\' => 3,
Expand All @@ -20,15 +20,15 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
);

public static $prefixDirsPsr4 = array (
'OC\\Core\\' =>
'OC\\Core\\' =>
array (
0 => __DIR__ . '/../../..' . '/core',
),
'OC\\' =>
'OC\\' =>
array (
0 => __DIR__ . '/../../..' . '/lib/private',
),
'OCP\\' =>
'OCP\\' =>
array (
0 => __DIR__ . '/../../..' . '/lib/public',
),
Expand Down Expand Up @@ -1551,6 +1551,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Preview\\Image' => __DIR__ . '/../../..' . '/lib/private/Preview/Image.php',
'OC\\Preview\\Imaginary' => __DIR__ . '/../../..' . '/lib/private/Preview/Imaginary.php',
'OC\\Preview\\JPEG' => __DIR__ . '/../../..' . '/lib/private/Preview/JPEG.php',
'OC\\Preview\\JXL' => __DIR__ . '/../../..' . '/lib/private/Preview/JXL.php',
'OC\\Preview\\Krita' => __DIR__ . '/../../..' . '/lib/private/Preview/Krita.php',
'OC\\Preview\\MP3' => __DIR__ . '/../../..' . '/lib/private/Preview/MP3.php',
'OC\\Preview\\MSOffice2003' => __DIR__ . '/../../..' . '/lib/private/Preview/MSOffice2003.php',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class LinkReferenceProvider implements IReferenceProvider {
'image/png',
'image/jpg',
'image/jpeg',
'image/jxl',
'image/gif',
'image/svg+xml',
'image/webp'
Expand Down
2 changes: 2 additions & 0 deletions lib/private/Preview/Generator.php
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,8 @@ private function getExtension($mimeType) {
return 'png';
case 'image/jpeg':
return 'jpg';
case 'image/jxl':
return 'jxl';
case 'image/webp':
return 'webp';
case 'image/gif':
Expand Down
5 changes: 4 additions & 1 deletion lib/private/Preview/Imaginary.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public function getMimeType(): string {
}

public static function supportedMimeTypes(): string {
return '/(image\/(bmp|x-bitmap|png|jpeg|gif|heic|heif|svg\+xml|tiff|webp)|application\/(pdf|illustrator))/';
return '/(image\/(bmp|x-bitmap|png|jpeg|jxl|gif|heic|heif|svg\+xml|tiff|webp)|application\/(pdf|illustrator))/';
}

public function getCroppedThumbnail(File $file, int $maxX, int $maxY, bool $crop): ?IImage {
Expand Down Expand Up @@ -93,6 +93,9 @@ public function getCroppedThumbnail(File $file, int $maxX, int $maxY, bool $crop
$autorotate = false;
$mimeType = 'jpeg';
break;
case 'image/jxl':
$mimeType = 'jxl';
break;
case 'image/gif':
case 'image/png':
$mimeType = 'png';
Expand Down
155 changes: 155 additions & 0 deletions lib/private/Preview/JXL.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Olivier Paroz <[email protected]>
* @author Robin Appelman <[email protected]>
* @author Peter Kovář <[email protected]>
*
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OC\Preview;

use OCP\Files\File;
use OCP\Files\FileInfo;
use OCP\IImage;
use Psr\Log\LoggerInterface;

/**
* Creates a JXL preview using ImageMagick via the PECL extension
*
* @package OC\Preview
*/
class JXL extends ProviderV2 {
/**
* {@inheritDoc}
*/
public function getMimeType(): string {
return '/image\/jxl/';
}

/**
* {@inheritDoc}
*/
public function isAvailable(FileInfo $file): bool {
return in_array('JXL', \Imagick::queryFormats("JXL"));
}

/**
* {@inheritDoc}
*/
public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage {
if (!$this->isAvailable($file)) {
return null;
}

$tmpPath = $this->getLocalFile($file);
if ($tmpPath === false) {
\OC::$server->get(LoggerInterface::class)->error(
'Failed to get thumbnail for: ' . $file->getPath(),
['app' => 'core']
);
return null;
}

// Creates \Imagick object from the heic file
try {
$bp = $this->getResizedPreview($tmpPath, $maxX, $maxY);
$bp->setFormat('jxl');
} catch (\Exception $e) {
\OC::$server->get(LoggerInterface::class)->error(
'File: ' . $file->getPath() . ' Imagick says:',
[
'exception' => $e,
'app' => 'core',
]
);
return null;
}

$this->cleanTmpFiles();

//new bitmap image object
$image = new \OCP\Image();
$image->loadFromData((string) $bp);
//check if image object is valid
return $image->valid() ? $image : null;
}

/**
* Returns a preview of maxX times maxY dimensions in JPG format
*
* * The default resolution is already 72dpi, no need to change it for a bitmap output
* * It's possible to have proper colour conversion using profileimage().
* ICC profiles are here: http://www.color.org/srgbprofiles.xalter
* * It's possible to Gamma-correct an image via gammaImage()
*
* @param string $tmpPath the location of the file to convert
* @param int $maxX
* @param int $maxY
*
* @return \Imagick
*/
private function getResizedPreview($tmpPath, $maxX, $maxY) {
$bp = new \Imagick();

// Layer 0 contains either the bitmap or a flat representation of all vector layers
$bp->readImage($tmpPath . '[0]');

// Fix orientation from EXIF
$bp->autoOrient();

$bp->setImageFormat('jxl');

$bp = $this->resize($bp, $maxX, $maxY);

return $bp;
}

/**
* Returns a resized \Imagick object
*
* If you want to know more on the various methods available to resize an
* image, check out this link : @link https://stackoverflow.com/questions/8517304/what-the-difference-of-sample-resample-scale-resize-adaptive-resize-thumbnail-im
*
* @param \Imagick $bp
* @param int $maxX
* @param int $maxY
*
* @return \Imagick
*/
private function resize($bp, $maxX, $maxY) {
[$previewWidth, $previewHeight] = array_values($bp->getImageGeometry());

// We only need to resize a preview which doesn't fit in the maximum dimensions
if ($previewWidth > $maxX || $previewHeight > $maxY) {
// If we want a small image (thumbnail) let's be most space- and time-efficient
if ($maxX <= 500 && $maxY <= 500) {
$bp->thumbnailImage($maxY, $maxX, true);
$bp->stripImage();
} else {
// A bigger image calls for some better resizing algorithm
// According to http://www.imagemagick.org/Usage/filter/#lanczos
// the catrom filter is almost identical to Lanczos2, but according
// to https://www.php.net/manual/en/imagick.resizeimage.php it is
// significantly faster
$bp->resizeImage($maxX, $maxY, \Imagick::FILTER_CATROM, 1, true);
}
}

return $bp;
}
}
Loading