2 * Copyright (c) 2008, Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #include "platform/graphics/skia/NativeImageSkia.h"
34 #include "platform/PlatformInstrumentation.h"
35 #include "platform/TraceEvent.h"
36 #include "platform/geometry/FloatPoint.h"
37 #include "platform/geometry/FloatRect.h"
38 #include "platform/geometry/FloatSize.h"
39 #include "platform/graphics/GraphicsContext.h"
40 #include "platform/graphics/Image.h"
41 #include "platform/graphics/DeferredImageDecoder.h"
42 #include "platform/graphics/skia/SkiaUtils.h"
43 #include "skia/ext/image_operations.h"
44 #include "third_party/skia/include/core/SkMatrix.h"
45 #include "third_party/skia/include/core/SkPaint.h"
46 #include "third_party/skia/include/core/SkScalar.h"
47 #include "third_party/skia/include/core/SkShader.h"
55 static bool nearlyIntegral(float value)
57 return fabs(value - floorf(value)) < std::numeric_limits<float>::epsilon();
60 InterpolationQuality NativeImageSkia::computeInterpolationQuality(const SkMatrix& matrix, float srcWidth, float srcHeight, float destWidth, float destHeight) const
62 // The percent change below which we will not resample. This usually means
63 // an off-by-one error on the web page, and just doing nearest neighbor
64 // sampling is usually good enough.
65 const float kFractionalChangeThreshold = 0.025f;
67 // Images smaller than this in either direction are considered "small" and
68 // are not resampled ever (see below).
69 const int kSmallImageSizeThreshold = 8;
71 // The amount an image can be stretched in a single direction before we
72 // say that it is being stretched so much that it must be a line or
73 // background that doesn't need resampling.
74 const float kLargeStretch = 3.0f;
76 // Figure out if we should resample this image. We try to prune out some
77 // common cases where resampling won't give us anything, since it is much
78 // slower than drawing stretched.
79 float diffWidth = fabs(destWidth - srcWidth);
80 float diffHeight = fabs(destHeight - srcHeight);
81 bool widthNearlyEqual = diffWidth < std::numeric_limits<float>::epsilon();
82 bool heightNearlyEqual = diffHeight < std::numeric_limits<float>::epsilon();
83 // We don't need to resample if the source and destination are the same.
84 if (widthNearlyEqual && heightNearlyEqual)
85 return InterpolationNone;
87 if (srcWidth <= kSmallImageSizeThreshold
88 || srcHeight <= kSmallImageSizeThreshold
89 || destWidth <= kSmallImageSizeThreshold
90 || destHeight <= kSmallImageSizeThreshold) {
91 // Small image detected.
93 // Resample in the case where the new size would be non-integral.
94 // This can cause noticeable breaks in repeating patterns, except
95 // when the source image is only one pixel wide in that dimension.
96 if ((!nearlyIntegral(destWidth) && srcWidth > 1 + std::numeric_limits<float>::epsilon())
97 || (!nearlyIntegral(destHeight) && srcHeight > 1 + std::numeric_limits<float>::epsilon()))
98 return InterpolationLow;
100 // Otherwise, don't resample small images. These are often used for
101 // borders and rules (think 1x1 images used to make lines).
102 return InterpolationNone;
105 if (srcHeight * kLargeStretch <= destHeight || srcWidth * kLargeStretch <= destWidth) {
106 // Large image detected.
108 // Don't resample if it is being stretched a lot in only one direction.
109 // This is trying to catch cases where somebody has created a border
110 // (which might be large) and then is stretching it to fill some part
112 if (widthNearlyEqual || heightNearlyEqual)
113 return InterpolationNone;
115 // The image is growing a lot and in more than one direction. Resampling
116 // is slow and doesn't give us very much when growing a lot.
117 return InterpolationLow;
120 if ((diffWidth / srcWidth < kFractionalChangeThreshold)
121 && (diffHeight / srcHeight < kFractionalChangeThreshold)) {
122 // It is disappointingly common on the web for image sizes to be off by
123 // one or two pixels. We don't bother resampling if the size difference
124 // is a small fraction of the original size.
125 return InterpolationNone;
128 // When the image is not yet done loading, use linear. We don't cache the
129 // partially resampled images, and as they come in incrementally, it causes
130 // us to have to resample the whole thing every time.
131 if (!isDataComplete())
132 return InterpolationLow;
134 // Everything else gets resampled.
135 // High quality interpolation only enabled for scaling and translation.
136 if (!(matrix.getType() & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)))
137 return InterpolationHigh;
139 return InterpolationLow;
142 static InterpolationQuality limitInterpolationQuality(GraphicsContext* context, InterpolationQuality resampling)
144 return std::min(resampling, context->imageInterpolationQuality());
147 static SkPaint::FilterLevel convertToSkiaFilterLevel(bool useBicubicFilter, InterpolationQuality resampling)
149 // FIXME: If we get rid of this special case, this function can go away entirely.
150 if (useBicubicFilter)
151 return SkPaint::kHigh_FilterLevel;
153 // InterpolationHigh if useBicubicFilter is false means that we do
154 // a manual high quality resampling before drawing to Skia.
155 if (resampling == InterpolationHigh)
156 return SkPaint::kNone_FilterLevel;
158 return static_cast<SkPaint::FilterLevel>(resampling);
161 // This function is used to scale an image and extract a scaled fragment.
165 // Because the scaled image size has to be integers, we approximate the real
166 // scale with the following formula (only X direction is shown):
168 // scaledImageWidth = round(scaleX * imageRect.width())
169 // approximateScaleX = scaledImageWidth / imageRect.width()
171 // With this method we maintain a constant scale factor among fragments in
172 // the scaled image. This allows fragments to stitch together to form the
173 // full scaled image. The downside is there will be a small difference
174 // between |scaleX| and |approximateScaleX|.
176 // A scaled image fragment is identified by:
178 // - Scaled image size
179 // - Scaled image fragment rectangle (IntRect)
181 // Scaled image size has been determined and the next step is to compute the
182 // rectangle for the scaled image fragment which needs to be an IntRect.
184 // scaledSrcRect = srcRect * (approximateScaleX, approximateScaleY)
185 // enclosingScaledSrcRect = enclosingIntRect(scaledSrcRect)
187 // Finally we extract the scaled image fragment using
188 // (scaledImageSize, enclosingScaledSrcRect).
190 SkBitmap NativeImageSkia::extractScaledImageFragment(const SkRect& srcRect, float scaleX, float scaleY, SkRect* scaledSrcRect) const
192 SkISize imageSize = SkISize::Make(bitmap().width(), bitmap().height());
193 SkISize scaledImageSize = SkISize::Make(clampToInteger(roundf(imageSize.width() * scaleX)),
194 clampToInteger(roundf(imageSize.height() * scaleY)));
196 SkRect imageRect = SkRect::MakeWH(imageSize.width(), imageSize.height());
197 SkRect scaledImageRect = SkRect::MakeWH(scaledImageSize.width(), scaledImageSize.height());
199 SkMatrix scaleTransform;
200 scaleTransform.setRectToRect(imageRect, scaledImageRect, SkMatrix::kFill_ScaleToFit);
201 scaleTransform.mapRect(scaledSrcRect, srcRect);
203 scaledSrcRect->intersect(scaledImageRect);
204 SkIRect enclosingScaledSrcRect = enclosingIntRect(*scaledSrcRect);
206 // |enclosingScaledSrcRect| can be larger than |scaledImageSize| because
207 // of float inaccuracy so clip to get inside.
208 enclosingScaledSrcRect.intersect(SkIRect::MakeSize(scaledImageSize));
210 // scaledSrcRect is relative to the pixel snapped fragment we're extracting.
211 scaledSrcRect->offset(-enclosingScaledSrcRect.x(), -enclosingScaledSrcRect.y());
213 return resizedBitmap(scaledImageSize, enclosingScaledSrcRect);
216 // This does a lot of computation to resample only the portion of the bitmap
217 // that will only be drawn. This is critical for performance since when we are
218 // scrolling, for example, we are only drawing a small strip of the image.
219 // Resampling the whole image every time is very slow, so this speeds up things
222 // Note: this code is only used when the canvas transformation is limited to
223 // scaling or translation.
224 void NativeImageSkia::drawResampledBitmap(GraphicsContext* context, SkPaint& paint, const SkRect& srcRect, const SkRect& destRect) const
226 TRACE_EVENT0("skia", "drawResampledBitmap");
227 if (context->paintingDisabled())
229 // We want to scale |destRect| with transformation in the canvas to obtain
230 // the final scale. The final scale is a combination of scale transform
231 // in canvas and explicit scaling (srcRect and destRect).
233 context->getTotalMatrix().mapRect(&screenRect, destRect);
234 float realScaleX = screenRect.width() / srcRect.width();
235 float realScaleY = screenRect.height() / srcRect.height();
237 // This part of code limits scaling only to visible portion in the
238 SkRect destRectVisibleSubset;
239 if (!context->canvas()->getClipBounds(&destRectVisibleSubset))
242 // ClipRectToCanvas often overshoots, resulting in a larger region than our
243 // original destRect. Intersecting gets us back inside.
244 if (!destRectVisibleSubset.intersect(destRect))
245 return; // Nothing visible in destRect.
247 // Find the corresponding rect in the source image.
248 SkMatrix destToSrcTransform;
249 SkRect srcRectVisibleSubset;
250 destToSrcTransform.setRectToRect(destRect, srcRect, SkMatrix::kFill_ScaleToFit);
251 destToSrcTransform.mapRect(&srcRectVisibleSubset, destRectVisibleSubset);
253 SkRect scaledSrcRect;
254 SkBitmap scaledImageFragment = extractScaledImageFragment(srcRectVisibleSubset, realScaleX, realScaleY, &scaledSrcRect);
256 context->drawBitmapRect(scaledImageFragment, &scaledSrcRect, destRectVisibleSubset, &paint);
259 NativeImageSkia::NativeImageSkia()
260 : m_resizeRequests(0)
264 NativeImageSkia::NativeImageSkia(const SkBitmap& other)
266 , m_resizeRequests(0)
270 NativeImageSkia::NativeImageSkia(const SkBitmap& image, const SkBitmap& resizedImage, const ImageResourceInfo& cachedImageInfo, int resizeRequests)
272 , m_resizedImage(resizedImage)
273 , m_cachedImageInfo(cachedImageInfo)
274 , m_resizeRequests(resizeRequests)
278 NativeImageSkia::~NativeImageSkia()
282 int NativeImageSkia::decodedSize() const
284 return m_image.getSize() + m_resizedImage.getSize();
287 bool NativeImageSkia::hasResizedBitmap(const SkISize& scaledImageSize, const SkIRect& scaledImageSubset) const
289 bool imageScaleEqual = m_cachedImageInfo.scaledImageSize == scaledImageSize;
290 bool scaledImageSubsetAvailable = m_cachedImageInfo.scaledImageSubset.contains(scaledImageSubset);
291 return imageScaleEqual && scaledImageSubsetAvailable && !m_resizedImage.empty();
294 SkBitmap NativeImageSkia::resizedBitmap(const SkISize& scaledImageSize, const SkIRect& scaledImageSubset) const
296 ASSERT(!DeferredImageDecoder::isLazyDecoded(m_image));
298 if (!hasResizedBitmap(scaledImageSize, scaledImageSubset)) {
299 bool shouldCache = isDataComplete()
300 && shouldCacheResampling(scaledImageSize, scaledImageSubset);
302 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "ResizeImage", "cached", shouldCache);
303 // FIXME(361045): remove InspectorInstrumentation calls once DevTools Timeline migrates to tracing.
304 PlatformInstrumentation::willResizeImage(shouldCache);
305 SkBitmap resizedImage = skia::ImageOperations::Resize(m_image, skia::ImageOperations::RESIZE_LANCZOS3, scaledImageSize.width(), scaledImageSize.height(), scaledImageSubset);
306 resizedImage.setImmutable();
307 PlatformInstrumentation::didResizeImage();
312 m_resizedImage = resizedImage;
315 SkBitmap resizedSubset;
316 SkIRect resizedSubsetRect = m_cachedImageInfo.rectInSubset(scaledImageSubset);
317 m_resizedImage.extractSubset(&resizedSubset, resizedSubsetRect);
318 return resizedSubset;
321 static bool shouldDrawAntiAliased(GraphicsContext* context, const SkRect& destRect)
323 if (!context->shouldAntialias())
325 const SkMatrix totalMatrix = context->getTotalMatrix();
326 // Don't disable anti-aliasing if we're rotated or skewed.
327 if (!totalMatrix.rectStaysRect())
329 // Disable anti-aliasing for scales or n*90 degree rotations.
330 // Allow to opt out of the optimization though for "hairline" geometry
331 // images - using the shouldAntialiasHairlineImages() GraphicsContext flag.
332 if (!context->shouldAntialiasHairlineImages())
334 // Check if the dimensions of the destination are "small" (less than one
335 // device pixel). To prevent sudden drop-outs. Since we know that
336 // kRectStaysRect_Mask is set, the matrix either has scale and no skew or
337 // vice versa. We can query the kAffine_Mask flag to determine which case
339 // FIXME: This queries the CTM while drawing, which is generally
340 // discouraged. Always drawing with AA can negatively impact performance
341 // though - that's why it's not always on.
342 SkScalar widthExpansion, heightExpansion;
343 if (totalMatrix.getType() & SkMatrix::kAffine_Mask)
344 widthExpansion = totalMatrix[SkMatrix::kMSkewY], heightExpansion = totalMatrix[SkMatrix::kMSkewX];
346 widthExpansion = totalMatrix[SkMatrix::kMScaleX], heightExpansion = totalMatrix[SkMatrix::kMScaleY];
347 return destRect.width() * fabs(widthExpansion) < 1 || destRect.height() * fabs(heightExpansion) < 1;
350 void NativeImageSkia::draw(GraphicsContext* context, const SkRect& srcRect, const SkRect& destRect, PassRefPtr<SkXfermode> compOp) const
352 TRACE_EVENT0("skia", "NativeImageSkia::draw");
354 paint.setXfermode(compOp.get());
355 paint.setColorFilter(context->colorFilter());
356 paint.setAlpha(context->getNormalizedAlpha());
357 paint.setLooper(context->drawLooper());
358 paint.setAntiAlias(shouldDrawAntiAliased(context, destRect));
360 bool isLazyDecoded = DeferredImageDecoder::isLazyDecoded(bitmap());
362 InterpolationQuality resampling;
363 if (context->isAccelerated()) {
364 resampling = InterpolationLow;
365 } else if (context->printing()) {
366 resampling = InterpolationNone;
367 } else if (isLazyDecoded) {
368 resampling = InterpolationHigh;
370 // Take into account scale applied to the canvas when computing sampling mode (e.g. CSS scale or page scale).
371 SkRect destRectTarget = destRect;
372 SkMatrix totalMatrix = context->getTotalMatrix();
373 if (!(totalMatrix.getType() & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)))
374 totalMatrix.mapRect(&destRectTarget, destRect);
376 resampling = computeInterpolationQuality(totalMatrix,
377 SkScalarToFloat(srcRect.width()), SkScalarToFloat(srcRect.height()),
378 SkScalarToFloat(destRectTarget.width()), SkScalarToFloat(destRectTarget.height()));
381 if (resampling == InterpolationNone) {
382 // FIXME: This is to not break tests (it results in the filter bitmap flag
383 // being set to true). We need to decide if we respect InterpolationNone
384 // being returned from computeInterpolationQuality.
385 resampling = InterpolationLow;
387 resampling = limitInterpolationQuality(context, resampling);
389 // FIXME: Bicubic filtering in Skia is only applied to defer-decoded images
390 // as an experiment. Once this filtering code path becomes stable we should
391 // turn this on for all cases, including non-defer-decoded images.
392 bool useBicubicFilter = resampling == InterpolationHigh && isLazyDecoded;
394 paint.setFilterLevel(convertToSkiaFilterLevel(useBicubicFilter, resampling));
396 if (resampling == InterpolationHigh && !useBicubicFilter) {
397 // Resample the image and then draw the result to canvas with bilinear
399 drawResampledBitmap(context, paint, srcRect, destRect);
401 // We want to filter it if we decided to do interpolation above, or if
402 // there is something interesting going on with the matrix (like a rotation).
403 // Note: for serialization, we will want to subset the bitmap first so we
404 // don't send extra pixels.
405 context->drawBitmapRect(bitmap(), &srcRect, destRect, &paint);
408 PlatformInstrumentation::didDrawLazyPixelRef(bitmap().getGenerationID());
409 context->didDrawRect(destRect, paint, &bitmap());
412 static SkBitmap createBitmapWithSpace(const SkBitmap& bitmap, int spaceWidth, int spaceHeight)
414 SkImageInfo info = bitmap.info();
415 info.fWidth += spaceWidth;
416 info.fHeight += spaceHeight;
417 info.fAlphaType = kPremul_SkAlphaType;
420 result.allocPixels(info);
421 result.eraseColor(SK_ColorTRANSPARENT);
422 bitmap.copyPixelsTo(reinterpret_cast<uint8_t*>(result.getPixels()), result.rowBytes() * result.height(), result.rowBytes());
427 void NativeImageSkia::drawPattern(
428 GraphicsContext* context,
429 const FloatRect& floatSrcRect,
430 const FloatSize& scale,
431 const FloatPoint& phase,
432 CompositeOperator compositeOp,
433 const FloatRect& destRect,
434 blink::WebBlendMode blendMode,
435 const IntSize& repeatSpacing) const
437 FloatRect normSrcRect = floatSrcRect;
438 normSrcRect.intersect(FloatRect(0, 0, bitmap().width(), bitmap().height()));
439 if (destRect.isEmpty() || normSrcRect.isEmpty())
440 return; // nothing to draw
442 SkMatrix totalMatrix = context->getTotalMatrix();
443 AffineTransform ctm = context->getCTM();
444 SkScalar ctmScaleX = ctm.xScale();
445 SkScalar ctmScaleY = ctm.yScale();
446 totalMatrix.preScale(scale.width(), scale.height());
448 // Figure out what size the bitmap will be in the destination. The
449 // destination rect is the bounds of the pattern, we need to use the
450 // matrix to see how big it will be.
451 SkRect destRectTarget;
452 totalMatrix.mapRect(&destRectTarget, normSrcRect);
454 float destBitmapWidth = SkScalarToFloat(destRectTarget.width());
455 float destBitmapHeight = SkScalarToFloat(destRectTarget.height());
457 bool isLazyDecoded = DeferredImageDecoder::isLazyDecoded(bitmap());
459 // Compute the resampling mode.
460 InterpolationQuality resampling;
461 if (context->isAccelerated() || context->printing())
462 resampling = InterpolationLow;
463 else if (isLazyDecoded)
464 resampling = InterpolationHigh;
466 resampling = computeInterpolationQuality(totalMatrix, normSrcRect.width(), normSrcRect.height(), destBitmapWidth, destBitmapHeight);
467 resampling = limitInterpolationQuality(context, resampling);
469 SkMatrix localMatrix;
470 // We also need to translate it such that the origin of the pattern is the
471 // origin of the destination rect, which is what WebKit expects. Skia uses
472 // the coordinate system origin as the base for the pattern. If WebKit wants
473 // a shifted image, it will shift it from there using the localMatrix.
474 const float adjustedX = phase.x() + normSrcRect.x() * scale.width();
475 const float adjustedY = phase.y() + normSrcRect.y() * scale.height();
476 localMatrix.setTranslate(SkFloatToScalar(adjustedX), SkFloatToScalar(adjustedY));
478 RefPtr<SkShader> shader;
480 // Bicubic filter is only applied to defer-decoded images, see
481 // NativeImageSkia::draw for details.
482 bool useBicubicFilter = resampling == InterpolationHigh && isLazyDecoded;
484 if (resampling == InterpolationHigh && !useBicubicFilter) {
485 // Do nice resampling.
486 float scaleX = destBitmapWidth / normSrcRect.width();
487 float scaleY = destBitmapHeight / normSrcRect.height();
488 SkRect scaledSrcRect;
490 // Since we are resizing the bitmap, we need to remove the scale
491 // applied to the pixels in the bitmap shader. This means we need
492 // CTM * localMatrix to have identity scale. Since we
493 // can't modify CTM (or the rectangle will be drawn in the wrong
494 // place), we must set localMatrix's scale to the inverse of
496 localMatrix.preScale(ctmScaleX ? 1 / ctmScaleX : 1, ctmScaleY ? 1 / ctmScaleY : 1);
498 // The image fragment generated here is not exactly what is
499 // requested. The scale factor used is approximated and image
500 // fragment is slightly larger to align to integer
502 SkBitmap resampled = extractScaledImageFragment(normSrcRect, scaleX, scaleY, &scaledSrcRect);
503 if (repeatSpacing.isZero()) {
504 shader = adoptRef(SkShader::CreateBitmapShader(resampled, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix));
506 shader = adoptRef(SkShader::CreateBitmapShader(
507 createBitmapWithSpace(resampled, repeatSpacing.width() * ctmScaleX, repeatSpacing.height() * ctmScaleY),
508 SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix));
511 // Because no resizing occurred, the shader transform should be
512 // set to the pattern's transform, which just includes scale.
513 localMatrix.preScale(scale.width(), scale.height());
515 // No need to resample before drawing.
517 bitmap().extractSubset(&srcSubset, enclosingIntRect(normSrcRect));
518 if (repeatSpacing.isZero()) {
519 shader = adoptRef(SkShader::CreateBitmapShader(srcSubset, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix));
521 shader = adoptRef(SkShader::CreateBitmapShader(
522 createBitmapWithSpace(srcSubset, repeatSpacing.width() * ctmScaleX, repeatSpacing.height() * ctmScaleY),
523 SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix));
528 paint.setShader(shader.get());
529 paint.setXfermode(WebCoreCompositeToSkiaComposite(compositeOp, blendMode).get());
530 paint.setColorFilter(context->colorFilter());
531 paint.setFilterLevel(convertToSkiaFilterLevel(useBicubicFilter, resampling));
534 PlatformInstrumentation::didDrawLazyPixelRef(bitmap().getGenerationID());
536 context->drawRect(destRect, paint);
539 bool NativeImageSkia::shouldCacheResampling(const SkISize& scaledImageSize, const SkIRect& scaledImageSubset) const
541 // Check whether the requested dimensions match previous request.
542 bool matchesPreviousRequest = m_cachedImageInfo.isEqual(scaledImageSize, scaledImageSubset);
543 if (matchesPreviousRequest)
546 m_cachedImageInfo.set(scaledImageSize, scaledImageSubset);
547 m_resizeRequests = 0;
548 // Reset m_resizedImage now, because we don't distinguish
549 // between the last requested resize info and m_resizedImage's
551 m_resizedImage.reset();
554 // We can not cache incomplete frames. This might be a good optimization in
555 // the future, were we know how much of the frame has been decoded, so when
556 // we incrementally draw more of the image, we only have to resample the
557 // parts that are changed.
558 if (!isDataComplete())
561 // If the destination bitmap is excessively large, we'll never allow caching.
562 static const unsigned long long kLargeBitmapSize = 4096ULL * 4096ULL;
563 unsigned long long fullSize = static_cast<unsigned long long>(scaledImageSize.width()) * static_cast<unsigned long long>(scaledImageSize.height());
564 unsigned long long fragmentSize = static_cast<unsigned long long>(scaledImageSubset.width()) * static_cast<unsigned long long>(scaledImageSubset.height());
566 if (fragmentSize > kLargeBitmapSize)
569 // If the destination bitmap is small, we'll always allow caching, since
570 // there is not very much penalty for computing it and it may come in handy.
571 static const unsigned kSmallBitmapSize = 4096;
572 if (fragmentSize <= kSmallBitmapSize)
575 // If "too many" requests have been made for this bitmap, we assume that
576 // many more will be made as well, and we'll go ahead and cache it.
577 static const int kManyRequestThreshold = 4;
578 if (m_resizeRequests >= kManyRequestThreshold)
581 // If more than 1/4 of the resized image is requested, it's worth caching.
582 return fragmentSize > fullSize / 4;
585 NativeImageSkia::ImageResourceInfo::ImageResourceInfo()
587 scaledImageSize.setEmpty();
588 scaledImageSubset.setEmpty();
591 bool NativeImageSkia::ImageResourceInfo::isEqual(const SkISize& otherScaledImageSize, const SkIRect& otherScaledImageSubset) const
593 return scaledImageSize == otherScaledImageSize && scaledImageSubset == otherScaledImageSubset;
596 void NativeImageSkia::ImageResourceInfo::set(const SkISize& otherScaledImageSize, const SkIRect& otherScaledImageSubset)
598 scaledImageSize = otherScaledImageSize;
599 scaledImageSubset = otherScaledImageSubset;
602 SkIRect NativeImageSkia::ImageResourceInfo::rectInSubset(const SkIRect& otherScaledImageSubset)
604 if (!scaledImageSubset.contains(otherScaledImageSubset))
605 return SkIRect::MakeEmpty();
606 SkIRect subsetRect = otherScaledImageSubset;
607 subsetRect.offset(-scaledImageSubset.x(), -scaledImageSubset.y());
611 } // namespace WebCore