aaxfermodes.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. /*
  2. * Copyright 2015 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/SkCanvas.h"
  10. #include "include/core/SkColor.h"
  11. #include "include/core/SkFont.h"
  12. #include "include/core/SkPaint.h"
  13. #include "include/core/SkPath.h"
  14. #include "include/core/SkPoint.h"
  15. #include "include/core/SkRect.h"
  16. #include "include/core/SkScalar.h"
  17. #include "include/core/SkSize.h"
  18. #include "include/core/SkString.h"
  19. #include "include/core/SkTypeface.h"
  20. #include "include/core/SkTypes.h"
  21. #include "include/utils/SkTextUtils.h"
  22. #include "tools/ToolUtils.h"
  23. enum {
  24. kXfermodeCount = (int)SkBlendMode::kLastMode + 1 + 1, // extra for arith
  25. kShapeSize = 22,
  26. kShapeSpacing = 36,
  27. kShapeTypeSpacing = 4 * kShapeSpacing / 3,
  28. kPaintSpacing = 4 * kShapeTypeSpacing,
  29. kLabelSpacing = 3 * kShapeSize,
  30. kMargin = kShapeSpacing / 2,
  31. kXfermodeTypeSpacing = kLabelSpacing + 2 * kPaintSpacing + kShapeTypeSpacing,
  32. kTitleSpacing = 3 * kShapeSpacing / 4,
  33. kSubtitleSpacing = 5 * kShapeSpacing / 8
  34. };
  35. constexpr SkColor kBGColor = 0xc8d2b887;
  36. constexpr SkColor kShapeColors[2] = {
  37. 0x82ff0080, // input color unknown
  38. 0xff00ffff, // input color opaque
  39. };
  40. enum Shape {
  41. kSquare_Shape,
  42. kDiamond_Shape,
  43. kOval_Shape,
  44. kConcave_Shape,
  45. kLast_Shape = kConcave_Shape
  46. };
  47. /**
  48. * Verifies AA works properly on all Xfermodes, including arithmetic, with both opaque and unknown
  49. * src colors.
  50. */
  51. class AAXfermodesGM : public skiagm::GM {
  52. public:
  53. AAXfermodesGM() {}
  54. protected:
  55. enum DrawingPass {
  56. kCheckerboard_Pass,
  57. kBackground_Pass,
  58. kShape_Pass
  59. };
  60. SkString onShortName() override {
  61. return SkString("aaxfermodes");
  62. }
  63. SkISize onISize() override {
  64. return SkISize::Make(2 * kMargin + 2 * kXfermodeTypeSpacing -
  65. (kXfermodeTypeSpacing - (kLabelSpacing + 2 * kPaintSpacing)),
  66. 2 * kMargin + kTitleSpacing + kSubtitleSpacing +
  67. (1 + (int)SkBlendMode::kLastCoeffMode) * kShapeSpacing);
  68. }
  69. void onOnceBeforeDraw() override {
  70. fLabelFont.setTypeface(ToolUtils::create_portable_typeface());
  71. fLabelFont.setSize(5 * kShapeSize/8);
  72. fLabelFont.setSubpixel(true);
  73. constexpr SkScalar radius = -1.4f * kShapeSize/2;
  74. SkPoint pts[4] = {
  75. {-radius, 0},
  76. {0, -1.33f * radius},
  77. {radius, 0},
  78. {0, 1.33f * radius}
  79. };
  80. fOval.moveTo(pts[0]);
  81. fOval.quadTo(pts[1], pts[2]);
  82. fOval.quadTo(pts[3], pts[0]);
  83. fConcave.moveTo(-radius, 0);
  84. fConcave.quadTo(0, 0, 0, -radius);
  85. fConcave.quadTo(0, 0, radius, 0);
  86. fConcave.quadTo(0, 0, 0, radius);
  87. fConcave.quadTo(0, 0, -radius, 0);
  88. fConcave.close();
  89. }
  90. void draw_pass(SkCanvas* canvas, DrawingPass drawingPass) {
  91. SkRect clipRect =
  92. { -kShapeSize*11/16, -kShapeSize*11/16, kShapeSize*11/16, kShapeSize*11/16 };
  93. canvas->save();
  94. if (kCheckerboard_Pass == drawingPass) {
  95. canvas->translate(kMargin, kMargin);
  96. }
  97. canvas->translate(0, kTitleSpacing);
  98. for (size_t xfermodeSet = 0; xfermodeSet < 2; xfermodeSet++) {
  99. size_t firstMode = ((size_t)SkBlendMode::kLastCoeffMode + 1) * xfermodeSet;
  100. canvas->save();
  101. if (kShape_Pass == drawingPass) {
  102. SkTextUtils::DrawString(canvas, "Src Unknown",
  103. kLabelSpacing + kShapeTypeSpacing * 1.5f + kShapeSpacing / 2,
  104. kSubtitleSpacing / 2 + fLabelFont.getSize() / 3, fLabelFont, SkPaint(),
  105. SkTextUtils::kCenter_Align);
  106. SkTextUtils::DrawString(canvas, "Src Opaque",
  107. kLabelSpacing + kShapeTypeSpacing * 1.5f + kShapeSpacing / 2 +
  108. kPaintSpacing, kSubtitleSpacing / 2 + fLabelFont.getSize() / 3,
  109. fLabelFont, SkPaint(), SkTextUtils::kCenter_Align);
  110. }
  111. canvas->translate(0, kSubtitleSpacing + kShapeSpacing/2);
  112. for (size_t m = 0; m <= (size_t)SkBlendMode::kLastCoeffMode; m++) {
  113. if (firstMode + m > (size_t)SkBlendMode::kLastMode) {
  114. break;
  115. }
  116. SkBlendMode mode = static_cast<SkBlendMode>(firstMode + m);
  117. canvas->save();
  118. if (kShape_Pass == drawingPass) {
  119. this->drawModeName(canvas, mode);
  120. }
  121. canvas->translate(kLabelSpacing + kShapeSpacing/2, 0);
  122. for (size_t colorIdx = 0; colorIdx < SK_ARRAY_COUNT(kShapeColors); colorIdx++) {
  123. SkPaint paint;
  124. this->setupShapePaint(canvas, kShapeColors[colorIdx], mode, &paint);
  125. SkASSERT(colorIdx == 0 || 255 == paint.getAlpha());
  126. canvas->save();
  127. for (size_t shapeIdx = 0; shapeIdx <= kLast_Shape; shapeIdx++) {
  128. if (kShape_Pass != drawingPass) {
  129. canvas->save();
  130. canvas->clipRect(clipRect);
  131. if (kCheckerboard_Pass == drawingPass) {
  132. ToolUtils::draw_checkerboard(canvas, 0xffffffff, 0xffc6c3c6, 10);
  133. } else {
  134. SkASSERT(kBackground_Pass == drawingPass);
  135. canvas->drawColor(kBGColor, SkBlendMode::kSrc);
  136. }
  137. canvas->restore();
  138. } else {
  139. this->drawShape(canvas, static_cast<Shape>(shapeIdx), paint, mode);
  140. }
  141. canvas->translate(kShapeTypeSpacing, 0);
  142. }
  143. canvas->restore();
  144. canvas->translate(kPaintSpacing, 0);
  145. }
  146. canvas->restore();
  147. canvas->translate(0, kShapeSpacing);
  148. }
  149. canvas->restore();
  150. canvas->translate(kXfermodeTypeSpacing, 0);
  151. }
  152. canvas->restore();
  153. }
  154. void onDraw(SkCanvas* canvas) override {
  155. draw_pass(canvas, kCheckerboard_Pass);
  156. canvas->saveLayer(nullptr, nullptr);
  157. canvas->translate(kMargin, kMargin);
  158. draw_pass(canvas, kBackground_Pass);
  159. SkFont titleFont(fLabelFont);
  160. titleFont.setSize(9 * titleFont.getSize() / 8);
  161. titleFont.setEmbolden(true);
  162. SkTextUtils::DrawString(canvas, "Porter Duff",
  163. kLabelSpacing + 4 * kShapeTypeSpacing,
  164. kTitleSpacing / 2 + titleFont.getSize() / 3, titleFont, SkPaint(),
  165. SkTextUtils::kCenter_Align);
  166. SkTextUtils::DrawString(canvas, "Advanced",
  167. kXfermodeTypeSpacing + kLabelSpacing + 4 * kShapeTypeSpacing,
  168. kTitleSpacing / 2 + titleFont.getSize() / 3, titleFont, SkPaint(),
  169. SkTextUtils::kCenter_Align);
  170. draw_pass(canvas, kShape_Pass);
  171. canvas->restore();
  172. }
  173. void drawModeName(SkCanvas* canvas, SkBlendMode mode) {
  174. const char* modeName = SkBlendMode_Name(mode);
  175. SkTextUtils::DrawString(canvas, modeName, kLabelSpacing - kShapeSize / 4,
  176. fLabelFont.getSize() / 4, fLabelFont, SkPaint(),
  177. SkTextUtils::kRight_Align);
  178. }
  179. void setupShapePaint(SkCanvas* canvas, SkColor color, SkBlendMode mode, SkPaint* paint) {
  180. paint->setColor(color);
  181. if (mode == SkBlendMode::kPlus) {
  182. // Check for overflow, otherwise we might get confusing AA artifacts.
  183. int maxSum = SkTMax(SkTMax(SkColorGetA(kBGColor) + SkColorGetA(color),
  184. SkColorGetR(kBGColor) + SkColorGetR(color)),
  185. SkTMax(SkColorGetG(kBGColor) + SkColorGetG(color),
  186. SkColorGetB(kBGColor) + SkColorGetB(color)));
  187. if (maxSum > 255) {
  188. SkPaint dimPaint;
  189. dimPaint.setAntiAlias(false);
  190. dimPaint.setBlendMode(SkBlendMode::kDstIn);
  191. if (255 != paint->getAlpha()) {
  192. // Dim the src and dst colors.
  193. dimPaint.setARGB(255 * 255 / maxSum, 0, 0, 0);
  194. paint->setAlpha(255 * paint->getAlpha() / maxSum);
  195. } else {
  196. // Just clear the dst, we need to preserve the paint's opacity.
  197. dimPaint.setARGB(0, 0, 0, 0);
  198. }
  199. canvas->drawRect({ -kShapeSpacing/2, -kShapeSpacing/2,
  200. kShapeSpacing/2 + 3 * kShapeTypeSpacing, kShapeSpacing/2 },
  201. dimPaint);
  202. }
  203. }
  204. }
  205. void drawShape(SkCanvas* canvas, Shape shape, const SkPaint& paint, SkBlendMode mode) {
  206. SkASSERT(mode <= SkBlendMode::kLastMode);
  207. SkPaint shapePaint(paint);
  208. shapePaint.setAntiAlias(kSquare_Shape != shape);
  209. shapePaint.setBlendMode(mode);
  210. switch (shape) {
  211. case kSquare_Shape:
  212. canvas->drawRect({ -kShapeSize/2, -kShapeSize/2, kShapeSize/2, kShapeSize/2 },
  213. shapePaint);
  214. break;
  215. case kDiamond_Shape:
  216. canvas->save();
  217. canvas->rotate(45);
  218. canvas->drawRect({ -kShapeSize/2, -kShapeSize/2, kShapeSize/2, kShapeSize/2 },
  219. shapePaint);
  220. canvas->restore();
  221. break;
  222. case kOval_Shape:
  223. canvas->save();
  224. canvas->rotate(static_cast<SkScalar>((511 * (int)mode + 257) % 360));
  225. canvas->drawPath(fOval, shapePaint);
  226. canvas->restore();
  227. break;
  228. case kConcave_Shape:
  229. canvas->drawPath(fConcave, shapePaint);
  230. break;
  231. default:
  232. SK_ABORT("Invalid shape.");
  233. }
  234. }
  235. private:
  236. SkFont fLabelFont;
  237. SkPath fOval;
  238. SkPath fConcave;
  239. typedef skiagm::GM INHERITED;
  240. };
  241. DEF_GM( return new AAXfermodesGM; )