|  | /* | 
|  | * Copyright 2012 Google Inc. | 
|  | * | 
|  | * Use of this source code is governed by a BSD-style license that can be | 
|  | * found in the LICENSE file. | 
|  | */ | 
|  |  | 
|  | #include "gm.h" | 
|  | #include "SkCanvas.h" | 
|  | #include "SkString.h" | 
|  | #include "SkTypeface.h" | 
|  | #include "SkTypes.h" | 
|  |  | 
|  | static const char* gFaces[] = { | 
|  | "Times Roman", | 
|  | "Hiragino Maru Gothic Pro", | 
|  | "Papyrus", | 
|  | "Helvetica", | 
|  | "Courier New" | 
|  | }; | 
|  |  | 
|  | class TypefaceGM : public skiagm::GM { | 
|  | public: | 
|  | TypefaceGM() { | 
|  | fFaces = new SkTypeface*[SK_ARRAY_COUNT(gFaces)]; | 
|  | for (size_t i = 0; i < SK_ARRAY_COUNT(gFaces); i++) { | 
|  | fFaces[i] = sk_tool_utils::create_portable_typeface(gFaces[i], SkTypeface::kNormal); | 
|  | } | 
|  | } | 
|  |  | 
|  | virtual ~TypefaceGM() { | 
|  | for (size_t i = 0; i < SK_ARRAY_COUNT(gFaces); i++) { | 
|  | SkSafeUnref(fFaces[i]); | 
|  | } | 
|  | delete [] fFaces; | 
|  | } | 
|  |  | 
|  | protected: | 
|  | virtual SkString onShortName() SK_OVERRIDE { | 
|  | return SkString("typeface"); | 
|  | } | 
|  |  | 
|  | virtual SkISize onISize() SK_OVERRIDE { | 
|  | return SkISize::Make(640, 480); | 
|  | } | 
|  |  | 
|  | virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE { | 
|  | SkString text("Typefaces are fun!"); | 
|  | SkScalar y = 0; | 
|  |  | 
|  | SkPaint paint; | 
|  | paint.setAntiAlias(true); | 
|  | for (int i = 0; i < (int)SK_ARRAY_COUNT(gFaces); i++) { | 
|  | this->drawWithFace(text, i, y, paint, canvas); | 
|  | } | 
|  | // Now go backwards | 
|  | for (int i = SK_ARRAY_COUNT(gFaces) - 1; i >= 0; i--) { | 
|  | this->drawWithFace(text, i, y, paint, canvas); | 
|  | } | 
|  | } | 
|  |  | 
|  | private: | 
|  | void drawWithFace(const SkString& text, int i, SkScalar& y, SkPaint& paint, | 
|  | SkCanvas* canvas) { | 
|  | paint.setTypeface(fFaces[i]); | 
|  | y += paint.getFontMetrics(NULL); | 
|  | canvas->drawText(text.c_str(), text.size(), 0, y, paint); | 
|  | } | 
|  |  | 
|  | SkTypeface** fFaces; | 
|  |  | 
|  | typedef skiagm::GM INHERITED; | 
|  | }; | 
|  |  | 
|  | /////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | static void getGlyphPositions(const SkPaint& paint, const uint16_t glyphs[], | 
|  | int count, SkScalar x, SkScalar y, SkPoint pos[]) { | 
|  | SkASSERT(SkPaint::kGlyphID_TextEncoding == paint.getTextEncoding()); | 
|  |  | 
|  | SkAutoSTMalloc<128, SkScalar> widthStorage(count); | 
|  | SkScalar* widths = widthStorage.get(); | 
|  | paint.getTextWidths(glyphs, count * sizeof(uint16_t), widths); | 
|  |  | 
|  | for (int i = 0; i < count; ++i) { | 
|  | pos[i].set(x, y); | 
|  | x += widths[i]; | 
|  | } | 
|  | } | 
|  |  | 
|  | static void applyKerning(SkPoint pos[], const int32_t adjustments[], int count, | 
|  | const SkPaint& paint) { | 
|  | SkScalar scale = paint.getTextSize() / paint.getTypeface()->getUnitsPerEm(); | 
|  |  | 
|  | SkScalar globalAdj = 0; | 
|  | for (int i = 0; i < count - 1; ++i) { | 
|  | globalAdj += adjustments[i] * scale; | 
|  | pos[i + 1].fX += globalAdj; | 
|  | } | 
|  | } | 
|  |  | 
|  | static void drawKernText(SkCanvas* canvas, const void* text, size_t len, | 
|  | SkScalar x, SkScalar y, const SkPaint& paint) { | 
|  | SkTypeface* face = paint.getTypeface(); | 
|  | if (!face) { | 
|  | canvas->drawText(text, len, x, y, paint); | 
|  | return; | 
|  | } | 
|  |  | 
|  | SkAutoSTMalloc<128, uint16_t> glyphStorage(len); | 
|  | uint16_t* glyphs = glyphStorage.get(); | 
|  | int glyphCount = paint.textToGlyphs(text, len, glyphs); | 
|  | if (glyphCount < 1) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | SkAutoSTMalloc<128, int32_t> adjustmentStorage(glyphCount - 1); | 
|  | int32_t* adjustments = adjustmentStorage.get(); | 
|  | if (!face->getKerningPairAdjustments(glyphs, glyphCount, adjustments)) { | 
|  | canvas->drawText(text, len, x, y, paint); | 
|  | return; | 
|  | } | 
|  |  | 
|  | SkPaint glyphPaint(paint); | 
|  | glyphPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); | 
|  |  | 
|  | SkAutoSTMalloc<128, SkPoint> posStorage(glyphCount); | 
|  | SkPoint* pos = posStorage.get(); | 
|  | getGlyphPositions(glyphPaint, glyphs, glyphCount, x, y, pos); | 
|  |  | 
|  | applyKerning(pos, adjustments, glyphCount, glyphPaint); | 
|  | canvas->drawPosText(glyphs, glyphCount * sizeof(uint16_t), pos, glyphPaint); | 
|  | } | 
|  |  | 
|  | static const struct { | 
|  | const char* fName; | 
|  | SkTypeface::Style   fStyle; | 
|  | } gFaceStyles[] = { | 
|  | { "sans-serif", SkTypeface::kNormal }, | 
|  | { "sans-serif", SkTypeface::kBold }, | 
|  | { "sans-serif", SkTypeface::kItalic }, | 
|  | { "sans-serif", SkTypeface::kBoldItalic }, | 
|  | { "serif", SkTypeface::kNormal }, | 
|  | { "serif", SkTypeface::kBold }, | 
|  | { "serif", SkTypeface::kItalic }, | 
|  | { "serif", SkTypeface::kBoldItalic }, | 
|  | { "monospace", SkTypeface::kNormal }, | 
|  | { "monospace", SkTypeface::kBold }, | 
|  | { "monospace", SkTypeface::kItalic }, | 
|  | { "monospace", SkTypeface::kBoldItalic }, | 
|  | }; | 
|  |  | 
|  | static const int gFaceStylesCount = SK_ARRAY_COUNT(gFaceStyles); | 
|  |  | 
|  | class TypefaceStylesGM : public skiagm::GM { | 
|  | SkTypeface* fFaces[gFaceStylesCount]; | 
|  | bool fApplyKerning; | 
|  |  | 
|  | public: | 
|  | TypefaceStylesGM(bool applyKerning) : fApplyKerning(applyKerning) { | 
|  | for (int i = 0; i < gFaceStylesCount; i++) { | 
|  | fFaces[i] = sk_tool_utils::create_portable_typeface(gFaceStyles[i].fName, | 
|  | gFaceStyles[i].fStyle); | 
|  | } | 
|  | } | 
|  |  | 
|  | virtual ~TypefaceStylesGM() { | 
|  | for (int i = 0; i < gFaceStylesCount; i++) { | 
|  | SkSafeUnref(fFaces[i]); | 
|  | } | 
|  | } | 
|  |  | 
|  | protected: | 
|  | virtual SkString onShortName() SK_OVERRIDE { | 
|  | SkString name("typefacestyles"); | 
|  | if (fApplyKerning) { | 
|  | name.append("_kerning"); | 
|  | } | 
|  | return name; | 
|  | } | 
|  |  | 
|  | virtual SkISize onISize() SK_OVERRIDE { | 
|  | return SkISize::Make(640, 480); | 
|  | } | 
|  |  | 
|  | virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE { | 
|  | SkPaint paint; | 
|  | paint.setAntiAlias(true); | 
|  | paint.setTextSize(SkIntToScalar(30)); | 
|  |  | 
|  | const char* text = fApplyKerning ? "Type AWAY" : "Hamburgefons"; | 
|  | const size_t textLen = strlen(text); | 
|  |  | 
|  | SkScalar x = SkIntToScalar(10); | 
|  | SkScalar dy = paint.getFontMetrics(NULL); | 
|  | SkScalar y = dy; | 
|  |  | 
|  | if (fApplyKerning) { | 
|  | paint.setSubpixelText(true); | 
|  | } else { | 
|  | paint.setLinearText(true); | 
|  | } | 
|  | for (int i = 0; i < gFaceStylesCount; i++) { | 
|  | paint.setTypeface(fFaces[i]); | 
|  | canvas->drawText(text, textLen, x, y, paint); | 
|  | if (fApplyKerning) { | 
|  | drawKernText(canvas, text, textLen, x + 240, y, paint); | 
|  | } | 
|  | y += dy; | 
|  | } | 
|  | } | 
|  |  | 
|  | private: | 
|  | typedef skiagm::GM INHERITED; | 
|  | }; | 
|  |  | 
|  | /////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | DEF_GM( return new TypefaceGM; ) | 
|  | DEF_GM( return new TypefaceStylesGM(false); ) | 
|  | DEF_GM( return new TypefaceStylesGM(true); ) |