static void scaleMatrix(const SkPath& one, const SkPath& two, SkMatrix& scale) { SkRect larger = one.getBounds(); larger.join(two.getBounds()); SkScalar largerWidth = larger.width(); if (largerWidth < 4) { largerWidth = 4; } SkScalar largerHeight = larger.height(); if (largerHeight < 4) { largerHeight = 4; } SkScalar hScale = (bitWidth - 2) / largerWidth; SkScalar vScale = (bitHeight - 2) / largerHeight; scale.reset(); scale.preScale(hScale, vScale); larger.fLeft *= hScale; larger.fRight *= hScale; larger.fTop *= vScale; larger.fBottom *= vScale; SkScalar dx = -16000 > larger.fLeft ? -16000 - larger.fLeft : 16000 < larger.fRight ? 16000 - larger.fRight : 0; SkScalar dy = -16000 > larger.fTop ? -16000 - larger.fTop : 16000 < larger.fBottom ? 16000 - larger.fBottom : 0; scale.postTranslate(dx, dy); }
// doesn't work yet void comparePathsTiny(const SkPath& one, const SkPath& two) { const SkRect& bounds1 = one.getBounds(); const SkRect& bounds2 = two.getBounds(); SkRect larger = bounds1; larger.join(bounds2); SkBitmap bits; int bitWidth = SkScalarCeil(larger.width()) + 2; int bitHeight = SkScalarCeil(larger.height()) + 2; bits.setConfig(SkBitmap::kA1_Config, bitWidth * 2, bitHeight); bits.allocPixels(); SkCanvas canvas(bits); canvas.drawColor(SK_ColorWHITE); SkPaint paint; canvas.save(); canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1); canvas.drawPath(one, paint); canvas.restore(); canvas.save(); canvas.translate(-bounds2.fLeft + 1, -bounds2.fTop + 1); canvas.drawPath(two, paint); canvas.restore(); for (int y = 0; y < bitHeight; ++y) { uint8_t* addr1 = bits.getAddr1(0, y); uint8_t* addr2 = bits.getAddr1(bitWidth, y); for (int x = 0; x < bits.rowBytes(); ++x) { SkASSERT(addr1[x] == addr2[x]); } } }
static void test_path_bounds(skiatest::Reporter* reporter) { SkPath path; SkAAClip clip; const int height = 40; const SkScalar sheight = SkIntToScalar(height); path.addOval(SkRect::MakeWH(sheight, sheight)); REPORTER_ASSERT(reporter, sheight == path.getBounds().height()); clip.setPath(path, nullptr, true); REPORTER_ASSERT(reporter, height == clip.getBounds().height()); // this is the trimmed height of this cubic (with aa). The critical thing // for this test is that it is less than height, which represents just // the bounds of the path's control-points. // // This used to fail until we tracked the MinY in the BuilderBlitter. // const int teardrop_height = 12; path.reset(); imoveTo(path, 0, 20); icubicTo(path, 40, 40, 40, 0, 0, 20); REPORTER_ASSERT(reporter, sheight == path.getBounds().height()); clip.setPath(path, nullptr, true); REPORTER_ASSERT(reporter, teardrop_height == clip.getBounds().height()); }
TessellatingPathBatch(const GrColor& color, const SkPath& path, const GrStrokeInfo& stroke, const SkMatrix& viewMatrix, const SkRect& clipBounds) : INHERITED(ClassID()) , fColor(color) , fPath(path) , fStroke(stroke) , fViewMatrix(viewMatrix) { const SkRect& pathBounds = path.getBounds(); fClipBounds = clipBounds; // Because the clip bounds are used to add a contour for inverse fills, they must also // include the path bounds. fClipBounds.join(pathBounds); if (path.isInverseFillType()) { fBounds = fClipBounds; } else { fBounds = path.getBounds(); } if (!stroke.isFillStyle()) { SkScalar radius = SkScalarHalf(stroke.getWidth()); if (stroke.getJoin() == SkPaint::kMiter_Join) { SkScalar scale = stroke.getMiter(); if (scale > SK_Scalar1) { radius = SkScalarMul(radius, scale); } } fBounds.outset(radius, radius); } viewMatrix.mapRect(&fBounds); }
bool drawAsciiPaths(const SkPath& one, const SkPath& two, bool drawPaths) { if (!drawPaths) { return true; } if (gShowAsciiPaths) { showPath(one, "one:"); showPath(two, "two:"); } const SkRect& bounds1 = one.getBounds(); const SkRect& bounds2 = two.getBounds(); SkRect larger = bounds1; larger.join(bounds2); SkBitmap bits; char out[256]; int bitWidth = SkScalarCeil(larger.width()) + 2; if (bitWidth * 2 + 1 >= (int) sizeof(out)) { return false; } int bitHeight = SkScalarCeil(larger.height()) + 2; if (bitHeight >= (int) sizeof(out)) { return false; } bits.setConfig(SkBitmap::kARGB_8888_Config, bitWidth * 2, bitHeight); bits.allocPixels(); SkCanvas canvas(bits); canvas.drawColor(SK_ColorWHITE); SkPaint paint; canvas.save(); canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1); canvas.drawPath(one, paint); canvas.restore(); canvas.save(); canvas.translate(-bounds2.fLeft + 1 + bitWidth, -bounds2.fTop + 1); canvas.drawPath(two, paint); canvas.restore(); for (int y = 0; y < bitHeight; ++y) { uint32_t* addr1 = bits.getAddr32(0, y); int x; char* outPtr = out; for (x = 0; x < bitWidth; ++x) { *outPtr++ = addr1[x] == (uint32_t) -1 ? '_' : 'x'; } *outPtr++ = '|'; for (x = bitWidth; x < bitWidth * 2; ++x) { *outPtr++ = addr1[x] == (uint32_t) -1 ? '_' : 'x'; } *outPtr++ = '\0'; SkDebugf("%s\n", out); } return true; }
static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p, const SkRect& bounds) { REPORTER_ASSERT(reporter, p.isConvex()); REPORTER_ASSERT(reporter, p.getBounds() == bounds); SkPath p2(p); REPORTER_ASSERT(reporter, p2.isConvex()); REPORTER_ASSERT(reporter, p2.getBounds() == bounds); SkPath other; other.swap(p2); REPORTER_ASSERT(reporter, other.isConvex()); REPORTER_ASSERT(reporter, other.getBounds() == bounds); }
static void test_maskFromPath(const SkPath& path) { SkIRect bounds; path.getBounds().roundOut(&bounds); SkPaint paint; paint.setAntiAlias(true); SkAutoTUnref<SkCanvas> path_canvas(MakeCanvas(bounds)); path_canvas->drawPath(path, paint); SkAutoTUnref<SkCanvas> rect_canvas(MakeCanvas(bounds)); drawRectAsPath(rect_canvas, path.getBounds(), paint); compare_canvas(path_canvas, rect_canvas); }
static void test_cubic2() { const char* str = "M2242 -590088L-377758 9.94099e+07L-377758 9.94099e+07L2242 -590088Z"; SkPath path; SkParsePath::FromSVGString(str, &path); { #ifdef SK_BUILD_FOR_WIN // windows doesn't have strtof float x = (float)strtod("9.94099e+07", NULL); #else float x = strtof("9.94099e+07", NULL); #endif int ix = (int)x; int fx = (int)(x * 65536); int ffx = SkScalarToFixed(x); printf("%g %x %x %x\n", x, ix, fx, ffx); SkRect r = path.getBounds(); SkIRect ir; r.round(&ir); printf("[%g %g %g %g] [%x %x %x %x]\n", SkScalarToDouble(r.fLeft), SkScalarToDouble(r.fTop), SkScalarToDouble(r.fRight), SkScalarToDouble(r.fBottom), ir.fLeft, ir.fTop, ir.fRight, ir.fBottom); } SkBitmap bitmap; bitmap.setConfig(SkBitmap::kARGB_8888_Config, 300, 200); bitmap.allocPixels(); SkCanvas canvas(bitmap); SkPaint paint; paint.setAntiAlias(true); canvas.drawPath(path, paint); }
bool GrAAConvexPathRenderer::onDrawPath(GrDrawTarget* target, GrPipelineBuilder* pipelineBuilder, GrColor color, const SkMatrix& vm, const SkPath& path, const GrStrokeInfo&, 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; }
void SkGScalerContext::generateMetrics(SkGlyph* glyph) { fProxy->getMetrics(glyph); SkVector advance; fMatrix.mapXY(SkFloatToScalar(glyph->fAdvanceX), SkFloatToScalar(glyph->fAdvanceY), &advance); glyph->fAdvanceX = SkScalarToFloat(advance.fX); glyph->fAdvanceY = SkScalarToFloat(advance.fY); SkPath path; fProxy->getPath(*glyph, &path); path.transform(fMatrix); SkRect storage; const SkPaint& paint = fFace->paint(); const SkRect& newBounds = paint.doComputeFastBounds(path.getBounds(), &storage, SkPaint::kFill_Style); SkIRect ibounds; newBounds.roundOut(&ibounds); glyph->fLeft = ibounds.fLeft; glyph->fTop = ibounds.fTop; glyph->fWidth = ibounds.width(); glyph->fHeight = ibounds.height(); glyph->fMaskFormat = SkMask::kARGB32_Format; }
DEFINE_BATCH_CLASS_ID AAConvexPathBatch(GrColor color, const SkMatrix& viewMatrix, const SkPath& path) : INHERITED(ClassID()) { fGeoData.emplace_back(Geometry{color, viewMatrix, path}); this->setTransformedBounds(path.getBounds(), viewMatrix, HasAABloat::kYes, IsZeroArea::kNo); }
bool PathSkia::StrokeContainsPoint(const StrokeOptions &aStrokeOptions, const Point &aPoint, const Matrix &aTransform) const { Matrix inverse = aTransform; inverse.Invert(); Point transformed = inverse * aPoint; SkPaint paint; StrokeOptionsToPaint(paint, aStrokeOptions); SkPath strokePath; paint.getFillPath(mPath, &strokePath); Rect bounds = aTransform.TransformBounds(SkRectToRect(strokePath.getBounds())); if (aPoint.x < bounds.x || aPoint.y < bounds.y || aPoint.x > bounds.XMost() || aPoint.y > bounds.YMost()) { return false; } SkRegion pointRect; pointRect.setRect(int32_t(SkFloatToScalar(transformed.x - 1.f)), int32_t(SkFloatToScalar(transformed.y - 1.f)), int32_t(SkFloatToScalar(transformed.x + 1.f)), int32_t(SkFloatToScalar(transformed.y + 1.f))); SkRegion pathRegion; return pathRegion.setPath(strokePath, pointRect); }
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(); }
bool SkRasterClip::op(const SkPath& path, const SkMatrix& matrix, const SkIRect& bounds, SkRegion::Op op, bool doAA) { AUTO_RASTERCLIP_VALIDATE(*this); if (fForceConservativeRects) { SkIRect ir; switch (mutate_conservative_op(&op, path.isInverseFillType())) { case kDoNothing_MutateResult: return !this->isEmpty(); case kReplaceClippedAgainstGlobalBounds_MutateResult: ir = bounds; break; case kContinue_MutateResult: { SkRect bounds = path.getBounds(); matrix.mapRect(&bounds); ir = bounds.roundOut(); break; } } return this->op(ir, op); } // base is used to limit the size (and therefore memory allocation) of the // region that results from scan converting devPath. SkRegion base; SkPath devPath; if (matrix.isIdentity()) { devPath = path; } else { path.transform(matrix, &devPath); devPath.setIsVolatile(true); } if (SkRegion::kIntersect_Op == op) { // since we are intersect, we can do better (tighter) with currRgn's // bounds, than just using the device. However, if currRgn is complex, // our region blitter may hork, so we do that case in two steps. if (this->isRect()) { // FIXME: we should also be able to do this when this->isBW(), // but relaxing the test above triggers GM asserts in // SkRgnBuilder::blitH(). We need to investigate what's going on. return this->setPath(devPath, this->bwRgn(), doAA); } else { base.setRect(this->getBounds()); SkRasterClip clip(fForceConservativeRects); clip.setPath(devPath, base, doAA); return this->op(clip, op); } } else { base.setRect(bounds); if (SkRegion::kReplace_Op == op) { return this->setPath(devPath, base, doAA); } else { SkRasterClip clip(fForceConservativeRects); clip.setPath(devPath, base, doAA); return this->op(clip, op); } } }
static void scaleMatrix(const SkPath& one, const SkPath& two, SkMatrix& scale) { SkRect larger = one.getBounds(); larger.join(two.getBounds()); SkScalar largerWidth = larger.width(); if (largerWidth < 4) { largerWidth = 4; } SkScalar largerHeight = larger.height(); if (largerHeight < 4) { largerHeight = 4; } SkScalar hScale = (bitWidth - 2) / largerWidth; SkScalar vScale = (bitHeight - 2) / largerHeight; scale.reset(); scale.preScale(hScale, vScale); }
void onDraw(const int loops, SkCanvas* canvas) override { SkPaint paint; paint.setAntiAlias(true); paint.setStyle(SkPaint::kStroke_Style); paint.setStrokeWidth(2); if (fRound) { paint.setStrokeJoin(SkPaint::kRound_Join); } this->setupPaint(&paint); const SkRect r = fPath.getBounds(); switch (fAlign) { case kLeft_Align: canvas->translate(-r.left(), 0); break; case kMiddle_Align: break; case kRight_Align: canvas->translate(640 - r.right(), 0); break; } for (int i = 0; i < loops; i++) { canvas->drawPath(fPath, paint); } }
static void lettersToBitmap2(SkBitmap* dst, const char chars[], const SkPaint& original, SkBitmap::Config config) { SkPath path; SkScalar x = 0; SkScalar width; SkPath p; for (size_t i = 0; i < strlen(chars); i++) { original.getTextPath(&chars[i], 1, x, 0, &p); path.addPath(p); original.getTextWidths(&chars[i], 1, &width); x += width; } SkRect bounds = path.getBounds(); SkScalar sw = -original.getStrokeWidth(); bounds.inset(sw, sw); path.offset(-bounds.fLeft, -bounds.fTop); bounds.offset(-bounds.fLeft, -bounds.fTop); int w = SkScalarRound(bounds.width()); int h = SkScalarRound(bounds.height()); SkPaint paint(original); paint.setAntiAlias(true); paint.setXfermodeMode(SkXfermode::kDstATop_Mode); paint.setColor(original.getColor()); paint.setStyle(SkPaint::kStroke_Style); dst->setConfig(config, w, h); dst->allocPixels(); dst->eraseColor(SK_ColorWHITE); SkCanvas canvas(*dst); canvas.drawPath(path, paint); }
bool GrAADistanceFieldPathRenderer::canDrawPath(const GrDrawTarget* target, const GrPipelineBuilder* pipelineBuilder, const SkMatrix& viewMatrix, const SkPath& path, const GrStrokeInfo& stroke, bool antiAlias) const { // TODO: Support inverse fill // TODO: Support strokes if (!target->caps()->shaderCaps()->shaderDerivativeSupport() || !antiAlias || path.isInverseFillType() || path.isVolatile() || !stroke.isFillStyle()) { return false; } // currently don't support perspective if (viewMatrix.hasPerspective()) { return false; } // only support paths smaller than 64x64, scaled to less than 256x256 // the goal is to accelerate rendering of lots of small paths that may be scaling SkScalar maxScale = viewMatrix.getMaxScale(); const SkRect& bounds = path.getBounds(); SkScalar maxDim = SkMaxScalar(bounds.width(), bounds.height()); return maxDim < 64.f && maxDim * maxScale < 256.f; }
static SkBitmap createBitmap(const SkPath& path) { SkBitmap bitmap; bitmap.setConfig(SkBitmap::kARGB_8888_Config, SkImageWidget::kImageWidgetWidth, SkImageWidget::kImageWidgetHeight); bitmap.allocPixels(); bitmap.eraseColor(SK_ColorWHITE); SkDevice* device = new SkDevice(bitmap); SkCanvas canvas(device); device->unref(); const SkRect& bounds = path.getBounds(); if (bounds.width() > bounds.height()) { canvas.scale(SkDoubleToScalar((0.9*SkImageWidget::kImageWidgetWidth)/bounds.width()), SkDoubleToScalar((0.9*SkImageWidget::kImageWidgetHeight)/bounds.width())); } else { canvas.scale(SkDoubleToScalar((0.9*SkImageWidget::kImageWidgetWidth)/bounds.height()), SkDoubleToScalar((0.9*SkImageWidget::kImageWidgetHeight)/bounds.height())); } canvas.translate(-bounds.fLeft+2, -bounds.fTop+2); SkPaint p; p.setColor(SK_ColorBLACK); p.setStyle(SkPaint::kStroke_Style); canvas.drawPath(path, p); return bitmap; }
bool Sk2DPathEffect::filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*) { if (!fMatrixIsInvertible) { return false; } SkPath tmp; SkIRect ir; src.transform(fInverse, &tmp); tmp.getBounds().round(&ir); if (!ir.isEmpty()) { this->begin(ir, dst); SkRegion rgn; rgn.setPath(tmp, SkRegion(ir)); SkRegion::Iterator iter(rgn); for (; !iter.done(); iter.next()) { const SkIRect& rect = iter.rect(); for (int y = rect.fTop; y < rect.fBottom; ++y) { this->nextSpan(rect.fLeft, y, rect.width(), dst); } } this->end(dst); } return true; }
void RandomScalerContext::generateMetrics(SkGlyph* glyph) { // Here we will change the mask format of the glyph // NOTE: this may be overridden by the base class (e.g. if a mask filter is applied). switch (glyph->getGlyphID() % 4) { case 0: glyph->fMaskFormat = SkMask::kLCD16_Format; break; case 1: glyph->fMaskFormat = SkMask::kA8_Format; break; case 2: glyph->fMaskFormat = SkMask::kARGB32_Format; break; case 3: glyph->fMaskFormat = SkMask::kBW_Format; break; } fProxy->getMetrics(glyph); if (fFakeIt || (glyph->getGlyphID() % 4) != 2) { return; } SkPath path; if (!fProxy->getPath(glyph->getPackedID(), &path)) { return; } glyph->fMaskFormat = SkMask::kARGB32_Format; SkRect storage; const SkPaint& paint = this->getRandomTypeface()->paint(); const SkRect& newBounds = paint.doComputeFastBounds(path.getBounds(), &storage, SkPaint::kFill_Style); SkIRect ibounds; newBounds.roundOut(&ibounds); glyph->fLeft = ibounds.fLeft; glyph->fTop = ibounds.fTop; glyph->fWidth = ibounds.width(); glyph->fHeight = ibounds.height(); }
void onDrawContent(SkCanvas* canvas) override { test_huge_stroke(canvas); return; canvas->translate(SkIntToScalar(10), SkIntToScalar(10)); SkPaint paint; paint.setAntiAlias(true); if (true) { canvas->drawColor(SK_ColorBLACK); paint.setTextSize(24); paint.setColor(SK_ColorWHITE); canvas->translate(10, 30); static const SkBlurStyle gStyle[] = { kNormal_SkBlurStyle, kInner_SkBlurStyle, kOuter_SkBlurStyle, kSolid_SkBlurStyle, }; for (int x = 0; x < 5; x++) { SkScalar sigma = SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(4)); for (int y = 0; y < 10; y++) { if (x) { paint.setMaskFilter(SkBlurMaskFilter::Make(gStyle[x - 1], sigma)); } canvas->drawString("Title Bar", x*SkIntToScalar(100), y*SkIntToScalar(30), paint); sigma *= 0.75f; } } return; } paint.setColor(SK_ColorBLUE); #if 1 SkPath p; float r = rand.nextUScalar1() + 0.5f; SkScalar x = 0, y = 0; p.moveTo(x, y); #if 0 p.cubicTo(x-75*r, y+75*r, x-40*r, y+125*r, x, y+85*r); p.cubicTo(x+40*r, y+125*r, x+75*r, y+75*r, x, y); #else p.cubicTo(x+75*r, y+75*r, x+40*r, y+125*r, x, y+85*r); p.cubicTo(x-40*r, y+125*r, x-75*r, y+75*r, x, y); #endif p.close(); fPath = p; fPath.offset(100, 0); #endif fPath.setFillType(SkPath::kWinding_FillType); drawSet(canvas, &paint); canvas->translate(0, fPath.getBounds().height() * 5 / 4); fPath.setFillType(SkPath::kEvenOdd_FillType); drawSet(canvas, &paint); }
static sk_sp<SkPicture> make_tri_picture() { SkPath tri = make_tri_path(SkScalarHalf(kTriSide), 0); SkPaint fill; fill.setStyle(SkPaint::kFill_Style); fill.setColor(sk_tool_utils::color_to_565(SK_ColorLTGRAY)); SkPaint stroke; stroke.setStyle(SkPaint::kStroke_Style); stroke.setStrokeWidth(3); SkPictureRecorder recorder; SkRTreeFactory bbhFactory; SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(kPicWidth), SkIntToScalar(kPicHeight), &bbhFactory); SkRect r = tri.getBounds(); r.outset(2.0f, 2.0f); // outset for stroke canvas->clipRect(r); // The saveLayer/restore block is to exercise layer hoisting canvas->saveLayer(nullptr, nullptr); canvas->drawPath(tri, fill); canvas->drawPath(tri, stroke); canvas->restore(); return recorder.finishRecordingAsPicture(); }
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; }
// ensure that we don't accidentally screw up the bounds when the oval is // fractional, and the impl computes the center and radii, and uses them to // reconstruct the edges of the circle. // see bug# 1504910 static void test_circlebounds(SkCanvas*) { #ifdef SK_SCALAR_IS_FLOAT SkRect r = { 1.39999998f, 1, 21.3999996f, 21 }; SkPath p; p.addOval(r); SkASSERT(r == p.getBounds()); #endif }
SkRect draw(SkCanvas* canvas, const SkPaint& paint) override { SkPath path; path.addCircle(15, 15, 10); path.addOval(SkRect::MakeXYWH(2, 2, 22, 37)); path.setFillType(SkPath::kEvenOdd_FillType); canvas->drawPath(path, paint); return path.getBounds(); }
// Computes the bounding box for the stroke and style currently selected into // the given bounding box. This also takes into account the stroke width. static FloatRect boundingBoxForCurrentStroke(const GraphicsContext* context) { SkPaint paint; context->platformContext()->setupPaintForStroking(&paint, 0, 0); SkPath boundingPath; paint.getFillPath(context->platformContext()->currentPathInLocalCoordinates(), &boundingPath); return boundingPath.getBounds(); }
FloatRect Path::strokeBoundingRect(const StrokeData& strokeData) const { SkPaint paint; strokeData.setupPaint(&paint); SkPath boundingPath; paint.getFillPath(m_path, &boundingPath); return boundingPath.getBounds(); }
void SkDeferredCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) { if (path.isInverseFillType()) { this->flush_before_saves(); } else { SkRect modRect = path.getBounds(); this->flush_check(&modRect, &paint, kNoClip_Flag | kNoTranslate_Flag | kNoScale_Flag); } fCanvas->drawPath(path, paint); }
/* return non-zero if the path is too big, and should be shrunk to avoid overflows during intermediate calculations. Note that we compute the bounds for this. If we had a custom callback/walker for paths, we could perhaps go faster by using that, and just perform the abs | in that routine */ static int needs_to_shrink(const SkPath& path) { const SkRect& r = path.getBounds(); SkFixed mask = SkAbs32(r.fLeft); mask |= SkAbs32(r.fTop); mask |= SkAbs32(r.fRight); mask |= SkAbs32(r.fBottom); // we need the top 3 bits clear (after abs) to avoid overflow return mask >> 29; }