1793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler// Copyright 2011 Google Inc. All Rights Reserved.
2793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler//
3793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler// Use of this source code is governed by a BSD-style license
4793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler// that can be found in the COPYING file in the root of the source
5793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler// tree. An additional intellectual property rights grant can be found
6793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler// in the file PATENTS. All contributing project authors may
7793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler// be found in the AUTHORS file in the root of the source tree.
8793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler// -----------------------------------------------------------------------------
9793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler//
10793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler// functions for sample output.
11793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler//
12793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler// Author: Skal (pascal.massimino@gmail.com)
13793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
14793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler#include <assert.h>
15793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler#include <stdlib.h>
16793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler#include "../dec/vp8i.h"
17793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler#include "./webpi.h"
18793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler#include "../dsp/dsp.h"
19793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler#include "../dsp/yuv.h"
20793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
21793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler#if defined(__cplusplus) || defined(c_plusplus)
22793ee12c6df9cad3806238d32528c49a3ff9331dNoah Preslerextern "C" {
23793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler#endif
24793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
25793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler//------------------------------------------------------------------------------
26793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler// Main YUV<->RGB conversion functions
27793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
28793ee12c6df9cad3806238d32528c49a3ff9331dNoah Preslerstatic int EmitYUV(const VP8Io* const io, WebPDecParams* const p) {
29793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  WebPDecBuffer* output = p->output;
30793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const WebPYUVABuffer* const buf = &output->u.YUVA;
31793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  uint8_t* const y_dst = buf->y + io->mb_y * buf->y_stride;
32793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  uint8_t* const u_dst = buf->u + (io->mb_y >> 1) * buf->u_stride;
33793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  uint8_t* const v_dst = buf->v + (io->mb_y >> 1) * buf->v_stride;
34793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const int mb_w = io->mb_w;
35793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const int mb_h = io->mb_h;
36793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const int uv_w = (mb_w + 1) / 2;
37793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const int uv_h = (mb_h + 1) / 2;
38793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  int j;
39793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  for (j = 0; j < mb_h; ++j) {
40793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    memcpy(y_dst + j * buf->y_stride, io->y + j * io->y_stride, mb_w);
41793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  }
42793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  for (j = 0; j < uv_h; ++j) {
43793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    memcpy(u_dst + j * buf->u_stride, io->u + j * io->uv_stride, uv_w);
44793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    memcpy(v_dst + j * buf->v_stride, io->v + j * io->uv_stride, uv_w);
45793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  }
46793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  return io->mb_h;
47793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler}
48793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
49793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler// Point-sampling U/V sampler.
50793ee12c6df9cad3806238d32528c49a3ff9331dNoah Preslerstatic int EmitSampledRGB(const VP8Io* const io, WebPDecParams* const p) {
51793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  WebPDecBuffer* output = p->output;
52793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const WebPRGBABuffer* const buf = &output->u.RGBA;
53793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  uint8_t* dst = buf->rgba + io->mb_y * buf->stride;
54793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const uint8_t* y_src = io->y;
55793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const uint8_t* u_src = io->u;
56793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const uint8_t* v_src = io->v;
57793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const WebPSampleLinePairFunc sample = WebPSamplers[output->colorspace];
58793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const int mb_w = io->mb_w;
59793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const int last = io->mb_h - 1;
60793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  int j;
61793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  for (j = 0; j < last; j += 2) {
62793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    sample(y_src, y_src + io->y_stride, u_src, v_src,
63793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler           dst, dst + buf->stride, mb_w);
64793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    y_src += 2 * io->y_stride;
65793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    u_src += io->uv_stride;
66793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    v_src += io->uv_stride;
67793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    dst += 2 * buf->stride;
68793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  }
69793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  if (j == last) {  // Just do the last line twice
70793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    sample(y_src, y_src, u_src, v_src, dst, dst, mb_w);
71793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  }
72793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  return io->mb_h;
73793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler}
74793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
75793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler//------------------------------------------------------------------------------
76793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler// YUV444 -> RGB conversion
77793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
78793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler#if 0   // TODO(skal): this is for future rescaling.
79793ee12c6df9cad3806238d32528c49a3ff9331dNoah Preslerstatic int EmitRGB(const VP8Io* const io, WebPDecParams* const p) {
80793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  WebPDecBuffer* output = p->output;
81793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const WebPRGBABuffer* const buf = &output->u.RGBA;
82793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  uint8_t* dst = buf->rgba + io->mb_y * buf->stride;
83793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const uint8_t* y_src = io->y;
84793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const uint8_t* u_src = io->u;
85793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const uint8_t* v_src = io->v;
86793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const WebPYUV444Converter convert = WebPYUV444Converters[output->colorspace];
87793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const int mb_w = io->mb_w;
88793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const int last = io->mb_h;
89793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  int j;
90793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  for (j = 0; j < last; ++j) {
91793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    convert(y_src, u_src, v_src, dst, mb_w);
92793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    y_src += io->y_stride;
93793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    u_src += io->uv_stride;
94793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    v_src += io->uv_stride;
95793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    dst += buf->stride;
96793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  }
97793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  return io->mb_h;
98793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler}
99793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler#endif
100793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
101793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler//------------------------------------------------------------------------------
102793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler// Fancy upsampling
103793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
104793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler#ifdef FANCY_UPSAMPLING
105793ee12c6df9cad3806238d32528c49a3ff9331dNoah Preslerstatic int EmitFancyRGB(const VP8Io* const io, WebPDecParams* const p) {
106793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  int num_lines_out = io->mb_h;   // a priori guess
107793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const WebPRGBABuffer* const buf = &p->output->u.RGBA;
108793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  uint8_t* dst = buf->rgba + io->mb_y * buf->stride;
109793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  WebPUpsampleLinePairFunc upsample = WebPUpsamplers[p->output->colorspace];
110793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const uint8_t* cur_y = io->y;
111793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const uint8_t* cur_u = io->u;
112793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const uint8_t* cur_v = io->v;
113793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const uint8_t* top_u = p->tmp_u;
114793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const uint8_t* top_v = p->tmp_v;
115793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  int y = io->mb_y;
116793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const int y_end = io->mb_y + io->mb_h;
117793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const int mb_w = io->mb_w;
118793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const int uv_w = (mb_w + 1) / 2;
119793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
120793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  if (y == 0) {
121793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    // First line is special cased. We mirror the u/v samples at boundary.
122793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    upsample(NULL, cur_y, cur_u, cur_v, cur_u, cur_v, NULL, dst, mb_w);
123793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  } else {
124793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    // We can finish the left-over line from previous call.
125793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    upsample(p->tmp_y, cur_y, top_u, top_v, cur_u, cur_v,
126793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler             dst - buf->stride, dst, mb_w);
127793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    ++num_lines_out;
128793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  }
129793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  // Loop over each output pairs of row.
130793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  for (; y + 2 < y_end; y += 2) {
131793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    top_u = cur_u;
132793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    top_v = cur_v;
133793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    cur_u += io->uv_stride;
134793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    cur_v += io->uv_stride;
135793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    dst += 2 * buf->stride;
136793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    cur_y += 2 * io->y_stride;
137793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    upsample(cur_y - io->y_stride, cur_y,
138793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler             top_u, top_v, cur_u, cur_v,
139793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler             dst - buf->stride, dst, mb_w);
140793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  }
141793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  // move to last row
142793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  cur_y += io->y_stride;
143793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  if (io->crop_top + y_end < io->crop_bottom) {
144793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    // Save the unfinished samples for next call (as we're not done yet).
145793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    memcpy(p->tmp_y, cur_y, mb_w * sizeof(*p->tmp_y));
146793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    memcpy(p->tmp_u, cur_u, uv_w * sizeof(*p->tmp_u));
147793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    memcpy(p->tmp_v, cur_v, uv_w * sizeof(*p->tmp_v));
148793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    // The fancy upsampler leaves a row unfinished behind
149793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    // (except for the very last row)
150793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    num_lines_out--;
151793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  } else {
152793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    // Process the very last row of even-sized picture
153793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    if (!(y_end & 1)) {
154793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v,
155793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler               dst + buf->stride, NULL, mb_w);
156793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    }
157793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  }
158793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  return num_lines_out;
159793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler}
160793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
161793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler#endif    /* FANCY_UPSAMPLING */
162793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
163793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler//------------------------------------------------------------------------------
164793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
165793ee12c6df9cad3806238d32528c49a3ff9331dNoah Preslerstatic int EmitAlphaYUV(const VP8Io* const io, WebPDecParams* const p) {
166793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const uint8_t* alpha = io->a;
167793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const WebPYUVABuffer* const buf = &p->output->u.YUVA;
168793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const int mb_w = io->mb_w;
169793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const int mb_h = io->mb_h;
170793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  uint8_t* dst = buf->a + io->mb_y * buf->a_stride;
171793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  int j;
172793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
173793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  if (alpha != NULL) {
174793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    for (j = 0; j < mb_h; ++j) {
175793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      memcpy(dst, alpha, mb_w * sizeof(*dst));
176793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      alpha += io->width;
177793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      dst += buf->a_stride;
178793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    }
179793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  } else if (buf->a != NULL) {
180793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    // the user requested alpha, but there is none, set it to opaque.
181793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    for (j = 0; j < mb_h; ++j) {
182793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      memset(dst, 0xff, mb_w * sizeof(*dst));
183793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      dst += buf->a_stride;
184793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    }
185793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  }
186793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  return 0;
187793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler}
188793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
189793ee12c6df9cad3806238d32528c49a3ff9331dNoah Preslerstatic int GetAlphaSourceRow(const VP8Io* const io,
190793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler                             const uint8_t** alpha, int* const num_rows) {
191793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  int start_y = io->mb_y;
192793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  *num_rows = io->mb_h;
193793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
194793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  // Compensate for the 1-line delay of the fancy upscaler.
195793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  // This is similar to EmitFancyRGB().
196793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  if (io->fancy_upsampling) {
197793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    if (start_y == 0) {
198793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      // We don't process the last row yet. It'll be done during the next call.
199793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      --*num_rows;
200793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    } else {
201793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      --start_y;
202793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      // Fortunately, *alpha data is persistent, so we can go back
203793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      // one row and finish alpha blending, now that the fancy upscaler
204793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      // completed the YUV->RGB interpolation.
205793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      *alpha -= io->width;
206793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    }
207793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    if (io->crop_top + io->mb_y + io->mb_h == io->crop_bottom) {
208793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      // If it's the very last call, we process all the remaining rows!
209793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      *num_rows = io->crop_bottom - io->crop_top - start_y;
210793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    }
211793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  }
212793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  return start_y;
213793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler}
214793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
215793ee12c6df9cad3806238d32528c49a3ff9331dNoah Preslerstatic int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p) {
216793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const uint8_t* alpha = io->a;
217793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  if (alpha != NULL) {
218793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    const int mb_w = io->mb_w;
219793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    const WEBP_CSP_MODE colorspace = p->output->colorspace;
220793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    const int alpha_first =
221793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler        (colorspace == MODE_ARGB || colorspace == MODE_Argb);
222793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    const WebPRGBABuffer* const buf = &p->output->u.RGBA;
223793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    int num_rows;
224793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    const int start_y = GetAlphaSourceRow(io, &alpha, &num_rows);
225793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    uint8_t* const base_rgba = buf->rgba + start_y * buf->stride;
226793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    uint8_t* dst = base_rgba + (alpha_first ? 0 : 3);
227793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    uint32_t alpha_mask = 0xff;
228793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    int i, j;
229793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
230793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    for (j = 0; j < num_rows; ++j) {
231793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      for (i = 0; i < mb_w; ++i) {
232793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler        const uint32_t alpha_value = alpha[i];
233793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler        dst[4 * i] = alpha_value;
234793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler        alpha_mask &= alpha_value;
235793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      }
236793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      alpha += io->width;
237793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      dst += buf->stride;
238793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    }
239793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    // alpha_mask is < 0xff if there's non-trivial alpha to premultiply with.
240793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    if (alpha_mask != 0xff && WebPIsPremultipliedMode(colorspace)) {
241793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      WebPApplyAlphaMultiply(base_rgba, alpha_first,
242793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler                             mb_w, num_rows, buf->stride);
243793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    }
244793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  }
245793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  return 0;
246793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler}
247793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
248793ee12c6df9cad3806238d32528c49a3ff9331dNoah Preslerstatic int EmitAlphaRGBA4444(const VP8Io* const io, WebPDecParams* const p) {
249793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const uint8_t* alpha = io->a;
250793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  if (alpha != NULL) {
251793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    const int mb_w = io->mb_w;
252793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    const WEBP_CSP_MODE colorspace = p->output->colorspace;
253793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    const WebPRGBABuffer* const buf = &p->output->u.RGBA;
254793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    int num_rows;
255793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    const int start_y = GetAlphaSourceRow(io, &alpha, &num_rows);
256793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    uint8_t* const base_rgba = buf->rgba + start_y * buf->stride;
257793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    uint8_t* alpha_dst = base_rgba + 1;
258793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    uint32_t alpha_mask = 0x0f;
259793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    int i, j;
260793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
261793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    for (j = 0; j < num_rows; ++j) {
262793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      for (i = 0; i < mb_w; ++i) {
263793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler        // Fill in the alpha value (converted to 4 bits).
264793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler        const uint32_t alpha_value = alpha[i] >> 4;
265793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler        alpha_dst[2 * i] = (alpha_dst[2 * i] & 0xf0) | alpha_value;
266793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler        alpha_mask &= alpha_value;
267793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      }
268793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      alpha += io->width;
269793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      alpha_dst += buf->stride;
270793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    }
271793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    if (alpha_mask != 0x0f && WebPIsPremultipliedMode(colorspace)) {
272793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      WebPApplyAlphaMultiply4444(base_rgba, mb_w, num_rows, buf->stride);
273793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    }
274793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  }
275793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  return 0;
276793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler}
277793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
278793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler//------------------------------------------------------------------------------
279793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler// YUV rescaling (no final RGB conversion needed)
280793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
281793ee12c6df9cad3806238d32528c49a3ff9331dNoah Preslerstatic int Rescale(const uint8_t* src, int src_stride,
282793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler                   int new_lines, WebPRescaler* const wrk) {
283793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  int num_lines_out = 0;
284793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  while (new_lines > 0) {    // import new contributions of source rows.
285793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    const int lines_in = WebPRescalerImport(wrk, new_lines, src, src_stride);
286793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    src += lines_in * src_stride;
287793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    new_lines -= lines_in;
288793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    num_lines_out += WebPRescalerExport(wrk);    // emit output row(s)
289793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  }
290793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  return num_lines_out;
291793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler}
292793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
293793ee12c6df9cad3806238d32528c49a3ff9331dNoah Preslerstatic int EmitRescaledYUV(const VP8Io* const io, WebPDecParams* const p) {
294793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const int mb_h = io->mb_h;
295793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const int uv_mb_h = (mb_h + 1) >> 1;
296793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const int num_lines_out = Rescale(io->y, io->y_stride, mb_h, &p->scaler_y);
297793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  Rescale(io->u, io->uv_stride, uv_mb_h, &p->scaler_u);
298793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  Rescale(io->v, io->uv_stride, uv_mb_h, &p->scaler_v);
299793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  return num_lines_out;
300793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler}
301793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
302793ee12c6df9cad3806238d32528c49a3ff9331dNoah Preslerstatic int EmitRescaledAlphaYUV(const VP8Io* const io, WebPDecParams* const p) {
303793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  if (io->a != NULL) {
304793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    Rescale(io->a, io->width, io->mb_h, &p->scaler_a);
305793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  }
306793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  return 0;
307793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler}
308793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
309793ee12c6df9cad3806238d32528c49a3ff9331dNoah Preslerstatic int InitYUVRescaler(const VP8Io* const io, WebPDecParams* const p) {
310793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const int has_alpha = WebPIsAlphaMode(p->output->colorspace);
311793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const WebPYUVABuffer* const buf = &p->output->u.YUVA;
312793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const int out_width  = io->scaled_width;
313793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const int out_height = io->scaled_height;
314793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const int uv_out_width  = (out_width + 1) >> 1;
315793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const int uv_out_height = (out_height + 1) >> 1;
316793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const int uv_in_width  = (io->mb_w + 1) >> 1;
317793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const int uv_in_height = (io->mb_h + 1) >> 1;
318793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const size_t work_size = 2 * out_width;   // scratch memory for luma rescaler
319793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const size_t uv_work_size = 2 * uv_out_width;  // and for each u/v ones
320793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  size_t tmp_size;
321793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  int32_t* work;
322793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
323793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  tmp_size = work_size + 2 * uv_work_size;
324793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  if (has_alpha) {
325793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    tmp_size += work_size;
326793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  }
327793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  p->memory = calloc(1, tmp_size * sizeof(*work));
328793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  if (p->memory == NULL) {
329793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    return 0;   // memory error
330793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  }
331793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  work = (int32_t*)p->memory;
332793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  WebPRescalerInit(&p->scaler_y, io->mb_w, io->mb_h,
333793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler                   buf->y, out_width, out_height, buf->y_stride, 1,
334793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler                   io->mb_w, out_width, io->mb_h, out_height,
335793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler                   work);
336793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  WebPRescalerInit(&p->scaler_u, uv_in_width, uv_in_height,
337793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler                   buf->u, uv_out_width, uv_out_height, buf->u_stride, 1,
338793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler                   uv_in_width, uv_out_width,
339793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler                   uv_in_height, uv_out_height,
340793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler                   work + work_size);
341793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  WebPRescalerInit(&p->scaler_v, uv_in_width, uv_in_height,
342793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler                   buf->v, uv_out_width, uv_out_height, buf->v_stride, 1,
343793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler                   uv_in_width, uv_out_width,
344793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler                   uv_in_height, uv_out_height,
345793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler                   work + work_size + uv_work_size);
346793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  p->emit = EmitRescaledYUV;
347793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
348793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  if (has_alpha) {
349793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    WebPRescalerInit(&p->scaler_a, io->mb_w, io->mb_h,
350793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler                     buf->a, out_width, out_height, buf->a_stride, 1,
351793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler                     io->mb_w, out_width, io->mb_h, out_height,
352793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler                     work + work_size + 2 * uv_work_size);
353793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    p->emit_alpha = EmitRescaledAlphaYUV;
354793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  }
355793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  return 1;
356793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler}
357793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
358793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler//------------------------------------------------------------------------------
359793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler// RGBA rescaling
360793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
361793ee12c6df9cad3806238d32528c49a3ff9331dNoah Preslerstatic int ExportRGB(WebPDecParams* const p, int y_pos) {
362793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const WebPYUV444Converter convert =
363793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      WebPYUV444Converters[p->output->colorspace];
364793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const WebPRGBABuffer* const buf = &p->output->u.RGBA;
365793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  uint8_t* dst = buf->rgba + (p->last_y + y_pos) * buf->stride;
366793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  int num_lines_out = 0;
367793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  // For RGB rescaling, because of the YUV420, current scan position
368793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  // U/V can be +1/-1 line from the Y one.  Hence the double test.
369793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  while (WebPRescalerHasPendingOutput(&p->scaler_y) &&
370793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler         WebPRescalerHasPendingOutput(&p->scaler_u)) {
371793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    assert(p->last_y + y_pos + num_lines_out < p->output->height);
372793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    assert(p->scaler_u.y_accum == p->scaler_v.y_accum);
373793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    WebPRescalerExportRow(&p->scaler_y);
374793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    WebPRescalerExportRow(&p->scaler_u);
375793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    WebPRescalerExportRow(&p->scaler_v);
376793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    convert(p->scaler_y.dst, p->scaler_u.dst, p->scaler_v.dst,
377793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler            dst, p->scaler_y.dst_width);
378793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    dst += buf->stride;
379793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    ++num_lines_out;
380793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  }
381793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  return num_lines_out;
382793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler}
383793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
384793ee12c6df9cad3806238d32528c49a3ff9331dNoah Preslerstatic int EmitRescaledRGB(const VP8Io* const io, WebPDecParams* const p) {
385793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const int mb_h = io->mb_h;
386793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const int uv_mb_h = (mb_h + 1) >> 1;
387793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  int j = 0, uv_j = 0;
388793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  int num_lines_out = 0;
389793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  while (j < mb_h) {
390793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    const int y_lines_in =
391793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler        WebPRescalerImport(&p->scaler_y, mb_h - j,
392793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler                           io->y + j * io->y_stride, io->y_stride);
393793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    const int u_lines_in =
394793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler        WebPRescalerImport(&p->scaler_u, uv_mb_h - uv_j,
395793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler                           io->u + uv_j * io->uv_stride, io->uv_stride);
396793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    const int v_lines_in =
397793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler        WebPRescalerImport(&p->scaler_v, uv_mb_h - uv_j,
398793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler                           io->v + uv_j * io->uv_stride, io->uv_stride);
399793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    (void)v_lines_in;   // remove a gcc warning
400793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    assert(u_lines_in == v_lines_in);
401793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    j += y_lines_in;
402793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    uv_j += u_lines_in;
403793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    num_lines_out += ExportRGB(p, num_lines_out);
404793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  }
405793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  return num_lines_out;
406793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler}
407793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
408793ee12c6df9cad3806238d32528c49a3ff9331dNoah Preslerstatic int ExportAlpha(WebPDecParams* const p, int y_pos) {
409793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const WebPRGBABuffer* const buf = &p->output->u.RGBA;
410793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  uint8_t* const base_rgba = buf->rgba + (p->last_y + y_pos) * buf->stride;
411793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const WEBP_CSP_MODE colorspace = p->output->colorspace;
412793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const int alpha_first =
413793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      (colorspace == MODE_ARGB || colorspace == MODE_Argb);
414793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  uint8_t* dst = base_rgba + (alpha_first ? 0 : 3);
415793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  int num_lines_out = 0;
416793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const int is_premult_alpha = WebPIsPremultipliedMode(colorspace);
417793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  uint32_t alpha_mask = 0xff;
418793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const int width = p->scaler_a.dst_width;
419793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
420793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  while (WebPRescalerHasPendingOutput(&p->scaler_a)) {
421793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    int i;
422793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    assert(p->last_y + y_pos + num_lines_out < p->output->height);
423793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    WebPRescalerExportRow(&p->scaler_a);
424793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    for (i = 0; i < width; ++i) {
425793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      const uint32_t alpha_value = p->scaler_a.dst[i];
426793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      dst[4 * i] = alpha_value;
427793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      alpha_mask &= alpha_value;
428793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    }
429793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    dst += buf->stride;
430793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    ++num_lines_out;
431793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  }
432793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  if (is_premult_alpha && alpha_mask != 0xff) {
433793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    WebPApplyAlphaMultiply(base_rgba, alpha_first,
434793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler                           width, num_lines_out, buf->stride);
435793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  }
436793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  return num_lines_out;
437793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler}
438793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
439793ee12c6df9cad3806238d32528c49a3ff9331dNoah Preslerstatic int ExportAlphaRGBA4444(WebPDecParams* const p, int y_pos) {
440793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const WebPRGBABuffer* const buf = &p->output->u.RGBA;
441793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  uint8_t* const base_rgba = buf->rgba + (p->last_y + y_pos) * buf->stride;
442793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  uint8_t* alpha_dst = base_rgba + 1;
443793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  int num_lines_out = 0;
444793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const WEBP_CSP_MODE colorspace = p->output->colorspace;
445793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const int width = p->scaler_a.dst_width;
446793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const int is_premult_alpha = WebPIsPremultipliedMode(colorspace);
447793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  uint32_t alpha_mask = 0x0f;
448793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
449793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  while (WebPRescalerHasPendingOutput(&p->scaler_a)) {
450793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    int i;
451793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    assert(p->last_y + y_pos + num_lines_out < p->output->height);
452793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    WebPRescalerExportRow(&p->scaler_a);
453793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    for (i = 0; i < width; ++i) {
454793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      // Fill in the alpha value (converted to 4 bits).
455793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      const uint32_t alpha_value = p->scaler_a.dst[i] >> 4;
456793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      alpha_dst[2 * i] = (alpha_dst[2 * i] & 0xf0) | alpha_value;
457793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      alpha_mask &= alpha_value;
458793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    }
459793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    alpha_dst += buf->stride;
460793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    ++num_lines_out;
461793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  }
462793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  if (is_premult_alpha && alpha_mask != 0x0f) {
463793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    WebPApplyAlphaMultiply4444(base_rgba, width, num_lines_out, buf->stride);
464793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  }
465793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  return num_lines_out;
466793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler}
467793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
468793ee12c6df9cad3806238d32528c49a3ff9331dNoah Preslerstatic int EmitRescaledAlphaRGB(const VP8Io* const io, WebPDecParams* const p) {
469793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  if (io->a != NULL) {
470793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    WebPRescaler* const scaler = &p->scaler_a;
471793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    int j = 0;
472793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    int pos = 0;
473793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    while (j < io->mb_h) {
474793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      j += WebPRescalerImport(scaler, io->mb_h - j,
475793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler                              io->a + j * io->width, io->width);
476793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      pos += p->emit_alpha_row(p, pos);
477793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    }
478793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  }
479793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  return 0;
480793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler}
481793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
482793ee12c6df9cad3806238d32528c49a3ff9331dNoah Preslerstatic int InitRGBRescaler(const VP8Io* const io, WebPDecParams* const p) {
483793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const int has_alpha = WebPIsAlphaMode(p->output->colorspace);
484793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const int out_width  = io->scaled_width;
485793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const int out_height = io->scaled_height;
486793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const int uv_in_width  = (io->mb_w + 1) >> 1;
487793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const int uv_in_height = (io->mb_h + 1) >> 1;
488793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const size_t work_size = 2 * out_width;   // scratch memory for one rescaler
489793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  int32_t* work;  // rescalers work area
490793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  uint8_t* tmp;   // tmp storage for scaled YUV444 samples before RGB conversion
491793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  size_t tmp_size1, tmp_size2;
492793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
493793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  tmp_size1 = 3 * work_size;
494793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  tmp_size2 = 3 * out_width;
495793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  if (has_alpha) {
496793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    tmp_size1 += work_size;
497793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    tmp_size2 += out_width;
498793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  }
499793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  p->memory = calloc(1, tmp_size1 * sizeof(*work) + tmp_size2 * sizeof(*tmp));
500793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  if (p->memory == NULL) {
501793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    return 0;   // memory error
502793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  }
503793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  work = (int32_t*)p->memory;
504793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  tmp = (uint8_t*)(work + tmp_size1);
505793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  WebPRescalerInit(&p->scaler_y, io->mb_w, io->mb_h,
506793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler                   tmp + 0 * out_width, out_width, out_height, 0, 1,
507793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler                   io->mb_w, out_width, io->mb_h, out_height,
508793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler                   work + 0 * work_size);
509793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  WebPRescalerInit(&p->scaler_u, uv_in_width, uv_in_height,
510793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler                   tmp + 1 * out_width, out_width, out_height, 0, 1,
511793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler                   io->mb_w, 2 * out_width, io->mb_h, 2 * out_height,
512793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler                   work + 1 * work_size);
513793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  WebPRescalerInit(&p->scaler_v, uv_in_width, uv_in_height,
514793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler                   tmp + 2 * out_width, out_width, out_height, 0, 1,
515793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler                   io->mb_w, 2 * out_width, io->mb_h, 2 * out_height,
516793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler                   work + 2 * work_size);
517793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  p->emit = EmitRescaledRGB;
518793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
519793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  if (has_alpha) {
520793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    WebPRescalerInit(&p->scaler_a, io->mb_w, io->mb_h,
521793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler                     tmp + 3 * out_width, out_width, out_height, 0, 1,
522793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler                     io->mb_w, out_width, io->mb_h, out_height,
523793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler                     work + 3 * work_size);
524793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    p->emit_alpha = EmitRescaledAlphaRGB;
525793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    if (p->output->colorspace == MODE_RGBA_4444 ||
526793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler        p->output->colorspace == MODE_rgbA_4444) {
527793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      p->emit_alpha_row = ExportAlphaRGBA4444;
528793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    } else {
529793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      p->emit_alpha_row = ExportAlpha;
530793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    }
531793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  }
532793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  return 1;
533793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler}
534793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
535793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler//------------------------------------------------------------------------------
536793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler// Default custom functions
537793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
538793ee12c6df9cad3806238d32528c49a3ff9331dNoah Preslerstatic int CustomSetup(VP8Io* io) {
539793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  WebPDecParams* const p = (WebPDecParams*)io->opaque;
540793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const WEBP_CSP_MODE colorspace = p->output->colorspace;
541793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const int is_rgb = WebPIsRGBMode(colorspace);
542793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const int is_alpha = WebPIsAlphaMode(colorspace);
543793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
544793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  p->memory = NULL;
545793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  p->emit = NULL;
546793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  p->emit_alpha = NULL;
547793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  p->emit_alpha_row = NULL;
548793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  if (!WebPIoInitFromOptions(p->options, io, is_alpha ? MODE_YUV : MODE_YUVA)) {
549793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    return 0;
550793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  }
551793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
552793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  if (io->use_scaling) {
553793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    const int ok = is_rgb ? InitRGBRescaler(io, p) : InitYUVRescaler(io, p);
554793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    if (!ok) {
555793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      return 0;    // memory error
556793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    }
557793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  } else {
558793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    if (is_rgb) {
559793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      p->emit = EmitSampledRGB;   // default
560793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler#ifdef FANCY_UPSAMPLING
561793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      if (io->fancy_upsampling) {
562793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler        const int uv_width = (io->mb_w + 1) >> 1;
563793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler        p->memory = malloc(io->mb_w + 2 * uv_width);
564793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler        if (p->memory == NULL) {
565793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler          return 0;   // memory error.
566793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler        }
567793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler        p->tmp_y = (uint8_t*)p->memory;
568793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler        p->tmp_u = p->tmp_y + io->mb_w;
569793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler        p->tmp_v = p->tmp_u + uv_width;
570793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler        p->emit = EmitFancyRGB;
571793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler        WebPInitUpsamplers();
572793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      }
573793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler#endif
574793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    } else {
575793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      p->emit = EmitYUV;
576793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    }
577793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    if (is_alpha) {  // need transparency output
578793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      if (WebPIsPremultipliedMode(colorspace)) WebPInitPremultiply();
579793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler      p->emit_alpha =
580793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler          (colorspace == MODE_RGBA_4444 || colorspace == MODE_rgbA_4444) ?
581793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler              EmitAlphaRGBA4444
582793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler          : is_rgb ? EmitAlphaRGB
583793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler          : EmitAlphaYUV;
584793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    }
585793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  }
586793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
587793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  if (is_rgb) {
588793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    VP8YUVInit();
589793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  }
590793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  return 1;
591793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler}
592793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
593793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler//------------------------------------------------------------------------------
594793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
595793ee12c6df9cad3806238d32528c49a3ff9331dNoah Preslerstatic int CustomPut(const VP8Io* io) {
596793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  WebPDecParams* const p = (WebPDecParams*)io->opaque;
597793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const int mb_w = io->mb_w;
598793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  const int mb_h = io->mb_h;
599793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  int num_lines_out;
600793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  assert(!(io->mb_y & 1));
601793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
602793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  if (mb_w <= 0 || mb_h <= 0) {
603793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    return 0;
604793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  }
605793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  num_lines_out = p->emit(io, p);
606793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  if (p->emit_alpha) {
607793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler    p->emit_alpha(io, p);
608793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  }
609793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  p->last_y += num_lines_out;
610793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  return 1;
611793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler}
612793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
613793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler//------------------------------------------------------------------------------
614793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
615793ee12c6df9cad3806238d32528c49a3ff9331dNoah Preslerstatic void CustomTeardown(const VP8Io* io) {
616793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  WebPDecParams* const p = (WebPDecParams*)io->opaque;
617793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  free(p->memory);
618793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  p->memory = NULL;
619793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler}
620793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
621793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler//------------------------------------------------------------------------------
622793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler// Main entry point
623793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
624793ee12c6df9cad3806238d32528c49a3ff9331dNoah Preslervoid WebPInitCustomIo(WebPDecParams* const params, VP8Io* const io) {
625793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  io->put      = CustomPut;
626793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  io->setup    = CustomSetup;
627793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  io->teardown = CustomTeardown;
628793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler  io->opaque   = params;
629793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler}
630793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
631793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler//------------------------------------------------------------------------------
632793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler
633793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler#if defined(__cplusplus) || defined(c_plusplus)
634793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler}    // extern "C"
635793ee12c6df9cad3806238d32528c49a3ff9331dNoah Presler#endif
636