载入中...
搜索中...
未找到
EvpackImageDecoder.cpp
浏览该文件的文档.
3
4#include <algorithm>
5
6namespace eve::asset {
7namespace {
8std::uint32_t little32(std::span<const std::uint8_t> bytes, std::size_t offset) {
9 return std::uint32_t(bytes[offset]) | (std::uint32_t(bytes[offset + 1]) << 8) |
10 (std::uint32_t(bytes[offset + 2]) << 16) | (std::uint32_t(bytes[offset + 3]) << 24);
11}
12} // namespace
13
15 const EvpackCapabilities& capabilities,
17 auto payload = reader.read(image, "eve.image/3", capabilities, limits.maximumDecodedBytes);
18 if (!payload && payload.error()->code() == DiagnosticCode::TypeMismatch)
19 payload = reader.read(image, "eve.image/2", capabilities, limits.maximumDecodedBytes);
21 const RuntimeAssetChunk* bulk = nullptr;
22 for (const auto& chunk : payload.value().chunks) {
23 if (chunk.kind != EvpackChunkKind::Bulk) continue;
24 if (bulk)
26 Diagnostic::error(DiagnosticCode::ParseError, "eve.image must contain exactly one bulk chunk", {}, {},
27 "asset.image.decode"));
28 bulk = &chunk;
29 }
30 const bool explicitSchema = payload.value().schemaVersion.value() == 3;
31 const std::uint8_t magic[] = {'E', 'V', 'I', 'M', 'G', 0, std::uint8_t(explicitSchema ? 2 : 1), 0};
32 const std::size_t headerBytes = explicitSchema ? 28 : 24;
33 if (!bulk || bulk->bytes.size() < headerBytes ||
34 !std::equal(std::begin(magic), std::end(magic), bulk->bytes.begin()))
36 DiagnosticCode::ParseError, "EVIMG bulk header is missing or invalid", {}, {}, "asset.image.decode"));
37 const std::uint32_t width = little32(bulk->bytes, 8), height = little32(bulk->bytes, 12);
38 const std::uint32_t flags = little32(bulk->bytes, 16);
39 const std::uint32_t levels = explicitSchema ? little32(bulk->bytes, 20) : 1;
40 const std::uint32_t reserved = little32(bulk->bytes, explicitSchema ? 24 : 20);
41 const std::uint64_t pixelCount = std::uint64_t(width) * height;
42 if (!width || !height || width > limits.maximumDimension || height > limits.maximumDimension ||
43 pixelCount > limits.maximumPixels || flags > 1 || reserved || !levels || levels > 32 ||
44 (explicitSchema && flags != 0))
46 Diagnostic::error(DiagnosticCode::InvalidArgument, "EVIMG dimensions, flags, or byte size are invalid", {},
47 {}, "asset.image.decode"));
48 std::uint64_t expected = headerBytes;
49 std::uint32_t fullCount = 0, w = width, h = height;
50 for (;;) {
51 if (fullCount < levels) {
52 const std::uint64_t levelPixels = std::uint64_t(w) * h;
53 if (expected > bulk->bytes.size() || levelPixels > (bulk->bytes.size() - expected) / 4)
55 DiagnosticCode::InvalidArgument, "EVIMG mip bytes are truncated", {}, {}, "asset.image.decode"));
56 expected += levelPixels * 4;
57 }
58 ++fullCount;
59 if (w == 1 && h == 1) break;
60 w = std::max(w / 2, 1u);
61 h = std::max(h / 2, 1u);
62 }
63 if ((levels != 1 && levels != fullCount) || expected != bulk->bytes.size())
65 "EVIMG mip count or packed byte size is invalid",
66 {}, {}, "asset.image.decode"));
67 if (explicitSchema) {
68 const RuntimeAssetChunk* definition = nullptr;
69 for (const auto& chunk : payload.value().chunks) {
70 if (chunk.kind != EvpackChunkKind::Definition) continue;
71 if (definition)
73 DiagnosticCode::ParseError, "image contains duplicate definitions", {}, {}, "asset.image.decode"));
74 definition = &chunk;
75 }
76 if (!definition)
78 DiagnosticCode::ParseError, "image definition is absent", {}, {}, "asset.image.decode"));
79 auto decoded = decodeRuntimeDefinition(definition->bytes);
80 if (!decoded) return Result<DecodedEvpackImage>::failure(decoded.status());
81 const auto* object = decoded.value().getIf<Value::Object>();
82 auto integerMatches = [&](const char* key, std::uint32_t value) {
83 if (!object) return false;
84 const auto found = object->find(key);
85 return found != object->end() && found->second.isInt64() && found->second.asInt() == value;
86 };
87 auto stringMatches = [&](const char* key, const char* value) {
88 if (!object) return false;
89 const auto found = object->find(key);
90 return found != object->end() && found->second.isString() && found->second.asString() == value;
91 };
92 if (!integerMatches("schemaVersion", 3) || !integerMatches("width", width) ||
93 !integerMatches("height", height) || !integerMatches("mipCount", levels) ||
94 !stringMatches("schema", "eve.image") || !stringMatches("encoding", "rgba8") ||
95 !stringMatches("blob", "chunk:1") || bulk->chunkId != 1)
97 DiagnosticCode::ParseError, "image definition and mip bulk disagree", {}, {}, "asset.image.decode"));
98 const auto color = object->find("color");
99 const auto* colorObject = color == object->end() ? nullptr : color->second.getIf<Value::Object>();
100 if (!colorObject || !colorObject->contains("transfer") || !colorObject->at("transfer").isString() ||
101 colorObject->at("transfer").asString() != "linear")
103 DiagnosticCode::ParseError, "explicit image transfer must be linear", {}, {}, "asset.image.decode"));
104 }
105 DecodedEvpackImage result{image, width, height, levels, flags == 1};
106 result.pixels.assign(bulk->bytes.begin() + std::ptrdiff_t(headerBytes), bulk->bytes.end());
107 result.variant = std::move(payload).takeValue().variant;
108 return Result<DecodedEvpackImage>::success(std::move(result));
109}
110
111} // namespace eve::asset
double value
Value::Object payload
float w
Definition AnimClip.cpp:738
Backend-neutral canonical EVIMG decoding.
std::uint32_t key
vk::UniqueImage image
int h
std::uint32_t height
std::uint32_t width
size_t offset
std::uint64_t bytes
bool found
JSON-free versioned runtime metadata codec.
const AssetImportLimits & limits
Stable asset identity backed by PersistentId.
Definition ResourceRef.h:53
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
std::map< std::string, Value > Object
Definition Value.h:34
Immutable reader retaining an admitted pack by shared ownership.
Result< RuntimeAssetPayload > read(const AssetRef &asset, std::string_view expectedType, const EvpackCapabilities &capabilities, std::uint64_t maximumDecodedBytes) const
Resolve, type-check, variant-select and decode a canonical runtime asset.
std::uint32_t little32(std::span< const std::uint8_t > bytes, std::size_t offset)
Read a uint32 after the caller has bounded the byte range.
Result< Value > decodeRuntimeDefinition(std::span< const std::uint8_t > bytes, const RuntimeDefinitionLimits &limits)
Decode a bounded EVDEF\0\1 metadata tree without a JSON parser.
Result< DecodedEvpackImage > decodeEvpackImage(const EvpackResourceReader &reader, const AssetRef &image, const EvpackCapabilities &capabilities, const EvpackImageDecodeLimits &limits)
Decode and validate canonical eve.image/3 data with eve.image/2 compatibility.
@ TypeMismatch
A stable reference resolved to a different canonical domain type.
Owning validated RGBA8 mip chain independent of a graphics backend.
Actual runtime device capabilities used for variant selection.
Definition Evpack.h:92
Bounds applied before allocating decoded image bytes.
One decoded runtime chunk with its admitted semantic metadata.
std::vector< std::uint8_t > bytes
glm::vec4 color