/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.graphics;

import java.io.InputStream;
import java.io.OutputStream;

A Picture records drawing calls (via the canvas returned by beginRecording) and can then play them back into Canvas (via draw(Canvas) or Canvas.drawPicture(Picture)).For most content (e.g. text, lines, rectangles), drawing a sequence from a picture can be faster than the equivalent API calls, since the picture performs its playback without incurring any method-call overhead.

Note: Prior to API level 23 a picture cannot be replayed on a hardware accelerated canvas.

/** * A Picture records drawing calls (via the canvas returned by beginRecording) * and can then play them back into Canvas (via {@link Picture#draw(Canvas)} or * {@link Canvas#drawPicture(Picture)}).For most content (e.g. text, lines, rectangles), * drawing a sequence from a picture can be faster than the equivalent API * calls, since the picture performs its playback without incurring any * method-call overhead. * * <p class="note"><strong>Note:</strong> Prior to API level 23 a picture cannot * be replayed on a hardware accelerated canvas.</p> */
public class Picture { private PictureCanvas mRecordingCanvas; private long mNativePicture; private boolean mRequiresHwAcceleration; private static final int WORKING_STREAM_STORAGE = 16 * 1024;
Creates an empty picture that is ready to record.
/** * Creates an empty picture that is ready to record. */
public Picture() { this(nativeConstructor(0)); }
Create a picture by making a copy of what has already been recorded in src. The contents of src are unchanged, and if src changes later, those changes will not be reflected in this picture.
/** * Create a picture by making a copy of what has already been recorded in * src. The contents of src are unchanged, and if src changes later, those * changes will not be reflected in this picture. */
public Picture(Picture src) { this(nativeConstructor(src != null ? src.mNativePicture : 0)); } private Picture(long nativePicture) { if (nativePicture == 0) { throw new RuntimeException(); } mNativePicture = nativePicture; } @Override protected void finalize() throws Throwable { try { nativeDestructor(mNativePicture); mNativePicture = 0; } finally { super.finalize(); } }
To record a picture, call beginRecording() and then draw into the Canvas that is returned. Nothing we appear on screen, but all of the draw commands (e.g. Canvas.drawRect(Rect, Paint)) will be recorded. To stop recording, call endRecording(). After endRecording() the Canvas that was returned must no longer be used, and nothing should be drawn into it.
/** * To record a picture, call beginRecording() and then draw into the Canvas * that is returned. Nothing we appear on screen, but all of the draw * commands (e.g. {@link Canvas#drawRect(Rect, Paint)}) will be recorded. * To stop recording, call endRecording(). After endRecording() the Canvas * that was returned must no longer be used, and nothing should be drawn * into it. */
public Canvas beginRecording(int width, int height) { if (mRecordingCanvas != null) { throw new IllegalStateException("Picture already recording, must call #endRecording()"); } long ni = nativeBeginRecording(mNativePicture, width, height); mRecordingCanvas = new PictureCanvas(this, ni); mRequiresHwAcceleration = false; return mRecordingCanvas; }
Call endRecording when the picture is built. After this call, the picture may be drawn, but the canvas that was returned by beginRecording must not be used anymore. This is automatically called if draw or Canvas.drawPicture(Picture) is called.
/** * Call endRecording when the picture is built. After this call, the picture * may be drawn, but the canvas that was returned by beginRecording must not * be used anymore. This is automatically called if {@link Picture#draw} * or {@link Canvas#drawPicture(Picture)} is called. */
public void endRecording() { if (mRecordingCanvas != null) { mRequiresHwAcceleration = mRecordingCanvas.mHoldsHwBitmap; mRecordingCanvas = null; nativeEndRecording(mNativePicture); } }
Get the width of the picture as passed to beginRecording. This does not reflect (per se) the content of the picture.
/** * Get the width of the picture as passed to beginRecording. This * does not reflect (per se) the content of the picture. */
public int getWidth() { return nativeGetWidth(mNativePicture); }
Get the height of the picture as passed to beginRecording. This does not reflect (per se) the content of the picture.
/** * Get the height of the picture as passed to beginRecording. This * does not reflect (per se) the content of the picture. */
public int getHeight() { return nativeGetHeight(mNativePicture); }
Indicates whether or not this Picture contains recorded commands that only work when drawn to a hardware-accelerated canvas. If this returns true then this Picture can only be drawn to another Picture or to a Canvas where canvas.isHardwareAccelerated() is true. Note this value is only updated after recording has finished by a call to endRecording(). Prior to that it will be the default value of false.
Returns:true if the Picture can only be drawn to a hardware-accelerated canvas, false otherwise.
/** * Indicates whether or not this Picture contains recorded commands that only work when * drawn to a hardware-accelerated canvas. If this returns true then this Picture can only * be drawn to another Picture or to a Canvas where canvas.isHardwareAccelerated() is true. * * Note this value is only updated after recording has finished by a call to * {@link #endRecording()}. Prior to that it will be the default value of false. * * @return true if the Picture can only be drawn to a hardware-accelerated canvas, * false otherwise. */
public boolean requiresHardwareAcceleration() { return mRequiresHwAcceleration; }
Draw this picture on the canvas.

Prior to VERSION_CODES.LOLLIPOP, this call could have the side effect of changing the matrix and clip of the canvas if this picture had imbalanced saves/restores.

Note: This forces the picture to internally call endRecording() in order to prepare for playback.

Params:
  • canvas – The picture is drawn to this canvas
/** * Draw this picture on the canvas. * <p> * Prior to {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this call could * have the side effect of changing the matrix and clip of the canvas * if this picture had imbalanced saves/restores. * * <p> * <strong>Note:</strong> This forces the picture to internally call * {@link Picture#endRecording()} in order to prepare for playback. * * @param canvas The picture is drawn to this canvas */
public void draw(Canvas canvas) { if (mRecordingCanvas != null) { endRecording(); } if (mRequiresHwAcceleration && !canvas.isHardwareAccelerated()) { canvas.onHwBitmapInSwMode(); } nativeDraw(canvas.getNativeCanvasWrapper(), mNativePicture); }
Create a new picture (already recorded) from the data in the stream. This data was generated by a previous call to writeToStream(). Pictures that have been persisted across device restarts are not guaranteed to decode properly and are highly discouraged.
See Also:
  • writeToStream(OutputStream)
Deprecated:The recommended alternative is to not use writeToStream and instead draw the picture into a Bitmap from which you can persist it as raw or compressed pixels.
/** * Create a new picture (already recorded) from the data in the stream. This * data was generated by a previous call to writeToStream(). Pictures that * have been persisted across device restarts are not guaranteed to decode * properly and are highly discouraged. * * @see #writeToStream(java.io.OutputStream) * @deprecated The recommended alternative is to not use writeToStream and * instead draw the picture into a Bitmap from which you can persist it as * raw or compressed pixels. */
@Deprecated public static Picture createFromStream(InputStream stream) { return new Picture(nativeCreateFromStream(stream, new byte[WORKING_STREAM_STORAGE])); }
Write the picture contents to a stream. The data can be used to recreate the picture in this or another process by calling createFromStream(...) The resulting stream is NOT to be persisted across device restarts as there is no guarantee that the Picture can be successfully reconstructed.
See Also:
  • createFromStream(InputStream)
Deprecated:The recommended alternative is to draw the picture into a Bitmap from which you can persist it as raw or compressed pixels.
/** * Write the picture contents to a stream. The data can be used to recreate * the picture in this or another process by calling createFromStream(...) * The resulting stream is NOT to be persisted across device restarts as * there is no guarantee that the Picture can be successfully reconstructed. * * @see #createFromStream(java.io.InputStream) * @deprecated The recommended alternative is to draw the picture into a * Bitmap from which you can persist it as raw or compressed pixels. */
@Deprecated public void writeToStream(OutputStream stream) { // do explicit check before calling the native method if (stream == null) { throw new NullPointerException(); } if (!nativeWriteToStream(mNativePicture, stream, new byte[WORKING_STREAM_STORAGE])) { throw new RuntimeException(); } } // return empty picture if src is 0, or a copy of the native src private static native long nativeConstructor(long nativeSrcOr0); private static native long nativeCreateFromStream(InputStream stream, byte[] storage); private static native int nativeGetWidth(long nativePicture); private static native int nativeGetHeight(long nativePicture); private static native long nativeBeginRecording(long nativeCanvas, int w, int h); private static native void nativeEndRecording(long nativeCanvas); private static native void nativeDraw(long nativeCanvas, long nativePicture); private static native boolean nativeWriteToStream(long nativePicture, OutputStream stream, byte[] storage); private static native void nativeDestructor(long nativePicture); private static class PictureCanvas extends Canvas { private final Picture mPicture; boolean mHoldsHwBitmap; public PictureCanvas(Picture pict, long nativeCanvas) { super(nativeCanvas); mPicture = pict; // Disable bitmap density scaling. This matches DisplayListCanvas. mDensity = 0; } @Override public void setBitmap(Bitmap bitmap) { throw new RuntimeException("Cannot call setBitmap on a picture canvas"); } @Override public void drawPicture(Picture picture) { if (mPicture == picture) { throw new RuntimeException("Cannot draw a picture into its recording canvas"); } super.drawPicture(picture); } @Override protected void onHwBitmapInSwMode() { mHoldsHwBitmap = true; } } }