add drawGlyphs for SkRSXform

Add a drawGlyphs to SkCanvas that takes SkRSXform instead of
positions. Update buffer sizing calculations to take
SkRSXform buffers into account.

Change-Id: I14529088199dcd0b1ae78b4605e1ba77fec2000e
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/399096
Reviewed-by: Ben Wagner <bungeman@google.com>
Reviewed-by: Mike Reed <reed@google.com>
Commit-Queue: Herb Derby <herb@google.com>
This commit is contained in:
Herb Derby 2021-04-21 10:57:25 -04:00 committed by Skia Commit-Bot
parent 29c06bc82a
commit f9cf1aa2ca
5 changed files with 121 additions and 35 deletions

View File

@ -9,7 +9,10 @@
#include "include/core/SkCanvas.h"
#include "include/core/SkFont.h"
#include "include/core/SkPaint.h"
#include "include/core/SkRSXform.h"
#include "include/private/SkTDArray.h"
#include "src/core/SkSpan.h"
#include "src/core/SkZip.h"
#include "tools/ToolUtils.h"
static const char gText[] = "Call me Ishmael. Some years ago—never mind how long precisely";
@ -21,7 +24,7 @@ public:
fFont = SkFont(fTypeface);
fFont.setSubpixel(true);
fFont.setSize(18);
size_t txtLen = strlen(gText);
const size_t txtLen = strlen(gText);
fGlyphCount = fFont.countText(gText, txtLen, SkTextEncoding::kUTF8);
fGlyphs.append(fGlyphCount);
@ -29,6 +32,19 @@ public:
fPositions.append(fGlyphCount);
fFont.getPos(fGlyphs.begin(), fGlyphCount, fPositions.begin());
auto positions = SkSpan(fPositions.begin(), fGlyphCount);
fLength = positions.back().x() - positions.front().x();
fRadius = fLength / SK_FloatPI;
fXforms.append(fGlyphCount);
for (auto [xform, pos] : SkMakeZip(fXforms.begin(), positions)) {
const SkScalar lengthToGlyph = pos.x() - positions.front().x();
const SkScalar angle = SK_FloatPI * (fLength - lengthToGlyph) / fLength;
const SkScalar cos = std::cos(angle);
const SkScalar sin = std::sin(angle);
xform = SkRSXform::Make(sin, cos, fRadius*cos, -fRadius*sin);
}
}
SkString onShortName() override {
@ -53,6 +69,9 @@ public:
canvas->drawGlyphs(fGlyphCount, fGlyphs.begin(), fPositions.begin(), {50, 640}, fFont,
SkPaint{});
canvas->drawGlyphs(fGlyphCount, fGlyphs.begin(), fXforms.begin(),
{50 + fLength / 2, 160 + fRadius}, fFont, SkPaint{});
// TODO: add tests for cluster versions of drawGlyphs.
}
@ -60,8 +79,11 @@ private:
sk_sp<SkTypeface> fTypeface;
SkFont fFont;
SkTDArray<SkGlyphID> fGlyphs;
SkTDArray<SkPoint> fPositions;
SkTDArray<SkPoint> fPositions;
SkTDArray<SkRSXform> fXforms;
int fGlyphCount;
SkScalar fRadius;
SkScalar fLength;
};
DEF_GM(return new DrawGlyphsGM{};)

View File

