Coverage Report

Created: 2026-02-14 09:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}