static void test_clear(skiatest::Reporter* reporter, GrContext* context,
                       GrTexture* rectangleTexture) {
    if (rectangleTexture->asRenderTarget()) {
        sk_sp<GrDrawContext> dc(
                            context->drawContext(sk_ref_sp(rectangleTexture->asRenderTarget()),
                                                 nullptr));
        if (!dc) {
            ERRORF(reporter, "Could not get GrDrawContext for rectangle texture.");
            return;
        }

        // Clear the whole thing.
        GrColor color0 = GrColorPackRGBA(0xA, 0xB, 0xC, 0xD);
        dc->clear(nullptr, color0, false);

        int w = rectangleTexture->width();
        int h = rectangleTexture->height();
        int pixelCnt = w * h;
        SkAutoTMalloc<uint32_t> expectedPixels(pixelCnt);

        // The clear color is a GrColor, our readback is to kRGBA_8888, which may be different.
        uint32_t expectedColor0 = 0;
        uint8_t* expectedBytes0 = SkTCast<uint8_t*>(&expectedColor0);
        expectedBytes0[0] = GrColorUnpackR(color0);
        expectedBytes0[1] = GrColorUnpackG(color0);
        expectedBytes0[2] = GrColorUnpackB(color0);
        expectedBytes0[3] = GrColorUnpackA(color0);
        for (int i = 0; i < rectangleTexture->width() * rectangleTexture->height(); ++i) {
            expectedPixels.get()[i] = expectedColor0;
        }

        // Clear the the top to a different color.
        GrColor color1 = GrColorPackRGBA(0x1, 0x2, 0x3, 0x4);
        SkIRect rect = SkIRect::MakeWH(w, h/2);
        dc->clear(&rect, color1, false);

        uint32_t expectedColor1 = 0;
        uint8_t* expectedBytes1 = SkTCast<uint8_t*>(&expectedColor1);
        expectedBytes1[0] = GrColorUnpackR(color1);
        expectedBytes1[1] = GrColorUnpackG(color1);
        expectedBytes1[2] = GrColorUnpackB(color1);
        expectedBytes1[3] = GrColorUnpackA(color1);

        for (int y = 0; y < h/2; ++y) {
            for (int x = 0; x < w; ++x) {
                expectedPixels.get()[y * h + x] = expectedColor1;
            }
        }

        test_read_pixels(reporter, context, rectangleTexture, expectedPixels.get());
    }
}
Exemple #2
0
bool GrDrawTarget::srcAlphaWillBeOne(GrVertexLayout layout) const {
    const GrDrawState& drawState = this->getDrawState();

    // Check if per-vertex or constant color may have partial alpha
    if ((layout & kColor_VertexLayoutBit) ||
        0xff != GrColorUnpackA(drawState.getColor())) {
        return false;
    }
    // Check if color filter could introduce an alpha
    // (TODO: Consider being more aggressive with regards to detecting 0xff
    // final alpha from color filter).
    if (SkXfermode::kDst_Mode != drawState.getColorFilterMode()) {
        return false;
    }
    // Check if a color stage could create a partial alpha
    for (int s = 0; s < drawState.getFirstCoverageStage(); ++s) {
        if (this->isStageEnabled(s)) {
            const GrEffect* effect = drawState.getStage(s).getEffect();
            // FIXME: The param indicates whether the texture is opaque or not. However, the effect
            // already controls its textures. It really needs to know whether the incoming color
            // (from a uni, per-vertex colors, or previous stage) is opaque or not.
            if (!effect->isOpaque(true)) {
                return false;
            }
        }
    }
    return true;
}
Exemple #3
0
bool GrDrawTarget::srcAlphaWillBeOne(GrVertexLayout layout) const {
    const GrDrawState& drawState = this->getDrawState();

    // Check if per-vertex or constant color may have partial alpha
    if ((layout & kColor_VertexLayoutBit) ||
        0xff != GrColorUnpackA(drawState.getColor())) {
        return false;
    }
    // Check if color filter could introduce an alpha
    // (TODO: Consider being more aggressive with regards to detecting 0xff
    // final alpha from color filter).
    if (SkXfermode::kDst_Mode != drawState.getColorFilterMode()) {
        return false;
    }
    // Check if a color stage could create a partial alpha
    for (int s = 0; s < drawState.getFirstCoverageStage(); ++s) {
        if (StageWillBeUsed(s, layout, this->getDrawState())) {
            GrAssert(NULL != drawState.getTexture(s));
            GrPixelConfig config = drawState.getTexture(s)->config();
            if (!GrPixelConfigIsOpaque(config)) {
                return false;
            }
        }
    }
    return true;
}
Exemple #4
0
bool GrDrawTarget::srcAlphaWillBeOne() const {
    const GrVertexLayout& layout = this->getGeomSrc().fVertexLayout;

    // Check if per-vertex or constant color may have partial alpha
    if ((layout & kColor_VertexLayoutBit) ||
        0xff != GrColorUnpackA(fCurrDrawState.fColor)) {
        return false;
    }
    // Check if color filter could introduce an alpha
    // (TODO: Consider being more aggressive with regards to detecting 0xff
    // final alpha from color filter).
    if (SkXfermode::kDst_Mode != fCurrDrawState.fColorFilterXfermode) {
        return false;
    }
    // Check if a color stage could create a partial alpha
    for (int s = 0; s < fCurrDrawState.fFirstCoverageStage; ++s) {
        if (StageWillBeUsed(s, layout, fCurrDrawState)) {
            GrAssert(NULL != fCurrDrawState.fTextures[s]);
            GrPixelConfig config = fCurrDrawState.fTextures[s]->config();
            if (!GrPixelConfigIsOpaque(config)) {
                return false;
            }
        }
    }
    return true;
}
Exemple #5
0
bool does_full_buffer_contain_correct_color(GrColor* buffer,
                                            GrColor clearColor,
                                            GrPixelConfig config,
                                            int width,
                                            int height) {
    GrColor matchColor;
    if (kRGBA_8888_GrPixelConfig == config) {
        matchColor = clearColor;
    } else if (kBGRA_8888_GrPixelConfig) {
        // Hack to flip the R, B componets in the GrColor so that the comparrison will work below
        matchColor = GrColorPackRGBA(GrColorUnpackB(clearColor),
                                     GrColorUnpackG(clearColor),
                                     GrColorUnpackR(clearColor),
                                     GrColorUnpackA(clearColor));
    } else {
        // currently only supporting rgba_8888 and bgra_8888
        return false;
    }

    for (int j = 0; j < height; ++j) {
        for (int i = 0; i < width; ++i) {
            if (buffer[j * width + i] != matchColor) {
                return false;
            }
        }
    }
    return true;
}
static void test_clear(skiatest::Reporter* reporter, GrSurfaceContext* rectContext) {
    if (GrRenderTargetContext* rtc = rectContext->asRenderTargetContext()) {
        // Clear the whole thing.
        GrColor color0 = GrColorPackRGBA(0xA, 0xB, 0xC, 0xD);
        rtc->clear(nullptr, color0, GrRenderTargetContext::CanClearFullscreen::kNo);

        int w = rtc->width();
        int h = rtc->height();
        int pixelCnt = w * h;
        SkAutoTMalloc<uint32_t> expectedPixels(pixelCnt);

        // The clear color is a GrColor, our readback is to kRGBA_8888, which may be different.
        uint32_t expectedColor0 = 0;
        uint8_t* expectedBytes0 = SkTCast<uint8_t*>(&expectedColor0);
        expectedBytes0[0] = GrColorUnpackR(color0);
        expectedBytes0[1] = GrColorUnpackG(color0);
        expectedBytes0[2] = GrColorUnpackB(color0);
        expectedBytes0[3] = GrColorUnpackA(color0);
        for (int i = 0; i < rtc->width() * rtc->height(); ++i) {
            expectedPixels.get()[i] = expectedColor0;
        }

        // Clear the the top to a different color.
        GrColor color1 = GrColorPackRGBA(0x1, 0x2, 0x3, 0x4);
        SkIRect rect = SkIRect::MakeWH(w, h/2);
        rtc->clear(&rect, color1, GrRenderTargetContext::CanClearFullscreen::kNo);

        uint32_t expectedColor1 = 0;
        uint8_t* expectedBytes1 = SkTCast<uint8_t*>(&expectedColor1);
        expectedBytes1[0] = GrColorUnpackR(color1);
        expectedBytes1[1] = GrColorUnpackG(color1);
        expectedBytes1[2] = GrColorUnpackB(color1);
        expectedBytes1[3] = GrColorUnpackA(color1);

        for (int y = 0; y < h/2; ++y) {
            for (int x = 0; x < w; ++x) {
                expectedPixels.get()[y * h + x] = expectedColor1;
            }
        }

        test_read_pixels(reporter, rtc, expectedPixels.get(), "RectangleTexture-clear");
    }
}
void GrExtractAlphaFragmentProcessor::onComputeInvariantOutput(GrInvariantOutput* inout) const {
    if (inout->validFlags() & kA_GrColorComponentFlag) {
        GrColor color = GrColorPackA4(GrColorUnpackA(inout->color()));
        inout->setToOther(kRGBA_GrColorComponentFlags, color,
                          GrInvariantOutput::kWill_ReadInput);
    } else {
        inout->setToUnknown(GrInvariantOutput::kWill_ReadInput);
    }
    this->childProcessor(0).computeInvariantOutput(inout);
}
/** We always use per-vertex colors so that rects can be batched across color changes. Sometimes we
    have explicit local coords and sometimes not. We *could* always provide explicit local coords
    and just duplicate the positions when the caller hasn't provided a local coord rect, but we
    haven't seen a use case which frequently switches between local rect and no local rect draws.

    The color param is used to determine whether the opaque hint can be set on the draw state.
    The caller must populate the vertex colors itself.

    The vertex attrib order is always pos, color, [local coords].
 */
