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 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
;
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
);
275 VP8PutBitUniform(bw
, 0); // colorspace
276 VP8PutBitUniform(bw
, 0); // clamp type
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);
284 VP8PutBitUniform(bw
, 0); // no proba update
285 VP8WriteProbas(bw
, &enc
->proba_
);
286 pos2
= VP8BitWriterPos(bw
);
287 VP8CodeIntraModes(enc
);
288 VP8BitWriterFinish(bw
);
290 pos3
= VP8BitWriterPos(bw
);
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_
;
298 return WebPEncodingSetError(enc
->pic_
, VP8_ENC_ERROR_OUT_OF_MEMORY
);
303 void VP8EncFreeBitWriters(VP8Encoder
* const enc
) {
305 VP8BitWriterWipeOut(&enc
->bw_
);
306 for (p
= 0; p
< enc
->num_parts_
; ++p
) {
307 VP8BitWriterWipeOut(enc
->parts_
+ p
);
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
;
318 size_t vp8_size
, pad
, riff_size
;
321 // Partition #0 with header and partition sizes
322 ok
= GeneratePartition0(enc
);
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
);
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
;
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
;
347 if (riff_size
> 0xfffffffeU
) {
348 return WebPEncodingSetError(pic
, VP8_ENC_ERROR_FILE_TOO_BIG
);
351 // Emit headers and partition #0
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.
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
);
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
,
374 ok
= PutPaddingByte(pic
);
377 enc
->coded_size_
= (int)(CHUNK_HEADER_SIZE
+ riff_size
);
378 ok
= ok
&& WebPReportProgress(pic
, final_percent
, &enc
->percent_
);
382 //------------------------------------------------------------------------------