From b0a34d80c5c7b06db8083a547f8e499781a9c169 Mon Sep 17 00:00:00 2001 From: "reed@google.com" Date: Wed, 11 Jul 2012 19:57:55 +0000 Subject: [PATCH] idea: add annotation to SkPaint Review URL: https://codereview.appspot.com/6355050 git-svn-id: http://skia.googlecode.com/svn/trunk@4555 2bbb7eff-a529-9590-31e7-b0007b416f81 --- gyp/core.gyp | 1 + gyp/tests.gyp | 1 + include/core/SkAnnotation.h | 87 +++++++++++++++++++++++++++++++++++++ include/core/SkPaint.h | 21 ++++++++- src/core/SkAnnotation.cpp | 62 ++++++++++++++++++++++++++ src/core/SkDevice.cpp | 7 +++ src/core/SkPaint.cpp | 27 ++++++++++-- src/gpu/SkGpuDevice.cpp | 8 ++++ tests/AnnotationTest.cpp | 34 +++++++++++++++ 9 files changed, 244 insertions(+), 4 deletions(-) create mode 100644 include/core/SkAnnotation.h create mode 100644 src/core/SkAnnotation.cpp create mode 100644 tests/AnnotationTest.cpp diff --git a/gyp/core.gyp b/gyp/core.gyp index a109d7b23..7b5402404 100644 --- a/gyp/core.gyp +++ b/gyp/core.gyp @@ -9,6 +9,7 @@ '../src/core/ARGB32_Clamp_Bilinear_BitmapShader.h', '../src/core/Sk64.cpp', '../src/core/SkAAClip.cpp', + '../src/core/SkAnnotation.cpp', '../src/core/SkAdvancedTypefaceMetrics.cpp', '../src/core/SkAlphaRuns.cpp', '../src/core/SkAntiRun.h', diff --git a/gyp/tests.gyp b/gyp/tests.gyp index cdf755ba5..c6e2786f0 100644 --- a/gyp/tests.gyp +++ b/gyp/tests.gyp @@ -17,6 +17,7 @@ ], 'sources': [ '../tests/AAClipTest.cpp', + '../tests/AnnotationTest.cpp', '../tests/BitmapCopyTest.cpp', '../tests/BitmapGetColorTest.cpp', '../tests/BitSetTest.cpp', diff --git a/include/core/SkAnnotation.h b/include/core/SkAnnotation.h new file mode 100644 index 000000000..d401588c3 --- /dev/null +++ b/include/core/SkAnnotation.h @@ -0,0 +1,87 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkAnnotation_DEFINED +#define SkAnnotation_DEFINED + +#include "SkFlattenable.h" + +class SkData; +class SkDataSet; + +/** + * Experimental class for annotating draws. Do not use directly yet. + * Use helper functions at the bottom of this file for now. + */ +class SkAnnotation : public SkFlattenable { +public: + enum Flags { + // If set, the associated drawing primitive should not be drawn + kNoDraw_Flag = 1 << 0, + }; + + SkAnnotation(SkDataSet*, uint32_t flags); + virtual ~SkAnnotation(); + + uint32_t getFlags() const { return fFlags; } + SkDataSet* getDataSet() const { return fDataSet; } + + bool isNoDraw() const { return SkToBool(fFlags & kNoDraw_Flag); } + + /** + * Helper for search the annotation's dataset. + */ + SkData* find(const char name[]) const; + + SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkAnnotation) + +protected: + SkAnnotation(SkFlattenableReadBuffer&); + virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE; + +private: + SkDataSet* fDataSet; + uint32_t fFlags; + + void writeToStream(SkWStream*) const; + void readFromStream(SkStream*); + + typedef SkFlattenable INHERITED; +}; + +/** + * Experimental collection of predefined Keys into the Annotation dictionary + */ +class SkAnnotationKeys { +public: + /** + * Returns the canonical key whose payload is a URL + */ + static const char* URL_Key(); +}; + +/////////////////////////////////////////////////////////////////////////////// +// +// Experimental helper functions to use Annotations +// + +struct SkRect; +class SkCanvas; + +/** + * Experimental! + * + * Annotate the canvas by associating the specified URL with the + * specified rectangle (in local coordinates, just like drawRect). If the + * backend of this canvas does not support annotations, this call is + * safely ignored. + * + * The caller is responsible for managing its ownership of the SkData. + */ +SK_API void SkAnnotateRectWithURL(SkCanvas*, const SkRect&, SkData*); + +#endif diff --git a/include/core/SkPaint.h b/include/core/SkPaint.h index 3d585276b..684bc455b 100644 --- a/include/core/SkPaint.h +++ b/include/core/SkPaint.h @@ -15,6 +15,7 @@ #include "SkDrawLooper.h" #include "SkXfermode.h" +class SkAnnotation; class SkAutoGlyphCache; class SkColorFilter; class SkDescriptor; @@ -581,6 +582,17 @@ public: SkImageFilter* getImageFilter() const { return fImageFilter; } SkImageFilter* setImageFilter(SkImageFilter*); + + SkAnnotation* getAnnotation() const { return fAnnotation; } + SkAnnotation* setAnnotation(SkAnnotation*); + + /** + * Returns true if there is an annotation installed on this paint, and + * the annotation specifics no-drawing. + */ + bool isNoDrawAnnotation() const { + return SkToBool(fPrivFlags & kNoDrawAnnotation_PrivFlag); + } /** * Return the paint's SkDrawLooper (if any). Does not affect the looper's @@ -903,17 +915,24 @@ private: SkRasterizer* fRasterizer; SkDrawLooper* fLooper; SkImageFilter* fImageFilter; + SkAnnotation* fAnnotation; SkColor fColor; SkScalar fWidth; SkScalar fMiterLimit; - unsigned fFlags : 15; + // all of these bitfields should add up to 32 + unsigned fFlags : 16; unsigned fTextAlign : 2; unsigned fCapType : 2; unsigned fJoinType : 2; unsigned fStyle : 2; unsigned fTextEncoding : 2; // 3 values unsigned fHinting : 2; + unsigned fPrivFlags : 4; // these are not flattened/unflattened + + enum PrivFlags { + kNoDrawAnnotation_PrivFlag = 1 << 0, + }; SkDrawCacheProc getDrawCacheProc() const; SkMeasureCacheProc getMeasureCacheProc(TextBufferDirection dir, diff --git a/src/core/SkAnnotation.cpp b/src/core/SkAnnotation.cpp new file mode 100644 index 000000000..94ce6a0fa --- /dev/null +++ b/src/core/SkAnnotation.cpp @@ -0,0 +1,62 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkAnnotation.h" +#include "SkDataSet.h" +#include "SkStream.h" + +SkAnnotation::SkAnnotation(SkDataSet* data, uint32_t flags) { + if (NULL == data) { + data = SkDataSet::NewEmpty(); + } else { + data->ref(); + } + fDataSet = data; + fFlags = flags; +} + +SkAnnotation::~SkAnnotation() { + fDataSet->unref(); +} + +SkAnnotation::SkAnnotation(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) { + fFlags = buffer.readU32(); + fDataSet = SkNEW_ARGS(SkDataSet, (buffer)); +} + +void SkAnnotation::flatten(SkFlattenableWriteBuffer& buffer) const { + buffer.write32(fFlags); + fDataSet->flatten(buffer); +} + +SK_DEFINE_FLATTENABLE_REGISTRAR(SkAnnotation) + +const char* SkAnnotationKeys::URL_Key() { + return "SkAnnotationKey_URL"; +}; + +/////////////////////////////////////////////////////////////////////////////// + +#include "SkCanvas.h" + +void SkAnnotateRectWithURL(SkCanvas* canvas, const SkRect& rect, SkData* value) { + if (NULL == value) { + return; + } + + const char* key = SkAnnotationKeys::URL_Key(); + SkAutoTUnref dataset(SkNEW_ARGS(SkDataSet, (key, value))); + SkAnnotation* ann = SkNEW_ARGS(SkAnnotation, (dataset, + SkAnnotation::kNoDraw_Flag)); + + SkPaint paint; + paint.setAnnotation(ann)->unref(); + SkASSERT(paint.isNoDrawAnnotation()); + + canvas->drawRect(rect, paint); +} + diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp index c5dfc6653..95664c88b 100644 --- a/src/core/SkDevice.cpp +++ b/src/core/SkDevice.cpp @@ -15,6 +15,11 @@ SK_DEFINE_INST_COUNT(SkDevice) /////////////////////////////////////////////////////////////////////////////// +#define CHECK_FOR_NODRAW_ANNOTATION(paint) \ + do { if (paint.isNoDrawAnnotation()) { return; } } while (0) + +/////////////////////////////////////////////////////////////////////////////// + SkDevice::SkDevice(const SkBitmap& bitmap) : fBitmap(bitmap) { fOrigin.setZero(); fMetaData = NULL; @@ -295,12 +300,14 @@ void SkDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, size_t c void SkDevice::drawRect(const SkDraw& draw, const SkRect& r, const SkPaint& paint) { + CHECK_FOR_NODRAW_ANNOTATION(paint); draw.drawRect(r, paint); } void SkDevice::drawPath(const SkDraw& draw, const SkPath& path, const SkPaint& paint, const SkMatrix* prePathMatrix, bool pathIsMutable) { + CHECK_FOR_NODRAW_ANNOTATION(paint); draw.drawPath(path, paint, prePathMatrix, pathIsMutable); } diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp index 08e642cfb..ea2d1f636 100644 --- a/src/core/SkPaint.cpp +++ b/src/core/SkPaint.cpp @@ -6,8 +6,8 @@ * found in the LICENSE file. */ - #include "SkPaint.h" +#include "SkAnnotation.h" #include "SkColorFilter.h" #include "SkFontHost.h" #include "SkImageFilter.h" @@ -55,6 +55,7 @@ SkPaint::SkPaint() { fRasterizer = NULL; fLooper = NULL; fImageFilter = NULL; + fAnnotation = NULL; fWidth = 0; #endif @@ -69,6 +70,7 @@ SkPaint::SkPaint() { fStyle = kFill_Style; fTextEncoding = kUTF8_TextEncoding; fHinting = SkPaintDefaults_Hinting; + fPrivFlags = 0; #ifdef SK_BUILD_FOR_ANDROID fGenerationID = 0; #endif @@ -86,6 +88,7 @@ SkPaint::SkPaint(const SkPaint& src) { SkSafeRef(fRasterizer); SkSafeRef(fLooper); SkSafeRef(fImageFilter); + SkSafeRef(fAnnotation); } SkPaint::~SkPaint() { @@ -98,6 +101,7 @@ SkPaint::~SkPaint() { SkSafeUnref(fRasterizer); SkSafeUnref(fLooper); SkSafeUnref(fImageFilter); + SkSafeUnref(fAnnotation); } SkPaint& SkPaint::operator=(const SkPaint& src) { @@ -112,6 +116,7 @@ SkPaint& SkPaint::operator=(const SkPaint& src) { SkSafeRef(src.fRasterizer); SkSafeRef(src.fLooper); SkSafeRef(src.fImageFilter); + SkSafeRef(src.fAnnotation); SkSafeUnref(fTypeface); SkSafeUnref(fPathEffect); @@ -122,6 +127,7 @@ SkPaint& SkPaint::operator=(const SkPaint& src) { SkSafeUnref(fRasterizer); SkSafeUnref(fLooper); SkSafeUnref(fImageFilter); + SkSafeUnref(fAnnotation); #ifdef SK_BUILD_FOR_ANDROID uint32_t oldGenerationID = fGenerationID; @@ -371,6 +377,16 @@ SkImageFilter* SkPaint::setImageFilter(SkImageFilter* imageFilter) { return imageFilter; } +SkAnnotation* SkPaint::setAnnotation(SkAnnotation* annotation) { + SkRefCnt_SafeAssign(fAnnotation, annotation); + GEN_ID_INC; + + bool isNoDraw = annotation && annotation->isNoDraw(); + fPrivFlags = SkSetClearMask(fPrivFlags, isNoDraw, kNoDrawAnnotation_PrivFlag); + + return annotation; +} + /////////////////////////////////////////////////////////////////////////////// #include "SkGlyphCache.h" @@ -1809,7 +1825,7 @@ static uint32_t pack_4(unsigned a, unsigned b, unsigned c, unsigned d) { enum FlatFlags { kHasTypeface_FlatFlag = 0x01, - kHasEffects_FlatFlag = 0x02 + kHasEffects_FlatFlag = 0x02, }; // The size of a flat paint's POD fields @@ -1833,6 +1849,7 @@ void SkPaint::flatten(SkFlattenableWriteBuffer& buffer) const { asint(this->getColorFilter()) | asint(this->getRasterizer()) | asint(this->getLooper()) | + asint(this->getAnnotation()) | asint(this->getImageFilter())) { flatFlags |= kHasEffects_FlatFlag; } @@ -1870,6 +1887,7 @@ void SkPaint::flatten(SkFlattenableWriteBuffer& buffer) const { buffer.writeFlattenable(this->getRasterizer()); buffer.writeFlattenable(this->getLooper()); buffer.writeFlattenable(this->getImageFilter()); + buffer.writeFlattenable(this->getAnnotation()); } } @@ -1878,6 +1896,8 @@ void SkPaint::unflatten(SkFlattenableReadBuffer& buffer) { const void* podData = buffer.skip(kPODPaintSize); const uint32_t* pod = reinterpret_cast(podData); + fPrivFlags = 0; + // the order we read must match the order we wrote in flatten() this->setTextSize(read_scalar(pod)); this->setTextScaleX(read_scalar(pod)); @@ -1920,6 +1940,7 @@ void SkPaint::unflatten(SkFlattenableReadBuffer& buffer) { SkSafeUnref(this->setRasterizer((SkRasterizer*) buffer.readFlattenable())); SkSafeUnref(this->setLooper((SkDrawLooper*) buffer.readFlattenable())); SkSafeUnref(this->setImageFilter((SkImageFilter*) buffer.readFlattenable())); + SkSafeUnref(this->setAnnotation((SkAnnotation*) buffer.readFlattenable())); } else { this->setPathEffect(NULL); this->setShader(NULL); @@ -2209,7 +2230,7 @@ bool SkImageFilter::asADilate(SkISize* radius) const { return false; } -////// +//////////////////// SK_DEFINE_INST_COUNT(SkDrawLooper) diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index c731999db..fc77c4709 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -71,6 +71,12 @@ enum { /////////////////////////////////////////////////////////////////////////////// +#define CHECK_FOR_NODRAW_ANNOTATION(paint) \ + do { if (paint.isNoDrawAnnotation()) { return; } } while (0) + +/////////////////////////////////////////////////////////////////////////////// + + class SkGpuDevice::SkAutoCachedTexture : public ::SkNoncopyable { public: SkAutoCachedTexture() { } @@ -662,6 +668,7 @@ void SkGpuDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, void SkGpuDevice::drawRect(const SkDraw& draw, const SkRect& rect, const SkPaint& paint) { + CHECK_FOR_NODRAW_ANNOTATION(paint); CHECK_SHOULD_DRAW(draw); bool doStroke = paint.getStyle() != SkPaint::kFill_Style; @@ -976,6 +983,7 @@ bool drawWithMaskFilter(GrContext* context, const SkPath& path, void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& origSrcPath, const SkPaint& paint, const SkMatrix* prePathMatrix, bool pathIsMutable) { + CHECK_FOR_NODRAW_ANNOTATION(paint); CHECK_SHOULD_DRAW(draw); bool doFill = true; diff --git a/tests/AnnotationTest.cpp b/tests/AnnotationTest.cpp new file mode 100644 index 000000000..fe2cd0190 --- /dev/null +++ b/tests/AnnotationTest.cpp @@ -0,0 +1,34 @@ + +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#include "Test.h" +#include "SkAnnotation.h" +#include "SkData.h" +#include "SkCanvas.h" + +static void test_nodraw(skiatest::Reporter* reporter) { + SkBitmap bm; + bm.setConfig(SkBitmap::kARGB_8888_Config, 10, 10); + bm.allocPixels(); + bm.eraseColor(0); + + SkCanvas canvas(bm); + SkRect r = SkRect::MakeWH(SkIntToScalar(10), SkIntToScalar(10)); + + SkAutoDataUnref data(SkData::NewWithCString("http://www.gooogle.com")); + + REPORTER_ASSERT(reporter, 0 == *bm.getAddr32(0, 0)); + SkAnnotateRectWithURL(&canvas, r, data.get()); + REPORTER_ASSERT(reporter, 0 == *bm.getAddr32(0, 0)); +} + +static void TestAnnotation(skiatest::Reporter* reporter) { + test_nodraw(reporter); +} + +#include "TestClassDef.h" +DEFINE_TESTCLASS("Annotation", AnnotationClass, TestAnnotation)