use initial device coordinates as subrun positions
Originally, the positions had a bias of -initialOrigin, so all that was needed was to add the new drawingOrigin in. Store the actual device coordinates thus eliminating the -initialOrigin, and calculate the origin offset drawingOrigin - initialOrigin at vertex fill time. Change-Id: I5a7fe0074f0c7fd01c5fe90a47e67509631379fe Reviewed-on: https://skia-review.googlesource.com/c/skia/+/333127 Reviewed-by: Ben Wagner <bungeman@google.com> Commit-Queue: Herb Derby <herb@google.com>
This commit is contained in:
parent
cd2e148ea4
commit
9c81e52171
@ -75,6 +75,8 @@ SkPoint SkDrawableGlyphBuffer::startGPUDevice(
|
||||
fInputSize = source.size();
|
||||
fDrawableSize = 0;
|
||||
|
||||
// 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.
|
||||
SkMatrix device = viewMatrix;
|
||||
SkPoint halfSampleFreq = roundingSpec.halfAxisSampleFreq;
|
||||
device.postTranslate(halfSampleFreq.x(), halfSampleFreq.y());
|
||||
@ -87,19 +89,19 @@ SkPoint SkDrawableGlyphBuffer::startGPUDevice(
|
||||
return {SkScalarFloorToScalar(pt.x()), SkScalarFloorToScalar(pt.y())};
|
||||
};
|
||||
|
||||
// q = [Q](0,0,1) = [R][V][O](0,0,1).
|
||||
SkPoint q = device.mapXY(0, 0);
|
||||
SkPoint qFloor = floor(q);
|
||||
// Map the origin from source space to device space without the halfSampleFreq offset.
|
||||
SkPoint originMappedToDevice = viewMatrix.mapXY(origin.x(), origin.y());
|
||||
|
||||
for (auto [packedGlyphID, glyphID, pos]
|
||||
: SkMakeZip(fMultiBuffer.get(), source.get<0>(), fPositions.get())) {
|
||||
packedGlyphID = SkPackedGlyphID{glyphID, pos, roundingSpec.ignorePositionFieldMask};
|
||||
pos = floor(pos - qFloor);
|
||||
// Store rounded device coords back in pos.
|
||||
pos = floor(pos);
|
||||
}
|
||||
|
||||
SkDEBUGCODE(fPhase = kInput);
|
||||
// Return the residual = Floor(q) - q + (rx,ry,0).
|
||||
return qFloor - q + roundingSpec.halfAxisSampleFreq;
|
||||
// Return the origin mapped through the initial matrix.
|
||||
return originMappedToDevice;
|
||||
}
|
||||
|
||||
|
||||
|
@ -155,95 +155,20 @@ public:
|
||||
|
||||
// Load the buffer with SkPackedGlyphIDs, calculating positions so they can be constant.
|
||||
//
|
||||
// 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.
|
||||
// The positions are calculated integer positions in devices space, and the mapping of the
|
||||
// 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
|
||||
// positions are given by the following equation where (ix, iy) is the integer positions of
|
||||
// the glyph, initialMappedOrigin is (0,0) in source mapped to the device using the initial
|
||||
// matrix, and newMappedOrigin is (0,0) in source mapped to the device using the current
|
||||
// drawing matrix.
|
||||
//
|
||||
// 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.
|
||||
// (ix', iy') = (ix, iy) + round(newMappedOrigin - initialMappedOrigin)
|
||||
//
|
||||
// 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.
|
||||
// In theory, newMappedOrigin - initialMappedOrigin should be integer, but the vagaries of
|
||||
// floating point don't guarantee that, so force it to integer.
|
||||
//
|
||||
// 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).
|
||||
// Returns the origin mapped through the initial matrix.
|
||||
SkPoint startGPUDevice(
|
||||
const SkZip<const SkGlyphID, const SkPoint>& source,
|
||||
SkPoint origin, const SkMatrix& viewMatrix,
|
||||
|
@ -93,11 +93,11 @@ SkPMColor4f calculate_colors(GrRenderTargetContext* rtc,
|
||||
// The 99% case. No clip. Non-color only.
|
||||
void direct_2D(SkZip<Mask2DVertex[4], const GrGlyph*, const SkIPoint> quadData,
|
||||
GrColor color,
|
||||
SkIPoint deviceOrigin) {
|
||||
SkIPoint integralOriginOffset) {
|
||||
for (auto[quad, glyph, leftTop] : quadData) {
|
||||
auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
|
||||
SkScalar dl = leftTop.x() + deviceOrigin.x(),
|
||||
dt = leftTop.y() + deviceOrigin.y(),
|
||||
SkScalar dl = leftTop.x() + integralOriginOffset.x(),
|
||||
dt = leftTop.y() + integralOriginOffset.y(),
|
||||
dr = dl + (ar - al),
|
||||
db = dt + (ab - at);
|
||||
|
||||
@ -117,13 +117,13 @@ auto ltbr(const Rect& r) {
|
||||
template<typename Quad, typename VertexData>
|
||||
void generalized_direct_2D(SkZip<Quad, const GrGlyph*, const VertexData> quadData,
|
||||
GrColor color,
|
||||
SkIPoint deviceOrigin,
|
||||
SkIPoint integralOriginOffset,
|
||||
SkIRect* clip = nullptr) {
|
||||
for (auto[quad, glyph, leftTop] : quadData) {
|
||||
auto[al, at, ar, ab] = glyph->fAtlasLocator.getUVs();
|
||||
uint16_t w = ar - al,
|
||||
h = ab - at;
|
||||
auto[l, t] = leftTop + deviceOrigin;
|
||||
auto[l, t] = leftTop + integralOriginOffset;
|
||||
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
|
||||
@ -556,7 +556,7 @@ private:
|
||||
SkRect deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const;
|
||||
|
||||
const GrMaskFormat fMaskFormat;
|
||||
const SkPoint fResidual;
|
||||
const SkPoint fInitialMappedOrigin;
|
||||
GrTextBlob* const fBlob;
|
||||
// The vertex bounds in device space. The bounds are the joined rectangles of all the glyphs.
|
||||
const SkRect fVertexBounds;
|
||||
@ -574,7 +574,7 @@ DirectMaskSubRun::DirectMaskSubRun(GrMaskFormat format,
|
||||
SkSpan<const VertexData> vertexData,
|
||||
GlyphVector glyphs)
|
||||
: fMaskFormat{format}
|
||||
, fResidual{residual}
|
||||
, fInitialMappedOrigin{residual}
|
||||
, fBlob{blob}
|
||||
, fVertexBounds{bounds}
|
||||
, fVertexData{vertexData}
|
||||
@ -724,39 +724,37 @@ void DirectMaskSubRun::fillVertexData(void* vertexDst, int offset, int count, Gr
|
||||
fVertexData.subspan(offset, count));
|
||||
};
|
||||
|
||||
SkMatrix matrix = drawMatrix;
|
||||
matrix.preTranslate(drawOrigin.x(), drawOrigin.y());
|
||||
SkPoint o = matrix.mapXY(0, 0) + fResidual;
|
||||
SkIPoint originInDeviceSpace = {SkScalarRoundToInt(o.x()), SkScalarRoundToInt(o.y())};
|
||||
SkPoint originOffset = drawMatrix.mapXY(drawOrigin.x(), drawOrigin.y()) - fInitialMappedOrigin;
|
||||
SkIPoint integralOriginOffset =
|
||||
{SkScalarRoundToInt(originOffset.x()), SkScalarRoundToInt(originOffset.y())};
|
||||
|
||||
if (clip.isEmpty()) {
|
||||
if (fMaskFormat != kARGB_GrMaskFormat) {
|
||||
using Quad = Mask2DVertex[4];
|
||||
SkASSERT(sizeof(Quad) == this->vertexStride() * kVerticesPerGlyph);
|
||||
direct_2D(quadData((Quad*)vertexDst), color, originInDeviceSpace);
|
||||
direct_2D(quadData((Quad*)vertexDst), color, integralOriginOffset);
|
||||
} else {
|
||||
using Quad = ARGB2DVertex[4];
|
||||
SkASSERT(sizeof(Quad) == this->vertexStride() * kVerticesPerGlyph);
|
||||
generalized_direct_2D(quadData((Quad*)vertexDst), color, originInDeviceSpace);
|
||||
generalized_direct_2D(quadData((Quad*)vertexDst), color, integralOriginOffset);
|
||||
}
|
||||
} else {
|
||||
if (fMaskFormat != kARGB_GrMaskFormat) {
|
||||
using Quad = Mask2DVertex[4];
|
||||
SkASSERT(sizeof(Quad) == this->vertexStride() * kVerticesPerGlyph);
|
||||
generalized_direct_2D(quadData((Quad*)vertexDst), color, originInDeviceSpace, &clip);
|
||||
generalized_direct_2D(quadData((Quad*)vertexDst), color, integralOriginOffset, &clip);
|
||||
} else {
|
||||
using Quad = ARGB2DVertex[4];
|
||||
SkASSERT(sizeof(Quad) == this->vertexStride() * kVerticesPerGlyph);
|
||||
generalized_direct_2D(quadData((Quad*)vertexDst), color, originInDeviceSpace, &clip);
|
||||
generalized_direct_2D(quadData((Quad*)vertexDst), color, integralOriginOffset, &clip);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SkRect DirectMaskSubRun::deviceRect(const SkMatrix& drawMatrix, SkPoint drawOrigin) const {
|
||||
SkRect outBounds = fVertexBounds;
|
||||
|
||||
SkPoint offset = drawMatrix.mapXY(drawOrigin.x(), drawOrigin.y());
|
||||
SkPoint offset = drawMatrix.mapXY(drawOrigin.x(), drawOrigin.y()) - fInitialMappedOrigin;
|
||||
// The vertex bounds are already {0, 0} based, so just add the new origin offset.
|
||||
outBounds.offset(offset);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user