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 (via picture.draw(canvas) or canvas.drawPicture). 25 * The picture's contents can also be written to a stream, and then later 26 * restored to a new picture (via writeToStream / createFromStream). For most 27 * content (esp. text, lines, rectangles), drawing a sequence from a picture can 28 * be faster than the equivalent API calls, since the picture performs its 29 * playback without incurring any java-call overhead. 30 */ 31public class Picture { 32 private Canvas mRecordingCanvas; 33 private final int mNativePicture; 34 35 /** 36 * @hide 37 */ 38 public final boolean createdFromStream; 39 40 private static final int WORKING_STREAM_STORAGE = 16 * 1024; 41 42 public Picture() { 43 this(nativeConstructor(0), false); 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), false); 53 } 54 55 /** 56 * To record a picture, call beginRecording() and then draw into the Canvas 57 * that is returned. Nothing we appear on screen, but all of the draw 58 * commands (e.g. drawRect(...)) will be recorded. To stop recording, call 59 * endRecording(). At this point the Canvas that was returned must no longer 60 * be referenced, and nothing should be drawn into it. 61 */ 62 public Canvas beginRecording(int width, int height) { 63 int ni = nativeBeginRecording(mNativePicture, width, height); 64 mRecordingCanvas = new RecordingCanvas(this, ni); 65 return mRecordingCanvas; 66 } 67 68 /** 69 * Call endRecording when the picture is built. After this call, the picture 70 * may be drawn, but the canvas that was returned by beginRecording must not 71 * be referenced anymore. This is automatically called if Picture.draw() or 72 * Canvas.drawPicture() is called. 73 */ 74 public void endRecording() { 75 if (mRecordingCanvas != null) { 76 mRecordingCanvas = null; 77 nativeEndRecording(mNativePicture); 78 } 79 } 80 81 /** 82 * Get the width of the picture as passed to beginRecording. This 83 * does not reflect (per se) the content of the picture. 84 */ 85 public native int getWidth(); 86 87 /** 88 * Get the height of the picture as passed to beginRecording. This 89 * does not reflect (per se) the content of the picture. 90 */ 91 public native int getHeight(); 92 93 /** 94 * Draw this picture on the canvas. The picture may have the side effect 95 * of changing the matrix and clip of the canvas. 96 * 97 * @param canvas The picture is drawn to this canvas 98 */ 99 public void draw(Canvas canvas) { 100 if (mRecordingCanvas != null) { 101 endRecording(); 102 } 103 nativeDraw(canvas.mNativeCanvas, mNativePicture); 104 } 105 106 /** 107 * Create a new picture (already recorded) from the data in the stream. This 108 * data was generated by a previous call to writeToStream(). 109 * 110 * <strong>Note:</strong> a picture created from an input stream cannot be 111 * replayed on a hardware accelerated canvas. 112 * 113 * @see #writeToStream(java.io.OutputStream) 114 */ 115 public static Picture createFromStream(InputStream stream) { 116 return new Picture(nativeCreateFromStream(stream, new byte[WORKING_STREAM_STORAGE]), true); 117 } 118 119 /** 120 * Write the picture contents to a stream. The data can be used to recreate 121 * the picture in this or another process by calling createFromStream. 122 * 123 * <strong>Note:</strong> a picture created from an input stream cannot be 124 * replayed on a hardware accelerated canvas. 125 * 126 * @see #createFromStream(java.io.InputStream) 127 */ 128 public void writeToStream(OutputStream stream) { 129 // do explicit check before calling the native method 130 if (stream == null) { 131 throw new NullPointerException(); 132 } 133 if (!nativeWriteToStream(mNativePicture, stream, 134 new byte[WORKING_STREAM_STORAGE])) { 135 throw new RuntimeException(); 136 } 137 } 138 139 protected void finalize() throws Throwable { 140 try { 141 nativeDestructor(mNativePicture); 142 } finally { 143 super.finalize(); 144 } 145 } 146 147 final int ni() { 148 return mNativePicture; 149 } 150 151 private Picture(int nativePicture, boolean fromStream) { 152 if (nativePicture == 0) { 153 throw new RuntimeException(); 154 } 155 mNativePicture = nativePicture; 156 createdFromStream = fromStream; 157 } 158 159 // return empty picture if src is 0, or a copy of the native src 160 private static native int nativeConstructor(int nativeSrcOr0); 161 private static native int nativeCreateFromStream(InputStream stream, 162 byte[] storage); 163 private static native int nativeBeginRecording(int nativeCanvas, 164 int w, int h); 165 private static native void nativeEndRecording(int nativeCanvas); 166 private static native void nativeDraw(int nativeCanvas, int nativePicture); 167 private static native boolean nativeWriteToStream(int nativePicture, 168 OutputStream stream, byte[] storage); 169 private static native void nativeDestructor(int nativePicture); 170 171 private static class RecordingCanvas extends Canvas { 172 private final Picture mPicture; 173 174 public RecordingCanvas(Picture pict, int nativeCanvas) { 175 super(nativeCanvas); 176 mPicture = pict; 177 } 178 179 @Override 180 public void setBitmap(Bitmap bitmap) { 181 throw new RuntimeException( 182 "Cannot call setBitmap on a picture canvas"); 183 } 184 185 @Override 186 public void drawPicture(Picture picture) { 187 if (mPicture == picture) { 188 throw new RuntimeException( 189 "Cannot draw a picture into its recording canvas"); 190 } 191 super.drawPicture(picture); 192 } 193 } 194} 195 196