/src/openexr/src/lib/OpenEXR/ImfDwaCompressor.cpp
Line | Count | Source |
1 | | // |
2 | | // SPDX-License-Identifier: BSD-3-Clause |
3 | | // Copyright (c) DreamWorks Animation LLC and Contributors of the OpenEXR Project |
4 | | // |
5 | | |
6 | | //--------------------------------------------------- |
7 | | // |
8 | | // class DwaCompressor -- Store lossy RGB data by quantizing |
9 | | // DCT components. |
10 | | // |
11 | | // First, we try and figure out what compression strategy to take |
12 | | // based in channel name. For RGB channels, we want a lossy method |
13 | | // described below. But, if we have alpha, we should do something |
14 | | // different (and probably using RLE). If we have depth, or velocity, |
15 | | // or something else, just fall back to ZIP. The rules for deciding |
16 | | // which strategy to use are setup in initializeDefaultChannelRules(). |
17 | | // When writing a file, the relevant rules needed to decode are written |
18 | | // into the start of the data block, making a self-contained file. |
19 | | // If initializeDefaultChannelRules() doesn't quite suite your naming |
20 | | // conventions, you can adjust the rules without breaking decoder |
21 | | // compatibility. |
22 | | // |
23 | | // If we're going to lossy compress R, G, or B channels, it's easier |
24 | | // to toss bits in a more perceptual uniform space. One could argue |
25 | | // at length as to what constitutes perceptually uniform, especially |
26 | | // when storing either scene/input/focal plane referred and output referred |
27 | | // data. |
28 | | // |
29 | | // We'll compromise. For values <= 1, we use a traditional power function |
30 | | // (without any of that straight-line business at the bottom). For values > 1, |
31 | | // we want something more like a log function, since power functions blow |
32 | | // up. At 1, we want a smooth blend between the functions. So, we use a |
33 | | // piecewise function that does just that - see dwaLookups.cpp for |
34 | | // a little more detail. |
35 | | // |
36 | | // Also, if we find that we have R, G, and B channels from the same layer, |
37 | | // we can get a bit more compression efficiency by transforming to a Y'CbCr |
38 | | // space. We use the 709 transform, but with Cb,Cr = 0 for an input of |
39 | | // (0, 0, 0), instead of the traditional Cb,Cr = .5. Shifting the zero point |
40 | | // makes no sense with large range data. Transforms are done to from |
41 | | // the perceptual space data, not the linear-light space data (R'G'B' -> |
42 | | // (Y'CbCr, not RGB -> YCbCr). |
43 | | // |
44 | | // Next, we forward DCT the data. This is done with a floating |
45 | | // point DCT, as we don't really have control over the src range. The |
46 | | // resulting values are dropped to half-float precision. |
47 | | // |
48 | | // Now, we need to quantize. Quantization departs from the usual way |
49 | | // of dividing and rounding. Instead, we start with some floating |
50 | | // point "base-error" value. From this, we can derive quantization |
51 | | // error for each DCT component. Take the standard JPEG quantization |
52 | | // tables and normalize them by the smallest value. Then, multiply |
53 | | // the normalized quant tables by our base-error value. This gives |
54 | | // a range of errors for each DCT component. |
55 | | // |
56 | | // For each DCT component, we want to find a quantized value that |
57 | | // is within +- the per-component error. Pick the quantized value |
58 | | // that has the fewest bits set in its' binary representation. |
59 | | // Brute-forcing the search would make for extremely inefficient |
60 | | // compression. Fortunately, we can precompute a table to assist |
61 | | // with this search. |
62 | | // |
63 | | // For each 16-bit float value, there are at most 15 other values with |
64 | | // fewer bits set. We can precompute these values in a compact form, since |
65 | | // many source values have far fewer that 15 possible quantized values. |
66 | | // Now, instead of searching the entire range +- the component error, |
67 | | // we can just search at most 15 quantization candidates. The search can |
68 | | // be accelerated a bit more by sorting the candidates by the |
69 | | // number of bits set, in increasing order. Then, the search can stop |
70 | | // once a candidate is found w/i the per-component quantization |
71 | | // error range. |
72 | | // |
73 | | // The quantization strategy has the side-benefit that there is no |
74 | | // de-quantization step upon decode, so we don't bother recording |
75 | | // the quantization table. |
76 | | // |
77 | | // Ok. So we now have quantized values. Time for entropy coding. We |
78 | | // can use either static Huffman or zlib/DEFLATE. The static Huffman |
79 | | // is more efficient at compacting data, but can have a greater |
80 | | // overhead, especially for smaller tile/strip sizes. |
81 | | // |
82 | | // There is some additional fun, like ZIP compressing the DC components |
83 | | // instead of Huffman/zlib, which helps make things slightly smaller. |
84 | | // |
85 | | // Compression level is controlled by setting an int/float/double attribute |
86 | | // on the header named "dwaCompressionLevel". This is a thinly veiled name for |
87 | | // the "base-error" value mentioned above. The "base-error" is just |
88 | | // dwaCompressionLevel / 100000. The default value of 45.0 is generally |
89 | | // pretty good at generating "visually lossless" values at reasonable |
90 | | // data rates. Setting dwaCompressionLevel to 0 should result in no additional |
91 | | // quantization at the quantization stage (though there may be |
92 | | // quantization in practice at the CSC/DCT steps). But if you really |
93 | | // want lossless compression, there are pleanty of other choices |
94 | | // of compressors ;) |
95 | | // |
96 | | // When dealing with FLOAT source buffers, we first quantize the source |
97 | | // to HALF and continue down as we would for HALF source. |
98 | | // |
99 | | //--------------------------------------------------- |
100 | | |
101 | | #include "ImfDwaCompressor.h" |
102 | | |
103 | | OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER |
104 | | |
105 | | // ============================================================== |
106 | | // |
107 | | // DwaCompressor |
108 | | // |
109 | | // -------------------------------------------------------------- |
110 | | |
111 | | // |
112 | | // DwaCompressor() |
113 | | // |
114 | | |
115 | | DwaCompressor::DwaCompressor ( |
116 | | const Header& hdr, |
117 | | size_t maxScanLineSize, |
118 | | int numScanLines, |
119 | | AcCompression acCompression) |
120 | 0 | : Compressor (hdr, |
121 | 0 | EXR_COMPRESSION_LAST_TYPE, /* use the header */ |
122 | 0 | maxScanLineSize, |
123 | 0 | numScanLines) |
124 | 0 | { |
125 | 0 | } |
126 | | |
127 | | DwaCompressor::~DwaCompressor () |
128 | 0 | { |
129 | 0 | } |
130 | | |
131 | | OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT |