• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
8 #include "gm.h"
9 
10 #include "SkAutoMalloc.h"
11 #include "SkCanvas.h"
12 #include "SkRSXform.h"
13 #include "SkSurface.h"
14 #include "SkTextBlob.h"
15 #include "sk_tool_utils.h"
16 
17 class DrawAtlasGM : public skiagm::GM {
MakeAtlas(SkCanvas * caller,const SkRect & target)18     static sk_sp<SkImage> MakeAtlas(SkCanvas* caller, const SkRect& target) {
19         SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
20         auto surface(sk_tool_utils::makeSurface(caller, info));
21         SkCanvas* canvas = surface->getCanvas();
22         // draw red everywhere, but we don't expect to see it in the draw, testing the notion
23         // that drawAtlas draws a subset-region of the atlas.
24         canvas->clear(SK_ColorRED);
25 
26         SkPaint paint;
27         paint.setBlendMode(SkBlendMode::kClear);
28         SkRect r(target);
29         r.inset(-1, -1);
30         // zero out a place (with a 1-pixel border) to land our drawing.
31         canvas->drawRect(r, paint);
32         paint.setBlendMode(SkBlendMode::kSrcOver);
33         paint.setColor(SK_ColorBLUE);
34         paint.setAntiAlias(true);
35         canvas->drawOval(target, paint);
36         return surface->makeImageSnapshot();
37     }
38 
39 public:
DrawAtlasGM()40     DrawAtlasGM() {}
41 
42 protected:
43 
onShortName()44     SkString onShortName() override {
45         return SkString("draw-atlas");
46     }
47 
onISize()48     SkISize onISize() override {
49         return SkISize::Make(640, 480);
50     }
51 
onDraw(SkCanvas * canvas)52     void onDraw(SkCanvas* canvas) override {
53         const SkRect target = { 50, 50, 80, 90 };
54         auto atlas = MakeAtlas(canvas, target);
55 
56         const struct {
57             SkScalar fScale;
58             SkScalar fDegrees;
59             SkScalar fTx;
60             SkScalar fTy;
61 
62             void apply(SkRSXform* xform) const {
63                 const SkScalar rad = SkDegreesToRadians(fDegrees);
64                 xform->fSCos = fScale * SkScalarCos(rad);
65                 xform->fSSin = fScale * SkScalarSin(rad);
66                 xform->fTx   = fTx;
67                 xform->fTy   = fTy;
68             }
69         } rec[] = {
70             { 1, 0, 10, 10 },       // just translate
71             { 2, 0, 110, 10 },      // scale + translate
72             { 1, 30, 210, 10 },     // rotate + translate
73             { 2, -30, 310, 30 },    // scale + rotate + translate
74         };
75 
76         const int N = SK_ARRAY_COUNT(rec);
77         SkRSXform xform[N];
78         SkRect tex[N];
79         SkColor colors[N];
80 
81         for (int i = 0; i < N; ++i) {
82             rec[i].apply(&xform[i]);
83             tex[i] = target;
84             colors[i] = 0x80FF0000 + (i * 40 * 256);
85         }
86 
87         SkPaint paint;
88         paint.setFilterQuality(kLow_SkFilterQuality);
89         paint.setAntiAlias(true);
90 
91         canvas->drawAtlas(atlas.get(), xform, tex, N, nullptr, &paint);
92         canvas->translate(0, 100);
93         canvas->drawAtlas(atlas.get(), xform, tex, colors, N, SkBlendMode::kSrcIn, nullptr, &paint);
94     }
95 
96 private:
97     typedef GM INHERITED;
98 };
DEF_GM(return new DrawAtlasGM;)99 DEF_GM( return new DrawAtlasGM; )
100 
101 ///////////////////////////////////////////////////////////////////////////////////////////////////
102 #include "SkFont.h"
103 #include "SkFontPriv.h"
104 #include "SkPath.h"
105 #include "SkPathMeasure.h"
106 
107 static void draw_text_on_path(SkCanvas* canvas, const void* text, size_t length,
108                               const SkPoint xy[], const SkPath& path, const SkFont& font, const SkPaint& paint,
109                               float baseline_offset) {
110     SkPathMeasure meas(path, false);
111 
112     int count = font.countText(text, length, kUTF8_SkTextEncoding);
113     size_t size = count * (sizeof(SkRSXform) + sizeof(SkScalar));
114     SkAutoSMalloc<512> storage(size);
115     SkRSXform* xform = (SkRSXform*)storage.get();
116     SkScalar* widths = (SkScalar*)(xform + count);
117 
118     // Compute a conservative bounds so we can cull the draw
119     const SkRect fontb = SkFontPriv::GetFontBounds(font);
120     const SkScalar max = SkTMax(SkTMax(SkScalarAbs(fontb.fLeft), SkScalarAbs(fontb.fRight)),
121                                 SkTMax(SkScalarAbs(fontb.fTop), SkScalarAbs(fontb.fBottom)));
122     const SkRect bounds = path.getBounds().makeOutset(max, max);
123 
124     SkAutoTArray<SkGlyphID> glyphs(count);
125     font.textToGlyphs(text, length, kUTF8_SkTextEncoding, glyphs.get(), count);
126     font.getWidths(glyphs.get(), count, widths);
127 
128     for (int i = 0; i < count; ++i) {
129         // we want to position each character on the center of its advance
130         const SkScalar offset = SkScalarHalf(widths[i]);
131         SkPoint pos;
132         SkVector tan;
133         if (!meas.getPosTan(xy[i].x() + offset, &pos, &tan)) {
134             pos = xy[i];
135             tan.set(1, 0);
136         }
137         pos += SkVector::Make(-tan.fY, tan.fX) * baseline_offset;
138 
139         xform[i].fSCos = tan.x();
140         xform[i].fSSin = tan.y();
141         xform[i].fTx   = pos.x() - tan.y() * xy[i].y() - tan.x() * offset;
142         xform[i].fTy   = pos.y() + tan.x() * xy[i].y() - tan.y() * offset;
143     }
144 
145     canvas->drawTextBlob(SkTextBlob::MakeFromRSXform(glyphs.get(), count * sizeof(SkGlyphID),
146                                          &xform[0], font, kGlyphID_SkTextEncoding),
147                          0, 0, paint);
148 
149     if (true) {
150         SkPaint p;
151         p.setStyle(SkPaint::kStroke_Style);
152         canvas->drawRect(bounds, p);
153     }
154 }
155 
156 #include "SkGradientShader.h"
make_shader()157 static sk_sp<SkShader> make_shader() {
158     SkPoint pts[2] = {{0, 0}, {220, 0}};
159     SkColor colors[2] = {SK_ColorRED, SK_ColorBLUE};
160     return SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkShader::kMirror_TileMode);
161 }
162 
drawTextPath(SkCanvas * canvas,bool doStroke)163 static void drawTextPath(SkCanvas* canvas, bool doStroke) {
164     const char text0[] = "ABCDFGHJKLMNOPQRSTUVWXYZ";
165     const int N = sizeof(text0) - 1;
166     SkPoint pos[N];
167 
168     SkFont font;
169     font.setSize(100);
170 
171     SkPaint paint;
172     paint.setShader(make_shader());
173     paint.setAntiAlias(true);
174     if (doStroke) {
175         paint.setStyle(SkPaint::kStroke_Style);
176         paint.setStrokeWidth(2.25f);
177         paint.setStrokeJoin(SkPaint::kRound_Join);
178     }
179 
180     SkScalar x = 0;
181     for (int i = 0; i < N; ++i) {
182         pos[i].set(x, 0);
183         x += font.measureText(&text0[i], 1, kUTF8_SkTextEncoding, nullptr, &paint);
184     }
185 
186     SkPath path;
187     const float baseline_offset = -5;
188 
189     const SkPath::Direction dirs[] = {
190         SkPath::kCW_Direction, SkPath::kCCW_Direction,
191     };
192     for (auto d : dirs) {
193         path.reset();
194         path.addOval(SkRect::MakeXYWH(160, 160, 540, 540), d);
195         draw_text_on_path(canvas, text0, N, pos, path, font, paint, baseline_offset);
196     }
197 
198     paint.reset();
199     paint.setStyle(SkPaint::kStroke_Style);
200     canvas->drawPath(path, paint);
201 }
202 
203 DEF_SIMPLE_GM(drawTextRSXform, canvas, 430, 860) {
204     canvas->scale(0.5f, 0.5f);
205     const bool doStroke[] = { false, true };
206     for (auto st : doStroke) {
207         drawTextPath(canvas, st);
208         canvas->translate(0, 860);
209     }
210 }
211 
212 // Exercise xform blob and its bounds
213 DEF_SIMPLE_GM(blob_rsxform, canvas, 500, 100) {
214     SkFont font;
215     font.setTypeface(sk_tool_utils::create_portable_typeface());
216     font.setSize(50);
217 
218     const char text[] = "CrazyXform";
219     constexpr size_t len = sizeof(text) - 1;
220 
221     SkRSXform xforms[len];
222     SkScalar scale = 1;
223     SkScalar x = 0, y = 0;
224     for (size_t i = 0; i < len; ++i) {
225         scale = SkScalarSin(i * SK_ScalarPI / (len-1)) * 0.75f + 0.5f;
226         xforms[i] = SkRSXform::Make(scale, 0, x, y);
227         x += 50 * scale;
228     }
229 
230     auto blob = SkTextBlob::MakeFromRSXform(text, len, xforms, font);
231 
232     SkPoint offset = { 20, 70 };
233     SkPaint paint;
234     paint.setColor(0xFFCCCCCC);
235     canvas->drawRect(blob->bounds().makeOffset(offset.fX, offset.fY), paint);
236     paint.setColor(SK_ColorBLACK);
237     canvas->drawTextBlob(blob, offset.fX, offset.fY, paint);
238 }
239 
240 #include "Resources.h"
241 #include "SkColorFilter.h"
242 #include "SkVertices.h"
243 
make_vertices(sk_sp<SkImage> image,const SkRect & r,SkColor color)244 static sk_sp<SkVertices> make_vertices(sk_sp<SkImage> image, const SkRect& r,
245                                        SkColor color) {
246     SkPoint pos[4];
247     r.toQuad(pos);
248     SkColor colors[4] = { color, color, color, color };
249     return SkVertices::MakeCopy(SkVertices::kTriangleFan_VertexMode, 4,
250                                 pos, pos, colors);
251 }
252 
253 /*
254  *  drawAtlas and drawVertices have several things in common:
255  *  - can create compound "shaders", combining texture and colors
256  *      - these are combined via an explicit blendmode
257  *  - like drawImage, they only respect parts of the paint
258  *      - colorfilter, imagefilter, blendmode, alpha
259  *
260  *  This GM produces a series of pairs of images (atlas | vertices).
261  *  Each pair should look the same, and each set shows a different combination
262  *  of alpha | colorFilter | mode
263  */
264 DEF_SIMPLE_GM(compare_atlas_vertices, canvas, 560, 585) {
265     const SkRect tex = SkRect::MakeWH(128, 128);
266     const SkRSXform xform = SkRSXform::Make(1, 0, 0, 0);
267     const SkColor color = 0x884488CC;
268 
269     auto image = GetResourceAsImage("images/mandrill_128.png");
270     auto verts = make_vertices(image, tex, color);
271     const sk_sp<SkColorFilter> filters[] = {
272         nullptr,
273         SkColorFilter::MakeModeFilter(0xFF00FF88, SkBlendMode::kModulate),
274     };
275     const SkBlendMode modes[] = {
276         SkBlendMode::kSrcOver,
277         SkBlendMode::kPlus,
278     };
279 
280     canvas->translate(10, 10);
281     SkPaint paint;
282     for (SkBlendMode mode : modes) {
283         for (int alpha : { 0xFF, 0x7F }) {
284             paint.setAlpha(alpha);
285             canvas->save();
286             for (auto cf : filters) {
287                 paint.setColorFilter(cf);
288                 canvas->drawAtlas(image, &xform, &tex, &color, 1,
289                                   mode, &tex, &paint);
290                 canvas->translate(128, 0);
291                 paint.setShader(image->makeShader());
292                 canvas->drawVertices(verts, mode, paint);
293                 paint.setShader(nullptr);
294                 canvas->translate(145, 0);
295             }
296             canvas->restore();
297             canvas->translate(0, 145);
298         }
299     }
300 }
301