1 /* 2 * Copyright (C) 2015 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 #ifndef CLIPAREA_H 17 #define CLIPAREA_H 18 19 #include "Matrix.h" 20 #include "Rect.h" 21 #include "utils/Pair.h" 22 23 #include <SkRegion.h> 24 25 namespace android { 26 namespace uirenderer { 27 28 class LinearAllocator; 29 30 Rect transformAndCalculateBounds(const Rect& r, const Matrix4& transform); 31 32 class TransformedRectangle { 33 public: 34 TransformedRectangle(); 35 TransformedRectangle(const Rect& bounds, const Matrix4& transform); 36 37 bool canSimplyIntersectWith(const TransformedRectangle& other) const; 38 void intersectWith(const TransformedRectangle& other); 39 40 bool isEmpty() const; 41 42 const Rect& getBounds() const { 43 return mBounds; 44 } 45 46 Rect transformedBounds() const { 47 Rect transformedBounds(transformAndCalculateBounds(mBounds, mTransform)); 48 return transformedBounds; 49 } 50 51 const Matrix4& getTransform() const { 52 return mTransform; 53 } 54 55 void transform(const Matrix4& transform) { 56 Matrix4 t; 57 t.loadMultiply(transform, mTransform); 58 mTransform = t; 59 } 60 61 private: 62 Rect mBounds; 63 Matrix4 mTransform; 64 }; 65 66 class RectangleList { 67 public: 68 RectangleList(); 69 70 bool isEmpty() const; 71 int getTransformedRectanglesCount() const; 72 const TransformedRectangle& getTransformedRectangle(int i) const; 73 74 void setEmpty(); 75 void set(const Rect& bounds, const Matrix4& transform); 76 bool intersectWith(const Rect& bounds, const Matrix4& transform); 77 void transform(const Matrix4& transform); 78 79 SkRegion convertToRegion(const SkRegion& clip) const; 80 Rect calculateBounds() const; 81 82 enum { 83 kMaxTransformedRectangles = 5 84 }; 85 86 private: 87 int mTransformedRectanglesCount; 88 TransformedRectangle mTransformedRectangles[kMaxTransformedRectangles]; 89 }; 90 91 enum class ClipMode { 92 Rectangle, 93 RectangleList, 94 95 // region and path - intersected. if either is empty, don't use 96 Region 97 }; 98 99 struct ClipBase { 100 ClipBase(ClipMode mode) 101 : mode(mode) {} 102 ClipBase(const Rect& rect) 103 : mode(ClipMode::Rectangle) 104 , rect(rect) {} 105 const ClipMode mode; 106 bool intersectWithRoot = false; 107 // Bounds of the clipping area, used to define the scissor, and define which 108 // portion of the stencil is updated/used 109 Rect rect; 110 111 void dump() const; 112 }; 113 114 struct ClipRect : ClipBase { 115 ClipRect(const Rect& rect) 116 : ClipBase(rect) {} 117 }; 118 119 struct ClipRectList : ClipBase { 120 ClipRectList(const RectangleList& rectList) 121 : ClipBase(ClipMode::RectangleList) 122 , rectList(rectList) {} 123 RectangleList rectList; 124 }; 125 126 struct ClipRegion : ClipBase { 127 ClipRegion(const SkRegion& region) 128 : ClipBase(ClipMode::Region) 129 , region(region) {} 130 ClipRegion() 131 : ClipBase(ClipMode::Region) {} 132 SkRegion region; 133 }; 134 135 class ClipArea { 136 public: 137 ClipArea(); 138 139 void setViewportDimensions(int width, int height); 140 141 bool isEmpty() const { 142 return mClipRect.isEmpty(); 143 } 144 145 void setEmpty(); 146 void setClip(float left, float top, float right, float bottom); 147 void clipRectWithTransform(const Rect& r, const mat4* transform, 148 SkRegion::Op op); 149 void clipRegion(const SkRegion& region, SkRegion::Op op); 150 void clipPathWithTransform(const SkPath& path, const mat4* transform, 151 SkRegion::Op op); 152 153 const Rect& getClipRect() const { 154 return mClipRect; 155 } 156 157 const SkRegion& getClipRegion() const { 158 return mClipRegion; 159 } 160 161 const RectangleList& getRectangleList() const { 162 return mRectangleList; 163 } 164 165 bool isRegion() const { 166 return ClipMode::Region == mMode; 167 } 168 169 bool isSimple() const { 170 return mMode == ClipMode::Rectangle; 171 } 172 173 bool isRectangleList() const { 174 return mMode == ClipMode::RectangleList; 175 } 176 177 WARN_UNUSED_RESULT const ClipBase* serializeClip(LinearAllocator& allocator); 178 WARN_UNUSED_RESULT const ClipBase* serializeIntersectedClip(LinearAllocator& allocator, 179 const ClipBase* recordedClip, const Matrix4& recordedClipTransform); 180 void applyClip(const ClipBase* recordedClip, const Matrix4& recordedClipTransform); 181 182 static void applyTransformToRegion(const Matrix4& transform, SkRegion* region); 183 184 private: 185 void enterRectangleMode(); 186 void rectangleModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op); 187 188 void enterRectangleListMode(); 189 void rectangleListModeClipRectWithTransform(const Rect& r, 190 const mat4* transform, SkRegion::Op op); 191 192 void enterRegionModeFromRectangleMode(); 193 void enterRegionModeFromRectangleListMode(); 194 void enterRegionMode(); 195 void regionModeClipRectWithTransform(const Rect& r, const mat4* transform, 196 SkRegion::Op op); 197 198 void ensureClipRegion(); 199 void onClipRegionUpdated(); 200 201 // Called by every state modifying public method. 202 void onClipUpdated() { 203 mPostViewportClipObserved = true; 204 mLastSerialization = nullptr; 205 mLastResolutionResult = nullptr; 206 } 207 208 SkRegion createViewportRegion() { 209 return SkRegion(mViewportBounds.toSkIRect()); 210 } 211 212 void regionFromPath(const SkPath& path, SkRegion& pathAsRegion) { 213 // TODO: this should not mask every path to the viewport - this makes it impossible to use 214 // paths to clip to larger areas (which is valid e.g. with SkRegion::kReplace_Op) 215 pathAsRegion.setPath(path, createViewportRegion()); 216 } 217 218 ClipMode mMode; 219 bool mPostViewportClipObserved = false; 220 bool mReplaceOpObserved = false; 221 222 /** 223 * If mLastSerialization is non-null, it represents an already serialized copy 224 * of the current clip state. If null, it has not been computed. 225 */ 226 const ClipBase* mLastSerialization = nullptr; 227 228 /** 229 * This pair of pointers is a single entry cache of most recently seen 230 */ 231 const ClipBase* mLastResolutionResult = nullptr; 232 const ClipBase* mLastResolutionClip = nullptr; 233 Matrix4 mLastResolutionTransform; 234 235 Rect mViewportBounds; 236 Rect mClipRect; 237 SkRegion mClipRegion; 238 RectangleList mRectangleList; 239 }; 240 241 } /* namespace uirenderer */ 242 } /* namespace android */ 243 244 #endif /* CLIPAREA_H_ */ 245