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