Home | History | Annotate | Download | only in graphics
      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