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