1// Copyright 2011 Google Inc.
2//
3// This code is licensed under the same terms as WebM:
4//  Software License Agreement:  http://www.webmproject.org/license/software/
5//  Additional IP Rights Grant:  http://www.webmproject.org/license/additional/
6// -----------------------------------------------------------------------------
7//
8// WebP encoder: main entry point
9//
10// Author: Skal (pascal.massimino@gmail.com)
11
12#include <stdlib.h>
13#include <string.h>
14#include <math.h>
15
16#include "vp8enci.h"
17
18// #define PRINT_MEMORY_INFO
19
20#if defined(__cplusplus) || defined(c_plusplus)
21extern "C" {
22#endif
23
24#ifdef PRINT_MEMORY_INFO
25#include <stdio.h>
26#endif
27
28#define MAX_DIMENSION 16384   // maximum width/height allowed by the spec
29
30//-----------------------------------------------------------------------------
31// WebPPicture
32//-----------------------------------------------------------------------------
33
34static int DummyWriter(const uint8_t* data, size_t data_size,
35                       const WebPPicture* const picture) {
36  return 1;
37}
38
39int WebPPictureInitInternal(WebPPicture* const picture, int version) {
40  if (version != WEBP_ENCODER_ABI_VERSION) {
41    return 0;   // caller/system version mismatch!
42  }
43  if (picture) {
44    memset(picture, 0, sizeof(*picture));
45    picture->writer = DummyWriter;
46  }
47  return 1;
48}
49
50//-----------------------------------------------------------------------------
51// VP8Encoder
52//-----------------------------------------------------------------------------
53
54static void ResetSegmentHeader(VP8Encoder* const enc) {
55  VP8SegmentHeader* const hdr = &enc->segment_hdr_;
56  hdr->num_segments_ = enc->config_->segments;
57  hdr->update_map_  = (hdr->num_segments_ > 1);
58  hdr->size_ = 0;
59}
60
61static void ResetFilterHeader(VP8Encoder* const enc) {
62  VP8FilterHeader* const hdr = &enc->filter_hdr_;
63  hdr->simple_ = 1;
64  hdr->level_ = 0;
65  hdr->sharpness_ = 0;
66  hdr->i4x4_lf_delta_ = 0;
67}
68
69static void ResetBoundaryPredictions(VP8Encoder* const enc) {
70  // init boundary values once for all
71  // Note: actually, initializing the preds_[] is only needed for intra4.
72  int i;
73  uint8_t* const top = enc->preds_ - enc->preds_w_;
74  uint8_t* const left = enc->preds_ - 1;
75  for (i = -1; i < 4 * enc->mb_w_; ++i) {
76    top[i] = B_DC_PRED;
77  }
78  for (i = 0; i < 4 * enc->mb_h_; ++i) {
79    left[i * enc->preds_w_] = B_DC_PRED;
80  }
81  enc->nz_[-1] = 0;   // constant
82}
83
84// Map configured quality level to coding tools used.
85//-------------+---+---+---+---+---+---+
86//   Quality   | 0 | 1 | 2 | 3 | 4 | 5 +
87//-------------+---+---+---+---+---+---+
88// dynamic prob| ~ | x | x | x | x | x |
89//-------------+---+---+---+---+---+---+
90// rd-opt modes|   |   | x | x | x | x |
91//-------------+---+---+---+---+---+---+
92// fast i4/i16 | x | x |   |   |   |   |
93//-------------+---+---+---+---+---+---+
94// rd-opt i4/16|   |   | x | x | x | x |
95//-------------+---+---+---+---+---+---+
96// Trellis     |   | x |   |   | x | x |
97//-------------+---+---+---+---+---+---+
98// full-SNS    |   |   |   |   |   | x |
99//-------------+---+---+---+---+---+---+
100
101static void MapConfigToTools(VP8Encoder* const enc) {
102  const int method = enc->config_->method;
103  enc->method_ = method;
104  enc->rd_opt_level_ = (method >= 6) ? 3
105                     : (method >= 5) ? 2
106                     : (method >= 3) ? 1
107                     : 0;
108}
109
110// Memory scaling with dimensions:
111//  memory (bytes) ~= 2.25 * w + 0.0625 * w * h
112//
113// Typical memory footprint (768x510 picture)
114// Memory used:
115//              encoder: 33919
116//          block cache: 2880
117//                 info: 3072
118//                preds: 24897
119//          top samples: 1623
120//             non-zero: 196
121//             lf-stats: 2048
122//                total: 68635
123// Transcient object sizes:
124//       VP8EncIterator: 352
125//         VP8ModeScore: 912
126//       VP8SegmentInfo: 532
127//             VP8Proba: 31032
128//              LFStats: 2048
129// Picture size (yuv): 589824
130
131static VP8Encoder* InitEncoder(const WebPConfig* const config,
132                               WebPPicture* const picture) {
133  const int use_filter =
134      (config->filter_strength > 0) || (config->autofilter > 0);
135  const int mb_w = (picture->width + 15) >> 4;
136  const int mb_h = (picture->height + 15) >> 4;
137  const int preds_w = 4 * mb_w + 1;
138  const int preds_h = 4 * mb_h + 1;
139  const size_t preds_size = preds_w * preds_h * sizeof(uint8_t);
140  const int top_stride = mb_w * 16;
141  const size_t nz_size = (mb_w + 1) * sizeof(uint32_t);
142  const size_t cache_size = (3 * YUV_SIZE + PRED_SIZE) * sizeof(uint8_t);
143  const size_t info_size = mb_w * mb_h * sizeof(VP8MBInfo);
144  const size_t samples_size = (2 * top_stride +         // top-luma/u/v
145                               16 + 16 + 16 + 8 + 1 +   // left y/u/v
146                               2 * ALIGN_CST)           // align all
147                               * sizeof(uint8_t);
148  const size_t lf_stats_size = config->autofilter ? sizeof(LFStats) : 0;
149  VP8Encoder* enc;
150  uint8_t* mem;
151  size_t size = sizeof(VP8Encoder) + ALIGN_CST  // main struct
152              + cache_size                      // working caches
153              + info_size                       // modes info
154              + preds_size                      // prediction modes
155              + samples_size                    // top/left samples
156              + nz_size                         // coeff context bits
157              + lf_stats_size;                  // autofilter stats
158
159#ifdef PRINT_MEMORY_INFO
160  printf("===================================\n");
161  printf("Memory used:\n"
162         "             encoder: %ld\n"
163         "         block cache: %ld\n"
164         "                info: %ld\n"
165         "               preds: %ld\n"
166         "         top samples: %ld\n"
167         "            non-zero: %ld\n"
168         "            lf-stats: %ld\n"
169         "               total: %ld\n",
170         sizeof(VP8Encoder) + ALIGN_CST, cache_size, info_size,
171         preds_size, samples_size, nz_size, lf_stats_size, size);
172  printf("Transcient object sizes:\n"
173         "      VP8EncIterator: %ld\n"
174         "        VP8ModeScore: %ld\n"
175         "      VP8SegmentInfo: %ld\n"
176         "            VP8Proba: %ld\n"
177         "             LFStats: %ld\n",
178         sizeof(VP8EncIterator), sizeof(VP8ModeScore),
179         sizeof(VP8SegmentInfo), sizeof(VP8Proba),
180         sizeof(LFStats));
181  printf("Picture size (yuv): %ld\n",
182         mb_w * mb_h * 384 * sizeof(uint8_t));
183  printf("===================================\n");
184#endif
185  mem = (uint8_t*)malloc(size);
186  if (mem == NULL) return NULL;
187  enc = (VP8Encoder*)mem;
188  mem = (uint8_t*)DO_ALIGN(mem + sizeof(*enc));
189  memset(enc, 0, sizeof(*enc));
190  enc->num_parts_ = 1 << config->partitions;
191  enc->mb_w_ = mb_w;
192  enc->mb_h_ = mb_h;
193  enc->preds_w_ = preds_w;
194  enc->yuv_in_ = (uint8_t*)mem;
195  mem += YUV_SIZE;
196  enc->yuv_out_ = (uint8_t*)mem;
197  mem += YUV_SIZE;
198  enc->yuv_out2_ = (uint8_t*)mem;
199  mem += YUV_SIZE;
200  enc->yuv_p_ = (uint8_t*)mem;
201  mem += PRED_SIZE;
202  enc->mb_info_ = (VP8MBInfo*)mem;
203  mem += info_size;
204  enc->preds_ = ((uint8_t*)mem) + 1 + enc->preds_w_;
205  mem += preds_w * preds_h * sizeof(uint8_t);
206  enc->nz_ = 1 + (uint32_t*)mem;
207  mem += nz_size;
208  enc->lf_stats_ = lf_stats_size ? (LFStats*)mem : NULL;
209  mem += lf_stats_size;
210
211  // top samples (all 16-aligned)
212  mem = (uint8_t*)DO_ALIGN(mem);
213  enc->y_top_ = (uint8_t*)mem;
214  enc->uv_top_ = enc->y_top_ + top_stride;
215  mem += 2 * top_stride;
216  mem = (uint8_t*)DO_ALIGN(mem + 1);
217  enc->y_left_ = (uint8_t*)mem;
218  mem += 16 + 16;
219  enc->u_left_ = (uint8_t*)mem;
220  mem += 16;
221  enc->v_left_ = (uint8_t*)mem;
222  mem += 8;
223
224  enc->config_ = config;
225  enc->profile_ = use_filter ? ((config->filter_type == 1) ? 0 : 1) : 2;
226  enc->pic_ = picture;
227
228  MapConfigToTools(enc);
229  VP8EncDspInit();
230  VP8DefaultProbas(enc);
231  ResetSegmentHeader(enc);
232  ResetFilterHeader(enc);
233  ResetBoundaryPredictions(enc);
234
235  return enc;
236}
237
238static void DeleteEncoder(VP8Encoder* enc) {
239  free(enc);
240}
241
242//-----------------------------------------------------------------------------
243
244static double GetPSNR(uint64_t err, uint64_t size) {
245  return err ? 10. * log10(255. * 255. * size / err) : 99.;
246}
247
248static void FinalizePSNR(const VP8Encoder* const enc) {
249  WebPAuxStats* stats = enc->pic_->stats;
250  const uint64_t size = enc->sse_count_;
251  const uint64_t* const sse = enc->sse_;
252  stats->PSNR[0] = (float)GetPSNR(sse[0], size);
253  stats->PSNR[1] = (float)GetPSNR(sse[1], size / 4);
254  stats->PSNR[2] = (float)GetPSNR(sse[2], size / 4);
255  stats->PSNR[3] = (float)GetPSNR(sse[0] + sse[1] + sse[2], size * 3 / 2);
256}
257
258static void StoreStats(VP8Encoder* const enc) {
259  WebPAuxStats* const stats = enc->pic_->stats;
260  if (stats) {
261    int i, s;
262    for (i = 0; i < NUM_MB_SEGMENTS; ++i) {
263      stats->segment_level[i] = enc->dqm_[i].fstrength_;
264      stats->segment_quant[i] = enc->dqm_[i].quant_;
265      for (s = 0; s <= 2; ++s) {
266        stats->residual_bytes[s][i] = enc->residual_bytes_[s][i];
267      }
268    }
269    FinalizePSNR(enc);
270    stats->coded_size = enc->coded_size_;
271    for (i = 0; i < 3; ++i) {
272      stats->block_count[i] = enc->block_count_[i];
273    }
274  }
275}
276
277//-----------------------------------------------------------------------------
278
279int WebPEncode(const WebPConfig* const config, WebPPicture* const pic) {
280  VP8Encoder* enc;
281  int ok;
282
283  if (config == NULL || pic == NULL)
284    return 0;   // bad params
285  if (!WebPValidateConfig(config))
286    return 0;   // invalid config.
287  if (pic->width <= 0 || pic->height <= 0)
288    return 0;   // invalid parameters
289  if (pic->y == NULL || pic->u == NULL || pic->v == NULL)
290    return 0;   // invalid parameters
291  if (pic->width >= MAX_DIMENSION || pic->height >= MAX_DIMENSION)
292    return 0;   // image is too big
293
294  enc = InitEncoder(config, pic);
295  if (enc == NULL) return 0;
296  ok = VP8EncAnalyze(enc)
297    && VP8StatLoop(enc)
298    && VP8EncLoop(enc)
299    && VP8EncWrite(enc);
300  StoreStats(enc);
301  DeleteEncoder(enc);
302  return ok;
303}
304
305#if defined(__cplusplus) || defined(c_plusplus)
306}    // extern "C"
307#endif
308