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.annotation.Nullable; 22 import android.graphics.Bitmap; 23 import android.graphics.Rect; 24 import android.os.Handler; 25 import android.view.ViewTreeObserver.OnDrawListener; 26 27 import java.lang.annotation.Retention; 28 import 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 */ 34 public 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 */ onPixelCopyFinished(@opyResultStatus int copyResult)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 */ request(@onNull SurfaceView source, @NonNull Bitmap dest, @NonNull OnPixelCopyFinishedListener listener, @NonNull Handler listenerThread)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 /** 111 * 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 */ request(@onNull SurfaceView source, @Nullable Rect srcRect, @NonNull Bitmap dest, @NonNull OnPixelCopyFinishedListener listener, @NonNull Handler listenerThread)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 /** 137 * 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 */ request(@onNull Surface source, @NonNull Bitmap dest, @NonNull OnPixelCopyFinishedListener listener, @NonNull Handler listenerThread)152 public static void request(@NonNull Surface source, @NonNull Bitmap dest, 153 @NonNull OnPixelCopyFinishedListener listener, @NonNull Handler listenerThread) { 154 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 */ request(@onNull Surface source, @Nullable Rect srcRect, @NonNull Bitmap dest, @NonNull OnPixelCopyFinishedListener listener, @NonNull Handler listenerThread)176 public static void request(@NonNull Surface source, @Nullable Rect srcRect, 177 @NonNull Bitmap dest, @NonNull OnPixelCopyFinishedListener listener, 178 @NonNull Handler listenerThread) { 179 validateBitmapDest(dest); 180 if (!source.isValid()) { 181 throw new IllegalArgumentException("Surface isn't valid, source.isValid() == false"); 182 } 183 if (srcRect != null && srcRect.isEmpty()) { 184 throw new IllegalArgumentException("sourceRect is empty"); 185 } 186 // TODO: Make this actually async and fast and cool and stuff 187 int result = ThreadedRenderer.copySurfaceInto(source, srcRect, dest); 188 listenerThread.post(new Runnable() { 189 @Override 190 public void run() { 191 listener.onPixelCopyFinished(result); 192 } 193 }); 194 } 195 196 /** 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 */ request(@onNull Window source, @NonNull Bitmap dest, @NonNull OnPixelCopyFinishedListener listener, @NonNull Handler listenerThread)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 */ request(@onNull Window source, @Nullable Rect srcRect, @NonNull Bitmap dest, @NonNull OnPixelCopyFinishedListener listener, @NonNull Handler listenerThread)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; 266 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 } 276 } 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 validateBitmapDest(Bitmap bitmap)284 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 PixelCopy()297 private PixelCopy() {} 298 } 299