// check for convexity if (fLastCross*cross < 0) {
fIsConvex = false;
} if (0 != cross) {
fLastCross = cross;
}
returntrue;
}
void SkBaseShadowTessellator::finishPathPolygon() { if (fPathPolygon.size() > 1) { if (!this->accumulateCentroid(fPathPolygon[fPathPolygon.size() - 1], fPathPolygon[0])) { // remove coincident point
fPathPolygon.pop_back();
}
}
if (fPathPolygon.size() > 2) { // do this before the final convexity check, so we use the correct fPathPolygon[0]
fCentroid *= sk_ieee_float_divide(1, 3 * fArea);
fCentroid += fPathPolygon[0]; if (!checkConvexity(fPathPolygon[fPathPolygon.size() - 2],
fPathPolygon[fPathPolygon.size() - 1],
fPathPolygon[0])) { // remove collinear point
fPathPolygon[0] = fPathPolygon[fPathPolygon.size() - 1];
fPathPolygon.pop_back();
}
}
// if area is positive, winding is ccw
fDirection = fArea > 0 ? -1 : 1;
}
// adjust inset distance and umbra color if necessary auto umbraColor = kUmbraColor;
SkScalar minDistSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid,
fPathPolygon[0],
fPathPolygon[1]);
SkRect bounds;
bounds.setBounds(&fPathPolygon[0], fPathPolygon.size()); for (int i = 1; i < fPathPolygon.size(); ++i) { int j = i + 1; if (i == fPathPolygon.size() - 1) {
j = 0;
}
SkPoint currPoint = fPathPolygon[i];
SkPoint nextPoint = fPathPolygon[j];
SkScalar distSq = SkPointPriv::DistanceToLineSegmentBetweenSqd(fCentroid, currPoint,
nextPoint); if (distSq < minDistSq) {
minDistSq = distSq;
}
}
SkTDArray<SkPoint> insetPolygon; if (inset > SK_ScalarNearlyZero) { static constexpr auto kTolerance = 1.0e-2f; if (minDistSq < (inset + kTolerance)*(inset + kTolerance)) { // if the umbra would collapse, we back off a bit on inner blur and adjust the alpha auto newInset = SkScalarSqrt(minDistSq) - kTolerance; auto ratio = 128 * (newInset / inset + 1);
SkASSERT(SkIsFinite(ratio)); // they aren't PMColors, but the interpolation algorithm is the same
umbraColor = SkPMLerp(kUmbraColor, kPenumbraColor, (unsigned)ratio);
inset = newInset;
}
// generate inner ring if (!SkInsetConvexPolygon(&fPathPolygon[0], fPathPolygon.size(), inset,
&insetPolygon)) { // not ideal, but in this case we'll inset using the centroid
fValidUmbra = false;
}
} const SkTDArray<SkPoint>& umbraPolygon = (inset > SK_ScalarNearlyZero) ? insetPolygon
: fPathPolygon;
// walk around the path polygon, generate outer ring and connect to inner ring if (fTransparent) {
fPositions.push_back(fCentroid);
fColors.push_back(umbraColor);
}
fCurrUmbraIndex = 0;
// initial setup // add first quad int polyCount = fPathPolygon.size(); if (!compute_normal(fPathPolygon[polyCount - 1], fPathPolygon[0], fDirection, &fFirstOutset)) { // polygon should be sanitized by this point, so this is unrecoverable returnfalse;
}
// merge "close" points if (fPrevUmbraIndex == -1 ||
!duplicate_pt(umbraPoint, fPositions[fPrevUmbraIndex])) { // if we've wrapped around, don't add a new point if (fPrevUmbraIndex >= 0 && duplicate_pt(umbraPoint, fPositions[fFirstVertexIndex])) {
*currUmbraIndex = fFirstVertexIndex;
} else {
*currUmbraIndex = fPositions.size();
fPositions.push_back(umbraPoint);
fColors.push_back(umbraColor);
} returnfalse;
} else {
*currUmbraIndex = fPrevUmbraIndex; returntrue;
}
}
int SkBaseShadowTessellator::getClosestUmbraIndex(const SkPoint& p, const SkTDArray<SkPoint>& umbraPolygon) {
SkScalar minDistance = SkPointPriv::DistanceToSqd(p, umbraPolygon[fCurrUmbraIndex]); int index = fCurrUmbraIndex; int dir = 1; int next = (index + dir) % umbraPolygon.size();
// init travel direction
SkScalar distance = SkPointPriv::DistanceToSqd(p, umbraPolygon[next]); if (distance < minDistance) {
index = next;
minDistance = distance;
} else {
dir = umbraPolygon.size() - 1;
}
// iterate until we find a point that increases the distance
next = (index + dir) % umbraPolygon.size();
distance = SkPointPriv::DistanceToSqd(p, umbraPolygon[next]); while (distance < minDistance) {
index = next;
minDistance = distance;
next = (index + dir) % umbraPolygon.size();
distance = SkPointPriv::DistanceToSqd(p, umbraPolygon[next]);
}
// shouldn't inset more than the half bounds of the polygon
inset = std::min(inset, std::min(SkTAbs(SkRectPriv::HalfWidth(fPathBounds)),
SkTAbs(SkRectPriv::HalfHeight(fPathBounds)))); // generate inner ring
SkTDArray<SkPoint> umbraPolygon;
SkTDArray<int> umbraIndices;
umbraIndices.reserve(fPathPolygon.size()); if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.size(), fPathBounds, inset,
&umbraPolygon, &umbraIndices)) { // TODO: figure out how to handle this case returnfalse;
}
// generate outer ring
SkTDArray<SkPoint> penumbraPolygon;
SkTDArray<int> penumbraIndices;
penumbraPolygon.reserve(umbraPolygon.size());
penumbraIndices.reserve(umbraPolygon.size()); if (!SkOffsetSimplePolygon(&fPathPolygon[0], fPathPolygon.size(), fPathBounds, -outset,
&penumbraPolygon, &penumbraIndices)) { // TODO: figure out how to handle this case returnfalse;
}
if (umbraPolygon.empty() || penumbraPolygon.empty()) { returnfalse;
}
// attach the rings together
this->stitchConcaveRings(umbraPolygon, &umbraIndices, penumbraPolygon, &penumbraIndices);
returntrue;
}
void SkBaseShadowTessellator::stitchConcaveRings(const SkTDArray<SkPoint>& umbraPolygon,
SkTDArray<int>* umbraIndices, const SkTDArray<SkPoint>& penumbraPolygon,
SkTDArray<int>* penumbraIndices) { // TODO: only create and fill indexMap when fTransparent is true?
AutoSTMalloc<64, uint16_t> indexMap(umbraPolygon.size());
// find minimum indices int minIndex = 0; int min = (*penumbraIndices)[0]; for (int i = 1; i < (*penumbraIndices).size(); ++i) { if ((*penumbraIndices)[i] < min) {
min = (*penumbraIndices)[i];
minIndex = i;
}
} int currPenumbra = minIndex;
minIndex = 0;
min = (*umbraIndices)[0]; for (int i = 1; i < (*umbraIndices).size(); ++i) { if ((*umbraIndices)[i] < min) {
min = (*umbraIndices)[i];
minIndex = i;
}
} int currUmbra = minIndex;
// now find a case where the indices are equal (there should be at least one) int maxPenumbraIndex = fPathPolygon.size() - 1; int maxUmbraIndex = fPathPolygon.size() - 1; while ((*penumbraIndices)[currPenumbra] != (*umbraIndices)[currUmbra]) { if ((*penumbraIndices)[currPenumbra] < (*umbraIndices)[currUmbra]) {
(*penumbraIndices)[currPenumbra] += fPathPolygon.size();
maxPenumbraIndex = (*penumbraIndices)[currPenumbra];
currPenumbra = (currPenumbra + 1) % penumbraPolygon.size();
} else {
(*umbraIndices)[currUmbra] += fPathPolygon.size();
maxUmbraIndex = (*umbraIndices)[currUmbra];
currUmbra = (currUmbra + 1) % umbraPolygon.size();
}
}
int nextPenumbra = (currPenumbra + 1) % penumbraPolygon.size(); int nextUmbra = (currUmbra + 1) % umbraPolygon.size(); while ((*penumbraIndices)[nextPenumbra] <= maxPenumbraIndex ||
(*umbraIndices)[nextUmbra] <= maxUmbraIndex) {
if ((*umbraIndices)[nextUmbra] == (*penumbraIndices)[nextPenumbra]) { // advance both one step
fPositions.push_back(penumbraPolygon[nextPenumbra]);
fColors.push_back(kPenumbraColor); int currPenumbraIndex = fPositions.size() - 1;
fPrevUmbraIndex = currUmbraIndex; // this ensures the ordering when we wrap around
(*umbraIndices)[currUmbra] += fPathPolygon.size();
currUmbra = nextUmbra;
nextUmbra = (currUmbra + 1) % umbraPolygon.size();
}
} // finish up by advancing both one step
fPositions.push_back(penumbraPolygon[nextPenumbra]);
fColors.push_back(kPenumbraColor); int currPenumbraIndex = fPositions.size() - 1;
// clamps the point to the nearest 16th of a pixel staticvoid sanitize_point(const SkPoint& in, SkPoint* out) {
out->fX = SkScalarRoundToScalar(16.f*in.fX)*0.0625f;
out->fY = SkScalarRoundToScalar(16.f*in.fY)*0.0625f;
}
if (!fPathPolygon.empty()) { if (!this->accumulateCentroid(fPathPolygon[fPathPolygon.size() - 1], pSanitized)) { // skip coincident point return;
}
}
if (fPathPolygon.size() > 1) { if (!checkConvexity(fPathPolygon[fPathPolygon.size() - 2],
fPathPolygon[fPathPolygon.size() - 1],
pSanitized)) { // remove collinear point
fPathPolygon.pop_back(); // it's possible that the previous point is coincident with the new one now if (duplicate_pt(fPathPolygon[fPathPolygon.size() - 1], pSanitized)) {
fPathPolygon.pop_back();
}
}
}
fPathPolygon.push_back(pSanitized);
}
void SkBaseShadowTessellator::handleLine(const SkMatrix& m, SkPoint* p) {
m.mapPoints(p, 1);
this->handleLine(*p);
}
void SkBaseShadowTessellator::handleQuad(const SkPoint pts[3]) { #ifdefined(SK_GANESH) // check for degeneracy
SkVector v0 = pts[1] - pts[0];
SkVector v1 = pts[2] - pts[0]; if (SkScalarNearlyZero(v0.cross(v1))) { return;
} // TODO: Pull PathUtils out of Ganesh? int maxCount = GrPathUtils::quadraticPointCount(pts, kQuadTolerance);
fPointBuffer.resize(maxCount);
SkPoint* target = fPointBuffer.begin(); int count = GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
kQuadToleranceSqd, &target, maxCount);
fPointBuffer.resize(count); for (int i = 0; i < count; i++) {
this->handleLine(fPointBuffer[i]);
} #else // for now, just to draw something
this->handleLine(pts[1]);
this->handleLine(pts[2]); #endif
}
SkAmbientShadowTessellator::SkAmbientShadowTessellator(const SkPath& path, const SkMatrix& ctm, const SkPoint3& zPlaneParams, bool transparent)
: INHERITED(zPlaneParams, path.getBounds(), transparent) { // Set base colors auto baseZ = heightFunc(fPathBounds.centerX(), fPathBounds.centerY()); // umbraColor is the interior value, penumbraColor the exterior value. auto outset = SkDrawShadowMetrics::AmbientBlurRadius(baseZ); auto inset = outset * SkDrawShadowMetrics::AmbientRecipAlpha(baseZ) - outset;
if (!this->computePathPolygon(path, ctm)) { return;
} if (fPathPolygon.size() < 3 || !SkIsFinite(fArea)) {
fSucceeded = true; // We don't want to try to blur these cases, so we will // return an empty SkVertices instead. return;
}
// walk around the path, tessellate and generate outer ring // if original path is transparent, will accumulate sum of points for centroid
SkPath::Iter iter(path, true);
SkPoint pts[4];
SkPath::Verb verb; bool verbSeen = false; bool closeSeen = false; while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { if (closeSeen) { returnfalse;
} switch (verb) { case SkPath::kLine_Verb:
this->handleLine(ctm, &pts[1]); break; case SkPath::kQuad_Verb:
this->handleQuad(ctm, pts); break; case SkPath::kCubic_Verb:
this->handleCubic(ctm, pts); break; case SkPath::kConic_Verb:
this->handleConic(ctm, pts, iter.conicWeight()); break; case SkPath::kMove_Verb: if (verbSeen) { returnfalse;
} break; case SkPath::kClose_Verb: case SkPath::kDone_Verb:
closeSeen = true; break;
}
verbSeen = true;
}
// Compute the blur radius, scale and translation for the spot shadow.
SkMatrix shadowTransform;
SkScalar outset; if (!SkDrawShadowMetrics::GetSpotShadowTransform(lightPos, lightRadius, ctm, zPlaneParams,
path.getBounds(), directional,
&shadowTransform, &outset)) { return;
}
SkScalar inset = outset;
// compute rough clip bounds for umbra, plus offset polygon, plus centroid if (!this->computeClipAndPathPolygons(path, ctm, shadowTransform)) { return;
} if (fClipPolygon.size() < 3 || fPathPolygon.size() < 3 || !SkIsFinite(fArea)) {
fSucceeded = true; // We don't want to try to blur these cases, so we will // return an empty SkVertices instead. return;
}
// Walk around the path and compute clip polygon and path polygon. // Will also accumulate sum of areas for centroid. // For Bezier curves, we compute additional interior points on curve.
SkPath::Iter iter(path, true);
SkPoint pts[4];
SkPoint clipPts[4];
SkPath::Verb verb;
// coefficients to compute cubic Bezier at t = 5/16 static constexpr SkScalar kA = 0.32495117187f; static constexpr SkScalar kB = 0.44311523437f; static constexpr SkScalar kC = 0.20141601562f; static constexpr SkScalar kD = 0.03051757812f;
SkPoint curvePoint;
SkScalar w; bool closeSeen = false; bool verbSeen = false; while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { if (closeSeen) { returnfalse;
} switch (verb) { case SkPath::kLine_Verb:
ctm.mapPoints(clipPts, &pts[1], 1);
this->addToClip(clipPts[0]);
this->handleLine(shadowTransform, &pts[1]); break; case SkPath::kQuad_Verb:
ctm.mapPoints(clipPts, pts, 3); // point at t = 1/2
curvePoint.fX = 0.25f*clipPts[0].fX + 0.5f*clipPts[1].fX + 0.25f*clipPts[2].fX;
curvePoint.fY = 0.25f*clipPts[0].fY + 0.5f*clipPts[1].fY + 0.25f*clipPts[2].fY;
this->addToClip(curvePoint);
this->addToClip(clipPts[2]);
this->handleQuad(shadowTransform, pts); break; case SkPath::kConic_Verb:
ctm.mapPoints(clipPts, pts, 3);
w = iter.conicWeight(); // point at t = 1/2
curvePoint.fX = 0.25f*clipPts[0].fX + w*0.5f*clipPts[1].fX + 0.25f*clipPts[2].fX;
curvePoint.fY = 0.25f*clipPts[0].fY + w*0.5f*clipPts[1].fY + 0.25f*clipPts[2].fY;
curvePoint *= SkScalarInvert(0.5f + 0.5f*w);
this->addToClip(curvePoint);
this->addToClip(clipPts[2]);
this->handleConic(shadowTransform, pts, w); break; case SkPath::kCubic_Verb:
ctm.mapPoints(clipPts, pts, 4); // point at t = 5/16
curvePoint.fX = kA*clipPts[0].fX + kB*clipPts[1].fX
+ kC*clipPts[2].fX + kD*clipPts[3].fX;
curvePoint.fY = kA*clipPts[0].fY + kB*clipPts[1].fY
+ kC*clipPts[2].fY + kD*clipPts[3].fY;
this->addToClip(curvePoint); // point at t = 11/16
curvePoint.fX = kD*clipPts[0].fX + kC*clipPts[1].fX
+ kB*clipPts[2].fX + kA*clipPts[3].fX;
curvePoint.fY = kD*clipPts[0].fY + kC*clipPts[1].fY
+ kB*clipPts[2].fY + kA*clipPts[3].fY;
this->addToClip(curvePoint);
this->addToClip(clipPts[3]);
this->handleCubic(shadowTransform, pts); break; case SkPath::kMove_Verb: if (verbSeen) { returnfalse;
} break; case SkPath::kClose_Verb: case SkPath::kDone_Verb:
closeSeen = true; break; default:
SkDEBUGFAIL("unknown verb");
}
verbSeen = true;
}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.20Bemerkung:
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.