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