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

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

Issue 416543002: Move logic from NativeImageSkia to GraphicsContext and SkiaUtils (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Test regressions fixed Created 6 years, 5 months 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
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 28 matching lines...) Expand all
39 #include "platform/graphics/GraphicsContext.h" 39 #include "platform/graphics/GraphicsContext.h"
40 #include "platform/graphics/Image.h" 40 #include "platform/graphics/Image.h"
41 #include "platform/graphics/DeferredImageDecoder.h" 41 #include "platform/graphics/DeferredImageDecoder.h"
42 #include "platform/graphics/skia/SkiaUtils.h" 42 #include "platform/graphics/skia/SkiaUtils.h"
43 #include "skia/ext/image_operations.h" 43 #include "skia/ext/image_operations.h"
44 #include "third_party/skia/include/core/SkMatrix.h" 44 #include "third_party/skia/include/core/SkMatrix.h"
45 #include "third_party/skia/include/core/SkPaint.h" 45 #include "third_party/skia/include/core/SkPaint.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 <algorithm>
50 #include <math.h> 49 #include <math.h>
51 #include <limits>
52 50
53 namespace blink { 51 namespace blink {
54 52
55 static bool nearlyIntegral(float value)
56 {
57 return fabs(value - floorf(value)) < std::numeric_limits<float>::epsilon();
58 }
59
60 InterpolationQuality NativeImageSkia::computeInterpolationQuality(const SkMatrix & matrix, float srcWidth, float srcHeight, float destWidth, float destHeight) co nst
61 {
62 // The percent change below which we will not resample. This usually means
63 // an off-by-one error on the web page, and just doing nearest neighbor
64 // sampling is usually good enough.
65 const float kFractionalChangeThreshold = 0.025f;
66
67 // Images smaller than this in either direction are considered "small" and
68 // are not resampled ever (see below).
69 const int kSmallImageSizeThreshold = 8;
70
71 // The amount an image can be stretched in a single direction before we
72 // say that it is being stretched so much that it must be a line or
73 // background that doesn't need resampling.
74 const float kLargeStretch = 3.0f;
75
76 // Figure out if we should resample this image. We try to prune out some
77 // common cases where resampling won't give us anything, since it is much
78 // slower than drawing stretched.
79 float diffWidth = fabs(destWidth - srcWidth);
80 float diffHeight = fabs(destHeight - srcHeight);
81 bool widthNearlyEqual = diffWidth < std::numeric_limits<float>::epsilon();
82 bool heightNearlyEqual = diffHeight < std::numeric_limits<float>::epsilon();
83 // We don't need to resample if the source and destination are the same.
84 if (widthNearlyEqual && heightNearlyEqual)
85 return InterpolationNone;
86
87 if (srcWidth <= kSmallImageSizeThreshold
88 || srcHeight <= kSmallImageSizeThreshold
89 || destWidth <= kSmallImageSizeThreshold
90 || destHeight <= kSmallImageSizeThreshold) {
91 // Small image detected.
92
93 // Resample in the case where the new size would be non-integral.
94 // This can cause noticeable breaks in repeating patterns, except
95 // when the source image is only one pixel wide in that dimension.
96 if ((!nearlyIntegral(destWidth) && srcWidth > 1 + std::numeric_limits<fl oat>::epsilon())
97 || (!nearlyIntegral(destHeight) && srcHeight > 1 + std::numeric_limi ts<float>::epsilon()))
98 return InterpolationLow;
99
100 // Otherwise, don't resample small images. These are often used for
101 // borders and rules (think 1x1 images used to make lines).
102 return InterpolationNone;
103 }
104
105 if (srcHeight * kLargeStretch <= destHeight || srcWidth * kLargeStretch <= d estWidth) {
106 // Large image detected.
107
108 // Don't resample if it is being stretched a lot in only one direction.
109 // This is trying to catch cases where somebody has created a border
110 // (which might be large) and then is stretching it to fill some part
111 // of the page.
112 if (widthNearlyEqual || heightNearlyEqual)
113 return InterpolationNone;
114
115 // The image is growing a lot and in more than one direction. Resampling
116 // is slow and doesn't give us very much when growing a lot.
117 return InterpolationLow;
118 }
119
120 if ((diffWidth / srcWidth < kFractionalChangeThreshold)
121 && (diffHeight / srcHeight < kFractionalChangeThreshold)) {
122 // It is disappointingly common on the web for image sizes to be off by
123 // one or two pixels. We don't bother resampling if the size difference
124 // is a small fraction of the original size.
125 return InterpolationNone;
126 }
127
128 // When the image is not yet done loading, use linear. We don't cache the
129 // partially resampled images, and as they come in incrementally, it causes
130 // us to have to resample the whole thing every time.
131 if (!isDataComplete())
132 return InterpolationLow;
133
134 // Everything else gets resampled.
135 // High quality interpolation only enabled for scaling and translation.
136 if (!(matrix.getType() & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Ma sk)))
137 return InterpolationHigh;
138
139 return InterpolationLow;
140 }
141
142 static InterpolationQuality limitInterpolationQuality(GraphicsContext* context, InterpolationQuality resampling)
143 {
144 return std::min(resampling, context->imageInterpolationQuality());
145 }
146
147 static SkPaint::FilterLevel convertToSkiaFilterLevel(bool useBicubicFilter, Inte rpolationQuality resampling)
148 {
149 // FIXME: If we get rid of this special case, this function can go away enti rely.
150 if (useBicubicFilter)
151 return SkPaint::kHigh_FilterLevel;
152
153 // InterpolationHigh if useBicubicFilter is false means that we do
154 // a manual high quality resampling before drawing to Skia.
155 if (resampling == InterpolationHigh)
156 return SkPaint::kNone_FilterLevel;
157
158 return static_cast<SkPaint::FilterLevel>(resampling);
159 }
160
161 // This function is used to scale an image and extract a scaled fragment. 53 // This function is used to scale an image and extract a scaled fragment.
162 // 54 //
163 // ALGORITHM 55 // ALGORITHM
164 // 56 //
165 // Because the scaled image size has to be integers, we approximate the real 57 // Because the scaled image size has to be integers, we approximate the real
166 // scale with the following formula (only X direction is shown): 58 // scale with the following formula (only X direction is shown):
167 // 59 //
168 // scaledImageWidth = round(scaleX * imageRect.width()) 60 // scaledImageWidth = round(scaleX * imageRect.width())
169 // approximateScaleX = scaledImageWidth / imageRect.width() 61 // approximateScaleX = scaledImageWidth / imageRect.width()
170 // 62 //
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after
268 160
269 m_resizedImage = resizedImage; 161 m_resizedImage = resizedImage;
270 } 162 }
271 163
272 SkBitmap resizedSubset; 164 SkBitmap resizedSubset;
273 SkIRect resizedSubsetRect = m_cachedImageInfo.rectInSubset(scaledImageSubset ); 165 SkIRect resizedSubsetRect = m_cachedImageInfo.rectInSubset(scaledImageSubset );
274 m_resizedImage.extractSubset(&resizedSubset, resizedSubsetRect); 166 m_resizedImage.extractSubset(&resizedSubset, resizedSubsetRect);
275 return resizedSubset; 167 return resizedSubset;
276 } 168 }
277 169
278 static bool shouldDrawAntiAliased(GraphicsContext* context, const SkRect& destRe ct) 170 void NativeImageSkia::draw(
279 { 171 GraphicsContext* context,
280 if (!context->shouldAntialias()) 172 const SkRect& srcRect,
281 return false; 173 const SkRect& destRect,
282 const SkMatrix totalMatrix = context->getTotalMatrix(); 174 CompositeOperator compositeOp,
283 // Don't disable anti-aliasing if we're rotated or skewed. 175 blink::WebBlendMode blendMode) const
284 if (!totalMatrix.rectStaysRect())
285 return true;
286 // Disable anti-aliasing for scales or n*90 degree rotations.
287 // Allow to opt out of the optimization though for "hairline" geometry
288 // images - using the shouldAntialiasHairlineImages() GraphicsContext flag.
289 if (!context->shouldAntialiasHairlineImages())
290 return false;
291 // Check if the dimensions of the destination are "small" (less than one
292 // device pixel). To prevent sudden drop-outs. Since we know that
293 // kRectStaysRect_Mask is set, the matrix either has scale and no skew or
294 // vice versa. We can query the kAffine_Mask flag to determine which case
295 // it is.
296 // FIXME: This queries the CTM while drawing, which is generally
297 // discouraged. Always drawing with AA can negatively impact performance
298 // though - that's why it's not always on.
299 SkScalar widthExpansion, heightExpansion;
300 if (totalMatrix.getType() & SkMatrix::kAffine_Mask)
301 widthExpansion = totalMatrix[SkMatrix::kMSkewY], heightExpansion = total Matrix[SkMatrix::kMSkewX];
302 else
303 widthExpansion = totalMatrix[SkMatrix::kMScaleX], heightExpansion = tota lMatrix[SkMatrix::kMScaleY];
304 return destRect.width() * fabs(widthExpansion) < 1 || destRect.height() * fa bs(heightExpansion) < 1;
305 }
306
307 void NativeImageSkia::draw(GraphicsContext* context, const SkRect& srcRect, cons t SkRect& destRect, PassRefPtr<SkXfermode> compOp) const
308 { 176 {
309 TRACE_EVENT0("skia", "NativeImageSkia::draw"); 177 TRACE_EVENT0("skia", "NativeImageSkia::draw");
310 SkPaint paint;
311 paint.setXfermode(compOp.get());
312 paint.setColorFilter(context->colorFilter());
313 paint.setAlpha(context->getNormalizedAlpha());
314 paint.setLooper(context->drawLooper());
315 paint.setAntiAlias(shouldDrawAntiAliased(context, destRect));
316 178
317 bool isLazyDecoded = DeferredImageDecoder::isLazyDecoded(bitmap()); 179 bool isLazyDecoded = DeferredImageDecoder::isLazyDecoded(bitmap());
318 180
319 InterpolationQuality resampling; 181 SkPaint paint;
320 if (context->isAccelerated()) { 182 context->preparePaintForDrawRectToRect(&paint, srcRect, destRect, compositeO p, blendMode, isLazyDecoded, isDataComplete());
321 resampling = InterpolationLow;
322 } else if (context->printing()) {
323 resampling = InterpolationNone;
324 } else if (isLazyDecoded) {
325 resampling = InterpolationHigh;
326 } else {
327 // Take into account scale applied to the canvas when computing sampling mode (e.g. CSS scale or page scale).
328 SkRect destRectTarget = destRect;
329 SkMatrix totalMatrix = context->getTotalMatrix();
330 if (!(totalMatrix.getType() & (SkMatrix::kAffine_Mask | SkMatrix::kPersp ective_Mask)))
331 totalMatrix.mapRect(&destRectTarget, destRect);
332
333 resampling = computeInterpolationQuality(totalMatrix,
334 SkScalarToFloat(srcRect.width()), SkScalarToFloat(srcRect.height()),
335 SkScalarToFloat(destRectTarget.width()), SkScalarToFloat(destRectTar get.height()));
336 }
337
338 if (resampling == InterpolationNone) {
339 // FIXME: This is to not break tests (it results in the filter bitmap fl ag
340 // being set to true). We need to decide if we respect InterpolationNone
341 // being returned from computeInterpolationQuality.
342 resampling = InterpolationLow;
343 }
344 resampling = limitInterpolationQuality(context, resampling);
345
346 bool useBicubicFilter = resampling == InterpolationHigh;
347 paint.setFilterLevel(convertToSkiaFilterLevel(useBicubicFilter, resampling)) ;
348
349 // We want to filter it if we decided to do interpolation above, or if 183 // We want to filter it if we decided to do interpolation above, or if
350 // there is something interesting going on with the matrix (like a rotation) . 184 // there is something interesting going on with the matrix (like a rotation) .
351 // Note: for serialization, we will want to subset the bitmap first so we 185 // Note: for serialization, we will want to subset the bitmap first so we
352 // don't send extra pixels. 186 // don't send extra pixels.
353 context->drawBitmapRect(bitmap(), &srcRect, destRect, &paint); 187 context->drawBitmapRect(bitmap(), &srcRect, destRect, &paint);
354 188
355 if (isLazyDecoded) 189 if (isLazyDecoded)
356 PlatformInstrumentation::didDrawLazyPixelRef(bitmap().getGenerationID()) ; 190 PlatformInstrumentation::didDrawLazyPixelRef(bitmap().getGenerationID()) ;
357 context->didDrawRect(destRect, paint, &bitmap()); 191 context->didDrawRect(destRect, paint, &bitmap());
358 } 192 }
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
404 238
405 bool isLazyDecoded = DeferredImageDecoder::isLazyDecoded(bitmap()); 239 bool isLazyDecoded = DeferredImageDecoder::isLazyDecoded(bitmap());
406 240
407 // Compute the resampling mode. 241 // Compute the resampling mode.
408 InterpolationQuality resampling; 242 InterpolationQuality resampling;
409 if (context->isAccelerated() || context->printing()) 243 if (context->isAccelerated() || context->printing())
410 resampling = InterpolationLow; 244 resampling = InterpolationLow;
411 else if (isLazyDecoded) 245 else if (isLazyDecoded)
412 resampling = InterpolationHigh; 246 resampling = InterpolationHigh;
413 else 247 else
414 resampling = computeInterpolationQuality(totalMatrix, normSrcRect.width( ), normSrcRect.height(), destBitmapWidth, destBitmapHeight); 248 resampling = computeInterpolationQuality(totalMatrix, normSrcRect.width( ), normSrcRect.height(), destBitmapWidth, destBitmapHeight, isDataComplete());
415 resampling = limitInterpolationQuality(context, resampling); 249 resampling = limitInterpolationQuality(context, resampling);
416 250
417 SkMatrix localMatrix; 251 SkMatrix localMatrix;
418 // We also need to translate it such that the origin of the pattern is the 252 // We also need to translate it such that the origin of the pattern is the
419 // origin of the destination rect, which is what WebKit expects. Skia uses 253 // origin of the destination rect, which is what WebKit expects. Skia uses
420 // the coordinate system origin as the base for the pattern. If WebKit wants 254 // the coordinate system origin as the base for the pattern. If WebKit wants
421 // a shifted image, it will shift it from there using the localMatrix. 255 // a shifted image, it will shift it from there using the localMatrix.
422 const float adjustedX = phase.x() + normSrcRect.x() * scale.width(); 256 const float adjustedX = phase.x() + normSrcRect.x() * scale.width();
423 const float adjustedY = phase.y() + normSrcRect.y() * scale.height(); 257 const float adjustedY = phase.y() + normSrcRect.y() * scale.height();
424 localMatrix.setTranslate(SkFloatToScalar(adjustedX), SkFloatToScalar(adjuste dY)); 258 localMatrix.setTranslate(SkFloatToScalar(adjustedX), SkFloatToScalar(adjuste dY));
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after
550 SkIRect NativeImageSkia::ImageResourceInfo::rectInSubset(const SkIRect& otherSca ledImageSubset) 384 SkIRect NativeImageSkia::ImageResourceInfo::rectInSubset(const SkIRect& otherSca ledImageSubset)
551 { 385 {
552 if (!scaledImageSubset.contains(otherScaledImageSubset)) 386 if (!scaledImageSubset.contains(otherScaledImageSubset))
553 return SkIRect::MakeEmpty(); 387 return SkIRect::MakeEmpty();
554 SkIRect subsetRect = otherScaledImageSubset; 388 SkIRect subsetRect = otherScaledImageSubset;
555 subsetRect.offset(-scaledImageSubset.x(), -scaledImageSubset.y()); 389 subsetRect.offset(-scaledImageSubset.x(), -scaledImageSubset.y());
556 return subsetRect; 390 return subsetRect;
557 } 391 }
558 392
559 } // namespace blink 393 } // namespace blink
OLDNEW
« no previous file with comments | « Source/platform/graphics/skia/NativeImageSkia.h ('k') | Source/platform/graphics/skia/SkiaUtils.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698