use SkIPoint for positions in GrTextBlob
Instead of using SkPoint to store position information, we can constrain the position information to be integers. This will lead to two future improvements. * Shrink position information to SkIPoint16 * Use integer arithmetic instead of floating point in vertex calculation. I'm interested in feedback on the comment which describes the math behind this technique. Change-Id: I8441bcbcad99d07e6d6d5e1788d1a47d87f22923 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/306948 Commit-Queue: Herb Derby <herb@google.com> Reviewed-by: Ben Wagner <bungeman@google.com> Reviewed-by: Robert Phillips <robertphillips@google.com>
This commit is contained in:
parent
86c272aa65
commit
1fbb331bbb
@ -68,41 +68,38 @@ void SkDrawableGlyphBuffer::startBitmapDevice(
|
||||
SkDEBUGCODE(fPhase = kInput);
|
||||
}
|
||||
|
||||
void SkDrawableGlyphBuffer::startGPUDevice(
|
||||
SkPoint 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);
|
||||
SkMatrix device = viewMatrix;
|
||||
SkPoint halfSampleFreq = roundingSpec.halfAxisSampleFreq;
|
||||
matrix.postTranslate(halfSampleFreq.x(), halfSampleFreq.y());
|
||||
matrix.mapPoints(fPositions, positions.data(), positions.size());
|
||||
device.postTranslate(halfSampleFreq.x(), halfSampleFreq.y());
|
||||
device.preTranslate(origin.x(), origin.y());
|
||||
|
||||
// Mask for controlling axis alignment.
|
||||
SkIPoint mask = roundingSpec.ignorePositionFieldMask;
|
||||
auto positions = source.get<1>();
|
||||
device.mapPoints(fPositions, positions.data(), positions.size());
|
||||
|
||||
// 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};
|
||||
}
|
||||
auto floor = [](SkPoint pt) -> SkPoint {
|
||||
return {SkScalarFloorToScalar(pt.x()), SkScalarFloorToScalar(pt.y())};
|
||||
};
|
||||
|
||||
for (SkPoint& pos : SkSpan<SkPoint>(fPositions, source.size())) {
|
||||
SkPoint P = SkPoint::Make(SkScalarFloorToScalar(pos.x()), SkScalarFloorToScalar(pos.y()));
|
||||
pos = P - Q;
|
||||
// q = [Q](0,0,1) = [R][V][O](0,0,1).
|
||||
SkPoint q = device.mapXY(0, 0);
|
||||
SkPoint qFloor = floor(q);
|
||||
|
||||
for (auto [packedGlyphID, glyphID, pos]
|
||||
: SkMakeZip(fMultiBuffer.get(), source.get<0>(), fPositions.get())) {
|
||||
packedGlyphID = SkPackedGlyphID{glyphID, pos, roundingSpec.ignorePositionFieldMask};
|
||||
pos = floor(pos - qFloor);
|
||||
}
|
||||
|
||||
SkDEBUGCODE(fPhase = kInput);
|
||||
// Return the residual = Floor(q) - q + (rx,ry,0).
|
||||
return qFloor - q + roundingSpec.halfAxisSampleFreq;
|
||||
}
|
||||
|
||||
|
||||
|
@ -155,22 +155,96 @@ public:
|
||||
|
||||
// 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(
|
||||
// We are looking for constant values for the x,y positions for all the glyphs that are not
|
||||
// dependant on the device origin mapping Q such that we can just add a new value to translate
|
||||
// all the glyph positions to a new device origin mapping Q'. We want (cx,cy,0) + [Q'](0,0,1)
|
||||
// draw the blob with device origin Q'. Ultimately we show there is an integer solution for
|
||||
// the glyph positions where (ix,iy,0) + ([Q'](0,0,1) + (sx,sy,0)) both parts of the top
|
||||
// level + are integers, and preserve all the flooring properties.
|
||||
//
|
||||
// Given (px,py) the glyph origin in source space. The glyph origin in device space (x,y) is:
|
||||
// (x,y,1) = Floor([R][V][O](px,py,1))
|
||||
// where:
|
||||
// * R - is the rounding matrix given as translate(sampling_freq_x/2, sampling_freq_y/2).
|
||||
// * V - is the mapping from source space to device space.
|
||||
// * O - is the blob origin given, as translate(origin.x(), origin.y()).
|
||||
// * (px,py,1) - is the vector of the glyph origin in source space. There is a position for
|
||||
// each glyph.
|
||||
//
|
||||
// It is given that if there is a change in position from V to V', and O to O' that the upper
|
||||
// 2x2 of V and V' are the same.
|
||||
//
|
||||
// The three matrices R,V, and O constitute the device mapping [Q] = [R][V][O], and the
|
||||
// device origin is given by q = [Q](0,0,1). Thus,
|
||||
// (x,y,1) = Floor([Q](0,0,1) + [V](px,py,0)) = Floor(q + [V](px,py,0))
|
||||
// Note: [V](px,py,0) is the vector transformed without the translation portion of V. That
|
||||
// translation of V is accounted for in q.
|
||||
//
|
||||
// If we want to translate the blob from the device mapping Q to the device mapping
|
||||
// [Q'] = [R'][V'][O], we can use the following translation. Restate as q' - q.
|
||||
// (x',y',1) = Floor(q + [V](px,py,0) + q' - q).
|
||||
//
|
||||
// We are given that q' - q is an integer translation. We can move the integer translation out
|
||||
// from the Floor expression as:
|
||||
// (x',y',1) = Floor(q + [V](px,py,0)) + q' - q (1)
|
||||
//
|
||||
// We can now see that (cx,cy,0) is constructed by dropping q' from above.
|
||||
// (cx,cy,0) = Floor(q + [V](px,py,0)) - q
|
||||
//
|
||||
// Notice that cx and cy are not guaranteed to be integers because q is not
|
||||
// constrained to be integer; only q' - q is constrained to be an integer.
|
||||
//
|
||||
// Let Floor(q) be the integer portion the vector elements and {q} be the fractional portion
|
||||
// which is calculated as q - Floor(q). This vector has a zero in the third place due to the
|
||||
// subtraction.
|
||||
// Rewriting (1) with this substitution of Floor(q) + {q} for q.
|
||||
// (x',y',1) = Floor(q + [V](px,py,0)) + q' - q
|
||||
// becomes,
|
||||
// (x',y',1) = Floor(Floor(q) + {q} + [V](px,py,0)) + q' - (q + {q})
|
||||
// simplifying by moving Floor(q) out of the Floor() because it is integer,
|
||||
// (x',y',1) = Floor({q} + [V](px,py,0)) + q' + Floor(q) - Floor(q) - {q}
|
||||
// removing terms that result in zero gives,
|
||||
// (x',y',1) = Floor({q} + [V](px,py,0)) + q' - {q}
|
||||
// Notice that q' - {q} and Floor({q} + [V](px,py,0)) are integer.
|
||||
// Let,
|
||||
// (ix,iy,0) = Floor({q} + [V](px,py,0)),
|
||||
// (sx,sy,0) = -{q}.
|
||||
// I call the (sx,sy,0) value the residual.
|
||||
// Thus,
|
||||
// (x',y',1) = (ix,iy,0) + (q' + (sx,sy,0)). (2)
|
||||
//
|
||||
// As a matter of practicality, we have the following already calculated for sub-pixel
|
||||
// positioning, and use it to calculate (ix,iy,0):
|
||||
// (fx,fy,1) = [R][V][O](px,py,1)
|
||||
// = [Q](0,0,1) + [V](px,py,0)
|
||||
// = q + [V](px,py,0)
|
||||
// = Floor(q) + {q} + [V](px,py,0)
|
||||
// So,
|
||||
// (ix,iy,0) = Floor((fx,fy,1) - Floor(q)).
|
||||
//
|
||||
// When calculating [Q'] = [R][V'][O'] we don't have the values for [R]. Notice that [R] is a
|
||||
// post translation to [V'][O']. This means that the values of R are added directly to the
|
||||
// translation values of [V'][O']. So, if [V'][O'](0,0,1) results in the vector (tx,ty,1)
|
||||
// then [R](tx,ty,0) = (tx + rx, ty + ry, 0). So, in practice we don't have the full [Q'] what
|
||||
// is available is [Q''] = [V'][O']. We can add the rounding terms to the residual
|
||||
// to account for not having [R]. Substituting -{q} for (sx,sy,0) in (2), gives:
|
||||
// (x',y',1) = (ix,iy,0) + (q' - {q}).
|
||||
// = (ix,iy,0) + ([Q'](0,0,1) - {q})
|
||||
// = (ix,iy,0) + ([R][V'][O'](0,0,1) - {q})
|
||||
// = (ix,iy,0) + ((rx,ry,0) + [V'][O'](0,0,1) - {q})
|
||||
// = (ix,iy,0) + ([V'][O'](0,0,1) + (rx,ry,0) - {q}.
|
||||
// So we redefine the residual to include the needed rounding terms.
|
||||
// (sx',sy',0) = (rx,ry,0) - (q - Floor(q))
|
||||
// = (rx,ry,0) + Floor(q) - q.
|
||||
//
|
||||
// Putting it all together:
|
||||
// Q'' = [V'][O'](0,0,1)
|
||||
// q'' = Q''(0, 0, 1)
|
||||
// (x',y',1) = (ix,iy,0) + (q'' + (sx',sy',0)).
|
||||
|
||||
|
||||
// Returns the residual -- (sx',sy',0).
|
||||
SkPoint startGPUDevice(
|
||||
const SkZip<const SkGlyphID, const SkPoint>& source,
|
||||
SkPoint origin, const SkMatrix& viewMatrix,
|
||||
const SkGlyphPositionRoundingSpec& roundingSpec);
|
||||
|
@ -190,14 +190,15 @@ void SkGlyphRunListPainter::processGlyphRunList(const SkGlyphRunList& glyphRunLi
|
||||
|
||||
SkScopedStrikeForGPU strike = strikeSpec.findOrCreateScopedStrike(fStrikeCache);
|
||||
|
||||
fDrawable.startGPUDevice(fRejects.source(), origin, drawMatrix, strike->roundingSpec());
|
||||
SkPoint residual = fDrawable.startGPUDevice(
|
||||
fRejects.source(), origin, drawMatrix, strike->roundingSpec());
|
||||
strike->prepareForMaskDrawing(&fDrawable, &fRejects);
|
||||
fRejects.flipRejectsToSource();
|
||||
|
||||
if (process && !fDrawable.drawableIsEmpty()) {
|
||||
// processDeviceMasks must be called even if there are no glyphs to make sure runs
|
||||
// are set correctly.
|
||||
process->processDeviceMasks(fDrawable.drawable(), strikeSpec);
|
||||
process->processDeviceMasks(fDrawable.drawable(), strikeSpec, residual);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -133,7 +133,8 @@ public:
|
||||
virtual ~SkGlyphRunPainterInterface() = default;
|
||||
|
||||
virtual void processDeviceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
|
||||
const SkStrikeSpec& strikeSpec) = 0;
|
||||
const SkStrikeSpec& strikeSpec,
|
||||
SkPoint residual) = 0;
|
||||
|
||||
virtual void processSourceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
|
||||
const SkStrikeSpec& strikeSpec) = 0;
|
||||
|
@ -226,11 +226,13 @@ static GrAtlasTextOp::MaskType op_mask_type(GrMaskFormat grMaskFormat) {
|
||||
|
||||
// -- GrDirectMaskSubRun ---------------------------------------------------------------------------
|
||||
GrDirectMaskSubRun::GrDirectMaskSubRun(GrMaskFormat format,
|
||||
SkPoint residual,
|
||||
GrTextBlob* blob,
|
||||
const SkRect& bounds,
|
||||
SkSpan<const VertexData> vertexData,
|
||||
GrGlyphVector glyphs)
|
||||
: fMaskFormat{format}
|
||||
, fResidual{residual}
|
||||
, fBlob{blob}
|
||||
, fVertexBounds{bounds}
|
||||
, fVertexData{vertexData}
|
||||
@ -239,10 +241,12 @@ GrDirectMaskSubRun::GrDirectMaskSubRun(GrMaskFormat format,
|
||||
GrSubRun* GrDirectMaskSubRun::Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
|
||||
const SkStrikeSpec& strikeSpec,
|
||||
GrMaskFormat format,
|
||||
SkPoint residual,
|
||||
GrTextBlob* blob,
|
||||
SkArenaAlloc* alloc) {
|
||||
size_t vertexCount = drawables.size();
|
||||
SkRect bounds = SkRectPriv::MakeLargestInverted();
|
||||
|
||||
auto initializer = [&](size_t i) {
|
||||
auto [variant, pos] = drawables[i];
|
||||
SkGlyph* skGlyph = variant;
|
||||
@ -254,14 +258,14 @@ GrSubRun* GrDirectMaskSubRun::Make(const SkZip<SkGlyphVariant, SkPoint>& drawabl
|
||||
rb = SkPoint::Make(r, b) + pos;
|
||||
|
||||
bounds.joinPossiblyEmptyRect(SkRect::MakeLTRB(lt.x(), lt.y(), rb.x(), rb.y()));
|
||||
return lt;
|
||||
return VertexData{SkScalarRoundToInt(lt.x()), SkScalarRoundToInt(lt.y())};
|
||||
};
|
||||
|
||||
SkSpan<const VertexData> vertexData{
|
||||
alloc->makeInitializedArray<VertexData>(vertexCount, initializer), vertexCount};
|
||||
|
||||
GrDirectMaskSubRun* subRun = alloc->make<GrDirectMaskSubRun>(
|
||||
format, blob, bounds, vertexData,
|
||||
format, residual, blob, bounds, vertexData,
|
||||
GrGlyphVector::Make(strikeSpec, drawables.get<0>(), alloc));
|
||||
|
||||
return subRun;
|
||||
@ -386,15 +390,15 @@ void GrDirectMaskSubRun::fillVertexData(void* vertexDst, int offset, int count,
|
||||
|
||||
auto direct2D = [&](auto dst, SkIRect* clip) {
|
||||
// Rectangles in device space
|
||||
SkPoint originInDeviceSpace = matrix.mapXY(0, 0);
|
||||
SkPoint originInDeviceSpace = matrix.mapXY(0, 0) + fResidual;
|
||||
SkIPoint originInDeviceSpaceI = {SkScalarRoundToInt(originInDeviceSpace.x()),
|
||||
SkScalarRoundToInt(originInDeviceSpace.y())};
|
||||
for (auto[quad, glyph, leftTop] : vertices(dst)) {
|
||||
GrIRect16 rect = glyph->fAtlasLocator.rect();
|
||||
int16_t w = rect.width(),
|
||||
h = rect.height();
|
||||
auto[l, t] = leftTop + originInDeviceSpaceI;
|
||||
auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
|
||||
auto[fl, ft] = leftTop + originInDeviceSpace;
|
||||
int l = SkScalarRoundToInt(fl),
|
||||
t = SkScalarRoundToInt(ft);
|
||||
if (clip == nullptr) {
|
||||
auto[dl, dt, dr, db] = SkRect::MakeLTRB(l, t, l + w, t + h);
|
||||
quad[0] = {{dl, dt}, color, {al, at}}; // L,T
|
||||
@ -411,16 +415,14 @@ void GrDirectMaskSubRun::fillVertexData(void* vertexDst, int offset, int count,
|
||||
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);
|
||||
int index = glyph->fAtlasLocator.pageIndex();
|
||||
std::tie(tl, tt) =
|
||||
GrDrawOpAtlas::PackIndexInTexCoords(tl + lD, tt + tD, indexLT);
|
||||
GrDrawOpAtlas::PackIndexInTexCoords(
|
||||
rect.fLeft + lD, rect.fTop + tD, index);
|
||||
std::tie(tr, tb) =
|
||||
GrDrawOpAtlas::PackIndexInTexCoords(tr + rD, tb + bD, indexRB);
|
||||
GrDrawOpAtlas::PackIndexInTexCoords(
|
||||
rect.fRight + rD, rect.fBottom + bD, index);
|
||||
} else {
|
||||
// TODO: omit generating any vertex data for fully clipped glyphs ?
|
||||
std::tie(dl, dt, dr, db) = std::make_tuple(0, 0, 0, 0);
|
||||
@ -491,6 +493,7 @@ GrTransformedMaskSubRun::GrTransformedMaskSubRun(GrMaskFormat format,
|
||||
GrSubRun* GrTransformedMaskSubRun::Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
|
||||
const SkStrikeSpec& strikeSpec,
|
||||
GrMaskFormat format,
|
||||
SkPoint residual,
|
||||
GrTextBlob* blob,
|
||||
SkArenaAlloc* alloc) {
|
||||
size_t vertexCount = drawables.size();
|
||||
@ -1059,7 +1062,8 @@ template<typename AddSingleMaskFormat>
|
||||
void GrTextBlob::addMultiMaskFormat(
|
||||
AddSingleMaskFormat addSingle,
|
||||
const SkZip<SkGlyphVariant, SkPoint>& drawables,
|
||||
const SkStrikeSpec& strikeSpec) {
|
||||
const SkStrikeSpec& strikeSpec,
|
||||
SkPoint residual) {
|
||||
this->setHasBitmap();
|
||||
if (drawables.empty()) { return; }
|
||||
|
||||
@ -1072,14 +1076,19 @@ void GrTextBlob::addMultiMaskFormat(
|
||||
GrMaskFormat nextFormat = GrGlyph::FormatFromSkGlyph(glyph->maskFormat());
|
||||
if (format != nextFormat) {
|
||||
auto sameFormat = drawables.subspan(startIndex, i - startIndex);
|
||||
GrSubRun* subRun = addSingle(sameFormat, strikeSpec, format, this, &fAlloc);
|
||||
GrSubRun* subRun = addSingle(sameFormat, strikeSpec, format, residual, this, &fAlloc);
|
||||
this->insertSubRun(subRun);
|
||||
format = nextFormat;
|
||||
startIndex = i;
|
||||
}
|
||||
}
|
||||
auto sameFormat = drawables.last(drawables.size() - startIndex);
|
||||
GrSubRun* subRun = addSingle(sameFormat, strikeSpec, format, this, &fAlloc);
|
||||
GrSubRun* subRun = addSingle(sameFormat,
|
||||
strikeSpec,
|
||||
format,
|
||||
residual,
|
||||
this,
|
||||
&fAlloc);
|
||||
this->insertSubRun(subRun);
|
||||
}
|
||||
|
||||
@ -1098,9 +1107,10 @@ void GrTextBlob::insertSubRun(GrSubRun* subRun) {
|
||||
}
|
||||
|
||||
void GrTextBlob::processDeviceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
|
||||
const SkStrikeSpec& strikeSpec) {
|
||||
const SkStrikeSpec& strikeSpec,
|
||||
SkPoint residual) {
|
||||
|
||||
this->addMultiMaskFormat(GrDirectMaskSubRun::Make, drawables, strikeSpec);
|
||||
this->addMultiMaskFormat(GrDirectMaskSubRun::Make, drawables, strikeSpec, residual);
|
||||
}
|
||||
|
||||
void GrTextBlob::processSourcePaths(const SkZip<SkGlyphVariant, SkPoint>& drawables,
|
||||
@ -1127,5 +1137,7 @@ void GrTextBlob::processSourceSDFT(const SkZip<SkGlyphVariant, SkPoint>& drawabl
|
||||
|
||||
void GrTextBlob::processSourceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
|
||||
const SkStrikeSpec& strikeSpec) {
|
||||
this->addMultiMaskFormat(GrTransformedMaskSubRun::Make, drawables, strikeSpec);
|
||||
// In this case the residual is {0, 0} because it is not used to calculate the positions of
|
||||
// transformed mask. Any value would do.
|
||||
this->addMultiMaskFormat(GrTransformedMaskSubRun::Make, drawables, strikeSpec, {0, 0});
|
||||
}
|
||||
|
@ -113,7 +113,8 @@ public:
|
||||
void addMultiMaskFormat(
|
||||
AddSingleMaskFormat addSingle,
|
||||
const SkZip<SkGlyphVariant, SkPoint>& drawables,
|
||||
const SkStrikeSpec& strikeSpec);
|
||||
const SkStrikeSpec& strikeSpec,
|
||||
SkPoint residual);
|
||||
|
||||
const SkTInternalLList<GrSubRun>& subRunList() const { return fSubRunList; }
|
||||
|
||||
@ -138,7 +139,8 @@ private:
|
||||
|
||||
// Methods to satisfy SkGlyphRunPainterInterface
|
||||
void processDeviceMasks(const SkZip<SkGlyphVariant, SkPoint>& drawables,
|
||||
const SkStrikeSpec& strikeSpec) override;
|
||||
const SkStrikeSpec& strikeSpec,
|
||||
SkPoint residual) override;
|
||||
void processSourcePaths(const SkZip<SkGlyphVariant, SkPoint>& drawables,
|
||||
const SkFont& runFont,
|
||||
const SkStrikeSpec& strikeSpec) override;
|
||||
@ -314,9 +316,10 @@ private:
|
||||
// -- GrDirectMaskSubRun ---------------------------------------------------------------------------
|
||||
class GrDirectMaskSubRun final : public GrAtlasSubRun {
|
||||
public:
|
||||
using VertexData = SkPoint; // The left top corner of the glyph bounding box.
|
||||
using VertexData = SkIPoint;
|
||||
|
||||
GrDirectMaskSubRun(GrMaskFormat format,
|
||||
SkPoint residual,
|
||||
GrTextBlob* blob,
|
||||
const SkRect& bounds,
|
||||
SkSpan<const VertexData> vertexData,
|
||||
@ -325,6 +328,7 @@ public:
|
||||
static GrSubRun* Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
|
||||
const SkStrikeSpec& strikeSpec,
|
||||
GrMaskFormat format,
|
||||
SkPoint residual,
|
||||
GrTextBlob* blob,
|
||||
SkArenaAlloc* alloc);
|
||||
|
||||
@ -350,10 +354,12 @@ public:
|
||||
const SkMatrix& drawMatrix, SkPoint drawOrigin,
|
||||
SkIRect clip) const override;
|
||||
private:
|
||||
|
||||
// The rectangle that surrounds all the glyph bounding boxes in device space.
|
||||
SkRect deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const;
|
||||
|
||||
const GrMaskFormat fMaskFormat;
|
||||
const SkPoint fResidual;
|
||||
GrTextBlob* const fBlob;
|
||||
// The vertex bounds in device space. The bounds are the joined rectangles of all the glyphs.
|
||||
const SkRect fVertexBounds;
|
||||
@ -384,6 +390,7 @@ public:
|
||||
static GrSubRun* Make(const SkZip<SkGlyphVariant, SkPoint>& drawables,
|
||||
const SkStrikeSpec& strikeSpec,
|
||||
GrMaskFormat format,
|
||||
SkPoint residual,
|
||||
GrTextBlob* blob,
|
||||
SkArenaAlloc* alloc);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user