diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/record/SkRecord.h | 198 | ||||
-rw-r--r-- | src/record/SkRecordDraw.h | 62 | ||||
-rw-r--r-- | src/record/SkRecorder.cpp | 224 | ||||
-rw-r--r-- | src/record/SkRecorder.h | 70 | ||||
-rw-r--r-- | src/record/SkRecords.h | 209 |
5 files changed, 763 insertions, 0 deletions
diff --git a/src/record/SkRecord.h b/src/record/SkRecord.h new file mode 100644 index 0000000000..4013874677 --- /dev/null +++ b/src/record/SkRecord.h @@ -0,0 +1,198 @@ +#ifndef SkRecord_DEFINED +#define SkRecord_DEFINED + +#include "SkChunkAlloc.h" +#include "SkRecords.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() { this->mutate(Destroyer()); } + + // Accepts a visitor functor with this interface: + // template <typename T> + // void operator()()(const T& record) { ... } + // This operator() must be defined for at least all SkRecords::*; your compiler will help you + // get this right. + // + // f will be called on each recorded canvas call in the order they were append()ed. + template <typename F> + void visit(F f) const { + for (unsigned i = 0; i < fCount; i++) { + fRecords[i].visit(fTypes[i], f); + } + } + + // Accepts a visitor functor with this interface: + // template <typename T> + // void operator()()(T* record) { ... } + // This operator() must be defined for at least all SkRecords::*; again, your compiler will help + // you get this right. + // + // f will be called on each recorded canvas call in the order they were append()ed. + template <typename F> + void mutate(F f) { + for (unsigned i = 0; i < fCount; i++) { + fRecords[i].mutate(fTypes[i], f); + } + } + + // Allocate contiguous space for count Ts, to be destroyed (not just freed) when the SkRecord is + // destroyed. For classes with constructors, placement new into this array. Throws on failure. + // Here T can really be any class, not just those from SkRecords. + template <typename T> + T* alloc(unsigned count = 1) { + return (T*)fAlloc.allocThrow(sizeof(T) * count); + } + + // Allocate space to record a canvas call of type T at 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++].alloc<T>(this); + } + +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. + // + // We pull one final sneaky trick in the implementation. When recording canvas calls that need + // to store less than a pointer of data, we don't go through the usual path of allocating the + // draw command in fAlloc and a pointer to it in fRecords; instead, we ignore fAlloc and + // directly allocate the object in the space we would have put the pointer in fRecords. This is + // why you'll see uintptr_t instead of void* in Record below. + // + // The cost of appending a single record into this structure is then: + // - 1 + sizeof(void*) + sizeof(T) if sizeof(T) > sizeof(void*) + // - 1 + sizeof(void*) if sizeof(T) <= sizeof(void*) + + + // A mutator that calls destructors of all the canvas calls we've recorded. + 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; + }; + + // Logically a void* to some bytes in fAlloc, but maybe has the bytes stored immediately + // instead. This is also the main interface for devirtualized polymorphic dispatch: see visit() + // and mutate(), which essentially do the work of the missing vtable. + struct Record { + public: + + // Allocate space for a T, perhaps using the SkRecord to allocate that space. + template <typename T> + T* alloc(SkRecord* record) { + if (IsLarge<T>()) { + fRecord = (uintptr_t)record->alloc<T>(); + } + return this->ptr<T>(); + } + + // Visit this record with functor F (see public API above) assuming the record we're + // pointing to has this type. + template <typename F> + void 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 + } + + // Mutate this record with functor F (see public API above) assuming the record we're + // pointing to has this type. + template <typename F> + void 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 + } + + private: + template <typename T> + T* ptr() const { return (T*)(IsLarge<T>() ? (void*)fRecord : &fRecord); } + + // Is T too big to fit directly into a uintptr_t, neededing external allocation? + template <typename T> + static bool IsLarge() { return sizeof(T) > sizeof(uintptr_t); } + + uintptr_t fRecord; + }; + + // 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 diff --git a/src/record/SkRecordDraw.h b/src/record/SkRecordDraw.h new file mode 100644 index 0000000000..744dfc5b33 --- /dev/null +++ b/src/record/SkRecordDraw.h @@ -0,0 +1,62 @@ +#ifndef SkRecordDraw_DEFINED +#define SkRecordDraw_DEFINED + +#include "SkRecord.h" +#include "SkRecords.h" +#include "SkCanvas.h" + +// This is an SkRecord visitor that will draw that SkRecord to an SkCanvas. + +struct SkRecordDraw { + explicit SkRecordDraw(SkCanvas* canvas) : canvas(canvas) {} + + // No base case, so we'll be compile-time checked that we implemented all possibilities below. + template <typename T> void operator()(const T& record); + + SkCanvas* canvas; +}; + +// Nothing fancy here. +// The structs in SkRecord are completely isomorphic to their corresponding SkCanvas calls. + +#define CASE(T) template <> void SkRecordDraw::operator()(const SkRecords::T& r) + +CASE(Restore) { canvas->restore(); } +CASE(Save) { canvas->save(r.flags); } +CASE(SaveLayer) { canvas->saveLayer(r.bounds, r.paint, r.flags); } + +CASE(Concat) { canvas->concat(r.matrix); } +CASE(SetMatrix) { canvas->setMatrix(r.matrix); } + +CASE(ClipPath) { canvas->clipPath(r.path, r.op, r.doAA); } +CASE(ClipRRect) { canvas->clipRRect(r.rrect, r.op, r.doAA); } +CASE(ClipRect) { canvas->clipRect(r.rect, r.op, r.doAA); } +CASE(ClipRegion) { canvas->clipRegion(r.region, r.op); } + +CASE(Clear) { canvas->clear(r.color); } +CASE(DrawBitmap) { canvas->drawBitmap(r.bitmap, r.left, r.top, r.paint); } +CASE(DrawBitmapMatrix) { canvas->drawBitmapMatrix(r.bitmap, r.matrix, r.paint); } +CASE(DrawBitmapNine) { canvas->drawBitmapNine(r.bitmap, r.center, r.dst, r.paint); } +CASE(DrawBitmapRectToRect) { + canvas->drawBitmapRectToRect(r.bitmap, r.src, r.dst, r.paint, r.flags); +} +CASE(DrawDRRect) { canvas->drawDRRect(r.outer, r.inner, r.paint); } +CASE(DrawOval) { canvas->drawOval(r.oval, r.paint); } +CASE(DrawPaint) { canvas->drawPaint(r.paint); } +CASE(DrawPath) { canvas->drawPath(r.path, r.paint); } +CASE(DrawPoints) { canvas->drawPoints(r.mode, r.count, r.pts, r.paint); } +CASE(DrawPosText) { canvas->drawPosText(r.text, r.byteLength, r.pos, r.paint); } +CASE(DrawPosTextH) { canvas->drawPosTextH(r.text, r.byteLength, r.xpos, r.y, r.paint); } +CASE(DrawRRect) { canvas->drawRRect(r.rrect, r.paint); } +CASE(DrawRect) { canvas->drawRect(r.rect, r.paint); } +CASE(DrawSprite) { canvas->drawSprite(r.bitmap, r.left, r.top, r.paint); } +CASE(DrawText) { canvas->drawText(r.text, r.byteLength, r.x, r.y, r.paint); } +CASE(DrawTextOnPath) { canvas->drawTextOnPath(r.text, r.byteLength, r.path, r.matrix, r.paint); } +CASE(DrawVertices) { + canvas->drawVertices(r.vmode, r.vertexCount, r.vertices, r.texs, r.colors, + r.xmode.get(), r.indices, r.indexCount, r.paint); +} + +#undef CASE + +#endif//SkRecordDraw_DEFINED diff --git a/src/record/SkRecorder.cpp b/src/record/SkRecorder.cpp new file mode 100644 index 0000000000..1edcc52239 --- /dev/null +++ b/src/record/SkRecorder.cpp @@ -0,0 +1,224 @@ +#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) {} + +// To make appending to fRecord a little less verbose. +#define APPEND(T, ...) \ + SkNEW_PLACEMENT_ARGS(fRecord->append<SkRecords::T>(), SkRecords::T, (__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, mode, count, this->copy(pts, count), delay_copy(paint)); +} + +void SkRecorder::drawRect(const SkRect& rect, const SkPaint& paint) { + APPEND(DrawRect, rect, delay_copy(paint)); +} + +void SkRecorder::drawOval(const SkRect& oval, const SkPaint& paint) { + APPEND(DrawOval, oval, delay_copy(paint)); +} + +void SkRecorder::drawRRect(const SkRRect& rrect, const SkPaint& paint) { + APPEND(DrawRRect, rrect, delay_copy(paint)); +} + +void SkRecorder::drawPath(const SkPath& path, const SkPaint& paint) { + APPEND(DrawPath, delay_copy(path), delay_copy(paint)); +} + +void SkRecorder::drawBitmap(const SkBitmap& bitmap, + SkScalar left, + SkScalar top, + const SkPaint* paint) { + APPEND(DrawBitmap, delay_copy(bitmap), left, top, this->copy(paint)); +} + +void SkRecorder::drawBitmapRectToRect(const SkBitmap& bitmap, + const SkRect* src, + const SkRect& dst, + const SkPaint* paint, + DrawBitmapRectFlags flags) { + APPEND(DrawBitmapRectToRect, + delay_copy(bitmap), this->copy(src), dst, this->copy(paint), flags); +} + +void SkRecorder::drawBitmapMatrix(const SkBitmap& bitmap, + const SkMatrix& matrix, + const SkPaint* paint) { + APPEND(DrawBitmapMatrix, delay_copy(bitmap), matrix, this->copy(paint)); +} + +void SkRecorder::drawBitmapNine(const SkBitmap& bitmap, + const SkIRect& center, + const SkRect& dst, + const SkPaint* paint) { + APPEND(DrawBitmapNine, delay_copy(bitmap), center, dst, this->copy(paint)); +} + +void SkRecorder::drawSprite(const SkBitmap& bitmap, int left, int top, const SkPaint* paint) { + APPEND(DrawSprite, delay_copy(bitmap), left, top, this->copy(paint)); +} + +void SkRecorder::drawText(const void* text, size_t byteLength, + SkScalar x, SkScalar y, const SkPaint& paint) { + APPEND(DrawText, + this->copy((const char*)text, byteLength), byteLength, x, y, delay_copy(paint)); +} + +void SkRecorder::drawPosText(const void* text, size_t byteLength, + const SkPoint pos[], const SkPaint& paint) { + const unsigned points = paint.countText(text, byteLength); + APPEND(DrawPosText, + this->copy((const char*)text, byteLength), byteLength, + this->copy(pos, points), delay_copy(paint)); +} + +void SkRecorder::drawPosTextH(const void* text, size_t byteLength, + const SkScalar xpos[], SkScalar constY, const SkPaint& paint) { + const unsigned points = paint.countText(text, byteLength); + APPEND(DrawPosTextH, + this->copy((const char*)text, byteLength), byteLength, + this->copy(xpos, points), constY, delay_copy(paint)); +} + +void SkRecorder::drawTextOnPath(const void* text, size_t byteLength, + const SkPath& path, const SkMatrix* matrix, const SkPaint& paint) { + APPEND(DrawTextOnPath, + this->copy((const char*)text, byteLength), byteLength, + delay_copy(path), this->copy(matrix), delay_copy(paint)); +} + +void SkRecorder::drawPicture(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, vmode, + vertexCount, + this->copy(vertices, vertexCount), + texs ? this->copy(texs, vertexCount) : NULL, + colors ? this->copy(colors, vertexCount) : NULL, + xmode, + this->copy(indices, indexCount), + indexCount, + delay_copy(paint)); +} + +void SkRecorder::willSave(SkCanvas::SaveFlags flags) { + APPEND(Save, flags); +} + +SkCanvas::SaveLayerStrategy SkRecorder::willSaveLayer(const SkRect* bounds, + const SkPaint* paint, + SkCanvas::SaveFlags flags) { + APPEND(SaveLayer, this->copy(bounds), this->copy(paint), flags); + return SkCanvas::kNoLayer_SaveLayerStrategy; +} + +void SkRecorder::willRestore() { + APPEND(Restore); +} + +void SkRecorder::didConcat(const SkMatrix& matrix) { + APPEND(Concat, matrix); +} + +void SkRecorder::didSetMatrix(const SkMatrix& matrix) { + APPEND(SetMatrix, matrix); +} + +void SkRecorder::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) { + APPEND(DrawDRRect, outer, inner, delay_copy(paint)); +} + +void SkRecorder::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) { + APPEND(ClipRect, rect, op, edgeStyle == kSoft_ClipEdgeStyle); +} + +void SkRecorder::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) { + APPEND(ClipRRect, rrect, op, edgeStyle == kSoft_ClipEdgeStyle); +} + +void SkRecorder::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) { + APPEND(ClipPath, delay_copy(path), op, edgeStyle == kSoft_ClipEdgeStyle); +} + +void SkRecorder::onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op) { + APPEND(ClipRegion, delay_copy(deviceRgn), op); +} diff --git a/src/record/SkRecorder.h b/src/record/SkRecorder.h new file mode 100644 index 0000000000..9d722b4b2b --- /dev/null +++ b/src/record/SkRecorder.h @@ -0,0 +1,70 @@ +#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); + + void clear(SkColor); + void drawPaint(const SkPaint& paint); + void drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint); + void drawRect(const SkRect& rect, const SkPaint& paint); + void drawOval(const SkRect& oval, const SkPaint&); + void drawRRect(const SkRRect& rrect, const SkPaint& paint); + void drawPath(const SkPath& path, const SkPaint& paint); + void drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top, + const SkPaint* paint = NULL); + void drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst, + const SkPaint* paint = NULL, + DrawBitmapRectFlags flags = kNone_DrawBitmapRectFlag); + void drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& m, const SkPaint* paint = NULL); + void drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst, + const SkPaint* paint = NULL); + void drawSprite(const SkBitmap& bitmap, int left, int top, const SkPaint* paint = NULL); + void drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, + const SkPaint& paint); + void drawPosText(const void* text, size_t byteLength, const SkPoint pos[], + const SkPaint& paint); + void drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[], SkScalar constY, + const SkPaint& paint); + void drawTextOnPath(const void* text, size_t byteLength, + const SkPath& path, const SkMatrix* matrix, const SkPaint& paint); + void drawPicture(SkPicture& picture); + 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); + + void willSave(SkCanvas::SaveFlags); + SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SkCanvas::SaveFlags); + void willRestore(); + + void didConcat(const SkMatrix&); + void didSetMatrix(const SkMatrix&); + + void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&); + void onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle); + void onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle); + void onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle); + void onClipRegion(const SkRegion& deviceRgn, SkRegion::Op op); + +private: + template <typename T> + T* copy(const T*); + + template <typename T> + T* copy(const T[], unsigned count); + + SkRecord* fRecord; +}; + +#endif//SkRecorder_DEFINED diff --git a/src/record/SkRecords.h b/src/record/SkRecords.h new file mode 100644 index 0000000000..67cfb20c4c --- /dev/null +++ b/src/record/SkRecords.h @@ -0,0 +1,209 @@ +#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.) +#define SK_RECORD_TYPES(M) \ + M(Restore) \ + M(Save) \ + M(SaveLayer) \ + 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) + +// 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; \ + T() {} \ +}; + +// 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; \ +}; + +// 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; +}; + +// Pointers here represent either an optional value or an array if accompanied by a count. +// None of these records manages the lifetimes of pointers, except for DrawVertices handling its +// Xfermode specially. + +RECORD0(Restore); +RECORD1(Save, SkCanvas::SaveFlags, flags); +RECORD3(SaveLayer, SkRect*, bounds, SkPaint*, paint, SkCanvas::SaveFlags, flags); + +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); +RECORD4(DrawBitmap, ImmutableBitmap, bitmap, SkScalar, left, SkScalar, top, SkPaint*, paint); +RECORD3(DrawBitmapMatrix, ImmutableBitmap, bitmap, SkMatrix, matrix, SkPaint*, paint); +RECORD4(DrawBitmapNine, ImmutableBitmap, bitmap, SkIRect, center, SkRect, dst, SkPaint*, paint); +RECORD5(DrawBitmapRectToRect, ImmutableBitmap, bitmap, + SkRect*, src, + SkRect, dst, + SkPaint*, paint, + SkCanvas::DrawBitmapRectFlags, flags); +RECORD3(DrawDRRect, SkRRect, outer, SkRRect, inner, SkPaint, paint); +RECORD2(DrawOval, SkRect, oval, SkPaint, paint); +RECORD1(DrawPaint, SkPaint, paint); +RECORD2(DrawPath, SkPath, path, SkPaint, paint); +RECORD4(DrawPoints, SkCanvas::PointMode, mode, size_t, count, SkPoint*, pts, SkPaint, paint); +RECORD4(DrawPosText, char*, text, size_t, byteLength, SkPoint*, pos, SkPaint, paint); +RECORD5(DrawPosTextH, char*, text, + size_t, byteLength, + SkScalar*, xpos, + SkScalar, y, + SkPaint, paint); +RECORD2(DrawRRect, SkRRect, rrect, SkPaint, paint); +RECORD2(DrawRect, SkRect, rect, SkPaint, paint); +RECORD4(DrawSprite, ImmutableBitmap, bitmap, int, left, int, top, SkPaint*, paint); +RECORD5(DrawText, char*, text, size_t, byteLength, SkScalar, x, SkScalar, y, SkPaint, paint); +RECORD5(DrawTextOnPath, char*, text, + size_t, byteLength, + SkPath, path, + SkMatrix*, matrix, + SkPaint, paint); + +// This guy is so ugly we just write it manually. +struct DrawVertices { + static const Type kType = DrawVertices_Type; + + DrawVertices(SkCanvas::VertexMode vmode, + int vertexCount, + SkPoint* vertices, + SkPoint* texs, + SkColor* colors, + SkXfermode* xmode, + uint16_t* indices, + int indexCount, + const SkPaint& paint) + : vmode(vmode) + , vertexCount(vertexCount) + , vertices(vertices) + , texs(texs) + , colors(colors) + , xmode(SkSafeRef(xmode)) + , indices(indices) + , indexCount(indexCount) + , paint(paint) {} + + SkCanvas::VertexMode vmode; + int vertexCount; + SkPoint* vertices; + SkPoint* texs; + SkColor* colors; + SkAutoTUnref<SkXfermode> xmode; + uint16_t* indices; + int indexCount; + SkPaint paint; +}; + +#undef RECORD0 +#undef RECORD1 +#undef RECORD2 +#undef RECORD3 +#undef RECORD4 +#undef RECORD5 + +} // namespace SkRecords + +#endif//SkRecords_DEFINED |