Index: Source/platform/graphics/skia/NativeImageSkia.cpp |
diff --git a/Source/platform/graphics/skia/NativeImageSkia.cpp b/Source/platform/graphics/skia/NativeImageSkia.cpp |
index 5d1be831229c9ee663588d7d42b2d8b6affbada7..ad09f191f0e97de08fb915392592e136808a6b82 100644 |
--- a/Source/platform/graphics/skia/NativeImageSkia.cpp |
+++ b/Source/platform/graphics/skia/NativeImageSkia.cpp |
@@ -33,127 +33,21 @@ |
#include "platform/PlatformInstrumentation.h" |
#include "platform/TraceEvent.h" |
-#include "platform/geometry/FloatPoint.h" |
#include "platform/geometry/FloatRect.h" |
-#include "platform/geometry/FloatSize.h" |
#include "platform/graphics/DeferredImageDecoder.h" |
#include "platform/graphics/GraphicsContext.h" |
-#include "platform/graphics/Image.h" |
#include "platform/graphics/skia/SkiaUtils.h" |
-#include "skia/ext/image_operations.h" |
+#include "platform/transforms/AffineTransform.h" |
+#include "third_party/skia/include/core/SkColor.h" |
+#include "third_party/skia/include/core/SkImageInfo.h" |
#include "third_party/skia/include/core/SkMatrix.h" |
#include "third_party/skia/include/core/SkPaint.h" |
+#include "third_party/skia/include/core/SkRect.h" |
#include "third_party/skia/include/core/SkScalar.h" |
#include "third_party/skia/include/core/SkShader.h" |
-#include <math.h> |
- |
namespace blink { |
-// This function is used to scale an image and extract a scaled fragment. |
-// |
-// ALGORITHM |
-// |
-// Because the scaled image size has to be integers, we approximate the real |
-// scale with the following formula (only X direction is shown): |
-// |
-// scaledImageWidth = round(scaleX * imageRect.width()) |
-// approximateScaleX = scaledImageWidth / imageRect.width() |
-// |
-// With this method we maintain a constant scale factor among fragments in |
-// the scaled image. This allows fragments to stitch together to form the |
-// full scaled image. The downside is there will be a small difference |
-// between |scaleX| and |approximateScaleX|. |
-// |
-// A scaled image fragment is identified by: |
-// |
-// - Scaled image size |
-// - Scaled image fragment rectangle (IntRect) |
-// |
-// Scaled image size has been determined and the next step is to compute the |
-// rectangle for the scaled image fragment which needs to be an IntRect. |
-// |
-// scaledSrcRect = srcRect * (approximateScaleX, approximateScaleY) |
-// enclosingScaledSrcRect = enclosingIntRect(scaledSrcRect) |
-// |
-// Finally we extract the scaled image fragment using |
-// (scaledImageSize, enclosingScaledSrcRect). |
-// |
-SkBitmap NativeImageSkia::extractScaledImageFragment(const SkRect& srcRect, float scaleX, float scaleY, SkRect* scaledSrcRect) const |
-{ |
- SkISize imageSize = SkISize::Make(bitmap().width(), bitmap().height()); |
- SkISize scaledImageSize = SkISize::Make(clampTo<int>(roundf(imageSize.width() * scaleX)), |
- clampTo<int>(roundf(imageSize.height() * scaleY))); |
- |
- SkRect imageRect = SkRect::MakeWH(imageSize.width(), imageSize.height()); |
- SkRect scaledImageRect = SkRect::MakeWH(scaledImageSize.width(), scaledImageSize.height()); |
- |
- SkMatrix scaleTransform; |
- scaleTransform.setRectToRect(imageRect, scaledImageRect, SkMatrix::kFill_ScaleToFit); |
- scaleTransform.mapRect(scaledSrcRect, srcRect); |
- |
- scaledSrcRect->intersect(scaledImageRect); |
- SkIRect enclosingScaledSrcRect = enclosingIntRect(*scaledSrcRect); |
- |
- // |enclosingScaledSrcRect| can be larger than |scaledImageSize| because |
- // of float inaccuracy so clip to get inside. |
- enclosingScaledSrcRect.intersect(SkIRect::MakeSize(scaledImageSize)); |
- |
- // scaledSrcRect is relative to the pixel snapped fragment we're extracting. |
- scaledSrcRect->offset(-enclosingScaledSrcRect.x(), -enclosingScaledSrcRect.y()); |
- |
- return resizedBitmap(scaledImageSize, enclosingScaledSrcRect); |
-} |
- |
-NativeImageSkia::NativeImageSkia() |
- : m_resizeRequests(0) |
-{ |
-} |
- |
-NativeImageSkia::NativeImageSkia(const SkBitmap& other) |
- : m_bitmap(other) |
- , m_resizeRequests(0) |
-{ |
-} |
- |
-NativeImageSkia::~NativeImageSkia() |
-{ |
-} |
- |
-bool NativeImageSkia::hasResizedBitmap(const SkISize& scaledImageSize, const SkIRect& scaledImageSubset) const |
-{ |
- bool imageScaleEqual = m_cachedImageInfo.scaledImageSize == scaledImageSize; |
- bool scaledImageSubsetAvailable = m_cachedImageInfo.scaledImageSubset.contains(scaledImageSubset); |
- return imageScaleEqual && scaledImageSubsetAvailable && !m_resizedImage.empty(); |
-} |
- |
-SkBitmap NativeImageSkia::resizedBitmap(const SkISize& scaledImageSize, const SkIRect& scaledImageSubset) const |
-{ |
- ASSERT(!DeferredImageDecoder::isLazyDecoded(bitmap())); |
- |
- if (!hasResizedBitmap(scaledImageSize, scaledImageSubset)) { |
- bool shouldCache = isDataComplete() |
- && shouldCacheResampling(scaledImageSize, scaledImageSubset); |
- |
- TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "ResizeImage", "cached", shouldCache); |
- // FIXME(361045): remove InspectorInstrumentation calls once DevTools Timeline migrates to tracing. |
- PlatformInstrumentation::willResizeImage(shouldCache); |
- SkBitmap resizedImage = skia::ImageOperations::Resize(bitmap(), skia::ImageOperations::RESIZE_LANCZOS3, scaledImageSize.width(), scaledImageSize.height(), scaledImageSubset); |
- resizedImage.setImmutable(); |
- PlatformInstrumentation::didResizeImage(); |
- |
- if (!shouldCache) |
- return resizedImage; |
- |
- m_resizedImage = resizedImage; |
- } |
- |
- SkBitmap resizedSubset; |
- SkIRect resizedSubsetRect = m_cachedImageInfo.rectInSubset(scaledImageSubset); |
- m_resizedImage.extractSubset(&resizedSubset, resizedSubsetRect); |
- return resizedSubset; |
-} |
- |
void NativeImageSkia::draw( |
GraphicsContext* context, |
const SkRect& srcRect, |
@@ -235,6 +129,7 @@ void NativeImageSkia::drawPattern( |
resampling = limitInterpolationQuality(context, resampling); |
SkMatrix localMatrix; |
+ |
// We also need to translate it such that the origin of the pattern is the |
// origin of the destination rect, which is what WebKit expects. Skia uses |
// the coordinate system origin as the base for the pattern. If WebKit wants |
@@ -243,60 +138,25 @@ void NativeImageSkia::drawPattern( |
const float adjustedY = phase.y() + normSrcRect.y() * scale.height(); |
localMatrix.setTranslate(SkFloatToScalar(adjustedX), SkFloatToScalar(adjustedY)); |
- RefPtr<SkShader> shader; |
- SkPaint::FilterLevel filterLevel = static_cast<SkPaint::FilterLevel>(resampling); |
- |
- // Bicubic filter is only applied to defer-decoded images, see |
- // NativeImageSkia::draw for details. |
- if (resampling == InterpolationHigh && !isLazyDecoded) { |
- // Do nice resampling. |
- filterLevel = SkPaint::kNone_FilterLevel; |
- float scaleX = destBitmapWidth / normSrcRect.width(); |
- float scaleY = destBitmapHeight / normSrcRect.height(); |
- SkRect scaledSrcRect; |
- |
- // Since we are resizing the bitmap, we need to remove the scale |
- // applied to the pixels in the bitmap shader. This means we need |
- // CTM * localMatrix to have identity scale. Since we |
- // can't modify CTM (or the rectangle will be drawn in the wrong |
- // place), we must set localMatrix's scale to the inverse of |
- // CTM scale. |
- localMatrix.preScale(ctmScaleX ? 1 / ctmScaleX : 1, ctmScaleY ? 1 / ctmScaleY : 1); |
- |
- // The image fragment generated here is not exactly what is |
- // requested. The scale factor used is approximated and image |
- // fragment is slightly larger to align to integer |
- // boundaries. |
- SkBitmap resampled = extractScaledImageFragment(normSrcRect, scaleX, scaleY, &scaledSrcRect); |
- if (repeatSpacing.isZero()) { |
- shader = adoptRef(SkShader::CreateBitmapShader(resampled, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix)); |
- } else { |
- shader = adoptRef(SkShader::CreateBitmapShader( |
- createBitmapWithSpace(resampled, repeatSpacing.width() * ctmScaleX, repeatSpacing.height() * ctmScaleY), |
- SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix)); |
- } |
- } else { |
- // Because no resizing occurred, the shader transform should be |
- // set to the pattern's transform, which just includes scale. |
- localMatrix.preScale(scale.width(), scale.height()); |
- |
- // No need to resample before drawing. |
- SkBitmap srcSubset; |
- bitmap().extractSubset(&srcSubset, enclosingIntRect(normSrcRect)); |
- if (repeatSpacing.isZero()) { |
- shader = adoptRef(SkShader::CreateBitmapShader(srcSubset, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix)); |
- } else { |
- shader = adoptRef(SkShader::CreateBitmapShader( |
- createBitmapWithSpace(srcSubset, repeatSpacing.width() * ctmScaleX, repeatSpacing.height() * ctmScaleY), |
- SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix)); |
- } |
+ // Because no resizing occurred, the shader transform should be |
+ // set to the pattern's transform, which just includes scale. |
+ localMatrix.preScale(scale.width(), scale.height()); |
+ |
+ SkBitmap bitmapToPaint; |
+ bitmap().extractSubset(&bitmapToPaint, enclosingIntRect(normSrcRect)); |
+ if (!repeatSpacing.isZero()) { |
+ bitmapToPaint = createBitmapWithSpace( |
+ bitmapToPaint, |
+ repeatSpacing.width() * ctmScaleX / scale.width(), |
+ repeatSpacing.height() * ctmScaleY / scale.height()); |
} |
+ RefPtr<SkShader> shader = adoptRef(SkShader::CreateBitmapShader(bitmapToPaint, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix)); |
SkPaint paint; |
paint.setShader(shader.get()); |
paint.setXfermodeMode(WebCoreCompositeToSkiaComposite(compositeOp, blendMode)); |
paint.setColorFilter(context->colorFilter()); |
- paint.setFilterLevel(filterLevel); |
+ paint.setFilterLevel(static_cast<SkPaint::FilterLevel>(resampling)); |
if (isLazyDecoded) |
PlatformInstrumentation::didDrawLazyPixelRef(bitmap().getGenerationID()); |
@@ -304,76 +164,4 @@ void NativeImageSkia::drawPattern( |
context->drawRect(destRect, paint); |
} |
-bool NativeImageSkia::shouldCacheResampling(const SkISize& scaledImageSize, const SkIRect& scaledImageSubset) const |
-{ |
- // Check whether the requested dimensions match previous request. |
- bool matchesPreviousRequest = m_cachedImageInfo.isEqual(scaledImageSize, scaledImageSubset); |
- if (matchesPreviousRequest) |
- ++m_resizeRequests; |
- else { |
- m_cachedImageInfo.set(scaledImageSize, scaledImageSubset); |
- m_resizeRequests = 0; |
- // Reset m_resizedImage now, because we don't distinguish |
- // between the last requested resize info and m_resizedImage's |
- // resize info. |
- m_resizedImage.reset(); |
- } |
- |
- // We can not cache incomplete frames. This might be a good optimization in |
- // the future, were we know how much of the frame has been decoded, so when |
- // we incrementally draw more of the image, we only have to resample the |
- // parts that are changed. |
- if (!isDataComplete()) |
- return false; |
- |
- // If the destination bitmap is excessively large, we'll never allow caching. |
- static const unsigned long long kLargeBitmapSize = 4096ULL * 4096ULL; |
- unsigned long long fullSize = static_cast<unsigned long long>(scaledImageSize.width()) * static_cast<unsigned long long>(scaledImageSize.height()); |
- unsigned long long fragmentSize = static_cast<unsigned long long>(scaledImageSubset.width()) * static_cast<unsigned long long>(scaledImageSubset.height()); |
- |
- if (fragmentSize > kLargeBitmapSize) |
- return false; |
- |
- // If the destination bitmap is small, we'll always allow caching, since |
- // there is not very much penalty for computing it and it may come in handy. |
- static const unsigned kSmallBitmapSize = 4096; |
- if (fragmentSize <= kSmallBitmapSize) |
- return true; |
- |
- // If "too many" requests have been made for this bitmap, we assume that |
- // many more will be made as well, and we'll go ahead and cache it. |
- static const int kManyRequestThreshold = 4; |
- if (m_resizeRequests >= kManyRequestThreshold) |
- return true; |
- |
- // If more than 1/4 of the resized image is requested, it's worth caching. |
- return fragmentSize > fullSize / 4; |
-} |
- |
-NativeImageSkia::ImageResourceInfo::ImageResourceInfo() |
-{ |
- scaledImageSize.setEmpty(); |
- scaledImageSubset.setEmpty(); |
-} |
- |
-bool NativeImageSkia::ImageResourceInfo::isEqual(const SkISize& otherScaledImageSize, const SkIRect& otherScaledImageSubset) const |
-{ |
- return scaledImageSize == otherScaledImageSize && scaledImageSubset == otherScaledImageSubset; |
-} |
- |
-void NativeImageSkia::ImageResourceInfo::set(const SkISize& otherScaledImageSize, const SkIRect& otherScaledImageSubset) |
-{ |
- scaledImageSize = otherScaledImageSize; |
- scaledImageSubset = otherScaledImageSubset; |
-} |
- |
-SkIRect NativeImageSkia::ImageResourceInfo::rectInSubset(const SkIRect& otherScaledImageSubset) |
-{ |
- if (!scaledImageSubset.contains(otherScaledImageSubset)) |
- return SkIRect::MakeEmpty(); |
- SkIRect subsetRect = otherScaledImageSubset; |
- subsetRect.offset(-scaledImageSubset.x(), -scaledImageSubset.y()); |
- return subsetRect; |
-} |
- |
} // namespace blink |