Home | History | Annotate | Download | only in ops
      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 "GrNonAAStrokeRectOp.h"
      9 
     10 #include "GrColor.h"
     11 #include "GrDefaultGeoProcFactory.h"
     12 #include "GrDrawOpTest.h"
     13 #include "GrMeshDrawOp.h"
     14 #include "GrOpFlushState.h"
     15 #include "SkStrokeRec.h"
     16 #include "SkRandom.h"
     17 
     18 /*  create a triangle strip that strokes the specified rect. There are 8
     19     unique vertices, but we repeat the last 2 to close up. Alternatively we
     20     could use an indices array, and then only send 8 verts, but not sure that
     21     would be faster.
     22     */
     23 static void init_stroke_rect_strip(SkPoint verts[10], const SkRect& rect, SkScalar width) {
     24     const SkScalar rad = SkScalarHalf(width);
     25     // TODO we should be able to enable this assert, but we'd have to filter these draws
     26     // this is a bug
     27     // SkASSERT(rad < rect.width() / 2 && rad < rect.height() / 2);
     28 
     29     verts[0].set(rect.fLeft + rad, rect.fTop + rad);
     30     verts[1].set(rect.fLeft - rad, rect.fTop - rad);
     31     verts[2].set(rect.fRight - rad, rect.fTop + rad);
     32     verts[3].set(rect.fRight + rad, rect.fTop - rad);
     33     verts[4].set(rect.fRight - rad, rect.fBottom - rad);
     34     verts[5].set(rect.fRight + rad, rect.fBottom + rad);
     35     verts[6].set(rect.fLeft + rad, rect.fBottom - rad);
     36     verts[7].set(rect.fLeft - rad, rect.fBottom + rad);
     37     verts[8] = verts[0];
     38     verts[9] = verts[1];
     39 }
     40 
     41 // Allow all hairlines and all miters, so long as the miter limit doesn't produce beveled corners.
     42 inline static bool allowed_stroke(const SkStrokeRec& stroke) {
     43     SkASSERT(stroke.getStyle() == SkStrokeRec::kStroke_Style ||
     44              stroke.getStyle() == SkStrokeRec::kHairline_Style);
     45     return !stroke.getWidth() ||
     46            (stroke.getJoin() == SkPaint::kMiter_Join && stroke.getMiter() > SK_ScalarSqrt2);
     47 }
     48 
     49 class NonAAStrokeRectOp final : public GrMeshDrawOp {
     50 public:
     51     DEFINE_OP_CLASS_ID
     52 
     53     const char* name() const override { return "NonAAStrokeRectOp"; }
     54 
     55     SkString dumpInfo() const override {
     56         SkString string;
     57         string.appendf(
     58                 "Color: 0x%08x, Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
     59                 "StrokeWidth: %.2f\n",
     60                 fColor, fRect.fLeft, fRect.fTop, fRect.fRight, fRect.fBottom, fStrokeWidth);
     61         string.append(DumpPipelineInfo(*this->pipeline()));
     62         string.append(INHERITED::dumpInfo());
     63         return string;
     64     }
     65 
     66     static std::unique_ptr<GrMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
     67                                               const SkRect& rect, const SkStrokeRec& stroke,
     68                                               bool snapToPixelCenters) {
     69         if (!allowed_stroke(stroke)) {
     70             return nullptr;
     71         }
     72         NonAAStrokeRectOp* op = new NonAAStrokeRectOp();
     73         op->fColor = color;
     74         op->fViewMatrix = viewMatrix;
     75         op->fRect = rect;
     76         // Sort the rect for hairlines
     77         op->fRect.sort();
     78         op->fStrokeWidth = stroke.getWidth();
     79 
     80         SkScalar rad = SkScalarHalf(op->fStrokeWidth);
     81         SkRect bounds = rect;
     82         bounds.outset(rad, rad);
     83 
     84         // If our caller snaps to pixel centers then we have to round out the bounds
     85         if (snapToPixelCenters) {
     86             viewMatrix.mapRect(&bounds);
     87             // We want to be consistent with how we snap non-aa lines. To match what we do in
     88             // GrGLSLVertexShaderBuilder, we first floor all the vertex values and then add half a
     89             // pixel to force us to pixel centers.
     90             bounds.set(SkScalarFloorToScalar(bounds.fLeft),
     91                        SkScalarFloorToScalar(bounds.fTop),
     92                        SkScalarFloorToScalar(bounds.fRight),
     93                        SkScalarFloorToScalar(bounds.fBottom));
     94             bounds.offset(0.5f, 0.5f);
     95             op->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
     96         } else {
     97             op->setTransformedBounds(bounds, op->fViewMatrix, HasAABloat::kNo, IsZeroArea::kNo);
     98         }
     99         return std::unique_ptr<GrMeshDrawOp>(op);
    100     }
    101 
    102 private:
    103     NonAAStrokeRectOp() : INHERITED(ClassID()) {}
    104 
    105     void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
    106                                             GrPipelineAnalysisCoverage* coverage) const override {
    107         color->setToConstant(fColor);
    108         *coverage = GrPipelineAnalysisCoverage::kNone;
    109     }
    110 
    111     void onPrepareDraws(Target* target) const override {
    112         sk_sp<GrGeometryProcessor> gp;
    113         {
    114             using namespace GrDefaultGeoProcFactory;
    115             Color color(fColor);
    116             LocalCoords::Type localCoordsType = fNeedsLocalCoords
    117                                                         ? LocalCoords::kUsePosition_Type
    118                                                         : LocalCoords::kUnused_Type;
    119             gp = GrDefaultGeoProcFactory::Make(color, Coverage::kSolid_Type, localCoordsType,
    120                                                fViewMatrix);
    121         }
    122 
    123         size_t vertexStride = gp->getVertexStride();
    124 
    125         SkASSERT(vertexStride == sizeof(GrDefaultGeoProcFactory::PositionAttr));
    126 
    127         int vertexCount = kVertsPerHairlineRect;
    128         if (fStrokeWidth > 0) {
    129             vertexCount = kVertsPerStrokeRect;
    130         }
    131 
    132         const GrBuffer* vertexBuffer;
    133         int firstVertex;
    134 
    135         void* verts =
    136                 target->makeVertexSpace(vertexStride, vertexCount, &vertexBuffer, &firstVertex);
    137 
    138         if (!verts) {
    139             SkDebugf("Could not allocate vertices\n");
    140             return;
    141         }
    142 
    143         SkPoint* vertex = reinterpret_cast<SkPoint*>(verts);
    144 
    145         GrPrimitiveType primType;
    146         if (fStrokeWidth > 0) {
    147             primType = kTriangleStrip_GrPrimitiveType;
    148             init_stroke_rect_strip(vertex, fRect, fStrokeWidth);
    149         } else {
    150             // hairline
    151             primType = kLineStrip_GrPrimitiveType;
    152             vertex[0].set(fRect.fLeft, fRect.fTop);
    153             vertex[1].set(fRect.fRight, fRect.fTop);
    154             vertex[2].set(fRect.fRight, fRect.fBottom);
    155             vertex[3].set(fRect.fLeft, fRect.fBottom);
    156             vertex[4].set(fRect.fLeft, fRect.fTop);
    157         }
    158 
    159         GrMesh mesh;
    160         mesh.init(primType, vertexBuffer, firstVertex, vertexCount);
    161         target->draw(gp.get(), mesh);
    162     }
    163 
    164     void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
    165         optimizations.getOverrideColorIfSet(&fColor);
    166         fNeedsLocalCoords = optimizations.readsLocalCoords();
    167     }
    168 
    169     bool onCombineIfPossible(GrOp* t, const GrCaps&) override {
    170         // NonAA stroke rects cannot combine right now
    171         // TODO make these combinable.
    172         return false;
    173     }
    174 
    175     GrColor fColor;
    176     SkMatrix fViewMatrix;
    177     SkRect fRect;
    178     SkScalar fStrokeWidth;
    179     bool fNeedsLocalCoords;
    180 
    181     const static int kVertsPerHairlineRect = 5;
    182     const static int kVertsPerStrokeRect = 10;
    183 
    184     typedef GrMeshDrawOp INHERITED;
    185 };
    186 
    187 namespace GrNonAAStrokeRectOp {
    188 
    189 std::unique_ptr<GrMeshDrawOp> Make(GrColor color,
    190                                    const SkMatrix& viewMatrix,
    191                                    const SkRect& rect,
    192                                    const SkStrokeRec& stroke,
    193                                    bool snapToPixelCenters) {
    194     return NonAAStrokeRectOp::Make(color, viewMatrix, rect, stroke, snapToPixelCenters);
    195 }
    196 }
    197 
    198 #if GR_TEST_UTILS
    199 
    200 DRAW_OP_TEST_DEFINE(NonAAStrokeRectOp) {
    201     SkMatrix viewMatrix = GrTest::TestMatrix(random);
    202     GrColor color = GrRandomColor(random);
    203     SkRect rect = GrTest::TestRect(random);
    204     SkScalar strokeWidth = random->nextBool() ? 0.0f : 2.0f;
    205     SkPaint paint;
    206     paint.setStrokeWidth(strokeWidth);
    207     paint.setStyle(SkPaint::kStroke_Style);
    208     paint.setStrokeJoin(SkPaint::kMiter_Join);
    209     SkStrokeRec strokeRec(paint);
    210     return GrNonAAStrokeRectOp::Make(color, viewMatrix, rect, strokeRec, random->nextBool());
    211 }
    212 
    213 #endif
    214