humper@google.com | b088947 | 2013-07-09 21:37:14 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2013 Google Inc. |
| 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license that can be |
| 5 | * found in the LICENSE file. |
| 6 | */ |
| 7 | |
humper@google.com | b088947 | 2013-07-09 21:37:14 +0000 | [diff] [blame] | 8 | #ifndef SkBitmapFilter_DEFINED |
| 9 | #define SkBitmapFilter_DEFINED |
| 10 | |
bungeman | d3ebb48 | 2015-08-05 13:57:49 -0700 | [diff] [blame] | 11 | #include "SkFixed.h" |
humper@google.com | b088947 | 2013-07-09 21:37:14 +0000 | [diff] [blame] | 12 | #include "SkMath.h" |
bungeman | d3ebb48 | 2015-08-05 13:57:49 -0700 | [diff] [blame] | 13 | #include "SkScalar.h" |
humper@google.com | b088947 | 2013-07-09 21:37:14 +0000 | [diff] [blame] | 14 | |
caryclark | 368342c | 2016-01-12 10:44:02 -0800 | [diff] [blame] | 15 | #include "SkNx.h" |
caryclark | 368342c | 2016-01-12 10:44:02 -0800 | [diff] [blame] | 16 | |
humper@google.com | b088947 | 2013-07-09 21:37:14 +0000 | [diff] [blame] | 17 | // size of the precomputed bitmap filter tables for high quality filtering. |
| 18 | // Used to precompute the shape of the filter kernel. |
| 19 | // Table size chosen from experiments to see where I could start to see a difference. |
| 20 | |
humper@google.com | 87fbf4f | 2013-07-10 21:49:29 +0000 | [diff] [blame] | 21 | #define SKBITMAP_FILTER_TABLE_SIZE 128 |
humper@google.com | b088947 | 2013-07-09 21:37:14 +0000 | [diff] [blame] | 22 | |
| 23 | class SkBitmapFilter { |
reed | a36c710 | 2016-01-14 10:10:56 -0800 | [diff] [blame] | 24 | public: |
| 25 | SkBitmapFilter(float width) : fWidth(width), fInvWidth(1.f/width) { |
| 26 | fPrecomputed = false; |
| 27 | fLookupMultiplier = this->invWidth() * (SKBITMAP_FILTER_TABLE_SIZE-1); |
| 28 | } |
| 29 | virtual ~SkBitmapFilter() {} |
skia.committer@gmail.com | 9e1ec1a | 2013-07-10 07:00:58 +0000 | [diff] [blame] | 30 | |
reed | a36c710 | 2016-01-14 10:10:56 -0800 | [diff] [blame] | 31 | SkScalar lookupScalar(float x) const { |
| 32 | if (!fPrecomputed) { |
| 33 | precomputeTable(); |
| 34 | } |
| 35 | int filter_idx = int(sk_float_abs(x * fLookupMultiplier)); |
| 36 | SkASSERT(filter_idx < SKBITMAP_FILTER_TABLE_SIZE); |
| 37 | return fFilterTableScalar[filter_idx]; |
| 38 | } |
skia.committer@gmail.com | 9e1ec1a | 2013-07-10 07:00:58 +0000 | [diff] [blame] | 39 | |
reed | a36c710 | 2016-01-14 10:10:56 -0800 | [diff] [blame] | 40 | float width() const { return fWidth; } |
| 41 | float invWidth() const { return fInvWidth; } |
| 42 | virtual float evaluate(float x) const = 0; |
caryclark | 368342c | 2016-01-12 10:44:02 -0800 | [diff] [blame] | 43 | |
reed | a36c710 | 2016-01-14 10:10:56 -0800 | [diff] [blame] | 44 | virtual float evaluate_n(float val, float diff, int count, float* output) const { |
| 45 | float sum = 0; |
| 46 | for (int index = 0; index < count; index++) { |
| 47 | float filterValue = evaluate(val); |
| 48 | *output++ = filterValue; |
| 49 | sum += filterValue; |
| 50 | val += diff; |
| 51 | } |
| 52 | return sum; |
| 53 | } |
caryclark | 368342c | 2016-01-12 10:44:02 -0800 | [diff] [blame] | 54 | |
reed | a36c710 | 2016-01-14 10:10:56 -0800 | [diff] [blame] | 55 | protected: |
| 56 | float fWidth; |
| 57 | float fInvWidth; |
| 58 | float fLookupMultiplier; |
skia.committer@gmail.com | fa1bd5f | 2013-07-13 07:00:56 +0000 | [diff] [blame] | 59 | |
reed | a36c710 | 2016-01-14 10:10:56 -0800 | [diff] [blame] | 60 | mutable bool fPrecomputed; |
reed | a36c710 | 2016-01-14 10:10:56 -0800 | [diff] [blame] | 61 | mutable SkScalar fFilterTableScalar[SKBITMAP_FILTER_TABLE_SIZE]; |
humper@google.com | 9c96d4b | 2013-07-14 01:44:59 +0000 | [diff] [blame] | 62 | |
reed | a36c710 | 2016-01-14 10:10:56 -0800 | [diff] [blame] | 63 | private: |
| 64 | void precomputeTable() const { |
| 65 | fPrecomputed = true; |
reed | a36c710 | 2016-01-14 10:10:56 -0800 | [diff] [blame] | 66 | SkScalar *ftpScalar = fFilterTableScalar; |
| 67 | for (int x = 0; x < SKBITMAP_FILTER_TABLE_SIZE; ++x) { |
| 68 | float fx = ((float)x + .5f) * this->width() / SKBITMAP_FILTER_TABLE_SIZE; |
| 69 | float filter_value = evaluate(fx); |
| 70 | *ftpScalar++ = filter_value; |
reed | a36c710 | 2016-01-14 10:10:56 -0800 | [diff] [blame] | 71 | } |
| 72 | } |
humper@google.com | b088947 | 2013-07-09 21:37:14 +0000 | [diff] [blame] | 73 | }; |
| 74 | |
reed | a36c710 | 2016-01-14 10:10:56 -0800 | [diff] [blame] | 75 | class SkMitchellFilter final : public SkBitmapFilter { |
| 76 | public: |
reed | a36c710 | 2016-01-14 10:10:56 -0800 | [diff] [blame] | 77 | SkMitchellFilter() |
| 78 | : INHERITED(2) |
| 79 | , fB(1.f / 3.f) |
| 80 | , fC(1.f / 3.f) |
| 81 | , fA1(-fB - 6*fC) |
| 82 | , fB1(6*fB + 30*fC) |
| 83 | , fC1(-12*fB - 48*fC) |
| 84 | , fD1(8*fB + 24*fC) |
| 85 | , fA2(12 - 9*fB - 6*fC) |
| 86 | , fB2(-18 + 12*fB + 6*fC) |
| 87 | , fD2(6 - 2*fB) |
| 88 | {} |
skia.committer@gmail.com | 9e1ec1a | 2013-07-10 07:00:58 +0000 | [diff] [blame] | 89 | |
reed | a36c710 | 2016-01-14 10:10:56 -0800 | [diff] [blame] | 90 | float evaluate(float x) const override { |
| 91 | x = fabsf(x); |
| 92 | if (x > 2.f) { |
| 93 | return 0; |
| 94 | } else if (x > 1.f) { |
reed | a36c710 | 2016-01-14 10:10:56 -0800 | [diff] [blame] | 95 | return (((fA1 * x + fB1) * x + fC1) * x + fD1) * (1.f/6.f); |
reed | a36c710 | 2016-01-14 10:10:56 -0800 | [diff] [blame] | 96 | } else { |
reed | a36c710 | 2016-01-14 10:10:56 -0800 | [diff] [blame] | 97 | return ((fA2 * x + fB2) * x*x + fD2) * (1.f/6.f); |
reed | a36c710 | 2016-01-14 10:10:56 -0800 | [diff] [blame] | 98 | } |
| 99 | } |
caryclark | 368342c | 2016-01-12 10:44:02 -0800 | [diff] [blame] | 100 | |
reed | a36c710 | 2016-01-14 10:10:56 -0800 | [diff] [blame] | 101 | Sk4f evalcore_n(const Sk4f& val) const { |
caryclark | 6b14c6b | 2016-02-18 05:45:33 -0800 | [diff] [blame] | 102 | Sk4f x = val.abs(); |
reed | a36c710 | 2016-01-14 10:10:56 -0800 | [diff] [blame] | 103 | Sk4f over2 = x > Sk4f(2); |
| 104 | Sk4f over1 = x > Sk4f(1); |
| 105 | Sk4f poly1 = (((Sk4f(fA1) * x + Sk4f(fB1)) * x + Sk4f(fC1)) * x + Sk4f(fD1)) |
| 106 | * Sk4f(1.f/6.f); |
| 107 | Sk4f poly0 = ((Sk4f(fA2) * x + Sk4f(fB2)) * x*x + Sk4f(fD2)) * Sk4f(1.f/6.f); |
| 108 | return over2.thenElse(Sk4f(0), over1.thenElse(poly1, poly0)); |
| 109 | } |
caryclark | 368342c | 2016-01-12 10:44:02 -0800 | [diff] [blame] | 110 | |
reed | a36c710 | 2016-01-14 10:10:56 -0800 | [diff] [blame] | 111 | float evaluate_n(float val, float diff, int count, float* output) const override { |
| 112 | Sk4f sum(0); |
| 113 | while (count >= 4) { |
| 114 | float v0 = val; |
| 115 | float v1 = val += diff; |
| 116 | float v2 = val += diff; |
| 117 | float v3 = val += diff; |
| 118 | val += diff; |
| 119 | Sk4f filterValue = evalcore_n(Sk4f(v0, v1, v2, v3)); |
| 120 | filterValue.store(output); |
| 121 | output += 4; |
| 122 | sum = sum + filterValue; |
| 123 | count -= 4; |
| 124 | } |
| 125 | float sums[4]; |
| 126 | sum.store(sums); |
| 127 | float result = sums[0] + sums[1] + sums[2] + sums[3]; |
| 128 | result += INHERITED::evaluate_n(val, diff, count, output); |
| 129 | return result; |
| 130 | } |
caryclark | 368342c | 2016-01-12 10:44:02 -0800 | [diff] [blame] | 131 | |
humper@google.com | b088947 | 2013-07-09 21:37:14 +0000 | [diff] [blame] | 132 | protected: |
caryclark | 368342c | 2016-01-12 10:44:02 -0800 | [diff] [blame] | 133 | float fB, fC; |
| 134 | float fA1, fB1, fC1, fD1; |
| 135 | float fA2, fB2, fD2; |
reed | a36c710 | 2016-01-14 10:10:56 -0800 | [diff] [blame] | 136 | private: |
| 137 | typedef SkBitmapFilter INHERITED; |
humper@google.com | b088947 | 2013-07-09 21:37:14 +0000 | [diff] [blame] | 138 | }; |
| 139 | |
reed | a36c710 | 2016-01-14 10:10:56 -0800 | [diff] [blame] | 140 | class SkGaussianFilter final : public SkBitmapFilter { |
| 141 | float fAlpha, fExpWidth; |
skia.committer@gmail.com | 9e1ec1a | 2013-07-10 07:00:58 +0000 | [diff] [blame] | 142 | |
humper@google.com | 138ebc3 | 2013-07-19 20:20:04 +0000 | [diff] [blame] | 143 | public: |
reed | a36c710 | 2016-01-14 10:10:56 -0800 | [diff] [blame] | 144 | SkGaussianFilter(float a, float width = 2) |
| 145 | : SkBitmapFilter(width) |
| 146 | , fAlpha(a) |
| 147 | , fExpWidth(expf(-a * width * width)) |
| 148 | {} |
| 149 | |
| 150 | float evaluate(float x) const override { |
| 151 | return SkTMax(0.f, float(expf(-fAlpha*x*x) - fExpWidth)); |
humper@google.com | 138ebc3 | 2013-07-19 20:20:04 +0000 | [diff] [blame] | 152 | } |
reed | a36c710 | 2016-01-14 10:10:56 -0800 | [diff] [blame] | 153 | }; |
| 154 | |
| 155 | class SkTriangleFilter final : public SkBitmapFilter { |
| 156 | public: |
| 157 | SkTriangleFilter(float width = 1) : SkBitmapFilter(width) {} |
| 158 | |
| 159 | float evaluate(float x) const override { |
| 160 | return SkTMax(0.f, fWidth - fabsf(x)); |
| 161 | } |
| 162 | }; |
| 163 | |
| 164 | class SkBoxFilter final : public SkBitmapFilter { |
| 165 | public: |
| 166 | SkBoxFilter(float width = 0.5f) : SkBitmapFilter(width) {} |
| 167 | |
| 168 | float evaluate(float x) const override { |
| 169 | return (x >= -fWidth && x < fWidth) ? 1.0f : 0.0f; |
| 170 | } |
| 171 | }; |
| 172 | |
| 173 | class SkHammingFilter final : public SkBitmapFilter { |
| 174 | public: |
| 175 | SkHammingFilter(float width = 1) : SkBitmapFilter(width) {} |
| 176 | |
mtklein | 36352bf | 2015-03-25 18:17:31 -0700 | [diff] [blame] | 177 | float evaluate(float x) const override { |
humper@google.com | 138ebc3 | 2013-07-19 20:20:04 +0000 | [diff] [blame] | 178 | if (x <= -fWidth || x >= fWidth) { |
| 179 | return 0.0f; // Outside of the window. |
| 180 | } |
| 181 | if (x > -FLT_EPSILON && x < FLT_EPSILON) { |
| 182 | return 1.0f; // Special case the sinc discontinuity at the origin. |
| 183 | } |
humper@google.com | 16acf75 | 2013-07-19 21:12:08 +0000 | [diff] [blame] | 184 | const float xpi = x * static_cast<float>(SK_ScalarPI); |
humper@google.com | b088947 | 2013-07-09 21:37:14 +0000 | [diff] [blame] | 185 | |
humper@google.com | 138ebc3 | 2013-07-19 20:20:04 +0000 | [diff] [blame] | 186 | return ((sk_float_sin(xpi) / xpi) * // sinc(x) |
| 187 | (0.54f + 0.46f * sk_float_cos(xpi / fWidth))); // hamming(x) |
| 188 | } |
| 189 | }; |
| 190 | |
reed | a36c710 | 2016-01-14 10:10:56 -0800 | [diff] [blame] | 191 | class SkLanczosFilter final : public SkBitmapFilter { |
| 192 | public: |
| 193 | SkLanczosFilter(float width = 3.f) : SkBitmapFilter(width) {} |
skia.committer@gmail.com | 9e1ec1a | 2013-07-10 07:00:58 +0000 | [diff] [blame] | 194 | |
reed | a36c710 | 2016-01-14 10:10:56 -0800 | [diff] [blame] | 195 | float evaluate(float x) const override { |
| 196 | if (x <= -fWidth || x >= fWidth) { |
| 197 | return 0.0f; // Outside of the window. |
| 198 | } |
| 199 | if (x > -FLT_EPSILON && x < FLT_EPSILON) { |
| 200 | return 1.0f; // Special case the discontinuity at the origin. |
| 201 | } |
| 202 | float xpi = x * static_cast<float>(SK_ScalarPI); |
| 203 | return (sk_float_sin(xpi) / xpi) * // sinc(x) |
| 204 | sk_float_sin(xpi / fWidth) / (xpi / fWidth); // sinc(x/fWidth) |
| 205 | } |
humper@google.com | b088947 | 2013-07-09 21:37:14 +0000 | [diff] [blame] | 206 | }; |
| 207 | |
| 208 | |
| 209 | #endif |