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