Fix SDF generation for pixel-aligned paths
BUG=668550 Change-Id: Ib496db82c7391aca61b31afaeb5445260170cc49 Reviewed-on: https://skia-review.googlesource.com/5549 Reviewed-by: Robert Phillips <robertphillips@google.com> Commit-Queue: Jim Van Verth <jvanverth@google.com>
This commit is contained in:
parent
d7e16661bb
commit
92964124c5
@ -50,15 +50,15 @@ static SkScalar make_oval(SkPath* path) {
|
||||
return SkIntToScalar(30);
|
||||
}
|
||||
|
||||
static SkScalar make_sawtooth(SkPath* path) {
|
||||
static SkScalar make_sawtooth(SkPath* path, int teeth) {
|
||||
SkScalar x = SkIntToScalar(20);
|
||||
SkScalar y = SkIntToScalar(20);
|
||||
const SkScalar x0 = x;
|
||||
const SkScalar dx = SK_Scalar1 * 5;
|
||||
const SkScalar dy = SK_Scalar1 * 10;
|
||||
const SkScalar dx = SkIntToScalar(5);
|
||||
const SkScalar dy = SkIntToScalar(10);
|
||||
|
||||
path->moveTo(x, y);
|
||||
for (int i = 0; i < 32; i++) {
|
||||
for (int i = 0; i < teeth; i++) {
|
||||
x += dx;
|
||||
path->lineTo(x, y - dy);
|
||||
x += dx;
|
||||
@ -70,6 +70,33 @@ static SkScalar make_sawtooth(SkPath* path) {
|
||||
return SkIntToScalar(30);
|
||||
}
|
||||
|
||||
static SkScalar make_sawtooth_3(SkPath* path) { return make_sawtooth(path, 3); }
|
||||
static SkScalar make_sawtooth_32(SkPath* path) { return make_sawtooth(path, 32); }
|
||||
|
||||
static SkScalar make_house(SkPath* path) {
|
||||
path->moveTo(21, 23);
|
||||
path->lineTo(21, 11.534f);
|
||||
path->lineTo(22.327f, 12.741f);
|
||||
path->lineTo(23.673f, 11.261f);
|
||||
path->lineTo(12, 0.648f);
|
||||
path->lineTo(8, 4.285f);
|
||||
path->lineTo(8, 2);
|
||||
path->lineTo(4, 2);
|
||||
path->lineTo(4, 7.921f);
|
||||
path->lineTo(0.327f, 11.26f);
|
||||
path->lineTo(1.673f, 12.74f);
|
||||
path->lineTo(3, 11.534f);
|
||||
path->lineTo(3, 23);
|
||||
path->lineTo(11, 23);
|
||||
path->lineTo(11, 18);
|
||||
path->lineTo(13, 18);
|
||||
path->lineTo(13, 23);
|
||||
path->lineTo(21, 23);
|
||||
path->close();
|
||||
path->offset(20, 0);
|
||||
return SkIntToScalar(30);
|
||||
}
|
||||
|
||||
static SkScalar make_star(SkPath* path, int n) {
|
||||
const SkScalar c = SkIntToScalar(45);
|
||||
const SkScalar r = SkIntToScalar(20);
|
||||
@ -107,10 +134,12 @@ constexpr MakePathProc gProcs[] = {
|
||||
make_triangle,
|
||||
make_rect,
|
||||
make_oval,
|
||||
make_sawtooth,
|
||||
make_sawtooth_32,
|
||||
make_star_5,
|
||||
make_star_13,
|
||||
make_line,
|
||||
make_house,
|
||||
make_sawtooth_3,
|
||||
};
|
||||
|
||||
#define N SK_ARRAY_COUNT(gProcs)
|
||||
|
@ -248,9 +248,16 @@ private:
|
||||
const SkRect& bounds = args.fShape.bounds();
|
||||
SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height());
|
||||
SkScalar size = maxScale * maxDim;
|
||||
uint32_t desiredDimension;
|
||||
if (size <= kSmallMIP) {
|
||||
SkScalar desiredDimension;
|
||||
// For minimizing (or the common case of identity) transforms, we try to
|
||||
// create the DF at the appropriately sized native src-space path resolution.
|
||||
// In the majority of cases this will yield a crisper rendering.
|
||||
if (size <= maxDim && maxDim < kSmallMIP) {
|
||||
desiredDimension = maxDim;
|
||||
} else if (size <= kSmallMIP) {
|
||||
desiredDimension = kSmallMIP;
|
||||
} else if (size <= maxDim) {
|
||||
desiredDimension = maxDim;
|
||||
} else if (size <= kMediumMIP) {
|
||||
desiredDimension = kMediumMIP;
|
||||
} else {
|
||||
@ -258,7 +265,7 @@ private:
|
||||
}
|
||||
|
||||
// check to see if path is cached
|
||||
ShapeData::Key key(args.fShape, desiredDimension);
|
||||
ShapeData::Key key(args.fShape, SkScalarCeilToInt(desiredDimension));
|
||||
ShapeData* shapeData = fShapeCache->find(key);
|
||||
if (nullptr == shapeData || !atlas->hasID(shapeData->fID)) {
|
||||
// Remove the stale cache entry
|
||||
@ -268,6 +275,7 @@ private:
|
||||
delete shapeData;
|
||||
}
|
||||
SkScalar scale = desiredDimension/maxDim;
|
||||
|
||||
shapeData = new ShapeData;
|
||||
if (!this->addPathToAtlas(target,
|
||||
&flushInfo,
|
||||
@ -275,7 +283,7 @@ private:
|
||||
shapeData,
|
||||
args.fShape,
|
||||
args.fAntiAlias,
|
||||
desiredDimension,
|
||||
SkScalarCeilToInt(desiredDimension),
|
||||
scale)) {
|
||||
delete shapeData;
|
||||
SkDebugf("Can't rasterize path\n");
|
||||
@ -311,29 +319,25 @@ private:
|
||||
scaledBounds.fTop *= scale;
|
||||
scaledBounds.fRight *= scale;
|
||||
scaledBounds.fBottom *= scale;
|
||||
// move the origin to an integer boundary (gives better results)
|
||||
SkScalar dx = SkScalarFraction(scaledBounds.fLeft);
|
||||
SkScalar dy = SkScalarFraction(scaledBounds.fTop);
|
||||
// subtract out integer portion of origin
|
||||
// (SDF created will be placed with fractional offset burnt in)
|
||||
SkScalar dx = SkScalarFloorToInt(scaledBounds.fLeft);
|
||||
SkScalar dy = SkScalarFloorToInt(scaledBounds.fTop);
|
||||
scaledBounds.offset(-dx, -dy);
|
||||
// get integer boundary
|
||||
SkIRect devPathBounds;
|
||||
scaledBounds.roundOut(&devPathBounds);
|
||||
// pad to allow room for antialiasing
|
||||
const int intPad = SkScalarCeilToInt(kAntiAliasPad);
|
||||
// pre-move origin (after outset, will be 0,0)
|
||||
int width = devPathBounds.width();
|
||||
int height = devPathBounds.height();
|
||||
devPathBounds.fLeft = intPad;
|
||||
devPathBounds.fTop = intPad;
|
||||
devPathBounds.fRight = intPad + width;
|
||||
devPathBounds.fBottom = intPad + height;
|
||||
devPathBounds.outset(intPad, intPad);
|
||||
// place devBounds at origin
|
||||
int width = devPathBounds.width() + 2*intPad;
|
||||
int height = devPathBounds.height() + 2*intPad;
|
||||
devPathBounds = SkIRect::MakeWH(width, height);
|
||||
|
||||
// draw path to bitmap
|
||||
SkMatrix drawMatrix;
|
||||
drawMatrix.setTranslate(-bounds.left(), -bounds.top());
|
||||
drawMatrix.postScale(scale, scale);
|
||||
drawMatrix.postTranslate(kAntiAliasPad, kAntiAliasPad);
|
||||
drawMatrix.setScale(scale, scale);
|
||||
drawMatrix.postTranslate(intPad - dx, intPad - dy);
|
||||
|
||||
// setup bitmap backing
|
||||
SkASSERT(devPathBounds.fLeft == 0);
|
||||
@ -378,7 +382,7 @@ private:
|
||||
// add to atlas
|
||||
SkIPoint16 atlasLocation;
|
||||
GrBatchAtlas::AtlasID id;
|
||||
if (!atlas->addToAtlas(&id, target, width, height, dfStorage.get(), &atlasLocation)) {
|
||||
if (!atlas->addToAtlas(&id, target, width, height, dfStorage.get(), &atlasLocation)) {
|
||||
this->flush(target, flushInfo);
|
||||
if (!atlas->addToAtlas(&id, target, width, height, dfStorage.get(), &atlasLocation)) {
|
||||
return false;
|
||||
@ -387,22 +391,20 @@ private:
|
||||
|
||||
// add to cache
|
||||
shapeData->fKey.set(shape, dimension);
|
||||
shapeData->fScale = scale;
|
||||
shapeData->fID = id;
|
||||
// change the scaled rect to match the size of the inset distance field
|
||||
scaledBounds.fRight = scaledBounds.fLeft +
|
||||
SkIntToScalar(devPathBounds.width() - 2*SK_DistanceFieldInset);
|
||||
scaledBounds.fBottom = scaledBounds.fTop +
|
||||
SkIntToScalar(devPathBounds.height() - 2*SK_DistanceFieldInset);
|
||||
// shift the origin to the correct place relative to the distance field
|
||||
// need to also restore the fractional translation
|
||||
scaledBounds.offset(-SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dx,
|
||||
-SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dy);
|
||||
shapeData->fBounds = scaledBounds;
|
||||
// origin we render from is inset from distance field edge
|
||||
atlasLocation.fX += SK_DistanceFieldInset;
|
||||
atlasLocation.fY += SK_DistanceFieldInset;
|
||||
shapeData->fAtlasLocation = atlasLocation;
|
||||
|
||||
// set the bounds rect to match the size and placement of the distance field in the atlas
|
||||
shapeData->fBounds.setXYWH(atlasLocation.fX + SK_DistanceFieldPad,
|
||||
atlasLocation.fY + SK_DistanceFieldPad,
|
||||
width - 2*SK_DistanceFieldPad,
|
||||
height - 2*SK_DistanceFieldPad);
|
||||
// we also need to store the transformation from texture space to the original path's space
|
||||
// which is a simple uniform scale and translate
|
||||
shapeData->fScaleToDev = SkScalarInvert(scale);
|
||||
dx -= SK_DistanceFieldPad + kAntiAliasPad;
|
||||
dy -= SK_DistanceFieldPad + kAntiAliasPad;
|
||||
shapeData->fTranslateToDev = SkPoint::Make((-atlasLocation.fX + dx)*shapeData->fScaleToDev,
|
||||
(-atlasLocation.fY + dy)*shapeData->fScaleToDev);
|
||||
|
||||
fShapeCache->add(shapeData);
|
||||
fShapeList->addToTail(shapeData);
|
||||
@ -426,11 +428,14 @@ private:
|
||||
SkScalar width = shapeData->fBounds.width();
|
||||
SkScalar height = shapeData->fBounds.height();
|
||||
|
||||
SkScalar invScale = 1.0f / shapeData->fScale;
|
||||
// transform texture bounds to the original path's space
|
||||
SkScalar invScale = shapeData->fScaleToDev;
|
||||
dx *= invScale;
|
||||
dy *= invScale;
|
||||
width *= invScale;
|
||||
height *= invScale;
|
||||
dx += shapeData->fTranslateToDev.fX;
|
||||
dy += shapeData->fTranslateToDev.fY;
|
||||
|
||||
SkPoint* positions = reinterpret_cast<SkPoint*>(offset);
|
||||
|
||||
@ -445,15 +450,12 @@ private:
|
||||
*colorPtr = color;
|
||||
}
|
||||
|
||||
const SkScalar tx = SkIntToScalar(shapeData->fAtlasLocation.fX);
|
||||
const SkScalar ty = SkIntToScalar(shapeData->fAtlasLocation.fY);
|
||||
|
||||
// vertex texture coords
|
||||
SkPoint* textureCoords = (SkPoint*)(offset + sizeof(SkPoint) + sizeof(GrColor));
|
||||
textureCoords->setRectFan(tx / texture->width(),
|
||||
ty / texture->height(),
|
||||
(tx + shapeData->fBounds.width()) / texture->width(),
|
||||
(ty + shapeData->fBounds.height()) / texture->height(),
|
||||
textureCoords->setRectFan((shapeData->fBounds.fLeft) / texture->width(),
|
||||
(shapeData->fBounds.fTop) / texture->height(),
|
||||
(shapeData->fBounds.fRight)/texture->width(),
|
||||
(shapeData->fBounds.fBottom)/texture->height(),
|
||||
vertexStride);
|
||||
}
|
||||
|
||||
|
@ -70,10 +70,10 @@ private:
|
||||
SkAutoSTArray<24, uint32_t> fKey;
|
||||
};
|
||||
Key fKey;
|
||||
SkScalar fScale;
|
||||
GrBatchAtlas::AtlasID fID;
|
||||
SkRect fBounds;
|
||||
SkIPoint16 fAtlasLocation;
|
||||
SkScalar fScaleToDev;
|
||||
SkPoint fTranslateToDev;
|
||||
SK_DECLARE_INTERNAL_LLIST_INTERFACE(ShapeData);
|
||||
|
||||
static inline const Key& GetKey(const ShapeData& data) {
|
||||
|
Loading…
Reference in New Issue
Block a user