#ifndef DWM_PLUGINS_MEDIA_UTILS_H #define DWM_PLUGINS_MEDIA_UTILS_H #include #include #include #include #include #include #include #include #include #include #include #include namespace bananarchy { struct JpegErrMgr { struct jpeg_error_mgr pub; jmp_buf setjmpBuf; }; inline void jpegErrorExit(j_common_ptr cinfo) { JpegErrMgr* myerr = reinterpret_cast(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(width); cinfo.image_height = static_cast(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(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* 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(cinfo.output_width); int h = static_cast(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(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* 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(image.width); int h = static_cast(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* outRgb, int* outW, int* outH) { std::string low = path; for (char& c : low) { c = static_cast(std::tolower(static_cast(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(sy) * srcW * 3; uint32_t* drow = dstBgrx + static_cast(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(px[0]) << 16) | (static_cast(px[1]) << 8) | static_cast(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(sy) * 800 * 3; uint32_t* drow = dstBgrx + static_cast(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(px[0]) << 16) | (static_cast(px[1]) << 8) | static_cast(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(bytes) / (1024.0 * 1024.0 * 1024.0)); } else if (bytes >= 1024LL * 1024LL) { std::snprintf(buf, sizeof(buf), "%.1fM", static_cast(bytes) / (1024.0 * 1024.0)); } else if (bytes >= 1024LL) { std::snprintf(buf, sizeof(buf), "%.1fK", static_cast(bytes) / 1024.0); } else { std::snprintf(buf, sizeof(buf), "%lldB", static_cast(bytes)); } return buf; } } // namespace bananarchy #endif // DWM_PLUGINS_MEDIA_UTILS_H