Changeset 93580 in webkit
- Timestamp:
- Aug 22, 2011 10:45:49 PM (13 years ago)
- Location:
- trunk
- Files:
-
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/platform/chromium/test_expectations.txt
r93570 r93580 3621 3621 BUGWK65462 VISTA : http/tests/cache/history-only-cached-subresource-loads-max-age-https.html = PASS TIMEOUT 3622 3622 3623 // Needs new baselines for change to image resize caching code. 3624 BUGWK65587 : svg/dynamic-updates/SVGFEImageElement-dom-preserveAspectRatio-attr.html = IMAGE 3625 BUGWK65587 : svg/dynamic-updates/SVGFEImageElement-svgdom-preserveAspectRatio-prop.html = IMAGE 3626 BUGWK65587 : svg/custom/image-small-width-height.svg = IMAGE 3627 3623 3628 // Introduced in r92298, which might cause another test crashing. 3624 3629 BUGZMO SKIP : fast/loader/reload-zero-byte-plugin.html = FAIL -
trunk/Source/WebCore/ChangeLog
r93579 r93580 1 2011-08-22 John Bates <jbates@google.com> 2 3 Implemented skia support for caching resizes of cropped images. 4 https://bugs.webkit.org/show_bug.cgi?id=65587 5 6 Reviewed by Darin Fisher. 7 8 Previously, resizes of cropped images would not be cached. This causes various websites to have janky CSS animations in software compositing mode. 9 10 * platform/graphics/skia/ImageSkia.cpp: 11 (WebCore::drawResampledBitmap): Changed to use new APIs for subset caching. 12 (WebCore::Image::drawPattern): Added allowCaching parameter. 13 * platform/graphics/skia/NativeImageSkia.cpp: 14 (WebCore::NativeImageSkia::NativeImageSkia): 15 (WebCore::NativeImageSkia::CachedImageInfo::CachedImageInfo): 16 (WebCore::NativeImageSkia::CachedImageInfo::isEqual): 17 (WebCore::NativeImageSkia::CachedImageInfo::set): 18 (WebCore::NativeImageSkia::hasResizedBitmap): Changed this method so that it does not modify caching data. Added a second version used for cropped image resizes. 19 (WebCore::NativeImageSkia::resizedBitmap): Added parameter to let caller specify whether caching is allowed. 20 (WebCore::NativeImageSkia::shouldCacheResampling): Added a second version used for cropped image resizes. 21 (WebCore::NativeImageSkia::shouldCacheResamplingInternal): Both shouldCacheResampling methods call down to this for the shared logic. 22 * platform/graphics/skia/NativeImageSkia.h: Added CachedImageInfo to uniquely identify the cached or requested image resize operation. 23 1 24 2011-08-22 Tony Gentilcore <tonyg@chromium.org> 2 25 -
trunk/Source/WebCore/platform/graphics/skia/ImageSkia.cpp
r93013 r93580 162 162 // Resampling the whole image every time is very slow, so this speeds up things 163 163 // dramatically. 164 // 165 // Note: this code is only used when the canvas transformation is limited to 166 // scaling or translation. 164 167 static void drawResampledBitmap(SkCanvas& canvas, SkPaint& paint, const NativeImageSkia& bitmap, const SkIRect& srcIRect, const SkRect& destRect) 165 168 { 166 // First get the subset we need. This is efficient and does not copy pixels.167 SkBitmap subset;168 bitmap.extractSubset(&subset, srcIRect);169 SkRect srcRect;170 srcRect.set(srcIRect);171 172 // Whether we're doing a subset or using the full source image.173 bool srcIsFull = srcIRect.fLeft == 0 && srcIRect.fTop == 0174 && srcIRect.width() == bitmap.width()175 && srcIRect.height() == bitmap.height();176 177 // We will always draw in integer sizes, so round the destination rect.178 SkIRect destRectRounded;179 destRect.round(&destRectRounded);180 SkIRect resizedImageRect = // Represents the size of the resized image.181 { 0, 0, destRectRounded.width(), destRectRounded.height() };182 183 169 // Apply forward transform to destRect to estimate required size of 184 170 // re-sampled bitmap, and use only in calls required to resize, or that … … 189 175 destRectTransformed.round(&destRectTransformedRounded); 190 176 191 if (srcIsFull && bitmap.hasResizedBitmap(destRectTransformedRounded.width(), destRectTransformedRounded.height())) {192 // Yay, this bitmap frame already has a resized version.193 SkBitmap resampled = bitmap.resizedBitmap(destRectTransformedRounded.width(), destRectTransformedRounded.height());194 canvas.drawBitmapRect(resampled, 0, destRect, &paint);195 return;196 }197 198 177 // Compute the visible portion of our rect. 199 // We also need to compute the transformed portion of the 200 // visible portion for use below. 201 SkRect destBitmapSubsetSk; 202 ClipRectToCanvas(canvas, destRect, &destBitmapSubsetSk); 203 SkRect destBitmapSubsetTransformed; 204 canvas.getTotalMatrix().mapRect(&destBitmapSubsetTransformed, destBitmapSubsetSk); 205 destBitmapSubsetSk.offset(-destRect.fLeft, -destRect.fTop); 178 SkRect destRectVisibleSubset; 179 ClipRectToCanvas(canvas, destRect, &destRectVisibleSubset); 180 // ClipRectToCanvas often overshoots, resulting in a larger region than our 181 // original destRect. Intersecting gets us back inside. 182 if (!destRectVisibleSubset.intersect(destRect)) 183 return; // Nothing visible in destRect. 184 185 // Compute the transformed (screen space) portion of the visible portion for 186 // use below. 187 SkRect destRectVisibleSubsetTransformed; 188 canvas.getTotalMatrix().mapRect(&destRectVisibleSubsetTransformed, destRectVisibleSubset); 189 SkRect destBitmapSubsetTransformed = destRectVisibleSubsetTransformed; 190 destBitmapSubsetTransformed.offset(-destRectTransformed.fLeft, 191 -destRectTransformed.fTop); 206 192 SkIRect destBitmapSubsetTransformedRounded; 207 193 destBitmapSubsetTransformed.round(&destBitmapSubsetTransformedRounded); 208 destBitmapSubsetTransformedRounded.offset(-destRectTransformedRounded.fLeft, -destRectTransformedRounded.fTop); 209 210 // The matrix inverting, etc. could have introduced rounding error which 211 // causes the bounds to be outside of the resized bitmap. We round outward 212 // so we always lean toward it being larger rather than smaller than we 213 // need, and then clamp to the bitmap bounds so we don't get any invalid 214 // data. 215 SkIRect destBitmapSubsetSkI; 216 destBitmapSubsetSk.roundOut(&destBitmapSubsetSkI); 217 if (!destBitmapSubsetSkI.intersect(resizedImageRect)) 218 return; // Resized image does not intersect. 219 220 if (srcIsFull && bitmap.shouldCacheResampling( 221 resizedImageRect.width(), 222 resizedImageRect.height(), 223 destBitmapSubsetSkI.width(), 224 destBitmapSubsetSkI.height())) { 225 // We're supposed to resize the entire image and cache it, even though 226 // we don't need all of it. 227 SkBitmap resampled = bitmap.resizedBitmap(destRectTransformedRounded.width(), 228 destRectTransformedRounded.height()); 229 canvas.drawBitmapRect(resampled, 0, destRect, &paint); 230 } else { 231 // We should only resize the exposed part of the bitmap to do the 232 // minimal possible work. 233 234 // Resample the needed part of the image. 235 // Transforms above plus rounding may cause destBitmapSubsetTransformedRounded 236 // to go outside the image, so need to clip to avoid problems. 237 if (destBitmapSubsetTransformedRounded.intersect(0, 0, 238 destRectTransformedRounded.width(), destRectTransformedRounded.height())) { 239 240 SkBitmap resampled = skia::ImageOperations::Resize(subset, 241 skia::ImageOperations::RESIZE_LANCZOS3, 242 destRectTransformedRounded.width(), destRectTransformedRounded.height(), 243 destBitmapSubsetTransformedRounded); 244 245 // Compute where the new bitmap should be drawn. Since our new bitmap 246 // may be smaller than the original, we have to shift it over by the 247 // same amount that we cut off the top and left. 248 destBitmapSubsetSkI.offset(destRect.fLeft, destRect.fTop); 249 SkRect offsetDestRect; 250 offsetDestRect.set(destBitmapSubsetSkI); 251 252 canvas.drawBitmapRect(resampled, 0, offsetDestRect, &paint); 253 } 254 } 194 195 // Transforms above plus rounding may cause destBitmapSubsetTransformedRounded 196 // to go outside the image, so need to clip to avoid problems. 197 if (!destBitmapSubsetTransformedRounded.intersect( 198 0, 0, destRectTransformedRounded.width(), destRectTransformedRounded.height())) 199 return; // Image is not visible. 200 201 SkBitmap resampled = bitmap.resizedBitmap(srcIRect, 202 destRectTransformedRounded.width(), 203 destRectTransformedRounded.height(), 204 destBitmapSubsetTransformedRounded); 205 canvas.drawBitmapRect(resampled, 0, destRectVisibleSubset, &paint); 255 206 } 256 207 … … 341 292 FloatRect normSrcRect = normalizeRect(floatSrcRect); 342 293 if (destRect.isEmpty() || normSrcRect.isEmpty()) 343 return; 294 return; // nothing to draw 344 295 345 296 NativeImageSkia* bitmap = nativeImageForCurrentFrame(); … … 347 298 return; 348 299 349 // This is a very inexpensive operation. It will generate a new bitmap but350 // it will internally reference the old bitmap's pixels, adjusting the row351 // stride so the extra pixels appear as padding to the subsetted bitmap.352 SkBitmap srcSubset;353 300 SkIRect srcRect = enclosingIntRect(normSrcRect); 354 bitmap->extractSubset(&srcSubset, srcRect);355 356 SkBitmap resampled;357 SkShader* shader;358 301 359 302 // Figure out what size the bitmap will be in the destination. The 360 303 // destination rect is the bounds of the pattern, we need to use the 361 // matrix to see how bi tit will be.304 // matrix to see how big it will be. 362 305 float destBitmapWidth, destBitmapHeight; 363 306 TransformDimensions(patternTransform, srcRect.width(), srcRect.height(), … … 366 309 // Compute the resampling mode. 367 310 ResamplingMode resampling; 368 if (context->platformContext()->isAccelerated() )311 if (context->platformContext()->isAccelerated() || context->platformContext()->printing()) 369 312 resampling = RESAMPLE_LINEAR; 370 else { 371 if (context->platformContext()->printing()) 372 resampling = RESAMPLE_LINEAR; 373 else 374 resampling = computeResamplingMode(context->platformContext(), *bitmap, srcRect.width(), srcRect.height(), destBitmapWidth, destBitmapHeight); 375 } 313 else 314 resampling = computeResamplingMode(context->platformContext(), *bitmap, srcRect.width(), srcRect.height(), destBitmapWidth, destBitmapHeight); 376 315 377 316 // Load the transform WebKit requested. 378 317 SkMatrix matrix(patternTransform); 379 318 319 SkShader* shader; 380 320 if (resampling == RESAMPLE_AWESOME) { 381 321 // Do nice resampling. 382 SkBitmap resampled;383 322 int width = static_cast<int>(destBitmapWidth); 384 323 int height = static_cast<int>(destBitmapHeight); 385 if (!srcRect.fLeft && !srcRect.fTop 386 && srcRect.fRight == bitmap->width() && srcRect.fBottom == bitmap->height() 387 && (bitmap->hasResizedBitmap(width, height) 388 || bitmap->shouldCacheResampling(width, height, width, height))) { 389 // resizedBitmap() caches resized image. 390 resampled = bitmap->resizedBitmap(width, height); 391 } else { 392 resampled = skia::ImageOperations::Resize(srcSubset, 393 skia::ImageOperations::RESIZE_LANCZOS3, width, height); 394 } 324 SkBitmap resampled = bitmap->resizedBitmap(srcRect, width, height); 395 325 shader = SkShader::CreateBitmapShader(resampled, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode); 396 326 … … 401 331 } else { 402 332 // No need to do nice resampling. 333 SkBitmap srcSubset; 334 bitmap->extractSubset(&srcSubset, srcRect); 403 335 shader = SkShader::CreateBitmapShader(srcSubset, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode); 404 336 } -
trunk/Source/WebCore/platform/graphics/skia/NativeImageSkia.cpp
r90872 r93580 41 41 NativeImageSkia::NativeImageSkia() 42 42 : m_isDataComplete(false), 43 m_lastRequestSize(0, 0),44 43 m_resizeRequests(0) 45 44 { … … 49 48 : SkBitmap(other), 50 49 m_isDataComplete(false), 51 m_lastRequestSize(0, 0),52 50 m_resizeRequests(0) 53 51 { 54 52 } 55 56 53 57 54 NativeImageSkia::~NativeImageSkia() … … 64 61 } 65 62 66 bool NativeImageSkia::hasResizedBitmap( int w, int h) const63 bool NativeImageSkia::hasResizedBitmap(const SkIRect& srcSubset, int destWidth, int destHeight) const 67 64 { 68 if (m_lastRequestSize.width() == w && m_lastRequestSize.height() == h) 69 m_resizeRequests++; 70 else { 71 m_lastRequestSize = IntSize(w, h); 72 m_resizeRequests = 0; 65 return m_cachedImageInfo.isEqual(srcSubset, destWidth, destHeight) && !m_resizedImage.empty(); 66 } 67 68 SkBitmap NativeImageSkia::resizedBitmap(const SkIRect& srcSubset, 69 int destWidth, 70 int destHeight, 71 const SkIRect& destVisibleSubset) const 72 { 73 if (!hasResizedBitmap(srcSubset, destWidth, destHeight)) { 74 bool shouldCache = m_isDataComplete 75 && shouldCacheResampling(srcSubset, destWidth, destHeight, destVisibleSubset); 76 77 SkBitmap subset; 78 extractSubset(&subset, srcSubset); 79 if (!shouldCache) { 80 // Just resize the visible subset and return it. 81 SkBitmap resizedImage = skia::ImageOperations::Resize(subset, skia::ImageOperations::RESIZE_LANCZOS3, destWidth, destHeight, destVisibleSubset); 82 return resizedImage; 83 } 84 85 m_resizedImage = skia::ImageOperations::Resize(subset, skia::ImageOperations::RESIZE_LANCZOS3, destWidth, destHeight); 73 86 } 74 87 75 return m_resizedImage.width() == w && m_resizedImage.height() == h; 88 SkBitmap visibleBitmap; 89 m_resizedImage.extractSubset(&visibleBitmap, destVisibleSubset); 90 return visibleBitmap; 76 91 } 77 92 78 // FIXME: don't cache when image is in-progress. 93 bool NativeImageSkia::shouldCacheResampling(const SkIRect& srcSubset, 94 int destWidth, 95 int destHeight, 96 const SkIRect& destVisibleSubset) const 97 { 98 // Check whether the requested dimensions match previous request. 99 bool matchesPreviousRequest = m_cachedImageInfo.isEqual(srcSubset, destWidth, destHeight); 100 if (matchesPreviousRequest) 101 ++m_resizeRequests; 102 else { 103 m_cachedImageInfo.set(srcSubset, destWidth, destHeight); 104 m_resizeRequests = 0; 105 // Reset m_resizedImage now, because we don't distinguish between the 106 // last requested resize info and m_resizedImage's resize info. 107 m_resizedImage.reset(); 108 } 79 109 80 SkBitmap NativeImageSkia::resizedBitmap(int w, int h) const81 {82 if (m_resizedImage.width() != w || m_resizedImage.height() != h)83 m_resizedImage = skia::ImageOperations::Resize(*this, skia::ImageOperations::RESIZE_LANCZOS3, w, h);84 85 return m_resizedImage;86 }87 88 bool NativeImageSkia::shouldCacheResampling(int destWidth,89 int destHeight,90 int destSubsetWidth,91 int destSubsetHeight) const92 {93 110 // We can not cache incomplete frames. This might be a good optimization in 94 111 // the future, were we know how much of the frame has been decoded, so when … … 107 124 // many more will be made as well, and we'll go ahead and cache it. 108 125 static const int kManyRequestThreshold = 4; 109 if (m_lastRequestSize.width() == destWidth && 110 m_lastRequestSize.height() == destHeight) { 111 if (m_resizeRequests >= kManyRequestThreshold) 112 return true; 113 } else { 114 // When a different size is being requested, count this as a query 115 // (hasResizedBitmap) and reset the counter. 116 m_lastRequestSize = IntSize(destWidth, destHeight); 117 m_resizeRequests = 0; 118 } 126 if (m_resizeRequests >= kManyRequestThreshold) 127 return true; 119 128 120 // Otherwise, use the heuristic that if more than 1/4 of the image is 121 // requested, it's worth caching. 122 int destSize = destWidth * destHeight; 123 int destSubsetSize = destSubsetWidth * destSubsetHeight; 124 return destSize / 4 < destSubsetSize; 129 // If more than 1/4 of the resized image is visible, it's worth caching. 130 int destVisibleSize = destVisibleSubset.width() * destVisibleSubset.height(); 131 return (destVisibleSize > (destWidth * destHeight) / 4); 132 } 133 134 NativeImageSkia::CachedImageInfo::CachedImageInfo() 135 { 136 srcSubset.setEmpty(); 137 } 138 139 bool NativeImageSkia::CachedImageInfo::isEqual(const SkIRect& otherSrcSubset, int width, int height) const 140 { 141 return srcSubset == otherSrcSubset 142 && requestSize.width() == width 143 && requestSize.height() == height; 144 } 145 146 void NativeImageSkia::CachedImageInfo::set(const SkIRect& otherSrcSubset, int width, int height) 147 { 148 srcSubset = otherSrcSubset; 149 requestSize.setWidth(width); 150 requestSize.setHeight(height); 125 151 } 126 152 -
trunk/Source/WebCore/platform/graphics/skia/NativeImageSkia.h
r67412 r93580 33 33 34 34 #include "SkBitmap.h" 35 #include "SkRect.h" 35 36 #include "IntSize.h" 36 37 … … 64 65 // We can keep a resized version of the bitmap cached on this object. 65 66 // This function will return true if there is a cached version of the 66 // given image subset with the given dimensions .67 bool hasResizedBitmap( int width, int height) const;67 // given image subset with the given dimensions and subsets. 68 bool hasResizedBitmap(const SkIRect& srcSubset, int width, int height) const; 68 69 69 // This will return an existing resized image, or generate a new one of 70 // the specified size and store it in the cache. Subsetted images can not 71 // be cached unless the subset is the entire bitmap. 72 SkBitmap resizedBitmap(int width, int height) const; 70 // This will return an existing resized image subset, or generate a new one 71 // of the specified size and subsets and possibly cache it. 72 // srcSubset is the subset of the image to resize in image space. 73 SkBitmap resizedBitmap(const SkIRect& srcSubset, int destWidth, int destHeight) const 74 { 75 SkIRect destVisibleSubset = {0, 0, destWidth, destHeight}; 76 return resizedBitmap(srcSubset, destWidth, destHeight, destVisibleSubset); 77 } 78 79 // Same as above, but returns a subset of the destination image (ie: the 80 // visible subset). destVisibleSubset is the subset of the resized 81 // (destWidth x destHeight) image. 82 // In other words: 83 // - crop image by srcSubset -> imageSubset. 84 // - resize imageSubset to destWidth x destHeight -> destImage. 85 // - return destImage cropped by destVisibleSubset. 86 SkBitmap resizedBitmap(const SkIRect& srcSubset, int destWidth, int destHeight, const SkIRect& destVisibleSubset) const; 87 88 private: 89 // CachedImageInfo is used to uniquely identify cached or requested image 90 // resizes. 91 struct CachedImageInfo { 92 IntSize requestSize; 93 SkIRect srcSubset; 94 95 CachedImageInfo(); 96 97 bool isEqual(const SkIRect& otherSrcSubset, int width, int height) const; 98 void set(const SkIRect& otherSrcSubset, int width, int height); 99 }; 73 100 74 101 // Returns true if the given resize operation should either resize the whole 75 102 // image and cache it, or resize just the part it needs and throw the result 76 103 // away. 104 // 105 // Calling this function may increment a request count that can change the 106 // result of subsequent calls. 77 107 // 78 108 // On the one hand, if only a small subset is desired, then we will waste a … … 82 112 // scrolling on and off the screen. Since we only cache when doing the 83 113 // entire thing, it's best to just do it up front. 84 bool shouldCacheResampling(int destWidth, 114 bool shouldCacheResampling(const SkIRect& srcSubset, 115 int destWidth, 85 116 int destHeight, 86 int destSubsetWidth, 87 int destSubsetHeight) const; 117 const SkIRect& destSubset) const; 88 118 89 private:90 119 // Set to true when the data is complete. Before the entire image has 91 120 // loaded, we do not want to cache a resize. … … 98 127 // the last size. 99 128 // 100 // Every time we get a request, if it matches the m_lastRequestSize, we'll101 // increment the counter, and if not, we'll reset the counter and save the102 // size.129 // Every time we get a call to shouldCacheResampling, if it matches the 130 // m_cachedImageInfo, we'll increment the counter, and if not, we'll reset 131 // the counter and save the dimensions. 103 132 // 104 133 // This allows us to see if many requests have been made for the same 105 134 // resized image, we know that we should probably cache it, even if all of 106 135 // those requests individually are small and would not otherwise be cached. 107 mutable IntSize m_lastRequestSize; 136 // 137 // We also track the source and destination subsets for caching partial 138 // image resizes. 139 mutable CachedImageInfo m_cachedImageInfo; 108 140 mutable int m_resizeRequests; 109 141 };
Note: See TracChangeset
for help on using the changeset viewer.