From d4cf099733725016da35f5f8253f350915e20f39 Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Thu, 28 Jan 2021 18:11:19 +0100 Subject: [PATCH] WebP support for style cover photos --- .../acp/update_com.woltlab.wcf_5.4_db.php | 1 + .../files/images/coverPhotos/default.webp | Bin 0 -> 14768 bytes .../files/lib/acp/form/StyleAddForm.class.php | 4 ++ .../files/lib/data/style/Style.class.php | 42 ++++++++++++----- .../lib/data/style/StyleAction.class.php | 8 +++- .../lib/data/style/StyleEditor.class.php | 4 ++ .../files/lib/data/user/User.class.php | 1 + .../files/lib/data/user/UserProfile.class.php | 3 +- .../photo/DefaultUserCoverPhoto.class.php | 12 ++--- .../cover/photo/IUserCoverPhoto.class.php | 12 ++--- .../user/cover/photo/UserCoverPhoto.class.php | 14 ++++-- .../files/lib/util/ImageUtil.class.php | 43 ++++++++++++++++++ wcfsetup/setup/db/install.sql | 1 + 13 files changed, 111 insertions(+), 34 deletions(-) create mode 100644 wcfsetup/install/files/images/coverPhotos/default.webp diff --git a/wcfsetup/install/files/acp/update_com.woltlab.wcf_5.4_db.php b/wcfsetup/install/files/acp/update_com.woltlab.wcf_5.4_db.php index 489c7416af..154879f5b2 100644 --- a/wcfsetup/install/files/acp/update_com.woltlab.wcf_5.4_db.php +++ b/wcfsetup/install/files/acp/update_com.woltlab.wcf_5.4_db.php @@ -70,6 +70,7 @@ $tables = [ PartialDatabaseTable::create('wcf1_user') ->columns([ + DefaultFalseBooleanDatabaseTableColumn::create('coverPhotoHasWebP'), DefaultFalseBooleanDatabaseTableColumn::create('multifactorActive'), ]), diff --git a/wcfsetup/install/files/images/coverPhotos/default.webp b/wcfsetup/install/files/images/coverPhotos/default.webp new file mode 100644 index 0000000000000000000000000000000000000000..2c826c6f77c9c58075b92a37ea2a66a701c575e5 GIT binary patch literal 14768 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 K4fnr_pa1|U=WC__ literal 0 HcmV?d00001 diff --git a/wcfsetup/install/files/lib/acp/form/StyleAddForm.class.php b/wcfsetup/install/files/lib/acp/form/StyleAddForm.class.php index 79d9a08dd0..cefeaaa253 100644 --- a/wcfsetup/install/files/lib/acp/form/StyleAddForm.class.php +++ b/wcfsetup/install/files/lib/acp/form/StyleAddForm.class.php @@ -296,7 +296,9 @@ class StyleAddForm extends AbstractForm 'coverPhoto' => [ 'size' => [ 'minWidth' => UserCoverPhoto::MIN_WIDTH, + 'maxWidth' => UserCoverPhoto::MAX_WIDTH, 'minHeight' => UserCoverPhoto::MIN_HEIGHT, + 'maxHeight' => UserCoverPhoto::MAX_HEIGHT, ], ], 'favicon' => [ @@ -993,7 +995,9 @@ class StyleAddForm extends AbstractForm 'newVariables' => $this->newVariables, 'scrollOffsets' => $this->scrollOffsets, 'coverPhotoMinHeight' => UserCoverPhoto::MIN_HEIGHT, + 'coverPhotoMaxHeight' => UserCoverPhoto::MAX_HEIGHT, 'coverPhotoMinWidth' => UserCoverPhoto::MIN_WIDTH, + 'coverPhotoMaxWidth' => UserCoverPhoto::MAX_WIDTH, ]); } diff --git a/wcfsetup/install/files/lib/data/style/Style.class.php b/wcfsetup/install/files/lib/data/style/Style.class.php index 0df81ce2ab..15f58cc77e 100644 --- a/wcfsetup/install/files/lib/data/style/Style.class.php +++ b/wcfsetup/install/files/lib/data/style/Style.class.php @@ -6,6 +6,7 @@ use wcf\data\DatabaseObject; use wcf\system\application\ApplicationHandler; use wcf\system\WCF; use wcf\util\FileUtil; +use wcf\util\ImageUtil; /** * Represents a style. @@ -248,48 +249,65 @@ class Style extends DatabaseObject /** * Returns the cover photo filename. - * - * @return string * @since 3.1 */ - public function getCoverPhoto() + public function getCoverPhoto(?bool $forceWebP = null): string { + $useWebP = $this->useWebP($forceWebP); + if ($this->coverPhotoExtension) { - return 'coverPhoto.' . $this->coverPhotoExtension; + return 'coverPhoto.' . ($useWebP ? 'webp' : $this->coverPhotoExtension); } - return 'default.jpg'; + return 'default.' . ($useWebP ? 'webp' : 'jpg'); } /** - * @return string * @since 5.2 */ - public function getCoverPhotoLocation() + public function getCoverPhotoLocation(?bool $forceWebP = null): string { + $useWebP = $this->useWebP($forceWebP); + if ($this->coverPhotoExtension) { - return $this->getAssetPath() . 'coverPhoto.' . $this->coverPhotoExtension; + return $this->getAssetPath() . 'coverPhoto.' . ($useWebP ? 'webp' : $this->coverPhotoExtension); } - return WCF_DIR . 'images/coverPhotos/default.jpg'; + return WCF_DIR . 'images/coverPhotos/default.' . ($useWebP ? 'webp' : 'jpg'); } /** - * @return string * @since 5.2 */ - public function getCoverPhotoUrl() + public function getCoverPhotoUrl(?bool $forceWebP = null): string { + $useWebP = $this->useWebP($forceWebP); + if ($this->coverPhotoExtension) { return WCF::getPath() . FileUtil::getRelativePath( WCF_DIR, $this->getAssetPath() - ) . 'coverPhoto.' . $this->coverPhotoExtension; + ) . 'coverPhoto.' . ($useWebP ? 'webp' : $this->coverPhotoExtension); } return WCF::getPath() . 'images/coverPhotos/' . $this->getCoverPhoto(); } + /** + * Serve the WebP variant of the cover photo if the browser supports + * it and the original cover photo is not a GIF. + * + * @since 5.4 + */ + protected function useWebP($forceWebP = null): bool + { + if ($this->coverPhotoExtension === "gif") { + return false; + } + + return $forceWebP || ($forceWebP === null && ImageUtil::browserSupportsWebP()); + } + /** * Returns the path to a favicon-related file. * diff --git a/wcfsetup/install/files/lib/data/style/StyleAction.class.php b/wcfsetup/install/files/lib/data/style/StyleAction.class.php index 8bd6dd9554..2b757352e3 100644 --- a/wcfsetup/install/files/lib/data/style/StyleAction.class.php +++ b/wcfsetup/install/files/lib/data/style/StyleAction.class.php @@ -446,10 +446,14 @@ BROWSERCONFIG; throw new \InvalidArgumentException('The given coverPhoto is not an image'); } $extension = ImageUtil::getExtensionByMimeType($imageData['mime']); - $newLocation = $style->getAssetPath() . 'coverPhoto.' . $extension; + $outputFilenameWithoutExtension = $style->getAssetPath() . 'coverPhoto'; + $newLocation = "{$outputFilenameWithoutExtension}.{$extension}"; \rename($fileLocation, $newLocation); + + $result = ImageUtil::createWebpVariant($newLocation, $outputFilenameWithoutExtension); + (new StyleEditor($style))->update([ - 'coverPhotoExtension' => $extension, + 'coverPhotoExtension' => ($result === false) ? 'jpg' : $extension, ]); $file->setProcessed($newLocation); diff --git a/wcfsetup/install/files/lib/data/style/StyleEditor.class.php b/wcfsetup/install/files/lib/data/style/StyleEditor.class.php index 03ea04fa37..6978568180 100644 --- a/wcfsetup/install/files/lib/data/style/StyleEditor.class.php +++ b/wcfsetup/install/files/lib/data/style/StyleEditor.class.php @@ -1146,6 +1146,10 @@ class StyleEditor extends DatabaseObjectEditor implements IEditableCachedObject StyleCompiler::getInstance()->compile($this->getDecoratedObject()); } + public function createCoverPhotoVariant(): void + { + } + /** * @inheritDoc * @return Style diff --git a/wcfsetup/install/files/lib/data/user/User.class.php b/wcfsetup/install/files/lib/data/user/User.class.php index 1c2cb61de8..157c630155 100644 --- a/wcfsetup/install/files/lib/data/user/User.class.php +++ b/wcfsetup/install/files/lib/data/user/User.class.php @@ -70,6 +70,7 @@ use wcf\util\UserUtil; * @property-read int $likesReceived cumulative result of likes (counting +1) the user's contents have received * @property-read string $coverPhotoHash hash of the user's cover photo * @property-read string $coverPhotoExtension extension of the user's cover photo file + * @property-read int $coverPhotoHasWebP is `1` if a webp variant of the cover photo and its thumbnail exists, otherwise `0` * @property-read int $disableCoverPhoto is `1` if the user's cover photo has been disabled, otherwise `0` * @property-read string $disableCoverPhotoReason reason why the user's cover photo is disabled * @property-read int $disableCoverPhotoExpires timestamp at which the user's cover photo will automatically be enabled again diff --git a/wcfsetup/install/files/lib/data/user/UserProfile.class.php b/wcfsetup/install/files/lib/data/user/UserProfile.class.php index 5e4b2108a7..759834ad42 100644 --- a/wcfsetup/install/files/lib/data/user/UserProfile.class.php +++ b/wcfsetup/install/files/lib/data/user/UserProfile.class.php @@ -411,7 +411,8 @@ class UserProfile extends DatabaseObjectDecorator implements ITitledLinkObject $this->coverPhoto = new UserCoverPhoto( $this->userID, $this->coverPhotoHash, - $this->coverPhotoExtension + $this->coverPhotoExtension, + $this->coverPhotoHasWebP ); } } diff --git a/wcfsetup/install/files/lib/data/user/cover/photo/DefaultUserCoverPhoto.class.php b/wcfsetup/install/files/lib/data/user/cover/photo/DefaultUserCoverPhoto.class.php index e01d576a53..4a47a8da26 100644 --- a/wcfsetup/install/files/lib/data/user/cover/photo/DefaultUserCoverPhoto.class.php +++ b/wcfsetup/install/files/lib/data/user/cover/photo/DefaultUserCoverPhoto.class.php @@ -25,24 +25,24 @@ class DefaultUserCoverPhoto implements IUserCoverPhoto /** * @inheritDoc */ - public function getLocation() + public function getLocation(?bool $forceWebP = null): string { - return StyleHandler::getInstance()->getStyle()->getCoverPhotoLocation(); + return StyleHandler::getInstance()->getStyle()->getCoverPhotoLocation($forceWebP); } /** * @inheritDoc */ - public function getURL() + public function getURL(?bool $forceWebP = null): string { - return StyleHandler::getInstance()->getStyle()->getCoverPhotoUrl(); + return StyleHandler::getInstance()->getStyle()->getCoverPhotoUrl($forceWebP); } /** * @inheritDoc */ - public function getFilename() + public function getFilename(?bool $forceWebP = null): string { - return StyleHandler::getInstance()->getStyle()->getCoverPhoto(); + return StyleHandler::getInstance()->getStyle()->getCoverPhoto($forceWebP); } } diff --git a/wcfsetup/install/files/lib/data/user/cover/photo/IUserCoverPhoto.class.php b/wcfsetup/install/files/lib/data/user/cover/photo/IUserCoverPhoto.class.php index 64573999e8..5f409c7e4b 100644 --- a/wcfsetup/install/files/lib/data/user/cover/photo/IUserCoverPhoto.class.php +++ b/wcfsetup/install/files/lib/data/user/cover/photo/IUserCoverPhoto.class.php @@ -19,22 +19,16 @@ interface IUserCoverPhoto /** * Returns the physical location of this cover photo. - * - * @return string */ - public function getLocation(); + public function getLocation(?bool $forceWebP = null): string; /** * Returns the url to this cover photo. - * - * @return string */ - public function getURL(); + public function getURL(?bool $forceWebP = null): string; /** * Returns the file name of this cover photo. - * - * @return string */ - public function getFilename(); + public function getFilename(?bool $forceWebP = null): string; } diff --git a/wcfsetup/install/files/lib/data/user/cover/photo/UserCoverPhoto.class.php b/wcfsetup/install/files/lib/data/user/cover/photo/UserCoverPhoto.class.php index 1a06449deb..74d374a558 100644 --- a/wcfsetup/install/files/lib/data/user/cover/photo/UserCoverPhoto.class.php +++ b/wcfsetup/install/files/lib/data/user/cover/photo/UserCoverPhoto.class.php @@ -26,6 +26,11 @@ class UserCoverPhoto implements IUserCoverPhoto */ protected $coverPhotoHash; + /** + * @var int + */ + protected $coverPhotoHasWebP = 0; + /** * user id * @var int @@ -47,11 +52,12 @@ class UserCoverPhoto implements IUserCoverPhoto * @param string $coverPhotoHash * @param string $coverPhotoExtension */ - public function __construct($userID, $coverPhotoHash, $coverPhotoExtension) + public function __construct($userID, $coverPhotoHash, $coverPhotoExtension, int $coverPhotoHasWebP) { $this->userID = $userID; $this->coverPhotoHash = $coverPhotoHash; $this->coverPhotoExtension = $coverPhotoExtension; + $this->coverPhotoHasWebP = $coverPhotoHasWebP; } /** @@ -67,7 +73,7 @@ class UserCoverPhoto implements IUserCoverPhoto /** * @inheritDoc */ - public function getLocation() + public function getLocation(?bool $forceWebP = null): string { return WCF_DIR . 'images/coverPhotos/' . $this->getFilename(); } @@ -75,7 +81,7 @@ class UserCoverPhoto implements IUserCoverPhoto /** * @inheritDoc */ - public function getURL() + public function getURL(?bool $forceWebP = null): string { return WCF::getPath() . 'images/coverPhotos/' . $this->getFilename(); } @@ -83,7 +89,7 @@ class UserCoverPhoto implements IUserCoverPhoto /** * @inheritDoc */ - public function getFilename() + public function getFilename(?bool $forceWebP = null): string { return \substr( $this->coverPhotoHash, diff --git a/wcfsetup/install/files/lib/util/ImageUtil.class.php b/wcfsetup/install/files/lib/util/ImageUtil.class.php index acaee1f71a..22cc655dd8 100644 --- a/wcfsetup/install/files/lib/util/ImageUtil.class.php +++ b/wcfsetup/install/files/lib/util/ImageUtil.class.php @@ -226,6 +226,49 @@ final class ImageUtil return $supportsWebP; } + /** + * Creates a WebP variant of the source image. Returns `true` if a + * `webp` file was created, `false` if a jpeg was create dand `null` + * if no action was taken. + */ + public static function createWebpVariant(string $sourceLocation, string $outputFilenameWithoutExtension): ?bool + { + $imageData = \getimagesize($sourceLocation); + if ($imageData === false) { + throw new \InvalidArgumentException("The source location is not a valid image."); + } + + $extension = self::getExtensionByMimeType($imageData['mime']); + switch ($extension) { + case 'gif': + // GIFs are not processed. + return null; + + case 'jpg': + case 'png': + case 'webp': + break; + + default: + throw new \InvalidArgumentException("Unsupported image format, expecting one of 'gif', 'jpg', 'png' or 'webp'."); + } + + $imageAdapter = ImageHandler::getInstance()->getAdapter(); + $imageAdapter->loadFile($sourceLocation); + $image = $imageAdapter->getImage(); + + // The source file is a webp, create a fallback jpeg instead. + if ($imageData[2] === 'webp') { + $imageAdapter->saveImageAs($image, "{$outputFilenameWithoutExtension}.jpg", "jpeg", 80); + + return false; + } else { + $imageAdapter->saveImageAs($image, "{$outputFilenameWithoutExtension}.webp", "webp", 80); + + return true; + } + } + /** * Forbid creation of ImageUtil objects. */ diff --git a/wcfsetup/setup/db/install.sql b/wcfsetup/setup/db/install.sql index 3c87fa3b35..78895f2595 100644 --- a/wcfsetup/setup/db/install.sql +++ b/wcfsetup/setup/db/install.sql @@ -1477,6 +1477,7 @@ CREATE TABLE wcf1_user ( trophyPoints INT(10) NOT NULL DEFAULT 0, coverPhotoHash CHAR(40) DEFAULT NULL, coverPhotoExtension VARCHAR(4) NOT NULL DEFAULT '', + coverPhotoHasWebP TINYINT(1) NOT NULL DEFAULT 0, disableCoverPhoto TINYINT(1) NOT NULL DEFAULT 0, disableCoverPhotoReason TEXT, disableCoverPhotoExpires INT(10) NOT NULL DEFAULT 0, -- 2.20.1