libpgf  6.13.45
PGF - Progressive Graphics File
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
PGFimage.cpp
Go to the documentation of this file.
1 /*
2  * The Progressive Graphics File; http://www.libpgf.org
3  *
4  * $Date: 2007-02-03 13:04:21 +0100 (Sa, 03 Feb 2007) $
5  * $Revision: 280 $
6  *
7  * This file Copyright (C) 2006 xeraina GmbH, Switzerland
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
11  * as published by the Free Software Foundation; either version 2.1
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22  */
23 
28 
29 #include "PGFimage.h"
30 #include "Decoder.h"
31 #include "Encoder.h"
32 #include <cmath>
33 #include <cstring>
34 
35 #define YUVoffset4 8 // 2^3
36 #define YUVoffset6 32 // 2^5
37 #define YUVoffset8 128 // 2^7
38 #define YUVoffset16 32768 // 2^15
39 //#define YUVoffset31 1073741824 // 2^30
40 
42 // global methods and variables
43 #ifdef NEXCEPTIONS
44  OSError _PGF_Error_;
45 
46  OSError GetLastPGFError() {
47  OSError tmp = _PGF_Error_;
48  _PGF_Error_ = NoError;
49  return tmp;
50  }
51 #endif
52 
54 // Standard constructor: It is used to create a PGF instance for opening and reading.
56 : m_decoder(0)
57 , m_encoder(0)
58 , m_levelLength(0)
59 , m_currentLevel(0)
60 , m_quant(0)
61 , m_userDataPos(0)
62 , m_downsample(false)
63 , m_favorSpeedOverSize(false)
64 , m_useOMPinEncoder(true)
65 , m_useOMPinDecoder(true)
66 , m_skipUserData(false)
67 #ifdef __PGFROISUPPORT__
68 , m_streamReinitialized(false)
69 #endif
70 , m_cb(0)
71 , m_cbArg(0)
72 , m_progressMode(PM_Relative)
73 , m_percent(0)
74 {
75 
76  // init preHeader
77  memcpy(m_preHeader.magic, PGFMagic, 3);
79  m_preHeader.hSize = 0;
80 
81  // init postHeader
84 
85  // init channels
86  for (int i=0; i < MaxChannels; i++) {
87  m_channel[i] = 0;
88  m_wtChannel[i] = 0;
89  }
90 
91  // set image width and height
92  m_width[0] = 0;
93  m_height[0] = 0;
94 }
95 
97 // Destructor: Destroy internal data structures.
99  Destroy();
100 }
101 
103 // Destroy internal data structures.
104 // Destructor calls this method during destruction.
106  Close();
107 
108  for (int i=0; i < m_header.channels; i++) {
109  delete m_wtChannel[i]; m_wtChannel[i]=0; // also deletes m_channel
110  m_channel[i] = 0;
111  }
113  delete[] m_levelLength; m_levelLength = 0;
114  delete m_encoder; m_encoder = NULL;
115 
116  m_userDataPos = 0;
117 }
118 
120 // Close PGF image after opening and reading.
121 // Destructor calls this method during destruction.
123  delete m_decoder; m_decoder = 0;
124 }
125 
127 // Open a PGF image at current stream position: read pre-header, header, levelLength, and ckeck image type.
128 // Precondition: The stream has been opened for reading.
129 // It might throw an IOException.
130 // @param stream A PGF stream
131 void CPGFImage::Open(CPGFStream *stream) THROW_ {
132  ASSERT(stream);
133 
134  // create decoder and read PGFPreHeader PGFHeader PGFPostHeader LevelLengths
135  m_decoder = new CDecoder(stream, m_preHeader, m_header, m_postHeader, m_levelLength,
136  m_userDataPos, m_useOMPinDecoder, m_skipUserData);
137 
138  if (m_header.nLevels > MaxLevel) ReturnWithError(FormatCannotRead);
139 
140  // set current level
141  m_currentLevel = m_header.nLevels;
142 
143  // set image width and height
144  m_width[0] = m_header.width;
145  m_height[0] = m_header.height;
146 
147  // complete header
148  CompleteHeader();
149 
150  // interpret quant parameter
151  if (m_header.quality > DownsampleThreshold &&
152  (m_header.mode == ImageModeRGBColor ||
153  m_header.mode == ImageModeRGBA ||
154  m_header.mode == ImageModeRGB48 ||
155  m_header.mode == ImageModeCMYKColor ||
156  m_header.mode == ImageModeCMYK64 ||
157  m_header.mode == ImageModeLabColor ||
158  m_header.mode == ImageModeLab48)) {
159  m_downsample = true;
160  m_quant = m_header.quality - 1;
161  } else {
162  m_downsample = false;
163  m_quant = m_header.quality;
164  }
165 
166  // set channel dimensions (chrominance is subsampled by factor 2)
167  if (m_downsample) {
168  for (int i=1; i < m_header.channels; i++) {
169  m_width[i] = (m_width[0] + 1)/2;
170  m_height[i] = (m_height[0] + 1)/2;
171  }
172  } else {
173  for (int i=1; i < m_header.channels; i++) {
174  m_width[i] = m_width[0];
175  m_height[i] = m_height[0];
176  }
177  }
178 
179  if (m_header.nLevels > 0) {
180  // init wavelet subbands
181  for (int i=0; i < m_header.channels; i++) {
182  m_wtChannel[i] = new CWaveletTransform(m_width[i], m_height[i], m_header.nLevels);
183  }
184 
185  // used in Read when PM_Absolute
186  m_percent = pow(0.25, m_header.nLevels);
187 
188  } else {
189  // very small image: we don't use DWT and encoding
190 
191  // read channels
192  for (int c=0; c < m_header.channels; c++) {
193  const UINT32 size = m_width[c]*m_height[c];
194  m_channel[c] = new(std::nothrow) DataT[size];
195  if (!m_channel[c]) ReturnWithError(InsufficientMemory);
196 
197  // read channel data from stream
198  for (UINT32 i=0; i < size; i++) {
199  int count = DataTSize;
200  stream->Read(&count, &m_channel[c][i]);
201  if (count != DataTSize) ReturnWithError(MissingData);
202  }
203  }
204  }
205 }
206 
209  if (m_header.mode == ImageModeUnknown) {
210  // undefined mode
211  switch(m_header.bpp) {
212  case 1: m_header.mode = ImageModeBitmap; break;
213  case 8: m_header.mode = ImageModeGrayScale; break;
214  case 12: m_header.mode = ImageModeRGB12; break;
215  case 16: m_header.mode = ImageModeRGB16; break;
216  case 24: m_header.mode = ImageModeRGBColor; break;
217  case 32: m_header.mode = ImageModeRGBA; break;
218  case 48: m_header.mode = ImageModeRGB48; break;
219  default: m_header.mode = ImageModeRGBColor; break;
220  }
221  }
222  if (!m_header.bpp) {
223  // undefined bpp
224  switch(m_header.mode) {
225  case ImageModeBitmap:
226  m_header.bpp = 1;
227  break;
229  case ImageModeGrayScale:
230  m_header.bpp = 8;
231  break;
232  case ImageModeRGB12:
233  m_header.bpp = 12;
234  break;
235  case ImageModeRGB16:
236  case ImageModeGray16:
237  m_header.bpp = 16;
238  break;
239  case ImageModeRGBColor:
240  case ImageModeLabColor:
241  m_header.bpp = 24;
242  break;
243  case ImageModeRGBA:
244  case ImageModeCMYKColor:
245  case ImageModeGray32:
246  m_header.bpp = 32;
247  break;
248  case ImageModeRGB48:
249  case ImageModeLab48:
250  m_header.bpp = 48;
251  break;
252  case ImageModeCMYK64:
253  m_header.bpp = 64;
254  break;
255  default:
256  ASSERT(false);
257  m_header.bpp = 24;
258  }
259  }
260  if (m_header.mode == ImageModeRGBColor && m_header.bpp == 32) {
261  // change mode
263  }
264  ASSERT(m_header.mode != ImageModeBitmap || m_header.bpp == 1);
265  ASSERT(m_header.mode != ImageModeIndexedColor || m_header.bpp == 8);
266  ASSERT(m_header.mode != ImageModeGrayScale || m_header.bpp == 8);
267  ASSERT(m_header.mode != ImageModeGray16 || m_header.bpp == 16);
268  ASSERT(m_header.mode != ImageModeGray32 || m_header.bpp == 32);
269  ASSERT(m_header.mode != ImageModeRGBColor || m_header.bpp == 24);
270  ASSERT(m_header.mode != ImageModeRGBA || m_header.bpp == 32);
271  ASSERT(m_header.mode != ImageModeRGB12 || m_header.bpp == 12);
272  ASSERT(m_header.mode != ImageModeRGB16 || m_header.bpp == 16);
273  ASSERT(m_header.mode != ImageModeRGB48 || m_header.bpp == 48);
274  ASSERT(m_header.mode != ImageModeLabColor || m_header.bpp == 24);
275  ASSERT(m_header.mode != ImageModeLab48 || m_header.bpp == 48);
276  ASSERT(m_header.mode != ImageModeCMYKColor || m_header.bpp == 32);
277  ASSERT(m_header.mode != ImageModeCMYK64 || m_header.bpp == 64);
278 
279  // set number of channels
280  if (!m_header.channels) {
281  switch(m_header.mode) {
282  case ImageModeBitmap:
284  case ImageModeGrayScale:
285  case ImageModeGray16:
286  case ImageModeGray32:
287  m_header.channels = 1;
288  break;
289  case ImageModeRGBColor:
290  case ImageModeRGB12:
291  case ImageModeRGB16:
292  case ImageModeRGB48:
293  case ImageModeLabColor:
294  case ImageModeLab48:
295  m_header.channels = 3;
296  break;
297  case ImageModeRGBA:
298  case ImageModeCMYKColor:
299  case ImageModeCMYK64:
300  m_header.channels = 4;
301  break;
302  default:
303  ASSERT(false);
304  m_header.channels = 3;
305  }
306  }
307 
308  // store used bits per channel
309  UINT8 bpc = m_header.bpp/m_header.channels;
310  if (bpc > 31) bpc = 31;
313  }
314 }
315 
321 const UINT8* CPGFImage::GetUserData(UINT32& size) const {
322  size = m_postHeader.userDataLen;
323  return m_postHeader.userData;
324 }
325 
331 void CPGFImage::Reconstruct(int level /*= 0*/) THROW_ {
332  if (m_header.nLevels == 0) {
333  // image didn't use wavelet transform
334  if (level == 0) {
335  for (int i=0; i < m_header.channels; i++) {
336  ASSERT(m_wtChannel[i]);
337  m_channel[i] = m_wtChannel[i]->GetSubband(0, LL)->GetBuffer();
338  }
339  }
340  } else {
341  int currentLevel = m_header.nLevels;
342 
343  if (ROIisSupported()) {
344  // enable ROI reading
345  SetROI(PGFRect(0, 0, m_header.width, m_header.height));
346  }
347 
348  while (currentLevel > level) {
349  for (int i=0; i < m_header.channels; i++) {
350  ASSERT(m_wtChannel[i]);
351  // dequantize subbands
352  if (currentLevel == m_header.nLevels) {
353  // last level also has LL band
354  m_wtChannel[i]->GetSubband(currentLevel, LL)->Dequantize(m_quant);
355  }
356  m_wtChannel[i]->GetSubband(currentLevel, HL)->Dequantize(m_quant);
357  m_wtChannel[i]->GetSubband(currentLevel, LH)->Dequantize(m_quant);
358  m_wtChannel[i]->GetSubband(currentLevel, HH)->Dequantize(m_quant);
359 
360  // inverse transform from m_wtChannel to m_channel
361  OSError err = m_wtChannel[i]->InverseTransform(currentLevel, &m_width[i], &m_height[i], &m_channel[i]);
362  if (err != NoError) ReturnWithError(err);
363  ASSERT(m_channel[i]);
364  }
365 
366  currentLevel--;
367  }
368  }
369 }
370 
372 // Read and decode some levels of a PGF image at current stream position.
373 // A PGF image is structered in levels, numbered between 0 and Levels() - 1.
374 // Each level can be seen as a single image, containing the same content
375 // as all other levels, but in a different size (width, height).
376 // The image size at level i is double the size (width, height) of the image at level i+1.
377 // The image at level 0 contains the original size.
378 // Precondition: The PGF image has been opened with a call of Open(...).
379 // It might throw an IOException.
380 // @param level The image level of the resulting image in the internal image buffer.
381 // @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.
382 // @param data Data Pointer to C++ class container to host callback procedure.
383 void CPGFImage::Read(int level /*= 0*/, CallbackPtr cb /*= NULL*/, void *data /*=NULL*/) THROW_ {
384  ASSERT((level >= 0 && level < m_header.nLevels) || m_header.nLevels == 0); // m_header.nLevels == 0: image didn't use wavelet transform
385  ASSERT(m_decoder);
386 
387 #ifdef __PGFROISUPPORT__
388  if (ROIisSupported() && m_header.nLevels > 0) {
389  // new encoding scheme supporting ROI
390  PGFRect rect(0, 0, m_header.width, m_header.height);
391  Read(rect, level, cb, data);
392  return;
393  }
394 #endif
395 
396  if (m_header.nLevels == 0) {
397  if (level == 0) {
398  // the data has already been read during open
399  // now update progress
400  if (cb) {
401  if ((*cb)(1.0, true, data)) ReturnWithError(EscapePressed);
402  }
403  }
404  } else {
405  const int levelDiff = m_currentLevel - level;
406  double percent = (m_progressMode == PM_Relative) ? pow(0.25, levelDiff) : m_percent;
407 
408  // encoding scheme without ROI
409  while (m_currentLevel > level) {
410  for (int i=0; i < m_header.channels; i++) {
411  ASSERT(m_wtChannel[i]);
412  // decode file and write stream to m_wtChannel
413  if (m_currentLevel == m_header.nLevels) {
414  // last level also has LL band
415  m_wtChannel[i]->GetSubband(m_currentLevel, LL)->PlaceTile(*m_decoder, m_quant);
416  }
417  if (m_preHeader.version & Version5) {
418  // since version 5
419  m_wtChannel[i]->GetSubband(m_currentLevel, HL)->PlaceTile(*m_decoder, m_quant);
420  m_wtChannel[i]->GetSubband(m_currentLevel, LH)->PlaceTile(*m_decoder, m_quant);
421  } else {
422  // until version 4
423  m_decoder->DecodeInterleaved(m_wtChannel[i], m_currentLevel, m_quant);
424  }
425  m_wtChannel[i]->GetSubband(m_currentLevel, HH)->PlaceTile(*m_decoder, m_quant);
426  }
427 
428  volatile OSError error = NoError; // volatile prevents optimizations
429 #ifdef LIBPGF_USE_OPENMP
430  #pragma omp parallel for default(shared)
431 #endif
432  for (int i=0; i < m_header.channels; i++) {
433  // inverse transform from m_wtChannel to m_channel
434  if (error == NoError) {
435  OSError err = m_wtChannel[i]->InverseTransform(m_currentLevel, &m_width[i], &m_height[i], &m_channel[i]);
436  if (err != NoError) error = err;
437  }
438  ASSERT(m_channel[i]);
439  }
440  if (error != NoError) ReturnWithError(error);
441 
442  // set new level: must be done before refresh callback
443  m_currentLevel--;
444 
445  // now we have to refresh the display
446  if (m_cb) m_cb(m_cbArg);
447 
448  // now update progress
449  if (cb) {
450  percent *= 4;
451  if (m_progressMode == PM_Absolute) m_percent = percent;
452  if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
453  }
454  }
455  }
456 
457  // automatically closing
458  if (m_currentLevel == 0) Close();
459 }
460 
461 #ifdef __PGFROISUPPORT__
462 
463 
464 
465 
466 
467 
468 
469 
470 
471 void CPGFImage::Read(PGFRect& rect, int level /*= 0*/, CallbackPtr cb /*= NULL*/, void *data /*=NULL*/) THROW_ {
472  ASSERT((level >= 0 && level < m_header.nLevels) || m_header.nLevels == 0); // m_header.nLevels == 0: image didn't use wavelet transform
473  ASSERT(m_decoder);
474 
475  if (m_header.nLevels == 0 || !ROIisSupported()) {
476  rect.left = rect.top = 0;
477  rect.right = m_header.width; rect.bottom = m_header.height;
478  Read(level, cb, data);
479  } else {
480  ASSERT(ROIisSupported());
481  // new encoding scheme supporting ROI
482  ASSERT(rect.left < m_header.width && rect.top < m_header.height);
483 
484  const int levelDiff = m_currentLevel - level;
485  double percent = (m_progressMode == PM_Relative) ? pow(0.25, levelDiff) : m_percent;
486 
487  // check level difference
488  if (levelDiff <= 0) {
489  // it is a new read call, probably with a new ROI
490  m_currentLevel = m_header.nLevels;
491  m_decoder->SetStreamPosToData();
492  }
493 
494  // check rectangle
495  if (rect.right == 0 || rect.right > m_header.width) rect.right = m_header.width;
496  if (rect.bottom == 0 || rect.bottom > m_header.height) rect.bottom = m_header.height;
497 
498  // enable ROI decoding and reading
499  SetROI(rect);
500 
501  while (m_currentLevel > level) {
502  for (int i=0; i < m_header.channels; i++) {
503  ASSERT(m_wtChannel[i]);
504 
505  // get number of tiles and tile indices
506  const UINT32 nTiles = m_wtChannel[i]->GetNofTiles(m_currentLevel);
507  const PGFRect& tileIndices = m_wtChannel[i]->GetTileIndices(m_currentLevel);
508 
509  // decode file and write stream to m_wtChannel
510  if (m_currentLevel == m_header.nLevels) { // last level also has LL band
511  ASSERT(nTiles == 1);
512  m_decoder->DecodeTileBuffer();
513  m_wtChannel[i]->GetSubband(m_currentLevel, LL)->PlaceTile(*m_decoder, m_quant);
514  }
515  for (UINT32 tileY=0; tileY < nTiles; tileY++) {
516  for (UINT32 tileX=0; tileX < nTiles; tileX++) {
517  // check relevance of tile
518  if (tileIndices.IsInside(tileX, tileY)) {
519  m_decoder->DecodeTileBuffer();
520  m_wtChannel[i]->GetSubband(m_currentLevel, HL)->PlaceTile(*m_decoder, m_quant, true, tileX, tileY);
521  m_wtChannel[i]->GetSubband(m_currentLevel, LH)->PlaceTile(*m_decoder, m_quant, true, tileX, tileY);
522  m_wtChannel[i]->GetSubband(m_currentLevel, HH)->PlaceTile(*m_decoder, m_quant, true, tileX, tileY);
523  } else {
524  // skip tile
525  m_decoder->SkipTileBuffer();
526  }
527  }
528  }
529  }
530 
531  volatile OSError error = NoError; // volatile prevents optimizations
532 #ifdef LIBPGF_USE_OPENMP
533  #pragma omp parallel for default(shared)
534 #endif
535  for (int i=0; i < m_header.channels; i++) {
536  // inverse transform from m_wtChannel to m_channel
537  if (error == NoError) {
538  OSError err = m_wtChannel[i]->InverseTransform(m_currentLevel, &m_width[i], &m_height[i], &m_channel[i]);
539  if (err != NoError) error = err;
540  }
541  ASSERT(m_channel[i]);
542  }
543  if (error != NoError) ReturnWithError(error);
544 
545  // set new level: must be done before refresh callback
546  m_currentLevel--;
547 
548  // now we have to refresh the display
549  if (m_cb) m_cb(m_cbArg);
550 
551  // now update progress
552  if (cb) {
553  percent *= 4;
554  if (m_progressMode == PM_Absolute) m_percent = percent;
555  if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
556  }
557  }
558  }
559 
560  // automatically closing
561  if (m_currentLevel == 0) Close();
562 }
563 
567 void CPGFImage::SetROI(PGFRect rect) {
568  ASSERT(m_decoder);
569  ASSERT(ROIisSupported());
570 
571  // store ROI for a later call of GetBitmap
572  m_roi = rect;
573 
574  // enable ROI decoding
575  m_decoder->SetROI();
576 
577  // enlarge ROI because of border artefacts
578  const UINT32 dx = FilterWidth/2*(1 << m_currentLevel);
579  const UINT32 dy = FilterHeight/2*(1 << m_currentLevel);
580 
581  if (rect.left < dx) rect.left = 0;
582  else rect.left -= dx;
583  if (rect.top < dy) rect.top = 0;
584  else rect.top -= dy;
585  rect.right += dx;
586  if (rect.right > m_header.width) rect.right = m_header.width;
587  rect.bottom += dy;
588  if (rect.bottom > m_header.height) rect.bottom = m_header.height;
589 
590  // prepare wavelet channels for using ROI
591  ASSERT(m_wtChannel[0]);
592  m_wtChannel[0]->SetROI(rect);
593  if (m_downsample && m_header.channels > 1) {
594  // all further channels are downsampled, therefore downsample ROI
595  rect.left >>= 1;
596  rect.top >>= 1;
597  rect.right >>= 1;
598  rect.bottom >>= 1;
599  }
600  for (int i=1; i < m_header.channels; i++) {
601  ASSERT(m_wtChannel[i]);
602  m_wtChannel[i]->SetROI(rect);
603  }
604 }
605 
606 #endif // __PGFROISUPPORT__
607 
613  ASSERT(m_decoder);
615 }
616 
624 UINT32 CPGFImage::ReadEncodedHeader(UINT8* target, UINT32 targetLen) const THROW_ {
625  ASSERT(target);
626  ASSERT(targetLen > 0);
627  ASSERT(m_decoder);
628 
629  // reset stream position
630  m_decoder->SetStreamPosToStart();
631 
632  // compute number of bytes to read
633  UINT32 len = __min(targetLen, GetEncodedHeaderLength());
634 
635  // read data
636  len = m_decoder->ReadEncodedData(target, len);
637  ASSERT(len >= 0 && len <= targetLen);
638 
639  return len;
640 }
641 
645  ASSERT(m_decoder);
646  return m_decoder->SetStreamPosToStart();
647 }
648 
658 UINT32 CPGFImage::ReadEncodedData(int level, UINT8* target, UINT32 targetLen) const THROW_ {
659  ASSERT(level >= 0 && level < m_header.nLevels);
660  ASSERT(target);
661  ASSERT(targetLen > 0);
662  ASSERT(m_decoder);
663 
664  // reset stream position
665  m_decoder->SetStreamPosToData();
666 
667  // position stream
668  UINT64 offset = 0;
669 
670  for (int i=m_header.nLevels - 1; i > level; i--) {
671  offset += m_levelLength[m_header.nLevels - 1 - i];
672  }
673  m_decoder->Skip(offset);
674 
675  // compute number of bytes to read
676  UINT32 len = __min(targetLen, GetEncodedLevelLength(level));
677 
678  // read data
679  len = m_decoder->ReadEncodedData(target, len);
680  ASSERT(len >= 0 && len <= targetLen);
681 
682  return len;
683 }
684 
689 void CPGFImage::SetMaxValue(UINT32 maxValue) {
690  const BYTE bpc = m_header.bpp/m_header.channels;
691  BYTE pot = 0;
692 
693  while(maxValue > 0) {
694  pot++;
695  maxValue >>= 1;
696  }
697  // store bits per channel
698  if (pot > bpc) pot = bpc;
699  if (pot > 31) pot = 31;
701 }
702 
708  const BYTE bpc = m_header.bpp/m_header.channels;
709 
710  if (bpc > 8) {
712  } else {
713  return bpc;
714  }
715 }
716 
719 BYTE CPGFImage::CurrentVersion(BYTE version) {
720  if (version & Version6) return 6;
721  if (version & Version5) return 5;
722  if (version & Version2) return 2;
723  return 1;
724 }
725 
727 // Import an image from a specified image buffer.
728 // This method is usually called before Write(...) and after SetHeader(...).
729 // It might throw an IOException.
730 // The absolute value of pitch is the number of bytes of an image row.
731 // If pitch is negative, then buff points to the last row of a bottom-up image (first byte on last row).
732 // If pitch is positive, then buff points to the first row of a top-down image (first byte).
733 // 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
734 // provide a channelMap of size of expected channels (depending on image mode). For example, PGF expects in RGB color mode a channel sequence BGR.
735 // If your provided image buffer contains a channel sequence ARGB, then the channelMap looks like { 3, 2, 1 }.
736 // @param pitch The number of bytes of a row of the image buffer.
737 // @param buff An image buffer.
738 // @param bpp The number of bits per pixel used in image buffer.
739 // @param channelMap A integer array containing the mapping of input channel ordering to expected channel ordering.
740 // @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.
741 // @param data Data Pointer to C++ class container to host callback procedure.
742 void CPGFImage::ImportBitmap(int pitch, UINT8 *buff, BYTE bpp, int channelMap[] /*= NULL */, CallbackPtr cb /*= NULL*/, void *data /*=NULL*/) THROW_ {
743  ASSERT(buff);
744  ASSERT(m_channel[0]);
745 
746  // color transform
747  RgbToYuv(pitch, buff, bpp, channelMap, cb, data);
748 
749  if (m_downsample) {
750  // Subsampling of the chrominance and alpha channels
751  for (int i=1; i < m_header.channels; i++) {
752  Downsample(i);
753  }
754  }
755 }
756 
758 // Bilinerar Subsampling of channel ch by a factor 2
759 void CPGFImage::Downsample(int ch) {
760  ASSERT(ch > 0);
761 
762  const int w = m_width[0];
763  const int w2 = w/2;
764  const int h2 = m_height[0]/2;
765  const int oddW = w%2; // don't use bool -> problems with MaxSpeed optimization
766  const int oddH = m_height[0]%2; // "
767  int loPos = 0;
768  int hiPos = w;
769  int sampledPos = 0;
770  DataT* buff = m_channel[ch]; ASSERT(buff);
771 
772  for (int i=0; i < h2; i++) {
773  for (int j=0; j < w2; j++) {
774  // compute average of pixel block
775  buff[sampledPos] = (buff[loPos] + buff[loPos + 1] + buff[hiPos] + buff[hiPos + 1]) >> 2;
776  loPos += 2; hiPos += 2;
777  sampledPos++;
778  }
779  if (oddW) {
780  buff[sampledPos] = (buff[loPos] + buff[hiPos]) >> 1;
781  loPos++; hiPos++;
782  sampledPos++;
783  }
784  loPos += w; hiPos += w;
785  }
786  if (oddH) {
787  for (int j=0; j < w2; j++) {
788  buff[sampledPos] = (buff[loPos] + buff[loPos+1]) >> 1;
789  loPos += 2; hiPos += 2;
790  sampledPos++;
791  }
792  if (oddW) {
793  buff[sampledPos] = buff[loPos];
794  }
795  }
796 
797  // downsampled image has half width and half height
798  m_width[ch] = (m_width[ch] + 1)/2;
799  m_height[ch] = (m_height[ch] + 1)/2;
800 }
801 
804  const int maxThumbnailWidth = 20*FilterWidth;
805  const int m = __min(m_header.width, m_header.height);
806  int s = m;
807 
808  if (m_header.nLevels < 1 || m_header.nLevels > MaxLevel) {
809  m_header.nLevels = 1;
810  // compute a good value depending on the size of the image
811  while (s > maxThumbnailWidth) {
812  m_header.nLevels++;
813  s = s/2;
814  }
815  }
816 
817  int levels = m_header.nLevels; // we need a signed value during level reduction
818 
819  // reduce number of levels if the image size is smaller than FilterWidth*2^levels
820  s = FilterWidth*(1 << levels); // must be at least the double filter size because of subsampling
821  while (m < s) {
822  levels--;
823  s = s/2;
824  }
825  if (levels > MaxLevel) m_header.nLevels = MaxLevel;
826  else if (levels < 0) m_header.nLevels = 0;
827  else m_header.nLevels = (UINT8)levels;
828 
829  // used in Write when PM_Absolute
830  m_percent = pow(0.25, m_header.nLevels);
831 
832  ASSERT(0 <= m_header.nLevels && m_header.nLevels <= MaxLevel);
833 }
834 
843 void CPGFImage::SetHeader(const PGFHeader& header, BYTE flags /*=0*/, UINT8* userData /*= 0*/, UINT32 userDataLength /*= 0*/) THROW_ {
844  ASSERT(!m_decoder); // current image must be closed
845  ASSERT(header.quality <= MaxQuality);
846 
847  // init state
848 #ifdef __PGFROISUPPORT__
849  m_streamReinitialized = false;
850 #endif
851 
852  // init preHeader
853  memcpy(m_preHeader.magic, PGFMagic, 3);
854  m_preHeader.version = PGFVersion | flags;
855  m_preHeader.hSize = HeaderSize;
856 
857  // copy header
858  memcpy(&m_header, &header, HeaderSize);
859 
860  // complete header
861  CompleteHeader();
862 
863  // check and set number of levels
864  ComputeLevels();
865 
866  // check for downsample
867  if (m_header.quality > DownsampleThreshold && (m_header.mode == ImageModeRGBColor ||
868  m_header.mode == ImageModeRGBA ||
869  m_header.mode == ImageModeRGB48 ||
870  m_header.mode == ImageModeCMYKColor ||
871  m_header.mode == ImageModeCMYK64 ||
872  m_header.mode == ImageModeLabColor ||
873  m_header.mode == ImageModeLab48)) {
874  m_downsample = true;
875  m_quant = m_header.quality - 1;
876  } else {
877  m_downsample = false;
878  m_quant = m_header.quality;
879  }
880 
881  // update header size and copy user data
882  if (m_header.mode == ImageModeIndexedColor) {
883  // update header size
884  m_preHeader.hSize += ColorTableSize;
885  }
886  if (userDataLength && userData) {
887  m_postHeader.userData = new(std::nothrow) UINT8[userDataLength];
888  if (!m_postHeader.userData) ReturnWithError(InsufficientMemory);
889  m_postHeader.userDataLen = userDataLength;
890  memcpy(m_postHeader.userData, userData, userDataLength);
891  // update header size
892  m_preHeader.hSize += userDataLength;
893  }
894 
895  // allocate channels
896  for (int i=0; i < m_header.channels; i++) {
897  // set current width and height
898  m_width[i] = m_header.width;
899  m_height[i] = m_header.height;
900 
901  // allocate channels
902  ASSERT(!m_channel[i]);
903  m_channel[i] = new(std::nothrow) DataT[m_header.width*m_header.height];
904  if (!m_channel[i]) {
905  if (i) i--;
906  while(i) {
907  delete[] m_channel[i]; m_channel[i] = 0;
908  i--;
909  }
910  ReturnWithError(InsufficientMemory);
911  }
912  }
913 }
914 
922 UINT32 CPGFImage::WriteHeader(CPGFStream* stream) THROW_ {
923  ASSERT(m_header.nLevels <= MaxLevel);
924  ASSERT(m_header.quality <= MaxQuality); // quality is already initialized
925 
926  if (m_header.nLevels > 0) {
927  volatile OSError error = NoError; // volatile prevents optimizations
928  // create new wt channels
929 #ifdef LIBPGF_USE_OPENMP
930  #pragma omp parallel for default(shared)
931 #endif
932  for (int i=0; i < m_header.channels; i++) {
933  DataT *temp = NULL;
934  if (error == NoError) {
935  if (m_wtChannel[i]) {
936  ASSERT(m_channel[i]);
937  // copy m_channel to temp
938  int size = m_height[i]*m_width[i];
939  temp = new(std::nothrow) DataT[size];
940  if (temp) {
941  memcpy(temp, m_channel[i], size*DataTSize);
942  delete m_wtChannel[i]; // also deletes m_channel
943  m_channel[i] = NULL;
944  } else {
945  error = InsufficientMemory;
946  }
947  }
948  if (error == NoError) {
949  if (temp) {
950  ASSERT(!m_channel[i]);
951  m_channel[i] = temp;
952  }
953  m_wtChannel[i] = new CWaveletTransform(m_width[i], m_height[i], m_header.nLevels, m_channel[i]);
954  if (m_wtChannel[i]) {
955  #ifdef __PGFROISUPPORT__
956  m_wtChannel[i]->SetROI(PGFRect(0, 0, m_width[i], m_height[i]));
957  #endif
958 
959  // wavelet subband decomposition
960  for (int l=0; error == NoError && l < m_header.nLevels; l++) {
961  OSError err = m_wtChannel[i]->ForwardTransform(l, m_quant);
962  if (err != NoError) error = err;
963  }
964  } else {
965  delete[] m_channel[i];
966  error = InsufficientMemory;
967  }
968  }
969  }
970  }
971  if (error != NoError) {
972  // free already allocated memory
973  for (int i=0; i < m_header.channels; i++) {
974  delete m_wtChannel[i];
975  }
976  ReturnWithError(error);
977  }
978 
979  m_currentLevel = m_header.nLevels;
980 
981  // create encoder and eventually write headers and levelLength
982  m_encoder = new CEncoder(stream, m_preHeader, m_header, m_postHeader, m_userDataPos, m_useOMPinEncoder);
983  if (m_favorSpeedOverSize) m_encoder->FavorSpeedOverSize();
984 
985  #ifdef __PGFROISUPPORT__
986  if (ROIisSupported()) {
987  // new encoding scheme supporting ROI
988  m_encoder->SetROI();
989  }
990  #endif
991 
992  } else {
993  // very small image: we don't use DWT and encoding
994 
995  // create encoder and eventually write headers and levelLength
996  m_encoder = new CEncoder(stream, m_preHeader, m_header, m_postHeader, m_userDataPos, m_useOMPinEncoder);
997  }
998 
999  INT64 nBytes = m_encoder->ComputeHeaderLength();
1000  return (nBytes > 0) ? (UINT32)nBytes : 0;
1001 }
1002 
1004 // Encode and write next level of a PGF image at current stream position.
1005 // A PGF image is structered in levels, numbered between 0 and Levels() - 1.
1006 // Each level can be seen as a single image, containing the same content
1007 // as all other levels, but in a different size (width, height).
1008 // The image size at level i is double the size (width, height) of the image at level i+1.
1009 // The image at level 0 contains the original size.
1010 // It might throw an IOException.
1011 void CPGFImage::WriteLevel() THROW_ {
1012  ASSERT(m_encoder);
1013  ASSERT(m_currentLevel > 0);
1014  ASSERT(m_header.nLevels > 0);
1015 
1016 #ifdef __PGFROISUPPORT__
1017  if (ROIisSupported()) {
1018  const int lastChannel = m_header.channels - 1;
1019 
1020  for (int i=0; i < m_header.channels; i++) {
1021  // get number of tiles and tile indices
1022  const UINT32 nTiles = m_wtChannel[i]->GetNofTiles(m_currentLevel);
1023  const UINT32 lastTile = nTiles - 1;
1024 
1025  if (m_currentLevel == m_header.nLevels) {
1026  // last level also has LL band
1027  ASSERT(nTiles == 1);
1029  m_encoder->EncodeTileBuffer();
1030  }
1031  for (UINT32 tileY=0; tileY < nTiles; tileY++) {
1032  for (UINT32 tileX=0; tileX < nTiles; tileX++) {
1033  m_wtChannel[i]->GetSubband(m_currentLevel, HL)->ExtractTile(*m_encoder, true, tileX, tileY);
1034  m_wtChannel[i]->GetSubband(m_currentLevel, LH)->ExtractTile(*m_encoder, true, tileX, tileY);
1035  m_wtChannel[i]->GetSubband(m_currentLevel, HH)->ExtractTile(*m_encoder, true, tileX, tileY);
1036  if (i == lastChannel && tileY == lastTile && tileX == lastTile) {
1037  // all necessary data are buffered. next call of EncodeBuffer will write the last piece of data of the current level.
1039  }
1040  m_encoder->EncodeTileBuffer();
1041  }
1042  }
1043  }
1044  } else
1045 #endif
1046  {
1047  for (int i=0; i < m_header.channels; i++) {
1048  ASSERT(m_wtChannel[i]);
1049  if (m_currentLevel == m_header.nLevels) {
1050  // last level also has LL band
1052  }
1053  //encoder.EncodeInterleaved(m_wtChannel[i], m_currentLevel, m_quant); // until version 4
1054  m_wtChannel[i]->GetSubband(m_currentLevel, HL)->ExtractTile(*m_encoder); // since version 5
1055  m_wtChannel[i]->GetSubband(m_currentLevel, LH)->ExtractTile(*m_encoder); // since version 5
1057  }
1058 
1059  // all necessary data are buffered. next call of EncodeBuffer will write the last piece of data of the current level.
1061  }
1062 }
1063 
1065 // Return written levelLength bytes
1067  ASSERT(m_encoder);
1068 
1069  INT64 offset = m_encoder->ComputeOffset(); ASSERT(offset >= 0);
1070 
1071  if (offset > 0) {
1072  // update post-header size and rewrite pre-header
1073  m_preHeader.hSize += (UINT32)offset;
1075  }
1076 
1077  // write dummy levelLength into stream
1079 }
1080 
1091 UINT32 CPGFImage::WriteImage(CPGFStream* stream, CallbackPtr cb /*= NULL*/, void *data /*= NULL*/) THROW_ {
1092  ASSERT(stream);
1093  ASSERT(m_preHeader.hSize);
1094 
1095  int levels = m_header.nLevels;
1096  double percent = pow(0.25, levels);
1097 
1098  // update post-header size, rewrite pre-header, and write dummy levelLength
1099  UINT32 nWrittenBytes = UpdatePostHeaderSize();
1100 
1101  if (levels == 0) {
1102  // write channels
1103  for (int c=0; c < m_header.channels; c++) {
1104  const UINT32 size = m_width[c]*m_height[c];
1105 
1106  // write channel data into stream
1107  for (UINT32 i=0; i < size; i++) {
1108  int count = DataTSize;
1109  stream->Write(&count, &m_channel[c][i]);
1110  }
1111  }
1112 
1113  // now update progress
1114  if (cb) {
1115  if ((*cb)(1, true, data)) ReturnWithError(EscapePressed);
1116  }
1117 
1118  } else {
1119  // encode quantized wavelet coefficients and write to PGF file
1120  // encode subbands, higher levels first
1121  // color channels are interleaved
1122 
1123  // encode all levels
1124  for (m_currentLevel = levels; m_currentLevel > 0; ) {
1125  WriteLevel(); // decrements m_currentLevel
1126 
1127  // now update progress
1128  if (cb) {
1129  percent *= 4;
1130  if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1131  }
1132  }
1133 
1134  // flush encoder and write level lengths
1135  m_encoder->Flush();
1136  }
1137 
1138  // update level lengths
1139  nWrittenBytes += m_encoder->UpdateLevelLength(); // return written image bytes
1140 
1141  // delete encoder
1142  delete m_encoder; m_encoder = NULL;
1143 
1144  ASSERT(!m_encoder);
1145 
1146  return nWrittenBytes;
1147 }
1148 
1162 void CPGFImage::Write(CPGFStream* stream, UINT32* nWrittenBytes /*= NULL*/, CallbackPtr cb /*= NULL*/, void *data /*=NULL*/) THROW_ {
1163  ASSERT(stream);
1164  ASSERT(m_preHeader.hSize);
1165 
1166  // create wavelet transform channels and encoder
1167  UINT32 nBytes = WriteHeader(stream);
1168 
1169  // write image
1170  nBytes += WriteImage(stream, cb, data);
1171 
1172  // return written bytes
1173  if (nWrittenBytes) *nWrittenBytes += nBytes;
1174 }
1175 
1176 #ifdef __PGFROISUPPORT__
1177 
1178 // Encode and write down to given level at current stream position.
1179 // A PGF image is structered in levels, numbered between 0 and Levels() - 1.
1180 // Each level can be seen as a single image, containing the same content
1181 // as all other levels, but in a different size (width, height).
1182 // The image size at level i is double the size (width, height) of the image at level i+1.
1183 // The image at level 0 contains the original size.
1184 // Precondition: the PGF image contains a valid header (see also SetHeader(...)) and WriteHeader() has been called before Write().
1185 // The ROI encoding scheme is used.
1186 // It might throw an IOException.
1187 // @param level The image level of the resulting image in the internal image buffer.
1188 // @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.
1189 // @param data Data Pointer to C++ class container to host callback procedure.
1190 // @return The number of bytes written into stream.
1191 UINT32 CPGFImage::Write(int level, CallbackPtr cb /*= NULL*/, void *data /*=NULL*/) THROW_ {
1192  ASSERT(m_header.nLevels > 0);
1193  ASSERT(0 <= level && level < m_header.nLevels);
1194  ASSERT(m_encoder);
1195  ASSERT(ROIisSupported());
1196 
1197  const int levelDiff = m_currentLevel - level;
1198  double percent = (m_progressMode == PM_Relative) ? pow(0.25, levelDiff) : m_percent;
1199  UINT32 nWrittenBytes = 0;
1200 
1201  if (m_currentLevel == m_header.nLevels) {
1202  // update post-header size, rewrite pre-header, and write dummy levelLength
1203  nWrittenBytes = UpdatePostHeaderSize();
1204  } else {
1205  // prepare for next level: save current file position, because the stream might have been reinitialized
1206  if (m_encoder->ComputeBufferLength()) {
1207  m_streamReinitialized = true;
1208  }
1209  }
1210 
1211  // encoding scheme with ROI
1212  while (m_currentLevel > level) {
1213  WriteLevel(); // decrements m_currentLevel
1214 
1215  if (m_levelLength) {
1216  nWrittenBytes += m_levelLength[m_header.nLevels - m_currentLevel - 1];
1217  }
1218 
1219  // now update progress
1220  if (cb) {
1221  percent *= 4;
1222  if (m_progressMode == PM_Absolute) m_percent = percent;
1223  if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1224  }
1225  }
1226 
1227  // automatically closing
1228  if (m_currentLevel == 0) {
1229  if (!m_streamReinitialized) {
1230  // don't write level lengths, if the stream position changed inbetween two Write operations
1231  m_encoder->UpdateLevelLength();
1232  }
1233  // delete encoder
1234  delete m_encoder; m_encoder = NULL;
1235  }
1236 
1237  return nWrittenBytes;
1238 }
1239 #endif // __PGFROISUPPORT__
1240 
1241 
1243 // Check for valid import image mode.
1244 // @param mode Image mode
1245 // @return True if an image of given mode can be imported with ImportBitmap(...)
1247  size_t size = DataTSize;
1248 
1249  if (size >= 2) {
1250  switch(mode) {
1251  case ImageModeBitmap:
1252  case ImageModeIndexedColor:
1253  case ImageModeGrayScale:
1254  case ImageModeRGBColor:
1255  case ImageModeCMYKColor:
1256  case ImageModeHSLColor:
1257  case ImageModeHSBColor:
1258  //case ImageModeDuotone:
1259  case ImageModeLabColor:
1260  case ImageModeRGB12:
1261  case ImageModeRGB16:
1262  case ImageModeRGBA:
1263  return true;
1264  }
1265  }
1266  if (size >= 3) {
1267  switch(mode) {
1268  case ImageModeGray16:
1269  case ImageModeRGB48:
1270  case ImageModeLab48:
1271  case ImageModeCMYK64:
1272  //case ImageModeDuotone16:
1273  return true;
1274  }
1275  }
1276  if (size >=4) {
1277  switch(mode) {
1278  case ImageModeGray32:
1279  return true;
1280  }
1281  }
1282  return false;
1283 }
1284 
1291 void CPGFImage::GetColorTable(UINT32 iFirstColor, UINT32 nColors, RGBQUAD* prgbColors) const THROW_ {
1292  if (iFirstColor + nColors > ColorTableLen) ReturnWithError(ColorTableError);
1293 
1294  for (UINT32 i=iFirstColor, j=0; j < nColors; i++, j++) {
1295  prgbColors[j] = m_postHeader.clut[i];
1296  }
1297 }
1298 
1305 void CPGFImage::SetColorTable(UINT32 iFirstColor, UINT32 nColors, const RGBQUAD* prgbColors) THROW_ {
1306  if (iFirstColor + nColors > ColorTableLen) ReturnWithError(ColorTableError);
1307 
1308  for (UINT32 i=iFirstColor, j=0; j < nColors; i++, j++) {
1309  m_postHeader.clut[i] = prgbColors[j];
1310  }
1311 }
1312 
1314 // Buffer transform from interleaved to channel seperated format
1315 // the absolute value of pitch is the number of bytes of an image row
1316 // if pitch is negative, then buff points to the last row of a bottom-up image (first byte on last row)
1317 // if pitch is positive, then buff points to the first row of a top-down image (first byte)
1318 // bpp is the number of bits per pixel used in image buffer buff
1319 //
1320 // RGB is transformed into YUV format (ordering of buffer data is BGR[A])
1321 // Y = (R + 2*G + B)/4 -128
1322 // U = R - G
1323 // V = B - G
1324 //
1325 // Since PGF Codec version 2.0 images are stored in top-down direction
1326 //
1327 // 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
1328 // provide a channelMap of size of expected channels (depending on image mode). For example, PGF expects in RGB color mode a channel sequence BGR.
1329 // If your provided image buffer contains a channel sequence ARGB, then the channelMap looks like { 3, 2, 1 }.
1330 void CPGFImage::RgbToYuv(int pitch, UINT8* buff, BYTE bpp, int channelMap[], CallbackPtr cb, void *data /*=NULL*/) THROW_ {
1331  ASSERT(buff);
1332  int yPos = 0, cnt = 0;
1333  double percent = 0;
1334  const double dP = 1.0/m_header.height;
1335  int defMap[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; ASSERT(sizeof(defMap)/sizeof(defMap[0]) == MaxChannels);
1336 
1337  if (channelMap == NULL) channelMap = defMap;
1338 
1339  switch(m_header.mode) {
1340  case ImageModeBitmap:
1341  {
1342  ASSERT(m_header.channels == 1);
1343  ASSERT(m_header.bpp == 1);
1344  ASSERT(bpp == 1);
1345 
1346  const UINT32 w = m_header.width;
1347  const UINT32 w2 = (m_header.width + 7)/8;
1348  DataT* y = m_channel[0]; ASSERT(y);
1349 
1350  for (UINT32 h=0; h < m_header.height; h++) {
1351  if (cb) {
1352  if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1353  percent += dP;
1354  }
1355 
1356  for (UINT32 j=0; j < w2; j++) {
1357  y[yPos++] = buff[j] - YUVoffset8;
1358  }
1359  for (UINT32 j=w2; j < w; j++) {
1360  y[yPos++] = YUVoffset8;
1361  }
1362 
1363  //UINT cnt = w;
1364  //for (UINT32 j=0; j < w2; j++) {
1365  // for (int k=7; k >= 0; k--) {
1366  // if (cnt) {
1367  // y[yPos++] = YUVoffset8 + (1 & (buff[j] >> k));
1368  // cnt--;
1369  // }
1370  // }
1371  //}
1372  buff += pitch;
1373  }
1374  }
1375  break;
1376  case ImageModeIndexedColor:
1377  case ImageModeGrayScale:
1378  case ImageModeHSLColor:
1379  case ImageModeHSBColor:
1380  case ImageModeLabColor:
1381  {
1382  ASSERT(m_header.channels >= 1);
1383  ASSERT(m_header.bpp == m_header.channels*8);
1384  ASSERT(bpp%8 == 0);
1385  const int channels = bpp/8; ASSERT(channels >= m_header.channels);
1386 
1387  for (UINT32 h=0; h < m_header.height; h++) {
1388  if (cb) {
1389  if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1390  percent += dP;
1391  }
1392 
1393  cnt = 0;
1394  for (UINT32 w=0; w < m_header.width; w++) {
1395  for (int c=0; c < m_header.channels; c++) {
1396  m_channel[c][yPos] = buff[cnt + channelMap[c]] - YUVoffset8;
1397  }
1398  cnt += channels;
1399  yPos++;
1400  }
1401  buff += pitch;
1402  }
1403  }
1404  break;
1405  case ImageModeGray16:
1406  case ImageModeLab48:
1407  {
1408  ASSERT(m_header.channels >= 1);
1409  ASSERT(m_header.bpp == m_header.channels*16);
1410  ASSERT(bpp%16 == 0);
1411 
1412  UINT16 *buff16 = (UINT16 *)buff;
1413  const int pitch16 = pitch/2;
1414  const int channels = bpp/16; ASSERT(channels >= m_header.channels);
1415  const int shift = 16 - UsedBitsPerChannel(); ASSERT(shift >= 0);
1416  const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1);
1417 
1418  for (UINT32 h=0; h < m_header.height; h++) {
1419  if (cb) {
1420  if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1421  percent += dP;
1422  }
1423 
1424  cnt = 0;
1425  for (UINT32 w=0; w < m_header.width; w++) {
1426  for (int c=0; c < m_header.channels; c++) {
1427  m_channel[c][yPos] = (buff16[cnt + channelMap[c]] >> shift) - yuvOffset16;
1428  }
1429  cnt += channels;
1430  yPos++;
1431  }
1432  buff16 += pitch16;
1433  }
1434  }
1435  break;
1436  case ImageModeRGBColor:
1437  {
1438  ASSERT(m_header.channels == 3);
1439  ASSERT(m_header.bpp == m_header.channels*8);
1440  ASSERT(bpp%8 == 0);
1441 
1442  DataT* y = m_channel[0]; ASSERT(y);
1443  DataT* u = m_channel[1]; ASSERT(u);
1444  DataT* v = m_channel[2]; ASSERT(v);
1445  const int channels = bpp/8; ASSERT(channels >= m_header.channels);
1446  UINT8 b, g, r;
1447 
1448  for (UINT32 h=0; h < m_header.height; h++) {
1449  if (cb) {
1450  if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1451  percent += dP;
1452  }
1453 
1454  cnt = 0;
1455  for (UINT32 w=0; w < m_header.width; w++) {
1456  b = buff[cnt + channelMap[0]];
1457  g = buff[cnt + channelMap[1]];
1458  r = buff[cnt + channelMap[2]];
1459  // Yuv
1460  y[yPos] = ((b + (g << 1) + r) >> 2) - YUVoffset8;
1461  u[yPos] = r - g;
1462  v[yPos] = b - g;
1463  yPos++;
1464  cnt += channels;
1465  }
1466  buff += pitch;
1467  }
1468  }
1469  break;
1470  case ImageModeRGB48:
1471  {
1472  ASSERT(m_header.channels == 3);
1473  ASSERT(m_header.bpp == m_header.channels*16);
1474  ASSERT(bpp%16 == 0);
1475 
1476  UINT16 *buff16 = (UINT16 *)buff;
1477  const int pitch16 = pitch/2;
1478  const int channels = bpp/16; ASSERT(channels >= m_header.channels);
1479  const int shift = 16 - UsedBitsPerChannel(); ASSERT(shift >= 0);
1480  const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1);
1481 
1482  DataT* y = m_channel[0]; ASSERT(y);
1483  DataT* u = m_channel[1]; ASSERT(u);
1484  DataT* v = m_channel[2]; ASSERT(v);
1485  UINT16 b, g, r;
1486 
1487  for (UINT32 h=0; h < m_header.height; h++) {
1488  if (cb) {
1489  if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1490  percent += dP;
1491  }
1492 
1493  cnt = 0;
1494  for (UINT32 w=0; w < m_header.width; w++) {
1495  b = buff16[cnt + channelMap[0]] >> shift;
1496  g = buff16[cnt + channelMap[1]] >> shift;
1497  r = buff16[cnt + channelMap[2]] >> shift;
1498  // Yuv
1499  y[yPos] = ((b + (g << 1) + r) >> 2) - yuvOffset16;
1500  u[yPos] = r - g;
1501  v[yPos] = b - g;
1502  yPos++;
1503  cnt += channels;
1504  }
1505  buff16 += pitch16;
1506  }
1507  }
1508  break;
1509  case ImageModeRGBA:
1510  case ImageModeCMYKColor:
1511  {
1512  ASSERT(m_header.channels == 4);
1513  ASSERT(m_header.bpp == m_header.channels*8);
1514  ASSERT(bpp%8 == 0);
1515  const int channels = bpp/8; ASSERT(channels >= m_header.channels);
1516 
1517  DataT* y = m_channel[0]; ASSERT(y);
1518  DataT* u = m_channel[1]; ASSERT(u);
1519  DataT* v = m_channel[2]; ASSERT(v);
1520  DataT* a = m_channel[3]; ASSERT(a);
1521  UINT8 b, g, r;
1522 
1523  for (UINT32 h=0; h < m_header.height; h++) {
1524  if (cb) {
1525  if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1526  percent += dP;
1527  }
1528 
1529  cnt = 0;
1530  for (UINT32 w=0; w < m_header.width; w++) {
1531  b = buff[cnt + channelMap[0]];
1532  g = buff[cnt + channelMap[1]];
1533  r = buff[cnt + channelMap[2]];
1534  // Yuv
1535  y[yPos] = ((b + (g << 1) + r) >> 2) - YUVoffset8;
1536  u[yPos] = r - g;
1537  v[yPos] = b - g;
1538  a[yPos++] = buff[cnt + channelMap[3]] - YUVoffset8;
1539  cnt += channels;
1540  }
1541  buff += pitch;
1542  }
1543  }
1544  break;
1545  case ImageModeCMYK64:
1546  {
1547  ASSERT(m_header.channels == 4);
1548  ASSERT(m_header.bpp == m_header.channels*16);
1549  ASSERT(bpp%16 == 0);
1550 
1551  UINT16 *buff16 = (UINT16 *)buff;
1552  const int pitch16 = pitch/2;
1553  const int channels = bpp/16; ASSERT(channels >= m_header.channels);
1554  const int shift = 16 - UsedBitsPerChannel(); ASSERT(shift >= 0);
1555  const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1);
1556 
1557  DataT* y = m_channel[0]; ASSERT(y);
1558  DataT* u = m_channel[1]; ASSERT(u);
1559  DataT* v = m_channel[2]; ASSERT(v);
1560  DataT* a = m_channel[3]; ASSERT(a);
1561  UINT16 b, g, r;
1562 
1563  for (UINT32 h=0; h < m_header.height; h++) {
1564  if (cb) {
1565  if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1566  percent += dP;
1567  }
1568 
1569  cnt = 0;
1570  for (UINT32 w=0; w < m_header.width; w++) {
1571  b = buff16[cnt + channelMap[0]] >> shift;
1572  g = buff16[cnt + channelMap[1]] >> shift;
1573  r = buff16[cnt + channelMap[2]] >> shift;
1574  // Yuv
1575  y[yPos] = ((b + (g << 1) + r) >> 2) - yuvOffset16;
1576  u[yPos] = r - g;
1577  v[yPos] = b - g;
1578  a[yPos++] = (buff16[cnt + channelMap[3]] >> shift) - yuvOffset16;
1579  cnt += channels;
1580  }
1581  buff16 += pitch16;
1582  }
1583  }
1584  break;
1585 #ifdef __PGF32SUPPORT__
1586  case ImageModeGray32:
1587  {
1588  ASSERT(m_header.channels == 1);
1589  ASSERT(m_header.bpp == 32);
1590  ASSERT(bpp == 32);
1591  ASSERT(DataTSize == sizeof(UINT32));
1592 
1593  DataT* y = m_channel[0]; ASSERT(y);
1594 
1595  UINT32 *buff32 = (UINT32 *)buff;
1596  const int pitch32 = pitch/4;
1597  const int shift = 31 - UsedBitsPerChannel(); ASSERT(shift >= 0);
1598  const DataT yuvOffset31 = 1 << (UsedBitsPerChannel() - 1);
1599 
1600  for (UINT32 h=0; h < m_header.height; h++) {
1601  if (cb) {
1602  if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1603  percent += dP;
1604  }
1605 
1606  for (UINT32 w=0; w < m_header.width; w++) {
1607  y[yPos++] = (buff32[w] >> shift) - yuvOffset31;
1608  }
1609  buff32 += pitch32;
1610  }
1611  }
1612  break;
1613 #endif
1614  case ImageModeRGB12:
1615  {
1616  ASSERT(m_header.channels == 3);
1617  ASSERT(m_header.bpp == m_header.channels*4);
1618  ASSERT(bpp == m_header.channels*4);
1619 
1620  DataT* y = m_channel[0]; ASSERT(y);
1621  DataT* u = m_channel[1]; ASSERT(u);
1622  DataT* v = m_channel[2]; ASSERT(v);
1623 
1624  UINT8 rgb = 0, b, g, r;
1625 
1626  for (UINT32 h=0; h < m_header.height; h++) {
1627  if (cb) {
1628  if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1629  percent += dP;
1630  }
1631 
1632  cnt = 0;
1633  for (UINT32 w=0; w < m_header.width; w++) {
1634  if (w%2 == 0) {
1635  // even pixel position
1636  rgb = buff[cnt];
1637  b = rgb & 0x0F;
1638  g = (rgb & 0xF0) >> 4;
1639  cnt++;
1640  rgb = buff[cnt];
1641  r = rgb & 0x0F;
1642  } else {
1643  // odd pixel position
1644  b = (rgb & 0xF0) >> 4;
1645  cnt++;
1646  rgb = buff[cnt];
1647  g = rgb & 0x0F;
1648  r = (rgb & 0xF0) >> 4;
1649  cnt++;
1650  }
1651 
1652  // Yuv
1653  y[yPos] = ((b + (g << 1) + r) >> 2) - YUVoffset4;
1654  u[yPos] = r - g;
1655  v[yPos] = b - g;
1656  yPos++;
1657  }
1658  buff += pitch;
1659  }
1660  }
1661  break;
1662  case ImageModeRGB16:
1663  {
1664  ASSERT(m_header.channels == 3);
1665  ASSERT(m_header.bpp == 16);
1666  ASSERT(bpp == 16);
1667 
1668  DataT* y = m_channel[0]; ASSERT(y);
1669  DataT* u = m_channel[1]; ASSERT(u);
1670  DataT* v = m_channel[2]; ASSERT(v);
1671 
1672  UINT16 *buff16 = (UINT16 *)buff;
1673  UINT16 rgb, b, g, r;
1674  const int pitch16 = pitch/2;
1675 
1676  for (UINT32 h=0; h < m_header.height; h++) {
1677  if (cb) {
1678  if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1679  percent += dP;
1680  }
1681  for (UINT32 w=0; w < m_header.width; w++) {
1682  rgb = buff16[w];
1683  r = (rgb & 0xF800) >> 10; // highest 5 bits
1684  g = (rgb & 0x07E0) >> 5; // middle 6 bits
1685  b = (rgb & 0x001F) << 1; // lowest 5 bits
1686  // Yuv
1687  y[yPos] = ((b + (g << 1) + r) >> 2) - YUVoffset6;
1688  u[yPos] = r - g;
1689  v[yPos] = b - g;
1690  yPos++;
1691  }
1692 
1693  buff16 += pitch16;
1694  }
1695  }
1696  break;
1697  default:
1698  ASSERT(false);
1699  }
1700 }
1701 
1703 // Get image data in interleaved format: (ordering of RGB data is BGR[A])
1704 // Upsampling, YUV to RGB transform and interleaving are done here to reduce the number
1705 // of passes over the data.
1706 // The absolute value of pitch is the number of bytes of an image row of the given image buffer.
1707 // If pitch is negative, then the image buffer must point to the last row of a bottom-up image (first byte on last row).
1708 // if pitch is positive, then the image buffer must point to the first row of a top-down image (first byte).
1709 // 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
1710 // provide a channelMap of size of expected channels (depending on image mode). For example, PGF provides a channel sequence BGR in RGB color mode.
1711 // If your provided image buffer expects a channel sequence ARGB, then the channelMap looks like { 3, 2, 1 }.
1712 // It might throw an IOException.
1713 // @param pitch The number of bytes of a row of the image buffer.
1714 // @param buff An image buffer.
1715 // @param bpp The number of bits per pixel used in image buffer.
1716 // @param channelMap A integer array containing the mapping of PGF channel ordering to expected channel ordering.
1717 // @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.
1718 // @param data Data Pointer to C++ class container to host callback procedure.
1719 void CPGFImage::GetBitmap(int pitch, UINT8* buff, BYTE bpp, int channelMap[] /*= NULL */, CallbackPtr cb /*= NULL*/, void *data /*=NULL*/) const THROW_ {
1720  ASSERT(buff);
1721  UINT32 w = m_width[0];
1722  UINT32 h = m_height[0];
1723  UINT8* targetBuff = 0; // used if ROI is used
1724  UINT8* buffStart = 0; // used if ROI is used
1725  int targetPitch = 0; // used if ROI is used
1726 
1727 #ifdef __PGFROISUPPORT__
1728  const PGFRect& roi = (ROIisSupported()) ? m_wtChannel[0]->GetROI(m_currentLevel) : PGFRect(0, 0, w, h); // roi is usually larger than m_roi
1729  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));
1730  ASSERT(w <= roi.Width() && h <= roi.Height());
1731  ASSERT(roi.left <= levelRoi.left && levelRoi.right <= roi.right);
1732  ASSERT(roi.top <= levelRoi.top && levelRoi.bottom <= roi.bottom);
1733 
1734  if (ROIisSupported() && (levelRoi.Width() < w || levelRoi.Height() < h)) {
1735  // ROI is used -> create a temporary image buffer for roi
1736  // compute pitch
1737  targetPitch = pitch;
1738  pitch = AlignWordPos(w*bpp)/8;
1739 
1740  // create temporary output buffer
1741  targetBuff = buff;
1742  buff = buffStart = new(std::nothrow) UINT8[pitch*h];
1743  if (!buff) ReturnWithError(InsufficientMemory);
1744  }
1745 #endif
1746 
1747  const bool wOdd = (1 == w%2);
1748 
1749  const double dP = 1.0/h;
1750  int defMap[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; ASSERT(sizeof(defMap)/sizeof(defMap[0]) == MaxChannels);
1751  if (channelMap == NULL) channelMap = defMap;
1752  int sampledPos = 0, yPos = 0;
1753  DataT uAvg, vAvg;
1754  double percent = 0;
1755  UINT32 i, j;
1756 
1757  switch(m_header.mode) {
1758  case ImageModeBitmap:
1759  {
1760  ASSERT(m_header.channels == 1);
1761  ASSERT(m_header.bpp == 1);
1762  ASSERT(bpp == 1);
1763 
1764  const UINT32 w2 = (w + 7)/8;
1765  DataT* y = m_channel[0]; ASSERT(y);
1766 
1767  for (i=0; i < h; i++) {
1768 
1769  for (j=0; j < w2; j++) {
1770  buff[j] = Clamp8(y[yPos++] + YUVoffset8);
1771  }
1772  yPos += w - w2;
1773 
1774  //UINT32 cnt = w;
1775  //for (j=0; j < w2; j++) {
1776  // buff[j] = 0;
1777  // for (int k=0; k < 8; k++) {
1778  // if (cnt) {
1779  // buff[j] <<= 1;
1780  // buff[j] |= (1 & (y[yPos++] - YUVoffset8));
1781  // cnt--;
1782  // }
1783  // }
1784  //}
1785  buff += pitch;
1786 
1787  if (cb) {
1788  percent += dP;
1789  if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1790  }
1791  }
1792  break;
1793  }
1794  case ImageModeIndexedColor:
1795  case ImageModeGrayScale:
1796  case ImageModeHSLColor:
1797  case ImageModeHSBColor:
1798  {
1799  ASSERT(m_header.channels >= 1);
1800  ASSERT(m_header.bpp == m_header.channels*8);
1801  ASSERT(bpp%8 == 0);
1802 
1803  int cnt, channels = bpp/8; ASSERT(channels >= m_header.channels);
1804 
1805  for (i=0; i < h; i++) {
1806  cnt = 0;
1807  for (j=0; j < w; j++) {
1808  for (int c=0; c < m_header.channels; c++) {
1809  buff[cnt + channelMap[c]] = Clamp8(m_channel[c][yPos] + YUVoffset8);
1810  }
1811  cnt += channels;
1812  yPos++;
1813  }
1814  buff += pitch;
1815 
1816  if (cb) {
1817  percent += dP;
1818  if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1819  }
1820  }
1821  break;
1822  }
1823  case ImageModeGray16:
1824  {
1825  ASSERT(m_header.channels >= 1);
1826  ASSERT(m_header.bpp == m_header.channels*16);
1827 
1828  const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1);
1829  int cnt, channels;
1830 
1831  if (bpp%16 == 0) {
1832  const int shift = 16 - UsedBitsPerChannel(); ASSERT(shift >= 0);
1833  UINT16 *buff16 = (UINT16 *)buff;
1834  int pitch16 = pitch/2;
1835  channels = bpp/16; ASSERT(channels >= m_header.channels);
1836 
1837  for (i=0; i < h; i++) {
1838  cnt = 0;
1839  for (j=0; j < w; j++) {
1840  for (int c=0; c < m_header.channels; c++) {
1841  buff16[cnt + channelMap[c]] = Clamp16((m_channel[c][yPos] + yuvOffset16) << shift);
1842  }
1843  cnt += channels;
1844  yPos++;
1845  }
1846  buff16 += pitch16;
1847 
1848  if (cb) {
1849  percent += dP;
1850  if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1851  }
1852  }
1853  } else {
1854  ASSERT(bpp%8 == 0);
1855  const int shift = __max(0, UsedBitsPerChannel() - 8);
1856  channels = bpp/8; ASSERT(channels >= m_header.channels);
1857 
1858  for (i=0; i < h; i++) {
1859  cnt = 0;
1860  for (j=0; j < w; j++) {
1861  for (int c=0; c < m_header.channels; c++) {
1862  buff[cnt + channelMap[c]] = Clamp8((m_channel[c][yPos] + yuvOffset16) >> shift);
1863  }
1864  cnt += channels;
1865  yPos++;
1866  }
1867  buff += pitch;
1868 
1869  if (cb) {
1870  percent += dP;
1871  if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1872  }
1873  }
1874  }
1875  break;
1876  }
1877  case ImageModeRGBColor:
1878  {
1879  ASSERT(m_header.channels == 3);
1880  ASSERT(m_header.bpp == m_header.channels*8);
1881  ASSERT(bpp%8 == 0);
1882  ASSERT(bpp >= m_header.bpp);
1883 
1884  DataT* y = m_channel[0]; ASSERT(y);
1885  DataT* u = m_channel[1]; ASSERT(u);
1886  DataT* v = m_channel[2]; ASSERT(v);
1887  UINT8 *buffg = &buff[channelMap[1]],
1888  *buffr = &buff[channelMap[2]],
1889  *buffb = &buff[channelMap[0]];
1890  UINT8 g;
1891  int cnt, channels = bpp/8;
1892  if(m_downsample){
1893  for (i=0; i < h; i++) {
1894  if (i%2) sampledPos -= (w + 1)/2;
1895  cnt = 0;
1896  for (j=0; j < w; j++) {
1897  // image was downsampled
1898  uAvg = u[sampledPos];
1899  vAvg = v[sampledPos];
1900  // Yuv
1901  buffg[cnt] = g = Clamp8(y[yPos] + YUVoffset8 - ((uAvg + vAvg ) >> 2)); // must be logical shift operator
1902  buffr[cnt] = Clamp8(uAvg + g);
1903  buffb[cnt] = Clamp8(vAvg + g);
1904  yPos++;
1905  cnt += channels;
1906  if (j%2) sampledPos++;
1907  }
1908  buffb += pitch;
1909  buffg += pitch;
1910  buffr += pitch;
1911  if (wOdd) sampledPos++;
1912  if (cb) {
1913  percent += dP;
1914  if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1915  }
1916  }
1917  }else{
1918  for (i=0; i < h; i++) {
1919  cnt = 0;
1920  for (j = 0; j < w; j++) {
1921  uAvg = u[yPos];
1922  vAvg = v[yPos];
1923  // Yuv
1924  buffg[cnt] = g = Clamp8(y[yPos] + YUVoffset8 - ((uAvg + vAvg ) >> 2)); // must be logical shift operator
1925  buffr[cnt] = Clamp8(uAvg + g);
1926  buffb[cnt] = Clamp8(vAvg + g);
1927  yPos++;
1928  cnt += channels;
1929  }
1930  buffb += pitch;
1931  buffg += pitch;
1932  buffr += pitch;
1933 
1934  if (cb) {
1935  percent += dP;
1936  if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1937  }
1938  }
1939  }
1940  break;
1941  }
1942  case ImageModeRGB48:
1943  {
1944  ASSERT(m_header.channels == 3);
1945  ASSERT(m_header.bpp == 48);
1946 
1947  const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1);
1948 
1949  DataT* y = m_channel[0]; ASSERT(y);
1950  DataT* u = m_channel[1]; ASSERT(u);
1951  DataT* v = m_channel[2]; ASSERT(v);
1952  int cnt, channels;
1953  DataT g;
1954 
1955  if (bpp >= 48 && bpp%16 == 0) {
1956  const int shift = 16 - UsedBitsPerChannel(); ASSERT(shift >= 0);
1957  UINT16 *buff16 = (UINT16 *)buff;
1958  int pitch16 = pitch/2;
1959  channels = bpp/16; ASSERT(channels >= m_header.channels);
1960 
1961  for (i=0; i < h; i++) {
1962  if (i%2) sampledPos -= (w + 1)/2;
1963  cnt = 0;
1964  for (j=0; j < w; j++) {
1965  if (m_downsample) {
1966  // image was downsampled
1967  uAvg = u[sampledPos];
1968  vAvg = v[sampledPos];
1969  } else {
1970  uAvg = u[yPos];
1971  vAvg = v[yPos];
1972  }
1973  // Yuv
1974  g = y[yPos] + yuvOffset16 - ((uAvg + vAvg ) >> 2); // must be logical shift operator
1975  buff16[cnt + channelMap[1]] = Clamp16(g << shift);
1976  buff16[cnt + channelMap[2]] = Clamp16((uAvg + g) << shift);
1977  buff16[cnt + channelMap[0]] = Clamp16((vAvg + g) << shift);
1978  yPos++;
1979  cnt += channels;
1980  if (j%2) sampledPos++;
1981  }
1982  buff16 += pitch16;
1983  if (wOdd) sampledPos++;
1984 
1985  if (cb) {
1986  percent += dP;
1987  if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
1988  }
1989  }
1990  } else {
1991  ASSERT(bpp%8 == 0);
1992  const int shift = __max(0, UsedBitsPerChannel() - 8);
1993  channels = bpp/8; ASSERT(channels >= m_header.channels);
1994 
1995  for (i=0; i < h; i++) {
1996  if (i%2) sampledPos -= (w + 1)/2;
1997  cnt = 0;
1998  for (j=0; j < w; j++) {
1999  if (m_downsample) {
2000  // image was downsampled
2001  uAvg = u[sampledPos];
2002  vAvg = v[sampledPos];
2003  } else {
2004  uAvg = u[yPos];
2005  vAvg = v[yPos];
2006  }
2007  // Yuv
2008  g = y[yPos] + yuvOffset16 - ((uAvg + vAvg ) >> 2); // must be logical shift operator
2009  buff[cnt + channelMap[1]] = Clamp8(g >> shift);
2010  buff[cnt + channelMap[2]] = Clamp8((uAvg + g) >> shift);
2011  buff[cnt + channelMap[0]] = Clamp8((vAvg + g) >> shift);
2012  yPos++;
2013  cnt += channels;
2014  if (j%2) sampledPos++;
2015  }
2016  buff += pitch;
2017  if (wOdd) sampledPos++;
2018 
2019  if (cb) {
2020  percent += dP;
2021  if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
2022  }
2023  }
2024  }
2025  break;
2026  }
2027  case ImageModeLabColor:
2028  {
2029  ASSERT(m_header.channels == 3);
2030  ASSERT(m_header.bpp == m_header.channels*8);
2031  ASSERT(bpp%8 == 0);
2032 
2033  DataT* l = m_channel[0]; ASSERT(l);
2034  DataT* a = m_channel[1]; ASSERT(a);
2035  DataT* b = m_channel[2]; ASSERT(b);
2036  int cnt, channels = bpp/8; ASSERT(channels >= m_header.channels);
2037 
2038  for (i=0; i < h; i++) {
2039  if (i%2) sampledPos -= (w + 1)/2;
2040  cnt = 0;
2041  for (j=0; j < w; j++) {
2042  if (m_downsample) {
2043  // image was downsampled
2044  uAvg = a[sampledPos];
2045  vAvg = b[sampledPos];
2046  } else {
2047  uAvg = a[yPos];
2048  vAvg = b[yPos];
2049  }
2050  buff[cnt + channelMap[0]] = Clamp8(l[yPos] + YUVoffset8);
2051  buff[cnt + channelMap[1]] = Clamp8(uAvg + YUVoffset8);
2052  buff[cnt + channelMap[2]] = Clamp8(vAvg + YUVoffset8);
2053  cnt += channels;
2054  yPos++;
2055  if (j%2) sampledPos++;
2056  }
2057  buff += pitch;
2058  if (wOdd) sampledPos++;
2059 
2060  if (cb) {
2061  percent += dP;
2062  if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
2063  }
2064  }
2065  break;
2066  }
2067  case ImageModeLab48:
2068  {
2069  ASSERT(m_header.channels == 3);
2070  ASSERT(m_header.bpp == m_header.channels*16);
2071 
2072  const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1);
2073 
2074  DataT* l = m_channel[0]; ASSERT(l);
2075  DataT* a = m_channel[1]; ASSERT(a);
2076  DataT* b = m_channel[2]; ASSERT(b);
2077  int cnt, channels;
2078 
2079  if (bpp%16 == 0) {
2080  const int shift = 16 - UsedBitsPerChannel(); ASSERT(shift >= 0);
2081  UINT16 *buff16 = (UINT16 *)buff;
2082  int pitch16 = pitch/2;
2083  channels = bpp/16; ASSERT(channels >= m_header.channels);
2084 
2085  for (i=0; i < h; i++) {
2086  if (i%2) sampledPos -= (w + 1)/2;
2087  cnt = 0;
2088  for (j=0; j < w; j++) {
2089  if (m_downsample) {
2090  // image was downsampled
2091  uAvg = a[sampledPos];
2092  vAvg = b[sampledPos];
2093  } else {
2094  uAvg = a[yPos];
2095  vAvg = b[yPos];
2096  }
2097  buff16[cnt + channelMap[0]] = Clamp16((l[yPos] + yuvOffset16) << shift);
2098  buff16[cnt + channelMap[1]] = Clamp16((uAvg + yuvOffset16) << shift);
2099  buff16[cnt + channelMap[2]] = Clamp16((vAvg + yuvOffset16) << shift);
2100  cnt += channels;
2101  yPos++;
2102  if (j%2) sampledPos++;
2103  }
2104  buff16 += pitch16;
2105  if (wOdd) sampledPos++;
2106 
2107  if (cb) {
2108  percent += dP;
2109  if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
2110  }
2111  }
2112  } else {
2113  ASSERT(bpp%8 == 0);
2114  const int shift = __max(0, UsedBitsPerChannel() - 8);
2115  channels = bpp/8; ASSERT(channels >= m_header.channels);
2116 
2117  for (i=0; i < h; i++) {
2118  if (i%2) sampledPos -= (w + 1)/2;
2119  cnt = 0;
2120  for (j=0; j < w; j++) {
2121  if (m_downsample) {
2122  // image was downsampled
2123  uAvg = a[sampledPos];
2124  vAvg = b[sampledPos];
2125  } else {
2126  uAvg = a[yPos];
2127  vAvg = b[yPos];
2128  }
2129  buff[cnt + channelMap[0]] = Clamp8((l[yPos] + yuvOffset16) >> shift);
2130  buff[cnt + channelMap[1]] = Clamp8((uAvg + yuvOffset16) >> shift);
2131  buff[cnt + channelMap[2]] = Clamp8((vAvg + yuvOffset16) >> shift);
2132  cnt += channels;
2133  yPos++;
2134  if (j%2) sampledPos++;
2135  }
2136  buff += pitch;
2137  if (wOdd) sampledPos++;
2138 
2139  if (cb) {
2140  percent += dP;
2141  if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
2142  }
2143  }
2144  }
2145  break;
2146  }
2147  case ImageModeRGBA:
2148  case ImageModeCMYKColor:
2149  {
2150  ASSERT(m_header.channels == 4);
2151  ASSERT(m_header.bpp == m_header.channels*8);
2152  ASSERT(bpp%8 == 0);
2153 
2154  DataT* y = m_channel[0]; ASSERT(y);
2155  DataT* u = m_channel[1]; ASSERT(u);
2156  DataT* v = m_channel[2]; ASSERT(v);
2157  DataT* a = m_channel[3]; ASSERT(a);
2158  UINT8 g, aAvg;
2159  int cnt, channels = bpp/8; ASSERT(channels >= m_header.channels);
2160 
2161  for (i=0; i < h; i++) {
2162  if (i%2) sampledPos -= (w + 1)/2;
2163  cnt = 0;
2164  for (j=0; j < w; j++) {
2165  if (m_downsample) {
2166  // image was downsampled
2167  uAvg = u[sampledPos];
2168  vAvg = v[sampledPos];
2169  aAvg = Clamp8(a[sampledPos] + YUVoffset8);
2170  } else {
2171  uAvg = u[yPos];
2172  vAvg = v[yPos];
2173  aAvg = Clamp8(a[yPos] + YUVoffset8);
2174  }
2175  // Yuv
2176  buff[cnt + channelMap[1]] = g = Clamp8(y[yPos] + YUVoffset8 - ((uAvg + vAvg ) >> 2)); // must be logical shift operator
2177  buff[cnt + channelMap[2]] = Clamp8(uAvg + g);
2178  buff[cnt + channelMap[0]] = Clamp8(vAvg + g);
2179  buff[cnt + channelMap[3]] = aAvg;
2180  yPos++;
2181  cnt += channels;
2182  if (j%2) sampledPos++;
2183  }
2184  buff += pitch;
2185  if (wOdd) sampledPos++;
2186 
2187  if (cb) {
2188  percent += dP;
2189  if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
2190  }
2191  }
2192  break;
2193  }
2194  case ImageModeCMYK64:
2195  {
2196  ASSERT(m_header.channels == 4);
2197  ASSERT(m_header.bpp == 64);
2198 
2199  const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1);
2200 
2201  DataT* y = m_channel[0]; ASSERT(y);
2202  DataT* u = m_channel[1]; ASSERT(u);
2203  DataT* v = m_channel[2]; ASSERT(v);
2204  DataT* a = m_channel[3]; ASSERT(a);
2205  DataT g, aAvg;
2206  int cnt, channels;
2207 
2208  if (bpp%16 == 0) {
2209  const int shift = 16 - UsedBitsPerChannel(); ASSERT(shift >= 0);
2210  UINT16 *buff16 = (UINT16 *)buff;
2211  int pitch16 = pitch/2;
2212  channels = bpp/16; ASSERT(channels >= m_header.channels);
2213 
2214  for (i=0; i < h; i++) {
2215  if (i%2) sampledPos -= (w + 1)/2;
2216  cnt = 0;
2217  for (j=0; j < w; j++) {
2218  if (m_downsample) {
2219  // image was downsampled
2220  uAvg = u[sampledPos];
2221  vAvg = v[sampledPos];
2222  aAvg = a[sampledPos] + yuvOffset16;
2223  } else {
2224  uAvg = u[yPos];
2225  vAvg = v[yPos];
2226  aAvg = a[yPos] + yuvOffset16;
2227  }
2228  // Yuv
2229  g = y[yPos] + yuvOffset16 - ((uAvg + vAvg ) >> 2); // must be logical shift operator
2230  buff16[cnt + channelMap[1]] = Clamp16(g << shift);
2231  buff16[cnt + channelMap[2]] = Clamp16((uAvg + g) << shift);
2232  buff16[cnt + channelMap[0]] = Clamp16((vAvg + g) << shift);
2233  buff16[cnt + channelMap[3]] = Clamp16(aAvg << shift);
2234  yPos++;
2235  cnt += channels;
2236  if (j%2) sampledPos++;
2237  }
2238  buff16 += pitch16;
2239  if (wOdd) sampledPos++;
2240 
2241  if (cb) {
2242  percent += dP;
2243  if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
2244  }
2245  }
2246  } else {
2247  ASSERT(bpp%8 == 0);
2248  const int shift = __max(0, UsedBitsPerChannel() - 8);
2249  channels = bpp/8; ASSERT(channels >= m_header.channels);
2250 
2251  for (i=0; i < h; i++) {
2252  if (i%2) sampledPos -= (w + 1)/2;
2253  cnt = 0;
2254  for (j=0; j < w; j++) {
2255  if (m_downsample) {
2256  // image was downsampled
2257  uAvg = u[sampledPos];
2258  vAvg = v[sampledPos];
2259  aAvg = a[sampledPos] + yuvOffset16;
2260  } else {
2261  uAvg = u[yPos];
2262  vAvg = v[yPos];
2263  aAvg = a[yPos] + yuvOffset16;
2264  }
2265  // Yuv
2266  g = y[yPos] + yuvOffset16 - ((uAvg + vAvg ) >> 2); // must be logical shift operator
2267  buff[cnt + channelMap[1]] = Clamp8(g >> shift);
2268  buff[cnt + channelMap[2]] = Clamp8((uAvg + g) >> shift);
2269  buff[cnt + channelMap[0]] = Clamp8((vAvg + g) >> shift);
2270  buff[cnt + channelMap[3]] = Clamp8(aAvg >> shift);
2271  yPos++;
2272  cnt += channels;
2273  if (j%2) sampledPos++;
2274  }
2275  buff += pitch;
2276  if (wOdd) sampledPos++;
2277 
2278  if (cb) {
2279  percent += dP;
2280  if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
2281  }
2282  }
2283  }
2284  break;
2285  }
2286 #ifdef __PGF32SUPPORT__
2287  case ImageModeGray32:
2288  {
2289  ASSERT(m_header.channels == 1);
2290  ASSERT(m_header.bpp == 32);
2291 
2292  const int yuvOffset31 = 1 << (UsedBitsPerChannel() - 1);
2293 
2294  DataT* y = m_channel[0]; ASSERT(y);
2295 
2296  if (bpp == 32) {
2297  const int shift = 31 - UsedBitsPerChannel(); ASSERT(shift >= 0);
2298  UINT32 *buff32 = (UINT32 *)buff;
2299  int pitch32 = pitch/4;
2300 
2301  for (i=0; i < h; i++) {
2302  for (j=0; j < w; j++) {
2303  buff32[j] = Clamp31((y[yPos++] + yuvOffset31) << shift);
2304  }
2305  buff32 += pitch32;
2306 
2307  if (cb) {
2308  percent += dP;
2309  if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
2310  }
2311  }
2312  } else if (bpp == 16) {
2313  const int usedBits = UsedBitsPerChannel();
2314  UINT16 *buff16 = (UINT16 *)buff;
2315  int pitch16 = pitch/2;
2316 
2317  if (usedBits < 16) {
2318  const int shift = 16 - usedBits;
2319  for (i=0; i < h; i++) {
2320  for (j=0; j < w; j++) {
2321  buff16[j] = Clamp16((y[yPos++] + yuvOffset31) << shift);
2322  }
2323  buff16 += pitch16;
2324 
2325  if (cb) {
2326  percent += dP;
2327  if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
2328  }
2329  }
2330  } else {
2331  const int shift = __max(0, usedBits - 16);
2332  for (i=0; i < h; i++) {
2333  for (j=0; j < w; j++) {
2334  buff16[j] = Clamp16((y[yPos++] + yuvOffset31) >> shift);
2335  }
2336  buff16 += pitch16;
2337 
2338  if (cb) {
2339  percent += dP;
2340  if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
2341  }
2342  }
2343  }
2344  } else {
2345  ASSERT(bpp == 8);
2346  const int shift = __max(0, UsedBitsPerChannel() - 8);
2347 
2348  for (i=0; i < h; i++) {
2349  for (j=0; j < w; j++) {
2350  buff[j] = Clamp8((y[yPos++] + yuvOffset31) >> shift);
2351  }
2352  buff += pitch;
2353 
2354  if (cb) {
2355  percent += dP;
2356  if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
2357  }
2358  }
2359  }
2360  break;
2361  }
2362 #endif
2363  case ImageModeRGB12:
2364  {
2365  ASSERT(m_header.channels == 3);
2366  ASSERT(m_header.bpp == m_header.channels*4);
2367  ASSERT(bpp == m_header.channels*4);
2368  ASSERT(!m_downsample);
2369 
2370  DataT* y = m_channel[0]; ASSERT(y);
2371  DataT* u = m_channel[1]; ASSERT(u);
2372  DataT* v = m_channel[2]; ASSERT(v);
2373  UINT16 yval;
2374  int cnt;
2375 
2376  for (i=0; i < h; i++) {
2377  cnt = 0;
2378  for (j=0; j < w; j++) {
2379  // Yuv
2380  uAvg = u[yPos];
2381  vAvg = v[yPos];
2382  yval = Clamp4(y[yPos++] + YUVoffset4 - ((uAvg + vAvg ) >> 2)); // must be logical shift operator
2383  if (j%2 == 0) {
2384  buff[cnt] = UINT8(Clamp4(vAvg + yval) | (yval << 4));
2385  cnt++;
2386  buff[cnt] = Clamp4(uAvg + yval);
2387  } else {
2388  buff[cnt] |= Clamp4(vAvg + yval) << 4;
2389  cnt++;
2390  buff[cnt] = UINT8(yval | (Clamp4(uAvg + yval) << 4));
2391  cnt++;
2392  }
2393  }
2394  buff += pitch;
2395 
2396  if (cb) {
2397  percent += dP;
2398  if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
2399  }
2400  }
2401  break;
2402  }
2403  case ImageModeRGB16:
2404  {
2405  ASSERT(m_header.channels == 3);
2406  ASSERT(m_header.bpp == 16);
2407  ASSERT(bpp == 16);
2408  ASSERT(!m_downsample);
2409 
2410  DataT* y = m_channel[0]; ASSERT(y);
2411  DataT* u = m_channel[1]; ASSERT(u);
2412  DataT* v = m_channel[2]; ASSERT(v);
2413  UINT16 yval;
2414  UINT16 *buff16 = (UINT16 *)buff;
2415  int pitch16 = pitch/2;
2416 
2417  for (i=0; i < h; i++) {
2418  for (j=0; j < w; j++) {
2419  // Yuv
2420  uAvg = u[yPos];
2421  vAvg = v[yPos];
2422  yval = Clamp6(y[yPos++] + YUVoffset6 - ((uAvg + vAvg ) >> 2)); // must be logical shift operator
2423  buff16[j] = (yval << 5) | ((Clamp6(uAvg + yval) >> 1) << 11) | (Clamp6(vAvg + yval) >> 1);
2424  }
2425  buff16 += pitch16;
2426 
2427  if (cb) {
2428  percent += dP;
2429  if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
2430  }
2431  }
2432  break;
2433  }
2434  default:
2435  ASSERT(false);
2436  }
2437 
2438 #ifdef __PGFROISUPPORT__
2439  if (targetBuff) {
2440  // copy valid ROI (m_roi) from temporary buffer (roi) to target buffer
2441  if (bpp%8 == 0) {
2442  BYTE bypp = bpp/8;
2443  buff = buffStart + (levelRoi.top - roi.top)*pitch + (levelRoi.left - roi.left)*bypp;
2444  w = levelRoi.Width()*bypp;
2445  h = levelRoi.Height();
2446 
2447  for (i=0; i < h; i++) {
2448  for (j=0; j < w; j++) {
2449  targetBuff[j] = buff[j];
2450  }
2451  targetBuff += targetPitch;
2452  buff += pitch;
2453  }
2454  } else {
2455  // to do
2456  }
2457 
2458  delete[] buffStart; buffStart = 0;
2459  }
2460 #endif
2461 }
2462 
2477 void CPGFImage::GetYUV(int pitch, DataT* buff, BYTE bpp, int channelMap[] /*= NULL*/, CallbackPtr cb /*= NULL*/, void *data /*=NULL*/) const THROW_ {
2478  ASSERT(buff);
2479  const UINT32 w = m_width[0];
2480  const UINT32 h = m_height[0];
2481  const bool wOdd = (1 == w%2);
2482  const int dataBits = DataTSize*8; ASSERT(dataBits == 16 || dataBits == 32);
2483  const int pitch2 = pitch/DataTSize;
2484  const int yuvOffset = (dataBits == 16) ? YUVoffset8 : YUVoffset16;
2485  const double dP = 1.0/h;
2486 
2487  int defMap[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; ASSERT(sizeof(defMap)/sizeof(defMap[0]) == MaxChannels);
2488  if (channelMap == NULL) channelMap = defMap;
2489  int sampledPos = 0, yPos = 0;
2490  DataT uAvg, vAvg;
2491  double percent = 0;
2492  UINT32 i, j;
2493 
2494  if (m_header.channels == 3) {
2495  ASSERT(bpp%dataBits == 0);
2496 
2497  DataT* y = m_channel[0]; ASSERT(y);
2498  DataT* u = m_channel[1]; ASSERT(u);
2499  DataT* v = m_channel[2]; ASSERT(v);
2500  int cnt, channels = bpp/dataBits; ASSERT(channels >= m_header.channels);
2501 
2502  for (i=0; i < h; i++) {
2503  if (i%2) sampledPos -= (w + 1)/2;
2504  cnt = 0;
2505  for (j=0; j < w; j++) {
2506  if (m_downsample) {
2507  // image was downsampled
2508  uAvg = u[sampledPos];
2509  vAvg = v[sampledPos];
2510  } else {
2511  uAvg = u[yPos];
2512  vAvg = v[yPos];
2513  }
2514  buff[cnt + channelMap[0]] = y[yPos];
2515  buff[cnt + channelMap[1]] = uAvg;
2516  buff[cnt + channelMap[2]] = vAvg;
2517  yPos++;
2518  cnt += channels;
2519  if (j%2) sampledPos++;
2520  }
2521  buff += pitch2;
2522  if (wOdd) sampledPos++;
2523 
2524  if (cb) {
2525  percent += dP;
2526  if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
2527  }
2528  }
2529  } else if (m_header.channels == 4) {
2530  ASSERT(m_header.bpp == m_header.channels*8);
2531  ASSERT(bpp%dataBits == 0);
2532 
2533  DataT* y = m_channel[0]; ASSERT(y);
2534  DataT* u = m_channel[1]; ASSERT(u);
2535  DataT* v = m_channel[2]; ASSERT(v);
2536  DataT* a = m_channel[3]; ASSERT(a);
2537  UINT8 aAvg;
2538  int cnt, channels = bpp/dataBits; ASSERT(channels >= m_header.channels);
2539 
2540  for (i=0; i < h; i++) {
2541  if (i%2) sampledPos -= (w + 1)/2;
2542  cnt = 0;
2543  for (j=0; j < w; j++) {
2544  if (m_downsample) {
2545  // image was downsampled
2546  uAvg = u[sampledPos];
2547  vAvg = v[sampledPos];
2548  aAvg = Clamp8(a[sampledPos] + yuvOffset);
2549  } else {
2550  uAvg = u[yPos];
2551  vAvg = v[yPos];
2552  aAvg = Clamp8(a[yPos] + yuvOffset);
2553  }
2554  // Yuv
2555  buff[cnt + channelMap[0]] = y[yPos];
2556  buff[cnt + channelMap[1]] = uAvg;
2557  buff[cnt + channelMap[2]] = vAvg;
2558  buff[cnt + channelMap[3]] = aAvg;
2559  yPos++;
2560  cnt += channels;
2561  if (j%2) sampledPos++;
2562  }
2563  buff += pitch2;
2564  if (wOdd) sampledPos++;
2565 
2566  if (cb) {
2567  percent += dP;
2568  if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
2569  }
2570  }
2571  }
2572 }
2573 
2588 void CPGFImage::ImportYUV(int pitch, DataT *buff, BYTE bpp, int channelMap[] /*= NULL*/, CallbackPtr cb /*= NULL*/, void *data /*=NULL*/) THROW_ {
2589  ASSERT(buff);
2590  const double dP = 1.0/m_header.height;
2591  const int dataBits = DataTSize*8; ASSERT(dataBits == 16 || dataBits == 32);
2592  const int pitch2 = pitch/DataTSize;
2593  const int yuvOffset = (dataBits == 16) ? YUVoffset8 : YUVoffset16;
2594 
2595  int yPos = 0, cnt = 0;
2596  double percent = 0;
2597  int defMap[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; ASSERT(sizeof(defMap)/sizeof(defMap[0]) == MaxChannels);
2598 
2599  if (channelMap == NULL) channelMap = defMap;
2600 
2601  if (m_header.channels == 3) {
2602  ASSERT(bpp%dataBits == 0);
2603 
2604  DataT* y = m_channel[0]; ASSERT(y);
2605  DataT* u = m_channel[1]; ASSERT(u);
2606  DataT* v = m_channel[2]; ASSERT(v);
2607  const int channels = bpp/dataBits; ASSERT(channels >= m_header.channels);
2608 
2609  for (UINT32 h=0; h < m_header.height; h++) {
2610  if (cb) {
2611  if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
2612  percent += dP;
2613  }
2614 
2615  cnt = 0;
2616  for (UINT32 w=0; w < m_header.width; w++) {
2617  y[yPos] = buff[cnt + channelMap[0]];
2618  u[yPos] = buff[cnt + channelMap[1]];
2619  v[yPos] = buff[cnt + channelMap[2]];
2620  yPos++;
2621  cnt += channels;
2622  }
2623  buff += pitch2;
2624  }
2625  } else if (m_header.channels == 4) {
2626  ASSERT(bpp%dataBits == 0);
2627 
2628  DataT* y = m_channel[0]; ASSERT(y);
2629  DataT* u = m_channel[1]; ASSERT(u);
2630  DataT* v = m_channel[2]; ASSERT(v);
2631  DataT* a = m_channel[3]; ASSERT(a);
2632  const int channels = bpp/dataBits; ASSERT(channels >= m_header.channels);
2633 
2634  for (UINT32 h=0; h < m_header.height; h++) {
2635  if (cb) {
2636  if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
2637  percent += dP;
2638  }
2639 
2640  cnt = 0;
2641  for (UINT32 w=0; w < m_header.width; w++) {
2642  y[yPos] = buff[cnt + channelMap[0]];
2643  u[yPos] = buff[cnt + channelMap[1]];
2644  v[yPos] = buff[cnt + channelMap[2]];
2645  a[yPos] = buff[cnt + channelMap[3]] - yuvOffset;
2646  yPos++;
2647  cnt += channels;
2648  }
2649  buff += pitch2;
2650  }
2651  }
2652 
2653  if (m_downsample) {
2654  // Subsampling of the chrominance and alpha channels
2655  for (int i=1; i < m_header.channels; i++) {
2656  Downsample(i);
2657  }
2658  }
2659 }
2660