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