/src/gdal/frmts/hfa/hfacompress.cpp
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Name: hfadataset.cpp |
4 | | * Project: Erdas Imagine Driver |
5 | | * Purpose: Imagine Compression code. |
6 | | * Author: Sam Gillingham <sam.gillingham at nrm.qld.gov> |
7 | | * |
8 | | ****************************************************************************** |
9 | | * Copyright (c) 2005, Sam Gillingham |
10 | | * |
11 | | * SPDX-License-Identifier: MIT |
12 | | ****************************************************************************/ |
13 | | |
14 | | #include "cpl_port.h" |
15 | | #include "hfa_p.h" |
16 | | |
17 | | #include <cstddef> |
18 | | |
19 | | #include "cpl_conv.h" |
20 | | #include "cpl_error.h" |
21 | | #include "cpl_vsi.h" |
22 | | #include "hfa.h" |
23 | | |
24 | | HFACompress::HFACompress(void *pData, GUInt32 nBlockSize, EPTType eDataType) |
25 | 1.60k | : m_pData(pData), m_nBlockSize(nBlockSize), |
26 | 1.60k | m_nBlockCount((nBlockSize * 8) / HFAGetDataTypeBits(eDataType)), |
27 | 1.60k | m_eDataType(eDataType), m_nDataTypeNumBits(HFAGetDataTypeBits(eDataType)), |
28 | 1.60k | m_pCounts(nullptr), m_pCurrCount(nullptr), m_nSizeCounts(0), |
29 | 1.60k | m_pValues(nullptr), m_pCurrValues(nullptr), m_nSizeValues(0), m_nMin(0), |
30 | 1.60k | m_nNumRuns(0), m_nNumBits(0) |
31 | 1.60k | { |
32 | | // Allocate some memory for the count and values - probably too big. |
33 | | // About right for worst case scenario. |
34 | 1.60k | m_pCounts = static_cast<GByte *>( |
35 | 1.60k | VSI_MALLOC_VERBOSE(m_nBlockCount * sizeof(GUInt32) + sizeof(GUInt32))); |
36 | | |
37 | 1.60k | m_pValues = static_cast<GByte *>( |
38 | 1.60k | VSI_MALLOC_VERBOSE(m_nBlockCount * sizeof(GUInt32) + sizeof(GUInt32))); |
39 | 1.60k | } |
40 | | |
41 | | HFACompress::~HFACompress() |
42 | 1.60k | { |
43 | | // Free the compressed data. |
44 | 1.60k | CPLFree(m_pCounts); |
45 | 1.60k | CPLFree(m_pValues); |
46 | 1.60k | } |
47 | | |
48 | | // Returns the number of bits needed to encode a count. |
49 | | static GByte _FindNumBits(GUInt32 range) |
50 | 1.60k | { |
51 | 1.60k | if (range < 0xff) |
52 | 501 | { |
53 | 501 | return 8; |
54 | 501 | } |
55 | | |
56 | 1.10k | if (range < 0xffff) |
57 | 602 | { |
58 | 602 | return 16; |
59 | 602 | } |
60 | | |
61 | 499 | return 32; |
62 | 1.10k | } |
63 | | |
64 | | // Gets the value from the uncompressed block as a GUInt32 no matter |
65 | | // the data type. |
66 | | GUInt32 HFACompress::valueAsUInt32(GUInt32 iPixel) |
67 | 13.1M | { |
68 | 13.1M | GUInt32 val = 0; |
69 | | |
70 | 13.1M | if (m_nDataTypeNumBits == 8) |
71 | 5.77M | { |
72 | 5.77M | val = ((GByte *)m_pData)[iPixel]; |
73 | 5.77M | } |
74 | 7.34M | else if (m_nDataTypeNumBits == 16) |
75 | 3.14M | { |
76 | 3.14M | val = ((GUInt16 *)m_pData)[iPixel]; |
77 | 3.14M | } |
78 | 4.20M | else if (m_nDataTypeNumBits == 32) |
79 | 4.20M | { |
80 | 4.20M | val = ((GUInt32 *)m_pData)[iPixel]; |
81 | 4.20M | } |
82 | 0 | else if (m_nDataTypeNumBits == 4) |
83 | 0 | { |
84 | 0 | if (iPixel % 2 == 0) |
85 | 0 | val = ((GByte *)m_pData)[iPixel / 2] & 0x0f; |
86 | 0 | else |
87 | 0 | val = (((GByte *)m_pData)[iPixel / 2] & 0xf0) >> 4; |
88 | 0 | } |
89 | 0 | else if (m_nDataTypeNumBits == 2) |
90 | 0 | { |
91 | 0 | if (iPixel % 4 == 0) |
92 | 0 | val = ((GByte *)m_pData)[iPixel / 4] & 0x03; |
93 | 0 | else if (iPixel % 4 == 1) |
94 | 0 | val = (((GByte *)m_pData)[iPixel / 4] & 0x0c) >> 2; |
95 | 0 | else if (iPixel % 4 == 2) |
96 | 0 | val = (((GByte *)m_pData)[iPixel / 4] & 0x30) >> 4; |
97 | 0 | else |
98 | 0 | val = (((GByte *)m_pData)[iPixel / 4] & 0xc0) >> 6; |
99 | 0 | } |
100 | 0 | else if (m_nDataTypeNumBits == 1) |
101 | 0 | { |
102 | 0 | if (((GByte *)m_pData)[iPixel >> 3] & (0x1 << (iPixel & 0x07))) |
103 | 0 | val = 1; |
104 | 0 | else |
105 | 0 | val = 0; |
106 | 0 | } |
107 | 0 | else |
108 | 0 | { |
109 | | // Should not get to here. Check in compressBlock() should return false |
110 | | // if we can't compress this block because we don't know about the type. |
111 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
112 | 0 | "Imagine Datatype 0x%x (0x%x bits) not supported", m_eDataType, |
113 | 0 | m_nDataTypeNumBits); |
114 | 0 | CPLAssert(false); |
115 | 0 | } |
116 | | |
117 | 13.1M | return val; |
118 | 13.1M | } |
119 | | |
120 | | // Finds the minimum value in a type specific fashion. This value is |
121 | | // subtracted from each value in the compressed dataset. The maximum |
122 | | // value is also found and the number of bits that the range can be stored |
123 | | // is also returned. |
124 | | // |
125 | | // TODO: Minimum value returned as pNumBits is now 8 - Imagine |
126 | | // can handle 1, 2, and 4 bits as well. |
127 | | GUInt32 HFACompress::findMin(GByte *pNumBits) |
128 | 1.60k | { |
129 | 1.60k | GUInt32 u32Min = valueAsUInt32(0); |
130 | 1.60k | GUInt32 u32Max = u32Min; |
131 | | |
132 | 6.56M | for (GUInt32 count = 1; count < m_nBlockCount; count++) |
133 | 6.56M | { |
134 | 6.56M | GUInt32 u32Val = valueAsUInt32(count); |
135 | 6.56M | if (u32Val < u32Min) |
136 | 2.47k | u32Min = u32Val; |
137 | 6.55M | else if (u32Val > u32Max) |
138 | 2.66k | u32Max = u32Val; |
139 | 6.56M | } |
140 | | |
141 | 1.60k | *pNumBits = _FindNumBits(u32Max - u32Min); |
142 | | |
143 | 1.60k | return u32Min; |
144 | 1.60k | } |
145 | | |
146 | | // Codes the count in the way expected by Imagine - i.e. the lower 2 bits |
147 | | // specify how many bytes the count takes up. |
148 | | void HFACompress::makeCount(GUInt32 count, GByte *pCounter, |
149 | | GUInt32 *pnSizeCount) |
150 | 349k | { |
151 | | // Because Imagine stores the number of bits used in the lower 2 bits of the |
152 | | // data it restricts what we can use. |
153 | 349k | if (count < 0x40) |
154 | 347k | { |
155 | 347k | pCounter[0] = static_cast<GByte>(count); |
156 | 347k | *pnSizeCount = 1; |
157 | 347k | } |
158 | 2.20k | else if (count < 0x4000) |
159 | 2.20k | { |
160 | 2.20k | pCounter[1] = count & 0xff; |
161 | 2.20k | count /= 256; |
162 | 2.20k | pCounter[0] = static_cast<GByte>(count | 0x40); |
163 | 2.20k | *pnSizeCount = 2; |
164 | 2.20k | } |
165 | 0 | else if (count < 0x400000) |
166 | 0 | { |
167 | 0 | pCounter[2] = count & 0xff; |
168 | 0 | count /= 256; |
169 | 0 | pCounter[1] = count & 0xff; |
170 | 0 | count /= 256; |
171 | 0 | pCounter[0] = static_cast<GByte>(count | 0x80); |
172 | 0 | *pnSizeCount = 3; |
173 | 0 | } |
174 | 0 | else |
175 | 0 | { |
176 | 0 | pCounter[3] = count & 0xff; |
177 | 0 | count /= 256; |
178 | 0 | pCounter[2] = count & 0xff; |
179 | 0 | count /= 256; |
180 | 0 | pCounter[1] = count & 0xff; |
181 | 0 | count /= 256; |
182 | 0 | pCounter[0] = static_cast<GByte>(count | 0xc0); |
183 | 0 | *pnSizeCount = 4; |
184 | 0 | } |
185 | 349k | } |
186 | | |
187 | | // Encodes the value depending on the number of bits we are using. |
188 | | void HFACompress::encodeValue(GUInt32 val, GUInt32 repeat) |
189 | 349k | { |
190 | 349k | GUInt32 nSizeCount = 0; |
191 | | |
192 | 349k | makeCount(repeat, m_pCurrCount, &nSizeCount); |
193 | 349k | m_pCurrCount += nSizeCount; |
194 | 349k | if (m_nNumBits == 8) |
195 | 518 | { |
196 | | // Only storing 8 bits per value as the range is small. |
197 | 518 | *m_pCurrValues = GByte(val - m_nMin); |
198 | 518 | m_pCurrValues += sizeof(GByte); |
199 | 518 | } |
200 | 349k | else if (m_nNumBits == 16) |
201 | 101k | { |
202 | | // Only storing 16 bits per value as the range is small. |
203 | 101k | *reinterpret_cast<GUInt16 *>(m_pCurrValues) = GUInt16(val - m_nMin); |
204 | 101k | #ifndef CPL_MSB |
205 | 101k | CPL_SWAP16PTR(m_pCurrValues); |
206 | 101k | #endif // ndef CPL_MSB |
207 | 101k | m_pCurrValues += sizeof(GUInt16); |
208 | 101k | } |
209 | 247k | else |
210 | 247k | { |
211 | 247k | *reinterpret_cast<GUInt32 *>(m_pCurrValues) = GUInt32(val - m_nMin); |
212 | 247k | #ifndef CPL_MSB |
213 | 247k | CPL_SWAP32PTR(m_pCurrValues); |
214 | 247k | #endif // ndef CPL_MSB |
215 | 247k | m_pCurrValues += sizeof(GUInt32); |
216 | 247k | } |
217 | 349k | } |
218 | | |
219 | | // This is the guts of the file - call this to compress the block returns false |
220 | | // if the compression fails - i.e. compressed block bigger than input. |
221 | | bool HFACompress::compressBlock() |
222 | 1.60k | { |
223 | 1.60k | GUInt32 nLastUnique = 0; |
224 | | |
225 | | // Check we know about the datatype to be compressed. |
226 | | // If we can't compress it we should return false so that |
227 | | // the block cannot be compressed (we can handle just about |
228 | | // any type uncompressed). |
229 | 1.60k | if (!QueryDataTypeSupported(m_eDataType)) |
230 | 0 | { |
231 | 0 | CPLDebug("HFA", |
232 | 0 | "Cannot compress HFA datatype 0x%x (0x%x bits). " |
233 | 0 | "Writing uncompressed instead.", |
234 | 0 | m_eDataType, m_nDataTypeNumBits); |
235 | 0 | return false; |
236 | 0 | } |
237 | | |
238 | | // Reset our pointers. |
239 | 1.60k | m_pCurrCount = m_pCounts; |
240 | 1.60k | m_pCurrValues = m_pValues; |
241 | | |
242 | | // Get the minimum value. this can be subtracted from each value in |
243 | | // the image. |
244 | 1.60k | m_nMin = findMin(&m_nNumBits); |
245 | | |
246 | | // Go through the block. |
247 | 1.60k | GUInt32 u32Last = valueAsUInt32(0); |
248 | 6.56M | for (GUInt32 count = 1; count < m_nBlockCount; count++) |
249 | 6.56M | { |
250 | 6.56M | const GUInt32 u32Val = valueAsUInt32(count); |
251 | 6.56M | if (u32Val != u32Last) |
252 | 348k | { |
253 | | // The values have changed - i.e. a run has come to and end. |
254 | 348k | encodeValue(u32Last, count - nLastUnique); |
255 | | |
256 | 348k | if ((m_pCurrValues - m_pValues) > static_cast<int>(m_nBlockSize)) |
257 | 0 | { |
258 | 0 | return false; |
259 | 0 | } |
260 | | |
261 | 348k | m_nNumRuns++; |
262 | 348k | u32Last = u32Val; |
263 | 348k | nLastUnique = count; |
264 | 348k | } |
265 | 6.56M | } |
266 | | |
267 | | // We have done the block but have not got the last run because we |
268 | | // were only looking for a change in values. |
269 | 1.60k | encodeValue(u32Last, m_nBlockCount - nLastUnique); |
270 | 1.60k | m_nNumRuns++; |
271 | | |
272 | | // Set the size variables. |
273 | 1.60k | m_nSizeCounts = static_cast<GUInt32>(m_pCurrCount - m_pCounts); |
274 | 1.60k | m_nSizeValues = static_cast<GUInt32>(m_pCurrValues - m_pValues); |
275 | | |
276 | | // The 13 is for the header size - maybe this should live with some |
277 | | // constants somewhere? |
278 | 1.60k | return (m_nSizeCounts + m_nSizeValues + 13) < m_nBlockSize; |
279 | 1.60k | } |
280 | | |
281 | | bool HFACompress::QueryDataTypeSupported(EPTType eHFADataType) |
282 | 1.60k | { |
283 | 1.60k | const int nBits = HFAGetDataTypeBits(eHFADataType); |
284 | | |
285 | 1.60k | return nBits == 1 || nBits == 2 || nBits == 4 || nBits == 8 || |
286 | 897 | nBits == 16 || nBits == 32; |
287 | 1.60k | } |