1*c8dee2aaSAndroid Build Coastguard Worker /* 2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2019 Google Inc. 3*c8dee2aaSAndroid Build Coastguard Worker * 4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be 5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file. 6*c8dee2aaSAndroid Build Coastguard Worker */ 7*c8dee2aaSAndroid Build Coastguard Worker 8*c8dee2aaSAndroid Build Coastguard Worker #include "gm/gm.h" 9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkCanvas.h" 10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColor.h" 11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkFont.h" 12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkFontTypes.h" 13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImage.h" 14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkM44.h" 15*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkMatrix.h" 16*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPaint.h" 17*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRRect.h" 18*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRect.h" 19*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRefCnt.h" 20*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkScalar.h" 21*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSize.h" 22*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkString.h" 23*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSurface.h" 24*c8dee2aaSAndroid Build Coastguard Worker #include "tools/fonts/FontToolUtils.h" 25*c8dee2aaSAndroid Build Coastguard Worker #include "tools/timer/TimeUtils.h" 26*c8dee2aaSAndroid Build Coastguard Worker 27*c8dee2aaSAndroid Build Coastguard Worker // Mimics https://output.jsbin.com/falefice/1/quiet?CC_POSTER_CIRCLE, which can't be captured as 28*c8dee2aaSAndroid Build Coastguard Worker // an SKP due to many 3D layers being composited post-SKP capture. 29*c8dee2aaSAndroid Build Coastguard Worker // See skbug.com/9028 30*c8dee2aaSAndroid Build Coastguard Worker class PosterCircleGM : public skiagm::GM { 31*c8dee2aaSAndroid Build Coastguard Worker public: PosterCircleGM()32*c8dee2aaSAndroid Build Coastguard Worker PosterCircleGM() : fTime(0.f) {} 33*c8dee2aaSAndroid Build Coastguard Worker 34*c8dee2aaSAndroid Build Coastguard Worker protected: getName() const35*c8dee2aaSAndroid Build Coastguard Worker SkString getName() const override { return SkString("poster_circle"); } 36*c8dee2aaSAndroid Build Coastguard Worker getISize()37*c8dee2aaSAndroid Build Coastguard Worker SkISize getISize() override { return SkISize::Make(kStageWidth, kStageHeight + 50); } 38*c8dee2aaSAndroid Build Coastguard Worker onAnimate(double nanos)39*c8dee2aaSAndroid Build Coastguard Worker bool onAnimate(double nanos) override { 40*c8dee2aaSAndroid Build Coastguard Worker fTime = TimeUtils::Scaled(1e-9 * nanos, 0.5f); 41*c8dee2aaSAndroid Build Coastguard Worker return true; 42*c8dee2aaSAndroid Build Coastguard Worker } 43*c8dee2aaSAndroid Build Coastguard Worker onOnceBeforeDraw()44*c8dee2aaSAndroid Build Coastguard Worker void onOnceBeforeDraw() override { 45*c8dee2aaSAndroid Build Coastguard Worker SkFont font = ToolUtils::DefaultPortableFont(); 46*c8dee2aaSAndroid Build Coastguard Worker font.setEdging(SkFont::Edging::kAntiAlias); 47*c8dee2aaSAndroid Build Coastguard Worker font.setEmbolden(true); 48*c8dee2aaSAndroid Build Coastguard Worker font.setSize(24.f); 49*c8dee2aaSAndroid Build Coastguard Worker 50*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkSurface> surface = 51*c8dee2aaSAndroid Build Coastguard Worker SkSurfaces::Raster(SkImageInfo::MakeN32Premul(kPosterSize, kPosterSize)); 52*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < kNumAngles; ++i) { 53*c8dee2aaSAndroid Build Coastguard Worker SkCanvas* canvas = surface->getCanvas(); 54*c8dee2aaSAndroid Build Coastguard Worker 55*c8dee2aaSAndroid Build Coastguard Worker SkPaint fillPaint; 56*c8dee2aaSAndroid Build Coastguard Worker fillPaint.setAntiAlias(true); 57*c8dee2aaSAndroid Build Coastguard Worker fillPaint.setColor(i % 2 == 0 ? SkColorSetRGB(0x99, 0x5C, 0x7F) 58*c8dee2aaSAndroid Build Coastguard Worker : SkColorSetRGB(0x83, 0x5A, 0x99)); 59*c8dee2aaSAndroid Build Coastguard Worker canvas->drawRRect(SkRRect::MakeRectXY(SkRect::MakeWH(kPosterSize, kPosterSize), 60*c8dee2aaSAndroid Build Coastguard Worker 10.f, 10.f), fillPaint); 61*c8dee2aaSAndroid Build Coastguard Worker 62*c8dee2aaSAndroid Build Coastguard Worker SkString label; 63*c8dee2aaSAndroid Build Coastguard Worker label.printf("%d", i); 64*c8dee2aaSAndroid Build Coastguard Worker SkRect labelBounds; 65*c8dee2aaSAndroid Build Coastguard Worker font.measureText(label.c_str(), label.size(), SkTextEncoding::kUTF8, &labelBounds); 66*c8dee2aaSAndroid Build Coastguard Worker SkScalar labelX = 0.5f * kPosterSize - 0.5f * labelBounds.width(); 67*c8dee2aaSAndroid Build Coastguard Worker SkScalar labelY = 0.5f * kPosterSize + 0.5f * labelBounds.height(); 68*c8dee2aaSAndroid Build Coastguard Worker 69*c8dee2aaSAndroid Build Coastguard Worker 70*c8dee2aaSAndroid Build Coastguard Worker SkPaint labelPaint; 71*c8dee2aaSAndroid Build Coastguard Worker labelPaint.setAntiAlias(true); 72*c8dee2aaSAndroid Build Coastguard Worker canvas->drawString(label, labelX, labelY, font, labelPaint); 73*c8dee2aaSAndroid Build Coastguard Worker 74*c8dee2aaSAndroid Build Coastguard Worker fPosterImages[i] = surface->makeImageSnapshot(); 75*c8dee2aaSAndroid Build Coastguard Worker } 76*c8dee2aaSAndroid Build Coastguard Worker } 77*c8dee2aaSAndroid Build Coastguard Worker onDraw(SkCanvas * canvas)78*c8dee2aaSAndroid Build Coastguard Worker void onDraw(SkCanvas* canvas) override { 79*c8dee2aaSAndroid Build Coastguard Worker // See https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/perspective 80*c8dee2aaSAndroid Build Coastguard Worker // for projection matrix when --webkit-perspective: 800px is used. 81*c8dee2aaSAndroid Build Coastguard Worker SkM44 proj; 82*c8dee2aaSAndroid Build Coastguard Worker proj.setRC(3, 2, -1.f / 800.f); 83*c8dee2aaSAndroid Build Coastguard Worker 84*c8dee2aaSAndroid Build Coastguard Worker for (int pass = 0; pass < 2; ++pass) { 85*c8dee2aaSAndroid Build Coastguard Worker // Want to draw 90 to 270 first (the back), then 270 to 90 (the front), but do all 3 86*c8dee2aaSAndroid Build Coastguard Worker // rings backsides, then their frontsides since the front projections overlap across 87*c8dee2aaSAndroid Build Coastguard Worker // rings. Note: we skip the poster circle's x axis rotation because that complicates the 88*c8dee2aaSAndroid Build Coastguard Worker // back-to-front drawing order and it isn't necessary to trigger draws aligned with Z. 89*c8dee2aaSAndroid Build Coastguard Worker bool drawFront = pass > 0; 90*c8dee2aaSAndroid Build Coastguard Worker 91*c8dee2aaSAndroid Build Coastguard Worker for (int y = 0; y < 3; ++y) { 92*c8dee2aaSAndroid Build Coastguard Worker float ringY = (y - 1) * (kPosterSize + 10.f); 93*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < kNumAngles; ++i) { 94*c8dee2aaSAndroid Build Coastguard Worker // Add an extra 45 degree rotation, which triggers the bug by aligning some of 95*c8dee2aaSAndroid Build Coastguard Worker // the posters with the z axis. 96*c8dee2aaSAndroid Build Coastguard Worker SkScalar yDuration = 5.f - y; 97*c8dee2aaSAndroid Build Coastguard Worker SkScalar yRotation = SkScalarMod(kAngleStep * i + 98*c8dee2aaSAndroid Build Coastguard Worker 360.f * SkScalarMod(fTime / yDuration, yDuration), 360.f); 99*c8dee2aaSAndroid Build Coastguard Worker // These rotation limits were chosen manually to line up with current projection 100*c8dee2aaSAndroid Build Coastguard Worker static constexpr SkScalar kBackMinAngle = 70.f; 101*c8dee2aaSAndroid Build Coastguard Worker static constexpr SkScalar kBackMaxAngle = 290.f; 102*c8dee2aaSAndroid Build Coastguard Worker if (drawFront) { 103*c8dee2aaSAndroid Build Coastguard Worker if (yRotation >= kBackMinAngle && yRotation <= kBackMaxAngle) { 104*c8dee2aaSAndroid Build Coastguard Worker // Back portion during a front draw 105*c8dee2aaSAndroid Build Coastguard Worker continue; 106*c8dee2aaSAndroid Build Coastguard Worker } 107*c8dee2aaSAndroid Build Coastguard Worker } else { 108*c8dee2aaSAndroid Build Coastguard Worker if (yRotation < kBackMinAngle || yRotation > kBackMaxAngle) { 109*c8dee2aaSAndroid Build Coastguard Worker // Front portion during a back draw 110*c8dee2aaSAndroid Build Coastguard Worker continue; 111*c8dee2aaSAndroid Build Coastguard Worker } 112*c8dee2aaSAndroid Build Coastguard Worker } 113*c8dee2aaSAndroid Build Coastguard Worker 114*c8dee2aaSAndroid Build Coastguard Worker canvas->save(); 115*c8dee2aaSAndroid Build Coastguard Worker 116*c8dee2aaSAndroid Build Coastguard Worker // Matrix matches transform: rotateY(<angle>deg) translateZ(200px); nested in an 117*c8dee2aaSAndroid Build Coastguard Worker // element with the perspective projection matrix above. 118*c8dee2aaSAndroid Build Coastguard Worker SkM44 model = SkM44::Translate(kStageWidth/2, kStageHeight/2 + 25, 0) 119*c8dee2aaSAndroid Build Coastguard Worker * proj 120*c8dee2aaSAndroid Build Coastguard Worker * SkM44::Translate(0, ringY, 0) 121*c8dee2aaSAndroid Build Coastguard Worker * SkM44::Rotate({0,1,0}, SkDegreesToRadians(yRotation)) 122*c8dee2aaSAndroid Build Coastguard Worker * SkM44::Translate(0, 0, kRingRadius); 123*c8dee2aaSAndroid Build Coastguard Worker canvas->concat(model); 124*c8dee2aaSAndroid Build Coastguard Worker 125*c8dee2aaSAndroid Build Coastguard Worker SkRect poster = SkRect::MakeLTRB(-0.5f * kPosterSize, -0.5f * kPosterSize, 126*c8dee2aaSAndroid Build Coastguard Worker 0.5f * kPosterSize, 0.5f * kPosterSize); 127*c8dee2aaSAndroid Build Coastguard Worker SkPaint fillPaint; 128*c8dee2aaSAndroid Build Coastguard Worker fillPaint.setAntiAlias(true); 129*c8dee2aaSAndroid Build Coastguard Worker fillPaint.setAlphaf(0.7f); 130*c8dee2aaSAndroid Build Coastguard Worker canvas->drawImageRect(fPosterImages[i], poster, 131*c8dee2aaSAndroid Build Coastguard Worker SkSamplingOptions(SkFilterMode::kLinear), &fillPaint); 132*c8dee2aaSAndroid Build Coastguard Worker 133*c8dee2aaSAndroid Build Coastguard Worker canvas->restore(); 134*c8dee2aaSAndroid Build Coastguard Worker } 135*c8dee2aaSAndroid Build Coastguard Worker } 136*c8dee2aaSAndroid Build Coastguard Worker } 137*c8dee2aaSAndroid Build Coastguard Worker } 138*c8dee2aaSAndroid Build Coastguard Worker 139*c8dee2aaSAndroid Build Coastguard Worker private: 140*c8dee2aaSAndroid Build Coastguard Worker static const int kAngleStep = 30; 141*c8dee2aaSAndroid Build Coastguard Worker static const int kNumAngles = 12; // 0 through 330 degrees 142*c8dee2aaSAndroid Build Coastguard Worker 143*c8dee2aaSAndroid Build Coastguard Worker static const int kStageWidth = 600; 144*c8dee2aaSAndroid Build Coastguard Worker static const int kStageHeight = 400; 145*c8dee2aaSAndroid Build Coastguard Worker static const int kRingRadius = 200; 146*c8dee2aaSAndroid Build Coastguard Worker static const int kPosterSize = 100; 147*c8dee2aaSAndroid Build Coastguard Worker 148*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkImage> fPosterImages[kNumAngles]; 149*c8dee2aaSAndroid Build Coastguard Worker SkScalar fTime; 150*c8dee2aaSAndroid Build Coastguard Worker }; 151*c8dee2aaSAndroid Build Coastguard Worker 152*c8dee2aaSAndroid Build Coastguard Worker DEF_GM(return new PosterCircleGM();) 153