载入中...
搜索中...
未找到
ImageImporter.cpp
浏览该文件的文档.
2
4#include "asset/SourceTga.h"
5#include "asset/SourceTiff.h"
7
8namespace eve::asset_import {
9namespace {
10
11struct ImageInfo {
12 std::string encoding;
13 std::uint32_t width = 0;
14 std::uint32_t height = 0;
15};
16
17std::uint32_t big32(const std::uint8_t* bytes) {
18 return (std::uint32_t(bytes[0]) << 24) | (std::uint32_t(bytes[1]) << 16) |
19 (std::uint32_t(bytes[2]) << 8) | bytes[3];
20}
21
22Result<ImageInfo> inspectImage(std::span<const std::uint8_t> bytes, std::uint64_t maximumDecodedBytes) {
23 if (bytes.size() >= 18 && bytes[1] == 0 && (bytes[2] == 2 || bytes[2] == 10) &&
24 (bytes[16] == 24 || bytes[16] == 32)) {
25 auto decoded = asset::detail::decodeTgaRgba8(bytes, maximumDecodedBytes);
26 if (!decoded) return Result<ImageInfo>::failure(decoded.status());
27 return Result<ImageInfo>::success({"tga", decoded.value().width, decoded.value().height});
28 }
29 if (bytes.size() >= 4 && ((bytes[0] == 'I' && bytes[1] == 'I') || (bytes[0] == 'M' && bytes[1] == 'M'))) {
30 auto decoded = asset::detail::decodeTiffRgba8(bytes, maximumDecodedBytes);
31 if (!decoded) return Result<ImageInfo>::failure(decoded.status());
32 return Result<ImageInfo>::success({"tiff", decoded.value().width, decoded.value().height});
33 }
34 static constexpr std::uint8_t png[] = {0x89, 'P', 'N', 'G', 0x0d, 0x0a, 0x1a, 0x0a};
35 if (bytes.size() >= 24 && std::equal(std::begin(png), std::end(png), bytes.begin()) &&
36 std::string_view(reinterpret_cast<const char*>(bytes.data() + 12), 4) == "IHDR") {
37 ImageInfo info{"png", big32(bytes.data() + 16), big32(bytes.data() + 20)};
38 if (info.width == 0 || info.height == 0)
40 DiagnosticCode::ParseError, "PNG dimensions must be positive", {}, {}, "asset.import"));
41 return Result<ImageInfo>::success(std::move(info));
42 }
43 if (bytes.size() >= 4 && bytes[0] == 0xff && bytes[1] == 0xd8) {
44 std::size_t cursor = 2;
45 while (cursor + 4 <= bytes.size()) {
46 if (bytes[cursor] != 0xff)
48 DiagnosticCode::ParseError, "JPEG marker stream is malformed", {}, {}, "asset.import"));
49 while (cursor < bytes.size() && bytes[cursor] == 0xff) ++cursor;
50 if (cursor >= bytes.size()) break;
51 const std::uint8_t marker = bytes[cursor++];
52 if (marker == 0xd9 || marker == 0xda) break;
53 if (marker >= 0xd0 && marker <= 0xd7) continue;
54 if (cursor + 2 > bytes.size()) break;
55 const std::uint16_t length = (std::uint16_t(bytes[cursor]) << 8) | bytes[cursor + 1];
56 if (length < 2 || length > bytes.size() - cursor) break;
57 const bool startOfFrame = (marker >= 0xc0 && marker <= 0xc3) ||
58 (marker >= 0xc5 && marker <= 0xc7) ||
59 (marker >= 0xc9 && marker <= 0xcb) ||
60 (marker >= 0xcd && marker <= 0xcf);
61 if (startOfFrame) {
62 if (length < 7) break;
63 ImageInfo info{"jpeg", (std::uint32_t(bytes[cursor + 5]) << 8) | bytes[cursor + 6],
64 (std::uint32_t(bytes[cursor + 3]) << 8) | bytes[cursor + 4]};
65 if (info.width == 0 || info.height == 0) break;
66 return Result<ImageInfo>::success(std::move(info));
67 }
68 cursor += length;
69 }
71 DiagnosticCode::ParseError, "JPEG dimensions are missing or malformed", {}, {}, "asset.import"));
72 }
74 DiagnosticCode::Unsupported, "image importer supports PNG, JPEG and admitted TIFF/TGA encoded sources", {}, {},
75 "asset.import"));
76}
77
78} // namespace
79
81 if (request.encodedBytes.empty() || request.sourceName.empty() || request.usage.empty())
83 "image source name, bytes and usage are required",
84 {}, {}, "asset.import"));
85 if (request.encodedBytes.size() > request.limits.maximumSourceBytes ||
89 DiagnosticCode::InvalidArgument, "image import budget is exceeded", {}, {}, "asset.import"));
90 auto info = inspectImage(request.encodedBytes, request.limits.maximumDecodedBytes);
91 if (!info) return Result<PreparedAssetImport>::failure(info.status());
92 const std::uint64_t pixels = std::uint64_t(info.value().width) * info.value().height;
95 DiagnosticCode::InvalidArgument, "decoded image budget is exceeded", {}, {}, "asset.import"));
96 auto manifestResult = detail::baseManifest(request.package, "eve.image");
97 if (!manifestResult) return Result<PreparedAssetImport>::failure(manifestResult.status());
98 auto manifest = std::move(manifestResult).takeValue();
99 const PersistentId id = request.package.packageId.child("image:default");
100 auto reference = detail::assetRef(id);
102 const std::string root = "assets/" + id.format() + "/";
103 const std::string blobPath = root + "source." + info.value().encoding;
104 Value::Object definition;
105 definition["schema"] = Value("eve.image");
106 definition["schemaVersion"] = Value(std::int64_t(3));
107 definition["mipCount"] = Value(std::int64_t(1));
108 definition["width"] = Value(static_cast<std::int64_t>(info.value().width));
109 definition["height"] = Value(static_cast<std::int64_t>(info.value().height));
110 definition["encoding"] = Value(info.value().encoding);
111 definition["color"] = Value(Value::Object{
112 {"primaries", Value("srgb")},
113 {"transfer", Value(request.colorSpace == ImageColorSpace::Srgb ? "srgb" : "linear")},
114 });
115 definition["usage"] = Value(request.usage);
116 definition["rowOrientation"] = Value("top-down");
117 definition["blob"] = Value(blobPath);
118 auto encodedDefinition = Value(std::move(definition)).toJson();
119 if (!encodedDefinition) return Result<PreparedAssetImport>::failure(encodedDefinition.status());
120 const std::string definitionText = std::move(encodedDefinition).takeValue();
121 if (info.value().encoding == "png") {
122 auto verified = asset::cookCanonicalImageRgba8(
123 std::span<const std::uint8_t>(
124 reinterpret_cast<const std::uint8_t*>(definitionText.data()),
125 definitionText.size()),
127 if (!verified) return Result<PreparedAssetImport>::failure(verified.status());
128 }
129 const std::string definitionPath = root + "asset.json";
130 manifest.assets.push_back(
131 {std::move(reference).takeValue(),
132 "eve.image",
133 SchemaVersion(3),
134 definitionPath,
135 detail::sha256(std::span<const std::uint8_t>(reinterpret_cast<const std::uint8_t*>(definitionText.data()),
136 definitionText.size())),
137 {"image", "usage:" + request.usage}});
138 auto entryRef = detail::assetRef(id);
139 if (!entryRef) return Result<PreparedAssetImport>::failure(entryRef.status());
140 manifest.entrypoints.emplace("default", std::move(entryRef).takeValue());
141 manifest.provenance["sourceName"] = Value(request.sourceName);
142 manifest.provenance["sourceHash"] = Value(detail::sha256(request.encodedBytes));
143 PreparedAssetImport result;
144 result.manifest = std::move(manifest);
145 result.entries.push_back({definitionPath, {definitionText.begin(), definitionText.end()}});
146 result.entries.push_back({blobPath, request.encodedBytes});
147 auto mappingRef = detail::assetRef(id);
148 if (!mappingRef) return Result<PreparedAssetImport>::failure(mappingRef.status());
149 result.sourceMappings.push_back({request.sourceName, std::move(mappingRef).takeValue()});
150 result.findings.push_back({request.sourceName, "Image." + info.value().encoding,
152 "encoded source and explicit colour semantics retained"});
153 auto report = detail::finalizeImportReport(
154 result, request.package, "image", info.value().encoding,
155 {{"colorSpace", Value(request.colorSpace == ImageColorSpace::Srgb ? "srgb" : "linear")},
156 {"usage", Value(request.usage)}});
157 if (!report) return Result<PreparedAssetImport>::failure(report.status());
158 return Result<PreparedAssetImport>::success(std::move(result));
159}
160
161} // namespace eve::asset_import
int root
Definition AnimSmr.cpp:119
Tool-side image and glTF importers producing canonical .eva candidates.
AuthorityStoreHandleRef reference
Definition Authority.cpp:24
Safe source-image conversion to runtime EVIMG payloads.
float length
Definition CaveMesh.cpp:94
const GltfImportRequest & request
std::string encoding
std::uint32_t height
std::uint32_t width
std::uint64_t bytes
uint8_t * pixels
std::size_t cursor
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
The canonical owning dynamic value used by data-facing protocols.
Definition Value.h:31
Result< std::string > toJson() const
Serialize this value as deterministic compact JSON.
Definition Value.cpp:67
std::map< std::string, Value > Object
Definition Value.h:34
Id128 public API.
Definition Identity.h:112
Id128 child(std::string_view role) const noexcept
Derives a deterministic child UUID for a hierarchical identity.
Definition Identity.h:192
Result< TgaPixels > decodeTgaRgba8(std::span< const std::uint8_t > b, std::uint64_t budget)
Decode true-color 24/32-bit raw or RLE TGA with bounded allocation.
Definition SourceTga.cpp:7
Result< TiffPixels > decodeTiffRgba8(std::span< const std::uint8_t > bytes, std::uint64_t maximumDecodedBytes)
Decode bounded strip-based unsigned RGB TIFF (8/16 bits, raw/LZW/Deflate).
std::string sha256(std::span< const std::uint8_t > bytes)
Sha 256.
EVENGINE_API_PLATFORM Result< void > finalizeImportReport(PreparedAssetImport &prepared, const ImportPackageIdentity &package, std::string_view sourceEngine, std::string_view sourceVersion, Value::Object options={})
Append the deterministic mandatory import report and bind it from provenance.
Result< asset::EvaManifest > baseManifest(const ImportPackageIdentity &package, std::string_view importer)
Base manifest.
Result< AssetRef > assetRef(PersistentId id)
Asset ref.
EVENGINE_API_PLATFORM Result< PreparedAssetImport > prepareImageImport(const ImageImportRequest &request)
Decode image metadata and prepare one canonical eve.image source asset.
Result< CookedCanonicalImage > cookCanonicalImageRgba8(std::span< const std::uint8_t > definition, std::span< const std::uint8_t > encodedSource, std::uint64_t maximumDecodedBytes)
Decode image/2 base images or image/3 explicit linear RGBA8 mip chains into bounded EVIMG.
detail::StrongUint64< detail::SchemaVersionTag > SchemaVersion
Persistent data-format version; not a runtime replacement generation.
Untrusted encoded image input and its explicit semantic settings.
Owning, validated source archive candidate not yet published to disk/database.
std::vector< ImportFinding > findings
std::vector< asset::EvaArchiveEntry > entries
std::vector< ImportSourceMapping > sourceMappings
glm::uvec4 info