Coverage Report

Created: 2026-01-15 07:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/opencv/3rdparty/openexr/IlmImf/ImfPizCompressor.cpp
Line
Count
Source
1
///////////////////////////////////////////////////////////////////////////
2
//
3
// Copyright (c) 2004, Industrial Light & Magic, a division of Lucas
4
// Digital Ltd. LLC
5
// 
6
// All rights reserved.
7
// 
8
// Redistribution and use in source and binary forms, with or without
9
// modification, are permitted provided that the following conditions are
10
// met:
11
// *       Redistributions of source code must retain the above copyright
12
// notice, this list of conditions and the following disclaimer.
13
// *       Redistributions in binary form must reproduce the above
14
// copyright notice, this list of conditions and the following disclaimer
15
// in the documentation and/or other materials provided with the
16
// distribution.
17
// *       Neither the name of Industrial Light & Magic nor the names of
18
// its contributors may be used to endorse or promote products derived
19
// from this software without specific prior written permission. 
20
// 
21
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
//
33
///////////////////////////////////////////////////////////////////////////
34
35
36
//-----------------------------------------------------------------------------
37
//
38
//  class PizCompressor
39
//
40
//-----------------------------------------------------------------------------
41
42
#include "ImfPizCompressor.h"
43
#include "ImfHeader.h"
44
#include "ImfChannelList.h"
45
#include "ImfHuf.h"
46
#include "ImfWav.h"
47
#include "ImfMisc.h"
48
#include "ImfCheckedArithmetic.h"
49
#include <ImathFun.h>
50
#include <ImathBox.h>
51
#include <Iex.h>
52
#include "ImfIO.h"
53
#include "ImfXdr.h"
54
#include "ImfAutoArray.h"
55
#include <string.h>
56
#include <assert.h>
57
#include "ImfNamespace.h"
58
59
OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER
60
61
using IMATH_NAMESPACE::divp;
62
using IMATH_NAMESPACE::modp;
63
using IMATH_NAMESPACE::Box2i;
64
using IMATH_NAMESPACE::V2i;
65
using IEX_NAMESPACE::InputExc;
66
67
namespace {
68
69
//
70
// Functions to compress the range of values in the pixel data
71
//
72
73
const int USHORT_RANGE = (1 << 16);
74
const int BITMAP_SIZE  = (USHORT_RANGE >> 3);
75
76
void
77
bitmapFromData (const unsigned short data[/*nData*/],
78
    int nData,
79
    unsigned char bitmap[BITMAP_SIZE],
80
    unsigned short &minNonZero,
81
    unsigned short &maxNonZero)
82
0
{
83
0
    for (int i = 0; i < BITMAP_SIZE; ++i)
84
0
  bitmap[i] = 0;
85
86
0
    for (int i = 0; i < nData; ++i)
87
0
  bitmap[data[i] >> 3] |= (1 << (data[i] & 7));
88
89
0
    bitmap[0] &= ~1;      // zero is not explicitly stored in
90
          // the bitmap; we assume that the
91
          // data always contain zeroes
92
0
    minNonZero = BITMAP_SIZE - 1;
93
0
    maxNonZero = 0;
94
95
0
    for (int i = 0; i < BITMAP_SIZE; ++i)
96
0
    {
97
0
  if (bitmap[i])
98
0
  {
99
0
      if (minNonZero > i)
100
0
    minNonZero = i;
101
0
      if (maxNonZero < i)
102
0
    maxNonZero = i;
103
0
  }
104
0
    }
105
0
}
106
107
108
unsigned short
109
forwardLutFromBitmap (const unsigned char bitmap[BITMAP_SIZE],
110
          unsigned short lut[USHORT_RANGE])
111
0
{
112
0
    int k = 0;
113
114
0
    for (int i = 0; i < USHORT_RANGE; ++i)
115
0
    {
116
0
  if ((i == 0) || (bitmap[i >> 3] & (1 << (i & 7))))
117
0
      lut[i] = k++;
118
0
  else
119
0
      lut[i] = 0;
120
0
    }
121
122
0
    return k - 1; // maximum value stored in lut[],
123
0
}      // i.e. number of ones in bitmap minus 1
124
125
126
unsigned short
127
reverseLutFromBitmap (const unsigned char bitmap[BITMAP_SIZE],
128
          unsigned short lut[USHORT_RANGE])
129
0
{
130
0
    int k = 0;
131
132
0
    for (int i = 0; i < USHORT_RANGE; ++i)
133
0
    {
134
0
  if ((i == 0) || (bitmap[i >> 3] & (1 << (i & 7))))
135
0
      lut[k++] = i;
136
0
    }
137
138
0
    int n = k - 1;
139
140
0
    while (k < USHORT_RANGE)
141
0
  lut[k++] = 0;
142
143
0
    return n;   // maximum k where lut[k] is non-zero,
144
0
}      // i.e. number of ones in bitmap minus 1
145
146
147
void
148
applyLut (const unsigned short lut[USHORT_RANGE],
149
    unsigned short data[/*nData*/],
150
    int nData)
151
0
{
152
0
    for (int i = 0; i < nData; ++i)
153
0
  data[i] = lut[data[i]];
154
0
}
155
156
157
} // namespace
158
159
160
struct PizCompressor::ChannelData
161
{
162
    unsigned short *  start;
163
    unsigned short *  end;
164
    int     nx;
165
    int     ny;
166
    int     ys;
167
    int     size;
168
};
169
170
171
PizCompressor::PizCompressor
172
    (const Header &hdr,
173
     size_t maxScanLineSize,
174
     size_t numScanLines)
