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(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