LCOV - code coverage report
Current view: top level - frmts/hfa - hfacompress.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 98 126 77.8 %
Date: 2024-05-06 13:02:59 Functions: 9 9 100.0 %

          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             : }

Generated by: LCOV version 1.14