/* * 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++ = SkFloatToScalar(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(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(SK_ScalarPI); return (sk_float_sin(xpi) / xpi) * // sinc(x) sk_float_sin(xpi / fWidth) / (xpi / fWidth); // sinc(x/fWidth) } }; #endif