SkPaint: eliminate some dead bytes in 64-bit build.
+ memcpy-based copy constructor was hiding this gap -> manual copy constructor.
+ Split tests for finer-grained failures.
BUG=skia:
Committed: http://code.google.com/p/skia/source/detail?r=13856
Committed: http://code.google.com/p/skia/source/detail?r=13887
R=reed@google.com, mtklein@google.com
Author: mtklein@chromium.org
Review URL: https://codereview.chromium.org/203203003
git-svn-id: http://skia.googlecode.com/svn/trunk@13927 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/include/core/SkPaint.h b/include/core/SkPaint.h
index e86d011..7a647f0 100644
--- a/include/core/SkPaint.h
+++ b/include/core/SkPaint.h
@@ -1046,10 +1046,6 @@
private:
SkTypeface* fTypeface;
- SkScalar fTextSize;
- SkScalar fTextScaleX;
- SkScalar fTextSkewX;
-
SkPathEffect* fPathEffect;
SkShader* fShader;
SkXfermode* fXfermode;
@@ -1060,10 +1056,12 @@
SkImageFilter* fImageFilter;
SkAnnotation* fAnnotation;
+ SkScalar fTextSize;
+ SkScalar fTextScaleX;
+ SkScalar fTextSkewX;
SkColor fColor;
SkScalar fWidth;
SkScalar fMiterLimit;
-
union {
struct {
// all of these bitfields should add up to 32
@@ -1078,11 +1076,11 @@
};
uint32_t fBitfields;
};
+ uint32_t fDirtyBits;
+
uint32_t getBitfields() const { return fBitfields; }
void setBitfields(uint32_t bitfields);
- uint32_t fDirtyBits;
-
SkDrawCacheProc getDrawCacheProc() const;
SkMeasureCacheProc getMeasureCacheProc(TextBufferDirection dir,
bool needFullMetrics) const;
diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp
index 2449ed6..9cb61bd 100644
--- a/src/core/SkPaint.cpp
+++ b/src/core/SkPaint.cpp
@@ -69,14 +69,7 @@
#endif
SkPaint::SkPaint() {
- // since we may have padding, we zero everything so that our memcmp() call
- // in operator== will work correctly.
- // with this, we can skip 0 and null individual initializations
- sk_bzero(this, sizeof(*this));
-
-#if 0 // not needed with the bzero call above
fTypeface = NULL;
- fTextSkewX = 0;
fPathEffect = NULL;
fShader = NULL;
fXfermode = NULL;
@@ -86,21 +79,25 @@
fLooper = NULL;
fImageFilter = NULL;
fAnnotation = NULL;
- fWidth = 0;
- fDirtyBits = 0;
-#endif
- fTextSize = SkPaintDefaults_TextSize;
- fTextScaleX = SK_Scalar1;
- fColor = SK_ColorBLACK;
- fMiterLimit = SkPaintDefaults_MiterLimit;
- fFlags = SkPaintDefaults_Flags;
- fCapType = kDefault_Cap;
- fJoinType = kDefault_Join;
- fTextAlign = kLeft_Align;
- fStyle = kFill_Style;
+ fTextSize = SkPaintDefaults_TextSize;
+ fTextScaleX = SK_Scalar1;
+ fTextSkewX = 0;
+ fColor = SK_ColorBLACK;
+ fWidth = 0;
+ fMiterLimit = SkPaintDefaults_MiterLimit;
+
+ // Zero all bitfields, then set some non-zero defaults.
+ fBitfields = 0;
+ fFlags = SkPaintDefaults_Flags;
+ fCapType = kDefault_Cap;
+ fJoinType = kDefault_Join;
+ fTextAlign = kLeft_Align;
+ fStyle = kFill_Style;
fTextEncoding = kUTF8_TextEncoding;
- fHinting = SkPaintDefaults_Hinting;
+ fHinting = SkPaintDefaults_Hinting;
+
+ fDirtyBits = 0;
#ifdef SK_BUILD_FOR_ANDROID
new (&fPaintOptionsAndroid) SkPaintOptionsAndroid;
fGenerationID = 0;
@@ -108,22 +105,36 @@
}
SkPaint::SkPaint(const SkPaint& src) {
- memcpy(this, &src, sizeof(src));
+#define COPY(field) field = src.field
+#define REF_COPY(field) field = SkSafeRef(src.field)
- SkSafeRef(fTypeface);
- SkSafeRef(fPathEffect);
- SkSafeRef(fShader);
- SkSafeRef(fXfermode);
- SkSafeRef(fMaskFilter);
- SkSafeRef(fColorFilter);
- SkSafeRef(fRasterizer);
- SkSafeRef(fLooper);
- SkSafeRef(fImageFilter);
- SkSafeRef(fAnnotation);
+ REF_COPY(fTypeface);
+ REF_COPY(fPathEffect);
+ REF_COPY(fShader);
+ REF_COPY(fXfermode);
+ REF_COPY(fMaskFilter);
+ REF_COPY(fColorFilter);
+ REF_COPY(fRasterizer);
+ REF_COPY(fLooper);
+ REF_COPY(fImageFilter);
+ REF_COPY(fAnnotation);
+
+ COPY(fTextSize);
+ COPY(fTextScaleX);
+ COPY(fTextSkewX);
+ COPY(fColor);
+ COPY(fWidth);
+ COPY(fMiterLimit);
+ COPY(fBitfields);
+ COPY(fDirtyBits);
#ifdef SK_BUILD_FOR_ANDROID
new (&fPaintOptionsAndroid) SkPaintOptionsAndroid(src.fPaintOptionsAndroid);
+ COPY(fGenerationID);
#endif
+
+#undef COPY
+#undef REF_COPY
}
SkPaint::~SkPaint() {
@@ -140,52 +151,68 @@
}
SkPaint& SkPaint::operator=(const SkPaint& src) {
+#define COPY(field) field = src.field
+#define REF_COPY(field) SkSafeUnref(field); field = SkSafeRef(src.field)
+
SkASSERT(&src);
- SkSafeRef(src.fTypeface);
- SkSafeRef(src.fPathEffect);
- SkSafeRef(src.fShader);
- SkSafeRef(src.fXfermode);
- SkSafeRef(src.fMaskFilter);
- SkSafeRef(src.fColorFilter);
- SkSafeRef(src.fRasterizer);
- SkSafeRef(src.fLooper);
- SkSafeRef(src.fImageFilter);
- SkSafeRef(src.fAnnotation);
+ REF_COPY(fTypeface);
+ REF_COPY(fPathEffect);
+ REF_COPY(fShader);
+ REF_COPY(fXfermode);
+ REF_COPY(fMaskFilter);
+ REF_COPY(fColorFilter);
+ REF_COPY(fRasterizer);
+ REF_COPY(fLooper);
+ REF_COPY(fImageFilter);
+ REF_COPY(fAnnotation);
- SkSafeUnref(fTypeface);
- SkSafeUnref(fPathEffect);
- SkSafeUnref(fShader);
- SkSafeUnref(fXfermode);
- SkSafeUnref(fMaskFilter);
- SkSafeUnref(fColorFilter);
- SkSafeUnref(fRasterizer);
- SkSafeUnref(fLooper);
- SkSafeUnref(fImageFilter);
- SkSafeUnref(fAnnotation);
+ COPY(fTextSize);
+ COPY(fTextScaleX);
+ COPY(fTextSkewX);
+ COPY(fColor);
+ COPY(fWidth);
+ COPY(fMiterLimit);
+ COPY(fBitfields);
+ COPY(fDirtyBits);
#ifdef SK_BUILD_FOR_ANDROID
fPaintOptionsAndroid.~SkPaintOptionsAndroid();
-
- uint32_t oldGenerationID = fGenerationID;
-#endif
- memcpy(this, &src, sizeof(src));
-#ifdef SK_BUILD_FOR_ANDROID
- fGenerationID = oldGenerationID + 1;
-
new (&fPaintOptionsAndroid) SkPaintOptionsAndroid(src.fPaintOptionsAndroid);
+ ++fGenerationID;
#endif
return *this;
+
+#undef COPY
+#undef REF_COPY
}
bool operator==(const SkPaint& a, const SkPaint& b) {
+#define EQUAL(field) (a.field == b.field)
+ // Don't check fGenerationID or fDirtyBits, which can be different for logically equal paints.
+ return EQUAL(fTypeface)
+ && EQUAL(fPathEffect)
+ && EQUAL(fShader)
+ && EQUAL(fXfermode)
+ && EQUAL(fMaskFilter)
+ && EQUAL(fColorFilter)
+ && EQUAL(fRasterizer)
+ && EQUAL(fLooper)
+ && EQUAL(fImageFilter)
+ && EQUAL(fAnnotation)
+ && EQUAL(fTextSize)
+ && EQUAL(fTextScaleX)
+ && EQUAL(fTextSkewX)
+ && EQUAL(fColor)
+ && EQUAL(fWidth)
+ && EQUAL(fMiterLimit)
+ && EQUAL(fBitfields)
#ifdef SK_BUILD_FOR_ANDROID
- //assumes that fGenerationID is the last field in the struct
- return !memcmp(&a, &b, SK_OFFSETOF(SkPaint, fGenerationID));
-#else
- return !memcmp(&a, &b, sizeof(a));
+ && EQUAL(fPaintOptionsAndroid)
#endif
+ ;
+#undef EQUAL
}
void SkPaint::reset() {
diff --git a/tests/PaintTest.cpp b/tests/PaintTest.cpp
index e7954b9..c41e23a 100644
--- a/tests/PaintTest.cpp
+++ b/tests/PaintTest.cpp
@@ -59,7 +59,11 @@
return count;
}
-static void test_cmap(skiatest::Reporter* reporter) {
+DEF_TEST(Paint_cmap, reporter) {
+ // need to implement charsToGlyphs on other backends (e.g. linux, win)
+ // before we can run this tests everywhere
+ return;
+
static const int NGLYPHS = 64;
SkUnichar src[NGLYPHS];
@@ -101,8 +105,7 @@
REPORTER_ASSERT(reporter, NGLYPHS == nglyphs);
REPORTER_ASSERT(reporter, index == first);
- REPORTER_ASSERT(reporter,
- !memcmp(glyphs0, glyphs1, NGLYPHS * sizeof(uint16_t)));
+ REPORTER_ASSERT(reporter, 0 == memcmp(glyphs0, glyphs1, NGLYPHS * sizeof(uint16_t)));
if (contains) {
REPORTER_ASSERT(reporter, NGLYPHS == first);
} else {
@@ -113,7 +116,7 @@
}
// temparary api for bicubic, just be sure we can set/clear it
-static void test_filterlevel(skiatest::Reporter* reporter) {
+DEF_TEST(Paint_filterlevel, reporter) {
SkPaint p0, p1;
REPORTER_ASSERT(reporter,
@@ -137,7 +140,7 @@
}
}
-static void test_copy(skiatest::Reporter* reporter) {
+DEF_TEST(Paint_copy, reporter) {
SkPaint paint;
// set a few member variables
paint.setStyle(SkPaint::kStrokeAndFill_Style);
@@ -159,7 +162,7 @@
uint32_t paintGenID = paint.getGenerationID();
uint32_t copiedPaintGenID = copiedPaint.getGenerationID();
REPORTER_ASSERT(reporter, paintGenID == copiedPaintGenID);
- REPORTER_ASSERT(reporter, !memcmp(&paint, &copiedPaint, sizeof(paint)));
+ REPORTER_ASSERT(reporter, paint == copiedPaint);
#endif
// copy the paint using the equal operator and check they are the same
@@ -171,7 +174,7 @@
REPORTER_ASSERT(reporter, paint.getGenerationID() == paintGenID);
REPORTER_ASSERT(reporter, copiedPaint.getGenerationID() != copiedPaintGenID);
copiedPaintGenID = copiedPaint.getGenerationID(); // reset to the new value
- REPORTER_ASSERT(reporter, memcmp(&paint, &copiedPaint, sizeof(paint)));
+ REPORTER_ASSERT(reporter, paint == copiedPaint); // operator== ignores fGenerationID
#endif
// clean the paint and check they are back to their initial states
@@ -185,14 +188,15 @@
// the reset function should increment the Generation ID
REPORTER_ASSERT(reporter, paint.getGenerationID() != paintGenID);
REPORTER_ASSERT(reporter, copiedPaint.getGenerationID() != copiedPaintGenID);
- REPORTER_ASSERT(reporter, memcmp(&cleanPaint, &paint, sizeof(cleanPaint)));
- REPORTER_ASSERT(reporter, memcmp(&cleanPaint, &copiedPaint, sizeof(cleanPaint)));
+ // operator== ignores fGenerationID
+ REPORTER_ASSERT(reporter, cleanPaint == paint);
+ REPORTER_ASSERT(reporter, cleanPaint == copiedPaint);
#endif
}
// found and fixed for webkit: mishandling when we hit recursion limit on
// mostly degenerate cubic flatness test
-static void regression_cubic(skiatest::Reporter* reporter) {
+DEF_TEST(Paint_regression_cubic, reporter) {
SkPath path, stroke;
SkPaint paint;
@@ -225,7 +229,7 @@
}
// found and fixed for android: not initializing rect for string's of length 0
-static void regression_measureText(skiatest::Reporter* reporter) {
+DEF_TEST(Paint_regression_measureText, reporter) {
SkPaint paint;
paint.setTextSize(12.0f);
@@ -238,23 +242,6 @@
REPORTER_ASSERT(reporter, r.isEmpty());
}
-DEF_TEST(Paint, reporter) {
- // TODO add general paint tests
- test_copy(reporter);
-
- // regression tests
- regression_cubic(reporter);
- regression_measureText(reporter);
-
- test_filterlevel(reporter);
-
- // need to implement charsToGlyphs on other backends (e.g. linux, win)
- // before we can run this tests everywhere
- if (false) {
- test_cmap(reporter);
- }
-}
-
#define ASSERT(expr) REPORTER_ASSERT(r, expr)
DEF_TEST(Paint_FlatteningTraits, r) {