static void set_vertex_attributes(GrDrawState* drawState, bool hasLocalCoords, GrColor color) {
    if (hasLocalCoords) {
        drawState->setVertexAttribs<kRectAttribs>(3, 2 * sizeof(SkPoint) + sizeof(SkColor));
    } else {
        drawState->setVertexAttribs<kRectAttribs>(2, sizeof(SkPoint) + sizeof(SkColor));
    }
    if (0xFF == GrColorUnpackA(color)) {
        drawState->setHint(GrDrawState::kVertexColorsAreOpaque_Hint, true);
    }
}
 GrColorComponentFlags componentsWithValue(unsigned value) const {
     GrColorComponentFlags flags = kNone_GrColorComponentFlags;
     if ((kR_GrColorComponentFlag & fFlags) && value == GrColorUnpackR(fColor)) {
         flags |= kR_GrColorComponentFlag;
     }
     if ((kG_GrColorComponentFlag & fFlags) && value == GrColorUnpackG(fColor)) {
         flags |= kG_GrColorComponentFlag;
     }
     if ((kB_GrColorComponentFlag & fFlags) && value == GrColorUnpackB(fColor)) {
         flags |= kB_GrColorComponentFlag;
     }
     if ((kA_GrColorComponentFlag & fFlags) && value == GrColorUnpackA(fColor)) {
         flags |= kA_GrColorComponentFlag;
     }
     return flags;
 }