175
:
176
0
    Compressor (hdr),
177
0
    _maxScanLineSize (maxScanLineSize),
178
0
    _format (XDR),
179
0
    _numScanLines (numScanLines),
180
0
    _tmpBuffer (0),
181
0
    _outBuffer (0),
182
0
    _numChans (0),
183
0
    _channels (hdr.channels()),
184
0
    _channelData (0)
185
0
{
186
0
    size_t tmpBufferSize =
187
0
                uiMult (maxScanLineSize, numScanLines) / 2;
188
189
0
    size_t outBufferSize =
190
0
                uiAdd (uiMult (maxScanLineSize, numScanLines),
191
0
                       size_t (65536 + 8192));
192
193
0
    _tmpBuffer = new unsigned short
194
0
            [checkArraySize (tmpBufferSize, sizeof (unsigned short))];
195
196
0
    _outBuffer = new char [outBufferSize];
197
198
0
    const ChannelList &channels = header().channels();
199
0
    bool onlyHalfChannels = true;
200
201
0
    for (ChannelList::ConstIterator c = channels.begin();
202
0
   c != channels.end();
203
0
   ++c)
204
0
    {
205
0
  _numChans++;
206
207
0
  assert (pixelTypeSize (c.channel().type) % pixelTypeSize (HALF) == 0);
208
209
0
  if (c.channel().type != HALF)
210
0
      onlyHalfChannels = false;
211
0
    }
212
213
0
    _channelData = new ChannelData[_numChans];
214
215
0
    const Box2i &dataWindow = hdr.dataWindow();
216
217
0
    _minX = dataWindow.min.x;
218
0
    _maxX = dataWindow.max.x;
219
0
    _maxY = dataWindow.max.y;
220
221
    //
222
    // We can support uncompressed data in the machine's native format
223
    // if all image channels are of type HALF, and if the Xdr and the
224
    // native represenations of a half have the same size.
225
    //
226
227
0
    if (onlyHalfChannels && (sizeof (half) == pixelTypeSize (HALF)))
228
0
  _format = NATIVE;
229
0
}
230
231
232
PizCompressor::~PizCompressor ()
233
0
{
234
0
    delete [] _tmpBuffer;
235
0
    delete [] _outBuffer;
236
0
    delete [] _channelData;
237
0
}
238
239
240
int
241
PizCompressor::numScanLines () const
242
0
{
243
0
    return _numScanLines;
244
0
}
245
246
247
Compressor::Format
248
PizCompressor::format () const
249
0
{
250
0
    return _format;
251
0
}
252
253
254
int
255
PizCompressor::compress (const char *inPtr,
256
       int inSize,
257
       int minY,
258
       const char *&outPtr)
259
0
{
260
0
    return compress (inPtr,
261
0
         inSize,
262
0
         Box2i (V2i (_minX, minY),
263
0
          V2i (_maxX, minY + numScanLines() - 1)),
264
0
         outPtr);
265
0
}
266
267
268
int
269
PizCompressor::compressTile (const char *inPtr,
270
           int inSize,
271
           IMATH_NAMESPACE::Box2i range,
272
           const char *&outPtr)
