Files
bananarchy/src/dwm-plugins/media_utils.h
T

270 lines
7.7 KiB
C++

#ifndef DWM_PLUGINS_MEDIA_UTILS_H
#define DWM_PLUGINS_MEDIA_UTILS_H
#include <algorithm>
#include <cctype>
#include <csetjmp>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <string>
#include <vector>
#include <jpeglib.h>
#include <png.h>
namespace bananarchy {
struct JpegErrMgr {
struct jpeg_error_mgr pub;
jmp_buf setjmpBuf;
};
inline void jpegErrorExit(j_common_ptr cinfo) {
JpegErrMgr* myerr = reinterpret_cast<JpegErrMgr*>(cinfo->err);
longjmp(myerr->setjmpBuf, 1);
}
inline bool saveRgb24AsJpeg(const std::string& path,
const uint8_t* rgb,
int width,
int height,
int quality = 92) {
if (!rgb || width <= 0 || height <= 0) {
return false;
}
FILE* fp = std::fopen(path.c_str(), "wb");
if (!fp) {
return false;
}
struct jpeg_compress_struct cinfo {};
JpegErrMgr jerr {};
cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = jpegErrorExit;
if (setjmp(jerr.setjmpBuf)) {
jpeg_destroy_compress(&cinfo);
std::fclose(fp);
return false;
}
jpeg_create_compress(&cinfo);
jpeg_stdio_dest(&cinfo, fp);
cinfo.image_width = static_cast<JDIMENSION>(width);
cinfo.image_height = static_cast<JDIMENSION>(height);
cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB;
jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, quality, TRUE);
jpeg_start_compress(&cinfo, TRUE);
int rowStride = width * 3;
while (cinfo.next_scanline < cinfo.image_height) {
JSAMPROW rowPtr =
const_cast<JSAMPROW>(rgb + cinfo.next_scanline * rowStride);
jpeg_write_scanlines(&cinfo, &rowPtr, 1);
}
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
std::fclose(fp);
return true;
}
inline bool loadJpegRgb24(const std::string& path,
std::vector<uint8_t>* outRgb,
int* outW,
int* outH) {
FILE* fp = std::fopen(path.c_str(), "rb");
if (!fp) {
return false;
}
struct jpeg_decompress_struct cinfo {};
JpegErrMgr jerr {};
cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = jpegErrorExit;
if (setjmp(jerr.setjmpBuf)) {
jpeg_destroy_decompress(&cinfo);
std::fclose(fp);
return false;
}
jpeg_create_decompress(&cinfo);
jpeg_stdio_src(&cinfo, fp);
if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK) {
jpeg_destroy_decompress(&cinfo);
std::fclose(fp);
return false;
}
// Use libjpeg fast DCT downscaling for large images so decoding is instant
if (cinfo.image_width > 960 || cinfo.image_height > 960) {
cinfo.scale_num = 1;
cinfo.scale_denom = 2;
}
cinfo.out_color_space = JCS_RGB;
jpeg_start_decompress(&cinfo);
int w = static_cast<int>(cinfo.output_width);
int h = static_cast<int>(cinfo.output_height);
if (w <= 0 || h <= 0 || w > 8192 || h > 8192) {
jpeg_destroy_decompress(&cinfo);
std::fclose(fp);
return false;
}
outRgb->resize(static_cast<size_t>(w) * h * 3);
int rowStride = w * 3;
while (cinfo.output_scanline < cinfo.output_height) {
JSAMPROW rowPtr = outRgb->data() + cinfo.output_scanline * rowStride;
jpeg_read_scanlines(&cinfo, &rowPtr, 1);
}
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
std::fclose(fp);
*outW = w;
*outH = h;
return true;
}
inline bool loadPngRgb24(const std::string& path,
std::vector<uint8_t>* outRgb,
int* outW,
int* outH) {
png_image image {};
image.version = PNG_IMAGE_VERSION;
if (!png_image_begin_read_from_file(&image, path.c_str())) {
return false;
}
image.format = PNG_FORMAT_RGB;
int w = static_cast<int>(image.width);
int h = static_cast<int>(image.height);
if (w <= 0 || h <= 0 || w > 8192 || h > 8192) {
png_image_free(&image);
return false;
}
outRgb->resize(PNG_IMAGE_SIZE(image));
if (!png_image_finish_read(&image, nullptr, outRgb->data(), 0, nullptr)) {
png_image_free(&image);
return false;
}
png_image_free(&image);
*outW = w;
*outH = h;
return true;
}
inline bool loadImageRgb24(const std::string& path,
std::vector<uint8_t>* outRgb,
int* outW,
int* outH) {
std::string low = path;
for (char& c : low) {
c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
}
if (low.size() >= 4 && low.substr(low.size() - 4) == ".png") {
if (loadPngRgb24(path, outRgb, outW, outH)) {
return true;
}
}
if (loadJpegRgb24(path, outRgb, outW, outH)) {
return true;
}
if (loadPngRgb24(path, outRgb, outW, outH)) {
return true;
}
return false;
}
inline void scaleRgb24ToBgrxCanvas(const uint8_t* srcRgb,
int srcW,
int srcH,
uint32_t* dstBgrx,
int canvasW,
int canvasH,
uint32_t bgPixel = 0x111111) {
for (int i = 0; i < canvasW * canvasH; ++i) {
dstBgrx[i] = bgPixel;
}
if (!srcRgb || srcW <= 0 || srcH <= 0 || canvasW <= 0 || canvasH <= 0) {
return;
}
int drawW = canvasW;
int drawH = (srcH * canvasW) / srcW;
if (drawH > canvasH) {
drawH = canvasH;
drawW = (srcW * canvasH) / srcH;
}
drawW = std::max(1, drawW);
drawH = std::max(1, drawH);
int offX = (canvasW - drawW) / 2;
int offY = (canvasH - drawH) / 2;
for (int dy = 0; dy < drawH; ++dy) {
int sy = std::min(srcH - 1, (dy * srcH) / drawH);
const uint8_t* srow = srcRgb + static_cast<size_t>(sy) * srcW * 3;
uint32_t* drow = dstBgrx + static_cast<size_t>(offY + dy) * canvasW + offX;
for (int dx = 0; dx < drawW; ++dx) {
int sx = std::min(srcW - 1, (dx * srcW) / drawW);
const uint8_t* px = srow + sx * 3;
drow[dx] = (static_cast<uint32_t>(px[0]) << 16) |
(static_cast<uint32_t>(px[1]) << 8) |
static_cast<uint32_t>(px[2]);
}
}
}
inline void downscaleViewfinder800x600To240x180(const uint8_t* srcRgb,
uint32_t* dstBgrx,
int zoomFactor = 1) {
if (!srcRgb || !dstBgrx) {
return;
}
zoomFactor = std::clamp(zoomFactor, 1, 4);
int cropW = 800 / zoomFactor;
int cropH = 600 / zoomFactor;
int startX = (800 - cropW) / 2;
int startY = (600 - cropH) / 2;
for (int dy = 0; dy < 180; ++dy) {
int sy = startY + (dy * cropH) / 180;
const uint8_t* srow = srcRgb + static_cast<size_t>(sy) * 800 * 3;
uint32_t* drow = dstBgrx + static_cast<size_t>(dy) * 240;
for (int dx = 0; dx < 240; ++dx) {
int sx = startX + (dx * cropW) / 240;
const uint8_t* px = srow + sx * 3;
drow[dx] = (static_cast<uint32_t>(px[0]) << 16) |
(static_cast<uint32_t>(px[1]) << 8) |
static_cast<uint32_t>(px[2]);
}
}
}
inline std::string formatFileSize(off_t bytes) {
char buf[32];
if (bytes >= 1024LL * 1024LL * 1024LL) {
std::snprintf(buf, sizeof(buf), "%.1fG",
static_cast<double>(bytes) / (1024.0 * 1024.0 * 1024.0));
} else if (bytes >= 1024LL * 1024LL) {
std::snprintf(buf, sizeof(buf), "%.1fM",
static_cast<double>(bytes) / (1024.0 * 1024.0));
} else if (bytes >= 1024LL) {
std::snprintf(buf, sizeof(buf), "%.1fK",
static_cast<double>(bytes) / 1024.0);
} else {
std::snprintf(buf, sizeof(buf), "%lldB", static_cast<long long>(bytes));
}
return buf;
}
} // namespace bananarchy
#endif // DWM_PLUGINS_MEDIA_UTILS_H