syntax_enc.c (12758B)
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 // Header syntax writing 11 // 12 // Author: Skal (pascal.massimino@gmail.com) 13 14 #include <assert.h> 15 #include <stddef.h> 16 17 #include "src/dec/common_dec.h" 18 #include "src/webp/types.h" 19 #include "src/enc/vp8i_enc.h" 20 #include "src/utils/bit_writer_utils.h" 21 #include "src/utils/utils.h" 22 #include "src/webp/encode.h" 23 #include "src/webp/format_constants.h" // RIFF constants 24 #include "src/webp/mux_types.h" // ALPHA_FLAG 25 26 //------------------------------------------------------------------------------ 27 // Helper functions 28 29 static int IsVP8XNeeded(const VP8Encoder* const enc) { 30 return !!enc->has_alpha; // Currently the only case when VP8X is needed. 31 // This could change in the future. 32 } 33 34 static int PutPaddingByte(const WebPPicture* const pic) { 35 const uint8_t pad_byte[1] = { 0 }; 36 return !!pic->writer(pad_byte, 1, pic); 37 } 38 39 //------------------------------------------------------------------------------ 40 // Writers for header's various pieces (in order of appearance) 41 42 static WebPEncodingError PutRIFFHeader(const VP8Encoder* const enc, 43 size_t riff_size) { 44 const WebPPicture* const pic = enc->pic; 45 uint8_t riff[RIFF_HEADER_SIZE] = { 46 'R', 'I', 'F', 'F', 0, 0, 0, 0, 'W', 'E', 'B', 'P' 47 }; 48 assert(riff_size == (uint32_t)riff_size); 49 PutLE32(riff + TAG_SIZE, (uint32_t)riff_size); 50 if (!pic->writer(riff, sizeof(riff), pic)) { 51 return VP8_ENC_ERROR_BAD_WRITE; 52 } 53 return VP8_ENC_OK; 54 } 55 56 static WebPEncodingError PutVP8XHeader(const VP8Encoder* const enc) { 57 const WebPPicture* const pic = enc->pic; 58 uint8_t vp8x[CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE] = { 59 'V', 'P', '8', 'X' 60 }; 61 uint32_t flags = 0; 62 63 assert(IsVP8XNeeded(enc)); 64 assert(pic->width >= 1 && pic->height >= 1); 65 assert(pic->width <= MAX_CANVAS_SIZE && pic->height <= MAX_CANVAS_SIZE); 66 67 if (enc->has_alpha) { 68 flags |= ALPHA_FLAG; 69 } 70 71 PutLE32(vp8x + TAG_SIZE, VP8X_CHUNK_SIZE); 72 PutLE32(vp8x + CHUNK_HEADER_SIZE, flags); 73 PutLE24(vp8x + CHUNK_HEADER_SIZE + 4, pic->width - 1); 74 PutLE24(vp8x + CHUNK_HEADER_SIZE + 7, pic->height - 1); 75 if (!pic->writer(vp8x, sizeof(vp8x), pic)) { 76 return VP8_ENC_ERROR_BAD_WRITE; 77 } 78 return VP8_ENC_OK; 79 } 80 81 static WebPEncodingError PutAlphaChunk(const VP8Encoder* const enc) { 82 const WebPPicture* const pic = enc->pic; 83 uint8_t alpha_chunk_hdr[CHUNK_HEADER_SIZE] = { 84 'A', 'L', 'P', 'H' 85 }; 86 87 assert(enc->has_alpha); 88 89 // Alpha chunk header. 90 PutLE32(alpha_chunk_hdr + TAG_SIZE, enc->alpha_data_size); 91 if (!pic->writer(alpha_chunk_hdr, sizeof(alpha_chunk_hdr), pic)) { 92 return VP8_ENC_ERROR_BAD_WRITE; 93 } 94 95 // Alpha chunk data. 96 if (!pic->writer(enc->alpha_data, enc->alpha_data_size, pic)) { 97 return VP8_ENC_ERROR_BAD_WRITE; 98 } 99 100 // Padding. 101 if ((enc->alpha_data_size & 1) && !PutPaddingByte(pic)) { 102 return VP8_ENC_ERROR_BAD_WRITE; 103 } 104 return VP8_ENC_OK; 105 } 106 107 static WebPEncodingError PutVP8Header(const WebPPicture* const pic, 108 size_t vp8_size) { 109 uint8_t vp8_chunk_hdr[CHUNK_HEADER_SIZE] = { 110 'V', 'P', '8', ' ' 111 }; 112 assert(vp8_size == (uint32_t)vp8_size); 113 PutLE32(vp8_chunk_hdr + TAG_SIZE, (uint32_t)vp8_size); 114 if (!pic->writer(vp8_chunk_hdr, sizeof(vp8_chunk_hdr), pic)) { 115 return VP8_ENC_ERROR_BAD_WRITE; 116 } 117 return VP8_ENC_OK; 118 } 119 120 static WebPEncodingError PutVP8FrameHeader(const WebPPicture* const pic, 121 int profile, size_t size0) { 122 uint8_t vp8_frm_hdr[VP8_FRAME_HEADER_SIZE]; 123 uint32_t bits; 124 125 if (size0 >= VP8_MAX_PARTITION0_SIZE) { // partition #0 is too big to fit 126 return VP8_ENC_ERROR_PARTITION0_OVERFLOW; 127 } 128 129 // Paragraph 9.1. 130 bits = 0 // keyframe (1b) 131 | (profile << 1) // profile (3b) 132 | (1 << 4) // visible (1b) 133 | ((uint32_t)size0 << 5); // partition length (19b) 134 vp8_frm_hdr[0] = (bits >> 0) & 0xff; 135 vp8_frm_hdr[1] = (bits >> 8) & 0xff; 136 vp8_frm_hdr[2] = (bits >> 16) & 0xff; 137 // signature 138 vp8_frm_hdr[3] = (VP8_SIGNATURE >> 16) & 0xff; 139 vp8_frm_hdr[4] = (VP8_SIGNATURE >> 8) & 0xff; 140 vp8_frm_hdr[5] = (VP8_SIGNATURE >> 0) & 0xff; 141 // dimensions 142 vp8_frm_hdr[6] = pic->width & 0xff; 143 vp8_frm_hdr[7] = pic->width >> 8; 144 vp8_frm_hdr[8] = pic->height & 0xff; 145 vp8_frm_hdr[9] = pic->height >> 8; 146 147 if (!pic->writer(vp8_frm_hdr, sizeof(vp8_frm_hdr), pic)) { 148 return VP8_ENC_ERROR_BAD_WRITE; 149 } 150 return VP8_ENC_OK; 151 } 152 153 // WebP Headers. 154 static int PutWebPHeaders(const VP8Encoder* const enc, size_t size0, 155 size_t vp8_size, size_t riff_size) { 156 WebPPicture* const pic = enc->pic; 157 WebPEncodingError err = VP8_ENC_OK; 158 159 // RIFF header. 160 err = PutRIFFHeader(enc, riff_size); 161 if (err != VP8_ENC_OK) goto Error; 162 163 // VP8X. 164 if (IsVP8XNeeded(enc)) { 165 err = PutVP8XHeader(enc); 166 if (err != VP8_ENC_OK) goto Error; 167 } 168 169 // Alpha. 170 if (enc->has_alpha) { 171 err = PutAlphaChunk(enc); 172 if (err != VP8_ENC_OK) goto Error; 173 } 174 175 // VP8 header. 176 err = PutVP8Header(pic, vp8_size); 177 if (err != VP8_ENC_OK) goto Error; 178 179 // VP8 frame header. 180 err = PutVP8FrameHeader(pic, enc->profile, size0); 181 if (err != VP8_ENC_OK) goto Error; 182 183 // All OK. 184 return 1; 185 186 // Error. 187 Error: 188 return WebPEncodingSetError(pic, err); 189 } 190 191 // Segmentation header 192 static void PutSegmentHeader(VP8BitWriter* const bw, 193 const VP8Encoder* const enc) { 194 const VP8EncSegmentHeader* const hdr = &enc->segment_hdr; 195 const VP8EncProba* const proba = &enc->proba; 196 if (VP8PutBitUniform(bw, (hdr->num_segments > 1))) { 197 // We always 'update' the quant and filter strength values 198 const int update_data = 1; 199 int s; 200 VP8PutBitUniform(bw, hdr->update_map); 201 if (VP8PutBitUniform(bw, update_data)) { 202 // we always use absolute values, not relative ones 203 VP8PutBitUniform(bw, 1); // (segment_feature_mode = 1. Paragraph 9.3.) 204 for (s = 0; s < NUM_MB_SEGMENTS; ++s) { 205 VP8PutSignedBits(bw, enc->dqm[s].quant, 7); 206 } 207 for (s = 0; s < NUM_MB_SEGMENTS; ++s) { 208 VP8PutSignedBits(bw, enc->dqm[s].fstrength, 6); 209 } 210 } 211 if (hdr->update_map) { 212 for (s = 0; s < 3; ++s) { 213 if (VP8PutBitUniform(bw, (proba->segments[s] != 255u))) { 214 VP8PutBits(bw, proba->segments[s], 8); 215 } 216 } 217 } 218 } 219 } 220 221 // Filtering parameters header 222 static void PutFilterHeader(VP8BitWriter* const bw, 223 const VP8EncFilterHeader* const hdr) { 224 const int use_lf_delta = (hdr->i4x4_lf_delta != 0); 225 VP8PutBitUniform(bw, hdr->simple); 226 VP8PutBits(bw, hdr->level, 6); 227 VP8PutBits(bw, hdr->sharpness, 3); 228 if (VP8PutBitUniform(bw, use_lf_delta)) { 229 // '0' is the default value for i4x4_lf_delta at frame #0. 230 const int need_update = (hdr->i4x4_lf_delta != 0); 231 if (VP8PutBitUniform(bw, need_update)) { 232 // we don't use ref_lf_delta => emit four 0 bits 233 VP8PutBits(bw, 0, 4); 234 // we use mode_lf_delta for i4x4 235 VP8PutSignedBits(bw, hdr->i4x4_lf_delta, 6); 236 VP8PutBits(bw, 0, 3); // all others unused 237 } 238 } 239 } 240 241 // Nominal quantization parameters 242 static void PutQuant(VP8BitWriter* const bw, 243 const VP8Encoder* const enc) { 244 VP8PutBits(bw, enc->base_quant, 7); 245 VP8PutSignedBits(bw, enc->dq_y1_dc, 4); 246 VP8PutSignedBits(bw, enc->dq_y2_dc, 4); 247 VP8PutSignedBits(bw, enc->dq_y2_ac, 4); 248 VP8PutSignedBits(bw, enc->dq_uv_dc, 4); 249 VP8PutSignedBits(bw, enc->dq_uv_ac, 4); 250 } 251 252 // Partition sizes 253 static int EmitPartitionsSize(const VP8Encoder* const enc, 254 WebPPicture* const pic) { 255 uint8_t buf[3 * (MAX_NUM_PARTITIONS - 1)]; 256 int p; 257 for (p = 0; p < enc->num_parts - 1; ++p) { 258 const size_t part_size = VP8BitWriterSize(enc->parts + p); 259 if (part_size >= VP8_MAX_PARTITION_SIZE) { 260 return WebPEncodingSetError(pic, VP8_ENC_ERROR_PARTITION_OVERFLOW); 261 } 262 buf[3 * p + 0] = (part_size >> 0) & 0xff; 263 buf[3 * p + 1] = (part_size >> 8) & 0xff; 264 buf[3 * p + 2] = (part_size >> 16) & 0xff; 265 } 266 if (p && !pic->writer(buf, 3 * p, pic)) { 267 return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_WRITE); 268 } 269 return 1; 270 } 271 272 //------------------------------------------------------------------------------ 273 274 static int GeneratePartition0(VP8Encoder* const enc) { 275 VP8BitWriter* const bw = &enc->bw; 276 const int mb_size = enc->mb_w * enc->mb_h; 277 uint64_t pos1, pos2, pos3; 278 279 pos1 = VP8BitWriterPos(bw); 280 if (!VP8BitWriterInit(bw, mb_size * 7 / 8)) { // ~7 bits per macroblock 281 return WebPEncodingSetError(enc->pic, VP8_ENC_ERROR_OUT_OF_MEMORY); 282 } 283 VP8PutBitUniform(bw, 0); // colorspace 284 VP8PutBitUniform(bw, 0); // clamp type 285 286 PutSegmentHeader(bw, enc); 287 PutFilterHeader(bw, &enc->filter_hdr); 288 VP8PutBits(bw, enc->num_parts == 8 ? 3 : 289 enc->num_parts == 4 ? 2 : 290 enc->num_parts == 2 ? 1 : 0, 2); 291 PutQuant(bw, enc); 292 VP8PutBitUniform(bw, 0); // no proba update 293 VP8WriteProbas(bw, &enc->proba); 294 pos2 = VP8BitWriterPos(bw); 295 VP8CodeIntraModes(enc); 296 VP8BitWriterFinish(bw); 297 298 pos3 = VP8BitWriterPos(bw); 299 300 #if !defined(WEBP_DISABLE_STATS) 301 if (enc->pic->stats) { 302 enc->pic->stats->header_bytes[0] = (int)((pos2 - pos1 + 7) >> 3); 303 enc->pic->stats->header_bytes[1] = (int)((pos3 - pos2 + 7) >> 3); 304 enc->pic->stats->alpha_data_size = (int)enc->alpha_data_size; 305 } 306 #else 307 (void)pos1; 308 (void)pos2; 309 (void)pos3; 310 #endif 311 if (bw->error) { 312 return WebPEncodingSetError(enc->pic, VP8_ENC_ERROR_OUT_OF_MEMORY); 313 } 314 return 1; 315 } 316 317 void VP8EncFreeBitWriters(VP8Encoder* const enc) { 318 int p; 319 VP8BitWriterWipeOut(&enc->bw); 320 for (p = 0; p < enc->num_parts; ++p) { 321 VP8BitWriterWipeOut(enc->parts + p); 322 } 323 } 324 325 int VP8EncWrite(VP8Encoder* const enc) { 326 WebPPicture* const pic = enc->pic; 327 VP8BitWriter* const bw = &enc->bw; 328 const int task_percent = 19; 329 const int percent_per_part = task_percent / enc->num_parts; 330 const int final_percent = enc->percent + task_percent; 331 int ok = 0; 332 size_t vp8_size, pad, riff_size; 333 int p; 334 335 // Partition #0 with header and partition sizes 336 ok = GeneratePartition0(enc); 337 if (!ok) return 0; 338 339 // Compute VP8 size 340 vp8_size = VP8_FRAME_HEADER_SIZE + 341 VP8BitWriterSize(bw) + 342 3 * (enc->num_parts - 1); 343 for (p = 0; p < enc->num_parts; ++p) { 344 vp8_size += VP8BitWriterSize(enc->parts + p); 345 } 346 pad = vp8_size & 1; 347 vp8_size += pad; 348 349 // Compute RIFF size 350 // At the minimum it is: "WEBPVP8 nnnn" + VP8 data size. 351 riff_size = TAG_SIZE + CHUNK_HEADER_SIZE + vp8_size; 352 if (IsVP8XNeeded(enc)) { // Add size for: VP8X header + data. 353 riff_size += CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE; 354 } 355 if (enc->has_alpha) { // Add size for: ALPH header + data. 356 const uint32_t padded_alpha_size = enc->alpha_data_size + 357 (enc->alpha_data_size & 1); 358 riff_size += CHUNK_HEADER_SIZE + padded_alpha_size; 359 } 360 // RIFF size should fit in 32-bits. 361 if (riff_size > 0xfffffffeU) { 362 return WebPEncodingSetError(pic, VP8_ENC_ERROR_FILE_TOO_BIG); 363 } 364 365 // Emit headers and partition #0 366 { 367 const uint8_t* const part0 = VP8BitWriterBuf(bw); 368 const size_t size0 = VP8BitWriterSize(bw); 369 ok = ok && PutWebPHeaders(enc, size0, vp8_size, riff_size) 370 && pic->writer(part0, size0, pic) 371 && EmitPartitionsSize(enc, pic); 372 VP8BitWriterWipeOut(bw); // will free the internal buffer. 373 } 374 375 // Token partitions 376 for (p = 0; p < enc->num_parts; ++p) { 377 const uint8_t* const buf = VP8BitWriterBuf(enc->parts + p); 378 const size_t size = VP8BitWriterSize(enc->parts + p); 379 if (size) ok = ok && pic->writer(buf, size, pic); 380 VP8BitWriterWipeOut(enc->parts + p); // will free the internal buffer. 381 ok = ok && WebPReportProgress(pic, enc->percent + percent_per_part, 382 &enc->percent); 383 } 384 385 // Padding byte 386 if (ok && pad) { 387 ok = PutPaddingByte(pic); 388 } 389 390 enc->coded_size = (int)(CHUNK_HEADER_SIZE + riff_size); 391 ok = ok && WebPReportProgress(pic, final_percent, &enc->percent); 392 if (!ok) WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_WRITE); 393 return ok; 394 } 395 396 //------------------------------------------------------------------------------