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