blob: 048351bab79d935c57f08bd93275e1aa1691bc37 [file] [log] [blame]
jvanverthfa38a302014-10-06 05:59:05 -07001/*
2 * Copyright 2014 Google Inc.
joel.liang8cbb4242017-01-09 18:39:43 -08003 * Copyright 2017 ARM Ltd.
jvanverthfa38a302014-10-06 05:59:05 -07004 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
Jim Van Verth83010462017-03-16 08:45:39 -04009#include "GrSmallPathRenderer.h"
jvanverthfa38a302014-10-06 05:59:05 -070010
cdalton397536c2016-03-25 12:15:03 -070011#include "GrBuffer.h"
jvanverthfa38a302014-10-06 05:59:05 -070012#include "GrContext.h"
Jim Van Verthf9e678d2017-02-15 15:46:52 -050013#include "GrDistanceFieldGenFromVector.h"
Brian Salomon5ec9def2016-12-20 15:34:05 -050014#include "GrDrawOpTest.h"
Brian Salomon742e31d2016-12-07 17:06:19 -050015#include "GrOpFlushState.h"
bsalomonbb243832016-07-22 07:10:19 -070016#include "GrPipelineBuilder.h"
bsalomonb5238a72015-05-05 07:49:49 -070017#include "GrResourceProvider.h"
jvanverthfa38a302014-10-06 05:59:05 -070018#include "GrSWMaskHelper.h"
Brian Salomondad29232016-12-01 16:40:24 -050019#include "GrSurfacePriv.h"
Robert Phillips32f28182017-02-28 16:20:03 -050020#include "GrSurfaceProxyPriv.h"
jvanverthfa38a302014-10-06 05:59:05 -070021#include "GrTexturePriv.h"
Jim Van Verth33632d82017-02-28 10:24:39 -050022#include "effects/GrBitmapTextGeoProc.h"
jvanverth8ed3b9a2015-04-09 08:00:49 -070023#include "effects/GrDistanceFieldGeoProc.h"
Brian Salomon89527432016-12-16 09:52:16 -050024#include "ops/GrMeshDrawOp.h"
jvanverthfa38a302014-10-06 05:59:05 -070025
Hal Canary95e3c052017-01-11 12:44:43 -050026#include "SkAutoMalloc.h"
jvanverthfa38a302014-10-06 05:59:05 -070027#include "SkDistanceFieldGen.h"
Jim Van Verthf9e678d2017-02-15 15:46:52 -050028#include "SkPathOps.h"
jvanverthfa38a302014-10-06 05:59:05 -070029
jvanverthfb1e2fc2015-09-15 13:11:11 -070030#define ATLAS_TEXTURE_WIDTH 2048
jvanverthb61283f2014-10-30 05:57:21 -070031#define ATLAS_TEXTURE_HEIGHT 2048
jvanverthfb1e2fc2015-09-15 13:11:11 -070032#define PLOT_WIDTH 512
reede4ef1ca2015-02-17 18:38:38 -080033#define PLOT_HEIGHT 256
jvanverthfa38a302014-10-06 05:59:05 -070034
35#define NUM_PLOTS_X (ATLAS_TEXTURE_WIDTH / PLOT_WIDTH)
36#define NUM_PLOTS_Y (ATLAS_TEXTURE_HEIGHT / PLOT_HEIGHT)
37
jvanverthb3eb6872014-10-24 07:12:51 -070038#ifdef DF_PATH_TRACKING
bsalomonee432412016-06-27 07:18:18 -070039static int g_NumCachedShapes = 0;
40static int g_NumFreedShapes = 0;
jvanverthb3eb6872014-10-24 07:12:51 -070041#endif
42
jvanverthb61283f2014-10-30 05:57:21 -070043// mip levels
Jim Van Verth25b8ca12017-02-17 14:02:13 -050044static const SkScalar kIdealMinMIP = 12;
Jim Van Verthf9e678d2017-02-15 15:46:52 -050045static const SkScalar kMaxMIP = 162;
46
47static const SkScalar kMaxDim = 73;
Jim Van Verth25b8ca12017-02-17 14:02:13 -050048static const SkScalar kMinSize = SK_ScalarHalf;
Jim Van Verthf9e678d2017-02-15 15:46:52 -050049static const SkScalar kMaxSize = 2*kMaxMIP;
jvanverthb61283f2014-10-30 05:57:21 -070050
joshualitt5bf99f12015-03-13 11:47:42 -070051// Callback to clear out internal path cache when eviction occurs
Jim Van Verth83010462017-03-16 08:45:39 -040052void GrSmallPathRenderer::HandleEviction(GrDrawOpAtlas::AtlasID id, void* pr) {
53 GrSmallPathRenderer* dfpr = (GrSmallPathRenderer*)pr;
joshualitt5bf99f12015-03-13 11:47:42 -070054 // remove any paths that use this plot
bsalomonee432412016-06-27 07:18:18 -070055 ShapeDataList::Iter iter;
56 iter.init(dfpr->fShapeList, ShapeDataList::Iter::kHead_IterStart);
57 ShapeData* shapeData;
58 while ((shapeData = iter.get())) {
joshualitt5bf99f12015-03-13 11:47:42 -070059 iter.next();
bsalomonee432412016-06-27 07:18:18 -070060 if (id == shapeData->fID) {
61 dfpr->fShapeCache.remove(shapeData->fKey);
62 dfpr->fShapeList.remove(shapeData);
63 delete shapeData;
joshualitt5bf99f12015-03-13 11:47:42 -070064#ifdef DF_PATH_TRACKING
65 ++g_NumFreedPaths;
66#endif
67 }
68 }
69}
70
jvanverthfa38a302014-10-06 05:59:05 -070071////////////////////////////////////////////////////////////////////////////////
Jim Van Verth83010462017-03-16 08:45:39 -040072GrSmallPathRenderer::GrSmallPathRenderer() : fAtlas(nullptr) {}
jvanverth6d22eca2014-10-28 11:10:48 -070073
Jim Van Verth83010462017-03-16 08:45:39 -040074GrSmallPathRenderer::~GrSmallPathRenderer() {
bsalomonee432412016-06-27 07:18:18 -070075 ShapeDataList::Iter iter;
76 iter.init(fShapeList, ShapeDataList::Iter::kHead_IterStart);
77 ShapeData* shapeData;
78 while ((shapeData = iter.get())) {
jvanverthfa38a302014-10-06 05:59:05 -070079 iter.next();
bsalomonee432412016-06-27 07:18:18 -070080 delete shapeData;
jvanverthfa38a302014-10-06 05:59:05 -070081 }
jvanverthb3eb6872014-10-24 07:12:51 -070082
83#ifdef DF_PATH_TRACKING
bsalomonee432412016-06-27 07:18:18 -070084 SkDebugf("Cached shapes: %d, freed shapes: %d\n", g_NumCachedShapes, g_NumFreedShapes);
jvanverthb3eb6872014-10-24 07:12:51 -070085#endif
jvanverthfa38a302014-10-06 05:59:05 -070086}
87
88////////////////////////////////////////////////////////////////////////////////
Jim Van Verth83010462017-03-16 08:45:39 -040089bool GrSmallPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
Eric Karl5c779752017-05-08 12:02:07 -070090 if (!args.fCaps->shaderCaps()->shaderDerivativeSupport()) {
bsalomonee432412016-06-27 07:18:18 -070091 return false;
92 }
93 // If the shape has no key then we won't get any reuse.
94 if (!args.fShape->hasUnstyledKey()) {
95 return false;
96 }
97 // This only supports filled paths, however, the caller may apply the style to make a filled
98 // path and try again.
99 if (!args.fShape->style().isSimpleFill()) {
100 return false;
101 }
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500102 // This does non-inverse coverage-based antialiased fills.
103 if (GrAAType::kCoverage != args.fAAType) {
bsalomon6663acf2016-05-10 09:14:17 -0700104 return false;
105 }
jvanverthfa38a302014-10-06 05:59:05 -0700106 // TODO: Support inverse fill
bsalomondb7979a2016-06-27 11:08:43 -0700107 if (args.fShape->inverseFilled()) {
jvanverthfa38a302014-10-06 05:59:05 -0700108 return false;
109 }
jvanverthb61283f2014-10-30 05:57:21 -0700110 // currently don't support perspective
bsalomon0aff2fa2015-07-31 06:48:27 -0700111 if (args.fViewMatrix->hasPerspective()) {
jvanverthfa38a302014-10-06 05:59:05 -0700112 return false;
113 }
halcanary9d524f22016-03-29 09:03:52 -0700114
Jim Van Verthf9e678d2017-02-15 15:46:52 -0500115 // Only support paths with bounds within kMaxDim by kMaxDim,
116 // scaled to have bounds within kMaxSize by kMaxSize.
Jim Van Verth77047542017-01-11 14:17:00 -0500117 // The goal is to accelerate rendering of lots of small paths that may be scaling.
Jim Van Verthf9e678d2017-02-15 15:46:52 -0500118 SkScalar scaleFactors[2];
119 if (!args.fViewMatrix->getMinMaxScales(scaleFactors)) {
120 return false;
121 }
bsalomon0a0f67e2016-06-28 11:56:42 -0700122 SkRect bounds = args.fShape->styledBounds();
Jim Van Verthf9e678d2017-02-15 15:46:52 -0500123 SkScalar minDim = SkMinScalar(bounds.width(), bounds.height());
bsalomon6663acf2016-05-10 09:14:17 -0700124 SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height());
Jim Van Verthd25cc9b2017-02-16 10:01:46 -0500125 SkScalar minSize = minDim * SkScalarAbs(scaleFactors[0]);
126 SkScalar maxSize = maxDim * SkScalarAbs(scaleFactors[1]);
bsalomon6266dca2016-03-11 06:22:00 -0800127
Jim Van Verthf9e678d2017-02-15 15:46:52 -0500128 return maxDim <= kMaxDim && kMinSize <= minSize && maxSize <= kMaxSize;
jvanverthfa38a302014-10-06 05:59:05 -0700129}
130
jvanverthfa38a302014-10-06 05:59:05 -0700131////////////////////////////////////////////////////////////////////////////////
132
joshualitt5bf99f12015-03-13 11:47:42 -0700133// padding around path bounds to allow for antialiased pixels
134static const SkScalar kAntiAliasPad = 1.0f;
135
Brian Salomond3ccb0a2017-04-03 10:38:00 -0400136class SmallPathOp final : public GrLegacyMeshDrawOp {
joshualitt5bf99f12015-03-13 11:47:42 -0700137public:
Brian Salomon25a88092016-12-01 09:36:50 -0500138 DEFINE_OP_CLASS_ID
reed1b55a962015-09-17 20:16:13 -0700139
Jim Van Verth83010462017-03-16 08:45:39 -0400140 using ShapeData = GrSmallPathRenderer::ShapeData;
Brian Salomond0a0a652016-12-15 15:25:22 -0500141 using ShapeCache = SkTDynamicHash<ShapeData, ShapeData::Key>;
Jim Van Verth83010462017-03-16 08:45:39 -0400142 using ShapeDataList = GrSmallPathRenderer::ShapeDataList;
joshualitt5bf99f12015-03-13 11:47:42 -0700143
Brian Salomond3ccb0a2017-04-03 10:38:00 -0400144 static std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color, const GrShape& shape,
145 const SkMatrix& viewMatrix,
146 GrDrawOpAtlas* atlas, ShapeCache* shapeCache,
147 ShapeDataList* shapeList, bool gammaCorrect) {
148 return std::unique_ptr<GrLegacyMeshDrawOp>(new SmallPathOp(
149 color, shape, viewMatrix, atlas, shapeCache, shapeList, gammaCorrect));
joshualitt5bf99f12015-03-13 11:47:42 -0700150 }
Brian Salomond0a0a652016-12-15 15:25:22 -0500151
Jim Van Verth83010462017-03-16 08:45:39 -0400152 const char* name() const override { return "SmallPathOp"; }
joshualitt5bf99f12015-03-13 11:47:42 -0700153
Brian Salomon7c3e7182016-12-01 09:35:30 -0500154 SkString dumpInfo() const override {
155 SkString string;
Brian Salomond0a0a652016-12-15 15:25:22 -0500156 for (const auto& geo : fShapes) {
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500157 string.appendf("Color: 0x%08x\n", geo.fColor);
Brian Salomon7c3e7182016-12-01 09:35:30 -0500158 }
159 string.append(DumpPipelineInfo(*this->pipeline()));
160 string.append(INHERITED::dumpInfo());
161 return string;
162 }
163
bsalomone46f9fe2015-08-18 06:05:14 -0700164private:
Jim Van Verth83010462017-03-16 08:45:39 -0400165 SmallPathOp(GrColor color, const GrShape& shape, const SkMatrix& viewMatrix,
166 GrDrawOpAtlas* atlas, ShapeCache* shapeCache, ShapeDataList* shapeList,
167 bool gammaCorrect)
Brian Salomond0a0a652016-12-15 15:25:22 -0500168 : INHERITED(ClassID()) {
169 SkASSERT(shape.hasUnstyledKey());
Jim Van Verth33632d82017-02-28 10:24:39 -0500170 // Compute bounds
171 this->setTransformedBounds(shape.bounds(), viewMatrix, HasAABloat::kYes, IsZeroArea::kNo);
172
Jim Van Verth83010462017-03-16 08:45:39 -0400173#if defined(SK_BUILD_FOR_ANDROID) && !defined(SK_BUILD_FOR_ANDROID_FRAMEWORK)
Jim Van Verth33632d82017-02-28 10:24:39 -0500174 fUsesDistanceField = true;
175#else
Jim Van Verth83010462017-03-16 08:45:39 -0400176 // only use distance fields on desktop and Android framework to save space in the atlas
Jim Van Verth33632d82017-02-28 10:24:39 -0500177 fUsesDistanceField = this->bounds().width() > kMaxMIP || this->bounds().height() > kMaxMIP;
178#endif
Brian Salomond0a0a652016-12-15 15:25:22 -0500179 fViewMatrix = viewMatrix;
Jim Van Verth33632d82017-02-28 10:24:39 -0500180 SkVector translate = SkVector::Make(0, 0);
181 if (!fUsesDistanceField) {
182 // In this case we don't apply a view matrix, so we need to remove the non-subpixel
183 // translation and add it back when we generate the quad for the path
184 SkScalar translateX = viewMatrix.getTranslateX();
185 SkScalar translateY = viewMatrix.getTranslateY();
186 translate = SkVector::Make(SkScalarFloorToScalar(translateX),
187 SkScalarFloorToScalar(translateY));
188 // Only store the fractional part of the translation in the view matrix
189 fViewMatrix.setTranslateX(translateX - translate.fX);
190 fViewMatrix.setTranslateY(translateY - translate.fY);
191 }
192
193 fShapes.emplace_back(Entry{color, shape, translate});
Brian Salomond0a0a652016-12-15 15:25:22 -0500194
195 fAtlas = atlas;
196 fShapeCache = shapeCache;
197 fShapeList = shapeList;
198 fGammaCorrect = gammaCorrect;
199
Brian Salomond0a0a652016-12-15 15:25:22 -0500200 }
201
Brian Salomona811b122017-03-30 08:21:32 -0400202 void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
203 GrProcessorAnalysisCoverage* coverage) const override {
Brian Salomonc0b642c2017-03-27 13:09:36 -0400204 color->setToConstant(fShapes[0].fColor);
Brian Salomona811b122017-03-30 08:21:32 -0400205 *coverage = GrProcessorAnalysisCoverage::kSingleChannel;
Brian Salomon92aee3d2016-12-21 09:20:25 -0500206 }
207
Brian Salomone7d30482017-03-29 12:09:15 -0400208 void applyPipelineOptimizations(const PipelineOptimizations& optimizations) override {
Brian Salomon92aee3d2016-12-21 09:20:25 -0500209 optimizations.getOverrideColorIfSet(&fShapes[0].fColor);
Brian Salomon92aee3d2016-12-21 09:20:25 -0500210 fUsesLocalCoords = optimizations.readsLocalCoords();
joshualitt5bf99f12015-03-13 11:47:42 -0700211 }
212
bsalomonb5238a72015-05-05 07:49:49 -0700213 struct FlushInfo {
Hal Canary144caf52016-11-07 17:57:18 -0500214 sk_sp<const GrBuffer> fVertexBuffer;
215 sk_sp<const GrBuffer> fIndexBuffer;
bungeman06ca8ec2016-06-09 08:01:03 -0700216 sk_sp<GrGeometryProcessor> fGeometryProcessor;
bsalomonb5238a72015-05-05 07:49:49 -0700217 int fVertexOffset;
218 int fInstancesToFlush;
219 };
220
joshualitt144c3c82015-11-30 12:30:13 -0800221 void onPrepareDraws(Target* target) const override {
Brian Salomond0a0a652016-12-15 15:25:22 -0500222 int instanceCount = fShapes.count();
joshualitt5bf99f12015-03-13 11:47:42 -0700223
jvanverthcf371bb2016-03-10 11:10:43 -0800224 const SkMatrix& ctm = this->viewMatrix();
joshualitt5bf99f12015-03-13 11:47:42 -0700225
bsalomon342bfc22016-04-01 06:06:20 -0700226 FlushInfo flushInfo;
227
joshualitt5bf99f12015-03-13 11:47:42 -0700228 // Setup GrGeometryProcessor
Brian Salomon2ee084e2016-12-16 18:59:19 -0500229 GrDrawOpAtlas* atlas = fAtlas;
Jim Van Verth33632d82017-02-28 10:24:39 -0500230 if (fUsesDistanceField) {
231 GrSamplerParams params(SkShader::kClamp_TileMode, GrSamplerParams::kBilerp_FilterMode);
232
233 uint32_t flags = 0;
234 flags |= ctm.isScaleTranslate() ? kScaleOnly_DistanceFieldEffectFlag : 0;
235 flags |= ctm.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
236 flags |= fGammaCorrect ? kGammaCorrect_DistanceFieldEffectFlag : 0;
237
Robert Phillips296b1cc2017-03-15 10:42:12 -0400238 flushInfo.fGeometryProcessor = GrDistanceFieldPathGeoProc::Make(
239 atlas->context()->resourceProvider(),
Robert Phillips32f28182017-02-28 16:20:03 -0500240 this->color(), this->viewMatrix(), atlas->getProxy(), params, flags,
Jim Van Verth33632d82017-02-28 10:24:39 -0500241 this->usesLocalCoords());
242 } else {
243 GrSamplerParams params(SkShader::kClamp_TileMode, GrSamplerParams::kNone_FilterMode);
244
245 SkMatrix invert;
246 if (this->usesLocalCoords()) {
247 if (!this->viewMatrix().invert(&invert)) {
248 SkDebugf("Could not invert viewmatrix\n");
249 return;
250 }
251 // for local coords, we need to add the translation back in that we removed
252 // from the stored view matrix
253 invert.preTranslate(-fShapes[0].fTranslate.fX, -fShapes[0].fTranslate.fY);
254 }
255
Robert Phillips296b1cc2017-03-15 10:42:12 -0400256 flushInfo.fGeometryProcessor = GrBitmapTextGeoProc::Make(
257 atlas->context()->resourceProvider(),
Robert Phillips32f28182017-02-28 16:20:03 -0500258 this->color(), atlas->getProxy(), params, kA8_GrMaskFormat, invert,
Jim Van Verth33632d82017-02-28 10:24:39 -0500259 this->usesLocalCoords());
260 }
joshualitt5bf99f12015-03-13 11:47:42 -0700261
joshualitt5bf99f12015-03-13 11:47:42 -0700262 // allocate vertices
bsalomon342bfc22016-04-01 06:06:20 -0700263 size_t vertexStride = flushInfo.fGeometryProcessor->getVertexStride();
Jim Van Verth33632d82017-02-28 10:24:39 -0500264 SkASSERT(vertexStride == sizeof(SkPoint) + sizeof(GrColor) + 2*sizeof(uint16_t));
bsalomonb5238a72015-05-05 07:49:49 -0700265
cdalton397536c2016-03-25 12:15:03 -0700266 const GrBuffer* vertexBuffer;
bsalomon75398562015-08-17 12:55:38 -0700267 void* vertices = target->makeVertexSpace(vertexStride,
268 kVerticesPerQuad * instanceCount,
269 &vertexBuffer,
270 &flushInfo.fVertexOffset);
bsalomonb5238a72015-05-05 07:49:49 -0700271 flushInfo.fVertexBuffer.reset(SkRef(vertexBuffer));
bsalomon75398562015-08-17 12:55:38 -0700272 flushInfo.fIndexBuffer.reset(target->resourceProvider()->refQuadIndexBuffer());
bsalomonb5238a72015-05-05 07:49:49 -0700273 if (!vertices || !flushInfo.fIndexBuffer) {
joshualitt5bf99f12015-03-13 11:47:42 -0700274 SkDebugf("Could not allocate vertices\n");
275 return;
276 }
277
bsalomonb5238a72015-05-05 07:49:49 -0700278 flushInfo.fInstancesToFlush = 0;
bsalomon6d6b6ad2016-07-13 14:45:28 -0700279 // Pointer to the next set of vertices to write.
280 intptr_t offset = reinterpret_cast<intptr_t>(vertices);
joshualitt5bf99f12015-03-13 11:47:42 -0700281 for (int i = 0; i < instanceCount; i++) {
Brian Salomond0a0a652016-12-15 15:25:22 -0500282 const Entry& args = fShapes[i];
joshualitt5bf99f12015-03-13 11:47:42 -0700283
Jim Van Verth33632d82017-02-28 10:24:39 -0500284 ShapeData* shapeData;
285 SkScalar maxScale;
286 if (fUsesDistanceField) {
287 // get mip level
288 maxScale = SkScalarAbs(this->viewMatrix().getMaxScale());
289 const SkRect& bounds = args.fShape.bounds();
290 SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height());
291 // We try to create the DF at a 2^n scaled path resolution (1/2, 1, 2, 4, etc.)
292 // In the majority of cases this will yield a crisper rendering.
293 SkScalar mipScale = 1.0f;
294 // Our mipscale is the maxScale clamped to the next highest power of 2
295 if (maxScale <= SK_ScalarHalf) {
296 SkScalar log = SkScalarFloorToScalar(SkScalarLog2(SkScalarInvert(maxScale)));
297 mipScale = SkScalarPow(2, -log);
298 } else if (maxScale > SK_Scalar1) {
299 SkScalar log = SkScalarCeilToScalar(SkScalarLog2(maxScale));
300 mipScale = SkScalarPow(2, log);
joshualitt5bf99f12015-03-13 11:47:42 -0700301 }
Jim Van Verth33632d82017-02-28 10:24:39 -0500302 SkASSERT(maxScale <= mipScale);
Jim Van Verthecdb6862016-12-13 18:17:47 -0500303
Jim Van Verth33632d82017-02-28 10:24:39 -0500304 SkScalar mipSize = mipScale*SkScalarAbs(maxDim);
305 // For sizes less than kIdealMinMIP we want to use as large a distance field as we can
306 // so we can preserve as much detail as possible. However, we can't scale down more
307 // than a 1/4 of the size without artifacts. So the idea is that we pick the mipsize
308 // just bigger than the ideal, and then scale down until we are no more than 4x the
309 // original mipsize.
310 if (mipSize < kIdealMinMIP) {
311 SkScalar newMipSize = mipSize;
312 do {
313 newMipSize *= 2;
314 } while (newMipSize < kIdealMinMIP);
315 while (newMipSize > 4 * mipSize) {
316 newMipSize *= 0.25f;
317 }
318 mipSize = newMipSize;
joshualitt5bf99f12015-03-13 11:47:42 -0700319 }
Jim Van Verth33632d82017-02-28 10:24:39 -0500320 SkScalar desiredDimension = SkTMin(mipSize, kMaxMIP);
Jim Van Verthc0bc1bb2017-02-27 18:21:16 -0500321
Jim Van Verth33632d82017-02-28 10:24:39 -0500322 // check to see if df path is cached
323 ShapeData::Key key(args.fShape, SkScalarCeilToInt(desiredDimension));
324 shapeData = fShapeCache->find(key);
325 if (nullptr == shapeData || !atlas->hasID(shapeData->fID)) {
326 // Remove the stale cache entry
327 if (shapeData) {
328 fShapeCache->remove(shapeData->fKey);
329 fShapeList->remove(shapeData);
330 delete shapeData;
331 }
332 SkScalar scale = desiredDimension / maxDim;
333
334 shapeData = new ShapeData;
335 if (!this->addDFPathToAtlas(target,
336 &flushInfo,
337 atlas,
338 shapeData,
339 args.fShape,
340 SkScalarCeilToInt(desiredDimension),
341 scale)) {
342 delete shapeData;
343 SkDebugf("Can't rasterize path\n");
344 continue;
345 }
Jim Van Verthc0bc1bb2017-02-27 18:21:16 -0500346 }
Jim Van Verth33632d82017-02-28 10:24:39 -0500347 } else {
348 // check to see if bitmap path is cached
349 ShapeData::Key key(args.fShape, this->viewMatrix());
350 shapeData = fShapeCache->find(key);
351 if (nullptr == shapeData || !atlas->hasID(shapeData->fID)) {
352 // Remove the stale cache entry
353 if (shapeData) {
354 fShapeCache->remove(shapeData->fKey);
355 fShapeList->remove(shapeData);
356 delete shapeData;
357 }
358
359 shapeData = new ShapeData;
360 if (!this->addBMPathToAtlas(target,
361 &flushInfo,
362 atlas,
363 shapeData,
364 args.fShape,
365 this->viewMatrix())) {
366 delete shapeData;
367 SkDebugf("Can't rasterize path\n");
368 continue;
369 }
370 }
371 maxScale = 1;
joshualitt5bf99f12015-03-13 11:47:42 -0700372 }
373
bsalomonee432412016-06-27 07:18:18 -0700374 atlas->setLastUseToken(shapeData->fID, target->nextDrawToken());
joshualitt5bf99f12015-03-13 11:47:42 -0700375
bsalomon75398562015-08-17 12:55:38 -0700376 this->writePathVertices(target,
bsalomonb5238a72015-05-05 07:49:49 -0700377 atlas,
joshualitt53f26aa2015-12-10 07:29:54 -0800378 offset,
379 args.fColor,
bsalomonb5238a72015-05-05 07:49:49 -0700380 vertexStride,
Jim Van Verthecdb6862016-12-13 18:17:47 -0500381 maxScale,
Jim Van Verth33632d82017-02-28 10:24:39 -0500382 args.fTranslate,
bsalomonee432412016-06-27 07:18:18 -0700383 shapeData);
bsalomon6d6b6ad2016-07-13 14:45:28 -0700384 offset += kVerticesPerQuad * vertexStride;
bsalomonb5238a72015-05-05 07:49:49 -0700385 flushInfo.fInstancesToFlush++;
joshualitt5bf99f12015-03-13 11:47:42 -0700386 }
387
bsalomon75398562015-08-17 12:55:38 -0700388 this->flush(target, &flushInfo);
joshualitt5bf99f12015-03-13 11:47:42 -0700389 }
390
Brian Salomond3ccb0a2017-04-03 10:38:00 -0400391 bool addDFPathToAtlas(GrLegacyMeshDrawOp::Target* target, FlushInfo* flushInfo,
392 GrDrawOpAtlas* atlas, ShapeData* shapeData, const GrShape& shape,
393 uint32_t dimension, SkScalar scale) const {
bsalomonee432412016-06-27 07:18:18 -0700394 const SkRect& bounds = shape.bounds();
joshualitt5bf99f12015-03-13 11:47:42 -0700395
396 // generate bounding rect for bitmap draw
397 SkRect scaledBounds = bounds;
398 // scale to mip level size
399 scaledBounds.fLeft *= scale;
400 scaledBounds.fTop *= scale;
401 scaledBounds.fRight *= scale;
402 scaledBounds.fBottom *= scale;
Jim Van Verthecdb6862016-12-13 18:17:47 -0500403 // subtract out integer portion of origin
404 // (SDF created will be placed with fractional offset burnt in)
Jim Van Verth07b6ad02016-12-20 10:23:09 -0500405 SkScalar dx = SkScalarFloorToScalar(scaledBounds.fLeft);
406 SkScalar dy = SkScalarFloorToScalar(scaledBounds.fTop);
joshualitt5bf99f12015-03-13 11:47:42 -0700407 scaledBounds.offset(-dx, -dy);
408 // get integer boundary
409 SkIRect devPathBounds;
410 scaledBounds.roundOut(&devPathBounds);
411 // pad to allow room for antialiasing
jvanverthecbed9d2015-12-18 10:07:52 -0800412 const int intPad = SkScalarCeilToInt(kAntiAliasPad);
Jim Van Verthecdb6862016-12-13 18:17:47 -0500413 // place devBounds at origin
414 int width = devPathBounds.width() + 2*intPad;
415 int height = devPathBounds.height() + 2*intPad;
416 devPathBounds = SkIRect::MakeWH(width, height);
joshualitt5bf99f12015-03-13 11:47:42 -0700417
418 // draw path to bitmap
419 SkMatrix drawMatrix;
Jim Van Verthecdb6862016-12-13 18:17:47 -0500420 drawMatrix.setScale(scale, scale);
421 drawMatrix.postTranslate(intPad - dx, intPad - dy);
joshualitt5bf99f12015-03-13 11:47:42 -0700422
jvanverth512e4372015-11-23 11:50:02 -0800423 SkASSERT(devPathBounds.fLeft == 0);
424 SkASSERT(devPathBounds.fTop == 0);
Jim Van Verth25b8ca12017-02-17 14:02:13 -0500425 SkASSERT(devPathBounds.width() > 0);
426 SkASSERT(devPathBounds.height() > 0);
bsalomonf93f5152016-10-26 08:00:00 -0700427
joel.liang8cbb4242017-01-09 18:39:43 -0800428 // setup signed distance field storage
429 SkIRect dfBounds = devPathBounds.makeOutset(SK_DistanceFieldPad, SK_DistanceFieldPad);
430 width = dfBounds.width();
431 height = dfBounds.height();
rmistry47842252016-12-21 04:25:18 -0800432 // TODO We should really generate this directly into the plot somehow
433 SkAutoSMalloc<1024> dfStorage(width * height * sizeof(unsigned char));
joel.liang6d2f73c2016-12-20 18:58:53 -0800434
joel.liang8cbb4242017-01-09 18:39:43 -0800435 SkPath path;
436 shape.asPath(&path);
437#ifndef SK_USE_LEGACY_DISTANCE_FIELDS
438 // Generate signed distance field directly from SkPath
439 bool succeed = GrGenerateDistanceFieldFromPath((unsigned char*)dfStorage.get(),
440 path, drawMatrix,
441 width, height, width * sizeof(unsigned char));
442 if (!succeed) {
443#endif
444 // setup bitmap backing
445 SkAutoPixmapStorage dst;
446 if (!dst.tryAlloc(SkImageInfo::MakeA8(devPathBounds.width(),
447 devPathBounds.height()))) {
448 return false;
449 }
450 sk_bzero(dst.writable_addr(), dst.getSafeSize());
451
452 // rasterize path
453 SkPaint paint;
454 paint.setStyle(SkPaint::kFill_Style);
455 paint.setAntiAlias(true);
456
457 SkDraw draw;
458 sk_bzero(&draw, sizeof(draw));
459
460 SkRasterClip rasterClip;
461 rasterClip.setRect(devPathBounds);
462 draw.fRC = &rasterClip;
463 draw.fMatrix = &drawMatrix;
464 draw.fDst = dst;
465
466 draw.drawPathCoverage(path, paint);
467
468 // Generate signed distance field
469 SkGenerateDistanceFieldFromA8Image((unsigned char*)dfStorage.get(),
470 (const unsigned char*)dst.addr(),
471 dst.width(), dst.height(), dst.rowBytes());
472#ifndef SK_USE_LEGACY_DISTANCE_FIELDS
473 }
474#endif
joshualitt5bf99f12015-03-13 11:47:42 -0700475
476 // add to atlas
477 SkIPoint16 atlasLocation;
Brian Salomon2ee084e2016-12-16 18:59:19 -0500478 GrDrawOpAtlas::AtlasID id;
Jim Van Verthecdb6862016-12-13 18:17:47 -0500479 if (!atlas->addToAtlas(&id, target, width, height, dfStorage.get(), &atlasLocation)) {
bsalomon75398562015-08-17 12:55:38 -0700480 this->flush(target, flushInfo);
bsalomon6d6b6ad2016-07-13 14:45:28 -0700481 if (!atlas->addToAtlas(&id, target, width, height, dfStorage.get(), &atlasLocation)) {
482 return false;
483 }
joshualitt5bf99f12015-03-13 11:47:42 -0700484 }
485
486 // add to cache
bsalomonee432412016-06-27 07:18:18 -0700487 shapeData->fKey.set(shape, dimension);
bsalomonee432412016-06-27 07:18:18 -0700488 shapeData->fID = id;
Jim Van Verthecdb6862016-12-13 18:17:47 -0500489
490 // set the bounds rect to the original bounds
491 shapeData->fBounds = bounds;
492
Jim Van Verth77047542017-01-11 14:17:00 -0500493 // set up path to texture coordinate transform
494 shapeData->fScale = scale;
Jim Van Verthecdb6862016-12-13 18:17:47 -0500495 dx -= SK_DistanceFieldPad + kAntiAliasPad;
496 dy -= SK_DistanceFieldPad + kAntiAliasPad;
Jim Van Verth77047542017-01-11 14:17:00 -0500497 shapeData->fTranslate.fX = atlasLocation.fX - dx;
498 shapeData->fTranslate.fY = atlasLocation.fY - dy;
joshualitt5bf99f12015-03-13 11:47:42 -0700499
bsalomonee432412016-06-27 07:18:18 -0700500 fShapeCache->add(shapeData);
501 fShapeList->addToTail(shapeData);
joshualitt5bf99f12015-03-13 11:47:42 -0700502#ifdef DF_PATH_TRACKING
503 ++g_NumCachedPaths;
504#endif
505 return true;
506 }
507
Brian Salomond3ccb0a2017-04-03 10:38:00 -0400508 bool addBMPathToAtlas(GrLegacyMeshDrawOp::Target* target, FlushInfo* flushInfo,
509 GrDrawOpAtlas* atlas, ShapeData* shapeData, const GrShape& shape,
510 const SkMatrix& ctm) const {
Jim Van Verth33632d82017-02-28 10:24:39 -0500511 const SkRect& bounds = shape.bounds();
512 if (bounds.isEmpty()) {
513 return false;
514 }
515 SkMatrix drawMatrix(ctm);
516 drawMatrix.set(SkMatrix::kMTransX, SkScalarFraction(ctm.get(SkMatrix::kMTransX)));
517 drawMatrix.set(SkMatrix::kMTransY, SkScalarFraction(ctm.get(SkMatrix::kMTransY)));
518 SkRect shapeDevBounds;
519 drawMatrix.mapRect(&shapeDevBounds, bounds);
520 SkScalar dx = SkScalarFloorToScalar(shapeDevBounds.fLeft);
521 SkScalar dy = SkScalarFloorToScalar(shapeDevBounds.fTop);
522
523 // get integer boundary
524 SkIRect devPathBounds;
525 shapeDevBounds.roundOut(&devPathBounds);
526 // pad to allow room for antialiasing
527 const int intPad = SkScalarCeilToInt(kAntiAliasPad);
528 // place devBounds at origin
529 int width = devPathBounds.width() + 2 * intPad;
530 int height = devPathBounds.height() + 2 * intPad;
531 devPathBounds = SkIRect::MakeWH(width, height);
532 SkScalar translateX = intPad - dx;
533 SkScalar translateY = intPad - dy;
534
535 SkASSERT(devPathBounds.fLeft == 0);
536 SkASSERT(devPathBounds.fTop == 0);
537 SkASSERT(devPathBounds.width() > 0);
538 SkASSERT(devPathBounds.height() > 0);
539
540 SkPath path;
541 shape.asPath(&path);
542 // setup bitmap backing
543 SkAutoPixmapStorage dst;
544 if (!dst.tryAlloc(SkImageInfo::MakeA8(devPathBounds.width(),
545 devPathBounds.height()))) {
546 return false;
547 }
548 sk_bzero(dst.writable_addr(), dst.getSafeSize());
549
550 // rasterize path
551 SkPaint paint;
552 paint.setStyle(SkPaint::kFill_Style);
553 paint.setAntiAlias(true);
554
555 SkDraw draw;
556 sk_bzero(&draw, sizeof(draw));
557
558 SkRasterClip rasterClip;
559 rasterClip.setRect(devPathBounds);
560 draw.fRC = &rasterClip;
561 drawMatrix.postTranslate(translateX, translateY);
562 draw.fMatrix = &drawMatrix;
563 draw.fDst = dst;
564
565 draw.drawPathCoverage(path, paint);
566
567 // add to atlas
568 SkIPoint16 atlasLocation;
569 GrDrawOpAtlas::AtlasID id;
570 if (!atlas->addToAtlas(&id, target, dst.width(), dst.height(), dst.addr(),
571 &atlasLocation)) {
572 this->flush(target, flushInfo);
573 if (!atlas->addToAtlas(&id, target, dst.width(), dst.height(), dst.addr(),
574 &atlasLocation)) {
575 return false;
576 }
577 }
578
579 // add to cache
580 shapeData->fKey.set(shape, ctm);
581 shapeData->fID = id;
582
583 // set the bounds rect to the original bounds
584 shapeData->fBounds = SkRect::Make(devPathBounds);
585 shapeData->fBounds.offset(-translateX, -translateY);
586
587 // set up path to texture coordinate transform
588 shapeData->fScale = SK_Scalar1;
589 shapeData->fTranslate.fX = atlasLocation.fX + translateX;
590 shapeData->fTranslate.fY = atlasLocation.fY + translateY;
591
592 fShapeCache->add(shapeData);
593 fShapeList->addToTail(shapeData);
594#ifdef DF_PATH_TRACKING
595 ++g_NumCachedPaths;
596#endif
597 return true;
598 }
599
Brian Salomon9afd3712016-12-01 10:59:09 -0500600 void writePathVertices(GrDrawOp::Target* target,
Brian Salomon2ee084e2016-12-16 18:59:19 -0500601 GrDrawOpAtlas* atlas,
joshualitt53f26aa2015-12-10 07:29:54 -0800602 intptr_t offset,
603 GrColor color,
bsalomonb5238a72015-05-05 07:49:49 -0700604 size_t vertexStride,
Jim Van Verthecdb6862016-12-13 18:17:47 -0500605 SkScalar maxScale,
Jim Van Verth33632d82017-02-28 10:24:39 -0500606 const SkVector& preTranslate,
bsalomonee432412016-06-27 07:18:18 -0700607 const ShapeData* shapeData) const {
joshualitt53f26aa2015-12-10 07:29:54 -0800608 SkPoint* positions = reinterpret_cast<SkPoint*>(offset);
609
Jim Van Verth77047542017-01-11 14:17:00 -0500610 SkRect bounds = shapeData->fBounds;
Jim Van Verth33632d82017-02-28 10:24:39 -0500611 if (fUsesDistanceField) {
612 // outset bounds to include ~1 pixel of AA in device space
613 SkScalar outset = SkScalarInvert(maxScale);
614 bounds.outset(outset, outset);
615 }
Jim Van Verth77047542017-01-11 14:17:00 -0500616
joshualitt5bf99f12015-03-13 11:47:42 -0700617 // vertex positions
618 // TODO make the vertex attributes a struct
Jim Van Verth33632d82017-02-28 10:24:39 -0500619 positions->setRectFan(bounds.left() + preTranslate.fX,
620 bounds.top() + preTranslate.fY,
621 bounds.right() + preTranslate.fX,
622 bounds.bottom() + preTranslate.fY,
Jim Van Verth77047542017-01-11 14:17:00 -0500623 vertexStride);
joshualitt5bf99f12015-03-13 11:47:42 -0700624
joshualitt53f26aa2015-12-10 07:29:54 -0800625 // colors
626 for (int i = 0; i < kVerticesPerQuad; i++) {
627 GrColor* colorPtr = (GrColor*)(offset + sizeof(SkPoint) + i * vertexStride);
628 *colorPtr = color;
629 }
630
Jim Van Verth77047542017-01-11 14:17:00 -0500631 // set up texture coordinates
632 SkScalar texLeft = bounds.fLeft;
633 SkScalar texTop = bounds.fTop;
634 SkScalar texRight = bounds.fRight;
635 SkScalar texBottom = bounds.fBottom;
636
637 // transform original path's bounds to texture space
638 SkScalar scale = shapeData->fScale;
639 const SkVector& translate = shapeData->fTranslate;
640 texLeft *= scale;
641 texTop *= scale;
642 texRight *= scale;
643 texBottom *= scale;
644 texLeft += translate.fX;
645 texTop += translate.fY;
646 texRight += translate.fX;
647 texBottom += translate.fY;
648
Jim Van Verth33632d82017-02-28 10:24:39 -0500649 // convert texcoords to unsigned short format
Robert Phillips32f28182017-02-28 16:20:03 -0500650 sk_sp<GrTextureProxy> proxy = atlas->getProxy();
651
Robert Phillipsb66b42f2017-03-14 08:53:02 -0400652 // The proxy must be functionally exact for this normalization to work correctly
653 SkASSERT(GrResourceProvider::IsFunctionallyExact(proxy.get()));
Robert Phillips32f28182017-02-28 16:20:03 -0500654 SkScalar uFactor = 65535.f / proxy->width();
655 SkScalar vFactor = 65535.f / proxy->height();
Jim Van Verth33632d82017-02-28 10:24:39 -0500656 uint16_t l = (uint16_t)(texLeft*uFactor);
657 uint16_t t = (uint16_t)(texTop*vFactor);
658 uint16_t r = (uint16_t)(texRight*uFactor);
659 uint16_t b = (uint16_t)(texBottom*vFactor);
660
661 // set vertex texture coords
662 intptr_t textureCoordOffset = offset + sizeof(SkPoint) + sizeof(GrColor);
663 uint16_t* textureCoords = (uint16_t*) textureCoordOffset;
664 textureCoords[0] = l;
665 textureCoords[1] = t;
666 textureCoordOffset += vertexStride;
667 textureCoords = (uint16_t*)textureCoordOffset;
668 textureCoords[0] = l;
669 textureCoords[1] = b;
670 textureCoordOffset += vertexStride;
671 textureCoords = (uint16_t*)textureCoordOffset;
672 textureCoords[0] = r;
673 textureCoords[1] = b;
674 textureCoordOffset += vertexStride;
675 textureCoords = (uint16_t*)textureCoordOffset;
676 textureCoords[0] = r;
677 textureCoords[1] = t;
joshualitt5bf99f12015-03-13 11:47:42 -0700678 }
679
Brian Salomond3ccb0a2017-04-03 10:38:00 -0400680 void flush(GrLegacyMeshDrawOp::Target* target, FlushInfo* flushInfo) const {
bsalomon6d6b6ad2016-07-13 14:45:28 -0700681 if (flushInfo->fInstancesToFlush) {
Chris Daltonbca46e22017-05-15 11:03:26 -0600682 GrMesh mesh(kTriangles_GrPrimitiveType);
bsalomon6d6b6ad2016-07-13 14:45:28 -0700683 int maxInstancesPerDraw =
684 static_cast<int>(flushInfo->fIndexBuffer->gpuMemorySize() / sizeof(uint16_t) / 6);
Chris Daltonbca46e22017-05-15 11:03:26 -0600685 mesh.setIndexedPatterned(flushInfo->fIndexBuffer.get(), kIndicesPerQuad,
Chris Dalton114a3c02017-05-26 15:17:19 -0600686 kVerticesPerQuad, flushInfo->fInstancesToFlush,
687 maxInstancesPerDraw);
688 mesh.setVertexData(flushInfo->fVertexBuffer.get(), flushInfo->fVertexOffset);
Brian Salomond3ccb0a2017-04-03 10:38:00 -0400689 target->draw(flushInfo->fGeometryProcessor.get(), this->pipeline(), mesh);
bsalomon6d6b6ad2016-07-13 14:45:28 -0700690 flushInfo->fVertexOffset += kVerticesPerQuad * flushInfo->fInstancesToFlush;
691 flushInfo->fInstancesToFlush = 0;
692 }
joshualitt5bf99f12015-03-13 11:47:42 -0700693 }
694
Brian Salomond0a0a652016-12-15 15:25:22 -0500695 GrColor color() const { return fShapes[0].fColor; }
696 const SkMatrix& viewMatrix() const { return fViewMatrix; }
697 bool usesLocalCoords() const { return fUsesLocalCoords; }
Jim Van Verth33632d82017-02-28 10:24:39 -0500698 bool usesDistanceField() const { return fUsesDistanceField; }
joshualitt5bf99f12015-03-13 11:47:42 -0700699
Brian Salomon25a88092016-12-01 09:36:50 -0500700 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
Jim Van Verth83010462017-03-16 08:45:39 -0400701 SmallPathOp* that = t->cast<SmallPathOp>();
bsalomonabd30f52015-08-13 13:34:48 -0700702 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
703 that->bounds(), caps)) {
joshualitt8cab9a72015-07-16 09:13:50 -0700704 return false;
705 }
706
Jim Van Verth33632d82017-02-28 10:24:39 -0500707 if (this->usesDistanceField() != that->usesDistanceField()) {
708 return false;
709 }
710
711 // TODO We can position on the cpu for distance field paths
joshualitt5bf99f12015-03-13 11:47:42 -0700712 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
713 return false;
714 }
715
Jim Van Verth33632d82017-02-28 10:24:39 -0500716 if (!this->usesDistanceField() && this->usesLocalCoords() &&
717 !this->fShapes[0].fTranslate.equalsWithinTolerance(that->fShapes[0].fTranslate)) {
718 return false;
719 }
720
Brian Salomond0a0a652016-12-15 15:25:22 -0500721 fShapes.push_back_n(that->fShapes.count(), that->fShapes.begin());
bsalomon88cf17d2016-07-08 06:40:56 -0700722 this->joinBounds(*that);
joshualitt5bf99f12015-03-13 11:47:42 -0700723 return true;
724 }
725
Brian Salomond0a0a652016-12-15 15:25:22 -0500726 SkMatrix fViewMatrix;
727 bool fUsesLocalCoords;
Jim Van Verth33632d82017-02-28 10:24:39 -0500728 bool fUsesDistanceField;
joshualitt5bf99f12015-03-13 11:47:42 -0700729
Brian Salomond0a0a652016-12-15 15:25:22 -0500730 struct Entry {
Jim Van Verth33632d82017-02-28 10:24:39 -0500731 GrColor fColor;
732 GrShape fShape;
733 SkVector fTranslate;
bsalomonf1703092016-06-29 18:41:53 -0700734 };
735
Brian Salomond0a0a652016-12-15 15:25:22 -0500736 SkSTArray<1, Entry> fShapes;
Brian Salomon2ee084e2016-12-16 18:59:19 -0500737 GrDrawOpAtlas* fAtlas;
bsalomonee432412016-06-27 07:18:18 -0700738 ShapeCache* fShapeCache;
739 ShapeDataList* fShapeList;
brianosman0e3c5542016-04-13 13:56:21 -0700740 bool fGammaCorrect;
reed1b55a962015-09-17 20:16:13 -0700741
Brian Salomond3ccb0a2017-04-03 10:38:00 -0400742 typedef GrLegacyMeshDrawOp INHERITED;
joshualitt5bf99f12015-03-13 11:47:42 -0700743};
744
Jim Van Verth83010462017-03-16 08:45:39 -0400745bool GrSmallPathRenderer::onDrawPath(const DrawPathArgs& args) {
Brian Osman11052242016-10-27 14:47:55 -0400746 GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
Jim Van Verth83010462017-03-16 08:45:39 -0400747 "GrSmallPathRenderer::onDrawPath");
csmartdaltonecbc12b2016-06-08 10:08:43 -0700748
jvanverthfa38a302014-10-06 05:59:05 -0700749 // we've already bailed on inverse filled paths, so this is safe
bsalomon8acedde2016-06-24 10:42:16 -0700750 SkASSERT(!args.fShape->isEmpty());
bsalomonee432412016-06-27 07:18:18 -0700751 SkASSERT(args.fShape->hasUnstyledKey());
joshualitt5bf99f12015-03-13 11:47:42 -0700752 if (!fAtlas) {
Robert Phillips256c37b2017-03-01 14:32:46 -0500753 fAtlas = GrDrawOpAtlas::Make(args.fContext,
754 kAlpha_8_GrPixelConfig,
755 ATLAS_TEXTURE_WIDTH, ATLAS_TEXTURE_HEIGHT,
756 NUM_PLOTS_X, NUM_PLOTS_Y,
Jim Van Verth83010462017-03-16 08:45:39 -0400757 &GrSmallPathRenderer::HandleEviction,
Robert Phillips256c37b2017-03-01 14:32:46 -0500758 (void*)this);
joshualitt21279c72015-05-11 07:21:37 -0700759 if (!fAtlas) {
jvanverthfa38a302014-10-06 05:59:05 -0700760 return false;
761 }
762 }
763
Brian Salomond3ccb0a2017-04-03 10:38:00 -0400764 std::unique_ptr<GrLegacyMeshDrawOp> op =
765 SmallPathOp::Make(args.fPaint.getColor(), *args.fShape, *args.fViewMatrix, fAtlas.get(),
766 &fShapeCache, &fShapeList, args.fGammaCorrect);
Brian Salomon82f44312017-01-11 13:42:54 -0500767 GrPipelineBuilder pipelineBuilder(std::move(args.fPaint), args.fAAType);
bsalomonbb243832016-07-22 07:10:19 -0700768 pipelineBuilder.setUserStencil(args.fUserStencilSettings);
769
Brian Salomone14bd802017-04-04 15:13:25 -0400770 args.fRenderTargetContext->addLegacyMeshDrawOp(std::move(pipelineBuilder), *args.fClip,
771 std::move(op));
joshualitt9491f7f2015-02-11 11:33:38 -0800772
jvanverthfa38a302014-10-06 05:59:05 -0700773 return true;
774}
775
joshualitt21279c72015-05-11 07:21:37 -0700776///////////////////////////////////////////////////////////////////////////////////////////////////
777
Hal Canary6f6961e2017-01-31 13:50:44 -0500778#if GR_TEST_UTILS
joshualitt21279c72015-05-11 07:21:37 -0700779
780struct PathTestStruct {
Jim Van Verth83010462017-03-16 08:45:39 -0400781 typedef GrSmallPathRenderer::ShapeCache ShapeCache;
782 typedef GrSmallPathRenderer::ShapeData ShapeData;
783 typedef GrSmallPathRenderer::ShapeDataList ShapeDataList;
halcanary96fcdcc2015-08-27 07:41:13 -0700784 PathTestStruct() : fContextID(SK_InvalidGenID), fAtlas(nullptr) {}
joshualitt21279c72015-05-11 07:21:37 -0700785 ~PathTestStruct() { this->reset(); }
786
787 void reset() {
bsalomonee432412016-06-27 07:18:18 -0700788 ShapeDataList::Iter iter;
789 iter.init(fShapeList, ShapeDataList::Iter::kHead_IterStart);
790 ShapeData* shapeData;
791 while ((shapeData = iter.get())) {
joshualitt21279c72015-05-11 07:21:37 -0700792 iter.next();
bsalomonee432412016-06-27 07:18:18 -0700793 fShapeList.remove(shapeData);
794 delete shapeData;
joshualitt21279c72015-05-11 07:21:37 -0700795 }
Ben Wagner594f9ed2016-11-08 14:13:39 -0500796 fAtlas = nullptr;
bsalomonee432412016-06-27 07:18:18 -0700797 fShapeCache.reset();
joshualitt21279c72015-05-11 07:21:37 -0700798 }
799
Brian Salomon2ee084e2016-12-16 18:59:19 -0500800 static void HandleEviction(GrDrawOpAtlas::AtlasID id, void* pr) {
joshualitt21279c72015-05-11 07:21:37 -0700801 PathTestStruct* dfpr = (PathTestStruct*)pr;
802 // remove any paths that use this plot
bsalomonee432412016-06-27 07:18:18 -0700803 ShapeDataList::Iter iter;
804 iter.init(dfpr->fShapeList, ShapeDataList::Iter::kHead_IterStart);
805 ShapeData* shapeData;
806 while ((shapeData = iter.get())) {
joshualitt21279c72015-05-11 07:21:37 -0700807 iter.next();
bsalomonee432412016-06-27 07:18:18 -0700808 if (id == shapeData->fID) {
809 dfpr->fShapeCache.remove(shapeData->fKey);
810 dfpr->fShapeList.remove(shapeData);
811 delete shapeData;
joshualitt21279c72015-05-11 07:21:37 -0700812 }
813 }
814 }
815
816 uint32_t fContextID;
Brian Salomon2ee084e2016-12-16 18:59:19 -0500817 std::unique_ptr<GrDrawOpAtlas> fAtlas;
bsalomonee432412016-06-27 07:18:18 -0700818 ShapeCache fShapeCache;
819 ShapeDataList fShapeList;
joshualitt21279c72015-05-11 07:21:37 -0700820};
821
Brian Salomon17726632017-05-12 14:09:46 -0400822GR_LEGACY_MESH_DRAW_OP_TEST_DEFINE(SmallPathOp) {
joshualitt21279c72015-05-11 07:21:37 -0700823 static PathTestStruct gTestStruct;
824
825 if (context->uniqueID() != gTestStruct.fContextID) {
826 gTestStruct.fContextID = context->uniqueID();
827 gTestStruct.reset();
Robert Phillips256c37b2017-03-01 14:32:46 -0500828 gTestStruct.fAtlas = GrDrawOpAtlas::Make(context, kAlpha_8_GrPixelConfig,
829 ATLAS_TEXTURE_WIDTH, ATLAS_TEXTURE_HEIGHT,
830 NUM_PLOTS_X, NUM_PLOTS_Y,
831 &PathTestStruct::HandleEviction,
832 (void*)&gTestStruct);
joshualitt21279c72015-05-11 07:21:37 -0700833 }
834
835 SkMatrix viewMatrix = GrTest::TestMatrix(random);
836 GrColor color = GrRandomColor(random);
brianosman0e3c5542016-04-13 13:56:21 -0700837 bool gammaCorrect = random->nextBool();
joshualitt21279c72015-05-11 07:21:37 -0700838
bsalomonee432412016-06-27 07:18:18 -0700839 // This path renderer only allows fill styles.
840 GrShape shape(GrTest::TestPath(random), GrStyle::SimpleFill());
joshualitt21279c72015-05-11 07:21:37 -0700841
Jim Van Verth83010462017-03-16 08:45:39 -0400842 return SmallPathOp::Make(color, shape, viewMatrix, gTestStruct.fAtlas.get(),
843 &gTestStruct.fShapeCache, &gTestStruct.fShapeList, gammaCorrect);
joshualitt21279c72015-05-11 07:21:37 -0700844}
845
846#endif