Index: Source/platform/graphics/skia/SkiaUtils.cpp |
diff --git a/Source/platform/graphics/skia/SkiaUtils.cpp b/Source/platform/graphics/skia/SkiaUtils.cpp |
index a6090712e64c3453bd0f299b3520095bffd0ae19..8391b1f18a0d6ebc76a6a7f5f39beeb7eb58bc36 100644 |
--- a/Source/platform/graphics/skia/SkiaUtils.cpp |
+++ b/Source/platform/graphics/skia/SkiaUtils.cpp |
@@ -189,4 +189,146 @@ SkMatrix affineTransformToSkMatrix(const AffineTransform& source) |
return result; |
} |
+bool nearlyIntegral(float value) |
+{ |
+ return fabs(value - floorf(value)) < std::numeric_limits<float>::epsilon(); |
+} |
+ |
+InterpolationQuality limitInterpolationQuality(const GraphicsContext* context, InterpolationQuality resampling) |
+{ |
+ return std::min(resampling, context->imageInterpolationQuality()); |
+} |
+ |
+SkPaint::FilterLevel convertToSkiaFilterLevel(bool useBicubicFilter, InterpolationQuality resampling) |
+{ |
+ // FIXME: If we get rid of this special case, this function can go away entirely. |
+ if (useBicubicFilter) |
+ return SkPaint::kHigh_FilterLevel; |
+ |
+ // InterpolationHigh if useBicubicFilter is false means that we do |
+ // a manual high quality resampling before drawing to Skia. |
+ if (resampling == InterpolationHigh) |
+ return SkPaint::kNone_FilterLevel; |
+ |
+ return static_cast<SkPaint::FilterLevel>(resampling); |
+} |
+ |
+InterpolationQuality computeInterpolationQuality( |
+ const SkMatrix& matrix, |
+ float srcWidth, |
+ float srcHeight, |
+ float destWidth, |
+ float destHeight, |
+ bool isDataComplete) |
+{ |
+ // The percent change below which we will not resample. This usually means |
+ // an off-by-one error on the web page, and just doing nearest neighbor |
+ // sampling is usually good enough. |
+ const float kFractionalChangeThreshold = 0.025f; |
+ |
+ // Images smaller than this in either direction are considered "small" and |
+ // are not resampled ever (see below). |
+ const int kSmallImageSizeThreshold = 8; |
+ |
+ // The amount an image can be stretched in a single direction before we |
+ // say that it is being stretched so much that it must be a line or |
+ // background that doesn't need resampling. |
+ const float kLargeStretch = 3.0f; |
+ |
+ // Figure out if we should resample this image. We try to prune out some |
+ // common cases where resampling won't give us anything, since it is much |
+ // slower than drawing stretched. |
+ float diffWidth = fabs(destWidth - srcWidth); |
+ float diffHeight = fabs(destHeight - srcHeight); |
+ bool widthNearlyEqual = diffWidth < std::numeric_limits<float>::epsilon(); |
+ bool heightNearlyEqual = diffHeight < std::numeric_limits<float>::epsilon(); |
+ // We don't need to resample if the source and destination are the same. |
+ if (widthNearlyEqual && heightNearlyEqual) |
+ return InterpolationNone; |
+ |
+ if (srcWidth <= kSmallImageSizeThreshold |
+ || srcHeight <= kSmallImageSizeThreshold |
+ || destWidth <= kSmallImageSizeThreshold |
+ || destHeight <= kSmallImageSizeThreshold) { |
+ // Small image detected. |
+ |
+ // Resample in the case where the new size would be non-integral. |
+ // This can cause noticeable breaks in repeating patterns, except |
+ // when the source image is only one pixel wide in that dimension. |
+ if ((!nearlyIntegral(destWidth) && srcWidth > 1 + std::numeric_limits<float>::epsilon()) |
+ || (!nearlyIntegral(destHeight) && srcHeight > 1 + std::numeric_limits<float>::epsilon())) |
+ return InterpolationLow; |
+ |
+ // Otherwise, don't resample small images. These are often used for |
+ // borders and rules (think 1x1 images used to make lines). |
+ return InterpolationNone; |
+ } |
+ |
+ if (srcHeight * kLargeStretch <= destHeight || srcWidth * kLargeStretch <= destWidth) { |
+ // Large image detected. |
+ |
+ // Don't resample if it is being stretched a lot in only one direction. |
+ // This is trying to catch cases where somebody has created a border |
+ // (which might be large) and then is stretching it to fill some part |
+ // of the page. |
+ if (widthNearlyEqual || heightNearlyEqual) |
+ return InterpolationNone; |
+ |
+ // The image is growing a lot and in more than one direction. Resampling |
+ // is slow and doesn't give us very much when growing a lot. |
+ return InterpolationLow; |
+ } |
+ |
+ if ((diffWidth / srcWidth < kFractionalChangeThreshold) |
+ && (diffHeight / srcHeight < kFractionalChangeThreshold)) { |
+ // It is disappointingly common on the web for image sizes to be off by |
+ // one or two pixels. We don't bother resampling if the size difference |
+ // is a small fraction of the original size. |
+ return InterpolationNone; |
+ } |
+ |
+ // When the image is not yet done loading, use linear. We don't cache the |
+ // partially resampled images, and as they come in incrementally, it causes |
+ // us to have to resample the whole thing every time. |
+ if (!isDataComplete) |
+ return InterpolationLow; |
+ |
+ // Everything else gets resampled. |
+ // High quality interpolation only enabled for scaling and translation. |
+ if (!(matrix.getType() & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask))) |
+ return InterpolationHigh; |
+ |
+ return InterpolationLow; |
+} |
+ |
+ |
+bool shouldDrawAntiAliased(const GraphicsContext* context, const SkRect& destRect) |
+{ |
+ if (!context->shouldAntialias()) |
+ return false; |
+ const SkMatrix totalMatrix = context->getTotalMatrix(); |
+ // Don't disable anti-aliasing if we're rotated or skewed. |
+ if (!totalMatrix.rectStaysRect()) |
+ return true; |
+ // Disable anti-aliasing for scales or n*90 degree rotations. |
+ // Allow to opt out of the optimization though for "hairline" geometry |
+ // images - using the shouldAntialiasHairlineImages() GraphicsContext flag. |
+ if (!context->shouldAntialiasHairlineImages()) |
+ return false; |
+ // Check if the dimensions of the destination are "small" (less than one |
+ // device pixel). To prevent sudden drop-outs. Since we know that |
+ // kRectStaysRect_Mask is set, the matrix either has scale and no skew or |
+ // vice versa. We can query the kAffine_Mask flag to determine which case |
+ // it is. |
+ // FIXME: This queries the CTM while drawing, which is generally |
+ // discouraged. Always drawing with AA can negatively impact performance |
+ // though - that's why it's not always on. |
+ SkScalar widthExpansion, heightExpansion; |
+ if (totalMatrix.getType() & SkMatrix::kAffine_Mask) |
+ widthExpansion = totalMatrix[SkMatrix::kMSkewY], heightExpansion = totalMatrix[SkMatrix::kMSkewX]; |
+ else |
+ widthExpansion = totalMatrix[SkMatrix::kMScaleX], heightExpansion = totalMatrix[SkMatrix::kMScaleY]; |
+ return destRect.width() * fabs(widthExpansion) < 1 || destRect.height() * fabs(heightExpansion) < 1; |
+} |
+ |
} // namespace blink |