blob: 2ea1844e274ebedb584e2885f278ab7c2c94c9f5 [file] [log] [blame]
cdalton6fd158e2015-05-27 15:08:33 -07001/*
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.h"
8#include "SkArithmeticMode.h"
bungemand3ebb482015-08-05 13:57:49 -07009#include "SkPath.h"
cdalton6fd158e2015-05-27 15:08:33 -070010#include "SkShader.h"
11#include "SkXfermode.h"
12
13enum {
14 kXfermodeCount = SkXfermode::kLastMode + 2, // All xfermodes plus arithmetic mode.
15 kShapeSize = 22,
16 kShapeSpacing = 36,
17 kShapeTypeSpacing = 4 * kShapeSpacing / 3,
ethannicholas3819d2d2015-12-17 10:58:28 -080018 kPaintSpacing = 4 * kShapeTypeSpacing,
cdalton6fd158e2015-05-27 15:08:33 -070019 kLabelSpacing = 3 * kShapeSize,
20 kMargin = kShapeSpacing / 2,
21 kXfermodeTypeSpacing = kLabelSpacing + 2 * kPaintSpacing + kShapeTypeSpacing,
22 kTitleSpacing = 3 * kShapeSpacing / 4,
23 kSubtitleSpacing = 5 * kShapeSpacing / 8
24};
25
26static const SkColor kBGColor = SkColorSetARGB(200, 210, 184, 135);
27
28static const SkColor kShapeColors[2] = {
29 SkColorSetARGB(130, 255, 0, 128), // input color unknown
30 SkColorSetARGB(255, 0, 255, 255) // input color opaque
31};
32
33enum Shape {
34 kSquare_Shape,
35 kDiamond_Shape,
36 kOval_Shape,
ethannicholas3819d2d2015-12-17 10:58:28 -080037 kConcave_Shape,
cdalton6fd158e2015-05-27 15:08:33 -070038
ethannicholas3819d2d2015-12-17 10:58:28 -080039 kLast_Shape = kConcave_Shape
cdalton6fd158e2015-05-27 15:08:33 -070040};
41
42namespace skiagm {
43
44/**
45 * Verifies AA works properly on all Xfermodes, including arithmetic, with both opaque and unknown
46 * src colors.
47 */
48class AAXfermodesGM : public GM {
49public:
50 AAXfermodesGM() {}
51
52protected:
caryclark83ca6282015-06-10 09:31:09 -070053 enum DrawingPass {
54 kCheckerboard_Pass,
55 kBackground_Pass,
56 kShape_Pass
57 };
58
cdalton6fd158e2015-05-27 15:08:33 -070059 SkString onShortName() override {
60 return SkString("aaxfermodes");
61 }
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 + SkXfermode::kLastCoeffMode) * kShapeSpacing);
68 }
69
70 void onOnceBeforeDraw() override {
71 fLabelPaint.setAntiAlias(true);
caryclark1818acb2015-07-24 12:09:25 -070072 sk_tool_utils::set_portable_typeface(&fLabelPaint);
cdalton6fd158e2015-05-27 15:08:33 -070073 fLabelPaint.setTextSize(5 * kShapeSize/8);
74 fLabelPaint.setSubpixelText(true);
75
76 static const SkScalar radius = -1.4f * kShapeSize/2;
77 SkPoint pts[4] = {
78 {-radius, 0},
79 {0, -1.33f * radius},
80 {radius, 0},
81 {0, 1.33f * radius}
82 };
ethannicholas3819d2d2015-12-17 10:58:28 -080083 fOval.moveTo(pts[0]);
84 fOval.quadTo(pts[1], pts[2]);
85 fOval.quadTo(pts[3], pts[0]);
86
87 fConcave.moveTo(-radius, 0);
88 fConcave.quadTo(0, 0, 0, -radius);
89 fConcave.quadTo(0, 0, radius, 0);
90 fConcave.quadTo(0, 0, 0, radius);
91 fConcave.quadTo(0, 0, -radius, 0);
92 fConcave.close();
cdalton6fd158e2015-05-27 15:08:33 -070093 }
94
caryclark83ca6282015-06-10 09:31:09 -070095 void draw_pass(SkCanvas* canvas, DrawingPass drawingPass) {
96 SkRect clipRect =
97 { -kShapeSize*11/16, -kShapeSize*11/16, kShapeSize*11/16, kShapeSize*11/16 };
cdalton6fd158e2015-05-27 15:08:33 -070098
caryclark83ca6282015-06-10 09:31:09 -070099 canvas->save();
100 if (kCheckerboard_Pass == drawingPass) {
101 canvas->translate(kMargin, kMargin);
102 }
cdalton6fd158e2015-05-27 15:08:33 -0700103 canvas->translate(0, kTitleSpacing);
104
105 for (size_t xfermodeSet = 0; xfermodeSet < 2; xfermodeSet++) {
106 size_t firstMode = (SkXfermode::kLastCoeffMode + 1) * xfermodeSet;
107 canvas->save();
108
caryclark83ca6282015-06-10 09:31:09 -0700109 if (kShape_Pass == drawingPass) {
110 fLabelPaint.setTextAlign(SkPaint::kCenter_Align);
111 canvas->drawText("Src Unknown", sizeof("Src Unknown") - 1,
ethannicholas3819d2d2015-12-17 10:58:28 -0800112 kLabelSpacing + kShapeTypeSpacing * 1.5f + kShapeSpacing / 2,
caryclark83ca6282015-06-10 09:31:09 -0700113 kSubtitleSpacing / 2 + fLabelPaint.getTextSize() / 3, fLabelPaint);
114 canvas->drawText("Src Opaque", sizeof("Src Opaque") - 1,
halcanary9d524f22016-03-29 09:03:52 -0700115 kLabelSpacing + kShapeTypeSpacing * 1.5f + kShapeSpacing / 2 +
116 kPaintSpacing, kSubtitleSpacing / 2 + fLabelPaint.getTextSize() / 3,
ethannicholas3819d2d2015-12-17 10:58:28 -0800117 fLabelPaint);
caryclark83ca6282015-06-10 09:31:09 -0700118 }
cdalton6fd158e2015-05-27 15:08:33 -0700119
120 canvas->translate(0, kSubtitleSpacing + kShapeSpacing/2);
121
122 for (size_t m = 0; m <= SkXfermode::kLastCoeffMode; m++) {
123 SkXfermode::Mode mode = static_cast<SkXfermode::Mode>(firstMode + m);
124 canvas->save();
125
caryclark83ca6282015-06-10 09:31:09 -0700126 if (kShape_Pass == drawingPass) {
127 this->drawModeName(canvas, mode);
128 }
cdalton6fd158e2015-05-27 15:08:33 -0700129 canvas->translate(kLabelSpacing + kShapeSpacing/2, 0);
130
131 for (size_t colorIdx = 0; colorIdx < SK_ARRAY_COUNT(kShapeColors); colorIdx++) {
132 SkPaint paint;
133 this->setupShapePaint(canvas, kShapeColors[colorIdx], mode, &paint);
134 SkASSERT(colorIdx == 0 || 255 == paint.getAlpha());
135 canvas->save();
136
137 for (size_t shapeIdx = 0; shapeIdx <= kLast_Shape; shapeIdx++) {
caryclark83ca6282015-06-10 09:31:09 -0700138 if (kShape_Pass != drawingPass) {
139 canvas->save();
140 canvas->clipRect(clipRect);
141 if (kCheckerboard_Pass == drawingPass) {
142 sk_tool_utils::draw_checkerboard(canvas, 0xffffffff, 0xffc6c3c6,
143 10);
144 } else {
145 SkASSERT(kBackground_Pass == drawingPass);
146 canvas->drawColor(kBGColor, SkXfermode::kSrc_Mode);
147 }
148 canvas->restore();
149 } else {
150 this->drawShape(canvas, static_cast<Shape>(shapeIdx), paint, mode);
151 }
cdalton6fd158e2015-05-27 15:08:33 -0700152 canvas->translate(kShapeTypeSpacing, 0);
153 }
154
155 canvas->restore();
156 canvas->translate(kPaintSpacing, 0);
157 }
158
159 canvas->restore();
160 canvas->translate(0, kShapeSpacing);
161 }
162
163 canvas->restore();
164 canvas->translate(kXfermodeTypeSpacing, 0);
165 }
166
167 canvas->restore();
168 }
169
caryclark83ca6282015-06-10 09:31:09 -0700170 void onDraw(SkCanvas* canvas) override {
171 draw_pass(canvas, kCheckerboard_Pass);
halcanary96fcdcc2015-08-27 07:41:13 -0700172 canvas->saveLayer(nullptr, nullptr);
caryclark83ca6282015-06-10 09:31:09 -0700173
174 canvas->translate(kMargin, kMargin);
175 draw_pass(canvas, kBackground_Pass);
176
177 SkPaint titlePaint(fLabelPaint);
178 titlePaint.setTextSize(9 * titlePaint.getTextSize() / 8);
179 titlePaint.setFakeBoldText(true);
180 titlePaint.setTextAlign(SkPaint::kCenter_Align);
181 canvas->drawText("Porter Duff", sizeof("Porter Duff") - 1,
ethannicholas3819d2d2015-12-17 10:58:28 -0800182 kLabelSpacing + 4 * kShapeTypeSpacing,
caryclark83ca6282015-06-10 09:31:09 -0700183 kTitleSpacing / 2 + titlePaint.getTextSize() / 3, titlePaint);
184 canvas->drawText("Advanced", sizeof("Advanced") - 1,
ethannicholas3819d2d2015-12-17 10:58:28 -0800185 kXfermodeTypeSpacing + kLabelSpacing + 4 * kShapeTypeSpacing,
caryclark83ca6282015-06-10 09:31:09 -0700186 kTitleSpacing / 2 + titlePaint.getTextSize() / 3, titlePaint);
187
188 draw_pass(canvas, kShape_Pass);
189 canvas->restore();
190 }
191
cdalton6fd158e2015-05-27 15:08:33 -0700192 void drawModeName(SkCanvas* canvas, SkXfermode::Mode mode) {
193 const char* modeName = mode <= SkXfermode::kLastMode ? SkXfermode::ModeName(mode)
194 : "Arithmetic";
195 fLabelPaint.setTextAlign(SkPaint::kRight_Align);
196 canvas->drawText(modeName, strlen(modeName), kLabelSpacing - kShapeSize / 4,
ethannicholas3819d2d2015-12-17 10:58:28 -0800197 fLabelPaint.getTextSize() / 4, fLabelPaint);
cdalton6fd158e2015-05-27 15:08:33 -0700198 }
199
200 void setupShapePaint(SkCanvas* canvas, GrColor color, SkXfermode::Mode mode, SkPaint* paint) {
201 paint->setColor(color);
202
203 if (mode == SkXfermode::kPlus_Mode) {
204 // Check for overflow, otherwise we might get confusing AA artifacts.
205 int maxSum = SkTMax(SkTMax(SkColorGetA(kBGColor) + SkColorGetA(color),
206 SkColorGetR(kBGColor) + SkColorGetR(color)),
207 SkTMax(SkColorGetG(kBGColor) + SkColorGetG(color),
208 SkColorGetB(kBGColor) + SkColorGetB(color)));
209
210 if (maxSum > 255) {
211 SkPaint dimPaint;
212 dimPaint.setAntiAlias(false);
reedcfb6bdf2016-03-29 11:32:50 -0700213 dimPaint.setXfermode(SkXfermode::Make(SkXfermode::kDstIn_Mode));
cdalton6fd158e2015-05-27 15:08:33 -0700214 if (255 != paint->getAlpha()) {
215 // Dim the src and dst colors.
216 dimPaint.setARGB(255 * 255 / maxSum, 0, 0, 0);
217 paint->setAlpha(255 * paint->getAlpha() / maxSum);
218 } else {
219 // Just clear the dst, we need to preserve the paint's opacity.
220 dimPaint.setARGB(0, 0, 0, 0);
221 }
222 canvas->drawRectCoords(-kShapeSpacing/2, -kShapeSpacing/2,
ethannicholas3819d2d2015-12-17 10:58:28 -0800223 kShapeSpacing/2 + 3 * kShapeTypeSpacing,
cdalton6fd158e2015-05-27 15:08:33 -0700224 kShapeSpacing/2, dimPaint);
225 }
226 }
227 }
228
229 void drawShape(SkCanvas* canvas, Shape shape, const SkPaint& paint, SkXfermode::Mode mode) {
230 SkPaint shapePaint(paint);
231 shapePaint.setAntiAlias(kSquare_Shape != shape);
232
reedcfb6bdf2016-03-29 11:32:50 -0700233 sk_sp<SkXfermode> xfermode;
cdalton6fd158e2015-05-27 15:08:33 -0700234 if (mode <= SkXfermode::kLastMode) {
reedcfb6bdf2016-03-29 11:32:50 -0700235 xfermode = SkXfermode::Make(mode);
cdalton6fd158e2015-05-27 15:08:33 -0700236 } else {
reedcfb6bdf2016-03-29 11:32:50 -0700237 xfermode = SkArithmeticMode::Make(+1.0f, +0.25f, -0.5f, +0.1f);
cdalton6fd158e2015-05-27 15:08:33 -0700238 }
reedcfb6bdf2016-03-29 11:32:50 -0700239 shapePaint.setXfermode(std::move(xfermode));
cdalton6fd158e2015-05-27 15:08:33 -0700240
241 switch (shape) {
242 case kSquare_Shape:
243 canvas->drawRectCoords(-kShapeSize/2, -kShapeSize/2, kShapeSize/2, kShapeSize/2,
244 shapePaint);
245 break;
246
247 case kDiamond_Shape:
248 canvas->save();
249 canvas->rotate(45);
250 canvas->drawRectCoords(-kShapeSize/2, -kShapeSize/2, kShapeSize/2, kShapeSize/2,
251 shapePaint);
252 canvas->restore();
253 break;
254
255 case kOval_Shape:
256 canvas->save();
257 canvas->rotate(static_cast<SkScalar>((511 * mode + 257) % 360));
ethannicholas3819d2d2015-12-17 10:58:28 -0800258 canvas->drawPath(fOval, shapePaint);
cdalton6fd158e2015-05-27 15:08:33 -0700259 canvas->restore();
260 break;
261
ethannicholas3819d2d2015-12-17 10:58:28 -0800262 case kConcave_Shape:
263 canvas->drawPath(fConcave, shapePaint);
264 break;
265
cdalton6fd158e2015-05-27 15:08:33 -0700266 default:
267 SkFAIL("Invalid shape.");
268 }
269 }
270
271private:
272 SkPaint fLabelPaint;
ethannicholas3819d2d2015-12-17 10:58:28 -0800273 SkPath fOval;
274 SkPath fConcave;
cdalton6fd158e2015-05-27 15:08:33 -0700275
276 typedef GM INHERITED;
277};
278
279//////////////////////////////////////////////////////////////////////////////
280
281static GM* MyFactory(void*) { return new AAXfermodesGM; }
282static GMRegistry reg(MyFactory);
283
284}