diff options
Diffstat (limited to 'record')
-rw-r--r-- | record/SkRecord.h | 37 | ||||
-rw-r--r-- | record/SkRecordDraw.cpp | 86 | ||||
-rw-r--r-- | record/SkRecordOpts.cpp | 217 | ||||
-rw-r--r-- | record/SkRecordOpts.h | 6 | ||||
-rw-r--r-- | record/SkRecordTraits.h | 31 | ||||
-rw-r--r-- | record/SkRecorder.cpp | 4 | ||||
-rw-r--r-- | record/SkRecorder.h | 3 | ||||
-rw-r--r-- | record/SkRecording.cpp | 35 | ||||
-rw-r--r-- | record/SkRecords.h | 10 |
9 files changed, 206 insertions, 223 deletions
diff --git a/record/SkRecord.h b/record/SkRecord.h index ddf1b3d9..b1e4ae24 100644 --- a/record/SkRecord.h +++ b/record/SkRecord.h @@ -82,20 +82,31 @@ public: // 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 old command remain valid for the life of the SkRecord, but - // you must destroy the old command. (It's okay to destroy it first before calling replace.) + // References to the original command are invalidated. template <typename T> T* replace(unsigned i) { SkASSERT(i < this->count()); + + Destroyer destroyer; + this->mutate(i, destroyer); + fTypes[i] = T::kType; return fRecords[i].set(this->alloc<T>()); } - // A mutator that can be used with replace to destroy canvas commands. - struct Destroyer { - template <typename T> - void operator()(T* record) { record->~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->alloc<T>()); + } private: // Implementation notes! @@ -134,6 +145,11 @@ private: // // 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 { @@ -157,6 +173,10 @@ private: 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 F> @@ -176,9 +196,6 @@ private: } private: - template <typename T> - T* ptr() const { return (T*)fPtr; } - void* fPtr; }; diff --git a/record/SkRecordDraw.cpp b/record/SkRecordDraw.cpp index 21b2c7a8..4878b3f4 100644 --- a/record/SkRecordDraw.cpp +++ b/record/SkRecordDraw.cpp @@ -12,7 +12,7 @@ namespace { // This is an SkRecord visitor that will draw that SkRecord to an SkCanvas. class Draw : SkNoncopyable { public: - explicit Draw(SkCanvas* canvas) : fCanvas(canvas), fIndex(0), fClipEmpty(false) {} + explicit Draw(SkCanvas* canvas) : fCanvas(canvas), fIndex(0) {} unsigned index() const { return fIndex; } void next() { ++fIndex; } @@ -20,65 +20,36 @@ public: template <typename T> void operator()(const T& r) { if (!this->skip(r)) { this->draw(r); - this->updateClip<T>(); } } private: - // Return true if we can skip this command, false if not. - // Update fIndex here directly to skip more than just this one command. - template <typename T> bool skip(const T&) { - // We can skip most commands if the clip is empty. Exceptions are specialized below. - return fClipEmpty; - } - // No base case, so we'll be compile-time checked that we implemented all possibilities below. template <typename T> void draw(const T&); - // Update fClipEmpty if necessary. - template <typename T> void updateClip() { - // Most commands don't change the clip. Exceptions are specialized below. + // 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 SkRecords::PairedPushCull& r) { + if (fCanvas->quickReject(r.base->rect)) { + fIndex += r.skip; + return true; + } + return false; + } + bool skip(const SkRecords::BoundedDrawPosTextH& r) { + return fCanvas->quickRejectY(r.minY, r.maxY); } SkCanvas* fCanvas; unsigned fIndex; - bool fClipEmpty; }; -// TODO(mtklein): do this specialization with template traits instead of macros - -// These commands may change the clip. -#define UPDATE_CLIP(T) template <> void Draw::updateClip<SkRecords::T>() \ - { fClipEmpty = fCanvas->isClipEmpty(); } -UPDATE_CLIP(Restore); -UPDATE_CLIP(SaveLayer); -UPDATE_CLIP(ClipPath); -UPDATE_CLIP(ClipRRect); -UPDATE_CLIP(ClipRect); -UPDATE_CLIP(ClipRegion); -#undef UPDATE_CLIP - -// These commands must always run. -#define SKIP(T) template <> bool Draw::skip(const SkRecords::T&) { return false; } -SKIP(Restore); -SKIP(Save); -SKIP(SaveLayer); -SKIP(Clear); -SKIP(PushCull); -SKIP(PopCull); -#undef SKIP - -// We can skip these commands if they're intersecting with a clip that's already empty. -#define SKIP(T) template <> bool Draw::skip(const SkRecords::T& r) \ - { return fClipEmpty && SkRegion::kIntersect_Op == r.op; } -SKIP(ClipPath); -SKIP(ClipRRect); -SKIP(ClipRect); -SKIP(ClipRegion); -#undef SKIP - -// NoOps can always be skipped and draw nothing. -template <> bool Draw::skip(const SkRecords::NoOp&) { return true; } +// NoOps draw nothing. template <> void Draw::draw(const SkRecords::NoOp&) {} #define DRAW(T, call) template <> void Draw::draw(const SkRecords::T& r) { fCanvas->call; } @@ -116,25 +87,8 @@ DRAW(DrawVertices, drawVertices(r.vmode, r.vertexCount, r.vertices, r.texs, r.co r.xmode.get(), r.indices, r.indexCount, r.paint)); #undef DRAW -// Added by SkRecordAnnotateCullingPairs. -template <> bool Draw::skip(const SkRecords::PairedPushCull& r) { - if (fCanvas->quickReject(r.base->rect)) { - fIndex += r.skip; - return true; - } - return false; -} - -// Added by SkRecordBoundDrawPosTextH -template <> bool Draw::skip(const SkRecords::BoundedDrawPosTextH& r) { - return fClipEmpty || fCanvas->quickRejectY(r.minY, r.maxY); -} - -// These draw by proxying to the commands they wrap. (All the optimization is for skip().) -#define DRAW(T) template <> void Draw::draw(const SkRecords::T& r) { this->draw(*r.base); } -DRAW(PairedPushCull); -DRAW(BoundedDrawPosTextH); -#undef DRAW +template <> void Draw::draw(const SkRecords::PairedPushCull& r) { this->draw(*r.base); } +template <> void Draw::draw(const SkRecords::BoundedDrawPosTextH& r) { this->draw(*r.base); } } // namespace diff --git a/record/SkRecordOpts.cpp b/record/SkRecordOpts.cpp index 3cf135fb..5b537de0 100644 --- a/record/SkRecordOpts.cpp +++ b/record/SkRecordOpts.cpp @@ -7,6 +7,7 @@ #include "SkRecordOpts.h" +#include "SkRecordTraits.h" #include "SkRecords.h" #include "SkTDArray.h" @@ -18,10 +19,6 @@ void SkRecordOptimize(SkRecord* record) { SkRecordBoundDrawPosTextH(record); } -// Streamline replacing one command with another. -#define REPLACE(record, index, T, ...) \ - SkNEW_PLACEMENT_ARGS(record->replace<SkRecords::T>(index), SkRecords::T, (__VA_ARGS__)) - namespace { // Convenience base class to share some common implementation code. @@ -44,9 +41,28 @@ public: explicit SaveRestoreNooper(SkRecord* record) : Common(record), fSave(kInactive), fChanged(false) {} - // Most drawing commands reset to inactive state without nooping anything. + // Drawing commands reset state to inactive without nooping. + template <typename T> + SK_WHEN(SkRecords::IsDraw<T>, void) operator()(T*) { fSave = kInactive; } + + // Most non-drawing commands can be ignored. template <typename T> - void operator()(T*) { fSave = kInactive; } + SK_WHEN(!SkRecords::IsDraw<T>, void) operator()(T*) {} + + void operator()(SkRecords::Save* r) { + fSave = SkCanvas::kMatrixClip_SaveFlag == r->flags ? this->index() : kInactive; + } + + void operator()(SkRecords::Restore* r) { + if (fSave != kInactive) { + // Remove everything between the save and restore, inclusive on both sides. + fChanged = true; + for (unsigned i = fSave; i <= this->index(); i++) { + fRecord->replace<SkRecords::NoOp>(i); + } + fSave = kInactive; + } + } bool changed() const { return fChanged; } @@ -56,41 +72,6 @@ private: bool fChanged; }; -// If the command doesn't draw anything, that doesn't reset the state back to inactive. -// TODO(mtklein): do this with some sort of template-based trait mechanism instead of macros -#define DOESNT_DRAW(T) template <> void SaveRestoreNooper::operator()(SkRecords::T*) {} -DOESNT_DRAW(NoOp) -DOESNT_DRAW(Concat) -DOESNT_DRAW(SetMatrix) -DOESNT_DRAW(ClipRect) -DOESNT_DRAW(ClipRRect) -DOESNT_DRAW(ClipPath) -DOESNT_DRAW(ClipRegion) -DOESNT_DRAW(PairedPushCull) -DOESNT_DRAW(PushCull) -DOESNT_DRAW(PopCull) -#undef DOESNT_DRAW - -template <> -void SaveRestoreNooper::operator()(SkRecords::Save* r) { - fSave = SkCanvas::kMatrixClip_SaveFlag == r->flags ? this->index() : kInactive; -} - -template <> -void SaveRestoreNooper::operator()(SkRecords::Restore* r) { - if (fSave != kInactive) { - // Remove everything between the save and restore, inclusive on both sides. - fChanged = true; - SkRecord::Destroyer destroyer; - for (unsigned i = fSave; i <= this->index(); i++) { - fRecord->mutate(i, destroyer); - REPLACE(fRecord, i, NoOp); - } - fSave = kInactive; - } -} - - // Tries to replace PushCull with PairedPushCull, which lets us skip to the paired PopCull // when the canvas can quickReject the cull rect. class CullAnnotator : public Common { @@ -98,8 +79,24 @@ public: explicit CullAnnotator(SkRecord* record) : Common(record) {} // Do nothing to most ops. - template <typename T> - void operator()(T*) {} + template <typename T> void operator()(T*) {} + + void operator()(SkRecords::PushCull* push) { + Pair pair = { this->index(), push }; + fPushStack.push(pair); + } + + void operator()(SkRecords::PopCull* pop) { + Pair push = fPushStack.top(); + fPushStack.pop(); + + SkASSERT(this->index() > push.index); + unsigned skip = this->index() - push.index; + + SkRecords::Adopted<SkRecords::PushCull> adopted(push.command); + SkNEW_PLACEMENT_ARGS(fRecord->replace<SkRecords::PairedPushCull>(push.index, adopted), + SkRecords::PairedPushCull, (&adopted, skip)); + } private: struct Pair { @@ -110,66 +107,46 @@ private: SkTDArray<Pair> fPushStack; }; -template <> -void CullAnnotator::operator()(SkRecords::PushCull* push) { - Pair pair = { this->index(), push }; - fPushStack.push(pair); -} - -template <> -void CullAnnotator::operator()(SkRecords::PopCull* pop) { - Pair push = fPushStack.top(); - fPushStack.pop(); - - SkASSERT(this->index() > push.index); - unsigned skip = this->index() - push.index; - - // PairedPushCull adopts push.command. - REPLACE(fRecord, push.index, PairedPushCull, push.command, skip); -} - // Replaces DrawPosText with DrawPosTextH when all Y coordinates are equal. class StrengthReducer : public Common { public: explicit StrengthReducer(SkRecord* record) : Common(record) {} // Do nothing to most ops. - template <typename T> - void operator()(T*) {} -}; + template <typename T> void operator()(T*) {} -template <> -void StrengthReducer::operator()(SkRecords::DrawPosText* r) { - const unsigned points = r->paint.countText(r->text, r->byteLength); - if (points == 0) { - // No point (ha!). - return; - } - - const SkScalar firstY = r->pos[0].fY; - for (unsigned i = 1; i < points; i++) { - if (r->pos[i].fY != firstY) { - // Needs the full strength of DrawPosText. + void operator()(SkRecords::DrawPosText* r) { + const unsigned points = r->paint.countText(r->text, r->byteLength); + if (points == 0) { + // No point (ha!). return; } - } - // All ys are the same. We can replace DrawPosText with DrawPosTextH. - - // r->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 = &r->pos[0].fX; - for (unsigned i = 0; i < 2*points; i += 2) { - scalars[i/2] = scalars[i]; - } - SkRecord::Destroyer destroyer; - fRecord->mutate(this->index(), destroyer); - REPLACE(fRecord, this->index(), - DrawPosTextH, (char*)r->text, r->byteLength, scalars, firstY, r->paint); -} + const SkScalar firstY = r->pos[0].fY; + for (unsigned i = 1; i < points; i++) { + if (r->pos[i].fY != firstY) { + // Needs the full strength of DrawPosText. + return; + } + } + // All ys are the same. We can replace DrawPosText with DrawPosTextH. + + // r->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 = &r->pos[0].fX; + for (unsigned i = 0; i < 2*points; i += 2) { + scalars[i/2] = scalars[i]; + } + // Extend lifetime of r to the end of the method so we can copy its parts. + SkRecords::Adopted<SkRecords::DrawPosText> adopted(r); + SkNEW_PLACEMENT_ARGS(fRecord->replace<SkRecords::DrawPosTextH>(this->index(), adopted), + SkRecords::DrawPosTextH, + (r->text, r->byteLength, scalars, firstY, r->paint)); + } +}; // Tries to replace DrawPosTextH with BoundedDrawPosTextH, which knows conservative upper and lower // bounds to use with SkCanvas::quickRejectY. @@ -178,35 +155,37 @@ public: explicit TextBounder(SkRecord* record) : Common(record) {} // Do nothing to most ops. - template <typename T> - void operator()(T*) {} -}; + template <typename T> void operator()(T*) {} -template <> -void TextBounder::operator()(SkRecords::DrawPosTextH* r) { - // 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 (r->paint.isVerticalText() || !r->paint.canComputeFastBounds()) { - return; + void operator()(SkRecords::DrawPosTextH* r) { + // 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 (r->paint.isVerticalText() || !r->paint.canComputeFastBounds()) { + return; + } + + // 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 = r->paint.getTextSize() * 1.5f; + SkDEBUGCODE(SkPaint::FontMetrics metrics;) + SkDEBUGCODE(r->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, r->y - buffer, SK_Scalar1, r->y + buffer); + SkRect adjusted = r->paint.computeFastBounds(bounds, &bounds); + + SkRecords::Adopted<SkRecords::DrawPosTextH> adopted(r); + SkNEW_PLACEMENT_ARGS( + fRecord->replace<SkRecords::BoundedDrawPosTextH>(this->index(), adopted), + SkRecords::BoundedDrawPosTextH, + (&adopted, adjusted.fTop, adjusted.fBottom)); } +}; - // 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 = r->paint.getTextSize() * 1.5f; - SkDEBUGCODE(SkPaint::FontMetrics metrics;) - SkDEBUGCODE(r->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, r->y - buffer, SK_Scalar1, r->y + buffer); - SkRect adjusted = r->paint.computeFastBounds(bounds, &bounds); - - // BoundedDrawPosTextH adopts r. - REPLACE(fRecord, this->index(), BoundedDrawPosTextH, r, adjusted.fTop, adjusted.fBottom); -} template <typename Pass> static void run_pass(Pass& pass, SkRecord* record) { @@ -242,5 +221,3 @@ void SkRecordBoundDrawPosTextH(SkRecord* record) { TextBounder bounder(record); run_pass(bounder, record); } - -#undef REPLACE diff --git a/record/SkRecordOpts.h b/record/SkRecordOpts.h index c9cbccf9..6db7abca 100644 --- a/record/SkRecordOpts.h +++ b/record/SkRecordOpts.h @@ -15,15 +15,15 @@ void SkRecordOptimize(SkRecord*); // Turns logical no-op Save-[non-drawing command]*-Restore patterns into actual no-ops. -void SkRecordNoopSaveRestores(SkRecord*); // TODO(mtklein): add unit tests +void SkRecordNoopSaveRestores(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*); // TODO(mtklein): add unit tests +void SkRecordReduceDrawPosTextStrength(SkRecord*); // Calculate min and max Y bounds for DrawPosTextH commands, for use with SkCanvas::quickRejectY. -void SkRecordBoundDrawPosTextH(SkRecord*); // TODO(mtklein): add unit tests +void SkRecordBoundDrawPosTextH(SkRecord*); #endif//SkRecordOpts_DEFINED diff --git a/record/SkRecordTraits.h b/record/SkRecordTraits.h new file mode 100644 index 00000000..570a717e --- /dev/null +++ b/record/SkRecordTraits.h @@ -0,0 +1,31 @@ +#include "SkRecords.h" +#include "SkTLogic.h" + +// Type traits that are useful for working with SkRecords. + +namespace SkRecords { + +namespace { + +// Abstracts away whether the T is optional or not. +template <typename T> const T* as_ptr(const SkRecords::Optional<T>& x) { return x; } +template <typename T> const T* as_ptr(const T& x) { return &x; } + +} // namespace + +// Gets the paint from any command that may have one. +template <typename Command> const SkPaint* GetPaint(const Command& x) { return as_ptr(x.paint); } + +// Have a paint? You are a draw command! +template <typename Command> struct IsDraw { + SK_CREATE_MEMBER_DETECTOR(paint); + static const bool value = HasMember_paint<Command>::value; +}; + +// Have a clip op? You are a clip command. +template <typename Command> struct IsClip { + SK_CREATE_MEMBER_DETECTOR(op); + static const bool value = HasMember_op<Command>::value; +}; + +} // namespace SkRecords diff --git a/record/SkRecorder.cpp b/record/SkRecorder.cpp index 345597c5..fe4f35f5 100644 --- a/record/SkRecorder.cpp +++ b/record/SkRecorder.cpp @@ -12,6 +12,10 @@ SkRecorder::SkRecorder(SkRecorder::Mode mode, SkRecord* record, int width, int height) : SkCanvas(width, height), fMode(mode), 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__)) diff --git a/record/SkRecorder.h b/record/SkRecorder.h index dc3de294..e6bddd75 100644 --- a/record/SkRecorder.h +++ b/record/SkRecorder.h @@ -28,6 +28,9 @@ public: // Does not take ownership of the SkRecord. SkRecorder(Mode mode, 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, diff --git a/record/SkRecording.cpp b/record/SkRecording.cpp index 6af19593..57743622 100644 --- a/record/SkRecording.cpp +++ b/record/SkRecording.cpp @@ -16,38 +16,29 @@ namespace EXPERIMENTAL { SkPlayback::SkPlayback(const SkRecord* record) : fRecord(record) {} -SkPlayback::~SkPlayback() { - SkDELETE(fRecord); -} +SkPlayback::~SkPlayback() {} void SkPlayback::draw(SkCanvas* canvas) const { - SkASSERT(fRecord != NULL); + SkASSERT(fRecord.get() != NULL); SkRecordDraw(*fRecord, canvas); } -/*static*/ SkRecording* SkRecording::Create(int width, int height) { - return SkNEW_ARGS(SkRecording, (width, height)); -} +SkRecording::SkRecording(int width, int height) + : fRecord(SkNEW(SkRecord)) + , fRecorder(SkNEW_ARGS(SkRecorder, (SkRecorder::kReadWrite_Mode, fRecord.get(), width, height))) + {} -SkRecording::SkRecording(int width, int height) { - SkRecord* record = SkNEW(SkRecord); - fRecorder = SkNEW_ARGS(SkRecorder, (SkRecorder::kReadWrite_Mode, record, width, height)); - fRecord = record; +SkPlayback* SkRecording::releasePlayback() { + SkASSERT(fRecorder->unique()); + fRecorder->forgetRecord(); + SkRecordOptimize(fRecord.get()); + return SkNEW_ARGS(SkPlayback, (fRecord.detach())); } -/*static*/ const SkPlayback* SkRecording::Delete(SkRecording* recording) { - SkRecord* record = recording->fRecord; - SkRecordOptimize(record); - SkDELETE(recording); - return SkNEW_ARGS(SkPlayback, (record)); -} - -SkRecording::~SkRecording() { - SkDELETE(fRecorder); -} +SkRecording::~SkRecording() {} SkCanvas* SkRecording::canvas() { - return fRecorder; + return fRecord.get() ? fRecorder.get() : NULL; } } // namespace EXPERIMENTAL diff --git a/record/SkRecords.h b/record/SkRecords.h index 8b96e8d9..bfa15496 100644 --- a/record/SkRecords.h +++ b/record/SkRecords.h @@ -133,7 +133,12 @@ template <typename T> class Adopted : SkNoncopyable { public: Adopted(T* ptr) : fPtr(ptr) { SkASSERT(fPtr); } - ~Adopted() { fPtr->~T(); } + 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: @@ -142,9 +147,10 @@ private: // PODArray doesn't own the pointer's memory, and we assume the data is POD. template <typename T> -class PODArray : SkNoncopyable { +class PODArray { public: PODArray(T* ptr) : fPtr(ptr) {} + // Default copy and assign. ACT_AS_PTR(fPtr); private: |