载入中...
搜索中...
未找到
SourceTiff.cpp
浏览该文件的文档.
1#include "asset/SourceTiff.h"
2#include <zlib.h>
3#include <algorithm>
4#include <array>
5#include <limits>
6#include <map>
7
8namespace eve::asset::detail {
9namespace {
10struct Invalid {};
11struct Unsupported {};
12struct Reader {
13 std::span<const std::uint8_t> bytes;
14 bool little;
15 std::uint32_t read(std::size_t at, unsigned count) const {
16 if (at > bytes.size() || count > bytes.size() - at) throw Invalid{};
17 std::uint32_t result = 0;
18 for (unsigned i = 0; i < count; ++i)
19 result |= std::uint32_t(bytes[at + i]) << ((little ? i : count - 1 - i) * 8);
20 return result;
21 }
22};
23struct Tag {
24 std::uint32_t type, count, offset;
25};
26
27std::vector<std::uint8_t> lzw(std::span<const std::uint8_t> source, std::size_t expected) {
28 std::array<std::uint16_t, 4096> prefix{};
29 std::array<std::uint8_t, 4096> suffix{}, stack{};
30 std::vector<std::uint8_t> out;
31 out.reserve(expected);
32 std::size_t bit = 0;
33 unsigned width = 9, next = 258, previous = 4096;
34 std::uint8_t first = 0;
35 bool cleared = false;
36 while (true) {
37 if (bit > source.size() * 8 || width > source.size() * 8 - bit) throw Invalid{};
38 unsigned code = 0;
39 for (unsigned i = 0; i < width; ++i, ++bit) code = (code << 1) | ((source[bit / 8] >> (7 - bit % 8)) & 1);
40 if (code == 256) {
41 width = 9;
42 next = 258;
43 previous = 4096;
44 cleared = true;
45 continue;
46 }
47 if (!cleared) throw Invalid{};
48 if (code == 257) {
49 if (out.size() != expected) throw Invalid{};
50 return out;
51 }
52 const unsigned original = code;
53 std::size_t length = 0;
54 if (code == next && previous != 4096) {
55 stack[length++] = first;
56 code = previous;
57 } else if (code >= next)
58 throw Invalid{};
59 while (code >= 258) {
60 if (code >= next || length >= stack.size()) throw Invalid{};
61 stack[length++] = suffix[code];
62 code = prefix[code];
63 }
64 if (code >= 256 || length >= stack.size()) throw Invalid{};
65 first = std::uint8_t(code);
66 stack[length++] = first;
67 if (length > expected - out.size()) throw Invalid{};
68 while (length) out.push_back(stack[--length]);
69 if (previous != 4096 && next < 4096) {
70 prefix[next] = std::uint16_t(previous);
71 suffix[next] = first;
72 ++next;
73 // TIFF uses early-change code widths, unlike GIF's late-change LZW.
74 if (width < 12 && next == (1u << width) - 1) ++width;
75 }
76 previous = original;
77 }
78}
79} // namespace
80
81Result<TiffPixels> decodeTiffRgba8(std::span<const std::uint8_t> bytes, std::uint64_t maximumDecodedBytes) {
82 try {
83 if (bytes.size() < 8 || !((bytes[0] == 'I' && bytes[1] == 'I') || (bytes[0] == 'M' && bytes[1] == 'M')))
84 throw Invalid{};
85 Reader r{bytes, bytes[0] == 'I'};
86 if (r.read(2, 2) != 42) throw Unsupported{};
87 const auto ifd = r.read(4, 4), count = r.read(ifd, 2);
88 if (ifd < 8 || count > 4096 || std::uint64_t(ifd) + 2 + 12ull * count + 4 > bytes.size()) throw Invalid{};
89 std::map<unsigned, Tag> tags;
90 for (unsigned i = 0; i < count; ++i) {
91 const auto at = ifd + 2 + i * 12, id = r.read(at, 2), type = r.read(at + 2, 2), n = r.read(at + 4, 4);
92 unsigned size = type == 1 || type == 2 || type == 6 || type == 7 ? 1
93 : type == 3 || type == 8 ? 2
94 : type == 4 || type == 9 || type == 11 ? 4
95 : type == 5 || type == 10 || type == 12 ? 8
96 : 0;
97 if (!size) throw Unsupported{};
98 const auto offset = std::uint64_t(n) * size <= 4 ? at + 8 : r.read(at + 8, 4);
99 if (offset > bytes.size() || std::uint64_t(n) * size > bytes.size() - offset) throw Invalid{};
100 if (!tags.emplace(id, Tag{type, n, offset}).second) throw Invalid{};
101 }
102 if (r.read(ifd + 2 + 12 * count, 4) != 0) throw Unsupported{};
103 auto values = [&](unsigned id, std::vector<std::uint32_t> fallback = {}) {
104 const auto it = tags.find(id);
105 if (it == tags.end()) return fallback;
106 const auto& t = it->second;
107 if ((t.type != 3 && t.type != 4) || t.count > maximumDecodedBytes / 4) throw Invalid{};
108 std::vector<std::uint32_t> result;
109 result.reserve(t.count);
110 const auto step = t.type == 3 ? 2 : 4;
111 for (unsigned i = 0; i < t.count; ++i) result.push_back(r.read(t.offset + std::size_t(i) * step, step));
112 return result;
113 };
114 auto scalar = [&](unsigned id, unsigned fallback) {
115 auto v = values(id, {fallback});
116 if (v.size() != 1) throw Invalid{};
117 return v[0];
118 };
119 const auto w = scalar(256, 0), h = scalar(257, 0), channels = scalar(277, 1), compression = scalar(259, 1);
120 const auto rows = scalar(278, UINT32_MAX), orientation = scalar(274, 1), predictor = scalar(317, 1);
121 if (!w || !h || !rows || maximumDecodedBytes < 24 || std::uint64_t(w) * h > (maximumDecodedBytes - 24) / 4)
122 throw Invalid{};
123 if (scalar(262, 0) != 2 || scalar(284, 1) != 1 || (channels != 3 && channels != 4) || orientation < 1 ||
124 orientation > 4 || (predictor != 1 && predictor != 2) || scalar(266, 1) != 1)
125 throw Unsupported{};
126 auto bits = values(258), format = values(339, {1}), extra = values(338);
127 if (bits.size() != channels || (bits[0] != 8 && bits[0] != 16) ||
128 (format.size() != 1 && format.size() != channels) ||
129 !std::all_of(bits.begin(), bits.end(), [&](auto b) { return b == bits[0]; }) ||
130 !std::all_of(format.begin(), format.end(), [](auto f) { return f == 1; }))
131 throw Unsupported{};
132 if (channels == 4 && (extra.size() != 1 || (extra[0] != 0 && extra[0] != 2))) throw Unsupported{};
133 if (channels == 3 && !extra.empty()) throw Unsupported{};
134 if (compression != 1 && compression != 5 && compression != 8 && compression != 32946) throw Unsupported{};
135 auto offsets = values(273), sizes = values(279);
136 if (offsets.size() != (std::uint64_t(h) + rows - 1) / rows || sizes.size() != offsets.size()) throw Invalid{};
137 TiffPixels out{w, h, std::vector<std::uint8_t>(std::size_t(w) * h * 4)};
138 const unsigned sampleBytes = bits[0] / 8;
139 for (std::size_t strip = 0; strip < offsets.size(); ++strip) {
140 const auto start = strip * rows, lines = std::min<std::size_t>(rows, h - start);
141 const std::uint64_t decodedSize = std::uint64_t(w) * lines * channels * sampleBytes;
142 if (decodedSize > maximumDecodedBytes || decodedSize > std::numeric_limits<uLongf>::max() ||
143 offsets[strip] > bytes.size() || sizes[strip] > bytes.size() - offsets[strip])
144 throw Invalid{};
145 auto source = bytes.subspan(offsets[strip], sizes[strip]);
146 std::vector<std::uint8_t> raw;
147 if (compression == 1) {
148 if (source.size() != decodedSize) throw Invalid{};
149 raw.assign(source.begin(), source.end());
150 } else if (compression == 5)
151 raw = lzw(source, std::size_t(decodedSize));
152 else {
153 raw.resize(std::size_t(decodedSize));
154 uLongf length = static_cast<uLongf>(decodedSize);
155 uLong input = static_cast<uLong>(source.size());
156 if (uncompress2(raw.data(), &length, source.data(), &input) != Z_OK || length != decodedSize ||
157 input != source.size())
158 throw Invalid{};
159 }
160 Reader pixels{raw, r.little};
161 const auto rowSamples = std::size_t(w) * channels;
162 std::array<unsigned, 4> previous{};
163 for (std::size_t y = 0; y < lines; ++y) {
164 previous.fill(0);
165 for (std::size_t x = 0; x < w; ++x) {
166 const auto dstX = (orientation == 2 || orientation == 3) ? w - 1 - x : x;
167 const auto srcY = start + y, dstY = orientation >= 3 ? h - 1 - srcY : srcY;
168 const auto dst = (dstY * w + dstX) * 4;
169 for (unsigned c = 0; c < channels; ++c) {
170 auto v = pixels.read((y * rowSamples + x * channels + c) * sampleBytes, sampleBytes);
171 if (predictor == 2) v = (v + previous[c]) & (sampleBytes == 1 ? 255 : 65535);
172 previous[c] = v;
173 out.rgba[dst + c] = std::uint8_t(sampleBytes == 1 ? v : (v + 128) / 257);
174 }
175 if (channels == 3) out.rgba[dst + 3] = 255;
176 }
177 }
178 }
179 return Result<TiffPixels>::success(std::move(out));
180 } catch (const Unsupported&) {
183 "unsupported TIFF layout; requires unsigned 8/16-bit RGB(A) strips with raw, LZW or Deflate compression",
184 {}, {}, "asset.image.tiff"));
185 } catch (const Invalid&) {
187 DiagnosticCode::ParseError, "malformed TIFF or decoded image budget exceeded", {}, {}, "asset.image.tiff"));
188 }
189}
190} // namespace eve::asset::detail
Duration start
float w
Definition AnimClip.cpp:738
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
std::map< std::string, std::vector< Key >, std::less<> > channels
float length
Definition CaveMesh.cpp:94
std::map< std::string, Var > values
std::uint64_t decodedSize
EvpackChunkInput input
Definition Evpack.cpp:170
int rows
DiagnosticCode code
glm::vec3 n
Definition Grass.cpp:63
double r
float v
std::int32_t c
std::int32_t first
int h
std::uint32_t width
size_t offset
std::uint64_t bytes
MeleePoint3 b
Definition MeleeHit.cpp:41
graphics::Canvas * previous
float f
std::string id
Definition PlayHost.cpp:108
float t
uint8_t * pixels
bool little
std::uint32_t count
float step
Definition TreeMesh.cpp:314
float size
Definition TreeMesh.cpp:156
std::vector< std::string_view > lines
const UnitySourceAsset & source
std::size_t at
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
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).
constexpr HexDirection next(HexDirection d) noexcept
The next direction clockwise (NW wraps to NE).
Definition HexMetrics.h:76
Owning top-down RGB(A) TIFF pixels; unspecified fourth samples remain alpha data.
Definition SourceTiff.h:10