2009-01-14 David Levin <levin@chromium.org>
[WebKit-https.git] / WebCore / platform / graphics / skia / ImageSkia.cpp
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 "BitmapImage.h"
34 #include "BitmapImageSingleFrameSkia.h"
35 #include "ChromiumBridge.h"
36 #include "FloatConversion.h"
37 #include "FloatRect.h"
38 #include "GraphicsContext.h"
39 #include "Logging.h"
40 #include "NativeImageSkia.h"
41 #include "NotImplemented.h"
42 #include "PlatformContextSkia.h"
43 #include "PlatformString.h"
44 #include "SkiaUtils.h"
45 #include "SkShader.h"
46 #include "TransformationMatrix.h"
47
48 #include "skia/ext/image_operations.h"
49 #include "skia/ext/platform_canvas.h"
50
51 namespace WebCore {
52
53 // Used by computeResamplingMode to tell how bitmaps should be resampled.
54 enum ResamplingMode {
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.
57     RESAMPLE_NONE,
58
59     // Default skia resampling. Used for large growing of images where high
60     // quality resampling doesn't get us very much except a slowdown.
61     RESAMPLE_LINEAR,
62
63     // High quality resampling.
64     RESAMPLE_AWESOME,
65 };
66
67 static ResamplingMode computeResamplingMode(const NativeImageSkia& bitmap, int srcWidth, int srcHeight, float destWidth, float destHeight)
68 {
69     int destIWidth = static_cast<int>(destWidth);
70     int destIHeight = static_cast<int>(destHeight);
71
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;
76
77     // Images smaller than this in either direction are considered "small" and
78     // are not resampled ever (see below).
79     const int kSmallImageSizeThreshold = 8;
80
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;
85
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.
91         return RESAMPLE_NONE;
92     }
93
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;
101     }
102
103     if (srcHeight * kLargeStretch <= destHeight || srcWidth * kLargeStretch <= destWidth) {
104         // Large image detected.
105
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
109         // of the page.
110         if (srcWidth == destWidth || srcHeight == destHeight)
111             return RESAMPLE_NONE;
112
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;
116     }
117
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;
124     }
125
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;
131
132     // Everything else gets resampled.
133     return RESAMPLE_AWESOME;
134 }
135
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.
140 //
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
145 // dramatically.
146 static void drawResampledBitmap(SkCanvas& canvas, SkPaint& paint, const NativeImageSkia& bitmap, const SkIRect& srcIRect, const SkRect& destRect)
147 {
148     // First get the subset we need. This is efficient and does not copy pixels.
149     SkBitmap subset;
150     bitmap.extractSubset(&subset, srcIRect);
151     SkRect srcRect;
152     srcRect.set(srcIRect);
153
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();
158
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());
164
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);
169         return;
170     }
171
172     // Compute the visible portion of our rect.
173     SkRect destBitmapSubsetSk;
174     ClipRectToCanvas(canvas, destRect, &destBitmapSubsetSk);
175     destBitmapSubsetSk.offset(-destRect.fLeft, -destRect.fTop);
176
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
181     // data.
182     SkIRect destBitmapSubsetSkI;
183     destBitmapSubsetSk.roundOut(&destBitmapSubsetSkI);
184     if (!destBitmapSubsetSkI.intersect(resizedImageRect))
185         return;  // Resized image does not intersect.
186
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);
197     } else {
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());
204
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(),
209             destBitmapSubset);
210
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 };
219
220         canvas.drawBitmapRect(resampled, 0, offsetDestRect, &paint);
221     }
222 }
223
224 static void paintSkBitmap(PlatformContextSkia* platformContext, const NativeImageSkia& bitmap, const SkIRect& srcRect, const SkRect& destRect, const SkPorterDuff::Mode& compOp)
225 {
226     SkPaint paint;
227     paint.setPorterDuffXfermode(compOp);
228
229     skia::PlatformCanvas* canvas = platformContext->canvas();
230
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);
238     } else {
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);
246     }
247 }
248
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));
257
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());
265 }
266
267 // A helper method for translating negative width and height values.
268 static FloatRect normalizeRect(const FloatRect& rect)
269 {
270     FloatRect norm = rect;
271     if (norm.width() < 0) {
272         norm.setX(norm.x() + norm.width());
273         norm.setWidth(-norm.width());
274     }
275     if (norm.height() < 0) {
276         norm.setY(norm.y() + norm.height());
277         norm.setHeight(-norm.height());
278     }
279     return norm;
280 }
281
282 bool FrameData::clear(bool clearMetadata)
283 {
284     if (clearMetadata)
285         m_haveMetadata = false;
286
287     if (m_frame) {
288         // ImageSource::createFrameAtIndex() allocated |m_frame| and passed
289         // ownership to BitmapImage; we must delete it here.
290         delete m_frame;
291         m_frame = 0;
292         return true;
293     }
294     return false;
295 }
296
297 PassRefPtr<Image> Image::loadPlatformResource(const char *name)
298 {
299     return ChromiumBridge::loadPlatformImageResource(name);
300 }
301
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)
308 {
309     if (destRect.isEmpty() || floatSrcRect.isEmpty())
310         return;  // nothing to draw
311
312     NativeImageSkia* bitmap = nativeImageForCurrentFrame();
313     if (!bitmap)
314         return;
315
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.
319     SkBitmap srcSubset;
320     SkIRect srcRect = enclosingIntRect(floatSrcRect);
321     bitmap->extractSubset(&srcSubset, srcRect);
322
323     SkBitmap resampled;
324     SkShader* shader;
325
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);
332
333     // Compute the resampling mode.
334     ResamplingMode resampling;
335     if (context->platformContext()->isPrinting())
336       resampling = RESAMPLE_LINEAR;
337     else {
338       resampling = computeResamplingMode(*bitmap,
339                                          srcRect.width(), srcRect.height(),
340                                          destBitmapWidth, destBitmapHeight);
341     }
342
343     // Load the transform WebKit requested.
344     SkMatrix matrix(patternTransform);
345
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);
353
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));
358     } else {
359         // No need to do nice resampling.
360         shader = SkShader::CreateBitmapShader(srcSubset, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode);
361     }
362
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);
374
375     SkPaint paint;
376     paint.setShader(shader)->unref();
377     paint.setPorterDuffXfermode(WebCoreCompositeToSkiaComposite(compositeOp));
378     paint.setFilterBitmap(resampling == RESAMPLE_LINEAR);
379
380     context->platformContext()->paintSkPaint(destRect, paint);
381 }
382
383 // ================================================
384 // BitmapImage Class
385 // ================================================
386
387 // FIXME: These should go to BitmapImageSkia.cpp
388
389 void BitmapImage::initPlatformData()
390 {
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.
395 }
396
397 void BitmapImage::invalidatePlatformData()
398 {
399     // See initPlatformData above.
400 }
401
402 void BitmapImage::checkForSolidColor()
403 {
404 }
405
406 void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect,
407                        const FloatRect& srcRect, CompositeOperator compositeOp)
408 {
409     if (!m_source.initialized())
410         return;
411
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.
415     startAnimation();
416
417     const NativeImageSkia* bm = nativeImageForCurrentFrame();
418     if (!bm)
419         return;  // It's too early and we don't have an image yet.
420
421     FloatRect normDstRect = normalizeRect(dstRect);
422     FloatRect normSrcRect = normalizeRect(srcRect);
423
424     if (normSrcRect.isEmpty() || normDstRect.isEmpty())
425         return;  // Nothing to draw.
426
427     paintSkBitmap(ctxt->platformContext(),
428                   *bm,
429                   enclosingIntRect(normSrcRect),
430                   enclosingIntRect(normDstRect),
431                   WebCoreCompositeToSkiaComposite(compositeOp));
432 }
433
434 // FIXME: These should go into BitmapImageSingleFrameSkia.cpp
435
436 void BitmapImageSingleFrameSkia::draw(GraphicsContext* ctxt,
437                                       const FloatRect& dstRect,
438                                       const FloatRect& srcRect,
439                                       CompositeOperator compositeOp)
440 {
441     FloatRect normDstRect = normalizeRect(dstRect);
442     FloatRect normSrcRect = normalizeRect(srcRect);
443
444     if (normSrcRect.isEmpty() || normDstRect.isEmpty())
445         return;  // Nothing to draw.
446
447     paintSkBitmap(ctxt->platformContext(),
448                   m_nativeImage,
449                   enclosingIntRect(normSrcRect),
450                   enclosingIntRect(normDstRect),
451                   WebCoreCompositeToSkiaComposite(compositeOp));
452 }
453
454 PassRefPtr<BitmapImageSingleFrameSkia> BitmapImageSingleFrameSkia::create(const SkBitmap& bitmap)
455 {
456     RefPtr<BitmapImageSingleFrameSkia> image(adoptRef(new BitmapImageSingleFrameSkia()));
457     if (!bitmap.copyTo(&image->m_nativeImage, bitmap.config()))
458         return 0;
459     return image.release();
460 }
461
462 }  // namespace WebCore