| 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 VP8EncSegmentHeader* const hdr = &enc->segment_hdr_; | |
| 190 const VP8EncProba* 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 VP8PutSignedBits(bw, enc->dqm_[s].quant_, 7); | |
| 201 } | |
| 202 for (s = 0; s < NUM_MB_SEGMENTS; ++s) { | |
| 203 VP8PutSignedBits(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 VP8PutBits(bw, proba->segments_[s], 8); | |
| 210 } | |
| 211 } | |
| 212 } | |
| 213 } | |
| 214 } | |
| 215 | |
| 216 // Filtering parameters header | |
| 217 static void PutFilterHeader(VP8BitWriter* const bw, | |
| 218 const VP8EncFilterHeader* const hdr) { | |
| 219 const int use_lf_delta = (hdr->i4x4_lf_delta_ != 0); | |
| 220 VP8PutBitUniform(bw, hdr->simple_); | |
| 221 VP8PutBits(bw, hdr->level_, 6); | |
| 222 VP8PutBits(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 VP8PutBits(bw, 0, 4); | |
| 229 // we use mode_lf_delta for i4x4 | |
| 230 VP8PutSignedBits(bw, hdr->i4x4_lf_delta_, 6); | |
| 231 VP8PutBits(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 VP8PutBits(bw, enc->base_quant_, 7); | |
| 240 VP8PutSignedBits(bw, enc->dq_y1_dc_, 4); | |
| 241 VP8PutSignedBits(bw, enc->dq_y2_dc_, 4); | |
| 242 VP8PutSignedBits(bw, enc->dq_y2_ac_, 4); | |
| 243 VP8PutSignedBits(bw, enc->dq_uv_dc_, 4); | |
| 244 VP8PutSignedBits(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 VP8PutBits(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 |