Reland "direct mask biased to (0,0)"

This is a reland of 56cde4923f

Original change's description:
> direct mask biased to (0,0)
>
> Create mask rectangles in device space.
> But, instead of offsetting to the drawing text blob origin
> offset to 0,0 to simplify mapping from source space to
> device space.
>
> Bug: skia:10251
>
> Change-Id: Ic637eb78879bcfae7e7944053d67d9eaef8490cc
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/290133
> Commit-Queue: Herb Derby <herb@google.com>
> Reviewed-by: Robert Phillips <robertphillips@google.com>

Bug: skia:10251
Change-Id: I622ed5c3c16379b06989bf737e74a7752984c158
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/290441
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Herb Derby <herb@google.com>
This commit is contained in:
Herb Derby 2020-05-15 10:08:49 -04:00 committed by Skia Commit-Bot
parent 39decdbe35
commit e7825ff7e3
7 changed files with 84 additions and 33 deletions

View File

@ -40,7 +40,7 @@ void SkDrawableGlyphBuffer::startSource(const SkZip<const SkGlyphID, const SkPoi
SkDEBUGCODE(fPhase = kInput);
}
void SkDrawableGlyphBuffer::startDevice(
void SkDrawableGlyphBuffer::startBitmapDevice(
const SkZip<const SkGlyphID, const SkPoint>& source,
SkPoint origin, const SkMatrix& viewMatrix,
const SkGlyphPositionRoundingSpec& roundingSpec) {
@ -68,6 +68,44 @@ void SkDrawableGlyphBuffer::startDevice(
SkDEBUGCODE(fPhase = kInput);
}
void SkDrawableGlyphBuffer::startGPUDevice(
const SkZip<const SkGlyphID, const SkPoint>& source,
SkPoint origin, const SkMatrix& viewMatrix,
const SkGlyphPositionRoundingSpec& roundingSpec) {
fInputSize = source.size();
fDrawableSize = 0;
// Map the positions including subpixel position.
auto positions = source.get<1>();
SkMatrix matrix = viewMatrix;
matrix.preTranslate(origin.x(), origin.y());
// Q = [M][T](0,0).
SkPoint Q = matrix.mapXY(0, 0);
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;
for (auto [glyphID, pos] : withMappedPos) {
*packedIDCursor++ = SkPackedGlyphID{glyphID, pos, mask};
}
for (SkPoint& pos : SkSpan<SkPoint>(fPositions, source.size())) {
SkPoint P = SkPoint::Make(SkScalarFloorToScalar(pos.x()), SkScalarFloorToScalar(pos.y()));
pos = P - Q;
}
SkDEBUGCODE(fPhase = kInput);
}
void SkDrawableGlyphBuffer::reset() {
SkDEBUGCODE(fPhase = kReset);
if (fMaxSize > 200) {

View File

@ -148,7 +148,29 @@ public:
void startSource(const SkZip<const SkGlyphID, const SkPoint>& source);
// Load the buffer with SkPackedGlyphIDs and positions using the device transform.
void startDevice(
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.
//
// A final device position is computed in the following manner:
// [x,y] = Floor[M][T][x',y']^t
// M is complicated but includes the rounding offsets for subpixel positioning.
// T is the translation matrix derived from the text blob origin.
// The final position is {Floor(x), Floor(y)}. If we want to move this position around in
// device space given a start origin T in source space and a end position T' in source space
// and new device matrix M', we need to calculate a suitable device space translation V. We
// know that V must be integer.
// V = [M'][T'](0,0)^t - [M][T](0,0)^t.
// V = Q' - Q
// So all the positions Ps are translated by V to translate from T to T' in source space. We can
// generate Ps such that we just need to add any Q' to the constant Ps to get a final positions.
// So, a single point P = {Floor(x)-Q_x, Floor(y)-Q_y}; this does not have to be integer.
// This allows positioning to be P + Q', which given ideal numbers would be an integer. Since
// the addition is done with floating point, it must be rounded.
void startGPUDevice(
const SkZip<const SkGlyphID, const SkPoint>& source,
SkPoint origin, const SkMatrix& viewMatrix,
const SkGlyphPositionRoundingSpec& roundingSpec);

View File

@ -124,7 +124,7 @@ void SkGlyphRunListPainter::drawForBitmapDevice(
auto strike = strikeSpec.findOrCreateStrike();
fDrawable.startDevice(
fDrawable.startBitmapDevice(
fRejects.source(), drawOrigin, deviceMatrix, strike->roundingSpec());
strike->prepareForDrawingMasksCPU(&fDrawable);
bitmapDevice->paintMasks(&fDrawable, runPaint);
@ -189,7 +189,7 @@ void SkGlyphRunListPainter::processGlyphRunList(const SkGlyphRunList& glyphRunLi
SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(fStrikeCache);
fDrawable.startDevice(fRejects.source(), origin, drawMatrix, strike->roundingSpec());
fDrawable.startGPUDevice(fRejects.source(), origin, drawMatrix, strike->roundingSpec());
strike->prepareForMaskDrawing(&fDrawable, &fRejects);
fRejects.flipRejectsToSource();

View File

@ -50,7 +50,7 @@ GrTextBlob::SubRun::SubRun(SubRunType type, GrTextBlob* textBlob, const SkStrike
, fVertexData{vertexData}
, fStrikeSpec{strikeSpec}
, fCurrentColor{textBlob->fColor}
, fCurrentOrigin{this->needsTransform() ? SkPoint{0, 0} : textBlob->fInitialOrigin}
, fCurrentOrigin{0,0}
, fCurrentMatrix{textBlob->fInitialMatrix} {
SkASSERT(type != kTransformedPath);
textBlob->insertSubRun(this);
@ -111,7 +111,6 @@ void GrTextBlob::SubRun::appendGlyphs(const SkZip<SkGlyphVariant, SkPoint>& draw
// Only floor the device coordinates.
SkRect dstRect;
if (!this->needsTransform()) {
pos = {SkScalarFloorToScalar(pos.x()), SkScalarFloorToScalar(pos.y())};
dstRect = dest_rect(*skGlyph, pos);
} else {
dstRect = dest_rect(*skGlyph, pos, strikeToSource);
@ -142,15 +141,6 @@ void GrTextBlob::SubRun::appendGlyphs(const SkZip<SkGlyphVariant, SkPoint>& draw
packedIDCursor->fPackedGlyphID = skGlyph->getPackedID();
packedIDCursor++;
}
if (!this->needsTransform()) {
// Use the negative initial origin to make the fVertexBounds {0, 0} based.
SkPoint pt = fBlob->fInitialOrigin;
// If the box is in device space, then transform the source space origin to device space.
pt = fBlob->fInitialMatrix.mapXY(pt.x(), pt.y());
fVertexBounds.offset(-pt);
}
}
void GrTextBlob::SubRun::resetBulkUseToken() { fBulkUseToken.reset(); }
@ -228,19 +218,12 @@ void GrTextBlob::SubRun::translateVerticesIfNeeded(
// 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 source space to a translation in device space. Calculate
// the translation by mapping (0, 0) through both the current matrix, and the draw
// matrix, and taking the difference.
SkMatrix currentMatrix{fCurrentMatrix};
currentMatrix.preTranslate(fCurrentOrigin.x(), fCurrentOrigin.y());
SkPoint currentDeviceOrigin{0, 0};
currentMatrix.mapPoints(&currentDeviceOrigin, 1);
SkMatrix completeDrawMatrix{drawMatrix};
completeDrawMatrix.preTranslate(drawOrigin.x(), drawOrigin.y());
SkPoint drawDeviceOrigin{0, 0};
completeDrawMatrix.mapPoints(&drawDeviceOrigin, 1);
translation = drawDeviceOrigin - currentDeviceOrigin;
// Calculate the translation in destination space.
SkPoint newOrigin = drawMatrix.mapXY(drawOrigin.x(), drawOrigin.y());
translation = newOrigin - fCurrentOrigin;
fCurrentOrigin = newOrigin;
}
if (translation != SkPoint{0, 0}) {
@ -248,12 +231,18 @@ void GrTextBlob::SubRun::translateVerticesIfNeeded(
for (size_t quad = 0; quad < fGlyphs.size(); quad++) {
SkPoint* vertexCursor = reinterpret_cast<SkPoint*>(quadStart(quad));
for (int i = 0; i < 4; ++i) {
*vertexCursor += translation;
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;
fCurrentOrigin = drawOrigin;
}
}
@ -529,7 +518,7 @@ void GrTextBlob::addOp(GrTextTarget* target,
if (!needsExactCTM) {
for (const auto& pathPos : subRun->fPaths) {
const SkPath& path = pathPos.fPath;
const SkPoint pos = pathPos.fOrigin; // Transform the glyph to source space.
const SkPoint pos = pathPos.fOrigin; // Transform the glyph to source space.
SkMatrix pathMatrix = strikeToSource;
pathMatrix.postTranslate(pos.x(), pos.y());
SkPreConcatMatrixProvider strikeToDevice(deviceMatrix, pathMatrix);

View File

@ -393,6 +393,8 @@ public:
GrDrawOpAtlas::BulkUseTokenUpdater fBulkUseToken;
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;

View File

@ -172,7 +172,7 @@ DEF_TEST(SkDrawableGlyphBufferBasic, reporter) {
drawable.ensureSize(100);
SkMatrix matrix = SkMatrix::MakeScale(0.5);
SkGlyphPositionRoundingSpec rounding{true, kX_SkAxisAlignment};
drawable.startDevice(source, {100, 100}, matrix, rounding);
drawable.startBitmapDevice(source, {100, 100}, matrix, rounding);
for (auto [i, packedID, pos] : SkMakeEnumerate(drawable.input())) {
REPORTER_ASSERT(reporter, glyphIDs[i] == packedID.packedID().glyphID());
REPORTER_ASSERT(reporter,

View File

@ -72,8 +72,8 @@ DEF_TEST(SkScalerCacheMultiThread, Reporter) {
drawable.ensureSize(glyphCount);
rejects.setSource(local);
drawable.startDevice(rejects.source(), {0, 0}, SkMatrix::I(),
scalerCache.roundingSpec());
drawable.startBitmapDevice(rejects.source(), {0, 0}, SkMatrix::I(),
scalerCache.roundingSpec());
scalerCache.prepareForMaskDrawing(&drawable, &rejects);
rejects.flipRejectsToSource();
drawable.reset();