postercircle.cpp 6.7 KB

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