github.com/cellofellow/gopkg@v0.0.0-20140722061823-eec0544a62ad/image/webp/libwebp/src/dec/io.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  // functions for sample output.
    11  //
    12  // Author: Skal (pascal.massimino@gmail.com)
    13  
    14  #include <assert.h>
    15  #include <stdlib.h>
    16  #include "../dec/vp8i.h"
    17  #include "./webpi.h"
    18  #include "../dsp/dsp.h"
    19  #include "../dsp/yuv.h"
    20  
    21  //------------------------------------------------------------------------------
    22  // Main YUV<->RGB conversion functions
    23  
    24  static int EmitYUV(const VP8Io* const io, WebPDecParams* const p) {
    25    WebPDecBuffer* output = p->output;
    26    const WebPYUVABuffer* const buf = &output->u.YUVA;
    27    uint8_t* const y_dst = buf->y + io->mb_y * buf->y_stride;
    28    uint8_t* const u_dst = buf->u + (io->mb_y >> 1) * buf->u_stride;
    29    uint8_t* const v_dst = buf->v + (io->mb_y >> 1) * buf->v_stride;
    30    const int mb_w = io->mb_w;
    31    const int mb_h = io->mb_h;
    32    const int uv_w = (mb_w + 1) / 2;
    33    const int uv_h = (mb_h + 1) / 2;
    34    int j;
    35    for (j = 0; j < mb_h; ++j) {
    36      memcpy(y_dst + j * buf->y_stride, io->y + j * io->y_stride, mb_w);
    37    }
    38    for (j = 0; j < uv_h; ++j) {
    39      memcpy(u_dst + j * buf->u_stride, io->u + j * io->uv_stride, uv_w);
    40      memcpy(v_dst + j * buf->v_stride, io->v + j * io->uv_stride, uv_w);
    41    }
    42    return io->mb_h;
    43  }
    44  
    45  // Point-sampling U/V sampler.
    46  static int EmitSampledRGB(const VP8Io* const io, WebPDecParams* const p) {
    47    WebPDecBuffer* output = p->output;
    48    const WebPRGBABuffer* const buf = &output->u.RGBA;
    49    uint8_t* dst = buf->rgba + io->mb_y * buf->stride;
    50    const uint8_t* y_src = io->y;
    51    const uint8_t* u_src = io->u;
    52    const uint8_t* v_src = io->v;
    53    const WebPSampleLinePairFunc sample = WebPSamplers[output->colorspace];
    54    const int mb_w = io->mb_w;
    55    const int last = io->mb_h - 1;
    56    int j;
    57    for (j = 0; j < last; j += 2) {
    58      sample(y_src, y_src + io->y_stride, u_src, v_src,
    59             dst, dst + buf->stride, mb_w);
    60      y_src += 2 * io->y_stride;
    61      u_src += io->uv_stride;
    62      v_src += io->uv_stride;
    63      dst += 2 * buf->stride;
    64    }
    65    if (j == last) {  // Just do the last line twice
    66      sample(y_src, y_src, u_src, v_src, dst, dst, mb_w);
    67    }
    68    return io->mb_h;
    69  }
    70  
    71  //------------------------------------------------------------------------------
    72  // YUV444 -> RGB conversion
    73  
    74  #if 0   // TODO(skal): this is for future rescaling.
    75  static int EmitRGB(const VP8Io* const io, WebPDecParams* const p) {
    76    WebPDecBuffer* output = p->output;
    77    const WebPRGBABuffer* const buf = &output->u.RGBA;
    78    uint8_t* dst = buf->rgba + io->mb_y * buf->stride;
    79    const uint8_t* y_src = io->y;
    80    const uint8_t* u_src = io->u;
    81    const uint8_t* v_src = io->v;
    82    const WebPYUV444Converter convert = WebPYUV444Converters[output->colorspace];
    83    const int mb_w = io->mb_w;
    84    const int last = io->mb_h;
    85    int j;
    86    for (j = 0; j < last; ++j) {
    87      convert(y_src, u_src, v_src, dst, mb_w);
    88      y_src += io->y_stride;
    89      u_src += io->uv_stride;
    90      v_src += io->uv_stride;
    91      dst += buf->stride;
    92    }
    93    return io->mb_h;
    94  }
    95  #endif
    96  
    97  //------------------------------------------------------------------------------
    98  // Fancy upsampling
    99  
   100  #ifdef FANCY_UPSAMPLING
   101  static int EmitFancyRGB(const VP8Io* const io, WebPDecParams* const p) {
   102    int num_lines_out = io->mb_h;   // a priori guess
   103    const WebPRGBABuffer* const buf = &p->output->u.RGBA;
   104    uint8_t* dst = buf->rgba + io->mb_y * buf->stride;
   105    WebPUpsampleLinePairFunc upsample = WebPUpsamplers[p->output->colorspace];
   106    const uint8_t* cur_y = io->y;
   107    const uint8_t* cur_u = io->u;
   108    const uint8_t* cur_v = io->v;
   109    const uint8_t* top_u = p->tmp_u;
   110    const uint8_t* top_v = p->tmp_v;
   111    int y = io->mb_y;
   112    const int y_end = io->mb_y + io->mb_h;
   113    const int mb_w = io->mb_w;
   114    const int uv_w = (mb_w + 1) / 2;
   115  
   116    if (y == 0) {
   117      // First line is special cased. We mirror the u/v samples at boundary.
   118      upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, dst, NULL, mb_w);
   119    } else {
   120      // We can finish the left-over line from previous call.
   121      upsample(p->tmp_y, cur_y, top_u, top_v, cur_u, cur_v,
   122               dst - buf->stride, dst, mb_w);
   123      ++num_lines_out;
   124    }
   125    // Loop over each output pairs of row.
   126    for (; y + 2 < y_end; y += 2) {
   127      top_u = cur_u;
   128      top_v = cur_v;
   129      cur_u += io->uv_stride;
   130      cur_v += io->uv_stride;
   131      dst += 2 * buf->stride;
   132      cur_y += 2 * io->y_stride;
   133      upsample(cur_y - io->y_stride, cur_y,
   134               top_u, top_v, cur_u, cur_v,
   135               dst - buf->stride, dst, mb_w);
   136    }
   137    // move to last row
   138    cur_y += io->y_stride;
   139    if (io->crop_top + y_end < io->crop_bottom) {
   140      // Save the unfinished samples for next call (as we're not done yet).
   141      memcpy(p->tmp_y, cur_y, mb_w * sizeof(*p->tmp_y));
   142      memcpy(p->tmp_u, cur_u, uv_w * sizeof(*p->tmp_u));
   143      memcpy(p->tmp_v, cur_v, uv_w * sizeof(*p->tmp_v));
   144      // The fancy upsampler leaves a row unfinished behind
   145      // (except for the very last row)
   146      num_lines_out--;
   147    } else {
   148      // Process the very last row of even-sized picture
   149      if (!(y_end & 1)) {
   150        upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v,
   151                 dst + buf->stride, NULL, mb_w);
   152      }
   153    }
   154    return num_lines_out;
   155  }
   156  
   157  #endif    /* FANCY_UPSAMPLING */
   158  
   159  //------------------------------------------------------------------------------
   160  
   161  static int EmitAlphaYUV(const VP8Io* const io, WebPDecParams* const p) {
   162    const uint8_t* alpha = io->a;
   163    const WebPYUVABuffer* const buf = &p->output->u.YUVA;
   164    const int mb_w = io->mb_w;
   165    const int mb_h = io->mb_h;
   166    uint8_t* dst = buf->a + io->mb_y * buf->a_stride;
   167    int j;
   168  
   169    if (alpha != NULL) {
   170      for (j = 0; j < mb_h; ++j) {
   171        memcpy(dst, alpha, mb_w * sizeof(*dst));
   172        alpha += io->width;
   173        dst += buf->a_stride;
   174      }
   175    } else if (buf->a != NULL) {
   176      // the user requested alpha, but there is none, set it to opaque.
   177      for (j = 0; j < mb_h; ++j) {
   178        memset(dst, 0xff, mb_w * sizeof(*dst));
   179        dst += buf->a_stride;
   180      }
   181    }
   182    return 0;
   183  }
   184  
   185  static int GetAlphaSourceRow(const VP8Io* const io,
   186                               const uint8_t** alpha, int* const num_rows) {
   187    int start_y = io->mb_y;
   188    *num_rows = io->mb_h;
   189  
   190    // Compensate for the 1-line delay of the fancy upscaler.
   191    // This is similar to EmitFancyRGB().
   192    if (io->fancy_upsampling) {
   193      if (start_y == 0) {
   194        // We don't process the last row yet. It'll be done during the next call.
   195        --*num_rows;
   196      } else {
   197        --start_y;
   198        // Fortunately, *alpha data is persistent, so we can go back
   199        // one row and finish alpha blending, now that the fancy upscaler
   200        // completed the YUV->RGB interpolation.
   201        *alpha -= io->width;
   202      }
   203      if (io->crop_top + io->mb_y + io->mb_h == io->crop_bottom) {
   204        // If it's the very last call, we process all the remaining rows!
   205        *num_rows = io->crop_bottom - io->crop_top - start_y;
   206      }
   207    }
   208    return start_y;
   209  }
   210  
   211  static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p) {
   212    const uint8_t* alpha = io->a;
   213    if (alpha != NULL) {
   214      const int mb_w = io->mb_w;
   215      const WEBP_CSP_MODE colorspace = p->output->colorspace;
   216      const int alpha_first =
   217          (colorspace == MODE_ARGB || colorspace == MODE_Argb);
   218      const WebPRGBABuffer* const buf = &p->output->u.RGBA;
   219      int num_rows;
   220      const int start_y = GetAlphaSourceRow(io, &alpha, &num_rows);
   221      uint8_t* const base_rgba = buf->rgba + start_y * buf->stride;
   222      uint8_t* dst = base_rgba + (alpha_first ? 0 : 3);
   223      uint32_t alpha_mask = 0xff;
   224      int i, j;
   225  
   226      for (j = 0; j < num_rows; ++j) {
   227        for (i = 0; i < mb_w; ++i) {
   228          const uint32_t alpha_value = alpha[i];
   229          dst[4 * i] = alpha_value;
   230          alpha_mask &= alpha_value;
   231        }
   232        alpha += io->width;
   233        dst += buf->stride;
   234      }
   235      // alpha_mask is < 0xff if there's non-trivial alpha to premultiply with.
   236      if (alpha_mask != 0xff && WebPIsPremultipliedMode(colorspace)) {
   237        WebPApplyAlphaMultiply(base_rgba, alpha_first,
   238                               mb_w, num_rows, buf->stride);
   239      }
   240    }
   241    return 0;
   242  }
   243  
   244  static int EmitAlphaRGBA4444(const VP8Io* const io, WebPDecParams* const p) {
   245    const uint8_t* alpha = io->a;
   246    if (alpha != NULL) {
   247      const int mb_w = io->mb_w;
   248      const WEBP_CSP_MODE colorspace = p->output->colorspace;
   249      const WebPRGBABuffer* const buf = &p->output->u.RGBA;
   250      int num_rows;
   251      const int start_y = GetAlphaSourceRow(io, &alpha, &num_rows);
   252      uint8_t* const base_rgba = buf->rgba + start_y * buf->stride;
   253      uint8_t* alpha_dst = base_rgba + 1;
   254      uint32_t alpha_mask = 0x0f;
   255      int i, j;
   256  
   257      for (j = 0; j < num_rows; ++j) {
   258        for (i = 0; i < mb_w; ++i) {
   259          // Fill in the alpha value (converted to 4 bits).
   260          const uint32_t alpha_value = alpha[i] >> 4;
   261          alpha_dst[2 * i] = (alpha_dst[2 * i] & 0xf0) | alpha_value;
   262          alpha_mask &= alpha_value;
   263        }
   264        alpha += io->width;
   265        alpha_dst += buf->stride;
   266      }
   267      if (alpha_mask != 0x0f && WebPIsPremultipliedMode(colorspace)) {
   268        WebPApplyAlphaMultiply4444(base_rgba, mb_w, num_rows, buf->stride);
   269      }
   270    }
   271    return 0;
   272  }
   273  
   274  //------------------------------------------------------------------------------
   275  // YUV rescaling (no final RGB conversion needed)
   276  
   277  static int Rescale(const uint8_t* src, int src_stride,
   278                     int new_lines, WebPRescaler* const wrk) {
   279    int num_lines_out = 0;
   280    while (new_lines > 0) {    // import new contributions of source rows.
   281      const int lines_in = WebPRescalerImport(wrk, new_lines, src, src_stride);
   282      src += lines_in * src_stride;
   283      new_lines -= lines_in;
   284      num_lines_out += WebPRescalerExport(wrk);    // emit output row(s)
   285    }
   286    return num_lines_out;
   287  }
   288  
   289  static int EmitRescaledYUV(const VP8Io* const io, WebPDecParams* const p) {
   290    const int mb_h = io->mb_h;
   291    const int uv_mb_h = (mb_h + 1) >> 1;
   292    const int num_lines_out = Rescale(io->y, io->y_stride, mb_h, &p->scaler_y);
   293    Rescale(io->u, io->uv_stride, uv_mb_h, &p->scaler_u);
   294    Rescale(io->v, io->uv_stride, uv_mb_h, &p->scaler_v);
   295    return num_lines_out;
   296  }
   297  
   298  static int EmitRescaledAlphaYUV(const VP8Io* const io, WebPDecParams* const p) {
   299    if (io->a != NULL) {
   300      Rescale(io->a, io->width, io->mb_h, &p->scaler_a);
   301    }
   302    return 0;
   303  }
   304  
   305  static int InitYUVRescaler(const VP8Io* const io, WebPDecParams* const p) {
   306    const int has_alpha = WebPIsAlphaMode(p->output->colorspace);
   307    const WebPYUVABuffer* const buf = &p->output->u.YUVA;
   308    const int out_width  = io->scaled_width;
   309    const int out_height = io->scaled_height;
   310    const int uv_out_width  = (out_width + 1) >> 1;
   311    const int uv_out_height = (out_height + 1) >> 1;
   312    const int uv_in_width  = (io->mb_w + 1) >> 1;
   313    const int uv_in_height = (io->mb_h + 1) >> 1;
   314    const size_t work_size = 2 * out_width;   // scratch memory for luma rescaler
   315    const size_t uv_work_size = 2 * uv_out_width;  // and for each u/v ones
   316    size_t tmp_size;
   317    int32_t* work;
   318  
   319    tmp_size = work_size + 2 * uv_work_size;
   320    if (has_alpha) {
   321      tmp_size += work_size;
   322    }
   323    p->memory = calloc(1, tmp_size * sizeof(*work));
   324    if (p->memory == NULL) {
   325      return 0;   // memory error
   326    }
   327    work = (int32_t*)p->memory;
   328    WebPRescalerInit(&p->scaler_y, io->mb_w, io->mb_h,
   329                     buf->y, out_width, out_height, buf->y_stride, 1,
   330                     io->mb_w, out_width, io->mb_h, out_height,
   331                     work);
   332    WebPRescalerInit(&p->scaler_u, uv_in_width, uv_in_height,
   333                     buf->u, uv_out_width, uv_out_height, buf->u_stride, 1,
   334                     uv_in_width, uv_out_width,
   335                     uv_in_height, uv_out_height,
   336                     work + work_size);
   337    WebPRescalerInit(&p->scaler_v, uv_in_width, uv_in_height,
   338                     buf->v, uv_out_width, uv_out_height, buf->v_stride, 1,
   339                     uv_in_width, uv_out_width,
   340                     uv_in_height, uv_out_height,
   341                     work + work_size + uv_work_size);
   342    p->emit = EmitRescaledYUV;
   343  
   344    if (has_alpha) {
   345      WebPRescalerInit(&p->scaler_a, io->mb_w, io->mb_h,
   346                       buf->a, out_width, out_height, buf->a_stride, 1,
   347                       io->mb_w, out_width, io->mb_h, out_height,
   348                       work + work_size + 2 * uv_work_size);
   349      p->emit_alpha = EmitRescaledAlphaYUV;
   350    }
   351    return 1;
   352  }
   353  
   354  //------------------------------------------------------------------------------
   355  // RGBA rescaling
   356  
   357  static int ExportRGB(WebPDecParams* const p, int y_pos) {
   358    const WebPYUV444Converter convert =
   359        WebPYUV444Converters[p->output->colorspace];
   360    const WebPRGBABuffer* const buf = &p->output->u.RGBA;
   361    uint8_t* dst = buf->rgba + (p->last_y + y_pos) * buf->stride;
   362    int num_lines_out = 0;
   363    // For RGB rescaling, because of the YUV420, current scan position
   364    // U/V can be +1/-1 line from the Y one.  Hence the double test.
   365    while (WebPRescalerHasPendingOutput(&p->scaler_y) &&
   366           WebPRescalerHasPendingOutput(&p->scaler_u)) {
   367      assert(p->last_y + y_pos + num_lines_out < p->output->height);
   368      assert(p->scaler_u.y_accum == p->scaler_v.y_accum);
   369      WebPRescalerExportRow(&p->scaler_y);
   370      WebPRescalerExportRow(&p->scaler_u);
   371      WebPRescalerExportRow(&p->scaler_v);
   372      convert(p->scaler_y.dst, p->scaler_u.dst, p->scaler_v.dst,
   373              dst, p->scaler_y.dst_width);
   374      dst += buf->stride;
   375      ++num_lines_out;
   376    }
   377    return num_lines_out;
   378  }
   379  
   380  static int EmitRescaledRGB(const VP8Io* const io, WebPDecParams* const p) {
   381    const int mb_h = io->mb_h;
   382    const int uv_mb_h = (mb_h + 1) >> 1;
   383    int j = 0, uv_j = 0;
   384    int num_lines_out = 0;
   385    while (j < mb_h) {
   386      const int y_lines_in =
   387          WebPRescalerImport(&p->scaler_y, mb_h - j,
   388                             io->y + j * io->y_stride, io->y_stride);
   389      const int u_lines_in =
   390          WebPRescalerImport(&p->scaler_u, uv_mb_h - uv_j,
   391                             io->u + uv_j * io->uv_stride, io->uv_stride);
   392      const int v_lines_in =
   393          WebPRescalerImport(&p->scaler_v, uv_mb_h - uv_j,
   394                             io->v + uv_j * io->uv_stride, io->uv_stride);
   395      (void)v_lines_in;   // remove a gcc warning
   396      assert(u_lines_in == v_lines_in);
   397      j += y_lines_in;
   398      uv_j += u_lines_in;
   399      num_lines_out += ExportRGB(p, num_lines_out);
   400    }
   401    return num_lines_out;
   402  }
   403  
   404  static int ExportAlpha(WebPDecParams* const p, int y_pos) {
   405    const WebPRGBABuffer* const buf = &p->output->u.RGBA;
   406    uint8_t* const base_rgba = buf->rgba + (p->last_y + y_pos) * buf->stride;
   407    const WEBP_CSP_MODE colorspace = p->output->colorspace;
   408    const int alpha_first =
   409        (colorspace == MODE_ARGB || colorspace == MODE_Argb);
   410    uint8_t* dst = base_rgba + (alpha_first ? 0 : 3);
   411    int num_lines_out = 0;
   412    const int is_premult_alpha = WebPIsPremultipliedMode(colorspace);
   413    uint32_t alpha_mask = 0xff;
   414    const int width = p->scaler_a.dst_width;
   415  
   416    while (WebPRescalerHasPendingOutput(&p->scaler_a)) {
   417      int i;
   418      assert(p->last_y + y_pos + num_lines_out < p->output->height);
   419      WebPRescalerExportRow(&p->scaler_a);
   420      for (i = 0; i < width; ++i) {
   421        const uint32_t alpha_value = p->scaler_a.dst[i];
   422        dst[4 * i] = alpha_value;
   423        alpha_mask &= alpha_value;
   424      }
   425      dst += buf->stride;
   426      ++num_lines_out;
   427    }
   428    if (is_premult_alpha && alpha_mask != 0xff) {
   429      WebPApplyAlphaMultiply(base_rgba, alpha_first,
   430                             width, num_lines_out, buf->stride);
   431    }
   432    return num_lines_out;
   433  }
   434  
   435  static int ExportAlphaRGBA4444(WebPDecParams* const p, int y_pos) {
   436    const WebPRGBABuffer* const buf = &p->output->u.RGBA;
   437    uint8_t* const base_rgba = buf->rgba + (p->last_y + y_pos) * buf->stride;
   438    uint8_t* alpha_dst = base_rgba + 1;
   439    int num_lines_out = 0;
   440    const WEBP_CSP_MODE colorspace = p->output->colorspace;
   441    const int width = p->scaler_a.dst_width;
   442    const int is_premult_alpha = WebPIsPremultipliedMode(colorspace);
   443    uint32_t alpha_mask = 0x0f;
   444  
   445    while (WebPRescalerHasPendingOutput(&p->scaler_a)) {
   446      int i;
   447      assert(p->last_y + y_pos + num_lines_out < p->output->height);
   448      WebPRescalerExportRow(&p->scaler_a);
   449      for (i = 0; i < width; ++i) {
   450        // Fill in the alpha value (converted to 4 bits).
   451        const uint32_t alpha_value = p->scaler_a.dst[i] >> 4;
   452        alpha_dst[2 * i] = (alpha_dst[2 * i] & 0xf0) | alpha_value;
   453        alpha_mask &= alpha_value;
   454      }
   455      alpha_dst += buf->stride;
   456      ++num_lines_out;
   457    }
   458    if (is_premult_alpha && alpha_mask != 0x0f) {
   459      WebPApplyAlphaMultiply4444(base_rgba, width, num_lines_out, buf->stride);
   460    }
   461    return num_lines_out;
   462  }
   463  
   464  static int EmitRescaledAlphaRGB(const VP8Io* const io, WebPDecParams* const p) {
   465    if (io->a != NULL) {
   466      WebPRescaler* const scaler = &p->scaler_a;
   467      int j = 0;
   468      int pos = 0;
   469      while (j < io->mb_h) {
   470        j += WebPRescalerImport(scaler, io->mb_h - j,
   471                                io->a + j * io->width, io->width);
   472        pos += p->emit_alpha_row(p, pos);
   473      }
   474    }
   475    return 0;
   476  }
   477  
   478  static int InitRGBRescaler(const VP8Io* const io, WebPDecParams* const p) {
   479    const int has_alpha = WebPIsAlphaMode(p->output->colorspace);
   480    const int out_width  = io->scaled_width;
   481    const int out_height = io->scaled_height;
   482    const int uv_in_width  = (io->mb_w + 1) >> 1;
   483    const int uv_in_height = (io->mb_h + 1) >> 1;
   484    const size_t work_size = 2 * out_width;   // scratch memory for one rescaler
   485    int32_t* work;  // rescalers work area
   486    uint8_t* tmp;   // tmp storage for scaled YUV444 samples before RGB conversion
   487    size_t tmp_size1, tmp_size2;
   488  
   489    tmp_size1 = 3 * work_size;
   490    tmp_size2 = 3 * out_width;
   491    if (has_alpha) {
   492      tmp_size1 += work_size;
   493      tmp_size2 += out_width;
   494    }
   495    p->memory = calloc(1, tmp_size1 * sizeof(*work) + tmp_size2 * sizeof(*tmp));
   496    if (p->memory == NULL) {
   497      return 0;   // memory error
   498    }
   499    work = (int32_t*)p->memory;
   500    tmp = (uint8_t*)(work + tmp_size1);
   501    WebPRescalerInit(&p->scaler_y, io->mb_w, io->mb_h,
   502                     tmp + 0 * out_width, out_width, out_height, 0, 1,
   503                     io->mb_w, out_width, io->mb_h, out_height,
   504                     work + 0 * work_size);
   505    WebPRescalerInit(&p->scaler_u, uv_in_width, uv_in_height,
   506                     tmp + 1 * out_width, out_width, out_height, 0, 1,
   507                     io->mb_w, 2 * out_width, io->mb_h, 2 * out_height,
   508                     work + 1 * work_size);
   509    WebPRescalerInit(&p->scaler_v, uv_in_width, uv_in_height,
   510                     tmp + 2 * out_width, out_width, out_height, 0, 1,
   511                     io->mb_w, 2 * out_width, io->mb_h, 2 * out_height,
   512                     work + 2 * work_size);
   513    p->emit = EmitRescaledRGB;
   514  
   515    if (has_alpha) {
   516      WebPRescalerInit(&p->scaler_a, io->mb_w, io->mb_h,
   517                       tmp + 3 * out_width, out_width, out_height, 0, 1,
   518                       io->mb_w, out_width, io->mb_h, out_height,
   519                       work + 3 * work_size);
   520      p->emit_alpha = EmitRescaledAlphaRGB;
   521      if (p->output->colorspace == MODE_RGBA_4444 ||
   522          p->output->colorspace == MODE_rgbA_4444) {
   523        p->emit_alpha_row = ExportAlphaRGBA4444;
   524      } else {
   525        p->emit_alpha_row = ExportAlpha;
   526      }
   527    }
   528    return 1;
   529  }
   530  
   531  //------------------------------------------------------------------------------
   532  // Default custom functions
   533  
   534  static int CustomSetup(VP8Io* io) {
   535    WebPDecParams* const p = (WebPDecParams*)io->opaque;
   536    const WEBP_CSP_MODE colorspace = p->output->colorspace;
   537    const int is_rgb = WebPIsRGBMode(colorspace);
   538    const int is_alpha = WebPIsAlphaMode(colorspace);
   539  
   540    p->memory = NULL;
   541    p->emit = NULL;
   542    p->emit_alpha = NULL;
   543    p->emit_alpha_row = NULL;
   544    if (!WebPIoInitFromOptions(p->options, io, is_alpha ? MODE_YUV : MODE_YUVA)) {
   545      return 0;
   546    }
   547  
   548    if (io->use_scaling) {
   549      const int ok = is_rgb ? InitRGBRescaler(io, p) : InitYUVRescaler(io, p);
   550      if (!ok) {
   551        return 0;    // memory error
   552      }
   553    } else {
   554      if (is_rgb) {
   555        p->emit = EmitSampledRGB;   // default
   556  #ifdef FANCY_UPSAMPLING
   557        if (io->fancy_upsampling) {
   558          const int uv_width = (io->mb_w + 1) >> 1;
   559          p->memory = malloc(io->mb_w + 2 * uv_width);
   560          if (p->memory == NULL) {
   561            return 0;   // memory error.
   562          }
   563          p->tmp_y = (uint8_t*)p->memory;
   564          p->tmp_u = p->tmp_y + io->mb_w;
   565          p->tmp_v = p->tmp_u + uv_width;
   566          p->emit = EmitFancyRGB;
   567          WebPInitUpsamplers();
   568        }
   569  #endif
   570      } else {
   571        p->emit = EmitYUV;
   572      }
   573      if (is_alpha) {  // need transparency output
   574        if (WebPIsPremultipliedMode(colorspace)) WebPInitPremultiply();
   575        p->emit_alpha =
   576            (colorspace == MODE_RGBA_4444 || colorspace == MODE_rgbA_4444) ?
   577                EmitAlphaRGBA4444
   578            : is_rgb ? EmitAlphaRGB
   579            : EmitAlphaYUV;
   580      }
   581    }
   582  
   583    if (is_rgb) {
   584      VP8YUVInit();
   585    }
   586    return 1;
   587  }
   588  
   589  //------------------------------------------------------------------------------
   590  
   591  static int CustomPut(const VP8Io* io) {
   592    WebPDecParams* const p = (WebPDecParams*)io->opaque;
   593    const int mb_w = io->mb_w;
   594    const int mb_h = io->mb_h;
   595    int num_lines_out;
   596    assert(!(io->mb_y & 1));
   597  
   598    if (mb_w <= 0 || mb_h <= 0) {
   599      return 0;
   600    }
   601    num_lines_out = p->emit(io, p);
   602    if (p->emit_alpha != NULL) {
   603      p->emit_alpha(io, p);
   604    }
   605    p->last_y += num_lines_out;
   606    return 1;
   607  }
   608  
   609  //------------------------------------------------------------------------------
   610  
   611  static void CustomTeardown(const VP8Io* io) {
   612    WebPDecParams* const p = (WebPDecParams*)io->opaque;
   613    free(p->memory);
   614    p->memory = NULL;
   615  }
   616  
   617  //------------------------------------------------------------------------------
   618  // Main entry point
   619  
   620  void WebPInitCustomIo(WebPDecParams* const params, VP8Io* const io) {
   621    io->put      = CustomPut;
   622    io->setup    = CustomSetup;
   623    io->teardown = CustomTeardown;
   624    io->opaque   = params;
   625  }
   626  
   627  //------------------------------------------------------------------------------
   628