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 getBounds()42 const Rect& getBounds() const { 43 return mBounds; 44 } 45 transformedBounds()46 Rect transformedBounds() const { 47 Rect transformedBounds(transformAndCalculateBounds(mBounds, mTransform)); 48 return transformedBounds; 49 } 50 getTransform()51 const Matrix4& getTransform() const { 52 return mTransform; 53 } 54 transform(const Matrix4 & transform)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 { ClipBaseClipBase100 ClipBase(ClipMode mode) 101 : mode(mode) {} ClipBaseClipBase102 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 { ClipRectClipRect115 ClipRect(const Rect& rect) 116 : ClipBase(rect) {} 117 }; 118 119 struct ClipRectList : ClipBase { ClipRectListClipRectList120 ClipRectList(const RectangleList& rectList) 121 : ClipBase(ClipMode::RectangleList) 122 , rectList(rectList) {} 123 RectangleList rectList; 124 }; 125 126 struct ClipRegion : ClipBase { ClipRegionClipRegion127 ClipRegion(const SkRegion& region) 128 : ClipBase(ClipMode::Region) 129 , region(region) {} ClipRegionClipRegion130 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 isEmpty()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 getClipRect()153 const Rect& getClipRect() const { 154 return mClipRect; 155 } 156 getClipRegion()157 const SkRegion& getClipRegion() const { 158 return mClipRegion; 159 } 160 getRectangleList()161 const RectangleList& getRectangleList() const { 162 return mRectangleList; 163 } 164 isRegion()165 bool isRegion() const { 166 return ClipMode::Region == mMode; 167 } 168 isSimple()169 bool isSimple() const { 170 return mMode == ClipMode::Rectangle; 171 } 172 isRectangleList()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 private: 183 void enterRectangleMode(); 184 void rectangleModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op); 185 186 void enterRectangleListMode(); 187 void rectangleListModeClipRectWithTransform(const Rect& r, 188 const mat4* transform, SkRegion::Op op); 189 190 void enterRegionModeFromRectangleMode(); 191 void enterRegionModeFromRectangleListMode(); 192 void enterRegionMode(); 193 void regionModeClipRectWithTransform(const Rect& r, const mat4* transform, 194 SkRegion::Op op); 195 196 void ensureClipRegion(); 197 void onClipRegionUpdated(); 198 199 // Called by every state modifying public method. onClipUpdated()200 void onClipUpdated() { 201 mPostViewportClipObserved = true; 202 mLastSerialization = nullptr; 203 mLastResolutionResult = nullptr; 204 } 205 createViewportRegion()206 SkRegion createViewportRegion() { 207 return SkRegion(mViewportBounds.toSkIRect()); 208 } 209 regionFromPath(const SkPath & path,SkRegion & pathAsRegion)210 void regionFromPath(const SkPath& path, SkRegion& pathAsRegion) { 211 // TODO: this should not mask every path to the viewport - this makes it impossible to use 212 // paths to clip to larger areas (which is valid e.g. with SkRegion::kReplace_Op) 213 pathAsRegion.setPath(path, createViewportRegion()); 214 } 215 216 ClipMode mMode; 217 bool mPostViewportClipObserved = false; 218 bool mReplaceOpObserved = false; 219 220 /** 221 * If mLastSerialization is non-null, it represents an already serialized copy 222 * of the current clip state. If null, it has not been computed. 223 */ 224 const ClipBase* mLastSerialization = nullptr; 225 226 /** 227 * This pair of pointers is a single entry cache of most recently seen 228 */ 229 const ClipBase* mLastResolutionResult = nullptr; 230 const ClipBase* mLastResolutionClip = nullptr; 231 Matrix4 mLastResolutionTransform; 232 233 Rect mViewportBounds; 234 Rect mClipRect; 235 SkRegion mClipRegion; 236 RectangleList mRectangleList; 237 }; 238 239 } /* namespace uirenderer */ 240 } /* namespace android */ 241 242 #endif /* CLIPAREA_H_ */ 243