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