github.com/cellofellow/gopkg@v0.0.0-20140722061823-eec0544a62ad/image/webp/libwebp/src/enc/webpenc.c (about)

     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 <stdlib.h>
    16  #include <string.h>
    17  #include <math.h>
    18  
    19  #include "./vp8enci.h"
    20  #include "./vp8li.h"
    21  #include "../utils/utils.h"
    22  
    23  // #define PRINT_MEMORY_INFO
    24  
    25  #ifdef PRINT_MEMORY_INFO
    26  #include <stdio.h>
    27  #endif
    28  
    29  //------------------------------------------------------------------------------
    30  
    31  int WebPGetEncoderVersion(void) {
    32    return (ENC_MAJ_VERSION << 16) | (ENC_MIN_VERSION << 8) | ENC_REV_VERSION;
    33  }
    34  
    35  //------------------------------------------------------------------------------
    36  // WebPPicture
    37  //------------------------------------------------------------------------------
    38  
    39  static int DummyWriter(const uint8_t* data, size_t data_size,
    40                         const WebPPicture* const picture) {
    41    // The following are to prevent 'unused variable' error message.
    42    (void)data;
    43    (void)data_size;
    44    (void)picture;
    45    return 1;
    46  }
    47  
    48  int WebPPictureInitInternal(WebPPicture* picture, int version) {
    49    if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_ENCODER_ABI_VERSION)) {
    50      return 0;   // caller/system version mismatch!
    51    }
    52    if (picture != NULL) {
    53      memset(picture, 0, sizeof(*picture));
    54      picture->writer = DummyWriter;
    55      WebPEncodingSetError(picture, VP8_ENC_OK);
    56    }
    57    return 1;
    58  }
    59  
    60  //------------------------------------------------------------------------------
    61  // VP8Encoder
    62  //------------------------------------------------------------------------------
    63  
    64  static void ResetSegmentHeader(VP8Encoder* const enc) {
    65    VP8SegmentHeader* const hdr = &enc->segment_hdr_;
    66    hdr->num_segments_ = enc->config_->segments;
    67    hdr->update_map_  = (hdr->num_segments_ > 1);
    68    hdr->size_ = 0;
    69  }
    70  
    71  static void ResetFilterHeader(VP8Encoder* const enc) {
    72    VP8FilterHeader* const hdr = &enc->filter_hdr_;
    73    hdr->simple_ = 1;
    74    hdr->level_ = 0;
    75    hdr->sharpness_ = 0;
    76    hdr->i4x4_lf_delta_ = 0;
    77  }
    78  
    79  static void ResetBoundaryPredictions(VP8Encoder* const enc) {
    80    // init boundary values once for all
    81    // Note: actually, initializing the preds_[] is only needed for intra4.
    82    int i;
    83    uint8_t* const top = enc->preds_ - enc->preds_w_;
    84    uint8_t* const left = enc->preds_ - 1;
    85    for (i = -1; i < 4 * enc->mb_w_; ++i) {
    86      top[i] = B_DC_PRED;
    87    }
    88    for (i = 0; i < 4 * enc->mb_h_; ++i) {
    89      left[i * enc->preds_w_] = B_DC_PRED;
    90    }
    91    enc->nz_[-1] = 0;   // constant
    92  }
    93  
    94  // Mapping from config->method_ to coding tools used.
    95  //-------------------+---+---+---+---+---+---+---+
    96  //   Method          | 0 | 1 | 2 | 3 |(4)| 5 | 6 |
    97  //-------------------+---+---+---+---+---+---+---+
    98  // fast probe        | x |   |   | x |   |   |   |
    99  //-------------------+---+---+---+---+---+---+---+
   100  // dynamic proba     | ~ | x | x | x | x | x | x |
   101  //-------------------+---+---+---+---+---+---+---+
   102  // fast mode analysis|   |   |   |   | x | x | x |
   103  //-------------------+---+---+---+---+---+---+---+
   104  // basic rd-opt      |   |   |   | x | x | x | x |
   105  //-------------------+---+---+---+---+---+---+---+
   106  // disto-score i4/16 |   |   | x |   |   |   |   |
   107  //-------------------+---+---+---+---+---+---+---+
   108  // rd-opt i4/16      |   |   | ~ | x | x | x | x |
   109  //-------------------+---+---+---+---+---+---+---+
   110  // token buffer (opt)|   |   |   | x | x | x | x |
   111  //-------------------+---+---+---+---+---+---+---+
   112  // Trellis           |   |   |   |   |   | x |Ful|
   113  //-------------------+---+---+---+---+---+---+---+
   114  // full-SNS          |   |   |   |   | x | x | x |
   115  //-------------------+---+---+---+---+---+---+---+
   116  
   117  static void MapConfigToTools(VP8Encoder* const enc) {
   118    const WebPConfig* const config = enc->config_;
   119    const int method = config->method;
   120    const int limit = 100 - config->partition_limit;
   121    enc->method_ = method;
   122    enc->rd_opt_level_ = (method >= 6) ? RD_OPT_TRELLIS_ALL
   123                       : (method >= 5) ? RD_OPT_TRELLIS
   124                       : (method >= 3) ? RD_OPT_BASIC
   125                       : RD_OPT_NONE;
   126    enc->max_i4_header_bits_ =
   127        256 * 16 * 16 *                 // upper bound: up to 16bit per 4x4 block
   128        (limit * limit) / (100 * 100);  // ... modulated with a quadratic curve.
   129  
   130    enc->thread_level_ = config->thread_level;
   131  
   132    enc->do_search_ = (config->target_size > 0 || config->target_PSNR > 0);
   133    if (!config->low_memory) {
   134  #if !defined(DISABLE_TOKEN_BUFFER)
   135      enc->use_tokens_ = (enc->rd_opt_level_ >= RD_OPT_BASIC);  // need rd stats
   136  #endif
   137      if (enc->use_tokens_) {
   138        enc->num_parts_ = 1;   // doesn't work with multi-partition
   139      }
   140    }
   141  }
   142  
   143  // Memory scaling with dimensions:
   144  //  memory (bytes) ~= 2.25 * w + 0.0625 * w * h
   145  //
   146  // Typical memory footprint (768x510 picture)
   147  // Memory used:
   148  //              encoder: 33919
   149  //          block cache: 2880
   150  //                 info: 3072
   151  //                preds: 24897
   152  //          top samples: 1623
   153  //             non-zero: 196
   154  //             lf-stats: 2048
   155  //                total: 68635
   156  // Transient object sizes:
   157  //       VP8EncIterator: 352
   158  //         VP8ModeScore: 912
   159  //       VP8SegmentInfo: 532
   160  //             VP8Proba: 31032
   161  //              LFStats: 2048
   162  // Picture size (yuv): 589824
   163  
   164  static VP8Encoder* InitVP8Encoder(const WebPConfig* const config,
   165                                    WebPPicture* const picture) {
   166    const int use_filter =
   167        (config->filter_strength > 0) || (config->autofilter > 0);
   168    const int mb_w = (picture->width + 15) >> 4;
   169    const int mb_h = (picture->height + 15) >> 4;
   170    const int preds_w = 4 * mb_w + 1;
   171    const int preds_h = 4 * mb_h + 1;
   172    const size_t preds_size = preds_w * preds_h * sizeof(uint8_t);
   173    const int top_stride = mb_w * 16;
   174    const size_t nz_size = (mb_w + 1) * sizeof(uint32_t) + ALIGN_CST;
   175    const size_t info_size = mb_w * mb_h * sizeof(VP8MBInfo);
   176    const size_t samples_size = 2 * top_stride * sizeof(uint8_t)  // top-luma/u/v
   177                              + ALIGN_CST;                        // align all
   178    const size_t lf_stats_size =
   179        config->autofilter ? sizeof(LFStats) + ALIGN_CST : 0;
   180    VP8Encoder* enc;
   181    uint8_t* mem;
   182    const uint64_t size = (uint64_t)sizeof(VP8Encoder)   // main struct
   183                        + ALIGN_CST                      // cache alignment
   184                        + info_size                      // modes info
   185                        + preds_size                     // prediction modes
   186                        + samples_size                   // top/left samples
   187                        + nz_size                        // coeff context bits
   188                        + lf_stats_size;                 // autofilter stats
   189  
   190  #ifdef PRINT_MEMORY_INFO
   191    printf("===================================\n");
   192    printf("Memory used:\n"
   193           "             encoder: %ld\n"
   194           "                info: %ld\n"
   195           "               preds: %ld\n"
   196           "         top samples: %ld\n"
   197           "            non-zero: %ld\n"
   198           "            lf-stats: %ld\n"
   199           "               total: %ld\n",
   200           sizeof(VP8Encoder) + ALIGN_CST, info_size,
   201           preds_size, samples_size, nz_size, lf_stats_size, size);
   202    printf("Transient object sizes:\n"
   203           "      VP8EncIterator: %ld\n"
   204           "        VP8ModeScore: %ld\n"
   205           "      VP8SegmentInfo: %ld\n"
   206           "            VP8Proba: %ld\n"
   207           "             LFStats: %ld\n",
   208           sizeof(VP8EncIterator), sizeof(VP8ModeScore),
   209           sizeof(VP8SegmentInfo), sizeof(VP8Proba),
   210           sizeof(LFStats));
   211    printf("Picture size (yuv): %ld\n",
   212           mb_w * mb_h * 384 * sizeof(uint8_t));
   213    printf("===================================\n");
   214  #endif
   215    mem = (uint8_t*)WebPSafeMalloc(size, sizeof(*mem));
   216    if (mem == NULL) {
   217      WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
   218      return NULL;
   219    }
   220    enc = (VP8Encoder*)mem;
   221    mem = (uint8_t*)DO_ALIGN(mem + sizeof(*enc));
   222    memset(enc, 0, sizeof(*enc));
   223    enc->num_parts_ = 1 << config->partitions;
   224    enc->mb_w_ = mb_w;
   225    enc->mb_h_ = mb_h;
   226    enc->preds_w_ = preds_w;
   227    enc->mb_info_ = (VP8MBInfo*)mem;
   228    mem += info_size;
   229    enc->preds_ = ((uint8_t*)mem) + 1 + enc->preds_w_;
   230    mem += preds_w * preds_h * sizeof(uint8_t);
   231    enc->nz_ = 1 + (uint32_t*)DO_ALIGN(mem);
   232    mem += nz_size;
   233    enc->lf_stats_ = lf_stats_size ? (LFStats*)DO_ALIGN(mem) : NULL;
   234    mem += lf_stats_size;
   235  
   236    // top samples (all 16-aligned)
   237    mem = (uint8_t*)DO_ALIGN(mem);
   238    enc->y_top_ = (uint8_t*)mem;
   239    enc->uv_top_ = enc->y_top_ + top_stride;
   240    mem += 2 * top_stride;
   241    assert(mem <= (uint8_t*)enc + size);
   242  
   243    enc->config_ = config;
   244    enc->profile_ = use_filter ? ((config->filter_type == 1) ? 0 : 1) : 2;
   245    enc->pic_ = picture;
   246    enc->percent_ = 0;
   247  
   248    MapConfigToTools(enc);
   249    VP8EncDspInit();
   250    VP8DefaultProbas(enc);
   251    ResetSegmentHeader(enc);
   252    ResetFilterHeader(enc);
   253    ResetBoundaryPredictions(enc);
   254  
   255    VP8EncInitAlpha(enc);
   256  #ifdef WEBP_EXPERIMENTAL_FEATURES
   257    VP8EncInitLayer(enc);
   258  #endif
   259  
   260    VP8TBufferInit(&enc->tokens_);
   261    return enc;
   262  }
   263  
   264  static int DeleteVP8Encoder(VP8Encoder* enc) {
   265    int ok = 1;
   266    if (enc != NULL) {
   267      ok = VP8EncDeleteAlpha(enc);
   268  #ifdef WEBP_EXPERIMENTAL_FEATURES
   269      VP8EncDeleteLayer(enc);
   270  #endif
   271      VP8TBufferClear(&enc->tokens_);
   272      free(enc);
   273    }
   274    return ok;
   275  }
   276  
   277  //------------------------------------------------------------------------------
   278  
   279  static double GetPSNR(uint64_t err, uint64_t size) {
   280    return (err > 0 && size > 0) ? 10. * log10(255. * 255. * size / err) : 99.;
   281  }
   282  
   283  static void FinalizePSNR(const VP8Encoder* const enc) {
   284    WebPAuxStats* stats = enc->pic_->stats;
   285    const uint64_t size = enc->sse_count_;
   286    const uint64_t* const sse = enc->sse_;
   287    stats->PSNR[0] = (float)GetPSNR(sse[0], size);
   288    stats->PSNR[1] = (float)GetPSNR(sse[1], size / 4);
   289    stats->PSNR[2] = (float)GetPSNR(sse[2], size / 4);
   290    stats->PSNR[3] = (float)GetPSNR(sse[0] + sse[1] + sse[2], size * 3 / 2);
   291    stats->PSNR[4] = (float)GetPSNR(sse[3], size);
   292  }
   293  
   294  static void StoreStats(VP8Encoder* const enc) {
   295    WebPAuxStats* const stats = enc->pic_->stats;
   296    if (stats != NULL) {
   297      int i, s;
   298      for (i = 0; i < NUM_MB_SEGMENTS; ++i) {
   299        stats->segment_level[i] = enc->dqm_[i].fstrength_;
   300        stats->segment_quant[i] = enc->dqm_[i].quant_;
   301        for (s = 0; s <= 2; ++s) {
   302          stats->residual_bytes[s][i] = enc->residual_bytes_[s][i];
   303        }
   304      }
   305      FinalizePSNR(enc);
   306      stats->coded_size = enc->coded_size_;
   307      for (i = 0; i < 3; ++i) {
   308        stats->block_count[i] = enc->block_count_[i];
   309      }
   310    }
   311    WebPReportProgress(enc->pic_, 100, &enc->percent_);  // done!
   312  }
   313  
   314  int WebPEncodingSetError(const WebPPicture* const pic,
   315                           WebPEncodingError error) {
   316    assert((int)error < VP8_ENC_ERROR_LAST);
   317    assert((int)error >= VP8_ENC_OK);
   318    ((WebPPicture*)pic)->error_code = error;
   319    return 0;
   320  }
   321  
   322  int WebPReportProgress(const WebPPicture* const pic,
   323                         int percent, int* const percent_store) {
   324    if (percent_store != NULL && percent != *percent_store) {
   325      *percent_store = percent;
   326      if (pic->progress_hook && !pic->progress_hook(percent, pic)) {
   327        // user abort requested
   328        WebPEncodingSetError(pic, VP8_ENC_ERROR_USER_ABORT);
   329        return 0;
   330      }
   331    }
   332    return 1;  // ok
   333  }
   334  //------------------------------------------------------------------------------
   335  
   336  int WebPEncode(const WebPConfig* config, WebPPicture* pic) {
   337    int ok = 0;
   338  
   339    if (pic == NULL)
   340      return 0;
   341    WebPEncodingSetError(pic, VP8_ENC_OK);  // all ok so far
   342    if (config == NULL)  // bad params
   343      return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER);
   344    if (!WebPValidateConfig(config))
   345      return WebPEncodingSetError(pic, VP8_ENC_ERROR_INVALID_CONFIGURATION);
   346    if (pic->width <= 0 || pic->height <= 0)
   347      return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION);
   348    if (pic->width > WEBP_MAX_DIMENSION || pic->height > WEBP_MAX_DIMENSION)
   349      return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION);
   350  
   351    if (pic->stats != NULL) memset(pic->stats, 0, sizeof(*pic->stats));
   352  
   353    if (!config->lossless) {
   354      VP8Encoder* enc = NULL;
   355      if (pic->y == NULL || pic->u == NULL || pic->v == NULL) {
   356        // Make sure we have YUVA samples.
   357        float dithering = 0.f;
   358        if (config->preprocessing & 2) {
   359          const float x = config->quality / 100.f;
   360          const float x2 = x * x;
   361          // slowly decreasing from max dithering at low quality (q->0)
   362          // to 0.5 dithering amplitude at high quality (q->100)
   363          dithering = 1.0f + (0.5f - 1.0f) * x2 * x2;
   364        }
   365        if (!WebPPictureARGBToYUVADithered(pic, WEBP_YUV420, dithering)) {
   366          return 0;
   367        }
   368      }
   369  
   370      enc = InitVP8Encoder(config, pic);
   371      if (enc == NULL) return 0;  // pic->error is already set.
   372      // Note: each of the tasks below account for 20% in the progress report.
   373      ok = VP8EncAnalyze(enc);
   374  
   375      // Analysis is done, proceed to actual coding.
   376      ok = ok && VP8EncStartAlpha(enc);   // possibly done in parallel
   377      if (!enc->use_tokens_) {
   378        ok = ok && VP8EncLoop(enc);
   379      } else {
   380        ok = ok && VP8EncTokenLoop(enc);
   381      }
   382      ok = ok && VP8EncFinishAlpha(enc);
   383  #ifdef WEBP_EXPERIMENTAL_FEATURES
   384      ok = ok && VP8EncFinishLayer(enc);
   385  #endif
   386  
   387      ok = ok && VP8EncWrite(enc);
   388      StoreStats(enc);
   389      if (!ok) {
   390        VP8EncFreeBitWriters(enc);
   391      }
   392      ok &= DeleteVP8Encoder(enc);  // must always be called, even if !ok
   393    } else {
   394      // Make sure we have ARGB samples.
   395      if (pic->argb == NULL && !WebPPictureYUVAToARGB(pic)) {
   396        return 0;
   397      }
   398  
   399      ok = VP8LEncodeImage(config, pic);  // Sets pic->error in case of problem.
   400    }
   401  
   402    return ok;
   403  }
   404