@ -1782,10 +1782,31 @@ public:
@param font typeface, text size and so, used to describe the text
@param paint blend, color, and so on, used to draw
*/
void drawGlyphs(int count, const SkGlyphID glyphs[], const SkPoint positions[],
SkPoint origin, const SkFont& font, const SkPaint& paint);
/** Draws count glyphs, at positions relative to origin styled with font and paint.
This function draw glyphs using the given scaling and rotations. They are positioned
relative to the given origin. It does not perform typeface fallback for glyphs not found
in the SkTypeface in font.
The drawing obeys the current transform matrix and clipping.
All elements of paint: SkPathEffect, SkMaskFilter, SkShader,
SkColorFilter, and SkImageFilter; apply to text. By
default, draws filled black glyphs.
@param count number of glyphs to draw
@param glyphs the array of glyphIDs to draw
@param xforms where to draw and orient each glyph
@param origin the origin of all the positions
@param font typeface, text size and so, used to describe the text
@param paint blend, color, and so on, used to draw
*/
void drawGlyphs(int count, const SkGlyphID glyphs[], const SkRSXform xforms[],
SkPoint origin, const SkFont& font, const SkPaint& paint);
/** Draws SkTextBlob blob at (x, y), using clip, SkMatrix, and SkPaint paint.
blob contains glyphs, their positions, and paint attributes specific to text:

View File

@ -2283,6 +2283,27 @@ void SkCanvas::drawSimpleText(const void* text, size_t byteLength, SkTextEncodin
}
}
void SkCanvas::drawGlyphs(int count, const SkGlyphID* glyphs, const SkPoint* positions,
const uint32_t* clusters, int textByteCount, const char* utf8text,
SkPoint origin, const SkFont& font, const SkPaint& paint) {
if (count <= 0) { return; }
SkGlyphRun glyphRun {
font,
SkSpan(positions, count),
SkSpan(glyphs, count),
SkSpan(utf8text, textByteCount),
SkSpan(clusters, count),
SkSpan<SkVector>()
};
SkGlyphRunList glyphRunList {
glyphRun,
glyphRun.sourceBounds(paint).makeOffset(origin),
origin
};
this->onDrawGlyphRunList(glyphRunList, paint);
}
void SkCanvas::drawGlyphs(int count, const SkGlyphID glyphs[], const SkPoint positions[],
SkPoint origin, const SkFont& font, const SkPaint& paint) {
if (count <= 0) { return; }
@ -2303,23 +2324,24 @@ void SkCanvas::drawGlyphs(int count, const SkGlyphID glyphs[], const SkPoint pos
this->onDrawGlyphRunList(glyphRunList, paint);
}
void SkCanvas::drawGlyphs(int count, const SkGlyphID* glyphs, const SkPoint* positions,
const uint32_t* clusters, int textByteCount, const char* utf8text,
void SkCanvas::drawGlyphs(int count, const SkGlyphID glyphs[], const SkRSXform xforms[],
SkPoint origin, const SkFont& font, const SkPaint& paint) {
if (count <= 0) { return; }
auto [positions, rotateScales] = fScratchGlyphRunBuilder->convertRSXForm(SkSpan(xforms, count));
SkGlyphRun glyphRun {
font,
SkSpan(positions, count),
SkSpan(glyphs, count),
SkSpan(utf8text, textByteCount),
SkSpan(clusters, count),
SkSpan<SkVector>()
font,
positions,
SkSpan(glyphs, count),
SkSpan<const char>(),
SkSpan<const uint32_t>(),
rotateScales
};
SkGlyphRunList glyphRunList {
glyphRun,
glyphRun.sourceBounds(paint).makeOffset(origin),
origin
glyphRun,
glyphRun.sourceBounds(paint).makeOffset(origin),
origin
};
this->onDrawGlyphRunList(glyphRunList, paint);
}

View File

@ -145,19 +145,28 @@ sk_sp<SkTextBlob> SkGlyphRunList::makeBlob() const {
SkTextBlobBuilder builder;
for (auto& run : *this) {
SkTextBlobBuilder::RunBuffer buffer;
if (run.text().empty()) {
buffer = builder.allocRunPos(run.font(), run.runSize(), nullptr);
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.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());
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());
auto positions = run.positions();
memcpy(buffer.points(), positions.data(), positions.size_bytes());
}
return builder.make();
}
@ -185,7 +194,7 @@ const SkGlyphRunList& SkGlyphRunBuilder::textToGlyphRunList(
auto glyphIDs = textToGlyphIDs(font, bytes, byteLength, encoding);
SkRect bounds = SkRect::MakeEmpty();
if (!glyphIDs.empty()) {
this->initialize(glyphIDs.size());
this->prepareBuffers(glyphIDs.size(), 0);
SkSpan<const SkPoint> positions = draw_text_positions(font, glyphIDs, {0, 0}, fPositions);
this->makeGlyphRun(font,
glyphIDs,
@ -258,14 +267,18 @@ const SkGlyphRunList& SkGlyphRunBuilder::blobToGlyphRunList(
return this->makeGlyphRunList(&blob, blob.bounds().makeOffset(origin), origin);
}
void SkGlyphRunBuilder::initialize(int totalRunSize) {
if (totalRunSize > fMaxTotalRunSize) {
fMaxTotalRunSize = totalRunSize;
fPositions.reset(fMaxTotalRunSize);
std::tuple<SkSpan<const SkPoint>, SkSpan<const SkVector>>
SkGlyphRunBuilder::convertRSXForm(SkSpan<const SkRSXform> xforms) {
const int count = xforms.count();
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};
}
fGlyphRunListStorage.clear();
return {positions, scaledRotations};
}
void SkGlyphRunBuilder::initialize(const SkTextBlob& blob) {
@ -280,14 +293,18 @@ void SkGlyphRunBuilder::initialize(const SkTextBlob& blob) {
}
}
prepareBuffers(positionCount, rsxFormCount);
}
void SkGlyphRunBuilder::prepareBuffers(int positionCount, int RSXFormCount) {
if (positionCount > fMaxTotalRunSize) {
fMaxTotalRunSize = positionCount;
fPositions.reset(fMaxTotalRunSize);
}
if (rsxFormCount > fMaxScaledRotations) {
fMaxScaledRotations = rsxFormCount;
fScaledRotations.reset(rsxFormCount);
if (RSXFormCount > fMaxScaledRotations) {
fMaxScaledRotations = RSXFormCount;
fScaledRotations.reset(RSXFormCount);
}
fGlyphRunListStorage.clear();

View File

@ -14,6 +14,7 @@
#include "include/core/SkFont.h"
#include "include/core/SkPaint.h"
#include "include/core/SkPoint.h"
#include "include/core/SkRSXform.h"
#include "include/core/SkTypes.h"
#include "include/private/SkTemplates.h"
#include "src/core/SkSpan.h"
@ -127,12 +128,15 @@ public:
SkPoint origin,
SkTextEncoding encoding = SkTextEncoding::kUTF8);
const SkGlyphRunList& blobToGlyphRunList(const SkTextBlob& blob, SkPoint origin);
std::tuple<SkSpan<const SkPoint>, SkSpan<const SkVector>>
convertRSXForm(SkSpan<const SkRSXform> xforms);
bool empty() const { return fGlyphRunListStorage.empty(); }
private:
void initialize(int totalRunSize);
void initialize(const SkTextBlob& blob);
void prepareBuffers(int positionCount, int RSXFormCount);
SkSpan<const SkGlyphID> textToGlyphIDs(
const SkFont& font, const void* bytes, size_t byteLength, SkTextEncoding);