1/*
2 * Copyright 2011 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#include "sk_tool_utils.h"
10#include "Resources.h"
11#include "SkPath.h"
12#include "SkTypeface.h"
13
14class SkJSCanvas {
15public:
16    SkJSCanvas(SkCanvas* target);
17    ~SkJSCanvas();
18
19    void save();
20    void restore();
21
22    double lineWidth;
23    void setLineWidth(double);
24
25    void beginPath();
26    void moveTo(double x, double y);
27    void lineTo(double x, double y);
28    void closePath();
29
30    void fill();
31    void stroke();
32
33    void fillText(const char text[], double x, double y);
34
35private:
36    SkCanvas*   fTarget;
37    SkPaint     fFillPaint;
38    SkPaint     fStrokePaint;
39    SkPath      fPath;
40};
41
42SkJSCanvas::SkJSCanvas(SkCanvas* target) : fTarget(target) {
43    fFillPaint.setAntiAlias(true);
44    sk_tool_utils::set_portable_typeface(&fFillPaint);
45    fStrokePaint.setAntiAlias(true);
46    fStrokePaint.setStyle(SkPaint::kStroke_Style);
47    fStrokePaint.setStrokeWidth(SK_Scalar1);
48}
49
50SkJSCanvas::~SkJSCanvas() {}
51
52void SkJSCanvas::save() { fTarget->save(); }
53void SkJSCanvas::restore() { fTarget->restore(); }
54
55void SkJSCanvas::beginPath() { fPath.reset(); }
56void SkJSCanvas::moveTo(double x, double y) {
57    fPath.moveTo(SkDoubleToScalar(x), SkDoubleToScalar(y));
58}
59
60void SkJSCanvas::lineTo(double x, double y) {
61    fPath.lineTo(SkDoubleToScalar(x), SkDoubleToScalar(y));
62}
63
64void SkJSCanvas::closePath() { fPath.close(); }
65
66void SkJSCanvas::fill() {
67    fTarget->drawPath(fPath, fFillPaint);
68}
69
70void SkJSCanvas::stroke() {
71    fStrokePaint.setStrokeWidth(SkDoubleToScalar(lineWidth));
72    fTarget->drawPath(fPath, fStrokePaint);
73}
74
75void SkJSCanvas::fillText(const char text[], double x, double y) {
76    fTarget->drawString(text, SkDoubleToScalar(x), SkDoubleToScalar(y), fFillPaint);
77}
78
79///////////////////////////////////////////////////////////////////////////////
80
81static void dump(const SkPath& path) {
82    const SkRect& r = path.getBounds();
83    SkDebugf("isEmpty %d, bounds [%g %g %g %g]\n", path.isEmpty(),
84             r.fLeft, r.fTop, r.fRight, r.fBottom);
85}
86
87static void test_stroke(SkCanvas* canvas) {
88    if (true) {
89        SkPath path;
90        dump(path);
91        path.reset(); path.moveTo(0, 0);
92        dump(path);
93        path.reset(); path.moveTo(100, 100);
94        dump(path);
95        path.reset(); path.moveTo(0, 0); path.moveTo(100, 100);
96        dump(path);
97        path.reset(); path.moveTo(0, 0); path.lineTo(100, 100);
98        dump(path);
99        path.reset(); path.moveTo(0, 0); path.lineTo(100, 100); path.moveTo(200, 200);
100        dump(path);
101    }
102
103#if 0
104    // TEST 1 - The rectangle as it's expected to look
105    var canvas = document.createElement('canvas');
106    document.body.appendChild(canvas);
107    var ctx = canvas.getContext("2d");
108#else
109    SkJSCanvas ctx(canvas);
110#endif
111
112    ctx.save();
113    ctx.lineWidth = 2;
114    ctx.beginPath();
115    ctx.moveTo(10, 100);
116    ctx.lineTo(150, 100);
117    ctx.lineTo(150, 15);
118    ctx.lineTo(10, 15);
119    ctx.closePath();
120
121    // no extra moveTo here
122    // ctx.moveTo(175, 125);
123
124    ctx.stroke();
125    ctx.restore();
126
127    ctx.fillText("As Expected", 10, 10);
128
129#if 0
130    // TEST 2 - Includes an extra moveTo call before stroke; the rectangle appears larger
131    canvas = document.createElement('canvas');
132    document.body.appendChild(canvas);
133    ctx = canvas.getContext("2d");
134#else
135    canvas->translate(200, 0);
136#endif
137
138    ctx.save();
139    ctx.lineWidth = 2;
140    ctx.beginPath();
141    ctx.moveTo(10, 100);
142    ctx.lineTo(150, 100);
143    ctx.lineTo(150, 15);
144    ctx.lineTo(10, 15);
145    ctx.closePath();
146
147    ctx.moveTo(175, 125);
148
149    ctx.stroke();
150    ctx.restore();
151
152    ctx.fillText("Larger Rectangle", 10, 10);
153
154#if 0
155    // TEST 3 - Identical to test 2 except the line width is 1
156    canvas = document.createElement('canvas');
157    document.body.appendChild(canvas);
158    ctx = canvas.getContext("2d");
159#else
160    canvas->translate(200, 0);
161#endif
162
163    ctx.save();
164    ctx.lineWidth = 1;
165    ctx.beginPath();
166    ctx.moveTo(10, 100);
167    ctx.lineTo(150, 100);
168    ctx.lineTo(150, 15);
169    ctx.lineTo(10, 15);
170    ctx.closePath();
171
172    ctx.moveTo(175, 125);
173
174    ctx.stroke();
175    ctx.restore();
176
177    ctx.fillText("As Expected - line width 1", 10, 10);
178}
179
180class Poly2PolyGM : public skiagm::GM {
181public:
182    Poly2PolyGM() {}
183
184protected:
185
186    SkString onShortName() override {
187        return SkString("poly2poly");
188    }
189
190    SkISize onISize() override {
191        return SkISize::Make(835, 840);
192    }
193
194    static void doDraw(SkCanvas* canvas, SkPaint* paint, const int isrc[],
195                       const int idst[], int count) {
196        SkMatrix matrix;
197        SkPoint src[4], dst[4];
198
199        for (int i = 0; i < count; i++) {
200            src[i].set(SkIntToScalar(isrc[2*i+0]), SkIntToScalar(isrc[2*i+1]));
201            dst[i].set(SkIntToScalar(idst[2*i+0]), SkIntToScalar(idst[2*i+1]));
202        }
203
204        canvas->save();
205        matrix.setPolyToPoly(src, dst, count);
206        canvas->concat(matrix);
207
208        paint->setColor(sk_tool_utils::color_to_565(SK_ColorGRAY));
209        paint->setStyle(SkPaint::kStroke_Style);
210        const SkScalar D = 64;
211        canvas->drawRect(SkRect::MakeWH(D, D), *paint);
212        canvas->drawLine(0, 0, D, D, *paint);
213        canvas->drawLine(0, D, D, 0, *paint);
214
215        SkPaint::FontMetrics fm;
216        paint->getFontMetrics(&fm);
217        paint->setColor(SK_ColorRED);
218        paint->setStyle(SkPaint::kFill_Style);
219        SkScalar x = D/2;
220        SkScalar y = D/2 - (fm.fAscent + fm.fDescent)/2;
221        uint16_t glyphID = 3; // X
222        canvas->drawText((void*) &glyphID, sizeof(glyphID), x, y, *paint);
223        canvas->restore();
224    }
225
226    void onOnceBeforeDraw() override {
227        fEmFace = MakeResourceAsTypeface("fonts/Em.ttf");
228    }
229
230    void onDraw(SkCanvas* canvas) override {
231        if (false) { test_stroke(canvas); return; }
232
233        SkPaint paint;
234        paint.setAntiAlias(true);
235        paint.setTypeface(fEmFace);
236        paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
237        paint.setStrokeWidth(SkIntToScalar(4));
238        paint.setTextSize(SkIntToScalar(40));
239        paint.setTextAlign(SkPaint::kCenter_Align);
240
241        canvas->save();
242        canvas->translate(SkIntToScalar(10), SkIntToScalar(10));
243        // translate (1 point)
244        const int src1[] = { 0, 0 };
245        const int dst1[] = { 5, 5 };
246        doDraw(canvas, &paint, src1, dst1, 1);
247        canvas->restore();
248
249        canvas->save();
250        canvas->translate(SkIntToScalar(160), SkIntToScalar(10));
251        // rotate/uniform-scale (2 points)
252        const int src2[] = { 32, 32, 64, 32 };
253        const int dst2[] = { 32, 32, 64, 48 };
254        doDraw(canvas, &paint, src2, dst2, 2);
255        canvas->restore();
256
257        canvas->save();
258        canvas->translate(SkIntToScalar(10), SkIntToScalar(110));
259        // rotate/skew (3 points)
260        const int src3[] = { 0, 0, 64, 0, 0, 64 };
261        const int dst3[] = { 0, 0, 96, 0, 24, 64 };
262        doDraw(canvas, &paint, src3, dst3, 3);
263        canvas->restore();
264
265        canvas->save();
266        canvas->translate(SkIntToScalar(160), SkIntToScalar(110));
267        // perspective (4 points)
268        const int src4[] = { 0, 0, 64, 0, 64, 64, 0, 64 };
269        const int dst4[] = { 0, 0, 96, 0, 64, 96, 0, 64 };
270        doDraw(canvas, &paint, src4, dst4, 4);
271        canvas->restore();
272    }
273
274private:
275    typedef skiagm::GM INHERITED;
276    sk_sp<SkTypeface> fEmFace;
277};
278
279//////////////////////////////////////////////////////////////////////////////
280
281DEF_GM( return new Poly2PolyGM; )
282