/src/openexr/src/lib/OpenEXR/ImfTiledMisc.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | // |
2 | | // SPDX-License-Identifier: BSD-3-Clause |
3 | | // Copyright (c) Contributors to the OpenEXR Project. |
4 | | // |
5 | | |
6 | | //----------------------------------------------------------------------------- |
7 | | // |
8 | | // Miscellaneous stuff related to tiled files |
9 | | // |
10 | | //----------------------------------------------------------------------------- |
11 | | |
12 | | #include "Iex.h" |
13 | | #include <ImfChannelList.h> |
14 | | #include <ImfHeader.h> |
15 | | #include <ImfMisc.h> |
16 | | #include <ImfTileDescription.h> |
17 | | #include <ImfTiledMisc.h> |
18 | | #include <algorithm> |
19 | | #include <limits> |
20 | | |
21 | | OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER |
22 | | |
23 | | using IMATH_NAMESPACE::Box2i; |
24 | | using IMATH_NAMESPACE::V2i; |
25 | | |
26 | | int |
27 | | levelSize (int min, int max, int l, LevelRoundingMode rmode) |
28 | 0 | { |
29 | 0 | if (l < 0) throw IEX_NAMESPACE::ArgExc ("Argument not in valid range."); |
30 | | |
31 | 0 | int a = max - min + 1; |
32 | 0 | int b = (1 << l); |
33 | 0 | int size = a / b; |
34 | |
|
35 | 0 | if (rmode == ROUND_UP && size * b < a) size += 1; |
36 | |
|
37 | 0 | return std::max (size, 1); |
38 | 0 | } |
39 | | |
40 | | Box2i |
41 | | dataWindowForLevel ( |
42 | | const TileDescription& tileDesc, |
43 | | int minX, |
44 | | int maxX, |
45 | | int minY, |
46 | | int maxY, |
47 | | int lx, |
48 | | int ly) |
49 | 0 | { |
50 | 0 | V2i levelMin = V2i (minX, minY); |
51 | |
|
52 | 0 | V2i levelMax = |
53 | 0 | levelMin + V2i ( |
54 | 0 | levelSize (minX, maxX, lx, tileDesc.roundingMode) - 1, |
55 | 0 | levelSize (minY, maxY, ly, tileDesc.roundingMode) - 1); |
56 | |
|
57 | 0 | return Box2i (levelMin, levelMax); |
58 | 0 | } |
59 | | |
60 | | Box2i |
61 | | dataWindowForTile ( |
62 | | const TileDescription& tileDesc, |
63 | | int minX, |
64 | | int maxX, |
65 | | int minY, |
66 | | int maxY, |
67 | | int dx, |
68 | | int dy, |
69 | | int lx, |
70 | | int ly) |
71 | 0 | { |
72 | 0 | V2i tileMin = V2i (minX + dx * tileDesc.xSize, minY + dy * tileDesc.ySize); |
73 | |
|
74 | 0 | int64_t tileMaxX = int64_t (tileMin[0]) + tileDesc.xSize - 1; |
75 | 0 | int64_t tileMaxY = int64_t (tileMin[1]) + tileDesc.ySize - 1; |
76 | |
|
77 | 0 | V2i levelMax = |
78 | 0 | dataWindowForLevel (tileDesc, minX, maxX, minY, maxY, lx, ly).max; |
79 | |
|
80 | 0 | V2i tileMax = V2i ( |
81 | 0 | std::min (tileMaxX, int64_t (levelMax[0])), |
82 | 0 | std::min (tileMaxY, int64_t (levelMax[1]))); |
83 | |
|
84 | 0 | return Box2i (tileMin, tileMax); |
85 | 0 | } |
86 | | |
87 | | size_t |
88 | | calculateBytesPerPixel (const Header& header) |
89 | 281k | { |
90 | 281k | const ChannelList& channels = header.channels (); |
91 | | |
92 | 281k | size_t bytesPerPixel = 0; |
93 | | |
94 | 743k | for (ChannelList::ConstIterator c = channels.begin (); c != channels.end (); |
95 | 462k | ++c) |
96 | 462k | { |
97 | 462k | bytesPerPixel += pixelTypeSize (c.channel ().type); |
98 | 462k | } |
99 | | |
100 | 281k | return bytesPerPixel; |
101 | 281k | } |
102 | | |
103 | | void |
104 | | calculateBytesPerLine ( |
105 | | const Header& header, |
106 | | char* sampleCountBase, |
107 | | int sampleCountXStride, |
108 | | int sampleCountYStride, |
109 | | int minX, |
110 | | int maxX, |
111 | | int minY, |
112 | | int maxY, |
113 | | std::vector<int>& xOffsets, |
114 | | std::vector<int>& yOffsets, |
115 | | std::vector<uint64_t>& bytesPerLine) |
116 | 0 | { |
117 | 0 | const ChannelList& channels = header.channels (); |
118 | |
|
119 | 0 | int pos = 0; |
120 | 0 | for (ChannelList::ConstIterator c = channels.begin (); c != channels.end (); |
121 | 0 | ++c, ++pos) |
122 | 0 | { |
123 | 0 | int xOffset = xOffsets[pos]; |
124 | 0 | int yOffset = yOffsets[pos]; |
125 | 0 | int i = 0; |
126 | 0 | for (int y = minY - yOffset; y <= maxY - yOffset; y++, i++) |
127 | 0 | for (int x = minX - xOffset; x <= maxX - xOffset; x++) |
128 | 0 | { |
129 | 0 | bytesPerLine[i] += sampleCount ( |
130 | 0 | sampleCountBase, |
131 | 0 | sampleCountXStride, |
132 | 0 | sampleCountYStride, |
133 | 0 | x, |
134 | 0 | y) * |
135 | 0 | pixelTypeSize (c.channel ().type); |
136 | 0 | } |
137 | 0 | } |
138 | 0 | } |
139 | | |
140 | | namespace |
141 | | { |
142 | | |
143 | | int |
144 | | floorLog2 (int x) |
145 | 0 | { |
146 | | // |
147 | | // For x > 0, floorLog2(y) returns floor(log(x)/log(2)). |
148 | | // |
149 | |
|
150 | 0 | int y = 0; |
151 | |
|
152 | 0 | while (x > 1) |
153 | 0 | { |
154 | 0 | y += 1; |
155 | 0 | x >>= 1; |
156 | 0 | } |
157 | |
|
158 | 0 | return y; |
159 | 0 | } |
160 | | |
161 | | int |
162 | | ceilLog2 (int x) |
163 | 0 | { |
164 | | // |
165 | | // For x > 0, ceilLog2(y) returns ceil(log(x)/log(2)). |
166 | | // |
167 | |
|
168 | 0 | int y = 0; |
169 | 0 | int r = 0; |
170 | |
|
171 | 0 | while (x > 1) |
172 | 0 | { |
173 | 0 | if (x & 1) r = 1; |
174 | |
|
175 | 0 | y += 1; |
176 | 0 | x >>= 1; |
177 | 0 | } |
178 | |
|
179 | 0 | return y + r; |
180 | 0 | } |
181 | | |
182 | | int |
183 | | roundLog2 (int x, LevelRoundingMode rmode) |
184 | 0 | { |
185 | 0 | return (rmode == ROUND_DOWN) ? floorLog2 (x) : ceilLog2 (x); |
186 | 0 | } |
187 | | |
188 | | int |
189 | | calculateNumXLevels ( |
190 | | const TileDescription& tileDesc, int minX, int maxX, int minY, int maxY) |
191 | 0 | { |
192 | 0 | int num = 0; |
193 | |
|
194 | 0 | switch (tileDesc.mode) |
195 | 0 | { |
196 | 0 | case ONE_LEVEL: num = 1; break; |
197 | | |
198 | 0 | case MIPMAP_LEVELS: |
199 | |
|
200 | 0 | { |
201 | 0 | int w = maxX - minX + 1; |
202 | 0 | int h = maxY - minY + 1; |
203 | 0 | num = roundLog2 (std::max (w, h), tileDesc.roundingMode) + 1; |
204 | 0 | } |
205 | 0 | break; |
206 | | |
207 | 0 | case RIPMAP_LEVELS: |
208 | |
|
209 | 0 | { |
210 | 0 | int w = maxX - minX + 1; |
211 | 0 | num = roundLog2 (w, tileDesc.roundingMode) + 1; |
212 | 0 | } |
213 | 0 | break; |
214 | | |
215 | 0 | default: throw IEX_NAMESPACE::ArgExc ("Unknown LevelMode format."); |
216 | 0 | } |
217 | | |
218 | 0 | return num; |
219 | 0 | } |
220 | | |
221 | | int |
222 | | calculateNumYLevels ( |
223 | | const TileDescription& tileDesc, int minX, int maxX, int minY, int maxY) |
224 | 0 | { |
225 | 0 | int num = 0; |
226 | |
|
227 | 0 | switch (tileDesc.mode) |
228 | 0 | { |
229 | 0 | case ONE_LEVEL: num = 1; break; |
230 | | |
231 | 0 | case MIPMAP_LEVELS: |
232 | |
|
233 | 0 | { |
234 | 0 | int w = maxX - minX + 1; |
235 | 0 | int h = maxY - minY + 1; |
236 | 0 | num = roundLog2 (std::max (w, h), tileDesc.roundingMode) + 1; |
237 | 0 | } |
238 | 0 | break; |
239 | | |
240 | 0 | case RIPMAP_LEVELS: |
241 | |
|
242 | 0 | { |
243 | 0 | int h = maxY - minY + 1; |
244 | 0 | num = roundLog2 (h, tileDesc.roundingMode) + 1; |
245 | 0 | } |
246 | 0 | break; |
247 | | |
248 | 0 | default: throw IEX_NAMESPACE::ArgExc ("Unknown LevelMode format."); |
249 | 0 | } |
250 | | |
251 | 0 | return num; |
252 | 0 | } |
253 | | |
254 | | void |
255 | | calculateNumTiles ( |
256 | | int* numTiles, |
257 | | int numLevels, |
258 | | int min, |
259 | | int max, |
260 | | int size, |
261 | | LevelRoundingMode rmode) |
262 | 0 | { |
263 | 0 | for (int i = 0; i < numLevels; i++) |
264 | 0 | { |
265 | | // use 64 bits to avoid int overflow if size is large. |
266 | 0 | uint64_t l = levelSize (min, max, i, rmode); |
267 | 0 | numTiles[i] = (l + size - 1) / size; |
268 | 0 | } |
269 | 0 | } |
270 | | |
271 | | } // namespace |
272 | | |
273 | | void |
274 | | precalculateTileInfo ( |
275 | | const TileDescription& tileDesc, |
276 | | int minX, |
277 | | int maxX, |
278 | | int minY, |
279 | | int maxY, |
280 | | int*& numXTiles, |
281 | | int*& numYTiles, |
282 | | int& numXLevels, |
283 | | int& numYLevels) |
284 | 0 | { |
285 | 0 | numXLevels = calculateNumXLevels (tileDesc, minX, maxX, minY, maxY); |
286 | 0 | numYLevels = calculateNumYLevels (tileDesc, minX, maxX, minY, maxY); |
287 | |
|
288 | 0 | numXTiles = new int[numXLevels]; |
289 | 0 | numYTiles = new int[numYLevels]; |
290 | |
|
291 | 0 | calculateNumTiles ( |
292 | 0 | numXTiles, |
293 | 0 | numXLevels, |
294 | 0 | minX, |
295 | 0 | maxX, |
296 | 0 | tileDesc.xSize, |
297 | 0 | tileDesc.roundingMode); |
298 | |
|
299 | 0 | calculateNumTiles ( |
300 | 0 | numYTiles, |
301 | 0 | numYLevels, |
302 | 0 | minY, |
303 | 0 | maxY, |
304 | 0 | tileDesc.ySize, |
305 | 0 | tileDesc.roundingMode); |
306 | 0 | } |
307 | | |
308 | | int |
309 | | getTiledChunkOffsetTableSize (const Header& header) |
310 | 0 | { |
311 | | // |
312 | | // Save the dataWindow information |
313 | | // |
314 | |
|
315 | 0 | const Box2i& dataWindow = header.dataWindow (); |
316 | | |
317 | | // |
318 | | // Precompute level and tile information. |
319 | | // |
320 | |
|
321 | 0 | int* numXTiles = nullptr; |
322 | 0 | int* numYTiles = nullptr; |
323 | 0 | int numXLevels; |
324 | 0 | int numYLevels; |
325 | 0 | try |
326 | 0 | { |
327 | 0 | precalculateTileInfo ( |
328 | 0 | header.tileDescription (), |
329 | 0 | dataWindow.min.x, |
330 | 0 | dataWindow.max.x, |
331 | 0 | dataWindow.min.y, |
332 | 0 | dataWindow.max.y, |
333 | 0 | numXTiles, |
334 | 0 | numYTiles, |
335 | 0 | numXLevels, |
336 | 0 | numYLevels); |
337 | | |
338 | | // |
339 | | // Calculate lineOffsetSize. |
340 | | // |
341 | 0 | uint64_t lineOffsetSize = 0; |
342 | 0 | const TileDescription& desc = header.tileDescription (); |
343 | 0 | switch (desc.mode) |
344 | 0 | { |
345 | 0 | case ONE_LEVEL: |
346 | 0 | case MIPMAP_LEVELS: |
347 | 0 | for (int i = 0; i < numXLevels; i++) |
348 | 0 | { |
349 | 0 | lineOffsetSize += static_cast<uint64_t> (numXTiles[i]) * |
350 | 0 | static_cast<uint64_t> (numYTiles[i]); |
351 | 0 | if (lineOffsetSize > static_cast<uint64_t> ( |
352 | 0 | std::numeric_limits<int>::max ())) |
353 | 0 | { |
354 | 0 | throw IEX_NAMESPACE::LogicExc ( |
355 | 0 | "Maximum number of tiles exceeded"); |
356 | 0 | } |
357 | 0 | } |
358 | 0 | break; |
359 | 0 | case RIPMAP_LEVELS: |
360 | 0 | for (int i = 0; i < numXLevels; i++) |
361 | 0 | { |
362 | 0 | for (int j = 0; j < numYLevels; j++) |
363 | 0 | { |
364 | 0 | lineOffsetSize += static_cast<uint64_t> (numXTiles[i]) * |
365 | 0 | static_cast<uint64_t> (numYTiles[j]); |
366 | 0 | if (lineOffsetSize > |
367 | 0 | static_cast<uint64_t> ( |
368 | 0 | std::numeric_limits<int>::max ())) |
369 | 0 | { |
370 | 0 | throw IEX_NAMESPACE::LogicExc ( |
371 | 0 | "Maximum number of tiles exceeded"); |
372 | 0 | } |
373 | 0 | } |
374 | 0 | } |
375 | 0 | break; |
376 | 0 | case NUM_LEVELMODES: |
377 | 0 | throw IEX_NAMESPACE::LogicExc ( |
378 | 0 | "Bad level mode getting chunk offset table size"); |
379 | 0 | } |
380 | 0 | delete[] numXTiles; |
381 | 0 | delete[] numYTiles; |
382 | |
|
383 | 0 | return static_cast<int> (lineOffsetSize); |
384 | 0 | } |
385 | 0 | catch (...) |
386 | 0 | { |
387 | 0 | delete[] numXTiles; |
388 | 0 | delete[] numYTiles; |
389 | |
|
390 | 0 | throw; |
391 | 0 | } |
392 | 0 | } |
393 | | |
394 | | OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT |