static void drawKernText(SkCanvas* canvas, const void* text, size_t len, SkScalar x, SkScalar y, const SkFont& font, const SkPaint& paint) { SkTypeface* face = font.getTypefaceOrDefault(); if (!face) { canvas->drawSimpleText(text, len, SkTextEncoding::kUTF8, x, y, font, paint); return; } SkAutoSTMalloc<128, uint16_t> glyphStorage(len); uint16_t* glyphs = glyphStorage.get(); int glyphCount = font.textToGlyphs(text, len, SkTextEncoding::kUTF8, glyphs, len); if (glyphCount < 1) { return; } SkAutoSTMalloc<128, int32_t> adjustmentStorage(glyphCount - 1); int32_t* adjustments = adjustmentStorage.get(); if (!face->getKerningPairAdjustments(glyphs, glyphCount, adjustments)) { canvas->drawSimpleText(text, len, SkTextEncoding::kUTF8, x, y, font, paint); return; } SkTextBlobBuilder builder; auto rec = builder.allocRunPos(font, glyphCount); memcpy(rec.glyphs, glyphs, glyphCount * sizeof(SkGlyphID)); getGlyphPositions(font, glyphs, glyphCount, x, y, rec.points()); applyKerning(rec.points(), adjustments, glyphCount, font); canvas->drawTextBlob(builder.make(), 0, 0, paint); }
DEF_TEST(TextBlob_extended, reporter) { SkTextBlobBuilder textBlobBuilder; SkFont font; const char text1[] = "Foo"; const char text2[] = "Bar"; int glyphCount = font.countText(text1, strlen(text1), SkTextEncoding::kUTF8); SkAutoTMalloc<uint16_t> glyphs(glyphCount); (void)font.textToGlyphs(text1, strlen(text1), SkTextEncoding::kUTF8, glyphs.get(), glyphCount); auto run = SkTextBlobBuilderPriv::AllocRunText(&textBlobBuilder, font, glyphCount, 0, 0, SkToInt(strlen(text2)), SkString(), nullptr); memcpy(run.glyphs, glyphs.get(), sizeof(uint16_t) * glyphCount); memcpy(run.utf8text, text2, strlen(text2)); for (int i = 0; i < glyphCount; ++i) { run.clusters[i] = SkTMin(SkToU32(i), SkToU32(strlen(text2))); } sk_sp<SkTextBlob> blob(textBlobBuilder.make()); REPORTER_ASSERT(reporter, blob); for (SkTextBlobRunIterator it(blob.get()); !it.done(); it.next()) { REPORTER_ASSERT(reporter, it.glyphCount() == (uint32_t)glyphCount); for (uint32_t i = 0; i < it.glyphCount(); ++i) { REPORTER_ASSERT(reporter, it.glyphs()[i] == glyphs[i]); } REPORTER_ASSERT(reporter, SkTextBlobRunIterator::kDefault_Positioning == it.positioning()); REPORTER_ASSERT(reporter, (SkPoint{0.0f, 0.0f}) == it.offset()); REPORTER_ASSERT(reporter, it.textSize() > 0); REPORTER_ASSERT(reporter, it.clusters()); for (uint32_t i = 0; i < it.glyphCount(); ++i) { REPORTER_ASSERT(reporter, i == it.clusters()[i]); } REPORTER_ASSERT(reporter, 0 == strncmp(text2, it.text(), it.textSize())); } }
void draw(SkCanvas* canvas) { SkFont font; font.setSize(24); SkPaint canvasPaint; canvasPaint.setColor(SK_ColorBLUE); // respected canvasPaint.setTextSize(2); // ignored sk_sp<SkTextBlob> blob = SkTextBlob::MakeFromString("Hello World", font); canvas->drawTextBlob(blob, 20, 20, canvasPaint); }
static void applyKerning(SkPoint pos[], const int32_t adjustments[], int count, const SkFont& font) { SkScalar scale = font.getSize() / font.getTypefaceOrDefault()->getUnitsPerEm(); SkScalar globalAdj = 0; for (int i = 0; i < count - 1; ++i) { globalAdj += adjustments[i] * scale; pos[i + 1].fX += globalAdj; } }
void draw(SkCanvas* canvas) { SkFont blobFont; blobFont.setSize(24); sk_sp<SkTextBlob> blob = SkTextBlob::MakeFromText("Hello World", 11, blobFont); char storage[2048]; size_t used = blob->serialize(SkSerialProcs(), storage, sizeof(storage)); sk_sp<SkTextBlob> copy = SkTextBlob::Deserialize(storage, used, SkDeserialProcs()); canvas->drawTextBlob(copy, 20, 20, SkPaint()); std::string usage = "size=" + std::to_string(sizeof(storage)) + " used=" + std::to_string(used); canvas->drawString(usage.c_str(), 20, 40, SkPaint()); }
static sk_sp<SkPicture> make_picture(sk_sp<SkTypeface> tf0, sk_sp<SkTypeface> tf1) { SkPictureRecorder rec; SkCanvas* canvas = rec.beginRecording(100, 100); SkPaint paint; SkFont font; font.setTypeface(tf0); canvas->drawString("hello", 0, 0, font, paint); font.setTypeface(tf1); canvas->drawString("hello", 0, 0, font, paint); font.setTypeface(tf0); canvas->drawString("hello", 0, 0, font, paint); font.setTypeface(tf1); canvas->drawString("hello", 0, 0, font, paint); return rec.finishRecordingAsPicture(); }
void draw(SkCanvas* canvas) { SkTextBlobBuilder textBlobBuilder; SkFont font; font.setSize(50); const SkTextBlobBuilder::RunBuffer& run = textBlobBuilder.allocRun(font, 1, 20, 100); run.glyphs[0] = 20; sk_sp<const SkTextBlob> blob = textBlobBuilder.make(); SkPaint paint; paint.setColor(SK_ColorBLUE); canvas->drawTextBlob(blob.get(), 0, 0, paint); }
void onDraw(int loops, SkCanvas* canvas) override { SkFont font; font.setEdging(SkFont::Edging::kAntiAlias); const uint16_t* array = gUniqueGlyphIDs; while (*array != gUniqueGlyphIDs_Sentinel) { int count = count_glyphs(array); for (int i = 0; i < loops; ++i) { (void)font.measureText(array, count * sizeof(uint16_t), SkTextEncoding::kGlyphID); } array += count + 1; // skip the sentinel } }
SkFont GrTextContext::InitDistanceFieldFont(const SkFont& font, const SkMatrix& viewMatrix, const Options& options, SkScalar* textRatio) { SkScalar textSize = font.getSize(); SkScalar scaledTextSize = scaled_text_size(textSize, viewMatrix); SkFont dfFont{font}; if (scaledTextSize <= kSmallDFFontLimit) { *textRatio = textSize / kSmallDFFontSize; dfFont.setSize(SkIntToScalar(kSmallDFFontSize)); } else if (scaledTextSize <= kMediumDFFontLimit) { *textRatio = textSize / kMediumDFFontSize; dfFont.setSize(SkIntToScalar(kMediumDFFontSize)); } else { *textRatio = textSize / kLargeDFFontSize; dfFont.setSize(SkIntToScalar(kLargeDFFontSize)); } dfFont.setEdging(SkFont::Edging::kAntiAlias); dfFont.setForceAutoHinting(false); dfFont.setHinting(SkFontHinting::kNormal); // The sub-pixel position will always happen when transforming to the screen. dfFont.setSubpixel(false); return dfFont; }
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); }
static void draw_scene(SkCanvas* canvas, const SkHighContrastConfig& config) { SkRect bounds = SkRect::MakeLTRB(0.0f, 0.0f, 1.0f, 1.0f); SkPaint xferPaint; xferPaint.setColorFilter(SkHighContrastFilter::Make(config)); canvas->saveLayer(&bounds, &xferPaint); SkPaint paint; bounds = SkRect::MakeLTRB(0.1f, 0.2f, 0.9f, 0.4f); paint.setARGB(0xff, 0x66, 0x11, 0x11); canvas->drawRect(bounds, paint); SkFont font; font.setSize(0.15f); font.setEdging(SkFont::Edging::kAlias); paint.setARGB(0xff, 0xbb, 0x77, 0x77); canvas->drawString("A", 0.15f, 0.35f, font, paint); bounds = SkRect::MakeLTRB(0.1f, 0.8f, 0.9f, 1.0f); paint.setARGB(0xff, 0xcc, 0xcc, 0xff); canvas->drawRect(bounds, paint); paint.setARGB(0xff, 0x88, 0x88, 0xbb); canvas->drawString("Z", 0.75f, 0.95f, font, paint); bounds = SkRect::MakeLTRB(0.1f, 0.4f, 0.9f, 0.6f); SkPoint pts[] = { { 0, 0 }, { 1, 0 } }; SkColor colors[] = { SK_ColorWHITE, SK_ColorBLACK }; SkScalar pos[] = { 0.2f, 0.8f }; paint.setShader(SkGradientShader::MakeLinear( pts, colors, pos, SK_ARRAY_COUNT(colors), SkTileMode::kClamp)); canvas->drawRect(bounds, paint); bounds = SkRect::MakeLTRB(0.1f, 0.6f, 0.9f, 0.8f); SkColor colors2[] = { SK_ColorGREEN, SK_ColorWHITE }; paint.setShader(SkGradientShader::MakeLinear( pts, colors2, pos, SK_ARRAY_COUNT(colors2), SkTileMode::kClamp)); canvas->drawRect(bounds, paint); canvas->restore(); }
static void getGlyphPositions(const SkFont& font, const uint16_t glyphs[], int count, SkScalar x, SkScalar y, SkPoint pos[]) { SkAutoSTMalloc<128, SkScalar> widthStorage(count); SkScalar* widths = widthStorage.get(); font.getWidths(glyphs, count, widths); for (int i = 0; i < count; ++i) { pos[i].set(x, y); x += widths[i]; } }
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 make_big_bitmap(SkBitmap* bm) { static const char gText[] = "We the people, in order to form a more perfect union, establish justice," " ensure domestic tranquility, provide for the common defense, promote the" " general welfare and ensure the blessings of liberty to ourselves and our" " posterity, do ordain and establish this constitution for the United" " States of America."; const int BIG_H = 120; SkFont font; font.setSize(SkIntToScalar(BIG_H)); const int BIG_W = SkScalarRoundToInt(font.measureText(gText, strlen(gText), SkTextEncoding::kUTF8)); bm->allocN32Pixels(BIG_W, BIG_H); bm->eraseColor(SK_ColorWHITE); SkCanvas canvas(*bm); canvas.drawSimpleText(gText, strlen(gText), SkTextEncoding::kUTF8, 0, font.getSize()*4/5, font, SkPaint()); }
void SkTextUtils::GetPath(const void* text, size_t length, SkTextEncoding encoding, SkScalar x, SkScalar y, const SkFont& font, SkPath* path) { SkAutoToGlyphs ag(font, text, length, encoding); SkAutoTArray<SkPoint> pos(ag.count()); font.getPos(ag.glyphs(), ag.count(), pos.get(), {x, y}); struct Rec { SkPath* fDst; const SkPoint* fPos; } rec = { path, pos.get() }; path->reset(); font.getPaths(ag.glyphs(), ag.count(), [](const SkPath* src, const SkMatrix& mx, void* ctx) { Rec* rec = (Rec*)ctx; if (src) { SkMatrix m(mx); m.postTranslate(rec->fPos->fX, rec->fPos->fY); rec->fDst->addPath(*src, m); } rec->fPos += 1; }, &rec); }
void SkTextUtils::Draw(SkCanvas* canvas, const void* text, size_t size, SkTextEncoding encoding, SkScalar x, SkScalar y, const SkFont& font, const SkPaint& paint, Align align) { if (align != kLeft_Align) { SkScalar width = font.measureText(text, size, encoding); if (align == kCenter_Align) { width *= 0.5f; } x -= width; } canvas->drawTextBlob(SkTextBlob::MakeFromText(text, size, font, encoding), x, y, paint); }
static void draw_label(SkCanvas* canvas, const SkHighContrastConfig& config) { char labelBuffer[256]; const char* invertStr = (config.fInvertStyle == InvertStyle::kInvertBrightness ? "InvBrightness" : (config.fInvertStyle == InvertStyle::kInvertLightness ? "InvLightness" : "NoInvert")); snprintf(labelBuffer, sizeof(labelBuffer), "%s%s contrast=%.1f", config.fGrayscale ? "Gray " : "", invertStr, config.fContrast); SkFont font; font.setTypeface(ToolUtils::create_portable_typeface()); font.setSize(0.05f); font.setEdging(SkFont::Edging::kAlias); size_t len = strlen(labelBuffer); SkScalar width = font.measureText(labelBuffer, len, SkTextEncoding::kUTF8); canvas->drawSimpleText(labelBuffer, len, SkTextEncoding::kUTF8, 0.5f - width / 2, 0.16f, font, SkPaint()); }
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); }
bool GrTextContext::CanDrawAsDistanceFields(const SkPaint& paint, const SkFont& font, const SkMatrix& viewMatrix, const SkSurfaceProps& props, bool contextSupportsDistanceFieldText, const Options& options) { if (!viewMatrix.hasPerspective()) { SkScalar maxScale = viewMatrix.getMaxScale(); SkScalar scaledTextSize = maxScale * font.getSize(); // Hinted text looks far better at small resolutions // Scaling up beyond 2x yields undesireable artifacts if (scaledTextSize < options.fMinDistanceFieldFontSize || scaledTextSize > options.fMaxDistanceFieldFontSize) { return false; } bool useDFT = props.isUseDeviceIndependentFonts(); #if SK_FORCE_DISTANCE_FIELD_TEXT useDFT = true; #endif if (!useDFT && scaledTextSize < kLargeDFFontSize) { return false; } } // mask filters modify alpha, which doesn't translate well to distance if (paint.getMaskFilter() || !contextSupportsDistanceFieldText) { return false; } // TODO: add some stroking support if (paint.getStyle() != SkPaint::kFill_Style) { return false; } return true; }
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); } }
virtual void onDrawContent(SkCanvas* canvas) { SkPath path; path.moveTo(SkIntToScalar(0), SkIntToScalar(50)); path.quadTo(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(50), SkIntToScalar(0)); path.lineTo(SkIntToScalar(175), SkIntToScalar(0)); path.quadTo(SkIntToScalar(200), SkIntToScalar(0), SkIntToScalar(200), SkIntToScalar(25)); path.lineTo(SkIntToScalar(200), SkIntToScalar(150)); path.quadTo(SkIntToScalar(200), SkIntToScalar(200), SkIntToScalar(150), SkIntToScalar(200)); path.lineTo(SkIntToScalar(0), SkIntToScalar(200)); path.close(); path.moveTo(SkIntToScalar(50), SkIntToScalar(50)); path.lineTo(SkIntToScalar(150), SkIntToScalar(50)); path.lineTo(SkIntToScalar(150), SkIntToScalar(125)); path.quadTo(SkIntToScalar(150), SkIntToScalar(150), SkIntToScalar(125), SkIntToScalar(150)); path.lineTo(SkIntToScalar(50), SkIntToScalar(150)); path.close(); path.setFillType(SkPath::kEvenOdd_FillType); SkColor pathColor = SK_ColorBLACK; SkPaint pathPaint; pathPaint.setAntiAlias(true); pathPaint.setColor(pathColor); SkPath clipA; clipA.moveTo(SkIntToScalar(10), SkIntToScalar(20)); clipA.lineTo(SkIntToScalar(165), SkIntToScalar(22)); clipA.lineTo(SkIntToScalar(70), SkIntToScalar(105)); clipA.lineTo(SkIntToScalar(165), SkIntToScalar(177)); clipA.lineTo(SkIntToScalar(-5), SkIntToScalar(180)); clipA.close(); SkColor colorA = SK_ColorCYAN; SkPath clipB; clipB.moveTo(SkIntToScalar(40), SkIntToScalar(10)); clipB.lineTo(SkIntToScalar(190), SkIntToScalar(15)); clipB.lineTo(SkIntToScalar(195), SkIntToScalar(190)); clipB.lineTo(SkIntToScalar(40), SkIntToScalar(185)); clipB.lineTo(SkIntToScalar(155), SkIntToScalar(100)); clipB.close(); SkColor colorB = SK_ColorRED; SkFont font; font.setSize(20); SkPaint paint; paint.setAntiAlias(true); paint.setStyle(SkPaint::kStroke_Style); paint.setStrokeWidth(0); canvas->translate(SkIntToScalar(10),SkIntToScalar(10)); canvas->drawPath(path, pathPaint); paint.setColor(colorA); canvas->drawPath(clipA, paint); paint.setColor(colorB); canvas->drawPath(clipB, paint); static const struct { SkClipOp fOp; const char* fName; } gOps[] = { //extra spaces in names for measureText {kIntersect_SkClipOp, "Isect "}, {kDifference_SkClipOp, "Diff " }, {kUnion_SkClipOp, "Union "}, {kXOR_SkClipOp, "Xor " }, {kReverseDifference_SkClipOp, "RDiff "} }; canvas->translate(0, SkIntToScalar(40)); canvas->scale(3 * SK_Scalar1 / 4, 3 * SK_Scalar1 / 4); canvas->save(); for (int invA = 0; invA < 2; ++invA) { for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); ++op) { size_t idx = invA * SK_ARRAY_COUNT(gOps) + op; if (!(idx % 3)) { canvas->restore(); canvas->translate(0, SkIntToScalar(250)); canvas->save(); } canvas->save(); // set clip clipA.setFillType(invA ? SkPath::kInverseEvenOdd_FillType : SkPath::kEvenOdd_FillType); canvas->clipPath(clipA); canvas->clipPath(clipB, gOps[op].fOp); // draw path clipped canvas->drawPath(path, pathPaint); canvas->restore(); // draw path in hairline paint.setColor(pathColor); canvas->drawPath(path, paint); // draw clips in hair line paint.setColor(colorA); canvas->drawPath(clipA, paint); paint.setColor(colorB); canvas->drawPath(clipB, paint); SkScalar txtX = SkIntToScalar(55); paint.setColor(colorA); const char* aTxt = invA ? "InverseA " : "A "; canvas->drawSimpleText(aTxt, strlen(aTxt), SkTextEncoding::kUTF8, txtX, SkIntToScalar(220), font, paint); txtX += font.measureText(aTxt, strlen(aTxt), SkTextEncoding::kUTF8); paint.setColor(SK_ColorBLACK); canvas->drawSimpleText(gOps[op].fName, strlen(gOps[op].fName), SkTextEncoding::kUTF8, txtX, 220, font, paint); txtX += font.measureText(gOps[op].fName, strlen(gOps[op].fName), SkTextEncoding::kUTF8); paint.setColor(colorB); canvas->drawSimpleText("B", 1, SkTextEncoding::kUTF8, txtX, 220, font, paint); canvas->translate(SkIntToScalar(250),0); } } canvas->restore(); }
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(); }
static SkScalar draw_string(SkCanvas* canvas, const SkString& text, SkScalar x, SkScalar y, const SkFont& font) { SkPaint paint; canvas->drawString(text, x, y, font, paint); return x + font.measureText(text.c_str(), text.size(), SkTextEncoding::kUTF8); }
// This unit test verifies blob bounds computation. static void TestBounds(skiatest::Reporter* reporter) { SkTextBlobBuilder builder; SkFont font; // Explicit bounds. { sk_sp<SkTextBlob> blob(builder.make()); REPORTER_ASSERT(reporter, !blob); } { SkRect r1 = SkRect::MakeXYWH(10, 10, 20, 20); builder.allocRun(font, 16, 0, 0, &r1); sk_sp<SkTextBlob> blob(builder.make()); REPORTER_ASSERT(reporter, blob->bounds() == r1); } { SkRect r1 = SkRect::MakeXYWH(10, 10, 20, 20); builder.allocRunPosH(font, 16, 0, &r1); sk_sp<SkTextBlob> blob(builder.make()); REPORTER_ASSERT(reporter, blob->bounds() == r1); } { SkRect r1 = SkRect::MakeXYWH(10, 10, 20, 20); builder.allocRunPos(font, 16, &r1); sk_sp<SkTextBlob> blob(builder.make()); REPORTER_ASSERT(reporter, blob->bounds() == r1); } { SkRect r1 = SkRect::MakeXYWH(10, 10, 20, 20); SkRect r2 = SkRect::MakeXYWH(15, 20, 50, 50); SkRect r3 = SkRect::MakeXYWH(0, 5, 10, 5); builder.allocRun(font, 16, 0, 0, &r1); builder.allocRunPosH(font, 16, 0, &r2); builder.allocRunPos(font, 16, &r3); sk_sp<SkTextBlob> blob(builder.make()); REPORTER_ASSERT(reporter, blob->bounds() == SkRect::MakeXYWH(0, 5, 65, 65)); } { sk_sp<SkTextBlob> blob(builder.make()); REPORTER_ASSERT(reporter, !blob); } // Implicit bounds { // Exercise the empty bounds path, and ensure that RunRecord-aligned pos buffers // don't trigger asserts (http://crbug.com/542643). SkFont font; font.setSize(0); const char* txt = "BOOO"; const size_t txtLen = strlen(txt); const int glyphCount = font.countText(txt, txtLen, SkTextEncoding::kUTF8); const SkTextBlobBuilder::RunBuffer& buffer = builder.allocRunPos(font, glyphCount); font.textToGlyphs(txt, txtLen, SkTextEncoding::kUTF8, buffer.glyphs, glyphCount); memset(buffer.pos, 0, sizeof(SkScalar) * glyphCount * 2); sk_sp<SkTextBlob> blob(builder.make()); REPORTER_ASSERT(reporter, blob->bounds().isEmpty()); } }
// 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(); } }
DrawResult onDraw(SkCanvas* canvas, SkString* errorMsg) override { if (!fAnim) { *errorMsg = "No animation."; return DrawResult::kFail; } SkMatrix44 camera, perspective, mv; SkMatrix viewport; { float w = this->width(); float h = this->height(); float s = std::min(w, h); viewport.setTranslate(1, -1); viewport.postScale(s/2, -s/2); draw_viewport(canvas, viewport); } Sk3Perspective(&perspective, fNear, fFar, fAngle); Sk3LookAt(&camera, fEye, fCOA, fUp); mv.postConcat(camera); mv.postConcat(perspective); SkPoint pts[8]; Sk3MapPts(pts, mv, fP3, 8); viewport.mapPoints(pts, 8); SkPaint paint; paint.setStyle(SkPaint::kStroke_Style); SkFont font; font.setEdging(SkFont::Edging::kAlias); SkPath cube; cube.moveTo(pts[0]); cube.lineTo(pts[2]); cube.lineTo(pts[6]); cube.lineTo(pts[4]); cube.close(); cube.moveTo(pts[1]); cube.lineTo(pts[3]); cube.lineTo(pts[7]); cube.lineTo(pts[5]); cube.close(); cube.moveTo(pts[0]); cube.lineTo(pts[1]); cube.moveTo(pts[2]); cube.lineTo(pts[3]); cube.moveTo(pts[4]); cube.lineTo(pts[5]); cube.moveTo(pts[6]); cube.lineTo(pts[7]); canvas->drawPath(cube, paint); { SkPoint3 src[4] = { { 0, 0, 0 }, { 2, 0, 0 }, { 0, 2, 0 }, { 0, 0, 2 }, }; SkPoint dst[4]; mv.setConcat(perspective, camera); Sk3MapPts(dst, mv, src, 4); viewport.mapPoints(dst, 4); const char* str[3] = { "X", "Y", "Z" }; for (int i = 1; i <= 3; ++i) { canvas->drawLine(dst[0], dst[i], paint); } for (int i = 0; i < 3; ++i) { canvas->drawString(str[i], dst[i + 1].fX, dst[i + 1].fY, font, paint); } } fAnim->seek(fAnimT); draw_skia(canvas, mv, viewport, fAnim.get()); return DrawResult::kOk; }