GrPaint encapsulation.

R=robertphillips@google.com
Review URL: https://codereview.appspot.com/6624052

git-svn-id: http://skia.googlecode.com/svn/trunk@5838 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gm/texdata.cpp b/gm/texdata.cpp
index a87be45..724b4ec 100644
--- a/gm/texdata.cpp
+++ b/gm/texdata.cpp
@@ -100,10 +100,7 @@
                 ctx->setRenderTarget(target);
 
                 GrPaint paint;
-                paint.reset();
-                paint.fColor = 0xffffffff;
-                paint.fSrcBlendCoeff = kOne_GrBlendCoeff;
-                paint.fDstBlendCoeff = kISA_GrBlendCoeff;
+                paint.setBlendFunc(kOne_GrBlendCoeff, kISA_GrBlendCoeff);
                 GrMatrix vm;
                 if (i) {
                     vm.setRotate(90 * SK_Scalar1,
diff --git a/include/gpu/GrPaint.h b/include/gpu/GrPaint.h
index 9b07a4b..9f9403e 100644
--- a/include/gpu/GrPaint.h
+++ b/include/gpu/GrPaint.h
@@ -17,9 +17,31 @@
 #include "SkXfermode.h"
 
 /**
- * The paint describes how pixels are colored when the context draws to
- * them. TODO: Make this a "real" class with getters and setters, default
- * values, and documentation.
+ * The paint describes how color and coverage are computed at each pixel by GrContext draw
+ * functions and the how color is blended with the destination pixel.
+ *
+ * The paint allows installation of custom color and coverage stages. New types of stages are
+ * created by subclassing GrCustomStage.
+ *
+ * The primitive color computation starts with the color specified by setColor(). This color is the
+ * input to the first color stage. Each color stage feeds its output to the next color stage. The
+ * final color stage's output color is input to the color filter specified by
+ * setXfermodeColorFilter which it turn feeds into the color matrix. The output of the color matrix
+ * is the final source color, S.
+ *
+ * Fractional pixel coverage follows a similar flow. The coverage is initially the value specified
+ * by setCoverage(). This is input to the first coverage stage. Coverage stages are chained
+ * together in the same manner as color stages. The output of the last stage is modulated by any
+ * fractional coverage produced by anti-aliasing. This last step produces the final coverage, C.
+ *
+ * setBlendFunc() specifies blending coefficients for S (described above) and D, the initial value
+ * of the destination pixel, labeled Bs and Bd respectively. The final value of the destination
+ * pixel is then D' = (1-C)*D + C*(Bd*D + Bs*S).
+ *
+ * Note that the coverage is applied after the blend. This is why they are computed as distinct
+ * values.
+ *
+ * TODO: Encapsulate setXfermodeColorFilter and color matrix in stages and remove from GrPaint.
  */
 class GrPaint {
 public:
@@ -28,20 +50,90 @@
         kMaxCoverageStages  = 1,
     };
 
-    // All the paint fields are public except textures/samplers
-    GrBlendCoeff                fSrcBlendCoeff;
-    GrBlendCoeff                fDstBlendCoeff;
-    bool                        fAntiAlias;
-    bool                        fDither;
-    bool                        fColorMatrixEnabled;
+    GrPaint() { this->reset(); }
 
-    GrColor                     fColor;
-    uint8_t                     fCoverage;
+    GrPaint(const GrPaint& paint) { *this = paint; }
 
-    GrColor                     fColorFilterColor;
-    SkXfermode::Mode            fColorFilterXfermode;
-    float                       fColorMatrix[20];
+    ~GrPaint() {}
 
+    /**
+     * Sets the blending coefficients to use to blend the final primitive color with the
+     * destination color. Defaults to kOne for src and kZero for dst (i.e. src mode).
+     */
+    void setBlendFunc(GrBlendCoeff srcCoeff, GrBlendCoeff dstCoeff) {
+        fSrcBlendCoeff = srcCoeff;
+        fDstBlendCoeff = dstCoeff;
+    }
+    GrBlendCoeff getSrcBlendCoeff() const { return fSrcBlendCoeff; }
+    GrBlendCoeff getDstBlendCoeff() const { return fDstBlendCoeff; }
+
+    /**
+     * The initial color of the drawn primitive. Defaults to solid white.
+     */
+    void setColor(GrColor color) { fColor = color; }
+    GrColor getColor() const { return fColor; }
+
+    /**
+     * Applies fractional coverage to the entire drawn primitive. Defaults to 0xff.
+     */
+    void setCoverage(uint8_t coverage) { fCoverage = coverage; }
+    uint8_t getCoverage() const { return fCoverage; }
+
+    /**
+     * Should primitives be anti-aliased or not. Defaults to false.
+     */
+    void setAntiAlias(bool aa) { fAntiAlias = aa; }
+    bool isAntiAlias() const { return fAntiAlias; }
+
+    /**
+     * Should dithering be applied. Defaults to false.
+     */
+    void setDither(bool dither) { fDither = dither; }
+    bool isDither() const { return fDither; }
+
+    /**
+     * Enables a SkXfermode::Mode-based color filter applied to the primitive color. The constant
+     * color passed to this function is considered the "src" color and the primitive's color is
+     * considered the "dst" color. Defaults to kDst_Mode which equates to simply passing through
+     * the primitive color unmodified.
+     */
+    void setXfermodeColorFilter(SkXfermode::Mode mode, GrColor color) {
+        fColorFilterColor = color;
+        fColorFilterXfermode = mode;
+    }
+    SkXfermode::Mode getColorFilterMode() const { return fColorFilterXfermode; }
+    GrColor getColorFilterColor() const { return fColorFilterColor; }
+
+    /**
+     * Turns off application of a color matrix. By default the color matrix is disabled.
+     */
+    void disableColorMatrix() { fColorMatrixEnabled = false; }
+
+    /**
+     * Specifies and enables a 4 x 5 color matrix.
+     */
+    void setColorMatrix(const float matrix[20]) {
+        fColorMatrixEnabled = true;
+        memcpy(fColorMatrix, matrix, sizeof(fColorMatrix));
+    }
+
+    bool isColorMatrixEnabled() const { return fColorMatrixEnabled; }
+    const float* getColorMatrix() const { return fColorMatrix; }
+
+    /**
+     * Disables both the matrix and SkXfermode::Mode color filters.
+     */
+    void resetColorFilter() {
+        fColorFilterXfermode = SkXfermode::kDst_Mode;
+        fColorFilterColor = GrColorPackRGBA(0xff, 0xff, 0xff, 0xff);
+        fColorMatrixEnabled = false;
+    }
+
+    /**
+     * Specifies a stage of the color pipeline. Usually the texture matrices of color stages apply
+     * to the primitive's positions. Some GrContext calls take explicit coords as an array or a
+     * rect. In this case these are the pre-matrix coords to colorSampler(0).
+     */
     GrSamplerState* colorSampler(int i) {
         GrAssert((unsigned)i < kMaxColorStages);
         return fColorSamplers + i;
@@ -57,8 +149,10 @@
         return (NULL != fColorSamplers[i].getCustomStage());
     }
 
-    // The coverage stage's sampler matrix is always applied to the positions
-    // (i.e. no explicit texture coordinates)
+    /**
+     * Specifies a stage of the coverage pipeline. Coverage stages' texture matrices are always
+     * applied to the primitive's position, never to explicit texture coords.
+     */
     GrSamplerState* coverageSampler(int i) {
         GrAssert((unsigned)i < kMaxCoverageStages);
         return fCoverageSamplers + i;
@@ -95,9 +189,9 @@
     bool hasStage() const { return this->hasColorStage() || this->hasCoverageStage(); }
 
     /**
-     * Preconcats the matrix of all samplers in the mask with the inverse of a
-     * matrix. If the matrix inverse cannot be computed (and there is at least
-     * one enabled stage) then false is returned.
+     * Preconcats the matrix of all samplers in the mask with the inverse of a matrix. If the
+     * matrix inverse cannot be computed (and there is at least one enabled stage) then false is
+     * returned.
      */
     bool preConcatSamplerMatricesWithInverse(const GrMatrix& matrix) {
         GrMatrix inv;
@@ -125,16 +219,6 @@
         return true;
     }
 
-    // uninitialized
-    GrPaint() {
-    }
-
-    GrPaint(const GrPaint& paint) {
-        *this = paint;
-    }
-
-    ~GrPaint() {}
-
     GrPaint& operator=(const GrPaint& paint) {
         fSrcBlendCoeff = paint.fSrcBlendCoeff;
         fDstBlendCoeff = paint.fDstBlendCoeff;
@@ -164,7 +248,9 @@
         return *this;
     }
 
-    // sets paint to src-over, solid white, no texture, no mask
+    /**
+     * Resets the paint to the defaults.
+     */
     void reset() {
         this->resetBlend();
         this->resetOptions();
@@ -175,12 +261,6 @@
         this->resetMasks();
     }
 
-    void resetColorFilter() {
-        fColorFilterXfermode = SkXfermode::kDst_Mode;
-        fColorFilterColor = GrColorPackRGBA(0xff, 0xff, 0xff, 0xff);
-        fColorMatrixEnabled = false;
-    }
-
     // internal use
     // GrPaint's textures and masks map to the first N stages
     // of GrDrawTarget in that order (textures followed by masks)
@@ -195,6 +275,19 @@
     GrSamplerState              fColorSamplers[kMaxColorStages];
     GrSamplerState              fCoverageSamplers[kMaxCoverageStages];
 
+    GrBlendCoeff                fSrcBlendCoeff;
+    GrBlendCoeff                fDstBlendCoeff;
+    bool                        fAntiAlias;
+    bool                        fDither;
+    bool                        fColorMatrixEnabled;
+
+    GrColor                     fColor;
+    uint8_t                     fCoverage;
+
+    GrColor                     fColorFilterColor;
+    SkXfermode::Mode            fColorFilterXfermode;
+    float                       fColorMatrix[20];
+
     void resetBlend() {
         fSrcBlendCoeff = kOne_GrBlendCoeff;
         fDstBlendCoeff = kZero_GrBlendCoeff;
diff --git a/src/effects/SkBlendImageFilter.cpp b/src/effects/SkBlendImageFilter.cpp
index f171400..1388e33 100644
--- a/src/effects/SkBlendImageFilter.cpp
+++ b/src/effects/SkBlendImageFilter.cpp
@@ -217,7 +217,6 @@
     GrMatrix sampleM;
     sampleM.setIDiv(background->width(), background->height());
     GrPaint paint;
-    paint.reset();
     paint.colorSampler(0)->reset(sampleM);
     paint.colorSampler(0)->setCustomStage(SkNEW_ARGS(GrSingleTextureEffect, (background.get())))->unref();
     paint.colorSampler(1)->reset(sampleM);
diff --git a/src/effects/SkMorphologyImageFilter.cpp b/src/effects/SkMorphologyImageFilter.cpp
index 7a4f5a9..e00d94a 100644
--- a/src/effects/SkMorphologyImageFilter.cpp
+++ b/src/effects/SkMorphologyImageFilter.cpp
@@ -431,7 +431,6 @@
     GrMatrix sampleM;
     sampleM.setIDiv(texture->width(), texture->height());
     GrPaint paint;
-    paint.reset();
     paint.colorSampler(0)->reset(sampleM);
     paint.colorSampler(0)->setCustomStage(SkNEW_ARGS(GrMorphologyEffect, (texture, direction, radius, morphType)))->unref();
     context->drawRect(paint, rect);
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index d6e0b50..3e53c19 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -613,13 +613,13 @@
         am.set(this, GrMatrix::I());
     }
     // by definition this fills the entire clip, no need for AA
-    if (paint.fAntiAlias) {
+    if (paint.isAntiAlias()) {
         if (!tmpPaint.isValid()) {
             tmpPaint.set(paint);
             p = tmpPaint.get();
         }
         GrAssert(p == tmpPaint.get());
-        tmpPaint.get()->fAntiAlias = false;
+        tmpPaint.get()->setAntiAlias(false);
     }
     this->drawRect(*p, r);
 }
@@ -736,7 +736,7 @@
     GrRect devRect = rect;
     GrMatrix combinedMatrix;
     bool useVertexCoverage;
-    bool needAA = paint.fAntiAlias &&
+    bool needAA = paint.isAntiAlias() &&
                   !this->getRenderTarget()->isMultisampled();
     bool doAA = needAA && apply_aa_to_rect(target, rect, width, matrix,
                                            &combinedMatrix, &devRect,
@@ -1013,7 +1013,7 @@
                          SkScalar strokeWidth) {
     GrAssert(strokeWidth <= 0);
     if (!isSimilarityTransformation(this->getMatrix()) ||
-        !paint.fAntiAlias ||
+        !paint.isAntiAlias() ||
         rect.height() != rect.width()) {
         SkPath path;
         path.addOval(rect);
@@ -1125,7 +1125,7 @@
     GrDrawTarget* target = this->prepareToDraw(&paint, DEFAULT_BUFFERING);
     GrDrawState::AutoStageDisable atr(fDrawState);
 
-    bool prAA = paint.fAntiAlias && !this->getRenderTarget()->isMultisampled();
+    bool prAA = paint.isAntiAlias() && !this->getRenderTarget()->isMultisampled();
 
     // An Assumption here is that path renderer would use some form of tweaking
     // the src color (either the input alpha or in the frag shader) to implement
diff --git a/src/gpu/GrDrawState.cpp b/src/gpu/GrDrawState.cpp
index bd6e268..f798cda 100644
--- a/src/gpu/GrDrawState.cpp
+++ b/src/gpu/GrDrawState.cpp
@@ -31,18 +31,18 @@
         this->disableStage(s);
     }
 
-    this->setColor(paint.fColor);
+    this->setColor(paint.getColor());
 
-    this->setState(GrDrawState::kDither_StateBit, paint.fDither);
-    this->setState(GrDrawState::kHWAntialias_StateBit, paint.fAntiAlias);
+    this->setState(GrDrawState::kDither_StateBit, paint.isDither());
+    this->setState(GrDrawState::kHWAntialias_StateBit, paint.isAntiAlias());
 
-    if (paint.fColorMatrixEnabled) {
+    if (paint.isColorMatrixEnabled()) {
         this->enableState(GrDrawState::kColorMatrix_StateBit);
-        this->setColorMatrix(paint.fColorMatrix);
+        this->setColorMatrix(paint.getColorMatrix());
     } else {
         this->disableState(GrDrawState::kColorMatrix_StateBit);
     }
-    this->setBlendFunc(paint.fSrcBlendCoeff, paint.fDstBlendCoeff);
-    this->setColorFilter(paint.fColorFilterColor, paint.fColorFilterXfermode);
-    this->setCoverage(paint.fCoverage);
+    this->setBlendFunc(paint.getSrcBlendCoeff(), paint.getDstBlendCoeff());
+    this->setColorFilter(paint.getColorFilterColor(), paint.getColorFilterMode());
+    this->setCoverage(paint.getCoverage());
 }
diff --git a/src/gpu/GrTextContext.cpp b/src/gpu/GrTextContext.cpp
index 5c8b9e5..c245401 100644
--- a/src/gpu/GrTextContext.cpp
+++ b/src/gpu/GrTextContext.cpp
@@ -38,22 +38,21 @@
         drawState->createTextureEffect(kGlyphMaskStage, fCurrTexture, params);
 
         if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
-            if (kOne_GrBlendCoeff != fPaint.fSrcBlendCoeff ||
-                kISA_GrBlendCoeff != fPaint.fDstBlendCoeff ||
+            if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
+                kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
                 fPaint.hasColorStage()) {
                 GrPrintf("LCD Text will not draw correctly.\n");
             }
             // setup blend so that we get mask * paintColor + (1-mask)*dstColor
-            drawState->setBlendConstant(fPaint.fColor);
+            drawState->setBlendConstant(fPaint.getColor());
             drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
             // don't modulate by the paint's color in the frag since we're
             // already doing it via the blend const.
             drawState->setColor(0xffffffff);
         } else {
             // set back to normal in case we took LCD path previously.
-            drawState->setBlendFunc(fPaint.fSrcBlendCoeff,
-                                    fPaint.fDstBlendCoeff);
-            drawState->setColor(fPaint.fColor);
+            drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
+            drawState->setColor(fPaint.getColor());
         }
 
         int nGlyphs = fCurrVertex / 4;
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index a792977..b0b5ed0 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -510,9 +510,8 @@
                                     SkGpuDevice::SkAutoCachedTexture* act,
                                     GrPaint* grPaint) {
 
-    grPaint->fDither    = skPaint.isDither();
-    grPaint->fAntiAlias = skPaint.isAntiAlias();
-    grPaint->fCoverage = 0xFF;
+    grPaint->setDither(skPaint.isDither());
+    grPaint->setAntiAlias(skPaint.isAntiAlias());
 
     SkXfermode::Coeff sm = SkXfermode::kOne_Coeff;
     SkXfermode::Coeff dm = SkXfermode::kISA_Coeff;
@@ -526,17 +525,16 @@
 #endif
         }
     }
-    grPaint->fSrcBlendCoeff = sk_blend_to_grblend(sm);
-    grPaint->fDstBlendCoeff = sk_blend_to_grblend(dm);
+    grPaint->setBlendFunc(sk_blend_to_grblend(sm), sk_blend_to_grblend(dm));
 
     if (justAlpha) {
         uint8_t alpha = skPaint.getAlpha();
-        grPaint->fColor = GrColorPackRGBA(alpha, alpha, alpha, alpha);
+        grPaint->setColor(GrColorPackRGBA(alpha, alpha, alpha, alpha));
         // justAlpha is currently set to true only if there is a texture,
         // so constantColor should not also be true.
         GrAssert(!constantColor);
     } else {
-        grPaint->fColor = SkColor2GrColor(skPaint.getColor());
+        grPaint->setColor(SkColor2GrColor(skPaint.getColor()));
         GrAssert(!grPaint->isColorStageEnabled(kShaderTextureIdx));
     }
     SkColorFilter* colorFilter = skPaint.getColorFilter();
@@ -544,24 +542,17 @@
     SkXfermode::Mode filterMode;
     SkScalar matrix[20];
     SkBitmap colorTransformTable;
-    grPaint->resetColorFilter();
     // TODO: SkColorFilter::asCustomStage()
     if (colorFilter != NULL && colorFilter->asColorMode(&color, &filterMode)) {
-        grPaint->fColorMatrixEnabled = false;
         if (!constantColor) {
-            grPaint->fColorFilterColor = SkColor2GrColor(color);
-            grPaint->fColorFilterXfermode = filterMode;
+            grPaint->setXfermodeColorFilter(filterMode, SkColor2GrColor(color));
         } else {
             SkColor filtered = colorFilter->filterColor(skPaint.getColor());
-            grPaint->fColor = SkColor2GrColor(filtered);
+            grPaint->setColor(SkColor2GrColor(filtered));
         }
     } else if (colorFilter != NULL && colorFilter->asColorMatrix(matrix)) {
-        grPaint->fColorMatrixEnabled = true;
-        memcpy(grPaint->fColorMatrix, matrix, sizeof(matrix));
-        grPaint->fColorFilterXfermode = SkXfermode::kDst_Mode;
+        grPaint->setColorMatrix(matrix);
     } else if (colorFilter != NULL && colorFilter->asComponentTable(&colorTransformTable)) {
-        grPaint->resetColorFilter();
-
         // pass NULL because the color table effect doesn't use tiling or filtering.
         GrTexture* texture = act->set(dev, colorTransformTable, NULL);
         GrSamplerState* colorSampler = grPaint->colorSampler(kColorFilterTextureIdx);
@@ -895,18 +886,16 @@
 
         context->clear(NULL, 0);
         GrPaint tempPaint;
-        tempPaint.reset();
 
-        tempPaint.fAntiAlias = grp->fAntiAlias;
-        if (tempPaint.fAntiAlias) {
+        if (grp->isAntiAlias()) {
+            tempPaint.setAntiAlias(true);
             // AA uses the "coverage" stages on GrDrawTarget. Coverage with a dst
             // blend coeff of zero requires dual source blending support in order
             // to properly blend partially covered pixels. This means the AA
             // code path may not be taken. So we use a dst blend coeff of ISA. We
             // could special case AA draws to a dst surface with known alpha=0 to
             // use a zero dst coeff when dual source blending isn't available.
-            tempPaint.fSrcBlendCoeff = kOne_GrBlendCoeff;
-            tempPaint.fDstBlendCoeff = kISC_GrBlendCoeff;
+            tempPaint.setBlendFunc(kOne_GrBlendCoeff, kISC_GrBlendCoeff);
         }
         // Draw hard shadow to pathTexture with path topleft at origin 0,0.
         context->drawPath(tempPaint, path, pathFillType, &offset);
@@ -928,18 +917,15 @@
                 (GrSingleTextureEffect, (pathTexture)))->unref();
             if (SkMaskFilter::kInner_BlurType == blurType) {
                 // inner:  dst = dst * src
-                paint.fSrcBlendCoeff = kDC_GrBlendCoeff;
-                paint.fDstBlendCoeff = kZero_GrBlendCoeff;
+                paint.setBlendFunc(kDC_GrBlendCoeff, kZero_GrBlendCoeff);
             } else if (SkMaskFilter::kSolid_BlurType == blurType) {
                 // solid:  dst = src + dst - src * dst
                 //             = (1 - dst) * src + 1 * dst
-                paint.fSrcBlendCoeff = kIDC_GrBlendCoeff;
-                paint.fDstBlendCoeff = kOne_GrBlendCoeff;
+                paint.setBlendFunc(kIDC_GrBlendCoeff, kOne_GrBlendCoeff);
             } else if (SkMaskFilter::kOuter_BlurType == blurType) {
                 // outer:  dst = dst * (1 - src)
                 //             = 0 * src + (1 - src) * dst
-                paint.fSrcBlendCoeff = kZero_GrBlendCoeff;
-                paint.fDstBlendCoeff = kISC_GrBlendCoeff;
+                paint.setBlendFunc(kZero_GrBlendCoeff, kISC_GrBlendCoeff);
             }
             context->drawRect(paint, srcRect);
         }
@@ -1059,8 +1045,7 @@
     SkScalar hairlineCoverage;
     if (SkDrawTreatAsHairline(paint, *draw.fMatrix, &hairlineCoverage)) {
         doFill = false;
-        grPaint.fCoverage = SkScalarRoundToInt(hairlineCoverage *
-                                               grPaint.fCoverage);
+        grPaint.setCoverage(SkScalarRoundToInt(hairlineCoverage * grPaint.getCoverage()));
     }
 
     // If we have a prematrix, apply it to the path, optimizing for the case
@@ -1520,7 +1505,6 @@
     GrMatrix sampleM;
     sampleM.setIDiv(srcTexture->width(), srcTexture->height());
     GrPaint paint;
-    paint.reset();
     paint.colorSampler(0)->reset(sampleM);
     paint.colorSampler(0)->setCustomStage(stage);
     context->drawRect(paint, rect);
@@ -1707,7 +1691,6 @@
     }
 
     GrPaint paint;
-    paint.reset();
 
     GrTexture* texture;
     // We assume here that the filter will not attempt to tile the src. Otherwise, this cache lookup
diff --git a/src/gpu/effects/GrConfigConversionEffect.cpp b/src/gpu/effects/GrConfigConversionEffect.cpp
index 3af0979..b9c9f63 100644
--- a/src/gpu/effects/GrConfigConversionEffect.cpp
+++ b/src/gpu/effects/GrConfigConversionEffect.cpp
@@ -176,7 +176,6 @@
         // We then verify that two reads produced the same values.
 
         GrPaint paint;
-        paint.reset();
 
         SkAutoTUnref<GrCustomStage> pmToUPMStage1(SkNEW_ARGS(GrConfigConversionEffect,
                                                              (dataTex, false, *pmToUPMRule)));