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 
     33 #include "AffineTransform.h"
     34 #include "BitmapImage.h"
     35 #include "BitmapImageSingleFrameSkia.h"
     36 #include "FloatConversion.h"
     37 #include "FloatRect.h"
     38 #include "GLES2Canvas.h"
     39 #include "GraphicsContext.h"
     40 #include "Logging.h"
     41 #include "NativeImageSkia.h"
     42 #include "PlatformContextSkia.h"
     43 #include "PlatformString.h"
     44 #include "SkPixelRef.h"
     45 #include "SkRect.h"
     46 #include "SkShader.h"
     47 #include "SkiaUtils.h"
     48 #include "Texture.h"
     49 
     50 #include "skia/ext/image_operations.h"
     51 #include "skia/ext/platform_canvas.h"
     52 
     53 namespace WebCore {
     54 
     55 // Used by computeResamplingMode to tell how bitmaps should be resampled.
     56 enum ResamplingMode {
     57     // Nearest neighbor resampling. Used when we detect that the page is
     58     // trying to make a pattern by stretching a small bitmap very large.
     59     RESAMPLE_NONE,
     60 
     61     // Default skia resampling. Used for large growing of images where high
     62     // quality resampling doesn't get us very much except a slowdown.
     63     RESAMPLE_LINEAR,
     64 
     65     // High quality resampling.
     66     RESAMPLE_AWESOME,
     67 };
     68 
     69 #if !ENABLE(SKIA_GPU)
     70 static ResamplingMode computeResamplingMode(PlatformContextSkia* platformContext, const NativeImageSkia& bitmap, int srcWidth, int srcHeight, float destWidth, float destHeight)
     71 {
     72     if (platformContext->hasImageResamplingHint()) {
     73         IntSize srcSize;
     74         FloatSize dstSize;
     75         platformContext->getImageResamplingHint(&srcSize, &dstSize);
     76         srcWidth = srcSize.width();
     77         srcHeight = srcSize.height();
     78         destWidth = dstSize.width();
     79         destHeight = dstSize.height();
     80     }
     81 
     82     int destIWidth = static_cast<int>(destWidth);
     83     int destIHeight = static_cast<int>(destHeight);
     84 
     85     // The percent change below which we will not resample. This usually means
     86     // an off-by-one error on the web page, and just doing nearest neighbor
     87     // sampling is usually good enough.
     88     const float kFractionalChangeThreshold = 0.025f;
     89 
     90     // Images smaller than this in either direction are considered "small" and
     91     // are not resampled ever (see below).
     92     const int kSmallImageSizeThreshold = 8;
     93 
     94     // The amount an image can be stretched in a single direction before we
     95     // say that it is being stretched so much that it must be a line or
     96     // background that doesn't need resampling.
     97     const float kLargeStretch = 3.0f;
     98 
     99     // Figure out if we should resample this image. We try to prune out some
    100     // common cases where resampling won't give us anything, since it is much
    101     // slower than drawing stretched.
    102     if (srcWidth == destIWidth && srcHeight == destIHeight) {
    103         // We don't need to resample if the source and destination are the same.
    104         return RESAMPLE_NONE;
    105     }
    106 
    107     if (srcWidth <= kSmallImageSizeThreshold
    108         || srcHeight <= kSmallImageSizeThreshold
    109         || destWidth <= kSmallImageSizeThreshold
    110         || destHeight <= kSmallImageSizeThreshold) {
    111         // Never resample small images. These are often used for borders and
    112         // rules (think 1x1 images used to make lines).
    113         return RESAMPLE_NONE;
    114     }
    115 
    116     if (srcHeight * kLargeStretch <= destHeight || srcWidth * kLargeStretch <= destWidth) {
    117         // Large image detected.
    118 
    119         // Don't resample if it is being stretched a lot in only one direction.
    120         // This is trying to catch cases where somebody has created a border
    121         // (which might be large) and then is stretching it to fill some part
    122         // of the page.
    123         if (srcWidth == destWidth || srcHeight == destHeight)
    124             return RESAMPLE_NONE;
    125 
    126         // The image is growing a lot and in more than one direction. Resampling
    127         // is slow and doesn't give us very much when growing a lot.
    128         return RESAMPLE_LINEAR;
    129     }
    130 
    131     if ((fabs(destWidth - srcWidth) / srcWidth < kFractionalChangeThreshold)
    132         && (fabs(destHeight - srcHeight) / srcHeight < kFractionalChangeThreshold)) {
    133         // It is disappointingly common on the web for image sizes to be off by
    134         // one or two pixels. We don't bother resampling if the size difference
    135         // is a small fraction of the original size.
    136         return RESAMPLE_NONE;
    137     }
    138 
    139     // When the image is not yet done loading, use linear. We don't cache the
    140     // partially resampled images, and as they come in incrementally, it causes
    141     // us to have to resample the whole thing every time.
    142     if (!bitmap.isDataComplete())
    143         return RESAMPLE_LINEAR;
    144 
    145     // Everything else gets resampled.
    146     // If the platform context permits high quality interpolation, use it.
    147     // High quality interpolation only enabled for scaling and translation.
    148     if (platformContext->interpolationQuality() == InterpolationHigh
    149         && !(platformContext->canvas()->getTotalMatrix().getType() & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)))
    150         return RESAMPLE_AWESOME;
    151 
    152     return RESAMPLE_LINEAR;
    153 }
    154 #endif
    155 
    156 // Draws the given bitmap to the given canvas. The subset of the source bitmap
    157 // identified by src_rect is drawn to the given destination rect. The bitmap
    158 // will be resampled to resample_width * resample_height (this is the size of
    159 // the whole image, not the subset). See shouldResampleBitmap for more.
    160 //
    161 // This does a lot of computation to resample only the portion of the bitmap
    162 // that will only be drawn. This is critical for performance since when we are
    163 // scrolling, for example, we are only drawing a small strip of the image.
    164 // Resampling the whole image every time is very slow, so this speeds up things
    165 // dramatically.
    166 static void drawResampledBitmap(SkCanvas& canvas, SkPaint& paint, const NativeImageSkia& bitmap, const SkIRect& srcIRect, const SkRect& destRect)
    167 {
    168     // First get the subset we need. This is efficient and does not copy pixels.
    169     SkBitmap subset;
    170     bitmap.extractSubset(&subset, srcIRect);
    171     SkRect srcRect;
    172     srcRect.set(srcIRect);
    173 
    174     // Whether we're doing a subset or using the full source image.
    175     bool srcIsFull = srcIRect.fLeft == 0 && srcIRect.fTop == 0
    176         && srcIRect.width() == bitmap.width()
    177         && srcIRect.height() == bitmap.height();
    178 
    179     // We will always draw in integer sizes, so round the destination rect.
    180     SkIRect destRectRounded;
    181     destRect.round(&destRectRounded);
    182     SkIRect resizedImageRect =  // Represents the size of the resized image.
    183         { 0, 0, destRectRounded.width(), destRectRounded.height() };
    184 
    185     // Apply forward transform to destRect to estimate required size of
    186     // re-sampled bitmap, and use only in calls required to resize, or that
    187     // check for the required size.
    188     SkRect destRectTransformed;
    189     canvas.getTotalMatrix().mapRect(&destRectTransformed, destRect);
    190     SkIRect destRectTransformedRounded;
    191     destRectTransformed.round(&destRectTransformedRounded);
    192 
    193     if (srcIsFull && bitmap.hasResizedBitmap(destRectTransformedRounded.width(), destRectTransformedRounded.height())) {
    194         // Yay, this bitmap frame already has a resized version.
    195         SkBitmap resampled = bitmap.resizedBitmap(destRectTransformedRounded.width(), destRectTransformedRounded.height());
    196         canvas.drawBitmapRect(resampled, 0, destRect, &paint);
    197         return;
    198     }
    199 
    200     // Compute the visible portion of our rect.
    201     // We also need to compute the transformed portion of the
    202     // visible portion for use below.
    203     SkRect destBitmapSubsetSk;
    204     ClipRectToCanvas(canvas, destRect, &destBitmapSubsetSk);
    205     SkRect destBitmapSubsetTransformed;
    206     canvas.getTotalMatrix().mapRect(&destBitmapSubsetTransformed, destBitmapSubsetSk);
    207     destBitmapSubsetSk.offset(-destRect.fLeft, -destRect.fTop);
    208     SkIRect destBitmapSubsetTransformedRounded;
    209     destBitmapSubsetTransformed.round(&destBitmapSubsetTransformedRounded);
    210     destBitmapSubsetTransformedRounded.offset(-destRectTransformedRounded.fLeft, -destRectTransformedRounded.fTop);
    211 
    212     // The matrix inverting, etc. could have introduced rounding error which
    213     // causes the bounds to be outside of the resized bitmap. We round outward
    214     // so we always lean toward it being larger rather than smaller than we
    215     // need, and then clamp to the bitmap bounds so we don't get any invalid
    216     // data.
    217     SkIRect destBitmapSubsetSkI;
    218     destBitmapSubsetSk.roundOut(&destBitmapSubsetSkI);
    219     if (!destBitmapSubsetSkI.intersect(resizedImageRect))
    220         return;  // Resized image does not intersect.
    221 
    222     if (srcIsFull && bitmap.shouldCacheResampling(
    223             resizedImageRect.width(),
    224             resizedImageRect.height(),
    225             destBitmapSubsetSkI.width(),
    226             destBitmapSubsetSkI.height())) {
    227         // We're supposed to resize the entire image and cache it, even though
    228         // we don't need all of it.
    229         SkBitmap resampled = bitmap.resizedBitmap(destRectTransformedRounded.width(),
    230                                                   destRectTransformedRounded.height());
    231         canvas.drawBitmapRect(resampled, 0, destRect, &paint);
    232     } else {
    233         // We should only resize the exposed part of the bitmap to do the
    234         // minimal possible work.
    235 
    236         // Resample the needed part of the image.
    237         // Transforms above plus rounding may cause destBitmapSubsetTransformedRounded
    238         // to go outside the image, so need to clip to avoid problems.
    239         if (destBitmapSubsetTransformedRounded.intersect(0, 0,
    240                 destRectTransformedRounded.width(), destRectTransformedRounded.height())) {
    241 
    242             SkBitmap resampled = skia::ImageOperations::Resize(subset,
    243                 skia::ImageOperations::RESIZE_LANCZOS3,
    244                 destRectTransformedRounded.width(), destRectTransformedRounded.height(),
    245                 destBitmapSubsetTransformedRounded);
    246 
    247             // Compute where the new bitmap should be drawn. Since our new bitmap
    248             // may be smaller than the original, we have to shift it over by the
    249             // same amount that we cut off the top and left.
    250             destBitmapSubsetSkI.offset(destRect.fLeft, destRect.fTop);
    251             SkRect offsetDestRect;
    252             offsetDestRect.set(destBitmapSubsetSkI);
    253 
    254             canvas.drawBitmapRect(resampled, 0, offsetDestRect, &paint);
    255         }
    256     }
    257 }
    258 
    259 static void paintSkBitmap(PlatformContextSkia* platformContext, const NativeImageSkia& bitmap, const SkIRect& srcRect, const SkRect& destRect, const SkXfermode::Mode& compOp)
    260 {
    261     SkPaint paint;
    262     paint.setXfermodeMode(compOp);
    263     paint.setFilterBitmap(true);
    264     paint.setAlpha(platformContext->getNormalizedAlpha());
    265     paint.setLooper(platformContext->getDrawLooper());
    266 
    267     SkCanvas* canvas = platformContext->canvas();
    268 
    269     ResamplingMode resampling;
    270 #if ENABLE(SKIA_GPU)
    271     resampling = RESAMPLE_LINEAR;
    272 #else
    273     resampling = platformContext->printing() ? RESAMPLE_NONE :
    274         computeResamplingMode(platformContext, bitmap, srcRect.width(), srcRect.height(),
    275                               SkScalarToFloat(destRect.width()),
    276                               SkScalarToFloat(destRect.height()));
    277 #endif
    278     if (resampling == RESAMPLE_AWESOME) {
    279         drawResampledBitmap(*canvas, paint, bitmap, srcRect, destRect);
    280     } else {
    281         // No resampling necessary, we can just draw the bitmap. We want to
    282         // filter it if we decided to do linear interpolation above, or if there
    283         // is something interesting going on with the matrix (like a rotation).
    284         // Note: for serialization, we will want to subset the bitmap first so
    285         // we don't send extra pixels.
    286         canvas->drawBitmapRect(bitmap, &srcRect, destRect, &paint);
    287     }
    288 }
    289 
    290 // Transforms the given dimensions with the given matrix. Used to see how big
    291 // images will be once transformed.
    292 static void TransformDimensions(const SkMatrix& matrix, float srcWidth, float srcHeight, float* destWidth, float* destHeight) {
    293     // Transform 3 points to see how long each side of the bitmap will be.
    294     SkPoint src_points[3];  // (0, 0), (width, 0), (0, height).
    295     src_points[0].set(0, 0);
    296     src_points[1].set(SkFloatToScalar(srcWidth), 0);
    297     src_points[2].set(0, SkFloatToScalar(srcHeight));
    298 
    299     // Now measure the length of the two transformed vectors relative to the
    300     // transformed origin to see how big the bitmap will be. Note: for skews,
    301     // this isn't the best thing, but we don't have skews.
    302     SkPoint dest_points[3];
    303     matrix.mapPoints(dest_points, src_points, 3);
    304     *destWidth = SkScalarToFloat((dest_points[1] - dest_points[0]).length());
    305     *destHeight = SkScalarToFloat((dest_points[2] - dest_points[0]).length());
    306 }
    307 
    308 // A helper method for translating negative width and height values.
    309 FloatRect normalizeRect(const FloatRect& rect)
    310 {
    311     FloatRect norm = rect;
    312     if (norm.width() < 0) {
    313         norm.setX(norm.x() + norm.width());
    314         norm.setWidth(-norm.width());
    315     }
    316     if (norm.height() < 0) {
    317         norm.setY(norm.y() + norm.height());
    318         norm.setHeight(-norm.height());
    319     }
    320     return norm;
    321 }
    322 
    323 bool FrameData::clear(bool clearMetadata)
    324 {
    325     if (clearMetadata)
    326         m_haveMetadata = false;
    327 
    328     if (m_frame) {
    329         // ImageSource::createFrameAtIndex() allocated |m_frame| and passed
    330         // ownership to BitmapImage; we must delete it here.
    331         delete m_frame;
    332         m_frame = 0;
    333         return true;
    334     }
    335     return false;
    336 }
    337 
    338 void Image::drawPattern(GraphicsContext* context,
    339                         const FloatRect& floatSrcRect,
    340                         const AffineTransform& patternTransform,
    341                         const FloatPoint& phase,
    342                         ColorSpace styleColorSpace,
    343                         CompositeOperator compositeOp,
    344                         const FloatRect& destRect)
    345 {
    346     FloatRect normSrcRect = normalizeRect(floatSrcRect);
    347     if (destRect.isEmpty() || normSrcRect.isEmpty())
    348         return;  // nothing to draw
    349 
    350     NativeImageSkia* bitmap = nativeImageForCurrentFrame();
    351     if (!bitmap)
    352         return;
    353 
    354     // This is a very inexpensive operation. It will generate a new bitmap but
    355     // it will internally reference the old bitmap's pixels, adjusting the row
    356     // stride so the extra pixels appear as padding to the subsetted bitmap.
    357     SkBitmap srcSubset;
    358     SkIRect srcRect = enclosingIntRect(normSrcRect);
    359     bitmap->extractSubset(&srcSubset, srcRect);
    360 
    361     SkBitmap resampled;
    362     SkShader* shader;
    363 
    364     // Figure out what size the bitmap will be in the destination. The
    365     // destination rect is the bounds of the pattern, we need to use the
    366     // matrix to see how bit it will be.
    367     float destBitmapWidth, destBitmapHeight;
    368     TransformDimensions(patternTransform, srcRect.width(), srcRect.height(),
    369                         &destBitmapWidth, &destBitmapHeight);
    370 
    371     // Compute the resampling mode.
    372     ResamplingMode resampling;
    373 #if ENABLE(SKIA_GPU)
    374     resampling = RESAMPLE_LINEAR;
    375 #else
    376     if (context->platformContext()->printing())
    377       resampling = RESAMPLE_LINEAR;
    378     else {
    379       resampling = computeResamplingMode(context->platformContext(), *bitmap,
    380                                          srcRect.width(), srcRect.height(),
    381                                          destBitmapWidth, destBitmapHeight);
    382     }
    383 #endif
    384 
    385     // Load the transform WebKit requested.
    386     SkMatrix matrix(patternTransform);
    387 
    388     if (resampling == RESAMPLE_AWESOME) {
    389         // Do nice resampling.
    390         SkBitmap resampled;
    391         int width = static_cast<int>(destBitmapWidth);
    392         int height = static_cast<int>(destBitmapHeight);
    393         if (!srcRect.fLeft && !srcRect.fTop
    394             && srcRect.fRight == bitmap->width() && srcRect.fBottom == bitmap->height()
    395             && (bitmap->hasResizedBitmap(width, height)
    396                 || bitmap->shouldCacheResampling(width, height, width, height))) {
    397             // resizedBitmap() caches resized image.
    398             resampled = bitmap->resizedBitmap(width, height);
    399         } else {
    400             resampled = skia::ImageOperations::Resize(srcSubset,
    401                 skia::ImageOperations::RESIZE_LANCZOS3, width, height);
    402         }
    403         shader = SkShader::CreateBitmapShader(resampled, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode);
    404 
    405         // Since we just resized the bitmap, we need to undo the scale set in
    406         // the image transform.
    407         matrix.setScaleX(SkIntToScalar(1));
    408         matrix.setScaleY(SkIntToScalar(1));
    409     } else {
    410         // No need to do nice resampling.
    411         shader = SkShader::CreateBitmapShader(srcSubset, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode);
    412     }
    413 
    414     // We also need to translate it such that the origin of the pattern is the
    415     // origin of the destination rect, which is what WebKit expects. Skia uses
    416     // the coordinate system origin as the base for the patter. If WebKit wants
    417     // a shifted image, it will shift it from there using the patternTransform.
    418     float adjustedX = phase.x() + normSrcRect.x() *
    419                       narrowPrecisionToFloat(patternTransform.a());
    420     float adjustedY = phase.y() + normSrcRect.y() *
    421                       narrowPrecisionToFloat(patternTransform.d());
    422     matrix.postTranslate(SkFloatToScalar(adjustedX),
    423                          SkFloatToScalar(adjustedY));
    424     shader->setLocalMatrix(matrix);
    425 
    426     SkPaint paint;
    427     paint.setShader(shader)->unref();
    428     paint.setXfermodeMode(WebCoreCompositeToSkiaComposite(compositeOp));
    429     paint.setFilterBitmap(resampling == RESAMPLE_LINEAR);
    430 
    431     context->platformContext()->paintSkPaint(destRect, paint);
    432 }
    433 
    434 static void drawBitmapGLES2(GraphicsContext* ctxt, NativeImageSkia* bitmap, const FloatRect& srcRect, const FloatRect& dstRect, ColorSpace styleColorSpace, CompositeOperator compositeOp)
    435 {
    436     ctxt->platformContext()->prepareForHardwareDraw();
    437     GLES2Canvas* gpuCanvas = ctxt->platformContext()->gpuCanvas();
    438     Texture* texture = gpuCanvas->getTexture(bitmap);
    439     if (!texture) {
    440         ASSERT(bitmap->config() == SkBitmap::kARGB_8888_Config);
    441         ASSERT(bitmap->rowBytes() == bitmap->width() * 4);
    442         texture = gpuCanvas->createTexture(bitmap, Texture::BGRA8, bitmap->width(), bitmap->height());
    443         SkAutoLockPixels lock(*bitmap);
    444         ASSERT(bitmap->getPixels());
    445         texture->load(bitmap->getPixels());
    446     }
    447     gpuCanvas->drawTexturedRect(texture, srcRect, dstRect, styleColorSpace, compositeOp);
    448 }
    449 
    450 // ================================================
    451 // BitmapImage Class
    452 // ================================================
    453 
    454 // FIXME: These should go to BitmapImageSkia.cpp
    455 
    456 void BitmapImage::initPlatformData()
    457 {
    458     // This is not used. On Mac, the "platform" data is a cache of some OS
    459     // specific versions of the image that are created is some cases. These
    460     // aren't normally used, it is equivalent to getHBITMAP on Windows, and
    461     // the platform data is the cache.
    462 }
    463 
    464 void BitmapImage::invalidatePlatformData()
    465 {
    466     // See initPlatformData above.
    467 }
    468 
    469 void BitmapImage::checkForSolidColor()
    470 {
    471     m_checkedForSolidColor = true;
    472 }
    473 
    474 void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect,
    475                        const FloatRect& srcRect, ColorSpace colorSpace, CompositeOperator compositeOp)
    476 {
    477     if (!m_source.initialized())
    478         return;
    479 
    480     // Spin the animation to the correct frame before we try to draw it, so we
    481     // don't draw an old frame and then immediately need to draw a newer one,
    482     // causing flicker and wasting CPU.
    483     startAnimation();
    484 
    485     NativeImageSkia* bm = nativeImageForCurrentFrame();
    486     if (!bm)
    487         return;  // It's too early and we don't have an image yet.
    488 
    489     FloatRect normDstRect = normalizeRect(dstRect);
    490     FloatRect normSrcRect = normalizeRect(srcRect);
    491 
    492     if (normSrcRect.isEmpty() || normDstRect.isEmpty())
    493         return;  // Nothing to draw.
    494 
    495     if (ctxt->platformContext()->useGPU() && ctxt->platformContext()->canAccelerate()) {
    496         drawBitmapGLES2(ctxt, bm, normSrcRect, normDstRect, colorSpace, compositeOp);
    497         return;
    498     }
    499 
    500     ctxt->platformContext()->prepareForSoftwareDraw();
    501 
    502     paintSkBitmap(ctxt->platformContext(),
    503                   *bm,
    504                   enclosingIntRect(normSrcRect),
    505                   normDstRect,
    506                   WebCoreCompositeToSkiaComposite(compositeOp));
    507 }
    508 
    509 // FIXME: These should go into BitmapImageSingleFrameSkia.cpp
    510 
    511 void BitmapImageSingleFrameSkia::draw(GraphicsContext* ctxt,
    512                                       const FloatRect& dstRect,
    513                                       const FloatRect& srcRect,
    514                                       ColorSpace styleColorSpace,
    515                                       CompositeOperator compositeOp)
    516 {
    517     FloatRect normDstRect = normalizeRect(dstRect);
    518     FloatRect normSrcRect = normalizeRect(srcRect);
    519 
    520     if (normSrcRect.isEmpty() || normDstRect.isEmpty())
    521         return;  // Nothing to draw.
    522 
    523     if (ctxt->platformContext()->useGPU() && ctxt->platformContext()->canAccelerate()) {
    524         drawBitmapGLES2(ctxt, &m_nativeImage, srcRect, dstRect, styleColorSpace, compositeOp);
    525         return;
    526     }
    527 
    528     ctxt->platformContext()->prepareForSoftwareDraw();
    529 
    530     paintSkBitmap(ctxt->platformContext(),
    531                   m_nativeImage,
    532                   enclosingIntRect(normSrcRect),
    533                   normDstRect,
    534                   WebCoreCompositeToSkiaComposite(compositeOp));
    535 }
    536 
    537 BitmapImageSingleFrameSkia::BitmapImageSingleFrameSkia(const SkBitmap& bitmap)
    538     : m_nativeImage(bitmap)
    539 {
    540 }
    541 
    542 PassRefPtr<BitmapImageSingleFrameSkia> BitmapImageSingleFrameSkia::create(const SkBitmap& bitmap, bool copyPixels)
    543 {
    544     if (copyPixels) {
    545         SkBitmap temp;
    546         bitmap.copyTo(&temp, bitmap.config());
    547         return adoptRef(new BitmapImageSingleFrameSkia(temp));
    548     }
    549     return adoptRef(new BitmapImageSingleFrameSkia(bitmap));
    550 }
    551 
    552 }  // namespace WebCore
    553