73 lines
3.0 KiB
C++
73 lines
3.0 KiB
C++
#include "core/rendering/screenshot.hpp"
|
|
|
|
#include <chrono>
|
|
#include <cstdint>
|
|
#include <ctime>
|
|
#include <filesystem>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "external/stb_image_write.h"
|
|
|
|
namespace Screenshot {
|
|
|
|
namespace {
|
|
|
|
// Estat de mòdul: ruta base on s'escriu la subcarpeta
|
|
// `screenshots/`. Buida per defecte ⇒ relativa al CWD (fallback
|
|
// si el Director no ha pogut establir la ruta per plataforma).
|
|
std::string g_base_dir; // NOLINT(*-avoid-non-const-global-variables) -- estat de mòdul (singleton-like): s'estableix una vegada al startup via setBaseDir() i es llegeix N vegades; encapsulat dins del namespace anònim, no és accessible des de fora.
|
|
|
|
// Construeix la ruta del directori on van les captures:
|
|
// `<base>/screenshots/`. Si la base és buida, retorna
|
|
// "screenshots/" relativa al CWD.
|
|
auto screenshotsDir() -> std::string {
|
|
if (g_base_dir.empty()) { return "screenshots/"; }
|
|
const bool ENDS_WITH_SEP = (g_base_dir.back() == '/' || g_base_dir.back() == '\\');
|
|
return g_base_dir + (ENDS_WITH_SEP ? "" : "/") + "screenshots/";
|
|
}
|
|
|
|
// Converteix ARGB8888 → RGBA8888 in-place i escriu el PNG. stb_image_write
|
|
// espera RGBA en ordre byte (little-endian: R al byte baix, A al byte alt).
|
|
auto writePng(std::uint32_t* buffer, int width, int height, const std::string& filepath) -> bool {
|
|
const int TOTAL = width * height;
|
|
for (int i = 0; i < TOTAL; ++i) {
|
|
const std::uint32_t P = buffer[i];
|
|
const std::uint32_t A = (P >> 24) & 0xFF;
|
|
const std::uint32_t R = (P >> 16) & 0xFF;
|
|
const std::uint32_t G = (P >> 8) & 0xFF;
|
|
const std::uint32_t B = P & 0xFF;
|
|
buffer[i] = R | (G << 8) | (B << 16) | (A << 24);
|
|
}
|
|
return stbi_write_png(filepath.c_str(), width, height, 4, buffer, width * 4) != 0;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void setBaseDir(std::string dir) {
|
|
g_base_dir = std::move(dir);
|
|
}
|
|
|
|
auto save(const std::uint32_t* buffer, int width, int height) -> Result {
|
|
const std::string FOLDER = screenshotsDir();
|
|
std::error_code ec;
|
|
std::filesystem::create_directories(FOLDER, ec);
|
|
|
|
const auto NOW = std::chrono::system_clock::now();
|
|
const std::time_t TIME = std::chrono::system_clock::to_time_t(NOW);
|
|
const std::tm* tm = std::localtime(&TIME);
|
|
char timestamp[20];
|
|
std::strftime(timestamp, sizeof(timestamp), "%Y%m%d_%H%M%S", tm);
|
|
|
|
const std::string FILENAME = std::string("scr_") + timestamp + ".png";
|
|
const std::string FILEPATH = FOLDER + FILENAME;
|
|
|
|
// Còpia local per no tocar el canvas original durant la conversió.
|
|
std::vector<std::uint32_t> copy(buffer, buffer + (static_cast<size_t>(width) * height));
|
|
if (!writePng(copy.data(), width, height, FILEPATH)) { return {}; }
|
|
return {.filename = FILENAME, .folder = FOLDER};
|
|
}
|
|
|
|
} // namespace Screenshot
|