blob: 8ef0f6e1ded91099a988de7147cc1106d7653fce [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 "SkMath.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);
}
SkFixed lookup(float x) const {
if (!fPrecomputed) {
precomputeTable();
}
int filter_idx = int(sk_float_abs(x * fLookupMultiplier));
SkASSERT(filter_idx < SKBITMAP_FILTER_TABLE_SIZE);
return fFilterTable[filter_idx];
}
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 ~SkBitmapFilter() {}
static SkBitmapFilter* Allocate();
protected:
float fWidth;
float fInvWidth;
float fLookupMultiplier;
mutable bool fPrecomputed;
mutable SkFixed fFilterTable[SKBITMAP_FILTER_TABLE_SIZE];
mutable SkScalar fFilterTableScalar[SKBITMAP_FILTER_TABLE_SIZE];
private:
void precomputeTable() const {
fPrecomputed = true;
SkFixed *ftp = fFilterTable;
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;
*ftp++ = SkFloatToFixed(filter_value);
}
}
};
class SkMitchellFilter: public SkBitmapFilter {
public:
SkMitchellFilter(float b, float c, float width=2.0f)
: SkBitmapFilter(width), B(b), C(c) {
}
virtual float evaluate(float x) const SK_OVERRIDE {
x = fabsf(x);
if (x > 2.f) {
return 0;
} else if (x > 1.f) {
return ((-B - 6*C) * x*x*x + (6*B + 30*C) * x*x +
(-12*B - 48*C) * x + (8*B + 24*C)) * (1.f/6.f);
} else {
return ((12 - 9*B - 6*C) * x*x*x +
(-18 + 12*B + 6*C) * x*x +
(6 - 2*B)) * (1.f/6.f);
}
}
protected:
float B, C;
};
class SkGaussianFilter: public SkBitmapFilter {
public:
SkGaussianFilter(float a, float width=2.0f)
: SkBitmapFilter(width), alpha(a), expWidth(expf(-alpha * width * width)) {
}
virtual float evaluate(float x) const SK_OVERRIDE {
return SkTMax(0.f, float(expf(-alpha*x*x) - expWidth));
}
protected:
float alpha, expWidth;
};
class SkTriangleFilter: public SkBitmapFilter {
public:
SkTriangleFilter(float width=1)
: SkBitmapFilter(width) {
}
virtual float evaluate(float x) const SK_OVERRIDE {
return SkTMax(0.f, fWidth - fabsf(x));
}
protected:
};
class SkBoxFilter: public SkBitmapFilter {
public:
SkBoxFilter(float width=0.5f)
: SkBitmapFilter(width) {
}
virtual float evaluate(float x) const SK_OVERRIDE {
return (x >= -fWidth && x < fWidth) ? 1.0f : 0.0f;
}
protected:
};
class SkHammingFilter: public SkBitmapFilter {
public:
SkHammingFilter(float width=1.f)
: SkBitmapFilter(width) {
}
virtual float evaluate(float x) const SK_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: public SkBitmapFilter {
public:
SkLanczosFilter(float width=3.f)
: SkBitmapFilter(width) {
}
virtual float evaluate(float x) const SK_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