Add document frame render automation

This commit is contained in:
2026-06-05 17:30:44 +02:00
parent d4dad133ea
commit 7c6c5f3e36
9 changed files with 354 additions and 12 deletions

View File

@@ -727,6 +727,12 @@ struct SimulateDocumentExportArgs {
std::uint32_t height = 32;
};
struct SimulateDocumentRenderArgs {
std::uint32_t width = 64;
std::uint32_t height = 32;
std::uint32_t frame = 0;
};
struct SimulateDocumentHistoryArgs {
std::uint32_t width = 64;
std::uint32_t height = 32;
@@ -2431,6 +2437,7 @@ void print_help()
<< " save-project --path FILE --width N --height N [--layer-name NAME] [--layer-opacity N] [--blend-mode N] [--alpha-locked] [--hidden] [--layers N] [--frames N] [--frame-duration-ms N] [--include-test-face-payload] [--payload-layer N] [--payload-frame N]\n"
<< " simulate-document-edits [--width N] [--height N]\n"
<< " simulate-document-export [--width N] [--height N]\n"
<< " simulate-document-render [--width N] [--height N] [--frame N]\n"
<< " simulate-document-history [--width N] [--height N] [--history N]\n"
<< " simulate-app-session [--no-canvas] [--new-document] [--unsaved] [--close-prompt-open] [--save-intent save|save-as|save-version|save-dirty-version]\n"
<< " simulate-blend\n"
@@ -10600,6 +10607,132 @@ int simulate_document_export(int argc, char** argv)
return 0;
}
pp::foundation::Status parse_simulate_document_render_args(
int argc,
char** argv,
SimulateDocumentRenderArgs& args)
{
for (int i = 2; i < argc; ++i) {
const std::string_view key(argv[i]);
if (key == "--width" || key == "--height" || key == "--frame") {
if (i + 1 >= argc) {
return pp::foundation::Status::invalid_argument("missing value for option");
}
const auto value = pp::foundation::parse_u32(argv[++i]);
if (!value) {
return value.status();
}
if (key == "--width") {
args.width = value.value();
} else if (key == "--height") {
args.height = value.value();
} else {
args.frame = value.value();
}
} else {
return pp::foundation::Status::invalid_argument("unknown option");
}
}
if (args.width == 0 || args.height == 0) {
return pp::foundation::Status::invalid_argument("width and height must be greater than zero");
}
if (args.width < 8 || args.height < 8) {
return pp::foundation::Status::out_of_range("width and height must be at least 8 for render simulation");
}
return pp::foundation::Status::success();
}
[[nodiscard]] bool pixel_differs_from_clear(pp::paint::Rgba pixel, pp::paint::Rgba clear) noexcept
{
return pixel.r != clear.r || pixel.g != clear.g || pixel.b != clear.b || pixel.a != clear.a;
}
int simulate_document_render(int argc, char** argv)
{
SimulateDocumentRenderArgs args;
const auto status = parse_simulate_document_render_args(argc, argv, args);
if (!status.ok()) {
print_error("simulate-document-render", status.message);
return 2;
}
const auto document_result = create_export_sample_document(args.width, args.height);
if (!document_result) {
print_error("simulate-document-render", document_result.status().message);
return 2;
}
constexpr pp::paint::Rgba clear_color {};
const auto composited = pp::paint_renderer::composite_document_frame(
pp::paint_renderer::DocumentFrameCompositeRequest {
.document = &document_result.value(),
.frame_index = args.frame,
.clear_color = clear_color,
});
if (!composited) {
print_error("simulate-document-render", composited.status().message);
return 2;
}
const auto& document = document_result.value();
const auto& result = composited.value();
std::cout << "{\"ok\":true,\"command\":\"simulate-document-render\""
<< ",\"source\":{\"width\":" << document.width()
<< ",\"height\":" << document.height()
<< ",\"layers\":" << document.layers().size()
<< ",\"frames\":" << document.frames().size()
<< ",\"facePayloads\":" << document.face_pixel_payload_count()
<< "},\"render\":{\"frame\":" << args.frame
<< ",\"width\":" << result.extent.width
<< ",\"height\":" << result.extent.height
<< ",\"faces\":" << result.faces.size()
<< ",\"visitedLayers\":" << result.visited_layer_count
<< ",\"compositedLayerFaces\":" << result.composited_layer_face_count
<< ",\"facePayloads\":" << result.face_payload_count
<< ",\"faceSummaries\":[";
for (std::size_t face_index = 0; face_index < result.faces.size(); ++face_index) {
const auto& face = result.faces[face_index];
std::size_t non_clear_pixels = 0;
std::size_t first_non_clear = face.pixels.size();
for (std::size_t pixel_index = 0; pixel_index < face.pixels.size(); ++pixel_index) {
if (!pixel_differs_from_clear(face.pixels[pixel_index], clear_color)) {
continue;
}
if (first_non_clear == face.pixels.size()) {
first_non_clear = pixel_index;
}
++non_clear_pixels;
}
if (face_index != 0U) {
std::cout << ",";
}
std::cout << "{\"face\":" << face_index
<< ",\"pixels\":" << face.pixels.size()
<< ",\"payloads\":" << face.face_payload_count
<< ",\"nonClearPixels\":" << non_clear_pixels;
if (first_non_clear != face.pixels.size()) {
const auto& pixel = face.pixels[first_non_clear];
std::cout << ",\"firstNonClear\":{\"index\":" << first_non_clear
<< ",\"x\":" << (first_non_clear % result.extent.width)
<< ",\"y\":" << (first_non_clear / result.extent.width)
<< ",\"r\":" << pixel.r
<< ",\"g\":" << pixel.g
<< ",\"b\":" << pixel.b
<< ",\"a\":" << pixel.a
<< "}";
}
std::cout << "}";
}
std::cout << "]}}\n";
return 0;
}
pp::foundation::Status parse_simulate_document_history_args(
int argc,
char** argv,
@@ -11736,6 +11869,10 @@ int main(int argc, char** argv)
return simulate_document_export(argc, argv);
}
if (command == "simulate-document-render") {
return simulate_document_render(argc, argv);
}
if (command == "simulate-document-history") {
return simulate_document_history(argc, argv);
}