diff --git a/offline/pgfutils/.gitignore b/offline/pgfutils/.gitignore new file mode 100644 index 0000000..6e83be0 --- /dev/null +++ b/offline/pgfutils/.gitignore @@ -0,0 +1,2 @@ +build* +CMakeLists.txt.user diff --git a/offline/pgfutils/CMakeLists.txt b/offline/pgfutils/CMakeLists.txt new file mode 100644 index 0000000..e7e4a4f --- /dev/null +++ b/offline/pgfutils/CMakeLists.txt @@ -0,0 +1,26 @@ +# +# Copyright (c) 2010-2019 by Gilles Caulier, +# Copyright (c) 2015 by Veaceslav Munteanu, +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +cmake_minimum_required(VERSION 3.1.0) +set(CMAKE_CXX_STANDARD 14) +project(pgfutils) + +find_package (Qt5 COMPONENTS Core Gui REQUIRED) + +set(libpgfutils_SRCS + pgfutils.cpp + libpgf/Decoder.cpp + libpgf/Encoder.cpp + libpgf/PGFimage.cpp + libpgf/PGFstream.cpp + libpgf/Subband.cpp + libpgf/WaveletTransform.cpp +) + +add_library(pgfutils ${libpgfutils_SRCS}) +target_link_libraries(pgfutils Qt5::Core Qt5::Gui) +target_include_directories(pgfutils PUBLIC libpgf) diff --git a/offline/pgfutils/README b/offline/pgfutils/README new file mode 100644 index 0000000..4fc2006 --- /dev/null +++ b/offline/pgfutils/README @@ -0,0 +1 @@ +ADAPTED FROM DIGIKAM pgfutils @ 33d0457 diff --git a/offline/pgfutils/libpgf/BitStream.h b/offline/pgfutils/libpgf/BitStream.h new file mode 100644 index 0000000..843012e --- /dev/null +++ b/offline/pgfutils/libpgf/BitStream.h @@ -0,0 +1,341 @@ +/* + * 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)) + +/* +static UINT8 lMask[] = { + 0x00, // 00000000 + 0x80, // 10000000 + 0xc0, // 11000000 + 0xe0, // 11100000 + 0xf0, // 11110000 + 0xf8, // 11111000 + 0xfc, // 11111100 + 0xfe, // 11111110 + 0xff, // 11111111 +}; +*/ +// 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; +} +/* +////////////////////////////////////////////////////////////////////// +/// BitCopy: copies k bits from source to destination +/// Note: only 8 bits are copied at a time, if speed is an issue, a more +/// complicated but faster 64 bit algorithm should be used. +inline void BitCopy(const UINT8 *sStream, UINT32 sPos, UINT8 *dStream, UINT32 dPos, UINT32 k) { + ASSERT(k > 0); + + div_t divS = div(sPos, 8); + div_t divD = div(dPos, 8); + UINT32 sOff = divS.rem; + UINT32 dOff = divD.rem; + INT32 tmp = div(dPos + k - 1, 8).quot; + + const UINT8 *sAddr = sStream + divS.quot; + UINT8 *dAddrS = dStream + divD.quot; + UINT8 *dAddrE = dStream + tmp; + UINT8 eMask; + + UINT8 destSB = *dAddrS; + UINT8 destEB = *dAddrE; + UINT8 *dAddr; + UINT8 prec; + INT32 shiftl, shiftr; + + if (dOff > sOff) { + prec = 0; + shiftr = dOff - sOff; + shiftl = 8 - dOff + sOff; + } else { + prec = *sAddr << (sOff - dOff); + shiftr = 8 - sOff + dOff; + shiftl = sOff - dOff; + sAddr++; + } + + for (dAddr = dAddrS; dAddr < dAddrE; dAddr++, sAddr++) { + *dAddr = prec | (*sAddr >> shiftr); + prec = *sAddr << shiftl; + } + + if ((sPos + k)%8 == 0) { + *dAddr = prec; + } else { + *dAddr = prec | (*sAddr >> shiftr); + } + + eMask = lMask[dOff]; + *dAddrS = (destSB & eMask) | (*dAddrS & (~eMask)); + + INT32 mind = (dPos + k) % 8; + eMask = (mind) ? lMask[mind] : lMask[8]; + *dAddrE = (destEB & (~eMask)) | (*dAddrE & eMask); +} +*/ +////////////////////////////////////////////////////////////////////// +/// 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 diff --git a/offline/pgfutils/libpgf/Decoder.cpp b/offline/pgfutils/libpgf/Decoder.cpp new file mode 100644 index 0000000..34078b0 --- /dev/null +++ b/offline/pgfutils/libpgf/Decoder.cpp @@ -0,0 +1,1055 @@ +/* + * 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 +#endif + +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wextra" +#endif + +#if defined(__APPLE__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wextra" +#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 userDataPolicy Policy of user data (meta-data) handling while reading PGF headers. +CDecoder::CDecoder(CPGFStream* stream, PGFPreHeader& preHeader, PGFHeader& header, + PGFPostHeader& postHeader, UINT32*& levelLength, UINT64& userDataPos, + bool useOMP, UINT32 userDataPolicy) +: 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; + + // 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 + + UINT32 size = preHeader.hSize; + + if (size > HeaderSize) { + size -= HeaderSize; + count = 0; + + // read post-header + if (header.mode == ImageModeIndexedColor) { + if (size < ColorTableSize) ReturnWithError(FormatCannotRead); + // read color table + count = expected = ColorTableSize; + m_stream->Read(&count, postHeader.clut); + if (count != expected) ReturnWithError(MissingData); + } + + if (size > (UINT32)count) { + size -= count; + + // read/skip user data + UserdataPolicy policy = (UserdataPolicy)((userDataPolicy <= MaxUserDataSize) ? UP_CachePrefix : 0xFFFFFFFF - userDataPolicy); + userDataPos = m_stream->GetPos(); + postHeader.userDataLen = size; + + if (policy == UP_Skip) { + postHeader.cachedUserDataLen = 0; + postHeader.userData = nullptr; + Skip(size); + } else { + postHeader.cachedUserDataLen = (policy == UP_CachePrefix) ? __min(size, userDataPolicy) : size; + + // create user data memory block + postHeader.userData = new(std::nothrow) UINT8[postHeader.cachedUserDataLen]; + if (!postHeader.userData) ReturnWithError(InsufficientMemory); + + // read user data + count = expected = postHeader.cachedUserDataLen; + m_stream->Read(&count, postHeader.userData); + if (count != expected) ReturnWithError(MissingData); + + // skip remaining user data + if (postHeader.cachedUserDataLen < size) Skip(size - postHeader.cachedUserDataLen); + } + } + } + + // 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); + + // 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(std::nothrow) CMacroBlock(); + if (!m_currentBlock) ReturnWithError(InsufficientMemory); + } +} + +///////////////////////////////////////////////////////////////////// +// 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 { + 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) { + 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; + } +} + +//////////////////////////////////////////////////////////////////// +// Decodes and dequantizes 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) { + 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++; + } + } +} + +//////////////////////////////////////////////////////////////////// +/// Skips a given number of bytes in the open stream. +/// It might throw an IOException. +void CDecoder::Skip(UINT64 offset) { + 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) { + ASSERT(m_currentBlock); + + if (m_currentBlock->IsCompletelyRead()) { + // all data of current macro block has been read --> prepare next macro block + GetNextMacroBlock(); + } + + band->SetData(bandPos, m_currentBlock->m_value[m_currentBlock->m_valuePos] << quantParam); + m_currentBlock->m_valuePos++; +} + +////////////////////////////////////////////////////////////////////// +// Gets next macro block +// It might throw an IOException. +void CDecoder::GetNextMacroBlock() { + // 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); +} + +////////////////////////////////////////////////////////////////////// +// Reads next block(s) from stream and decodes them +// Decoding scheme: (16 bits) [ ROI ] data +// ROI ::= (15 bits) (1 bit) +// It might throw an IOException. +void CDecoder::DecodeBuffer() { + 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 || ex.error == FormatCannotRead) { + break; // no further data available or the data isn't valid PGF data (might occur in streaming or PPPExt) + } 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]; + } +} + +////////////////////////////////////////////////////////////////////// +// Reads next block from stream and stores it in the given macro block +// It might throw an IOException. +void CDecoder::ReadMacroBlock(CMacroBlock* block) { + 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); // convert wordLen + if (wordLen > BufferSize) ReturnWithError(FormatCannotRead); + +#ifdef __PGFROISUPPORT__ + // read ROIBlockHeader + if (m_roi) { + count = expected = sizeof(ROIBlockHeader); + m_stream->Read(&count, &h.val); + if (count != expected) ReturnWithError(MissingData); + h.val = __VAL(h.val); // convert ROIBlockHeader + } +#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 +} + +#ifdef __PGFROISUPPORT__ +////////////////////////////////////////////////////////////////////// +// Resets stream position to next tile. +// Used with ROI encoding scheme only. +// Reads several next blocks from stream but doesn't decode them into macro blocks +// Encoding scheme: (16 bits) ROI data +// ROI ::= (15 bits) (1 bit) +// It might throw an IOException. +void CDecoder::SkipTileBuffer() { + ASSERT(m_roi); + + // current macro block belongs to the last tile, so go to the next macro block + m_macroBlocksAvailable--; + m_currentBlockIndex++; + + // check if pre-decoded data is available + while (m_macroBlocksAvailable > 0 && !m_macroBlocks[m_currentBlockIndex]->m_header.rbh.tileEnd) { + m_macroBlocksAvailable--; + m_currentBlockIndex++; + } + if (m_macroBlocksAvailable > 0) { + // set new current macro block + m_currentBlock = m_macroBlocks[m_currentBlockIndex]; + ASSERT(m_currentBlock->m_header.rbh.tileEnd); + return; + } + + ASSERT(m_macroBlocksAvailable <= 0); + m_macroBlocksAvailable = 0; + UINT16 wordLen; + ROIBlockHeader h(0); + int count, expected; + + // skips all blocks until tile end + do { + // read wordLen + count = expected = sizeof(wordLen); + m_stream->Read(&count, &wordLen); + if (count != expected) ReturnWithError(MissingData); + wordLen = __VAL(wordLen); // convert wordLen + if (wordLen > BufferSize) ReturnWithError(FormatCannotRead); + + // read ROIBlockHeader + count = expected = sizeof(ROIBlockHeader); + m_stream->Read(&count, &h.val); + if (count != expected) ReturnWithError(MissingData); + h.val = __VAL(h.val); // convert ROIBlockHeader + + // skip data + m_stream->SetPos(FSFromCurrent, wordLen*WordBytes); + } while (!h.rbh.tileEnd); +} +#endif + +////////////////////////////////////////////////////////////////////// +// Decodes macro 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 ::= (5 bits) foreach(plane i): Plane[i] +// Plane[i] ::= [ Sig1 | Sig2 ] [DWORD alignment] refBits +// Sig1 ::= 1 (15 bits) codedSigAndSignBits +// Sig2 ::= 0 (15 bits) [Sign1 | Sign2 ] [DWORD alignment] sigBits +// Sign1 ::= 1 (15 bits) codedSignBits +// Sign2 ::= 0 (15 bits) [DWORD alignment] signBits +void CDecoder::CMacroBlock::BitplaneDecode() { + UINT32 bufferSize = m_header.rbh.bufferSize; ASSERT(bufferSize <= BufferSize); + + // 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 + // + UINT32 nPlanes = GetValueBlock(m_codeBuffer, 0, MaxBitPlanesLog); + UINT32 codePos = MaxBitPlanesLog; + + // loop through all bit planes + if (nPlanes == 0) nPlanes = MaxBitPlanes + 1; + ASSERT(0 < nPlanes && nPlanes <= MaxBitPlanes + 1); + DataT planeMask = 1 << (nPlanes - 1); + + for (int plane = nPlanes - 1; plane >= 0; plane--) { + UINT32 sigLen = 0; + + // read RL code + if (GetBit(m_codeBuffer, codePos)) { + // RL coding of sigBits is used + // <1>_ + codePos++; + + // read codeLen + UINT32 codeLen = GetValueBlock(m_codeBuffer, codePos, RLblockSizeLen); ASSERT(codeLen <= MaxCodeLen); + + // position of encoded sigBits and signBits + UINT32 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> + 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>__ + codePos++; + + // read codeLen + UINT32 codeLen = GetValueBlock(m_codeBuffer, codePos, RLblockSizeLen); ASSERT(codeLen <= MaxCodeLen); + + // sign bits + UINT32 signPos = codePos + RLblockSizeLen; ASSERT(signPos < CodeBufferBitLen); + + // significant bits + UINT32 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>___ + codePos++; + + // read signLen + UINT32 signLen = GetValueBlock(m_codeBuffer, codePos, RLblockSizeLen); ASSERT(signLen <= MaxCodeLen); + + // sign bits + UINT32 signPos = AlignWordPos(codePos + RLblockSizeLen); ASSERT(signPos < CodeBufferBitLen); + + // significant bits + UINT32 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; +} + +//////////////////////////////////////////////////////////////////// +// Reconstructs 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, sigPos = 0; + + while (valPos < bufferSize) { + // search next 1 in m_sigFlagVector using searching with sentinel + UINT32 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 + UINT32 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; +} + +//////////////////////////////////////////////////////////////////// +// Reconstructs 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: 1x +// - 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; +} + +//////////////////////////////////////////////////////////////////// +// Reconstructs 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 +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]); + //} +} + +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + +#if defined(__APPLE__) +#pragma clang diagnostic pop +#endif + +#endif //TRACE diff --git a/offline/pgfutils/libpgf/Decoder.h b/offline/pgfutils/libpgf/Decoder.h new file mode 100644 index 0000000..8a96c44 --- /dev/null +++ b/offline/pgfutils/libpgf/Decoder.h @@ -0,0 +1,218 @@ +/* + * 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. + CMacroBlock() + : m_header(0) // makes sure that IsCompletelyRead() returns true for an empty macro block +#ifdef _MSC_VER +#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 userDataPolicy Policy of user data (meta-data) handling while reading PGF headers. + CDecoder(CPGFStream* stream, PGFPreHeader& preHeader, PGFHeader& header, + PGFPostHeader& postHeader, UINT32*& levelLength, UINT64& userDataPos, + bool useOMP, UINT32 userDataPolicy); // 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); + + ///////////////////////////////////////////////////////////////////// + /// 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); + + ////////////////////////////////////////////////////////////////////// + /// Returns the length of all encoded headers in bytes. + /// @return The length of all encoded headers in bytes + UINT32 GetEncodedHeaderLength() const { return m_encodedHeaderLength; } + + //////////////////////////////////////////////////////////////////// + /// Resets stream position to beginning of PGF pre-header + void SetStreamPosToStart() { ASSERT(m_stream); m_stream->SetPos(FSFromStart, m_startPos); } + + //////////////////////////////////////////////////////////////////// + /// Resets stream position to beginning of data block + void SetStreamPosToData() { ASSERT(m_stream); m_stream->SetPos(FSFromStart, m_startPos + m_encodedHeaderLength); } + + //////////////////////////////////////////////////////////////////// + /// Skips a given number of bytes in the open stream. + /// It might throw an IOException. + void Skip(UINT64 offset); + + ///////////////////////////////////////////////////////////////////// + /// 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); + + ////////////////////////////////////////////////////////////////////// + /// 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; + + ///////////////////////////////////////////////////////////////////// + /// Reads next block(s) from stream and decodes them + /// It might throw an IOException. + void DecodeBuffer(); + + ///////////////////////////////////////////////////////////////////// + /// @return Stream + CPGFStream* GetStream() { return m_stream; } + + ///////////////////////////////////////////////////////////////////// + /// Gets next macro block + /// It might throw an IOException. + void GetNextMacroBlock(); + +#ifdef __PGFROISUPPORT__ + ///////////////////////////////////////////////////////////////////// + /// Resets stream position to next tile. + /// Used with ROI encoding scheme only. + /// It might throw an IOException. + void SkipTileBuffer(); + + ///////////////////////////////////////////////////////////////////// + /// Enables region of interest (ROI) status. + void SetROI() { m_roi = true; } +#endif + +#ifdef TRACE + void DumpBuffer(); +#endif + +private: + void ReadMacroBlock(CMacroBlock* block); ///< 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 diff --git a/offline/pgfutils/libpgf/Encoder.cpp b/offline/pgfutils/libpgf/Encoder.cpp new file mode 100644 index 0000000..a30c482 --- /dev/null +++ b/offline/pgfutils/libpgf/Encoder.cpp @@ -0,0 +1,830 @@ +/* + * 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 +#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) +: 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 = nullptr; + + // 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) { + UINT64 curPos = m_stream->GetPos(); // end of user data + int count = PreHeaderSize; + + // write preHeader + SetStreamPosToStart(); + 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) { + // 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() { + 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) { + 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() { + 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) { + 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: (16 bits) [ ROI ] data +// ROI ::= (15 bits) (1 bit) +// It might throw an IOException. +void CEncoder::EncodeBuffer(ROIBlockHeader h) { + 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) { + 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) { + count = sizeof(ROIBlockHeader); + h.val = __VAL(h.val); + m_stream->Write(&count, &h.val); ASSERT(count == sizeof(ROIBlockHeader)); + } +#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) { + count = sizeof(ROIBlockHeader); + m_stream->Write(&count, &h.val); ASSERT(count == sizeof(ROIBlockHeader)); + } +#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 ::= (5 bits) foreach(plane i): Plane[i] +// Plane[i] ::= [ Sig1 | Sig2 ] [DWORD alignment] refBits +// Sig1 ::= 1 (15 bits) codedSigAndSignBits +// Sig2 ::= 0 (15 bits) [Sign1 | Sign2 ] [DWORD alignment] sigBits +// Sign1 ::= 1 (15 bits) codedSignBits +// Sign2 ::= 0 (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 + // + 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> + 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> + 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>_ + // 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>__ + // 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 + // _ + 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) + // _ + 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: 1x +// - 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(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(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. +// - 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 + + diff --git a/offline/pgfutils/libpgf/Encoder.h b/offline/pgfutils/libpgf/Encoder.h new file mode 100644 index 0000000..7d57afa --- /dev/null +++ b/offline/pgfutils/libpgf/Encoder.h @@ -0,0 +1,235 @@ +/* + * 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) +#ifdef _MSC_VER +#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); // 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(); + + ///////////////////////////////////////////////////////////////////// + /// 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); + + ///////////////////////////////////////////////////////////////////// + /// 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); + + ///////////////////////////////////////////////////////////////////// + /// Write new levelLength into stream. + /// It might throw an IOException. + /// @return Written image bytes. + UINT32 UpdateLevelLength(); + + ///////////////////////////////////////////////////////////////////// + /// 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); + + ///////////////////////////////////////////////////////////////////// + /// 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); + + ///////////////////////////////////////////////////////////////////// + /// 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; } + + //////////////////////////////////////////////////////////////////// + /// Resets stream position to beginning of PGF pre-header + void SetStreamPosToStart() { ASSERT(m_stream); m_stream->SetPos(FSFromStart, m_startPosition); } + + ///////////////////////////////////////////////////////////////////// + /// 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() { 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); // throws IOException + void WriteMacroBlock(CMacroBlock* block); // 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 diff --git a/offline/pgfutils/libpgf/PGFimage.cpp b/offline/pgfutils/libpgf/PGFimage.cpp new file mode 100644 index 0000000..039d4d8 --- /dev/null +++ b/offline/pgfutils/libpgf/PGFimage.cpp @@ -0,0 +1,2732 @@ +/* + * 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 "BitStream.h" +#include +#include + +#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 + +#ifdef _DEBUG + // allows RGB and RGBA image visualization inside Visual Studio Debugger + struct DebugBGRImage { + int width, height, pitch; + BYTE *data; + } roiimage; +#endif + +////////////////////////////////////////////////////////////////////// +// Standard constructor +CPGFImage::CPGFImage() { + Init(); +} + +////////////////////////////////////////////////////////////////////// +void CPGFImage::Init() { + // init pointers + m_decoder = nullptr; + m_encoder = nullptr; + m_levelLength = nullptr; + + // init members +#ifdef __PGFROISUPPORT__ + m_streamReinitialized = false; +#endif + m_currentLevel = 0; + m_quant = 0; + m_userDataPos = 0; + m_downsample = false; + m_favorSpeedOverSize = false; + m_useOMPinEncoder = true; + m_useOMPinDecoder = true; + m_cb = nullptr; + m_cbArg = nullptr; + m_progressMode = PM_Relative; + m_percent = 0; + m_userDataPolicy = UP_CacheAll; + + // init preHeader + memcpy(m_preHeader.magic, PGFMagic, 3); + m_preHeader.version = PGFVersion; + m_preHeader.hSize = 0; + + // init postHeader + m_postHeader.userData = nullptr; + m_postHeader.userDataLen = 0; + m_postHeader.cachedUserDataLen = 0; + + // init channels + for (int i = 0; i < MaxChannels; i++) { + m_channel[i] = nullptr; + m_wtChannel[i] = nullptr; + } + + // set image width and height + for (int i = 0; i < MaxChannels; i++) { + m_width[0] = 0; + m_height[0] = 0; + } +} + +////////////////////////////////////////////////////////////////////// +// Destructor: Destroy internal data structures. +CPGFImage::~CPGFImage() { + m_currentLevel = -100; // unusual value used as marker in Destroy() + Destroy(); +} + +////////////////////////////////////////////////////////////////////// +// Destroy internal data structures. Object state after this is the same as after CPGFImage(). +void CPGFImage::Destroy() { + for (int i = 0; i < m_header.channels; i++) { + delete m_wtChannel[i]; // also deletes m_channel + } + delete[] m_postHeader.userData; + delete[] m_levelLength; + delete m_decoder; + delete m_encoder; + + if (m_currentLevel != -100) Init(); +} + +///////////////////////////////////////////////////////////////////////////// +// 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) { + 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_userDataPolicy); + + 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 + if (!CompleteHeader()) ReturnWithError(FormatCannotRead); + + // 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) >> 1; + m_height[i] = (m_height[0] + 1) >> 1; + } + } 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); + } + } + } +} + +//////////////////////////////////////////////////////////// +bool CPGFImage::CompleteHeader() { + // set current codec version + m_header.version = PGFVersionNumber(PGFMajorNumber, PGFYear, PGFWeek); + + 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; + } + if (m_header.mode == ImageModeBitmap && m_header.bpp != 1) return false; + if (m_header.mode == ImageModeIndexedColor && m_header.bpp != 8) return false; + if (m_header.mode == ImageModeGrayScale && m_header.bpp != 8) return false; + if (m_header.mode == ImageModeGray16 && m_header.bpp != 16) return false; + if (m_header.mode == ImageModeGray32 && m_header.bpp != 32) return false; + if (m_header.mode == ImageModeRGBColor && m_header.bpp != 24) return false; + if (m_header.mode == ImageModeRGBA && m_header.bpp != 32) return false; + if (m_header.mode == ImageModeRGB12 && m_header.bpp != 12) return false; + if (m_header.mode == ImageModeRGB16 && m_header.bpp != 16) return false; + if (m_header.mode == ImageModeRGB48 && m_header.bpp != 48) return false; + if (m_header.mode == ImageModeLabColor && m_header.bpp != 24) return false; + if (m_header.mode == ImageModeLab48 && m_header.bpp != 48) return false; + if (m_header.mode == ImageModeCMYKColor && m_header.bpp != 32) return false; + if (m_header.mode == ImageModeCMYK64 && m_header.bpp != 64) return false; + + // 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: + return false; + } + } + + // 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 true; +} + +////////////////////////////////////////////////////////////////////// +/// Return user data and size of user data. +/// Precondition: The PGF image has been opened with a call of Open(...). +/// In an encoder scenario don't call this method before WriteHeader(). +/// @param cachedSize [out] Size of returned user data in bytes. +/// @param pTotalSize [optional out] Pointer to return the size of user data stored in image header in bytes. +/// @return A pointer to user data or nullptr if there is no user data available. +const UINT8* CPGFImage::GetUserData(UINT32& cachedSize, UINT32* pTotalSize /*= nullptr*/) const { + cachedSize = m_postHeader.cachedUserDataLen; + if (pTotalSize) *pTotalSize = 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*/) { + 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; + + #ifdef __PGFROISUPPORT__ + if (ROIisSupported()) { + // enable ROI reading + SetROI(PGFRect(0, 0, m_header.width, m_header.height)); + } + #endif + + 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 /*= nullptr*/, void *data /*=nullptr*/) { + 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++) { + CWaveletTransform* wtChannel = m_wtChannel[i]; + ASSERT(wtChannel); + + // decode file and write stream to m_wtChannel + if (m_currentLevel == m_header.nLevels) { + // last level also has LL band + wtChannel->GetSubband(m_currentLevel, LL)->PlaceTile(*m_decoder, m_quant); + } + if (m_preHeader.version & Version5) { + // since version 5 + wtChannel->GetSubband(m_currentLevel, HL)->PlaceTile(*m_decoder, m_quant); + wtChannel->GetSubband(m_currentLevel, LH)->PlaceTile(*m_decoder, m_quant); + } else { + // until version 4 + m_decoder->DecodeInterleaved(wtChannel, m_currentLevel, m_quant); + } + wtChannel->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); + } + } + } +} + +#ifdef __PGFROISUPPORT__ +////////////////////////////////////////////////////////////////////// +/// Read and decode rectangular region of interest (ROI) 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) at level 0. 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 /*= nullptr*/, void *data /*=nullptr*/) { + 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); + + // 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; + + 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(); + } + + // enable ROI decoding and reading + SetROI(rect); + + while (m_currentLevel > level) { + for (int i=0; i < m_header.channels; i++) { + CWaveletTransform* wtChannel = m_wtChannel[i]; + ASSERT(wtChannel); + + // get number of tiles and tile indices + const UINT32 nTiles = wtChannel->GetNofTiles(m_currentLevel); // independent of ROI + + // 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->GetNextMacroBlock(); + wtChannel->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 (wtChannel->TileIsRelevant(m_currentLevel, tileX, tileY)) { + m_decoder->GetNextMacroBlock(); + wtChannel->GetSubband(m_currentLevel, HL)->PlaceTile(*m_decoder, m_quant, true, tileX, tileY); + wtChannel->GetSubband(m_currentLevel, LH)->PlaceTile(*m_decoder, m_quant, true, tileX, tileY); + wtChannel->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); + } + } + } +} + +////////////////////////////////////////////////////////////////////// +/// Return ROI of channel 0 at current level in pixels. +/// The returned rect is only valid after reading a ROI. +/// @return ROI in pixels +PGFRect CPGFImage::ComputeLevelROI() const { + if (m_currentLevel == 0) { + return m_roi; + } else { + const UINT32 rLeft = LevelSizeL(m_roi.left, m_currentLevel); + const UINT32 rRight = LevelSizeL(m_roi.right, m_currentLevel); + const UINT32 rTop = LevelSizeL(m_roi.top, m_currentLevel); + const UINT32 rBottom = LevelSizeL(m_roi.bottom, m_currentLevel); + return PGFRect(rLeft, rTop, rRight - rLeft, rBottom - rTop); + } +} + +////////////////////////////////////////////////////////////////////// +/// Returns aligned ROI in pixels of current level of channel c +/// @param c A channel index +PGFRect CPGFImage::GetAlignedROI(int c /*= 0*/) const { + PGFRect roi(0, 0, m_width[c], m_height[c]); + + if (ROIisSupported()) { + ASSERT(m_wtChannel[c]); + + roi = m_wtChannel[c]->GetAlignedROI(m_currentLevel); + } + ASSERT(roi.Width() == m_width[c]); + ASSERT(roi.Height() == m_height[c]); + return roi; +} + +////////////////////////////////////////////////////////////////////// +/// Compute ROIs for each channel and each level <= current level +/// Called inside of Read(rect, ...). +/// @param rect rectangular region of interest (ROI) at level 0 +void CPGFImage::SetROI(PGFRect rect) { + ASSERT(m_decoder); + ASSERT(ROIisSupported()); + ASSERT(m_wtChannel[0]); + + // store ROI for a later call of GetBitmap + m_roi = rect; + + // enable ROI decoding + m_decoder->SetROI(); + + // prepare wavelet channels for using ROI + 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 = (rect.right + 1) >> 1; + rect.bottom = (rect.bottom + 1) >> 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 header 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 { + 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 or start of data. Must not be called before Open() or before Write(). +/// Use this method after Read() if you want to read the same image several times, e.g. reading different ROIs. +/// @param startOfData true: you want to read the same image several times. false: resets stream position to the initial position +void CPGFImage::ResetStreamPos(bool startOfData) { + if (startOfData) { + ASSERT(m_decoder); + m_decoder->SetStreamPosToData(); + } else { + if (m_decoder) { + m_decoder->SetStreamPosToStart(); + } else if (m_encoder) { + m_encoder->SetStreamPosToStart(); + } else { + ASSERT(false); + } + } +} + +////////////////////////////////////////////////////////////////////// +/// 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 { + 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 major version +BYTE CPGFImage::CodecMajorVersion(BYTE version) { + if (version & Version7) return 7; + 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[] /*= nullptr */, CallbackPtr cb /*= nullptr*/, void *data /*=nullptr*/) { + 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 +// Called before Write() +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*FilterSize; + 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 >>= 1; + } + } + + int levels = m_header.nLevels; // we need a signed value during level reduction + + // reduce number of levels if the image size is smaller than FilterSize*(2^levels) + s = FilterSize*(1 << levels); // must be at least the double filter size because of subsampling + while (m < s) { + levels--; + s >>= 1; + } + 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 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*/, const UINT8* userData /*= 0*/, UINT32 userDataLength /*= 0*/) { + ASSERT(!m_decoder); // current image must be closed + ASSERT(header.quality <= MaxQuality); + ASSERT(userDataLength <= MaxUserDataSize); + + // 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); + + // check quality + if (m_header.quality > MaxQuality) m_header.quality = MaxQuality; + + // 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) { + if (userDataLength > MaxUserDataSize) userDataLength = MaxUserDataSize; + m_postHeader.userData = new(std::nothrow) UINT8[userDataLength]; + if (!m_postHeader.userData) ReturnWithError(InsufficientMemory); + m_postHeader.userDataLen = m_postHeader.cachedUserDataLen = 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. +/// Performs forward FWT. +/// 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) { + 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 = nullptr; + 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] = nullptr; + } 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, write headers and user data, but not level-length area + 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, write headers and user data, but not level-length area + 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() { + 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(); // encode macro block with tile-end = true + } + for (UINT32 tileY=0; tileY < nTiles; tileY++) { + for (UINT32 tileX=0; tileX < nTiles; tileX++) { + // extract tile to macro block and encode already filled macro blocks with tile-end = false + 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 EncodeTileBuffer will write the last piece of data of the current level. + m_encoder->SetEncodedLevel(--m_currentLevel); + } + m_encoder->EncodeTileBuffer(); // encode last macro block with tile-end = true + } + } + } + } 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() { + 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 an 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 /*= nullptr*/, void *data /*= nullptr*/) { + 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) { + // for very small images: write channels uncoded + 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 = nullptr; + + ASSERT(!m_encoder); + + return nWrittenBytes; +} + +////////////////////////////////////////////////////////////////// +/// Encode and write an 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 /*= nullptr*/, CallbackPtr cb /*= nullptr*/, void *data /*=nullptr*/) { + 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. +// 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 /*= nullptr*/, void *data /*=nullptr*/) { + 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 = nullptr; + } + + 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 { + 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) { + 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 /*=nullptr*/) { + ASSERT(buff); + UINT32 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 == nullptr) 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); + + // new unpacked version since version 7 + for (UINT32 h = 0; h < m_header.height; h++) { + if (cb) { + if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed); + percent += dP; + } + cnt = 0; + for (UINT32 j = 0; j < w2; j++) { + UINT8 byte = buff[j]; + for (int k = 0; k < 8; k++) { + UINT8 bit = (byte & 0x80) >> 7; + if (cnt < w) y[yPos++] = bit; + byte <<= 1; + cnt++; + } + } + buff += pitch; + } + /* old version: packed values: 8 pixels in 1 byte + 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; + } + // version 5 and 6 + // for (UINT32 j = w2; j < w; j++) { + // y[yPos++] = 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 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[] /*= nullptr */, CallbackPtr cb /*= nullptr*/, void *data /*=nullptr*/) const { + ASSERT(buff); + UINT32 w = m_width[0]; // width of decoded image + UINT32 h = m_height[0]; // height of decoded image + UINT32 yw = w; // y-channel width + UINT32 uw = m_width[1]; // u-channel width + UINT32 roiOffsetX = 0; + UINT32 roiOffsetY = 0; + UINT32 yOffset = 0; + UINT32 uOffset = 0; + +#ifdef __PGFROISUPPORT__ + const PGFRect& roi = GetAlignedROI(); // in pixels, roi is usually larger than levelRoi + ASSERT(w == roi.Width() && h == roi.Height()); + const PGFRect levelRoi = ComputeLevelROI(); + 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 + w = levelRoi.Width(); + h = levelRoi.Height(); + roiOffsetX = levelRoi.left - roi.left; + roiOffsetY = levelRoi.top - roi.top; + yOffset = roiOffsetX + roiOffsetY*yw; + + if (m_downsample) { + const PGFRect& downsampledRoi = GetAlignedROI(1); + uOffset = levelRoi.left/2 - downsampledRoi.left + (levelRoi.top/2 - downsampledRoi.top)*m_width[1]; + } else { + uOffset = yOffset; + } + } +#endif + + 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 == nullptr) channelMap = defMap; + 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); + + if (m_preHeader.version & Version7) { + // new unpacked version has a little better compression ratio + // since version 7 + for (i = 0; i < h; i++) { + UINT32 cnt = 0; + for (j = 0; j < w2; j++) { + UINT8 byte = 0; + for (int k = 0; k < 8; k++) { + byte <<= 1; + UINT8 bit = 0; + if (cnt < w) { + bit = y[yOffset + cnt] & 1; + } + byte |= bit; + cnt++; + } + buff[j] = byte; + } + yOffset += yw; + buff += pitch; + + if (cb) { + percent += dP; + if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed); + } + } + } else { + // old versions + // packed pixels: 8 pixel in 1 byte of channel[0] + if (!(m_preHeader.version & Version5)) yw = w2; // not version 5 or 6 + yOffset = roiOffsetX/8 + roiOffsetY*yw; // 1 byte in y contains 8 pixel values + for (i = 0; i < h; i++) { + for (j = 0; j < w2; j++) { + buff[j] = Clamp8(y[yOffset + j] + YUVoffset8); + } + yOffset += yw; + 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); + + UINT32 cnt, channels = bpp/8; ASSERT(channels >= m_header.channels); + + for (i=0; i < h; i++) { + UINT32 yPos = yOffset; + cnt = 0; + for (j=0; j < w; j++) { + for (UINT32 c=0; c < m_header.channels; c++) { + buff[cnt + channelMap[c]] = Clamp8(m_channel[c][yPos] + YUVoffset8); + } + cnt += channels; + yPos++; + } + yOffset += yw; + 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); + UINT32 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++) { + UINT32 yPos = yOffset; + cnt = 0; + for (j=0; j < w; j++) { + for (UINT32 c=0; c < m_header.channels; c++) { + buff16[cnt + channelMap[c]] = Clamp16((m_channel[c][yPos] + yuvOffset16) << shift); + } + cnt += channels; + yPos++; + } + yOffset += yw; + 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++) { + UINT32 yPos = yOffset; + cnt = 0; + for (j=0; j < w; j++) { + for (UINT32 c=0; c < m_header.channels; c++) { + buff[cnt + channelMap[c]] = Clamp8((m_channel[c][yPos] + yuvOffset16) >> shift); + } + cnt += channels; + yPos++; + } + yOffset += yw; + 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; + UINT32 cnt, channels = bpp/8; + + if (m_downsample) { + for (i=0; i < h; i++) { + UINT32 uPos = uOffset; + UINT32 yPos = yOffset; + cnt = 0; + for (j=0; j < w; j++) { + // u and v are downsampled + uAvg = u[uPos]; + vAvg = v[uPos]; + // 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); + cnt += channels; + if (j & 1) uPos++; + yPos++; + } + if (i & 1) uOffset += uw; + yOffset += yw; + buffb += pitch; + buffg += pitch; + buffr += pitch; + + if (cb) { + percent += dP; + if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed); + } + } + + } else { + for (i=0; i < h; i++) { + cnt = 0; + UINT32 yPos = yOffset; + 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); + cnt += channels; + yPos++; + } + yOffset += yw; + 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); + UINT32 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++) { + UINT32 uPos = uOffset; + UINT32 yPos = yOffset; + cnt = 0; + for (j=0; j < w; j++) { + uAvg = u[uPos]; + vAvg = v[uPos]; + // 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); + cnt += channels; + if (!m_downsample || (j & 1)) uPos++; + yPos++; + } + if (!m_downsample || (i & 1)) uOffset += uw; + yOffset += yw; + 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++) { + UINT32 uPos = uOffset; + UINT32 yPos = yOffset; + cnt = 0; + for (j=0; j < w; j++) { + uAvg = u[uPos]; + vAvg = v[uPos]; + // 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); + cnt += channels; + if (!m_downsample || (j & 1)) uPos++; + yPos++; + } + if (!m_downsample || (i & 1)) uOffset += uw; + yOffset += yw; + buff += pitch; + + 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); + UINT32 cnt, channels = bpp/8; ASSERT(channels >= m_header.channels); + + for (i=0; i < h; i++) { + UINT32 uPos = uOffset; + UINT32 yPos = yOffset; + cnt = 0; + for (j=0; j < w; j++) { + uAvg = a[uPos]; + vAvg = b[uPos]; + buff[cnt + channelMap[0]] = Clamp8(l[yPos] + YUVoffset8); + buff[cnt + channelMap[1]] = Clamp8(uAvg + YUVoffset8); + buff[cnt + channelMap[2]] = Clamp8(vAvg + YUVoffset8); + cnt += channels; + if (!m_downsample || (j & 1)) uPos++; + yPos++; + } + if (!m_downsample || (i & 1)) uOffset += uw; + yOffset += yw; + buff += pitch; + + 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); + UINT32 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++) { + UINT32 uPos = uOffset; + UINT32 yPos = yOffset; + cnt = 0; + for (j=0; j < w; j++) { + uAvg = a[uPos]; + vAvg = b[uPos]; + 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; + if (!m_downsample || (j & 1)) uPos++; + yPos++; + } + if (!m_downsample || (i & 1)) uOffset += uw; + yOffset += yw; + 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++) { + UINT32 uPos = uOffset; + UINT32 yPos = yOffset; + cnt = 0; + for (j=0; j < w; j++) { + uAvg = a[uPos]; + vAvg = b[uPos]; + 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; + if (!m_downsample || (j & 1)) uPos++; + yPos++; + } + if (!m_downsample || (i & 1)) uOffset += uw; + yOffset += yw; + buff += pitch; + + 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; + UINT32 cnt, channels = bpp/8; ASSERT(channels >= m_header.channels); + + for (i=0; i < h; i++) { + UINT32 uPos = uOffset; + UINT32 yPos = yOffset; + cnt = 0; + for (j=0; j < w; j++) { + uAvg = u[uPos]; + vAvg = v[uPos]; + aAvg = Clamp8(a[uPos] + 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; + cnt += channels; + if (!m_downsample || (j & 1)) uPos++; + yPos++; + } + if (!m_downsample || (i & 1)) uOffset += uw; + yOffset += yw; + buff += pitch; + + 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; + UINT32 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++) { + UINT32 uPos = uOffset; + UINT32 yPos = yOffset; + cnt = 0; + for (j=0; j < w; j++) { + uAvg = u[uPos]; + vAvg = v[uPos]; + aAvg = a[uPos] + 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); + cnt += channels; + if (!m_downsample || (j & 1)) uPos++; + yPos++; + } + if (!m_downsample || (i & 1)) uOffset += uw; + yOffset += yw; + 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++) { + UINT32 uPos = uOffset; + UINT32 yPos = yOffset; + cnt = 0; + for (j=0; j < w; j++) { + uAvg = u[uPos]; + vAvg = v[uPos]; + aAvg = a[uPos] + 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); + cnt += channels; + if (!m_downsample || (j & 1)) uPos++; + yPos++; + } + if (!m_downsample || (i & 1)) uOffset += uw; + yOffset += yw; + buff += pitch; + + 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++) { + UINT32 yPos = yOffset; + for (j = 0; j < w; j++) { + buff32[j] = Clamp31((y[yPos++] + yuvOffset31) << shift); + } + yOffset += yw; + 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++) { + UINT32 yPos = yOffset; + for (j = 0; j < w; j++) { + buff16[j] = Clamp16((y[yPos++] + yuvOffset31) << shift); + } + yOffset += yw; + 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++) { + UINT32 yPos = yOffset; + for (j = 0; j < w; j++) { + buff16[j] = Clamp16((y[yPos++] + yuvOffset31) >> shift); + } + yOffset += yw; + 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++) { + UINT32 yPos = yOffset; + for (j = 0; j < w; j++) { + buff[j] = Clamp8((y[yPos++] + yuvOffset31) >> shift); + } + yOffset += yw; + 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; + UINT32 cnt; + + for (i=0; i < h; i++) { + UINT32 yPos = yOffset; + 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++; + } + yPos++; + } + yOffset += yw; + 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++) { + UINT32 yPos = yOffset; + 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); + } + yOffset += yw; + buff16 += pitch16; + + if (cb) { + percent += dP; + if ((*cb)(percent, true, data)) ReturnWithError(EscapePressed); + } + } + break; + } + default: + ASSERT(false); + } + +#ifdef _DEBUG + // display ROI (RGB) in debugger + roiimage.width = w; + roiimage.height = h; + if (pitch > 0) { + roiimage.pitch = pitch; + roiimage.data = buff; + } else { + roiimage.pitch = -pitch; + roiimage.data = buff + (h - 1)*pitch; + } +#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[] /*= nullptr*/, CallbackPtr cb /*= nullptr*/, void *data /*=nullptr*/) const { + 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 == nullptr) 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[] /*= nullptr*/, CallbackPtr cb /*= nullptr*/, void *data /*=nullptr*/) { + 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 == nullptr) 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); + } + } +} + diff --git a/offline/pgfutils/libpgf/PGFimage.h b/offline/pgfutils/libpgf/PGFimage.h new file mode 100644 index 0000000..7110529 --- /dev/null +++ b/offline/pgfutils/libpgf/PGFimage.h @@ -0,0 +1,581 @@ +/* + * 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" + +////////////////////////////////////////////////////////////////////// +// 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: +/// Open() +/// Read() +/// GetBitmap() +/// Encoding: +/// SetHeader() +/// ImportBitmap() +/// Write() +/// @author C. Stamm, R. Spuler +/// @brief PGF main class +class CPGFImage { +public: + + ////////////////////////////////////////////////////////////////////// + /// Standard constructor + CPGFImage(); + + ////////////////////////////////////////////////////////////////////// + /// Destructor + virtual ~CPGFImage(); + + ////////////////////////////////////////////////////////////////////// + // Destroy internal data structures. Object state after this is the same as after CPGFImage(). + 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); + + ////////////////////////////////////////////////////////////////////// + /// Returns true if the PGF has been opened for reading. + bool IsOpen() const { return m_decoder != nullptr; } + + ////////////////////////////////////////////////////////////////////// + /// 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 = nullptr, void *data = nullptr); + +#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) at level 0. 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 = nullptr, void *data = nullptr); +#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() { 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); + + ////////////////////////////////////////////////////////////////////// + /// 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[] = nullptr, CallbackPtr cb = nullptr, void *data = nullptr) const; // 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[] = nullptr, CallbackPtr cb = nullptr, void *data = nullptr) const; // 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[] = nullptr, CallbackPtr cb = nullptr, void *data = nullptr); + + ////////////////////////////////////////////////////////////////////// + /// 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[] = nullptr, CallbackPtr cb = nullptr, void *data = nullptr); + + ////////////////////////////////////////////////////////////////////// + /// Encode and write an 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 = nullptr, CallbackPtr cb = nullptr, void *data = nullptr); + + ////////////////////////////////////////////////////////////////// + /// 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); + + ////////////////////////////////////////////////////////////////////// + /// Encode and write an 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 = nullptr, void *data = nullptr); + +#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 = nullptr, void *data = nullptr); +#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 policy The file might contain user data (e.g. metadata). The policy defines the behaviour during Open(). + /// UP_CacheAll: User data is read and stored completely in a new allocated memory block. It can be accessed by GetUserData(). + /// UP_CachePrefix: Only prefixSize bytes at the beginning of the user data are stored in a new allocated memory block. It can be accessed by GetUserData(). + /// UP_Skip: User data is skipped and nothing is cached. + /// @param prefixSize Is only used in combination with UP_CachePrefix. It defines the number of bytes cached. + void ConfigureDecoder(bool useOMP = true, UserdataPolicy policy = UP_CacheAll, UINT32 prefixSize = 0) { ASSERT(prefixSize <= MaxUserDataSize); m_useOMPinDecoder = useOMP; m_userDataPolicy = (UP_CachePrefix) ? prefixSize : 0xFFFFFFFF - policy; } + + //////////////////////////////////////////////////////////////////// + /// Reset stream position to start of PGF pre-header or start of data. Must not be called before Open() or before Write(). + /// Use this method after Read() if you want to read the same image several times, e.g. reading different ROIs. + /// @param startOfData true: you want to read the same image several times. false: resets stream position to the initial position + void ResetStreamPos(bool startOfData); + + ////////////////////////////////////////////////////////////////////// + /// 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 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, const UINT8* userData = 0, UINT32 userDataLength = 0); // 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); + + ////////////////////////////////////////////////////////////////////// + /// 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; + + ////////////////////////////////////////////////////////////////////// + // 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 cachedSize [out] Size of returned user data in bytes. + /// @param pTotalSize [optional out] Pointer to return the size of user data stored in image header in bytes. + /// @return A pointer to user data or nullptr if there is no user data available. + const UINT8* GetUserData(UINT32& cachedSize, UINT32* pTotalSize = nullptr) 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 header 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; + + ////////////////////////////////////////////////////////////////////// + /// 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; + + ////////////////////////////////////////////////////////////////////// + /// 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 MaxChannelDepth(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 LevelSizeL(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 LevelSizeL(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 true if all levels have been read + bool IsFullyRead() const { return m_currentLevel == 0; } + + ////////////////////////////////////////////////////////////////////// + /// 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; } + +#ifdef __PGFROISUPPORT__ + /// Return ROI of channel 0 at current level in pixels. + /// The returned rect is only valid after reading a ROI. + /// @return ROI in pixels + PGFRect ComputeLevelROI() const; +#endif + + ////////////////////////////////////////////////////////////////////// + /// 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 the used codec major version of a pgf image + /// @return PGF codec major version of this image + BYTE Version() const { BYTE ver = CodecMajorVersion(m_preHeader.version); return (ver <= 7) ? ver : (BYTE)m_header.version.major; } + + //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/height of LL subband at given level. + /// @param size Original image size (e.g. width or height at level 0) + /// @param level An image level + /// @return Image width/height at given level in pixels + static UINT32 LevelSizeL(UINT32 size, int level) { ASSERT(level >= 0); UINT32 d = 1 << level; return (size + d - 1) >> level; } + + ////////////////////////////////////////////////////////////////////// + /// Compute and return image width/height of HH subband at given level. + /// @param size Original image size (e.g. width or height at level 0) + /// @param level An image level + /// @return high pass size at given level in pixels + static UINT32 LevelSizeH(UINT32 size, int level) { ASSERT(level >= 0); UINT32 d = 1 << (level - 1); return (size + d - 1) >> level; } + + ////////////////////////////////////////////////////////////////////// + /// Return codec major version. + /// @param version pgf pre-header version number + /// @return PGF major of given version + static BYTE CodecMajorVersion(BYTE version = PGFVersion); + + ////////////////////////////////////////////////////////////////////// + /// Return maximum channel depth. + /// @param version pgf pre-header version number + /// @return maximum channel depth in bit of given version (16 or 32 bit) + static BYTE MaxChannelDepth(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 + UINT32 m_userDataPolicy; ///< user data (metadata) policy during open + 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_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 Init(); + void ComputeLevels(); + bool CompleteHeader(); + void RgbToYuv(int pitch, UINT8* rgbBuff, BYTE bpp, int channelMap[], CallbackPtr cb, void *data); + void Downsample(int nChannel); + UINT32 UpdatePostHeaderSize(); + void WriteLevel(); + +#ifdef __PGFROISUPPORT__ + PGFRect GetAlignedROI(int c = 0) const; + 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 diff --git a/offline/pgfutils/libpgf/PGFplatform.h b/offline/pgfutils/libpgf/PGFplatform.h new file mode 100644 index 0000000..84c2995 --- /dev/null +++ b/offline/pgfutils/libpgf/PGFplatform.h @@ -0,0 +1,631 @@ +/* + * The Progressive Graphics File; http://www.libpgf.org + * + * $Date: 2007-06-12 19:27:47 +0200 (Di, 12 Jun 2007) $ + * $Revision: 307 $ + * + * 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 PGFplatform.h +/// @brief PGF platform specific definitions +/// @author C. Stamm + +#ifndef PGF_PGFPLATFORM_H +#define PGF_PGFPLATFORM_H + +#include +#include +#include + +//------------------------------------------------------------------------------- +// Endianess detection taken from lcms2 header. +// This list can be endless, so only some checks are performed over here. +//------------------------------------------------------------------------------- +#if defined(_HOST_BIG_ENDIAN) || defined(__BIG_ENDIAN__) || defined(WORDS_BIGENDIAN) +#define PGF_USE_BIG_ENDIAN 1 +#endif + +#if defined(__sgi__) || defined(__sgi) || defined(__powerpc__) || defined(__sparc) || defined(__sparc__) +#define PGF_USE_BIG_ENDIAN 1 +#endif + +#if defined(__ppc__) || defined(__s390__) || defined(__s390x__) +#define PGF_USE_BIG_ENDIAN 1 +#endif + +#ifdef TARGET_CPU_PPC +#define PGF_USE_BIG_ENDIAN 1 +#endif + +//------------------------------------------------------------------------------- +// ROI support +//------------------------------------------------------------------------------- +#ifndef NPGFROI +#define __PGFROISUPPORT__ // without ROI support the program code gets simpler and smaller +#endif + +//------------------------------------------------------------------------------- +// 32 bit per channel support +//------------------------------------------------------------------------------- +#ifndef NPGF32 +#define __PGF32SUPPORT__ // without 32 bit the memory consumption during encoding and decoding is much lesser +#endif + +//------------------------------------------------------------------------------- +// 32 Bit platform constants +//------------------------------------------------------------------------------- +#define WordWidth 32 ///< WordBytes*8 +#define WordWidthLog 5 ///< ld of WordWidth +#define WordMask 0xFFFFFFE0 ///< least WordWidthLog bits are zero +#define WordBytes 4 ///< sizeof(UINT32) +#define WordBytesMask 0xFFFFFFFC ///< least WordBytesLog bits are zero +#define WordBytesLog 2 ///< ld of WordBytes + +//------------------------------------------------------------------------------- +// Alignment macros (used in PGF based libraries) +//------------------------------------------------------------------------------- +#define DWWIDTHBITS(bits) (((bits) + WordWidth - 1) & WordMask) ///< aligns scanline width in bits to DWORD value +#define DWWIDTH(bytes) (((bytes) + WordBytes - 1) & WordBytesMask) ///< aligns scanline width in bytes to DWORD value +#define DWWIDTHREST(bytes) ((WordBytes - (bytes)%WordBytes)%WordBytes) ///< DWWIDTH(bytes) - bytes + +//------------------------------------------------------------------------------- +// Min-Max macros +//------------------------------------------------------------------------------- +#ifndef __min + #define __min(x, y) ((x) <= (y) ? (x) : (y)) + #define __max(x, y) ((x) >= (y) ? (x) : (y)) +#endif // __min + +//------------------------------------------------------------------------------- +// Defines -- Adobe image modes. +//------------------------------------------------------------------------------- +#define ImageModeBitmap 0 +#define ImageModeGrayScale 1 +#define ImageModeIndexedColor 2 +#define ImageModeRGBColor 3 +#define ImageModeCMYKColor 4 +#define ImageModeHSLColor 5 +#define ImageModeHSBColor 6 +#define ImageModeMultichannel 7 +#define ImageModeDuotone 8 +#define ImageModeLabColor 9 +#define ImageModeGray16 10 // 565 +#define ImageModeRGB48 11 +#define ImageModeLab48 12 +#define ImageModeCMYK64 13 +#define ImageModeDeepMultichannel 14 +#define ImageModeDuotone16 15 +// pgf extension +#define ImageModeRGBA 17 +#define ImageModeGray32 18 // MSB is 0 (can be interpreted as signed 15.16 fixed point format) +#define ImageModeRGB12 19 +#define ImageModeRGB16 20 +#define ImageModeUnknown 255 + + +//------------------------------------------------------------------------------- +// WINDOWS +//------------------------------------------------------------------------------- +#if defined(WIN32) || defined(WINCE) || defined(WIN64) || defined(__MINGW32__) +#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers + +//------------------------------------------------------------------------------- +// MFC +//------------------------------------------------------------------------------- +#ifdef _MFC_VER +#ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows Vista. +#define _WIN32_WINNT 0x0600 // Change this to the appropriate value to target other versions of Windows. +#endif +#include +#include // MFC core and standard components +#include // MFC extensions +#include // MFC support for Internet Explorer 4 Common Controls +#ifndef _AFX_NO_AFXCMN_SUPPORT +#include // MFC support for Windows Common Controls +#endif // _AFX_NO_AFXCMN_SUPPORT + +#else + +#include +#include + +#endif // _MFC_VER +//------------------------------------------------------------------------------- + +#define DllExport __declspec( dllexport ) + +//------------------------------------------------------------------------------- +// unsigned number type definitions +//------------------------------------------------------------------------------- +typedef unsigned char UINT8; +typedef unsigned char BYTE; +typedef unsigned short UINT16; +typedef unsigned short WORD; +typedef unsigned int UINT32; +typedef unsigned long DWORD; +typedef unsigned long ULONG; +typedef unsigned __int64 UINT64; +typedef unsigned __int64 ULONGLONG; + +//------------------------------------------------------------------------------- +// signed number type definitions +//------------------------------------------------------------------------------- +typedef signed char INT8; +typedef signed short INT16; +typedef signed int INT32; +typedef signed int BOOL; +typedef signed long LONG; +typedef signed __int64 INT64; +typedef signed __int64 LONGLONG; + +//------------------------------------------------------------------------------- +// other types +//------------------------------------------------------------------------------- +typedef int OSError; +typedef bool (__cdecl *CallbackPtr)(double percent, bool escapeAllowed, void *data); + +//------------------------------------------------------------------------------- +// struct type definitions +//------------------------------------------------------------------------------- + +//------------------------------------------------------------------------------- +// DEBUG macros +//------------------------------------------------------------------------------- +#ifndef ASSERT + #ifdef _DEBUG + #define ASSERT(x) assert(x) + #else + #if defined(__GNUC__) + #define ASSERT(ignore)((void) 0) + #elif _MSC_VER >= 1300 + #define ASSERT __noop + #else + #define ASSERT ((void)0) + #endif + #endif //_DEBUG +#endif //ASSERT + +//------------------------------------------------------------------------------- +// Exception handling macros +//------------------------------------------------------------------------------- +#ifdef NEXCEPTIONS + extern OSError _PGF_Error_; + extern OSError GetLastPGFError(); + + #define ReturnWithError(err) { _PGF_Error_ = err; return; } + #define ReturnWithError2(err, ret) { _PGF_Error_ = err; return ret; } +#else + #define ReturnWithError(err) throw IOException(err) + #define ReturnWithError2(err, ret) throw IOException(err) +#endif //NEXCEPTIONS + +//------------------------------------------------------------------------------- +// constants +//------------------------------------------------------------------------------- +#define FSFromStart FILE_BEGIN // 0 +#define FSFromCurrent FILE_CURRENT // 1 +#define FSFromEnd FILE_END // 2 + +#define INVALID_SET_FILE_POINTER ((DWORD)-1) + +//------------------------------------------------------------------------------- +// IO Error constants +//------------------------------------------------------------------------------- +#define NoError ERROR_SUCCESS ///< no error +#define AppError 0x20000000 ///< all application error messages must be larger than this value +#define InsufficientMemory 0x20000001 ///< memory allocation was not successfull +#define InvalidStreamPos 0x20000002 ///< invalid memory stream position +#define EscapePressed 0x20000003 ///< user break by ESC +#define WrongVersion 0x20000004 ///< wrong PGF version +#define FormatCannotRead 0x20000005 ///< wrong data file format +#define ImageTooSmall 0x20000006 ///< image is too small +#define ZlibError 0x20000007 ///< error in zlib functions +#define ColorTableError 0x20000008 ///< errors related to color table size +#define PNGError 0x20000009 ///< errors in png functions +#define MissingData 0x2000000A ///< expected data cannot be read + +//------------------------------------------------------------------------------- +// methods +//------------------------------------------------------------------------------- +inline OSError FileRead(HANDLE hFile, int *count, void *buffPtr) { + if (ReadFile(hFile, buffPtr, *count, (ULONG *)count, nullptr)) { + return NoError; + } else { + return GetLastError(); + } +} + +inline OSError FileWrite(HANDLE hFile, int *count, void *buffPtr) { + if (WriteFile(hFile, buffPtr, *count, (ULONG *)count, nullptr)) { + return NoError; + } else { + return GetLastError(); + } +} + +inline OSError GetFPos(HANDLE hFile, UINT64 *pos) { +#ifdef WINCE + LARGE_INTEGER li; + li.QuadPart = 0; + + li.LowPart = SetFilePointer (hFile, li.LowPart, &li.HighPart, FILE_CURRENT); + if (li.LowPart == INVALID_SET_FILE_POINTER) { + OSError err = GetLastError(); + if (err != NoError) { + return err; + } + } + *pos = li.QuadPart; + return NoError; +#else + LARGE_INTEGER li; + li.QuadPart = 0; + if (SetFilePointerEx(hFile, li, (PLARGE_INTEGER)pos, FILE_CURRENT)) { + return NoError; + } else { + return GetLastError(); + } +#endif +} + +inline OSError SetFPos(HANDLE hFile, int posMode, INT64 posOff) { +#ifdef WINCE + LARGE_INTEGER li; + li.QuadPart = posOff; + + if (SetFilePointer (hFile, li.LowPart, &li.HighPart, posMode) == INVALID_SET_FILE_POINTER) { + OSError err = GetLastError(); + if (err != NoError) { + return err; + } + } + return NoError; +#else + LARGE_INTEGER li; + li.QuadPart = posOff; + if (SetFilePointerEx(hFile, li, nullptr, posMode)) { + return NoError; + } else { + return GetLastError(); + } +#endif +} +#endif //WIN32 + + +//------------------------------------------------------------------------------- +// Apple OSX +//------------------------------------------------------------------------------- +#ifdef __APPLE__ +#define __POSIX__ +#endif // __APPLE__ + + +//------------------------------------------------------------------------------- +// LINUX +//------------------------------------------------------------------------------- +#if defined(__linux__) || defined(__GLIBC__) +#define __POSIX__ +#endif // __linux__ or __GLIBC__ + + +//------------------------------------------------------------------------------- +// SOLARIS +//------------------------------------------------------------------------------- +#ifdef __sun +#define __POSIX__ +#endif // __sun + + +//------------------------------------------------------------------------------- +// *BSD +//------------------------------------------------------------------------------- +#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__) +#ifndef __POSIX__ +#define __POSIX__ +#endif + +#ifndef off64_t +#define off64_t off_t +#endif + +#ifndef lseek64 +#define lseek64 lseek +#endif + +#endif // __NetBSD__ or __OpenBSD__ or __FreeBSD__ + + +//------------------------------------------------------------------------------- +// POSIX *NIXes +//------------------------------------------------------------------------------- + +#ifdef __POSIX__ +#include +#include +#include // for int64_t and uint64_t +#include // memcpy() + +#undef major + +//------------------------------------------------------------------------------- +// unsigned number type definitions +//------------------------------------------------------------------------------- + +typedef unsigned char UINT8; +typedef unsigned char BYTE; +typedef unsigned short UINT16; +typedef unsigned short WORD; +typedef unsigned int UINT32; +typedef unsigned int DWORD; +typedef unsigned long ULONG; +typedef unsigned long long __Uint64; +typedef __Uint64 UINT64; +typedef __Uint64 ULONGLONG; + +//------------------------------------------------------------------------------- +// signed number type definitions +//------------------------------------------------------------------------------- +typedef signed char INT8; +typedef signed short INT16; +typedef signed int INT32; +typedef signed int BOOL; +typedef signed long LONG; +typedef int64_t INT64; +typedef int64_t LONGLONG; + +//------------------------------------------------------------------------------- +// other types +//------------------------------------------------------------------------------- +typedef int OSError; +typedef int HANDLE; +typedef unsigned long ULONG_PTR; +typedef void* PVOID; +typedef char* LPTSTR; +typedef bool (*CallbackPtr)(double percent, bool escapeAllowed, void *data); + +//------------------------------------------------------------------------------- +// struct type definitions +//------------------------------------------------------------------------------- +typedef struct tagRGBTRIPLE { + BYTE rgbtBlue; + BYTE rgbtGreen; + BYTE rgbtRed; +} RGBTRIPLE; + +typedef struct tagRGBQUAD { + BYTE rgbBlue; + BYTE rgbGreen; + BYTE rgbRed; + BYTE rgbReserved; +} RGBQUAD; + +typedef union _LARGE_INTEGER { + struct { + DWORD LowPart; + LONG HighPart; + } u; + LONGLONG QuadPart; +} LARGE_INTEGER, *PLARGE_INTEGER; +#endif // __POSIX__ + + +#if defined(__POSIX__) || defined(WINCE) +// CMYK macros +#define GetKValue(cmyk) ((BYTE)(cmyk)) +#define GetYValue(cmyk) ((BYTE)((cmyk)>> 8)) +#define GetMValue(cmyk) ((BYTE)((cmyk)>>16)) +#define GetCValue(cmyk) ((BYTE)((cmyk)>>24)) +#define CMYK(c,m,y,k) ((COLORREF)((((BYTE)(k)|((WORD)((BYTE)(y))<<8))|(((DWORD)(BYTE)(m))<<16))|(((DWORD)(BYTE)(c))<<24))) + +//------------------------------------------------------------------------------- +// methods +//------------------------------------------------------------------------------- +/* The MulDiv function multiplies two 32-bit values and then divides the 64-bit + * result by a third 32-bit value. The return value is rounded up or down to + * the nearest integer. + * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winprog/winprog/muldiv.asp + * */ +__inline int MulDiv(int nNumber, int nNumerator, int nDenominator) { + INT64 multRes = nNumber*nNumerator; + INT32 divRes = INT32(multRes/nDenominator); + return divRes; +} +#endif // __POSIX__ or WINCE + + +#ifdef __POSIX__ +//------------------------------------------------------------------------------- +// DEBUG macros +//------------------------------------------------------------------------------- +#ifndef ASSERT + #ifdef _DEBUG + #define ASSERT(x) assert(x) + #else + #define ASSERT(x) + #endif //_DEBUG +#endif //ASSERT + +//------------------------------------------------------------------------------- +// Exception handling macros +//------------------------------------------------------------------------------- +#ifdef NEXCEPTIONS + extern OSError _PGF_Error_; + extern OSError GetLastPGFError(); + + #define ReturnWithError(err) { _PGF_Error_ = err; return; } + #define ReturnWithError2(err, ret) { _PGF_Error_ = err; return ret; } +#else + #define ReturnWithError(err) throw IOException(err) + #define ReturnWithError2(err, ret) throw IOException(err) +#endif //NEXCEPTIONS + +#define THROW_ throw(IOException) +#define CONST const + +//------------------------------------------------------------------------------- +// constants +//------------------------------------------------------------------------------- +#define FSFromStart SEEK_SET +#define FSFromCurrent SEEK_CUR +#define FSFromEnd SEEK_END +#define nullptr NULL + +//------------------------------------------------------------------------------- +// IO Error constants +//------------------------------------------------------------------------------- +#define NoError 0x0000 ///< no error +#define AppError 0x2000 ///< all application error messages must be larger than this value +#define InsufficientMemory 0x2001 ///< memory allocation wasn't successfull +#define InvalidStreamPos 0x2002 ///< invalid memory stream position +#define EscapePressed 0x2003 ///< user break by ESC +#define WrongVersion 0x2004 ///< wrong pgf version +#define FormatCannotRead 0x2005 ///< wrong data file format +#define ImageTooSmall 0x2006 ///< image is too small +#define ZlibError 0x2007 ///< error in zlib functions +#define ColorTableError 0x2008 ///< errors related to color table size +#define PNGError 0x2009 ///< errors in png functions +#define MissingData 0x200A ///< expected data cannot be read + +//------------------------------------------------------------------------------- +// methods +//------------------------------------------------------------------------------- +__inline OSError FileRead(HANDLE hFile, int *count, void *buffPtr) { + *count = (int)read(hFile, buffPtr, *count); + if (*count != -1) { + return NoError; + } else { + return errno; + } +} + +__inline OSError FileWrite(HANDLE hFile, int *count, void *buffPtr) { + *count = (int)write(hFile, buffPtr, (size_t)*count); + if (*count != -1) { + return NoError; + } else { + return errno; + } +} + +__inline OSError GetFPos(HANDLE hFile, UINT64 *pos) { + #ifdef __APPLE__ + off_t ret; + if ((ret = lseek(hFile, 0, SEEK_CUR)) == -1) { + return errno; + } else { + *pos = (UINT64)ret; + return NoError; + } + #else + off64_t ret; + if ((ret = lseek64(hFile, 0, SEEK_CUR)) == -1) { + return errno; + } else { + *pos = (UINT64)ret; + return NoError; + } + #endif +} + +__inline OSError SetFPos(HANDLE hFile, int posMode, INT64 posOff) { + #ifdef __APPLE__ + if ((lseek(hFile, (off_t)posOff, posMode)) == -1) { + return errno; + } else { + return NoError; + } + #else + if ((lseek64(hFile, (off64_t)posOff, posMode)) == -1) { + return errno; + } else { + return NoError; + } + #endif +} + +#endif /* __POSIX__ */ +//------------------------------------------------------------------------------- + + +//------------------------------------------------------------------------------- +// Big Endian +//------------------------------------------------------------------------------- +#ifdef PGF_USE_BIG_ENDIAN + +#ifndef _lrotl + #define _lrotl(x,n) (((x) << ((UINT32)(n))) | ((x) >> (32 - (UINT32)(n)))) +#endif + +__inline UINT16 ByteSwap(UINT16 wX) { + return ((wX & 0xFF00) >> 8) | ((wX & 0x00FF) << 8); +} + +__inline UINT32 ByteSwap(UINT32 dwX) { +#ifdef _X86_ + _asm mov eax, dwX + _asm bswap eax + _asm mov dwX, eax + return dwX; +#else + return _lrotl(((dwX & 0xFF00FF00) >> 8) | ((dwX & 0x00FF00FF) << 8), 16); +#endif +} + +#if defined(WIN32) || defined(WIN64) +__inline UINT64 ByteSwap(UINT64 ui64) { + return _byteswap_uint64(ui64); +} +#endif + +#define __VAL(x) ByteSwap(x) + +#else //PGF_USE_BIG_ENDIAN + + #define __VAL(x) (x) + +#endif //PGF_USE_BIG_ENDIAN + +// OpenMP rules (inspired from libraw project) +// NOTE: Use LIBPGF_DISABLE_OPENMP to disable OpenMP support in whole libpgf +#ifndef LIBPGF_DISABLE_OPENMP +# if defined (_OPENMP) +# if defined (WIN32) || defined(WIN64) +# if defined (_MSC_VER) && (_MSC_VER >= 1500) +// VS2008 SP1 and VS2010+ : OpenMP works OK +# define LIBPGF_USE_OPENMP +# elif defined (__INTEL_COMPILER) && (__INTEL_COMPILER >=910) +// untested on 9.x and 10.x, Intel documentation claims OpenMP 2.5 support in 9.1 +# define LIBPGF_USE_OPENMP +# else +# undef LIBPGF_USE_OPENMP +# endif +// Not Win32 +# elif (defined(__APPLE__) || defined(__MACOSX__)) && defined(_REENTRANT) +# undef LIBPGF_USE_OPENMP +# else +# define LIBPGF_USE_OPENMP +# endif +# endif // defined (_OPENMP) +#endif // ifndef LIBPGF_DISABLE_OPENMP +#ifdef LIBPGF_USE_OPENMP +#include +#endif + +#endif //PGF_PGFPLATFORM_H diff --git a/offline/pgfutils/libpgf/PGFstream.cpp b/offline/pgfutils/libpgf/PGFstream.cpp new file mode 100644 index 0000000..b4fe16d --- /dev/null +++ b/offline/pgfutils/libpgf/PGFstream.cpp @@ -0,0 +1,274 @@ +/* + * The Progressive Graphics File; http://www.libpgf.org + * + * $Date: 2007-01-19 11:51:24 +0100 (Fr, 19 Jan 2007) $ + * $Revision: 268 $ + * + * 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 PGFstream.cpp +/// @brief PGF stream class implementation +/// @author C. Stamm + +#include "PGFstream.h" + +#ifdef WIN32 +#include +#endif + +////////////////////////////////////////////////////////////////////// +// CPGFFileStream +////////////////////////////////////////////////////////////////////// +void CPGFFileStream::Write(int *count, void *buffPtr) { + ASSERT(count); + ASSERT(buffPtr); + ASSERT(IsValid()); + OSError err; + if ((err = FileWrite(m_hFile, count, buffPtr)) != NoError) ReturnWithError(err); + +} + +////////////////////////////////////////////////////////////////////// +void CPGFFileStream::Read(int *count, void *buffPtr) { + ASSERT(count); + ASSERT(buffPtr); + ASSERT(IsValid()); + OSError err; + if ((err = FileRead(m_hFile, count, buffPtr)) != NoError) ReturnWithError(err); +} + +////////////////////////////////////////////////////////////////////// +void CPGFFileStream::SetPos(short posMode, INT64 posOff) { + ASSERT(IsValid()); + OSError err; + if ((err = SetFPos(m_hFile, posMode, posOff)) != NoError) ReturnWithError(err); +} + +////////////////////////////////////////////////////////////////////// +UINT64 CPGFFileStream::GetPos() const { + ASSERT(IsValid()); + OSError err; + UINT64 pos = 0; + if ((err = GetFPos(m_hFile, &pos)) != NoError) ReturnWithError2(err, pos); + return pos; +} + + +////////////////////////////////////////////////////////////////////// +// CPGFMemoryStream +////////////////////////////////////////////////////////////////////// +/// Allocate memory block of given size +/// @param size Memory size +CPGFMemoryStream::CPGFMemoryStream(size_t size) +: m_size(size) +, m_allocated(true) { + m_buffer = m_pos = m_eos = new(std::nothrow) UINT8[m_size]; + if (!m_buffer) ReturnWithError(InsufficientMemory); +} + +////////////////////////////////////////////////////////////////////// +/// Use already allocated memory of given size +/// @param pBuffer Memory location +/// @param size Memory size +CPGFMemoryStream::CPGFMemoryStream(UINT8 *pBuffer, size_t size) +: m_buffer(pBuffer) +, m_pos(pBuffer) +, m_eos(pBuffer + size) +, m_size(size) +, m_allocated(false) { + ASSERT(IsValid()); +} + +////////////////////////////////////////////////////////////////////// +/// Use already allocated memory of given size +/// @param pBuffer Memory location +/// @param size Memory size +void CPGFMemoryStream::Reinitialize(UINT8 *pBuffer, size_t size) { + if (!m_allocated) { + m_buffer = m_pos = pBuffer; + m_size = size; + m_eos = m_buffer + size; + } +} + +////////////////////////////////////////////////////////////////////// +void CPGFMemoryStream::Write(int *count, void *buffPtr) { + ASSERT(count); + ASSERT(buffPtr); + ASSERT(IsValid()); + const size_t deltaSize = 0x4000 + *count; + + if (m_pos + *count <= m_buffer + m_size) { + memcpy(m_pos, buffPtr, *count); + m_pos += *count; + if (m_pos > m_eos) m_eos = m_pos; + } else if (m_allocated) { + // memory block is too small -> reallocate a deltaSize larger block + size_t offset = m_pos - m_buffer; + UINT8 *buf_tmp = (UINT8 *)realloc(m_buffer, m_size + deltaSize); + if (!buf_tmp) { + delete[] m_buffer; + m_buffer = 0; + ReturnWithError(InsufficientMemory); + } else { + m_buffer = buf_tmp; + } + m_size += deltaSize; + + // reposition m_pos + m_pos = m_buffer + offset; + + // write block + memcpy(m_pos, buffPtr, *count); + m_pos += *count; + if (m_pos > m_eos) m_eos = m_pos; + } else { + ReturnWithError(InsufficientMemory); + } + ASSERT(m_pos <= m_eos); +} + +////////////////////////////////////////////////////////////////////// +void CPGFMemoryStream::Read(int *count, void *buffPtr) { + ASSERT(IsValid()); + ASSERT(count); + ASSERT(buffPtr); + ASSERT(m_buffer + m_size >= m_eos); + ASSERT(m_pos <= m_eos); + + if (m_pos + *count <= m_eos) { + memcpy(buffPtr, m_pos, *count); + m_pos += *count; + } else { + // end of memory block reached -> read only until end + *count = (int)__max(0, m_eos - m_pos); + memcpy(buffPtr, m_pos, *count); + m_pos += *count; + } + ASSERT(m_pos <= m_eos); +} + +////////////////////////////////////////////////////////////////////// +void CPGFMemoryStream::SetPos(short posMode, INT64 posOff) { + ASSERT(IsValid()); + switch(posMode) { + case FSFromStart: + m_pos = m_buffer + posOff; + break; + case FSFromCurrent: + m_pos += posOff; + break; + case FSFromEnd: + m_pos = m_eos + posOff; + break; + default: + ASSERT(false); + } + if (m_pos > m_eos) + ReturnWithError(InvalidStreamPos); +} + + +////////////////////////////////////////////////////////////////////// +// CPGFMemFileStream +#ifdef _MFC_VER +////////////////////////////////////////////////////////////////////// +void CPGFMemFileStream::Write(int *count, void *buffPtr) { + ASSERT(count); + ASSERT(buffPtr); + ASSERT(IsValid()); + m_memFile->Write(buffPtr, *count); +} + +////////////////////////////////////////////////////////////////////// +void CPGFMemFileStream::Read(int *count, void *buffPtr) { + ASSERT(count); + ASSERT(buffPtr); + ASSERT(IsValid()); + m_memFile->Read(buffPtr, *count); +} + +////////////////////////////////////////////////////////////////////// +void CPGFMemFileStream::SetPos(short posMode, INT64 posOff) { + ASSERT(IsValid()); + m_memFile->Seek(posOff, posMode); +} + +////////////////////////////////////////////////////////////////////// +UINT64 CPGFMemFileStream::GetPos() const { + return (UINT64)m_memFile->GetPosition(); +} +#endif // _MFC_VER + +////////////////////////////////////////////////////////////////////// +// CPGFIStream +#if defined(WIN32) || defined(WINCE) +////////////////////////////////////////////////////////////////////// +void CPGFIStream::Write(int *count, void *buffPtr) { + ASSERT(count); + ASSERT(buffPtr); + ASSERT(IsValid()); + + HRESULT hr = m_stream->Write(buffPtr, *count, (ULONG *)count); + if (FAILED(hr)) { + ReturnWithError(hr); + } +} + +////////////////////////////////////////////////////////////////////// +void CPGFIStream::Read(int *count, void *buffPtr) { + ASSERT(count); + ASSERT(buffPtr); + ASSERT(IsValid()); + + HRESULT hr = m_stream->Read(buffPtr, *count, (ULONG *)count); + if (FAILED(hr)) { + ReturnWithError(hr); + } +} + +////////////////////////////////////////////////////////////////////// +void CPGFIStream::SetPos(short posMode, INT64 posOff) { + ASSERT(IsValid()); + + LARGE_INTEGER li; + li.QuadPart = posOff; + + HRESULT hr = m_stream->Seek(li, posMode, nullptr); + if (FAILED(hr)) { + ReturnWithError(hr); + } +} + +////////////////////////////////////////////////////////////////////// +UINT64 CPGFIStream::GetPos() const { + ASSERT(IsValid()); + + LARGE_INTEGER n; + ULARGE_INTEGER pos; + n.QuadPart = 0; + + HRESULT hr = m_stream->Seek(n, FSFromCurrent, &pos); + if (SUCCEEDED(hr)) { + return pos.QuadPart; + } else { + ReturnWithError2(hr, pos.QuadPart); + } +} +#endif // WIN32 || WINCE diff --git a/offline/pgfutils/libpgf/PGFstream.h b/offline/pgfutils/libpgf/PGFstream.h new file mode 100644 index 0000000..5353004 --- /dev/null +++ b/offline/pgfutils/libpgf/PGFstream.h @@ -0,0 +1,193 @@ +/* + * The Progressive Graphics File; http://www.libpgf.org + * + * $Date: 2007-06-11 10:56:17 +0200 (Mo, 11 Jun 2007) $ + * $Revision: 299 $ + * + * 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 PGFstream.h +/// @brief PGF stream class +/// @author C. Stamm + +#ifndef PGF_STREAM_H +#define PGF_STREAM_H + +#include "PGFtypes.h" +#include + +///////////////////////////////////////////////////////////////////// +/// Abstract stream base class. +/// @author C. Stamm +/// @brief Abstract stream base class +class CPGFStream { +public: + ////////////////////////////////////////////////////////////////////// + /// Standard constructor. + CPGFStream() {} + + ////////////////////////////////////////////////////////////////////// + /// Standard destructor. + virtual ~CPGFStream() {} + + ////////////////////////////////////////////////////////////////////// + /// Write some bytes out of a buffer into this stream. + /// @param count A pointer to a value containing the number of bytes should be written. After this call it contains the number of written bytes. + /// @param buffer A memory buffer + virtual void Write(int *count, void *buffer)=0; + + ////////////////////////////////////////////////////////////////////// + /// Read some bytes from this stream and stores them into a buffer. + /// @param count A pointer to a value containing the number of bytes should be read. After this call it contains the number of read bytes. + /// @param buffer A memory buffer + virtual void Read(int *count, void *buffer)=0; + + ////////////////////////////////////////////////////////////////////// + /// Set stream position either absolute or relative. + /// @param posMode A position mode (FSFromStart, FSFromCurrent, FSFromEnd) + /// @param posOff A new stream position (absolute positioning) or a position offset (relative positioning) + virtual void SetPos(short posMode, INT64 posOff)=0; + + ////////////////////////////////////////////////////////////////////// + /// Get current stream position. + /// @return Current stream position + virtual UINT64 GetPos() const=0; + + ////////////////////////////////////////////////////////////////////// + /// Check stream validity. + /// @return True if stream and current position is valid + virtual bool IsValid() const=0; +}; + +///////////////////////////////////////////////////////////////////// +/// A PGF stream subclass for external storage files. +/// @author C. Stamm +/// @brief File stream class +class CPGFFileStream : public CPGFStream { +protected: + HANDLE m_hFile; ///< file handle + +public: + CPGFFileStream() : m_hFile(0) {} + /// Constructor + /// @param hFile File handle + CPGFFileStream(HANDLE hFile) : m_hFile(hFile) {} + /// @return File handle + HANDLE GetHandle() { return m_hFile; } + + virtual ~CPGFFileStream() { m_hFile = 0; } + virtual void Write(int *count, void *buffer); // throws IOException + virtual void Read(int *count, void *buffer); // throws IOException + virtual void SetPos(short posMode, INT64 posOff); // throws IOException + virtual UINT64 GetPos() const; // throws IOException + virtual bool IsValid() const { return m_hFile != 0; } +}; + +///////////////////////////////////////////////////////////////////// +/// A PGF stream subclass for internal memory. +/// @author C. Stamm +/// @brief Memory stream class +class CPGFMemoryStream : public CPGFStream { +protected: + UINT8 *m_buffer, *m_pos;///< buffer start address and current buffer address + UINT8 *m_eos; ///< end of stream (first address beyond written area) + size_t m_size; ///< buffer size + bool m_allocated; ///< indicates a new allocated buffer + +public: + /// Constructor + /// @param size Size of new allocated memory buffer + CPGFMemoryStream(size_t size); + + /// Constructor. Use already allocated memory of given size + /// @param pBuffer Memory location + /// @param size Memory size + CPGFMemoryStream(UINT8 *pBuffer, size_t size); + + /// Use already allocated memory of given size + /// @param pBuffer Memory location + /// @param size Memory size + void Reinitialize(UINT8 *pBuffer, size_t size); + + virtual ~CPGFMemoryStream() { + m_pos = 0; + if (m_allocated) { + // the memory buffer has been allocated inside of CPMFmemoryStream constructor + delete[] m_buffer; m_buffer = 0; + } + } + + virtual void Write(int *count, void *buffer); // throws IOException + virtual void Read(int *count, void *buffer); + virtual void SetPos(short posMode, INT64 posOff); // throws IOException + virtual UINT64 GetPos() const { ASSERT(IsValid()); return m_pos - m_buffer; } + virtual bool IsValid() const { return m_buffer != 0; } + + /// @return Memory size + size_t GetSize() const { return m_size; } + /// @return Memory buffer + const UINT8* GetBuffer() const { return m_buffer; } + /// @return Memory buffer + UINT8* GetBuffer() { return m_buffer; } + /// @return relative position of end of stream (= stream length) + UINT64 GetEOS() const { ASSERT(IsValid()); return m_eos - m_buffer; } + /// @param length Stream length (= relative position of end of stream) + void SetEOS(UINT64 length) { ASSERT(IsValid()); m_eos = m_buffer + length; } +}; + +///////////////////////////////////////////////////////////////////// +/// A PGF stream subclass for internal memory files. Usable only with MFC. +/// @author C. Stamm +/// @brief Cached memory file stream class +#ifdef _MFC_VER +class CPGFMemFileStream : public CPGFStream { +protected: + CMemFile *m_memFile; ///< MFC memory file +public: + CPGFMemFileStream(CMemFile *memFile) : m_memFile(memFile) {} + virtual bool IsValid() const { return m_memFile != nullptr; } + virtual ~CPGFMemFileStream() {} + virtual void Write(int *count, void *buffer); // throws IOException + virtual void Read(int *count, void *buffer); // throws IOException + virtual void SetPos(short posMode, INT64 posOff); // throws IOException + virtual UINT64 GetPos() const; // throws IOException +}; +#endif + +///////////////////////////////////////////////////////////////////// +/// A PGF stream subclass for IStream. Usable only with COM. +/// @author C. Stamm +/// @brief COM IStream class +#if defined(WIN32) || defined(WINCE) +class CPGFIStream : public CPGFStream { +protected: + IStream *m_stream; ///< COM+ IStream +public: + CPGFIStream(IStream *stream) : m_stream(stream) { m_stream->AddRef(); } + virtual bool IsValid() const { return m_stream != 0; } + virtual ~CPGFIStream() { m_stream->Release(); } + virtual void Write(int *count, void *buffer); // throws IOException + virtual void Read(int *count, void *buffer); // throws IOException + virtual void SetPos(short posMode, INT64 posOff); // throws IOException + virtual UINT64 GetPos() const; // throws IOException + IStream* GetIStream() const { return m_stream; } +}; +#endif + +#endif // PGF_STREAM_H diff --git a/offline/pgfutils/libpgf/PGFtypes.h b/offline/pgfutils/libpgf/PGFtypes.h new file mode 100644 index 0000000..f7eb08f --- /dev/null +++ b/offline/pgfutils/libpgf/PGFtypes.h @@ -0,0 +1,290 @@ +/* + * The Progressive Graphics File; http://www.libpgf.org + * + * $Date: 2007-06-11 10:56:17 +0200 (Mo, 11 Jun 2007) $ + * $Revision: 299 $ + * + * 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 PGFtypes.h +/// @brief PGF definitions +/// @author C. Stamm + +#ifndef PGF_PGFTYPES_H +#define PGF_PGFTYPES_H + +#include "PGFplatform.h" + +//------------------------------------------------------------------------------- +// Codec versions +// +// Version 2: modified data structure PGFHeader (backward compatibility assured) +// Version 4: DataT: INT32 instead of INT16, allows 31 bit per pixel and channel (backward compatibility assured) +// Version 5: ROI, new block-reordering scheme (backward compatibility assured) +// Version 6: modified data structure PGFPreHeader: hSize (header size) is now a UINT32 instead of a UINT16 (backward compatibility assured) +// Version 7: last two bytes in header are now used for extended version numbers; new data representation for bitmaps (backward compatibility assured) +// +//------------------------------------------------------------------------------- +#define PGFMajorNumber 7 +#define PGFYear 19 +#define PGFWeek 03 + +#define PPCAT_NX(A, B) A ## B +#define PPCAT(A, B) PPCAT_NX(A, B) +#define STRINGIZE_NX(A) #A +#define STRINGIZE(A) STRINGIZE_NX(A) + +//#define PGFCodecVersionID 0x071822 +#define PGFCodecVersionID PPCAT(PPCAT(PPCAT(0x0, PGFMajorNumber), PGFYear), PGFWeek) +//#define PGFCodecVersion "7.19.3" ///< Major number, Minor number: Year (2) Week (2) +#define PGFCodecVersion STRINGIZE(PPCAT(PPCAT(PPCAT(PPCAT(PGFMajorNumber, .), PGFYear), .), PGFWeek)) + +//------------------------------------------------------------------------------- +// Image constants +//------------------------------------------------------------------------------- +#define PGFMagic "PGF" ///< PGF identification +#define MaxLevel 30 ///< maximum number of transform levels +#define NSubbands 4 ///< number of subbands per level +#define MaxChannels 8 ///< maximum number of (color) channels +#define DownsampleThreshold 3 ///< if quality is larger than this threshold than downsampling is used +#define ColorTableLen 256 ///< size of color lookup table (clut) +// version flags +#define Version2 2 ///< data structure PGFHeader of major version 2 +#define PGF32 4 ///< 32 bit values are used -> allows at maximum 31 bits, otherwise 16 bit values are used -> allows at maximum 15 bits +#define PGFROI 8 ///< supports Regions Of Interest +#define Version5 16 ///< new coding scheme since major version 5 +#define Version6 32 ///< hSize in PGFPreHeader uses 32 bits instead of 16 bits +#define Version7 64 ///< Codec major and minor version number stored in PGFHeader +// version numbers +#ifdef __PGF32SUPPORT__ +#define PGFVersion (Version2 | PGF32 | Version5 | Version6 | Version7) ///< current standard version +#else +#define PGFVersion (Version2 | Version5 | Version6 | Version7) ///< current standard version +#endif + +//------------------------------------------------------------------------------- +// Coder constants +//------------------------------------------------------------------------------- +#define BufferSize 16384 ///< must be a multiple of WordWidth, BufferSize <= UINT16_MAX +#define RLblockSizeLen 15 ///< block size length (< 16): ld(BufferSize) < RLblockSizeLen <= 2*ld(BufferSize) +#define LinBlockSize 8 ///< side length of a coefficient block in a HH or LL subband +#define InterBlockSize 4 ///< side length of a coefficient block in a HL or LH subband +#ifdef __PGF32SUPPORT__ + #define MaxBitPlanes 31 ///< maximum number of bit planes of m_value: 32 minus sign bit +#else + #define MaxBitPlanes 15 ///< maximum number of bit planes of m_value: 16 minus sign bit +#endif +#define MaxBitPlanesLog 5 ///< number of bits to code the maximum number of bit planes (in 32 or 16 bit mode) +#define MaxQuality MaxBitPlanes ///< maximum quality + +//------------------------------------------------------------------------------- +// Types +//------------------------------------------------------------------------------- +enum Orientation { LL = 0, HL = 1, LH = 2, HH = 3 }; +enum ProgressMode { PM_Relative, PM_Absolute }; +enum UserdataPolicy { UP_Skip = 0, UP_CachePrefix = 1, UP_CacheAll = 2 }; + +/// general PGF file structure +/// PGFPreHeader PGFHeader [PGFPostHeader] LevelLengths Level_n-1 Level_n-2 ... Level_0 +/// PGFPostHeader ::= [ColorTable] [UserData] +/// LevelLengths ::= UINT32[nLevels] + +#pragma pack(1) +///////////////////////////////////////////////////////////////////// +/// PGF magic and version (part of PGF pre-header) +/// @author C. Stamm +/// @brief PGF identification and version +struct PGFMagicVersion { + char magic[3]; ///< PGF identification = "PGF" + UINT8 version; ///< PGF version + // total: 4 Bytes +}; + +///////////////////////////////////////////////////////////////////// +/// PGF pre-header defined header length and PGF identification and version +/// @author C. Stamm +/// @brief PGF pre-header +struct PGFPreHeader : PGFMagicVersion { + UINT32 hSize; ///< total size of PGFHeader, [ColorTable], and [UserData] in bytes (since Version 6: 4 Bytes) + // total: 8 Bytes +}; + +///////////////////////////////////////////////////////////////////// +/// Version number since major version 7 +/// @author C. Stamm +/// @brief version number stored in header since major version 7 +struct PGFVersionNumber { + PGFVersionNumber(UINT8 _major, UINT8 _year, UINT8 _week) +#ifdef PGF_USE_BIG_ENDIAN + : week(_week), year(_year), major(_major) {} +#else + : major(_major), year(_year), week(_week) {} +#endif // PGF_USE_BIG_ENDIAN + +#ifdef PGF_USE_BIG_ENDIAN + UINT16 week : 6; ///< week number in a year + UINT16 year : 6; ///< year since 2000 (year 2001 = 1) + UINT16 major : 4; ///< major version number +#else + UINT16 major : 4; ///< major version number + UINT16 year : 6; ///< year since 2000 (year 2001 = 1) + UINT16 week : 6; ///< week number in a year +#endif // PGF_USE_BIG_ENDIAN +}; + +///////////////////////////////////////////////////////////////////// +/// PGF header contains image information +/// @author C. Stamm +/// @brief PGF header +struct PGFHeader { + PGFHeader() : width(0), height(0), nLevels(0), quality(0), bpp(0), channels(0), mode(ImageModeUnknown), usedBitsPerChannel(0), version(0, 0, 0) {} + UINT32 width; ///< image width in pixels + UINT32 height; ///< image height in pixels + UINT8 nLevels; ///< number of FWT transforms + UINT8 quality; ///< quantization parameter: 0=lossless, 4=standard, 6=poor quality + UINT8 bpp; ///< bits per pixel + UINT8 channels; ///< number of channels + UINT8 mode; ///< image mode according to Adobe's image modes + UINT8 usedBitsPerChannel; ///< number of used bits per channel in 16- and 32-bit per channel modes + PGFVersionNumber version; ///< codec version number: (since Version 7) + // total: 16 Bytes +}; + +///////////////////////////////////////////////////////////////////// +/// PGF post-header is optional. It contains color table and user data +/// @author C. Stamm +/// @brief Optional PGF post-header +struct PGFPostHeader { + RGBQUAD clut[ColorTableLen];///< color table for indexed color images (optional part of file header) + UINT8 *userData; ///< user data of size userDataLen (optional part of file header) + UINT32 userDataLen; ///< user data size in bytes (not part of file header) + UINT32 cachedUserDataLen; ///< cached user data size in bytes (not part of file header) +}; + +///////////////////////////////////////////////////////////////////// +/// ROI block header is used with ROI coding scheme. It contains block size and tile end flag +/// @author C. Stamm +/// @brief Block header used with ROI coding scheme +union ROIBlockHeader { + UINT16 val; ///< unstructured union value + /// @brief Named ROI block header (part of the union) + struct RBH { +#ifdef PGF_USE_BIG_ENDIAN + UINT16 tileEnd : 1; ///< 1: last part of a tile + UINT16 bufferSize: RLblockSizeLen; ///< number of uncoded UINT32 values in a block +#else + UINT16 bufferSize: RLblockSizeLen; ///< number of uncoded UINT32 values in a block + UINT16 tileEnd : 1; ///< 1: last part of a tile +#endif // PGF_USE_BIG_ENDIAN + } rbh; ///< ROI block header + // total: 2 Bytes + + /// Constructor + /// @param v Buffer size + ROIBlockHeader(UINT16 v) { val = v; } + + /// Constructor + /// @param size Buffer size + /// @param end 0/1 Flag; 1: last part of a tile + ROIBlockHeader(UINT32 size, bool end) { ASSERT(size < (1 << RLblockSizeLen)); rbh.bufferSize = size; rbh.tileEnd = end; } +}; + +#pragma pack() + +///////////////////////////////////////////////////////////////////// +/// PGF I/O exception +/// @author C. Stamm +/// @brief PGF exception +struct IOException { + OSError error; ///< operating system error code + + /// Standard constructor + IOException() : error(NoError) {} + + /// Constructor + /// @param err Run-time error + IOException(OSError err) : error(err) {} +}; + +///////////////////////////////////////////////////////////////////// +/// Rectangle +/// @author C. Stamm +/// @brief Rectangle +struct PGFRect { + UINT32 left, top, right, bottom; + + /// Standard constructor + PGFRect() : left(0), top(0), right(0), bottom(0) {} + + /// Constructor + /// @param x Left offset + /// @param y Top offset + /// @param width Rectangle width + /// @param height Rectangle height + PGFRect(UINT32 x, UINT32 y, UINT32 width, UINT32 height) : left(x), top(y), right(x + width), bottom(y + height) {} + +#ifdef WIN32 + PGFRect(const RECT& rect) : left(rect.left), top(rect.top), right(rect.right), bottom(rect.bottom) { + ASSERT(rect.left >= 0 && rect.right >= 0 && rect.left <= rect.right); + ASSERT(rect.top >= 0 && rect.bottom >= 0 && rect.top <= rect.bottom); + } + + PGFRect& operator=(const RECT& rect) { + left = rect.left; top = rect.top; right = rect.right; bottom = rect.bottom; + return *this; + } + + operator RECT() { + RECT rect = { (LONG)left, (LONG)top, (LONG)right, (LONG)bottom }; + return rect; + } +#endif + + /// @return Rectangle width + UINT32 Width() const { return right - left; } + + /// @return Rectangle height + UINT32 Height() const { return bottom - top; } + + /// Test if point (x,y) is inside this rectangle (inclusive top-left edges, exclusive bottom-right edges) + /// @param x Point coordinate x + /// @param y Point coordinate y + /// @return True if point (x,y) is inside this rectangle (inclusive top-left edges, exclusive bottom-right edges) + bool IsInside(UINT32 x, UINT32 y) const { return (x >= left && x < right && y >= top && y < bottom); } +}; + +#ifdef __PGF32SUPPORT__ +typedef INT32 DataT; +#else +typedef INT16 DataT; +#endif + +typedef void (*RefreshCB)(void *p); + +//------------------------------------------------------------------------------- +// Image constants +//------------------------------------------------------------------------------- +#define MagicVersionSize sizeof(PGFMagicVersion) +#define PreHeaderSize sizeof(PGFPreHeader) +#define HeaderSize sizeof(PGFHeader) +#define ColorTableSize (ColorTableLen*sizeof(RGBQUAD)) +#define DataTSize sizeof(DataT) +#define MaxUserDataSize 0x7FFFFFFF + +#endif //PGF_PGFTYPES_H diff --git a/offline/pgfutils/libpgf/README b/offline/pgfutils/libpgf/README new file mode 100644 index 0000000..be12ba5 --- /dev/null +++ b/offline/pgfutils/libpgf/README @@ -0,0 +1,114 @@ +The Progressive Graphics File +============================= + +For more information see http://www.libpgf.org. There you can find some +documents concerning this progressive graphic file codec. + +This project is hosted on the Sourceforge.net platform. For support and +questions, please use the installed mailing list and forums there. +The Sourceforge URL of our project is: http://sourceforge.net/projects/libpgf + +============================= +Release Notes + +Version 7.19.3, (Tue, 15 Jan 2019) +------------------------------------ + +1. The new version is a minor update of version 7.15.25. + +2. This version fixes a compilation bug seen when ROI support is disabled. + + +Version 7.15.32, (Thu, 6 Aug 2015) +------------------------------------ + +1. The new version is a minor update of version 7.15.25. + +2. This version improves the reuse of CPGFImage objects for several decoding operations. + It clarifies the usage of CPGFImage::Close() and CPGFImage::Destroy() by deletion of + Close(). Several reading operations can be performed in the following way: + Open(), Read(), GetBitmap(), ResetStreamPos(), Read(), GetBitmap(), ResetStreamPos(), ... + Calling Destroy() frees all allocated ressources and reinitializes the object to the + same state as the constructor does. This allows the reuse of the CPGFImage object for + encoding and decoding: + SetHeader(), ImportBitmap(), Write(), ResetStreamPos(), Destroy(), Open(), Read(), GetBitmap() + +3. Caching or skipping of user data (meta data) while opening a PGF image can be controlled + by a new UserdataPolicy in ConfigureDecoder(). + + +Version 7.15.25, (Sat, 20 June 2015) +------------------------------------ + +1. This new version is a replacement of version 6.14.12. + In case you use the ROI decoding, we strongly encourage using version 6.15.25 instead of an older version. + +2. This version fixes some decoder bugs only seen in ROI decoding. + ROI decoding is now also supported for Bitmap and RGB12 image modes. + +3. This version introduces a new and more efficient data format for binary images (bitmaps). + The new format allows ROI decoding. + The decoder supports both the old and the new format, but ROI decoding works only with the new format. + +4. The two reserverd bytes in PGFHeader are now used for a more detailled PGF version number. + +5. The Visual Studio project files are in the VS12 format. + + +Version 6.14.12, (Wed, 9 April 2014) +------------------------------------ + +1. The new version is a minor update of version 6.12.24. + +2. It mainly contains some fixes of memory leaks in the OpenMP part and some improvements suggested by cppcheck and Coverity. + +3. The Visual Studio project files are in the VS11 format. + + +Version 6.12.24, (Thu, 14 June 2012) +------------------------------------ + +1. The new version is a replacement of version 6.11.42. + In case you use the ROI encoding scheme, we strongly encourage using version 6.12.24 instead of version 6.11.42. + +2. This version fixes some decoder bugs, sometimes seen in ROI decoding. + +3. This version allows a simpler user-data handling, especially for uncached metadata. The following two methods + in the class PGFimage are designed for this purpose: + + GetUserDataPos() returns in an opened PGF image the stream position of the user data area. + + WriteImage() encodes and writes the image at the current stream position. This method is called after + WriteHeader(). In case you want to write uncached metadata into the stream, then do that after WriteHeader() + and just before WriteImage(). If you are not interested in writing uncached metadata, then you usually use + Write() instead of WriteImage(). WriteHeader() and WriteImage() are called inside of Write(). + + +Version 6.11.42, (Sun, 23 Oct 2011) +----------------------------------- + +1. The new version is a replacement of version 6.11.24. + We strongly encourage using version 6.11.42 instead of version 6.11.24. + +2. This version fixes some decoder bugs, only seen in lossless compression of + large images. + +3. The rarely used, but sometimes misused, background information (3 Bytes) + in the PGFHeader has been replaced by + + UINT8 usedBitsPerChannel; // number of used bits per channel + // in 16- and 32-bit per channel modes + UINT8 reserved1, reserved2; // not used + +The value usedBitsPerChannel is helpful in case you have more than 8 (16) but +less than 16 (32) significant bits per channel, stored in the most +significant bits of a pixel. For example, you have a grayscale image with 14 +bit significant data per pixel stored in the ImageModeGray16 pixel format. In +case you have left shifted the 14 bits to be the most significant 14 bits, +then you should set usedBitsPerChannel=14. This will increase the compression +ratio without any drawbacks, because the 14 bits are internally right shifted. +On the other side, if the 14 bits are the least significant bits in +your 16 bit pixel format, then you shoulden’t set usedBitsPerChannel. It will +be automatically set to 16, but this is no problem, since the not used most +significant bits per pixel are never coded at all. So, in both cases the same +compression ratio will result. \ No newline at end of file diff --git a/offline/pgfutils/libpgf/Subband.cpp b/offline/pgfutils/libpgf/Subband.cpp new file mode 100644 index 0000000..0cae4bf --- /dev/null +++ b/offline/pgfutils/libpgf/Subband.cpp @@ -0,0 +1,388 @@ +/* + * 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 Subband.cpp +/// @brief PGF wavelet subband class implementation +/// @author C. Stamm + +#include "Subband.h" +#include "Encoder.h" +#include "Decoder.h" + +///////////////////////////////////////////////////////////////////// +// Default constructor +CSubband::CSubband() +: m_width(0) +, m_height(0) +, m_size(0) +, m_level(0) +, m_orientation(LL) +, m_dataPos(0) +, m_data(0) +#ifdef __PGFROISUPPORT__ +, m_nTiles(0) +#endif +{ +} + +///////////////////////////////////////////////////////////////////// +// Destructor +CSubband::~CSubband() { + FreeMemory(); +} + +///////////////////////////////////////////////////////////////////// +// Initialize subband parameters +void CSubband::Initialize(UINT32 width, UINT32 height, int level, Orientation orient) { + m_width = width; + m_height = height; + m_size = m_width*m_height; + m_level = level; + m_orientation = orient; + m_data = 0; + m_dataPos = 0; +#ifdef __PGFROISUPPORT__ + m_ROI.left = 0; + m_ROI.top = 0; + m_ROI.right = m_width; + m_ROI.bottom = m_height; + m_nTiles = 0; +#endif +} + +///////////////////////////////////////////////////////////////////// +// Allocate a memory buffer to store all wavelet coefficients of this subband. +// @return True if the allocation works without any problems +bool CSubband::AllocMemory() { + UINT32 oldSize = m_size; + +#ifdef __PGFROISUPPORT__ + m_size = BufferWidth()*m_ROI.Height(); +#endif + ASSERT(m_size > 0); + + if (m_data) { + if (oldSize >= m_size) { + return true; + } else { + delete[] m_data; + m_data = new(std::nothrow) DataT[m_size]; + return (m_data != 0); + } + } else { + m_data = new(std::nothrow) DataT[m_size]; + return (m_data != 0); + } +} + +///////////////////////////////////////////////////////////////////// +// Delete the memory buffer of this subband. +void CSubband::FreeMemory() { + if (m_data) { + delete[] m_data; m_data = 0; + } +} + +///////////////////////////////////////////////////////////////////// +// Perform subband quantization with given quantization parameter. +// A scalar quantization (with dead-zone) is used. A large quantization value +// results in strong quantization and therefore in big quality loss. +// @param quantParam A quantization parameter (larger or equal to 0) +void CSubband::Quantize(int quantParam) { + if (m_orientation == LL) { + quantParam -= (m_level + 1); + // uniform rounding quantization + if (quantParam > 0) { + quantParam--; + for (UINT32 i=0; i < m_size; i++) { + if (m_data[i] < 0) { + m_data[i] = -(((-m_data[i] >> quantParam) + 1) >> 1); + } else { + m_data[i] = ((m_data[i] >> quantParam) + 1) >> 1; + } + } + } + } else { + if (m_orientation == HH) { + quantParam -= (m_level - 1); + } else { + quantParam -= m_level; + } + // uniform deadzone quantization + if (quantParam > 0) { + int threshold = ((1 << quantParam) * 7)/5; // good value + quantParam--; + for (UINT32 i=0; i < m_size; i++) { + if (m_data[i] < -threshold) { + m_data[i] = -(((-m_data[i] >> quantParam) + 1) >> 1); + } else if (m_data[i] > threshold) { + m_data[i] = ((m_data[i] >> quantParam) + 1) >> 1; + } else { + m_data[i] = 0; + } + } + } + } +} + +////////////////////////////////////////////////////////////////////// +/// Perform subband dequantization with given quantization parameter. +/// A scalar quantization (with dead-zone) is used. A large quantization value +/// results in strong quantization and therefore in big quality loss. +/// @param quantParam A quantization parameter (larger or equal to 0) +void CSubband::Dequantize(int quantParam) { + if (m_orientation == LL) { + quantParam -= m_level + 1; + } else if (m_orientation == HH) { + quantParam -= m_level - 1; + } else { + quantParam -= m_level; + } + if (quantParam > 0) { + for (UINT32 i=0; i < m_size; i++) { + m_data[i] <<= quantParam; + } + } +} + +///////////////////////////////////////////////////////////////////// +/// Extracts a rectangular subregion of this subband. +/// Write wavelet coefficients into buffer. +/// It might throw an IOException. +/// @param encoder An encoder instance +/// @param tile True if just a rectangular region is extracted, false if the entire subband is extracted. +/// @param tileX Tile index in x-direction +/// @param tileY Tile index in y-direction +void CSubband::ExtractTile(CEncoder& encoder, bool tile /*= false*/, UINT32 tileX /*= 0*/, UINT32 tileY /*= 0*/) { +#ifdef __PGFROISUPPORT__ + if (tile) { + // compute tile position and size + UINT32 xPos, yPos, w, h; + TilePosition(tileX, tileY, xPos, yPos, w, h); + + // write values into buffer using partitiong scheme + encoder.Partition(this, w, h, xPos + yPos*m_width, m_width); + } else +#endif + { + (void)tileX; (void)tileY; (void)tile; // prevents from unreferenced formal parameter warning + // write values into buffer using partitiong scheme + encoder.Partition(this, m_width, m_height, 0, m_width); + } +} + +///////////////////////////////////////////////////////////////////// +/// Decoding and dequantization of this subband. +/// It might throw an IOException. +/// @param decoder A decoder instance +/// @param quantParam Dequantization value +/// @param tile True if just a rectangular region is placed, false if the entire subband is placed. +/// @param tileX Tile index in x-direction +/// @param tileY Tile index in y-direction +void CSubband::PlaceTile(CDecoder& decoder, int quantParam, bool tile /*= false*/, UINT32 tileX /*= 0*/, UINT32 tileY /*= 0*/) { + // allocate memory + if (!AllocMemory()) ReturnWithError(InsufficientMemory); + + // correct quantParam with normalization factor + if (m_orientation == LL) { + quantParam -= m_level + 1; + } else if (m_orientation == HH) { + quantParam -= m_level - 1; + } else { + quantParam -= m_level; + } + if (quantParam < 0) quantParam = 0; + +#ifdef __PGFROISUPPORT__ + if (tile) { + UINT32 xPos, yPos, w, h; + + // compute tile position and size + TilePosition(tileX, tileY, xPos, yPos, w, h); + + ASSERT(xPos >= m_ROI.left && yPos >= m_ROI.top); + decoder.Partition(this, quantParam, w, h, (xPos - m_ROI.left) + (yPos - m_ROI.top)*BufferWidth(), BufferWidth()); + } else +#endif + { + (void)tileX; (void)tileY; (void)tile; // prevents from unreferenced formal parameter warning + // read values into buffer using partitiong scheme + decoder.Partition(this, quantParam, m_width, m_height, 0, m_width); + } +} + + + +#ifdef __PGFROISUPPORT__ +////////////////////////////////////////////////////////////////////// +/// Set ROI +void CSubband::SetAlignedROI(const PGFRect& roi) { + ASSERT(roi.left <= m_width); + ASSERT(roi.top <= m_height); + + m_ROI = roi; + if (m_ROI.right > m_width) m_ROI.right = m_width; + if (m_ROI.bottom > m_height) m_ROI.bottom = m_height; +} + +////////////////////////////////////////////////////////////////////// +/// Compute tile position and size. +/// @param tileX Tile index in x-direction +/// @param tileY Tile index in y-direction +/// @param xPos [out] Offset to left +/// @param yPos [out] Offset to top +/// @param w [out] Tile width +/// @param h [out] Tile height +void CSubband::TilePosition(UINT32 tileX, UINT32 tileY, UINT32& xPos, UINT32& yPos, UINT32& w, UINT32& h) const { + ASSERT(tileX < m_nTiles); ASSERT(tileY < m_nTiles); + // example + // band = HH, w = 30, ldTiles = 2 -> 4 tiles in a row/column + // --> tile widths + // 8 7 8 7 + // + // tile partitioning scheme + // 0 1 2 3 + // 4 5 6 7 + // 8 9 A B + // C D E F + + UINT32 nTiles = m_nTiles; + UINT32 m; + UINT32 left = 0, right = nTiles; + UINT32 top = 0, bottom = nTiles; + + xPos = 0; + yPos = 0; + w = m_width; + h = m_height; + + while (nTiles > 1) { + // compute xPos and w with binary search + m = left + ((right - left) >> 1); + if (tileX >= m) { + xPos += (w + 1) >> 1; + w >>= 1; + left = m; + } else { + w = (w + 1) >> 1; + right = m; + } + // compute yPos and h with binary search + m = top + ((bottom - top) >> 1); + if (tileY >= m) { + yPos += (h + 1) >> 1; + h >>= 1; + top = m; + } else { + h = (h + 1) >> 1; + bottom = m; + } + nTiles >>= 1; + } + ASSERT(xPos < m_width && (xPos + w <= m_width)); + ASSERT(yPos < m_height && (yPos + h <= m_height)); +} + +////////////////////////////////////////////////////////////////////// +/// Compute tile index and extrem position (x,y) of given position (xPos, yPos). +void CSubband::TileIndex(bool topLeft, UINT32 xPos, UINT32 yPos, UINT32& tileX, UINT32& tileY, UINT32& x, UINT32& y) const { + UINT32 m; + UINT32 left = 0, right = m_width; + UINT32 top = 0, bottom = m_height; + UINT32 nTiles = m_nTiles; + + if (xPos > m_width) xPos = m_width; + if (yPos > m_height) yPos = m_height; + + if (topLeft) { + // compute tileX with binary search + tileX = 0; + while (nTiles > 1) { + nTiles >>= 1; + m = left + ((right - left + 1) >> 1); + if (xPos < m) { + // exclusive m + right = m; + } else { + tileX += nTiles; + left = m; + } + } + x = left; + ASSERT(tileX >= 0 && tileX < m_nTiles); + + // compute tileY with binary search + nTiles = m_nTiles; + tileY = 0; + while (nTiles > 1) { + nTiles >>= 1; + m = top + ((bottom - top + 1) >> 1); + if (yPos < m) { + // exclusive m + bottom = m; + } else { + tileY += nTiles; + top = m; + } + } + y = top; + ASSERT(tileY >= 0 && tileY < m_nTiles); + + } else { + // compute tileX with binary search + tileX = 1; + while (nTiles > 1) { + nTiles >>= 1; + m = left + ((right - left + 1) >> 1); + if (xPos <= m) { + // inclusive m + right = m; + } else { + tileX += nTiles; + left = m; + } + } + x = right; + ASSERT(tileX > 0 && tileX <= m_nTiles); + + // compute tileY with binary search + nTiles = m_nTiles; + tileY = 1; + while (nTiles > 1) { + nTiles >>= 1; + m = top + ((bottom - top + 1) >> 1); + if (yPos <= m) { + // inclusive m + bottom = m; + } else { + tileY += nTiles; + top = m; + } + } + y = bottom; + ASSERT(tileY > 0 && tileY <= m_nTiles); + } +} + +#endif diff --git a/offline/pgfutils/libpgf/Subband.h b/offline/pgfutils/libpgf/Subband.h new file mode 100644 index 0000000..060470a --- /dev/null +++ b/offline/pgfutils/libpgf/Subband.h @@ -0,0 +1,180 @@ +/* + * 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 Subband.h +/// @brief PGF wavelet subband class +/// @author C. Stamm + +#ifndef PGF_SUBBAND_H +#define PGF_SUBBAND_H + +#include "PGFtypes.h" + +class CEncoder; +class CDecoder; +class CRoiIndices; + +////////////////////////////////////////////////////////////////////// +/// PGF wavelet channel subband class. +/// @author C. Stamm, R. Spuler +/// @brief Wavelet channel class +class CSubband { + friend class CWaveletTransform; + friend class CRoiIndices; + +public: + ////////////////////////////////////////////////////////////////////// + /// Standard constructor. + CSubband(); + + ////////////////////////////////////////////////////////////////////// + /// Destructor. + ~CSubband(); + + ////////////////////////////////////////////////////////////////////// + /// Allocate a memory buffer to store all wavelet coefficients of this subband. + /// @return True if the allocation did work without any problems + bool AllocMemory(); + + ////////////////////////////////////////////////////////////////////// + /// Delete the memory buffer of this subband. + void FreeMemory(); + + ///////////////////////////////////////////////////////////////////// + /// Extracts a rectangular subregion of this subband. + /// Write wavelet coefficients into buffer. + /// It might throw an IOException. + /// @param encoder An encoder instance + /// @param tile True if just a rectangular region is extracted, false if the entire subband is extracted. + /// @param tileX Tile index in x-direction + /// @param tileY Tile index in y-direction + void ExtractTile(CEncoder& encoder, bool tile = false, UINT32 tileX = 0, UINT32 tileY = 0); + + ///////////////////////////////////////////////////////////////////// + /// Decoding and dequantization of this subband. + /// It might throw an IOException. + /// @param decoder A decoder instance + /// @param quantParam Dequantization value + /// @param tile True if just a rectangular region is placed, false if the entire subband is placed. + /// @param tileX Tile index in x-direction + /// @param tileY Tile index in y-direction + void PlaceTile(CDecoder& decoder, int quantParam, bool tile = false, UINT32 tileX = 0, UINT32 tileY = 0); + + ////////////////////////////////////////////////////////////////////// + /// Perform subband quantization with given quantization parameter. + /// A scalar quantization (with dead-zone) is used. A large quantization value + /// results in strong quantization and therefore in big quality loss. + /// @param quantParam A quantization parameter (larger or equal to 0) + void Quantize(int quantParam); + + ////////////////////////////////////////////////////////////////////// + /// Perform subband dequantization with given quantization parameter. + /// A scalar quantization (with dead-zone) is used. A large quantization value + /// results in strong quantization and therefore in big quality loss. + /// @param quantParam A quantization parameter (larger or equal to 0) + void Dequantize(int quantParam); + + ////////////////////////////////////////////////////////////////////// + /// Store wavelet coefficient in subband at given position. + /// @param pos A subband position (>= 0) + /// @param v A wavelet coefficient + void SetData(UINT32 pos, DataT v) { ASSERT(pos < m_size); m_data[pos] = v; } + + ////////////////////////////////////////////////////////////////////// + /// Get a pointer to an array of all wavelet coefficients of this subband. + /// @return Pointer to array of wavelet coefficients + DataT* GetBuffer() { return m_data; } + + ////////////////////////////////////////////////////////////////////// + /// Return wavelet coefficient at given position. + /// @param pos A subband position (>= 0) + /// @return Wavelet coefficient + DataT GetData(UINT32 pos) const { ASSERT(pos < m_size); return m_data[pos]; } + + ////////////////////////////////////////////////////////////////////// + /// Return level of this subband. + /// @return Level of this subband + int GetLevel() const { return m_level; } + + ////////////////////////////////////////////////////////////////////// + /// Return height of this subband. + /// @return Height of this subband (in pixels) + int GetHeight() const { return m_height; } + + ////////////////////////////////////////////////////////////////////// + /// Return width of this subband. + /// @return Width of this subband (in pixels) + int GetWidth() const { return m_width; } + + ////////////////////////////////////////////////////////////////////// + /// Return orientation of this subband. + /// LL LH + /// HL HH + /// @return Orientation of this subband (LL, HL, LH, HH) + Orientation GetOrientation() const { return m_orientation; } + +#ifdef __PGFROISUPPORT__ + ///////////////////////////////////////////////////////////////////// + /// Set data buffer position to given position + one row. + /// @param pos Given position + void IncBuffRow(UINT32 pos) { m_dataPos = pos + BufferWidth(); } + +#endif + +private: + void Initialize(UINT32 width, UINT32 height, int level, Orientation orient); + void WriteBuffer(DataT val) { ASSERT(m_dataPos < m_size); m_data[m_dataPos++] = val; } + void SetBuffer(DataT* b) { ASSERT(b); m_data = b; } + DataT ReadBuffer() { ASSERT(m_dataPos < m_size); return m_data[m_dataPos++]; } + + UINT32 GetBuffPos() const { return m_dataPos; } + +#ifdef __PGFROISUPPORT__ + UINT32 BufferWidth() const { return m_ROI.Width(); } + void TilePosition(UINT32 tileX, UINT32 tileY, UINT32& left, UINT32& top, UINT32& w, UINT32& h) const; + void TileIndex(bool topLeft, UINT32 xPos, UINT32 yPos, UINT32& tileX, UINT32& tileY, UINT32& x, UINT32& y) const; + const PGFRect& GetAlignedROI() const { return m_ROI; } + void SetNTiles(UINT32 nTiles) { m_nTiles = nTiles; } + void SetAlignedROI(const PGFRect& roi); + void InitBuffPos(UINT32 left = 0, UINT32 top = 0) { m_dataPos = top*BufferWidth() + left; ASSERT(m_dataPos < m_size); } +#else + void InitBuffPos() { m_dataPos = 0; } +#endif + +private: + UINT32 m_width; ///< width in pixels + UINT32 m_height; ///< height in pixels + UINT32 m_size; ///< size of data buffer m_data + int m_level; ///< recursion level + Orientation m_orientation; ///< 0=LL, 1=HL, 2=LH, 3=HH L=lowpass filtered, H=highpass filterd + UINT32 m_dataPos; ///< current position in m_data + DataT* m_data; ///< buffer + +#ifdef __PGFROISUPPORT__ + PGFRect m_ROI; ///< region of interest (block aligned) + UINT32 m_nTiles; ///< number of tiles in one dimension in this subband +#endif +}; + +#endif //PGF_SUBBAND_H diff --git a/offline/pgfutils/libpgf/WaveletTransform.cpp b/offline/pgfutils/libpgf/WaveletTransform.cpp new file mode 100644 index 0000000..d534e8a --- /dev/null +++ b/offline/pgfutils/libpgf/WaveletTransform.cpp @@ -0,0 +1,570 @@ +/* + * The Progressive Graphics File; http://www.libpgf.org + * + * $Date: 2006-05-18 16:03:32 +0200 (Do, 18 Mai 2006) $ + * $Revision: 194 $ + * + * 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 WaveletTransform.cpp +/// @brief PGF wavelet transform class implementation +/// @author C. Stamm + +#include "WaveletTransform.h" + +#define c1 1 // best value 1 +#define c2 2 // best value 2 + +////////////////////////////////////////////////////////////////////////// +// Constructor: Constructs a wavelet transform pyramid of given size and levels. +// @param width The width of the original image (at level 0) in pixels +// @param height The height of the original image (at level 0) in pixels +// @param levels The number of levels (>= 0) +// @param data Input data of subband LL at level 0 +CWaveletTransform::CWaveletTransform(UINT32 width, UINT32 height, int levels, DataT* data) +: +#ifdef __PGFROISUPPORT__ + m_indices(nullptr), +#endif + m_nLevels(levels + 1) // m_nLevels in CPGFImage determines the number of FWT steps; this.m_nLevels determines the number subband-planes +, m_subband(nullptr) +{ + ASSERT(m_nLevels > 0 && m_nLevels <= MaxLevel + 1); + InitSubbands(width, height, data); +} + +///////////////////////////////////////////////////////////////////// +// Initialize size subbands on all levels +void CWaveletTransform::InitSubbands(UINT32 width, UINT32 height, DataT* data) { + if (m_subband) Destroy(); + + // create subbands + m_subband = new CSubband[m_nLevels][NSubbands]; + + // init subbands + UINT32 loWidth = width; + UINT32 hiWidth = width; + UINT32 loHeight = height; + UINT32 hiHeight = height; + + for (int level = 0; level < m_nLevels; level++) { + m_subband[level][LL].Initialize(loWidth, loHeight, level, LL); // LL + m_subband[level][HL].Initialize(hiWidth, loHeight, level, HL); // HL + m_subband[level][LH].Initialize(loWidth, hiHeight, level, LH); // LH + m_subband[level][HH].Initialize(hiWidth, hiHeight, level, HH); // HH + hiWidth = loWidth >> 1; hiHeight = loHeight >> 1; + loWidth = (loWidth + 1) >> 1; loHeight = (loHeight + 1) >> 1; + } + if (data) { + m_subband[0][LL].SetBuffer(data); + } +} + +////////////////////////////////////////////////////////////////////////// +// Compute fast forward wavelet transform of LL subband at given level and +// stores result in all 4 subbands of level + 1. +// Wavelet transform used in writing a PGF file +// Forward Transform of srcBand and split and store it into subbands on destLevel +// low pass filter at even positions: 1/8[-1, 2, (6), 2, -1] +// high pass filter at odd positions: 1/4[-2, (4), -2] +// @param level A wavelet transform pyramid level (>= 0 && < Levels()) +// @param quant A quantization value (linear scalar quantization) +// @return error in case of a memory allocation problem +OSError CWaveletTransform::ForwardTransform(int level, int quant) { + ASSERT(level >= 0 && level < m_nLevels - 1); + const int destLevel = level + 1; + ASSERT(m_subband[destLevel]); + CSubband* srcBand = &m_subband[level][LL]; ASSERT(srcBand); + const UINT32 width = srcBand->GetWidth(); + const UINT32 height = srcBand->GetHeight(); + DataT* src = srcBand->GetBuffer(); ASSERT(src); + DataT *row0, *row1, *row2, *row3; + + // Allocate memory for next transform level + for (int i=0; i < NSubbands; i++) { + if (!m_subband[destLevel][i].AllocMemory()) return InsufficientMemory; + } + + if (height >= FilterSize) { // changed from FilterSizeH to FilterSize + // top border handling + row0 = src; row1 = row0 + width; row2 = row1 + width; + ForwardRow(row0, width); + ForwardRow(row1, width); + ForwardRow(row2, width); + for (UINT32 k=0; k < width; k++) { + row1[k] -= ((row0[k] + row2[k] + c1) >> 1); // high pass + row0[k] += ((row1[k] + c1) >> 1); // low pass + } + InterleavedToSubbands(destLevel, row0, row1, width); + row0 = row1; row1 = row2; row2 += width; row3 = row2 + width; + + // middle part + for (UINT32 i=3; i < height-1; i += 2) { + ForwardRow(row2, width); + ForwardRow(row3, width); + for (UINT32 k=0; k < width; k++) { + row2[k] -= ((row1[k] + row3[k] + c1) >> 1); // high pass filter + row1[k] += ((row0[k] + row2[k] + c2) >> 2); // low pass filter + } + InterleavedToSubbands(destLevel, row1, row2, width); + row0 = row2; row1 = row3; row2 = row3 + width; row3 = row2 + width; + } + + // bottom border handling + if (height & 1) { + for (UINT32 k=0; k < width; k++) { + row1[k] += ((row0[k] + c1) >> 1); // low pass + } + InterleavedToSubbands(destLevel, row1, nullptr, width); + row0 = row1; row1 += width; + } else { + ForwardRow(row2, width); + for (UINT32 k=0; k < width; k++) { + row2[k] -= row1[k]; // high pass + row1[k] += ((row0[k] + row2[k] + c2) >> 2); // low pass + } + InterleavedToSubbands(destLevel, row1, row2, width); + row0 = row1; row1 = row2; row2 += width; + } + } else { + // if height is too small + row0 = src; row1 = row0 + width; + // first part + for (UINT32 k=0; k < height; k += 2) { + ForwardRow(row0, width); + ForwardRow(row1, width); + InterleavedToSubbands(destLevel, row0, row1, width); + row0 += width << 1; row1 += width << 1; + } + // bottom + if (height & 1) { + InterleavedToSubbands(destLevel, row0, nullptr, width); + } + } + + if (quant > 0) { + // subband quantization (without LL) + for (int i=1; i < NSubbands; i++) { + m_subband[destLevel][i].Quantize(quant); + } + // LL subband quantization + if (destLevel == m_nLevels - 1) { + m_subband[destLevel][LL].Quantize(quant); + } + } + + // free source band + srcBand->FreeMemory(); + return NoError; +} + +////////////////////////////////////////////////////////////// +// Forward transform one row +// low pass filter at even positions: 1/8[-1, 2, (6), 2, -1] +// high pass filter at odd positions: 1/4[-2, (4), -2] +void CWaveletTransform::ForwardRow(DataT* src, UINT32 width) { + if (width >= FilterSize) { + UINT32 i = 3; + + // left border handling + src[1] -= ((src[0] + src[2] + c1) >> 1); // high pass + src[0] += ((src[1] + c1) >> 1); // low pass + + // middle part + for (; i < width-1; i += 2) { + src[i] -= ((src[i-1] + src[i+1] + c1) >> 1); // high pass + src[i-1] += ((src[i-2] + src[i] + c2) >> 2); // low pass + } + + // right border handling + if (width & 1) { + src[i-1] += ((src[i-2] + c1) >> 1); // low pass + } else { + src[i] -= src[i-1]; // high pass + src[i-1] += ((src[i-2] + src[i] + c2) >> 2); // low pass + } + } +} + +///////////////////////////////////////////////////////////////// +// Copy transformed and interleaved (L,H,L,H,...) rows loRow and hiRow to subbands LL,HL,LH,HH +void CWaveletTransform::InterleavedToSubbands(int destLevel, DataT* loRow, DataT* hiRow, UINT32 width) { + const UINT32 wquot = width >> 1; + const bool wrem = (width & 1); + CSubband &ll = m_subband[destLevel][LL], &hl = m_subband[destLevel][HL]; + CSubband &lh = m_subband[destLevel][LH], &hh = m_subband[destLevel][HH]; + + if (hiRow) { + for (UINT32 i=0; i < wquot; i++) { + ll.WriteBuffer(*loRow++); // first access, than increment + hl.WriteBuffer(*loRow++); + lh.WriteBuffer(*hiRow++); // first access, than increment + hh.WriteBuffer(*hiRow++); + } + if (wrem) { + ll.WriteBuffer(*loRow); + lh.WriteBuffer(*hiRow); + } + } else { + for (UINT32 i=0; i < wquot; i++) { + ll.WriteBuffer(*loRow++); // first access, than increment + hl.WriteBuffer(*loRow++); + } + if (wrem) ll.WriteBuffer(*loRow); + } +} + +////////////////////////////////////////////////////////////////////////// +// Compute fast inverse wavelet transform of all 4 subbands of given level and +// stores result in LL subband of level - 1. +// Inverse wavelet transform used in reading a PGF file +// Inverse Transform srcLevel and combine to destBand +// low-pass coefficients at even positions, high-pass coefficients at odd positions +// inverse filter for even positions: 1/4[-1, (4), -1] +// inverse filter for odd positions: 1/8[-1, 4, (6), 4, -1] +// @param srcLevel A wavelet transform pyramid level (> 0 && <= Levels()) +// @param w [out] A pointer to the returned width of subband LL (in pixels) +// @param h [out] A pointer to the returned height of subband LL (in pixels) +// @param data [out] A pointer to the returned array of image data +// @return error in case of a memory allocation problem +OSError CWaveletTransform::InverseTransform(int srcLevel, UINT32* w, UINT32* h, DataT** data) { + ASSERT(srcLevel > 0 && srcLevel < m_nLevels); + const int destLevel = srcLevel - 1; + ASSERT(m_subband[destLevel]); + CSubband* destBand = &m_subband[destLevel][LL]; + UINT32 width, height; + + // allocate memory for the results of the inverse transform + if (!destBand->AllocMemory()) return InsufficientMemory; + DataT *origin = destBand->GetBuffer(), *row0, *row1, *row2, *row3; + +#ifdef __PGFROISUPPORT__ + PGFRect destROI = destBand->GetAlignedROI(); + const UINT32 destWidth = destROI.Width(); // destination buffer width + const UINT32 destHeight = destROI.Height(); // destination buffer height + width = destWidth; // destination working width + height = destHeight; // destination working height + + // update destination ROI + if (destROI.top & 1) { + destROI.top++; + origin += destWidth; + height--; + } + if (destROI.left & 1) { + destROI.left++; + origin++; + width--; + } + + // init source buffer position + const UINT32 leftD = destROI.left >> 1; + const UINT32 left0 = m_subband[srcLevel][LL].GetAlignedROI().left; + const UINT32 left1 = m_subband[srcLevel][HL].GetAlignedROI().left; + const UINT32 topD = destROI.top >> 1; + const UINT32 top0 = m_subband[srcLevel][LL].GetAlignedROI().top; + const UINT32 top1 = m_subband[srcLevel][LH].GetAlignedROI().top; + ASSERT(m_subband[srcLevel][LH].GetAlignedROI().left == left0); + ASSERT(m_subband[srcLevel][HH].GetAlignedROI().left == left1); + ASSERT(m_subband[srcLevel][HL].GetAlignedROI().top == top0); + ASSERT(m_subband[srcLevel][HH].GetAlignedROI().top == top1); + + UINT32 srcOffsetX[2] = { 0, 0 }; + UINT32 srcOffsetY[2] = { 0, 0 }; + + if (leftD >= __max(left0, left1)) { + srcOffsetX[0] = leftD - left0; + srcOffsetX[1] = leftD - left1; + } else { + if (left0 <= left1) { + const UINT32 dx = (left1 - leftD) << 1; + destROI.left += dx; + origin += dx; + width -= dx; + srcOffsetX[0] = left1 - left0; + } else { + const UINT32 dx = (left0 - leftD) << 1; + destROI.left += dx; + origin += dx; + width -= dx; + srcOffsetX[1] = left0 - left1; + } + } + if (topD >= __max(top0, top1)) { + srcOffsetY[0] = topD - top0; + srcOffsetY[1] = topD - top1; + } else { + if (top0 <= top1) { + const UINT32 dy = (top1 - topD) << 1; + destROI.top += dy; + origin += dy*destWidth; + height -= dy; + srcOffsetY[0] = top1 - top0; + } else { + const UINT32 dy = (top0 - topD) << 1; + destROI.top += dy; + origin += dy*destWidth; + height -= dy; + srcOffsetY[1] = top0 - top1; + } + } + + m_subband[srcLevel][LL].InitBuffPos(srcOffsetX[0], srcOffsetY[0]); + m_subband[srcLevel][HL].InitBuffPos(srcOffsetX[1], srcOffsetY[0]); + m_subband[srcLevel][LH].InitBuffPos(srcOffsetX[0], srcOffsetY[1]); + m_subband[srcLevel][HH].InitBuffPos(srcOffsetX[1], srcOffsetY[1]); + +#else + width = destBand->GetWidth(); + height = destBand->GetHeight(); + PGFRect destROI(0, 0, width, height); + const UINT32 destWidth = width; // destination buffer width + const UINT32 destHeight = height; // destination buffer height + + // init source buffer position + for (int i = 0; i < NSubbands; i++) { + m_subband[srcLevel][i].InitBuffPos(); + } +#endif + + if (destHeight >= FilterSize) { // changed from FilterSizeH to FilterSize + // top border handling + row0 = origin; row1 = row0 + destWidth; + SubbandsToInterleaved(srcLevel, row0, row1, width); + for (UINT32 k = 0; k < width; k++) { + row0[k] -= ((row1[k] + c1) >> 1); // even + } + + // middle part + row2 = row1 + destWidth; row3 = row2 + destWidth; + for (UINT32 i = destROI.top + 2; i < destROI.bottom - 1; i += 2) { + SubbandsToInterleaved(srcLevel, row2, row3, width); + for (UINT32 k = 0; k < width; k++) { + row2[k] -= ((row1[k] + row3[k] + c2) >> 2); // even + row1[k] += ((row0[k] + row2[k] + c1) >> 1); // odd + } + InverseRow(row0, width); + InverseRow(row1, width); + row0 = row2; row1 = row3; row2 = row1 + destWidth; row3 = row2 + destWidth; + } + + // bottom border handling + if (height & 1) { + SubbandsToInterleaved(srcLevel, row2, nullptr, width); + for (UINT32 k = 0; k < width; k++) { + row2[k] -= ((row1[k] + c1) >> 1); // even + row1[k] += ((row0[k] + row2[k] + c1) >> 1); // odd + } + InverseRow(row0, width); + InverseRow(row1, width); + InverseRow(row2, width); + row0 = row1; row1 = row2; row2 += destWidth; + } else { + for (UINT32 k = 0; k < width; k++) { + row1[k] += row0[k]; + } + InverseRow(row0, width); + InverseRow(row1, width); + row0 = row1; row1 += destWidth; + } + } else { + // height is too small + row0 = origin; row1 = row0 + destWidth; + // first part + for (UINT32 k = 0; k < height; k += 2) { + SubbandsToInterleaved(srcLevel, row0, row1, width); + InverseRow(row0, width); + InverseRow(row1, width); + row0 += destWidth << 1; row1 += destWidth << 1; + } + // bottom + if (height & 1) { + SubbandsToInterleaved(srcLevel, row0, nullptr, width); + InverseRow(row0, width); + } + } + + // free memory of the current srcLevel + for (int i = 0; i < NSubbands; i++) { + m_subband[srcLevel][i].FreeMemory(); + } + + // return info + *w = destWidth; + *h = destHeight; + *data = destBand->GetBuffer(); + return NoError; +} + +////////////////////////////////////////////////////////////////////// +// Inverse Wavelet Transform of one row +// low-pass coefficients at even positions, high-pass coefficients at odd positions +// inverse filter for even positions: 1/4[-1, (4), -1] +// inverse filter for odd positions: 1/8[-1, 4, (6), 4, -1] +void CWaveletTransform::InverseRow(DataT* dest, UINT32 width) { + if (width >= FilterSize) { + UINT32 i = 2; + + // left border handling + dest[0] -= ((dest[1] + c1) >> 1); // even + + // middle part + for (; i < width - 1; i += 2) { + dest[i] -= ((dest[i-1] + dest[i+1] + c2) >> 2); // even + dest[i-1] += ((dest[i-2] + dest[i] + c1) >> 1); // odd + } + + // right border handling + if (width & 1) { + dest[i] -= ((dest[i-1] + c1) >> 1); // even + dest[i-1] += ((dest[i-2] + dest[i] + c1) >> 1); // odd + } else { + dest[i-1] += dest[i-2]; // odd + } + } +} + +/////////////////////////////////////////////////////////////////// +// Copy transformed coefficients from subbands LL,HL,LH,HH to interleaved format (L,H,L,H,...) +void CWaveletTransform::SubbandsToInterleaved(int srcLevel, DataT* loRow, DataT* hiRow, UINT32 width) { + const UINT32 wquot = width >> 1; + const bool wrem = (width & 1); + CSubband &ll = m_subband[srcLevel][LL], &hl = m_subband[srcLevel][HL]; + CSubband &lh = m_subband[srcLevel][LH], &hh = m_subband[srcLevel][HH]; + + if (hiRow) { + #ifdef __PGFROISUPPORT__ + const bool storePos = wquot < ll.BufferWidth(); + UINT32 llPos = 0, hlPos = 0, lhPos = 0, hhPos = 0; + + if (storePos) { + // save current src buffer positions + llPos = ll.GetBuffPos(); + hlPos = hl.GetBuffPos(); + lhPos = lh.GetBuffPos(); + hhPos = hh.GetBuffPos(); + } + #endif + + for (UINT32 i=0; i < wquot; i++) { + *loRow++ = ll.ReadBuffer();// first access, than increment + *loRow++ = hl.ReadBuffer();// first access, than increment + *hiRow++ = lh.ReadBuffer();// first access, than increment + *hiRow++ = hh.ReadBuffer();// first access, than increment + } + + if (wrem) { + *loRow++ = ll.ReadBuffer();// first access, than increment + *hiRow++ = lh.ReadBuffer();// first access, than increment + } + + #ifdef __PGFROISUPPORT__ + if (storePos) { + // increment src buffer positions + ll.IncBuffRow(llPos); + hl.IncBuffRow(hlPos); + lh.IncBuffRow(lhPos); + hh.IncBuffRow(hhPos); + } + #endif + + } else { + #ifdef __PGFROISUPPORT__ + const bool storePos = wquot < ll.BufferWidth(); + UINT32 llPos = 0, hlPos = 0; + + if (storePos) { + // save current src buffer positions + llPos = ll.GetBuffPos(); + hlPos = hl.GetBuffPos(); + } + #endif + + for (UINT32 i=0; i < wquot; i++) { + *loRow++ = ll.ReadBuffer();// first access, than increment + *loRow++ = hl.ReadBuffer();// first access, than increment + } + if (wrem) *loRow++ = ll.ReadBuffer(); + + #ifdef __PGFROISUPPORT__ + if (storePos) { + // increment src buffer positions + ll.IncBuffRow(llPos); + hl.IncBuffRow(hlPos); + } + #endif + } +} + +#ifdef __PGFROISUPPORT__ +////////////////////////////////////////////////////////////////////// +/// Compute and store ROIs for nLevels +/// @param roi rectangular region of interest at level 0 +void CWaveletTransform::SetROI(PGFRect roi) { + const UINT32 delta = (FilterSize >> 1) << m_nLevels; + + // create tile indices + delete[] m_indices; + m_indices = new PGFRect[m_nLevels]; + + // enlarge rect: add margin + roi.left = (roi.left > delta) ? roi.left - delta : 0; + roi.top = (roi.top > delta) ? roi.top - delta : 0; + roi.right += delta; + roi.bottom += delta; + + for (int l = 0; l < m_nLevels; l++) { + PGFRect alignedROI; + PGFRect& indices = m_indices[l]; + UINT32 nTiles = GetNofTiles(l); + CSubband& subband = m_subband[l][LL]; + + // use roi to determine the necessary tile indices (for all subbands the same) and aligned ROI for LL subband + subband.SetNTiles(nTiles); // must be called before TileIndex() + subband.TileIndex(true, roi.left, roi.top, indices.left, indices.top, alignedROI.left, alignedROI.top); + subband.TileIndex(false, roi.right, roi.bottom, indices.right, indices.bottom, alignedROI.right, alignedROI.bottom); + subband.SetAlignedROI(alignedROI); + ASSERT(l == 0 || + (m_indices[l-1].left >= 2*m_indices[l].left && + m_indices[l-1].top >= 2*m_indices[l].top && + m_indices[l-1].right <= 2*m_indices[l].right && + m_indices[l-1].bottom <= 2*m_indices[l].bottom)); + + // determine aligned ROI of other three subbands + PGFRect aroi; + UINT32 w, h; + for (int b = 1; b < NSubbands; b++) { + CSubband& sb = m_subband[l][b]; + sb.SetNTiles(nTiles); // must be called before TilePosition() + sb.TilePosition(indices.left, indices.top, aroi.left, aroi.top, w, h); + sb.TilePosition(indices.right - 1, indices.bottom - 1, aroi.right, aroi.bottom, w, h); + aroi.right += w; + aroi.bottom += h; + sb.SetAlignedROI(aroi); + } + + // use aligned ROI of LL subband for next level + roi.left = alignedROI.left >> 1; + roi.top = alignedROI.top >> 1; + roi.right = (alignedROI.right + 1) >> 1; + roi.bottom = (alignedROI.bottom + 1) >> 1; + } +} + +#endif // __PGFROISUPPORT__ diff --git a/offline/pgfutils/libpgf/WaveletTransform.h b/offline/pgfutils/libpgf/WaveletTransform.h new file mode 100644 index 0000000..6fc7468 --- /dev/null +++ b/offline/pgfutils/libpgf/WaveletTransform.h @@ -0,0 +1,163 @@ +/* + * The Progressive Graphics File; http://www.libpgf.org + * + * $Date: 2006-05-18 16:03:32 +0200 (Do, 18 Mai 2006) $ + * $Revision: 194 $ + * + * 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 WaveletTransform.h +/// @brief PGF wavelet transform class +/// @author C. Stamm + +#ifndef PGF_WAVELETTRANSFORM_H +#define PGF_WAVELETTRANSFORM_H + +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wignored-qualifiers" +#endif + +#if defined(__APPLE__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wignored-qualifiers" +#endif + +#include "PGFtypes.h" +#include "Subband.h" + +////////////////////////////////////////////////////////////////////// +// Constants +const UINT32 FilterSizeL = 5; ///< number of coefficients of the low pass filter +const UINT32 FilterSizeH = 3; ///< number of coefficients of the high pass filter +const UINT32 FilterSize = __max(FilterSizeL, FilterSizeH); + +#ifdef __PGFROISUPPORT__ +////////////////////////////////////////////////////////////////////// +/// PGF ROI and tile support. This is a helper class for CWaveletTransform. +/// @author C. Stamm +/// @brief ROI indices +class CRoiIndices { +}; +#endif //__PGFROISUPPORT__ + + +////////////////////////////////////////////////////////////////////// +/// PGF wavelet transform class. +/// @author C. Stamm, R. Spuler +/// @brief PGF wavelet transform +class CWaveletTransform { + friend class CSubband; + +public: + ////////////////////////////////////////////////////////////////////// + /// Constructor: Constructs a wavelet transform pyramid of given size and levels. + /// @param width The width of the original image (at level 0) in pixels + /// @param height The height of the original image (at level 0) in pixels + /// @param levels The number of levels (>= 0) + /// @param data Input data of subband LL at level 0 + CWaveletTransform(UINT32 width, UINT32 height, int levels, DataT* data = nullptr); + + ////////////////////////////////////////////////////////////////////// + /// Destructor + ~CWaveletTransform() { Destroy(); } + + ////////////////////////////////////////////////////////////////////// + /// Compute fast forward wavelet transform of LL subband at given level and + /// stores result in all 4 subbands of level + 1. + /// @param level A wavelet transform pyramid level (>= 0 && < Levels()) + /// @param quant A quantization value (linear scalar quantization) + /// @return error in case of a memory allocation problem + OSError ForwardTransform(int level, int quant); + + ////////////////////////////////////////////////////////////////////// + /// Compute fast inverse wavelet transform of all 4 subbands of given level and + /// stores result in LL subband of level - 1. + /// @param level A wavelet transform pyramid level (> 0 && <= Levels()) + /// @param width A pointer to the returned width of subband LL (in pixels) + /// @param height A pointer to the returned height of subband LL (in pixels) + /// @param data A pointer to the returned array of image data + /// @return error in case of a memory allocation problem + OSError InverseTransform(int level, UINT32* width, UINT32* height, DataT** data); + + ////////////////////////////////////////////////////////////////////// + /// Get pointer to one of the 4 subband at a given level. + /// @param level A wavelet transform pyramid level (>= 0 && <= Levels()) + /// @param orientation A quarter of the subband (LL, LH, HL, HH) + CSubband* GetSubband(int level, Orientation orientation) { + ASSERT(level >= 0 && level < m_nLevels); + return &m_subband[level][orientation]; + } + +#ifdef __PGFROISUPPORT__ + ////////////////////////////////////////////////////////////////////// + /// Compute and store ROIs for nLevels + /// @param rect rectangular region of interest (ROI) at level 0 + void SetROI(PGFRect rect); + + ////////////////////////////////////////////////////////////////////// + /// Checks the relevance of a given tile at given level. + /// @param level A valid subband level. + /// @param tileX x-index of the given tile + /// @param tileY y-index of the given tile + const bool TileIsRelevant(int level, UINT32 tileX, UINT32 tileY) const { ASSERT(m_indices); ASSERT(level >= 0 && level < m_nLevels); return m_indices[level].IsInside(tileX, tileY); } + + ////////////////////////////////////////////////////////////////////// + /// Get number of tiles in x- or y-direction at given level. + /// This number is independent of the given ROI. + /// @param level A valid subband level. + UINT32 GetNofTiles(int level) const { ASSERT(level >= 0 && level < m_nLevels); return 1 << (m_nLevels - level - 1); } + + ////////////////////////////////////////////////////////////////////// + /// Return ROI at given level. + /// @param level A valid subband level. + const PGFRect& GetAlignedROI(int level) const { return m_subband[level][LL].GetAlignedROI(); } + +#endif // __PGFROISUPPORT__ + +private: + void Destroy() { + delete[] m_subband; m_subband = nullptr; + #ifdef __PGFROISUPPORT__ + delete[] m_indices; m_indices = nullptr; + #endif + } + void InitSubbands(UINT32 width, UINT32 height, DataT* data); + void ForwardRow(DataT* buff, UINT32 width); + void InverseRow(DataT* buff, UINT32 width); + void InterleavedToSubbands(int destLevel, DataT* loRow, DataT* hiRow, UINT32 width); + void SubbandsToInterleaved(int srcLevel, DataT* loRow, DataT* hiRow, UINT32 width); + +#ifdef __PGFROISUPPORT__ + PGFRect *m_indices; ///< array of length m_nLevels of tile indices +#endif //__PGFROISUPPORT__ + + int m_nLevels; ///< number of LL levels: one more than header.nLevels in PGFimage + CSubband (*m_subband)[NSubbands]; ///< quadtree of subbands: LL HL LH HH +}; + +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + +#if defined(__APPLE__) +#pragma clang diagnostic pop +#endif + +#endif //PGF_WAVELETTRANSFORM_H diff --git a/offline/pgfutils/pgfutils.cpp b/offline/pgfutils/pgfutils.cpp new file mode 100644 index 0000000..7272cae --- /dev/null +++ b/offline/pgfutils/pgfutils.cpp @@ -0,0 +1,471 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2009-05-29 + * Description : static helper methods for PGF image format. + * + * Copyright (C) 2009-2019 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#include "pgfutils.h" + +// C Ansi includes + +extern "C" +{ +#include +#include +#include +#include +} + +#include + +// Qt includes + +#include +#include +#include +#include + +// Windows includes + +#ifdef Q_OS_WIN32 +# include +#endif + +// LibPGF includes + +#if defined(Q_CC_CLANG) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wkeyword-macro" +#endif + +#include + +#if defined(Q_CC_CLANG) +# pragma clang diagnostic pop +#endif + +// Private method +bool writePGFImageDataToStream(const QImage& image, + CPGFStream& stream, + int quality, + UINT32& nWrittenBytes, + bool verbose); + +bool readPGFImageData(const QByteArray& data, + QImage& img, + bool verbose) +{ + try + { + if (data.isEmpty()) + { + std::cout << "PGFUtils: PGF image data to decode : size is null"; + return false; + } + + CPGFMemoryStream stream((UINT8*)data.data(), (size_t)data.size()); + + if (verbose) + std::cout << "PGFUtils: image data stream size is : " << stream.GetSize(); + + CPGFImage pgfImg; + // NOTE: see bug #273765 : Loading PGF thumbs with OpenMP support through a separated thread do not work properly with libppgf 6.11.24 + pgfImg.ConfigureDecoder(false); + + pgfImg.Open(&stream); + + if (verbose) + std::cout << "PGFUtils: PGF image is open"; + + if (pgfImg.Channels() != 4) + { + std::cout << "PGFUtils: PGF channels not supported"; + return false; + } + + img = QImage(pgfImg.Width(), pgfImg.Height(), QImage::Format_ARGB32); + pgfImg.Read(); + + if (verbose) + std::cout << "PGFUtils: PGF image is read"; + + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) + { + int map[] = {3, 2, 1, 0}; + pgfImg.GetBitmap(img.bytesPerLine(), (UINT8*)img.bits(), img.depth(), map); + } + else + { + int map[] = {0, 1, 2, 3}; + pgfImg.GetBitmap(img.bytesPerLine(), (UINT8*)img.bits(), img.depth(), map); + } + + if (verbose) + std::cout << "PGFUtils: PGF image is decoded"; + } + catch (IOException& e) + { + int err = e.error; + + if (err >= AppError) + { + err -= AppError; + } + + std::cout << "PGFUtils: Error running libpgf (" << err << ")!"; + return false; + } + + return true; +} + +bool writePGFImageFile(const QImage& image, + const QString& filePath, + int quality, + bool verbose) +{ +#ifdef Q_OS_WIN32 +#ifdef UNICODE + HANDLE fd = CreateFile((LPCWSTR)(QFile::encodeName(filePath).constData()), GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0); +#else + HANDLE fd = CreateFile(QFile::encodeName(filePath).constData(), GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0); +#endif + + if (fd == INVALID_HANDLE_VALUE) + { + qCDebug(DIGIKAM_GENERAL_LOG) << "PGFUtils: Error: Could not open destination file."; + return false; + } + +#elif defined(__POSIX__) + int fd = QT_OPEN(QFile::encodeName(filePath).constData(), + O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + if (fd == -1) + { + std::cout << "PGFUtils: Error: Could not open destination file."; + return false; + } +#endif + + CPGFFileStream stream(fd); + UINT32 nWrittenBytes = 0; + bool ret = writePGFImageDataToStream(image, stream, quality, nWrittenBytes, verbose); + + if (!nWrittenBytes) + { + std::cout << "PGFUtils: Written PGF file : data size is null"; + ret = false; + } + else + { + if (verbose) + std::cout << "PGFUtils: file size written : " << nWrittenBytes; + } + +#ifdef Q_OS_WIN32 + CloseHandle(fd); +#else + close(fd); +#endif + + return ret; +} + +bool writePGFImageData(const QImage& image, QByteArray& data, int quality, bool verbose) +{ + try + { + // We will use uncompressed image bytes size to allocate PGF stream in memory. In all case, due to PGF compression ratio, + // PGF data will be so far lesser than image raw size. +#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) + int rawSize = image.sizeInBytes(); +#else + int rawSize = image.byteCount(); +#endif + + CPGFMemoryStream stream(rawSize); + + if (verbose) + std::cout << "PGFUtils: PGF stream memory allocation in bytes: " << rawSize; + + UINT32 nWrittenBytes = 0; + bool ret = writePGFImageDataToStream(image, stream, quality, nWrittenBytes, verbose); + int pgfsize = +#ifdef PGFCodecVersionID +# if PGFCodecVersionID == 0x061224 + // Wrap around libpgf 6.12.24 about CPGFMemoryStream bytes size generated to make PGF file data. + // It miss 16 bytes at end. This solution fix the problem. Problem have been fixed in 6.12.27. + nWrittenBytes + 16; +# else + nWrittenBytes; +# endif +#else + nWrittenBytes; +#endif + + data = QByteArray((const char*)stream.GetBuffer(), pgfsize); + + if (!pgfsize) + { + std::cout << "PGFUtils: Encoded PGF image : data size is null"; + ret = false; + } + else + { + if (verbose) + std::cout << "PGFUtils: data size written : " << pgfsize; + } + + return ret; + } + catch (IOException& e) + { + int err = e.error; + + if (err >= AppError) + { + err -= AppError; + } + + std::cout << "PGFUtils: Error running libpgf (" << err << ")!"; + return false; + } +} + +bool writePGFImageDataToStream(const QImage& image, + CPGFStream& stream, + int quality, + UINT32& nWrittenBytes, + bool verbose) +{ + try + { + if (image.isNull()) + { + std::cout << "PGFUtils: Thumb image is null"; + return false; + } + + QImage img; + + // Convert image with Alpha channel. + if (image.format() != QImage::Format_ARGB32) + { + img = image.convertToFormat(QImage::Format_ARGB32); + + if (verbose) + std::cout << "PGFUtils: RGB => ARGB"; + } + else + { + img = image; + } + + CPGFImage pgfImg; + PGFHeader header; + header.width = img.width(); + header.height = img.height(); + header.nLevels = 0; // Auto. + header.quality = quality; + header.bpp = img.depth(); + header.channels = 4; + header.mode = ImageModeRGBA; + header.usedBitsPerChannel = 0; // Auto + +#ifdef PGFCodecVersionID +# if PGFCodecVersionID < 0x061142 + header.background.rgbtBlue = 0; + header.background.rgbtGreen = 0; + header.background.rgbtRed = 0; +# endif +#endif + pgfImg.SetHeader(header); + + // NOTE: see bug #273765 : Loading PGF thumbs with OpenMP support through a separated thread do not work properly with libppgf 6.11.24 + pgfImg.ConfigureEncoder(false); + + if (verbose) + { + std::cout << "PGFUtils: PGF image settings:"; + std::cout << "PGFUtils: width: " << header.width; + std::cout << "PGFUtils: height: " << header.height; + std::cout << "PGFUtils: nLevels: " << header.nLevels; + std::cout << "PGFUtils: quality: " << header.quality; + std::cout << "PGFUtils: bpp: " << header.bpp; + std::cout << "PGFUtils: channels: " << header.channels; + std::cout << "PGFUtils: mode: " << header.mode; + std::cout << "PGFUtils: usedBitsPerChannel: " << header.usedBitsPerChannel; + } + + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) + { + int map[] = {3, 2, 1, 0}; + pgfImg.ImportBitmap(img.bytesPerLine(), (UINT8*)img.bits(), img.depth(), map); + } + else + { + int map[] = {0, 1, 2, 3}; + pgfImg.ImportBitmap(img.bytesPerLine(), (UINT8*)img.bits(), img.depth(), map); + } + + nWrittenBytes = 0; + +#ifdef PGFCodecVersionID +# if PGFCodecVersionID >= 0x061124 + pgfImg.Write(&stream, &nWrittenBytes); +# else + pgfImg.Write(&stream, 0, 0, &nWrittenBytes); +# endif +#else + pgfImg.Write(&stream, 0, 0, &nWrittenBytes); +#endif + + } + catch (IOException& e) + { + int err = e.error; + + if (err >= AppError) + { + err -= AppError; + } + + std::cout << "PGFUtils: Error running libpgf (" << err << ")!"; + return false; + } + + return true; +} + +bool loadPGFScaled(QImage& img, const QString& path, int maximumSize) +{ + FILE* const file = fopen(QFile::encodeName(path).constData(), "rb"); + + if (!file) + { + std::cout << "PGFUtils: Error: Could not open source file."; + return false; + } + + unsigned char header[3]; + + if (fread(&header, 3, 1, file) != 1) + { + fclose(file); + return false; + } + + unsigned char pgfID[3] = { 0x50, 0x47, 0x46 }; + + if (memcmp(&header[0], &pgfID, 3) != 0) + { + // not a PGF file + fclose(file); + return false; + } + + fclose(file); + + // ------------------------------------------------------------------- + // Initialize PGF API. + +#ifdef Q_OS_WIN32 +#ifdef UNICODE + HANDLE fd = CreateFile((LPCWSTR)(QFile::encodeName(path).constData()), GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0); +#else + HANDLE fd = CreateFile(QFile::encodeName(path).constData(), GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0); +#endif + + if (fd == INVALID_HANDLE_VALUE) + { + return false; + } + +#else + int fd = QT_OPEN(QFile::encodeName(path).constData(), O_RDONLY); + + if (fd == -1) + { + return false; + } + +#endif + + try + { + CPGFFileStream stream(fd); + CPGFImage pgf; + pgf.Open(&stream); + + // Try to find the right PGF level to get reduced image accordingly + // with preview size wanted. + int i = 0; + + if (pgf.Levels() > 0) + { + for (i = pgf.Levels()-1 ; i >= 0 ; --i) + { + if (qMin((int)pgf.Width(i), (int)pgf.Height(i)) >= maximumSize) + { + break; + } + } + } + + if (i < 0) + { + i = 0; + } + + pgf.Read(i); // Read PGF image at reduced level i. + img = QImage(pgf.Width(i), pgf.Height(i), QImage::Format_RGB32); + + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) + { + int map[] = {3, 2, 1, 0}; + pgf.GetBitmap(img.bytesPerLine(), (UINT8*)img.bits(), img.depth(), map); + } + else + { + int map[] = {0, 1, 2, 3}; + pgf.GetBitmap(img.bytesPerLine(), (UINT8*)img.bits(), img.depth(), map); + } + } + catch (IOException& e) + { + int err = e.error; + + if (err >= AppError) + { + err -= AppError; + } + + std::cerr << "PGFUtils: Error running libpgf (" << err << ")!"; + return false; + } + + return true; +} + +QString libPGFVersion() +{ + return (QLatin1String(PGFCodecVersion)); +} diff --git a/offline/pgfutils/pgfutils.h b/offline/pgfutils/pgfutils.h new file mode 100644 index 0000000..473d7de --- /dev/null +++ b/offline/pgfutils/pgfutils.h @@ -0,0 +1,75 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2009-05-29 + * Description : static helper methods for PGF image format. + * + * Copyright (C) 2009-2019 by Gilles Caulier + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, 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. + * + * ============================================================ */ + +#pragma once + +// Qt includes +#include +#include +#include + +/** + * PGF image data to QImage using memory stream. + * NOTE: Only use this method to manage PGF thumbnails stored in database. + */ +bool readPGFImageData(const QByteArray& data, + QImage& img, + bool verbose=false); + +/** + * QImage to PGF image data using memory stream. 'quality' argument set compression ratio: + * 0 => lossless compression, as PNG. + * 1 => Not loss less compression, wavelets based... + * 2 => ... + * 3 => ... + * 4 => Same compression ratio near than JPEG quality=85. + * Image quality is valid for thumbnails. + * >= 5 => provide artifacts due to down-sampling. Do not use it... + * NOTE: Only use this method to manage PGF thumbnails stored in database. + */ +bool writePGFImageData(const QImage& image, + QByteArray& data, + int quality, + bool verbose=false); + +/** + * QImage to PGF image data using file stream. + * Same arguments than writePGFImageData() excepted 'filePath' + * which is PGF target file path. + */ +bool writePGFImageFile(const QImage& image, + const QString& filePath, + int quality, + bool verbose=false); + +/** + * Load a reduced version of PGF file + */ +bool loadPGFScaled(QImage& img, + const QString& path, + int maximumSize); + +/** + * Return a libpgf version string + */ +QString libPGFVersion();