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 60 public static Region_Delegate getDelegate(long nativeShader) { 61 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 267 /*package*/ static long nativeConstructor() { 268 Region_Delegate newDelegate = new Region_Delegate(); 269 return sManager.addNewDelegate(newDelegate); 270 } 271 272 @LayoutlibDelegate 273 /*package*/ static void nativeDestructor(long native_region) { 274 sManager.removeJavaReferenceFor(native_region); 275 } 276 277 @LayoutlibDelegate 278 /*package*/ static void nativeSetRegion(long native_dst, long native_src) { 279 Region_Delegate dstRegion = sManager.getDelegate(native_dst); 280 if (dstRegion == null) { 281 return; 282 } 283 284 Region_Delegate srcRegion = sManager.getDelegate(native_src); 285 if (srcRegion == null) { 286 return; 287 } 288 289 dstRegion.mArea.reset(); 290 dstRegion.mArea.add(srcRegion.mArea); 291 292 } 293 294 @LayoutlibDelegate 295 /*package*/ static boolean nativeSetRect(long native_dst, 296 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 307 /*package*/ static boolean nativeSetPath(long native_dst, long native_path, long native_clip) { 308 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 329 /*package*/ static boolean nativeGetBounds(long native_region, Rect rect) { 330 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 349 /*package*/ static boolean nativeGetBoundaryPath(long native_region, long native_path) { 350 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 370 /*package*/ static boolean nativeOp(long native_dst, 371 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 389 /*package*/ static boolean nativeOp(long native_dst, Rect rect, long native_region, int op) { 390 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 407 /*package*/ static boolean nativeOp(long native_dst, 408 long native_region1, long native_region2, int op) { 409 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 436 /*package*/ static long nativeCreateFromParcel(Parcel p) { 437 // 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 446 /*package*/ static boolean nativeWriteToParcel(long native_region, 447 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 457 /*package*/ static boolean nativeEquals(long native_r1, long native_r2) { 458 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 472 /*package*/ static String nativeToString(long native_region) { 473 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 } 484