1/*
2 * Copyright 2014 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 "SkCanvas.h"
10#include "SkTypeface.h"
11
12/* This test tries to define the effect of using hairline strokes on text.
13 * Provides non-hairline images for reference and consistency checks.
14 * glyph_pos_(h/n)_(s/f/b)
15 *   -> test hairline/non-hairline stroke/fill/stroke+fill.
16 */
17static const SkScalar kTextHeight = 14.0f;
18static const char kText[] = "Proportional Hamburgefons #% fi";
19
20namespace skiagm {
21
22class GlyphPosGM : public GM {
23public:
24    GlyphPosGM(SkScalar strokeWidth, SkPaint::Style strokeStyle)
25        : fStrokeWidth(strokeWidth)
26        , fStrokeStyle(strokeStyle) {
27        }
28
29protected:
30
31    SkString onShortName() override {
32        SkString str("glyph_pos");
33        if (fStrokeWidth == 0.0f) {
34            str.append("_h"); // h == Hairline.
35        } else {
36            str.append("_n"); // n == Normal.
37        }
38        if (fStrokeStyle == SkPaint::kStroke_Style) {
39            str.append("_s");
40        } else if (fStrokeStyle == SkPaint::kFill_Style) {
41            str.append("_f");
42        } else {
43            str.append("_b"); // b == Both.
44        }
45        return str;
46    }
47
48    SkISize onISize() override { return SkISize::Make(800, 600); }
49
50    void onDraw(SkCanvas* canvas) override {
51        if (!fProp) {
52            fProp.reset(sk_tool_utils::create_portable_typeface("Helvetica", SkTypeface::kNormal));
53        }
54
55        // There's a black pixel at 40, 40 for reference.
56        canvas->drawPoint(40.0f, 40.0f, SK_ColorBLACK);
57
58        // Two reference images.
59        canvas->translate(50.0f, 50.0f);
60        drawTestCase(canvas, 1.0f);
61
62        canvas->translate(0.0f, 50.0f);
63        drawTestCase(canvas, 3.0f);
64
65        // Uniform scaling test.
66        canvas->translate(0.0f, 100.0f);
67        canvas->save();
68        canvas->scale(3.0f, 3.0f);
69        drawTestCase(canvas, 1.0f);
70        canvas->restore();
71
72        // Non-uniform scaling test.
73        canvas->translate(0.0f, 100.0f);
74        canvas->save();
75        canvas->scale(3.0f, 6.0f);
76        drawTestCase(canvas, 1.0f);
77        canvas->restore();
78
79        // Skew test.
80        canvas->translate(0.0f, 80.0f);
81        canvas->save();
82        canvas->scale(3.0f, 3.0f);
83        SkMatrix skew;
84        skew.setIdentity();
85        skew.setSkewX(8.0f / 25.0f);
86        skew.setSkewY(2.0f / 25.0f);
87        canvas->concat(skew);
88        drawTestCase(canvas, 1.0f);
89        canvas->restore();
90
91        // Perspective test.
92        canvas->translate(0.0f, 80.0f);
93        canvas->save();
94        SkMatrix perspective;
95        perspective.setIdentity();
96        perspective.setPerspX(-SkScalarInvert(340));
97        perspective.setSkewX(8.0f / 25.0f);
98        perspective.setSkewY(2.0f / 25.0f);
99
100
101        canvas->concat(perspective);
102        drawTestCase(canvas, 1.0f);
103        canvas->restore();
104    }
105
106    void drawTestCase(SkCanvas* canvas, SkScalar textScale) {
107        SkPaint paint;
108        paint.setColor(SK_ColorBLACK);
109        paint.setAntiAlias(true);
110        paint.setTextSize(kTextHeight * textScale);
111        paint.setTypeface(fProp);
112        paint.setDevKernText(true);
113        paint.setStrokeWidth(fStrokeWidth);
114        paint.setStyle(fStrokeStyle);
115
116        // This demonstrates that we can not measure the text if there's a device transform. The
117        // canvas total matrix will end up being a device transform.
118        bool drawRef = !(canvas->getTotalMatrix().getType() &
119                         ~(SkMatrix::kIdentity_Mask | SkMatrix::kTranslate_Mask));
120
121        SkRect bounds;
122        if (drawRef) {
123            SkScalar advance = paint.measureText(kText, sizeof(kText) - 1, &bounds);
124
125            paint.setStrokeWidth(0.0f);
126            paint.setStyle(SkPaint::kStroke_Style);
127
128            // Green box is the measured text bounds.
129            paint.setColor(SK_ColorGREEN);
130            canvas->drawRect(bounds, paint);
131
132            // Red line is the measured advance from the 0,0 of the text position.
133            paint.setColor(SK_ColorRED);
134            canvas->drawLine(0.0f, 0.0f, advance, 0.0f, paint);
135        }
136
137        // Black text is the testcase, eg. the text.
138        paint.setColor(SK_ColorBLACK);
139        paint.setStrokeWidth(fStrokeWidth);
140        paint.setStyle(fStrokeStyle);
141        canvas->drawText(kText, sizeof(kText) - 1, 0.0f, 0.0f, paint);
142
143        if (drawRef) {
144            SkScalar widths[sizeof(kText) - 1];
145            paint.getTextWidths(kText, sizeof(kText) - 1, widths, NULL);
146
147            paint.setStrokeWidth(0.0f);
148            paint.setStyle(SkPaint::kStroke_Style);
149
150            // Magenta lines are the positions for the characters.
151            paint.setColor(SK_ColorMAGENTA);
152            SkScalar w = bounds.x();
153            for (size_t i = 0; i < sizeof(kText) - 1; ++i) {
154                canvas->drawLine(w, 0.0f, w, 5.0f, paint);
155                w += widths[i];
156            }
157        }
158    }
159
160private:
161    SkAutoTUnref<SkTypeface> fProp;
162    SkScalar fStrokeWidth;
163    SkPaint::Style fStrokeStyle;
164
165    typedef GM INHERITED;
166};
167
168//////////////////////////////////////////////////////////////////////////////
169
170static GM* GlyphPosHairlineStrokeAndFillFactory(void*) {
171    return new GlyphPosGM(0.0f, SkPaint::kStrokeAndFill_Style);
172}
173static GM* GlyphPosStrokeAndFillFactory(void*) {
174    return new GlyphPosGM(1.2f, SkPaint::kStrokeAndFill_Style);
175}
176static GM* GlyphPosHairlineStrokeFactory(void*) {
177    return new GlyphPosGM(0.0f, SkPaint::kStroke_Style);
178}
179static GM* GlyphPosStrokeFactory(void*) {
180    return new GlyphPosGM(1.2f, SkPaint::kStroke_Style);
181}
182static GM* GlyphPosHairlineFillFactory(void*) {
183    return new GlyphPosGM(0.0f, SkPaint::kFill_Style);
184}
185static GM* GlyphPosFillFactory(void*) {
186    return new GlyphPosGM(1.2f, SkPaint::kFill_Style);
187}
188
189static GMRegistry reg1(GlyphPosHairlineStrokeAndFillFactory);
190static GMRegistry reg2(GlyphPosStrokeAndFillFactory);
191static GMRegistry reg3(GlyphPosHairlineStrokeFactory);
192static GMRegistry reg4(GlyphPosStrokeFactory);
193static GMRegistry reg5(GlyphPosHairlineFillFactory);
194static GMRegistry reg6(GlyphPosFillFactory);
195
196
197}
198