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