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(int 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 int nativeConstructor() { 268 Region_Delegate newDelegate = new Region_Delegate(); 269 return sManager.addNewDelegate(newDelegate); 270 } 271 272 @LayoutlibDelegate 273 /*package*/ static void nativeDestructor(int native_region) { 274 sManager.removeJavaReferenceFor(native_region); 275 } 276 277 @LayoutlibDelegate 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 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 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 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 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 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 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 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 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 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 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 @LayoutlibDelegate 473 /*package*/ static String nativeToString(int native_region) { 474 Region_Delegate region = sManager.getDelegate(native_region); 475 if (region == null) { 476 return "not found"; 477 } 478 479 return region.mArea.toString(); 480 } 481 482 // ---- Private delegate/helper methods ---- 483 484 } 485