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:
parent
29c06bc82a
commit
f9cf1aa2ca
@ -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{};)
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user