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