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  */
17 static const SkScalar kTextHeight = 14.0f;
18 static const char kText[] = "Proportional Hamburgefons #% fi";
19 
20 namespace skiagm {
21 
22 class GlyphPosGM : public GM {
23 public:
GlyphPosGM(SkScalar strokeWidth,SkPaint::Style strokeStyle)24     GlyphPosGM(SkScalar strokeWidth, SkPaint::Style strokeStyle)
25         : fStrokeWidth(strokeWidth)
26         , fStrokeStyle(strokeStyle) {
27         }
28 
29 protected:
30 
onShortName()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 
onISize()48     SkISize onISize() override { return SkISize::Make(800, 600); }
49 
onDraw(SkCanvas * canvas)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 
drawTestCase(SkCanvas * canvas,SkScalar textScale)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 
160 private:
161     SkAutoTUnref<SkTypeface> fProp;
162     SkScalar fStrokeWidth;
163     SkPaint::Style fStrokeStyle;
164 
165     typedef GM INHERITED;
166 };
167 
168 //////////////////////////////////////////////////////////////////////////////
169 
GlyphPosHairlineStrokeAndFillFactory(void *)170 static GM* GlyphPosHairlineStrokeAndFillFactory(void*) {
171     return new GlyphPosGM(0.0f, SkPaint::kStrokeAndFill_Style);
172 }
GlyphPosStrokeAndFillFactory(void *)173 static GM* GlyphPosStrokeAndFillFactory(void*) {
174     return new GlyphPosGM(1.2f, SkPaint::kStrokeAndFill_Style);
175 }
GlyphPosHairlineStrokeFactory(void *)176 static GM* GlyphPosHairlineStrokeFactory(void*) {
177     return new GlyphPosGM(0.0f, SkPaint::kStroke_Style);
178 }
GlyphPosStrokeFactory(void *)179 static GM* GlyphPosStrokeFactory(void*) {
180     return new GlyphPosGM(1.2f, SkPaint::kStroke_Style);
181 }
GlyphPosHairlineFillFactory(void *)182 static GM* GlyphPosHairlineFillFactory(void*) {
183     return new GlyphPosGM(0.0f, SkPaint::kFill_Style);
184 }
GlyphPosFillFactory(void *)185 static GM* GlyphPosFillFactory(void*) {
186     return new GlyphPosGM(1.2f, SkPaint::kFill_Style);
187 }
188 
189 static GMRegistry reg1(GlyphPosHairlineStrokeAndFillFactory);
190 static GMRegistry reg2(GlyphPosStrokeAndFillFactory);
191 static GMRegistry reg3(GlyphPosHairlineStrokeFactory);
192 static GMRegistry reg4(GlyphPosStrokeFactory);
193 static GMRegistry reg5(GlyphPosHairlineFillFactory);
194 static GMRegistry reg6(GlyphPosFillFactory);
195 
196 
197 }
198