Alpha coding: reorganize the filter/unfiltering code
Move the filtering code to their own dsp/ spot New function: VP8FiltersInit() Change-Id: I0b2041eab42346c59b972f2575b05509e6a8f7b1
This commit is contained in:
parent
ec0d1be577
commit
7afdaf8496
@ -51,6 +51,7 @@ LOCAL_SRC_FILES := \
|
|||||||
src/dsp/enc_mips_dsp_r2.c \
|
src/dsp/enc_mips_dsp_r2.c \
|
||||||
src/dsp/enc_neon.$(NEON) \
|
src/dsp/enc_neon.$(NEON) \
|
||||||
src/dsp/enc_sse2.c \
|
src/dsp/enc_sse2.c \
|
||||||
|
src/dsp/filters.c \
|
||||||
src/dsp/filters_mips_dsp_r2.c \
|
src/dsp/filters_mips_dsp_r2.c \
|
||||||
src/dsp/lossless.c \
|
src/dsp/lossless.c \
|
||||||
src/dsp/lossless_mips32.c \
|
src/dsp/lossless_mips32.c \
|
||||||
|
@ -195,6 +195,7 @@ DSP_DEC_OBJS = \
|
|||||||
$(DIROBJ)\dsp\dec_mips_dsp_r2.obj \
|
$(DIROBJ)\dsp\dec_mips_dsp_r2.obj \
|
||||||
$(DIROBJ)\dsp\dec_neon.obj \
|
$(DIROBJ)\dsp\dec_neon.obj \
|
||||||
$(DIROBJ)\dsp\dec_sse2.obj \
|
$(DIROBJ)\dsp\dec_sse2.obj \
|
||||||
|
$(DIROBJ)\dsp\filters.obj \
|
||||||
$(DIROBJ)\dsp\filters_mips_dsp_r2.obj \
|
$(DIROBJ)\dsp\filters_mips_dsp_r2.obj \
|
||||||
$(DIROBJ)\dsp\lossless.obj \
|
$(DIROBJ)\dsp\lossless.obj \
|
||||||
$(DIROBJ)\dsp\lossless_mips32.obj \
|
$(DIROBJ)\dsp\lossless_mips32.obj \
|
||||||
|
@ -119,6 +119,7 @@ DSP_DEC_OBJS = \
|
|||||||
src/dsp/dec_mips_dsp_r2.o \
|
src/dsp/dec_mips_dsp_r2.o \
|
||||||
src/dsp/dec_neon.o \
|
src/dsp/dec_neon.o \
|
||||||
src/dsp/dec_sse2.o \
|
src/dsp/dec_sse2.o \
|
||||||
|
src/dsp/filters.o \
|
||||||
src/dsp/filters_mips_dsp_r2.o \
|
src/dsp/filters_mips_dsp_r2.o \
|
||||||
src/dsp/lossless.o \
|
src/dsp/lossless.o \
|
||||||
src/dsp/lossless_mips32.o \
|
src/dsp/lossless_mips32.o \
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include "./alphai.h"
|
#include "./alphai.h"
|
||||||
#include "./vp8i.h"
|
#include "./vp8i.h"
|
||||||
#include "./vp8li.h"
|
#include "./vp8li.h"
|
||||||
|
#include "../dsp/dsp.h"
|
||||||
#include "../utils/quant_levels_dec.h"
|
#include "../utils/quant_levels_dec.h"
|
||||||
#include "../utils/utils.h"
|
#include "../utils/utils.h"
|
||||||
#include "../webp/format_constants.h"
|
#include "../webp/format_constants.h"
|
||||||
@ -78,6 +79,7 @@ static int ALPHInit(ALPHDecoder* const dec, const uint8_t* data,
|
|||||||
assert(dec->method_ == ALPHA_LOSSLESS_COMPRESSION);
|
assert(dec->method_ == ALPHA_LOSSLESS_COMPRESSION);
|
||||||
ok = VP8LDecodeAlphaHeader(dec, alpha_data, alpha_data_size, output);
|
ok = VP8LDecodeAlphaHeader(dec, alpha_data, alpha_data_size, output);
|
||||||
}
|
}
|
||||||
|
VP8FiltersInit();
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ COMMON_SOURCES += dec_mips32.c
|
|||||||
COMMON_SOURCES += dec_mips_dsp_r2.c
|
COMMON_SOURCES += dec_mips_dsp_r2.c
|
||||||
COMMON_SOURCES += dec_neon.c
|
COMMON_SOURCES += dec_neon.c
|
||||||
COMMON_SOURCES += dsp.h
|
COMMON_SOURCES += dsp.h
|
||||||
|
COMMON_SOURCES += filters.c
|
||||||
COMMON_SOURCES += filters_mips_dsp_r2.c
|
COMMON_SOURCES += filters_mips_dsp_r2.c
|
||||||
COMMON_SOURCES += lossless.c
|
COMMON_SOURCES += lossless.c
|
||||||
COMMON_SOURCES += lossless.h
|
COMMON_SOURCES += lossless.h
|
||||||
|
@ -343,7 +343,6 @@ int (*WebPExtractAlpha)(const uint8_t*, int, int, int, uint8_t*, int);
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Init function
|
// Init function
|
||||||
|
|
||||||
extern void VP8FiltersInitMIPSdspR2(void);
|
|
||||||
extern void WebPInitAlphaProcessingMIPSdspR2(void);
|
extern void WebPInitAlphaProcessingMIPSdspR2(void);
|
||||||
extern void WebPInitAlphaProcessingSSE2(void);
|
extern void WebPInitAlphaProcessingSSE2(void);
|
||||||
|
|
||||||
@ -370,7 +369,6 @@ WEBP_TSAN_IGNORE_FUNCTION void WebPInitAlphaProcessing(void) {
|
|||||||
#endif
|
#endif
|
||||||
#if defined(WEBP_USE_MIPS_DSP_R2)
|
#if defined(WEBP_USE_MIPS_DSP_R2)
|
||||||
if (VP8GetCPUInfo(kMIPSdspR2)) {
|
if (VP8GetCPUInfo(kMIPSdspR2)) {
|
||||||
VP8FiltersInitMIPSdspR2();
|
|
||||||
WebPInitAlphaProcessingMIPSdspR2();
|
WebPInitAlphaProcessingMIPSdspR2();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -373,6 +373,39 @@ extern void (*VP8PackRGB)(const uint8_t* r, const uint8_t* g, const uint8_t* b,
|
|||||||
// To be called first before using the above.
|
// To be called first before using the above.
|
||||||
WEBP_TSAN_IGNORE_FUNCTION void VP8EncDspARGBInit(void);
|
WEBP_TSAN_IGNORE_FUNCTION void VP8EncDspARGBInit(void);
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Filter functions
|
||||||
|
|
||||||
|
typedef enum { // Filter types.
|
||||||
|
WEBP_FILTER_NONE = 0,
|
||||||
|
WEBP_FILTER_HORIZONTAL,
|
||||||
|
WEBP_FILTER_VERTICAL,
|
||||||
|
WEBP_FILTER_GRADIENT,
|
||||||
|
WEBP_FILTER_LAST = WEBP_FILTER_GRADIENT + 1, // end marker
|
||||||
|
WEBP_FILTER_BEST, // meta-types
|
||||||
|
WEBP_FILTER_FAST
|
||||||
|
} WEBP_FILTER_TYPE;
|
||||||
|
|
||||||
|
typedef void (*WebPFilterFunc)(const uint8_t* in, int width, int height,
|
||||||
|
int stride, uint8_t* out);
|
||||||
|
typedef void (*WebPUnfilterFunc)(int width, int height, int stride,
|
||||||
|
int row, int num_rows, uint8_t* data);
|
||||||
|
|
||||||
|
// Filter the given data using the given predictor.
|
||||||
|
// 'in' corresponds to a 2-dimensional pixel array of size (stride * height)
|
||||||
|
// in raster order.
|
||||||
|
// 'stride' is number of bytes per scan line (with possible padding).
|
||||||
|
// 'out' should be pre-allocated.
|
||||||
|
extern WebPFilterFunc WebPFilters[WEBP_FILTER_LAST];
|
||||||
|
|
||||||
|
// In-place reconstruct the original data from the given filtered data.
|
||||||
|
// The reconstruction will be done for 'num_rows' rows starting from 'row'
|
||||||
|
// (assuming rows upto 'row - 1' are already reconstructed).
|
||||||
|
extern WebPUnfilterFunc WebPUnfilters[WEBP_FILTER_LAST];
|
||||||
|
|
||||||
|
// To be called first before using the above.
|
||||||
|
WEBP_TSAN_IGNORE_FUNCTION void VP8FiltersInit(void);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
#endif
|
#endif
|
||||||
|
234
src/dsp/filters.c
Normal file
234
src/dsp/filters.c
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
// Copyright 2011 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.
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Spatial prediction using various filters
|
||||||
|
//
|
||||||
|
// Author: Urvang (urvang@google.com)
|
||||||
|
|
||||||
|
#include "./dsp.h"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Helpful macro.
|
||||||
|
|
||||||
|
# define SANITY_CHECK(in, out) \
|
||||||
|
assert(in != NULL); \
|
||||||
|
assert(out != NULL); \
|
||||||
|
assert(width > 0); \
|
||||||
|
assert(height > 0); \
|
||||||
|
assert(stride >= width); \
|
||||||
|
assert(row >= 0 && num_rows > 0 && row + num_rows <= height); \
|
||||||
|
(void)height; // Silence unused warning.
|
||||||
|
|
||||||
|
static WEBP_INLINE void PredictLine(const uint8_t* src, const uint8_t* pred,
|
||||||
|
uint8_t* dst, int length, int inverse) {
|
||||||
|
int i;
|
||||||
|
if (inverse) {
|
||||||
|
for (i = 0; i < length; ++i) dst[i] = src[i] + pred[i];
|
||||||
|
} else {
|
||||||
|
for (i = 0; i < length; ++i) dst[i] = src[i] - pred[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Horizontal filter.
|
||||||
|
|
||||||
|
static WEBP_INLINE void DoHorizontalFilter(const uint8_t* in,
|
||||||
|
int width, int height, int stride,
|
||||||
|
int row, int num_rows,
|
||||||
|
int inverse, uint8_t* out) {
|
||||||
|
const uint8_t* preds;
|
||||||
|
const size_t start_offset = row * stride;
|
||||||
|
const int last_row = row + num_rows;
|
||||||
|
SANITY_CHECK(in, out);
|
||||||
|
in += start_offset;
|
||||||
|
out += start_offset;
|
||||||
|
preds = inverse ? out : in;
|
||||||
|
|
||||||
|
if (row == 0) {
|
||||||
|
// Leftmost pixel is the same as input for topmost scanline.
|
||||||
|
out[0] = in[0];
|
||||||
|
PredictLine(in + 1, preds, out + 1, width - 1, inverse);
|
||||||
|
row = 1;
|
||||||
|
preds += stride;
|
||||||
|
in += stride;
|
||||||
|
out += stride;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter line-by-line.
|
||||||
|
while (row < last_row) {
|
||||||
|
// Leftmost pixel is predicted from above.
|
||||||
|
PredictLine(in, preds - stride, out, 1, inverse);
|
||||||
|
PredictLine(in + 1, preds, out + 1, width - 1, inverse);
|
||||||
|
++row;
|
||||||
|
preds += stride;
|
||||||
|
in += stride;
|
||||||
|
out += stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Vertical filter.
|
||||||
|
|
||||||
|
static WEBP_INLINE void DoVerticalFilter(const uint8_t* in,
|
||||||
|
int width, int height, int stride,
|
||||||
|
int row, int num_rows,
|
||||||
|
int inverse, uint8_t* out) {
|
||||||
|
const uint8_t* preds;
|
||||||
|
const size_t start_offset = row * stride;
|
||||||
|
const int last_row = row + num_rows;
|
||||||
|
SANITY_CHECK(in, out);
|
||||||
|
in += start_offset;
|
||||||
|
out += start_offset;
|
||||||
|
preds = inverse ? out : in;
|
||||||
|
|
||||||
|
if (row == 0) {
|
||||||
|
// Very first top-left pixel is copied.
|
||||||
|
out[0] = in[0];
|
||||||
|
// Rest of top scan-line is left-predicted.
|
||||||
|
PredictLine(in + 1, preds, out + 1, width - 1, inverse);
|
||||||
|
row = 1;
|
||||||
|
in += stride;
|
||||||
|
out += stride;
|
||||||
|
} else {
|
||||||
|
// We are starting from in-between. Make sure 'preds' points to prev row.
|
||||||
|
preds -= stride;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter line-by-line.
|
||||||
|
while (row < last_row) {
|
||||||
|
PredictLine(in, preds, out, width, inverse);
|
||||||
|
++row;
|
||||||
|
preds += stride;
|
||||||
|
in += stride;
|
||||||
|
out += stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Gradient filter.
|
||||||
|
|
||||||
|
static WEBP_INLINE int GradientPredictor(uint8_t a, uint8_t b, uint8_t c) {
|
||||||
|
const int g = a + b - c;
|
||||||
|
return ((g & ~0xff) == 0) ? g : (g < 0) ? 0 : 255; // clip to 8bit
|
||||||
|
}
|
||||||
|
|
||||||
|
static WEBP_INLINE void DoGradientFilter(const uint8_t* in,
|
||||||
|
int width, int height, int stride,
|
||||||
|
int row, int num_rows,
|
||||||
|
int inverse, uint8_t* out) {
|
||||||
|
const uint8_t* preds;
|
||||||
|
const size_t start_offset = row * stride;
|
||||||
|
const int last_row = row + num_rows;
|
||||||
|
SANITY_CHECK(in, out);
|
||||||
|
in += start_offset;
|
||||||
|
out += start_offset;
|
||||||
|
preds = inverse ? out : in;
|
||||||
|
|
||||||
|
// left prediction for top scan-line
|
||||||
|
if (row == 0) {
|
||||||
|
out[0] = in[0];
|
||||||
|
PredictLine(in + 1, preds, out + 1, width - 1, inverse);
|
||||||
|
row = 1;
|
||||||
|
preds += stride;
|
||||||
|
in += stride;
|
||||||
|
out += stride;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter line-by-line.
|
||||||
|
while (row < last_row) {
|
||||||
|
int w;
|
||||||
|
// leftmost pixel: predict from above.
|
||||||
|
PredictLine(in, preds - stride, out, 1, inverse);
|
||||||
|
for (w = 1; w < width; ++w) {
|
||||||
|
const int pred = GradientPredictor(preds[w - 1],
|
||||||
|
preds[w - stride],
|
||||||
|
preds[w - stride - 1]);
|
||||||
|
out[w] = in[w] + (inverse ? pred : -pred);
|
||||||
|
}
|
||||||
|
++row;
|
||||||
|
preds += stride;
|
||||||
|
in += stride;
|
||||||
|
out += stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef SANITY_CHECK
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static void HorizontalFilter(const uint8_t* data, int width, int height,
|
||||||
|
int stride, uint8_t* filtered_data) {
|
||||||
|
DoHorizontalFilter(data, width, height, stride, 0, height, 0, filtered_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void VerticalFilter(const uint8_t* data, int width, int height,
|
||||||
|
int stride, uint8_t* filtered_data) {
|
||||||
|
DoVerticalFilter(data, width, height, stride, 0, height, 0, filtered_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void GradientFilter(const uint8_t* data, int width, int height,
|
||||||
|
int stride, uint8_t* filtered_data) {
|
||||||
|
DoGradientFilter(data, width, height, stride, 0, height, 0, filtered_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static void VerticalUnfilter(int width, int height, int stride, int row,
|
||||||
|
int num_rows, uint8_t* data) {
|
||||||
|
DoVerticalFilter(data, width, height, stride, row, num_rows, 1, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void HorizontalUnfilter(int width, int height, int stride, int row,
|
||||||
|
int num_rows, uint8_t* data) {
|
||||||
|
DoHorizontalFilter(data, width, height, stride, row, num_rows, 1, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GradientUnfilter(int width, int height, int stride, int row,
|
||||||
|
int num_rows, uint8_t* data) {
|
||||||
|
DoGradientFilter(data, width, height, stride, row, num_rows, 1, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Init function
|
||||||
|
|
||||||
|
WebPFilterFunc WebPFilters[WEBP_FILTER_LAST];
|
||||||
|
WebPUnfilterFunc WebPUnfilters[WEBP_FILTER_LAST];
|
||||||
|
|
||||||
|
extern void VP8FiltersInitMIPSdspR2(void);
|
||||||
|
|
||||||
|
static volatile VP8CPUInfo filters_last_cpuinfo_used =
|
||||||
|
(VP8CPUInfo)&filters_last_cpuinfo_used;
|
||||||
|
|
||||||
|
WEBP_TSAN_IGNORE_FUNCTION void VP8FiltersInit(void) {
|
||||||
|
if (filters_last_cpuinfo_used == VP8GetCPUInfo) return;
|
||||||
|
|
||||||
|
WebPUnfilters[WEBP_FILTER_NONE] = NULL;
|
||||||
|
WebPUnfilters[WEBP_FILTER_HORIZONTAL] = HorizontalUnfilter;
|
||||||
|
WebPUnfilters[WEBP_FILTER_VERTICAL] = VerticalUnfilter;
|
||||||
|
WebPUnfilters[WEBP_FILTER_GRADIENT] = GradientUnfilter;
|
||||||
|
|
||||||
|
WebPFilters[WEBP_FILTER_NONE] = NULL;
|
||||||
|
WebPFilters[WEBP_FILTER_HORIZONTAL] = HorizontalFilter;
|
||||||
|
WebPFilters[WEBP_FILTER_VERTICAL] = VerticalFilter;
|
||||||
|
WebPFilters[WEBP_FILTER_GRADIENT] = GradientFilter;
|
||||||
|
|
||||||
|
if (VP8GetCPUInfo != NULL) {
|
||||||
|
#if defined(WEBP_USE_MIPS_DSP_R2)
|
||||||
|
if (VP8GetCPUInfo(kMIPSdspR2)) {
|
||||||
|
VP8FiltersInitMIPSdspR2();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
filters_last_cpuinfo_used = VP8GetCPUInfo;
|
||||||
|
}
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
#if defined(WEBP_USE_MIPS_DSP_R2)
|
#if defined(WEBP_USE_MIPS_DSP_R2)
|
||||||
|
|
||||||
#include "../utils/filters.h"
|
#include "../dsp/dsp.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "./vp8enci.h"
|
#include "./vp8enci.h"
|
||||||
|
#include "../dsp/dsp.h"
|
||||||
#include "../utils/filters.h"
|
#include "../utils/filters.h"
|
||||||
#include "../utils/quant_levels.h"
|
#include "../utils/quant_levels.h"
|
||||||
#include "../utils/utils.h"
|
#include "../utils/utils.h"
|
||||||
@ -208,8 +209,9 @@ static uint32_t GetFilterMap(const uint8_t* alpha, int width, int height,
|
|||||||
const int kMaxColorsForFilterNone = 192;
|
const int kMaxColorsForFilterNone = 192;
|
||||||
const int num_colors = GetNumColors(alpha, width, height, width);
|
const int num_colors = GetNumColors(alpha, width, height, width);
|
||||||
// For low number of colors, NONE yields better compression.
|
// For low number of colors, NONE yields better compression.
|
||||||
filter = (num_colors <= kMinColorsForFilterNone) ? WEBP_FILTER_NONE :
|
filter = (num_colors <= kMinColorsForFilterNone)
|
||||||
EstimateBestFilter(alpha, width, height, width);
|
? WEBP_FILTER_NONE
|
||||||
|
: WebPEstimateBestFilter(alpha, width, height, width);
|
||||||
bit_map |= 1 << filter;
|
bit_map |= 1 << filter;
|
||||||
// For large number of colors, try FILTER_NONE in addition to the best
|
// For large number of colors, try FILTER_NONE in addition to the best
|
||||||
// filter as well.
|
// filter as well.
|
||||||
@ -240,6 +242,7 @@ static int ApplyFiltersAndEncode(const uint8_t* alpha, int width, int height,
|
|||||||
uint32_t try_map =
|
uint32_t try_map =
|
||||||
GetFilterMap(alpha, width, height, filter, effort_level);
|
GetFilterMap(alpha, width, height, filter, effort_level);
|
||||||
InitFilterTrial(&best);
|
InitFilterTrial(&best);
|
||||||
|
|
||||||
if (try_map != FILTER_TRY_NONE) {
|
if (try_map != FILTER_TRY_NONE) {
|
||||||
uint8_t* filtered_alpha = (uint8_t*)WebPSafeMalloc(1ULL, data_size);
|
uint8_t* filtered_alpha = (uint8_t*)WebPSafeMalloc(1ULL, data_size);
|
||||||
if (filtered_alpha == NULL) return 0;
|
if (filtered_alpha == NULL) return 0;
|
||||||
@ -326,6 +329,7 @@ static int EncodeAlpha(VP8Encoder* const enc,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ok) {
|
if (ok) {
|
||||||
|
VP8FiltersInit();
|
||||||
ok = ApplyFiltersAndEncode(quant_alpha, width, height, data_size, method,
|
ok = ApplyFiltersAndEncode(quant_alpha, width, height, data_size, method,
|
||||||
filter, reduce_levels, effort_level, output,
|
filter, reduce_levels, effort_level, output,
|
||||||
output_size, pic->stats);
|
output_size, pic->stats);
|
||||||
|
@ -7,200 +7,27 @@
|
|||||||
// be found in the AUTHORS file in the root of the source tree.
|
// be found in the AUTHORS file in the root of the source tree.
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
//
|
//
|
||||||
// Spatial prediction using various filters
|
// filter estimation
|
||||||
//
|
//
|
||||||
// Author: Urvang (urvang@google.com)
|
// Author: Urvang (urvang@google.com)
|
||||||
|
|
||||||
#include "./filters.h"
|
#include "./filters.h"
|
||||||
#include <assert.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Helpful macro.
|
|
||||||
|
|
||||||
# define SANITY_CHECK(in, out) \
|
|
||||||
assert(in != NULL); \
|
|
||||||
assert(out != NULL); \
|
|
||||||
assert(width > 0); \
|
|
||||||
assert(height > 0); \
|
|
||||||
assert(stride >= width); \
|
|
||||||
assert(row >= 0 && num_rows > 0 && row + num_rows <= height); \
|
|
||||||
(void)height; // Silence unused warning.
|
|
||||||
|
|
||||||
static WEBP_INLINE void PredictLine(const uint8_t* src, const uint8_t* pred,
|
|
||||||
uint8_t* dst, int length, int inverse) {
|
|
||||||
int i;
|
|
||||||
if (inverse) {
|
|
||||||
for (i = 0; i < length; ++i) dst[i] = src[i] + pred[i];
|
|
||||||
} else {
|
|
||||||
for (i = 0; i < length; ++i) dst[i] = src[i] - pred[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Horizontal filter.
|
|
||||||
|
|
||||||
static WEBP_INLINE void DoHorizontalFilter(const uint8_t* in,
|
|
||||||
int width, int height, int stride,
|
|
||||||
int row, int num_rows,
|
|
||||||
int inverse, uint8_t* out) {
|
|
||||||
const uint8_t* preds;
|
|
||||||
const size_t start_offset = row * stride;
|
|
||||||
const int last_row = row + num_rows;
|
|
||||||
SANITY_CHECK(in, out);
|
|
||||||
in += start_offset;
|
|
||||||
out += start_offset;
|
|
||||||
preds = inverse ? out : in;
|
|
||||||
|
|
||||||
if (row == 0) {
|
|
||||||
// Leftmost pixel is the same as input for topmost scanline.
|
|
||||||
out[0] = in[0];
|
|
||||||
PredictLine(in + 1, preds, out + 1, width - 1, inverse);
|
|
||||||
row = 1;
|
|
||||||
preds += stride;
|
|
||||||
in += stride;
|
|
||||||
out += stride;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter line-by-line.
|
|
||||||
while (row < last_row) {
|
|
||||||
// Leftmost pixel is predicted from above.
|
|
||||||
PredictLine(in, preds - stride, out, 1, inverse);
|
|
||||||
PredictLine(in + 1, preds, out + 1, width - 1, inverse);
|
|
||||||
++row;
|
|
||||||
preds += stride;
|
|
||||||
in += stride;
|
|
||||||
out += stride;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void HorizontalFilter(const uint8_t* data, int width, int height,
|
|
||||||
int stride, uint8_t* filtered_data) {
|
|
||||||
DoHorizontalFilter(data, width, height, stride, 0, height, 0, filtered_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void HorizontalUnfilter(int width, int height, int stride, int row,
|
|
||||||
int num_rows, uint8_t* data) {
|
|
||||||
DoHorizontalFilter(data, width, height, stride, row, num_rows, 1, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Vertical filter.
|
|
||||||
|
|
||||||
static WEBP_INLINE void DoVerticalFilter(const uint8_t* in,
|
|
||||||
int width, int height, int stride,
|
|
||||||
int row, int num_rows,
|
|
||||||
int inverse, uint8_t* out) {
|
|
||||||
const uint8_t* preds;
|
|
||||||
const size_t start_offset = row * stride;
|
|
||||||
const int last_row = row + num_rows;
|
|
||||||
SANITY_CHECK(in, out);
|
|
||||||
in += start_offset;
|
|
||||||
out += start_offset;
|
|
||||||
preds = inverse ? out : in;
|
|
||||||
|
|
||||||
if (row == 0) {
|
|
||||||
// Very first top-left pixel is copied.
|
|
||||||
out[0] = in[0];
|
|
||||||
// Rest of top scan-line is left-predicted.
|
|
||||||
PredictLine(in + 1, preds, out + 1, width - 1, inverse);
|
|
||||||
row = 1;
|
|
||||||
in += stride;
|
|
||||||
out += stride;
|
|
||||||
} else {
|
|
||||||
// We are starting from in-between. Make sure 'preds' points to prev row.
|
|
||||||
preds -= stride;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter line-by-line.
|
|
||||||
while (row < last_row) {
|
|
||||||
PredictLine(in, preds, out, width, inverse);
|
|
||||||
++row;
|
|
||||||
preds += stride;
|
|
||||||
in += stride;
|
|
||||||
out += stride;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void VerticalFilter(const uint8_t* data, int width, int height,
|
|
||||||
int stride, uint8_t* filtered_data) {
|
|
||||||
DoVerticalFilter(data, width, height, stride, 0, height, 0, filtered_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void VerticalUnfilter(int width, int height, int stride, int row,
|
|
||||||
int num_rows, uint8_t* data) {
|
|
||||||
DoVerticalFilter(data, width, height, stride, row, num_rows, 1, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Gradient filter.
|
|
||||||
|
|
||||||
static WEBP_INLINE int GradientPredictor(uint8_t a, uint8_t b, uint8_t c) {
|
|
||||||
const int g = a + b - c;
|
|
||||||
return ((g & ~0xff) == 0) ? g : (g < 0) ? 0 : 255; // clip to 8bit
|
|
||||||
}
|
|
||||||
|
|
||||||
static WEBP_INLINE void DoGradientFilter(const uint8_t* in,
|
|
||||||
int width, int height, int stride,
|
|
||||||
int row, int num_rows,
|
|
||||||
int inverse, uint8_t* out) {
|
|
||||||
const uint8_t* preds;
|
|
||||||
const size_t start_offset = row * stride;
|
|
||||||
const int last_row = row + num_rows;
|
|
||||||
SANITY_CHECK(in, out);
|
|
||||||
in += start_offset;
|
|
||||||
out += start_offset;
|
|
||||||
preds = inverse ? out : in;
|
|
||||||
|
|
||||||
// left prediction for top scan-line
|
|
||||||
if (row == 0) {
|
|
||||||
out[0] = in[0];
|
|
||||||
PredictLine(in + 1, preds, out + 1, width - 1, inverse);
|
|
||||||
row = 1;
|
|
||||||
preds += stride;
|
|
||||||
in += stride;
|
|
||||||
out += stride;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter line-by-line.
|
|
||||||
while (row < last_row) {
|
|
||||||
int w;
|
|
||||||
// leftmost pixel: predict from above.
|
|
||||||
PredictLine(in, preds - stride, out, 1, inverse);
|
|
||||||
for (w = 1; w < width; ++w) {
|
|
||||||
const int pred = GradientPredictor(preds[w - 1],
|
|
||||||
preds[w - stride],
|
|
||||||
preds[w - stride - 1]);
|
|
||||||
out[w] = in[w] + (inverse ? pred : -pred);
|
|
||||||
}
|
|
||||||
++row;
|
|
||||||
preds += stride;
|
|
||||||
in += stride;
|
|
||||||
out += stride;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void GradientFilter(const uint8_t* data, int width, int height,
|
|
||||||
int stride, uint8_t* filtered_data) {
|
|
||||||
DoGradientFilter(data, width, height, stride, 0, height, 0, filtered_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void GradientUnfilter(int width, int height, int stride, int row,
|
|
||||||
int num_rows, uint8_t* data) {
|
|
||||||
DoGradientFilter(data, width, height, stride, row, num_rows, 1, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
#undef SANITY_CHECK
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Quick estimate of a potentially interesting filter mode to try.
|
// Quick estimate of a potentially interesting filter mode to try.
|
||||||
|
|
||||||
#define SMAX 16
|
#define SMAX 16
|
||||||
#define SDIFF(a, b) (abs((a) - (b)) >> 4) // Scoring diff, in [0..SMAX)
|
#define SDIFF(a, b) (abs((a) - (b)) >> 4) // Scoring diff, in [0..SMAX)
|
||||||
|
|
||||||
WEBP_FILTER_TYPE EstimateBestFilter(const uint8_t* data,
|
static WEBP_INLINE int GradientPredictor(uint8_t a, uint8_t b, uint8_t c) {
|
||||||
int width, int height, int stride) {
|
const int g = a + b - c;
|
||||||
|
return ((g & ~0xff) == 0) ? g : (g < 0) ? 0 : 255; // clip to 8bit
|
||||||
|
}
|
||||||
|
|
||||||
|
WEBP_FILTER_TYPE WebPEstimateBestFilter(const uint8_t* data,
|
||||||
|
int width, int height, int stride) {
|
||||||
int i, j;
|
int i, j;
|
||||||
int bins[WEBP_FILTER_LAST][SMAX];
|
int bins[WEBP_FILTER_LAST][SMAX];
|
||||||
memset(bins, 0, sizeof(bins));
|
memset(bins, 0, sizeof(bins));
|
||||||
@ -247,20 +74,3 @@ WEBP_FILTER_TYPE EstimateBestFilter(const uint8_t* data,
|
|||||||
#undef SDIFF
|
#undef SDIFF
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
WebPFilterFunc WebPFilters[WEBP_FILTER_LAST] = {
|
|
||||||
NULL, // WEBP_FILTER_NONE
|
|
||||||
HorizontalFilter, // WEBP_FILTER_HORIZONTAL
|
|
||||||
VerticalFilter, // WEBP_FILTER_VERTICAL
|
|
||||||
GradientFilter // WEBP_FILTER_GRADIENT
|
|
||||||
};
|
|
||||||
|
|
||||||
WebPUnfilterFunc WebPUnfilters[WEBP_FILTER_LAST] = {
|
|
||||||
NULL, // WEBP_FILTER_NONE
|
|
||||||
HorizontalUnfilter, // WEBP_FILTER_HORIZONTAL
|
|
||||||
VerticalUnfilter, // WEBP_FILTER_VERTICAL
|
|
||||||
GradientUnfilter // WEBP_FILTER_GRADIENT
|
|
||||||
};
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
|
@ -15,42 +15,15 @@
|
|||||||
#define WEBP_UTILS_FILTERS_H_
|
#define WEBP_UTILS_FILTERS_H_
|
||||||
|
|
||||||
#include "../webp/types.h"
|
#include "../webp/types.h"
|
||||||
|
#include "../dsp/dsp.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Filters.
|
|
||||||
typedef enum {
|
|
||||||
WEBP_FILTER_NONE = 0,
|
|
||||||
WEBP_FILTER_HORIZONTAL,
|
|
||||||
WEBP_FILTER_VERTICAL,
|
|
||||||
WEBP_FILTER_GRADIENT,
|
|
||||||
WEBP_FILTER_LAST = WEBP_FILTER_GRADIENT + 1, // end marker
|
|
||||||
WEBP_FILTER_BEST,
|
|
||||||
WEBP_FILTER_FAST
|
|
||||||
} WEBP_FILTER_TYPE;
|
|
||||||
|
|
||||||
typedef void (*WebPFilterFunc)(const uint8_t* in, int width, int height,
|
|
||||||
int stride, uint8_t* out);
|
|
||||||
typedef void (*WebPUnfilterFunc)(int width, int height, int stride,
|
|
||||||
int row, int num_rows, uint8_t* data);
|
|
||||||
|
|
||||||
// Filter the given data using the given predictor.
|
|
||||||
// 'in' corresponds to a 2-dimensional pixel array of size (stride * height)
|
|
||||||
// in raster order.
|
|
||||||
// 'stride' is number of bytes per scan line (with possible padding).
|
|
||||||
// 'out' should be pre-allocated.
|
|
||||||
extern WebPFilterFunc WebPFilters[WEBP_FILTER_LAST];
|
|
||||||
|
|
||||||
// In-place reconstruct the original data from the given filtered data.
|
|
||||||
// The reconstruction will be done for 'num_rows' rows starting from 'row'
|
|
||||||
// (assuming rows upto 'row - 1' are already reconstructed).
|
|
||||||
extern WebPUnfilterFunc WebPUnfilters[WEBP_FILTER_LAST];
|
|
||||||
|
|
||||||
// Fast estimate of a potentially good filter.
|
// Fast estimate of a potentially good filter.
|
||||||
WEBP_FILTER_TYPE EstimateBestFilter(const uint8_t* data,
|
WEBP_FILTER_TYPE WebPEstimateBestFilter(const uint8_t* data,
|
||||||
int width, int height, int stride);
|
int width, int height, int stride);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user