This source file includes following definitions.
- asRect
- xfermodeIsOpaque
- xfermodePreservesOpaque
- paintIsOpaque
- getDeviceClipAsRect
- pushCanvasLayer
- popCanvasLayer
- setImageMask
- didDrawRect
- didDrawPath
- didDrawPoints
- didDrawBounded
- didDraw
- didDrawUnbounded
- applyOpaqueRegionFromLayer
- markRectAsOpaque
- markRectAsNonOpaque
- markAllAsNonOpaque
- currentTrackingOpaqueRect
#include "config.h"
#include "platform/graphics/skia/OpaqueRegionSkia.h"
#include "platform/graphics/GraphicsContext.h"
#include "SkColorFilter.h"
#include "SkShader.h"
namespace WebCore {
OpaqueRegionSkia::OpaqueRegionSkia()
: m_opaqueRect(SkRect::MakeEmpty())
{
}
IntRect OpaqueRegionSkia::asRect() const
{
int left = SkScalarCeilToInt(m_opaqueRect.fLeft);
int top = SkScalarCeilToInt(m_opaqueRect.fTop);
int right = SkScalarFloorToInt(m_opaqueRect.fRight);
int bottom = SkScalarFloorToInt(m_opaqueRect.fBottom);
return IntRect(left, top, right-left, bottom-top);
}
static inline bool xfermodeIsOpaque(const SkPaint& paint, bool srcIsOpaque)
{
if (!srcIsOpaque)
return false;
SkXfermode* xfermode = paint.getXfermode();
if (!xfermode)
return true;
SkXfermode::Mode mode;
if (!xfermode->asMode(&mode))
return false;
switch (mode) {
case SkXfermode::kSrc_Mode:
case SkXfermode::kSrcOver_Mode:
case SkXfermode::kDstOver_Mode:
case SkXfermode::kDstATop_Mode:
case SkXfermode::kPlus_Mode:
default:
return true;
case SkXfermode::kClear_Mode:
case SkXfermode::kDst_Mode:
case SkXfermode::kSrcIn_Mode:
case SkXfermode::kDstIn_Mode:
case SkXfermode::kSrcOut_Mode:
case SkXfermode::kDstOut_Mode:
case SkXfermode::kSrcATop_Mode:
case SkXfermode::kXor_Mode:
return false;
}
}
static inline bool xfermodePreservesOpaque(const SkPaint& paint, bool srcIsOpaque)
{
SkXfermode* xfermode = paint.getXfermode();
if (!xfermode)
return true;
SkXfermode::Mode mode;
if (!xfermode->asMode(&mode))
return false;
switch (mode) {
case SkXfermode::kDst_Mode:
case SkXfermode::kSrcOver_Mode:
case SkXfermode::kDstOver_Mode:
case SkXfermode::kSrcATop_Mode:
case SkXfermode::kPlus_Mode:
default:
return true;
case SkXfermode::kClear_Mode:
case SkXfermode::kSrcOut_Mode:
case SkXfermode::kDstOut_Mode:
case SkXfermode::kXor_Mode:
return false;
case SkXfermode::kSrc_Mode:
case SkXfermode::kSrcIn_Mode:
case SkXfermode::kDstIn_Mode:
case SkXfermode::kDstATop_Mode:
return srcIsOpaque;
}
}
static inline bool paintIsOpaque(const SkPaint& paint, OpaqueRegionSkia::DrawType drawType, const SkBitmap* bitmap)
{
if (paint.getAlpha() < 0xFF)
return false;
bool checkFillOnly = drawType != OpaqueRegionSkia::FillOrStroke;
if (!checkFillOnly && paint.getStyle() != SkPaint::kFill_Style && paint.isAntiAlias())
return false;
SkShader* shader = paint.getShader();
if (shader && !shader->isOpaque())
return false;
if (bitmap && !bitmap->isOpaque())
return false;
if (paint.getLooper())
return false;
if (paint.getImageFilter())
return false;
if (paint.getMaskFilter())
return false;
SkColorFilter* colorFilter = paint.getColorFilter();
if (colorFilter && !(colorFilter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag))
return false;
return true;
}
static inline bool getDeviceClipAsRect(const GraphicsContext* context, SkRect& deviceClipRect)
{
if (!context->canvas()->isClipRect())
return false;
SkIRect deviceClipIRect;
if (context->canvas()->getClipDeviceBounds(&deviceClipIRect))
deviceClipRect.set(deviceClipIRect);
else
deviceClipRect.setEmpty();
return true;
}
void OpaqueRegionSkia::pushCanvasLayer(const SkPaint* paint)
{
CanvasLayerState state;
if (paint)
state.paint = *paint;
m_canvasLayerStack.append(state);
}
void OpaqueRegionSkia::popCanvasLayer(const GraphicsContext* context)
{
ASSERT(!m_canvasLayerStack.isEmpty());
if (m_canvasLayerStack.isEmpty())
return;
const CanvasLayerState& canvasLayer = m_canvasLayerStack.last();
SkRect layerOpaqueRect = canvasLayer.opaqueRect;
SkPaint layerPaint = canvasLayer.paint;
if (canvasLayer.hasImageMask && !layerOpaqueRect.intersect(canvasLayer.imageOpaqueRect))
layerOpaqueRect.setEmpty();
m_canvasLayerStack.removeLast();
applyOpaqueRegionFromLayer(context, layerOpaqueRect, layerPaint);
}
void OpaqueRegionSkia::setImageMask(const SkRect& imageOpaqueRect)
{
ASSERT(!m_canvasLayerStack.isEmpty());
m_canvasLayerStack.last().hasImageMask = true;
m_canvasLayerStack.last().imageOpaqueRect = imageOpaqueRect;
}
void OpaqueRegionSkia::didDrawRect(const GraphicsContext* context, const SkRect& fillRect, const SkPaint& paint, const SkBitmap* sourceBitmap)
{
if (paint.getStyle() != SkPaint::kFill_Style) {
bool fillsBounds = false;
if (!paint.canComputeFastBounds())
didDrawUnbounded(context, paint, FillOrStroke);
else {
SkRect strokeRect;
strokeRect = paint.computeFastBounds(fillRect, &strokeRect);
didDraw(context, strokeRect, paint, sourceBitmap, fillsBounds, FillOrStroke);
}
}
bool fillsBounds = paint.getStyle() != SkPaint::kStroke_Style;
didDraw(context, fillRect, paint, sourceBitmap, fillsBounds, FillOnly);
}
void OpaqueRegionSkia::didDrawPath(const GraphicsContext* context, const SkPath& path, const SkPaint& paint)
{
SkRect rect;
if (path.isRect(&rect)) {
didDrawRect(context, rect, paint, 0);
return;
}
bool fillsBounds = false;
if (!paint.canComputeFastBounds())
didDrawUnbounded(context, paint, FillOrStroke);
else {
rect = paint.computeFastBounds(path.getBounds(), &rect);
didDraw(context, rect, paint, 0, fillsBounds, FillOrStroke);
}
}
void OpaqueRegionSkia::didDrawPoints(const GraphicsContext* context, SkCanvas::PointMode mode, int numPoints, const SkPoint points[], const SkPaint& paint)
{
if (!numPoints)
return;
SkRect rect;
rect.fLeft = points[0].fX;
rect.fRight = points[0].fX + 1;
rect.fTop = points[0].fY;
rect.fBottom = points[0].fY + 1;
for (int i = 1; i < numPoints; ++i) {
rect.fLeft = std::min(rect.fLeft, points[i].fX);
rect.fRight = std::max(rect.fRight, points[i].fX + 1);
rect.fTop = std::min(rect.fTop, points[i].fY);
rect.fBottom = std::max(rect.fBottom, points[i].fY + 1);
}
bool fillsBounds = false;
if (!paint.canComputeFastBounds())
didDrawUnbounded(context, paint, FillOrStroke);
else {
rect = paint.computeFastBounds(rect, &rect);
didDraw(context, rect, paint, 0, fillsBounds, FillOrStroke);
}
}
void OpaqueRegionSkia::didDrawBounded(const GraphicsContext* context, const SkRect& bounds, const SkPaint& paint)
{
bool fillsBounds = false;
if (!paint.canComputeFastBounds())
didDrawUnbounded(context, paint, FillOrStroke);
else {
SkRect rect;
rect = paint.computeFastBounds(bounds, &rect);
didDraw(context, rect, paint, 0, fillsBounds, FillOrStroke);
}
}
void OpaqueRegionSkia::didDraw(const GraphicsContext* context, const SkRect& rect, const SkPaint& paint, const SkBitmap* sourceBitmap, bool fillsBounds, DrawType drawType)
{
SkRect targetRect = rect;
SkMatrix canvasTransform = context->canvas()->getTotalMatrix();
if (!canvasTransform.mapRect(&targetRect))
fillsBounds = false;
SkRect deviceClipRect;
if (!getDeviceClipAsRect(context, deviceClipRect))
fillsBounds = false;
else if (!targetRect.intersect(deviceClipRect))
return;
bool drawsOpaque = paintIsOpaque(paint, drawType, sourceBitmap);
bool xfersOpaque = xfermodeIsOpaque(paint, drawsOpaque);
bool preservesOpaque = xfermodePreservesOpaque(paint, drawsOpaque);
if (fillsBounds && xfersOpaque)
markRectAsOpaque(targetRect);
else if (!preservesOpaque)
markRectAsNonOpaque(targetRect);
}
void OpaqueRegionSkia::didDrawUnbounded(const GraphicsContext* context, const SkPaint& paint, DrawType drawType)
{
bool drawsOpaque = paintIsOpaque(paint, drawType, 0);
bool preservesOpaque = xfermodePreservesOpaque(paint, drawsOpaque);
if (preservesOpaque)
return;
SkRect deviceClipRect;
getDeviceClipAsRect(context, deviceClipRect);
markRectAsNonOpaque(deviceClipRect);
}
void OpaqueRegionSkia::applyOpaqueRegionFromLayer(const GraphicsContext* context, const SkRect& layerOpaqueRect, const SkPaint& paint)
{
SkRect deviceClipRect;
bool deviceClipIsARect = getDeviceClipAsRect(context, deviceClipRect);
if (deviceClipRect.isEmpty())
return;
SkRect sourceOpaqueRect = layerOpaqueRect;
SkRect destinationOpaqueRect = currentTrackingOpaqueRect();
bool outsideSourceOpaqueRectPreservesOpaque = xfermodePreservesOpaque(paint, false);
if (!outsideSourceOpaqueRectPreservesOpaque)
markRectAsNonOpaque(deviceClipRect);
if (!deviceClipIsARect)
return;
if (!sourceOpaqueRect.intersect(deviceClipRect))
return;
bool sourceOpaqueRectDrawsOpaque = paintIsOpaque(paint, FillOnly, 0);
bool sourceOpaqueRectXfersOpaque = xfermodeIsOpaque(paint, sourceOpaqueRectDrawsOpaque);
bool sourceOpaqueRectPreservesOpaque = xfermodePreservesOpaque(paint, sourceOpaqueRectDrawsOpaque);
if (sourceOpaqueRectXfersOpaque)
markRectAsOpaque(sourceOpaqueRect);
else if (sourceOpaqueRectPreservesOpaque && sourceOpaqueRect.intersect(destinationOpaqueRect))
markRectAsOpaque(sourceOpaqueRect);
}
void OpaqueRegionSkia::markRectAsOpaque(const SkRect& rect)
{
SkRect& opaqueRect = currentTrackingOpaqueRect();
if (rect.isEmpty())
return;
if (opaqueRect.contains(rect))
return;
if (rect.contains(opaqueRect)) {
opaqueRect = rect;
return;
}
if (rect.fTop <= opaqueRect.fTop && rect.fBottom >= opaqueRect.fBottom) {
if (rect.fLeft < opaqueRect.fLeft && rect.fRight >= opaqueRect.fLeft)
opaqueRect.fLeft = rect.fLeft;
if (rect.fRight > opaqueRect.fRight && rect.fLeft <= opaqueRect.fRight)
opaqueRect.fRight = rect.fRight;
} else if (rect.fLeft <= opaqueRect.fLeft && rect.fRight >= opaqueRect.fRight) {
if (rect.fTop < opaqueRect.fTop && rect.fBottom >= opaqueRect.fTop)
opaqueRect.fTop = rect.fTop;
if (rect.fBottom > opaqueRect.fBottom && rect.fTop <= opaqueRect.fBottom)
opaqueRect.fBottom = rect.fBottom;
}
long opaqueArea = (long)opaqueRect.width() * (long)opaqueRect.height();
long area = (long)rect.width() * (long)rect.height();
if (area > opaqueArea)
opaqueRect = rect;
}
void OpaqueRegionSkia::markRectAsNonOpaque(const SkRect& rect)
{
SkRect& opaqueRect = currentTrackingOpaqueRect();
if (!SkRect::Intersects(rect, opaqueRect))
return;
if (rect.contains(opaqueRect)) {
markAllAsNonOpaque();
return;
}
int deltaLeft = rect.fLeft - opaqueRect.fLeft;
int deltaRight = opaqueRect.fRight - rect.fRight;
int deltaTop = rect.fTop - opaqueRect.fTop;
int deltaBottom = opaqueRect.fBottom - rect.fBottom;
SkRect horizontal = opaqueRect;
if (deltaTop > deltaBottom)
horizontal.fBottom = rect.fTop;
else
horizontal.fTop = rect.fBottom;
SkRect vertical = opaqueRect;
if (deltaLeft > deltaRight)
vertical.fRight = rect.fLeft;
else
vertical.fLeft = rect.fRight;
if ((long)horizontal.width() * (long)horizontal.height() > (long)vertical.width() * (long)vertical.height())
opaqueRect = horizontal;
else
opaqueRect = vertical;
}
void OpaqueRegionSkia::markAllAsNonOpaque()
{
SkRect& opaqueRect = currentTrackingOpaqueRect();
opaqueRect.setEmpty();
}
SkRect& OpaqueRegionSkia::currentTrackingOpaqueRect()
{
return m_canvasLayerStack.isEmpty() ? m_opaqueRect : m_canvasLayerStack.last().opaqueRect;
}
}