Add document export file automation
This commit is contained in:
@@ -51,6 +51,12 @@ struct SaveProjectArgs {
|
||||
std::uint32_t payload_frame = 0;
|
||||
};
|
||||
|
||||
struct SaveDocumentProjectArgs {
|
||||
std::string path;
|
||||
std::uint32_t width = 64;
|
||||
std::uint32_t height = 32;
|
||||
};
|
||||
|
||||
struct InspectImageArgs {
|
||||
std::string path;
|
||||
};
|
||||
@@ -214,6 +220,7 @@ void print_help()
|
||||
<< " load-project --path FILE\n"
|
||||
<< " parse-layout --path FILE\n"
|
||||
<< " record-render [--width N] [--height N]\n"
|
||||
<< " save-document-project --path FILE [--width N] [--height N]\n"
|
||||
<< " 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"
|
||||
@@ -321,6 +328,175 @@ int create_document(int argc, char** argv)
|
||||
return 0;
|
||||
}
|
||||
|
||||
pp::foundation::Result<pp::document::CanvasDocument> create_export_sample_document(
|
||||
std::uint32_t width,
|
||||
std::uint32_t height)
|
||||
{
|
||||
const std::vector<std::uint8_t> red_pixel { 255, 0, 0, 255 };
|
||||
const std::vector<std::uint8_t> blue_pixel { 0, 0, 255, 128 };
|
||||
const pp::document::AnimationFrame root_frames[] {
|
||||
{ .duration_ms = 100, .face_pixels = {} },
|
||||
{ .duration_ms = 250, .face_pixels = {} },
|
||||
};
|
||||
const pp::document::AnimationFrame base_frames[] {
|
||||
{
|
||||
.duration_ms = 100,
|
||||
.face_pixels = {
|
||||
pp::document::LayerFacePixels {
|
||||
.face_index = 0,
|
||||
.x = 2,
|
||||
.y = 3,
|
||||
.width = 1,
|
||||
.height = 1,
|
||||
.rgba8 = red_pixel,
|
||||
},
|
||||
},
|
||||
},
|
||||
{ .duration_ms = 250, .face_pixels = {} },
|
||||
};
|
||||
const pp::document::AnimationFrame paint_frames[] {
|
||||
{
|
||||
.duration_ms = 333,
|
||||
.face_pixels = {
|
||||
pp::document::LayerFacePixels {
|
||||
.face_index = 5,
|
||||
.x = 4,
|
||||
.y = 5,
|
||||
.width = 1,
|
||||
.height = 1,
|
||||
.rgba8 = blue_pixel,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const pp::document::DocumentLayerConfig layers[] {
|
||||
{
|
||||
.name = "Base",
|
||||
.visible = true,
|
||||
.alpha_locked = false,
|
||||
.opacity = 1.0F,
|
||||
.blend_mode = pp::paint::BlendMode::normal,
|
||||
.frames = std::span<const pp::document::AnimationFrame>(base_frames, 2),
|
||||
},
|
||||
{
|
||||
.name = "Paint",
|
||||
.visible = false,
|
||||
.alpha_locked = true,
|
||||
.opacity = 0.5F,
|
||||
.blend_mode = pp::paint::BlendMode::overlay,
|
||||
.frames = std::span<const pp::document::AnimationFrame>(paint_frames, 1),
|
||||
},
|
||||
};
|
||||
|
||||
return pp::document::CanvasDocument::create_from_snapshot(
|
||||
pp::document::DocumentSnapshotConfig {
|
||||
.width = width,
|
||||
.height = height,
|
||||
.layers = std::span<const pp::document::DocumentLayerConfig>(layers, 2),
|
||||
.frames = std::span<const pp::document::AnimationFrame>(root_frames, 2),
|
||||
.selection_masks = {},
|
||||
});
|
||||
}
|
||||
|
||||
pp::foundation::Status parse_save_document_project_args(
|
||||
int argc,
|
||||
char** argv,
|
||||
SaveDocumentProjectArgs& args)
|
||||
{
|
||||
for (int i = 2; i < argc; ++i) {
|
||||
const std::string_view key(argv[i]);
|
||||
if (key == "--path") {
|
||||
if (i + 1 >= argc) {
|
||||
return pp::foundation::Status::invalid_argument("missing value for option");
|
||||
}
|
||||
args.path = argv[++i];
|
||||
} else if (key == "--width" || key == "--height") {
|
||||
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 {
|
||||
args.height = value.value();
|
||||
}
|
||||
} else {
|
||||
return pp::foundation::Status::invalid_argument("unknown option");
|
||||
}
|
||||
}
|
||||
|
||||
if (args.path.empty()) {
|
||||
return pp::foundation::Status::invalid_argument("path must not be empty");
|
||||
}
|
||||
|
||||
if (args.width < 8 || args.height < 8) {
|
||||
return pp::foundation::Status::out_of_range("width and height must be at least 8 for document export");
|
||||
}
|
||||
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
int save_document_project(int argc, char** argv)
|
||||
{
|
||||
SaveDocumentProjectArgs args;
|
||||
const auto status = parse_save_document_project_args(argc, argv, args);
|
||||
if (!status.ok()) {
|
||||
print_error("save-document-project", status.message);
|
||||
return 2;
|
||||
}
|
||||
|
||||
const auto document = create_export_sample_document(args.width, args.height);
|
||||
if (!document) {
|
||||
print_error("save-document-project", document.status().message);
|
||||
return 2;
|
||||
}
|
||||
|
||||
const auto exported = pp::document::export_ppi_project_document(document.value());
|
||||
if (!exported) {
|
||||
print_error("save-document-project", exported.status().message);
|
||||
return 2;
|
||||
}
|
||||
|
||||
std::ofstream stream(args.path, std::ios::binary);
|
||||
if (!stream) {
|
||||
print_error("save-document-project", "project file could not be opened for writing");
|
||||
return 2;
|
||||
}
|
||||
|
||||
const auto& bytes = exported.value();
|
||||
stream.write(reinterpret_cast<const char*>(bytes.data()), static_cast<std::streamsize>(bytes.size()));
|
||||
if (!stream) {
|
||||
print_error("save-document-project", "project file could not be written");
|
||||
return 2;
|
||||
}
|
||||
|
||||
const auto decoded = pp::assets::decode_ppi_project_images(bytes);
|
||||
if (!decoded) {
|
||||
print_error("save-document-project", decoded.status().message);
|
||||
return 2;
|
||||
}
|
||||
|
||||
std::cout << "{\"ok\":true,\"command\":\"save-document-project\""
|
||||
<< ",\"path\":\"" << json_escape(args.path) << "\""
|
||||
<< ",\"format\":\"ppi\""
|
||||
<< ",\"bytes\":" << bytes.size()
|
||||
<< ",\"document\":{\"width\":" << document.value().width()
|
||||
<< ",\"height\":" << document.value().height()
|
||||
<< ",\"layers\":" << document.value().layers().size()
|
||||
<< ",\"frames\":" << document.value().frames().size()
|
||||
<< ",\"facePayloads\":" << document.value().face_pixel_payload_count()
|
||||
<< "},\"export\":{\"dirtyFaces\":" << decoded.value().project.body.summary.dirty_face_count
|
||||
<< ",\"rgbaFacePayloads\":" << decoded.value().project.body.summary.rgba_face_payload_count
|
||||
<< ",\"compressedBytes\":" << decoded.value().project.body.summary.compressed_face_bytes
|
||||
<< "}}\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
pp::foundation::Status parse_save_project_args(int argc, char** argv, SaveProjectArgs& args)
|
||||
{
|
||||
for (int i = 2; i < argc; ++i) {
|
||||
@@ -1518,70 +1694,7 @@ int simulate_document_export(int argc, char** argv)
|
||||
return 2;
|
||||
}
|
||||
|
||||
const std::vector<std::uint8_t> red_pixel { 255, 0, 0, 255 };
|
||||
const std::vector<std::uint8_t> blue_pixel { 0, 0, 255, 128 };
|
||||
const pp::document::AnimationFrame root_frames[] {
|
||||
{ .duration_ms = 100, .face_pixels = {} },
|
||||
{ .duration_ms = 250, .face_pixels = {} },
|
||||
};
|
||||
const pp::document::AnimationFrame base_frames[] {
|
||||
{
|
||||
.duration_ms = 100,
|
||||
.face_pixels = {
|
||||
pp::document::LayerFacePixels {
|
||||
.face_index = 0,
|
||||
.x = 2,
|
||||
.y = 3,
|
||||
.width = 1,
|
||||
.height = 1,
|
||||
.rgba8 = red_pixel,
|
||||
},
|
||||
},
|
||||
},
|
||||
{ .duration_ms = 250, .face_pixels = {} },
|
||||
};
|
||||
const pp::document::AnimationFrame paint_frames[] {
|
||||
{
|
||||
.duration_ms = 333,
|
||||
.face_pixels = {
|
||||
pp::document::LayerFacePixels {
|
||||
.face_index = 5,
|
||||
.x = 4,
|
||||
.y = 5,
|
||||
.width = 1,
|
||||
.height = 1,
|
||||
.rgba8 = blue_pixel,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const pp::document::DocumentLayerConfig layers[] {
|
||||
{
|
||||
.name = "Base",
|
||||
.visible = true,
|
||||
.alpha_locked = false,
|
||||
.opacity = 1.0F,
|
||||
.blend_mode = pp::paint::BlendMode::normal,
|
||||
.frames = std::span<const pp::document::AnimationFrame>(base_frames, 2),
|
||||
},
|
||||
{
|
||||
.name = "Paint",
|
||||
.visible = false,
|
||||
.alpha_locked = true,
|
||||
.opacity = 0.5F,
|
||||
.blend_mode = pp::paint::BlendMode::overlay,
|
||||
.frames = std::span<const pp::document::AnimationFrame>(paint_frames, 1),
|
||||
},
|
||||
};
|
||||
|
||||
const auto document_result = pp::document::CanvasDocument::create_from_snapshot(
|
||||
pp::document::DocumentSnapshotConfig {
|
||||
.width = args.width,
|
||||
.height = args.height,
|
||||
.layers = std::span<const pp::document::DocumentLayerConfig>(layers, 2),
|
||||
.frames = std::span<const pp::document::AnimationFrame>(root_frames, 2),
|
||||
.selection_masks = {},
|
||||
});
|
||||
const auto document_result = create_export_sample_document(args.width, args.height);
|
||||
if (!document_result) {
|
||||
print_error("simulate-document-export", document_result.status().message);
|
||||
return 2;
|
||||
@@ -2030,6 +2143,10 @@ int main(int argc, char** argv)
|
||||
return record_render(argc, argv);
|
||||
}
|
||||
|
||||
if (command == "save-document-project") {
|
||||
return save_document_project(argc, argv);
|
||||
}
|
||||
|
||||
if (command == "save-project") {
|
||||
return save_project(argc, argv);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user