GrXferProcessor* PDLCDXferProcessor::Create(SkBlendMode xfermode,
                                            const GrProcOptInfo& colorPOI) {
    if (SkBlendMode::kSrcOver != xfermode) {
        return nullptr;
    }

    if (kRGBA_GrColorComponentFlags != colorPOI.validFlags()) {
        return nullptr;
    }

    GrColor blendConstant = GrUnpremulColor(colorPOI.color());
    uint8_t alpha = GrColorUnpackA(blendConstant);
    blendConstant |= (0xff << GrColor_SHIFT_A);

    return new PDLCDXferProcessor(blendConstant, alpha);
}
GrXferProcessor* PDLCDXferProcessor::Create(SkXfermode::Mode xfermode,
                                            const GrProcOptInfo& colorPOI) {
    if (SkXfermode::kSrcOver_Mode != xfermode) {
        return NULL;
    }

    if (kRGBA_GrColorComponentFlags != colorPOI.validFlags()) {
        return NULL;
    }

    GrColor blendConstant = GrUnPreMulColor(colorPOI.color());
    uint8_t alpha = GrColorUnpackA(blendConstant);
    blendConstant |= (0xff << GrColor_SHIFT_A);

    return SkNEW_ARGS(PDLCDXferProcessor, (blendConstant, alpha));
}
 void onSetData(const GrGLSLProgramDataManager& pdm, const GrProcessor& processor) override {
     GrColor color = processor.cast<GrConstColorProcessor>().color();
     // We use the "illegal" color value as an uninit sentinel. However, ut isn't inherently
     // illegal to use this processor with unpremul colors. So we correctly handle the case
     // when the "illegal" color is used but we will always upload it.
     if (GrColor_ILLEGAL == color || fPrevColor != color) {
         static const float scale = 1.f / 255.f;
         float floatColor[4] = {
             GrColorUnpackR(color) * scale,
             GrColorUnpackG(color) * scale,
             GrColorUnpackB(color) * scale,
             GrColorUnpackA(color) * scale,
         };
         pdm.set4fv(fColorUniform, 1, floatColor);
         fPrevColor = color;
     }
 }
