Decode PPI face payloads
This commit is contained in:
@@ -86,6 +86,16 @@ add_test(NAME pp_assets_image_metadata_tests COMMAND pp_assets_image_metadata_te
|
||||
set_tests_properties(pp_assets_image_metadata_tests PROPERTIES
|
||||
LABELS "assets;desktop-fast")
|
||||
|
||||
add_executable(pp_assets_image_pixels_tests
|
||||
assets/image_pixels_tests.cpp)
|
||||
target_link_libraries(pp_assets_image_pixels_tests PRIVATE
|
||||
pp_assets
|
||||
pp_test_harness)
|
||||
|
||||
add_test(NAME pp_assets_image_pixels_tests COMMAND pp_assets_image_pixels_tests)
|
||||
set_tests_properties(pp_assets_image_pixels_tests PROPERTIES
|
||||
LABELS "assets;desktop-fast")
|
||||
|
||||
add_executable(pp_assets_ppi_header_tests
|
||||
assets/ppi_header_tests.cpp)
|
||||
target_link_libraries(pp_assets_ppi_header_tests PRIVATE
|
||||
|
||||
67
tests/assets/image_pixels_tests.cpp
Normal file
67
tests/assets/image_pixels_tests.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#include "assets/image_pixels.h"
|
||||
#include "test_harness.h"
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <span>
|
||||
|
||||
using pp::assets::decode_png_rgba8;
|
||||
using pp::foundation::StatusCode;
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr std::array<std::byte, 68> transparent_png_1x1 {
|
||||
std::byte { 0x89 }, std::byte { 0x50 }, std::byte { 0x4e }, std::byte { 0x47 },
|
||||
std::byte { 0x0d }, std::byte { 0x0a }, std::byte { 0x1a }, std::byte { 0x0a },
|
||||
std::byte { 0x00 }, std::byte { 0x00 }, std::byte { 0x00 }, std::byte { 0x0d },
|
||||
std::byte { 0x49 }, std::byte { 0x48 }, std::byte { 0x44 }, std::byte { 0x52 },
|
||||
std::byte { 0x00 }, std::byte { 0x00 }, std::byte { 0x00 }, std::byte { 0x01 },
|
||||
std::byte { 0x00 }, std::byte { 0x00 }, std::byte { 0x00 }, std::byte { 0x01 },
|
||||
std::byte { 0x08 }, std::byte { 0x06 }, std::byte { 0x00 }, std::byte { 0x00 },
|
||||
std::byte { 0x00 }, std::byte { 0x1f }, std::byte { 0x15 }, std::byte { 0xc4 },
|
||||
std::byte { 0x89 }, std::byte { 0x00 }, std::byte { 0x00 }, std::byte { 0x00 },
|
||||
std::byte { 0x0b }, std::byte { 0x49 }, std::byte { 0x44 }, std::byte { 0x41 },
|
||||
std::byte { 0x54 }, std::byte { 0x78 }, std::byte { 0x9c }, std::byte { 0x63 },
|
||||
std::byte { 0x60 }, std::byte { 0x00 }, std::byte { 0x02 }, std::byte { 0x00 },
|
||||
std::byte { 0x00 }, std::byte { 0x05 }, std::byte { 0x00 }, std::byte { 0x01 },
|
||||
std::byte { 0x7a }, std::byte { 0x5e }, std::byte { 0xab }, std::byte { 0x3f },
|
||||
std::byte { 0x00 }, std::byte { 0x00 }, std::byte { 0x00 }, std::byte { 0x00 },
|
||||
std::byte { 0x49 }, std::byte { 0x45 }, std::byte { 0x4e }, std::byte { 0x44 },
|
||||
std::byte { 0xae }, std::byte { 0x42 }, std::byte { 0x60 }, std::byte { 0x82 },
|
||||
};
|
||||
|
||||
void decodes_png_to_rgba8_pixels(pp::tests::Harness& h)
|
||||
{
|
||||
const auto image = decode_png_rgba8(transparent_png_1x1);
|
||||
|
||||
PP_EXPECT(h, image.ok());
|
||||
PP_EXPECT(h, image.value().width == 1U);
|
||||
PP_EXPECT(h, image.value().height == 1U);
|
||||
PP_EXPECT(h, image.value().pixels.size() == 4U);
|
||||
PP_EXPECT(h, image.value().pixels[0] == 0U);
|
||||
PP_EXPECT(h, image.value().pixels[1] == 0U);
|
||||
PP_EXPECT(h, image.value().pixels[2] == 0U);
|
||||
PP_EXPECT(h, image.value().pixels[3] == 0U);
|
||||
}
|
||||
|
||||
void rejects_corrupt_png_payload(pp::tests::Harness& h)
|
||||
{
|
||||
auto corrupt = transparent_png_1x1;
|
||||
corrupt[0] = std::byte { 0x00 };
|
||||
|
||||
const auto image = decode_png_rgba8(corrupt);
|
||||
|
||||
PP_EXPECT(h, !image.ok());
|
||||
PP_EXPECT(h, image.status().code == StatusCode::invalid_argument);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
pp::tests::Harness harness;
|
||||
harness.run("decodes_png_to_rgba8_pixels", decodes_png_to_rgba8_pixels);
|
||||
harness.run("rejects_corrupt_png_payload", rejects_corrupt_png_payload);
|
||||
return harness.finish();
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <vector>
|
||||
|
||||
using pp::assets::parse_ppi_header;
|
||||
using pp::assets::decode_ppi_project_images;
|
||||
using pp::assets::parse_ppi_project_index;
|
||||
using pp::assets::parse_ppi_project_summary;
|
||||
using pp::assets::parse_ppi_project_layout;
|
||||
@@ -119,6 +120,29 @@ std::vector<std::byte> png_ihdr_payload(
|
||||
return bytes;
|
||||
}
|
||||
|
||||
std::vector<std::byte> transparent_png_1x1()
|
||||
{
|
||||
return {
|
||||
std::byte { 0x89 }, std::byte { 0x50 }, std::byte { 0x4e }, std::byte { 0x47 },
|
||||
std::byte { 0x0d }, std::byte { 0x0a }, std::byte { 0x1a }, std::byte { 0x0a },
|
||||
std::byte { 0x00 }, std::byte { 0x00 }, std::byte { 0x00 }, std::byte { 0x0d },
|
||||
std::byte { 0x49 }, std::byte { 0x48 }, std::byte { 0x44 }, std::byte { 0x52 },
|
||||
std::byte { 0x00 }, std::byte { 0x00 }, std::byte { 0x00 }, std::byte { 0x01 },
|
||||
std::byte { 0x00 }, std::byte { 0x00 }, std::byte { 0x00 }, std::byte { 0x01 },
|
||||
std::byte { 0x08 }, std::byte { 0x06 }, std::byte { 0x00 }, std::byte { 0x00 },
|
||||
std::byte { 0x00 }, std::byte { 0x1f }, std::byte { 0x15 }, std::byte { 0xc4 },
|
||||
std::byte { 0x89 }, std::byte { 0x00 }, std::byte { 0x00 }, std::byte { 0x00 },
|
||||
std::byte { 0x0b }, std::byte { 0x49 }, std::byte { 0x44 }, std::byte { 0x41 },
|
||||
std::byte { 0x54 }, std::byte { 0x78 }, std::byte { 0x9c }, std::byte { 0x63 },
|
||||
std::byte { 0x60 }, std::byte { 0x00 }, std::byte { 0x02 }, std::byte { 0x00 },
|
||||
std::byte { 0x00 }, std::byte { 0x05 }, std::byte { 0x00 }, std::byte { 0x01 },
|
||||
std::byte { 0x7a }, std::byte { 0x5e }, std::byte { 0xab }, std::byte { 0x3f },
|
||||
std::byte { 0x00 }, std::byte { 0x00 }, std::byte { 0x00 }, std::byte { 0x00 },
|
||||
std::byte { 0x49 }, std::byte { 0x45 }, std::byte { 0x4e }, std::byte { 0x44 },
|
||||
std::byte { 0xae }, std::byte { 0x42 }, std::byte { 0x60 }, std::byte { 0x82 },
|
||||
};
|
||||
}
|
||||
|
||||
std::vector<std::byte> minimal_project()
|
||||
{
|
||||
auto bytes = valid_header();
|
||||
@@ -127,7 +151,10 @@ std::vector<std::byte> minimal_project()
|
||||
return bytes;
|
||||
}
|
||||
|
||||
std::vector<std::byte> project_with_single_face_payload(std::vector<std::byte> payload)
|
||||
std::vector<std::byte> project_with_single_face_payload(
|
||||
std::vector<std::byte> payload,
|
||||
std::uint32_t dirty_width = 8,
|
||||
std::uint32_t dirty_height = 4)
|
||||
{
|
||||
auto bytes = valid_header();
|
||||
bytes.resize(ppi_header_size + (128U * 128U * 4U), std::byte { 0 });
|
||||
@@ -149,8 +176,8 @@ std::vector<std::byte> project_with_single_face_payload(std::vector<std::byte> p
|
||||
append_u32(bytes, 1);
|
||||
append_u32(bytes, 2);
|
||||
append_u32(bytes, 3);
|
||||
append_u32(bytes, 10);
|
||||
append_u32(bytes, 7);
|
||||
append_u32(bytes, 2 + dirty_width);
|
||||
append_u32(bytes, 3 + dirty_height);
|
||||
append_u32(bytes, static_cast<std::uint32_t>(payload.size()));
|
||||
bytes.insert(bytes.end(), payload.begin(), payload.end());
|
||||
|
||||
@@ -289,6 +316,31 @@ void validates_dirty_face_png_payload_metadata(pp::tests::Harness& h)
|
||||
PP_EXPECT(h, summary.value().body.compressed_face_bytes == 33U);
|
||||
}
|
||||
|
||||
void decodes_dirty_face_png_payloads(pp::tests::Harness& h)
|
||||
{
|
||||
const auto project = project_with_single_face_payload(transparent_png_1x1(), 1, 1);
|
||||
const auto decoded = decode_ppi_project_images(project);
|
||||
|
||||
PP_EXPECT(h, decoded.ok());
|
||||
PP_EXPECT(h, decoded.value().faces.size() == 1U);
|
||||
PP_EXPECT(h, decoded.value().faces[0].layer_index == 0U);
|
||||
PP_EXPECT(h, decoded.value().faces[0].frame_index == 0U);
|
||||
PP_EXPECT(h, decoded.value().faces[0].face_index == 0U);
|
||||
PP_EXPECT(h, decoded.value().faces[0].image.width == 1U);
|
||||
PP_EXPECT(h, decoded.value().faces[0].image.height == 1U);
|
||||
PP_EXPECT(h, decoded.value().faces[0].image.pixels.size() == 4U);
|
||||
PP_EXPECT(h, decoded.value().faces[0].image.pixels[3] == 0U);
|
||||
}
|
||||
|
||||
void rejects_metadata_only_payload_when_decoding_pixels(pp::tests::Harness& h)
|
||||
{
|
||||
const auto project = project_with_single_face_payload(png_ihdr_payload(8, 4));
|
||||
const auto decoded = decode_ppi_project_images(project);
|
||||
|
||||
PP_EXPECT(h, !decoded.ok());
|
||||
PP_EXPECT(h, decoded.status().code == StatusCode::invalid_argument);
|
||||
}
|
||||
|
||||
void rejects_invalid_dirty_face_png_payloads(pp::tests::Harness& h)
|
||||
{
|
||||
auto mismatched_dimensions = project_with_single_face_payload(png_ihdr_payload(7, 4));
|
||||
@@ -345,6 +397,8 @@ int main()
|
||||
harness.run("parses_minimal_project_body_summary", parses_minimal_project_body_summary);
|
||||
harness.run("indexes_project_layers_frames_and_faces", indexes_project_layers_frames_and_faces);
|
||||
harness.run("validates_dirty_face_png_payload_metadata", validates_dirty_face_png_payload_metadata);
|
||||
harness.run("decodes_dirty_face_png_payloads", decodes_dirty_face_png_payloads);
|
||||
harness.run("rejects_metadata_only_payload_when_decoding_pixels", rejects_metadata_only_payload_when_decoding_pixels);
|
||||
harness.run("rejects_invalid_dirty_face_png_payloads", rejects_invalid_dirty_face_png_payloads);
|
||||
harness.run("rejects_invalid_project_body_summaries", rejects_invalid_project_body_summaries);
|
||||
return harness.finish();
|
||||
|
||||
Reference in New Issue
Block a user