generate vertex data in onPrepare
Switch to a compact encoding for vertex data
that can be easily expanded in onPrepare.
Bug: skia:10251
Change-Id: I53893c94514a7ff3b4f33be444f4ec2002e63ec4
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/290297
Commit-Queue: Herb Derby <herb@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
Reviewed-by: Ben Wagner <bungeman@google.com>
diff --git a/src/atlastext/SkAtlasTextTarget.cpp b/src/atlastext/SkAtlasTextTarget.cpp
index 4e6c197..6372955 100644
--- a/src/atlastext/SkAtlasTextTarget.cpp
+++ b/src/atlastext/SkAtlasTextTarget.cpp
@@ -242,8 +242,6 @@
auto subRun = fGeoData[i].fSubRunPtr;
subRun->prepareGrGlyphs(context.grContext()->priv().getGrStrikeCache());
// TODO4F: Preserve float colors
- subRun->updateVerticesColorIfNeeded(fGeoData[i].fColor.toBytes_RGBA());
- subRun->translateVerticesIfNeeded(fGeoData[i].fDrawMatrix, fGeoData[i].fDrawOrigin);
GrTextBlob::VertexRegenerator regenerator(resourceProvider, subRun, &context, atlasManager);
int subRunEnd = subRun->glyphCount();
for (int subRunIndex = 0; subRunIndex < subRunEnd;) {
@@ -252,7 +250,10 @@
break;
}
- context.recordDraw(subRun->quadStart(subRunIndex), glyphsRegenerated,
+ std::unique_ptr<GrTextBlob::Mask3DVertex[][4]> vertexData =
+ fGeoData[i].textTargetCreateVertexData(subRunIndex, glyphsRegenerated);
+
+ context.recordDraw(vertexData.get(), glyphsRegenerated,
fGeoData[i].fDrawMatrix, target->handle());
subRunIndex += glyphsRegenerated;
if (subRunIndex != subRunEnd) {
diff --git a/src/gpu/ops/GrAtlasTextOp.cpp b/src/gpu/ops/GrAtlasTextOp.cpp
index cc7611f..8bfda87 100644
--- a/src/gpu/ops/GrAtlasTextOp.cpp
+++ b/src/gpu/ops/GrAtlasTextOp.cpp
@@ -62,6 +62,22 @@
this->setBounds(bounds, HasAABloat::kNo, IsHairline::kNo);
}
+// Entry point just for the SkAtlasTextTarget
+std::unique_ptr<GrTextBlob::Mask3DVertex[][4]> GrAtlasTextOp::Geometry::textTargetCreateVertexData(
+ int offset, int count) const {
+ std::unique_ptr<GrTextBlob::Mask3DVertex[][4]> data{new GrTextBlob::Mask3DVertex[count][4]};
+
+ fSubRunPtr->fillTextTargetVertexData(data.get(), offset, count, fColor.toBytes_RGBA(),
+ fDrawOrigin);
+
+ return data;
+}
+
+void GrAtlasTextOp::Geometry::fillVertexData(void *dst, int offset, int count) const {
+ fSubRunPtr->fillVertexData(dst, offset, count, fColor.toBytes_RGBA(),
+ fDrawMatrix, fDrawOrigin, fClipRect);
+}
+
std::unique_ptr<GrAtlasTextOp> GrAtlasTextOp::MakeBitmap(GrRecordingContext* context,
GrPaint&& paint,
GrTextBlob::SubRun* subrun,
@@ -193,113 +209,6 @@
return analysis;
}
-static void clip_quads(const SkIRect& clipRect, char* currVertex, const char* blobVertices,
- size_t vertexStride, int glyphCount) {
- for (int i = 0; i < glyphCount; ++i) {
- const SkPoint* blobPositionLT = reinterpret_cast<const SkPoint*>(blobVertices);
- const SkPoint* blobPositionRB =
- reinterpret_cast<const SkPoint*>(blobVertices + 3 * vertexStride);
-
- // positions for bitmap glyphs are pixel boundary aligned
- SkIRect positionRect = SkIRect::MakeLTRB(SkScalarRoundToInt(blobPositionLT->fX),
- SkScalarRoundToInt(blobPositionLT->fY),
- SkScalarRoundToInt(blobPositionRB->fX),
- SkScalarRoundToInt(blobPositionRB->fY));
- if (clipRect.contains(positionRect)) {
- memcpy(currVertex, blobVertices, 4 * vertexStride);
- currVertex += 4 * vertexStride;
- } else {
- // Pull out some more data that we'll need.
- // In the LCD case the color will be garbage, but we'll overwrite it with the texcoords
- // and it avoids a lot of conditionals.
- auto color = *reinterpret_cast<const SkColor*>(blobVertices + sizeof(SkPoint));
- size_t coordOffset = vertexStride - 2*sizeof(uint16_t);
- auto* blobCoordsLT = reinterpret_cast<const uint16_t*>(blobVertices + coordOffset);
- auto* blobCoordsRB = reinterpret_cast<const uint16_t*>(blobVertices + 3 * vertexStride +
- coordOffset);
- // Pull out the texel coordinates and texture index bits
- uint16_t coordsRectL = blobCoordsLT[0];
- uint16_t coordsRectT = blobCoordsLT[1];
- uint16_t coordsRectR = blobCoordsRB[0];
- uint16_t coordsRectB = blobCoordsRB[1];
- int index0, index1;
- std::tie(coordsRectL, coordsRectT, index0) =
- GrDrawOpAtlas::UnpackIndexFromTexCoords(coordsRectL, coordsRectT);
- std::tie(coordsRectR, coordsRectB, index1) =
- GrDrawOpAtlas::UnpackIndexFromTexCoords(coordsRectR, coordsRectB);
- SkASSERT(index0 == index1);
-
- int positionRectWidth = positionRect.width();
- int positionRectHeight = positionRect.height();
- SkASSERT(positionRectWidth == (coordsRectR - coordsRectL));
- SkASSERT(positionRectHeight == (coordsRectB - coordsRectT));
-
- // Clip position and texCoords to the clipRect
- unsigned int delta;
- delta = std::min(std::max(clipRect.fLeft - positionRect.fLeft, 0), positionRectWidth);
- coordsRectL += delta;
- positionRect.fLeft += delta;
-
- delta = std::min(std::max(clipRect.fTop - positionRect.fTop, 0), positionRectHeight);
- coordsRectT += delta;
- positionRect.fTop += delta;
-
- delta = std::min(std::max(positionRect.fRight - clipRect.fRight, 0), positionRectWidth);
- coordsRectR -= delta;
- positionRect.fRight -= delta;
-
- delta = std::min(std::max(positionRect.fBottom - clipRect.fBottom, 0), positionRectHeight);
- coordsRectB -= delta;
- positionRect.fBottom -= delta;
-
- // Repack texel coordinates and index
- std::tie(coordsRectL, coordsRectT) =
- GrDrawOpAtlas::PackIndexInTexCoords(coordsRectL, coordsRectT, index0);
- std::tie(coordsRectR, coordsRectB) =
- GrDrawOpAtlas::PackIndexInTexCoords(coordsRectR, coordsRectB, index1);
-
- // Set new positions and coords
- SkPoint* currPosition = reinterpret_cast<SkPoint*>(currVertex);
- currPosition->fX = positionRect.fLeft;
- currPosition->fY = positionRect.fTop;
- *(reinterpret_cast<SkColor*>(currVertex + sizeof(SkPoint))) = color;
- uint16_t* currCoords = reinterpret_cast<uint16_t*>(currVertex + coordOffset);
- currCoords[0] = coordsRectL;
- currCoords[1] = coordsRectT;
- currVertex += vertexStride;
-
- currPosition = reinterpret_cast<SkPoint*>(currVertex);
- currPosition->fX = positionRect.fLeft;
- currPosition->fY = positionRect.fBottom;
- *(reinterpret_cast<SkColor*>(currVertex + sizeof(SkPoint))) = color;
- currCoords = reinterpret_cast<uint16_t*>(currVertex + coordOffset);
- currCoords[0] = coordsRectL;
- currCoords[1] = coordsRectB;
- currVertex += vertexStride;
-
- currPosition = reinterpret_cast<SkPoint*>(currVertex);
- currPosition->fX = positionRect.fRight;
- currPosition->fY = positionRect.fTop;
- *(reinterpret_cast<SkColor*>(currVertex + sizeof(SkPoint))) = color;
- currCoords = reinterpret_cast<uint16_t*>(currVertex + coordOffset);
- currCoords[0] = coordsRectR;
- currCoords[1] = coordsRectT;
- currVertex += vertexStride;
-
- currPosition = reinterpret_cast<SkPoint*>(currVertex);
- currPosition->fX = positionRect.fRight;
- currPosition->fY = positionRect.fBottom;
- *(reinterpret_cast<SkColor*>(currVertex + sizeof(SkPoint))) = color;
- currCoords = reinterpret_cast<uint16_t*>(currVertex + coordOffset);
- currCoords[0] = coordsRectR;
- currCoords[1] = coordsRectB;
- currVertex += vertexStride;
- }
-
- blobVertices += 4 * vertexStride;
- }
-}
-
void GrAtlasTextOp::onPrepareDraws(Target* target) {
auto resourceProvider = target->resourceProvider();
@@ -381,8 +290,6 @@
SkASSERT((int)subRun->vertexStride() == vertexStride);
subRun->prepareGrGlyphs(target->strikeCache());
- subRun->updateVerticesColorIfNeeded(args.fColor.toBytes_RGBA());
- subRun->translateVerticesIfNeeded(args.fDrawMatrix, args.fDrawOrigin);
// TODO4F: Preserve float colors
GrTextBlob::VertexRegenerator regenerator(resourceProvider, subRun,
@@ -409,39 +316,10 @@
// Update all the vertices for glyphsRegenerate glyphs.
if (glyphsRegenerated > 0) {
int quadBufferIndex = totalGlyphsRegened - quadBufferBegin;
- int subRunIndex = totalGlyphsRegened - subRunBegin;
auto regeneratedQuadBuffer =
SkTAddOffset<char>(vertices, subRun->quadOffset(quadBufferIndex));
- if (args.fClipRect.isEmpty()) {
- memcpy(regeneratedQuadBuffer,
- subRun->quadStart(subRunIndex),
- glyphsRegenerated * quadSize);
- } else {
- SkASSERT(!vmPerspective);
- clip_quads(args.fClipRect,
- regeneratedQuadBuffer,
- subRun->quadStart(subRunIndex),
- vertexStride,
- glyphsRegenerated);
- }
- if (fNeedsGlyphTransform && !args.fDrawMatrix.isIdentity()) {
- // We always do the distance field view matrix transformation after copying
- // rather than during blob vertex generation time in the blob as handling
- // successive arbitrary transformations would be complicated and accumulate
- // error.
- if (args.fDrawMatrix.hasPerspective()) {
- auto* pos = reinterpret_cast<SkPoint3*>(regeneratedQuadBuffer);
- SkMatrixPriv::MapHomogeneousPointsWithStride(
- args.fDrawMatrix, pos,
- vertexStride, pos,
- vertexStride,
- glyphsRegenerated * kVerticesPerGlyph);
- } else {
- auto* pos = reinterpret_cast<SkPoint*>(regeneratedQuadBuffer);
- SkMatrixPriv::MapPointsWithStride(args.fDrawMatrix, pos, vertexStride,
- glyphsRegenerated * kVerticesPerGlyph);
- }
- }
+ int subRunIndex = totalGlyphsRegened - subRunBegin;
+ args.fillVertexData(regeneratedQuadBuffer, subRunIndex, glyphsRegenerated);
}
totalGlyphsRegened += glyphsRegenerated;
diff --git a/src/gpu/ops/GrAtlasTextOp.h b/src/gpu/ops/GrAtlasTextOp.h
index 451de49..b8fb9e3 100644
--- a/src/gpu/ops/GrAtlasTextOp.h
+++ b/src/gpu/ops/GrAtlasTextOp.h
@@ -34,6 +34,9 @@
SkPoint fDrawOrigin;
GrTextBlob::SubRun* fSubRunPtr;
SkPMColor4f fColor;
+ std::unique_ptr<GrTextBlob::Mask3DVertex[][4]> textTargetCreateVertexData(
+ int offset, int count) const;
+ void fillVertexData(void* dst, int offset, int count) const;
};
static std::unique_ptr<GrAtlasTextOp> MakeBitmap(GrRecordingContext* context,
diff --git a/src/gpu/text/GrTextBlob.cpp b/src/gpu/text/GrTextBlob.cpp
index d708561..5e3d928 100644
--- a/src/gpu/text/GrTextBlob.cpp
+++ b/src/gpu/text/GrTextBlob.cpp
@@ -41,18 +41,15 @@
// -- GrTextBlob::SubRun ---------------------------------------------------------------------------
GrTextBlob::SubRun::SubRun(SubRunType type, GrTextBlob* textBlob, const SkStrikeSpec& strikeSpec,
- GrMaskFormat format, const SkSpan<PackedGlyphIDorGrGlyph>& glyphs,
- const SkSpan<char>& vertexData)
+ GrMaskFormat format, SkRect vertexBounds,
+ const SkSpan<VertexData>& vertexData)
: fType{type}
, fBlob{textBlob}
, fMaskFormat{format}
- , fVertexData{vertexData}
, fStrikeSpec{strikeSpec}
- , fCurrentColor{textBlob->fColor}
- , fCurrentOrigin{0,0}
- , fCurrentMatrix{textBlob->fInitialMatrix}
- , fGlyphs{glyphs} {
- SkASSERT(type != kTransformedPath);
+ , fVertexBounds(vertexBounds)
+ , fVertexData{vertexData} {
+ SkASSERT(fType != kTransformedPath);
textBlob->insertSubRun(this);
}
@@ -60,129 +57,242 @@
: fType{kTransformedPath}
, fBlob{textBlob}
, fMaskFormat{kA8_GrMaskFormat}
- , fVertexData{SkSpan<char>{}}
, fStrikeSpec{strikeSpec}
- , fCurrentColor{textBlob->fColor}
, fPaths{}
- , fGlyphs{SkSpan<PackedGlyphIDorGrGlyph>{}} {
+ , fVertexBounds(SkRect::MakeEmpty())
+ , fVertexData{SkSpan<VertexData>{}} {
textBlob->insertSubRun(this);
}
-static SkRect dest_rect(const SkGlyph& g, SkPoint origin) {
- return SkRect::MakeXYWH(
- SkIntToScalar(g.left()) + origin.x(),
- SkIntToScalar(g.top()) + origin.y(),
- SkIntToScalar(g.width()),
- SkIntToScalar(g.height()));
-}
-
-static bool is_SDF(const SkGlyph& skGlyph) {
- return skGlyph.maskFormat() == SkMask::kSDF_Format;
-}
-
-static SkRect dest_rect(const SkGlyph& g, SkPoint origin, SkScalar textScale) {
- if (!is_SDF(g)) {
- return SkRect::MakeXYWH(
- SkIntToScalar(g.left()) * textScale + origin.x(),
- SkIntToScalar(g.top()) * textScale + origin.y(),
- SkIntToScalar(g.width()) * textScale,
- SkIntToScalar(g.height()) * textScale);
- } else {
- return SkRect::MakeXYWH(
- (SkIntToScalar(g.left()) + SK_DistanceFieldInset) * textScale + origin.x(),
- (SkIntToScalar(g.top()) + SK_DistanceFieldInset) * textScale + origin.y(),
- (SkIntToScalar(g.width()) - 2 * SK_DistanceFieldInset) * textScale,
- (SkIntToScalar(g.height()) - 2 * SK_DistanceFieldInset) * textScale);
- }
-}
-
-void GrTextBlob::SubRun::appendGlyphs(const SkZip<SkGlyphVariant, SkPoint>& drawables) {
- SkScalar strikeToSource = fStrikeSpec.strikeToSourceRatio();
- SkASSERT(!this->isPrepared());
- PackedGlyphIDorGrGlyph* packedIDCursor = fGlyphs.data();
- char* vertexCursor = fVertexData.data();
- size_t vertexStride = this->vertexStride();
- // We always write the third position component used by SDFs. If it is unused it gets
- // overwritten. Similarly, we always write the color and the blob will later overwrite it
- // with texture coords if it is unused.
- size_t colorOffset = this->colorOffset();
- for (auto [variant, pos] : drawables) {
- SkGlyph* skGlyph = variant;
- // Only floor the device coordinates.
- SkRect dstRect;
- if (!this->needsTransform()) {
- dstRect = dest_rect(*skGlyph, pos);
- } else {
- dstRect = dest_rect(*skGlyph, pos, strikeToSource);
- }
-
- this->joinGlyphBounds(dstRect);
-
- // V0
- *reinterpret_cast<SkPoint3*>(vertexCursor) = {dstRect.fLeft, dstRect.fTop, 1.f};
- *reinterpret_cast<GrColor*>(vertexCursor + colorOffset) = fCurrentColor;
- vertexCursor += vertexStride;
-
- // V1
- *reinterpret_cast<SkPoint3*>(vertexCursor) = {dstRect.fLeft, dstRect.fBottom, 1.f};
- *reinterpret_cast<GrColor*>(vertexCursor + colorOffset) = fCurrentColor;
- vertexCursor += vertexStride;
-
- // V2
- *reinterpret_cast<SkPoint3*>(vertexCursor) = {dstRect.fRight, dstRect.fTop, 1.f};
- *reinterpret_cast<GrColor*>(vertexCursor + colorOffset) = fCurrentColor;
- vertexCursor += vertexStride;
-
- // V3
- *reinterpret_cast<SkPoint3*>(vertexCursor) = {dstRect.fRight, dstRect.fBottom, 1.f};
- *reinterpret_cast<GrColor*>(vertexCursor + colorOffset) = fCurrentColor;
- vertexCursor += vertexStride;
-
- packedIDCursor->fPackedGlyphID = skGlyph->getPackedID();
- packedIDCursor++;
- }
-}
-
void GrTextBlob::SubRun::resetBulkUseToken() { fBulkUseToken.reset(); }
GrDrawOpAtlas::BulkUseTokenUpdater* GrTextBlob::SubRun::bulkUseToken() { return &fBulkUseToken; }
GrTextStrike* GrTextBlob::SubRun::strike() const { return fStrike.get(); }
GrMaskFormat GrTextBlob::SubRun::maskFormat() const { return fMaskFormat; }
-size_t GrTextBlob::SubRun::vertexStride() const {
- return GetVertexStride(this->maskFormat(), this->hasW());
-}
-size_t GrTextBlob::SubRun::colorOffset() const {
- return this->hasW() ? offsetof(Mask3DVertex, color) : offsetof(Mask2DVertex, color);
-}
-size_t GrTextBlob::SubRun::texCoordOffset() const {
- switch (fMaskFormat) {
+size_t GrTextBlob::SubRun::vertexStride() const {
+ switch (this->maskFormat()) {
case kA8_GrMaskFormat:
- return this->hasW() ? offsetof(Mask3DVertex, atlasPos)
- : offsetof(Mask2DVertex, atlasPos);
+ return this->hasW() ? sizeof(Mask3DVertex) : sizeof(Mask2DVertex);
case kARGB_GrMaskFormat:
- return this->hasW() ? offsetof(ARGB3DVertex, atlasPos)
- : offsetof(ARGB2DVertex, atlasPos);
+ return this->hasW() ? sizeof(ARGB3DVertex) : sizeof(ARGB2DVertex);
default:
SkASSERT(!this->hasW());
- return offsetof(Mask2DVertex, atlasPos);
+ return sizeof(Mask2DVertex);
}
-}
-
-char* GrTextBlob::SubRun::quadStart(size_t index) const {
- return SkTAddOffset<char>(fVertexData.data(), this->quadOffset(index));
+ SkUNREACHABLE;
}
size_t GrTextBlob::SubRun::quadOffset(size_t index) const {
return index * kVerticesPerGlyph * this->vertexStride();
}
-int GrTextBlob::SubRun::glyphCount() const {
- return fGlyphs.count();
+template <typename Rect>
+static auto ltbr(const Rect& r) {
+ return std::make_tuple(r.left(), r.top(), r.right(), r.bottom());
}
-void GrTextBlob::SubRun::joinGlyphBounds(const SkRect& glyphBounds) {
- fVertexBounds.joinNonEmptyArg(glyphBounds);
+void GrTextBlob::SubRun::fillVertexData(
+ void *vertexDst, int offset, int count,
+ GrColor color, const SkMatrix& drawMatrix, SkPoint drawOrigin, SkIRect clip) const {
+
+ SkMatrix matrix = drawMatrix;
+ matrix.preTranslate(drawOrigin.x(), drawOrigin.y());
+
+ auto transformed2D = [&](auto dst, SkScalar dstPadding, SkScalar srcPadding) {
+ SkScalar strikeToSource = fStrikeSpec.strikeToSourceRatio();
+ SkPoint inset = {dstPadding, dstPadding};
+ for (auto[quad, vertexData] : SkMakeZip(dst, fVertexData.subspan(offset, count))) {
+ auto[glyph, pos, rect] = vertexData;
+ auto [l, t, r, b] = rect;
+ SkPoint sLT = (SkPoint::Make(l, t) + inset) * strikeToSource + pos,
+ sRB = (SkPoint::Make(r, b) - inset) * strikeToSource + pos;
+ SkPoint lt = matrix.mapXY(sLT.x(), sLT.y()),
+ lb = matrix.mapXY(sLT.x(), sRB.y()),
+ rt = matrix.mapXY(sRB.x(), sLT.y()),
+ rb = matrix.mapXY(sRB.x(), sRB.y());
+ auto[al, at, ar, ab] = glyph.grGlyph->fAtlasLocator.getUVs(srcPadding);
+ quad[0] = {lt, color, {al, at}}; // L,T
+ quad[1] = {lb, color, {al, ab}}; // L,B
+ quad[2] = {rt, color, {ar, at}}; // R,T
+ quad[3] = {rb, color, {ar, ab}}; // R,B
+ }
+ };
+
+ auto transformed3D = [&](auto dst, SkScalar dstPadding, SkScalar srcPadding) {
+ SkScalar strikeToSource = fStrikeSpec.strikeToSourceRatio();
+ SkPoint inset = {dstPadding, dstPadding};
+ auto mapXYZ = [&](SkScalar x, SkScalar y) {
+ SkPoint pt{x, y};
+ SkPoint3 result;
+ matrix.mapHomogeneousPoints(&result, &pt, 1);
+ return result;
+ };
+ for (auto[quad, vertexData] : SkMakeZip(dst, fVertexData.subspan(offset, count))) {
+ auto[glyph, pos, rect] = vertexData;
+ auto [l, t, r, b] = rect;
+ SkPoint sLT = (SkPoint::Make(l, t) + inset) * strikeToSource + pos,
+ sRB = (SkPoint::Make(r, b) - inset) * strikeToSource + pos;
+ SkPoint3 lt = mapXYZ(sLT.x(), sLT.y()),
+ lb = mapXYZ(sLT.x(), sRB.y()),
+ rt = mapXYZ(sRB.x(), sLT.y()),
+ rb = mapXYZ(sRB.x(), sRB.y());
+ auto[al, at, ar, ab] = glyph.grGlyph->fAtlasLocator.getUVs(srcPadding);
+ quad[0] = {lt, color, {al, at}}; // L,T
+ quad[1] = {lb, color, {al, ab}}; // L,B
+ quad[2] = {rt, color, {ar, at}}; // R,T
+ quad[3] = {rb, color, {ar, ab}}; // R,B
+ }
+ };
+
+ auto direct2D = [&](auto dst, SkIRect* clip) {
+ // Rectangles in device space
+ SkPoint originInDeviceSpace = matrix.mapXY(0, 0);
+ for (auto[quad, vertexData] : SkMakeZip(dst, fVertexData.subspan(offset, count))) {
+ auto[glyph, pos, rect] = vertexData;
+ const auto[l, t, r, b] = rect;
+ const auto[fx, fy] = pos + originInDeviceSpace;
+ auto[al, at, ar, ab] = glyph.grGlyph->fAtlasLocator.getUVs(0);
+ if (clip == nullptr) {
+ SkScalar dx = SkScalarRoundToScalar(fx),
+ dy = SkScalarRoundToScalar(fy);
+ auto[dl, dt, dr, db] = SkRect::MakeLTRB(l + dx, t + dy, r + dx, b + dy);
+ quad[0] = {{dl, dt}, color, {al, at}}; // L,T
+ quad[1] = {{dl, db}, color, {al, ab}}; // L,B
+ quad[2] = {{dr, dt}, color, {ar, at}}; // R,T
+ quad[3] = {{dr, db}, color, {ar, ab}}; // R,B
+ } else {
+ int dx = SkScalarRoundToInt(fx),
+ dy = SkScalarRoundToInt(fy);
+ SkIRect devIRect = SkIRect::MakeLTRB(l + dx, t + dy, r + dx, b + dy);
+ SkScalar dl, dt, dr, db;
+ uint16_t tl, tt, tr, tb;
+ if (!clip->containsNoEmptyCheck(devIRect)) {
+ if (SkIRect clipped; clipped.intersect(devIRect, *clip)) {
+ int lD = clipped.left() - devIRect.left();
+ int tD = clipped.top() - devIRect.top();
+ int rD = clipped.right() - devIRect.right();
+ int bD = clipped.bottom() - devIRect.bottom();
+ int indexLT, indexRB;
+ std::tie(dl, dt, dr, db) = ltbr(clipped);
+ std::tie(tl, tt, indexLT) =
+ GrDrawOpAtlas::UnpackIndexFromTexCoords(al, at);
+ std::tie(tr, tb, indexRB) =
+ GrDrawOpAtlas::UnpackIndexFromTexCoords(ar, ab);
+ std::tie(tl, tt) =
+ GrDrawOpAtlas::PackIndexInTexCoords(tl + lD, tt + tD, indexLT);
+ std::tie(tr, tb) =
+ GrDrawOpAtlas::PackIndexInTexCoords(tr + rD, tb + bD, indexRB);
+ } else {
+ // TODO: omit generating any vertex data for fully clipped glyphs ?
+ std::tie(dl, dt, dr, db) = std::make_tuple(0, 0, 0, 0);
+ std::tie(tl, tt, tr, tb) = std::make_tuple(0, 0, 0, 0);
+ }
+
+ } else {
+ std::tie(dl, dt, dr, db) = ltbr(devIRect);
+ std::tie(tl, tt, tr, tb) = std::tie(al, at, ar, ab);
+ }
+ quad[0] = {{dl, dt}, color, {tl, tt}}; // L,T
+ quad[1] = {{dl, db}, color, {tl, tb}}; // L,B
+ quad[2] = {{dr, dt}, color, {tr, tt}}; // R,T
+ quad[3] = {{dr, db}, color, {tr, tb}}; // R,B
+ }
+ }
+ };
+
+ switch (fType) {
+ case kDirectMask: {
+ if (clip.isEmpty()) {
+ if (this->maskFormat() != kARGB_GrMaskFormat) {
+ using Quad = Mask2DVertex[4];
+ SkASSERT(sizeof(Quad) == this->vertexStride() * kVerticesPerGlyph);
+ direct2D((Quad*) vertexDst, nullptr);
+ } else {
+ using Quad = ARGB2DVertex[4];
+ SkASSERT(sizeof(Quad) == this->vertexStride() * kVerticesPerGlyph);
+ direct2D((Quad*) vertexDst, nullptr);
+ }
+ } else {
+ if (this->maskFormat() != kARGB_GrMaskFormat) {
+ using Quad = Mask2DVertex[4];
+ SkASSERT(sizeof(Quad) == this->vertexStride() * kVerticesPerGlyph);
+ direct2D((Quad*) vertexDst, &clip);
+ } else {
+ using Quad = ARGB2DVertex[4];
+ SkASSERT(sizeof(Quad) == this->vertexStride() * kVerticesPerGlyph);
+ direct2D((Quad*) vertexDst, &clip);
+ }
+ }
+ break;
+ }
+ case kTransformedMask: {
+ if (!this->hasW()) {
+ if (this->maskFormat() == GrMaskFormat::kARGB_GrMaskFormat) {
+ using Quad = ARGB2DVertex[4];
+ SkASSERT(sizeof(Quad) == this->vertexStride() * kVerticesPerGlyph);
+ transformed2D((Quad*) vertexDst, 0, 1);
+ } else {
+ using Quad = Mask2DVertex[4];
+ SkASSERT(sizeof(Quad) == this->vertexStride() * kVerticesPerGlyph);
+ transformed2D((Quad*) vertexDst, 0, 1);
+ }
+ } else {
+ if (this->maskFormat() == GrMaskFormat::kARGB_GrMaskFormat) {
+ using Quad = ARGB3DVertex[4];
+ SkASSERT(sizeof(Quad) == this->vertexStride() * kVerticesPerGlyph);
+ transformed3D((Quad*) vertexDst, 0, 1);
+ } else {
+ using Quad = Mask3DVertex[4];
+ SkASSERT(sizeof(Quad) == this->vertexStride() * kVerticesPerGlyph);
+ transformed3D((Quad*) vertexDst, 0, 1);
+ }
+ }
+ break;
+ }
+ case kTransformedSDFT: {
+ if (!this->hasW()) {
+ using Quad = Mask2DVertex[4];
+ SkASSERT(sizeof(Quad) == this->vertexStride() * kVerticesPerGlyph);
+ transformed2D((Quad*) vertexDst, SK_DistanceFieldInset, SK_DistanceFieldInset);
+ } else {
+ using Quad = Mask3DVertex[4];
+ SkASSERT(sizeof(Quad) == this->vertexStride() * kVerticesPerGlyph);
+ transformed3D((Quad*) vertexDst, SK_DistanceFieldInset, SK_DistanceFieldInset);
+ }
+ break;
+ }
+ case kTransformedPath:
+ SK_ABORT("Paths don't generate vertex data.");
+ }
+}
+
+// Note: this method is only used with SkAtlasTextTarget. The SkAtlasTextTarget only uses SDF,
+// and does the rectangle transforms on the GPU. For the normal text execution path see
+// fillVertexData.
+void GrTextBlob::SubRun::fillTextTargetVertexData(
+ Mask3DVertex vertexDst[][4], int offset, int count, GrColor color, SkPoint origin) const {
+ SkScalar strikeToSource = fStrikeSpec.strikeToSourceRatio();
+ SkPoint inset = {SK_DistanceFieldInset, SK_DistanceFieldInset};
+ for (auto[dst, vertexData] : SkMakeZip(vertexDst, fVertexData.subspan(offset, count))) {
+ auto[glyph, pos, rect] = vertexData;
+ auto [l, t, r, b] = rect;
+ SkPoint sLT = (SkPoint::Make(l, t) + inset) * strikeToSource + pos + origin,
+ sRB = (SkPoint::Make(r, b) - inset) * strikeToSource + pos + origin;
+ SkPoint3 lt = SkPoint3{sLT.x(), sLT.y(), 1.f},
+ lb = SkPoint3{sLT.x(), sRB.y(), 1.f},
+ rt = SkPoint3{sRB.x(), sLT.y(), 1.f},
+ rb = SkPoint3{sRB.x(), sRB.y(), 1.f};
+ auto[al, at, ar, ab] = glyph.grGlyph->fAtlasLocator.getUVs(SK_DistanceFieldInset);
+ dst[0] = {lt, color, {al, at}}; // L,T
+ dst[1] = {lb, color, {al, ab}}; // L,B
+ dst[2] = {rt, color, {ar, at}}; // R,T
+ dst[3] = {rb, color, {ar, ab}}; // R,B
+ }
+}
+
+int GrTextBlob::SubRun::glyphCount() const {
+ return fVertexData.count();
}
bool GrTextBlob::SubRun::drawAsDistanceFields() const { return fType == kTransformedSDFT; }
@@ -210,88 +320,8 @@
fStrike = fStrikeSpec.findOrCreateGrStrike(strikeCache);
- for (auto& tmp : fGlyphs) {
- tmp.fGrGlyph = fStrike->getGlyph(tmp.fPackedGlyphID);
- }
-}
-
-void GrTextBlob::SubRun::translateVerticesIfNeeded(
- const SkMatrix& drawMatrix, SkPoint drawOrigin) {
- SkVector translation;
- if (this->needsTransform()) {
- // If transform is needed, then the vertices are in source space, calculate the source
- // space translation.
- translation = drawOrigin - fCurrentOrigin;
- fCurrentOrigin = drawOrigin;
- } else {
- // Calculate the translation in destination space.
- SkPoint newOrigin = drawMatrix.mapXY(drawOrigin.x(), drawOrigin.y());
- translation = newOrigin - fCurrentOrigin;
- fCurrentOrigin = newOrigin;
- }
-
- if (translation != SkPoint{0, 0}) {
- size_t vertexStride = this->vertexStride();
- for (size_t quad = 0; quad < fGlyphs.size(); quad++) {
- SkPoint* vertexCursor = reinterpret_cast<SkPoint*>(quadStart(quad));
- for (int i = 0; i < 4; ++i) {
- if (this->needsTransform()) {
- *vertexCursor += translation;
- } else {
- // This should result in an integer, but floating point is not accurate. This
- // result should be very close to an integer; round to an integer.
- *vertexCursor = {SkScalarRoundToScalar(vertexCursor->x() + translation.x()),
- SkScalarRoundToScalar(vertexCursor->y() + translation.y())};
- }
- vertexCursor = SkTAddOffset<SkPoint>(vertexCursor, vertexStride);
- }
- }
- fCurrentMatrix = drawMatrix;
- }
-}
-
-void GrTextBlob::SubRun::updateVerticesColorIfNeeded(GrColor newColor) {
- if (this->maskFormat() != kARGB_GrMaskFormat && fCurrentColor != newColor) {
- size_t vertexStride = this->vertexStride();
- size_t colorOffset = this->colorOffset();
- for (size_t quad = 0; quad < fGlyphs.size(); quad++) {
- GrColor* colorCursor = SkTAddOffset<GrColor>(quadStart(quad), colorOffset);
- for (int i = 0; i < 4; ++i) {
- *colorCursor = newColor;
- colorCursor = SkTAddOffset<GrColor>(colorCursor, vertexStride);
- }
- }
- this->fCurrentColor = newColor;
- }
-}
-
-void GrTextBlob::SubRun::updateTexCoords(int begin, int end) {
- SkASSERT(this->isPrepared());
-
- const size_t vertexStride = this->vertexStride();
- const size_t texCoordOffset = this->texCoordOffset();
- char* vertex = this->quadStart(begin);
- uint16_t* textureCoords = reinterpret_cast<uint16_t*>(vertex + texCoordOffset);
- for (int i = begin; i < end; i++) {
- GrGlyph* glyph = this->fGlyphs[i].fGrGlyph;
- SkASSERT(glyph != nullptr);
-
- int pad = this->drawAsDistanceFields() ? SK_DistanceFieldInset
- : (this->needsPadding() ? 1 : 0);
- std::array<uint16_t, 4> uvs = glyph->fAtlasLocator.getUVs(pad);
-
- textureCoords[0] = uvs[0];
- textureCoords[1] = uvs[1];
- textureCoords = SkTAddOffset<uint16_t>(textureCoords, vertexStride);
- textureCoords[0] = uvs[0];
- textureCoords[1] = uvs[3];
- textureCoords = SkTAddOffset<uint16_t>(textureCoords, vertexStride);
- textureCoords[0] = uvs[2];
- textureCoords[1] = uvs[1];
- textureCoords = SkTAddOffset<uint16_t>(textureCoords, vertexStride);
- textureCoords[0] = uvs[2];
- textureCoords[1] = uvs[3];
- textureCoords = SkTAddOffset<uint16_t>(textureCoords, vertexStride);
+ for (auto& tmp : fVertexData) {
+ tmp.glyph.grGlyph = fStrike->getGlyph(tmp.glyph.packedGlyphID);
}
}
@@ -313,7 +343,7 @@
}
GrGlyph* GrTextBlob::SubRun::grGlyph(int i) const {
- return fGlyphs[i].fGrGlyph;
+ return fVertexData[i].glyph.grGlyph;
}
void GrTextBlob::SubRun::setUseLCDText(bool useLCDText) { fFlags.useLCDText = useLCDText; }
@@ -333,29 +363,11 @@
const SkMatrix& drawMatrix,
GrColor color,
bool forceWForDistanceFields) {
-
- static_assert(sizeof(ARGB2DVertex) <= sizeof(Mask2DVertex));
- static_assert(alignof(ARGB2DVertex) <= alignof(Mask2DVertex));
- size_t quadSize = sizeof(Mask2DVertex) * kVerticesPerGlyph;
- if (drawMatrix.hasPerspective() || forceWForDistanceFields) {
- static_assert(sizeof(ARGB3DVertex) <= sizeof(Mask3DVertex));
- static_assert(alignof(ARGB3DVertex) <= alignof(Mask3DVertex));
- quadSize = sizeof(Mask3DVertex) * kVerticesPerGlyph;
- }
-
- // We can use the alignment of SDFT3DVertex as a proxy for all Vertex alignments.
- static_assert(alignof(Mask3DVertex) >= alignof(Mask2DVertex));
- // Assume there is no padding needed between glyph pointers and vertices.
- static_assert(alignof(GrGlyph*) >= alignof(Mask3DVertex));
-
- // In the arena, the layout is GrGlyph*... | SDFT3DVertex... | SubRun, so there is no padding
- // between GrGlyph* and SDFT3DVertex, but padding is needed between the Mask2DVertex array
- // and the SubRun.
- size_t vertexToSubRunPadding = alignof(Mask3DVertex) - alignof(SubRun);
- size_t arenaSize =
- sizeof(GrGlyph*) * glyphRunList.totalGlyphCount()
- + quadSize * glyphRunList.totalGlyphCount()
- + glyphRunList.runCount() * (sizeof(SubRun) + vertexToSubRunPadding);
+ // The difference in alignment from the storage of VertexData to SubRun;
+ constexpr size_t alignDiff = alignof(SubRun) - alignof(SubRun::VertexData);
+ constexpr size_t vertexDataToSubRunPadding = alignDiff > 0 ? alignDiff : 0;
+ size_t arenaSize = sizeof(SubRun::VertexData) * glyphRunList.totalGlyphCount()
+ + glyphRunList.runCount() * (sizeof(SubRun) + vertexDataToSubRunPadding);
size_t allocationSize = sizeof(GrTextBlob) + arenaSize;
@@ -398,18 +410,6 @@
fMinMaxScale = std::min(scaledMax, fMinMaxScale);
}
-size_t GrTextBlob::GetVertexStride(GrMaskFormat maskFormat, bool hasWCoord) {
- switch (maskFormat) {
- case kA8_GrMaskFormat:
- return hasWCoord ? sizeof(Mask3DVertex) : sizeof(Mask2DVertex);
- case kARGB_GrMaskFormat:
- return hasWCoord ? sizeof(ARGB3DVertex) : sizeof(ARGB2DVertex);
- default:
- SkASSERT(!hasWCoord);
- return sizeof(Mask2DVertex);
- }
-}
-
bool GrTextBlob::mustRegenerate(const SkPaint& paint, bool anyRunHasSubpixelPosition,
const SkMaskFilterBase::BlurRec& blurRec,
const SkMatrix& drawMatrix, SkPoint drawOrigin) {
@@ -626,18 +626,26 @@
const SkZip<SkGlyphVariant, SkPoint>& drawables,
const SkStrikeSpec& strikeSpec,
GrMaskFormat format) {
- SkSpan<SubRun::PackedGlyphIDorGrGlyph> glyphs{
- fAlloc.makeArrayDefault<SubRun::PackedGlyphIDorGrGlyph>(drawables.size()), drawables.size()};
- bool hasW = this->hasW(type);
+ size_t vertexCount = drawables.size();
+ using Data = SubRun::VertexData;
+ SkRect bounds = SkRectPriv::MakeLargestInverted();
+ auto initializer = [&, strikeToSource=strikeSpec.strikeToSourceRatio()](size_t i) {
+ auto [variant, pos] = drawables[i];
+ SkGlyph* skGlyph = variant;
+ int16_t l = skGlyph->left();
+ int16_t t = skGlyph->top();
+ int16_t r = l + skGlyph->width();
+ int16_t b = t + skGlyph->height();
+ SkPoint lt = SkPoint::Make(l, t) * strikeToSource + pos,
+ rb = SkPoint::Make(r, b) * strikeToSource + pos;
+ bounds.joinNonEmptyArg(SkRect::MakeLTRB(lt.x(), lt.y(), rb.x(), rb.y()));
+ return Data{{skGlyph->getPackedID()}, pos, {l, t, r, b}};
+ };
- SkASSERT(!fInitialMatrix.hasPerspective() || hasW);
+ SkSpan<Data> vertexData{
+ fAlloc.makeInitializedArray<Data>(vertexCount, initializer), vertexCount};
- size_t vertexDataSize = drawables.size() * GetVertexStride(format, hasW) * kVerticesPerGlyph;
- SkSpan<char> vertexData{fAlloc.makeArrayDefault<char>(vertexDataSize), vertexDataSize};
-
- SubRun* subRun = fAlloc.make<SubRun>(type, this, strikeSpec, format, glyphs, vertexData);
-
- subRun->appendGlyphs(drawables);
+ SubRun* subRun = fAlloc.make<SubRun>(type, this, strikeSpec, format, bounds, vertexData);
return subRun;
}
@@ -966,9 +974,6 @@
}
int glyphsPlacedInAtlas = i - begin;
- // Update the quads with the new atlas coordinates.
- fSubRun->updateTexCoords(begin, begin + glyphsPlacedInAtlas);
-
return {code != GrDrawOpAtlas::ErrorCode::kError, glyphsPlacedInAtlas};
}
@@ -987,6 +992,7 @@
// updateTextureCoordinates may have changed it.
fSubRun->fAtlasGeneration = fFullAtlasManager->atlasGeneration(fSubRun->maskFormat());
}
+
return {success, glyphsPlacedInAtlas};
} else {
// The atlas hasn't changed, so our texture coordinates are still valid.
diff --git a/src/gpu/text/GrTextBlob.h b/src/gpu/text/GrTextBlob.h
index f5d6108..9c20be6 100644
--- a/src/gpu/text/GrTextBlob.h
+++ b/src/gpu/text/GrTextBlob.h
@@ -124,8 +124,6 @@
void setHasBitmap();
void setMinAndMaxScale(SkScalar scaledMin, SkScalar scaledMax);
- static size_t GetVertexStride(GrMaskFormat maskFormat, bool hasWCoord);
-
bool mustRegenerate(const SkPaint&, bool, const SkMaskFilterBase::BlurRec& blurRec,
const SkMatrix& drawMatrix, SkPoint drawOrigin);
@@ -321,14 +319,16 @@
// glyphs that are included in them.
class GrTextBlob::SubRun {
public:
- // Within a glyph-based subRun, the glyphs are initially recorded as SkPackedGlyphs. At
- // flush time they are then converted to GrGlyph's (via the GrTextStrike). Once converted
- // they are never converted back.
- union PackedGlyphIDorGrGlyph {
- PackedGlyphIDorGrGlyph() {}
-
- SkPackedGlyphID fPackedGlyphID;
- GrGlyph* fGrGlyph;
+ struct VertexData {
+ union {
+ // Initially, filled with packed id, but changed to GrGlyph* in the onPrepare stage.
+ SkPackedGlyphID packedGlyphID;
+ GrGlyph* grGlyph;
+ } glyph;
+ const SkPoint pos;
+ // The rectangle of the glyphs in strike space. But, for kDirectMask this also implies a
+ // device space rect.
+ GrIRect16 rect;
};
// SubRun for masks
@@ -336,14 +336,12 @@
GrTextBlob* textBlob,
const SkStrikeSpec& strikeSpec,
GrMaskFormat format,
- const SkSpan<PackedGlyphIDorGrGlyph>& glyphs,
- const SkSpan<char>& vertexData);
+ SkRect vertexBounds,
+ const SkSpan<VertexData>& vertexData);
// SubRun for paths
SubRun(GrTextBlob* textBlob, const SkStrikeSpec& strikeSpec);
- void appendGlyphs(const SkZip<SkGlyphVariant, SkPoint>& drawables);
-
// TODO when this object is more internal, drop the privacy
void resetBulkUseToken();
GrDrawOpAtlas::BulkUseTokenUpdater* bulkUseToken();
@@ -352,15 +350,21 @@
GrMaskFormat maskFormat() const;
size_t vertexStride() const;
- size_t colorOffset() const;
- size_t texCoordOffset() const;
- char* quadStart(size_t index) const;
size_t quadOffset(size_t index) const;
+ void fillVertexData(
+ void* vertexDst, int offset, int count,
+ GrColor color, const SkMatrix& drawMatrix, SkPoint drawOrigin,
+ SkIRect clip) const;
+
+ void fillTextTargetVertexData(
+ Mask3DVertex vertexDst[][4],
+ int offset,
+ int count,
+ GrColor color,
+ SkPoint origin) const;
int glyphCount() const;
- void joinGlyphBounds(const SkRect& glyphBounds);
-
bool drawAsDistanceFields() const;
bool drawAsPaths() const;
bool needsTransform() const;
@@ -371,10 +375,6 @@
// has 'prepareGrGlyphs' been called (i.e., can the GrGlyphs be accessed) ?
SkDEBUGCODE(bool isPrepared() const { return SkToBool(fStrike); })
- void translateVerticesIfNeeded(const SkMatrix& drawMatrix, SkPoint drawOrigin);
- void updateVerticesColorIfNeeded(GrColor newColor);
- void updateTexCoords(int begin, int end);
-
// The rectangle that surrounds all the glyph bounding boxes in device space.
SkRect deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const;
@@ -392,28 +392,23 @@
const SubRunType fType;
GrTextBlob* const fBlob;
const GrMaskFormat fMaskFormat;
- const SkSpan<char> fVertexData;
const SkStrikeSpec fStrikeSpec;
sk_sp<GrTextStrike> fStrike;
struct {
bool useLCDText:1;
bool antiAliased:1;
- } fFlags{false, false};
- GrDrawOpAtlas::BulkUseTokenUpdater fBulkUseToken;
+ } fFlags {false, false};
uint64_t fAtlasGeneration{GrDrawOpAtlas::kInvalidAtlasGeneration};
- GrColor fCurrentColor;
- // If the vertex data needTransform(), then fCurrentOrigin is in source space else it is in
- // device space.
- SkPoint fCurrentOrigin;
- SkMatrix fCurrentMatrix;
std::vector<PathGlyph> fPaths;
+
private:
bool hasW() const;
- const SkSpan<PackedGlyphIDorGrGlyph> fGlyphs;
+ GrDrawOpAtlas::BulkUseTokenUpdater fBulkUseToken;
// The vertex bounds in device space if needsTransform() is false, otherwise the bounds in
// source space. The bounds are the joined rectangles of all the glyphs.
- SkRect fVertexBounds = SkRectPriv::MakeLargestInverted();
+ const SkRect fVertexBounds;
+ const SkSpan<VertexData> fVertexData;
}; // SubRun
#endif // GrTextBlob_DEFINED