tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

webp_enc.c (14317B)


      1 // Copyright 2011 Google Inc. All Rights Reserved.
      2 //
      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 // -----------------------------------------------------------------------------
      9 //
     10 // WebP encoder: main entry point
     11 //
     12 // Author: Skal (pascal.massimino@gmail.com)
     13 
     14 #include <assert.h>
     15 #include <math.h>
     16 #include <stdlib.h>
     17 #include <string.h>
     18 
     19 #include "src/dec/common_dec.h"
     20 #include "src/webp/types.h"
     21 #include "src/dsp/dsp.h"
     22 #include "src/enc/cost_enc.h"
     23 #include "src/enc/vp8i_enc.h"
     24 #include "src/enc/vp8li_enc.h"
     25 #include "src/utils/utils.h"
     26 #include "src/webp/encode.h"
     27 
     28 // #define PRINT_MEMORY_INFO
     29 
     30 #ifdef PRINT_MEMORY_INFO
     31 #include <stdio.h>
     32 #endif
     33 
     34 //------------------------------------------------------------------------------
     35 
     36 int WebPGetEncoderVersion(void) {
     37  return (ENC_MAJ_VERSION << 16) | (ENC_MIN_VERSION << 8) | ENC_REV_VERSION;
     38 }
     39 
     40 //------------------------------------------------------------------------------
     41 // VP8Encoder
     42 //------------------------------------------------------------------------------
     43 
     44 static void ResetSegmentHeader(VP8Encoder* const enc) {
     45  VP8EncSegmentHeader* const hdr = &enc->segment_hdr;
     46  hdr->num_segments = enc->config->segments;
     47  hdr->update_map  = (hdr->num_segments > 1);
     48  hdr->size = 0;
     49 }
     50 
     51 static void ResetFilterHeader(VP8Encoder* const enc) {
     52  VP8EncFilterHeader* const hdr = &enc->filter_hdr;
     53  hdr->simple = 1;
     54  hdr->level = 0;
     55  hdr->sharpness = 0;
     56  hdr->i4x4_lf_delta = 0;
     57 }
     58 
     59 static void ResetBoundaryPredictions(VP8Encoder* const enc) {
     60  // init boundary values once for all
     61  // Note: actually, initializing the 'preds[]' is only needed for intra4.
     62  int i;
     63  uint8_t* const top = enc->preds - enc->preds_w;
     64  uint8_t* const left = enc->preds - 1;
     65  for (i = -1; i < 4 * enc->mb_w; ++i) {
     66    top[i] = B_DC_PRED;
     67  }
     68  for (i = 0; i < 4 * enc->mb_h; ++i) {
     69    left[i * enc->preds_w] = B_DC_PRED;
     70  }
     71  enc->nz[-1] = 0;   // constant
     72 }
     73 
     74 // Mapping from config->method to coding tools used.
     75 //-------------------+---+---+---+---+---+---+---+
     76 //   Method          | 0 | 1 | 2 | 3 |(4)| 5 | 6 |
     77 //-------------------+---+---+---+---+---+---+---+
     78 // fast probe        | x |   |   | x |   |   |   |
     79 //-------------------+---+---+---+---+---+---+---+
     80 // dynamic proba     | ~ | x | x | x | x | x | x |
     81 //-------------------+---+---+---+---+---+---+---+
     82 // fast mode analysis|[x]|[x]|   |   | x | x | x |
     83 //-------------------+---+---+---+---+---+---+---+
     84 // basic rd-opt      |   |   |   | x | x | x | x |
     85 //-------------------+---+---+---+---+---+---+---+
     86 // disto-refine i4/16| x | x | x |   |   |   |   |
     87 //-------------------+---+---+---+---+---+---+---+
     88 // disto-refine uv   |   | x | x |   |   |   |   |
     89 //-------------------+---+---+---+---+---+---+---+
     90 // rd-opt i4/16      |   |   | ~ | x | x | x | x |
     91 //-------------------+---+---+---+---+---+---+---+
     92 // token buffer (opt)|   |   |   | x | x | x | x |
     93 //-------------------+---+---+---+---+---+---+---+
     94 // Trellis           |   |   |   |   |   | x |Ful|
     95 //-------------------+---+---+---+---+---+---+---+
     96 // full-SNS          |   |   |   |   | x | x | x |
     97 //-------------------+---+---+---+---+---+---+---+
     98 
     99 static void MapConfigToTools(VP8Encoder* const enc) {
    100  const WebPConfig* const config = enc->config;
    101  const int method = config->method;
    102  const int limit = 100 - config->partition_limit;
    103  enc->method = method;
    104  enc->rd_opt_level = (method >= 6) ? RD_OPT_TRELLIS_ALL
    105                    : (method >= 5) ? RD_OPT_TRELLIS
    106                    : (method >= 3) ? RD_OPT_BASIC
    107                    : RD_OPT_NONE;
    108  enc->max_i4_header_bits =
    109      256 * 16 * 16 *                 // upper bound: up to 16bit per 4x4 block
    110      (limit * limit) / (100 * 100);  // ... modulated with a quadratic curve.
    111 
    112  // partition0 = 512k max.
    113  enc->mb_header_limit =
    114      (score_t)256 * 510 * 8 * 1024 / (enc->mb_w * enc->mb_h);
    115 
    116  enc->thread_level = config->thread_level;
    117 
    118  enc->do_search = (config->target_size > 0 || config->target_PSNR > 0);
    119  if (!config->low_memory) {
    120 #if !defined(DISABLE_TOKEN_BUFFER)
    121    enc->use_tokens = (enc->rd_opt_level >= RD_OPT_BASIC);  // need rd stats
    122 #endif
    123    if (enc->use_tokens) {
    124      enc->num_parts = 1;   // doesn't work with multi-partition
    125    }
    126  }
    127 }
    128 
    129 // Memory scaling with dimensions:
    130 //  memory (bytes) ~= 2.25 * w + 0.0625 * w * h
    131 //
    132 // Typical memory footprint (614x440 picture)
    133 //              encoder: 22111
    134 //                 info: 4368
    135 //                preds: 17741
    136 //          top samples: 1263
    137 //             non-zero: 175
    138 //             lf-stats: 0
    139 //                total: 45658
    140 // Transient object sizes:
    141 //       VP8EncIterator: 3360
    142 //         VP8ModeScore: 872
    143 //       VP8SegmentInfo: 732
    144 //          VP8EncProba: 18352
    145 //              LFStats: 2048
    146 // Picture size (yuv): 419328
    147 
    148 static VP8Encoder* InitVP8Encoder(const WebPConfig* const config,
    149                                  WebPPicture* const picture) {
    150  VP8Encoder* enc;
    151  const int use_filter =
    152      (config->filter_strength > 0) || (config->autofilter > 0);
    153  const int mb_w = (picture->width + 15) >> 4;
    154  const int mb_h = (picture->height + 15) >> 4;
    155  const int preds_w = 4 * mb_w + 1;
    156  const int preds_h = 4 * mb_h + 1;
    157  const size_t preds_size = preds_w * preds_h * sizeof(*enc->preds);
    158  const int top_stride = mb_w * 16;
    159  const size_t nz_size = (mb_w + 1) * sizeof(*enc->nz) + WEBP_ALIGN_CST;
    160  const size_t info_size = mb_w * mb_h * sizeof(*enc->mb_info);
    161  const size_t samples_size =
    162      2 * top_stride * sizeof(*enc->y_top)   // top-luma/u/v
    163      + WEBP_ALIGN_CST;                      // align all
    164  const size_t lf_stats_size =
    165      config->autofilter ? sizeof(*enc->lf_stats) + WEBP_ALIGN_CST : 0;
    166  const size_t top_derr_size =
    167      (config->quality <= ERROR_DIFFUSION_QUALITY || config->pass > 1) ?
    168          mb_w * sizeof(*enc->top_derr) : 0;
    169  uint8_t* mem;
    170  const uint64_t size = (uint64_t)sizeof(*enc)   // main struct
    171                      + WEBP_ALIGN_CST           // cache alignment
    172                      + info_size                // modes info
    173                      + preds_size               // prediction modes
    174                      + samples_size             // top/left samples
    175                      + top_derr_size            // top diffusion error
    176                      + nz_size                  // coeff context bits
    177                      + lf_stats_size;           // autofilter stats
    178 
    179 #ifdef PRINT_MEMORY_INFO
    180  printf("===================================\n");
    181  printf("Memory used:\n"
    182         "             encoder: %ld\n"
    183         "                info: %ld\n"
    184         "               preds: %ld\n"
    185         "         top samples: %ld\n"
    186         "       top diffusion: %ld\n"
    187         "            non-zero: %ld\n"
    188         "            lf-stats: %ld\n"
    189         "               total: %ld\n",
    190         sizeof(*enc) + WEBP_ALIGN_CST, info_size,
    191         preds_size, samples_size, top_derr_size, nz_size, lf_stats_size, size);
    192  printf("Transient object sizes:\n"
    193         "      VP8EncIterator: %ld\n"
    194         "        VP8ModeScore: %ld\n"
    195         "      VP8SegmentInfo: %ld\n"
    196         "         VP8EncProba: %ld\n"
    197         "             LFStats: %ld\n",
    198         sizeof(VP8EncIterator), sizeof(VP8ModeScore),
    199         sizeof(VP8SegmentInfo), sizeof(VP8EncProba),
    200         sizeof(LFStats));
    201  printf("Picture size (yuv): %ld\n",
    202         mb_w * mb_h * 384 * sizeof(uint8_t));
    203  printf("===================================\n");
    204 #endif
    205  mem = (uint8_t*)WebPSafeMalloc(size, sizeof(*mem));
    206  if (mem == NULL) {
    207    WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
    208    return NULL;
    209  }
    210  enc = (VP8Encoder*)mem;
    211  mem = (uint8_t*)WEBP_ALIGN(mem + sizeof(*enc));
    212  memset(enc, 0, sizeof(*enc));
    213  enc->num_parts = 1 << config->partitions;
    214  enc->mb_w = mb_w;
    215  enc->mb_h = mb_h;
    216  enc->preds_w = preds_w;
    217  enc->mb_info = (VP8MBInfo*)mem;
    218  mem += info_size;
    219  enc->preds = mem + 1 + enc->preds_w;
    220  mem += preds_size;
    221  enc->nz = 1 + (uint32_t*)WEBP_ALIGN(mem);
    222  mem += nz_size;
    223  enc->lf_stats = lf_stats_size ? (LFStats*)WEBP_ALIGN(mem) : NULL;
    224  mem += lf_stats_size;
    225 
    226  // top samples (all 16-aligned)
    227  mem = (uint8_t*)WEBP_ALIGN(mem);
    228  enc->y_top = mem;
    229  enc->uv_top = enc->y_top + top_stride;
    230  mem += 2 * top_stride;
    231  enc->top_derr = top_derr_size ? (DError*)mem : NULL;
    232  mem += top_derr_size;
    233  assert(mem <= (uint8_t*)enc + size);
    234 
    235  enc->config = config;
    236  enc->profile = use_filter ? ((config->filter_type == 1) ? 0 : 1) : 2;
    237  enc->pic = picture;
    238  enc->percent = 0;
    239 
    240  MapConfigToTools(enc);
    241  VP8EncDspInit();
    242  VP8DefaultProbas(enc);
    243  ResetSegmentHeader(enc);
    244  ResetFilterHeader(enc);
    245  ResetBoundaryPredictions(enc);
    246  VP8EncDspCostInit();
    247  VP8EncInitAlpha(enc);
    248 
    249  // lower quality means smaller output -> we modulate a little the page
    250  // size based on quality. This is just a crude 1rst-order prediction.
    251  {
    252    const float scale = 1.f + config->quality * 5.f / 100.f;  // in [1,6]
    253    VP8TBufferInit(&enc->tokens, (int)(mb_w * mb_h * 4 * scale));
    254  }
    255  return enc;
    256 }
    257 
    258 static int DeleteVP8Encoder(VP8Encoder* enc) {
    259  int ok = 1;
    260  if (enc != NULL) {
    261    ok = VP8EncDeleteAlpha(enc);
    262    VP8TBufferClear(&enc->tokens);
    263    WebPSafeFree(enc);
    264  }
    265  return ok;
    266 }
    267 
    268 //------------------------------------------------------------------------------
    269 
    270 #if !defined(WEBP_DISABLE_STATS)
    271 static double GetPSNR(uint64_t err, uint64_t size) {
    272  return (err > 0 && size > 0) ? 10. * log10(255. * 255. * size / err) : 99.;
    273 }
    274 
    275 static void FinalizePSNR(const VP8Encoder* const enc) {
    276  WebPAuxStats* stats = enc->pic->stats;
    277  const uint64_t size = enc->sse_count;
    278  const uint64_t* const sse = enc->sse;
    279  stats->PSNR[0] = (float)GetPSNR(sse[0], size);
    280  stats->PSNR[1] = (float)GetPSNR(sse[1], size / 4);
    281  stats->PSNR[2] = (float)GetPSNR(sse[2], size / 4);
    282  stats->PSNR[3] = (float)GetPSNR(sse[0] + sse[1] + sse[2], size * 3 / 2);
    283  stats->PSNR[4] = (float)GetPSNR(sse[3], size);
    284 }
    285 #endif  // !defined(WEBP_DISABLE_STATS)
    286 
    287 static void StoreStats(VP8Encoder* const enc) {
    288 #if !defined(WEBP_DISABLE_STATS)
    289  WebPAuxStats* const stats = enc->pic->stats;
    290  if (stats != NULL) {
    291    int i, s;
    292    for (i = 0; i < NUM_MB_SEGMENTS; ++i) {
    293      stats->segment_level[i] = enc->dqm[i].fstrength;
    294      stats->segment_quant[i] = enc->dqm[i].quant;
    295      for (s = 0; s <= 2; ++s) {
    296        stats->residual_bytes[s][i] = enc->residual_bytes[s][i];
    297      }
    298    }
    299    FinalizePSNR(enc);
    300    stats->coded_size = enc->coded_size;
    301    for (i = 0; i < 3; ++i) {
    302      stats->block_count[i] = enc->block_count[i];
    303    }
    304  }
    305 #else  // defined(WEBP_DISABLE_STATS)
    306  WebPReportProgress(enc->pic, 100, &enc->percent);  // done!
    307 #endif  // !defined(WEBP_DISABLE_STATS)
    308 }
    309 
    310 int WebPEncodingSetError(const WebPPicture* const pic,
    311                         WebPEncodingError error) {
    312  assert((int)error < VP8_ENC_ERROR_LAST);
    313  assert((int)error >= VP8_ENC_OK);
    314  // The oldest error reported takes precedence over the new one.
    315  if (pic->error_code == VP8_ENC_OK) {
    316    ((WebPPicture*)pic)->error_code = error;
    317  }
    318  return 0;
    319 }
    320 
    321 int WebPReportProgress(const WebPPicture* const pic,
    322                       int percent, int* const percent_store) {
    323  if (percent_store != NULL && percent != *percent_store) {
    324    *percent_store = percent;
    325    if (pic->progress_hook && !pic->progress_hook(percent, pic)) {
    326      // user abort requested
    327      return WebPEncodingSetError(pic, VP8_ENC_ERROR_USER_ABORT);
    328    }
    329  }
    330  return 1;  // ok
    331 }
    332 //------------------------------------------------------------------------------
    333 
    334 int WebPEncode(const WebPConfig* config, WebPPicture* pic) {
    335  int ok = 0;
    336  if (pic == NULL) return 0;
    337 
    338  pic->error_code = VP8_ENC_OK;  // all ok so far
    339  if (config == NULL) {  // bad params
    340    return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER);
    341  }
    342  if (!WebPValidateConfig(config)) {
    343    return WebPEncodingSetError(pic, VP8_ENC_ERROR_INVALID_CONFIGURATION);
    344  }
    345  if (!WebPValidatePicture(pic)) return 0;
    346  if (pic->width > WEBP_MAX_DIMENSION || pic->height > WEBP_MAX_DIMENSION) {
    347    return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION);
    348  }
    349 
    350  if (pic->stats != NULL) memset(pic->stats, 0, sizeof(*pic->stats));
    351 
    352  if (!config->lossless) {
    353    VP8Encoder* enc = NULL;
    354 
    355    if (pic->use_argb || pic->y == NULL || pic->u == NULL || pic->v == NULL) {
    356      // Make sure we have YUVA samples.
    357      if (config->use_sharp_yuv || (config->preprocessing & 4)) {
    358        if (!WebPPictureSharpARGBToYUVA(pic)) {
    359          return 0;
    360        }
    361      } else {
    362        float dithering = 0.f;
    363        if (config->preprocessing & 2) {
    364          const float x = config->quality / 100.f;
    365          const float x2 = x * x;
    366          // slowly decreasing from max dithering at low quality (q->0)
    367          // to 0.5 dithering amplitude at high quality (q->100)
    368          dithering = 1.0f + (0.5f - 1.0f) * x2 * x2;
    369        }
    370        if (!WebPPictureARGBToYUVADithered(pic, WEBP_YUV420, dithering)) {
    371          return 0;
    372        }
    373      }
    374    }
    375 
    376    if (!config->exact) {
    377      WebPCleanupTransparentArea(pic);
    378    }
    379 
    380    enc = InitVP8Encoder(config, pic);
    381    if (enc == NULL) return 0;  // pic->error is already set.
    382    // Note: each of the tasks below account for 20% in the progress report.
    383    ok = VP8EncAnalyze(enc);
    384 
    385    // Analysis is done, proceed to actual coding.
    386    ok = ok && VP8EncStartAlpha(enc);   // possibly done in parallel
    387    if (!enc->use_tokens) {
    388      ok = ok && VP8EncLoop(enc);
    389    } else {
    390      ok = ok && VP8EncTokenLoop(enc);
    391    }
    392    ok = ok && VP8EncFinishAlpha(enc);
    393 
    394    ok = ok && VP8EncWrite(enc);
    395    StoreStats(enc);
    396    if (!ok) {
    397      VP8EncFreeBitWriters(enc);
    398    }
    399    ok &= DeleteVP8Encoder(enc);  // must always be called, even if !ok
    400  } else {
    401    // Make sure we have ARGB samples.
    402    if (pic->argb == NULL && !WebPPictureYUVAToARGB(pic)) {
    403      return 0;
    404    }
    405 
    406    if (!config->exact) {
    407      WebPReplaceTransparentPixels(pic, 0x000000);
    408    }
    409 
    410    ok = VP8LEncodeImage(config, pic);  // Sets pic->error in case of problem.
    411  }
    412 
    413  return ok;
    414 }