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