diff --git a/src/enc/token.c b/src/enc/token.c index 319a262f..16759509 100644 --- a/src/enc/token.c +++ b/src/enc/token.c @@ -27,23 +27,27 @@ #if !defined(DISABLE_TOKEN_BUFFER) // we use pages to reduce the number of memcpy() -#define MAX_NUM_TOKEN 8192 // max number of token per page +#define MIN_PAGE_SIZE 8192 // minimum number of token per page #define FIXED_PROBA_BIT (1u << 14) +typedef uint16_t token_t; // bit#15: bit + // bit #14: constant proba or idx + // bits 0..13: slot or constant proba struct VP8Tokens { - uint16_t tokens_[MAX_NUM_TOKEN]; // bit#15: bit - // bit #14: constant proba or idx - // bits 0..13: slot or constant proba - VP8Tokens* next_; + VP8Tokens* next_; // pointer to next page }; +// Token data is located in memory just after the next_ field. +// This macro is used to return their address and hide the trick. +#define TOKEN_DATA(p) ((token_t*)&(p)[1]) //------------------------------------------------------------------------------ -void VP8TBufferInit(VP8TBuffer* const b) { +void VP8TBufferInit(VP8TBuffer* const b, int page_size) { b->tokens_ = NULL; b->pages_ = NULL; b->last_page_ = &b->pages_; b->left_ = 0; + b->page_size_ = (page_size < MIN_PAGE_SIZE) ? MIN_PAGE_SIZE : page_size; b->error_ = 0; } @@ -55,22 +59,26 @@ void VP8TBufferClear(VP8TBuffer* const b) { WebPSafeFree((void*)p); p = next; } - VP8TBufferInit(b); + VP8TBufferInit(b, b->page_size_); } } static int TBufferNewPage(VP8TBuffer* const b) { - VP8Tokens* const page = - b->error_ ? NULL : (VP8Tokens*)WebPSafeMalloc(1ULL, sizeof(*page)); + VP8Tokens* page = NULL; + const size_t size = sizeof(*page) + b->page_size_ * sizeof(token_t); + if (!b->error_) { + page = (VP8Tokens*)WebPSafeMalloc(1ULL, size); + } if (page == NULL) { b->error_ = 1; return 0; } + page->next_ = NULL; + *b->last_page_ = page; b->last_page_ = &page->next_; - b->left_ = MAX_NUM_TOKEN; - b->tokens_ = page->tokens_; - page->next_ = NULL; + b->left_ = b->page_size_; + b->tokens_ = TOKEN_DATA(page); return 1; } @@ -197,8 +205,9 @@ void VP8TokenToStats(const VP8TBuffer* const b, proba_t* const stats) { while (p != NULL) { const int N = (p->next_ == NULL) ? b->left_ : 0; int n = MAX_NUM_TOKEN; + const token_t* const tokens = TOKEN_DATA(p); while (n-- > N) { - const uint16_t token = p->tokens_[n]; + const token_t token = tokens[n]; if (!(token & FIXED_PROBA_BIT)) { Record((token >> 15) & 1, stats + (token & 0x3fffu)); } @@ -220,9 +229,10 @@ int VP8EmitTokens(VP8TBuffer* const b, VP8BitWriter* const bw, while (p != NULL) { const VP8Tokens* const next = p->next_; const int N = (next == NULL) ? b->left_ : 0; - int n = MAX_NUM_TOKEN; + int n = b->page_size_; + const token_t* const tokens = TOKEN_DATA(p); while (n-- > N) { - const uint16_t token = p->tokens_[n]; + const token_t token = tokens[n]; const int bit = (token >> 15) & 1; if (token & FIXED_PROBA_BIT) { VP8PutBit(bw, bit, token & 0xffu); // constant proba @@ -245,9 +255,10 @@ size_t VP8EstimateTokenSize(VP8TBuffer* const b, const uint8_t* const probas) { while (p != NULL) { const VP8Tokens* const next = p->next_; const int N = (next == NULL) ? b->left_ : 0; - int n = MAX_NUM_TOKEN; + int n = b->page_size_; + const token_t* const tokens = TOKEN_DATA(p); while (n-- > N) { - const uint16_t token = p->tokens_[n]; + const token_t token = tokens[n]; const int bit = token & (1 << 15); if (token & FIXED_PROBA_BIT) { size += VP8BitCost(bit, token & 0xffu); diff --git a/src/enc/vp8enci.h b/src/enc/vp8enci.h index 7d7f262f..39430bbd 100644 --- a/src/enc/vp8enci.h +++ b/src/enc/vp8enci.h @@ -363,12 +363,14 @@ typedef struct { VP8Tokens* pages_; // first page VP8Tokens** last_page_; // last page uint16_t* tokens_; // set to (*last_page_)->tokens_ - int left_; // how many free tokens left before the page is full. + int left_; // how many free tokens left before the page is full + int page_size_; // number of tokens per page #endif int error_; // true in case of malloc error } VP8TBuffer; -void VP8TBufferInit(VP8TBuffer* const b); // initialize an empty buffer +// initialize an empty buffer +void VP8TBufferInit(VP8TBuffer* const b, int page_size); void VP8TBufferClear(VP8TBuffer* const b); // de-allocate pages memory #if !defined(DISABLE_TOKEN_BUFFER) diff --git a/src/enc/webpenc.c b/src/enc/webpenc.c index 177a6e28..ec8cebcf 100644 --- a/src/enc/webpenc.c +++ b/src/enc/webpenc.c @@ -258,7 +258,12 @@ static VP8Encoder* InitVP8Encoder(const WebPConfig* const config, VP8EncInitLayer(enc); #endif - VP8TBufferInit(&enc->tokens_); + // lower quality means smaller output -> we modulate a little the page + // size based on quality. This is just a crude 1rst-order prediction. + { + const float scale = 1.f + config->quality * 5.f / 100.f; // in [1,6] + VP8TBufferInit(&enc->tokens_, (int)(mb_w * mb_h * 4 * scale)); + } return enc; }