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.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          */
     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      */
    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      */
    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 
    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 
    152     private PixelCopy() {}
    153 }
    154