Michael Goderbauer 08961f8ec5 Format all c-like sources with clang-format (#4088)
* format

* license script adaptions

* updated licenses

* review comments
2017-09-12 15:36:20 -07:00

330 lines
13 KiB
C++

/*
* 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 "flutter/sky/engine/platform/graphics/skia/SkiaUtils.h"
#include "flutter/sky/engine/platform/graphics/GraphicsContext.h"
#include "third_party/skia/include/core/SkColorPriv.h"
#include "third_party/skia/include/core/SkRegion.h"
namespace blink {
static const struct CompositOpToXfermodeMode {
CompositeOperator mCompositOp;
SkBlendMode m_xfermodeMode;
} gMapCompositOpsToXfermodeModes[] = {
{CompositeClear, SkBlendMode::kClear},
{CompositeCopy, SkBlendMode::kSrc},
{CompositeSourceOver, SkBlendMode::kSrcOver},
{CompositeSourceIn, SkBlendMode::kSrcIn},
{CompositeSourceOut, SkBlendMode::kSrcOut},
{CompositeSourceAtop, SkBlendMode::kSrcATop},
{CompositeDestinationOver, SkBlendMode::kDstOver},
{CompositeDestinationIn, SkBlendMode::kDstIn},
{CompositeDestinationOut, SkBlendMode::kDstOut},
{CompositeDestinationAtop, SkBlendMode::kDstATop},
{CompositeXOR, SkBlendMode::kXor},
{CompositePlusDarker, SkBlendMode::kDarken},
{CompositePlusLighter, SkBlendMode::kPlus}};
// keep this array in sync with WebBlendMode enum in
// public/platform/WebBlendMode.h
static const SkBlendMode gMapBlendOpsToXfermodeModes[] = {
SkBlendMode::kClear, // WebBlendModeNormal
SkBlendMode::kMultiply, // WebBlendModeMultiply
SkBlendMode::kScreen, // WebBlendModeScreen
SkBlendMode::kOverlay, // WebBlendModeOverlay
SkBlendMode::kDarken, // WebBlendModeDarken
SkBlendMode::kLighten, // WebBlendModeLighten
SkBlendMode::kColorDodge, // WebBlendModeColorDodge
SkBlendMode::kColorBurn, // WebBlendModeColorBurn
SkBlendMode::kHardLight, // WebBlendModeHardLight
SkBlendMode::kSoftLight, // WebBlendModeSoftLight
SkBlendMode::kDifference, // WebBlendModeDifference
SkBlendMode::kExclusion, // WebBlendModeExclusion
SkBlendMode::kHue, // WebBlendModeHue
SkBlendMode::kSaturation, // WebBlendModeSaturation
SkBlendMode::kColor, // WebBlendModeColor
SkBlendMode::kLuminosity // WebBlendModeLuminosity
};
SkBlendMode WebCoreCompositeToSkiaComposite(CompositeOperator op,
WebBlendMode blendMode) {
if (blendMode != WebBlendModeNormal) {
if (static_cast<uint8_t>(blendMode) >=
SK_ARRAY_COUNT(gMapBlendOpsToXfermodeModes)) {
SkDEBUGF(
("GraphicsContext::setPlatformCompositeOperation unknown "
"WebBlendMode %d\n",
blendMode));
return SkBlendMode::kSrcOver;
}
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 SkBlendMode::kSrcOver;
}
SkASSERT(table[static_cast<uint8_t>(op)].mCompositOp == op);
return table[static_cast<uint8_t>(op)].m_xfermodeMode;
}
static U8CPU InvScaleByte(U8CPU component, uint32_t scale) {
SkASSERT(component == (uint8_t)component);
return (component * scale + 0x8000) >> 16;
}
SkColor SkPMColorToColor(SkPMColor pm) {
if (!pm)
return 0;
unsigned a = SkGetPackedA32(pm);
if (!a) {
// A zero alpha value when there are non-zero R, G, or B channels is an
// invalid premultiplied color (since all channels should have been
// multiplied by 0 if a=0).
SkASSERT(false);
// In production, return 0 to protect against division by zero.
return 0;
}
uint32_t scale = (255 << 16) / a;
return SkColorSetARGB(a, InvScaleByte(SkGetPackedR32(pm), scale),
InvScaleByte(SkGetPackedG32(pm), scale),
InvScaleByte(SkGetPackedB32(pm), scale));
}
bool SkPathContainsPoint(const SkPath& originalPath,
const FloatPoint& point,
SkPath::FillType ft) {
SkRect bounds = originalPath.getBounds();
// We can immediately return false if the point is outside the bounding
// rect. We don't use bounds.contains() here, since it would exclude
// points on the right and bottom edges of the bounding rect, and we want
// to include them.
SkScalar fX = SkFloatToScalar(point.x());
SkScalar fY = SkFloatToScalar(point.y());
if (fX < bounds.fLeft || fX > bounds.fRight || fY < bounds.fTop ||
fY > bounds.fBottom)
return false;
// Scale the path to a large size before hit testing for two reasons:
// 1) Skia has trouble with coordinates close to the max signed 16-bit values,
// so we scale larger paths down.
// TODO: when Skia is patched to work properly with large values, this will
// not be necessary.
// 2) Skia does not support analytic hit testing, so we scale paths up to do
// raster hit testing with subpixel accuracy.
SkScalar biggestCoord =
std::max(std::max(std::max(bounds.fRight, bounds.fBottom), -bounds.fLeft),
-bounds.fTop);
if (SkScalarNearlyZero(biggestCoord))
return false;
biggestCoord = std::max(std::max(biggestCoord, fX + 1), fY + 1);
const SkScalar kMaxCoordinate = SkIntToScalar(1 << 15);
SkScalar scale = kMaxCoordinate / biggestCoord;
SkRegion rgn;
SkRegion clip;
SkMatrix m;
SkPath scaledPath(originalPath);
scaledPath.setFillType(ft);
m.setScale(scale, scale);
scaledPath.transform(m, 0);
int x = static_cast<int>(floorf(0.5f + point.x() * scale));
int y = static_cast<int>(floorf(0.5f + point.y() * scale));
clip.setRect(x - 1, y - 1, x + 1, y + 1);
return rgn.setPath(scaledPath, clip);
}
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(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