Use SDF path miplevels based on the original path's size
Should produce sharper results than arbitrary fixed sizes. Adds a new test to pathfill GM. Was: https://skia-review.googlesource.com/c/8328/ BUG=chromium:682918, skia:6238 Change-Id: Ia62ea5ce6b4a5ac2b8b51d06d57dc951d6c340b8 Reviewed-on: https://skia-review.googlesource.com/8384 Reviewed-by: Brian Salomon <bsalomon@google.com> Commit-Queue: Jim Van Verth <jvanverth@google.com>
This commit is contained in:
parent
fa64774820
commit
f9e678d696
@ -260,29 +260,29 @@ protected:
|
|||||||
virtual void onDraw(SkCanvas* canvas) {
|
virtual void onDraw(SkCanvas* canvas) {
|
||||||
this->makePaths();
|
this->makePaths();
|
||||||
|
|
||||||
SkPaint paint;
|
SkPaint paint;
|
||||||
paint.setAntiAlias(true);
|
paint.setAntiAlias(true);
|
||||||
SkRandom rand;
|
SkRandom rand;
|
||||||
canvas->translate(20 * SK_Scalar1, 20 * SK_Scalar1);
|
canvas->translate(20 * SK_Scalar1, 20 * SK_Scalar1);
|
||||||
|
|
||||||
// As we've added more paths this has gotten pretty big. Scale the whole thing down.
|
// As we've added more paths this has gotten pretty big. Scale the whole thing down.
|
||||||
canvas->scale(2 * SK_Scalar1 / 3, 2 * SK_Scalar1 / 3);
|
canvas->scale(2 * SK_Scalar1 / 3, 2 * SK_Scalar1 / 3);
|
||||||
|
|
||||||
for (int i = 0; i < fPaths.count(); ++i) {
|
for (int i = 0; i < fPaths.count(); ++i) {
|
||||||
canvas->save();
|
canvas->save();
|
||||||
// position the path, and make it at off-integer coords.
|
// position the path, and make it at off-integer coords.
|
||||||
canvas->translate(SK_Scalar1 * 200 * (i % 5) + SK_Scalar1 / 10,
|
canvas->translate(SK_Scalar1 * 200 * (i % 5) + SK_Scalar1 / 10,
|
||||||
SK_Scalar1 * 200 * (i / 5) + 9 * SK_Scalar1 / 10);
|
SK_Scalar1 * 200 * (i / 5) + 9 * SK_Scalar1 / 10);
|
||||||
SkColor color = rand.nextU();
|
SkColor color = rand.nextU();
|
||||||
color |= 0xff000000;
|
color |= 0xff000000;
|
||||||
paint.setColor(color);
|
paint.setColor(color);
|
||||||
#if 0 // This hitting on 32bit Linux builds for some paths. Temporarily disabling while it is
|
#if 0 // This hitting on 32bit Linux builds for some paths. Temporarily disabling while it is
|
||||||
// debugged.
|
// debugged.
|
||||||
SkASSERT(fPaths[i].isConvex());
|
SkASSERT(fPaths[i].isConvex());
|
||||||
#endif
|
#endif
|
||||||
canvas->drawPath(fPaths[i], paint);
|
canvas->drawPath(fPaths[i], paint);
|
||||||
canvas->restore();
|
canvas->restore();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -140,7 +140,7 @@ static SkScalar make_line(SkPath* path) {
|
|||||||
return SkIntToScalar(40);
|
return SkIntToScalar(40);
|
||||||
}
|
}
|
||||||
|
|
||||||
static SkScalar make_info(SkPath* path) {
|
static void make_info(SkPath* path) {
|
||||||
path->moveTo(24, 4);
|
path->moveTo(24, 4);
|
||||||
path->cubicTo(12.94999980926514f,
|
path->cubicTo(12.94999980926514f,
|
||||||
4,
|
4,
|
||||||
@ -179,8 +179,49 @@ static SkScalar make_info(SkPath* path) {
|
|||||||
path->lineTo(26, 14);
|
path->lineTo(26, 14);
|
||||||
path->lineTo(26, 18);
|
path->lineTo(26, 18);
|
||||||
path->close();
|
path->close();
|
||||||
|
}
|
||||||
|
|
||||||
return SkIntToScalar(44);
|
static void make_accessibility(SkPath* path) {
|
||||||
|
path->moveTo(12, 2);
|
||||||
|
path->cubicTo(13.10000038146973f,
|
||||||
|
2,
|
||||||
|
14,
|
||||||
|
2.900000095367432f,
|
||||||
|
14,
|
||||||
|
4);
|
||||||
|
path->cubicTo(14,
|
||||||
|
5.099999904632568f,
|
||||||
|
13.10000038146973f,
|
||||||
|
6,
|
||||||
|
12,
|
||||||
|
6);
|
||||||
|
path->cubicTo(10.89999961853027f,
|
||||||
|
6,
|
||||||
|
10,
|
||||||
|
5.099999904632568f,
|
||||||
|
10,
|
||||||
|
4);
|
||||||
|
path->cubicTo(10,
|
||||||
|
2.900000095367432f,
|
||||||
|
10.89999961853027f,
|
||||||
|
2,
|
||||||
|
12,
|
||||||
|
2);
|
||||||
|
path->close();
|
||||||
|
path->moveTo(21, 9);
|
||||||
|
path->lineTo(15, 9);
|
||||||
|
path->lineTo(15, 22);
|
||||||
|
path->lineTo(13, 22);
|
||||||
|
path->lineTo(13, 16);
|
||||||
|
path->lineTo(11, 16);
|
||||||
|
path->lineTo(11, 22);
|
||||||
|
path->lineTo(9, 22);
|
||||||
|
path->lineTo(9, 9);
|
||||||
|
path->lineTo(3, 9);
|
||||||
|
path->lineTo(3, 7);
|
||||||
|
path->lineTo(21, 7);
|
||||||
|
path->lineTo(21, 9);
|
||||||
|
path->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr MakePathProc gProcs[] = {
|
constexpr MakePathProc gProcs[] = {
|
||||||
@ -202,13 +243,15 @@ class PathFillGM : public skiagm::GM {
|
|||||||
SkPath fPath[N];
|
SkPath fPath[N];
|
||||||
SkScalar fDY[N];
|
SkScalar fDY[N];
|
||||||
SkPath fInfoPath;
|
SkPath fInfoPath;
|
||||||
|
SkPath fAccessibilityPath;
|
||||||
protected:
|
protected:
|
||||||
void onOnceBeforeDraw() override {
|
void onOnceBeforeDraw() override {
|
||||||
for (size_t i = 0; i < N; i++) {
|
for (size_t i = 0; i < N; i++) {
|
||||||
fDY[i] = gProcs[i](&fPath[i]);
|
fDY[i] = gProcs[i](&fPath[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
(void) make_info(&fInfoPath);
|
make_info(&fInfoPath);
|
||||||
|
make_accessibility(&fAccessibilityPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -229,9 +272,15 @@ protected:
|
|||||||
canvas->translate(SkIntToScalar(0), fDY[i]);
|
canvas->translate(SkIntToScalar(0), fDY[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
canvas->save();
|
||||||
canvas->scale(0.300000011920929f, 0.300000011920929f);
|
canvas->scale(0.300000011920929f, 0.300000011920929f);
|
||||||
canvas->translate(50, 50);
|
canvas->translate(50, 50);
|
||||||
canvas->drawPath(fInfoPath, paint);
|
canvas->drawPath(fInfoPath, paint);
|
||||||
|
canvas->restore();
|
||||||
|
|
||||||
|
canvas->scale(2, 2);
|
||||||
|
canvas->translate(5, 15);
|
||||||
|
canvas->drawPath(fAccessibilityPath, paint);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -131,6 +131,16 @@ static inline int SkNextPow2(int value) {
|
|||||||
return 1 << (32 - SkCLZ(value - 1));
|
return 1 << (32 - SkCLZ(value - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the largest power-of-2 that is <= the specified value. If value
|
||||||
|
* is already a power of 2, then it is returned unchanged. It is undefined
|
||||||
|
* if value is <= 0.
|
||||||
|
*/
|
||||||
|
static inline int SkPrevPow2(int value) {
|
||||||
|
SkASSERT(value > 0);
|
||||||
|
return 1 << (32 - SkCLZ(value >> 1));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the log2 of the specified value, were that value to be rounded up
|
* Returns the log2 of the specified value, were that value to be rounded up
|
||||||
* to the next power of 2. It is undefined to pass 0. Examples:
|
* to the next power of 2. It is undefined to pass 0. Examples:
|
||||||
@ -145,6 +155,20 @@ static inline int SkNextLog2(uint32_t value) {
|
|||||||
return 32 - SkCLZ(value - 1);
|
return 32 - SkCLZ(value - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the log2 of the specified value, were that value to be rounded down
|
||||||
|
* to the previous power of 2. It is undefined to pass 0. Examples:
|
||||||
|
* SkPrevLog2(1) -> 0
|
||||||
|
* SkPrevLog2(2) -> 1
|
||||||
|
* SkPrevLog2(3) -> 1
|
||||||
|
* SkPrevLog2(4) -> 2
|
||||||
|
* SkPrevLog2(5) -> 2
|
||||||
|
*/
|
||||||
|
static inline int SkPrevLog2(uint32_t value) {
|
||||||
|
SkASSERT(value != 0);
|
||||||
|
return 32 - SkCLZ(value >> 1);
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include "GrBuffer.h"
|
#include "GrBuffer.h"
|
||||||
#include "GrContext.h"
|
#include "GrContext.h"
|
||||||
|
#include "GrDistanceFieldGenFromVector.h"
|
||||||
#include "GrDrawOpTest.h"
|
#include "GrDrawOpTest.h"
|
||||||
#include "GrOpFlushState.h"
|
#include "GrOpFlushState.h"
|
||||||
#include "GrPipelineBuilder.h"
|
#include "GrPipelineBuilder.h"
|
||||||
@ -20,10 +21,9 @@
|
|||||||
#include "effects/GrDistanceFieldGeoProc.h"
|
#include "effects/GrDistanceFieldGeoProc.h"
|
||||||
#include "ops/GrMeshDrawOp.h"
|
#include "ops/GrMeshDrawOp.h"
|
||||||
|
|
||||||
#include "SkPathOps.h"
|
|
||||||
#include "SkAutoMalloc.h"
|
#include "SkAutoMalloc.h"
|
||||||
#include "SkDistanceFieldGen.h"
|
#include "SkDistanceFieldGen.h"
|
||||||
#include "GrDistanceFieldGenFromVector.h"
|
#include "SkPathOps.h"
|
||||||
|
|
||||||
#define ATLAS_TEXTURE_WIDTH 2048
|
#define ATLAS_TEXTURE_WIDTH 2048
|
||||||
#define ATLAS_TEXTURE_HEIGHT 2048
|
#define ATLAS_TEXTURE_HEIGHT 2048
|
||||||
@ -39,9 +39,11 @@ static int g_NumFreedShapes = 0;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// mip levels
|
// mip levels
|
||||||
static const int kSmallMIP = 32;
|
static const SkScalar kMaxMIP = 162;
|
||||||
static const int kMediumMIP = 73;
|
|
||||||
static const int kLargeMIP = 162;
|
static const SkScalar kMaxDim = 73;
|
||||||
|
static const SkScalar kMinSize = 8;
|
||||||
|
static const SkScalar kMaxSize = 2*kMaxMIP;
|
||||||
|
|
||||||
// Callback to clear out internal path cache when eviction occurs
|
// Callback to clear out internal path cache when eviction occurs
|
||||||
void GrAADistanceFieldPathRenderer::HandleEviction(GrDrawOpAtlas::AtlasID id, void* pr) {
|
void GrAADistanceFieldPathRenderer::HandleEviction(GrDrawOpAtlas::AtlasID id, void* pr) {
|
||||||
@ -107,14 +109,20 @@ bool GrAADistanceFieldPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) c
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only support paths with bounds within kMediumMIP by kMediumMIP,
|
// Only support paths with bounds within kMaxDim by kMaxDim,
|
||||||
// scaled to have bounds within 2.0f*kLargeMIP by 2.0f*kLargeMIP.
|
// scaled to have bounds within kMaxSize by kMaxSize.
|
||||||
// The goal is to accelerate rendering of lots of small paths that may be scaling.
|
// The goal is to accelerate rendering of lots of small paths that may be scaling.
|
||||||
SkScalar maxScale = args.fViewMatrix->getMaxScale();
|
SkScalar scaleFactors[2];
|
||||||
|
if (!args.fViewMatrix->getMinMaxScales(scaleFactors)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
SkRect bounds = args.fShape->styledBounds();
|
SkRect bounds = args.fShape->styledBounds();
|
||||||
|
SkScalar minDim = SkMinScalar(bounds.width(), bounds.height());
|
||||||
SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height());
|
SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height());
|
||||||
|
SkScalar minSize = minDim * scaleFactors[0];
|
||||||
|
SkScalar maxSize = maxDim * scaleFactors[1];
|
||||||
|
|
||||||
return maxDim <= kMediumMIP && maxDim * maxScale <= 2.0f*kLargeMIP;
|
return maxDim <= kMaxDim && kMinSize <= minSize && maxSize <= kMaxSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
@ -240,23 +248,22 @@ private:
|
|||||||
SkScalar maxScale = this->viewMatrix().getMaxScale();
|
SkScalar maxScale = this->viewMatrix().getMaxScale();
|
||||||
const SkRect& bounds = args.fShape.bounds();
|
const SkRect& bounds = args.fShape.bounds();
|
||||||
SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height());
|
SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height());
|
||||||
SkScalar size = maxScale * maxDim;
|
// We try to create the DF at a power of two scaled path resolution (1/2, 1, 2, 4, etc)
|
||||||
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.
|
// In the majority of cases this will yield a crisper rendering.
|
||||||
if (size <= maxDim && maxDim < kSmallMIP) {
|
SkScalar mipScale = 1.0f;
|
||||||
desiredDimension = maxDim;
|
// Our mipscale is the maxScale clamped to the next highest power of 2
|
||||||
} else if (size <= kSmallMIP) {
|
if (maxScale < SK_ScalarHalf) {
|
||||||
desiredDimension = kSmallMIP;
|
SkScalar log = SkScalarFloorToScalar(SkScalarLog2(SkScalarInvert(maxScale)));
|
||||||
} else if (size <= maxDim) {
|
mipScale = SkScalarPow(2, -log);
|
||||||
desiredDimension = maxDim;
|
} else if (maxScale > SK_Scalar1) {
|
||||||
} else if (size <= kMediumMIP) {
|
SkScalar log = SkScalarCeilToScalar(SkScalarLog2(maxScale));
|
||||||
desiredDimension = kMediumMIP;
|
mipScale = SkScalarPow(2, log);
|
||||||
} else {
|
|
||||||
desiredDimension = kLargeMIP;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SkScalar mipSize = mipScale*maxDim;
|
||||||
|
SkASSERT(maxScale * maxDim <= mipSize);
|
||||||
|
SkScalar desiredDimension = SkTMin(mipSize, kMaxMIP);
|
||||||
|
|
||||||
// check to see if path is cached
|
// check to see if path is cached
|
||||||
ShapeData::Key key(args.fShape, SkScalarCeilToInt(desiredDimension));
|
ShapeData::Key key(args.fShape, SkScalarCeilToInt(desiredDimension));
|
||||||
ShapeData* shapeData = fShapeCache->find(key);
|
ShapeData* shapeData = fShapeCache->find(key);
|
||||||
|
Loading…
Reference in New Issue
Block a user