Pass full matrix + clip save flags to the native SkCanvas.
With granular state flags being deprecated, always pass full save flags to the native canvas. Legacy behavior is emulated in NativeCanvasWrapper, with a focus on minimizing the overhead when there are no side effects to be persisted. Change-Id: Ifdad2ff9bb3a1f9736c6c41afc9ec6c07f62320e
This commit is contained in:
@@ -19,11 +19,14 @@
|
||||
#include <android_runtime/AndroidRuntime.h>
|
||||
|
||||
#include "SkCanvas.h"
|
||||
#include "SkClipStack.h"
|
||||
#include "SkDevice.h"
|
||||
#include "SkDeque.h"
|
||||
#include "SkDrawFilter.h"
|
||||
#include "SkGraphics.h"
|
||||
#include "SkPorterDuff.h"
|
||||
#include "SkShader.h"
|
||||
#include "SkTArray.h"
|
||||
#include "SkTemplates.h"
|
||||
|
||||
#ifdef USE_MINIKIN
|
||||
@@ -43,25 +46,6 @@
|
||||
|
||||
namespace android {
|
||||
|
||||
// Holds an SkCanvas reference plus additional native data.
|
||||
class NativeCanvasWrapper {
|
||||
public:
|
||||
NativeCanvasWrapper(SkCanvas* canvas)
|
||||
: mCanvas(canvas) { }
|
||||
|
||||
SkCanvas* getCanvas() const {
|
||||
return mCanvas.get();
|
||||
}
|
||||
|
||||
void setCanvas(SkCanvas* canvas) {
|
||||
SkASSERT(canvas);
|
||||
mCanvas.reset(canvas);
|
||||
}
|
||||
|
||||
private:
|
||||
SkAutoTUnref<SkCanvas> mCanvas;
|
||||
};
|
||||
|
||||
class ClipCopier : public SkCanvas::ClipVisitor {
|
||||
public:
|
||||
ClipCopier(SkCanvas* dstCanvas) : m_dstCanvas(dstCanvas) {}
|
||||
@@ -80,6 +64,155 @@ private:
|
||||
SkCanvas* m_dstCanvas;
|
||||
};
|
||||
|
||||
// Holds an SkCanvas reference plus additional native data.
|
||||
class NativeCanvasWrapper {
|
||||
private:
|
||||
struct SaveRec {
|
||||
int saveCount;
|
||||
SkCanvas::SaveFlags saveFlags;
|
||||
};
|
||||
|
||||
public:
|
||||
NativeCanvasWrapper(SkCanvas* canvas)
|
||||
: mCanvas(canvas)
|
||||
, mSaveStack(NULL) {
|
||||
SkASSERT(canvas);
|
||||
}
|
||||
|
||||
~NativeCanvasWrapper() {
|
||||
delete mSaveStack;
|
||||
}
|
||||
|
||||
SkCanvas* getCanvas() const {
|
||||
return mCanvas.get();
|
||||
}
|
||||
|
||||
void setCanvas(SkCanvas* canvas) {
|
||||
SkASSERT(canvas);
|
||||
mCanvas.reset(canvas);
|
||||
|
||||
delete mSaveStack;
|
||||
mSaveStack = NULL;
|
||||
}
|
||||
|
||||
int save(SkCanvas::SaveFlags flags) {
|
||||
int count = mCanvas->save();
|
||||
recordPartialSave(flags);
|
||||
return count;
|
||||
}
|
||||
|
||||
int saveLayer(const SkRect* bounds, const SkPaint* paint,
|
||||
SkCanvas::SaveFlags flags) {
|
||||
int count = mCanvas->saveLayer(bounds, paint,
|
||||
static_cast<SkCanvas::SaveFlags>(flags | SkCanvas::kMatrixClip_SaveFlag));
|
||||
recordPartialSave(flags);
|
||||
return count;
|
||||
}
|
||||
|
||||
int saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
|
||||
SkCanvas::SaveFlags flags) {
|
||||
int count = mCanvas->saveLayerAlpha(bounds, alpha,
|
||||
static_cast<SkCanvas::SaveFlags>(flags | SkCanvas::kMatrixClip_SaveFlag));
|
||||
recordPartialSave(flags);
|
||||
return count;
|
||||
}
|
||||
|
||||
void restore() {
|
||||
const SaveRec* rec = (NULL == mSaveStack)
|
||||
? NULL
|
||||
: static_cast<SaveRec*>(mSaveStack->back());
|
||||
int currentSaveCount = mCanvas->getSaveCount() - 1;
|
||||
SkASSERT(NULL == rec || currentSaveCount >= rec->saveCount);
|
||||
|
||||
if (NULL == rec || rec->saveCount != currentSaveCount) {
|
||||
// Fast path - no record for this frame.
|
||||
mCanvas->restore();
|
||||
return;
|
||||
}
|
||||
|
||||
bool preserveMatrix = !(rec->saveFlags & SkCanvas::kMatrix_SaveFlag);
|
||||
bool preserveClip = !(rec->saveFlags & SkCanvas::kClip_SaveFlag);
|
||||
|
||||
SkMatrix savedMatrix;
|
||||
if (preserveMatrix) {
|
||||
savedMatrix = mCanvas->getTotalMatrix();
|
||||
}
|
||||
|
||||
SkTArray<SkClipStack::Element> savedClips;
|
||||
if (preserveClip) {
|
||||
saveClipsForFrame(savedClips, currentSaveCount);
|
||||
}
|
||||
|
||||
mCanvas->restore();
|
||||
|
||||
if (preserveMatrix) {
|
||||
mCanvas->setMatrix(savedMatrix);
|
||||
}
|
||||
|
||||
if (preserveClip && !savedClips.empty()) {
|
||||
applyClips(savedClips);
|
||||
}
|
||||
|
||||
mSaveStack->pop_back();
|
||||
}
|
||||
|
||||
private:
|
||||
void recordPartialSave(SkCanvas::SaveFlags flags) {
|
||||
// A partial save is a save operation which doesn't capture the full canvas state.
|
||||
// (either kMatrix_SaveFlags or kClip_SaveFlag is missing).
|
||||
|
||||
// Mask-out non canvas state bits.
|
||||
flags = static_cast<SkCanvas::SaveFlags>(flags & SkCanvas::kMatrixClip_SaveFlag);
|
||||
|
||||
if (SkCanvas::kMatrixClip_SaveFlag == flags) {
|
||||
// not a partial save.
|
||||
return;
|
||||
}
|
||||
|
||||
if (NULL == mSaveStack) {
|
||||
mSaveStack = new SkDeque(sizeof(struct SaveRec), 8);
|
||||
}
|
||||
|
||||
SaveRec* rec = static_cast<SaveRec*>(mSaveStack->push_back());
|
||||
// Store the save counter in the SkClipStack domain.
|
||||
// (0-based, equal to the number of save ops on the stack).
|
||||
rec->saveCount = mCanvas->getSaveCount() - 1;
|
||||
rec->saveFlags = flags;
|
||||
}
|
||||
|
||||
void saveClipsForFrame(SkTArray<SkClipStack::Element>& clips,
|
||||
int frameSaveCount) {
|
||||
SkClipStack::Iter clipIterator(*mCanvas->getClipStack(),
|
||||
SkClipStack::Iter::kTop_IterStart);
|
||||
while (const SkClipStack::Element* elem = clipIterator.next()) {
|
||||
if (elem->getSaveCount() < frameSaveCount) {
|
||||
// done with the current frame.
|
||||
break;
|
||||
}
|
||||
SkASSERT(elem->getSaveCount() == frameSaveCount);
|
||||
clips.push_back(*elem);
|
||||
}
|
||||
}
|
||||
|
||||
void applyClips(const SkTArray<SkClipStack::Element>& clips) {
|
||||
ClipCopier clipCopier(mCanvas);
|
||||
|
||||
// The clip stack stores clips in device space.
|
||||
SkMatrix origMatrix = mCanvas->getTotalMatrix();
|
||||
mCanvas->resetMatrix();
|
||||
|
||||
// We pushed the clips in reverse order.
|
||||
for (int i = clips.count() - 1; i >= 0; --i) {
|
||||
clips[i].replay(&clipCopier);
|
||||
}
|
||||
|
||||
mCanvas->setMatrix(origMatrix);
|
||||
}
|
||||
|
||||
SkAutoTUnref<SkCanvas> mCanvas;
|
||||
SkDeque* mSaveStack; // lazily allocated, tracks partial saves.
|
||||
};
|
||||
|
||||
// Returns true if the SkCanvas's clip is non-empty.
|
||||
static jboolean hasNonEmptyClip(const SkCanvas& canvas) {
|
||||
bool emptyClip = canvas.isClipEmpty();
|
||||
@@ -88,11 +221,15 @@ static jboolean hasNonEmptyClip(const SkCanvas& canvas) {
|
||||
|
||||
class SkCanvasGlue {
|
||||
public:
|
||||
// Get the native wrapper for a given handle.
|
||||
static inline NativeCanvasWrapper* getNativeWrapper(jlong nativeHandle) {
|
||||
SkASSERT(nativeHandle);
|
||||
return reinterpret_cast<NativeCanvasWrapper*>(nativeHandle);
|
||||
}
|
||||
|
||||
// Get the SkCanvas for a given native handle.
|
||||
static inline SkCanvas* getNativeCanvas(jlong nativeHandle) {
|
||||
SkASSERT(nativeHandle);
|
||||
NativeCanvasWrapper* wrapper = reinterpret_cast<NativeCanvasWrapper*>(nativeHandle);
|
||||
NativeCanvasWrapper* wrapper = getNativeWrapper(nativeHandle);
|
||||
SkCanvas* canvas = wrapper->getCanvas();
|
||||
SkASSERT(canvas);
|
||||
|
||||
@@ -186,56 +323,56 @@ public:
|
||||
}
|
||||
|
||||
static jint save(JNIEnv*, jobject, jlong canvasHandle, jint flagsHandle) {
|
||||
SkCanvas* canvas = getNativeCanvas(canvasHandle);
|
||||
NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle);
|
||||
SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
|
||||
return static_cast<jint>(canvas->save(flags));
|
||||
return static_cast<jint>(wrapper->save(flags));
|
||||
}
|
||||
|
||||
static jint saveLayer(JNIEnv* env, jobject, jlong canvasHandle,
|
||||
jfloat l, jfloat t, jfloat r, jfloat b,
|
||||
jlong paintHandle, jint flags) {
|
||||
SkCanvas* canvas = getNativeCanvas(canvasHandle);
|
||||
jlong paintHandle, jint flagsHandle) {
|
||||
NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle);
|
||||
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
|
||||
SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
|
||||
SkRect bounds;
|
||||
bounds.set(l, t, r, b);
|
||||
int result = canvas->saveLayer(&bounds, paint,
|
||||
static_cast<SkCanvas::SaveFlags>(flags));
|
||||
return static_cast<jint>(result);
|
||||
return static_cast<jint>(wrapper->saveLayer(&bounds, paint, flags));
|
||||
}
|
||||
|
||||
static jint saveLayerAlpha(JNIEnv* env, jobject, jlong canvasHandle,
|
||||
jfloat l, jfloat t, jfloat r, jfloat b,
|
||||
jint alpha, jint flags) {
|
||||
SkCanvas* canvas = getNativeCanvas(canvasHandle);
|
||||
jint alpha, jint flagsHandle) {
|
||||
NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle);
|
||||
SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
|
||||
SkRect bounds;
|
||||
bounds.set(l, t, r, b);
|
||||
int result = canvas->saveLayerAlpha(&bounds, alpha,
|
||||
static_cast<SkCanvas::SaveFlags>(flags));
|
||||
return static_cast<jint>(result);
|
||||
return static_cast<jint>(wrapper->saveLayerAlpha(&bounds, alpha, flags));
|
||||
}
|
||||
|
||||
static void restore(JNIEnv* env, jobject, jlong canvasHandle) {
|
||||
SkCanvas* canvas = getNativeCanvas(canvasHandle);
|
||||
if (canvas->getSaveCount() <= 1) { // cannot restore anymore
|
||||
NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle);
|
||||
if (wrapper->getCanvas()->getSaveCount() <= 1) { // cannot restore anymore
|
||||
doThrowISE(env, "Underflow in restore");
|
||||
return;
|
||||
}
|
||||
canvas->restore();
|
||||
wrapper->restore();
|
||||
}
|
||||
|
||||
static jint getSaveCount(JNIEnv*, jobject, jlong canvasHandle) {
|
||||
SkCanvas* canvas = getNativeCanvas(canvasHandle);
|
||||
return static_cast<jint>(canvas->getSaveCount());
|
||||
return static_cast<jint>(getNativeCanvas(canvasHandle)->getSaveCount());
|
||||
}
|
||||
|
||||
static void restoreToCount(JNIEnv* env, jobject, jlong canvasHandle,
|
||||
jint restoreCount) {
|
||||
SkCanvas* canvas = getNativeCanvas(canvasHandle);
|
||||
NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle);
|
||||
if (restoreCount < 1) {
|
||||
doThrowIAE(env, "Underflow in restoreToCount");
|
||||
return;
|
||||
}
|
||||
canvas->restoreToCount(restoreCount);
|
||||
|
||||
while (wrapper->getCanvas()->getSaveCount() > restoreCount) {
|
||||
wrapper->restore();
|
||||
}
|
||||
}
|
||||
|
||||
static void translate(JNIEnv*, jobject, jlong canvasHandle,
|
||||
|
||||
Reference in New Issue
Block a user