megalooper.cpp 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. /*
  2. * Copyright 2013 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/SkBlendMode.h"
  9. #include "include/core/SkBlurTypes.h"
  10. #include "include/core/SkCanvas.h"
  11. #include "include/core/SkColor.h"
  12. #include "include/core/SkColorFilter.h"
  13. #include "include/core/SkDrawLooper.h"
  14. #include "include/core/SkMaskFilter.h"
  15. #include "include/core/SkPaint.h"
  16. #include "include/core/SkPoint.h"
  17. #include "include/core/SkRect.h"
  18. #include "include/core/SkRefCnt.h"
  19. #include "include/core/SkScalar.h"
  20. #include "include/core/SkSize.h"
  21. #include "include/core/SkString.h"
  22. #include "include/effects/SkLayerDrawLooper.h"
  23. #include "src/core/SkBlurMask.h"
  24. #include "src/core/SkClipOpPriv.h"
  25. #ifdef SK_SUPPORT_LEGACY_DRAWLOOPER
  26. namespace {
  27. // This GM tests 3 different ways of drawing four shadows around a square:
  28. // just using 4 blurred rects
  29. // using 4 1-level draw loopers
  30. // using 1 4-level draw looper
  31. // They all produce exactly the same pixels
  32. class MegaLooperGM : public skiagm::GM {
  33. public:
  34. // The types define "<# of loopers> x <# of stages per looper>"
  35. enum Type {
  36. k0x0_Type, // draw without loopers at all
  37. k4x1_Type, // a looper for each shadow
  38. k1x4_Type, // all four shadows in one looper
  39. };
  40. MegaLooperGM(Type type) : fType(type) {}
  41. private:
  42. SkString onShortName() override {
  43. switch (fType) {
  44. case k0x0_Type:
  45. return SkString("megalooper_0x0");
  46. break;
  47. case k4x1_Type:
  48. return SkString("megalooper_4x1");
  49. break;
  50. case k1x4_Type: // fall through
  51. default:
  52. return SkString("megalooper_1x4");
  53. break;
  54. }
  55. }
  56. SkISize onISize() override {
  57. return {kWidth, kHeight};
  58. }
  59. void onDraw(SkCanvas* canvas) override {
  60. for (int y = 100; y < kHeight; y += 200) {
  61. for (int x = 100; x < kWidth; x += 200) {
  62. switch (fType) {
  63. case k0x0_Type:
  64. draw0x0(canvas, SkIntToScalar(x), SkIntToScalar(y));
  65. break;
  66. case k4x1_Type:
  67. draw4x1(canvas, SkIntToScalar(x), SkIntToScalar(y));
  68. break;
  69. case k1x4_Type: // fall through
  70. default:
  71. draw1x4(canvas, SkIntToScalar(x), SkIntToScalar(y));
  72. break;
  73. }
  74. }
  75. }
  76. }
  77. static constexpr int kWidth = 800;
  78. static constexpr int kHeight = 800;
  79. static constexpr int kHalfOuterClipSize = 100;
  80. static constexpr int kHalfSquareSize = 50;
  81. static constexpr int kOffsetToOutsideClip = kHalfSquareSize + kHalfOuterClipSize + 1;
  82. static const SkPoint gBlurOffsets[4];
  83. static const SkColor gColors[4];
  84. Type fType;
  85. // Just draw a blurred rect at each of the four corners of a square (centered at x,y).
  86. // Use two clips to define a rectori where we want pixels to appear.
  87. void draw0x0(SkCanvas* canvas, SkScalar x, SkScalar y) {
  88. SkRect innerClip = { -kHalfSquareSize, -kHalfSquareSize, kHalfSquareSize, kHalfSquareSize };
  89. innerClip.offset(x, y);
  90. SkRect outerClip = {
  91. -kHalfOuterClipSize-kHalfSquareSize, -kHalfOuterClipSize-kHalfSquareSize,
  92. kHalfOuterClipSize+kHalfSquareSize, kHalfOuterClipSize+kHalfSquareSize
  93. };
  94. outerClip.offset(x, y);
  95. canvas->save();
  96. canvas->clipRect(outerClip, kIntersect_SkClipOp);
  97. canvas->clipRect(innerClip, kDifference_SkClipOp);
  98. SkPaint paint;
  99. paint.setAntiAlias(true);
  100. paint.setMaskFilter(MakeBlur());
  101. for (int i = 0; i < 4; ++i) {
  102. paint.setColor(gColors[i]);
  103. SkRect rect = { -kHalfSquareSize, -kHalfSquareSize, kHalfSquareSize, kHalfSquareSize };
  104. rect.offset(gBlurOffsets[i]);
  105. rect.offset(x, y);
  106. canvas->drawRect(rect, paint);
  107. }
  108. canvas->restore();
  109. }
  110. static sk_sp<SkMaskFilter> MakeBlur() {
  111. const SkScalar kBlurSigma = SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(25));
  112. return SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, kBlurSigma);
  113. }
  114. // This draws 4 blurred shadows around a single square (centered at x, y).
  115. // Each blur is offset +/- half the square's side in x & y from the original
  116. // (so each blurred rect is centered at one of the corners of the original).
  117. // For each blur a large outer clip is centered around the blurred rect
  118. // while a difference clip stays at the location of the original rect.
  119. // Each blurred rect is drawn with a draw looper where the original (non-
  120. // blurred rect) is offset to reside outside of the large outer clip (so
  121. // it never appears) but the offset in the draw looper is used to translate
  122. // the blurred version back into the clip.
  123. void draw4x1(SkCanvas* canvas, SkScalar x, SkScalar y) {
  124. for (int i = 0; i < 4; ++i) {
  125. SkPaint loopPaint;
  126. loopPaint.setLooper(create1Looper(-kOffsetToOutsideClip, 0, gColors[i]));
  127. loopPaint.setAntiAlias(true);
  128. SkRect outerClip = {
  129. -kHalfOuterClipSize, -kHalfOuterClipSize,
  130. kHalfOuterClipSize, kHalfOuterClipSize
  131. };
  132. outerClip.offset(x, y);
  133. // center it on the blurred rect
  134. outerClip.offset(gBlurOffsets[i]);
  135. SkRect rect = { -kHalfSquareSize, -kHalfSquareSize, kHalfSquareSize, kHalfSquareSize };
  136. rect.offset(x, y);
  137. canvas->save();
  138. canvas->clipRect(outerClip, kIntersect_SkClipOp);
  139. canvas->clipRect(rect, kDifference_SkClipOp);
  140. // move the rect to where we want the blur to appear
  141. rect.offset(gBlurOffsets[i]);
  142. // then move it outside the clip (the blur stage of the draw
  143. // looper will undo this translation)
  144. rect.offset(SkIntToScalar(kOffsetToOutsideClip), 0);
  145. canvas->drawRect(rect, loopPaint);
  146. canvas->restore();
  147. }
  148. }
  149. // Create a 1-tier drawlooper
  150. sk_sp<SkDrawLooper> create1Looper(SkScalar xOff, SkScalar yOff, SkColor color) {
  151. SkLayerDrawLooper::Builder looperBuilder;
  152. SkLayerDrawLooper::LayerInfo info;
  153. info.fPaintBits = SkLayerDrawLooper::kColorFilter_Bit |
  154. SkLayerDrawLooper::kMaskFilter_Bit;
  155. info.fColorMode = SkBlendMode::kSrc;
  156. info.fOffset.set(xOff, yOff);
  157. info.fPostTranslate = false;
  158. SkPaint* paint = looperBuilder.addLayer(info);
  159. paint->setMaskFilter(MakeBlur());
  160. paint->setColorFilter(SkColorFilters::Blend(color, SkBlendMode::kSrcIn));
  161. return looperBuilder.detach();
  162. }
  163. void draw1x4(SkCanvas* canvas, SkScalar x, SkScalar y) {
  164. SkRect rect = { -kHalfSquareSize, -kHalfSquareSize, kHalfSquareSize, kHalfSquareSize };
  165. rect.offset(x, y);
  166. SkRect outerClip = {
  167. -kHalfOuterClipSize-kHalfSquareSize, -kHalfOuterClipSize-kHalfSquareSize,
  168. kHalfOuterClipSize+kHalfSquareSize, kHalfOuterClipSize+kHalfSquareSize
  169. };
  170. outerClip.offset(x, y);
  171. SkPaint paint;
  172. paint.setAntiAlias(true);
  173. paint.setLooper(create4Looper(-kOffsetToOutsideClip-kHalfSquareSize, 0));
  174. canvas->save();
  175. canvas->clipRect(outerClip, kIntersect_SkClipOp);
  176. canvas->clipRect(rect, kDifference_SkClipOp);
  177. rect.offset(SkIntToScalar(kOffsetToOutsideClip+kHalfSquareSize), 0);
  178. canvas->drawRect(rect, paint);
  179. canvas->restore();
  180. }
  181. // Create a 4-tier draw looper
  182. sk_sp<SkDrawLooper> create4Looper(SkScalar xOff, SkScalar yOff) {
  183. SkLayerDrawLooper::Builder looperBuilder;
  184. SkLayerDrawLooper::LayerInfo info;
  185. info.fPaintBits = SkLayerDrawLooper::kColorFilter_Bit |
  186. SkLayerDrawLooper::kMaskFilter_Bit;
  187. info.fColorMode = SkBlendMode::kSrc;
  188. info.fPostTranslate = false;
  189. SkPaint* paint;
  190. for (int i = 3; i >= 0; --i) {
  191. info.fOffset.set(xOff+gBlurOffsets[i].fX, yOff+gBlurOffsets[i].fY);
  192. paint = looperBuilder.addLayer(info);
  193. paint->setMaskFilter(MakeBlur());
  194. paint->setColorFilter(SkColorFilters::Blend(gColors[i], SkBlendMode::kSrcIn));
  195. }
  196. return looperBuilder.detach();
  197. }
  198. };
  199. const SkPoint MegaLooperGM::gBlurOffsets[4] = {
  200. { kHalfSquareSize, kHalfSquareSize },
  201. { -kHalfSquareSize, kHalfSquareSize },
  202. { kHalfSquareSize, -kHalfSquareSize },
  203. { -kHalfSquareSize, -kHalfSquareSize }
  204. };
  205. const SkColor MegaLooperGM::gColors[4] = {
  206. SK_ColorGREEN, SK_ColorYELLOW, SK_ColorBLUE, SK_ColorRED
  207. };
  208. } // namespace
  209. DEF_GM( return new MegaLooperGM(MegaLooperGM::k0x0_Type); )
  210. DEF_GM( return new MegaLooperGM(MegaLooperGM::k4x1_Type); )
  211. DEF_GM( return new MegaLooperGM(MegaLooperGM::k1x4_Type); )
  212. #endif