libpgf
6.11.32
PGF - Progressive Graphics File
|
00001 /* 00002 * The Progressive Graphics File; http://www.libpgf.org 00003 * 00004 * $Date: 2007-02-03 13:04:21 +0100 (Sa, 03 Feb 2007) $ 00005 * $Revision: 280 $ 00006 * 00007 * This file Copyright (C) 2006 xeraina GmbH, Switzerland 00008 * 00009 * This program is free software; you can redistribute it and/or 00010 * modify it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE 00011 * as published by the Free Software Foundation; either version 2.1 00012 * of the License, or (at your option) any later version. 00013 * 00014 * This program is distributed in the hope that it will be useful, 00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00017 * GNU General Public License for more details. 00018 * 00019 * You should have received a copy of the GNU General Public License 00020 * along with this program; if not, write to the Free Software 00021 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 00022 */ 00023 00028 00029 #include "PGFimage.h" 00030 #include "Decoder.h" 00031 #include "Encoder.h" 00032 #include <cmath> 00033 #include <cstring> 00034 00035 #define YUVoffset4 8 // 2^3 00036 #define YUVoffset6 32 // 2^5 00037 #define YUVoffset8 128 // 2^7 00038 #define YUVoffset16 32768 // 2^15 00039 #define YUVoffset31 1073741824 // 2^30 00040 #define MaxValue 2147483648 // 2^MaxBitPlanes = 2^31 00041 00043 // global methods and variables 00044 #ifdef NEXCEPTIONS 00045 OSError _PGF_Error_; 00046 00047 OSError GetLastPGFError() { 00048 OSError tmp = _PGF_Error_; 00049 _PGF_Error_ = NoError; 00050 return tmp; 00051 } 00052 #endif 00053 00055 // Standard constructor: It is used to create a PGF instance for opening and reading. 00056 CPGFImage::CPGFImage() 00057 : m_decoder(0) 00058 , m_encoder(0) 00059 , m_levelLength(0) 00060 , m_quant(0) 00061 , m_downsample(false) 00062 , m_favorSpeedOverSize(false) 00063 , m_useOMPinEncoder(true) 00064 , m_useOMPinDecoder(true) 00065 #ifdef __PGFROISUPPORT__ 00066 , m_levelwise(true) 00067 , m_streamReinitialized(false) 00068 #endif 00069 , m_cb(0) 00070 , m_cbArg(0) 00071 { 00072 00073 // init preHeader 00074 memcpy(m_preHeader.magic, Magic, 3); 00075 m_preHeader.version = PGFVersion; 00076 m_preHeader.hSize = 0; 00077 00078 // init postHeader 00079 m_postHeader.userData = 0; 00080 m_postHeader.userDataLen = 0; 00081 00082 // init channels 00083 for (int i=0; i < MaxChannels; i++) { 00084 m_channel[i] = 0; 00085 m_wtChannel[i] = 0; 00086 } 00087 00088 // set image width and height 00089 m_width[0] = 0; 00090 m_height[0] = 0; 00091 } 00092 00094 // Destructor: Destroy internal data structures. 00095 CPGFImage::~CPGFImage() { 00096 Destroy(); 00097 } 00098 00100 // Destroy internal data structures. 00101 // Destructor calls this method during destruction. 00102 void CPGFImage::Destroy() { 00103 Close(); 00104 00105 for (int i=0; i < m_header.channels; i++) { 00106 delete m_wtChannel[i]; m_wtChannel[i]=0; // also deletes m_channel 00107 m_channel[i] = 0; 00108 } 00109 delete[] m_postHeader.userData; m_postHeader.userData = 0; m_postHeader.userDataLen = 0; 00110 delete[] m_levelLength; m_levelLength = 0; 00111 delete m_encoder; m_encoder = NULL; 00112 } 00113 00115 // Close PGF image after opening and reading. 00116 // Destructor calls this method during destruction. 00117 void CPGFImage::Close() { 00118 delete m_decoder; m_decoder = 0; 00119 } 00120 00122 // Open a PGF image at current stream position: read pre-header, header, levelLength, and ckeck image type. 00123 // Precondition: The stream has been opened for reading. 00124 // It might throw an IOException. 00125 // @param stream A PGF stream 00126 void CPGFImage::Open(CPGFStream *stream) THROW_ { 00127 ASSERT(stream); 00128 00129 m_decoder = new CDecoder(stream, m_preHeader, m_header, m_postHeader, m_levelLength, m_useOMPinDecoder); 00130 00131 if (m_header.nLevels > MaxLevel) ReturnWithError(FormatCannotRead); 00132 00133 // set current level 00134 m_currentLevel = m_header.nLevels; 00135 00136 // set image width and height 00137 m_width[0] = m_header.width; 00138 m_height[0] = m_header.height; 00139 00140 // complete header 00141 CompleteHeader(); 00142 00143 // interpret quant parameter 00144 if (m_header.quality > DownsampleThreshold && 00145 (m_header.mode == ImageModeRGBColor || 00146 m_header.mode == ImageModeRGBA || 00147 m_header.mode == ImageModeRGB48 || 00148 m_header.mode == ImageModeCMYKColor || 00149 m_header.mode == ImageModeCMYK64 || 00150 m_header.mode == ImageModeLabColor || 00151 m_header.mode == ImageModeLab48)) { 00152 m_downsample = true; 00153 m_quant = m_header.quality - 1; 00154 } else { 00155 m_downsample = false; 00156 m_quant = m_header.quality; 00157 } 00158 00159 // set channel dimensions (chrominance is subsampled by factor 2) 00160 if (m_downsample) { 00161 for (int i=1; i < m_header.channels; i++) { 00162 m_width[i] = (m_width[0] + 1)/2; 00163 m_height[i] = (m_height[0] + 1)/2; 00164 } 00165 } else { 00166 for (int i=1; i < m_header.channels; i++) { 00167 m_width[i] = m_width[0]; 00168 m_height[i] = m_height[0]; 00169 } 00170 } 00171 00172 if (m_header.nLevels > 0) { 00173 // init wavelet subbands 00174 for (int i=0; i < m_header.channels; i++) { 00175 m_wtChannel[i] = new CWaveletTransform(m_width[i], m_height[i], m_header.nLevels); 00176 } 00177 } else { 00178 // very small image: we don't use DWT and encoding 00179 00180 // read channels 00181 for (int c=0; c < m_header.channels; c++) { 00182 const UINT32 size = m_width[c]*m_height[c]; 00183 m_channel[c] = new DataT[size]; 00184 00185 // read channel data from stream 00186 for (UINT32 i=0; i < size; i++) { 00187 int count = DataTSize; 00188 stream->Read(&count, &m_channel[c][i]); 00189 if (count != DataTSize) ReturnWithError(MissingData); 00190 } 00191 } 00192 } 00193 } 00194 00196 void CPGFImage::CompleteHeader() { 00197 if (m_header.mode == ImageModeUnknown) { 00198 // undefined mode 00199 switch(m_header.bpp) { 00200 case 1: m_header.mode = ImageModeBitmap; break; 00201 case 8: m_header.mode = ImageModeGrayScale; break; 00202 case 12: m_header.mode = ImageModeRGB12; break; 00203 case 16: m_header.mode = ImageModeRGB16; break; 00204 case 24: m_header.mode = ImageModeRGBColor; break; 00205 case 32: m_header.mode = ImageModeRGBA; break; 00206 case 48: m_header.mode = ImageModeRGB48; break; 00207 default: m_header.mode = ImageModeRGBColor; break; 00208 } 00209 } 00210 if (!m_header.bpp) { 00211 // undefined bpp 00212 switch(m_header.mode) { 00213 case ImageModeBitmap: 00214 m_header.bpp = 1; 00215 break; 00216 case ImageModeIndexedColor: 00217 case ImageModeGrayScale: 00218 m_header.bpp = 8; 00219 break; 00220 case ImageModeRGB12: 00221 m_header.bpp = 12; 00222 break; 00223 case ImageModeRGB16: 00224 case ImageModeGray16: 00225 m_header.bpp = 16; 00226 break; 00227 case ImageModeRGBColor: 00228 case ImageModeLabColor: 00229 m_header.bpp = 24; 00230 break; 00231 case ImageModeRGBA: 00232 case ImageModeCMYKColor: 00233 #ifdef __PGF32SUPPORT__ 00234 case ImageModeGray31: 00235 #endif 00236 m_header.bpp = 32; 00237 break; 00238 case ImageModeRGB48: 00239 case ImageModeLab48: 00240 m_header.bpp = 48; 00241 break; 00242 case ImageModeCMYK64: 00243 m_header.bpp = 64; 00244 break; 00245 default: 00246 ASSERT(false); 00247 m_header.bpp = 24; 00248 } 00249 } 00250 if (m_header.mode == ImageModeRGBColor && m_header.bpp == 32) { 00251 // change mode 00252 m_header.mode = ImageModeRGBA; 00253 } 00254 ASSERT(m_header.mode != ImageModeBitmap || m_header.bpp == 1); 00255 ASSERT(m_header.mode != ImageModeGrayScale || m_header.bpp == 8); 00256 ASSERT(m_header.mode != ImageModeGray16 || m_header.bpp == 16); 00257 ASSERT(m_header.mode != ImageModeRGBColor || m_header.bpp == 24); 00258 ASSERT(m_header.mode != ImageModeRGBA || m_header.bpp == 32); 00259 ASSERT(m_header.mode != ImageModeRGB12 || m_header.bpp == 12); 00260 ASSERT(m_header.mode != ImageModeRGB16 || m_header.bpp == 16); 00261 ASSERT(m_header.mode != ImageModeRGB48 || m_header.bpp == 48); 00262 ASSERT(m_header.mode != ImageModeLabColor || m_header.bpp == 24); 00263 ASSERT(m_header.mode != ImageModeLab48 || m_header.bpp == 48); 00264 ASSERT(m_header.mode != ImageModeCMYKColor || m_header.bpp == 32); 00265 ASSERT(m_header.mode != ImageModeCMYK64 || m_header.bpp == 64); 00266 00267 // set number of channels 00268 if (!m_header.channels) { 00269 switch(m_header.mode) { 00270 case ImageModeBitmap: 00271 case ImageModeIndexedColor: 00272 case ImageModeGrayScale: 00273 case ImageModeGray16: 00274 #ifdef __PGF32SUPPORT__ 00275 case ImageModeGray31: 00276 #endif 00277 m_header.channels = 1; 00278 break; 00279 case ImageModeRGBColor: 00280 case ImageModeRGB12: 00281 case ImageModeRGB16: 00282 case ImageModeRGB48: 00283 case ImageModeLabColor: 00284 case ImageModeLab48: 00285 m_header.channels = 3; 00286 break; 00287 case ImageModeRGBA: 00288 case ImageModeCMYKColor: 00289 case ImageModeCMYK64: 00290 m_header.channels = 4; 00291 break; 00292 default: 00293 ASSERT(false); 00294 m_header.channels = 3; 00295 } 00296 } 00297 } 00298 00303 const UINT8* CPGFImage::GetUserData(UINT32& size) const { 00304 size = m_postHeader.userDataLen; 00305 return m_postHeader.userData; 00306 } 00307 00313 void CPGFImage::Reconstruct(int level /*= 0*/) THROW_ { 00314 if (m_header.nLevels == 0) { 00315 // image didn't use wavelet transform 00316 if (level == 0) { 00317 for (int i=0; i < m_header.channels; i++) { 00318 ASSERT(m_wtChannel[i]); 00319 m_channel[i] = m_wtChannel[i]->GetSubband(0, LL)->GetBuffer(); 00320 } 00321 } 00322 } else { 00323 int currentLevel = m_header.nLevels; 00324 00325 if (ROIisSupported()) { 00326 // enable ROI reading 00327 SetROI(PGFRect(0, 0, m_header.width, m_header.height)); 00328 } 00329 00330 while (currentLevel > level) { 00331 for (int i=0; i < m_header.channels; i++) { 00332 ASSERT(m_wtChannel[i]); 00333 // dequantize subbands 00334 if (currentLevel == m_header.nLevels) { 00335 // last level also has LL band 00336 m_wtChannel[i]->GetSubband(currentLevel, LL)->Dequantize(m_quant); 00337 } 00338 m_wtChannel[i]->GetSubband(currentLevel, HL)->Dequantize(m_quant); 00339 m_wtChannel[i]->GetSubband(currentLevel, LH)->Dequantize(m_quant); 00340 m_wtChannel[i]->GetSubband(currentLevel, HH)->Dequantize(m_quant); 00341 00342 // inverse transform from m_wtChannel to m_channel 00343 if (!m_wtChannel[i]->InverseTransform(currentLevel, &m_width[i], &m_height[i], &m_channel[i])) ReturnWithError(InsufficientMemory); 00344 ASSERT(m_channel[i]); 00345 } 00346 00347 currentLevel--; 00348 } 00349 } 00350 } 00351 00353 // Read and decode some levels of a PGF image at current stream position. 00354 // A PGF image is structered in levels, numbered between 0 and Levels() - 1. 00355 // Each level can be seen as a single image, containing the same content 00356 // as all other levels, but in a different size (width, height). 00357 // The image size at level i is double the size (width, height) of the image at level i+1. 00358 // The image at level 0 contains the original size. 00359 // Precondition: The PGF image has been opened with a call of Open(...). 00360 // It might throw an IOException. 00361 // @param level The image level of the resulting image in the internal image buffer. 00362 // @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. 00363 // @param data Data Pointer to C++ class container to host callback procedure. 00364 void CPGFImage::Read(int level /*= 0*/, CallbackPtr cb /*= NULL*/, void *data /*=NULL*/) THROW_ { 00365 ASSERT((level >= 0 && level < m_header.nLevels) || m_header.nLevels == 0); // m_header.nLevels == 0: image didn't use wavelet transform 00366 ASSERT(m_decoder); 00367 00368 #ifdef __PGFROISUPPORT__ 00369 if (ROIisSupported() && m_header.nLevels > 0) { 00370 // new encoding scheme supporting ROI 00371 PGFRect rect(0, 0, m_header.width, m_header.height); 00372 Read(rect, level, cb, data); 00373 return; 00374 } 00375 #endif 00376 00377 if (m_header.nLevels == 0) { 00378 if (level == 0) { 00379 // the data has already been read during open 00380 // now update progress 00381 if (cb) { 00382 if ((*cb)(1.0, true, data)) ReturnWithError(EscapePressed); 00383 } 00384 } 00385 } else { 00386 const int levelDiff = m_currentLevel - level; 00387 double percent = pow(0.25, levelDiff); 00388 00389 // encoding scheme without ROI 00390 while (m_currentLevel > level) { 00391 for (int i=0; i < m_header.channels; i++) { 00392 ASSERT(m_wtChannel[i]); 00393 // decode file and write stream to m_wtChannel 00394 if (m_currentLevel == m_header.nLevels) { 00395 // last level also has LL band 00396 m_wtChannel[i]->GetSubband(m_currentLevel, LL)->PlaceTile(*m_decoder, m_quant); 00397 } 00398 if (m_preHeader.version & Version5) { 00399 // since version 5 00400 m_wtChannel[i]->GetSubband(m_currentLevel, HL)->PlaceTile(*m_decoder, m_quant); 00401 m_wtChannel[i]->GetSubband(m_currentLevel, LH)->PlaceTile(*m_decoder, m_quant); 00402 } else { 00403 // until version 4 00404 m_decoder->DecodeInterleaved(m_wtChannel[i], m_currentLevel, m_quant); 00405 } 00406 m_wtChannel[i]->GetSubband(m_currentLevel, HH)->PlaceTile(*m_decoder, m_quant); 00407 } 00408 00409 volatile OSError error = NoError; // volatile prevents optimizations 00410 #pragma omp parallel for default(shared) 00411 for (int i=0; i < m_header.channels; i++) { 00412 // inverse transform from m_wtChannel to m_channel 00413 if (error == NoError) { 00414 OSError err = m_wtChannel[i]->InverseTransform(m_currentLevel, &m_width[i], &m_height[i], &m_channel[i]); 00415 if (err != NoError) error = err; 00416 } 00417 ASSERT(m_channel[i]); 00418 } 00419 if (error != NoError) ReturnWithError(error); 00420 00421 // set new level: must be done before refresh callback 00422 m_currentLevel--; 00423 00424 // now we have to refresh the display 00425 if (m_cb) m_cb(m_cbArg); 00426 00427 // now update progress 00428 if (cb) { 00429 percent += 3*percent; 00430 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed); 00431 } 00432 } 00433 } 00434 00435 // automatically closing 00436 if (m_currentLevel == 0) Close(); 00437 } 00438 00439 #ifdef __PGFROISUPPORT__ 00440 00441 00442 00443 00444 00445 00446 00447 00448 00449 void CPGFImage::Read(PGFRect& rect, int level /*= 0*/, CallbackPtr cb /*= NULL*/, void *data /*=NULL*/) THROW_ { 00450 ASSERT((level >= 0 && level < m_header.nLevels) || m_header.nLevels == 0); // m_header.nLevels == 0: image didn't use wavelet transform 00451 ASSERT(m_decoder); 00452 00453 if (m_header.nLevels == 0 || !ROIisSupported()) { 00454 rect.left = rect.top = 0; 00455 rect.right = m_header.width; rect.bottom = m_header.height; 00456 Read(level, cb, data); 00457 } else { 00458 ASSERT(ROIisSupported()); 00459 // new encoding scheme supporting ROI 00460 ASSERT(rect.left < m_header.width && rect.top < m_header.height); 00461 const int levelDiff = m_currentLevel - level; 00462 double percent = pow(0.25, levelDiff); 00463 00464 // check level difference 00465 if (levelDiff <= 0) { 00466 // it is a new read call, probably with a new ROI 00467 m_currentLevel = m_header.nLevels; 00468 m_decoder->SetStreamPosToData(); 00469 } 00470 00471 // check rectangle 00472 if (rect.right == 0 || rect.right > m_header.width) rect.right = m_header.width; 00473 if (rect.bottom == 0 || rect.bottom > m_header.height) rect.bottom = m_header.height; 00474 00475 // enable ROI decoding and reading 00476 SetROI(rect); 00477 00478 while (m_currentLevel > level) { 00479 for (int i=0; i < m_header.channels; i++) { 00480 ASSERT(m_wtChannel[i]); 00481 00482 // get number of tiles and tile indices 00483 const UINT32 nTiles = m_wtChannel[i]->GetNofTiles(m_currentLevel); 00484 const PGFRect& tileIndices = m_wtChannel[i]->GetTileIndices(m_currentLevel); 00485 00486 // decode file and write stream to m_wtChannel 00487 if (m_currentLevel == m_header.nLevels) { // last level also has LL band 00488 ASSERT(nTiles == 1); 00489 m_decoder->DecodeTileBuffer(); 00490 m_wtChannel[i]->GetSubband(m_currentLevel, LL)->PlaceTile(*m_decoder, m_quant); 00491 } 00492 for (UINT32 tileY=0; tileY < nTiles; tileY++) { 00493 for (UINT32 tileX=0; tileX < nTiles; tileX++) { 00494 // check relevance of tile 00495 if (tileIndices.IsInside(tileX, tileY)) { 00496 m_decoder->DecodeTileBuffer(); 00497 m_wtChannel[i]->GetSubband(m_currentLevel, HL)->PlaceTile(*m_decoder, m_quant, true, tileX, tileY); 00498 m_wtChannel[i]->GetSubband(m_currentLevel, LH)->PlaceTile(*m_decoder, m_quant, true, tileX, tileY); 00499 m_wtChannel[i]->GetSubband(m_currentLevel, HH)->PlaceTile(*m_decoder, m_quant, true, tileX, tileY); 00500 } else { 00501 // skip tile 00502 m_decoder->SkipTileBuffer(); 00503 } 00504 } 00505 } 00506 } 00507 00508 volatile OSError error = NoError; // volatile prevents optimizations 00509 #pragma omp parallel for default(shared) 00510 for (int i=0; i < m_header.channels; i++) { 00511 // inverse transform from m_wtChannel to m_channel 00512 if (error == NoError) { 00513 OSError err = m_wtChannel[i]->InverseTransform(m_currentLevel, &m_width[i], &m_height[i], &m_channel[i]); 00514 if (err != NoError) error = err; 00515 } 00516 ASSERT(m_channel[i]); 00517 } 00518 if (error != NoError) ReturnWithError(error); 00519 00520 // set new level: must be done before refresh callback 00521 m_currentLevel--; 00522 00523 // now we have to refresh the display 00524 if (m_cb) m_cb(m_cbArg); 00525 00526 // now update progress 00527 if (cb) { 00528 percent += 3*percent; 00529 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed); 00530 } 00531 } 00532 } 00533 00534 // automatically closing 00535 if (m_currentLevel == 0) Close(); 00536 } 00537 00541 void CPGFImage::SetROI(PGFRect rect) { 00542 ASSERT(m_decoder); 00543 ASSERT(ROIisSupported()); 00544 00545 // store ROI for a later call of GetBitmap 00546 m_roi = rect; 00547 00548 // enable ROI decoding 00549 m_decoder->SetROI(); 00550 00551 // enlarge ROI because of border artefacts 00552 const UINT32 dx = FilterWidth/2*(1 << m_currentLevel); 00553 const UINT32 dy = FilterHeight/2*(1 << m_currentLevel); 00554 00555 if (rect.left < dx) rect.left = 0; 00556 else rect.left -= dx; 00557 if (rect.top < dy) rect.top = 0; 00558 else rect.top -= dy; 00559 rect.right += dx; 00560 if (rect.right > m_header.width) rect.right = m_header.width; 00561 rect.bottom += dy; 00562 if (rect.bottom > m_header.height) rect.bottom = m_header.height; 00563 00564 // prepare wavelet channels for using ROI 00565 ASSERT(m_wtChannel[0]); 00566 m_wtChannel[0]->SetROI(rect); 00567 if (m_downsample && m_header.channels > 1) { 00568 // all further channels are downsampled, therefore downsample ROI 00569 rect.left >>= 1; 00570 rect.top >>= 1; 00571 rect.right >>= 1; 00572 rect.bottom >>= 1; 00573 } 00574 for (int i=1; i < m_header.channels; i++) { 00575 ASSERT(m_wtChannel[i]); 00576 m_wtChannel[i]->SetROI(rect); 00577 } 00578 } 00579 00580 #endif // __PGFROISUPPORT__ 00581 00586 UINT32 CPGFImage::GetEncodedHeaderLength() const { 00587 ASSERT(m_decoder); 00588 return m_decoder->GetEncodedHeaderLength(); 00589 } 00590 00598 UINT32 CPGFImage::ReadEncodedHeader(UINT8* target, UINT32 targetLen) const THROW_ { 00599 ASSERT(target); 00600 ASSERT(targetLen > 0); 00601 ASSERT(m_decoder); 00602 00603 // reset stream position 00604 m_decoder->SetStreamPosToStart(); 00605 00606 // compute number of bytes to read 00607 UINT32 len = __min(targetLen, GetEncodedHeaderLength()); 00608 00609 // read data 00610 len = m_decoder->ReadEncodedData(target, len); 00611 ASSERT(len >= 0 && len <= targetLen); 00612 00613 return len; 00614 } 00615 00618 void CPGFImage::ResetStreamPos() THROW_ { 00619 ASSERT(m_decoder); 00620 return m_decoder->SetStreamPosToStart(); 00621 } 00622 00632 UINT32 CPGFImage::ReadEncodedData(int level, UINT8* target, UINT32 targetLen) const THROW_ { 00633 ASSERT(level >= 0 && level < m_header.nLevels); 00634 ASSERT(target); 00635 ASSERT(targetLen > 0); 00636 ASSERT(m_decoder); 00637 00638 // reset stream position 00639 m_decoder->SetStreamPosToData(); 00640 00641 // position stream 00642 UINT64 offset = 0; 00643 00644 for (int i=m_header.nLevels - 1; i > level; i--) { 00645 offset += m_levelLength[m_header.nLevels - 1 - i]; 00646 } 00647 m_decoder->Skip(offset); 00648 00649 // compute number of bytes to read 00650 UINT32 len = __min(targetLen, GetEncodedLevelLength(level)); 00651 00652 // read data 00653 len = m_decoder->ReadEncodedData(target, len); 00654 ASSERT(len >= 0 && len <= targetLen); 00655 00656 return len; 00657 } 00658 00660 // Set background of an RGB image with transparency channel or reset to default background. 00661 // @param bg A pointer to a background color or NULL (reset to default background) 00662 void CPGFImage::SetBackground(const RGBTRIPLE* bg) { 00663 if (bg) { 00664 m_header.background = *bg; 00665 // m_backgroundSet = true; 00666 } else { 00667 m_header.background.rgbtBlue = DefaultBGColor; 00668 m_header.background.rgbtGreen = DefaultBGColor; 00669 m_header.background.rgbtRed = DefaultBGColor; 00670 // m_backgroundSet = false; 00671 } 00672 } 00673 00678 void CPGFImage::SetMaxValue(UINT32 maxValue) { 00679 BYTE pot = 0; 00680 00681 while(maxValue > 0) { 00682 pot++; 00683 maxValue >>= 1; 00684 } 00685 // store bits per channel 00686 if (pot > 31) pot = 31; 00687 m_header.background.rgbtBlue = pot; 00688 } 00689 00694 BYTE CPGFImage::UsedBitsPerChannel() const { 00695 BYTE bpc = m_header.bpp/m_header.channels; 00696 00697 if (bpc > 8) { 00698 // see also GetMaxValue() 00699 return m_header.background.rgbtBlue; 00700 } else { 00701 return bpc; 00702 } 00703 } 00704 00707 BYTE CPGFImage::Version() const { 00708 if (m_preHeader.version & Version6) return 6; 00709 if (m_preHeader.version & Version5) return 5; 00710 if (m_preHeader.version & Version2) return 2; 00711 return 1; 00712 } 00713 00715 // Import an image from a specified image buffer. 00716 // This method is usually called before Write(...) and after SetHeader(...). 00717 // It might throw an IOException. 00718 // The absolute value of pitch is the number of bytes of an image row. 00719 // If pitch is negative, then buff points to the last row of a bottom-up image (first byte on last row). 00720 // If pitch is positive, then buff points to the first row of a top-down image (first byte). 00721 // 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 00722 // provide a channelMap of size of expected channels (depending on image mode). For example, PGF expects in RGB color mode a channel sequence BGR. 00723 // If your provided image buffer contains a channel sequence ARGB, then the channelMap looks like { 3, 2, 1 }. 00724 // @param pitch The number of bytes of a row of the image buffer. 00725 // @param buff An image buffer. 00726 // @param bpp The number of bits per pixel used in image buffer. 00727 // @param channelMap A integer array containing the mapping of input channel ordering to expected channel ordering. 00728 // @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. 00729 // @param data Data Pointer to C++ class container to host callback procedure. 00730 void CPGFImage::ImportBitmap(int pitch, UINT8 *buff, BYTE bpp, int channelMap[] /*= NULL */, CallbackPtr cb /*= NULL*/, void *data /*=NULL*/) THROW_ { 00731 ASSERT(buff); 00732 ASSERT(m_channel[0]); 00733 00734 // color transform 00735 RgbToYuv(pitch, buff, bpp, channelMap, cb, data); 00736 00737 if (m_downsample) { 00738 // Subsampling of the chrominance and alpha channels 00739 for (int i=1; i < m_header.channels; i++) { 00740 Downsample(i); 00741 } 00742 } 00743 } 00744 00746 // Bilinerar Subsampling of channel ch by a factor 2 00747 void CPGFImage::Downsample(int ch) { 00748 ASSERT(ch > 0); 00749 00750 const int w = m_width[0]; 00751 const int w2 = w/2; 00752 const int h2 = m_height[0]/2; 00753 const int oddW = w%2; // don't use bool -> problems with MaxSpeed optimization 00754 const int oddH = m_height[0]%2; // " 00755 int i, j; 00756 int loPos = 0; 00757 int hiPos = w; 00758 int sampledPos = 0; 00759 DataT* buff = m_channel[ch]; ASSERT(buff); 00760 00761 for (i=0; i < h2; i++) { 00762 for (j=0; j < w2; j++) { 00763 // compute average of pixel block 00764 buff[sampledPos] = (buff[loPos] + buff[loPos + 1] + buff[hiPos] + buff[hiPos + 1]) >> 2; 00765 loPos += 2; hiPos += 2; 00766 sampledPos++; 00767 } 00768 if (oddW) { 00769 buff[sampledPos] = (buff[loPos] + buff[hiPos]) >> 1; 00770 loPos++; hiPos++; 00771 sampledPos++; 00772 } 00773 loPos += w; hiPos += w; 00774 } 00775 if (oddH) { 00776 for (j=0; j < w2; j++) { 00777 buff[sampledPos] = (buff[loPos] + buff[loPos+1]) >> 1; 00778 loPos += 2; hiPos += 2; 00779 sampledPos++; 00780 } 00781 if (oddW) { 00782 buff[sampledPos] = buff[loPos]; 00783 } 00784 } 00785 00786 // downsampled image has half width and half height 00787 m_width[ch] = (m_width[ch] + 1)/2; 00788 m_height[ch] = (m_height[ch] + 1)/2; 00789 } 00790 00792 void CPGFImage::ComputeLevels() { 00793 const int maxThumbnailWidth = 20*FilterWidth; 00794 const int m = __min(m_header.width, m_header.height); 00795 int s = m; 00796 00797 if (m_header.nLevels < 1 || m_header.nLevels > MaxLevel) { 00798 m_header.nLevels = 1; 00799 // compute a good value depending on the size of the image 00800 while (s > maxThumbnailWidth) { 00801 m_header.nLevels++; 00802 s = s/2; 00803 } 00804 } 00805 00806 int levels = m_header.nLevels; // we need a signed value during level reduction 00807 00808 // reduce number of levels if the image size is smaller than FilterWidth*2^levels 00809 s = FilterWidth*(1 << levels); // must be at least the double filter size because of subsampling 00810 while (m < s) { 00811 levels--; 00812 s = s/2; 00813 } 00814 if (levels > MaxLevel) m_header.nLevels = MaxLevel; 00815 else if (levels < 0) m_header.nLevels = 0; 00816 else m_header.nLevels = (UINT8)levels; 00817 00818 ASSERT(0 <= m_header.nLevels && m_header.nLevels <= MaxLevel); 00819 } 00820 00829 void CPGFImage::SetHeader(const PGFHeader& header, BYTE flags /*=0*/, UINT8* userData /*= 0*/, UINT32 userDataLength /*= 0*/) THROW_ { 00830 ASSERT(!m_decoder); // current image must be closed 00831 ASSERT(header.quality <= MaxQuality); 00832 00833 // init state 00834 #ifdef __PGFROISUPPORT__ 00835 m_levelwise = true; 00836 m_streamReinitialized = false; 00837 #endif 00838 00839 // init preHeader 00840 memcpy(m_preHeader.magic, Magic, 3); 00841 m_preHeader.version = PGFVersion | flags; 00842 m_preHeader.hSize = HeaderSize; 00843 00844 // copy header 00845 memcpy(&m_header, &header, HeaderSize); 00846 00847 // complete header 00848 CompleteHeader(); 00849 00850 // check and set number of levels 00851 ComputeLevels(); 00852 00853 // misuse background value to store bits per channel 00854 BYTE bpc = m_header.bpp/m_header.channels; 00855 if (bpc > 8) { 00856 if (bpc > 31) bpc = 31; 00857 m_header.background.rgbtBlue = bpc; 00858 } 00859 00860 // check for downsample 00861 if (m_header.quality > DownsampleThreshold && (m_header.mode == ImageModeRGBColor || 00862 m_header.mode == ImageModeRGBA || 00863 m_header.mode == ImageModeRGB48 || 00864 m_header.mode == ImageModeCMYKColor || 00865 m_header.mode == ImageModeCMYK64 || 00866 m_header.mode == ImageModeLabColor || 00867 m_header.mode == ImageModeLab48)) { 00868 m_downsample = true; 00869 m_quant = m_header.quality - 1; 00870 } else { 00871 m_downsample = false; 00872 m_quant = m_header.quality; 00873 } 00874 00875 // update header size and copy user data 00876 if (m_header.mode == ImageModeIndexedColor) { 00877 m_preHeader.hSize += ColorTableSize; 00878 } 00879 if (userDataLength && userData) { 00880 m_postHeader.userData = new(std::nothrow) UINT8[userDataLength]; 00881 if (!m_postHeader.userData) ReturnWithError(InsufficientMemory); 00882 m_postHeader.userDataLen = userDataLength; 00883 memcpy(m_postHeader.userData, userData, userDataLength); 00884 m_preHeader.hSize += userDataLength; 00885 } 00886 00887 // allocate channels 00888 for (int i=0; i < m_header.channels; i++) { 00889 // set current width and height 00890 m_width[i] = m_header.width; 00891 m_height[i] = m_header.height; 00892 00893 // allocate channels 00894 ASSERT(!m_channel[i]); 00895 m_channel[i] = new(std::nothrow) DataT[m_header.width*m_header.height]; 00896 if (!m_channel[i]) { 00897 if (i) i--; 00898 while(i) { 00899 delete[] m_channel[i]; m_channel[i] = 0; 00900 i--; 00901 } 00902 ReturnWithError(InsufficientMemory); 00903 } 00904 } 00905 } 00906 00908 // Create wavelet transform channels and encoder. 00909 // Call this method before your first call of Write(int level), but after SetHeader(). 00910 // Don't use this method when you call Write(). 00911 // It might throw an IOException. 00912 // @param stream A PGF stream 00913 // @return The number of bytes written into stream. 00914 UINT32 CPGFImage::WriteHeader(CPGFStream* stream) THROW_ { 00915 ASSERT(m_header.nLevels <= MaxLevel); 00916 ASSERT(m_header.quality <= MaxQuality); // quality is already initialized 00917 00918 if (m_header.nLevels > 0) { 00919 volatile OSError error = NoError; // volatile prevents optimizations 00920 // create new wt channels 00921 #pragma omp parallel for default(shared) 00922 for (int i=0; i < m_header.channels; i++) { 00923 DataT *temp = NULL; 00924 if (error == NoError) { 00925 if (m_wtChannel[i]) { 00926 ASSERT(m_channel[i]); 00927 // copy m_channel to temp 00928 int size = m_height[i]*m_width[i]; 00929 temp = new(std::nothrow) DataT[size]; 00930 if (temp) { 00931 memcpy(temp, m_channel[i], size*DataTSize); 00932 delete m_wtChannel[i]; // also deletes m_channel 00933 } else { 00934 error = InsufficientMemory; 00935 } 00936 } 00937 if (error == NoError) { 00938 if (temp) m_channel[i] = temp; 00939 m_wtChannel[i] = new CWaveletTransform(m_width[i], m_height[i], m_header.nLevels, m_channel[i]); 00940 00941 // wavelet subband decomposition 00942 for (int l=0; error == NoError && l < m_header.nLevels; l++) { 00943 OSError err = m_wtChannel[i]->ForwardTransform(l, m_quant); 00944 if (err != NoError) error = err; 00945 } 00946 } 00947 } 00948 } 00949 if (error != NoError) ReturnWithError(error); 00950 00951 m_currentLevel = m_header.nLevels; 00952 00953 #ifdef __PGFROISUPPORT__ 00954 if (m_levelwise) { 00955 m_preHeader.version |= PGFROI; 00956 } 00957 #endif 00958 00959 // create encoder and eventually write headers and levelLength 00960 m_encoder = new CEncoder(stream, m_preHeader, m_header, m_postHeader, m_levelLength, m_useOMPinEncoder); 00961 if (m_favorSpeedOverSize) m_encoder->FavorSpeedOverSize(); 00962 00963 #ifdef __PGFROISUPPORT__ 00964 if (ROIisSupported()) { 00965 // new encoding scheme supporting ROI 00966 m_encoder->SetROI(); 00967 } 00968 #endif 00969 00970 // return number of written bytes 00971 return m_encoder->ComputeHeaderLength(); 00972 00973 } else { 00974 // very small image: we don't use DWT and encoding 00975 00976 // create encoder and eventually write headers and levelLength 00977 m_encoder = new CEncoder(stream, m_preHeader, m_header, m_postHeader, m_levelLength, m_useOMPinEncoder); 00978 00979 // write channels 00980 for (int c=0; c < m_header.channels; c++) { 00981 const UINT32 size = m_width[c]*m_height[c]; 00982 00983 // write channel data into stream 00984 for (UINT32 i=0; i < size; i++) { 00985 int count = DataTSize; 00986 stream->Write(&count, &m_channel[c][i]); 00987 } 00988 } 00989 00990 // write level lengths 00991 UINT32 nBytes = m_encoder->WriteLevelLength(); // return written bytes inclusive header 00992 00993 // delete encoder 00994 delete m_encoder; m_encoder = NULL; 00995 00996 // return number of written bytes 00997 return nBytes; 00998 } 00999 } 01000 01002 // Encode and write next level of a PGF image at current stream position. 01003 // A PGF image is structered in levels, numbered between 0 and Levels() - 1. 01004 // Each level can be seen as a single image, containing the same content 01005 // as all other levels, but in a different size (width, height). 01006 // The image size at level i is double the size (width, height) of the image at level i+1. 01007 // The image at level 0 contains the original size. 01008 // It might throw an IOException. 01009 void CPGFImage::WriteLevel() THROW_ { 01010 ASSERT(m_encoder); 01011 ASSERT(m_currentLevel > 0); 01012 ASSERT(m_header.nLevels > 0); 01013 01014 #ifdef __PGFROISUPPORT__ 01015 if (ROIisSupported()) { 01016 const int lastChannel = m_header.channels - 1; 01017 01018 for (int i=0; i < m_header.channels; i++) { 01019 m_wtChannel[i]->SetROI(); 01020 01021 // get number of tiles and tile indices 01022 const UINT32 nTiles = m_wtChannel[i]->GetNofTiles(m_currentLevel); 01023 const UINT32 lastTile = nTiles - 1; 01024 01025 if (m_currentLevel == m_header.nLevels) { 01026 // last level also has LL band 01027 ASSERT(nTiles == 1); 01028 m_wtChannel[i]->GetSubband(m_currentLevel, LL)->ExtractTile(*m_encoder); 01029 m_encoder->EncodeTileBuffer(); 01030 } 01031 for (UINT32 tileY=0; tileY < nTiles; tileY++) { 01032 for (UINT32 tileX=0; tileX < nTiles; tileX++) { 01033 m_wtChannel[i]->GetSubband(m_currentLevel, HL)->ExtractTile(*m_encoder, true, tileX, tileY); 01034 m_wtChannel[i]->GetSubband(m_currentLevel, LH)->ExtractTile(*m_encoder, true, tileX, tileY); 01035 m_wtChannel[i]->GetSubband(m_currentLevel, HH)->ExtractTile(*m_encoder, true, tileX, tileY); 01036 if (i == lastChannel && tileY == lastTile && tileX == lastTile) { 01037 // all necessary data are buffered. next call of EncodeBuffer will write the last piece of data of the current level. 01038 m_encoder->SetEncodedLevel(--m_currentLevel); 01039 } 01040 m_encoder->EncodeTileBuffer(); 01041 } 01042 } 01043 } 01044 } else 01045 #endif 01046 { 01047 for (int i=0; i < m_header.channels; i++) { 01048 ASSERT(m_wtChannel[i]); 01049 if (m_currentLevel == m_header.nLevels) { 01050 // last level also has LL band 01051 m_wtChannel[i]->GetSubband(m_currentLevel, LL)->ExtractTile(*m_encoder); 01052 } 01053 //encoder.EncodeInterleaved(m_wtChannel[i], m_currentLevel, m_quant); // until version 4 01054 m_wtChannel[i]->GetSubband(m_currentLevel, HL)->ExtractTile(*m_encoder); // since version 5 01055 m_wtChannel[i]->GetSubband(m_currentLevel, LH)->ExtractTile(*m_encoder); // since version 5 01056 m_wtChannel[i]->GetSubband(m_currentLevel, HH)->ExtractTile(*m_encoder); 01057 } 01058 01059 // all necessary data are buffered. next call of EncodeBuffer will write the last piece of data of the current level. 01060 m_encoder->SetEncodedLevel(--m_currentLevel); 01061 } 01062 } 01063 01065 // Encode and write a PGF image at current stream position. 01066 // A PGF image is structered in levels, numbered between 0 and Levels() - 1. 01067 // Each level can be seen as a single image, containing the same content 01068 // as all other levels, but in a different size (width, height). 01069 // The image size at level i is double the size (width, height) of the image at level i+1. 01070 // The image at level 0 contains the original size. 01071 // Precondition: the PGF image contains a valid header (see also SetHeader(...)). 01072 // It might throw an IOException. 01073 // @param stream A PGF stream 01074 // @param nWrittenBytes [in-out] The number of bytes written into stream are added to the input value. 01075 // @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. 01076 // @param data Data Pointer to C++ class container to host callback procedure. 01077 void CPGFImage::Write(CPGFStream* stream, UINT32* nWrittenBytes /*= NULL*/, CallbackPtr cb /*= NULL*/, void *data /*=NULL*/) THROW_ { 01078 ASSERT(stream); 01079 ASSERT(m_preHeader.hSize); 01080 01081 #ifdef __PGFROISUPPORT__ 01082 // don't use level-wise writing 01083 m_levelwise = false; 01084 #endif 01085 01086 // create wavelet transform channels and encoder 01087 WriteHeader(stream); 01088 01089 int levels = m_header.nLevels; 01090 double percent = pow(0.25, levels - 1); 01091 01092 if (levels == 0) { 01093 // data has been written in WriteHeader 01094 // now update progress 01095 if (cb) { 01096 if ((*cb)(1, true, data)) ReturnWithError(EscapePressed); 01097 } 01098 } else { 01099 // encode quantized wavelet coefficients and write to PGF file 01100 // encode subbands, higher levels first 01101 // color channels are interleaved 01102 01103 // encode all levels 01104 for (m_currentLevel = levels; m_currentLevel > 0; ) { 01105 WriteLevel(); // decrements m_currentLevel 01106 01107 // now update progress 01108 if (cb) { 01109 percent *= 4; 01110 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed); 01111 } 01112 } 01113 01114 // flush encoder and write level lengths 01115 m_encoder->Flush(); 01116 UINT32 nBytes = m_encoder->WriteLevelLength(); // inclusive header 01117 01118 // delete encoder 01119 delete m_encoder; m_encoder = NULL; 01120 01121 // return written bytes 01122 if (nWrittenBytes) *nWrittenBytes += nBytes; 01123 } 01124 01125 ASSERT(!m_encoder); 01126 } 01127 01128 #ifdef __PGFROISUPPORT__ 01129 01130 // Encode and write down to given level at current stream position. 01131 // A PGF image is structered in levels, numbered between 0 and Levels() - 1. 01132 // Each level can be seen as a single image, containing the same content 01133 // as all other levels, but in a different size (width, height). 01134 // The image size at level i is double the size (width, height) of the image at level i+1. 01135 // The image at level 0 contains the original size. 01136 // Precondition: the PGF image contains a valid header (see also SetHeader(...)) and WriteHeader() has been called before Write(). 01137 // The ROI encoding scheme is used. 01138 // It might throw an IOException. 01139 // @param level The image level of the resulting image in the internal image buffer. 01140 // @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. 01141 // @param data Data Pointer to C++ class container to host callback procedure. 01142 // @return The number of bytes written into stream. 01143 UINT32 CPGFImage::Write(int level, CallbackPtr cb /*= NULL*/, void *data /*=NULL*/) THROW_ { 01144 ASSERT(m_header.nLevels > 0); 01145 ASSERT(0 <= level && level < m_header.nLevels); 01146 ASSERT(m_encoder); 01147 ASSERT(ROIisSupported()); 01148 01149 // prepare for next level: save current file position, because the stream might have been reinitialized 01150 UINT32 diff = m_encoder->ComputeBufferLength(); 01151 if (diff) { 01152 m_streamReinitialized = true; 01153 m_encoder->SetBufferStartPos(); 01154 } 01155 01156 const int levelDiff = m_currentLevel - level; 01157 double percent = pow(0.25, levelDiff); 01158 UINT32 nWrittenBytes = 0; 01159 int levelIndex = m_header.nLevels - 1 - m_currentLevel; 01160 01161 // encoding scheme with ROI 01162 while (m_currentLevel > level) { 01163 levelIndex++; 01164 01165 WriteLevel(); 01166 01167 if (m_levelLength) nWrittenBytes += m_levelLength[levelIndex]; 01168 01169 // now update progress 01170 if (cb) { 01171 percent *= 4; 01172 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed); 01173 } 01174 } 01175 01176 // automatically closing 01177 if (m_currentLevel == 0) { 01178 if (!m_streamReinitialized) { 01179 // don't write level lengths, if the stream position changed inbetween two Write operations 01180 m_encoder->WriteLevelLength(); 01181 } 01182 // delete encoder 01183 delete m_encoder; m_encoder = NULL; 01184 } 01185 01186 return nWrittenBytes; 01187 } 01188 #endif // __PGFROISUPPORT__ 01189 01190 01192 // Check for valid import image mode. 01193 // @param mode Image mode 01194 // @return True if an image of given mode can be imported with ImportBitmap(...) 01195 bool CPGFImage::ImportIsSupported(BYTE mode) { 01196 size_t size = DataTSize; 01197 01198 if (size >= 2) { 01199 switch(mode) { 01200 case ImageModeBitmap: 01201 case ImageModeIndexedColor: 01202 case ImageModeGrayScale: 01203 case ImageModeRGBColor: 01204 case ImageModeCMYKColor: 01205 case ImageModeHSLColor: 01206 case ImageModeHSBColor: 01207 //case ImageModeDuotone: 01208 case ImageModeLabColor: 01209 case ImageModeRGB12: 01210 case ImageModeRGB16: 01211 case ImageModeRGBA: 01212 return true; 01213 } 01214 } 01215 if (size >= 3) { 01216 switch(mode) { 01217 case ImageModeGray16: 01218 case ImageModeRGB48: 01219 case ImageModeLab48: 01220 case ImageModeCMYK64: 01221 //case ImageModeDuotone16: 01222 return true; 01223 } 01224 } 01225 if (size >=4) { 01226 switch(mode) { 01227 case ImageModeGray31: 01228 return true; 01229 } 01230 } 01231 return false; 01232 } 01233 01240 void CPGFImage::GetColorTable(UINT32 iFirstColor, UINT32 nColors, RGBQUAD* prgbColors) const THROW_ { 01241 if (iFirstColor + nColors > ColorTableLen) ReturnWithError(ColorTableError); 01242 01243 for (UINT32 i=iFirstColor, j=0; j < nColors; i++, j++) { 01244 prgbColors[j] = m_postHeader.clut[i]; 01245 } 01246 } 01247 01254 void CPGFImage::SetColorTable(UINT32 iFirstColor, UINT32 nColors, const RGBQUAD* prgbColors) THROW_ { 01255 if (iFirstColor + nColors > ColorTableLen) ReturnWithError(ColorTableError); 01256 01257 for (UINT32 i=iFirstColor, j=0; j < nColors; i++, j++) { 01258 m_postHeader.clut[i] = prgbColors[j]; 01259 } 01260 } 01261 01263 // Buffer transform from interleaved to channel seperated format 01264 // the absolute value of pitch is the number of bytes of an image row 01265 // if pitch is negative, then buff points to the last row of a bottom-up image (first byte on last row) 01266 // if pitch is positive, then buff points to the first row of a top-down image (first byte) 01267 // bpp is the number of bits per pixel used in image buffer buff 01268 // 01269 // RGB is transformed into YUV format (ordering of buffer data is BGR[A]) 01270 // Y = (R + 2*G + B)/4 -128 01271 // U = R - G 01272 // V = B - G 01273 // 01274 // Since PGF Codec version 2.0 images are stored in top-down direction 01275 // 01276 // 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 01277 // provide a channelMap of size of expected channels (depending on image mode). For example, PGF expects in RGB color mode a channel sequence BGR. 01278 // If your provided image buffer contains a channel sequence ARGB, then the channelMap looks like { 3, 2, 1 }. 01279 void CPGFImage::RgbToYuv(int pitch, UINT8* buff, BYTE bpp, int channelMap[], CallbackPtr cb, void *data /*=NULL*/) THROW_ { 01280 ASSERT(buff); 01281 int yPos = 0, cnt = 0; 01282 double percent = 0; 01283 const double dP = 1.0/m_header.height; 01284 int defMap[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; ASSERT(sizeof(defMap)/sizeof(defMap[0]) == MaxChannels); 01285 01286 if (channelMap == NULL) channelMap = defMap; 01287 01288 switch(m_header.mode) { 01289 case ImageModeBitmap: 01290 { 01291 ASSERT(m_header.channels == 1); 01292 ASSERT(m_header.bpp == 1); 01293 ASSERT(bpp == 1); 01294 01295 const UINT32 w2 = (m_header.width + 7)/8; 01296 DataT* y = m_channel[0]; ASSERT(y); 01297 01298 for (UINT32 h=0; h < m_header.height; h++) { 01299 if (cb) { 01300 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed); 01301 percent += dP; 01302 } 01303 01304 for (UINT32 w=0; w < w2; w++) { 01305 y[yPos++] = buff[w] - YUVoffset8; 01306 } 01307 buff += pitch; 01308 } 01309 } 01310 break; 01311 case ImageModeIndexedColor: 01312 case ImageModeGrayScale: 01313 case ImageModeHSLColor: 01314 case ImageModeHSBColor: 01315 case ImageModeLabColor: 01316 { 01317 ASSERT(m_header.channels >= 1); 01318 ASSERT(m_header.bpp == m_header.channels*8); 01319 ASSERT(bpp%8 == 0); 01320 const int channels = bpp/8; ASSERT(channels >= m_header.channels); 01321 01322 for (UINT32 h=0; h < m_header.height; h++) { 01323 if (cb) { 01324 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed); 01325 percent += dP; 01326 } 01327 01328 cnt = 0; 01329 for (UINT32 w=0; w < m_header.width; w++) { 01330 for (int c=0; c < m_header.channels; c++) { 01331 m_channel[c][yPos] = buff[cnt + channelMap[c]] - YUVoffset8; 01332 } 01333 cnt += channels; 01334 yPos++; 01335 } 01336 buff += pitch; 01337 } 01338 } 01339 break; 01340 case ImageModeGray16: 01341 case ImageModeLab48: 01342 { 01343 ASSERT(m_header.channels >= 1); 01344 ASSERT(m_header.bpp == m_header.channels*16); 01345 ASSERT(bpp%16 == 0); 01346 01347 UINT16 *buff16 = (UINT16 *)buff; 01348 const int pitch16 = pitch/2; 01349 const int channels = bpp/16; ASSERT(channels >= m_header.channels); 01350 const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1); 01351 01352 for (UINT32 h=0; h < m_header.height; h++) { 01353 if (cb) { 01354 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed); 01355 percent += dP; 01356 } 01357 01358 cnt = 0; 01359 for (UINT32 w=0; w < m_header.width; w++) { 01360 for (int c=0; c < m_header.channels; c++) { 01361 m_channel[c][yPos] = buff16[cnt + channelMap[c]] - yuvOffset16; 01362 } 01363 cnt += channels; 01364 yPos++; 01365 } 01366 buff16 += pitch16; 01367 } 01368 } 01369 break; 01370 case ImageModeRGBColor: 01371 { 01372 ASSERT(m_header.channels == 3); 01373 ASSERT(m_header.bpp == m_header.channels*8); 01374 ASSERT(bpp%8 == 0); 01375 01376 DataT* y = m_channel[0]; ASSERT(y); 01377 DataT* u = m_channel[1]; ASSERT(u); 01378 DataT* v = m_channel[2]; ASSERT(v); 01379 const int channels = bpp/8; ASSERT(channels >= m_header.channels); 01380 UINT8 b, g, r; 01381 01382 for (UINT32 h=0; h < m_header.height; h++) { 01383 if (cb) { 01384 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed); 01385 percent += dP; 01386 } 01387 01388 cnt = 0; 01389 for (UINT32 w=0; w < m_header.width; w++) { 01390 b = buff[cnt + channelMap[0]]; 01391 g = buff[cnt + channelMap[1]]; 01392 r = buff[cnt + channelMap[2]]; 01393 // Yuv 01394 y[yPos] = ((b + (g << 1) + r) >> 2) - YUVoffset8; 01395 u[yPos] = r - g; 01396 v[yPos] = b - g; 01397 yPos++; 01398 cnt += channels; 01399 } 01400 buff += pitch; 01401 } 01402 } 01403 break; 01404 case ImageModeRGB48: 01405 { 01406 ASSERT(m_header.channels == 3); 01407 ASSERT(m_header.bpp == m_header.channels*16); 01408 ASSERT(bpp%16 == 0); 01409 01410 UINT16 *buff16 = (UINT16 *)buff; 01411 const int pitch16 = pitch/2; 01412 const int channels = bpp/16; ASSERT(channels >= m_header.channels); 01413 const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1); 01414 01415 DataT* y = m_channel[0]; ASSERT(y); 01416 DataT* u = m_channel[1]; ASSERT(u); 01417 DataT* v = m_channel[2]; ASSERT(v); 01418 UINT16 b, g, r; 01419 01420 for (UINT32 h=0; h < m_header.height; h++) { 01421 if (cb) { 01422 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed); 01423 percent += dP; 01424 } 01425 01426 cnt = 0; 01427 for (UINT32 w=0; w < m_header.width; w++) { 01428 b = buff16[cnt + channelMap[0]]; 01429 g = buff16[cnt + channelMap[1]]; 01430 r = buff16[cnt + channelMap[2]]; 01431 // Yuv 01432 y[yPos] = ((b + (g << 1) + r) >> 2) - yuvOffset16; 01433 u[yPos] = r - g; 01434 v[yPos] = b - g; 01435 yPos++; 01436 cnt += channels; 01437 } 01438 buff16 += pitch16; 01439 } 01440 } 01441 break; 01442 case ImageModeRGBA: 01443 case ImageModeCMYKColor: 01444 { 01445 ASSERT(m_header.channels == 4); 01446 ASSERT(m_header.bpp == m_header.channels*8); 01447 ASSERT(bpp%8 == 0); 01448 const int channels = bpp/8; ASSERT(channels >= m_header.channels); 01449 01450 DataT* y = m_channel[0]; ASSERT(y); 01451 DataT* u = m_channel[1]; ASSERT(u); 01452 DataT* v = m_channel[2]; ASSERT(v); 01453 DataT* a = m_channel[3]; ASSERT(a); 01454 UINT8 b, g, r; 01455 01456 for (UINT32 h=0; h < m_header.height; h++) { 01457 if (cb) { 01458 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed); 01459 percent += dP; 01460 } 01461 01462 cnt = 0; 01463 for (UINT32 w=0; w < m_header.width; w++) { 01464 b = buff[cnt + channelMap[0]]; 01465 g = buff[cnt + channelMap[1]]; 01466 r = buff[cnt + channelMap[2]]; 01467 // Yuv 01468 y[yPos] = ((b + (g << 1) + r) >> 2) - YUVoffset8; 01469 u[yPos] = r - g; 01470 v[yPos] = b - g; 01471 a[yPos++] = buff[cnt + channelMap[3]] - YUVoffset8; 01472 cnt += channels; 01473 } 01474 buff += pitch; 01475 } 01476 } 01477 break; 01478 case ImageModeCMYK64: 01479 { 01480 ASSERT(m_header.channels == 4); 01481 ASSERT(m_header.bpp == m_header.channels*16); 01482 ASSERT(bpp%16 == 0); 01483 01484 UINT16 *buff16 = (UINT16 *)buff; 01485 const int pitch16 = pitch/2; 01486 const int channels = bpp/16; ASSERT(channels >= m_header.channels); 01487 const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1); 01488 01489 DataT* y = m_channel[0]; ASSERT(y); 01490 DataT* u = m_channel[1]; ASSERT(u); 01491 DataT* v = m_channel[2]; ASSERT(v); 01492 DataT* a = m_channel[3]; ASSERT(a); 01493 UINT16 b, g, r; 01494 01495 for (UINT32 h=0; h < m_header.height; h++) { 01496 if (cb) { 01497 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed); 01498 percent += dP; 01499 } 01500 01501 cnt = 0; 01502 for (UINT32 w=0; w < m_header.width; w++) { 01503 b = buff16[cnt + channelMap[0]]; 01504 g = buff16[cnt + channelMap[1]]; 01505 r = buff16[cnt + channelMap[2]]; 01506 // Yuv 01507 y[yPos] = ((b + (g << 1) + r) >> 2) - yuvOffset16; 01508 u[yPos] = r - g; 01509 v[yPos] = b - g; 01510 a[yPos++] = buff16[cnt + channelMap[3]] - yuvOffset16; 01511 cnt += channels; 01512 } 01513 buff16 += pitch16; 01514 } 01515 } 01516 break; 01517 #ifdef __PGF32SUPPORT__ 01518 case ImageModeGray31: 01519 { 01520 ASSERT(m_header.channels == 1); 01521 ASSERT(m_header.bpp == 32); 01522 ASSERT(bpp == 32); 01523 ASSERT(DataTSize == sizeof(UINT32)); 01524 01525 DataT* y = m_channel[0]; ASSERT(y); 01526 01527 UINT32 *buff32 = (UINT32 *)buff; 01528 const int pitch32 = pitch/4; 01529 const DataT yuvOffset31 = 1 << (UsedBitsPerChannel() - 1); 01530 01531 for (UINT32 h=0; h < m_header.height; h++) { 01532 if (cb) { 01533 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed); 01534 percent += dP; 01535 } 01536 01537 for (UINT32 w=0; w < m_header.width; w++) { 01538 ASSERT(buff32[cnt] < MaxValue); 01539 y[yPos++] = buff32[w] - yuvOffset31; 01540 } 01541 buff32 += pitch32; 01542 } 01543 } 01544 break; 01545 #endif 01546 case ImageModeRGB12: 01547 { 01548 ASSERT(m_header.channels == 3); 01549 ASSERT(m_header.bpp == m_header.channels*4); 01550 ASSERT(bpp == m_header.channels*4); 01551 01552 DataT* y = m_channel[0]; ASSERT(y); 01553 DataT* u = m_channel[1]; ASSERT(u); 01554 DataT* v = m_channel[2]; ASSERT(v); 01555 01556 UINT8 rgb = 0, b, g, r; 01557 01558 for (UINT32 h=0; h < m_header.height; h++) { 01559 if (cb) { 01560 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed); 01561 percent += dP; 01562 } 01563 01564 cnt = 0; 01565 for (UINT32 w=0; w < m_header.width; w++) { 01566 if (w%2 == 0) { 01567 // even pixel position 01568 rgb = buff[cnt]; 01569 b = rgb & 0x0F; 01570 g = (rgb & 0xF0) >> 4; 01571 cnt++; 01572 rgb = buff[cnt]; 01573 r = rgb & 0x0F; 01574 } else { 01575 // odd pixel position 01576 b = (rgb & 0xF0) >> 4; 01577 cnt++; 01578 rgb = buff[cnt]; 01579 g = rgb & 0x0F; 01580 r = (rgb & 0xF0) >> 4; 01581 cnt++; 01582 } 01583 01584 // Yuv 01585 y[yPos] = ((b + (g << 1) + r) >> 2) - YUVoffset4; 01586 u[yPos] = r - g; 01587 v[yPos] = b - g; 01588 yPos++; 01589 } 01590 buff += pitch; 01591 } 01592 } 01593 break; 01594 case ImageModeRGB16: 01595 { 01596 ASSERT(m_header.channels == 3); 01597 ASSERT(m_header.bpp == 16); 01598 ASSERT(bpp == 16); 01599 01600 DataT* y = m_channel[0]; ASSERT(y); 01601 DataT* u = m_channel[1]; ASSERT(u); 01602 DataT* v = m_channel[2]; ASSERT(v); 01603 01604 UINT16 *buff16 = (UINT16 *)buff; 01605 UINT16 rgb, b, g, r; 01606 const int pitch16 = pitch/2; 01607 01608 for (UINT32 h=0; h < m_header.height; h++) { 01609 if (cb) { 01610 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed); 01611 percent += dP; 01612 } 01613 for (UINT32 w=0; w < m_header.width; w++) { 01614 rgb = buff16[w]; 01615 r = (rgb & 0xF800) >> 10; // highest 5 bits 01616 g = (rgb & 0x07E0) >> 5; // middle 6 bits 01617 b = (rgb & 0x001F) << 1; // lowest 5 bits 01618 // Yuv 01619 y[yPos] = ((b + (g << 1) + r) >> 2) - YUVoffset6; 01620 u[yPos] = r - g; 01621 v[yPos] = b - g; 01622 yPos++; 01623 } 01624 01625 buff16 += pitch16; 01626 } 01627 } 01628 break; 01629 default: 01630 ASSERT(false); 01631 } 01632 } 01633 01635 // Get image data in interleaved format: (ordering of RGB data is BGR[A]) 01636 // Upsampling, YUV to RGB transform and interleaving are done here to reduce the number 01637 // of passes over the data. 01638 // The absolute value of pitch is the number of bytes of an image row of the given image buffer. 01639 // If pitch is negative, then the image buffer must point to the last row of a bottom-up image (first byte on last row). 01640 // if pitch is positive, then the image buffer must point to the first row of a top-down image (first byte). 01641 // 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 01642 // provide a channelMap of size of expected channels (depending on image mode). For example, PGF provides a channel sequence BGR in RGB color mode. 01643 // If your provided image buffer expects a channel sequence ARGB, then the channelMap looks like { 3, 2, 1 }. 01644 // It might throw an IOException. 01645 // @param pitch The number of bytes of a row of the image buffer. 01646 // @param buff An image buffer. 01647 // @param bpp The number of bits per pixel used in image buffer. 01648 // @param channelMap A integer array containing the mapping of PGF channel ordering to expected channel ordering. 01649 // @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. 01650 // @param data Data Pointer to C++ class container to host callback procedure. 01651 void CPGFImage::GetBitmap(int pitch, UINT8* buff, BYTE bpp, int channelMap[] /*= NULL */, CallbackPtr cb /*= NULL*/, void *data /*=NULL*/) const THROW_ { 01652 ASSERT(buff); 01653 UINT32 w = m_width[0]; 01654 UINT32 h = m_height[0]; 01655 UINT8* targetBuff = 0; // used if ROI is used 01656 UINT8* buffStart = 0; // used if ROI is used 01657 int targetPitch = 0; // used if ROI is used 01658 01659 #ifdef __PGFROISUPPORT__ 01660 const PGFRect& roi = (ROIisSupported()) ? m_wtChannel[0]->GetROI(m_currentLevel) : PGFRect(0, 0, w, h); // roi is usually larger than m_roi 01661 const PGFRect levelRoi(LevelWidth(m_roi.left, m_currentLevel), LevelHeight(m_roi.top, m_currentLevel), LevelWidth(m_roi.Width(), m_currentLevel), LevelHeight(m_roi.Height(), m_currentLevel)); 01662 ASSERT(w == roi.Width() && h == roi.Height()); 01663 ASSERT(roi.left <= levelRoi.left && levelRoi.right <= roi.right); 01664 ASSERT(roi.top <= levelRoi.top && levelRoi.bottom <= roi.bottom); 01665 01666 if (ROIisSupported() && (levelRoi.Width() < w || levelRoi.Height() < h)) { 01667 // ROI is used -> create a temporary image buffer for roi 01668 // compute pitch 01669 targetPitch = pitch; 01670 pitch = AlignWordPos(w*bpp)/8; 01671 01672 // create temporary output buffer 01673 targetBuff = buff; 01674 buff = buffStart = new(std::nothrow) UINT8[pitch*h]; 01675 if (!buff) ReturnWithError(InsufficientMemory); 01676 } 01677 #endif 01678 01679 const bool wOdd = (1 == w%2); 01680 01681 const double dP = 1.0/h; 01682 int defMap[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; ASSERT(sizeof(defMap)/sizeof(defMap[0]) == MaxChannels); 01683 if (channelMap == NULL) channelMap = defMap; 01684 int sampledPos = 0, yPos = 0; 01685 DataT uAvg, vAvg; 01686 double percent = 0; 01687 UINT32 i, j; 01688 01689 switch(m_header.mode) { 01690 case ImageModeBitmap: 01691 { 01692 ASSERT(m_header.channels == 1); 01693 ASSERT(m_header.bpp == 1); 01694 ASSERT(bpp == 1); 01695 01696 const UINT32 w2 = (w + 7)/8; 01697 DataT* y = m_channel[0]; ASSERT(y); 01698 01699 for (i=0; i < h; i++) { 01700 for (j=0; j < w2; j++) { 01701 buff[j] = Clamp(y[yPos++] + YUVoffset8); 01702 } 01703 buff += pitch; 01704 01705 if (cb) { 01706 percent += dP; 01707 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed); 01708 } 01709 } 01710 break; 01711 } 01712 case ImageModeIndexedColor: 01713 case ImageModeGrayScale: 01714 case ImageModeHSLColor: 01715 case ImageModeHSBColor: 01716 { 01717 ASSERT(m_header.channels >= 1); 01718 ASSERT(m_header.bpp == m_header.channels*8); 01719 ASSERT(bpp%8 == 0); 01720 01721 int cnt, channels = bpp/8; ASSERT(channels >= m_header.channels); 01722 01723 for (i=0; i < h; i++) { 01724 cnt = 0; 01725 for (j=0; j < w; j++) { 01726 for (int c=0; c < m_header.channels; c++) { 01727 buff[cnt + channelMap[c]] = Clamp(m_channel[c][yPos] + YUVoffset8); 01728 } 01729 cnt += channels; 01730 yPos++; 01731 } 01732 buff += pitch; 01733 01734 if (cb) { 01735 percent += dP; 01736 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed); 01737 } 01738 } 01739 break; 01740 } 01741 case ImageModeGray16: 01742 { 01743 ASSERT(m_header.channels >= 1); 01744 ASSERT(m_header.bpp == m_header.channels*16); 01745 01746 const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1); 01747 const int shift = UsedBitsPerChannel() - 8; 01748 int cnt, channels; 01749 01750 if (bpp%16 == 0) { 01751 UINT16 *buff16 = (UINT16 *)buff; 01752 int pitch16 = pitch/2; 01753 channels = bpp/16; ASSERT(channels >= m_header.channels); 01754 01755 for (i=0; i < h; i++) { 01756 cnt = 0; 01757 for (j=0; j < w; j++) { 01758 for (int c=0; c < m_header.channels; c++) { 01759 buff16[cnt + channelMap[c]] = Clamp16(m_channel[c][yPos] + yuvOffset16); 01760 } 01761 cnt += channels; 01762 yPos++; 01763 } 01764 buff16 += pitch16; 01765 01766 if (cb) { 01767 percent += dP; 01768 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed); 01769 } 01770 } 01771 } else { 01772 ASSERT(bpp%8 == 0); 01773 channels = bpp/8; ASSERT(channels >= m_header.channels); 01774 01775 for (i=0; i < h; i++) { 01776 cnt = 0; 01777 for (j=0; j < w; j++) { 01778 for (int c=0; c < m_header.channels; c++) { 01779 buff[cnt + channelMap[c]] = UINT8(Clamp16(m_channel[c][yPos] + yuvOffset16) >> shift); 01780 } 01781 cnt += channels; 01782 yPos++; 01783 } 01784 buff += pitch; 01785 01786 if (cb) { 01787 percent += dP; 01788 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed); 01789 } 01790 } 01791 } 01792 break; 01793 } 01794 case ImageModeRGBColor: 01795 { 01796 ASSERT(m_header.channels == 3); 01797 ASSERT(m_header.bpp == m_header.channels*8); 01798 ASSERT(bpp%8 == 0); 01799 ASSERT(bpp >= m_header.bpp); 01800 01801 DataT* y = m_channel[0]; ASSERT(y); 01802 DataT* u = m_channel[1]; ASSERT(u); 01803 DataT* v = m_channel[2]; ASSERT(v); 01804 UINT8 *buffg = &buff[channelMap[1]], 01805 *buffr = &buff[channelMap[2]], 01806 *buffb = &buff[channelMap[0]]; 01807 UINT8 g; 01808 int cnt, channels = bpp/8; 01809 if(m_downsample){ 01810 for (i=0; i < h; i++) { 01811 if (i%2) sampledPos -= (w + 1)/2; 01812 cnt = 0; 01813 for (j=0; j < w; j++) { 01814 // image was downsampled 01815 uAvg = u[sampledPos]; 01816 vAvg = v[sampledPos]; 01817 // Yuv 01818 buffg[cnt] = g = Clamp(y[yPos] + YUVoffset8 - ((uAvg + vAvg ) >> 2)); // must be logical shift operator 01819 buffr[cnt] = Clamp(uAvg + g); 01820 buffb[cnt] = Clamp(vAvg + g); 01821 yPos++; 01822 cnt += channels; 01823 if (j%2) sampledPos++; 01824 } 01825 buffb += pitch; 01826 buffg += pitch; 01827 buffr += pitch; 01828 if (wOdd) sampledPos++; 01829 if (cb) { 01830 percent += dP; 01831 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed); 01832 } 01833 } 01834 }else{ 01835 for (i=0; i < h; i++) { 01836 cnt = 0; 01837 for (j = 0; j < w; j++) { 01838 uAvg = u[yPos]; 01839 vAvg = v[yPos]; 01840 // Yuv 01841 buffg[cnt] = g = Clamp(y[yPos] + YUVoffset8 - ((uAvg + vAvg ) >> 2)); // must be logical shift operator 01842 buffr[cnt] = Clamp(uAvg + g); 01843 buffb[cnt] = Clamp(vAvg + g); 01844 yPos++; 01845 cnt += channels; 01846 } 01847 buffb += pitch; 01848 buffg += pitch; 01849 buffr += pitch; 01850 01851 if (cb) { 01852 percent += dP; 01853 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed); 01854 } 01855 } 01856 } 01857 break; 01858 } 01859 case ImageModeRGB48: 01860 { 01861 ASSERT(m_header.channels == 3); 01862 ASSERT(m_header.bpp == 48); 01863 01864 const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1); 01865 const int shift = UsedBitsPerChannel() - 8; 01866 01867 DataT* y = m_channel[0]; ASSERT(y); 01868 DataT* u = m_channel[1]; ASSERT(u); 01869 DataT* v = m_channel[2]; ASSERT(v); 01870 UINT16 g; 01871 int cnt, channels; 01872 01873 if (bpp >= 48 && bpp%16 == 0) { 01874 UINT16 *buff16 = (UINT16 *)buff; 01875 int pitch16 = pitch/2; 01876 channels = bpp/16; ASSERT(channels >= m_header.channels); 01877 01878 for (i=0; i < h; i++) { 01879 if (i%2) sampledPos -= (w + 1)/2; 01880 cnt = 0; 01881 for (j=0; j < w; j++) { 01882 if (m_downsample) { 01883 // image was downsampled 01884 uAvg = u[sampledPos]; 01885 vAvg = v[sampledPos]; 01886 } else { 01887 uAvg = u[yPos]; 01888 vAvg = v[yPos]; 01889 } 01890 // Yuv 01891 buff16[cnt + channelMap[1]] = g = Clamp16(y[yPos] + yuvOffset16 - ((uAvg + vAvg ) >> 2)); // must be logical shift operator 01892 buff16[cnt + channelMap[2]] = Clamp16(uAvg + g); 01893 buff16[cnt + channelMap[0]] = Clamp16(vAvg + g); 01894 yPos++; 01895 cnt += channels; 01896 if (j%2) sampledPos++; 01897 } 01898 buff16 += pitch16; 01899 if (wOdd) sampledPos++; 01900 01901 if (cb) { 01902 percent += dP; 01903 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed); 01904 } 01905 } 01906 } else { 01907 ASSERT(bpp%8 == 0); 01908 channels = bpp/8; ASSERT(channels >= m_header.channels); 01909 01910 for (i=0; i < h; i++) { 01911 if (i%2) sampledPos -= (w + 1)/2; 01912 cnt = 0; 01913 for (j=0; j < w; j++) { 01914 if (m_downsample) { 01915 // image was downsampled 01916 uAvg = u[sampledPos]; 01917 vAvg = v[sampledPos]; 01918 } else { 01919 uAvg = u[yPos]; 01920 vAvg = v[yPos]; 01921 } 01922 // Yuv 01923 g = Clamp16(y[yPos] + yuvOffset16 - ((uAvg + vAvg ) >> 2)); // must be logical shift operator 01924 buff[cnt + channelMap[1]] = UINT8(g >> shift); 01925 buff[cnt + channelMap[2]] = UINT8(Clamp16(uAvg + g) >> shift); 01926 buff[cnt + channelMap[0]] = UINT8(Clamp16(vAvg + g) >> shift); 01927 yPos++; 01928 cnt += channels; 01929 if (j%2) sampledPos++; 01930 } 01931 buff += pitch; 01932 if (wOdd) sampledPos++; 01933 01934 if (cb) { 01935 percent += dP; 01936 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed); 01937 } 01938 } 01939 } 01940 break; 01941 } 01942 case ImageModeLabColor: 01943 { 01944 ASSERT(m_header.channels == 3); 01945 ASSERT(m_header.bpp == m_header.channels*8); 01946 ASSERT(bpp%8 == 0); 01947 01948 DataT* l = m_channel[0]; ASSERT(l); 01949 DataT* a = m_channel[1]; ASSERT(a); 01950 DataT* b = m_channel[2]; ASSERT(b); 01951 int cnt, channels = bpp/8; ASSERT(channels >= m_header.channels); 01952 01953 for (i=0; i < h; i++) { 01954 if (i%2) sampledPos -= (w + 1)/2; 01955 cnt = 0; 01956 for (j=0; j < w; j++) { 01957 if (m_downsample) { 01958 // image was downsampled 01959 uAvg = a[sampledPos]; 01960 vAvg = b[sampledPos]; 01961 } else { 01962 uAvg = a[yPos]; 01963 vAvg = b[yPos]; 01964 } 01965 buff[cnt + channelMap[0]] = Clamp(l[yPos] + YUVoffset8); 01966 buff[cnt + channelMap[1]] = Clamp(uAvg + YUVoffset8); 01967 buff[cnt + channelMap[2]] = Clamp(vAvg + YUVoffset8); 01968 cnt += channels; 01969 yPos++; 01970 if (j%2) sampledPos++; 01971 } 01972 buff += pitch; 01973 if (wOdd) sampledPos++; 01974 01975 if (cb) { 01976 percent += dP; 01977 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed); 01978 } 01979 } 01980 break; 01981 } 01982 case ImageModeLab48: 01983 { 01984 ASSERT(m_header.channels == 3); 01985 ASSERT(m_header.bpp == m_header.channels*16); 01986 01987 const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1); 01988 const int shift = UsedBitsPerChannel() - 8; 01989 01990 DataT* l = m_channel[0]; ASSERT(l); 01991 DataT* a = m_channel[1]; ASSERT(a); 01992 DataT* b = m_channel[2]; ASSERT(b); 01993 int cnt, channels; 01994 01995 if (bpp%16 == 0) { 01996 UINT16 *buff16 = (UINT16 *)buff; 01997 int pitch16 = pitch/2; 01998 channels = bpp/16; ASSERT(channels >= m_header.channels); 01999 02000 for (i=0; i < h; i++) { 02001 if (i%2) sampledPos -= (w + 1)/2; 02002 cnt = 0; 02003 for (j=0; j < w; j++) { 02004 if (m_downsample) { 02005 // image was downsampled 02006 uAvg = a[sampledPos]; 02007 vAvg = b[sampledPos]; 02008 } else { 02009 uAvg = a[yPos]; 02010 vAvg = b[yPos]; 02011 } 02012 buff16[cnt + channelMap[0]] = Clamp16(l[yPos] + yuvOffset16); 02013 buff16[cnt + channelMap[1]] = Clamp16(uAvg + yuvOffset16); 02014 buff16[cnt + channelMap[2]] = Clamp16(vAvg + yuvOffset16); 02015 cnt += channels; 02016 yPos++; 02017 if (j%2) sampledPos++; 02018 } 02019 buff16 += pitch16; 02020 if (wOdd) sampledPos++; 02021 02022 if (cb) { 02023 percent += dP; 02024 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed); 02025 } 02026 } 02027 } else { 02028 ASSERT(bpp%8 == 0); 02029 channels = bpp/8; ASSERT(channels >= m_header.channels); 02030 02031 for (i=0; i < h; i++) { 02032 if (i%2) sampledPos -= (w + 1)/2; 02033 cnt = 0; 02034 for (j=0; j < w; j++) { 02035 if (m_downsample) { 02036 // image was downsampled 02037 uAvg = a[sampledPos]; 02038 vAvg = b[sampledPos]; 02039 } else { 02040 uAvg = a[yPos]; 02041 vAvg = b[yPos]; 02042 } 02043 buff[cnt + channelMap[0]] = UINT8(Clamp16(l[yPos] + yuvOffset16) >> shift); 02044 buff[cnt + channelMap[1]] = UINT8(Clamp16(uAvg + yuvOffset16) >> shift); 02045 buff[cnt + channelMap[2]] = UINT8(Clamp16(vAvg + yuvOffset16) >> shift); 02046 cnt += channels; 02047 yPos++; 02048 if (j%2) sampledPos++; 02049 } 02050 buff += pitch; 02051 if (wOdd) sampledPos++; 02052 02053 if (cb) { 02054 percent += dP; 02055 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed); 02056 } 02057 } 02058 } 02059 break; 02060 } 02061 case ImageModeRGBA: 02062 case ImageModeCMYKColor: 02063 { 02064 ASSERT(m_header.channels == 4); 02065 ASSERT(m_header.bpp == m_header.channels*8); 02066 ASSERT(bpp%8 == 0); 02067 02068 DataT* y = m_channel[0]; ASSERT(y); 02069 DataT* u = m_channel[1]; ASSERT(u); 02070 DataT* v = m_channel[2]; ASSERT(v); 02071 DataT* a = m_channel[3]; ASSERT(a); 02072 UINT8 g, aAvg; 02073 int cnt, channels = bpp/8; ASSERT(channels >= m_header.channels); 02074 02075 for (i=0; i < h; i++) { 02076 if (i%2) sampledPos -= (w + 1)/2; 02077 cnt = 0; 02078 for (j=0; j < w; j++) { 02079 if (m_downsample) { 02080 // image was downsampled 02081 uAvg = u[sampledPos]; 02082 vAvg = v[sampledPos]; 02083 aAvg = Clamp(a[sampledPos] + YUVoffset8); 02084 } else { 02085 uAvg = u[yPos]; 02086 vAvg = v[yPos]; 02087 aAvg = Clamp(a[yPos] + YUVoffset8); 02088 } 02089 // Yuv 02090 buff[cnt + channelMap[1]] = g = Clamp(y[yPos] + YUVoffset8 - ((uAvg + vAvg ) >> 2)); // must be logical shift operator 02091 buff[cnt + channelMap[2]] = Clamp(uAvg + g); 02092 buff[cnt + channelMap[0]] = Clamp(vAvg + g); 02093 buff[cnt + channelMap[3]] = aAvg; 02094 yPos++; 02095 cnt += channels; 02096 if (j%2) sampledPos++; 02097 } 02098 buff += pitch; 02099 if (wOdd) sampledPos++; 02100 02101 if (cb) { 02102 percent += dP; 02103 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed); 02104 } 02105 } 02106 break; 02107 } 02108 case ImageModeCMYK64: 02109 { 02110 ASSERT(m_header.channels == 4); 02111 ASSERT(m_header.bpp == 64); 02112 02113 const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1); 02114 const int shift = UsedBitsPerChannel() - 8; 02115 02116 DataT* y = m_channel[0]; ASSERT(y); 02117 DataT* u = m_channel[1]; ASSERT(u); 02118 DataT* v = m_channel[2]; ASSERT(v); 02119 DataT* a = m_channel[3]; ASSERT(a); 02120 UINT16 g, aAvg; 02121 int cnt, channels; 02122 02123 if (bpp%16 == 0) { 02124 UINT16 *buff16 = (UINT16 *)buff; 02125 int pitch16 = pitch/2; 02126 channels = bpp/16; ASSERT(channels >= m_header.channels); 02127 02128 for (i=0; i < h; i++) { 02129 if (i%2) sampledPos -= (w + 1)/2; 02130 cnt = 0; 02131 for (j=0; j < w; j++) { 02132 if (m_downsample) { 02133 // image was downsampled 02134 uAvg = u[sampledPos]; 02135 vAvg = v[sampledPos]; 02136 aAvg = Clamp16(a[sampledPos] + yuvOffset16); 02137 } else { 02138 uAvg = u[yPos]; 02139 vAvg = v[yPos]; 02140 aAvg = Clamp16(a[yPos] + yuvOffset16); 02141 } 02142 // Yuv 02143 buff16[cnt + channelMap[1]] = g = Clamp16(y[yPos] + yuvOffset16 - ((uAvg + vAvg ) >> 2)); // must be logical shift operator 02144 buff16[cnt + channelMap[2]] = Clamp16(uAvg + g); 02145 buff16[cnt + channelMap[0]] = Clamp16(vAvg + g); 02146 buff16[cnt + channelMap[3]] = aAvg; 02147 yPos++; 02148 cnt += channels; 02149 if (j%2) sampledPos++; 02150 } 02151 buff16 += pitch16; 02152 if (wOdd) sampledPos++; 02153 02154 if (cb) { 02155 percent += dP; 02156 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed); 02157 } 02158 } 02159 } else { 02160 ASSERT(bpp%8 == 0); 02161 channels = bpp/8; ASSERT(channels >= m_header.channels); 02162 02163 for (i=0; i < h; i++) { 02164 if (i%2) sampledPos -= (w + 1)/2; 02165 cnt = 0; 02166 for (j=0; j < w; j++) { 02167 if (m_downsample) { 02168 // image was downsampled 02169 uAvg = u[sampledPos]; 02170 vAvg = v[sampledPos]; 02171 aAvg = Clamp16(a[sampledPos] + yuvOffset16); 02172 } else { 02173 uAvg = u[yPos]; 02174 vAvg = v[yPos]; 02175 aAvg = Clamp16(a[yPos] + yuvOffset16); 02176 } 02177 // Yuv 02178 g = Clamp16(y[yPos] + yuvOffset16 - ((uAvg + vAvg ) >> 2)); // must be logical shift operator 02179 buff[cnt + channelMap[1]] = UINT8(g >> shift); 02180 buff[cnt + channelMap[2]] = UINT8(Clamp16(uAvg + g) >> shift); 02181 buff[cnt + channelMap[0]] = UINT8(Clamp16(vAvg + g) >> shift); 02182 buff[cnt + channelMap[3]] = UINT8(aAvg >> shift); 02183 yPos++; 02184 cnt += channels; 02185 if (j%2) sampledPos++; 02186 } 02187 buff += pitch; 02188 if (wOdd) sampledPos++; 02189 02190 if (cb) { 02191 percent += dP; 02192 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed); 02193 } 02194 } 02195 } 02196 break; 02197 } 02198 #ifdef __PGF32SUPPORT__ 02199 case ImageModeGray31: 02200 { 02201 ASSERT(m_header.channels == 1); 02202 ASSERT(m_header.bpp == 32); 02203 02204 const int yuvOffset31 = 1 << (UsedBitsPerChannel() - 1); 02205 const int shift = UsedBitsPerChannel() - 8; 02206 02207 DataT* y = m_channel[0]; ASSERT(y); 02208 02209 if (bpp == 32) { 02210 UINT32 *buff32 = (UINT32 *)buff; 02211 int pitch32 = pitch/4; 02212 02213 for (i=0; i < h; i++) { 02214 for (j=0; j < w; j++) { 02215 buff32[j] = Clamp31(y[yPos++] + yuvOffset31); 02216 } 02217 buff32 += pitch32; 02218 02219 if (cb) { 02220 percent += dP; 02221 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed); 02222 } 02223 } 02224 } else { 02225 ASSERT(bpp == 8); 02226 02227 for (i=0; i < h; i++) { 02228 for (j=0; j < w; j++) { 02229 buff[j] = UINT8(Clamp31(y[yPos++] + yuvOffset31) >> shift); 02230 } 02231 buff += pitch; 02232 02233 if (cb) { 02234 percent += dP; 02235 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed); 02236 } 02237 } 02238 } 02239 break; 02240 } 02241 #endif 02242 case ImageModeRGB12: 02243 { 02244 ASSERT(m_header.channels == 3); 02245 ASSERT(m_header.bpp == m_header.channels*4); 02246 ASSERT(bpp == m_header.channels*4); 02247 ASSERT(!m_downsample); 02248 02249 DataT* y = m_channel[0]; ASSERT(y); 02250 DataT* u = m_channel[1]; ASSERT(u); 02251 DataT* v = m_channel[2]; ASSERT(v); 02252 UINT16 yval; 02253 int cnt; 02254 02255 for (i=0; i < h; i++) { 02256 cnt = 0; 02257 for (j=0; j < w; j++) { 02258 // Yuv 02259 uAvg = u[yPos]; 02260 vAvg = v[yPos]; 02261 yval = Clamp4(y[yPos++] + YUVoffset4 - ((uAvg + vAvg ) >> 2)); // must be logical shift operator 02262 if (j%2 == 0) { 02263 buff[cnt] = UINT8(Clamp4(vAvg + yval) | (yval << 4)); 02264 cnt++; 02265 buff[cnt] = Clamp4(uAvg + yval); 02266 } else { 02267 buff[cnt] |= Clamp4(vAvg + yval) << 4; 02268 cnt++; 02269 buff[cnt] = UINT8(yval | (Clamp4(uAvg + yval) << 4)); 02270 cnt++; 02271 } 02272 } 02273 buff += pitch; 02274 02275 if (cb) { 02276 percent += dP; 02277 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed); 02278 } 02279 } 02280 break; 02281 } 02282 case ImageModeRGB16: 02283 { 02284 ASSERT(m_header.channels == 3); 02285 ASSERT(m_header.bpp == 16); 02286 ASSERT(bpp == 16); 02287 ASSERT(!m_downsample); 02288 02289 DataT* y = m_channel[0]; ASSERT(y); 02290 DataT* u = m_channel[1]; ASSERT(u); 02291 DataT* v = m_channel[2]; ASSERT(v); 02292 UINT16 yval; 02293 UINT16 *buff16 = (UINT16 *)buff; 02294 int pitch16 = pitch/2; 02295 02296 for (i=0; i < h; i++) { 02297 for (j=0; j < w; j++) { 02298 // Yuv 02299 uAvg = u[yPos]; 02300 vAvg = v[yPos]; 02301 yval = Clamp6(y[yPos++] + YUVoffset6 - ((uAvg + vAvg ) >> 2)); // must be logical shift operator 02302 buff16[j] = (yval << 5) | ((Clamp6(uAvg + yval) >> 1) << 11) | (Clamp6(vAvg + yval) >> 1); 02303 } 02304 buff16 += pitch16; 02305 02306 if (cb) { 02307 percent += dP; 02308 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed); 02309 } 02310 } 02311 break; 02312 } 02313 default: 02314 ASSERT(false); 02315 } 02316 02317 #ifdef __PGFROISUPPORT__ 02318 if (targetBuff) { 02319 // copy valid ROI (m_roi) from temporary buffer (roi) to target buffer 02320 if (bpp%8 == 0) { 02321 BYTE bypp = bpp/8; 02322 buff = buffStart + (levelRoi.top - roi.top)*pitch + (levelRoi.left - roi.left)*bypp; 02323 w = levelRoi.Width()*bypp; 02324 h = levelRoi.Height(); 02325 02326 for (i=0; i < h; i++) { 02327 for (j=0; j < w; j++) { 02328 targetBuff[j] = buff[j]; 02329 } 02330 targetBuff += targetPitch; 02331 buff += pitch; 02332 } 02333 } else { 02334 // to do 02335 } 02336 02337 delete[] buffStart; 02338 } 02339 #endif 02340 } 02341 02356 void CPGFImage::GetYUV(int pitch, DataT* buff, BYTE bpp, int channelMap[] /*= NULL*/, CallbackPtr cb /*= NULL*/, void *data /*=NULL*/) const THROW_ { 02357 ASSERT(buff); 02358 const UINT32 w = m_width[0]; 02359 const UINT32 h = m_height[0]; 02360 const bool wOdd = (1 == w%2); 02361 const int dataBits = DataTSize*8; ASSERT(dataBits == 16 || dataBits == 32); 02362 const int pitch2 = pitch/DataTSize; 02363 const int yuvOffset = (dataBits == 16) ? YUVoffset8 : YUVoffset16; 02364 const double dP = 1.0/h; 02365 02366 int defMap[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; ASSERT(sizeof(defMap)/sizeof(defMap[0]) == MaxChannels); 02367 if (channelMap == NULL) channelMap = defMap; 02368 int sampledPos = 0, yPos = 0; 02369 DataT uAvg, vAvg; 02370 double percent = 0; 02371 UINT32 i, j; 02372 02373 if (m_header.channels == 3) { 02374 ASSERT(bpp%dataBits == 0); 02375 02376 DataT* y = m_channel[0]; ASSERT(y); 02377 DataT* u = m_channel[1]; ASSERT(u); 02378 DataT* v = m_channel[2]; ASSERT(v); 02379 int cnt, channels = bpp/dataBits; ASSERT(channels >= m_header.channels); 02380 02381 for (i=0; i < h; i++) { 02382 if (i%2) sampledPos -= (w + 1)/2; 02383 cnt = 0; 02384 for (j=0; j < w; j++) { 02385 if (m_downsample) { 02386 // image was downsampled 02387 uAvg = u[sampledPos]; 02388 vAvg = v[sampledPos]; 02389 } else { 02390 uAvg = u[yPos]; 02391 vAvg = v[yPos]; 02392 } 02393 buff[cnt + channelMap[0]] = y[yPos]; 02394 buff[cnt + channelMap[1]] = uAvg; 02395 buff[cnt + channelMap[2]] = vAvg; 02396 yPos++; 02397 cnt += channels; 02398 if (j%2) sampledPos++; 02399 } 02400 buff += pitch2; 02401 if (wOdd) sampledPos++; 02402 02403 if (cb) { 02404 percent += dP; 02405 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed); 02406 } 02407 } 02408 } else if (m_header.channels == 4) { 02409 ASSERT(m_header.bpp == m_header.channels*8); 02410 ASSERT(bpp%dataBits == 0); 02411 02412 DataT* y = m_channel[0]; ASSERT(y); 02413 DataT* u = m_channel[1]; ASSERT(u); 02414 DataT* v = m_channel[2]; ASSERT(v); 02415 DataT* a = m_channel[3]; ASSERT(a); 02416 UINT8 aAvg; 02417 int cnt, channels = bpp/dataBits; ASSERT(channels >= m_header.channels); 02418 02419 for (i=0; i < h; i++) { 02420 if (i%2) sampledPos -= (w + 1)/2; 02421 cnt = 0; 02422 for (j=0; j < w; j++) { 02423 if (m_downsample) { 02424 // image was downsampled 02425 uAvg = u[sampledPos]; 02426 vAvg = v[sampledPos]; 02427 aAvg = Clamp(a[sampledPos] + yuvOffset); 02428 } else { 02429 uAvg = u[yPos]; 02430 vAvg = v[yPos]; 02431 aAvg = Clamp(a[yPos] + yuvOffset); 02432 } 02433 // Yuv 02434 buff[cnt + channelMap[0]] = y[yPos]; 02435 buff[cnt + channelMap[1]] = uAvg; 02436 buff[cnt + channelMap[2]] = vAvg; 02437 buff[cnt + channelMap[3]] = aAvg; 02438 yPos++; 02439 cnt += channels; 02440 if (j%2) sampledPos++; 02441 } 02442 buff += pitch2; 02443 if (wOdd) sampledPos++; 02444 02445 if (cb) { 02446 percent += dP; 02447 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed); 02448 } 02449 } 02450 } 02451 } 02452 02467 void CPGFImage::ImportYUV(int pitch, DataT *buff, BYTE bpp, int channelMap[] /*= NULL*/, CallbackPtr cb /*= NULL*/, void *data /*=NULL*/) THROW_ { 02468 ASSERT(buff); 02469 const double dP = 1.0/m_header.height; 02470 const int dataBits = DataTSize*8; ASSERT(dataBits == 16 || dataBits == 32); 02471 const int pitch2 = pitch/DataTSize; 02472 const int yuvOffset = (dataBits == 16) ? YUVoffset8 : YUVoffset16; 02473 02474 int yPos = 0, cnt = 0; 02475 double percent = 0; 02476 int defMap[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; ASSERT(sizeof(defMap)/sizeof(defMap[0]) == MaxChannels); 02477 02478 if (channelMap == NULL) channelMap = defMap; 02479 02480 if (m_header.channels == 3) { 02481 ASSERT(bpp%dataBits == 0); 02482 02483 DataT* y = m_channel[0]; ASSERT(y); 02484 DataT* u = m_channel[1]; ASSERT(u); 02485 DataT* v = m_channel[2]; ASSERT(v); 02486 const int channels = bpp/dataBits; ASSERT(channels >= m_header.channels); 02487 02488 for (UINT32 h=0; h < m_header.height; h++) { 02489 if (cb) { 02490 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed); 02491 percent += dP; 02492 } 02493 02494 cnt = 0; 02495 for (UINT32 w=0; w < m_header.width; w++) { 02496 y[yPos] = buff[cnt + channelMap[0]]; 02497 u[yPos] = buff[cnt + channelMap[1]]; 02498 v[yPos] = buff[cnt + channelMap[2]]; 02499 yPos++; 02500 cnt += channels; 02501 } 02502 buff += pitch2; 02503 } 02504 } else if (m_header.channels == 4) { 02505 ASSERT(bpp%dataBits == 0); 02506 02507 DataT* y = m_channel[0]; ASSERT(y); 02508 DataT* u = m_channel[1]; ASSERT(u); 02509 DataT* v = m_channel[2]; ASSERT(v); 02510 DataT* a = m_channel[3]; ASSERT(a); 02511 const int channels = bpp/dataBits; ASSERT(channels >= m_header.channels); 02512 02513 for (UINT32 h=0; h < m_header.height; h++) { 02514 if (cb) { 02515 if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed); 02516 percent += dP; 02517 } 02518 02519 cnt = 0; 02520 for (UINT32 w=0; w < m_header.width; w++) { 02521 y[yPos] = buff[cnt + channelMap[0]]; 02522 u[yPos] = buff[cnt + channelMap[1]]; 02523 v[yPos] = buff[cnt + channelMap[2]]; 02524 a[yPos] = buff[cnt + channelMap[3]] - yuvOffset; 02525 yPos++; 02526 cnt += channels; 02527 } 02528 buff += pitch2; 02529 } 02530 } 02531 02532 if (m_downsample) { 02533 // Subsampling of the chrominance and alpha channels 02534 for (int i=1; i < m_header.channels; i++) { 02535 Downsample(i); 02536 } 02537 } 02538 } 02539