Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / platform / graphics / skia / NativeImageSkia.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 #include "platform/graphics/skia/NativeImageSkia.h"
33
34 #include "platform/PlatformInstrumentation.h"
35 #include "platform/TraceEvent.h"
36 #include "platform/geometry/FloatPoint.h"
37 #include "platform/geometry/FloatRect.h"
38 #include "platform/geometry/FloatSize.h"
39 #include "platform/graphics/DeferredImageDecoder.h"
40 #include "platform/graphics/GraphicsContext.h"
41 #include "platform/graphics/Image.h"
42 #include "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 <math.h>
50
51 namespace blink {
52
53 // This function is used to scale an image and extract a scaled fragment.
54 //
55 // ALGORITHM
56 //
57 // Because the scaled image size has to be integers, we approximate the real
58 // scale with the following formula (only X direction is shown):
59 //
60 // scaledImageWidth = round(scaleX * imageRect.width())
61 // approximateScaleX = scaledImageWidth / imageRect.width()
62 //
63 // With this method we maintain a constant scale factor among fragments in
64 // the scaled image. This allows fragments to stitch together to form the
65 // full scaled image. The downside is there will be a small difference
66 // between |scaleX| and |approximateScaleX|.
67 //
68 // A scaled image fragment is identified by:
69 //
70 // - Scaled image size
71 // - Scaled image fragment rectangle (IntRect)
72 //
73 // Scaled image size has been determined and the next step is to compute the
74 // rectangle for the scaled image fragment which needs to be an IntRect.
75 //
76 // scaledSrcRect = srcRect * (approximateScaleX, approximateScaleY)
77 // enclosingScaledSrcRect = enclosingIntRect(scaledSrcRect)
78 //
79 // Finally we extract the scaled image fragment using
80 // (scaledImageSize, enclosingScaledSrcRect).
81 //
82 SkBitmap NativeImageSkia::extractScaledImageFragment(const SkRect& srcRect, float scaleX, float scaleY, SkRect* scaledSrcRect) const
83 {
84     SkISize imageSize = SkISize::Make(bitmap().width(), bitmap().height());
85     SkISize scaledImageSize = SkISize::Make(clampToInteger(roundf(imageSize.width() * scaleX)),
86         clampToInteger(roundf(imageSize.height() * scaleY)));
87
88     SkRect imageRect = SkRect::MakeWH(imageSize.width(), imageSize.height());
89     SkRect scaledImageRect = SkRect::MakeWH(scaledImageSize.width(), scaledImageSize.height());
90
91     SkMatrix scaleTransform;
92     scaleTransform.setRectToRect(imageRect, scaledImageRect, SkMatrix::kFill_ScaleToFit);
93     scaleTransform.mapRect(scaledSrcRect, srcRect);
94
95     scaledSrcRect->intersect(scaledImageRect);
96     SkIRect enclosingScaledSrcRect = enclosingIntRect(*scaledSrcRect);
97
98     // |enclosingScaledSrcRect| can be larger than |scaledImageSize| because
99     // of float inaccuracy so clip to get inside.
100     enclosingScaledSrcRect.intersect(SkIRect::MakeSize(scaledImageSize));
101
102     // scaledSrcRect is relative to the pixel snapped fragment we're extracting.
103     scaledSrcRect->offset(-enclosingScaledSrcRect.x(), -enclosingScaledSrcRect.y());
104
105     return resizedBitmap(scaledImageSize, enclosingScaledSrcRect);
106 }
107
108 NativeImageSkia::NativeImageSkia()
109     : m_resizeRequests(0)
110 {
111 }
112
113 NativeImageSkia::NativeImageSkia(const SkBitmap& other)
114     : m_bitmap(other)
115     , m_resizeRequests(0)
116 {
117 }
118
119 NativeImageSkia::~NativeImageSkia()
120 {
121 }
122
123 bool NativeImageSkia::hasResizedBitmap(const SkISize& scaledImageSize, const SkIRect& scaledImageSubset) const
124 {
125     bool imageScaleEqual = m_cachedImageInfo.scaledImageSize == scaledImageSize;
126     bool scaledImageSubsetAvailable = m_cachedImageInfo.scaledImageSubset.contains(scaledImageSubset);
127     return imageScaleEqual && scaledImageSubsetAvailable && !m_resizedImage.empty();
128 }
129
130 SkBitmap NativeImageSkia::resizedBitmap(const SkISize& scaledImageSize, const SkIRect& scaledImageSubset) const
131 {
132     ASSERT(!DeferredImageDecoder::isLazyDecoded(bitmap()));
133
134     if (!hasResizedBitmap(scaledImageSize, scaledImageSubset)) {
135         bool shouldCache = isDataComplete()
136             && shouldCacheResampling(scaledImageSize, scaledImageSubset);
137
138         TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "ResizeImage", "cached", shouldCache);
139         // FIXME(361045): remove InspectorInstrumentation calls once DevTools Timeline migrates to tracing.
140         PlatformInstrumentation::willResizeImage(shouldCache);
141         SkBitmap resizedImage = skia::ImageOperations::Resize(bitmap(), skia::ImageOperations::RESIZE_LANCZOS3, scaledImageSize.width(), scaledImageSize.height(), scaledImageSubset);
142         resizedImage.setImmutable();
143         PlatformInstrumentation::didResizeImage();
144
145         if (!shouldCache)
146             return resizedImage;
147
148         m_resizedImage = resizedImage;
149     }
150
151     SkBitmap resizedSubset;
152     SkIRect resizedSubsetRect = m_cachedImageInfo.rectInSubset(scaledImageSubset);
153     m_resizedImage.extractSubset(&resizedSubset, resizedSubsetRect);
154     return resizedSubset;
155 }
156
157 void NativeImageSkia::draw(
158     GraphicsContext* context,
159     const SkRect& srcRect,
160     const SkRect& destRect,
161     CompositeOperator compositeOp,
162     WebBlendMode blendMode) const
163 {
164     TRACE_EVENT0("skia", "NativeImageSkia::draw");
165
166     bool isLazyDecoded = DeferredImageDecoder::isLazyDecoded(bitmap());
167
168     SkPaint paint;
169     context->preparePaintForDrawRectToRect(&paint, srcRect, destRect, compositeOp, blendMode, isLazyDecoded, isDataComplete());
170     // We want to filter it if we decided to do interpolation above, or if
171     // there is something interesting going on with the matrix (like a rotation).
172     // Note: for serialization, we will want to subset the bitmap first so we
173     // don't send extra pixels.
174     context->drawBitmapRect(bitmap(), &srcRect, destRect, &paint);
175
176     if (isLazyDecoded)
177         PlatformInstrumentation::didDrawLazyPixelRef(bitmap().getGenerationID());
178     context->didDrawRect(destRect, paint, &bitmap());
179 }
180
181 static SkBitmap createBitmapWithSpace(const SkBitmap& bitmap, int spaceWidth, int spaceHeight)
182 {
183     SkImageInfo info = bitmap.info();
184     info.fWidth += spaceWidth;
185     info.fHeight += spaceHeight;
186     info.fAlphaType = kPremul_SkAlphaType;
187
188     SkBitmap result;
189     result.allocPixels(info);
190     result.eraseColor(SK_ColorTRANSPARENT);
191     bitmap.copyPixelsTo(reinterpret_cast<uint8_t*>(result.getPixels()), result.rowBytes() * result.height(), result.rowBytes());
192
193     return result;
194 }
195
196 void NativeImageSkia::drawPattern(
197     GraphicsContext* context,
198     const FloatRect& floatSrcRect,
199     const FloatSize& scale,
200     const FloatPoint& phase,
201     CompositeOperator compositeOp,
202     const FloatRect& destRect,
203     WebBlendMode blendMode,
204     const IntSize& repeatSpacing) const
205 {
206     FloatRect normSrcRect = floatSrcRect;
207     normSrcRect.intersect(FloatRect(0, 0, bitmap().width(), bitmap().height()));
208     if (destRect.isEmpty() || normSrcRect.isEmpty())
209         return; // nothing to draw
210
211     SkMatrix totalMatrix = context->getTotalMatrix();
212     AffineTransform ctm = context->getCTM();
213     SkScalar ctmScaleX = ctm.xScale();
214     SkScalar ctmScaleY = ctm.yScale();
215     totalMatrix.preScale(scale.width(), scale.height());
216
217     // Figure out what size the bitmap will be in the destination. The
218     // destination rect is the bounds of the pattern, we need to use the
219     // matrix to see how big it will be.
220     SkRect destRectTarget;
221     totalMatrix.mapRect(&destRectTarget, normSrcRect);
222
223     float destBitmapWidth = SkScalarToFloat(destRectTarget.width());
224     float destBitmapHeight = SkScalarToFloat(destRectTarget.height());
225
226     bool isLazyDecoded = DeferredImageDecoder::isLazyDecoded(bitmap());
227
228     // Compute the resampling mode.
229     InterpolationQuality resampling;
230     if (context->isAccelerated() || context->printing())
231         resampling = InterpolationLow;
232     else if (isLazyDecoded)
233         resampling = InterpolationHigh;
234     else
235         resampling = computeInterpolationQuality(totalMatrix, normSrcRect.width(), normSrcRect.height(), destBitmapWidth, destBitmapHeight, isDataComplete());
236     resampling = limitInterpolationQuality(context, resampling);
237
238     SkMatrix localMatrix;
239     // We also need to translate it such that the origin of the pattern is the
240     // origin of the destination rect, which is what WebKit expects. Skia uses
241     // the coordinate system origin as the base for the pattern. If WebKit wants
242     // a shifted image, it will shift it from there using the localMatrix.
243     const float adjustedX = phase.x() + normSrcRect.x() * scale.width();
244     const float adjustedY = phase.y() + normSrcRect.y() * scale.height();
245     localMatrix.setTranslate(SkFloatToScalar(adjustedX), SkFloatToScalar(adjustedY));
246
247     RefPtr<SkShader> shader;
248     SkPaint::FilterLevel filterLevel = static_cast<SkPaint::FilterLevel>(resampling);
249
250     // Bicubic filter is only applied to defer-decoded images, see
251     // NativeImageSkia::draw for details.
252     if (resampling == InterpolationHigh && !isLazyDecoded) {
253         // Do nice resampling.
254         filterLevel = SkPaint::kNone_FilterLevel;
255         float scaleX = destBitmapWidth / normSrcRect.width();
256         float scaleY = destBitmapHeight / normSrcRect.height();
257         SkRect scaledSrcRect;
258
259         // Since we are resizing the bitmap, we need to remove the scale
260         // applied to the pixels in the bitmap shader. This means we need
261         // CTM * localMatrix to have identity scale. Since we
262         // can't modify CTM (or the rectangle will be drawn in the wrong
263         // place), we must set localMatrix's scale to the inverse of
264         // CTM scale.
265         localMatrix.preScale(ctmScaleX ? 1 / ctmScaleX : 1, ctmScaleY ? 1 / ctmScaleY : 1);
266
267         // The image fragment generated here is not exactly what is
268         // requested. The scale factor used is approximated and image
269         // fragment is slightly larger to align to integer
270         // boundaries.
271         SkBitmap resampled = extractScaledImageFragment(normSrcRect, scaleX, scaleY, &scaledSrcRect);
272         if (repeatSpacing.isZero()) {
273             shader = adoptRef(SkShader::CreateBitmapShader(resampled, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix));
274         } else {
275             shader = adoptRef(SkShader::CreateBitmapShader(
276                 createBitmapWithSpace(resampled, repeatSpacing.width() * ctmScaleX, repeatSpacing.height() * ctmScaleY),
277                 SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix));
278         }
279     } else {
280         // Because no resizing occurred, the shader transform should be
281         // set to the pattern's transform, which just includes scale.
282         localMatrix.preScale(scale.width(), scale.height());
283
284         // No need to resample before drawing.
285         SkBitmap srcSubset;
286         bitmap().extractSubset(&srcSubset, enclosingIntRect(normSrcRect));
287         if (repeatSpacing.isZero()) {
288             shader = adoptRef(SkShader::CreateBitmapShader(srcSubset, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix));
289         } else {
290             shader = adoptRef(SkShader::CreateBitmapShader(
291                 createBitmapWithSpace(srcSubset, repeatSpacing.width() * ctmScaleX, repeatSpacing.height() * ctmScaleY),
292                 SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix));
293         }
294     }
295
296     SkPaint paint;
297     paint.setShader(shader.get());
298     paint.setXfermodeMode(WebCoreCompositeToSkiaComposite(compositeOp, blendMode));
299     paint.setColorFilter(context->colorFilter());
300     paint.setFilterLevel(filterLevel);
301
302     if (isLazyDecoded)
303         PlatformInstrumentation::didDrawLazyPixelRef(bitmap().getGenerationID());
304
305     context->drawRect(destRect, paint);
306 }
307
308 bool NativeImageSkia::shouldCacheResampling(const SkISize& scaledImageSize, const SkIRect& scaledImageSubset) const
309 {
310     // Check whether the requested dimensions match previous request.
311     bool matchesPreviousRequest = m_cachedImageInfo.isEqual(scaledImageSize, scaledImageSubset);
312     if (matchesPreviousRequest)
313         ++m_resizeRequests;
314     else {
315         m_cachedImageInfo.set(scaledImageSize, scaledImageSubset);
316         m_resizeRequests = 0;
317         // Reset m_resizedImage now, because we don't distinguish
318         // between the last requested resize info and m_resizedImage's
319         // resize info.
320         m_resizedImage.reset();
321     }
322
323     // We can not cache incomplete frames. This might be a good optimization in
324     // the future, were we know how much of the frame has been decoded, so when
325     // we incrementally draw more of the image, we only have to resample the
326     // parts that are changed.
327     if (!isDataComplete())
328         return false;
329
330     // If the destination bitmap is excessively large, we'll never allow caching.
331     static const unsigned long long kLargeBitmapSize = 4096ULL * 4096ULL;
332     unsigned long long fullSize = static_cast<unsigned long long>(scaledImageSize.width()) * static_cast<unsigned long long>(scaledImageSize.height());
333     unsigned long long fragmentSize = static_cast<unsigned long long>(scaledImageSubset.width()) * static_cast<unsigned long long>(scaledImageSubset.height());
334
335     if (fragmentSize > kLargeBitmapSize)
336         return false;
337
338     // If the destination bitmap is small, we'll always allow caching, since
339     // there is not very much penalty for computing it and it may come in handy.
340     static const unsigned kSmallBitmapSize = 4096;
341     if (fragmentSize <= kSmallBitmapSize)
342         return true;
343
344     // If "too many" requests have been made for this bitmap, we assume that
345     // many more will be made as well, and we'll go ahead and cache it.
346     static const int kManyRequestThreshold = 4;
347     if (m_resizeRequests >= kManyRequestThreshold)
348         return true;
349
350     // If more than 1/4 of the resized image is requested, it's worth caching.
351     return fragmentSize > fullSize / 4;
352 }
353
354 NativeImageSkia::ImageResourceInfo::ImageResourceInfo()
355 {
356     scaledImageSize.setEmpty();
357     scaledImageSubset.setEmpty();
358 }
359
360 bool NativeImageSkia::ImageResourceInfo::isEqual(const SkISize& otherScaledImageSize, const SkIRect& otherScaledImageSubset) const
361 {
362     return scaledImageSize == otherScaledImageSize && scaledImageSubset == otherScaledImageSubset;
363 }
364
365 void NativeImageSkia::ImageResourceInfo::set(const SkISize& otherScaledImageSize, const SkIRect& otherScaledImageSubset)
366 {
367     scaledImageSize = otherScaledImageSize;
368     scaledImageSubset = otherScaledImageSubset;
369 }
370
371 SkIRect NativeImageSkia::ImageResourceInfo::rectInSubset(const SkIRect& otherScaledImageSubset)
372 {
373     if (!scaledImageSubset.contains(otherScaledImageSubset))
374         return SkIRect::MakeEmpty();
375     SkIRect subsetRect = otherScaledImageSubset;
376     subsetRect.offset(-scaledImageSubset.x(), -scaledImageSubset.y());
377     return subsetRect;
378 }
379
380 } // namespace blink