github.com/cellofellow/gopkg@v0.0.0-20140722061823-eec0544a62ad/image/webp/libwebp/src/enc/token.c (about) 1 // Copyright 2011 Google Inc. All Rights Reserved. 2 // 3 // Use of this source code is governed by a BSD-style license 4 // that can be found in the COPYING file in the root of the source 5 // tree. An additional intellectual property rights grant can be found 6 // in the file PATENTS. All contributing project authors may 7 // be found in the AUTHORS file in the root of the source tree. 8 // ----------------------------------------------------------------------------- 9 // 10 // Paginated token buffer 11 // 12 // A 'token' is a bit value associated with a probability, either fixed 13 // or a later-to-be-determined after statistics have been collected. 14 // For dynamic probability, we just record the slot id (idx) for the probability 15 // value in the final probability array (uint8_t* probas in VP8EmitTokens). 16 // 17 // Author: Skal (pascal.massimino@gmail.com) 18 19 #include <assert.h> 20 #include <stdlib.h> 21 #include <string.h> 22 23 #include "./cost.h" 24 #include "./vp8enci.h" 25 26 #if !defined(DISABLE_TOKEN_BUFFER) 27 28 // we use pages to reduce the number of memcpy() 29 #define MAX_NUM_TOKEN 8192 // max number of token per page 30 #define FIXED_PROBA_BIT (1u << 14) 31 32 struct VP8Tokens { 33 uint16_t tokens_[MAX_NUM_TOKEN]; // bit#15: bit 34 // bit #14: constant proba or idx 35 // bits 0..13: slot or constant proba 36 VP8Tokens* next_; 37 }; 38 39 //------------------------------------------------------------------------------ 40 41 void VP8TBufferInit(VP8TBuffer* const b) { 42 b->tokens_ = NULL; 43 b->pages_ = NULL; 44 b->last_page_ = &b->pages_; 45 b->left_ = 0; 46 b->error_ = 0; 47 } 48 49 void VP8TBufferClear(VP8TBuffer* const b) { 50 if (b != NULL) { 51 const VP8Tokens* p = b->pages_; 52 while (p != NULL) { 53 const VP8Tokens* const next = p->next_; 54 free((void*)p); 55 p = next; 56 } 57 VP8TBufferInit(b); 58 } 59 } 60 61 static int TBufferNewPage(VP8TBuffer* const b) { 62 VP8Tokens* const page = b->error_ ? NULL : (VP8Tokens*)malloc(sizeof(*page)); 63 if (page == NULL) { 64 b->error_ = 1; 65 return 0; 66 } 67 *b->last_page_ = page; 68 b->last_page_ = &page->next_; 69 b->left_ = MAX_NUM_TOKEN; 70 b->tokens_ = page->tokens_; 71 page->next_ = NULL; 72 return 1; 73 } 74 75 //------------------------------------------------------------------------------ 76 77 #define TOKEN_ID(t, b, ctx, p) \ 78 ((p) + NUM_PROBAS * ((ctx) + NUM_CTX * ((b) + NUM_BANDS * (t)))) 79 80 static WEBP_INLINE int AddToken(VP8TBuffer* const b, 81 int bit, uint32_t proba_idx) { 82 assert(proba_idx < FIXED_PROBA_BIT); 83 assert(bit == 0 || bit == 1); 84 if (b->left_ > 0 || TBufferNewPage(b)) { 85 const int slot = --b->left_; 86 b->tokens_[slot] = (bit << 15) | proba_idx; 87 } 88 return bit; 89 } 90 91 static WEBP_INLINE void AddConstantToken(VP8TBuffer* const b, 92 int bit, int proba) { 93 assert(proba < 256); 94 assert(bit == 0 || bit == 1); 95 if (b->left_ > 0 || TBufferNewPage(b)) { 96 const int slot = --b->left_; 97 b->tokens_[slot] = (bit << 15) | FIXED_PROBA_BIT | proba; 98 } 99 } 100 101 int VP8RecordCoeffTokens(int ctx, int coeff_type, int first, int last, 102 const int16_t* const coeffs, 103 VP8TBuffer* const tokens) { 104 int n = first; 105 uint32_t base_id = TOKEN_ID(coeff_type, n, ctx, 0); 106 if (!AddToken(tokens, last >= 0, base_id + 0)) { 107 return 0; 108 } 109 110 while (n < 16) { 111 const int c = coeffs[n++]; 112 const int sign = c < 0; 113 int v = sign ? -c : c; 114 if (!AddToken(tokens, v != 0, base_id + 1)) { 115 ctx = 0; 116 base_id = TOKEN_ID(coeff_type, VP8EncBands[n], ctx, 0); 117 continue; 118 } 119 if (!AddToken(tokens, v > 1, base_id + 2)) { 120 ctx = 1; 121 } else { 122 if (!AddToken(tokens, v > 4, base_id + 3)) { 123 if (AddToken(tokens, v != 2, base_id + 4)) 124 AddToken(tokens, v == 4, base_id + 5); 125 } else if (!AddToken(tokens, v > 10, base_id + 6)) { 126 if (!AddToken(tokens, v > 6, base_id + 7)) { 127 AddConstantToken(tokens, v == 6, 159); 128 } else { 129 AddConstantToken(tokens, v >= 9, 165); 130 AddConstantToken(tokens, !(v & 1), 145); 131 } 132 } else { 133 int mask; 134 const uint8_t* tab; 135 if (v < 3 + (8 << 1)) { // VP8Cat3 (3b) 136 AddToken(tokens, 0, base_id + 8); 137 AddToken(tokens, 0, base_id + 9); 138 v -= 3 + (8 << 0); 139 mask = 1 << 2; 140 tab = VP8Cat3; 141 } else if (v < 3 + (8 << 2)) { // VP8Cat4 (4b) 142 AddToken(tokens, 0, base_id + 8); 143 AddToken(tokens, 1, base_id + 9); 144 v -= 3 + (8 << 1); 145 mask = 1 << 3; 146 tab = VP8Cat4; 147 } else if (v < 3 + (8 << 3)) { // VP8Cat5 (5b) 148 AddToken(tokens, 1, base_id + 8); 149 AddToken(tokens, 0, base_id + 10); 150 v -= 3 + (8 << 2); 151 mask = 1 << 4; 152 tab = VP8Cat5; 153 } else { // VP8Cat6 (11b) 154 AddToken(tokens, 1, base_id + 8); 155 AddToken(tokens, 1, base_id + 10); 156 v -= 3 + (8 << 3); 157 mask = 1 << 10; 158 tab = VP8Cat6; 159 } 160 while (mask) { 161 AddConstantToken(tokens, !!(v & mask), *tab++); 162 mask >>= 1; 163 } 164 } 165 ctx = 2; 166 } 167 AddConstantToken(tokens, sign, 128); 168 base_id = TOKEN_ID(coeff_type, VP8EncBands[n], ctx, 0); 169 if (n == 16 || !AddToken(tokens, n <= last, base_id + 0)) { 170 return 1; // EOB 171 } 172 } 173 return 1; 174 } 175 176 #undef TOKEN_ID 177 178 //------------------------------------------------------------------------------ 179 // This function works, but isn't currently used. Saved for later. 180 181 #if 0 182 183 static void Record(int bit, proba_t* const stats) { 184 proba_t p = *stats; 185 if (p >= 0xffff0000u) { // an overflow is inbound. 186 p = ((p + 1u) >> 1) & 0x7fff7fffu; // -> divide the stats by 2. 187 } 188 // record bit count (lower 16 bits) and increment total count (upper 16 bits). 189 p += 0x00010000u + bit; 190 *stats = p; 191 } 192 193 void VP8TokenToStats(const VP8TBuffer* const b, proba_t* const stats) { 194 const VP8Tokens* p = b->pages_; 195 while (p != NULL) { 196 const int N = (p->next_ == NULL) ? b->left_ : 0; 197 int n = MAX_NUM_TOKEN; 198 while (n-- > N) { 199 const uint16_t token = p->tokens_[n]; 200 if (!(token & FIXED_PROBA_BIT)) { 201 Record((token >> 15) & 1, stats + (token & 0x3fffu)); 202 } 203 } 204 p = p->next_; 205 } 206 } 207 208 #endif // 0 209 210 //------------------------------------------------------------------------------ 211 // Final coding pass, with known probabilities 212 213 int VP8EmitTokens(VP8TBuffer* const b, VP8BitWriter* const bw, 214 const uint8_t* const probas, int final_pass) { 215 const VP8Tokens* p = b->pages_; 216 (void)final_pass; 217 if (b->error_) return 0; 218 while (p != NULL) { 219 const VP8Tokens* const next = p->next_; 220 const int N = (next == NULL) ? b->left_ : 0; 221 int n = MAX_NUM_TOKEN; 222 while (n-- > N) { 223 const uint16_t token = p->tokens_[n]; 224 const int bit = (token >> 15) & 1; 225 if (token & FIXED_PROBA_BIT) { 226 VP8PutBit(bw, bit, token & 0xffu); // constant proba 227 } else { 228 VP8PutBit(bw, bit, probas[token & 0x3fffu]); 229 } 230 } 231 if (final_pass) free((void*)p); 232 p = next; 233 } 234 if (final_pass) b->pages_ = NULL; 235 return 1; 236 } 237 238 // Size estimation 239 size_t VP8EstimateTokenSize(VP8TBuffer* const b, const uint8_t* const probas) { 240 size_t size = 0; 241 const VP8Tokens* p = b->pages_; 242 if (b->error_) return 0; 243 while (p != NULL) { 244 const VP8Tokens* const next = p->next_; 245 const int N = (next == NULL) ? b->left_ : 0; 246 int n = MAX_NUM_TOKEN; 247 while (n-- > N) { 248 const uint16_t token = p->tokens_[n]; 249 const int bit = token & (1 << 15); 250 if (token & FIXED_PROBA_BIT) { 251 size += VP8BitCost(bit, token & 0xffu); 252 } else { 253 size += VP8BitCost(bit, probas[token & 0x3fffu]); 254 } 255 } 256 p = next; 257 } 258 return size; 259 } 260 261 //------------------------------------------------------------------------------ 262 263 #else // DISABLE_TOKEN_BUFFER 264 265 void VP8TBufferInit(VP8TBuffer* const b) { 266 (void)b; 267 } 268 void VP8TBufferClear(VP8TBuffer* const b) { 269 (void)b; 270 } 271 272 #endif // !DISABLE_TOKEN_BUFFER 273