Beispiel #1
0
void SkSVGDevice::AutoElement::addClipResources(const SkDraw& draw, Resources* resources) {
    SkASSERT(!draw.fClipStack->isWideOpen());

    SkPath clipPath;
    (void) draw.fClipStack->asPath(&clipPath);

    SkString clipID = fResourceBucket->addClip();
    const char* clipRule = clipPath.getFillType() == SkPath::kEvenOdd_FillType ?
                           "evenodd" : "nonzero";
    {
        // clipPath is in device space, but since we're only pushing transform attributes
        // to the leaf nodes, so are all our elements => SVG userSpaceOnUse == device space.
        AutoElement clipPathElement("clipPath", fWriter);
        clipPathElement.addAttribute("id", clipID);

        SkRect clipRect = SkRect::MakeEmpty();
        if (clipPath.isEmpty() || clipPath.isRect(&clipRect)) {
            AutoElement rectElement("rect", fWriter);
            rectElement.addRectAttributes(clipRect);
            rectElement.addAttribute("clip-rule", clipRule);
        } else {
            AutoElement pathElement("path", fWriter);
            pathElement.addPathAttributes(clipPath);
            pathElement.addAttribute("clip-rule", clipRule);
        }
    }

    resources->fClip.printf("url(#%s)", clipID.c_str());
}
Beispiel #2
0
sk_sp<SkPathEffect> SkPath1DPathEffect::Make(const SkPath& path, SkScalar advance, SkScalar phase,
        Style style) {
    if (advance <= 0 || path.isEmpty()) {
        return nullptr;
    }
    return sk_sp<SkPathEffect>(new SkPath1DPathEffect(path, advance, phase, style));
}
Beispiel #3
0
 void makePath() {
     if (fPath.isEmpty()) {
         const SkScalar radius = SkIntToScalar(45);
         fPath.addCircle(SkIntToScalar(50), SkIntToScalar(50), radius);
         fPath.addCircle(SkIntToScalar(100), SkIntToScalar(100), radius);
     }
 }
