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 "core/platform/graphics/skia/NativeImageSkia.h"
     33 
     34 #include "core/platform/PlatformInstrumentation.h"
     35 #include "core/platform/chromium/TraceEvent.h"
     36 #include "core/platform/graphics/FloatPoint.h"
     37 #include "core/platform/graphics/FloatRect.h"
     38 #include "core/platform/graphics/FloatSize.h"
     39 #include "core/platform/graphics/GraphicsContext.h"
     40 #include "core/platform/graphics/Image.h"
     41 #include "core/platform/graphics/chromium/DeferredImageDecoder.h"
     42 #include "core/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 <limits>
     50 #include <math.h>
     51 
     52 namespace WebCore {
     53 
     54 static bool nearlyIntegral(float value)
     55 {
     56     return fabs(value - floorf(value)) < std::numeric_limits<float>::epsilon();
     57 }
     58 
     59 ResamplingMode NativeImageSkia::computeResamplingMode(const SkMatrix& matrix, float srcWidth, float srcHeight, float destWidth, float destHeight) const
     60 {
     61     // The percent change below which we will not resample. This usually means
     62     // an off-by-one error on the web page, and just doing nearest neighbor
     63     // sampling is usually good enough.
     64     const float kFractionalChangeThreshold = 0.025f;
     65 
     66     // Images smaller than this in either direction are considered "small" and
     67     // are not resampled ever (see below).
     68     const int kSmallImageSizeThreshold = 8;
     69 
     70     // The amount an image can be stretched in a single direction before we
     71     // say that it is being stretched so much that it must be a line or
     72     // background that doesn't need resampling.
     73     const float kLargeStretch = 3.0f;
     74 
     75     // Figure out if we should resample this image. We try to prune out some
     76     // common cases where resampling won't give us anything, since it is much
     77     // slower than drawing stretched.
     78     float diffWidth = fabs(destWidth - srcWidth);
     79     float diffHeight = fabs(destHeight - srcHeight);
     80     bool widthNearlyEqual = diffWidth < std::numeric_limits<float>::epsilon();
     81     bool heightNearlyEqual = diffHeight < std::numeric_limits<float>::epsilon();
     82     // We don't need to resample if the source and destination are the same.
     83     if (widthNearlyEqual && heightNearlyEqual)
     84         return NoResampling;
     85 
     86     if (srcWidth <= kSmallImageSizeThreshold
     87         || srcHeight <= kSmallImageSizeThreshold
     88         || destWidth <= kSmallImageSizeThreshold
     89         || destHeight <= kSmallImageSizeThreshold) {
     90         // Small image detected.
     91 
     92         // Resample in the case where the new size would be non-integral.
     93         // This can cause noticeable breaks in repeating patterns, except
     94         // when the source image is only one pixel wide in that dimension.
     95         if ((!nearlyIntegral(destWidth) && srcWidth > 1 + std::numeric_limits<float>::epsilon())
     96             || (!nearlyIntegral(destHeight) && srcHeight > 1 + std::numeric_limits<float>::epsilon()))
     97             return LinearResampling;
     98 
     99         // Otherwise, don't resample small images. These are often used for
    100         // borders and rules (think 1x1 images used to make lines).
    101         return NoResampling;
    102     }
    103 
    104     if (srcHeight * kLargeStretch <= destHeight || srcWidth * kLargeStretch <= destWidth) {
    105         // Large image detected.
    106 
    107         // Don't resample if it is being stretched a lot in only one direction.
    108         // This is trying to catch cases where somebody has created a border
    109         // (which might be large) and then is stretching it to fill some part
    110         // of the page.
    111         if (widthNearlyEqual || heightNearlyEqual)
    112             return NoResampling;
    113 
    114         // The image is growing a lot and in more than one direction. Resampling
    115         // is slow and doesn't give us very much when growing a lot.
    116         return LinearResampling;
    117     }
    118 
    119     if ((diffWidth / srcWidth < kFractionalChangeThreshold)
    120         && (diffHeight / srcHeight < kFractionalChangeThreshold)) {
    121         // It is disappointingly common on the web for image sizes to be off by
    122         // one or two pixels. We don't bother resampling if the size difference
    123         // is a small fraction of the original size.
    124         return NoResampling;
    125     }
    126 
    127     // When the image is not yet done loading, use linear. We don't cache the
    128     // partially resampled images, and as they come in incrementally, it causes
    129     // us to have to resample the whole thing every time.
    130     if (!isDataComplete())
    131         return LinearResampling;
    132 
    133     // Everything else gets resampled.
    134     // High quality interpolation only enabled for scaling and translation.
    135     if (!(matrix.getType() & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)))
    136         return AwesomeResampling;
    137 
    138     return LinearResampling;
    139 }
    140 
    141 static ResamplingMode limitResamplingMode(GraphicsContext* context, ResamplingMode resampling)
    142 {
    143     switch (context->imageInterpolationQuality()) {
    144     case InterpolationNone:
    145         return NoResampling;
    146     case InterpolationMedium:
    147         // For now we treat InterpolationMedium and InterpolationLow the same.
    148     case InterpolationLow:
    149         if (resampling == AwesomeResampling)
    150             return LinearResampling;
    151         break;
    152     case InterpolationHigh:
    153     case InterpolationDefault:
    154         break;
    155     }
    156 
    157     return resampling;
    158 }
    159 
    160 // This function is used to scale an image and extract a scaled fragment.
    161 //
    162 // ALGORITHM
    163 //
    164 // Because the scaled image size has to be integers, we approximate the real
    165 // scale with the following formula (only X direction is shown):
    166 //
    167 // scaledImageWidth = round(scaleX * imageRect.width())
    168 // approximateScaleX = scaledImageWidth / imageRect.width()
    169 //
    170 // With this method we maintain a constant scale factor among fragments in
    171 // the scaled image. This allows fragments to stitch together to form the
    172 // full scaled image. The downside is there will be a small difference
    173 // between |scaleX| and |approximateScaleX|.
    174 //
    175 // A scaled image fragment is identified by:
    176 //
    177 // - Scaled image size
    178 // - Scaled image fragment rectangle (IntRect)
    179 //
    180 // Scaled image size has been determined and the next step is to compute the
    181 // rectangle for the scaled image fragment which needs to be an IntRect.
    182 //
    183 // scaledSrcRect = srcRect * (approximateScaleX, approximateScaleY)
    184 // enclosingScaledSrcRect = enclosingIntRect(scaledSrcRect)
    185 //
    186 // Finally we extract the scaled image fragment using
    187 // (scaledImageSize, enclosingScaledSrcRect).
    188 //
    189 SkBitmap NativeImageSkia::extractScaledImageFragment(const SkRect& srcRect, float scaleX, float scaleY, SkRect* scaledSrcRect) const
    190 {
    191     SkISize imageSize = SkISize::Make(bitmap().width(), bitmap().height());
    192     SkISize scaledImageSize = SkISize::Make(clampToInteger(roundf(imageSize.width() * scaleX)),
    193         clampToInteger(roundf(imageSize.height() * scaleY)));
    194 
    195     SkRect imageRect = SkRect::MakeWH(imageSize.width(), imageSize.height());
    196     SkRect scaledImageRect = SkRect::MakeWH(scaledImageSize.width(), scaledImageSize.height());
    197 
    198     SkMatrix scaleTransform;
    199     scaleTransform.setRectToRect(imageRect, scaledImageRect, SkMatrix::kFill_ScaleToFit);
    200     scaleTransform.mapRect(scaledSrcRect, srcRect);
    201 
    202     scaledSrcRect->intersect(scaledImageRect);
    203     SkIRect enclosingScaledSrcRect = enclosingIntRect(*scaledSrcRect);
    204 
    205     // |enclosingScaledSrcRect| can be larger than |scaledImageSize| because
    206     // of float inaccuracy so clip to get inside.
    207     enclosingScaledSrcRect.intersect(SkIRect::MakeSize(scaledImageSize));
    208 
    209     // scaledSrcRect is relative to the pixel snapped fragment we're extracting.
    210     scaledSrcRect->offset(-enclosingScaledSrcRect.x(), -enclosingScaledSrcRect.y());
    211 
    212     return resizedBitmap(scaledImageSize, enclosingScaledSrcRect);
    213 }
    214 
    215 // This does a lot of computation to resample only the portion of the bitmap
    216 // that will only be drawn. This is critical for performance since when we are
    217 // scrolling, for example, we are only drawing a small strip of the image.
    218 // Resampling the whole image every time is very slow, so this speeds up things
    219 // dramatically.
    220 //
    221 // Note: this code is only used when the canvas transformation is limited to
    222 // scaling or translation.
    223 void NativeImageSkia::drawResampledBitmap(GraphicsContext* context, SkPaint& paint, const SkRect& srcRect, const SkRect& destRect) const
    224 {
    225     TRACE_EVENT0("skia", "drawResampledBitmap");
    226     // We want to scale |destRect| with transformation in the canvas to obtain
    227     // the final scale. The final scale is a combination of scale transform
    228     // in canvas and explicit scaling (srcRect and destRect).
    229     SkRect screenRect;
    230     context->getTotalMatrix().mapRect(&screenRect, destRect);
    231     float realScaleX = screenRect.width() / srcRect.width();
    232     float realScaleY = screenRect.height() / srcRect.height();
    233 
    234     // This part of code limits scaling only to visible portion in the
    235     SkRect destRectVisibleSubset;
    236     ClipRectToCanvas(context, destRect, &destRectVisibleSubset);
    237 
    238     // ClipRectToCanvas often overshoots, resulting in a larger region than our
    239     // original destRect. Intersecting gets us back inside.
    240     if (!destRectVisibleSubset.intersect(destRect))
    241         return; // Nothing visible in destRect.
    242 
    243     // Find the corresponding rect in the source image.
    244     SkMatrix destToSrcTransform;
    245     SkRect srcRectVisibleSubset;
    246     destToSrcTransform.setRectToRect(destRect, srcRect, SkMatrix::kFill_ScaleToFit);
    247     destToSrcTransform.mapRect(&srcRectVisibleSubset, destRectVisibleSubset);
    248 
    249     SkRect scaledSrcRect;
    250     SkBitmap scaledImageFragment = extractScaledImageFragment(srcRectVisibleSubset, realScaleX, realScaleY, &scaledSrcRect);
    251 
    252     context->drawBitmapRect(scaledImageFragment, &scaledSrcRect, destRectVisibleSubset, &paint);
    253 }
    254 
    255 NativeImageSkia::NativeImageSkia()
    256     : m_resolutionScale(1)
    257     , m_resizeRequests(0)
    258 {
    259 }
    260 
    261 NativeImageSkia::NativeImageSkia(const SkBitmap& other, float resolutionScale)
    262     : m_image(other)
    263     , m_resolutionScale(resolutionScale)
    264     , m_resizeRequests(0)
    265 {
    266 }
    267 
    268 NativeImageSkia::NativeImageSkia(const SkBitmap& image, float resolutionScale, const SkBitmap& resizedImage, const ImageResourceInfo& cachedImageInfo, int resizeRequests)
    269     : m_image(image)
    270     , m_resolutionScale(resolutionScale)
    271     , m_resizedImage(resizedImage)
    272     , m_cachedImageInfo(cachedImageInfo)
    273     , m_resizeRequests(resizeRequests)
    274 {
    275 }
    276 
    277 NativeImageSkia::~NativeImageSkia()
    278 {
    279 }
    280 
    281 int NativeImageSkia::decodedSize() const
    282 {
    283     return m_image.getSize() + m_resizedImage.getSize();
    284 }
    285 
    286 bool NativeImageSkia::hasResizedBitmap(const SkISize& scaledImageSize, const SkIRect& scaledImageSubset) const
    287 {
    288     bool imageScaleEqual = m_cachedImageInfo.scaledImageSize == scaledImageSize;
    289     bool scaledImageSubsetAvailable = m_cachedImageInfo.scaledImageSubset.contains(scaledImageSubset);
    290     return imageScaleEqual && scaledImageSubsetAvailable && !m_resizedImage.empty();
    291 }
    292 
    293 SkBitmap NativeImageSkia::resizedBitmap(const SkISize& scaledImageSize, const SkIRect& scaledImageSubset) const
    294 {
    295     ASSERT(!DeferredImageDecoder::isLazyDecoded(m_image));
    296 
    297     if (!hasResizedBitmap(scaledImageSize, scaledImageSubset)) {
    298         bool shouldCache = isDataComplete()
    299             && shouldCacheResampling(scaledImageSize, scaledImageSubset);
    300 
    301         PlatformInstrumentation::willResizeImage(shouldCache);
    302         SkBitmap resizedImage = skia::ImageOperations::Resize(m_image, skia::ImageOperations::RESIZE_LANCZOS3, scaledImageSize.width(), scaledImageSize.height(), scaledImageSubset);
    303         resizedImage.setImmutable();
    304         PlatformInstrumentation::didResizeImage();
    305 
    306         if (!shouldCache)
    307             return resizedImage;
    308 
    309         m_resizedImage = resizedImage;
    310     }
    311 
    312     SkBitmap resizedSubset;
    313     SkIRect resizedSubsetRect = m_cachedImageInfo.rectInSubset(scaledImageSubset);
    314     m_resizedImage.extractSubset(&resizedSubset, resizedSubsetRect);
    315     return resizedSubset;
    316 }
    317 
    318 static bool hasNon90rotation(GraphicsContext* context)
    319 {
    320     return !context->getTotalMatrix().rectStaysRect();
    321 }
    322 
    323 void NativeImageSkia::draw(GraphicsContext* context, const SkRect& srcRect, const SkRect& destRect, SkXfermode::Mode compOp) const
    324 {
    325     TRACE_EVENT0("skia", "NativeImageSkia::draw");
    326     SkPaint paint;
    327     paint.setXfermodeMode(compOp);
    328     paint.setAlpha(context->getNormalizedAlpha());
    329     paint.setLooper(context->drawLooper());
    330     // only antialias if we're rotated or skewed
    331     paint.setAntiAlias(hasNon90rotation(context));
    332 
    333     ResamplingMode resampling;
    334     if (context->isAccelerated()) {
    335         resampling = LinearResampling;
    336     } else if (context->printing()) {
    337         resampling = NoResampling;
    338     } else {
    339         // Take into account scale applied to the canvas when computing sampling mode (e.g. CSS scale or page scale).
    340         SkRect destRectTarget = destRect;
    341         if (!(context->getTotalMatrix().getType() & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)))
    342             context->getTotalMatrix().mapRect(&destRectTarget, destRect);
    343 
    344         resampling = computeResamplingMode(context->getTotalMatrix(),
    345             SkScalarToFloat(srcRect.width()), SkScalarToFloat(srcRect.height()),
    346             SkScalarToFloat(destRectTarget.width()), SkScalarToFloat(destRectTarget.height()));
    347     }
    348 
    349     if (resampling == NoResampling) {
    350         // FIXME: This is to not break tests (it results in the filter bitmap flag
    351         // being set to true). We need to decide if we respect NoResampling
    352         // being returned from computeResamplingMode.
    353         resampling = LinearResampling;
    354     }
    355     resampling = limitResamplingMode(context, resampling);
    356     paint.setFilterBitmap(resampling == LinearResampling);
    357 
    358     // FIXME: Bicubic filtering in Skia is only applied to defer-decoded images
    359     // as an experiment. Once this filtering code path becomes stable we should
    360     // turn this on for all cases, including non-defer-decoded images.
    361     bool useBicubicFilter = resampling == AwesomeResampling
    362         && DeferredImageDecoder::isLazyDecoded(bitmap());
    363     if (useBicubicFilter)
    364         paint.setFilterLevel(SkPaint::kHigh_FilterLevel);
    365 
    366     if (resampling == AwesomeResampling && !useBicubicFilter) {
    367         // Resample the image and then draw the result to canvas with bilinear
    368         // filtering.
    369         drawResampledBitmap(context, paint, srcRect, destRect);
    370     } else {
    371         // We want to filter it if we decided to do interpolation above, or if
    372         // there is something interesting going on with the matrix (like a rotation).
    373         // Note: for serialization, we will want to subset the bitmap first so we
    374         // don't send extra pixels.
    375         context->drawBitmapRect(bitmap(), &srcRect, destRect, &paint);
    376     }
    377     context->didDrawRect(destRect, paint, &bitmap());
    378 }
    379 
    380 void NativeImageSkia::drawPattern(
    381     GraphicsContext* context,
    382     const FloatRect& floatSrcRect,
    383     const FloatSize& scale,
    384     const FloatPoint& phase,
    385     CompositeOperator compositeOp,
    386     const FloatRect& destRect,
    387     BlendMode blendMode) const
    388 {
    389     FloatRect normSrcRect = floatSrcRect;
    390     normSrcRect.intersect(FloatRect(0, 0, bitmap().width(), bitmap().height()));
    391     if (destRect.isEmpty() || normSrcRect.isEmpty())
    392         return; // nothing to draw
    393 
    394     SkMatrix totalMatrix = context->getTotalMatrix();
    395     SkScalar ctmScaleX = totalMatrix.getScaleX();
    396     SkScalar ctmScaleY = totalMatrix.getScaleY();
    397     totalMatrix.preScale(scale.width(), scale.height());
    398 
    399     // Figure out what size the bitmap will be in the destination. The
    400     // destination rect is the bounds of the pattern, we need to use the
    401     // matrix to see how big it will be.
    402     SkRect destRectTarget;
    403     totalMatrix.mapRect(&destRectTarget, normSrcRect);
    404 
    405     float destBitmapWidth = SkScalarToFloat(destRectTarget.width());
    406     float destBitmapHeight = SkScalarToFloat(destRectTarget.height());
    407 
    408     // Compute the resampling mode.
    409     ResamplingMode resampling;
    410     if (context->isAccelerated() || context->printing())
    411         resampling = LinearResampling;
    412     else
    413         resampling = computeResamplingMode(totalMatrix, normSrcRect.width(), normSrcRect.height(), destBitmapWidth, destBitmapHeight);
    414     resampling = limitResamplingMode(context, resampling);
    415 
    416     SkMatrix shaderTransform;
    417     RefPtr<SkShader> shader;
    418 
    419     // Bicubic filter is only applied to defer-decoded images, see
    420     // NativeImageSkia::draw for details.
    421     bool useBicubicFilter = resampling == AwesomeResampling && DeferredImageDecoder::isLazyDecoded(bitmap());
    422 
    423     if (resampling == AwesomeResampling && !useBicubicFilter) {
    424         // Do nice resampling.
    425         float scaleX = destBitmapWidth / normSrcRect.width();
    426         float scaleY = destBitmapHeight / normSrcRect.height();
    427         SkRect scaledSrcRect;
    428 
    429         // The image fragment generated here is not exactly what is
    430         // requested. The scale factor used is approximated and image
    431         // fragment is slightly larger to align to integer
    432         // boundaries.
    433         SkBitmap resampled = extractScaledImageFragment(normSrcRect, scaleX, scaleY, &scaledSrcRect);
    434         shader = adoptRef(SkShader::CreateBitmapShader(resampled, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode));
    435 
    436         // Since we just resized the bitmap, we need to remove the scale
    437         // applied to the pixels in the bitmap shader. This means we need
    438         // CTM * shaderTransform to have identity scale. Since we
    439         // can't modify CTM (or the rectangle will be drawn in the wrong
    440         // place), we must set shaderTransform's scale to the inverse of
    441         // CTM scale.
    442         shaderTransform.setScale(ctmScaleX ? 1 / ctmScaleX : 1, ctmScaleY ? 1 / ctmScaleY : 1);
    443     } else {
    444         // No need to resample before drawing.
    445         SkBitmap srcSubset;
    446         bitmap().extractSubset(&srcSubset, enclosingIntRect(normSrcRect));
    447         shader = adoptRef(SkShader::CreateBitmapShader(srcSubset, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode));
    448         // Because no resizing occurred, the shader transform should be
    449         // set to the pattern's transform, which just includes scale.
    450         shaderTransform.setScale(scale.width(), scale.height());
    451     }
    452 
    453     // We also need to translate it such that the origin of the pattern is the
    454     // origin of the destination rect, which is what WebKit expects. Skia uses
    455     // the coordinate system origin as the base for the pattern. If WebKit wants
    456     // a shifted image, it will shift it from there using the shaderTransform.
    457     float adjustedX = phase.x() + normSrcRect.x() * scale.width();
    458     float adjustedY = phase.y() + normSrcRect.y() * scale.height();
    459     shaderTransform.postTranslate(SkFloatToScalar(adjustedX), SkFloatToScalar(adjustedY));
    460     shader->setLocalMatrix(shaderTransform);
    461 
    462     SkPaint paint;
    463     paint.setShader(shader.get());
    464     paint.setXfermodeMode(WebCoreCompositeToSkiaComposite(compositeOp, blendMode));
    465 
    466     paint.setFilterBitmap(resampling == LinearResampling);
    467     if (useBicubicFilter)
    468         paint.setFilterLevel(SkPaint::kHigh_FilterLevel);
    469 
    470     context->drawRect(destRect, paint);
    471 }
    472 
    473 bool NativeImageSkia::shouldCacheResampling(const SkISize& scaledImageSize, const SkIRect& scaledImageSubset) const
    474 {
    475     // Check whether the requested dimensions match previous request.
    476     bool matchesPreviousRequest = m_cachedImageInfo.isEqual(scaledImageSize, scaledImageSubset);
    477     if (matchesPreviousRequest)
    478         ++m_resizeRequests;
    479     else {
    480         m_cachedImageInfo.set(scaledImageSize, scaledImageSubset);
    481         m_resizeRequests = 0;
    482         // Reset m_resizedImage now, because we don't distinguish
    483         // between the last requested resize info and m_resizedImage's
    484         // resize info.
    485         m_resizedImage.reset();
    486     }
    487 
    488     // We can not cache incomplete frames. This might be a good optimization in
    489     // the future, were we know how much of the frame has been decoded, so when
    490     // we incrementally draw more of the image, we only have to resample the
    491     // parts that are changed.
    492     if (!isDataComplete())
    493         return false;
    494 
    495     // If the destination bitmap is excessively large, we'll never allow caching.
    496     static const unsigned long long kLargeBitmapSize = 4096ULL * 4096ULL;
    497     unsigned long long fullSize = static_cast<unsigned long long>(scaledImageSize.width()) * static_cast<unsigned long long>(scaledImageSize.height());
    498     unsigned long long fragmentSize = static_cast<unsigned long long>(scaledImageSubset.width()) * static_cast<unsigned long long>(scaledImageSubset.height());
    499 
    500     if (fragmentSize > kLargeBitmapSize)
    501         return false;
    502 
    503     // If the destination bitmap is small, we'll always allow caching, since
    504     // there is not very much penalty for computing it and it may come in handy.
    505     static const unsigned kSmallBitmapSize = 4096;
    506     if (fragmentSize <= kSmallBitmapSize)
    507         return true;
    508 
    509     // If "too many" requests have been made for this bitmap, we assume that
    510     // many more will be made as well, and we'll go ahead and cache it.
    511     static const int kManyRequestThreshold = 4;
    512     if (m_resizeRequests >= kManyRequestThreshold)
    513         return true;
    514 
    515     // If more than 1/4 of the resized image is requested, it's worth caching.
    516     return fragmentSize > fullSize / 4;
    517 }
    518 
    519 NativeImageSkia::ImageResourceInfo::ImageResourceInfo()
    520 {
    521     scaledImageSize.setEmpty();
    522     scaledImageSubset.setEmpty();
    523 }
    524 
    525 bool NativeImageSkia::ImageResourceInfo::isEqual(const SkISize& otherScaledImageSize, const SkIRect& otherScaledImageSubset) const
    526 {
    527     return scaledImageSize == otherScaledImageSize && scaledImageSubset == otherScaledImageSubset;
    528 }
    529 
    530 void NativeImageSkia::ImageResourceInfo::set(const SkISize& otherScaledImageSize, const SkIRect& otherScaledImageSubset)
    531 {
    532     scaledImageSize = otherScaledImageSize;
    533     scaledImageSubset = otherScaledImageSubset;
    534 }
    535 
    536 SkIRect NativeImageSkia::ImageResourceInfo::rectInSubset(const SkIRect& otherScaledImageSubset)
    537 {
    538     if (!scaledImageSubset.contains(otherScaledImageSubset))
    539         return SkIRect::MakeEmpty();
    540     SkIRect subsetRect = otherScaledImageSubset;
    541     subsetRect.offset(-scaledImageSubset.x(), -scaledImageSubset.y());
    542     return subsetRect;
    543 }
    544 
    545 } // namespace WebCore
    546