| /* | |
| * Copyright 2016 Google Inc. | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "include/core/SkCanvas.h" | |
| #include "include/core/SkColor.h" | |
| #include "include/core/SkData.h" | |
| #include "include/core/SkDrawable.h" | |
| #include "include/core/SkFlattenable.h" | |
| #include "include/core/SkFont.h" | |
| #include "include/core/SkImageFilter.h" | |
| #include "include/core/SkMaskFilter.h" | |
| #include "include/core/SkPaint.h" | |
| #include "include/core/SkPictureRecorder.h" | |
| #include "include/core/SkRect.h" | |
| #include "include/core/SkRefCnt.h" | |
| #include "include/core/SkTypes.h" | |
| #include "src/core/SkPathEffectBase.h" | |
| #include "src/core/SkReadBuffer.h" | |
| #include "src/core/SkWriteBuffer.h" | |
| #include "src/effects/colorfilters/SkColorFilterBase.h" | |
| #include "src/shaders/SkShaderBase.h" | |
| #include "tests/Test.h" | |
| #include "tools/fonts/FontToolUtils.h" | |
| #include <cstdint> | |
| #include <cstring> | |
| class IntDrawable : public SkDrawable { | |
| public: | |
| IntDrawable(uint32_t a, uint32_t b, uint32_t c, uint32_t d) | |
| : fA(a) | |
| , fB(b) | |
| , fC(c) | |
| , fD(d) | |
| {} | |
| void flatten(SkWriteBuffer& buffer) const override { | |
| buffer.writeUInt(fA); | |
| buffer.writeUInt(fB); | |
| buffer.writeUInt(fC); | |
| buffer.writeUInt(fD); | |
| } | |
| static sk_sp<SkFlattenable> CreateProc(SkReadBuffer& buffer) { | |
| uint32_t a = buffer.readUInt(); | |
| uint32_t b = buffer.readUInt(); | |
| uint32_t c = buffer.readUInt(); | |
| uint32_t d = buffer.readUInt(); | |
| return sk_sp<IntDrawable>(new IntDrawable(a, b, c, d)); | |
| } | |
| Factory getFactory() const override { return CreateProc; } | |
| uint32_t a() const { return fA; } | |
| uint32_t b() const { return fB; } | |
| uint32_t c() const { return fC; } | |
| uint32_t d() const { return fD; } | |
| const char* getTypeName() const override { return "IntDrawable"; } | |
| protected: | |
| SkRect onGetBounds() override { return SkRect::MakeEmpty(); } | |
| void onDraw(SkCanvas*) override {} | |
| private: | |
| uint32_t fA; | |
| uint32_t fB; | |
| uint32_t fC; | |
| uint32_t fD; | |
| }; | |
| class PaintDrawable : public SkDrawable { | |
| public: | |
| PaintDrawable(const SkPaint& paint) | |
| : fPaint(paint) | |
| {} | |
| void flatten(SkWriteBuffer& buffer) const override { | |
| buffer.writePaint(fPaint); | |
| } | |
| static sk_sp<SkFlattenable> CreateProc(SkReadBuffer& buffer) { | |
| return sk_sp<PaintDrawable>(new PaintDrawable(buffer.readPaint())); | |
| } | |
| Factory getFactory() const override { return CreateProc; } | |
| const SkPaint& paint() const { return fPaint; } | |
| const char* getTypeName() const override { return "PaintDrawable"; } | |
| protected: | |
| SkRect onGetBounds() override { return SkRect::MakeEmpty(); } | |
| void onDraw(SkCanvas*) override {} | |
| private: | |
| SkPaint fPaint; | |
| }; | |
| class CompoundDrawable : public SkDrawable { | |
| public: | |
| CompoundDrawable(uint32_t a, uint32_t b, uint32_t c, uint32_t d, const SkPaint& paint) | |
| : fIntDrawable(new IntDrawable(a, b, c, d)) | |
| , fPaintDrawable(new PaintDrawable(paint)) | |
| {} | |
| CompoundDrawable(IntDrawable* intDrawable, PaintDrawable* paintDrawable) | |
| : fIntDrawable(SkRef(intDrawable)) | |
| , fPaintDrawable(SkRef(paintDrawable)) | |
| {} | |
| void flatten(SkWriteBuffer& buffer) const override { | |
| buffer.writeFlattenable(fIntDrawable.get()); | |
| buffer.writeFlattenable(fPaintDrawable.get()); | |
| } | |
| static sk_sp<SkFlattenable> CreateProc(SkReadBuffer& buffer) { | |
| sk_sp<SkFlattenable> intDrawable( | |
| buffer.readFlattenable(SkFlattenable::kSkDrawable_Type)); | |
| SkASSERT(intDrawable); | |
| SkASSERT(!strcmp("IntDrawable", intDrawable->getTypeName())); | |
| sk_sp<SkFlattenable> paintDrawable( | |
| buffer.readFlattenable(SkFlattenable::kSkDrawable_Type)); | |
| SkASSERT(paintDrawable); | |
| SkASSERT(!strcmp("PaintDrawable", paintDrawable->getTypeName())); | |
| return sk_sp<CompoundDrawable>(new CompoundDrawable((IntDrawable*) intDrawable.get(), | |
| (PaintDrawable*) paintDrawable.get())); | |
| } | |
| Factory getFactory() const override { return CreateProc; } | |
| IntDrawable* intDrawable() const { return fIntDrawable.get(); } | |
| PaintDrawable* paintDrawable() const { return fPaintDrawable.get(); } | |
| const char* getTypeName() const override { return "CompoundDrawable"; } | |
| protected: | |
| SkRect onGetBounds() override { return SkRect::MakeEmpty(); } | |
| void onDraw(SkCanvas*) override {} | |
| private: | |
| sk_sp<IntDrawable> fIntDrawable; | |
| sk_sp<PaintDrawable> fPaintDrawable; | |
| }; | |
| class RootDrawable : public SkDrawable { | |
| public: | |
| RootDrawable(uint32_t a, uint32_t b, uint32_t c, uint32_t d, const SkPaint& paint, | |
| uint32_t e, uint32_t f, uint32_t g, uint32_t h, SkDrawable* drawable) | |
| : fCompoundDrawable(new CompoundDrawable(a, b, c, d, paint)) | |
| , fIntDrawable(new IntDrawable(e, f, g, h)) | |
| , fDrawable(SkRef(drawable)) | |
| {} | |
| RootDrawable(CompoundDrawable* compoundDrawable, IntDrawable* intDrawable, | |
| SkDrawable* drawable) | |
| : fCompoundDrawable(SkRef(compoundDrawable)) | |
| , fIntDrawable(SkRef(intDrawable)) | |
| , fDrawable(SkRef(drawable)) | |
| {} | |
| void flatten(SkWriteBuffer& buffer) const override { | |
| buffer.writeFlattenable(fCompoundDrawable.get()); | |
| buffer.writeFlattenable(fIntDrawable.get()); | |
| buffer.writeFlattenable(fDrawable.get()); | |
| } | |
| static sk_sp<SkFlattenable> CreateProc(SkReadBuffer& buffer) { | |
| sk_sp<SkFlattenable> compoundDrawable( | |
| buffer.readFlattenable(SkFlattenable::kSkDrawable_Type)); | |
| SkASSERT(compoundDrawable); | |
| SkASSERT(!strcmp("CompoundDrawable", compoundDrawable->getTypeName())); | |
| sk_sp<SkFlattenable> intDrawable( | |
| buffer.readFlattenable(SkFlattenable::kSkDrawable_Type)); | |
| SkASSERT(intDrawable); | |
| SkASSERT(!strcmp("IntDrawable", intDrawable->getTypeName())); | |
| sk_sp<SkFlattenable> drawable( | |
| buffer.readFlattenable(SkFlattenable::kSkDrawable_Type)); | |
| SkASSERT(drawable); | |
| return sk_sp<RootDrawable>(new RootDrawable((CompoundDrawable*) compoundDrawable.get(), | |
| (IntDrawable*) intDrawable.get(), | |
| (SkDrawable*) drawable.get())); | |
| } | |
| Factory getFactory() const override { return CreateProc; } | |
| CompoundDrawable* compoundDrawable() const { return fCompoundDrawable.get(); } | |
| IntDrawable* intDrawable() const { return fIntDrawable.get(); } | |
| SkDrawable* drawable() const { return fDrawable.get(); } | |
| const char* getTypeName() const override { return "RootDrawable"; } | |
| protected: | |
| SkRect onGetBounds() override { return SkRect::MakeEmpty(); } | |
| void onDraw(SkCanvas*) override {} | |
| private: | |
| sk_sp<CompoundDrawable> fCompoundDrawable; | |
| sk_sp<IntDrawable> fIntDrawable; | |
| sk_sp<SkDrawable> fDrawable; | |
| }; | |
| // Register these drawables for deserialization some time before main(). | |
| static struct Initializer { | |
| Initializer() { | |
| SK_REGISTER_FLATTENABLE(IntDrawable); | |
| SK_REGISTER_FLATTENABLE(PaintDrawable); | |
| SK_REGISTER_FLATTENABLE(CompoundDrawable); | |
| SK_REGISTER_FLATTENABLE(RootDrawable); | |
| } | |
| } initializer; | |
| DEF_TEST(FlattenDrawable, r) { | |
| // Create and serialize the test drawable | |
| sk_sp<SkDrawable> drawable(new IntDrawable(1, 2, 3, 4)); | |
| SkPaint paint; | |
| paint.setColor(SK_ColorBLUE); | |
| sk_sp<RootDrawable> root(new RootDrawable(5, 6, 7, 8, paint, 9, 10, 11, 12, drawable.get())); | |
| SkBinaryWriteBuffer writeBuffer({}); | |
| writeBuffer.writeFlattenable(root.get()); | |
| // Copy the contents of the write buffer into a read buffer | |
| sk_sp<SkData> data = SkData::MakeUninitialized(writeBuffer.bytesWritten()); | |
| writeBuffer.writeToMemory(data->writable_data()); | |
| SkReadBuffer readBuffer(data->data(), data->size()); | |
| // Deserialize and verify the drawable | |
| sk_sp<SkDrawable> out((SkDrawable*)readBuffer.readFlattenable(SkFlattenable::kSkDrawable_Type)); | |
| REPORTER_ASSERT(r, out); | |
| REPORTER_ASSERT(r, !strcmp("RootDrawable", out->getTypeName())); | |
| RootDrawable* rootOut = (RootDrawable*) out.get(); | |
| REPORTER_ASSERT(r, 5 == rootOut->compoundDrawable()->intDrawable()->a()); | |
| REPORTER_ASSERT(r, 6 == rootOut->compoundDrawable()->intDrawable()->b()); | |
| REPORTER_ASSERT(r, 7 == rootOut->compoundDrawable()->intDrawable()->c()); | |
| REPORTER_ASSERT(r, 8 == rootOut->compoundDrawable()->intDrawable()->d()); | |
| REPORTER_ASSERT(r, SK_ColorBLUE == | |
| rootOut->compoundDrawable()->paintDrawable()->paint().getColor()); | |
| REPORTER_ASSERT(r, 9 == rootOut->intDrawable()->a()); | |
| REPORTER_ASSERT(r, 10 == rootOut->intDrawable()->b()); | |
| REPORTER_ASSERT(r, 11 == rootOut->intDrawable()->c()); | |
| REPORTER_ASSERT(r, 12 == rootOut->intDrawable()->d()); | |
| // Note that we can still recognize the generic drawable as an IntDrawable | |
| SkDrawable* generic = rootOut->drawable(); | |
| REPORTER_ASSERT(r, !strcmp("IntDrawable", generic->getTypeName())); | |
| IntDrawable* integer = (IntDrawable*) generic; | |
| REPORTER_ASSERT(r, 1 == integer->a()); | |
| REPORTER_ASSERT(r, 2 == integer->b()); | |
| REPORTER_ASSERT(r, 3 == integer->c()); | |
| REPORTER_ASSERT(r, 4 == integer->d()); | |
| } | |
| DEF_TEST(FlattenRecordedDrawable, r) { | |
| // Record a set of canvas draw commands | |
| SkPictureRecorder recorder; | |
| SkCanvas* canvas = recorder.beginRecording(1000.0f, 1000.0f); | |
| SkPaint paint; | |
| paint.setColor(SK_ColorGREEN); | |
| canvas->drawPoint(42.0f, 17.0f, paint); | |
| paint.setColor(SK_ColorRED); | |
| canvas->drawPaint(paint); | |
| SkPaint textPaint; | |
| textPaint.setColor(SK_ColorBLUE); | |
| canvas->drawString("TEXT", 467.0f, 100.0f, ToolUtils::DefaultFont(), textPaint); | |
| // Draw some drawables as well | |
| sk_sp<SkDrawable> drawable(new IntDrawable(1, 2, 3, 4)); | |
| sk_sp<RootDrawable> root(new RootDrawable(5, 6, 7, 8, paint, 9, 10, 11, 12, drawable.get())); | |
| canvas->drawDrawable(root.get(), 747.0f, 242.0f); | |
| sk_sp<PaintDrawable> paintDrawable(new PaintDrawable(paint)); | |
| canvas->drawDrawable(paintDrawable.get(), 500.0, 500.0f); | |
| sk_sp<CompoundDrawable> comDrawable(new CompoundDrawable(13, 14, 15, 16, textPaint)); | |
| canvas->drawDrawable(comDrawable.get(), 10.0f, 10.0f); | |
| // Serialize the recorded drawable | |
| sk_sp<SkDrawable> recordedDrawable = recorder.finishRecordingAsDrawable(); | |
| SkBinaryWriteBuffer writeBuffer({}); | |
| writeBuffer.writeFlattenable(recordedDrawable.get()); | |
| // Copy the contents of the write buffer into a read buffer | |
| sk_sp<SkData> data = SkData::MakeUninitialized(writeBuffer.bytesWritten()); | |
| writeBuffer.writeToMemory(data->writable_data()); | |
| SkReadBuffer readBuffer(data->data(), data->size()); | |
| // Deserialize and verify the drawable | |
| sk_sp<SkDrawable> out((SkDrawable*)readBuffer.readFlattenable(SkFlattenable::kSkDrawable_Type)); | |
| REPORTER_ASSERT(r, out); | |
| REPORTER_ASSERT(r, !strcmp("SkRecordedDrawable", out->getTypeName())); | |
| } | |
| // be sure these constructs compile, don't assert, and return null | |
| DEF_TEST(Flattenable_EmptyDeserialze, reporter) { | |
| auto data = SkData::MakeEmpty(); | |
| #define test(name) REPORTER_ASSERT(reporter, !name::Deserialize(data->data(), data->size())) | |
| test(SkPathEffectBase); | |
| test(SkMaskFilter); | |
| test(SkShaderBase); // todo: make this just be shader! | |
| test(SkColorFilterBase); | |
| test(SkImageFilter); | |
| #undef test | |
| } | |