• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 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 package android.graphics;
18 
19 import com.android.ide.common.rendering.api.LayoutLog;
20 import com.android.layoutlib.bridge.Bridge;
21 import com.android.layoutlib.bridge.impl.DelegateManager;
22 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
23 
24 import android.os.Parcel;
25 
26 import java.awt.Rectangle;
27 import java.awt.Shape;
28 import java.awt.geom.AffineTransform;
29 import java.awt.geom.Area;
30 import java.awt.geom.Rectangle2D;
31 
32 /**
33  * Delegate implementing the native methods of android.graphics.Region
34  *
35  * Through the layoutlib_create tool, the original native methods of Region have been replaced
36  * by calls to methods of the same name in this delegate class.
37  *
38  * This class behaves like the original native implementation, but in Java, keeping previously
39  * native data into its own objects and mapping them to int that are sent back and forth between
40  * it and the original Region class.
41  *
42  * This also serve as a base class for all Region delegate classes.
43  *
44  * @see DelegateManager
45  *
46  */
47 public class Region_Delegate {
48 
49     // ---- delegate manager ----
50     protected static final DelegateManager<Region_Delegate> sManager =
51             new DelegateManager<Region_Delegate>(Region_Delegate.class);
52 
53     // ---- delegate helper data ----
54 
55     // ---- delegate data ----
56     private Area mArea = new Area();
57 
58     // ---- Public Helper methods ----
59 
getDelegate(int nativeShader)60     public static Region_Delegate getDelegate(int nativeShader) {
61         return sManager.getDelegate(nativeShader);
62     }
63 
getJavaArea()64     public Area getJavaArea() {
65         return mArea;
66     }
67 
68     /**
69      * Combines two {@link Shape} into another one (actually an {@link Area}), according
70      * to the given {@link Region.Op}.
71      *
72      * If the Op is not one that combines two shapes, then this return null
73      *
74      * @param shape1 the firt shape to combine which can be null if there's no original clip.
75      * @param shape2 the 2nd shape to combine
76      * @param regionOp the operande for the combine
77      * @return a new area or null.
78      */
combineShapes(Shape shape1, Shape shape2, int regionOp)79     public static Area combineShapes(Shape shape1, Shape shape2, int regionOp) {
80         if (regionOp == Region.Op.DIFFERENCE.nativeInt) {
81             // if shape1 is null (empty), then the result is null.
82             if (shape1 == null) {
83                 return null;
84             }
85 
86             // result is always a new area.
87             Area result = new Area(shape1);
88             result.subtract(shape2 instanceof Area ? (Area) shape2 : new Area(shape2));
89             return result;
90 
91         } else if (regionOp == Region.Op.INTERSECT.nativeInt) {
92             // if shape1 is null, then the result is simply shape2.
93             if (shape1 == null) {
94                 return new Area(shape2);
95             }
96 
97             // result is always a new area.
98             Area result = new Area(shape1);
99             result.intersect(shape2 instanceof Area ? (Area) shape2 : new Area(shape2));
100             return result;
101 
102         } else if (regionOp == Region.Op.UNION.nativeInt) {
103             // if shape1 is null, then the result is simply shape2.
104             if (shape1 == null) {
105                 return new Area(shape2);
106             }
107 
108             // result is always a new area.
109             Area result = new Area(shape1);
110             result.add(shape2 instanceof Area ? (Area) shape2 : new Area(shape2));
111             return result;
112 
113         } else if (regionOp == Region.Op.XOR.nativeInt) {
114             // if shape1 is null, then the result is simply shape2
115             if (shape1 == null) {
116                 return new Area(shape2);
117             }
118 
119             // result is always a new area.
120             Area result = new Area(shape1);
121             result.exclusiveOr(shape2 instanceof Area ? (Area) shape2 : new Area(shape2));
122             return result;
123 
124         } else if (regionOp == Region.Op.REVERSE_DIFFERENCE.nativeInt) {
125             // result is always a new area.
126             Area result = new Area(shape2);
127 
128             if (shape1 != null) {
129                 result.subtract(shape1 instanceof Area ? (Area) shape1 : new Area(shape1));
130             }
131 
132             return result;
133         }
134 
135         return null;
136     }
137 
138     // ---- native methods ----
139 
140     @LayoutlibDelegate
isEmpty(Region thisRegion)141     /*package*/ static boolean isEmpty(Region thisRegion) {
142         Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
143         if (regionDelegate == null) {
144             return true;
145         }
146 
147         return regionDelegate.mArea.isEmpty();
148     }
149 
150     @LayoutlibDelegate
isRect(Region thisRegion)151     /*package*/ static boolean isRect(Region thisRegion) {
152         Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
153         if (regionDelegate == null) {
154             return true;
155         }
156 
157         return regionDelegate.mArea.isRectangular();
158     }
159 
160     @LayoutlibDelegate
isComplex(Region thisRegion)161     /*package*/ static boolean isComplex(Region thisRegion) {
162         Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
163         if (regionDelegate == null) {
164             return true;
165         }
166 
167         return regionDelegate.mArea.isSingular() == false;
168     }
169 
170     @LayoutlibDelegate
contains(Region thisRegion, int x, int y)171     /*package*/ static boolean contains(Region thisRegion, int x, int y) {
172         Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
173         if (regionDelegate == null) {
174             return false;
175         }
176 
177         return regionDelegate.mArea.contains(x, y);
178     }
179 
180     @LayoutlibDelegate
quickContains(Region thisRegion, int left, int top, int right, int bottom)181     /*package*/ static boolean quickContains(Region thisRegion,
182             int left, int top, int right, int bottom) {
183         Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
184         if (regionDelegate == null) {
185             return false;
186         }
187 
188         return regionDelegate.mArea.isRectangular() &&
189                 regionDelegate.mArea.contains(left, top, right - left, bottom - top);
190     }
191 
192     @LayoutlibDelegate
quickReject(Region thisRegion, int left, int top, int right, int bottom)193     /*package*/ static boolean quickReject(Region thisRegion,
194             int left, int top, int right, int bottom) {
195         Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
196         if (regionDelegate == null) {
197             return false;
198         }
199 
200         return regionDelegate.mArea.isEmpty() ||
201                 regionDelegate.mArea.intersects(left, top, right - left, bottom - top) == false;
202     }
203 
204     @LayoutlibDelegate
quickReject(Region thisRegion, Region rgn)205     /*package*/ static boolean quickReject(Region thisRegion, Region rgn) {
206         Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
207         if (regionDelegate == null) {
208             return false;
209         }
210 
211         Region_Delegate targetRegionDelegate = sManager.getDelegate(rgn.mNativeRegion);
212         if (targetRegionDelegate == null) {
213             return false;
214         }
215 
216         return regionDelegate.mArea.isEmpty() ||
217                 regionDelegate.mArea.getBounds().intersects(
218                         targetRegionDelegate.mArea.getBounds()) == false;
219 
220     }
221 
222     @LayoutlibDelegate
translate(Region thisRegion, int dx, int dy, Region dst)223     /*package*/ static void translate(Region thisRegion, int dx, int dy, Region dst) {
224         Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
225         if (regionDelegate == null) {
226             return;
227         }
228 
229         Region_Delegate targetRegionDelegate = sManager.getDelegate(dst.mNativeRegion);
230         if (targetRegionDelegate == null) {
231             return;
232         }
233 
234         if (regionDelegate.mArea.isEmpty()) {
235             targetRegionDelegate.mArea = new Area();
236         } else {
237             targetRegionDelegate.mArea = new Area(regionDelegate.mArea);
238             AffineTransform mtx = new AffineTransform();
239             mtx.translate(dx, dy);
240             targetRegionDelegate.mArea.transform(mtx);
241         }
242     }
243 
244     @LayoutlibDelegate
scale(Region thisRegion, float scale, Region dst)245     /*package*/ static void scale(Region thisRegion, float scale, Region dst) {
246         Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
247         if (regionDelegate == null) {
248             return;
249         }
250 
251         Region_Delegate targetRegionDelegate = sManager.getDelegate(dst.mNativeRegion);
252         if (targetRegionDelegate == null) {
253             return;
254         }
255 
256         if (regionDelegate.mArea.isEmpty()) {
257             targetRegionDelegate.mArea = new Area();
258         } else {
259             targetRegionDelegate.mArea = new Area(regionDelegate.mArea);
260             AffineTransform mtx = new AffineTransform();
261             mtx.scale(scale, scale);
262             targetRegionDelegate.mArea.transform(mtx);
263         }
264     }
265 
266     @LayoutlibDelegate
nativeConstructor()267     /*package*/ static int nativeConstructor() {
268         Region_Delegate newDelegate = new Region_Delegate();
269         return sManager.addNewDelegate(newDelegate);
270     }
271 
272     @LayoutlibDelegate
nativeDestructor(int native_region)273     /*package*/ static void nativeDestructor(int native_region) {
274         sManager.removeJavaReferenceFor(native_region);
275     }
276 
277     @LayoutlibDelegate
nativeSetRegion(int native_dst, int native_src)278     /*package*/ static boolean nativeSetRegion(int native_dst, int native_src) {
279         Region_Delegate dstRegion = sManager.getDelegate(native_dst);
280         if (dstRegion == null) {
281             return true;
282         }
283 
284         Region_Delegate srcRegion = sManager.getDelegate(native_src);
285         if (srcRegion == null) {
286             return true;
287         }
288 
289         dstRegion.mArea.reset();
290         dstRegion.mArea.add(srcRegion.mArea);
291 
292         return true;
293     }
294 
295     @LayoutlibDelegate
nativeSetRect(int native_dst, int left, int top, int right, int bottom)296     /*package*/ static boolean nativeSetRect(int native_dst,
297             int left, int top, int right, int bottom) {
298         Region_Delegate dstRegion = sManager.getDelegate(native_dst);
299         if (dstRegion == null) {
300             return true;
301         }
302 
303         dstRegion.mArea = new Area(new Rectangle2D.Float(left, top, right - left, bottom - top));
304         return dstRegion.mArea.getBounds().isEmpty() == false;
305     }
306 
307     @LayoutlibDelegate
nativeSetPath(int native_dst, int native_path, int native_clip)308     /*package*/ static boolean nativeSetPath(int native_dst, int native_path, int native_clip) {
309         Region_Delegate dstRegion = sManager.getDelegate(native_dst);
310         if (dstRegion == null) {
311             return true;
312         }
313 
314         Path_Delegate path = Path_Delegate.getDelegate(native_path);
315         if (path == null) {
316             return true;
317         }
318 
319         dstRegion.mArea = new Area(path.getJavaShape());
320 
321         Region_Delegate clip = sManager.getDelegate(native_clip);
322         if (clip != null) {
323             dstRegion.mArea.subtract(clip.getJavaArea());
324         }
325 
326         return dstRegion.mArea.getBounds().isEmpty() == false;
327     }
328 
329     @LayoutlibDelegate
nativeGetBounds(int native_region, Rect rect)330     /*package*/ static boolean nativeGetBounds(int native_region, Rect rect) {
331         Region_Delegate region = sManager.getDelegate(native_region);
332         if (region == null) {
333             return true;
334         }
335 
336         Rectangle bounds = region.mArea.getBounds();
337         if (bounds.isEmpty()) {
338             rect.left = rect.top = rect.right = rect.bottom = 0;
339             return false;
340         }
341 
342         rect.left = bounds.x;
343         rect.top = bounds.y;
344         rect.right = bounds.x + bounds.width;
345         rect.bottom = bounds.y + bounds.height;
346         return true;
347     }
348 
349     @LayoutlibDelegate
nativeGetBoundaryPath(int native_region, int native_path)350     /*package*/ static boolean nativeGetBoundaryPath(int native_region, int native_path) {
351         Region_Delegate region = sManager.getDelegate(native_region);
352         if (region == null) {
353             return false;
354         }
355 
356         Path_Delegate path = Path_Delegate.getDelegate(native_path);
357         if (path == null) {
358             return false;
359         }
360 
361         if (region.mArea.isEmpty()) {
362             path.reset();
363             return false;
364         }
365 
366         path.setPathIterator(region.mArea.getPathIterator(new AffineTransform()));
367         return true;
368     }
369 
370     @LayoutlibDelegate
nativeOp(int native_dst, int left, int top, int right, int bottom, int op)371     /*package*/ static boolean nativeOp(int native_dst,
372             int left, int top, int right, int bottom, int op) {
373         Region_Delegate region = sManager.getDelegate(native_dst);
374         if (region == null) {
375             return false;
376         }
377 
378         region.mArea = combineShapes(region.mArea,
379                 new Rectangle2D.Float(left, top, right - left, bottom - top), op);
380 
381         assert region.mArea != null;
382         if (region.mArea != null) {
383             region.mArea = new Area();
384         }
385 
386         return region.mArea.getBounds().isEmpty() == false;
387     }
388 
389     @LayoutlibDelegate
nativeOp(int native_dst, Rect rect, int native_region, int op)390     /*package*/ static boolean nativeOp(int native_dst, Rect rect, int native_region, int op) {
391         Region_Delegate region = sManager.getDelegate(native_dst);
392         if (region == null) {
393             return false;
394         }
395 
396         region.mArea = combineShapes(region.mArea,
397                 new Rectangle2D.Float(rect.left, rect.top, rect.width(), rect.height()), op);
398 
399         assert region.mArea != null;
400         if (region.mArea != null) {
401             region.mArea = new Area();
402         }
403 
404         return region.mArea.getBounds().isEmpty() == false;
405     }
406 
407     @LayoutlibDelegate
nativeOp(int native_dst, int native_region1, int native_region2, int op)408     /*package*/ static boolean nativeOp(int native_dst,
409             int native_region1, int native_region2, int op) {
410         Region_Delegate dstRegion = sManager.getDelegate(native_dst);
411         if (dstRegion == null) {
412             return true;
413         }
414 
415         Region_Delegate region1 = sManager.getDelegate(native_region1);
416         if (region1 == null) {
417             return false;
418         }
419 
420         Region_Delegate region2 = sManager.getDelegate(native_region2);
421         if (region2 == null) {
422             return false;
423         }
424 
425         dstRegion.mArea = combineShapes(region1.mArea, region2.mArea, op);
426 
427         assert dstRegion.mArea != null;
428         if (dstRegion.mArea != null) {
429             dstRegion.mArea = new Area();
430         }
431 
432         return dstRegion.mArea.getBounds().isEmpty() == false;
433 
434     }
435 
436     @LayoutlibDelegate
nativeCreateFromParcel(Parcel p)437     /*package*/ static int nativeCreateFromParcel(Parcel p) {
438         // This is only called by Region.CREATOR (Parcelable.Creator<Region>), which is only
439         // used during aidl call so really this should not be called.
440         Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
441                 "AIDL is not suppored, and therefore Regions cannot be created from parcels.",
442                 null /*data*/);
443         return 0;
444     }
445 
446     @LayoutlibDelegate
nativeWriteToParcel(int native_region, Parcel p)447     /*package*/ static boolean nativeWriteToParcel(int native_region,
448                                                       Parcel p) {
449         // This is only called when sending a region through aidl, so really this should not
450         // be called.
451         Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
452                 "AIDL is not suppored, and therefore Regions cannot be written to parcels.",
453                 null /*data*/);
454         return false;
455     }
456 
457     @LayoutlibDelegate
nativeEquals(int native_r1, int native_r2)458     /*package*/ static boolean nativeEquals(int native_r1, int native_r2) {
459         Region_Delegate region1 = sManager.getDelegate(native_r1);
460         if (region1 == null) {
461             return false;
462         }
463 
464         Region_Delegate region2 = sManager.getDelegate(native_r2);
465         if (region2 == null) {
466             return false;
467         }
468 
469         return region1.mArea.equals(region2.mArea);
470     }
471 
472     // ---- Private delegate/helper methods ----
473 
474 }
475