Home | History | Annotate | Download | only in graphics
      1 /*
      2  * Copyright (C) 2010 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.graphics;
     18 
     19 import java.io.OutputStream;
     20 
     21 /**
     22  * YuvImage contains YUV data and provides a method that compresses a region of
     23  * the YUV data to a Jpeg. The YUV data should be provided as a single byte
     24  * array irrespective of the number of image planes in it.
     25  * Currently only ImageFormat.NV21 and ImageFormat.YUY2 are supported.
     26  *
     27  * To compress a rectangle region in the YUV data, users have to specify the
     28  * region by left, top, width and height.
     29  */
     30 public class YuvImage {
     31 
     32     /**
     33      * Number of bytes of temp storage we use for communicating between the
     34      * native compressor and the java OutputStream.
     35      */
     36     private final static int WORKING_COMPRESS_STORAGE = 4096;
     37 
     38    /**
     39      * The YUV format as defined in {@link PixelFormat}.
     40      */
     41     private int mFormat;
     42 
     43     /**
     44      * The raw YUV data.
     45      * In the case of more than one image plane, the image planes must be
     46      * concatenated into a single byte array.
     47      */
     48     private byte[] mData;
     49 
     50     /**
     51      * The number of row bytes in each image plane.
     52      */
     53     private int[] mStrides;
     54 
     55     /**
     56      * The width of the image.
     57      */
     58     private int mWidth;
     59 
     60     /**
     61      * The height of the the image.
     62      */
     63     private int mHeight;
     64 
     65     /**
     66      * Construct an YuvImage.
     67      *
     68      * @param yuv     The YUV data. In the case of more than one image plane, all the planes must be
     69      *                concatenated into a single byte array.
     70      * @param format  The YUV data format as defined in {@link PixelFormat}.
     71      * @param width   The width of the YuvImage.
     72      * @param height  The height of the YuvImage.
     73      * @param strides (Optional) Row bytes of each image plane. If yuv contains padding, the stride
     74      *                of each image must be provided. If strides is null, the method assumes no
     75      *                padding and derives the row bytes by format and width itself.
     76      * @throws IllegalArgumentException if format is not support; width or height <= 0; or yuv is
     77      *                null.
     78      */
     79     public YuvImage(byte[] yuv, int format, int width, int height, int[] strides) {
     80         if (format != ImageFormat.NV21 &&
     81                 format != ImageFormat.YUY2) {
     82             throw new IllegalArgumentException(
     83                     "only support ImageFormat.NV21 " +
     84                     "and ImageFormat.YUY2 for now");
     85         }
     86 
     87         if (width <= 0  || height <= 0) {
     88             throw new IllegalArgumentException(
     89                     "width and height must large than 0");
     90         }
     91 
     92         if (yuv == null) {
     93             throw new IllegalArgumentException("yuv cannot be null");
     94         }
     95 
     96         if (strides == null) {
     97             mStrides = calculateStrides(width, format);
     98         } else {
     99             mStrides = strides;
    100         }
    101 
    102         mData = yuv;
    103         mFormat = format;
    104         mWidth = width;
    105         mHeight = height;
    106     }
    107 
    108     /**
    109      * Compress a rectangle region in the YuvImage to a jpeg.
    110      * Only ImageFormat.NV21 and ImageFormat.YUY2
    111      * are supported for now.
    112      *
    113      * @param rectangle The rectangle region to be compressed. The medthod checks if rectangle is
    114      *                  inside the image. Also, the method modifies rectangle if the chroma pixels
    115      *                  in it are not matched with the luma pixels in it.
    116      * @param quality   Hint to the compressor, 0-100. 0 meaning compress for
    117      *                  small size, 100 meaning compress for max quality.
    118      * @param stream    OutputStream to write the compressed data.
    119      * @return          True if the compression is successful.
    120      * @throws IllegalArgumentException if rectangle is invalid; quality is not within [0,
    121      *                  100]; or stream is null.
    122      */
    123     public boolean compressToJpeg(Rect rectangle, int quality, OutputStream stream) {
    124         Rect wholeImage = new Rect(0, 0, mWidth, mHeight);
    125         if (!wholeImage.contains(rectangle)) {
    126             throw new IllegalArgumentException(
    127                     "rectangle is not inside the image");
    128         }
    129 
    130         if (quality < 0 || quality > 100) {
    131             throw new IllegalArgumentException("quality must be 0..100");
    132         }
    133 
    134         if (stream == null) {
    135             throw new IllegalArgumentException("stream cannot be null");
    136         }
    137 
    138         adjustRectangle(rectangle);
    139         int[] offsets = calculateOffsets(rectangle.left, rectangle.top);
    140 
    141         return nativeCompressToJpeg(mData, mFormat, rectangle.width(),
    142                 rectangle.height(), offsets, mStrides, quality, stream,
    143                 new byte[WORKING_COMPRESS_STORAGE]);
    144     }
    145 
    146 
    147    /**
    148      * @return the YUV data.
    149      */
    150     public byte[] getYuvData() {
    151         return mData;
    152     }
    153 
    154     /**
    155      * @return the YUV format as defined in {@link PixelFormat}.
    156      */
    157     public int getYuvFormat() {
    158         return mFormat;
    159     }
    160 
    161     /**
    162      * @return the number of row bytes in each image plane.
    163      */
    164     public int[] getStrides() {
    165         return mStrides;
    166     }
    167 
    168     /**
    169      * @return the width of the image.
    170      */
    171     public int getWidth() {
    172         return mWidth;
    173     }
    174 
    175     /**
    176      * @return the height of the image.
    177      */
    178     public int getHeight() {
    179         return mHeight;
    180     }
    181 
    182     int[] calculateOffsets(int left, int top) {
    183         int[] offsets = null;
    184         if (mFormat == ImageFormat.NV21) {
    185             offsets = new int[] {top * mStrides[0] + left,
    186                   mHeight * mStrides[0] + top / 2 * mStrides[1]
    187                   + left / 2 * 2 };
    188             return offsets;
    189         }
    190 
    191         if (mFormat == ImageFormat.YUY2) {
    192             offsets = new int[] {top * mStrides[0] + left / 2 * 4};
    193             return offsets;
    194         }
    195 
    196         return offsets;
    197     }
    198 
    199     private int[] calculateStrides(int width, int format) {
    200         int[] strides = null;
    201         if (format == ImageFormat.NV21) {
    202             strides = new int[] {width, width};
    203             return strides;
    204         }
    205 
    206         if (format == ImageFormat.YUY2) {
    207             strides = new int[] {width * 2};
    208             return strides;
    209         }
    210 
    211         return strides;
    212     }
    213 
    214    private void adjustRectangle(Rect rect) {
    215        int width = rect.width();
    216        int height = rect.height();
    217        if (mFormat == ImageFormat.NV21) {
    218            // Make sure left, top, width and height are all even.
    219            width &= ~1;
    220            height &= ~1;
    221            rect.left &= ~1;
    222            rect.top &= ~1;
    223            rect.right = rect.left + width;
    224            rect.bottom = rect.top + height;
    225         }
    226 
    227         if (mFormat == ImageFormat.YUY2) {
    228             // Make sure left and width are both even.
    229             width &= ~1;
    230             rect.left &= ~1;
    231             rect.right = rect.left + width;
    232         }
    233     }
    234 
    235     //////////// native methods
    236 
    237     private static native boolean nativeCompressToJpeg(byte[] oriYuv,
    238             int format, int width, int height, int[] offsets, int[] strides,
    239             int quality, OutputStream stream, byte[] tempStorage);
    240 }
    241