/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 |
/trunk/Scribus/scribus/third_party/pgf/PG |
---|