go-libwebp

Experimental translation from libwebp to Go source.
Log | Files | Refs | README | LICENSE

syntax_enc.c (12758B)


      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 // Header syntax writing
     11 //
     12 // Author: Skal (pascal.massimino@gmail.com)
     13 
     14 #include <assert.h>
     15 #include <stddef.h>
     16 
     17 #include "src/dec/common_dec.h"
     18 #include "src/webp/types.h"
     19 #include "src/enc/vp8i_enc.h"
     20 #include "src/utils/bit_writer_utils.h"
     21 #include "src/utils/utils.h"
     22 #include "src/webp/encode.h"
     23 #include "src/webp/format_constants.h"  // RIFF constants
     24 #include "src/webp/mux_types.h"         // ALPHA_FLAG
     25 
     26 //------------------------------------------------------------------------------
     27 // Helper functions
     28 
     29 static int IsVP8XNeeded(const VP8Encoder* const enc) {
     30   return !!enc->has_alpha;  // Currently the only case when VP8X is needed.
     31                             // This could change in the future.
     32 }
     33 
     34 static int PutPaddingByte(const WebPPicture* const pic) {
     35   const uint8_t pad_byte[1] = { 0 };
     36   return !!pic->writer(pad_byte, 1, pic);
     37 }
     38 
     39 //------------------------------------------------------------------------------
     40 // Writers for header's various pieces (in order of appearance)
     41 
     42 static WebPEncodingError PutRIFFHeader(const VP8Encoder* const enc,
     43                                        size_t riff_size) {
     44   const WebPPicture* const pic = enc->pic;
     45   uint8_t riff[RIFF_HEADER_SIZE] = {
     46     'R', 'I', 'F', 'F', 0, 0, 0, 0, 'W', 'E', 'B', 'P'
     47   };
     48   assert(riff_size == (uint32_t)riff_size);
     49   PutLE32(riff + TAG_SIZE, (uint32_t)riff_size);
     50   if (!pic->writer(riff, sizeof(riff), pic)) {
     51     return VP8_ENC_ERROR_BAD_WRITE;
     52   }
     53   return VP8_ENC_OK;
     54 }
     55 
     56 static WebPEncodingError PutVP8XHeader(const VP8Encoder* const enc) {
     57   const WebPPicture* const pic = enc->pic;
     58   uint8_t vp8x[CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE] = {
     59     'V', 'P', '8', 'X'
     60   };
     61   uint32_t flags = 0;
     62 
     63   assert(IsVP8XNeeded(enc));
     64   assert(pic->width >= 1 && pic->height >= 1);
     65   assert(pic->width <= MAX_CANVAS_SIZE && pic->height <= MAX_CANVAS_SIZE);
     66 
     67   if (enc->has_alpha) {
     68     flags |= ALPHA_FLAG;
     69   }
     70 
     71   PutLE32(vp8x + TAG_SIZE,              VP8X_CHUNK_SIZE);
     72   PutLE32(vp8x + CHUNK_HEADER_SIZE,     flags);
     73   PutLE24(vp8x + CHUNK_HEADER_SIZE + 4, pic->width - 1);
     74   PutLE24(vp8x + CHUNK_HEADER_SIZE + 7, pic->height - 1);
     75   if (!pic->writer(vp8x, sizeof(vp8x), pic)) {
     76     return VP8_ENC_ERROR_BAD_WRITE;
     77   }
     78   return VP8_ENC_OK;
     79 }
     80 
     81 static WebPEncodingError PutAlphaChunk(const VP8Encoder* const enc) {
     82   const WebPPicture* const pic = enc->pic;
     83   uint8_t alpha_chunk_hdr[CHUNK_HEADER_SIZE] = {
     84     'A', 'L', 'P', 'H'
     85   };
     86 
     87   assert(enc->has_alpha);
     88 
     89   // Alpha chunk header.
     90   PutLE32(alpha_chunk_hdr + TAG_SIZE, enc->alpha_data_size);
     91   if (!pic->writer(alpha_chunk_hdr, sizeof(alpha_chunk_hdr), pic)) {
     92     return VP8_ENC_ERROR_BAD_WRITE;
     93   }
     94 
     95   // Alpha chunk data.
     96   if (!pic->writer(enc->alpha_data, enc->alpha_data_size, pic)) {
     97     return VP8_ENC_ERROR_BAD_WRITE;
     98   }
     99 
    100   // Padding.
    101   if ((enc->alpha_data_size & 1) && !PutPaddingByte(pic)) {
    102     return VP8_ENC_ERROR_BAD_WRITE;
    103   }
    104   return VP8_ENC_OK;
    105 }
    106 
    107 static WebPEncodingError PutVP8Header(const WebPPicture* const pic,
    108                                       size_t vp8_size) {
    109   uint8_t vp8_chunk_hdr[CHUNK_HEADER_SIZE] = {
    110     'V', 'P', '8', ' '
    111   };
    112   assert(vp8_size == (uint32_t)vp8_size);
    113   PutLE32(vp8_chunk_hdr + TAG_SIZE, (uint32_t)vp8_size);
    114   if (!pic->writer(vp8_chunk_hdr, sizeof(vp8_chunk_hdr), pic)) {
    115     return VP8_ENC_ERROR_BAD_WRITE;
    116   }
    117   return VP8_ENC_OK;
    118 }
    119 
    120 static WebPEncodingError PutVP8FrameHeader(const WebPPicture* const pic,
    121                                            int profile, size_t size0) {
    122   uint8_t vp8_frm_hdr[VP8_FRAME_HEADER_SIZE];
    123   uint32_t bits;
    124 
    125   if (size0 >= VP8_MAX_PARTITION0_SIZE) {  // partition #0 is too big to fit
    126     return VP8_ENC_ERROR_PARTITION0_OVERFLOW;
    127   }
    128 
    129   // Paragraph 9.1.
    130   bits = 0                         // keyframe (1b)
    131        | (profile << 1)            // profile (3b)
    132        | (1 << 4)                  // visible (1b)
    133        | ((uint32_t)size0 << 5);   // partition length (19b)
    134   vp8_frm_hdr[0] = (bits >>  0) & 0xff;
    135   vp8_frm_hdr[1] = (bits >>  8) & 0xff;
    136   vp8_frm_hdr[2] = (bits >> 16) & 0xff;
    137   // signature
    138   vp8_frm_hdr[3] = (VP8_SIGNATURE >> 16) & 0xff;
    139   vp8_frm_hdr[4] = (VP8_SIGNATURE >>  8) & 0xff;
    140   vp8_frm_hdr[5] = (VP8_SIGNATURE >>  0) & 0xff;
    141   // dimensions
    142   vp8_frm_hdr[6] = pic->width & 0xff;
    143   vp8_frm_hdr[7] = pic->width >> 8;
    144   vp8_frm_hdr[8] = pic->height & 0xff;
    145   vp8_frm_hdr[9] = pic->height >> 8;
    146 
    147   if (!pic->writer(vp8_frm_hdr, sizeof(vp8_frm_hdr), pic)) {
    148     return VP8_ENC_ERROR_BAD_WRITE;
    149   }
    150   return VP8_ENC_OK;
    151 }
    152 
    153 // WebP Headers.
    154 static int PutWebPHeaders(const VP8Encoder* const enc, size_t size0,
    155                           size_t vp8_size, size_t riff_size) {
    156   WebPPicture* const pic = enc->pic;
    157   WebPEncodingError err = VP8_ENC_OK;
    158 
    159   // RIFF header.
    160   err = PutRIFFHeader(enc, riff_size);
    161   if (err != VP8_ENC_OK) goto Error;
    162 
    163   // VP8X.
    164   if (IsVP8XNeeded(enc)) {
    165     err = PutVP8XHeader(enc);
    166     if (err != VP8_ENC_OK) goto Error;
    167   }
    168 
    169   // Alpha.
    170   if (enc->has_alpha) {
    171     err = PutAlphaChunk(enc);
    172     if (err != VP8_ENC_OK) goto Error;
    173   }
    174 
    175   // VP8 header.
    176   err = PutVP8Header(pic, vp8_size);
    177   if (err != VP8_ENC_OK) goto Error;
    178 
    179   // VP8 frame header.
    180   err = PutVP8FrameHeader(pic, enc->profile, size0);
    181   if (err != VP8_ENC_OK) goto Error;
    182 
    183   // All OK.
    184   return 1;
    185 
    186   // Error.
    187  Error:
    188   return WebPEncodingSetError(pic, err);
    189 }
    190 
    191 // Segmentation header
    192 static void PutSegmentHeader(VP8BitWriter* const bw,
    193                              const VP8Encoder* const enc) {
    194   const VP8EncSegmentHeader* const hdr = &enc->segment_hdr;
    195   const VP8EncProba* const proba = &enc->proba;
    196   if (VP8PutBitUniform(bw, (hdr->num_segments > 1))) {
    197     // We always 'update' the quant and filter strength values
    198     const int update_data = 1;
    199     int s;
    200     VP8PutBitUniform(bw, hdr->update_map);
    201     if (VP8PutBitUniform(bw, update_data)) {
    202       // we always use absolute values, not relative ones
    203       VP8PutBitUniform(bw, 1);   // (segment_feature_mode = 1. Paragraph 9.3.)
    204       for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
    205         VP8PutSignedBits(bw, enc->dqm[s].quant, 7);
    206       }
    207       for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
    208         VP8PutSignedBits(bw, enc->dqm[s].fstrength, 6);
    209       }
    210     }
    211     if (hdr->update_map) {
    212       for (s = 0; s < 3; ++s) {
    213         if (VP8PutBitUniform(bw, (proba->segments[s] != 255u))) {
    214           VP8PutBits(bw, proba->segments[s], 8);
    215         }
    216       }
    217     }
    218   }
    219 }
    220 
    221 // Filtering parameters header
    222 static void PutFilterHeader(VP8BitWriter* const bw,
    223                             const VP8EncFilterHeader* const hdr) {
    224   const int use_lf_delta = (hdr->i4x4_lf_delta != 0);
    225   VP8PutBitUniform(bw, hdr->simple);
    226   VP8PutBits(bw, hdr->level, 6);
    227   VP8PutBits(bw, hdr->sharpness, 3);
    228   if (VP8PutBitUniform(bw, use_lf_delta)) {
    229     // '0' is the default value for i4x4_lf_delta at frame #0.
    230     const int need_update = (hdr->i4x4_lf_delta != 0);
    231     if (VP8PutBitUniform(bw, need_update)) {
    232       // we don't use ref_lf_delta => emit four 0 bits
    233       VP8PutBits(bw, 0, 4);
    234       // we use mode_lf_delta for i4x4
    235       VP8PutSignedBits(bw, hdr->i4x4_lf_delta, 6);
    236       VP8PutBits(bw, 0, 3);    // all others unused
    237     }
    238   }
    239 }
    240 
    241 // Nominal quantization parameters
    242 static void PutQuant(VP8BitWriter* const bw,
    243                      const VP8Encoder* const enc) {
    244   VP8PutBits(bw, enc->base_quant, 7);
    245   VP8PutSignedBits(bw, enc->dq_y1_dc, 4);
    246   VP8PutSignedBits(bw, enc->dq_y2_dc, 4);
    247   VP8PutSignedBits(bw, enc->dq_y2_ac, 4);
    248   VP8PutSignedBits(bw, enc->dq_uv_dc, 4);
    249   VP8PutSignedBits(bw, enc->dq_uv_ac, 4);
    250 }
    251 
    252 // Partition sizes
    253 static int EmitPartitionsSize(const VP8Encoder* const enc,
    254                               WebPPicture* const pic) {
    255   uint8_t buf[3 * (MAX_NUM_PARTITIONS - 1)];
    256   int p;
    257   for (p = 0; p < enc->num_parts - 1; ++p) {
    258     const size_t part_size = VP8BitWriterSize(enc->parts + p);
    259     if (part_size >= VP8_MAX_PARTITION_SIZE) {
    260       return WebPEncodingSetError(pic, VP8_ENC_ERROR_PARTITION_OVERFLOW);
    261     }
    262     buf[3 * p + 0] = (part_size >>  0) & 0xff;
    263     buf[3 * p + 1] = (part_size >>  8) & 0xff;
    264     buf[3 * p + 2] = (part_size >> 16) & 0xff;
    265   }
    266   if (p && !pic->writer(buf, 3 * p, pic)) {
    267     return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_WRITE);
    268   }
    269   return 1;
    270 }
    271 
    272 //------------------------------------------------------------------------------
    273 
    274 static int GeneratePartition0(VP8Encoder* const enc) {
    275   VP8BitWriter* const bw = &enc->bw;
    276   const int mb_size = enc->mb_w * enc->mb_h;
    277   uint64_t pos1, pos2, pos3;
    278 
    279   pos1 = VP8BitWriterPos(bw);
    280   if (!VP8BitWriterInit(bw, mb_size * 7 / 8)) {        // ~7 bits per macroblock
    281     return WebPEncodingSetError(enc->pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
    282   }
    283   VP8PutBitUniform(bw, 0);   // colorspace
    284   VP8PutBitUniform(bw, 0);   // clamp type
    285 
    286   PutSegmentHeader(bw, enc);
    287   PutFilterHeader(bw, &enc->filter_hdr);
    288   VP8PutBits(bw, enc->num_parts == 8 ? 3 :
    289                  enc->num_parts == 4 ? 2 :
    290                  enc->num_parts == 2 ? 1 : 0, 2);
    291   PutQuant(bw, enc);
    292   VP8PutBitUniform(bw, 0);   // no proba update
    293   VP8WriteProbas(bw, &enc->proba);
    294   pos2 = VP8BitWriterPos(bw);
    295   VP8CodeIntraModes(enc);
    296   VP8BitWriterFinish(bw);
    297 
    298   pos3 = VP8BitWriterPos(bw);
    299 
    300 #if !defined(WEBP_DISABLE_STATS)
    301   if (enc->pic->stats) {
    302     enc->pic->stats->header_bytes[0] = (int)((pos2 - pos1 + 7) >> 3);
    303     enc->pic->stats->header_bytes[1] = (int)((pos3 - pos2 + 7) >> 3);
    304     enc->pic->stats->alpha_data_size = (int)enc->alpha_data_size;
    305   }
    306 #else
    307   (void)pos1;
    308   (void)pos2;
    309   (void)pos3;
    310 #endif
    311   if (bw->error) {
    312     return WebPEncodingSetError(enc->pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
    313   }
    314   return 1;
    315 }
    316 
    317 void VP8EncFreeBitWriters(VP8Encoder* const enc) {
    318   int p;
    319   VP8BitWriterWipeOut(&enc->bw);
    320   for (p = 0; p < enc->num_parts; ++p) {
    321     VP8BitWriterWipeOut(enc->parts + p);
    322   }
    323 }
    324 
    325 int VP8EncWrite(VP8Encoder* const enc) {
    326   WebPPicture* const pic = enc->pic;
    327   VP8BitWriter* const bw = &enc->bw;
    328   const int task_percent = 19;
    329   const int percent_per_part = task_percent / enc->num_parts;
    330   const int final_percent = enc->percent + task_percent;
    331   int ok = 0;
    332   size_t vp8_size, pad, riff_size;
    333   int p;
    334 
    335   // Partition #0 with header and partition sizes
    336   ok = GeneratePartition0(enc);
    337   if (!ok) return 0;
    338 
    339   // Compute VP8 size
    340   vp8_size = VP8_FRAME_HEADER_SIZE +
    341              VP8BitWriterSize(bw) +
    342              3 * (enc->num_parts - 1);
    343   for (p = 0; p < enc->num_parts; ++p) {
    344     vp8_size += VP8BitWriterSize(enc->parts + p);
    345   }
    346   pad = vp8_size & 1;
    347   vp8_size += pad;
    348 
    349   // Compute RIFF size
    350   // At the minimum it is: "WEBPVP8 nnnn" + VP8 data size.
    351   riff_size = TAG_SIZE + CHUNK_HEADER_SIZE + vp8_size;
    352   if (IsVP8XNeeded(enc)) {  // Add size for: VP8X header + data.
    353     riff_size += CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE;
    354   }
    355   if (enc->has_alpha) {  // Add size for: ALPH header + data.
    356     const uint32_t padded_alpha_size = enc->alpha_data_size +
    357                                        (enc->alpha_data_size & 1);
    358     riff_size += CHUNK_HEADER_SIZE + padded_alpha_size;
    359   }
    360   // RIFF size should fit in 32-bits.
    361   if (riff_size > 0xfffffffeU) {
    362     return WebPEncodingSetError(pic, VP8_ENC_ERROR_FILE_TOO_BIG);
    363   }
    364 
    365   // Emit headers and partition #0
    366   {
    367     const uint8_t* const part0 = VP8BitWriterBuf(bw);
    368     const size_t size0 = VP8BitWriterSize(bw);
    369     ok = ok && PutWebPHeaders(enc, size0, vp8_size, riff_size)
    370             && pic->writer(part0, size0, pic)
    371             && EmitPartitionsSize(enc, pic);
    372     VP8BitWriterWipeOut(bw);    // will free the internal buffer.
    373   }
    374 
    375   // Token partitions
    376   for (p = 0; p < enc->num_parts; ++p) {
    377     const uint8_t* const buf = VP8BitWriterBuf(enc->parts + p);
    378     const size_t size = VP8BitWriterSize(enc->parts + p);
    379     if (size) ok = ok && pic->writer(buf, size, pic);
    380     VP8BitWriterWipeOut(enc->parts + p);    // will free the internal buffer.
    381     ok = ok && WebPReportProgress(pic, enc->percent + percent_per_part,
    382                                   &enc->percent);
    383   }
    384 
    385   // Padding byte
    386   if (ok && pad) {
    387     ok = PutPaddingByte(pic);
    388   }
    389 
    390   enc->coded_size = (int)(CHUNK_HEADER_SIZE + riff_size);
    391   ok = ok && WebPReportProgress(pic, final_percent, &enc->percent);
    392   if (!ok) WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_WRITE);
    393   return ok;
    394 }
    395 
    396 //------------------------------------------------------------------------------