273
0
{
274
0
    return compress (inPtr, inSize, range, outPtr);
275
0
}
276
277
278
int
279
PizCompressor::uncompress (const char *inPtr,
280
         int inSize,
281
         int minY,
282
         const char *&outPtr)
283
0
{
284
0
    return uncompress (inPtr,
285
0
           inSize,
286
0
           Box2i (V2i (_minX, minY),
287
0
            V2i (_maxX, minY + numScanLines() - 1)),
288
0
           outPtr);
289
0
}
290
291
292
int
293
PizCompressor::uncompressTile (const char *inPtr,
294
             int inSize,
295
             IMATH_NAMESPACE::Box2i range,
296
             const char *&outPtr)
297
0
{
298
0
    return uncompress (inPtr, inSize, range, outPtr);
299
0
}
300
301
302
int
303
PizCompressor::compress (const char *inPtr,
304
       int inSize,
305
       IMATH_NAMESPACE::Box2i range,
306
       const char *&outPtr)
307
0
{
308
    //
309
    // This is the compress function which is used by both the tiled and
310
    // scanline compression routines.
311
    //
312
313
    //
314
    // Special case �- empty input buffer
315
    //
316
317
0
    if (inSize == 0)
318
0
    {
319
0
  outPtr = _outBuffer;
320
0
  return 0;
321
0
    }
322
323
    //
324
    // Rearrange the pixel data so that the wavelet
325
    // and Huffman encoders can process them easily.
326
    //
327
    // The wavelet and Huffman encoders both handle only
328
    // 16-bit data, so 32-bit data must be split into smaller
329
    // pieces.  We treat each 32-bit channel (UINT, FLOAT) as
330
    // two interleaved 16-bit channels.
331
    //
332
333
0
    int minX = range.min.x;
334
0
    int maxX = range.max.x;
335
0
    int minY = range.min.y;
336
0
    int maxY = range.max.y;
337
    
338
0
    if (maxY > _maxY)
339
0
        maxY = _maxY;
340
    
341
0
    if (maxX > _maxX)
342
0
        maxX = _maxX;
343
344
0
    unsigned short *tmpBufferEnd = _tmpBuffer;
345
0
    int i = 0;
346
347
0
    for (ChannelList::ConstIterator c = _channels.begin();
348
0
   c != _channels.end();
349
0
   ++c, ++i)
350
0
    {
351
0
  ChannelData &cd = _channelData[i];
352
353
0
  cd.start = tmpBufferEnd;
354
0
  cd.end = cd.start;
355
356
0
  cd.nx = numSamples (c.channel().xSampling, minX, maxX);
357
0
  cd.ny = numSamples (c.channel().ySampling, minY, maxY);
358
0
  cd.ys = c.channel().ySampling;
359
360
0
  cd.size = pixelTypeSize (c.channel().type) / pixelTypeSize (HALF);
361
362
0
  tmpBufferEnd += cd.nx * cd.ny * cd.size;
363
0
    }
364
365
0
    if (_format == XDR)
366
0
    {
367
  //
368
  // Machine-independent (Xdr) data format
369
  //
370
371
0
  for (int y = minY; y <= maxY; ++y)
372
0
  {
373
0
      for (int i = 0; i < _numChans; ++i)
374
0
      {
375
0
    ChannelData &cd = _channelData[i];
376
377
0
    if (modp (y, cd.ys) != 0)
378
0
        continue;
379
380
0
    for (int x = cd.nx * cd.size; x > 0; --x)
381
0
    {
382
0
        Xdr::read <CharPtrIO> (inPtr, *cd.end);
383
0
        ++cd.end;
384
0
    }
385
0
      }
386
0
  }
387
0
    }
388
0
    else
389
0
    {
390
  //
391
  // Native, machine-dependent data format
392
  //
393
394
0
  for (int y = minY; y <= maxY; ++y)
395
0
  {
396
0
      for (int i = 0; i < _numChans; ++i)
397
0
      {
398
0
    ChannelData &cd = _channelData[i];
399
400
0
    if (modp (y, cd.ys) != 0)
401
0
        continue;
402
403
0
    int n = cd.nx * cd.size;
404
0
    memcpy (cd.end, inPtr, n * sizeof (unsigned short));
405
0
    inPtr  += n * sizeof (unsigned short);
406
0
    cd.end += n;
407
0
      }
408
0
  }
409
0
    }
410
411
    #if defined (DEBUG)
412
413
  for (int i = 1; i < _numChans; ++i)
414
      assert (_channelData[i-1].end == _channelData[i].start);
415
416
  assert (_channelData[_numChans-1].end == tmpBufferEnd);
417
418
    #endif
419
420
    //
421
    // Compress the range of the pixel data
422
    //
423
424
0
    AutoArray <unsigned char, BITMAP_SIZE> bitmap;
425
0
    unsigned short minNonZero;
426
0
    unsigned short maxNonZero;
427
428
0
    bitmapFromData (_tmpBuffer,
429
0
        tmpBufferEnd - _tmpBuffer,
430
0
        bitmap,
431
0
        minNonZero, maxNonZero);
432
433
0
    AutoArray <unsigned short, USHORT_RANGE> lut;
434
0
    unsigned short maxValue = forwardLutFromBitmap (bitmap, lut);
435
0
    applyLut (lut, _tmpBuffer, tmpBufferEnd - _tmpBuffer);
436
437
    //
438
    // Store range compression info in _outBuffer
439
    //
440
441
0
    char *buf = _outBuffer;
442
443
0
    Xdr::write <CharPtrIO> (buf, minNonZero);
444
0
    Xdr::write <CharPtrIO> (buf, maxNonZero);
445
446
0
    if (minNonZero <= maxNonZero)
447
0
    {
448
0
  Xdr::write <CharPtrIO> (buf, (char *) &bitmap[0] + minNonZero,
449
0
        maxNonZero - minNonZero + 1);
450
0
    }
451
452
    //
453
    // Apply wavelet encoding
454
    //
455
456
0
    for (int i = 0; i < _numChans; ++i)
457
0
    {
458
0
  ChannelData &cd = _channelData[i];
459
460
0
  for (int j = 0; j < cd.size; ++j)
461
0
  {
462
0
      wav2Encode (cd.start + j,
463
0
      cd.nx, cd.size,
464
0
      cd.ny, cd.nx * cd.size,
465
0
      maxValue);
466
0
  }
467
0
    }
468
469
    //
470
    // Apply Huffman encoding; append the result to _outBuffer
471
    //
472
473
0
    char *lengthPtr = buf;
474
0
    Xdr::write <CharPtrIO> (buf, int(0));
475
476
0
    int length = hufCompress (_tmpBuffer, tmpBufferEnd - _tmpBuffer, buf);
477
0
    Xdr::write <CharPtrIO> (lengthPtr, length);
478
479
0
    outPtr = _outBuffer;
480
0
    return buf - _outBuffer + length;
481
0
}
482
483
484
int
485
PizCompressor::uncompress (const char *inPtr,
486
         int inSize,
487
         IMATH_NAMESPACE::Box2i range,
488
         const char *&outPtr)
