libpgf: PGFimage.cpp Source File

| libpgf 7.21.2 PGF - Progressive Graphics File

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