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/SkTypeface.h" 15 #include "include/utils/SkNoDrawCanvas.h" 16 #include "src/base/SkRandom.h" 17 #include "tools/SkMetaData.h" 18 #include "tools/ToolUtils.h" 19 #include "tools/fonts/FontToolUtils.h" 20 #include "tools/timer/TimeUtils.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 paint; 79 if (fOutline) { 80 paint.setStroke(true); 81 paint.setStrokeWidth(0); 82 } 83 84 if (fCurrentTypefaceDirty) { 85 this->updateCurrentTypeface(); 86 } 87 SkFont font(fCurrentTypeface, fFontSize); 88 SkFontMetrics metrics; 89 font.getMetrics(&metrics); 90 91 SkPoint origin{0, 0}; 92 SkPoint position{0, 0}; 93 SkRect drawBounds = SkRect::MakeXYWH(0, 0, 1, 1); 94 struct Line { 95 SkRect bounds; 96 SkGlyphID firstGlyph = 0; // inclusive 97 SkGlyphID lastGlyph = 0; // inclusive 98 int number = 0; 99 } line; 100 101 int numGlyphs = fCurrentTypeface->countGlyphs(); 102 if (numGlyphs == 0) { 103 fDrawArea.setEmpty(); 104 return; 105 } 106 SkGlyphID lastGlyph = numGlyphs - 1; 107 for (SkGlyphID glyph = 0; true; ++glyph) { 108 // measure a line 109 SkRect beginLineGlyphBounds; 110 { 111 SkRect newLineBounds = line.bounds; 112 if (glyph != line.firstGlyph) { 113 newLineBounds.fRight += 10; 114 } 115 SkScalar advance; 116 SkRect glyphBounds; 117 font.getWidths(&glyph, 1, &advance, &glyphBounds); 118 SkRect advanceBounds = SkRect::MakeWH(advance, 1); 119 SkRect glyphAndAdvanceBounds = glyphBounds; 120 glyphAndAdvanceBounds.join(advanceBounds); 121 beginLineGlyphBounds = glyphAndAdvanceBounds; 122 beginLineGlyphBounds.offset(-beginLineGlyphBounds.fLeft, 0); 123 124 SkRect glyphDrawBounds = beginLineGlyphBounds.makeOffset(newLineBounds.right(), 0); 125 if (line.number == -1) { 126 SkPaint p; 127 p.setColor(SK_ColorBLUE); 128 canvas->drawRect(glyphDrawBounds, p); 129 } 130 newLineBounds.join(glyphDrawBounds); 131 if (newLineBounds.width() < fWindowSize.width()) { 132 line.lastGlyph = glyph; 133 line.bounds = newLineBounds; 134 if (glyph != lastGlyph) { 135 continue; 136 } 137 } 138 } 139 140 // draw the line 141 position.fY -= line.bounds.top(); 142 for (SkGlyphID gid = line.firstGlyph; gid <= line.lastGlyph; ++gid) { 143 if (gid != line.firstGlyph) { 144 position.fX += 10; 145 } 146 SkScalar advance; 147 SkRect glyphBounds; 148 font.getWidths(&gid, 1, &advance, &glyphBounds); 149 SkRect advanceBounds = SkRect::MakeWH(advance, 1); 150 SkRect glyphAndAdvanceBounds = glyphBounds; 151 glyphAndAdvanceBounds.join(advanceBounds); 152 153 position.fX -= glyphAndAdvanceBounds.left(); 154 155 if (fDrawGlyphMetrics) { 156 SkPaint metricPaint; 157 metricPaint.setColor(SK_ColorRED); 158 canvas->drawRect(glyphBounds.makeOffset(position), metricPaint); 159 metricPaint.setColor(SK_ColorGREEN); 160 canvas->drawRect(advanceBounds.makeOffset(position), metricPaint); 161 } 162 163 canvas->drawGlyphs(1, &gid, &position, origin, font, paint); 164 165 // TODO: also handle drawable by using a paint override canvas? 166 SkPath glyphPath; 167 if (fOutline && font.getPath(gid, &glyphPath)) { 168 SkContourMeasureIter iter(glyphPath, false); 169 sk_sp<SkContourMeasure> contour; 170 while ((contour = iter.next())) { 171 SkPoint contourStart; 172 SkVector tangent; 173 if (contour->getPosTan(0, &contourStart, &tangent)) { 174 contourStart += position; 175 SkAutoCanvasRestore acr(canvas, true); 176 SkMatrix matrix; 177 matrix.setSinCos(tangent.y(), tangent.x(), 0, 0); 178 matrix.postTranslate(contourStart.x(), contourStart.y()); 179 canvas->concat(matrix); 180 canvas->drawPath(fPathDirectionIndicator, fPathDirectionIndicatorPaint); 181 } 182 } 183 } 184 185 position.fX += glyphAndAdvanceBounds.right(); 186 } 187 if (line.lastGlyph == lastGlyph) { 188 break; 189 } 190 drawBounds.join(line.bounds.makeOffset(-line.bounds.fLeft, position.fY)); 191 position.fX = 0; 192 position.fY += line.bounds.bottom() + 10; 193 line.bounds = beginLineGlyphBounds; 194 line.firstGlyph = glyph; 195 line.lastGlyph = glyph; 196 ++line.number; 197 } 198 199 fDrawArea = drawBounds.roundOut().size(); 200 } 201 onGetControls(SkMetaData * controls)202 bool onGetControls(SkMetaData* controls) override { 203 // requested font size 204 SkScalar size[3] = {fFontSize, 0, 256}; 205 controls->setScalars("Size", 3, size); 206 207 // TODO: toggle glyph numbers on top? 208 // TODO: limit number of glyphs and set first glyph 209 // TODO: choose typeface factory 210 // TODO: choose between typefaces 211 // TODO: font metrics like underline, strikeout, x-height, cap-height, etc. 212 213 // show glyph metrics like advances and bounds 214 controls->setBool("Glyph Metrics", fDrawGlyphMetrics); 215 216 // hairline contours with initial direction mark 217 controls->setBool("Outline", fOutline); 218 219 return fVariationSliders.writeControls(controls); 220 } 221 onSetControls(const SkMetaData & controls)222 void onSetControls(const SkMetaData& controls) override { 223 SkScalar size[3] = {0}; 224 int numReturnedScalars = 0; 225 SkASSERT_RELEASE(controls.findScalars("Size", &numReturnedScalars, size)); 226 SkASSERT_RELEASE(numReturnedScalars == 3); 227 if (fFontSize != size[0]) { 228 fFontSize = size[0]; 229 fDrawArea.setEmpty(); 230 } 231 232 controls.findBool("Glyph Metrics", &fDrawGlyphMetrics); 233 controls.findBool("Outline", &fOutline); 234 235 fVariationSliders.readControls(controls, &fCurrentTypefaceDirty); 236 } 237 238 private: 239 sk_sp<SkTypeface> fBaseTypeface; 240 sk_sp<SkTypeface> fCurrentTypeface; 241 242 std::unique_ptr<SkFontArguments::VariationPosition::Coordinate[]> fCoordinates; 243 SkFontArguments::VariationPosition fVariationPosition; 244 ToolUtils::VariationSliders fVariationSliders; 245 bool fCurrentTypefaceDirty = true; 246 SkScalar fFontSize = 12; 247 bool fOutline = false; 248 bool fDrawGlyphMetrics = false; 249 250 SkSize fWindowSize; 251 SkISize fDrawArea; 252 253 SkPath fPathDirectionIndicator; 254 SkPaint fPathDirectionIndicatorPaint; 255 }; 256 257 } //namespace 258 259 ////////////////////////////////////////////////////////////////////////////// 260 261 DEF_SLIDE( return new TypefaceSlide(); ) 262