Support arbitrary bicubic filtering in raster pipeline

Guarded for chromium and Google3

Bug: chromium:1302855
Change-Id: If9b085a24d5f03e6971506c98463aa8c09100a67
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/516016
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
This commit is contained in:
Brian Osman 2022-03-04 11:34:04 -05:00 committed by SkCQ
parent d8590052e8
commit 22c94544b3
3 changed files with 65 additions and 8 deletions

View File

@ -126,6 +126,8 @@ struct SkRasterPipeline_GatherCtx {
int stride;
float width;
float height;
float weights[16]; // for bicubic and bicubic_clamp_8888
};
// State shared by save_xy, accumulate, and bilinear_* / bicubic_*.
@ -136,6 +138,8 @@ struct SkRasterPipeline_SamplerCtx {
float fy[SkRasterPipeline_kMaxStride];
float scalex[SkRasterPipeline_kMaxStride];
float scaley[SkRasterPipeline_kMaxStride];
float weights[16]; // for bicubic_[np][13][xy]
};
struct SkRasterPipeline_TileCtx {

View File

@ -2651,9 +2651,14 @@ STAGE(bilinear_py, SkRasterPipeline_SamplerCtx* ctx) { bilinear_y<+1>(ctx, &g);
// In bicubic interpolation, the 16 pixels and +/- 0.5 and +/- 1.5 offsets from the sample
// pixel center are combined with a non-uniform cubic filter, with higher values near the center.
//
// We break this function into two parts, one for near 0.5 offsets and one for far 1.5 offsets.
// See GrCubicEffect for details of this particular filter.
// This helper computes the total weight along one axis (our bicubic filter is separable), given one
// column of the sampling matrix, and a fractional pixel offset. See SkCubicResampler for details.
SI F bicubic_wts(F t, float A, float B, float C, float D) {
return mad(t, mad(t, mad(t, D, C), B), A);
}
#if defined(SK_LEGACY_RP_BICUBIC)
SI F bicubic_near(F t) {
// 1/18 + 9/18t + 27/18t^2 - 21/18t^3 == t ( t ( -21/18t + 27/18) + 9/18) + 1/18
return mad(t, mad(t, mad((-21/18.0f), t, (27/18.0f)), (9/18.0f)), (1/18.0f));
@ -2662,6 +2667,7 @@ SI F bicubic_far(F t) {
// 0/18 + 0/18*t - 6/18t^2 + 7/18t^3 == t^2 (7/18t - 6/18)
return (t*t)*mad((7/18.0f), t, (-6/18.0f));
}
#endif
template <int kScale>
SI void bicubic_x(SkRasterPipeline_SamplerCtx* ctx, F* x) {
@ -2669,10 +2675,18 @@ SI void bicubic_x(SkRasterPipeline_SamplerCtx* ctx, F* x) {
F fx = sk_unaligned_load<F>(ctx->fx);
F scalex;
#if defined(SK_LEGACY_RP_BICUBIC)
if (kScale == -3) { scalex = bicubic_far (1.0f - fx); }
if (kScale == -1) { scalex = bicubic_near(1.0f - fx); }
if (kScale == +1) { scalex = bicubic_near( fx); }
if (kScale == +3) { scalex = bicubic_far ( fx); }
#else
const float* w = ctx->weights;
if (kScale == -3) { scalex = bicubic_wts(fx, w[0], w[4], w[ 8], w[12]); }
if (kScale == -1) { scalex = bicubic_wts(fx, w[1], w[5], w[ 9], w[13]); }
if (kScale == +1) { scalex = bicubic_wts(fx, w[2], w[6], w[10], w[14]); }
if (kScale == +3) { scalex = bicubic_wts(fx, w[3], w[7], w[11], w[15]); }
#endif
sk_unaligned_store(ctx->scalex, scalex);
}
template <int kScale>
@ -2681,10 +2695,18 @@ SI void bicubic_y(SkRasterPipeline_SamplerCtx* ctx, F* y) {
F fy = sk_unaligned_load<F>(ctx->fy);
F scaley;
#if defined(SK_LEGACY_RP_BICUBIC)
if (kScale == -3) { scaley = bicubic_far (1.0f - fy); }
if (kScale == -1) { scaley = bicubic_near(1.0f - fy); }
if (kScale == +1) { scaley = bicubic_near( fy); }
if (kScale == +3) { scaley = bicubic_far ( fy); }
#else
const float* w = ctx->weights;
if (kScale == -3) { scaley = bicubic_wts(fy, w[0], w[4], w[ 8], w[12]); }
if (kScale == -1) { scaley = bicubic_wts(fy, w[1], w[5], w[ 9], w[13]); }
if (kScale == +1) { scaley = bicubic_wts(fy, w[2], w[6], w[10], w[14]); }
if (kScale == +3) { scaley = bicubic_wts(fy, w[3], w[7], w[11], w[15]); }
#endif
sk_unaligned_store(ctx->scaley, scaley);
}
@ -2788,8 +2810,20 @@ STAGE(bilinear, const SkRasterPipeline_SamplerCtx2* ctx) {
STAGE(bicubic, SkRasterPipeline_SamplerCtx2* ctx) {
F x = r, fx = fract(x + 0.5f),
y = g, fy = fract(y + 0.5f);
#if defined(SK_LEGACY_RP_BICUBIC)
const F wx[] = { bicubic_far(1-fx), bicubic_near(1-fx), bicubic_near(fx), bicubic_far(fx) };
const F wy[] = { bicubic_far(1-fy), bicubic_near(1-fy), bicubic_near(fy), bicubic_far(fy) };
#else
const float* w = ctx->weights;
const F wx[] = {bicubic_wts(fx, w[0], w[4], w[ 8], w[12]),
bicubic_wts(fx, w[1], w[5], w[ 9], w[13]),
bicubic_wts(fx, w[2], w[6], w[10], w[14]),
bicubic_wts(fx, w[3], w[7], w[11], w[15])};
const F wy[] = {bicubic_wts(fy, w[0], w[4], w[ 8], w[12]),
bicubic_wts(fy, w[1], w[5], w[ 9], w[13]),
bicubic_wts(fy, w[2], w[6], w[10], w[14]),
bicubic_wts(fy, w[3], w[7], w[11], w[15])};
#endif
sampler(ctx, x,y, wx,wy, &r,&g,&b,&a);
}
@ -2850,6 +2884,7 @@ STAGE(bicubic_clamp_8888, const SkRasterPipeline_GatherCtx* ctx) {
// We'll accumulate the color of all four samples into {r,g,b,a} directly.
r = g = b = a = 0;
#if defined(SK_LEGACY_RP_BICUBIC)
const F scaley[4] = {
bicubic_far (1.0f - fy), bicubic_near(1.0f - fy),
bicubic_near( fy), bicubic_far ( fy),
@ -2858,6 +2893,17 @@ STAGE(bicubic_clamp_8888, const SkRasterPipeline_GatherCtx* ctx) {
bicubic_far (1.0f - fx), bicubic_near(1.0f - fx),
bicubic_near( fx), bicubic_far ( fx),
};
#else
const float* w = ctx->weights;
const F scaley[4] = {bicubic_wts(fy, w[0], w[4], w[ 8], w[12]),
bicubic_wts(fy, w[1], w[5], w[ 9], w[13]),
bicubic_wts(fy, w[2], w[6], w[10], w[14]),
bicubic_wts(fy, w[3], w[7], w[11], w[15])};
const F scalex[4] = {bicubic_wts(fx, w[0], w[4], w[ 8], w[12]),
bicubic_wts(fx, w[1], w[5], w[ 9], w[13]),
bicubic_wts(fx, w[2], w[6], w[10], w[14]),
bicubic_wts(fx, w[3], w[7], w[11], w[15])};
#endif
F sample_y = cy - 1.5f;
for (int yy = 0; yy <= 3; ++yy) {

View File

@ -174,12 +174,14 @@ bool SkImageShader::isOpaque() const {
fTileModeX != SkTileMode::kDecal && fTileModeY != SkTileMode::kDecal;
}
#if defined(SK_LEGACY_RP_BICUBIC)
constexpr SkCubicResampler kDefaultCubicResampler{1.0f/3, 1.0f/3};
static bool is_default_cubic_resampler(SkCubicResampler cubic) {
return SkScalarNearlyEqual(cubic.B, kDefaultCubicResampler.B) &&
SkScalarNearlyEqual(cubic.C, kDefaultCubicResampler.C);
}
#endif
#ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
@ -459,14 +461,14 @@ bool SkImageShader::doStages(const SkStageRec& rec, TransformShader* updater) co
SkASSERT(!needs_subset(fImage.get(), fSubset)); // TODO(skbug.com/12784)
// We only support certain sampling options in stages so far
auto sampling = fSampling;
if (sampling.useCubic) {
if (!is_default_cubic_resampler(sampling.cubic)) {
return false;
}
} else if (sampling.mipmap == SkMipmapMode::kLinear) {
if (sampling.mipmap == SkMipmapMode::kLinear) {
return false;
}
#if defined(SK_LEGACY_RP_BICUBIC)
if (sampling.useCubic && !is_default_cubic_resampler(sampling.cubic)) {
return false;
}
#endif
if (updater && (sampling.mipmap != SkMipmapMode::kNone)) {
// TODO: medium: recall RequestBitmap and update width/height accordingly
@ -510,6 +512,9 @@ bool SkImageShader::doStages(const SkStageRec& rec, TransformShader* updater) co
gather->stride = pm.rowBytesAsPixels();
gather->width = pm.width();
gather->height = pm.height();
if (sampling.useCubic) {
CubicResamplerMatrix(sampling.cubic.B, sampling.cubic.C).getColMajor(gather->weights);
}
auto limit_x = alloc->make<SkRasterPipeline_TileCtx>(),
limit_y = alloc->make<SkRasterPipeline_TileCtx>();
@ -693,6 +698,8 @@ bool SkImageShader::doStages(const SkStageRec& rec, TransformShader* updater) co
};
if (sampling.useCubic) {
CubicResamplerMatrix(sampling.cubic.B, sampling.cubic.C).getColMajor(sampler->weights);
p->append(SkRasterPipeline::save_xy, sampler);
sample(SkRasterPipeline::bicubic_n3x, SkRasterPipeline::bicubic_n3y);