/*
* Copyright 2011 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkDeferredCanvas.h"
#include "SkPaint.h"
#include "SkShader.h"
#include "SkColorFilter.h"
#include "SkDrawFilter.h"
namespace {
bool isPaintOpaque(const SkPaint* paint,
const SkBitmap* bmpReplacesShader = NULL) {
// TODO: SkXfermode should have a virtual isOpaque method, which would
// make it possible to test modes that do not have a Coeff representation.
if (!paint) {
return bmpReplacesShader ? bmpReplacesShader->isOpaque() : true;
}
SkXfermode::Coeff srcCoeff, dstCoeff;
if (SkXfermode::AsCoeff(paint->getXfermode(), &srcCoeff, &dstCoeff)){
switch (dstCoeff) {
case SkXfermode::kZero_Coeff:
return true;
case SkXfermode::kISA_Coeff:
if (paint->getAlpha() != 255) {
break;
}
if (bmpReplacesShader) {
if (!bmpReplacesShader->isOpaque()) {
break;
}
} else if (paint->getShader() && !paint->getShader()->isOpaque()) {
break;
}
if (paint->getColorFilter() &&
((paint->getColorFilter()->getFlags() &
SkColorFilter::kAlphaUnchanged_Flag) == 0)) {
break;
}
return true;
case SkXfermode::kSA_Coeff:
if (paint->getAlpha() != 0) {
break;
}
if (paint->getColorFilter() &&
((paint->getColorFilter()->getFlags() &
SkColorFilter::kAlphaUnchanged_Flag) == 0)) {
break;
}
return true;
case SkXfermode::kSC_Coeff:
if (paint->getColor() != 0) { // all components must be 0
break;
}
if (bmpReplacesShader || paint->getShader()) {
break;
}
if (paint->getColorFilter() && (
(paint->getColorFilter()->getFlags() &
SkColorFilter::kAlphaUnchanged_Flag) == 0)) {
break;
}
return true;
default:
break;
}
}
return false;
}
} // unnamed namespace
SkDeferredCanvas::SkDeferredCanvas() {
init();
}
SkDeferredCanvas::SkDeferredCanvas(SkDevice* device) {
init();
setDevice(device);
}
SkDeferredCanvas::SkDeferredCanvas(SkDevice* device,
DeviceContext* deviceContext) {
init();
setDevice(device);
setDeviceContext(deviceContext);
}
void SkDeferredCanvas::init() {
fDeferredDrawing = true; // On by default
}
void SkDeferredCanvas::validate() const {
SkASSERT(getDevice());
}
SkCanvas* SkDeferredCanvas::drawingCanvas() const {
validate();
return fDeferredDrawing ? getDeferredDevice()->recordingCanvas() :
getDeferredDevice()->immediateCanvas();
}
void SkDeferredCanvas::flushIfNeeded(const SkBitmap& bitmap) {
validate();
if (fDeferredDrawing) {
getDeferredDevice()->flushIfNeeded(bitmap);
}
}
SkDeferredCanvas::DeferredDevice* SkDeferredCanvas::getDeferredDevice() const {
return static_cast<SkDeferredCanvas::DeferredDevice*>(getDevice());
}
void SkDeferredCanvas::setDeferredDrawing(bool val) {
validate(); // Must set device before calling this method
SkASSERT(drawingCanvas()->getSaveCount() == 1);
if (val != fDeferredDrawing) {
if (fDeferredDrawing) {
// Going live.
getDeferredDevice()->flushPending();
}
fDeferredDrawing = val;
}
}
SkDeferredCanvas::~SkDeferredCanvas() {
}
SkDevice* SkDeferredCanvas::setDevice(SkDevice* device) {
INHERITED::setDevice(SkNEW_ARGS(DeferredDevice, (device)))->unref();
return device;
}
SkDeferredCanvas::DeviceContext* SkDeferredCanvas::setDeviceContext(
DeviceContext* deviceContext) {
DeferredDevice* deferredDevice = getDeferredDevice();
SkASSERT(deferredDevice);
if (deferredDevice) {
deferredDevice->setDeviceContext(deviceContext);
}
return deviceContext;
}
bool SkDeferredCanvas::isFullFrame(const SkRect* rect,
const SkPaint* paint) const {
SkCanvas* canvas = drawingCanvas();
SkISize canvasSize = getDeviceSize();
if (rect) {
if (!canvas->getTotalMatrix().rectStaysRect()) {
return false; // conservative
}
SkRect transformedRect;
canvas->getTotalMatrix().mapRect(&transformedRect, *rect);
if (paint) {
SkPaint::Style paintStyle = paint->getStyle();
if (!(paintStyle == SkPaint::kFill_Style ||
paintStyle == SkPaint::kStrokeAndFill_Style)) {
return false;
}
if (paint->getMaskFilter() || paint->getLooper()
|| paint->getPathEffect() || paint->getImageFilter()) {
return false; // conservative
}
}
// The following test holds with AA enabled, and is conservative
// by a 0.5 pixel margin with AA disabled
if (transformedRect.fLeft > SkIntToScalar(0) ||
transformedRect.fTop > SkIntToScalar(0) ||
transformedRect.fRight < SkIntToScalar(canvasSize.fWidth) ||
transformedRect.fBottom < SkIntToScalar(canvasSize.fHeight)) {
return false;
}
}
switch (canvas->getClipType()) {
case SkCanvas::kRect_ClipType :
{
SkIRect bounds;
canvas->getClipDeviceBounds(&bounds);
if (bounds.fLeft > 0 || bounds.fTop > 0 ||
bounds.fRight < canvasSize.fWidth ||
bounds.fBottom < canvasSize.fHeight)
return false;
}
break;
case SkCanvas::kComplex_ClipType :
return false; // conservative
case SkCanvas::kEmpty_ClipType:
default:
break;
};
return true;
}
int SkDeferredCanvas::save(SaveFlags flags) {
drawingCanvas()->save(flags);
return this->INHERITED::save(flags);
}
int SkDeferredCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
SaveFlags flags) {
drawingCanvas()->saveLayer(bounds, paint, flags);
int count = this->INHERITED::save(flags);
this->clipRectBounds(bounds, flags, NULL);
return count;
}
void SkDeferredCanvas::restore() {
drawingCanvas()->restore();
this->INHERITED::restore();
}
bool SkDeferredCanvas::isDrawingToLayer() const {
return drawingCanvas()->isDrawingToLayer();
}
bool SkDeferredCanvas::translate(SkScalar dx, SkScalar dy) {
drawingCanvas()->translate(dx, dy);
return this->INHERITED::translate(dx, dy);
}
bool SkDeferredCanvas::scale(SkScalar sx, SkScalar sy) {
drawingCanvas()->scale(sx, sy);
return this->INHERITED::scale(sx, sy);
}
bool SkDeferredCanvas::rotate(SkScalar degrees) {
drawingCanvas()->rotate(degrees);
return this->INHERITED::rotate(degrees);
}
bool SkDeferredCanvas::skew(SkScalar sx, SkScalar sy) {
drawingCanvas()->skew(sx, sy);
return this->INHERITED::skew(sx, sy);
}
bool SkDeferredCanvas::concat(const SkMatrix& matrix) {
drawingCanvas()->concat(matrix);
return this->INHERITED::concat(matrix);
}
void SkDeferredCanvas::setMatrix(const SkMatrix& matrix) {
drawingCanvas()->setMatrix(matrix);
this->INHERITED::setMatrix(matrix);
}
bool SkDeferredCanvas::clipRect(const SkRect& rect,
SkRegion::Op op,
bool doAntiAlias) {
drawingCanvas()->clipRect(rect, op, doAntiAlias);
return this->INHERITED::clipRect(rect, op, doAntiAlias);
}
bool SkDeferredCanvas::clipPath(const SkPath& path,
SkRegion::Op op,
bool doAntiAlias) {
drawingCanvas()->clipPath(path, op, doAntiAlias);
return this->INHERITED::clipPath(path, op, doAntiAlias);
}
bool SkDeferredCanvas::clipRegion(const SkRegion& deviceRgn,
SkRegion::Op op) {
drawingCanvas()->clipRegion(deviceRgn, op);
return this->INHERITED::clipRegion(deviceRgn, op);
}
void SkDeferredCanvas::clear(SkColor color) {
// purge pending commands
if (fDeferredDrawing) {
getDeferredDevice()->contentsCleared();
}
drawingCanvas()->clear(color);
}
void SkDeferredCanvas::drawPaint(const SkPaint& paint) {
if (fDeferredDrawing && isFullFrame(NULL, &paint) &&
isPaintOpaque(&paint)) {
getDeferredDevice()->contentsCleared();
}
drawingCanvas()->drawPaint(paint);
}
void SkDeferredCanvas::drawPoints(PointMode mode, size_t count,
const SkPoint pts[], const SkPaint& paint) {
drawingCanvas()->drawPoints(mode, count, pts, paint);
}
void SkDeferredCanvas::drawRect(const SkRect& rect, const SkPaint& paint) {
if (fDeferredDrawing && isFullFrame(&rect, &paint) &&
isPaintOpaque(&paint)) {
getDeferredDevice()->contentsCleared();
}
drawingCanvas()->drawRect(rect, paint);
}
void SkDeferredCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
drawingCanvas()->drawPath(path, paint);
}
void SkDeferredCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar left,
SkScalar top, const SkPaint* paint) {
SkRect bitmapRect = SkRect::MakeXYWH(left, top,
SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height()));
if (fDeferredDrawing &&
isFullFrame(&bitmapRect, paint) &&
isPaintOpaque(paint, &bitmap)) {
getDeferredDevice()->contentsCleared();
}
drawingCanvas()->drawBitmap(bitmap, left, top, paint);
flushIfNeeded(bitmap);
}
void SkDeferredCanvas::drawBitmapRect(const SkBitmap& bitmap,
const SkIRect* src,
const SkRect& dst,
const SkPaint* paint) {
if (fDeferredDrawing &&
isFullFrame(&dst, paint) &&
isPaintOpaque(paint, &bitmap)) {
getDeferredDevice()->contentsCleared();
}
drawingCanvas()->drawBitmapRect(bitmap, src,
dst, paint);
flushIfNeeded(bitmap);
}
void SkDeferredCanvas::drawBitmapMatrix(const SkBitmap& bitmap,
const SkMatrix& m,
const SkPaint* paint) {
// TODO: reset recording canvas if paint+bitmap is opaque and clip rect
// covers canvas entirely and transformed bitmap covers canvas entirely
drawingCanvas()->drawBitmapMatrix(bitmap, m, paint);
flushIfNeeded(bitmap);
}
void SkDeferredCanvas::drawBitmapNine(const SkBitmap& bitmap,
const SkIRect& center, const SkRect& dst,
const SkPaint* paint) {
// TODO: reset recording canvas if paint+bitmap is opaque and clip rect
// covers canvas entirely and dst covers canvas entirely
drawingCanvas()->drawBitmapNine(bitmap, center,
dst, paint);
flushIfNeeded(bitmap);
}
void SkDeferredCanvas::drawSprite(const SkBitmap& bitmap, int left, int top,
const SkPaint* paint) {
SkRect bitmapRect = SkRect::MakeXYWH(
SkIntToScalar(left),
SkIntToScalar(top),
SkIntToScalar(bitmap.width()),
SkIntToScalar(bitmap.height()));
if (fDeferredDrawing &&
isFullFrame(&bitmapRect, paint) &&
isPaintOpaque(paint, &bitmap)) {
getDeferredDevice()->contentsCleared();
}
drawingCanvas()->drawSprite(bitmap, left, top,
paint);
flushIfNeeded(bitmap);
}
void SkDeferredCanvas::drawText(const void* text, size_t byteLength,
SkScalar x, SkScalar y, const SkPaint& paint) {
drawingCanvas()->drawText(text, byteLength, x, y, paint);
}
void SkDeferredCanvas::drawPosText(const void* text, size_t byteLength,
const SkPoint pos[], const SkPaint& paint) {
drawingCanvas()->drawPosText(text, byteLength, pos, paint);
}
void SkDeferredCanvas::drawPosTextH(const void* text, size_t byteLength,
const SkScalar xpos[], SkScalar constY,
const SkPaint& paint) {
drawingCanvas()->drawPosTextH(text, byteLength, xpos, constY, paint);
}
void SkDeferredCanvas::drawTextOnPath(const void* text, size_t byteLength,
const SkPath& path,
const SkMatrix* matrix,
const SkPaint& paint) {
drawingCanvas()->drawTextOnPath(text, byteLength,
path, matrix,
paint);
}
void SkDeferredCanvas::drawPicture(SkPicture& picture) {
drawingCanvas()->drawPicture(picture);
}
void SkDeferredCanvas::drawVertices(VertexMode vmode, int vertexCount,
const SkPoint vertices[],
const SkPoint texs[],
const SkColor colors[], SkXfermode* xmode,
const uint16_t indices[], int indexCount,
const SkPaint& paint) {
drawingCanvas()->drawVertices(vmode, vertexCount,
vertices, texs,
colors, xmode,
indices, indexCount,
paint);
}
SkBounder* SkDeferredCanvas::setBounder(SkBounder* bounder) {
drawingCanvas()->setBounder(bounder);
return INHERITED::setBounder(bounder);
}
SkDrawFilter* SkDeferredCanvas::setDrawFilter(SkDrawFilter* filter) {
drawingCanvas()->setDrawFilter(filter);
return INHERITED::setDrawFilter(filter);
}
SkCanvas* SkDeferredCanvas::canvasForDrawIter() {
return drawingCanvas();
}
// SkDeferredCanvas::DeferredDevice
//------------------------------------
SkDeferredCanvas::DeferredDevice::DeferredDevice(
SkDevice* immediateDevice, DeviceContext* deviceContext) :
SkDevice(SkBitmap::kNo_Config, immediateDevice->width(),
immediateDevice->height(), immediateDevice->isOpaque())
, fFreshFrame(true) {
fDeviceContext = deviceContext;
SkSafeRef(fDeviceContext);
fImmediateDevice = immediateDevice; // ref counted via fImmediateCanvas
fImmediateCanvas = SkNEW_ARGS(SkCanvas, (fImmediateDevice));
fRecordingCanvas = fPicture.beginRecording(fImmediateDevice->width(),
fImmediateDevice->height(), 0);
}
SkDeferredCanvas::DeferredDevice::~DeferredDevice() {
SkSafeUnref(fImmediateCanvas);
SkSafeUnref(fDeviceContext);
}
void SkDeferredCanvas::DeferredDevice::setDeviceContext(
DeviceContext* deviceContext) {
SkRefCnt_SafeAssign(fDeviceContext, deviceContext);
}
void SkDeferredCanvas::DeferredDevice::contentsCleared() {
if (!fRecordingCanvas->isDrawingToLayer()) {
fFreshFrame = true;
// TODO: find a way to transfer the state stack and layers
// to the new recording canvas. For now, purging only works
// with an empty stack.
if (fRecordingCanvas->getSaveCount() == 0) {
// Save state that is trashed by the purge
SkDrawFilter* drawFilter = fRecordingCanvas->getDrawFilter();
SkSafeRef(drawFilter); // So that it survives the purge
SkMatrix matrix = fRecordingCanvas->getTotalMatrix();
SkRegion clipRegion = fRecordingCanvas->getTotalClip();
// beginRecording creates a new recording canvas and discards the
// old one, hence purging deferred draw ops.
fRecordingCanvas = fPicture.beginRecording(
fImmediateDevice->width(),
fImmediateDevice->height(), 0);
// Restore pre-purge state
if (!clipRegion.isEmpty()) {
fRecordingCanvas->clipRegion(clipRegion,
SkRegion::kReplace_Op);
}
if (!matrix.isIdentity()) {
fRecordingCanvas->setMatrix(matrix);
}
if (drawFilter) {
fRecordingCanvas->setDrawFilter(drawFilter)->unref();
}
}
}
}
bool SkDeferredCanvas::DeferredDevice::isFreshFrame() {
bool ret = fFreshFrame;
fFreshFrame = false;
return ret;
}
void SkDeferredCanvas::DeferredDevice::flushPending() {
if (fDeviceContext) {
fDeviceContext->prepareForDraw();
}
fPicture.draw(fImmediateCanvas);
fRecordingCanvas = fPicture.beginRecording(fImmediateDevice->width(),
fImmediateDevice->height(), 0);
}
void SkDeferredCanvas::DeferredDevice::flush() {
flushPending();
fImmediateCanvas->flush();
}
void SkDeferredCanvas::DeferredDevice::flushIfNeeded(const SkBitmap& bitmap) {
if (bitmap.isImmutable()) {
return; // safe to deffer without registering a dependency
}
// For now, drawing a writable bitmap triggers a flush
// TODO: implement read-only semantics and auto buffer duplication on write
// in SkBitmap/SkPixelRef, which will make deferral possible in this case.
flushPending();
}
uint32_t SkDeferredCanvas::DeferredDevice::getDeviceCapabilities() {
return fImmediateDevice->getDeviceCapabilities();
}
int SkDeferredCanvas::DeferredDevice::width() const {
return fImmediateDevice->width();
}
int SkDeferredCanvas::DeferredDevice::height() const {
return fImmediateDevice->height();
}
SkGpuRenderTarget* SkDeferredCanvas::DeferredDevice::accessRenderTarget() {
flushPending();
return fImmediateDevice->accessRenderTarget();
}
void SkDeferredCanvas::DeferredDevice::writePixels(const SkBitmap& bitmap,
int x, int y, SkCanvas::Config8888 config8888) {
if (x <= 0 && y <= 0 && (x + bitmap.width()) >= width() &&
(y + bitmap.height()) >= height()) {
contentsCleared();
}
if (SkBitmap::kARGB_8888_Config == bitmap.config() &&
SkCanvas::kNative_Premul_Config8888 != config8888 &&
kPMColorAlias != config8888) {
//Special case config: no deferral
flushPending();
fImmediateDevice->writePixels(bitmap, x, y, config8888);
}
SkPaint paint;
paint.setXfermodeMode(SkXfermode::kSrc_Mode);
fRecordingCanvas->drawSprite(bitmap, x, y, &paint);
flushIfNeeded(bitmap);
}
const SkBitmap& SkDeferredCanvas::DeferredDevice::onAccessBitmap(SkBitmap*) {
flushPending();
return fImmediateDevice->accessBitmap(false);
}
SkDevice* SkDeferredCanvas::DeferredDevice::onCreateCompatibleDevice(
SkBitmap::Config config, int width, int height, bool isOpaque,
Usage usage) {
// Save layer usage not supported, and not required by SkDeferredCanvas.
SkASSERT(usage != kSaveLayer_Usage);
// Create a compatible non-deferred device.
SkDevice* compatibleDevice =
fImmediateDevice->createCompatibleDevice(config, width, height,
isOpaque);
return SkNEW_ARGS(DeferredDevice, (compatibleDevice, fDeviceContext));
}
bool SkDeferredCanvas::DeferredDevice::onReadPixels(
const SkBitmap& bitmap, int x, int y, SkCanvas::Config8888 config8888) {
flushPending();
return fImmediateCanvas->readPixels(const_cast<SkBitmap*>(&bitmap),
x, y, config8888);
}