1 // Copyright 2011 Google Inc. All Rights Reserved.
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 // -----------------------------------------------------------------------------
10 // Header syntax writing
12 // Author: Skal (pascal.massimino@gmail.com)
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"
21 //------------------------------------------------------------------------------
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.
29 static int PutPaddingByte(const WebPPicture
* const pic
) {
30 const uint8_t pad_byte
[1] = { 0 };
31 return !!pic
->writer(pad_byte
, 1, pic
);
34 //------------------------------------------------------------------------------
35 // Writers for header's various pieces (in order of appearance)
37 static WebPEncodingError
PutRIFFHeader(const VP8Encoder
* const enc
,
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'
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
;
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
] = {
58 assert(IsVP8XNeeded(enc
));
59 assert(pic
->width
>= 1 && pic
->height
>= 1);
60 assert(pic
->width
<= MAX_CANVAS_SIZE
&& pic
->height
<= MAX_CANVAS_SIZE
);
62 if (enc
->has_alpha_
) {
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
;
76 static WebPEncodingError
PutAlphaChunk(const VP8Encoder
* const enc
) {
77 const WebPPicture
* const pic
= enc
->pic_
;
78 uint8_t alpha_chunk_hdr
[CHUNK_HEADER_SIZE
] = {
82 assert(enc
->has_alpha_
);
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
;
91 if (!pic
->writer(enc
->alpha_data_
, enc
->alpha_data_size_
, pic
)) {
92 return VP8_ENC_ERROR_BAD_WRITE
;
96 if ((enc
->alpha_data_size_
& 1) && !PutPaddingByte(pic
)) {
97 return VP8_ENC_ERROR_BAD_WRITE
;
102 static WebPEncodingError
PutVP8Header(const WebPPicture
* const pic
,
104 uint8_t vp8_chunk_hdr
[CHUNK_HEADER_SIZE
] = {
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
;
115 static WebPEncodingError
PutVP8FrameHeader(const WebPPicture
* const pic
,
116 int profile
, size_t size0
) {
117 uint8_t vp8_frm_hdr
[VP8_FRAME_HEADER_SIZE
];
120 if (size0
>= VP8_MAX_PARTITION0_SIZE
) { // partition #0 is too big to fit
121 return VP8_ENC_ERROR_PARTITION0_OVERFLOW
;
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;
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;
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;
142 if (!pic
->writer(vp8_frm_hdr
, sizeof(vp8_frm_hdr
), pic
)) {
143 return VP8_ENC_ERROR_BAD_WRITE
;
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
;
155 err
= PutRIFFHeader(enc
, riff_size
);
156 if (err
!= VP8_ENC_OK
) goto Error
;
159 if (IsVP8XNeeded(enc
)) {
160 err
= PutVP8XHeader(enc
);
161 if (err
!= VP8_ENC_OK
) goto Error
;
165 if (enc
->has_alpha_
) {
166 err
= PutAlphaChunk(enc
);
167 if (err
!= VP8_ENC_OK
) goto Error
;
171 err
= PutVP8Header(pic
, vp8_size
);
172 if (err
!= VP8_ENC_OK
) goto Error
;
175 err
= PutVP8FrameHeader(pic
, enc
->profile_
, size0
);
176 if (err
!= VP8_ENC_OK
) goto Error
;
183 return WebPEncodingSetError(pic
, err
);
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;
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);
202 for (s
= 0; s
< NUM_MB_SEGMENTS
; ++s
) {
203 VP8PutSignedValue(bw
, enc
->dqm_
[s
].fstrength_
, 6);
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);
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
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);
248 static int EmitPartitionsSize(const VP8Encoder
* const enc
,
249 WebPPicture
* const pic
) {
250 uint8_t buf
[3 * (MAX_NUM_PARTITIONS
- 1)];
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
);
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;
261 return p
? pic
->writer(buf
, 3 * p
, pic
) : 1;
264 //------------------------------------------------------------------------------
266 #ifdef WEBP_EXPERIMENTAL_FEATURES
268 #define KTRAILER_SIZE 8
270 static int WriteExtensions(VP8Encoder
* const enc
) {
271 uint8_t buffer
[KTRAILER_SIZE
];
272 VP8BitWriter
* const bw
= &enc
->bw_
;
273 WebPPicture
* const pic
= enc
->pic_
;
275 // Layer (bytes 0..3)
276 PutLE24(buffer
+ 0, enc
->layer_data_size_
);
277 buffer
[3] = enc
->pic_
->colorspace
& WEBP_CSP_UV_MASK
;
278 if (enc
->layer_data_size_
> 0) {
279 assert(enc
->use_layer_
);
280 // append layer data to last partition
281 if (!VP8BitWriterAppend(&enc
->parts_
[enc
->num_parts_
- 1],
282 enc
->layer_data_
, enc
->layer_data_size_
)) {
283 return WebPEncodingSetError(pic
, VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY
);
287 buffer
[KTRAILER_SIZE
- 1] = 0x01; // marker
288 if (!VP8BitWriterAppend(bw
, buffer
, KTRAILER_SIZE
)) {
289 return WebPEncodingSetError(pic
, VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY
);
294 #endif /* WEBP_EXPERIMENTAL_FEATURES */
296 //------------------------------------------------------------------------------
298 static size_t GeneratePartition0(VP8Encoder
* const enc
) {
299 VP8BitWriter
* const bw
= &enc
->bw_
;
300 const int mb_size
= enc
->mb_w_
* enc
->mb_h_
;
301 uint64_t pos1
, pos2
, pos3
;
302 #ifdef WEBP_EXPERIMENTAL_FEATURES
303 const int need_extensions
= enc
->use_layer_
;
306 pos1
= VP8BitWriterPos(bw
);
307 VP8BitWriterInit(bw
, mb_size
* 7 / 8); // ~7 bits per macroblock
308 #ifdef WEBP_EXPERIMENTAL_FEATURES
309 VP8PutBitUniform(bw
, need_extensions
); // extensions
311 VP8PutBitUniform(bw
, 0); // colorspace
313 VP8PutBitUniform(bw
, 0); // clamp type
315 PutSegmentHeader(bw
, enc
);
316 PutFilterHeader(bw
, &enc
->filter_hdr_
);
317 VP8PutValue(bw
, enc
->num_parts_
== 8 ? 3 :
318 enc
->num_parts_
== 4 ? 2 :
319 enc
->num_parts_
== 2 ? 1 : 0, 2);
321 VP8PutBitUniform(bw
, 0); // no proba update
322 VP8WriteProbas(bw
, &enc
->proba_
);
323 pos2
= VP8BitWriterPos(bw
);
324 VP8CodeIntraModes(enc
);
325 VP8BitWriterFinish(bw
);
327 #ifdef WEBP_EXPERIMENTAL_FEATURES
328 if (need_extensions
&& !WriteExtensions(enc
)) {
333 pos3
= VP8BitWriterPos(bw
);
335 if (enc
->pic_
->stats
) {
336 enc
->pic_
->stats
->header_bytes
[0] = (int)((pos2
- pos1
+ 7) >> 3);
337 enc
->pic_
->stats
->header_bytes
[1] = (int)((pos3
- pos2
+ 7) >> 3);
338 enc
->pic_
->stats
->alpha_data_size
= (int)enc
->alpha_data_size_
;
339 enc
->pic_
->stats
->layer_data_size
= (int)enc
->layer_data_size_
;
344 void VP8EncFreeBitWriters(VP8Encoder
* const enc
) {
346 VP8BitWriterWipeOut(&enc
->bw_
);
347 for (p
= 0; p
< enc
->num_parts_
; ++p
) {
348 VP8BitWriterWipeOut(enc
->parts_
+ p
);
352 int VP8EncWrite(VP8Encoder
* const enc
) {
353 WebPPicture
* const pic
= enc
->pic_
;
354 VP8BitWriter
* const bw
= &enc
->bw_
;
355 const int task_percent
= 19;
356 const int percent_per_part
= task_percent
/ enc
->num_parts_
;
357 const int final_percent
= enc
->percent_
+ task_percent
;
359 size_t vp8_size
, pad
, riff_size
;
362 // Partition #0 with header and partition sizes
363 ok
= !!GeneratePartition0(enc
);
366 vp8_size
= VP8_FRAME_HEADER_SIZE
+
367 VP8BitWriterSize(bw
) +
368 3 * (enc
->num_parts_
- 1);
369 for (p
= 0; p
< enc
->num_parts_
; ++p
) {
370 vp8_size
+= VP8BitWriterSize(enc
->parts_
+ p
);
376 // At the minimum it is: "WEBPVP8 nnnn" + VP8 data size.
377 riff_size
= TAG_SIZE
+ CHUNK_HEADER_SIZE
+ vp8_size
;
378 if (IsVP8XNeeded(enc
)) { // Add size for: VP8X header + data.
379 riff_size
+= CHUNK_HEADER_SIZE
+ VP8X_CHUNK_SIZE
;
381 if (enc
->has_alpha_
) { // Add size for: ALPH header + data.
382 const uint32_t padded_alpha_size
= enc
->alpha_data_size_
+
383 (enc
->alpha_data_size_
& 1);
384 riff_size
+= CHUNK_HEADER_SIZE
+ padded_alpha_size
;
387 if (riff_size
> 0xfffffffeU
) {
388 return WebPEncodingSetError(pic
, VP8_ENC_ERROR_FILE_TOO_BIG
);
391 // Emit headers and partition #0
393 const uint8_t* const part0
= VP8BitWriterBuf(bw
);
394 const size_t size0
= VP8BitWriterSize(bw
);
395 ok
= ok
&& PutWebPHeaders(enc
, size0
, vp8_size
, riff_size
)
396 && pic
->writer(part0
, size0
, pic
)
397 && EmitPartitionsSize(enc
, pic
);
398 VP8BitWriterWipeOut(bw
); // will free the internal buffer.
402 for (p
= 0; p
< enc
->num_parts_
; ++p
) {
403 const uint8_t* const buf
= VP8BitWriterBuf(enc
->parts_
+ p
);
404 const size_t size
= VP8BitWriterSize(enc
->parts_
+ p
);
406 ok
= ok
&& pic
->writer(buf
, size
, pic
);
407 VP8BitWriterWipeOut(enc
->parts_
+ p
); // will free the internal buffer.
408 ok
= ok
&& WebPReportProgress(pic
, enc
->percent_
+ percent_per_part
,
414 ok
= PutPaddingByte(pic
);
417 enc
->coded_size_
= (int)(CHUNK_HEADER_SIZE
+ riff_size
);
418 ok
= ok
&& WebPReportProgress(pic
, final_percent
, &enc
->percent_
);
422 //------------------------------------------------------------------------------