blob: b183a4e69aabfb10c1fc60586062231e0b54db4c [file] [log] [blame]
/*
* Copyright (c) 2006,2007,2008, Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "platform/graphics/skia/SkiaUtils.h"
#include "platform/graphics/GraphicsContext.h"
#include "third_party/skia/include/effects/SkCornerPathEffect.h"
namespace blink {
static const struct CompositOpToXfermodeMode {
CompositeOperator mCompositOp;
SkXfermode::Mode m_xfermodeMode;
} gMapCompositOpsToXfermodeModes[] = {
{CompositeClear, SkXfermode::kClear_Mode},
{CompositeCopy, SkXfermode::kSrc_Mode},
{CompositeSourceOver, SkXfermode::kSrcOver_Mode},
{CompositeSourceIn, SkXfermode::kSrcIn_Mode},
{CompositeSourceOut, SkXfermode::kSrcOut_Mode},
{CompositeSourceAtop, SkXfermode::kSrcATop_Mode},
{CompositeDestinationOver, SkXfermode::kDstOver_Mode},
{CompositeDestinationIn, SkXfermode::kDstIn_Mode},
{CompositeDestinationOut, SkXfermode::kDstOut_Mode},
{CompositeDestinationAtop, SkXfermode::kDstATop_Mode},
{CompositeXOR, SkXfermode::kXor_Mode},
{CompositePlusLighter, SkXfermode::kPlus_Mode}};
// Keep this array in sync with the WebBlendMode enum in
// public/platform/WebBlendMode.h.
static const SkXfermode::Mode gMapBlendOpsToXfermodeModes[] = {
SkXfermode::kClear_Mode, // WebBlendModeNormal
SkXfermode::kMultiply_Mode, // WebBlendModeMultiply
SkXfermode::kScreen_Mode, // WebBlendModeScreen
SkXfermode::kOverlay_Mode, // WebBlendModeOverlay
SkXfermode::kDarken_Mode, // WebBlendModeDarken
SkXfermode::kLighten_Mode, // WebBlendModeLighten
SkXfermode::kColorDodge_Mode, // WebBlendModeColorDodge
SkXfermode::kColorBurn_Mode, // WebBlendModeColorBurn
SkXfermode::kHardLight_Mode, // WebBlendModeHardLight
SkXfermode::kSoftLight_Mode, // WebBlendModeSoftLight
SkXfermode::kDifference_Mode, // WebBlendModeDifference
SkXfermode::kExclusion_Mode, // WebBlendModeExclusion
SkXfermode::kHue_Mode, // WebBlendModeHue
SkXfermode::kSaturation_Mode, // WebBlendModeSaturation
SkXfermode::kColor_Mode, // WebBlendModeColor
SkXfermode::kLuminosity_Mode // WebBlendModeLuminosity
};
SkXfermode::Mode WebCoreCompositeToSkiaComposite(CompositeOperator op,
WebBlendMode blendMode) {
ASSERT(op == CompositeSourceOver || blendMode == WebBlendModeNormal);
if (blendMode != WebBlendModeNormal) {
if (static_cast<uint8_t>(blendMode) >=
SK_ARRAY_COUNT(gMapBlendOpsToXfermodeModes)) {
SkDEBUGF(
("GraphicsContext::setPlatformCompositeOperation unknown "
"WebBlendMode %d\n",
blendMode));
return SkXfermode::kSrcOver_Mode;
}
return gMapBlendOpsToXfermodeModes[static_cast<uint8_t>(blendMode)];
}
const CompositOpToXfermodeMode* table = gMapCompositOpsToXfermodeModes;
if (static_cast<uint8_t>(op) >=
SK_ARRAY_COUNT(gMapCompositOpsToXfermodeModes)) {
SkDEBUGF(
("GraphicsContext::setPlatformCompositeOperation unknown "
"CompositeOperator %d\n",
op));
return SkXfermode::kSrcOver_Mode;
}
SkASSERT(table[static_cast<uint8_t>(op)].mCompositOp == op);
return table[static_cast<uint8_t>(op)].m_xfermodeMode;
}
CompositeOperator compositeOperatorFromSkia(SkXfermode::Mode xferMode) {
switch (xferMode) {
case SkXfermode::kClear_Mode:
return CompositeClear;
case SkXfermode::kSrc_Mode:
return CompositeCopy;
case SkXfermode::kSrcOver_Mode:
return CompositeSourceOver;
case SkXfermode::kSrcIn_Mode:
return CompositeSourceIn;
case SkXfermode::kSrcOut_Mode:
return CompositeSourceOut;
case SkXfermode::kSrcATop_Mode:
return CompositeSourceAtop;
case SkXfermode::kDstOver_Mode:
return CompositeDestinationOver;
case SkXfermode::kDstIn_Mode:
return CompositeDestinationIn;
case SkXfermode::kDstOut_Mode:
return CompositeDestinationOut;
case SkXfermode::kDstATop_Mode:
return CompositeDestinationAtop;
case SkXfermode::kXor_Mode:
return CompositeXOR;
case SkXfermode::kPlus_Mode:
return CompositePlusLighter;
default:
break;
}
return CompositeSourceOver;
}
WebBlendMode blendModeFromSkia(SkXfermode::Mode xferMode) {
switch (xferMode) {
case SkXfermode::kSrcOver_Mode:
return WebBlendModeNormal;
case SkXfermode::kMultiply_Mode:
return WebBlendModeMultiply;
case SkXfermode::kScreen_Mode:
return WebBlendModeScreen;
case SkXfermode::kOverlay_Mode:
return WebBlendModeOverlay;
case SkXfermode::kDarken_Mode:
return WebBlendModeDarken;
case SkXfermode::kLighten_Mode:
return WebBlendModeLighten;
case SkXfermode::kColorDodge_Mode:
return WebBlendModeColorDodge;
case SkXfermode::kColorBurn_Mode:
return WebBlendModeColorBurn;
case SkXfermode::kHardLight_Mode:
return WebBlendModeHardLight;
case SkXfermode::kSoftLight_Mode:
return WebBlendModeSoftLight;
case SkXfermode::kDifference_Mode:
return WebBlendModeDifference;
case SkXfermode::kExclusion_Mode:
return WebBlendModeExclusion;
case SkXfermode::kHue_Mode:
return WebBlendModeHue;
case SkXfermode::kSaturation_Mode:
return WebBlendModeSaturation;
case SkXfermode::kColor_Mode:
return WebBlendModeColor;
case SkXfermode::kLuminosity_Mode:
return WebBlendModeLuminosity;
default:
break;
}
return WebBlendModeNormal;
}
SkMatrix affineTransformToSkMatrix(const AffineTransform& source) {
SkMatrix result;
result.setScaleX(WebCoreDoubleToSkScalar(source.a()));
result.setSkewX(WebCoreDoubleToSkScalar(source.c()));
result.setTranslateX(WebCoreDoubleToSkScalar(source.e()));
result.setScaleY(WebCoreDoubleToSkScalar(source.d()));
result.setSkewY(WebCoreDoubleToSkScalar(source.b()));
result.setTranslateY(WebCoreDoubleToSkScalar(source.f()));
// FIXME: Set perspective properly.
result.setPerspX(0);
result.setPerspY(0);
result.set(SkMatrix::kMPersp2, SK_Scalar1);
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());
}
InterpolationQuality computeInterpolationQuality(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 at high quality.
return InterpolationHigh;
}
int clampedAlphaForBlending(float alpha) {
if (alpha < 0)
return 0;
int roundedAlpha = roundf(alpha * 256);
if (roundedAlpha > 256)
roundedAlpha = 256;
return roundedAlpha;
}
SkColor scaleAlpha(SkColor color, float alpha) {
return scaleAlpha(color, clampedAlphaForBlending(alpha));
}
SkColor scaleAlpha(SkColor color, int alpha) {
int a = (SkColorGetA(color) * alpha) >> 8;
return (color & 0x00FFFFFF) | (a << 24);
}
template <typename PrimitiveType>
void drawFocusRingPrimitive(const PrimitiveType&,
SkCanvas*,
const SkPaint&,
float cornerRadius) {
ASSERT_NOT_REACHED(); // Missing an explicit specialization?
}
template <>
void drawFocusRingPrimitive<SkRect>(const SkRect& rect,
SkCanvas* canvas,
const SkPaint& paint,
float cornerRadius) {
SkRRect rrect;
rrect.setRectXY(rect, SkFloatToScalar(cornerRadius),
SkFloatToScalar(cornerRadius));
canvas->drawRRect(rrect, paint);
}
template <>
void drawFocusRingPrimitive<SkPath>(const SkPath& path,
SkCanvas* canvas,
const SkPaint& paint,
float cornerRadius) {
SkPaint pathPaint = paint;
pathPaint.setPathEffect(
SkCornerPathEffect::Make(SkFloatToScalar(cornerRadius)));
canvas->drawPath(path, pathPaint);
}
template <typename PrimitiveType>
void drawPlatformFocusRing(const PrimitiveType& primitive,
SkCanvas* canvas,
SkColor color,
int width) {
SkPaint paint;
paint.setAntiAlias(true);
paint.setStyle(SkPaint::kStroke_Style);
paint.setColor(color);
paint.setStrokeWidth(GraphicsContext::focusRingWidth(width));
#if OS(MACOSX)
paint.setAlpha(64);
const float cornerRadius = (width - 1) * 0.5f;
#else
const float cornerRadius = 1;
#endif
drawFocusRingPrimitive(primitive, canvas, paint, cornerRadius);
#if OS(MACOSX)
// Inner part
paint.setAlpha(128);
paint.setStrokeWidth(paint.getStrokeWidth() * 0.5f);
drawFocusRingPrimitive(primitive, canvas, paint, cornerRadius);
#endif
}
template void PLATFORM_EXPORT drawPlatformFocusRing<SkRect>(const SkRect&,
SkCanvas*,
SkColor,
int width);
template void PLATFORM_EXPORT drawPlatformFocusRing<SkPath>(const SkPath&,
SkCanvas*,
SkColor,
int width);
} // namespace blink