Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(102)

Side by Side Diff: src/core/SkBitmapScaler.cpp

Issue 300113008: Make image scaling have floating point scales. Third attempt to land this (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Windows build fix Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/core/SkBitmapScaler.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #include "SkBitmapScaler.h" 1 #include "SkBitmapScaler.h"
2 #include "SkBitmapFilter.h" 2 #include "SkBitmapFilter.h"
3 #include "SkRect.h" 3 #include "SkRect.h"
4 #include "SkTArray.h" 4 #include "SkTArray.h"
5 #include "SkErrorInternals.h" 5 #include "SkErrorInternals.h"
6 #include "SkConvolver.h" 6 #include "SkConvolver.h"
7 7
8 // SkResizeFilter -------------------------------------------------------------- -- 8 // SkResizeFilter -------------------------------------------------------------- --
9 9
10 // Encapsulates computation and storage of the filters required for one complete 10 // Encapsulates computation and storage of the filters required for one complete
11 // resize operation. 11 // resize operation.
12 class SkResizeFilter { 12 class SkResizeFilter {
13 public: 13 public:
14 SkResizeFilter(SkBitmapScaler::ResizeMethod method, 14 SkResizeFilter(SkBitmapScaler::ResizeMethod method,
15 int srcFullWidth, int srcFullHeight, 15 int srcFullWidth, int srcFullHeight,
16 int destWidth, int destHeight, 16 float destWidth, float destHeight,
17 const SkIRect& destSubset, 17 const SkRect& destSubset,
18 const SkConvolutionProcs& convolveProcs); 18 const SkConvolutionProcs& convolveProcs);
19 ~SkResizeFilter() { 19 ~SkResizeFilter() {
20 SkDELETE( fBitmapFilter ); 20 SkDELETE( fBitmapFilter );
21 } 21 }
22 22
23 // Returns the filled filter values. 23 // Returns the filled filter values.
24 const SkConvolutionFilter1D& xFilter() { return fXFilter; } 24 const SkConvolutionFilter1D& xFilter() { return fXFilter; }
25 const SkConvolutionFilter1D& yFilter() { return fYFilter; } 25 const SkConvolutionFilter1D& yFilter() { return fYFilter; }
26 26
27 private: 27 private:
28 28
29 SkBitmapFilter* fBitmapFilter; 29 SkBitmapFilter* fBitmapFilter;
30 30
31 // Computes one set of filters either horizontally or vertically. The caller 31 // Computes one set of filters either horizontally or vertically. The caller
32 // will specify the "min" and "max" rather than the bottom/top and 32 // will specify the "min" and "max" rather than the bottom/top and
33 // right/bottom so that the same code can be re-used in each dimension. 33 // right/bottom so that the same code can be re-used in each dimension.
34 // 34 //
35 // |srcDependLo| and |srcDependSize| gives the range for the source 35 // |srcDependLo| and |srcDependSize| gives the range for the source
36 // depend rectangle (horizontally or vertically at the caller's discretion 36 // depend rectangle (horizontally or vertically at the caller's discretion
37 // -- see above for what this means). 37 // -- see above for what this means).
38 // 38 //
39 // Likewise, the range of destination values to compute and the scale factor 39 // Likewise, the range of destination values to compute and the scale factor
40 // for the transform is also specified. 40 // for the transform is also specified.
41 41
42 void computeFilters(int srcSize, 42 void computeFilters(int srcSize,
43 int destSubsetLo, int destSubsetSize, 43 float destSubsetLo, float destSubsetSize,
44 float scale, 44 float scale,
45 SkConvolutionFilter1D* output, 45 SkConvolutionFilter1D* output,
46 const SkConvolutionProcs& convolveProcs); 46 const SkConvolutionProcs& convolveProcs);
47 47
48 SkConvolutionFilter1D fXFilter; 48 SkConvolutionFilter1D fXFilter;
49 SkConvolutionFilter1D fYFilter; 49 SkConvolutionFilter1D fYFilter;
50 }; 50 };
51 51
52 SkResizeFilter::SkResizeFilter(SkBitmapScaler::ResizeMethod method, 52 SkResizeFilter::SkResizeFilter(SkBitmapScaler::ResizeMethod method,
53 int srcFullWidth, int srcFullHeight, 53 int srcFullWidth, int srcFullHeight,
54 int destWidth, int destHeight, 54 float destWidth, float destHeight,
55 const SkIRect& destSubset, 55 const SkRect& destSubset,
56 const SkConvolutionProcs& convolveProcs) { 56 const SkConvolutionProcs& convolveProcs) {
57 57
58 // method will only ever refer to an "algorithm method". 58 // method will only ever refer to an "algorithm method".
59 SkASSERT((SkBitmapScaler::RESIZE_FIRST_ALGORITHM_METHOD <= method) && 59 SkASSERT((SkBitmapScaler::RESIZE_FIRST_ALGORITHM_METHOD <= method) &&
60 (method <= SkBitmapScaler::RESIZE_LAST_ALGORITHM_METHOD)); 60 (method <= SkBitmapScaler::RESIZE_LAST_ALGORITHM_METHOD));
61 61
62 switch(method) { 62 switch(method) {
63 case SkBitmapScaler::RESIZE_BOX: 63 case SkBitmapScaler::RESIZE_BOX:
64 fBitmapFilter = SkNEW(SkBoxFilter); 64 fBitmapFilter = SkNEW(SkBoxFilter);
65 break; 65 break;
66 case SkBitmapScaler::RESIZE_TRIANGLE: 66 case SkBitmapScaler::RESIZE_TRIANGLE:
67 fBitmapFilter = SkNEW(SkTriangleFilter); 67 fBitmapFilter = SkNEW(SkTriangleFilter);
68 break; 68 break;
69 case SkBitmapScaler::RESIZE_MITCHELL: 69 case SkBitmapScaler::RESIZE_MITCHELL:
70 fBitmapFilter = SkNEW_ARGS(SkMitchellFilter, (1.f/3.f, 1.f/3.f)); 70 fBitmapFilter = SkNEW_ARGS(SkMitchellFilter, (1.f/3.f, 1.f/3.f));
71 break; 71 break;
72 case SkBitmapScaler::RESIZE_HAMMING: 72 case SkBitmapScaler::RESIZE_HAMMING:
73 fBitmapFilter = SkNEW(SkHammingFilter); 73 fBitmapFilter = SkNEW(SkHammingFilter);
74 break; 74 break;
75 case SkBitmapScaler::RESIZE_LANCZOS3: 75 case SkBitmapScaler::RESIZE_LANCZOS3:
76 fBitmapFilter = SkNEW(SkLanczosFilter); 76 fBitmapFilter = SkNEW(SkLanczosFilter);
77 break; 77 break;
78 default: 78 default:
79 // NOTREACHED: 79 // NOTREACHED:
80 fBitmapFilter = SkNEW_ARGS(SkMitchellFilter, (1.f/3.f, 1.f/3.f)); 80 fBitmapFilter = SkNEW_ARGS(SkMitchellFilter, (1.f/3.f, 1.f/3.f));
81 break; 81 break;
82 } 82 }
83 83
84 84
85 float scaleX = static_cast<float>(destWidth) / 85 float scaleX = destWidth / srcFullWidth;
86 static_cast<float>(srcFullWidth); 86 float scaleY = destHeight / srcFullHeight;
87 float scaleY = static_cast<float>(destHeight) /
88 static_cast<float>(srcFullHeight);
89 87
90 this->computeFilters(srcFullWidth, destSubset.fLeft, destSubset.width(), 88 this->computeFilters(srcFullWidth, destSubset.fLeft, destSubset.width(),
91 scaleX, &fXFilter, convolveProcs); 89 scaleX, &fXFilter, convolveProcs);
92 if (srcFullWidth == srcFullHeight && 90 if (srcFullWidth == srcFullHeight &&
93 destSubset.fLeft == destSubset.fTop && 91 destSubset.fLeft == destSubset.fTop &&
94 destSubset.width() == destSubset.height()&& 92 destSubset.width() == destSubset.height()&&
95 scaleX == scaleY) { 93 scaleX == scaleY) {
96 fYFilter = fXFilter; 94 fYFilter = fXFilter;
97 } else { 95 } else {
98 this->computeFilters(srcFullHeight, destSubset.fTop, destSubset.height() , 96 this->computeFilters(srcFullHeight, destSubset.fTop, destSubset.height() ,
99 scaleY, &fYFilter, convolveProcs); 97 scaleY, &fYFilter, convolveProcs);
100 } 98 }
101 } 99 }
102 100
103 // TODO(egouriou): Take advantage of periods in the convolution. 101 // TODO(egouriou): Take advantage of periods in the convolution.
104 // Practical resizing filters are periodic outside of the border area. 102 // Practical resizing filters are periodic outside of the border area.
105 // For Lanczos, a scaling by a (reduced) factor of p/q (q pixels in the 103 // For Lanczos, a scaling by a (reduced) factor of p/q (q pixels in the
106 // source become p pixels in the destination) will have a period of p. 104 // source become p pixels in the destination) will have a period of p.
107 // A nice consequence is a period of 1 when downscaling by an integral 105 // A nice consequence is a period of 1 when downscaling by an integral
108 // factor. Downscaling from typical display resolutions is also bound 106 // factor. Downscaling from typical display resolutions is also bound
109 // to produce interesting periods as those are chosen to have multiple 107 // to produce interesting periods as those are chosen to have multiple
110 // small factors. 108 // small factors.
111 // Small periods reduce computational load and improve cache usage if 109 // Small periods reduce computational load and improve cache usage if
112 // the coefficients can be shared. For periods of 1 we can consider 110 // the coefficients can be shared. For periods of 1 we can consider
113 // loading the factors only once outside the borders. 111 // loading the factors only once outside the borders.
114 void SkResizeFilter::computeFilters(int srcSize, 112 void SkResizeFilter::computeFilters(int srcSize,
115 int destSubsetLo, int destSubsetSize, 113 float destSubsetLo, float destSubsetSize,
116 float scale, 114 float scale,
117 SkConvolutionFilter1D* output, 115 SkConvolutionFilter1D* output,
118 const SkConvolutionProcs& convolveProcs) { 116 const SkConvolutionProcs& convolveProcs) {
119 int destSubsetHi = destSubsetLo + destSubsetSize; // [lo, hi) 117 float destSubsetHi = destSubsetLo + destSubsetSize; // [lo, hi)
120 118
121 // When we're doing a magnification, the scale will be larger than one. This 119 // When we're doing a magnification, the scale will be larger than one. This
122 // means the destination pixels are much smaller than the source pixels, and 120 // means the destination pixels are much smaller than the source pixels, and
123 // that the range covered by the filter won't necessarily cover any source 121 // that the range covered by the filter won't necessarily cover any source
124 // pixel boundaries. Therefore, we use these clamped values (max of 1) for 122 // pixel boundaries. Therefore, we use these clamped values (max of 1) for
125 // some computations. 123 // some computations.
126 float clampedScale = SkTMin(1.0f, scale); 124 float clampedScale = SkTMin(1.0f, scale);
127 125
128 // This is how many source pixels from the center we need to count 126 // This is how many source pixels from the center we need to count
129 // to support the filtering function. 127 // to support the filtering function.
130 float srcSupport = fBitmapFilter->width() / clampedScale; 128 float srcSupport = fBitmapFilter->width() / clampedScale;
131 129
132 // Speed up the divisions below by turning them into multiplies. 130 // Speed up the divisions below by turning them into multiplies.
133 float invScale = 1.0f / scale; 131 float invScale = 1.0f / scale;
134 132
135 SkTArray<float> filterValues(64); 133 SkTArray<float> filterValues(64);
136 SkTArray<short> fixedFilterValues(64); 134 SkTArray<short> fixedFilterValues(64);
137 135
138 // Loop over all pixels in the output range. We will generate one set of 136 // Loop over all pixels in the output range. We will generate one set of
139 // filter values for each one. Those values will tell us how to blend the 137 // filter values for each one. Those values will tell us how to blend the
140 // source pixels to compute the destination pixel. 138 // source pixels to compute the destination pixel.
141 for (int destSubsetI = destSubsetLo; destSubsetI < destSubsetHi; 139 for (int destSubsetI = SkScalarFloorToInt(destSubsetLo); destSubsetI < SkScala rCeilToInt(destSubsetHi);
142 destSubsetI++) { 140 destSubsetI++) {
143 // Reset the arrays. We don't declare them inside so they can re-use the 141 // Reset the arrays. We don't declare them inside so they can re-use the
144 // same malloc-ed buffer. 142 // same malloc-ed buffer.
145 filterValues.reset(); 143 filterValues.reset();
146 fixedFilterValues.reset(); 144 fixedFilterValues.reset();
147 145
148 // This is the pixel in the source directly under the pixel in the dest. 146 // This is the pixel in the source directly under the pixel in the dest.
149 // Note that we base computations on the "center" of the pixels. To see 147 // Note that we base computations on the "center" of the pixels. To see
150 // why, observe that the destination pixel at coordinates (0, 0) in a 5.0x 148 // why, observe that the destination pixel at coordinates (0, 0) in a 5.0x
151 // downscale should "cover" the pixels around the pixel with *its center* 149 // downscale should "cover" the pixels around the pixel with *its center*
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after
240 #else 238 #else
241 return SkBitmapScaler::RESIZE_MITCHELL; 239 return SkBitmapScaler::RESIZE_MITCHELL;
242 #endif 240 #endif
243 } 241 }
244 } 242 }
245 243
246 // static 244 // static
247 bool SkBitmapScaler::Resize(SkBitmap* resultPtr, 245 bool SkBitmapScaler::Resize(SkBitmap* resultPtr,
248 const SkBitmap& source, 246 const SkBitmap& source,
249 ResizeMethod method, 247 ResizeMethod method,
250 int destWidth, int destHeight, 248 float destWidth, float destHeight,
251 const SkIRect& destSubset,
252 const SkConvolutionProcs& convolveProcs, 249 const SkConvolutionProcs& convolveProcs,
253 SkBitmap::Allocator* allocator) { 250 SkBitmap::Allocator* allocator) {
251
252 SkRect destSubset = { 0, 0, destWidth, destHeight };
253
254 // Ensure that the ResizeMethod enumeration is sound. 254 // Ensure that the ResizeMethod enumeration is sound.
255 SkASSERT(((RESIZE_FIRST_QUALITY_METHOD <= method) && 255 SkASSERT(((RESIZE_FIRST_QUALITY_METHOD <= method) &&
256 (method <= RESIZE_LAST_QUALITY_METHOD)) || 256 (method <= RESIZE_LAST_QUALITY_METHOD)) ||
257 ((RESIZE_FIRST_ALGORITHM_METHOD <= method) && 257 ((RESIZE_FIRST_ALGORITHM_METHOD <= method) &&
258 (method <= RESIZE_LAST_ALGORITHM_METHOD))); 258 (method <= RESIZE_LAST_ALGORITHM_METHOD)));
259 259
260 SkIRect dest = { 0, 0, destWidth, destHeight }; 260 SkRect dest = { 0, 0, destWidth, destHeight };
261 if (!dest.contains(destSubset)) { 261 if (!dest.contains(destSubset)) {
262 SkErrorInternals::SetError( kInvalidArgument_SkError, 262 SkErrorInternals::SetError( kInvalidArgument_SkError,
263 "Sorry, you passed me a bitmap resize " 263 "Sorry, the destination bitmap scale subset "
264 " method I have never heard of: %d", 264 "falls outside the full destination bitmap." );
265 method );
266 } 265 }
267 266
268 // If the size of source or destination is 0, i.e. 0x0, 0xN or Nx0, just 267 // If the size of source or destination is 0, i.e. 0x0, 0xN or Nx0, just
269 // return empty. 268 // return empty.
270 if (source.width() < 1 || source.height() < 1 || 269 if (source.width() < 1 || source.height() < 1 ||
271 destWidth < 1 || destHeight < 1) { 270 destWidth < 1 || destHeight < 1) {
272 // todo: seems like we could handle negative dstWidth/Height, since that 271 // todo: seems like we could handle negative dstWidth/Height, since that
273 // is just a negative scale (flip) 272 // is just a negative scale (flip)
274 return false; 273 return false;
275 } 274 }
(...skipping 14 matching lines...) Expand all
290 destWidth, destHeight, destSubset, convolveProcs); 289 destWidth, destHeight, destSubset, convolveProcs);
291 290
292 // Get a source bitmap encompassing this touched area. We construct the 291 // Get a source bitmap encompassing this touched area. We construct the
293 // offsets and row strides such that it looks like a new bitmap, while 292 // offsets and row strides such that it looks like a new bitmap, while
294 // referring to the old data. 293 // referring to the old data.
295 const unsigned char* sourceSubset = 294 const unsigned char* sourceSubset =
296 reinterpret_cast<const unsigned char*>(source.getPixels()); 295 reinterpret_cast<const unsigned char*>(source.getPixels());
297 296
298 // Convolve into the result. 297 // Convolve into the result.
299 SkBitmap result; 298 SkBitmap result;
300 result.setConfig(SkImageInfo::MakeN32(destSubset.width(), 299 result.setConfig(SkImageInfo::MakeN32(SkScalarCeilToInt(destSubset.width()),
301 destSubset.height(), 300 SkScalarCeilToInt(destSubset.height()) ,
302 source.alphaType())); 301 source.alphaType()));
303 result.allocPixels(allocator, NULL); 302 result.allocPixels(allocator, NULL);
304 if (!result.readyToDraw()) { 303 if (!result.readyToDraw()) {
305 return false; 304 return false;
306 } 305 }
307 306
308 BGRAConvolve2D(sourceSubset, static_cast<int>(source.rowBytes()), 307 BGRAConvolve2D(sourceSubset, static_cast<int>(source.rowBytes()),
309 !source.isOpaque(), filter.xFilter(), filter.yFilter(), 308 !source.isOpaque(), filter.xFilter(), filter.yFilter(),
310 static_cast<int>(result.rowBytes()), 309 static_cast<int>(result.rowBytes()),
311 static_cast<unsigned char*>(result.getPixels()), 310 static_cast<unsigned char*>(result.getPixels()),
312 convolveProcs, true); 311 convolveProcs, true);
313 312
314 *resultPtr = result; 313 *resultPtr = result;
315 resultPtr->lockPixels(); 314 resultPtr->lockPixels();
316 SkASSERT(NULL != resultPtr->getPixels()); 315 SkASSERT(NULL != resultPtr->getPixels());
317 return true; 316 return true;
318 } 317 }
319
320 // static
321 bool SkBitmapScaler::Resize(SkBitmap* resultPtr,
322 const SkBitmap& source,
323 ResizeMethod method,
324 int destWidth, int destHeight,
325 const SkConvolutionProcs& convolveProcs,
326 SkBitmap::Allocator* allocator) {
327 SkIRect destSubset = { 0, 0, destWidth, destHeight };
328 return Resize(resultPtr, source, method, destWidth, destHeight, destSubset,
329 convolveProcs, allocator);
330 }
OLDNEW
« no previous file with comments | « src/core/SkBitmapScaler.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698