Home | History | Annotate | Download | only in graphics
      1 /*
      2  * Copyright (C) 2007 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 java.io.InputStream;
     20 import java.io.OutputStream;
     21 
     22 /**
     23  * A Picture records drawing calls (via the canvas returned by beginRecording)
     24  * and can then play them back into Canvas (via {@link Picture#draw(Canvas)} or
     25  * {@link Canvas#drawPicture(Picture)}).For most content (e.g. text, lines, rectangles),
     26  * drawing a sequence from a picture can be faster than the equivalent API
     27  * calls, since the picture performs its playback without incurring any
     28  * method-call overhead.
     29  */
     30 public class Picture {
     31     private Canvas mRecordingCanvas;
     32     private final int mNativePicture;
     33 
     34     /**
     35      * @hide
     36      */
     37     public final boolean createdFromStream;
     38 
     39     private static final int WORKING_STREAM_STORAGE = 16 * 1024;
     40 
     41     /**
     42      * Creates an empty picture that is ready to record.
     43      */
     44     public Picture() {
     45         this(nativeConstructor(0), false);
     46     }
     47 
     48     /**
     49      * Create a picture by making a copy of what has already been recorded in
     50      * src. The contents of src are unchanged, and if src changes later, those
     51      * changes will not be reflected in this picture.
     52      */
     53     public Picture(Picture src) {
     54         this(nativeConstructor(src != null ? src.mNativePicture : 0), false);
     55     }
     56 
     57     /**
     58      * To record a picture, call beginRecording() and then draw into the Canvas
     59      * that is returned. Nothing we appear on screen, but all of the draw
     60      * commands (e.g. {@link Canvas#drawRect(Rect, Paint)}) will be recorded.
     61      * To stop recording, call endRecording(). After endRecording() the Canvas
     62      * that was returned must no longer be used, and nothing should be drawn
     63      * into it.
     64      */
     65     public Canvas beginRecording(int width, int height) {
     66         int ni = nativeBeginRecording(mNativePicture, width, height);
     67         mRecordingCanvas = new RecordingCanvas(this, ni);
     68         return mRecordingCanvas;
     69     }
     70 
     71     /**
     72      * Call endRecording when the picture is built. After this call, the picture
     73      * may be drawn, but the canvas that was returned by beginRecording must not
     74      * be used anymore. This is automatically called if {@link Picture#draw}
     75      * or {@link Canvas#drawPicture(Picture)} is called.
     76      */
     77     public void endRecording() {
     78         if (mRecordingCanvas != null) {
     79             mRecordingCanvas = null;
     80             nativeEndRecording(mNativePicture);
     81         }
     82     }
     83 
     84     /**
     85      * Get the width of the picture as passed to beginRecording. This
     86      * does not reflect (per se) the content of the picture.
     87      */
     88     public native int getWidth();
     89 
     90     /**
     91      * Get the height of the picture as passed to beginRecording. This
     92      * does not reflect (per se) the content of the picture.
     93      */
     94     public native int getHeight();
     95 
     96     /**
     97      * Draw this picture on the canvas. The picture may have the side effect
     98      * of changing the matrix and clip of the canvas.
     99      *
    100      * <p>
    101      * <strong>Note:</strong> This forces the picture to internally call
    102      * {@link Picture#endRecording()} in order to prepare for playback.
    103      *
    104      * @param canvas  The picture is drawn to this canvas
    105      */
    106     public void draw(Canvas canvas) {
    107         if (mRecordingCanvas != null) {
    108             endRecording();
    109         }
    110         nativeDraw(canvas.mNativeCanvas, mNativePicture);
    111     }
    112 
    113     /**
    114      * Create a new picture (already recorded) from the data in the stream. This
    115      * data was generated by a previous call to writeToStream(). Pictures that
    116      * have been persisted across device restarts are not guaranteed to decode
    117      * properly and are highly discouraged.
    118      *
    119      * <p>
    120      * <strong>Note:</strong> a picture created from an input stream cannot be
    121      * replayed on a hardware accelerated canvas.
    122      *
    123      * @see #writeToStream(java.io.OutputStream)
    124      * @deprecated The recommended alternative is to not use writeToStream and
    125      * instead draw the picture into a Bitmap from which you can persist it as
    126      * raw or compressed pixels.
    127      */
    128     @Deprecated
    129     public static Picture createFromStream(InputStream stream) {
    130         return new Picture(nativeCreateFromStream(stream, new byte[WORKING_STREAM_STORAGE]), true);
    131     }
    132 
    133     /**
    134      * Write the picture contents to a stream. The data can be used to recreate
    135      * the picture in this or another process by calling createFromStream(...)
    136      * The resulting stream is NOT to be persisted across device restarts as
    137      * there is no guarantee that the Picture can be successfully reconstructed.
    138      *
    139      * <p>
    140      * <strong>Note:</strong> a picture created from an input stream cannot be
    141      * replayed on a hardware accelerated canvas.
    142      *
    143      * @see #createFromStream(java.io.InputStream)
    144      * @deprecated The recommended alternative is to draw the picture into a
    145      * Bitmap from which you can persist it as raw or compressed pixels.
    146      */
    147     @Deprecated
    148     public void writeToStream(OutputStream stream) {
    149         // do explicit check before calling the native method
    150         if (stream == null) {
    151             throw new NullPointerException();
    152         }
    153         if (!nativeWriteToStream(mNativePicture, stream,
    154                              new byte[WORKING_STREAM_STORAGE])) {
    155             throw new RuntimeException();
    156         }
    157     }
    158 
    159     protected void finalize() throws Throwable {
    160         try {
    161             nativeDestructor(mNativePicture);
    162         } finally {
    163             super.finalize();
    164         }
    165     }
    166 
    167     final int ni() {
    168         return mNativePicture;
    169     }
    170 
    171     private Picture(int nativePicture, boolean fromStream) {
    172         if (nativePicture == 0) {
    173             throw new RuntimeException();
    174         }
    175         mNativePicture = nativePicture;
    176         createdFromStream = fromStream;
    177     }
    178 
    179     // return empty picture if src is 0, or a copy of the native src
    180     private static native int nativeConstructor(int nativeSrcOr0);
    181     private static native int nativeCreateFromStream(InputStream stream,
    182                                                 byte[] storage);
    183     private static native int nativeBeginRecording(int nativeCanvas,
    184                                                     int w, int h);
    185     private static native void nativeEndRecording(int nativeCanvas);
    186     private static native void nativeDraw(int nativeCanvas, int nativePicture);
    187     private static native boolean nativeWriteToStream(int nativePicture,
    188                                            OutputStream stream, byte[] storage);
    189     private static native void nativeDestructor(int nativePicture);
    190 
    191     private static class RecordingCanvas extends Canvas {
    192         private final Picture mPicture;
    193 
    194         public RecordingCanvas(Picture pict, int nativeCanvas) {
    195             super(nativeCanvas);
    196             mPicture = pict;
    197         }
    198 
    199         @Override
    200         public void setBitmap(Bitmap bitmap) {
    201             throw new RuntimeException(
    202                                 "Cannot call setBitmap on a picture canvas");
    203         }
    204 
    205         @Override
    206         public void drawPicture(Picture picture) {
    207             if (mPicture == picture) {
    208                 throw new RuntimeException(
    209                             "Cannot draw a picture into its recording canvas");
    210             }
    211             super.drawPicture(picture);
    212         }
    213     }
    214 }
    215 
    216