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