Home | History | Annotate | Download | only in skia
      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
    612