diff options
-rw-r--r-- | include/core/SkPaint.h | 33 | ||||
-rw-r--r-- | src/core/SkPaint.cpp | 151 | ||||
-rw-r--r-- | src/core/SkPictureFlat.h | 10 | ||||
-rw-r--r-- | src/core/SkPicturePlayback.cpp | 6 | ||||
-rw-r--r-- | tests/PaintTest.cpp | 40 |
5 files changed, 216 insertions, 24 deletions
diff --git a/include/core/SkPaint.h b/include/core/SkPaint.h index abb859950c..bf0f91ceb2 100644 --- a/include/core/SkPaint.h +++ b/include/core/SkPaint.h @@ -980,6 +980,11 @@ public: SkDEVCODE(void toString(SkString*) const;) + struct FlatteningTraits { + static void Flatten(SkWriteBuffer& buffer, const SkPaint& paint); + static void Unflatten(SkReadBuffer& buffer, SkPaint* paint); + }; + private: SkTypeface* fTypeface; SkScalar fTextSize; @@ -999,15 +1004,25 @@ private: SkColor fColor; SkScalar fWidth; SkScalar fMiterLimit; - // 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 fFreeBits : 4; + + union { + struct { + // 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 fFreeBits : 4; + }; + uint32_t fBitfields; + }; + uint32_t getBitfields() const { return fBitfields; } + void setBitfields(uint32_t bitfields); + + uint32_t fDirtyBits; SkDrawCacheProc getDrawCacheProc() const; diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp index c3f217cefc..9191ce0067 100644 --- a/src/core/SkPaint.cpp +++ b/src/core/SkPaint.cpp @@ -35,6 +35,26 @@ #include "SkTypeface.h" #include "SkXfermode.h" +enum { + kColor_DirtyBit = 1 << 0, + kBitfields_DirtyBit = 1 << 1, + kTextSize_DirtyBit = 1 << 2, + kTextScaleX_DirtyBit = 1 << 3, + kTextSkewX_DirtyBit = 1 << 4, + kStrokeWidth_DirtyBit = 1 << 5, + kStrokeMiter_DirtyBit = 1 << 6, + kPathEffect_DirtyBit = 1 << 7, + kShader_DirtyBit = 1 << 8, + kXfermode_DirtyBit = 1 << 9, + kMaskFilter_DirtyBit = 1 << 10, + kColorFilter_DirtyBit = 1 << 11, + kRasterizer_DirtyBit = 1 << 12, + kLooper_DirtyBit = 1 << 13, + kImageFilter_DirtyBit = 1 << 14, + kTypeface_DirtyBit = 1 << 15, + kAnnotation_DirtyBit = 1 << 16, + kPaintOptionsAndroid_DirtyBit = 1 << 17, +}; // define this to get a printf for out-of-range parameter in setters // e.g. setTextSize(-1) @@ -55,8 +75,8 @@ SkPaint::SkPaint() { sk_bzero(this, sizeof(*this)); #if 0 // not needed with the bzero call above - fTypeface = NULL; - fTextSkewX = 0; + fTypeface = NULL; + fTextSkewX = 0; fPathEffect = NULL; fShader = NULL; fXfermode = NULL; @@ -66,7 +86,8 @@ SkPaint::SkPaint() { fLooper = NULL; fImageFilter = NULL; fAnnotation = NULL; - fWidth = 0; + fWidth = 0; + fDirtyBits = 0; #endif fTextSize = SkPaintDefaults_TextSize; @@ -198,6 +219,7 @@ void SkPaint::setPaintOptionsAndroid(const SkPaintOptionsAndroid& options) { if (options != fPaintOptionsAndroid) { fPaintOptionsAndroid = options; GEN_ID_INC; + fDirtyBits |= kPaintOptionsAndroid_DirtyBit; } } #endif @@ -228,11 +250,13 @@ void SkPaint::setFilterLevel(FilterLevel level) { void SkPaint::setHinting(Hinting hintingLevel) { GEN_ID_INC_EVAL((unsigned) hintingLevel != fHinting); fHinting = hintingLevel; + fDirtyBits |= kBitfields_DirtyBit; } void SkPaint::setFlags(uint32_t flags) { GEN_ID_INC_EVAL(fFlags != flags); fFlags = flags; + fDirtyBits |= kBitfields_DirtyBit; } void SkPaint::setAntiAlias(bool doAA) { @@ -287,6 +311,7 @@ void SkPaint::setStyle(Style style) { if ((unsigned)style < kStyleCount) { GEN_ID_INC_EVAL((unsigned)style != fStyle); fStyle = style; + fDirtyBits |= kBitfields_DirtyBit; } else { #ifdef SK_REPORT_API_RANGE_CHECK SkDebugf("SkPaint::setStyle(%d) out of range\n", style); @@ -297,6 +322,7 @@ void SkPaint::setStyle(Style style) { void SkPaint::setColor(SkColor color) { GEN_ID_INC_EVAL(color != fColor); fColor = color; + fDirtyBits |= kColor_DirtyBit; } void SkPaint::setAlpha(U8CPU a) { @@ -312,6 +338,7 @@ void SkPaint::setStrokeWidth(SkScalar width) { if (width >= 0) { GEN_ID_INC_EVAL(width != fWidth); fWidth = width; + fDirtyBits |= kStrokeWidth_DirtyBit; } else { #ifdef SK_REPORT_API_RANGE_CHECK SkDebugf("SkPaint::setStrokeWidth() called with negative value\n"); @@ -323,6 +350,7 @@ void SkPaint::setStrokeMiter(SkScalar limit) { if (limit >= 0) { GEN_ID_INC_EVAL(limit != fMiterLimit); fMiterLimit = limit; + fDirtyBits |= kStrokeMiter_DirtyBit; } else { #ifdef SK_REPORT_API_RANGE_CHECK SkDebugf("SkPaint::setStrokeMiter() called with negative value\n"); @@ -334,6 +362,7 @@ void SkPaint::setStrokeCap(Cap ct) { if ((unsigned)ct < kCapCount) { GEN_ID_INC_EVAL((unsigned)ct != fCapType); fCapType = SkToU8(ct); + fDirtyBits |= kBitfields_DirtyBit; } else { #ifdef SK_REPORT_API_RANGE_CHECK SkDebugf("SkPaint::setStrokeCap(%d) out of range\n", ct); @@ -345,6 +374,7 @@ void SkPaint::setStrokeJoin(Join jt) { if ((unsigned)jt < kJoinCount) { GEN_ID_INC_EVAL((unsigned)jt != fJoinType); fJoinType = SkToU8(jt); + fDirtyBits |= kBitfields_DirtyBit; } else { #ifdef SK_REPORT_API_RANGE_CHECK SkDebugf("SkPaint::setStrokeJoin(%d) out of range\n", jt); @@ -358,6 +388,7 @@ void SkPaint::setTextAlign(Align align) { if ((unsigned)align < kAlignCount) { GEN_ID_INC_EVAL((unsigned)align != fTextAlign); fTextAlign = SkToU8(align); + fDirtyBits |= kBitfields_DirtyBit; } else { #ifdef SK_REPORT_API_RANGE_CHECK SkDebugf("SkPaint::setTextAlign(%d) out of range\n", align); @@ -369,6 +400,7 @@ void SkPaint::setTextSize(SkScalar ts) { if (ts >= 0) { GEN_ID_INC_EVAL(ts != fTextSize); fTextSize = ts; + fDirtyBits |= kTextSize_DirtyBit; } else { #ifdef SK_REPORT_API_RANGE_CHECK SkDebugf("SkPaint::setTextSize() called with negative value\n"); @@ -379,17 +411,20 @@ void SkPaint::setTextSize(SkScalar ts) { void SkPaint::setTextScaleX(SkScalar scaleX) { GEN_ID_INC_EVAL(scaleX != fTextScaleX); fTextScaleX = scaleX; + fDirtyBits |= kTextScaleX_DirtyBit; } void SkPaint::setTextSkewX(SkScalar skewX) { GEN_ID_INC_EVAL(skewX != fTextSkewX); fTextSkewX = skewX; + fDirtyBits |= kTextSkewX_DirtyBit; } void SkPaint::setTextEncoding(TextEncoding encoding) { if ((unsigned)encoding <= kGlyphID_TextEncoding) { GEN_ID_INC_EVAL((unsigned)encoding != fTextEncoding); fTextEncoding = encoding; + fDirtyBits |= kBitfields_DirtyBit; } else { #ifdef SK_REPORT_API_RANGE_CHECK SkDebugf("SkPaint::setTextEncoding(%d) out of range\n", encoding); @@ -399,33 +434,43 @@ void SkPaint::setTextEncoding(TextEncoding encoding) { /////////////////////////////////////////////////////////////////////////////// +// Returns dst with the given bitmask enabled or disabled, depending on value. +inline static uint32_t set_mask(uint32_t dst, uint32_t bitmask, bool value) { + return value ? (dst | bitmask) : (dst & ~bitmask); +} + SkTypeface* SkPaint::setTypeface(SkTypeface* font) { SkRefCnt_SafeAssign(fTypeface, font); GEN_ID_INC; + fDirtyBits = set_mask(fDirtyBits, kTypeface_DirtyBit, font != NULL); return font; } SkRasterizer* SkPaint::setRasterizer(SkRasterizer* r) { SkRefCnt_SafeAssign(fRasterizer, r); GEN_ID_INC; + fDirtyBits = set_mask(fDirtyBits, kRasterizer_DirtyBit, r != NULL); return r; } SkDrawLooper* SkPaint::setLooper(SkDrawLooper* looper) { SkRefCnt_SafeAssign(fLooper, looper); GEN_ID_INC; + fDirtyBits = set_mask(fDirtyBits, kLooper_DirtyBit, looper != NULL); return looper; } SkImageFilter* SkPaint::setImageFilter(SkImageFilter* imageFilter) { SkRefCnt_SafeAssign(fImageFilter, imageFilter); GEN_ID_INC; + fDirtyBits = set_mask(fDirtyBits, kImageFilter_DirtyBit, imageFilter != NULL); return imageFilter; } SkAnnotation* SkPaint::setAnnotation(SkAnnotation* annotation) { SkRefCnt_SafeAssign(fAnnotation, annotation); GEN_ID_INC; + fDirtyBits = set_mask(fDirtyBits, kAnnotation_DirtyBit, annotation != NULL); return annotation; } @@ -2149,18 +2194,21 @@ void SkPaint::unflatten(SkReadBuffer& buffer) { SkShader* SkPaint::setShader(SkShader* shader) { GEN_ID_INC_EVAL(shader != fShader); SkRefCnt_SafeAssign(fShader, shader); + fDirtyBits = set_mask(fDirtyBits, kShader_DirtyBit, shader != NULL); return shader; } SkColorFilter* SkPaint::setColorFilter(SkColorFilter* filter) { GEN_ID_INC_EVAL(filter != fColorFilter); SkRefCnt_SafeAssign(fColorFilter, filter); + fDirtyBits = set_mask(fDirtyBits, kColorFilter_DirtyBit, filter != NULL); return filter; } SkXfermode* SkPaint::setXfermode(SkXfermode* mode) { GEN_ID_INC_EVAL(mode != fXfermode); SkRefCnt_SafeAssign(fXfermode, mode); + fDirtyBits = set_mask(fDirtyBits, kXfermode_DirtyBit, mode != NULL); return mode; } @@ -2168,18 +2216,21 @@ SkXfermode* SkPaint::setXfermodeMode(SkXfermode::Mode mode) { SkSafeUnref(fXfermode); fXfermode = SkXfermode::Create(mode); GEN_ID_INC; + fDirtyBits = set_mask(fDirtyBits, kXfermode_DirtyBit, fXfermode != NULL); return fXfermode; } SkPathEffect* SkPaint::setPathEffect(SkPathEffect* effect) { GEN_ID_INC_EVAL(effect != fPathEffect); SkRefCnt_SafeAssign(fPathEffect, effect); + fDirtyBits = set_mask(fDirtyBits, kPathEffect_DirtyBit, effect != NULL); return effect; } SkMaskFilter* SkPaint::setMaskFilter(SkMaskFilter* filter) { GEN_ID_INC_EVAL(filter != fMaskFilter); SkRefCnt_SafeAssign(fMaskFilter, filter); + fDirtyBits = set_mask(fDirtyBits, kMaskFilter_DirtyBit, filter != NULL); return filter; } @@ -2550,3 +2601,97 @@ bool SkPaint::nothingToDraw() const { } return false; } + +void SkPaint::setBitfields(uint32_t bitfields) { + fBitfields = bitfields; + fDirtyBits |= kBitfields_DirtyBit; +} + +inline static unsigned popcount(uint8_t x) { + // As in Hacker's delight, adapted for just 8 bits. + x = (x & 0x55) + ((x >> 1) & 0x55); // a b c d w x y z -> a+b c+d w+x y+z + x = (x & 0x33) + ((x >> 2) & 0x33); // a+b c+d w+x y+z -> a+b+c+d w+x+y+z + x = (x & 0x0F) + ((x >> 4) & 0x0F); // a+b+c+d w+x+y+z -> a+b+c+d+w+x+y+z + return x; +} + +void SkPaint::FlatteningTraits::Flatten(SkWriteBuffer& buffer, const SkPaint& paint) { + const uint32_t dirty = paint.fDirtyBits; + + // Each of the low 7 dirty bits corresponds to a 4-byte flat value, plus one for the dirty bits. + const size_t flatBytes = 4 * (popcount(dirty & 127) + 1); + SkASSERT(flatBytes <= 32); + uint32_t* u32 = buffer.reserve(flatBytes); + *u32++ = dirty; + if (dirty == 0) { + return; + } + +#define F(dst, field) if (dirty & k##field##_DirtyBit) *dst++ = paint.get##field() + F(u32, Color); + F(u32, Bitfields); + SkScalar* f32 = reinterpret_cast<SkScalar*>(u32); + F(f32, TextSize); + F(f32, TextScaleX); + F(f32, TextSkewX); + F(f32, StrokeWidth); + F(f32, StrokeMiter); +#undef F +#define F(field) if (dirty & k##field##_DirtyBit) buffer.writeFlattenable(paint.get##field()) + F(PathEffect); + F(Shader); + F(Xfermode); + F(MaskFilter); + F(ColorFilter); + F(Rasterizer); + F(Looper); + F(ImageFilter); +#undef F + if (dirty & kTypeface_DirtyBit) buffer.writeTypeface(paint.getTypeface()); + if (dirty & kAnnotation_DirtyBit) paint.getAnnotation()->writeToBuffer(buffer); +#ifdef SK_BUILD_FOR_ANDROID + if (dirty & kPaintOptionsAndroid_DirtyBit) paint.getPaintOptionsAndroid().flatten(buffer); +#endif +} + +void SkPaint::FlatteningTraits::Unflatten(SkReadBuffer& buffer, SkPaint* paint) { + const uint32_t dirty = buffer.readUInt(); + if (dirty == 0) { + return; + } +#define F(field, reader) if (dirty & k##field##_DirtyBit) paint->set##field(buffer.reader()) +// Same function, except it unrefs the object newly set on the paint: +#define F_UNREF(field, reader) \ + if (dirty & k##field##_DirtyBit) \ + paint->set##field(buffer.reader())->unref() + + F(Color, readUInt); + F(Bitfields, readUInt); + F(TextSize, readScalar); + F(TextScaleX, readScalar); + F(TextSkewX, readScalar); + F(StrokeWidth, readScalar); + F(StrokeMiter, readScalar); + F_UNREF(PathEffect, readPathEffect); + F_UNREF(Shader, readShader); + F_UNREF(Xfermode, readXfermode); + F_UNREF(MaskFilter, readMaskFilter); + F_UNREF(ColorFilter, readColorFilter); + F_UNREF(Rasterizer, readRasterizer); + F_UNREF(Looper, readDrawLooper); + F_UNREF(ImageFilter, readImageFilter); + F(Typeface, readTypeface); +#undef F +#undef F_UNREF + if (dirty & kAnnotation_DirtyBit) { + paint->setAnnotation(SkNEW_ARGS(SkAnnotation, (buffer)))->unref(); + } +#ifdef SK_BUILD_FOR_ANDROID + if (dirty & kPaintOptionsAndroid_DirtyBit) { + SkPaintOptionsAndroid options; + options.unflatten(buffer); + paint->setPaintOptionsAndroid(options); + } +#endif + SkASSERT(dirty == paint->fDirtyBits); +} diff --git a/src/core/SkPictureFlat.h b/src/core/SkPictureFlat.h index 68b398482a..77b29c4d34 100644 --- a/src/core/SkPictureFlat.h +++ b/src/core/SkPictureFlat.h @@ -564,15 +564,7 @@ private: SkFlatData::Identity, SkFlatData::Hash, SkFlatData::Equal> fHash; }; -struct SkPaintTraits { - static void Flatten(SkWriteBuffer& buffer, const SkPaint& paint) { - paint.flatten(buffer); - } - static void Unflatten(SkReadBuffer& buffer, SkPaint* paint) { - paint->unflatten(buffer); - } -}; -typedef SkFlatDictionary<SkPaint, SkPaintTraits> SkPaintDictionary; +typedef SkFlatDictionary<SkPaint, SkPaint::FlatteningTraits> SkPaintDictionary; class SkChunkFlatController : public SkFlatController { public: diff --git a/src/core/SkPicturePlayback.cpp b/src/core/SkPicturePlayback.cpp index 57356459ee..b546d397c0 100644 --- a/src/core/SkPicturePlayback.cpp +++ b/src/core/SkPicturePlayback.cpp @@ -209,7 +209,7 @@ SkPicturePlayback::SkPicturePlayback(const SkPicturePlayback& src, SkPictCopyInf for (int i = 0; i < paintCount; i++) { if (needs_deep_copy(src.fPaints->at(i))) { deepCopyInfo->paintData[i] = - SkFlatData::Create<SkPaintTraits>(&deepCopyInfo->controller, + SkFlatData::Create<SkPaint::FlatteningTraits>(&deepCopyInfo->controller, src.fPaints->at(i), 0); } else { @@ -230,8 +230,8 @@ SkPicturePlayback::SkPicturePlayback(const SkPicturePlayback& src, SkPictCopyInf SkTypefacePlayback* tfPlayback = deepCopyInfo->controller.getTypefacePlayback(); for (int i = 0; i < paintCount; i++) { if (deepCopyInfo->paintData[i]) { - deepCopyInfo->paintData[i]->unflatten<SkPaintTraits>(&fPaints->writableAt(i), - bmHeap, tfPlayback); + deepCopyInfo->paintData[i]->unflatten<SkPaint::FlatteningTraits>( + &fPaints->writableAt(i), bmHeap, tfPlayback); } else { // needs_deep_copy was false, so just need to assign fPaints->writableAt(i) = src.fPaints->at(i); diff --git a/tests/PaintTest.cpp b/tests/PaintTest.cpp index 3210e77f19..e7954b9136 100644 --- a/tests/PaintTest.cpp +++ b/tests/PaintTest.cpp @@ -11,8 +11,11 @@ #include "SkPaint.h" #include "SkPath.h" #include "SkRandom.h" +#include "SkReadBuffer.h" #include "SkTypeface.h" #include "SkUtils.h" +#include "SkWriteBuffer.h" +#include "SkXfermode.h" #include "Test.h" static size_t uni_to_utf8(const SkUnichar src[], void* dst, int count) { @@ -251,3 +254,40 @@ DEF_TEST(Paint, reporter) { test_cmap(reporter); } } + +#define ASSERT(expr) REPORTER_ASSERT(r, expr) + +DEF_TEST(Paint_FlatteningTraits, r) { + SkPaint paint; + paint.setColor(0x00AABBCC); + paint.setTextScaleX(1.0f); // Encoded despite being the default value. + paint.setTextSize(19); + paint.setXfermode(SkXfermode::Create(SkXfermode::kModulate_Mode))->unref(); + paint.setLooper(NULL); // Ignored. + + SkWriteBuffer writer; + SkPaint::FlatteningTraits::Flatten(writer, paint); + const size_t expectedBytesWritten = sizeof(void*) == 8 ? 48 : 40; + ASSERT(expectedBytesWritten == writer.bytesWritten()); + + const uint32_t* written = writer.getWriter32()->contiguousArray(); + SkASSERT(written != NULL); + ASSERT(*written == ((1<<0) | (1<<2) | (1<<3) | (1<<9))); // Dirty bits for our 4. + + SkReadBuffer reader(written, writer.bytesWritten()); + SkPaint other; + SkPaint::FlatteningTraits::Unflatten(reader, &other); + ASSERT(reader.offset() == writer.bytesWritten()); + + // No matter the encoding, these must always hold. + ASSERT(other.getColor() == paint.getColor()); + ASSERT(other.getTextScaleX() == paint.getTextScaleX()); + ASSERT(other.getTextSize() == paint.getTextSize()); + ASSERT(other.getLooper() == paint.getLooper()); + + // We have to be a little looser and compare just the modes. Pointers might not be the same. + SkXfermode::Mode otherMode, paintMode; + ASSERT(other.getXfermode()->asMode(&otherMode)); + ASSERT(paint.getXfermode()->asMode(&paintMode)); + ASSERT(otherMode == paintMode); +} |