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 ImageFormat}. 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 ImageFormat}. 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 */ YuvImage(byte[] yuv, int format, int width, int height, int[] strides)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 */ compressToJpeg(Rect rectangle, int quality, OutputStream stream)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 */ getYuvData()150 public byte[] getYuvData() { 151 return mData; 152 } 153 154 /** 155 * @return the YUV format as defined in {@link ImageFormat}. 156 */ getYuvFormat()157 public int getYuvFormat() { 158 return mFormat; 159 } 160 161 /** 162 * @return the number of row bytes in each image plane. 163 */ getStrides()164 public int[] getStrides() { 165 return mStrides; 166 } 167 168 /** 169 * @return the width of the image. 170 */ getWidth()171 public int getWidth() { 172 return mWidth; 173 } 174 175 /** 176 * @return the height of the image. 177 */ getHeight()178 public int getHeight() { 179 return mHeight; 180 } 181 calculateOffsets(int left, int top)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 calculateStrides(int width, int format)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 adjustRectangle(Rect rect)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 nativeCompressToJpeg(byte[] oriYuv, int format, int width, int height, int[] offsets, int[] strides, int quality, OutputStream stream, byte[] tempStorage)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