| /* | 
 |  * Copyright 2014 Google Inc. | 
 |  * | 
 |  * Use of this source code is governed by a BSD-style license that can be | 
 |  * found in the LICENSE file. | 
 |  */ | 
 |  | 
 | #include "gm.h" | 
 | #if SK_SUPPORT_GPU | 
 | #include "GrFragmentProcessor.h" | 
 | #include "GrCoordTransform.h" | 
 | #include "GrInvariantOutput.h" | 
 | #include "effects/GrXfermodeFragmentProcessor.h" | 
 | #include "glsl/GrGLSLFragmentProcessor.h" | 
 | #include "glsl/GrGLSLFragmentShaderBuilder.h" | 
 | #include "glsl/GrGLSLProgramBuilder.h" | 
 | #include "glsl/GrGLSLProgramDataManager.h" | 
 | #include "Resources.h" | 
 | #include "SkReadBuffer.h" | 
 | #include "SkShader.h" | 
 | #include "SkStream.h" | 
 | #include "SkTypeface.h" | 
 | #include "SkWriteBuffer.h" | 
 |  | 
 | namespace skiagm { | 
 |  | 
 | /////////////////////////////////////////////////////////////////////////////// | 
 |  | 
 | class DCShader : public SkShader { | 
 | public: | 
 |     DCShader(const SkMatrix& matrix) : fDeviceMatrix(matrix) {} | 
 |  | 
 |     SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(DCShader); | 
 |  | 
 |     void flatten(SkWriteBuffer& buf) const override { | 
 |         buf.writeMatrix(fDeviceMatrix); | 
 |     } | 
 |  | 
 |     sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const override; | 
 |  | 
 | #ifndef SK_IGNORE_TO_STRING | 
 |     void toString(SkString* str) const override { | 
 |         str->appendf("DCShader: ()"); | 
 |     } | 
 | #endif | 
 |  | 
 | private: | 
 |     const SkMatrix fDeviceMatrix; | 
 | }; | 
 |  | 
 | sk_sp<SkFlattenable> DCShader::CreateProc(SkReadBuffer& buf) { | 
 |     SkMatrix matrix; | 
 |     buf.readMatrix(&matrix); | 
 |     return sk_make_sp<DCShader>(matrix); | 
 | } | 
 |  | 
 | class DCFP : public GrFragmentProcessor { | 
 | public: | 
 |     DCFP(const SkMatrix& m) : fDeviceTransform(kDevice_GrCoordSet, m) { | 
 |         this->addCoordTransform(&fDeviceTransform); | 
 |         this->initClassID<DCFP>(); | 
 |     } | 
 |  | 
 |     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { | 
 |         class DCGLFP : public GrGLSLFragmentProcessor { | 
 |             void emitCode(EmitArgs& args) override { | 
 |                 GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; | 
 |                 fragBuilder->codeAppendf("vec2 c = %s;", | 
 |                                          fragBuilder->ensureFSCoords2D(args.fCoords, 0).c_str()); | 
 |                 fragBuilder->codeAppend("vec2 r = mod(c, vec2(20.0));"); | 
 |                 fragBuilder->codeAppend("vec4 color = vec4(0.5*sin(c.x / 15.0) + 0.5," | 
 |                                                       "0.5*cos((c.x + c.y) / 15.0) + 0.5," | 
 |                                                       "(r.x + r.y) / 20.0," | 
 |                                                       "distance(r, vec2(15.0)) / 20.0 + 0.2);"); | 
 |                 fragBuilder->codeAppendf("color.rgb *= color.a;" | 
 |                                          "%s = color * %s;", | 
 |                                          args.fOutputColor, GrGLSLExpr4(args.fInputColor).c_str()); | 
 |             } | 
 |             void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override {} | 
 |         }; | 
 |         return new DCGLFP; | 
 |     } | 
 |  | 
 |     const char* name() const override { return "DCFP"; } | 
 |  | 
 |     void onComputeInvariantOutput(GrInvariantOutput* inout) const override { | 
 |         inout->mulByUnknownFourComponents(); | 
 |     } | 
 |  | 
 | private: | 
 |     void onGetGLSLProcessorKey(const GrGLSLCaps& caps, | 
 |                                GrProcessorKeyBuilder* b) const override {} | 
 |  | 
 |     bool onIsEqual(const GrFragmentProcessor&) const override { return true; } | 
 |  | 
 |     GrCoordTransform fDeviceTransform; | 
 | }; | 
 |  | 
 | sk_sp<GrFragmentProcessor> DCShader::asFragmentProcessor(const AsFPArgs&) const { | 
 |     sk_sp<GrFragmentProcessor> inner(new DCFP(fDeviceMatrix)); | 
 |     return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner)); | 
 | } | 
 |  | 
 | class DCShaderGM : public GM { | 
 | public: | 
 |     DCShaderGM() { | 
 |         this->setBGColor(sk_tool_utils::color_to_565(0xFFAABBCC)); | 
 |     } | 
 |  | 
 |     ~DCShaderGM() override { | 
 |         for (int i = 0; i < fPrims.count(); ++i) { | 
 |             delete fPrims[i]; | 
 |         } | 
 |     } | 
 |  | 
 | protected: | 
 |  | 
 |     SkString onShortName() override { | 
 |         return SkString("dcshader"); | 
 |     } | 
 |  | 
 |     SkISize onISize() override { return SkISize::Make(1000, 900); } | 
 |  | 
 |     void onOnceBeforeDraw() override { | 
 |         struct Rect : public Prim { | 
 |             SkRect draw(SkCanvas* canvas, const SkPaint& paint) override { | 
 |                 SkRect rect = SkRect::MakeXYWH(0, 0, 50, 50); | 
 |                 canvas->drawRect(rect, paint); | 
 |                 return rect; | 
 |             } | 
 |         }; | 
 |  | 
 |         struct Circle : public Prim { | 
 |             SkRect draw(SkCanvas* canvas, const SkPaint& paint) override { | 
 |                 static const SkScalar radius = 25; | 
 |                 canvas->drawCircle(radius, radius, radius, paint); | 
 |                 return SkRect::MakeXYWH(0, 0, 2 * radius, 2 * radius); | 
 |             } | 
 |         }; | 
 |  | 
 |         struct RRect : public Prim { | 
 |             SkRect draw(SkCanvas* canvas, const SkPaint& paint) override { | 
 |                 SkRRect rrect; | 
 |                 rrect.setRectXY(SkRect::MakeXYWH(0, 0, 50, 50), 10, 10); | 
 |                 canvas->drawRRect(rrect, paint); | 
 |                 return rrect.getBounds(); | 
 |             } | 
 |         }; | 
 |  | 
 |         struct DRRect : public Prim { | 
 |             SkRect draw(SkCanvas* canvas, const SkPaint& paint) override { | 
 |                 SkRRect outerRRect; | 
 |                 outerRRect.setRectXY(SkRect::MakeXYWH(0, 0, 50, 50), 5, 5); | 
 |                 SkRRect innerRRect; | 
 |                 innerRRect.setRectXY(SkRect::MakeXYWH(5, 8, 35, 30), 8, 3); | 
 |                 canvas->drawDRRect(outerRRect, innerRRect, paint); | 
 |                 return outerRRect.getBounds(); | 
 |             } | 
 |         }; | 
 |         struct Path : public Prim { | 
 |             SkRect draw(SkCanvas* canvas, const SkPaint& paint) override { | 
 |                 SkPath path; | 
 |                 path.addCircle(15, 15, 10); | 
 |                 path.addOval(SkRect::MakeXYWH(2, 2, 22, 37)); | 
 |                 path.setFillType(SkPath::kEvenOdd_FillType); | 
 |                 canvas->drawPath(path, paint); | 
 |                 return path.getBounds(); | 
 |             } | 
 |         }; | 
 |  | 
 |         struct Points : public Prim { | 
 |             Points(SkCanvas::PointMode mode) : fMode(mode) {} | 
 |  | 
 |             SkRect draw(SkCanvas* canvas, const SkPaint& paint) override { | 
 |                 SkRandom random; | 
 |                 SkPoint points[500]; | 
 |                 SkRect bounds = SkRect::MakeWH(50, 50); | 
 |                 int count = SkToInt(SK_ARRAY_COUNT(points)); | 
 |                 if (SkCanvas::kPoints_PointMode != fMode) { | 
 |                     count = SkTMin(count, 10); | 
 |                 } | 
 |                 for (int p = 0; p < count; ++p) { | 
 |                     points[p].fX = random.nextUScalar1() * bounds.width(); | 
 |                     points[p].fY = random.nextUScalar1() * bounds.width(); | 
 |                 } | 
 |                 canvas->drawPoints(fMode, count, points, paint); | 
 |                 return bounds; | 
 |             } | 
 |             SkCanvas::PointMode fMode; | 
 |         }; | 
 |  | 
 |         struct Text : public Prim { | 
 |             SkRect draw(SkCanvas* canvas, const SkPaint& origPaint) override { | 
 |                 SkPaint paint = origPaint; | 
 |                 paint.setTextSize(30.f); | 
 |                 this->setFont(&paint); | 
 |                 const char* text = this->text(); | 
 |                 static const SkVector offset = SkVector::Make(10, 10); | 
 |                 canvas->drawText(text, strlen(text), offset.fX, offset.fY, paint); | 
 |                 SkRect bounds; | 
 |                 paint.measureText(text, strlen(text), &bounds); | 
 |                 bounds.offset(offset); | 
 |                 return bounds; | 
 |             } | 
 |  | 
 |             virtual void setFont(SkPaint* paint) { | 
 |                 sk_tool_utils::set_portable_typeface(paint); | 
 |             } | 
 |  | 
 |             virtual const char* text() const { return "Hello, Skia!"; } | 
 |         }; | 
 |  | 
 |         fPrims.push_back(new Rect); | 
 |         fPrims.push_back(new Circle); | 
 |         fPrims.push_back(new RRect); | 
 |         fPrims.push_back(new DRRect); | 
 |         fPrims.push_back(new Path); | 
 |         fPrims.push_back(new Points(SkCanvas::kPoints_PointMode)); | 
 |         fPrims.push_back(new Points(SkCanvas::kLines_PointMode)); | 
 |         fPrims.push_back(new Points(SkCanvas::kPolygon_PointMode)); | 
 |         fPrims.push_back(new Text); | 
 |     } | 
 |  | 
 |     void onDraw(SkCanvas* canvas) override { | 
 |         // This GM exists to test a specific feature of the GPU backend. It does not work with the | 
 |         // sw rasterizer, tile modes, etc. | 
 |         if (nullptr == canvas->getGrContext()) { | 
 |             skiagm::GM::DrawGpuOnlyMessage(canvas); | 
 |             return; | 
 |         } | 
 |         SkPaint paint; | 
 |         SkTArray<SkMatrix> devMats; | 
 |         devMats.push_back().reset(); | 
 |         devMats.push_back().setRotate(45, 500, 500); | 
 |         devMats.push_back().setRotate(-30, 200, 200); | 
 |         devMats.back().setPerspX(-SK_Scalar1 / 2000); | 
 |         devMats.back().setPerspY(SK_Scalar1 / 1000); | 
 |  | 
 |  | 
 |         SkTArray<SkMatrix> viewMats; | 
 |         viewMats.push_back().setScale(0.75f, 0.75f); | 
 |         viewMats.push_back().setRotate(45, 50, 50); | 
 |         viewMats.back().postScale(0.5f, 1.1f); | 
 |  | 
 |         canvas->translate(10, 20); | 
 |         canvas->save(); | 
 |         SkScalar tx = 0, maxTy = 0; | 
 |         static const SkScalar kW = 900; | 
 |  | 
 |         for (int aa = 0; aa < 2; ++aa) { | 
 |             for (int i = 0; i < fPrims.count(); ++i) { | 
 |                 for (int j = 0; j < devMats.count(); ++j) { | 
 |                     for (int k = 0; k < viewMats.count(); ++k) { | 
 |                         paint.setShader(sk_make_sp<DCShader>(devMats[j])); | 
 |                         paint.setAntiAlias(SkToBool(aa)); | 
 |                         canvas->save(); | 
 |                         canvas->concat(viewMats[k]); | 
 |                         SkRect bounds = fPrims[i]->draw(canvas, paint); | 
 |                         canvas->restore(); | 
 |                         viewMats[k].mapRect(&bounds); | 
 |                         // add margins | 
 |                         bounds.fRight += 20; | 
 |                         bounds.fBottom += 20; | 
 |                         canvas->translate(bounds.fRight, 0); | 
 |                         tx += bounds.fRight; | 
 |                         maxTy = SkTMax(bounds.fBottom, maxTy); | 
 |                         if (tx > kW) { | 
 |                             tx = 0; | 
 |                             canvas->restore(); | 
 |                             canvas->translate(0, maxTy); | 
 |                             canvas->save(); | 
 |                             maxTy = 0; | 
 |                         } | 
 |                     } | 
 |                 } | 
 |             } | 
 |         } | 
 |         canvas->restore(); | 
 |     } | 
 |  | 
 | private: | 
 |     struct Prim { | 
 |         virtual ~Prim() {} | 
 |         virtual SkRect draw(SkCanvas*, const SkPaint&) = 0; | 
 |     }; | 
 |  | 
 |     SkTArray<Prim*> fPrims; | 
 |  | 
 |     typedef GM INHERITED; | 
 | }; | 
 |  | 
 | DEF_GM(return new DCShaderGM;) | 
 | } | 
 | #endif |