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