Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(37)

Side by Side Diff: Source/platform/graphics/skia/NativeImageSkia.cpp

Issue 685333003: Remove pre-scaling code from NativeImageSkia. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: rebase; add TestExpectations Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « Source/platform/graphics/skia/NativeImageSkia.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright (c) 2008, Google Inc. All rights reserved. 2 * Copyright (c) 2008, Google Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are 5 * modification, are permitted provided that the following conditions are
6 * met: 6 * met:
7 * 7 *
8 * * Redistributions of source code must retain the above copyright 8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer. 9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above 10 * * Redistributions in binary form must reproduce the above
(...skipping 15 matching lines...) Expand all
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 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. 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */ 29 */
30 30
31 #include "config.h" 31 #include "config.h"
32 #include "platform/graphics/skia/NativeImageSkia.h" 32 #include "platform/graphics/skia/NativeImageSkia.h"
33 33
34 #include "platform/PlatformInstrumentation.h" 34 #include "platform/PlatformInstrumentation.h"
35 #include "platform/TraceEvent.h" 35 #include "platform/TraceEvent.h"
36 #include "platform/geometry/FloatPoint.h"
37 #include "platform/geometry/FloatRect.h" 36 #include "platform/geometry/FloatRect.h"
38 #include "platform/geometry/FloatSize.h"
39 #include "platform/graphics/DeferredImageDecoder.h" 37 #include "platform/graphics/DeferredImageDecoder.h"
40 #include "platform/graphics/GraphicsContext.h" 38 #include "platform/graphics/GraphicsContext.h"
41 #include "platform/graphics/Image.h"
42 #include "platform/graphics/skia/SkiaUtils.h" 39 #include "platform/graphics/skia/SkiaUtils.h"
43 #include "skia/ext/image_operations.h" 40 #include "platform/transforms/AffineTransform.h"
41 #include "third_party/skia/include/core/SkColor.h"
42 #include "third_party/skia/include/core/SkImageInfo.h"
44 #include "third_party/skia/include/core/SkMatrix.h" 43 #include "third_party/skia/include/core/SkMatrix.h"
45 #include "third_party/skia/include/core/SkPaint.h" 44 #include "third_party/skia/include/core/SkPaint.h"
45 #include "third_party/skia/include/core/SkRect.h"
46 #include "third_party/skia/include/core/SkScalar.h" 46 #include "third_party/skia/include/core/SkScalar.h"
47 #include "third_party/skia/include/core/SkShader.h" 47 #include "third_party/skia/include/core/SkShader.h"
48 48
49 #include <math.h>
50
51 namespace blink { 49 namespace blink {
52 50
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, floa t scaleX, float scaleY, SkRect* scaledSrcRect) const
83 {
84 SkISize imageSize = SkISize::Make(bitmap().width(), bitmap().height());
85 SkISize scaledImageSize = SkISize::Make(clampTo<int>(roundf(imageSize.width( ) * scaleX)),
86 clampTo<int>(roundf(imageSize.height() * scaleY)));
87
88 SkRect imageRect = SkRect::MakeWH(imageSize.width(), imageSize.height());
89 SkRect scaledImageRect = SkRect::MakeWH(scaledImageSize.width(), scaledImage Size.height());
90
91 SkMatrix scaleTransform;
92 scaleTransform.setRectToRect(imageRect, scaledImageRect, SkMatrix::kFill_Sca leToFit);
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 SkI Rect& scaledImageSubset) const
124 {
125 bool imageScaleEqual = m_cachedImageInfo.scaledImageSize == scaledImageSize;
126 bool scaledImageSubsetAvailable = m_cachedImageInfo.scaledImageSubset.contai ns(scaledImageSubset);
127 return imageScaleEqual && scaledImageSubsetAvailable && !m_resizedImage.empt y();
128 }
129
130 SkBitmap NativeImageSkia::resizedBitmap(const SkISize& scaledImageSize, const Sk IRect& 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"), "ResizeImag e", "cached", shouldCache);
139 // FIXME(361045): remove InspectorInstrumentation calls once DevTools Ti meline migrates to tracing.
140 PlatformInstrumentation::willResizeImage(shouldCache);
141 SkBitmap resizedImage = skia::ImageOperations::Resize(bitmap(), skia::Im ageOperations::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( 51 void NativeImageSkia::draw(
158 GraphicsContext* context, 52 GraphicsContext* context,
159 const SkRect& srcRect, 53 const SkRect& srcRect,
160 const SkRect& destRect, 54 const SkRect& destRect,
161 CompositeOperator compositeOp, 55 CompositeOperator compositeOp,
162 WebBlendMode blendMode) const 56 WebBlendMode blendMode) const
163 { 57 {
164 TRACE_EVENT0("skia", "NativeImageSkia::draw"); 58 TRACE_EVENT0("skia", "NativeImageSkia::draw");
165 59
166 bool isLazyDecoded = DeferredImageDecoder::isLazyDecoded(bitmap()); 60 bool isLazyDecoded = DeferredImageDecoder::isLazyDecoded(bitmap());
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
228 InterpolationQuality resampling; 122 InterpolationQuality resampling;
229 if (context->isAccelerated() || context->printing()) 123 if (context->isAccelerated() || context->printing())
230 resampling = InterpolationLow; 124 resampling = InterpolationLow;
231 else if (isLazyDecoded) 125 else if (isLazyDecoded)
232 resampling = InterpolationHigh; 126 resampling = InterpolationHigh;
233 else 127 else
234 resampling = computeInterpolationQuality(totalMatrix, normSrcRect.width( ), normSrcRect.height(), destBitmapWidth, destBitmapHeight, isDataComplete()); 128 resampling = computeInterpolationQuality(totalMatrix, normSrcRect.width( ), normSrcRect.height(), destBitmapWidth, destBitmapHeight, isDataComplete());
235 resampling = limitInterpolationQuality(context, resampling); 129 resampling = limitInterpolationQuality(context, resampling);
236 130
237 SkMatrix localMatrix; 131 SkMatrix localMatrix;
132
238 // We also need to translate it such that the origin of the pattern is the 133 // We also need to translate it such that the origin of the pattern is the
239 // origin of the destination rect, which is what WebKit expects. Skia uses 134 // origin of the destination rect, which is what WebKit expects. Skia uses
240 // the coordinate system origin as the base for the pattern. If WebKit wants 135 // the coordinate system origin as the base for the pattern. If WebKit wants
241 // a shifted image, it will shift it from there using the localMatrix. 136 // a shifted image, it will shift it from there using the localMatrix.
242 const float adjustedX = phase.x() + normSrcRect.x() * scale.width(); 137 const float adjustedX = phase.x() + normSrcRect.x() * scale.width();
243 const float adjustedY = phase.y() + normSrcRect.y() * scale.height(); 138 const float adjustedY = phase.y() + normSrcRect.y() * scale.height();
244 localMatrix.setTranslate(SkFloatToScalar(adjustedX), SkFloatToScalar(adjuste dY)); 139 localMatrix.setTranslate(SkFloatToScalar(adjustedX), SkFloatToScalar(adjuste dY));
245 140
246 RefPtr<SkShader> shader; 141 // Because no resizing occurred, the shader transform should be
247 SkPaint::FilterLevel filterLevel = static_cast<SkPaint::FilterLevel>(resampl ing); 142 // set to the pattern's transform, which just includes scale.
143 localMatrix.preScale(scale.width(), scale.height());
248 144
249 // Bicubic filter is only applied to defer-decoded images, see 145 SkBitmap bitmapToPaint;
250 // NativeImageSkia::draw for details. 146 bitmap().extractSubset(&bitmapToPaint, enclosingIntRect(normSrcRect));
251 if (resampling == InterpolationHigh && !isLazyDecoded) { 147 if (!repeatSpacing.isZero()) {
252 // Do nice resampling. 148 bitmapToPaint = createBitmapWithSpace(
253 filterLevel = SkPaint::kNone_FilterLevel; 149 bitmapToPaint,
254 float scaleX = destBitmapWidth / normSrcRect.width(); 150 repeatSpacing.width() * ctmScaleX / scale.width(),
255 float scaleY = destBitmapHeight / normSrcRect.height(); 151 repeatSpacing.height() * ctmScaleY / scale.height());
256 SkRect scaledSrcRect;
257
258 // Since we are resizing the bitmap, we need to remove the scale
259 // applied to the pixels in the bitmap shader. This means we need
260 // CTM * localMatrix to have identity scale. Since we
261 // can't modify CTM (or the rectangle will be drawn in the wrong
262 // place), we must set localMatrix's scale to the inverse of
263 // CTM scale.
264 localMatrix.preScale(ctmScaleX ? 1 / ctmScaleX : 1, ctmScaleY ? 1 / ctmS caleY : 1);
265
266 // The image fragment generated here is not exactly what is
267 // requested. The scale factor used is approximated and image
268 // fragment is slightly larger to align to integer
269 // boundaries.
270 SkBitmap resampled = extractScaledImageFragment(normSrcRect, scaleX, sca leY, &scaledSrcRect);
271 if (repeatSpacing.isZero()) {
272 shader = adoptRef(SkShader::CreateBitmapShader(resampled, SkShader:: kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix));
273 } else {
274 shader = adoptRef(SkShader::CreateBitmapShader(
275 createBitmapWithSpace(resampled, repeatSpacing.width() * ctmScal eX, repeatSpacing.height() * ctmScaleY),
276 SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMa trix));
277 }
278 } else {
279 // Because no resizing occurred, the shader transform should be
280 // set to the pattern's transform, which just includes scale.
281 localMatrix.preScale(scale.width(), scale.height());
282
283 // No need to resample before drawing.
284 SkBitmap srcSubset;
285 bitmap().extractSubset(&srcSubset, enclosingIntRect(normSrcRect));
286 if (repeatSpacing.isZero()) {
287 shader = adoptRef(SkShader::CreateBitmapShader(srcSubset, SkShader:: kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix));
288 } else {
289 shader = adoptRef(SkShader::CreateBitmapShader(
290 createBitmapWithSpace(srcSubset, repeatSpacing.width() * ctmScal eX, repeatSpacing.height() * ctmScaleY),
291 SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMa trix));
292 }
293 } 152 }
153 RefPtr<SkShader> shader = adoptRef(SkShader::CreateBitmapShader(bitmapToPain t, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix));
294 154
295 SkPaint paint; 155 SkPaint paint;
296 paint.setShader(shader.get()); 156 paint.setShader(shader.get());
297 paint.setXfermodeMode(WebCoreCompositeToSkiaComposite(compositeOp, blendMode )); 157 paint.setXfermodeMode(WebCoreCompositeToSkiaComposite(compositeOp, blendMode ));
298 paint.setColorFilter(context->colorFilter()); 158 paint.setColorFilter(context->colorFilter());
299 paint.setFilterLevel(filterLevel); 159 paint.setFilterLevel(static_cast<SkPaint::FilterLevel>(resampling));
300 160
301 if (isLazyDecoded) 161 if (isLazyDecoded)
302 PlatformInstrumentation::didDrawLazyPixelRef(bitmap().getGenerationID()) ; 162 PlatformInstrumentation::didDrawLazyPixelRef(bitmap().getGenerationID()) ;
303 163
304 context->drawRect(destRect, paint); 164 context->drawRect(destRect, paint);
305 } 165 }
306 166
307 bool NativeImageSkia::shouldCacheResampling(const SkISize& scaledImageSize, cons t SkIRect& scaledImageSubset) const
308 {
309 // Check whether the requested dimensions match previous request.
310 bool matchesPreviousRequest = m_cachedImageInfo.isEqual(scaledImageSize, sca ledImageSubset);
311 if (matchesPreviousRequest)
312 ++m_resizeRequests;
313 else {
314 m_cachedImageInfo.set(scaledImageSize, scaledImageSubset);
315 m_resizeRequests = 0;
316 // Reset m_resizedImage now, because we don't distinguish
317 // between the last requested resize info and m_resizedImage's
318 // resize info.
319 m_resizedImage.reset();
320 }
321
322 // We can not cache incomplete frames. This might be a good optimization in
323 // the future, were we know how much of the frame has been decoded, so when
324 // we incrementally draw more of the image, we only have to resample the
325 // parts that are changed.
326 if (!isDataComplete())
327 return false;
328
329 // If the destination bitmap is excessively large, we'll never allow caching .
330 static const unsigned long long kLargeBitmapSize = 4096ULL * 4096ULL;
331 unsigned long long fullSize = static_cast<unsigned long long>(scaledImageSiz e.width()) * static_cast<unsigned long long>(scaledImageSize.height());
332 unsigned long long fragmentSize = static_cast<unsigned long long>(scaledImag eSubset.width()) * static_cast<unsigned long long>(scaledImageSubset.height());
333
334 if (fragmentSize > kLargeBitmapSize)
335 return false;
336
337 // If the destination bitmap is small, we'll always allow caching, since
338 // there is not very much penalty for computing it and it may come in handy.
339 static const unsigned kSmallBitmapSize = 4096;
340 if (fragmentSize <= kSmallBitmapSize)
341 return true;
342
343 // If "too many" requests have been made for this bitmap, we assume that
344 // many more will be made as well, and we'll go ahead and cache it.
345 static const int kManyRequestThreshold = 4;
346 if (m_resizeRequests >= kManyRequestThreshold)
347 return true;
348
349 // If more than 1/4 of the resized image is requested, it's worth caching.
350 return fragmentSize > fullSize / 4;
351 }
352
353 NativeImageSkia::ImageResourceInfo::ImageResourceInfo()
354 {
355 scaledImageSize.setEmpty();
356 scaledImageSubset.setEmpty();
357 }
358
359 bool NativeImageSkia::ImageResourceInfo::isEqual(const SkISize& otherScaledImage Size, const SkIRect& otherScaledImageSubset) const
360 {
361 return scaledImageSize == otherScaledImageSize && scaledImageSubset == other ScaledImageSubset;
362 }
363
364 void NativeImageSkia::ImageResourceInfo::set(const SkISize& otherScaledImageSize , const SkIRect& otherScaledImageSubset)
365 {
366 scaledImageSize = otherScaledImageSize;
367 scaledImageSubset = otherScaledImageSubset;
368 }
369
370 SkIRect NativeImageSkia::ImageResourceInfo::rectInSubset(const SkIRect& otherSca ledImageSubset)
371 {
372 if (!scaledImageSubset.contains(otherScaledImageSubset))
373 return SkIRect::MakeEmpty();
374 SkIRect subsetRect = otherScaledImageSubset;
375 subsetRect.offset(-scaledImageSubset.x(), -scaledImageSubset.y());
376 return subsetRect;
377 }
378
379 } // namespace blink 167 } // namespace blink
OLDNEW
« no previous file with comments | « Source/platform/graphics/skia/NativeImageSkia.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698