// Copyright 2011 Google Inc. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the COPYING file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. // ----------------------------------------------------------------------------- // // Incremental decoding // // Author: somnath@google.com (Somnath Banerjee)
// In append mode, buffer allocations increase as multiples of this value. // Needs to be a power of 2. #define CHUNK_SIZE 4096 #define MAX_MB_SIZE 4096
//------------------------------------------------------------------------------ // Data structures for memory and states
// Decoding states. State normally flows as: // WEBP_HEADER->VP8_HEADER->VP8_PARTS0->VP8_DATA->DONE for a lossy image, and // WEBP_HEADER->VP8L_HEADER->VP8L_DATA->DONE for a lossless image. // If there is any error the decoder goes into state ERROR. typedefenum {
STATE_WEBP_HEADER, // All the data before that of the VP8/VP8L chunk.
STATE_VP8_HEADER, // The VP8 Frame header (within the VP8 chunk).
STATE_VP8_PARTS0,
STATE_VP8_DATA,
STATE_VP8L_HEADER,
STATE_VP8L_DATA,
STATE_DONE,
STATE_ERROR
} DecState;
// Operating state for the MemBuffer typedefenum {
MEM_MODE_NONE = 0,
MEM_MODE_APPEND,
MEM_MODE_MAP
} MemBufferMode;
// storage for partition #0 and partial data (in a rolling fashion) typedefstruct {
MemBufferMode mode_; // Operation mode
size_t start_; // start location of the data to be decoded
size_t end_; // end location
size_t buf_size_; // size of the allocated buffer
uint8_t* buf_; // We don't own this buffer in case WebPIUpdate()
size_t part0_size_; // size of partition #0 const uint8_t* part0_buf_; // buffer to store partition #0
} MemBuffer;
struct WebPIDecoder {
DecState state_; // current decoding state
WebPDecParams params_; // Params to store output info int is_lossless_; // for down-casting 'dec_'. void* dec_; // either a VP8Decoder or a VP8LDecoder instance
VP8Io io_;
MemBuffer mem_; // input memory buffer.
WebPDecBuffer output_; // output buffer (when no external one is supplied, // or if the external one has slow-memory)
WebPDecBuffer* final_output_; // Slow-memory output to copy to eventually.
size_t chunk_size_; // Compressed VP8/VP8L size extracted from Header.
int last_mb_y_; // last row reached for intra-mode decoding
};
// MB context to restore in case VP8DecodeMB() fails typedefstruct {
VP8MB left_;
VP8MB info_;
VP8BitReader token_br_;
} MBContext;
//------------------------------------------------------------------------------ // MemBuffer: incoming data handling
// Check if we need to preserve the compressed alpha data, as it may not have // been decoded yet. staticint NeedCompressedAlpha(const WebPIDecoder* const idec) { if (idec->state_ == STATE_WEBP_HEADER) { // We haven't parsed the headers yet, so we don't know whether the image is // lossy or lossless. This also means that we haven't parsed the ALPH chunk. return 0;
} if (idec->is_lossless_) { return 0; // ALPH chunk is not present for lossless images.
} else { const VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
assert(dec != NULL); // Must be true as idec->state_ != STATE_WEBP_HEADER. return (dec->alpha_data_ != NULL) && !dec->is_alpha_decoded_;
}
}
staticvoid DoRemap(WebPIDecoder* const idec, ptrdiff_t offset) {
MemBuffer* const mem = &idec->mem_; const uint8_t* const new_base = mem->buf_ + mem->start_; // note: for VP8, setting up idec->io_ is only really needed at the beginning // of the decoding, till partition #0 is complete.
idec->io_.data = new_base;
idec->io_.data_size = MemDataSize(mem);
if (idec->dec_ != NULL) { if (!idec->is_lossless_) {
VP8Decoder* const dec = (VP8Decoder*)idec->dec_; const uint32_t last_part = dec->num_parts_minus_one_; if (offset != 0) {
uint32_t p; for (p = 0; p <= last_part; ++p) {
VP8RemapBitReader(dec->parts_ + p, offset);
} // Remap partition #0 data pointer to new offset, but only in MAP // mode (in APPEND mode, partition #0 is copied into a fixed memory). if (mem->mode_ == MEM_MODE_MAP) {
VP8RemapBitReader(&dec->br_, offset);
}
}
{ const uint8_t* const last_start = dec->parts_[last_part].buf_;
VP8BitReaderSetBuffer(&dec->parts_[last_part], last_start,
mem->buf_ + mem->end_ - last_start);
} if (NeedCompressedAlpha(idec)) {
ALPHDecoder* const alph_dec = dec->alph_dec_;
dec->alpha_data_ += offset; if (alph_dec != NULL && alph_dec->vp8l_dec_ != NULL) { if (alph_dec->method_ == ALPHA_LOSSLESS_COMPRESSION) {
VP8LDecoder* const alph_vp8l_dec = alph_dec->vp8l_dec_;
assert(dec->alpha_data_size_ >= ALPHA_HEADER_LEN);
VP8LBitReaderSetBuffer(&alph_vp8l_dec->br_,
dec->alpha_data_ + ALPHA_HEADER_LEN,
dec->alpha_data_size_ - ALPHA_HEADER_LEN);
} else { // alph_dec->method_ == ALPHA_NO_COMPRESSION // Nothing special to do in this case.
}
}
}
} else { // Resize lossless bitreader
VP8LDecoder* const dec = (VP8LDecoder*)idec->dec_;
VP8LBitReaderSetBuffer(&dec->br_, new_base, MemDataSize(mem));
}
}
}
// Appends data to the end of MemBuffer->buf_. It expands the allocated memory // size if required and also updates VP8BitReader's if new memory is allocated.
WEBP_NODISCARD staticint AppendToMemBuffer(WebPIDecoder* const idec, const uint8_t* const data,
size_t data_size) {
VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
MemBuffer* const mem = &idec->mem_; constint need_compressed_alpha = NeedCompressedAlpha(idec); const uint8_t* const old_start =
(mem->buf_ == NULL) ? NULL : mem->buf_ + mem->start_; const uint8_t* const old_base =
need_compressed_alpha ? dec->alpha_data_ : old_start;
assert(mem->buf_ != NULL || mem->start_ == 0);
assert(mem->mode_ == MEM_MODE_APPEND); if (data_size > MAX_CHUNK_PAYLOAD) { // security safeguard: trying to allocate more than what the format // allows for a chunk should be considered a smoke smell. return 0;
}
// Finish setting up the decoding parameters. Will call io->setup(). if (VP8EnterCritical(dec, io) != VP8_STATUS_OK) { return IDecError(idec, dec->status_);
}
// Note: past this point, teardown() must always be called // in case of error.
idec->state_ = STATE_VP8_DATA; // Allocate memory and prepare everything. if (!VP8InitFrame(dec, io)) { return IDecError(idec, dec->status_);
} return VP8_STATUS_OK;
}
// Make sure partition #0 has been read before, to set dec to ready_. if (!dec->ready_) { return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);
} for (; dec->mb_y_ < dec->mb_h_; ++dec->mb_y_) { if (idec->last_mb_y_ != dec->mb_y_) { if (!VP8ParseIntraModeRow(&dec->br_, dec)) { // note: normally, error shouldn't occur since we already have the whole // partition0 available here in DecodeRemaining(). Reaching EOF while // reading intra modes really means a BITSTREAM_ERROR. return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);
}
idec->last_mb_y_ = dec->mb_y_;
} for (; dec->mb_x_ < dec->mb_w_; ++dec->mb_x_) {
VP8BitReader* const token_br =
&dec->parts_[dec->mb_y_ & dec->num_parts_minus_one_];
MBContext context;
SaveContext(dec, token_br, &context); if (!VP8DecodeMB(dec, token_br)) { // We shouldn't fail when MAX_MB data was available if (dec->num_parts_minus_one_ == 0 &&
MemDataSize(&idec->mem_) > MAX_MB_SIZE) { return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);
} // Synchronize the threads. if (dec->mt_method_ > 0) { if (!WebPGetWorkerInterface()->Sync(&dec->worker_)) { return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);
}
}
RestoreContext(&context, dec, token_br); return VP8_STATUS_SUSPENDED;
} // Release buffer only if there is only one partition if (dec->num_parts_minus_one_ == 0) {
idec->mem_.start_ = token_br->buf_ - idec->mem_.buf_;
assert(idec->mem_.start_ <= idec->mem_.end_);
}
}
VP8InitScanline(dec); // Prepare for next scanline
// Reconstruct, filter and emit the row. if (!VP8ProcessRow(dec, io)) { return IDecError(idec, VP8_STATUS_USER_ABORT);
}
} // Synchronize the thread and check for errors. if (!VP8ExitCritical(dec, io)) {
idec->state_ = STATE_ERROR; // prevent re-entry in IDecError return IDecError(idec, VP8_STATUS_USER_ABORT);
}
dec->ready_ = 0; return FinishDecoding(idec);
}
// Main decoding loop static VP8StatusCode IDecode(WebPIDecoder* idec) {
VP8StatusCode status = VP8_STATUS_SUSPENDED;
if (idec->state_ == STATE_WEBP_HEADER) {
status = DecodeWebPHeaders(idec);
} else { if (idec->dec_ == NULL) { return VP8_STATUS_SUSPENDED; // can't continue if we have no decoder.
}
} if (idec->state_ == STATE_VP8_HEADER) {
status = DecodeVP8FrameHeader(idec);
} if (idec->state_ == STATE_VP8_PARTS0) {
status = DecodePartition0(idec);
} if (idec->state_ == STATE_VP8_DATA) { const VP8Decoder* const dec = (VP8Decoder*)idec->dec_; if (dec == NULL) { return VP8_STATUS_SUSPENDED; // can't continue if we have no decoder.
}
status = DecodeRemaining(idec);
} if (idec->state_ == STATE_VP8L_HEADER) {
status = DecodeVP8LHeader(idec);
} if (idec->state_ == STATE_VP8L_DATA) {
status = DecodeVP8LData(idec);
} return status;
}
// Parse the bitstream's features, if requested: if (data != NULL && data_size > 0) { if (WebPGetFeatures(data, data_size, features) != VP8_STATUS_OK) { return NULL;
}
}
// Create an instance of the incremental decoder
idec = (config != NULL) ? NewDecoder(&config->output, features)
: NewDecoder(NULL, features); if (idec == NULL) { return NULL;
} // Finish initialization if (config != NULL) {
idec->params_.options = &config->options;
} return idec;
}
void WebPIDelete(WebPIDecoder* idec) { if (idec == NULL) return; if (idec->dec_ != NULL) { if (!idec->is_lossless_) { if (idec->state_ == STATE_VP8_DATA) { // Synchronize the thread, clean-up and check for errors. // TODO(vrabaud) do we care about the return result?
(void)VP8ExitCritical((VP8Decoder*)idec->dec_, &idec->io_);
}
VP8Delete((VP8Decoder*)idec->dec_);
} else {
VP8LDelete((VP8LDecoder*)idec->dec_);
}
}
ClearMemBuffer(&idec->mem_);
WebPFreeDecBuffer(&idec->output_);
WebPSafeFree(idec);
}
VP8StatusCode WebPIAppend(WebPIDecoder* idec, const uint8_t* data, size_t data_size) {
VP8StatusCode status; if (idec == NULL || data == NULL) { return VP8_STATUS_INVALID_PARAM;
}
status = IDecCheckStatus(idec); if (status != VP8_STATUS_SUSPENDED) { return status;
} // Check mixed calls between RemapMemBuffer and AppendToMemBuffer. if (!CheckMemBufferMode(&idec->mem_, MEM_MODE_APPEND)) { return VP8_STATUS_INVALID_PARAM;
} // Append data to memory buffer if (!AppendToMemBuffer(idec, data, data_size)) { return VP8_STATUS_OUT_OF_MEMORY;
} return IDecode(idec);
}
VP8StatusCode WebPIUpdate(WebPIDecoder* idec, const uint8_t* data, size_t data_size) {
VP8StatusCode status; if (idec == NULL || data == NULL) { return VP8_STATUS_INVALID_PARAM;
}
status = IDecCheckStatus(idec); if (status != VP8_STATUS_SUSPENDED) { return status;
} // Check mixed calls between RemapMemBuffer and AppendToMemBuffer. if (!CheckMemBufferMode(&idec->mem_, MEM_MODE_MAP)) { return VP8_STATUS_INVALID_PARAM;
} // Make the memory buffer point to the new buffer if (!RemapMemBuffer(idec, data, data_size)) { return VP8_STATUS_INVALID_PARAM;
} return IDecode(idec);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.