path->close();
}
-// test case for http://crbug.com/695196
-static void make_visualizer(SkPath* path) {
- path->moveTo(1.9520f, 2.0000f);
- path->conicTo(1.5573f, 1.9992f, 1.2782f, 2.2782f, 0.9235f);
- path->conicTo(0.9992f, 2.5573f, 1.0000f, 2.9520f, 0.9235f);
- path->lineTo(1.0000f, 5.4300f);
- path->lineTo(17.0000f, 5.4300f);
- path->lineTo(17.0000f, 2.9520f);
- path->conicTo(17.0008f, 2.5573f, 16.7218f, 2.2782f, 0.9235f);
- path->conicTo(16.4427f, 1.9992f, 16.0480f, 2.0000f, 0.9235f);
- path->lineTo(1.9520f, 2.0000f);
- path->close();
- path->moveTo(2.7140f, 3.1430f);
- path->conicTo(3.0547f, 3.1287f, 3.2292f, 3.4216f, 0.8590f);
- path->conicTo(3.4038f, 3.7145f, 3.2292f, 4.0074f, 0.8590f);
- path->conicTo(3.0547f, 4.3003f, 2.7140f, 4.2860f, 0.8590f);
- path->conicTo(2.1659f, 4.2631f, 2.1659f, 3.7145f, 0.7217f);
- path->conicTo(2.1659f, 3.1659f, 2.7140f, 3.1430f, 0.7217f);
- path->lineTo(2.7140f, 3.1430f);
- path->close();
- path->moveTo(5.0000f, 3.1430f);
- path->conicTo(5.3407f, 3.1287f, 5.5152f, 3.4216f, 0.8590f);
- path->conicTo(5.6898f, 3.7145f, 5.5152f, 4.0074f, 0.8590f);
- path->conicTo(5.3407f, 4.3003f, 5.0000f, 4.2860f, 0.8590f);
- path->conicTo(4.4519f, 4.2631f, 4.4519f, 3.7145f, 0.7217f);
- path->conicTo(4.4519f, 3.1659f, 5.0000f, 3.1430f, 0.7217f);
- path->lineTo(5.0000f, 3.1430f);
- path->close();
- path->moveTo(7.2860f, 3.1430f);
- path->conicTo(7.6267f, 3.1287f, 7.8012f, 3.4216f, 0.8590f);
- path->conicTo(7.9758f, 3.7145f, 7.8012f, 4.0074f, 0.8590f);
- path->conicTo(7.6267f, 4.3003f, 7.2860f, 4.2860f, 0.8590f);
- path->conicTo(6.7379f, 4.2631f, 6.7379f, 3.7145f, 0.7217f);
- path->conicTo(6.7379f, 3.1659f, 7.2860f, 3.1430f, 0.7217f);
- path->close();
- path->moveTo(1.0000f, 6.1900f);
- path->lineTo(1.0000f, 14.3810f);
- path->conicTo(0.9992f, 14.7757f, 1.2782f, 15.0548f, 0.9235f);
- path->conicTo(1.5573f, 15.3338f, 1.9520f, 15.3330f, 0.9235f);
- path->lineTo(16.0480f, 15.3330f);
- path->conicTo(16.4427f, 15.3338f, 16.7218f, 15.0548f, 0.9235f);
- path->conicTo(17.0008f, 14.7757f, 17.0000f, 14.3810f, 0.9235f);
- path->lineTo(17.0000f, 6.1910f);
- path->lineTo(1.0000f, 6.1910f);
- path->lineTo(1.0000f, 6.1900f);
- path->close();
-}
-
constexpr MakePathProc gProcs[] = {
make_frame,
make_triangle,
SkScalar fDY[N];
SkPath fInfoPath;
SkPath fAccessibilityPath;
- SkPath fVisualizerPath;
protected:
void onOnceBeforeDraw() override {
for (size_t i = 0; i < N; i++) {
make_info(&fInfoPath);
make_accessibility(&fAccessibilityPath);
- make_visualizer(&fVisualizerPath);
}
canvas->scale(2, 2);
canvas->translate(5, 15);
canvas->drawPath(fAccessibilityPath, paint);
-
- canvas->scale(0.5f, 0.5f);
- canvas->translate(5, 50);
- canvas->drawPath(fVisualizerPath, paint);
}
private:
#include "GrSWMaskHelper.h"
#include "GrSurfacePriv.h"
#include "GrTexturePriv.h"
-#include "effects/GrBitmapTextGeoProc.h"
#include "effects/GrDistanceFieldGeoProc.h"
#include "ops/GrMeshDrawOp.h"
bool gammaCorrect)
: INHERITED(ClassID()) {
SkASSERT(shape.hasUnstyledKey());
- // Compute bounds
- this->setTransformedBounds(shape.bounds(), viewMatrix, HasAABloat::kYes, IsZeroArea::kNo);
-
-#ifdef SK_BUILD_FOR_ANDROID
- fUsesDistanceField = true;
-#else
- // only use distance fields on desktop to save space in the atlas
- fUsesDistanceField = this->bounds().width() > kMaxMIP || this->bounds().height() > kMaxMIP;
-#endif
fViewMatrix = viewMatrix;
- SkVector translate = SkVector::Make(0, 0);
- if (!fUsesDistanceField) {
- // In this case we don't apply a view matrix, so we need to remove the non-subpixel
- // translation and add it back when we generate the quad for the path
- SkScalar translateX = viewMatrix.getTranslateX();
- SkScalar translateY = viewMatrix.getTranslateY();
- translate = SkVector::Make(SkScalarFloorToScalar(translateX),
- SkScalarFloorToScalar(translateY));
- // Only store the fractional part of the translation in the view matrix
- fViewMatrix.setTranslateX(translateX - translate.fX);
- fViewMatrix.setTranslateY(translateY - translate.fY);
- }
-
- fShapes.emplace_back(Entry{color, shape, translate});
+ fShapes.emplace_back(Entry{color, shape});
fAtlas = atlas;
fShapeCache = shapeCache;
fShapeList = shapeList;
fGammaCorrect = gammaCorrect;
+ // Compute bounds
+ this->setTransformedBounds(shape.bounds(), viewMatrix, HasAABloat::kYes, IsZeroArea::kNo);
}
void getFragmentProcessorAnalysisInputs(FragmentProcessorAnalysisInputs* input) const override {
void onPrepareDraws(Target* target) const override {
int instanceCount = fShapes.count();
+ SkMatrix invert;
+ if (this->usesLocalCoords() && !this->viewMatrix().invert(&invert)) {
+ SkDebugf("Could not invert viewmatrix\n");
+ return;
+ }
+
const SkMatrix& ctm = this->viewMatrix();
+ uint32_t flags = 0;
+ flags |= ctm.isScaleTranslate() ? kScaleOnly_DistanceFieldEffectFlag : 0;
+ flags |= ctm.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
+ flags |= fGammaCorrect ? kGammaCorrect_DistanceFieldEffectFlag : 0;
+
+ GrSamplerParams params(SkShader::kRepeat_TileMode, GrSamplerParams::kBilerp_FilterMode);
FlushInfo flushInfo;
// Setup GrGeometryProcessor
GrDrawOpAtlas* atlas = fAtlas;
- if (fUsesDistanceField) {
- GrSamplerParams params(SkShader::kClamp_TileMode, GrSamplerParams::kBilerp_FilterMode);
-
- uint32_t flags = 0;
- flags |= ctm.isScaleTranslate() ? kScaleOnly_DistanceFieldEffectFlag : 0;
- flags |= ctm.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
- flags |= fGammaCorrect ? kGammaCorrect_DistanceFieldEffectFlag : 0;
-
- flushInfo.fGeometryProcessor = GrDistanceFieldPathGeoProc::Make(
- this->color(), this->viewMatrix(), atlas->getTexture(), params, flags,
- this->usesLocalCoords());
- } else {
- GrSamplerParams params(SkShader::kClamp_TileMode, GrSamplerParams::kNone_FilterMode);
-
- SkMatrix invert;
- if (this->usesLocalCoords()) {
- if (!this->viewMatrix().invert(&invert)) {
- SkDebugf("Could not invert viewmatrix\n");
- return;
- }
- // for local coords, we need to add the translation back in that we removed
- // from the stored view matrix
- invert.preTranslate(-fShapes[0].fTranslate.fX, -fShapes[0].fTranslate.fY);
- }
-
- flushInfo.fGeometryProcessor = GrBitmapTextGeoProc::Make(
- this->color(), atlas->getTexture(), params, kA8_GrMaskFormat, invert,
- this->usesLocalCoords());
- }
+ flushInfo.fGeometryProcessor = GrDistanceFieldPathGeoProc::Make(this->color(),
+ this->viewMatrix(),
+ atlas->getTexture(),
+ params,
+ flags,
+ this->usesLocalCoords());
// allocate vertices
size_t vertexStride = flushInfo.fGeometryProcessor->getVertexStride();
- SkASSERT(vertexStride == sizeof(SkPoint) + sizeof(GrColor) + 2*sizeof(uint16_t));
+ SkASSERT(vertexStride == 2 * sizeof(SkPoint) + sizeof(GrColor));
const GrBuffer* vertexBuffer;
void* vertices = target->makeVertexSpace(vertexStride,
for (int i = 0; i < instanceCount; i++) {
const Entry& args = fShapes[i];
- ShapeData* shapeData;
- SkScalar maxScale;
- if (fUsesDistanceField) {
- // get mip level
- maxScale = SkScalarAbs(this->viewMatrix().getMaxScale());
- const SkRect& bounds = args.fShape.bounds();
- SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height());
- // We try to create the DF at a 2^n scaled path resolution (1/2, 1, 2, 4, etc.)
- // In the majority of cases this will yield a crisper rendering.
- SkScalar mipScale = 1.0f;
- // Our mipscale is the maxScale clamped to the next highest power of 2
- if (maxScale <= SK_ScalarHalf) {
- SkScalar log = SkScalarFloorToScalar(SkScalarLog2(SkScalarInvert(maxScale)));
- mipScale = SkScalarPow(2, -log);
- } else if (maxScale > SK_Scalar1) {
- SkScalar log = SkScalarCeilToScalar(SkScalarLog2(maxScale));
- mipScale = SkScalarPow(2, log);
- }
- SkASSERT(maxScale <= mipScale);
-
- SkScalar mipSize = mipScale*SkScalarAbs(maxDim);
- // For sizes less than kIdealMinMIP we want to use as large a distance field as we can
- // so we can preserve as much detail as possible. However, we can't scale down more
- // than a 1/4 of the size without artifacts. So the idea is that we pick the mipsize
- // just bigger than the ideal, and then scale down until we are no more than 4x the
- // original mipsize.
- if (mipSize < kIdealMinMIP) {
- SkScalar newMipSize = mipSize;
- do {
- newMipSize *= 2;
- } while (newMipSize < kIdealMinMIP);
- while (newMipSize > 4 * mipSize) {
- newMipSize *= 0.25f;
- }
- mipSize = newMipSize;
+ // get mip level
+ SkScalar maxScale = SkScalarAbs(this->viewMatrix().getMaxScale());
+ const SkRect& bounds = args.fShape.bounds();
+ SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height());
+ // We try to create the DF at a power of two scaled path resolution (1/2, 1, 2, 4, etc)
+ // In the majority of cases this will yield a crisper rendering.
+ SkScalar mipScale = 1.0f;
+ // Our mipscale is the maxScale clamped to the next highest power of 2
+ if (maxScale <= SK_ScalarHalf) {
+ SkScalar log = SkScalarFloorToScalar(SkScalarLog2(SkScalarInvert(maxScale)));
+ mipScale = SkScalarPow(2, -log);
+ } else if (maxScale > SK_Scalar1) {
+ SkScalar log = SkScalarCeilToScalar(SkScalarLog2(maxScale));
+ mipScale = SkScalarPow(2, log);
+ }
+ SkASSERT(maxScale <= mipScale);
+
+ SkScalar mipSize = mipScale*SkScalarAbs(maxDim);
+ // For sizes less than kIdealMinMIP we want to use as large a distance field as we can
+ // so we can preserve as much detail as possible. However, we can't scale down more
+ // than a 1/4 of the size without artifacts. So the idea is that we pick the mipsize
+ // just bigger than the ideal, and then scale down until we are no more than 4x the
+ // original mipsize.
+ if (mipSize < kIdealMinMIP) {
+ SkScalar newMipSize = mipSize;
+ do {
+ newMipSize *= 2;
+ } while (newMipSize < kIdealMinMIP);
+ while (newMipSize > 4*mipSize) {
+ newMipSize *= 0.25f;
}
- SkScalar desiredDimension = SkTMin(mipSize, kMaxMIP);
-
- // check to see if df path is cached
- ShapeData::Key key(args.fShape, SkScalarCeilToInt(desiredDimension));
- shapeData = fShapeCache->find(key);
- if (nullptr == shapeData || !atlas->hasID(shapeData->fID)) {
- // Remove the stale cache entry
- if (shapeData) {
- fShapeCache->remove(shapeData->fKey);
- fShapeList->remove(shapeData);
- delete shapeData;
- }
- SkScalar scale = desiredDimension / maxDim;
-
- shapeData = new ShapeData;
- if (!this->addDFPathToAtlas(target,
- &flushInfo,
- atlas,
- shapeData,
- args.fShape,
- SkScalarCeilToInt(desiredDimension),
- scale)) {
- delete shapeData;
- SkDebugf("Can't rasterize path\n");
- continue;
- }
+ mipSize = newMipSize;
+ }
+ SkScalar desiredDimension = SkTMin(mipSize, kMaxMIP);
+
+ // check to see if path is cached
+ ShapeData::Key key(args.fShape, SkScalarCeilToInt(desiredDimension));
+ ShapeData* shapeData = fShapeCache->find(key);
+ if (nullptr == shapeData || !atlas->hasID(shapeData->fID)) {
+ // Remove the stale cache entry
+ if (shapeData) {
+ fShapeCache->remove(shapeData->fKey);
+ fShapeList->remove(shapeData);
+ delete shapeData;
}
- } else {
- // check to see if bitmap path is cached
- ShapeData::Key key(args.fShape, this->viewMatrix());
- shapeData = fShapeCache->find(key);
- if (nullptr == shapeData || !atlas->hasID(shapeData->fID)) {
- // Remove the stale cache entry
- if (shapeData) {
- fShapeCache->remove(shapeData->fKey);
- fShapeList->remove(shapeData);
- delete shapeData;
- }
-
- shapeData = new ShapeData;
- if (!this->addBMPathToAtlas(target,
- &flushInfo,
- atlas,
- shapeData,
- args.fShape,
- this->viewMatrix())) {
- delete shapeData;
- SkDebugf("Can't rasterize path\n");
- continue;
- }
+ SkScalar scale = desiredDimension/maxDim;
+
+ shapeData = new ShapeData;
+ if (!this->addPathToAtlas(target,
+ &flushInfo,
+ atlas,
+ shapeData,
+ args.fShape,
+ SkScalarCeilToInt(desiredDimension),
+ scale)) {
+ delete shapeData;
+ SkDebugf("Can't rasterize path\n");
+ continue;
}
- maxScale = 1;
}
atlas->setLastUseToken(shapeData->fID, target->nextDrawToken());
args.fColor,
vertexStride,
maxScale,
- args.fTranslate,
shapeData);
offset += kVerticesPerQuad * vertexStride;
flushInfo.fInstancesToFlush++;
this->flush(target, &flushInfo);
}
- bool addDFPathToAtlas(GrMeshDrawOp::Target* target, FlushInfo* flushInfo, GrDrawOpAtlas* atlas,
- ShapeData* shapeData, const GrShape& shape, uint32_t dimension,
- SkScalar scale) const {
+ bool addPathToAtlas(GrMeshDrawOp::Target* target, FlushInfo* flushInfo, GrDrawOpAtlas* atlas,
+ ShapeData* shapeData, const GrShape& shape, uint32_t dimension,
+ SkScalar scale) const {
const SkRect& bounds = shape.bounds();
// generate bounding rect for bitmap draw
return true;
}
- bool addBMPathToAtlas(GrMeshDrawOp::Target* target, FlushInfo* flushInfo,
- GrDrawOpAtlas* atlas, ShapeData* shapeData,
- const GrShape& shape, const SkMatrix& ctm) const {
- const SkRect& bounds = shape.bounds();
- if (bounds.isEmpty()) {
- return false;
- }
- SkMatrix drawMatrix(ctm);
- drawMatrix.set(SkMatrix::kMTransX, SkScalarFraction(ctm.get(SkMatrix::kMTransX)));
- drawMatrix.set(SkMatrix::kMTransY, SkScalarFraction(ctm.get(SkMatrix::kMTransY)));
- SkRect shapeDevBounds;
- drawMatrix.mapRect(&shapeDevBounds, bounds);
- SkScalar dx = SkScalarFloorToScalar(shapeDevBounds.fLeft);
- SkScalar dy = SkScalarFloorToScalar(shapeDevBounds.fTop);
-
- // get integer boundary
- SkIRect devPathBounds;
- shapeDevBounds.roundOut(&devPathBounds);
- // pad to allow room for antialiasing
- const int intPad = SkScalarCeilToInt(kAntiAliasPad);
- // place devBounds at origin
- int width = devPathBounds.width() + 2 * intPad;
- int height = devPathBounds.height() + 2 * intPad;
- devPathBounds = SkIRect::MakeWH(width, height);
- SkScalar translateX = intPad - dx;
- SkScalar translateY = intPad - dy;
-
- SkASSERT(devPathBounds.fLeft == 0);
- SkASSERT(devPathBounds.fTop == 0);
- SkASSERT(devPathBounds.width() > 0);
- SkASSERT(devPathBounds.height() > 0);
-
- SkPath path;
- shape.asPath(&path);
- // setup bitmap backing
- SkAutoPixmapStorage dst;
- if (!dst.tryAlloc(SkImageInfo::MakeA8(devPathBounds.width(),
- devPathBounds.height()))) {
- return false;
- }
- sk_bzero(dst.writable_addr(), dst.getSafeSize());
-
- // rasterize path
- SkPaint paint;
- paint.setStyle(SkPaint::kFill_Style);
- paint.setAntiAlias(true);
-
- SkDraw draw;
- sk_bzero(&draw, sizeof(draw));
-
- SkRasterClip rasterClip;
- rasterClip.setRect(devPathBounds);
- draw.fRC = &rasterClip;
- drawMatrix.postTranslate(translateX, translateY);
- draw.fMatrix = &drawMatrix;
- draw.fDst = dst;
-
- draw.drawPathCoverage(path, paint);
-
- // add to atlas
- SkIPoint16 atlasLocation;
- GrDrawOpAtlas::AtlasID id;
- if (!atlas->addToAtlas(&id, target, dst.width(), dst.height(), dst.addr(),
- &atlasLocation)) {
- this->flush(target, flushInfo);
- if (!atlas->addToAtlas(&id, target, dst.width(), dst.height(), dst.addr(),
- &atlasLocation)) {
- return false;
- }
- }
-
- // add to cache
- shapeData->fKey.set(shape, drawMatrix);
- shapeData->fID = id;
-
- // set the bounds rect to the original bounds
- shapeData->fBounds = SkRect::Make(devPathBounds);
- shapeData->fBounds.offset(-translateX, -translateY);
-
- // set up path to texture coordinate transform
- shapeData->fScale = SK_Scalar1;
- shapeData->fTranslate.fX = atlasLocation.fX + translateX;
- shapeData->fTranslate.fY = atlasLocation.fY + translateY;
-
- fShapeCache->add(shapeData);
- fShapeList->addToTail(shapeData);
-#ifdef DF_PATH_TRACKING
- ++g_NumCachedPaths;
-#endif
- return true;
- }
-
void writePathVertices(GrDrawOp::Target* target,
GrDrawOpAtlas* atlas,
intptr_t offset,
GrColor color,
size_t vertexStride,
SkScalar maxScale,
- const SkVector& preTranslate,
const ShapeData* shapeData) const {
SkPoint* positions = reinterpret_cast<SkPoint*>(offset);
+ // outset bounds to include ~1 pixel of AA in device space
SkRect bounds = shapeData->fBounds;
- if (fUsesDistanceField) {
- // outset bounds to include ~1 pixel of AA in device space
- SkScalar outset = SkScalarInvert(maxScale);
- bounds.outset(outset, outset);
- }
+ SkScalar outset = SkScalarInvert(maxScale);
+ bounds.outset(outset, outset);
// vertex positions
// TODO make the vertex attributes a struct
- positions->setRectFan(bounds.left() + preTranslate.fX,
- bounds.top() + preTranslate.fY,
- bounds.right() + preTranslate.fX,
- bounds.bottom() + preTranslate.fY,
+ positions->setRectFan(bounds.left(), bounds.top(), bounds.right(), bounds.bottom(),
vertexStride);
// colors
texRight += translate.fX;
texBottom += translate.fY;
- // convert texcoords to unsigned short format
+ // vertex texture coords
+ // TODO make these int16_t
+ SkPoint* textureCoords = (SkPoint*)(offset + sizeof(SkPoint) + sizeof(GrColor));
GrTexture* texture = atlas->getTexture();
- SkScalar uFactor = 65535.f / texture->width();
- SkScalar vFactor = 65535.f / texture->height();
- uint16_t l = (uint16_t)(texLeft*uFactor);
- uint16_t t = (uint16_t)(texTop*vFactor);
- uint16_t r = (uint16_t)(texRight*uFactor);
- uint16_t b = (uint16_t)(texBottom*vFactor);
-
- // set vertex texture coords
- intptr_t textureCoordOffset = offset + sizeof(SkPoint) + sizeof(GrColor);
- uint16_t* textureCoords = (uint16_t*) textureCoordOffset;
- textureCoords[0] = l;
- textureCoords[1] = t;
- textureCoordOffset += vertexStride;
- textureCoords = (uint16_t*)textureCoordOffset;
- textureCoords[0] = l;
- textureCoords[1] = b;
- textureCoordOffset += vertexStride;
- textureCoords = (uint16_t*)textureCoordOffset;
- textureCoords[0] = r;
- textureCoords[1] = b;
- textureCoordOffset += vertexStride;
- textureCoords = (uint16_t*)textureCoordOffset;
- textureCoords[0] = r;
- textureCoords[1] = t;
+ textureCoords->setRectFan(texLeft / texture->width(),
+ texTop / texture->height(),
+ texRight / texture->width(),
+ texBottom / texture->height(),
+ vertexStride);
}
void flush(GrMeshDrawOp::Target* target, FlushInfo* flushInfo) const {
GrColor color() const { return fShapes[0].fColor; }
const SkMatrix& viewMatrix() const { return fViewMatrix; }
bool usesLocalCoords() const { return fUsesLocalCoords; }
- bool usesDistanceField() const { return fUsesDistanceField; }
bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
AADistanceFieldPathOp* that = t->cast<AADistanceFieldPathOp>();
return false;
}
- if (this->usesDistanceField() != that->usesDistanceField()) {
- return false;
- }
-
- // TODO We can position on the cpu for distance field paths
+ // TODO We can position on the cpu
if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
return false;
}
- if (!this->usesDistanceField() && this->usesLocalCoords() &&
- !this->fShapes[0].fTranslate.equalsWithinTolerance(that->fShapes[0].fTranslate)) {
- return false;
- }
-
fShapes.push_back_n(that->fShapes.count(), that->fShapes.begin());
this->joinBounds(*that);
return true;
SkMatrix fViewMatrix;
bool fUsesLocalCoords;
- bool fUsesDistanceField;
struct Entry {
- GrColor fColor;
- GrShape fShape;
- SkVector fTranslate;
+ GrColor fColor;
+ GrShape fShape;
};
SkSTArray<1, Entry> fShapes;