38bool oriented_bbox_intersection(
const Rect&
a,
const Transform& aXform,
45 SkV4 quadA[4], quadB[4];
47 aXform.mapPoints(
a, quadA);
48 bXform.mapPoints(
b, quadB);
53 auto axesX =
skvx::float4(-aXform.matrix().rc(1,0), -aXform.matrix().rc(1,1),
54 -bXform.matrix().rc(1,0), -bXform.matrix().rc(1,1));
55 auto axesY =
skvx::float4(aXform.matrix().rc(0,0), aXform.matrix().rc(0,1),
56 bXform.matrix().rc(0,0), bXform.matrix().rc(0,1));
62 auto aProj0 = quadA[0].
x * axesX + quadA[0].
y * axesY;
63 auto aProj1 = quadA[1].
x * axesX + quadA[1].
y * axesY;
64 auto aProj2 = quadA[2].
x * axesX + quadA[2].
y * axesY;
65 auto aProj3 = quadA[3].
x * axesX + quadA[3].
y * axesY;
67 auto bProj0 = quadB[0].
x * axesX + quadB[0].
y * axesY;
68 auto bProj1 = quadB[1].
x * axesX + quadB[1].
y * axesY;
69 auto bProj2 = quadB[2].
x * axesX + quadB[2].
y * axesY;
70 auto bProj3 = quadB[3].
x * axesX + quadB[3].
y * axesY;
74 auto minA =
min(
min(aProj0, aProj1),
min(aProj2, aProj3));
75 auto maxA =
max(
max(aProj0, aProj1),
max(aProj2, aProj3));
76 auto minB =
min(
min(bProj0, bProj1),
min(bProj2, bProj3));
77 auto maxB =
max(
max(bProj0, bProj1),
max(bProj2, bProj3));
79 auto overlaps = (minB <= maxA) & (minA <= maxB);
153 if (fContainsChecksOnlyBounds || !fOuterBounds.contains(o.
fOuterBounds)) {
157 if (fContainsChecksOnlyBounds) {
165 static constexpr int kMaxPathComparePoints = 16;
172 (
fShape.path().countPoints() <= kMaxPathComparePoints &&
181 Rect localBounds = fLocalToDevice.inverseMapRect(
183 return fShape.conservativeContains(localBounds);
184 }
else if (
fShape.convex()) {
190 fLocalToDevice.inverseMapPoints(deviceQuad, localQuad, 4);
191 for (
int i = 0;
i < 4; ++
i) {
212 enum class ClipCombo {
219 switch(
static_cast<ClipCombo
>(((
int)
a.fOp << 1) | (
int)
b.fOp)) {
222 if (!
a.intersects(
b)) {
224 return SimplifyResult::kEmpty;
225 }
else if (
b.contains(
a)) {
227 return SimplifyResult::kAOnly;
228 }
else if (
a.contains(
b)) {
230 return SimplifyResult::kBOnly;
233 return SimplifyResult::kBoth;
237 if (!
a.intersects(
b)) {
239 return SimplifyResult::kAOnly;
240 }
else if (
b.contains(
a)) {
242 return SimplifyResult::kEmpty;
246 return SimplifyResult::kBoth;
251 if (!
b.intersects(
a)) {
253 return SimplifyResult::kBOnly;
254 }
else if (
a.contains(
b)) {
256 return SimplifyResult::kEmpty;
259 return SimplifyResult::kBoth;
266 return SimplifyResult::kAOnly;
267 }
else if (
b.contains(
a)) {
269 return SimplifyResult::kBOnly;
273 return SimplifyResult::kBoth;
282ClipStack::RawElement::RawElement(
const Rect& deviceBounds,
286 :
Element{shape, localToDevice, op}
287 , fUsageBounds{
Rect::InfiniteInverted()}
288 , fOrder(DrawOrder::kNoIntersection)
289 , fMaxZ(DrawOrder::kClearDepth)
290 , fInvalidatedByIndex(-1) {
345ClipStack::RawElement::operator ClipStack::TransformedShape()
const {
346 return {fLocalToDevice,
fShape, fOuterBounds, fInnerBounds, fOp};
349void ClipStack::RawElement::drawClip(Device*
device) {
353 if (!this->hasPendingDraw()) {
354 SkASSERT(fUsageBounds.isEmptyNegativeOrNaN());
358 SkASSERT(!fUsageBounds.isEmptyNegativeOrNaN());
360 Rect scissor = fUsageBounds.makeRoundOut();
361 Rect drawBounds = fOuterBounds.makeIntersect(scissor);
362 if (!drawBounds.isEmptyNegativeOrNaN()) {
368 DrawOrder order{fMaxZ.next(), fOrder};
374 device->drawClipShape(fLocalToDevice,
376 Clip{drawBounds, drawBounds, scissor.asSkIRect(),
nullptr},
391void ClipStack::RawElement::validate()
const {
395 (fInnerBounds.isEmptyNegativeOrNaN() || fOuterBounds.contains(fInnerBounds)));
398 SkASSERT(!this->hasPendingDraw() || !fUsageBounds.isEmptyNegativeOrNaN());
401void ClipStack::RawElement::markInvalid(
const SaveRecord& current) {
403 fInvalidatedByIndex = current.firstActiveElementIndex();
411void ClipStack::RawElement::restoreValid(
const SaveRecord& current) {
412 if (current.firstActiveElementIndex() < fInvalidatedByIndex) {
413 fInvalidatedByIndex = -1;
417bool ClipStack::RawElement::combine(
const RawElement& other,
const SaveRecord& current) {
419 if (this->hasPendingDraw() || other.hasPendingDraw()) {
431 bool shapeUpdated =
false;
433 if (fLocalToDevice == other.fLocalToDevice) {
434 Rect intersection =
fShape.rect().makeIntersect(other.fShape.rect());
436 SkASSERT(!intersection.isEmptyNegativeOrNaN());
437 fShape.setRect(intersection);
441 (other.fShape.isRect() || other.fShape.isRRect())) {
442 if (fLocalToDevice == other.fLocalToDevice) {
470 fOuterBounds.intersect(other.fOuterBounds);
471 fInnerBounds.intersect(other.fInnerBounds);
473 SkASSERT(!fOuterBounds.isEmptyNegativeOrNaN());
482void ClipStack::RawElement::updateForElement(RawElement* added,
const SaveRecord& current) {
483 if (this->isInvalid()) {
489 switch (Simplify(*
this, *added)) {
490 case SimplifyResult::kEmpty:
492 this->markInvalid(current);
493 added->markInvalid(current);
496 case SimplifyResult::kAOnly:
498 added->markInvalid(current);
501 case SimplifyResult::kBOnly:
503 this->markInvalid(current);
506 case SimplifyResult::kBoth:
509 if (added->combine(*
this, current)) {
511 this->markInvalid(current);
517ClipStack::RawElement::DrawInfluence
518ClipStack::RawElement::testForDraw(
const TransformedShape&
draw)
const {
519 if (this->isInvalid()) {
525 switch(Simplify(*
this,
draw)) {
526 case SimplifyResult::kEmpty:
528 return DrawInfluence::kClipOut;
530 case SimplifyResult::kBOnly:
534 case SimplifyResult::kAOnly:
540 case SimplifyResult::kBoth:
541 return DrawInfluence::kIntersect;
548 const Rect& drawBounds,
551 SkASSERT(!drawBounds.isEmptyNegativeOrNaN());
553 if (!this->hasPendingDraw()) {
578 fOrder = boundsManager->getMostRecentDraw(fOuterBounds).next();
579 fUsageBounds = drawBounds;
585 fUsageBounds.join(drawBounds);
624ClipStack::SaveRecord::SaveRecord(
const Rect& deviceBounds)
625 : fInnerBounds(deviceBounds)
626 , fOuterBounds(deviceBounds)
628 , fStartingElementIndex(0)
629 , fOldestValidIndex(0)
630 , fDeferredSaveCount(0)
634ClipStack::SaveRecord::SaveRecord(
const SaveRecord& prior,
635 int startingElementIndex)
636 : fInnerBounds(prior.fInnerBounds)
637 , fOuterBounds(prior.fOuterBounds)
638 , fShader(prior.fShader)
639 , fStartingElementIndex(startingElementIndex)
640 , fOldestValidIndex(prior.fOldestValidIndex)
641 , fDeferredSaveCount(0)
642 , fStackOp(prior.fStackOp)
643 , fState(prior.fState) {
646 SkASSERT(startingElementIndex >= prior.fStartingElementIndex);
657Rect ClipStack::SaveRecord::scissor(
const Rect& deviceBounds,
const Rect& drawBounds)
const {
662 SkASSERT(deviceBounds.contains(drawBounds));
668 if (!fOuterBounds.intersects(drawBounds)) {
673 return subtract(drawBounds, fInnerBounds,
true);
679 if (fOuterBounds.contains(drawBounds)) {
688void ClipStack::SaveRecord::removeElements(RawElement::Stack* elements, Device*
device) {
689 while (elements->count() > fStartingElementIndex) {
692 elements->back().drawClip(
device);
693 elements->pop_back();
697void ClipStack::SaveRecord::restoreElements(RawElement::Stack* elements) {
702 int i = elements->count() - 1;
703 for (RawElement&
e : elements->ritems()) {
704 if (
i < fOldestValidIndex) {
707 e.restoreValid(*
this);
716 fShader = std::move(shader);
725bool ClipStack::SaveRecord::addElement(RawElement&& toAdd,
726 RawElement::Stack* elements,
737 }
else if (toAdd.shape().isEmpty()) {
741 this->removeElements(elements,
device);
749 Shape outerSaveBounds{fOuterBounds};
750 TransformedShape
save{
kIdentity, outerSaveBounds, fOuterBounds, fInnerBounds, fStackOp,
755 switch (Simplify(
save, toAdd)) {
756 case SimplifyResult::kEmpty:
759 this->removeElements(elements,
device);
762 case SimplifyResult::kAOnly:
766 case SimplifyResult::kBOnly:
769 this->replaceWithElement(std::move(toAdd), elements,
device);
772 case SimplifyResult::kBoth:
782 this->replaceWithElement(std::move(toAdd), elements,
device);
791 fOuterBounds.intersect(toAdd.outerBounds());
792 fInnerBounds.intersect(toAdd.innerBounds());
794 SkASSERT(!fOuterBounds.isEmptyNegativeOrNaN());
800 fOuterBounds =
subtract(fOuterBounds, toAdd.innerBounds(),
true);
801 fInnerBounds =
subtract(fInnerBounds, toAdd.outerBounds(),
false);
807 Rect oldOuter = fOuterBounds;
808 fOuterBounds =
subtract(toAdd.outerBounds(), fInnerBounds,
true);
809 fInnerBounds =
subtract(toAdd.innerBounds(), oldOuter,
false);
814 fOuterBounds.join(toAdd.outerBounds());
815 if (toAdd.innerBounds().area() > fInnerBounds.area()) {
816 fInnerBounds = toAdd.innerBounds();
824 SkASSERT(!fOuterBounds.isEmptyNegativeOrNaN() &&
825 (fInnerBounds.isEmptyNegativeOrNaN() || fOuterBounds.contains(fInnerBounds)));
827 return this->appendElement(std::move(toAdd), elements,
device);
830bool ClipStack::SaveRecord::appendElement(RawElement&& toAdd,
831 RawElement::Stack* elements,
834 int i = elements->count() - 1;
839 int youngestValid = fStartingElementIndex - 1;
842 int oldestValid = elements->count();
846 RawElement* oldestActiveInvalid =
nullptr;
847 int oldestActiveInvalidIndex = elements->count();
849 for (RawElement& existing : elements->ritems()) {
850 if (
i < fOldestValidIndex) {
855 existing.updateForElement(&toAdd, *
this);
857 if (toAdd.isInvalid()) {
858 if (existing.isInvalid()) {
866 }
else if (existing.isInvalid()) {
869 if (
i >= fStartingElementIndex) {
871 oldestActiveInvalid = &existing;
872 oldestActiveInvalidIndex =
i;
877 if (
i > youngestValid) {
886 SkASSERT(oldestValid == elements->count() ||
887 (oldestValid >= fOldestValidIndex && oldestValid < elements->
count()));
888 SkASSERT(youngestValid == fStartingElementIndex - 1 ||
889 (youngestValid >= fStartingElementIndex && youngestValid < elements->
count()));
890 SkASSERT((oldestActiveInvalid && oldestActiveInvalidIndex >= fStartingElementIndex &&
891 oldestActiveInvalidIndex < elements->
count()) || !oldestActiveInvalid);
894 SkASSERT(oldestValid >= fOldestValidIndex);
895 fOldestValidIndex =
std::min(oldestValid, oldestActiveInvalidIndex);
902 int targetCount = youngestValid + 1;
903 if (!oldestActiveInvalid || oldestActiveInvalidIndex >= targetCount) {
906 oldestActiveInvalid =
nullptr;
908 while (elements->count() > targetCount) {
909 SkASSERT(oldestActiveInvalid != &elements->back());
910 elements->back().drawClip(
device);
911 elements->pop_back();
913 if (oldestActiveInvalid) {
914 oldestActiveInvalid->drawClip(
device);
915 *oldestActiveInvalid = std::move(toAdd);
916 }
else if (elements->count() < targetCount) {
917 elements->push_back(std::move(toAdd));
919 elements->back().drawClip(
device);
920 elements->back() = std::move(toAdd);
926void ClipStack::SaveRecord::replaceWithElement(RawElement&& toAdd,
927 RawElement::Stack* elements,
930 fInnerBounds = toAdd.innerBounds();
931 fOuterBounds = toAdd.outerBounds();
932 fStackOp = toAdd.op();
933 fState = toAdd.clipType();
936 int targetCount = fStartingElementIndex + 1;
937 while (elements->count() > targetCount) {
938 elements->back().drawClip(
device);
939 elements->pop_back();
941 if (elements->count() < targetCount) {
942 elements->push_back(std::move(toAdd));
944 elements->back().drawClip(
device);
945 elements->back() = std::move(toAdd);
948 SkASSERT(elements->count() == fStartingElementIndex + 1);
951 fOldestValidIndex = fStartingElementIndex;
969 , fDevice(owningDevice) {
978 fSaves.
back().pushSave();
983 SaveRecord& current = fSaves.
back();
984 if (current.popSave()) {
991 current.removeElements(&fElements, fDevice);
995 fSaves.
back().restoreElements(&fElements);
998Rect ClipStack::deviceBounds()
const {
1003 const SaveRecord& current = this->currentSaveRecord();
1007 return this->deviceBounds();
1012 return subtract(this->deviceBounds(), current.innerBounds(),
true);
1015 return current.outerBounds();
1020ClipStack::SaveRecord& ClipStack::writableSaveRecord(
bool* wasDeferred) {
1021 SaveRecord& current = fSaves.
back();
1022 if (current.canBeUpdated()) {
1024 *wasDeferred =
false;
1029 *wasDeferred =
true;
1041 this->writableSaveRecord(&wasDeferred).addShader(std::move(shader));
1061 RawElement element{this->deviceBounds(), localToDevice, shape, op};
1065 if (element.shape().isEmpty()) {
1075 SaveRecord&
save = this->writableSaveRecord(&wasDeferred);
1077 if (!
save.addElement(std::move(element), &fElements, fDevice)) {
1083 fSaves.
back().pushSave();
1091 bool outsetBoundsForAA,
1093 static const Clip kClippedOut = {
1096 const SaveRecord& cs = this->currentSaveRecord();
1103 const Rect deviceBounds = this->deviceBounds();
1117 if (!
SkIsFinite(origSize.x(), origSize.y())) {
1125 bool infiniteBounds = styledShape->inverted();
1132 if (!infiniteBounds && (styledShape->isLine() ||
any(origSize == 0.f))) {
1138 Rect transformedShapeBounds;
1139 bool shapeInDeviceSpace =
false;
1142 float rendererOutset = outsetBoundsForAA ? localToDevice.
localAARadius(styledShape->bounds())
1145 transformedShapeBounds = deviceBounds;
1146 infiniteBounds =
true;
1150 transformedShapeBounds = styledShape->bounds();
1155 transformedShapeBounds.
outset(localStyleOutset);
1157 if (!style.
isFillStyle() || rendererOutset != 0.0f) {
1160 styledShape.
writable()->setRect(transformedShapeBounds);
1164 transformedShapeBounds = localToDevice.
mapRect(transformedShapeBounds);
1169 transformedShapeBounds.
outset(0.5f);
1172 styledShape.
writable()->setRect(transformedShapeBounds);
1173 shapeInDeviceSpace =
true;
1177 transformedShapeBounds.
intersect(deviceBounds);
1181 if (infiniteBounds) {
1182 drawBounds = deviceBounds;
1183 styledShape.
writable()->setRect(drawBounds);
1184 shapeInDeviceSpace =
true;
1186 drawBounds = transformedShapeBounds;
1193 return Clip(drawBounds, transformedShapeBounds, deviceBounds.
asSkIRect(), cs.shader());
1207 transformedShapeBounds.
intersect(scissor);
1210 return Clip(drawBounds, transformedShapeBounds, scissor.
asSkIRect(), cs.shader());
1227 int i = fElements.
count();
1228 for (
const RawElement&
e : fElements.
ritems()) {
1230 if (
i < cs.oldestElementIndex()) {
1236 auto influence =
e.testForDraw(
draw);
1237 if (influence == RawElement::DrawInfluence::kClipOut) {
1238 outEffectiveElements->
clear();
1241 if (influence == RawElement::DrawInfluence::kIntersect) {
1246 return Clip(drawBounds, transformedShapeBounds, scissor.
asSkIRect(), cs.shader());
1253 if (
clip.isClippedOut()) {
1257 SkDEBUGCODE(
const SaveRecord& cs = this->currentSaveRecord();)
1261 for (
int i = 0;
i < effectiveElements.
size(); ++
i) {
1266 const RawElement*
e =
static_cast<const RawElement*
>(effectiveElements[
i]);
1268 const_cast<RawElement*
>(
e)->updateForDraw(boundsManager,
clip.drawBounds(), z);
1269 maxClipOrder =
std::max(order, maxClipOrder);
1272 return maxClipOrder;
1276 for (
auto&
e : fElements.
items()) {
1282 e.drawClip(fDevice);
SkAssertResult(font.textToGlyphs("Hello", 5, SkTextEncoding::kUTF8, glyphs, std::size(glyphs))==count)
static bool SkIsFinite(T x, Pack... values)
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
static bool subtract(const R &a, const R &b, R *out)
SkDEBUGCODE(SK_SPI) SkThreadID SkGetThreadID()
static void draw(SkCanvas *canvas, SkRect &target, int x, int y)
@ kButt_Cap
no stroke extension
static constexpr SkScalar kW0PlaneDistance
uint32_t getGenerationID() const
static SkRect InnerBounds(const SkRRect &rr)
static SkRRect ConservativeIntersect(const SkRRect &a, const SkRRect &b)
const SkRect & rect() const
bool transform(const SkMatrix &matrix, SkRRect *dst) const
static SkRRect MakeRect(const SkRect &r)
static bool Subtract(const SkRect &a, const SkRect &b, SkRect *out)
SkScalar getInflationRadius() const
bool isHairlineStyle() const
SkPaint::Cap getCap() const
T & emplace_back(Args &&... args)
void initIfNeeded(Args &&... args)
void init(const T &initial)
void clipShader(sk_sp< SkShader > shader)
ClipStack(Device *owningDevice)
CompressedPaintersOrder updateClipStateForDraw(const Clip &clip, const ElementList &effectiveElements, const BoundsManager *, PaintersDepth z)
Clip visitClipStackForDraw(const Transform &, const Geometry &, const SkStrokeRec &, bool outsetBoundsForAA, ElementList *outEffectiveElements) const
void recordDeferredClipDraws()
Rect conservativeBounds() const
void clipShape(const Transform &localToDevice, const Shape &shape, SkClipOp op)
static constexpr PaintersDepth kClearDepth
static constexpr CompressedPaintersOrder kNoIntersection
const Shape & shape() const
static AI Rect InfiniteInverted()
AI Rect makeIntersect(Rect rect) const
AI bool isEmptyNegativeOrNaN() const
static AI Rect WH(float w, float h)
AI bool intersects(ComplementRect comp) const
AI Rect & intersect(Rect rect)
AI SkIRect asSkIRect() const
AI Rect makeRoundOut() const
AI Rect & outset(float outset)
const SkPath & path() const
void setRRect(const SkRRect &rrect)
const SkRRect & rrect() const
void setInverted(bool inverted)
void setRect(const SkRect &rect)
static float max(float r, float g, float b)
static float min(float r, float g, float b)
SK_API sk_sp< SkShader > Blend(SkBlendMode mode, sk_sp< SkShader > dst, sk_sp< SkShader > src)
constexpr std::array< float, 9 > kIdentity
static constexpr int kElementStackIncrement
MonotonicValue< PaintersDepthSequence > PaintersDepth
static constexpr int kSaveStackIncrement
MonotonicValue< CompressedPaintersOrderSequence > CompressedPaintersOrder
constexpr bool contains(std::string_view str, std::string_view needle)
skgpu::graphite::Transform Transform
SIT bool all(const Vec< 1, T > &x)
SIT bool any(const Vec< 1, T > &x)
static constexpr SkIRect MakeEmpty()
static SKVX_ALWAYS_INLINE Vec Load(const void *ptr)