'../include/utils',
'../include/xml',
'../src/core',
- '../src/opts',
'../src/image',
+ '../src/opts',
+ '../src/utils',
],
'sources': [
'core.gypi', # Makes the gypi appear in IDEs (but does not modify the build).
'<(skia_src_path)/core/SkRasterClip.cpp',
'<(skia_src_path)/core/SkRasterizer.cpp',
'<(skia_src_path)/core/SkReadBuffer.cpp',
+ '<(skia_src_path)/core/SkRecordDraw.cpp',
+ '<(skia_src_path)/core/SkRecordOpts.cpp',
+ '<(skia_src_path)/core/SkRecorder.cpp',
+ '<(skia_src_path)/core/SkRecording.cpp',
'<(skia_src_path)/core/SkRect.cpp',
'<(skia_src_path)/core/SkRefDict.cpp',
'<(skia_src_path)/core/SkRegion.cpp',
'flags.gyp:flags',
'jsoncpp.gyp:jsoncpp',
'gputest.gyp:skgputest',
- 'record.gyp:*',
'etc1.gyp:libetc1',
],
'conditions': [
+++ /dev/null
-# An experimental library for faster recording of SkCanvas commands.
-{
- 'targets': [{
- 'target_name': 'record',
- 'type': 'static_library',
- 'includes': [ 'record.gypi' ],
- 'include_dirs': [
- '../include/config',
- '../include/core',
- '../include/record',
- '../src/utils',
- ],
- 'direct_dependent_settings': {
- 'include_dirs': [
- '../include/record', # Public headers.
- ],
- },
- }]
-}
-# Source list for SkRecord
-# The parent gyp/gypi file must define
-# 'skia_src_path' e.g. skia/trunk/src
-# The Skia build defines this in common_variables.gypi.
+# TODO: cleanup references in Chrome build and remove
{
- 'sources': [
- '<(skia_src_path)/record/SkRecordDraw.cpp',
- '<(skia_src_path)/record/SkRecordOpts.cpp',
- '<(skia_src_path)/record/SkRecorder.cpp',
- '<(skia_src_path)/record/SkRecording.cpp',
- ]
}
'../src/pathops',
'../src/pdf',
'../src/pipe/utils',
- '../src/record',
'../src/utils',
'../src/utils/debugger',
'../tools/',
'flags.gyp:flags',
'pdf.gyp:pdf',
'tools.gyp:picture_utils',
- 'record.gyp:record',
],
'sources': [
'../tests/Test.cpp',
'bench.gyp:bench_timer',
'flags.gyp:flags',
'skia_lib.gyp:skia_lib',
- 'record.gyp:*',
],
},
{
'include_dirs': [
'../src/core/',
'../src/images',
- '../src/record',
],
'dependencies': [
'bench.gyp:bench_timer',
'flags.gyp:flags',
'skia_lib.gyp:skia_lib',
- 'record.gyp:*',
],
},
{
'../src/core/',
'../src/images',
'../src/lazy',
- '../src/record',
],
'dependencies': [
'bench.gyp:bench_timer',
'flags.gyp:flags',
- 'record.gyp:*',
'skia_lib.gyp:skia_lib',
],
},
class SkCanvas;
class SkPictureRecord;
+class SkRecord;
+class SkRecorder;
class SK_API SkPictureRecorder : SkNoncopyable {
public:
- SkPictureRecorder() : fCanvas(NULL) { }
+ SkPictureRecorder() : fPictureRecord(NULL), fRecorder(NULL), fRecord(NULL) { }
~SkPictureRecorder();
/** Returns the canvas that records the drawing commands.
SkBBHFactory* bbhFactory = NULL,
uint32_t recordFlags = 0);
+ /** Same as beginRecording(), using a new faster backend. */
+ SkCanvas* EXPERIMENTAL_beginRecording(int width, int height,
+ SkBBHFactory* bbhFactory = NULL);
+
/** Returns the recording canvas if one is active, or NULL if recording is
not active. This does not alter the refcnt on the canvas (if present).
*/
void internalOnly_EnableOpts(bool enableOpts);
private:
+ void reset();
+
/** Replay the current (partially recorded) operation stream into
canvas. This call doesn't close the current recording.
*/
int fWidth;
int fHeight;
- SkPictureRecord* fCanvas; // ref counted
+
+ // Both ref counted. One of these two will be non-null:
+ SkPictureRecord* fPictureRecord; // beginRecording()
+ SkRecorder* fRecorder; // EXPERIMENTAL_beginRecording()
+
+ // Not refcounted. Used by EXPERIMENTAL_beginRecording().
+ SkRecord* fRecord;
typedef SkNoncopyable INHERITED;
};
#include "SkPicturePlayback.h"
#include "SkPictureRecord.h"
#include "SkPictureRecorder.h"
-#include "SkTypes.h"
+#include "SkRecord.h"
+#include "SkRecordDraw.h"
+#include "SkRecorder.h"
+#include "SkTypes.h"
SkPictureRecorder::~SkPictureRecorder() {
- SkSafeSetNull(fCanvas);
+ this->reset();
+}
+
+void SkPictureRecorder::reset() {
+ SkSafeSetNull(fPictureRecord);
+ SkSafeSetNull(fRecorder);
+ SkDELETE(fRecord);
+ fRecord = NULL;
}
SkCanvas* SkPictureRecorder::beginRecording(int width, int height,
SkBBHFactory* bbhFactory /* = NULL */,
uint32_t recordFlags /* = 0 */) {
- SkSafeSetNull(fCanvas); // terminate any prior recording(s)
-
+ this->reset(); // terminate any prior recording(s)
fWidth = width;
fHeight = height;
if (NULL != bbhFactory) {
SkAutoTUnref<SkBBoxHierarchy> tree((*bbhFactory)(width, height));
SkASSERT(NULL != tree);
- fCanvas = SkNEW_ARGS(SkBBoxHierarchyRecord, (size, recordFlags, tree.get()));
+ fPictureRecord = SkNEW_ARGS(SkBBoxHierarchyRecord, (size, recordFlags, tree.get()));
} else {
- fCanvas = SkNEW_ARGS(SkPictureRecord, (size, recordFlags));
+ fPictureRecord = SkNEW_ARGS(SkPictureRecord, (size, recordFlags));
}
- fCanvas->beginRecording();
+ fPictureRecord->beginRecording();
+ return this->getRecordingCanvas();
+}
+
+SkCanvas* SkPictureRecorder::EXPERIMENTAL_beginRecording(int width, int height,
+ SkBBHFactory* bbhFactory /* = NULL */) {
+ this->reset();
+ fWidth = width;
+ fHeight = height;
- return fCanvas;
+ // TODO: plumb bbhFactory through
+ fRecord = SkNEW(SkRecord);
+ fRecorder = SkNEW_ARGS(SkRecorder, (fRecord, width, height));
+ return this->getRecordingCanvas();
}
SkCanvas* SkPictureRecorder::getRecordingCanvas() {
- return fCanvas;
+ if (NULL != fRecorder) {
+ return fRecorder;
+ }
+ return fPictureRecord;
}
SkPicture* SkPictureRecorder::endRecording() {
- if (NULL == fCanvas) {
- return NULL;
- }
+ SkPicture* picture = NULL;
- fCanvas->endRecording();
+ if (NULL != fRecorder) {
+ // TODO: picture = SkNEW_ARGS(SkPicture, (fWidth, fHeight, fRecord));
+ // fRecord = NULL;
+ }
- const bool deepCopyOps = false;
- SkAutoTUnref<SkPicture> picture(SkNEW_ARGS(SkPicture, (fWidth, fHeight,
- *fCanvas, deepCopyOps)));
- SkSafeSetNull(fCanvas);
+ if (NULL != fPictureRecord) {
+ fPictureRecord->endRecording();
+ const bool deepCopyOps = false;
+ picture = SkNEW_ARGS(SkPicture, (fWidth, fHeight, *fPictureRecord, deepCopyOps));
+ }
- return picture.detach();
+ this->reset();
+ return picture;
}
void SkPictureRecorder::internalOnly_EnableOpts(bool enableOpts) {
- if (NULL != fCanvas) {
- fCanvas->internalOnly_EnableOpts(enableOpts);
+ if (NULL != fPictureRecord) {
+ fPictureRecord->internalOnly_EnableOpts(enableOpts);
}
}
void SkPictureRecorder::partialReplay(SkCanvas* canvas) const {
- if (NULL == fCanvas || NULL == canvas) {
- // Not recording or nothing to replay into
+ if (NULL == canvas) {
return;
}
- const bool deepCopyOps = true;
- SkAutoTUnref<SkPicture> picture(SkNEW_ARGS(SkPicture, (fWidth, fHeight,
- *fCanvas, deepCopyOps)));
- picture->draw(canvas);
+ if (NULL != fRecorder) {
+ SkRecordDraw(*fRecord, canvas);
+ }
+
+ if (NULL != fPictureRecord) {
+ const bool deepCopyOps = true;
+ SkPicture picture(fWidth, fHeight, *fPictureRecord, deepCopyOps);
+ picture.draw(canvas);
+ }
}
--- /dev/null
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkRecord_DEFINED
+#define SkRecord_DEFINED
+
+#include "SkChunkAlloc.h"
+#include "SkRecords.h"
+#include "SkTLogic.h"
+#include "SkTemplates.h"
+
+// SkRecord (REC-ord) represents a sequence of SkCanvas calls, saved for future use.
+// These future uses may include: replay, optimization, serialization, or combinations of those.
+//
+// Though an enterprising user may find calling alloc(), append(), visit(), and mutate() enough to
+// work with SkRecord, you probably want to look at SkRecorder which presents an SkCanvas interface
+// for creating an SkRecord, and SkRecordDraw which plays an SkRecord back into another SkCanvas.
+//
+// SkRecord often looks like it's compatible with any type T, but really it's compatible with any
+// type T which has a static const SkRecords::Type kType. That is to say, SkRecord is compatible
+// only with SkRecords::* structs defined in SkRecords.h. Your compiler will helpfully yell if you
+// get this wrong.
+
+class SkRecord : SkNoncopyable {
+public:
+ SkRecord(size_t chunkBytes = 4096, unsigned firstReserveCount = 64 / sizeof(void*))
+ : fAlloc(chunkBytes), fCount(0), fReserved(0), kFirstReserveCount(firstReserveCount) {}
+
+ ~SkRecord() {
+ Destroyer destroyer;
+ for (unsigned i = 0; i < this->count(); i++) {
+ this->mutate<void>(i, destroyer);
+ }
+ }
+
+ // Returns the number of canvas commands in this SkRecord.
+ unsigned count() const { return fCount; }
+
+ // Visit the i-th canvas command with a functor matching this interface:
+ // template <typename T>
+ // R operator()(const T& record) { ... }
+ // This operator() must be defined for at least all SkRecords::*.
+ template <typename R, typename F>
+ R visit(unsigned i, F& f) const {
+ SkASSERT(i < this->count());
+ return fRecords[i].visit<R>(fTypes[i], f);
+ }
+
+ // Mutate the i-th canvas command with a functor matching this interface:
+ // template <typename T>
+ // R operator()(T* record) { ... }
+ // This operator() must be defined for at least all SkRecords::*.
+ template <typename R, typename F>
+ R mutate(unsigned i, F& f) {
+ SkASSERT(i < this->count());
+ return fRecords[i].mutate<R>(fTypes[i], f);
+ }
+ // TODO: It'd be nice to infer R from F for visit and mutate if we ever get std::result_of.
+
+ // Allocate contiguous space for count Ts, to be freed when the SkRecord is destroyed.
+ // Here T can be any class, not just those from SkRecords. Throws on failure.
+ template <typename T>
+ T* alloc(unsigned count = 1) {
+ return (T*)fAlloc.allocThrow(sizeof(T) * count);
+ }
+
+ // Add a new command of type T to the end of this SkRecord.
+ // You are expected to placement new an object of type T onto this pointer.
+ template <typename T>
+ T* append() {
+ if (fCount == fReserved) {
+ fReserved = SkTMax(kFirstReserveCount, fReserved*2);
+ fRecords.realloc(fReserved);
+ fTypes.realloc(fReserved);
+ }
+
+ fTypes[fCount] = T::kType;
+ return fRecords[fCount++].set(this->allocCommand<T>());
+ }
+
+ // Replace the i-th command with a new command of type T.
+ // You are expected to placement new an object of type T onto this pointer.
+ // References to the original command are invalidated.
+ template <typename T>
+ T* replace(unsigned i) {
+ SkASSERT(i < this->count());
+
+ Destroyer destroyer;
+ this->mutate<void>(i, destroyer);
+
+ fTypes[i] = T::kType;
+ return fRecords[i].set(this->allocCommand<T>());
+ }
+
+ // Replace the i-th command with a new command of type T.
+ // You are expected to placement new an object of type T onto this pointer.
+ // You must show proof that you've already adopted the existing command.
+ template <typename T, typename Existing>
+ T* replace(unsigned i, const SkRecords::Adopted<Existing>& proofOfAdoption) {
+ SkASSERT(i < this->count());
+
+ SkASSERT(Existing::kType == fTypes[i]);
+ SkASSERT(proofOfAdoption == fRecords[i].ptr<Existing>());
+
+ fTypes[i] = T::kType;
+ return fRecords[i].set(this->allocCommand<T>());
+ }
+
+private:
+ // Implementation notes!
+ //
+ // Logically an SkRecord is structured as an array of pointers into a big chunk of memory where
+ // records representing each canvas draw call are stored:
+ //
+ // fRecords: [*][*][*]...
+ // | | |
+ // | | |
+ // | | +---------------------------------------+
+ // | +-----------------+ |
+ // | | |
+ // v v v
+ // fAlloc: [SkRecords::DrawRect][SkRecords::DrawPosTextH][SkRecords::DrawRect]...
+ //
+ // In the scheme above, the pointers in fRecords are void*: they have no type. The type is not
+ // stored in fAlloc either; we just write raw data there. But we need that type information.
+ // Here are some options:
+ // 1) use inheritance, virtuals, and vtables to make the fRecords pointers smarter
+ // 2) store the type data manually in fAlloc at the start of each record
+ // 3) store the type data manually somewhere with fRecords
+ //
+ // This code uses approach 3). The implementation feels very similar to 1), but it's
+ // devirtualized instead of using the language's polymorphism mechanisms. This lets us work
+ // with the types themselves (as SkRecords::Type), a sort of limited free RTTI; it lets us pay
+ // only 1 byte to store the type instead of a full pointer (4-8 bytes); and it leads to better
+ // decoupling between the SkRecords::* record types and the operations performed on them in
+ // visit() or mutate(). The recorded canvas calls don't have to have any idea about the
+ // operations performed on them.
+ //
+ // We store the types in a parallel fTypes array, mainly so that they can be tightly packed as
+ // single bytes. This has the side effect of allowing very fast analysis passes over an
+ // SkRecord looking for just patterns of draw commands (or using this as a quick reject
+ // mechanism) though there's admittedly not a very good API exposed publically for this.
+ //
+ // The cost to append a T into this structure is 1 + sizeof(void*) + sizeof(T).
+
+ // A mutator that can be used with replace to destroy canvas commands.
+ struct Destroyer {
+ template <typename T>
+ void operator()(T* record) { record->~T(); }
+ };
+
+ // Logically the same as SkRecords::Type, but packed into 8 bits.
+ struct Type8 {
+ public:
+ // This intentionally converts implicitly back and forth.
+ Type8(SkRecords::Type type) : fType(type) { SkASSERT(*this == type); }
+ operator SkRecords::Type () { return (SkRecords::Type)fType; }
+
+ private:
+ uint8_t fType;
+ };
+
+ // No point in allocating any more than one of an empty struct.
+ // We could just return NULL but it's sort of confusing to return NULL on success.
+ template <typename T>
+ SK_WHEN(SkTIsEmpty<T>, T*) allocCommand() {
+ static T singleton = {};
+ return &singleton;
+ }
+
+ template <typename T>
+ SK_WHEN(!SkTIsEmpty<T>, T*) allocCommand() { return this->alloc<T>(); }
+
+ // An untyped pointer to some bytes in fAlloc. This is the interface for polymorphic dispatch:
+ // visit() and mutate() work with the parallel fTypes array to do the work of a vtable.
+ struct Record {
+ public:
+ // Point this record to its data in fAlloc. Returns ptr for convenience.
+ template <typename T>
+ T* set(T* ptr) {
+ fPtr = ptr;
+ return ptr;
+ }
+
+ // Get the data in fAlloc, assuming it's of type T.
+ template <typename T>
+ T* ptr() const { return (T*)fPtr; }
+
+ // Visit this record with functor F (see public API above) assuming the record we're
+ // pointing to has this type.
+ template <typename R, typename F>
+ R visit(Type8 type, F& f) const {
+ #define CASE(T) case SkRecords::T##_Type: return f(*this->ptr<SkRecords::T>());
+ switch(type) { SK_RECORD_TYPES(CASE) }
+ #undef CASE
+ SkDEBUGFAIL("Unreachable");
+ return R();
+ }
+
+ // Mutate this record with functor F (see public API above) assuming the record we're
+ // pointing to has this type.
+ template <typename R, typename F>
+ R mutate(Type8 type, F& f) {
+ #define CASE(T) case SkRecords::T##_Type: return f(this->ptr<SkRecords::T>());
+ switch(type) { SK_RECORD_TYPES(CASE) }
+ #undef CASE
+ SkDEBUGFAIL("Unreachable");
+ return R();
+ }
+
+ private:
+ void* fPtr;
+ };
+
+ // fAlloc needs to be a data structure which can append variable length data in contiguous
+ // chunks, returning a stable handle to that data for later retrieval.
+ //
+ // fRecords and fTypes need to be data structures that can append fixed length data, and need to
+ // support efficient forward iteration. (They don't need to be contiguous or indexable.)
+
+ SkChunkAlloc fAlloc;
+ SkAutoTMalloc<Record> fRecords;
+ SkAutoTMalloc<Type8> fTypes;
+ // fCount and fReserved measure both fRecords and fTypes, which always grow in lock step.
+ unsigned fCount;
+ unsigned fReserved;
+ const unsigned kFirstReserveCount;
+};
+
+#endif//SkRecord_DEFINED
--- /dev/null
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkRecordDraw.h"
+
+void SkRecordDraw(const SkRecord& record, SkCanvas* canvas) {
+ for (SkRecords::Draw draw(canvas); draw.index() < record.count(); draw.next()) {
+ record.visit<void>(draw.index(), draw);
+ }
+}
+
+namespace SkRecords {
+
+bool Draw::skip(const PairedPushCull& r) {
+ if (fCanvas->quickReject(r.base->rect)) {
+ fIndex += r.skip;
+ return true;
+ }
+ return false;
+}
+
+bool Draw::skip(const BoundedDrawPosTextH& r) {
+ return fCanvas->quickRejectY(r.minY, r.maxY);
+}
+
+// NoOps draw nothing.
+template <> void Draw::draw(const NoOp&) {}
+
+#define DRAW(T, call) template <> void Draw::draw(const T& r) { fCanvas->call; }
+DRAW(Restore, restore());
+DRAW(Save, save(r.flags));
+DRAW(SaveLayer, saveLayer(r.bounds, r.paint, r.flags));
+DRAW(PopCull, popCull());
+DRAW(PushCull, pushCull(r.rect));
+DRAW(Clear, clear(r.color));
+DRAW(Concat, concat(r.matrix));
+DRAW(SetMatrix, setMatrix(SkMatrix::Concat(fInitialCTM, r.matrix)));
+
+DRAW(ClipPath, clipPath(r.path, r.op, r.doAA));
+DRAW(ClipRRect, clipRRect(r.rrect, r.op, r.doAA));
+DRAW(ClipRect, clipRect(r.rect, r.op, r.doAA));
+DRAW(ClipRegion, clipRegion(r.region, r.op));
+
+DRAW(DrawBitmap, drawBitmap(r.bitmap, r.left, r.top, r.paint));
+DRAW(DrawBitmapMatrix, drawBitmapMatrix(r.bitmap, r.matrix, r.paint));
+DRAW(DrawBitmapNine, drawBitmapNine(r.bitmap, r.center, r.dst, r.paint));
+DRAW(DrawBitmapRectToRect, drawBitmapRectToRect(r.bitmap, r.src, r.dst, r.paint, r.flags));
+DRAW(DrawDRRect, drawDRRect(r.outer, r.inner, r.paint));
+DRAW(DrawOval, drawOval(r.oval, r.paint));
+DRAW(DrawPaint, drawPaint(r.paint));
+DRAW(DrawPath, drawPath(r.path, r.paint));
+DRAW(DrawPoints, drawPoints(r.mode, r.count, r.pts, r.paint));
+DRAW(DrawPosText, drawPosText(r.text, r.byteLength, r.pos, r.paint));
+DRAW(DrawPosTextH, drawPosTextH(r.text, r.byteLength, r.xpos, r.y, r.paint));
+DRAW(DrawRRect, drawRRect(r.rrect, r.paint));
+DRAW(DrawRect, drawRect(r.rect, r.paint));
+DRAW(DrawSprite, drawSprite(r.bitmap, r.left, r.top, r.paint));
+DRAW(DrawText, drawText(r.text, r.byteLength, r.x, r.y, r.paint));
+DRAW(DrawTextOnPath, drawTextOnPath(r.text, r.byteLength, r.path, r.matrix, r.paint));
+DRAW(DrawVertices, drawVertices(r.vmode, r.vertexCount, r.vertices, r.texs, r.colors,
+ r.xmode.get(), r.indices, r.indexCount, r.paint));
+#undef DRAW
+
+template <> void Draw::draw(const PairedPushCull& r) { this->draw(*r.base); }
+template <> void Draw::draw(const BoundedDrawPosTextH& r) { this->draw(*r.base); }
+
+} // namespace SkRecords
--- /dev/null
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkRecordDraw_DEFINED
+#define SkRecordDraw_DEFINED
+
+#include "SkRecord.h"
+#include "SkCanvas.h"
+
+// Draw an SkRecord into an SkCanvas. A convenience wrapper around SkRecords::Draw.
+void SkRecordDraw(const SkRecord&, SkCanvas*);
+
+namespace SkRecords {
+
+// This is an SkRecord visitor that will draw that SkRecord to an SkCanvas.
+class Draw : SkNoncopyable {
+public:
+ explicit Draw(SkCanvas* canvas)
+ : fInitialCTM(canvas->getTotalMatrix()), fCanvas(canvas), fIndex(0) {}
+
+ unsigned index() const { return fIndex; }
+ void next() { ++fIndex; }
+
+ template <typename T> void operator()(const T& r) {
+ if (!this->skip(r)) {
+ this->draw(r);
+ }
+ }
+
+private:
+ // No base case, so we'll be compile-time checked that we implement all possibilities.
+ template <typename T> void draw(const T&);
+
+ // skip() should return true if we can skip this command, false if not.
+ // It may update fIndex directly to skip more than just this one command.
+
+ // Mostly we just blindly call fCanvas and let it handle quick rejects itself.
+ template <typename T> bool skip(const T&) { return false; }
+
+ // We add our own quick rejects for commands added by optimizations.
+ bool skip(const PairedPushCull&);
+ bool skip(const BoundedDrawPosTextH&);
+
+ const SkMatrix fInitialCTM;
+ SkCanvas* fCanvas;
+ unsigned fIndex;
+};
+
+} // namespace SkRecords
+
+#endif//SkRecordDraw_DEFINED
--- /dev/null
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkRecordOpts.h"
+
+#include "SkRecordPattern.h"
+#include "SkRecords.h"
+#include "SkTDArray.h"
+
+using namespace SkRecords;
+
+void SkRecordOptimize(SkRecord* record) {
+ // TODO(mtklein): fuse independent optimizations to reduce number of passes?
+ SkRecordNoopCulls(record);
+ SkRecordNoopSaveRestores(record);
+ // TODO(mtklein): figure out why we draw differently and reenable
+ //SkRecordNoopSaveLayerDrawRestores(record);
+
+ SkRecordAnnotateCullingPairs(record);
+ SkRecordReduceDrawPosTextStrength(record); // Helpful to run this before BoundDrawPosTextH.
+ SkRecordBoundDrawPosTextH(record);
+}
+
+// Most of the optimizations in this file are pattern-based. These are all defined as structs with:
+// - a Pattern typedef
+// - a bool onMatch(SkRceord*, Pattern*, unsigned begin, unsigned end) method,
+// which returns true if it made changes and false if not.
+
+// Run a pattern-based optimization once across the SkRecord, returning true if it made any changes.
+// It looks for spans which match Pass::Pattern, and when found calls onMatch() with the pattern,
+// record, and [begin,end) span of the commands that matched.
+template <typename Pass>
+static bool apply(Pass* pass, SkRecord* record) {
+ typename Pass::Pattern pattern;
+ bool changed = false;
+ unsigned begin, end = 0;
+
+ while (pattern.search(record, &begin, &end)) {
+ changed |= pass->onMatch(record, &pattern, begin, end);
+ }
+ return changed;
+}
+
+struct CullNooper {
+ typedef Pattern3<Is<PushCull>, Star<Is<NoOp> >, Is<PopCull> > Pattern;
+
+ bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned end) {
+ record->replace<NoOp>(begin); // PushCull
+ record->replace<NoOp>(end-1); // PopCull
+ return true;
+ }
+};
+
+void SkRecordNoopCulls(SkRecord* record) {
+ CullNooper pass;
+ while (apply(&pass, record));
+}
+
+// Turns the logical NoOp Save and Restore in Save-Draw*-Restore patterns into actual NoOps.
+struct SaveOnlyDrawsRestoreNooper {
+ typedef Pattern3<Is<Save>,
+ Star<Or<Is<NoOp>, IsDraw> >,
+ Is<Restore> >
+ Pattern;
+
+ bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned end) {
+ record->replace<NoOp>(begin); // Save
+ record->replace<NoOp>(end-1); // Restore
+ return true;
+ }
+};
+// Turns logical no-op Save-[non-drawing command]*-Restore patterns into actual no-ops.
+struct SaveNoDrawsRestoreNooper {
+ // Star matches greedily, so we also have to exclude Save and Restore.
+ typedef Pattern3<Is<Save>,
+ Star<Not<Or3<Is<Save>,
+ Is<Restore>,
+ IsDraw> > >,
+ Is<Restore> >
+ Pattern;
+
+ bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned end) {
+ // If restore doesn't revert both matrix and clip, this isn't safe to noop away.
+ if (pattern->first<Save>()->flags != SkCanvas::kMatrixClip_SaveFlag) {
+ return false;
+ }
+
+ // The entire span between Save and Restore (inclusively) does nothing.
+ for (unsigned i = begin; i < end; i++) {
+ record->replace<NoOp>(i);
+ }
+ return true;
+ }
+};
+void SkRecordNoopSaveRestores(SkRecord* record) {
+ SaveOnlyDrawsRestoreNooper onlyDraws;
+ SaveNoDrawsRestoreNooper noDraws;
+
+ // Run until they stop changing things.
+ while (apply(&onlyDraws, record) || apply(&noDraws, record));
+}
+
+// For some SaveLayer-[drawing command]-Restore patterns, merge the SaveLayer's alpha into the
+// draw, and no-op the SaveLayer and Restore.
+struct SaveLayerDrawRestoreNooper {
+ typedef Pattern3<Is<SaveLayer>, IsDraw, Is<Restore> > Pattern;
+
+ bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned end) {
+ SaveLayer* saveLayer = pattern->first<SaveLayer>();
+ if (saveLayer->bounds != NULL) {
+ // SaveLayer with bounds is too tricky for us.
+ return false;
+ }
+
+ SkPaint* layerPaint = saveLayer->paint;
+ if (NULL == layerPaint) {
+ // There wasn't really any point to this SaveLayer at all.
+ return KillSaveLayerAndRestore(record, begin);
+ }
+
+ SkPaint* drawPaint = pattern->second<SkPaint>();
+ if (drawPaint == NULL) {
+ // We can just give the draw the SaveLayer's paint.
+ // TODO(mtklein): figure out how to do this clearly
+ return false;
+ }
+
+ const uint32_t layerColor = layerPaint->getColor();
+ const uint32_t drawColor = drawPaint->getColor();
+ if (!IsOnlyAlpha(layerColor) || !IsOpaque(drawColor) ||
+ HasAnyEffect(*layerPaint) || HasAnyEffect(*drawPaint)) {
+ // Too fancy for us. Actually, as long as layerColor is just an alpha
+ // we can blend it into drawColor's alpha; drawColor doesn't strictly have to be opaque.
+ return false;
+ }
+
+ drawPaint->setColor(SkColorSetA(drawColor, SkColorGetA(layerColor)));
+ return KillSaveLayerAndRestore(record, begin);
+ }
+
+ static bool KillSaveLayerAndRestore(SkRecord* record, unsigned saveLayerIndex) {
+ record->replace<NoOp>(saveLayerIndex); // SaveLayer
+ record->replace<NoOp>(saveLayerIndex+2); // Restore
+ return true;
+ }
+
+ static bool HasAnyEffect(const SkPaint& paint) {
+ return paint.getPathEffect() ||
+ paint.getShader() ||
+ paint.getXfermode() ||
+ paint.getMaskFilter() ||
+ paint.getColorFilter() ||
+ paint.getRasterizer() ||
+ paint.getLooper() ||
+ paint.getImageFilter();
+ }
+
+ static bool IsOpaque(SkColor color) {
+ return SkColorGetA(color) == SK_AlphaOPAQUE;
+ }
+ static bool IsOnlyAlpha(SkColor color) {
+ return SK_ColorTRANSPARENT == SkColorSetA(color, SK_AlphaTRANSPARENT);
+ }
+};
+void SkRecordNoopSaveLayerDrawRestores(SkRecord* record) {
+ SaveLayerDrawRestoreNooper pass;
+ apply(&pass, record);
+}
+
+
+// Replaces DrawPosText with DrawPosTextH when all Y coordinates are equal.
+struct StrengthReducer {
+ typedef Pattern1<Is<DrawPosText> > Pattern;
+
+ bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned end) {
+ SkASSERT(end == begin + 1);
+ DrawPosText* draw = pattern->first<DrawPosText>();
+
+ const unsigned points = draw->paint.countText(draw->text, draw->byteLength);
+ if (points == 0) {
+ return false; // No point (ha!).
+ }
+
+ const SkScalar firstY = draw->pos[0].fY;
+ for (unsigned i = 1; i < points; i++) {
+ if (draw->pos[i].fY != firstY) {
+ return false; // Needs full power of DrawPosText.
+ }
+ }
+ // All ys are the same. We can replace DrawPosText with DrawPosTextH.
+
+ // draw->pos is points SkPoints, [(x,y),(x,y),(x,y),(x,y), ... ].
+ // We're going to squint and look at that as 2*points SkScalars, [x,y,x,y,x,y,x,y, ...].
+ // Then we'll rearrange things so all the xs are in order up front, clobbering the ys.
+ SK_COMPILE_ASSERT(sizeof(SkPoint) == 2 * sizeof(SkScalar), SquintingIsNotSafe);
+ SkScalar* scalars = &draw->pos[0].fX;
+ for (unsigned i = 0; i < 2*points; i += 2) {
+ scalars[i/2] = scalars[i];
+ }
+
+ // Extend lifetime of draw to the end of the loop so we can copy its paint.
+ Adopted<DrawPosText> adopted(draw);
+ SkNEW_PLACEMENT_ARGS(record->replace<DrawPosTextH>(begin, adopted),
+ DrawPosTextH,
+ (draw->paint, draw->text, draw->byteLength, scalars, firstY));
+ return true;
+ }
+};
+void SkRecordReduceDrawPosTextStrength(SkRecord* record) {
+ StrengthReducer pass;
+ apply(&pass, record);
+}
+
+// Tries to replace DrawPosTextH with BoundedDrawPosTextH, which knows conservative upper and lower
+// bounds to use with SkCanvas::quickRejectY.
+struct TextBounder {
+ typedef Pattern1<Is<DrawPosTextH> > Pattern;
+
+ bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned end) {
+ SkASSERT(end == begin + 1);
+ DrawPosTextH* draw = pattern->first<DrawPosTextH>();
+
+ // If we're drawing vertical text, none of the checks we're about to do make any sense.
+ // We'll need to call SkPaint::computeFastBounds() later, so bail if that's not possible.
+ if (draw->paint.isVerticalText() || !draw->paint.canComputeFastBounds()) {
+ return false;
+ }
+
+ // Rather than checking the top and bottom font metrics, we guess. Actually looking up the
+ // top and bottom metrics is slow, and this overapproximation should be good enough.
+ const SkScalar buffer = draw->paint.getTextSize() * 1.5f;
+ SkDEBUGCODE(SkPaint::FontMetrics metrics;)
+ SkDEBUGCODE(draw->paint.getFontMetrics(&metrics);)
+ SkASSERT(-buffer <= metrics.fTop);
+ SkASSERT(+buffer >= metrics.fBottom);
+
+ // Let the paint adjust the text bounds. We don't care about left and right here, so we use
+ // 0 and 1 respectively just so the bounds rectangle isn't empty.
+ SkRect bounds;
+ bounds.set(0, draw->y - buffer, SK_Scalar1, draw->y + buffer);
+ SkRect adjusted = draw->paint.computeFastBounds(bounds, &bounds);
+
+ Adopted<DrawPosTextH> adopted(draw);
+ SkNEW_PLACEMENT_ARGS(record->replace<BoundedDrawPosTextH>(begin, adopted),
+ BoundedDrawPosTextH,
+ (&adopted, adjusted.fTop, adjusted.fBottom));
+ return true;
+ }
+};
+void SkRecordBoundDrawPosTextH(SkRecord* record) {
+ TextBounder pass;
+ apply(&pass, record);
+}
+
+// Replaces PushCull with PairedPushCull, which lets us skip to the paired PopCull when the canvas
+// can quickReject the cull rect.
+// There's no efficient way (yet?) to express this one as a pattern, so we write a custom pass.
+class CullAnnotator {
+public:
+ // Do nothing to most ops.
+ template <typename T> void operator()(T*) {}
+
+ void operator()(PushCull* push) {
+ Pair pair = { fIndex, push };
+ fPushStack.push(pair);
+ }
+
+ void operator()(PopCull* pop) {
+ Pair push = fPushStack.top();
+ fPushStack.pop();
+
+ SkASSERT(fIndex > push.index);
+ unsigned skip = fIndex - push.index;
+
+ Adopted<PushCull> adopted(push.command);
+ SkNEW_PLACEMENT_ARGS(fRecord->replace<PairedPushCull>(push.index, adopted),
+ PairedPushCull, (&adopted, skip));
+ }
+
+ void apply(SkRecord* record) {
+ for (fRecord = record, fIndex = 0; fIndex < record->count(); fIndex++) {
+ fRecord->mutate<void>(fIndex, *this);
+ }
+ }
+
+private:
+ struct Pair {
+ unsigned index;
+ PushCull* command;
+ };
+
+ SkTDArray<Pair> fPushStack;
+ SkRecord* fRecord;
+ unsigned fIndex;
+};
+void SkRecordAnnotateCullingPairs(SkRecord* record) {
+ CullAnnotator pass;
+ pass.apply(record);
+}
--- /dev/null
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkRecordOpts_DEFINED
+#define SkRecordOpts_DEFINED
+
+#include "SkRecord.h"
+
+// Run all optimizations in recommended order.
+void SkRecordOptimize(SkRecord*);
+
+// NoOp away pointless PushCull/PopCull pairs with nothing between them.
+void SkRecordNoopCulls(SkRecord*);
+
+// Turns logical no-op Save-[non-drawing command]*-Restore patterns into actual no-ops.
+void SkRecordNoopSaveRestores(SkRecord*);
+
+// For some SaveLayer-[drawing command]-Restore patterns, merge the SaveLayer's alpha into the
+// draw, and no-op the SaveLayer and Restore.
+void SkRecordNoopSaveLayerDrawRestores(SkRecord*);
+
+// Annotates PushCull commands with the relative offset of their paired PopCull.
+void SkRecordAnnotateCullingPairs(SkRecord*);
+
+// Convert DrawPosText to DrawPosTextH when all the Y coordinates are equal.
+void SkRecordReduceDrawPosTextStrength(SkRecord*);
+
+// Calculate min and max Y bounds for DrawPosTextH commands, for use with SkCanvas::quickRejectY.
+void SkRecordBoundDrawPosTextH(SkRecord*);
+
+#endif//SkRecordOpts_DEFINED
--- /dev/null
+#ifndef SkRecordPattern_DEFINED
+#define SkRecordPattern_DEFINED
+
+#include "SkTLogic.h"
+
+namespace SkRecords {
+
+// First, some matchers. These match a single command in the SkRecord,
+// and may hang onto some data from it. If so, you can get the data by calling .get().
+
+// Matches a command of type T, and stores that command.
+template <typename T>
+class Is {
+public:
+ Is() : fPtr(NULL) {}
+
+ typedef T type;
+ type* get() { return fPtr; }
+
+ bool operator()(T* ptr) {
+ fPtr = ptr;
+ return true;
+ }
+
+ template <typename U>
+ bool operator()(U*) {
+ fPtr = NULL;
+ return false;
+ }
+
+private:
+ type* fPtr;
+};
+
+// Matches any command that draws, and stores its paint.
+class IsDraw {
+ SK_CREATE_MEMBER_DETECTOR(paint);
+public:
+ IsDraw() : fPaint(NULL) {}
+
+ typedef SkPaint type;
+ type* get() { return fPaint; }
+
+ template <typename T>
+ SK_WHEN(HasMember_paint<T>, bool) operator()(T* draw) {
+ fPaint = AsPtr(draw->paint);
+ return true;
+ }
+
+ template <typename T>
+ SK_WHEN(!HasMember_paint<T>, bool) operator()(T*) {
+ fPaint = NULL;
+ return false;
+ }
+
+ // SaveLayer has an SkPaint named paint, but it's not a draw.
+ bool operator()(SaveLayer*) {
+ fPaint = NULL;
+ return false;
+ }
+
+private:
+ // Abstracts away whether the paint is always part of the command or optional.
+ template <typename T> static T* AsPtr(SkRecords::Optional<T>& x) { return x; }
+ template <typename T> static T* AsPtr(T& x) { return &x; }
+
+ type* fPaint;
+};
+
+// Matches if Matcher doesn't. Stores nothing.
+template <typename Matcher>
+struct Not {
+ template <typename T>
+ bool operator()(T* ptr) { return !Matcher()(ptr); }
+};
+
+// Matches if either of A or B does. Stores nothing.
+template <typename A, typename B>
+struct Or {
+ template <typename T>
+ bool operator()(T* ptr) { return A()(ptr) || B()(ptr); }
+};
+
+// Matches if any of A, B or C does. Stores nothing.
+template <typename A, typename B, typename C>
+struct Or3 : Or<A, Or<B, C> > {};
+
+// Star is a special matcher that greedily matches Matcher 0 or more times. Stores nothing.
+template <typename Matcher>
+struct Star {
+ template <typename T>
+ bool operator()(T* ptr) { return Matcher()(ptr); }
+};
+
+// Cons builds a list of Matchers.
+// It first matches Matcher (something from above), then Pattern (another Cons or Nil).
+//
+// This is the main entry point to pattern matching, and so provides a couple of extra API bits:
+// - search scans through the record to look for matches;
+// - first, second, and third return the data stored by their respective matchers in the pattern.
+//
+// These Cons build lists analogously to Lisp's "cons". See Pattern# for the "list" equivalent.
+template <typename Matcher, typename Pattern>
+class Cons {
+public:
+ // If this pattern matches the SkRecord starting at i,
+ // return the index just past the end of the pattern, otherwise return 0.
+ SK_ALWAYS_INLINE unsigned match(SkRecord* record, unsigned i) {
+ i = this->matchHead(&fHead, record, i);
+ return i == 0 ? 0 : fTail.match(record, i);
+ }
+
+ // Starting from *end, walk through the SkRecord to find the first span matching this pattern.
+ // If there is no such span, return false. If there is, return true and set [*begin, *end).
+ SK_ALWAYS_INLINE bool search(SkRecord* record, unsigned* begin, unsigned* end) {
+ for (*begin = *end; *begin < record->count(); ++(*begin)) {
+ *end = this->match(record, *begin);
+ if (*end != 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Once either match or search has succeeded, access the stored data of the first, second,
+ // or third matcher in this pattern. Add as needed for longer patterns.
+ // T is checked statically at compile time; no casting is involved. It's just an API wart.
+ template <typename T> T* first() { return fHead.get(); }
+ template <typename T> T* second() { return fTail.fHead.get(); }
+ template <typename T> T* third() { return fTail.fTail.fHead.get(); }
+
+private:
+ // If head isn't a Star, try to match at i once.
+ template <typename T>
+ unsigned matchHead(T*, SkRecord* record, unsigned i) {
+ if (i < record->count()) {
+ if (record->mutate<bool>(i, fHead)) {
+ return i+1;
+ }
+ }
+ return 0;
+ }
+
+ // If head is a Star, walk i until it doesn't match.
+ template <typename T>
+ unsigned matchHead(Star<T>*, SkRecord* record, unsigned i) {
+ while (i < record->count()) {
+ if (!record->mutate<bool>(i, fHead)) {
+ return i;
+ }
+ i++;
+ }
+ return 0;
+ }
+
+ Matcher fHead;
+ Pattern fTail;
+
+ // All Cons are friends with each other. This lets first, second, and third work.
+ template <typename, typename> friend class Cons;
+};
+
+// Nil is the end of every pattern Cons chain.
+struct Nil {
+ // Bottoms out recursion down the fTail chain. Just return whatever i the front decided on.
+ unsigned match(SkRecord*, unsigned i) { return i; }
+};
+
+// These Pattern# types are syntax sugar over Cons and Nil, just to help eliminate some of the
+// template noise. Use these if you can. Feel free to add more for longer patterns.
+// All types A, B, C, ... are Matchers.
+template <typename A>
+struct Pattern1 : Cons<A, Nil> {};
+
+template <typename A, typename B>
+struct Pattern2 : Cons<A, Pattern1<B> > {};
+
+template <typename A, typename B, typename C>
+struct Pattern3 : Cons<A, Pattern2<B, C> > {};
+
+} // namespace SkRecords
+
+#endif//SkRecordPattern_DEFINED
--- /dev/null
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkRecorder.h"
+#include "SkPicture.h"
+
+// SkCanvas will fail in mysterious ways if it doesn't know the real width and height.
+SkRecorder::SkRecorder(SkRecord* record, int width, int height)
+ : SkCanvas(width, height), fRecord(record) {}
+
+void SkRecorder::forgetRecord() {
+ fRecord = NULL;
+}
+
+// To make appending to fRecord a little less verbose.
+#define APPEND(T, ...) \
+ SkNEW_PLACEMENT_ARGS(fRecord->append<SkRecords::T>(), SkRecords::T, (__VA_ARGS__))
+
+// For methods which must call back into SkCanvas.
+#define INHERITED(method, ...) this->SkCanvas::method(__VA_ARGS__)
+
+// The structs we're creating all copy their constructor arguments. Given the way the SkRecords
+// framework works, sometimes they happen to technically be copied twice, which is fine and elided
+// into a single copy unless the class has a non-trivial copy constructor. For classes with
+// non-trivial copy constructors, we skip the first copy (and its destruction) by wrapping the value
+// with delay_copy(), forcing the argument to be passed by const&.
+//
+// This is used below for SkBitmap, SkPaint, SkPath, and SkRegion, which all have non-trivial copy
+// constructors and destructors. You'll know you've got a good candidate T if you see ~T() show up
+// unexpectedly on a profile of record time. Otherwise don't bother.
+template <typename T>
+class Reference {
+public:
+ Reference(const T& x) : fX(x) {}
+ operator const T&() const { return fX; }
+private:
+ const T& fX;
+};
+
+template <typename T>
+static Reference<T> delay_copy(const T& x) { return Reference<T>(x); }
+
+// Use copy() only for optional arguments, to be copied if present or skipped if not.
+// (For most types we just pass by value and let copy constructors do their thing.)
+template <typename T>
+T* SkRecorder::copy(const T* src) {
+ if (NULL == src) {
+ return NULL;
+ }
+ return SkNEW_PLACEMENT_ARGS(fRecord->alloc<T>(), T, (*src));
+}
+
+// This copy() is for arrays.
+// It will work with POD or non-POD, though currently we only use it for POD.
+template <typename T>
+T* SkRecorder::copy(const T src[], unsigned count) {
+ if (NULL == src) {
+ return NULL;
+ }
+ T* dst = fRecord->alloc<T>(count);
+ for (unsigned i = 0; i < count; i++) {
+ SkNEW_PLACEMENT_ARGS(dst + i, T, (src[i]));
+ }
+ return dst;
+}
+
+// Specialization for copying strings, using memcpy.
+// This measured around 2x faster for copying code points,
+// but I found no corresponding speedup for other arrays.
+template <>
+char* SkRecorder::copy(const char src[], unsigned count) {
+ if (NULL == src) {
+ return NULL;
+ }
+ char* dst = fRecord->alloc<char>(count);
+ memcpy(dst, src, count);
+ return dst;
+}
+
+void SkRecorder::clear(SkColor color) {
+ APPEND(Clear, color);
+}
+
+void SkRecorder::drawPaint(const SkPaint& paint) {
+ APPEND(DrawPaint, delay_copy(paint));
+}
+
+void SkRecorder::drawPoints(PointMode mode,
+ size_t count,
+ const SkPoint pts[],
+ const SkPaint& paint) {
+ APPEND(DrawPoints, delay_copy(paint), mode, count, this->copy(pts, count));
+}
+
+void SkRecorder::drawRect(const SkRect& rect, const SkPaint& paint) {
+ APPEND(DrawRect, delay_copy(paint), rect);
+}
+
+void SkRecorder::drawOval(const SkRect& oval, const SkPaint& paint) {
+ APPEND(DrawOval, delay_copy(paint), oval);
+}
+
+void SkRecorder::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
+ APPEND(DrawRRect, delay_copy(paint), rrect);
+}
+
+void SkRecorder::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) {
+ APPEND(DrawDRRect, delay_copy(paint), outer, inner);
+}
+
+void SkRecorder::drawPath(const SkPath& path, const SkPaint& paint) {
+ APPEND(DrawPath, delay_copy(paint), delay_copy(path));
+}
+
+void SkRecorder::drawBitmap(const SkBitmap& bitmap,
+ SkScalar left,
+ SkScalar top,
+ const SkPaint* paint) {
+ APPEND(DrawBitmap, this->copy(paint), delay_copy(bitmap), left, top);
+}
+
+void SkRecorder::drawBitmapRectToRect(const SkBitmap& bitmap,
+ const SkRect* src,
+ const SkRect& dst,
+ const SkPaint* paint,
+ DrawBitmapRectFlags flags) {
+ APPEND(DrawBitmapRectToRect,
+ this->copy(paint), delay_copy(bitmap), this->copy(src), dst, flags);
+}
+
+void SkRecorder::drawBitmapMatrix(const SkBitmap& bitmap,
+ const SkMatrix& matrix,
+ const SkPaint* paint) {
+ APPEND(DrawBitmapMatrix, this->copy(paint), delay_copy(bitmap), matrix);
+}
+
+void SkRecorder::drawBitmapNine(const SkBitmap& bitmap,
+ const SkIRect& center,
+ const SkRect& dst,
+ const SkPaint* paint) {
+ APPEND(DrawBitmapNine, this->copy(paint), delay_copy(bitmap), center, dst);
+}
+
+void SkRecorder::drawSprite(const SkBitmap& bitmap, int left, int top, const SkPaint* paint) {
+ APPEND(DrawSprite, this->copy(paint), delay_copy(bitmap), left, top);
+}
+
+void SkRecorder::onDrawText(const void* text, size_t byteLength,
+ SkScalar x, SkScalar y, const SkPaint& paint) {
+ APPEND(DrawText,
+ delay_copy(paint), this->copy((const char*)text, byteLength), byteLength, x, y);
+}
+
+void SkRecorder::onDrawPosText(const void* text, size_t byteLength,
+ const SkPoint pos[], const SkPaint& paint) {
+ const unsigned points = paint.countText(text, byteLength);
+ APPEND(DrawPosText,
+ delay_copy(paint),
+ this->copy((const char*)text, byteLength),
+ byteLength,
+ this->copy(pos, points));
+}
+
+void SkRecorder::onDrawPosTextH(const void* text, size_t byteLength,
+ const SkScalar xpos[], SkScalar constY, const SkPaint& paint) {
+ const unsigned points = paint.countText(text, byteLength);
+ APPEND(DrawPosTextH,
+ delay_copy(paint),
+ this->copy((const char*)text, byteLength),
+ byteLength,
+ this->copy(xpos, points),
+ constY);
+}
+
+void SkRecorder::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
+ const SkMatrix* matrix, const SkPaint& paint) {
+ APPEND(DrawTextOnPath,
+ delay_copy(paint),
+ this->copy((const char*)text, byteLength),
+ byteLength,
+ delay_copy(path),
+ this->copy(matrix));
+}
+
+void SkRecorder::onDrawPicture(const SkPicture* picture) {
+ picture->draw(this);
+}
+
+void SkRecorder::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) {
+ APPEND(DrawVertices, delay_copy(paint),
+ vmode,
+ vertexCount,
+ this->copy(vertices, vertexCount),
+ texs ? this->copy(texs, vertexCount) : NULL,
+ colors ? this->copy(colors, vertexCount) : NULL,
+ xmode,
+ this->copy(indices, indexCount),
+ indexCount);
+}
+
+void SkRecorder::willSave(SkCanvas::SaveFlags flags) {
+ APPEND(Save, flags);
+ INHERITED(willSave, flags);
+}
+
+SkCanvas::SaveLayerStrategy SkRecorder::willSaveLayer(const SkRect* bounds,
+ const SkPaint* paint,
+ SkCanvas::SaveFlags flags) {
+ APPEND(SaveLayer, this->copy(bounds), this->copy(paint), flags);
+ INHERITED(willSaveLayer, bounds, paint, flags);
+ return SkCanvas::kNoLayer_SaveLayerStrategy;
+}
+
+void SkRecorder::willRestore() {
+ APPEND(Restore);
+ INHERITED(willRestore);
+}
+
+void SkRecorder::onPushCull(const SkRect& rect) {
+ APPEND(PushCull, rect);
+}
+
+void SkRecorder::onPopCull() {
+ APPEND(PopCull);
+}
+
+void SkRecorder::didConcat(const SkMatrix& matrix) {
+ APPEND(Concat, matrix);
+ INHERITED(didConcat, matrix);
+}
+
+void SkRecorder::didSetMatrix(const SkMatrix& matrix) {
+ APPEND(SetMatrix, matrix);
+ INHERITED(didSetMatrix, matrix);
+}
+
+void SkRecorder::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
+ APPEND(ClipRect, rect, op, edgeStyle == kSoft_ClipEdgeStyle);
+ INHERITED(onClipRect, rect, op, edgeStyle);
+}
+
+void SkRecorder::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
+ APPEND(ClipRRect, rrect, op, edgeStyle == kSoft_ClipEdgeStyle);
+ INHERITED(updateClipConservativelyUsingBounds, rrect.getBounds(), op, false);
+}
+
+void SkRecorder::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
+ APPEND(ClipPath, delay_copy(path), op, edgeStyle == kSoft_ClipEdgeStyle);
+ INHERITED(updateClipConservativelyUsingBounds, path.getBounds(), op, path.isInverseFillType());
+}
+
+void SkRecorder::onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) {
+ APPEND(ClipRegion, delay_copy(deviceRgn), op);
+ INHERITED(onClipRegion, deviceRgn, op);
+}
--- /dev/null
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkRecorder_DEFINED
+#define SkRecorder_DEFINED
+
+#include "SkCanvas.h"
+#include "SkRecord.h"
+#include "SkRecords.h"
+
+// SkRecorder provides an SkCanvas interface for recording into an SkRecord.
+
+class SkRecorder : public SkCanvas {
+public:
+ // Does not take ownership of the SkRecord.
+ SkRecorder(SkRecord*, int width, int height);
+
+ // Make SkRecorder forget entirely about its SkRecord*; all calls to SkRecorder will fail.
+ void forgetRecord();
+
+ void clear(SkColor) SK_OVERRIDE;
+ void drawPaint(const SkPaint& paint) SK_OVERRIDE;
+ void drawPoints(PointMode mode,
+ size_t count,
+ const SkPoint pts[],
+ const SkPaint& paint) SK_OVERRIDE;
+ void drawRect(const SkRect& rect, const SkPaint& paint) SK_OVERRIDE;
+ void drawOval(const SkRect& oval, const SkPaint&) SK_OVERRIDE;
+ void drawRRect(const SkRRect& rrect, const SkPaint& paint) SK_OVERRIDE;
+ void drawPath(const SkPath& path, const SkPaint& paint) SK_OVERRIDE;
+ void drawBitmap(const SkBitmap& bitmap,
+ SkScalar left,
+ SkScalar top,
+ const SkPaint* paint = NULL) SK_OVERRIDE;
+ void drawBitmapRectToRect(const SkBitmap& bitmap,
+ const SkRect* src,
+ const SkRect& dst,
+ const SkPaint* paint = NULL,
+ DrawBitmapRectFlags flags = kNone_DrawBitmapRectFlag) SK_OVERRIDE;
+ void drawBitmapMatrix(const SkBitmap& bitmap,
+ const SkMatrix& m,
+ const SkPaint* paint = NULL) SK_OVERRIDE;
+ void drawBitmapNine(const SkBitmap& bitmap,
+ const SkIRect& center,
+ const SkRect& dst,
+ const SkPaint* paint = NULL) SK_OVERRIDE;
+ void drawSprite(const SkBitmap& bitmap,
+ int left,
+ int top,
+ const SkPaint* paint = NULL) SK_OVERRIDE;
+ void 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) SK_OVERRIDE;
+
+ void willSave(SkCanvas::SaveFlags) SK_OVERRIDE;
+ SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SkCanvas::SaveFlags) SK_OVERRIDE;
+ void willRestore() SK_OVERRIDE;
+
+ void didConcat(const SkMatrix&) SK_OVERRIDE;
+ void didSetMatrix(const SkMatrix&) SK_OVERRIDE;
+
+ void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) SK_OVERRIDE;
+ void onDrawText(const void* text,
+ size_t byteLength,
+ SkScalar x,
+ SkScalar y,
+ const SkPaint& paint) SK_OVERRIDE;
+ void onDrawPosText(const void* text,
+ size_t byteLength,
+ const SkPoint pos[],
+ const SkPaint& paint) SK_OVERRIDE;
+ void onDrawPosTextH(const void* text,
+ size_t byteLength,
+ const SkScalar xpos[],
+ SkScalar constY,
+ const SkPaint& paint) SK_OVERRIDE;
+ void onDrawTextOnPath(const void* text,
+ size_t byteLength,
+ const SkPath& path,
+ const SkMatrix* matrix,
+ const SkPaint& paint) SK_OVERRIDE;
+ void onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) SK_OVERRIDE;
+ void onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) SK_OVERRIDE;
+ void onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) SK_OVERRIDE;
+ void onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) SK_OVERRIDE;
+
+ void onDrawPicture(const SkPicture* picture) SK_OVERRIDE;
+
+ void onPushCull(const SkRect& cullRect) SK_OVERRIDE;
+ void onPopCull() SK_OVERRIDE;
+
+private:
+ template <typename T>
+ T* copy(const T*);
+
+ template <typename T>
+ T* copy(const T[], unsigned count);
+
+ SkRecord* fRecord;
+};
+
+#endif//SkRecorder_DEFINED
--- /dev/null
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "../../include/record/SkRecording.h"
+
+#include "SkRecord.h"
+#include "SkRecordOpts.h"
+#include "SkRecordDraw.h"
+#include "SkRecorder.h"
+
+namespace EXPERIMENTAL {
+
+SkPlayback::SkPlayback(const SkRecord* record) : fRecord(record) {}
+
+SkPlayback::~SkPlayback() {}
+
+void SkPlayback::draw(SkCanvas* canvas) const {
+ SkASSERT(fRecord.get() != NULL);
+ SkRecordDraw(*fRecord, canvas);
+}
+
+SkRecording::SkRecording(int width, int height)
+ : fRecord(SkNEW(SkRecord))
+ , fRecorder(SkNEW_ARGS(SkRecorder, (fRecord.get(), width, height)))
+ {}
+
+SkPlayback* SkRecording::releasePlayback() {
+ SkASSERT(fRecorder->unique());
+ fRecorder->forgetRecord();
+ SkRecordOptimize(fRecord.get());
+ return SkNEW_ARGS(SkPlayback, (fRecord.detach()));
+}
+
+SkRecording::~SkRecording() {}
+
+SkCanvas* SkRecording::canvas() {
+ return fRecord.get() ? fRecorder.get() : NULL;
+}
+
+} // namespace EXPERIMENTAL
--- /dev/null
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkRecords_DEFINED
+#define SkRecords_DEFINED
+
+#include "SkCanvas.h"
+
+namespace SkRecords {
+
+// A list of all the types of canvas calls we can record.
+// Each of these is reified into a struct below.
+//
+// (We're using the macro-of-macro trick here to do several different things with the same list.)
+//
+// We leave this SK_RECORD_TYPES macro defined for use by code that wants to operate on SkRecords
+// types polymorphically. (See SkRecord::Record::{visit,mutate} for an example.)
+//
+// Order doesn't technically matter here, but the compiler can generally generate better code if
+// you keep them semantically grouped, especially the Draws. It's also nice to leave NoOp at 0.
+#define SK_RECORD_TYPES(M) \
+ M(NoOp) \
+ M(Restore) \
+ M(Save) \
+ M(SaveLayer) \
+ M(PushCull) \
+ M(PopCull) \
+ M(PairedPushCull) /*From SkRecordAnnotateCullingPairs*/ \
+ M(Concat) \
+ M(SetMatrix) \
+ M(ClipPath) \
+ M(ClipRRect) \
+ M(ClipRect) \
+ M(ClipRegion) \
+ M(Clear) \
+ M(DrawBitmap) \
+ M(DrawBitmapMatrix) \
+ M(DrawBitmapNine) \
+ M(DrawBitmapRectToRect) \
+ M(DrawDRRect) \
+ M(DrawOval) \
+ M(DrawPaint) \
+ M(DrawPath) \
+ M(DrawPoints) \
+ M(DrawPosText) \
+ M(DrawPosTextH) \
+ M(DrawRRect) \
+ M(DrawRect) \
+ M(DrawSprite) \
+ M(DrawText) \
+ M(DrawTextOnPath) \
+ M(DrawVertices) \
+ M(BoundedDrawPosTextH) /*From SkRecordBoundDrawPosTextH*/
+
+// Defines SkRecords::Type, an enum of all record types.
+#define ENUM(T) T##_Type,
+enum Type { SK_RECORD_TYPES(ENUM) };
+#undef ENUM
+
+// Macros to make it easier to define a record for a draw call with 0 args, 1 args, 2 args, etc.
+// These should be clearer when you look at their use below.
+#define RECORD0(T) \
+struct T { \
+ static const Type kType = T##_Type; \
+};
+
+// We try to be flexible about the types the constructors take. Instead of requring the exact type
+// A here, we take any type Z which implicitly casts to A. This allows the delay_copy() trick to
+// work, allowing the caller to decide whether to pass by value or by const&.
+
+#define RECORD1(T, A, a) \
+struct T { \
+ static const Type kType = T##_Type; \
+ template <typename Z> \
+ T(Z a) : a(a) {} \
+ A a; \
+};
+
+#define RECORD2(T, A, a, B, b) \
+struct T { \
+ static const Type kType = T##_Type; \
+ template <typename Z, typename Y> \
+ T(Z a, Y b) : a(a), b(b) {} \
+ A a; B b; \
+};
+
+#define RECORD3(T, A, a, B, b, C, c) \
+struct T { \
+ static const Type kType = T##_Type; \
+ template <typename Z, typename Y, typename X> \
+ T(Z a, Y b, X c) : a(a), b(b), c(c) {} \
+ A a; B b; C c; \
+};
+
+#define RECORD4(T, A, a, B, b, C, c, D, d) \
+struct T { \
+ static const Type kType = T##_Type; \
+ template <typename Z, typename Y, typename X, typename W> \
+ T(Z a, Y b, X c, W d) : a(a), b(b), c(c), d(d) {} \
+ A a; B b; C c; D d; \
+};
+
+#define RECORD5(T, A, a, B, b, C, c, D, d, E, e) \
+struct T { \
+ static const Type kType = T##_Type; \
+ template <typename Z, typename Y, typename X, typename W, typename V> \
+ T(Z a, Y b, X c, W d, V e) : a(a), b(b), c(c), d(d), e(e) {} \
+ A a; B b; C c; D d; E e; \
+};
+
+#define ACT_AS_PTR(ptr) \
+ operator T*() { return ptr; } \
+ operator const T*() const { return ptr; } \
+ T* operator->() { return ptr; } \
+ const T* operator->() const { return ptr; }
+
+// An Optional doesn't own the pointer's memory, but may need to destroy non-POD data.
+template <typename T>
+class Optional : SkNoncopyable {
+public:
+ Optional(T* ptr) : fPtr(ptr) {}
+ ~Optional() { if (fPtr) fPtr->~T(); }
+
+ ACT_AS_PTR(fPtr);
+private:
+ T* fPtr;
+};
+
+// Like Optional, but ptr must not be NULL.
+template <typename T>
+class Adopted : SkNoncopyable {
+public:
+ Adopted(T* ptr) : fPtr(ptr) { SkASSERT(fPtr); }
+ Adopted(Adopted* source) {
+ // Transfer ownership from source to this.
+ fPtr = source->fPtr;
+ source->fPtr = NULL;
+ }
+ ~Adopted() { if (fPtr) fPtr->~T(); }
+
+ ACT_AS_PTR(fPtr);
+private:
+ T* fPtr;
+};
+
+// PODArray doesn't own the pointer's memory, and we assume the data is POD.
+template <typename T>
+class PODArray {
+public:
+ PODArray(T* ptr) : fPtr(ptr) {}
+ // Default copy and assign.
+
+ ACT_AS_PTR(fPtr);
+private:
+ T* fPtr;
+};
+
+#undef ACT_AS_PTR
+
+// Like SkBitmap, but deep copies pixels if they're not immutable.
+// Using this, we guarantee the immutability of all bitmaps we record.
+class ImmutableBitmap {
+public:
+ explicit ImmutableBitmap(const SkBitmap& bitmap) {
+ if (bitmap.isImmutable()) {
+ fBitmap = bitmap;
+ } else {
+ bitmap.copyTo(&fBitmap);
+ }
+ fBitmap.setImmutable();
+ }
+
+ operator const SkBitmap& () const { return fBitmap; }
+
+private:
+ SkBitmap fBitmap;
+};
+
+RECORD0(NoOp);
+
+RECORD0(Restore);
+RECORD1(Save, SkCanvas::SaveFlags, flags);
+RECORD3(SaveLayer, Optional<SkRect>, bounds, Optional<SkPaint>, paint, SkCanvas::SaveFlags, flags);
+
+RECORD1(PushCull, SkRect, rect);
+RECORD0(PopCull);
+
+RECORD1(Concat, SkMatrix, matrix);
+RECORD1(SetMatrix, SkMatrix, matrix);
+
+RECORD3(ClipPath, SkPath, path, SkRegion::Op, op, bool, doAA);
+RECORD3(ClipRRect, SkRRect, rrect, SkRegion::Op, op, bool, doAA);
+RECORD3(ClipRect, SkRect, rect, SkRegion::Op, op, bool, doAA);
+RECORD2(ClipRegion, SkRegion, region, SkRegion::Op, op);
+
+RECORD1(Clear, SkColor, color);
+// While not strictly required, if you have an SkPaint, it's fastest to put it first.
+RECORD4(DrawBitmap, Optional<SkPaint>, paint,
+ ImmutableBitmap, bitmap,
+ SkScalar, left,
+ SkScalar, top);
+RECORD3(DrawBitmapMatrix, Optional<SkPaint>, paint, ImmutableBitmap, bitmap, SkMatrix, matrix);
+RECORD4(DrawBitmapNine, Optional<SkPaint>, paint,
+ ImmutableBitmap, bitmap,
+ SkIRect, center,
+ SkRect, dst);
+RECORD5(DrawBitmapRectToRect, Optional<SkPaint>, paint,
+ ImmutableBitmap, bitmap,
+ Optional<SkRect>, src,
+ SkRect, dst,
+ SkCanvas::DrawBitmapRectFlags, flags);
+RECORD3(DrawDRRect, SkPaint, paint, SkRRect, outer, SkRRect, inner);
+RECORD2(DrawOval, SkPaint, paint, SkRect, oval);
+RECORD1(DrawPaint, SkPaint, paint);
+RECORD2(DrawPath, SkPaint, paint, SkPath, path);
+RECORD4(DrawPoints, SkPaint, paint, SkCanvas::PointMode, mode, size_t, count, SkPoint*, pts);
+RECORD4(DrawPosText, SkPaint, paint,
+ PODArray<char>, text,
+ size_t, byteLength,
+ PODArray<SkPoint>, pos);
+RECORD5(DrawPosTextH, SkPaint, paint,
+ PODArray<char>, text,
+ size_t, byteLength,
+ PODArray<SkScalar>, xpos,
+ SkScalar, y);
+RECORD2(DrawRRect, SkPaint, paint, SkRRect, rrect);
+RECORD2(DrawRect, SkPaint, paint, SkRect, rect);
+RECORD4(DrawSprite, Optional<SkPaint>, paint, ImmutableBitmap, bitmap, int, left, int, top);
+RECORD5(DrawText, SkPaint, paint,
+ PODArray<char>, text,
+ size_t, byteLength,
+ SkScalar, x,
+ SkScalar, y);
+RECORD5(DrawTextOnPath, SkPaint, paint,
+ PODArray<char>, text,
+ size_t, byteLength,
+ SkPath, path,
+ Optional<SkMatrix>, matrix);
+
+// This guy is so ugly we just write it manually.
+struct DrawVertices {
+ static const Type kType = DrawVertices_Type;
+
+ DrawVertices(const SkPaint& paint,
+ SkCanvas::VertexMode vmode,
+ int vertexCount,
+ SkPoint* vertices,
+ SkPoint* texs,
+ SkColor* colors,
+ SkXfermode* xmode,
+ uint16_t* indices,
+ int indexCount)
+ : paint(paint)
+ , vmode(vmode)
+ , vertexCount(vertexCount)
+ , vertices(vertices)
+ , texs(texs)
+ , colors(colors)
+ , xmode(SkSafeRef(xmode))
+ , indices(indices)
+ , indexCount(indexCount) {}
+
+ SkPaint paint;
+ SkCanvas::VertexMode vmode;
+ int vertexCount;
+ PODArray<SkPoint> vertices;
+ PODArray<SkPoint> texs;
+ PODArray<SkColor> colors;
+ SkAutoTUnref<SkXfermode> xmode;
+ PODArray<uint16_t> indices;
+ int indexCount;
+};
+
+// Records added by optimizations.
+RECORD2(PairedPushCull, Adopted<PushCull>, base, unsigned, skip);
+RECORD3(BoundedDrawPosTextH, Adopted<DrawPosTextH>, base, SkScalar, minY, SkScalar, maxY);
+
+#undef RECORD0
+#undef RECORD1
+#undef RECORD2
+#undef RECORD3
+#undef RECORD4
+#undef RECORD5
+
+} // namespace SkRecords
+
+#endif//SkRecords_DEFINED
+++ /dev/null
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkRecord_DEFINED
-#define SkRecord_DEFINED
-
-#include "SkChunkAlloc.h"
-#include "SkRecords.h"
-#include "SkTLogic.h"
-#include "SkTemplates.h"
-
-// SkRecord (REC-ord) represents a sequence of SkCanvas calls, saved for future use.
-// These future uses may include: replay, optimization, serialization, or combinations of those.
-//
-// Though an enterprising user may find calling alloc(), append(), visit(), and mutate() enough to
-// work with SkRecord, you probably want to look at SkRecorder which presents an SkCanvas interface
-// for creating an SkRecord, and SkRecordDraw which plays an SkRecord back into another SkCanvas.
-//
-// SkRecord often looks like it's compatible with any type T, but really it's compatible with any
-// type T which has a static const SkRecords::Type kType. That is to say, SkRecord is compatible
-// only with SkRecords::* structs defined in SkRecords.h. Your compiler will helpfully yell if you
-// get this wrong.
-
-class SkRecord : SkNoncopyable {
-public:
- SkRecord(size_t chunkBytes = 4096, unsigned firstReserveCount = 64 / sizeof(void*))
- : fAlloc(chunkBytes), fCount(0), fReserved(0), kFirstReserveCount(firstReserveCount) {}
-
- ~SkRecord() {
- Destroyer destroyer;
- for (unsigned i = 0; i < this->count(); i++) {
- this->mutate<void>(i, destroyer);
- }
- }
-
- // Returns the number of canvas commands in this SkRecord.
- unsigned count() const { return fCount; }
-
- // Visit the i-th canvas command with a functor matching this interface:
- // template <typename T>
- // R operator()(const T& record) { ... }
- // This operator() must be defined for at least all SkRecords::*.
- template <typename R, typename F>
- R visit(unsigned i, F& f) const {
- SkASSERT(i < this->count());
- return fRecords[i].visit<R>(fTypes[i], f);
- }
-
- // Mutate the i-th canvas command with a functor matching this interface:
- // template <typename T>
- // R operator()(T* record) { ... }
- // This operator() must be defined for at least all SkRecords::*.
- template <typename R, typename F>
- R mutate(unsigned i, F& f) {
- SkASSERT(i < this->count());
- return fRecords[i].mutate<R>(fTypes[i], f);
- }
- // TODO: It'd be nice to infer R from F for visit and mutate if we ever get std::result_of.
-
- // Allocate contiguous space for count Ts, to be freed when the SkRecord is destroyed.
- // Here T can be any class, not just those from SkRecords. Throws on failure.
- template <typename T>
- T* alloc(unsigned count = 1) {
- return (T*)fAlloc.allocThrow(sizeof(T) * count);
- }
-
- // Add a new command of type T to the end of this SkRecord.
- // You are expected to placement new an object of type T onto this pointer.
- template <typename T>
- T* append() {
- if (fCount == fReserved) {
- fReserved = SkTMax(kFirstReserveCount, fReserved*2);
- fRecords.realloc(fReserved);
- fTypes.realloc(fReserved);
- }
-
- fTypes[fCount] = T::kType;
- return fRecords[fCount++].set(this->allocCommand<T>());
- }
-
- // Replace the i-th command with a new command of type T.
- // You are expected to placement new an object of type T onto this pointer.
- // References to the original command are invalidated.
- template <typename T>
- T* replace(unsigned i) {
- SkASSERT(i < this->count());
-
- Destroyer destroyer;
- this->mutate<void>(i, destroyer);
-
- fTypes[i] = T::kType;
- return fRecords[i].set(this->allocCommand<T>());
- }
-
- // Replace the i-th command with a new command of type T.
- // You are expected to placement new an object of type T onto this pointer.
- // You must show proof that you've already adopted the existing command.
- template <typename T, typename Existing>
- T* replace(unsigned i, const SkRecords::Adopted<Existing>& proofOfAdoption) {
- SkASSERT(i < this->count());
-
- SkASSERT(Existing::kType == fTypes[i]);
- SkASSERT(proofOfAdoption == fRecords[i].ptr<Existing>());
-
- fTypes[i] = T::kType;
- return fRecords[i].set(this->allocCommand<T>());
- }
-
-private:
- // Implementation notes!
- //
- // Logically an SkRecord is structured as an array of pointers into a big chunk of memory where
- // records representing each canvas draw call are stored:
- //
- // fRecords: [*][*][*]...
- // | | |
- // | | |
- // | | +---------------------------------------+
- // | +-----------------+ |
- // | | |
- // v v v
- // fAlloc: [SkRecords::DrawRect][SkRecords::DrawPosTextH][SkRecords::DrawRect]...
- //
- // In the scheme above, the pointers in fRecords are void*: they have no type. The type is not
- // stored in fAlloc either; we just write raw data there. But we need that type information.
- // Here are some options:
- // 1) use inheritance, virtuals, and vtables to make the fRecords pointers smarter
- // 2) store the type data manually in fAlloc at the start of each record
- // 3) store the type data manually somewhere with fRecords
- //
- // This code uses approach 3). The implementation feels very similar to 1), but it's
- // devirtualized instead of using the language's polymorphism mechanisms. This lets us work
- // with the types themselves (as SkRecords::Type), a sort of limited free RTTI; it lets us pay
- // only 1 byte to store the type instead of a full pointer (4-8 bytes); and it leads to better
- // decoupling between the SkRecords::* record types and the operations performed on them in
- // visit() or mutate(). The recorded canvas calls don't have to have any idea about the
- // operations performed on them.
- //
- // We store the types in a parallel fTypes array, mainly so that they can be tightly packed as
- // single bytes. This has the side effect of allowing very fast analysis passes over an
- // SkRecord looking for just patterns of draw commands (or using this as a quick reject
- // mechanism) though there's admittedly not a very good API exposed publically for this.
- //
- // The cost to append a T into this structure is 1 + sizeof(void*) + sizeof(T).
-
- // A mutator that can be used with replace to destroy canvas commands.
- struct Destroyer {
- template <typename T>
- void operator()(T* record) { record->~T(); }
- };
-
- // Logically the same as SkRecords::Type, but packed into 8 bits.
- struct Type8 {
- public:
- // This intentionally converts implicitly back and forth.
- Type8(SkRecords::Type type) : fType(type) { SkASSERT(*this == type); }
- operator SkRecords::Type () { return (SkRecords::Type)fType; }
-
- private:
- uint8_t fType;
- };
-
- // No point in allocating any more than one of an empty struct.
- // We could just return NULL but it's sort of confusing to return NULL on success.
- template <typename T>
- SK_WHEN(SkTIsEmpty<T>, T*) allocCommand() {
- static T singleton = {};
- return &singleton;
- }
-
- template <typename T>
- SK_WHEN(!SkTIsEmpty<T>, T*) allocCommand() { return this->alloc<T>(); }
-
- // An untyped pointer to some bytes in fAlloc. This is the interface for polymorphic dispatch:
- // visit() and mutate() work with the parallel fTypes array to do the work of a vtable.
- struct Record {
- public:
- // Point this record to its data in fAlloc. Returns ptr for convenience.
- template <typename T>
- T* set(T* ptr) {
- fPtr = ptr;
- return ptr;
- }
-
- // Get the data in fAlloc, assuming it's of type T.
- template <typename T>
- T* ptr() const { return (T*)fPtr; }
-
- // Visit this record with functor F (see public API above) assuming the record we're
- // pointing to has this type.
- template <typename R, typename F>
- R visit(Type8 type, F& f) const {
- #define CASE(T) case SkRecords::T##_Type: return f(*this->ptr<SkRecords::T>());
- switch(type) { SK_RECORD_TYPES(CASE) }
- #undef CASE
- SkDEBUGFAIL("Unreachable");
- return R();
- }
-
- // Mutate this record with functor F (see public API above) assuming the record we're
- // pointing to has this type.
- template <typename R, typename F>
- R mutate(Type8 type, F& f) {
- #define CASE(T) case SkRecords::T##_Type: return f(this->ptr<SkRecords::T>());
- switch(type) { SK_RECORD_TYPES(CASE) }
- #undef CASE
- SkDEBUGFAIL("Unreachable");
- return R();
- }
-
- private:
- void* fPtr;
- };
-
- // fAlloc needs to be a data structure which can append variable length data in contiguous
- // chunks, returning a stable handle to that data for later retrieval.
- //
- // fRecords and fTypes need to be data structures that can append fixed length data, and need to
- // support efficient forward iteration. (They don't need to be contiguous or indexable.)
-
- SkChunkAlloc fAlloc;
- SkAutoTMalloc<Record> fRecords;
- SkAutoTMalloc<Type8> fTypes;
- // fCount and fReserved measure both fRecords and fTypes, which always grow in lock step.
- unsigned fCount;
- unsigned fReserved;
- const unsigned kFirstReserveCount;
-};
-
-#endif//SkRecord_DEFINED
+++ /dev/null
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkRecordDraw.h"
-
-void SkRecordDraw(const SkRecord& record, SkCanvas* canvas) {
- for (SkRecords::Draw draw(canvas); draw.index() < record.count(); draw.next()) {
- record.visit<void>(draw.index(), draw);
- }
-}
-
-namespace SkRecords {
-
-bool Draw::skip(const PairedPushCull& r) {
- if (fCanvas->quickReject(r.base->rect)) {
- fIndex += r.skip;
- return true;
- }
- return false;
-}
-
-bool Draw::skip(const BoundedDrawPosTextH& r) {
- return fCanvas->quickRejectY(r.minY, r.maxY);
-}
-
-// NoOps draw nothing.
-template <> void Draw::draw(const NoOp&) {}
-
-#define DRAW(T, call) template <> void Draw::draw(const T& r) { fCanvas->call; }
-DRAW(Restore, restore());
-DRAW(Save, save(r.flags));
-DRAW(SaveLayer, saveLayer(r.bounds, r.paint, r.flags));
-DRAW(PopCull, popCull());
-DRAW(PushCull, pushCull(r.rect));
-DRAW(Clear, clear(r.color));
-DRAW(Concat, concat(r.matrix));
-DRAW(SetMatrix, setMatrix(SkMatrix::Concat(fInitialCTM, r.matrix)));
-
-DRAW(ClipPath, clipPath(r.path, r.op, r.doAA));
-DRAW(ClipRRect, clipRRect(r.rrect, r.op, r.doAA));
-DRAW(ClipRect, clipRect(r.rect, r.op, r.doAA));
-DRAW(ClipRegion, clipRegion(r.region, r.op));
-
-DRAW(DrawBitmap, drawBitmap(r.bitmap, r.left, r.top, r.paint));
-DRAW(DrawBitmapMatrix, drawBitmapMatrix(r.bitmap, r.matrix, r.paint));
-DRAW(DrawBitmapNine, drawBitmapNine(r.bitmap, r.center, r.dst, r.paint));
-DRAW(DrawBitmapRectToRect, drawBitmapRectToRect(r.bitmap, r.src, r.dst, r.paint, r.flags));
-DRAW(DrawDRRect, drawDRRect(r.outer, r.inner, r.paint));
-DRAW(DrawOval, drawOval(r.oval, r.paint));
-DRAW(DrawPaint, drawPaint(r.paint));
-DRAW(DrawPath, drawPath(r.path, r.paint));
-DRAW(DrawPoints, drawPoints(r.mode, r.count, r.pts, r.paint));
-DRAW(DrawPosText, drawPosText(r.text, r.byteLength, r.pos, r.paint));
-DRAW(DrawPosTextH, drawPosTextH(r.text, r.byteLength, r.xpos, r.y, r.paint));
-DRAW(DrawRRect, drawRRect(r.rrect, r.paint));
-DRAW(DrawRect, drawRect(r.rect, r.paint));
-DRAW(DrawSprite, drawSprite(r.bitmap, r.left, r.top, r.paint));
-DRAW(DrawText, drawText(r.text, r.byteLength, r.x, r.y, r.paint));
-DRAW(DrawTextOnPath, drawTextOnPath(r.text, r.byteLength, r.path, r.matrix, r.paint));
-DRAW(DrawVertices, drawVertices(r.vmode, r.vertexCount, r.vertices, r.texs, r.colors,
- r.xmode.get(), r.indices, r.indexCount, r.paint));
-#undef DRAW
-
-template <> void Draw::draw(const PairedPushCull& r) { this->draw(*r.base); }
-template <> void Draw::draw(const BoundedDrawPosTextH& r) { this->draw(*r.base); }
-
-} // namespace SkRecords
+++ /dev/null
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkRecordDraw_DEFINED
-#define SkRecordDraw_DEFINED
-
-#include "SkRecord.h"
-#include "SkCanvas.h"
-
-// Draw an SkRecord into an SkCanvas. A convenience wrapper around SkRecords::Draw.
-void SkRecordDraw(const SkRecord&, SkCanvas*);
-
-namespace SkRecords {
-
-// This is an SkRecord visitor that will draw that SkRecord to an SkCanvas.
-class Draw : SkNoncopyable {
-public:
- explicit Draw(SkCanvas* canvas)
- : fInitialCTM(canvas->getTotalMatrix()), fCanvas(canvas), fIndex(0) {}
-
- unsigned index() const { return fIndex; }
- void next() { ++fIndex; }
-
- template <typename T> void operator()(const T& r) {
- if (!this->skip(r)) {
- this->draw(r);
- }
- }
-
-private:
- // No base case, so we'll be compile-time checked that we implement all possibilities.
- template <typename T> void draw(const T&);
-
- // skip() should return true if we can skip this command, false if not.
- // It may update fIndex directly to skip more than just this one command.
-
- // Mostly we just blindly call fCanvas and let it handle quick rejects itself.
- template <typename T> bool skip(const T&) { return false; }
-
- // We add our own quick rejects for commands added by optimizations.
- bool skip(const PairedPushCull&);
- bool skip(const BoundedDrawPosTextH&);
-
- const SkMatrix fInitialCTM;
- SkCanvas* fCanvas;
- unsigned fIndex;
-};
-
-} // namespace SkRecords
-
-#endif//SkRecordDraw_DEFINED
+++ /dev/null
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkRecordOpts.h"
-
-#include "SkRecordPattern.h"
-#include "SkRecords.h"
-#include "SkTDArray.h"
-
-using namespace SkRecords;
-
-void SkRecordOptimize(SkRecord* record) {
- // TODO(mtklein): fuse independent optimizations to reduce number of passes?
- SkRecordNoopCulls(record);
- SkRecordNoopSaveRestores(record);
- // TODO(mtklein): figure out why we draw differently and reenable
- //SkRecordNoopSaveLayerDrawRestores(record);
-
- SkRecordAnnotateCullingPairs(record);
- SkRecordReduceDrawPosTextStrength(record); // Helpful to run this before BoundDrawPosTextH.
- SkRecordBoundDrawPosTextH(record);
-}
-
-// Most of the optimizations in this file are pattern-based. These are all defined as structs with:
-// - a Pattern typedef
-// - a bool onMatch(SkRceord*, Pattern*, unsigned begin, unsigned end) method,
-// which returns true if it made changes and false if not.
-
-// Run a pattern-based optimization once across the SkRecord, returning true if it made any changes.
-// It looks for spans which match Pass::Pattern, and when found calls onMatch() with the pattern,
-// record, and [begin,end) span of the commands that matched.
-template <typename Pass>
-static bool apply(Pass* pass, SkRecord* record) {
- typename Pass::Pattern pattern;
- bool changed = false;
- unsigned begin, end = 0;
-
- while (pattern.search(record, &begin, &end)) {
- changed |= pass->onMatch(record, &pattern, begin, end);
- }
- return changed;
-}
-
-struct CullNooper {
- typedef Pattern3<Is<PushCull>, Star<Is<NoOp> >, Is<PopCull> > Pattern;
-
- bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned end) {
- record->replace<NoOp>(begin); // PushCull
- record->replace<NoOp>(end-1); // PopCull
- return true;
- }
-};
-
-void SkRecordNoopCulls(SkRecord* record) {
- CullNooper pass;
- while (apply(&pass, record));
-}
-
-// Turns the logical NoOp Save and Restore in Save-Draw*-Restore patterns into actual NoOps.
-struct SaveOnlyDrawsRestoreNooper {
- typedef Pattern3<Is<Save>,
- Star<Or<Is<NoOp>, IsDraw> >,
- Is<Restore> >
- Pattern;
-
- bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned end) {
- record->replace<NoOp>(begin); // Save
- record->replace<NoOp>(end-1); // Restore
- return true;
- }
-};
-// Turns logical no-op Save-[non-drawing command]*-Restore patterns into actual no-ops.
-struct SaveNoDrawsRestoreNooper {
- // Star matches greedily, so we also have to exclude Save and Restore.
- typedef Pattern3<Is<Save>,
- Star<Not<Or3<Is<Save>,
- Is<Restore>,
- IsDraw> > >,
- Is<Restore> >
- Pattern;
-
- bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned end) {
- // If restore doesn't revert both matrix and clip, this isn't safe to noop away.
- if (pattern->first<Save>()->flags != SkCanvas::kMatrixClip_SaveFlag) {
- return false;
- }
-
- // The entire span between Save and Restore (inclusively) does nothing.
- for (unsigned i = begin; i < end; i++) {
- record->replace<NoOp>(i);
- }
- return true;
- }
-};
-void SkRecordNoopSaveRestores(SkRecord* record) {
- SaveOnlyDrawsRestoreNooper onlyDraws;
- SaveNoDrawsRestoreNooper noDraws;
-
- // Run until they stop changing things.
- while (apply(&onlyDraws, record) || apply(&noDraws, record));
-}
-
-// For some SaveLayer-[drawing command]-Restore patterns, merge the SaveLayer's alpha into the
-// draw, and no-op the SaveLayer and Restore.
-struct SaveLayerDrawRestoreNooper {
- typedef Pattern3<Is<SaveLayer>, IsDraw, Is<Restore> > Pattern;
-
- bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned end) {
- SaveLayer* saveLayer = pattern->first<SaveLayer>();
- if (saveLayer->bounds != NULL) {
- // SaveLayer with bounds is too tricky for us.
- return false;
- }
-
- SkPaint* layerPaint = saveLayer->paint;
- if (NULL == layerPaint) {
- // There wasn't really any point to this SaveLayer at all.
- return KillSaveLayerAndRestore(record, begin);
- }
-
- SkPaint* drawPaint = pattern->second<SkPaint>();
- if (drawPaint == NULL) {
- // We can just give the draw the SaveLayer's paint.
- // TODO(mtklein): figure out how to do this clearly
- return false;
- }
-
- const uint32_t layerColor = layerPaint->getColor();
- const uint32_t drawColor = drawPaint->getColor();
- if (!IsOnlyAlpha(layerColor) || !IsOpaque(drawColor) ||
- HasAnyEffect(*layerPaint) || HasAnyEffect(*drawPaint)) {
- // Too fancy for us. Actually, as long as layerColor is just an alpha
- // we can blend it into drawColor's alpha; drawColor doesn't strictly have to be opaque.
- return false;
- }
-
- drawPaint->setColor(SkColorSetA(drawColor, SkColorGetA(layerColor)));
- return KillSaveLayerAndRestore(record, begin);
- }
-
- static bool KillSaveLayerAndRestore(SkRecord* record, unsigned saveLayerIndex) {
- record->replace<NoOp>(saveLayerIndex); // SaveLayer
- record->replace<NoOp>(saveLayerIndex+2); // Restore
- return true;
- }
-
- static bool HasAnyEffect(const SkPaint& paint) {
- return paint.getPathEffect() ||
- paint.getShader() ||
- paint.getXfermode() ||
- paint.getMaskFilter() ||
- paint.getColorFilter() ||
- paint.getRasterizer() ||
- paint.getLooper() ||
- paint.getImageFilter();
- }
-
- static bool IsOpaque(SkColor color) {
- return SkColorGetA(color) == SK_AlphaOPAQUE;
- }
- static bool IsOnlyAlpha(SkColor color) {
- return SK_ColorTRANSPARENT == SkColorSetA(color, SK_AlphaTRANSPARENT);
- }
-};
-void SkRecordNoopSaveLayerDrawRestores(SkRecord* record) {
- SaveLayerDrawRestoreNooper pass;
- apply(&pass, record);
-}
-
-
-// Replaces DrawPosText with DrawPosTextH when all Y coordinates are equal.
-struct StrengthReducer {
- typedef Pattern1<Is<DrawPosText> > Pattern;
-
- bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned end) {
- SkASSERT(end == begin + 1);
- DrawPosText* draw = pattern->first<DrawPosText>();
-
- const unsigned points = draw->paint.countText(draw->text, draw->byteLength);
- if (points == 0) {
- return false; // No point (ha!).
- }
-
- const SkScalar firstY = draw->pos[0].fY;
- for (unsigned i = 1; i < points; i++) {
- if (draw->pos[i].fY != firstY) {
- return false; // Needs full power of DrawPosText.
- }
- }
- // All ys are the same. We can replace DrawPosText with DrawPosTextH.
-
- // draw->pos is points SkPoints, [(x,y),(x,y),(x,y),(x,y), ... ].
- // We're going to squint and look at that as 2*points SkScalars, [x,y,x,y,x,y,x,y, ...].
- // Then we'll rearrange things so all the xs are in order up front, clobbering the ys.
- SK_COMPILE_ASSERT(sizeof(SkPoint) == 2 * sizeof(SkScalar), SquintingIsNotSafe);
- SkScalar* scalars = &draw->pos[0].fX;
- for (unsigned i = 0; i < 2*points; i += 2) {
- scalars[i/2] = scalars[i];
- }
-
- // Extend lifetime of draw to the end of the loop so we can copy its paint.
- Adopted<DrawPosText> adopted(draw);
- SkNEW_PLACEMENT_ARGS(record->replace<DrawPosTextH>(begin, adopted),
- DrawPosTextH,
- (draw->paint, draw->text, draw->byteLength, scalars, firstY));
- return true;
- }
-};
-void SkRecordReduceDrawPosTextStrength(SkRecord* record) {
- StrengthReducer pass;
- apply(&pass, record);
-}
-
-// Tries to replace DrawPosTextH with BoundedDrawPosTextH, which knows conservative upper and lower
-// bounds to use with SkCanvas::quickRejectY.
-struct TextBounder {
- typedef Pattern1<Is<DrawPosTextH> > Pattern;
-
- bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned end) {
- SkASSERT(end == begin + 1);
- DrawPosTextH* draw = pattern->first<DrawPosTextH>();
-
- // If we're drawing vertical text, none of the checks we're about to do make any sense.
- // We'll need to call SkPaint::computeFastBounds() later, so bail if that's not possible.
- if (draw->paint.isVerticalText() || !draw->paint.canComputeFastBounds()) {
- return false;
- }
-
- // Rather than checking the top and bottom font metrics, we guess. Actually looking up the
- // top and bottom metrics is slow, and this overapproximation should be good enough.
- const SkScalar buffer = draw->paint.getTextSize() * 1.5f;
- SkDEBUGCODE(SkPaint::FontMetrics metrics;)
- SkDEBUGCODE(draw->paint.getFontMetrics(&metrics);)
- SkASSERT(-buffer <= metrics.fTop);
- SkASSERT(+buffer >= metrics.fBottom);
-
- // Let the paint adjust the text bounds. We don't care about left and right here, so we use
- // 0 and 1 respectively just so the bounds rectangle isn't empty.
- SkRect bounds;
- bounds.set(0, draw->y - buffer, SK_Scalar1, draw->y + buffer);
- SkRect adjusted = draw->paint.computeFastBounds(bounds, &bounds);
-
- Adopted<DrawPosTextH> adopted(draw);
- SkNEW_PLACEMENT_ARGS(record->replace<BoundedDrawPosTextH>(begin, adopted),
- BoundedDrawPosTextH,
- (&adopted, adjusted.fTop, adjusted.fBottom));
- return true;
- }
-};
-void SkRecordBoundDrawPosTextH(SkRecord* record) {
- TextBounder pass;
- apply(&pass, record);
-}
-
-// Replaces PushCull with PairedPushCull, which lets us skip to the paired PopCull when the canvas
-// can quickReject the cull rect.
-// There's no efficient way (yet?) to express this one as a pattern, so we write a custom pass.
-class CullAnnotator {
-public:
- // Do nothing to most ops.
- template <typename T> void operator()(T*) {}
-
- void operator()(PushCull* push) {
- Pair pair = { fIndex, push };
- fPushStack.push(pair);
- }
-
- void operator()(PopCull* pop) {
- Pair push = fPushStack.top();
- fPushStack.pop();
-
- SkASSERT(fIndex > push.index);
- unsigned skip = fIndex - push.index;
-
- Adopted<PushCull> adopted(push.command);
- SkNEW_PLACEMENT_ARGS(fRecord->replace<PairedPushCull>(push.index, adopted),
- PairedPushCull, (&adopted, skip));
- }
-
- void apply(SkRecord* record) {
- for (fRecord = record, fIndex = 0; fIndex < record->count(); fIndex++) {
- fRecord->mutate<void>(fIndex, *this);
- }
- }
-
-private:
- struct Pair {
- unsigned index;
- PushCull* command;
- };
-
- SkTDArray<Pair> fPushStack;
- SkRecord* fRecord;
- unsigned fIndex;
-};
-void SkRecordAnnotateCullingPairs(SkRecord* record) {
- CullAnnotator pass;
- pass.apply(record);
-}
+++ /dev/null
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkRecordOpts_DEFINED
-#define SkRecordOpts_DEFINED
-
-#include "SkRecord.h"
-
-// Run all optimizations in recommended order.
-void SkRecordOptimize(SkRecord*);
-
-// NoOp away pointless PushCull/PopCull pairs with nothing between them.
-void SkRecordNoopCulls(SkRecord*);
-
-// Turns logical no-op Save-[non-drawing command]*-Restore patterns into actual no-ops.
-void SkRecordNoopSaveRestores(SkRecord*);
-
-// For some SaveLayer-[drawing command]-Restore patterns, merge the SaveLayer's alpha into the
-// draw, and no-op the SaveLayer and Restore.
-void SkRecordNoopSaveLayerDrawRestores(SkRecord*);
-
-// Annotates PushCull commands with the relative offset of their paired PopCull.
-void SkRecordAnnotateCullingPairs(SkRecord*);
-
-// Convert DrawPosText to DrawPosTextH when all the Y coordinates are equal.
-void SkRecordReduceDrawPosTextStrength(SkRecord*);
-
-// Calculate min and max Y bounds for DrawPosTextH commands, for use with SkCanvas::quickRejectY.
-void SkRecordBoundDrawPosTextH(SkRecord*);
-
-#endif//SkRecordOpts_DEFINED
+++ /dev/null
-#ifndef SkRecordPattern_DEFINED
-#define SkRecordPattern_DEFINED
-
-#include "SkTLogic.h"
-
-namespace SkRecords {
-
-// First, some matchers. These match a single command in the SkRecord,
-// and may hang onto some data from it. If so, you can get the data by calling .get().
-
-// Matches a command of type T, and stores that command.
-template <typename T>
-class Is {
-public:
- Is() : fPtr(NULL) {}
-
- typedef T type;
- type* get() { return fPtr; }
-
- bool operator()(T* ptr) {
- fPtr = ptr;
- return true;
- }
-
- template <typename U>
- bool operator()(U*) {
- fPtr = NULL;
- return false;
- }
-
-private:
- type* fPtr;
-};
-
-// Matches any command that draws, and stores its paint.
-class IsDraw {
- SK_CREATE_MEMBER_DETECTOR(paint);
-public:
- IsDraw() : fPaint(NULL) {}
-
- typedef SkPaint type;
- type* get() { return fPaint; }
-
- template <typename T>
- SK_WHEN(HasMember_paint<T>, bool) operator()(T* draw) {
- fPaint = AsPtr(draw->paint);
- return true;
- }
-
- template <typename T>
- SK_WHEN(!HasMember_paint<T>, bool) operator()(T*) {
- fPaint = NULL;
- return false;
- }
-
- // SaveLayer has an SkPaint named paint, but it's not a draw.
- bool operator()(SaveLayer*) {
- fPaint = NULL;
- return false;
- }
-
-private:
- // Abstracts away whether the paint is always part of the command or optional.
- template <typename T> static T* AsPtr(SkRecords::Optional<T>& x) { return x; }
- template <typename T> static T* AsPtr(T& x) { return &x; }
-
- type* fPaint;
-};
-
-// Matches if Matcher doesn't. Stores nothing.
-template <typename Matcher>
-struct Not {
- template <typename T>
- bool operator()(T* ptr) { return !Matcher()(ptr); }
-};
-
-// Matches if either of A or B does. Stores nothing.
-template <typename A, typename B>
-struct Or {
- template <typename T>
- bool operator()(T* ptr) { return A()(ptr) || B()(ptr); }
-};
-
-// Matches if any of A, B or C does. Stores nothing.
-template <typename A, typename B, typename C>
-struct Or3 : Or<A, Or<B, C> > {};
-
-// Star is a special matcher that greedily matches Matcher 0 or more times. Stores nothing.
-template <typename Matcher>
-struct Star {
- template <typename T>
- bool operator()(T* ptr) { return Matcher()(ptr); }
-};
-
-// Cons builds a list of Matchers.
-// It first matches Matcher (something from above), then Pattern (another Cons or Nil).
-//
-// This is the main entry point to pattern matching, and so provides a couple of extra API bits:
-// - search scans through the record to look for matches;
-// - first, second, and third return the data stored by their respective matchers in the pattern.
-//
-// These Cons build lists analogously to Lisp's "cons". See Pattern# for the "list" equivalent.
-template <typename Matcher, typename Pattern>
-class Cons {
-public:
- // If this pattern matches the SkRecord starting at i,
- // return the index just past the end of the pattern, otherwise return 0.
- SK_ALWAYS_INLINE unsigned match(SkRecord* record, unsigned i) {
- i = this->matchHead(&fHead, record, i);
- return i == 0 ? 0 : fTail.match(record, i);
- }
-
- // Starting from *end, walk through the SkRecord to find the first span matching this pattern.
- // If there is no such span, return false. If there is, return true and set [*begin, *end).
- SK_ALWAYS_INLINE bool search(SkRecord* record, unsigned* begin, unsigned* end) {
- for (*begin = *end; *begin < record->count(); ++(*begin)) {
- *end = this->match(record, *begin);
- if (*end != 0) {
- return true;
- }
- }
- return false;
- }
-
- // Once either match or search has succeeded, access the stored data of the first, second,
- // or third matcher in this pattern. Add as needed for longer patterns.
- // T is checked statically at compile time; no casting is involved. It's just an API wart.
- template <typename T> T* first() { return fHead.get(); }
- template <typename T> T* second() { return fTail.fHead.get(); }
- template <typename T> T* third() { return fTail.fTail.fHead.get(); }
-
-private:
- // If head isn't a Star, try to match at i once.
- template <typename T>
- unsigned matchHead(T*, SkRecord* record, unsigned i) {
- if (i < record->count()) {
- if (record->mutate<bool>(i, fHead)) {
- return i+1;
- }
- }
- return 0;
- }
-
- // If head is a Star, walk i until it doesn't match.
- template <typename T>
- unsigned matchHead(Star<T>*, SkRecord* record, unsigned i) {
- while (i < record->count()) {
- if (!record->mutate<bool>(i, fHead)) {
- return i;
- }
- i++;
- }
- return 0;
- }
-
- Matcher fHead;
- Pattern fTail;
-
- // All Cons are friends with each other. This lets first, second, and third work.
- template <typename, typename> friend class Cons;
-};
-
-// Nil is the end of every pattern Cons chain.
-struct Nil {
- // Bottoms out recursion down the fTail chain. Just return whatever i the front decided on.
- unsigned match(SkRecord*, unsigned i) { return i; }
-};
-
-// These Pattern# types are syntax sugar over Cons and Nil, just to help eliminate some of the
-// template noise. Use these if you can. Feel free to add more for longer patterns.
-// All types A, B, C, ... are Matchers.
-template <typename A>
-struct Pattern1 : Cons<A, Nil> {};
-
-template <typename A, typename B>
-struct Pattern2 : Cons<A, Pattern1<B> > {};
-
-template <typename A, typename B, typename C>
-struct Pattern3 : Cons<A, Pattern2<B, C> > {};
-
-} // namespace SkRecords
-
-#endif//SkRecordPattern_DEFINED
+++ /dev/null
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkRecorder.h"
-#include "SkPicture.h"
-
-// SkCanvas will fail in mysterious ways if it doesn't know the real width and height.
-SkRecorder::SkRecorder(SkRecord* record, int width, int height)
- : SkCanvas(width, height), fRecord(record) {}
-
-void SkRecorder::forgetRecord() {
- fRecord = NULL;
-}
-
-// To make appending to fRecord a little less verbose.
-#define APPEND(T, ...) \
- SkNEW_PLACEMENT_ARGS(fRecord->append<SkRecords::T>(), SkRecords::T, (__VA_ARGS__))
-
-// For methods which must call back into SkCanvas.
-#define INHERITED(method, ...) this->SkCanvas::method(__VA_ARGS__)
-
-// The structs we're creating all copy their constructor arguments. Given the way the SkRecords
-// framework works, sometimes they happen to technically be copied twice, which is fine and elided
-// into a single copy unless the class has a non-trivial copy constructor. For classes with
-// non-trivial copy constructors, we skip the first copy (and its destruction) by wrapping the value
-// with delay_copy(), forcing the argument to be passed by const&.
-//
-// This is used below for SkBitmap, SkPaint, SkPath, and SkRegion, which all have non-trivial copy
-// constructors and destructors. You'll know you've got a good candidate T if you see ~T() show up
-// unexpectedly on a profile of record time. Otherwise don't bother.
-template <typename T>
-class Reference {
-public:
- Reference(const T& x) : fX(x) {}
- operator const T&() const { return fX; }
-private:
- const T& fX;
-};
-
-template <typename T>
-static Reference<T> delay_copy(const T& x) { return Reference<T>(x); }
-
-// Use copy() only for optional arguments, to be copied if present or skipped if not.
-// (For most types we just pass by value and let copy constructors do their thing.)
-template <typename T>
-T* SkRecorder::copy(const T* src) {
- if (NULL == src) {
- return NULL;
- }
- return SkNEW_PLACEMENT_ARGS(fRecord->alloc<T>(), T, (*src));
-}
-
-// This copy() is for arrays.
-// It will work with POD or non-POD, though currently we only use it for POD.
-template <typename T>
-T* SkRecorder::copy(const T src[], unsigned count) {
- if (NULL == src) {
- return NULL;
- }
- T* dst = fRecord->alloc<T>(count);
- for (unsigned i = 0; i < count; i++) {
- SkNEW_PLACEMENT_ARGS(dst + i, T, (src[i]));
- }
- return dst;
-}
-
-// Specialization for copying strings, using memcpy.
-// This measured around 2x faster for copying code points,
-// but I found no corresponding speedup for other arrays.
-template <>
-char* SkRecorder::copy(const char src[], unsigned count) {
- if (NULL == src) {
- return NULL;
- }
- char* dst = fRecord->alloc<char>(count);
- memcpy(dst, src, count);
- return dst;
-}
-
-void SkRecorder::clear(SkColor color) {
- APPEND(Clear, color);
-}
-
-void SkRecorder::drawPaint(const SkPaint& paint) {
- APPEND(DrawPaint, delay_copy(paint));
-}
-
-void SkRecorder::drawPoints(PointMode mode,
- size_t count,
- const SkPoint pts[],
- const SkPaint& paint) {
- APPEND(DrawPoints, delay_copy(paint), mode, count, this->copy(pts, count));
-}
-
-void SkRecorder::drawRect(const SkRect& rect, const SkPaint& paint) {
- APPEND(DrawRect, delay_copy(paint), rect);
-}
-
-void SkRecorder::drawOval(const SkRect& oval, const SkPaint& paint) {
- APPEND(DrawOval, delay_copy(paint), oval);
-}
-
-void SkRecorder::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
- APPEND(DrawRRect, delay_copy(paint), rrect);
-}
-
-void SkRecorder::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) {
- APPEND(DrawDRRect, delay_copy(paint), outer, inner);
-}
-
-void SkRecorder::drawPath(const SkPath& path, const SkPaint& paint) {
- APPEND(DrawPath, delay_copy(paint), delay_copy(path));
-}
-
-void SkRecorder::drawBitmap(const SkBitmap& bitmap,
- SkScalar left,
- SkScalar top,
- const SkPaint* paint) {
- APPEND(DrawBitmap, this->copy(paint), delay_copy(bitmap), left, top);
-}
-
-void SkRecorder::drawBitmapRectToRect(const SkBitmap& bitmap,
- const SkRect* src,
- const SkRect& dst,
- const SkPaint* paint,
- DrawBitmapRectFlags flags) {
- APPEND(DrawBitmapRectToRect,
- this->copy(paint), delay_copy(bitmap), this->copy(src), dst, flags);
-}
-
-void SkRecorder::drawBitmapMatrix(const SkBitmap& bitmap,
- const SkMatrix& matrix,
- const SkPaint* paint) {
- APPEND(DrawBitmapMatrix, this->copy(paint), delay_copy(bitmap), matrix);
-}
-
-void SkRecorder::drawBitmapNine(const SkBitmap& bitmap,
- const SkIRect& center,
- const SkRect& dst,
- const SkPaint* paint) {
- APPEND(DrawBitmapNine, this->copy(paint), delay_copy(bitmap), center, dst);
-}
-
-void SkRecorder::drawSprite(const SkBitmap& bitmap, int left, int top, const SkPaint* paint) {
- APPEND(DrawSprite, this->copy(paint), delay_copy(bitmap), left, top);
-}
-
-void SkRecorder::onDrawText(const void* text, size_t byteLength,
- SkScalar x, SkScalar y, const SkPaint& paint) {
- APPEND(DrawText,
- delay_copy(paint), this->copy((const char*)text, byteLength), byteLength, x, y);
-}
-
-void SkRecorder::onDrawPosText(const void* text, size_t byteLength,
- const SkPoint pos[], const SkPaint& paint) {
- const unsigned points = paint.countText(text, byteLength);
- APPEND(DrawPosText,
- delay_copy(paint),
- this->copy((const char*)text, byteLength),
- byteLength,
- this->copy(pos, points));
-}
-
-void SkRecorder::onDrawPosTextH(const void* text, size_t byteLength,
- const SkScalar xpos[], SkScalar constY, const SkPaint& paint) {
- const unsigned points = paint.countText(text, byteLength);
- APPEND(DrawPosTextH,
- delay_copy(paint),
- this->copy((const char*)text, byteLength),
- byteLength,
- this->copy(xpos, points),
- constY);
-}
-
-void SkRecorder::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
- const SkMatrix* matrix, const SkPaint& paint) {
- APPEND(DrawTextOnPath,
- delay_copy(paint),
- this->copy((const char*)text, byteLength),
- byteLength,
- delay_copy(path),
- this->copy(matrix));
-}
-
-void SkRecorder::onDrawPicture(const SkPicture* picture) {
- picture->draw(this);
-}
-
-void SkRecorder::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) {
- APPEND(DrawVertices, delay_copy(paint),
- vmode,
- vertexCount,
- this->copy(vertices, vertexCount),
- texs ? this->copy(texs, vertexCount) : NULL,
- colors ? this->copy(colors, vertexCount) : NULL,
- xmode,
- this->copy(indices, indexCount),
- indexCount);
-}
-
-void SkRecorder::willSave(SkCanvas::SaveFlags flags) {
- APPEND(Save, flags);
- INHERITED(willSave, flags);
-}
-
-SkCanvas::SaveLayerStrategy SkRecorder::willSaveLayer(const SkRect* bounds,
- const SkPaint* paint,
- SkCanvas::SaveFlags flags) {
- APPEND(SaveLayer, this->copy(bounds), this->copy(paint), flags);
- INHERITED(willSaveLayer, bounds, paint, flags);
- return SkCanvas::kNoLayer_SaveLayerStrategy;
-}
-
-void SkRecorder::willRestore() {
- APPEND(Restore);
- INHERITED(willRestore);
-}
-
-void SkRecorder::onPushCull(const SkRect& rect) {
- APPEND(PushCull, rect);
-}
-
-void SkRecorder::onPopCull() {
- APPEND(PopCull);
-}
-
-void SkRecorder::didConcat(const SkMatrix& matrix) {
- APPEND(Concat, matrix);
- INHERITED(didConcat, matrix);
-}
-
-void SkRecorder::didSetMatrix(const SkMatrix& matrix) {
- APPEND(SetMatrix, matrix);
- INHERITED(didSetMatrix, matrix);
-}
-
-void SkRecorder::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
- APPEND(ClipRect, rect, op, edgeStyle == kSoft_ClipEdgeStyle);
- INHERITED(onClipRect, rect, op, edgeStyle);
-}
-
-void SkRecorder::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
- APPEND(ClipRRect, rrect, op, edgeStyle == kSoft_ClipEdgeStyle);
- INHERITED(updateClipConservativelyUsingBounds, rrect.getBounds(), op, false);
-}
-
-void SkRecorder::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
- APPEND(ClipPath, delay_copy(path), op, edgeStyle == kSoft_ClipEdgeStyle);
- INHERITED(updateClipConservativelyUsingBounds, path.getBounds(), op, path.isInverseFillType());
-}
-
-void SkRecorder::onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) {
- APPEND(ClipRegion, delay_copy(deviceRgn), op);
- INHERITED(onClipRegion, deviceRgn, op);
-}
+++ /dev/null
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkRecorder_DEFINED
-#define SkRecorder_DEFINED
-
-#include "SkCanvas.h"
-#include "SkRecord.h"
-#include "SkRecords.h"
-
-// SkRecorder provides an SkCanvas interface for recording into an SkRecord.
-
-class SkRecorder : public SkCanvas {
-public:
- // Does not take ownership of the SkRecord.
- SkRecorder(SkRecord*, int width, int height);
-
- // Make SkRecorder forget entirely about its SkRecord*; all calls to SkRecorder will fail.
- void forgetRecord();
-
- void clear(SkColor) SK_OVERRIDE;
- void drawPaint(const SkPaint& paint) SK_OVERRIDE;
- void drawPoints(PointMode mode,
- size_t count,
- const SkPoint pts[],
- const SkPaint& paint) SK_OVERRIDE;
- void drawRect(const SkRect& rect, const SkPaint& paint) SK_OVERRIDE;
- void drawOval(const SkRect& oval, const SkPaint&) SK_OVERRIDE;
- void drawRRect(const SkRRect& rrect, const SkPaint& paint) SK_OVERRIDE;
- void drawPath(const SkPath& path, const SkPaint& paint) SK_OVERRIDE;
- void drawBitmap(const SkBitmap& bitmap,
- SkScalar left,
- SkScalar top,
- const SkPaint* paint = NULL) SK_OVERRIDE;
- void drawBitmapRectToRect(const SkBitmap& bitmap,
- const SkRect* src,
- const SkRect& dst,
- const SkPaint* paint = NULL,
- DrawBitmapRectFlags flags = kNone_DrawBitmapRectFlag) SK_OVERRIDE;
- void drawBitmapMatrix(const SkBitmap& bitmap,
- const SkMatrix& m,
- const SkPaint* paint = NULL) SK_OVERRIDE;
- void drawBitmapNine(const SkBitmap& bitmap,
- const SkIRect& center,
- const SkRect& dst,
- const SkPaint* paint = NULL) SK_OVERRIDE;
- void drawSprite(const SkBitmap& bitmap,
- int left,
- int top,
- const SkPaint* paint = NULL) SK_OVERRIDE;
- void 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) SK_OVERRIDE;
-
- void willSave(SkCanvas::SaveFlags) SK_OVERRIDE;
- SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SkCanvas::SaveFlags) SK_OVERRIDE;
- void willRestore() SK_OVERRIDE;
-
- void didConcat(const SkMatrix&) SK_OVERRIDE;
- void didSetMatrix(const SkMatrix&) SK_OVERRIDE;
-
- void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) SK_OVERRIDE;
- void onDrawText(const void* text,
- size_t byteLength,
- SkScalar x,
- SkScalar y,
- const SkPaint& paint) SK_OVERRIDE;
- void onDrawPosText(const void* text,
- size_t byteLength,
- const SkPoint pos[],
- const SkPaint& paint) SK_OVERRIDE;
- void onDrawPosTextH(const void* text,
- size_t byteLength,
- const SkScalar xpos[],
- SkScalar constY,
- const SkPaint& paint) SK_OVERRIDE;
- void onDrawTextOnPath(const void* text,
- size_t byteLength,
- const SkPath& path,
- const SkMatrix* matrix,
- const SkPaint& paint) SK_OVERRIDE;
- void onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) SK_OVERRIDE;
- void onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) SK_OVERRIDE;
- void onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) SK_OVERRIDE;
- void onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) SK_OVERRIDE;
-
- void onDrawPicture(const SkPicture* picture) SK_OVERRIDE;
-
- void onPushCull(const SkRect& cullRect) SK_OVERRIDE;
- void onPopCull() SK_OVERRIDE;
-
-private:
- template <typename T>
- T* copy(const T*);
-
- template <typename T>
- T* copy(const T[], unsigned count);
-
- SkRecord* fRecord;
-};
-
-#endif//SkRecorder_DEFINED
+++ /dev/null
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkRecording.h"
-
-#include "SkRecord.h"
-#include "SkRecordOpts.h"
-#include "SkRecordDraw.h"
-#include "SkRecorder.h"
-
-namespace EXPERIMENTAL {
-
-SkPlayback::SkPlayback(const SkRecord* record) : fRecord(record) {}
-
-SkPlayback::~SkPlayback() {}
-
-void SkPlayback::draw(SkCanvas* canvas) const {
- SkASSERT(fRecord.get() != NULL);
- SkRecordDraw(*fRecord, canvas);
-}
-
-SkRecording::SkRecording(int width, int height)
- : fRecord(SkNEW(SkRecord))
- , fRecorder(SkNEW_ARGS(SkRecorder, (fRecord.get(), width, height)))
- {}
-
-SkPlayback* SkRecording::releasePlayback() {
- SkASSERT(fRecorder->unique());
- fRecorder->forgetRecord();
- SkRecordOptimize(fRecord.get());
- return SkNEW_ARGS(SkPlayback, (fRecord.detach()));
-}
-
-SkRecording::~SkRecording() {}
-
-SkCanvas* SkRecording::canvas() {
- return fRecord.get() ? fRecorder.get() : NULL;
-}
-
-} // namespace EXPERIMENTAL
+++ /dev/null
-/*
- * Copyright 2014 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkRecords_DEFINED
-#define SkRecords_DEFINED
-
-#include "SkCanvas.h"
-
-namespace SkRecords {
-
-// A list of all the types of canvas calls we can record.
-// Each of these is reified into a struct below.
-//
-// (We're using the macro-of-macro trick here to do several different things with the same list.)
-//
-// We leave this SK_RECORD_TYPES macro defined for use by code that wants to operate on SkRecords
-// types polymorphically. (See SkRecord::Record::{visit,mutate} for an example.)
-//
-// Order doesn't technically matter here, but the compiler can generally generate better code if
-// you keep them semantically grouped, especially the Draws. It's also nice to leave NoOp at 0.
-#define SK_RECORD_TYPES(M) \
- M(NoOp) \
- M(Restore) \
- M(Save) \
- M(SaveLayer) \
- M(PushCull) \
- M(PopCull) \
- M(PairedPushCull) /*From SkRecordAnnotateCullingPairs*/ \
- M(Concat) \
- M(SetMatrix) \
- M(ClipPath) \
- M(ClipRRect) \
- M(ClipRect) \
- M(ClipRegion) \
- M(Clear) \
- M(DrawBitmap) \
- M(DrawBitmapMatrix) \
- M(DrawBitmapNine) \
- M(DrawBitmapRectToRect) \
- M(DrawDRRect) \
- M(DrawOval) \
- M(DrawPaint) \
- M(DrawPath) \
- M(DrawPoints) \
- M(DrawPosText) \
- M(DrawPosTextH) \
- M(DrawRRect) \
- M(DrawRect) \
- M(DrawSprite) \
- M(DrawText) \
- M(DrawTextOnPath) \
- M(DrawVertices) \
- M(BoundedDrawPosTextH) /*From SkRecordBoundDrawPosTextH*/
-
-// Defines SkRecords::Type, an enum of all record types.
-#define ENUM(T) T##_Type,
-enum Type { SK_RECORD_TYPES(ENUM) };
-#undef ENUM
-
-// Macros to make it easier to define a record for a draw call with 0 args, 1 args, 2 args, etc.
-// These should be clearer when you look at their use below.
-#define RECORD0(T) \
-struct T { \
- static const Type kType = T##_Type; \
-};
-
-// We try to be flexible about the types the constructors take. Instead of requring the exact type
-// A here, we take any type Z which implicitly casts to A. This allows the delay_copy() trick to
-// work, allowing the caller to decide whether to pass by value or by const&.
-
-#define RECORD1(T, A, a) \
-struct T { \
- static const Type kType = T##_Type; \
- template <typename Z> \
- T(Z a) : a(a) {} \
- A a; \
-};
-
-#define RECORD2(T, A, a, B, b) \
-struct T { \
- static const Type kType = T##_Type; \
- template <typename Z, typename Y> \
- T(Z a, Y b) : a(a), b(b) {} \
- A a; B b; \
-};
-
-#define RECORD3(T, A, a, B, b, C, c) \
-struct T { \
- static const Type kType = T##_Type; \
- template <typename Z, typename Y, typename X> \
- T(Z a, Y b, X c) : a(a), b(b), c(c) {} \
- A a; B b; C c; \
-};
-
-#define RECORD4(T, A, a, B, b, C, c, D, d) \
-struct T { \
- static const Type kType = T##_Type; \
- template <typename Z, typename Y, typename X, typename W> \
- T(Z a, Y b, X c, W d) : a(a), b(b), c(c), d(d) {} \
- A a; B b; C c; D d; \
-};
-
-#define RECORD5(T, A, a, B, b, C, c, D, d, E, e) \
-struct T { \
- static const Type kType = T##_Type; \
- template <typename Z, typename Y, typename X, typename W, typename V> \
- T(Z a, Y b, X c, W d, V e) : a(a), b(b), c(c), d(d), e(e) {} \
- A a; B b; C c; D d; E e; \
-};
-
-#define ACT_AS_PTR(ptr) \
- operator T*() { return ptr; } \
- operator const T*() const { return ptr; } \
- T* operator->() { return ptr; } \
- const T* operator->() const { return ptr; }
-
-// An Optional doesn't own the pointer's memory, but may need to destroy non-POD data.
-template <typename T>
-class Optional : SkNoncopyable {
-public:
- Optional(T* ptr) : fPtr(ptr) {}
- ~Optional() { if (fPtr) fPtr->~T(); }
-
- ACT_AS_PTR(fPtr);
-private:
- T* fPtr;
-};
-
-// Like Optional, but ptr must not be NULL.
-template <typename T>
-class Adopted : SkNoncopyable {
-public:
- Adopted(T* ptr) : fPtr(ptr) { SkASSERT(fPtr); }
- Adopted(Adopted* source) {
- // Transfer ownership from source to this.
- fPtr = source->fPtr;
- source->fPtr = NULL;
- }
- ~Adopted() { if (fPtr) fPtr->~T(); }
-
- ACT_AS_PTR(fPtr);
-private:
- T* fPtr;
-};
-
-// PODArray doesn't own the pointer's memory, and we assume the data is POD.
-template <typename T>
-class PODArray {
-public:
- PODArray(T* ptr) : fPtr(ptr) {}
- // Default copy and assign.
-
- ACT_AS_PTR(fPtr);
-private:
- T* fPtr;
-};
-
-#undef ACT_AS_PTR
-
-// Like SkBitmap, but deep copies pixels if they're not immutable.
-// Using this, we guarantee the immutability of all bitmaps we record.
-class ImmutableBitmap {
-public:
- explicit ImmutableBitmap(const SkBitmap& bitmap) {
- if (bitmap.isImmutable()) {
- fBitmap = bitmap;
- } else {
- bitmap.copyTo(&fBitmap);
- }
- fBitmap.setImmutable();
- }
-
- operator const SkBitmap& () const { return fBitmap; }
-
-private:
- SkBitmap fBitmap;
-};
-
-RECORD0(NoOp);
-
-RECORD0(Restore);
-RECORD1(Save, SkCanvas::SaveFlags, flags);
-RECORD3(SaveLayer, Optional<SkRect>, bounds, Optional<SkPaint>, paint, SkCanvas::SaveFlags, flags);
-
-RECORD1(PushCull, SkRect, rect);
-RECORD0(PopCull);
-
-RECORD1(Concat, SkMatrix, matrix);
-RECORD1(SetMatrix, SkMatrix, matrix);
-
-RECORD3(ClipPath, SkPath, path, SkRegion::Op, op, bool, doAA);
-RECORD3(ClipRRect, SkRRect, rrect, SkRegion::Op, op, bool, doAA);
-RECORD3(ClipRect, SkRect, rect, SkRegion::Op, op, bool, doAA);
-RECORD2(ClipRegion, SkRegion, region, SkRegion::Op, op);
-
-RECORD1(Clear, SkColor, color);
-// While not strictly required, if you have an SkPaint, it's fastest to put it first.
-RECORD4(DrawBitmap, Optional<SkPaint>, paint,
- ImmutableBitmap, bitmap,
- SkScalar, left,
- SkScalar, top);
-RECORD3(DrawBitmapMatrix, Optional<SkPaint>, paint, ImmutableBitmap, bitmap, SkMatrix, matrix);
-RECORD4(DrawBitmapNine, Optional<SkPaint>, paint,
- ImmutableBitmap, bitmap,
- SkIRect, center,
- SkRect, dst);
-RECORD5(DrawBitmapRectToRect, Optional<SkPaint>, paint,
- ImmutableBitmap, bitmap,
- Optional<SkRect>, src,
- SkRect, dst,
- SkCanvas::DrawBitmapRectFlags, flags);
-RECORD3(DrawDRRect, SkPaint, paint, SkRRect, outer, SkRRect, inner);
-RECORD2(DrawOval, SkPaint, paint, SkRect, oval);
-RECORD1(DrawPaint, SkPaint, paint);
-RECORD2(DrawPath, SkPaint, paint, SkPath, path);
-RECORD4(DrawPoints, SkPaint, paint, SkCanvas::PointMode, mode, size_t, count, SkPoint*, pts);
-RECORD4(DrawPosText, SkPaint, paint,
- PODArray<char>, text,
- size_t, byteLength,
- PODArray<SkPoint>, pos);
-RECORD5(DrawPosTextH, SkPaint, paint,
- PODArray<char>, text,
- size_t, byteLength,
- PODArray<SkScalar>, xpos,
- SkScalar, y);
-RECORD2(DrawRRect, SkPaint, paint, SkRRect, rrect);
-RECORD2(DrawRect, SkPaint, paint, SkRect, rect);
-RECORD4(DrawSprite, Optional<SkPaint>, paint, ImmutableBitmap, bitmap, int, left, int, top);
-RECORD5(DrawText, SkPaint, paint,
- PODArray<char>, text,
- size_t, byteLength,
- SkScalar, x,
- SkScalar, y);
-RECORD5(DrawTextOnPath, SkPaint, paint,
- PODArray<char>, text,
- size_t, byteLength,
- SkPath, path,
- Optional<SkMatrix>, matrix);
-
-// This guy is so ugly we just write it manually.
-struct DrawVertices {
- static const Type kType = DrawVertices_Type;
-
- DrawVertices(const SkPaint& paint,
- SkCanvas::VertexMode vmode,
- int vertexCount,
- SkPoint* vertices,
- SkPoint* texs,
- SkColor* colors,
- SkXfermode* xmode,
- uint16_t* indices,
- int indexCount)
- : paint(paint)
- , vmode(vmode)
- , vertexCount(vertexCount)
- , vertices(vertices)
- , texs(texs)
- , colors(colors)
- , xmode(SkSafeRef(xmode))
- , indices(indices)
- , indexCount(indexCount) {}
-
- SkPaint paint;
- SkCanvas::VertexMode vmode;
- int vertexCount;
- PODArray<SkPoint> vertices;
- PODArray<SkPoint> texs;
- PODArray<SkColor> colors;
- SkAutoTUnref<SkXfermode> xmode;
- PODArray<uint16_t> indices;
- int indexCount;
-};
-
-// Records added by optimizations.
-RECORD2(PairedPushCull, Adopted<PushCull>, base, unsigned, skip);
-RECORD3(BoundedDrawPosTextH, Adopted<DrawPosTextH>, base, SkScalar, minY, SkScalar, maxY);
-
-#undef RECORD0
-#undef RECORD1
-#undef RECORD2
-#undef RECORD3
-#undef RECORD4
-#undef RECORD5
-
-} // namespace SkRecords
-
-#endif//SkRecords_DEFINED
#include "Test.h"
-#include "SkRecording.h"
+#include "../include/record/SkRecording.h"
// Minimally exercise the public SkRecording API.
#include "SkOSFile.h"
#include "SkPicture.h"
#include "SkPictureRecorder.h"
-#include "SkRecording.h"
#include "SkStream.h"
#include "SkString.h"
+#include "../include/record/SkRecording.h"
+
#include "BenchTimer.h"
#include "Stats.h"
#include "SkOSFile.h"
#include "SkPicture.h"
#include "SkPictureRecorder.h"
-#include "SkRecording.h"
#include "SkStream.h"
#include "SkString.h"
}
static void rerecord(const SkPicture& src, SkBBHFactory* bbhFactory) {
+ SkPictureRecorder recorder;
if (FLAGS_skr) {
- EXPERIMENTAL::SkRecording recording(src.width(), src.height());
- src.draw(recording.canvas());
- // Release and delete the SkPlayback so that recording optimizes its SkRecord.
- SkDELETE(recording.releasePlayback());
+ src.draw(recorder.EXPERIMENTAL_beginRecording(src.width(), src.height(), bbhFactory));
} else {
- SkPictureRecorder recorder;
src.draw(recorder.beginRecording(src.width(), src.height(), bbhFactory));
- SkAutoTUnref<SkPicture> dst(recorder.endRecording());
}
+ SkAutoTUnref<SkPicture> pic(recorder.endRecording());
}
static void bench_record(const SkPicture& src,