1 /*
2  * Copyright (C) 2016 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.view;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.graphics.Bitmap;
22 import android.os.Handler;
23 
24 import java.lang.annotation.Retention;
25 import java.lang.annotation.RetentionPolicy;
26 
27 /**
28  * Provides a mechanisms to issue pixel copy requests to allow for copy
29  * operations from {@link Surface} to {@link Bitmap}
30  */
31 public final class PixelCopy {
32 
33     /** @hide */
34     @Retention(RetentionPolicy.SOURCE)
35     @IntDef({SUCCESS, ERROR_UNKNOWN, ERROR_TIMEOUT, ERROR_SOURCE_NO_DATA,
36         ERROR_SOURCE_INVALID, ERROR_DESTINATION_INVALID})
37     public @interface CopyResultStatus {}
38 
39     /** The pixel copy request succeeded */
40     public static final int SUCCESS = 0;
41 
42     /** The pixel copy request failed with an unknown error. */
43     public static final int ERROR_UNKNOWN = 1;
44 
45     /**
46      * A timeout occurred while trying to acquire a buffer from the source to
47      * copy from.
48      */
49     public static final int ERROR_TIMEOUT = 2;
50 
51     /**
52      * The source has nothing to copy from. When the source is a {@link Surface}
53      * this means that no buffers have been queued yet. Wait for the source
54      * to produce a frame and try again.
55      */
56     public static final int ERROR_SOURCE_NO_DATA = 3;
57 
58     /**
59      * It is not possible to copy from the source. This can happen if the source
60      * is hardware-protected or destroyed.
61      */
62     public static final int ERROR_SOURCE_INVALID = 4;
63 
64     /**
65      * The destination isn't a valid copy target. If the destination is a bitmap
66      * this can occur if the bitmap is too large for the hardware to copy to.
67      * It can also occur if the destination has been destroyed.
68      */
69     public static final int ERROR_DESTINATION_INVALID = 5;
70 
71     /**
72      * Listener for observing the completion of a PixelCopy request.
73      */
74     public interface OnPixelCopyFinishedListener {
75         /**
76          * Callback for when a pixel copy request has completed. This will be called
77          * regardless of whether the copy succeeded or failed.
78          *
79          * @param copyResult Contains the resulting status of the copy request.
80          * This will either be {@link PixelCopy#SUCCESS} or one of the
81          * <code>PixelCopy.ERROR_*</code> values.
82          */
onPixelCopyFinished(@opyResultStatus int copyResult)83         void onPixelCopyFinished(@CopyResultStatus int copyResult);
84     }
85 
86     /**
87      * Requests for the display content of a {@link SurfaceView} to be copied
88      * into a provided {@link Bitmap}.
89      *
90      * The contents of the source will be scaled to fit exactly inside the bitmap.
91      * The pixel format of the source buffer will be converted, as part of the copy,
92      * to fit the the bitmap's {@link Bitmap.Config}. The most recently queued buffer
93      * in the SurfaceView's Surface will be used as the source of the copy.
94      *
95      * @param source The source from which to copy
96      * @param dest The destination of the copy. The source will be scaled to
97      * match the width, height, and format of this bitmap.
98      * @param listener Callback for when the pixel copy request completes
99      * @param listenerThread The callback will be invoked on this Handler when
100      * the copy is finished.
101      */
request(@onNull SurfaceView source, @NonNull Bitmap dest, @NonNull OnPixelCopyFinishedListener listener, @NonNull Handler listenerThread)102     public static void request(@NonNull SurfaceView source, @NonNull Bitmap dest,
103             @NonNull OnPixelCopyFinishedListener listener, @NonNull Handler listenerThread) {
104         request(source.getHolder().getSurface(), dest, listener, listenerThread);
105     }
106 
107     /**
108      * Requests a copy of the pixels from a {@link Surface} to be copied into
109      * a provided {@link Bitmap}.
110      *
111      * The contents of the source will be scaled to fit exactly inside the bitmap.
112      * The pixel format of the source buffer will be converted, as part of the copy,
113      * to fit the the bitmap's {@link Bitmap.Config}. The most recently queued buffer
114      * in the Surface will be used as the source of the copy.
115      *
116      * @param source The source from which to copy
117      * @param dest The destination of the copy. The source will be scaled to
118      * match the width, height, and format of this bitmap.
119      * @param listener Callback for when the pixel copy request completes
120      * @param listenerThread The callback will be invoked on this Handler when
121      * the copy is finished.
122      */
request(@onNull Surface source, @NonNull Bitmap dest, @NonNull OnPixelCopyFinishedListener listener, @NonNull Handler listenerThread)123     public static void request(@NonNull Surface source, @NonNull Bitmap dest,
124             @NonNull OnPixelCopyFinishedListener listener, @NonNull Handler listenerThread) {
125         validateBitmapDest(dest);
126         if (!source.isValid()) {
127             throw new IllegalArgumentException("Surface isn't valid, source.isValid() == false");
128         }
129         // TODO: Make this actually async and fast and cool and stuff
130         int result = ThreadedRenderer.copySurfaceInto(source, dest);
131         listenerThread.post(new Runnable() {
132             @Override
133             public void run() {
134                 listener.onPixelCopyFinished(result);
135             }
136         });
137     }
138 
validateBitmapDest(Bitmap bitmap)139     private static void validateBitmapDest(Bitmap bitmap) {
140         // TODO: Pre-check max texture dimens if we can
141         if (bitmap == null) {
142             throw new IllegalArgumentException("Bitmap cannot be null");
143         }
144         if (bitmap.isRecycled()) {
145             throw new IllegalArgumentException("Bitmap is recycled");
146         }
147         if (!bitmap.isMutable()) {
148             throw new IllegalArgumentException("Bitmap is immutable");
149         }
150     }
151 
PixelCopy()152     private PixelCopy() {}
153 }
154