Index: third_party/libwebp/enc/syntax.c |
diff --git a/third_party/libwebp/enc/syntax.c b/third_party/libwebp/enc/syntax.c |
index f119018135742930ff90e1a244bdcedd69ef3245..7c8c7b1a848123443938e8417cd884861d6454c8 100644 |
--- a/third_party/libwebp/enc/syntax.c |
+++ b/third_party/libwebp/enc/syntax.c |
@@ -1,4 +1,4 @@ |
-// Copyright 2011 Google Inc. |
+// Copyright 2011 Google Inc. All Rights Reserved. |
// |
// This code is licensed under the same terms as WebM: |
// Software License Agreement: http://www.webmproject.org/license/software/ |
@@ -10,74 +10,190 @@ |
// Author: Skal (pascal.massimino@gmail.com) |
#include <assert.h> |
-#include <math.h> |
-#include "vp8enci.h" |
+#include "../webp/format_constants.h" |
+#include "./vp8enci.h" |
#if defined(__cplusplus) || defined(c_plusplus) |
extern "C" { |
#endif |
-#define KSIGNATURE 0x9d012a |
-#define KHEADER_SIZE 10 |
-#define KRIFF_SIZE 20 |
-#define KSIZE_OFFSET (KRIFF_SIZE - 8) |
- |
-#define MAX_PARTITION0_SIZE (1 << 19) // max size of mode partition |
-#define MAX_PARTITION_SIZE (1 << 24) // max size for token partition |
- |
//------------------------------------------------------------------------------ |
-// Writers for header's various pieces (in order of appearance) |
+// Helper functions |
-// Main keyframe header |
- |
-static void PutLE32(uint8_t* const data, uint32_t val) { |
+// TODO(later): Move to webp/format_constants.h? |
+static void PutLE24(uint8_t* const data, uint32_t val) { |
data[0] = (val >> 0) & 0xff; |
data[1] = (val >> 8) & 0xff; |
data[2] = (val >> 16) & 0xff; |
+} |
+ |
+static void PutLE32(uint8_t* const data, uint32_t val) { |
+ PutLE24(data, val); |
data[3] = (val >> 24) & 0xff; |
} |
-static int PutHeader(int profile, size_t size0, size_t total_size, |
- WebPPicture* const pic) { |
- uint8_t buf[KHEADER_SIZE]; |
- uint8_t RIFF[KRIFF_SIZE] = { |
- 'R', 'I', 'F', 'F', 0, 0, 0, 0, 'W', 'E', 'B', 'P', 'V', 'P', '8', ' ' |
+static int IsVP8XNeeded(const VP8Encoder* const enc) { |
+ return !!enc->has_alpha_; // Currently the only case when VP8X is needed. |
+ // This could change in the future. |
+} |
+ |
+static int PutPaddingByte(const WebPPicture* const pic) { |
+ |
+ const uint8_t pad_byte[1] = { 0 }; |
+ return !!pic->writer(pad_byte, 1, pic); |
+} |
+ |
+//------------------------------------------------------------------------------ |
+// Writers for header's various pieces (in order of appearance) |
+ |
+static WebPEncodingError PutRIFFHeader(const VP8Encoder* const enc, |
+ size_t riff_size) { |
+ const WebPPicture* const pic = enc->pic_; |
+ uint8_t riff[RIFF_HEADER_SIZE] = { |
+ 'R', 'I', 'F', 'F', 0, 0, 0, 0, 'W', 'E', 'B', 'P' |
}; |
- uint32_t bits; |
+ assert(riff_size == (uint32_t)riff_size); |
+ PutLE32(riff + TAG_SIZE, (uint32_t)riff_size); |
+ if (!pic->writer(riff, sizeof(riff), pic)) { |
+ return VP8_ENC_ERROR_BAD_WRITE; |
+ } |
+ return VP8_ENC_OK; |
+} |
+ |
+static WebPEncodingError PutVP8XHeader(const VP8Encoder* const enc) { |
+ const WebPPicture* const pic = enc->pic_; |
+ uint8_t vp8x[CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE] = { |
+ 'V', 'P', '8', 'X' |
+ }; |
+ uint32_t flags = 0; |
+ |
+ assert(IsVP8XNeeded(enc)); |
+ assert(pic->width >= 1 && pic->height >= 1); |
+ assert(pic->width <= MAX_CANVAS_SIZE && pic->height <= MAX_CANVAS_SIZE); |
- if (size0 >= MAX_PARTITION0_SIZE) { // partition #0 is too big to fit |
- return WebPEncodingSetError(pic, VP8_ENC_ERROR_PARTITION0_OVERFLOW); |
+ if (enc->has_alpha_) { |
+ flags |= ALPHA_FLAG_BIT; |
} |
- if (total_size > 0xfffffffeU - KRIFF_SIZE) { |
- return WebPEncodingSetError(pic, VP8_ENC_ERROR_FILE_TOO_BIG); |
+ PutLE32(vp8x + TAG_SIZE, VP8X_CHUNK_SIZE); |
+ PutLE32(vp8x + CHUNK_HEADER_SIZE, flags); |
+ PutLE24(vp8x + CHUNK_HEADER_SIZE + 4, pic->width - 1); |
+ PutLE24(vp8x + CHUNK_HEADER_SIZE + 7, pic->height - 1); |
+ if(!pic->writer(vp8x, sizeof(vp8x), pic)) { |
+ return VP8_ENC_ERROR_BAD_WRITE; |
+ } |
+ return VP8_ENC_OK; |
+} |
+ |
+static WebPEncodingError PutAlphaChunk(const VP8Encoder* const enc) { |
+ const WebPPicture* const pic = enc->pic_; |
+ uint8_t alpha_chunk_hdr[CHUNK_HEADER_SIZE] = { |
+ 'A', 'L', 'P', 'H' |
+ }; |
+ |
+ assert(enc->has_alpha_); |
+ |
+ // Alpha chunk header. |
+ PutLE32(alpha_chunk_hdr + TAG_SIZE, enc->alpha_data_size_); |
+ if (!pic->writer(alpha_chunk_hdr, sizeof(alpha_chunk_hdr), pic)) { |
+ return VP8_ENC_ERROR_BAD_WRITE; |
+ } |
+ |
+ // Alpha chunk data. |
+ if (!pic->writer(enc->alpha_data_, enc->alpha_data_size_, pic)) { |
+ return VP8_ENC_ERROR_BAD_WRITE; |
+ } |
+ |
+ // Padding. |
+ if ((enc->alpha_data_size_ & 1) && !PutPaddingByte(pic)) { |
+ return VP8_ENC_ERROR_BAD_WRITE; |
+ } |
+ return VP8_ENC_OK; |
+} |
+ |
+static WebPEncodingError PutVP8Header(const WebPPicture* const pic, |
+ size_t vp8_size) { |
+ uint8_t vp8_chunk_hdr[CHUNK_HEADER_SIZE] = { |
+ 'V', 'P', '8', ' ' |
+ }; |
+ assert(vp8_size == (uint32_t)vp8_size); |
+ PutLE32(vp8_chunk_hdr + TAG_SIZE, (uint32_t)vp8_size); |
+ if (!pic->writer(vp8_chunk_hdr, sizeof(vp8_chunk_hdr), pic)) { |
+ return VP8_ENC_ERROR_BAD_WRITE; |
} |
+ return VP8_ENC_OK; |
+} |
- PutLE32(RIFF + 4, (uint32_t)(total_size + KSIZE_OFFSET)); |
- PutLE32(RIFF + 16, (uint32_t)total_size); |
- if (!pic->writer(RIFF, sizeof(RIFF), pic)) { |
- return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_WRITE); |
+static WebPEncodingError PutVP8FrameHeader(const WebPPicture* const pic, |
+ int profile, size_t size0) { |
+ uint8_t vp8_frm_hdr[VP8_FRAME_HEADER_SIZE]; |
+ uint32_t bits; |
+ |
+ if (size0 >= VP8_MAX_PARTITION0_SIZE) { // partition #0 is too big to fit |
+ return VP8_ENC_ERROR_PARTITION0_OVERFLOW; |
} |
+ // Paragraph 9.1. |
bits = 0 // keyframe (1b) |
| (profile << 1) // profile (3b) |
| (1 << 4) // visible (1b) |
| ((uint32_t)size0 << 5); // partition length (19b) |
- buf[0] = bits & 0xff; |
- buf[1] = (bits >> 8) & 0xff; |
- buf[2] = (bits >> 16) & 0xff; |
+ vp8_frm_hdr[0] = (bits >> 0) & 0xff; |
+ vp8_frm_hdr[1] = (bits >> 8) & 0xff; |
+ vp8_frm_hdr[2] = (bits >> 16) & 0xff; |
// signature |
- buf[3] = (KSIGNATURE >> 16) & 0xff; |
- buf[4] = (KSIGNATURE >> 8) & 0xff; |
- buf[5] = (KSIGNATURE >> 0) & 0xff; |
+ vp8_frm_hdr[3] = (VP8_SIGNATURE >> 16) & 0xff; |
+ vp8_frm_hdr[4] = (VP8_SIGNATURE >> 8) & 0xff; |
+ vp8_frm_hdr[5] = (VP8_SIGNATURE >> 0) & 0xff; |
// dimensions |
- buf[6] = pic->width & 0xff; |
- buf[7] = pic->width >> 8; |
- buf[8] = pic->height & 0xff; |
- buf[9] = pic->height >> 8; |
+ vp8_frm_hdr[6] = pic->width & 0xff; |
+ vp8_frm_hdr[7] = pic->width >> 8; |
+ vp8_frm_hdr[8] = pic->height & 0xff; |
+ vp8_frm_hdr[9] = pic->height >> 8; |
+ |
+ if (!pic->writer(vp8_frm_hdr, sizeof(vp8_frm_hdr), pic)) { |
+ return VP8_ENC_ERROR_BAD_WRITE; |
+ } |
+ return VP8_ENC_OK; |
+} |
+ |
+// WebP Headers. |
+static int PutWebPHeaders(const VP8Encoder* const enc, size_t size0, |
+ size_t vp8_size, size_t riff_size) { |
+ WebPPicture* const pic = enc->pic_; |
+ WebPEncodingError err = VP8_ENC_OK; |
+ |
+ // RIFF header. |
+ err = PutRIFFHeader(enc, riff_size); |
+ if (err != VP8_ENC_OK) goto Error; |
+ |
+ // VP8X. |
+ if (IsVP8XNeeded(enc)) { |
+ err = PutVP8XHeader(enc); |
+ if (err != VP8_ENC_OK) goto Error; |
+ } |
+ |
+ // Alpha. |
+ if (enc->has_alpha_) { |
+ err = PutAlphaChunk(enc); |
+ if (err != VP8_ENC_OK) goto Error; |
+ } |
+ |
+ // VP8 header. |
+ err = PutVP8Header(pic, vp8_size); |
+ if (err != VP8_ENC_OK) goto Error; |
- return pic->writer(buf, sizeof(buf), pic); |
+ // VP8 frame header. |
+ err = PutVP8FrameHeader(pic, enc->profile_, size0); |
+ if (err != VP8_ENC_OK) goto Error; |
+ |
+ // All OK. |
+ return 1; |
+ |
+ // Error. |
+ Error: |
+ return WebPEncodingSetError(pic, err); |
} |
// Segmentation header |
@@ -148,7 +264,7 @@ static int EmitPartitionsSize(const VP8Encoder* const enc, |
int p; |
for (p = 0; p < enc->num_parts_ - 1; ++p) { |
const size_t part_size = VP8BitWriterSize(enc->parts_ + p); |
- if (part_size >= MAX_PARTITION_SIZE) { |
+ if (part_size >= VP8_MAX_PARTITION_SIZE) { |
return WebPEncodingSetError(pic, VP8_ENC_ERROR_PARTITION_OVERFLOW); |
} |
buf[3 * p + 0] = (part_size >> 0) & 0xff; |
@@ -164,12 +280,6 @@ static int EmitPartitionsSize(const VP8Encoder* const enc, |
#define KTRAILER_SIZE 8 |
-static void PutLE24(uint8_t* buf, size_t value) { |
- buf[0] = (value >> 0) & 0xff; |
- buf[1] = (value >> 8) & 0xff; |
- buf[2] = (value >> 16) & 0xff; |
-} |
- |
static int WriteExtensions(VP8Encoder* const enc) { |
uint8_t buffer[KTRAILER_SIZE]; |
VP8BitWriter* const bw = &enc->bw_; |
@@ -186,14 +296,6 @@ static int WriteExtensions(VP8Encoder* const enc) { |
return WebPEncodingSetError(pic, VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY); |
} |
} |
- // Alpha (bytes 4..6) |
- PutLE24(buffer + 4, enc->alpha_data_size_); |
- if (enc->alpha_data_size_ > 0) { |
- assert(enc->has_alpha_); |
- if (!VP8BitWriterAppend(bw, enc->alpha_data_, enc->alpha_data_size_)) { |
- return WebPEncodingSetError(pic, VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY); |
- } |
- } |
buffer[KTRAILER_SIZE - 1] = 0x01; // marker |
if (!VP8BitWriterAppend(bw, buffer, KTRAILER_SIZE)) { |
@@ -211,7 +313,7 @@ static size_t GeneratePartition0(VP8Encoder* const enc) { |
const int mb_size = enc->mb_w_ * enc->mb_h_; |
uint64_t pos1, pos2, pos3; |
#ifdef WEBP_EXPERIMENTAL_FEATURES |
- const int need_extensions = enc->has_alpha_ || enc->use_layer_; |
+ const int need_extensions = enc->use_layer_; |
#endif |
pos1 = VP8BitWriterPos(bw); |
@@ -250,32 +352,61 @@ static size_t GeneratePartition0(VP8Encoder* const enc) { |
return !bw->error_; |
} |
+void VP8EncFreeBitWriters(VP8Encoder* const enc) { |
+ int p; |
+ VP8BitWriterWipeOut(&enc->bw_); |
+ for (p = 0; p < enc->num_parts_; ++p) { |
+ VP8BitWriterWipeOut(enc->parts_ + p); |
+ } |
+} |
+ |
int VP8EncWrite(VP8Encoder* const enc) { |
WebPPicture* const pic = enc->pic_; |
VP8BitWriter* const bw = &enc->bw_; |
+ const int task_percent = 19; |
+ const int percent_per_part = task_percent / enc->num_parts_; |
+ const int final_percent = enc->percent_ + task_percent; |
int ok = 0; |
- size_t coded_size, pad; |
+ size_t vp8_size, pad, riff_size; |
int p; |
// Partition #0 with header and partition sizes |
ok = !!GeneratePartition0(enc); |
- // Compute total size (for the RIFF header) |
- coded_size = KHEADER_SIZE + VP8BitWriterSize(bw) + 3 * (enc->num_parts_ - 1); |
+ // Compute VP8 size |
+ vp8_size = VP8_FRAME_HEADER_SIZE + |
+ VP8BitWriterSize(bw) + |
+ 3 * (enc->num_parts_ - 1); |
for (p = 0; p < enc->num_parts_; ++p) { |
- coded_size += VP8BitWriterSize(enc->parts_ + p); |
+ vp8_size += VP8BitWriterSize(enc->parts_ + p); |
+ } |
+ pad = vp8_size & 1; |
+ vp8_size += pad; |
+ |
+ // Compute RIFF size |
+ // At the minimum it is: "WEBPVP8 nnnn" + VP8 data size. |
+ riff_size = TAG_SIZE + CHUNK_HEADER_SIZE + vp8_size; |
+ if (IsVP8XNeeded(enc)) { // Add size for: VP8X header + data. |
+ riff_size += CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE; |
+ } |
+ if (enc->has_alpha_) { // Add size for: ALPH header + data. |
+ const uint32_t padded_alpha_size = enc->alpha_data_size_ + |
+ (enc->alpha_data_size_ & 1); |
+ riff_size += CHUNK_HEADER_SIZE + padded_alpha_size; |
+ } |
+ // Sanity check. |
+ if (riff_size > 0xfffffffeU) { |
+ return WebPEncodingSetError(pic, VP8_ENC_ERROR_FILE_TOO_BIG); |
} |
- pad = coded_size & 1; |
- coded_size += pad; |
// Emit headers and partition #0 |
{ |
const uint8_t* const part0 = VP8BitWriterBuf(bw); |
const size_t size0 = VP8BitWriterSize(bw); |
- ok = ok && PutHeader(enc->profile_, size0, coded_size, pic) |
+ ok = ok && PutWebPHeaders(enc, size0, vp8_size, riff_size) |
&& pic->writer(part0, size0, pic) |
&& EmitPartitionsSize(enc, pic); |
- free((void*)part0); |
+ VP8BitWriterWipeOut(bw); // will free the internal buffer. |
} |
// Token partitions |
@@ -284,16 +415,18 @@ int VP8EncWrite(VP8Encoder* const enc) { |
const size_t size = VP8BitWriterSize(enc->parts_ + p); |
if (size) |
ok = ok && pic->writer(buf, size, pic); |
- free((void*)buf); |
+ VP8BitWriterWipeOut(enc->parts_ + p); // will free the internal buffer. |
+ ok = ok && WebPReportProgress(pic, enc->percent_ + percent_per_part, |
+ &enc->percent_); |
} |
// Padding byte |
if (ok && pad) { |
- const uint8_t pad_byte[1] = { 0 }; |
- ok = pic->writer(pad_byte, 1, pic); |
+ ok = PutPaddingByte(pic); |
} |
- enc->coded_size_ = (int)coded_size + KRIFF_SIZE; |
+ enc->coded_size_ = (int)(CHUNK_HEADER_SIZE + riff_size); |
+ ok = ok && WebPReportProgress(pic, final_percent, &enc->percent_); |
return ok; |
} |