| libpgf 7.21.2 PGF - Progressive Graphics File

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