OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2015 Google Inc. | 2 * Copyright 2015 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "SkBitmapScaler.h" | 8 #include "SkBitmapScaler.h" |
9 #include "SkBitmapFilter.h" | 9 #include "SkBitmapFilter.h" |
10 #include "SkConvolver.h" | 10 #include "SkConvolver.h" |
11 #include "SkImageInfo.h" | 11 #include "SkImageInfo.h" |
12 #include "SkPixmap.h" | 12 #include "SkPixmap.h" |
13 #include "SkRect.h" | 13 #include "SkRect.h" |
14 #include "SkScalar.h" | |
15 #include "SkTArray.h" | 14 #include "SkTArray.h" |
16 | 15 |
17 // SkResizeFilter --------------------------------------------------------------
-- | 16 // SkResizeFilter --------------------------------------------------------------
-- |
18 | 17 |
19 // Encapsulates computation and storage of the filters required for one complete | 18 // Encapsulates computation and storage of the filters required for one complete |
20 // resize operation. | 19 // resize operation. |
21 class SkResizeFilter { | 20 class SkResizeFilter { |
22 public: | 21 public: |
23 SkResizeFilter(SkBitmapScaler::ResizeMethod method, | 22 SkResizeFilter(SkBitmapScaler::ResizeMethod method, |
24 int srcFullWidth, int srcFullHeight, | 23 int srcFullWidth, int srcFullHeight, |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
67 | 66 |
68 fBitmapFilter = nullptr; | 67 fBitmapFilter = nullptr; |
69 switch(method) { | 68 switch(method) { |
70 case SkBitmapScaler::RESIZE_BOX: | 69 case SkBitmapScaler::RESIZE_BOX: |
71 fBitmapFilter = new SkBoxFilter; | 70 fBitmapFilter = new SkBoxFilter; |
72 break; | 71 break; |
73 case SkBitmapScaler::RESIZE_TRIANGLE: | 72 case SkBitmapScaler::RESIZE_TRIANGLE: |
74 fBitmapFilter = new SkTriangleFilter; | 73 fBitmapFilter = new SkTriangleFilter; |
75 break; | 74 break; |
76 case SkBitmapScaler::RESIZE_MITCHELL: | 75 case SkBitmapScaler::RESIZE_MITCHELL: |
77 fBitmapFilter = new SkMitchellFilter(1.f / 3.f, 1.f / 3.f); | 76 fBitmapFilter = new SkMitchellFilter; |
78 break; | 77 break; |
79 case SkBitmapScaler::RESIZE_HAMMING: | 78 case SkBitmapScaler::RESIZE_HAMMING: |
80 fBitmapFilter = new SkHammingFilter; | 79 fBitmapFilter = new SkHammingFilter; |
81 break; | 80 break; |
82 case SkBitmapScaler::RESIZE_LANCZOS3: | 81 case SkBitmapScaler::RESIZE_LANCZOS3: |
83 fBitmapFilter = new SkLanczosFilter; | 82 fBitmapFilter = new SkLanczosFilter; |
84 break; | 83 break; |
85 } | 84 } |
86 | 85 |
87 | 86 |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
123 // means the destination pixels are much smaller than the source pixels, and | 122 // means the destination pixels are much smaller than the source pixels, and |
124 // that the range covered by the filter won't necessarily cover any source | 123 // that the range covered by the filter won't necessarily cover any source |
125 // pixel boundaries. Therefore, we use these clamped values (max of 1) for | 124 // pixel boundaries. Therefore, we use these clamped values (max of 1) for |
126 // some computations. | 125 // some computations. |
127 float clampedScale = SkTMin(1.0f, scale); | 126 float clampedScale = SkTMin(1.0f, scale); |
128 | 127 |
129 // This is how many source pixels from the center we need to count | 128 // This is how many source pixels from the center we need to count |
130 // to support the filtering function. | 129 // to support the filtering function. |
131 float srcSupport = fBitmapFilter->width() / clampedScale; | 130 float srcSupport = fBitmapFilter->width() / clampedScale; |
132 | 131 |
133 // Speed up the divisions below by turning them into multiplies. | |
134 float invScale = 1.0f / scale; | 132 float invScale = 1.0f / scale; |
135 | 133 |
136 SkTArray<float> filterValues(64); | 134 SkSTArray<64, float, true> filterValuesArray; |
137 SkTArray<short> fixedFilterValues(64); | 135 SkSTArray<64, SkConvolutionFilter1D::ConvolutionFixed, true> fixedFilterValues
Array; |
138 | 136 |
139 // Loop over all pixels in the output range. We will generate one set of | 137 // Loop over all pixels in the output range. We will generate one set of |
140 // filter values for each one. Those values will tell us how to blend the | 138 // filter values for each one. Those values will tell us how to blend the |
141 // source pixels to compute the destination pixel. | 139 // source pixels to compute the destination pixel. |
| 140 |
| 141 // This is the pixel in the source directly under the pixel in the dest. |
| 142 // Note that we base computations on the "center" of the pixels. To see |
| 143 // why, observe that the destination pixel at coordinates (0, 0) in a 5.0x |
| 144 // downscale should "cover" the pixels around the pixel with *its center* |
| 145 // at coordinates (2.5, 2.5) in the source, not those around (0, 0). |
| 146 // Hence we need to scale coordinates (0.5, 0.5), not (0, 0). |
| 147 #ifdef SK_SUPPORT_LEGACY_BITMAP_FILTER |
| 148 int destLimit = SkScalarTruncToInt(SkScalarCeilToScalar(destSubsetHi) |
| 149 - SkScalarFloorToScalar(destSubsetLo)); |
| 150 #else |
| 151 destSubsetLo = SkScalarFloorToScalar(destSubsetLo); |
| 152 destSubsetHi = SkScalarCeilToScalar(destSubsetHi); |
| 153 float srcPixel = (destSubsetLo + 0.5f) * invScale; |
| 154 int destLimit = SkScalarTruncToInt(destSubsetHi - destSubsetLo); |
| 155 #endif |
| 156 output->reserveAdditional(destLimit, SkScalarCeilToInt(destLimit * srcSupport
* 2)); |
| 157 #ifdef SK_SUPPORT_LEGACY_BITMAP_FILTER |
142 for (int destSubsetI = SkScalarFloorToInt(destSubsetLo); destSubsetI < SkScala
rCeilToInt(destSubsetHi); | 158 for (int destSubsetI = SkScalarFloorToInt(destSubsetLo); destSubsetI < SkScala
rCeilToInt(destSubsetHi); |
143 destSubsetI++) { | 159 destSubsetI++) |
144 // Reset the arrays. We don't declare them inside so they can re-use the | 160 #else |
145 // same malloc-ed buffer. | 161 for (int destI = 0; destI < destLimit; srcPixel += invScale, destI++) |
146 filterValues.reset(); | 162 #endif |
147 fixedFilterValues.reset(); | 163 { |
148 | 164 // Compute the (inclusive) range of source pixels the filter covers. |
149 // This is the pixel in the source directly under the pixel in the dest. | 165 #ifdef SK_SUPPORT_LEGACY_BITMAP_FILTER |
150 // Note that we base computations on the "center" of the pixels. To see | |
151 // why, observe that the destination pixel at coordinates (0, 0) in a 5.0x | |
152 // downscale should "cover" the pixels around the pixel with *its center* | |
153 // at coordinates (2.5, 2.5) in the source, not those around (0, 0). | |
154 // Hence we need to scale coordinates (0.5, 0.5), not (0, 0). | |
155 float srcPixel = (static_cast<float>(destSubsetI) + 0.5f) * invScale; | 166 float srcPixel = (static_cast<float>(destSubsetI) + 0.5f) * invScale; |
156 | |
157 // Compute the (inclusive) range of source pixels the filter covers. | |
158 int srcBegin = SkTMax(0, SkScalarFloorToInt(srcPixel - srcSupport)); | 167 int srcBegin = SkTMax(0, SkScalarFloorToInt(srcPixel - srcSupport)); |
159 int srcEnd = SkTMin(srcSize - 1, SkScalarCeilToInt(srcPixel + srcSupport)); | 168 int srcEnd = SkTMin(srcSize - 1, SkScalarCeilToInt(srcPixel + srcSupport)); |
| 169 #else |
| 170 float srcBegin = SkTMax(0.f, SkScalarFloorToScalar(srcPixel - srcSupport)); |
| 171 float srcEnd = SkTMin(srcSize - 1.f, SkScalarCeilToScalar(srcPixel + srcSupp
ort)); |
| 172 #endif |
160 | 173 |
161 // Compute the unnormalized filter value at each location of the source | 174 // Compute the unnormalized filter value at each location of the source |
162 // it covers. | 175 // it covers. |
| 176 #ifdef SK_SUPPORT_LEGACY_BITMAP_FILTER |
163 float filterSum = 0.0f; // Sub of the filter values for normalizing. | 177 float filterSum = 0.0f; // Sub of the filter values for normalizing. |
| 178 int filterCount = srcEnd - srcBegin + 1; |
| 179 filterValuesArray.reset(filterCount); |
164 for (int curFilterPixel = srcBegin; curFilterPixel <= srcEnd; | 180 for (int curFilterPixel = srcBegin; curFilterPixel <= srcEnd; |
165 curFilterPixel++) { | 181 curFilterPixel++) { |
166 // Distance from the center of the filter, this is the filter coordinate | 182 #endif |
167 // in source space. We also need to consider the center of the pixel | 183 // Sum of the filter values for normalizing. |
168 // when comparing distance against 'srcPixel'. In the 5x downscale | 184 // Distance from the center of the filter, this is the filter coordinate |
169 // example used above the distance from the center of the filter to | 185 // in source space. We also need to consider the center of the pixel |
170 // the pixel with coordinates (2, 2) should be 0, because its center | 186 // when comparing distance against 'srcPixel'. In the 5x downscale |
171 // is at (2.5, 2.5). | 187 // example used above the distance from the center of the filter to |
| 188 // the pixel with coordinates (2, 2) should be 0, because its center |
| 189 // is at (2.5, 2.5). |
| 190 #ifdef SK_SUPPORT_LEGACY_BITMAP_FILTER |
172 float srcFilterDist = | 191 float srcFilterDist = |
173 ((static_cast<float>(curFilterPixel) + 0.5f) - srcPixel); | 192 ((static_cast<float>(curFilterPixel) + 0.5f) - srcPixel); |
174 | 193 |
175 // Since the filter really exists in dest space, map it there. | 194 // Since the filter really exists in dest space, map it there. |
176 float destFilterDist = srcFilterDist * clampedScale; | 195 float destFilterDist = srcFilterDist * clampedScale; |
177 | 196 |
178 // Compute the filter value at that location. | 197 // Compute the filter value at that location. |
179 float filterValue = fBitmapFilter->evaluate(destFilterDist); | 198 float filterValue = fBitmapFilter->evaluate(destFilterDist); |
180 filterValues.push_back(filterValue); | 199 filterValuesArray[curFilterPixel - srcBegin] = filterValue; |
181 | 200 |
182 filterSum += filterValue; | 201 filterSum += filterValue; |
183 } | 202 } |
184 SkASSERT(!filterValues.empty()); | 203 #else |
185 | 204 float destFilterDist = (srcBegin + 0.5f - srcPixel) * clampedScale; |
| 205 int filterCount = SkScalarTruncToInt(srcEnd - srcBegin) + 1; |
| 206 SkASSERT(filterCount > 0); |
| 207 filterValuesArray.reset(filterCount); |
| 208 float filterSum = fBitmapFilter->evaluate_n(destFilterDist, clampedScale, fi
lterCount, |
| 209 filterValuesArray.begin()); |
| 210 #endif |
186 // The filter must be normalized so that we don't affect the brightness of | 211 // The filter must be normalized so that we don't affect the brightness of |
187 // the image. Convert to normalized fixed point. | 212 // the image. Convert to normalized fixed point. |
188 short fixedSum = 0; | 213 int fixedSum = 0; |
189 for (int i = 0; i < filterValues.count(); i++) { | 214 fixedFilterValuesArray.reset(filterCount); |
190 short curFixed = output->FloatToFixed(filterValues[i] / filterSum); | 215 const float* filterValues = filterValuesArray.begin(); |
| 216 SkConvolutionFilter1D::ConvolutionFixed* fixedFilterValues = fixedFilterValu
esArray.begin(); |
| 217 #ifndef SK_SUPPORT_LEGACY_BITMAP_FILTER |
| 218 float invFilterSum = 1 / filterSum; |
| 219 #endif |
| 220 for (int fixedI = 0; fixedI < filterCount; fixedI++) { |
| 221 #ifdef SK_SUPPORT_LEGACY_BITMAP_FILTER |
| 222 int curFixed = SkConvolutionFilter1D::FloatToFixed(filterValues[fixedI] /
filterSum); |
| 223 #else |
| 224 int curFixed = SkConvolutionFilter1D::FloatToFixed(filterValues[fixedI] *
invFilterSum); |
| 225 #endif |
191 fixedSum += curFixed; | 226 fixedSum += curFixed; |
192 fixedFilterValues.push_back(curFixed); | 227 fixedFilterValues[fixedI] = SkToS16(curFixed); |
193 } | 228 } |
| 229 SkASSERT(fixedSum <= 0x7FFF); |
194 | 230 |
195 // The conversion to fixed point will leave some rounding errors, which | 231 // The conversion to fixed point will leave some rounding errors, which |
196 // we add back in to avoid affecting the brightness of the image. We | 232 // we add back in to avoid affecting the brightness of the image. We |
197 // arbitrarily add this to the center of the filter array (this won't always | 233 // arbitrarily add this to the center of the filter array (this won't always |
198 // be the center of the filter function since it could get clipped on the | 234 // be the center of the filter function since it could get clipped on the |
199 // edges, but it doesn't matter enough to worry about that case). | 235 // edges, but it doesn't matter enough to worry about that case). |
200 short leftovers = output->FloatToFixed(1.0f) - fixedSum; | 236 int leftovers = SkConvolutionFilter1D::FloatToFixed(1) - fixedSum; |
201 fixedFilterValues[fixedFilterValues.count() / 2] += leftovers; | 237 fixedFilterValues[filterCount / 2] += leftovers; |
202 | 238 |
203 // Now it's ready to go. | 239 // Now it's ready to go. |
204 output->AddFilter(srcBegin, &fixedFilterValues[0], | 240 #ifdef SK_SUPPORT_LEGACY_BITMAP_FILTER |
205 static_cast<int>(fixedFilterValues.count())); | 241 output->AddFilter(srcBegin, fixedFilterValues, filterCount); |
| 242 #else |
| 243 output->AddFilter(SkScalarFloorToInt(srcBegin), fixedFilterValues, filterCou
nt); |
| 244 #endif |
206 } | 245 } |
207 | 246 |
208 if (convolveProcs.fApplySIMDPadding) { | 247 if (convolveProcs.fApplySIMDPadding) { |
209 convolveProcs.fApplySIMDPadding( output ); | 248 convolveProcs.fApplySIMDPadding(output); |
210 } | 249 } |
211 } | 250 } |
212 | 251 |
213 bool SkBitmapScaler::Resize(SkBitmap* resultPtr, const SkPixmap& source, ResizeM
ethod method, | 252 bool SkBitmapScaler::Resize(SkBitmap* resultPtr, const SkPixmap& source, ResizeM
ethod method, |
214 int destWidth, int destHeight, SkBitmap::Allocator*
allocator) { | 253 int destWidth, int destHeight, SkBitmap::Allocator*
allocator) { |
215 if (nullptr == source.addr() || source.colorType() != kN32_SkColorType || | 254 if (nullptr == source.addr() || source.colorType() != kN32_SkColorType || |
216 source.width() < 1 || source.height() < 1) | 255 source.width() < 1 || source.height() < 1) |
217 { | 256 { |
218 return false; | 257 return false; |
219 } | 258 } |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
252 convolveProcs, true)) { | 291 convolveProcs, true)) { |
253 return false; | 292 return false; |
254 } | 293 } |
255 | 294 |
256 *resultPtr = result; | 295 *resultPtr = result; |
257 resultPtr->lockPixels(); | 296 resultPtr->lockPixels(); |
258 SkASSERT(resultPtr->getPixels()); | 297 SkASSERT(resultPtr->getPixels()); |
259 return true; | 298 return true; |
260 } | 299 } |
261 | 300 |
OLD | NEW |