diff options
-rw-r--r-- | gm/constcolorprocessor.cpp | 197 | ||||
-rw-r--r-- | gyp/gmslides.gypi | 1 | ||||
-rw-r--r-- | gyp/gpu.gypi | 2 | ||||
-rw-r--r-- | include/gpu/GrInvariantOutput.h | 97 | ||||
-rw-r--r-- | include/gpu/SkGr.h | 5 | ||||
-rw-r--r-- | include/gpu/effects/GrConstColorProcessor.h | 64 | ||||
-rw-r--r-- | src/gpu/GrProcessor.cpp | 2 | ||||
-rw-r--r-- | src/gpu/effects/GrConstColorProcessor.cpp | 133 |
8 files changed, 498 insertions, 3 deletions
diff --git a/gm/constcolorprocessor.cpp b/gm/constcolorprocessor.cpp new file mode 100644 index 0000000000..672e07ec17 --- /dev/null +++ b/gm/constcolorprocessor.cpp @@ -0,0 +1,197 @@ + +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +// This test only works with the GPU backend. + +#include "gm.h" + +#if SK_SUPPORT_GPU + +#include "GrContext.h" +#include "GrTest.h" +#include "effects/GrConstColorProcessor.h" +#include "SkGr.h" +#include "SkGradientShader.h" + +namespace skiagm { +/** + * This GM directly exercises GrConstColorProcessor. + */ +class ConstColorProcessor : public GM { +public: + ConstColorProcessor() { + this->setBGColor(0xFFDDDDDD); + } + +protected: + SkString onShortName() override { + return SkString("const_color_processor"); + } + + SkISize onISize() override { + return SkISize::Make(kWidth, kHeight); + } + + void onOnceBeforeDraw() override { + SkColor colors[] = { 0xFFFF0000, 0x2000FF00, 0xFF0000FF}; + SkPoint pts[] = { SkPoint::Make(0, 0), SkPoint::Make(kRectSize, kRectSize) }; + fShader.reset(SkGradientShader::CreateLinear(pts, colors, NULL, SK_ARRAY_COUNT(colors), + SkShader::kClamp_TileMode)); + } + + void onDraw(SkCanvas* canvas) override { + GrRenderTarget* rt = canvas->internal_private_accessTopLayerRenderTarget(); + if (NULL == rt) { + return; + } + GrContext* context = rt->getContext(); + if (NULL == context) { + this->drawGpuOnlyMessage(canvas); + return; + } + + static const GrColor kColors[] = { + 0xFFFFFFFF, + 0xFFFF00FF, + 0x80000000, + 0x00000000, + }; + + static const SkColor kPaintColors[] = { + 0xFFFFFFFF, + 0xFFFF0000, + 0x80FF0000, + 0x00000000, + }; + + static const char* kModeStrs[] { + "kIgnore", + "kModulateRGBA", + "kModulateA", + }; + GR_STATIC_ASSERT(SK_ARRAY_COUNT(kModeStrs) == GrConstColorProcessor::kInputModeCnt); + + SkScalar y = kPad; + SkScalar x = kPad; + SkScalar maxW = 0; + for (size_t paintType = 0; paintType < SK_ARRAY_COUNT(kPaintColors) + 1; ++paintType) { + for (size_t procColor = 0; procColor < SK_ARRAY_COUNT(kColors); ++procColor) { + for (int m = 0; m < GrConstColorProcessor::kInputModeCnt; ++m) { + // translate by x,y for the canvas draws and the test target draws. + canvas->save(); + canvas->translate(x, y); + const SkMatrix viewMatrix = SkMatrix::MakeTrans(x, y); + + // rect to draw + SkRect renderRect = SkRect::MakeXYWH(0, 0, kRectSize, kRectSize); + + GrTestTarget tt; + context->getTestTarget(&tt); + if (NULL == tt.target()) { + SkDEBUGFAIL("Couldn't get Gr test target."); + return; + } + + GrPaint grPaint; + SkPaint skPaint; + if (paintType >= SK_ARRAY_COUNT(kPaintColors)) { + skPaint.setShader(fShader); + } else { + skPaint.setColor(kPaintColors[paintType]); + } + SkPaint2GrPaintShader(context, rt, skPaint, viewMatrix, false, &grPaint); + + GrConstColorProcessor::InputMode mode = (GrConstColorProcessor::InputMode) m; + GrColor color = kColors[procColor]; + SkAutoTUnref<GrFragmentProcessor> fp(GrConstColorProcessor::Create(color, mode)); + + GrPipelineBuilder pipelineBuilder; + GrClip clip; + pipelineBuilder.setFromPaint(grPaint, rt, clip); + pipelineBuilder.addColorProcessor(fp); + + tt.target()->drawSimpleRect(&pipelineBuilder, + grPaint.getColor(), + viewMatrix, + renderRect); + + // Draw labels for the input to the processor and the processor to the right of + // the test rect. The input label appears above the processor label. + SkPaint labelPaint; + labelPaint.setAntiAlias(true); + labelPaint.setTextSize(10.f); + SkString inputLabel; + inputLabel.set("Input: "); + if (paintType >= SK_ARRAY_COUNT(kPaintColors)) { + inputLabel.append("gradient"); + } else { + inputLabel.appendf("0x%08x", kPaintColors[paintType]); + } + SkString procLabel; + procLabel.printf("Proc: [0x%08x, %s]", kColors[procColor], kModeStrs[m]); + + SkRect inputLabelBounds; + // get the bounds of the text in order to position it + labelPaint.measureText(inputLabel.c_str(), inputLabel.size(), + &inputLabelBounds); + canvas->drawText(inputLabel.c_str(), inputLabel.size(), + renderRect.fRight + kPad, + -inputLabelBounds.fTop, labelPaint); + // update the bounds to reflect the offset we used to draw it. + inputLabelBounds.offset(renderRect.fRight + kPad, -inputLabelBounds.fTop); + + SkRect procLabelBounds; + labelPaint.measureText(procLabel.c_str(), procLabel.size(), + &procLabelBounds); + canvas->drawText(procLabel.c_str(), procLabel.size(), + renderRect.fRight + kPad, + inputLabelBounds.fBottom + 2.f - procLabelBounds.fTop, + labelPaint); + procLabelBounds.offset(renderRect.fRight + kPad, + inputLabelBounds.fBottom + 2.f - procLabelBounds.fTop); + + labelPaint.setStrokeWidth(0); + labelPaint.setStyle(SkPaint::kStroke_Style); + canvas->drawRect(renderRect, labelPaint); + + canvas->restore(); + + // update x and y for the next test case. + SkScalar height = renderRect.height(); + SkScalar width = SkTMax(inputLabelBounds.fRight, procLabelBounds.fRight); + maxW = SkTMax(maxW, width); + y += height + kPad; + if (y + height > kHeight) { + y = kPad; + x += maxW + kPad; + maxW = 0; + } + } + } + } + } + +private: + // Use this as a way of generating and input FP + SkAutoTUnref<SkShader> fShader; + + static const SkScalar kPad; + static const SkScalar kRectSize; + static const int kWidth = 820; + static const int kHeight = 500; + + typedef GM INHERITED; +}; + +const SkScalar ConstColorProcessor::kPad = 10.f; +const SkScalar ConstColorProcessor::kRectSize = 20.f; + +DEF_GM( return SkNEW(ConstColorProcessor); ) +} + +#endif diff --git a/gyp/gmslides.gypi b/gyp/gmslides.gypi index 8effe7fe75..c575194bee 100644 --- a/gyp/gmslides.gypi +++ b/gyp/gmslides.gypi @@ -65,6 +65,7 @@ '../gm/complexclip3.cpp', '../gm/composeshader.cpp', '../gm/conicpaths.cpp', + '../gm/constcolorprocessor.cpp', '../gm/convexpaths.cpp', '../gm/convexpolyclip.cpp', '../gm/convexpolyeffect.cpp', diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi index 8d2dcbd241..a2c227a1b0 100644 --- a/gyp/gpu.gypi +++ b/gyp/gpu.gypi @@ -38,6 +38,7 @@ '<(skia_include_path)/gpu/GrUserConfig.h', '<(skia_include_path)/gpu/GrXferProcessor.h', + '<(skia_include_path)/gpu/effects/GrConstColorProcessor.h', '<(skia_include_path)/gpu/effects/GrCoverageSetOpXP.h', '<(skia_include_path)/gpu/effects/GrCustomXfermode.h', '<(skia_include_path)/gpu/effects/GrPorterDuffXferProcessor.h', @@ -204,6 +205,7 @@ '<(skia_src_path)/gpu/effects/Gr1DKernelEffect.h', '<(skia_src_path)/gpu/effects/GrConfigConversionEffect.cpp', '<(skia_src_path)/gpu/effects/GrConfigConversionEffect.h', + '<(skia_src_path)/gpu/effects/GrConstColorProcessor.cpp', '<(skia_src_path)/gpu/effects/GrCoverageSetOpXP.cpp', '<(skia_src_path)/gpu/effects/GrCustomXfermode.cpp', '<(skia_src_path)/gpu/effects/GrCustomXfermodePriv.h', diff --git a/include/gpu/GrInvariantOutput.h b/include/gpu/GrInvariantOutput.h index 4977333de4..fd0a7f8eb5 100644 --- a/include/gpu/GrInvariantOutput.h +++ b/include/gpu/GrInvariantOutput.h @@ -79,6 +79,7 @@ public: }; void mulByUnknownOpaqueFourComponents() { + SkDEBUGCODE(this->validate()); if (this->isOpaque()) { fValidFlags = kA_GrColorComponentFlag; fIsSingleComponent = false; @@ -87,26 +88,32 @@ public: // multiplied is opaque. this->mulByUnknownFourComponents(); } + SkDEBUGCODE(this->validate()); } void mulByUnknownFourComponents() { + SkDEBUGCODE(this->validate()); if (this->hasZeroAlpha()) { this->internalSetToTransparentBlack(); } else { this->internalSetToUnknown(); } + SkDEBUGCODE(this->validate()); } void mulByUnknownSingleComponent() { + SkDEBUGCODE(this->validate()); if (this->hasZeroAlpha()) { this->internalSetToTransparentBlack(); } else { // We don't need to change fIsSingleComponent in this case fValidFlags = 0; } + SkDEBUGCODE(this->validate()); } void mulByKnownSingleComponent(uint8_t alpha) { + SkDEBUGCODE(this->validate()); if (this->hasZeroAlpha() || 0 == alpha) { this->internalSetToTransparentBlack(); } else { @@ -116,20 +123,89 @@ public: SkMulDiv255Round(GrColorUnpackG(fColor), alpha), SkMulDiv255Round(GrColorUnpackB(fColor), alpha), SkMulDiv255Round(GrColorUnpackA(fColor), alpha)); + // We don't need to change fIsSingleComponent in this case } } + SkDEBUGCODE(this->validate()); + } + + void mulByKnownFourComponents(GrColor color) { + SkDEBUGCODE(this->validate()); + uint32_t a; + if (GetAlphaAndCheckSingleChannel(color, &a)) { + this->mulByKnownSingleComponent(a); + } else { + if (color != 0xffffffff) { + fColor = GrColorPackRGBA( + SkMulDiv255Round(GrColorUnpackR(fColor), GrColorUnpackR(color)), + SkMulDiv255Round(GrColorUnpackG(fColor), GrColorUnpackG(color)), + SkMulDiv255Round(GrColorUnpackB(fColor), GrColorUnpackB(color)), + SkMulDiv255Round(GrColorUnpackA(fColor), a)); + if (kRGBA_GrColorComponentFlags == fValidFlags) { + fIsSingleComponent = GetAlphaAndCheckSingleChannel(fColor, &a); + } + } + } + SkDEBUGCODE(this->validate()); + } + + // Ignores the incoming color's RGB and muls its alpha by color. + void mulAlphaByKnownFourComponents(GrColor color) { + SkDEBUGCODE(this->validate()); + uint32_t a; + if (GetAlphaAndCheckSingleChannel(color, &a)) { + this->mulAlphaByKnownSingleComponent(a); + } else if (fValidFlags & kA_GrColorComponentFlag) { + GrColor preAlpha = GrColorUnpackA(fColor); + if (0 == preAlpha) { + this->internalSetToTransparentBlack(); + } else { + fColor = GrColorPackRGBA( + SkMulDiv255Round(preAlpha, GrColorUnpackR(color)), + SkMulDiv255Round(preAlpha, GrColorUnpackG(color)), + SkMulDiv255Round(preAlpha, GrColorUnpackB(color)), + SkMulDiv255Round(preAlpha, a)); + fValidFlags = kRGBA_GrColorComponentFlags; + } + } else { + fValidFlags = 0; + } + SkDEBUGCODE(this->validate()); + } + + // Ignores the incoming color's RGB and muls its alpha by the alpha param and sets all channels + // equal to that value. + void mulAlphaByKnownSingleComponent(uint8_t alpha) { + SkDEBUGCODE(this->validate()); + if (0 == alpha || this->hasZeroAlpha()) { + this->internalSetToTransparentBlack(); + } else { + if (fValidFlags & kA_GrColorComponentFlag) { + GrColor a = GrColorUnpackA(fColor); + a = SkMulDiv255Round(alpha, a); + fColor = GrColorPackRGBA(a, a, a, a); + fValidFlags = kRGBA_GrColorComponentFlags; + } else { + fValidFlags = 0; + } + fIsSingleComponent = true; + } + SkDEBUGCODE(this->validate()); } void invalidateComponents(uint8_t invalidateFlags, ReadInput readsInput) { + SkDEBUGCODE(this->validate()); fValidFlags &= ~invalidateFlags; fIsSingleComponent = false; fNonMulStageFound = true; if (kWillNot_ReadInput == readsInput) { fWillUseInputColor = false; } + SkDEBUGCODE(this->validate()); } void setToOther(uint8_t validFlags, GrColor color, ReadInput readsInput) { + SkDEBUGCODE(this->validate()); fValidFlags = validFlags; fColor = color; fIsSingleComponent = false; @@ -137,14 +213,28 @@ public: if (kWillNot_ReadInput == readsInput) { fWillUseInputColor = false; } + if (kRGBA_GrColorComponentFlags == fValidFlags) { + uint32_t a; + if (GetAlphaAndCheckSingleChannel(color, &a)) { + fIsSingleComponent = true; + } + } else if (kA_GrColorComponentFlag & fValidFlags) { + // Assuming fColor is premul means if a is 0 the color must be all 0s. + if (!GrColorUnpackA(fColor)) { + this->internalSetToTransparentBlack(); + } + } + SkDEBUGCODE(this->validate()); } void setToUnknown(ReadInput readsInput) { + SkDEBUGCODE(this->validate()); this->internalSetToUnknown(); fNonMulStageFound= true; if (kWillNot_ReadInput == readsInput) { fWillUseInputColor = false; } + SkDEBUGCODE(this->validate()); } // Temporary setter to handle LCD text correctly until we improve texture pixel config queries @@ -165,6 +255,13 @@ public: private: friend class GrProcOptInfo; + /** Extracts the alpha channel and returns true if r,g,b == a. */ + static bool GetAlphaAndCheckSingleChannel(GrColor color, uint32_t* alpha) { + *alpha = GrColorUnpackA(color); + return *alpha == GrColorUnpackR(color) && *alpha == GrColorUnpackG(color) && + *alpha == GrColorUnpackB(color); + } + void reset(GrColor color, GrColorComponentFlags flags, bool isSingleComponent) { fColor = color; fValidFlags = flags; diff --git a/include/gpu/SkGr.h b/include/gpu/SkGr.h index 7cb614dd4c..026b25685e 100644 --- a/include/gpu/SkGr.h +++ b/include/gpu/SkGr.h @@ -78,8 +78,9 @@ GrTexture* GrRefCachedBitmapTexture(GrContext*, const SkBitmap&, const GrTexture // Sets the color of GrPaint to the value of the parameter paintColor // Callers may subsequently modify the GrPaint. Setting constantColor indicates // that the final paint will draw the same color at every pixel. This allows -// an optimization where the the color filter can be applied to the SkPaint's -// color once while converting to GrPaint and then ignored. +// an optimization where the color filter can be applied to the SkPaint's +// color once while converting to GrPaint and then ignored. TODO: Remove this +// bool and use the invariant info to automatically apply the color filter. void SkPaint2GrPaintNoShader(GrContext* context, GrRenderTarget*, const SkPaint& skPaint, GrColor paintColor, bool constantColor, GrPaint* grPaint); diff --git a/include/gpu/effects/GrConstColorProcessor.h b/include/gpu/effects/GrConstColorProcessor.h new file mode 100644 index 0000000000..27ee0dfd08 --- /dev/null +++ b/include/gpu/effects/GrConstColorProcessor.h @@ -0,0 +1,64 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrColorProcessor_DEFINED +#define GrColorProcessor_DEFINED + +#include "GrFragmentProcessor.h" + +class GrInvariantOutput; + +/** + * This is a simple GrFragmentProcessor that outputs a constant color. It may do one of the + * following with its input color: ignore it, or multiply it by the constant color, multiply its + * alpha by the constant color and ignore the input color's r, g, and b. + */ +class GrConstColorProcessor : public GrFragmentProcessor { +public: + enum InputMode { + kIgnore_InputMode, + kModulateRGBA_InputMode, + kModulateA_InputMode, + + kLastInputMode = kModulateA_InputMode + }; + static const int kInputModeCnt = kLastInputMode + 1; + + static GrFragmentProcessor* Create(GrColor color, InputMode mode) { + return SkNEW_ARGS(GrConstColorProcessor, (color, mode)); + } + + ~GrConstColorProcessor() override {} + + const char* name() const override { return "Color"; } + + void getGLProcessorKey(const GrGLCaps&, GrProcessorKeyBuilder*) const override; + + GrGLFragmentProcessor* createGLInstance() const override; + + GrColor color() const { return fColor; } + + InputMode inputMode() const { return fMode; } + +private: + GrConstColorProcessor(GrColor color, InputMode mode) : fColor(color), fMode(mode) { + this->initClassID<GrConstColorProcessor>(); + } + + bool onIsEqual(const GrFragmentProcessor&) const override; + + void onComputeInvariantOutput(GrInvariantOutput* inout) const override; + + GR_DECLARE_FRAGMENT_PROCESSOR_TEST; + + GrColor fColor; + InputMode fMode; + + typedef GrFragmentProcessor INHERITED; +}; + +#endif diff --git a/src/gpu/GrProcessor.cpp b/src/gpu/GrProcessor.cpp index 4bda2e18b6..1457bed7ba 100644 --- a/src/gpu/GrProcessor.cpp +++ b/src/gpu/GrProcessor.cpp @@ -49,7 +49,7 @@ GrProcessorTestFactory<GrGeometryProcessor>::GetFactories() { * we verify the count is as expected. If a new factory is added, then these numbers must be * manually adjusted. */ -static const int kFPFactoryCount = 37; +static const int kFPFactoryCount = 38; static const int kGPFactoryCount = 14; static const int kXPFactoryCount = 5; diff --git a/src/gpu/effects/GrConstColorProcessor.cpp b/src/gpu/effects/GrConstColorProcessor.cpp new file mode 100644 index 0000000000..5f28694634 --- /dev/null +++ b/src/gpu/effects/GrConstColorProcessor.cpp @@ -0,0 +1,133 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "effects/GrConstColorProcessor.h" +#include "gl/GrGLProcessor.h" +#include "gl/GrGLSL.h" +#include "gl/builders/GrGLProgramBuilder.h" + +class GLConstColorProcessor : public GrGLFragmentProcessor { +public: + GLConstColorProcessor() : fPrevColor(GrColor_ILLEGAL) {} + + void emitCode(GrGLFPBuilder* builder, + const GrFragmentProcessor& fp, + const char* outputColor, + const char* inputColor, + const TransformedCoordsArray& coords, + const TextureSamplerArray& samplers) override { + GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder(); + const char* colorUni; + fColorUniform = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility, + kVec4f_GrSLType, kMedium_GrSLPrecision, "constantColor", + &colorUni); + switch (fp.cast<GrConstColorProcessor>().inputMode()) { + case GrConstColorProcessor::kIgnore_InputMode: + fsBuilder->codeAppendf("%s = %s;", outputColor, colorUni); + break; + case GrConstColorProcessor::kModulateRGBA_InputMode: + fsBuilder->codeAppendf("%s = %s * %s;", outputColor, inputColor, colorUni); + break; + case GrConstColorProcessor::kModulateA_InputMode: + fsBuilder->codeAppendf("%s = %s.a * %s;", outputColor, inputColor, colorUni); + break; + } + } + + void setData(const GrGLProgramDataManager& 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 GrGLfloat scale = 1.f / 255.f; + GrGLfloat floatColor[4] = { + GrColorUnpackR(color) * scale, + GrColorUnpackG(color) * scale, + GrColorUnpackB(color) * scale, + GrColorUnpackA(color) * scale, + }; + pdm.set4fv(fColorUniform, 1, floatColor); + fPrevColor = color; + } + } + +private: + GrGLProgramDataManager::UniformHandle fColorUniform; + GrColor fPrevColor; + + typedef GrGLFragmentProcessor INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +void GrConstColorProcessor::onComputeInvariantOutput(GrInvariantOutput* inout) const { + if (kIgnore_InputMode == fMode) { + inout->setToOther(kRGBA_GrColorComponentFlags, fColor, GrInvariantOutput::kWill_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); + } + } + } +} + +void GrConstColorProcessor::getGLProcessorKey(const GrGLCaps&, GrProcessorKeyBuilder* b) const { + b->add32(fMode); +} + +GrGLFragmentProcessor* GrConstColorProcessor::createGLInstance() const { + return SkNEW(GLConstColorProcessor); +} + +bool GrConstColorProcessor::onIsEqual(const GrFragmentProcessor& other) const { + const GrConstColorProcessor& that = other.cast<GrConstColorProcessor>(); + return fMode == that.fMode && fColor == that.fColor; +} + +/////////////////////////////////////////////////////////////////////////////// + +GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrConstColorProcessor); + +GrFragmentProcessor* GrConstColorProcessor::TestCreate(SkRandom* random, + GrContext*, + const GrDrawTargetCaps&, + GrTexture*[]) { + GrColor color; + int colorPicker = random->nextULessThan(3); + switch (colorPicker) { + case 0: { + uint32_t a = random->nextULessThan(0x100); + uint32_t r = random->nextULessThan(a+1); + uint32_t g = random->nextULessThan(a+1); + uint32_t b = random->nextULessThan(a+1); + color = GrColorPackRGBA(r, g, b, a); + break; + } + case 1: + color = 0; + break; + case 2: + color = random->nextULessThan(0x100); + color = color | (color << 8) | (color << 16) | (color << 24); + break; + } + InputMode mode = static_cast<InputMode>(random->nextULessThan(kInputModeCnt)); + return GrConstColorProcessor::Create(color, mode); +} |