combine direct position calculation

Make the glyph buffers use the same Direct position calculations
for Bitmap and GPU processing. This will create fewer changes when
switching to SkGlyphDigest based API.

Bug: skia:13192

Change-Id: I7e3e44dcfc1a4bad014d0ebe2bef3c1b28c712f4
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/529277
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Herb Derby <herb@google.com>
This commit is contained in:
Herb Derby 2022-04-11 13:15:31 -06:00 committed by SkCQ
parent e4047903db
commit 38f104e85d
8 changed files with 48 additions and 70 deletions

View File

@ -2225,6 +2225,7 @@ generated_cc_atom(
":SkGlyph_hdr", ":SkGlyph_hdr",
":SkScalerContext_hdr", ":SkScalerContext_hdr",
"//include/core:SkDrawable_hdr", "//include/core:SkDrawable_hdr",
"//include/core:SkScalar_hdr",
"//src/pathops:SkPathOpsCubic_hdr", "//src/pathops:SkPathOpsCubic_hdr",
"//src/pathops:SkPathOpsQuad_hdr", "//src/pathops:SkPathOpsQuad_hdr",
], ],

View File

@ -8,6 +8,7 @@
#include "src/core/SkGlyph.h" #include "src/core/SkGlyph.h"
#include "include/core/SkDrawable.h" #include "include/core/SkDrawable.h"
#include "include/core/SkScalar.h"
#include "src/core/SkArenaAlloc.h" #include "src/core/SkArenaAlloc.h"
#include "src/core/SkScalerContext.h" #include "src/core/SkScalerContext.h"
#include "src/pathops/SkPathOpsCubic.h" #include "src/pathops/SkPathOpsCubic.h"
@ -29,6 +30,7 @@ SkMask SkGlyph::mask() const {
} }
SkMask SkGlyph::mask(SkPoint position) const { SkMask SkGlyph::mask(SkPoint position) const {
SkASSERT(SkScalarIsInt(position.x()) && SkScalarIsInt(position.y()));
SkMask answer = this->mask(); SkMask answer = this->mask();
answer.fBounds.offset(SkScalarFloorToInt(position.x()), SkScalarFloorToInt(position.y())); answer.fBounds.offset(SkScalarFloorToInt(position.x()), SkScalarFloorToInt(position.y()));
return answer; return answer;

View File

@ -40,49 +40,21 @@ void SkDrawableGlyphBuffer::startSource(const SkZip<const SkGlyphID, const SkPoi
SkDEBUGCODE(fPhase = kInput); SkDEBUGCODE(fPhase = kInput);
} }
void SkDrawableGlyphBuffer::startBitmapDevice( void SkDrawableGlyphBuffer::startDevicePositioning(
const SkZip<const SkGlyphID, const SkPoint>& source, const SkZip<const SkGlyphID, const SkPoint>& source,
SkPoint origin, const SkMatrix& viewMatrix, const SkMatrix& positionMatrix,
const SkGlyphPositionRoundingSpec& roundingSpec) {
fInputSize = source.size();
fAcceptedSize = 0;
// Map the positions including subpixel position.
auto positions = source.get<1>();
SkMatrix matrix = viewMatrix;
matrix.preTranslate(origin.x(), origin.y());
SkPoint halfSampleFreq = roundingSpec.halfAxisSampleFreq;
matrix.postTranslate(halfSampleFreq.x(), halfSampleFreq.y());
matrix.mapPoints(fPositions, positions.data(), positions.size());
// Mask for controlling axis alignment.
SkIPoint mask = roundingSpec.ignorePositionFieldMask;
// Convert glyph ids and positions to packed glyph ids.
SkZip<const SkGlyphID, const SkPoint> withMappedPos =
SkMakeZip(source.get<0>(), fPositions.get());
SkGlyphVariant* packedIDCursor = fMultiBuffer.get();
for (auto [glyphID, pos] : withMappedPos) {
*packedIDCursor++ = SkPackedGlyphID{glyphID, pos, mask};
}
SkDEBUGCODE(fPhase = kInput);
}
void SkDrawableGlyphBuffer::startGPUDevice(
const SkZip<const SkGlyphID, const SkPoint>& source,
const SkMatrix& drawMatrix,
const SkGlyphPositionRoundingSpec& roundingSpec) { const SkGlyphPositionRoundingSpec& roundingSpec) {
fInputSize = source.size(); fInputSize = source.size();
fAcceptedSize = 0; fAcceptedSize = 0;
// Build up the mapping from source space to device space. Add the rounding constant // Build up the mapping from source space to device space. Add the rounding constant
// halfSampleFreq so we just need to floor to get the device result. // halfSampleFreq, so we just need to floor to get the device result.
SkMatrix device = drawMatrix; SkMatrix positionMatrixWithRounding = positionMatrix;
SkPoint halfSampleFreq = roundingSpec.halfAxisSampleFreq; SkPoint halfSampleFreq = roundingSpec.halfAxisSampleFreq;
device.postTranslate(halfSampleFreq.x(), halfSampleFreq.y()); positionMatrixWithRounding.postTranslate(halfSampleFreq.x(), halfSampleFreq.y());
auto positions = source.get<1>(); auto positions = source.get<1>();
device.mapPoints(fPositions, positions.data(), positions.size()); positionMatrixWithRounding.mapPoints(fPositions, positions.data(), positions.size());
auto floor = [](SkPoint pt) -> SkPoint { auto floor = [](SkPoint pt) -> SkPoint {
return {SkScalarFloorToScalar(pt.x()), SkScalarFloorToScalar(pt.y())}; return {SkScalarFloorToScalar(pt.x()), SkScalarFloorToScalar(pt.y())};

View File

@ -145,15 +145,9 @@ public:
// during drawing. // during drawing.
void startSource(const SkZip<const SkGlyphID, const SkPoint>& source); void startSource(const SkZip<const SkGlyphID, const SkPoint>& source);
// Load the buffer with SkPackedGlyphIDs and positions using the device transform. // Load the buffer with SkPackedGlyphIDs, calculating positions, so they can be constant.
void startBitmapDevice(
const SkZip<const SkGlyphID, const SkPoint>& source,
SkPoint origin, const SkMatrix& viewMatrix,
const SkGlyphPositionRoundingSpec& roundingSpec);
// Load the buffer with SkPackedGlyphIDs, calculating positions so they can be constant.
// //
// The positions are calculated integer positions in devices space, and the mapping of the // The positions are calculated integer positions in devices space, and the mapping of
// the source origin through the initial matrix is returned. It is given that these positions // the source origin through the initial matrix is returned. It is given that these positions
// are only reused when the blob is translated by an integral amount. Thus, the shifted // are only reused when the blob is translated by an integral amount. Thus, the shifted
// positions are given by the following equation where (ix, iy) is the integer positions of // positions are given by the following equation where (ix, iy) is the integer positions of
@ -165,9 +159,11 @@ public:
// //
// In theory, newMappedOrigin - initialMappedOrigin should be integer, but the vagaries of // In theory, newMappedOrigin - initialMappedOrigin should be integer, but the vagaries of
// floating point don't guarantee that, so force it to integer. // floating point don't guarantee that, so force it to integer.
void startGPUDevice( //
// N.B. The positionMatrix is already translated by the origin of the glyph run list.
void startDevicePositioning(
const SkZip<const SkGlyphID, const SkPoint>& source, const SkZip<const SkGlyphID, const SkPoint>& source,
const SkMatrix& drawMatrix, const SkMatrix& positionMatrix,
const SkGlyphPositionRoundingSpec& roundingSpec); const SkGlyphPositionRoundingSpec& roundingSpec);
SkString dumpInput() const; SkString dumpInput() const;

View File

@ -82,7 +82,7 @@ SkGlyphRunListPainter::SkGlyphRunListPainter(const skgpu::v1::SurfaceDrawContext
void SkGlyphRunListPainter::drawForBitmapDevice( void SkGlyphRunListPainter::drawForBitmapDevice(
SkCanvas* canvas, const BitmapDevicePainter* bitmapDevice, SkCanvas* canvas, const BitmapDevicePainter* bitmapDevice,
const SkGlyphRunList& glyphRunList, const SkPaint& paint, const SkMatrix& deviceMatrix) { const SkGlyphRunList& glyphRunList, const SkPaint& paint, const SkMatrix& drawMatrix) {
ScopedBuffers _ = this->ensureBuffers(glyphRunList); ScopedBuffers _ = this->ensureBuffers(glyphRunList);
// TODO: fStrikeCache is only used for GPU, and some compilers complain about it during the no // TODO: fStrikeCache is only used for GPU, and some compilers complain about it during the no
@ -96,12 +96,14 @@ void SkGlyphRunListPainter::drawForBitmapDevice(
: fBitmapFallbackProps; : fBitmapFallbackProps;
SkPoint drawOrigin = glyphRunList.origin(); SkPoint drawOrigin = glyphRunList.origin();
SkMatrix positionMatrix{drawMatrix};
positionMatrix.preTranslate(drawOrigin.x(), drawOrigin.y());
for (auto& glyphRun : glyphRunList) { for (auto& glyphRun : glyphRunList) {
const SkFont& runFont = glyphRun.font(); const SkFont& runFont = glyphRun.font();
fRejected.setSource(glyphRun.source()); fRejected.setSource(glyphRun.source());
if (SkStrikeSpec::ShouldDrawAsPath(paint, runFont, deviceMatrix)) { if (SkStrikeSpec::ShouldDrawAsPath(paint, runFont, positionMatrix)) {
auto [strikeSpec, strikeToSourceScale] = auto [strikeSpec, strikeToSourceScale] =
SkStrikeSpec::MakePath(runFont, paint, props, fScalerContextFlags); SkStrikeSpec::MakePath(runFont, paint, props, fScalerContextFlags);
@ -168,21 +170,20 @@ void SkGlyphRunListPainter::drawForBitmapDevice(
} }
} }
} }
if (!fRejected.source().empty() && !deviceMatrix.hasPerspective()) { if (!fRejected.source().empty() && !positionMatrix.hasPerspective()) {
SkStrikeSpec strikeSpec = SkStrikeSpec::MakeMask( SkStrikeSpec strikeSpec = SkStrikeSpec::MakeMask(
runFont, paint, props, fScalerContextFlags, deviceMatrix); runFont, paint, props, fScalerContextFlags, positionMatrix);
auto strike = strikeSpec.findOrCreateStrike(); auto strike = strikeSpec.findOrCreateStrike();
fAccepted.startBitmapDevice( fAccepted.startDevicePositioning(
fRejected.source(), drawOrigin, deviceMatrix, strike->roundingSpec()); fRejected.source(), positionMatrix, strike->roundingSpec());
strike->prepareForDrawingMasksCPU(&fAccepted); strike->prepareForDrawingMasksCPU(&fAccepted);
fRejected.flipRejectsToSource(); fRejected.flipRejectsToSource();
bitmapDevice->paintMasks(&fAccepted, paint); bitmapDevice->paintMasks(&fAccepted, paint);
} }
if (!fRejected.source().empty()) { if (!fRejected.source().empty()) {
SkMatrix runMatrix = deviceMatrix;
runMatrix.preTranslate(drawOrigin.x(), drawOrigin.y());
std::vector<SkPoint> sourcePositions; std::vector<SkPoint> sourcePositions;
// Create a strike is source space to calculate scale information. // Create a strike is source space to calculate scale information.
@ -203,7 +204,7 @@ void SkGlyphRunListPainter::drawForBitmapDevice(
sourcePositions.push_back(srcPos); sourcePositions.push_back(srcPos);
SkRect rect = glyph->rect(); SkRect rect = glyph->rect();
rect.makeOffset(srcPos); rect.makeOffset(srcPos);
runMatrix.mapRectToQuad(corners, rect); positionMatrix.mapRectToQuad(corners, rect);
// left top -> right top // left top -> right top
SkScalar scale = (corners[1] - corners[0]).length() / rect.width(); SkScalar scale = (corners[1] - corners[0]).length() / rect.width();
maxScale = std::max(maxScale, scale); maxScale = std::max(maxScale, scale);
@ -229,8 +230,8 @@ void SkGlyphRunListPainter::drawForBitmapDevice(
auto strike = strikeSpec.findOrCreateStrike(); auto strike = strikeSpec.findOrCreateStrike();
// Figure out all the positions and packed glyphIDs based on the device matrix. // Figure out all the positions and packed glyphIDs based on the device matrix.
fAccepted.startBitmapDevice( fAccepted.startDevicePositioning(
fRejected.source(), drawOrigin, deviceMatrix, strike->roundingSpec()); fRejected.source(), positionMatrix, strike->roundingSpec());
strike->prepareForDrawingMasksCPU(&fAccepted); strike->prepareForDrawingMasksCPU(&fAccepted);
auto variants = fAccepted.accepted().get<0>(); auto variants = fAccepted.accepted().get<0>();
@ -277,7 +278,7 @@ void SkGlyphRunListPainter::drawForBitmapDevice(
#if SK_SUPPORT_GPU #if SK_SUPPORT_GPU
void SkGlyphRunListPainter::processGlyphRun(SkGlyphRunPainterInterface* process, void SkGlyphRunListPainter::processGlyphRun(SkGlyphRunPainterInterface* process,
const SkGlyphRun& glyphRun, const SkGlyphRun& glyphRun,
const SkMatrix& drawMatrix, const SkMatrix& positionMatrix,
const SkPaint& runPaint, const SkPaint& runPaint,
const GrSDFTControl& control, const GrSDFTControl& control,
const char* tag, const char* tag,
@ -293,8 +294,8 @@ void SkGlyphRunListPainter::processGlyphRun(SkGlyphRunPainterInterface* process,
} }
msg.appendf("\n matrix\n"); msg.appendf("\n matrix\n");
msg.appendf(" %7.3g %7.3g %7.3g\n %7.3g %7.3g %7.3g\n", msg.appendf(" %7.3g %7.3g %7.3g\n %7.3g %7.3g %7.3g\n",
drawMatrix[0], drawMatrix[1], drawMatrix[2], positionMatrix[0], positionMatrix[1], positionMatrix[2],
drawMatrix[3], drawMatrix[4], drawMatrix[5]); positionMatrix[3], positionMatrix[4], positionMatrix[5]);
#endif #endif
ScopedBuffers _ = this->ensureBuffers(glyphRun); ScopedBuffers _ = this->ensureBuffers(glyphRun);
fRejected.setSource(glyphRun.source()); fRejected.setSource(glyphRun.source());
@ -302,14 +303,14 @@ void SkGlyphRunListPainter::processGlyphRun(SkGlyphRunPainterInterface* process,
// Only consider using direct or SDFT drawing if not drawing hairlines and not perspective. // Only consider using direct or SDFT drawing if not drawing hairlines and not perspective.
if ((runPaint.getStyle() != SkPaint::kStroke_Style || runPaint.getStrokeWidth() != 0) if ((runPaint.getStyle() != SkPaint::kStroke_Style || runPaint.getStrokeWidth() != 0)
&& !drawMatrix.hasPerspective()) { && !positionMatrix.hasPerspective()) {
SkScalar approximateDeviceTextSize = SkScalar approximateDeviceTextSize =
SkFontPriv::ApproximateTransformedTextSize(runFont, drawMatrix); SkFontPriv::ApproximateTransformedTextSize(runFont, positionMatrix);
if (control.isSDFT(approximateDeviceTextSize, runPaint)) { if (control.isSDFT(approximateDeviceTextSize, runPaint)) {
// Process SDFT - This should be the .009% case. // Process SDFT - This should be the .009% case.
const auto& [strikeSpec, strikeToSourceScale, matrixRange] = const auto& [strikeSpec, strikeToSourceScale, matrixRange] =
SkStrikeSpec::MakeSDFT(runFont, runPaint, fDeviceProps, drawMatrix, control); SkStrikeSpec::MakeSDFT(runFont, runPaint, fDeviceProps, positionMatrix, control);
#if defined(SK_TRACE_GLYPH_RUN_PROCESS) #if defined(SK_TRACE_GLYPH_RUN_PROCESS)
msg.appendf(" SDFT case:\n%s", strikeSpec.dump().c_str()); msg.appendf(" SDFT case:\n%s", strikeSpec.dump().c_str());
@ -342,7 +343,7 @@ void SkGlyphRunListPainter::processGlyphRun(SkGlyphRunPainterInterface* process,
// This will handle medium size emoji that are sharing the run with SDFT drawn text. // This will handle medium size emoji that are sharing the run with SDFT drawn text.
// If things are too big they will be passed along to the drawing of last resort below. // If things are too big they will be passed along to the drawing of last resort below.
SkStrikeSpec strikeSpec = SkStrikeSpec::MakeMask( SkStrikeSpec strikeSpec = SkStrikeSpec::MakeMask(
runFont, runPaint, fDeviceProps, fScalerContextFlags, drawMatrix); runFont, runPaint, fDeviceProps, fScalerContextFlags, positionMatrix);
#if defined(SK_TRACE_GLYPH_RUN_PROCESS) #if defined(SK_TRACE_GLYPH_RUN_PROCESS)
msg.appendf(" Mask case:\n%s", strikeSpec.dump().c_str()); msg.appendf(" Mask case:\n%s", strikeSpec.dump().c_str());
@ -350,7 +351,8 @@ void SkGlyphRunListPainter::processGlyphRun(SkGlyphRunPainterInterface* process,
SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(fStrikeCache); SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(fStrikeCache);
fAccepted.startGPUDevice(fRejected.source(), drawMatrix, strike->roundingSpec()); fAccepted.startDevicePositioning(
fRejected.source(), positionMatrix, strike->roundingSpec());
#if defined(SK_TRACE_GLYPH_RUN_PROCESS) #if defined(SK_TRACE_GLYPH_RUN_PROCESS)
msg.appendf(" glyphs:(x,y):\n %s\n", fAccepted.dumpInput().c_str()); msg.appendf(" glyphs:(x,y):\n %s\n", fAccepted.dumpInput().c_str());
#endif #endif

View File

@ -79,14 +79,15 @@ public:
void drawForBitmapDevice( void drawForBitmapDevice(
SkCanvas* canvas, const BitmapDevicePainter* bitmapDevice, SkCanvas* canvas, const BitmapDevicePainter* bitmapDevice,
const SkGlyphRunList& glyphRunList, const SkPaint& paint, const SkMatrix& deviceMatrix); const SkGlyphRunList& glyphRunList, const SkPaint& paint, const SkMatrix& drawMatrix);
#if SK_SUPPORT_GPU #if SK_SUPPORT_GPU
// A nullptr for process means that the calls to the cache will be performed, but none of the // A nullptr for process means that the calls to the cache will be performed, but none of the
// callbacks will be called. // callbacks will be called.
// N.B. The positionMatrix has already been translated to the glyph run list origin.
void processGlyphRun(SkGlyphRunPainterInterface* process, void processGlyphRun(SkGlyphRunPainterInterface* process,
const SkGlyphRun& glyphRun, const SkGlyphRun& glyphRun,
const SkMatrix& drawMatrix, const SkMatrix& positionMatrix,
const SkPaint& drawPaint, const SkPaint& drawPaint,
const GrSDFTControl& control, const GrSDFTControl& control,
const char* tag = nullptr, const char* tag = nullptr,

View File

@ -172,12 +172,16 @@ DEF_TEST(SkDrawableGlyphBufferBasic, reporter) {
accepted.ensureSize(100); accepted.ensureSize(100);
SkMatrix matrix = SkMatrix::Scale(0.5, 0.5); SkMatrix matrix = SkMatrix::Scale(0.5, 0.5);
SkGlyphPositionRoundingSpec rounding{true, SkAxisAlignment::kX}; SkGlyphPositionRoundingSpec rounding{true, SkAxisAlignment::kX};
accepted.startBitmapDevice(source, {100, 100}, matrix, rounding); SkMatrix positionMatrix{matrix};
positionMatrix.preTranslate(100, 100);
accepted.startDevicePositioning(source, positionMatrix, rounding);
for (auto [i, packedID, pos] : SkMakeEnumerate(accepted.input())) { for (auto [i, packedID, pos] : SkMakeEnumerate(accepted.input())) {
REPORTER_ASSERT(reporter, glyphIDs[i] == packedID.packedID().glyphID()); REPORTER_ASSERT(reporter, glyphIDs[i] == packedID.packedID().glyphID());
REPORTER_ASSERT(reporter, REPORTER_ASSERT(reporter,
pos.x() == positions[i].x() * 0.5 + 50 + SkPackedGlyphID::kSubpixelRound); pos.x() == SkScalarFloorToInt(positions[i].x() * 0.5 + 50 +
REPORTER_ASSERT(reporter, pos.y() == positions[i].y() * 0.5 + 50 + 0.5); SkPackedGlyphID::kSubpixelRound));
REPORTER_ASSERT(reporter,
pos.y() == SkScalarFloorToInt(positions[i].y() * 0.5 + 50 + 0.5));
} }
} }

View File

@ -69,8 +69,8 @@ DEF_TEST(SkScalerCacheMultiThread, Reporter) {
accepted.ensureSize(glyphCount); accepted.ensureSize(glyphCount);
rejected.setSource(local); rejected.setSource(local);
accepted.startBitmapDevice(rejected.source(), {0, 0}, SkMatrix::I(), accepted.startDevicePositioning(
scalerCache.roundingSpec()); rejected.source(), SkMatrix::I(), scalerCache.roundingSpec());
scalerCache.prepareForMaskDrawing(&accepted, &rejected); scalerCache.prepareForMaskDrawing(&accepted, &rejected);
rejected.flipRejectsToSource(); rejected.flipRejectsToSource();
accepted.reset(); accepted.reset();