mirror of
https://github.com/hyprwm/hyprlock.git
synced 2024-11-16 23:05:58 +01:00
core: Add support for JPEG and WEBP background images (#286)
* Add KDevelop files to the .gitignore * Add support for JPEG and WEBP background images Most of the code is copy-pasted from hyprpaper * Try doing nix stuff * Do not use brackets for short ifs
This commit is contained in:
parent
8658386f21
commit
415262065f
8 changed files with 237 additions and 4 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -6,3 +6,4 @@ build/
|
||||||
compile_commands.json
|
compile_commands.json
|
||||||
protocols/*.c
|
protocols/*.c
|
||||||
protocols/*.h
|
protocols/*.h
|
||||||
|
*.kdev4
|
||||||
|
|
|
@ -35,7 +35,7 @@ message(STATUS "Checking deps...")
|
||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
find_package(OpenGL REQUIRED)
|
find_package(OpenGL REQUIRED)
|
||||||
pkg_check_modules(deps REQUIRED IMPORTED_TARGET wayland-client wayland-protocols wayland-egl hyprlang>=0.4.0 egl opengl xkbcommon cairo pangocairo libdrm gbm)
|
pkg_check_modules(deps REQUIRED IMPORTED_TARGET wayland-client wayland-protocols wayland-egl hyprlang>=0.4.0 egl opengl xkbcommon libjpeg libwebp libmagic cairo pangocairo libdrm gbm)
|
||||||
|
|
||||||
file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp")
|
file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp")
|
||||||
add_executable(hyprlock ${SRCFILES})
|
add_executable(hyprlock ${SRCFILES})
|
||||||
|
|
|
@ -4,8 +4,11 @@
|
||||||
cmake,
|
cmake,
|
||||||
pkg-config,
|
pkg-config,
|
||||||
cairo,
|
cairo,
|
||||||
|
file,
|
||||||
libdrm,
|
libdrm,
|
||||||
libGL,
|
libGL,
|
||||||
|
libjpeg,
|
||||||
|
libwebp,
|
||||||
libxkbcommon,
|
libxkbcommon,
|
||||||
mesa,
|
mesa,
|
||||||
hyprlang,
|
hyprlang,
|
||||||
|
@ -28,8 +31,11 @@ stdenv.mkDerivation {
|
||||||
|
|
||||||
buildInputs = [
|
buildInputs = [
|
||||||
cairo
|
cairo
|
||||||
|
file
|
||||||
libdrm
|
libdrm
|
||||||
libGL
|
libGL
|
||||||
|
libjpeg
|
||||||
|
libwebp
|
||||||
libxkbcommon
|
libxkbcommon
|
||||||
mesa
|
mesa
|
||||||
hyprlang
|
hyprlang
|
||||||
|
|
75
src/helpers/Jpeg.cpp
Normal file
75
src/helpers/Jpeg.cpp
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
#include "Jpeg.hpp"
|
||||||
|
#include "Log.hpp"
|
||||||
|
|
||||||
|
#include <jpeglib.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
cairo_surface_t* JPEG::createSurfaceFromJPEG(const std::filesystem::path& path) {
|
||||||
|
|
||||||
|
if (!std::filesystem::exists(path)) {
|
||||||
|
Debug::log(ERR, "createSurfaceFromJPEG: file doesn't exist??");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (__BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__) {
|
||||||
|
Debug::log(CRIT, "tried to load a jpeg on a big endian system! ping vaxry he is lazy.");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 JPEG is in the memory
|
||||||
|
|
||||||
|
jpeg_decompress_struct decompressStruct = {};
|
||||||
|
jpeg_error_mgr errorManager = {};
|
||||||
|
|
||||||
|
decompressStruct.err = jpeg_std_error(&errorManager);
|
||||||
|
jpeg_create_decompress(&decompressStruct);
|
||||||
|
jpeg_mem_src(&decompressStruct, (const unsigned char*)imageRawData, fileInfo.st_size);
|
||||||
|
jpeg_read_header(&decompressStruct, true);
|
||||||
|
|
||||||
|
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||||
|
decompressStruct.out_color_space = JCS_EXT_BGRA;
|
||||||
|
#else
|
||||||
|
decompressStruct.out_color_space = JCS_EXT_ARGB;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// decompress
|
||||||
|
jpeg_start_decompress(&decompressStruct);
|
||||||
|
|
||||||
|
auto cairoSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, decompressStruct.output_width, decompressStruct.output_height);
|
||||||
|
|
||||||
|
if (cairo_surface_status(cairoSurface) != CAIRO_STATUS_SUCCESS) {
|
||||||
|
Debug::log(ERR, "createSurfaceFromJPEG: Cairo Failed (?)");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto CAIRODATA = cairo_image_surface_get_data(cairoSurface);
|
||||||
|
const auto CAIROSTRIDE = cairo_image_surface_get_stride(cairoSurface);
|
||||||
|
JSAMPROW rowRead;
|
||||||
|
|
||||||
|
while (decompressStruct.output_scanline < decompressStruct.output_height) {
|
||||||
|
const auto PROW = CAIRODATA + (decompressStruct.output_scanline * CAIROSTRIDE);
|
||||||
|
rowRead = PROW;
|
||||||
|
jpeg_read_scanlines(&decompressStruct, &rowRead, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
cairo_surface_mark_dirty(cairoSurface);
|
||||||
|
cairo_surface_set_mime_data(cairoSurface, CAIRO_MIME_TYPE_JPEG, (const unsigned char*)imageRawData, fileInfo.st_size, free, imageRawData);
|
||||||
|
jpeg_finish_decompress(&decompressStruct);
|
||||||
|
jpeg_destroy_decompress(&decompressStruct);
|
||||||
|
|
||||||
|
return cairoSurface;
|
||||||
|
}
|
8
src/helpers/Jpeg.hpp
Normal file
8
src/helpers/Jpeg.hpp
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <cairo/cairo.h>
|
||||||
|
|
||||||
|
namespace JPEG {
|
||||||
|
cairo_surface_t* createSurfaceFromJPEG(const std::filesystem::path&);
|
||||||
|
};
|
85
src/helpers/Webp.cpp
Normal file
85
src/helpers/Webp.cpp
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
#include "Webp.hpp"
|
||||||
|
#include "Log.hpp"
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include <webp/decode.h>
|
||||||
|
|
||||||
|
cairo_surface_t* WEBP::createSurfaceFromWEBP(const std::filesystem::path& path) {
|
||||||
|
|
||||||
|
if (!std::filesystem::exists(path)) {
|
||||||
|
Debug::log(ERR, "createSurfaceFromWEBP: file doesn't exist??");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||||
|
config.output.colorspace = MODE_bgrA;
|
||||||
|
#else
|
||||||
|
config.output.colorspace = MODE_Argb;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
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 (?)");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
}
|
8
src/helpers/Webp.hpp
Normal file
8
src/helpers/Webp.hpp
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <cairo/cairo.h>
|
||||||
|
|
||||||
|
namespace WEBP {
|
||||||
|
cairo_surface_t* createSurfaceFromWEBP(const std::filesystem::path&);
|
||||||
|
};
|
|
@ -2,10 +2,14 @@
|
||||||
#include "../config/ConfigManager.hpp"
|
#include "../config/ConfigManager.hpp"
|
||||||
#include "../core/Egl.hpp"
|
#include "../core/Egl.hpp"
|
||||||
#include <cairo/cairo.h>
|
#include <cairo/cairo.h>
|
||||||
|
#include <magic.h>
|
||||||
#include <pango/pangocairo.h>
|
#include <pango/pangocairo.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <filesystem>
|
||||||
#include "../core/hyprlock.hpp"
|
#include "../core/hyprlock.hpp"
|
||||||
#include "../helpers/MiscFunctions.hpp"
|
#include "../helpers/MiscFunctions.hpp"
|
||||||
|
#include "../helpers/Jpeg.hpp"
|
||||||
|
#include "../helpers/Webp.hpp"
|
||||||
|
|
||||||
std::mutex cvmtx;
|
std::mutex cvmtx;
|
||||||
|
|
||||||
|
@ -105,6 +109,13 @@ SPreloadedAsset* CAsyncResourceGatherer::getAssetByID(const std::string& id) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class FileType {
|
||||||
|
PNG,
|
||||||
|
JPEG,
|
||||||
|
WEBP,
|
||||||
|
UNKNOWN,
|
||||||
|
};
|
||||||
|
|
||||||
void CAsyncResourceGatherer::gather() {
|
void CAsyncResourceGatherer::gather() {
|
||||||
const auto CWIDGETS = g_pConfigManager->getWidgetConfigs();
|
const auto CWIDGETS = g_pConfigManager->getWidgetConfigs();
|
||||||
|
|
||||||
|
@ -132,10 +143,49 @@ void CAsyncResourceGatherer::gather() {
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
std::string id = (c.type == "background" ? std::string{"background:"} : std::string{"image:"}) + path;
|
std::string id = (c.type == "background" ? std::string{"background:"} : std::string{"image:"}) + path;
|
||||||
const auto ABSOLUTEPATH = absolutePath(path, "");
|
std::filesystem::path ABSOLUTEPATH(absolutePath(path, ""));
|
||||||
|
|
||||||
|
// determine the file type
|
||||||
|
std::string ext = ABSOLUTEPATH.extension().string();
|
||||||
|
// convert the extension to lower case
|
||||||
|
std::transform(ext.begin(), ext.end(), ext.begin(), [](char c) { return c <= 'Z' && c >= 'A' ? c - ('Z' - 'z') : c; });
|
||||||
|
|
||||||
|
FileType ft = FileType::UNKNOWN;
|
||||||
|
Debug::log(WARN, "Extension: {}", ext);
|
||||||
|
if (ext == ".png")
|
||||||
|
ft = FileType::PNG;
|
||||||
|
else if (ext == ".jpg" || ext == ".jpeg")
|
||||||
|
ft = FileType::JPEG;
|
||||||
|
else if (ext == ".webp")
|
||||||
|
ft = FileType::WEBP;
|
||||||
|
else {
|
||||||
|
// magic is slow, so only use it when no recognized extension is found
|
||||||
|
auto handle = magic_open(MAGIC_NONE | MAGIC_COMPRESS);
|
||||||
|
magic_load(handle, nullptr);
|
||||||
|
|
||||||
|
const auto type_str = std::string(magic_file(handle, path.c_str()));
|
||||||
|
const auto first_word = type_str.substr(0, type_str.find(" "));
|
||||||
|
magic_close(handle);
|
||||||
|
|
||||||
|
if (first_word == "PNG")
|
||||||
|
ft = FileType::PNG;
|
||||||
|
else if (first_word == "JPEG")
|
||||||
|
ft = FileType::JPEG;
|
||||||
|
else if (first_word == "RIFF" && type_str.find("Web/P image") != std::string::npos)
|
||||||
|
ft = FileType::WEBP;
|
||||||
|
}
|
||||||
|
|
||||||
// preload bg img
|
// preload bg img
|
||||||
const auto CAIROISURFACE = cairo_image_surface_create_from_png(ABSOLUTEPATH.c_str());
|
cairo_surface_t* CAIROISURFACE = nullptr;
|
||||||
|
switch (ft) {
|
||||||
|
case FileType::PNG: CAIROISURFACE = cairo_image_surface_create_from_png(ABSOLUTEPATH.c_str()); break;
|
||||||
|
case FileType::JPEG: CAIROISURFACE = JPEG::createSurfaceFromJPEG(ABSOLUTEPATH); break;
|
||||||
|
case FileType::WEBP: CAIROISURFACE = WEBP::createSurfaceFromWEBP(ABSOLUTEPATH); break;
|
||||||
|
default: Debug::log(ERR, "unrecognized image format of {}", path.c_str()); continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CAIROISURFACE == nullptr)
|
||||||
|
continue;
|
||||||
|
|
||||||
const auto CAIRO = cairo_create(CAIROISURFACE);
|
const auto CAIRO = cairo_create(CAIROISURFACE);
|
||||||
cairo_scale(CAIRO, 1, 1);
|
cairo_scale(CAIRO, 1, 1);
|
||||||
|
|
Loading…
Reference in a new issue