Home | History | Annotate | Download | only in view
      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          */
     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     /**
    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      */
    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      */
    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      */
    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      */
    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;
    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 
    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 
    297     private PixelCopy() {}
    298 }
    299