Reject duplicate document snapshot payloads
This commit is contained in:
@@ -96,7 +96,7 @@ Known local toolchain state:
|
||||
including foundation binary-stream/event/logging/task queue coverage, PNG metadata and
|
||||
decode, PPI header/layout/non-finite opacity and blend-mode rejection, settings document, document
|
||||
snapshot/per-layer-frame/move/duration/face-pixel/PPI export coverage,
|
||||
snapshot-embedded face-payload rejection, paint brush/final-blend/
|
||||
snapshot-embedded duplicate/invalid face-payload and selection-mask rejection, paint brush/final-blend/
|
||||
stroke-alpha-blend/stroke spacing/stroke stress/stroke-script coverage,
|
||||
renderer shader descriptor and OpenGL capability coverage, UI
|
||||
color parsing, and layout XML parse coverage.
|
||||
|
||||
@@ -330,7 +330,8 @@ construction, per-layer frame metadata, layer metadata operations, frame
|
||||
move/duration queries, renderer-free RGBA8 cube-face payload storage,
|
||||
renderer-free alpha8 selection-mask storage, PPI image import/export, and
|
||||
layer/frame/undo-redo history invariant tests. Snapshot construction validates
|
||||
embedded face-pixel payload bounds and byte counts.
|
||||
embedded face-pixel payload bounds, byte counts, duplicate face payloads, and
|
||||
duplicate selection masks.
|
||||
`pp_renderer_api` has started with renderer-neutral
|
||||
texture/readback descriptors and validation tests. `pp_paint_renderer` has
|
||||
started with deterministic CPU layer compositing over renderer extents using
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "document/document.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
@@ -238,11 +239,18 @@ namespace {
|
||||
std::uint32_t document_height) noexcept
|
||||
{
|
||||
for (const auto& frame : frames) {
|
||||
std::array<bool, cube_face_count> seen_faces {};
|
||||
for (const auto& pixels : frame.face_pixels) {
|
||||
const auto pixels_status = validate_face_pixels(pixels, document_width, document_height);
|
||||
if (!pixels_status.ok()) {
|
||||
return pixels_status;
|
||||
}
|
||||
|
||||
if (seen_faces[pixels.face_index]) {
|
||||
return pp::foundation::Status::invalid_argument(
|
||||
"snapshot contains duplicate face pixel payloads for a cube face");
|
||||
}
|
||||
seen_faces[pixels.face_index] = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,11 +374,19 @@ pp::foundation::Result<CanvasDocument> CanvasDocument::create_from_snapshot(Docu
|
||||
document.frames_.push_back(frame_config);
|
||||
}
|
||||
|
||||
std::array<bool, cube_face_count> seen_selection_masks {};
|
||||
for (const auto& mask : config.selection_masks) {
|
||||
const auto mask_status = validate_selection_mask(mask, document.width_, document.height_);
|
||||
if (!mask_status.ok()) {
|
||||
return pp::foundation::Result<CanvasDocument>::failure(mask_status);
|
||||
}
|
||||
|
||||
if (seen_selection_masks[mask.face_index]) {
|
||||
return pp::foundation::Result<CanvasDocument>::failure(
|
||||
pp::foundation::Status::invalid_argument(
|
||||
"snapshot contains duplicate selection masks for a cube face"));
|
||||
}
|
||||
seen_selection_masks[mask.face_index] = true;
|
||||
document.selection_masks_.push_back(mask);
|
||||
}
|
||||
|
||||
|
||||
@@ -344,6 +344,29 @@ void rejects_invalid_snapshot_face_pixels(pp::tests::Harness& h)
|
||||
},
|
||||
},
|
||||
};
|
||||
const AnimationFrame duplicate_face_frames[] {
|
||||
{
|
||||
.duration_ms = 100,
|
||||
.face_pixels = {
|
||||
LayerFacePixels {
|
||||
.face_index = 0,
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = 1,
|
||||
.height = 1,
|
||||
.rgba8 = { 1, 2, 3, 4 },
|
||||
},
|
||||
LayerFacePixels {
|
||||
.face_index = 0,
|
||||
.x = 1,
|
||||
.y = 0,
|
||||
.width = 1,
|
||||
.height = 1,
|
||||
.rgba8 = { 5, 6, 7, 8 },
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const DocumentLayerConfig bad_byte_count_layers[] {
|
||||
{
|
||||
.name = "Ink",
|
||||
@@ -356,6 +379,12 @@ void rejects_invalid_snapshot_face_pixels(pp::tests::Harness& h)
|
||||
.frames = outside_frames,
|
||||
},
|
||||
};
|
||||
const DocumentLayerConfig duplicate_face_layers[] {
|
||||
{
|
||||
.name = "Ink",
|
||||
.frames = duplicate_face_frames,
|
||||
},
|
||||
};
|
||||
const DocumentLayerConfig layers[] {
|
||||
{
|
||||
.name = "Ink",
|
||||
@@ -384,6 +413,13 @@ void rejects_invalid_snapshot_face_pixels(pp::tests::Harness& h)
|
||||
.frames = bad_byte_count_frames,
|
||||
.selection_masks = {},
|
||||
});
|
||||
const auto duplicate_face_payload = CanvasDocument::create_from_snapshot(DocumentSnapshotConfig {
|
||||
.width = 64,
|
||||
.height = 64,
|
||||
.layers = duplicate_face_layers,
|
||||
.frames = frames,
|
||||
.selection_masks = {},
|
||||
});
|
||||
|
||||
PP_EXPECT(h, !bad_layer_payload.ok());
|
||||
PP_EXPECT(h, bad_layer_payload.status().code == StatusCode::invalid_argument);
|
||||
@@ -391,6 +427,8 @@ void rejects_invalid_snapshot_face_pixels(pp::tests::Harness& h)
|
||||
PP_EXPECT(h, outside_layer_payload.status().code == StatusCode::out_of_range);
|
||||
PP_EXPECT(h, !bad_root_payload.ok());
|
||||
PP_EXPECT(h, bad_root_payload.status().code == StatusCode::invalid_argument);
|
||||
PP_EXPECT(h, !duplicate_face_payload.ok());
|
||||
PP_EXPECT(h, duplicate_face_payload.status().code == StatusCode::invalid_argument);
|
||||
}
|
||||
|
||||
void manages_animation_frames_and_duration(pp::tests::Harness& h)
|
||||
@@ -649,6 +687,19 @@ void rejects_invalid_selection_masks(pp::tests::Harness& h)
|
||||
SelectionMask { .face_index = 0, .x = 0, .y = 0, .width = 2, .height = 1, .alpha8 = { 1 } });
|
||||
const auto missing_clear = document.clear_selection_mask(2);
|
||||
const auto bad_clear_face = document.clear_selection_mask(6);
|
||||
const DocumentLayerConfig layers[] { { .name = "Ink", .frames = {} } };
|
||||
const AnimationFrame frames[] { { .duration_ms = 100, .face_pixels = {} } };
|
||||
const SelectionMask duplicate_masks[] {
|
||||
{ .face_index = 2, .x = 0, .y = 0, .width = 1, .height = 1, .alpha8 = { 1 } },
|
||||
{ .face_index = 2, .x = 1, .y = 0, .width = 1, .height = 1, .alpha8 = { 2 } },
|
||||
};
|
||||
const auto duplicate_snapshot_masks = CanvasDocument::create_from_snapshot(DocumentSnapshotConfig {
|
||||
.width = 64,
|
||||
.height = 32,
|
||||
.layers = layers,
|
||||
.frames = frames,
|
||||
.selection_masks = duplicate_masks,
|
||||
});
|
||||
|
||||
PP_EXPECT(h, !bad_face.ok());
|
||||
PP_EXPECT(h, bad_face.code == StatusCode::out_of_range);
|
||||
@@ -662,6 +713,8 @@ void rejects_invalid_selection_masks(pp::tests::Harness& h)
|
||||
PP_EXPECT(h, missing_clear.code == StatusCode::out_of_range);
|
||||
PP_EXPECT(h, !bad_clear_face.ok());
|
||||
PP_EXPECT(h, bad_clear_face.code == StatusCode::out_of_range);
|
||||
PP_EXPECT(h, !duplicate_snapshot_masks.ok());
|
||||
PP_EXPECT(h, duplicate_snapshot_masks.status().code == StatusCode::invalid_argument);
|
||||
PP_EXPECT(h, document.selection_mask_payload_count() == 0U);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user