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