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) {