aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/gpu/batches/GrStrokeRectBatch.cpp
blob: 766302bf5b5b251b52a0163b2a224099ec432a67 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/*
 * Copyright 2015 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "GrStrokeRectBatch.h"
#include "GrBatchTest.h"
#include "GrBatchFlushState.h"
#include "SkRandom.h"

GrStrokeRectBatch::GrStrokeRectBatch(const Geometry& geometry, bool snapToPixelCenters) {
    this->initClassID<GrStrokeRectBatch>();

    fBatch.fHairline = geometry.fStrokeWidth == 0;

    fGeoData.push_back(geometry);

    // setup bounds
    fBounds = geometry.fRect;
    SkScalar rad = SkScalarHalf(geometry.fStrokeWidth);
    fBounds.outset(rad, rad);
    geometry.fViewMatrix.mapRect(&fBounds);

    // If our caller snaps to pixel centers then we have to round out the bounds
    if (snapToPixelCenters) {
        fBounds.roundOut();
    }
}

void GrStrokeRectBatch::initBatchTracker(const GrPipelineOptimizations& opt) {
    // Handle any color overrides
    if (!opt.readsColor()) {
        fGeoData[0].fColor = GrColor_ILLEGAL;
    }
    opt.getOverrideColorIfSet(&fGeoData[0].fColor);

    // setup batch properties
    fBatch.fColorIgnored = !opt.readsColor();
    fBatch.fColor = fGeoData[0].fColor;
    fBatch.fUsesLocalCoords = opt.readsLocalCoords();
    fBatch.fCoverageIgnored = !opt.readsCoverage();
}

/*  create a triangle strip that strokes the specified rect. There are 8
    unique vertices, but we repeat the last 2 to close up. Alternatively we
    could use an indices array, and then only send 8 verts, but not sure that
    would be faster.
    */
static void init_stroke_rect_strip(SkPoint verts[10], const SkRect& rect, SkScalar width) {
    const SkScalar rad = SkScalarHalf(width);
    // TODO we should be able to enable this assert, but we'd have to filter these draws
    // this is a bug
    //SkASSERT(rad < rect.width() / 2 && rad < rect.height() / 2);

    verts[0].set(rect.fLeft + rad, rect.fTop + rad);
    verts[1].set(rect.fLeft - rad, rect.fTop - rad);
    verts[2].set(rect.fRight - rad, rect.fTop + rad);
    verts[3].set(rect.fRight + rad, rect.fTop - rad);
    verts[4].set(rect.fRight - rad, rect.fBottom - rad);
    verts[5].set(rect.fRight + rad, rect.fBottom + rad);
    verts[6].set(rect.fLeft + rad, rect.fBottom - rad);
    verts[7].set(rect.fLeft - rad, rect.fBottom + rad);
    verts[8] = verts[0];
    verts[9] = verts[1];
}

void GrStrokeRectBatch::onPrepareDraws(Target* target) {
    SkAutoTUnref<const GrGeometryProcessor> gp;
    {
        using namespace GrDefaultGeoProcFactory;
        Color color(this->color());
        Coverage coverage(this->coverageIgnored() ? Coverage::kSolid_Type :
                                                    Coverage::kNone_Type);
        LocalCoords localCoords(this->usesLocalCoords() ? LocalCoords::kUsePosition_Type :
                                                            LocalCoords::kUnused_Type);
        gp.reset(GrDefaultGeoProcFactory::Create(color, coverage, localCoords,
                                                    this->viewMatrix()));
    }

    target->initDraw(gp, this->pipeline());

    size_t vertexStride = gp->getVertexStride();

    SkASSERT(vertexStride == sizeof(GrDefaultGeoProcFactory::PositionAttr));

    Geometry& args = fGeoData[0];

    int vertexCount = kVertsPerHairlineRect;
    if (args.fStrokeWidth > 0) {
        vertexCount = kVertsPerStrokeRect;
    }

    const GrVertexBuffer* vertexBuffer;
    int firstVertex;

    void* verts = target->makeVertexSpace(vertexStride, vertexCount, &vertexBuffer, &firstVertex);

    if (!verts) {
        SkDebugf("Could not allocate vertices\n");
        return;
    }

    SkPoint* vertex = reinterpret_cast<SkPoint*>(verts);

    GrPrimitiveType primType;

    if (args.fStrokeWidth > 0) {;
        primType = kTriangleStrip_GrPrimitiveType;
        args.fRect.sort();
        init_stroke_rect_strip(vertex, args.fRect, args.fStrokeWidth);
    } else {
        // hairline
        primType = kLineStrip_GrPrimitiveType;
        vertex[0].set(args.fRect.fLeft, args.fRect.fTop);
        vertex[1].set(args.fRect.fRight, args.fRect.fTop);
        vertex[2].set(args.fRect.fRight, args.fRect.fBottom);
        vertex[3].set(args.fRect.fLeft, args.fRect.fBottom);
        vertex[4].set(args.fRect.fLeft, args.fRect.fTop);
    }

    GrVertices vertices;
    vertices.init(primType, vertexBuffer, firstVertex, vertexCount);
    target->draw(vertices);
}

#ifdef GR_TEST_UTILS

DRAW_BATCH_TEST_DEFINE(GrStrokeRectBatch) {
    GrStrokeRectBatch::Geometry geometry;
    geometry.fViewMatrix = GrTest::TestMatrix(random);
    geometry.fColor = GrRandomColor(random);
    geometry.fRect = GrTest::TestRect(random);
    geometry.fStrokeWidth = random->nextBool() ? 0.0f : 1.0f;

    return GrStrokeRectBatch::Create(geometry, random->nextBool());
}

#endif