1
2/*
3 * Copyright 2012 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8#include "gm.h"
9
10#include "SkColorPriv.h"
11#include "SkGeometry.h"
12#include "SkShader.h"
13
14#define WIRE_FRAME_WIDTH    1.1f
15
16static void tesselate(const SkPath& src, SkPath* dst) {
17    SkPath::Iter iter(src, true);
18    SkPoint pts[4];
19    SkPath::Verb verb;
20    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
21        switch (verb) {
22            case SkPath::kMove_Verb:
23                dst->moveTo(pts[0]);
24                break;
25            case SkPath::kLine_Verb:
26                dst->lineTo(pts[1]);
27                break;
28            case SkPath::kQuad_Verb: {
29                SkPoint p;
30                for (int i = 1; i <= 8; ++i) {
31                    SkEvalQuadAt(pts, i / 8.0f, &p, NULL);
32                    dst->lineTo(p);
33                }
34            } break;
35            case SkPath::kCubic_Verb: {
36                SkPoint p;
37                for (int i = 1; i <= 8; ++i) {
38                    SkEvalCubicAt(pts, i / 8.0f, &p, NULL, NULL);
39                    dst->lineTo(p);
40                }
41            } break;
42        }
43    }
44}
45
46static void setFade(SkPaint* paint, bool showGL) {
47    paint->setAlpha(showGL ? 0x66 : 0xFF);
48}
49
50static void setGLFrame(SkPaint* paint) {
51    paint->setColor(0xFFFF0000);
52    paint->setStyle(SkPaint::kStroke_Style);
53    paint->setAntiAlias(true);
54    paint->setStrokeWidth(WIRE_FRAME_WIDTH);
55}
56
57static void show_mesh(SkCanvas* canvas, const SkRect& r) {
58    SkPaint paint;
59    setGLFrame(&paint);
60
61    canvas->drawRect(r, paint);
62    canvas->drawLine(r.fLeft, r.fTop, r.fRight, r.fBottom, paint);
63}
64
65static void drawLine(SkCanvas* canvas, const SkPoint& p0, const SkPoint& p1,
66                     const SkPaint& paint) {
67    canvas->drawLine(p0.fX, p0.fY, p1.fX, p1.fY, paint);
68}
69
70static void show_mesh(SkCanvas* canvas, const SkPoint pts[],
71                      const uint16_t indices[], int count) {
72    SkPaint paint;
73    setGLFrame(&paint);
74
75    for (int i = 0; i < count - 2; ++i) {
76        drawLine(canvas, pts[indices[i]], pts[indices[i+1]], paint);
77        drawLine(canvas, pts[indices[i+1]], pts[indices[i+2]], paint);
78        drawLine(canvas, pts[indices[i+2]], pts[indices[i]], paint);
79    }
80}
81
82static void show_glframe(SkCanvas* canvas, const SkPath& path) {
83    SkPaint paint;
84    setGLFrame(&paint);
85    canvas->drawPath(path, paint);
86}
87
88static void show_mesh_between(SkCanvas* canvas, const SkPath& p0, const SkPath& p1) {
89    SkPath d0, d1;
90    tesselate(p0, &d0);
91    tesselate(p1, &d1);
92
93    SkPoint pts0[256*2], pts1[256];
94    int count = d0.getPoints(pts0, SK_ARRAY_COUNT(pts0));
95    int count1 = d1.getPoints(pts1, SK_ARRAY_COUNT(pts1));
96    SkASSERT(count == count1);
97    memcpy(&pts0[count], pts1, count * sizeof(SkPoint));
98
99    uint16_t indices[256*6];
100    uint16_t* ndx = indices;
101    for (int i = 0; i < count; ++i) {
102        *ndx++ = i;
103        *ndx++ = i + count;
104    }
105    *ndx++ = 0;
106
107    show_mesh(canvas, pts0, indices, ndx - indices);
108}
109
110static void show_fan(SkCanvas* canvas, const SkPath& path, SkScalar cx, SkScalar cy) {
111    SkPaint paint;
112    setGLFrame(&paint);
113
114    canvas->drawPath(path, paint);
115
116    SkPoint pts[256];
117    int count = path.getPoints(pts, SK_ARRAY_COUNT(pts));
118    for (int i = 0; i < count; ++i) {
119        canvas->drawLine(pts[i].fX, pts[i].fY, cx, cy, paint);
120    }
121}
122
123///////////////////////////////////////////////////////////////////////////////
124
125typedef void (*DrawProc)(SkCanvas* canvas, bool showGL, int flags);
126
127static void draw_line(SkCanvas* canvas, bool showGL, int flags) {
128    SkPaint paint;
129    paint.setAntiAlias(true);
130
131    if (showGL) {
132        setGLFrame(&paint);
133    }
134    canvas->drawLine(50, 50, 400, 100, paint);
135    paint.setColor(SK_ColorBLACK);
136
137    canvas->rotate(40);
138    setFade(&paint, showGL);
139    paint.setStrokeWidth(40);
140    canvas->drawLine(100, 50, 450, 50, paint);
141    if (showGL) {
142        show_mesh(canvas, SkRect::MakeLTRB(100, 50-20, 450, 50+20));
143    }
144}
145
146static void draw_rect(SkCanvas* canvas, bool showGL, int flags) {
147    SkPaint paint;
148    paint.setAntiAlias(true);
149
150    SkRect r = SkRect::MakeLTRB(50, 70, 250, 370);
151
152    setFade(&paint, showGL);
153    canvas->drawRect(r, paint);
154    if (showGL) {
155        show_mesh(canvas, r);
156    }
157
158    canvas->translate(320, 0);
159
160    paint.setStyle(SkPaint::kStroke_Style);
161    paint.setStrokeWidth(25);
162    canvas->drawRect(r, paint);
163    if (showGL) {
164        SkScalar rad = paint.getStrokeWidth() / 2;
165        SkPoint pts[8];
166        r.outset(rad, rad);
167        r.toQuad(&pts[0]);
168        r.inset(rad*2, rad*2);
169        r.toQuad(&pts[4]);
170
171        const uint16_t indices[] = {
172            0, 4, 1, 5, 2, 6, 3, 7, 0, 4
173        };
174        show_mesh(canvas, pts, indices, SK_ARRAY_COUNT(indices));
175    }
176}
177
178static void draw_oval(SkCanvas* canvas, bool showGL, int flags) {
179    SkPaint paint;
180    paint.setAntiAlias(true);
181
182    SkRect r = SkRect::MakeLTRB(50, 70, 250, 370);
183
184    setFade(&paint, showGL);
185    canvas->drawOval(r, paint);
186    if (showGL) {
187        switch (flags) {
188            case 0: {
189                SkPath path;
190                path.addOval(r);
191                show_glframe(canvas, path);
192            } break;
193            case 1:
194            case 3: {
195                SkPath src, dst;
196                src.addOval(r);
197                tesselate(src, &dst);
198                show_fan(canvas, dst, r.centerX(), r.centerY());
199            } break;
200            case 2: {
201                SkPaint p(paint);
202                show_mesh(canvas, r);
203                setGLFrame(&p);
204                paint.setStyle(SkPaint::kFill_Style);
205                canvas->drawCircle(r.centerX(), r.centerY(), 3, p);
206            } break;
207        }
208    }
209
210    canvas->translate(320, 0);
211
212    paint.setStyle(SkPaint::kStroke_Style);
213    paint.setStrokeWidth(25);
214    canvas->drawOval(r, paint);
215    if (showGL) {
216        switch (flags) {
217            case 0: {
218                SkPath path;
219                SkScalar rad = paint.getStrokeWidth() / 2;
220                r.outset(rad, rad);
221                path.addOval(r);
222                r.inset(rad*2, rad*2);
223                path.addOval(r);
224                show_glframe(canvas, path);
225            } break;
226            case 1: {
227                SkPath path0, path1;
228                SkScalar rad = paint.getStrokeWidth() / 2;
229                r.outset(rad, rad);
230                path0.addOval(r);
231                r.inset(rad*2, rad*2);
232                path1.addOval(r);
233                show_mesh_between(canvas, path0, path1);
234            } break;
235            case 2: {
236                SkPath path;
237                path.addOval(r);
238                show_glframe(canvas, path);
239                SkScalar rad = paint.getStrokeWidth() / 2;
240                r.outset(rad, rad);
241                show_mesh(canvas, r);
242            } break;
243            case 3: {
244                SkScalar rad = paint.getStrokeWidth() / 2;
245                r.outset(rad, rad);
246                SkPaint paint;
247                paint.setAlpha(0x33);
248                canvas->drawRect(r, paint);
249                show_mesh(canvas, r);
250            } break;
251        }
252    }
253}
254
255#include "SkImageDecoder.h"
256
257static void draw_image(SkCanvas* canvas, bool showGL, int flags) {
258    SkPaint paint;
259    paint.setAntiAlias(true);
260    paint.setFilterBitmap(true);
261    setFade(&paint, showGL);
262
263    static SkBitmap* gBM;
264    if (NULL == gBM) {
265        gBM = new SkBitmap;
266        SkImageDecoder::DecodeFile("/skimages/startrek.png", gBM);
267    }
268    SkRect r = SkRect::MakeWH(gBM->width(), gBM->height());
269
270    canvas->save();
271    canvas->translate(30, 30);
272    canvas->scale(0.8f, 0.8f);
273    canvas->drawBitmap(*gBM, 0, 0, &paint);
274    if (showGL) {
275        show_mesh(canvas, r);
276    }
277    canvas->restore();
278
279    canvas->translate(210, 290);
280    canvas->rotate(-35);
281    canvas->drawBitmap(*gBM, 0, 0, &paint);
282    if (showGL) {
283        show_mesh(canvas, r);
284    }
285}
286
287static void draw_text(SkCanvas* canvas, bool showGL, int flags) {
288    SkPaint paint;
289    paint.setAntiAlias(true);
290    paint.setLCDRenderText(true);
291    const char text[] = "Graphics at Google";
292    size_t len = strlen(text);
293    setFade(&paint, showGL);
294
295    canvas->translate(40, 50);
296    for (int i = 0; i < 10; ++i) {
297        paint.setTextSize(12 + i * 3);
298        canvas->drawText(text, len, 0, 0, paint);
299        if (showGL) {
300            SkRect bounds[256];
301            SkScalar widths[256];
302            int count = paint.getTextWidths(text, len, widths, bounds);
303            SkScalar adv = 0;
304            for (int j = 0; j < count; ++j) {
305                bounds[j].offset(adv, 0);
306                show_mesh(canvas, bounds[j]);
307                adv += widths[j];
308            }
309        }
310        canvas->translate(0, paint.getTextSize() * 3 / 2);
311    }
312}
313
314static const struct {
315    DrawProc    fProc;
316    const char* fName;
317} gRec[] = {
318    {   draw_line,  "Lines" },
319    {   draw_rect,  "Rects" },
320    {   draw_oval,  "Ovals" },
321    {   draw_image, "Images" },
322    {   draw_text,  "Text" },
323};
324
325class TalkGM : public skiagm::GM {
326    DrawProc fProc;
327    SkString fName;
328    bool     fShowGL;
329    int      fFlags;
330
331public:
332    TalkGM(int index, bool showGL, int flags = 0) {
333        fProc = gRec[index].fProc;
334        fName.set(gRec[index].fName);
335        if (showGL) {
336            fName.append("-gl");
337        }
338        fShowGL = showGL;
339        fFlags = flags;
340    }
341
342protected:
343    virtual SkString onShortName() {
344        return fName;
345    }
346
347    virtual SkISize onISize() {
348        return SkISize::Make(640, 480);
349    }
350
351    virtual void onDraw(SkCanvas* canvas) {
352        SkISize size = canvas->getDeviceSize();
353        SkRect dst = SkRect::MakeWH(size.width(), size.height());
354        SkRect src = SkRect::MakeWH(640, 480);
355        SkMatrix matrix;
356        matrix.setRectToRect(src, dst, SkMatrix::kCenter_ScaleToFit);
357
358        canvas->concat(matrix);
359        fProc(canvas, fShowGL, fFlags);
360    }
361
362    virtual uint32_t onGetFlags() const SK_OVERRIDE {
363        return  kSkipPDF_Flag | kSkipPicture_Flag | kSkipPipe_Flag | kSkipTiled_Flag;
364    }
365
366private:
367    typedef skiagm::GM INHERITED;
368};
369
370//////////////////////////////////////////////////////////////////////////////
371
372#define GM_CONCAT(X,Y) GM_CONCAT_IMPL(X,Y)
373#define GM_CONCAT_IMPL(X,Y) X##Y
374
375#define FACTORY_NAME  GM_CONCAT(Factory, __LINE__)
376#define REGISTRY_NAME  GM_CONCAT(gReg, __LINE__)
377
378#define ADD_GM(Class, args)    \
379    static skiagm::GM* FACTORY_NAME(void*) { return new Class args; } \
380    static skiagm::GMRegistry REGISTRY_NAME(FACTORY_NAME);
381
382ADD_GM(TalkGM, (0, false))
383ADD_GM(TalkGM, (0, true))
384ADD_GM(TalkGM, (1, false))
385ADD_GM(TalkGM, (1, true))
386ADD_GM(TalkGM, (2, false))
387ADD_GM(TalkGM, (2, true))
388ADD_GM(TalkGM, (2, true, 1))
389ADD_GM(TalkGM, (2, true, 2))
390ADD_GM(TalkGM, (2, true, 3))
391ADD_GM(TalkGM, (3, false))
392ADD_GM(TalkGM, (3, true))
393ADD_GM(TalkGM, (4, false))
394ADD_GM(TalkGM, (4, true))
395
396//static GM* MyFactory(void*) { return new TalkGM(0, false); }
397//static GMRegistry reg(MyFactory);
398