diff --git a/docs/modernization/build-inventory.md b/docs/modernization/build-inventory.md index 0f20605..acbaa83 100644 --- a/docs/modernization/build-inventory.md +++ b/docs/modernization/build-inventory.md @@ -92,7 +92,7 @@ Known local toolchain state: `pp_paint_renderer`, `pp_ui_core`, `pano_cli`, and their current headless test binaries, including foundation binary-stream/event/logging/task queue coverage, PNG metadata and - decode, PPI header/layout/non-finite opacity rejection, settings document, document + 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/ stroke-alpha-blend/stroke spacing/stroke stress/stroke-script coverage, diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index d12e32e..209d615 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -316,7 +316,8 @@ PNG IHDR metadata parsing, PPI header/project byte-layout/body-summary recognition, layer/frame indexing, dirty-face PNG payload metadata validation, asset-level RGBA PNG payload decoding, and a pure typed settings document model, with -corrupt/truncated/unsupported, non-finite opacity, extreme-dimension, and key/value limit tests. +corrupt/truncated/unsupported, non-finite opacity, unsupported blend-mode, +extreme-dimension, and key/value limit tests. `pp_paint` has started with pure brush parameter validation/stamp evaluation, CPU reference math for the five current final RGBA shader blend modes plus the shader-style stroke-alpha blend modes used by pattern/dual-brush mixing, and deterministic diff --git a/src/assets/ppi_header.cpp b/src/assets/ppi_header.cpp index 1270d36..830afe2 100644 --- a/src/assets/ppi_header.cpp +++ b/src/assets/ppi_header.cpp @@ -383,6 +383,11 @@ pp::foundation::Result parse_ppi_body_impl( pp::foundation::Status::invalid_argument("PPI layer boolean field is invalid")); } + if (blend_mode.value() > 4U) { + return pp::foundation::Result::failure( + pp::foundation::Status::out_of_range("PPI layer blend mode is outside the supported range")); + } + if (index != nullptr) { layer_summary.blend_mode = blend_mode.value(); layer_summary.alpha_locked = alpha_locked.value() != 0U; diff --git a/tests/assets/ppi_header_tests.cpp b/tests/assets/ppi_header_tests.cpp index 1481dbf..bb8fb67 100644 --- a/tests/assets/ppi_header_tests.cpp +++ b/tests/assets/ppi_header_tests.cpp @@ -395,10 +395,14 @@ void rejects_invalid_project_body_summaries(pp::tests::Harness& h) ppi_header_size + (128U * 128U * 4U) + 20U, std::numeric_limits::quiet_NaN()); + auto bad_blend_mode = minimal_project(); + write_u32_at(bad_blend_mode, ppi_header_size + (128U * 128U * 4U) + 31U, 99U); + const auto truncated_result = parse_ppi_project_summary(truncated); const auto mismatched_frames_result = parse_ppi_project_summary(mismatched_frames); const auto bad_layer_name_result = parse_ppi_project_summary(bad_layer_name); const auto non_finite_opacity_result = parse_ppi_project_summary(non_finite_opacity); + const auto bad_blend_mode_result = parse_ppi_project_summary(bad_blend_mode); PP_EXPECT(h, !truncated_result.ok()); PP_EXPECT(h, truncated_result.status().code == StatusCode::out_of_range); @@ -408,6 +412,8 @@ void rejects_invalid_project_body_summaries(pp::tests::Harness& h) PP_EXPECT(h, bad_layer_name_result.status().code == StatusCode::out_of_range); PP_EXPECT(h, !non_finite_opacity_result.ok()); PP_EXPECT(h, non_finite_opacity_result.status().code == StatusCode::invalid_argument); + PP_EXPECT(h, !bad_blend_mode_result.ok()); + PP_EXPECT(h, bad_blend_mode_result.status().code == StatusCode::out_of_range); } void creates_minimal_project_for_roundtrip_load(pp::tests::Harness& h)