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
EmitYUV(const VP8Io * const io,WebPDecParams * const p)25 static 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.
EmitSampledRGB(const VP8Io * const io,WebPDecParams * const p)47 static 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
EmitFancyRGB(const VP8Io * const io,WebPDecParams * const p)62 static 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
EmitAlphaYUV(const VP8Io * const io,WebPDecParams * const p,int expected_num_lines_out)122 static 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
GetAlphaSourceRow(const VP8Io * const io,const uint8_t ** alpha,int * const num_rows)148 static 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
EmitAlphaRGB(const VP8Io * const io,WebPDecParams * const p,int expected_num_lines_out)174 static 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
EmitAlphaRGBA4444(const VP8Io * const io,WebPDecParams * const p,int expected_num_lines_out)200 static 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
Rescale(const uint8_t * src,int src_stride,int new_lines,WebPRescaler * const wrk)239 static 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
EmitRescaledYUV(const VP8Io * const io,WebPDecParams * const p)251 static 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
EmitRescaledAlphaYUV(const VP8Io * const io,WebPDecParams * const p,int expected_num_lines_out)270 static 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
InitYUVRescaler(const VP8Io * const io,WebPDecParams * const p)287 static 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
ExportRGB(WebPDecParams * const p,int y_pos)334 static 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
EmitRescaledRGB(const VP8Io * const io,WebPDecParams * const p)357 static 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
ExportAlpha(WebPDecParams * const p,int y_pos,int max_lines_out)383 static 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
ExportAlphaRGBA4444(WebPDecParams * const p,int y_pos,int max_lines_out)410 static 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
EmitRescaledAlphaRGB(const VP8Io * const io,WebPDecParams * const p,int expected_num_out_lines)445 static 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
InitRGBRescaler(const VP8Io * const io,WebPDecParams * const p)461 static 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
CustomSetup(VP8Io * io)516 static 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
CustomPut(const VP8Io * io)578 static 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
CustomTeardown(const VP8Io * io)598 static 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
WebPInitCustomIo(WebPDecParams * const params,VP8Io * const io)607 void 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