void GrConstColorProcessor::onComputeInvariantOutput(GrInvariantOutput* inout) const {
    if (kIgnore_InputMode == fMode) {
        inout->setToOther(kRGBA_GrColorComponentFlags, fColor,
                          GrInvariantOutput::kWillNot_ReadInput);
    } else {
        GrColor r = GrColorUnpackR(fColor);
        bool colorIsSingleChannel = r == GrColorUnpackG(fColor) && r == GrColorUnpackB(fColor) &&
                                    r == GrColorUnpackA(fColor);
        if (kModulateRGBA_InputMode == fMode) {
            if (colorIsSingleChannel) {
                inout->mulByKnownSingleComponent(r);
            } else {
                inout->mulByKnownFourComponents(fColor);
            }
        } else {
            if (colorIsSingleChannel) {
                inout->mulAlphaByKnownSingleComponent(r);
            } else {
                inout->mulAlphaByKnownFourComponents(fColor);
            }
        }
    }
}
GrXferProcessor::OptFlags
PorterDuffXferProcessor::getOptimizations(const GrProcOptInfo& colorPOI,
                                          const GrProcOptInfo& coveragePOI,
                                          bool doesStencilWrite,
                                          GrColor* overrideColor,
                                          const GrDrawTargetCaps& caps) {
    GrXferProcessor::OptFlags optFlags;
    // Optimizations when doing RGB Coverage
    if (coveragePOI.isFourChannelOutput()) {
        // We want to force our primary output to be alpha * Coverage, where alpha is the alpha
        // value of the blend the constant. We should already have valid blend coeff's if we are at
        // a point where we have RGB coverage. We don't need any color stages since the known color
        // output is already baked into the blendConstant.
        uint8_t alpha = GrColorUnpackA(fBlendConstant);
        *overrideColor = GrColorPackRGBA(alpha, alpha, alpha, alpha);
        optFlags = GrXferProcessor::kOverrideColor_OptFlag;
    } else {
        optFlags = this->internalGetOptimizations(colorPOI,
                                                  coveragePOI,
                                                  doesStencilWrite);
    }
    this->calcOutputTypes(optFlags, caps, coveragePOI.isSolidWhite());
    return optFlags;
}
 static MaskedColor ExtractInverseAlpha(const MaskedColor& in) {
     GrColorComponentFlags flags = (in.fFlags & kA_GrColorComponentFlag) ?
         kRGBA_GrColorComponentFlags : kNone_GrColorComponentFlags;
     return MaskedColor(GrColorPackA4(0xFF - GrColorUnpackA(in.fColor)), flags);
 }
