Home | History | Annotate | Download | only in media
      1 /*
      2  * Copyright 2015 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.media;
     18 
     19 import android.graphics.ImageFormat;
     20 import android.graphics.PixelFormat;
     21 import android.media.Image.Plane;
     22 import android.util.Size;
     23 
     24 import libcore.io.Memory;
     25 
     26 import java.nio.ByteBuffer;
     27 
     28 /**
     29  * Package private utility class for hosting commonly used Image related methods.
     30  */
     31 class ImageUtils {
     32 
     33     /**
     34      * Only a subset of the formats defined in
     35      * {@link android.graphics.ImageFormat ImageFormat} and
     36      * {@link android.graphics.PixelFormat PixelFormat} are supported by
     37      * ImageReader. When reading RGB data from a surface, the formats defined in
     38      * {@link android.graphics.PixelFormat PixelFormat} can be used; when
     39      * reading YUV, JPEG or raw sensor data (for example, from the camera or video
     40      * decoder), formats from {@link android.graphics.ImageFormat ImageFormat}
     41      * are used.
     42      */
     43     public static int getNumPlanesForFormat(int format) {
     44         switch (format) {
     45             case ImageFormat.YV12:
     46             case ImageFormat.YUV_420_888:
     47             case ImageFormat.NV21:
     48                 return 3;
     49             case ImageFormat.NV16:
     50                 return 2;
     51             case PixelFormat.RGB_565:
     52             case PixelFormat.RGBA_8888:
     53             case PixelFormat.RGBX_8888:
     54             case PixelFormat.RGB_888:
     55             case ImageFormat.JPEG:
     56             case ImageFormat.YUY2:
     57             case ImageFormat.Y8:
     58             case ImageFormat.Y16:
     59             case ImageFormat.RAW_SENSOR:
     60             case ImageFormat.RAW_PRIVATE:
     61             case ImageFormat.RAW10:
     62             case ImageFormat.RAW12:
     63             case ImageFormat.DEPTH16:
     64             case ImageFormat.DEPTH_POINT_CLOUD:
     65                 return 1;
     66             case ImageFormat.PRIVATE:
     67                 return 0;
     68             default:
     69                 throw new UnsupportedOperationException(
     70                         String.format("Invalid format specified %d", format));
     71         }
     72     }
     73 
     74     /**
     75      * <p>
     76      * Copy source image data to destination Image.
     77      * </p>
     78      * <p>
     79      * Only support the copy between two non-{@link ImageFormat#PRIVATE PRIVATE} format
     80      * images with same properties (format, size, etc.). The data from the
     81      * source image will be copied to the byteBuffers from the destination Image
     82      * starting from position zero, and the destination image will be rewound to
     83      * zero after copy is done.
     84      * </p>
     85      *
     86      * @param src The source image to be copied from.
     87      * @param dst The destination image to be copied to.
     88      * @throws IllegalArgumentException If the source and destination images
     89      *             have different format, or one of the images is not copyable.
     90      */
     91     public static void imageCopy(Image src, Image dst) {
     92         if (src == null || dst == null) {
     93             throw new IllegalArgumentException("Images should be non-null");
     94         }
     95         if (src.getFormat() != dst.getFormat()) {
     96             throw new IllegalArgumentException("Src and dst images should have the same format");
     97         }
     98         if (src.getFormat() == ImageFormat.PRIVATE ||
     99                 dst.getFormat() == ImageFormat.PRIVATE) {
    100             throw new IllegalArgumentException("PRIVATE format images are not copyable");
    101         }
    102         if (src.getFormat() == ImageFormat.RAW_PRIVATE) {
    103             throw new IllegalArgumentException(
    104                     "Copy of RAW_OPAQUE format has not been implemented");
    105         }
    106         if (!(dst.getOwner() instanceof ImageWriter)) {
    107             throw new IllegalArgumentException("Destination image is not from ImageWriter. Only"
    108                     + " the images from ImageWriter are writable");
    109         }
    110         Size srcSize = new Size(src.getWidth(), src.getHeight());
    111         Size dstSize = new Size(dst.getWidth(), dst.getHeight());
    112         if (!srcSize.equals(dstSize)) {
    113             throw new IllegalArgumentException("source image size " + srcSize + " is different"
    114                     + " with " + "destination image size " + dstSize);
    115         }
    116 
    117         Plane[] srcPlanes = src.getPlanes();
    118         Plane[] dstPlanes = dst.getPlanes();
    119         ByteBuffer srcBuffer = null;
    120         ByteBuffer dstBuffer = null;
    121         for (int i = 0; i < srcPlanes.length; i++) {
    122             int srcRowStride = srcPlanes[i].getRowStride();
    123             int dstRowStride = dstPlanes[i].getRowStride();
    124             srcBuffer = srcPlanes[i].getBuffer();
    125             dstBuffer = dstPlanes[i].getBuffer();
    126             if (!(srcBuffer.isDirect() && dstBuffer.isDirect())) {
    127                 throw new IllegalArgumentException("Source and destination ByteBuffers must be"
    128                         + " direct byteBuffer!");
    129             }
    130             if (srcPlanes[i].getPixelStride() != dstPlanes[i].getPixelStride()) {
    131                 throw new IllegalArgumentException("Source plane image pixel stride " +
    132                         srcPlanes[i].getPixelStride() +
    133                         " must be same as destination image pixel stride " +
    134                         dstPlanes[i].getPixelStride());
    135             }
    136 
    137             int srcPos = srcBuffer.position();
    138             srcBuffer.rewind();
    139             dstBuffer.rewind();
    140             if (srcRowStride == dstRowStride) {
    141                 // Fast path, just copy the content if the byteBuffer all together.
    142                 dstBuffer.put(srcBuffer);
    143             } else {
    144                 // Source and destination images may have different alignment requirements,
    145                 // therefore may have different strides. Copy row by row for such case.
    146                 int srcOffset = srcBuffer.position();
    147                 int dstOffset = dstBuffer.position();
    148                 Size effectivePlaneSize = getEffectivePlaneSizeForImage(src, i);
    149                 int srcByteCount = effectivePlaneSize.getWidth() * srcPlanes[i].getPixelStride();
    150                 for (int row = 0; row < effectivePlaneSize.getHeight(); row++) {
    151                     if (row == effectivePlaneSize.getHeight() - 1) {
    152                         // Special case for NV21 backed YUV420_888: need handle the last row
    153                         // carefully to avoid memory corruption. Check if we have enough bytes to
    154                         // copy.
    155                         int remainingBytes = srcBuffer.remaining() - srcOffset;
    156                         if (srcByteCount > remainingBytes) {
    157                             srcByteCount = remainingBytes;
    158                         }
    159                     }
    160                     directByteBufferCopy(srcBuffer, srcOffset, dstBuffer, dstOffset, srcByteCount);
    161                     srcOffset += srcRowStride;
    162                     dstOffset += dstRowStride;
    163                 }
    164             }
    165 
    166             srcBuffer.position(srcPos);
    167             dstBuffer.rewind();
    168         }
    169     }
    170 
    171     /**
    172      * Return the estimated native allocation size in bytes based on width, height, format,
    173      * and number of images.
    174      *
    175      * <p>This is a very rough estimation and should only be used for native allocation
    176      * registration in VM so it can be accounted for during GC.</p>
    177      *
    178      * @param width The width of the images.
    179      * @param height The height of the images.
    180      * @param format The format of the images.
    181      * @param numImages The number of the images.
    182      */
    183     public static int getEstimatedNativeAllocBytes(int width, int height, int format,
    184             int numImages) {
    185         double estimatedBytePerPixel;
    186         switch (format) {
    187             // 10x compression from RGB_888
    188             case ImageFormat.JPEG:
    189             case ImageFormat.DEPTH_POINT_CLOUD:
    190                 estimatedBytePerPixel = 0.3;
    191                 break;
    192             case ImageFormat.Y8:
    193                 estimatedBytePerPixel = 1.0;
    194                 break;
    195             case ImageFormat.RAW10:
    196                 estimatedBytePerPixel = 1.25;
    197                 break;
    198             case ImageFormat.YV12:
    199             case ImageFormat.YUV_420_888:
    200             case ImageFormat.NV21:
    201             case ImageFormat.RAW12:
    202             case ImageFormat.PRIVATE: // A rough estimate because the real size is unknown.
    203                 estimatedBytePerPixel = 1.5;
    204                 break;
    205             case ImageFormat.NV16:
    206             case PixelFormat.RGB_565:
    207             case ImageFormat.YUY2:
    208             case ImageFormat.Y16:
    209             case ImageFormat.RAW_SENSOR:
    210             case ImageFormat.RAW_PRIVATE: // round estimate, real size is unknown
    211             case ImageFormat.DEPTH16:
    212                 estimatedBytePerPixel = 2.0;
    213                 break;
    214             case PixelFormat.RGB_888:
    215                 estimatedBytePerPixel = 3.0;
    216                 break;
    217             case PixelFormat.RGBA_8888:
    218             case PixelFormat.RGBX_8888:
    219                 estimatedBytePerPixel = 4.0;
    220                 break;
    221             default:
    222                 throw new UnsupportedOperationException(
    223                         String.format("Invalid format specified %d", format));
    224         }
    225 
    226         return (int)(width * height * estimatedBytePerPixel * numImages);
    227     }
    228 
    229     private static Size getEffectivePlaneSizeForImage(Image image, int planeIdx) {
    230         switch (image.getFormat()) {
    231             case ImageFormat.YV12:
    232             case ImageFormat.YUV_420_888:
    233             case ImageFormat.NV21:
    234                 if (planeIdx == 0) {
    235                     return new Size(image.getWidth(), image.getHeight());
    236                 } else {
    237                     return new Size(image.getWidth() / 2, image.getHeight() / 2);
    238                 }
    239             case ImageFormat.NV16:
    240                 if (planeIdx == 0) {
    241                     return new Size(image.getWidth(), image.getHeight());
    242                 } else {
    243                     return new Size(image.getWidth(), image.getHeight() / 2);
    244                 }
    245             case PixelFormat.RGB_565:
    246             case PixelFormat.RGBA_8888:
    247             case PixelFormat.RGBX_8888:
    248             case PixelFormat.RGB_888:
    249             case ImageFormat.JPEG:
    250             case ImageFormat.YUY2:
    251             case ImageFormat.Y8:
    252             case ImageFormat.Y16:
    253             case ImageFormat.RAW_SENSOR:
    254             case ImageFormat.RAW10:
    255             case ImageFormat.RAW12:
    256                 return new Size(image.getWidth(), image.getHeight());
    257             case ImageFormat.PRIVATE:
    258                 return new Size(0, 0);
    259             default:
    260                 throw new UnsupportedOperationException(
    261                         String.format("Invalid image format %d", image.getFormat()));
    262         }
    263     }
    264 
    265     private static void directByteBufferCopy(ByteBuffer srcBuffer, int srcOffset,
    266             ByteBuffer dstBuffer, int dstOffset, int srcByteCount) {
    267         Memory.memmove(dstBuffer, dstOffset, srcBuffer, srcOffset, srcByteCount);
    268     }
    269 }
    270