From ae4f498fdac07eea6ba48e5c85349edd38b5a2f7 Mon Sep 17 00:00:00 2001 From: tobiichi3227 <86729076+tobiichi3227@users.noreply.github.com> Date: Fri, 22 Dec 2023 05:15:05 +0800 Subject: [PATCH] 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 --- CMakeLists.txt | 4 +- README.md | 11 +++-- nix/default.nix | 2 + src/helpers/Webp.cpp | 84 ++++++++++++++++++++++++++++++++++ src/helpers/Webp.hpp | 7 +++ src/render/WallpaperTarget.cpp | 2 + src/render/WallpaperTarget.hpp | 3 +- 7 files changed, 105 insertions(+), 8 deletions(-) create mode 100644 src/helpers/Webp.cpp create mode 100644 src/helpers/Webp.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e5bf610..67349a0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.4) -project(hyprpaper +project(hyprpaper DESCRIPTION "A blazing fast wayland wallpaper utility" ) @@ -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") diff --git a/README.md b/README.md index 0209bff..0809d55 100644 --- a/README.md +++ b/README.md @@ -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: @@ -106,9 +107,9 @@ In the actual configuration for Hyprland, *hyprland.conf*, variables can be set *~/.config/hypr/hyprland.conf* ``` -$w1 = hyprctl hyprpaper wallpaper "DP-1,~/Pictures/myepicpng.png" -$w2 = hyprctl hyprpaper wallpaper "DP-1,~/Pictures/myepicpngToo.png" -$w3 = hyprctl hyprpaper wallpaper "DP-1,~/Pictures/myepicpngAlso.png" +$w1 = hyprctl hyprpaper wallpaper "DP-1,~/Pictures/myepicpng.png" +$w2 = hyprctl hyprpaper wallpaper "DP-1,~/Pictures/myepicpngToo.png" +$w3 = hyprctl hyprpaper wallpaper "DP-1,~/Pictures/myepicpngAlso.png" #yes use quotes around desired monitor and wallpaper #... continued with desired amount ``` @@ -127,7 +128,7 @@ bind=SUPER,2,exec,$w2 #SuperKey + 2 switches to wallpaper $w2 on DP-1 as def bind=SUPER,3,workspace,3 #Superkey + 3 switches to workspace 3 bind=SUPER,3,exec,$w3 #SuperKey + 3 switches to wallpaper $w3 on DP-1 as defined in the variable -#... and so on +#... and so on ``` Because the default behavior in Hyprland is to also switch the workspace whenever movetoworkspace is used to move a window to another workspace you may want to include the following: diff --git a/nix/default.nix b/nix/default.nix index c4816af..9114625 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -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 diff --git a/src/helpers/Webp.cpp b/src/helpers/Webp.cpp new file mode 100644 index 0000000..de80f89 --- /dev/null +++ b/src/helpers/Webp.cpp @@ -0,0 +1,84 @@ +#include "Webp.hpp" + +#include +#include +#include + +#include + +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; + +} diff --git a/src/helpers/Webp.hpp b/src/helpers/Webp.hpp new file mode 100644 index 0000000..bc09611 --- /dev/null +++ b/src/helpers/Webp.hpp @@ -0,0 +1,7 @@ +#pragma once + +#include "../defines.hpp" + +namespace WEBP { + cairo_surface_t* createSurfaceFromWEBP(const std::string&); +}; diff --git a/src/render/WallpaperTarget.cpp b/src/render/WallpaperTarget.cpp index ae4d986..7881d57 100644 --- a/src/render/WallpaperTarget.cpp +++ b/src/render/WallpaperTarget.cpp @@ -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); diff --git a/src/render/WallpaperTarget.hpp b/src/render/WallpaperTarget.hpp index 15ebccf..356c046 100644 --- a/src/render/WallpaperTarget.hpp +++ b/src/render/WallpaperTarget.hpp @@ -2,12 +2,13 @@ #include "../defines.hpp" #include "../helpers/Jpeg.hpp" +#include "../helpers/Webp.hpp" class CWallpaperTarget { public: ~CWallpaperTarget(); - + void create(const std::string& path); std::string m_szPath;