Subversion Repositories Scribus

Compare Revisions

Ignore whitespace Rev 19720 → Rev 19721

/trunk/Scribus/scribus/third_party/pgf/BitStream.h
1,272 → 1,272
/*
* The Progressive Graphics File; http://www.libpgf.org
*
* $Date: 2006-06-04 22:05:59 +0200 (So, 04 Jun 2006) $
* $Revision: 229 $
*
* This file Copyright (C) 2006 xeraina GmbH, Switzerland
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
 
//////////////////////////////////////////////////////////////////////
/// @file Bitstream.h
/// @brief PGF bit-stream operations
/// @author C. Stamm
 
#ifndef PGF_BITSTREAM_H
#define PGF_BITSTREAM_H
 
#include "PGFtypes.h"
 
// constants
//static const WordWidth = 32;
//static const WordWidthLog = 5;
static const UINT32 Filled = 0xFFFFFFFF;
 
/// @brief Make 64 bit unsigned integer from two 32 bit unsigned integers
#define MAKEU64(a, b) ((UINT64) (((UINT32) (a)) | ((UINT64) ((UINT32) (b))) << 32))
 
// these procedures have to be inlined because of performance reasons
 
//////////////////////////////////////////////////////////////////////
/// Set one bit of a bit stream to 1
/// @param stream A bit stream stored in array of unsigned integers
/// @param pos A valid zero-based position in the bit stream
inline void SetBit(UINT32* stream, UINT32 pos) {
stream[pos >> WordWidthLog] |= (1 << (pos%WordWidth));
}
 
//////////////////////////////////////////////////////////////////////
/// Set one bit of a bit stream to 0
/// @param stream A bit stream stored in array of unsigned integers
/// @param pos A valid zero-based position in the bit stream
inline void ClearBit(UINT32* stream, UINT32 pos) {
stream[pos >> WordWidthLog] &= ~(1 << (pos%WordWidth));
}
 
//////////////////////////////////////////////////////////////////////
/// Return one bit of a bit stream
/// @param stream A bit stream stored in array of unsigned integers
/// @param pos A valid zero-based position in the bit stream
/// @return bit at position pos of bit stream stream
inline bool GetBit(UINT32* stream, UINT32 pos) {
return (stream[pos >> WordWidthLog] & (1 << (pos%WordWidth))) > 0;
 
}
 
//////////////////////////////////////////////////////////////////////
/// Compare k-bit binary representation of stream at position pos with val
/// @param stream A bit stream stored in array of unsigned integers
/// @param pos A valid zero-based position in the bit stream
/// @param k Number of bits to compare
/// @param val Value to compare with
/// @return true if equal
inline bool CompareBitBlock(UINT32* stream, UINT32 pos, UINT32 k, UINT32 val) {
const UINT32 iLoInt = pos >> WordWidthLog;
const UINT32 iHiInt = (pos + k - 1) >> WordWidthLog;
ASSERT(iLoInt <= iHiInt);
const UINT32 mask = (Filled >> (WordWidth - k));
 
if (iLoInt == iHiInt) {
// fits into one integer
val &= mask;
val <<= (pos%WordWidth);
return (stream[iLoInt] & val) == val;
} else {
// must be splitted over integer boundary
UINT64 v1 = MAKEU64(stream[iLoInt], stream[iHiInt]);
UINT64 v2 = UINT64(val & mask) << (pos%WordWidth);
return (v1 & v2) == v2;
}
}
 
//////////////////////////////////////////////////////////////////////
/// Store k-bit binary representation of val in stream at position pos
/// @param stream A bit stream stored in array of unsigned integers
/// @param pos A valid zero-based position in the bit stream
/// @param val Value to store in stream at position pos
/// @param k Number of bits of integer representation of val
inline void SetValueBlock(UINT32* stream, UINT32 pos, UINT32 val, UINT32 k) {
const UINT32 offset = pos%WordWidth;
const UINT32 iLoInt = pos >> WordWidthLog;
const UINT32 iHiInt = (pos + k - 1) >> WordWidthLog;
ASSERT(iLoInt <= iHiInt);
const UINT32 loMask = Filled << offset;
const UINT32 hiMask = Filled >> (WordWidth - 1 - ((pos + k - 1)%WordWidth));
 
if (iLoInt == iHiInt) {
// fits into one integer
stream[iLoInt] &= ~(loMask & hiMask); // clear bits
stream[iLoInt] |= val << offset; // write value
} else {
// must be splitted over integer boundary
stream[iLoInt] &= ~loMask; // clear bits
stream[iLoInt] |= val << offset; // write lower part of value
stream[iHiInt] &= ~hiMask; // clear bits
stream[iHiInt] |= val >> (WordWidth - offset); // write higher part of value
}
}
 
//////////////////////////////////////////////////////////////////////
/// Read k-bit number from stream at position pos
/// @param stream A bit stream stored in array of unsigned integers
/// @param pos A valid zero-based position in the bit stream
/// @param k Number of bits to read: 1 <= k <= 32
inline UINT32 GetValueBlock(UINT32* stream, UINT32 pos, UINT32 k) {
UINT32 count, hiCount;
const UINT32 iLoInt = pos >> WordWidthLog; // integer of first bit
const UINT32 iHiInt = (pos + k - 1) >> WordWidthLog; // integer of last bit
const UINT32 loMask = Filled << (pos%WordWidth);
const UINT32 hiMask = Filled >> (WordWidth - 1 - ((pos + k - 1)%WordWidth));
 
if (iLoInt == iHiInt) {
// inside integer boundary
count = stream[iLoInt] & (loMask & hiMask);
count >>= pos%WordWidth;
} else {
// overlapping integer boundary
count = stream[iLoInt] & loMask;
count >>= pos%WordWidth;
hiCount = stream[iHiInt] & hiMask;
hiCount <<= WordWidth - (pos%WordWidth);
count |= hiCount;
}
return count;
}
 
//////////////////////////////////////////////////////////////////////
/// Clear block of size at least len at position pos in stream
/// @param stream A bit stream stored in array of unsigned integers
/// @param pos A valid zero-based position in the bit stream
/// @param len Number of bits set to 0
inline void ClearBitBlock(UINT32* stream, UINT32 pos, UINT32 len) {
ASSERT(len > 0);
const UINT32 iFirstInt = pos >> WordWidthLog;
const UINT32 iLastInt = (pos + len - 1) >> WordWidthLog;
 
const UINT32 startMask = Filled << (pos%WordWidth);
// const UINT32 endMask=Filled>>(WordWidth-1-((pos+len-1)%WordWidth));
 
if (iFirstInt == iLastInt) {
stream[iFirstInt] &= ~(startMask /*& endMask*/);
} else {
stream[iFirstInt] &= ~startMask;
for (UINT32 i = iFirstInt + 1; i <= iLastInt; i++) { // changed <=
stream[i] = 0;
}
//stream[iLastInt] &= ~endMask;
}
}
 
//////////////////////////////////////////////////////////////////////
/// Set block of size at least len at position pos in stream
/// @param stream A bit stream stored in array of unsigned integers
/// @param pos A valid zero-based position in the bit stream
/// @param len Number of bits set to 1
inline void SetBitBlock(UINT32* stream, UINT32 pos, UINT32 len) {
ASSERT(len > 0);
 
const UINT32 iFirstInt = pos >> WordWidthLog;
const UINT32 iLastInt = (pos + len - 1) >> WordWidthLog;
 
const UINT32 startMask = Filled << (pos%WordWidth);
// const UINT32 endMask=Filled>>(WordWidth-1-((pos+len-1)%WordWidth));
 
if (iFirstInt == iLastInt) {
stream[iFirstInt] |= (startMask /*& endMask*/);
} else {
stream[iFirstInt] |= startMask;
for (UINT32 i = iFirstInt + 1; i <= iLastInt; i++) { // changed <=
stream[i] = Filled;
}
//stream[iLastInt] &= ~endMask;
}
}
 
//////////////////////////////////////////////////////////////////////
/// Returns the distance to the next 1 in stream at position pos.
/// If no 1 is found within len bits, then len is returned.
/// @param stream A bit stream stored in array of unsigned integers
/// @param pos A valid zero-based position in the bit stream
/// @param len size of search area (in bits)
/// return The distance to the next 1 in stream at position pos
inline UINT32 SeekBitRange(UINT32* stream, UINT32 pos, UINT32 len) {
UINT32 count = 0;
UINT32 testMask = 1 << (pos%WordWidth);
UINT32* word = stream + (pos >> WordWidthLog);
 
while (((*word & testMask) == 0) && (count < len)) {
count++;
testMask <<= 1;
if (!testMask) {
word++; testMask = 1;
 
// fast steps if all bits in a word are zero
while ((count + WordWidth <= len) && (*word == 0)) {
word++;
count += WordWidth;
}
}
}
 
return count;
}
 
//////////////////////////////////////////////////////////////////////
/// Returns the distance to the next 0 in stream at position pos.
/// If no 0 is found within len bits, then len is returned.
/// @param stream A bit stream stored in array of unsigned integers
/// @param pos A valid zero-based position in the bit stream
/// @param len size of search area (in bits)
/// return The distance to the next 0 in stream at position pos
inline UINT32 SeekBit1Range(UINT32* stream, UINT32 pos, UINT32 len) {
UINT32 count = 0;
UINT32 testMask = 1 << (pos%WordWidth);
UINT32* word = stream + (pos >> WordWidthLog);
 
while (((*word & testMask) != 0) && (count < len)) {
count++;
testMask <<= 1;
if (!testMask) {
word++; testMask = 1;
 
// fast steps if all bits in a word are one
while ((count + WordWidth <= len) && (*word == Filled)) {
word++;
count += WordWidth;
}
}
}
return count;
}
 
//////////////////////////////////////////////////////////////////////
/// Compute bit position of the next 32-bit word
/// @param pos current bit stream position
/// @return bit position of next 32-bit word
inline UINT32 AlignWordPos(UINT32 pos) {
// return ((pos + WordWidth - 1) >> WordWidthLog) << WordWidthLog;
return (pos + WordWidth - 1) & WordMask;
}
 
//////////////////////////////////////////////////////////////////////
/// Compute number of the 32-bit words
/// @param pos Current bit stream position
/// @return Number of 32-bit words
inline UINT32 NumberOfWords(UINT32 pos) {
return (pos + WordWidth - 1) >> WordWidthLog;
}
#endif //PGF_BITSTREAM_H
/*
* The Progressive Graphics File; http://www.libpgf.org
*
* $Date: 2006-06-04 22:05:59 +0200 (So, 04 Jun 2006) $
* $Revision: 229 $
*
* This file Copyright (C) 2006 xeraina GmbH, Switzerland
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
 
//////////////////////////////////////////////////////////////////////
/// @file Bitstream.h
/// @brief PGF bit-stream operations
/// @author C. Stamm
 
#ifndef PGF_BITSTREAM_H
#define PGF_BITSTREAM_H
 
#include "PGFtypes.h"
 
// constants
//static const WordWidth = 32;
//static const WordWidthLog = 5;
static const UINT32 Filled = 0xFFFFFFFF;
 
/// @brief Make 64 bit unsigned integer from two 32 bit unsigned integers
#define MAKEU64(a, b) ((UINT64) (((UINT32) (a)) | ((UINT64) ((UINT32) (b))) << 32))
// these procedures have to be inlined because of performance reasons
 
//////////////////////////////////////////////////////////////////////
/// Set one bit of a bit stream to 1
/// @param stream A bit stream stored in array of unsigned integers
/// @param pos A valid zero-based position in the bit stream
inline void SetBit(UINT32* stream, UINT32 pos) {
stream[pos >> WordWidthLog] |= (1 << (pos%WordWidth));
}
 
//////////////////////////////////////////////////////////////////////
/// Set one bit of a bit stream to 0
/// @param stream A bit stream stored in array of unsigned integers
/// @param pos A valid zero-based position in the bit stream
inline void ClearBit(UINT32* stream, UINT32 pos) {
stream[pos >> WordWidthLog] &= ~(1 << (pos%WordWidth));
}
 
//////////////////////////////////////////////////////////////////////
/// Return one bit of a bit stream
/// @param stream A bit stream stored in array of unsigned integers
/// @param pos A valid zero-based position in the bit stream
/// @return bit at position pos of bit stream stream
inline bool GetBit(UINT32* stream, UINT32 pos) {
return (stream[pos >> WordWidthLog] & (1 << (pos%WordWidth))) > 0;
 
}
 
//////////////////////////////////////////////////////////////////////
/// Compare k-bit binary representation of stream at position pos with val
/// @param stream A bit stream stored in array of unsigned integers
/// @param pos A valid zero-based position in the bit stream
/// @param k Number of bits to compare
/// @param val Value to compare with
/// @return true if equal
inline bool CompareBitBlock(UINT32* stream, UINT32 pos, UINT32 k, UINT32 val) {
const UINT32 iLoInt = pos >> WordWidthLog;
const UINT32 iHiInt = (pos + k - 1) >> WordWidthLog;
ASSERT(iLoInt <= iHiInt);
const UINT32 mask = (Filled >> (WordWidth - k));
 
if (iLoInt == iHiInt) {
// fits into one integer
val &= mask;
val <<= (pos%WordWidth);
return (stream[iLoInt] & val) == val;
} else {
// must be splitted over integer boundary
UINT64 v1 = MAKEU64(stream[iLoInt], stream[iHiInt]);
UINT64 v2 = UINT64(val & mask) << (pos%WordWidth);
return (v1 & v2) == v2;
}
}
 
//////////////////////////////////////////////////////////////////////
/// Store k-bit binary representation of val in stream at position pos
/// @param stream A bit stream stored in array of unsigned integers
/// @param pos A valid zero-based position in the bit stream
/// @param val Value to store in stream at position pos
/// @param k Number of bits of integer representation of val
inline void SetValueBlock(UINT32* stream, UINT32 pos, UINT32 val, UINT32 k) {
const UINT32 offset = pos%WordWidth;
const UINT32 iLoInt = pos >> WordWidthLog;
const UINT32 iHiInt = (pos + k - 1) >> WordWidthLog;
ASSERT(iLoInt <= iHiInt);
const UINT32 loMask = Filled << offset;
const UINT32 hiMask = Filled >> (WordWidth - 1 - ((pos + k - 1)%WordWidth));
 
if (iLoInt == iHiInt) {
// fits into one integer
stream[iLoInt] &= ~(loMask & hiMask); // clear bits
stream[iLoInt] |= val << offset; // write value
} else {
// must be splitted over integer boundary
stream[iLoInt] &= ~loMask; // clear bits
stream[iLoInt] |= val << offset; // write lower part of value
stream[iHiInt] &= ~hiMask; // clear bits
stream[iHiInt] |= val >> (WordWidth - offset); // write higher part of value
}
}
 
//////////////////////////////////////////////////////////////////////
/// Read k-bit number from stream at position pos
/// @param stream A bit stream stored in array of unsigned integers
/// @param pos A valid zero-based position in the bit stream
/// @param k Number of bits to read: 1 <= k <= 32
inline UINT32 GetValueBlock(UINT32* stream, UINT32 pos, UINT32 k) {
UINT32 count, hiCount;
const UINT32 iLoInt = pos >> WordWidthLog; // integer of first bit
const UINT32 iHiInt = (pos + k - 1) >> WordWidthLog; // integer of last bit
const UINT32 loMask = Filled << (pos%WordWidth);
const UINT32 hiMask = Filled >> (WordWidth - 1 - ((pos + k - 1)%WordWidth));
if (iLoInt == iHiInt) {
// inside integer boundary
count = stream[iLoInt] & (loMask & hiMask);
count >>= pos%WordWidth;
} else {
// overlapping integer boundary
count = stream[iLoInt] & loMask;
count >>= pos%WordWidth;
hiCount = stream[iHiInt] & hiMask;
hiCount <<= WordWidth - (pos%WordWidth);
count |= hiCount;
}
return count;
}
 
//////////////////////////////////////////////////////////////////////
/// Clear block of size at least len at position pos in stream
/// @param stream A bit stream stored in array of unsigned integers
/// @param pos A valid zero-based position in the bit stream
/// @param len Number of bits set to 0
inline void ClearBitBlock(UINT32* stream, UINT32 pos, UINT32 len) {
ASSERT(len > 0);
const UINT32 iFirstInt = pos >> WordWidthLog;
const UINT32 iLastInt = (pos + len - 1) >> WordWidthLog;
 
const UINT32 startMask = Filled << (pos%WordWidth);
// const UINT32 endMask=Filled>>(WordWidth-1-((pos+len-1)%WordWidth));
 
if (iFirstInt == iLastInt) {
stream[iFirstInt] &= ~(startMask /*& endMask*/);
} else {
stream[iFirstInt] &= ~startMask;
for (UINT32 i = iFirstInt + 1; i <= iLastInt; i++) { // changed <=
stream[i] = 0;
}
//stream[iLastInt] &= ~endMask;
}
}
 
//////////////////////////////////////////////////////////////////////
/// Set block of size at least len at position pos in stream
/// @param stream A bit stream stored in array of unsigned integers
/// @param pos A valid zero-based position in the bit stream
/// @param len Number of bits set to 1
inline void SetBitBlock(UINT32* stream, UINT32 pos, UINT32 len) {
ASSERT(len > 0);
 
const UINT32 iFirstInt = pos >> WordWidthLog;
const UINT32 iLastInt = (pos + len - 1) >> WordWidthLog;
 
const UINT32 startMask = Filled << (pos%WordWidth);
// const UINT32 endMask=Filled>>(WordWidth-1-((pos+len-1)%WordWidth));
 
if (iFirstInt == iLastInt) {
stream[iFirstInt] |= (startMask /*& endMask*/);
} else {
stream[iFirstInt] |= startMask;
for (UINT32 i = iFirstInt + 1; i <= iLastInt; i++) { // changed <=
stream[i] = Filled;
}
//stream[iLastInt] &= ~endMask;
}
}
 
//////////////////////////////////////////////////////////////////////
/// Returns the distance to the next 1 in stream at position pos.
/// If no 1 is found within len bits, then len is returned.
/// @param stream A bit stream stored in array of unsigned integers
/// @param pos A valid zero-based position in the bit stream
/// @param len size of search area (in bits)
/// return The distance to the next 1 in stream at position pos
inline UINT32 SeekBitRange(UINT32* stream, UINT32 pos, UINT32 len) {
UINT32 count = 0;
UINT32 testMask = 1 << (pos%WordWidth);
UINT32* word = stream + (pos >> WordWidthLog);
 
while (((*word & testMask) == 0) && (count < len)) {
count++;
testMask <<= 1;
if (!testMask) {
word++; testMask = 1;
 
// fast steps if all bits in a word are zero
while ((count + WordWidth <= len) && (*word == 0)) {
word++;
count += WordWidth;
}
}
}
 
return count;
}
 
//////////////////////////////////////////////////////////////////////
/// Returns the distance to the next 0 in stream at position pos.
/// If no 0 is found within len bits, then len is returned.
/// @param stream A bit stream stored in array of unsigned integers
/// @param pos A valid zero-based position in the bit stream
/// @param len size of search area (in bits)
/// return The distance to the next 0 in stream at position pos
inline UINT32 SeekBit1Range(UINT32* stream, UINT32 pos, UINT32 len) {
UINT32 count = 0;
UINT32 testMask = 1 << (pos%WordWidth);
UINT32* word = stream + (pos >> WordWidthLog);
 
while (((*word & testMask) != 0) && (count < len)) {
count++;
testMask <<= 1;
if (!testMask) {
word++; testMask = 1;
 
// fast steps if all bits in a word are one
while ((count + WordWidth <= len) && (*word == Filled)) {
word++;
count += WordWidth;
}
}
}
return count;
}
 
//////////////////////////////////////////////////////////////////////
/// Compute bit position of the next 32-bit word
/// @param pos current bit stream position
/// @return bit position of next 32-bit word
inline UINT32 AlignWordPos(UINT32 pos) {
// return ((pos + WordWidth - 1) >> WordWidthLog) << WordWidthLog;
return DWWIDTHBITS(pos);
}
 
//////////////////////////////////////////////////////////////////////
/// Compute number of the 32-bit words
/// @param pos Current bit stream position
/// @return Number of 32-bit words
inline UINT32 NumberOfWords(UINT32 pos) {
return (pos + WordWidth - 1) >> WordWidthLog;
}
#endif //PGF_BITSTREAM_H
/trunk/Scribus/scribus/third_party/pgf/Decoder.cpp
1,990 → 1,1009
/*
* The Progressive Graphics File; http://www.libpgf.org
*
* $Date: 2006-06-04 22:05:59 +0200 (So, 04 Jun 2006) $
* $Revision: 229 $
*
* This file Copyright (C) 2006 xeraina GmbH, Switzerland
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
 
//////////////////////////////////////////////////////////////////////
/// @file Decoder.cpp
/// @brief PGF decoder class implementation
/// @author C. Stamm, R. Spuler
 
#include "Decoder.h"
 
#ifdef TRACE
#include <stdio.h>
#endif
 
//////////////////////////////////////////////////////
// PGF: file structure
//
// PGFPreHeader PGFHeader PGFPostHeader LevelLengths Level_n-1 Level_n-2 ... Level_0
// PGFPostHeader ::= [ColorTable] [UserData]
// LevelLengths ::= UINT32[nLevels]
 
//////////////////////////////////////////////////////
// Decoding scheme
// input: binary file
// output: wavelet coefficients stored in subbands
//
// file (for each buffer: packedLength (16 bit), packed bits)
// |
// m_codeBuffer (for each plane: RLcodeLength (16 bit), RLcoded sigBits + m_sign, refBits)
// | | |
// m_sign sigBits refBits [BufferLen, BufferLen, BufferLen]
// | | |
// m_value [BufferSize]
// |
// subband
//
 
// Constants
#define CodeBufferBitLen (BufferSize*WordWidth) // max number of bits in m_codeBuffer
 
/////////////////////////////////////////////////////////////////////
// Constructor
// Read pre-header, header, and levelLength
// It might throw an IOException.
CDecoder::CDecoder(CPGFStream* stream, PGFPreHeader& preHeader, PGFHeader& header, PGFPostHeader& postHeader, UINT32*& levelLength, bool useOMP /*= true*/) THROW_
: m_stream(stream)
, m_startPos(0)
, m_streamSizeEstimation(0)
, m_encodedHeaderLength(0)
, m_currentBlockIndex(0)
, m_macroBlocksAvailable(0)
#ifdef __PGFROISUPPORT__
, m_roi(false)
#endif
{
ASSERT(m_stream);
 
int count, expected;
 
// set number of threads
#ifdef LIBPGF_USE_OPENMP
m_macroBlockLen = omp_get_num_procs();
#else
m_macroBlockLen = 1;
#endif
 
if (useOMP && m_macroBlockLen > 1) {
#ifdef LIBPGF_USE_OPENMP
omp_set_num_threads(m_macroBlockLen);
#endif
 
// create macro block array
m_macroBlocks = new CMacroBlock*[m_macroBlockLen];
for (int i=0; i < m_macroBlockLen; i++) m_macroBlocks[i] = new CMacroBlock(this);
} else {
m_macroBlocks = 0;
m_currentBlock = new CMacroBlock(this);
}
 
// store current stream position
m_startPos = m_stream->GetPos();
 
// read magic and version
count = expected = MagicVersionSize;
m_stream->Read(&count, &preHeader);
if (count != expected) ReturnWithError(MissingData);
 
// read header size
if (preHeader.version & Version6) {
// 32 bit header size since version 6
count = expected = 4;
} else {
count = expected = 2;
}
m_stream->Read(&count, ((UINT8*)&preHeader) + MagicVersionSize);
if (count != expected) ReturnWithError(MissingData);
 
// make sure the values are correct read
preHeader.hSize = __VAL(preHeader.hSize);
 
// check magic number
if (memcmp(preHeader.magic, Magic, 3) != 0) {
// error condition: wrong Magic number
ReturnWithError(FormatCannotRead);
}
 
// read file header
count = expected = (preHeader.hSize < HeaderSize) ? preHeader.hSize : HeaderSize;
m_stream->Read(&count, &header);
if (count != expected) ReturnWithError(MissingData);
 
// make sure the values are correct read
header.height = __VAL(UINT32(header.height));
header.width = __VAL(UINT32(header.width));
 
// be ready to read all versions including version 0
if (preHeader.version > 0) {
#ifndef __PGFROISUPPORT__
// check ROI usage
if (preHeader.version & PGFROI) ReturnWithError(FormatCannotRead);
#endif
 
int size = preHeader.hSize - HeaderSize;
 
if (size > 0) {
// read post header
if (header.mode == ImageModeIndexedColor) {
ASSERT(size >= ColorTableSize);
// read color table
count = expected = ColorTableSize;
m_stream->Read(&count, postHeader.clut);
if (count != expected) ReturnWithError(MissingData);
size -= count;
}
 
if (size > 0) {
// create user data memory block
postHeader.userDataLen = size;
postHeader.userData = new UINT8[postHeader.userDataLen];
 
// read user data
count = expected = postHeader.userDataLen;
m_stream->Read(&count, postHeader.userData);
if (count != expected) ReturnWithError(MissingData);
}
}
 
// create levelLength
levelLength = new UINT32[header.nLevels];
if (!levelLength) ReturnWithError(InsufficientMemory);
 
// read levelLength
count = expected = header.nLevels*WordBytes;
m_stream->Read(&count, levelLength);
if (count != expected) ReturnWithError(MissingData);
 
#ifdef PGF_USE_BIG_ENDIAN
// make sure the values are correct read
for (int i=0; i < header.nLevels; i++) {
levelLength[i] = __VAL(levelLength[i]);
}
#endif
 
// compute the total size in bytes; keep attention: level length information is optional
for (int i=0; i < header.nLevels; i++) {
m_streamSizeEstimation += levelLength[i];
}
 
}
 
// store current stream position
m_encodedHeaderLength = UINT32(m_stream->GetPos() - m_startPos);
}
 
/////////////////////////////////////////////////////////////////////
// Destructor
CDecoder::~CDecoder() {
if (m_macroBlocks) {
for (int i=0; i < m_macroBlockLen; i++) delete m_macroBlocks[i];
delete[] m_macroBlocks;
} else {
delete m_currentBlock;
}
}
 
//////////////////////////////////////////////////////////////////////
/// Copies data from the open stream to a target buffer.
/// It might throw an IOException.
/// @param target The target buffer
/// @param len The number of bytes to read
/// @return The number of bytes copied to the target buffer
UINT32 CDecoder::ReadEncodedData(UINT8* target, UINT32 len) const THROW_ {
ASSERT(m_stream);
 
int count = len;
m_stream->Read(&count, target);
 
return count;
}
 
/////////////////////////////////////////////////////////////////////
/// Unpartitions a rectangular region of a given subband.
/// Partitioning scheme: The plane is partitioned in squares of side length LinBlockSize.
/// Write wavelet coefficients into buffer.
/// It might throw an IOException.
/// @param band A subband
/// @param quantParam Dequantization value
/// @param width The width of the rectangle
/// @param height The height of the rectangle
/// @param startPos The buffer position of the top left corner of the rectangular region
/// @param pitch The number of bytes in row of the subband
void CDecoder::Partition(CSubband* band, int quantParam, int width, int height, int startPos, int pitch) THROW_ {
ASSERT(band);
 
const div_t ww = div(width, LinBlockSize);
const div_t hh = div(height, LinBlockSize);
const int ws = pitch - LinBlockSize;
const int wr = pitch - ww.rem;
int pos, base = startPos, base2;
 
// main height
for (int i=0; i < hh.quot; i++) {
// main width
base2 = base;
for (int j=0; j < ww.quot; j++) {
pos = base2;
for (int y=0; y < LinBlockSize; y++) {
for (int x=0; x < LinBlockSize; x++) {
DequantizeValue(band, pos, quantParam);
pos++;
}
pos += ws;
}
base2 += LinBlockSize;
}
// rest of width
pos = base2;
for (int y=0; y < LinBlockSize; y++) {
for (int x=0; x < ww.rem; x++) {
DequantizeValue(band, pos, quantParam);
pos++;
}
pos += wr;
base += pitch;
}
}
// main width
base2 = base;
for (int j=0; j < ww.quot; j++) {
// rest of height
pos = base2;
for (int y=0; y < hh.rem; y++) {
for (int x=0; x < LinBlockSize; x++) {
DequantizeValue(band, pos, quantParam);
pos++;
}
pos += ws;
}
base2 += LinBlockSize;
}
// rest of height
pos = base2;
for (int y=0; y < hh.rem; y++) {
// rest of width
for (int x=0; x < ww.rem; x++) {
DequantizeValue(band, pos, quantParam);
pos++;
}
pos += wr;
}
}
 
////////////////////////////////////////////////////////////////////
// Decode and dequantize HL, and LH band of one level
// LH and HH are interleaved in the codestream and must be split
// Deccoding and dequantization of HL and LH Band (interleaved) using partitioning scheme
// partitions the plane in squares of side length InterBlockSize
// It might throw an IOException.
void CDecoder::DecodeInterleaved(CWaveletTransform* wtChannel, int level, int quantParam) THROW_ {
CSubband* hlBand = wtChannel->GetSubband(level, HL);
CSubband* lhBand = wtChannel->GetSubband(level, LH);
const div_t lhH = div(lhBand->GetHeight(), InterBlockSize);
const div_t hlW = div(hlBand->GetWidth(), InterBlockSize);
const int hlws = hlBand->GetWidth() - InterBlockSize;
const int hlwr = hlBand->GetWidth() - hlW.rem;
const int lhws = lhBand->GetWidth() - InterBlockSize;
const int lhwr = lhBand->GetWidth() - hlW.rem;
int hlPos, lhPos;
int hlBase = 0, lhBase = 0, hlBase2, lhBase2;
 
ASSERT(lhBand->GetWidth() >= hlBand->GetWidth());
ASSERT(hlBand->GetHeight() >= lhBand->GetHeight());
 
hlBand->AllocMemory();
lhBand->AllocMemory();
 
// correct quantParam with normalization factor
quantParam -= level;
if (quantParam < 0) quantParam = 0;
 
// main height
for (int i=0; i < lhH.quot; i++) {
// main width
hlBase2 = hlBase;
lhBase2 = lhBase;
for (int j=0; j < hlW.quot; j++) {
hlPos = hlBase2;
lhPos = lhBase2;
for (int y=0; y < InterBlockSize; y++) {
for (int x=0; x < InterBlockSize; x++) {
DequantizeValue(hlBand, hlPos, quantParam);
DequantizeValue(lhBand, lhPos, quantParam);
hlPos++;
lhPos++;
}
hlPos += hlws;
lhPos += lhws;
}
hlBase2 += InterBlockSize;
lhBase2 += InterBlockSize;
}
// rest of width
hlPos = hlBase2;
lhPos = lhBase2;
for (int y=0; y < InterBlockSize; y++) {
for (int x=0; x < hlW.rem; x++) {
DequantizeValue(hlBand, hlPos, quantParam);
DequantizeValue(lhBand, lhPos, quantParam);
hlPos++;
lhPos++;
}
// width difference between HL and LH
if (lhBand->GetWidth() > hlBand->GetWidth()) {
DequantizeValue(lhBand, lhPos, quantParam);
}
hlPos += hlwr;
lhPos += lhwr;
hlBase += hlBand->GetWidth();
lhBase += lhBand->GetWidth();
}
}
// main width
hlBase2 = hlBase;
lhBase2 = lhBase;
for (int j=0; j < hlW.quot; j++) {
// rest of height
hlPos = hlBase2;
lhPos = lhBase2;
for (int y=0; y < lhH.rem; y++) {
for (int x=0; x < InterBlockSize; x++) {
DequantizeValue(hlBand, hlPos, quantParam);
DequantizeValue(lhBand, lhPos, quantParam);
hlPos++;
lhPos++;
}
hlPos += hlws;
lhPos += lhws;
}
hlBase2 += InterBlockSize;
lhBase2 += InterBlockSize;
}
// rest of height
hlPos = hlBase2;
lhPos = lhBase2;
for (int y=0; y < lhH.rem; y++) {
// rest of width
for (int x=0; x < hlW.rem; x++) {
DequantizeValue(hlBand, hlPos, quantParam);
DequantizeValue(lhBand, lhPos, quantParam);
hlPos++;
lhPos++;
}
// width difference between HL and LH
if (lhBand->GetWidth() > hlBand->GetWidth()) {
DequantizeValue(lhBand, lhPos, quantParam);
}
hlPos += hlwr;
lhPos += lhwr;
hlBase += hlBand->GetWidth();
}
// height difference between HL and LH
if (hlBand->GetHeight() > lhBand->GetHeight()) {
// total width
hlPos = hlBase;
for (int j=0; j < hlBand->GetWidth(); j++) {
DequantizeValue(hlBand, hlPos, quantParam);
hlPos++;
}
}
}
 
////////////////////////////////////////////////////////////////////
/// Skip a given number of bytes in the open stream.
/// It might throw an IOException.
void CDecoder::Skip(UINT64 offset) THROW_ {
m_stream->SetPos(FSFromCurrent, offset);
}
 
//////////////////////////////////////////////////////////////////////
/// Dequantization of a single value at given position in subband.
/// If encoded data is available, then stores dequantized band value into
/// buffer m_value at position m_valuePos.
/// Otherwise reads encoded data buffer and decodes it.
/// @param band A subband
/// @param bandPos A valid position in subband band
/// @param quantParam The quantization parameter
void CDecoder::DequantizeValue(CSubband* band, UINT32 bandPos, int quantParam) {
if (!m_macroBlocksAvailable) {
DecodeBuffer();
ASSERT(m_currentBlock);
ASSERT(m_currentBlock->m_valuePos == 0);
ASSERT(m_macroBlocksAvailable);
}
band->SetData(bandPos, m_currentBlock->m_value[m_currentBlock->m_valuePos] << quantParam);
m_currentBlock->m_valuePos++;
if (m_currentBlock->m_valuePos == BufferSize) {
// current block has been read
m_macroBlocksAvailable--;
if (m_macroBlocksAvailable)
m_currentBlock = m_macroBlocks[++m_currentBlockIndex];
}
}
 
//////////////////////////////////////////////////////////////////////
// Read next block from stream and store it in the given block
// It might throw an IOException.
void CDecoder::ReadMacroBlock(CMacroBlock* block) THROW_ {
ASSERT(block);
 
UINT16 wordLen;
ROIBlockHeader h(BufferSize);
int count, expected;
 
#ifdef TRACE
//UINT32 filePos = (UINT32)m_stream->GetPos();
//printf("DecodeBuffer: %d\n", filePos);
#endif
 
// read wordLen
count = expected = sizeof(UINT16);
m_stream->Read(&count, &wordLen);
if (count != expected) ReturnWithError(MissingData);
wordLen = __VAL(wordLen);
if (wordLen > BufferSize)
ReturnWithError(FormatCannotRead);
 
#ifdef __PGFROISUPPORT__
// read ROIBlockHeader
if (m_roi) {
m_stream->Read(&count, &h.val);
if (count != expected) ReturnWithError(MissingData);
 
// convert ROIBlockHeader
h.val = __VAL(h.val);
}
#endif
// save header
block->m_header = h;
 
// read data
count = expected = wordLen*WordBytes;
m_stream->Read(&count, block->m_codeBuffer);
if (count != expected) ReturnWithError(MissingData);
 
#ifdef PGF_USE_BIG_ENDIAN
// convert data
count /= WordBytes;
for (int i=0; i < count; i++) {
block->m_codeBuffer[i] = __VAL(block->m_codeBuffer[i]);
}
#endif
 
#ifdef __PGFROISUPPORT__
ASSERT(m_roi && h.rbh.bufferSize <= BufferSize || h.rbh.bufferSize == BufferSize);
#else
ASSERT(h.rbh.bufferSize == BufferSize);
#endif
}
 
//////////////////////////////////////////////////////////////////////
// Read next block from stream but don't decode into macro block
// Encoding scheme: <wordLen>(16 bits) [ ROI ] data
// ROI ::= <bufferSize>(15 bits) <eofTile>(1 bit)
// It might throw an IOException.
void CDecoder::SkipTileBuffer() THROW_ {
// check if pre-decoded data is available
if (m_macroBlocksAvailable) {
// current block is not used
m_macroBlocksAvailable--;
if (m_macroBlocksAvailable)
m_currentBlock = m_macroBlocks[++m_currentBlockIndex];
return;
}
 
UINT16 wordLen;
int count, expected;
 
// read wordLen
count = expected = sizeof(wordLen);
m_stream->Read(&count, &wordLen);
if (count != expected) ReturnWithError(MissingData);
wordLen = __VAL(wordLen);
ASSERT(wordLen <= BufferSize);
 
#ifdef __PGFROISUPPORT__
if (m_roi) {
// skip ROIBlockHeader
m_stream->SetPos(FSFromCurrent, sizeof(ROIBlockHeader));
}
#endif
 
// skip data
m_stream->SetPos(FSFromCurrent, wordLen*WordBytes);
}
 
//////////////////////////////////////////////////////////////////////
// Read next block from stream and decode into macro block
// It might throw an IOException.
void CDecoder::DecodeTileBuffer() THROW_ {
if (m_macroBlocksAvailable) {
// current block has been read
m_macroBlocksAvailable--;
if (m_macroBlocksAvailable)
m_currentBlock = m_macroBlocks[++m_currentBlockIndex];
} else {
DecodeBuffer();
ASSERT(m_currentBlock);
ASSERT(m_currentBlock->m_valuePos == 0);
ASSERT(m_macroBlocksAvailable);
}
}
 
//////////////////////////////////////////////////////////////////////
// Read next block from stream and decode into macro block
// Decoding scheme: <wordLen>(16 bits) [ ROI ] data
// ROI ::= <bufferSize>(15 bits) <eofTile>(1 bit)
// It might throw an IOException.
void CDecoder::DecodeBuffer() THROW_ {
ASSERT(m_macroBlocksAvailable == 0);
 
// macro block management
if (m_macroBlockLen == 1) {
ASSERT(m_currentBlock);
ReadMacroBlock(m_currentBlock);
m_currentBlock->BitplaneDecode();
m_macroBlocksAvailable = 1;
} else {
for (int i=0; i < m_macroBlockLen; i++) {
// read sequentially several blocks
try {
ReadMacroBlock(m_macroBlocks[i]);
m_macroBlocksAvailable++;
} catch(const IOException &ex) {
if (ex.error == MissingData) {
break; // no further levels available
} else {
throw;
}
}
}
 
// decode in parallel
#pragma omp parallel for default(shared) //no declared exceptions in next block
for (int i=0; i < m_macroBlocksAvailable; i++) {
m_macroBlocks[i]->BitplaneDecode();
}
 
m_currentBlockIndex = 0;
m_currentBlock = m_macroBlocks[m_currentBlockIndex];
}
}
 
//////////////////////////////////////////////////////////////////////
// Decode block into buffer of given size using bit plane coding.
// A buffer contains bufferLen UINT32 values, thus, bufferSize bits per bit plane.
// Following coding scheme is used:
// Buffer ::= <nPlanes>(5 bits) foreach(plane i): Plane[i]
// Plane[i] ::= [ Sig1 | Sig2 ] [DWORD alignment] refBits
// Sig1 ::= 1 <codeLen>(15 bits) codedSigAndSignBits
// Sig2 ::= 0 <sigLen>(15 bits) [Sign1 | Sign2 ] sigBits
// Sign1 ::= 1 <codeLen>(15 bits) [DWORD alignment] codedSignBits
// Sign2 ::= 0 <signLen>(15 bits) [DWORD alignment] signBits
void CDecoder::CMacroBlock::BitplaneDecode() {
UINT32 bufferSize = m_header.rbh.bufferSize; ASSERT(bufferSize <= BufferSize);
 
UINT32 nPlanes;
UINT32 codePos = 0, codeLen, sigLen, sigPos, signLen, signPos;
DataT planeMask;
 
// clear significance vector
for (UINT32 k=0; k < bufferSize; k++) {
m_sigFlagVector[k] = false;
}
m_sigFlagVector[bufferSize] = true; // sentinel
 
// clear output buffer
for (UINT32 k=0; k < BufferSize; k++) {
m_value[k] = 0;
}
 
// read number of bit planes
nPlanes = GetValueBlock(m_codeBuffer, 0, MaxBitPlanesLog);
codePos += MaxBitPlanesLog;
 
// loop through all bit planes
if (nPlanes == 0) nPlanes = MaxBitPlanes + 1;
ASSERT(0 < nPlanes && nPlanes <= MaxBitPlanes + 1);
planeMask = 1 << (nPlanes - 1);
 
for (int plane = nPlanes - 1; plane >= 0; plane--) {
// read RL code
if (GetBit(m_codeBuffer, codePos)) {
// RL coding of sigBits is used
codePos++;
 
// read codeLen
codeLen = GetValueBlock(m_codeBuffer, codePos, RLblockSizeLen); ASSERT(codeLen < (1 << RLblockSizeLen));
 
// position of encoded sigBits and signBits
sigPos = codePos + RLblockSizeLen; ASSERT(sigPos < CodeBufferBitLen);
 
// refinement bits
codePos = AlignWordPos(sigPos + codeLen); ASSERT(codePos < CodeBufferBitLen);
 
// run-length decode significant bits and signs from m_codeBuffer and
// read refinement bits from m_codeBuffer and compose bit plane
sigLen = ComposeBitplaneRLD(bufferSize, planeMask, sigPos, &m_codeBuffer[codePos >> WordWidthLog]);
 
} else {
// no RL coding is used
codePos++;
 
// read sigLen
sigLen = GetValueBlock(m_codeBuffer, codePos, RLblockSizeLen); ASSERT(sigLen <= BufferSize);
codePos += RLblockSizeLen; ASSERT(codePos < CodeBufferBitLen);
 
// read RL code for signBits
if (GetBit(m_codeBuffer, codePos)) {
// RL coding is used
codePos++;
 
// read codeLen
codeLen = GetValueBlock(m_codeBuffer, codePos, RLblockSizeLen);
 
// sign bits
signPos = codePos + RLblockSizeLen; ASSERT(signPos < CodeBufferBitLen);
 
// significant bits
sigPos = AlignWordPos(signPos + codeLen); ASSERT(sigPos < CodeBufferBitLen);
 
// refinement bits
codePos = AlignWordPos(sigPos + sigLen); ASSERT(codePos < CodeBufferBitLen);
 
// read significant and refinement bitset from m_codeBuffer
sigLen = ComposeBitplaneRLD(bufferSize, planeMask, &m_codeBuffer[sigPos >> WordWidthLog], &m_codeBuffer[codePos >> WordWidthLog], &m_codeBuffer[signPos >> WordWidthLog]);
 
} else {
// RL coding of signBits was not efficient and therefore not used
codePos++;
 
// read signLen
signLen = AlignWordPos(GetValueBlock(m_codeBuffer, codePos, RLblockSizeLen)); ASSERT(signLen <= bufferSize);
 
// sign bits
signPos = AlignWordPos(codePos + RLblockSizeLen); ASSERT(signPos < CodeBufferBitLen);
 
// significant bits
sigPos = signPos + signLen; ASSERT(sigPos < CodeBufferBitLen);
 
// refinement bits
codePos = AlignWordPos(sigPos + sigLen); ASSERT(codePos < CodeBufferBitLen);
 
// read significant and refinement bitset from m_codeBuffer
sigLen = ComposeBitplane(bufferSize, planeMask, &m_codeBuffer[sigPos >> WordWidthLog], &m_codeBuffer[codePos >> WordWidthLog], &m_codeBuffer[signPos >> WordWidthLog]);
}
}
 
// start of next chunk
codePos = AlignWordPos(codePos + bufferSize - sigLen); ASSERT(codePos < CodeBufferBitLen);
 
// next plane
planeMask >>= 1;
}
 
m_valuePos = 0;
}
 
////////////////////////////////////////////////////////////////////
// Reconstruct bitplane from significant bitset and refinement bitset
// returns length [bits] of sigBits
// input: sigBits, refBits, signBits
// output: m_value
UINT32 CDecoder::CMacroBlock::ComposeBitplane(UINT32 bufferSize, DataT planeMask, UINT32* sigBits, UINT32* refBits, UINT32* signBits) {
ASSERT(sigBits);
ASSERT(refBits);
ASSERT(signBits);
 
UINT32 valPos = 0, signPos = 0, refPos = 0;
UINT32 sigPos = 0, sigEnd;
UINT32 zerocnt;
 
while (valPos < bufferSize) {
// search next 1 in m_sigFlagVector using searching with sentinel
sigEnd = valPos;
while(!m_sigFlagVector[sigEnd]) { sigEnd++; }
sigEnd -= valPos;
sigEnd += sigPos;
 
// search 1's in sigBits[sigPos..sigEnd)
// these 1's are significant bits
while (sigPos < sigEnd) {
// search 0's
zerocnt = SeekBitRange(sigBits, sigPos, sigEnd - sigPos);
sigPos += zerocnt;
valPos += zerocnt;
if (sigPos < sigEnd) {
// write bit to m_value
SetBitAtPos(valPos, planeMask);
 
// copy sign bit
SetSign(valPos, GetBit(signBits, signPos++));
 
// update significance flag vector
m_sigFlagVector[valPos++] = true;
sigPos++;
}
}
// refinement bit
if (valPos < bufferSize) {
// write one refinement bit
if (GetBit(refBits, refPos)) {
SetBitAtPos(valPos, planeMask);
}
refPos++;
valPos++;
}
}
ASSERT(sigPos <= bufferSize);
ASSERT(refPos <= bufferSize);
ASSERT(signPos <= bufferSize);
ASSERT(valPos == bufferSize);
 
return sigPos;
}
 
////////////////////////////////////////////////////////////////////
// Reconstruct bitplane from significant bitset and refinement bitset
// returns length [bits] of decoded significant bits
// input: RL encoded sigBits and signBits in m_codeBuffer, refBits
// output: m_value
// RLE:
// - Decode run of 2^k zeros by a single 0.
// - Decode run of count 0's followed by a 1 with codeword: 1<count>x
// - x is 0: if a positive sign has been stored, otherwise 1
// - Read each bit from m_codeBuffer[codePos] and increment codePos.
UINT32 CDecoder::CMacroBlock::ComposeBitplaneRLD(UINT32 bufferSize, DataT planeMask, UINT32 codePos, UINT32* refBits) {
ASSERT(refBits);
 
UINT32 valPos = 0, refPos = 0;
UINT32 sigPos = 0, sigEnd;
UINT32 k = 3;
UINT32 runlen = 1 << k; // = 2^k
UINT32 count = 0, rest = 0;
bool set1 = false;
 
while (valPos < bufferSize) {
// search next 1 in m_sigFlagVector using searching with sentinel
sigEnd = valPos;
while(!m_sigFlagVector[sigEnd]) { sigEnd++; }
sigEnd -= valPos;
sigEnd += sigPos;
 
while (sigPos < sigEnd) {
if (rest || set1) {
// rest of last run
sigPos += rest;
valPos += rest;
rest = 0;
} else {
// decode significant bits
if (GetBit(m_codeBuffer, codePos++)) {
// extract counter and generate zero run of length count
if (k > 0) {
// extract counter
count = GetValueBlock(m_codeBuffer, codePos, k);
codePos += k;
if (count > 0) {
sigPos += count;
valPos += count;
}
 
// adapt k (half run-length interval)
k--;
runlen >>= 1;
}
 
set1 = true;
 
} else {
// generate zero run of length 2^k
sigPos += runlen;
valPos += runlen;
 
// adapt k (double run-length interval)
if (k < WordWidth) {
k++;
runlen <<= 1;
}
}
}
 
if (sigPos < sigEnd) {
if (set1) {
set1 = false;
 
// write 1 bit
SetBitAtPos(valPos, planeMask);
 
// set sign bit
SetSign(valPos, GetBit(m_codeBuffer, codePos++));
 
// update significance flag vector
m_sigFlagVector[valPos++] = true;
sigPos++;
}
} else {
rest = sigPos - sigEnd;
sigPos = sigEnd;
valPos -= rest;
}
 
}
 
// refinement bit
if (valPos < bufferSize) {
// write one refinement bit
if (GetBit(refBits, refPos)) {
SetBitAtPos(valPos, planeMask);
}
refPos++;
valPos++;
}
}
ASSERT(sigPos <= bufferSize);
ASSERT(refPos <= bufferSize);
ASSERT(valPos == bufferSize);
 
return sigPos;
}
 
////////////////////////////////////////////////////////////////////
// Reconstruct bitplane from significant bitset, refinement bitset, and RL encoded sign bits
// returns length [bits] of sigBits
// input: sigBits, refBits, RL encoded signBits
// output: m_value
// RLE:
// decode run of 2^k 1's by a single 1
// decode run of count 1's followed by a 0 with codeword: 0<count>
UINT32 CDecoder::CMacroBlock::ComposeBitplaneRLD(UINT32 bufferSize, DataT planeMask, UINT32* sigBits, UINT32* refBits, UINT32* signBits) {
ASSERT(sigBits);
ASSERT(refBits);
ASSERT(signBits);
 
UINT32 valPos = 0, signPos = 0, refPos = 0;
UINT32 sigPos = 0, sigEnd;
UINT32 zerocnt, count = 0;
UINT32 k = 0;
UINT32 runlen = 1 << k; // = 2^k
bool signBit = false;
bool zeroAfterRun = false;
 
while (valPos < bufferSize) {
// search next 1 in m_sigFlagVector using searching with sentinel
sigEnd = valPos;
while(!m_sigFlagVector[sigEnd]) { sigEnd++; }
sigEnd -= valPos;
sigEnd += sigPos;
 
// search 1's in sigBits[sigPos..sigEnd)
// these 1's are significant bits
while (sigPos < sigEnd) {
// search 0's
zerocnt = SeekBitRange(sigBits, sigPos, sigEnd - sigPos);
sigPos += zerocnt;
valPos += zerocnt;
if (sigPos < sigEnd) {
// write bit to m_value
SetBitAtPos(valPos, planeMask);
 
// check sign bit
if (count == 0) {
// all 1's have been set
if (zeroAfterRun) {
// finish the run with a 0
signBit = false;
zeroAfterRun = false;
} else {
// decode next sign bit
if (GetBit(signBits, signPos++)) {
// generate 1's run of length 2^k
count = runlen - 1;
signBit = true;
 
// adapt k (double run-length interval)
if (k < WordWidth) {
k++;
runlen <<= 1;
}
} else {
// extract counter and generate 1's run of length count
if (k > 0) {
// extract counter
count = GetValueBlock(signBits, signPos, k);
signPos += k;
 
// adapt k (half run-length interval)
k--;
runlen >>= 1;
}
if (count > 0) {
count--;
signBit = true;
zeroAfterRun = true;
} else {
signBit = false;
}
}
}
} else {
ASSERT(count > 0);
ASSERT(signBit);
count--;
}
 
// copy sign bit
SetSign(valPos, signBit);
 
// update significance flag vector
m_sigFlagVector[valPos++] = true;
sigPos++;
}
}
 
// refinement bit
if (valPos < bufferSize) {
// write one refinement bit
if (GetBit(refBits, refPos)) {
SetBitAtPos(valPos, planeMask);
}
refPos++;
valPos++;
}
}
ASSERT(sigPos <= bufferSize);
ASSERT(refPos <= bufferSize);
ASSERT(signPos <= bufferSize);
ASSERT(valPos == bufferSize);
 
return sigPos;
}
 
////////////////////////////////////////////////////////////////////
#ifdef TRACE
void CDecoder::DumpBuffer() {
//printf("\nDump\n");
//for (int i=0; i < BufferSize; i++) {
// printf("%d", m_value[i]);
//}
}
#endif //TRACE
/*
* The Progressive Graphics File; http://www.libpgf.org
*
* $Date: 2006-06-04 22:05:59 +0200 (So, 04 Jun 2006) $
* $Revision: 229 $
*
* This file Copyright (C) 2006 xeraina GmbH, Switzerland
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
 
//////////////////////////////////////////////////////////////////////
/// @file Decoder.cpp
/// @brief PGF decoder class implementation
/// @author C. Stamm, R. Spuler
 
#include "Decoder.h"
#ifdef TRACE
#include <stdio.h>
#endif
 
//////////////////////////////////////////////////////
// PGF: file structure
//
// PGFPreHeader PGFHeader PGFPostHeader LevelLengths Level_n-1 Level_n-2 ... Level_0
// PGFPostHeader ::= [ColorTable] [UserData]
// LevelLengths ::= UINT32[nLevels]
 
//////////////////////////////////////////////////////
// Decoding scheme
// input: binary file
// output: wavelet coefficients stored in subbands
//
// file (for each buffer: packedLength (16 bit), packed bits)
// |
// m_codeBuffer (for each plane: RLcodeLength (16 bit), RLcoded sigBits + m_sign, refBits)
// | | |
// m_sign sigBits refBits [BufferLen, BufferLen, BufferLen]
// | | |
// m_value [BufferSize]
// |
// subband
//
 
// Constants
#define CodeBufferBitLen (CodeBufferLen*WordWidth) ///< max number of bits in m_codeBuffer
#define MaxCodeLen ((1 << RLblockSizeLen) - 1) ///< max length of RL encoded block
 
/////////////////////////////////////////////////////////////////////
/// Constructor
/// Read pre-header, header, and levelLength
/// It might throw an IOException.
/// @param stream A PGF stream
/// @param preHeader [out] A PGF pre-header
/// @param header [out] A PGF header
/// @param postHeader [out] A PGF post-header
/// @param levelLength The location of the levelLength array. The array is allocated in this method. The caller has to delete this array.
/// @param userDataPos The stream position of the user data (metadata)
/// @param useOMP If true, then the decoder will use multi-threading based on openMP
/// @param skipUserData If true, then user data is not read. In case of available user data, the file position is still returned in userDataPos.
CDecoder::CDecoder(CPGFStream* stream, PGFPreHeader& preHeader, PGFHeader& header,
PGFPostHeader& postHeader, UINT32*& levelLength, UINT64& userDataPos,
bool useOMP, bool skipUserData) THROW_
: m_stream(stream)
, m_startPos(0)
, m_streamSizeEstimation(0)
, m_encodedHeaderLength(0)
, m_currentBlockIndex(0)
, m_macroBlocksAvailable(0)
#ifdef __PGFROISUPPORT__
, m_roi(false)
#endif
{
ASSERT(m_stream);
 
int count, expected;
 
// set number of threads
#ifdef LIBPGF_USE_OPENMP
m_macroBlockLen = omp_get_num_procs();
#else
m_macroBlockLen = 1;
#endif
if (useOMP && m_macroBlockLen > 1) {
#ifdef LIBPGF_USE_OPENMP
omp_set_num_threads(m_macroBlockLen);
#endif
 
// create macro block array
m_macroBlocks = new(std::nothrow) CMacroBlock*[m_macroBlockLen];
if (!m_macroBlocks) ReturnWithError(InsufficientMemory);
for (int i=0; i < m_macroBlockLen; i++) m_macroBlocks[i] = new CMacroBlock();
m_currentBlock = m_macroBlocks[m_currentBlockIndex];
} else {
m_macroBlocks = 0;
m_macroBlockLen = 1; // there is only one macro block
m_currentBlock = new CMacroBlock();
}
 
// store current stream position
m_startPos = m_stream->GetPos();
 
// read magic and version
count = expected = MagicVersionSize;
m_stream->Read(&count, &preHeader);
if (count != expected) ReturnWithError(MissingData);
 
// read header size
if (preHeader.version & Version6) {
// 32 bit header size since version 6
count = expected = 4;
} else {
count = expected = 2;
}
m_stream->Read(&count, ((UINT8*)&preHeader) + MagicVersionSize);
if (count != expected) ReturnWithError(MissingData);
 
// make sure the values are correct read
preHeader.hSize = __VAL(preHeader.hSize);
 
// check magic number
if (memcmp(preHeader.magic, PGFMagic, 3) != 0) {
// error condition: wrong Magic number
ReturnWithError(FormatCannotRead);
}
 
// read file header
count = expected = (preHeader.hSize < HeaderSize) ? preHeader.hSize : HeaderSize;
m_stream->Read(&count, &header);
if (count != expected) ReturnWithError(MissingData);
 
// make sure the values are correct read
header.height = __VAL(UINT32(header.height));
header.width = __VAL(UINT32(header.width));
 
// be ready to read all versions including version 0
if (preHeader.version > 0) {
#ifndef __PGFROISUPPORT__
// check ROI usage
if (preHeader.version & PGFROI) ReturnWithError(FormatCannotRead);
#endif
 
int size = preHeader.hSize - HeaderSize;
 
if (size > 0) {
// read post-header
if (header.mode == ImageModeIndexedColor) {
ASSERT((size_t)size >= ColorTableSize);
// read color table
count = expected = ColorTableSize;
m_stream->Read(&count, postHeader.clut);
if (count != expected) ReturnWithError(MissingData);
size -= count;
}
 
if (size > 0) {
userDataPos = m_stream->GetPos();
postHeader.userDataLen = size;
if (skipUserData) {
Skip(size);
} else {
// create user data memory block
postHeader.userData = new(std::nothrow) UINT8[postHeader.userDataLen];
if (!postHeader.userData) ReturnWithError(InsufficientMemory);
 
// read user data
count = expected = postHeader.userDataLen;
m_stream->Read(&count, postHeader.userData);
if (count != expected) ReturnWithError(MissingData);
}
}
}
 
// create levelLength
levelLength = new(std::nothrow) UINT32[header.nLevels];
if (!levelLength) ReturnWithError(InsufficientMemory);
 
// read levelLength
count = expected = header.nLevels*WordBytes;
m_stream->Read(&count, levelLength);
if (count != expected) ReturnWithError(MissingData);
 
#ifdef PGF_USE_BIG_ENDIAN
// make sure the values are correct read
for (int i=0; i < header.nLevels; i++) {
levelLength[i] = __VAL(levelLength[i]);
}
#endif
 
// compute the total size in bytes; keep attention: level length information is optional
for (int i=0; i < header.nLevels; i++) {
m_streamSizeEstimation += levelLength[i];
}
}
 
// store current stream position
m_encodedHeaderLength = UINT32(m_stream->GetPos() - m_startPos);
}
 
/////////////////////////////////////////////////////////////////////
// Destructor
CDecoder::~CDecoder() {
if (m_macroBlocks) {
for (int i=0; i < m_macroBlockLen; i++) delete m_macroBlocks[i];
delete[] m_macroBlocks;
} else {
delete m_currentBlock;
}
}
 
//////////////////////////////////////////////////////////////////////
/// Copies data from the open stream to a target buffer.
/// It might throw an IOException.
/// @param target The target buffer
/// @param len The number of bytes to read
/// @return The number of bytes copied to the target buffer
UINT32 CDecoder::ReadEncodedData(UINT8* target, UINT32 len) const THROW_ {
ASSERT(m_stream);
 
int count = len;
m_stream->Read(&count, target);
 
return count;
}
 
/////////////////////////////////////////////////////////////////////
/// Unpartitions a rectangular region of a given subband.
/// Partitioning scheme: The plane is partitioned in squares of side length LinBlockSize.
/// Read wavelet coefficients from the output buffer of a macro block.
/// It might throw an IOException.
/// @param band A subband
/// @param quantParam Dequantization value
/// @param width The width of the rectangle
/// @param height The height of the rectangle
/// @param startPos The relative subband position of the top left corner of the rectangular region
/// @param pitch The number of bytes in row of the subband
void CDecoder::Partition(CSubband* band, int quantParam, int width, int height, int startPos, int pitch) THROW_ {
ASSERT(band);
 
const div_t ww = div(width, LinBlockSize);
const div_t hh = div(height, LinBlockSize);
const int ws = pitch - LinBlockSize;
const int wr = pitch - ww.rem;
int pos, base = startPos, base2;
 
// main height
for (int i=0; i < hh.quot; i++) {
// main width
base2 = base;
for (int j=0; j < ww.quot; j++) {
pos = base2;
for (int y=0; y < LinBlockSize; y++) {
for (int x=0; x < LinBlockSize; x++) {
DequantizeValue(band, pos, quantParam);
pos++;
}
pos += ws;
}
base2 += LinBlockSize;
}
// rest of width
pos = base2;
for (int y=0; y < LinBlockSize; y++) {
for (int x=0; x < ww.rem; x++) {
DequantizeValue(band, pos, quantParam);
pos++;
}
pos += wr;
base += pitch;
}
}
// main width
base2 = base;
for (int j=0; j < ww.quot; j++) {
// rest of height
pos = base2;
for (int y=0; y < hh.rem; y++) {
for (int x=0; x < LinBlockSize; x++) {
DequantizeValue(band, pos, quantParam);
pos++;
}
pos += ws;
}
base2 += LinBlockSize;
}
// rest of height
pos = base2;
for (int y=0; y < hh.rem; y++) {
// rest of width
for (int x=0; x < ww.rem; x++) {
DequantizeValue(band, pos, quantParam);
pos++;
}
pos += wr;
}
}
 
////////////////////////////////////////////////////////////////////
// Decode and dequantize HL, and LH band of one level
// LH and HH are interleaved in the codestream and must be split
// Deccoding and dequantization of HL and LH Band (interleaved) using partitioning scheme
// partitions the plane in squares of side length InterBlockSize
// It might throw an IOException.
void CDecoder::DecodeInterleaved(CWaveletTransform* wtChannel, int level, int quantParam) THROW_ {
CSubband* hlBand = wtChannel->GetSubband(level, HL);
CSubband* lhBand = wtChannel->GetSubband(level, LH);
const div_t lhH = div(lhBand->GetHeight(), InterBlockSize);
const div_t hlW = div(hlBand->GetWidth(), InterBlockSize);
const int hlws = hlBand->GetWidth() - InterBlockSize;
const int hlwr = hlBand->GetWidth() - hlW.rem;
const int lhws = lhBand->GetWidth() - InterBlockSize;
const int lhwr = lhBand->GetWidth() - hlW.rem;
int hlPos, lhPos;
int hlBase = 0, lhBase = 0, hlBase2, lhBase2;
 
ASSERT(lhBand->GetWidth() >= hlBand->GetWidth());
ASSERT(hlBand->GetHeight() >= lhBand->GetHeight());
 
if (!hlBand->AllocMemory()) ReturnWithError(InsufficientMemory);
if (!lhBand->AllocMemory()) ReturnWithError(InsufficientMemory);
 
// correct quantParam with normalization factor
quantParam -= level;
if (quantParam < 0) quantParam = 0;
 
// main height
for (int i=0; i < lhH.quot; i++) {
// main width
hlBase2 = hlBase;
lhBase2 = lhBase;
for (int j=0; j < hlW.quot; j++) {
hlPos = hlBase2;
lhPos = lhBase2;
for (int y=0; y < InterBlockSize; y++) {
for (int x=0; x < InterBlockSize; x++) {
DequantizeValue(hlBand, hlPos, quantParam);
DequantizeValue(lhBand, lhPos, quantParam);
hlPos++;
lhPos++;
}
hlPos += hlws;
lhPos += lhws;
}
hlBase2 += InterBlockSize;
lhBase2 += InterBlockSize;
}
// rest of width
hlPos = hlBase2;
lhPos = lhBase2;
for (int y=0; y < InterBlockSize; y++) {
for (int x=0; x < hlW.rem; x++) {
DequantizeValue(hlBand, hlPos, quantParam);
DequantizeValue(lhBand, lhPos, quantParam);
hlPos++;
lhPos++;
}
// width difference between HL and LH
if (lhBand->GetWidth() > hlBand->GetWidth()) {
DequantizeValue(lhBand, lhPos, quantParam);
}
hlPos += hlwr;
lhPos += lhwr;
hlBase += hlBand->GetWidth();
lhBase += lhBand->GetWidth();
}
}
// main width
hlBase2 = hlBase;
lhBase2 = lhBase;
for (int j=0; j < hlW.quot; j++) {
// rest of height
hlPos = hlBase2;
lhPos = lhBase2;
for (int y=0; y < lhH.rem; y++) {
for (int x=0; x < InterBlockSize; x++) {
DequantizeValue(hlBand, hlPos, quantParam);
DequantizeValue(lhBand, lhPos, quantParam);
hlPos++;
lhPos++;
}
hlPos += hlws;
lhPos += lhws;
}
hlBase2 += InterBlockSize;
lhBase2 += InterBlockSize;
}
// rest of height
hlPos = hlBase2;
lhPos = lhBase2;
for (int y=0; y < lhH.rem; y++) {
// rest of width
for (int x=0; x < hlW.rem; x++) {
DequantizeValue(hlBand, hlPos, quantParam);
DequantizeValue(lhBand, lhPos, quantParam);
hlPos++;
lhPos++;
}
// width difference between HL and LH
if (lhBand->GetWidth() > hlBand->GetWidth()) {
DequantizeValue(lhBand, lhPos, quantParam);
}
hlPos += hlwr;
lhPos += lhwr;
hlBase += hlBand->GetWidth();
}
// height difference between HL and LH
if (hlBand->GetHeight() > lhBand->GetHeight()) {
// total width
hlPos = hlBase;
for (int j=0; j < hlBand->GetWidth(); j++) {
DequantizeValue(hlBand, hlPos, quantParam);
hlPos++;
}
}
}
 
////////////////////////////////////////////////////////////////////
/// Skip a given number of bytes in the open stream.
/// It might throw an IOException.
void CDecoder::Skip(UINT64 offset) THROW_ {
m_stream->SetPos(FSFromCurrent, offset);
}
 
//////////////////////////////////////////////////////////////////////
/// Dequantization of a single value at given position in subband.
/// If encoded data is available, then stores dequantized band value into
/// buffer m_value at position m_valuePos.
/// Otherwise reads encoded data block and decodes it.
/// It might throw an IOException.
/// @param band A subband
/// @param bandPos A valid position in subband band
/// @param quantParam The quantization parameter
void CDecoder::DequantizeValue(CSubband* band, UINT32 bandPos, int quantParam) THROW_ {
ASSERT(m_currentBlock);
 
if (m_currentBlock->IsCompletelyRead()) {
// all data of current macro block has been read --> prepare next macro block
DecodeTileBuffer();
}
band->SetData(bandPos, m_currentBlock->m_value[m_currentBlock->m_valuePos] << quantParam);
m_currentBlock->m_valuePos++;
}
 
//////////////////////////////////////////////////////////////////////
// Read next group of blocks from stream and decodes them into macro blocks
// It might throw an IOException.
void CDecoder::DecodeTileBuffer() THROW_ {
// current block has been read --> prepare next current block
m_macroBlocksAvailable--;
 
if (m_macroBlocksAvailable > 0) {
m_currentBlock = m_macroBlocks[++m_currentBlockIndex];
} else {
DecodeBuffer();
}
ASSERT(m_currentBlock);
}
 
//////////////////////////////////////////////////////////////////////
// Read next block from stream and decode into macro block
// Decoding scheme: <wordLen>(16 bits) [ ROI ] data
// ROI ::= <bufferSize>(15 bits) <eofTile>(1 bit)
// It might throw an IOException.
void CDecoder::DecodeBuffer() THROW_ {
ASSERT(m_macroBlocksAvailable <= 0);
 
// macro block management
if (m_macroBlockLen == 1) {
ASSERT(m_currentBlock);
ReadMacroBlock(m_currentBlock);
m_currentBlock->BitplaneDecode();
m_macroBlocksAvailable = 1;
} else {
m_macroBlocksAvailable = 0;
for (int i=0; i < m_macroBlockLen; i++) {
// read sequentially several blocks
try {
ReadMacroBlock(m_macroBlocks[i]);
m_macroBlocksAvailable++;
} catch(IOException& ex) {
if (ex.error == MissingData) {
break; // no further data available
} else {
throw;
}
}
}
#ifdef LIBPGF_USE_OPENMP
// decode in parallel
#pragma omp parallel for default(shared) //no declared exceptions in next block
#endif
for (int i=0; i < m_macroBlocksAvailable; i++) {
m_macroBlocks[i]->BitplaneDecode();
}
// prepare current macro block
m_currentBlockIndex = 0;
m_currentBlock = m_macroBlocks[m_currentBlockIndex];
}
}
 
//////////////////////////////////////////////////////////////////////
// Read next block from stream and store it in the given block
// It might throw an IOException.
void CDecoder::ReadMacroBlock(CMacroBlock* block) THROW_ {
ASSERT(block);
 
UINT16 wordLen;
ROIBlockHeader h(BufferSize);
int count, expected;
 
#ifdef TRACE
//UINT32 filePos = (UINT32)m_stream->GetPos();
//printf("DecodeBuffer: %d\n", filePos);
#endif
 
// read wordLen
count = expected = sizeof(UINT16);
m_stream->Read(&count, &wordLen);
if (count != expected) ReturnWithError(MissingData);
wordLen = __VAL(wordLen);
if (wordLen > BufferSize)
ReturnWithError(FormatCannotRead);
 
#ifdef __PGFROISUPPORT__
// read ROIBlockHeader
if (m_roi) {
m_stream->Read(&count, &h.val);
if (count != expected) ReturnWithError(MissingData);
// convert ROIBlockHeader
h.val = __VAL(h.val);
}
#endif
// save header
block->m_header = h;
 
// read data
count = expected = wordLen*WordBytes;
m_stream->Read(&count, block->m_codeBuffer);
if (count != expected) ReturnWithError(MissingData);
 
#ifdef PGF_USE_BIG_ENDIAN
// convert data
count /= WordBytes;
for (int i=0; i < count; i++) {
block->m_codeBuffer[i] = __VAL(block->m_codeBuffer[i]);
}
#endif
 
#ifdef __PGFROISUPPORT__
ASSERT(m_roi && h.rbh.bufferSize <= BufferSize || h.rbh.bufferSize == BufferSize);
#else
ASSERT(h.rbh.bufferSize == BufferSize);
#endif
}
 
//////////////////////////////////////////////////////////////////////
// Read next block from stream but don't decode into macro block
// Encoding scheme: <wordLen>(16 bits) [ ROI ] data
// ROI ::= <bufferSize>(15 bits) <eofTile>(1 bit)
// It might throw an IOException.
void CDecoder::SkipTileBuffer() THROW_ {
// current block is not used
m_macroBlocksAvailable--;
 
// check if pre-decoded data is available
if (m_macroBlocksAvailable > 0) {
m_currentBlock = m_macroBlocks[++m_currentBlockIndex];
return;
}
 
UINT16 wordLen;
int count, expected;
 
// read wordLen
count = expected = sizeof(wordLen);
m_stream->Read(&count, &wordLen);
if (count != expected) ReturnWithError(MissingData);
wordLen = __VAL(wordLen);
ASSERT(wordLen <= BufferSize);
 
#ifdef __PGFROISUPPORT__
if (m_roi) {
// skip ROIBlockHeader
m_stream->SetPos(FSFromCurrent, sizeof(ROIBlockHeader));
}
#endif
 
// skip data
m_stream->SetPos(FSFromCurrent, wordLen*WordBytes);
}
 
//////////////////////////////////////////////////////////////////////
// Decode block into buffer of given size using bit plane coding.
// A buffer contains bufferLen UINT32 values, thus, bufferSize bits per bit plane.
// Following coding scheme is used:
// Buffer ::= <nPlanes>(5 bits) foreach(plane i): Plane[i]
// Plane[i] ::= [ Sig1 | Sig2 ] [DWORD alignment] refBits
// Sig1 ::= 1 <codeLen>(15 bits) codedSigAndSignBits
// Sig2 ::= 0 <sigLen>(15 bits) [Sign1 | Sign2 ] [DWORD alignment] sigBits
// Sign1 ::= 1 <codeLen>(15 bits) codedSignBits
// Sign2 ::= 0 <signLen>(15 bits) [DWORD alignment] signBits
void CDecoder::CMacroBlock::BitplaneDecode() {
UINT32 bufferSize = m_header.rbh.bufferSize; ASSERT(bufferSize <= BufferSize);
 
UINT32 nPlanes;
UINT32 codePos = 0, codeLen, sigLen, sigPos, signLen, signPos;
DataT planeMask;
 
// clear significance vector
for (UINT32 k=0; k < bufferSize; k++) {
m_sigFlagVector[k] = false;
}
m_sigFlagVector[bufferSize] = true; // sentinel
 
// clear output buffer
for (UINT32 k=0; k < BufferSize; k++) {
m_value[k] = 0;
}
 
// read number of bit planes
// <nPlanes>
nPlanes = GetValueBlock(m_codeBuffer, 0, MaxBitPlanesLog);
codePos += MaxBitPlanesLog;
 
// loop through all bit planes
if (nPlanes == 0) nPlanes = MaxBitPlanes + 1;
ASSERT(0 < nPlanes && nPlanes <= MaxBitPlanes + 1);
planeMask = 1 << (nPlanes - 1);
 
for (int plane = nPlanes - 1; plane >= 0; plane--) {
// read RL code
if (GetBit(m_codeBuffer, codePos)) {
// RL coding of sigBits is used
// <1><codeLen><codedSigAndSignBits>_<refBits>
codePos++;
 
// read codeLen
codeLen = GetValueBlock(m_codeBuffer, codePos, RLblockSizeLen); ASSERT(codeLen <= MaxCodeLen);
 
// position of encoded sigBits and signBits
sigPos = codePos + RLblockSizeLen; ASSERT(sigPos < CodeBufferBitLen);
 
// refinement bits
codePos = AlignWordPos(sigPos + codeLen); ASSERT(codePos < CodeBufferBitLen);
 
// run-length decode significant bits and signs from m_codeBuffer and
// read refinement bits from m_codeBuffer and compose bit plane
sigLen = ComposeBitplaneRLD(bufferSize, planeMask, sigPos, &m_codeBuffer[codePos >> WordWidthLog]);
 
} else {
// no RL coding is used for sigBits and signBits together
// <0><sigLen>
codePos++;
 
// read sigLen
sigLen = GetValueBlock(m_codeBuffer, codePos, RLblockSizeLen); ASSERT(sigLen <= MaxCodeLen);
codePos += RLblockSizeLen; ASSERT(codePos < CodeBufferBitLen);
 
// read RL code for signBits
if (GetBit(m_codeBuffer, codePos)) {
// RL coding is used just for signBits
// <1><codeLen><codedSignBits>_<sigBits>_<refBits>
codePos++;
 
// read codeLen
codeLen = GetValueBlock(m_codeBuffer, codePos, RLblockSizeLen); ASSERT(codeLen <= MaxCodeLen);
 
// sign bits
signPos = codePos + RLblockSizeLen; ASSERT(signPos < CodeBufferBitLen);
// significant bits
sigPos = AlignWordPos(signPos + codeLen); ASSERT(sigPos < CodeBufferBitLen);
 
// refinement bits
codePos = AlignWordPos(sigPos + sigLen); ASSERT(codePos < CodeBufferBitLen);
 
// read significant and refinement bitset from m_codeBuffer
sigLen = ComposeBitplaneRLD(bufferSize, planeMask, &m_codeBuffer[sigPos >> WordWidthLog], &m_codeBuffer[codePos >> WordWidthLog], signPos);
} else {
// RL coding of signBits was not efficient and therefore not used
// <0><signLen>_<signBits>_<sigBits>_<refBits>
codePos++;
 
// read signLen
signLen = GetValueBlock(m_codeBuffer, codePos, RLblockSizeLen); ASSERT(signLen <= MaxCodeLen);
// sign bits
signPos = AlignWordPos(codePos + RLblockSizeLen); ASSERT(signPos < CodeBufferBitLen);
 
// significant bits
sigPos = AlignWordPos(signPos + signLen); ASSERT(sigPos < CodeBufferBitLen);
 
// refinement bits
codePos = AlignWordPos(sigPos + sigLen); ASSERT(codePos < CodeBufferBitLen);
 
// read significant and refinement bitset from m_codeBuffer
sigLen = ComposeBitplane(bufferSize, planeMask, &m_codeBuffer[sigPos >> WordWidthLog], &m_codeBuffer[codePos >> WordWidthLog], &m_codeBuffer[signPos >> WordWidthLog]);
}
}
 
// start of next chunk
codePos = AlignWordPos(codePos + bufferSize - sigLen); ASSERT(codePos < CodeBufferBitLen);
// next plane
planeMask >>= 1;
}
 
m_valuePos = 0;
}
 
////////////////////////////////////////////////////////////////////
// Reconstruct bitplane from significant bitset and refinement bitset
// returns length [bits] of sigBits
// input: sigBits, refBits, signBits
// output: m_value
UINT32 CDecoder::CMacroBlock::ComposeBitplane(UINT32 bufferSize, DataT planeMask, UINT32* sigBits, UINT32* refBits, UINT32* signBits) {
ASSERT(sigBits);
ASSERT(refBits);
ASSERT(signBits);
 
UINT32 valPos = 0, signPos = 0, refPos = 0;
UINT32 sigPos = 0, sigEnd;
UINT32 zerocnt;
 
while (valPos < bufferSize) {
// search next 1 in m_sigFlagVector using searching with sentinel
sigEnd = valPos;
while(!m_sigFlagVector[sigEnd]) { sigEnd++; }
sigEnd -= valPos;
sigEnd += sigPos;
 
// search 1's in sigBits[sigPos..sigEnd)
// these 1's are significant bits
while (sigPos < sigEnd) {
// search 0's
zerocnt = SeekBitRange(sigBits, sigPos, sigEnd - sigPos);
sigPos += zerocnt;
valPos += zerocnt;
if (sigPos < sigEnd) {
// write bit to m_value
SetBitAtPos(valPos, planeMask);
 
// copy sign bit
SetSign(valPos, GetBit(signBits, signPos++));
 
// update significance flag vector
m_sigFlagVector[valPos++] = true;
sigPos++;
}
}
// refinement bit
if (valPos < bufferSize) {
// write one refinement bit
if (GetBit(refBits, refPos)) {
SetBitAtPos(valPos, planeMask);
}
refPos++;
valPos++;
}
}
ASSERT(sigPos <= bufferSize);
ASSERT(refPos <= bufferSize);
ASSERT(signPos <= bufferSize);
ASSERT(valPos == bufferSize);
 
return sigPos;
}
 
////////////////////////////////////////////////////////////////////
// Reconstruct bitplane from significant bitset and refinement bitset
// returns length [bits] of decoded significant bits
// input: RL encoded sigBits and signBits in m_codeBuffer, refBits
// output: m_value
// RLE:
// - Decode run of 2^k zeros by a single 0.
// - Decode run of count 0's followed by a 1 with codeword: 1<count>x
// - x is 0: if a positive sign has been stored, otherwise 1
// - Read each bit from m_codeBuffer[codePos] and increment codePos.
UINT32 CDecoder::CMacroBlock::ComposeBitplaneRLD(UINT32 bufferSize, DataT planeMask, UINT32 codePos, UINT32* refBits) {
ASSERT(refBits);
 
UINT32 valPos = 0, refPos = 0;
UINT32 sigPos = 0, sigEnd;
UINT32 k = 3;
UINT32 runlen = 1 << k; // = 2^k
UINT32 count = 0, rest = 0;
bool set1 = false;
 
while (valPos < bufferSize) {
// search next 1 in m_sigFlagVector using searching with sentinel
sigEnd = valPos;
while(!m_sigFlagVector[sigEnd]) { sigEnd++; }
sigEnd -= valPos;
sigEnd += sigPos;
 
while (sigPos < sigEnd) {
if (rest || set1) {
// rest of last run
sigPos += rest;
valPos += rest;
rest = 0;
} else {
// decode significant bits
if (GetBit(m_codeBuffer, codePos++)) {
// extract counter and generate zero run of length count
if (k > 0) {
// extract counter
count = GetValueBlock(m_codeBuffer, codePos, k);
codePos += k;
if (count > 0) {
sigPos += count;
valPos += count;
}
 
// adapt k (half run-length interval)
k--;
runlen >>= 1;
}
 
set1 = true;
 
} else {
// generate zero run of length 2^k
sigPos += runlen;
valPos += runlen;
 
// adapt k (double run-length interval)
if (k < WordWidth) {
k++;
runlen <<= 1;
}
}
}
 
if (sigPos < sigEnd) {
if (set1) {
set1 = false;
 
// write 1 bit
SetBitAtPos(valPos, planeMask);
 
// set sign bit
SetSign(valPos, GetBit(m_codeBuffer, codePos++));
 
// update significance flag vector
m_sigFlagVector[valPos++] = true;
sigPos++;
}
} else {
rest = sigPos - sigEnd;
sigPos = sigEnd;
valPos -= rest;
}
 
}
 
// refinement bit
if (valPos < bufferSize) {
// write one refinement bit
if (GetBit(refBits, refPos)) {
SetBitAtPos(valPos, planeMask);
}
refPos++;
valPos++;
}
}
ASSERT(sigPos <= bufferSize);
ASSERT(refPos <= bufferSize);
ASSERT(valPos == bufferSize);
 
return sigPos;
}
 
////////////////////////////////////////////////////////////////////
// Reconstruct bitplane from significant bitset, refinement bitset, and RL encoded sign bits
// returns length [bits] of sigBits
// input: sigBits, refBits, RL encoded signBits
// output: m_value
// RLE:
// decode run of 2^k 1's by a single 1
// decode run of count 1's followed by a 0 with codeword: 0<count>
UINT32 CDecoder::CMacroBlock::ComposeBitplaneRLD(UINT32 bufferSize, DataT planeMask, UINT32* sigBits, UINT32* refBits, UINT32 signPos) {
ASSERT(sigBits);
ASSERT(refBits);
 
UINT32 valPos = 0, refPos = 0;
UINT32 sigPos = 0, sigEnd;
UINT32 zerocnt, count = 0;
UINT32 k = 0;
UINT32 runlen = 1 << k; // = 2^k
bool signBit = false;
bool zeroAfterRun = false;
 
while (valPos < bufferSize) {
// search next 1 in m_sigFlagVector using searching with sentinel
sigEnd = valPos;
while(!m_sigFlagVector[sigEnd]) { sigEnd++; }
sigEnd -= valPos;
sigEnd += sigPos;
 
// search 1's in sigBits[sigPos..sigEnd)
// these 1's are significant bits
while (sigPos < sigEnd) {
// search 0's
zerocnt = SeekBitRange(sigBits, sigPos, sigEnd - sigPos);
sigPos += zerocnt;
valPos += zerocnt;
if (sigPos < sigEnd) {
// write bit to m_value
SetBitAtPos(valPos, planeMask);
 
// check sign bit
if (count == 0) {
// all 1's have been set
if (zeroAfterRun) {
// finish the run with a 0
signBit = false;
zeroAfterRun = false;
} else {
// decode next sign bit
if (GetBit(m_codeBuffer, signPos++)) {
// generate 1's run of length 2^k
count = runlen - 1;
signBit = true;
// adapt k (double run-length interval)
if (k < WordWidth) {
k++;
runlen <<= 1;
}
} else {
// extract counter and generate 1's run of length count
if (k > 0) {
// extract counter
count = GetValueBlock(m_codeBuffer, signPos, k);
signPos += k;
 
// adapt k (half run-length interval)
k--;
runlen >>= 1;
}
if (count > 0) {
count--;
signBit = true;
zeroAfterRun = true;
} else {
signBit = false;
}
}
}
} else {
ASSERT(count > 0);
ASSERT(signBit);
count--;
}
 
// copy sign bit
SetSign(valPos, signBit);
 
// update significance flag vector
m_sigFlagVector[valPos++] = true;
sigPos++;
}
}
 
// refinement bit
if (valPos < bufferSize) {
// write one refinement bit
if (GetBit(refBits, refPos)) {
SetBitAtPos(valPos, planeMask);
}
refPos++;
valPos++;
}
}
ASSERT(sigPos <= bufferSize);
ASSERT(refPos <= bufferSize);
ASSERT(valPos == bufferSize);
 
return sigPos;
}
 
////////////////////////////////////////////////////////////////////
#ifdef TRACE
void CDecoder::DumpBuffer() {
//printf("\nDump\n");
//for (int i=0; i < BufferSize; i++) {
// printf("%d", m_value[i]);
//}
}
#endif //TRACE
/trunk/Scribus/scribus/third_party/pgf/Decoder.h
1,193 → 1,222
/*
* The Progressive Graphics File; http://www.libpgf.org
*
* $Date: 2006-06-04 22:05:59 +0200 (So, 04 Jun 2006) $
* $Revision: 229 $
*
* This file Copyright (C) 2006 xeraina GmbH, Switzerland
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
 
//////////////////////////////////////////////////////////////////////
/// @file Decoder.h
/// @brief PGF decoder class
/// @author C. Stamm, R. Spuler
 
#ifndef PGF_DECODER_H
#define PGF_DECODER_H
 
#include "PGFstream.h"
#include "BitStream.h"
#include "Subband.h"
#include "WaveletTransform.h"
 
/////////////////////////////////////////////////////////////////////
// Constants
#define BufferLen (BufferSize/WordWidth) // number of words per buffer
 
/////////////////////////////////////////////////////////////////////
/// PGF decoder class.
/// @author C. Stamm, R. Spuler
/// @brief PGF decoder
class CDecoder {
//////////////////////////////////////////////////////////////////////
/// PGF decoder macro block class.
/// @author C. Stamm, I. Bauersachs
/// @brief A macro block is a decoding unit of fixed size (uncoded)
class CMacroBlock {
public:
CMacroBlock(CDecoder *decoder)
: m_header(0)
, m_valuePos(0)
, m_decoder(decoder)
{
ASSERT(m_decoder);
}
 
void BitplaneDecode(); // several macro blocks can be encoded in parallel
 
ROIBlockHeader m_header; // block header
DataT m_value[BufferSize]; // output buffer of values with index m_valuePos
UINT32 m_codeBuffer[BufferSize]; // input buffer for encoded bitstream
UINT32 m_valuePos; // current position in m_value
 
private:
UINT32 ComposeBitplane(UINT32 bufferSize, DataT planeMask, UINT32* sigBits, UINT32* refBits, UINT32* signBits);
UINT32 ComposeBitplaneRLD(UINT32 bufferSize, DataT planeMask, UINT32 sigPos, UINT32* refBits);
UINT32 ComposeBitplaneRLD(UINT32 bufferSize, DataT planeMask, UINT32* sigBits, UINT32* refBits, UINT32* signBits);
void SetBitAtPos(UINT32 pos, DataT planeMask) { (m_value[pos] >= 0) ? m_value[pos] |= planeMask : m_value[pos] -= planeMask; }
void SetSign(UINT32 pos, bool sign) { m_value[pos] = -m_value[pos]*sign + m_value[pos]*(!sign); }
 
CDecoder *m_decoder; // outer class
bool m_sigFlagVector[BufferSize+1]; // see paper from Malvar, Fast Progressive Wavelet Coder
};
 
public:
/////////////////////////////////////////////////////////////////////
/// Constructor: Read pre-header, header, and levelLength at current stream position.
/// It might throw an IOException.
/// @param stream A PGF stream
/// @param preHeader [out] A PGF pre-header
/// @param header [out] A PGF header
/// @param postHeader [out] A PGF post-header
/// @param levelLength The location of the levelLength array. The array is allocated in this method. The caller has to delete this array.
/// @param useOMP If true, then the decoder will use multi-threading based on openMP
CDecoder(CPGFStream* stream, PGFPreHeader& preHeader, PGFHeader& header, PGFPostHeader& postHeader, UINT32*& levelLength, bool useOMP = true) THROW_; // throws IOException
 
/////////////////////////////////////////////////////////////////////
/// Destructor
~CDecoder();
 
/////////////////////////////////////////////////////////////////////
/// Unpartitions a rectangular region of a given subband.
/// Partitioning scheme: The plane is partitioned in squares of side length LinBlockSize.
/// Write wavelet coefficients into buffer.
/// It might throw an IOException.
/// @param band A subband
/// @param quantParam Dequantization value
/// @param width The width of the rectangle
/// @param height The height of the rectangle
/// @param startPos The buffer position of the top left corner of the rectangular region
/// @param pitch The number of bytes in row of the subband
void Partition(CSubband* band, int quantParam, int width, int height, int startPos, int pitch) THROW_;
 
/////////////////////////////////////////////////////////////////////
/// Deccoding and dequantization of HL and LH subband (interleaved) using partitioning scheme.
/// Partitioning scheme: The plane is partitioned in squares of side length InterBlockSize.
/// It might throw an IOException.
/// @param wtChannel A wavelet transform channel containing the HL and HL band
/// @param level Wavelet transform level
/// @param quantParam Dequantization value
void DecodeInterleaved(CWaveletTransform* wtChannel, int level, int quantParam) THROW_;
 
//////////////////////////////////////////////////////////////////////
/// Return the length of all encoded headers in bytes.
/// @return The length of all encoded headers in bytes
UINT32 GetEncodedHeaderLength() const { return m_encodedHeaderLength; }
 
////////////////////////////////////////////////////////////////////
/// Reset stream position to beginning of PGF pre header
void SetStreamPosToStart() THROW_ { ASSERT(m_stream); m_stream->SetPos(FSFromStart, m_startPos); }
 
////////////////////////////////////////////////////////////////////
/// Reset stream position to beginning of data block
void SetStreamPosToData() THROW_ { ASSERT(m_stream); m_stream->SetPos(FSFromStart, m_startPos + m_encodedHeaderLength); }
 
////////////////////////////////////////////////////////////////////
/// Skip a given number of bytes in the open stream.
/// It might throw an IOException.
void Skip(UINT64 offset) THROW_;
 
/////////////////////////////////////////////////////////////////////
/// Dequantization of a single value at given position in subband.
/// @param band A subband
/// @param bandPos A valid position in subband band
/// @param quantParam The quantization parameter
void DequantizeValue(CSubband* band, UINT32 bandPos, int quantParam);
 
//////////////////////////////////////////////////////////////////////
/// Copies data from the open stream to a target buffer.
/// It might throw an IOException.
/// @param target The target buffer
/// @param len The number of bytes to read
/// @return The number of bytes copied to the target buffer
UINT32 ReadEncodedData(UINT8* target, UINT32 len) const THROW_;
 
/////////////////////////////////////////////////////////////////////
/// Reads stream and decodes tile buffer
/// It might throw an IOException.
void DecodeBuffer() THROW_;
 
#ifdef __PGFROISUPPORT__
/////////////////////////////////////////////////////////////////////
/// Reads stream and decodes tile buffer
/// It might throw an IOException.
void DecodeTileBuffer() THROW_;
 
/////////////////////////////////////////////////////////////////////
/// Resets stream position to next tile.
/// It might throw an IOException.
void SkipTileBuffer() THROW_;
 
/////////////////////////////////////////////////////////////////////
/// Enables region of interest (ROI) status.
void SetROI() { m_roi = true; }
#endif
 
#ifdef TRACE
void DumpBuffer();
#endif
 
private:
void ReadMacroBlock(CMacroBlock* block) THROW_; // throws IOException
 
CPGFStream *m_stream; // input pgf stream
UINT64 m_startPos; // stream position at the beginning of the PGF pre header
UINT64 m_streamSizeEstimation; // estimation of stream size
UINT32 m_encodedHeaderLength; // stream offset from startPos to the beginning of the data part (highest level)
 
CMacroBlock **m_macroBlocks; // array of macroblocks
int m_currentBlockIndex; // index of current macro block
int m_macroBlockLen; // array length
int m_macroBlocksAvailable; // number of decoded macro blocks
CMacroBlock *m_currentBlock; // current macro block (used by main thread)
 
#ifdef __PGFROISUPPORT__
bool m_roi; // true: ensures region of interest (ROI) decoding
#endif
};
 
#endif //PGF_DECODER_H
/*
* The Progressive Graphics File; http://www.libpgf.org
*
* $Date: 2006-06-04 22:05:59 +0200 (So, 04 Jun 2006) $
* $Revision: 229 $
*
* This file Copyright (C) 2006 xeraina GmbH, Switzerland
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
 
//////////////////////////////////////////////////////////////////////
/// @file Decoder.h
/// @brief PGF decoder class
/// @author C. Stamm, R. Spuler
 
#ifndef PGF_DECODER_H
#define PGF_DECODER_H
 
#include "PGFstream.h"
#include "BitStream.h"
#include "Subband.h"
#include "WaveletTransform.h"
 
/////////////////////////////////////////////////////////////////////
// Constants
#define BufferLen (BufferSize/WordWidth) ///< number of words per buffer
#define CodeBufferLen BufferSize ///< number of words in code buffer (CodeBufferLen > BufferLen)
 
/////////////////////////////////////////////////////////////////////
/// PGF decoder class.
/// @author C. Stamm, R. Spuler
/// @brief PGF decoder
class CDecoder {
//////////////////////////////////////////////////////////////////////
/// PGF decoder macro block class.
/// @author C. Stamm, I. Bauersachs
/// @brief A macro block is a decoding unit of fixed size (uncoded)
class CMacroBlock {
public:
//////////////////////////////////////////////////////////////////////
/// Constructor: Initializes new macro block.
/// @param decoder Pointer to outer class.
CMacroBlock()
: m_header(0) // makes sure that IsCompletelyRead() returns true for an empty macro block
#if defined(WIN32) || defined(WINCE) || defined(WIN64)
#pragma warning( suppress : 4351 )
#endif
, m_value()
, m_codeBuffer()
, m_valuePos(0)
, m_sigFlagVector()
{
}
 
//////////////////////////////////////////////////////////////////////
/// Returns true if this macro block has been completely read.
/// @return true if current value position is at block end
bool IsCompletelyRead() const { return m_valuePos >= m_header.rbh.bufferSize; }
 
//////////////////////////////////////////////////////////////////////
/// Decodes already read input data into this macro block.
/// Several macro blocks can be decoded in parallel.
/// Call CDecoder::ReadMacroBlock before this method.
void BitplaneDecode();
 
ROIBlockHeader m_header; ///< block header
DataT m_value[BufferSize]; ///< output buffer of values with index m_valuePos
UINT32 m_codeBuffer[CodeBufferLen]; ///< input buffer for encoded bitstream
UINT32 m_valuePos; ///< current position in m_value
 
private:
UINT32 ComposeBitplane(UINT32 bufferSize, DataT planeMask, UINT32* sigBits, UINT32* refBits, UINT32* signBits);
UINT32 ComposeBitplaneRLD(UINT32 bufferSize, DataT planeMask, UINT32 sigPos, UINT32* refBits);
UINT32 ComposeBitplaneRLD(UINT32 bufferSize, DataT planeMask, UINT32* sigBits, UINT32* refBits, UINT32 signPos);
void SetBitAtPos(UINT32 pos, DataT planeMask) { (m_value[pos] >= 0) ? m_value[pos] |= planeMask : m_value[pos] -= planeMask; }
void SetSign(UINT32 pos, bool sign) { m_value[pos] = -m_value[pos]*sign + m_value[pos]*(!sign); }
 
bool m_sigFlagVector[BufferSize+1]; // see paper from Malvar, Fast Progressive Wavelet Coder
};
 
public:
/////////////////////////////////////////////////////////////////////
/// Constructor: Read pre-header, header, and levelLength at current stream position.
/// It might throw an IOException.
/// @param stream A PGF stream
/// @param preHeader [out] A PGF pre-header
/// @param header [out] A PGF header
/// @param postHeader [out] A PGF post-header
/// @param levelLength The location of the levelLength array. The array is allocated in this method. The caller has to delete this array.
/// @param userDataPos The stream position of the user data (metadata)
/// @param useOMP If true, then the decoder will use multi-threading based on openMP
/// @param skipUserData If true, then user data is not read. In case of available user data, the file position is still returned in userDataPos.
CDecoder(CPGFStream* stream, PGFPreHeader& preHeader, PGFHeader& header,
PGFPostHeader& postHeader, UINT32*& levelLength, UINT64& userDataPos,
bool useOMP, bool skipUserData) THROW_; // throws IOException
 
/////////////////////////////////////////////////////////////////////
/// Destructor
~CDecoder();
 
/////////////////////////////////////////////////////////////////////
/// Unpartitions a rectangular region of a given subband.
/// Partitioning scheme: The plane is partitioned in squares of side length LinBlockSize.
/// Read wavelet coefficients from the output buffer of a macro block.
/// It might throw an IOException.
/// @param band A subband
/// @param quantParam Dequantization value
/// @param width The width of the rectangle
/// @param height The height of the rectangle
/// @param startPos The relative subband position of the top left corner of the rectangular region
/// @param pitch The number of bytes in row of the subband
void Partition(CSubband* band, int quantParam, int width, int height, int startPos, int pitch) THROW_;
 
/////////////////////////////////////////////////////////////////////
/// Deccoding and dequantization of HL and LH subband (interleaved) using partitioning scheme.
/// Partitioning scheme: The plane is partitioned in squares of side length InterBlockSize.
/// It might throw an IOException.
/// @param wtChannel A wavelet transform channel containing the HL and HL band
/// @param level Wavelet transform level
/// @param quantParam Dequantization value
void DecodeInterleaved(CWaveletTransform* wtChannel, int level, int quantParam) THROW_;
 
//////////////////////////////////////////////////////////////////////
/// Return the length of all encoded headers in bytes.
/// @return The length of all encoded headers in bytes
UINT32 GetEncodedHeaderLength() const { return m_encodedHeaderLength; }
 
////////////////////////////////////////////////////////////////////
/// Reset stream position to beginning of PGF pre-header
void SetStreamPosToStart() THROW_ { ASSERT(m_stream); m_stream->SetPos(FSFromStart, m_startPos); }
 
////////////////////////////////////////////////////////////////////
/// Reset stream position to beginning of data block
void SetStreamPosToData() THROW_ { ASSERT(m_stream); m_stream->SetPos(FSFromStart, m_startPos + m_encodedHeaderLength); }
 
////////////////////////////////////////////////////////////////////
/// Skip a given number of bytes in the open stream.
/// It might throw an IOException.
void Skip(UINT64 offset) THROW_;
 
/////////////////////////////////////////////////////////////////////
/// Dequantization of a single value at given position in subband.
/// It might throw an IOException.
/// @param band A subband
/// @param bandPos A valid position in subband band
/// @param quantParam The quantization parameter
void DequantizeValue(CSubband* band, UINT32 bandPos, int quantParam) THROW_;
 
//////////////////////////////////////////////////////////////////////
/// Copies data from the open stream to a target buffer.
/// It might throw an IOException.
/// @param target The target buffer
/// @param len The number of bytes to read
/// @return The number of bytes copied to the target buffer
UINT32 ReadEncodedData(UINT8* target, UINT32 len) const THROW_;
 
/////////////////////////////////////////////////////////////////////
/// Reads stream and decodes tile buffer
/// It might throw an IOException.
void DecodeBuffer() THROW_;
 
/////////////////////////////////////////////////////////////////////
/// @return Stream
CPGFStream* GetStream() { return m_stream; }
 
/////////////////////////////////////////////////////////////////////
/// @return True if decoded macro blocks are available for processing
bool MacroBlocksAvailable() const { return m_macroBlocksAvailable > 1; }
 
#ifdef __PGFROISUPPORT__
/////////////////////////////////////////////////////////////////////
/// Reads stream and decodes tile buffer
/// It might throw an IOException.
void DecodeTileBuffer() THROW_;
 
/////////////////////////////////////////////////////////////////////
/// Resets stream position to next tile.
/// It might throw an IOException.
void SkipTileBuffer() THROW_;
 
/////////////////////////////////////////////////////////////////////
/// Enables region of interest (ROI) status.
void SetROI() { m_roi = true; }
#endif
 
#ifdef TRACE
void DumpBuffer();
#endif
 
private:
void ReadMacroBlock(CMacroBlock* block) THROW_; ///< throws IOException
 
CPGFStream *m_stream; ///< input PGF stream
UINT64 m_startPos; ///< stream position at the beginning of the PGF pre-header
UINT64 m_streamSizeEstimation; ///< estimation of stream size
UINT32 m_encodedHeaderLength; ///< stream offset from startPos to the beginning of the data part (highest level)
 
CMacroBlock **m_macroBlocks; ///< array of macroblocks
int m_currentBlockIndex; ///< index of current macro block
int m_macroBlockLen; ///< array length
int m_macroBlocksAvailable; ///< number of decoded macro blocks (including currently used macro block)
CMacroBlock *m_currentBlock; ///< current macro block (used by main thread)
 
#ifdef __PGFROISUPPORT__
bool m_roi; ///< true: ensures region of interest (ROI) decoding
#endif
};
 
#endif //PGF_DECODER_H
/trunk/Scribus/scribus/third_party/pgf/Encoder.cpp
1,763 → 1,828
/*
* The Progressive Graphics File; http://www.libpgf.org
*
* $Date: 2007-02-03 13:04:21 +0100 (Sa, 03 Feb 2007) $
* $Revision: 280 $
*
* This file Copyright (C) 2006 xeraina GmbH, Switzerland
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
 
//////////////////////////////////////////////////////////////////////
/// @file Encoder.cpp
/// @brief PGF encoder class implementation
/// @author C. Stamm, R. Spuler
 
#include "Encoder.h"
 
#ifdef TRACE
#include <stdio.h>
#endif
 
//////////////////////////////////////////////////////
// PGF: file structure
//
// PGFPreHeader PGFHeader PGFPostHeader LevelLengths Level_n-1 Level_n-2 ... Level_0
// PGFPostHeader ::= [ColorTable] [UserData]
// LevelLengths ::= UINT32[nLevels]
 
//////////////////////////////////////////////////////
// Encoding scheme
// input: wavelet coefficients stored in subbands
// output: binary file
//
// subband
// |
// m_value [BufferSize]
// | | |
// m_sign sigBits refBits [BufferSize, BufferLen, BufferLen]
// | | |
// m_codeBuffer (for each plane: RLcodeLength (16 bit), RLcoded sigBits + m_sign, refBits)
// |
// file (for each buffer: packedLength (16 bit), packed bits)
//
 
// Constants
#define CodeBufferBitLen (BufferSize*WordWidth) // max number of bits in m_codeBuffer
#define MaxCodeLen ((1 << RLblockSizeLen) - 1) // max length of RL encoded block
 
//////////////////////////////////////////////////////
// Constructor
// Write pre-header, header, postHeader, and levelLength.
// It might throw an IOException.
// preHeader and header must not be references, because on BigEndian platforms they are modified
CEncoder::CEncoder(CPGFStream* stream, PGFPreHeader preHeader, PGFHeader header, const PGFPostHeader& postHeader, UINT32*& levelLength, bool useOMP /*= true*/) THROW_
: m_stream(stream)
, m_startPosition(0)
, m_currLevelIndex(0)
, m_nLevels(header.nLevels)
, m_favorSpeed(false)
, m_forceWriting(false)
#ifdef __PGFROISUPPORT__
, m_roi(false)
#endif
{
ASSERT(m_stream);
 
int count;
 
// set number of threads
#ifdef LIBPGF_USE_OPENMP
m_macroBlockLen = omp_get_num_procs();
#else
m_macroBlockLen = 1;
#endif
 
if (useOMP && m_macroBlockLen > 1) {
#ifdef LIBPGF_USE_OPENMP
omp_set_num_threads(m_macroBlockLen);
#endif
 
// create macro block array
m_macroBlocks = new CMacroBlock*[m_macroBlockLen];
for (int i=0; i < m_macroBlockLen; i++) m_macroBlocks[i] = new CMacroBlock(this);
m_lastMacroBlock = 0;
m_currentBlock = m_macroBlocks[m_lastMacroBlock++];
} else {
m_macroBlocks = 0;
m_currentBlock = new CMacroBlock(this);
}
 
// save file position
m_startPosition = m_stream->GetPos();
 
// write preHeader
preHeader.hSize = __VAL(preHeader.hSize);
count = PreHeaderSize;
m_stream->Write(&count, &preHeader);
 
// write file header
header.height = __VAL(header.height);
header.width = __VAL(header.width);
count = HeaderSize;
m_stream->Write(&count, &header);
 
// write postHeader
if (header.mode == ImageModeIndexedColor) {
// write color table
count = ColorTableSize;
m_stream->Write(&count, (void *)postHeader.clut);
}
if (postHeader.userData && postHeader.userDataLen) {
// write user data
count = postHeader.userDataLen;
m_stream->Write(&count, postHeader.userData);
}
 
// renew levelLength
delete[] levelLength;
levelLength = new UINT32[m_nLevels];
if (!levelLength) ReturnWithError(InsufficientMemory);
for (UINT8 l = 0; l < m_nLevels; l++) levelLength[l] = 0;
m_levelLength = levelLength;
 
// write dummy levelLength
m_levelLengthPos = m_stream->GetPos();
count = m_nLevels*WordBytes;
m_stream->Write(&count, m_levelLength);
 
// save current file position
SetBufferStartPos();
}
 
//////////////////////////////////////////////////////
// Destructor
CEncoder::~CEncoder() {
delete m_currentBlock;
delete[] m_macroBlocks;
}
 
/////////////////////////////////////////////////////////////////////
/// Partitions a rectangular region of a given subband.
/// Partitioning scheme: The plane is partitioned in squares of side length LinBlockSize.
/// Write wavelet coefficients into buffer.
/// It might throw an IOException.
/// @param band A subband
/// @param width The width of the rectangle
/// @param height The height of the rectangle
/// @param startPos The buffer position of the top left corner of the rectangular region
/// @param pitch The number of bytes in row of the subband
void CEncoder::Partition(CSubband* band, int width, int height, int startPos, int pitch) THROW_ {
ASSERT(band);
 
const div_t hh = div(height, LinBlockSize);
const div_t ww = div(width, LinBlockSize);
const int ws = pitch - LinBlockSize;
const int wr = pitch - ww.rem;
int pos, base = startPos, base2;
 
// main height
for (int i=0; i < hh.quot; i++) {
// main width
base2 = base;
for (int j=0; j < ww.quot; j++) {
pos = base2;
for (int y=0; y < LinBlockSize; y++) {
for (int x=0; x < LinBlockSize; x++) {
WriteValue(band, pos);
pos++;
}
pos += ws;
}
base2 += LinBlockSize;
}
// rest of width
pos = base2;
for (int y=0; y < LinBlockSize; y++) {
for (int x=0; x < ww.rem; x++) {
WriteValue(band, pos);
pos++;
}
pos += wr;
base += pitch;
}
}
// main width
base2 = base;
for (int j=0; j < ww.quot; j++) {
// rest of height
pos = base2;
for (int y=0; y < hh.rem; y++) {
for (int x=0; x < LinBlockSize; x++) {
WriteValue(band, pos);
pos++;
}
pos += ws;
}
base2 += LinBlockSize;
}
// rest of height
pos = base2;
for (int y=0; y < hh.rem; y++) {
// rest of width
for (int x=0; x < ww.rem; x++) {
WriteValue(band, pos);
pos++;
}
pos += wr;
}
}
 
//////////////////////////////////////////////////////
/// Pad buffer with zeros and encode buffer.
/// It might throw an IOException.
void CEncoder::Flush() THROW_ {
// pad buffer with zeros
memset(&(m_currentBlock->m_value[m_currentBlock->m_valuePos]), 0, (BufferSize - m_currentBlock->m_valuePos)*DataTSize);
m_currentBlock->m_valuePos = BufferSize;
 
// encode buffer
m_forceWriting = true; // makes sure that the following EncodeBuffer is really written into the stream
EncodeBuffer(ROIBlockHeader(m_currentBlock->m_valuePos, true));
}
 
//////////////////////////////////////////////////////
/// Write levelLength into header.
/// @return number of bytes written into stream
/// It might throw an IOException.
UINT32 CEncoder::WriteLevelLength() THROW_ {
UINT64 curPos = m_stream->GetPos();
UINT32 retValue = UINT32(curPos - m_startPosition);
 
if (m_levelLength) {
// append levelLength to file, directly after post-header
// set file pos to levelLength
m_stream->SetPos(FSFromStart, m_levelLengthPos);
#ifdef PGF_USE_BIG_ENDIAN
UINT32 levelLength;
int count = WordBytes;
 
for (int i=0; i < m_currLevelIndex; i++) {
levelLength = __VAL(UINT32(m_levelLength[i]));
m_stream->Write(&count, &levelLength);
}
#else
int count = m_currLevelIndex*WordBytes;
 
m_stream->Write(&count, m_levelLength);
#endif //PGF_USE_BIG_ENDIAN
 
// restore file position
m_stream->SetPos(FSFromStart, curPos);
}
 
return retValue;
}
 
/////////////////////////////////////////////////////////////////////
// Stores band value from given position bandPos into buffer m_value at position m_valuePos
// If buffer is full encode it to file
// It might throw an IOException.
void CEncoder::WriteValue(CSubband* band, int bandPos) THROW_ {
if (m_currentBlock->m_valuePos == BufferSize) {
EncodeBuffer(ROIBlockHeader(BufferSize, false));
}
DataT val = m_currentBlock->m_value[m_currentBlock->m_valuePos++] = band->GetData(bandPos);
UINT32 v = abs(val);
if (v > m_currentBlock->m_maxAbsValue) m_currentBlock->m_maxAbsValue = v;
}
 
/////////////////////////////////////////////////////////////////////
// Write encoded macro block into stream.
// It might throw an IOException.
void CEncoder::WriteMacroBlock(CMacroBlock* block) THROW_ {
ASSERT(block);
 
ROIBlockHeader h = block->m_header;
UINT16 wordLen = UINT16(NumberOfWords(block->m_codePos)); ASSERT(wordLen <= BufferSize);
int count = sizeof(UINT16);
 
#ifdef TRACE
//UINT32 filePos = (UINT32)m_stream->GetPos();
//printf("EncodeBuffer: %d\n", filePos);
#endif
 
#ifdef PGF_USE_BIG_ENDIAN
// write wordLen
UINT16 wl = __VAL(wordLen);
m_stream->Write(&count, &wl); ASSERT(count == sizeof(UINT16));
 
#ifdef __PGFROISUPPORT__
// write ROIBlockHeader
if (m_roi) {
h.val = __VAL(h.val);
m_stream->Write(&count, &h.val); ASSERT(count == sizeof(UINT16));
}
#endif // __PGFROISUPPORT__
 
// convert data
for (int i=0; i < wordLen; i++) {
m_codeBuffer[i] = __VAL(m_codeBuffer[i]);
}
#else
// write wordLen
m_stream->Write(&count, &wordLen); ASSERT(count == sizeof(UINT16));
 
#ifdef __PGFROISUPPORT__
// write ROIBlockHeader
if (m_roi) {
m_stream->Write(&count, &h.val); ASSERT(count == sizeof(UINT16));
}
#endif // __PGFROISUPPORT__
#endif // PGF_USE_BIG_ENDIAN
 
// write encoded data into stream
count = wordLen*WordBytes;
m_stream->Write(&count, block->m_codeBuffer);
 
// store levelLength
if (m_levelLength) {
// store level length
// EncodeBuffer has been called after m_lastLevelIndex has been updated
m_levelLength[m_currLevelIndex] += ComputeBufferLength();
m_currLevelIndex = block->m_lastLevelIndex + 1;
 
}
 
// prepare for next buffer
SetBufferStartPos();
 
// reset values
block->m_valuePos = 0;
block->m_maxAbsValue = 0;
}
 
/////////////////////////////////////////////////////////////////////
// Encode buffer and write data into stream.
// h contains buffer size and flag indicating end of tile.
// Encoding scheme: <wordLen>(16 bits) [ ROI ] data
// ROI ::= <bufferSize>(15 bits) <eofTile>(1 bit)
// It might throw an IOException.
void CEncoder::EncodeBuffer(ROIBlockHeader h) THROW_ {
ASSERT(m_currentBlock);
#ifdef __PGFROISUPPORT__
ASSERT(m_roi && h.rbh.bufferSize <= BufferSize || h.rbh.bufferSize == BufferSize);
#else
ASSERT(h.rbh.bufferSize == BufferSize);
#endif
m_currentBlock->m_header = h;
 
// macro block management
if (m_macroBlockLen == 1) {
m_currentBlock->BitplaneEncode();
WriteMacroBlock(m_currentBlock);
} else {
// save last level index
int lastLevelIndex = m_currentBlock->m_lastLevelIndex;
 
if (m_forceWriting || m_lastMacroBlock == m_macroBlockLen) {
// encode macro blocks
/*
volatile OSError error = NoError;
#pragma omp parallel for ordered default(shared)
for (int i=0; i < m_lastMacroBlock; i++) {
if (error == NoError) {
m_macroBlocks[i]->BitplaneEncode();
#pragma omp ordered
{
try {
WriteMacroBlock(m_macroBlocks[i]);
} catch (IOException& e) {
error = e.error;
}
delete m_macroBlocks[i]; m_macroBlocks[i] = 0;
}
}
}
if (error != NoError) ReturnWithError(error);
*/
#pragma omp parallel for default(shared) //no declared exceptions in next block
for (int i=0; i < m_lastMacroBlock; i++) {
m_macroBlocks[i]->BitplaneEncode();
}
for (int i=0; i < m_lastMacroBlock; i++) {
WriteMacroBlock(m_macroBlocks[i]);
}
 
// prepare for next round
m_forceWriting = false;
m_lastMacroBlock = 0;
}
// re-initialize macro block
m_currentBlock = m_macroBlocks[m_lastMacroBlock++];
m_currentBlock->Init(lastLevelIndex);
}
}
 
////////////////////////////////////////////////////////
// Encode buffer of given size using bit plane coding.
// A buffer contains bufferLen UINT32 values, thus, bufferSize bits per bit plane.
// Following coding scheme is used:
// Buffer ::= <nPlanes>(5 bits) foreach(plane i): Plane[i]
// Plane[i] ::= [ Sig1 | Sig2 ] [DWORD alignment] refBits
// Sig1 ::= 1 <codeLen>(15 bits) codedSigAndSignBits
// Sig2 ::= 0 <sigLen>(15 bits) [Sign1 | Sign2 ] sigBits
// Sign1 ::= 1 <codeLen>(15 bits) [DWORD alignment] codedSignBits
// Sign2 ::= 0 <signLen>(15 bits) [DWORD alignment] signBits
void CEncoder::CMacroBlock::BitplaneEncode() {
UINT8 nPlanes;
UINT32 sigLen, codeLen = 0, wordPos, refLen, signLen;
UINT32 sigBits[BufferLen] = { 0 };
UINT32 refBits[BufferLen] = { 0 };
UINT32 signBits[BufferLen] = { 0 };
UINT32 planeMask;
UINT32 bufferSize = m_header.rbh.bufferSize; ASSERT(bufferSize <= BufferSize);
bool useRL;
//const UINT32 bufferLen = NumberOfWords(m_bufferSize);
 
#ifdef TRACE
//printf("which thread: %d\n", omp_get_thread_num());
#endif
 
// clear significance vector
for (UINT32 k=0; k < bufferSize; k++) {
m_sigFlagVector[k] = false;
}
m_sigFlagVector[bufferSize] = true; // sentinel
 
// clear output buffer
for (UINT32 k=0; k < bufferSize; k++) {
m_codeBuffer[k] = 0;
}
m_codePos = 0;
 
// compute number of bit planes and split buffer into separate bit planes
nPlanes = NumberOfBitplanes();
 
// write number of bit planes to m_codeBuffer
SetValueBlock(m_codeBuffer, 0, nPlanes, MaxBitPlanesLog);
m_codePos += MaxBitPlanesLog;
 
// loop through all bit planes
if (nPlanes == 0) nPlanes = MaxBitPlanes + 1;
planeMask = 1 << (nPlanes - 1);
 
for (int plane = nPlanes - 1; plane >= 0; plane--) {
// clear significant bitset
for (UINT32 k=0; k < BufferLen; k++) {
sigBits[k] = 0;
}
 
// split bitplane in significant bitset and refinement bitset
sigLen = DecomposeBitplane(bufferSize, planeMask, m_codePos + RLblockSizeLen + 1, sigBits, refBits, signBits, signLen, codeLen);
 
if (sigLen > 0 && codeLen <= MaxCodeLen && codeLen < AlignWordPos(sigLen) + AlignWordPos(signLen) + 2*RLblockSizeLen) {
// set RL code bit
SetBit(m_codeBuffer, m_codePos++);
 
// write length codeLen to m_codeBuffer
SetValueBlock(m_codeBuffer, m_codePos, codeLen, RLblockSizeLen);
m_codePos += RLblockSizeLen + codeLen;
} else {
#ifdef TRACE
//printf("new\n");
//for (UINT32 i=0; i < bufferSize; i++) {
// printf("%s", (GetBit(sigBits, i)) ? "1" : "_");
// if (i%120 == 119) printf("\n");
//}
//printf("\n");
#endif // TRACE
 
// run-length coding wasn't efficient enough
// we don't use RL coding for sigBits
ClearBit(m_codeBuffer, m_codePos++);
 
// write length sigLen to m_codeBuffer
ASSERT(sigLen <= MaxCodeLen);
SetValueBlock(m_codeBuffer, m_codePos, sigLen, RLblockSizeLen);
m_codePos += RLblockSizeLen;
 
if (m_encoder->m_favorSpeed || signLen == 0) {
useRL = false;
} else {
// overwrite m_codeBuffer
useRL = true;
// run-length encode m_sign and append them to the m_codeBuffer
codeLen = RLESigns(m_codePos + RLblockSizeLen + 1, signBits, signLen);
}
 
if (useRL && codeLen <= MaxCodeLen && codeLen < signLen) {
// RL encoding of m_sign was efficient
// write RL code bit
SetBit(m_codeBuffer, m_codePos++);
 
// write codeLen to m_codeBuffer
SetValueBlock(m_codeBuffer, m_codePos, codeLen, RLblockSizeLen);
 
// compute position of sigBits
wordPos = NumberOfWords(m_codePos + codeLen + RLblockSizeLen);
ASSERT(0 <= wordPos && wordPos < BufferSize);
} else {
// RL encoding of signBits wasn't efficient
// clear RL code bit
ClearBit(m_codeBuffer, m_codePos++);
 
// write signLen to m_codeBuffer
ASSERT(signLen <= MaxCodeLen);
SetValueBlock(m_codeBuffer, m_codePos, signLen, RLblockSizeLen);
 
// write signBits to m_codeBuffer
wordPos = NumberOfWords(m_codePos + RLblockSizeLen);
ASSERT(0 <= wordPos && wordPos < BufferSize);
codeLen = NumberOfWords(signLen);
 
for (UINT32 k=0; k < codeLen; k++) {
m_codeBuffer[wordPos++] = signBits[k];
}
 
}
 
// write sigBits
ASSERT(0 <= wordPos && wordPos < BufferSize);
refLen = NumberOfWords(sigLen);
 
for (UINT32 k=0; k < refLen; k++) {
m_codeBuffer[wordPos++] = sigBits[k];
}
m_codePos = wordPos << WordWidthLog;
}
 
// append refinement bitset (aligned to word boundary)
wordPos = NumberOfWords(m_codePos);
ASSERT(0 <= wordPos && wordPos < BufferSize);
refLen = NumberOfWords(bufferSize - sigLen);
 
for (UINT32 k=0; k < refLen; k++) {
m_codeBuffer[wordPos++] = refBits[k];
}
m_codePos = wordPos << WordWidthLog;
planeMask >>= 1;
}
ASSERT(0 <= m_codePos && m_codePos <= CodeBufferBitLen);
}
 
//////////////////////////////////////////////////////////
// Split bitplane of length bufferSize into significant and refinement bitset
// returns length [bits] of significant bits
// input: bufferSize, planeMask, codePos
// output: sigBits, refBits, signBits, signLen [bits], codeLen [bits]
// RLE
// - Encode run of 2^k zeros by a single 0.
// - Encode run of count 0's followed by a 1 with codeword: 1<count>x
// - x is 0: if a positive sign is stored, otherwise 1
// - Store each bit in m_codeBuffer[codePos] and increment codePos.
UINT32 CEncoder::CMacroBlock::DecomposeBitplane(UINT32 bufferSize, UINT32 planeMask, UINT32 codePos, UINT32* sigBits, UINT32* refBits, UINT32* signBits, UINT32& signLen, UINT32& codeLen) {
ASSERT(sigBits);
ASSERT(refBits);
ASSERT(signBits);
ASSERT(codePos < CodeBufferBitLen);
 
UINT32 sigPos = 0;
UINT32 valuePos = 0, valueEnd;
UINT32 refPos = 0;
 
// set output value
signLen = 0;
 
// prepare RLE of Sigs and Signs
const UINT32 outStartPos = codePos;
UINT32 k = 3;
UINT32 runlen = 1 << k; // = 2^k
UINT32 count = 0;
 
while (valuePos < bufferSize) {
// search next 1 in m_sigFlagVector using searching with sentinel
valueEnd = valuePos;
while(!m_sigFlagVector[valueEnd]) { valueEnd++; }
 
// search 1's in m_value[plane][valuePos..valueEnd)
// these 1's are significant bits
while (valuePos < valueEnd) {
if (GetBitAtPos(valuePos, planeMask)) {
// RLE encoding
// encode run of count 0's followed by a 1
// with codeword: 1<count>(signBits[signPos])
SetBit(m_codeBuffer, codePos++);
if (k > 0) {
SetValueBlock(m_codeBuffer, codePos, count, k);
codePos += k;
 
// adapt k (half the zero run-length)
k--;
runlen >>= 1;
}
 
// copy and write sign bit
if (m_value[valuePos] < 0) {
SetBit(signBits, signLen++);
SetBit(m_codeBuffer, codePos++);
} else {
ClearBit(signBits, signLen++);
ClearBit(m_codeBuffer, codePos++);
}
 
// write a 1 to sigBits
SetBit(sigBits, sigPos++);
 
// update m_sigFlagVector
m_sigFlagVector[valuePos] = true;
 
// prepare for next run
count = 0;
} else {
// RLE encoding
count++;
if (count == runlen) {
// encode run of 2^k zeros by a single 0
ClearBit(m_codeBuffer, codePos++);
// adapt k (double the zero run-length)
if (k < WordWidth) {
k++;
runlen <<= 1;
}
 
// prepare for next run
count = 0;
}
 
// write 0 to sigBits
sigPos++;
}
valuePos++;
}
// refinement bit
if (valuePos < bufferSize) {
// write one refinement bit
if (GetBitAtPos(valuePos++, planeMask)) {
SetBit(refBits, refPos);
} else {
ClearBit(refBits, refPos);
}
refPos++;
}
}
// RLE encoding of the rest of the plane
// encode run of count 0's followed by a 1
// with codeword: 1<count>(signBits[signPos])
SetBit(m_codeBuffer, codePos++);
if (k > 0) {
SetValueBlock(m_codeBuffer, codePos, count, k);
codePos += k;
}
// write dmmy sign bit
SetBit(m_codeBuffer, codePos++);
 
// write word filler zeros
 
ASSERT(sigPos <= bufferSize);
ASSERT(refPos <= bufferSize);
ASSERT(signLen <= bufferSize);
ASSERT(valuePos == bufferSize);
ASSERT(codePos >= outStartPos && codePos < CodeBufferBitLen);
codeLen = codePos - outStartPos;
 
return sigPos;
}
 
 
///////////////////////////////////////////////////////
// Compute number of bit planes needed
UINT8 CEncoder::CMacroBlock::NumberOfBitplanes() {
UINT8 cnt = 0;
 
// determine number of bitplanes for max value
if (m_maxAbsValue > 0) {
while (m_maxAbsValue > 0) {
m_maxAbsValue >>= 1; cnt++;
}
if (cnt == MaxBitPlanes + 1) cnt = 0;
// end cs
ASSERT(cnt <= MaxBitPlanes);
ASSERT((cnt >> MaxBitPlanesLog) == 0);
return cnt;
} else {
return 1;
}
}
 
//////////////////////////////////////////////////////
// Adaptive Run-Length encoder for long sequences of ones.
// Returns length of output in bits.
// - Encode run of 2^k ones by a single 1.
// - Encode run of count 1's followed by a 0 with codeword: 0<count>.
// - Store each bit in m_codeBuffer[codePos] and increment codePos.
UINT32 CEncoder::CMacroBlock::RLESigns(UINT32 codePos, UINT32* signBits, UINT32 signLen) {
ASSERT(signBits);
ASSERT(0 <= codePos && codePos < CodeBufferBitLen);
ASSERT(0 < signLen && signLen <= BufferSize);
 
const UINT32 outStartPos = codePos;
UINT32 k = 0;
UINT32 runlen = 1 << k; // = 2^k
UINT32 count = 0;
UINT32 signPos = 0;
 
while (signPos < signLen) {
// search next 0 in signBits starting at position signPos
count = SeekBit1Range(signBits, signPos, __min(runlen, signLen - signPos));
// count 1's found
if (count == runlen) {
// encode run of 2^k ones by a single 1
signPos += count;
SetBit(m_codeBuffer, codePos++);
// adapt k (double the 1's run-length)
if (k < WordWidth) {
k++;
runlen <<= 1;
}
} else {
// encode run of count 1's followed by a 0
// with codeword: 0(count)
signPos += count + 1;
ClearBit(m_codeBuffer, codePos++);
if (k > 0) {
SetValueBlock(m_codeBuffer, codePos, count, k);
codePos += k;
}
// adapt k (half the 1's run-length)
if (k > 0) {
k--;
runlen >>= 1;
}
}
}
ASSERT(signPos == signLen || signPos == signLen + 1);
ASSERT(codePos >= outStartPos && codePos < CodeBufferBitLen);
return codePos - outStartPos;
}
 
//////////////////////////////////////////////////////
#ifdef TRACE
void CEncoder::DumpBuffer() const {
//printf("\nDump\n");
//for (UINT32 i=0; i < BufferSize; i++) {
// printf("%d", m_value[i]);
//}
//printf("\n");
}
#endif //TRACE
 
 
/*
* The Progressive Graphics File; http://www.libpgf.org
*
* $Date: 2007-02-03 13:04:21 +0100 (Sa, 03 Feb 2007) $
* $Revision: 280 $
*
* This file Copyright (C) 2006 xeraina GmbH, Switzerland
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
 
//////////////////////////////////////////////////////////////////////
/// @file Encoder.cpp
/// @brief PGF encoder class implementation
/// @author C. Stamm, R. Spuler
 
#include "Encoder.h"
#ifdef TRACE
#include <stdio.h>
#endif
 
//////////////////////////////////////////////////////
// PGF: file structure
//
// PGFPreHeader PGFHeader PGFPostHeader LevelLengths Level_n-1 Level_n-2 ... Level_0
// PGFPostHeader ::= [ColorTable] [UserData]
// LevelLengths ::= UINT32[nLevels]
 
//////////////////////////////////////////////////////
// Encoding scheme
// input: wavelet coefficients stored in subbands
// output: binary file
//
// subband
// |
// m_value [BufferSize]
// | | |
// m_sign sigBits refBits [BufferSize, BufferLen, BufferLen]
// | | |
// m_codeBuffer (for each plane: RLcodeLength (16 bit), RLcoded sigBits + m_sign, refBits)
// |
// file (for each buffer: packedLength (16 bit), packed bits)
//
 
// Constants
#define CodeBufferBitLen (CodeBufferLen*WordWidth) ///< max number of bits in m_codeBuffer
#define MaxCodeLen ((1 << RLblockSizeLen) - 1) ///< max length of RL encoded block
 
//////////////////////////////////////////////////////
/// Write pre-header, header, postHeader, and levelLength.
/// It might throw an IOException.
/// @param stream A PGF stream
/// @param preHeader A already filled in PGF pre-header
/// @param header An already filled in PGF header
/// @param postHeader [in] An already filled in PGF post-header (containing color table, user data, ...)
/// @param userDataPos [out] File position of user data
/// @param useOMP If true, then the encoder will use multi-threading based on openMP
CEncoder::CEncoder(CPGFStream* stream, PGFPreHeader preHeader, PGFHeader header, const PGFPostHeader& postHeader, UINT64& userDataPos, bool useOMP) THROW_
: m_stream(stream)
, m_bufferStartPos(0)
, m_currLevelIndex(0)
, m_nLevels(header.nLevels)
, m_favorSpeed(false)
, m_forceWriting(false)
#ifdef __PGFROISUPPORT__
, m_roi(false)
#endif
{
ASSERT(m_stream);
 
int count;
m_lastMacroBlock = 0;
m_levelLength = NULL;
 
// set number of threads
#ifdef LIBPGF_USE_OPENMP
m_macroBlockLen = omp_get_num_procs();
#else
m_macroBlockLen = 1;
#endif
if (useOMP && m_macroBlockLen > 1) {
#ifdef LIBPGF_USE_OPENMP
omp_set_num_threads(m_macroBlockLen);
#endif
// create macro block array
m_macroBlocks = new(std::nothrow) CMacroBlock*[m_macroBlockLen];
if (!m_macroBlocks) ReturnWithError(InsufficientMemory);
for (int i=0; i < m_macroBlockLen; i++) m_macroBlocks[i] = new CMacroBlock(this);
m_currentBlock = m_macroBlocks[m_lastMacroBlock++];
} else {
m_macroBlocks = 0;
m_macroBlockLen = 1;
m_currentBlock = new CMacroBlock(this);
}
 
// save file position
m_startPosition = m_stream->GetPos();
 
// write preHeader
preHeader.hSize = __VAL(preHeader.hSize);
count = PreHeaderSize;
m_stream->Write(&count, &preHeader);
 
// write file header
header.height = __VAL(header.height);
header.width = __VAL(header.width);
count = HeaderSize;
m_stream->Write(&count, &header);
 
// write postHeader
if (header.mode == ImageModeIndexedColor) {
// write color table
count = ColorTableSize;
m_stream->Write(&count, (void *)postHeader.clut);
}
// save user data file position
userDataPos = m_stream->GetPos();
if (postHeader.userDataLen) {
if (postHeader.userData) {
// write user data
count = postHeader.userDataLen;
m_stream->Write(&count, postHeader.userData);
} else {
m_stream->SetPos(FSFromCurrent, count);
}
}
 
// save level length file position
m_levelLengthPos = m_stream->GetPos();
}
 
//////////////////////////////////////////////////////
// Destructor
CEncoder::~CEncoder() {
if (m_macroBlocks) {
for (int i=0; i < m_macroBlockLen; i++) delete m_macroBlocks[i];
delete[] m_macroBlocks;
} else {
delete m_currentBlock;
}
}
 
/////////////////////////////////////////////////////////////////////
/// Increase post-header size and write new size into stream.
/// @param preHeader An already filled in PGF pre-header
/// It might throw an IOException.
void CEncoder::UpdatePostHeaderSize(PGFPreHeader preHeader) THROW_ {
UINT64 curPos = m_stream->GetPos(); // end of user data
int count = PreHeaderSize;
 
// write preHeader
m_stream->SetPos(FSFromStart, m_startPosition);
preHeader.hSize = __VAL(preHeader.hSize);
m_stream->Write(&count, &preHeader);
 
m_stream->SetPos(FSFromStart, curPos);
}
 
/////////////////////////////////////////////////////////////////////
/// Create level length data structure and write a place holder into stream.
/// It might throw an IOException.
/// @param levelLength A reference to an integer array, large enough to save the relative file positions of all PGF levels
/// @return number of bytes written into stream
UINT32 CEncoder::WriteLevelLength(UINT32*& levelLength) THROW_ {
// renew levelLength
delete[] levelLength;
levelLength = new(std::nothrow) UINT32[m_nLevels];
if (!levelLength) ReturnWithError(InsufficientMemory);
for (UINT8 l = 0; l < m_nLevels; l++) levelLength[l] = 0;
m_levelLength = levelLength;
 
// save level length file position
m_levelLengthPos = m_stream->GetPos();
 
// write dummy levelLength
int count = m_nLevels*WordBytes;
m_stream->Write(&count, m_levelLength);
 
// save current file position
SetBufferStartPos();
 
return count;
}
 
//////////////////////////////////////////////////////
/// Write new levelLength into stream.
/// It might throw an IOException.
/// @return Written image bytes.
UINT32 CEncoder::UpdateLevelLength() THROW_ {
UINT64 curPos = m_stream->GetPos(); // end of image
 
// set file pos to levelLength
m_stream->SetPos(FSFromStart, m_levelLengthPos);
 
if (m_levelLength) {
#ifdef PGF_USE_BIG_ENDIAN
UINT32 levelLength;
int count = WordBytes;
for (int i=0; i < m_currLevelIndex; i++) {
levelLength = __VAL(UINT32(m_levelLength[i]));
m_stream->Write(&count, &levelLength);
}
#else
int count = m_currLevelIndex*WordBytes;
m_stream->Write(&count, m_levelLength);
#endif //PGF_USE_BIG_ENDIAN
} else {
int count = m_currLevelIndex*WordBytes;
m_stream->SetPos(FSFromCurrent, count);
}
 
// begin of image
UINT32 retValue = UINT32(curPos - m_stream->GetPos());
// restore file position
m_stream->SetPos(FSFromStart, curPos);
 
return retValue;
}
 
/////////////////////////////////////////////////////////////////////
/// Partitions a rectangular region of a given subband.
/// Partitioning scheme: The plane is partitioned in squares of side length LinBlockSize.
/// Write wavelet coefficients from subband into the input buffer of a macro block.
/// It might throw an IOException.
/// @param band A subband
/// @param width The width of the rectangle
/// @param height The height of the rectangle
/// @param startPos The absolute subband position of the top left corner of the rectangular region
/// @param pitch The number of bytes in row of the subband
void CEncoder::Partition(CSubband* band, int width, int height, int startPos, int pitch) THROW_ {
ASSERT(band);
 
const div_t hh = div(height, LinBlockSize);
const div_t ww = div(width, LinBlockSize);
const int ws = pitch - LinBlockSize;
const int wr = pitch - ww.rem;
int pos, base = startPos, base2;
 
// main height
for (int i=0; i < hh.quot; i++) {
// main width
base2 = base;
for (int j=0; j < ww.quot; j++) {
pos = base2;
for (int y=0; y < LinBlockSize; y++) {
for (int x=0; x < LinBlockSize; x++) {
WriteValue(band, pos);
pos++;
}
pos += ws;
}
base2 += LinBlockSize;
}
// rest of width
pos = base2;
for (int y=0; y < LinBlockSize; y++) {
for (int x=0; x < ww.rem; x++) {
WriteValue(band, pos);
pos++;
}
pos += wr;
base += pitch;
}
}
// main width
base2 = base;
for (int j=0; j < ww.quot; j++) {
// rest of height
pos = base2;
for (int y=0; y < hh.rem; y++) {
for (int x=0; x < LinBlockSize; x++) {
WriteValue(band, pos);
pos++;
}
pos += ws;
}
base2 += LinBlockSize;
}
// rest of height
pos = base2;
for (int y=0; y < hh.rem; y++) {
// rest of width
for (int x=0; x < ww.rem; x++) {
WriteValue(band, pos);
pos++;
}
pos += wr;
}
}
 
//////////////////////////////////////////////////////
/// Pad buffer with zeros and encode buffer.
/// It might throw an IOException.
void CEncoder::Flush() THROW_ {
if (m_currentBlock->m_valuePos > 0) {
// pad buffer with zeros
memset(&(m_currentBlock->m_value[m_currentBlock->m_valuePos]), 0, (BufferSize - m_currentBlock->m_valuePos)*DataTSize);
m_currentBlock->m_valuePos = BufferSize;
 
// encode buffer
m_forceWriting = true; // makes sure that the following EncodeBuffer is really written into the stream
EncodeBuffer(ROIBlockHeader(m_currentBlock->m_valuePos, true));
}
}
 
/////////////////////////////////////////////////////////////////////
// Stores band value from given position bandPos into buffer m_value at position m_valuePos
// If buffer is full encode it to file
// It might throw an IOException.
void CEncoder::WriteValue(CSubband* band, int bandPos) THROW_ {
if (m_currentBlock->m_valuePos == BufferSize) {
EncodeBuffer(ROIBlockHeader(BufferSize, false));
}
DataT val = m_currentBlock->m_value[m_currentBlock->m_valuePos++] = band->GetData(bandPos);
UINT32 v = abs(val);
if (v > m_currentBlock->m_maxAbsValue) m_currentBlock->m_maxAbsValue = v;
}
 
/////////////////////////////////////////////////////////////////////
// Encode buffer and write data into stream.
// h contains buffer size and flag indicating end of tile.
// Encoding scheme: <wordLen>(16 bits) [ ROI ] data
// ROI ::= <bufferSize>(15 bits) <eofTile>(1 bit)
// It might throw an IOException.
void CEncoder::EncodeBuffer(ROIBlockHeader h) THROW_ {
ASSERT(m_currentBlock);
#ifdef __PGFROISUPPORT__
ASSERT(m_roi && h.rbh.bufferSize <= BufferSize || h.rbh.bufferSize == BufferSize);
#else
ASSERT(h.rbh.bufferSize == BufferSize);
#endif
m_currentBlock->m_header = h;
 
// macro block management
if (m_macroBlockLen == 1) {
m_currentBlock->BitplaneEncode();
WriteMacroBlock(m_currentBlock);
} else {
// save last level index
int lastLevelIndex = m_currentBlock->m_lastLevelIndex;
 
if (m_forceWriting || m_lastMacroBlock == m_macroBlockLen) {
// encode macro blocks
/*
volatile OSError error = NoError;
#ifdef LIBPGF_USE_OPENMP
#pragma omp parallel for ordered default(shared)
#endif
for (int i=0; i < m_lastMacroBlock; i++) {
if (error == NoError) {
m_macroBlocks[i]->BitplaneEncode();
#ifdef LIBPGF_USE_OPENMP
#pragma omp ordered
#endif
{
try {
WriteMacroBlock(m_macroBlocks[i]);
} catch (IOException& e) {
error = e.error;
}
delete m_macroBlocks[i]; m_macroBlocks[i] = 0;
}
}
}
if (error != NoError) ReturnWithError(error);
*/
#ifdef LIBPGF_USE_OPENMP
#pragma omp parallel for default(shared) //no declared exceptions in next block
#endif
for (int i=0; i < m_lastMacroBlock; i++) {
m_macroBlocks[i]->BitplaneEncode();
}
for (int i=0; i < m_lastMacroBlock; i++) {
WriteMacroBlock(m_macroBlocks[i]);
}
// prepare for next round
m_forceWriting = false;
m_lastMacroBlock = 0;
}
// re-initialize macro block
m_currentBlock = m_macroBlocks[m_lastMacroBlock++];
m_currentBlock->Init(lastLevelIndex);
}
}
 
/////////////////////////////////////////////////////////////////////
// Write encoded macro block into stream.
// It might throw an IOException.
void CEncoder::WriteMacroBlock(CMacroBlock* block) THROW_ {
ASSERT(block);
#ifdef __PGFROISUPPORT__
ROIBlockHeader h = block->m_header;
#endif
UINT16 wordLen = UINT16(NumberOfWords(block->m_codePos)); ASSERT(wordLen <= CodeBufferLen);
int count = sizeof(UINT16);
#ifdef TRACE
//UINT32 filePos = (UINT32)m_stream->GetPos();
//printf("EncodeBuffer: %d\n", filePos);
#endif
 
#ifdef PGF_USE_BIG_ENDIAN
// write wordLen
UINT16 wl = __VAL(wordLen);
m_stream->Write(&count, &wl); ASSERT(count == sizeof(UINT16));
 
#ifdef __PGFROISUPPORT__
// write ROIBlockHeader
if (m_roi) {
h.val = __VAL(h.val);
m_stream->Write(&count, &h.val); ASSERT(count == sizeof(UINT16));
}
#endif // __PGFROISUPPORT__
 
// convert data
for (int i=0; i < wordLen; i++) {
block->m_codeBuffer[i] = __VAL(block->m_codeBuffer[i]);
}
#else
// write wordLen
m_stream->Write(&count, &wordLen); ASSERT(count == sizeof(UINT16));
 
#ifdef __PGFROISUPPORT__
// write ROIBlockHeader
if (m_roi) {
m_stream->Write(&count, &h.val); ASSERT(count == sizeof(UINT16));
}
#endif // __PGFROISUPPORT__
#endif // PGF_USE_BIG_ENDIAN
 
// write encoded data into stream
count = wordLen*WordBytes;
m_stream->Write(&count, block->m_codeBuffer);
 
// store levelLength
if (m_levelLength) {
// store level length
// EncodeBuffer has been called after m_lastLevelIndex has been updated
ASSERT(m_currLevelIndex < m_nLevels);
m_levelLength[m_currLevelIndex] += (UINT32)ComputeBufferLength();
m_currLevelIndex = block->m_lastLevelIndex + 1;
 
}
 
// prepare for next buffer
SetBufferStartPos();
 
// reset values
block->m_valuePos = 0;
block->m_maxAbsValue = 0;
}
 
////////////////////////////////////////////////////////
// Encode buffer of given size using bit plane coding.
// A buffer contains bufferLen UINT32 values, thus, bufferSize bits per bit plane.
// Following coding scheme is used:
// Buffer ::= <nPlanes>(5 bits) foreach(plane i): Plane[i]
// Plane[i] ::= [ Sig1 | Sig2 ] [DWORD alignment] refBits
// Sig1 ::= 1 <codeLen>(15 bits) codedSigAndSignBits
// Sig2 ::= 0 <sigLen>(15 bits) [Sign1 | Sign2 ] [DWORD alignment] sigBits
// Sign1 ::= 1 <codeLen>(15 bits) codedSignBits
// Sign2 ::= 0 <signLen>(15 bits) [DWORD alignment] signBits
void CEncoder::CMacroBlock::BitplaneEncode() {
UINT8 nPlanes;
UINT32 sigLen, codeLen = 0, wordPos, refLen, signLen;
UINT32 sigBits[BufferLen] = { 0 };
UINT32 refBits[BufferLen] = { 0 };
UINT32 signBits[BufferLen] = { 0 };
UINT32 planeMask;
UINT32 bufferSize = m_header.rbh.bufferSize; ASSERT(bufferSize <= BufferSize);
bool useRL;
 
#ifdef TRACE
//printf("which thread: %d\n", omp_get_thread_num());
#endif
 
// clear significance vector
for (UINT32 k=0; k < bufferSize; k++) {
m_sigFlagVector[k] = false;
}
m_sigFlagVector[bufferSize] = true; // sentinel
 
// clear output buffer
for (UINT32 k=0; k < bufferSize; k++) {
m_codeBuffer[k] = 0;
}
m_codePos = 0;
 
// compute number of bit planes and split buffer into separate bit planes
nPlanes = NumberOfBitplanes();
 
// write number of bit planes to m_codeBuffer
// <nPlanes>
SetValueBlock(m_codeBuffer, 0, nPlanes, MaxBitPlanesLog);
m_codePos += MaxBitPlanesLog;
 
// loop through all bit planes
if (nPlanes == 0) nPlanes = MaxBitPlanes + 1;
planeMask = 1 << (nPlanes - 1);
 
for (int plane = nPlanes - 1; plane >= 0; plane--) {
// clear significant bitset
for (UINT32 k=0; k < BufferLen; k++) {
sigBits[k] = 0;
}
 
// split bitplane in significant bitset and refinement bitset
sigLen = DecomposeBitplane(bufferSize, planeMask, m_codePos + RLblockSizeLen + 1, sigBits, refBits, signBits, signLen, codeLen);
 
if (sigLen > 0 && codeLen <= MaxCodeLen && codeLen < AlignWordPos(sigLen) + AlignWordPos(signLen) + 2*RLblockSizeLen) {
// set RL code bit
// <1><codeLen>
SetBit(m_codeBuffer, m_codePos++);
 
// write length codeLen to m_codeBuffer
SetValueBlock(m_codeBuffer, m_codePos, codeLen, RLblockSizeLen);
m_codePos += RLblockSizeLen + codeLen;
} else {
#ifdef TRACE
//printf("new\n");
//for (UINT32 i=0; i < bufferSize; i++) {
// printf("%s", (GetBit(sigBits, i)) ? "1" : "_");
// if (i%120 == 119) printf("\n");
//}
//printf("\n");
#endif // TRACE
 
// run-length coding wasn't efficient enough
// we don't use RL coding for sigBits
// <0><sigLen>
ClearBit(m_codeBuffer, m_codePos++);
 
// write length sigLen to m_codeBuffer
ASSERT(sigLen <= MaxCodeLen);
SetValueBlock(m_codeBuffer, m_codePos, sigLen, RLblockSizeLen);
m_codePos += RLblockSizeLen;
 
if (m_encoder->m_favorSpeed || signLen == 0) {
useRL = false;
} else {
// overwrite m_codeBuffer
useRL = true;
// run-length encode m_sign and append them to the m_codeBuffer
codeLen = RLESigns(m_codePos + RLblockSizeLen + 1, signBits, signLen);
}
 
if (useRL && codeLen <= MaxCodeLen && codeLen < signLen) {
// RL encoding of m_sign was efficient
// <1><codeLen><codedSignBits>_
// write RL code bit
SetBit(m_codeBuffer, m_codePos++);
// write codeLen to m_codeBuffer
SetValueBlock(m_codeBuffer, m_codePos, codeLen, RLblockSizeLen);
 
// compute position of sigBits
wordPos = NumberOfWords(m_codePos + RLblockSizeLen + codeLen);
ASSERT(0 <= wordPos && wordPos < CodeBufferLen);
} else {
// RL encoding of signBits wasn't efficient
// <0><signLen>_<signBits>_
// clear RL code bit
ClearBit(m_codeBuffer, m_codePos++);
 
// write signLen to m_codeBuffer
ASSERT(signLen <= MaxCodeLen);
SetValueBlock(m_codeBuffer, m_codePos, signLen, RLblockSizeLen);
 
// write signBits to m_codeBuffer
wordPos = NumberOfWords(m_codePos + RLblockSizeLen);
ASSERT(0 <= wordPos && wordPos < CodeBufferLen);
codeLen = NumberOfWords(signLen);
 
for (UINT32 k=0; k < codeLen; k++) {
m_codeBuffer[wordPos++] = signBits[k];
}
}
 
// write sigBits
// <sigBits>_
ASSERT(0 <= wordPos && wordPos < CodeBufferLen);
refLen = NumberOfWords(sigLen);
 
for (UINT32 k=0; k < refLen; k++) {
m_codeBuffer[wordPos++] = sigBits[k];
}
m_codePos = wordPos << WordWidthLog;
}
 
// append refinement bitset (aligned to word boundary)
// _<refBits>
wordPos = NumberOfWords(m_codePos);
ASSERT(0 <= wordPos && wordPos < CodeBufferLen);
refLen = NumberOfWords(bufferSize - sigLen);
 
for (UINT32 k=0; k < refLen; k++) {
m_codeBuffer[wordPos++] = refBits[k];
}
m_codePos = wordPos << WordWidthLog;
planeMask >>= 1;
}
ASSERT(0 <= m_codePos && m_codePos <= CodeBufferBitLen);
}
 
//////////////////////////////////////////////////////////
// Split bitplane of length bufferSize into significant and refinement bitset
// returns length [bits] of significant bits
// input: bufferSize, planeMask, codePos
// output: sigBits, refBits, signBits, signLen [bits], codeLen [bits]
// RLE
// - Encode run of 2^k zeros by a single 0.
// - Encode run of count 0's followed by a 1 with codeword: 1<count>x
// - x is 0: if a positive sign is stored, otherwise 1
// - Store each bit in m_codeBuffer[codePos] and increment codePos.
UINT32 CEncoder::CMacroBlock::DecomposeBitplane(UINT32 bufferSize, UINT32 planeMask, UINT32 codePos, UINT32* sigBits, UINT32* refBits, UINT32* signBits, UINT32& signLen, UINT32& codeLen) {
ASSERT(sigBits);
ASSERT(refBits);
ASSERT(signBits);
ASSERT(codePos < CodeBufferBitLen);
 
UINT32 sigPos = 0;
UINT32 valuePos = 0, valueEnd;
UINT32 refPos = 0;
 
// set output value
signLen = 0;
 
// prepare RLE of Sigs and Signs
const UINT32 outStartPos = codePos;
UINT32 k = 3;
UINT32 runlen = 1 << k; // = 2^k
UINT32 count = 0;
 
while (valuePos < bufferSize) {
// search next 1 in m_sigFlagVector using searching with sentinel
valueEnd = valuePos;
while(!m_sigFlagVector[valueEnd]) { valueEnd++; }
 
// search 1's in m_value[plane][valuePos..valueEnd)
// these 1's are significant bits
while (valuePos < valueEnd) {
if (GetBitAtPos(valuePos, planeMask)) {
// RLE encoding
// encode run of count 0's followed by a 1
// with codeword: 1<count>(signBits[signPos])
SetBit(m_codeBuffer, codePos++);
if (k > 0) {
SetValueBlock(m_codeBuffer, codePos, count, k);
codePos += k;
 
// adapt k (half the zero run-length)
k--;
runlen >>= 1;
}
 
// copy and write sign bit
if (m_value[valuePos] < 0) {
SetBit(signBits, signLen++);
SetBit(m_codeBuffer, codePos++);
} else {
ClearBit(signBits, signLen++);
ClearBit(m_codeBuffer, codePos++);
}
 
// write a 1 to sigBits
SetBit(sigBits, sigPos++);
 
// update m_sigFlagVector
m_sigFlagVector[valuePos] = true;
 
// prepare for next run
count = 0;
} else {
// RLE encoding
count++;
if (count == runlen) {
// encode run of 2^k zeros by a single 0
ClearBit(m_codeBuffer, codePos++);
// adapt k (double the zero run-length)
if (k < WordWidth) {
k++;
runlen <<= 1;
}
 
// prepare for next run
count = 0;
}
 
// write 0 to sigBits
sigPos++;
}
valuePos++;
}
// refinement bit
if (valuePos < bufferSize) {
// write one refinement bit
if (GetBitAtPos(valuePos++, planeMask)) {
SetBit(refBits, refPos);
} else {
ClearBit(refBits, refPos);
}
refPos++;
}
}
// RLE encoding of the rest of the plane
// encode run of count 0's followed by a 1
// with codeword: 1<count>(signBits[signPos])
SetBit(m_codeBuffer, codePos++);
if (k > 0) {
SetValueBlock(m_codeBuffer, codePos, count, k);
codePos += k;
}
// write dmmy sign bit
SetBit(m_codeBuffer, codePos++);
 
// write word filler zeros
 
ASSERT(sigPos <= bufferSize);
ASSERT(refPos <= bufferSize);
ASSERT(signLen <= bufferSize);
ASSERT(valuePos == bufferSize);
ASSERT(codePos >= outStartPos && codePos < CodeBufferBitLen);
codeLen = codePos - outStartPos;
 
return sigPos;
}
 
 
///////////////////////////////////////////////////////
// Compute number of bit planes needed
UINT8 CEncoder::CMacroBlock::NumberOfBitplanes() {
UINT8 cnt = 0;
 
// determine number of bitplanes for max value
if (m_maxAbsValue > 0) {
while (m_maxAbsValue > 0) {
m_maxAbsValue >>= 1; cnt++;
}
if (cnt == MaxBitPlanes + 1) cnt = 0;
// end cs
ASSERT(cnt <= MaxBitPlanes);
ASSERT((cnt >> MaxBitPlanesLog) == 0);
return cnt;
} else {
return 1;
}
}
 
//////////////////////////////////////////////////////
// Adaptive Run-Length encoder for long sequences of ones.
// Returns length of output in bits.
// - Encode run of 2^k ones by a single 1.
// - Encode run of count 1's followed by a 0 with codeword: 0<count>.
// - Store each bit in m_codeBuffer[codePos] and increment codePos.
UINT32 CEncoder::CMacroBlock::RLESigns(UINT32 codePos, UINT32* signBits, UINT32 signLen) {
ASSERT(signBits);
ASSERT(0 <= codePos && codePos < CodeBufferBitLen);
ASSERT(0 < signLen && signLen <= BufferSize);
const UINT32 outStartPos = codePos;
UINT32 k = 0;
UINT32 runlen = 1 << k; // = 2^k
UINT32 count = 0;
UINT32 signPos = 0;
 
while (signPos < signLen) {
// search next 0 in signBits starting at position signPos
count = SeekBit1Range(signBits, signPos, __min(runlen, signLen - signPos));
// count 1's found
if (count == runlen) {
// encode run of 2^k ones by a single 1
signPos += count;
SetBit(m_codeBuffer, codePos++);
// adapt k (double the 1's run-length)
if (k < WordWidth) {
k++;
runlen <<= 1;
}
} else {
// encode run of count 1's followed by a 0
// with codeword: 0(count)
signPos += count + 1;
ClearBit(m_codeBuffer, codePos++);
if (k > 0) {
SetValueBlock(m_codeBuffer, codePos, count, k);
codePos += k;
}
// adapt k (half the 1's run-length)
if (k > 0) {
k--;
runlen >>= 1;
}
}
}
ASSERT(signPos == signLen || signPos == signLen + 1);
ASSERT(codePos >= outStartPos && codePos < CodeBufferBitLen);
return codePos - outStartPos;
}
 
//////////////////////////////////////////////////////
#ifdef TRACE
void CEncoder::DumpBuffer() const {
//printf("\nDump\n");
//for (UINT32 i=0; i < BufferSize; i++) {
// printf("%d", m_value[i]);
//}
//printf("\n");
}
#endif //TRACE
 
 
/trunk/Scribus/scribus/third_party/pgf/Encoder.h
1,194 → 1,231
/*
* The Progressive Graphics File; http://www.libpgf.org
*
* $Date: 2006-06-04 22:05:59 +0200 (So, 04 Jun 2006) $
* $Revision: 229 $
*
* This file Copyright (C) 2006 xeraina GmbH, Switzerland
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
 
//////////////////////////////////////////////////////////////////////
/// @file Encoder.h
/// @brief PGF encoder class
/// @author C. Stamm, R. Spuler
 
#ifndef PGF_ENCODER_H
#define PGF_ENCODER_H
 
#include "PGFstream.h"
#include "BitStream.h"
#include "Subband.h"
#include "WaveletTransform.h"
 
/////////////////////////////////////////////////////////////////////
// Constants
#define BufferLen (BufferSize/WordWidth) // number of words per buffer
 
/////////////////////////////////////////////////////////////////////
/// PGF encoder class.
/// @author C. Stamm
/// @brief PGF encoder
class CEncoder {
//////////////////////////////////////////////////////////////////////
/// PGF encoder macro block class.
/// @author C. Stamm, I. Bauersachs
/// @brief A macro block is an encoding unit of fixed size (uncoded)
class CMacroBlock {
public:
CMacroBlock(CEncoder *encoder)
: m_header(0)
, m_encoder(encoder)
{
ASSERT(m_encoder);
Init(-1);
}
 
DataT m_value[BufferSize]; // input buffer of values with index m_valuePos
UINT32 m_codeBuffer[BufferSize]; // output buffer for encoded bitstream
 
ROIBlockHeader m_header; // block header
UINT32 m_valuePos; // current buffer position
UINT32 m_maxAbsValue; // maximum absolute coefficient in each buffer
UINT32 m_codePos; // current position in encoded bitstream
int m_lastLevelIndex; // index of last encoded level: [0, nLevels); used because a level-end can occur before a buffer is full
 
void Init(int lastLevelIndex) { // initialize for reusage
m_valuePos = 0;
m_maxAbsValue = 0;
m_codePos = 0;
m_lastLevelIndex = lastLevelIndex;
}
void BitplaneEncode(); // several macro blocks can be encoded in parallel
private:
UINT32 RLESigns(UINT32 codePos, UINT32* signBits, UINT32 signLen);
UINT32 DecomposeBitplane(UINT32 bufferSize, UINT32 planeMask, UINT32 codePos, UINT32* sigBits, UINT32* refBits, UINT32* signBits, UINT32& signLen, UINT32& codeLen);
UINT8 NumberOfBitplanes();
bool GetBitAtPos(UINT32 pos, UINT32 planeMask) const { return (abs(m_value[pos]) & planeMask) > 0; }
 
CEncoder *m_encoder; // encoder instance
bool m_sigFlagVector[BufferSize+1]; // see paper from Malvar, Fast Progressive Wavelet Coder
};
 
public:
/////////////////////////////////////////////////////////////////////
/// Write pre-header, header, postHeader, and levelLength.
/// It might throw an IOException.
/// @param stream A PGF stream
/// @param preHeader A already filled in PGF pre header
/// @param header An already filled in PGF header
/// @param postHeader [in] A already filled in PGF post header (containing color table, user data, ...)
/// @param levelLength A reference to an integer array, large enough to save the relative file positions of all PGF levels
/// @param useOMP If true, then the encoder will use multi-threading based on openMP
CEncoder(CPGFStream* stream, PGFPreHeader preHeader, PGFHeader header, const PGFPostHeader& postHeader, UINT32*& levelLength, bool useOMP = true) THROW_; // throws IOException
 
/////////////////////////////////////////////////////////////////////
/// Destructor
~CEncoder();
 
/////////////////////////////////////////////////////////////////////
/// Encoder favors speed over compression size
void FavorSpeedOverSize() { m_favorSpeed = true; }
 
/////////////////////////////////////////////////////////////////////
/// Pad buffer with zeros and encode buffer.
/// It might throw an IOException.
void Flush() THROW_;
 
/////////////////////////////////////////////////////////////////////
/// Write levelLength into header.
/// @return number of bytes written into stream
/// It might throw an IOException.
UINT32 WriteLevelLength() THROW_;
 
/////////////////////////////////////////////////////////////////////
/// Partitions a rectangular region of a given subband.
/// Partitioning scheme: The plane is partitioned in squares of side length LinBlockSize.
/// Write wavelet coefficients into buffer.
/// It might throw an IOException.
/// @param band A subband
/// @param width The width of the rectangle
/// @param height The height of the rectangle
/// @param startPos The buffer position of the top left corner of the rectangular region
/// @param pitch The number of bytes in row of the subband
void Partition(CSubband* band, int width, int height, int startPos, int pitch) THROW_;
 
/////////////////////////////////////////////////////////////////////
/// Informs the encoder about the encoded level.
/// @param currentLevel encoded level [0, nLevels)
void SetEncodedLevel(int currentLevel) { ASSERT(currentLevel >= 0); m_currentBlock->m_lastLevelIndex = m_nLevels - currentLevel - 1; m_forceWriting = true; }
 
/////////////////////////////////////////////////////////////////////
/// Write a single value into subband at given position.
/// It might throw an IOException.
/// @param band A subband
/// @param bandPos A valid position in subband band
void WriteValue(CSubband* band, int bandPos) THROW_;
 
/////////////////////////////////////////////////////////////////////
/// Compute stream length of header.
/// @return header length
UINT32 ComputeHeaderLength() const { return UINT32(m_bufferStartPos - m_startPosition); }
 
/////////////////////////////////////////////////////////////////////
/// Compute stream length of encoded buffer.
/// @return encoded buffer length
UINT32 ComputeBufferLength() const { return UINT32(m_stream->GetPos() - m_bufferStartPos); }
 
/////////////////////////////////////////////////////////////////////
/// Save current stream position as beginning of current level.
void SetBufferStartPos() { m_bufferStartPos = m_stream->GetPos(); }
 
#ifdef __PGFROISUPPORT__
/////////////////////////////////////////////////////////////////////
/// Encodes tile buffer and writes it into stream
/// It might throw an IOException.
void EncodeTileBuffer() THROW_ { ASSERT(m_currentBlock && m_currentBlock->m_valuePos >= 0 && m_currentBlock->m_valuePos <= BufferSize); EncodeBuffer(ROIBlockHeader(m_currentBlock->m_valuePos, true)); }
 
/////////////////////////////////////////////////////////////////////
/// Enables region of interest (ROI) status.
void SetROI() { m_roi = true; }
#endif
 
#ifdef TRACE
void DumpBuffer() const;
#endif
 
private:
void EncodeBuffer(ROIBlockHeader h) THROW_; // throws IOException
void WriteMacroBlock(CMacroBlock* block) THROW_; // throws IOException
 
CPGFStream *m_stream;
UINT64 m_startPosition; // file position of PGF start (PreHeader)
UINT64 m_levelLengthPos; // file position of Metadata
UINT64 m_bufferStartPos; // file position of encoded buffer
 
CMacroBlock **m_macroBlocks; // array of macroblocks
int m_macroBlockLen; // array length
int m_lastMacroBlock; // array index of the last created macro block
CMacroBlock *m_currentBlock; // current macro block (used by main thread)
 
UINT32* m_levelLength; // temporary saves the level index
int m_currLevelIndex; // counts where (=index) to save next value
UINT8 m_nLevels; // number of levels
bool m_favorSpeed; // favor speed over size
bool m_forceWriting; // all macro blocks have to be written into the stream
#ifdef __PGFROISUPPORT__
bool m_roi; // true: ensures region of interest (ROI) encoding
#endif
};
 
#endif //PGF_ENCODER
/*
* The Progressive Graphics File; http://www.libpgf.org
*
* $Date: 2006-06-04 22:05:59 +0200 (So, 04 Jun 2006) $
* $Revision: 229 $
*
* This file Copyright (C) 2006 xeraina GmbH, Switzerland
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
 
//////////////////////////////////////////////////////////////////////
/// @file Encoder.h
/// @brief PGF encoder class
/// @author C. Stamm, R. Spuler
 
#ifndef PGF_ENCODER_H
#define PGF_ENCODER_H
 
#include "PGFstream.h"
#include "BitStream.h"
#include "Subband.h"
#include "WaveletTransform.h"
 
/////////////////////////////////////////////////////////////////////
// Constants
#define BufferLen (BufferSize/WordWidth) ///< number of words per buffer
#define CodeBufferLen BufferSize ///< number of words in code buffer (CodeBufferLen > BufferLen)
 
/////////////////////////////////////////////////////////////////////
/// PGF encoder class.
/// @author C. Stamm
/// @brief PGF encoder
class CEncoder {
//////////////////////////////////////////////////////////////////////
/// PGF encoder macro block class.
/// @author C. Stamm, I. Bauersachs
/// @brief A macro block is an encoding unit of fixed size (uncoded)
class CMacroBlock {
public:
//////////////////////////////////////////////////////////////////////
/// Constructor: Initializes new macro block.
/// @param encoder Pointer to outer class.
CMacroBlock(CEncoder *encoder)
#if defined(WIN32) || defined(WINCE) || defined(WIN64)
#pragma warning( suppress : 4351 )
#endif
: m_value()
, m_codeBuffer()
, m_header(0)
, m_encoder(encoder)
, m_sigFlagVector()
{
ASSERT(m_encoder);
Init(-1);
}
 
//////////////////////////////////////////////////////////////////////
/// Reinitialzes this macro block (allows reusage).
/// @param lastLevelIndex Level length directory index of last encoded level: [0, nLevels)
void Init(int lastLevelIndex) { // initialize for reusage
m_valuePos = 0;
m_maxAbsValue = 0;
m_codePos = 0;
m_lastLevelIndex = lastLevelIndex;
}
 
//////////////////////////////////////////////////////////////////////
/// Encodes this macro block into internal code buffer.
/// Several macro blocks can be encoded in parallel.
/// Call CEncoder::WriteMacroBlock after this method.
void BitplaneEncode();
 
DataT m_value[BufferSize]; ///< input buffer of values with index m_valuePos
UINT32 m_codeBuffer[CodeBufferLen]; ///< output buffer for encoded bitstream
ROIBlockHeader m_header; ///< block header
UINT32 m_valuePos; ///< current buffer position
UINT32 m_maxAbsValue; ///< maximum absolute coefficient in each buffer
UINT32 m_codePos; ///< current position in encoded bitstream
int m_lastLevelIndex; ///< index of last encoded level: [0, nLevels); used because a level-end can occur before a buffer is full
 
private:
UINT32 RLESigns(UINT32 codePos, UINT32* signBits, UINT32 signLen);
UINT32 DecomposeBitplane(UINT32 bufferSize, UINT32 planeMask, UINT32 codePos, UINT32* sigBits, UINT32* refBits, UINT32* signBits, UINT32& signLen, UINT32& codeLen);
UINT8 NumberOfBitplanes();
bool GetBitAtPos(UINT32 pos, UINT32 planeMask) const { return (abs(m_value[pos]) & planeMask) > 0; }
 
CEncoder *m_encoder; // encoder instance
bool m_sigFlagVector[BufferSize+1]; // see paper from Malvar, Fast Progressive Wavelet Coder
};
 
public:
/////////////////////////////////////////////////////////////////////
/// Write pre-header, header, post-Header, and levelLength.
/// It might throw an IOException.
/// @param stream A PGF stream
/// @param preHeader A already filled in PGF pre-header
/// @param header An already filled in PGF header
/// @param postHeader [in] An already filled in PGF post-header (containing color table, user data, ...)
/// @param userDataPos [out] File position of user data
/// @param useOMP If true, then the encoder will use multi-threading based on openMP
CEncoder(CPGFStream* stream, PGFPreHeader preHeader, PGFHeader header, const PGFPostHeader& postHeader,
UINT64& userDataPos, bool useOMP) THROW_; // throws IOException
 
/////////////////////////////////////////////////////////////////////
/// Destructor
~CEncoder();
 
/////////////////////////////////////////////////////////////////////
/// Encoder favors speed over compression size
void FavorSpeedOverSize() { m_favorSpeed = true; }
 
/////////////////////////////////////////////////////////////////////
/// Pad buffer with zeros and encode buffer.
/// It might throw an IOException.
void Flush() THROW_;
 
/////////////////////////////////////////////////////////////////////
/// Increase post-header size and write new size into stream.
/// @param preHeader An already filled in PGF pre-header
/// It might throw an IOException.
void UpdatePostHeaderSize(PGFPreHeader preHeader) THROW_;
 
/////////////////////////////////////////////////////////////////////
/// Create level length data structure and write a place holder into stream.
/// It might throw an IOException.
/// @param levelLength A reference to an integer array, large enough to save the relative file positions of all PGF levels
/// @return number of bytes written into stream
UINT32 WriteLevelLength(UINT32*& levelLength) THROW_;
 
/////////////////////////////////////////////////////////////////////
/// Write new levelLength into stream.
/// It might throw an IOException.
/// @return Written image bytes.
UINT32 UpdateLevelLength() THROW_;
 
/////////////////////////////////////////////////////////////////////
/// Partitions a rectangular region of a given subband.
/// Partitioning scheme: The plane is partitioned in squares of side length LinBlockSize.
/// Write wavelet coefficients from subband into the input buffer of a macro block.
/// It might throw an IOException.
/// @param band A subband
/// @param width The width of the rectangle
/// @param height The height of the rectangle
/// @param startPos The absolute subband position of the top left corner of the rectangular region
/// @param pitch The number of bytes in row of the subband
void Partition(CSubband* band, int width, int height, int startPos, int pitch) THROW_;
 
/////////////////////////////////////////////////////////////////////
/// Informs the encoder about the encoded level.
/// @param currentLevel encoded level [0, nLevels)
void SetEncodedLevel(int currentLevel) { ASSERT(currentLevel >= 0); m_currentBlock->m_lastLevelIndex = m_nLevels - currentLevel - 1; m_forceWriting = true; }
 
/////////////////////////////////////////////////////////////////////
/// Write a single value into subband at given position.
/// It might throw an IOException.
/// @param band A subband
/// @param bandPos A valid position in subband band
void WriteValue(CSubband* band, int bandPos) THROW_;
 
/////////////////////////////////////////////////////////////////////
/// Compute stream length of header.
/// @return header length
INT64 ComputeHeaderLength() const { return m_levelLengthPos - m_startPosition; }
 
/////////////////////////////////////////////////////////////////////
/// Compute stream length of encoded buffer.
/// @return encoded buffer length
INT64 ComputeBufferLength() const { return m_stream->GetPos() - m_bufferStartPos; }
 
/////////////////////////////////////////////////////////////////////
/// Compute file offset between real and expected levelLength position.
/// @return file offset
INT64 ComputeOffset() const { return m_stream->GetPos() - m_levelLengthPos; }
 
/////////////////////////////////////////////////////////////////////
/// Save current stream position as beginning of current level.
void SetBufferStartPos() { m_bufferStartPos = m_stream->GetPos(); }
 
#ifdef __PGFROISUPPORT__
/////////////////////////////////////////////////////////////////////
/// Encodes tile buffer and writes it into stream
/// It might throw an IOException.
void EncodeTileBuffer() THROW_ { ASSERT(m_currentBlock && m_currentBlock->m_valuePos >= 0 && m_currentBlock->m_valuePos <= BufferSize); EncodeBuffer(ROIBlockHeader(m_currentBlock->m_valuePos, true)); }
 
/////////////////////////////////////////////////////////////////////
/// Enables region of interest (ROI) status.
void SetROI() { m_roi = true; }
#endif
 
#ifdef TRACE
void DumpBuffer() const;
#endif
 
private:
void EncodeBuffer(ROIBlockHeader h) THROW_; // throws IOException
void WriteMacroBlock(CMacroBlock* block) THROW_; // throws IOException
 
CPGFStream *m_stream; ///< output PMF stream
UINT64 m_startPosition; ///< stream position of PGF start (PreHeader)
UINT64 m_levelLengthPos; ///< stream position of Metadata
UINT64 m_bufferStartPos; ///< stream position of encoded buffer
 
CMacroBlock **m_macroBlocks; ///< array of macroblocks
int m_macroBlockLen; ///< array length
int m_lastMacroBlock; ///< array index of the last created macro block
CMacroBlock *m_currentBlock; ///< current macro block (used by main thread)
 
UINT32* m_levelLength; ///< temporary saves the level index
int m_currLevelIndex; ///< counts where (=index) to save next value
UINT8 m_nLevels; ///< number of levels
bool m_favorSpeed; ///< favor speed over size
bool m_forceWriting; ///< all macro blocks have to be written into the stream
#ifdef __PGFROISUPPORT__
bool m_roi; ///< true: ensures region of interest (ROI) encoding
#endif
};
 
#endif //PGF_ENCODER
/trunk/Scribus/scribus/third_party/pgf/PGFimage.cpp
1,2524 → 1,2660
/*
* The Progressive Graphics File; http://www.libpgf.org
*
* $Date: 2007-02-03 13:04:21 +0100 (Sa, 03 Feb 2007) $
* $Revision: 280 $
*
* This file Copyright (C) 2006 xeraina GmbH, Switzerland
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
 
//////////////////////////////////////////////////////////////////////
/// @file PGFimage.cpp
/// @brief PGF image class implementation
/// @author C. Stamm
 
#include "PGFimage.h"
#include "Decoder.h"
#include "Encoder.h"
#include <cmath>
#include <cstring>
 
#define YUVoffset4 8 // 2^3
#define YUVoffset6 32 // 2^5
#define YUVoffset8 128 // 2^7
#define YUVoffset16 32768 // 2^15
#define YUVoffset31 1073741824 // 2^30
#define MaxValue 2147483648 // 2^MaxBitPlanes = 2^31
 
//////////////////////////////////////////////////////////////////////
// global methods and variables
#ifdef NEXCEPTIONS
OSError _PGF_Error_;
 
OSError GetLastPGFError() {
OSError tmp = _PGF_Error_;
_PGF_Error_ = NoError;
return tmp;
}
#endif
 
//////////////////////////////////////////////////////////////////////
// Standard constructor: It is used to create a PGF instance for opening and reading.
CPGFImage::CPGFImage()
: m_decoder(0)
, m_encoder(0)
, m_levelLength(0)
, m_quant(0)
, m_downsample(false)
, m_favorSpeedOverSize(false)
, m_useOMPinEncoder(true)
, m_useOMPinDecoder(true)
#ifdef __PGFROISUPPORT__
, m_levelwise(true)
, m_streamReinitialized(false)
#endif
, m_cb(0)
, m_cbArg(0)
{
 
// init preHeader
memcpy(m_preHeader.magic, Magic, 3);
m_preHeader.version = PGFVersion;
m_preHeader.hSize = 0;
 
// init postHeader
m_postHeader.userData = 0;
m_postHeader.userDataLen = 0;
 
// init channels
for (int i=0; i < MaxChannels; i++) {
m_channel[i] = 0;
m_wtChannel[i] = 0;
}
 
// set image width and height
m_width[0] = 0;
m_height[0] = 0;
}
 
//////////////////////////////////////////////////////////////////////
// Destructor: Destroy internal data structures.
CPGFImage::~CPGFImage() {
Destroy();
}
 
//////////////////////////////////////////////////////////////////////
// Destroy internal data structures.
// Destructor calls this method during destruction.
void CPGFImage::Destroy() {
Close();
 
for (int i=0; i < m_header.channels; i++) {
delete m_wtChannel[i]; m_wtChannel[i]=0;
m_channel[i] = 0;
}
delete[] m_postHeader.userData; m_postHeader.userData = 0; m_postHeader.userDataLen = 0;
delete[] m_levelLength; m_levelLength = 0;
delete m_encoder; m_encoder = NULL;
}
 
//////////////////////////////////////////////////////////////////////
// Close PGF image after opening and reading.
// Destructor calls this method during destruction.
void CPGFImage::Close() {
delete m_decoder; m_decoder = 0;
}
 
/////////////////////////////////////////////////////////////////////////////
// Open a PGF image at current stream position: read pre-header, header, levelLength, and ckeck image type.
// Precondition: The stream has been opened for reading.
// It might throw an IOException.
// @param stream A PGF stream
void CPGFImage::Open(CPGFStream *stream) THROW_ {
ASSERT(stream);
 
m_decoder = new CDecoder(stream, m_preHeader, m_header, m_postHeader, m_levelLength, m_useOMPinDecoder);
if (!m_decoder) ReturnWithError(InsufficientMemory);
ASSERT(m_decoder);
 
if (m_header.nLevels > MaxLevel) ReturnWithError(FormatCannotRead);
 
// set current level
m_currentLevel = m_header.nLevels;
 
// set image width and height
m_width[0] = m_header.width;
m_height[0] = m_header.height;
 
// complete header
CompleteHeader();
 
// interpret quant parameter
if (m_header.quality > DownsampleThreshold &&
(m_header.mode == ImageModeRGBColor ||
m_header.mode == ImageModeRGBA ||
m_header.mode == ImageModeRGB48 ||
m_header.mode == ImageModeCMYKColor ||
m_header.mode == ImageModeCMYK64 ||
m_header.mode == ImageModeLabColor ||
m_header.mode == ImageModeLab48)) {
m_downsample = true;
m_quant = m_header.quality - 1;
} else {
m_downsample = false;
m_quant = m_header.quality;
}
 
// set channel dimensions (chrominance is subsampled by factor 2)
if (m_downsample) {
for (int i=1; i < m_header.channels; i++) {
m_width[i] = (m_width[0] + 1)/2;
m_height[i] = (m_height[0] + 1)/2;
}
} else {
for (int i=1; i < m_header.channels; i++) {
m_width[i] = m_width[0];
m_height[i] = m_height[0];
}
}
 
if (m_header.nLevels > 0) {
// init wavelet subbands
for (int i=0; i < m_header.channels; i++) {
m_wtChannel[i] = new CWaveletTransform(m_width[i], m_height[i], m_header.nLevels);
if (!m_wtChannel[i]) ReturnWithError(InsufficientMemory);
}
} else {
// very small image: we don't use DWT and encoding
 
// read channels
for (int c=0; c < m_header.channels; c++) {
const UINT32 size = m_width[c]*m_height[c];
m_channel[c] = new DataT[size];
 
// read channel data from stream
for (UINT32 i=0; i < size; i++) {
int count = DataTSize;
stream->Read(&count, &m_channel[c][i]);
if (count != DataTSize) ReturnWithError(MissingData);
}
}
}
}
 
////////////////////////////////////////////////////////////
void CPGFImage::CompleteHeader() {
if (m_header.mode == ImageModeUnknown) {
// undefined mode
switch(m_header.bpp) {
case 1: m_header.mode = ImageModeBitmap; break;
case 8: m_header.mode = ImageModeGrayScale; break;
case 12: m_header.mode = ImageModeRGB12; break;
case 16: m_header.mode = ImageModeRGB16; break;
case 24: m_header.mode = ImageModeRGBColor; break;
case 32: m_header.mode = ImageModeRGBA; break;
case 48: m_header.mode = ImageModeRGB48; break;
default: m_header.mode = ImageModeRGBColor; break;
}
}
if (!m_header.bpp) {
// undefined bpp
switch(m_header.mode) {
case ImageModeBitmap:
m_header.bpp = 1;
break;
case ImageModeIndexedColor:
case ImageModeGrayScale:
m_header.bpp = 8;
break;
case ImageModeRGB12:
m_header.bpp = 12;
break;
case ImageModeRGB16:
case ImageModeGray16:
m_header.bpp = 16;
break;
case ImageModeRGBColor:
case ImageModeLabColor:
m_header.bpp = 24;
break;
case ImageModeRGBA:
case ImageModeCMYKColor:
#ifdef __PGF32SUPPORT__
case ImageModeGray31:
#endif
m_header.bpp = 32;
break;
case ImageModeRGB48:
case ImageModeLab48:
m_header.bpp = 48;
break;
case ImageModeCMYK64:
m_header.bpp = 64;
break;
default:
ASSERT(false);
m_header.bpp = 24;
}
}
if (m_header.mode == ImageModeRGBColor && m_header.bpp == 32) {
// change mode
m_header.mode = ImageModeRGBA;
}
ASSERT(m_header.mode != ImageModeBitmap || m_header.bpp == 1);
ASSERT(m_header.mode != ImageModeGrayScale || m_header.bpp == 8);
ASSERT(m_header.mode != ImageModeGray16 || m_header.bpp == 16);
ASSERT(m_header.mode != ImageModeRGBColor || m_header.bpp == 24);
ASSERT(m_header.mode != ImageModeRGBA || m_header.bpp == 32);
ASSERT(m_header.mode != ImageModeRGB12 || m_header.bpp == 12);
ASSERT(m_header.mode != ImageModeRGB16 || m_header.bpp == 16);
ASSERT(m_header.mode != ImageModeRGB48 || m_header.bpp == 48);
ASSERT(m_header.mode != ImageModeLabColor || m_header.bpp == 24);
ASSERT(m_header.mode != ImageModeLab48 || m_header.bpp == 48);
ASSERT(m_header.mode != ImageModeCMYKColor || m_header.bpp == 32);
ASSERT(m_header.mode != ImageModeCMYK64 || m_header.bpp == 64);
 
// set number of channels
if (!m_header.channels) {
switch(m_header.mode) {
case ImageModeBitmap:
case ImageModeIndexedColor:
case ImageModeGrayScale:
case ImageModeGray16:
#ifdef __PGF32SUPPORT__
case ImageModeGray31:
#endif
m_header.channels = 1;
break;
case ImageModeRGBColor:
case ImageModeRGB12:
case ImageModeRGB16:
case ImageModeRGB48:
case ImageModeLabColor:
case ImageModeLab48:
m_header.channels = 3;
break;
case ImageModeRGBA:
case ImageModeCMYKColor:
case ImageModeCMYK64:
m_header.channels = 4;
break;
default:
ASSERT(false);
m_header.channels = 3;
}
}
}
 
//////////////////////////////////////////////////////////////////////
/// Return user data and size of user data.
/// @param size [out] Size of user data in bytes.
/// @return A pointer to user data or NULL if there is no user data.
const UINT8* CPGFImage::GetUserData(UINT32& size) const {
size = m_postHeader.userDataLen;
return m_postHeader.userData;
}
 
//////////////////////////////////////////////////////////////////////
/// After you've written a PGF image, you can call this method followed by GetBitmap/GetYUV
/// to get a quick reconstruction (coded -> decoded image).
/// @param level The image level of the resulting image in the internal image buffer.
void CPGFImage::Reconstruct(int level /*= 0*/) {
if (m_header.nLevels == 0) {
// image didn't use wavelet transform
if (level == 0) {
for (int i=0; i < m_header.channels; i++) {
ASSERT(m_wtChannel[i]);
m_channel[i] = m_wtChannel[i]->GetSubband(0, LL)->GetBuffer();
}
}
} else {
int currentLevel = m_header.nLevels;
 
if (ROIisSupported()) {
// enable ROI reading
SetROI(PGFRect(0, 0, m_header.width, m_header.height));
}
 
while (currentLevel > level) {
for (int i=0; i < m_header.channels; i++) {
ASSERT(m_wtChannel[i]);
// dequantize subbands
if (currentLevel == m_header.nLevels) {
// last level also has LL band
m_wtChannel[i]->GetSubband(currentLevel, LL)->Dequantize(m_quant);
}
m_wtChannel[i]->GetSubband(currentLevel, HL)->Dequantize(m_quant);
m_wtChannel[i]->GetSubband(currentLevel, LH)->Dequantize(m_quant);
m_wtChannel[i]->GetSubband(currentLevel, HH)->Dequantize(m_quant);
 
// inverse transform from m_wtChannel to m_channel
m_wtChannel[i]->InverseTransform(currentLevel, &m_width[i], &m_height[i], &m_channel[i]);
ASSERT(m_channel[i]);
}
 
currentLevel--;
}
}
}
 
//////////////////////////////////////////////////////////////////////
// Read and decode some levels of a PGF image at current stream position.
// A PGF image is structered in levels, numbered between 0 and Levels() - 1.
// Each level can be seen as a single image, containing the same content
// as all other levels, but in a different size (width, height).
// The image size at level i is double the size (width, height) of the image at level i+1.
// The image at level 0 contains the original size.
// Precondition: The PGF image has been opened with a call of Open(...).
// It might throw an IOException.
// @param level The image level of the resulting image in the internal image buffer.
// @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.
// @param data Data Pointer to C++ class container to host callback procedure.
void CPGFImage::Read(int level /*= 0*/, CallbackPtr cb /*= NULL*/, void *data /*=NULL*/) THROW_ {
ASSERT((level >= 0 && level < m_header.nLevels) || m_header.nLevels == 0); // m_header.nLevels == 0: image didn't use wavelet transform
ASSERT(m_decoder);
 
#ifdef __PGFROISUPPORT__
if (ROIisSupported() && m_header.nLevels > 0) {
// new encoding scheme supporting ROI
PGFRect rect(0, 0, m_header.width, m_header.height);
Read(rect, level, cb, data);
return;
}
#endif
 
if (m_header.nLevels == 0) {
if (level == 0) {
// the data has already been read during open
// now update progress
if (cb) {
if ((*cb)(1.0, true, data)) ReturnWithError(EscapePressed);
}
}
} else {
const int levelDiff = m_currentLevel - level;
double percent = pow(0.25, levelDiff);
 
// encoding scheme without ROI
while (m_currentLevel > level) {
for (int i=0; i < m_header.channels; i++) {
ASSERT(m_wtChannel[i]);
// decode file and write stream to m_wtChannel
if (m_currentLevel == m_header.nLevels) {
// last level also has LL band
m_wtChannel[i]->GetSubband(m_currentLevel, LL)->PlaceTile(*m_decoder, m_quant);
}
if (m_preHeader.version & Version5) {
// since version 5
m_wtChannel[i]->GetSubband(m_currentLevel, HL)->PlaceTile(*m_decoder, m_quant);
m_wtChannel[i]->GetSubband(m_currentLevel, LH)->PlaceTile(*m_decoder, m_quant);
} else {
// until version 4
m_decoder->DecodeInterleaved(m_wtChannel[i], m_currentLevel, m_quant);
}
m_wtChannel[i]->GetSubband(m_currentLevel, HH)->PlaceTile(*m_decoder, m_quant);
}
 
#pragma omp parallel for default(shared)
for (int i=0; i < m_header.channels; i++) {
// inverse transform from m_wtChannel to m_channel
m_wtChannel[i]->InverseTransform(m_currentLevel, &m_width[i], &m_height[i], &m_channel[i]);
ASSERT(m_channel[i]);
}
 
// set new level: must be done before refresh callback
m_currentLevel--;
 
// now we have to refresh the display
if (m_cb) m_cb(m_cbArg);
 
// now update progress
if (cb) {
percent += 3*percent;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
}
 
// automatically closing
if (m_currentLevel == 0) Close();
}
 
#ifdef __PGFROISUPPORT__
//////////////////////////////////////////////////////////////////////
/// Read a rectangular region of interest of a PGF image at current stream position.
/// The origin of the coordinate axis is the top-left corner of the image.
/// All coordinates are measured in pixels.
/// It might throw an IOException.
/// @param rect [inout] Rectangular region of interest (ROI). The rect might be cropped.
/// @param level The image level of the resulting image in the internal image buffer.
/// @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.
/// @param data Data Pointer to C++ class container to host callback procedure.
void CPGFImage::Read(PGFRect& rect, int level /*= 0*/, CallbackPtr cb /*= NULL*/, void *data /*=NULL*/) THROW_ {
ASSERT((level >= 0 && level < m_header.nLevels) || m_header.nLevels == 0); // m_header.nLevels == 0: image didn't use wavelet transform
ASSERT(m_decoder);
 
if (m_header.nLevels == 0 || !ROIisSupported()) {
rect.left = rect.top = 0;
rect.right = m_header.width; rect.bottom = m_header.height;
Read(level, cb, data);
} else {
ASSERT(ROIisSupported());
// new encoding scheme supporting ROI
ASSERT(rect.left < m_header.width && rect.top < m_header.height);
const int levelDiff = m_currentLevel - level;
double percent = pow(0.25, levelDiff);
 
// check level difference
if (levelDiff <= 0) {
// it is a new read call, probably with a new ROI
m_currentLevel = m_header.nLevels;
m_decoder->SetStreamPosToData();
}
 
// check rectangle
if (rect.right == 0 || rect.right > m_header.width) rect.right = m_header.width;
if (rect.bottom == 0 || rect.bottom > m_header.height) rect.bottom = m_header.height;
 
// enable ROI decoding and reading
SetROI(rect);
 
while (m_currentLevel > level) {
for (int i=0; i < m_header.channels; i++) {
ASSERT(m_wtChannel[i]);
 
// get number of tiles and tile indices
const UINT32 nTiles = m_wtChannel[i]->GetNofTiles(m_currentLevel);
const PGFRect& tileIndices = m_wtChannel[i]->GetTileIndices(m_currentLevel);
 
// decode file and write stream to m_wtChannel
if (m_currentLevel == m_header.nLevels) { // last level also has LL band
ASSERT(nTiles == 1);
m_decoder->DecodeTileBuffer();
m_wtChannel[i]->GetSubband(m_currentLevel, LL)->PlaceTile(*m_decoder, m_quant);
}
for (UINT32 tileY=0; tileY < nTiles; tileY++) {
for (UINT32 tileX=0; tileX < nTiles; tileX++) {
// check relevance of tile
if (tileIndices.IsInside(tileX, tileY)) {
m_decoder->DecodeTileBuffer();
m_wtChannel[i]->GetSubband(m_currentLevel, HL)->PlaceTile(*m_decoder, m_quant, true, tileX, tileY);
m_wtChannel[i]->GetSubband(m_currentLevel, LH)->PlaceTile(*m_decoder, m_quant, true, tileX, tileY);
m_wtChannel[i]->GetSubband(m_currentLevel, HH)->PlaceTile(*m_decoder, m_quant, true, tileX, tileY);
} else {
// skip tile
m_decoder->SkipTileBuffer();
}
}
}
}
 
#pragma omp parallel for default(shared)
for (int i=0; i < m_header.channels; i++) {
// inverse transform from m_wtChannel to m_channel
m_wtChannel[i]->InverseTransform(m_currentLevel, &m_width[i], &m_height[i], &m_channel[i]);
ASSERT(m_channel[i]);
}
 
// set new level: must be done before refresh callback
m_currentLevel--;
 
// now we have to refresh the display
if (m_cb) m_cb(m_cbArg);
 
// now update progress
if (cb) {
percent += 3*percent;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
}
 
// automatically closing
if (m_currentLevel == 0) Close();
}
 
//////////////////////////////////////////////////////////////////////
/// Compute ROIs for each channel and each level
/// @param rect rectangular region of interest (ROI)
void CPGFImage::SetROI(PGFRect rect) {
ASSERT(m_decoder);
ASSERT(ROIisSupported());
 
// store ROI for a later call of GetBitmap
m_roi = rect;
 
// enable ROI decoding
m_decoder->SetROI();
 
// enlarge ROI because of border artefacts
const UINT32 dx = FilterWidth/2*(1 << m_currentLevel);
const UINT32 dy = FilterHeight/2*(1 << m_currentLevel);
 
if (rect.left < dx) rect.left = 0;
else rect.left -= dx;
if (rect.top < dy) rect.top = 0;
else rect.top -= dy;
rect.right += dx;
if (rect.right > m_header.width) rect.right = m_header.width;
rect.bottom += dy;
if (rect.bottom > m_header.height) rect.bottom = m_header.height;
 
// prepare wavelet channels for using ROI
ASSERT(m_wtChannel[0]);
m_wtChannel[0]->SetROI(rect);
if (m_downsample && m_header.channels > 1) {
// all further channels are downsampled, therefore downsample ROI
rect.left >>= 1;
rect.top >>= 1;
rect.right >>= 1;
rect.bottom >>= 1;
}
for (int i=1; i < m_header.channels; i++) {
ASSERT(m_wtChannel[i]);
m_wtChannel[i]->SetROI(rect);
}
}
 
#endif // __PGFROISUPPORT__
 
//////////////////////////////////////////////////////////////////////
/// Return the length of all encoded headers in bytes.
/// Precondition: The PGF image has been opened with a call of Open(...).
/// @return The length of all encoded headers in bytes
UINT32 CPGFImage::GetEncodedHeaderLength() const {
ASSERT(m_decoder);
return m_decoder->GetEncodedHeaderLength();
}
 
//////////////////////////////////////////////////////////////////////
/// Reads the encoded PGF headers and copies it to a target buffer.
/// Precondition: The PGF image has been opened with a call of Open(...).
/// It might throw an IOException.
/// @param target The target buffer
/// @param targetLen The length of the target buffer in bytes
/// @return The number of bytes copied to the target buffer
UINT32 CPGFImage::ReadEncodedHeader(UINT8* target, UINT32 targetLen) const THROW_ {
ASSERT(target);
ASSERT(targetLen > 0);
ASSERT(m_decoder);
 
// reset stream position
m_decoder->SetStreamPosToStart();
 
// compute number of bytes to read
UINT32 len = __min(targetLen, GetEncodedHeaderLength());
 
// read data
len = m_decoder->ReadEncodedData(target, len);
ASSERT(len >= 0 && len <= targetLen);
 
return len;
}
 
////////////////////////////////////////////////////////////////////
/// Reset stream position to start of PGF pre-header
void CPGFImage::ResetStreamPos() THROW_ {
ASSERT(m_decoder);
return m_decoder->SetStreamPosToStart();
}
 
//////////////////////////////////////////////////////////////////////
/// Reads the data of an encoded PGF level and copies it to a target buffer
/// without decoding.
/// Precondition: The PGF image has been opened with a call of Open(...).
/// It might throw an IOException.
/// @param level The image level
/// @param target The target buffer
/// @param targetLen The length of the target buffer in bytes
/// @return The number of bytes copied to the target buffer
UINT32 CPGFImage::ReadEncodedData(int level, UINT8* target, UINT32 targetLen) const THROW_ {
ASSERT(level >= 0 && level < m_header.nLevels);
ASSERT(target);
ASSERT(targetLen > 0);
ASSERT(m_decoder);
 
// reset stream position
m_decoder->SetStreamPosToData();
 
// position stream
UINT64 offset = 0;
 
for (int i=m_header.nLevels - 1; i > level; i--) {
offset += m_levelLength[m_header.nLevels - 1 - i];
}
m_decoder->Skip(offset);
 
// compute number of bytes to read
UINT32 len = __min(targetLen, GetEncodedLevelLength(level));
 
// read data
len = m_decoder->ReadEncodedData(target, len);
ASSERT(len >= 0 && len <= targetLen);
 
return len;
}
 
//////////////////////////////////////////////////////////////////
// Set background of an RGB image with transparency channel or reset to default background.
// @param bg A pointer to a background color or NULL (reset to default background)
void CPGFImage::SetBackground(const RGBTRIPLE* bg) {
if (bg) {
m_header.background = *bg;
// m_backgroundSet = true;
} else {
m_header.background.rgbtBlue = DefaultBGColor;
m_header.background.rgbtGreen = DefaultBGColor;
m_header.background.rgbtRed = DefaultBGColor;
// m_backgroundSet = false;
}
}
 
//////////////////////////////////////////////////////////////////////
/// Set maximum intensity value for image modes with more than eight bits per channel.
/// Don't call this method before SetHeader.
/// @param maxValue The maximum intensity value.
void CPGFImage::SetMaxValue(UINT32 maxValue) {
BYTE pot = 0;
 
while(maxValue > 0) {
pot++;
maxValue >>= 1;
}
// store bits per channel
if (pot > 31) pot = 31;
m_header.background.rgbtBlue = pot;
}
 
//////////////////////////////////////////////////////////////////////
/// Returns number of used bits per input/output image channel.
/// Precondition: header must be initialized.
/// @return number of used bits per input/output image channel.
BYTE CPGFImage::UsedBitsPerChannel() const {
BYTE bpc = m_header.bpp/m_header.channels;
 
if (bpc > 8) {
// see also GetMaxValue()
return m_header.background.rgbtBlue;
} else {
return bpc;
}
}
 
//////////////////////////////////////////////////////////////////////
/// Returns highest supported version
BYTE CPGFImage::Version() const {
if (m_preHeader.version & Version6) return 6;
if (m_preHeader.version & Version5) return 5;
if (m_preHeader.version & Version2) return 2;
return 1;
}
 
//////////////////////////////////////////////////////////////////
// Import an image from a specified image buffer.
// This method is usually called before Write(...) and after SetHeader(...).
// It might throw an IOException.
// The absolute value of pitch is the number of bytes of an image row.
// If pitch is negative, then buff points to the last row of a bottom-up image (first byte on last row).
// If pitch is positive, then buff points to the first row of a top-down image (first byte).
// 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
// provide a channelMap of size of expected channels (depending on image mode). For example, PGF expects in RGB color mode a channel sequence BGR.
// If your provided image buffer contains a channel sequence ARGB, then the channelMap looks like { 3, 2, 1 }.
// @param pitch The number of bytes of a row of the image buffer.
// @param buff An image buffer.
// @param bpp The number of bits per pixel used in image buffer.
// @param channelMap A integer array containing the mapping of input channel ordering to expected channel ordering.
// @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.
// @param data Data Pointer to C++ class container to host callback procedure.
void CPGFImage::ImportBitmap(int pitch, UINT8 *buff, BYTE bpp, int channelMap[] /*= NULL */, CallbackPtr cb /*= NULL*/, void *data /*=NULL*/) THROW_ {
ASSERT(buff);
ASSERT(m_channel[0]);
 
// color transform
RgbToYuv(pitch, buff, bpp, channelMap, cb, data);
 
if (m_downsample) {
// Subsampling of the chrominance and alpha channels
for (int i=1; i < m_header.channels; i++) {
Downsample(i);
}
}
}
 
/////////////////////////////////////////////////////////////////
// Bilinerar Subsampling of channel ch by a factor 2
void CPGFImage::Downsample(int ch) {
ASSERT(ch > 0);
 
const int w = m_width[0];
const int w2 = w/2;
const int h2 = m_height[0]/2;
const int oddW = w%2; // don't use bool -> problems with MaxSpeed optimization
const int oddH = m_height[0]%2; // "
int i, j;
int loPos = 0;
int hiPos = w;
int sampledPos = 0;
DataT* buff = m_channel[ch]; ASSERT(buff);
 
for (i=0; i < h2; i++) {
for (j=0; j < w2; j++) {
// compute average of pixel block
buff[sampledPos] = (buff[loPos] + buff[loPos + 1] + buff[hiPos] + buff[hiPos + 1]) >> 2;
loPos += 2; hiPos += 2;
sampledPos++;
}
if (oddW) {
buff[sampledPos] = (buff[loPos] + buff[hiPos]) >> 1;
loPos++; hiPos++;
sampledPos++;
}
loPos += w; hiPos += w;
}
if (oddH) {
for (j=0; j < w2; j++) {
buff[sampledPos] = (buff[loPos] + buff[loPos+1]) >> 1;
loPos += 2; hiPos += 2;
sampledPos++;
}
if (oddW) {
buff[sampledPos] = buff[loPos];
}
}
 
// downsampled image has half width and half height
m_width[ch] = (m_width[ch] + 1)/2;
m_height[ch] = (m_height[ch] + 1)/2;
}
 
//////////////////////////////////////////////////////////////////////
void CPGFImage::ComputeLevels() {
const int maxThumbnailWidth = 20*FilterWidth;
const int m = __min(m_header.width, m_header.height);
int s = m;
 
if (m_header.nLevels < 1 || m_header.nLevels > MaxLevel) {
m_header.nLevels = 1;
// compute a good value depending on the size of the image
while (s > maxThumbnailWidth) {
m_header.nLevels++;
s = s/2;
}
}
 
int levels = m_header.nLevels; // we need a signed value during level reduction
 
// reduce number of levels if the image size is smaller than FilterWidth*2^levels
s = FilterWidth*(1 << levels); // must be at least the double filter size because of subsampling
while (m < s) {
levels--;
s = s/2;
}
if (levels > MaxLevel) m_header.nLevels = MaxLevel;
else if (levels < 0) m_header.nLevels = 0;
else m_header.nLevels = (UINT8)levels;
 
ASSERT(0 <= m_header.nLevels && m_header.nLevels <= MaxLevel);
}
 
//////////////////////////////////////////////////////////////////////
/// Set PGF header and user data.
/// Precondition: The PGF image has been closed with Close(...) or never opened with Open(...).
/// It might throw an IOException.
/// @param header A valid and already filled in PGF header structure
/// @param flags A combination of additional version flags
/// @param userData A user-defined memory block
/// @param userDataLength The size of user-defined memory block in bytes
void CPGFImage::SetHeader(const PGFHeader& header, BYTE flags /*=0*/, UINT8* userData /*= 0*/, UINT32 userDataLength /*= 0*/) THROW_ {
ASSERT(!m_decoder); // current image must be closed
ASSERT(header.quality <= MaxQuality);
int i;
 
// init state
#ifdef __PGFROISUPPORT__
m_levelwise = true;
m_streamReinitialized = false;
#endif
 
// init preHeader
memcpy(m_preHeader.magic, Magic, 3);
m_preHeader.version = PGFVersion | flags;
m_preHeader.hSize = HeaderSize;
 
// copy header
memcpy(&m_header, &header, HeaderSize);
 
// complete header
CompleteHeader();
 
// check and set number of levels
ComputeLevels();
 
// misuse background value to store bits per channel
BYTE bpc = m_header.bpp/m_header.channels;
if (bpc > 8) {
if (bpc > 31) bpc = 31;
m_header.background.rgbtBlue = bpc;
}
 
// check for downsample
if (m_header.quality > DownsampleThreshold && (m_header.mode == ImageModeRGBColor ||
m_header.mode == ImageModeRGBA ||
m_header.mode == ImageModeRGB48 ||
m_header.mode == ImageModeCMYKColor ||
m_header.mode == ImageModeCMYK64 ||
m_header.mode == ImageModeLabColor ||
m_header.mode == ImageModeLab48)) {
m_downsample = true;
m_quant = m_header.quality - 1;
} else {
m_downsample = false;
m_quant = m_header.quality;
}
 
// update header size and copy user data
if (m_header.mode == ImageModeIndexedColor) {
m_preHeader.hSize += ColorTableSize;
}
if (userDataLength && userData) {
m_postHeader.userData = new UINT8[userDataLength];
m_postHeader.userDataLen = userDataLength;
memcpy(m_postHeader.userData, userData, userDataLength);
m_preHeader.hSize += userDataLength;
}
 
// allocate channels
for (i=0; i < m_header.channels; i++) {
// set current width and height
m_width[i] = m_header.width;
m_height[i] = m_header.height;
 
// allocate channels
ASSERT(!m_channel[i]);
m_channel[i] = new DataT[m_header.width*m_header.height];
if (!m_channel[i]) ReturnWithError(InsufficientMemory);
}
}
 
//////////////////////////////////////////////////////////////////
// Create wavelet transform channels and encoder.
// Call this method before your first call of Write(int level), but after SetHeader().
// Don't use this method when you call Write().
// It might throw an IOException.
// @param stream A PGF stream
// @return The number of bytes written into stream.
UINT32 CPGFImage::WriteHeader(CPGFStream* stream) THROW_ {
ASSERT(m_header.nLevels <= MaxLevel);
ASSERT(m_header.quality <= MaxQuality); // quality is already initialized
 
if (m_header.nLevels > 0) {
volatile OSError error = NoError; // volatile prevents optimizations
// create new wt channels
#pragma omp parallel for default(shared)
for (int i=0; i < m_header.channels; i++) {
DataT *temp = NULL;
if (error == NoError) {
if (m_wtChannel[i]) {
ASSERT(m_channel[i]);
// copy m_channel to temp
int size = m_height[i]*m_width[i];
temp = new DataT[size];
if (temp) {
memcpy(temp, m_channel[i], size*DataTSize);
delete m_wtChannel[i]; // also deletes m_channel
} else {
error = InsufficientMemory;
}
}
if (temp) m_channel[i] = temp;
m_wtChannel[i] = new CWaveletTransform(m_width[i], m_height[i], m_header.nLevels, m_channel[i]);
if (m_wtChannel[i]) {
// wavelet subband decomposition
for (int l=0; l < m_header.nLevels; l++) {
m_wtChannel[i]->ForwardTransform(l);
}
} else {
delete temp;
error = InsufficientMemory;
}
}
}
if (error != NoError) ReturnWithError(error);
 
m_currentLevel = m_header.nLevels;
 
#ifdef __PGFROISUPPORT__
if (m_levelwise) {
m_preHeader.version |= PGFROI;
}
#endif
 
// create encoder and eventually write headers and levelLength
m_encoder = new CEncoder(stream, m_preHeader, m_header, m_postHeader, m_levelLength, m_useOMPinEncoder);
if (m_favorSpeedOverSize) m_encoder->FavorSpeedOverSize();
 
#ifdef __PGFROISUPPORT__
if (ROIisSupported()) {
// new encoding scheme supporting ROI
m_encoder->SetROI();
}
#endif
 
// return number of written bytes
return m_encoder->ComputeHeaderLength();
 
} else {
// very small image: we don't use DWT and encoding
 
// create encoder and eventually write headers and levelLength
m_encoder = new CEncoder(stream, m_preHeader, m_header, m_postHeader, m_levelLength, m_useOMPinEncoder);
 
// write channels
for (int c=0; c < m_header.channels; c++) {
const UINT32 size = m_width[c]*m_height[c];
 
// write channel data into stream
for (UINT32 i=0; i < size; i++) {
int count = DataTSize;
stream->Write(&count, &m_channel[c][i]);
}
}
 
// write level lengths
UINT32 nBytes = m_encoder->WriteLevelLength(); // return written bytes inclusive header
 
// delete encoder
delete m_encoder; m_encoder = NULL;
 
// return number of written bytes
return nBytes;
}
}
 
//////////////////////////////////////////////////////////////////
// Encode and write next level of a PGF image at current stream position.
// A PGF image is structered in levels, numbered between 0 and Levels() - 1.
// Each level can be seen as a single image, containing the same content
// as all other levels, but in a different size (width, height).
// The image size at level i is double the size (width, height) of the image at level i+1.
// The image at level 0 contains the original size.
// It might throw an IOException.
void CPGFImage::WriteLevel() THROW_ {
ASSERT(m_encoder);
ASSERT(m_currentLevel > 0);
ASSERT(m_header.nLevels > 0);
 
#ifdef __PGFROISUPPORT__
if (ROIisSupported()) {
const int lastChannel = m_header.channels - 1;
 
for (int i=0; i < m_header.channels; i++) {
m_wtChannel[i]->SetROI();
 
// get number of tiles and tile indices
const UINT32 nTiles = m_wtChannel[i]->GetNofTiles(m_currentLevel);
const UINT32 lastTile = nTiles - 1;
 
if (m_currentLevel == m_header.nLevels) {
// last level also has LL band
ASSERT(nTiles == 1);
m_wtChannel[i]->GetSubband(m_currentLevel, LL)->ExtractTile(*m_encoder, m_quant);
m_encoder->EncodeTileBuffer();
}
for (UINT32 tileY=0; tileY < nTiles; tileY++) {
for (UINT32 tileX=0; tileX < nTiles; tileX++) {
m_wtChannel[i]->GetSubband(m_currentLevel, HL)->ExtractTile(*m_encoder, m_quant, true, tileX, tileY);
m_wtChannel[i]->GetSubband(m_currentLevel, LH)->ExtractTile(*m_encoder, m_quant, true, tileX, tileY);
m_wtChannel[i]->GetSubband(m_currentLevel, HH)->ExtractTile(*m_encoder, m_quant, true, tileX, tileY);
if (i == lastChannel && tileY == lastTile && tileX == lastTile) {
// all necessary data are buffered. next call of EncodeBuffer will write the last piece of data of the current level.
m_encoder->SetEncodedLevel(--m_currentLevel);
}
m_encoder->EncodeTileBuffer();
}
}
}
} else
#endif
{
for (int i=0; i < m_header.channels; i++) {
ASSERT(m_wtChannel[i]);
if (m_currentLevel == m_header.nLevels) {
// last level also has LL band
m_wtChannel[i]->GetSubband(m_currentLevel, LL)->ExtractTile(*m_encoder, m_quant);
}
//encoder.EncodeInterleaved(m_wtChannel[i], m_currentLevel, m_quant); // until version 4
m_wtChannel[i]->GetSubband(m_currentLevel, HL)->ExtractTile(*m_encoder, m_quant); // since version 5
m_wtChannel[i]->GetSubband(m_currentLevel, LH)->ExtractTile(*m_encoder, m_quant); // since version 5
m_wtChannel[i]->GetSubband(m_currentLevel, HH)->ExtractTile(*m_encoder, m_quant);
}
 
// all necessary data are buffered. next call of EncodeBuffer will write the last piece of data of the current level.
m_encoder->SetEncodedLevel(--m_currentLevel);
}
}
 
//////////////////////////////////////////////////////////////////
// Encode and write a PGF image at current stream position.
// A PGF image is structered in levels, numbered between 0 and Levels() - 1.
// Each level can be seen as a single image, containing the same content
// as all other levels, but in a different size (width, height).
// The image size at level i is double the size (width, height) of the image at level i+1.
// The image at level 0 contains the original size.
// Precondition: the PGF image contains a valid header (see also SetHeader(...)).
// It might throw an IOException.
// @param stream A PGF stream
// @param nWrittenBytes [in-out] The number of bytes written into stream are added to the input value.
// @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.
// @param data Data Pointer to C++ class container to host callback procedure.
void CPGFImage::Write(CPGFStream* stream, UINT32* nWrittenBytes /*= NULL*/, CallbackPtr cb /*= NULL*/, void *data /*=NULL*/) THROW_ {
ASSERT(stream);
ASSERT(m_preHeader.hSize);
 
#ifdef __PGFROISUPPORT__
// don't use level-wise writing
m_levelwise = false;
#endif
 
// create wavelet transform channels and encoder
WriteHeader(stream);
 
int levels = m_header.nLevels;
double percent = pow(0.25, levels - 1);
 
if (levels == 0) {
// data has been written in WriteHeader
// now update progress
if (cb) {
if ((*cb)(1, true, data)) ReturnWithError(EscapePressed);
}
} else {
// encode quantized wavelet coefficients and write to PGF file
// encode subbands, higher levels first
// color channels are interleaved
 
// encode all levels
for (m_currentLevel = levels; m_currentLevel > 0; ) {
WriteLevel(); // decrements m_currentLevel
 
// now update progress
if (cb) {
percent *= 4;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
 
// flush encoder and write level lengths
m_encoder->Flush();
UINT32 nBytes = m_encoder->WriteLevelLength(); // inclusive header
 
// delete encoder
delete m_encoder; m_encoder = NULL;
 
// return written bytes
if (nWrittenBytes) *nWrittenBytes += nBytes;
}
 
ASSERT(!m_encoder);
}
 
#ifdef __PGFROISUPPORT__
//////////////////////////////////////////////////////////////////
// Encode and write down to given level at current stream position.
// A PGF image is structered in levels, numbered between 0 and Levels() - 1.
// Each level can be seen as a single image, containing the same content
// as all other levels, but in a different size (width, height).
// The image size at level i is double the size (width, height) of the image at level i+1.
// The image at level 0 contains the original size.
// Precondition: the PGF image contains a valid header (see also SetHeader(...)) and WriteHeader() has been called before Write().
// The ROI encoding scheme is used.
// It might throw an IOException.
// @param level The image level of the resulting image in the internal image buffer.
// @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.
// @param data Data Pointer to C++ class container to host callback procedure.
// @return The number of bytes written into stream.
UINT32 CPGFImage::Write(int level, CallbackPtr cb /*= NULL*/, void *data /*=NULL*/) THROW_ {
ASSERT(m_header.nLevels > 0);
ASSERT(0 <= level && level < m_header.nLevels);
ASSERT(m_encoder);
ASSERT(ROIisSupported());
 
// prepare for next level: save current file position, because the stream might have been reinitialized
UINT32 diff = m_encoder->ComputeBufferLength();
if (diff) {
m_streamReinitialized = true;
m_encoder->SetBufferStartPos();
}
 
const int levelDiff = m_currentLevel - level;
double percent = pow(0.25, levelDiff);
UINT32 nWrittenBytes = 0;
int levelIndex = m_header.nLevels - 1 - m_currentLevel;
 
// encoding scheme with ROI
while (m_currentLevel > level) {
levelIndex++;
 
WriteLevel();
 
if (m_levelLength) nWrittenBytes += m_levelLength[levelIndex];
 
// now update progress
if (cb) {
percent *= 4;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
 
// automatically closing
if (m_currentLevel == 0) {
if (!m_streamReinitialized) {
// don't write level lengths, if the stream position changed inbetween two Write operations
m_encoder->WriteLevelLength();
}
// delete encoder
delete m_encoder; m_encoder = NULL;
}
 
return nWrittenBytes;
}
#endif // __PGFROISUPPORT__
 
 
//////////////////////////////////////////////////////////////////
// Check for valid import image mode.
// @param mode Image mode
// @return True if an image of given mode can be imported with ImportBitmap(...)
bool CPGFImage::ImportIsSupported(BYTE mode) {
size_t size = DataTSize;
 
if (size >= 2) {
switch(mode) {
case ImageModeBitmap:
case ImageModeIndexedColor:
case ImageModeGrayScale:
case ImageModeRGBColor:
case ImageModeCMYKColor:
case ImageModeHSLColor:
case ImageModeHSBColor:
//case ImageModeDuotone:
case ImageModeLabColor:
case ImageModeRGB12:
case ImageModeRGBA:
return true;
}
}
if (size >= 3) {
switch(mode) {
case ImageModeGray16:
case ImageModeRGB16:
case ImageModeRGB48:
case ImageModeLab48:
case ImageModeCMYK64:
//case ImageModeDuotone16:
return true;
}
}
if (size >=4) {
switch(mode) {
case ImageModeGray31:
return true;
}
}
return false;
}
 
//////////////////////////////////////////////////////////////////////
/// Retrieves red, green, blue (RGB) color values from a range of entries in the palette of the DIB section.
/// It might throw an IOException.
/// @param iFirstColor The color table index of the first entry to retrieve.
/// @param nColors The number of color table entries to retrieve.
/// @param prgbColors A pointer to the array of RGBQUAD structures to retrieve the color table entries.
void CPGFImage::GetColorTable(UINT32 iFirstColor, UINT32 nColors, RGBQUAD* prgbColors) const THROW_ {
if (iFirstColor + nColors > ColorTableLen) ReturnWithError(ColorTableError);
 
for (UINT32 i=iFirstColor, j=0; j < nColors; i++, j++) {
prgbColors[j] = m_postHeader.clut[i];
}
}
 
//////////////////////////////////////////////////////////////////////
/// Sets the red, green, blue (RGB) color values for a range of entries in the palette (clut).
/// It might throw an IOException.
/// @param iFirstColor The color table index of the first entry to set.
/// @param nColors The number of color table entries to set.
/// @param prgbColors A pointer to the array of RGBQUAD structures to set the color table entries.
void CPGFImage::SetColorTable(UINT32 iFirstColor, UINT32 nColors, const RGBQUAD* prgbColors) THROW_ {
if (iFirstColor + nColors > ColorTableLen) ReturnWithError(ColorTableError);
 
for (UINT32 i=iFirstColor, j=0; j < nColors; i++, j++) {
m_postHeader.clut[i] = prgbColors[j];
}
}
 
//////////////////////////////////////////////////////////////////
// Buffer transform from interleaved to channel seperated format
// the absolute value of pitch is the number of bytes of an image row
// if pitch is negative, then buff points to the last row of a bottom-up image (first byte on last row)
// if pitch is positive, then buff points to the first row of a top-down image (first byte)
// bpp is the number of bits per pixel used in image buffer buff
//
// RGB is transformed into YUV format (ordering of buffer data is BGR[A])
// Y = (R + 2*G + B)/4 -128
// U = R - G
// V = B - G
//
// Since PGF Codec version 2.0 images are stored in top-down direction
//
// 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
// provide a channelMap of size of expected channels (depending on image mode). For example, PGF expects in RGB color mode a channel sequence BGR.
// If your provided image buffer contains a channel sequence ARGB, then the channelMap looks like { 3, 2, 1 }.
void CPGFImage::RgbToYuv(int pitch, UINT8* buff, BYTE bpp, int channelMap[], CallbackPtr cb, void *data /*=NULL*/) THROW_ {
ASSERT(buff);
int yPos = 0, cnt = 0;
double percent = 0;
const double dP = 1.0/m_header.height;
int defMap[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; ASSERT(sizeof(defMap)/sizeof(defMap[0]) == MaxChannels);
 
if (channelMap == NULL) channelMap = defMap;
 
switch(m_header.mode) {
case ImageModeBitmap:
{
ASSERT(m_header.channels == 1);
ASSERT(m_header.bpp == 1);
ASSERT(bpp == 1);
 
const UINT32 w2 = (m_header.width + 7)/8;
DataT* y = m_channel[0]; ASSERT(y);
 
for (UINT32 h=0; h < m_header.height; h++) {
if (cb) {
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
percent += dP;
}
 
for (UINT32 w=0; w < w2; w++) {
y[yPos++] = buff[w] - YUVoffset8;
}
buff += pitch;
}
}
break;
case ImageModeIndexedColor:
case ImageModeGrayScale:
case ImageModeHSLColor:
case ImageModeHSBColor:
case ImageModeLabColor:
{
ASSERT(m_header.channels >= 1);
ASSERT(m_header.bpp == m_header.channels*8);
ASSERT(bpp%8 == 0);
const int channels = bpp/8; ASSERT(channels >= m_header.channels);
 
for (UINT32 h=0; h < m_header.height; h++) {
if (cb) {
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
percent += dP;
}
 
cnt = 0;
for (UINT32 w=0; w < m_header.width; w++) {
for (int c=0; c < m_header.channels; c++) {
m_channel[c][yPos] = buff[cnt + channelMap[c]] - YUVoffset8;
}
cnt += channels;
yPos++;
}
buff += pitch;
}
}
break;
case ImageModeGray16:
case ImageModeLab48:
{
ASSERT(m_header.channels >= 1);
ASSERT(m_header.bpp == m_header.channels*16);
ASSERT(bpp%16 == 0);
 
UINT16 *buff16 = (UINT16 *)buff;
const int pitch16 = pitch/2;
const int channels = bpp/16; ASSERT(channels >= m_header.channels);
const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1);
 
for (UINT32 h=0; h < m_header.height; h++) {
if (cb) {
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
percent += dP;
}
 
cnt = 0;
for (UINT32 w=0; w < m_header.width; w++) {
for (int c=0; c < m_header.channels; c++) {
m_channel[c][yPos] = buff16[cnt + channelMap[c]] - yuvOffset16;
}
cnt += channels;
yPos++;
}
buff16 += pitch16;
}
}
break;
case ImageModeRGBColor:
{
ASSERT(m_header.channels == 3);
ASSERT(m_header.bpp == m_header.channels*8);
ASSERT(bpp%8 == 0);
 
DataT* y = m_channel[0]; ASSERT(y);
DataT* u = m_channel[1]; ASSERT(u);
DataT* v = m_channel[2]; ASSERT(v);
const int channels = bpp/8; ASSERT(channels >= m_header.channels);
UINT8 b, g, r;
 
for (UINT32 h=0; h < m_header.height; h++) {
if (cb) {
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
percent += dP;
}
 
cnt = 0;
for (UINT32 w=0; w < m_header.width; w++) {
b = buff[cnt + channelMap[0]];
g = buff[cnt + channelMap[1]];
r = buff[cnt + channelMap[2]];
// Yuv
y[yPos] = ((b + (g << 1) + r) >> 2) - YUVoffset8;
u[yPos] = r - g;
v[yPos] = b - g;
yPos++;
cnt += channels;
}
buff += pitch;
}
}
break;
case ImageModeRGB48:
{
ASSERT(m_header.channels == 3);
ASSERT(m_header.bpp == m_header.channels*16);
ASSERT(bpp%16 == 0);
 
UINT16 *buff16 = (UINT16 *)buff;
const int pitch16 = pitch/2;
const int channels = bpp/16; ASSERT(channels >= m_header.channels);
const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1);
 
DataT* y = m_channel[0]; ASSERT(y);
DataT* u = m_channel[1]; ASSERT(u);
DataT* v = m_channel[2]; ASSERT(v);
UINT16 b, g, r;
 
for (UINT32 h=0; h < m_header.height; h++) {
if (cb) {
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
percent += dP;
}
 
cnt = 0;
for (UINT32 w=0; w < m_header.width; w++) {
b = buff16[cnt + channelMap[0]];
g = buff16[cnt + channelMap[1]];
r = buff16[cnt + channelMap[2]];
// Yuv
y[yPos] = ((b + (g << 1) + r) >> 2) - yuvOffset16;
u[yPos] = r - g;
v[yPos] = b - g;
yPos++;
cnt += channels;
}
buff16 += pitch16;
}
}
break;
case ImageModeRGBA:
case ImageModeCMYKColor:
{
ASSERT(m_header.channels == 4);
ASSERT(m_header.bpp == m_header.channels*8);
ASSERT(bpp%8 == 0);
const int channels = bpp/8; ASSERT(channels >= m_header.channels);
 
DataT* y = m_channel[0]; ASSERT(y);
DataT* u = m_channel[1]; ASSERT(u);
DataT* v = m_channel[2]; ASSERT(v);
DataT* a = m_channel[3]; ASSERT(a);
UINT8 b, g, r;
 
for (UINT32 h=0; h < m_header.height; h++) {
if (cb) {
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
percent += dP;
}
 
cnt = 0;
for (UINT32 w=0; w < m_header.width; w++) {
b = buff[cnt + channelMap[0]];
g = buff[cnt + channelMap[1]];
r = buff[cnt + channelMap[2]];
// Yuv
y[yPos] = ((b + (g << 1) + r) >> 2) - YUVoffset8;
u[yPos] = r - g;
v[yPos] = b - g;
a[yPos++] = buff[cnt + channelMap[3]] - YUVoffset8;
cnt += channels;
}
buff += pitch;
}
}
break;
case ImageModeCMYK64:
{
ASSERT(m_header.channels == 4);
ASSERT(m_header.bpp == m_header.channels*16);
ASSERT(bpp%16 == 0);
 
UINT16 *buff16 = (UINT16 *)buff;
const int pitch16 = pitch/2;
const int channels = bpp/16; ASSERT(channels >= m_header.channels);
const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1);
 
DataT* y = m_channel[0]; ASSERT(y);
DataT* u = m_channel[1]; ASSERT(u);
DataT* v = m_channel[2]; ASSERT(v);
DataT* a = m_channel[3]; ASSERT(a);
UINT16 b, g, r;
 
for (UINT32 h=0; h < m_header.height; h++) {
if (cb) {
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
percent += dP;
}
 
cnt = 0;
for (UINT32 w=0; w < m_header.width; w++) {
b = buff16[cnt + channelMap[0]];
g = buff16[cnt + channelMap[1]];
r = buff16[cnt + channelMap[2]];
// Yuv
y[yPos] = ((b + (g << 1) + r) >> 2) - yuvOffset16;
u[yPos] = r - g;
v[yPos] = b - g;
a[yPos++] = buff16[cnt + channelMap[3]] - yuvOffset16;
cnt += channels;
}
buff16 += pitch16;
}
}
break;
#ifdef __PGF32SUPPORT__
case ImageModeGray31:
{
ASSERT(m_header.channels == 1);
ASSERT(m_header.bpp == 32);
ASSERT(bpp == 32);
ASSERT(DataTSize == sizeof(UINT32));
 
DataT* y = m_channel[0]; ASSERT(y);
 
UINT32 *buff32 = (UINT32 *)buff;
const int pitch32 = pitch/4;
const DataT yuvOffset31 = 1 << (UsedBitsPerChannel() - 1);
 
for (UINT32 h=0; h < m_header.height; h++) {
if (cb) {
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
percent += dP;
}
 
for (UINT32 w=0; w < m_header.width; w++) {
ASSERT(buff32[cnt] < MaxValue);
y[yPos++] = buff32[w] - yuvOffset31;
}
buff32 += pitch32;
}
}
break;
#endif
case ImageModeRGB12:
{
ASSERT(m_header.channels == 3);
ASSERT(m_header.bpp == m_header.channels*4);
ASSERT(bpp == m_header.channels*4);
 
DataT* y = m_channel[0]; ASSERT(y);
DataT* u = m_channel[1]; ASSERT(u);
DataT* v = m_channel[2]; ASSERT(v);
 
UINT8 rgb = 0, b, g, r;
 
for (UINT32 h=0; h < m_header.height; h++) {
if (cb) {
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
percent += dP;
}
 
cnt = 0;
for (UINT32 w=0; w < m_header.width; w++) {
if (w%2 == 0) {
// even pixel position
rgb = buff[cnt];
b = rgb & 0x0F;
g = (rgb & 0xF0) >> 4;
cnt++;
rgb = buff[cnt];
r = rgb & 0x0F;
} else {
// odd pixel position
b = (rgb & 0xF0) >> 4;
cnt++;
rgb = buff[cnt];
g = rgb & 0x0F;
r = (rgb & 0xF0) >> 4;
cnt++;
}
 
// Yuv
y[yPos] = ((b + (g << 1) + r) >> 2) - YUVoffset4;
u[yPos] = r - g;
v[yPos] = b - g;
yPos++;
}
buff += pitch;
}
}
break;
case ImageModeRGB16:
{
ASSERT(m_header.channels == 3);
ASSERT(m_header.bpp == 16);
ASSERT(bpp == 16);
 
DataT* y = m_channel[0]; ASSERT(y);
DataT* u = m_channel[1]; ASSERT(u);
DataT* v = m_channel[2]; ASSERT(v);
 
UINT16 *buff16 = (UINT16 *)buff;
UINT16 rgb, b, g, r;
const int pitch16 = pitch/2;
 
for (UINT32 h=0; h < m_header.height; h++) {
if (cb) {
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
percent += dP;
}
for (UINT32 w=0; w < m_header.width; w++) {
rgb = buff16[w];
r = (rgb & 0xF800) >> 10; // highest 5 bits
g = (rgb & 0x07E0) >> 5; // middle 6 bits
b = (rgb & 0x001F) << 1; // lowest 5 bits
// Yuv
y[yPos] = ((b + (g << 1) + r) >> 2) - YUVoffset6;
u[yPos] = r - g;
v[yPos] = b - g;
yPos++;
}
 
buff16 += pitch16;
}
}
break;
default:
ASSERT(false);
}
}
 
//////////////////////////////////////////////////////////////////
// Get image data in interleaved format: (ordering of RGB data is BGR[A])
// Upsampling, YUV to RGB transform and interleaving are done here to reduce the number
// of passes over the data.
// The absolute value of pitch is the number of bytes of an image row of the given image buffer.
// If pitch is negative, then the image buffer must point to the last row of a bottom-up image (first byte on last row).
// if pitch is positive, then the image buffer must point to the first row of a top-down image (first byte).
// 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
// provide a channelMap of size of expected channels (depending on image mode). For example, PGF provides a channel sequence BGR in RGB color mode.
// If your provided image buffer expects a channel sequence ARGB, then the channelMap looks like { 3, 2, 1 }.
// It might throw an IOException.
// @param pitch The number of bytes of a row of the image buffer.
// @param buff An image buffer.
// @param bpp The number of bits per pixel used in image buffer.
// @param channelMap A integer array containing the mapping of PGF channel ordering to expected channel ordering.
// @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.
// @param data Data Pointer to C++ class container to host callback procedure.
void CPGFImage::GetBitmap(int pitch, UINT8* buff, BYTE bpp, int channelMap[] /*= NULL */, CallbackPtr cb /*= NULL*/, void *data /*=NULL*/) const THROW_ {
ASSERT(buff);
UINT32 w = m_width[0];
UINT32 h = m_height[0];
UINT8* targetBuff = 0; // used if ROI is used
UINT8* buffStart = 0; // used if ROI is used
int targetPitch = 0; // used if ROI is used
 
#ifdef __PGFROISUPPORT__
const PGFRect& roi = (ROIisSupported()) ? m_wtChannel[0]->GetROI(m_currentLevel) : PGFRect(0, 0, w, h); // roi is usually larger than m_roi
const PGFRect levelRoi(LevelWidth(m_roi.left, m_currentLevel), LevelHeight(m_roi.top, m_currentLevel), LevelWidth(m_roi.Width(), m_currentLevel), LevelHeight(m_roi.Height(), m_currentLevel));
ASSERT(w == roi.Width() && h == roi.Height());
ASSERT(roi.left <= levelRoi.left && levelRoi.right <= roi.right);
ASSERT(roi.top <= levelRoi.top && levelRoi.bottom <= roi.bottom);
 
if (ROIisSupported() && (levelRoi.Width() < w || levelRoi.Height() < h)) {
// ROI is used -> create a temporary image buffer for roi
// compute pitch
targetPitch = pitch;
pitch = AlignWordPos(w*bpp)/8;
 
// create temporary output buffer
targetBuff = buff;
buff = buffStart = new UINT8[pitch*h];
}
#endif
 
const bool wOdd = (1 == w%2);
 
const double dP = 1.0/h;
int defMap[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; ASSERT(sizeof(defMap)/sizeof(defMap[0]) == MaxChannels);
if (channelMap == NULL) channelMap = defMap;
int sampledPos = 0, yPos = 0;
DataT uAvg, vAvg;
double percent = 0;
UINT32 i, j;
 
switch(m_header.mode) {
case ImageModeBitmap:
{
ASSERT(m_header.channels == 1);
ASSERT(m_header.bpp == 1);
ASSERT(bpp == 1);
 
const UINT32 w2 = (w + 7)/8;
DataT* y = m_channel[0]; ASSERT(y);
 
for (i=0; i < h; i++) {
for (j=0; j < w2; j++) {
buff[j] = Clamp(y[yPos++] + YUVoffset8);
}
buff += pitch;
 
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
break;
}
case ImageModeIndexedColor:
case ImageModeGrayScale:
case ImageModeHSLColor:
case ImageModeHSBColor:
{
ASSERT(m_header.channels >= 1);
ASSERT(m_header.bpp == m_header.channels*8);
ASSERT(bpp%8 == 0);
 
int cnt, channels = bpp/8; ASSERT(channels >= m_header.channels);
 
for (i=0; i < h; i++) {
cnt = 0;
for (j=0; j < w; j++) {
for (int c=0; c < m_header.channels; c++) {
buff[cnt + channelMap[c]] = Clamp(m_channel[c][yPos] + YUVoffset8);
}
cnt += channels;
yPos++;
}
buff += pitch;
 
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
break;
}
case ImageModeGray16:
{
ASSERT(m_header.channels >= 1);
ASSERT(m_header.bpp == m_header.channels*16);
 
const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1);
const int shift = UsedBitsPerChannel() - 8;
int cnt, channels;
 
if (bpp%16 == 0) {
UINT16 *buff16 = (UINT16 *)buff;
int pitch16 = pitch/2;
channels = bpp/16; ASSERT(channels >= m_header.channels);
 
for (i=0; i < h; i++) {
cnt = 0;
for (j=0; j < w; j++) {
for (int c=0; c < m_header.channels; c++) {
buff16[cnt + channelMap[c]] = Clamp16(m_channel[c][yPos] + yuvOffset16);
}
cnt += channels;
yPos++;
}
buff16 += pitch16;
 
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
} else {
ASSERT(bpp%8 == 0);
channels = bpp/8; ASSERT(channels >= m_header.channels);
 
for (i=0; i < h; i++) {
cnt = 0;
for (j=0; j < w; j++) {
for (int c=0; c < m_header.channels; c++) {
buff[cnt + channelMap[c]] = UINT8(Clamp16(m_channel[c][yPos] + yuvOffset16) >> shift);
}
cnt += channels;
yPos++;
}
buff += pitch;
 
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
}
break;
}
case ImageModeRGBColor:
{
ASSERT(m_header.channels == 3);
ASSERT(m_header.bpp == m_header.channels*8);
ASSERT(bpp%8 == 0);
ASSERT(bpp >= m_header.bpp);
 
DataT* y = m_channel[0]; ASSERT(y);
DataT* u = m_channel[1]; ASSERT(u);
DataT* v = m_channel[2]; ASSERT(v);
UINT8 *buffg = &buff[channelMap[1]],
*buffr = &buff[channelMap[2]],
*buffb = &buff[channelMap[0]];
UINT8 g;
int cnt, channels = bpp/8;
if(m_downsample){
for (i=0; i < h; i++) {
if (i%2) sampledPos -= (w + 1)/2;
cnt = 0;
for (j=0; j < w; j++) {
// image was downsampled
uAvg = u[sampledPos];
vAvg = v[sampledPos];
// Yuv
buffg[cnt] = g = Clamp(y[yPos] + YUVoffset8 - ((uAvg + vAvg ) >> 2)); // must be logical shift operator
buffr[cnt] = Clamp(uAvg + g);
buffb[cnt] = Clamp(vAvg + g);
yPos++;
cnt += channels;
if (j%2) sampledPos++;
}
buffb += pitch;
buffg += pitch;
buffr += pitch;
if (wOdd) sampledPos++;
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
}else{
for (i=0; i < h; i++) {
cnt = 0;
for (j = 0; j < w; j++) {
uAvg = u[yPos];
vAvg = v[yPos];
// Yuv
buffg[cnt] = g = Clamp(y[yPos] + YUVoffset8 - ((uAvg + vAvg ) >> 2)); // must be logical shift operator
buffr[cnt] = Clamp(uAvg + g);
buffb[cnt] = Clamp(vAvg + g);
yPos++;
cnt += channels;
}
buffb += pitch;
buffg += pitch;
buffr += pitch;
 
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
}
break;
}
case ImageModeRGB48:
{
ASSERT(m_header.channels == 3);
ASSERT(m_header.bpp == 48);
 
const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1);
const int shift = UsedBitsPerChannel() - 8;
 
DataT* y = m_channel[0]; ASSERT(y);
DataT* u = m_channel[1]; ASSERT(u);
DataT* v = m_channel[2]; ASSERT(v);
UINT16 g;
int cnt, channels;
 
if (bpp >= 48 && bpp%16 == 0) {
UINT16 *buff16 = (UINT16 *)buff;
int pitch16 = pitch/2;
channels = bpp/16; ASSERT(channels >= m_header.channels);
 
for (i=0; i < h; i++) {
if (i%2) sampledPos -= (w + 1)/2;
cnt = 0;
for (j=0; j < w; j++) {
if (m_downsample) {
// image was downsampled
uAvg = u[sampledPos];
vAvg = v[sampledPos];
} else {
uAvg = u[yPos];
vAvg = v[yPos];
}
// Yuv
buff16[cnt + channelMap[1]] = g = Clamp16(y[yPos] + yuvOffset16 - ((uAvg + vAvg ) >> 2)); // must be logical shift operator
buff16[cnt + channelMap[2]] = Clamp16(uAvg + g);
buff16[cnt + channelMap[0]] = Clamp16(vAvg + g);
yPos++;
cnt += channels;
if (j%2) sampledPos++;
}
buff16 += pitch16;
if (wOdd) sampledPos++;
 
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
} else {
ASSERT(bpp%8 == 0);
channels = bpp/8; ASSERT(channels >= m_header.channels);
 
for (i=0; i < h; i++) {
if (i%2) sampledPos -= (w + 1)/2;
cnt = 0;
for (j=0; j < w; j++) {
if (m_downsample) {
// image was downsampled
uAvg = u[sampledPos];
vAvg = v[sampledPos];
} else {
uAvg = u[yPos];
vAvg = v[yPos];
}
// Yuv
g = Clamp16(y[yPos] + yuvOffset16 - ((uAvg + vAvg ) >> 2)); // must be logical shift operator
buff[cnt + channelMap[1]] = UINT8(g >> shift);
buff[cnt + channelMap[2]] = UINT8(Clamp16(uAvg + g) >> shift);
buff[cnt + channelMap[0]] = UINT8(Clamp16(vAvg + g) >> shift);
yPos++;
cnt += channels;
if (j%2) sampledPos++;
}
buff += pitch;
if (wOdd) sampledPos++;
 
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
}
break;
}
case ImageModeLabColor:
{
ASSERT(m_header.channels == 3);
ASSERT(m_header.bpp == m_header.channels*8);
ASSERT(bpp%8 == 0);
 
DataT* l = m_channel[0]; ASSERT(l);
DataT* a = m_channel[1]; ASSERT(a);
DataT* b = m_channel[2]; ASSERT(b);
int cnt, channels = bpp/8; ASSERT(channels >= m_header.channels);
 
for (i=0; i < h; i++) {
if (i%2) sampledPos -= (w + 1)/2;
cnt = 0;
for (j=0; j < w; j++) {
if (m_downsample) {
// image was downsampled
uAvg = a[sampledPos];
vAvg = b[sampledPos];
} else {
uAvg = a[yPos];
vAvg = b[yPos];
}
buff[cnt + channelMap[0]] = Clamp(l[yPos] + YUVoffset8);
buff[cnt + channelMap[1]] = Clamp(uAvg + YUVoffset8);
buff[cnt + channelMap[2]] = Clamp(vAvg + YUVoffset8);
cnt += channels;
yPos++;
if (j%2) sampledPos++;
}
buff += pitch;
if (wOdd) sampledPos++;
 
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
break;
}
case ImageModeLab48:
{
ASSERT(m_header.channels == 3);
ASSERT(m_header.bpp == m_header.channels*16);
 
const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1);
const int shift = UsedBitsPerChannel() - 8;
 
DataT* l = m_channel[0]; ASSERT(l);
DataT* a = m_channel[1]; ASSERT(a);
DataT* b = m_channel[2]; ASSERT(b);
int cnt, channels;
 
if (bpp%16 == 0) {
UINT16 *buff16 = (UINT16 *)buff;
int pitch16 = pitch/2;
channels = bpp/16; ASSERT(channels >= m_header.channels);
 
for (i=0; i < h; i++) {
if (i%2) sampledPos -= (w + 1)/2;
cnt = 0;
for (j=0; j < w; j++) {
if (m_downsample) {
// image was downsampled
uAvg = a[sampledPos];
vAvg = b[sampledPos];
} else {
uAvg = a[yPos];
vAvg = b[yPos];
}
buff16[cnt + channelMap[0]] = Clamp16(l[yPos] + yuvOffset16);
buff16[cnt + channelMap[1]] = Clamp16(uAvg + yuvOffset16);
buff16[cnt + channelMap[2]] = Clamp16(vAvg + yuvOffset16);
cnt += channels;
yPos++;
if (j%2) sampledPos++;
}
buff16 += pitch16;
if (wOdd) sampledPos++;
 
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
} else {
ASSERT(bpp%8 == 0);
channels = bpp/8; ASSERT(channels >= m_header.channels);
 
for (i=0; i < h; i++) {
if (i%2) sampledPos -= (w + 1)/2;
cnt = 0;
for (j=0; j < w; j++) {
if (m_downsample) {
// image was downsampled
uAvg = a[sampledPos];
vAvg = b[sampledPos];
} else {
uAvg = a[yPos];
vAvg = b[yPos];
}
buff[cnt + channelMap[0]] = UINT8(Clamp16(l[yPos] + yuvOffset16) >> shift);
buff[cnt + channelMap[1]] = UINT8(Clamp16(uAvg + yuvOffset16) >> shift);
buff[cnt + channelMap[2]] = UINT8(Clamp16(vAvg + yuvOffset16) >> shift);
cnt += channels;
yPos++;
if (j%2) sampledPos++;
}
buff += pitch;
if (wOdd) sampledPos++;
 
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
}
break;
}
case ImageModeRGBA:
case ImageModeCMYKColor:
{
ASSERT(m_header.channels == 4);
ASSERT(m_header.bpp == m_header.channels*8);
ASSERT(bpp%8 == 0);
 
DataT* y = m_channel[0]; ASSERT(y);
DataT* u = m_channel[1]; ASSERT(u);
DataT* v = m_channel[2]; ASSERT(v);
DataT* a = m_channel[3]; ASSERT(a);
UINT8 g, aAvg;
int cnt, channels = bpp/8; ASSERT(channels >= m_header.channels);
 
for (i=0; i < h; i++) {
if (i%2) sampledPos -= (w + 1)/2;
cnt = 0;
for (j=0; j < w; j++) {
if (m_downsample) {
// image was downsampled
uAvg = u[sampledPos];
vAvg = v[sampledPos];
aAvg = Clamp(a[sampledPos] + YUVoffset8);
} else {
uAvg = u[yPos];
vAvg = v[yPos];
aAvg = Clamp(a[yPos] + YUVoffset8);
}
// Yuv
buff[cnt + channelMap[1]] = g = Clamp(y[yPos] + YUVoffset8 - ((uAvg + vAvg ) >> 2)); // must be logical shift operator
buff[cnt + channelMap[2]] = Clamp(uAvg + g);
buff[cnt + channelMap[0]] = Clamp(vAvg + g);
buff[cnt + channelMap[3]] = aAvg;
yPos++;
cnt += channels;
if (j%2) sampledPos++;
}
buff += pitch;
if (wOdd) sampledPos++;
 
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
break;
}
case ImageModeCMYK64:
{
ASSERT(m_header.channels == 4);
ASSERT(m_header.bpp == 64);
 
const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1);
const int shift = UsedBitsPerChannel() - 8;
 
DataT* y = m_channel[0]; ASSERT(y);
DataT* u = m_channel[1]; ASSERT(u);
DataT* v = m_channel[2]; ASSERT(v);
DataT* a = m_channel[3]; ASSERT(a);
UINT16 g, aAvg;
int cnt, channels;
 
if (bpp%16 == 0) {
UINT16 *buff16 = (UINT16 *)buff;
int pitch16 = pitch/2;
channels = bpp/16; ASSERT(channels >= m_header.channels);
 
for (i=0; i < h; i++) {
if (i%2) sampledPos -= (w + 1)/2;
cnt = 0;
for (j=0; j < w; j++) {
if (m_downsample) {
// image was downsampled
uAvg = u[sampledPos];
vAvg = v[sampledPos];
aAvg = Clamp16(a[sampledPos] + yuvOffset16);
} else {
uAvg = u[yPos];
vAvg = v[yPos];
aAvg = Clamp16(a[yPos] + yuvOffset16);
}
// Yuv
buff16[cnt + channelMap[1]] = g = Clamp16(y[yPos] + yuvOffset16 - ((uAvg + vAvg ) >> 2)); // must be logical shift operator
buff16[cnt + channelMap[2]] = Clamp16(uAvg + g);
buff16[cnt + channelMap[0]] = Clamp16(vAvg + g);
buff16[cnt + channelMap[3]] = aAvg;
yPos++;
cnt += channels;
if (j%2) sampledPos++;
}
buff16 += pitch16;
if (wOdd) sampledPos++;
 
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
} else {
ASSERT(bpp%8 == 0);
channels = bpp/8; ASSERT(channels >= m_header.channels);
 
for (i=0; i < h; i++) {
if (i%2) sampledPos -= (w + 1)/2;
cnt = 0;
for (j=0; j < w; j++) {
if (m_downsample) {
// image was downsampled
uAvg = u[sampledPos];
vAvg = v[sampledPos];
aAvg = Clamp16(a[sampledPos] + yuvOffset16);
} else {
uAvg = u[yPos];
vAvg = v[yPos];
aAvg = Clamp16(a[yPos] + yuvOffset16);
}
// Yuv
g = Clamp16(y[yPos] + yuvOffset16 - ((uAvg + vAvg ) >> 2)); // must be logical shift operator
buff[cnt + channelMap[1]] = UINT8(g >> shift);
buff[cnt + channelMap[2]] = UINT8(Clamp16(uAvg + g) >> shift);
buff[cnt + channelMap[0]] = UINT8(Clamp16(vAvg + g) >> shift);
buff[cnt + channelMap[3]] = UINT8(aAvg >> shift);
yPos++;
cnt += channels;
if (j%2) sampledPos++;
}
buff += pitch;
if (wOdd) sampledPos++;
 
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
}
break;
}
#ifdef __PGF32SUPPORT__
case ImageModeGray31:
{
ASSERT(m_header.channels == 1);
ASSERT(m_header.bpp == 32);
 
const int yuvOffset31 = 1 << (UsedBitsPerChannel() - 1);
const int shift = UsedBitsPerChannel() - 8;
 
DataT* y = m_channel[0]; ASSERT(y);
 
if (bpp == 32) {
UINT32 *buff32 = (UINT32 *)buff;
int pitch32 = pitch/4;
 
for (i=0; i < h; i++) {
for (j=0; j < w; j++) {
buff32[j] = Clamp31(y[yPos++] + yuvOffset31);
}
buff32 += pitch32;
 
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
} else {
ASSERT(bpp == 8);
 
for (i=0; i < h; i++) {
for (j=0; j < w; j++) {
buff[j] = UINT8(Clamp31(y[yPos++] + yuvOffset31) >> shift);
}
buff += pitch;
 
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
}
break;
}
#endif
case ImageModeRGB12:
{
ASSERT(m_header.channels == 3);
ASSERT(m_header.bpp == m_header.channels*4);
ASSERT(bpp == m_header.channels*4);
ASSERT(!m_downsample);
 
DataT* y = m_channel[0]; ASSERT(y);
DataT* u = m_channel[1]; ASSERT(u);
DataT* v = m_channel[2]; ASSERT(v);
UINT16 yval;
int cnt;
 
for (i=0; i < h; i++) {
cnt = 0;
for (j=0; j < w; j++) {
// Yuv
uAvg = u[yPos];
vAvg = v[yPos];
yval = Clamp4(y[yPos++] + YUVoffset4 - ((uAvg + vAvg ) >> 2)); // must be logical shift operator
if (j%2 == 0) {
buff[cnt] = UINT8(Clamp4(vAvg + yval) | (yval << 4));
cnt++;
buff[cnt] = Clamp4(uAvg + yval);
} else {
buff[cnt] |= Clamp4(vAvg + yval) << 4;
cnt++;
buff[cnt] = UINT8(yval | (Clamp4(uAvg + yval) << 4));
cnt++;
}
}
buff += pitch;
 
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
break;
}
case ImageModeRGB16:
{
ASSERT(m_header.channels == 3);
ASSERT(m_header.bpp == 16);
ASSERT(bpp == 16);
ASSERT(!m_downsample);
 
DataT* y = m_channel[0]; ASSERT(y);
DataT* u = m_channel[1]; ASSERT(u);
DataT* v = m_channel[2]; ASSERT(v);
UINT16 yval;
UINT16 *buff16 = (UINT16 *)buff;
int pitch16 = pitch/2;
 
for (i=0; i < h; i++) {
for (j=0; j < w; j++) {
// Yuv
uAvg = u[yPos];
vAvg = v[yPos];
yval = Clamp6(y[yPos++] + YUVoffset6 - ((uAvg + vAvg ) >> 2)); // must be logical shift operator
buff16[j] = (yval << 5) | ((Clamp6(uAvg + yval) >> 1) << 11) | (Clamp6(vAvg + yval) >> 1);
}
buff16 += pitch16;
 
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
break;
}
default:
ASSERT(false);
}
 
#ifdef __PGFROISUPPORT__
if (targetBuff) {
// copy valid ROI (m_roi) from temporary buffer (roi) to target buffer
if (bpp%8 == 0) {
BYTE bypp = bpp/8;
buff = buffStart + (levelRoi.top - roi.top)*pitch + (levelRoi.left - roi.left)*bypp;
w = levelRoi.Width()*bypp;
h = levelRoi.Height();
 
for (i=0; i < h; i++) {
for (j=0; j < w; j++) {
targetBuff[j] = buff[j];
}
targetBuff += targetPitch;
buff += pitch;
}
} else {
// to do
}
 
delete[] buffStart;
}
#endif
}
 
//////////////////////////////////////////////////////////////////////
/// Get YUV image data in interleaved format: (ordering is YUV[A])
/// The absolute value of pitch is the number of bytes of an image row of the given image buffer.
/// If pitch is negative, then the image buffer must point to the last row of a bottom-up image (first byte on last row).
/// if pitch is positive, then the image buffer must point to the first row of a top-down image (first byte).
/// 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
/// provide a channelMap of size of expected channels (depending on image mode). For example, PGF provides a channel sequence BGR in RGB color mode.
/// If your provided image buffer expects a channel sequence VUY, then the channelMap looks like { 2, 1, 0 }.
/// It might throw an IOException.
/// @param pitch The number of bytes of a row of the image buffer.
/// @param buff An image buffer.
/// @param bpp The number of bits per pixel used in image buffer.
/// @param channelMap A integer array containing the mapping of PGF channel ordering to expected channel ordering.
/// @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.
void CPGFImage::GetYUV(int pitch, DataT* buff, BYTE bpp, int channelMap[] /*= NULL*/, CallbackPtr cb /*= NULL*/, void *data /*=NULL*/) const THROW_ {
ASSERT(buff);
const UINT32 w = m_width[0];
const UINT32 h = m_height[0];
const bool wOdd = (1 == w%2);
const int dataBits = DataTSize*8; ASSERT(dataBits == 16 || dataBits == 32);
const int pitch2 = pitch/DataTSize;
const int yuvOffset = (dataBits == 16) ? YUVoffset8 : YUVoffset16;
const double dP = 1.0/h;
 
int defMap[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; ASSERT(sizeof(defMap)/sizeof(defMap[0]) == MaxChannels);
if (channelMap == NULL) channelMap = defMap;
int sampledPos = 0, yPos = 0;
DataT uAvg, vAvg;
double percent = 0;
UINT32 i, j;
 
if (m_header.channels == 3) {
ASSERT(bpp%dataBits == 0);
 
DataT* y = m_channel[0]; ASSERT(y);
DataT* u = m_channel[1]; ASSERT(u);
DataT* v = m_channel[2]; ASSERT(v);
int cnt, channels = bpp/dataBits; ASSERT(channels >= m_header.channels);
 
for (i=0; i < h; i++) {
if (i%2) sampledPos -= (w + 1)/2;
cnt = 0;
for (j=0; j < w; j++) {
if (m_downsample) {
// image was downsampled
uAvg = u[sampledPos];
vAvg = v[sampledPos];
} else {
uAvg = u[yPos];
vAvg = v[yPos];
}
buff[cnt + channelMap[0]] = y[yPos];
buff[cnt + channelMap[1]] = uAvg;
buff[cnt + channelMap[2]] = vAvg;
yPos++;
cnt += channels;
if (j%2) sampledPos++;
}
buff += pitch2;
if (wOdd) sampledPos++;
 
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
} else if (m_header.channels == 4) {
ASSERT(m_header.bpp == m_header.channels*8);
ASSERT(bpp%dataBits == 0);
 
DataT* y = m_channel[0]; ASSERT(y);
DataT* u = m_channel[1]; ASSERT(u);
DataT* v = m_channel[2]; ASSERT(v);
DataT* a = m_channel[3]; ASSERT(a);
UINT8 aAvg;
int cnt, channels = bpp/dataBits; ASSERT(channels >= m_header.channels);
 
for (i=0; i < h; i++) {
if (i%2) sampledPos -= (w + 1)/2;
cnt = 0;
for (j=0; j < w; j++) {
if (m_downsample) {
// image was downsampled
uAvg = u[sampledPos];
vAvg = v[sampledPos];
aAvg = Clamp(a[sampledPos] + yuvOffset);
} else {
uAvg = u[yPos];
vAvg = v[yPos];
aAvg = Clamp(a[yPos] + yuvOffset);
}
// Yuv
buff[cnt + channelMap[0]] = y[yPos];
buff[cnt + channelMap[1]] = uAvg;
buff[cnt + channelMap[2]] = vAvg;
buff[cnt + channelMap[3]] = aAvg;
yPos++;
cnt += channels;
if (j%2) sampledPos++;
}
buff += pitch2;
if (wOdd) sampledPos++;
 
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
}
}
 
//////////////////////////////////////////////////////////////////////
/// Import a YUV image from a specified image buffer.
/// The absolute value of pitch is the number of bytes of an image row.
/// If pitch is negative, then buff points to the last row of a bottom-up image (first byte on last row).
/// If pitch is positive, then buff points to the first row of a top-down image (first byte).
/// 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
/// provide a channelMap of size of expected channels (depending on image mode). For example, PGF expects in RGB color mode a channel sequence BGR.
/// If your provided image buffer contains a channel sequence VUY, then the channelMap looks like { 2, 1, 0 }.
/// It might throw an IOException.
/// @param pitch The number of bytes of a row of the image buffer.
/// @param buff An image buffer.
/// @param bpp The number of bits per pixel used in image buffer.
/// @param channelMap A integer array containing the mapping of input channel ordering to expected channel ordering.
/// @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.
void CPGFImage::ImportYUV(int pitch, DataT *buff, BYTE bpp, int channelMap[] /*= NULL*/, CallbackPtr cb /*= NULL*/, void *data /*=NULL*/) THROW_ {
ASSERT(buff);
const double dP = 1.0/m_header.height;
const int dataBits = DataTSize*8; ASSERT(dataBits == 16 || dataBits == 32);
const int pitch2 = pitch/DataTSize;
const int yuvOffset = (dataBits == 16) ? YUVoffset8 : YUVoffset16;
 
int yPos = 0, cnt = 0;
double percent = 0;
int defMap[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; ASSERT(sizeof(defMap)/sizeof(defMap[0]) == MaxChannels);
 
if (channelMap == NULL) channelMap = defMap;
 
if (m_header.channels == 3) {
ASSERT(bpp%dataBits == 0);
 
DataT* y = m_channel[0]; ASSERT(y);
DataT* u = m_channel[1]; ASSERT(u);
DataT* v = m_channel[2]; ASSERT(v);
const int channels = bpp/dataBits; ASSERT(channels >= m_header.channels);
 
for (UINT32 h=0; h < m_header.height; h++) {
if (cb) {
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
percent += dP;
}
 
cnt = 0;
for (UINT32 w=0; w < m_header.width; w++) {
y[yPos] = buff[cnt + channelMap[0]];
u[yPos] = buff[cnt + channelMap[1]];
v[yPos] = buff[cnt + channelMap[2]];
yPos++;
cnt += channels;
}
buff += pitch2;
}
} else if (m_header.channels == 4) {
ASSERT(bpp%dataBits == 0);
 
DataT* y = m_channel[0]; ASSERT(y);
DataT* u = m_channel[1]; ASSERT(u);
DataT* v = m_channel[2]; ASSERT(v);
DataT* a = m_channel[3]; ASSERT(a);
const int channels = bpp/dataBits; ASSERT(channels >= m_header.channels);
 
for (UINT32 h=0; h < m_header.height; h++) {
if (cb) {
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
percent += dP;
}
 
cnt = 0;
for (UINT32 w=0; w < m_header.width; w++) {
y[yPos] = buff[cnt + channelMap[0]];
u[yPos] = buff[cnt + channelMap[1]];
v[yPos] = buff[cnt + channelMap[2]];
a[yPos] = buff[cnt + channelMap[3]] - yuvOffset;
yPos++;
cnt += channels;
}
buff += pitch2;
}
}
 
if (m_downsample) {
// Subsampling of the chrominance and alpha channels
for (int i=1; i < m_header.channels; i++) {
Downsample(i);
}
}
}
 
/*
* The Progressive Graphics File; http://www.libpgf.org
*
* $Date: 2007-02-03 13:04:21 +0100 (Sa, 03 Feb 2007) $
* $Revision: 280 $
*
* This file Copyright (C) 2006 xeraina GmbH, Switzerland
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
 
//////////////////////////////////////////////////////////////////////
/// @file PGFimage.cpp
/// @brief PGF image class implementation
/// @author C. Stamm
 
#include "PGFimage.h"
#include "Decoder.h"
#include "Encoder.h"
#include <cmath>
#include <cstring>
 
#define YUVoffset4 8 // 2^3
#define YUVoffset6 32 // 2^5
#define YUVoffset8 128 // 2^7
#define YUVoffset16 32768 // 2^15
//#define YUVoffset31 1073741824 // 2^30
 
//////////////////////////////////////////////////////////////////////
// global methods and variables
#ifdef NEXCEPTIONS
OSError _PGF_Error_;
 
OSError GetLastPGFError() {
OSError tmp = _PGF_Error_;
_PGF_Error_ = NoError;
return tmp;
}
#endif
 
//////////////////////////////////////////////////////////////////////
// Standard constructor: It is used to create a PGF instance for opening and reading.
CPGFImage::CPGFImage()
: m_decoder(0)
, m_encoder(0)
, m_levelLength(0)
, m_userDataPos(0)
, m_currentLevel(0)
, m_quant(0)
, m_downsample(false)
, m_favorSpeedOverSize(false)
, m_useOMPinEncoder(true)
, m_useOMPinDecoder(true)
, m_skipUserData(false)
#ifdef __PGFROISUPPORT__
, m_streamReinitialized(false)
#endif
, m_cb(0)
, m_cbArg(0)
, m_percent(0)
, m_progressMode(PM_Relative)
{
 
// init preHeader
memcpy(m_preHeader.magic, PGFMagic, 3);
m_preHeader.version = PGFVersion;
m_preHeader.hSize = 0;
 
// init postHeader
m_postHeader.userData = 0;
m_postHeader.userDataLen = 0;
 
// init channels
for (int i=0; i < MaxChannels; i++) {
m_channel[i] = 0;
m_wtChannel[i] = 0;
}
 
// set image width and height
m_width[0] = 0;
m_height[0] = 0;
}
 
//////////////////////////////////////////////////////////////////////
// Destructor: Destroy internal data structures.
CPGFImage::~CPGFImage() {
Destroy();
}
 
//////////////////////////////////////////////////////////////////////
// Destroy internal data structures.
// Destructor calls this method during destruction.
void CPGFImage::Destroy() {
Close();
 
for (int i=0; i < m_header.channels; i++) {
delete m_wtChannel[i]; m_wtChannel[i]=0; // also deletes m_channel
m_channel[i] = 0;
}
delete[] m_postHeader.userData; m_postHeader.userData = 0; m_postHeader.userDataLen = 0;
delete[] m_levelLength; m_levelLength = 0;
delete m_encoder; m_encoder = NULL;
m_userDataPos = 0;
}
 
//////////////////////////////////////////////////////////////////////
// Close PGF image after opening and reading.
// Destructor calls this method during destruction.
void CPGFImage::Close() {
delete m_decoder; m_decoder = 0;
}
 
/////////////////////////////////////////////////////////////////////////////
// Open a PGF image at current stream position: read pre-header, header, levelLength, and ckeck image type.
// Precondition: The stream has been opened for reading.
// It might throw an IOException.
// @param stream A PGF stream
void CPGFImage::Open(CPGFStream *stream) THROW_ {
ASSERT(stream);
 
// create decoder and read PGFPreHeader PGFHeader PGFPostHeader LevelLengths
m_decoder = new CDecoder(stream, m_preHeader, m_header, m_postHeader, m_levelLength,
m_userDataPos, m_useOMPinDecoder, m_skipUserData);
 
if (m_header.nLevels > MaxLevel) ReturnWithError(FormatCannotRead);
 
// set current level
m_currentLevel = m_header.nLevels;
 
// set image width and height
m_width[0] = m_header.width;
m_height[0] = m_header.height;
 
// complete header
CompleteHeader();
 
// interpret quant parameter
if (m_header.quality > DownsampleThreshold &&
(m_header.mode == ImageModeRGBColor ||
m_header.mode == ImageModeRGBA ||
m_header.mode == ImageModeRGB48 ||
m_header.mode == ImageModeCMYKColor ||
m_header.mode == ImageModeCMYK64 ||
m_header.mode == ImageModeLabColor ||
m_header.mode == ImageModeLab48)) {
m_downsample = true;
m_quant = m_header.quality - 1;
} else {
m_downsample = false;
m_quant = m_header.quality;
}
 
// set channel dimensions (chrominance is subsampled by factor 2)
if (m_downsample) {
for (int i=1; i < m_header.channels; i++) {
m_width[i] = (m_width[0] + 1)/2;
m_height[i] = (m_height[0] + 1)/2;
}
} else {
for (int i=1; i < m_header.channels; i++) {
m_width[i] = m_width[0];
m_height[i] = m_height[0];
}
}
 
if (m_header.nLevels > 0) {
// init wavelet subbands
for (int i=0; i < m_header.channels; i++) {
m_wtChannel[i] = new CWaveletTransform(m_width[i], m_height[i], m_header.nLevels);
}
 
// used in Read when PM_Absolute
m_percent = pow(0.25, m_header.nLevels);
 
} else {
// very small image: we don't use DWT and encoding
 
// read channels
for (int c=0; c < m_header.channels; c++) {
const UINT32 size = m_width[c]*m_height[c];
m_channel[c] = new(std::nothrow) DataT[size];
if (!m_channel[c]) ReturnWithError(InsufficientMemory);
 
// read channel data from stream
for (UINT32 i=0; i < size; i++) {
int count = DataTSize;
stream->Read(&count, &m_channel[c][i]);
if (count != DataTSize) ReturnWithError(MissingData);
}
}
}
}
 
////////////////////////////////////////////////////////////
void CPGFImage::CompleteHeader() {
if (m_header.mode == ImageModeUnknown) {
// undefined mode
switch(m_header.bpp) {
case 1: m_header.mode = ImageModeBitmap; break;
case 8: m_header.mode = ImageModeGrayScale; break;
case 12: m_header.mode = ImageModeRGB12; break;
case 16: m_header.mode = ImageModeRGB16; break;
case 24: m_header.mode = ImageModeRGBColor; break;
case 32: m_header.mode = ImageModeRGBA; break;
case 48: m_header.mode = ImageModeRGB48; break;
default: m_header.mode = ImageModeRGBColor; break;
}
}
if (!m_header.bpp) {
// undefined bpp
switch(m_header.mode) {
case ImageModeBitmap:
m_header.bpp = 1;
break;
case ImageModeIndexedColor:
case ImageModeGrayScale:
m_header.bpp = 8;
break;
case ImageModeRGB12:
m_header.bpp = 12;
break;
case ImageModeRGB16:
case ImageModeGray16:
m_header.bpp = 16;
break;
case ImageModeRGBColor:
case ImageModeLabColor:
m_header.bpp = 24;
break;
case ImageModeRGBA:
case ImageModeCMYKColor:
case ImageModeGray32:
m_header.bpp = 32;
break;
case ImageModeRGB48:
case ImageModeLab48:
m_header.bpp = 48;
break;
case ImageModeCMYK64:
m_header.bpp = 64;
break;
default:
ASSERT(false);
m_header.bpp = 24;
}
}
if (m_header.mode == ImageModeRGBColor && m_header.bpp == 32) {
// change mode
m_header.mode = ImageModeRGBA;
}
ASSERT(m_header.mode != ImageModeBitmap || m_header.bpp == 1);
ASSERT(m_header.mode != ImageModeIndexedColor || m_header.bpp == 8);
ASSERT(m_header.mode != ImageModeGrayScale || m_header.bpp == 8);
ASSERT(m_header.mode != ImageModeGray16 || m_header.bpp == 16);
ASSERT(m_header.mode != ImageModeGray32 || m_header.bpp == 32);
ASSERT(m_header.mode != ImageModeRGBColor || m_header.bpp == 24);
ASSERT(m_header.mode != ImageModeRGBA || m_header.bpp == 32);
ASSERT(m_header.mode != ImageModeRGB12 || m_header.bpp == 12);
ASSERT(m_header.mode != ImageModeRGB16 || m_header.bpp == 16);
ASSERT(m_header.mode != ImageModeRGB48 || m_header.bpp == 48);
ASSERT(m_header.mode != ImageModeLabColor || m_header.bpp == 24);
ASSERT(m_header.mode != ImageModeLab48 || m_header.bpp == 48);
ASSERT(m_header.mode != ImageModeCMYKColor || m_header.bpp == 32);
ASSERT(m_header.mode != ImageModeCMYK64 || m_header.bpp == 64);
 
// set number of channels
if (!m_header.channels) {
switch(m_header.mode) {
case ImageModeBitmap:
case ImageModeIndexedColor:
case ImageModeGrayScale:
case ImageModeGray16:
case ImageModeGray32:
m_header.channels = 1;
break;
case ImageModeRGBColor:
case ImageModeRGB12:
case ImageModeRGB16:
case ImageModeRGB48:
case ImageModeLabColor:
case ImageModeLab48:
m_header.channels = 3;
break;
case ImageModeRGBA:
case ImageModeCMYKColor:
case ImageModeCMYK64:
m_header.channels = 4;
break;
default:
ASSERT(false);
m_header.channels = 3;
}
}
 
// store used bits per channel
UINT8 bpc = m_header.bpp/m_header.channels;
if (bpc > 31) bpc = 31;
if (!m_header.usedBitsPerChannel || m_header.usedBitsPerChannel > bpc) {
m_header.usedBitsPerChannel = bpc;
}
}
 
//////////////////////////////////////////////////////////////////////
/// Return user data and size of user data.
/// Precondition: The PGF image has been opened with a call of Open(...).
/// @param size [out] Size of user data in bytes.
/// @return A pointer to user data or NULL if there is no user data.
const UINT8* CPGFImage::GetUserData(UINT32& size) const {
size = m_postHeader.userDataLen;
return m_postHeader.userData;
}
 
//////////////////////////////////////////////////////////////////////
/// After you've written a PGF image, you can call this method followed by GetBitmap/GetYUV
/// to get a quick reconstruction (coded -> decoded image).
/// It might throw an IOException.
/// @param level The image level of the resulting image in the internal image buffer.
void CPGFImage::Reconstruct(int level /*= 0*/) THROW_ {
if (m_header.nLevels == 0) {
// image didn't use wavelet transform
if (level == 0) {
for (int i=0; i < m_header.channels; i++) {
ASSERT(m_wtChannel[i]);
m_channel[i] = m_wtChannel[i]->GetSubband(0, LL)->GetBuffer();
}
}
} else {
int currentLevel = m_header.nLevels;
 
if (ROIisSupported()) {
// enable ROI reading
SetROI(PGFRect(0, 0, m_header.width, m_header.height));
}
 
while (currentLevel > level) {
for (int i=0; i < m_header.channels; i++) {
ASSERT(m_wtChannel[i]);
// dequantize subbands
if (currentLevel == m_header.nLevels) {
// last level also has LL band
m_wtChannel[i]->GetSubband(currentLevel, LL)->Dequantize(m_quant);
}
m_wtChannel[i]->GetSubband(currentLevel, HL)->Dequantize(m_quant);
m_wtChannel[i]->GetSubband(currentLevel, LH)->Dequantize(m_quant);
m_wtChannel[i]->GetSubband(currentLevel, HH)->Dequantize(m_quant);
 
// inverse transform from m_wtChannel to m_channel
OSError err = m_wtChannel[i]->InverseTransform(currentLevel, &m_width[i], &m_height[i], &m_channel[i]);
if (err != NoError) ReturnWithError(err);
ASSERT(m_channel[i]);
}
 
currentLevel--;
}
}
}
 
//////////////////////////////////////////////////////////////////////
// Read and decode some levels of a PGF image at current stream position.
// A PGF image is structered in levels, numbered between 0 and Levels() - 1.
// Each level can be seen as a single image, containing the same content
// as all other levels, but in a different size (width, height).
// The image size at level i is double the size (width, height) of the image at level i+1.
// The image at level 0 contains the original size.
// Precondition: The PGF image has been opened with a call of Open(...).
// It might throw an IOException.
// @param level The image level of the resulting image in the internal image buffer.
// @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.
// @param data Data Pointer to C++ class container to host callback procedure.
void CPGFImage::Read(int level /*= 0*/, CallbackPtr cb /*= NULL*/, void *data /*=NULL*/) THROW_ {
ASSERT((level >= 0 && level < m_header.nLevels) || m_header.nLevels == 0); // m_header.nLevels == 0: image didn't use wavelet transform
ASSERT(m_decoder);
 
#ifdef __PGFROISUPPORT__
if (ROIisSupported() && m_header.nLevels > 0) {
// new encoding scheme supporting ROI
PGFRect rect(0, 0, m_header.width, m_header.height);
Read(rect, level, cb, data);
return;
}
#endif
 
if (m_header.nLevels == 0) {
if (level == 0) {
// the data has already been read during open
// now update progress
if (cb) {
if ((*cb)(1.0, true, data)) ReturnWithError(EscapePressed);
}
}
} else {
const int levelDiff = m_currentLevel - level;
double percent = (m_progressMode == PM_Relative) ? pow(0.25, levelDiff) : m_percent;
 
// encoding scheme without ROI
while (m_currentLevel > level) {
for (int i=0; i < m_header.channels; i++) {
ASSERT(m_wtChannel[i]);
// decode file and write stream to m_wtChannel
if (m_currentLevel == m_header.nLevels) {
// last level also has LL band
m_wtChannel[i]->GetSubband(m_currentLevel, LL)->PlaceTile(*m_decoder, m_quant);
}
if (m_preHeader.version & Version5) {
// since version 5
m_wtChannel[i]->GetSubband(m_currentLevel, HL)->PlaceTile(*m_decoder, m_quant);
m_wtChannel[i]->GetSubband(m_currentLevel, LH)->PlaceTile(*m_decoder, m_quant);
} else {
// until version 4
m_decoder->DecodeInterleaved(m_wtChannel[i], m_currentLevel, m_quant);
}
m_wtChannel[i]->GetSubband(m_currentLevel, HH)->PlaceTile(*m_decoder, m_quant);
}
 
volatile OSError error = NoError; // volatile prevents optimizations
#ifdef LIBPGF_USE_OPENMP
#pragma omp parallel for default(shared)
#endif
for (int i=0; i < m_header.channels; i++) {
// inverse transform from m_wtChannel to m_channel
if (error == NoError) {
OSError err = m_wtChannel[i]->InverseTransform(m_currentLevel, &m_width[i], &m_height[i], &m_channel[i]);
if (err != NoError) error = err;
}
ASSERT(m_channel[i]);
}
if (error != NoError) ReturnWithError(error);
 
// set new level: must be done before refresh callback
m_currentLevel--;
 
// now we have to refresh the display
if (m_cb) m_cb(m_cbArg);
 
// now update progress
if (cb) {
percent *= 4;
if (m_progressMode == PM_Absolute) m_percent = percent;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
}
 
// automatically closing
if (m_currentLevel == 0) Close();
}
 
#ifdef __PGFROISUPPORT__
//////////////////////////////////////////////////////////////////////
/// Read a rectangular region of interest of a PGF image at current stream position.
/// The origin of the coordinate axis is the top-left corner of the image.
/// All coordinates are measured in pixels.
/// It might throw an IOException.
/// @param rect [inout] Rectangular region of interest (ROI). The rect might be cropped.
/// @param level The image level of the resulting image in the internal image buffer.
/// @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.
/// @param data Data Pointer to C++ class container to host callback procedure.
void CPGFImage::Read(PGFRect& rect, int level /*= 0*/, CallbackPtr cb /*= NULL*/, void *data /*=NULL*/) THROW_ {
ASSERT((level >= 0 && level < m_header.nLevels) || m_header.nLevels == 0); // m_header.nLevels == 0: image didn't use wavelet transform
ASSERT(m_decoder);
 
if (m_header.nLevels == 0 || !ROIisSupported()) {
rect.left = rect.top = 0;
rect.right = m_header.width; rect.bottom = m_header.height;
Read(level, cb, data);
} else {
ASSERT(ROIisSupported());
// new encoding scheme supporting ROI
ASSERT(rect.left < m_header.width && rect.top < m_header.height);
 
const int levelDiff = m_currentLevel - level;
double percent = (m_progressMode == PM_Relative) ? pow(0.25, levelDiff) : m_percent;
// check level difference
if (levelDiff <= 0) {
// it is a new read call, probably with a new ROI
m_currentLevel = m_header.nLevels;
m_decoder->SetStreamPosToData();
}
 
// check rectangle
if (rect.right == 0 || rect.right > m_header.width) rect.right = m_header.width;
if (rect.bottom == 0 || rect.bottom > m_header.height) rect.bottom = m_header.height;
// enable ROI decoding and reading
SetROI(rect);
 
while (m_currentLevel > level) {
for (int i=0; i < m_header.channels; i++) {
ASSERT(m_wtChannel[i]);
 
// get number of tiles and tile indices
const UINT32 nTiles = m_wtChannel[i]->GetNofTiles(m_currentLevel);
const PGFRect& tileIndices = m_wtChannel[i]->GetTileIndices(m_currentLevel);
 
// decode file and write stream to m_wtChannel
if (m_currentLevel == m_header.nLevels) { // last level also has LL band
ASSERT(nTiles == 1);
m_decoder->DecodeTileBuffer();
m_wtChannel[i]->GetSubband(m_currentLevel, LL)->PlaceTile(*m_decoder, m_quant);
}
for (UINT32 tileY=0; tileY < nTiles; tileY++) {
for (UINT32 tileX=0; tileX < nTiles; tileX++) {
// check relevance of tile
if (tileIndices.IsInside(tileX, tileY)) {
m_decoder->DecodeTileBuffer();
m_wtChannel[i]->GetSubband(m_currentLevel, HL)->PlaceTile(*m_decoder, m_quant, true, tileX, tileY);
m_wtChannel[i]->GetSubband(m_currentLevel, LH)->PlaceTile(*m_decoder, m_quant, true, tileX, tileY);
m_wtChannel[i]->GetSubband(m_currentLevel, HH)->PlaceTile(*m_decoder, m_quant, true, tileX, tileY);
} else {
// skip tile
m_decoder->SkipTileBuffer();
}
}
}
}
 
volatile OSError error = NoError; // volatile prevents optimizations
#ifdef LIBPGF_USE_OPENMP
#pragma omp parallel for default(shared)
#endif
for (int i=0; i < m_header.channels; i++) {
// inverse transform from m_wtChannel to m_channel
if (error == NoError) {
OSError err = m_wtChannel[i]->InverseTransform(m_currentLevel, &m_width[i], &m_height[i], &m_channel[i]);
if (err != NoError) error = err;
}
ASSERT(m_channel[i]);
}
if (error != NoError) ReturnWithError(error);
 
// set new level: must be done before refresh callback
m_currentLevel--;
 
// now we have to refresh the display
if (m_cb) m_cb(m_cbArg);
 
// now update progress
if (cb) {
percent *= 4;
if (m_progressMode == PM_Absolute) m_percent = percent;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
}
 
// automatically closing
if (m_currentLevel == 0) Close();
}
 
//////////////////////////////////////////////////////////////////////
/// Compute ROIs for each channel and each level
/// @param rect rectangular region of interest (ROI)
void CPGFImage::SetROI(PGFRect rect) {
ASSERT(m_decoder);
ASSERT(ROIisSupported());
 
// store ROI for a later call of GetBitmap
m_roi = rect;
 
// enable ROI decoding
m_decoder->SetROI();
 
// enlarge ROI because of border artefacts
const UINT32 dx = FilterWidth/2*(1 << m_currentLevel);
const UINT32 dy = FilterHeight/2*(1 << m_currentLevel);
 
if (rect.left < dx) rect.left = 0;
else rect.left -= dx;
if (rect.top < dy) rect.top = 0;
else rect.top -= dy;
rect.right += dx;
if (rect.right > m_header.width) rect.right = m_header.width;
rect.bottom += dy;
if (rect.bottom > m_header.height) rect.bottom = m_header.height;
 
// prepare wavelet channels for using ROI
ASSERT(m_wtChannel[0]);
m_wtChannel[0]->SetROI(rect);
if (m_downsample && m_header.channels > 1) {
// all further channels are downsampled, therefore downsample ROI
rect.left >>= 1;
rect.top >>= 1;
rect.right >>= 1;
rect.bottom >>= 1;
}
for (int i=1; i < m_header.channels; i++) {
ASSERT(m_wtChannel[i]);
m_wtChannel[i]->SetROI(rect);
}
}
 
#endif // __PGFROISUPPORT__
 
//////////////////////////////////////////////////////////////////////
/// Return the length of all encoded headers in bytes.
/// Precondition: The PGF image has been opened with a call of Open(...).
/// @return The length of all encoded headers in bytes
UINT32 CPGFImage::GetEncodedHeaderLength() const {
ASSERT(m_decoder);
return m_decoder->GetEncodedHeaderLength();
}
 
//////////////////////////////////////////////////////////////////////
/// Reads the encoded PGF headers and copies it to a target buffer.
/// Precondition: The PGF image has been opened with a call of Open(...).
/// It might throw an IOException.
/// @param target The target buffer
/// @param targetLen The length of the target buffer in bytes
/// @return The number of bytes copied to the target buffer
UINT32 CPGFImage::ReadEncodedHeader(UINT8* target, UINT32 targetLen) const THROW_ {
ASSERT(target);
ASSERT(targetLen > 0);
ASSERT(m_decoder);
 
// reset stream position
m_decoder->SetStreamPosToStart();
 
// compute number of bytes to read
UINT32 len = __min(targetLen, GetEncodedHeaderLength());
 
// read data
len = m_decoder->ReadEncodedData(target, len);
ASSERT(len >= 0 && len <= targetLen);
 
return len;
}
 
////////////////////////////////////////////////////////////////////
/// Reset stream position to start of PGF pre-header
void CPGFImage::ResetStreamPos() THROW_ {
ASSERT(m_decoder);
return m_decoder->SetStreamPosToStart();
}
 
//////////////////////////////////////////////////////////////////////
/// Reads the data of an encoded PGF level and copies it to a target buffer
/// without decoding.
/// Precondition: The PGF image has been opened with a call of Open(...).
/// It might throw an IOException.
/// @param level The image level
/// @param target The target buffer
/// @param targetLen The length of the target buffer in bytes
/// @return The number of bytes copied to the target buffer
UINT32 CPGFImage::ReadEncodedData(int level, UINT8* target, UINT32 targetLen) const THROW_ {
ASSERT(level >= 0 && level < m_header.nLevels);
ASSERT(target);
ASSERT(targetLen > 0);
ASSERT(m_decoder);
 
// reset stream position
m_decoder->SetStreamPosToData();
 
// position stream
UINT64 offset = 0;
 
for (int i=m_header.nLevels - 1; i > level; i--) {
offset += m_levelLength[m_header.nLevels - 1 - i];
}
m_decoder->Skip(offset);
 
// compute number of bytes to read
UINT32 len = __min(targetLen, GetEncodedLevelLength(level));
 
// read data
len = m_decoder->ReadEncodedData(target, len);
ASSERT(len >= 0 && len <= targetLen);
 
return len;
}
 
//////////////////////////////////////////////////////////////////////
/// Set maximum intensity value for image modes with more than eight bits per channel.
/// Call this method after SetHeader, but before ImportBitmap.
/// @param maxValue The maximum intensity value.
void CPGFImage::SetMaxValue(UINT32 maxValue) {
const BYTE bpc = m_header.bpp/m_header.channels;
BYTE pot = 0;
 
while(maxValue > 0) {
pot++;
maxValue >>= 1;
}
// store bits per channel
if (pot > bpc) pot = bpc;
if (pot > 31) pot = 31;
m_header.usedBitsPerChannel = pot;
}
 
//////////////////////////////////////////////////////////////////////
/// Returns number of used bits per input/output image channel.
/// Precondition: header must be initialized.
/// @return number of used bits per input/output image channel.
BYTE CPGFImage::UsedBitsPerChannel() const {
const BYTE bpc = m_header.bpp/m_header.channels;
 
if (bpc > 8) {
return m_header.usedBitsPerChannel;
} else {
return bpc;
}
}
 
//////////////////////////////////////////////////////////////////////
/// Return version
BYTE CPGFImage::CurrentVersion(BYTE version) {
if (version & Version6) return 6;
if (version & Version5) return 5;
if (version & Version2) return 2;
return 1;
}
 
//////////////////////////////////////////////////////////////////
// Import an image from a specified image buffer.
// This method is usually called before Write(...) and after SetHeader(...).
// It might throw an IOException.
// The absolute value of pitch is the number of bytes of an image row.
// If pitch is negative, then buff points to the last row of a bottom-up image (first byte on last row).
// If pitch is positive, then buff points to the first row of a top-down image (first byte).
// 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
// provide a channelMap of size of expected channels (depending on image mode). For example, PGF expects in RGB color mode a channel sequence BGR.
// If your provided image buffer contains a channel sequence ARGB, then the channelMap looks like { 3, 2, 1 }.
// @param pitch The number of bytes of a row of the image buffer.
// @param buff An image buffer.
// @param bpp The number of bits per pixel used in image buffer.
// @param channelMap A integer array containing the mapping of input channel ordering to expected channel ordering.
// @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.
// @param data Data Pointer to C++ class container to host callback procedure.
void CPGFImage::ImportBitmap(int pitch, UINT8 *buff, BYTE bpp, int channelMap[] /*= NULL */, CallbackPtr cb /*= NULL*/, void *data /*=NULL*/) THROW_ {
ASSERT(buff);
ASSERT(m_channel[0]);
 
// color transform
RgbToYuv(pitch, buff, bpp, channelMap, cb, data);
 
if (m_downsample) {
// Subsampling of the chrominance and alpha channels
for (int i=1; i < m_header.channels; i++) {
Downsample(i);
}
}
}
 
/////////////////////////////////////////////////////////////////
// Bilinerar Subsampling of channel ch by a factor 2
void CPGFImage::Downsample(int ch) {
ASSERT(ch > 0);
 
const int w = m_width[0];
const int w2 = w/2;
const int h2 = m_height[0]/2;
const int oddW = w%2; // don't use bool -> problems with MaxSpeed optimization
const int oddH = m_height[0]%2; // "
int loPos = 0;
int hiPos = w;
int sampledPos = 0;
DataT* buff = m_channel[ch]; ASSERT(buff);
 
for (int i=0; i < h2; i++) {
for (int j=0; j < w2; j++) {
// compute average of pixel block
buff[sampledPos] = (buff[loPos] + buff[loPos + 1] + buff[hiPos] + buff[hiPos + 1]) >> 2;
loPos += 2; hiPos += 2;
sampledPos++;
}
if (oddW) {
buff[sampledPos] = (buff[loPos] + buff[hiPos]) >> 1;
loPos++; hiPos++;
sampledPos++;
}
loPos += w; hiPos += w;
}
if (oddH) {
for (int j=0; j < w2; j++) {
buff[sampledPos] = (buff[loPos] + buff[loPos+1]) >> 1;
loPos += 2; hiPos += 2;
sampledPos++;
}
if (oddW) {
buff[sampledPos] = buff[loPos];
}
}
 
// downsampled image has half width and half height
m_width[ch] = (m_width[ch] + 1)/2;
m_height[ch] = (m_height[ch] + 1)/2;
}
 
//////////////////////////////////////////////////////////////////////
void CPGFImage::ComputeLevels() {
const int maxThumbnailWidth = 20*FilterWidth;
const int m = __min(m_header.width, m_header.height);
int s = m;
 
if (m_header.nLevels < 1 || m_header.nLevels > MaxLevel) {
m_header.nLevels = 1;
// compute a good value depending on the size of the image
while (s > maxThumbnailWidth) {
m_header.nLevels++;
s = s/2;
}
}
 
int levels = m_header.nLevels; // we need a signed value during level reduction
 
// reduce number of levels if the image size is smaller than FilterWidth*2^levels
s = FilterWidth*(1 << levels); // must be at least the double filter size because of subsampling
while (m < s) {
levels--;
s = s/2;
}
if (levels > MaxLevel) m_header.nLevels = MaxLevel;
else if (levels < 0) m_header.nLevels = 0;
else m_header.nLevels = (UINT8)levels;
 
// used in Write when PM_Absolute
m_percent = pow(0.25, m_header.nLevels);
 
ASSERT(0 <= m_header.nLevels && m_header.nLevels <= MaxLevel);
}
 
//////////////////////////////////////////////////////////////////////
/// Set PGF header and user data.
/// Precondition: The PGF image has been closed with Close(...) or never opened with Open(...).
/// It might throw an IOException.
/// @param header A valid and already filled in PGF header structure
/// @param flags A combination of additional version flags. In case you use level-wise encoding then set flag = PGFROI.
/// @param userData A user-defined memory block containing any kind of cached metadata.
/// @param userDataLength The size of user-defined memory block in bytes
void CPGFImage::SetHeader(const PGFHeader& header, BYTE flags /*=0*/, UINT8* userData /*= 0*/, UINT32 userDataLength /*= 0*/) THROW_ {
ASSERT(!m_decoder); // current image must be closed
ASSERT(header.quality <= MaxQuality);
 
// init state
#ifdef __PGFROISUPPORT__
m_streamReinitialized = false;
#endif
 
// init preHeader
memcpy(m_preHeader.magic, PGFMagic, 3);
m_preHeader.version = PGFVersion | flags;
m_preHeader.hSize = HeaderSize;
 
// copy header
memcpy(&m_header, &header, HeaderSize);
 
// complete header
CompleteHeader();
 
// check and set number of levels
ComputeLevels();
 
// check for downsample
if (m_header.quality > DownsampleThreshold && (m_header.mode == ImageModeRGBColor ||
m_header.mode == ImageModeRGBA ||
m_header.mode == ImageModeRGB48 ||
m_header.mode == ImageModeCMYKColor ||
m_header.mode == ImageModeCMYK64 ||
m_header.mode == ImageModeLabColor ||
m_header.mode == ImageModeLab48)) {
m_downsample = true;
m_quant = m_header.quality - 1;
} else {
m_downsample = false;
m_quant = m_header.quality;
}
 
// update header size and copy user data
if (m_header.mode == ImageModeIndexedColor) {
// update header size
m_preHeader.hSize += ColorTableSize;
}
if (userDataLength && userData) {
m_postHeader.userData = new(std::nothrow) UINT8[userDataLength];
if (!m_postHeader.userData) ReturnWithError(InsufficientMemory);
m_postHeader.userDataLen = userDataLength;
memcpy(m_postHeader.userData, userData, userDataLength);
// update header size
m_preHeader.hSize += userDataLength;
}
 
// allocate channels
for (int i=0; i < m_header.channels; i++) {
// set current width and height
m_width[i] = m_header.width;
m_height[i] = m_header.height;
 
// allocate channels
ASSERT(!m_channel[i]);
m_channel[i] = new(std::nothrow) DataT[m_header.width*m_header.height];
if (!m_channel[i]) {
if (i) i--;
while(i) {
delete[] m_channel[i]; m_channel[i] = 0;
i--;
}
ReturnWithError(InsufficientMemory);
}
}
}
 
//////////////////////////////////////////////////////////////////
/// Create wavelet transform channels and encoder. Write header at current stream position.
/// Call this method before your first call of Write(int level) or WriteImage(), but after SetHeader().
/// This method is called inside of Write(stream, ...).
/// It might throw an IOException.
/// @param stream A PGF stream
/// @return The number of bytes written into stream.
UINT32 CPGFImage::WriteHeader(CPGFStream* stream) THROW_ {
ASSERT(m_header.nLevels <= MaxLevel);
ASSERT(m_header.quality <= MaxQuality); // quality is already initialized
 
if (m_header.nLevels > 0) {
volatile OSError error = NoError; // volatile prevents optimizations
// create new wt channels
#ifdef LIBPGF_USE_OPENMP
#pragma omp parallel for default(shared)
#endif
for (int i=0; i < m_header.channels; i++) {
DataT *temp = NULL;
if (error == NoError) {
if (m_wtChannel[i]) {
ASSERT(m_channel[i]);
// copy m_channel to temp
int size = m_height[i]*m_width[i];
temp = new(std::nothrow) DataT[size];
if (temp) {
memcpy(temp, m_channel[i], size*DataTSize);
delete m_wtChannel[i]; // also deletes m_channel
m_channel[i] = NULL;
} else {
error = InsufficientMemory;
}
}
if (error == NoError) {
if (temp) {
ASSERT(!m_channel[i]);
m_channel[i] = temp;
}
m_wtChannel[i] = new CWaveletTransform(m_width[i], m_height[i], m_header.nLevels, m_channel[i]);
if (m_wtChannel[i]) {
#ifdef __PGFROISUPPORT__
m_wtChannel[i]->SetROI(PGFRect(0, 0, m_width[i], m_height[i]));
#endif
// wavelet subband decomposition
for (int l=0; error == NoError && l < m_header.nLevels; l++) {
OSError err = m_wtChannel[i]->ForwardTransform(l, m_quant);
if (err != NoError) error = err;
}
} else {
delete[] m_channel[i];
error = InsufficientMemory;
}
}
}
}
if (error != NoError) {
// free already allocated memory
for (int i=0; i < m_header.channels; i++) {
delete m_wtChannel[i];
}
ReturnWithError(error);
}
 
m_currentLevel = m_header.nLevels;
 
// create encoder and eventually write headers and levelLength
m_encoder = new CEncoder(stream, m_preHeader, m_header, m_postHeader, m_userDataPos, m_useOMPinEncoder);
if (m_favorSpeedOverSize) m_encoder->FavorSpeedOverSize();
 
#ifdef __PGFROISUPPORT__
if (ROIisSupported()) {
// new encoding scheme supporting ROI
m_encoder->SetROI();
}
#endif
 
} else {
// very small image: we don't use DWT and encoding
 
// create encoder and eventually write headers and levelLength
m_encoder = new CEncoder(stream, m_preHeader, m_header, m_postHeader, m_userDataPos, m_useOMPinEncoder);
}
 
INT64 nBytes = m_encoder->ComputeHeaderLength();
return (nBytes > 0) ? (UINT32)nBytes : 0;
}
 
//////////////////////////////////////////////////////////////////
// Encode and write next level of a PGF image at current stream position.
// A PGF image is structered in levels, numbered between 0 and Levels() - 1.
// Each level can be seen as a single image, containing the same content
// as all other levels, but in a different size (width, height).
// The image size at level i is double the size (width, height) of the image at level i+1.
// The image at level 0 contains the original size.
// It might throw an IOException.
void CPGFImage::WriteLevel() THROW_ {
ASSERT(m_encoder);
ASSERT(m_currentLevel > 0);
ASSERT(m_header.nLevels > 0);
 
#ifdef __PGFROISUPPORT__
if (ROIisSupported()) {
const int lastChannel = m_header.channels - 1;
 
for (int i=0; i < m_header.channels; i++) {
// get number of tiles and tile indices
const UINT32 nTiles = m_wtChannel[i]->GetNofTiles(m_currentLevel);
const UINT32 lastTile = nTiles - 1;
 
if (m_currentLevel == m_header.nLevels) {
// last level also has LL band
ASSERT(nTiles == 1);
m_wtChannel[i]->GetSubband(m_currentLevel, LL)->ExtractTile(*m_encoder);
m_encoder->EncodeTileBuffer();
}
for (UINT32 tileY=0; tileY < nTiles; tileY++) {
for (UINT32 tileX=0; tileX < nTiles; tileX++) {
m_wtChannel[i]->GetSubband(m_currentLevel, HL)->ExtractTile(*m_encoder, true, tileX, tileY);
m_wtChannel[i]->GetSubband(m_currentLevel, LH)->ExtractTile(*m_encoder, true, tileX, tileY);
m_wtChannel[i]->GetSubband(m_currentLevel, HH)->ExtractTile(*m_encoder, true, tileX, tileY);
if (i == lastChannel && tileY == lastTile && tileX == lastTile) {
// all necessary data are buffered. next call of EncodeBuffer will write the last piece of data of the current level.
m_encoder->SetEncodedLevel(--m_currentLevel);
}
m_encoder->EncodeTileBuffer();
}
}
}
} else
#endif
{
for (int i=0; i < m_header.channels; i++) {
ASSERT(m_wtChannel[i]);
if (m_currentLevel == m_header.nLevels) {
// last level also has LL band
m_wtChannel[i]->GetSubband(m_currentLevel, LL)->ExtractTile(*m_encoder);
}
//encoder.EncodeInterleaved(m_wtChannel[i], m_currentLevel, m_quant); // until version 4
m_wtChannel[i]->GetSubband(m_currentLevel, HL)->ExtractTile(*m_encoder); // since version 5
m_wtChannel[i]->GetSubband(m_currentLevel, LH)->ExtractTile(*m_encoder); // since version 5
m_wtChannel[i]->GetSubband(m_currentLevel, HH)->ExtractTile(*m_encoder);
}
 
// all necessary data are buffered. next call of EncodeBuffer will write the last piece of data of the current level.
m_encoder->SetEncodedLevel(--m_currentLevel);
}
}
 
//////////////////////////////////////////////////////////////////////
// Return written levelLength bytes
UINT32 CPGFImage::UpdatePostHeaderSize() THROW_ {
ASSERT(m_encoder);
 
INT64 offset = m_encoder->ComputeOffset(); ASSERT(offset >= 0);
 
if (offset > 0) {
// update post-header size and rewrite pre-header
m_preHeader.hSize += (UINT32)offset;
m_encoder->UpdatePostHeaderSize(m_preHeader);
}
 
// write dummy levelLength into stream
return m_encoder->WriteLevelLength(m_levelLength);
}
 
//////////////////////////////////////////////////////////////////////
/// Encode and write the one and only image at current stream position.
/// Call this method after WriteHeader(). In case you want to write uncached metadata,
/// then do that after WriteHeader() and before WriteImage().
/// This method is called inside of Write(stream, ...).
/// It might throw an IOException.
/// @param stream A PGF stream
/// @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.
/// @param data Data Pointer to C++ class container to host callback procedure.
/// @return The number of bytes written into stream.
UINT32 CPGFImage::WriteImage(CPGFStream* stream, CallbackPtr cb /*= NULL*/, void *data /*= NULL*/) THROW_ {
ASSERT(stream);
ASSERT(m_preHeader.hSize);
 
int levels = m_header.nLevels;
double percent = pow(0.25, levels);
 
// update post-header size, rewrite pre-header, and write dummy levelLength
UINT32 nWrittenBytes = UpdatePostHeaderSize();
 
if (levels == 0) {
// write channels
for (int c=0; c < m_header.channels; c++) {
const UINT32 size = m_width[c]*m_height[c];
 
// write channel data into stream
for (UINT32 i=0; i < size; i++) {
int count = DataTSize;
stream->Write(&count, &m_channel[c][i]);
}
}
 
// now update progress
if (cb) {
if ((*cb)(1, true, data)) ReturnWithError(EscapePressed);
}
 
} else {
// encode quantized wavelet coefficients and write to PGF file
// encode subbands, higher levels first
// color channels are interleaved
 
// encode all levels
for (m_currentLevel = levels; m_currentLevel > 0; ) {
WriteLevel(); // decrements m_currentLevel
 
// now update progress
if (cb) {
percent *= 4;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
 
// flush encoder and write level lengths
m_encoder->Flush();
}
 
// update level lengths
nWrittenBytes += m_encoder->UpdateLevelLength(); // return written image bytes
 
// delete encoder
delete m_encoder; m_encoder = NULL;
 
ASSERT(!m_encoder);
 
return nWrittenBytes;
}
 
//////////////////////////////////////////////////////////////////
/// Encode and write a entire PGF image (header and image) at current stream position.
/// A PGF image is structered in levels, numbered between 0 and Levels() - 1.
/// Each level can be seen as a single image, containing the same content
/// as all other levels, but in a different size (width, height).
/// The image size at level i is double the size (width, height) of the image at level i+1.
/// The image at level 0 contains the original size.
/// Precondition: the PGF image contains a valid header (see also SetHeader(...)).
/// It might throw an IOException.
/// @param stream A PGF stream
/// @param nWrittenBytes [in-out] The number of bytes written into stream are added to the input value.
/// @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.
/// @param data Data Pointer to C++ class container to host callback procedure.
void CPGFImage::Write(CPGFStream* stream, UINT32* nWrittenBytes /*= NULL*/, CallbackPtr cb /*= NULL*/, void *data /*=NULL*/) THROW_ {
ASSERT(stream);
ASSERT(m_preHeader.hSize);
 
// create wavelet transform channels and encoder
UINT32 nBytes = WriteHeader(stream);
 
// write image
nBytes += WriteImage(stream, cb, data);
 
// return written bytes
if (nWrittenBytes) *nWrittenBytes += nBytes;
}
 
#ifdef __PGFROISUPPORT__
//////////////////////////////////////////////////////////////////
// Encode and write down to given level at current stream position.
// A PGF image is structered in levels, numbered between 0 and Levels() - 1.
// Each level can be seen as a single image, containing the same content
// as all other levels, but in a different size (width, height).
// The image size at level i is double the size (width, height) of the image at level i+1.
// The image at level 0 contains the original size.
// Precondition: the PGF image contains a valid header (see also SetHeader(...)) and WriteHeader() has been called before Write().
// The ROI encoding scheme is used.
// It might throw an IOException.
// @param level The image level of the resulting image in the internal image buffer.
// @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.
// @param data Data Pointer to C++ class container to host callback procedure.
// @return The number of bytes written into stream.
UINT32 CPGFImage::Write(int level, CallbackPtr cb /*= NULL*/, void *data /*=NULL*/) THROW_ {
ASSERT(m_header.nLevels > 0);
ASSERT(0 <= level && level < m_header.nLevels);
ASSERT(m_encoder);
ASSERT(ROIisSupported());
 
const int levelDiff = m_currentLevel - level;
double percent = (m_progressMode == PM_Relative) ? pow(0.25, levelDiff) : m_percent;
UINT32 nWrittenBytes = 0;
 
if (m_currentLevel == m_header.nLevels) {
// update post-header size, rewrite pre-header, and write dummy levelLength
nWrittenBytes = UpdatePostHeaderSize();
} else {
// prepare for next level: save current file position, because the stream might have been reinitialized
if (m_encoder->ComputeBufferLength()) {
m_streamReinitialized = true;
}
}
 
// encoding scheme with ROI
while (m_currentLevel > level) {
WriteLevel(); // decrements m_currentLevel
 
if (m_levelLength) {
nWrittenBytes += m_levelLength[m_header.nLevels - m_currentLevel - 1];
}
 
// now update progress
if (cb) {
percent *= 4;
if (m_progressMode == PM_Absolute) m_percent = percent;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
 
// automatically closing
if (m_currentLevel == 0) {
if (!m_streamReinitialized) {
// don't write level lengths, if the stream position changed inbetween two Write operations
m_encoder->UpdateLevelLength();
}
// delete encoder
delete m_encoder; m_encoder = NULL;
}
 
return nWrittenBytes;
}
#endif // __PGFROISUPPORT__
 
 
//////////////////////////////////////////////////////////////////
// Check for valid import image mode.
// @param mode Image mode
// @return True if an image of given mode can be imported with ImportBitmap(...)
bool CPGFImage::ImportIsSupported(BYTE mode) {
size_t size = DataTSize;
 
if (size >= 2) {
switch(mode) {
case ImageModeBitmap:
case ImageModeIndexedColor:
case ImageModeGrayScale:
case ImageModeRGBColor:
case ImageModeCMYKColor:
case ImageModeHSLColor:
case ImageModeHSBColor:
//case ImageModeDuotone:
case ImageModeLabColor:
case ImageModeRGB12:
case ImageModeRGB16:
case ImageModeRGBA:
return true;
}
}
if (size >= 3) {
switch(mode) {
case ImageModeGray16:
case ImageModeRGB48:
case ImageModeLab48:
case ImageModeCMYK64:
//case ImageModeDuotone16:
return true;
}
}
if (size >=4) {
switch(mode) {
case ImageModeGray32:
return true;
}
}
return false;
}
 
//////////////////////////////////////////////////////////////////////
/// Retrieves red, green, blue (RGB) color values from a range of entries in the palette of the DIB section.
/// It might throw an IOException.
/// @param iFirstColor The color table index of the first entry to retrieve.
/// @param nColors The number of color table entries to retrieve.
/// @param prgbColors A pointer to the array of RGBQUAD structures to retrieve the color table entries.
void CPGFImage::GetColorTable(UINT32 iFirstColor, UINT32 nColors, RGBQUAD* prgbColors) const THROW_ {
if (iFirstColor + nColors > ColorTableLen) ReturnWithError(ColorTableError);
 
for (UINT32 i=iFirstColor, j=0; j < nColors; i++, j++) {
prgbColors[j] = m_postHeader.clut[i];
}
}
 
//////////////////////////////////////////////////////////////////////
/// Sets the red, green, blue (RGB) color values for a range of entries in the palette (clut).
/// It might throw an IOException.
/// @param iFirstColor The color table index of the first entry to set.
/// @param nColors The number of color table entries to set.
/// @param prgbColors A pointer to the array of RGBQUAD structures to set the color table entries.
void CPGFImage::SetColorTable(UINT32 iFirstColor, UINT32 nColors, const RGBQUAD* prgbColors) THROW_ {
if (iFirstColor + nColors > ColorTableLen) ReturnWithError(ColorTableError);
 
for (UINT32 i=iFirstColor, j=0; j < nColors; i++, j++) {
m_postHeader.clut[i] = prgbColors[j];
}
}
 
//////////////////////////////////////////////////////////////////
// Buffer transform from interleaved to channel seperated format
// the absolute value of pitch is the number of bytes of an image row
// if pitch is negative, then buff points to the last row of a bottom-up image (first byte on last row)
// if pitch is positive, then buff points to the first row of a top-down image (first byte)
// bpp is the number of bits per pixel used in image buffer buff
//
// RGB is transformed into YUV format (ordering of buffer data is BGR[A])
// Y = (R + 2*G + B)/4 -128
// U = R - G
// V = B - G
//
// Since PGF Codec version 2.0 images are stored in top-down direction
//
// 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
// provide a channelMap of size of expected channels (depending on image mode). For example, PGF expects in RGB color mode a channel sequence BGR.
// If your provided image buffer contains a channel sequence ARGB, then the channelMap looks like { 3, 2, 1 }.
void CPGFImage::RgbToYuv(int pitch, UINT8* buff, BYTE bpp, int channelMap[], CallbackPtr cb, void *data /*=NULL*/) THROW_ {
ASSERT(buff);
int yPos = 0, cnt = 0;
double percent = 0;
const double dP = 1.0/m_header.height;
int defMap[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; ASSERT(sizeof(defMap)/sizeof(defMap[0]) == MaxChannels);
 
if (channelMap == NULL) channelMap = defMap;
 
switch(m_header.mode) {
case ImageModeBitmap:
{
ASSERT(m_header.channels == 1);
ASSERT(m_header.bpp == 1);
ASSERT(bpp == 1);
const UINT32 w = m_header.width;
const UINT32 w2 = (m_header.width + 7)/8;
DataT* y = m_channel[0]; ASSERT(y);
 
for (UINT32 h=0; h < m_header.height; h++) {
if (cb) {
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
percent += dP;
}
for (UINT32 j=0; j < w2; j++) {
y[yPos++] = buff[j] - YUVoffset8;
}
for (UINT32 j=w2; j < w; j++) {
y[yPos++] = YUVoffset8;
}
//UINT cnt = w;
//for (UINT32 j=0; j < w2; j++) {
// for (int k=7; k >= 0; k--) {
// if (cnt) {
// y[yPos++] = YUVoffset8 + (1 & (buff[j] >> k));
// cnt--;
// }
// }
//}
buff += pitch;
}
}
break;
case ImageModeIndexedColor:
case ImageModeGrayScale:
case ImageModeHSLColor:
case ImageModeHSBColor:
case ImageModeLabColor:
{
ASSERT(m_header.channels >= 1);
ASSERT(m_header.bpp == m_header.channels*8);
ASSERT(bpp%8 == 0);
const int channels = bpp/8; ASSERT(channels >= m_header.channels);
 
for (UINT32 h=0; h < m_header.height; h++) {
if (cb) {
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
percent += dP;
}
 
cnt = 0;
for (UINT32 w=0; w < m_header.width; w++) {
for (int c=0; c < m_header.channels; c++) {
m_channel[c][yPos] = buff[cnt + channelMap[c]] - YUVoffset8;
}
cnt += channels;
yPos++;
}
buff += pitch;
}
}
break;
case ImageModeGray16:
case ImageModeLab48:
{
ASSERT(m_header.channels >= 1);
ASSERT(m_header.bpp == m_header.channels*16);
ASSERT(bpp%16 == 0);
 
UINT16 *buff16 = (UINT16 *)buff;
const int pitch16 = pitch/2;
const int channels = bpp/16; ASSERT(channels >= m_header.channels);
const int shift = 16 - UsedBitsPerChannel(); ASSERT(shift >= 0);
const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1);
 
for (UINT32 h=0; h < m_header.height; h++) {
if (cb) {
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
percent += dP;
}
 
cnt = 0;
for (UINT32 w=0; w < m_header.width; w++) {
for (int c=0; c < m_header.channels; c++) {
m_channel[c][yPos] = (buff16[cnt + channelMap[c]] >> shift) - yuvOffset16;
}
cnt += channels;
yPos++;
}
buff16 += pitch16;
}
}
break;
case ImageModeRGBColor:
{
ASSERT(m_header.channels == 3);
ASSERT(m_header.bpp == m_header.channels*8);
ASSERT(bpp%8 == 0);
 
DataT* y = m_channel[0]; ASSERT(y);
DataT* u = m_channel[1]; ASSERT(u);
DataT* v = m_channel[2]; ASSERT(v);
const int channels = bpp/8; ASSERT(channels >= m_header.channels);
UINT8 b, g, r;
 
for (UINT32 h=0; h < m_header.height; h++) {
if (cb) {
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
percent += dP;
}
 
cnt = 0;
for (UINT32 w=0; w < m_header.width; w++) {
b = buff[cnt + channelMap[0]];
g = buff[cnt + channelMap[1]];
r = buff[cnt + channelMap[2]];
// Yuv
y[yPos] = ((b + (g << 1) + r) >> 2) - YUVoffset8;
u[yPos] = r - g;
v[yPos] = b - g;
yPos++;
cnt += channels;
}
buff += pitch;
}
}
break;
case ImageModeRGB48:
{
ASSERT(m_header.channels == 3);
ASSERT(m_header.bpp == m_header.channels*16);
ASSERT(bpp%16 == 0);
 
UINT16 *buff16 = (UINT16 *)buff;
const int pitch16 = pitch/2;
const int channels = bpp/16; ASSERT(channels >= m_header.channels);
const int shift = 16 - UsedBitsPerChannel(); ASSERT(shift >= 0);
const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1);
 
DataT* y = m_channel[0]; ASSERT(y);
DataT* u = m_channel[1]; ASSERT(u);
DataT* v = m_channel[2]; ASSERT(v);
UINT16 b, g, r;
 
for (UINT32 h=0; h < m_header.height; h++) {
if (cb) {
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
percent += dP;
}
 
cnt = 0;
for (UINT32 w=0; w < m_header.width; w++) {
b = buff16[cnt + channelMap[0]] >> shift;
g = buff16[cnt + channelMap[1]] >> shift;
r = buff16[cnt + channelMap[2]] >> shift;
// Yuv
y[yPos] = ((b + (g << 1) + r) >> 2) - yuvOffset16;
u[yPos] = r - g;
v[yPos] = b - g;
yPos++;
cnt += channels;
}
buff16 += pitch16;
}
}
break;
case ImageModeRGBA:
case ImageModeCMYKColor:
{
ASSERT(m_header.channels == 4);
ASSERT(m_header.bpp == m_header.channels*8);
ASSERT(bpp%8 == 0);
const int channels = bpp/8; ASSERT(channels >= m_header.channels);
 
DataT* y = m_channel[0]; ASSERT(y);
DataT* u = m_channel[1]; ASSERT(u);
DataT* v = m_channel[2]; ASSERT(v);
DataT* a = m_channel[3]; ASSERT(a);
UINT8 b, g, r;
 
for (UINT32 h=0; h < m_header.height; h++) {
if (cb) {
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
percent += dP;
}
 
cnt = 0;
for (UINT32 w=0; w < m_header.width; w++) {
b = buff[cnt + channelMap[0]];
g = buff[cnt + channelMap[1]];
r = buff[cnt + channelMap[2]];
// Yuv
y[yPos] = ((b + (g << 1) + r) >> 2) - YUVoffset8;
u[yPos] = r - g;
v[yPos] = b - g;
a[yPos++] = buff[cnt + channelMap[3]] - YUVoffset8;
cnt += channels;
}
buff += pitch;
}
}
break;
case ImageModeCMYK64:
{
ASSERT(m_header.channels == 4);
ASSERT(m_header.bpp == m_header.channels*16);
ASSERT(bpp%16 == 0);
 
UINT16 *buff16 = (UINT16 *)buff;
const int pitch16 = pitch/2;
const int channels = bpp/16; ASSERT(channels >= m_header.channels);
const int shift = 16 - UsedBitsPerChannel(); ASSERT(shift >= 0);
const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1);
DataT* y = m_channel[0]; ASSERT(y);
DataT* u = m_channel[1]; ASSERT(u);
DataT* v = m_channel[2]; ASSERT(v);
DataT* a = m_channel[3]; ASSERT(a);
UINT16 b, g, r;
 
for (UINT32 h=0; h < m_header.height; h++) {
if (cb) {
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
percent += dP;
}
 
cnt = 0;
for (UINT32 w=0; w < m_header.width; w++) {
b = buff16[cnt + channelMap[0]] >> shift;
g = buff16[cnt + channelMap[1]] >> shift;
r = buff16[cnt + channelMap[2]] >> shift;
// Yuv
y[yPos] = ((b + (g << 1) + r) >> 2) - yuvOffset16;
u[yPos] = r - g;
v[yPos] = b - g;
a[yPos++] = (buff16[cnt + channelMap[3]] >> shift) - yuvOffset16;
cnt += channels;
}
buff16 += pitch16;
}
}
break;
#ifdef __PGF32SUPPORT__
case ImageModeGray32:
{
ASSERT(m_header.channels == 1);
ASSERT(m_header.bpp == 32);
ASSERT(bpp == 32);
ASSERT(DataTSize == sizeof(UINT32));
 
DataT* y = m_channel[0]; ASSERT(y);
 
UINT32 *buff32 = (UINT32 *)buff;
const int pitch32 = pitch/4;
const int shift = 31 - UsedBitsPerChannel(); ASSERT(shift >= 0);
const DataT yuvOffset31 = 1 << (UsedBitsPerChannel() - 1);
 
for (UINT32 h=0; h < m_header.height; h++) {
if (cb) {
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
percent += dP;
}
 
for (UINT32 w=0; w < m_header.width; w++) {
y[yPos++] = (buff32[w] >> shift) - yuvOffset31;
}
buff32 += pitch32;
}
}
break;
#endif
case ImageModeRGB12:
{
ASSERT(m_header.channels == 3);
ASSERT(m_header.bpp == m_header.channels*4);
ASSERT(bpp == m_header.channels*4);
 
DataT* y = m_channel[0]; ASSERT(y);
DataT* u = m_channel[1]; ASSERT(u);
DataT* v = m_channel[2]; ASSERT(v);
 
UINT8 rgb = 0, b, g, r;
 
for (UINT32 h=0; h < m_header.height; h++) {
if (cb) {
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
percent += dP;
}
 
cnt = 0;
for (UINT32 w=0; w < m_header.width; w++) {
if (w%2 == 0) {
// even pixel position
rgb = buff[cnt];
b = rgb & 0x0F;
g = (rgb & 0xF0) >> 4;
cnt++;
rgb = buff[cnt];
r = rgb & 0x0F;
} else {
// odd pixel position
b = (rgb & 0xF0) >> 4;
cnt++;
rgb = buff[cnt];
g = rgb & 0x0F;
r = (rgb & 0xF0) >> 4;
cnt++;
}
 
// Yuv
y[yPos] = ((b + (g << 1) + r) >> 2) - YUVoffset4;
u[yPos] = r - g;
v[yPos] = b - g;
yPos++;
}
buff += pitch;
}
}
break;
case ImageModeRGB16:
{
ASSERT(m_header.channels == 3);
ASSERT(m_header.bpp == 16);
ASSERT(bpp == 16);
DataT* y = m_channel[0]; ASSERT(y);
DataT* u = m_channel[1]; ASSERT(u);
DataT* v = m_channel[2]; ASSERT(v);
 
UINT16 *buff16 = (UINT16 *)buff;
UINT16 rgb, b, g, r;
const int pitch16 = pitch/2;
 
for (UINT32 h=0; h < m_header.height; h++) {
if (cb) {
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
percent += dP;
}
for (UINT32 w=0; w < m_header.width; w++) {
rgb = buff16[w];
r = (rgb & 0xF800) >> 10; // highest 5 bits
g = (rgb & 0x07E0) >> 5; // middle 6 bits
b = (rgb & 0x001F) << 1; // lowest 5 bits
// Yuv
y[yPos] = ((b + (g << 1) + r) >> 2) - YUVoffset6;
u[yPos] = r - g;
v[yPos] = b - g;
yPos++;
}
 
buff16 += pitch16;
}
}
break;
default:
ASSERT(false);
}
}
 
//////////////////////////////////////////////////////////////////
// Get image data in interleaved format: (ordering of RGB data is BGR[A])
// Upsampling, YUV to RGB transform and interleaving are done here to reduce the number
// of passes over the data.
// The absolute value of pitch is the number of bytes of an image row of the given image buffer.
// If pitch is negative, then the image buffer must point to the last row of a bottom-up image (first byte on last row).
// if pitch is positive, then the image buffer must point to the first row of a top-down image (first byte).
// 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
// provide a channelMap of size of expected channels (depending on image mode). For example, PGF provides a channel sequence BGR in RGB color mode.
// If your provided image buffer expects a channel sequence ARGB, then the channelMap looks like { 3, 2, 1 }.
// It might throw an IOException.
// @param pitch The number of bytes of a row of the image buffer.
// @param buff An image buffer.
// @param bpp The number of bits per pixel used in image buffer.
// @param channelMap A integer array containing the mapping of PGF channel ordering to expected channel ordering.
// @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.
// @param data Data Pointer to C++ class container to host callback procedure.
void CPGFImage::GetBitmap(int pitch, UINT8* buff, BYTE bpp, int channelMap[] /*= NULL */, CallbackPtr cb /*= NULL*/, void *data /*=NULL*/) const THROW_ {
ASSERT(buff);
UINT32 w = m_width[0];
UINT32 h = m_height[0];
UINT8* targetBuff = 0; // used if ROI is used
UINT8* buffStart = 0; // used if ROI is used
int targetPitch = 0; // used if ROI is used
 
#ifdef __PGFROISUPPORT__
const PGFRect& roi = (ROIisSupported()) ? m_wtChannel[0]->GetROI(m_currentLevel) : PGFRect(0, 0, w, h); // roi is usually larger than m_roi
const PGFRect levelRoi(LevelWidth(m_roi.left, m_currentLevel), LevelHeight(m_roi.top, m_currentLevel), LevelWidth(m_roi.Width(), m_currentLevel), LevelHeight(m_roi.Height(), m_currentLevel));
ASSERT(w <= roi.Width() && h <= roi.Height());
ASSERT(roi.left <= levelRoi.left && levelRoi.right <= roi.right);
ASSERT(roi.top <= levelRoi.top && levelRoi.bottom <= roi.bottom);
 
if (ROIisSupported() && (levelRoi.Width() < w || levelRoi.Height() < h)) {
// ROI is used -> create a temporary image buffer for roi
// compute pitch
targetPitch = pitch;
pitch = AlignWordPos(w*bpp)/8;
 
// create temporary output buffer
targetBuff = buff;
buff = buffStart = new(std::nothrow) UINT8[pitch*h];
if (!buff) ReturnWithError(InsufficientMemory);
}
#endif
 
const bool wOdd = (1 == w%2);
 
const double dP = 1.0/h;
int defMap[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; ASSERT(sizeof(defMap)/sizeof(defMap[0]) == MaxChannels);
if (channelMap == NULL) channelMap = defMap;
int sampledPos = 0, yPos = 0;
DataT uAvg, vAvg;
double percent = 0;
UINT32 i, j;
 
switch(m_header.mode) {
case ImageModeBitmap:
{
ASSERT(m_header.channels == 1);
ASSERT(m_header.bpp == 1);
ASSERT(bpp == 1);
 
const UINT32 w2 = (w + 7)/8;
DataT* y = m_channel[0]; ASSERT(y);
 
for (i=0; i < h; i++) {
for (j=0; j < w2; j++) {
buff[j] = Clamp8(y[yPos++] + YUVoffset8);
}
yPos += w - w2;
//UINT32 cnt = w;
//for (j=0; j < w2; j++) {
// buff[j] = 0;
// for (int k=0; k < 8; k++) {
// if (cnt) {
// buff[j] <<= 1;
// buff[j] |= (1 & (y[yPos++] - YUVoffset8));
// cnt--;
// }
// }
//}
buff += pitch;
 
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
break;
}
case ImageModeIndexedColor:
case ImageModeGrayScale:
case ImageModeHSLColor:
case ImageModeHSBColor:
{
ASSERT(m_header.channels >= 1);
ASSERT(m_header.bpp == m_header.channels*8);
ASSERT(bpp%8 == 0);
 
int cnt, channels = bpp/8; ASSERT(channels >= m_header.channels);
 
for (i=0; i < h; i++) {
cnt = 0;
for (j=0; j < w; j++) {
for (int c=0; c < m_header.channels; c++) {
buff[cnt + channelMap[c]] = Clamp8(m_channel[c][yPos] + YUVoffset8);
}
cnt += channels;
yPos++;
}
buff += pitch;
 
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
break;
}
case ImageModeGray16:
{
ASSERT(m_header.channels >= 1);
ASSERT(m_header.bpp == m_header.channels*16);
 
const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1);
int cnt, channels;
 
if (bpp%16 == 0) {
const int shift = 16 - UsedBitsPerChannel(); ASSERT(shift >= 0);
UINT16 *buff16 = (UINT16 *)buff;
int pitch16 = pitch/2;
channels = bpp/16; ASSERT(channels >= m_header.channels);
 
for (i=0; i < h; i++) {
cnt = 0;
for (j=0; j < w; j++) {
for (int c=0; c < m_header.channels; c++) {
buff16[cnt + channelMap[c]] = Clamp16((m_channel[c][yPos] + yuvOffset16) << shift);
}
cnt += channels;
yPos++;
}
buff16 += pitch16;
 
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
} else {
ASSERT(bpp%8 == 0);
const int shift = __max(0, UsedBitsPerChannel() - 8);
channels = bpp/8; ASSERT(channels >= m_header.channels);
for (i=0; i < h; i++) {
cnt = 0;
for (j=0; j < w; j++) {
for (int c=0; c < m_header.channels; c++) {
buff[cnt + channelMap[c]] = Clamp8((m_channel[c][yPos] + yuvOffset16) >> shift);
}
cnt += channels;
yPos++;
}
buff += pitch;
 
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
}
break;
}
case ImageModeRGBColor:
{
ASSERT(m_header.channels == 3);
ASSERT(m_header.bpp == m_header.channels*8);
ASSERT(bpp%8 == 0);
ASSERT(bpp >= m_header.bpp);
 
DataT* y = m_channel[0]; ASSERT(y);
DataT* u = m_channel[1]; ASSERT(u);
DataT* v = m_channel[2]; ASSERT(v);
UINT8 *buffg = &buff[channelMap[1]],
*buffr = &buff[channelMap[2]],
*buffb = &buff[channelMap[0]];
UINT8 g;
int cnt, channels = bpp/8;
if(m_downsample){
for (i=0; i < h; i++) {
if (i%2) sampledPos -= (w + 1)/2;
cnt = 0;
for (j=0; j < w; j++) {
// image was downsampled
uAvg = u[sampledPos];
vAvg = v[sampledPos];
// Yuv
buffg[cnt] = g = Clamp8(y[yPos] + YUVoffset8 - ((uAvg + vAvg ) >> 2)); // must be logical shift operator
buffr[cnt] = Clamp8(uAvg + g);
buffb[cnt] = Clamp8(vAvg + g);
yPos++;
cnt += channels;
if (j%2) sampledPos++;
}
buffb += pitch;
buffg += pitch;
buffr += pitch;
if (wOdd) sampledPos++;
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
}else{
for (i=0; i < h; i++) {
cnt = 0;
for (j = 0; j < w; j++) {
uAvg = u[yPos];
vAvg = v[yPos];
// Yuv
buffg[cnt] = g = Clamp8(y[yPos] + YUVoffset8 - ((uAvg + vAvg ) >> 2)); // must be logical shift operator
buffr[cnt] = Clamp8(uAvg + g);
buffb[cnt] = Clamp8(vAvg + g);
yPos++;
cnt += channels;
}
buffb += pitch;
buffg += pitch;
buffr += pitch;
 
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
}
break;
}
case ImageModeRGB48:
{
ASSERT(m_header.channels == 3);
ASSERT(m_header.bpp == 48);
 
const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1);
 
DataT* y = m_channel[0]; ASSERT(y);
DataT* u = m_channel[1]; ASSERT(u);
DataT* v = m_channel[2]; ASSERT(v);
int cnt, channels;
DataT g;
 
if (bpp >= 48 && bpp%16 == 0) {
const int shift = 16 - UsedBitsPerChannel(); ASSERT(shift >= 0);
UINT16 *buff16 = (UINT16 *)buff;
int pitch16 = pitch/2;
channels = bpp/16; ASSERT(channels >= m_header.channels);
 
for (i=0; i < h; i++) {
if (i%2) sampledPos -= (w + 1)/2;
cnt = 0;
for (j=0; j < w; j++) {
if (m_downsample) {
// image was downsampled
uAvg = u[sampledPos];
vAvg = v[sampledPos];
} else {
uAvg = u[yPos];
vAvg = v[yPos];
}
// Yuv
g = y[yPos] + yuvOffset16 - ((uAvg + vAvg ) >> 2); // must be logical shift operator
buff16[cnt + channelMap[1]] = Clamp16(g << shift);
buff16[cnt + channelMap[2]] = Clamp16((uAvg + g) << shift);
buff16[cnt + channelMap[0]] = Clamp16((vAvg + g) << shift);
yPos++;
cnt += channels;
if (j%2) sampledPos++;
}
buff16 += pitch16;
if (wOdd) sampledPos++;
 
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
} else {
ASSERT(bpp%8 == 0);
const int shift = __max(0, UsedBitsPerChannel() - 8);
channels = bpp/8; ASSERT(channels >= m_header.channels);
 
for (i=0; i < h; i++) {
if (i%2) sampledPos -= (w + 1)/2;
cnt = 0;
for (j=0; j < w; j++) {
if (m_downsample) {
// image was downsampled
uAvg = u[sampledPos];
vAvg = v[sampledPos];
} else {
uAvg = u[yPos];
vAvg = v[yPos];
}
// Yuv
g = y[yPos] + yuvOffset16 - ((uAvg + vAvg ) >> 2); // must be logical shift operator
buff[cnt + channelMap[1]] = Clamp8(g >> shift);
buff[cnt + channelMap[2]] = Clamp8((uAvg + g) >> shift);
buff[cnt + channelMap[0]] = Clamp8((vAvg + g) >> shift);
yPos++;
cnt += channels;
if (j%2) sampledPos++;
}
buff += pitch;
if (wOdd) sampledPos++;
 
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
}
break;
}
case ImageModeLabColor:
{
ASSERT(m_header.channels == 3);
ASSERT(m_header.bpp == m_header.channels*8);
ASSERT(bpp%8 == 0);
 
DataT* l = m_channel[0]; ASSERT(l);
DataT* a = m_channel[1]; ASSERT(a);
DataT* b = m_channel[2]; ASSERT(b);
int cnt, channels = bpp/8; ASSERT(channels >= m_header.channels);
 
for (i=0; i < h; i++) {
if (i%2) sampledPos -= (w + 1)/2;
cnt = 0;
for (j=0; j < w; j++) {
if (m_downsample) {
// image was downsampled
uAvg = a[sampledPos];
vAvg = b[sampledPos];
} else {
uAvg = a[yPos];
vAvg = b[yPos];
}
buff[cnt + channelMap[0]] = Clamp8(l[yPos] + YUVoffset8);
buff[cnt + channelMap[1]] = Clamp8(uAvg + YUVoffset8);
buff[cnt + channelMap[2]] = Clamp8(vAvg + YUVoffset8);
cnt += channels;
yPos++;
if (j%2) sampledPos++;
}
buff += pitch;
if (wOdd) sampledPos++;
 
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
break;
}
case ImageModeLab48:
{
ASSERT(m_header.channels == 3);
ASSERT(m_header.bpp == m_header.channels*16);
 
const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1);
 
DataT* l = m_channel[0]; ASSERT(l);
DataT* a = m_channel[1]; ASSERT(a);
DataT* b = m_channel[2]; ASSERT(b);
int cnt, channels;
 
if (bpp%16 == 0) {
const int shift = 16 - UsedBitsPerChannel(); ASSERT(shift >= 0);
UINT16 *buff16 = (UINT16 *)buff;
int pitch16 = pitch/2;
channels = bpp/16; ASSERT(channels >= m_header.channels);
 
for (i=0; i < h; i++) {
if (i%2) sampledPos -= (w + 1)/2;
cnt = 0;
for (j=0; j < w; j++) {
if (m_downsample) {
// image was downsampled
uAvg = a[sampledPos];
vAvg = b[sampledPos];
} else {
uAvg = a[yPos];
vAvg = b[yPos];
}
buff16[cnt + channelMap[0]] = Clamp16((l[yPos] + yuvOffset16) << shift);
buff16[cnt + channelMap[1]] = Clamp16((uAvg + yuvOffset16) << shift);
buff16[cnt + channelMap[2]] = Clamp16((vAvg + yuvOffset16) << shift);
cnt += channels;
yPos++;
if (j%2) sampledPos++;
}
buff16 += pitch16;
if (wOdd) sampledPos++;
 
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
} else {
ASSERT(bpp%8 == 0);
const int shift = __max(0, UsedBitsPerChannel() - 8);
channels = bpp/8; ASSERT(channels >= m_header.channels);
 
for (i=0; i < h; i++) {
if (i%2) sampledPos -= (w + 1)/2;
cnt = 0;
for (j=0; j < w; j++) {
if (m_downsample) {
// image was downsampled
uAvg = a[sampledPos];
vAvg = b[sampledPos];
} else {
uAvg = a[yPos];
vAvg = b[yPos];
}
buff[cnt + channelMap[0]] = Clamp8((l[yPos] + yuvOffset16) >> shift);
buff[cnt + channelMap[1]] = Clamp8((uAvg + yuvOffset16) >> shift);
buff[cnt + channelMap[2]] = Clamp8((vAvg + yuvOffset16) >> shift);
cnt += channels;
yPos++;
if (j%2) sampledPos++;
}
buff += pitch;
if (wOdd) sampledPos++;
 
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
}
break;
}
case ImageModeRGBA:
case ImageModeCMYKColor:
{
ASSERT(m_header.channels == 4);
ASSERT(m_header.bpp == m_header.channels*8);
ASSERT(bpp%8 == 0);
 
DataT* y = m_channel[0]; ASSERT(y);
DataT* u = m_channel[1]; ASSERT(u);
DataT* v = m_channel[2]; ASSERT(v);
DataT* a = m_channel[3]; ASSERT(a);
UINT8 g, aAvg;
int cnt, channels = bpp/8; ASSERT(channels >= m_header.channels);
 
for (i=0; i < h; i++) {
if (i%2) sampledPos -= (w + 1)/2;
cnt = 0;
for (j=0; j < w; j++) {
if (m_downsample) {
// image was downsampled
uAvg = u[sampledPos];
vAvg = v[sampledPos];
aAvg = Clamp8(a[sampledPos] + YUVoffset8);
} else {
uAvg = u[yPos];
vAvg = v[yPos];
aAvg = Clamp8(a[yPos] + YUVoffset8);
}
// Yuv
buff[cnt + channelMap[1]] = g = Clamp8(y[yPos] + YUVoffset8 - ((uAvg + vAvg ) >> 2)); // must be logical shift operator
buff[cnt + channelMap[2]] = Clamp8(uAvg + g);
buff[cnt + channelMap[0]] = Clamp8(vAvg + g);
buff[cnt + channelMap[3]] = aAvg;
yPos++;
cnt += channels;
if (j%2) sampledPos++;
}
buff += pitch;
if (wOdd) sampledPos++;
 
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
break;
}
case ImageModeCMYK64:
{
ASSERT(m_header.channels == 4);
ASSERT(m_header.bpp == 64);
 
const DataT yuvOffset16 = 1 << (UsedBitsPerChannel() - 1);
 
DataT* y = m_channel[0]; ASSERT(y);
DataT* u = m_channel[1]; ASSERT(u);
DataT* v = m_channel[2]; ASSERT(v);
DataT* a = m_channel[3]; ASSERT(a);
DataT g, aAvg;
int cnt, channels;
 
if (bpp%16 == 0) {
const int shift = 16 - UsedBitsPerChannel(); ASSERT(shift >= 0);
UINT16 *buff16 = (UINT16 *)buff;
int pitch16 = pitch/2;
channels = bpp/16; ASSERT(channels >= m_header.channels);
 
for (i=0; i < h; i++) {
if (i%2) sampledPos -= (w + 1)/2;
cnt = 0;
for (j=0; j < w; j++) {
if (m_downsample) {
// image was downsampled
uAvg = u[sampledPos];
vAvg = v[sampledPos];
aAvg = a[sampledPos] + yuvOffset16;
} else {
uAvg = u[yPos];
vAvg = v[yPos];
aAvg = a[yPos] + yuvOffset16;
}
// Yuv
g = y[yPos] + yuvOffset16 - ((uAvg + vAvg ) >> 2); // must be logical shift operator
buff16[cnt + channelMap[1]] = Clamp16(g << shift);
buff16[cnt + channelMap[2]] = Clamp16((uAvg + g) << shift);
buff16[cnt + channelMap[0]] = Clamp16((vAvg + g) << shift);
buff16[cnt + channelMap[3]] = Clamp16(aAvg << shift);
yPos++;
cnt += channels;
if (j%2) sampledPos++;
}
buff16 += pitch16;
if (wOdd) sampledPos++;
 
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
} else {
ASSERT(bpp%8 == 0);
const int shift = __max(0, UsedBitsPerChannel() - 8);
channels = bpp/8; ASSERT(channels >= m_header.channels);
 
for (i=0; i < h; i++) {
if (i%2) sampledPos -= (w + 1)/2;
cnt = 0;
for (j=0; j < w; j++) {
if (m_downsample) {
// image was downsampled
uAvg = u[sampledPos];
vAvg = v[sampledPos];
aAvg = a[sampledPos] + yuvOffset16;
} else {
uAvg = u[yPos];
vAvg = v[yPos];
aAvg = a[yPos] + yuvOffset16;
}
// Yuv
g = y[yPos] + yuvOffset16 - ((uAvg + vAvg ) >> 2); // must be logical shift operator
buff[cnt + channelMap[1]] = Clamp8(g >> shift);
buff[cnt + channelMap[2]] = Clamp8((uAvg + g) >> shift);
buff[cnt + channelMap[0]] = Clamp8((vAvg + g) >> shift);
buff[cnt + channelMap[3]] = Clamp8(aAvg >> shift);
yPos++;
cnt += channels;
if (j%2) sampledPos++;
}
buff += pitch;
if (wOdd) sampledPos++;
 
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
}
break;
}
#ifdef __PGF32SUPPORT__
case ImageModeGray32:
{
ASSERT(m_header.channels == 1);
ASSERT(m_header.bpp == 32);
 
const int yuvOffset31 = 1 << (UsedBitsPerChannel() - 1);
 
DataT* y = m_channel[0]; ASSERT(y);
 
if (bpp == 32) {
const int shift = 31 - UsedBitsPerChannel(); ASSERT(shift >= 0);
UINT32 *buff32 = (UINT32 *)buff;
int pitch32 = pitch/4;
 
for (i=0; i < h; i++) {
for (j=0; j < w; j++) {
buff32[j] = Clamp31((y[yPos++] + yuvOffset31) << shift);
}
buff32 += pitch32;
 
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
} else if (bpp == 16) {
const int usedBits = UsedBitsPerChannel();
UINT16 *buff16 = (UINT16 *)buff;
int pitch16 = pitch/2;
 
if (usedBits < 16) {
const int shift = 16 - usedBits;
for (i=0; i < h; i++) {
for (j=0; j < w; j++) {
buff16[j] = Clamp16((y[yPos++] + yuvOffset31) << shift);
}
buff16 += pitch16;
 
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
} else {
const int shift = __max(0, usedBits - 16);
for (i=0; i < h; i++) {
for (j=0; j < w; j++) {
buff16[j] = Clamp16((y[yPos++] + yuvOffset31) >> shift);
}
buff16 += pitch16;
 
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
}
} else {
ASSERT(bpp == 8);
const int shift = __max(0, UsedBitsPerChannel() - 8);
for (i=0; i < h; i++) {
for (j=0; j < w; j++) {
buff[j] = Clamp8((y[yPos++] + yuvOffset31) >> shift);
}
buff += pitch;
 
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
}
break;
}
#endif
case ImageModeRGB12:
{
ASSERT(m_header.channels == 3);
ASSERT(m_header.bpp == m_header.channels*4);
ASSERT(bpp == m_header.channels*4);
ASSERT(!m_downsample);
 
DataT* y = m_channel[0]; ASSERT(y);
DataT* u = m_channel[1]; ASSERT(u);
DataT* v = m_channel[2]; ASSERT(v);
UINT16 yval;
int cnt;
 
for (i=0; i < h; i++) {
cnt = 0;
for (j=0; j < w; j++) {
// Yuv
uAvg = u[yPos];
vAvg = v[yPos];
yval = Clamp4(y[yPos++] + YUVoffset4 - ((uAvg + vAvg ) >> 2)); // must be logical shift operator
if (j%2 == 0) {
buff[cnt] = UINT8(Clamp4(vAvg + yval) | (yval << 4));
cnt++;
buff[cnt] = Clamp4(uAvg + yval);
} else {
buff[cnt] |= Clamp4(vAvg + yval) << 4;
cnt++;
buff[cnt] = UINT8(yval | (Clamp4(uAvg + yval) << 4));
cnt++;
}
}
buff += pitch;
 
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
break;
}
case ImageModeRGB16:
{
ASSERT(m_header.channels == 3);
ASSERT(m_header.bpp == 16);
ASSERT(bpp == 16);
ASSERT(!m_downsample);
 
DataT* y = m_channel[0]; ASSERT(y);
DataT* u = m_channel[1]; ASSERT(u);
DataT* v = m_channel[2]; ASSERT(v);
UINT16 yval;
UINT16 *buff16 = (UINT16 *)buff;
int pitch16 = pitch/2;
 
for (i=0; i < h; i++) {
for (j=0; j < w; j++) {
// Yuv
uAvg = u[yPos];
vAvg = v[yPos];
yval = Clamp6(y[yPos++] + YUVoffset6 - ((uAvg + vAvg ) >> 2)); // must be logical shift operator
buff16[j] = (yval << 5) | ((Clamp6(uAvg + yval) >> 1) << 11) | (Clamp6(vAvg + yval) >> 1);
}
buff16 += pitch16;
 
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
break;
}
default:
ASSERT(false);
}
 
#ifdef __PGFROISUPPORT__
if (targetBuff) {
// copy valid ROI (m_roi) from temporary buffer (roi) to target buffer
if (bpp%8 == 0) {
BYTE bypp = bpp/8;
buff = buffStart + (levelRoi.top - roi.top)*pitch + (levelRoi.left - roi.left)*bypp;
w = levelRoi.Width()*bypp;
h = levelRoi.Height();
 
for (i=0; i < h; i++) {
for (j=0; j < w; j++) {
targetBuff[j] = buff[j];
}
targetBuff += targetPitch;
buff += pitch;
}
} else {
// to do
}
 
delete[] buffStart; buffStart = 0;
}
#endif
}
 
//////////////////////////////////////////////////////////////////////
/// Get YUV image data in interleaved format: (ordering is YUV[A])
/// The absolute value of pitch is the number of bytes of an image row of the given image buffer.
/// If pitch is negative, then the image buffer must point to the last row of a bottom-up image (first byte on last row).
/// if pitch is positive, then the image buffer must point to the first row of a top-down image (first byte).
/// 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
/// provide a channelMap of size of expected channels (depending on image mode). For example, PGF provides a channel sequence BGR in RGB color mode.
/// If your provided image buffer expects a channel sequence VUY, then the channelMap looks like { 2, 1, 0 }.
/// It might throw an IOException.
/// @param pitch The number of bytes of a row of the image buffer.
/// @param buff An image buffer.
/// @param bpp The number of bits per pixel used in image buffer.
/// @param channelMap A integer array containing the mapping of PGF channel ordering to expected channel ordering.
/// @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.
void CPGFImage::GetYUV(int pitch, DataT* buff, BYTE bpp, int channelMap[] /*= NULL*/, CallbackPtr cb /*= NULL*/, void *data /*=NULL*/) const THROW_ {
ASSERT(buff);
const UINT32 w = m_width[0];
const UINT32 h = m_height[0];
const bool wOdd = (1 == w%2);
const int dataBits = DataTSize*8; ASSERT(dataBits == 16 || dataBits == 32);
const int pitch2 = pitch/DataTSize;
const int yuvOffset = (dataBits == 16) ? YUVoffset8 : YUVoffset16;
const double dP = 1.0/h;
 
int defMap[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; ASSERT(sizeof(defMap)/sizeof(defMap[0]) == MaxChannels);
if (channelMap == NULL) channelMap = defMap;
int sampledPos = 0, yPos = 0;
DataT uAvg, vAvg;
double percent = 0;
UINT32 i, j;
 
if (m_header.channels == 3) {
ASSERT(bpp%dataBits == 0);
 
DataT* y = m_channel[0]; ASSERT(y);
DataT* u = m_channel[1]; ASSERT(u);
DataT* v = m_channel[2]; ASSERT(v);
int cnt, channels = bpp/dataBits; ASSERT(channels >= m_header.channels);
 
for (i=0; i < h; i++) {
if (i%2) sampledPos -= (w + 1)/2;
cnt = 0;
for (j=0; j < w; j++) {
if (m_downsample) {
// image was downsampled
uAvg = u[sampledPos];
vAvg = v[sampledPos];
} else {
uAvg = u[yPos];
vAvg = v[yPos];
}
buff[cnt + channelMap[0]] = y[yPos];
buff[cnt + channelMap[1]] = uAvg;
buff[cnt + channelMap[2]] = vAvg;
yPos++;
cnt += channels;
if (j%2) sampledPos++;
}
buff += pitch2;
if (wOdd) sampledPos++;
 
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
} else if (m_header.channels == 4) {
ASSERT(m_header.bpp == m_header.channels*8);
ASSERT(bpp%dataBits == 0);
 
DataT* y = m_channel[0]; ASSERT(y);
DataT* u = m_channel[1]; ASSERT(u);
DataT* v = m_channel[2]; ASSERT(v);
DataT* a = m_channel[3]; ASSERT(a);
UINT8 aAvg;
int cnt, channels = bpp/dataBits; ASSERT(channels >= m_header.channels);
 
for (i=0; i < h; i++) {
if (i%2) sampledPos -= (w + 1)/2;
cnt = 0;
for (j=0; j < w; j++) {
if (m_downsample) {
// image was downsampled
uAvg = u[sampledPos];
vAvg = v[sampledPos];
aAvg = Clamp8(a[sampledPos] + yuvOffset);
} else {
uAvg = u[yPos];
vAvg = v[yPos];
aAvg = Clamp8(a[yPos] + yuvOffset);
}
// Yuv
buff[cnt + channelMap[0]] = y[yPos];
buff[cnt + channelMap[1]] = uAvg;
buff[cnt + channelMap[2]] = vAvg;
buff[cnt + channelMap[3]] = aAvg;
yPos++;
cnt += channels;
if (j%2) sampledPos++;
}
buff += pitch2;
if (wOdd) sampledPos++;
 
if (cb) {
percent += dP;
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
}
}
}
}
 
//////////////////////////////////////////////////////////////////////
/// Import a YUV image from a specified image buffer.
/// The absolute value of pitch is the number of bytes of an image row.
/// If pitch is negative, then buff points to the last row of a bottom-up image (first byte on last row).
/// If pitch is positive, then buff points to the first row of a top-down image (first byte).
/// 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
/// provide a channelMap of size of expected channels (depending on image mode). For example, PGF expects in RGB color mode a channel sequence BGR.
/// If your provided image buffer contains a channel sequence VUY, then the channelMap looks like { 2, 1, 0 }.
/// It might throw an IOException.
/// @param pitch The number of bytes of a row of the image buffer.
/// @param buff An image buffer.
/// @param bpp The number of bits per pixel used in image buffer.
/// @param channelMap A integer array containing the mapping of input channel ordering to expected channel ordering.
/// @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.
void CPGFImage::ImportYUV(int pitch, DataT *buff, BYTE bpp, int channelMap[] /*= NULL*/, CallbackPtr cb /*= NULL*/, void *data /*=NULL*/) THROW_ {
ASSERT(buff);
const double dP = 1.0/m_header.height;
const int dataBits = DataTSize*8; ASSERT(dataBits == 16 || dataBits == 32);
const int pitch2 = pitch/DataTSize;
const int yuvOffset = (dataBits == 16) ? YUVoffset8 : YUVoffset16;
 
int yPos = 0, cnt = 0;
double percent = 0;
int defMap[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; ASSERT(sizeof(defMap)/sizeof(defMap[0]) == MaxChannels);
 
if (channelMap == NULL) channelMap = defMap;
 
if (m_header.channels == 3) {
ASSERT(bpp%dataBits == 0);
 
DataT* y = m_channel[0]; ASSERT(y);
DataT* u = m_channel[1]; ASSERT(u);
DataT* v = m_channel[2]; ASSERT(v);
const int channels = bpp/dataBits; ASSERT(channels >= m_header.channels);
 
for (UINT32 h=0; h < m_header.height; h++) {
if (cb) {
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
percent += dP;
}
 
cnt = 0;
for (UINT32 w=0; w < m_header.width; w++) {
y[yPos] = buff[cnt + channelMap[0]];
u[yPos] = buff[cnt + channelMap[1]];
v[yPos] = buff[cnt + channelMap[2]];
yPos++;
cnt += channels;
}
buff += pitch2;
}
} else if (m_header.channels == 4) {
ASSERT(bpp%dataBits == 0);
 
DataT* y = m_channel[0]; ASSERT(y);
DataT* u = m_channel[1]; ASSERT(u);
DataT* v = m_channel[2]; ASSERT(v);
DataT* a = m_channel[3]; ASSERT(a);
const int channels = bpp/dataBits; ASSERT(channels >= m_header.channels);
 
for (UINT32 h=0; h < m_header.height; h++) {
if (cb) {
if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed);
percent += dP;
}
 
cnt = 0;
for (UINT32 w=0; w < m_header.width; w++) {
y[yPos] = buff[cnt + channelMap[0]];
u[yPos] = buff[cnt + channelMap[1]];
v[yPos] = buff[cnt + channelMap[2]];
a[yPos] = buff[cnt + channelMap[3]] - yuvOffset;
yPos++;
cnt += channels;
}
buff += pitch2;
}
}
 
if (m_downsample) {
// Subsampling of the chrominance and alpha channels
for (int i=1; i < m_header.channels; i++) {
Downsample(i);
}
}
}
 
/trunk/Scribus/scribus/third_party/pgf/PGFimage.h
1,539 → 1,569
/*
* The Progressive Graphics File; http://www.libpgf.org
*
* $Date: 2007-02-03 13:04:21 +0100 (Sa, 03 Feb 2007) $
* $Revision: 280 $
*
* This file Copyright (C) 2006 xeraina GmbH, Switzerland
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
 
//////////////////////////////////////////////////////////////////////
/// @file PGFimage.h
/// @brief PGF image class
/// @author C. Stamm
 
#ifndef PGF_PGFIMAGE_H
#define PGF_PGFIMAGE_H
 
#include "PGFstream.h"
 
class CDecoder;
class CEncoder;
class CWaveletTransform;
 
//////////////////////////////////////////////////////////////////////
/// PGF image class is the main class. You always need a PGF object
/// for encoding or decoding image data.
/// Decoding:
/// pgf.Open(...)
/// pgf.Read(...)
/// pgf.GetBitmap(...)
/// Encoding:
/// pgf.SetHeader(...)
/// pgf.ImportBitmap(...)
/// pgf.Write(...)
/// @author C. Stamm, R. Spuler
/// @brief PGF main class
class CPGFImage {
public:
 
//////////////////////////////////////////////////////////////////////
/// Standard constructor: It is used to create a PGF instance for opening and reading.
CPGFImage();
 
//////////////////////////////////////////////////////////////////////
/// Destructor: Destroy internal data structures.
virtual ~CPGFImage();
 
//////////////////////////////////////////////////////////////////////
/// Close PGF image after opening and reading.
/// Destructor calls this method during destruction.
virtual void Close();
 
//////////////////////////////////////////////////////////////////////
/// Destroy internal data structures.
/// Destructor calls this method during destruction.
virtual void Destroy();
 
//////////////////////////////////////////////////////////////////////
/// Open a PGF image at current stream position: read pre-header, header, and ckeck image type.
/// Precondition: The stream has been opened for reading.
/// It might throw an IOException.
/// @param stream A PGF stream
void Open(CPGFStream* stream) THROW_;
 
//////////////////////////////////////////////////////////////////////
/// Returns true if the PGF has been opened and not closed.
bool IsOpen() const { return m_decoder != NULL; }
 
//////////////////////////////////////////////////////////////////////
/// Read and decode some levels of a PGF image at current stream position.
/// A PGF image is structered in levels, numbered between 0 and Levels() - 1.
/// Each level can be seen as a single image, containing the same content
/// as all other levels, but in a different size (width, height).
/// The image size at level i is double the size (width, height) of the image at level i+1.
/// The image at level 0 contains the original size.
/// Precondition: The PGF image has been opened with a call of Open(...).
/// It might throw an IOException.
/// @param level [0, nLevels) The image level of the resulting image in the internal image buffer.
/// @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.
/// @param data Data Pointer to C++ class container to host callback procedure.
void Read(int level = 0, CallbackPtr cb = NULL, void *data = NULL) THROW_;
 
#ifdef __PGFROISUPPORT__
//////////////////////////////////////////////////////////////////////
/// Read a rectangular region of interest of a PGF image at current stream position.
/// The origin of the coordinate axis is the top-left corner of the image.
/// All coordinates are measured in pixels.
/// It might throw an IOException.
/// @param rect [inout] Rectangular region of interest (ROI). The rect might be cropped.
/// @param level [0, nLevels) The image level of the resulting image in the internal image buffer.
/// @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.
/// @param data Data Pointer to C++ class container to host callback procedure.
void Read(PGFRect& rect, int level = 0, CallbackPtr cb = NULL, void *data = NULL) THROW_;
#endif
 
//////////////////////////////////////////////////////////////////////
/// Read and decode smallest level of a PGF image at current stream position.
/// For details, please refert to Read(...)
/// Precondition: The PGF image has been opened with a call of Open(...).
/// It might throw an IOException.
void ReadPreview() THROW_ { Read(Levels() - 1); }
 
//////////////////////////////////////////////////////////////////////
/// After you've written a PGF image, you can call this method followed by GetBitmap/GetYUV
/// to get a quick reconstruction (coded -> decoded image).
/// @param level The image level of the resulting image in the internal image buffer.
void Reconstruct(int level = 0);
 
//////////////////////////////////////////////////////////////////////
/// Get image data in interleaved format: (ordering of RGB data is BGR[A])
/// Upsampling, YUV to RGB transform and interleaving are done here to reduce the number
/// of passes over the data.
/// The absolute value of pitch is the number of bytes of an image row of the given image buffer.
/// If pitch is negative, then the image buffer must point to the last row of a bottom-up image (first byte on last row).
/// if pitch is positive, then the image buffer must point to the first row of a top-down image (first byte).
/// 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
/// provide a channelMap of size of expected channels (depending on image mode). For example, PGF provides a channel sequence BGR in RGB color mode.
/// If your provided image buffer expects a channel sequence ARGB, then the channelMap looks like { 3, 2, 1, 0 }.
/// It might throw an IOException.
/// @param pitch The number of bytes of a row of the image buffer.
/// @param buff An image buffer.
/// @param bpp The number of bits per pixel used in image buffer.
/// @param channelMap A integer array containing the mapping of PGF channel ordering to expected channel ordering.
/// @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.
/// @param data Data Pointer to C++ class container to host callback procedure.
void GetBitmap(int pitch, UINT8* buff, BYTE bpp, int channelMap[] = NULL, CallbackPtr cb = NULL, void *data = NULL) const THROW_; // throws IOException
 
//////////////////////////////////////////////////////////////////////
/// Get YUV image data in interleaved format: (ordering is YUV[A])
/// The absolute value of pitch is the number of bytes of an image row of the given image buffer.
/// If pitch is negative, then the image buffer must point to the last row of a bottom-up image (first byte on last row).
/// if pitch is positive, then the image buffer must point to the first row of a top-down image (first byte).
/// 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
/// provide a channelMap of size of expected channels (depending on image mode). For example, PGF provides a channel sequence BGR in RGB color mode.
/// If your provided image buffer expects a channel sequence VUY, then the channelMap looks like { 2, 1, 0 }.
/// It might throw an IOException.
/// @param pitch The number of bytes of a row of the image buffer.
/// @param buff An image buffer.
/// @param bpp The number of bits per pixel used in image buffer.
/// @param channelMap A integer array containing the mapping of PGF channel ordering to expected channel ordering.
/// @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.
/// @param data Data Pointer to C++ class container to host callback procedure.
void GetYUV(int pitch, DataT* buff, BYTE bpp, int channelMap[] = NULL, CallbackPtr cb = NULL, void *data = NULL) const THROW_; // throws IOException
 
//////////////////////////////////////////////////////////////////////
/// Import an image from a specified image buffer.
/// This method is usually called before Write(...) and after SetHeader(...).
/// The absolute value of pitch is the number of bytes of an image row.
/// If pitch is negative, then buff points to the last row of a bottom-up image (first byte on last row).
/// If pitch is positive, then buff points to the first row of a top-down image (first byte).
/// 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
/// provide a channelMap of size of expected channels (depending on image mode). For example, PGF expects in RGB color mode a channel sequence BGR.
/// If your provided image buffer contains a channel sequence ARGB, then the channelMap looks like { 3, 2, 1, 0 }.
/// It might throw an IOException.
/// @param pitch The number of bytes of a row of the image buffer.
/// @param buff An image buffer.
/// @param bpp The number of bits per pixel used in image buffer.
/// @param channelMap A integer array containing the mapping of input channel ordering to expected channel ordering.
/// @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.
/// @param data Data Pointer to C++ class container to host callback procedure.
void ImportBitmap(int pitch, UINT8 *buff, BYTE bpp, int channelMap[] = NULL, CallbackPtr cb = NULL, void *data = NULL) THROW_;
 
//////////////////////////////////////////////////////////////////////
/// Import a YUV image from a specified image buffer.
/// The absolute value of pitch is the number of bytes of an image row.
/// If pitch is negative, then buff points to the last row of a bottom-up image (first byte on last row).
/// If pitch is positive, then buff points to the first row of a top-down image (first byte).
/// 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
/// provide a channelMap of size of expected channels (depending on image mode). For example, PGF expects in RGB color mode a channel sequence BGR.
/// If your provided image buffer contains a channel sequence VUY, then the channelMap looks like { 2, 1, 0 }.
/// It might throw an IOException.
/// @param pitch The number of bytes of a row of the image buffer.
/// @param buff An image buffer.
/// @param bpp The number of bits per pixel used in image buffer.
/// @param channelMap A integer array containing the mapping of input channel ordering to expected channel ordering.
/// @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.
/// @param data Data Pointer to C++ class container to host callback procedure.
void ImportYUV(int pitch, DataT *buff, BYTE bpp, int channelMap[] = NULL, CallbackPtr cb = NULL, void *data = NULL) THROW_;
 
//////////////////////////////////////////////////////////////////////
/// Encode and write a PGF image at current stream position.
/// A PGF image is structered in levels, numbered between 0 and Levels() - 1.
/// Each level can be seen as a single image, containing the same content
/// as all other levels, but in a different size (width, height).
/// The image size at level i is double the size (width, height) of the image at level i+1.
/// The image at level 0 contains the original size.
/// Precondition: the PGF image contains a valid header (see also SetHeader(...)).
/// Please note: the earlier parameter nLevels has now to be set with SetHeader. Either specify the number of levels
/// or use the value 0 for automatic setting.
/// It might throw an IOException.
/// @param stream A PGF stream
/// @param nWrittenBytes [in-out] The number of bytes written into stream are added to the input value.
/// @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.
/// @param data Data Pointer to C++ class container to host callback procedure.
void Write(CPGFStream* stream, UINT32* nWrittenBytes = NULL, CallbackPtr cb = NULL, void *data = NULL) THROW_;
 
//////////////////////////////////////////////////////////////////
/// Create wavelet transform channels and encoder.
/// Call this method before your first call of Write(int level), but after SetHeader().
/// Don't use this method when you call Write().
/// It might throw an IOException.
/// @param stream A PGF stream
/// @return The number of bytes written into stream.
UINT32 WriteHeader(CPGFStream* stream) THROW_;
 
#ifdef __PGFROISUPPORT__
//////////////////////////////////////////////////////////////////
/// Encode and write down to given level at current stream position.
/// A PGF image is structered in levels, numbered between 0 and Levels() - 1.
/// Each level can be seen as a single image, containing the same content
/// as all other levels, but in a different size (width, height).
/// The image size at level i is double the size (width, height) of the image at level i+1.
/// The image at level 0 contains the original size.
/// Precondition: the PGF image contains a valid header (see also SetHeader(...)) and
/// WriteHeader() has been called before Write().
/// The ROI encoding scheme is used.
/// It might throw an IOException.
/// @param level [0, nLevels) The image level of the resulting image in the internal image buffer.
/// @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.
/// @param data Data Pointer to C++ class container to host callback procedure.
/// @return The number of bytes written into stream.
UINT32 Write(int level, CallbackPtr cb = NULL, void *data = NULL) THROW_;
#endif
 
/////////////////////////////////////////////////////////////////////
/// Configures the encoder.
/// @param useOMP Use parallel threading with Open MP during encoding. Default value: true. Influences the encoding only if the codec has been compiled with OpenMP support.
/// @param favorSpeedOverSize Favors encoding speed over compression ratio. Default value: false
void ConfigureEncoder(bool useOMP = true, bool favorSpeedOverSize = false) { m_useOMPinEncoder = useOMP; m_favorSpeedOverSize = favorSpeedOverSize; }
 
/////////////////////////////////////////////////////////////////////
/// Configures the encoder.
/// @param useOMP Use parallel threading with Open MP during decoding. Default value: true. Influences the decoding only if the codec has been compiled with OpenMP support.
void ConfigureDecoder(bool useOMP = true) { m_useOMPinDecoder = useOMP; }
 
//////////////////////////////////////////////////////////////////////
/// Set background of an RGB image with transparency channel or reset to default background.
/// @param bg A pointer to a background color or NULL (reset to default background)
void SetBackground(const RGBTRIPLE* bg);
 
//////////////////////////////////////////////////////////////////////
/// Set background of an RGB image with transparency channel.
/// @param red A red value (0..255)
/// @param green A green value (0..255)
/// @param blue A blue value (0..255)
void SetBackground(BYTE red, BYTE green, BYTE blue) { /*m_backgroundSet = true;*/ m_header.background.rgbtRed = red; m_header.background.rgbtGreen = green; m_header.background.rgbtBlue = blue; }
 
//////////////////////////////////////////////////////////////////////
/// Set internal PGF image buffer channel.
/// @param channel A YUV data channel
/// @param c A channel index
void SetChannel(DataT* channel, int c = 0) { ASSERT(c >= 0 && c < MaxChannels); m_channel[c] = channel; }
 
//////////////////////////////////////////////////////////////////////
/// Set PGF header and user data.
/// Precondition: The PGF image has been closed with Close(...) or never opened with Open(...).
/// It might throw an IOException.
/// @param header A valid and already filled in PGF header structure
/// @param flags A combination of additional version flags
/// @param userData A user-defined memory block
/// @param userDataLength The size of user-defined memory block in bytes
void SetHeader(const PGFHeader& header, BYTE flags = 0, UINT8* userData = 0, UINT32 userDataLength = 0) THROW_; // throws IOException
 
//////////////////////////////////////////////////////////////////////
/// Set maximum intensity value for image modes with more than eight bits per channel.
/// Don't call this method before SetHeader.
/// @param maxValue The maximum intensity value.
void SetMaxValue(UINT32 maxValue);
 
//////////////////////////////////////////////////////////////////////
/// Returns number of used bits per input/output image channel.
/// Precondition: header must be initialized.
/// @return number of used bits per input/output image channel.
BYTE UsedBitsPerChannel() const;
 
//////////////////////////////////////////////////////////////////////
/// Set refresh callback procedure and its parameter.
/// The refresh callback is called during Read(...) after each level read.
/// @param callback A refresh callback procedure
/// @param arg A parameter of the refresh callback procedure
void SetRefreshCallback(RefreshCB callback, void* arg) { m_cb = callback; m_cbArg = arg; }
 
//////////////////////////////////////////////////////////////////////
/// Sets the red, green, blue (RGB) color values for a range of entries in the palette (clut).
/// It might throw an IOException.
/// @param iFirstColor The color table index of the first entry to set.
/// @param nColors The number of color table entries to set.
/// @param prgbColors A pointer to the array of RGBQUAD structures to set the color table entries.
void SetColorTable(UINT32 iFirstColor, UINT32 nColors, const RGBQUAD* prgbColors) THROW_;
 
//////////////////////////////////////////////////////////////////////
/// Return the background color of an RGB image with transparency channel.
/// @return Background color in RGB
RGBTRIPLE Background() const { return m_header.background; }
 
//////////////////////////////////////////////////////////////////////
/// Return an internal YUV image channel.
/// @param c A channel index
/// @return An internal YUV image channel
DataT* GetChannel(int c = 0) { ASSERT(c >= 0 && c < MaxChannels); return m_channel[c]; }
 
//////////////////////////////////////////////////////////////////////
/// Retrieves red, green, blue (RGB) color values from a range of entries in the palette of the DIB section.
/// It might throw an IOException.
/// @param iFirstColor The color table index of the first entry to retrieve.
/// @param nColors The number of color table entries to retrieve.
/// @param prgbColors A pointer to the array of RGBQUAD structures to retrieve the color table entries.
void GetColorTable(UINT32 iFirstColor, UINT32 nColors, RGBQUAD* prgbColors) const THROW_;
 
//////////////////////////////////////////////////////////////////////
// Returns address of internal color table
/// @return Address of color table
const RGBQUAD* GetColorTable() const { return m_postHeader.clut; }
 
//////////////////////////////////////////////////////////////////////
/// Return the PGF header structure.
/// @return A PGF header structure
const PGFHeader* GetHeader() const { return &m_header; }
 
//////////////////////////////////////////////////////////////////////
/// Get maximum intensity value for image modes with more than eight bits per channel.
/// Don't call this method before the PGF header has been read.
/// @return The maximum intensity value.
UINT32 GetMaxValue() const { return (1 << m_header.background.rgbtBlue) - 1; }
 
//////////////////////////////////////////////////////////////////////
/// Return user data and size of user data.
/// @param size [out] Size of user data in bytes.
/// @return A pointer to user data or NULL if there is no user data.
const UINT8* GetUserData(UINT32& size) const;
 
//////////////////////////////////////////////////////////////////////
/// Return the length of all encoded headers in bytes.
/// Precondition: The PGF image has been opened with a call of Open(...).
/// @return The length of all encoded headers in bytes
UINT32 GetEncodedHeaderLength() const;
 
//////////////////////////////////////////////////////////////////////
/// Return the length of an encoded PGF level in bytes.
/// Precondition: The PGF image has been opened with a call of Open(...).
/// @param level The image level
/// @return The length of a PGF level in bytes
UINT32 GetEncodedLevelLength(int level) const { ASSERT(level >= 0 && level < m_header.nLevels); return m_levelLength[m_header.nLevels - level - 1]; }
 
////////////////////////////////////////////////////////////////////
/// Reset stream position to start of PGF pre-header
void ResetStreamPos() THROW_;
 
//////////////////////////////////////////////////////////////////////
/// Reads the encoded PGF headers and copies it to a target buffer.
/// Precondition: The PGF image has been opened with a call of Open(...).
/// It might throw an IOException.
/// @param target The target buffer
/// @param targetLen The length of the target buffer in bytes
/// @return The number of bytes copied to the target buffer
UINT32 ReadEncodedHeader(UINT8* target, UINT32 targetLen) const THROW_;
 
//////////////////////////////////////////////////////////////////////
/// Reads the data of an encoded PGF level and copies it to a target buffer
/// without decoding.
/// Precondition: The PGF image has been opened with a call of Open(...).
/// It might throw an IOException.
/// @param level The image level
/// @param target The target buffer
/// @param targetLen The length of the target buffer in bytes
/// @return The number of bytes copied to the target buffer
UINT32 ReadEncodedData(int level, UINT8* target, UINT32 targetLen) const THROW_;
 
//////////////////////////////////////////////////////////////////////
/// Return current image width of given channel in pixels.
/// The returned width depends on the levels read so far and on ROI.
/// @param c A channel index
/// @return Channel width in pixels
UINT32 ChannelWidth(int c = 0) const { ASSERT(c >= 0 && c < MaxChannels); return m_width[c]; }
 
//////////////////////////////////////////////////////////////////////
/// Return current image height of given channel in pixels.
/// The returned height depends on the levels read so far and on ROI.
/// @param c A channel index
/// @return Channel height in pixels
UINT32 ChannelHeight(int c = 0) const { ASSERT(c >= 0 && c < MaxChannels); return m_height[c]; }
 
//////////////////////////////////////////////////////////////////////
/// Return bits per channel.
/// @return Bits per channel
BYTE ChannelDepth() const { return DataTSize*8; }
 
//////////////////////////////////////////////////////////////////////
/// Return image width of channel 0 at given level in pixels.
/// The returned width is independent of any Read-operations and ROI.
/// @param level A level
/// @return Image level width in pixels
UINT32 Width(int level = 0) const { ASSERT(level >= 0); return LevelWidth(m_header.width, level); }
 
//////////////////////////////////////////////////////////////////////
/// Return image height of channel 0 at given level in pixels.
/// The returned height is independent of any Read-operations and ROI.
/// @param level A level
/// @return Image level height in pixels
UINT32 Height(int level = 0) const { ASSERT(level >= 0); return LevelHeight(m_header.height, level); }
 
//////////////////////////////////////////////////////////////////////
/// Return current image level.
/// Since Read(...) can be used to read each image level separately, it is
/// helpful to know the current level. The current level immediately after Open(...) is Levels().
/// @return Current image level
BYTE Level() const { return (BYTE)m_currentLevel; }
 
//////////////////////////////////////////////////////////////////////
/// Return the number of image levels.
/// @return Number of image levels
BYTE Levels() const { return m_header.nLevels; }
 
//////////////////////////////////////////////////////////////////////
/// Return the PGF quality. The quality is inbetween 0 and MaxQuality.
/// PGF quality 0 means lossless quality.
/// @return PGF quality
BYTE Quality() const { return m_header.quality; }
 
//////////////////////////////////////////////////////////////////////
/// Return the number of image channels.
/// An image of type RGB contains 3 image channels (B, G, R).
/// @return Number of image channels
BYTE Channels() const { return m_header.channels; }
 
//////////////////////////////////////////////////////////////////////
/// Return the image mode.
/// An image mode is a predefined constant value (see also PGFtypes.h) compatible with Adobe Photoshop.
/// It represents an image type and format.
/// @return Image mode
BYTE Mode() const { return m_header.mode; }
 
//////////////////////////////////////////////////////////////////////
/// Return the number of bits per pixel.
/// Valid values can be 1, 8, 12, 16, 24, 31, 32, 48, 64.
/// @return Number of bits per pixel.
BYTE BPP() const { return m_header.bpp; }
 
//////////////////////////////////////////////////////////////////////
/// Return true if the pgf image supports Region Of Interest (ROI).
/// @return true if the pgf image supports ROI.
bool ROIisSupported() const { return (m_preHeader.version & PGFROI) == PGFROI; }
 
//////////////////////////////////////////////////////////////////////
/// Returns highest supported version
BYTE Version() const;
 
//class methods
 
//////////////////////////////////////////////////////////////////////
/// Check for valid import image mode.
/// @param mode Image mode
/// @return True if an image of given mode can be imported with ImportBitmap(...)
static bool ImportIsSupported(BYTE mode);
 
//////////////////////////////////////////////////////////////////////
/// Compute and return image width at given level.
/// @param width Original image width (at level 0)
/// @param level An image level
/// @return Image level width in pixels
static UINT32 LevelWidth(UINT32 width, int level) { ASSERT(level >= 0); UINT32 w = (width >> level); return ((w << level) == width) ? w : w + 1; }
 
//////////////////////////////////////////////////////////////////////
/// Compute and return image height at given level.
/// @param height Original image height (at level 0)
/// @param level An image level
/// @return Image level height in pixels
static UINT32 LevelHeight(UINT32 height, int level) { ASSERT(level >= 0); UINT32 h = (height >> level); return ((h << level) == height) ? h : h + 1; }
 
protected:
CWaveletTransform* m_wtChannel[MaxChannels]; // wavelet transformed color channels
DataT* m_channel[MaxChannels]; // untransformed channels in YUV format
CDecoder* m_decoder; // PGF decoder
CEncoder* m_encoder; // PGF encoder
UINT32* m_levelLength; // length of each level in bytes; first level starts immediately after this array
UINT32 m_width[MaxChannels]; // width of each channel at current level
UINT32 m_height[MaxChannels]; // height of each channel at current level
PGFPreHeader m_preHeader; // PGF pre header
PGFHeader m_header; // PGF file header
PGFPostHeader m_postHeader; // PGF post header
int m_currentLevel; // transform level of current image
BYTE m_quant; // quantization parameter
bool m_downsample; // chrominance channels are downsampled
bool m_favorSpeedOverSize; // favor encoding speed over compression ratio
bool m_useOMPinEncoder; // use Open MP in encoder
bool m_useOMPinDecoder; // use Open MP in decoder
#ifdef __PGFROISUPPORT__
bool m_levelwise; // write level-wise (only used with WriteNextLevel)
bool m_streamReinitialized; // stream has been reinitialized
PGFRect m_roi; // region of interest
#endif
 
private:
RefreshCB m_cb; // pointer to refresh callback procedure
void *m_cbArg; // refresh callback argument
 
void ComputeLevels();
void CompleteHeader();
void RgbToYuv(int pitch, UINT8* rgbBuff, BYTE bpp, int channelMap[], CallbackPtr cb, void *data) THROW_;
void Downsample(int nChannel);
void WriteLevel() THROW_;
 
#ifdef __PGFROISUPPORT__
void SetROI(PGFRect rect);
#endif
 
UINT8 Clamp(DataT v) const {
// needs only one test in the normal case
if (v & 0xFFFFFF00) return (v < 0) ? (UINT8)0 : (UINT8)255; else return (UINT8)v;
}
UINT8 Clamp4(DataT v) const {
if (v & 0xFFFFFFF0) return (v < 0) ? (UINT8)0: (UINT8)15; else return (UINT8)v;
}
UINT16 Clamp6(DataT v) const {
if (v & 0xFFFFFFC0) return (v < 0) ? (UINT16)0: (UINT16)63; else return (UINT16)v;
}
UINT16 Clamp16(DataT v) const {
if (v & 0xFFFF0000) return (v < 0) ? (UINT16)0: (UINT16)65535; else return (UINT16)v;
}
UINT32 Clamp31(DataT v) const {
if (v < 0) return 0; else return (UINT32)v;
}
};
 
#endif //PGF_PGFIMAGE_H
/*
* The Progressive Graphics File; http://www.libpgf.org
*
* $Date: 2007-02-03 13:04:21 +0100 (Sa, 03 Feb 2007) $
* $Revision: 280 $
*
* This file Copyright (C) 2006 xeraina GmbH, Switzerland
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
 
//////////////////////////////////////////////////////////////////////
/// @file PGFimage.h
/// @brief PGF image class
/// @author C. Stamm
 
#ifndef PGF_PGFIMAGE_H
#define PGF_PGFIMAGE_H
 
#include "PGFstream.h"
 
//////////////////////////////////////////////////////////////////////
// types
enum ProgressMode { PM_Relative, PM_Absolute };
 
//////////////////////////////////////////////////////////////////////
// prototypes
class CDecoder;
class CEncoder;
class CWaveletTransform;
 
//////////////////////////////////////////////////////////////////////
/// PGF image class is the main class. You always need a PGF object
/// for encoding or decoding image data.
/// Decoding:
/// pgf.Open(...)
/// pgf.Read(...)
/// pgf.GetBitmap(...)
/// Encoding:
/// pgf.SetHeader(...)
/// pgf.ImportBitmap(...)
/// pgf.Write(...)
/// @author C. Stamm, R. Spuler
/// @brief PGF main class
class CPGFImage {
public:
//////////////////////////////////////////////////////////////////////
/// Standard constructor: It is used to create a PGF instance for opening and reading.
CPGFImage();
 
//////////////////////////////////////////////////////////////////////
/// Destructor: Destroy internal data structures.
virtual ~CPGFImage();
 
//////////////////////////////////////////////////////////////////////
/// Close PGF image after opening and reading.
/// Destructor calls this method during destruction.
virtual void Close();
 
//////////////////////////////////////////////////////////////////////
/// Destroy internal data structures.
/// Destructor calls this method during destruction.
virtual void Destroy();
 
//////////////////////////////////////////////////////////////////////
/// Open a PGF image at current stream position: read pre-header, header, and ckeck image type.
/// Precondition: The stream has been opened for reading.
/// It might throw an IOException.
/// @param stream A PGF stream
void Open(CPGFStream* stream) THROW_;
 
//////////////////////////////////////////////////////////////////////
/// Returns true if the PGF has been opened and not closed.
bool IsOpen() const { return m_decoder != NULL; }
 
//////////////////////////////////////////////////////////////////////
/// Read and decode some levels of a PGF image at current stream position.
/// A PGF image is structered in levels, numbered between 0 and Levels() - 1.
/// Each level can be seen as a single image, containing the same content
/// as all other levels, but in a different size (width, height).
/// The image size at level i is double the size (width, height) of the image at level i+1.
/// The image at level 0 contains the original size.
/// Precondition: The PGF image has been opened with a call of Open(...).
/// It might throw an IOException.
/// @param level [0, nLevels) The image level of the resulting image in the internal image buffer.
/// @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.
/// @param data Data Pointer to C++ class container to host callback procedure.
void Read(int level = 0, CallbackPtr cb = NULL, void *data = NULL) THROW_;
 
#ifdef __PGFROISUPPORT__
//////////////////////////////////////////////////////////////////////
/// Read a rectangular region of interest of a PGF image at current stream position.
/// The origin of the coordinate axis is the top-left corner of the image.
/// All coordinates are measured in pixels.
/// It might throw an IOException.
/// @param rect [inout] Rectangular region of interest (ROI). The rect might be cropped.
/// @param level [0, nLevels) The image level of the resulting image in the internal image buffer.
/// @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.
/// @param data Data Pointer to C++ class container to host callback procedure.
void Read(PGFRect& rect, int level = 0, CallbackPtr cb = NULL, void *data = NULL) THROW_;
#endif
 
//////////////////////////////////////////////////////////////////////
/// Read and decode smallest level of a PGF image at current stream position.
/// For details, please refert to Read(...)
/// Precondition: The PGF image has been opened with a call of Open(...).
/// It might throw an IOException.
void ReadPreview() THROW_ { Read(Levels() - 1); }
 
//////////////////////////////////////////////////////////////////////
/// After you've written a PGF image, you can call this method followed by GetBitmap/GetYUV
/// to get a quick reconstruction (coded -> decoded image).
/// It might throw an IOException.
/// @param level The image level of the resulting image in the internal image buffer.
void Reconstruct(int level = 0) THROW_;
 
//////////////////////////////////////////////////////////////////////
/// Get image data in interleaved format: (ordering of RGB data is BGR[A])
/// Upsampling, YUV to RGB transform and interleaving are done here to reduce the number
/// of passes over the data.
/// The absolute value of pitch is the number of bytes of an image row of the given image buffer.
/// If pitch is negative, then the image buffer must point to the last row of a bottom-up image (first byte on last row).
/// if pitch is positive, then the image buffer must point to the first row of a top-down image (first byte).
/// 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
/// provide a channelMap of size of expected channels (depending on image mode). For example, PGF provides a channel sequence BGR in RGB color mode.
/// If your provided image buffer expects a channel sequence ARGB, then the channelMap looks like { 3, 2, 1, 0 }.
/// It might throw an IOException.
/// @param pitch The number of bytes of a row of the image buffer.
/// @param buff An image buffer.
/// @param bpp The number of bits per pixel used in image buffer.
/// @param channelMap A integer array containing the mapping of PGF channel ordering to expected channel ordering.
/// @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.
/// @param data Data Pointer to C++ class container to host callback procedure.
void GetBitmap(int pitch, UINT8* buff, BYTE bpp, int channelMap[] = NULL, CallbackPtr cb = NULL, void *data = NULL) const THROW_; // throws IOException
 
//////////////////////////////////////////////////////////////////////
/// Get YUV image data in interleaved format: (ordering is YUV[A])
/// The absolute value of pitch is the number of bytes of an image row of the given image buffer.
/// If pitch is negative, then the image buffer must point to the last row of a bottom-up image (first byte on last row).
/// if pitch is positive, then the image buffer must point to the first row of a top-down image (first byte).
/// 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
/// provide a channelMap of size of expected channels (depending on image mode). For example, PGF provides a channel sequence BGR in RGB color mode.
/// If your provided image buffer expects a channel sequence VUY, then the channelMap looks like { 2, 1, 0 }.
/// It might throw an IOException.
/// @param pitch The number of bytes of a row of the image buffer.
/// @param buff An image buffer.
/// @param bpp The number of bits per pixel used in image buffer.
/// @param channelMap A integer array containing the mapping of PGF channel ordering to expected channel ordering.
/// @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.
/// @param data Data Pointer to C++ class container to host callback procedure.
void GetYUV(int pitch, DataT* buff, BYTE bpp, int channelMap[] = NULL, CallbackPtr cb = NULL, void *data = NULL) const THROW_; // throws IOException
 
//////////////////////////////////////////////////////////////////////
/// Import an image from a specified image buffer.
/// This method is usually called before Write(...) and after SetHeader(...).
/// The absolute value of pitch is the number of bytes of an image row.
/// If pitch is negative, then buff points to the last row of a bottom-up image (first byte on last row).
/// If pitch is positive, then buff points to the first row of a top-down image (first byte).
/// 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
/// provide a channelMap of size of expected channels (depending on image mode). For example, PGF expects in RGB color mode a channel sequence BGR.
/// If your provided image buffer contains a channel sequence ARGB, then the channelMap looks like { 3, 2, 1, 0 }.
/// It might throw an IOException.
/// @param pitch The number of bytes of a row of the image buffer.
/// @param buff An image buffer.
/// @param bpp The number of bits per pixel used in image buffer.
/// @param channelMap A integer array containing the mapping of input channel ordering to expected channel ordering.
/// @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.
/// @param data Data Pointer to C++ class container to host callback procedure.
void ImportBitmap(int pitch, UINT8 *buff, BYTE bpp, int channelMap[] = NULL, CallbackPtr cb = NULL, void *data = NULL) THROW_;
 
//////////////////////////////////////////////////////////////////////
/// Import a YUV image from a specified image buffer.
/// The absolute value of pitch is the number of bytes of an image row.
/// If pitch is negative, then buff points to the last row of a bottom-up image (first byte on last row).
/// If pitch is positive, then buff points to the first row of a top-down image (first byte).
/// 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
/// provide a channelMap of size of expected channels (depending on image mode). For example, PGF expects in RGB color mode a channel sequence BGR.
/// If your provided image buffer contains a channel sequence VUY, then the channelMap looks like { 2, 1, 0 }.
/// It might throw an IOException.
/// @param pitch The number of bytes of a row of the image buffer.
/// @param buff An image buffer.
/// @param bpp The number of bits per pixel used in image buffer.
/// @param channelMap A integer array containing the mapping of input channel ordering to expected channel ordering.
/// @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.
/// @param data Data Pointer to C++ class container to host callback procedure.
void ImportYUV(int pitch, DataT *buff, BYTE bpp, int channelMap[] = NULL, CallbackPtr cb = NULL, void *data = NULL) THROW_;
 
//////////////////////////////////////////////////////////////////////
/// Encode and write a entire PGF image (header and image) at current stream position.
/// A PGF image is structered in levels, numbered between 0 and Levels() - 1.
/// Each level can be seen as a single image, containing the same content
/// as all other levels, but in a different size (width, height).
/// The image size at level i is double the size (width, height) of the image at level i+1.
/// The image at level 0 contains the original size.
/// Precondition: the PGF image contains a valid header (see also SetHeader(...)).
/// It might throw an IOException.
/// @param stream A PGF stream
/// @param nWrittenBytes [in-out] The number of bytes written into stream are added to the input value.
/// @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.
/// @param data Data Pointer to C++ class container to host callback procedure.
void Write(CPGFStream* stream, UINT32* nWrittenBytes = NULL, CallbackPtr cb = NULL, void *data = NULL) THROW_;
 
//////////////////////////////////////////////////////////////////
/// Create wavelet transform channels and encoder. Write header at current stream position.
/// Call this method before your first call of Write(int level) or WriteImage(), but after SetHeader().
/// This method is called inside of Write(stream, ...).
/// It might throw an IOException.
/// @param stream A PGF stream
/// @return The number of bytes written into stream.
UINT32 WriteHeader(CPGFStream* stream) THROW_;
 
//////////////////////////////////////////////////////////////////////
/// Encode and write the one and only image at current stream position.
/// Call this method after WriteHeader(). In case you want to write uncached metadata,
/// then do that after WriteHeader() and before WriteImage().
/// This method is called inside of Write(stream, ...).
/// It might throw an IOException.
/// @param stream A PGF stream
/// @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.
/// @param data Data Pointer to C++ class container to host callback procedure.
/// @return The number of bytes written into stream.
UINT32 WriteImage(CPGFStream* stream, CallbackPtr cb = NULL, void *data = NULL) THROW_;
 
#ifdef __PGFROISUPPORT__
//////////////////////////////////////////////////////////////////
/// Encode and write down to given level at current stream position.
/// A PGF image is structered in levels, numbered between 0 and Levels() - 1.
/// Each level can be seen as a single image, containing the same content
/// as all other levels, but in a different size (width, height).
/// The image size at level i is double the size (width, height) of the image at level i+1.
/// The image at level 0 contains the original size.
/// Preconditions: the PGF image contains a valid header (see also SetHeader(...)) and
/// WriteHeader() has been called before. Levels() > 0.
/// The ROI encoding scheme must be used (see also SetHeader(...)).
/// It might throw an IOException.
/// @param level [0, nLevels) The image level of the resulting image in the internal image buffer.
/// @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.
/// @param data Data Pointer to C++ class container to host callback procedure.
/// @return The number of bytes written into stream.
UINT32 Write(int level, CallbackPtr cb = NULL, void *data = NULL) THROW_;
#endif
 
/////////////////////////////////////////////////////////////////////
/// Configures the encoder.
/// @param useOMP Use parallel threading with Open MP during encoding. Default value: true. Influences the encoding only if the codec has been compiled with OpenMP support.
/// @param favorSpeedOverSize Favors encoding speed over compression ratio. Default value: false
void ConfigureEncoder(bool useOMP = true, bool favorSpeedOverSize = false) { m_useOMPinEncoder = useOMP; m_favorSpeedOverSize = favorSpeedOverSize; }
 
/////////////////////////////////////////////////////////////////////
/// Configures the decoder.
/// @param useOMP Use parallel threading with Open MP during decoding. Default value: true. Influences the decoding only if the codec has been compiled with OpenMP support.
/// @param skipUserData The file might contain user data (metadata). User data ist usually read during Open and stored in memory. Set this flag to false when storing in memory is not needed.
void ConfigureDecoder(bool useOMP = true, bool skipUserData = false) { m_useOMPinDecoder = useOMP; m_skipUserData = skipUserData; }
 
////////////////////////////////////////////////////////////////////
/// Reset stream position to start of PGF pre-header
void ResetStreamPos() THROW_;
 
//////////////////////////////////////////////////////////////////////
/// Set internal PGF image buffer channel.
/// @param channel A YUV data channel
/// @param c A channel index
void SetChannel(DataT* channel, int c = 0) { ASSERT(c >= 0 && c < MaxChannels); m_channel[c] = channel; }
 
//////////////////////////////////////////////////////////////////////
/// Set PGF header and user data.
/// Precondition: The PGF image has been closed with Close(...) or never opened with Open(...).
/// It might throw an IOException.
/// @param header A valid and already filled in PGF header structure
/// @param flags A combination of additional version flags. In case you use level-wise encoding then set flag = PGFROI.
/// @param userData A user-defined memory block containing any kind of cached metadata.
/// @param userDataLength The size of user-defined memory block in bytes
void SetHeader(const PGFHeader& header, BYTE flags = 0, UINT8* userData = 0, UINT32 userDataLength = 0) THROW_; // throws IOException
 
//////////////////////////////////////////////////////////////////////
/// Set maximum intensity value for image modes with more than eight bits per channel.
/// Call this method after SetHeader, but before ImportBitmap.
/// @param maxValue The maximum intensity value.
void SetMaxValue(UINT32 maxValue);
 
//////////////////////////////////////////////////////////////////////
/// Set progress mode used in Read and Write.
/// Default mode is PM_Relative.
/// This method must be called before Open() or SetHeader().
/// PM_Relative: 100% = level difference between current level and target level of Read/Write
/// PM_Absolute: 100% = number of levels
void SetProgressMode(ProgressMode pm) { m_progressMode = pm; }
 
//////////////////////////////////////////////////////////////////////
/// Set refresh callback procedure and its parameter.
/// The refresh callback is called during Read(...) after each level read.
/// @param callback A refresh callback procedure
/// @param arg A parameter of the refresh callback procedure
void SetRefreshCallback(RefreshCB callback, void* arg) { m_cb = callback; m_cbArg = arg; }
 
//////////////////////////////////////////////////////////////////////
/// Sets the red, green, blue (RGB) color values for a range of entries in the palette (clut).
/// It might throw an IOException.
/// @param iFirstColor The color table index of the first entry to set.
/// @param nColors The number of color table entries to set.
/// @param prgbColors A pointer to the array of RGBQUAD structures to set the color table entries.
void SetColorTable(UINT32 iFirstColor, UINT32 nColors, const RGBQUAD* prgbColors) THROW_;
 
//////////////////////////////////////////////////////////////////////
/// Return an internal YUV image channel.
/// @param c A channel index
/// @return An internal YUV image channel
DataT* GetChannel(int c = 0) { ASSERT(c >= 0 && c < MaxChannels); return m_channel[c]; }
 
//////////////////////////////////////////////////////////////////////
/// Retrieves red, green, blue (RGB) color values from a range of entries in the palette of the DIB section.
/// It might throw an IOException.
/// @param iFirstColor The color table index of the first entry to retrieve.
/// @param nColors The number of color table entries to retrieve.
/// @param prgbColors A pointer to the array of RGBQUAD structures to retrieve the color table entries.
void GetColorTable(UINT32 iFirstColor, UINT32 nColors, RGBQUAD* prgbColors) const THROW_;
 
//////////////////////////////////////////////////////////////////////
// Returns address of internal color table
/// @return Address of color table
const RGBQUAD* GetColorTable() const { return m_postHeader.clut; }
 
//////////////////////////////////////////////////////////////////////
/// Return the PGF header structure.
/// @return A PGF header structure
const PGFHeader* GetHeader() const { return &m_header; }
 
//////////////////////////////////////////////////////////////////////
/// Get maximum intensity value for image modes with more than eight bits per channel.
/// Don't call this method before the PGF header has been read.
/// @return The maximum intensity value.
UINT32 GetMaxValue() const { return (1 << m_header.usedBitsPerChannel) - 1; }
 
//////////////////////////////////////////////////////////////////////
/// Return the stream position of the user data or 0.
/// Precondition: The PGF image has been opened with a call of Open(...).
UINT64 GetUserDataPos() const { return m_userDataPos; }
 
//////////////////////////////////////////////////////////////////////
/// Return user data and size of user data.
/// Precondition: The PGF image has been opened with a call of Open(...).
/// @param size [out] Size of user data in bytes.
/// @return A pointer to user data or NULL if there is no user data.
const UINT8* GetUserData(UINT32& size) const;
 
//////////////////////////////////////////////////////////////////////
/// Return the length of all encoded headers in bytes.
/// Precondition: The PGF image has been opened with a call of Open(...).
/// @return The length of all encoded headers in bytes
UINT32 GetEncodedHeaderLength() const;
 
//////////////////////////////////////////////////////////////////////
/// Return the length of an encoded PGF level in bytes.
/// Precondition: The PGF image has been opened with a call of Open(...).
/// @param level The image level
/// @return The length of a PGF level in bytes
UINT32 GetEncodedLevelLength(int level) const { ASSERT(level >= 0 && level < m_header.nLevels); return m_levelLength[m_header.nLevels - level - 1]; }
 
//////////////////////////////////////////////////////////////////////
/// Reads the encoded PGF headers and copies it to a target buffer.
/// Precondition: The PGF image has been opened with a call of Open(...).
/// It might throw an IOException.
/// @param target The target buffer
/// @param targetLen The length of the target buffer in bytes
/// @return The number of bytes copied to the target buffer
UINT32 ReadEncodedHeader(UINT8* target, UINT32 targetLen) const THROW_;
 
//////////////////////////////////////////////////////////////////////
/// Reads the data of an encoded PGF level and copies it to a target buffer
/// without decoding.
/// Precondition: The PGF image has been opened with a call of Open(...).
/// It might throw an IOException.
/// @param level The image level
/// @param target The target buffer
/// @param targetLen The length of the target buffer in bytes
/// @return The number of bytes copied to the target buffer
UINT32 ReadEncodedData(int level, UINT8* target, UINT32 targetLen) const THROW_;
 
//////////////////////////////////////////////////////////////////////
/// Return current image width of given channel in pixels.
/// The returned width depends on the levels read so far and on ROI.
/// @param c A channel index
/// @return Channel width in pixels
UINT32 ChannelWidth(int c = 0) const { ASSERT(c >= 0 && c < MaxChannels); return m_width[c]; }
 
//////////////////////////////////////////////////////////////////////
/// Return current image height of given channel in pixels.
/// The returned height depends on the levels read so far and on ROI.
/// @param c A channel index
/// @return Channel height in pixels
UINT32 ChannelHeight(int c = 0) const { ASSERT(c >= 0 && c < MaxChannels); return m_height[c]; }
 
//////////////////////////////////////////////////////////////////////
/// Return bits per channel of the image's encoder.
/// @return Bits per channel
BYTE ChannelDepth() const { return CurrentChannelDepth(m_preHeader.version); }
 
//////////////////////////////////////////////////////////////////////
/// Return image width of channel 0 at given level in pixels.
/// The returned width is independent of any Read-operations and ROI.
/// @param level A level
/// @return Image level width in pixels
UINT32 Width(int level = 0) const { ASSERT(level >= 0); return LevelWidth(m_header.width, level); }
 
//////////////////////////////////////////////////////////////////////
/// Return image height of channel 0 at given level in pixels.
/// The returned height is independent of any Read-operations and ROI.
/// @param level A level
/// @return Image level height in pixels
UINT32 Height(int level = 0) const { ASSERT(level >= 0); return LevelHeight(m_header.height, level); }
 
//////////////////////////////////////////////////////////////////////
/// Return current image level.
/// Since Read(...) can be used to read each image level separately, it is
/// helpful to know the current level. The current level immediately after Open(...) is Levels().
/// @return Current image level
BYTE Level() const { return (BYTE)m_currentLevel; }
 
//////////////////////////////////////////////////////////////////////
/// Return the number of image levels.
/// @return Number of image levels
BYTE Levels() const { return m_header.nLevels; }
 
//////////////////////////////////////////////////////////////////////
/// Return the PGF quality. The quality is inbetween 0 and MaxQuality.
/// PGF quality 0 means lossless quality.
/// @return PGF quality
BYTE Quality() const { return m_header.quality; }
 
//////////////////////////////////////////////////////////////////////
/// Return the number of image channels.
/// An image of type RGB contains 3 image channels (B, G, R).
/// @return Number of image channels
BYTE Channels() const { return m_header.channels; }
//////////////////////////////////////////////////////////////////////
/// Return the image mode.
/// An image mode is a predefined constant value (see also PGFtypes.h) compatible with Adobe Photoshop.
/// It represents an image type and format.
/// @return Image mode
BYTE Mode() const { return m_header.mode; }
 
//////////////////////////////////////////////////////////////////////
/// Return the number of bits per pixel.
/// Valid values can be 1, 8, 12, 16, 24, 32, 48, 64.
/// @return Number of bits per pixel.
BYTE BPP() const { return m_header.bpp; }
 
//////////////////////////////////////////////////////////////////////
/// Return true if the pgf image supports Region Of Interest (ROI).
/// @return true if the pgf image supports ROI.
bool ROIisSupported() const { return (m_preHeader.version & PGFROI) == PGFROI; }
 
//////////////////////////////////////////////////////////////////////
/// Returns number of used bits per input/output image channel.
/// Precondition: header must be initialized.
/// @return number of used bits per input/output image channel.
BYTE UsedBitsPerChannel() const;
 
//////////////////////////////////////////////////////////////////////
/// Returns images' PGF version
/// @return PGF codec version of the image
BYTE Version() const { return CurrentVersion(m_preHeader.version); }
 
//class methods
 
//////////////////////////////////////////////////////////////////////
/// Check for valid import image mode.
/// @param mode Image mode
/// @return True if an image of given mode can be imported with ImportBitmap(...)
static bool ImportIsSupported(BYTE mode);
 
//////////////////////////////////////////////////////////////////////
/// Compute and return image width at given level.
/// @param width Original image width (at level 0)
/// @param level An image level
/// @return Image level width in pixels
static UINT32 LevelWidth(UINT32 width, int level) { ASSERT(level >= 0); UINT32 w = (width >> level); return ((w << level) == width) ? w : w + 1; }
 
//////////////////////////////////////////////////////////////////////
/// Compute and return image height at given level.
/// @param height Original image height (at level 0)
/// @param level An image level
/// @return Image level height in pixels
static UINT32 LevelHeight(UINT32 height, int level) { ASSERT(level >= 0); UINT32 h = (height >> level); return ((h << level) == height) ? h : h + 1; }
 
//////////////////////////////////////////////////////////////////////
/// Compute and return codec version.
/// @return current PGF codec version
static BYTE CurrentVersion(BYTE version = PGFVersion);
 
//////////////////////////////////////////////////////////////////////
/// Compute and return codec version.
/// @return current PGF codec version
static BYTE CurrentChannelDepth(BYTE version = PGFVersion) { return (version & PGF32) ? 32 : 16; }
 
protected:
CWaveletTransform* m_wtChannel[MaxChannels]; ///< wavelet transformed color channels
DataT* m_channel[MaxChannels]; ///< untransformed channels in YUV format
CDecoder* m_decoder; ///< PGF decoder
CEncoder* m_encoder; ///< PGF encoder
UINT32* m_levelLength; ///< length of each level in bytes; first level starts immediately after this array
UINT32 m_width[MaxChannels]; ///< width of each channel at current level
UINT32 m_height[MaxChannels]; ///< height of each channel at current level
PGFPreHeader m_preHeader; ///< PGF pre-header
PGFHeader m_header; ///< PGF file header
PGFPostHeader m_postHeader; ///< PGF post-header
UINT64 m_userDataPos; ///< stream position of user data
int m_currentLevel; ///< transform level of current image
BYTE m_quant; ///< quantization parameter
bool m_downsample; ///< chrominance channels are downsampled
bool m_favorSpeedOverSize; ///< favor encoding speed over compression ratio
bool m_useOMPinEncoder; ///< use Open MP in encoder
bool m_useOMPinDecoder; ///< use Open MP in decoder
bool m_skipUserData; ///< skip user data (metadata) during open
#ifdef __PGFROISUPPORT__
bool m_streamReinitialized; ///< stream has been reinitialized
PGFRect m_roi; ///< region of interest
#endif
 
private:
RefreshCB m_cb; ///< pointer to refresh callback procedure
void *m_cbArg; ///< refresh callback argument
double m_percent; ///< progress [0..1]
ProgressMode m_progressMode; ///< progress mode used in Read and Write; PM_Relative is default mode
 
void ComputeLevels();
void CompleteHeader();
void RgbToYuv(int pitch, UINT8* rgbBuff, BYTE bpp, int channelMap[], CallbackPtr cb, void *data) THROW_;
void Downsample(int nChannel);
UINT32 UpdatePostHeaderSize() THROW_;
void WriteLevel() THROW_;
 
#ifdef __PGFROISUPPORT__
void SetROI(PGFRect rect);
#endif
 
UINT8 Clamp4(DataT v) const {
if (v & 0xFFFFFFF0) return (v < 0) ? (UINT8)0: (UINT8)15; else return (UINT8)v;
}
UINT16 Clamp6(DataT v) const {
if (v & 0xFFFFFFC0) return (v < 0) ? (UINT16)0: (UINT16)63; else return (UINT16)v;
}
UINT8 Clamp8(DataT v) const {
// needs only one test in the normal case
if (v & 0xFFFFFF00) return (v < 0) ? (UINT8)0 : (UINT8)255; else return (UINT8)v;
}
UINT16 Clamp16(DataT v) const {
if (v & 0xFFFF0000) return (v < 0) ? (UINT16)0: (UINT16)65535; else return (UINT16)v;
}
UINT32 Clamp31(DataT v) const {
return (v < 0) ? 0 : (UINT32)v;
}
};
 
#endif //PGF_PGFIMAGE_H