PassTextBlobPtr Font::buildTextBlob(const GlyphBuffer& glyphBuffer, float initialAdvance, const FloatRect& bounds) const { SkTextBlobBuilder builder; SkScalar advance = SkFloatToScalar(initialAdvance); bool success = glyphBuffer.hasOffsets() ? buildTextBlobInternal<true>(glyphBuffer, advance, builder) : buildTextBlobInternal<false>(glyphBuffer, advance, builder); return success ? adoptRef(builder.build()) : nullptr; }
void WriteLine(const SkShaper& shaper, const char *text, size_t textBytes) { if (!pageCanvas || current_y > config->page_height.value) { if (pageCanvas) { document->endPage(); } pageCanvas = document->beginPage(config->page_width.value, config->page_height.value); pageCanvas->drawPaint(white_paint); current_x = config->left_margin.value; current_y = config->line_spacing_ratio.value * config->font_size.value; } SkTextBlobBuilder textBlobBuilder; shaper.shape(&textBlobBuilder, glyph_paint, text, textBytes, SkPoint{0, 0}); sk_sp<const SkTextBlob> blob(textBlobBuilder.build()); pageCanvas->drawTextBlob(blob.get(), current_x, current_y, glyph_paint); // Advance to the next line. current_y += config->line_spacing_ratio.value * config->font_size.value; }
void onDelayedSetup() override { fTypeface.reset(sk_tool_utils::create_portable_typeface("serif", SkTypeface::kNormal)); // make textblob SkPaint paint; paint.setTypeface(fTypeface); const char* text = "Hello blob!"; SkTDArray<uint16_t> glyphs; size_t len = strlen(text); glyphs.append(paint.textToGlyphs(text, len, nullptr)); paint.textToGlyphs(text, len, glyphs.begin()); SkTextBlobBuilder builder; paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); const SkTextBlobBuilder::RunBuffer& run = builder.allocRun(paint, glyphs.count(), 10, 10, nullptr); memcpy(run.glyphs, glyphs.begin(), glyphs.count() * sizeof(uint16_t)); fBlob.reset(builder.build()); }
// This test hammers the GPU textblobcache and font atlas static void text_blob_cache_inner(skiatest::Reporter* reporter, GrContext* context, int maxTotalText, int maxGlyphID, int maxFamilies, bool normal, bool stressTest) { // setup surface uint32_t flags = 0; SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType); // configure our context for maximum stressing of cache and atlas if (stressTest) { GrTest::SetupAlwaysEvictAtlas(context); context->setTextBlobCacheLimit_ForTesting(0); } SkImageInfo info = SkImageInfo::Make(kWidth, kHeight, kN32_SkColorType, kPremul_SkAlphaType); auto surface(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info, 0, &props)); REPORTER_ASSERT(reporter, surface); if (!surface) { return; } SkCanvas* canvas = surface->getCanvas(); SkAutoTUnref<SkFontMgr> fm(SkFontMgr::RefDefault()); int count = SkMin32(fm->countFamilies(), maxFamilies); // make a ton of text SkAutoTArray<uint16_t> text(maxTotalText); for (int i = 0; i < maxTotalText; i++) { text[i] = i % maxGlyphID; } // generate textblobs SkTArray<TextBlobWrapper> blobs; for (int i = 0; i < count; i++) { SkPaint paint; paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); paint.setTextSize(48); // draw big glyphs to really stress the atlas SkString familyName; fm->getFamilyName(i, &familyName); SkAutoTUnref<SkFontStyleSet> set(fm->createStyleSet(i)); for (int j = 0; j < set->count(); ++j) { SkFontStyle fs; set->getStyle(j, &fs, nullptr); // We use a typeface which randomy returns unexpected mask formats to fuzz SkAutoTUnref<SkTypeface> orig(set->createTypeface(j)); if (normal) { paint.setTypeface(orig); } else { SkAutoTUnref<SkTypeface> typeface(new SkRandomTypeface(orig, paint, true)); paint.setTypeface(typeface); } SkTextBlobBuilder builder; for (int aa = 0; aa < 2; aa++) { for (int subpixel = 0; subpixel < 2; subpixel++) { for (int lcd = 0; lcd < 2; lcd++) { paint.setAntiAlias(SkToBool(aa)); paint.setSubpixelText(SkToBool(subpixel)); paint.setLCDRenderText(SkToBool(lcd)); if (!SkToBool(lcd)) { paint.setTextSize(160); } const SkTextBlobBuilder::RunBuffer& run = builder.allocRun(paint, maxTotalText, 0, 0, nullptr); memcpy(run.glyphs, text.get(), maxTotalText * sizeof(uint16_t)); } } } blobs.emplace_back(builder.build()); } } // create surface where LCD is impossible info = SkImageInfo::MakeN32Premul(kWidth, kHeight); SkSurfaceProps propsNoLCD(0, kUnknown_SkPixelGeometry); auto surfaceNoLCD(canvas->makeSurface(info, &propsNoLCD)); REPORTER_ASSERT(reporter, surface); if (!surface) { return; } SkCanvas* canvasNoLCD = surfaceNoLCD->getCanvas(); // test redraw draw(canvas, 2, blobs); draw(canvasNoLCD, 2, blobs); // test draw after free context->freeGpuResources(); draw(canvas, 1, blobs); context->freeGpuResources(); draw(canvasNoLCD, 1, blobs); // test draw after abandon context->abandonContext(); draw(canvas, 1, blobs); }
const SkTextBlob* makeBlob(unsigned blobIndex) { SkTextBlobBuilder builder; SkPaint font; font.setTextEncoding(SkPaint::kGlyphID_TextEncoding); font.setAntiAlias(true); font.setSubpixelText(true); 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.setTextSize(kFontSize * cfg->scale); const SkScalar advanceX = font.getTextSize() * 0.85f; const SkScalar advanceY = font.getTextSize() * 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.build(); }
void onOnceBeforeDraw() override { SkTextBlobBuilder builder; // LCD SkPaint paint; paint.setTextSize(32); const char* text = "The quick brown fox jumps over the lazy dog"; paint.setSubpixelText(true); paint.setLCDRenderText(true); paint.setAntiAlias(true); sk_tool_utils::set_portable_typeface(&paint); add_to_text_blob(&builder, text, paint, 0, 0); fBlob.reset(builder.build()); // create a looper which sandwhiches an effect in two normal draws LooperSettings looperSandwhich[] = { { SkXfermode::kSrc_Mode, SK_ColorMAGENTA, SkPaint::kFill_Style, 0, 0, 0, false }, { SkXfermode::kSrcOver_Mode, 0x88000000, SkPaint::kFill_Style, 0, 10.f, 0, true }, { SkXfermode::kSrcOver_Mode, 0x50FF00FF, SkPaint::kFill_Style, 0, 20.f, 0, false }, }; LooperSettings compound[] = { { SkXfermode::kSrc_Mode, SK_ColorWHITE, SkPaint::kStroke_Style, 1.f * 3/4, 0, 0, false }, { SkXfermode::kSrc_Mode, SK_ColorRED, SkPaint::kStroke_Style, 4.f, 0, 0, false }, { SkXfermode::kSrc_Mode, SK_ColorBLUE, SkPaint::kFill_Style, 0, 0, 0, false }, { SkXfermode::kSrcOver_Mode, 0x88000000, SkPaint::kFill_Style, 0, 10.f, 0, true } }; LooperSettings xfermode[] = { { SkXfermode::kDifference_Mode, SK_ColorWHITE, SkPaint::kFill_Style, 0, 0, 0, false }, { SkXfermode::kSrcOver_Mode, 0xFF000000, SkPaint::kFill_Style, 0, 1.f, 0, true }, { SkXfermode::kSrcOver_Mode, 0x50FF00FF, SkPaint::kFill_Style, 0, 2.f, 0, false }, }; // NOTE, this should be ignored by textblobs LooperSettings skew[] = { { SkXfermode::kSrc_Mode, SK_ColorRED, SkPaint::kFill_Style, 0, 0, -1.f, false }, { SkXfermode::kSrc_Mode, SK_ColorGREEN, SkPaint::kFill_Style, 0, 10.f, -1.f, false }, { SkXfermode::kSrc_Mode, SK_ColorBLUE, SkPaint::kFill_Style, 0, 20.f, -1.f, false }, }; LooperSettings kitchenSink[] = { { SkXfermode::kSrc_Mode, SK_ColorWHITE, SkPaint::kStroke_Style, 1.f * 3/4, 0, 0, false }, { SkXfermode::kSrc_Mode, SK_ColorBLACK, SkPaint::kFill_Style, 0, 0, 0, false }, { SkXfermode::kDifference_Mode, SK_ColorWHITE, SkPaint::kFill_Style, 1.f, 10.f, 0, false }, { SkXfermode::kSrc_Mode, SK_ColorWHITE, SkPaint::kFill_Style, 0, 10.f, 0, true }, { SkXfermode::kSrcOver_Mode, 0x50FF00FF, SkPaint::kFill_Style, 0, 20.f, 0, false }, }; fLoopers.push_back(setupLooper(SkLayerDrawLooper::kMaskFilter_Bit | SkLayerDrawLooper::kXfermode_Bit | SkLayerDrawLooper::kStyle_Bit, &mask_filter, compound, SK_ARRAY_COUNT(compound))); fLoopers.push_back(setupLooper(SkLayerDrawLooper::kPathEffect_Bit | SkLayerDrawLooper::kXfermode_Bit, &path_effect, looperSandwhich, SK_ARRAY_COUNT(looperSandwhich))); fLoopers.push_back(setupLooper(SkLayerDrawLooper::kShader_Bit | SkLayerDrawLooper::kColorFilter_Bit | SkLayerDrawLooper::kXfermode_Bit, &color_filter, looperSandwhich, SK_ARRAY_COUNT(looperSandwhich))); fLoopers.push_back(setupLooper(SkLayerDrawLooper::kShader_Bit | SkLayerDrawLooper::kColorFilter_Bit | SkLayerDrawLooper::kXfermode_Bit, &color_filter, xfermode, SK_ARRAY_COUNT(xfermode))); fLoopers.push_back(setupLooper(0, nullptr, skew, SK_ARRAY_COUNT(skew))); fLoopers.push_back(setupLooper(SkLayerDrawLooper::kMaskFilter_Bit | SkLayerDrawLooper::kShader_Bit | SkLayerDrawLooper::kColorFilter_Bit | SkLayerDrawLooper::kPathEffect_Bit | SkLayerDrawLooper::kStyle_Bit | SkLayerDrawLooper::kXfermode_Bit, &kitchen_sink, kitchenSink, SK_ARRAY_COUNT(kitchenSink))); // Test we respect overrides fLoopers.push_back(setupLooper(0, &kitchen_sink, kitchenSink, SK_ARRAY_COUNT(kitchenSink))); }