1/*
2 * Copyright 2016 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "GrRegionOp.h"
9
10#include "GrDefaultGeoProcFactory.h"
11#include "GrMeshDrawOp.h"
12#include "GrOpFlushState.h"
13#include "GrResourceProvider.h"
14#include "SkMatrixPriv.h"
15#include "SkRegion.h"
16
17static const int kVertsPerInstance = 4;
18static const int kIndicesPerInstance = 6;
19
20static sk_sp<GrGeometryProcessor> make_gp(const SkMatrix& viewMatrix) {
21    using namespace GrDefaultGeoProcFactory;
22    return GrDefaultGeoProcFactory::Make(Color::kPremulGrColorAttribute_Type, Coverage::kSolid_Type,
23                                         LocalCoords::kUsePosition_Type, viewMatrix);
24}
25
26static void tesselate_region(intptr_t vertices,
27                             size_t vertexStride,
28                             GrColor color,
29                             const SkRegion& region) {
30    SkRegion::Iterator iter(region);
31
32    intptr_t verts = vertices;
33    while (!iter.done()) {
34        SkRect rect = SkRect::Make(iter.rect());
35        SkPoint* position = (SkPoint*)verts;
36        position->setRectFan(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, vertexStride);
37
38        static const int kColorOffset = sizeof(SkPoint);
39        GrColor* vertColor = reinterpret_cast<GrColor*>(verts + kColorOffset);
40        for (int i = 0; i < kVertsPerInstance; i++) {
41            *vertColor = color;
42            vertColor = (GrColor*)((intptr_t)vertColor + vertexStride);
43        }
44
45        verts += vertexStride * kVertsPerInstance;
46        iter.next();
47    }
48}
49
50class RegionOp final : public GrMeshDrawOp {
51public:
52    DEFINE_OP_CLASS_ID
53
54    RegionOp(GrColor color, const SkMatrix& viewMatrix, const SkRegion& region)
55            : INHERITED(ClassID()), fViewMatrix(viewMatrix) {
56        RegionInfo& info = fRegions.push_back();
57        info.fColor = color;
58        info.fRegion = region;
59
60        SkRect bounds = SkRect::Make(region.getBounds());
61        this->setTransformedBounds(bounds, viewMatrix, HasAABloat::kNo, IsZeroArea::kNo);
62    }
63
64    const char* name() const override { return "GrRegionOp"; }
65
66    SkString dumpInfo() const override {
67        SkString str;
68        str.appendf("# combined: %d\n", fRegions.count());
69        for (int i = 0; i < fRegions.count(); ++i) {
70            const RegionInfo& info = fRegions[i];
71            str.appendf("%d: Color: 0x%08x, Region with %d rects\n", i, info.fColor,
72                        info.fRegion.computeRegionComplexity());
73        }
74        str.append(DumpPipelineInfo(*this->pipeline()));
75        str.append(INHERITED::dumpInfo());
76        return str;
77    }
78
79private:
80    void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
81                                            GrPipelineAnalysisCoverage* coverage) const override {
82        color->setToConstant(fRegions[0].fColor);
83        *coverage = GrPipelineAnalysisCoverage::kNone;
84    }
85
86    void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
87        optimizations.getOverrideColorIfSet(&fRegions[0].fColor);
88    }
89
90    void onPrepareDraws(Target* target) const override {
91        sk_sp<GrGeometryProcessor> gp = make_gp(fViewMatrix);
92        if (!gp) {
93            SkDebugf("Couldn't create GrGeometryProcessor\n");
94            return;
95        }
96        SkASSERT(gp->getVertexStride() == sizeof(GrDefaultGeoProcFactory::PositionColorAttr));
97
98        int numRegions = fRegions.count();
99        int numRects = 0;
100        for (int i = 0; i < numRegions; i++) {
101            numRects += fRegions[i].fRegion.computeRegionComplexity();
102        }
103
104        size_t vertexStride = gp->getVertexStride();
105        sk_sp<const GrBuffer> indexBuffer(target->resourceProvider()->refQuadIndexBuffer());
106        InstancedHelper helper;
107        void* vertices =
108                helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer.get(),
109                            kVertsPerInstance, kIndicesPerInstance, numRects);
110        if (!vertices || !indexBuffer) {
111            SkDebugf("Could not allocate vertices\n");
112            return;
113        }
114
115        intptr_t verts = reinterpret_cast<intptr_t>(vertices);
116        for (int i = 0; i < numRegions; i++) {
117            tesselate_region(verts, vertexStride, fRegions[i].fColor, fRegions[i].fRegion);
118            int numRectsInRegion = fRegions[i].fRegion.computeRegionComplexity();
119            verts += numRectsInRegion * kVertsPerInstance * vertexStride;
120        }
121        helper.recordDraw(target, gp.get());
122    }
123
124    bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
125        RegionOp* that = t->cast<RegionOp>();
126        if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
127                                    that->bounds(), caps)) {
128            return false;
129        }
130
131        if (fViewMatrix != that->fViewMatrix) {
132            return false;
133        }
134
135        fRegions.push_back_n(that->fRegions.count(), that->fRegions.begin());
136        this->joinBounds(*that);
137        return true;
138    }
139
140    struct RegionInfo {
141        GrColor fColor;
142        SkRegion fRegion;
143    };
144
145    SkMatrix fViewMatrix;
146    SkSTArray<1, RegionInfo, true> fRegions;
147
148    typedef GrMeshDrawOp INHERITED;
149};
150
151namespace GrRegionOp {
152
153std::unique_ptr<GrMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
154                                   const SkRegion& region) {
155    return std::unique_ptr<GrMeshDrawOp>(new RegionOp(color, viewMatrix, region));
156}
157}
158