bool GrGpuGLFixed::flushGraphicsState(GrPrimitiveType type) {

    bool usingTextures[kNumStages];

    for (int s = 0; s < kNumStages; ++s) {
        usingTextures[s] = VertexUsesStage(s, fGeometrySrc.fVertexLayout);

        if (usingTextures[s] && fCurrDrawState.fSamplerStates[s].isGradient()) {
            unimpl("Fixed pipe doesn't support radial/sweep gradients");
            return false;
        }
    }

    if (GR_GL_SUPPORT_ES1) {
        if (BlendCoefReferencesConstant(fCurrDrawState.fSrcBlend) ||
            BlendCoefReferencesConstant(fCurrDrawState.fDstBlend)) {
            unimpl("ES1 doesn't support blend constant");
            return false;
        }
    }

    if (!flushGLStateCommon(type)) {
        return false;
    }

    if (fDirtyFlags.fRenderTargetChanged) {
        flushProjectionMatrix();
    }

    for (int s = 0; s < kNumStages; ++s) {
        bool wasUsingTexture = VertexUsesStage(s, fHWGeometryState.fVertexLayout);
        if (usingTextures[s] != wasUsingTexture) {
            setTextureUnit(s);
            if (usingTextures[s]) {
                GR_GL(Enable(GR_GL_TEXTURE_2D));
            } else {
                GR_GL(Disable(GR_GL_TEXTURE_2D));
            }
        }
    }

    uint32_t vertColor = (fGeometrySrc.fVertexLayout & kColor_VertexLayoutBit);
    uint32_t prevVertColor = (fHWGeometryState.fVertexLayout &
                              kColor_VertexLayoutBit);

    if (vertColor != prevVertColor) {
        if (vertColor) {
            GR_GL(ShadeModel(GR_GL_SMOOTH));
            // invalidate the immediate mode color
            fHWDrawState.fColor = GrColor_ILLEGAL;
        } else {
            GR_GL(ShadeModel(GR_GL_FLAT));
        }
    }


    if (!vertColor && fHWDrawState.fColor != fCurrDrawState.fColor) {
        GR_GL(Color4ub(GrColorUnpackR(fCurrDrawState.fColor),
                       GrColorUnpackG(fCurrDrawState.fColor),
                       GrColorUnpackB(fCurrDrawState.fColor),
                       GrColorUnpackA(fCurrDrawState.fColor)));
        fHWDrawState.fColor = fCurrDrawState.fColor;
    }

    // set texture environment, decide whether we are modulating by RGB or A.
    for (int s = 0; s < kNumStages; ++s) {
        if (usingTextures[s]) {
            GrGLTexture* texture = (GrGLTexture*)fCurrDrawState.fTextures[s];
            if (NULL != texture) {
                TextureEnvRGBOperands nextRGBOperand0 =
                    (GrPixelConfigIsAlphaOnly(texture->config())) ?
                        kAlpha_TextureEnvRGBOperand :
                        kColor_TextureEnvRGBOperand;
                if (fHWRGBOperand0[s] != nextRGBOperand0) {
                    setTextureUnit(s);
                    GR_GL(TexEnvi(GR_GL_TEXTURE_ENV,
                                  GR_GL_OPERAND0_RGB,
                                  (nextRGBOperand0==kAlpha_TextureEnvRGBOperand) ?
                                    GR_GL_SRC_ALPHA :
                                    GR_GL_SRC_COLOR));
                    fHWRGBOperand0[s] = nextRGBOperand0;
                }

                if (((1 << s) & fDirtyFlags.fTextureChangedMask) ||
                    (fHWDrawState.fSamplerStates[s].getMatrix() !=
                     getSamplerMatrix(s))) {

                    GrMatrix texMat = getSamplerMatrix(s);
                    AdjustTextureMatrix(texture,
                                        GrSamplerState::kNormal_SampleMode,
                                        &texMat);
                    GrGpuMatrix glm;
                    glm.set(texMat);
                    setTextureUnit(s);
                    GR_GL(MatrixMode(GR_GL_TEXTURE));
                    GR_GL(LoadMatrixf(glm.fMat));
                    recordHWSamplerMatrix(s, getSamplerMatrix(s));
                }
            } else {
                GrAssert(!"Rendering with texture vert flag set but no bound texture");
                return false;
            }
        }
    }

    if (fHWDrawState.fViewMatrix != fCurrDrawState.fViewMatrix) {
        GrGpuMatrix glm;
        glm.set(fCurrDrawState.fViewMatrix);
        GR_GL(MatrixMode(GR_GL_MODELVIEW));
        GR_GL(LoadMatrixf(glm.fMat));
        fHWDrawState.fViewMatrix =
        fCurrDrawState.fViewMatrix;
    }
    resetDirtyFlags();
    return true;
}
bool GrGpuGLShaders::flushGraphicsState(GrPrimitiveType type) {
    if (!flushGLStateCommon(type)) {
        return false;
    }

    if (fDirtyFlags.fRenderTargetChanged) {
        // our coords are in pixel space and the GL matrices map to NDC
        // so if the viewport changed, our matrix is now wrong.
#if ATTRIBUTE_MATRIX
        fHWDrawState.fViewMatrix = GrMatrix::InvalidMatrix();
#else
        // we assume all shader matrices may be wrong after viewport changes
        fProgramCache->invalidateViewMatrices();
#endif
    }

    if (fGeometrySrc.fVertexLayout & kColor_VertexLayoutBit) {
        // invalidate the immediate mode color
        fHWDrawState.fColor = GrColor_ILLEGAL;
    } else {
        if (fHWDrawState.fColor != fCurrDrawState.fColor) {
            // OpenGL ES only supports the float varities of glVertexAttrib
            float c[] = {
                GrColorUnpackR(fCurrDrawState.fColor) / 255.f,
                GrColorUnpackG(fCurrDrawState.fColor) / 255.f,
                GrColorUnpackB(fCurrDrawState.fColor) / 255.f,
                GrColorUnpackA(fCurrDrawState.fColor) / 255.f
            };
            GR_GL(VertexAttrib4fv(COL_ATTR_LOCATION, c));
            fHWDrawState.fColor = fCurrDrawState.fColor;
        }
    }

    buildProgram(type);
    fProgramData = fProgramCache->getProgramData(fCurrentProgram, this);

    if (fHWProgramID != fProgramData->fProgramID) {
        GR_GL(UseProgram(fProgramData->fProgramID));
        fHWProgramID = fProgramData->fProgramID;
    }

    if (!fCurrentProgram.doGLSetup(type, fProgramData)) {
        return false;
    }

#if ATTRIBUTE_MATRIX
    GrMatrix& currViewMatrix = fHWDrawState.fViewMatrix;
#else
    GrMatrix& currViewMatrix = fProgramData->fViewMatrix;
#endif

    if (currViewMatrix != fCurrDrawState.fViewMatrix) {
        flushViewMatrix();
        currViewMatrix = fCurrDrawState.fViewMatrix;
    }

    for (int s = 0; s < kNumStages; ++s) {
        GrGLTexture* texture = (GrGLTexture*) fCurrDrawState.fTextures[s];
        if (NULL != texture) {
            if (-1 != fProgramData->fUniLocations.fStages[s].fTextureMatrixUni &&
                (((1 << s) & fDirtyFlags.fTextureChangedMask) ||
                getHWSamplerMatrix(s) != getSamplerMatrix(s))) {
                flushTextureMatrix(s);
                recordHWSamplerMatrix(s, getSamplerMatrix(s));
            }
        }

        const GrSamplerState& sampler = fCurrDrawState.fSamplerStates[s];
        if (-1 != fProgramData->fUniLocations.fStages[s].fRadial2Uni &&
            (fProgramData->fRadial2CenterX1[s] != sampler.getRadial2CenterX1() ||
             fProgramData->fRadial2Radius0[s]  != sampler.getRadial2Radius0()  ||
             fProgramData->fRadial2PosRoot[s]  != sampler.isRadial2PosRoot())) {

            flushRadial2(s);

            fProgramData->fRadial2CenterX1[s] = sampler.getRadial2CenterX1();
            fProgramData->fRadial2Radius0[s]  = sampler.getRadial2Radius0();
            fProgramData->fRadial2PosRoot[s]  = sampler.isRadial2PosRoot();
        }
    }
    resetDirtyFlags();
    return true;
}
void GrDistanceFieldTextContext::flush() {
    if (NULL == fDrawTarget) {
        return;
    }

    if (fCurrVertex > 0) {
        GrDrawState drawState;
        drawState.setFromPaint(fPaint, fContext->getMatrix(), fContext->getRenderTarget());
        bool useColorVerts = !fUseLCDText;
        set_vertex_attributes(&drawState, useColorVerts);

        // setup our sampler state for our text texture/atlas
        SkASSERT(SkIsAlign4(fCurrVertex));

        // get our current color
        SkColor filteredColor;
        SkColorFilter* colorFilter = fSkPaint.getColorFilter();
        if (colorFilter) {
            filteredColor = colorFilter->filterColor(fSkPaint.getColor());
        } else {
            filteredColor = fSkPaint.getColor();
        }
        this->setupCoverageEffect(filteredColor);

        // Effects could be stored with one of the cache objects (atlas?)
        drawState.setGeometryProcessor(fCachedGeometryProcessor.get());

        // Set draw state
        if (fUseLCDText) {
            GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
            if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
                kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
                fPaint.numColorStages()) {
                SkDebugf("LCD Text will not draw correctly.\n");
            }
            SkASSERT(!drawState.hasColorVertexAttribute());
            // We don't use the GrPaint's color in this case because it's been premultiplied by
            // alpha. Instead we feed in a non-premultiplied color, and multiply its alpha by
            // the mask texture color. The end result is that we get
            //            mask*paintAlpha*paintColor + (1-mask*paintAlpha)*dstColor
            int a = SkColorGetA(fSkPaint.getColor());
            // paintAlpha
            drawState.setColor(SkColorSetARGB(a, a, a, a));
            // paintColor
            drawState.setBlendConstant(colorNoPreMul);
            drawState.setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
        } else {
            if (0xFF == GrColorUnpackA(fPaint.getColor())) {
                drawState.setHint(GrDrawState::kVertexColorsAreOpaque_Hint, true);
            }
            // set back to normal in case we took LCD path previously.
            drawState.setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
            // We're using per-vertex color.
            SkASSERT(drawState.hasColorVertexAttribute());
        }
        int nGlyphs = fCurrVertex / kVerticesPerGlyph;
        fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
        fDrawTarget->drawIndexedInstances(&drawState,
                                          kTriangles_GrPrimitiveType,
                                          nGlyphs,
                                          kVerticesPerGlyph,
                                          kIndicesPerGlyph,
                                          &fVertexBounds);
        fDrawTarget->resetVertexSource();
        fVertices = NULL;
        fTotalVertexCount -= fCurrVertex;
        fCurrVertex = 0;
        SkSafeSetNull(fCurrTexture);
        fVertexBounds.setLargestInverted();
    }
}