CPythonGraphic::SaveScreenShot queues a readback of the next presented frame (ScreenShotHost -> VulkanWindow::request_frame); WriteScreenShot runs the rest of 40250's function verbatim (X8R8G8B8 rows, SaveJPEG, EXIF tag). jpeg_save is encoded with stb_image_write. SHGetSpecialFolderPath returns $HOME/Documents (else cwd); CreateDirectory/_access/jpeg_save accept '\\'. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
79 lines
2.9 KiB
C++
79 lines
2.9 KiB
C++
// grp.SaveScreenShot: CPythonGraphic::SaveScreenShot queues a host frame request (ScreenShotHost.h) and
|
|
// ScreenShotHost::WriteScreenShot runs the rest of 40250's SaveScreenShot on the frame: X8R8G8B8 rows ->
|
|
// RGB -> SaveJPEG (jpeg_save, quality 100).
|
|
#include "EterPythonLib/StdAfx.h"
|
|
#include "EterPythonLib/PythonGraphic.h"
|
|
#include "../../src/platform/EterPythonLib/ScreenShotHost.h"
|
|
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <string>
|
|
#include <unistd.h>
|
|
#include <vector>
|
|
|
|
static int g_failures = 0;
|
|
#define CHECK(cond) \
|
|
do { \
|
|
if (!(cond)) { \
|
|
std::fprintf(stderr, "%s:%d: CHECK(%s)\n", __FILE__, __LINE__, #cond); \
|
|
++g_failures; \
|
|
} \
|
|
} while (0)
|
|
|
|
static std::vector<unsigned char> ReadFile(const std::string& path)
|
|
{
|
|
std::vector<unsigned char> data;
|
|
if (FILE* f = std::fopen(path.c_str(), "rb")) {
|
|
int c;
|
|
while ((c = std::fgetc(f)) != EOF)
|
|
data.push_back(static_cast<unsigned char>(c));
|
|
std::fclose(f);
|
|
}
|
|
return data;
|
|
}
|
|
|
|
int main()
|
|
{
|
|
CPythonGraphic graphic;
|
|
|
|
// No host: 40250's GetBackBuffer failure path.
|
|
ScreenShotHost::SetRequest(nullptr);
|
|
CHECK(!graphic.SaveScreenShot("unused.jpg"));
|
|
|
|
char dir[] = "/tmp/mt_screenshot_XXXXXX";
|
|
CHECK(mkdtemp(dir) != nullptr);
|
|
const std::string path = std::string(dir) + "/shot.jpg";
|
|
|
|
std::vector<std::string> requested;
|
|
ScreenShotHost::SetRequest([&](const std::string& p) { requested.push_back(p); return true; });
|
|
CHECK(graphic.SaveScreenShot(path.c_str()));
|
|
CHECK(requested.size() == 1 && requested[0] == path);
|
|
CHECK(ReadFile(path).empty()); // written when the frame arrives, not by the call
|
|
|
|
// 8x4 frame with a padded pitch; the padding bytes must not reach the image.
|
|
const unsigned w = 8, h = 4, pitch = w * 4 + 16;
|
|
std::vector<std::uint8_t> frame(pitch * h, 0xAB);
|
|
for (unsigned y = 0; y < h; ++y)
|
|
for (unsigned x = 0; x < w; ++x) {
|
|
std::uint8_t* px = &frame[y * pitch + x * 4];
|
|
px[0] = 0x20; px[1] = 0x80; px[2] = 0xE0; px[3] = 0xFF; // B, G, R, X
|
|
}
|
|
CHECK(ScreenShotHost::WriteScreenShot(requested[0].c_str(), frame.data(), w, h, pitch));
|
|
const std::vector<unsigned char> jpeg = ReadFile(path);
|
|
CHECK(jpeg.size() > 4);
|
|
CHECK(jpeg.size() > 4 && jpeg[0] == 0xFF && jpeg[1] == 0xD8);
|
|
CHECK(jpeg.size() > 4 && jpeg[jpeg.size() - 2] == 0xFF && jpeg[jpeg.size() - 1] == 0xD9);
|
|
|
|
// An unwritable path is SaveJPEG's failure.
|
|
CHECK(!ScreenShotHost::WriteScreenShot((std::string(dir) + "/missing/shot.jpg").c_str(), frame.data(), w, h, pitch));
|
|
|
|
ScreenShotHost::SetRequest(nullptr);
|
|
std::remove(path.c_str());
|
|
rmdir(dir);
|
|
if (g_failures)
|
|
std::fprintf(stderr, "port_screenshot_test: %d failure(s)\n", g_failures);
|
|
else
|
|
std::puts("port_screenshot_test: ok");
|
|
return g_failures ? 1 : 0;
|
|
}
|