489
0
{
490
    //
491
    // This is the cunompress function which is used by both the tiled and
492
    // scanline decompression routines.
493
    //
494
    
495
    //
496
    // Special case - empty input buffer
497
    //
498
499
0
    if (inSize == 0)
500
0
    {
501
0
  outPtr = _outBuffer;
502
0
  return 0;
503
0
    }
504
505
    //
506
    // Determine the layout of the compressed pixel data
507
    //
508
509
0
    int minX = range.min.x;
510
0
    int maxX = range.max.x;
511
0
    int minY = range.min.y;
512
0
    int maxY = range.max.y;
513
    
514
0
    if (maxY > _maxY)
515
0
        maxY = _maxY;
516
    
517
0
    if (maxX > _maxX)
518
0
        maxX = _maxX;
519
520
0
    unsigned short *tmpBufferEnd = _tmpBuffer;
521
0
    int i = 0;
522
523
0
    for (ChannelList::ConstIterator c = _channels.begin();
524
0
   c != _channels.end();
525
0
   ++c, ++i)
526
0
    {
527
0
  ChannelData &cd = _channelData[i];
528
529
0
  cd.start = tmpBufferEnd;
530
0
  cd.end = cd.start;
531
532
0
  cd.nx = numSamples (c.channel().xSampling, minX, maxX);
533
0
  cd.ny = numSamples (c.channel().ySampling, minY, maxY);
534
0
  cd.ys = c.channel().ySampling;
535
536
0
  cd.size = pixelTypeSize (c.channel().type) / pixelTypeSize (HALF);
537
538
0
  tmpBufferEnd += cd.nx * cd.ny * cd.size;
539
0
    }
540
541
    //
542
    // Read range compression data
543
    //
544
545
0
    unsigned short minNonZero;
546
0
    unsigned short maxNonZero;
547
548
0
    AutoArray <unsigned char, BITMAP_SIZE> bitmap;
549
0
    memset (bitmap, 0, sizeof (unsigned char) * BITMAP_SIZE);
550
551
0
    Xdr::read <CharPtrIO> (inPtr, minNonZero);
552
0
    Xdr::read <CharPtrIO> (inPtr, maxNonZero);
553
554
0
    if (maxNonZero >= BITMAP_SIZE)
555
0
    {
556
0
  throw InputExc ("Error in header for PIZ-compressed data "
557
0
      "(invalid bitmap size).");
558
0
    }
559
560
0
    if (minNonZero <= maxNonZero)
561
0
    {
562
0
  Xdr::read <CharPtrIO> (inPtr, (char *) &bitmap[0] + minNonZero,
563
0
             maxNonZero - minNonZero + 1);
564
0
    }
565
566
0
    AutoArray <unsigned short, USHORT_RANGE> lut;
567
0
    unsigned short maxValue = reverseLutFromBitmap (bitmap, lut);
568
569
    //
570
    // Huffman decoding
571
    //
572
573
0
    int length;
574
0
    Xdr::read <CharPtrIO> (inPtr, length);
575
576
0
    if (length > inSize)
577
0
    {
578
0
  throw InputExc ("Error in header for PIZ-compressed data "
579
0
      "(invalid array length).");
580
0
    }
581
582
0
    hufUncompress (inPtr, length, _tmpBuffer, tmpBufferEnd - _tmpBuffer);
583
584
    //
585
    // Wavelet decoding
586
    //
587
588
0
    for (int i = 0; i < _numChans; ++i)
589
0
    {
590
0
  ChannelData &cd = _channelData[i];
591
592
0
  for (int j = 0; j < cd.size; ++j)
593
0
  {
594
0
      wav2Decode (cd.start + j,
595
0
      cd.nx, cd.size,
596
0
      cd.ny, cd.nx * cd.size,
597
0
      maxValue);
598
0
  }
599
0
    }
600
601
    //
602
    // Expand the pixel data to their original range
603
    //
604
605
0
    applyLut (lut, _tmpBuffer, tmpBufferEnd - _tmpBuffer);
606
    
607
    //
608
    // Rearrange the pixel data into the format expected by the caller.
609
    //
610
611
0
    char *outEnd = _outBuffer;
612
613
0
    if (_format == XDR)
614
0
    {
615
  //
616
  // Machine-independent (Xdr) data format
617
  //
618
619
0
  for (int y = minY; y <= maxY; ++y)
620
0
  {
621
0
      for (int i = 0; i < _numChans; ++i)
622
0
      {
623
0
    ChannelData &cd = _channelData[i];
624
625
0
    if (modp (y, cd.ys) != 0)
626
0
        continue;
627
628
0
    for (int x = cd.nx * cd.size; x > 0; --x)
629
0
    {
630
0
        Xdr::write <CharPtrIO> (outEnd, *cd.end);
631
0
        ++cd.end;
632
0
    }
633
0
      }
634
0
  }
635
0
    }
636
0
    else
637
0
    {
638
  //
639
  // Native, machine-dependent data format
640
  //
641
642
0
  for (int y = minY; y <= maxY; ++y)
643
0
  {
644
0
      for (int i = 0; i < _numChans; ++i)
645
0
      {
646
0
    ChannelData &cd = _channelData[i];
647
648
0
    if (modp (y, cd.ys) != 0)
649
0
        continue;
650
651
0
    int n = cd.nx * cd.size;
652
0
    memcpy (outEnd, cd.end, n * sizeof (unsigned short));
653
0
    outEnd += n * sizeof (unsigned short);
654
0
    cd.end += n;
655
0
      }
656
0
  }
657
0
    }
658
659
    #if defined (DEBUG)
660
661
  for (int i = 1; i < _numChans; ++i)
662
      assert (_channelData[i-1].end == _channelData[i].start);
663
664
  assert (_channelData[_numChans-1].end == tmpBufferEnd);
665
666
    #endif
667
668
0
    outPtr = _outBuffer;
669
0
    return outEnd - _outBuffer;
670
0
}
671
672
673
OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT