blob: f7d7e7bb099c4fce1e61c17f91083e573ff94d46 [file] [log] [blame]
robertphillipsea461502015-05-26 11:38:03 -07001
2/*
3 * Copyright 2015 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9#include "GrAARectRenderer.h"
robertphillips2334fb62015-06-17 05:43:33 -070010#include "GrAtlasTextContext.h"
robertphillipsea461502015-05-26 11:38:03 -070011#include "GrBatch.h"
12#include "GrBatchTest.h"
13#include "GrDefaultGeoProcFactory.h"
14#include "GrDrawContext.h"
15#include "GrOvalRenderer.h"
16#include "GrPathRenderer.h"
robertphillips2334fb62015-06-17 05:43:33 -070017#include "GrRenderTarget.h"
18#include "GrRenderTargetPriv.h"
19#include "GrStencilAndCoverTextContext.h"
robertphillipsea461502015-05-26 11:38:03 -070020
21#define ASSERT_OWNED_RESOURCE(R) SkASSERT(!(R) || (R)->getContext() == fContext)
22#define RETURN_IF_ABANDONED if (!fDrawTarget) { return; }
23#define RETURN_FALSE_IF_ABANDONED if (!fDrawTarget) { return false; }
24#define RETURN_NULL_IF_ABANDONED if (!fDrawTarget) { return NULL; }
25
26class AutoCheckFlush {
27public:
28 AutoCheckFlush(GrContext* context) : fContext(context) { SkASSERT(context); }
29 ~AutoCheckFlush() { fContext->flushIfNecessary(); }
30
31private:
32 GrContext* fContext;
33};
34
robertphillips2334fb62015-06-17 05:43:33 -070035GrDrawContext::GrDrawContext(GrContext* context,
36 GrDrawTarget* drawTarget,
robertphillipsfcf78292015-06-19 11:49:52 -070037 const SkSurfaceProps& surfaceProps)
robertphillipsea461502015-05-26 11:38:03 -070038 : fContext(context)
robertphillips2334fb62015-06-17 05:43:33 -070039 , fDrawTarget(SkRef(drawTarget))
40 , fTextContext(NULL)
robertphillipsfcf78292015-06-19 11:49:52 -070041 , fSurfaceProps(surfaceProps) {
robertphillipsea461502015-05-26 11:38:03 -070042}
43
robertphillips4b195e52015-05-26 14:37:00 -070044GrDrawContext::~GrDrawContext() {
45 SkSafeUnref(fDrawTarget);
robertphillips2334fb62015-06-17 05:43:33 -070046 SkDELETE(fTextContext);
robertphillips4b195e52015-05-26 14:37:00 -070047}
48
robertphillipsea461502015-05-26 11:38:03 -070049void GrDrawContext::copySurface(GrRenderTarget* dst, GrSurface* src,
50 const SkIRect& srcRect, const SkIPoint& dstPoint) {
51 if (!this->prepareToDraw(dst)) {
52 return;
53 }
54
55 fDrawTarget->copySurface(dst, src, srcRect, dstPoint);
56}
57
robertphillips2334fb62015-06-17 05:43:33 -070058GrTextContext* GrDrawContext::createTextContext(GrRenderTarget* renderTarget,
robertphillipsfcf78292015-06-19 11:49:52 -070059 const SkSurfaceProps& surfaceProps) {
robertphillips2334fb62015-06-17 05:43:33 -070060 if (fContext->caps()->shaderCaps()->pathRenderingSupport() &&
cdaltone04edd82015-06-29 14:15:19 -070061 renderTarget->isStencilBufferMultisampled() &&
62 fSurfaceProps.isUseDistanceFieldFonts()) { // FIXME: Rename the dff flag to be more general.
robertphillips2334fb62015-06-17 05:43:33 -070063 GrStencilAttachment* sb = renderTarget->renderTargetPriv().attachStencilAttachment();
64 if (sb) {
robertphillipsfcf78292015-06-19 11:49:52 -070065 return GrStencilAndCoverTextContext::Create(fContext, this, surfaceProps);
robertphillips2334fb62015-06-17 05:43:33 -070066 }
67 }
68
robertphillipsfcf78292015-06-19 11:49:52 -070069 return GrAtlasTextContext::Create(fContext, this, surfaceProps);
robertphillips2334fb62015-06-17 05:43:33 -070070}
71
72void GrDrawContext::drawText(GrRenderTarget* rt, const GrClip& clip, const GrPaint& grPaint,
73 const SkPaint& skPaint,
74 const SkMatrix& viewMatrix,
75 const char text[], size_t byteLength,
76 SkScalar x, SkScalar y, const SkIRect& clipBounds) {
77 if (!fTextContext) {
robertphillipsfcf78292015-06-19 11:49:52 -070078 fTextContext = this->createTextContext(rt, fSurfaceProps);
robertphillips2334fb62015-06-17 05:43:33 -070079 }
80
81 fTextContext->drawText(rt, clip, grPaint, skPaint, viewMatrix,
82 text, byteLength, x, y, clipBounds);
83
84}
85void GrDrawContext::drawPosText(GrRenderTarget* rt, const GrClip& clip, const GrPaint& grPaint,
86 const SkPaint& skPaint,
87 const SkMatrix& viewMatrix,
88 const char text[], size_t byteLength,
89 const SkScalar pos[], int scalarsPerPosition,
90 const SkPoint& offset, const SkIRect& clipBounds) {
91 if (!fTextContext) {
robertphillipsfcf78292015-06-19 11:49:52 -070092 fTextContext = this->createTextContext(rt, fSurfaceProps);
robertphillips2334fb62015-06-17 05:43:33 -070093 }
94
95 fTextContext->drawPosText(rt, clip, grPaint, skPaint, viewMatrix, text, byteLength,
96 pos, scalarsPerPosition, offset, clipBounds);
97
98}
99void GrDrawContext::drawTextBlob(GrRenderTarget* rt, const GrClip& clip, const SkPaint& skPaint,
100 const SkMatrix& viewMatrix, const SkTextBlob* blob,
101 SkScalar x, SkScalar y,
102 SkDrawFilter* filter, const SkIRect& clipBounds) {
103 if (!fTextContext) {
robertphillipsfcf78292015-06-19 11:49:52 -0700104 fTextContext = this->createTextContext(rt, fSurfaceProps);
robertphillips2334fb62015-06-17 05:43:33 -0700105 }
106
107 fTextContext->drawTextBlob(rt, clip, skPaint, viewMatrix, blob, x, y, filter, clipBounds);
robertphillipsea461502015-05-26 11:38:03 -0700108}
109
110void GrDrawContext::drawPaths(GrPipelineBuilder* pipelineBuilder,
111 const GrPathProcessor* pathProc,
112 const GrPathRange* pathRange,
113 const void* indices,
114 int /*GrDrawTarget::PathIndexType*/ indexType,
115 const float transformValues[],
116 int /*GrDrawTarget::PathTransformType*/ transformType,
117 int count,
118 int /*GrPathRendering::FillType*/ fill) {
119 fDrawTarget->drawPaths(pipelineBuilder, pathProc, pathRange,
120 indices, (GrDrawTarget::PathIndexType) indexType,
121 transformValues,
122 (GrDrawTarget::PathTransformType) transformType,
123 count, (GrPathRendering::FillType) fill);
124}
125
126void GrDrawContext::discard(GrRenderTarget* renderTarget) {
127 RETURN_IF_ABANDONED
128 SkASSERT(renderTarget);
129 AutoCheckFlush acf(fContext);
130 if (!this->prepareToDraw(renderTarget)) {
131 return;
132 }
133 fDrawTarget->discard(renderTarget);
134}
135
136void GrDrawContext::clear(GrRenderTarget* renderTarget,
137 const SkIRect* rect,
138 const GrColor color,
139 bool canIgnoreRect) {
140 RETURN_IF_ABANDONED
141 SkASSERT(renderTarget);
142
143 AutoCheckFlush acf(fContext);
144 if (!this->prepareToDraw(renderTarget)) {
145 return;
146 }
147 fDrawTarget->clear(rect, color, canIgnoreRect, renderTarget);
148}
149
150
151void GrDrawContext::drawPaint(GrRenderTarget* rt,
152 const GrClip& clip,
153 const GrPaint& origPaint,
154 const SkMatrix& viewMatrix) {
155 RETURN_IF_ABANDONED
156 // set rect to be big enough to fill the space, but not super-huge, so we
157 // don't overflow fixed-point implementations
158 SkRect r;
159 r.setLTRB(0, 0,
160 SkIntToScalar(rt->width()),
161 SkIntToScalar(rt->height()));
162 SkTCopyOnFirstWrite<GrPaint> paint(origPaint);
163
164 // by definition this fills the entire clip, no need for AA
165 if (paint->isAntiAlias()) {
166 paint.writable()->setAntiAlias(false);
167 }
168
169 bool isPerspective = viewMatrix.hasPerspective();
170
171 // We attempt to map r by the inverse matrix and draw that. mapRect will
172 // map the four corners and bound them with a new rect. This will not
173 // produce a correct result for some perspective matrices.
174 if (!isPerspective) {
175 SkMatrix inverse;
176 if (!viewMatrix.invert(&inverse)) {
177 SkDebugf("Could not invert matrix\n");
178 return;
179 }
180 inverse.mapRect(&r);
181 this->drawRect(rt, clip, *paint, viewMatrix, r);
182 } else {
183 SkMatrix localMatrix;
184 if (!viewMatrix.invert(&localMatrix)) {
185 SkDebugf("Could not invert matrix\n");
186 return;
187 }
188
189 AutoCheckFlush acf(fContext);
190 GrPipelineBuilder pipelineBuilder;
191 if (!this->prepareToDraw(&pipelineBuilder, rt, clip, paint)) {
192 return;
193 }
194
195 fDrawTarget->drawBWRect(&pipelineBuilder,
196 paint->getColor(),
197 SkMatrix::I(),
198 r,
199 NULL,
200 &localMatrix);
201 }
202}
203
204static inline bool is_irect(const SkRect& r) {
205 return SkScalarIsInt(r.fLeft) && SkScalarIsInt(r.fTop) &&
206 SkScalarIsInt(r.fRight) && SkScalarIsInt(r.fBottom);
207}
208
209static bool apply_aa_to_rect(GrDrawTarget* target,
210 GrPipelineBuilder* pipelineBuilder,
211 SkRect* devBoundRect,
212 const SkRect& rect,
213 SkScalar strokeWidth,
214 const SkMatrix& combinedMatrix,
215 GrColor color) {
vbuzinovdded6962015-06-12 08:59:45 -0700216 if (pipelineBuilder->getRenderTarget()->isUnifiedMultisampled()) {
robertphillipsea461502015-05-26 11:38:03 -0700217 return false;
218 }
219
220#if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT)
221 if (strokeWidth >= 0) {
222#endif
223 if (!combinedMatrix.preservesAxisAlignment()) {
224 return false;
225 }
226
227#if defined(SHADER_AA_FILL_RECT) || !defined(IGNORE_ROT_AA_RECT_OPT)
228 } else {
229 if (!combinedMatrix.preservesRightAngles()) {
230 return false;
231 }
232 }
233#endif
234
235 combinedMatrix.mapRect(devBoundRect, rect);
236 if (!combinedMatrix.rectStaysRect()) {
237 return true;
238 }
239
240 if (strokeWidth < 0) {
241 return !is_irect(*devBoundRect);
242 }
243
244 return true;
245}
246
247static inline bool rect_contains_inclusive(const SkRect& rect, const SkPoint& point) {
248 return point.fX >= rect.fLeft && point.fX <= rect.fRight &&
249 point.fY >= rect.fTop && point.fY <= rect.fBottom;
250}
251
252class StrokeRectBatch : public GrBatch {
253public:
254 struct Geometry {
255 GrColor fColor;
256 SkMatrix fViewMatrix;
257 SkRect fRect;
258 SkScalar fStrokeWidth;
259 };
260
261 static GrBatch* Create(const Geometry& geometry, bool snapToPixelCenters) {
262 return SkNEW_ARGS(StrokeRectBatch, (geometry, snapToPixelCenters));
263 }
264
265 const char* name() const override { return "StrokeRectBatch"; }
266
267 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
268 // When this is called on a batch, there is only one geometry bundle
269 out->setKnownFourComponents(fGeoData[0].fColor);
270 }
271
272 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
273 out->setKnownSingleComponent(0xff);
274 }
275
276 void initBatchTracker(const GrPipelineInfo& init) override {
277 // Handle any color overrides
bsalomond07a2792015-07-08 10:20:21 -0700278 if (init.fColorIgnored) {
robertphillipsea461502015-05-26 11:38:03 -0700279 fGeoData[0].fColor = GrColor_ILLEGAL;
bsalomond07a2792015-07-08 10:20:21 -0700280 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
281 fGeoData[0].fColor = init.fOverrideColor;
robertphillipsea461502015-05-26 11:38:03 -0700282 }
283
284 // setup batch properties
bsalomond07a2792015-07-08 10:20:21 -0700285 fBatch.fColorIgnored = init.fColorIgnored;
robertphillipsea461502015-05-26 11:38:03 -0700286 fBatch.fColor = fGeoData[0].fColor;
bsalomond07a2792015-07-08 10:20:21 -0700287 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
288 fBatch.fCoverageIgnored = init.fCoverageIgnored;
robertphillipsea461502015-05-26 11:38:03 -0700289 }
290
291 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
292 SkAutoTUnref<const GrGeometryProcessor> gp(
293 GrDefaultGeoProcFactory::Create(GrDefaultGeoProcFactory::kPosition_GPType,
294 this->color(),
295 this->usesLocalCoords(),
296 this->coverageIgnored(),
297 this->viewMatrix(),
298 SkMatrix::I()));
299
300 batchTarget->initDraw(gp, pipeline);
301
302 size_t vertexStride = gp->getVertexStride();
303
304 SkASSERT(vertexStride == sizeof(GrDefaultGeoProcFactory::PositionAttr));
305
306 Geometry& args = fGeoData[0];
307
308 int vertexCount = kVertsPerHairlineRect;
309 if (args.fStrokeWidth > 0) {
310 vertexCount = kVertsPerStrokeRect;
311 }
312
313 const GrVertexBuffer* vertexBuffer;
314 int firstVertex;
315
316 void* verts = batchTarget->makeVertSpace(vertexStride, vertexCount,
317 &vertexBuffer, &firstVertex);
318
319 if (!verts) {
320 SkDebugf("Could not allocate vertices\n");
321 return;
322 }
323
324 SkPoint* vertex = reinterpret_cast<SkPoint*>(verts);
325
326 GrPrimitiveType primType;
327
328 if (args.fStrokeWidth > 0) {;
329 primType = kTriangleStrip_GrPrimitiveType;
330 args.fRect.sort();
331 this->setStrokeRectStrip(vertex, args.fRect, args.fStrokeWidth);
332 } else {
333 // hairline
334 primType = kLineStrip_GrPrimitiveType;
335 vertex[0].set(args.fRect.fLeft, args.fRect.fTop);
336 vertex[1].set(args.fRect.fRight, args.fRect.fTop);
337 vertex[2].set(args.fRect.fRight, args.fRect.fBottom);
338 vertex[3].set(args.fRect.fLeft, args.fRect.fBottom);
339 vertex[4].set(args.fRect.fLeft, args.fRect.fTop);
340 }
341
342 GrVertices vertices;
343 vertices.init(primType, vertexBuffer, firstVertex, vertexCount);
344 batchTarget->draw(vertices);
345 }
346
347 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
348
349private:
350 StrokeRectBatch(const Geometry& geometry, bool snapToPixelCenters) {
351 this->initClassID<StrokeRectBatch>();
352
353 fBatch.fHairline = geometry.fStrokeWidth == 0;
354
355 fGeoData.push_back(geometry);
356
357 // setup bounds
358 fBounds = geometry.fRect;
359 SkScalar rad = SkScalarHalf(geometry.fStrokeWidth);
360 fBounds.outset(rad, rad);
361 geometry.fViewMatrix.mapRect(&fBounds);
362
363 // If our caller snaps to pixel centers then we have to round out the bounds
364 if (snapToPixelCenters) {
365 fBounds.roundOut();
366 }
367 }
368
369 /* create a triangle strip that strokes the specified rect. There are 8
370 unique vertices, but we repeat the last 2 to close up. Alternatively we
371 could use an indices array, and then only send 8 verts, but not sure that
372 would be faster.
373 */
374 void setStrokeRectStrip(SkPoint verts[10], const SkRect& rect, SkScalar width) {
375 const SkScalar rad = SkScalarHalf(width);
376 // TODO we should be able to enable this assert, but we'd have to filter these draws
377 // this is a bug
378 //SkASSERT(rad < rect.width() / 2 && rad < rect.height() / 2);
379
380 verts[0].set(rect.fLeft + rad, rect.fTop + rad);
381 verts[1].set(rect.fLeft - rad, rect.fTop - rad);
382 verts[2].set(rect.fRight - rad, rect.fTop + rad);
383 verts[3].set(rect.fRight + rad, rect.fTop - rad);
384 verts[4].set(rect.fRight - rad, rect.fBottom - rad);
385 verts[5].set(rect.fRight + rad, rect.fBottom + rad);
386 verts[6].set(rect.fLeft + rad, rect.fBottom - rad);
387 verts[7].set(rect.fLeft - rad, rect.fBottom + rad);
388 verts[8] = verts[0];
389 verts[9] = verts[1];
390 }
391
392
393 GrColor color() const { return fBatch.fColor; }
394 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
395 bool colorIgnored() const { return fBatch.fColorIgnored; }
396 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
397 bool hairline() const { return fBatch.fHairline; }
398 bool coverageIgnored() const { return fBatch.fCoverageIgnored; }
399
400 bool onCombineIfPossible(GrBatch* t) override {
401 // StrokeRectBatch* that = t->cast<StrokeRectBatch>();
402
403 // NonAA stroke rects cannot batch right now
404 // TODO make these batchable
405 return false;
406 }
407
408 struct BatchTracker {
409 GrColor fColor;
410 bool fUsesLocalCoords;
411 bool fColorIgnored;
412 bool fCoverageIgnored;
413 bool fHairline;
414 };
415
416 const static int kVertsPerHairlineRect = 5;
417 const static int kVertsPerStrokeRect = 10;
418
419 BatchTracker fBatch;
420 SkSTArray<1, Geometry, true> fGeoData;
421};
422
423void GrDrawContext::drawRect(GrRenderTarget* rt,
424 const GrClip& clip,
425 const GrPaint& paint,
426 const SkMatrix& viewMatrix,
427 const SkRect& rect,
428 const GrStrokeInfo* strokeInfo) {
429 RETURN_IF_ABANDONED
430 if (strokeInfo && strokeInfo->isDashed()) {
431 SkPath path;
432 path.setIsVolatile(true);
433 path.addRect(rect);
434 this->drawPath(rt, clip, paint, viewMatrix, path, *strokeInfo);
435 return;
436 }
437
438 AutoCheckFlush acf(fContext);
439 GrPipelineBuilder pipelineBuilder;
440 if (!this->prepareToDraw(&pipelineBuilder, rt, clip, &paint)) {
441 return;
442 }
443
444 SkScalar width = NULL == strokeInfo ? -1 : strokeInfo->getWidth();
445
446 // Check if this is a full RT draw and can be replaced with a clear. We don't bother checking
447 // cases where the RT is fully inside a stroke.
448 if (width < 0) {
449 SkRect rtRect;
450 pipelineBuilder.getRenderTarget()->getBoundsRect(&rtRect);
451 SkRect clipSpaceRTRect = rtRect;
452 bool checkClip = GrClip::kWideOpen_ClipType != clip.clipType();
453 if (checkClip) {
454 clipSpaceRTRect.offset(SkIntToScalar(clip.origin().fX),
455 SkIntToScalar(clip.origin().fY));
456 }
457 // Does the clip contain the entire RT?
458 if (!checkClip || clip.quickContains(clipSpaceRTRect)) {
459 SkMatrix invM;
460 if (!viewMatrix.invert(&invM)) {
461 return;
462 }
463 // Does the rect bound the RT?
464 SkPoint srcSpaceRTQuad[4];
465 invM.mapRectToQuad(srcSpaceRTQuad, rtRect);
466 if (rect_contains_inclusive(rect, srcSpaceRTQuad[0]) &&
467 rect_contains_inclusive(rect, srcSpaceRTQuad[1]) &&
468 rect_contains_inclusive(rect, srcSpaceRTQuad[2]) &&
469 rect_contains_inclusive(rect, srcSpaceRTQuad[3])) {
470 // Will it blend?
471 GrColor clearColor;
cdalton1fa45722015-06-02 10:43:39 -0700472 if (paint.isConstantBlendedColor(&clearColor)) {
robertphillipsea461502015-05-26 11:38:03 -0700473 fDrawTarget->clear(NULL, clearColor, true, rt);
474 return;
475 }
476 }
477 }
478 }
479
480 GrColor color = paint.getColor();
481 SkRect devBoundRect;
vbuzinovdded6962015-06-12 08:59:45 -0700482 bool needAA = paint.isAntiAlias() &&
483 !pipelineBuilder.getRenderTarget()->isUnifiedMultisampled();
robertphillipsea461502015-05-26 11:38:03 -0700484 bool doAA = needAA && apply_aa_to_rect(fDrawTarget, &pipelineBuilder, &devBoundRect, rect,
485 width, viewMatrix, color);
486
487 if (doAA) {
488 if (width >= 0) {
489 GrAARectRenderer::StrokeAARect(fDrawTarget,
490 &pipelineBuilder,
491 color,
492 viewMatrix,
493 rect,
494 devBoundRect,
495 *strokeInfo);
496 } else {
497 // filled AA rect
498 GrAARectRenderer::FillAARect(fDrawTarget,
499 &pipelineBuilder,
500 color,
501 viewMatrix,
502 rect,
503 devBoundRect);
504 }
505 return;
506 }
507
508 if (width >= 0) {
509 StrokeRectBatch::Geometry geometry;
510 geometry.fViewMatrix = viewMatrix;
511 geometry.fColor = color;
512 geometry.fRect = rect;
513 geometry.fStrokeWidth = width;
514
515 // Non-AA hairlines are snapped to pixel centers to make which pixels are hit deterministic
vbuzinovdded6962015-06-12 08:59:45 -0700516 bool snapToPixelCenters = (0 == width && !rt->isUnifiedMultisampled());
robertphillipsea461502015-05-26 11:38:03 -0700517 SkAutoTUnref<GrBatch> batch(StrokeRectBatch::Create(geometry, snapToPixelCenters));
518
519 // Depending on sub-pixel coordinates and the particular GPU, we may lose a corner of
520 // hairline rects. We jam all the vertices to pixel centers to avoid this, but not when MSAA
521 // is enabled because it can cause ugly artifacts.
522 pipelineBuilder.setState(GrPipelineBuilder::kSnapVerticesToPixelCenters_Flag,
523 snapToPixelCenters);
524 fDrawTarget->drawBatch(&pipelineBuilder, batch);
525 } else {
526 // filled BW rect
527 fDrawTarget->drawSimpleRect(&pipelineBuilder, color, viewMatrix, rect);
528 }
529}
530
531void GrDrawContext::drawNonAARectToRect(GrRenderTarget* rt,
532 const GrClip& clip,
533 const GrPaint& paint,
534 const SkMatrix& viewMatrix,
535 const SkRect& rectToDraw,
536 const SkRect& localRect,
537 const SkMatrix* localMatrix) {
538 RETURN_IF_ABANDONED
539 AutoCheckFlush acf(fContext);
540 GrPipelineBuilder pipelineBuilder;
541 if (!this->prepareToDraw(&pipelineBuilder, rt, clip, &paint)) {
542 return;
543 }
544
545 fDrawTarget->drawBWRect(&pipelineBuilder,
546 paint.getColor(),
547 viewMatrix,
548 rectToDraw,
549 &localRect,
550 localMatrix);
551}
552
553static const GrGeometryProcessor* set_vertex_attributes(bool hasLocalCoords,
554 bool hasColors,
555 int* colorOffset,
556 int* texOffset,
557 GrColor color,
558 const SkMatrix& viewMatrix,
559 bool coverageIgnored) {
560 *texOffset = -1;
561 *colorOffset = -1;
562 uint32_t flags = GrDefaultGeoProcFactory::kPosition_GPType;
563 if (hasLocalCoords && hasColors) {
564 *colorOffset = sizeof(SkPoint);
565 *texOffset = sizeof(SkPoint) + sizeof(GrColor);
566 flags |= GrDefaultGeoProcFactory::kColor_GPType |
567 GrDefaultGeoProcFactory::kLocalCoord_GPType;
568 } else if (hasLocalCoords) {
569 *texOffset = sizeof(SkPoint);
570 flags |= GrDefaultGeoProcFactory::kLocalCoord_GPType;
571 } else if (hasColors) {
572 *colorOffset = sizeof(SkPoint);
573 flags |= GrDefaultGeoProcFactory::kColor_GPType;
574 }
575 return GrDefaultGeoProcFactory::Create(flags, color, hasLocalCoords, coverageIgnored,
576 viewMatrix, SkMatrix::I());
577}
578
579class DrawVerticesBatch : public GrBatch {
580public:
581 struct Geometry {
582 GrColor fColor;
583 SkTDArray<SkPoint> fPositions;
584 SkTDArray<uint16_t> fIndices;
585 SkTDArray<GrColor> fColors;
586 SkTDArray<SkPoint> fLocalCoords;
587 };
588
589 static GrBatch* Create(const Geometry& geometry, GrPrimitiveType primitiveType,
590 const SkMatrix& viewMatrix,
591 const SkPoint* positions, int vertexCount,
592 const uint16_t* indices, int indexCount,
593 const GrColor* colors, const SkPoint* localCoords,
594 const SkRect& bounds) {
595 return SkNEW_ARGS(DrawVerticesBatch, (geometry, primitiveType, viewMatrix, positions,
596 vertexCount, indices, indexCount, colors,
597 localCoords, bounds));
598 }
599
600 const char* name() const override { return "DrawVerticesBatch"; }
601
602 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
603 // When this is called on a batch, there is only one geometry bundle
604 if (this->hasColors()) {
605 out->setUnknownFourComponents();
606 } else {
607 out->setKnownFourComponents(fGeoData[0].fColor);
608 }
609 }
610
611 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
612 out->setKnownSingleComponent(0xff);
613 }
614
615 void initBatchTracker(const GrPipelineInfo& init) override {
616 // Handle any color overrides
bsalomond07a2792015-07-08 10:20:21 -0700617 if (init.fColorIgnored) {
robertphillipsea461502015-05-26 11:38:03 -0700618 fGeoData[0].fColor = GrColor_ILLEGAL;
bsalomond07a2792015-07-08 10:20:21 -0700619 } else if (GrColor_ILLEGAL != init.fOverrideColor) {
620 fGeoData[0].fColor = init.fOverrideColor;
robertphillipsea461502015-05-26 11:38:03 -0700621 }
622
623 // setup batch properties
bsalomond07a2792015-07-08 10:20:21 -0700624 fBatch.fColorIgnored = init.fColorIgnored;
robertphillipsea461502015-05-26 11:38:03 -0700625 fBatch.fColor = fGeoData[0].fColor;
bsalomond07a2792015-07-08 10:20:21 -0700626 fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
627 fBatch.fCoverageIgnored = init.fCoverageIgnored;
robertphillipsea461502015-05-26 11:38:03 -0700628 }
629
630 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
631 int colorOffset = -1, texOffset = -1;
632 SkAutoTUnref<const GrGeometryProcessor> gp(
633 set_vertex_attributes(this->hasLocalCoords(), this->hasColors(), &colorOffset,
634 &texOffset, this->color(), this->viewMatrix(),
635 this->coverageIgnored()));
636
637 batchTarget->initDraw(gp, pipeline);
638
639 size_t vertexStride = gp->getVertexStride();
640
641 SkASSERT(vertexStride == sizeof(SkPoint) + (this->hasLocalCoords() ? sizeof(SkPoint) : 0)
642 + (this->hasColors() ? sizeof(GrColor) : 0));
643
644 int instanceCount = fGeoData.count();
645
646 const GrVertexBuffer* vertexBuffer;
647 int firstVertex;
648
649 void* verts = batchTarget->makeVertSpace(vertexStride, this->vertexCount(),
650 &vertexBuffer, &firstVertex);
651
652 if (!verts) {
653 SkDebugf("Could not allocate vertices\n");
654 return;
655 }
656
657 const GrIndexBuffer* indexBuffer = NULL;
658 int firstIndex = 0;
659
660 uint16_t* indices = NULL;
661 if (this->hasIndices()) {
662 indices = batchTarget->makeIndexSpace(this->indexCount(), &indexBuffer, &firstIndex);
663
664 if (!indices) {
665 SkDebugf("Could not allocate indices\n");
666 return;
667 }
668 }
669
670 int indexOffset = 0;
671 int vertexOffset = 0;
672 for (int i = 0; i < instanceCount; i++) {
673 const Geometry& args = fGeoData[i];
674
675 // TODO we can actually cache this interleaved and then just memcopy
676 if (this->hasIndices()) {
677 for (int j = 0; j < args.fIndices.count(); ++j, ++indexOffset) {
678 *(indices + indexOffset) = args.fIndices[j] + vertexOffset;
679 }
680 }
681
682 for (int j = 0; j < args.fPositions.count(); ++j) {
683 *((SkPoint*)verts) = args.fPositions[j];
684 if (this->hasColors()) {
685 *(GrColor*)((intptr_t)verts + colorOffset) = args.fColors[j];
686 }
687 if (this->hasLocalCoords()) {
688 *(SkPoint*)((intptr_t)verts + texOffset) = args.fLocalCoords[j];
689 }
690 verts = (void*)((intptr_t)verts + vertexStride);
691 vertexOffset++;
692 }
693 }
694
695 GrVertices vertices;
696 if (this->hasIndices()) {
697 vertices.initIndexed(this->primitiveType(), vertexBuffer, indexBuffer, firstVertex,
698 firstIndex, this->vertexCount(), this->indexCount());
699
700 } else {
701 vertices.init(this->primitiveType(), vertexBuffer, firstVertex, this->vertexCount());
702 }
703 batchTarget->draw(vertices);
704 }
705
706 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
707
708private:
709 DrawVerticesBatch(const Geometry& geometry, GrPrimitiveType primitiveType,
710 const SkMatrix& viewMatrix,
711 const SkPoint* positions, int vertexCount,
712 const uint16_t* indices, int indexCount,
713 const GrColor* colors, const SkPoint* localCoords, const SkRect& bounds) {
714 this->initClassID<DrawVerticesBatch>();
715 SkASSERT(positions);
716
717 fBatch.fViewMatrix = viewMatrix;
718 Geometry& installedGeo = fGeoData.push_back(geometry);
719
720 installedGeo.fPositions.append(vertexCount, positions);
721 if (indices) {
722 installedGeo.fIndices.append(indexCount, indices);
723 fBatch.fHasIndices = true;
724 } else {
725 fBatch.fHasIndices = false;
726 }
727
728 if (colors) {
729 installedGeo.fColors.append(vertexCount, colors);
730 fBatch.fHasColors = true;
731 } else {
732 fBatch.fHasColors = false;
733 }
734
735 if (localCoords) {
736 installedGeo.fLocalCoords.append(vertexCount, localCoords);
737 fBatch.fHasLocalCoords = true;
738 } else {
739 fBatch.fHasLocalCoords = false;
740 }
741 fBatch.fVertexCount = vertexCount;
742 fBatch.fIndexCount = indexCount;
743 fBatch.fPrimitiveType = primitiveType;
744
745 this->setBounds(bounds);
746 }
747
748 GrPrimitiveType primitiveType() const { return fBatch.fPrimitiveType; }
749 bool batchablePrimitiveType() const {
750 return kTriangles_GrPrimitiveType == fBatch.fPrimitiveType ||
751 kLines_GrPrimitiveType == fBatch.fPrimitiveType ||
752 kPoints_GrPrimitiveType == fBatch.fPrimitiveType;
753 }
754 GrColor color() const { return fBatch.fColor; }
755 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
756 bool colorIgnored() const { return fBatch.fColorIgnored; }
757 const SkMatrix& viewMatrix() const { return fBatch.fViewMatrix; }
758 bool hasColors() const { return fBatch.fHasColors; }
759 bool hasIndices() const { return fBatch.fHasIndices; }
760 bool hasLocalCoords() const { return fBatch.fHasLocalCoords; }
761 int vertexCount() const { return fBatch.fVertexCount; }
762 int indexCount() const { return fBatch.fIndexCount; }
763 bool coverageIgnored() const { return fBatch.fCoverageIgnored; }
764
765 bool onCombineIfPossible(GrBatch* t) override {
766 DrawVerticesBatch* that = t->cast<DrawVerticesBatch>();
767
768 if (!this->batchablePrimitiveType() || this->primitiveType() != that->primitiveType()) {
769 return false;
770 }
771
772 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
773
774 // We currently use a uniform viewmatrix for this batch
775 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
776 return false;
777 }
778
779 if (this->hasColors() != that->hasColors()) {
780 return false;
781 }
782
783 if (this->hasIndices() != that->hasIndices()) {
784 return false;
785 }
786
787 if (this->hasLocalCoords() != that->hasLocalCoords()) {
788 return false;
789 }
790
791 if (!this->hasColors() && this->color() != that->color()) {
792 return false;
793 }
794
795 if (this->color() != that->color()) {
796 fBatch.fColor = GrColor_ILLEGAL;
797 }
798 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
799 fBatch.fVertexCount += that->vertexCount();
800 fBatch.fIndexCount += that->indexCount();
801
802 this->joinBounds(that->bounds());
803 return true;
804 }
805
806 struct BatchTracker {
807 GrPrimitiveType fPrimitiveType;
808 SkMatrix fViewMatrix;
809 GrColor fColor;
810 bool fUsesLocalCoords;
811 bool fColorIgnored;
812 bool fCoverageIgnored;
813 bool fHasColors;
814 bool fHasIndices;
815 bool fHasLocalCoords;
816 int fVertexCount;
817 int fIndexCount;
818 };
819
820 BatchTracker fBatch;
821 SkSTArray<1, Geometry, true> fGeoData;
822};
823
824void GrDrawContext::drawVertices(GrRenderTarget* rt,
825 const GrClip& clip,
826 const GrPaint& paint,
827 const SkMatrix& viewMatrix,
828 GrPrimitiveType primitiveType,
829 int vertexCount,
830 const SkPoint positions[],
831 const SkPoint texCoords[],
832 const GrColor colors[],
833 const uint16_t indices[],
834 int indexCount) {
835 RETURN_IF_ABANDONED
836 AutoCheckFlush acf(fContext);
837 GrPipelineBuilder pipelineBuilder;
838
839 if (!this->prepareToDraw(&pipelineBuilder, rt, clip, &paint)) {
840 return;
841 }
842
843 // TODO clients should give us bounds
844 SkRect bounds;
845 if (!bounds.setBoundsCheck(positions, vertexCount)) {
846 SkDebugf("drawVertices call empty bounds\n");
847 return;
848 }
849
850 viewMatrix.mapRect(&bounds);
851
852 // If we don't have AA then we outset for a half pixel in each direction to account for
853 // snapping
854 if (!paint.isAntiAlias()) {
855 bounds.outset(0.5f, 0.5f);
856 }
857
858 DrawVerticesBatch::Geometry geometry;
859 geometry.fColor = paint.getColor();
860 SkAutoTUnref<GrBatch> batch(DrawVerticesBatch::Create(geometry, primitiveType, viewMatrix,
861 positions, vertexCount, indices,
862 indexCount, colors, texCoords,
863 bounds));
864
865 fDrawTarget->drawBatch(&pipelineBuilder, batch);
866}
867
868///////////////////////////////////////////////////////////////////////////////
869
870void GrDrawContext::drawRRect(GrRenderTarget*rt,
871 const GrClip& clip,
872 const GrPaint& paint,
873 const SkMatrix& viewMatrix,
874 const SkRRect& rrect,
875 const GrStrokeInfo& strokeInfo) {
876 RETURN_IF_ABANDONED
877 if (rrect.isEmpty()) {
878 return;
879 }
880
881 if (strokeInfo.isDashed()) {
882 SkPath path;
883 path.setIsVolatile(true);
884 path.addRRect(rrect);
885 this->drawPath(rt, clip, paint, viewMatrix, path, strokeInfo);
886 return;
887 }
888
889 AutoCheckFlush acf(fContext);
890 GrPipelineBuilder pipelineBuilder;
891 if (!this->prepareToDraw(&pipelineBuilder, rt, clip, &paint)) {
892 return;
893 }
894
895 GrColor color = paint.getColor();
896 if (!GrOvalRenderer::DrawRRect(fDrawTarget,
897 &pipelineBuilder,
898 color,
899 viewMatrix,
900 paint.isAntiAlias(),
901 rrect,
902 strokeInfo)) {
903 SkPath path;
904 path.setIsVolatile(true);
905 path.addRRect(rrect);
906 this->internalDrawPath(fDrawTarget, &pipelineBuilder, viewMatrix, color,
907 paint.isAntiAlias(), path, strokeInfo);
908 }
909}
910
911///////////////////////////////////////////////////////////////////////////////
912
913void GrDrawContext::drawDRRect(GrRenderTarget* rt,
914 const GrClip& clip,
915 const GrPaint& paint,
916 const SkMatrix& viewMatrix,
917 const SkRRect& outer,
918 const SkRRect& inner) {
919 RETURN_IF_ABANDONED
920 if (outer.isEmpty()) {
921 return;
922 }
923
924 AutoCheckFlush acf(fContext);
925 GrPipelineBuilder pipelineBuilder;
926 if (!this->prepareToDraw(&pipelineBuilder, rt, clip, &paint)) {
927 return;
928 }
929
930 GrColor color = paint.getColor();
931 if (!GrOvalRenderer::DrawDRRect(fDrawTarget,
932 &pipelineBuilder,
933 color,
934 viewMatrix,
935 paint.isAntiAlias(),
936 outer,
937 inner)) {
938 SkPath path;
939 path.setIsVolatile(true);
940 path.addRRect(inner);
941 path.addRRect(outer);
942 path.setFillType(SkPath::kEvenOdd_FillType);
943
944 GrStrokeInfo fillRec(SkStrokeRec::kFill_InitStyle);
945 this->internalDrawPath(fDrawTarget, &pipelineBuilder, viewMatrix, color,
946 paint.isAntiAlias(), path, fillRec);
947 }
948}
949
950///////////////////////////////////////////////////////////////////////////////
951
952void GrDrawContext::drawOval(GrRenderTarget* rt,
953 const GrClip& clip,
954 const GrPaint& paint,
955 const SkMatrix& viewMatrix,
956 const SkRect& oval,
957 const GrStrokeInfo& strokeInfo) {
958 RETURN_IF_ABANDONED
959 if (oval.isEmpty()) {
960 return;
961 }
962
963 if (strokeInfo.isDashed()) {
964 SkPath path;
965 path.setIsVolatile(true);
966 path.addOval(oval);
967 this->drawPath(rt, clip, paint, viewMatrix, path, strokeInfo);
968 return;
969 }
970
971 AutoCheckFlush acf(fContext);
972 GrPipelineBuilder pipelineBuilder;
973 if (!this->prepareToDraw(&pipelineBuilder, rt, clip, &paint)) {
974 return;
975 }
976
977 GrColor color = paint.getColor();
978 if (!GrOvalRenderer::DrawOval(fDrawTarget,
979 &pipelineBuilder,
980 color,
981 viewMatrix,
982 paint.isAntiAlias(),
983 oval,
984 strokeInfo)) {
985 SkPath path;
986 path.setIsVolatile(true);
987 path.addOval(oval);
988 this->internalDrawPath(fDrawTarget, &pipelineBuilder, viewMatrix, color,
989 paint.isAntiAlias(), path, strokeInfo);
990 }
991}
992
993// Can 'path' be drawn as a pair of filled nested rectangles?
994static bool is_nested_rects(GrDrawTarget* target,
995 GrPipelineBuilder* pipelineBuilder,
996 GrColor color,
997 const SkMatrix& viewMatrix,
998 const SkPath& path,
999 const SkStrokeRec& stroke,
1000 SkRect rects[2]) {
1001 SkASSERT(stroke.isFillStyle());
1002
1003 if (path.isInverseFillType()) {
1004 return false;
1005 }
1006
1007 // TODO: this restriction could be lifted if we were willing to apply
1008 // the matrix to all the points individually rather than just to the rect
1009 if (!viewMatrix.preservesAxisAlignment()) {
1010 return false;
1011 }
1012
1013 SkPath::Direction dirs[2];
1014 if (!path.isNestedFillRects(rects, dirs)) {
1015 return false;
1016 }
1017
1018 if (SkPath::kWinding_FillType == path.getFillType() && dirs[0] == dirs[1]) {
1019 // The two rects need to be wound opposite to each other
1020 return false;
1021 }
1022
1023 // Right now, nested rects where the margin is not the same width
1024 // all around do not render correctly
1025 const SkScalar* outer = rects[0].asScalars();
1026 const SkScalar* inner = rects[1].asScalars();
1027
1028 bool allEq = true;
1029
1030 SkScalar margin = SkScalarAbs(outer[0] - inner[0]);
1031 bool allGoE1 = margin >= SK_Scalar1;
1032
1033 for (int i = 1; i < 4; ++i) {
1034 SkScalar temp = SkScalarAbs(outer[i] - inner[i]);
1035 if (temp < SK_Scalar1) {
1036 allGoE1 = false;
1037 }
1038 if (!SkScalarNearlyEqual(margin, temp)) {
1039 allEq = false;
1040 }
1041 }
1042
1043 return allEq || allGoE1;
1044}
1045
1046void GrDrawContext::drawPath(GrRenderTarget* rt,
1047 const GrClip& clip,
1048 const GrPaint& paint,
1049 const SkMatrix& viewMatrix,
1050 const SkPath& path,
1051 const GrStrokeInfo& strokeInfo) {
1052 RETURN_IF_ABANDONED
1053 if (path.isEmpty()) {
1054 if (path.isInverseFillType()) {
1055 this->drawPaint(rt, clip, paint, viewMatrix);
1056 }
1057 return;
1058 }
1059
1060 GrColor color = paint.getColor();
1061
1062 // Note that internalDrawPath may sw-rasterize the path into a scratch texture.
1063 // Scratch textures can be recycled after they are returned to the texture
1064 // cache. This presents a potential hazard for buffered drawing. However,
1065 // the writePixels that uploads to the scratch will perform a flush so we're
1066 // OK.
1067 AutoCheckFlush acf(fContext);
1068 GrPipelineBuilder pipelineBuilder;
1069 if (!this->prepareToDraw(&pipelineBuilder, rt, clip, &paint)) {
1070 return;
1071 }
1072
1073 if (!strokeInfo.isDashed()) {
1074 bool useCoverageAA = paint.isAntiAlias() &&
vbuzinovdded6962015-06-12 08:59:45 -07001075 !pipelineBuilder.getRenderTarget()->isUnifiedMultisampled();
robertphillipsea461502015-05-26 11:38:03 -07001076
1077 if (useCoverageAA && strokeInfo.getWidth() < 0 && !path.isConvex()) {
1078 // Concave AA paths are expensive - try to avoid them for special cases
1079 SkRect rects[2];
1080
1081 if (is_nested_rects(fDrawTarget, &pipelineBuilder, color, viewMatrix, path, strokeInfo,
1082 rects)) {
1083 GrAARectRenderer::FillAANestedRects(fDrawTarget, &pipelineBuilder, color,
1084 viewMatrix, rects);
1085 return;
1086 }
1087 }
1088 SkRect ovalRect;
1089 bool isOval = path.isOval(&ovalRect);
1090
1091 if (isOval && !path.isInverseFillType()) {
1092 if (GrOvalRenderer::DrawOval(fDrawTarget,
1093 &pipelineBuilder,
1094 color,
1095 viewMatrix,
1096 paint.isAntiAlias(),
1097 ovalRect,
1098 strokeInfo)) {
1099 return;
1100 }
1101 }
1102 }
1103 this->internalDrawPath(fDrawTarget, &pipelineBuilder, viewMatrix, color, paint.isAntiAlias(),
1104 path, strokeInfo);
1105}
1106
1107void GrDrawContext::internalDrawPath(GrDrawTarget* target,
1108 GrPipelineBuilder* pipelineBuilder,
1109 const SkMatrix& viewMatrix,
1110 GrColor color,
1111 bool useAA,
1112 const SkPath& path,
1113 const GrStrokeInfo& strokeInfo) {
1114 RETURN_IF_ABANDONED
1115 SkASSERT(!path.isEmpty());
1116
1117
1118 // An Assumption here is that path renderer would use some form of tweaking
1119 // the src color (either the input alpha or in the frag shader) to implement
1120 // aa. If we have some future driver-mojo path AA that can do the right
1121 // thing WRT to the blend then we'll need some query on the PR.
1122 bool useCoverageAA = useAA &&
vbuzinovdded6962015-06-12 08:59:45 -07001123 !pipelineBuilder->getRenderTarget()->isUnifiedMultisampled();
robertphillipsea461502015-05-26 11:38:03 -07001124
1125
1126 GrPathRendererChain::DrawType type =
1127 useCoverageAA ? GrPathRendererChain::kColorAntiAlias_DrawType :
1128 GrPathRendererChain::kColor_DrawType;
1129
1130 const SkPath* pathPtr = &path;
1131 SkTLazy<SkPath> tmpPath;
1132 const GrStrokeInfo* strokeInfoPtr = &strokeInfo;
1133
1134 // Try a 1st time without stroking the path and without allowing the SW renderer
1135 GrPathRenderer* pr = fContext->getPathRenderer(target, pipelineBuilder, viewMatrix, *pathPtr,
1136 *strokeInfoPtr, false, type);
1137
1138 GrStrokeInfo dashlessStrokeInfo(strokeInfo, false);
1139 if (NULL == pr && strokeInfo.isDashed()) {
1140 // It didn't work above, so try again with dashed stroke converted to a dashless stroke.
1141 if (!strokeInfo.applyDashToPath(tmpPath.init(), &dashlessStrokeInfo, *pathPtr)) {
1142 return;
1143 }
1144 pathPtr = tmpPath.get();
1145 if (pathPtr->isEmpty()) {
1146 return;
1147 }
1148 strokeInfoPtr = &dashlessStrokeInfo;
1149 pr = fContext->getPathRenderer(target, pipelineBuilder, viewMatrix, *pathPtr, *strokeInfoPtr,
1150 false, type);
1151 }
1152
1153 if (NULL == pr) {
1154 if (!GrPathRenderer::IsStrokeHairlineOrEquivalent(*strokeInfoPtr, viewMatrix, NULL) &&
1155 !strokeInfoPtr->isFillStyle()) {
1156 // It didn't work above, so try again with stroke converted to a fill.
1157 if (!tmpPath.isValid()) {
1158 tmpPath.init();
1159 }
1160 dashlessStrokeInfo.setResScale(SkScalarAbs(viewMatrix.getMaxScale()));
1161 if (!dashlessStrokeInfo.applyToPath(tmpPath.get(), *pathPtr)) {
1162 return;
1163 }
1164 pathPtr = tmpPath.get();
1165 if (pathPtr->isEmpty()) {
1166 return;
1167 }
1168 dashlessStrokeInfo.setFillStyle();
1169 strokeInfoPtr = &dashlessStrokeInfo;
1170 }
1171
1172 // This time, allow SW renderer
1173 pr = fContext->getPathRenderer(target, pipelineBuilder, viewMatrix, *pathPtr, *strokeInfoPtr,
1174 true, type);
1175 }
1176
1177 if (NULL == pr) {
1178#ifdef SK_DEBUG
1179 SkDebugf("Unable to find path renderer compatible with path.\n");
1180#endif
1181 return;
1182 }
1183
1184 pr->drawPath(target, pipelineBuilder, color, viewMatrix, *pathPtr, *strokeInfoPtr, useCoverageAA);
1185}
1186
1187bool GrDrawContext::prepareToDraw(GrPipelineBuilder* pipelineBuilder,
1188 GrRenderTarget* rt,
1189 const GrClip& clip,
1190 const GrPaint* paint) {
1191 RETURN_FALSE_IF_ABANDONED
1192
1193 ASSERT_OWNED_RESOURCE(rt);
1194 SkASSERT(rt && paint);
1195 pipelineBuilder->setFromPaint(*paint, rt, clip);
1196 return true;
1197}
1198
1199bool GrDrawContext::prepareToDraw(GrRenderTarget* rt) {
1200 RETURN_FALSE_IF_ABANDONED
1201
1202 ASSERT_OWNED_RESOURCE(rt);
1203 SkASSERT(rt);
1204 return true;
1205}
1206
robertphillips2334fb62015-06-17 05:43:33 -07001207void GrDrawContext::drawBatch(GrPipelineBuilder* pipelineBuilder, GrBatch* batch) {
1208 fDrawTarget->drawBatch(pipelineBuilder, batch);
1209}
1210
robertphillipsea461502015-05-26 11:38:03 -07001211///////////////////////////////////////////////////////////////////////////////////////////////////
1212
1213#ifdef GR_TEST_UTILS
1214
1215BATCH_TEST_DEFINE(StrokeRectBatch) {
1216 StrokeRectBatch::Geometry geometry;
1217 geometry.fViewMatrix = GrTest::TestMatrix(random);
1218 geometry.fColor = GrRandomColor(random);
1219 geometry.fRect = GrTest::TestRect(random);
1220 geometry.fStrokeWidth = random->nextBool() ? 0.0f : 1.0f;
1221
1222 return StrokeRectBatch::Create(geometry, random->nextBool());
1223}
1224
1225static uint32_t seed_vertices(GrPrimitiveType type) {
1226 switch (type) {
1227 case kTriangles_GrPrimitiveType:
1228 case kTriangleStrip_GrPrimitiveType:
1229 case kTriangleFan_GrPrimitiveType:
1230 return 3;
1231 case kPoints_GrPrimitiveType:
1232 return 1;
1233 case kLines_GrPrimitiveType:
1234 case kLineStrip_GrPrimitiveType:
1235 return 2;
1236 }
1237 SkFAIL("Incomplete switch\n");
1238 return 0;
1239}
1240
1241static uint32_t primitive_vertices(GrPrimitiveType type) {
1242 switch (type) {
1243 case kTriangles_GrPrimitiveType:
1244 return 3;
1245 case kLines_GrPrimitiveType:
1246 return 2;
1247 case kTriangleStrip_GrPrimitiveType:
1248 case kTriangleFan_GrPrimitiveType:
1249 case kPoints_GrPrimitiveType:
1250 case kLineStrip_GrPrimitiveType:
1251 return 1;
1252 }
1253 SkFAIL("Incomplete switch\n");
1254 return 0;
1255}
1256
1257static SkPoint random_point(SkRandom* random, SkScalar min, SkScalar max) {
1258 SkPoint p;
1259 p.fX = random->nextRangeScalar(min, max);
1260 p.fY = random->nextRangeScalar(min, max);
1261 return p;
1262}
1263
1264static void randomize_params(size_t count, size_t maxVertex, SkScalar min, SkScalar max,
1265 SkRandom* random,
1266 SkTArray<SkPoint>* positions,
1267 SkTArray<SkPoint>* texCoords, bool hasTexCoords,
1268 SkTArray<GrColor>* colors, bool hasColors,
1269 SkTArray<uint16_t>* indices, bool hasIndices) {
1270 for (uint32_t v = 0; v < count; v++) {
1271 positions->push_back(random_point(random, min, max));
1272 if (hasTexCoords) {
1273 texCoords->push_back(random_point(random, min, max));
1274 }
1275 if (hasColors) {
1276 colors->push_back(GrRandomColor(random));
1277 }
1278 if (hasIndices) {
1279 SkASSERT(maxVertex <= SK_MaxU16);
1280 indices->push_back(random->nextULessThan((uint16_t)maxVertex));
1281 }
1282 }
1283}
1284
1285BATCH_TEST_DEFINE(VerticesBatch) {
1286 GrPrimitiveType type = GrPrimitiveType(random->nextULessThan(kLast_GrPrimitiveType + 1));
1287 uint32_t primitiveCount = random->nextRangeU(1, 100);
1288
1289 // TODO make 'sensible' indexbuffers
1290 SkTArray<SkPoint> positions;
1291 SkTArray<SkPoint> texCoords;
1292 SkTArray<GrColor> colors;
1293 SkTArray<uint16_t> indices;
1294
1295 bool hasTexCoords = random->nextBool();
1296 bool hasIndices = random->nextBool();
1297 bool hasColors = random->nextBool();
1298
1299 uint32_t vertexCount = seed_vertices(type) + (primitiveCount - 1) * primitive_vertices(type);
1300
1301 static const SkScalar kMinVertExtent = -100.f;
1302 static const SkScalar kMaxVertExtent = 100.f;
1303 randomize_params(seed_vertices(type), vertexCount, kMinVertExtent, kMaxVertExtent,
1304 random,
1305 &positions,
1306 &texCoords, hasTexCoords,
1307 &colors, hasColors,
1308 &indices, hasIndices);
1309
1310 for (uint32_t i = 1; i < primitiveCount; i++) {
1311 randomize_params(primitive_vertices(type), vertexCount, kMinVertExtent, kMaxVertExtent,
1312 random,
1313 &positions,
1314 &texCoords, hasTexCoords,
1315 &colors, hasColors,
1316 &indices, hasIndices);
1317 }
1318
1319 SkMatrix viewMatrix = GrTest::TestMatrix(random);
1320 SkRect bounds;
1321 SkDEBUGCODE(bool result = ) bounds.setBoundsCheck(positions.begin(), vertexCount);
1322 SkASSERT(result);
1323
1324 viewMatrix.mapRect(&bounds);
1325
1326 DrawVerticesBatch::Geometry geometry;
1327 geometry.fColor = GrRandomColor(random);
1328 return DrawVerticesBatch::Create(geometry, type, viewMatrix,
1329 positions.begin(), vertexCount,
1330 indices.begin(), hasIndices ? vertexCount : 0,
1331 colors.begin(),
1332 texCoords.begin(),
1333 bounds);
1334}
1335
1336#endif
1337