blob: 2797a4daa92534215d863a851a9bb8ccbf7d0021 [file] [log] [blame]
John Recke94cbc72016-04-25 13:03:44 -07001/*
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
17package android.view;
18
19import android.annotation.IntDef;
20import android.annotation.NonNull;
John Reck95801462016-09-01 09:44:09 -070021import android.annotation.Nullable;
John Recke94cbc72016-04-25 13:03:44 -070022import android.graphics.Bitmap;
John Reck95801462016-09-01 09:44:09 -070023import android.graphics.Rect;
John Recke94cbc72016-04-25 13:03:44 -070024import android.os.Handler;
John Reck95801462016-09-01 09:44:09 -070025import android.view.ViewTreeObserver.OnDrawListener;
John Recke94cbc72016-04-25 13:03:44 -070026
27import java.lang.annotation.Retention;
28import java.lang.annotation.RetentionPolicy;
29
30/**
31 * Provides a mechanisms to issue pixel copy requests to allow for copy
32 * operations from {@link Surface} to {@link Bitmap}
33 */
34public final class PixelCopy {
35
36 /** @hide */
37 @Retention(RetentionPolicy.SOURCE)
38 @IntDef({SUCCESS, ERROR_UNKNOWN, ERROR_TIMEOUT, ERROR_SOURCE_NO_DATA,
39 ERROR_SOURCE_INVALID, ERROR_DESTINATION_INVALID})
40 public @interface CopyResultStatus {}
41
42 /** The pixel copy request succeeded */
43 public static final int SUCCESS = 0;
44
45 /** The pixel copy request failed with an unknown error. */
46 public static final int ERROR_UNKNOWN = 1;
47
48 /**
49 * A timeout occurred while trying to acquire a buffer from the source to
50 * copy from.
51 */
52 public static final int ERROR_TIMEOUT = 2;
53
54 /**
55 * The source has nothing to copy from. When the source is a {@link Surface}
56 * this means that no buffers have been queued yet. Wait for the source
57 * to produce a frame and try again.
58 */
59 public static final int ERROR_SOURCE_NO_DATA = 3;
60
61 /**
62 * It is not possible to copy from the source. This can happen if the source
63 * is hardware-protected or destroyed.
64 */
65 public static final int ERROR_SOURCE_INVALID = 4;
66
67 /**
68 * The destination isn't a valid copy target. If the destination is a bitmap
69 * this can occur if the bitmap is too large for the hardware to copy to.
70 * It can also occur if the destination has been destroyed.
71 */
72 public static final int ERROR_DESTINATION_INVALID = 5;
73
74 /**
75 * Listener for observing the completion of a PixelCopy request.
76 */
77 public interface OnPixelCopyFinishedListener {
78 /**
79 * Callback for when a pixel copy request has completed. This will be called
80 * regardless of whether the copy succeeded or failed.
81 *
82 * @param copyResult Contains the resulting status of the copy request.
83 * This will either be {@link PixelCopy#SUCCESS} or one of the
84 * <code>PixelCopy.ERROR_*</code> values.
85 */
86 void onPixelCopyFinished(@CopyResultStatus int copyResult);
87 }
88
89 /**
90 * Requests for the display content of a {@link SurfaceView} to be copied
91 * into a provided {@link Bitmap}.
92 *
93 * The contents of the source will be scaled to fit exactly inside the bitmap.
94 * The pixel format of the source buffer will be converted, as part of the copy,
95 * to fit the the bitmap's {@link Bitmap.Config}. The most recently queued buffer
96 * in the SurfaceView's Surface will be used as the source of the copy.
97 *
98 * @param source The source from which to copy
99 * @param dest The destination of the copy. The source will be scaled to
100 * match the width, height, and format of this bitmap.
101 * @param listener Callback for when the pixel copy request completes
102 * @param listenerThread The callback will be invoked on this Handler when
103 * the copy is finished.
104 */
105 public static void request(@NonNull SurfaceView source, @NonNull Bitmap dest,
106 @NonNull OnPixelCopyFinishedListener listener, @NonNull Handler listenerThread) {
107 request(source.getHolder().getSurface(), dest, listener, listenerThread);
108 }
109
110 /**
John Reck95801462016-09-01 09:44:09 -0700111 * Requests for the display content of a {@link SurfaceView} to be copied
112 * into a provided {@link Bitmap}.
113 *
114 * The contents of the source will be scaled to fit exactly inside the bitmap.
115 * The pixel format of the source buffer will be converted, as part of the copy,
116 * to fit the the bitmap's {@link Bitmap.Config}. The most recently queued buffer
117 * in the SurfaceView's Surface will be used as the source of the copy.
118 *
119 * @param source The source from which to copy
120 * @param srcRect The area of the source to copy from. If this is null
121 * the copy area will be the entire surface. The rect will be clamped to
122 * the bounds of the Surface.
123 * @param dest The destination of the copy. The source will be scaled to
124 * match the width, height, and format of this bitmap.
125 * @param listener Callback for when the pixel copy request completes
126 * @param listenerThread The callback will be invoked on this Handler when
127 * the copy is finished.
128 */
129 public static void request(@NonNull SurfaceView source, @Nullable Rect srcRect,
130 @NonNull Bitmap dest, @NonNull OnPixelCopyFinishedListener listener,
131 @NonNull Handler listenerThread) {
132 request(source.getHolder().getSurface(), srcRect,
133 dest, listener, listenerThread);
134 }
135
136 /**
John Recke94cbc72016-04-25 13:03:44 -0700137 * Requests a copy of the pixels from a {@link Surface} to be copied into
138 * a provided {@link Bitmap}.
139 *
140 * The contents of the source will be scaled to fit exactly inside the bitmap.
141 * The pixel format of the source buffer will be converted, as part of the copy,
142 * to fit the the bitmap's {@link Bitmap.Config}. The most recently queued buffer
143 * in the Surface will be used as the source of the copy.
144 *
145 * @param source The source from which to copy
146 * @param dest The destination of the copy. The source will be scaled to
147 * match the width, height, and format of this bitmap.
148 * @param listener Callback for when the pixel copy request completes
149 * @param listenerThread The callback will be invoked on this Handler when
150 * the copy is finished.
151 */
152 public static void request(@NonNull Surface source, @NonNull Bitmap dest,
153 @NonNull OnPixelCopyFinishedListener listener, @NonNull Handler listenerThread) {
John Reck95801462016-09-01 09:44:09 -0700154 request(source, null, dest, listener, listenerThread);
155 }
156
157 /**
158 * Requests a copy of the pixels at the provided {@link Rect} from
159 * a {@link Surface} to be copied into a provided {@link Bitmap}.
160 *
161 * The contents of the source rect will be scaled to fit exactly inside the bitmap.
162 * The pixel format of the source buffer will be converted, as part of the copy,
163 * to fit the the bitmap's {@link Bitmap.Config}. The most recently queued buffer
164 * in the Surface will be used as the source of the copy.
165 *
166 * @param source The source from which to copy
167 * @param srcRect The area of the source to copy from. If this is null
168 * the copy area will be the entire surface. The rect will be clamped to
169 * the bounds of the Surface.
170 * @param dest The destination of the copy. The source will be scaled to
171 * match the width, height, and format of this bitmap.
172 * @param listener Callback for when the pixel copy request completes
173 * @param listenerThread The callback will be invoked on this Handler when
174 * the copy is finished.
175 */
176 public static void request(@NonNull Surface source, @Nullable Rect srcRect,
177 @NonNull Bitmap dest, @NonNull OnPixelCopyFinishedListener listener,
178 @NonNull Handler listenerThread) {
John Recke94cbc72016-04-25 13:03:44 -0700179 validateBitmapDest(dest);
John Reckf3a51d62016-04-27 15:23:51 -0700180 if (!source.isValid()) {
181 throw new IllegalArgumentException("Surface isn't valid, source.isValid() == false");
182 }
John Reck95801462016-09-01 09:44:09 -0700183 if (srcRect != null && srcRect.isEmpty()) {
184 throw new IllegalArgumentException("sourceRect is empty");
185 }
John Recke94cbc72016-04-25 13:03:44 -0700186 // TODO: Make this actually async and fast and cool and stuff
John Reck95801462016-09-01 09:44:09 -0700187 int result = ThreadedRenderer.copySurfaceInto(source, srcRect, dest);
John Recke94cbc72016-04-25 13:03:44 -0700188 listenerThread.post(new Runnable() {
189 @Override
190 public void run() {
191 listener.onPixelCopyFinished(result);
192 }
193 });
194 }
195
John Reck95801462016-09-01 09:44:09 -0700196 /**
197 * Requests a copy of the pixels from a {@link Window} to be copied into
198 * a provided {@link Bitmap}.
199 *
200 * The contents of the source will be scaled to fit exactly inside the bitmap.
201 * The pixel format of the source buffer will be converted, as part of the copy,
202 * to fit the the bitmap's {@link Bitmap.Config}. The most recently queued buffer
203 * in the Window's Surface will be used as the source of the copy.
204 *
205 * Note: This is limited to being able to copy from Window's with a non-null
206 * DecorView. If {@link Window#peekDecorView()} is null this throws an
207 * {@link IllegalArgumentException}. It will similarly throw an exception
208 * if the DecorView has not yet acquired a backing surface. It is recommended
209 * that {@link OnDrawListener} is used to ensure that at least one draw
210 * has happened before trying to copy from the window, otherwise either
211 * an {@link IllegalArgumentException} will be thrown or an error will
212 * be returned to the {@link OnPixelCopyFinishedListener}.
213 *
214 * @param source The source from which to copy
215 * @param dest The destination of the copy. The source will be scaled to
216 * match the width, height, and format of this bitmap.
217 * @param listener Callback for when the pixel copy request completes
218 * @param listenerThread The callback will be invoked on this Handler when
219 * the copy is finished.
220 */
221 public static void request(@NonNull Window source, @NonNull Bitmap dest,
222 @NonNull OnPixelCopyFinishedListener listener, @NonNull Handler listenerThread) {
223 request(source, null, dest, listener, listenerThread);
224 }
225
226 /**
227 * Requests a copy of the pixels at the provided {@link Rect} from
228 * a {@link Window} to be copied into a provided {@link Bitmap}.
229 *
230 * The contents of the source rect will be scaled to fit exactly inside the bitmap.
231 * The pixel format of the source buffer will be converted, as part of the copy,
232 * to fit the the bitmap's {@link Bitmap.Config}. The most recently queued buffer
233 * in the Window's Surface will be used as the source of the copy.
234 *
235 * Note: This is limited to being able to copy from Window's with a non-null
236 * DecorView. If {@link Window#peekDecorView()} is null this throws an
237 * {@link IllegalArgumentException}. It will similarly throw an exception
238 * if the DecorView has not yet acquired a backing surface. It is recommended
239 * that {@link OnDrawListener} is used to ensure that at least one draw
240 * has happened before trying to copy from the window, otherwise either
241 * an {@link IllegalArgumentException} will be thrown or an error will
242 * be returned to the {@link OnPixelCopyFinishedListener}.
243 *
244 * @param source The source from which to copy
245 * @param srcRect The area of the source to copy from. If this is null
246 * the copy area will be the entire surface. The rect will be clamped to
247 * the bounds of the Surface.
248 * @param dest The destination of the copy. The source will be scaled to
249 * match the width, height, and format of this bitmap.
250 * @param listener Callback for when the pixel copy request completes
251 * @param listenerThread The callback will be invoked on this Handler when
252 * the copy is finished.
253 */
254 public static void request(@NonNull Window source, @Nullable Rect srcRect,
255 @NonNull Bitmap dest, @NonNull OnPixelCopyFinishedListener listener,
256 @NonNull Handler listenerThread) {
257 validateBitmapDest(dest);
258 if (source == null) {
259 throw new IllegalArgumentException("source is null");
260 }
261 if (source.peekDecorView() == null) {
262 throw new IllegalArgumentException(
263 "Only able to copy windows with decor views");
264 }
265 Surface surface = null;
John Reck9bdd2e62018-02-21 11:02:20 -0800266 final ViewRootImpl root = source.peekDecorView().getViewRootImpl();
267 if (root != null) {
268 surface = root.mSurface;
269 final Rect surfaceInsets = root.mWindowAttributes.surfaceInsets;
270 if (srcRect == null) {
271 srcRect = new Rect(surfaceInsets.left, surfaceInsets.top,
272 root.mWidth + surfaceInsets.left, root.mHeight + surfaceInsets.top);
273 } else {
274 srcRect.offset(surfaceInsets.left, surfaceInsets.top);
275 }
John Reck95801462016-09-01 09:44:09 -0700276 }
277 if (surface == null || !surface.isValid()) {
278 throw new IllegalArgumentException(
279 "Window doesn't have a backing surface!");
280 }
281 request(surface, srcRect, dest, listener, listenerThread);
282 }
283
John Recke94cbc72016-04-25 13:03:44 -0700284 private static void validateBitmapDest(Bitmap bitmap) {
285 // TODO: Pre-check max texture dimens if we can
286 if (bitmap == null) {
287 throw new IllegalArgumentException("Bitmap cannot be null");
288 }
289 if (bitmap.isRecycled()) {
290 throw new IllegalArgumentException("Bitmap is recycled");
291 }
292 if (!bitmap.isMutable()) {
293 throw new IllegalArgumentException("Bitmap is immutable");
294 }
295 }
296
297 private PixelCopy() {}
298}