Line data Source code
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 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : #include "cpl_port.h"
31 : #include "hfa_p.h"
32 :
33 : #include <cstddef>
34 :
35 : #include "cpl_conv.h"
36 : #include "cpl_error.h"
37 : #include "cpl_vsi.h"
38 : #include "hfa.h"
39 :
40 15 : HFACompress::HFACompress(void *pData, GUInt32 nBlockSize, EPTType eDataType)
41 : : m_pData(pData), m_nBlockSize(nBlockSize),
42 30 : m_nBlockCount((nBlockSize * 8) / HFAGetDataTypeBits(eDataType)),
43 30 : m_eDataType(eDataType), m_nDataTypeNumBits(HFAGetDataTypeBits(eDataType)),
44 : m_pCounts(nullptr), m_pCurrCount(nullptr), m_nSizeCounts(0),
45 : m_pValues(nullptr), m_pCurrValues(nullptr), m_nSizeValues(0), m_nMin(0),
46 15 : m_nNumRuns(0), m_nNumBits(0)
47 : {
48 : // Allocate some memory for the count and values - probably too big.
49 : // About right for worst case scenario.
50 15 : m_pCounts = static_cast<GByte *>(
51 15 : VSI_MALLOC_VERBOSE(m_nBlockCount * sizeof(GUInt32) + sizeof(GUInt32)));
52 :
53 15 : m_pValues = static_cast<GByte *>(
54 15 : VSI_MALLOC_VERBOSE(m_nBlockCount * sizeof(GUInt32) + sizeof(GUInt32)));
55 15 : }
56 :
57 30 : HFACompress::~HFACompress()
58 : {
59 : // Free the compressed data.
60 15 : CPLFree(m_pCounts);
61 15 : CPLFree(m_pValues);
62 15 : }
63 :
64 : // Returns the number of bits needed to encode a count.
65 14 : static GByte _FindNumBits(GUInt32 range)
66 : {
67 14 : if (range < 0xff)
68 : {
69 1 : return 8;
70 : }
71 :
72 13 : if (range < 0xffff)
73 : {
74 11 : return 16;
75 : }
76 :
77 2 : return 32;
78 : }
79 :
80 : // Gets the value from the uncompressed block as a GUInt32 no matter
81 : // the data type.
82 137218 : GUInt32 HFACompress::valueAsUInt32(GUInt32 iPixel)
83 : {
84 137218 : GUInt32 val = 0;
85 :
86 137218 : if (m_nDataTypeNumBits == 8)
87 : {
88 81920 : val = ((GByte *)m_pData)[iPixel];
89 : }
90 55298 : else if (m_nDataTypeNumBits == 16)
91 : {
92 14338 : val = ((GUInt16 *)m_pData)[iPixel];
93 : }
94 40960 : else if (m_nDataTypeNumBits == 32)
95 : {
96 32768 : val = ((GUInt32 *)m_pData)[iPixel];
97 : }
98 8192 : else if (m_nDataTypeNumBits == 4)
99 : {
100 0 : if (iPixel % 2 == 0)
101 0 : val = ((GByte *)m_pData)[iPixel / 2] & 0x0f;
102 : else
103 0 : val = (((GByte *)m_pData)[iPixel / 2] & 0xf0) >> 4;
104 : }
105 8192 : else if (m_nDataTypeNumBits == 2)
106 : {
107 0 : if (iPixel % 4 == 0)
108 0 : val = ((GByte *)m_pData)[iPixel / 4] & 0x03;
109 0 : else if (iPixel % 4 == 1)
110 0 : val = (((GByte *)m_pData)[iPixel / 4] & 0x0c) >> 2;
111 0 : else if (iPixel % 4 == 2)
112 0 : val = (((GByte *)m_pData)[iPixel / 4] & 0x30) >> 4;
113 : else
114 0 : val = (((GByte *)m_pData)[iPixel / 4] & 0xc0) >> 6;
115 : }
116 8192 : else if (m_nDataTypeNumBits == 1)
117 : {
118 8192 : if (((GByte *)m_pData)[iPixel >> 3] & (0x1 << (iPixel & 0x07)))
119 504 : val = 1;
120 : else
121 7688 : val = 0;
122 : }
123 : else
124 : {
125 : // Should not get to here. Check in compressBlock() should return false
126 : // if we can't compress this block because we don't know about the type.
127 0 : CPLError(CE_Failure, CPLE_FileIO,
128 0 : "Imagine Datatype 0x%x (0x%x bits) not supported", m_eDataType,
129 : m_nDataTypeNumBits);
130 0 : CPLAssert(false);
131 : }
132 :
133 137218 : return val;
134 : }
135 :
136 : // Finds the minimum value in a type specific fashion. This value is
137 : // subtracted from each value in the compressed dataset. The maximum
138 : // value is also found and the number of bits that the range can be stored
139 : // is also returned.
140 : //
141 : // TODO: Minimum value returned as pNumBits is now 8 - Imagine
142 : // can handle 1, 2, and 4 bits as well.
143 14 : GUInt32 HFACompress::findMin(GByte *pNumBits)
144 : {
145 14 : GUInt32 u32Min = valueAsUInt32(0);
146 14 : GUInt32 u32Max = u32Min;
147 :
148 69632 : for (GUInt32 count = 1; count < m_nBlockCount; count++)
149 : {
150 69618 : GUInt32 u32Val = valueAsUInt32(count);
151 69618 : if (u32Val < u32Min)
152 17 : u32Min = u32Val;
153 69601 : else if (u32Val > u32Max)
154 22 : u32Max = u32Val;
155 : }
156 :
157 14 : *pNumBits = _FindNumBits(u32Max - u32Min);
158 :
159 14 : return u32Min;
160 : }
161 :
162 : // Codes the count in the way expected by Imagine - i.e. the lower 2 bits
163 : // specify how many bytes the count takes up.
164 5834 : void HFACompress::makeCount(GUInt32 count, GByte *pCounter,
165 : GUInt32 *pnSizeCount)
166 : {
167 : // Because Imagine stores the number of bits used in the lower 2 bits of the
168 : // data it restricts what we can use.
169 5834 : if (count < 0x40)
170 : {
171 5825 : pCounter[0] = static_cast<GByte>(count);
172 5825 : *pnSizeCount = 1;
173 : }
174 9 : else if (count < 0x4000)
175 : {
176 9 : pCounter[1] = count & 0xff;
177 9 : count /= 256;
178 9 : pCounter[0] = static_cast<GByte>(count | 0x40);
179 9 : *pnSizeCount = 2;
180 : }
181 0 : else if (count < 0x400000)
182 : {
183 0 : pCounter[2] = count & 0xff;
184 0 : count /= 256;
185 0 : pCounter[1] = count & 0xff;
186 0 : count /= 256;
187 0 : pCounter[0] = static_cast<GByte>(count | 0x80);
188 0 : *pnSizeCount = 3;
189 : }
190 : else
191 : {
192 0 : pCounter[3] = count & 0xff;
193 0 : count /= 256;
194 0 : pCounter[2] = count & 0xff;
195 0 : count /= 256;
196 0 : pCounter[1] = count & 0xff;
197 0 : count /= 256;
198 0 : pCounter[0] = static_cast<GByte>(count | 0xc0);
199 0 : *pnSizeCount = 4;
200 : }
201 5834 : }
202 :
203 : // Encodes the value depending on the number of bits we are using.
204 5834 : void HFACompress::encodeValue(GUInt32 val, GUInt32 repeat)
205 : {
206 5834 : GUInt32 nSizeCount = 0;
207 :
208 5834 : makeCount(repeat, m_pCurrCount, &nSizeCount);
209 5834 : m_pCurrCount += nSizeCount;
210 5834 : if (m_nNumBits == 8)
211 : {
212 : // Only storing 8 bits per value as the range is small.
213 196 : *(GByte *)m_pCurrValues = GByte(val - m_nMin);
214 196 : m_pCurrValues += sizeof(GByte);
215 : }
216 5638 : else if (m_nNumBits == 16)
217 : {
218 : // Only storing 16 bits per value as the range is small.
219 3587 : *(GUInt16 *)m_pCurrValues = GUInt16(val - m_nMin);
220 : #ifndef CPL_MSB
221 3587 : CPL_SWAP16PTR(m_pCurrValues);
222 : #endif // ndef CPL_MSB
223 3587 : m_pCurrValues += sizeof(GUInt16);
224 : }
225 : else
226 : {
227 2051 : *(GUInt32 *)m_pCurrValues = GUInt32(val - m_nMin);
228 : #ifndef CPL_MSB
229 2051 : CPL_SWAP32PTR(m_pCurrValues);
230 : #endif // ndef CPL_MSB
231 2051 : m_pCurrValues += sizeof(GUInt32);
232 : }
233 5834 : }
234 :
235 : // This is the guts of the file - call this to compress the block returns false
236 : // if the compression fails - i.e. compressed block bigger than input.
237 15 : bool HFACompress::compressBlock()
238 : {
239 15 : GUInt32 nLastUnique = 0;
240 :
241 : // Check we know about the datatype to be compressed.
242 : // If we can't compress it we should return false so that
243 : // the block cannot be compressed (we can handle just about
244 : // any type uncompressed).
245 15 : if (!QueryDataTypeSupported(m_eDataType))
246 : {
247 1 : CPLDebug("HFA",
248 : "Cannot compress HFA datatype 0x%x (0x%x bits). "
249 : "Writing uncompressed instead.",
250 1 : m_eDataType, m_nDataTypeNumBits);
251 1 : return false;
252 : }
253 :
254 : // Reset our pointers.
255 14 : m_pCurrCount = m_pCounts;
256 14 : m_pCurrValues = m_pValues;
257 :
258 : // Get the minimum value. this can be subtracted from each value in
259 : // the image.
260 14 : m_nMin = findMin(&m_nNumBits);
261 :
262 : // Go through the block.
263 14 : GUInt32 u32Last = valueAsUInt32(0);
264 67585 : for (GUInt32 count = 1; count < m_nBlockCount; count++)
265 : {
266 67572 : const GUInt32 u32Val = valueAsUInt32(count);
267 67572 : if (u32Val != u32Last)
268 : {
269 : // The values have changed - i.e. a run has come to and end.
270 5821 : encodeValue(u32Last, count - nLastUnique);
271 :
272 5821 : if ((m_pCurrValues - m_pValues) > static_cast<int>(m_nBlockSize))
273 : {
274 1 : return false;
275 : }
276 :
277 5820 : m_nNumRuns++;
278 5820 : u32Last = u32Val;
279 5820 : nLastUnique = count;
280 : }
281 : }
282 :
283 : // We have done the block but have not got the last run because we
284 : // were only looking for a change in values.
285 13 : encodeValue(u32Last, m_nBlockCount - nLastUnique);
286 13 : m_nNumRuns++;
287 :
288 : // Set the size variables.
289 13 : m_nSizeCounts = static_cast<GUInt32>(m_pCurrCount - m_pCounts);
290 13 : m_nSizeValues = static_cast<GUInt32>(m_pCurrValues - m_pValues);
291 :
292 : // The 13 is for the header size - maybe this should live with some
293 : // constants somewhere?
294 13 : return (m_nSizeCounts + m_nSizeValues + 13) < m_nBlockSize;
295 : }
296 :
297 15 : bool HFACompress::QueryDataTypeSupported(EPTType eHFADataType)
298 : {
299 15 : const int nBits = HFAGetDataTypeBits(eHFADataType);
300 :
301 14 : return nBits == 1 || nBits == 2 || nBits == 4 || nBits == 8 ||
302 29 : nBits == 16 || nBits == 32;
303 : }
|