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