File indexing completed on 2025-01-19 03:56:10

0001 /*
0002  * The Progressive Graphics File; http://www.libpgf.org
0003  *
0004  * $Date: 2007-02-03 13:04:21 +0100 (Sa, 03 Feb 2007) $
0005  * $Revision: 280 $
0006  *
0007  * This file Copyright (C) 2006 xeraina GmbH, Switzerland
0008  *
0009  * This program is free software; you can redistribute it and/or
0010  * modify it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
0011  * as published by the Free Software Foundation; either version 2.1
0012  * of the License, or (at your option) any later version.
0013  *
0014  * This program is distributed in the hope that it will be useful,
0015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0017  * GNU General Public License for more details.
0018  *
0019  * You should have received a copy of the GNU General Public License
0020  * along with this program; if not, write to the Free Software
0021  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
0022  */
0023 
0024 //////////////////////////////////////////////////////////////////////
0025 /// @file PGFimage.cpp
0026 /// @brief PGF image class implementation
0027 /// @author C. Stamm
0028 
0029 #include "PGFimage.h"
0030 #include "Decoder.h"
0031 #include "Encoder.h"
0032 #include "BitStream.h"
0033 #include <cmath>
0034 #include <cstring>
0035 
0036 #define YUVoffset4      8               // 2^3
0037 #define YUVoffset6      32              // 2^5
0038 #define YUVoffset8      128             // 2^7
0039 #define YUVoffset16     32768           // 2^15
0040 //#define YUVoffset31       1073741824      // 2^30
0041 
0042 //////////////////////////////////////////////////////////////////////
0043 // global methods and variables
0044 #ifdef NEXCEPTIONS
0045     OSError _PGF_Error_;
0046 
0047     OSError GetLastPGFError() {
0048         OSError tmp = _PGF_Error_;
0049         _PGF_Error_ = NoError;
0050         return tmp;
0051     }
0052 #endif
0053 
0054 #ifdef _DEBUG
0055     // allows RGB and RGBA image visualization inside Visual Studio Debugger
0056     struct DebugBGRImage {
0057         int width, height, pitch;
0058         BYTE *data;
0059     } roiimage;
0060 #endif
0061 
0062 //////////////////////////////////////////////////////////////////////
0063 // Standard constructor
0064 CPGFImage::CPGFImage() {
0065     Init();
0066 }
0067 
0068 //////////////////////////////////////////////////////////////////////
0069 void CPGFImage::Init() {
0070     // init pointers
0071     m_decoder = nullptr;
0072     m_encoder = nullptr;
0073     m_levelLength = nullptr;
0074 
0075     // init members
0076 #ifdef __PGFROISUPPORT__
0077     m_streamReinitialized = false;
0078 #endif
0079     m_currentLevel = 0;
0080     m_quant = 0;
0081     m_userDataPos = 0;
0082     m_downsample = false;
0083     m_favorSpeedOverSize = false;
0084     m_useOMPinEncoder = true;
0085     m_useOMPinDecoder = true;
0086     m_cb = nullptr;
0087     m_cbArg = nullptr;
0088     m_progressMode = PM_Relative;
0089     m_percent = 0;
0090     m_userDataPolicy = UP_CacheAll;
0091 
0092     // init preHeader
0093     memcpy(m_preHeader.magic, PGFMagic, 3);
0094     m_preHeader.version = PGFVersion;
0095     m_preHeader.hSize = 0;
0096 
0097     // init postHeader
0098     m_postHeader.userData = nullptr;
0099     m_postHeader.userDataLen = 0;
0100     m_postHeader.cachedUserDataLen = 0;
0101 
0102     // init channels
0103     for (int i = 0; i < MaxChannels; i++) {
0104         m_channel[i] = nullptr;
0105         m_wtChannel[i] = nullptr;
0106     }
0107 
0108     // set image width and height
0109     for (int i = 0; i < MaxChannels; i++) {
0110         m_width[0] = 0;
0111         m_height[0] = 0;
0112     }
0113 }
0114 
0115 //////////////////////////////////////////////////////////////////////
0116 // Destructor: Destroy internal data structures.
0117 CPGFImage::~CPGFImage() {
0118     m_currentLevel = -100; // unusual value used as marker in Destroy()
0119     Destroy();
0120 }
0121 
0122 //////////////////////////////////////////////////////////////////////
0123 // Destroy internal data structures. Object state after this is the same as after CPGFImage().
0124 void CPGFImage::Destroy() {
0125     for (int i = 0; i < m_header.channels; i++) {
0126         delete m_wtChannel[i]; // also deletes m_channel
0127     }
0128     delete[] m_postHeader.userData;
0129     delete[] m_levelLength;
0130     delete m_decoder;
0131     delete m_encoder;
0132 
0133     if (m_currentLevel != -100) Init();
0134 }
0135 
0136 /////////////////////////////////////////////////////////////////////////////
0137 // Open a PGF image at current stream position: read pre-header, header, levelLength, and ckeck image type.
0138 // Precondition: The stream has been opened for reading.
0139 // It might throw an IOException.
0140 // @param stream A PGF stream
0141 void CPGFImage::Open(CPGFStream *stream) {
0142     ASSERT(stream);
0143 
0144     // create decoder and read PGFPreHeader PGFHeader PGFPostHeader LevelLengths
0145     m_decoder = new CDecoder(stream, m_preHeader, m_header, m_postHeader, m_levelLength,
0146         m_userDataPos, m_useOMPinDecoder, m_userDataPolicy);
0147 
0148     if (m_header.nLevels > MaxLevel) ReturnWithError(FormatCannotRead);
0149 
0150     // set current level
0151     m_currentLevel = m_header.nLevels;
0152 
0153     // set image width and height
0154     m_width[0] = m_header.width;
0155     m_height[0] = m_header.height;
0156 
0157     // complete header
0158     if (!CompleteHeader()) ReturnWithError(FormatCannotRead);
0159 
0160     // interpret quant parameter
0161     if (m_header.quality > DownsampleThreshold &&
0162         (m_header.mode == ImageModeRGBColor ||
0163          m_header.mode == ImageModeRGBA ||
0164          m_header.mode == ImageModeRGB48 ||
0165          m_header.mode == ImageModeCMYKColor ||
0166          m_header.mode == ImageModeCMYK64 ||
0167          m_header.mode == ImageModeLabColor ||
0168          m_header.mode == ImageModeLab48)) {
0169         m_downsample = true;
0170         m_quant = m_header.quality - 1;
0171     } else {
0172         m_downsample = false;
0173         m_quant = m_header.quality;
0174     }
0175 
0176     // set channel dimensions (chrominance is subsampled by factor 2)
0177     if (m_downsample) {
0178         for (int i=1; i < m_header.channels; i++) {
0179             m_width[i] = (m_width[0] + 1) >> 1;
0180             m_height[i] = (m_height[0] + 1) >> 1;
0181         }
0182     } else {
0183         for (int i=1; i < m_header.channels; i++) {
0184             m_width[i] = m_width[0];
0185             m_height[i] = m_height[0];
0186         }
0187     }
0188 
0189     if (m_header.nLevels > 0) {
0190         // init wavelet subbands
0191         for (int i=0; i < m_header.channels; i++) {
0192             m_wtChannel[i] = new CWaveletTransform(m_width[i], m_height[i], m_header.nLevels);
0193         }
0194 
0195         // used in Read when PM_Absolute
0196         m_percent = pow(0.25, m_header.nLevels);
0197 
0198     } else {
0199         // very small image: we don't use DWT and encoding
0200 
0201         // read channels
0202         for (int c=0; c < m_header.channels; c++) {
0203             const UINT32 size = m_width[c]*m_height[c];
0204             m_channel[c] = new(std::nothrow) DataT[size];
0205             if (!m_channel[c]) ReturnWithError(InsufficientMemory);
0206 
0207             // read channel data from stream
0208             for (UINT32 i=0; i < size; i++) {
0209                 int count = DataTSize;
0210                 stream->Read(&count, &m_channel[c][i]);
0211                 if (count != DataTSize) ReturnWithError(MissingData);
0212             }
0213         }
0214     }
0215 }
0216 
0217 ////////////////////////////////////////////////////////////
0218 bool CPGFImage::CompleteHeader() {
0219     // set current codec version
0220     m_header.version = PGFVersionNumber(PGFMajorNumber, PGFYear, PGFWeek);
0221 
0222     if (m_header.mode == ImageModeUnknown) {
0223         // undefined mode
0224         switch(m_header.bpp) {
0225         case 1: m_header.mode = ImageModeBitmap; break;
0226         case 8: m_header.mode = ImageModeGrayScale; break;
0227         case 12: m_header.mode = ImageModeRGB12; break;
0228         case 16: m_header.mode = ImageModeRGB16; break;
0229         case 24: m_header.mode = ImageModeRGBColor; break;
0230         case 32: m_header.mode = ImageModeRGBA; break;
0231         case 48: m_header.mode = ImageModeRGB48; break;
0232         default: m_header.mode = ImageModeRGBColor; break;
0233         }
0234     }
0235     if (!m_header.bpp) {
0236         // undefined bpp
0237         switch(m_header.mode) {
0238         case ImageModeBitmap:
0239             m_header.bpp = 1;
0240             break;
0241         case ImageModeIndexedColor:
0242         case ImageModeGrayScale:
0243             m_header.bpp = 8;
0244             break;
0245         case ImageModeRGB12:
0246             m_header.bpp = 12;
0247             break;
0248         case ImageModeRGB16:
0249         case ImageModeGray16:
0250             m_header.bpp = 16;
0251             break;
0252         case ImageModeRGBColor:
0253         case ImageModeLabColor:
0254             m_header.bpp = 24;
0255             break;
0256         case ImageModeRGBA:
0257         case ImageModeCMYKColor:
0258         case ImageModeGray32:
0259             m_header.bpp = 32;
0260             break;
0261         case ImageModeRGB48:
0262         case ImageModeLab48:
0263             m_header.bpp = 48;
0264             break;
0265         case ImageModeCMYK64:
0266             m_header.bpp = 64;
0267             break;
0268         default:
0269             ASSERT(false);
0270             m_header.bpp = 24;
0271         }
0272     }
0273     if (m_header.mode == ImageModeRGBColor && m_header.bpp == 32) {
0274         // change mode
0275         m_header.mode = ImageModeRGBA;
0276     }
0277     if (m_header.mode == ImageModeBitmap && m_header.bpp != 1) return false;
0278     if (m_header.mode == ImageModeIndexedColor && m_header.bpp != 8) return false;
0279     if (m_header.mode == ImageModeGrayScale && m_header.bpp != 8) return false;
0280     if (m_header.mode == ImageModeGray16 && m_header.bpp != 16) return false;
0281     if (m_header.mode == ImageModeGray32 && m_header.bpp != 32) return false;
0282     if (m_header.mode == ImageModeRGBColor && m_header.bpp != 24) return false;
0283     if (m_header.mode == ImageModeRGBA && m_header.bpp != 32) return false;
0284     if (m_header.mode == ImageModeRGB12 && m_header.bpp != 12) return false;
0285     if (m_header.mode == ImageModeRGB16 && m_header.bpp != 16) return false;
0286     if (m_header.mode == ImageModeRGB48 && m_header.bpp != 48) return false;
0287     if (m_header.mode == ImageModeLabColor && m_header.bpp != 24) return false;
0288     if (m_header.mode == ImageModeLab48 && m_header.bpp != 48) return false;
0289     if (m_header.mode == ImageModeCMYKColor && m_header.bpp != 32) return false;
0290     if (m_header.mode == ImageModeCMYK64 && m_header.bpp != 64) return false;
0291 
0292     // set number of channels
0293     if (!m_header.channels) {
0294         switch(m_header.mode) {
0295         case ImageModeBitmap:
0296         case ImageModeIndexedColor:
0297         case ImageModeGrayScale:
0298         case ImageModeGray16:
0299         case ImageModeGray32:
0300             m_header.channels = 1;
0301             break;
0302         case ImageModeRGBColor:
0303         case ImageModeRGB12:
0304         case ImageModeRGB16:
0305         case ImageModeRGB48:
0306         case ImageModeLabColor:
0307         case ImageModeLab48:
0308             m_header.channels = 3;
0309             break;
0310         case ImageModeRGBA:
0311         case ImageModeCMYKColor:
0312         case ImageModeCMYK64:
0313             m_header.channels = 4;
0314             break;
0315         default:
0316             return false;
0317         }
0318     }
0319 
0320     // store used bits per channel
0321     UINT8 bpc = m_header.bpp/m_header.channels;
0322     if (bpc > 31) bpc = 31;
0323     if (!m_header.usedBitsPerChannel || m_header.usedBitsPerChannel > bpc) {
0324         m_header.usedBitsPerChannel = bpc;
0325     }
0326 
0327     return true;
0328 }
0329 
0330 //////////////////////////////////////////////////////////////////////
0331 /// Return user data and size of user data.
0332 /// Precondition: The PGF image has been opened with a call of Open(...).
0333 /// In an encoder scenario don't call this method before WriteHeader().
0334 /// @param cachedSize [out] Size of returned user data in bytes.
0335 /// @param pTotalSize [optional out] Pointer to return the size of user data stored in image header in bytes.
0336 /// @return A pointer to user data or nullptr if there is no user data available.
0337 const UINT8* CPGFImage::GetUserData(UINT32& cachedSize, UINT32* pTotalSize /*= nullptr*/) const {
0338     cachedSize = m_postHeader.cachedUserDataLen;
0339     if (pTotalSize) *pTotalSize = m_postHeader.userDataLen;
0340     return m_postHeader.userData;
0341 }
0342 
0343 //////////////////////////////////////////////////////////////////////
0344 /// After you've written a PGF image, you can call this method followed by GetBitmap/GetYUV
0345 /// to get a quick reconstruction (coded -> decoded image).
0346 /// It might throw an IOException.
0347 /// @param level The image level of the resulting image in the internal image buffer.
0348 void CPGFImage::Reconstruct(int level /*= 0*/) {
0349     if (m_header.nLevels == 0) {
0350         // image didn't use wavelet transform
0351         if (level == 0) {
0352             for (int i=0; i < m_header.channels; i++) {
0353                 ASSERT(m_wtChannel[i]);
0354                 m_channel[i] = m_wtChannel[i]->GetSubband(0, LL)->GetBuffer();
0355             }
0356         }
0357     } else {
0358         int currentLevel = m_header.nLevels;
0359 
0360     #ifdef __PGFROISUPPORT__
0361         if (ROIisSupported()) {
0362             // enable ROI reading
0363             SetROI(PGFRect(0, 0, m_header.width, m_header.height));
0364         }
0365     #endif
0366 
0367         while (currentLevel > level) {
0368             for (int i=0; i < m_header.channels; i++) {
0369                 ASSERT(m_wtChannel[i]);
0370                 // dequantize subbands
0371                 if (currentLevel == m_header.nLevels) {
0372                     // last level also has LL band
0373                     m_wtChannel[i]->GetSubband(currentLevel, LL)->Dequantize(m_quant);
0374                 }
0375                 m_wtChannel[i]->GetSubband(currentLevel, HL)->Dequantize(m_quant);
0376                 m_wtChannel[i]->GetSubband(currentLevel, LH)->Dequantize(m_quant);
0377                 m_wtChannel[i]->GetSubband(currentLevel, HH)->Dequantize(m_quant);
0378 
0379                 // inverse transform from m_wtChannel to m_channel
0380                 OSError err = m_wtChannel[i]->InverseTransform(currentLevel, &m_width[i], &m_height[i], &m_channel[i]);
0381                 if (err != NoError) ReturnWithError(err);
0382                 ASSERT(m_channel[i]);
0383             }
0384 
0385             currentLevel--;
0386         }
0387     }
0388 }
0389 
0390 //////////////////////////////////////////////////////////////////////
0391 // Read and decode some levels of a PGF image at current stream position.
0392 // A PGF image is structered in levels, numbered between 0 and Levels() - 1.
0393 // Each level can be seen as a single image, containing the same content
0394 // as all other levels, but in a different size (width, height).
0395 // The image size at level i is double the size (width, height) of the image at level i+1.
0396 // The image at level 0 contains the original size.
0397 // Precondition: The PGF image has been opened with a call of Open(...).
0398 // It might throw an IOException.
0399 // @param level The image level of the resulting image in the internal image buffer.
0400 // @param cb A pointer to a callback procedure. The procedure is called after reading a single level. If cb returns true, then it stops proceeding.
0401 // @param data Data Pointer to C++ class container to host callback procedure.
0402 void CPGFImage::Read(int level /*= 0*/, CallbackPtr cb /*= nullptr*/, void *data /*=nullptr*/) {
0403     ASSERT((level >= 0 && level < m_header.nLevels) || m_header.nLevels == 0); // m_header.nLevels == 0: image didn't use wavelet transform
0404     ASSERT(m_decoder);
0405 
0406 #ifdef __PGFROISUPPORT__
0407     if (ROIisSupported() && m_header.nLevels > 0) {
0408         // new encoding scheme supporting ROI
0409         PGFRect rect(0, 0, m_header.width, m_header.height);
0410         Read(rect, level, cb, data);
0411         return;
0412     }
0413 #endif
0414 
0415     if (m_header.nLevels == 0) {
0416         if (level == 0) {
0417             // the data has already been read during open
0418             // now update progress
0419             if (cb) {
0420                 if ((*cb)(1.0, true, data)) ReturnWithError(EscapePressed);
0421             }
0422         }
0423     } else {
0424         const int levelDiff = m_currentLevel - level;
0425         double percent = (m_progressMode == PM_Relative) ? pow(0.25, levelDiff) : m_percent;
0426 
0427         // encoding scheme without ROI
0428         while (m_currentLevel > level) {
0429             for (int i=0; i < m_header.channels; i++) {
0430                 CWaveletTransform* wtChannel = m_wtChannel[i];
0431                 ASSERT(wtChannel);
0432 
0433                 // decode file and write stream to m_wtChannel
0434                 if (m_currentLevel == m_header.nLevels) {
0435                     // last level also has LL band
0436                     wtChannel->GetSubband(m_currentLevel, LL)->PlaceTile(*m_decoder, m_quant);
0437                 }
0438                 if (m_preHeader.version & Version5) {
0439                     // since version 5
0440                     wtChannel->GetSubband(m_currentLevel, HL)->PlaceTile(*m_decoder, m_quant);
0441                     wtChannel->GetSubband(m_currentLevel, LH)->PlaceTile(*m_decoder, m_quant);
0442                 } else {
0443                     // until version 4
0444                     m_decoder->DecodeInterleaved(wtChannel, m_currentLevel, m_quant);
0445                 }
0446                 wtChannel->GetSubband(m_currentLevel, HH)->PlaceTile(*m_decoder, m_quant);
0447             }
0448 
0449             volatile OSError error = NoError; // volatile prevents optimizations
0450 #ifdef LIBPGF_USE_OPENMP
0451             #pragma omp parallel for default(shared)
0452 #endif
0453             for (int i=0; i < m_header.channels; i++) {
0454                 // inverse transform from m_wtChannel to m_channel
0455                 if (error == NoError) {
0456                     OSError err = m_wtChannel[i]->InverseTransform(m_currentLevel, &m_width[i], &m_height[i], &m_channel[i]);
0457                     if (err != NoError) error = err;
0458                 }
0459                 ASSERT(m_channel[i]);
0460             }
0461             if (error != NoError) ReturnWithError(error);
0462 
0463             // set new level: must be done before refresh callback
0464             m_currentLevel--;
0465 
0466             // now we have to refresh the display
0467             if (m_cb) m_cb(m_cbArg);
0468 
0469             // now update progress
0470             if (cb) {
0471                 percent *= 4;
0472                 if (m_progressMode == PM_Absolute) m_percent = percent;
0473                 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
0474             }
0475         }
0476     }
0477 }
0478 
0479 #ifdef __PGFROISUPPORT__
0480 //////////////////////////////////////////////////////////////////////
0481 /// Read and decode rectangular region of interest (ROI) of a PGF image at current stream position.
0482 /// The origin of the coordinate axis is the top-left corner of the image.
0483 /// All coordinates are measured in pixels.
0484 /// It might throw an IOException.
0485 /// @param rect [inout] Rectangular region of interest (ROI) at level 0. The rect might be cropped.
0486 /// @param level The image level of the resulting image in the internal image buffer.
0487 /// @param cb A pointer to a callback procedure. The procedure is called after reading a single level. If cb returns true, then it stops proceeding.
0488 /// @param data Data Pointer to C++ class container to host callback procedure.
0489 void CPGFImage::Read(PGFRect& rect, int level /*= 0*/, CallbackPtr cb /*= nullptr*/, void *data /*=nullptr*/) {
0490     ASSERT((level >= 0 && level < m_header.nLevels) || m_header.nLevels == 0); // m_header.nLevels == 0: image didn't use wavelet transform
0491     ASSERT(m_decoder);
0492 
0493     if (m_header.nLevels == 0 || !ROIisSupported()) {
0494         rect.left = rect.top = 0;
0495         rect.right = m_header.width; rect.bottom = m_header.height;
0496         Read(level, cb, data);
0497     } else {
0498         ASSERT(ROIisSupported());
0499         // new encoding scheme supporting ROI
0500         ASSERT(rect.left < m_header.width && rect.top < m_header.height);
0501 
0502         // check rectangle
0503         if (rect.right == 0 || rect.right > m_header.width) rect.right = m_header.width;
0504         if (rect.bottom == 0 || rect.bottom > m_header.height) rect.bottom = m_header.height;
0505 
0506         const int levelDiff = m_currentLevel - level;
0507         double percent = (m_progressMode == PM_Relative) ? pow(0.25, levelDiff) : m_percent;
0508 
0509         // check level difference
0510         if (levelDiff <= 0) {
0511             // it is a new read call, probably with a new ROI
0512             m_currentLevel = m_header.nLevels;
0513             m_decoder->SetStreamPosToData();
0514         }
0515 
0516         // enable ROI decoding and reading
0517         SetROI(rect);
0518 
0519         while (m_currentLevel > level) {
0520             for (int i=0; i < m_header.channels; i++) {
0521                 CWaveletTransform* wtChannel = m_wtChannel[i];
0522                 ASSERT(wtChannel);
0523 
0524                 // get number of tiles and tile indices
0525                 const UINT32 nTiles = wtChannel->GetNofTiles(m_currentLevel); // independent of ROI
0526 
0527                 // decode file and write stream to m_wtChannel
0528                 if (m_currentLevel == m_header.nLevels) { // last level also has LL band
0529                     ASSERT(nTiles == 1);
0530                     m_decoder->GetNextMacroBlock();
0531                     wtChannel->GetSubband(m_currentLevel, LL)->PlaceTile(*m_decoder, m_quant);
0532                 }
0533                 for (UINT32 tileY=0; tileY < nTiles; tileY++) {
0534                     for (UINT32 tileX=0; tileX < nTiles; tileX++) {
0535                         // check relevance of tile
0536                         if (wtChannel->TileIsRelevant(m_currentLevel, tileX, tileY)) {
0537                             m_decoder->GetNextMacroBlock();
0538                             wtChannel->GetSubband(m_currentLevel, HL)->PlaceTile(*m_decoder, m_quant, true, tileX, tileY);
0539                             wtChannel->GetSubband(m_currentLevel, LH)->PlaceTile(*m_decoder, m_quant, true, tileX, tileY);
0540                             wtChannel->GetSubband(m_currentLevel, HH)->PlaceTile(*m_decoder, m_quant, true, tileX, tileY);
0541                         } else {
0542                             // skip tile
0543                             m_decoder->SkipTileBuffer();
0544                         }
0545                     }
0546                 }
0547             }
0548 
0549             volatile OSError error = NoError; // volatile prevents optimizations
0550 #ifdef LIBPGF_USE_OPENMP
0551             #pragma omp parallel for default(shared)
0552 #endif
0553             for (int i=0; i < m_header.channels; i++) {
0554                 // inverse transform from m_wtChannel to m_channel
0555                 if (error == NoError) {
0556                     OSError err = m_wtChannel[i]->InverseTransform(m_currentLevel, &m_width[i], &m_height[i], &m_channel[i]);
0557                     if (err != NoError) error = err;
0558                 }
0559                 ASSERT(m_channel[i]);
0560             }
0561             if (error != NoError) ReturnWithError(error);
0562 
0563             // set new level: must be done before refresh callback
0564             m_currentLevel--;
0565 
0566             // now we have to refresh the display
0567             if (m_cb) m_cb(m_cbArg);
0568 
0569             // now update progress
0570             if (cb) {
0571                 percent *= 4;
0572                 if (m_progressMode == PM_Absolute) m_percent = percent;
0573                 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
0574             }
0575         }
0576     }
0577 }
0578 
0579 //////////////////////////////////////////////////////////////////////
0580 /// Return ROI of channel 0 at current level in pixels.
0581 /// The returned rect is only valid after reading a ROI.
0582 /// @return ROI in pixels
0583 PGFRect CPGFImage::ComputeLevelROI() const {
0584     if (m_currentLevel == 0) {
0585         return m_roi;
0586     } else {
0587         const UINT32 rLeft = LevelSizeL(m_roi.left, m_currentLevel);
0588         const UINT32 rRight = LevelSizeL(m_roi.right, m_currentLevel);
0589         const UINT32 rTop = LevelSizeL(m_roi.top, m_currentLevel);
0590         const UINT32 rBottom = LevelSizeL(m_roi.bottom, m_currentLevel);
0591         return PGFRect(rLeft, rTop, rRight - rLeft, rBottom - rTop);
0592     }
0593 }
0594 
0595 //////////////////////////////////////////////////////////////////////
0596 /// Returns aligned ROI in pixels of current level of channel c
0597 /// @param c A channel index
0598 PGFRect CPGFImage::GetAlignedROI(int c /*= 0*/) const {
0599     PGFRect roi(0, 0, m_width[c], m_height[c]);
0600 
0601     if (ROIisSupported()) {
0602         ASSERT(m_wtChannel[c]);
0603 
0604         roi = m_wtChannel[c]->GetAlignedROI(m_currentLevel);
0605     }
0606     ASSERT(roi.Width() == m_width[c]);
0607     ASSERT(roi.Height() == m_height[c]);
0608     return roi;
0609 }
0610 
0611 //////////////////////////////////////////////////////////////////////
0612 /// Compute ROIs for each channel and each level <= current level
0613 /// Called inside of Read(rect, ...).
0614 /// @param rect rectangular region of interest (ROI) at level 0
0615 void CPGFImage::SetROI(PGFRect rect) {
0616     ASSERT(m_decoder);
0617     ASSERT(ROIisSupported());
0618     ASSERT(m_wtChannel[0]);
0619 
0620     // store ROI for a later call of GetBitmap
0621     m_roi = rect;
0622 
0623     // enable ROI decoding
0624     m_decoder->SetROI();
0625 
0626     // prepare wavelet channels for using ROI
0627     m_wtChannel[0]->SetROI(rect);
0628 
0629     if (m_downsample && m_header.channels > 1) {
0630         // all further channels are downsampled, therefore downsample ROI
0631         rect.left >>= 1;
0632         rect.top >>= 1;
0633         rect.right = (rect.right + 1) >> 1;
0634         rect.bottom = (rect.bottom + 1) >> 1;
0635     }
0636     for (int i=1; i < m_header.channels; i++) {
0637         ASSERT(m_wtChannel[i]);
0638         m_wtChannel[i]->SetROI(rect);
0639     }
0640 }
0641 
0642 #endif // __PGFROISUPPORT__
0643 
0644 //////////////////////////////////////////////////////////////////////
0645 /// Return the length of all encoded headers in bytes.
0646 /// Precondition: The PGF image has been opened with a call of Open(...).
0647 /// @return The length of all encoded headers in bytes
0648 UINT32 CPGFImage::GetEncodedHeaderLength() const {
0649     ASSERT(m_decoder);
0650     return m_decoder->GetEncodedHeaderLength();
0651 }
0652 
0653 //////////////////////////////////////////////////////////////////////
0654 /// Reads the encoded PGF header and copies it to a target buffer.
0655 /// Precondition: The PGF image has been opened with a call of Open(...).
0656 /// It might throw an IOException.
0657 /// @param target The target buffer
0658 /// @param targetLen The length of the target buffer in bytes
0659 /// @return The number of bytes copied to the target buffer
0660 UINT32 CPGFImage::ReadEncodedHeader(UINT8* target, UINT32 targetLen) const {
0661     ASSERT(target);
0662     ASSERT(targetLen > 0);
0663     ASSERT(m_decoder);
0664 
0665     // reset stream position
0666     m_decoder->SetStreamPosToStart();
0667 
0668     // compute number of bytes to read
0669     UINT32 len = __min(targetLen, GetEncodedHeaderLength());
0670 
0671     // read data
0672     len = m_decoder->ReadEncodedData(target, len);
0673     ASSERT(len >= 0 && len <= targetLen);
0674 
0675     return len;
0676 }
0677 
0678 ////////////////////////////////////////////////////////////////////
0679 /// Reset stream position to start of PGF pre-header or start of data. Must not be called before Open() or before Write().
0680 /// Use this method after Read() if you want to read the same image several times, e.g. reading different ROIs.
0681 /// @param startOfData true: you want to read the same image several times. false: resets stream position to the initial position
0682 void CPGFImage::ResetStreamPos(bool startOfData) {
0683     m_currentLevel = 0;
0684     if (startOfData) {
0685         ASSERT(m_decoder);
0686         m_decoder->SetStreamPosToData();
0687     } else {
0688         if (m_decoder) {
0689             m_decoder->SetStreamPosToStart();
0690         } else if (m_encoder) {
0691             m_encoder->SetStreamPosToStart();
0692         } else {
0693             ASSERT(false);
0694         }
0695     }
0696 }
0697 
0698 //////////////////////////////////////////////////////////////////////
0699 /// Reads the data of an encoded PGF level and copies it to a target buffer
0700 /// without decoding.
0701 /// Precondition: The PGF image has been opened with a call of Open(...).
0702 /// It might throw an IOException.
0703 /// @param level The image level
0704 /// @param target The target buffer
0705 /// @param targetLen The length of the target buffer in bytes
0706 /// @return The number of bytes copied to the target buffer
0707 UINT32 CPGFImage::ReadEncodedData(int level, UINT8* target, UINT32 targetLen) const {
0708     ASSERT(level >= 0 && level < m_header.nLevels);
0709     ASSERT(target);
0710     ASSERT(targetLen > 0);
0711     ASSERT(m_decoder);
0712 
0713     // reset stream position
0714     m_decoder->SetStreamPosToData();
0715 
0716     // position stream
0717     UINT64 offset = 0;
0718 
0719     for (int i=m_header.nLevels - 1; i > level; i--) {
0720         offset += m_levelLength[m_header.nLevels - 1 - i];
0721     }
0722     m_decoder->Skip(offset);
0723 
0724     // compute number of bytes to read
0725     UINT32 len = __min(targetLen, GetEncodedLevelLength(level));
0726 
0727     // read data
0728     len = m_decoder->ReadEncodedData(target, len);
0729     ASSERT(len >= 0 && len <= targetLen);
0730 
0731     return len;
0732 }
0733 
0734 //////////////////////////////////////////////////////////////////////
0735 /// Set maximum intensity value for image modes with more than eight bits per channel.
0736 /// Call this method after SetHeader, but before ImportBitmap.
0737 /// @param maxValue The maximum intensity value.
0738 void CPGFImage::SetMaxValue(UINT32 maxValue) {
0739     const BYTE bpc = m_header.bpp/m_header.channels;
0740     BYTE pot = 0;
0741 
0742     while(maxValue > 0) {
0743         pot++;
0744         maxValue >>= 1;
0745     }
0746     // store bits per channel
0747     if (pot > bpc) pot = bpc;
0748     if (pot > 31) pot = 31;
0749     m_header.usedBitsPerChannel = pot;
0750 }
0751 
0752 //////////////////////////////////////////////////////////////////////
0753 /// Returns number of used bits per input/output image channel.
0754 /// Precondition: header must be initialized.
0755 /// @return number of used bits per input/output image channel.
0756 BYTE CPGFImage::UsedBitsPerChannel() const {
0757     const BYTE bpc = m_header.bpp/m_header.channels;
0758 
0759     if (bpc > 8) {
0760         return m_header.usedBitsPerChannel;
0761     } else {
0762         return bpc;
0763     }
0764 }
0765 
0766 //////////////////////////////////////////////////////////////////////
0767 /// Return major version
0768 BYTE CPGFImage::CodecMajorVersion(BYTE version) {
0769     if (version & Version7) return 7;
0770     if (version & Version6) return 6;
0771     if (version & Version5) return 5;
0772     if (version & Version2) return 2;
0773     return 1;
0774 }
0775 
0776 //////////////////////////////////////////////////////////////////
0777 // Import an image from a specified image buffer.
0778 // This method is usually called before Write(...) and after SetHeader(...).
0779 // It might throw an IOException.
0780 // The absolute value of pitch is the number of bytes of an image row.
0781 // If pitch is negative, then buff points to the last row of a bottom-up image (first byte on last row).
0782 // If pitch is positive, then buff points to the first row of a top-down image (first byte).
0783 // The sequence of input channels in the input image buffer does not need to be the same as expected from PGF. In case of different sequences you have to
0784 // provide a channelMap of size of expected channels (depending on image mode). For example, PGF expects in RGB color mode a channel sequence BGR.
0785 // If your provided image buffer contains a channel sequence ARGB, then the channelMap looks like { 3, 2, 1 }.
0786 // @param pitch The number of bytes of a row of the image buffer.
0787 // @param buff An image buffer.
0788 // @param bpp The number of bits per pixel used in image buffer.
0789 // @param channelMap A integer array containing the mapping of input channel ordering to expected channel ordering.
0790 // @param cb A pointer to a callback procedure. The procedure is called after each imported buffer row. If cb returns true, then it stops proceeding.
0791 // @param data Data Pointer to C++ class container to host callback procedure.
0792 void CPGFImage::ImportBitmap(int pitch, UINT8 *buff, BYTE bpp, int channelMap[] /*= nullptr */, CallbackPtr cb /*= nullptr*/, void *data /*=nullptr*/) {
0793     ASSERT(buff);
0794     ASSERT(m_channel[0]);
0795 
0796     // color transform
0797     RgbToYuv(pitch, buff, bpp, channelMap, cb, data);
0798 
0799     if (m_downsample) {
0800         // Subsampling of the chrominance and alpha channels
0801         for (int i=1; i < m_header.channels; i++) {
0802             Downsample(i);
0803         }
0804     }
0805 }
0806 
0807 /////////////////////////////////////////////////////////////////
0808 // Bilinerar Subsampling of channel ch by a factor 2
0809 // Called before Write()
0810 void CPGFImage::Downsample(int ch) {
0811     ASSERT(ch > 0);
0812 
0813     const int w = m_width[0];
0814     const int w2 = w/2;
0815     const int h2 = m_height[0]/2;
0816     const int oddW = w%2;               // don't use bool -> problems with MaxSpeed optimization
0817     const int oddH = m_height[0]%2;     // "
0818     int loPos = 0;
0819     int hiPos = w;
0820     int sampledPos = 0;
0821     DataT* buff = m_channel[ch]; ASSERT(buff);
0822 
0823     for (int i=0; i < h2; i++) {
0824         for (int j=0; j < w2; j++) {
0825             // compute average of pixel block
0826             buff[sampledPos] = (buff[loPos] + buff[loPos + 1] + buff[hiPos] + buff[hiPos + 1]) >> 2;
0827             loPos += 2; hiPos += 2;
0828             sampledPos++;
0829         }
0830         if (oddW) {
0831             buff[sampledPos] = (buff[loPos] + buff[hiPos]) >> 1;
0832             loPos++; hiPos++;
0833             sampledPos++;
0834         }
0835         loPos += w; hiPos += w;
0836     }
0837     if (oddH) {
0838         for (int j=0; j < w2; j++) {
0839             buff[sampledPos] = (buff[loPos] + buff[loPos+1]) >> 1;
0840             loPos += 2; hiPos += 2;
0841             sampledPos++;
0842         }
0843         if (oddW) {
0844             buff[sampledPos] = buff[loPos];
0845         }
0846     }
0847 
0848     // downsampled image has half width and half height
0849     m_width[ch] = (m_width[ch] + 1)/2;
0850     m_height[ch] = (m_height[ch] + 1)/2;
0851 }
0852 
0853 //////////////////////////////////////////////////////////////////////
0854 void CPGFImage::ComputeLevels() {
0855     const int maxThumbnailWidth = 20*FilterSize;
0856     const int m = __min(m_header.width, m_header.height);
0857     int s = m;
0858 
0859     if (m_header.nLevels < 1 || m_header.nLevels > MaxLevel) {
0860         m_header.nLevels = 1;
0861         // compute a good value depending on the size of the image
0862         while (s > maxThumbnailWidth) {
0863             m_header.nLevels++;
0864             s >>= 1;
0865         }
0866     }
0867 
0868     int levels = m_header.nLevels; // we need a signed value during level reduction
0869 
0870     // reduce number of levels if the image size is smaller than FilterSize*(2^levels)
0871     s = FilterSize*(1 << levels);   // must be at least the double filter size because of subsampling
0872     while (m < s) {
0873         levels--;
0874         s >>= 1;
0875     }
0876     if (levels > MaxLevel) m_header.nLevels = MaxLevel;
0877     else if (levels < 0) m_header.nLevels = 0;
0878     else m_header.nLevels = (UINT8)levels;
0879 
0880     // used in Write when PM_Absolute
0881     m_percent = pow(0.25, m_header.nLevels);
0882 
0883     ASSERT(0 <= m_header.nLevels && m_header.nLevels <= MaxLevel);
0884 }
0885 
0886 //////////////////////////////////////////////////////////////////////
0887 /// Set PGF header and user data.
0888 /// Precondition: The PGF image has been never opened with Open(...).
0889 /// It might throw an IOException.
0890 /// @param header A valid and already filled in PGF header structure
0891 /// @param flags A combination of additional version flags. In case you use level-wise encoding then set flag = PGFROI.
0892 /// @param userData A user-defined memory block containing any kind of cached metadata.
0893 /// @param userDataLength The size of user-defined memory block in bytes
0894 void CPGFImage::SetHeader(const PGFHeader& header, BYTE flags /*=0*/, const UINT8* userData /*= 0*/, UINT32 userDataLength /*= 0*/) {
0895     ASSERT(!m_decoder); // current image must be closed
0896     ASSERT(header.quality <= MaxQuality);
0897     ASSERT(userDataLength <= MaxUserDataSize);
0898 
0899     // init state
0900 #ifdef __PGFROISUPPORT__
0901     m_streamReinitialized = false;
0902 #endif
0903 
0904     // init preHeader
0905     memcpy(m_preHeader.magic, PGFMagic, 3);
0906     m_preHeader.version = PGFVersion | flags;
0907     m_preHeader.hSize = HeaderSize;
0908 
0909     // copy header
0910     memcpy(&m_header, &header, HeaderSize);
0911 
0912     // check quality
0913     if (m_header.quality > MaxQuality) m_header.quality = MaxQuality;
0914 
0915     // complete header
0916     CompleteHeader();
0917 
0918     // check and set number of levels
0919     ComputeLevels();
0920 
0921     // check for downsample
0922     if (m_header.quality > DownsampleThreshold &&  (m_header.mode == ImageModeRGBColor ||
0923                                                     m_header.mode == ImageModeRGBA ||
0924                                                     m_header.mode == ImageModeRGB48 ||
0925                                                     m_header.mode == ImageModeCMYKColor ||
0926                                                     m_header.mode == ImageModeCMYK64 ||
0927                                                     m_header.mode == ImageModeLabColor ||
0928                                                     m_header.mode == ImageModeLab48)) {
0929         m_downsample = true;
0930         m_quant = m_header.quality - 1;
0931     } else {
0932         m_downsample = false;
0933         m_quant = m_header.quality;
0934     }
0935 
0936     // update header size and copy user data
0937     if (m_header.mode == ImageModeIndexedColor) {
0938         // update header size
0939         m_preHeader.hSize += ColorTableSize;
0940     }
0941     if (userDataLength && userData) {
0942         if (userDataLength > MaxUserDataSize) userDataLength = MaxUserDataSize;
0943         m_postHeader.userData = new(std::nothrow) UINT8[userDataLength];
0944         if (!m_postHeader.userData) ReturnWithError(InsufficientMemory);
0945         m_postHeader.userDataLen = m_postHeader.cachedUserDataLen = userDataLength;
0946         memcpy(m_postHeader.userData, userData, userDataLength);
0947         // update header size
0948         m_preHeader.hSize += userDataLength;
0949     }
0950 
0951     // allocate channels
0952     for (int i=0; i < m_header.channels; i++) {
0953         // set current width and height
0954         m_width[i] = m_header.width;
0955         m_height[i] = m_header.height;
0956 
0957         // allocate channels
0958         ASSERT(!m_channel[i]);
0959         m_channel[i] = new(std::nothrow) DataT[m_header.width*m_header.height];
0960         if (!m_channel[i]) {
0961             if (i) i--;
0962             while(i) {
0963                 delete[] m_channel[i]; m_channel[i] = 0;
0964                 i--;
0965             }
0966             ReturnWithError(InsufficientMemory);
0967         }
0968     }
0969 }
0970 
0971 //////////////////////////////////////////////////////////////////
0972 /// Create wavelet transform channels and encoder. Write header at current stream position.
0973 /// Performs forward FWT.
0974 /// Call this method before your first call of Write(int level) or WriteImage(), but after SetHeader().
0975 /// This method is called inside of Write(stream, ...).
0976 /// It might throw an IOException.
0977 /// @param stream A PGF stream
0978 /// @return The number of bytes written into stream.
0979 UINT32 CPGFImage::WriteHeader(CPGFStream* stream) {
0980     ASSERT(m_header.nLevels <= MaxLevel);
0981     ASSERT(m_header.quality <= MaxQuality); // quality is already initialized
0982 
0983     if (m_header.nLevels > 0) {
0984         volatile OSError error = NoError; // volatile prevents optimizations
0985         // create new wt channels
0986 #ifdef LIBPGF_USE_OPENMP
0987         #pragma omp parallel for default(shared)
0988 #endif
0989         for (int i=0; i < m_header.channels; i++) {
0990             DataT *temp = nullptr;
0991             if (error == NoError) {
0992                 if (m_wtChannel[i]) {
0993                     ASSERT(m_channel[i]);
0994                     // copy m_channel to temp
0995                     int size = m_height[i]*m_width[i];
0996                     temp = new(std::nothrow) DataT[size];
0997                     if (temp) {
0998                         memcpy(temp, m_channel[i], size*DataTSize);
0999                         delete m_wtChannel[i];  // also deletes m_channel
1000                         m_channel[i] = nullptr;
1001                     } else {
1002                         error = InsufficientMemory;
1003                     }
1004                 }
1005                 if (error == NoError) {
1006                     if (temp) {
1007                         ASSERT(!m_channel[i]);
1008                         m_channel[i] = temp;
1009                     }
1010                     m_wtChannel[i] = new CWaveletTransform(m_width[i], m_height[i], m_header.nLevels, m_channel[i]);
1011                     if (m_wtChannel[i]) {
1012                     #ifdef __PGFROISUPPORT__
1013                         m_wtChannel[i]->SetROI(PGFRect(0, 0, m_width[i], m_height[i]));
1014                     #endif
1015 
1016                         // wavelet subband decomposition
1017                         for (int l=0; error == NoError && l < m_header.nLevels; l++) {
1018                             OSError err = m_wtChannel[i]->ForwardTransform(l, m_quant);
1019                             if (err != NoError) error = err;
1020                         }
1021                     } else {
1022                         delete[] m_channel[i];
1023                         error = InsufficientMemory;
1024                     }
1025                 }
1026             }
1027         }
1028         if (error != NoError) {
1029             // free already allocated memory
1030             for (int i=0; i < m_header.channels; i++) {
1031                 delete m_wtChannel[i];
1032             }
1033             ReturnWithError(error);
1034         }
1035 
1036         m_currentLevel = m_header.nLevels;
1037 
1038         // create encoder, write headers and user data, but not level-length area
1039         m_encoder = new CEncoder(stream, m_preHeader, m_header, m_postHeader, m_userDataPos, m_useOMPinEncoder);
1040         if (m_favorSpeedOverSize) m_encoder->FavorSpeedOverSize();
1041 
1042     #ifdef __PGFROISUPPORT__
1043         if (ROIisSupported()) {
1044             // new encoding scheme supporting ROI
1045             m_encoder->SetROI();
1046         }
1047     #endif
1048 
1049     } else {
1050         // very small image: we don't use DWT and encoding
1051 
1052         // create encoder, write headers and user data, but not level-length area
1053         m_encoder = new CEncoder(stream, m_preHeader, m_header, m_postHeader, m_userDataPos, m_useOMPinEncoder);
1054     }
1055 
1056     INT64 nBytes = m_encoder->ComputeHeaderLength();
1057     return (nBytes > 0) ? (UINT32)nBytes : 0;
1058 }
1059 
1060 //////////////////////////////////////////////////////////////////
1061 // Encode and write next level of a PGF image at current stream position.
1062 // A PGF image is structered in levels, numbered between 0 and Levels() - 1.
1063 // Each level can be seen as a single image, containing the same content
1064 // as all other levels, but in a different size (width, height).
1065 // The image size at level i is double the size (width, height) of the image at level i+1.
1066 // The image at level 0 contains the original size.
1067 // It might throw an IOException.
1068 void CPGFImage::WriteLevel() {
1069     ASSERT(m_encoder);
1070     ASSERT(m_currentLevel > 0);
1071     ASSERT(m_header.nLevels > 0);
1072 
1073 #ifdef __PGFROISUPPORT__
1074     if (ROIisSupported()) {
1075         const int lastChannel = m_header.channels - 1;
1076 
1077         for (int i=0; i < m_header.channels; i++) {
1078             // get number of tiles and tile indices
1079             const UINT32 nTiles = m_wtChannel[i]->GetNofTiles(m_currentLevel);
1080             const UINT32 lastTile = nTiles - 1;
1081 
1082             if (m_currentLevel == m_header.nLevels) {
1083                 // last level also has LL band
1084                 ASSERT(nTiles == 1);
1085                 m_wtChannel[i]->GetSubband(m_currentLevel, LL)->ExtractTile(*m_encoder);
1086                 m_encoder->EncodeTileBuffer(); // encode macro block with tile-end = true
1087             }
1088             for (UINT32 tileY=0; tileY < nTiles; tileY++) {
1089                 for (UINT32 tileX=0; tileX < nTiles; tileX++) {
1090                     // extract tile to macro block and encode already filled macro blocks with tile-end = false
1091                     m_wtChannel[i]->GetSubband(m_currentLevel, HL)->ExtractTile(*m_encoder, true, tileX, tileY);
1092                     m_wtChannel[i]->GetSubband(m_currentLevel, LH)->ExtractTile(*m_encoder, true, tileX, tileY);
1093                     m_wtChannel[i]->GetSubband(m_currentLevel, HH)->ExtractTile(*m_encoder, true, tileX, tileY);
1094                     if (i == lastChannel && tileY == lastTile && tileX == lastTile) {
1095                         // all necessary data are buffered. next call of EncodeTileBuffer will write the last piece of data of the current level.
1096                         m_encoder->SetEncodedLevel(--m_currentLevel);
1097                     }
1098                     m_encoder->EncodeTileBuffer(); // encode last macro block with tile-end = true
1099                 }
1100             }
1101         }
1102     } else
1103 #endif
1104     {
1105         for (int i=0; i < m_header.channels; i++) {
1106             ASSERT(m_wtChannel[i]);
1107             if (m_currentLevel == m_header.nLevels) {
1108                 // last level also has LL band
1109                 m_wtChannel[i]->GetSubband(m_currentLevel, LL)->ExtractTile(*m_encoder);
1110             }
1111             //encoder.EncodeInterleaved(m_wtChannel[i], m_currentLevel, m_quant); // until version 4
1112             m_wtChannel[i]->GetSubband(m_currentLevel, HL)->ExtractTile(*m_encoder); // since version 5
1113             m_wtChannel[i]->GetSubband(m_currentLevel, LH)->ExtractTile(*m_encoder); // since version 5
1114             m_wtChannel[i]->GetSubband(m_currentLevel, HH)->ExtractTile(*m_encoder);
1115         }
1116 
1117         // all necessary data are buffered. next call of EncodeBuffer will write the last piece of data of the current level.
1118         m_encoder->SetEncodedLevel(--m_currentLevel);
1119     }
1120 }
1121 
1122 //////////////////////////////////////////////////////////////////////
1123 // Return written levelLength bytes
1124 UINT32 CPGFImage::UpdatePostHeaderSize() {
1125     ASSERT(m_encoder);
1126 
1127     INT64 offset = m_encoder->ComputeOffset(); ASSERT(offset >= 0);
1128 
1129     if (offset > 0) {
1130         // update post-header size and rewrite pre-header
1131         m_preHeader.hSize += (UINT32)offset;
1132         m_encoder->UpdatePostHeaderSize(m_preHeader);
1133     }
1134 
1135     // write dummy levelLength into stream
1136     return m_encoder->WriteLevelLength(m_levelLength);
1137 }
1138 
1139 //////////////////////////////////////////////////////////////////////
1140 /// Encode and write an image at current stream position.
1141 /// Call this method after WriteHeader().
1142 /// In case you want to write uncached metadata,
1143 /// then do that after WriteHeader() and before WriteImage().
1144 /// This method is called inside of Write(stream, ...).
1145 /// It might throw an IOException.
1146 /// @param stream A PGF stream
1147 /// @param cb A pointer to a callback procedure. The procedure is called after writing a single level. If cb returns true, then it stops proceeding.
1148 /// @param data Data Pointer to C++ class container to host callback procedure.
1149 /// @return The number of bytes written into stream.
1150 UINT32 CPGFImage::WriteImage(CPGFStream* stream, CallbackPtr cb /*= nullptr*/, void *data /*= nullptr*/) {
1151     ASSERT(stream);
1152     ASSERT(m_preHeader.hSize);
1153 
1154     int levels = m_header.nLevels;
1155     double percent = pow(0.25, levels);
1156 
1157     // update post-header size, rewrite pre-header, and write dummy levelLength
1158     UINT32 nWrittenBytes = UpdatePostHeaderSize();
1159 
1160     if (levels == 0) {
1161         // for very small images: write channels uncoded
1162         for (int c=0; c < m_header.channels; c++) {
1163             const UINT32 size = m_width[c]*m_height[c];
1164 
1165             // write channel data into stream
1166             for (UINT32 i=0; i < size; i++) {
1167                 int count = DataTSize;
1168                 stream->Write(&count, &m_channel[c][i]);
1169             }
1170         }
1171 
1172         // now update progress
1173         if (cb) {
1174             if ((*cb)(1, true, data)) ReturnWithError(EscapePressed);
1175         }
1176 
1177     } else {
1178         // encode quantized wavelet coefficients and write to PGF file
1179         // encode subbands, higher levels first
1180         // color channels are interleaved
1181 
1182         // encode all levels
1183         for (m_currentLevel = levels; m_currentLevel > 0; ) {
1184             WriteLevel(); // decrements m_currentLevel
1185 
1186             // now update progress
1187             if (cb) {
1188                 percent *= 4;
1189                 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1190             }
1191         }
1192 
1193         // flush encoder and write level lengths
1194         m_encoder->Flush();
1195     }
1196 
1197     // update level lengths
1198     nWrittenBytes += m_encoder->UpdateLevelLength(); // return written image bytes
1199 
1200     // delete encoder
1201     delete m_encoder; m_encoder = nullptr;
1202 
1203     ASSERT(!m_encoder);
1204 
1205     return nWrittenBytes;
1206 }
1207 
1208 //////////////////////////////////////////////////////////////////
1209 /// Encode and write an entire PGF image (header and image) at current stream position.
1210 /// A PGF image is structered in levels, numbered between 0 and Levels() - 1.
1211 /// Each level can be seen as a single image, containing the same content
1212 /// as all other levels, but in a different size (width, height).
1213 /// The image size at level i is double the size (width, height) of the image at level i+1.
1214 /// The image at level 0 contains the original size.
1215 /// Precondition: the PGF image contains a valid header (see also SetHeader(...)).
1216 /// It might throw an IOException.
1217 /// @param stream A PGF stream
1218 /// @param nWrittenBytes [in-out] The number of bytes written into stream are added to the input value.
1219 /// @param cb A pointer to a callback procedure. The procedure is called after writing a single level. If cb returns true, then it stops proceeding.
1220 /// @param data Data Pointer to C++ class container to host callback procedure.
1221 void CPGFImage::Write(CPGFStream* stream, UINT32* nWrittenBytes /*= nullptr*/, CallbackPtr cb /*= nullptr*/, void *data /*=nullptr*/) {
1222     ASSERT(stream);
1223     ASSERT(m_preHeader.hSize);
1224 
1225     // create wavelet transform channels and encoder
1226     UINT32 nBytes = WriteHeader(stream);
1227 
1228     // write image
1229     nBytes += WriteImage(stream, cb, data);
1230 
1231     // return written bytes
1232     if (nWrittenBytes) *nWrittenBytes += nBytes;
1233 }
1234 
1235 #ifdef __PGFROISUPPORT__
1236 //////////////////////////////////////////////////////////////////
1237 // Encode and write down to given level at current stream position.
1238 // A PGF image is structered in levels, numbered between 0 and Levels() - 1.
1239 // Each level can be seen as a single image, containing the same content
1240 // as all other levels, but in a different size (width, height).
1241 // The image size at level i is double the size (width, height) of the image at level i+1.
1242 // The image at level 0 contains the original size.
1243 // Precondition: the PGF image contains a valid header (see also SetHeader(...)) and WriteHeader() has been called before.
1244 // The ROI encoding scheme is used.
1245 // It might throw an IOException.
1246 // @param level The image level of the resulting image in the internal image buffer.
1247 // @param cb A pointer to a callback procedure. The procedure is called after writing a single level. If cb returns true, then it stops proceeding.
1248 // @param data Data Pointer to C++ class container to host callback procedure.
1249 // @return The number of bytes written into stream.
1250 UINT32 CPGFImage::Write(int level, CallbackPtr cb /*= nullptr*/, void *data /*=nullptr*/) {
1251     ASSERT(m_header.nLevels > 0);
1252     ASSERT(0 <= level && level < m_header.nLevels);
1253     ASSERT(m_encoder);
1254     ASSERT(ROIisSupported());
1255 
1256     const int levelDiff = m_currentLevel - level;
1257     double percent = (m_progressMode == PM_Relative) ? pow(0.25, levelDiff) : m_percent;
1258     UINT32 nWrittenBytes = 0;
1259 
1260     if (m_currentLevel == m_header.nLevels) {
1261         // update post-header size, rewrite pre-header, and write dummy levelLength
1262         nWrittenBytes = UpdatePostHeaderSize();
1263     } else {
1264         // prepare for next level: save current file position, because the stream might have been reinitialized
1265         if (m_encoder->ComputeBufferLength()) {
1266             m_streamReinitialized = true;
1267         }
1268     }
1269 
1270     // encoding scheme with ROI
1271     while (m_currentLevel > level) {
1272         WriteLevel();   // decrements m_currentLevel
1273 
1274         if (m_levelLength) {
1275             nWrittenBytes += m_levelLength[m_header.nLevels - m_currentLevel - 1];
1276         }
1277 
1278         // now update progress
1279         if (cb) {
1280             percent *= 4;
1281             if (m_progressMode == PM_Absolute) m_percent = percent;
1282             if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1283         }
1284     }
1285 
1286     // automatically closing
1287     if (m_currentLevel == 0) {
1288         if (!m_streamReinitialized) {
1289             // don't write level lengths, if the stream position changed inbetween two Write operations
1290             m_encoder->UpdateLevelLength();
1291         }
1292         // delete encoder
1293         delete m_encoder; m_encoder = nullptr;
1294     }
1295 
1296     return nWrittenBytes;
1297 }
1298 #endif // __PGFROISUPPORT__
1299 
1300 
1301 //////////////////////////////////////////////////////////////////
1302 // Check for valid import image mode.
1303 // @param mode Image mode
1304 // @return True if an image of given mode can be imported with ImportBitmap(...)
1305 bool CPGFImage::ImportIsSupported(BYTE mode) {
1306     size_t size = DataTSize;
1307 
1308     if (size >= 2) {
1309         switch(mode) {
1310             case ImageModeBitmap:
1311             case ImageModeIndexedColor:
1312             case ImageModeGrayScale:
1313             case ImageModeRGBColor:
1314             case ImageModeCMYKColor:
1315             case ImageModeHSLColor:
1316             case ImageModeHSBColor:
1317             //case ImageModeDuotone:
1318             case ImageModeLabColor:
1319             case ImageModeRGB12:
1320             case ImageModeRGB16:
1321             case ImageModeRGBA:
1322                 return true;
1323         }
1324     }
1325     if (size >= 3) {
1326         switch(mode) {
1327             case ImageModeGray16:
1328             case ImageModeRGB48:
1329             case ImageModeLab48:
1330             case ImageModeCMYK64:
1331             //case ImageModeDuotone16:
1332                 return true;
1333         }
1334     }
1335     if (size >=4) {
1336         switch(mode) {
1337             case ImageModeGray32:
1338                 return true;
1339         }
1340     }
1341     return false;
1342 }
1343 
1344 //////////////////////////////////////////////////////////////////////
1345 /// Retrieves red, green, blue (RGB) color values from a range of entries in the palette of the DIB section.
1346 /// It might throw an IOException.
1347 /// @param iFirstColor The color table index of the first entry to retrieve.
1348 /// @param nColors The number of color table entries to retrieve.
1349 /// @param prgbColors A pointer to the array of RGBQUAD structures to retrieve the color table entries.
1350 void CPGFImage::GetColorTable(UINT32 iFirstColor, UINT32 nColors, RGBQUAD* prgbColors) const {
1351     if (iFirstColor + nColors > ColorTableLen)  ReturnWithError(ColorTableError);
1352 
1353     for (UINT32 i=iFirstColor, j=0; j < nColors; i++, j++) {
1354         prgbColors[j] = m_postHeader.clut[i];
1355     }
1356 }
1357 
1358 //////////////////////////////////////////////////////////////////////
1359 /// Sets the red, green, blue (RGB) color values for a range of entries in the palette (clut).
1360 /// It might throw an IOException.
1361 /// @param iFirstColor The color table index of the first entry to set.
1362 /// @param nColors The number of color table entries to set.
1363 /// @param prgbColors A pointer to the array of RGBQUAD structures to set the color table entries.
1364 void CPGFImage::SetColorTable(UINT32 iFirstColor, UINT32 nColors, const RGBQUAD* prgbColors) {
1365     if (iFirstColor + nColors > ColorTableLen)  ReturnWithError(ColorTableError);
1366 
1367     for (UINT32 i=iFirstColor, j=0; j < nColors; i++, j++) {
1368         m_postHeader.clut[i] = prgbColors[j];
1369     }
1370 }
1371 
1372 //////////////////////////////////////////////////////////////////
1373 // Buffer transform from interleaved to channel seperated format
1374 // the absolute value of pitch is the number of bytes of an image row
1375 // if pitch is negative, then buff points to the last row of a bottom-up image (first byte on last row)
1376 // if pitch is positive, then buff points to the first row of a top-down image (first byte)
1377 // bpp is the number of bits per pixel used in image buffer buff
1378 //
1379 // RGB is transformed into YUV format (ordering of buffer data is BGR[A])
1380 // Y = (R + 2*G + B)/4 -128
1381 // U = R - G
1382 // V = B - G
1383 //
1384 // Since PGF Codec version 2.0 images are stored in top-down direction
1385 //
1386 // The sequence of input channels in the input image buffer does not need to be the same as expected from PGF. In case of different sequences you have to
1387 // provide a channelMap of size of expected channels (depending on image mode). For example, PGF expects in RGB color mode a channel sequence BGR.
1388 // If your provided image buffer contains a channel sequence ARGB, then the channelMap looks like { 3, 2, 1 }.
1389 void CPGFImage::RgbToYuv(int pitch, UINT8* buff, BYTE bpp, int channelMap[], CallbackPtr cb, void *data /*=nullptr*/) {
1390     ASSERT(buff);
1391     UINT32 yPos = 0, cnt = 0;
1392     double percent = 0;
1393     const double dP = 1.0/m_header.height;
1394     int defMap[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; ASSERT(sizeof(defMap)/sizeof(defMap[0]) == MaxChannels);
1395 
1396     if (channelMap == nullptr) channelMap = defMap;
1397 
1398     switch(m_header.mode) {
1399     case ImageModeBitmap:
1400         {
1401             ASSERT(m_header.channels == 1);
1402             ASSERT(m_header.bpp == 1);
1403             ASSERT(bpp == 1);
1404 
1405             const UINT32 w = m_header.width;
1406             const UINT32 w2 = (m_header.width + 7)/8;
1407             DataT* y = m_channel[0]; ASSERT(y);
1408 
1409             // new unpacked version since version 7
1410             for (UINT32 h = 0; h < m_header.height; h++) {
1411                 if (cb) {
1412                     if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1413                     percent += dP;
1414                 }
1415                 cnt = 0;
1416                 for (UINT32 j = 0; j < w2; j++) {
1417                     UINT8 byte = buff[j];
1418                     for (int k = 0; k < 8; k++) {
1419                         UINT8 bit = (byte & 0x80) >> 7;
1420                         if (cnt < w) y[yPos++] = bit;
1421                         byte <<= 1;
1422                         cnt++;
1423                     }
1424                 }
1425                 buff += pitch;
1426             }
1427             /* old version: packed values: 8 pixels in 1 byte
1428             for (UINT32 h = 0; h < m_header.height; h++) {
1429                 if (cb) {
1430                     if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1431                     percent += dP;
1432                 }
1433 
1434                 for (UINT32 j = 0; j < w2; j++) {
1435                     y[yPos++] = buff[j] - YUVoffset8;
1436                 }
1437                 // version 5 and 6
1438                 // for (UINT32 j = w2; j < w; j++) {
1439                 //  y[yPos++] = YUVoffset8;
1440                 //}
1441                 buff += pitch;
1442             }
1443             */
1444         }
1445         break;
1446     case ImageModeIndexedColor:
1447     case ImageModeGrayScale:
1448     case ImageModeHSLColor:
1449     case ImageModeHSBColor:
1450     case ImageModeLabColor:
1451         {
1452             ASSERT(m_header.channels >= 1);
1453             ASSERT(m_header.bpp == m_header.channels*8);
1454             ASSERT(bpp%8 == 0);
1455             const int channels = bpp/8; ASSERT(channels >= m_header.channels);
1456 
1457             for (UINT32 h=0; h < m_header.height; h++) {
1458                 if (cb) {
1459                     if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1460                     percent += dP;
1461                 }
1462 
1463                 cnt = 0;
1464                 for (UINT32 w=0; w < m_header.width; w++) {
1465                     for (int c=0; c < m_header.channels; c++) {
1466                         m_channel[c][yPos] = buff[cnt + channelMap[c]] - YUVoffset8;
1467                     }
1468                     cnt += channels;
1469                     yPos++;
1470                 }
1471                 buff += pitch;
1472             }
1473         }
1474         break;
1475     case ImageModeGray16:
1476     case ImageModeLab48:
1477         {
1478             ASSERT(m_header.channels >= 1);
1479             ASSERT(m_header.bpp == m_header.channels*16);
1480             ASSERT(bpp%16 == 0);
1481 
1482             UINT16 *buff16 = (UINT16 *)buff;
1483             const int pitch16 = pitch/2;
1484             const int channels = bpp/16; ASSERT(channels >= m_header.channels);
1485             const int shift = 16 - UsedBitsPerChannel(); ASSERT(shift >= 0);
1486             const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1);
1487 
1488             for (UINT32 h=0; h < m_header.height; h++) {
1489                 if (cb) {
1490                     if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1491                     percent += dP;
1492                 }
1493 
1494                 cnt = 0;
1495                 for (UINT32 w=0; w < m_header.width; w++) {
1496                     for (int c=0; c < m_header.channels; c++) {
1497                         m_channel[c][yPos] = (buff16[cnt + channelMap[c]] >> shift) - yuvOffset16;
1498                     }
1499                     cnt += channels;
1500                     yPos++;
1501                 }
1502                 buff16 += pitch16;
1503             }
1504         }
1505         break;
1506     case ImageModeRGBColor:
1507         {
1508             ASSERT(m_header.channels == 3);
1509             ASSERT(m_header.bpp == m_header.channels*8);
1510             ASSERT(bpp%8 == 0);
1511 
1512             DataT* y = m_channel[0]; ASSERT(y);
1513             DataT* u = m_channel[1]; ASSERT(u);
1514             DataT* v = m_channel[2]; ASSERT(v);
1515             const int channels = bpp/8; ASSERT(channels >= m_header.channels);
1516             UINT8 b, g, r;
1517 
1518             for (UINT32 h=0; h < m_header.height; h++) {
1519                 if (cb) {
1520                     if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1521                     percent += dP;
1522                 }
1523 
1524                 cnt = 0;
1525                 for (UINT32 w=0; w < m_header.width; w++) {
1526                     b = buff[cnt + channelMap[0]];
1527                     g = buff[cnt + channelMap[1]];
1528                     r = buff[cnt + channelMap[2]];
1529                     // Yuv
1530                     y[yPos] = ((b + (g << 1) + r) >> 2) - YUVoffset8;
1531                     u[yPos] = r - g;
1532                     v[yPos] = b - g;
1533                     yPos++;
1534                     cnt += channels;
1535                 }
1536                 buff += pitch;
1537             }
1538         }
1539         break;
1540     case ImageModeRGB48:
1541         {
1542             ASSERT(m_header.channels == 3);
1543             ASSERT(m_header.bpp == m_header.channels*16);
1544             ASSERT(bpp%16 == 0);
1545 
1546             UINT16 *buff16 = (UINT16 *)buff;
1547             const int pitch16 = pitch/2;
1548             const int channels = bpp/16; ASSERT(channels >= m_header.channels);
1549             const int shift = 16 - UsedBitsPerChannel(); ASSERT(shift >= 0);
1550             const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1);
1551 
1552             DataT* y = m_channel[0]; ASSERT(y);
1553             DataT* u = m_channel[1]; ASSERT(u);
1554             DataT* v = m_channel[2]; ASSERT(v);
1555             UINT16 b, g, r;
1556 
1557             for (UINT32 h=0; h < m_header.height; h++) {
1558                 if (cb) {
1559                     if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1560                     percent += dP;
1561                 }
1562 
1563                 cnt = 0;
1564                 for (UINT32 w=0; w < m_header.width; w++) {
1565                     b = buff16[cnt + channelMap[0]] >> shift;
1566                     g = buff16[cnt + channelMap[1]] >> shift;
1567                     r = buff16[cnt + channelMap[2]] >> shift;
1568                     // Yuv
1569                     y[yPos] = ((b + (g << 1) + r) >> 2) - yuvOffset16;
1570                     u[yPos] = r - g;
1571                     v[yPos] = b - g;
1572                     yPos++;
1573                     cnt += channels;
1574                 }
1575                 buff16 += pitch16;
1576             }
1577         }
1578         break;
1579     case ImageModeRGBA:
1580     case ImageModeCMYKColor:
1581         {
1582             ASSERT(m_header.channels == 4);
1583             ASSERT(m_header.bpp == m_header.channels*8);
1584             ASSERT(bpp%8 == 0);
1585             const int channels = bpp/8; ASSERT(channels >= m_header.channels);
1586 
1587             DataT* y = m_channel[0]; ASSERT(y);
1588             DataT* u = m_channel[1]; ASSERT(u);
1589             DataT* v = m_channel[2]; ASSERT(v);
1590             DataT* a = m_channel[3]; ASSERT(a);
1591             UINT8 b, g, r;
1592 
1593             for (UINT32 h=0; h < m_header.height; h++) {
1594                 if (cb) {
1595                     if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1596                     percent += dP;
1597                 }
1598 
1599                 cnt = 0;
1600                 for (UINT32 w=0; w < m_header.width; w++) {
1601                     b = buff[cnt + channelMap[0]];
1602                     g = buff[cnt + channelMap[1]];
1603                     r = buff[cnt + channelMap[2]];
1604                     // Yuv
1605                     y[yPos] = ((b + (g << 1) + r) >> 2) - YUVoffset8;
1606                     u[yPos] = r - g;
1607                     v[yPos] = b - g;
1608                     a[yPos++] = buff[cnt + channelMap[3]] - YUVoffset8;
1609                     cnt += channels;
1610                 }
1611                 buff += pitch;
1612             }
1613         }
1614         break;
1615     case ImageModeCMYK64:
1616         {
1617             ASSERT(m_header.channels == 4);
1618             ASSERT(m_header.bpp == m_header.channels*16);
1619             ASSERT(bpp%16 == 0);
1620 
1621             UINT16 *buff16 = (UINT16 *)buff;
1622             const int pitch16 = pitch/2;
1623             const int channels = bpp/16; ASSERT(channels >= m_header.channels);
1624             const int shift = 16 - UsedBitsPerChannel(); ASSERT(shift >= 0);
1625             const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1);
1626 
1627             DataT* y = m_channel[0]; ASSERT(y);
1628             DataT* u = m_channel[1]; ASSERT(u);
1629             DataT* v = m_channel[2]; ASSERT(v);
1630             DataT* a = m_channel[3]; ASSERT(a);
1631             UINT16 b, g, r;
1632 
1633             for (UINT32 h=0; h < m_header.height; h++) {
1634                 if (cb) {
1635                     if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1636                     percent += dP;
1637                 }
1638 
1639                 cnt = 0;
1640                 for (UINT32 w=0; w < m_header.width; w++) {
1641                     b = buff16[cnt + channelMap[0]] >> shift;
1642                     g = buff16[cnt + channelMap[1]] >> shift;
1643                     r = buff16[cnt + channelMap[2]] >> shift;
1644                     // Yuv
1645                     y[yPos] = ((b + (g << 1) + r) >> 2) - yuvOffset16;
1646                     u[yPos] = r - g;
1647                     v[yPos] = b - g;
1648                     a[yPos++] = (buff16[cnt + channelMap[3]] >> shift) - yuvOffset16;
1649                     cnt += channels;
1650                 }
1651                 buff16 += pitch16;
1652             }
1653         }
1654         break;
1655 #ifdef __PGF32SUPPORT__
1656     case ImageModeGray32:
1657         {
1658             ASSERT(m_header.channels == 1);
1659             ASSERT(m_header.bpp == 32);
1660             ASSERT(bpp == 32);
1661             ASSERT(DataTSize == sizeof(UINT32));
1662 
1663             DataT* y = m_channel[0]; ASSERT(y);
1664 
1665             UINT32 *buff32 = (UINT32 *)buff;
1666             const int pitch32 = pitch/4;
1667             const int shift = 31 - UsedBitsPerChannel(); ASSERT(shift >= 0);
1668             const DataT yuvOffset31 = 1 << (UsedBitsPerChannel() - 1);
1669 
1670             for (UINT32 h=0; h < m_header.height; h++) {
1671                 if (cb) {
1672                     if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1673                     percent += dP;
1674                 }
1675 
1676                 for (UINT32 w=0; w < m_header.width; w++) {
1677                     y[yPos++] = (buff32[w] >> shift) - yuvOffset31;
1678                 }
1679                 buff32 += pitch32;
1680             }
1681         }
1682         break;
1683 #endif
1684     case ImageModeRGB12:
1685         {
1686             ASSERT(m_header.channels == 3);
1687             ASSERT(m_header.bpp == m_header.channels*4);
1688             ASSERT(bpp == m_header.channels*4);
1689 
1690             DataT* y = m_channel[0]; ASSERT(y);
1691             DataT* u = m_channel[1]; ASSERT(u);
1692             DataT* v = m_channel[2]; ASSERT(v);
1693 
1694             UINT8 rgb = 0, b, g, r;
1695 
1696             for (UINT32 h=0; h < m_header.height; h++) {
1697                 if (cb) {
1698                     if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1699                     percent += dP;
1700                 }
1701 
1702                 cnt = 0;
1703                 for (UINT32 w=0; w < m_header.width; w++) {
1704                     if (w%2 == 0) {
1705                         // even pixel position
1706                         rgb = buff[cnt];
1707                         b = rgb & 0x0F;
1708                         g = (rgb & 0xF0) >> 4;
1709                         cnt++;
1710                         rgb = buff[cnt];
1711                         r = rgb & 0x0F;
1712                     } else {
1713                         // odd pixel position
1714                         b = (rgb & 0xF0) >> 4;
1715                         cnt++;
1716                         rgb = buff[cnt];
1717                         g = rgb & 0x0F;
1718                         r = (rgb & 0xF0) >> 4;
1719                         cnt++;
1720                     }
1721 
1722                     // Yuv
1723                     y[yPos] = ((b + (g << 1) + r) >> 2) - YUVoffset4;
1724                     u[yPos] = r - g;
1725                     v[yPos] = b - g;
1726                     yPos++;
1727                 }
1728                 buff += pitch;
1729             }
1730         }
1731         break;
1732     case ImageModeRGB16:
1733         {
1734             ASSERT(m_header.channels == 3);
1735             ASSERT(m_header.bpp == 16);
1736             ASSERT(bpp == 16);
1737 
1738             DataT* y = m_channel[0]; ASSERT(y);
1739             DataT* u = m_channel[1]; ASSERT(u);
1740             DataT* v = m_channel[2]; ASSERT(v);
1741 
1742             UINT16 *buff16 = (UINT16 *)buff;
1743             UINT16 rgb, b, g, r;
1744             const int pitch16 = pitch/2;
1745 
1746             for (UINT32 h=0; h < m_header.height; h++) {
1747                 if (cb) {
1748                     if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1749                     percent += dP;
1750                 }
1751                 for (UINT32 w=0; w < m_header.width; w++) {
1752                     rgb = buff16[w];
1753                     r = (rgb & 0xF800) >> 10;   // highest 5 bits
1754                     g = (rgb & 0x07E0) >> 5;    // middle 6 bits
1755                     b = (rgb & 0x001F) << 1;    // lowest 5 bits
1756                     // Yuv
1757                     y[yPos] = ((b + (g << 1) + r) >> 2) - YUVoffset6;
1758                     u[yPos] = r - g;
1759                     v[yPos] = b - g;
1760                     yPos++;
1761                 }
1762 
1763                 buff16 += pitch16;
1764             }
1765         }
1766         break;
1767     default:
1768         ASSERT(false);
1769     }
1770 }
1771 
1772 //////////////////////////////////////////////////////////////////
1773 // Get image data in interleaved format: (ordering of RGB data is BGR[A])
1774 // Upsampling, YUV to RGB transform and interleaving are done here to reduce the number
1775 // of passes over the data.
1776 // The absolute value of pitch is the number of bytes of an image row of the given image buffer.
1777 // If pitch is negative, then the image buffer must point to the last row of a bottom-up image (first byte on last row).
1778 // if pitch is positive, then the image buffer must point to the first row of a top-down image (first byte).
1779 // The sequence of output channels in the output image buffer does not need to be the same as provided by PGF. In case of different sequences you have to
1780 // provide a channelMap of size of expected channels (depending on image mode). For example, PGF provides a channel sequence BGR in RGB color mode.
1781 // If your provided image buffer expects a channel sequence ARGB, then the channelMap looks like { 3, 2, 1 }.
1782 // It might throw an IOException.
1783 // @param pitch The number of bytes of a row of the image buffer.
1784 // @param buff An image buffer.
1785 // @param bpp The number of bits per pixel used in image buffer.
1786 // @param channelMap A integer array containing the mapping of PGF channel ordering to expected channel ordering.
1787 // @param cb A pointer to a callback procedure. The procedure is called after each copied buffer row. If cb returns true, then it stops proceeding.
1788 // @param data Data Pointer to C++ class container to host callback procedure.
1789 void CPGFImage::GetBitmap(int pitch, UINT8* buff, BYTE bpp, int channelMap[] /*= nullptr */, CallbackPtr cb /*= nullptr*/, void *data /*=nullptr*/) const {
1790     ASSERT(buff);
1791     UINT32 w = m_width[0];  // width of decoded image
1792     UINT32 h = m_height[0]; // height of decoded image
1793     UINT32 yw = w;          // y-channel width
1794     UINT32 uw = m_width[1]; // u-channel width
1795     UINT32 roiOffsetX = 0;
1796     UINT32 roiOffsetY = 0;
1797     UINT32 yOffset = 0;
1798     UINT32 uOffset = 0;
1799 
1800 #ifdef __PGFROISUPPORT__
1801     const PGFRect& roi = GetAlignedROI(); // in pixels, roi is usually larger than levelRoi
1802     ASSERT(w == roi.Width() && h == roi.Height());
1803     const PGFRect levelRoi = ComputeLevelROI();
1804     ASSERT(roi.left <= levelRoi.left && levelRoi.right <= roi.right);
1805     ASSERT(roi.top <= levelRoi.top && levelRoi.bottom <= roi.bottom);
1806 
1807     if (ROIisSupported() && (levelRoi.Width() < w || levelRoi.Height() < h)) {
1808         // ROI is used
1809         w = levelRoi.Width();
1810         h = levelRoi.Height();
1811         roiOffsetX = levelRoi.left - roi.left;
1812         roiOffsetY = levelRoi.top - roi.top;
1813         yOffset = roiOffsetX + roiOffsetY*yw;
1814 
1815         if (m_downsample) {
1816             const PGFRect& downsampledRoi = GetAlignedROI(1);
1817             uOffset = levelRoi.left/2 - downsampledRoi.left + (levelRoi.top/2 - downsampledRoi.top)*m_width[1];
1818         } else {
1819             uOffset = yOffset;
1820         }
1821     }
1822 #endif
1823 
1824     const double dP = 1.0/h;
1825     int defMap[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; ASSERT(sizeof(defMap)/sizeof(defMap[0]) == MaxChannels);
1826     if (channelMap == nullptr) channelMap = defMap;
1827     DataT uAvg, vAvg;
1828     double percent = 0;
1829     UINT32 i, j;
1830 
1831     switch(m_header.mode) {
1832     case ImageModeBitmap:
1833         {
1834             ASSERT(m_header.channels == 1);
1835             ASSERT(m_header.bpp == 1);
1836             ASSERT(bpp == 1);
1837 
1838             const UINT32 w2 = (w + 7)/8;
1839             DataT* y = m_channel[0]; ASSERT(y);
1840 
1841             if (m_preHeader.version & Version7) {
1842                 // new unpacked version has a little better compression ratio
1843                 // since version 7
1844                 for (i = 0; i < h; i++) {
1845                     UINT32 cnt = 0;
1846                     for (j = 0; j < w2; j++) {
1847                         UINT8 byte = 0;
1848                         for (int k = 0; k < 8; k++) {
1849                             byte <<= 1;
1850                             UINT8 bit = 0;
1851                             if (cnt < w) {
1852                                 bit = y[yOffset + cnt] & 1;
1853                             }
1854                             byte |= bit;
1855                             cnt++;
1856                         }
1857                         buff[j] = byte;
1858                     }
1859                     yOffset += yw;
1860                     buff += pitch;
1861 
1862                     if (cb) {
1863                         percent += dP;
1864                         if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1865                     }
1866                 }
1867             } else {
1868                 // old versions
1869                 // packed pixels: 8 pixel in 1 byte of channel[0]
1870                 if (!(m_preHeader.version & Version5)) yw = w2; // not version 5 or 6
1871                 yOffset = roiOffsetX/8 + roiOffsetY*yw; // 1 byte in y contains 8 pixel values
1872                 for (i = 0; i < h; i++) {
1873                     for (j = 0; j < w2; j++) {
1874                         buff[j] = Clamp8(y[yOffset + j] + YUVoffset8);
1875                     }
1876                     yOffset += yw;
1877                     buff += pitch;
1878 
1879                     if (cb) {
1880                         percent += dP;
1881                         if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1882                     }
1883                 }
1884             }
1885             break;
1886         }
1887     case ImageModeIndexedColor:
1888     case ImageModeGrayScale:
1889     case ImageModeHSLColor:
1890     case ImageModeHSBColor:
1891         {
1892             ASSERT(m_header.channels >= 1);
1893             ASSERT(m_header.bpp == m_header.channels*8);
1894             ASSERT(bpp%8 == 0);
1895 
1896             UINT32 cnt, channels = bpp/8; ASSERT(channels >= m_header.channels);
1897 
1898             for (i=0; i < h; i++) {
1899                 UINT32 yPos = yOffset;
1900                 cnt = 0;
1901                 for (j=0; j < w; j++) {
1902                     for (UINT32 c=0; c < m_header.channels; c++) {
1903                         buff[cnt + channelMap[c]] = Clamp8(m_channel[c][yPos] + YUVoffset8);
1904                     }
1905                     cnt += channels;
1906                     yPos++;
1907                 }
1908                 yOffset += yw;
1909                 buff += pitch;
1910 
1911                 if (cb) {
1912                     percent += dP;
1913                     if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1914                 }
1915             }
1916             break;
1917         }
1918     case ImageModeGray16:
1919         {
1920             ASSERT(m_header.channels >= 1);
1921             ASSERT(m_header.bpp == m_header.channels*16);
1922 
1923             const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1);
1924             UINT32 cnt, channels;
1925 
1926             if (bpp%16 == 0) {
1927                 const int shift = 16 - UsedBitsPerChannel(); ASSERT(shift >= 0);
1928                 UINT16 *buff16 = (UINT16 *)buff;
1929                 int pitch16 = pitch/2;
1930                 channels = bpp/16; ASSERT(channels >= m_header.channels);
1931 
1932                 for (i=0; i < h; i++) {
1933                     UINT32 yPos = yOffset;
1934                     cnt = 0;
1935                     for (j=0; j < w; j++) {
1936                         for (UINT32 c=0; c < m_header.channels; c++) {
1937                             buff16[cnt + channelMap[c]] = Clamp16((m_channel[c][yPos] + yuvOffset16) << shift);
1938                         }
1939                         cnt += channels;
1940                         yPos++;
1941                     }
1942                     yOffset += yw;
1943                     buff16 += pitch16;
1944 
1945                     if (cb) {
1946                         percent += dP;
1947                         if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1948                     }
1949                 }
1950             } else {
1951                 ASSERT(bpp%8 == 0);
1952                 const int shift = __max(0, UsedBitsPerChannel() - 8);
1953                 channels = bpp/8; ASSERT(channels >= m_header.channels);
1954 
1955                 for (i=0; i < h; i++) {
1956                     UINT32 yPos = yOffset;
1957                     cnt = 0;
1958                     for (j=0; j < w; j++) {
1959                         for (UINT32 c=0; c < m_header.channels; c++) {
1960                             buff[cnt + channelMap[c]] = Clamp8((m_channel[c][yPos] + yuvOffset16) >> shift);
1961                         }
1962                         cnt += channels;
1963                         yPos++;
1964                     }
1965                     yOffset += yw;
1966                     buff += pitch;
1967 
1968                     if (cb) {
1969                         percent += dP;
1970                         if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1971                     }
1972                 }
1973             }
1974             break;
1975         }
1976     case ImageModeRGBColor:
1977         {
1978             ASSERT(m_header.channels == 3);
1979             ASSERT(m_header.bpp == m_header.channels*8);
1980             ASSERT(bpp%8 == 0);
1981             ASSERT(bpp >= m_header.bpp);
1982 
1983             DataT* y = m_channel[0]; ASSERT(y);
1984             DataT* u = m_channel[1]; ASSERT(u);
1985             DataT* v = m_channel[2]; ASSERT(v);
1986             UINT8 *buffg = &buff[channelMap[1]],
1987                   *buffr = &buff[channelMap[2]],
1988                   *buffb = &buff[channelMap[0]];
1989             UINT8 g;
1990             UINT32 cnt, channels = bpp/8;
1991 
1992             if (m_downsample) {
1993                 for (i=0; i < h; i++) {
1994                     UINT32 uPos = uOffset;
1995                     UINT32 yPos = yOffset;
1996                     cnt = 0;
1997                     for (j=0; j < w; j++) {
1998                         // u and v are downsampled
1999                         uAvg = u[uPos];
2000                         vAvg = v[uPos];
2001                         // Yuv
2002                         buffg[cnt] = g = Clamp8(y[yPos] + YUVoffset8 - ((uAvg + vAvg ) >> 2)); // must be logical shift operator
2003                         buffr[cnt] = Clamp8(uAvg + g);
2004                         buffb[cnt] = Clamp8(vAvg + g);
2005                         cnt += channels;
2006                         if (j & 1) uPos++;
2007                         yPos++;
2008                     }
2009                     if (i & 1) uOffset += uw;
2010                     yOffset += yw;
2011                     buffb += pitch;
2012                     buffg += pitch;
2013                     buffr += pitch;
2014 
2015                     if (cb) {
2016                         percent += dP;
2017                         if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
2018                     }
2019                 }
2020 
2021             } else {
2022                 for (i=0; i < h; i++) {
2023                     cnt = 0;
2024                     UINT32 yPos = yOffset;
2025                     for (j = 0; j < w; j++) {
2026                         uAvg = u[yPos];
2027                         vAvg = v[yPos];
2028                         // Yuv
2029                         buffg[cnt] = g = Clamp8(y[yPos] + YUVoffset8 - ((uAvg + vAvg ) >> 2)); // must be logical shift operator
2030                         buffr[cnt] = Clamp8(uAvg + g);
2031                         buffb[cnt] = Clamp8(vAvg + g);
2032                         cnt += channels;
2033                         yPos++;
2034                     }
2035                     yOffset += yw;
2036                     buffb += pitch;
2037                     buffg += pitch;
2038                     buffr += pitch;
2039 
2040                     if (cb) {
2041                         percent += dP;
2042                         if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
2043                     }
2044                 }
2045             }
2046             break;
2047         }
2048     case ImageModeRGB48:
2049         {
2050             ASSERT(m_header.channels == 3);
2051             ASSERT(m_header.bpp == 48);
2052 
2053             const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1);
2054 
2055             DataT* y = m_channel[0]; ASSERT(y);
2056             DataT* u = m_channel[1]; ASSERT(u);
2057             DataT* v = m_channel[2]; ASSERT(v);
2058             UINT32 cnt, channels;
2059             DataT g;
2060 
2061             if (bpp >= 48 && bpp%16 == 0) {
2062                 const int shift = 16 - UsedBitsPerChannel(); ASSERT(shift >= 0);
2063                 UINT16 *buff16 = (UINT16 *)buff;
2064                 int pitch16 = pitch/2;
2065                 channels = bpp/16; ASSERT(channels >= m_header.channels);
2066 
2067                 for (i=0; i < h; i++) {
2068                     UINT32 uPos = uOffset;
2069                     UINT32 yPos = yOffset;
2070                     cnt = 0;
2071                     for (j=0; j < w; j++) {
2072                         uAvg = u[uPos];
2073                         vAvg = v[uPos];
2074                         // Yuv
2075                         g = y[yPos] + yuvOffset16 - ((uAvg + vAvg ) >> 2); // must be logical shift operator
2076                         buff16[cnt + channelMap[1]] = Clamp16(g << shift);
2077                         buff16[cnt + channelMap[2]] = Clamp16((uAvg + g) << shift);
2078                         buff16[cnt + channelMap[0]] = Clamp16((vAvg + g) << shift);
2079                         cnt += channels;
2080                         if (!m_downsample || (j & 1)) uPos++;
2081                         yPos++;
2082                     }
2083                     if (!m_downsample || (i & 1)) uOffset += uw;
2084                     yOffset += yw;
2085                     buff16 += pitch16;
2086 
2087                     if (cb) {
2088                         percent += dP;
2089                         if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
2090                     }
2091                 }
2092             } else {
2093                 ASSERT(bpp%8 == 0);
2094                 const int shift = __max(0, UsedBitsPerChannel() - 8);
2095                 channels = bpp/8; ASSERT(channels >= m_header.channels);
2096 
2097                 for (i=0; i < h; i++) {
2098                     UINT32 uPos = uOffset;
2099                     UINT32 yPos = yOffset;
2100                     cnt = 0;
2101                     for (j=0; j < w; j++) {
2102                         uAvg = u[uPos];
2103                         vAvg = v[uPos];
2104                         // Yuv
2105                         g = y[yPos] + yuvOffset16 - ((uAvg + vAvg ) >> 2); // must be logical shift operator
2106                         buff[cnt + channelMap[1]] = Clamp8(g >> shift);
2107                         buff[cnt + channelMap[2]] = Clamp8((uAvg + g) >> shift);
2108                         buff[cnt + channelMap[0]] = Clamp8((vAvg + g) >> shift);
2109                         cnt += channels;
2110                         if (!m_downsample || (j & 1)) uPos++;
2111                         yPos++;
2112                     }
2113                     if (!m_downsample || (i & 1)) uOffset += uw;
2114                     yOffset += yw;
2115                     buff += pitch;
2116 
2117                     if (cb) {
2118                         percent += dP;
2119                         if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
2120                     }
2121                 }
2122             }
2123             break;
2124         }
2125     case ImageModeLabColor:
2126         {
2127             ASSERT(m_header.channels == 3);
2128             ASSERT(m_header.bpp == m_header.channels*8);
2129             ASSERT(bpp%8 == 0);
2130 
2131             DataT* l = m_channel[0]; ASSERT(l);
2132             DataT* a = m_channel[1]; ASSERT(a);
2133             DataT* b = m_channel[2]; ASSERT(b);
2134             UINT32 cnt, channels = bpp/8; ASSERT(channels >= m_header.channels);
2135 
2136             for (i=0; i < h; i++) {
2137                 UINT32 uPos = uOffset;
2138                 UINT32 yPos = yOffset;
2139                 cnt = 0;
2140                 for (j=0; j < w; j++) {
2141                     uAvg = a[uPos];
2142                     vAvg = b[uPos];
2143                     buff[cnt + channelMap[0]] = Clamp8(l[yPos] + YUVoffset8);
2144                     buff[cnt + channelMap[1]] = Clamp8(uAvg + YUVoffset8);
2145                     buff[cnt + channelMap[2]] = Clamp8(vAvg + YUVoffset8);
2146                     cnt += channels;
2147                     if (!m_downsample || (j & 1)) uPos++;
2148                     yPos++;
2149                 }
2150                 if (!m_downsample || (i & 1)) uOffset += uw;
2151                 yOffset += yw;
2152                 buff += pitch;
2153 
2154                 if (cb) {
2155                     percent += dP;
2156                     if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
2157                 }
2158             }
2159             break;
2160         }
2161     case ImageModeLab48:
2162         {
2163             ASSERT(m_header.channels == 3);
2164             ASSERT(m_header.bpp == m_header.channels*16);
2165 
2166             const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1);
2167 
2168             DataT* l = m_channel[0]; ASSERT(l);
2169             DataT* a = m_channel[1]; ASSERT(a);
2170             DataT* b = m_channel[2]; ASSERT(b);
2171             UINT32 cnt, channels;
2172 
2173             if (bpp%16 == 0) {
2174                 const int shift = 16 - UsedBitsPerChannel(); ASSERT(shift >= 0);
2175                 UINT16 *buff16 = (UINT16 *)buff;
2176                 int pitch16 = pitch/2;
2177                 channels = bpp/16; ASSERT(channels >= m_header.channels);
2178 
2179                 for (i=0; i < h; i++) {
2180                     UINT32 uPos = uOffset;
2181                     UINT32 yPos = yOffset;
2182                     cnt = 0;
2183                     for (j=0; j < w; j++) {
2184                         uAvg = a[uPos];
2185                         vAvg = b[uPos];
2186                         buff16[cnt + channelMap[0]] = Clamp16((l[yPos] + yuvOffset16) << shift);
2187                         buff16[cnt + channelMap[1]] = Clamp16((uAvg + yuvOffset16) << shift);
2188                         buff16[cnt + channelMap[2]] = Clamp16((vAvg + yuvOffset16) << shift);
2189                         cnt += channels;
2190                         if (!m_downsample || (j & 1)) uPos++;
2191                         yPos++;
2192                     }
2193                     if (!m_downsample || (i & 1)) uOffset += uw;
2194                     yOffset += yw;
2195                     buff16 += pitch16;
2196 
2197                     if (cb) {
2198                         percent += dP;
2199                         if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
2200                     }
2201                 }
2202             } else {
2203                 ASSERT(bpp%8 == 0);
2204                 const int shift = __max(0, UsedBitsPerChannel() - 8);
2205                 channels = bpp/8; ASSERT(channels >= m_header.channels);
2206 
2207                 for (i=0; i < h; i++) {
2208                     UINT32 uPos = uOffset;
2209                     UINT32 yPos = yOffset;
2210                     cnt = 0;
2211                     for (j=0; j < w; j++) {
2212                         uAvg = a[uPos];
2213                         vAvg = b[uPos];
2214                         buff[cnt + channelMap[0]] = Clamp8((l[yPos] + yuvOffset16) >> shift);
2215                         buff[cnt + channelMap[1]] = Clamp8((uAvg + yuvOffset16) >> shift);
2216                         buff[cnt + channelMap[2]] = Clamp8((vAvg + yuvOffset16) >> shift);
2217                         cnt += channels;
2218                         if (!m_downsample || (j & 1)) uPos++;
2219                         yPos++;
2220                     }
2221                     if (!m_downsample || (i & 1)) uOffset += uw;
2222                     yOffset += yw;
2223                     buff += pitch;
2224 
2225                     if (cb) {
2226                         percent += dP;
2227                         if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
2228                     }
2229                 }
2230             }
2231             break;
2232         }
2233     case ImageModeRGBA:
2234     case ImageModeCMYKColor:
2235         {
2236             ASSERT(m_header.channels == 4);
2237             ASSERT(m_header.bpp == m_header.channels*8);
2238             ASSERT(bpp%8 == 0);
2239 
2240             DataT* y = m_channel[0]; ASSERT(y);
2241             DataT* u = m_channel[1]; ASSERT(u);
2242             DataT* v = m_channel[2]; ASSERT(v);
2243             DataT* a = m_channel[3]; ASSERT(a);
2244             UINT8 g, aAvg;
2245             UINT32 cnt, channels = bpp/8; ASSERT(channels >= m_header.channels);
2246 
2247             for (i=0; i < h; i++) {
2248                 UINT32 uPos = uOffset;
2249                 UINT32 yPos = yOffset;
2250                 cnt = 0;
2251                 for (j=0; j < w; j++) {
2252                     uAvg = u[uPos];
2253                     vAvg = v[uPos];
2254                     aAvg = Clamp8(a[uPos] + YUVoffset8);
2255                     // Yuv
2256                     buff[cnt + channelMap[1]] = g = Clamp8(y[yPos] + YUVoffset8 - ((uAvg + vAvg ) >> 2)); // must be logical shift operator
2257                     buff[cnt + channelMap[2]] = Clamp8(uAvg + g);
2258                     buff[cnt + channelMap[0]] = Clamp8(vAvg + g);
2259                     buff[cnt + channelMap[3]] = aAvg;
2260                     cnt += channels;
2261                     if (!m_downsample || (j & 1)) uPos++;
2262                     yPos++;
2263                 }
2264                 if (!m_downsample || (i & 1)) uOffset += uw;
2265                 yOffset += yw;
2266                 buff += pitch;
2267 
2268                 if (cb) {
2269                     percent += dP;
2270                     if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
2271                 }
2272             }
2273             break;
2274         }
2275     case ImageModeCMYK64:
2276         {
2277             ASSERT(m_header.channels == 4);
2278             ASSERT(m_header.bpp == 64);
2279 
2280             const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1);
2281 
2282             DataT* y = m_channel[0]; ASSERT(y);
2283             DataT* u = m_channel[1]; ASSERT(u);
2284             DataT* v = m_channel[2]; ASSERT(v);
2285             DataT* a = m_channel[3]; ASSERT(a);
2286             DataT g, aAvg;
2287             UINT32 cnt, channels;
2288 
2289             if (bpp%16 == 0) {
2290                 const int shift = 16 - UsedBitsPerChannel(); ASSERT(shift >= 0);
2291                 UINT16 *buff16 = (UINT16 *)buff;
2292                 int pitch16 = pitch/2;
2293                 channels = bpp/16; ASSERT(channels >= m_header.channels);
2294 
2295                 for (i=0; i < h; i++) {
2296                     UINT32 uPos = uOffset;
2297                     UINT32 yPos = yOffset;
2298                     cnt = 0;
2299                     for (j=0; j < w; j++) {
2300                         uAvg = u[uPos];
2301                         vAvg = v[uPos];
2302                         aAvg = a[uPos] + yuvOffset16;
2303                         // Yuv
2304                         g = y[yPos] + yuvOffset16 - ((uAvg + vAvg ) >> 2); // must be logical shift operator
2305                         buff16[cnt + channelMap[1]] = Clamp16(g << shift);
2306                         buff16[cnt + channelMap[2]] = Clamp16((uAvg + g) << shift);
2307                         buff16[cnt + channelMap[0]] = Clamp16((vAvg + g) << shift);
2308                         buff16[cnt + channelMap[3]] = Clamp16(aAvg << shift);
2309                         cnt += channels;
2310                         if (!m_downsample || (j & 1)) uPos++;
2311                         yPos++;
2312                     }
2313                     if (!m_downsample || (i & 1)) uOffset += uw;
2314                     yOffset += yw;
2315                     buff16 += pitch16;
2316 
2317                     if (cb) {
2318                         percent += dP;
2319                         if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
2320                     }
2321                 }
2322             } else {
2323                 ASSERT(bpp%8 == 0);
2324                 const int shift = __max(0, UsedBitsPerChannel() - 8);
2325                 channels = bpp/8; ASSERT(channels >= m_header.channels);
2326 
2327                 for (i=0; i < h; i++) {
2328                     UINT32 uPos = uOffset;
2329                     UINT32 yPos = yOffset;
2330                     cnt = 0;
2331                     for (j=0; j < w; j++) {
2332                         uAvg = u[uPos];
2333                         vAvg = v[uPos];
2334                         aAvg = a[uPos] + yuvOffset16;
2335                         // Yuv
2336                         g = y[yPos] + yuvOffset16 - ((uAvg + vAvg ) >> 2); // must be logical shift operator
2337                         buff[cnt + channelMap[1]] = Clamp8(g >> shift);
2338                         buff[cnt + channelMap[2]] = Clamp8((uAvg + g) >> shift);
2339                         buff[cnt + channelMap[0]] = Clamp8((vAvg + g) >> shift);
2340                         buff[cnt + channelMap[3]] = Clamp8(aAvg >> shift);
2341                         cnt += channels;
2342                         if (!m_downsample || (j & 1)) uPos++;
2343                         yPos++;
2344                     }
2345                     if (!m_downsample || (i & 1)) uOffset += uw;
2346                     yOffset += yw;
2347                     buff += pitch;
2348 
2349                     if (cb) {
2350                         percent += dP;
2351                         if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
2352                     }
2353                 }
2354             }
2355             break;
2356         }
2357 #ifdef __PGF32SUPPORT__
2358     case ImageModeGray32:
2359         {
2360             ASSERT(m_header.channels == 1);
2361             ASSERT(m_header.bpp == 32);
2362 
2363             const int yuvOffset31 = 1 << (UsedBitsPerChannel() - 1);
2364             DataT* y = m_channel[0]; ASSERT(y);
2365 
2366             if (bpp == 32) {
2367                 const int shift = 31 - UsedBitsPerChannel(); ASSERT(shift >= 0);
2368                 UINT32 *buff32 = (UINT32 *)buff;
2369                 int pitch32 = pitch/4;
2370 
2371                 for (i=0; i < h; i++) {
2372                     UINT32 yPos = yOffset;
2373                     for (j = 0; j < w; j++) {
2374                         buff32[j] = Clamp31((y[yPos++] + yuvOffset31) << shift);
2375                     }
2376                     yOffset += yw;
2377                     buff32 += pitch32;
2378 
2379                     if (cb) {
2380                         percent += dP;
2381                         if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
2382                     }
2383                 }
2384             } else if (bpp == 16) {
2385                 const int usedBits = UsedBitsPerChannel();
2386                 UINT16 *buff16 = (UINT16 *)buff;
2387                 int pitch16 = pitch/2;
2388 
2389                 if (usedBits < 16) {
2390                     const int shift = 16 - usedBits;
2391                     for (i=0; i < h; i++) {
2392                         UINT32 yPos = yOffset;
2393                         for (j = 0; j < w; j++) {
2394                             buff16[j] = Clamp16((y[yPos++] + yuvOffset31) << shift);
2395                         }
2396                         yOffset += yw;
2397                         buff16 += pitch16;
2398 
2399                         if (cb) {
2400                             percent += dP;
2401                             if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
2402                         }
2403                     }
2404                 } else {
2405                     const int shift = __max(0, usedBits - 16);
2406                     for (i=0; i < h; i++) {
2407                         UINT32 yPos = yOffset;
2408                         for (j = 0; j < w; j++) {
2409                             buff16[j] = Clamp16((y[yPos++] + yuvOffset31) >> shift);
2410                         }
2411                         yOffset += yw;
2412                         buff16 += pitch16;
2413 
2414                         if (cb) {
2415                             percent += dP;
2416                             if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
2417                         }
2418                     }
2419                 }
2420             } else {
2421                 ASSERT(bpp == 8);
2422                 const int shift = __max(0, UsedBitsPerChannel() - 8);
2423 
2424                 for (i=0; i < h; i++) {
2425                     UINT32 yPos = yOffset;
2426                     for (j = 0; j < w; j++) {
2427                         buff[j] = Clamp8((y[yPos++] + yuvOffset31) >> shift);
2428                     }
2429                     yOffset += yw;
2430                     buff += pitch;
2431 
2432                     if (cb) {
2433                         percent += dP;
2434                         if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
2435                     }
2436                 }
2437             }
2438             break;
2439         }
2440 #endif
2441     case ImageModeRGB12:
2442         {
2443             ASSERT(m_header.channels == 3);
2444             ASSERT(m_header.bpp == m_header.channels*4);
2445             ASSERT(bpp == m_header.channels*4);
2446             ASSERT(!m_downsample);
2447 
2448             DataT* y = m_channel[0]; ASSERT(y);
2449             DataT* u = m_channel[1]; ASSERT(u);
2450             DataT* v = m_channel[2]; ASSERT(v);
2451             UINT16 yval;
2452             UINT32 cnt;
2453 
2454             for (i=0; i < h; i++) {
2455                 UINT32 yPos = yOffset;
2456                 cnt = 0;
2457                 for (j=0; j < w; j++) {
2458                     // Yuv
2459                     uAvg = u[yPos];
2460                     vAvg = v[yPos];
2461                     yval = Clamp4(y[yPos] + YUVoffset4 - ((uAvg + vAvg ) >> 2)); // must be logical shift operator
2462                     if (j%2 == 0) {
2463                         buff[cnt] = UINT8(Clamp4(vAvg + yval) | (yval << 4));
2464                         cnt++;
2465                         buff[cnt] = Clamp4(uAvg + yval);
2466                     } else {
2467                         buff[cnt] |= Clamp4(vAvg + yval) << 4;
2468                         cnt++;
2469                         buff[cnt] = UINT8(yval | (Clamp4(uAvg + yval) << 4));
2470                         cnt++;
2471                     }
2472                     yPos++;
2473                 }
2474                 yOffset += yw;
2475                 buff += pitch;
2476 
2477                 if (cb) {
2478                     percent += dP;
2479                     if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
2480                 }
2481             }
2482             break;
2483         }
2484     case ImageModeRGB16:
2485         {
2486             ASSERT(m_header.channels == 3);
2487             ASSERT(m_header.bpp == 16);
2488             ASSERT(bpp == 16);
2489             ASSERT(!m_downsample);
2490 
2491             DataT* y = m_channel[0]; ASSERT(y);
2492             DataT* u = m_channel[1]; ASSERT(u);
2493             DataT* v = m_channel[2]; ASSERT(v);
2494             UINT16 yval;
2495             UINT16 *buff16 = (UINT16 *)buff;
2496             int pitch16 = pitch/2;
2497 
2498             for (i=0; i < h; i++) {
2499                 UINT32 yPos = yOffset;
2500                 for (j = 0; j < w; j++) {
2501                     // Yuv
2502                     uAvg = u[yPos];
2503                     vAvg = v[yPos];
2504                     yval = Clamp6(y[yPos++] + YUVoffset6 - ((uAvg + vAvg ) >> 2)); // must be logical shift operator
2505                     buff16[j] = (yval << 5) | ((Clamp6(uAvg + yval) >> 1) << 11) | (Clamp6(vAvg + yval) >> 1);
2506                 }
2507                 yOffset += yw;
2508                 buff16 += pitch16;
2509 
2510                 if (cb) {
2511                     percent += dP;
2512                     if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
2513                 }
2514             }
2515             break;
2516         }
2517     default:
2518         ASSERT(false);
2519     }
2520 
2521 #ifdef _DEBUG
2522     // display ROI (RGB) in debugger
2523     roiimage.width = w;
2524     roiimage.height = h;
2525     if (pitch > 0) {
2526         roiimage.pitch = pitch;
2527         roiimage.data = buff;
2528     } else {
2529         roiimage.pitch = -pitch;
2530         roiimage.data = buff + (h - 1)*pitch;
2531     }
2532 #endif
2533 
2534 }
2535 
2536 //////////////////////////////////////////////////////////////////////
2537 /// Get YUV image data in interleaved format: (ordering is YUV[A])
2538 /// The absolute value of pitch is the number of bytes of an image row of the given image buffer.
2539 /// If pitch is negative, then the image buffer must point to the last row of a bottom-up image (first byte on last row).
2540 /// if pitch is positive, then the image buffer must point to the first row of a top-down image (first byte).
2541 /// The sequence of output channels in the output image buffer does not need to be the same as provided by PGF. In case of different sequences you have to
2542 /// provide a channelMap of size of expected channels (depending on image mode). For example, PGF provides a channel sequence BGR in RGB color mode.
2543 /// If your provided image buffer expects a channel sequence VUY, then the channelMap looks like { 2, 1, 0 }.
2544 /// It might throw an IOException.
2545 /// @param pitch The number of bytes of a row of the image buffer.
2546 /// @param buff An image buffer.
2547 /// @param bpp The number of bits per pixel used in image buffer.
2548 /// @param channelMap A integer array containing the mapping of PGF channel ordering to expected channel ordering.
2549 /// @param cb A pointer to a callback procedure. The procedure is called after each copied buffer row. If cb returns true, then it stops proceeding.
2550 void CPGFImage::GetYUV(int pitch, DataT* buff, BYTE bpp, int channelMap[] /*= nullptr*/, CallbackPtr cb /*= nullptr*/, void *data /*=nullptr*/) const {
2551     ASSERT(buff);
2552     const UINT32 w = m_width[0];
2553     const UINT32 h = m_height[0];
2554     const bool wOdd = (1 == w%2);
2555     const int dataBits = DataTSize*8; ASSERT(dataBits == 16 || dataBits == 32);
2556     const int pitch2 = pitch/DataTSize;
2557     const int yuvOffset = (dataBits == 16) ? YUVoffset8 : YUVoffset16;
2558     const double dP = 1.0/h;
2559 
2560     int defMap[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; ASSERT(sizeof(defMap)/sizeof(defMap[0]) == MaxChannels);
2561     if (channelMap == nullptr) channelMap = defMap;
2562     int sampledPos = 0, yPos = 0;
2563     DataT uAvg, vAvg;
2564     double percent = 0;
2565     UINT32 i, j;
2566 
2567     if (m_header.channels == 3) {
2568         ASSERT(bpp%dataBits == 0);
2569 
2570         DataT* y = m_channel[0]; ASSERT(y);
2571         DataT* u = m_channel[1]; ASSERT(u);
2572         DataT* v = m_channel[2]; ASSERT(v);
2573         int cnt, channels = bpp/dataBits; ASSERT(channels >= m_header.channels);
2574 
2575         for (i=0; i < h; i++) {
2576             if (i%2) sampledPos -= (w + 1)/2;
2577             cnt = 0;
2578             for (j=0; j < w; j++) {
2579                 if (m_downsample) {
2580                     // image was downsampled
2581                     uAvg = u[sampledPos];
2582                     vAvg = v[sampledPos];
2583                 } else {
2584                     uAvg = u[yPos];
2585                     vAvg = v[yPos];
2586                 }
2587                 buff[cnt + channelMap[0]] = y[yPos];
2588                 buff[cnt + channelMap[1]] = uAvg;
2589                 buff[cnt + channelMap[2]] = vAvg;
2590                 yPos++;
2591                 cnt += channels;
2592                 if (j%2) sampledPos++;
2593             }
2594             buff += pitch2;
2595             if (wOdd) sampledPos++;
2596 
2597             if (cb) {
2598                 percent += dP;
2599                 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
2600             }
2601         }
2602     } else if (m_header.channels == 4) {
2603         ASSERT(m_header.bpp == m_header.channels*8);
2604         ASSERT(bpp%dataBits == 0);
2605 
2606         DataT* y = m_channel[0]; ASSERT(y);
2607         DataT* u = m_channel[1]; ASSERT(u);
2608         DataT* v = m_channel[2]; ASSERT(v);
2609         DataT* a = m_channel[3]; ASSERT(a);
2610         UINT8 aAvg;
2611         int cnt, channels = bpp/dataBits; ASSERT(channels >= m_header.channels);
2612 
2613         for (i=0; i < h; i++) {
2614             if (i%2) sampledPos -= (w + 1)/2;
2615             cnt = 0;
2616             for (j=0; j < w; j++) {
2617                 if (m_downsample) {
2618                     // image was downsampled
2619                     uAvg = u[sampledPos];
2620                     vAvg = v[sampledPos];
2621                     aAvg = Clamp8(a[sampledPos] + yuvOffset);
2622                 } else {
2623                     uAvg = u[yPos];
2624                     vAvg = v[yPos];
2625                     aAvg = Clamp8(a[yPos] + yuvOffset);
2626                 }
2627                 // Yuv
2628                 buff[cnt + channelMap[0]] = y[yPos];
2629                 buff[cnt + channelMap[1]] = uAvg;
2630                 buff[cnt + channelMap[2]] = vAvg;
2631                 buff[cnt + channelMap[3]] = aAvg;
2632                 yPos++;
2633                 cnt += channels;
2634                 if (j%2) sampledPos++;
2635             }
2636             buff += pitch2;
2637             if (wOdd) sampledPos++;
2638 
2639             if (cb) {
2640                 percent += dP;
2641                 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
2642             }
2643         }
2644     }
2645 }
2646 
2647 //////////////////////////////////////////////////////////////////////
2648 /// Import a YUV image from a specified image buffer.
2649 /// The absolute value of pitch is the number of bytes of an image row.
2650 /// If pitch is negative, then buff points to the last row of a bottom-up image (first byte on last row).
2651 /// If pitch is positive, then buff points to the first row of a top-down image (first byte).
2652 /// The sequence of input channels in the input image buffer does not need to be the same as expected from PGF. In case of different sequences you have to
2653 /// provide a channelMap of size of expected channels (depending on image mode). For example, PGF expects in RGB color mode a channel sequence BGR.
2654 /// If your provided image buffer contains a channel sequence VUY, then the channelMap looks like { 2, 1, 0 }.
2655 /// It might throw an IOException.
2656 /// @param pitch The number of bytes of a row of the image buffer.
2657 /// @param buff An image buffer.
2658 /// @param bpp The number of bits per pixel used in image buffer.
2659 /// @param channelMap A integer array containing the mapping of input channel ordering to expected channel ordering.
2660 /// @param cb A pointer to a callback procedure. The procedure is called after each imported buffer row. If cb returns true, then it stops proceeding.
2661 void CPGFImage::ImportYUV(int pitch, DataT *buff, BYTE bpp, int channelMap[] /*= nullptr*/, CallbackPtr cb /*= nullptr*/, void *data /*=nullptr*/) {
2662     ASSERT(buff);
2663     const double dP = 1.0/m_header.height;
2664     const int dataBits = DataTSize*8; ASSERT(dataBits == 16 || dataBits == 32);
2665     const int pitch2 = pitch/DataTSize;
2666     const int yuvOffset = (dataBits == 16) ? YUVoffset8 : YUVoffset16;
2667 
2668     int yPos = 0, cnt = 0;
2669     double percent = 0;
2670     int defMap[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; ASSERT(sizeof(defMap)/sizeof(defMap[0]) == MaxChannels);
2671 
2672     if (channelMap == nullptr) channelMap = defMap;
2673 
2674     if (m_header.channels == 3) {
2675         ASSERT(bpp%dataBits == 0);
2676 
2677         DataT* y = m_channel[0]; ASSERT(y);
2678         DataT* u = m_channel[1]; ASSERT(u);
2679         DataT* v = m_channel[2]; ASSERT(v);
2680         const int channels = bpp/dataBits; ASSERT(channels >= m_header.channels);
2681 
2682         for (UINT32 h=0; h < m_header.height; h++) {
2683             if (cb) {
2684                 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
2685                 percent += dP;
2686             }
2687 
2688             cnt = 0;
2689             for (UINT32 w=0; w < m_header.width; w++) {
2690                 y[yPos] = buff[cnt + channelMap[0]];
2691                 u[yPos] = buff[cnt + channelMap[1]];
2692                 v[yPos] = buff[cnt + channelMap[2]];
2693                 yPos++;
2694                 cnt += channels;
2695             }
2696             buff += pitch2;
2697         }
2698     } else if (m_header.channels == 4) {
2699         ASSERT(bpp%dataBits == 0);
2700 
2701         DataT* y = m_channel[0]; ASSERT(y);
2702         DataT* u = m_channel[1]; ASSERT(u);
2703         DataT* v = m_channel[2]; ASSERT(v);
2704         DataT* a = m_channel[3]; ASSERT(a);
2705         const int channels = bpp/dataBits; ASSERT(channels >= m_header.channels);
2706 
2707         for (UINT32 h=0; h < m_header.height; h++) {
2708             if (cb) {
2709                 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
2710                 percent += dP;
2711             }
2712 
2713             cnt = 0;
2714             for (UINT32 w=0; w < m_header.width; w++) {
2715                 y[yPos] = buff[cnt + channelMap[0]];
2716                 u[yPos] = buff[cnt + channelMap[1]];
2717                 v[yPos] = buff[cnt + channelMap[2]];
2718                 a[yPos] = buff[cnt + channelMap[3]] - yuvOffset;
2719                 yPos++;
2720                 cnt += channels;
2721             }
2722             buff += pitch2;
2723         }
2724     }
2725 
2726     if (m_downsample) {
2727         // Subsampling of the chrominance and alpha channels
2728         for (int i=1; i < m_header.channels; i++) {
2729             Downsample(i);
2730         }
2731     }
2732 }
2733