Coverage Report

Created: 2025-07-18 06:58

/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