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