Home | History | Annotate | Download | only in impl
      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 com.android.layoutlib.bridge.impl;
     18 
     19 import com.android.ide.common.rendering.api.LayoutLog;
     20 import com.android.layoutlib.bridge.Bridge;
     21 
     22 import android.graphics.Bitmap_Delegate;
     23 import android.graphics.Canvas;
     24 import android.graphics.Paint;
     25 import android.graphics.Paint_Delegate;
     26 import android.graphics.Rect;
     27 import android.graphics.RectF;
     28 import android.graphics.Region;
     29 import android.graphics.Region_Delegate;
     30 import android.graphics.Shader_Delegate;
     31 import android.graphics.Xfermode_Delegate;
     32 
     33 import java.awt.AlphaComposite;
     34 import java.awt.Color;
     35 import java.awt.Composite;
     36 import java.awt.Graphics2D;
     37 import java.awt.RenderingHints;
     38 import java.awt.Shape;
     39 import java.awt.geom.AffineTransform;
     40 import java.awt.geom.Area;
     41 import java.awt.geom.Rectangle2D;
     42 import java.awt.image.BufferedImage;
     43 import java.util.ArrayList;
     44 
     45 /**
     46  * Class representing a graphics context snapshot, as well as a context stack as a linked list.
     47  * <p>
     48  * This is based on top of {@link Graphics2D} but can operate independently if none are available
     49  * yet when setting transforms and clip information.
     50  * <p>
     51  * This allows for drawing through {@link #draw(Drawable, Paint_Delegate)} and
     52  * {@link #draw(Drawable, Paint_Delegate)}
     53  *
     54  * Handling of layers (created with {@link Canvas#saveLayer(RectF, Paint, int)}) is handled through
     55  * a list of Graphics2D for each layers. The class actually maintains a list of {@link Layer}
     56  * for each layer. Doing a save() will duplicate this list so that each graphics2D object
     57  * ({@link Layer#getGraphics()}) is configured only for the new snapshot.
     58  */
     59 public class GcSnapshot {
     60 
     61     private final GcSnapshot mPrevious;
     62     private final int mFlags;
     63 
     64     /** list of layers. The first item in the list is always the  */
     65     private final ArrayList<Layer> mLayers = new ArrayList<Layer>();
     66 
     67     /** temp transform in case transformation are set before a Graphics2D exists */
     68     private AffineTransform mTransform = null;
     69     /** temp clip in case clipping is set before a Graphics2D exists */
     70     private Area mClip = null;
     71 
     72     // local layer data
     73     /** a local layer created with {@link Canvas#saveLayer(RectF, Paint, int)}.
     74      * If this is null, this does not mean there's no layer, just that the snapshot is not the
     75      * one that created the layer.
     76      * @see #getLayerSnapshot()
     77      */
     78     private final Layer mLocalLayer;
     79     private final Paint_Delegate mLocalLayerPaint;
     80     private final Rect mLayerBounds;
     81 
     82     public interface Drawable {
     83         void draw(Graphics2D graphics, Paint_Delegate paint);
     84     }
     85 
     86     /**
     87      * Class containing information about a layer.
     88      *
     89      * This contains graphics, bitmap and layer information.
     90      */
     91     private static class Layer {
     92         private final Graphics2D mGraphics;
     93         private final Bitmap_Delegate mBitmap;
     94         private final BufferedImage mImage;
     95         /** the flags that were used to configure the layer. This is never changed, and passed
     96          * as is when {@link #makeCopy()} is called */
     97         private final int mFlags;
     98         /** the original content of the layer when the next object was created. This is not
     99          * passed in {@link #makeCopy()} and instead is recreated when a new layer is added
    100          * (depending on its flags) */
    101         private BufferedImage mOriginalCopy;
    102 
    103         /**
    104          * Creates a layer with a graphics and a bitmap. This is only used to create
    105          * the base layer.
    106          *
    107          * @param graphics the graphics
    108          * @param bitmap the bitmap
    109          */
    110         Layer(Graphics2D graphics, Bitmap_Delegate bitmap) {
    111             mGraphics = graphics;
    112             mBitmap = bitmap;
    113             mImage = mBitmap.getImage();
    114             mFlags = 0;
    115         }
    116 
    117         /**
    118          * Creates a layer with a graphics and an image. If the image belongs to a
    119          * {@link Bitmap_Delegate} (case of the base layer), then
    120          * {@link Layer#Layer(Graphics2D, Bitmap_Delegate)} should be used.
    121          *
    122          * @param graphics the graphics the new graphics for this layer
    123          * @param image the image the image from which the graphics came
    124          * @param flags the flags that were used to save this layer
    125          */
    126         Layer(Graphics2D graphics, BufferedImage image, int flags) {
    127             mGraphics = graphics;
    128             mBitmap = null;
    129             mImage = image;
    130             mFlags = flags;
    131         }
    132 
    133         /** The Graphics2D, guaranteed to be non null */
    134         Graphics2D getGraphics() {
    135             return mGraphics;
    136         }
    137 
    138         /** The BufferedImage, guaranteed to be non null */
    139         BufferedImage getImage() {
    140             return mImage;
    141         }
    142 
    143         /** Returns the layer save flags. This is only valid for additional layers.
    144          * For the base layer this will always return 0;
    145          * For a given layer, all further copies of this {@link Layer} object in new snapshots
    146          * will always return the same value.
    147          */
    148         int getFlags() {
    149             return mFlags;
    150         }
    151 
    152         Layer makeCopy() {
    153             if (mBitmap != null) {
    154                 return new Layer((Graphics2D) mGraphics.create(), mBitmap);
    155             }
    156 
    157             return new Layer((Graphics2D) mGraphics.create(), mImage, mFlags);
    158         }
    159 
    160         /** sets an optional copy of the original content to be used during restore */
    161         void setOriginalCopy(BufferedImage image) {
    162             mOriginalCopy = image;
    163         }
    164 
    165         BufferedImage getOriginalCopy() {
    166             return mOriginalCopy;
    167         }
    168 
    169         void change() {
    170             if (mBitmap != null) {
    171                 mBitmap.change();
    172             }
    173         }
    174 
    175         /**
    176          * Sets the clip for the graphics2D object associated with the layer.
    177          * This should be used over the normal Graphics2D setClip method.
    178          *
    179          * @param clipShape the shape to use a the clip shape.
    180          */
    181         void setClip(Shape clipShape) {
    182             // because setClip is only guaranteed to work with rectangle shape,
    183             // first reset the clip to max and then intersect the current (empty)
    184             // clip with the shap.
    185             mGraphics.setClip(null);
    186             mGraphics.clip(clipShape);
    187         }
    188 
    189         /**
    190          * Clips the layer with the given shape. This performs an intersect between the current
    191          * clip shape and the given shape.
    192          * @param shape the new clip shape.
    193          */
    194         public void clip(Shape shape) {
    195             mGraphics.clip(shape);
    196         }
    197     }
    198 
    199     /**
    200      * Creates the root snapshot associating it with a given bitmap.
    201      * <p>
    202      * If <var>bitmap</var> is null, then {@link GcSnapshot#setBitmap(Bitmap_Delegate)} must be
    203      * called before the snapshot can be used to draw. Transform and clip operations are permitted
    204      * before.
    205      *
    206      * @param image the image to associate to the snapshot or null.
    207      * @return the root snapshot
    208      */
    209     public static GcSnapshot createDefaultSnapshot(Bitmap_Delegate bitmap) {
    210         GcSnapshot snapshot = new GcSnapshot();
    211         if (bitmap != null) {
    212             snapshot.setBitmap(bitmap);
    213         }
    214 
    215         return snapshot;
    216     }
    217 
    218     /**
    219      * Saves the current state according to the given flags and returns the new current snapshot.
    220      * <p/>
    221      * This is the equivalent of {@link Canvas#save(int)}
    222      *
    223      * @param flags the save flags.
    224      * @return the new snapshot
    225      *
    226      * @see Canvas#save(int)
    227      */
    228     public GcSnapshot save(int flags) {
    229         return new GcSnapshot(this, null /*layerbounds*/, null /*paint*/, flags);
    230     }
    231 
    232     /**
    233      * Saves the current state and creates a new layer, and returns the new current snapshot.
    234      * <p/>
    235      * This is the equivalent of {@link Canvas#saveLayer(RectF, Paint, int)}
    236      *
    237      * @param layerBounds the layer bounds
    238      * @param paint the Paint information used to blit the layer back into the layers underneath
    239      *          upon restore
    240      * @param flags the save flags.
    241      * @return the new snapshot
    242      *
    243      * @see Canvas#saveLayer(RectF, Paint, int)
    244      */
    245     public GcSnapshot saveLayer(RectF layerBounds, Paint_Delegate paint, int flags) {
    246         return new GcSnapshot(this, layerBounds, paint, flags);
    247     }
    248 
    249     /**
    250      * Creates the root snapshot.
    251      * {@link #setGraphics2D(Graphics2D)} will have to be called on it when possible.
    252      */
    253     private GcSnapshot() {
    254         mPrevious = null;
    255         mFlags = 0;
    256         mLocalLayer = null;
    257         mLocalLayerPaint = null;
    258         mLayerBounds = null;
    259     }
    260 
    261     /**
    262      * Creates a new {@link GcSnapshot} on top of another one, with a layer data to be restored
    263      * into the main graphics when {@link #restore()} is called.
    264      *
    265      * @param previous the previous snapshot head.
    266      * @param layerBounds the region of the layer. Optional, if null, this is a normal save()
    267      * @param paint the Paint information used to blit the layer back into the layers underneath
    268      *          upon restore
    269      * @param flags the flags regarding what should be saved.
    270      */
    271     private GcSnapshot(GcSnapshot previous, RectF layerBounds, Paint_Delegate paint, int flags) {
    272         assert previous != null;
    273         mPrevious = previous;
    274         mFlags = flags;
    275 
    276         // make a copy of the current layers before adding the new one.
    277         // This keeps the same BufferedImage reference but creates new Graphics2D for this
    278         // snapshot.
    279         // It does not copy whatever original copy the layers have, as they will be done
    280         // only if the new layer doesn't clip drawing to itself.
    281         for (Layer layer : mPrevious.mLayers) {
    282             mLayers.add(layer.makeCopy());
    283         }
    284 
    285         if (layerBounds != null) {
    286             // get the current transform
    287             AffineTransform matrix = mLayers.get(0).getGraphics().getTransform();
    288 
    289             // transform the layerBounds with the current transform and stores it into a int rect
    290             RectF rect2 = new RectF();
    291             mapRect(matrix, rect2, layerBounds);
    292             mLayerBounds = new Rect();
    293             rect2.round(mLayerBounds);
    294 
    295             // get the base layer (always at index 0)
    296             Layer baseLayer = mLayers.get(0);
    297 
    298             // create the image for the layer
    299             BufferedImage layerImage = new BufferedImage(
    300                     baseLayer.getImage().getWidth(),
    301                     baseLayer.getImage().getHeight(),
    302                     (mFlags & Canvas.HAS_ALPHA_LAYER_SAVE_FLAG) != 0 ?
    303                             BufferedImage.TYPE_INT_ARGB :
    304                                 BufferedImage.TYPE_INT_RGB);
    305 
    306             // create a graphics for it so that drawing can be done.
    307             Graphics2D layerGraphics = layerImage.createGraphics();
    308 
    309             // because this layer inherits the current context for transform and clip,
    310             // set them to one from the base layer.
    311             AffineTransform currentMtx = baseLayer.getGraphics().getTransform();
    312             layerGraphics.setTransform(currentMtx);
    313 
    314             // create a new layer for this new layer and add it to the list at the end.
    315             mLayers.add(mLocalLayer = new Layer(layerGraphics, layerImage, flags));
    316 
    317             // set the clip on it.
    318             Shape currentClip = baseLayer.getGraphics().getClip();
    319             mLocalLayer.setClip(currentClip);
    320 
    321             // if the drawing is not clipped to the local layer only, we save the current content
    322             // of all other layers. We are only interested in the part that will actually
    323             // be drawn, so we create as small bitmaps as we can.
    324             // This is so that we can erase the drawing that goes in the layers below that will
    325             // be coming from the layer itself.
    326             if ((mFlags & Canvas.CLIP_TO_LAYER_SAVE_FLAG) == 0) {
    327                 int w = mLayerBounds.width();
    328                 int h = mLayerBounds.height();
    329                 for (int i = 0 ; i < mLayers.size() - 1 ; i++) {
    330                     Layer layer = mLayers.get(i);
    331                     BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
    332                     Graphics2D graphics = image.createGraphics();
    333                     graphics.drawImage(layer.getImage(),
    334                             0, 0, w, h,
    335                             mLayerBounds.left, mLayerBounds.top,
    336                                     mLayerBounds.right, mLayerBounds.bottom,
    337                             null);
    338                     graphics.dispose();
    339                     layer.setOriginalCopy(image);
    340                 }
    341             }
    342         } else {
    343             mLocalLayer = null;
    344             mLayerBounds = null;
    345         }
    346 
    347         mLocalLayerPaint  = paint;
    348     }
    349 
    350     public void dispose() {
    351         for (Layer layer : mLayers) {
    352             layer.getGraphics().dispose();
    353         }
    354 
    355         if (mPrevious != null) {
    356             mPrevious.dispose();
    357         }
    358     }
    359 
    360     /**
    361      * Restores the top {@link GcSnapshot}, and returns the next one.
    362      */
    363     public GcSnapshot restore() {
    364         return doRestore();
    365     }
    366 
    367     /**
    368      * Restores the {@link GcSnapshot} to <var>saveCount</var>.
    369      * @param saveCount the saveCount or -1 to only restore 1.
    370      *
    371      * @return the new head of the Gc snapshot stack.
    372      */
    373     public GcSnapshot restoreTo(int saveCount) {
    374         return doRestoreTo(size(), saveCount);
    375     }
    376 
    377     public int size() {
    378         if (mPrevious != null) {
    379             return mPrevious.size() + 1;
    380         }
    381 
    382         return 1;
    383     }
    384 
    385     /**
    386      * Link the snapshot to a Bitmap_Delegate.
    387      * <p/>
    388      * This is only for the case where the snapshot was created with a null image when calling
    389      * {@link #createDefaultSnapshot(Bitmap_Delegate)}, and is therefore not yet linked to
    390      * a previous snapshot.
    391      * <p/>
    392      * If any transform or clip information was set before, they are put into the Graphics object.
    393      * @param bitmap the bitmap to link to.
    394      */
    395     public void setBitmap(Bitmap_Delegate bitmap) {
    396         // create a new Layer for the bitmap. This will be the base layer.
    397         Graphics2D graphics2D = bitmap.getImage().createGraphics();
    398         Layer baseLayer = new Layer(graphics2D, bitmap);
    399 
    400         // Set the current transform and clip which can either come from mTransform/mClip if they
    401         // were set when there was no bitmap/layers or from the current base layers if there is
    402         // one already.
    403 
    404         graphics2D.setTransform(getTransform());
    405         // reset mTransform in case there was one.
    406         mTransform = null;
    407 
    408         baseLayer.setClip(getClip());
    409         // reset mClip in case there was one.
    410         mClip = null;
    411 
    412         // replace whatever current layers we have with this.
    413         mLayers.clear();
    414         mLayers.add(baseLayer);
    415 
    416     }
    417 
    418     public void translate(float dx, float dy) {
    419         if (mLayers.size() > 0) {
    420             for (Layer layer : mLayers) {
    421                 layer.getGraphics().translate(dx, dy);
    422             }
    423         } else {
    424             if (mTransform == null) {
    425                 mTransform = new AffineTransform();
    426             }
    427             mTransform.translate(dx, dy);
    428         }
    429     }
    430 
    431     public void rotate(double radians) {
    432         if (mLayers.size() > 0) {
    433             for (Layer layer : mLayers) {
    434                 layer.getGraphics().rotate(radians);
    435             }
    436         } else {
    437             if (mTransform == null) {
    438                 mTransform = new AffineTransform();
    439             }
    440             mTransform.rotate(radians);
    441         }
    442     }
    443 
    444     public void scale(float sx, float sy) {
    445         if (mLayers.size() > 0) {
    446             for (Layer layer : mLayers) {
    447                 layer.getGraphics().scale(sx, sy);
    448             }
    449         } else {
    450             if (mTransform == null) {
    451                 mTransform = new AffineTransform();
    452             }
    453             mTransform.scale(sx, sy);
    454         }
    455     }
    456 
    457     public AffineTransform getTransform() {
    458         if (mLayers.size() > 0) {
    459             // all graphics2D in the list have the same transform
    460             return mLayers.get(0).getGraphics().getTransform();
    461         } else {
    462             if (mTransform == null) {
    463                 mTransform = new AffineTransform();
    464             }
    465             return mTransform;
    466         }
    467     }
    468 
    469     public void setTransform(AffineTransform transform) {
    470         if (mLayers.size() > 0) {
    471             for (Layer layer : mLayers) {
    472                 layer.getGraphics().setTransform(transform);
    473             }
    474         } else {
    475             if (mTransform == null) {
    476                 mTransform = new AffineTransform();
    477             }
    478             mTransform.setTransform(transform);
    479         }
    480     }
    481 
    482     public boolean clip(Shape shape, int regionOp) {
    483         // Simple case of intersect with existing layers.
    484         // Because Graphics2D#setClip works a bit peculiarly, we optimize
    485         // the case of clipping by intersection, as it's supported natively.
    486         if (regionOp == Region.Op.INTERSECT.nativeInt && mLayers.size() > 0) {
    487             for (Layer layer : mLayers) {
    488                 layer.clip(shape);
    489             }
    490 
    491             Shape currentClip = getClip();
    492             return currentClip != null && currentClip.getBounds().isEmpty() == false;
    493         }
    494 
    495         Area area = null;
    496 
    497         if (regionOp == Region.Op.REPLACE.nativeInt) {
    498             area = new Area(shape);
    499         } else {
    500             area = Region_Delegate.combineShapes(getClip(), shape, regionOp);
    501         }
    502 
    503         assert area != null;
    504 
    505         if (mLayers.size() > 0) {
    506             if (area != null) {
    507                 for (Layer layer : mLayers) {
    508                     layer.setClip(area);
    509                 }
    510             }
    511 
    512             Shape currentClip = getClip();
    513             return currentClip != null && currentClip.getBounds().isEmpty() == false;
    514         } else {
    515             if (area != null) {
    516                 mClip = area;
    517             } else {
    518                 mClip = new Area();
    519             }
    520 
    521             return mClip.getBounds().isEmpty() == false;
    522         }
    523     }
    524 
    525     public boolean clipRect(float left, float top, float right, float bottom, int regionOp) {
    526         return clip(new Rectangle2D.Float(left, top, right - left, bottom - top), regionOp);
    527     }
    528 
    529     /**
    530      * Returns the current clip, or null if none have been setup.
    531      */
    532     public Shape getClip() {
    533         if (mLayers.size() > 0) {
    534             // they all have the same clip
    535             return mLayers.get(0).getGraphics().getClip();
    536         } else {
    537             return mClip;
    538         }
    539     }
    540 
    541     private GcSnapshot doRestoreTo(int size, int saveCount) {
    542         if (size <= saveCount) {
    543             return this;
    544         }
    545 
    546         // restore the current one first.
    547         GcSnapshot previous = doRestore();
    548 
    549         if (size == saveCount + 1) { // this was the only one that needed restore.
    550             return previous;
    551         } else {
    552             return previous.doRestoreTo(size - 1, saveCount);
    553         }
    554     }
    555 
    556     /**
    557      * Executes the Drawable's draw method, with a null paint delegate.
    558      * <p/>
    559      * Note that the method can be called several times if there are more than one active layer.
    560      * @param drawable
    561      */
    562     public void draw(Drawable drawable) {
    563         draw(drawable, null, false /*compositeOnly*/, false /*forceSrcMode*/);
    564     }
    565 
    566     /**
    567      * Executes the Drawable's draw method.
    568      * <p/>
    569      * Note that the method can be called several times if there are more than one active layer.
    570      * @param drawable
    571      * @param paint
    572      * @param compositeOnly whether the paint is used for composite only. This is typically
    573      *          the case for bitmaps.
    574      * @param forceSrcMode if true, this overrides the composite to be SRC
    575      */
    576     public void draw(Drawable drawable, Paint_Delegate paint, boolean compositeOnly,
    577             boolean forceSrcMode) {
    578         // the current snapshot may not have a mLocalLayer (ie it was created on save() instead
    579         // of saveLayer(), but that doesn't mean there's no layer.
    580         // mLayers however saves all the information we need (flags).
    581         if (mLayers.size() == 1) {
    582             // no layer, only base layer. easy case.
    583             drawInLayer(mLayers.get(0), drawable, paint, compositeOnly, forceSrcMode);
    584         } else {
    585             // draw in all the layers until the layer save flags tells us to stop (ie drawing
    586             // in that layer is limited to the layer itself.
    587             int flags;
    588             int i = mLayers.size() - 1;
    589 
    590             do {
    591                 Layer layer = mLayers.get(i);
    592 
    593                 drawInLayer(layer, drawable, paint, compositeOnly, forceSrcMode);
    594 
    595                 // then go to previous layer, only if there are any left, and its flags
    596                 // doesn't restrict drawing to the layer itself.
    597                 i--;
    598                 flags = layer.getFlags();
    599             } while (i >= 0 && (flags & Canvas.CLIP_TO_LAYER_SAVE_FLAG) == 0);
    600         }
    601     }
    602 
    603     private void drawInLayer(Layer layer, Drawable drawable, Paint_Delegate paint,
    604             boolean compositeOnly, boolean forceSrcMode) {
    605         Graphics2D originalGraphics = layer.getGraphics();
    606         // get a Graphics2D object configured with the drawing parameters.
    607         Graphics2D configuredGraphics2D =
    608             paint != null ?
    609                     createCustomGraphics(originalGraphics, paint, compositeOnly, forceSrcMode) :
    610                         (Graphics2D) originalGraphics.create();
    611 
    612         try {
    613             drawable.draw(configuredGraphics2D, paint);
    614             layer.change();
    615         } finally {
    616             // dispose Graphics2D object
    617             configuredGraphics2D.dispose();
    618         }
    619     }
    620 
    621     private GcSnapshot doRestore() {
    622         if (mPrevious != null) {
    623             if (mLocalLayer != null) {
    624                 // prepare to blit the layers in which we have draw, in the layer beneath
    625                 // them, starting with the top one (which is the current local layer).
    626                 int i = mLayers.size() - 1;
    627                 int flags;
    628                 do {
    629                     Layer dstLayer = mLayers.get(i - 1);
    630 
    631                     restoreLayer(dstLayer);
    632 
    633                     flags = dstLayer.getFlags();
    634                     i--;
    635                 } while (i > 0 && (flags & Canvas.CLIP_TO_LAYER_SAVE_FLAG) == 0);
    636             }
    637 
    638             // if this snapshot does not save everything, then set the previous snapshot
    639             // to this snapshot content
    640 
    641             // didn't save the matrix? set the current matrix on the previous snapshot
    642             if ((mFlags & Canvas.MATRIX_SAVE_FLAG) == 0) {
    643                 AffineTransform mtx = getTransform();
    644                 for (Layer layer : mPrevious.mLayers) {
    645                     layer.getGraphics().setTransform(mtx);
    646                 }
    647             }
    648 
    649             // didn't save the clip? set the current clip on the previous snapshot
    650             if ((mFlags & Canvas.CLIP_SAVE_FLAG) == 0) {
    651                 Shape clip = getClip();
    652                 for (Layer layer : mPrevious.mLayers) {
    653                     layer.setClip(clip);
    654                 }
    655             }
    656         }
    657 
    658         for (Layer layer : mLayers) {
    659             layer.getGraphics().dispose();
    660         }
    661 
    662         return mPrevious;
    663     }
    664 
    665     private void restoreLayer(Layer dstLayer) {
    666 
    667         Graphics2D baseGfx = dstLayer.getImage().createGraphics();
    668 
    669         // if the layer contains an original copy this means the flags
    670         // didn't restrict drawing to the local layer and we need to make sure the
    671         // layer bounds in the layer beneath didn't receive any drawing.
    672         // so we use the originalCopy to erase the new drawings in there.
    673         BufferedImage originalCopy = dstLayer.getOriginalCopy();
    674         if (originalCopy != null) {
    675             Graphics2D g = (Graphics2D) baseGfx.create();
    676             g.setComposite(AlphaComposite.Src);
    677 
    678             g.drawImage(originalCopy,
    679                     mLayerBounds.left, mLayerBounds.top, mLayerBounds.right, mLayerBounds.bottom,
    680                     0, 0, mLayerBounds.width(), mLayerBounds.height(),
    681                     null);
    682             g.dispose();
    683         }
    684 
    685         // now draw put the content of the local layer onto the layer,
    686         // using the paint information
    687         Graphics2D g = createCustomGraphics(baseGfx, mLocalLayerPaint,
    688                 true /*alphaOnly*/, false /*forceSrcMode*/);
    689 
    690         g.drawImage(mLocalLayer.getImage(),
    691                 mLayerBounds.left, mLayerBounds.top, mLayerBounds.right, mLayerBounds.bottom,
    692                 mLayerBounds.left, mLayerBounds.top, mLayerBounds.right, mLayerBounds.bottom,
    693                 null);
    694         g.dispose();
    695 
    696         baseGfx.dispose();
    697     }
    698 
    699     /**
    700      * Creates a new {@link Graphics2D} based on the {@link Paint} parameters.
    701      * <p/>The object must be disposed ({@link Graphics2D#dispose()}) after being used.
    702      */
    703     private Graphics2D createCustomGraphics(Graphics2D original, Paint_Delegate paint,
    704             boolean compositeOnly, boolean forceSrcMode) {
    705         // make new one graphics
    706         Graphics2D g = (Graphics2D) original.create();
    707 
    708         // configure it
    709 
    710         if (paint.isAntiAliased()) {
    711             g.setRenderingHint(
    712                     RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    713             g.setRenderingHint(
    714                     RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
    715         }
    716 
    717         boolean customShader = false;
    718 
    719         // get the shader first, as it'll replace the color if it can be used it.
    720         if (compositeOnly == false) {
    721             Shader_Delegate shaderDelegate = paint.getShader();
    722             if (shaderDelegate != null) {
    723                 if (shaderDelegate.isSupported()) {
    724                     java.awt.Paint shaderPaint = shaderDelegate.getJavaPaint();
    725                     assert shaderPaint != null;
    726                     if (shaderPaint != null) {
    727                         g.setPaint(shaderPaint);
    728                         customShader = true;
    729                     }
    730                 } else {
    731                     Bridge.getLog().fidelityWarning(LayoutLog.TAG_SHADER,
    732                             shaderDelegate.getSupportMessage(),
    733                             null /*throwable*/, null /*data*/);
    734                 }
    735             }
    736 
    737             // if no shader, use the paint color
    738             if (customShader == false) {
    739                 g.setColor(new Color(paint.getColor(), true /*hasAlpha*/));
    740             }
    741 
    742             // set the stroke
    743             g.setStroke(paint.getJavaStroke());
    744         }
    745 
    746         // the alpha for the composite. Always opaque if the normal paint color is used since
    747         // it contains the alpha
    748         int alpha = (compositeOnly || customShader) ? paint.getAlpha() : 0xFF;
    749 
    750         if (forceSrcMode) {
    751             g.setComposite(AlphaComposite.getInstance(
    752                     AlphaComposite.SRC, (float) alpha / 255.f));
    753         } else {
    754             boolean customXfermode = false;
    755             Xfermode_Delegate xfermodeDelegate = paint.getXfermode();
    756             if (xfermodeDelegate != null) {
    757                 if (xfermodeDelegate.isSupported()) {
    758                     Composite composite = xfermodeDelegate.getComposite(alpha);
    759                     assert composite != null;
    760                     if (composite != null) {
    761                         g.setComposite(composite);
    762                         customXfermode = true;
    763                     }
    764                 } else {
    765                     Bridge.getLog().fidelityWarning(LayoutLog.TAG_XFERMODE,
    766                             xfermodeDelegate.getSupportMessage(),
    767                             null /*throwable*/, null /*data*/);
    768                 }
    769             }
    770 
    771             // if there was no custom xfermode, but we have alpha (due to a shader and a non
    772             // opaque alpha channel in the paint color), then we create an AlphaComposite anyway
    773             // that will handle the alpha.
    774             if (customXfermode == false && alpha != 0xFF) {
    775                 g.setComposite(AlphaComposite.getInstance(
    776                         AlphaComposite.SRC_OVER, (float) alpha / 255.f));
    777             }
    778         }
    779 
    780         return g;
    781     }
    782 
    783     private void mapRect(AffineTransform matrix, RectF dst, RectF src) {
    784         // array with 4 corners
    785         float[] corners = new float[] {
    786                 src.left, src.top,
    787                 src.right, src.top,
    788                 src.right, src.bottom,
    789                 src.left, src.bottom,
    790         };
    791 
    792         // apply the transform to them.
    793         matrix.transform(corners, 0, corners, 0, 4);
    794 
    795         // now put the result in the rect. We take the min/max of Xs and min/max of Ys
    796         dst.left = Math.min(Math.min(corners[0], corners[2]), Math.min(corners[4], corners[6]));
    797         dst.right = Math.max(Math.max(corners[0], corners[2]), Math.max(corners[4], corners[6]));
    798 
    799         dst.top = Math.min(Math.min(corners[1], corners[3]), Math.min(corners[5], corners[7]));
    800         dst.bottom = Math.max(Math.max(corners[1], corners[3]), Math.max(corners[5], corners[7]));
    801     }
    802 
    803 }
    804