diff options
Diffstat (limited to 'chromium/third_party/skia/src/text/GlyphRun.cpp')
-rw-r--r-- | chromium/third_party/skia/src/text/GlyphRun.cpp | 382 |
1 files changed, 382 insertions, 0 deletions
diff --git a/chromium/third_party/skia/src/text/GlyphRun.cpp b/chromium/third_party/skia/src/text/GlyphRun.cpp new file mode 100644 index 00000000000..d230fddb797 --- /dev/null +++ b/chromium/third_party/skia/src/text/GlyphRun.cpp @@ -0,0 +1,382 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "src/text/GlyphRun.h" + +#include "include/core/SkFont.h" +#include "include/core/SkPaint.h" +#include "include/core/SkRSXform.h" +#include "include/core/SkTextBlob.h" +#include "src/core/SkDevice.h" +#include "src/core/SkFontPriv.h" +#include "src/core/SkScalerCache.h" +#include "src/core/SkStrikeCache.h" +#include "src/core/SkStrikeSpec.h" +#include "src/core/SkTextBlobPriv.h" +#include "src/core/SkUtils.h" + +namespace sktext { +// -- GlyphRun ------------------------------------------------------------------------------------- +GlyphRun::GlyphRun(const SkFont& font, + SkSpan<const SkPoint> positions, + SkSpan<const SkGlyphID> glyphIDs, + SkSpan<const char> text, + SkSpan<const uint32_t> clusters, + SkSpan<const SkVector> scaledRotations) + : fSource{SkMakeZip(glyphIDs, positions)} + , fText{text} + , fClusters{clusters} + , fScaledRotations{scaledRotations} + , fFont{font} {} + +GlyphRun::GlyphRun(const GlyphRun& that, const SkFont& font) + : fSource{that.fSource} + , fText{that.fText} + , fClusters{that.fClusters} + , fFont{font} {} + +SkRect GlyphRun::sourceBounds(const SkPaint& paint) const { + SkASSERT(this->runSize() > 0); + const SkRect fontBounds = SkFontPriv::GetFontBounds(fFont); + + if (fontBounds.isEmpty()) { + // Empty font bounds are likely a font bug. TightBounds has a better chance of + // producing useful results in this case. + auto [strikeSpec, strikeToSourceScale] = SkStrikeSpec::MakeCanonicalized(fFont, &paint); + SkBulkGlyphMetrics metrics{strikeSpec}; + SkSpan<const SkGlyph*> glyphs = metrics.glyphs(this->glyphsIDs()); + if (fScaledRotations.empty()) { + // No RSXForm data - glyphs x/y aligned. + auto scaleAndTranslateRect = + [scale = strikeToSourceScale](const SkRect& in, const SkPoint& pos) { + return SkRect::MakeLTRB(in.left() * scale + pos.x(), + in.top() * scale + pos.y(), + in.right() * scale + pos.x(), + in.bottom() * scale + pos.y()); + }; + + SkRect bounds = SkRect::MakeEmpty(); + for (auto [pos, glyph] : SkMakeZip(this->positions(), glyphs)) { + if (SkRect r = glyph->rect(); !r.isEmpty()) { + bounds.join(scaleAndTranslateRect(r, pos)); + } + } + return bounds; + } else { + // RSXForm - glyphs can be any scale or rotation. + SkRect bounds = SkRect::MakeEmpty(); + for (auto [pos, scaleRotate, glyph] : + SkMakeZip(this->positions(), fScaledRotations, glyphs)) { + if (!glyph->rect().isEmpty()) { + SkMatrix xform = SkMatrix().setRSXform( + SkRSXform{pos.x(), pos.y(), scaleRotate.x(), scaleRotate.y()}); + xform.preScale(strikeToSourceScale, strikeToSourceScale); + bounds.join(xform.mapRect(glyph->rect())); + } + } + return bounds; + } + } + + // Use conservative bounds. All glyph have a box of fontBounds size. + if (fScaledRotations.empty()) { + SkRect bounds; + bounds.setBounds(this->positions().data(), SkCount(this->positions())); + bounds.fLeft += fontBounds.left(); + bounds.fTop += fontBounds.top(); + bounds.fRight += fontBounds.right(); + bounds.fBottom += fontBounds.bottom(); + return bounds; + } else { + // RSXForm case glyphs can be any scale or rotation. + SkRect bounds; + bounds.setEmpty(); + for (auto [pos, scaleRotate] : SkMakeZip(this->positions(), fScaledRotations)) { + const SkRSXform xform{pos.x(), pos.y(), scaleRotate.x(), scaleRotate.y()}; + bounds.join(SkMatrix().setRSXform(xform).mapRect(fontBounds)); + } + return bounds; + } +} + +// -- GlyphRunList --------------------------------------------------------------------------------- +GlyphRunList::GlyphRunList(const SkTextBlob* blob, + SkRect bounds, + SkPoint origin, + SkSpan<const GlyphRun> glyphRunList, + GlyphRunBuilder* builder) + : fGlyphRuns{glyphRunList} + , fOriginalTextBlob{blob} + , fSourceBounds{bounds} + , fOrigin{origin} + , fBuilder{builder} {} + +GlyphRunList::GlyphRunList(const GlyphRun& glyphRun, + const SkRect& bounds, + SkPoint origin, + GlyphRunBuilder* builder) + : fGlyphRuns{SkSpan<const GlyphRun>{&glyphRun, 1}} + , fOriginalTextBlob{nullptr} + , fSourceBounds{bounds} + , fOrigin{origin} + , fBuilder{builder} {} + +uint64_t GlyphRunList::uniqueID() const { + return fOriginalTextBlob != nullptr ? fOriginalTextBlob->uniqueID() + : SK_InvalidUniqueID; +} + +bool GlyphRunList::anyRunsLCD() const { + for (const auto& r : fGlyphRuns) { + if (r.font().getEdging() == SkFont::Edging::kSubpixelAntiAlias) { + return true; + } + } + return false; +} + +void GlyphRunList::temporaryShuntBlobNotifyAddedToCache(uint32_t cacheID) const { + SkASSERT(fOriginalTextBlob != nullptr); + fOriginalTextBlob->notifyAddedToCache(cacheID); +} + +sk_sp<SkTextBlob> GlyphRunList::makeBlob() const { + SkTextBlobBuilder builder; + for (auto& run : *this) { + SkTextBlobBuilder::RunBuffer buffer; + if (run.scaledRotations().empty()) { + if (run.text().empty()) { + buffer = builder.allocRunPos(run.font(), run.runSize(), nullptr); + } else { + buffer = builder.allocRunTextPos(run.font(), run.runSize(), run.text().size(), nullptr); + auto text = run.text(); + memcpy(buffer.utf8text, text.data(), text.size_bytes()); + auto clusters = run.clusters(); + memcpy(buffer.clusters, clusters.data(), clusters.size_bytes()); + } + auto positions = run.positions(); + memcpy(buffer.points(), positions.data(), positions.size_bytes()); + } else { + buffer = builder.allocRunRSXform(run.font(), run.runSize()); + for (auto [xform, pos, sr] : SkMakeZip(buffer.xforms(), + run.positions(), + run.scaledRotations())) { + xform = SkRSXform::Make(sr.x(), sr.y(), pos.x(), pos.y()); + } + } + auto glyphIDs = run.glyphsIDs(); + memcpy(buffer.glyphs, glyphIDs.data(), glyphIDs.size_bytes()); + } + return builder.make(); +} + +// -- GlyphRunBuilder ------------------------------------------------------------------------------ +GlyphRunList GlyphRunBuilder::makeGlyphRunList( + const GlyphRun& run, SkRect bounds, SkPoint origin) { + return GlyphRunList{run, bounds, origin, this}; +} + +static SkSpan<const SkPoint> draw_text_positions( + const SkFont& font, SkSpan<const SkGlyphID> glyphIDs, SkPoint origin, SkPoint* buffer) { + SkStrikeSpec strikeSpec = SkStrikeSpec::MakeWithNoDevice(font); + SkBulkGlyphMetrics storage{strikeSpec}; + auto glyphs = storage.glyphs(glyphIDs); + + SkPoint* positionCursor = buffer; + SkPoint endOfLastGlyph = origin; + for (auto glyph : glyphs) { + *positionCursor++ = endOfLastGlyph; + endOfLastGlyph += glyph->advanceVector(); + } + return SkSpan(buffer, glyphIDs.size()); +} + +const GlyphRunList& GlyphRunBuilder::textToGlyphRunList( + const SkFont& font, const SkPaint& paint, + const void* bytes, size_t byteLength, SkPoint origin, + SkTextEncoding encoding) { + auto glyphIDs = textToGlyphIDs(font, bytes, byteLength, encoding); + SkRect bounds = SkRect::MakeEmpty(); + this->prepareBuffers(glyphIDs.size(), 0); + if (!glyphIDs.empty()) { + SkSpan<const SkPoint> positions = draw_text_positions(font, glyphIDs, {0, 0}, fPositions); + this->makeGlyphRun(font, + glyphIDs, + positions, + SkSpan<const char>{}, + SkSpan<const uint32_t>{}, + SkSpan<const SkVector>{}); + bounds = fGlyphRunListStorage.front().sourceBounds(paint); + } + + return this->setGlyphRunList(nullptr, bounds.makeOffset(origin), origin); +} + +const GlyphRunList& sktext::GlyphRunBuilder::blobToGlyphRunList( + const SkTextBlob& blob, SkPoint origin) { + // Pre-size all the buffers, so they don't move during processing. + this->initialize(blob); + + SkPoint* positionCursor = fPositions; + SkVector* scaledRotationsCursor = fScaledRotations; + for (SkTextBlobRunIterator it(&blob); !it.done(); it.next()) { + size_t runSize = it.glyphCount(); + if (runSize == 0 || !SkFontPriv::IsFinite(it.font())) { + // If no glyphs or the font is not finite, don't add the run. + continue; + } + + const SkFont& font = it.font(); + auto glyphIDs = SkSpan<const SkGlyphID>{it.glyphs(), runSize}; + + SkSpan<const SkPoint> positions; + SkSpan<const SkVector> scaledRotations; + switch (it.positioning()) { + case SkTextBlobRunIterator::kDefault_Positioning: { + positions = draw_text_positions(font, glyphIDs, it.offset(), positionCursor); + positionCursor += positions.size(); + break; + } + case SkTextBlobRunIterator::kHorizontal_Positioning: { + positions = SkSpan(positionCursor, runSize); + for (auto x : SkSpan<const SkScalar>{it.pos(), glyphIDs.size()}) { + *positionCursor++ = SkPoint::Make(x, it.offset().y()); + } + break; + } + case SkTextBlobRunIterator::kFull_Positioning: { + positions = SkSpan(it.points(), runSize); + break; + } + case SkTextBlobRunIterator::kRSXform_Positioning: { + positions = SkSpan(positionCursor, runSize); + scaledRotations = SkSpan(scaledRotationsCursor, runSize); + for (const SkRSXform& xform : SkSpan(it.xforms(), runSize)) { + *positionCursor++ = {xform.fTx, xform.fTy}; + *scaledRotationsCursor++ = {xform.fSCos, xform.fSSin}; + } + break; + } + } + + const uint32_t* clusters = it.clusters(); + this->makeGlyphRun( + font, + glyphIDs, + positions, + SkSpan<const char>(it.text(), it.textSize()), + SkSpan<const uint32_t>(clusters, clusters ? runSize : 0), + scaledRotations); + } + + return this->setGlyphRunList(&blob, blob.bounds().makeOffset(origin), origin); +} + +std::tuple<SkSpan<const SkPoint>, SkSpan<const SkVector>> +GlyphRunBuilder::convertRSXForm(SkSpan<const SkRSXform> xforms) { + const int count = SkCount(xforms); + this->prepareBuffers(count, count); + auto positions = SkSpan(fPositions.get(), count); + auto scaledRotations = SkSpan(fScaledRotations.get(), count); + for (auto [pos, sr, xform] : SkMakeZip(positions, scaledRotations, xforms)) { + auto [scos, ssin, tx, ty] = xform; + pos = {tx, ty}; + sr = {scos, ssin}; + } + return {positions, scaledRotations}; +} + +void GlyphRunBuilder::initialize(const SkTextBlob& blob) { + int positionCount = 0; + int rsxFormCount = 0; + for (SkTextBlobRunIterator it(&blob); !it.done(); it.next()) { + if (it.positioning() != SkTextBlobRunIterator::kFull_Positioning) { + positionCount += it.glyphCount(); + } + if (it.positioning() == SkTextBlobRunIterator::kRSXform_Positioning) { + rsxFormCount += it.glyphCount(); + } + } + + prepareBuffers(positionCount, rsxFormCount); +} + +void GlyphRunBuilder::prepareBuffers(int positionCount, int RSXFormCount) { + if (positionCount > fMaxTotalRunSize) { + fMaxTotalRunSize = positionCount; + fPositions.reset(fMaxTotalRunSize); + } + + if (RSXFormCount > fMaxScaledRotations) { + fMaxScaledRotations = RSXFormCount; + fScaledRotations.reset(RSXFormCount); + } + + fGlyphRunListStorage.clear(); +} + +SkSpan<const SkGlyphID> GlyphRunBuilder::textToGlyphIDs( + const SkFont& font, const void* bytes, size_t byteLength, SkTextEncoding encoding) { + if (encoding != SkTextEncoding::kGlyphID) { + int count = font.countText(bytes, byteLength, encoding); + if (count > 0) { + fScratchGlyphIDs.resize(count); + font.textToGlyphs(bytes, byteLength, encoding, fScratchGlyphIDs.data(), count); + return SkSpan(fScratchGlyphIDs); + } else { + return SkSpan<const SkGlyphID>(); + } + } else { + return SkSpan<const SkGlyphID>((const SkGlyphID*)bytes, byteLength / 2); + } +} + +void GlyphRunBuilder::makeGlyphRun( + const SkFont& font, + SkSpan<const SkGlyphID> glyphIDs, + SkSpan<const SkPoint> positions, + SkSpan<const char> text, + SkSpan<const uint32_t> clusters, + SkSpan<const SkVector> scaledRotations) { + + // Ignore empty runs. + if (!glyphIDs.empty()) { + fGlyphRunListStorage.emplace_back( + font, + positions, + glyphIDs, + text, + clusters, + scaledRotations); + } +} + +const GlyphRunList& sktext::GlyphRunBuilder::setGlyphRunList( + const SkTextBlob* blob, const SkRect& bounds, SkPoint origin) { + fGlyphRunList.emplace(blob, bounds, origin, SkSpan(fGlyphRunListStorage), this); + return fGlyphRunList.value(); +} + +// -- SKSubRunBuffers ------------------------------------------------------------------------------ +auto SkSubRunBuffers::EnsureBuffers(const GlyphRunList& glyphRunList) -> ScopedBuffers { + size_t size = 0; + for (const GlyphRun& run : glyphRunList) { + size = std::max(run.runSize(), size); + } + return ScopedBuffers(glyphRunList.buffers(), size); +} + +SkSubRunBuffers::ScopedBuffers::ScopedBuffers(SkSubRunBuffers* buffers, size_t size) + : fBuffers{buffers} { + fBuffers->fAccepted.ensureSize(size); +} + +SkSubRunBuffers::ScopedBuffers::~ScopedBuffers() { + fBuffers->fAccepted.reset(); + fBuffers->fRejected.reset(); +} +} // namespace sktext |