1*d57664e9SAndroid Build Coastguard Worker /*
2*d57664e9SAndroid Build Coastguard Worker * Copyright (C) 2020 The Android Open Source Project
3*d57664e9SAndroid Build Coastguard Worker *
4*d57664e9SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*d57664e9SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*d57664e9SAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*d57664e9SAndroid Build Coastguard Worker *
8*d57664e9SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*d57664e9SAndroid Build Coastguard Worker *
10*d57664e9SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*d57664e9SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*d57664e9SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*d57664e9SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*d57664e9SAndroid Build Coastguard Worker * limitations under the License.
15*d57664e9SAndroid Build Coastguard Worker */
16*d57664e9SAndroid Build Coastguard Worker
17*d57664e9SAndroid Build Coastguard Worker #include "CanvasFrontend.h"
18*d57664e9SAndroid Build Coastguard Worker #include "CanvasOps.h"
19*d57664e9SAndroid Build Coastguard Worker #include "CanvasOpBuffer.h"
20*d57664e9SAndroid Build Coastguard Worker
21*d57664e9SAndroid Build Coastguard Worker namespace android::uirenderer {
22*d57664e9SAndroid Build Coastguard Worker
CanvasStateHelper(int width,int height)23*d57664e9SAndroid Build Coastguard Worker CanvasStateHelper::CanvasStateHelper(int width, int height) {
24*d57664e9SAndroid Build Coastguard Worker resetState(width, height);
25*d57664e9SAndroid Build Coastguard Worker }
26*d57664e9SAndroid Build Coastguard Worker
resetState(int width,int height)27*d57664e9SAndroid Build Coastguard Worker void CanvasStateHelper::resetState(int width, int height) {
28*d57664e9SAndroid Build Coastguard Worker mInitialBounds = SkIRect::MakeWH(width, height);
29*d57664e9SAndroid Build Coastguard Worker mSaveStack.clear();
30*d57664e9SAndroid Build Coastguard Worker mClipStack.clear();
31*d57664e9SAndroid Build Coastguard Worker mTransformStack.clear();
32*d57664e9SAndroid Build Coastguard Worker mSaveStack.emplace_back();
33*d57664e9SAndroid Build Coastguard Worker mClipStack.emplace_back();
34*d57664e9SAndroid Build Coastguard Worker mTransformStack.emplace_back();
35*d57664e9SAndroid Build Coastguard Worker
36*d57664e9SAndroid Build Coastguard Worker clip().bounds = mInitialBounds;
37*d57664e9SAndroid Build Coastguard Worker }
38*d57664e9SAndroid Build Coastguard Worker
internalSave(SaveEntry saveEntry)39*d57664e9SAndroid Build Coastguard Worker bool CanvasStateHelper::internalSave(SaveEntry saveEntry) {
40*d57664e9SAndroid Build Coastguard Worker mSaveStack.push_back(saveEntry);
41*d57664e9SAndroid Build Coastguard Worker if (saveEntry.matrix) {
42*d57664e9SAndroid Build Coastguard Worker pushEntry(&mTransformStack);
43*d57664e9SAndroid Build Coastguard Worker }
44*d57664e9SAndroid Build Coastguard Worker if (saveEntry.clip) {
45*d57664e9SAndroid Build Coastguard Worker pushEntry(&mClipStack);
46*d57664e9SAndroid Build Coastguard Worker return true;
47*d57664e9SAndroid Build Coastguard Worker }
48*d57664e9SAndroid Build Coastguard Worker return false;
49*d57664e9SAndroid Build Coastguard Worker }
50*d57664e9SAndroid Build Coastguard Worker
apply(SkClipOp op,const SkMatrix & matrix,const SkRect & bounds,bool aa,bool fillsBounds)51*d57664e9SAndroid Build Coastguard Worker void CanvasStateHelper::ConservativeClip::apply(SkClipOp op, const SkMatrix& matrix,
52*d57664e9SAndroid Build Coastguard Worker const SkRect& bounds, bool aa, bool fillsBounds) {
53*d57664e9SAndroid Build Coastguard Worker this->aa |= aa;
54*d57664e9SAndroid Build Coastguard Worker
55*d57664e9SAndroid Build Coastguard Worker if (op == SkClipOp::kIntersect) {
56*d57664e9SAndroid Build Coastguard Worker SkRect devBounds;
57*d57664e9SAndroid Build Coastguard Worker bool rect = matrix.mapRect(&devBounds, bounds) && fillsBounds;
58*d57664e9SAndroid Build Coastguard Worker if (!this->bounds.intersect(aa ? devBounds.roundOut() : devBounds.round())) {
59*d57664e9SAndroid Build Coastguard Worker this->bounds.setEmpty();
60*d57664e9SAndroid Build Coastguard Worker }
61*d57664e9SAndroid Build Coastguard Worker this->rect &= rect;
62*d57664e9SAndroid Build Coastguard Worker } else {
63*d57664e9SAndroid Build Coastguard Worker // Difference operations subtracts a region from the clip, so conservatively
64*d57664e9SAndroid Build Coastguard Worker // the bounds remain unchanged and the shape is unlikely to remain a rect.
65*d57664e9SAndroid Build Coastguard Worker this->rect = false;
66*d57664e9SAndroid Build Coastguard Worker }
67*d57664e9SAndroid Build Coastguard Worker }
68*d57664e9SAndroid Build Coastguard Worker
internalClipRect(const SkRect & rect,SkClipOp op)69*d57664e9SAndroid Build Coastguard Worker void CanvasStateHelper::internalClipRect(const SkRect& rect, SkClipOp op) {
70*d57664e9SAndroid Build Coastguard Worker clip().apply(op, transform(), rect, /*aa=*/false, /*fillsBounds=*/true);
71*d57664e9SAndroid Build Coastguard Worker }
72*d57664e9SAndroid Build Coastguard Worker
internalClipPath(const SkPath & path,SkClipOp op)73*d57664e9SAndroid Build Coastguard Worker void CanvasStateHelper::internalClipPath(const SkPath& path, SkClipOp op) {
74*d57664e9SAndroid Build Coastguard Worker SkRect bounds = path.getBounds();
75*d57664e9SAndroid Build Coastguard Worker if (path.isInverseFillType()) {
76*d57664e9SAndroid Build Coastguard Worker // Toggle op type if the path is inverse filled
77*d57664e9SAndroid Build Coastguard Worker op = (op == SkClipOp::kIntersect ? SkClipOp::kDifference : SkClipOp::kIntersect);
78*d57664e9SAndroid Build Coastguard Worker }
79*d57664e9SAndroid Build Coastguard Worker clip().apply(op, transform(), bounds, /*aa=*/true, /*fillsBounds=*/false);
80*d57664e9SAndroid Build Coastguard Worker }
81*d57664e9SAndroid Build Coastguard Worker
clip()82*d57664e9SAndroid Build Coastguard Worker CanvasStateHelper::ConservativeClip& CanvasStateHelper::clip() {
83*d57664e9SAndroid Build Coastguard Worker return writableEntry(&mClipStack);
84*d57664e9SAndroid Build Coastguard Worker }
85*d57664e9SAndroid Build Coastguard Worker
transform()86*d57664e9SAndroid Build Coastguard Worker SkMatrix& CanvasStateHelper::transform() {
87*d57664e9SAndroid Build Coastguard Worker return writableEntry(&mTransformStack);
88*d57664e9SAndroid Build Coastguard Worker }
89*d57664e9SAndroid Build Coastguard Worker
internalRestore()90*d57664e9SAndroid Build Coastguard Worker bool CanvasStateHelper::internalRestore() {
91*d57664e9SAndroid Build Coastguard Worker // Prevent underflows
92*d57664e9SAndroid Build Coastguard Worker if (saveCount() <= 1) {
93*d57664e9SAndroid Build Coastguard Worker return false;
94*d57664e9SAndroid Build Coastguard Worker }
95*d57664e9SAndroid Build Coastguard Worker
96*d57664e9SAndroid Build Coastguard Worker SaveEntry entry = mSaveStack[mSaveStack.size() - 1];
97*d57664e9SAndroid Build Coastguard Worker mSaveStack.pop_back();
98*d57664e9SAndroid Build Coastguard Worker bool needsRestorePropagation = entry.layer;
99*d57664e9SAndroid Build Coastguard Worker if (entry.matrix) {
100*d57664e9SAndroid Build Coastguard Worker popEntry(&mTransformStack);
101*d57664e9SAndroid Build Coastguard Worker }
102*d57664e9SAndroid Build Coastguard Worker if (entry.clip) {
103*d57664e9SAndroid Build Coastguard Worker popEntry(&mClipStack);
104*d57664e9SAndroid Build Coastguard Worker needsRestorePropagation = true;
105*d57664e9SAndroid Build Coastguard Worker }
106*d57664e9SAndroid Build Coastguard Worker return needsRestorePropagation;
107*d57664e9SAndroid Build Coastguard Worker }
108*d57664e9SAndroid Build Coastguard Worker
getClipBounds() const109*d57664e9SAndroid Build Coastguard Worker SkRect CanvasStateHelper::getClipBounds() const {
110*d57664e9SAndroid Build Coastguard Worker SkIRect bounds = clip().bounds;
111*d57664e9SAndroid Build Coastguard Worker
112*d57664e9SAndroid Build Coastguard Worker SkMatrix inverse;
113*d57664e9SAndroid Build Coastguard Worker // if we can't invert the CTM, we can't return local clip bounds
114*d57664e9SAndroid Build Coastguard Worker if (bounds.isEmpty() || !transform().invert(&inverse)) {
115*d57664e9SAndroid Build Coastguard Worker return SkRect::MakeEmpty();
116*d57664e9SAndroid Build Coastguard Worker }
117*d57664e9SAndroid Build Coastguard Worker
118*d57664e9SAndroid Build Coastguard Worker return inverse.mapRect(SkRect::Make(bounds));
119*d57664e9SAndroid Build Coastguard Worker }
120*d57664e9SAndroid Build Coastguard Worker
quickReject(const SkMatrix & matrix,const SkRect & bounds) const121*d57664e9SAndroid Build Coastguard Worker bool CanvasStateHelper::ConservativeClip::quickReject(const SkMatrix& matrix,
122*d57664e9SAndroid Build Coastguard Worker const SkRect& bounds) const {
123*d57664e9SAndroid Build Coastguard Worker SkRect devRect = matrix.mapRect(bounds);
124*d57664e9SAndroid Build Coastguard Worker return devRect.isFinite() &&
125*d57664e9SAndroid Build Coastguard Worker SkIRect::Intersects(this->bounds, aa ? devRect.roundOut() : devRect.round());
126*d57664e9SAndroid Build Coastguard Worker }
127*d57664e9SAndroid Build Coastguard Worker
quickRejectRect(float left,float top,float right,float bottom) const128*d57664e9SAndroid Build Coastguard Worker bool CanvasStateHelper::quickRejectRect(float left, float top, float right, float bottom) const {
129*d57664e9SAndroid Build Coastguard Worker return clip().quickReject(transform(), SkRect::MakeLTRB(left, top, right, bottom));
130*d57664e9SAndroid Build Coastguard Worker }
131*d57664e9SAndroid Build Coastguard Worker
quickRejectPath(const SkPath & path) const132*d57664e9SAndroid Build Coastguard Worker bool CanvasStateHelper::quickRejectPath(const SkPath& path) const {
133*d57664e9SAndroid Build Coastguard Worker if (this->isClipEmpty()) {
134*d57664e9SAndroid Build Coastguard Worker // reject everything (prioritized above path inverse fill type).
135*d57664e9SAndroid Build Coastguard Worker return true;
136*d57664e9SAndroid Build Coastguard Worker } else {
137*d57664e9SAndroid Build Coastguard Worker // Don't reject inverse-filled paths, since even if they are "empty" of points/verbs,
138*d57664e9SAndroid Build Coastguard Worker // they fill out the entire clip.
139*d57664e9SAndroid Build Coastguard Worker return !path.isInverseFillType() && clip().quickReject(transform(), path.getBounds());
140*d57664e9SAndroid Build Coastguard Worker }
141*d57664e9SAndroid Build Coastguard Worker }
142*d57664e9SAndroid Build Coastguard Worker
143*d57664e9SAndroid Build Coastguard Worker } // namespace android::uirenderer
144