blob: edb7025b7cd7341dfbc8df430edf15400c2603e6 [file] [log] [blame]
Adam Lesinski282e1812014-01-23 18:17:42 -08001/*
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
17package android.graphics;
18
19import com.android.ide.common.rendering.api.LayoutLog;
20import com.android.layoutlib.bridge.Bridge;
21import com.android.layoutlib.bridge.impl.DelegateManager;
22import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
23
24import android.os.Parcel;
25
26import java.awt.Rectangle;
27import java.awt.Shape;
28import java.awt.geom.AffineTransform;
29import java.awt.geom.Area;
30import 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 */
47public 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
Narayan Kamath88a83642014-01-27 14:24:16 +000060 public static Region_Delegate getDelegate(long nativeShader) {
Adam Lesinski282e1812014-01-23 18:17:42 -080061 return sManager.getDelegate(nativeShader);
62 }
63
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 */
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
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
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
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
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
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
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
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
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
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
Narayan Kamath88a83642014-01-27 14:24:16 +0000267 /*package*/ static long nativeConstructor() {
Adam Lesinski282e1812014-01-23 18:17:42 -0800268 Region_Delegate newDelegate = new Region_Delegate();
269 return sManager.addNewDelegate(newDelegate);
270 }
271
272 @LayoutlibDelegate
Narayan Kamath88a83642014-01-27 14:24:16 +0000273 /*package*/ static void nativeDestructor(long native_region) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800274 sManager.removeJavaReferenceFor(native_region);
275 }
276
277 @LayoutlibDelegate
Deepanshu Gupta9be03c42014-02-21 16:22:10 -0800278 /*package*/ static void nativeSetRegion(long native_dst, long native_src) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800279 Region_Delegate dstRegion = sManager.getDelegate(native_dst);
280 if (dstRegion == null) {
Deepanshu Gupta9be03c42014-02-21 16:22:10 -0800281 return;
Adam Lesinski282e1812014-01-23 18:17:42 -0800282 }
283
284 Region_Delegate srcRegion = sManager.getDelegate(native_src);
285 if (srcRegion == null) {
Deepanshu Gupta9be03c42014-02-21 16:22:10 -0800286 return;
Adam Lesinski282e1812014-01-23 18:17:42 -0800287 }
288
289 dstRegion.mArea.reset();
290 dstRegion.mArea.add(srcRegion.mArea);
291
Adam Lesinski282e1812014-01-23 18:17:42 -0800292 }
293
294 @LayoutlibDelegate
Narayan Kamath88a83642014-01-27 14:24:16 +0000295 /*package*/ static boolean nativeSetRect(long native_dst,
Adam Lesinski282e1812014-01-23 18:17:42 -0800296 int left, int top, int right, int bottom) {
297 Region_Delegate dstRegion = sManager.getDelegate(native_dst);
298 if (dstRegion == null) {
299 return true;
300 }
301
302 dstRegion.mArea = new Area(new Rectangle2D.Float(left, top, right - left, bottom - top));
303 return dstRegion.mArea.getBounds().isEmpty() == false;
304 }
305
306 @LayoutlibDelegate
Narayan Kamath88a83642014-01-27 14:24:16 +0000307 /*package*/ static boolean nativeSetPath(long native_dst, long native_path, long native_clip) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800308 Region_Delegate dstRegion = sManager.getDelegate(native_dst);
309 if (dstRegion == null) {
310 return true;
311 }
312
313 Path_Delegate path = Path_Delegate.getDelegate(native_path);
314 if (path == null) {
315 return true;
316 }
317
318 dstRegion.mArea = new Area(path.getJavaShape());
319
320 Region_Delegate clip = sManager.getDelegate(native_clip);
321 if (clip != null) {
322 dstRegion.mArea.subtract(clip.getJavaArea());
323 }
324
325 return dstRegion.mArea.getBounds().isEmpty() == false;
326 }
327
328 @LayoutlibDelegate
Narayan Kamath88a83642014-01-27 14:24:16 +0000329 /*package*/ static boolean nativeGetBounds(long native_region, Rect rect) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800330 Region_Delegate region = sManager.getDelegate(native_region);
331 if (region == null) {
332 return true;
333 }
334
335 Rectangle bounds = region.mArea.getBounds();
336 if (bounds.isEmpty()) {
337 rect.left = rect.top = rect.right = rect.bottom = 0;
338 return false;
339 }
340
341 rect.left = bounds.x;
342 rect.top = bounds.y;
343 rect.right = bounds.x + bounds.width;
344 rect.bottom = bounds.y + bounds.height;
345 return true;
346 }
347
348 @LayoutlibDelegate
Narayan Kamath88a83642014-01-27 14:24:16 +0000349 /*package*/ static boolean nativeGetBoundaryPath(long native_region, long native_path) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800350 Region_Delegate region = sManager.getDelegate(native_region);
351 if (region == null) {
352 return false;
353 }
354
355 Path_Delegate path = Path_Delegate.getDelegate(native_path);
356 if (path == null) {
357 return false;
358 }
359
360 if (region.mArea.isEmpty()) {
361 path.reset();
362 return false;
363 }
364
365 path.setPathIterator(region.mArea.getPathIterator(new AffineTransform()));
366 return true;
367 }
368
369 @LayoutlibDelegate
Narayan Kamath88a83642014-01-27 14:24:16 +0000370 /*package*/ static boolean nativeOp(long native_dst,
Adam Lesinski282e1812014-01-23 18:17:42 -0800371 int left, int top, int right, int bottom, int op) {
372 Region_Delegate region = sManager.getDelegate(native_dst);
373 if (region == null) {
374 return false;
375 }
376
377 region.mArea = combineShapes(region.mArea,
378 new Rectangle2D.Float(left, top, right - left, bottom - top), op);
379
380 assert region.mArea != null;
381 if (region.mArea != null) {
382 region.mArea = new Area();
383 }
384
385 return region.mArea.getBounds().isEmpty() == false;
386 }
387
388 @LayoutlibDelegate
Narayan Kamath88a83642014-01-27 14:24:16 +0000389 /*package*/ static boolean nativeOp(long native_dst, Rect rect, long native_region, int op) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800390 Region_Delegate region = sManager.getDelegate(native_dst);
391 if (region == null) {
392 return false;
393 }
394
395 region.mArea = combineShapes(region.mArea,
396 new Rectangle2D.Float(rect.left, rect.top, rect.width(), rect.height()), op);
397
398 assert region.mArea != null;
399 if (region.mArea != null) {
400 region.mArea = new Area();
401 }
402
403 return region.mArea.getBounds().isEmpty() == false;
404 }
405
406 @LayoutlibDelegate
Narayan Kamath88a83642014-01-27 14:24:16 +0000407 /*package*/ static boolean nativeOp(long native_dst,
408 long native_region1, long native_region2, int op) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800409 Region_Delegate dstRegion = sManager.getDelegate(native_dst);
410 if (dstRegion == null) {
411 return true;
412 }
413
414 Region_Delegate region1 = sManager.getDelegate(native_region1);
415 if (region1 == null) {
416 return false;
417 }
418
419 Region_Delegate region2 = sManager.getDelegate(native_region2);
420 if (region2 == null) {
421 return false;
422 }
423
424 dstRegion.mArea = combineShapes(region1.mArea, region2.mArea, op);
425
426 assert dstRegion.mArea != null;
427 if (dstRegion.mArea != null) {
428 dstRegion.mArea = new Area();
429 }
430
431 return dstRegion.mArea.getBounds().isEmpty() == false;
432
433 }
434
435 @LayoutlibDelegate
Narayan Kamath88a83642014-01-27 14:24:16 +0000436 /*package*/ static long nativeCreateFromParcel(Parcel p) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800437 // This is only called by Region.CREATOR (Parcelable.Creator<Region>), which is only
438 // used during aidl call so really this should not be called.
439 Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
440 "AIDL is not suppored, and therefore Regions cannot be created from parcels.",
441 null /*data*/);
442 return 0;
443 }
444
445 @LayoutlibDelegate
Narayan Kamath88a83642014-01-27 14:24:16 +0000446 /*package*/ static boolean nativeWriteToParcel(long native_region,
Adam Lesinski282e1812014-01-23 18:17:42 -0800447 Parcel p) {
448 // This is only called when sending a region through aidl, so really this should not
449 // be called.
450 Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
451 "AIDL is not suppored, and therefore Regions cannot be written to parcels.",
452 null /*data*/);
453 return false;
454 }
455
456 @LayoutlibDelegate
Narayan Kamath88a83642014-01-27 14:24:16 +0000457 /*package*/ static boolean nativeEquals(long native_r1, long native_r2) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800458 Region_Delegate region1 = sManager.getDelegate(native_r1);
459 if (region1 == null) {
460 return false;
461 }
462
463 Region_Delegate region2 = sManager.getDelegate(native_r2);
464 if (region2 == null) {
465 return false;
466 }
467
468 return region1.mArea.equals(region2.mArea);
469 }
470
471 @LayoutlibDelegate
Narayan Kamath88a83642014-01-27 14:24:16 +0000472 /*package*/ static String nativeToString(long native_region) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800473 Region_Delegate region = sManager.getDelegate(native_region);
474 if (region == null) {
475 return "not found";
476 }
477
478 return region.mArea.toString();
479 }
480
481 // ---- Private delegate/helper methods ----
482
483}