Upstream version 8.37.180.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / platform / graphics / skia / NativeImageSkia.cpp
1 /*
2  * Copyright (c) 2008, Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
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
13  * distribution.
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.
17  *
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.
29  */
30
31 #include "config.h"
32 #include "platform/graphics/skia/NativeImageSkia.h"
33
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"
48
49 #include <algorithm>
50 #include <math.h>
51 #include <limits>
52
53 namespace WebCore {
54
55 static bool nearlyIntegral(float value)
56 {
57     return fabs(value - floorf(value)) < std::numeric_limits<float>::epsilon();
58 }
59
60 InterpolationQuality NativeImageSkia::computeInterpolationQuality(const SkMatrix& matrix, float srcWidth, float srcHeight, float destWidth, float destHeight) const
61 {
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;
66
67     // Images smaller than this in either direction are considered "small" and
68     // are not resampled ever (see below).
69     const int kSmallImageSizeThreshold = 8;
70
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;
75
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;
86
87     if (srcWidth <= kSmallImageSizeThreshold
88         || srcHeight <= kSmallImageSizeThreshold
89         || destWidth <= kSmallImageSizeThreshold
90         || destHeight <= kSmallImageSizeThreshold) {
91         // Small image detected.
92
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;
99
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;
103     }
104
105     if (srcHeight * kLargeStretch <= destHeight || srcWidth * kLargeStretch <= destWidth) {
106         // Large image detected.
107
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
111         // of the page.
112         if (widthNearlyEqual || heightNearlyEqual)
113             return InterpolationNone;
114
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;
118     }
119
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;
126     }
127
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;
133
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;
138
139     return InterpolationLow;
140 }
141
142 static InterpolationQuality limitInterpolationQuality(GraphicsContext* context, InterpolationQuality resampling)
143 {
144     return std::min(resampling, context->imageInterpolationQuality());
145 }
146
147 static SkPaint::FilterLevel convertToSkiaFilterLevel(bool useBicubicFilter, InterpolationQuality resampling)
148 {
149     // FIXME: If we get rid of this special case, this function can go away entirely.
150     if (useBicubicFilter)
151         return SkPaint::kHigh_FilterLevel;
152
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;
157
158     return static_cast<SkPaint::FilterLevel>(resampling);
159 }
160
161 // This function is used to scale an image and extract a scaled fragment.
162 //
163 // ALGORITHM
164 //
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):
167 //
168 // scaledImageWidth = round(scaleX * imageRect.width())
169 // approximateScaleX = scaledImageWidth / imageRect.width()
170 //
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|.
175 //
176 // A scaled image fragment is identified by:
177 //
178 // - Scaled image size
179 // - Scaled image fragment rectangle (IntRect)
180 //
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.
183 //
184 // scaledSrcRect = srcRect * (approximateScaleX, approximateScaleY)
185 // enclosingScaledSrcRect = enclosingIntRect(scaledSrcRect)
186 //
187 // Finally we extract the scaled image fragment using
188 // (scaledImageSize, enclosingScaledSrcRect).
189 //
190 SkBitmap NativeImageSkia::extractScaledImageFragment(const SkRect& srcRect, float scaleX, float scaleY, SkRect* scaledSrcRect) const
191 {
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)));
195
196     SkRect imageRect = SkRect::MakeWH(imageSize.width(), imageSize.height());
197     SkRect scaledImageRect = SkRect::MakeWH(scaledImageSize.width(), scaledImageSize.height());
198
199     SkMatrix scaleTransform;
200     scaleTransform.setRectToRect(imageRect, scaledImageRect, SkMatrix::kFill_ScaleToFit);
201     scaleTransform.mapRect(scaledSrcRect, srcRect);
202
203     scaledSrcRect->intersect(scaledImageRect);
204     SkIRect enclosingScaledSrcRect = enclosingIntRect(*scaledSrcRect);
205
206     // |enclosingScaledSrcRect| can be larger than |scaledImageSize| because
207     // of float inaccuracy so clip to get inside.
208     enclosingScaledSrcRect.intersect(SkIRect::MakeSize(scaledImageSize));
209
210     // scaledSrcRect is relative to the pixel snapped fragment we're extracting.
211     scaledSrcRect->offset(-enclosingScaledSrcRect.x(), -enclosingScaledSrcRect.y());
212
213     return resizedBitmap(scaledImageSize, enclosingScaledSrcRect);
214 }
215
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
220 // dramatically.
221 //
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
225 {
226     TRACE_EVENT0("skia", "drawResampledBitmap");
227     if (context->paintingDisabled())
228         return;
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).
232     SkRect screenRect;
233     context->getTotalMatrix().mapRect(&screenRect, destRect);
234     float realScaleX = screenRect.width() / srcRect.width();
235     float realScaleY = screenRect.height() / srcRect.height();
236
237     // This part of code limits scaling only to visible portion in the
238     SkRect destRectVisibleSubset;
239     if (!context->canvas()->getClipBounds(&destRectVisibleSubset))
240         return;
241
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.
246
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);
252
253     SkRect scaledSrcRect;
254     SkBitmap scaledImageFragment = extractScaledImageFragment(srcRectVisibleSubset, realScaleX, realScaleY, &scaledSrcRect);
255
256     context->drawBitmapRect(scaledImageFragment, &scaledSrcRect, destRectVisibleSubset, &paint);
257 }
258
259 NativeImageSkia::NativeImageSkia()
260     : m_resizeRequests(0)
261 {
262 }
263
264 NativeImageSkia::NativeImageSkia(const SkBitmap& other)
265     : m_image(other)
266     , m_resizeRequests(0)
267 {
268 }
269
270 NativeImageSkia::NativeImageSkia(const SkBitmap& image, const SkBitmap& resizedImage, const ImageResourceInfo& cachedImageInfo, int resizeRequests)
271     : m_image(image)
272     , m_resizedImage(resizedImage)
273     , m_cachedImageInfo(cachedImageInfo)
274     , m_resizeRequests(resizeRequests)
275 {
276 }
277
278 NativeImageSkia::~NativeImageSkia()
279 {
280 }
281
282 int NativeImageSkia::decodedSize() const
283 {
284     return m_image.getSize() + m_resizedImage.getSize();
285 }
286
287 bool NativeImageSkia::hasResizedBitmap(const SkISize& scaledImageSize, const SkIRect& scaledImageSubset) const
288 {
289     bool imageScaleEqual = m_cachedImageInfo.scaledImageSize == scaledImageSize;
290     bool scaledImageSubsetAvailable = m_cachedImageInfo.scaledImageSubset.contains(scaledImageSubset);
291     return imageScaleEqual && scaledImageSubsetAvailable && !m_resizedImage.empty();
292 }
293
294 SkBitmap NativeImageSkia::resizedBitmap(const SkISize& scaledImageSize, const SkIRect& scaledImageSubset) const
295 {
296     ASSERT(!DeferredImageDecoder::isLazyDecoded(m_image));
297
298     if (!hasResizedBitmap(scaledImageSize, scaledImageSubset)) {
299         bool shouldCache = isDataComplete()
300             && shouldCacheResampling(scaledImageSize, scaledImageSubset);
301
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();
308
309         if (!shouldCache)
310             return resizedImage;
311
312         m_resizedImage = resizedImage;
313     }
314
315     SkBitmap resizedSubset;
316     SkIRect resizedSubsetRect = m_cachedImageInfo.rectInSubset(scaledImageSubset);
317     m_resizedImage.extractSubset(&resizedSubset, resizedSubsetRect);
318     return resizedSubset;
319 }
320
321 static bool shouldDrawAntiAliased(GraphicsContext* context, const SkRect& destRect)
322 {
323     if (!context->shouldAntialias())
324         return false;
325     const SkMatrix totalMatrix = context->getTotalMatrix();
326     // Don't disable anti-aliasing if we're rotated or skewed.
327     if (!totalMatrix.rectStaysRect())
328         return true;
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())
333         return false;
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
338     // it is.
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];
345     else
346         widthExpansion = totalMatrix[SkMatrix::kMScaleX], heightExpansion = totalMatrix[SkMatrix::kMScaleY];
347     return destRect.width() * fabs(widthExpansion) < 1 || destRect.height() * fabs(heightExpansion) < 1;
348 }
349
350 void NativeImageSkia::draw(GraphicsContext* context, const SkRect& srcRect, const SkRect& destRect, PassRefPtr<SkXfermode> compOp) const
351 {
352     TRACE_EVENT0("skia", "NativeImageSkia::draw");
353     SkPaint paint;
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));
359
360     bool isLazyDecoded = DeferredImageDecoder::isLazyDecoded(bitmap());
361
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;
369     } else {
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);
375
376         resampling = computeInterpolationQuality(totalMatrix,
377             SkScalarToFloat(srcRect.width()), SkScalarToFloat(srcRect.height()),
378             SkScalarToFloat(destRectTarget.width()), SkScalarToFloat(destRectTarget.height()));
379     }
380
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;
386     }
387     resampling = limitInterpolationQuality(context, resampling);
388
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;
393
394     paint.setFilterLevel(convertToSkiaFilterLevel(useBicubicFilter, resampling));
395
396     if (resampling == InterpolationHigh && !useBicubicFilter) {
397         // Resample the image and then draw the result to canvas with bilinear
398         // filtering.
399         drawResampledBitmap(context, paint, srcRect, destRect);
400     } else {
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);
406     }
407     if (isLazyDecoded)
408         PlatformInstrumentation::didDrawLazyPixelRef(bitmap().getGenerationID());
409     context->didDrawRect(destRect, paint, &bitmap());
410 }
411
412 static SkBitmap createBitmapWithSpace(const SkBitmap& bitmap, int spaceWidth, int spaceHeight)
413 {
414     SkImageInfo info = bitmap.info();
415     info.fWidth += spaceWidth;
416     info.fHeight += spaceHeight;
417     info.fAlphaType = kPremul_SkAlphaType;
418
419     SkBitmap result;
420     result.allocPixels(info);
421     result.eraseColor(SK_ColorTRANSPARENT);
422     bitmap.copyPixelsTo(reinterpret_cast<uint8_t*>(result.getPixels()), result.rowBytes() * result.height(), result.rowBytes());
423
424     return result;
425 }
426
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
436 {
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
441
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());
447
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);
453
454     float destBitmapWidth = SkScalarToFloat(destRectTarget.width());
455     float destBitmapHeight = SkScalarToFloat(destRectTarget.height());
456
457     bool isLazyDecoded = DeferredImageDecoder::isLazyDecoded(bitmap());
458
459     // Compute the resampling mode.
460     InterpolationQuality resampling;
461     if (context->isAccelerated() || context->printing())
462         resampling = InterpolationLow;
463     else if (isLazyDecoded)
464         resampling = InterpolationHigh;
465     else
466         resampling = computeInterpolationQuality(totalMatrix, normSrcRect.width(), normSrcRect.height(), destBitmapWidth, destBitmapHeight);
467     resampling = limitInterpolationQuality(context, resampling);
468
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));
477
478     RefPtr<SkShader> shader;
479
480     // Bicubic filter is only applied to defer-decoded images, see
481     // NativeImageSkia::draw for details.
482     bool useBicubicFilter = resampling == InterpolationHigh && isLazyDecoded;
483
484     if (resampling == InterpolationHigh && !useBicubicFilter) {
485         // Do nice resampling.
486         float scaleX = destBitmapWidth / normSrcRect.width();
487         float scaleY = destBitmapHeight / normSrcRect.height();
488         SkRect scaledSrcRect;
489
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
495         // CTM scale.
496         localMatrix.preScale(ctmScaleX ? 1 / ctmScaleX : 1, ctmScaleY ? 1 / ctmScaleY : 1);
497
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
501         // boundaries.
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));
505         } else {
506             shader = adoptRef(SkShader::CreateBitmapShader(
507                 createBitmapWithSpace(resampled, repeatSpacing.width() * ctmScaleX, repeatSpacing.height() * ctmScaleY),
508                 SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix));
509         }
510     } else {
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());
514
515         // No need to resample before drawing.
516         SkBitmap srcSubset;
517         bitmap().extractSubset(&srcSubset, enclosingIntRect(normSrcRect));
518         if (repeatSpacing.isZero()) {
519             shader = adoptRef(SkShader::CreateBitmapShader(srcSubset, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix));
520         } else {
521             shader = adoptRef(SkShader::CreateBitmapShader(
522                 createBitmapWithSpace(srcSubset, repeatSpacing.width() * ctmScaleX, repeatSpacing.height() * ctmScaleY),
523                 SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix));
524         }
525     }
526
527     SkPaint paint;
528     paint.setShader(shader.get());
529     paint.setXfermode(WebCoreCompositeToSkiaComposite(compositeOp, blendMode).get());
530     paint.setColorFilter(context->colorFilter());
531     paint.setFilterLevel(convertToSkiaFilterLevel(useBicubicFilter, resampling));
532
533     if (isLazyDecoded)
534         PlatformInstrumentation::didDrawLazyPixelRef(bitmap().getGenerationID());
535
536     context->drawRect(destRect, paint);
537 }
538
539 bool NativeImageSkia::shouldCacheResampling(const SkISize& scaledImageSize, const SkIRect& scaledImageSubset) const
540 {
541     // Check whether the requested dimensions match previous request.
542     bool matchesPreviousRequest = m_cachedImageInfo.isEqual(scaledImageSize, scaledImageSubset);
543     if (matchesPreviousRequest)
544         ++m_resizeRequests;
545     else {
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
550         // resize info.
551         m_resizedImage.reset();
552     }
553
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())
559         return false;
560
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());
565
566     if (fragmentSize > kLargeBitmapSize)
567         return false;
568
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)
573         return true;
574
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)
579         return true;
580
581     // If more than 1/4 of the resized image is requested, it's worth caching.
582     return fragmentSize > fullSize / 4;
583 }
584
585 NativeImageSkia::ImageResourceInfo::ImageResourceInfo()
586 {
587     scaledImageSize.setEmpty();
588     scaledImageSubset.setEmpty();
589 }
590
591 bool NativeImageSkia::ImageResourceInfo::isEqual(const SkISize& otherScaledImageSize, const SkIRect& otherScaledImageSubset) const
592 {
593     return scaledImageSize == otherScaledImageSize && scaledImageSubset == otherScaledImageSubset;
594 }
595
596 void NativeImageSkia::ImageResourceInfo::set(const SkISize& otherScaledImageSize, const SkIRect& otherScaledImageSubset)
597 {
598     scaledImageSize = otherScaledImageSize;
599     scaledImageSubset = otherScaledImageSubset;
600 }
601
602 SkIRect NativeImageSkia::ImageResourceInfo::rectInSubset(const SkIRect& otherScaledImageSubset)
603 {
604     if (!scaledImageSubset.contains(otherScaledImageSubset))
605         return SkIRect::MakeEmpty();
606     SkIRect subsetRect = otherScaledImageSubset;
607     subsetRect.offset(-scaledImageSubset.x(), -scaledImageSubset.y());
608     return subsetRect;
609 }
610
611 } // namespace WebCore