// 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. return0;
} if (idec->is_lossless) { return0; // 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; // 'last_start' will be NULL when 'idec->state' is < STATE_VP8_PARTS0 // and through a portion of that state (when there isn't enough data to // parse the partitions). The bitreader is only used meaningfully when // there is enough data to begin parsing partition 0. if (last_start != NULL) {
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. return0;
}
// 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.