• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2024 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 #include "include/core/SkCanvas.h"
8 #include "include/core/SkContourMeasure.h"
9 #include "include/core/SkFont.h"
10 #include "include/core/SkFontMetrics.h"
11 #include "include/core/SkFontMgr.h"
12 #include "include/core/SkPath.h"
13 #include "include/core/SkRRect.h"
14 #include "include/core/SkScalar.h"
15 #include "include/core/SkTypeface.h"
16 #include "include/utils/SkNoDrawCanvas.h"
17 #include "src/base/SkRandom.h"
18 #include "tools/SkMetaData.h"
19 #include "tools/ToolUtils.h"
20 #include "tools/fonts/FontToolUtils.h"
21 #include "tools/viewer/Slide.h"
22 
23 namespace {
24 
25 class TypefaceSlide : public Slide {
26 public:
TypefaceSlide()27     TypefaceSlide() { fName = "Typeface Viewer"; }
28 
load(SkScalar w,SkScalar h)29     void load(SkScalar w, SkScalar h) override {
30         fWindowSize = {w, h};
31 
32         fBaseTypeface = ToolUtils::CreateTypefaceFromResource("fonts/Variable.ttf");
33         fCurrentTypeface = fBaseTypeface;
34         fVariationSliders = ToolUtils::VariationSliders(fCurrentTypeface.get(), fVariationPosition);
35 
36         fPathDirectionIndicator.reset();
37         fPathDirectionIndicator.moveTo(0, -3);
38         fPathDirectionIndicator.lineTo(3, 0);
39         fPathDirectionIndicator.lineTo(0, 3);
40         fPathDirectionIndicator.close();
41 
42         fPathDirectionIndicatorPaint.setColor(SK_ColorRED);
43         fPathDirectionIndicatorPaint.setStroke(true);
44         fPathDirectionIndicatorPaint.setStrokeWidth(0);
45     }
46 
resize(SkScalar w,SkScalar h)47     void resize(SkScalar w, SkScalar h) override { fWindowSize = {w, h}; }
48 
unload()49     void unload() override {
50         fBaseTypeface = nullptr;
51         fCurrentTypeface = nullptr;
52     }
53 
getDimensions() const54     SkISize getDimensions() const override {
55         if (fDrawArea.isEmpty()) {
56             TypefaceSlide& self = *const_cast<TypefaceSlide*>(this);
57             SkNoDrawCanvas noDraw(fWindowSize.width(), fWindowSize.height());
58             self.draw(&noDraw);
59         }
60         return fDrawArea;
61     }
62 
updateCurrentTypeface()63     void updateCurrentTypeface() {
64         if (!fBaseTypeface) {
65             return;
66         }
67         SkSpan<const SkFontArguments::VariationPosition::Coordinate> coords =
68                 fVariationSliders.getCoordinates();
69         SkFontArguments::VariationPosition varPos = {coords.data(),
70                                                      static_cast<int>(coords.size())};
71         SkFontArguments args;
72         args.setVariationDesignPosition(varPos);
73         fCurrentTypeface = fBaseTypeface->makeClone(args);
74         fCurrentTypefaceDirty = false;
75     }
76 
draw(SkCanvas * canvas)77     void draw(SkCanvas* canvas) override {
78         SkPaint notationPaint;
79         notationPaint.setColor(SK_ColorRED);
80         SkFont notationFont(ToolUtils::DefaultTypeface(), 8.0);
81 
82         SkPaint paint;
83         if (fOutline) {
84             paint.setStroke(true);
85             paint.setStrokeWidth(0);
86         }
87         if (fCurrentTypefaceDirty) {
88             this->updateCurrentTypeface();
89         }
90         SkFont font(fCurrentTypeface, fFontSize);
91         SkFontMetrics metrics;
92         font.getMetrics(&metrics);
93 
94         SkPoint origin{0, 0};
95         SkPoint position{0, 0};
96         SkRect drawBounds = SkRect::MakeXYWH(0, 0, 1, 1);
97         struct Line {
98             SkRect bounds;
99             SkGlyphID firstGlyph; // inclusive
100             SkGlyphID lastGlyph; // inclusive
101             int number;
102         } line{{}, fCurrentGlyphID, fCurrentGlyphID, 0};
103 
104         int numGlyphs = fCurrentTypeface->countGlyphs();
105         if (numGlyphs == 0) {
106             fDrawArea.setEmpty();
107             return;
108         }
109         SkGlyphID lastGlyph = numGlyphs - 1;
110         for (SkGlyphID glyph = line.firstGlyph; line.number < 2; ++glyph) {
111             // measure a line
112             SkRect beginLineGlyphBounds;
113             {
114                 SkRect newLineBounds = line.bounds;
115                 if (glyph != line.firstGlyph) {
116                     newLineBounds.fRight += 10;
117                 }
118                 SkScalar advance;
119                 SkRect glyphBounds;
120                 font.getWidths(&glyph, 1, &advance, &glyphBounds);
121                 SkRect advanceBounds = SkRect::MakeWH(advance, 1);
122                 SkRect glyphAndAdvanceBounds = glyphBounds;
123                 glyphAndAdvanceBounds.join(advanceBounds);
124                 beginLineGlyphBounds = glyphAndAdvanceBounds;
125                 beginLineGlyphBounds.offset(-beginLineGlyphBounds.fLeft, 0);
126 
127                 SkRect glyphDrawBounds = beginLineGlyphBounds.makeOffset(newLineBounds.right(), 0);
128                 if (line.number == -1) {
129                     SkPaint p;
130                     p.setColor(SK_ColorBLUE);
131                     canvas->drawRect(glyphDrawBounds, p);
132                 }
133                 newLineBounds.join(glyphDrawBounds);
134                 if (newLineBounds.width() < fWindowSize.width()) {
135                     line.lastGlyph = glyph;
136                     line.bounds = newLineBounds;
137                     if (glyph != lastGlyph) {
138                         continue;
139                     }
140                 }
141             }
142 
143             // draw the line
144             position.fY -= line.bounds.top();
145             for (SkGlyphID gid = line.firstGlyph; gid <= line.lastGlyph; ++gid) {
146                 if (gid != line.firstGlyph) {
147                     position.fX += 10;
148                 }
149                 SkScalar advance;
150                 SkRect glyphBounds;
151                 font.getWidths(&gid, 1, &advance, &glyphBounds);
152                 SkRect advanceBounds = SkRect::MakeWH(advance, 1);
153                 SkRect glyphAndAdvanceBounds = glyphBounds;
154                 glyphAndAdvanceBounds.join(advanceBounds);
155 
156                 position.fX -= glyphAndAdvanceBounds.left();
157 
158                 if (fDrawGlyphMetrics) {
159                     SkPaint metricPaint;
160                     metricPaint.setColor(SK_ColorRED);
161                     canvas->drawRect(glyphBounds.makeOffset(position), metricPaint);
162                     metricPaint.setColor(SK_ColorGREEN);
163                     canvas->drawRect(advanceBounds.makeOffset(position), metricPaint);
164                 }
165 
166                 canvas->drawGlyphs(1, &gid, &position, origin, font, paint);
167 
168                 if (fGlyphNumbers) {
169                     SkString gidStr;
170                     gidStr.appendS32(gid);
171                     canvas->drawSimpleText(gidStr.c_str(), gidStr.size(), SkTextEncoding::kUTF8,
172                                            position.x(), position.y(), notationFont, notationPaint);
173                 }
174                 // TODO: also handle drawable by using a paint override canvas?
175                 SkPath glyphPath;
176                 if (fOutline && font.getPath(gid, &glyphPath)) {
177                     SkContourMeasureIter iter(glyphPath, false);
178                     sk_sp<SkContourMeasure> contour;
179                     int contourIndex = 0;
180                     while ((contour = iter.next())) {
181                         SkPoint contourStart;
182                         SkVector tangent;
183                         if (contour->getPosTan(0, &contourStart, &tangent)) {
184                             contourStart += position;
185                             SkAutoCanvasRestore acr(canvas, true);
186                             SkMatrix matrix;
187                             matrix.setSinCos(tangent.y(), tangent.x(), 0, 0);
188                             matrix.postTranslate(contourStart.x(), contourStart.y());
189                             canvas->concat(matrix);
190 
191                             if (fOutlineContourNumbers) {
192                                 SkString contourStr;
193                                 contourStr.appendS32(contourIndex);
194                                 canvas->drawSimpleText(contourStr.c_str(), contourStr.size(),
195                                                        SkTextEncoding::kUTF8, 0, 0,
196                                                        notationFont, notationPaint);
197                             } else {
198                                 canvas->drawPath(fPathDirectionIndicator, fPathDirectionIndicatorPaint);
199                             }
200 
201                             ++contourIndex;
202                         }
203                     }
204                 }
205 
206                 position.fX += glyphAndAdvanceBounds.right();
207             }
208             if (line.lastGlyph == lastGlyph) {
209                 break;
210             }
211             drawBounds.join(line.bounds.makeOffset(-line.bounds.fLeft, position.fY));
212             position.fX = 0;
213             position.fY += line.bounds.bottom() + 10;
214             line.bounds = beginLineGlyphBounds;
215             line.firstGlyph = glyph;
216             line.lastGlyph = glyph;
217             ++line.number;
218         }
219 
220         fDrawArea = drawBounds.roundOut().size();
221     }
222 
onGetControls(SkMetaData * controls)223     bool onGetControls(SkMetaData* controls) override {
224         // requested font size
225         SkScalar size[3] = {fFontSize, 0, 256};
226         controls->setScalars("Size", 3, size);
227 
228         // the first glyph on the first line
229         SkScalar glyph[3] = {SkScalar(fCurrentGlyphID), 0, SkScalar(this->fCurrentTypeface->countGlyphs())};
230         controls->setScalars("Glyph", 3, glyph);
231 
232         // TODO: limit number of glyphs
233         // TODO: choose typeface factory
234         // TODO: choose between typefaces
235         // TODO: font metrics like underline, strikeout, x-height, cap-height, etc.
236 
237         // show glyph metrics like advances and bounds
238         controls->setBool("Glyph Metrics", fDrawGlyphMetrics);
239 
240         // Show glyph numbers at origin
241         controls->setBool("Glyph numbers", fGlyphNumbers);
242 
243         // hairline contours with initial direction mark
244         controls->setBool("Outline", fOutline);
245 
246         // draw contour numbers instead of initial contour direction mark
247         controls->setBool("Outline contour numbers", fOutlineContourNumbers);
248 
249         return fVariationSliders.writeControls(controls);
250     }
251 
onSetControls(const SkMetaData & controls)252     void onSetControls(const SkMetaData& controls) override {
253         SkScalar size[3] = {0};
254         int numReturnedScalars = 0;
255         SkASSERT_RELEASE(controls.findScalars("Size", &numReturnedScalars, size));
256         SkASSERT_RELEASE(numReturnedScalars == 3);
257         if (fFontSize != size[0]) {
258             fFontSize = size[0];
259             fDrawArea.setEmpty();
260         }
261 
262         SkScalar glyph[3] = {0};
263         numReturnedScalars = 0;
264         SkASSERT_RELEASE(controls.findScalars("Glyph", &numReturnedScalars, glyph));
265         SkASSERT_RELEASE(numReturnedScalars == 3);
266         if (fCurrentGlyphID != SkScalarRoundToInt(glyph[0])) {
267             fCurrentGlyphID = SkScalarRoundToInt(glyph[0]);
268             fDrawArea.setEmpty();
269         }
270 
271         controls.findBool("Glyph Metrics", &fDrawGlyphMetrics);
272         controls.findBool("Glyph numbers", &fGlyphNumbers);
273         controls.findBool("Outline", &fOutline);
274         controls.findBool("Outline contour numbers", &fOutlineContourNumbers);
275 
276         fVariationSliders.readControls(controls, &fCurrentTypefaceDirty);
277     }
278 
279 private:
280     sk_sp<SkTypeface> fBaseTypeface;
281     sk_sp<SkTypeface> fCurrentTypeface;
282 
283     std::unique_ptr<SkFontArguments::VariationPosition::Coordinate[]> fCoordinates;
284     SkFontArguments::VariationPosition fVariationPosition;
285     ToolUtils::VariationSliders fVariationSliders;
286     bool fCurrentTypefaceDirty = true;
287     SkScalar fFontSize = 80;
288     bool fOutline = false;
289     bool fOutlineContourNumbers = false;
290     bool fGlyphNumbers = false;
291     bool fDrawGlyphMetrics = false;
292     SkGlyphID fCurrentGlyphID = 0;
293 
294     SkSize fWindowSize;
295     SkISize fDrawArea;
296 
297     SkPath fPathDirectionIndicator;
298     SkPaint fPathDirectionIndicatorPaint;
299 };
300 
301 }  //namespace
302 
303 //////////////////////////////////////////////////////////////////////////////
304 
305 DEF_SLIDE( return new TypefaceSlide(); )
306