blob: ca3e0930f27570ebff359e95370a81f59d189f5c [file] [log] [blame]
/*
* Copyright 2013 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkBitmapFilter_DEFINED
#define SkBitmapFilter_DEFINED
#include "SkFixed.h"
#include "SkMath.h"
#include "SkScalar.h"
#include "SkNx.h"
// size of the precomputed bitmap filter tables for high quality filtering.
// Used to precompute the shape of the filter kernel.
// Table size chosen from experiments to see where I could start to see a difference.
#define SKBITMAP_FILTER_TABLE_SIZE 128
class SkBitmapFilter {
public:
SkBitmapFilter(float width) : fWidth(width), fInvWidth(1.f/width) {
fPrecomputed = false;
fLookupMultiplier = this->invWidth() * (SKBITMAP_FILTER_TABLE_SIZE-1);
}
virtual ~SkBitmapFilter() {}
SkScalar lookupScalar(float x) const {
if (!fPrecomputed) {
precomputeTable();
}
int filter_idx = int(sk_float_abs(x * fLookupMultiplier));
SkASSERT(filter_idx < SKBITMAP_FILTER_TABLE_SIZE);
return fFilterTableScalar[filter_idx];
}
float width() const { return fWidth; }
float invWidth() const { return fInvWidth; }
virtual float evaluate(float x) const = 0;
virtual float evaluate_n(float val, float diff, int count, float* output) const {
float sum = 0;
for (int index = 0; index < count; index++) {
float filterValue = evaluate(val);
*output++ = filterValue;
sum += filterValue;
val += diff;
}
return sum;
}
protected:
float fWidth;
float fInvWidth;
float fLookupMultiplier;
mutable bool fPrecomputed;
mutable SkScalar fFilterTableScalar[SKBITMAP_FILTER_TABLE_SIZE];
private:
void precomputeTable() const {
fPrecomputed = true;
SkScalar *ftpScalar = fFilterTableScalar;
for (int x = 0; x < SKBITMAP_FILTER_TABLE_SIZE; ++x) {
float fx = ((float)x + .5f) * this->width() / SKBITMAP_FILTER_TABLE_SIZE;
float filter_value = evaluate(fx);
*ftpScalar++ = filter_value;
}
}
};
class SkMitchellFilter final : public SkBitmapFilter {
public:
SkMitchellFilter()
: INHERITED(2)
, fB(1.f / 3.f)
, fC(1.f / 3.f)
, fA1(-fB - 6*fC)
, fB1(6*fB + 30*fC)
, fC1(-12*fB - 48*fC)
, fD1(8*fB + 24*fC)
, fA2(12 - 9*fB - 6*fC)
, fB2(-18 + 12*fB + 6*fC)
, fD2(6 - 2*fB)
{}
float evaluate(float x) const override {
x = fabsf(x);
if (x > 2.f) {
return 0;
} else if (x > 1.f) {
return (((fA1 * x + fB1) * x + fC1) * x + fD1) * (1.f/6.f);
} else {
return ((fA2 * x + fB2) * x*x + fD2) * (1.f/6.f);
}
}
Sk4f evalcore_n(const Sk4f& val) const {
Sk4f x = val.abs();
Sk4f over2 = x > Sk4f(2);
Sk4f over1 = x > Sk4f(1);
Sk4f poly1 = (((Sk4f(fA1) * x + Sk4f(fB1)) * x + Sk4f(fC1)) * x + Sk4f(fD1))
* Sk4f(1.f/6.f);
Sk4f poly0 = ((Sk4f(fA2) * x + Sk4f(fB2)) * x*x + Sk4f(fD2)) * Sk4f(1.f/6.f);
return over2.thenElse(Sk4f(0), over1.thenElse(poly1, poly0));
}
float evaluate_n(float val, float diff, int count, float* output) const override {
Sk4f sum(0);
while (count >= 4) {
float v0 = val;
float v1 = val += diff;
float v2 = val += diff;
float v3 = val += diff;
val += diff;
Sk4f filterValue = evalcore_n(Sk4f(v0, v1, v2, v3));
filterValue.store(output);
output += 4;
sum = sum + filterValue;
count -= 4;
}
float sums[4];
sum.store(sums);
float result = sums[0] + sums[1] + sums[2] + sums[3];
result += INHERITED::evaluate_n(val, diff, count, output);
return result;
}
protected:
float fB, fC;
float fA1, fB1, fC1, fD1;
float fA2, fB2, fD2;
private:
typedef SkBitmapFilter INHERITED;
};
class SkGaussianFilter final : public SkBitmapFilter {
float fAlpha, fExpWidth;
public:
SkGaussianFilter(float a, float width = 2)
: SkBitmapFilter(width)
, fAlpha(a)
, fExpWidth(expf(-a * width * width))
{}
float evaluate(float x) const override {
return SkTMax(0.f, float(expf(-fAlpha*x*x) - fExpWidth));
}
};
class SkTriangleFilter final : public SkBitmapFilter {
public:
SkTriangleFilter(float width = 1) : SkBitmapFilter(width) {}
float evaluate(float x) const override {
return SkTMax(0.f, fWidth - fabsf(x));
}
};
class SkBoxFilter final : public SkBitmapFilter {
public:
SkBoxFilter(float width = 0.5f) : SkBitmapFilter(width) {}
float evaluate(float x) const override {
return (x >= -fWidth && x < fWidth) ? 1.0f : 0.0f;
}
};
class SkHammingFilter final : public SkBitmapFilter {
public:
SkHammingFilter(float width = 1) : SkBitmapFilter(width) {}
float evaluate(float x) const override {
if (x <= -fWidth || x >= fWidth) {
return 0.0f; // Outside of the window.
}
if (x > -FLT_EPSILON && x < FLT_EPSILON) {
return 1.0f; // Special case the sinc discontinuity at the origin.
}
const float xpi = x * static_cast<float>(SK_ScalarPI);
return ((sk_float_sin(xpi) / xpi) * // sinc(x)
(0.54f + 0.46f * sk_float_cos(xpi / fWidth))); // hamming(x)
}
};
class SkLanczosFilter final : public SkBitmapFilter {
public:
SkLanczosFilter(float width = 3.f) : SkBitmapFilter(width) {}
float evaluate(float x) const override {
if (x <= -fWidth || x >= fWidth) {
return 0.0f; // Outside of the window.
}
if (x > -FLT_EPSILON && x < FLT_EPSILON) {
return 1.0f; // Special case the discontinuity at the origin.
}
float xpi = x * static_cast<float>(SK_ScalarPI);
return (sk_float_sin(xpi) / xpi) * // sinc(x)
sk_float_sin(xpi / fWidth) / (xpi / fWidth); // sinc(x/fWidth)
}
};
#endif