| /* | |
| * Copyright 2017 Google Inc. | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "src/core/SkClipStackDevice.h" | |
| #include "include/core/SkImageInfo.h" | |
| #include "include/core/SkMatrix.h" | |
| #include "include/core/SkPath.h" | |
| #include "include/core/SkPathBuilder.h" | |
| #include "include/core/SkPoint.h" | |
| #include "include/core/SkRegion.h" | |
| #include "include/core/SkShader.h" | |
| #include "include/private/base/SkAssert.h" | |
| #include "src/core/SkMatrixPriv.h" | |
| #include <utility> | |
| class SkRRect; | |
| enum class SkClipOp; | |
| SkIRect SkClipStackDevice::devClipBounds() const { | |
| SkIRect r = fClipStack.bounds(this->imageInfo().bounds()).roundOut(); | |
| if (!r.isEmpty()) { | |
| SkASSERT(this->imageInfo().bounds().contains(r)); | |
| } | |
| return r; | |
| } | |
| /////////////////////////////////////////////////////////////////////////////////////////////////// | |
| void SkClipStackDevice::pushClipStack() { | |
| fClipStack.save(); | |
| } | |
| void SkClipStackDevice::popClipStack() { | |
| fClipStack.restore(); | |
| } | |
| void SkClipStackDevice::clipRect(const SkRect& rect, SkClipOp op, bool aa) { | |
| fClipStack.clipRect(rect, this->localToDevice(), op, aa); | |
| } | |
| void SkClipStackDevice::clipRRect(const SkRRect& rrect, SkClipOp op, bool aa) { | |
| fClipStack.clipRRect(rrect, this->localToDevice(), op, aa); | |
| } | |
| void SkClipStackDevice::clipPath(const SkPath& path, SkClipOp op, bool aa) { | |
| fClipStack.clipPath(path, this->localToDevice(), op, aa); | |
| } | |
| void SkClipStackDevice::onClipShader(sk_sp<SkShader> shader) { | |
| fClipStack.clipShader(std::move(shader)); | |
| } | |
| void SkClipStackDevice::clipRegion(const SkRegion& rgn, SkClipOp op) { | |
| SkIPoint origin = this->getOrigin(); | |
| SkRegion tmp; | |
| SkPathBuilder builder; | |
| rgn.addBoundaryPath(&builder); | |
| builder.transform(SkMatrix::Translate(-origin)); | |
| fClipStack.clipPath(builder.detach(), SkMatrix::I(), op, false); | |
| } | |
| void SkClipStackDevice::replaceClip(const SkIRect& rect) { | |
| SkRect deviceRect = SkMatrixPriv::MapRect(this->globalToDevice(), SkRect::Make(rect)); | |
| fClipStack.replaceClip(deviceRect, /*doAA=*/false); | |
| } | |
| bool SkClipStackDevice::isClipAntiAliased() const { | |
| SkClipStack::B2TIter iter(fClipStack); | |
| const SkClipStack::Element* element; | |
| while ((element = iter.next()) != nullptr) { | |
| if (element->isAA()) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| bool SkClipStackDevice::isClipWideOpen() const { | |
| return fClipStack.quickContains(SkRect::MakeIWH(this->width(), this->height())); | |
| } | |
| bool SkClipStackDevice::isClipEmpty() const { | |
| return fClipStack.isEmpty(SkIRect::MakeWH(this->width(), this->height())); | |
| } | |
| bool SkClipStackDevice::isClipRect() const { | |
| if (this->isClipWideOpen()) { | |
| return true; | |
| } else if (this->isClipEmpty()) { | |
| return false; | |
| } | |
| SkClipStack::BoundsType boundType; | |
| bool isIntersectionOfRects; | |
| SkRect bounds; | |
| fClipStack.getBounds(&bounds, &boundType, &isIntersectionOfRects); | |
| return isIntersectionOfRects && boundType == SkClipStack::kNormal_BoundsType; | |
| } | |
| void SkClipStackDevice::android_utils_clipAsRgn(SkRegion* rgn) const { | |
| SkClipStack::BoundsType boundType; | |
| bool isIntersectionOfRects; | |
| SkRect bounds; | |
| fClipStack.getBounds(&bounds, &boundType, &isIntersectionOfRects); | |
| if (isIntersectionOfRects && SkClipStack::kNormal_BoundsType == boundType) { | |
| rgn->setRect(bounds.round()); | |
| } else { | |
| SkRegion boundsRgn({0, 0, this->width(), this->height()}); | |
| *rgn = boundsRgn; | |
| SkClipStack::B2TIter iter(fClipStack); | |
| while (auto elem = iter.next()) { | |
| SkPath tmpPath = elem->asDeviceSpacePath(); | |
| SkRegion tmpRgn; | |
| tmpRgn.setPath(tmpPath, boundsRgn); | |
| if (elem->isReplaceOp()) { | |
| // All replace elements are rectangles | |
| // TODO: SkClipStack can be simplified to be I,D,R ops now, which means element | |
| // iteration can be from top of the stack to the most recent replace element. | |
| // When that's done, this loop will be simplifiable. | |
| rgn->setRect(elem->getDeviceSpaceRect().round()); | |
| } else { | |
| rgn->op(tmpRgn, static_cast<SkRegion::Op>(elem->getOp())); | |
| } | |
| } | |
| } | |
| } |