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

@ -22,7 +22,7 @@
void WebPRescalerInit(WebPRescaler* const wrk, int src_width, int src_height,
uint8_t* const dst,
int dst_width, int dst_height, int dst_stride,
int num_channels, int32_t* const work) {
int num_channels, rescaler_t* const work) {
const int x_add = src_width, x_sub = dst_width;
const int y_add = src_height, y_sub = dst_height;
wrk->x_expand = (src_width < dst_width);
@ -31,6 +31,8 @@ void WebPRescalerInit(WebPRescaler* const wrk, int src_width, int src_height,
wrk->src_height = src_height;
wrk->dst_width = dst_width;
wrk->dst_height = dst_height;
wrk->src_y = 0;
wrk->dst_y = 0;
wrk->dst = dst;
wrk->dst_stride = dst_stride;
wrk->num_channels = num_channels;
@ -39,18 +41,20 @@ void WebPRescalerInit(WebPRescaler* const wrk, int src_width, int src_height,
wrk->x_add = wrk->x_expand ? (x_sub - 1) : x_add;
wrk->x_sub = wrk->x_expand ? (x_add - 1) : x_sub;
if (!wrk->x_expand) { // fx_scale is not used otherwise
wrk->fx_scale = (1 << WEBP_RESCALER_RFIX) / wrk->x_sub;
wrk->fx_scale = WEBP_RESCALER_ONE / wrk->x_sub;
}
// vertical scaling parameters
wrk->y_accum = y_add;
wrk->y_add = y_add;
wrk->y_sub = y_sub;
wrk->fy_scale = (1 << WEBP_RESCALER_RFIX) / wrk->y_sub;
wrk->fxy_scale =
((int64_t)dst_height << WEBP_RESCALER_RFIX) / (wrk->x_add * wrk->y_add);
wrk->y_add = wrk->y_expand ? y_add - 1 : y_add;
wrk->y_sub = wrk->y_expand ? y_sub - 1: y_sub;
wrk->y_accum = wrk->y_expand ? wrk->y_sub : wrk->y_add;
if (!wrk->y_expand) {
wrk->fy_scale = WEBP_RESCALER_ONE / wrk->y_sub;
wrk->fxy_scale = ((uint64_t)dst_height << WEBP_RESCALER_RFIX)
/ (wrk->x_add * wrk->y_add);
} else {
wrk->fy_scale = WEBP_RESCALER_ONE / wrk->x_add;
wrk->fxy_scale = WEBP_RESCALER_ONE / (wrk->x_add * wrk->y_sub);
}
wrk->irow = work;
wrk->frow = work + num_channels * dst_width;
memset(work, 0, 2 * dst_width * num_channels * sizeof(*work));
@ -98,10 +102,21 @@ int WebPRescalerImport(WebPRescaler* const wrk, int num_lines,
const uint8_t* src, int src_stride) {
int total_imported = 0;
while (total_imported < num_lines && !WebPRescalerHasPendingOutput(wrk)) {
int channel;
int x, channel;
if (wrk->y_expand) {
rescaler_t* const tmp = wrk->irow;
wrk->irow = wrk->frow;
wrk->frow = tmp;
}
for (channel = 0; channel < wrk->num_channels; ++channel) {
WebPRescalerImportRow(wrk, src, channel);
}
if (!wrk->y_expand) { // Accumulate the contribution of the new row.
for (x = 0; x < wrk->num_channels * wrk->dst_width; ++x) {
wrk->irow[x] += wrk->frow[x];
}
}
++wrk->src_y;
src += src_stride;
++total_imported;
wrk->y_accum -= wrk->y_sub;
@ -112,7 +127,7 @@ int WebPRescalerImport(WebPRescaler* const wrk, int num_lines,
int WebPRescalerExport(WebPRescaler* const rescaler) {
int total_exported = 0;
while (WebPRescalerHasPendingOutput(rescaler)) {
WebPRescalerExportRow(rescaler, 0);
WebPRescalerExportRow(rescaler);
++total_exported;
}
return total_exported;

View File

@ -21,23 +21,27 @@ extern "C" {
#include "../webp/types.h"
#define WEBP_RESCALER_RFIX 30 // fixed-point precision for multiplies
#define WEBP_RESCALER_ONE (1u << WEBP_RESCALER_RFIX)
// Structure used for on-the-fly rescaling
typedef int32_t rescaler_t; // type for side-buffer
typedef struct WebPRescaler WebPRescaler;
struct WebPRescaler {
int x_expand; // true if we're expanding in the x direction
int y_expand; // true if we're expanding in the y direction
int num_channels; // bytes to jump between pixels
int fy_scale, fx_scale; // fixed-point scaling factor
int64_t fxy_scale; // ''
uint32_t fx_scale; // fixed-point scaling factors
uint32_t fy_scale; // ''
uint64_t fxy_scale; // ''
int y_accum; // vertical accumulator
int y_add, y_sub; // vertical increments
int x_add, x_sub; // horizontal increments
int src_width, src_height; // source dimensions
int dst_width, dst_height; // destination dimensions
int src_y, dst_y; // row counters for input and output
uint8_t* dst;
int dst_stride;
int32_t* irow, *frow; // work buffer
rescaler_t* irow, *frow; // work buffer
};
// Initialize a rescaler given scratch area 'work' and dimensions of src & dst.
@ -46,7 +50,7 @@ void WebPRescalerInit(WebPRescaler* const rescaler,
uint8_t* const dst,
int dst_width, int dst_height, int dst_stride,
int num_channels,
int32_t* const work);
rescaler_t* const work);
// If either 'scaled_width' or 'scaled_height' (but not both) is 0 the value
// will be calculated preserving the aspect ratio, otherwise the values are
@ -66,15 +70,26 @@ int WebPRescaleNeededLines(const WebPRescaler* const rescaler,
int WebPRescalerImport(WebPRescaler* const rescaler, int num_rows,
const uint8_t* src, int src_stride);
// Return true if there is pending output rows ready.
static WEBP_INLINE
int WebPRescalerHasPendingOutput(const WebPRescaler* const rescaler) {
return (rescaler->y_accum <= 0);
}
// Export as many rows as possible. Return the numbers of rows written.
int WebPRescalerExport(WebPRescaler* const rescaler);
// Return true if input is finished
static WEBP_INLINE
int WebPRescalerInputDone(const WebPRescaler* const rescaler) {
return (rescaler->src_y >= rescaler->src_height);
}
// Return true if output is finished
static WEBP_INLINE
int WebPRescalerOutputDone(const WebPRescaler* const rescaler) {
return (rescaler->dst_y >= rescaler->dst_height);
}
// Return true if there are pending output rows ready.
static WEBP_INLINE
int WebPRescalerHasPendingOutput(const WebPRescaler* const rescaler) {
return !WebPRescalerOutputDone(rescaler) && (rescaler->y_accum <= 0);
}
//------------------------------------------------------------------------------
#ifdef __cplusplus