void FatBits::drawLineSkeleton(SkCanvas* max, const SkPoint pts[]) { SkPaint paint; this->setupSkeletonPaint(&paint); SkPath path; path.moveTo(pts[0]); path.lineTo(pts[1]); switch (fStyle) { case kHair_Style: if (fUseGPU) { SkPaint p; p.setStyle(SkPaint::kStroke_Style); p.setStrokeWidth(SK_Scalar1 * fZoom); SkPath dst; p.getFillPath(path, &dst); path.addPath(dst); } break; case kStroke_Style: { SkPaint p; p.setStyle(SkPaint::kStroke_Style); p.setStrokeWidth(SK_Scalar1 * fZoom); SkPath dst; p.getFillPath(path, &dst); path = dst; if (fUseGPU) { path.moveTo(dst.getPoint(0)); path.lineTo(dst.getPoint(2)); } } break; } max->drawPath(path, 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); }
// Draws the given text string using skia. Note that gradient or // pattern may be NULL, in which case a solid colour is used. static bool skiaDrawText(HFONT hfont, HDC dc, SkCanvas* canvas, const SkPoint& point, SkPaint* paint, const WORD* glyphs, const int* advances, const GOFFSET* offsets, int numGlyphs) { float x = point.fX, y = point.fY; for (int i = 0; i < numGlyphs; i++) { const SkPath* path = SkiaWinOutlineCache::lookupOrCreatePathForGlyph(dc, hfont, glyphs[i]); if (!path) return false; float offsetX = 0.0f, offsetY = 0.0f; if (offsets && (offsets[i].du != 0 || offsets[i].dv != 0)) { offsetX = offsets[i].du; offsetY = offsets[i].dv; } SkPath newPath; newPath.addPath(*path, x + offsetX, y + offsetY); canvas->drawPath(newPath, *paint); x += advances[i]; } return true; }
static bool innerPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b, const SkPathOp shapeOp, const char* testName, bool threaded) { #if DEBUG_SHOW_TEST_NAME if (testName == NULL) { SkDebugf("\n"); showPathData(a); showOp(shapeOp); showPathData(b); } else { SkPathOpsDebug::ShowPath(a, b, shapeOp, testName); } #endif SkPath out; if (!Op(a, b, shapeOp, &out) ) { SkDebugf("%s did not expect failure\n", __FUNCTION__); REPORTER_ASSERT(reporter, 0); return false; } if (threaded && !reporter->verbose()) { return true; } SkPath pathOut, scaledPathOut; SkRegion rgnA, rgnB, openClip, rgnOut; openClip.setRect(-16000, -16000, 16000, 16000); rgnA.setPath(a, openClip); rgnB.setPath(b, openClip); rgnOut.op(rgnA, rgnB, (SkRegion::Op) shapeOp); rgnOut.getBoundaryPath(&pathOut); SkMatrix scale; scaleMatrix(a, b, scale); SkRegion scaledRgnA, scaledRgnB, scaledRgnOut; SkPath scaledA, scaledB; scaledA.addPath(a, scale); scaledA.setFillType(a.getFillType()); scaledB.addPath(b, scale); scaledB.setFillType(b.getFillType()); scaledRgnA.setPath(scaledA, openClip); scaledRgnB.setPath(scaledB, openClip); scaledRgnOut.op(scaledRgnA, scaledRgnB, (SkRegion::Op) shapeOp); scaledRgnOut.getBoundaryPath(&scaledPathOut); SkBitmap bitmap; SkPath scaledOut; scaledOut.addPath(out, scale); scaledOut.setFillType(out.getFillType()); int result = comparePaths(reporter, pathOut, scaledPathOut, out, scaledOut, bitmap, a, b, shapeOp, scale); if (result && gPathStrAssert) { REPORTER_ASSERT(reporter, 0); } reporter->bumpTestCount(); return result == 0; }
static bool innerPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b, const SkPathOp shapeOp, const char* testName, ExpectSuccess expectSuccess, SkipAssert skipAssert, ExpectMatch expectMatch) { #if 0 && DEBUG_SHOW_TEST_NAME showName(a, b, shapeOp); #endif SkPath out; if (!OpDebug(a, b, shapeOp, &out SkDEBUGPARAMS(SkipAssert::kYes == skipAssert) SkDEBUGPARAMS(testName))) { if (ExpectSuccess::kYes == expectSuccess) { SkDebugf("%s %s did not expect failure\n", __FUNCTION__, testName); REPORTER_ASSERT(reporter, 0); } return false; } else { if (ExpectSuccess::kNo == expectSuccess) { SkDebugf("%s %s unexpected success\n", __FUNCTION__, testName); REPORTER_ASSERT(reporter, 0); } } if (!reporter->verbose()) { return true; } SkPath pathOut, scaledPathOut; SkRegion rgnA, rgnB, openClip, rgnOut; openClip.setRect(-16000, -16000, 16000, 16000); rgnA.setPath(a, openClip); rgnB.setPath(b, openClip); rgnOut.op(rgnA, rgnB, (SkRegion::Op) shapeOp); rgnOut.getBoundaryPath(&pathOut); SkMatrix scale; scaleMatrix(a, b, scale); SkRegion scaledRgnA, scaledRgnB, scaledRgnOut; SkPath scaledA, scaledB; scaledA.addPath(a, scale); scaledA.setFillType(a.getFillType()); scaledB.addPath(b, scale); scaledB.setFillType(b.getFillType()); scaledRgnA.setPath(scaledA, openClip); scaledRgnB.setPath(scaledB, openClip); scaledRgnOut.op(scaledRgnA, scaledRgnB, (SkRegion::Op) shapeOp); scaledRgnOut.getBoundaryPath(&scaledPathOut); SkBitmap bitmap; SkPath scaledOut; scaledOut.addPath(out, scale); scaledOut.setFillType(out.getFillType()); int result = comparePaths(reporter, testName, pathOut, scaledPathOut, out, scaledOut, bitmap, a, b, shapeOp, scale, ExpectMatch::kYes == expectMatch); reporter->bumpTestCount(); return result == 0; }
static void _update_path(mbe_t *mbe) { SkPath *path = mbe->path; SkPath *subpath = mbe->subpath; SkMatrix canvas_matrix; SkPoint point; MB_MATRIX_2_SKMATRIX(canvas_matrix, mbe->states->matrix); path->addPath(*subpath, canvas_matrix); subpath->getLastPt(&point); subpath->rewind(); subpath->moveTo(point); }
void Path::addArc(const FloatPoint& p, float r, float sa, float ea, bool clockwise) { SkScalar cx = SkFloatToScalar(p.x()); SkScalar cy = SkFloatToScalar(p.y()); SkScalar radius = SkFloatToScalar(r); SkRect oval; oval.set(cx - radius, cy - radius, cx + radius, cy + radius); float sweep = ea - sa; bool prependOval = false; /* Note if clockwise and the sign of the sweep disagree. This particular logic was deduced from http://canvex.lazyilluminati.com/misc/arc.html */ if (clockwise && (sweep > 0 || sweep < -g2PI)) { sweep = fmodf(sweep, g2PI) - g2PI; } else if (!clockwise && (sweep < 0 || sweep > g2PI)) { sweep = fmodf(sweep, g2PI) + g2PI; } // If the abs(sweep) >= 2PI, then we need to add a circle before we call // arcTo, since it treats the sweep mod 2PI. We don't have a prepend call, // so we just remember this, and at the end create a new path with an oval // and our current path, and then swap then. // if (sweep >= g2PI || sweep <= -g2PI) { prependOval = true; // SkDebugf("addArc sa=%g ea=%g cw=%d sweep %g treat as circle\n", sa, ea, clockwise, sweep); // now reduce sweep to just the amount we need, so that the current // point is left where the caller expects it. sweep = fmodf(sweep, g2PI); } sa = fast_mod(sa, g2PI); SkScalar startDegrees = SkFloatToScalar(sa * g180OverPI); SkScalar sweepDegrees = SkFloatToScalar(sweep * g180OverPI); // SkDebugf("addArc sa=%g ea=%g cw=%d sweep=%g ssweep=%g\n", sa, ea, clockwise, sweep, SkScalarToFloat(sweepDegrees)); m_path->arcTo(oval, startDegrees, sweepDegrees, false); if (prependOval) { SkPath tmp; tmp.addOval(oval); tmp.addPath(*m_path); m_path->swap(tmp); } }
void mbe_arc(mbe_t *mbe, co_aix x, co_aix y, co_aix radius, co_aix angle_start, co_aix angle_stop) { SkPoint point; SkPath *subpath = mbe->subpath; SkRect rect; SkScalar x0, y0; SkScalar ang_start, ang_stop; SkScalar sweep; SkScalar r; /* radius */ subpath->getLastPt(&point); x0 = point.fX; y0 = point.fX; r = CO_AIX_2_SKSCALAR(radius); ang_start = CO_AIX_2_SKSCALAR(angle_start * 180 / PI); ang_stop = CO_AIX_2_SKSCALAR(angle_stop * 180 / PI); /* Skia can only draw an arc in clockwise directly. We negative * start and stop point to draw the arc in the mirror along x-axis * in a sub-path. Then, the sub-path are reflected along x-axis, * again. We get a right path, and add it to the path of mbe_t. */ if(ang_start > ang_stop) { SkPath tmppath; SkMatrix matrix; co_aix reflect[6] = { 1, 0, 0, 0, -1, 0}; rect.set(-r, -r, r, r); sweep = ang_start - ang_stop; tmppath.arcTo(rect, -ang_start, sweep, false); reflect[2] = x; reflect[5] = y; MB_MATRIX_2_SKMATRIX(matrix, reflect); subpath->addPath(tmppath, matrix); } else { rect.set(x0 - r, y0 - r, x0 + r, y0 + r); sweep = ang_stop - ang_start; subpath->arcTo(rect, ang_start, sweep, false); } }
void TextArt::EnvelopeWarp::morph(SkPath& bSkeleton, SkPathMeasure& bMeasure, SkPathCrossing& bCrossing, SkPath& tSkeleton, SkPathMeasure& tMeasure, SkPathCrossing& tCrossing, SkPath& glypthPath, SkPathMeasure& lineMeasure, SkMatrix& scaleMatrix, SkScalar xpos, SkScalar hBOffset, SkScalar hTOffset, SkPath& warpedPath) { SkRect glypthBound; glypthBound = glypthPath.getBounds(); SkScalar k1 = 1.0; if (!isSymmetric_) { SkScalar hBOffsetTmp = 0.0; SkScalar hTOffsetTmp = 0.0; if (isTopBased_) { glypthBound.fTop = 0; glypthBound.fBottom = -boundsRect_.height(); SkMatrix compositeTMatrix(scaleMatrix); compositeTMatrix.postTranslate(xpos + hTOffset, 0); compositeTMatrix.postConcat(matrix_); if ( getK(glypthBound, bCrossing, tCrossing, tMeasure, compositeTMatrix, k1, hBOffsetTmp, hTOffsetTmp) ) { k1 = 1/k1; hBOffset = hBOffsetTmp; hTOffset = xpos + hTOffset; } } else { glypthBound.fTop = -boundsRect_.height(); glypthBound.fBottom = 0; SkMatrix compositeBMatrix(scaleMatrix); compositeBMatrix.postTranslate(xpos + hBOffset, 0); compositeBMatrix.postConcat(matrix_); if ( getK(glypthBound, bCrossing, tCrossing, bMeasure, compositeBMatrix, k1, hBOffsetTmp, hTOffsetTmp) ) { //use distance to Left on Top Skeleton for positioning Top glypthn hTOffset = hTOffsetTmp; hBOffset = xpos + hBOffset; } } } else { hBOffset = xpos + hBOffset; hTOffset = hBOffset; } SkMatrix compositeBMatrix(scaleMatrix); compositeBMatrix.postTranslate(hBOffset, 0); compositeBMatrix.postConcat(matrix_); //warp Glypth by bottom line k1_ = isTopBased_ ? k1 : SkIntToScalar(1); isTop = false; SkPath bWarped; morphpath(&bWarped, glypthPath, bMeasure, compositeBMatrix); bWarped_.addPath(bWarped); if (!tSkeleton_.isEmpty()) { SkMatrix compositeTMatrix(scaleMatrix); compositeTMatrix.postTranslate(hTOffset, 0); compositeTMatrix.postConcat(matrix_); //warp Glypth by top line k1_ = !isTopBased_ ? k1 : SkIntToScalar(1); isTop = true; SkPath tWarped; morphpath(&tWarped, glypthPath, tMeasure, compositeTMatrix); tWarped_.addPath(tWarped); //convert Glypth to Path to allow weighting SkPath lineMorphed; morphpath(&lineMorphed, glypthPath, lineMeasure, scaleMatrix); weight(lineMorphed, tWarped, bWarped, &warpedPath); } else warpedPath.addPath(bWarped_); }
SkPath makePath() { SkPath path; for (uint32_t cIndex = 0; cIndex < fPathContourCount; ++cIndex) { uint32_t segments = makeSegmentCount(); for (uint32_t sIndex = 0; sIndex < segments; ++sIndex) { RandomAddPath addPathType = makeAddPathType(); ++fAddCount; if (fPrintName) { SkDebugf("%.*s%s\n", fPathDepth * 3, fTab, gRandomAddPathNames[addPathType]); } switch (addPathType) { case kAddArc: { SkRect oval = makeRect(); SkScalar startAngle = makeAngle(); SkScalar sweepAngle = makeAngle(); path.addArc(oval, startAngle, sweepAngle); validate(path); } break; case kAddRoundRect1: { SkRect rect = makeRect(); SkScalar rx = makeScalar(), ry = makeScalar(); SkPath::Direction dir = makeDirection(); path.addRoundRect(rect, rx, ry, dir); validate(path); } break; case kAddRoundRect2: { SkRect rect = makeRect(); SkScalar radii[8]; makeScalarArray(SK_ARRAY_COUNT(radii), radii); SkPath::Direction dir = makeDirection(); path.addRoundRect(rect, radii, dir); validate(path); } break; case kAddRRect: { SkRRect rrect = makeRRect(); SkPath::Direction dir = makeDirection(); path.addRRect(rrect, dir); validate(path); } break; case kAddPoly: { SkTDArray<SkPoint> points; makePointArray(&points); bool close = makeBool(); path.addPoly(&points[0], points.count(), close); validate(path); } break; case kAddPath1: if (fPathDepth < fPathDepthLimit) { ++fPathDepth; SkPath src = makePath(); validate(src); SkScalar dx = makeScalar(); SkScalar dy = makeScalar(); SkPath::AddPathMode mode = makeAddPathMode(); path.addPath(src, dx, dy, mode); --fPathDepth; validate(path); } break; case kAddPath2: if (fPathDepth < fPathDepthLimit) { ++fPathDepth; SkPath src = makePath(); validate(src); SkPath::AddPathMode mode = makeAddPathMode(); path.addPath(src, mode); --fPathDepth; validate(path); } break; case kAddPath3: if (fPathDepth < fPathDepthLimit) { ++fPathDepth; SkPath src = makePath(); validate(src); SkMatrix matrix = makeMatrix(); SkPath::AddPathMode mode = makeAddPathMode(); path.addPath(src, matrix, mode); --fPathDepth; validate(path); } break; case kReverseAddPath: if (fPathDepth < fPathDepthLimit) { ++fPathDepth; SkPath src = makePath(); validate(src); path.reverseAddPath(src); --fPathDepth; validate(path); } break; case kMoveToPath: { SkScalar x = makeScalar(); SkScalar y = makeScalar(); path.moveTo(x, y); validate(path); } break; case kRMoveToPath: { SkScalar x = makeScalar(); SkScalar y = makeScalar(); path.rMoveTo(x, y); validate(path); } break; case kLineToPath: { SkScalar x = makeScalar(); SkScalar y = makeScalar(); path.lineTo(x, y); validate(path); } break; case kRLineToPath: { SkScalar x = makeScalar(); SkScalar y = makeScalar(); path.rLineTo(x, y); validate(path); } break; case kQuadToPath: { SkPoint pt[2]; makePointArray(SK_ARRAY_COUNT(pt), pt); path.quadTo(pt[0], pt[1]); validate(path); } break; case kRQuadToPath: { SkPoint pt[2]; makePointArray(SK_ARRAY_COUNT(pt), pt); path.rQuadTo(pt[0].fX, pt[0].fY, pt[1].fX, pt[1].fY); validate(path); } break; case kConicToPath: { SkPoint pt[2]; makePointArray(SK_ARRAY_COUNT(pt), pt); SkScalar weight = makeScalar(); path.conicTo(pt[0], pt[1], weight); validate(path); } break; case kRConicToPath: { SkPoint pt[2]; makePointArray(SK_ARRAY_COUNT(pt), pt); SkScalar weight = makeScalar(); path.rConicTo(pt[0].fX, pt[0].fY, pt[1].fX, pt[1].fY, weight); validate(path); } break; case kCubicToPath: { SkPoint pt[3]; makePointArray(SK_ARRAY_COUNT(pt), pt); path.cubicTo(pt[0], pt[1], pt[2]); validate(path); } break; case kRCubicToPath: { SkPoint pt[3]; makePointArray(SK_ARRAY_COUNT(pt), pt); path.rCubicTo(pt[0].fX, pt[0].fY, pt[1].fX, pt[1].fY, pt[2].fX, pt[2].fY); validate(path); } break; case kArcToPath: { SkPoint pt[2]; makePointArray(SK_ARRAY_COUNT(pt), pt); SkScalar radius = makeScalar(); path.arcTo(pt[0], pt[1], radius); validate(path); } break; case kArcTo2Path: { SkRect oval = makeRect(); SkScalar startAngle = makeAngle(); SkScalar sweepAngle = makeAngle(); bool forceMoveTo = makeBool(); path.arcTo(oval, startAngle, sweepAngle, forceMoveTo); validate(path); } break; case kClosePath: path.close(); validate(path); break; } } } return path; }
/* OPTIMIZATION: Union doesn't need to be all-or-nothing. A run of three or more convex paths with union ops could be locally resolved and still improve over doing the ops one at a time. */ bool SkOpBuilder::resolve(SkPath* result) { SkPath original = *result; int count = fOps.count(); bool allUnion = true; SkPathPriv::FirstDirection firstDir = SkPathPriv::kUnknown_FirstDirection; for (int index = 0; index < count; ++index) { SkPath* test = &fPathRefs[index]; if (kUnion_SkPathOp != fOps[index] || test->isInverseFillType()) { allUnion = false; break; } // If all paths are convex, track direction, reversing as needed. if (test->isConvex()) { SkPathPriv::FirstDirection dir; if (!SkPathPriv::CheapComputeFirstDirection(*test, &dir)) { allUnion = false; break; } if (firstDir == SkPathPriv::kUnknown_FirstDirection) { firstDir = dir; } else if (firstDir != dir) { SkPath temp; temp.reverseAddPath(*test); *test = temp; } continue; } // If the path is not convex but its bounds do not intersect the others, simplify is enough. const SkRect& testBounds = test->getBounds(); for (int inner = 0; inner < index; ++inner) { // OPTIMIZE: check to see if the contour bounds do not intersect other contour bounds? if (SkRect::Intersects(fPathRefs[inner].getBounds(), testBounds)) { allUnion = false; break; } } } if (!allUnion) { *result = fPathRefs[0]; for (int index = 1; index < count; ++index) { if (!Op(*result, fPathRefs[index], fOps[index], result)) { reset(); *result = original; return false; } } reset(); return true; } SkPath sum; for (int index = 0; index < count; ++index) { if (!Simplify(fPathRefs[index], &fPathRefs[index])) { reset(); *result = original; return false; } if (!fPathRefs[index].isEmpty()) { // convert the even odd result back to winding form before accumulating it if (!FixWinding(&fPathRefs[index])) { *result = original; return false; } sum.addPath(fPathRefs[index]); } } reset(); bool success = Simplify(sum, result); if (!success) { *result = original; } return success; }