| 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
|
|
|