fix rescaler vertical interpolation

* vertical expansion now uses bilinear interpolation
  * heavily assumes that the alpha plane is decoded in full, not row-by-row
  * split the RescalerExportRow and RescalerImportRow methods into Shrink
    and Expand variants.
  * MIPS implementation of ExportRowExpand is missing.

There's room for extra speed optim and code re-org, but let's keep that for later patches.

addresses https://code.google.com/p/webp/issues/detail?id=254

Change-Id: I8f12b855342bf07dd467fe85e4fde5fd814effdb
This commit is contained in:
Pascal Massimino
2015-09-18 10:45:03 +02:00
committed by James Zern
parent cd82440ec7
commit 5ff0079ece
10 changed files with 553 additions and 388 deletions

View File

@ -17,78 +17,125 @@
//------------------------------------------------------------------------------
// Implementations of critical functions ImportRow / ExportRow
#define ROUNDER (1 << (WEBP_RESCALER_RFIX - 1))
#define MULT_FIX(x, y) (((int64_t)(x) * (y) + ROUNDER) >> WEBP_RESCALER_RFIX)
#define ROUNDER (WEBP_RESCALER_ONE >> 1)
#define MULT_FIX(x, y) (((uint64_t)(x) * (y) + ROUNDER) >> WEBP_RESCALER_RFIX)
static void RescalerImportRowC(WebPRescaler* const wrk,
const uint8_t* const src, int channel) {
//------------------------------------------------------------------------------
// Row import
void WebPRescalerImportRowExpandC(WebPRescaler* const wrk,
const uint8_t* const src, int channel) {
const int x_stride = wrk->num_channels;
const int x_out_max = wrk->dst_width * wrk->num_channels;
int x_in = channel;
int x_out;
if (!wrk->x_expand) {
uint32_t sum = 0;
int accum = 0;
for (x_out = channel; x_out < x_out_max; x_out += x_stride) {
uint32_t base = 0;
// simple bilinear interpolation
int accum = wrk->x_add;
int left = src[x_in];
int right = (wrk->src_width > 1) ? src[x_in + x_stride] : left;
x_in += x_stride;
x_out = channel;
assert(!WebPRescalerInputDone(wrk));
assert(wrk->x_expand);
while (1) {
wrk->frow[x_out] = right * wrk->x_add + (left - right) * accum;
x_out += x_stride;
if (x_out >= x_out_max) break;
accum -= wrk->x_sub;
if (accum < 0) {
left = right;
x_in += x_stride;
assert(x_in < wrk->src_width * x_stride);
right = src[x_in];
accum += wrk->x_add;
while (accum > 0) {
accum -= wrk->x_sub;
assert(x_in < wrk->src_width * x_stride);
base = src[x_in];
sum += base;
x_in += x_stride;
}
{ // Emit next horizontal pixel.
const int32_t frac = base * (-accum);
wrk->frow[x_out] = sum * wrk->x_sub - frac;
// fresh fractional start for next pixel
sum = (int)MULT_FIX(frac, wrk->fx_scale);
}
}
assert(accum == 0);
} else { // simple bilinear interpolation
int accum = wrk->x_add;
int left = src[x_in];
int right = (wrk->src_width > 1) ? src[x_in + x_stride] : left;
x_in += x_stride;
x_out = channel;
while (1) {
wrk->frow[x_out] = right * wrk->x_add + (left - right) * accum;
x_out += x_stride;
if (x_out >= x_out_max) break;
accum -= wrk->x_sub;
if (accum < 0) {
left = right;
x_in += x_stride;
assert(x_in < wrk->src_width * x_stride);
right = src[x_in];
accum += wrk->x_add;
}
}
assert(wrk->x_sub == 0 /* <- special case for src_width=1 */ || accum == 0);
}
// Accumulate the contribution of the new row.
assert(wrk->x_sub == 0 /* <- special case for src_width=1 */ || accum == 0);
}
void WebPRescalerImportRowShrinkC(WebPRescaler* const wrk,
const uint8_t* const src, int channel) {
const int x_stride = wrk->num_channels;
const int x_out_max = wrk->dst_width * wrk->num_channels;
int x_in = channel;
int x_out;
uint32_t sum = 0;
int accum = 0;
assert(!WebPRescalerInputDone(wrk));
assert(!wrk->x_expand);
for (x_out = channel; x_out < x_out_max; x_out += x_stride) {
wrk->irow[x_out] += wrk->frow[x_out];
uint32_t base = 0;
accum += wrk->x_add;
while (accum > 0) {
accum -= wrk->x_sub;
assert(x_in < wrk->src_width * x_stride);
base = src[x_in];
sum += base;
x_in += x_stride;
}
{ // Emit next horizontal pixel.
const rescaler_t frac = base * (-accum);
wrk->frow[x_out] = sum * wrk->x_sub - frac;
// fresh fractional start for next pixel
sum = (int)MULT_FIX(frac, wrk->fx_scale);
}
}
assert(accum == 0);
}
//------------------------------------------------------------------------------
// Row export
void WebPRescalerExportRowExpandC(WebPRescaler* const wrk) {
int x_out;
uint8_t* const dst = wrk->dst;
rescaler_t* const irow = wrk->irow;
const int x_out_max = wrk->dst_width * wrk->num_channels;
const rescaler_t* const frow = wrk->frow;
assert(!WebPRescalerOutputDone(wrk));
assert(wrk->y_accum <= 0);
assert(wrk->y_expand);
if (wrk->y_accum == 0) {
for (x_out = 0; x_out < x_out_max; ++x_out) {
const int v = (int)MULT_FIX(frow[x_out], wrk->fy_scale);
dst[x_out] = (!(v & ~0xff)) ? v : (v < 0) ? 0 : 255;
}
} else {
const int64_t A = wrk->y_sub + wrk->y_accum;
const int64_t B = -wrk->y_accum;
for (x_out = 0; x_out < x_out_max; ++x_out) {
const int64_t I = A * frow[x_out] + B * irow[x_out];
const int v = (int)MULT_FIX(I, wrk->fxy_scale);
dst[x_out] = (!(v & ~0xff)) ? v : (v < 0) ? 0 : 255;
}
}
}
void WebPRescalerExportRowC(WebPRescaler* const wrk, int x_out) {
if (wrk->y_accum <= 0) {
uint8_t* const dst = wrk->dst;
int32_t* const irow = wrk->irow;
const int32_t* const frow = wrk->frow;
const int yscale = wrk->fy_scale * (-wrk->y_accum);
const int x_out_max = wrk->dst_width * wrk->num_channels;
for (; x_out < x_out_max; ++x_out) {
void WebPRescalerExportRowShrinkC(WebPRescaler* const wrk) {
int x_out;
uint8_t* const dst = wrk->dst;
rescaler_t* const irow = wrk->irow;
const int x_out_max = wrk->dst_width * wrk->num_channels;
const rescaler_t* const frow = wrk->frow;
const int yscale = wrk->fy_scale * (-wrk->y_accum);
assert(!WebPRescalerOutputDone(wrk));
assert(wrk->y_accum <= 0);
assert(!wrk->y_expand);
if (yscale) {
for (x_out = 0; x_out < x_out_max; ++x_out) {
const int frac = (int)MULT_FIX(frow[x_out], yscale);
const int v = (int)MULT_FIX(irow[x_out] - frac, wrk->fxy_scale);
dst[x_out] = (!(v & ~0xff)) ? v : (v < 0) ? 0 : 255;
irow[x_out] = frac; // new fractional start
}
wrk->y_accum += wrk->y_add;
wrk->dst += wrk->dst_stride;
} else {
for (x_out = 0; x_out < x_out_max; ++x_out) {
const int v = (int)MULT_FIX(irow[x_out], wrk->fxy_scale);
dst[x_out] = (!(v & ~0xff)) ? v : (v < 0) ? 0 : 255;
irow[x_out] = 0;
}
}
}
@ -96,10 +143,39 @@ void WebPRescalerExportRowC(WebPRescaler* const wrk, int x_out) {
#undef ROUNDER
//------------------------------------------------------------------------------
// Main entry calls
void (*WebPRescalerImportRow)(struct WebPRescaler* const wrk,
const uint8_t* const src, int channel);
void (*WebPRescalerExportRow)(struct WebPRescaler* const wrk, int x_out);
void WebPRescalerImportRow(WebPRescaler* const wrk,
const uint8_t* const src, int channel) {
assert(!WebPRescalerInputDone(wrk));
if (!wrk->x_expand) {
WebPRescalerImportRowShrink(wrk, src, channel);
} else {
WebPRescalerImportRowExpand(wrk, src, channel);
}
}
void WebPRescalerExportRow(WebPRescaler* const wrk) {
if (wrk->y_accum <= 0) {
assert(!WebPRescalerOutputDone(wrk));
if (wrk->y_expand) {
WebPRescalerExportRowExpand(wrk);
} else {
WebPRescalerExportRowShrink(wrk);
}
wrk->y_accum += wrk->y_add;
wrk->dst += wrk->dst_stride;
++wrk->dst_y;
}
}
//------------------------------------------------------------------------------
WebPRescalerImportRowFunc WebPRescalerImportRowExpand;
WebPRescalerImportRowFunc WebPRescalerImportRowShrink;
WebPRescalerExportRowFunc WebPRescalerExportRowExpand;
WebPRescalerExportRowFunc WebPRescalerExportRowShrink;
extern void WebPRescalerDspInitMIPS32(void);
extern void WebPRescalerDspInitMIPSdspR2(void);
@ -110,8 +186,11 @@ static volatile VP8CPUInfo rescaler_last_cpuinfo_used =
WEBP_TSAN_IGNORE_FUNCTION void WebPRescalerDspInit(void) {
if (rescaler_last_cpuinfo_used == VP8GetCPUInfo) return;
WebPRescalerImportRow = RescalerImportRowC;
WebPRescalerExportRow = WebPRescalerExportRowC;
WebPRescalerImportRowExpand = WebPRescalerImportRowExpandC;
WebPRescalerImportRowShrink = WebPRescalerImportRowShrinkC;
WebPRescalerExportRowExpand = WebPRescalerExportRowExpandC;
WebPRescalerExportRowShrink = WebPRescalerExportRowShrinkC;
if (VP8GetCPUInfo != NULL) {
#if defined(WEBP_USE_MIPS32)
if (VP8GetCPUInfo(kMIPS32)) {