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 // WebP encoder: main entry point
12 // Author: Skal (pascal.massimino@gmail.com)
19 #include "./vp8enci.h"
22 #include "../utils/utils.h"
24 // #define PRINT_MEMORY_INFO
26 #ifdef PRINT_MEMORY_INFO
30 //------------------------------------------------------------------------------
32 int WebPGetEncoderVersion(void) {
33 return (ENC_MAJ_VERSION
<< 16) | (ENC_MIN_VERSION
<< 8) | ENC_REV_VERSION
;
36 //------------------------------------------------------------------------------
38 //------------------------------------------------------------------------------
40 static void ResetSegmentHeader(VP8Encoder
* const enc
) {
41 VP8SegmentHeader
* const hdr
= &enc
->segment_hdr_
;
42 hdr
->num_segments_
= enc
->config_
->segments
;
43 hdr
->update_map_
= (hdr
->num_segments_
> 1);
47 static void ResetFilterHeader(VP8Encoder
* const enc
) {
48 VP8FilterHeader
* const hdr
= &enc
->filter_hdr_
;
52 hdr
->i4x4_lf_delta_
= 0;
55 static void ResetBoundaryPredictions(VP8Encoder
* const enc
) {
56 // init boundary values once for all
57 // Note: actually, initializing the preds_[] is only needed for intra4.
59 uint8_t* const top
= enc
->preds_
- enc
->preds_w_
;
60 uint8_t* const left
= enc
->preds_
- 1;
61 for (i
= -1; i
< 4 * enc
->mb_w_
; ++i
) {
64 for (i
= 0; i
< 4 * enc
->mb_h_
; ++i
) {
65 left
[i
* enc
->preds_w_
] = B_DC_PRED
;
67 enc
->nz_
[-1] = 0; // constant
70 // Mapping from config->method_ to coding tools used.
71 //-------------------+---+---+---+---+---+---+---+
72 // Method | 0 | 1 | 2 | 3 |(4)| 5 | 6 |
73 //-------------------+---+---+---+---+---+---+---+
74 // fast probe | x | | | x | | | |
75 //-------------------+---+---+---+---+---+---+---+
76 // dynamic proba | ~ | x | x | x | x | x | x |
77 //-------------------+---+---+---+---+---+---+---+
78 // fast mode analysis| | | | | x | x | x |
79 //-------------------+---+---+---+---+---+---+---+
80 // basic rd-opt | | | | x | x | x | x |
81 //-------------------+---+---+---+---+---+---+---+
82 // disto-score i4/16 | | | x | | | | |
83 //-------------------+---+---+---+---+---+---+---+
84 // rd-opt i4/16 | | | ~ | x | x | x | x |
85 //-------------------+---+---+---+---+---+---+---+
86 // token buffer (opt)| | | | x | x | x | x |
87 //-------------------+---+---+---+---+---+---+---+
88 // Trellis | | | | | | x |Ful|
89 //-------------------+---+---+---+---+---+---+---+
90 // full-SNS | | | | | x | x | x |
91 //-------------------+---+---+---+---+---+---+---+
93 static void MapConfigToTools(VP8Encoder
* const enc
) {
94 const WebPConfig
* const config
= enc
->config_
;
95 const int method
= config
->method
;
96 const int limit
= 100 - config
->partition_limit
;
97 enc
->method_
= method
;
98 enc
->rd_opt_level_
= (method
>= 6) ? RD_OPT_TRELLIS_ALL
99 : (method
>= 5) ? RD_OPT_TRELLIS
100 : (method
>= 3) ? RD_OPT_BASIC
102 enc
->max_i4_header_bits_
=
103 256 * 16 * 16 * // upper bound: up to 16bit per 4x4 block
104 (limit
* limit
) / (100 * 100); // ... modulated with a quadratic curve.
106 enc
->thread_level_
= config
->thread_level
;
108 enc
->do_search_
= (config
->target_size
> 0 || config
->target_PSNR
> 0);
109 if (!config
->low_memory
) {
110 #if !defined(DISABLE_TOKEN_BUFFER)
111 enc
->use_tokens_
= (enc
->rd_opt_level_
>= RD_OPT_BASIC
); // need rd stats
113 if (enc
->use_tokens_
) {
114 enc
->num_parts_
= 1; // doesn't work with multi-partition
119 // Memory scaling with dimensions:
120 // memory (bytes) ~= 2.25 * w + 0.0625 * w * h
122 // Typical memory footprint (614x440 picture)
130 // Transient object sizes:
131 // VP8EncIterator: 3360
133 // VP8SegmentInfo: 732
136 // Picture size (yuv): 419328
138 static VP8Encoder
* InitVP8Encoder(const WebPConfig
* const config
,
139 WebPPicture
* const picture
) {
140 const int use_filter
=
141 (config
->filter_strength
> 0) || (config
->autofilter
> 0);
142 const int mb_w
= (picture
->width
+ 15) >> 4;
143 const int mb_h
= (picture
->height
+ 15) >> 4;
144 const int preds_w
= 4 * mb_w
+ 1;
145 const int preds_h
= 4 * mb_h
+ 1;
146 const size_t preds_size
= preds_w
* preds_h
* sizeof(uint8_t);
147 const int top_stride
= mb_w
* 16;
148 const size_t nz_size
= (mb_w
+ 1) * sizeof(uint32_t) + ALIGN_CST
;
149 const size_t info_size
= mb_w
* mb_h
* sizeof(VP8MBInfo
);
150 const size_t samples_size
= 2 * top_stride
* sizeof(uint8_t) // top-luma/u/v
151 + ALIGN_CST
; // align all
152 const size_t lf_stats_size
=
153 config
->autofilter
? sizeof(LFStats
) + ALIGN_CST
: 0;
156 const uint64_t size
= (uint64_t)sizeof(VP8Encoder
) // main struct
157 + ALIGN_CST
// cache alignment
158 + info_size
// modes info
159 + preds_size
// prediction modes
160 + samples_size
// top/left samples
161 + nz_size
// coeff context bits
162 + lf_stats_size
; // autofilter stats
164 #ifdef PRINT_MEMORY_INFO
165 printf("===================================\n");
166 printf("Memory used:\n"
170 " top samples: %ld\n"
174 sizeof(VP8Encoder
) + ALIGN_CST
, info_size
,
175 preds_size
, samples_size
, nz_size
, lf_stats_size
, size
);
176 printf("Transient object sizes:\n"
177 " VP8EncIterator: %ld\n"
178 " VP8ModeScore: %ld\n"
179 " VP8SegmentInfo: %ld\n"
182 sizeof(VP8EncIterator
), sizeof(VP8ModeScore
),
183 sizeof(VP8SegmentInfo
), sizeof(VP8Proba
),
185 printf("Picture size (yuv): %ld\n",
186 mb_w
* mb_h
* 384 * sizeof(uint8_t));
187 printf("===================================\n");
189 mem
= (uint8_t*)WebPSafeMalloc(size
, sizeof(*mem
));
191 WebPEncodingSetError(picture
, VP8_ENC_ERROR_OUT_OF_MEMORY
);
194 enc
= (VP8Encoder
*)mem
;
195 mem
= (uint8_t*)DO_ALIGN(mem
+ sizeof(*enc
));
196 memset(enc
, 0, sizeof(*enc
));
197 enc
->num_parts_
= 1 << config
->partitions
;
200 enc
->preds_w_
= preds_w
;
201 enc
->mb_info_
= (VP8MBInfo
*)mem
;
203 enc
->preds_
= ((uint8_t*)mem
) + 1 + enc
->preds_w_
;
204 mem
+= preds_w
* preds_h
* sizeof(uint8_t);
205 enc
->nz_
= 1 + (uint32_t*)DO_ALIGN(mem
);
207 enc
->lf_stats_
= lf_stats_size
? (LFStats
*)DO_ALIGN(mem
) : NULL
;
208 mem
+= lf_stats_size
;
210 // top samples (all 16-aligned)
211 mem
= (uint8_t*)DO_ALIGN(mem
);
212 enc
->y_top_
= (uint8_t*)mem
;
213 enc
->uv_top_
= enc
->y_top_
+ top_stride
;
214 mem
+= 2 * top_stride
;
215 assert(mem
<= (uint8_t*)enc
+ size
);
217 enc
->config_
= config
;
218 enc
->profile_
= use_filter
? ((config
->filter_type
== 1) ? 0 : 1) : 2;
222 MapConfigToTools(enc
);
224 VP8DefaultProbas(enc
);
225 ResetSegmentHeader(enc
);
226 ResetFilterHeader(enc
);
227 ResetBoundaryPredictions(enc
);
228 VP8GetResidualCostInit();
229 VP8SetResidualCoeffsInit();
230 VP8EncInitAlpha(enc
);
232 // lower quality means smaller output -> we modulate a little the page
233 // size based on quality. This is just a crude 1rst-order prediction.
235 const float scale
= 1.f
+ config
->quality
* 5.f
/ 100.f
; // in [1,6]
236 VP8TBufferInit(&enc
->tokens_
, (int)(mb_w
* mb_h
* 4 * scale
));
241 static int DeleteVP8Encoder(VP8Encoder
* enc
) {
244 ok
= VP8EncDeleteAlpha(enc
);
245 VP8TBufferClear(&enc
->tokens_
);
251 //------------------------------------------------------------------------------
253 static double GetPSNR(uint64_t err
, uint64_t size
) {
254 return (err
> 0 && size
> 0) ? 10. * log10(255. * 255. * size
/ err
) : 99.;
257 static void FinalizePSNR(const VP8Encoder
* const enc
) {
258 WebPAuxStats
* stats
= enc
->pic_
->stats
;
259 const uint64_t size
= enc
->sse_count_
;
260 const uint64_t* const sse
= enc
->sse_
;
261 stats
->PSNR
[0] = (float)GetPSNR(sse
[0], size
);
262 stats
->PSNR
[1] = (float)GetPSNR(sse
[1], size
/ 4);
263 stats
->PSNR
[2] = (float)GetPSNR(sse
[2], size
/ 4);
264 stats
->PSNR
[3] = (float)GetPSNR(sse
[0] + sse
[1] + sse
[2], size
* 3 / 2);
265 stats
->PSNR
[4] = (float)GetPSNR(sse
[3], size
);
268 static void StoreStats(VP8Encoder
* const enc
) {
269 WebPAuxStats
* const stats
= enc
->pic_
->stats
;
272 for (i
= 0; i
< NUM_MB_SEGMENTS
; ++i
) {
273 stats
->segment_level
[i
] = enc
->dqm_
[i
].fstrength_
;
274 stats
->segment_quant
[i
] = enc
->dqm_
[i
].quant_
;
275 for (s
= 0; s
<= 2; ++s
) {
276 stats
->residual_bytes
[s
][i
] = enc
->residual_bytes_
[s
][i
];
280 stats
->coded_size
= enc
->coded_size_
;
281 for (i
= 0; i
< 3; ++i
) {
282 stats
->block_count
[i
] = enc
->block_count_
[i
];
285 WebPReportProgress(enc
->pic_
, 100, &enc
->percent_
); // done!
288 int WebPEncodingSetError(const WebPPicture
* const pic
,
289 WebPEncodingError error
) {
290 assert((int)error
< VP8_ENC_ERROR_LAST
);
291 assert((int)error
>= VP8_ENC_OK
);
292 ((WebPPicture
*)pic
)->error_code
= error
;
296 int WebPReportProgress(const WebPPicture
* const pic
,
297 int percent
, int* const percent_store
) {
298 if (percent_store
!= NULL
&& percent
!= *percent_store
) {
299 *percent_store
= percent
;
300 if (pic
->progress_hook
&& !pic
->progress_hook(percent
, pic
)) {
301 // user abort requested
302 WebPEncodingSetError(pic
, VP8_ENC_ERROR_USER_ABORT
);
308 //------------------------------------------------------------------------------
310 int WebPEncode(const WebPConfig
* config
, WebPPicture
* pic
) {
315 WebPEncodingSetError(pic
, VP8_ENC_OK
); // all ok so far
316 if (config
== NULL
) // bad params
317 return WebPEncodingSetError(pic
, VP8_ENC_ERROR_NULL_PARAMETER
);
318 if (!WebPValidateConfig(config
))
319 return WebPEncodingSetError(pic
, VP8_ENC_ERROR_INVALID_CONFIGURATION
);
320 if (pic
->width
<= 0 || pic
->height
<= 0)
321 return WebPEncodingSetError(pic
, VP8_ENC_ERROR_BAD_DIMENSION
);
322 if (pic
->width
> WEBP_MAX_DIMENSION
|| pic
->height
> WEBP_MAX_DIMENSION
)
323 return WebPEncodingSetError(pic
, VP8_ENC_ERROR_BAD_DIMENSION
);
325 if (pic
->stats
!= NULL
) memset(pic
->stats
, 0, sizeof(*pic
->stats
));
327 if (!config
->lossless
) {
328 VP8Encoder
* enc
= NULL
;
329 if (pic
->y
== NULL
|| pic
->u
== NULL
|| pic
->v
== NULL
) {
330 // Make sure we have YUVA samples.
331 if (config
->preprocessing
& 4) {
332 #if WEBP_ENCODER_ABI_VERSION > 0x0204
333 if (!WebPPictureSmartARGBToYUVA(pic
)) {
338 float dithering
= 0.f
;
339 if (config
->preprocessing
& 2) {
340 const float x
= config
->quality
/ 100.f
;
341 const float x2
= x
* x
;
342 // slowly decreasing from max dithering at low quality (q->0)
343 // to 0.5 dithering amplitude at high quality (q->100)
344 dithering
= 1.0f
+ (0.5f
- 1.0f
) * x2
* x2
;
346 if (!WebPPictureARGBToYUVADithered(pic
, WEBP_YUV420
, dithering
)) {
352 enc
= InitVP8Encoder(config
, pic
);
353 if (enc
== NULL
) return 0; // pic->error is already set.
354 // Note: each of the tasks below account for 20% in the progress report.
355 ok
= VP8EncAnalyze(enc
);
357 // Analysis is done, proceed to actual coding.
358 ok
= ok
&& VP8EncStartAlpha(enc
); // possibly done in parallel
359 if (!enc
->use_tokens_
) {
360 ok
= ok
&& VP8EncLoop(enc
);
362 ok
= ok
&& VP8EncTokenLoop(enc
);
364 ok
= ok
&& VP8EncFinishAlpha(enc
);
366 ok
= ok
&& VP8EncWrite(enc
);
369 VP8EncFreeBitWriters(enc
);
371 ok
&= DeleteVP8Encoder(enc
); // must always be called, even if !ok
373 // Make sure we have ARGB samples.
374 if (pic
->argb
== NULL
&& !WebPPictureYUVAToARGB(pic
)) {
378 ok
= VP8LEncodeImage(config
, pic
); // Sets pic->error in case of problem.