feat: Add webp support (#113)

* feat: add webp support

* readme: add libwebp dependency

* refactor: move including webp to source file

* style: fix using tab

* style: make const value upper-case

* Nix: add libwebp

---------

Co-authored-by: Mihai Fufezan <fufexan@protonmail.com>
This commit is contained in:
tobiichi3227 2023-12-22 05:15:05 +08:00 committed by GitHub
parent a5e21e2d4a
commit ae4f498fda
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 105 additions and 8 deletions

View file

@ -41,7 +41,7 @@ add_compile_options(-Wall -Wextra -Wno-unused-parameter -Wno-unused-value -Wno-m
find_package(Threads REQUIRED)
find_package(PkgConfig REQUIRED)
pkg_check_modules(deps REQUIRED IMPORTED_TARGET wayland-client wayland-protocols cairo pango pangocairo libjpeg)
pkg_check_modules(deps REQUIRED IMPORTED_TARGET wayland-client wayland-protocols cairo pango pangocairo libjpeg libwebp)
file(GLOB_RECURSE SRCFILES "src/*.cpp")

View file

@ -26,6 +26,7 @@ The development files of these packages need to be installed on the system for `
- libglvnd
- libglvnd-core
- libjpeg-turbo
- libwebp
Also `gcc-c++` and `ninja` needs to installed.
@ -67,7 +68,7 @@ wallpaper = monitor2,/path/to/next_image.png
# .. more monitors
```
Preload will tell Hyprland to load a particular image (supported formats: png, jpg, jpeg). Wallpaper will apply the wallpaper to the selected output (`monitor` is the monitor's name, easily can be retrieved with `hyprctl monitors`. You can leave it empty for a wildcard (aka fallback). You can also use `desc:` followed by the monitor's description without the (PORT) at the end)
Preload will tell Hyprland to load a particular image (supported formats: png, jpg, jpeg, webp). Wallpaper will apply the wallpaper to the selected output (`monitor` is the monitor's name, easily can be retrieved with `hyprctl monitors`. You can leave it empty for a wildcard (aka fallback). You can also use `desc:` followed by the monitor's description without the (PORT) at the end)
You may add `contain:` before the file path in `wallpaper=` to set the mode to contain instead of cover:

View file

@ -13,6 +13,7 @@
libselinux,
libsepol,
libthai,
libwebp,
pango,
pcre,
util-linux,
@ -44,6 +45,7 @@ stdenv.mkDerivation {
libselinux
libsepol
libthai
libwebp
pango
pcre
wayland

84
src/helpers/Webp.cpp Normal file
View file

@ -0,0 +1,84 @@
#include "Webp.hpp"
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <webp/decode.h>
cairo_surface_t* WEBP::createSurfaceFromWEBP(const std::string& path) {
if (!std::filesystem::exists(path)) {
Debug::log(ERR, "createSurfaceFromWEBP: file doesn't exist??");
exit(1);
}
void* imageRawData;
struct stat fileInfo = {};
const auto FD = open(path.c_str(), O_RDONLY);
fstat(FD, &fileInfo);
imageRawData = malloc(fileInfo.st_size);
read(FD, imageRawData, fileInfo.st_size);
close(FD);
// now the WebP is in the memory
WebPDecoderConfig config;
if (!WebPInitDecoderConfig(&config)) {
Debug::log(CRIT, "WebPInitDecoderConfig Failed");
exit(1);
}
if (WebPGetFeatures((const unsigned char*)imageRawData, fileInfo.st_size, &config.input) != VP8_STATUS_OK) {
Debug::log(ERR, "createSurfaceFromWEBP: file is not webp format");
free(imageRawData);
exit(1);
}
const auto HEIGHT = config.input.height;
const auto WIDTH = config.input.width;
auto cairoSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, WIDTH, HEIGHT);
if (cairo_surface_status(cairoSurface) != CAIRO_STATUS_SUCCESS) {
Debug::log(CRIT, "createSurfaceFromWEBP: Cairo Failed (?)");
cairo_surface_destroy(cairoSurface);
exit(1);
}
if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
config.output.colorspace = MODE_bgrA;
else
config.output.colorspace = MODE_Argb;
const auto CAIRODATA = cairo_image_surface_get_data(cairoSurface);
const auto CAIROSTRIDE = cairo_image_surface_get_stride(cairoSurface);
config.options.no_fancy_upsampling = 1;
config.output.u.RGBA.rgba = CAIRODATA;
config.output.u.RGBA.stride = CAIROSTRIDE;
config.output.u.RGBA.size = CAIROSTRIDE * HEIGHT;
config.output.is_external_memory = 1;
config.output.width = WIDTH;
config.output.height = HEIGHT;
if (WebPDecode((const unsigned char*)imageRawData, fileInfo.st_size, &config) != VP8_STATUS_OK) {
Debug::log(CRIT, "createSurfaceFromWEBP: WebP Decode Failed (?)");
exit(1);
}
cairo_surface_mark_dirty(cairoSurface);
cairo_surface_set_mime_data(cairoSurface, CAIRO_MIME_TYPE_PNG, (const unsigned char*)imageRawData, fileInfo.st_size, free, imageRawData);
WebPFreeDecBuffer(&config.output);
return cairoSurface;
}

7
src/helpers/Webp.hpp Normal file
View file

@ -0,0 +1,7 @@
#pragma once
#include "../defines.hpp"
namespace WEBP {
cairo_surface_t* createSurfaceFromWEBP(const std::string&);
};

View file

@ -18,6 +18,8 @@ void CWallpaperTarget::create(const std::string& path) {
} else if (path.find(".jpg") == len - 4 || path.find(".JPG") == len - 4 || path.find(".jpeg") == len - 5 || path.find(".JPEG") == len - 5) {
CAIROSURFACE = JPEG::createSurfaceFromJPEG(path);
m_bHasAlpha = false;
} else if (path.find(".webp") == len - 5 || path.find(".WEBP") == len - 5) {
CAIROSURFACE = WEBP::createSurfaceFromWEBP(path);
} else {
// magic is slow, so only use it when no recognized extension is found
auto handle = magic_open(MAGIC_NONE|MAGIC_COMPRESS);

View file

@ -2,6 +2,7 @@
#include "../defines.hpp"
#include "../helpers/Jpeg.hpp"
#include "../helpers/Webp.hpp"
class CWallpaperTarget {
public: