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