// Copyright 2021 Google LLC. #include "experimental/sktext/src/Paint.h" namespace skia { namespace text { bool Paint::drawText(std::u16string text, SkCanvas* canvas, SkScalar x, SkScalar y) { SkPaint foregroundPaint(SkColors::kBlack); SkPaint backgroundPaint(SkColors::kWhite); return drawText(std::move(text), canvas, TextDirection::kLtr, TextAlign::kLeft, foregroundPaint, backgroundPaint, SkString("Roboto"), 14, SkFontStyle::Normal(), x, y); } bool Paint::drawText(std::u16string text, SkCanvas* canvas, SkScalar width) { SkPaint foregroundPaint(SkColors::kBlack); SkPaint backgroundPaint(SkColors::kWhite); return drawText(std::move(text), canvas, TextDirection::kLtr, TextAlign::kLeft, foregroundPaint, backgroundPaint, SkString("Roboto"), 14, SkFontStyle::Normal(), SkSize::Make(width, SK_ScalarInfinity), 0, 0); } bool Paint::drawText(std::u16string text, SkCanvas* canvas, TextDirection textDirection, TextAlign textAlign, SkPaint foreground, SkPaint background, const SkString& fontFamily, SkScalar fontSize, SkFontStyle fontStyle, SkScalar x, SkScalar y) { return drawText(std::move(text), canvas, textDirection, textAlign, foreground, background, fontFamily, fontSize, fontStyle, SkSize::Make(SK_ScalarInfinity, SK_ScalarInfinity), x, y); } bool Paint::drawText(std::u16string text, SkCanvas* canvas, TextDirection textDirection, TextAlign textAlign, SkPaint foreground, SkPaint background, const SkString& fontFamily, SkScalar fontSize, SkFontStyle fontStyle, SkSize reqSize, SkScalar x, SkScalar y) { size_t textSize = text.size(); sk_sp fontChain = sk_make_sp(fontFamily.c_str(), fontSize, fontStyle); FontBlock fontBlock(text.size(), fontChain); if (fontChain->getTypeface() == nullptr || fontChain->getTypeface().get() == nullptr) { return false; } auto formattedText = Paint::layout(std::move(text), textDirection, textAlign, reqSize, SkSpan(&fontBlock, 1)); if (formattedText == nullptr) { return false; } DecoratedBlock decoratedBlock(textSize, foreground, background); Paint paint; paint.paint(canvas, SkPoint::Make(x, y), nullptr, formattedText.get(), SkSpan(&decoratedBlock, 1)); return true; } void Paint::onGlyphRun(const SkFont& font, DirTextRange dirTextRange, SkRect bounds, TextIndex trailingSpaces, size_t glyphCount, const uint16_t glyphs[], const SkPoint positions[], const TextIndex clusters[]) { DecoratedBlock decoratedBlock = findDecoratedBlock(dirTextRange); SkTextBlobBuilder builder; const auto& blobBuffer = builder.allocRunPos(font , SkToInt(glyphCount)); sk_careful_memcpy(blobBuffer.glyphs, glyphs, glyphCount * sizeof(uint16_t)); sk_careful_memcpy(blobBuffer.points(), positions, glyphCount * sizeof(SkPoint)); auto blob = builder.make(); if (!decoratedBlock.backgroundPaint.nothingToDraw()) { bounds.offset(fXY.fX, fXY.fY); fCanvas->drawRect(bounds, decoratedBlock.backgroundPaint); } fCanvas->drawTextBlob(blob, fXY.fX, fXY.fY, decoratedBlock.foregroundPaint); } std::unique_ptr Paint::layout(std::u16string text, TextDirection textDirection, TextAlign textAlign, SkSize reqSize, SkSpan fontBlocks) { auto unicode = SkUnicode::Make(); auto unicodeText = std::make_unique(std::move(unicode), SkSpan((uint16_t*)text.data(), text.size())); auto fontResolvedText = unicodeText->resolveFonts(fontBlocks); auto shapedText = fontResolvedText->shape(unicodeText.get(), TextDirection::kLtr); auto wrappedText = shapedText->wrap(unicodeText.get(), reqSize.width(), reqSize.height()); wrappedText->format(textAlign, textDirection); return wrappedText; } DecoratedBlock Paint::findDecoratedBlock(TextRange textRange) { TextIndex start = 0; for (auto& block : fDecoratedBlocks) { if (start + block.charCount <= textRange.fStart) { start += block.charCount; continue; } else if (start >= textRange.fEnd) { break; } return block; } return DecoratedBlock(0, SkPaint(), SkPaint()); } void Paint::paint(SkCanvas* canvas, SkPoint xy, UnicodeText* unicodeText, WrappedText* wrappedText, SkSpan decoratedBlocks) { fCanvas = canvas; fXY = xy; fDecoratedBlocks = decoratedBlocks; SkTArray chunks; chunks.resize(decoratedBlocks.size()); size_t index = 0; for (size_t i = 0; i < decoratedBlocks.size(); ++i) { index += decoratedBlocks[i].charCount; chunks[i] = index; } if (chunks.size() == 1) { wrappedText->visit( this); } else { wrappedText->visit(unicodeText, this, PositionType::kGraphemeCluster, SkSpan(chunks.data(), chunks.size())); } } } // namespace text } // namespace skia