/* * 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(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(), SkPicture::kFlattenMutableNonTexturePixelRefs_RecordingFlag); } 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(), SkPicture::kFlattenMutableNonTexturePixelRefs_RecordingFlag); // 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 (!fPicture.hasRecorded()) { return; } if (fDeviceContext) { fDeviceContext->prepareForDraw(); } fPicture.draw(fImmediateCanvas); fRecordingCanvas = fPicture.beginRecording(fImmediateDevice->width(), fImmediateDevice->height(), SkPicture::kFlattenMutableNonTexturePixelRefs_RecordingFlag); } void SkDeferredCanvas::DeferredDevice::flush() { flushPending(); fImmediateCanvas->flush(); } void SkDeferredCanvas::DeferredDevice::flushIfNeeded(const SkBitmap& bitmap) { if (bitmap.isImmutable() || fPicture.willFlattenPixelsOnRecord(bitmap)) { return; // safe to defer. } // 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(&bitmap), x, y, config8888); }