void SkDeferredCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) { SkRect modRect = rrect.getBounds(); this->flush_check(&modRect, &paint, kNoClip_Flag); fCanvas->drawRRect(make_offset(rrect, modRect.x() - rrect.getBounds().x(), modRect.y() - rrect.getBounds().y()), paint); }
void SkPictureRecord::drawRRect(const SkRRect& rrect, const SkPaint& paint) { if (rrect.isRect()) { addDraw(DRAW_RECT); addPaint(paint); addRect(rrect.getBounds()); } else if (rrect.isOval()) { addDraw(DRAW_OVAL); addPaint(paint); addRect(rrect.getBounds()); } else { addDraw(DRAW_RRECT); addPaint(paint); addRRect(rrect); } validate(); }
SkRect draw(SkCanvas* canvas, const SkPaint& paint) override { SkRRect outerRRect; outerRRect.setRectXY(SkRect::MakeXYWH(0, 0, 50, 50), 5, 5); SkRRect innerRRect; innerRRect.setRectXY(SkRect::MakeXYWH(5, 8, 35, 30), 8, 3); canvas->drawDRRect(outerRRect, innerRRect, paint); return outerRRect.getBounds(); }
bool SkPictureRecord::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) { if (rrect.isRect()) { return this->SkPictureRecord::clipRect(rrect.getBounds(), op, doAA); } addDraw(CLIP_RRECT); addRRect(rrect); addInt(ClipParams_pack(op, doAA)); recordRestoreOffsetPlaceholder(op); validate(); if (fRecordFlags & SkPicture::kUsePathBoundsForClip_RecordingFlag) { return this->INHERITED::clipRect(rrect.getBounds(), op, doAA); } else { return this->INHERITED::clipRRect(rrect, op, doAA); } }
bool SkRasterClip::op(const SkRRect& rrect, const SkMatrix& matrix, const SkIRect& bounds, SkRegion::Op op, bool doAA) { if (fForceConservativeRects) { return this->op(rrect.getBounds(), matrix, bounds, op, doAA); } SkPath path; path.addRRect(rrect); return this->op(path, matrix, bounds, op, doAA); }
void draw(SkCanvas* canvas) { SkPaint paint; paint.setAntiAlias(true); paint.setTextSize(16); SkRRect rrect = SkRRect::MakeRectXY({30, 10, 100, 60}, 40, 30); canvas->drawRRect(rrect, paint); canvas->drawString(rrect.isOval() ? "oval" : "not oval", 64, 90, paint); rrect.setRectXY(rrect.getBounds(), 35, 25); canvas->translate(128, 0); canvas->drawRRect(rrect, paint); canvas->drawString(rrect.isOval() ? "oval" : "not oval", 64, 90, paint); }
// The tallest inset rect SkRect compute_tallest_occluder(const SkRRect& rr) { const SkRect& r = rr.getBounds(); const SkVector& ul = rr.radii(SkRRect::kUpperLeft_Corner); const SkVector& ur = rr.radii(SkRRect::kUpperRight_Corner); const SkVector& lr = rr.radii(SkRRect::kLowerRight_Corner); const SkVector& ll = rr.radii(SkRRect::kLowerLeft_Corner); SkScalar maxL = SkTMax(ul.fX, ll.fX); SkScalar maxR = SkTMax(ur.fX, lr.fX); return SkRect::MakeLTRB(r.fLeft + maxL, r.fTop, r.fRight - maxR, r.fBottom); }
// The widest inset rect SkRect compute_widest_occluder(const SkRRect& rr) { const SkRect& r = rr.getBounds(); const SkVector& ul = rr.radii(SkRRect::kUpperLeft_Corner); const SkVector& ur = rr.radii(SkRRect::kUpperRight_Corner); const SkVector& lr = rr.radii(SkRRect::kLowerRight_Corner); const SkVector& ll = rr.radii(SkRRect::kLowerLeft_Corner); SkScalar maxT = SkTMax(ul.fY, ur.fY); SkScalar maxB = SkTMax(ll.fY, lr.fY); return SkRect::MakeLTRB(r.fLeft, r.fTop + maxT, r.fRight, r.fBottom - maxB); }
// Use the intersection of the corners' diagonals with their ellipses to shrink // the bounding rect SkRect compute_central_occluder(const SkRRect& rr) { const SkRect r = rr.getBounds(); SkScalar newL = r.fLeft, newT = r.fTop, newR = r.fRight, newB = r.fBottom; SkVector radii = rr.radii(SkRRect::kUpperLeft_Corner); if (!radii.isZero()) { SkPoint p = intersection(radii.fX, radii.fY); newL = SkTMax(newL, r.fLeft + radii.fX - p.fX); newT = SkTMax(newT, r.fTop + radii.fY - p.fY); } radii = rr.radii(SkRRect::kUpperRight_Corner); if (!radii.isZero()) { SkPoint p = intersection(radii.fX, radii.fY); newR = SkTMin(newR, r.fRight + p.fX - radii.fX); newT = SkTMax(newT, r.fTop + radii.fY - p.fY); } radii = rr.radii(SkRRect::kLowerRight_Corner); if (!radii.isZero()) { SkPoint p = intersection(radii.fX, radii.fY); newR = SkTMin(newR, r.fRight + p.fX - radii.fX); newB = SkTMin(newB, r.fBottom - radii.fY + p.fY); } radii = rr.radii(SkRRect::kLowerLeft_Corner); if (!radii.isZero()) { SkPoint p = intersection(radii.fX, radii.fY); newL = SkTMax(newL, r.fLeft + radii.fX - p.fX); newB = SkTMin(newB, r.fBottom - radii.fY + p.fY); } return SkRect::MakeLTRB(newL, newT, newR, newB); }
static void toString(const SkRRect& rrect, SkString* str) { SkRect r = rrect.getBounds(); str->appendf("[%g,%g %g:%g]", SkScalarToFloat(r.fLeft), SkScalarToFloat(r.fTop), SkScalarToFloat(r.width()), SkScalarToFloat(r.height())); if (rrect.isOval()) { str->append("()"); } else if (rrect.isSimple()) { const SkVector& rad = rrect.getSimpleRadii(); str->appendf("(%g,%g)", rad.x(), rad.y()); } else if (rrect.isComplex()) { SkVector radii[4] = { rrect.radii(SkRRect::kUpperLeft_Corner), rrect.radii(SkRRect::kUpperRight_Corner), rrect.radii(SkRRect::kLowerRight_Corner), rrect.radii(SkRRect::kLowerLeft_Corner), }; str->appendf("(%g,%g %g,%g %g,%g %g,%g)", radii[0].x(), radii[0].y(), radii[1].x(), radii[1].y(), radii[2].x(), radii[2].y(), radii[3].x(), radii[3].y()); } }
bool SkClipStack::quickContains(const SkRRect& rrect) const { Iter iter(*this, Iter::kTop_IterStart); const Element* element = iter.prev(); while (element != nullptr) { if (SkRegion::kIntersect_Op != element->getOp() && SkRegion::kReplace_Op != element->getOp()) return false; if (element->isInverseFilled()) { // Part of 'rrect' could be trimmed off by the inverse-filled clip element if (SkRect::Intersects(element->getBounds(), rrect.getBounds())) { return false; } } else { if (!element->contains(rrect)) { return false; } } if (SkRegion::kReplace_Op == element->getOp()) { break; } element = iter.prev(); } return true; }
void SkRecorder::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) { APPEND(ClipRRect, rrect, op, edgeStyle == kSoft_ClipEdgeStyle); INHERITED(updateClipConservativelyUsingBounds, rrect.getBounds(), op, false); }
SkRect draw(SkCanvas* canvas, const SkPaint& paint) override { SkRRect rrect; rrect.setRectXY(SkRect::MakeXYWH(0, 0, 50, 50), 10, 10); canvas->drawRRect(rrect, paint); return rrect.getBounds(); }
std::unique_ptr<GrFragmentProcessor> GrRRectEffect::Make(GrClipEdgeType edgeType, const SkRRect& rrect, const GrShaderCaps& caps) { if (rrect.isRect()) { return GrConvexPolyEffect::Make(edgeType, rrect.getBounds()); } if (rrect.isOval()) { return GrOvalEffect::Make(edgeType, rrect.getBounds(), caps); } if (rrect.isSimple()) { if (SkRRectPriv::GetSimpleRadii(rrect).fX < kRadiusMin || SkRRectPriv::GetSimpleRadii(rrect).fY < kRadiusMin) { // In this case the corners are extremely close to rectangular and we collapse the // clip to a rectangular clip. return GrConvexPolyEffect::Make(edgeType, rrect.getBounds()); } if (SkRRectPriv::GetSimpleRadii(rrect).fX == SkRRectPriv::GetSimpleRadii(rrect).fY) { return CircularRRectEffect::Make(edgeType, CircularRRectEffect::kAll_CornerFlags, rrect); } else { return EllipticalRRectEffect::Make(edgeType, rrect); } } if (rrect.isComplex() || rrect.isNinePatch()) { // Check for the "tab" cases - two adjacent circular corners and two square corners. SkScalar circularRadius = 0; uint32_t cornerFlags = 0; SkVector radii[4]; bool squashedRadii = false; for (int c = 0; c < 4; ++c) { radii[c] = rrect.radii((SkRRect::Corner)c); SkASSERT((0 == radii[c].fX) == (0 == radii[c].fY)); if (0 == radii[c].fX) { // The corner is square, so no need to squash or flag as circular. continue; } if (radii[c].fX < kRadiusMin || radii[c].fY < kRadiusMin) { radii[c].set(0, 0); squashedRadii = true; continue; } if (radii[c].fX != radii[c].fY) { cornerFlags = ~0U; break; } if (!cornerFlags) { circularRadius = radii[c].fX; cornerFlags = 1 << c; } else { if (radii[c].fX != circularRadius) { cornerFlags = ~0U; break; } cornerFlags |= 1 << c; } } switch (cornerFlags) { case CircularRRectEffect::kAll_CornerFlags: // This rrect should have been caught in the simple case above. Though, it would // be correctly handled in the fallthrough code. SkASSERT(false); case CircularRRectEffect::kTopLeft_CornerFlag: case CircularRRectEffect::kTopRight_CornerFlag: case CircularRRectEffect::kBottomRight_CornerFlag: case CircularRRectEffect::kBottomLeft_CornerFlag: case CircularRRectEffect::kLeft_CornerFlags: case CircularRRectEffect::kTop_CornerFlags: case CircularRRectEffect::kRight_CornerFlags: case CircularRRectEffect::kBottom_CornerFlags: { SkTCopyOnFirstWrite<SkRRect> rr(rrect); if (squashedRadii) { rr.writable()->setRectRadii(rrect.getBounds(), radii); } return CircularRRectEffect::Make(edgeType, cornerFlags, *rr); } case CircularRRectEffect::kNone_CornerFlags: return GrConvexPolyEffect::Make(edgeType, rrect.getBounds()); default: { if (squashedRadii) { // If we got here then we squashed some but not all the radii to zero. (If all // had been squashed cornerFlags would be 0.) The elliptical effect doesn't // support some rounded and some square corners. return nullptr; } if (rrect.isNinePatch()) { return EllipticalRRectEffect::Make(edgeType, rrect); } return nullptr; } } } return nullptr; }
void GrTextBlob::flush(GrTextTarget* target, const SkSurfaceProps& props, const GrDistanceFieldAdjustTable* distanceAdjustTable, const SkPaint& paint, GrColor filteredColor, const GrClip& clip, const SkMatrix& viewMatrix, SkScalar x, SkScalar y) { // GrTextBlob::makeOp only takes uint16_t values for run and subRun indices. // Encountering something larger than this is highly unlikely, so we'll just not draw it. int lastRun = SkTMin(fRunCount, (1 << 16)) - 1; // For each run in the GrTextBlob we're going to churn through all the glyphs. // Each run is broken into a path part and a Mask / DFT / ARGB part. for (int runIndex = 0; runIndex <= lastRun; runIndex++) { Run& run = fRuns[runIndex]; // first flush any path glyphs if (run.fPathGlyphs.count()) { SkPaint runPaint{paint}; runPaint.setFlags((runPaint.getFlags() & ~Run::kPaintFlagsMask) | run.fPaintFlags); for (int i = 0; i < run.fPathGlyphs.count(); i++) { GrTextBlob::Run::PathGlyph& pathGlyph = run.fPathGlyphs[i]; SkMatrix ctm; const SkPath* path = &pathGlyph.fPath; // TmpPath must be in the same scope as GrShape shape below. SkTLazy<SkPath> tmpPath; // The glyph positions and glyph outlines are either in device space or in source // space based on fPreTransformed. if (!pathGlyph.fPreTransformed) { // Positions and outlines are in source space. ctm = viewMatrix; SkMatrix pathMatrix = SkMatrix::MakeScale(pathGlyph.fScale, pathGlyph.fScale); // The origin for the blob may have changed, so figure out the delta. SkVector originShift = SkPoint{x, y} - SkPoint{fInitialX, fInitialY}; // Shift the original glyph location in source space to the position of the new // blob. pathMatrix.postTranslate(originShift.x() + pathGlyph.fX, originShift.y() + pathGlyph.fY); // If there are shaders, blurs or styles, the path must be scaled into source // space independently of the CTM. This allows the CTM to be correct for the // different effects. GrStyle style(runPaint); bool scalePath = runPaint.getShader() || style.applies() || runPaint.getMaskFilter(); if (!scalePath) { // Scale can be applied to CTM -- no effects. ctm.preConcat(pathMatrix); } else { // Scale the outline into source space. // Transform the path form the normalized outline to source space. This // way the CTM will remain the same so it can be used by the effects. SkPath* sourceOutline = tmpPath.init(); path->transform(pathMatrix, sourceOutline); sourceOutline->setIsVolatile(true); path = sourceOutline; } } else { // Positions and outlines are in device space. SkPoint originalOrigin = {fInitialX, fInitialY}; fInitialViewMatrix.mapPoints(&originalOrigin, 1); SkPoint newOrigin = {x, y}; viewMatrix.mapPoints(&newOrigin, 1); // The origin shift in device space. SkPoint originShift = newOrigin - originalOrigin; // Shift the original glyph location in device space to the position of the // new blob. ctm = SkMatrix::MakeTrans(originShift.x() + pathGlyph.fX, originShift.y() + pathGlyph.fY); } // TODO: we are losing the mutability of the path here GrShape shape(*path, paint); target->drawShape(clip, runPaint, ctm, shape); } } // then flush each subrun, if any if (!run.fInitialized) { continue; } int lastSubRun = SkTMin(run.fSubRunInfo.count(), 1 << 16) - 1; for (int subRun = 0; subRun <= lastSubRun; subRun++) { const Run::SubRunInfo& info = run.fSubRunInfo[subRun]; int glyphCount = info.glyphCount(); if (0 == glyphCount) { continue; } bool skipClip = false; bool submitOp = true; SkIRect clipRect = SkIRect::MakeEmpty(); SkRect rtBounds = SkRect::MakeWH(target->width(), target->height()); SkRRect clipRRect; GrAA aa; // We can clip geometrically if we're not using SDFs or transformed glyphs, // and we have an axis-aligned rectangular non-AA clip if (!info.drawAsDistanceFields() && !info.needsTransform() && clip.isRRect(rtBounds, &clipRRect, &aa) && clipRRect.isRect() && GrAA::kNo == aa) { skipClip = true; // We only need to do clipping work if the subrun isn't contained by the clip SkRect subRunBounds; this->computeSubRunBounds(&subRunBounds, runIndex, subRun, viewMatrix, x, y, false); if (!clipRRect.getBounds().contains(subRunBounds)) { // If the subrun is completely outside, don't add an op for it if (!clipRRect.getBounds().intersects(subRunBounds)) { submitOp = false; } else { clipRRect.getBounds().round(&clipRect); } } } if (submitOp) { auto op = this->makeOp(info, glyphCount, runIndex, subRun, viewMatrix, x, y, clipRect, paint, filteredColor, props, distanceAdjustTable, target); if (op) { if (skipClip) { target->addDrawOp(GrNoClip(), std::move(op)); } else { target->addDrawOp(clip, std::move(op)); } } } } } }
void SkConservativeClip::opRRect(const SkRRect& rrect, const SkMatrix& ctm, const SkIRect& devBounds, SkRegion::Op op, bool doAA) { this->opRect(rrect.getBounds(), ctm, devBounds, op, doAA); }