void onDraw(SkCanvas* canvas) override { SkFont font; font.setSize(30); const char* text = fApplyKerning ? "Type AWAY" : "Hamburgefons"; const size_t textLen = strlen(text); SkScalar x = SkIntToScalar(10); SkScalar dy = font.getMetrics(nullptr); SkScalar y = dy; if (fApplyKerning) { font.setSubpixel(true); } else { font.setLinearMetrics(true); } SkPaint paint; for (int i = 0; i < gStylesCount; i++) { font.setTypeface(fFaces[i]); canvas->drawSimpleText(text, textLen, SkTextEncoding::kUTF8, x, y, font, paint); if (fApplyKerning) { drawKernText(canvas, text, textLen, x + 240, y, font, paint); } y += dy; } }
static void add_run(SkTextBlobBuilder* builder, const char text[], SkScalar x, SkScalar y, sk_sp<SkTypeface> tf) { SkFont font; font.setEdging(SkFont::Edging::kAntiAlias); font.setSubpixel(true); font.setSize(16); font.setTypeface(tf); int glyphCount = font.countText(text, strlen(text), SkTextEncoding::kUTF8); SkTextBlobBuilder::RunBuffer buffer = builder->allocRun(font, glyphCount, x, y); (void)font.textToGlyphs(text, strlen(text), SkTextEncoding::kUTF8, buffer.glyphs, glyphCount); }
void drawText(SkCanvas* canvas) { static const int kSizes[] = {8, 9, 10, 11, 12, 13, 18, 20, 25}; static const SkString kTexts[] = {SkString("ABCDEFGHIJKLMNOPQRSTUVWXYZ"), SkString("abcdefghijklmnopqrstuvwxyz"), SkString("0123456789"), SkString("!@#$%^&*()<>[]{}")}; SkFont font; font.setEdging(SkFont::Edging::kAntiAlias); font.setSubpixel(true); static const SkScalar kSubPixelInc = 1 / 2.f; SkScalar x = 0; SkScalar y = 10; SkScalar subpixelX = 0; SkScalar subpixelY = 0; bool offsetX = true; if (GrContextOptions::Enable::kYes == fAllowMultipleTextures) { canvas->scale(10, 10); } do { for (auto s : kSizes) { auto size = 2 * s; font.setSize(size); for (const auto& typeface : fTypefaces) { font.setTypeface(typeface); for (const auto& text : kTexts) { x = size + draw_string(canvas, text, x + subpixelX, y + subpixelY, font); x = SkScalarCeilToScalar(x); if (x + 100 > kSize) { x = 0; y += SkScalarCeilToScalar(size + 3); if (y > kSize) { return; } } } } (offsetX ? subpixelX : subpixelY) += kSubPixelInc; offsetX = !offsetX; } } while (true); }
// Verify that text-related properties are captured in run paints. static void TestPaintProps(skiatest::Reporter* reporter) { SkFont font; // Kitchen sink font. font.setSize(42); font.setScaleX(4.2f); font.setTypeface(ToolUtils::create_portable_typeface()); font.setSkewX(0.42f); font.setHinting(SkFontHinting::kFull); font.setEdging(SkFont::Edging::kSubpixelAntiAlias); font.setEmbolden(true); font.setLinearMetrics(true); font.setSubpixel(true); font.setEmbeddedBitmaps(true); font.setForceAutoHinting(true); // Ensure we didn't pick default values by mistake. SkFont defaultFont; REPORTER_ASSERT(reporter, defaultFont.getSize() != font.getSize()); REPORTER_ASSERT(reporter, defaultFont.getScaleX() != font.getScaleX()); REPORTER_ASSERT(reporter, defaultFont.getTypefaceOrDefault() != font.getTypefaceOrDefault()); REPORTER_ASSERT(reporter, defaultFont.getSkewX() != font.getSkewX()); REPORTER_ASSERT(reporter, defaultFont.getHinting() != font.getHinting()); REPORTER_ASSERT(reporter, defaultFont.getEdging() != font.getEdging()); REPORTER_ASSERT(reporter, defaultFont.isEmbolden() != font.isEmbolden()); REPORTER_ASSERT(reporter, defaultFont.isLinearMetrics() != font.isLinearMetrics()); REPORTER_ASSERT(reporter, defaultFont.isSubpixel() != font.isSubpixel()); REPORTER_ASSERT(reporter, defaultFont.isEmbeddedBitmaps() != font.isEmbeddedBitmaps()); REPORTER_ASSERT(reporter, defaultFont.isForceAutoHinting() != font.isForceAutoHinting()); SkTextBlobBuilder builder; AddRun(font, 1, SkTextBlobRunIterator::kDefault_Positioning, SkPoint::Make(0, 0), builder); AddRun(font, 1, SkTextBlobRunIterator::kHorizontal_Positioning, SkPoint::Make(0, 0), builder); AddRun(font, 1, SkTextBlobRunIterator::kFull_Positioning, SkPoint::Make(0, 0), builder); sk_sp<SkTextBlob> blob(builder.make()); SkTextBlobRunIterator it(blob.get()); while (!it.done()) { REPORTER_ASSERT(reporter, it.font() == font); it.next(); } }
virtual void onDraw(SkCanvas* inputCanvas) override { SkScalar textSizes[] = { 9.0f, 9.0f*2.0f, 9.0f*5.0f, 9.0f*2.0f*5.0f }; SkScalar scales[] = { 2.0f*5.0f, 5.0f, 2.0f, 1.0f }; // set up offscreen rendering with distance field text GrContext* ctx = inputCanvas->getGrContext(); SkISize size = onISize(); SkImageInfo info = SkImageInfo::MakeN32(size.width(), size.height(), kPremul_SkAlphaType, inputCanvas->imageInfo().refColorSpace()); SkSurfaceProps props(SkSurfaceProps::kUseDeviceIndependentFonts_Flag, SkSurfaceProps::kLegacyFontHost_InitType); auto surface(SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info, 0, &props)); SkCanvas* canvas = surface ? surface->getCanvas() : inputCanvas; // init our new canvas with the old canvas's matrix canvas->setMatrix(inputCanvas->getTotalMatrix()); // apply global scale to test glyph positioning canvas->scale(1.05f, 1.05f); canvas->clear(0xffffffff); SkPaint paint; paint.setAntiAlias(true); SkFont font(ToolUtils::create_portable_typeface("serif", SkFontStyle())); font.setSubpixel(true); const char* text = "Hamburgefons"; const size_t textLen = strlen(text); // check scaling up SkScalar x = SkIntToScalar(0); SkScalar y = SkIntToScalar(78); for (size_t i = 0; i < SK_ARRAY_COUNT(textSizes); ++i) { SkAutoCanvasRestore acr(canvas, true); canvas->translate(x, y); canvas->scale(scales[i], scales[i]); font.setSize(textSizes[i]); canvas->drawSimpleText(text, textLen, SkTextEncoding::kUTF8, 0, 0, font, paint); y += font.getMetrics(nullptr)*scales[i]; } // check rotation for (size_t i = 0; i < 5; ++i) { SkScalar rotX = SkIntToScalar(10); SkScalar rotY = y; SkAutoCanvasRestore acr(canvas, true); canvas->translate(SkIntToScalar(10 + i * 200), -80); canvas->rotate(SkIntToScalar(i * 5), rotX, rotY); for (int ps = 6; ps <= 32; ps += 3) { font.setSize(SkIntToScalar(ps)); canvas->drawSimpleText(text, textLen, SkTextEncoding::kUTF8, rotX, rotY, font, paint); rotY += font.getMetrics(nullptr); } } // check scaling down font.setEdging(SkFont::Edging::kSubpixelAntiAlias); x = SkIntToScalar(680); y = SkIntToScalar(20); size_t arraySize = SK_ARRAY_COUNT(textSizes); for (size_t i = 0; i < arraySize; ++i) { SkAutoCanvasRestore acr(canvas, true); canvas->translate(x, y); SkScalar scaleFactor = SkScalarInvert(scales[arraySize - i - 1]); canvas->scale(scaleFactor, scaleFactor); font.setSize(textSizes[i]); canvas->drawSimpleText(text, textLen, SkTextEncoding::kUTF8, 0, 0, font, paint); y += font.getMetrics(nullptr)*scaleFactor; } // check pos text { SkAutoCanvasRestore acr(canvas, true); canvas->scale(2.0f, 2.0f); SkAutoTArray<SkGlyphID> glyphs(SkToInt(textLen)); int count = font.textToGlyphs(text, textLen, SkTextEncoding::kUTF8, glyphs.get(), textLen); SkAutoTArray<SkPoint> pos(count); font.setSize(textSizes[0]); font.getPos(glyphs.get(), count, pos.get(), {340, 75}); auto blob = SkTextBlob::MakeFromPosText(glyphs.get(), count * sizeof(SkGlyphID), pos.get(), font, SkTextEncoding::kGlyphID); canvas->drawTextBlob(blob, 0, 0, paint); } // check gamma-corrected blending const SkColor fg[] = { 0xFFFFFFFF, 0xFFFFFF00, 0xFFFF00FF, 0xFF00FFFF, 0xFFFF0000, 0xFF00FF00, 0xFF0000FF, 0xFF000000, }; paint.setColor(0xFFF7F3F7); SkRect r = SkRect::MakeLTRB(670, 215, 820, 397); canvas->drawRect(r, paint); x = SkIntToScalar(680); y = SkIntToScalar(235); font.setSize(SkIntToScalar(19)); for (size_t i = 0; i < SK_ARRAY_COUNT(fg); ++i) { paint.setColor(fg[i]); canvas->drawSimpleText(text, textLen, SkTextEncoding::kUTF8, x, y, font, paint); y += font.getMetrics(nullptr); } paint.setColor(0xFF181C18); r = SkRect::MakeLTRB(820, 215, 970, 397); canvas->drawRect(r, paint); x = SkIntToScalar(830); y = SkIntToScalar(235); font.setSize(SkIntToScalar(19)); for (size_t i = 0; i < SK_ARRAY_COUNT(fg); ++i) { paint.setColor(fg[i]); canvas->drawSimpleText(text, textLen, SkTextEncoding::kUTF8, x, y, font, paint); y += font.getMetrics(nullptr); } // check skew { font.setEdging(SkFont::Edging::kAntiAlias); SkAutoCanvasRestore acr(canvas, true); canvas->skew(0.0f, 0.151515f); font.setSize(SkIntToScalar(32)); canvas->drawSimpleText(text, textLen, SkTextEncoding::kUTF8, 745, 70, font, paint); } { font.setEdging(SkFont::Edging::kSubpixelAntiAlias); SkAutoCanvasRestore acr(canvas, true); canvas->skew(0.5f, 0.0f); font.setSize(SkIntToScalar(32)); canvas->drawSimpleText(text, textLen, SkTextEncoding::kUTF8, 580, 125, font, paint); } // check perspective { font.setEdging(SkFont::Edging::kAntiAlias); SkAutoCanvasRestore acr(canvas, true); SkMatrix persp; persp.setAll(0.9839f, 0, 0, 0.2246f, 0.6829f, 0, 0.0002352f, -0.0003844f, 1); canvas->concat(persp); canvas->translate(1100, -295); font.setSize(37.5f); canvas->drawSimpleText(text, textLen, SkTextEncoding::kUTF8, 0, 0, font, paint); } { font.setSubpixel(false); font.setEdging(SkFont::Edging::kAlias); SkAutoCanvasRestore acr(canvas, true); SkMatrix persp; persp.setAll(0.9839f, 0, 0, 0.2246f, 0.6829f, 0, 0.0002352f, -0.0003844f, 1); canvas->concat(persp); canvas->translate(1075, -245); canvas->scale(375, 375); font.setSize(0.1f); canvas->drawSimpleText(text, textLen, SkTextEncoding::kUTF8, 0, 0, font, paint); } // check color emoji if (fEmojiTypeface) { SkFont emoiFont; emoiFont.setSubpixel(true); emoiFont.setTypeface(fEmojiTypeface); emoiFont.setSize(SkIntToScalar(19)); canvas->drawSimpleText(fEmojiText, strlen(fEmojiText), SkTextEncoding::kUTF8, 670, 90, emoiFont, paint); } // render offscreen buffer if (surface) { SkAutoCanvasRestore acr(inputCanvas, true); // since we prepended this matrix already, we blit using identity inputCanvas->resetMatrix(); inputCanvas->drawImage(surface->makeImageSnapshot().get(), 0, 0, nullptr); } }
sk_sp<SkTextBlob> makeBlob(unsigned blobIndex) { SkTextBlobBuilder builder; SkFont font; font.setSubpixel(true); font.setEdging(SkFont::Edging::kAntiAlias); font.setTypeface(fTypeface); for (unsigned l = 0; l < SK_ARRAY_COUNT(blobConfigs[blobIndex]); ++l) { unsigned currentGlyph = 0; for (unsigned c = 0; c < SK_ARRAY_COUNT(blobConfigs[blobIndex][l]); ++c) { const BlobCfg* cfg = &blobConfigs[blobIndex][l][c]; unsigned count = cfg->count; if (count > fGlyphs.count() - currentGlyph) { count = fGlyphs.count() - currentGlyph; } if (0 == count) { break; } font.setSize(kFontSize * cfg->scale); const SkScalar advanceX = font.getSize() * 0.85f; const SkScalar advanceY = font.getSize() * 1.5f; SkPoint offset = SkPoint::Make(currentGlyph * advanceX + c * advanceX, advanceY * l); switch (cfg->pos) { case kDefault_Pos: { const SkTextBlobBuilder::RunBuffer& buf = builder.allocRun(font, count, offset.x(), offset.y()); memcpy(buf.glyphs, fGlyphs.begin() + currentGlyph, count * sizeof(uint16_t)); } break; case kScalar_Pos: { const SkTextBlobBuilder::RunBuffer& buf = builder.allocRunPosH(font, count, offset.y()); SkTDArray<SkScalar> pos; for (unsigned i = 0; i < count; ++i) { *pos.append() = offset.x() + i * advanceX; } memcpy(buf.glyphs, fGlyphs.begin() + currentGlyph, count * sizeof(uint16_t)); memcpy(buf.pos, pos.begin(), count * sizeof(SkScalar)); } break; case kPoint_Pos: { const SkTextBlobBuilder::RunBuffer& buf = builder.allocRunPos(font, count); SkTDArray<SkScalar> pos; for (unsigned i = 0; i < count; ++i) { *pos.append() = offset.x() + i * advanceX; *pos.append() = offset.y() + i * (advanceY / count); } memcpy(buf.glyphs, fGlyphs.begin() + currentGlyph, count * sizeof(uint16_t)); memcpy(buf.pos, pos.begin(), count * sizeof(SkScalar) * 2); } break; default: SK_ABORT("unhandled pos value"); } currentGlyph += count; } } return builder.make(); }