2 * Copyright (c) 2008, Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
33 #include "BitmapImage.h"
34 #include "BitmapImageSingleFrameSkia.h"
35 #include "ChromiumBridge.h"
36 #include "FloatConversion.h"
37 #include "FloatRect.h"
38 #include "GraphicsContext.h"
40 #include "NativeImageSkia.h"
41 #include "NotImplemented.h"
42 #include "PlatformContextSkia.h"
43 #include "PlatformString.h"
44 #include "SkiaUtils.h"
46 #include "TransformationMatrix.h"
48 #include "skia/ext/image_operations.h"
49 #include "skia/ext/platform_canvas.h"
53 // Used by computeResamplingMode to tell how bitmaps should be resampled.
55 // Nearest neighbor resampling. Used when we detect that the page is
56 // trying to make a pattern by stretching a small bitmap very large.
59 // Default skia resampling. Used for large growing of images where high
60 // quality resampling doesn't get us very much except a slowdown.
63 // High quality resampling.
67 static ResamplingMode computeResamplingMode(const NativeImageSkia& bitmap, int srcWidth, int srcHeight, float destWidth, float destHeight)
69 int destIWidth = static_cast<int>(destWidth);
70 int destIHeight = static_cast<int>(destHeight);
72 // The percent change below which we will not resample. This usually means
73 // an off-by-one error on the web page, and just doing nearest neighbor
74 // sampling is usually good enough.
75 const float kFractionalChangeThreshold = 0.025f;
77 // Images smaller than this in either direction are considered "small" and
78 // are not resampled ever (see below).
79 const int kSmallImageSizeThreshold = 8;
81 // The amount an image can be stretched in a single direction before we
82 // say that it is being stretched so much that it must be a line or
83 // background that doesn't need resampling.
84 const float kLargeStretch = 3.0f;
86 // Figure out if we should resample this image. We try to prune out some
87 // common cases where resampling won't give us anything, since it is much
88 // slower than drawing stretched.
89 if (srcWidth == destIWidth && srcHeight == destIHeight) {
90 // We don't need to resample if the source and destination are the same.
94 if (srcWidth <= kSmallImageSizeThreshold
95 || srcHeight <= kSmallImageSizeThreshold
96 || destWidth <= kSmallImageSizeThreshold
97 || destHeight <= kSmallImageSizeThreshold) {
98 // Never resample small images. These are often used for borders and
99 // rules (think 1x1 images used to make lines).
100 return RESAMPLE_NONE;
103 if (srcHeight * kLargeStretch <= destHeight || srcWidth * kLargeStretch <= destWidth) {
104 // Large image detected.
106 // Don't resample if it is being stretched a lot in only one direction.
107 // This is trying to catch cases where somebody has created a border
108 // (which might be large) and then is stretching it to fill some part
110 if (srcWidth == destWidth || srcHeight == destHeight)
111 return RESAMPLE_NONE;
113 // The image is growing a lot and in more than one direction. Resampling
114 // is slow and doesn't give us very much when growing a lot.
115 return RESAMPLE_LINEAR;
118 if ((fabs(destWidth - srcWidth) / srcWidth < kFractionalChangeThreshold)
119 && (fabs(destHeight - srcHeight) / srcHeight < kFractionalChangeThreshold)) {
120 // It is disappointingly common on the web for image sizes to be off by
121 // one or two pixels. We don't bother resampling if the size difference
122 // is a small fraction of the original size.
123 return RESAMPLE_NONE;
126 // When the image is not yet done loading, use linear. We don't cache the
127 // partially resampled images, and as they come in incrementally, it causes
128 // us to have to resample the whole thing every time.
129 if (!bitmap.isDataComplete())
130 return RESAMPLE_LINEAR;
132 // Everything else gets resampled.
133 return RESAMPLE_AWESOME;
136 // Draws the given bitmap to the given canvas. The subset of the source bitmap
137 // identified by src_rect is drawn to the given destination rect. The bitmap
138 // will be resampled to resample_width * resample_height (this is the size of
139 // the whole image, not the subset). See shouldResampleBitmap for more.
141 // This does a lot of computation to resample only the portion of the bitmap
142 // that will only be drawn. This is critical for performance since when we are
143 // scrolling, for example, we are only drawing a small strip of the image.
144 // Resampling the whole image every time is very slow, so this speeds up things
146 static void drawResampledBitmap(SkCanvas& canvas, SkPaint& paint, const NativeImageSkia& bitmap, const SkIRect& srcIRect, const SkRect& destRect)
148 // First get the subset we need. This is efficient and does not copy pixels.
150 bitmap.extractSubset(&subset, srcIRect);
152 srcRect.set(srcIRect);
154 // Whether we're doing a subset or using the full source image.
155 bool srcIsFull = srcIRect.fLeft == 0 && srcIRect.fTop == 0
156 && srcIRect.width() == bitmap.width()
157 && srcIRect.height() == bitmap.height();
159 // We will always draw in integer sizes, so round the destination rect.
160 SkIRect destRectRounded;
161 destRect.round(&destRectRounded);
162 SkIRect resizedImageRect; // Represents the size of the resized image.
163 resizedImageRect.set(0, 0, destRectRounded.width(), destRectRounded.height());
165 if (srcIsFull && bitmap.hasResizedBitmap(destRectRounded.width(), destRectRounded.height())) {
166 // Yay, this bitmap frame already has a resized version.
167 SkBitmap resampled = bitmap.resizedBitmap(destRectRounded.width(), destRectRounded.height());
168 canvas.drawBitmapRect(resampled, 0, destRect, &paint);
172 // Compute the visible portion of our rect.
173 SkRect destBitmapSubsetSk;
174 ClipRectToCanvas(canvas, destRect, &destBitmapSubsetSk);
175 destBitmapSubsetSk.offset(-destRect.fLeft, -destRect.fTop);
177 // The matrix inverting, etc. could have introduced rounding error which
178 // causes the bounds to be outside of the resized bitmap. We round outward
179 // so we always lean toward it being larger rather than smaller than we
180 // need, and then clamp to the bitmap bounds so we don't get any invalid
182 SkIRect destBitmapSubsetSkI;
183 destBitmapSubsetSk.roundOut(&destBitmapSubsetSkI);
184 if (!destBitmapSubsetSkI.intersect(resizedImageRect))
185 return; // Resized image does not intersect.
187 if (srcIsFull && bitmap.shouldCacheResampling(
188 resizedImageRect.width(),
189 resizedImageRect.height(),
190 destBitmapSubsetSkI.width(),
191 destBitmapSubsetSkI.height())) {
192 // We're supposed to resize the entire image and cache it, even though
193 // we don't need all of it.
194 SkBitmap resampled = bitmap.resizedBitmap(destRectRounded.width(),
195 destRectRounded.height());
196 canvas.drawBitmapRect(resampled, 0, destRect, &paint);
198 // We should only resize the exposed part of the bitmap to do the
199 // minimal possible work.
200 gfx::Rect destBitmapSubset(destBitmapSubsetSkI.fLeft,
201 destBitmapSubsetSkI.fTop,
202 destBitmapSubsetSkI.width(),
203 destBitmapSubsetSkI.height());
205 // Resample the needed part of the image.
206 SkBitmap resampled = skia::ImageOperations::Resize(subset,
207 skia::ImageOperations::RESIZE_LANCZOS3,
208 destRectRounded.width(), destRectRounded.height(),
211 // Compute where the new bitmap should be drawn. Since our new bitmap
212 // may be smaller than the original, we have to shift it over by the
213 // same amount that we cut off the top and left.
214 SkRect offsetDestRect = {
215 destBitmapSubset.x() + destRect.fLeft,
216 destBitmapSubset.y() + destRect.fTop,
217 destBitmapSubset.right() + destRect.fLeft,
218 destBitmapSubset.bottom() + destRect.fTop };
220 canvas.drawBitmapRect(resampled, 0, offsetDestRect, &paint);
224 static void paintSkBitmap(PlatformContextSkia* platformContext, const NativeImageSkia& bitmap, const SkIRect& srcRect, const SkRect& destRect, const SkPorterDuff::Mode& compOp)
227 paint.setPorterDuffXfermode(compOp);
229 skia::PlatformCanvas* canvas = platformContext->canvas();
231 ResamplingMode resampling = platformContext->isPrinting() ? RESAMPLE_NONE :
232 computeResamplingMode(bitmap, srcRect.width(), srcRect.height(),
233 SkScalarToFloat(destRect.width()),
234 SkScalarToFloat(destRect.height()));
235 if (resampling == RESAMPLE_AWESOME) {
236 paint.setFilterBitmap(false);
237 drawResampledBitmap(*canvas, paint, bitmap, srcRect, destRect);
239 // No resampling necessary, we can just draw the bitmap. We want to
240 // filter it if we decided to do linear interpolation above, or if there
241 // is something interesting going on with the matrix (like a rotation).
242 // Note: for serialization, we will want to subset the bitmap first so
243 // we don't send extra pixels.
244 paint.setFilterBitmap(resampling == RESAMPLE_LINEAR);
245 canvas->drawBitmapRect(bitmap, &srcRect, destRect, &paint);
249 // Transforms the given dimensions with the given matrix. Used to see how big
250 // images will be once transformed.
251 static void TransformDimensions(const SkMatrix& matrix, float srcWidth, float srcHeight, float* destWidth, float* destHeight) {
252 // Transform 3 points to see how long each side of the bitmap will be.
253 SkPoint src_points[3]; // (0, 0), (width, 0), (0, height).
254 src_points[0].set(0, 0);
255 src_points[1].set(SkFloatToScalar(srcWidth), 0);
256 src_points[2].set(0, SkFloatToScalar(srcHeight));
258 // Now measure the length of the two transformed vectors relative to the
259 // transformed origin to see how big the bitmap will be. Note: for skews,
260 // this isn't the best thing, but we don't have skews.
261 SkPoint dest_points[3];
262 matrix.mapPoints(dest_points, src_points, 3);
263 *destWidth = SkScalarToFloat((dest_points[1] - dest_points[0]).length());
264 *destHeight = SkScalarToFloat((dest_points[2] - dest_points[0]).length());
267 // A helper method for translating negative width and height values.
268 static FloatRect normalizeRect(const FloatRect& rect)
270 FloatRect norm = rect;
271 if (norm.width() < 0) {
272 norm.setX(norm.x() + norm.width());
273 norm.setWidth(-norm.width());
275 if (norm.height() < 0) {
276 norm.setY(norm.y() + norm.height());
277 norm.setHeight(-norm.height());
282 bool FrameData::clear(bool clearMetadata)
285 m_haveMetadata = false;
288 // ImageSource::createFrameAtIndex() allocated |m_frame| and passed
289 // ownership to BitmapImage; we must delete it here.
297 PassRefPtr<Image> Image::loadPlatformResource(const char *name)
299 return ChromiumBridge::loadPlatformImageResource(name);
302 void Image::drawPattern(GraphicsContext* context,
303 const FloatRect& floatSrcRect,
304 const TransformationMatrix& patternTransform,
305 const FloatPoint& phase,
306 CompositeOperator compositeOp,
307 const FloatRect& destRect)
309 if (destRect.isEmpty() || floatSrcRect.isEmpty())
310 return; // nothing to draw
312 NativeImageSkia* bitmap = nativeImageForCurrentFrame();
316 // This is a very inexpensive operation. It will generate a new bitmap but
317 // it will internally reference the old bitmap's pixels, adjusting the row
318 // stride so the extra pixels appear as padding to the subsetted bitmap.
320 SkIRect srcRect = enclosingIntRect(floatSrcRect);
321 bitmap->extractSubset(&srcSubset, srcRect);
326 // Figure out what size the bitmap will be in the destination. The
327 // destination rect is the bounds of the pattern, we need to use the
328 // matrix to see how bit it will be.
329 float destBitmapWidth, destBitmapHeight;
330 TransformDimensions(patternTransform, srcRect.width(), srcRect.height(),
331 &destBitmapWidth, &destBitmapHeight);
333 // Compute the resampling mode.
334 ResamplingMode resampling;
335 if (context->platformContext()->isPrinting())
336 resampling = RESAMPLE_LINEAR;
338 resampling = computeResamplingMode(*bitmap,
339 srcRect.width(), srcRect.height(),
340 destBitmapWidth, destBitmapHeight);
343 // Load the transform WebKit requested.
344 SkMatrix matrix(patternTransform);
346 if (resampling == RESAMPLE_AWESOME) {
347 // Do nice resampling.
348 SkBitmap resampled = skia::ImageOperations::Resize(srcSubset,
349 skia::ImageOperations::RESIZE_LANCZOS3,
350 static_cast<int>(destBitmapWidth),
351 static_cast<int>(destBitmapHeight));
352 shader = SkShader::CreateBitmapShader(resampled, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode);
354 // Since we just resized the bitmap, we need to undo the scale set in
355 // the image transform.
356 matrix.setScaleX(SkIntToScalar(1));
357 matrix.setScaleY(SkIntToScalar(1));
359 // No need to do nice resampling.
360 shader = SkShader::CreateBitmapShader(srcSubset, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode);
363 // We also need to translate it such that the origin of the pattern is the
364 // origin of the destination rect, which is what WebKit expects. Skia uses
365 // the coordinate system origin as the base for the patter. If WebKit wants
366 // a shifted image, it will shift it from there using the patternTransform.
367 float adjustedX = phase.x() + floatSrcRect.x() *
368 narrowPrecisionToFloat(patternTransform.a());
369 float adjustedY = phase.y() + floatSrcRect.y() *
370 narrowPrecisionToFloat(patternTransform.d());
371 matrix.postTranslate(SkFloatToScalar(adjustedX),
372 SkFloatToScalar(adjustedY));
373 shader->setLocalMatrix(matrix);
376 paint.setShader(shader)->unref();
377 paint.setPorterDuffXfermode(WebCoreCompositeToSkiaComposite(compositeOp));
378 paint.setFilterBitmap(resampling == RESAMPLE_LINEAR);
380 context->platformContext()->paintSkPaint(destRect, paint);
383 // ================================================
385 // ================================================
387 // FIXME: These should go to BitmapImageSkia.cpp
389 void BitmapImage::initPlatformData()
391 // This is not used. On Mac, the "platform" data is a cache of some OS
392 // specific versions of the image that are created is some cases. These
393 // aren't normally used, it is equivalent to getHBITMAP on Windows, and
394 // the platform data is the cache.
397 void BitmapImage::invalidatePlatformData()
399 // See initPlatformData above.
402 void BitmapImage::checkForSolidColor()
406 void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect,
407 const FloatRect& srcRect, CompositeOperator compositeOp)
409 if (!m_source.initialized())
412 // Spin the animation to the correct frame before we try to draw it, so we
413 // don't draw an old frame and then immediately need to draw a newer one,
414 // causing flicker and wasting CPU.
417 const NativeImageSkia* bm = nativeImageForCurrentFrame();
419 return; // It's too early and we don't have an image yet.
421 FloatRect normDstRect = normalizeRect(dstRect);
422 FloatRect normSrcRect = normalizeRect(srcRect);
424 if (normSrcRect.isEmpty() || normDstRect.isEmpty())
425 return; // Nothing to draw.
427 paintSkBitmap(ctxt->platformContext(),
429 enclosingIntRect(normSrcRect),
430 enclosingIntRect(normDstRect),
431 WebCoreCompositeToSkiaComposite(compositeOp));
434 // FIXME: These should go into BitmapImageSingleFrameSkia.cpp
436 void BitmapImageSingleFrameSkia::draw(GraphicsContext* ctxt,
437 const FloatRect& dstRect,
438 const FloatRect& srcRect,
439 CompositeOperator compositeOp)
441 FloatRect normDstRect = normalizeRect(dstRect);
442 FloatRect normSrcRect = normalizeRect(srcRect);
444 if (normSrcRect.isEmpty() || normDstRect.isEmpty())
445 return; // Nothing to draw.
447 paintSkBitmap(ctxt->platformContext(),
449 enclosingIntRect(normSrcRect),
450 enclosingIntRect(normDstRect),
451 WebCoreCompositeToSkiaComposite(compositeOp));
454 PassRefPtr<BitmapImageSingleFrameSkia> BitmapImageSingleFrameSkia::create(const SkBitmap& bitmap)
456 RefPtr<BitmapImageSingleFrameSkia> image(adoptRef(new BitmapImageSingleFrameSkia()));
457 if (!bitmap.copyTo(&image->m_nativeImage, bitmap.config()))
459 return image.release();
462 } // namespace WebCore