You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1055 lines
31 KiB
1055 lines
31 KiB
/* |
|
* The Progressive Graphics File; http://www.libpgf.org |
|
* |
|
* $Date: 2006-06-04 22:05:59 +0200 (So, 04 Jun 2006) $ |
|
* $Revision: 229 $ |
|
* |
|
* This file Copyright (C) 2006 xeraina GmbH, Switzerland |
|
* |
|
* This program is free software; you can redistribute it and/or |
|
* modify it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE |
|
* as published by the Free Software Foundation; either version 2.1 |
|
* of the License, or (at your option) any later version. |
|
* |
|
* This program is distributed in the hope that it will be useful, |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
* GNU General Public License for more details. |
|
* |
|
* You should have received a copy of the GNU General Public License |
|
* along with this program; if not, write to the Free Software |
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
|
*/ |
|
|
|
////////////////////////////////////////////////////////////////////// |
|
/// @file Decoder.cpp |
|
/// @brief PGF decoder class implementation |
|
/// @author C. Stamm, R. Spuler |
|
|
|
#include "Decoder.h" |
|
#ifdef TRACE |
|
#include <stdio.h> |
|
#endif |
|
|
|
#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: <wordLen>(16 bits) [ ROI ] data |
|
// ROI ::= <bufferSize>(15 bits) <eofTile>(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: <wordLen>(16 bits) ROI data |
|
// ROI ::= <bufferSize>(15 bits) <eofTile>(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 ::= <nPlanes>(5 bits) foreach(plane i): Plane[i] |
|
// Plane[i] ::= [ Sig1 | Sig2 ] [DWORD alignment] refBits |
|
// Sig1 ::= 1 <codeLen>(15 bits) codedSigAndSignBits |
|
// Sig2 ::= 0 <sigLen>(15 bits) [Sign1 | Sign2 ] [DWORD alignment] sigBits |
|
// Sign1 ::= 1 <codeLen>(15 bits) codedSignBits |
|
// Sign2 ::= 0 <signLen>(15 bits) [DWORD alignment] signBits |
|
void CDecoder::CMacroBlock::BitplaneDecode() { |
|
UINT32 bufferSize = m_header.rbh.bufferSize; ASSERT(bufferSize <= BufferSize); |
|
|
|
// clear significance vector |
|
for (UINT32 k=0; k < bufferSize; k++) { |
|
m_sigFlagVector[k] = false; |
|
} |
|
m_sigFlagVector[bufferSize] = true; // sentinel |
|
|
|
// clear output buffer |
|
for (UINT32 k=0; k < BufferSize; k++) { |
|
m_value[k] = 0; |
|
} |
|
|
|
// read number of bit planes |
|
// <nPlanes> |
|
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><codeLen><codedSigAndSignBits>_<refBits> |
|
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><sigLen> |
|
codePos++; |
|
|
|
// read sigLen |
|
sigLen = GetValueBlock(m_codeBuffer, codePos, RLblockSizeLen); ASSERT(sigLen <= MaxCodeLen); |
|
codePos += RLblockSizeLen; ASSERT(codePos < CodeBufferBitLen); |
|
|
|
// read RL code for signBits |
|
if (GetBit(m_codeBuffer, codePos)) { |
|
// RL coding is used just for signBits |
|
// <1><codeLen><codedSignBits>_<sigBits>_<refBits> |
|
codePos++; |
|
|
|
// read codeLen |
|
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><signLen>_<signBits>_<sigBits>_<refBits> |
|
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: 1<count>x |
|
// - x is 0: if a positive sign has been stored, otherwise 1 |
|
// - Read each bit from m_codeBuffer[codePos] and increment codePos. |
|
UINT32 CDecoder::CMacroBlock::ComposeBitplaneRLD(UINT32 bufferSize, DataT planeMask, UINT32 codePos, UINT32* refBits) { |
|
ASSERT(refBits); |
|
|
|
UINT32 valPos = 0, refPos = 0; |
|
UINT32 sigPos = 0, sigEnd; |
|
UINT32 k = 3; |
|
UINT32 runlen = 1 << k; // = 2^k |
|
UINT32 count = 0, rest = 0; |
|
bool set1 = false; |
|
|
|
while (valPos < bufferSize) { |
|
// search next 1 in m_sigFlagVector using searching with sentinel |
|
sigEnd = valPos; |
|
while(!m_sigFlagVector[sigEnd]) { sigEnd++; } |
|
sigEnd -= valPos; |
|
sigEnd += sigPos; |
|
|
|
while (sigPos < sigEnd) { |
|
if (rest || set1) { |
|
// rest of last run |
|
sigPos += rest; |
|
valPos += rest; |
|
rest = 0; |
|
} else { |
|
// decode significant bits |
|
if (GetBit(m_codeBuffer, codePos++)) { |
|
// extract counter and generate zero run of length count |
|
if (k > 0) { |
|
// extract counter |
|
count = GetValueBlock(m_codeBuffer, codePos, k); |
|
codePos += k; |
|
if (count > 0) { |
|
sigPos += count; |
|
valPos += count; |
|
} |
|
|
|
// adapt k (half run-length interval) |
|
k--; |
|
runlen >>= 1; |
|
} |
|
|
|
set1 = true; |
|
|
|
} else { |
|
// generate zero run of length 2^k |
|
sigPos += runlen; |
|
valPos += runlen; |
|
|
|
// adapt k (double run-length interval) |
|
if (k < WordWidth) { |
|
k++; |
|
runlen <<= 1; |
|
} |
|
} |
|
} |
|
|
|
if (sigPos < sigEnd) { |
|
if (set1) { |
|
set1 = false; |
|
|
|
// write 1 bit |
|
SetBitAtPos(valPos, planeMask); |
|
|
|
// set sign bit |
|
SetSign(valPos, GetBit(m_codeBuffer, codePos++)); |
|
|
|
// update significance flag vector |
|
m_sigFlagVector[valPos++] = true; |
|
sigPos++; |
|
} |
|
} else { |
|
rest = sigPos - sigEnd; |
|
sigPos = sigEnd; |
|
valPos -= rest; |
|
} |
|
|
|
} |
|
|
|
// refinement bit |
|
if (valPos < bufferSize) { |
|
// write one refinement bit |
|
if (GetBit(refBits, refPos)) { |
|
SetBitAtPos(valPos, planeMask); |
|
} |
|
refPos++; |
|
valPos++; |
|
} |
|
} |
|
ASSERT(sigPos <= bufferSize); |
|
ASSERT(refPos <= bufferSize); |
|
ASSERT(valPos == bufferSize); |
|
|
|
return sigPos; |
|
} |
|
|
|
//////////////////////////////////////////////////////////////////// |
|
// 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<count> |
|
UINT32 CDecoder::CMacroBlock::ComposeBitplaneRLD(UINT32 bufferSize, DataT planeMask, UINT32* sigBits, UINT32* refBits, UINT32 signPos) { |
|
ASSERT(sigBits); |
|
ASSERT(refBits); |
|
|
|
UINT32 valPos = 0, refPos = 0; |
|
UINT32 sigPos = 0, sigEnd; |
|
UINT32 zerocnt, count = 0; |
|
UINT32 k = 0; |
|
UINT32 runlen = 1 << k; // = 2^k |
|
bool signBit = false; |
|
bool zeroAfterRun = false; |
|
|
|
while (valPos < bufferSize) { |
|
// search next 1 in m_sigFlagVector using searching with sentinel |
|
sigEnd = valPos; |
|
while(!m_sigFlagVector[sigEnd]) { sigEnd++; } |
|
sigEnd -= valPos; |
|
sigEnd += sigPos; |
|
|
|
// search 1's in sigBits[sigPos..sigEnd) |
|
// these 1's are significant bits |
|
while (sigPos < sigEnd) { |
|
// search 0's |
|
zerocnt = SeekBitRange(sigBits, sigPos, sigEnd - sigPos); |
|
sigPos += zerocnt; |
|
valPos += zerocnt; |
|
if (sigPos < sigEnd) { |
|
// write bit to m_value |
|
SetBitAtPos(valPos, planeMask); |
|
|
|
// check sign bit |
|
if (count == 0) { |
|
// all 1's have been set |
|
if (zeroAfterRun) { |
|
// finish the run with a 0 |
|
signBit = false; |
|
zeroAfterRun = false; |
|
} else { |
|
// decode next sign bit |
|
if (GetBit(m_codeBuffer, signPos++)) { |
|
// generate 1's run of length 2^k |
|
count = runlen - 1; |
|
signBit = true; |
|
|
|
// adapt k (double run-length interval) |
|
if (k < WordWidth) { |
|
k++; |
|
runlen <<= 1; |
|
} |
|
} else { |
|
// extract counter and generate 1's run of length count |
|
if (k > 0) { |
|
// extract counter |
|
count = GetValueBlock(m_codeBuffer, signPos, k); |
|
signPos += k; |
|
|
|
// adapt k (half run-length interval) |
|
k--; |
|
runlen >>= 1; |
|
} |
|
if (count > 0) { |
|
count--; |
|
signBit = true; |
|
zeroAfterRun = true; |
|
} else { |
|
signBit = false; |
|
} |
|
} |
|
} |
|
} else { |
|
ASSERT(count > 0); |
|
ASSERT(signBit); |
|
count--; |
|
} |
|
|
|
// copy sign bit |
|
SetSign(valPos, signBit); |
|
|
|
// update significance flag vector |
|
m_sigFlagVector[valPos++] = true; |
|
sigPos++; |
|
} |
|
} |
|
|
|
// refinement bit |
|
if (valPos < bufferSize) { |
|
// write one refinement bit |
|
if (GetBit(refBits, refPos)) { |
|
SetBitAtPos(valPos, planeMask); |
|
} |
|
refPos++; |
|
valPos++; |
|
} |
|
} |
|
ASSERT(sigPos <= bufferSize); |
|
ASSERT(refPos <= bufferSize); |
|
ASSERT(valPos == bufferSize); |
|
|
|
return sigPos; |
|
} |
|
|
|
//////////////////////////////////////////////////////////////////// |
|
#ifdef TRACE |
|
void CDecoder::DumpBuffer() { |
|
//printf("\nDump\n"); |
|
//for (int i=0; i < BufferSize; i++) { |
|
// printf("%d", m_value[i]); |
|
//} |
|
} |
|
|
|
#if defined(__GNUC__) |
|
#pragma GCC diagnostic pop |
|
#endif |
|
|
|
#if defined(__APPLE__) |
|
#pragma clang diagnostic pop |
|
#endif |
|
|
|
#endif //TRACE
|
|
|