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