cosmetics & warnings
- remove some unused functions - move global arrays from data to read only section - explicitly cast malloc returns; not specifically necessary, but helps show intent - miscellaneous formatting Change-Id: Ib15fe5b37fe6c29c369ad928bdc3a7290cd13c84
This commit is contained in:
parent
b9600308e8
commit
a0b2736d79
@ -443,7 +443,7 @@ static int ReadPNG(FILE* in_file, WebPPicture* const pic, int keep_alpha) {
|
|||||||
typedef enum {
|
typedef enum {
|
||||||
PNG = 0,
|
PNG = 0,
|
||||||
JPEG,
|
JPEG,
|
||||||
UNSUPPORTED,
|
UNSUPPORTED
|
||||||
} InputFileFormat;
|
} InputFileFormat;
|
||||||
|
|
||||||
static InputFileFormat GetImageType(FILE* in_file) {
|
static InputFileFormat GetImageType(FILE* in_file) {
|
||||||
|
@ -140,38 +140,38 @@ static void WebPDataFree(WebPData* const webpdata) {
|
|||||||
memset(webpdata, 0, sizeof(*webpdata));
|
memset(webpdata, 0, sizeof(*webpdata));
|
||||||
}
|
}
|
||||||
|
|
||||||
#define RETURN_IF_ERROR(ERR_MSG) \
|
#define RETURN_IF_ERROR(ERR_MSG) \
|
||||||
if (err != WEBP_MUX_OK) { \
|
if (err != WEBP_MUX_OK) { \
|
||||||
fprintf(stderr, ERR_MSG); \
|
fprintf(stderr, ERR_MSG); \
|
||||||
return err; \
|
return err; \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define RETURN_IF_ERROR2(ERR_MSG, FORMAT_STR) \
|
#define RETURN_IF_ERROR2(ERR_MSG, FORMAT_STR) \
|
||||||
if (err != WEBP_MUX_OK) { \
|
if (err != WEBP_MUX_OK) { \
|
||||||
fprintf(stderr, ERR_MSG, FORMAT_STR); \
|
fprintf(stderr, ERR_MSG, FORMAT_STR); \
|
||||||
return err; \
|
return err; \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ERROR_GOTO1(ERR_MSG, LABEL) \
|
#define ERROR_GOTO1(ERR_MSG, LABEL) \
|
||||||
do { \
|
do { \
|
||||||
fprintf(stderr, ERR_MSG); \
|
fprintf(stderr, ERR_MSG); \
|
||||||
ok = 0; \
|
ok = 0; \
|
||||||
goto LABEL; \
|
goto LABEL; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define ERROR_GOTO2(ERR_MSG, FORMAT_STR, LABEL) \
|
#define ERROR_GOTO2(ERR_MSG, FORMAT_STR, LABEL) \
|
||||||
do { \
|
do { \
|
||||||
fprintf(stderr, ERR_MSG, FORMAT_STR); \
|
fprintf(stderr, ERR_MSG, FORMAT_STR); \
|
||||||
ok = 0; \
|
ok = 0; \
|
||||||
goto LABEL; \
|
goto LABEL; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define ERROR_GOTO3(ERR_MSG, FORMAT_STR1, FORMAT_STR2, LABEL) \
|
#define ERROR_GOTO3(ERR_MSG, FORMAT_STR1, FORMAT_STR2, LABEL) \
|
||||||
do { \
|
do { \
|
||||||
fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
|
fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
|
||||||
ok = 0; \
|
ok = 0; \
|
||||||
goto LABEL; \
|
goto LABEL; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
static WebPMuxError DisplayInfo(const WebPMux* mux) {
|
static WebPMuxError DisplayInfo(const WebPMux* mux) {
|
||||||
uint32_t flag;
|
uint32_t flag;
|
||||||
|
339
src/dec/frame.c
339
src/dec/frame.c
@ -19,54 +19,7 @@ extern "C" {
|
|||||||
#define ALIGN_MASK (32 - 1)
|
#define ALIGN_MASK (32 - 1)
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// For multi-threaded decoding we need to use 3 rows of 16 pixels as delay line.
|
// Filtering
|
||||||
//
|
|
||||||
// Reason is: the deblocking filter cannot deblock the bottom horizontal edges
|
|
||||||
// immediately, and needs to wait for first few rows of the next macroblock to
|
|
||||||
// be decoded. Hence, deblocking is lagging behind by 4 or 8 pixels (depending
|
|
||||||
// on strength).
|
|
||||||
// With two threads, the vertical positions of the rows being decoded are:
|
|
||||||
// Decode: [ 0..15][16..31][32..47][48..63][64..79][...
|
|
||||||
// Deblock: [ 0..11][12..27][28..43][44..59][...
|
|
||||||
// If we use two threads and two caches of 16 pixels, the sequence would be:
|
|
||||||
// Decode: [ 0..15][16..31][ 0..15!!][16..31][ 0..15][...
|
|
||||||
// Deblock: [ 0..11][12..27!!][-4..11][12..27][...
|
|
||||||
// The problem occurs during row [12..15!!] that both the decoding and
|
|
||||||
// deblocking threads are writing simultaneously.
|
|
||||||
// With 3 cache lines, one get a safe write pattern:
|
|
||||||
// Decode: [ 0..15][16..31][32..47][ 0..15][16..31][32..47][0..
|
|
||||||
// Deblock: [ 0..11][12..27][28..43][-4..11][12..27][28...
|
|
||||||
// Note that multi-threaded output _without_ deblocking can make use of two
|
|
||||||
// cache lines of 16 pixels only, since there's no lagging behind. The decoding
|
|
||||||
// and output process have non-concurrent writing:
|
|
||||||
// Decode: [ 0..15][16..31][ 0..15][16..31][...
|
|
||||||
// io->put: [ 0..15][16..31][ 0..15][...
|
|
||||||
|
|
||||||
#define MT_CACHE_LINES 3
|
|
||||||
#define ST_CACHE_LINES 1 // 1 cache row only for single-threaded case
|
|
||||||
|
|
||||||
// Initialize multi/single-thread worker
|
|
||||||
static int InitThreadContext(VP8Decoder* const dec) {
|
|
||||||
dec->cache_id_ = 0;
|
|
||||||
if (dec->use_threads_) {
|
|
||||||
WebPWorker* const worker = &dec->worker_;
|
|
||||||
if (!WebPWorkerReset(worker)) {
|
|
||||||
return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
|
|
||||||
"thread initialization failed.");
|
|
||||||
}
|
|
||||||
worker->data1 = dec;
|
|
||||||
worker->data2 = (void*)&dec->thread_ctx_.io_;
|
|
||||||
worker->hook = (WebPWorkerHook)VP8FinishRow;
|
|
||||||
dec->num_caches_ =
|
|
||||||
(dec->filter_type_ > 0) ? MT_CACHE_LINES : MT_CACHE_LINES - 1;
|
|
||||||
} else {
|
|
||||||
dec->num_caches_ = ST_CACHE_LINES;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Memory setup
|
|
||||||
|
|
||||||
// kFilterExtraRows[] = How many extra lines are needed on the MB boundary
|
// kFilterExtraRows[] = How many extra lines are needed on the MB boundary
|
||||||
// for caching, given a filtering level.
|
// for caching, given a filtering level.
|
||||||
@ -75,124 +28,6 @@ static int InitThreadContext(VP8Decoder* const dec) {
|
|||||||
// U/V, so it's 8 samples total (because of the 2x upsampling).
|
// U/V, so it's 8 samples total (because of the 2x upsampling).
|
||||||
static const uint8_t kFilterExtraRows[3] = { 0, 2, 8 };
|
static const uint8_t kFilterExtraRows[3] = { 0, 2, 8 };
|
||||||
|
|
||||||
static int AllocateMemory(VP8Decoder* const dec) {
|
|
||||||
const int num_caches = dec->num_caches_;
|
|
||||||
const int mb_w = dec->mb_w_;
|
|
||||||
const size_t intra_pred_mode_size = 4 * mb_w * sizeof(uint8_t);
|
|
||||||
const size_t top_size = (16 + 8 + 8) * mb_w;
|
|
||||||
const size_t mb_info_size = (mb_w + 1) * sizeof(VP8MB);
|
|
||||||
const size_t f_info_size =
|
|
||||||
(dec->filter_type_ > 0) ?
|
|
||||||
mb_w * (dec->use_threads_ ? 2 : 1) * sizeof(VP8FInfo)
|
|
||||||
: 0;
|
|
||||||
const size_t yuv_size = YUV_SIZE * sizeof(*dec->yuv_b_);
|
|
||||||
const size_t coeffs_size = 384 * sizeof(*dec->coeffs_);
|
|
||||||
const size_t cache_height = (16 * num_caches
|
|
||||||
+ kFilterExtraRows[dec->filter_type_]) * 3 / 2;
|
|
||||||
const size_t cache_size = top_size * cache_height;
|
|
||||||
const size_t alpha_size =
|
|
||||||
dec->alpha_data_ ? (dec->pic_hdr_.width_ * dec->pic_hdr_.height_) : 0;
|
|
||||||
const size_t needed = intra_pred_mode_size
|
|
||||||
+ top_size + mb_info_size + f_info_size
|
|
||||||
+ yuv_size + coeffs_size
|
|
||||||
+ cache_size + alpha_size + ALIGN_MASK;
|
|
||||||
uint8_t* mem;
|
|
||||||
|
|
||||||
if (needed > dec->mem_size_) {
|
|
||||||
free(dec->mem_);
|
|
||||||
dec->mem_size_ = 0;
|
|
||||||
dec->mem_ = (uint8_t*)malloc(needed);
|
|
||||||
if (dec->mem_ == NULL) {
|
|
||||||
return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
|
|
||||||
"no memory during frame initialization.");
|
|
||||||
}
|
|
||||||
dec->mem_size_ = needed;
|
|
||||||
}
|
|
||||||
|
|
||||||
mem = (uint8_t*)dec->mem_;
|
|
||||||
dec->intra_t_ = (uint8_t*)mem;
|
|
||||||
mem += intra_pred_mode_size;
|
|
||||||
|
|
||||||
dec->y_t_ = (uint8_t*)mem;
|
|
||||||
mem += 16 * mb_w;
|
|
||||||
dec->u_t_ = (uint8_t*)mem;
|
|
||||||
mem += 8 * mb_w;
|
|
||||||
dec->v_t_ = (uint8_t*)mem;
|
|
||||||
mem += 8 * mb_w;
|
|
||||||
|
|
||||||
dec->mb_info_ = ((VP8MB*)mem) + 1;
|
|
||||||
mem += mb_info_size;
|
|
||||||
|
|
||||||
dec->f_info_ = f_info_size ? (VP8FInfo*)mem : NULL;
|
|
||||||
mem += f_info_size;
|
|
||||||
dec->thread_ctx_.id_ = 0;
|
|
||||||
dec->thread_ctx_.f_info_ = dec->f_info_;
|
|
||||||
if (dec->use_threads_) {
|
|
||||||
// secondary cache line. The deblocking process need to make use of the
|
|
||||||
// filtering strength from previous macroblock row, while the new ones
|
|
||||||
// are being decoded in parallel. We'll just swap the pointers.
|
|
||||||
dec->thread_ctx_.f_info_ += mb_w;
|
|
||||||
}
|
|
||||||
|
|
||||||
mem = (uint8_t*)((uintptr_t)(mem + ALIGN_MASK) & ~ALIGN_MASK);
|
|
||||||
assert((yuv_size & ALIGN_MASK) == 0);
|
|
||||||
dec->yuv_b_ = (uint8_t*)mem;
|
|
||||||
mem += yuv_size;
|
|
||||||
|
|
||||||
dec->coeffs_ = (int16_t*)mem;
|
|
||||||
mem += coeffs_size;
|
|
||||||
|
|
||||||
dec->cache_y_stride_ = 16 * mb_w;
|
|
||||||
dec->cache_uv_stride_ = 8 * mb_w;
|
|
||||||
{
|
|
||||||
const int extra_rows = kFilterExtraRows[dec->filter_type_];
|
|
||||||
const int extra_y = extra_rows * dec->cache_y_stride_;
|
|
||||||
const int extra_uv = (extra_rows / 2) * dec->cache_uv_stride_;
|
|
||||||
dec->cache_y_ = ((uint8_t*)mem) + extra_y;
|
|
||||||
dec->cache_u_ = dec->cache_y_
|
|
||||||
+ 16 * num_caches * dec->cache_y_stride_ + extra_uv;
|
|
||||||
dec->cache_v_ = dec->cache_u_
|
|
||||||
+ 8 * num_caches * dec->cache_uv_stride_ + extra_uv;
|
|
||||||
dec->cache_id_ = 0;
|
|
||||||
}
|
|
||||||
mem += cache_size;
|
|
||||||
|
|
||||||
// alpha plane
|
|
||||||
dec->alpha_plane_ = alpha_size ? (uint8_t*)mem : NULL;
|
|
||||||
mem += alpha_size;
|
|
||||||
|
|
||||||
// note: left-info is initialized once for all.
|
|
||||||
memset(dec->mb_info_ - 1, 0, mb_info_size);
|
|
||||||
|
|
||||||
// initialize top
|
|
||||||
memset(dec->intra_t_, B_DC_PRED, intra_pred_mode_size);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void InitIo(VP8Decoder* const dec, VP8Io* io) {
|
|
||||||
// prepare 'io'
|
|
||||||
io->mb_y = 0;
|
|
||||||
io->y = dec->cache_y_;
|
|
||||||
io->u = dec->cache_u_;
|
|
||||||
io->v = dec->cache_v_;
|
|
||||||
io->y_stride = dec->cache_y_stride_;
|
|
||||||
io->uv_stride = dec->cache_uv_stride_;
|
|
||||||
io->fancy_upsampling = 0; // default
|
|
||||||
io->a = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int VP8InitFrame(VP8Decoder* const dec, VP8Io* io) {
|
|
||||||
if (!InitThreadContext(dec)) return 0; // call first. Sets dec->num_caches_.
|
|
||||||
if (!AllocateMemory(dec)) return 0;
|
|
||||||
InitIo(dec, io);
|
|
||||||
VP8DspInit(); // Init critical function pointers and look-up tables.
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Filtering
|
|
||||||
|
|
||||||
static WEBP_INLINE int hev_thresh_from_level(int level, int keyframe) {
|
static WEBP_INLINE int hev_thresh_from_level(int level, int keyframe) {
|
||||||
if (keyframe) {
|
if (keyframe) {
|
||||||
return (level >= 40) ? 2 : (level >= 15) ? 1 : 0;
|
return (level >= 40) ? 2 : (level >= 15) ? 1 : 0;
|
||||||
@ -326,7 +161,7 @@ void VP8StoreBlock(VP8Decoder* const dec) {
|
|||||||
#define MACROBLOCK_VPOS(mb_y) ((mb_y) * 16) // vertical position of a MB
|
#define MACROBLOCK_VPOS(mb_y) ((mb_y) * 16) // vertical position of a MB
|
||||||
|
|
||||||
// Finalize and transmit a complete row. Return false in case of user-abort.
|
// Finalize and transmit a complete row. Return false in case of user-abort.
|
||||||
int VP8FinishRow(VP8Decoder* const dec, VP8Io* io) {
|
static int FinishRow(VP8Decoder* const dec, VP8Io* const io) {
|
||||||
int ok = 1;
|
int ok = 1;
|
||||||
const VP8ThreadContext* const ctx = &dec->thread_ctx_;
|
const VP8ThreadContext* const ctx = &dec->thread_ctx_;
|
||||||
const int extra_y_rows = kFilterExtraRows[dec->filter_type_];
|
const int extra_y_rows = kFilterExtraRows[dec->filter_type_];
|
||||||
@ -419,7 +254,7 @@ int VP8ProcessRow(VP8Decoder* const dec, VP8Io* const io) {
|
|||||||
// ctx->id_ and ctx->f_info_ are already set
|
// ctx->id_ and ctx->f_info_ are already set
|
||||||
ctx->mb_y_ = dec->mb_y_;
|
ctx->mb_y_ = dec->mb_y_;
|
||||||
ctx->filter_row_ = dec->filter_row_;
|
ctx->filter_row_ = dec->filter_row_;
|
||||||
ok = VP8FinishRow(dec, io);
|
ok = FinishRow(dec, io);
|
||||||
} else {
|
} else {
|
||||||
WebPWorker* const worker = &dec->worker_;
|
WebPWorker* const worker = &dec->worker_;
|
||||||
// Finish previous job *before* updating context
|
// Finish previous job *before* updating context
|
||||||
@ -513,6 +348,174 @@ int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io) {
|
|||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// For multi-threaded decoding we need to use 3 rows of 16 pixels as delay line.
|
||||||
|
//
|
||||||
|
// Reason is: the deblocking filter cannot deblock the bottom horizontal edges
|
||||||
|
// immediately, and needs to wait for first few rows of the next macroblock to
|
||||||
|
// be decoded. Hence, deblocking is lagging behind by 4 or 8 pixels (depending
|
||||||
|
// on strength).
|
||||||
|
// With two threads, the vertical positions of the rows being decoded are:
|
||||||
|
// Decode: [ 0..15][16..31][32..47][48..63][64..79][...
|
||||||
|
// Deblock: [ 0..11][12..27][28..43][44..59][...
|
||||||
|
// If we use two threads and two caches of 16 pixels, the sequence would be:
|
||||||
|
// Decode: [ 0..15][16..31][ 0..15!!][16..31][ 0..15][...
|
||||||
|
// Deblock: [ 0..11][12..27!!][-4..11][12..27][...
|
||||||
|
// The problem occurs during row [12..15!!] that both the decoding and
|
||||||
|
// deblocking threads are writing simultaneously.
|
||||||
|
// With 3 cache lines, one get a safe write pattern:
|
||||||
|
// Decode: [ 0..15][16..31][32..47][ 0..15][16..31][32..47][0..
|
||||||
|
// Deblock: [ 0..11][12..27][28..43][-4..11][12..27][28...
|
||||||
|
// Note that multi-threaded output _without_ deblocking can make use of two
|
||||||
|
// cache lines of 16 pixels only, since there's no lagging behind. The decoding
|
||||||
|
// and output process have non-concurrent writing:
|
||||||
|
// Decode: [ 0..15][16..31][ 0..15][16..31][...
|
||||||
|
// io->put: [ 0..15][16..31][ 0..15][...
|
||||||
|
|
||||||
|
#define MT_CACHE_LINES 3
|
||||||
|
#define ST_CACHE_LINES 1 // 1 cache row only for single-threaded case
|
||||||
|
|
||||||
|
// Initialize multi/single-thread worker
|
||||||
|
static int InitThreadContext(VP8Decoder* const dec) {
|
||||||
|
dec->cache_id_ = 0;
|
||||||
|
if (dec->use_threads_) {
|
||||||
|
WebPWorker* const worker = &dec->worker_;
|
||||||
|
if (!WebPWorkerReset(worker)) {
|
||||||
|
return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
|
||||||
|
"thread initialization failed.");
|
||||||
|
}
|
||||||
|
worker->data1 = dec;
|
||||||
|
worker->data2 = (void*)&dec->thread_ctx_.io_;
|
||||||
|
worker->hook = (WebPWorkerHook)FinishRow;
|
||||||
|
dec->num_caches_ =
|
||||||
|
(dec->filter_type_ > 0) ? MT_CACHE_LINES : MT_CACHE_LINES - 1;
|
||||||
|
} else {
|
||||||
|
dec->num_caches_ = ST_CACHE_LINES;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef MT_CACHE_LINES
|
||||||
|
#undef ST_CACHE_LINES
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Memory setup
|
||||||
|
|
||||||
|
static int AllocateMemory(VP8Decoder* const dec) {
|
||||||
|
const int num_caches = dec->num_caches_;
|
||||||
|
const int mb_w = dec->mb_w_;
|
||||||
|
const size_t intra_pred_mode_size = 4 * mb_w * sizeof(uint8_t);
|
||||||
|
const size_t top_size = (16 + 8 + 8) * mb_w;
|
||||||
|
const size_t mb_info_size = (mb_w + 1) * sizeof(VP8MB);
|
||||||
|
const size_t f_info_size =
|
||||||
|
(dec->filter_type_ > 0) ?
|
||||||
|
mb_w * (dec->use_threads_ ? 2 : 1) * sizeof(VP8FInfo)
|
||||||
|
: 0;
|
||||||
|
const size_t yuv_size = YUV_SIZE * sizeof(*dec->yuv_b_);
|
||||||
|
const size_t coeffs_size = 384 * sizeof(*dec->coeffs_);
|
||||||
|
const size_t cache_height = (16 * num_caches
|
||||||
|
+ kFilterExtraRows[dec->filter_type_]) * 3 / 2;
|
||||||
|
const size_t cache_size = top_size * cache_height;
|
||||||
|
const size_t alpha_size =
|
||||||
|
dec->alpha_data_ ? (dec->pic_hdr_.width_ * dec->pic_hdr_.height_) : 0;
|
||||||
|
const size_t needed = intra_pred_mode_size
|
||||||
|
+ top_size + mb_info_size + f_info_size
|
||||||
|
+ yuv_size + coeffs_size
|
||||||
|
+ cache_size + alpha_size + ALIGN_MASK;
|
||||||
|
uint8_t* mem;
|
||||||
|
|
||||||
|
if (needed > dec->mem_size_) {
|
||||||
|
free(dec->mem_);
|
||||||
|
dec->mem_size_ = 0;
|
||||||
|
dec->mem_ = (uint8_t*)malloc(needed);
|
||||||
|
if (dec->mem_ == NULL) {
|
||||||
|
return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
|
||||||
|
"no memory during frame initialization.");
|
||||||
|
}
|
||||||
|
dec->mem_size_ = needed;
|
||||||
|
}
|
||||||
|
|
||||||
|
mem = (uint8_t*)dec->mem_;
|
||||||
|
dec->intra_t_ = (uint8_t*)mem;
|
||||||
|
mem += intra_pred_mode_size;
|
||||||
|
|
||||||
|
dec->y_t_ = (uint8_t*)mem;
|
||||||
|
mem += 16 * mb_w;
|
||||||
|
dec->u_t_ = (uint8_t*)mem;
|
||||||
|
mem += 8 * mb_w;
|
||||||
|
dec->v_t_ = (uint8_t*)mem;
|
||||||
|
mem += 8 * mb_w;
|
||||||
|
|
||||||
|
dec->mb_info_ = ((VP8MB*)mem) + 1;
|
||||||
|
mem += mb_info_size;
|
||||||
|
|
||||||
|
dec->f_info_ = f_info_size ? (VP8FInfo*)mem : NULL;
|
||||||
|
mem += f_info_size;
|
||||||
|
dec->thread_ctx_.id_ = 0;
|
||||||
|
dec->thread_ctx_.f_info_ = dec->f_info_;
|
||||||
|
if (dec->use_threads_) {
|
||||||
|
// secondary cache line. The deblocking process need to make use of the
|
||||||
|
// filtering strength from previous macroblock row, while the new ones
|
||||||
|
// are being decoded in parallel. We'll just swap the pointers.
|
||||||
|
dec->thread_ctx_.f_info_ += mb_w;
|
||||||
|
}
|
||||||
|
|
||||||
|
mem = (uint8_t*)((uintptr_t)(mem + ALIGN_MASK) & ~ALIGN_MASK);
|
||||||
|
assert((yuv_size & ALIGN_MASK) == 0);
|
||||||
|
dec->yuv_b_ = (uint8_t*)mem;
|
||||||
|
mem += yuv_size;
|
||||||
|
|
||||||
|
dec->coeffs_ = (int16_t*)mem;
|
||||||
|
mem += coeffs_size;
|
||||||
|
|
||||||
|
dec->cache_y_stride_ = 16 * mb_w;
|
||||||
|
dec->cache_uv_stride_ = 8 * mb_w;
|
||||||
|
{
|
||||||
|
const int extra_rows = kFilterExtraRows[dec->filter_type_];
|
||||||
|
const int extra_y = extra_rows * dec->cache_y_stride_;
|
||||||
|
const int extra_uv = (extra_rows / 2) * dec->cache_uv_stride_;
|
||||||
|
dec->cache_y_ = ((uint8_t*)mem) + extra_y;
|
||||||
|
dec->cache_u_ = dec->cache_y_
|
||||||
|
+ 16 * num_caches * dec->cache_y_stride_ + extra_uv;
|
||||||
|
dec->cache_v_ = dec->cache_u_
|
||||||
|
+ 8 * num_caches * dec->cache_uv_stride_ + extra_uv;
|
||||||
|
dec->cache_id_ = 0;
|
||||||
|
}
|
||||||
|
mem += cache_size;
|
||||||
|
|
||||||
|
// alpha plane
|
||||||
|
dec->alpha_plane_ = alpha_size ? (uint8_t*)mem : NULL;
|
||||||
|
mem += alpha_size;
|
||||||
|
|
||||||
|
// note: left-info is initialized once for all.
|
||||||
|
memset(dec->mb_info_ - 1, 0, mb_info_size);
|
||||||
|
|
||||||
|
// initialize top
|
||||||
|
memset(dec->intra_t_, B_DC_PRED, intra_pred_mode_size);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void InitIo(VP8Decoder* const dec, VP8Io* io) {
|
||||||
|
// prepare 'io'
|
||||||
|
io->mb_y = 0;
|
||||||
|
io->y = dec->cache_y_;
|
||||||
|
io->u = dec->cache_u_;
|
||||||
|
io->v = dec->cache_v_;
|
||||||
|
io->y_stride = dec->cache_y_stride_;
|
||||||
|
io->uv_stride = dec->cache_uv_stride_;
|
||||||
|
io->fancy_upsampling = 0; // default
|
||||||
|
io->a = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int VP8InitFrame(VP8Decoder* const dec, VP8Io* io) {
|
||||||
|
if (!InitThreadContext(dec)) return 0; // call first. Sets dec->num_caches_.
|
||||||
|
if (!AllocateMemory(dec)) return 0;
|
||||||
|
InitIo(dec, io);
|
||||||
|
VP8DspInit(); // Init critical function pointers and look-up tables.
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Main reconstruction function.
|
// Main reconstruction function.
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ int VP8InitIoInternal(VP8Io* const io, int version) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
VP8Decoder* VP8New(void) {
|
VP8Decoder* VP8New(void) {
|
||||||
VP8Decoder* dec = (VP8Decoder*)calloc(1, sizeof(VP8Decoder));
|
VP8Decoder* const dec = (VP8Decoder*)calloc(1, sizeof(VP8Decoder));
|
||||||
if (dec) {
|
if (dec) {
|
||||||
SetOk(dec);
|
SetOk(dec);
|
||||||
WebPWorkerInit(&dec->worker_);
|
WebPWorkerInit(&dec->worker_);
|
||||||
|
@ -316,14 +316,10 @@ VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io);
|
|||||||
// Must always be called in pair with VP8EnterCritical().
|
// Must always be called in pair with VP8EnterCritical().
|
||||||
// Returns false in case of error.
|
// Returns false in case of error.
|
||||||
int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io);
|
int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io);
|
||||||
// Filter the decoded macroblock row (if needed)
|
|
||||||
int VP8FinishRow(VP8Decoder* const dec, VP8Io* io); // multi threaded call
|
|
||||||
// Process the last decoded row (filtering + output)
|
// Process the last decoded row (filtering + output)
|
||||||
int VP8ProcessRow(VP8Decoder* const dec, VP8Io* const io);
|
int VP8ProcessRow(VP8Decoder* const dec, VP8Io* const io);
|
||||||
// Store a block, along with filtering params
|
// Store a block, along with filtering params
|
||||||
void VP8StoreBlock(VP8Decoder* const dec);
|
void VP8StoreBlock(VP8Decoder* const dec);
|
||||||
// Finalize and transmit a complete row. Return false in case of user-abort.
|
|
||||||
int VP8FinishRow(VP8Decoder* const dec, VP8Io* const io);
|
|
||||||
// To be called at the start of a new scanline, to initialize predictors.
|
// To be called at the start of a new scanline, to initialize predictors.
|
||||||
void VP8InitScanline(VP8Decoder* const dec);
|
void VP8InitScanline(VP8Decoder* const dec);
|
||||||
// Decode one macroblock. Returns false if there is not enough data.
|
// Decode one macroblock. Returns false if there is not enough data.
|
||||||
|
@ -59,6 +59,7 @@ VP8CPUInfo VP8GetCPUInfo = x86CPUInfo;
|
|||||||
// define a dummy function to enable turning off NEON at runtime by setting
|
// define a dummy function to enable turning off NEON at runtime by setting
|
||||||
// VP8DecGetCPUInfo = NULL
|
// VP8DecGetCPUInfo = NULL
|
||||||
static int armCPUInfo(CPUFeature feature) {
|
static int armCPUInfo(CPUFeature feature) {
|
||||||
|
(void)feature;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
VP8CPUInfo VP8GetCPUInfo = armCPUInfo;
|
VP8CPUInfo VP8GetCPUInfo = armCPUInfo;
|
||||||
|
@ -467,16 +467,16 @@ static void DC8uvNoTopLeft(uint8_t *dst) { // DC with nothing
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// default C implementations
|
// default C implementations
|
||||||
|
|
||||||
VP8PredFunc VP8PredLuma4[/* NUM_BMODES */] = {
|
const VP8PredFunc VP8PredLuma4[NUM_BMODES] = {
|
||||||
DC4, TM4, VE4, HE4, RD4, VR4, LD4, VL4, HD4, HU4
|
DC4, TM4, VE4, HE4, RD4, VR4, LD4, VL4, HD4, HU4
|
||||||
};
|
};
|
||||||
|
|
||||||
VP8PredFunc VP8PredLuma16[/*NUM_B_DC_MODES */] = {
|
const VP8PredFunc VP8PredLuma16[NUM_B_DC_MODES] = {
|
||||||
DC16, TM16, VE16, HE16,
|
DC16, TM16, VE16, HE16,
|
||||||
DC16NoTop, DC16NoLeft, DC16NoTopLeft
|
DC16NoTop, DC16NoLeft, DC16NoTopLeft
|
||||||
};
|
};
|
||||||
|
|
||||||
VP8PredFunc VP8PredChroma8[/*NUM_B_DC_MODES */] = {
|
const VP8PredFunc VP8PredChroma8[NUM_B_DC_MODES] = {
|
||||||
DC8uv, TM8uv, VE8uv, HE8uv,
|
DC8uv, TM8uv, VE8uv, HE8uv,
|
||||||
DC8uvNoTop, DC8uvNoLeft, DC8uvNoTopLeft
|
DC8uvNoTop, DC8uvNoLeft, DC8uvNoTopLeft
|
||||||
};
|
};
|
||||||
|
@ -71,8 +71,6 @@ extern VP8WMetric VP8TDisto4x4, VP8TDisto16x16;
|
|||||||
|
|
||||||
typedef void (*VP8BlockCopy)(const uint8_t* src, uint8_t* dst);
|
typedef void (*VP8BlockCopy)(const uint8_t* src, uint8_t* dst);
|
||||||
extern VP8BlockCopy VP8Copy4x4;
|
extern VP8BlockCopy VP8Copy4x4;
|
||||||
extern VP8BlockCopy VP8Copy8x8;
|
|
||||||
extern VP8BlockCopy VP8Copy16x16;
|
|
||||||
// Quantization
|
// Quantization
|
||||||
struct VP8Matrix; // forward declaration
|
struct VP8Matrix; // forward declaration
|
||||||
typedef int (*VP8QuantizeBlock)(int16_t in[16], int16_t out[16],
|
typedef int (*VP8QuantizeBlock)(int16_t in[16], int16_t out[16],
|
||||||
@ -103,9 +101,9 @@ extern void (*VP8TransformWHT)(const int16_t* in, int16_t* out);
|
|||||||
// *dst is the destination block, with stride BPS. Boundary samples are
|
// *dst is the destination block, with stride BPS. Boundary samples are
|
||||||
// assumed accessible when needed.
|
// assumed accessible when needed.
|
||||||
typedef void (*VP8PredFunc)(uint8_t* dst);
|
typedef void (*VP8PredFunc)(uint8_t* dst);
|
||||||
extern VP8PredFunc VP8PredLuma16[/* NUM_B_DC_MODES */];
|
extern const VP8PredFunc VP8PredLuma16[/* NUM_B_DC_MODES */];
|
||||||
extern VP8PredFunc VP8PredChroma8[/* NUM_B_DC_MODES */];
|
extern const VP8PredFunc VP8PredChroma8[/* NUM_B_DC_MODES */];
|
||||||
extern VP8PredFunc VP8PredLuma4[/* NUM_BMODES */];
|
extern const VP8PredFunc VP8PredLuma4[/* NUM_BMODES */];
|
||||||
|
|
||||||
// simple filter (only for luma)
|
// simple filter (only for luma)
|
||||||
typedef void (*VP8SimpleFilterFunc)(uint8_t* p, int stride, int thresh);
|
typedef void (*VP8SimpleFilterFunc)(uint8_t* p, int stride, int thresh);
|
||||||
@ -132,7 +130,7 @@ extern VP8ChromaFilterFunc VP8VFilter8i; // filtering u and v altogether
|
|||||||
extern VP8ChromaFilterFunc VP8HFilter8i;
|
extern VP8ChromaFilterFunc VP8HFilter8i;
|
||||||
|
|
||||||
// must be called before anything using the above
|
// must be called before anything using the above
|
||||||
extern void VP8DspInit(void);
|
void VP8DspInit(void);
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// WebP I/O
|
// WebP I/O
|
||||||
|
@ -681,8 +681,6 @@ static WEBP_INLINE void Copy(const uint8_t* src, uint8_t* dst, int size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void Copy4x4(const uint8_t* src, uint8_t* dst) { Copy(src, dst, 4); }
|
static void Copy4x4(const uint8_t* src, uint8_t* dst) { Copy(src, dst, 4); }
|
||||||
static void Copy8x8(const uint8_t* src, uint8_t* dst) { Copy(src, dst, 8); }
|
|
||||||
static void Copy16x16(const uint8_t* src, uint8_t* dst) { Copy(src, dst, 16); }
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Initialization
|
// Initialization
|
||||||
@ -705,8 +703,6 @@ VP8WMetric VP8TDisto4x4;
|
|||||||
VP8WMetric VP8TDisto16x16;
|
VP8WMetric VP8TDisto16x16;
|
||||||
VP8QuantizeBlock VP8EncQuantizeBlock;
|
VP8QuantizeBlock VP8EncQuantizeBlock;
|
||||||
VP8BlockCopy VP8Copy4x4;
|
VP8BlockCopy VP8Copy4x4;
|
||||||
VP8BlockCopy VP8Copy8x8;
|
|
||||||
VP8BlockCopy VP8Copy16x16;
|
|
||||||
|
|
||||||
extern void VP8EncDspInitSSE2(void);
|
extern void VP8EncDspInitSSE2(void);
|
||||||
|
|
||||||
@ -730,8 +726,6 @@ void VP8EncDspInit(void) {
|
|||||||
VP8TDisto16x16 = Disto16x16;
|
VP8TDisto16x16 = Disto16x16;
|
||||||
VP8EncQuantizeBlock = QuantizeBlock;
|
VP8EncQuantizeBlock = QuantizeBlock;
|
||||||
VP8Copy4x4 = Copy4x4;
|
VP8Copy4x4 = Copy4x4;
|
||||||
VP8Copy8x8 = Copy8x8;
|
|
||||||
VP8Copy16x16 = Copy16x16;
|
|
||||||
|
|
||||||
// If defined, use CPUInfo() to overwrite some pointers with faster versions.
|
// If defined, use CPUInfo() to overwrite some pointers with faster versions.
|
||||||
if (VP8GetCPUInfo) {
|
if (VP8GetCPUInfo) {
|
||||||
|
@ -35,7 +35,7 @@ static void SmoothSegmentMap(VP8Encoder* const enc) {
|
|||||||
const int w = enc->mb_w_;
|
const int w = enc->mb_w_;
|
||||||
const int h = enc->mb_h_;
|
const int h = enc->mb_h_;
|
||||||
const int majority_cnt_3_x_3_grid = 5;
|
const int majority_cnt_3_x_3_grid = 5;
|
||||||
uint8_t* tmp = (uint8_t*)malloc(w * h * sizeof(uint8_t));
|
uint8_t* const tmp = (uint8_t*)malloc(w * h * sizeof(uint8_t));
|
||||||
|
|
||||||
if (tmp == NULL) return;
|
if (tmp == NULL) return;
|
||||||
for (y = 1; y < h - 1; ++y) {
|
for (y = 1; y < h - 1; ++y) {
|
||||||
|
@ -332,7 +332,7 @@ int WebPPictureRescale(WebPPicture* const pic, int width, int height) {
|
|||||||
tmp.height = height;
|
tmp.height = height;
|
||||||
if (!WebPPictureAlloc(&tmp)) return 0;
|
if (!WebPPictureAlloc(&tmp)) return 0;
|
||||||
|
|
||||||
work = malloc(2 * width * sizeof(int32_t));
|
work = (int32_t*)malloc(2 * width * sizeof(int32_t));
|
||||||
if (work == NULL) {
|
if (work == NULL) {
|
||||||
WebPPictureFree(&tmp);
|
WebPPictureFree(&tmp);
|
||||||
return 0;
|
return 0;
|
||||||
@ -697,12 +697,12 @@ int WebPPictureDistortion(const WebPPicture* const pic1,
|
|||||||
for (c = 0; c <= 4; ++c) {
|
for (c = 0; c <= 4; ++c) {
|
||||||
if (type == 1) {
|
if (type == 1) {
|
||||||
const double v = VP8SSIMGet(&stats[c]);
|
const double v = VP8SSIMGet(&stats[c]);
|
||||||
result[c] = (v < 1.) ? -10.0 * log10(1. - v)
|
result[c] = (float)((v < 1.) ? -10.0 * log10(1. - v)
|
||||||
: kMinDistortion_dB;
|
: kMinDistortion_dB);
|
||||||
} else {
|
} else {
|
||||||
const double v = VP8SSIMGetSquaredError(&stats[c]);
|
const double v = VP8SSIMGetSquaredError(&stats[c]);
|
||||||
result[c] = (v > 0.) ? -4.3429448 * log(v / (255 * 255.))
|
result[c] = (float)((v > 0.) ? -4.3429448 * log(v / (255 * 255.))
|
||||||
: kMinDistortion_dB;
|
: kMinDistortion_dB);
|
||||||
}
|
}
|
||||||
// Accumulate forward
|
// Accumulate forward
|
||||||
if (c < 4) VP8SSIMAddStats(&stats[c], &stats[4]);
|
if (c < 4) VP8SSIMAddStats(&stats[c], &stats[4]);
|
||||||
|
@ -323,7 +323,7 @@ void VP8IteratorResetCosts(VP8EncIterator* const it);
|
|||||||
|
|
||||||
typedef struct VP8Tokens VP8Tokens;
|
typedef struct VP8Tokens VP8Tokens;
|
||||||
struct VP8Tokens {
|
struct VP8Tokens {
|
||||||
uint16_t tokens_[MAX_NUM_TOKEN]; // bit#15: bit, bits 0..14: slot
|
uint16_t tokens_[MAX_NUM_TOKEN]; // bit#15: bit, bits 0..14: slot
|
||||||
int left_;
|
int left_;
|
||||||
VP8Tokens* next_;
|
VP8Tokens* next_;
|
||||||
};
|
};
|
||||||
@ -346,13 +346,13 @@ int VP8EmitTokens(const VP8TBuffer* const b, VP8BitWriter* const bw,
|
|||||||
static WEBP_INLINE int VP8AddToken(VP8TBuffer* const b,
|
static WEBP_INLINE int VP8AddToken(VP8TBuffer* const b,
|
||||||
int bit, int proba_idx) {
|
int bit, int proba_idx) {
|
||||||
if (b->left_ > 0 || VP8TBufferNewPage(b)) {
|
if (b->left_ > 0 || VP8TBufferNewPage(b)) {
|
||||||
const int slot = --b->left_;
|
const int slot = --b->left_;
|
||||||
b->tokens_[slot] = (bit << 15) | proba_idx;
|
b->tokens_[slot] = (bit << 15) | proba_idx;
|
||||||
}
|
}
|
||||||
return bit;
|
return bit;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif // USE_TOKEN_BUFFER
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// VP8Encoder
|
// VP8Encoder
|
||||||
@ -501,7 +501,9 @@ void VP8EncDeleteLayer(VP8Encoder* enc); // reclaim memory
|
|||||||
// in filter.c
|
// in filter.c
|
||||||
|
|
||||||
// SSIM utils
|
// SSIM utils
|
||||||
typedef struct { double w, xm, ym, xxm, xym, yym; } DistoStats;
|
typedef struct {
|
||||||
|
double w, xm, ym, xxm, xym, yym;
|
||||||
|
} DistoStats;
|
||||||
void VP8SSIMAddStats(const DistoStats* const src, DistoStats* const dst);
|
void VP8SSIMAddStats(const DistoStats* const src, DistoStats* const dst);
|
||||||
void VP8SSIMAccumulatePlane(const uint8_t* src1, int stride1,
|
void VP8SSIMAccumulatePlane(const uint8_t* src1, int stride1,
|
||||||
const uint8_t* src2, int stride2,
|
const uint8_t* src2, int stride2,
|
||||||
|
@ -62,15 +62,15 @@ void WebPMuxDelete(WebPMux* const mux) {
|
|||||||
|
|
||||||
// Handy MACRO, makes MuxSet() very symmetric to MuxGet().
|
// Handy MACRO, makes MuxSet() very symmetric to MuxGet().
|
||||||
#define SWITCH_ID_LIST(ID, LIST) \
|
#define SWITCH_ID_LIST(ID, LIST) \
|
||||||
if (id == (ID)) { \
|
if (id == (ID)) { \
|
||||||
err = ChunkAssignDataImageInfo(&chunk, data, size, \
|
err = ChunkAssignDataImageInfo(&chunk, data, size, \
|
||||||
image_info, \
|
image_info, \
|
||||||
copy_data, kChunks[(ID)].chunkTag); \
|
copy_data, kChunks[(ID)].chunkTag); \
|
||||||
if (err == WEBP_MUX_OK) { \
|
if (err == WEBP_MUX_OK) { \
|
||||||
err = ChunkSetNth(&chunk, (LIST), nth); \
|
err = ChunkSetNth(&chunk, (LIST), nth); \
|
||||||
} \
|
} \
|
||||||
return err; \
|
return err; \
|
||||||
}
|
}
|
||||||
|
|
||||||
static WebPMuxError MuxSet(WebPMux* const mux, TAG_ID id, uint32_t nth,
|
static WebPMuxError MuxSet(WebPMux* const mux, TAG_ID id, uint32_t nth,
|
||||||
const uint8_t* data, uint32_t size,
|
const uint8_t* data, uint32_t size,
|
||||||
@ -142,7 +142,7 @@ static WebPImageInfo* CreateImageInfo(uint32_t x_offset, uint32_t y_offset,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create data for frame/tile given image_info.
|
// Create data for frame/tile given image_info.
|
||||||
static WebPMuxError CreateDataFromImageInfo(WebPImageInfo* image_info,
|
static WebPMuxError CreateDataFromImageInfo(const WebPImageInfo* image_info,
|
||||||
int is_frame,
|
int is_frame,
|
||||||
uint8_t** data, uint32_t* size) {
|
uint8_t** data, uint32_t* size) {
|
||||||
assert(data);
|
assert(data);
|
||||||
@ -154,7 +154,7 @@ static WebPMuxError CreateDataFromImageInfo(WebPImageInfo* image_info,
|
|||||||
if (*data == NULL) return WEBP_MUX_MEMORY_ERROR;
|
if (*data == NULL) return WEBP_MUX_MEMORY_ERROR;
|
||||||
|
|
||||||
// Fill in data according to frame/tile chunk format.
|
// Fill in data according to frame/tile chunk format.
|
||||||
PutLE32(*data, image_info->x_offset_);
|
PutLE32(*data + 0, image_info->x_offset_);
|
||||||
PutLE32(*data + 4, image_info->y_offset_);
|
PutLE32(*data + 4, image_info->y_offset_);
|
||||||
|
|
||||||
if (is_frame) {
|
if (is_frame) {
|
||||||
@ -168,17 +168,16 @@ static WebPMuxError CreateDataFromImageInfo(WebPImageInfo* image_info,
|
|||||||
// Outputs image data given data from a webp file (including RIFF header).
|
// Outputs image data given data from a webp file (including RIFF header).
|
||||||
static WebPMuxError GetImageData(const uint8_t* data, uint32_t size,
|
static WebPMuxError GetImageData(const uint8_t* data, uint32_t size,
|
||||||
WebPData* const image, WebPData* const alpha) {
|
WebPData* const image, WebPData* const alpha) {
|
||||||
if ((size < TAG_SIZE) || (memcmp(data, "RIFF", TAG_SIZE))) {
|
if (size < TAG_SIZE || memcmp(data, "RIFF", TAG_SIZE)) {
|
||||||
// It is NOT webp file data. Return input data as is.
|
// It is NOT webp file data. Return input data as is.
|
||||||
image->bytes_ = data;
|
image->bytes_ = data;
|
||||||
image->size_ = size;
|
image->size_ = size;
|
||||||
return WEBP_MUX_OK;
|
return WEBP_MUX_OK;
|
||||||
} else {
|
} else {
|
||||||
// It is webp file data. Extract image data from it.
|
// It is webp file data. Extract image data from it.
|
||||||
WebPMux* mux;
|
|
||||||
WebPMuxError err;
|
WebPMuxError err;
|
||||||
WebPMuxState mux_state;
|
WebPMuxState mux_state;
|
||||||
mux = WebPMuxCreate(data, size, 0, &mux_state);
|
WebPMux* const mux = WebPMuxCreate(data, size, 0, &mux_state);
|
||||||
if (mux == NULL || mux_state != WEBP_MUX_STATE_COMPLETE) {
|
if (mux == NULL || mux_state != WEBP_MUX_STATE_COMPLETE) {
|
||||||
return WEBP_MUX_BAD_DATA;
|
return WEBP_MUX_BAD_DATA;
|
||||||
}
|
}
|
||||||
@ -209,7 +208,7 @@ static WebPMuxError MuxDeleteAllNamedData(WebPMux* const mux,
|
|||||||
TAG_ID id;
|
TAG_ID id;
|
||||||
WebPChunk** chunk_list;
|
WebPChunk** chunk_list;
|
||||||
|
|
||||||
if ((mux == NULL) || (tag == NULL)) return WEBP_MUX_INVALID_ARGUMENT;
|
if (mux == NULL || tag == NULL) return WEBP_MUX_INVALID_ARGUMENT;
|
||||||
|
|
||||||
id = ChunkGetIdFromName(tag);
|
id = ChunkGetIdFromName(tag);
|
||||||
if (IsWPI(id)) return WEBP_MUX_INVALID_ARGUMENT;
|
if (IsWPI(id)) return WEBP_MUX_INVALID_ARGUMENT;
|
||||||
@ -237,7 +236,7 @@ WebPMuxError WebPMuxSetImage(WebPMux* const mux,
|
|||||||
WebPData image;
|
WebPData image;
|
||||||
const int has_alpha = (alpha_data != NULL && alpha_size != 0);
|
const int has_alpha = (alpha_data != NULL && alpha_size != 0);
|
||||||
|
|
||||||
if ((mux == NULL) || (data == NULL) || (size > MAX_CHUNK_PAYLOAD)) {
|
if (mux == NULL || data == NULL || size > MAX_CHUNK_PAYLOAD) {
|
||||||
return WEBP_MUX_INVALID_ARGUMENT;
|
return WEBP_MUX_INVALID_ARGUMENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,7 +274,7 @@ WebPMuxError WebPMuxSetMetadata(WebPMux* const mux, const uint8_t* data,
|
|||||||
uint32_t size, int copy_data) {
|
uint32_t size, int copy_data) {
|
||||||
WebPMuxError err;
|
WebPMuxError err;
|
||||||
|
|
||||||
if ((mux == NULL) || (data == NULL) || (size > MAX_CHUNK_PAYLOAD)) {
|
if (mux == NULL || data == NULL || size > MAX_CHUNK_PAYLOAD) {
|
||||||
return WEBP_MUX_INVALID_ARGUMENT;
|
return WEBP_MUX_INVALID_ARGUMENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,7 +290,7 @@ WebPMuxError WebPMuxSetColorProfile(WebPMux* const mux, const uint8_t* data,
|
|||||||
uint32_t size, int copy_data) {
|
uint32_t size, int copy_data) {
|
||||||
WebPMuxError err;
|
WebPMuxError err;
|
||||||
|
|
||||||
if ((mux == NULL) || (data == NULL) || (size > MAX_CHUNK_PAYLOAD)) {
|
if (mux == NULL || data == NULL || size > MAX_CHUNK_PAYLOAD) {
|
||||||
return WEBP_MUX_INVALID_ARGUMENT;
|
return WEBP_MUX_INVALID_ARGUMENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -314,7 +313,7 @@ WebPMuxError WebPMuxSetLoopCount(WebPMux* const mux, uint32_t loop_count) {
|
|||||||
if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err;
|
if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err;
|
||||||
|
|
||||||
// Add the given loop count.
|
// Add the given loop count.
|
||||||
data = (uint8_t *)malloc(kChunks[LOOP_ID].chunkSize);
|
data = (uint8_t*)malloc(kChunks[LOOP_ID].chunkSize);
|
||||||
if (data == NULL) return WEBP_MUX_MEMORY_ERROR;
|
if (data == NULL) return WEBP_MUX_MEMORY_ERROR;
|
||||||
|
|
||||||
PutLE32(data, loop_count);
|
PutLE32(data, loop_count);
|
||||||
@ -364,8 +363,8 @@ static WebPMuxError MuxAddFrameTileInternal(WebPMux* const mux, uint32_t nth,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create image_info object.
|
// Create image_info object.
|
||||||
image_info = CreateImageInfo(x_offset, y_offset, duration, image.bytes_,
|
image_info = CreateImageInfo(x_offset, y_offset, duration,
|
||||||
image.size_);
|
image.bytes_, image.size_);
|
||||||
if (image_info == NULL) {
|
if (image_info == NULL) {
|
||||||
MuxImageRelease(&wpi);
|
MuxImageRelease(&wpi);
|
||||||
return WEBP_MUX_MEMORY_ERROR;
|
return WEBP_MUX_MEMORY_ERROR;
|
||||||
@ -492,13 +491,12 @@ static WebPMuxError GetImageCanvasHeightWidth(const WebPMux* const mux,
|
|||||||
|
|
||||||
wpi = mux->images_;
|
wpi = mux->images_;
|
||||||
assert(wpi != NULL);
|
assert(wpi != NULL);
|
||||||
|
assert(wpi->vp8_ != NULL);
|
||||||
|
|
||||||
if (wpi->next_) {
|
if (wpi->next_) {
|
||||||
// Aggregate the bounding box for Animation frames & Tiled images.
|
// Aggregate the bounding box for animation frames & tiled images.
|
||||||
for (; wpi != NULL; wpi = wpi->next_) {
|
for (; wpi != NULL; wpi = wpi->next_) {
|
||||||
const WebPImageInfo* image_info;
|
const WebPImageInfo* image_info = wpi->vp8_->image_info_;
|
||||||
assert(wpi->vp8_ != NULL);
|
|
||||||
image_info = wpi->vp8_->image_info_;
|
|
||||||
|
|
||||||
if (image_info != NULL) {
|
if (image_info != NULL) {
|
||||||
const uint32_t max_x_pos = image_info->x_offset_ + image_info->width_;
|
const uint32_t max_x_pos = image_info->x_offset_ + image_info->width_;
|
||||||
@ -509,22 +507,18 @@ static WebPMuxError GetImageCanvasHeightWidth(const WebPMux* const mux,
|
|||||||
if (max_y_pos < image_info->y_offset_) { // Overflow occurred.
|
if (max_y_pos < image_info->y_offset_) { // Overflow occurred.
|
||||||
return WEBP_MUX_INVALID_ARGUMENT;
|
return WEBP_MUX_INVALID_ARGUMENT;
|
||||||
}
|
}
|
||||||
if (max_x_pos > max_x) {
|
if (max_x_pos > max_x) max_x = max_x_pos;
|
||||||
max_x = max_x_pos;
|
if (max_y_pos > max_y) max_y = max_y_pos;
|
||||||
}
|
|
||||||
if (max_y_pos > max_y) {
|
|
||||||
max_y = max_y_pos;
|
|
||||||
}
|
|
||||||
image_area += (image_info->width_ * image_info->height_);
|
image_area += (image_info->width_ * image_info->height_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*width = max_x;
|
*width = max_x;
|
||||||
*height = max_y;
|
*height = max_y;
|
||||||
// Crude check to validate that there are no image overlaps/holes for Tile
|
// Crude check to validate that there are no image overlaps/holes for tile
|
||||||
// images. Check that the aggregated image area for individual tiles exactly
|
// images. Check that the aggregated image area for individual tiles exactly
|
||||||
// matches the image area of the constructed Canvas. However, the area-match
|
// matches the image area of the constructed canvas. However, the area-match
|
||||||
// is necessary but not sufficient condition.
|
// is necessary but not sufficient condition.
|
||||||
if (!!(flags & TILE_FLAG) && (image_area != (max_x * max_y))) {
|
if ((flags & TILE_FLAG) && (image_area != (max_x * max_y))) {
|
||||||
*width = 0;
|
*width = 0;
|
||||||
*height = 0;
|
*height = 0;
|
||||||
return WEBP_MUX_INVALID_ARGUMENT;
|
return WEBP_MUX_INVALID_ARGUMENT;
|
||||||
@ -543,9 +537,9 @@ static WebPMuxError GetImageCanvasHeightWidth(const WebPMux* const mux,
|
|||||||
return WEBP_MUX_OK;
|
return WEBP_MUX_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Following VP8X format followed:
|
// VP8X format:
|
||||||
// Total Size : 12,
|
// Total Size : 12,
|
||||||
// Flags : 4 bytes,
|
// Flags : 4 bytes,
|
||||||
// Width : 4 bytes,
|
// Width : 4 bytes,
|
||||||
// Height : 4 bytes.
|
// Height : 4 bytes.
|
||||||
static WebPMuxError CreateVP8XChunk(WebPMux* const mux) {
|
static WebPMuxError CreateVP8XChunk(WebPMux* const mux) {
|
||||||
@ -610,8 +604,8 @@ static WebPMuxError CreateVP8XChunk(WebPMux* const mux) {
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
WebPMuxError WebPMuxAssemble(WebPMux* const mux, uint8_t** output_data,
|
WebPMuxError WebPMuxAssemble(WebPMux* const mux,
|
||||||
uint32_t* output_size) {
|
uint8_t** output_data, uint32_t* output_size) {
|
||||||
uint32_t size = 0;
|
uint32_t size = 0;
|
||||||
uint8_t* data = NULL;
|
uint8_t* data = NULL;
|
||||||
uint8_t* dst = NULL;
|
uint8_t* dst = NULL;
|
||||||
@ -649,9 +643,9 @@ WebPMuxError WebPMuxAssemble(WebPMux* const mux, uint8_t** output_data,
|
|||||||
|
|
||||||
// Allocate data.
|
// Allocate data.
|
||||||
size = ChunksListDiskSize(mux->vp8x_) + ChunksListDiskSize(mux->iccp_)
|
size = ChunksListDiskSize(mux->vp8x_) + ChunksListDiskSize(mux->iccp_)
|
||||||
+ ChunksListDiskSize(mux->loop_) + MuxImageListDiskSize(mux->images_)
|
+ ChunksListDiskSize(mux->loop_) + MuxImageListDiskSize(mux->images_)
|
||||||
+ ChunksListDiskSize(mux->meta_) + ChunksListDiskSize(mux->unknown_)
|
+ ChunksListDiskSize(mux->meta_) + ChunksListDiskSize(mux->unknown_)
|
||||||
+ RIFF_HEADER_SIZE;
|
+ RIFF_HEADER_SIZE;
|
||||||
|
|
||||||
data = (uint8_t*)malloc(size);
|
data = (uint8_t*)malloc(size);
|
||||||
if (data == NULL) return WEBP_MUX_MEMORY_ERROR;
|
if (data == NULL) return WEBP_MUX_MEMORY_ERROR;
|
||||||
|
@ -143,8 +143,8 @@ TAG_ID ChunkGetIdFromTag(uint32_t tag);
|
|||||||
WebPChunk* ChunkSearchList(WebPChunk* first, uint32_t nth, uint32_t tag);
|
WebPChunk* ChunkSearchList(WebPChunk* first, uint32_t nth, uint32_t tag);
|
||||||
|
|
||||||
// Fill the chunk with the given data & image_info.
|
// Fill the chunk with the given data & image_info.
|
||||||
WebPMuxError ChunkAssignDataImageInfo(WebPChunk* chunk, const uint8_t* data,
|
WebPMuxError ChunkAssignDataImageInfo(WebPChunk* chunk,
|
||||||
uint32_t data_size,
|
const uint8_t* data, uint32_t data_size,
|
||||||
WebPImageInfo* image_info,
|
WebPImageInfo* image_info,
|
||||||
int copy_data, uint32_t tag);
|
int copy_data, uint32_t tag);
|
||||||
|
|
||||||
|
@ -127,8 +127,8 @@ static int ChunkSearchListToSet(WebPChunk** chunk_list, uint32_t nth,
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Chunk writer methods.
|
// Chunk writer methods.
|
||||||
|
|
||||||
WebPMuxError ChunkAssignDataImageInfo(WebPChunk* chunk, const uint8_t* data,
|
WebPMuxError ChunkAssignDataImageInfo(WebPChunk* chunk,
|
||||||
uint32_t data_size,
|
const uint8_t* data, uint32_t data_size,
|
||||||
WebPImageInfo* image_info,
|
WebPImageInfo* image_info,
|
||||||
int copy_data, uint32_t tag) {
|
int copy_data, uint32_t tag) {
|
||||||
// For internally allocated chunks, always copy data & make it owner of data.
|
// For internally allocated chunks, always copy data & make it owner of data.
|
||||||
@ -231,11 +231,7 @@ uint8_t* ChunkListEmit(const WebPChunk* chunk_list, uint8_t* dst) {
|
|||||||
|
|
||||||
void MuxImageInit(WebPMuxImage* const wpi) {
|
void MuxImageInit(WebPMuxImage* const wpi) {
|
||||||
assert(wpi);
|
assert(wpi);
|
||||||
wpi->header_ = NULL;
|
memset(wpi, 0, sizeof(*wpi));
|
||||||
wpi->alpha_ = NULL;
|
|
||||||
wpi->vp8_ = NULL;
|
|
||||||
wpi->is_partial_ = 0;
|
|
||||||
wpi->next_ = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WebPMuxImage* MuxImageRelease(WebPMuxImage* const wpi) {
|
WebPMuxImage* MuxImageRelease(WebPMuxImage* const wpi) {
|
||||||
@ -260,8 +256,8 @@ int MuxImageCount(WebPMuxImage* const wpi_list, TAG_ID id) {
|
|||||||
WebPChunk** const wpi_chunk_ptr = MuxImageGetListFromId(current, id);
|
WebPChunk** const wpi_chunk_ptr = MuxImageGetListFromId(current, id);
|
||||||
assert(wpi_chunk_ptr != NULL);
|
assert(wpi_chunk_ptr != NULL);
|
||||||
|
|
||||||
if ((*wpi_chunk_ptr != NULL) &&
|
if (*wpi_chunk_ptr != NULL &&
|
||||||
((*wpi_chunk_ptr)->tag_ == kChunks[id].chunkTag)) {
|
(*wpi_chunk_ptr)->tag_ == kChunks[id].chunkTag) {
|
||||||
++count;
|
++count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -374,7 +370,7 @@ WebPMuxError MuxImageGetNth(const WebPMuxImage** wpi_list, uint32_t nth,
|
|||||||
assert(wpi_list);
|
assert(wpi_list);
|
||||||
assert(wpi);
|
assert(wpi);
|
||||||
if (!SearchImageToGetOrDelete((WebPMuxImage**)wpi_list, nth, id,
|
if (!SearchImageToGetOrDelete((WebPMuxImage**)wpi_list, nth, id,
|
||||||
(WebPMuxImage*** const)&wpi_list)) {
|
(WebPMuxImage***)&wpi_list)) {
|
||||||
return WEBP_MUX_NOT_FOUND;
|
return WEBP_MUX_NOT_FOUND;
|
||||||
}
|
}
|
||||||
*wpi = (WebPMuxImage*)*wpi_list;
|
*wpi = (WebPMuxImage*)*wpi_list;
|
||||||
@ -438,12 +434,9 @@ WebPChunk** GetChunkListFromId(const WebPMux* mux, TAG_ID id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
WebPMuxError ValidateForImage(const WebPMux* const mux) {
|
WebPMuxError ValidateForImage(const WebPMux* const mux) {
|
||||||
int num_vp8;
|
const int num_vp8 = MuxImageCount(mux->images_, IMAGE_ID);
|
||||||
int num_frames;
|
const int num_frames = MuxImageCount(mux->images_, FRAME_ID);
|
||||||
int num_tiles;
|
const int num_tiles = MuxImageCount(mux->images_, TILE_ID);
|
||||||
num_vp8 = MuxImageCount(mux->images_, IMAGE_ID);
|
|
||||||
num_frames = MuxImageCount(mux->images_, FRAME_ID);
|
|
||||||
num_tiles = MuxImageCount(mux->images_, TILE_ID);
|
|
||||||
|
|
||||||
if (num_vp8 == 0) {
|
if (num_vp8 == 0) {
|
||||||
// No images in mux.
|
// No images in mux.
|
||||||
|
@ -226,8 +226,7 @@ WebPMuxError WebPMuxGetFeatures(const WebPMux* const mux, uint32_t* flags) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
WebPMuxError WebPMuxGetImage(const WebPMux* const mux,
|
WebPMuxError WebPMuxGetImage(const WebPMux* const mux,
|
||||||
WebPData* const image,
|
WebPData* const image, WebPData* const alpha) {
|
||||||
WebPData* const alpha) {
|
|
||||||
WebPMuxError err;
|
WebPMuxError err;
|
||||||
WebPMuxImage* wpi = NULL;
|
WebPMuxImage* wpi = NULL;
|
||||||
|
|
||||||
@ -324,7 +323,7 @@ static WebPMuxError MuxGetFrameTileInternal(const WebPMux* const mux,
|
|||||||
frame_tile_size = wpi->header_->payload_size_;
|
frame_tile_size = wpi->header_->payload_size_;
|
||||||
|
|
||||||
if (frame_tile_size < kChunks[id].chunkSize) return WEBP_MUX_BAD_DATA;
|
if (frame_tile_size < kChunks[id].chunkSize) return WEBP_MUX_BAD_DATA;
|
||||||
*x_offset = GetLE32(frame_tile_data);
|
*x_offset = GetLE32(frame_tile_data + 0);
|
||||||
*y_offset = GetLE32(frame_tile_data + 4);
|
*y_offset = GetLE32(frame_tile_data + 4);
|
||||||
if (is_frame) *duration = GetLE32(frame_tile_data + 16);
|
if (is_frame) *duration = GetLE32(frame_tile_data + 16);
|
||||||
|
|
||||||
@ -370,7 +369,7 @@ static int CountChunks(WebPChunk* const chunk_list, uint32_t tag) {
|
|||||||
int count = 0;
|
int count = 0;
|
||||||
WebPChunk* current;
|
WebPChunk* current;
|
||||||
for (current = chunk_list; current != NULL; current = current->next_) {
|
for (current = chunk_list; current != NULL; current = current->next_) {
|
||||||
if ((tag == NIL_TAG) || (current->tag_ == tag)) {
|
if (tag == NIL_TAG || current->tag_ == tag) {
|
||||||
count++; // Count chunks whose tags match.
|
count++; // Count chunks whose tags match.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -210,17 +210,17 @@ WEBP_FILTER_TYPE EstimateBestFilter(const uint8_t* data,
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
const WebPFilterFunc WebPFilters[WEBP_FILTER_LAST] = {
|
const WebPFilterFunc WebPFilters[WEBP_FILTER_LAST] = {
|
||||||
NULL, // WEBP_FILTER_NONE
|
NULL, // WEBP_FILTER_NONE
|
||||||
HorizontalFilter, // WEBP_FILTER_HORIZONTAL
|
HorizontalFilter, // WEBP_FILTER_HORIZONTAL
|
||||||
VerticalFilter, // WEBP_FILTER_VERTICAL
|
VerticalFilter, // WEBP_FILTER_VERTICAL
|
||||||
GradientFilter // WEBP_FILTER_GRADIENT
|
GradientFilter // WEBP_FILTER_GRADIENT
|
||||||
};
|
};
|
||||||
|
|
||||||
const WebPFilterFunc WebPUnfilters[WEBP_FILTER_LAST] = {
|
const WebPFilterFunc WebPUnfilters[WEBP_FILTER_LAST] = {
|
||||||
NULL, // WEBP_FILTER_NONE
|
NULL, // WEBP_FILTER_NONE
|
||||||
HorizontalUnfilter, // WEBP_FILTER_HORIZONTAL
|
HorizontalUnfilter, // WEBP_FILTER_HORIZONTAL
|
||||||
VerticalUnfilter, // WEBP_FILTER_VERTICAL
|
VerticalUnfilter, // WEBP_FILTER_VERTICAL
|
||||||
GradientUnfilter // WEBP_FILTER_GRADIENT
|
GradientUnfilter // WEBP_FILTER_GRADIENT
|
||||||
};
|
};
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
@ -115,7 +115,8 @@ WEBP_EXTERN(void) WebPMuxDelete(WebPMux* const mux);
|
|||||||
// A pointer to the mux object created from given data - on success.
|
// A pointer to the mux object created from given data - on success.
|
||||||
// NULL - In case of invalid data or memory error.
|
// NULL - In case of invalid data or memory error.
|
||||||
WEBP_EXTERN(WebPMux*) WebPMuxCreate(const uint8_t* data, uint32_t size,
|
WEBP_EXTERN(WebPMux*) WebPMuxCreate(const uint8_t* data, uint32_t size,
|
||||||
int copy_data, WebPMuxState* mux_state);
|
int copy_data,
|
||||||
|
WebPMuxState* const mux_state);
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Single Image.
|
// Single Image.
|
||||||
|
Loading…
Reference in New Issue
Block a user