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 "GrColor.h"
      9 #include "GrDefaultGeoProcFactory.h"
     10 #include "GrDrawOpTest.h"
     11 #include "GrMeshDrawOp.h"
     12 #include "GrOpFlushState.h"
     13 #include "GrRectOpFactory.h"
     14 #include "GrSimpleMeshDrawOpHelper.h"
     15 #include "SkRandom.h"
     16 #include "SkStrokeRec.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 
     26     verts[0].set(rect.fLeft + rad, rect.fTop + rad);
     27     verts[1].set(rect.fLeft - rad, rect.fTop - rad);
     28     verts[2].set(rect.fRight - rad, rect.fTop + rad);
     29     verts[3].set(rect.fRight + rad, rect.fTop - rad);
     30     verts[4].set(rect.fRight - rad, rect.fBottom - rad);
     31     verts[5].set(rect.fRight + rad, rect.fBottom + rad);
     32     verts[6].set(rect.fLeft + rad, rect.fBottom - rad);
     33     verts[7].set(rect.fLeft - rad, rect.fBottom + rad);
     34     verts[8] = verts[0];
     35     verts[9] = verts[1];
     36 
     37     // TODO: we should be catching this higher up the call stack and just draw a single
     38     // non-AA rect
     39     if (2*rad >= rect.width()) {
     40         verts[0].fX = verts[2].fX = verts[4].fX = verts[6].fX = verts[8].fX = rect.centerX();
     41     }
     42     if (2*rad >= rect.height()) {
     43         verts[0].fY = verts[2].fY = verts[4].fY = verts[6].fY = verts[8].fY = rect.centerY();
     44     }
     45 }
     46 
     47 // Allow all hairlines and all miters, so long as the miter limit doesn't produce beveled corners.
     48 inline static bool allowed_stroke(const SkStrokeRec& stroke) {
     49     SkASSERT(stroke.getStyle() == SkStrokeRec::kStroke_Style ||
     50              stroke.getStyle() == SkStrokeRec::kHairline_Style);
     51     return !stroke.getWidth() ||
     52            (stroke.getJoin() == SkPaint::kMiter_Join && stroke.getMiter() > SK_ScalarSqrt2);
     53 }
     54 
     55 namespace {
     56 
     57 class NonAAStrokeRectOp final : public GrMeshDrawOp {
     58 private:
     59     using Helper = GrSimpleMeshDrawOpHelper;
     60 
     61 public:
     62     DEFINE_OP_CLASS_ID
     63 
     64     const char* name() const override { return "NonAAStrokeRectOp"; }
     65 
     66     void visitProxies(const VisitProxyFunc& func) const override {
     67         fHelper.visitProxies(func);
     68     }
     69 
     70     SkString dumpInfo() const override {
     71         SkString string;
     72         string.appendf(
     73                 "Color: 0x%08x, Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
     74                 "StrokeWidth: %.2f\n",
     75                 fColor, fRect.fLeft, fRect.fTop, fRect.fRight, fRect.fBottom, fStrokeWidth);
     76         string += fHelper.dumpInfo();
     77         string += INHERITED::dumpInfo();
     78         return string;
     79     }
     80 
     81     static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
     82                                           const SkRect& rect, const SkStrokeRec& stroke,
     83                                           GrAAType aaType) {
     84         if (!allowed_stroke(stroke)) {
     85             return nullptr;
     86         }
     87         Helper::Flags flags = Helper::Flags::kNone;
     88         // Depending on sub-pixel coordinates and the particular GPU, we may lose a corner of
     89         // hairline rects. We jam all the vertices to pixel centers to avoid this, but not
     90         // when MSAA is enabled because it can cause ugly artifacts.
     91         if (stroke.getStyle() == SkStrokeRec::kHairline_Style && aaType != GrAAType::kMSAA) {
     92             flags |= Helper::Flags::kSnapVerticesToPixelCenters;
     93         }
     94         return Helper::FactoryHelper<NonAAStrokeRectOp>(std::move(paint), flags, viewMatrix, rect,
     95                                                         stroke, aaType);
     96     }
     97 
     98     NonAAStrokeRectOp(const Helper::MakeArgs& helperArgs, GrColor color, Helper::Flags flags,
     99                       const SkMatrix& viewMatrix, const SkRect& rect, const SkStrokeRec& stroke,
    100                       GrAAType aaType)
    101             : INHERITED(ClassID()), fHelper(helperArgs, aaType, flags) {
    102         fColor = color;
    103         fViewMatrix = viewMatrix;
    104         fRect = rect;
    105         // Sort the rect for hairlines
    106         fRect.sort();
    107         fStrokeWidth = stroke.getWidth();
    108 
    109         SkScalar rad = SkScalarHalf(fStrokeWidth);
    110         SkRect bounds = rect;
    111         bounds.outset(rad, rad);
    112 
    113         // If our caller snaps to pixel centers then we have to round out the bounds
    114         if (flags & Helper::Flags::kSnapVerticesToPixelCenters) {
    115             viewMatrix.mapRect(&bounds);
    116             // We want to be consistent with how we snap non-aa lines. To match what we do in
    117             // GrGLSLVertexShaderBuilder, we first floor all the vertex values and then add half a
    118             // pixel to force us to pixel centers.
    119             bounds.set(SkScalarFloorToScalar(bounds.fLeft),
    120                        SkScalarFloorToScalar(bounds.fTop),
    121                        SkScalarFloorToScalar(bounds.fRight),
    122                        SkScalarFloorToScalar(bounds.fBottom));
    123             bounds.offset(0.5f, 0.5f);
    124             this->setBounds(bounds, HasAABloat::kNo, IsZeroArea::kNo);
    125         } else {
    126             this->setTransformedBounds(bounds, fViewMatrix, HasAABloat::kNo, IsZeroArea::kNo);
    127         }
    128     }
    129 
    130     FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
    131 
    132     RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
    133                                 GrPixelConfigIsClamped dstIsClamped) override {
    134         return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
    135                                             GrProcessorAnalysisCoverage::kNone, &fColor);
    136     }
    137 
    138 private:
    139     void onPrepareDraws(Target* target) override {
    140         sk_sp<GrGeometryProcessor> gp;
    141         {
    142             using namespace GrDefaultGeoProcFactory;
    143             Color color(fColor);
    144             LocalCoords::Type localCoordsType = fHelper.usesLocalCoords()
    145                                                         ? LocalCoords::kUsePosition_Type
    146                                                         : LocalCoords::kUnused_Type;
    147             gp = GrDefaultGeoProcFactory::Make(color, Coverage::kSolid_Type, localCoordsType,
    148                                                fViewMatrix);
    149         }
    150 
    151         size_t vertexStride = gp->getVertexStride();
    152 
    153         SkASSERT(vertexStride == sizeof(GrDefaultGeoProcFactory::PositionAttr));
    154 
    155         int vertexCount = kVertsPerHairlineRect;
    156         if (fStrokeWidth > 0) {
    157             vertexCount = kVertsPerStrokeRect;
    158         }
    159 
    160         const GrBuffer* vertexBuffer;
    161         int firstVertex;
    162 
    163         void* verts =
    164                 target->makeVertexSpace(vertexStride, vertexCount, &vertexBuffer, &firstVertex);
    165 
    166         if (!verts) {
    167             SkDebugf("Could not allocate vertices\n");
    168             return;
    169         }
    170 
    171         SkPoint* vertex = reinterpret_cast<SkPoint*>(verts);
    172 
    173         GrPrimitiveType primType;
    174         if (fStrokeWidth > 0) {
    175             primType = GrPrimitiveType::kTriangleStrip;
    176             init_stroke_rect_strip(vertex, fRect, fStrokeWidth);
    177         } else {
    178             // hairline
    179             primType = GrPrimitiveType::kLineStrip;
    180             vertex[0].set(fRect.fLeft, fRect.fTop);
    181             vertex[1].set(fRect.fRight, fRect.fTop);
    182             vertex[2].set(fRect.fRight, fRect.fBottom);
    183             vertex[3].set(fRect.fLeft, fRect.fBottom);
    184             vertex[4].set(fRect.fLeft, fRect.fTop);
    185         }
    186 
    187         GrMesh mesh(primType);
    188         mesh.setNonIndexedNonInstanced(vertexCount);
    189         mesh.setVertexData(vertexBuffer, firstVertex);
    190         target->draw(gp.get(), fHelper.makePipeline(target), mesh);
    191     }
    192 
    193     bool onCombineIfPossible(GrOp* t, const GrCaps&) override {
    194         // NonAA stroke rects cannot combine right now
    195         // TODO make these combinable.
    196         return false;
    197     }
    198 
    199     Helper fHelper;
    200     GrColor fColor;
    201     SkMatrix fViewMatrix;
    202     SkRect fRect;
    203     SkScalar fStrokeWidth;
    204 
    205     const static int kVertsPerHairlineRect = 5;
    206     const static int kVertsPerStrokeRect = 10;
    207 
    208     typedef GrMeshDrawOp INHERITED;
    209 };
    210 
    211 }  // anonymous namespace
    212 
    213 namespace GrRectOpFactory {
    214 std::unique_ptr<GrDrawOp> MakeNonAAStroke(GrPaint&& paint,
    215                                           const SkMatrix& viewMatrix,
    216                                           const SkRect& rect,
    217                                           const SkStrokeRec& stroke,
    218                                           GrAAType aaType) {
    219     return NonAAStrokeRectOp::Make(std::move(paint), viewMatrix, rect, stroke, aaType);
    220 }
    221 }  // namespace GrRectOpFactory
    222 
    223 #if GR_TEST_UTILS
    224 
    225 GR_DRAW_OP_TEST_DEFINE(NonAAStrokeRectOp) {
    226     SkMatrix viewMatrix = GrTest::TestMatrix(random);
    227     SkRect rect = GrTest::TestRect(random);
    228     SkScalar strokeWidth = random->nextBool() ? 0.0f : 2.0f;
    229     SkPaint strokePaint;
    230     strokePaint.setStrokeWidth(strokeWidth);
    231     strokePaint.setStyle(SkPaint::kStroke_Style);
    232     strokePaint.setStrokeJoin(SkPaint::kMiter_Join);
    233     SkStrokeRec strokeRec(strokePaint);
    234     GrAAType aaType = GrAAType::kNone;
    235     if (fsaaType == GrFSAAType::kUnifiedMSAA) {
    236         aaType = random->nextBool() ? GrAAType::kMSAA : GrAAType::kNone;
    237     }
    238     return NonAAStrokeRectOp::Make(std::move(paint), viewMatrix, rect, strokeRec, aaType);
    239 }
    240 
    241 #endif
    242