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