blob: 112ac124ffa1304e7e81460f8b37769f3b729a43 [file] [log] [blame]
John Reckdc95f102020-11-16 12:35:02 -05001/*
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
21namespace android::uirenderer {
22
23CanvasStateHelper::CanvasStateHelper(int width, int height) {
John Reckb5eeb182020-12-09 13:45:39 -050024 resetState(width, height);
25}
26
27void CanvasStateHelper::resetState(int width, int height) {
John Reckdc95f102020-11-16 12:35:02 -050028 mInitialBounds = SkIRect::MakeWH(width, height);
John Reckb5eeb182020-12-09 13:45:39 -050029 mSaveStack.clear();
30 mClipStack.clear();
31 mTransformStack.clear();
John Reckdc95f102020-11-16 12:35:02 -050032 mSaveStack.emplace_back();
Michael Ludwig8c5c96f2021-08-18 01:36:21 +000033 mClipStack.emplace_back();
John Reckdc95f102020-11-16 12:35:02 -050034 mTransformStack.emplace_back();
Michael Ludwig8c5c96f2021-08-18 01:36:21 +000035
36 clip().bounds = mInitialBounds;
John Reckdc95f102020-11-16 12:35:02 -050037}
38
39bool CanvasStateHelper::internalSave(SaveEntry saveEntry) {
40 mSaveStack.push_back(saveEntry);
41 if (saveEntry.matrix) {
Michael Ludwig8c5c96f2021-08-18 01:36:21 +000042 pushEntry(&mTransformStack);
John Reckdc95f102020-11-16 12:35:02 -050043 }
44 if (saveEntry.clip) {
Michael Ludwig8c5c96f2021-08-18 01:36:21 +000045 pushEntry(&mClipStack);
John Reckdc95f102020-11-16 12:35:02 -050046 return true;
47 }
48 return false;
49}
50
Michael Ludwig8c5c96f2021-08-18 01:36:21 +000051void 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}
John Reckdc95f102020-11-16 12:35:02 -050068
69void CanvasStateHelper::internalClipRect(const SkRect& rect, SkClipOp op) {
Michael Ludwig8c5c96f2021-08-18 01:36:21 +000070 clip().apply(op, transform(), rect, /*aa=*/false, /*fillsBounds=*/true);
John Reckdc95f102020-11-16 12:35:02 -050071}
72
73void CanvasStateHelper::internalClipPath(const SkPath& path, SkClipOp op) {
Michael Ludwig8c5c96f2021-08-18 01:36:21 +000074 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
82CanvasStateHelper::ConservativeClip& CanvasStateHelper::clip() {
83 return writableEntry(&mClipStack);
84}
85
86SkMatrix& CanvasStateHelper::transform() {
87 return writableEntry(&mTransformStack);
John Reckdc95f102020-11-16 12:35:02 -050088}
89
90bool 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) {
Michael Ludwig8c5c96f2021-08-18 01:36:21 +0000100 popEntry(&mTransformStack);
John Reckdc95f102020-11-16 12:35:02 -0500101 }
102 if (entry.clip) {
Michael Ludwig8c5c96f2021-08-18 01:36:21 +0000103 popEntry(&mClipStack);
John Reckdc95f102020-11-16 12:35:02 -0500104 needsRestorePropagation = true;
105 }
106 return needsRestorePropagation;
107}
108
109SkRect CanvasStateHelper::getClipBounds() const {
Michael Ludwig8c5c96f2021-08-18 01:36:21 +0000110 SkIRect bounds = clip().bounds;
John Reckdc95f102020-11-16 12:35:02 -0500111
112 SkMatrix inverse;
113 // if we can't invert the CTM, we can't return local clip bounds
Michael Ludwig8c5c96f2021-08-18 01:36:21 +0000114 if (bounds.isEmpty() || !transform().invert(&inverse)) {
John Reckdc95f102020-11-16 12:35:02 -0500115 return SkRect::MakeEmpty();
116 }
117
Michael Ludwig8c5c96f2021-08-18 01:36:21 +0000118 return inverse.mapRect(SkRect::Make(bounds));
119}
120
121bool 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());
John Reckdc95f102020-11-16 12:35:02 -0500126}
127
128bool CanvasStateHelper::quickRejectRect(float left, float top, float right, float bottom) const {
Michael Ludwig8c5c96f2021-08-18 01:36:21 +0000129 return clip().quickReject(transform(), SkRect::MakeLTRB(left, top, right, bottom));
John Reckdc95f102020-11-16 12:35:02 -0500130}
131
132bool CanvasStateHelper::quickRejectPath(const SkPath& path) const {
Michael Ludwig8c5c96f2021-08-18 01:36:21 +0000133 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 }
John Reckdc95f102020-11-16 12:35:02 -0500141}
142
143} // namespace android::uirenderer