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