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)
computeResamplingMode(PlatformContextSkia * platformContext,const NativeImageSkia & bitmap,int srcWidth,int srcHeight,float destWidth,float destHeight)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.
drawResampledBitmap(SkCanvas & canvas,SkPaint & paint,const NativeImageSkia & bitmap,const SkIRect & srcIRect,const SkRect & destRect)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
paintSkBitmap(PlatformContextSkia * platformContext,const NativeImageSkia & bitmap,const SkIRect & srcRect,const SkRect & destRect,const SkXfermode::Mode & compOp)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.
TransformDimensions(const SkMatrix & matrix,float srcWidth,float srcHeight,float * destWidth,float * destHeight)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.
normalizeRect(const FloatRect & rect)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
clear(bool clearMetadata)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
drawPattern(GraphicsContext * context,const FloatRect & floatSrcRect,const AffineTransform & patternTransform,const FloatPoint & phase,ColorSpace styleColorSpace,CompositeOperator compositeOp,const FloatRect & destRect)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
drawBitmapGLES2(GraphicsContext * ctxt,NativeImageSkia * bitmap,const FloatRect & srcRect,const FloatRect & dstRect,ColorSpace styleColorSpace,CompositeOperator compositeOp)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
initPlatformData()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
invalidatePlatformData()464 void BitmapImage::invalidatePlatformData()
465 {
466 // See initPlatformData above.
467 }
468
checkForSolidColor()469 void BitmapImage::checkForSolidColor()
470 {
471 m_checkedForSolidColor = true;
472 }
473
draw(GraphicsContext * ctxt,const FloatRect & dstRect,const FloatRect & srcRect,ColorSpace colorSpace,CompositeOperator compositeOp)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
draw(GraphicsContext * ctxt,const FloatRect & dstRect,const FloatRect & srcRect,ColorSpace styleColorSpace,CompositeOperator compositeOp)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
BitmapImageSingleFrameSkia(const SkBitmap & bitmap)537 BitmapImageSingleFrameSkia::BitmapImageSingleFrameSkia(const SkBitmap& bitmap)
538 : m_nativeImage(bitmap)
539 {
540 }
541
create(const SkBitmap & bitmap,bool copyPixels)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