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.h" 9 #include "sk_tool_utils.h" 10 11 #include "SkCanvas.h" 12 #include "SkPoint.h" 13 #include "SkTextBlob.h" 14 #include "SkTDArray.h" 15 16 namespace { 17 18 enum Pos { 19 kDefault_Pos = 0, 20 kScalar_Pos = 1, 21 kPoint_Pos = 2, 22 }; 23 24 const struct BlobCfg { 25 unsigned count; 26 Pos pos; 27 SkScalar scale; 28 } blobConfigs[][3][3] = { 29 { 30 { { 1024, kDefault_Pos, 1 }, { 0, kDefault_Pos, 0 }, { 0, kDefault_Pos, 0 } }, 31 { { 1024, kScalar_Pos, 1 }, { 0, kScalar_Pos, 0 }, { 0, kScalar_Pos, 0 } }, 32 { { 1024, kPoint_Pos, 1 }, { 0, kPoint_Pos, 0 }, { 0, kPoint_Pos, 0 } }, 33 }, 34 { 35 { { 4, kDefault_Pos, 1 }, { 4, kDefault_Pos, 1 }, { 4, kDefault_Pos, 1 } }, 36 { { 4, kScalar_Pos, 1 }, { 4, kScalar_Pos, 1 }, { 4, kScalar_Pos, 1 } }, 37 { { 4, kPoint_Pos, 1 }, { 4, kPoint_Pos, 1 }, { 4, kPoint_Pos, 1 } }, 38 }, 39 40 { 41 { { 4, kDefault_Pos, 1 }, { 4, kDefault_Pos, 1 }, { 4, kScalar_Pos, 1 } }, 42 { { 4, kScalar_Pos, 1 }, { 4, kScalar_Pos, 1 }, { 4, kPoint_Pos, 1 } }, 43 { { 4, kPoint_Pos, 1 }, { 4, kPoint_Pos, 1 }, { 4, kDefault_Pos, 1 } }, 44 }, 45 46 { 47 { { 4, kDefault_Pos, 1 }, { 4, kScalar_Pos, 1 }, { 4, kPoint_Pos, 1 } }, 48 { { 4, kScalar_Pos, 1 }, { 4, kPoint_Pos, 1 }, { 4, kDefault_Pos, 1 } }, 49 { { 4, kPoint_Pos, 1 }, { 4, kDefault_Pos, 1 }, { 4, kScalar_Pos, 1 } }, 50 }, 51 52 { 53 { { 4, kDefault_Pos, .75f }, { 4, kDefault_Pos, 1 }, { 4, kScalar_Pos, 1.25f } }, 54 { { 4, kScalar_Pos, .75f }, { 4, kScalar_Pos, 1 }, { 4, kPoint_Pos, 1.25f } }, 55 { { 4, kPoint_Pos, .75f }, { 4, kPoint_Pos, 1 }, { 4, kDefault_Pos, 1.25f } }, 56 }, 57 58 { 59 { { 4, kDefault_Pos, 1 }, { 4, kScalar_Pos, .75f }, { 4, kPoint_Pos, 1.25f } }, 60 { { 4, kScalar_Pos, 1 }, { 4, kPoint_Pos, .75f }, { 4, kDefault_Pos, 1.25f } }, 61 { { 4, kPoint_Pos, 1 }, { 4, kDefault_Pos, .75f }, { 4, kScalar_Pos, 1.25f } }, 62 }, 63 }; 64 65 const SkScalar kFontSize = 16; 66 } 67 68 class TextBlobGM : public skiagm::GM { 69 public: TextBlobGM(const char * txt)70 TextBlobGM(const char* txt) 71 : fText(txt) { 72 } 73 74 protected: onOnceBeforeDraw()75 void onOnceBeforeDraw() override { 76 fTypeface = sk_tool_utils::create_portable_typeface("serif", SkFontStyle()); 77 SkPaint p; 78 p.setTypeface(fTypeface); 79 size_t txtLen = strlen(fText); 80 int glyphCount = p.textToGlyphs(fText, txtLen, nullptr); 81 82 fGlyphs.append(glyphCount); 83 p.textToGlyphs(fText, txtLen, fGlyphs.begin()); 84 } 85 onShortName()86 SkString onShortName() override { 87 return SkString("textblob"); 88 } 89 onISize()90 SkISize onISize() override { 91 return SkISize::Make(640, 480); 92 } 93 onDraw(SkCanvas * canvas)94 void onDraw(SkCanvas* canvas) override { 95 for (unsigned b = 0; b < SK_ARRAY_COUNT(blobConfigs); ++b) { 96 sk_sp<SkTextBlob> blob(this->makeBlob(b)); 97 98 SkPaint p; 99 SkPoint offset = SkPoint::Make(SkIntToScalar(10 + 300 * (b % 2)), 100 SkIntToScalar(20 + 150 * (b / 2))); 101 102 canvas->drawTextBlob(blob, offset.x(), offset.y(), p); 103 104 p.setColor(SK_ColorBLUE); 105 p.setStyle(SkPaint::kStroke_Style); 106 SkRect box = blob->bounds(); 107 box.offset(offset); 108 canvas->drawRect(box, p); 109 110 } 111 } 112 113 private: makeBlob(unsigned blobIndex)114 sk_sp<SkTextBlob> makeBlob(unsigned blobIndex) { 115 SkTextBlobBuilder builder; 116 117 SkPaint font; 118 font.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 119 font.setAntiAlias(true); 120 font.setSubpixelText(true); 121 font.setTypeface(fTypeface); 122 123 for (unsigned l = 0; l < SK_ARRAY_COUNT(blobConfigs[blobIndex]); ++l) { 124 unsigned currentGlyph = 0; 125 126 for (unsigned c = 0; c < SK_ARRAY_COUNT(blobConfigs[blobIndex][l]); ++c) { 127 const BlobCfg* cfg = &blobConfigs[blobIndex][l][c]; 128 unsigned count = cfg->count; 129 130 if (count > fGlyphs.count() - currentGlyph) { 131 count = fGlyphs.count() - currentGlyph; 132 } 133 if (0 == count) { 134 break; 135 } 136 137 font.setTextSize(kFontSize * cfg->scale); 138 const SkScalar advanceX = font.getTextSize() * 0.85f; 139 const SkScalar advanceY = font.getTextSize() * 1.5f; 140 141 SkPoint offset = SkPoint::Make(currentGlyph * advanceX + c * advanceX, 142 advanceY * l); 143 switch (cfg->pos) { 144 case kDefault_Pos: { 145 const SkTextBlobBuilder::RunBuffer& buf = builder.allocRun(font, count, 146 offset.x(), 147 offset.y()); 148 memcpy(buf.glyphs, fGlyphs.begin() + currentGlyph, count * sizeof(uint16_t)); 149 } break; 150 case kScalar_Pos: { 151 const SkTextBlobBuilder::RunBuffer& buf = builder.allocRunPosH(font, count, 152 offset.y()); 153 SkTDArray<SkScalar> pos; 154 for (unsigned i = 0; i < count; ++i) { 155 *pos.append() = offset.x() + i * advanceX; 156 } 157 158 memcpy(buf.glyphs, fGlyphs.begin() + currentGlyph, count * sizeof(uint16_t)); 159 memcpy(buf.pos, pos.begin(), count * sizeof(SkScalar)); 160 } break; 161 case kPoint_Pos: { 162 const SkTextBlobBuilder::RunBuffer& buf = builder.allocRunPos(font, count); 163 164 SkTDArray<SkScalar> pos; 165 for (unsigned i = 0; i < count; ++i) { 166 *pos.append() = offset.x() + i * advanceX; 167 *pos.append() = offset.y() + i * (advanceY / count); 168 } 169 170 memcpy(buf.glyphs, fGlyphs.begin() + currentGlyph, count * sizeof(uint16_t)); 171 memcpy(buf.pos, pos.begin(), count * sizeof(SkScalar) * 2); 172 } break; 173 default: 174 SK_ABORT("unhandled pos value"); 175 } 176 177 currentGlyph += count; 178 } 179 } 180 181 return builder.make(); 182 } 183 184 SkTDArray<uint16_t> fGlyphs; 185 sk_sp<SkTypeface> fTypeface; 186 const char* fText; 187 typedef skiagm::GM INHERITED; 188 }; 189 190 DEF_GM(return new TextBlobGM("hamburgefons");) 191