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

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.4) cmake_minimum_required(VERSION 3.4)
project(hyprpaper project(hyprpaper
DESCRIPTION "A blazing fast wayland wallpaper utility" 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(Threads REQUIRED)
find_package(PkgConfig 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") 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
- libglvnd-core - libglvnd-core
- libjpeg-turbo - libjpeg-turbo
- libwebp
Also `gcc-c++` and `ninja` needs to installed. Also `gcc-c++` and `ninja` needs to installed.
@ -67,7 +68,7 @@ wallpaper = monitor2,/path/to/next_image.png
# .. more monitors # .. 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: 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* *~/.config/hypr/hyprland.conf*
``` ```
$w1 = hyprctl hyprpaper wallpaper "DP-1,~/Pictures/myepicpng.png" $w1 = hyprctl hyprpaper wallpaper "DP-1,~/Pictures/myepicpng.png"
$w2 = hyprctl hyprpaper wallpaper "DP-1,~/Pictures/myepicpngToo.png" $w2 = hyprctl hyprpaper wallpaper "DP-1,~/Pictures/myepicpngToo.png"
$w3 = hyprctl hyprpaper wallpaper "DP-1,~/Pictures/myepicpngAlso.png" $w3 = hyprctl hyprpaper wallpaper "DP-1,~/Pictures/myepicpngAlso.png"
#yes use quotes around desired monitor and wallpaper #yes use quotes around desired monitor and wallpaper
#... continued with desired amount #... 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,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 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: 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:

View file

@ -13,6 +13,7 @@
libselinux, libselinux,
libsepol, libsepol,
libthai, libthai,
libwebp,
pango, pango,
pcre, pcre,
util-linux, util-linux,
@ -44,6 +45,7 @@ stdenv.mkDerivation {
libselinux libselinux
libsepol libsepol
libthai libthai
libwebp
pango pango
pcre pcre
wayland 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) { } 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); CAIROSURFACE = JPEG::createSurfaceFromJPEG(path);
m_bHasAlpha = false; m_bHasAlpha = false;
} else if (path.find(".webp") == len - 5 || path.find(".WEBP") == len - 5) {
CAIROSURFACE = WEBP::createSurfaceFromWEBP(path);
} else { } else {
// magic is slow, so only use it when no recognized extension is found // magic is slow, so only use it when no recognized extension is found
auto handle = magic_open(MAGIC_NONE|MAGIC_COMPRESS); auto handle = magic_open(MAGIC_NONE|MAGIC_COMPRESS);

View file

@ -2,12 +2,13 @@
#include "../defines.hpp" #include "../defines.hpp"
#include "../helpers/Jpeg.hpp" #include "../helpers/Jpeg.hpp"
#include "../helpers/Webp.hpp"
class CWallpaperTarget { class CWallpaperTarget {
public: public:
~CWallpaperTarget(); ~CWallpaperTarget();
void create(const std::string& path); void create(const std::string& path);
std::string m_szPath; std::string m_szPath;