xref: /aosp_15_r20/frameworks/base/libs/hwui/canvas/CanvasFrontend.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
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