From 8dcae8b3cfb33c0824a2d2b7b8455adeb71da355 Mon Sep 17 00:00:00 2001 From: Pascal Massimino Date: Fri, 26 Jul 2013 12:05:42 -0700 Subject: [PATCH] fix rescaling-with-alpha inaccuracy (still missing YUVA decoding case for now) https://code.google.com/p/webp/issues/detail?id=160 Change-Id: If723b4a5c0a303d0853ec9d839f995adce056095 --- Android.mk | 1 + Makefile.vc | 1 + makefile.unix | 2 + src/dec/vp8l.c | 64 ++++++----- src/enc/picture.c | 43 +++++++- src/utils/Makefile.am | 2 + src/utils/alpha_processing.c | 203 +++++++++++++++++++++++++++++++++++ src/utils/alpha_processing.h | 46 ++++++++ src/utils/rescaler.c | 5 + src/utils/rescaler.h | 12 ++- 10 files changed, 342 insertions(+), 37 deletions(-) create mode 100644 src/utils/alpha_processing.c create mode 100644 src/utils/alpha_processing.h diff --git a/Android.mk b/Android.mk index 3d27f349..b0bec4b4 100644 --- a/Android.mk +++ b/Android.mk @@ -39,6 +39,7 @@ LOCAL_SRC_FILES := \ src/enc/tree.c \ src/enc/vp8l.c \ src/enc/webpenc.c \ + src/utils/alpha_processing.c \ src/utils/bit_reader.c \ src/utils/bit_writer.c \ src/utils/color_cache.c \ diff --git a/Makefile.vc b/Makefile.vc index 468f8b3e..a5b3d49e 100644 --- a/Makefile.vc +++ b/Makefile.vc @@ -217,6 +217,7 @@ MUX_OBJS = \ $(DIROBJ)\mux\muxread.obj \ UTILS_DEC_OBJS = \ + $(DIROBJ)\utils\alpha_processing.obj \ $(DIROBJ)\utils\bit_reader.obj \ $(DIROBJ)\utils\color_cache.obj \ $(DIROBJ)\utils\filters.obj \ diff --git a/makefile.unix b/makefile.unix index ace67a68..221ce625 100644 --- a/makefile.unix +++ b/makefile.unix @@ -147,6 +147,7 @@ MUX_OBJS = \ src/mux/muxread.o \ UTILS_DEC_OBJS = \ + src/utils/alpha_processing.o \ src/utils/bit_reader.o \ src/utils/color_cache.o \ src/utils/filters.o \ @@ -186,6 +187,7 @@ HDRS = \ src/dsp/yuv.h \ src/enc/cost.h \ src/enc/vp8enci.h \ + src/utils/alpha_processing.h \ src/utils/bit_reader.h \ src/utils/bit_writer.h \ src/utils/color_cache.h \ diff --git a/src/dec/vp8l.c b/src/dec/vp8l.c index 119eb2f6..014bc91d 100644 --- a/src/dec/vp8l.c +++ b/src/dec/vp8l.c @@ -18,6 +18,7 @@ #include "./vp8li.h" #include "../dsp/lossless.h" #include "../dsp/yuv.h" +#include "../utils/alpha_processing.h" #include "../utils/huffman.h" #include "../utils/utils.h" @@ -417,12 +418,13 @@ static int AllocateAndInitRescaler(VP8LDecoder* const dec, VP8Io* const io) { // We have special "export" function since we need to convert from BGRA static int Export(WebPRescaler* const rescaler, WEBP_CSP_MODE colorspace, int rgba_stride, uint8_t* const rgba) { - const uint32_t* const src = (const uint32_t*)rescaler->dst; + uint32_t* const src = (uint32_t*)rescaler->dst; const int dst_width = rescaler->dst_width; int num_lines_out = 0; while (WebPRescalerHasPendingOutput(rescaler)) { uint8_t* const dst = rgba + num_lines_out * rgba_stride; WebPRescalerExportRow(rescaler); + WebPMultARGBRow(src, dst_width, 1); VP8LConvertFromBGRA(src, dst_width, colorspace, dst); ++num_lines_out; } @@ -430,18 +432,22 @@ static int Export(WebPRescaler* const rescaler, WEBP_CSP_MODE colorspace, } // Emit scaled rows. -static int EmitRescaledRows(const VP8LDecoder* const dec, - const uint32_t* const data, int in_stride, int mb_h, +static int EmitRescaledRowsRGBA(const VP8LDecoder* const dec, + uint8_t* in, int in_stride, int mb_h, uint8_t* const out, int out_stride) { const WEBP_CSP_MODE colorspace = dec->output_->colorspace; - const uint8_t* const in = (const uint8_t*)data; int num_lines_in = 0; int num_lines_out = 0; while (num_lines_in < mb_h) { - const uint8_t* const row_in = in + num_lines_in * in_stride; + uint8_t* const row_in = in + num_lines_in * in_stride; uint8_t* const row_out = out + num_lines_out * out_stride; - num_lines_in += WebPRescalerImport(dec->rescaler, mb_h - num_lines_in, - row_in, in_stride); + const int lines_left = mb_h - num_lines_in; + const int needed_lines = WebPRescaleNeededLines(dec->rescaler, lines_left); + assert(needed_lines > 0 && needed_lines <= lines_left); + WebPMultARGBRows(row_in, in_stride, + dec->rescaler->src_width, needed_lines, 0); + WebPRescalerImport(dec->rescaler, lines_left, row_in, in_stride); + num_lines_in += needed_lines; num_lines_out += Export(dec->rescaler, colorspace, out_stride, row_out); } return num_lines_out; @@ -449,11 +455,10 @@ static int EmitRescaledRows(const VP8LDecoder* const dec, // Emit rows without any scaling. static int EmitRows(WEBP_CSP_MODE colorspace, - const uint32_t* const data, int in_stride, + const uint8_t* row_in, int in_stride, int mb_w, int mb_h, uint8_t* const out, int out_stride) { int lines = mb_h; - const uint8_t* row_in = (const uint8_t*)data; uint8_t* row_out = out; while (lines-- > 0) { VP8LConvertFromBGRA((const uint32_t*)row_in, mb_w, colorspace, row_out); @@ -530,11 +535,12 @@ static void ConvertToYUVA(const uint32_t* const src, int width, int y_pos, static int ExportYUVA(const VP8LDecoder* const dec, int y_pos) { WebPRescaler* const rescaler = dec->rescaler; - const uint32_t* const src = (const uint32_t*)rescaler->dst; + uint32_t* const src = (uint32_t*)rescaler->dst; const int dst_width = rescaler->dst_width; int num_lines_out = 0; while (WebPRescalerHasPendingOutput(rescaler)) { WebPRescalerExportRow(rescaler); + WebPMultARGBRow(src, dst_width, 1); ConvertToYUVA(src, dst_width, y_pos, dec->output_); ++y_pos; ++num_lines_out; @@ -543,28 +549,28 @@ static int ExportYUVA(const VP8LDecoder* const dec, int y_pos) { } static int EmitRescaledRowsYUVA(const VP8LDecoder* const dec, - const uint32_t* const data, - int in_stride, int mb_h) { - const uint8_t* const in = (const uint8_t*)data; + uint8_t* in, int in_stride, int mb_h) { int num_lines_in = 0; int y_pos = dec->last_out_row_; while (num_lines_in < mb_h) { - const uint8_t* const row_in = in + num_lines_in * in_stride; - num_lines_in += WebPRescalerImport(dec->rescaler, mb_h - num_lines_in, - row_in, in_stride); + const int lines_left = mb_h - num_lines_in; + const int needed_lines = WebPRescaleNeededLines(dec->rescaler, lines_left); + WebPMultARGBRows(in, in_stride, dec->rescaler->src_width, needed_lines, 0); + WebPRescalerImport(dec->rescaler, lines_left, in, in_stride); + num_lines_in += needed_lines; + in += needed_lines * in_stride; y_pos += ExportYUVA(dec, y_pos); } return y_pos; } static int EmitRowsYUVA(const VP8LDecoder* const dec, - const uint32_t* const data, int in_stride, + const uint8_t* in, int in_stride, int mb_w, int num_rows) { int y_pos = dec->last_out_row_; - const uint8_t* row_in = (const uint8_t*)data; while (num_rows-- > 0) { - ConvertToYUVA((const uint32_t*)row_in, mb_w, y_pos, dec->output_); - row_in += in_stride; + ConvertToYUVA((const uint32_t*)in, mb_w, y_pos, dec->output_); + in += in_stride; ++y_pos; } return y_pos; @@ -575,11 +581,11 @@ static int EmitRowsYUVA(const VP8LDecoder* const dec, // Sets io->mb_y, io->mb_h & io->mb_w according to start row, end row and // crop options. Also updates the input data pointer, so that it points to the -// start of the cropped window. -// Note that 'pixel_stride' is in units of 'uint32_t' (and not 'bytes). +// start of the cropped window. Note that pixels are in ARGB format even if +// 'in_data' is uint8_t*. // Returns true if the crop window is not empty. static int SetCropWindow(VP8Io* const io, int y_start, int y_end, - const uint32_t** const in_data, int pixel_stride) { + uint8_t** const in_data, int pixel_stride) { assert(y_start < y_end); assert(io->crop_left < io->crop_right); if (y_end > io->crop_bottom) { @@ -588,11 +594,11 @@ static int SetCropWindow(VP8Io* const io, int y_start, int y_end, if (y_start < io->crop_top) { const int delta = io->crop_top - y_start; y_start = io->crop_top; - *in_data += pixel_stride * delta; + *in_data += delta * pixel_stride; } if (y_start >= y_end) return 0; // Crop window is empty. - *in_data += io->crop_left; + *in_data += io->crop_left * sizeof(uint32_t); io->mb_y = y_start - io->crop_top; io->mb_w = io->crop_right - io->crop_left; @@ -666,17 +672,17 @@ static void ProcessRows(VP8LDecoder* const dec, int row) { // Emit output. { VP8Io* const io = dec->io_; - const uint32_t* rows_data = dec->argb_cache_; - if (!SetCropWindow(io, dec->last_row_, row, &rows_data, io->width)) { + uint8_t* rows_data = (uint8_t*)dec->argb_cache_; + const int in_stride = io->width * sizeof(uint32_t); // in unit of RGBA + if (!SetCropWindow(io, dec->last_row_, row, &rows_data, in_stride)) { // Nothing to output (this time). } else { const WebPDecBuffer* const output = dec->output_; - const int in_stride = io->width * sizeof(*rows_data); if (output->colorspace < MODE_YUV) { // convert to RGBA const WebPRGBABuffer* const buf = &output->u.RGBA; uint8_t* const rgba = buf->rgba + dec->last_out_row_ * buf->stride; const int num_rows_out = io->use_scaling ? - EmitRescaledRows(dec, rows_data, in_stride, io->mb_h, + EmitRescaledRowsRGBA(dec, rows_data, in_stride, io->mb_h, rgba, buf->stride) : EmitRows(output->colorspace, rows_data, in_stride, io->mb_w, io->mb_h, rgba, buf->stride); diff --git a/src/enc/picture.c b/src/enc/picture.c index 6cff5b92..5f40b33d 100644 --- a/src/enc/picture.c +++ b/src/enc/picture.c @@ -16,6 +16,7 @@ #include #include "./vp8enci.h" +#include "../utils/alpha_processing.h" #include "../utils/rescaler.h" #include "../utils/utils.h" #include "../dsp/dsp.h" @@ -399,6 +400,28 @@ static void RescalePlane(const uint8_t* src, } } +static void AlphaMultiplyARGB(WebPPicture* const pic, int inverse) { + uint32_t* ptr = pic->argb; + int y; + for (y = 0; y < pic->height; ++y) { + WebPMultARGBRow(ptr, pic->width, inverse); + ptr += pic->argb_stride; + } +} + +static void AlphaMultiplyY(WebPPicture* const pic, int inverse) { + const uint8_t* ptr_a = pic->a; + if (ptr_a != NULL) { + uint8_t* ptr_y = pic->y; + int y; + for (y = 0; y < pic->height; ++y) { + WebPMultRow(ptr_y, ptr_a, pic->width, inverse); + ptr_y += pic->y_stride; + ptr_a += pic->a_stride; + } + } +} + int WebPPictureRescale(WebPPicture* pic, int width, int height) { WebPPicture tmp; int prev_width, prev_height; @@ -429,9 +452,19 @@ int WebPPictureRescale(WebPPicture* pic, int width, int height) { WebPPictureFree(&tmp); return 0; } + // If present, we need to rescale alpha first (for AlphaMultiplyY). + if (pic->a != NULL) { + RescalePlane(pic->a, prev_width, prev_height, pic->a_stride, + tmp.a, width, height, tmp.a_stride, work, 1); + } + // We take transparency into account on the luma plane only. That's not + // totally exact blending, but still is a good approximation. + AlphaMultiplyY(pic, 0); RescalePlane(pic->y, prev_width, prev_height, pic->y_stride, tmp.y, width, height, tmp.y_stride, work, 1); + AlphaMultiplyY(&tmp, 1); + RescalePlane(pic->u, HALVE(prev_width), HALVE(prev_height), pic->uv_stride, tmp.u, @@ -441,10 +474,6 @@ int WebPPictureRescale(WebPPicture* pic, int width, int height) { tmp.v, HALVE(width), HALVE(height), tmp.uv_stride, work, 1); - if (tmp.a != NULL) { - RescalePlane(pic->a, prev_width, prev_height, pic->a_stride, - tmp.a, width, height, tmp.a_stride, work, 1); - } #ifdef WEBP_EXPERIMENTAL_FEATURES if (tmp.u0 != NULL) { const int s = IS_YUV_CSP(tmp.colorspace, WEBP_YUV422) ? 2 : 1; @@ -462,12 +491,16 @@ int WebPPictureRescale(WebPPicture* pic, int width, int height) { WebPPictureFree(&tmp); return 0; } - + // In order to correctly interpolate colors, we need to apply the alpha + // weighting first (black-matting), scale the RGB values, and remove + // the premultiplication afterward (while preserving the alpha channel). + AlphaMultiplyARGB(pic, 0); RescalePlane((const uint8_t*)pic->argb, prev_width, prev_height, pic->argb_stride * 4, (uint8_t*)tmp.argb, width, height, tmp.argb_stride * 4, work, 4); + AlphaMultiplyARGB(&tmp, 1); } WebPPictureFree(pic); free(work); diff --git a/src/utils/Makefile.am b/src/utils/Makefile.am index 5fb35a24..47a0f8ee 100644 --- a/src/utils/Makefile.am +++ b/src/utils/Makefile.am @@ -9,6 +9,8 @@ common_HEADERS = ../webp/types.h commondir = $(includedir)/webp COMMON_SOURCES = +COMMON_SOURCES += alpha_processing.c +COMMON_SOURCES += alpha_processing.h COMMON_SOURCES += bit_reader.c COMMON_SOURCES += bit_reader.h COMMON_SOURCES += color_cache.c diff --git a/src/utils/alpha_processing.c b/src/utils/alpha_processing.c new file mode 100644 index 00000000..1ca899a3 --- /dev/null +++ b/src/utils/alpha_processing.c @@ -0,0 +1,203 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Utilities for processing transparent channel. +// +// Author: Skal (pascal.massimino@gmail.com) + +#include +#include "./alpha_processing.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +// Tables can be faster on some platform but incur some extra binary size (~2k). +// #define USE_TABLES_FOR_ALPHA_MULT + +// ----------------------------------------------------------------------------- + +#define MFIX 24 // 24bit fixed-point arithmetic +#define HALF ((1u << MFIX) >> 1) +#define KINV_255 ((1u << MFIX) / 255u) + +static uint32_t Mult(uint8_t x, uint32_t mult) { + const uint32_t v = (x * mult + HALF) >> MFIX; + assert(v <= 255); // <- 24bit precision is enough to ensure that. + return v; +} + +#ifdef USE_TABLES_FOR_ALPHA_MULT + +static const uint32_t kMultTables[2][256] = { + { // (255u << MFIX) / alpha + 0x00000000, 0xff000000, 0x7f800000, 0x55000000, 0x3fc00000, 0x33000000, + 0x2a800000, 0x246db6db, 0x1fe00000, 0x1c555555, 0x19800000, 0x172e8ba2, + 0x15400000, 0x139d89d8, 0x1236db6d, 0x11000000, 0x0ff00000, 0x0f000000, + 0x0e2aaaaa, 0x0d6bca1a, 0x0cc00000, 0x0c249249, 0x0b9745d1, 0x0b1642c8, + 0x0aa00000, 0x0a333333, 0x09cec4ec, 0x0971c71c, 0x091b6db6, 0x08cb08d3, + 0x08800000, 0x0839ce73, 0x07f80000, 0x07ba2e8b, 0x07800000, 0x07492492, + 0x07155555, 0x06e45306, 0x06b5e50d, 0x0689d89d, 0x06600000, 0x063831f3, + 0x06124924, 0x05ee23b8, 0x05cba2e8, 0x05aaaaaa, 0x058b2164, 0x056cefa8, + 0x05500000, 0x05343eb1, 0x05199999, 0x05000000, 0x04e76276, 0x04cfb2b7, + 0x04b8e38e, 0x04a2e8ba, 0x048db6db, 0x0479435e, 0x04658469, 0x045270d0, + 0x04400000, 0x042e29f7, 0x041ce739, 0x040c30c3, 0x03fc0000, 0x03ec4ec4, + 0x03dd1745, 0x03ce540f, 0x03c00000, 0x03b21642, 0x03a49249, 0x03976fc6, + 0x038aaaaa, 0x037e3f1f, 0x03722983, 0x03666666, 0x035af286, 0x034fcace, + 0x0344ec4e, 0x033a5440, 0x03300000, 0x0325ed09, 0x031c18f9, 0x0312818a, + 0x03092492, 0x03000000, 0x02f711dc, 0x02ee5846, 0x02e5d174, 0x02dd7baf, + 0x02d55555, 0x02cd5cd5, 0x02c590b2, 0x02bdef7b, 0x02b677d4, 0x02af286b, + 0x02a80000, 0x02a0fd5c, 0x029a1f58, 0x029364d9, 0x028ccccc, 0x0286562d, + 0x02800000, 0x0279c952, 0x0273b13b, 0x026db6db, 0x0267d95b, 0x026217ec, + 0x025c71c7, 0x0256e62a, 0x0251745d, 0x024c1bac, 0x0246db6d, 0x0241b2f9, + 0x023ca1af, 0x0237a6f4, 0x0232c234, 0x022df2df, 0x02293868, 0x02249249, + 0x02200000, 0x021b810e, 0x021714fb, 0x0212bb51, 0x020e739c, 0x020a3d70, + 0x02061861, 0x02020408, 0x01fe0000, 0x01fa0be8, 0x01f62762, 0x01f25213, + 0x01ee8ba2, 0x01ead3ba, 0x01e72a07, 0x01e38e38, 0x01e00000, 0x01dc7f10, + 0x01d90b21, 0x01d5a3e9, 0x01d24924, 0x01cefa8d, 0x01cbb7e3, 0x01c880e5, + 0x01c55555, 0x01c234f7, 0x01bf1f8f, 0x01bc14e5, 0x01b914c1, 0x01b61eed, + 0x01b33333, 0x01b05160, 0x01ad7943, 0x01aaaaaa, 0x01a7e567, 0x01a5294a, + 0x01a27627, 0x019fcbd2, 0x019d2a20, 0x019a90e7, 0x01980000, 0x01957741, + 0x0192f684, 0x01907da4, 0x018e0c7c, 0x018ba2e8, 0x018940c5, 0x0186e5f0, + 0x01849249, 0x018245ae, 0x01800000, 0x017dc11f, 0x017b88ee, 0x0179574e, + 0x01772c23, 0x01750750, 0x0172e8ba, 0x0170d045, 0x016ebdd7, 0x016cb157, + 0x016aaaaa, 0x0168a9b9, 0x0166ae6a, 0x0164b8a7, 0x0162c859, 0x0160dd67, + 0x015ef7bd, 0x015d1745, 0x015b3bea, 0x01596596, 0x01579435, 0x0155c7b4, + 0x01540000, 0x01523d03, 0x01507eae, 0x014ec4ec, 0x014d0fac, 0x014b5edc, + 0x0149b26c, 0x01480a4a, 0x01466666, 0x0144c6af, 0x01432b16, 0x0141938b, + 0x01400000, 0x013e7063, 0x013ce4a9, 0x013b5cc0, 0x0139d89d, 0x01385830, + 0x0136db6d, 0x01356246, 0x0133ecad, 0x01327a97, 0x01310bf6, 0x012fa0be, + 0x012e38e3, 0x012cd459, 0x012b7315, 0x012a150a, 0x0128ba2e, 0x01276276, + 0x01260dd6, 0x0124bc44, 0x01236db6, 0x01222222, 0x0120d97c, 0x011f93bc, + 0x011e50d7, 0x011d10c4, 0x011bd37a, 0x011a98ef, 0x0119611a, 0x01182bf2, + 0x0116f96f, 0x0115c988, 0x01149c34, 0x0113716a, 0x01124924, 0x01112358, + 0x01100000, 0x010edf12, 0x010dc087, 0x010ca458, 0x010b8a7d, 0x010a72f0, + 0x01095da8, 0x01084a9f, 0x010739ce, 0x01062b2e, 0x01051eb8, 0x01041465, + 0x01030c30, 0x01020612, 0x01010204, 0x01000000 }, + { // alpha * KINV_255 + 0x00000000, 0x00010101, 0x00020202, 0x00030303, 0x00040404, 0x00050505, + 0x00060606, 0x00070707, 0x00080808, 0x00090909, 0x000a0a0a, 0x000b0b0b, + 0x000c0c0c, 0x000d0d0d, 0x000e0e0e, 0x000f0f0f, 0x00101010, 0x00111111, + 0x00121212, 0x00131313, 0x00141414, 0x00151515, 0x00161616, 0x00171717, + 0x00181818, 0x00191919, 0x001a1a1a, 0x001b1b1b, 0x001c1c1c, 0x001d1d1d, + 0x001e1e1e, 0x001f1f1f, 0x00202020, 0x00212121, 0x00222222, 0x00232323, + 0x00242424, 0x00252525, 0x00262626, 0x00272727, 0x00282828, 0x00292929, + 0x002a2a2a, 0x002b2b2b, 0x002c2c2c, 0x002d2d2d, 0x002e2e2e, 0x002f2f2f, + 0x00303030, 0x00313131, 0x00323232, 0x00333333, 0x00343434, 0x00353535, + 0x00363636, 0x00373737, 0x00383838, 0x00393939, 0x003a3a3a, 0x003b3b3b, + 0x003c3c3c, 0x003d3d3d, 0x003e3e3e, 0x003f3f3f, 0x00404040, 0x00414141, + 0x00424242, 0x00434343, 0x00444444, 0x00454545, 0x00464646, 0x00474747, + 0x00484848, 0x00494949, 0x004a4a4a, 0x004b4b4b, 0x004c4c4c, 0x004d4d4d, + 0x004e4e4e, 0x004f4f4f, 0x00505050, 0x00515151, 0x00525252, 0x00535353, + 0x00545454, 0x00555555, 0x00565656, 0x00575757, 0x00585858, 0x00595959, + 0x005a5a5a, 0x005b5b5b, 0x005c5c5c, 0x005d5d5d, 0x005e5e5e, 0x005f5f5f, + 0x00606060, 0x00616161, 0x00626262, 0x00636363, 0x00646464, 0x00656565, + 0x00666666, 0x00676767, 0x00686868, 0x00696969, 0x006a6a6a, 0x006b6b6b, + 0x006c6c6c, 0x006d6d6d, 0x006e6e6e, 0x006f6f6f, 0x00707070, 0x00717171, + 0x00727272, 0x00737373, 0x00747474, 0x00757575, 0x00767676, 0x00777777, + 0x00787878, 0x00797979, 0x007a7a7a, 0x007b7b7b, 0x007c7c7c, 0x007d7d7d, + 0x007e7e7e, 0x007f7f7f, 0x00808080, 0x00818181, 0x00828282, 0x00838383, + 0x00848484, 0x00858585, 0x00868686, 0x00878787, 0x00888888, 0x00898989, + 0x008a8a8a, 0x008b8b8b, 0x008c8c8c, 0x008d8d8d, 0x008e8e8e, 0x008f8f8f, + 0x00909090, 0x00919191, 0x00929292, 0x00939393, 0x00949494, 0x00959595, + 0x00969696, 0x00979797, 0x00989898, 0x00999999, 0x009a9a9a, 0x009b9b9b, + 0x009c9c9c, 0x009d9d9d, 0x009e9e9e, 0x009f9f9f, 0x00a0a0a0, 0x00a1a1a1, + 0x00a2a2a2, 0x00a3a3a3, 0x00a4a4a4, 0x00a5a5a5, 0x00a6a6a6, 0x00a7a7a7, + 0x00a8a8a8, 0x00a9a9a9, 0x00aaaaaa, 0x00ababab, 0x00acacac, 0x00adadad, + 0x00aeaeae, 0x00afafaf, 0x00b0b0b0, 0x00b1b1b1, 0x00b2b2b2, 0x00b3b3b3, + 0x00b4b4b4, 0x00b5b5b5, 0x00b6b6b6, 0x00b7b7b7, 0x00b8b8b8, 0x00b9b9b9, + 0x00bababa, 0x00bbbbbb, 0x00bcbcbc, 0x00bdbdbd, 0x00bebebe, 0x00bfbfbf, + 0x00c0c0c0, 0x00c1c1c1, 0x00c2c2c2, 0x00c3c3c3, 0x00c4c4c4, 0x00c5c5c5, + 0x00c6c6c6, 0x00c7c7c7, 0x00c8c8c8, 0x00c9c9c9, 0x00cacaca, 0x00cbcbcb, + 0x00cccccc, 0x00cdcdcd, 0x00cecece, 0x00cfcfcf, 0x00d0d0d0, 0x00d1d1d1, + 0x00d2d2d2, 0x00d3d3d3, 0x00d4d4d4, 0x00d5d5d5, 0x00d6d6d6, 0x00d7d7d7, + 0x00d8d8d8, 0x00d9d9d9, 0x00dadada, 0x00dbdbdb, 0x00dcdcdc, 0x00dddddd, + 0x00dedede, 0x00dfdfdf, 0x00e0e0e0, 0x00e1e1e1, 0x00e2e2e2, 0x00e3e3e3, + 0x00e4e4e4, 0x00e5e5e5, 0x00e6e6e6, 0x00e7e7e7, 0x00e8e8e8, 0x00e9e9e9, + 0x00eaeaea, 0x00ebebeb, 0x00ececec, 0x00ededed, 0x00eeeeee, 0x00efefef, + 0x00f0f0f0, 0x00f1f1f1, 0x00f2f2f2, 0x00f3f3f3, 0x00f4f4f4, 0x00f5f5f5, + 0x00f6f6f6, 0x00f7f7f7, 0x00f8f8f8, 0x00f9f9f9, 0x00fafafa, 0x00fbfbfb, + 0x00fcfcfc, 0x00fdfdfd, 0x00fefefe, 0x00ffffff } +}; + +static WEBP_INLINE uint32_t GetScale(uint32_t a, int inverse) { + return kMultTables[!inverse][a]; +} + +#else + +static WEBP_INLINE uint32_t GetScale(uint32_t a, int inverse) { + return inverse ? (255u << MFIX) / a : a * KINV_255; +} + +#endif // USE_TABLES_FOR_ALPHA_MULT + +void WebPMultARGBRow(uint32_t* const ptr, int width, int inverse) { + int x; + for (x = 0; x < width; ++x) { + const uint32_t argb = ptr[x]; + if (argb < 0xff000000u) { // alpha < 255 + if (argb <= 0x00ffffffu) { // alpha == 0 + ptr[x] = 0; + } else { + const uint32_t alpha = (argb >> 24) & 0xff; + const uint32_t scale = GetScale(alpha, inverse); + uint32_t out = argb & 0xff000000u; + out |= Mult(argb >> 0, scale) << 0; + out |= Mult(argb >> 8, scale) << 8; + out |= Mult(argb >> 16, scale) << 16; + ptr[x] = out; + } + } + } +} + +void WebPMultARGBRows(uint8_t* ptr, int stride, int width, int num_rows, + int inverse) { + int n; + for (n = 0; n < num_rows; ++n) { + WebPMultARGBRow((uint32_t*)ptr, width, inverse); + ptr += stride; + } +} + +void WebPMultRow(uint8_t* const ptr, const uint8_t* const alpha, + int width, int inverse) { + int x; + for (x = 0; x < width; ++x) { + const uint32_t a = alpha[x]; + if (a != 255) { + if (a == 0) { + ptr[x] = 0; + } else { + const uint32_t scale = GetScale(a, inverse); + ptr[x] = Mult(ptr[x], scale); + } + } + } +} + +void WebPMultRows(uint8_t* ptr, int stride, + const uint8_t* alpha, int alpha_stride, + int width, int num_rows, int inverse) { + int n; + for (n = 0; n < num_rows; ++n) { + WebPMultRow(ptr, alpha, width, inverse); + ptr += stride; + alpha += alpha_stride; + } +} + +#undef KINV_255 +#undef HALF +#undef MFIX + +#if defined(__cplusplus) || defined(c_plusplus) +} // extern "C" +#endif diff --git a/src/utils/alpha_processing.h b/src/utils/alpha_processing.h new file mode 100644 index 00000000..5dcc097d --- /dev/null +++ b/src/utils/alpha_processing.h @@ -0,0 +1,46 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the COPYING file in the root of the source +// tree. An additional intellectual property rights grant can be found +// in the file PATENTS. All contributing project authors may +// be found in the AUTHORS file in the root of the source tree. +// ----------------------------------------------------------------------------- +// +// Utilities for processing transparent channel. +// +// Author: Skal (pascal.massimino@gmail.com) + +#ifndef WEBP_UTILS_ALPHA_PROCESSING_H_ +#define WEBP_UTILS_ALPHA_PROCESSING_H_ + +#include "../webp/types.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +// Pre-Multiply operation transforms x into x * A / 255 (where x=Y,R,G or B). +// Un-Multiply operation transforms x into x * 255 / A. + +// Pre-Multiply or Un-Multiply (if 'inverse' is true) argb values in a row. +void WebPMultARGBRow(uint32_t* const ptr, int width, int inverse); + +// Same a WebPMultARGBRow(), but for several rows. +void WebPMultARGBRows(uint8_t* ptr, int stride, int width, int num_rows, + int inverse); + +// Same for a row of single values, with side alpha values. +void WebPMultRow(uint8_t* const ptr, const uint8_t* const alpha, + int width, int inverse); + +// Same a WebPMultRow(), but for several 'num_rows' rows. +void WebPMultRows(uint8_t* ptr, int stride, + const uint8_t* alpha, int alpha_stride, + int width, int num_rows, int inverse); + +#if defined(__cplusplus) || defined(c_plusplus) +} // extern "C" +#endif + +#endif // WEBP_UTILS_ALPHA_PROCESSING_H_ diff --git a/src/utils/rescaler.c b/src/utils/rescaler.c index e5ddc296..cdaef710 100644 --- a/src/utils/rescaler.c +++ b/src/utils/rescaler.c @@ -123,6 +123,11 @@ uint8_t* WebPRescalerExportRow(WebPRescaler* const wrk) { //------------------------------------------------------------------------------ // all-in-one calls +int WebPRescaleNeededLines(const WebPRescaler* const wrk, int max_num_lines) { + const int num_lines = (wrk->y_accum + wrk->y_sub - 1) / wrk->y_sub; + return (num_lines > max_num_lines) ? max_num_lines : num_lines; +} + int WebPRescalerImport(WebPRescaler* const wrk, int num_lines, const uint8_t* src, int src_stride) { int total_imported = 0; diff --git a/src/utils/rescaler.h b/src/utils/rescaler.h index aedce462..a5b40d45 100644 --- a/src/utils/rescaler.h +++ b/src/utils/rescaler.h @@ -38,7 +38,8 @@ typedef struct { } WebPRescaler; // Initialize a rescaler given scratch area 'work' and dimensions of src & dst. -void WebPRescalerInit(WebPRescaler* const wrk, int src_width, int src_height, +void WebPRescalerInit(WebPRescaler* const rescaler, + int src_width, int src_height, uint8_t* const dst, int dst_width, int dst_height, int dst_stride, int num_channels, @@ -46,6 +47,11 @@ void WebPRescalerInit(WebPRescaler* const wrk, int src_width, int src_height, int y_add, int y_sub, int32_t* const work); +// Returns the number of input lines needed next to produce one output line, +// considering that the maximum available input lines are 'max_num_lines'. +int WebPRescaleNeededLines(const WebPRescaler* const rescaler, + int max_num_lines); + // Import a row of data and save its contribution in the rescaler. // 'channel' denotes the channel number to be imported. void WebPRescalerImportRow(WebPRescaler* const rescaler, @@ -64,10 +70,10 @@ int WebPRescalerHasPendingOutput(const WebPRescaler* const rescaler) { // Export one row from rescaler. Returns the pointer where output was written, // or NULL if no row was pending. -uint8_t* WebPRescalerExportRow(WebPRescaler* const wrk); +uint8_t* WebPRescalerExportRow(WebPRescaler* const rescaler); // Export as many rows as possible. Return the numbers of rows written. -int WebPRescalerExport(WebPRescaler* const wrk); +int WebPRescalerExport(WebPRescaler* const rescaler); //------------------------------------------------------------------------------