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-11-21 22:18:42 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             :  * 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             : }

Generated by: LCOV version 1.14