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