GrGLPath::GrGLPath(GrGpuGL* gpu, const SkPath& path) : INHERITED(gpu, kIsWrapped) {
#ifndef SK_SCALAR_IS_FLOAT
    GrCrash("Assumes scalar is float.");
#endif
    SkASSERT(!path.isEmpty());

    GL_CALL_RET(fPathID, GenPaths(1));

    SkSTArray<16, GrGLubyte, true> pathCommands;
    SkSTArray<16, SkPoint, true> pathPoints;

    int verbCnt = path.countVerbs();
    int pointCnt = path.countPoints();
    pathCommands.resize_back(verbCnt);
    pathPoints.resize_back(pointCnt);

    // TODO: Direct access to path points since we could pass them on directly.
    path.getPoints(&pathPoints[0], pointCnt);
    path.getVerbs(&pathCommands[0], verbCnt);

    GR_DEBUGCODE(int numPts = 0);
    for (int i = 0; i < verbCnt; ++i) {
        SkPath::Verb v = static_cast<SkPath::Verb>(pathCommands[i]);
        pathCommands[i] = verb_to_gl_path_cmd(v);
        GR_DEBUGCODE(numPts += num_pts(v));
    }
    GrAssert(pathPoints.count() == numPts);

    GL_CALL(PathCommands(fPathID,
                         verbCnt, &pathCommands[0],
                         2 * pointCnt, GR_GL_FLOAT, &pathPoints[0]));
    fBounds = path.getBounds();
}
Beispiel #5
0
static void failOne(skiatest::Reporter* reporter, int index) {
    SkPath path;
    int i = (int) (index % nonFinitePtsCount);
    int f = (int) (index % finitePtsCount);
    int g = (int) ((f + 1) % finitePtsCount);
    switch (index % 13) {
        case 0: path.lineTo(nonFinitePts[i]); break;
        case 1: path.quadTo(nonFinitePts[i], nonFinitePts[i]); break;
        case 2: path.quadTo(nonFinitePts[i], finitePts[f]); break;
        case 3: path.quadTo(finitePts[f], nonFinitePts[i]); break;
        case 4: path.cubicTo(nonFinitePts[i], finitePts[f], finitePts[f]); break;
        case 5: path.cubicTo(finitePts[f], nonFinitePts[i], finitePts[f]); break;
        case 6: path.cubicTo(finitePts[f], finitePts[f], nonFinitePts[i]); break;
        case 7: path.cubicTo(nonFinitePts[i], nonFinitePts[i], finitePts[f]); break;
        case 8: path.cubicTo(nonFinitePts[i], finitePts[f], nonFinitePts[i]); break;
        case 9: path.cubicTo(finitePts[f], nonFinitePts[i], nonFinitePts[i]); break;
        case 10: path.cubicTo(nonFinitePts[i], nonFinitePts[i], nonFinitePts[i]); break;
        case 11: path.cubicTo(nonFinitePts[i], finitePts[f], finitePts[g]); break;
        case 12: path.moveTo(nonFinitePts[i]); break;
    }
    SkPath result;
    result.setFillType(SkPath::kWinding_FillType);
    bool success = Simplify(path, &result);
    REPORTER_ASSERT(reporter, !success);
    REPORTER_ASSERT(reporter, result.isEmpty());
    REPORTER_ASSERT(reporter, result.getFillType() == SkPath::kWinding_FillType);
    reporter->bumpTestCount();
}
bool GrAAConvexPathRenderer::onDrawPath(GrDrawTarget* target,
                                        GrPipelineBuilder* pipelineBuilder,
                                        GrColor color,
                                        const SkMatrix& vm,
                                        const SkPath& path,
                                        const SkStrokeRec&,
                                        bool antiAlias) {
    if (path.isEmpty()) {
        return true;
    }

    // We outset our vertices one pixel and add one more pixel for precision.
    // TODO create tighter bounds when we start reordering.
    SkRect devRect = path.getBounds();
    vm.mapRect(&devRect);
    devRect.outset(2, 2);

    AAConvexPathBatch::Geometry geometry;
    geometry.fColor = color;
    geometry.fViewMatrix = vm;
    geometry.fPath = path;

    SkAutoTUnref<GrBatch> batch(AAConvexPathBatch::Create(geometry));
    target->drawBatch(pipelineBuilder, batch, &devRect);

    return true;

}
Beispiel #7
0
SkPath1DPathEffect::SkPath1DPathEffect(const SkPath& path, SkScalar advance,
                                       SkScalar phase, Style style) : fPath(path)
{
    SkASSERT(advance > 0 && !path.isEmpty());
    // cleanup their phase parameter, inverting it so that it becomes an
    // offset along the path (to match the interpretation in PostScript)
    if (phase < 0) {
        phase = -phase;
        if (phase > advance) {
            phase = SkScalarMod(phase, advance);
        }
    } else {
        if (phase > advance) {
            phase = SkScalarMod(phase, advance);
        }
        phase = advance - phase;
    }
    // now catch the edge case where phase == advance (within epsilon)
    if (phase >= advance) {
        phase = 0;
    }
    SkASSERT(phase >= 0);

    fAdvance = advance;
    fInitialOffset = phase;

    if ((unsigned)style > kMorph_Style) {
        SkDEBUGF(("SkPath1DPathEffect style enum out of range %d\n", style));
    }
    fStyle = style;
}
Beispiel #8
0
GrGLPath::GrGLPath(GrGLGpu* gpu, const SkPath& origSkPath, const GrStrokeInfo& origStroke)
    : INHERITED(gpu, origSkPath, origStroke),
      fPathID(gpu->glPathRendering()->genPaths(1)) {

    if (origSkPath.isEmpty()) {
        InitPathObjectEmptyPath(gpu, fPathID);
        fShouldStroke = false;
        fShouldFill = false;
    } else {
        const SkPath* skPath = &origSkPath;
        SkTLazy<SkPath> tmpPath;
        const GrStrokeInfo* stroke = &origStroke;
        GrStrokeInfo tmpStroke(SkStrokeRec::kFill_InitStyle);

        if (stroke->isDashed()) {
            // Skia stroking and NVPR stroking differ with respect to dashing
            // pattern.
            // Convert a dashing to either a stroke or a fill.
            if (stroke->applyDashToPath(tmpPath.init(), &tmpStroke, *skPath)) {
                skPath = tmpPath.get();
                stroke = &tmpStroke;
            }
        }

        bool didInit = false;
        if (stroke->needToApply() && stroke->getCap() != SkPaint::kButt_Cap) {
            // Skia stroking and NVPR stroking differ with respect to stroking
            // end caps of empty subpaths.
            // Convert stroke to fill if path contains empty subpaths.
            didInit = InitPathObjectPathDataCheckingDegenerates(gpu, fPathID, *skPath);
            if (!didInit) {
                if (!tmpPath.isValid()) {
                    tmpPath.init();
                }
                SkAssertResult(stroke->applyToPath(tmpPath.get(), *skPath));
                skPath = tmpPath.get();
                tmpStroke.setFillStyle();
                stroke = &tmpStroke;
            }
        }

        if (!didInit) {
            InitPathObjectPathData(gpu, fPathID, *skPath);
        }

        fShouldStroke = stroke->needToApply();
        fShouldFill = stroke->isFillStyle() ||
                stroke->getStyle() == SkStrokeRec::kStrokeAndFill_Style;

        if (fShouldStroke) {
            InitPathObjectStroke(gpu, fPathID, *stroke);

            // FIXME: try to account for stroking, without rasterizing the stroke.
            fBounds.outset(stroke->getWidth(), stroke->getWidth());
        }
    }

    this->registerWithCache();
}
Beispiel #9
0
bool SkHitTestPath(const SkPath& path, SkRect& target, bool hires) {
    if (target.isEmpty()) {
        return false;
    }

    bool isInverse = path.isInverseFillType();
    if (path.isEmpty()) {
        return isInverse;
    }

    SkRect bounds = path.getBounds();

    bool sects = SkRect::Intersects(target, bounds);
    if (isInverse) {
        if (!sects) {
            return true;
        }
    } else {
        if (!sects) {
            return false;
        }
        if (target.contains(bounds)) {
            return true;
        }
    }

    SkPath devPath;
    const SkPath* pathPtr;
    SkRect        devTarget;

    if (hires) {
        const SkScalar coordLimit = SkIntToScalar(16384);
        const SkRect limit = { 0, 0, coordLimit, coordLimit };
        
        SkMatrix matrix;
        matrix.setRectToRect(bounds, limit, SkMatrix::kFill_ScaleToFit);

        path.transform(matrix, &devPath);
        matrix.mapRect(&devTarget, target);

        pathPtr = &devPath;
    } else {
        devTarget = target;
        pathPtr = &path;
    }

    SkIRect iTarget;
    devTarget.round(&iTarget);
    if (iTarget.isEmpty()) {
        iTarget.fLeft = SkScalarFloorToInt(devTarget.fLeft);
        iTarget.fTop = SkScalarFloorToInt(devTarget.fTop);
        iTarget.fRight = iTarget.fLeft + 1;
        iTarget.fBottom = iTarget.fTop + 1;
    }

    SkRegion clip(iTarget);
    SkRegion rgn;
    return rgn.setPath(*pathPtr, clip) ^ isInverse;
}
static void PathOpsSimplifyFailTest(skiatest::Reporter* reporter) {
    for (int index = 0; index < (int) (13 * nonFinitePtsCount * finitePtsCount); ++index) {
        SkPath path;
        int i = (int) (index % nonFinitePtsCount);
        int f = (int) (index % finitePtsCount);
        int g = (int) ((f + 1) % finitePtsCount);
        switch (index % 13) {
            case 0: path.lineTo(nonFinitePts[i]); break;
            case 1: path.quadTo(nonFinitePts[i], nonFinitePts[i]); break;
            case 2: path.quadTo(nonFinitePts[i], finitePts[f]); break;
            case 3: path.quadTo(finitePts[f], nonFinitePts[i]); break;
            case 4: path.cubicTo(nonFinitePts[i], finitePts[f], finitePts[f]); break;
            case 5: path.cubicTo(finitePts[f], nonFinitePts[i], finitePts[f]); break;
            case 6: path.cubicTo(finitePts[f], finitePts[f], nonFinitePts[i]); break;
            case 7: path.cubicTo(nonFinitePts[i], nonFinitePts[i], finitePts[f]); break;
            case 8: path.cubicTo(nonFinitePts[i], finitePts[f], nonFinitePts[i]); break;
            case 9: path.cubicTo(finitePts[f], nonFinitePts[i], nonFinitePts[i]); break;
            case 10: path.cubicTo(nonFinitePts[i], nonFinitePts[i], nonFinitePts[i]); break;
            case 11: path.cubicTo(nonFinitePts[i], finitePts[f], finitePts[g]); break;
            case 12: path.moveTo(nonFinitePts[i]); break;
        }
        SkPath result;
        result.setFillType(SkPath::kWinding_FillType);
        bool success = Simplify(path, &result);
        REPORTER_ASSERT(reporter, !success);
        REPORTER_ASSERT(reporter, result.isEmpty());
        REPORTER_ASSERT(reporter, result.getFillType() == SkPath::kWinding_FillType);
        reporter->bumpTestCount();
    }
    if (sizeof(reporter) == 4) {
        return;
    }
    for (int index = 0; index < (int) (11 * finitePtsCount); ++index) {
        SkPath path;
        int f = (int) (index % finitePtsCount);
        int g = (int) ((f + 1) % finitePtsCount);
        switch (index % 11) {
            case 0: path.lineTo(finitePts[f]); break;
            case 1: path.quadTo(finitePts[f], finitePts[f]); break;
            case 2: path.quadTo(finitePts[f], finitePts[g]); break;
            case 3: path.quadTo(finitePts[g], finitePts[f]); break;
            case 4: path.cubicTo(finitePts[f], finitePts[f], finitePts[f]); break;
            case 5: path.cubicTo(finitePts[f], finitePts[f], finitePts[g]); break;
            case 6: path.cubicTo(finitePts[f], finitePts[g], finitePts[f]); break;
            case 7: path.cubicTo(finitePts[f], finitePts[g], finitePts[g]); break;
            case 8: path.cubicTo(finitePts[g], finitePts[f], finitePts[f]); break;
            case 9: path.cubicTo(finitePts[g], finitePts[f], finitePts[g]); break;
            case 10: path.moveTo(finitePts[f]); break;
        }
        SkPath result;
        result.setFillType(SkPath::kWinding_FillType);
        bool success = Simplify(path, &result);
        REPORTER_ASSERT(reporter, success);
        REPORTER_ASSERT(reporter, result.getFillType() != SkPath::kWinding_FillType);
        reporter->bumpTestCount();
    }
}
Beispiel #11
0
static void toString(const SkPath& path, SkString* str) {
    if (path.isEmpty()) {
        str->append("path:empty");
    } else {
        toString(path.getBounds(), str);
#if 1
        SkString s;
        dumpVerbs(path, &s);
        str->append(s.c_str());
#endif
        str->append("]");
        str->prepend("path:[");
    }
}
Beispiel #12
0
// http://crbug.com/165432
// Limit extreme dash path effects to avoid exhausting the system memory.
static void test_crbug_165432(skiatest::Reporter* reporter) {
    SkPath path;
    path.moveTo(0, 0);
    path.lineTo(10000000, 0);

    SkScalar intervals[] = { 0.5f, 0.5f };
    SkDashPathEffect dash(intervals, 2, 0);

    SkPaint paint;
    paint.setStyle(SkPaint::kStroke_Style);
    paint.setPathEffect(&dash);

    SkPath filteredPath;
    SkStrokeRec rec(paint);
    REPORTER_ASSERT(reporter, !dash.filterPath(&filteredPath, path, &rec, NULL));
    REPORTER_ASSERT(reporter, filteredPath.isEmpty());
}
Beispiel #13
0
bool SkHitTestPathEx(const SkPath& path, SkScalar x, SkScalar y) {
    bool isInverse = path.isInverseFillType();
    if (path.isEmpty()) {
        return isInverse;
    }
    
    const SkRect& bounds = path.getBounds();
    if (!bounds.contains(x, y)) {
        return isInverse;
    }

    SkPath::Iter iter(path, true);
    bool done = false;
    int w = 0;
    do {
        SkPoint pts[4];
        switch (iter.next(pts, false)) {
            case SkPath::kMove_Verb:
            case SkPath::kClose_Verb:
                break;
            case SkPath::kLine_Verb:
                w += winding_line(pts, x, y);
                break;
            case SkPath::kQuad_Verb:
                w += winding_quad(pts, x, y);
                break;
            case SkPath::kCubic_Verb:
                w += winding_cubic(pts, x, y);
                break;
            case SkPath::kDone_Verb:
                done = true;
                break;
        }
    } while (!done);

    switch (path.getFillType()) {
        case SkPath::kEvenOdd_FillType:
        case SkPath::kInverseEvenOdd_FillType:
            w &= 1;
            break;
        default:
            break;
    }
    return SkToBool(w);
}
Beispiel #14
0
void GrGLPath::InitPathObjectPathData(GrGLGpu* gpu,
                                      GrGLuint pathID,
                                      const SkPath& skPath) {
    SkASSERT(!skPath.isEmpty());

#ifdef SK_SCALAR_IS_FLOAT
    // This branch does type punning, converting SkPoint* to GrGLfloat*.
    if ((skPath.getSegmentMasks() & SkPath::kConic_SegmentMask) == 0) {
        int verbCnt = skPath.countVerbs();
        int pointCnt = skPath.countPoints();
        int coordCnt = pointCnt * 2;
        SkSTArray<16, GrGLubyte, true> pathCommands(verbCnt);
        SkSTArray<16, GrGLfloat, true> pathCoords(coordCnt);

        static_assert(sizeof(SkPoint) == sizeof(GrGLfloat) * 2, "sk_point_not_two_floats");

        pathCommands.resize_back(verbCnt);
        pathCoords.resize_back(coordCnt);
        skPath.getPoints(reinterpret_cast<SkPoint*>(&pathCoords[0]), pointCnt);
        skPath.getVerbs(&pathCommands[0], verbCnt);

        SkDEBUGCODE(int verbCoordCnt = 0);
        for (int i = 0; i < verbCnt; ++i) {
            SkPath::Verb v = static_cast<SkPath::Verb>(pathCommands[i]);
            pathCommands[i] = verb_to_gl_path_cmd(v);
            SkDEBUGCODE(verbCoordCnt += num_coords(v));
        }
        SkASSERT(verbCnt == pathCommands.count());
        SkASSERT(verbCoordCnt == pathCoords.count());
        SkDEBUGCODE(verify_floats(&pathCoords[0], pathCoords.count()));
        GR_GL_CALL(gpu->glInterface(), PathCommands(pathID, pathCommands.count(), &pathCommands[0],
                                                    pathCoords.count(), GR_GL_FLOAT,
                                                    &pathCoords[0]));
        return;
    }
#endif
    SkAssertResult(init_path_object_for_general_path<false>(gpu, pathID, skPath));
}
bool GrAALinearizingConvexPathRenderer::onDrawPath(GrDrawTarget* target,
                                                   GrPipelineBuilder* pipelineBuilder,
                                                   GrColor color,
                                                   const SkMatrix& vm,
                                                   const SkPath& path,
                                                   const GrStrokeInfo& stroke,
                                                   bool antiAlias) {
    if (path.isEmpty()) {
        return true;
    }
    AAFlatteningConvexPathBatch::Geometry geometry;
    geometry.fColor = color;
    geometry.fViewMatrix = vm;
    geometry.fPath = path;
    geometry.fStrokeWidth = stroke.isFillStyle() ? -1.0f : stroke.getWidth();
    geometry.fJoin = stroke.isFillStyle() ? SkPaint::Join::kMiter_Join : stroke.getJoin();
    geometry.fMiterLimit = stroke.getMiter();

    SkAutoTUnref<GrBatch> batch(AAFlatteningConvexPathBatch::Create(geometry));
    target->drawBatch(pipelineBuilder, batch);

    return true;
}
Beispiel #16
0
DEF_TEST(PathOpsBuilder, reporter) {
    SkOpBuilder builder;
    SkPath result;
    REPORTER_ASSERT(reporter, builder.resolve(&result));
    REPORTER_ASSERT(reporter, result.isEmpty());

    builder.add(result, kDifference_SkPathOp);
    REPORTER_ASSERT(reporter, builder.resolve(&result));
    REPORTER_ASSERT(reporter, result.isEmpty());

    builder.add(result, kUnion_SkPathOp);
    REPORTER_ASSERT(reporter, builder.resolve(&result));
    REPORTER_ASSERT(reporter, result.isEmpty());

    SkPath rectPath;
    rectPath.setFillType(SkPath::kEvenOdd_FillType);
    rectPath.addRect(0, 1, 2, 3, SkPath::kCW_Direction);
    builder.add(rectPath, kUnion_SkPathOp);
    REPORTER_ASSERT(reporter, builder.resolve(&result));
    bool closed;
    SkPath::Direction dir;
    REPORTER_ASSERT(reporter, result.isRect(nullptr, &closed, &dir));
    REPORTER_ASSERT(reporter, closed);
    REPORTER_ASSERT(reporter, dir == SkPath::kCCW_Direction);
    int pixelDiff = comparePaths(reporter, __FUNCTION__, rectPath, result);
    REPORTER_ASSERT(reporter, pixelDiff == 0);

    rectPath.reset();
    rectPath.setFillType(SkPath::kEvenOdd_FillType);
    rectPath.addRect(0, 1, 2, 3, SkPath::kCCW_Direction);
    builder.add(rectPath, kUnion_SkPathOp);
    REPORTER_ASSERT(reporter, builder.resolve(&result));
    REPORTER_ASSERT(reporter, result.isRect(nullptr, &closed, &dir));
    REPORTER_ASSERT(reporter, closed);
    REPORTER_ASSERT(reporter, dir == SkPath::kCCW_Direction);
    REPORTER_ASSERT(reporter, rectPath == result);

    builder.add(rectPath, kDifference_SkPathOp);
    REPORTER_ASSERT(reporter, builder.resolve(&result));
    REPORTER_ASSERT(reporter, result.isEmpty());

    SkPath rect2, rect3;
    rect2.addRect(2, 1, 4, 3, SkPath::kCW_Direction);
    rect3.addRect(4, 1, 5, 3, SkPath::kCCW_Direction);
    builder.add(rectPath, kUnion_SkPathOp);
    builder.add(rect2, kUnion_SkPathOp);
    builder.add(rect3, kUnion_SkPathOp);
    REPORTER_ASSERT(reporter, builder.resolve(&result));
    REPORTER_ASSERT(reporter, result.isRect(nullptr, &closed, &dir));
    REPORTER_ASSERT(reporter, closed);
    SkRect expected;
    expected.set(0, 1, 5, 3);
    REPORTER_ASSERT(reporter, result.getBounds() == expected);

    SkPath circle1, circle2, circle3;
    circle1.addCircle(5, 6, 4, SkPath::kCW_Direction);
    circle2.addCircle(7, 4, 8, SkPath::kCCW_Direction);
    circle3.addCircle(6, 5, 6, SkPath::kCW_Direction);
    SkPath opCompare;
    Op(circle1, circle2, kUnion_SkPathOp, &opCompare);
    Op(opCompare, circle3, kDifference_SkPathOp, &opCompare);
    builder.add(circle1, kUnion_SkPathOp);
    builder.add(circle2, kUnion_SkPathOp);
    builder.add(circle3, kDifference_SkPathOp);
    REPORTER_ASSERT(reporter, builder.resolve(&result));
    pixelDiff = comparePaths(reporter, __FUNCTION__, opCompare, result);
    REPORTER_ASSERT(reporter, pixelDiff == 0);
}
////////////////////////////////////////////////////////////////////////////////
// Create a 1-bit clip mask in the stencil buffer. 'devClipBounds' are in device
// (as opposed to canvas) coordinates
bool GrClipMaskManager::createStencilClipMask(int32_t elementsGenID,
                                              InitialState initialState,
                                              const ElementList& elements,
                                              const SkIRect& clipSpaceIBounds,
                                              const SkIPoint& clipSpaceToStencilOffset) {

    SkASSERT(kNone_ClipMaskType == fCurrClipMaskType);

    GrDrawState* drawState = fGpu->drawState();
    SkASSERT(drawState->isClipState());

    GrRenderTarget* rt = drawState->getRenderTarget();
    SkASSERT(NULL != rt);

    // TODO: dynamically attach a SB when needed.
    GrStencilBuffer* stencilBuffer = rt->getStencilBuffer();
    if (NULL == stencilBuffer) {
        return false;
    }

    if (stencilBuffer->mustRenderClip(elementsGenID, clipSpaceIBounds, clipSpaceToStencilOffset)) {

        stencilBuffer->setLastClip(elementsGenID, clipSpaceIBounds, clipSpaceToStencilOffset);

        // Set the matrix so that rendered clip elements are transformed from clip to stencil space.
        SkVector translate = {
            SkIntToScalar(clipSpaceToStencilOffset.fX),
            SkIntToScalar(clipSpaceToStencilOffset.fY)
        };
        SkMatrix matrix;
        matrix.setTranslate(translate);
        GrDrawTarget::AutoGeometryAndStatePush agasp(fGpu, GrDrawTarget::kReset_ASRInit, &matrix);
        drawState = fGpu->drawState();

        drawState->setRenderTarget(rt);

        // We set the current clip to the bounds so that our recursive draws are scissored to them.
        SkIRect stencilSpaceIBounds(clipSpaceIBounds);
        stencilSpaceIBounds.offset(clipSpaceToStencilOffset);
        GrDrawTarget::AutoClipRestore acr(fGpu, stencilSpaceIBounds);
        drawState->enableState(GrDrawState::kClip_StateBit);

#if !VISUALIZE_COMPLEX_CLIP
        drawState->enableState(GrDrawState::kNoColorWrites_StateBit);
#endif

        int clipBit = stencilBuffer->bits();
        SkASSERT((clipBit <= 16) && "Ganesh only handles 16b or smaller stencil buffers");
        clipBit = (1 << (clipBit-1));

        fGpu->clearStencilClip(stencilSpaceIBounds, kAllIn_InitialState == initialState);

        // walk through each clip element and perform its set op
        // with the existing clip.
        for (ElementList::Iter iter(elements.headIter()); NULL != iter.get(); iter.next()) {
            const Element* element = iter.get();
            bool fillInverted = false;
            // enabled at bottom of loop
            drawState->disableState(GrGpu::kModifyStencilClip_StateBit);
            // if the target is MSAA then we want MSAA enabled when the clip is soft
            if (rt->isMultisampled()) {
                drawState->setState(GrDrawState::kHWAntialias_StateBit, element->isAA());
            }

            // This will be used to determine whether the clip shape can be rendered into the
            // stencil with arbitrary stencil settings.
            GrPathRenderer::StencilSupport stencilSupport;

            SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);

            SkRegion::Op op = element->getOp();

            GrPathRenderer* pr = NULL;
            SkPath clipPath;
            if (Element::kRect_Type == element->getType()) {
                stencilSupport = GrPathRenderer::kNoRestriction_StencilSupport;
                fillInverted = false;
            } else {
                element->asPath(&clipPath);
                fillInverted = clipPath.isInverseFillType();
                if (fillInverted) {
                    clipPath.toggleInverseFillType();
                }
                pr = this->getContext()->getPathRenderer(clipPath,
                                                         stroke,
                                                         fGpu,
                                                         false,
                                                         GrPathRendererChain::kStencilOnly_DrawType,
                                                         &stencilSupport);
                if (NULL == pr) {
                    return false;
                }
            }

            int passes;
            GrStencilSettings stencilSettings[GrStencilSettings::kMaxStencilClipPasses];

            bool canRenderDirectToStencil =
                GrPathRenderer::kNoRestriction_StencilSupport == stencilSupport;
            bool canDrawDirectToClip; // Given the renderer, the element,
                                      // fill rule, and set operation can
                                      // we render the element directly to
                                      // stencil bit used for clipping.
            canDrawDirectToClip = GrStencilSettings::GetClipPasses(op,
                                                                   canRenderDirectToStencil,
                                                                   clipBit,
                                                                   fillInverted,
                                                                   &passes,
                                                                   stencilSettings);

            // draw the element to the client stencil bits if necessary
            if (!canDrawDirectToClip) {
                GR_STATIC_CONST_SAME_STENCIL(gDrawToStencil,
                                             kIncClamp_StencilOp,
                                             kIncClamp_StencilOp,
                                             kAlways_StencilFunc,
                                             0xffff,
                                             0x0000,
                                             0xffff);
                SET_RANDOM_COLOR
                if (Element::kRect_Type == element->getType()) {
                    *drawState->stencil() = gDrawToStencil;
                    fGpu->drawSimpleRect(element->getRect(), NULL);
                } else {
                    if (!clipPath.isEmpty()) {
                        if (canRenderDirectToStencil) {
                            *drawState->stencil() = gDrawToStencil;
                            pr->drawPath(clipPath, stroke, fGpu, false);
                        } else {
                            pr->stencilPath(clipPath, stroke, fGpu);
                        }
                    }
                }
            }

            // now we modify the clip bit by rendering either the clip
            // element directly or a bounding rect of the entire clip.
            drawState->enableState(GrGpu::kModifyStencilClip_StateBit);
            for (int p = 0; p < passes; ++p) {
                *drawState->stencil() = stencilSettings[p];
                if (canDrawDirectToClip) {
                    if (Element::kRect_Type == element->getType()) {
                        SET_RANDOM_COLOR
                        fGpu->drawSimpleRect(element->getRect(), NULL);
                    } else {
                        SET_RANDOM_COLOR
                        pr->drawPath(clipPath, stroke, fGpu, false);
                    }
                } else {
                    SET_RANDOM_COLOR
                    // The view matrix is setup to do clip space -> stencil space translation, so
                    // draw rect in clip space.
                    fGpu->drawSimpleRect(SkRect::Make(clipSpaceIBounds), NULL);
                }
            }
        }
Beispiel #18
0
void GrCCFiller::parseDeviceSpaceFill(const SkPath& path, const SkPoint* deviceSpacePts,
                                      GrScissorTest scissorTest, const SkIRect& clippedDevIBounds,
                                      const SkIVector& devToAtlasOffset) {
    SkASSERT(!fInstanceBuffer);  // Can't call after prepareToDraw().
    SkASSERT(!path.isEmpty());

    int currPathPointsIdx = fGeometry.points().count();
    int currPathVerbsIdx = fGeometry.verbs().count();
    PrimitiveTallies currPathPrimitiveCounts = PrimitiveTallies();

    fGeometry.beginPath();

    const float* conicWeights = SkPathPriv::ConicWeightData(path);
    int ptsIdx = 0;
    int conicWeightsIdx = 0;
    bool insideContour = false;

    for (SkPath::Verb verb : SkPathPriv::Verbs(path)) {
        switch (verb) {
            case SkPath::kMove_Verb:
                if (insideContour) {
                    currPathPrimitiveCounts += fGeometry.endContour();
                }
                fGeometry.beginContour(deviceSpacePts[ptsIdx]);
                ++ptsIdx;
                insideContour = true;
                continue;
            case SkPath::kClose_Verb:
                if (insideContour) {
                    currPathPrimitiveCounts += fGeometry.endContour();
                }
                insideContour = false;
                continue;
            case SkPath::kLine_Verb:
                fGeometry.lineTo(&deviceSpacePts[ptsIdx - 1]);
                ++ptsIdx;
                continue;
            case SkPath::kQuad_Verb:
                fGeometry.quadraticTo(&deviceSpacePts[ptsIdx - 1]);
                ptsIdx += 2;
                continue;
            case SkPath::kCubic_Verb:
                fGeometry.cubicTo(&deviceSpacePts[ptsIdx - 1]);
                ptsIdx += 3;
                continue;
            case SkPath::kConic_Verb:
                fGeometry.conicTo(&deviceSpacePts[ptsIdx - 1], conicWeights[conicWeightsIdx]);
                ptsIdx += 2;
                ++conicWeightsIdx;
                continue;
            default:
                SK_ABORT("Unexpected path verb.");
        }
    }
    SkASSERT(ptsIdx == path.countPoints());
    SkASSERT(conicWeightsIdx == SkPathPriv::ConicWeightCnt(path));

    if (insideContour) {
        currPathPrimitiveCounts += fGeometry.endContour();
    }

    fPathInfos.emplace_back(scissorTest, devToAtlasOffset);

    // Tessellate fans from very large and/or simple paths, in order to reduce overdraw.
    int numVerbs = fGeometry.verbs().count() - currPathVerbsIdx - 1;
    int64_t tessellationWork = (int64_t)numVerbs * (32 - SkCLZ(numVerbs)); // N log N.
    int64_t fanningWork = (int64_t)clippedDevIBounds.height() * clippedDevIBounds.width();
    if (tessellationWork * (50*50) + (100*100) < fanningWork) { // Don't tessellate under 100x100.
        fPathInfos.back().tessellateFan(fGeometry, currPathVerbsIdx, currPathPointsIdx,
                                        clippedDevIBounds, &currPathPrimitiveCounts);
    }

    fTotalPrimitiveCounts[(int)scissorTest] += currPathPrimitiveCounts;

    if (GrScissorTest::kEnabled == scissorTest) {
        fScissorSubBatches.push_back() = {fTotalPrimitiveCounts[(int)GrScissorTest::kEnabled],
                                          clippedDevIBounds.makeOffset(devToAtlasOffset.fX,
                                                                       devToAtlasOffset.fY)};
    }
}
Beispiel #19
0
void GrGLPath::InitPathObject(GrGLGpu* gpu,
                              GrGLuint pathID,
                              const SkPath& skPath,
                              const GrStrokeInfo& stroke) {
    SkASSERT(!stroke.isDashed());
    if (!skPath.isEmpty()) {
        int verbCnt = skPath.countVerbs();
        int pointCnt = skPath.countPoints();
        int minCoordCnt = pointCnt * 2;

        SkSTArray<16, GrGLubyte, true> pathCommands(verbCnt);
        SkSTArray<16, GrGLfloat, true> pathCoords(minCoordCnt);

        SkDEBUGCODE(int numCoords = 0);

        if ((skPath.getSegmentMasks() & SkPath::kConic_SegmentMask) == 0) {
            // This branch does type punning, converting SkPoint* to GrGLfloat*.
            SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(GrGLfloat) * 2, sk_point_not_two_floats);
            // This branch does not convert with SkScalarToFloat.
#ifndef SK_SCALAR_IS_FLOAT
#error Need SK_SCALAR_IS_FLOAT.
#endif
            pathCommands.resize_back(verbCnt);
            pathCoords.resize_back(minCoordCnt);
            skPath.getPoints(reinterpret_cast<SkPoint*>(&pathCoords[0]), pointCnt);
            skPath.getVerbs(&pathCommands[0], verbCnt);
            for (int i = 0; i < verbCnt; ++i) {
                SkPath::Verb v = static_cast<SkPath::Verb>(pathCommands[i]);
                pathCommands[i] = verb_to_gl_path_cmd(v);
                SkDEBUGCODE(numCoords += num_coords(v));
            }
        } else {
            SkPoint points[4];
            SkPath::RawIter iter(skPath);
            SkPath::Verb verb;
            while ((verb = iter.next(points)) != SkPath::kDone_Verb) {
                pathCommands.push_back(verb_to_gl_path_cmd(verb));
                GrGLfloat coords[6];
                int coordsForVerb;
                switch (verb) {
                    case SkPath::kMove_Verb:
                        points_to_coords(points, 0, 1, coords);
                        coordsForVerb = 2;
                        break;
                    case SkPath::kLine_Verb:
                        points_to_coords(points, 1, 1, coords);
                        coordsForVerb = 2;
                        break;
                    case SkPath::kConic_Verb:
                        points_to_coords(points, 1, 2, coords);
                        coords[4] = SkScalarToFloat(iter.conicWeight());
                        coordsForVerb = 5;
                        break;
                    case SkPath::kQuad_Verb:
                        points_to_coords(points, 1, 2, coords);
                        coordsForVerb = 4;
                        break;
                    case SkPath::kCubic_Verb:
                        points_to_coords(points, 1, 3, coords);
                        coordsForVerb = 6;
                        break;
                    case SkPath::kClose_Verb:
                        continue;
                    default:
                        SkASSERT(false);  // Not reached.
                        continue;
                }
                SkDEBUGCODE(numCoords += num_coords(verb));
                pathCoords.push_back_n(coordsForVerb, coords);
            }
        }

        SkASSERT(verbCnt == pathCommands.count());
        SkASSERT(numCoords == pathCoords.count());

        GR_GL_CALL(gpu->glInterface(), PathCommands(pathID, pathCommands.count(), &pathCommands[0],
                   pathCoords.count(), GR_GL_FLOAT, &pathCoords[0]));
    } else {
        GR_GL_CALL(gpu->glInterface(), PathCommands(pathID, 0, NULL, 0, GR_GL_FLOAT, NULL));
    }

    if (stroke.needToApply()) {
        SkASSERT(!stroke.isHairlineStyle());
        GR_GL_CALL(gpu->glInterface(),
            PathParameterf(pathID, GR_GL_PATH_STROKE_WIDTH, SkScalarToFloat(stroke.getWidth())));
        GR_GL_CALL(gpu->glInterface(),
            PathParameterf(pathID, GR_GL_PATH_MITER_LIMIT, SkScalarToFloat(stroke.getMiter())));
        GrGLenum join = join_to_gl_join(stroke.getJoin());
        GR_GL_CALL(gpu->glInterface(), PathParameteri(pathID, GR_GL_PATH_JOIN_STYLE, join));
        GrGLenum cap = cap_to_gl_cap(stroke.getCap());
        GR_GL_CALL(gpu->glInterface(), PathParameteri(pathID, GR_GL_PATH_END_CAPS, cap));
        GR_GL_CALL(gpu->glInterface(), PathParameterf(pathID, GR_GL_PATH_STROKE_BOUND, 0.02f));
    }
}
static void test_zero_length_paths(skiatest::Reporter* reporter) {
    SkPath p;
    SkRect bounds;

    // Lone moveTo case
    p.moveTo(SK_Scalar1, SK_Scalar1);
    bounds.set(0, 0, 0, 0);
    REPORTER_ASSERT(reporter, !p.isEmpty());
    REPORTER_ASSERT(reporter, 1 == p.countPoints());
    REPORTER_ASSERT(reporter, bounds == p.getBounds());

    // MoveTo-MoveTo case
    p.moveTo(SK_Scalar1*2, SK_Scalar1);
    bounds.set(SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1);
    REPORTER_ASSERT(reporter, !p.isEmpty());
    REPORTER_ASSERT(reporter, 2 == p.countPoints());
    REPORTER_ASSERT(reporter, bounds == p.getBounds()); 

    // moveTo-close case
    p.reset();
    p.moveTo(SK_Scalar1, SK_Scalar1);
    p.close();
    bounds.set(0, 0, 0, 0);
    REPORTER_ASSERT(reporter, !p.isEmpty());
    REPORTER_ASSERT(reporter, 1 == p.countPoints());
    REPORTER_ASSERT(reporter, bounds == p.getBounds());

    // moveTo-close-moveTo-close case
    p.moveTo(SK_Scalar1*2, SK_Scalar1);
    p.close();
    bounds.set(SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1);
    REPORTER_ASSERT(reporter, !p.isEmpty());
    REPORTER_ASSERT(reporter, 2 == p.countPoints());
    REPORTER_ASSERT(reporter, bounds == p.getBounds());

    // moveTo-line case
    p.reset();
    p.moveTo(SK_Scalar1, SK_Scalar1);
    p.lineTo(SK_Scalar1, SK_Scalar1);
    bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
    REPORTER_ASSERT(reporter, !p.isEmpty());
    REPORTER_ASSERT(reporter, 2 == p.countPoints());
    REPORTER_ASSERT(reporter, bounds == p.getBounds());

    // moveTo-lineTo-moveTo-lineTo case
    p.moveTo(SK_Scalar1*2, SK_Scalar1);
    p.lineTo(SK_Scalar1*2, SK_Scalar1);
    bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
    REPORTER_ASSERT(reporter, !p.isEmpty());
    REPORTER_ASSERT(reporter, 4 == p.countPoints());
    REPORTER_ASSERT(reporter, bounds == p.getBounds());

    // moveTo-line-close case
    p.reset();
    p.moveTo(SK_Scalar1, SK_Scalar1);
    p.lineTo(SK_Scalar1, SK_Scalar1);
    p.close();
    bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
    REPORTER_ASSERT(reporter, !p.isEmpty());
    REPORTER_ASSERT(reporter, 2 == p.countPoints());
    REPORTER_ASSERT(reporter, bounds == p.getBounds());

    // moveTo-line-close-moveTo-line-close case
    p.moveTo(SK_Scalar1*2, SK_Scalar1);
    p.lineTo(SK_Scalar1*2, SK_Scalar1);
    p.close();
    bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
    REPORTER_ASSERT(reporter, !p.isEmpty());
    REPORTER_ASSERT(reporter, 4 == p.countPoints());
    REPORTER_ASSERT(reporter, bounds == p.getBounds());

    // moveTo-quadTo case
    p.reset();
    p.moveTo(SK_Scalar1, SK_Scalar1);
    p.quadTo(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
    bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
    REPORTER_ASSERT(reporter, !p.isEmpty());
    REPORTER_ASSERT(reporter, 3 == p.countPoints());
    REPORTER_ASSERT(reporter, bounds == p.getBounds());

    // moveTo-quadTo-close case
    p.close();
    REPORTER_ASSERT(reporter, !p.isEmpty());
    REPORTER_ASSERT(reporter, 3 == p.countPoints());
    REPORTER_ASSERT(reporter, bounds == p.getBounds());

    // moveTo-quadTo-moveTo-quadTo case
    p.reset();
    p.moveTo(SK_Scalar1, SK_Scalar1);
    p.quadTo(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
    p.moveTo(SK_Scalar1*2, SK_Scalar1);
    p.quadTo(SK_Scalar1*2, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
    bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
    REPORTER_ASSERT(reporter, !p.isEmpty());
    REPORTER_ASSERT(reporter, 6 == p.countPoints());
    REPORTER_ASSERT(reporter, bounds == p.getBounds());

    // moveTo-cubicTo case
    p.reset();
    p.moveTo(SK_Scalar1, SK_Scalar1);
    p.cubicTo(SK_Scalar1, SK_Scalar1,
              SK_Scalar1, SK_Scalar1,
              SK_Scalar1, SK_Scalar1);
    bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
    REPORTER_ASSERT(reporter, !p.isEmpty());
    REPORTER_ASSERT(reporter, 4 == p.countPoints());
    REPORTER_ASSERT(reporter, bounds == p.getBounds());

    // moveTo-quadTo-close case
    p.close();
    REPORTER_ASSERT(reporter, !p.isEmpty());
    REPORTER_ASSERT(reporter, 4 == p.countPoints());
    REPORTER_ASSERT(reporter, bounds == p.getBounds());

    // moveTo-quadTo-moveTo-quadTo case
    p.reset();
    p.moveTo(SK_Scalar1, SK_Scalar1);
    p.cubicTo(SK_Scalar1, SK_Scalar1,
              SK_Scalar1, SK_Scalar1,
              SK_Scalar1, SK_Scalar1);
    p.moveTo(SK_Scalar1*2, SK_Scalar1);
    p.cubicTo(SK_Scalar1*2, SK_Scalar1,
              SK_Scalar1*2, SK_Scalar1,
              SK_Scalar1*2, SK_Scalar1);
    bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
    REPORTER_ASSERT(reporter, !p.isEmpty());
    REPORTER_ASSERT(reporter, 8 == p.countPoints());
    REPORTER_ASSERT(reporter, bounds == p.getBounds());
}
Beispiel #21
0
////////////////////////////////////////////////////////////////////////////////
// Create a 1-bit clip mask in the stencil buffer. 'devClipBounds' are in device
// (as opposed to canvas) coordinates
bool GrClipMaskManager::CreateStencilClipMask(GrContext* context,
                                              GrDrawContext* drawContext,
                                              int32_t elementsGenID,
                                              GrReducedClip::InitialState initialState,
                                              const GrReducedClip::ElementList& elements,
                                              const SkIRect& clipSpaceIBounds,
                                              const SkIPoint& clipSpaceToStencilOffset) {
    SkASSERT(drawContext);

    GrStencilAttachment* stencilAttachment = context->resourceProvider()->attachStencilAttachment(
                                                    drawContext->accessRenderTarget());
    if (nullptr == stencilAttachment) {
        return false;
    }

    // TODO: these need to be swapped over to using a StencilAttachmentProxy
    if (stencilAttachment->mustRenderClip(elementsGenID, clipSpaceIBounds, clipSpaceToStencilOffset)) {
        stencilAttachment->setLastClip(elementsGenID, clipSpaceIBounds, clipSpaceToStencilOffset);
        // Set the matrix so that rendered clip elements are transformed from clip to stencil space.
        SkVector translate = {
            SkIntToScalar(clipSpaceToStencilOffset.fX),
            SkIntToScalar(clipSpaceToStencilOffset.fY)
        };
        SkMatrix viewMatrix;
        viewMatrix.setTranslate(translate);

        // We set the current clip to the bounds so that our recursive draws are scissored to them.
        SkIRect stencilSpaceIBounds(clipSpaceIBounds);
        stencilSpaceIBounds.offset(clipSpaceToStencilOffset);
        GrFixedClip clip(stencilSpaceIBounds);

        drawContext->drawContextPriv().clearStencilClip(
                                            stencilSpaceIBounds,
                                            GrReducedClip::kAllIn_InitialState == initialState);

        // walk through each clip element and perform its set op
        // with the existing clip.
        for (GrReducedClip::ElementList::Iter iter(elements.headIter()); iter.get(); iter.next()) {
            const Element* element = iter.get();
            bool useHWAA = element->isAA() && drawContext->isStencilBufferMultisampled();

            bool fillInverted = false;
            // enabled at bottom of loop
            clip.enableStencilClip(false);

            // This will be used to determine whether the clip shape can be rendered into the
            // stencil with arbitrary stencil settings.
            GrPathRenderer::StencilSupport stencilSupport;

            SkRegion::Op op = element->getOp();

            GrPathRenderer* pr = nullptr;
            SkPath clipPath;
            if (Element::kRect_Type == element->getType()) {
                stencilSupport = GrPathRenderer::kNoRestriction_StencilSupport;
                fillInverted = false;
            } else {
                element->asPath(&clipPath);
                fillInverted = clipPath.isInverseFillType();
                if (fillInverted) {
                    clipPath.toggleInverseFillType();
                }

                GrShape shape(clipPath, GrStyle::SimpleFill());
                GrPathRenderer::CanDrawPathArgs canDrawArgs;
                canDrawArgs.fShaderCaps = context->caps()->shaderCaps();
                canDrawArgs.fViewMatrix = &viewMatrix;
                canDrawArgs.fShape = &shape;
                canDrawArgs.fAntiAlias = false;
                canDrawArgs.fHasUserStencilSettings = false;
                canDrawArgs.fIsStencilBufferMSAA = drawContext->isStencilBufferMultisampled();

                pr = context->drawingManager()->getPathRenderer(canDrawArgs, false,
                                                                GrPathRendererChain::kStencilOnly_DrawType,
                                                                &stencilSupport);
                if (!pr) {
                    return false;
                }
            }

            bool canRenderDirectToStencil =
                GrPathRenderer::kNoRestriction_StencilSupport == stencilSupport;
            bool drawDirectToClip; // Given the renderer, the element,
                                   // fill rule, and set operation should
                                   // we render the element directly to
                                   // stencil bit used for clipping.
            GrUserStencilSettings const* const* stencilPasses =
                GrStencilSettings::GetClipPasses(op, canRenderDirectToStencil, fillInverted,
                                                 &drawDirectToClip);

            // draw the element to the client stencil bits if necessary
            if (!drawDirectToClip) {
                static constexpr GrUserStencilSettings kDrawToStencil(
                     GrUserStencilSettings::StaticInit<
                         0x0000,
                         GrUserStencilTest::kAlways,
                         0xffff,
                         GrUserStencilOp::kIncMaybeClamp,
                         GrUserStencilOp::kIncMaybeClamp,
                         0xffff>()
                );
                if (Element::kRect_Type == element->getType()) {
                    drawContext->drawContextPriv().stencilRect(clip, &kDrawToStencil, useHWAA,
                                                               viewMatrix, element->getRect());
                } else {
                    if (!clipPath.isEmpty()) {
                        GrShape shape(clipPath, GrStyle::SimpleFill());
                        if (canRenderDirectToStencil) {
                            GrPaint paint;
                            paint.setXPFactory(GrDisableColorXPFactory::Make());
                            paint.setAntiAlias(element->isAA());

                            GrPathRenderer::DrawPathArgs args;
                            args.fResourceProvider = context->resourceProvider();
                            args.fPaint = &paint;
                            args.fUserStencilSettings = &kDrawToStencil;
                            args.fDrawContext = drawContext;
                            args.fClip = &clip;
                            args.fColor = GrColor_WHITE;
                            args.fViewMatrix = &viewMatrix;
                            args.fShape = &shape;
                            args.fAntiAlias = false;
                            args.fGammaCorrect = false;
                            pr->drawPath(args);
                        } else {
                            GrPathRenderer::StencilPathArgs args;
                            args.fResourceProvider = context->resourceProvider();
                            args.fDrawContext = drawContext;
                            args.fClip = &clip;
                            args.fViewMatrix = &viewMatrix;
                            args.fIsAA = element->isAA();
                            args.fShape = &shape;
                            pr->stencilPath(args);
                        }
                    }
                }
            }

            // now we modify the clip bit by rendering either the clip
            // element directly or a bounding rect of the entire clip.
            clip.enableStencilClip(true);
            for (GrUserStencilSettings const* const* pass = stencilPasses; *pass; ++pass) {

                if (drawDirectToClip) {
                    if (Element::kRect_Type == element->getType()) {
                        drawContext->drawContextPriv().stencilRect(clip, *pass, useHWAA, viewMatrix,
                                                                   element->getRect());
                    } else {
                        GrShape shape(clipPath, GrStyle::SimpleFill());
                        GrPaint paint;
                        paint.setXPFactory(GrDisableColorXPFactory::Make());
                        paint.setAntiAlias(element->isAA());
                        GrPathRenderer::DrawPathArgs args;
                        args.fResourceProvider = context->resourceProvider();
                        args.fPaint = &paint;
                        args.fUserStencilSettings = *pass;
                        args.fDrawContext = drawContext;
                        args.fClip = &clip;
                        args.fColor = GrColor_WHITE;
                        args.fViewMatrix = &viewMatrix;
                        args.fShape = &shape;
                        args.fAntiAlias = false;
                        args.fGammaCorrect = false;
                        pr->drawPath(args);
                    }
                } else {
                    // The view matrix is setup to do clip space -> stencil space translation, so
                    // draw rect in clip space.
                    drawContext->drawContextPriv().stencilRect(clip, *pass, false, viewMatrix,
                                                               SkRect::Make(clipSpaceIBounds));
                }
            }
        }
    }
    return true;
}
void draw(SkCanvas* canvas) {
    SkPath path;
    SkDebugf("path is " "%s" "empty", path.isEmpty() ? "" : "not ");
}
Beispiel #23
0
    void draw_stroke(SkCanvas* canvas, const SkPath& path, SkScalar width, SkScalar scale,
            bool drawText) {
        if (path.isEmpty()) {
            return;
        }
        SkRect bounds = path.getBounds();
        this->setWHZ(SkScalarCeilToInt(bounds.right()), drawText 
                ? SkScalarRoundToInt(scale * 3 / 2) : SkScalarRoundToInt(scale),
                SkScalarRoundToInt(950.0f / scale));
        erase(fMinSurface);
        SkPaint paint;
        paint.setColor(0x1f1f0f0f);
        paint.setStyle(SkPaint::kStroke_Style);
        paint.setStrokeWidth(width * scale * scale);
        paint.setColor(0x3f0f1f3f);
        if (drawText) {
            fMinSurface->getCanvas()->drawPath(path, paint);
            this->copyMinToMax();
            fMaxSurface->draw(canvas, 0, 0, NULL);
        }
        paint.setAntiAlias(true);
        paint.setStyle(SkPaint::kStroke_Style);
        paint.setStrokeWidth(1);

        paint.setColor(SKELETON_COLOR);
        SkPath scaled;
        SkMatrix matrix;
        matrix.reset();
        matrix.setScale(950 / scale, 950 / scale);
        if (drawText) {
            path.transform(matrix, &scaled);
        } else {
            scaled = path;
        }
        canvas->drawPath(scaled, paint);
        draw_points(canvas, scaled, SKELETON_COLOR, true);

        if (fDrawRibs) {
            draw_ribs(canvas, scaled, width, 0xFF00FF00);
        }

        SkPath fill;

        SkPaint p;
        p.setStyle(SkPaint::kStroke_Style);
        if (drawText) {
            p.setStrokeWidth(width * scale * scale);
        } else {
            p.setStrokeWidth(width);
        }
        p.getFillPath(path, &fill);
        SkPath scaledFill;
        if (drawText) {
            fill.transform(matrix, &scaledFill);
        } else {
            scaledFill = fill;
        }
        paint.setColor(WIREFRAME_COLOR);
        canvas->drawPath(scaledFill, paint);
        draw_points(canvas, scaledFill, WIREFRAME_COLOR, false);
    }
bool GrStrokePathRenderer::onDrawPath(const SkPath& origPath,
                                      const SkStrokeRec& stroke,
                                      GrDrawTarget* target,
                                      bool antiAlias) {
    if (origPath.isEmpty()) {
        return true;
    }

    SkScalar width = stroke.getWidth();
    if (width <= 0) {
        return false;
    }

    // Get the join type
    SkPaint::Join join = stroke.getJoin();
    SkScalar miterLimit = stroke.getMiter();
    SkScalar sqMiterLimit = SkScalarMul(miterLimit, miterLimit);
    if ((join == SkPaint::kMiter_Join) && (miterLimit <= SK_Scalar1)) {
        // If the miter limit is small, treat it as a bevel join
        join = SkPaint::kBevel_Join;
    }
    const bool isMiter       = (join == SkPaint::kMiter_Join);
    const bool isBevel       = (join == SkPaint::kBevel_Join);
    SkScalar invMiterLimit   = isMiter ? SK_Scalar1 / miterLimit : 0;
    SkScalar invMiterLimitSq = SkScalarMul(invMiterLimit, invMiterLimit);

    // Allocate vertices
    const int nbQuads     = origPath.countPoints() + 1; // Could be "-1" if path is not closed
    const int extraVerts  = isMiter || isBevel ? 1 : 0;
    const int maxVertexCount = nbQuads * (4 + extraVerts);
    const int maxIndexCount  = nbQuads * (6 + extraVerts * 3); // Each extra vert adds a triangle
    target->drawState()->setDefaultVertexAttribs();
    GrDrawTarget::AutoReleaseGeometry arg(target, maxVertexCount, maxIndexCount);
    if (!arg.succeeded()) {
        return false;
    }
    SkPoint* verts = reinterpret_cast<SkPoint*>(arg.vertices());
    uint16_t* idxs = reinterpret_cast<uint16_t*>(arg.indices());
    int vCount = 0, iCount = 0;

    // Transform the path into a list of triangles
    SkPath::Iter iter(origPath, false);
    SkPoint pts[4];
    const SkScalar radius = SkScalarMul(width, 0.5f);
    SkPoint *firstPt = verts, *lastPt = NULL;
    SkVector firstDir, dir;
    firstDir.set(0, 0);
    dir.set(0, 0);
    bool isOpen = true;
    for(SkPath::Verb v = iter.next(pts); v != SkPath::kDone_Verb; v = iter.next(pts)) {
        switch(v) {
            case SkPath::kMove_Verb:
                // This will already be handled as pts[0] of the 1st line
                break;
            case SkPath::kClose_Verb:
                isOpen = (lastPt == NULL);
                break;
            case SkPath::kLine_Verb:
            {
                SkVector v0 = dir;
                dir = pts[1] - pts[0];
                if (dir.setLength(radius)) {
                    SkVector dirT;
                    dirT.set(dir.fY, -dir.fX); // Get perpendicular direction
                    SkPoint l1a = pts[0]+dirT, l1b = pts[1]+dirT,
                            l2a = pts[0]-dirT, l2b = pts[1]-dirT;
                    SkPoint miterPt[2];
                    bool useMiterPoint = false;
                    int idx0(-1), idx1(-1);
                    if (NULL == lastPt) {
                        firstDir = dir;
                    } else {
                        SkVector v1 = dir;
                        if (v0.normalize() && v1.normalize()) {
                            SkScalar dotProd = v0.dot(v1);
                            // No need for bevel or miter join if the angle
                            // is either 0 or 180 degrees
                            if (!SkScalarNearlyZero(dotProd + SK_Scalar1) &&
                                !SkScalarNearlyZero(dotProd - SK_Scalar1)) {
                                bool ccw = !is_clockwise(v0, v1);
                                int offset = ccw ? 1 : 0;
                                idx0 = vCount-2+offset;
                                idx1 = vCount+offset;
                                const SkPoint* pt0 = &(lastPt[offset]);
                                const SkPoint* pt1 = ccw ? &l2a : &l1a;
                                switch(join) {
                                    case SkPaint::kMiter_Join:
                                    {
                                        // *Note : Logic is from MiterJoiner

                                        // FIXME : Special case if we have a right angle ?
                                        // if (SkScalarNearlyZero(dotProd)) {...}

                                        SkScalar sinHalfAngleSq =
                                                SkScalarHalf(SK_Scalar1 + dotProd);
                                        if (sinHalfAngleSq >= invMiterLimitSq) {
                                            // Find the miter point (or points if it is further
                                            // than the miter limit)
                                            const SkPoint pt2 = *pt0+v0, pt3 = *pt1+v1;
                                            if (intersection(*pt0, pt2, *pt1, pt3, miterPt[0]) !=
                                                kNone_IntersectionType) {
                                                SkPoint miterPt0 = miterPt[0] - *pt0;
                                                SkPoint miterPt1 = miterPt[0] - *pt1;
                                                SkScalar sqDist0 = miterPt0.dot(miterPt0);
                                                SkScalar sqDist1 = miterPt1.dot(miterPt1);
                                                const SkScalar rSq =
                                                        SkScalarDiv(SkScalarMul(radius, radius),
                                                                    sinHalfAngleSq);
                                                const SkScalar sqRLimit =
                                                        SkScalarMul(sqMiterLimit, rSq);
                                                if (sqDist0 > sqRLimit || sqDist1 > sqRLimit) {
                                                    if (sqDist1 > sqRLimit) {
                                                        v1.setLength(SkScalarSqrt(sqRLimit));
                                                        miterPt[1] = *pt1+v1;
                                                    } else {
                                                        miterPt[1] = miterPt[0];
                                                    }
                                                    if (sqDist0 > sqRLimit) {
                                                        v0.setLength(SkScalarSqrt(sqRLimit));
                                                        miterPt[0] = *pt0+v0;
                                                    }
                                                } else {
                                                    miterPt[1] = miterPt[0];
                                                }
                                                useMiterPoint = true;
                                            }
                                        }
                                        if (useMiterPoint && (miterPt[1] == miterPt[0])) {
                                            break;
                                        }
                                    }
                                    default:
                                    case SkPaint::kBevel_Join:
                                    {
                                        // Note : This currently causes some overdraw where both
                                        //        lines initially intersect. We'd need to add
                                        //        another line intersection check here if the
                                        //        overdraw becomes an issue instead of using the
                                        //        current point directly.

                                        // Add center point
                                        *verts++ = pts[0]; // Use current point directly
                                        // This idx is passed the current point so increment it
                                        ++idx1;
                                        // Add center triangle
                                        *idxs++ = idx0;
                                        *idxs++ = vCount;
                                        *idxs++ = idx1;
                                        vCount++;
                                        iCount += 3;
                                    }
                                    break;
                                }
                            }
                        }
                    }
                    *verts++ = l1a;
                    *verts++ = l2a;
                    lastPt   = verts;
                    *verts++ = l1b;
                    *verts++ = l2b;

                    if (useMiterPoint && (idx0 >= 0) && (idx1 >= 0)) {
                        firstPt[idx0] = miterPt[0];
                        firstPt[idx1] = miterPt[1];
                    }

                    // 1st triangle
                    *idxs++  = vCount+0;
                    *idxs++  = vCount+2;
                    *idxs++  = vCount+1;
                    // 2nd triangle
                    *idxs++  = vCount+1;
                    *idxs++  = vCount+2;
                    *idxs++  = vCount+3;

                    vCount += 4;
                    iCount += 6;
                }
            }
                break;
            case SkPath::kQuad_Verb:
            case SkPath::kCubic_Verb:
                SkDEBUGFAIL("Curves not supported!");
            default:
                // Unhandled cases
                SkASSERT(false);
        }
    }

    if (isOpen) {
        // Add caps
        switch (stroke.getCap()) {
            case SkPaint::kSquare_Cap:
                firstPt[0] -= firstDir;
                firstPt[1] -= firstDir;
                lastPt [0] += dir;
                lastPt [1] += dir;
                break;
            case SkPaint::kRound_Cap:
                SkDEBUGFAIL("Round caps not supported!");
            default: // No cap
                break;
        }
    }

    SkASSERT(vCount <= maxVertexCount);
    SkASSERT(iCount <= maxIndexCount);

    if (vCount > 0) {
        target->drawIndexed(kTriangles_GrPrimitiveType,
                            0,        // start vertex
                            0,        // start index
                            vCount,
                            iCount);
    }

    return true;
}
Beispiel #25
0
static void dump(const SkPath& path) {
    const SkRect& r = path.getBounds();
    SkDebugf("isEmpty %d, bounds [%g %g %g %g]\n", path.isEmpty(),
             r.fLeft, r.fTop, r.fRight, r.fBottom);
}
Beispiel #26
0
////////////////////////////////////////////////////////////////////////////////
// Create a 1-bit clip mask in the stencil buffer. 'devClipBounds' are in device
// (as opposed to canvas) coordinates
bool GrClipMaskManager::createStencilClipMask(GrRenderTarget* rt,
                                              int32_t elementsGenID,
                                              GrReducedClip::InitialState initialState,
                                              const GrReducedClip::ElementList& elements,
                                              const SkIRect& clipSpaceIBounds,
                                              const SkIPoint& clipSpaceToStencilOffset) {
    SkASSERT(rt);

    GrStencilAttachment* stencilAttachment =
        fDrawTarget->cmmAccess().resourceProvider()->attachStencilAttachment(rt);
    if (nullptr == stencilAttachment) {
        return false;
    }

    if (stencilAttachment->mustRenderClip(elementsGenID, clipSpaceIBounds, clipSpaceToStencilOffset)) {
        stencilAttachment->setLastClip(elementsGenID, clipSpaceIBounds, clipSpaceToStencilOffset);
        // Set the matrix so that rendered clip elements are transformed from clip to stencil space.
        SkVector translate = {
            SkIntToScalar(clipSpaceToStencilOffset.fX),
            SkIntToScalar(clipSpaceToStencilOffset.fY)
        };
        SkMatrix viewMatrix;
        viewMatrix.setTranslate(translate);

        // We set the current clip to the bounds so that our recursive draws are scissored to them.
        SkIRect stencilSpaceIBounds(clipSpaceIBounds);
        stencilSpaceIBounds.offset(clipSpaceToStencilOffset);
        GrClip clip(stencilSpaceIBounds);

        int clipBit = stencilAttachment->bits();
        SkASSERT((clipBit <= 16) && "Ganesh only handles 16b or smaller stencil buffers");
        clipBit = (1 << (clipBit-1));

        fDrawTarget->cmmAccess().clearStencilClip(stencilSpaceIBounds,
            GrReducedClip::kAllIn_InitialState == initialState, rt);

        // walk through each clip element and perform its set op
        // with the existing clip.
        for (GrReducedClip::ElementList::Iter iter(elements.headIter()); iter.get(); iter.next()) {
            const Element* element = iter.get();

            GrPipelineBuilder pipelineBuilder;
            pipelineBuilder.setClip(clip);
            pipelineBuilder.setRenderTarget(rt);

            pipelineBuilder.setDisableColorXPFactory();

            // if the target is MSAA then we want MSAA enabled when the clip is soft
            if (rt->isStencilBufferMultisampled()) {
                pipelineBuilder.setState(GrPipelineBuilder::kHWAntialias_Flag, element->isAA());
            }

            bool fillInverted = false;
            // enabled at bottom of loop
            fClipMode = kIgnoreClip_StencilClipMode;

            // This will be used to determine whether the clip shape can be rendered into the
            // stencil with arbitrary stencil settings.
            GrPathRenderer::StencilSupport stencilSupport;

            GrStrokeInfo stroke(SkStrokeRec::kFill_InitStyle);
            SkRegion::Op op = element->getOp();

            GrPathRenderer* pr = nullptr;
            SkPath clipPath;
            if (Element::kRect_Type == element->getType()) {
                stencilSupport = GrPathRenderer::kNoRestriction_StencilSupport;
                fillInverted = false;
            } else {
                element->asPath(&clipPath);
                fillInverted = clipPath.isInverseFillType();
                if (fillInverted) {
                    clipPath.toggleInverseFillType();
                }
                pr = this->getContext()->getPathRenderer(fDrawTarget,
                                                         &pipelineBuilder,
                                                         viewMatrix,
                                                         clipPath,
                                                         stroke,
                                                         false,
                                                         GrPathRendererChain::kStencilOnly_DrawType,
                                                         &stencilSupport);
                if (nullptr == pr) {
                    return false;
                }
            }

            int passes;
            GrStencilSettings stencilSettings[GrStencilSettings::kMaxStencilClipPasses];

            bool canRenderDirectToStencil =
                GrPathRenderer::kNoRestriction_StencilSupport == stencilSupport;
            bool canDrawDirectToClip; // Given the renderer, the element,
                                      // fill rule, and set operation can
                                      // we render the element directly to
                                      // stencil bit used for clipping.
            canDrawDirectToClip = GrStencilSettings::GetClipPasses(op,
                                                                   canRenderDirectToStencil,
                                                                   clipBit,
                                                                   fillInverted,
                                                                   &passes,
                                                                   stencilSettings);

            // draw the element to the client stencil bits if necessary
            if (!canDrawDirectToClip) {
                GR_STATIC_CONST_SAME_STENCIL(gDrawToStencil,
                                             kIncClamp_StencilOp,
                                             kIncClamp_StencilOp,
                                             kAlways_StencilFunc,
                                             0xffff,
                                             0x0000,
                                             0xffff);
                if (Element::kRect_Type == element->getType()) {
                    *pipelineBuilder.stencil() = gDrawToStencil;

                    // We need this AGP until everything is in GrBatch
                    fDrawTarget->drawNonAARect(pipelineBuilder,
                                               GrColor_WHITE,
                                               viewMatrix,
                                               element->getRect());
                } else {
                    if (!clipPath.isEmpty()) {
                        if (canRenderDirectToStencil) {
                            *pipelineBuilder.stencil() = gDrawToStencil;

                            GrPathRenderer::DrawPathArgs args;
                            args.fTarget = fDrawTarget;
                            args.fResourceProvider = this->getContext()->resourceProvider();
                            args.fPipelineBuilder = &pipelineBuilder;
                            args.fColor = GrColor_WHITE;
                            args.fViewMatrix = &viewMatrix;
                            args.fPath = &clipPath;
                            args.fStroke = &stroke;
                            args.fAntiAlias = false;
                            pr->drawPath(args);
                        } else {
                            GrPathRenderer::StencilPathArgs args;
                            args.fTarget = fDrawTarget;
                            args.fResourceProvider = this->getContext()->resourceProvider();
                            args.fPipelineBuilder = &pipelineBuilder;
                            args.fViewMatrix = &viewMatrix;
                            args.fPath = &clipPath;
                            args.fStroke = &stroke;
                            pr->stencilPath(args);
                        }
                    }
                }
            }

            // now we modify the clip bit by rendering either the clip
            // element directly or a bounding rect of the entire clip.
            fClipMode = kModifyClip_StencilClipMode;
            for (int p = 0; p < passes; ++p) {
                *pipelineBuilder.stencil() = stencilSettings[p];

                if (canDrawDirectToClip) {
                    if (Element::kRect_Type == element->getType()) {
                        // We need this AGP until everything is in GrBatch
                        fDrawTarget->drawNonAARect(pipelineBuilder,
                                                   GrColor_WHITE,
                                                   viewMatrix,
                                                   element->getRect());
                    } else {
                        GrPathRenderer::DrawPathArgs args;
                        args.fTarget = fDrawTarget;
                        args.fResourceProvider = this->getContext()->resourceProvider();
                        args.fPipelineBuilder = &pipelineBuilder;
                        args.fColor = GrColor_WHITE;
                        args.fViewMatrix = &viewMatrix;
                        args.fPath = &clipPath;
                        args.fStroke = &stroke;
                        args.fAntiAlias = false;
                        pr->drawPath(args);
                    }
                } else {
                    // The view matrix is setup to do clip space -> stencil space translation, so
                    // draw rect in clip space.
                    fDrawTarget->drawNonAARect(pipelineBuilder,
                                               GrColor_WHITE,
                                               viewMatrix,
                                               SkRect::Make(clipSpaceIBounds));
                }
            }
        }
    }
    fClipMode = kRespectClip_StencilClipMode;
    return true;
}