1 /* 2 * Copyright 2015 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 // This test only works with the GPU backend. 9 10 #include "gm/gm.h" 11 #include "include/core/SkCanvas.h" 12 #include "include/core/SkColor.h" 13 #include "include/core/SkFont.h" 14 #include "include/core/SkFontTypes.h" 15 #include "include/core/SkMatrix.h" 16 #include "include/core/SkPaint.h" 17 #include "include/core/SkPoint.h" 18 #include "include/core/SkRect.h" 19 #include "include/core/SkRefCnt.h" 20 #include "include/core/SkScalar.h" 21 #include "include/core/SkShader.h" 22 #include "include/core/SkSize.h" 23 #include "include/core/SkString.h" 24 #include "include/core/SkTileMode.h" 25 #include "include/core/SkTypeface.h" 26 #include "include/core/SkTypes.h" 27 #include "include/effects/SkGradientShader.h" 28 #include "include/private/SkColorData.h" 29 #include "include/private/gpu/ganesh/GrTypesPriv.h" 30 #include "src/core/SkCanvasPriv.h" 31 #include "src/gpu/ganesh/GrCanvas.h" 32 #include "src/gpu/ganesh/GrColor.h" 33 #include "src/gpu/ganesh/GrFPArgs.h" 34 #include "src/gpu/ganesh/GrFragmentProcessor.h" 35 #include "src/gpu/ganesh/GrFragmentProcessors.h" 36 #include "src/gpu/ganesh/GrPaint.h" 37 #include "src/gpu/ganesh/SkGr.h" 38 #include "src/gpu/ganesh/SurfaceDrawContext.h" 39 #include "src/gpu/ganesh/ops/GrOp.h" 40 #include "tools/ToolUtils.h" 41 #include "tools/fonts/FontToolUtils.h" 42 #include "tools/gpu/TestOps.h" 43 44 #include <utility> 45 46 namespace skiagm { 47 /** 48 * This GM directly exercises Color and ModulateRGBA. 49 */ 50 class ColorProcessor : public GpuGM { 51 public: 52 enum class TestMode { 53 kConstColor, 54 kModulateRGBA 55 }; 56 ColorProcessor(TestMode mode)57 ColorProcessor(TestMode mode) : fMode(mode) { 58 this->setBGColor(0xFFDDDDDD); 59 } 60 61 protected: getName() const62 SkString getName() const override { 63 switch (fMode) { 64 case TestMode::kConstColor: return SkString("const_color_processor"); 65 case TestMode::kModulateRGBA: return SkString("modulate_rgba"); 66 } 67 SkUNREACHABLE; 68 } 69 getISize()70 SkISize getISize() override { return SkISize::Make(kWidth, kHeight); } 71 onOnceBeforeDraw()72 void onOnceBeforeDraw() override { 73 SkColor colors[] = { 0xFFFF0000, 0x2000FF00, 0xFF0000FF}; 74 SkPoint pts[] = { SkPoint::Make(0, 0), SkPoint::Make(kRectSize, kRectSize) }; 75 fShader = SkGradientShader::MakeLinear(pts, colors, nullptr, std::size(colors), 76 SkTileMode::kClamp); 77 } 78 onDraw(GrRecordingContext * rContext,SkCanvas * canvas,SkString * errorMsg)79 DrawResult onDraw(GrRecordingContext* rContext, SkCanvas* canvas, SkString* errorMsg) override { 80 auto sdc = skgpu::ganesh::TopDeviceSurfaceDrawContext(canvas); 81 if (!sdc) { 82 *errorMsg = kErrorMsg_DrawSkippedGpuOnly; 83 return DrawResult::kSkip; 84 } 85 86 constexpr GrColor kColors[] = { 87 0xFFFFFFFF, 88 0xFFFF00FF, 89 0x80000000, 90 0x00000000, 91 }; 92 93 constexpr GrColor kPaintColors[] = { 94 0xFFFFFFFF, 95 0xFF0000FF, 96 0x80000080, 97 0x00000000, 98 }; 99 100 SkScalar y = kPad; 101 SkScalar x = kPad; 102 SkScalar maxW = 0; 103 for (size_t paintType = 0; paintType < std::size(kPaintColors) + 1; ++paintType) { 104 for (size_t procColor = 0; procColor < std::size(kColors); ++procColor) { 105 // translate by x,y for the canvas draws and the test target draws. 106 canvas->save(); 107 canvas->translate(x, y); 108 109 // rect to draw 110 SkRect renderRect = SkRect::MakeXYWH(0, 0, kRectSize, kRectSize); 111 112 // Create a base-layer FP for the const color processor to draw on top of. 113 std::unique_ptr<GrFragmentProcessor> baseFP; 114 if (paintType >= std::size(kPaintColors)) { 115 GrColorInfo colorInfo; 116 SkSurfaceProps props; 117 GrFPArgs args(rContext, &colorInfo, props, GrFPArgs::Scope::kDefault); 118 baseFP = GrFragmentProcessors::Make(fShader.get(), args, SkMatrix::I()); 119 } else { 120 baseFP = GrFragmentProcessor::MakeColor( 121 SkPMColor4f::FromBytes_RGBA(kPaintColors[paintType])); 122 } 123 124 // Layer a color/modulation FP on top of the base layer, using various colors. 125 std::unique_ptr<GrFragmentProcessor> colorFP; 126 switch (fMode) { 127 case TestMode::kConstColor: 128 colorFP = GrFragmentProcessor::MakeColor( 129 SkPMColor4f::FromBytes_RGBA(kColors[procColor])); 130 break; 131 132 case TestMode::kModulateRGBA: 133 colorFP = GrFragmentProcessor::ModulateRGBA( 134 std::move(baseFP), SkPMColor4f::FromBytes_RGBA(kColors[procColor])); 135 break; 136 } 137 138 // Render the FP tree. 139 if (auto op = sk_gpu_test::test_ops::MakeRect(rContext, 140 std::move(colorFP), 141 renderRect.makeOffset(x, y), 142 renderRect, 143 SkMatrix::I())) { 144 sdc->addDrawOp(std::move(op)); 145 } 146 147 // Draw labels for the input to the processor and the processor to the right of 148 // the test rect. The input label appears above the processor label. 149 SkFont labelFont; 150 labelFont.setTypeface(ToolUtils::DefaultPortableTypeface()); 151 labelFont.setEdging(SkFont::Edging::kAntiAlias); 152 labelFont.setSize(10.f); 153 SkPaint labelPaint; 154 labelPaint.setAntiAlias(true); 155 SkString inputLabel("Input: "); 156 if (paintType >= std::size(kPaintColors)) { 157 inputLabel.append("gradient"); 158 } else { 159 inputLabel.appendf("0x%08x", kPaintColors[paintType]); 160 } 161 SkString procLabel; 162 procLabel.printf("Proc: [0x%08x]", kColors[procColor]); 163 164 SkRect inputLabelBounds; 165 // get the bounds of the text in order to position it 166 labelFont.measureText(inputLabel.c_str(), inputLabel.size(), 167 SkTextEncoding::kUTF8, &inputLabelBounds); 168 canvas->drawString(inputLabel, renderRect.fRight + kPad, -inputLabelBounds.fTop, 169 labelFont, labelPaint); 170 // update the bounds to reflect the offset we used to draw it. 171 inputLabelBounds.offset(renderRect.fRight + kPad, -inputLabelBounds.fTop); 172 173 SkRect procLabelBounds; 174 labelFont.measureText(procLabel.c_str(), procLabel.size(), 175 SkTextEncoding::kUTF8, &procLabelBounds); 176 canvas->drawString(procLabel, renderRect.fRight + kPad, 177 inputLabelBounds.fBottom + 2.f - procLabelBounds.fTop, 178 labelFont, labelPaint); 179 procLabelBounds.offset(renderRect.fRight + kPad, 180 inputLabelBounds.fBottom + 2.f - procLabelBounds.fTop); 181 182 labelPaint.setStrokeWidth(0); 183 labelPaint.setStyle(SkPaint::kStroke_Style); 184 canvas->drawRect(renderRect, labelPaint); 185 186 canvas->restore(); 187 188 // update x and y for the next test case. 189 SkScalar height = renderRect.height(); 190 SkScalar width = std::max(inputLabelBounds.fRight, procLabelBounds.fRight); 191 maxW = std::max(maxW, width); 192 y += height + kPad; 193 if (y + height > kHeight) { 194 y = kPad; 195 x += maxW + kPad; 196 maxW = 0; 197 } 198 } 199 } 200 201 return DrawResult::kOk; 202 } 203 204 private: 205 // Use this as a way of generating an input FP 206 sk_sp<SkShader> fShader; 207 TestMode fMode; 208 209 inline static constexpr SkScalar kPad = 10.f; 210 inline static constexpr SkScalar kRectSize = 20.f; 211 inline static constexpr int kWidth = 820; 212 inline static constexpr int kHeight = 500; 213 214 using INHERITED = GM; 215 }; 216 217 DEF_GM(return new ColorProcessor{ColorProcessor::TestMode::kConstColor};) 218 DEF_GM(return new ColorProcessor{ColorProcessor::TestMode::kModulateRGBA};) 219 220 } // namespace skiagm 221