1/*
2 * Copyright 2015 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 "GrDrawAtlasOp.h"
9#include "GrDrawOpTest.h"
10#include "GrOpFlushState.h"
11#include "SkGr.h"
12#include "SkRSXform.h"
13#include "SkRandom.h"
14
15void GrDrawAtlasOp::applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) {
16    SkASSERT(fGeoData.count() == 1);
17    if (optimizations.getOverrideColorIfSet(&fGeoData[0].fColor) && fHasColors) {
18        size_t vertexStride =
19                sizeof(SkPoint) + sizeof(SkPoint) + (this->hasColors() ? sizeof(GrColor) : 0);
20        uint8_t* currVertex = fGeoData[0].fVerts.begin();
21        for (int i = 0; i < 4 * fQuadCount; ++i) {
22            *(reinterpret_cast<GrColor*>(currVertex + sizeof(SkPoint))) = fGeoData[0].fColor;
23            currVertex += vertexStride;
24        }
25    }
26
27    fColor = fGeoData[0].fColor;
28    // We'd like to assert this, but we can't because of GLPrograms test
29    // SkASSERT(init.readsLocalCoords());
30}
31
32static sk_sp<GrGeometryProcessor> make_gp(bool hasColors,
33                                          GrColor color,
34                                          const SkMatrix& viewMatrix) {
35    using namespace GrDefaultGeoProcFactory;
36    Color gpColor(color);
37    if (hasColors) {
38        gpColor.fType = Color::kPremulGrColorAttribute_Type;
39    }
40
41    return GrDefaultGeoProcFactory::Make(gpColor, Coverage::kSolid_Type,
42                                         LocalCoords::kHasExplicit_Type, viewMatrix);
43}
44
45void GrDrawAtlasOp::onPrepareDraws(Target* target) const {
46    // Setup geometry processor
47    sk_sp<GrGeometryProcessor> gp(make_gp(this->hasColors(), this->color(), this->viewMatrix()));
48
49    int instanceCount = fGeoData.count();
50    size_t vertexStride = gp->getVertexStride();
51    SkASSERT(vertexStride ==
52             sizeof(SkPoint) + sizeof(SkPoint) + (this->hasColors() ? sizeof(GrColor) : 0));
53
54    QuadHelper helper;
55    int numQuads = this->quadCount();
56    void* verts = helper.init(target, vertexStride, numQuads);
57    if (!verts) {
58        SkDebugf("Could not allocate vertices\n");
59        return;
60    }
61
62    uint8_t* vertPtr = reinterpret_cast<uint8_t*>(verts);
63    for (int i = 0; i < instanceCount; i++) {
64        const Geometry& args = fGeoData[i];
65
66        size_t allocSize = args.fVerts.count();
67        memcpy(vertPtr, args.fVerts.begin(), allocSize);
68        vertPtr += allocSize;
69    }
70    helper.recordDraw(target, gp.get());
71}
72
73GrDrawAtlasOp::GrDrawAtlasOp(GrColor color, const SkMatrix& viewMatrix, int spriteCount,
74                             const SkRSXform* xforms, const SkRect* rects, const SkColor* colors)
75        : INHERITED(ClassID()) {
76    SkASSERT(xforms);
77    SkASSERT(rects);
78
79    fViewMatrix = viewMatrix;
80    Geometry& installedGeo = fGeoData.push_back();
81    installedGeo.fColor = color;
82
83    // Figure out stride and offsets
84    // Order within the vertex is: position [color] texCoord
85    size_t texOffset = sizeof(SkPoint);
86    size_t vertexStride = 2 * sizeof(SkPoint);
87    fHasColors = SkToBool(colors);
88    if (colors) {
89        texOffset += sizeof(GrColor);
90        vertexStride += sizeof(GrColor);
91    }
92
93    // Compute buffer size and alloc buffer
94    fQuadCount = spriteCount;
95    int allocSize = static_cast<int>(4 * vertexStride * spriteCount);
96    installedGeo.fVerts.reset(allocSize);
97    uint8_t* currVertex = installedGeo.fVerts.begin();
98
99    SkRect bounds;
100    bounds.setLargestInverted();
101    int paintAlpha = GrColorUnpackA(installedGeo.fColor);
102    for (int spriteIndex = 0; spriteIndex < spriteCount; ++spriteIndex) {
103        // Transform rect
104        SkPoint quad[4];
105        const SkRect& currRect = rects[spriteIndex];
106        xforms[spriteIndex].toQuad(currRect.width(), currRect.height(), quad);
107
108        // Copy colors if necessary
109        if (colors) {
110            // convert to GrColor
111            SkColor color = colors[spriteIndex];
112            if (paintAlpha != 255) {
113                color = SkColorSetA(color, SkMulDiv255Round(SkColorGetA(color), paintAlpha));
114            }
115            GrColor grColor = SkColorToPremulGrColor(color);
116
117            *(reinterpret_cast<GrColor*>(currVertex + sizeof(SkPoint))) = grColor;
118            *(reinterpret_cast<GrColor*>(currVertex + vertexStride + sizeof(SkPoint))) = grColor;
119            *(reinterpret_cast<GrColor*>(currVertex + 2 * vertexStride + sizeof(SkPoint))) =
120                    grColor;
121            *(reinterpret_cast<GrColor*>(currVertex + 3 * vertexStride + sizeof(SkPoint))) =
122                    grColor;
123        }
124
125        // Copy position and uv to verts
126        *(reinterpret_cast<SkPoint*>(currVertex)) = quad[0];
127        *(reinterpret_cast<SkPoint*>(currVertex + texOffset)) =
128                SkPoint::Make(currRect.fLeft, currRect.fTop);
129        bounds.growToInclude(quad[0].fX, quad[0].fY);
130        currVertex += vertexStride;
131
132        *(reinterpret_cast<SkPoint*>(currVertex)) = quad[1];
133        *(reinterpret_cast<SkPoint*>(currVertex + texOffset)) =
134                SkPoint::Make(currRect.fRight, currRect.fTop);
135        bounds.growToInclude(quad[1].fX, quad[1].fY);
136        currVertex += vertexStride;
137
138        *(reinterpret_cast<SkPoint*>(currVertex)) = quad[2];
139        *(reinterpret_cast<SkPoint*>(currVertex + texOffset)) =
140                SkPoint::Make(currRect.fRight, currRect.fBottom);
141        bounds.growToInclude(quad[2].fX, quad[2].fY);
142        currVertex += vertexStride;
143
144        *(reinterpret_cast<SkPoint*>(currVertex)) = quad[3];
145        *(reinterpret_cast<SkPoint*>(currVertex + texOffset)) =
146                SkPoint::Make(currRect.fLeft, currRect.fBottom);
147        bounds.growToInclude(quad[3].fX, quad[3].fY);
148        currVertex += vertexStride;
149    }
150
151    this->setTransformedBounds(bounds, viewMatrix, HasAABloat::kNo, IsZeroArea::kNo);
152}
153
154bool GrDrawAtlasOp::onCombineIfPossible(GrOp* t, const GrCaps& caps) {
155    GrDrawAtlasOp* that = t->cast<GrDrawAtlasOp>();
156
157    if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
158                                that->bounds(), caps)) {
159        return false;
160    }
161
162    // We currently use a uniform viewmatrix for this op.
163    if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
164        return false;
165    }
166
167    if (this->hasColors() != that->hasColors()) {
168        return false;
169    }
170
171    if (!this->hasColors() && this->color() != that->color()) {
172        return false;
173    }
174
175    fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
176    fQuadCount += that->quadCount();
177
178    this->joinBounds(*that);
179    return true;
180}
181
182#if GR_TEST_UTILS
183
184static SkRSXform random_xform(SkRandom* random) {
185    static const SkScalar kMinExtent = -100.f;
186    static const SkScalar kMaxExtent = 100.f;
187    static const SkScalar kMinScale = 0.1f;
188    static const SkScalar kMaxScale = 100.f;
189    static const SkScalar kMinRotate = -SK_ScalarPI;
190    static const SkScalar kMaxRotate = SK_ScalarPI;
191
192    SkRSXform xform = SkRSXform::MakeFromRadians(random->nextRangeScalar(kMinScale, kMaxScale),
193                                                 random->nextRangeScalar(kMinRotate, kMaxRotate),
194                                                 random->nextRangeScalar(kMinExtent, kMaxExtent),
195                                                 random->nextRangeScalar(kMinExtent, kMaxExtent),
196                                                 random->nextRangeScalar(kMinExtent, kMaxExtent),
197                                                 random->nextRangeScalar(kMinExtent, kMaxExtent));
198    return xform;
199}
200
201static SkRect random_texRect(SkRandom* random) {
202    static const SkScalar kMinCoord = 0.0f;
203    static const SkScalar kMaxCoord = 1024.f;
204
205    SkRect texRect = SkRect::MakeLTRB(random->nextRangeScalar(kMinCoord, kMaxCoord),
206                                      random->nextRangeScalar(kMinCoord, kMaxCoord),
207                                      random->nextRangeScalar(kMinCoord, kMaxCoord),
208                                      random->nextRangeScalar(kMinCoord, kMaxCoord));
209    texRect.sort();
210    return texRect;
211}
212
213static void randomize_params(uint32_t count, SkRandom* random, SkTArray<SkRSXform>* xforms,
214                             SkTArray<SkRect>* texRects, SkTArray<GrColor>* colors,
215                             bool hasColors) {
216    for (uint32_t v = 0; v < count; v++) {
217        xforms->push_back(random_xform(random));
218        texRects->push_back(random_texRect(random));
219        if (hasColors) {
220            colors->push_back(GrRandomColor(random));
221        }
222    }
223}
224
225DRAW_OP_TEST_DEFINE(GrDrawAtlasOp) {
226    uint32_t spriteCount = random->nextRangeU(1, 100);
227
228    SkTArray<SkRSXform> xforms(spriteCount);
229    SkTArray<SkRect> texRects(spriteCount);
230    SkTArray<GrColor> colors;
231
232    bool hasColors = random->nextBool();
233
234    randomize_params(spriteCount, random, &xforms, &texRects, &colors, hasColors);
235
236    SkMatrix viewMatrix = GrTest::TestMatrix(random);
237
238    GrColor color = GrRandomColor(random);
239    return GrDrawAtlasOp::Make(color, viewMatrix, spriteCount, xforms.begin(), texRects.begin(),
240                               hasColors ? colors.begin() : nullptr);
241}
242
243#endif
244