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 : * 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 15 : HFACompress::HFACompress(void *pData, GUInt32 nBlockSize, EPTType eDataType) 25 : : m_pData(pData), m_nBlockSize(nBlockSize), 26 30 : m_nBlockCount((nBlockSize * 8) / HFAGetDataTypeBits(eDataType)), 27 30 : m_eDataType(eDataType), m_nDataTypeNumBits(HFAGetDataTypeBits(eDataType)), 28 : m_pCounts(nullptr), m_pCurrCount(nullptr), m_nSizeCounts(0), 29 : m_pValues(nullptr), m_pCurrValues(nullptr), m_nSizeValues(0), m_nMin(0), 30 15 : m_nNumRuns(0), m_nNumBits(0) 31 : { 32 : // Allocate some memory for the count and values - probably too big. 33 : // About right for worst case scenario. 34 15 : m_pCounts = static_cast<GByte *>( 35 15 : VSI_MALLOC_VERBOSE(m_nBlockCount * sizeof(GUInt32) + sizeof(GUInt32))); 36 : 37 15 : m_pValues = static_cast<GByte *>( 38 15 : VSI_MALLOC_VERBOSE(m_nBlockCount * sizeof(GUInt32) + sizeof(GUInt32))); 39 15 : } 40 : 41 30 : HFACompress::~HFACompress() 42 : { 43 : // Free the compressed data. 44 15 : CPLFree(m_pCounts); 45 15 : CPLFree(m_pValues); 46 15 : } 47 : 48 : // Returns the number of bits needed to encode a count. 49 14 : static GByte _FindNumBits(GUInt32 range) 50 : { 51 14 : if (range < 0xff) 52 : { 53 1 : return 8; 54 : } 55 : 56 13 : if (range < 0xffff) 57 : { 58 11 : return 16; 59 : } 60 : 61 2 : return 32; 62 : } 63 : 64 : // Gets the value from the uncompressed block as a GUInt32 no matter 65 : // the data type. 66 137218 : GUInt32 HFACompress::valueAsUInt32(GUInt32 iPixel) 67 : { 68 137218 : GUInt32 val = 0; 69 : 70 137218 : if (m_nDataTypeNumBits == 8) 71 : { 72 81920 : val = ((GByte *)m_pData)[iPixel]; 73 : } 74 55298 : else if (m_nDataTypeNumBits == 16) 75 : { 76 14338 : val = ((GUInt16 *)m_pData)[iPixel]; 77 : } 78 40960 : else if (m_nDataTypeNumBits == 32) 79 : { 80 32768 : val = ((GUInt32 *)m_pData)[iPixel]; 81 : } 82 8192 : else if (m_nDataTypeNumBits == 4) 83 : { 84 0 : if (iPixel % 2 == 0) 85 0 : val = ((GByte *)m_pData)[iPixel / 2] & 0x0f; 86 : else 87 0 : val = (((GByte *)m_pData)[iPixel / 2] & 0xf0) >> 4; 88 : } 89 8192 : else if (m_nDataTypeNumBits == 2) 90 : { 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 : else 98 0 : val = (((GByte *)m_pData)[iPixel / 4] & 0xc0) >> 6; 99 : } 100 8192 : else if (m_nDataTypeNumBits == 1) 101 : { 102 8192 : if (((GByte *)m_pData)[iPixel >> 3] & (0x1 << (iPixel & 0x07))) 103 504 : val = 1; 104 : else 105 7688 : val = 0; 106 : } 107 : else 108 : { 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 : m_nDataTypeNumBits); 114 0 : CPLAssert(false); 115 : } 116 : 117 137218 : return val; 118 : } 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 14 : GUInt32 HFACompress::findMin(GByte *pNumBits) 128 : { 129 14 : GUInt32 u32Min = valueAsUInt32(0); 130 14 : GUInt32 u32Max = u32Min; 131 : 132 69632 : for (GUInt32 count = 1; count < m_nBlockCount; count++) 133 : { 134 69618 : GUInt32 u32Val = valueAsUInt32(count); 135 69618 : if (u32Val < u32Min) 136 17 : u32Min = u32Val; 137 69601 : else if (u32Val > u32Max) 138 22 : u32Max = u32Val; 139 : } 140 : 141 14 : *pNumBits = _FindNumBits(u32Max - u32Min); 142 : 143 14 : return u32Min; 144 : } 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 5834 : void HFACompress::makeCount(GUInt32 count, GByte *pCounter, 149 : GUInt32 *pnSizeCount) 150 : { 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 5834 : if (count < 0x40) 154 : { 155 5825 : pCounter[0] = static_cast<GByte>(count); 156 5825 : *pnSizeCount = 1; 157 : } 158 9 : else if (count < 0x4000) 159 : { 160 9 : pCounter[1] = count & 0xff; 161 9 : count /= 256; 162 9 : pCounter[0] = static_cast<GByte>(count | 0x40); 163 9 : *pnSizeCount = 2; 164 : } 165 0 : else if (count < 0x400000) 166 : { 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 : } 174 : else 175 : { 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 : } 185 5834 : } 186 : 187 : // Encodes the value depending on the number of bits we are using. 188 5834 : void HFACompress::encodeValue(GUInt32 val, GUInt32 repeat) 189 : { 190 5834 : GUInt32 nSizeCount = 0; 191 : 192 5834 : makeCount(repeat, m_pCurrCount, &nSizeCount); 193 5834 : m_pCurrCount += nSizeCount; 194 5834 : if (m_nNumBits == 8) 195 : { 196 : // Only storing 8 bits per value as the range is small. 197 196 : *(GByte *)m_pCurrValues = GByte(val - m_nMin); 198 196 : m_pCurrValues += sizeof(GByte); 199 : } 200 5638 : else if (m_nNumBits == 16) 201 : { 202 : // Only storing 16 bits per value as the range is small. 203 3587 : *(GUInt16 *)m_pCurrValues = GUInt16(val - m_nMin); 204 : #ifndef CPL_MSB 205 3587 : CPL_SWAP16PTR(m_pCurrValues); 206 : #endif // ndef CPL_MSB 207 3587 : m_pCurrValues += sizeof(GUInt16); 208 : } 209 : else 210 : { 211 2051 : *(GUInt32 *)m_pCurrValues = GUInt32(val - m_nMin); 212 : #ifndef CPL_MSB 213 2051 : CPL_SWAP32PTR(m_pCurrValues); 214 : #endif // ndef CPL_MSB 215 2051 : m_pCurrValues += sizeof(GUInt32); 216 : } 217 5834 : } 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 15 : bool HFACompress::compressBlock() 222 : { 223 15 : 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 15 : if (!QueryDataTypeSupported(m_eDataType)) 230 : { 231 1 : CPLDebug("HFA", 232 : "Cannot compress HFA datatype 0x%x (0x%x bits). " 233 : "Writing uncompressed instead.", 234 1 : m_eDataType, m_nDataTypeNumBits); 235 1 : return false; 236 : } 237 : 238 : // Reset our pointers. 239 14 : m_pCurrCount = m_pCounts; 240 14 : m_pCurrValues = m_pValues; 241 : 242 : // Get the minimum value. this can be subtracted from each value in 243 : // the image. 244 14 : m_nMin = findMin(&m_nNumBits); 245 : 246 : // Go through the block. 247 14 : GUInt32 u32Last = valueAsUInt32(0); 248 67585 : for (GUInt32 count = 1; count < m_nBlockCount; count++) 249 : { 250 67572 : const GUInt32 u32Val = valueAsUInt32(count); 251 67572 : if (u32Val != u32Last) 252 : { 253 : // The values have changed - i.e. a run has come to and end. 254 5821 : encodeValue(u32Last, count - nLastUnique); 255 : 256 5821 : if ((m_pCurrValues - m_pValues) > static_cast<int>(m_nBlockSize)) 257 : { 258 1 : return false; 259 : } 260 : 261 5820 : m_nNumRuns++; 262 5820 : u32Last = u32Val; 263 5820 : nLastUnique = count; 264 : } 265 : } 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 13 : encodeValue(u32Last, m_nBlockCount - nLastUnique); 270 13 : m_nNumRuns++; 271 : 272 : // Set the size variables. 273 13 : m_nSizeCounts = static_cast<GUInt32>(m_pCurrCount - m_pCounts); 274 13 : 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 13 : return (m_nSizeCounts + m_nSizeValues + 13) < m_nBlockSize; 279 : } 280 : 281 15 : bool HFACompress::QueryDataTypeSupported(EPTType eHFADataType) 282 : { 283 15 : const int nBits = HFAGetDataTypeBits(eHFADataType); 284 : 285 14 : return nBits == 1 || nBits == 2 || nBits == 4 || nBits == 8 || 286 29 : nBits == 16 || nBits == 32; 287 : }