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/gm.h"
9 #include "include/core/SkCanvas.h"
10 #include "include/core/SkColor.h"
11 #include "include/core/SkFont.h"
12 #include "include/core/SkFontTypes.h"
13 #include "include/core/SkMatrix.h"
14 #include "include/core/SkPaint.h"
15 #include "include/core/SkRect.h"
16 #include "include/core/SkScalar.h"
17 #include "include/core/SkTypeface.h"
18 #include "include/core/SkTypes.h"
19 #include "tools/ToolUtils.h"
20 #include "tools/fonts/FontToolUtils.h"
21
22 /* This test tries to define the effect of using hairline strokes on text.
23 * Provides non-hairline images for reference and consistency checks.
24 * glyph_pos_(h/n)_(s/f/b)
25 * -> test hairline/non-hairline stroke/fill/stroke+fill.
26 */
27 constexpr SkScalar kTextHeight = 14.0f;
28 constexpr char kText[] = "Proportional Hamburgefons #% fi";
29
30 static void drawTestCase(SkCanvas* canvas,
31 SkScalar textScale,
32 SkScalar strokeWidth,
33 SkPaint::Style strokeStyle);
34
draw_gm(SkCanvas * canvas,SkScalar strokeWidth,SkPaint::Style strokeStyle)35 static void draw_gm(SkCanvas* canvas,
36 SkScalar strokeWidth,
37 SkPaint::Style strokeStyle) {
38 // There's a black pixel at 40, 40 for reference.
39 canvas->drawPoint(40, 40, SkPaint());
40
41 // Two reference images.
42 canvas->translate(50.0f, 50.0f);
43 drawTestCase(canvas, 1.0f, strokeWidth, strokeStyle);
44
45 canvas->translate(0.0f, 50.0f);
46 drawTestCase(canvas, 3.0f, strokeWidth, strokeStyle);
47
48 // Uniform scaling test.
49 canvas->translate(0.0f, 100.0f);
50 canvas->save();
51 canvas->scale(3.0f, 3.0f);
52 drawTestCase(canvas, 1.0f, strokeWidth, strokeStyle);
53 canvas->restore();
54
55 // Non-uniform scaling test.
56 canvas->translate(0.0f, 100.0f);
57 canvas->save();
58 canvas->scale(3.0f, 6.0f);
59 drawTestCase(canvas, 1.0f, strokeWidth, strokeStyle);
60 canvas->restore();
61
62 // Skew test.
63 canvas->translate(0.0f, 80.0f);
64 canvas->save();
65 canvas->scale(3.0f, 3.0f);
66 SkMatrix skew;
67 skew.setIdentity();
68 skew.setSkewX(8.0f / 25.0f);
69 skew.setSkewY(2.0f / 25.0f);
70 canvas->concat(skew);
71 drawTestCase(canvas, 1.0f, strokeWidth, strokeStyle);
72 canvas->restore();
73
74 // Perspective test.
75 canvas->translate(0.0f, 80.0f);
76 canvas->save();
77 SkMatrix perspective;
78 perspective.setIdentity();
79 perspective.setPerspX(-SkScalarInvert(340));
80 perspective.setSkewX(8.0f / 25.0f);
81 perspective.setSkewY(2.0f / 25.0f);
82
83 canvas->concat(perspective);
84 drawTestCase(canvas, 1.0f, strokeWidth, strokeStyle);
85 canvas->restore();
86 }
87
drawTestCase(SkCanvas * canvas,SkScalar textScale,SkScalar strokeWidth,SkPaint::Style strokeStyle)88 static void drawTestCase(SkCanvas* canvas,
89 SkScalar textScale,
90 SkScalar strokeWidth,
91 SkPaint::Style strokeStyle) {
92 SkPaint paint;
93 paint.setColor(SK_ColorBLACK);
94 paint.setAntiAlias(true);
95 paint.setStrokeWidth(strokeWidth);
96 paint.setStyle(strokeStyle);
97
98 SkFont font(ToolUtils::DefaultPortableTypeface(), kTextHeight * textScale);
99
100 // This demonstrates that we can not measure the text if
101 // there's a device transform. The canvas total matrix will
102 // end up being a device transform.
103 bool drawRef = !(canvas->getLocalToDeviceAs3x3().getType() &
104 ~(SkMatrix::kIdentity_Mask | SkMatrix::kTranslate_Mask));
105
106 SkRect bounds;
107 if (drawRef) {
108 SkScalar advance = font.measureText(kText, sizeof(kText) - 1, SkTextEncoding::kUTF8,
109 &bounds, &paint);
110
111 paint.setStrokeWidth(0.0f);
112 paint.setStyle(SkPaint::kStroke_Style);
113
114 // Green box is the measured text bounds.
115 paint.setColor(SK_ColorGREEN);
116 canvas->drawRect(bounds, paint);
117
118 // Red line is the measured advance from the 0,0 of the text position.
119 paint.setColor(SK_ColorRED);
120 canvas->drawLine(0.0f, 0.0f, advance, 0.0f, paint);
121 }
122
123 // Black text is the testcase, eg. the text.
124 paint.setColor(SK_ColorBLACK);
125 paint.setStrokeWidth(strokeWidth);
126 paint.setStyle(strokeStyle);
127 canvas->drawSimpleText(kText, sizeof(kText) - 1, SkTextEncoding::kUTF8,
128 0.0f, 0.0f, font, paint);
129
130 if (drawRef) {
131 const size_t len = sizeof(kText) - 1;
132 SkGlyphID glyphs[len];
133 const int count = font.textToGlyphs(kText, len, SkTextEncoding::kUTF8, glyphs, len);
134 SkScalar widths[len]; // len is conservative. we really only need 'count'
135 font.getWidthsBounds(glyphs, count, widths, nullptr, &paint);
136
137 paint.setStrokeWidth(0.0f);
138 paint.setStyle(SkPaint::kStroke_Style);
139
140 // Magenta lines are the positions for the characters.
141 paint.setColor(SK_ColorMAGENTA);
142 SkScalar w = 0;
143 for (size_t i = 0; i < sizeof(kText) - 1; ++i) {
144 canvas->drawLine(w, 0.0f, w, 5.0f, paint);
145 w += widths[i];
146 }
147 }
148 }
149
150 DEF_SIMPLE_GM(glyph_pos_h_b, c, 800, 600) {
151 draw_gm(c, 0.0f, SkPaint::kStrokeAndFill_Style);
152 }
153 DEF_SIMPLE_GM(glyph_pos_n_b, c, 800, 600) {
154 draw_gm(c, 1.2f, SkPaint::kStrokeAndFill_Style);
155 }
156 DEF_SIMPLE_GM(glyph_pos_h_s, c, 800, 600) {
157 draw_gm(c, 0.0f, SkPaint::kStroke_Style);
158 }
159 DEF_SIMPLE_GM(glyph_pos_n_s, c, 800, 600) {
160 draw_gm(c, 1.2f, SkPaint::kStroke_Style);
161 }
162 DEF_SIMPLE_GM(glyph_pos_h_f, c, 800, 600) {
163 draw_gm(c, 0.0f, SkPaint::kFill_Style);
164 }
165 DEF_SIMPLE_GM(glyph_pos_n_f, c, 800, 600) {
166 draw_gm(c, 1.2f, SkPaint::kFill_Style);
167 }
168