1 /*
2  * Copyright 2014 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.cts;
18 
19 import android.graphics.Bitmap;
20 import android.graphics.Color;
21 import android.graphics.Rect;
22 import android.media.cts.CodecImage;
23 import android.media.Image;
24 import android.media.MediaCodec.BufferInfo;
25 import android.os.Environment;
26 import android.util.Log;
27 
28 import java.io.File;
29 import java.io.FileDescriptor;
30 import java.io.FileOutputStream;
31 import java.io.InputStream;
32 import java.io.OutputStream;
33 import java.nio.ByteBuffer;
34 
35 public class CodecUtils  {
36     private static final String TAG = "CodecUtils";
37 
38     /** Load jni on initialization */
39     static {
Log.i(TAG, "before loadlibrary")40         Log.i(TAG, "before loadlibrary");
41         System.loadLibrary("ctscodecutils_jni");
Log.i(TAG, "after loadlibrary")42         Log.i(TAG, "after loadlibrary");
43     }
44 
45     private static class ImageWrapper extends CodecImage {
46         private final Image mImage;
47         private final Plane[] mPlanes;
48 
ImageWrapper(Image image)49         private ImageWrapper(Image image) {
50             mImage = image;
51             Image.Plane[] planes = mImage.getPlanes();
52 
53             mPlanes = new Plane[planes.length];
54             for (int i = 0; i < planes.length; i++) {
55                 mPlanes[i] = new PlaneWrapper(planes[i]);
56             }
57 
58             setCropRect(image.getCropRect());
59         }
60 
createFromImage(Image image)61         public static ImageWrapper createFromImage(Image image) {
62             return new ImageWrapper(image);
63         }
64 
65         @Override
getFormat()66         public int getFormat() {
67             return mImage.getFormat();
68         }
69 
70         @Override
getWidth()71         public int getWidth() {
72             return mImage.getWidth();
73         }
74 
75         @Override
getHeight()76         public int getHeight() {
77             return mImage.getHeight();
78         }
79 
80         @Override
getTimestamp()81         public long getTimestamp() {
82             return mImage.getTimestamp();
83         }
84 
85         @Override
getPlanes()86         public Plane[] getPlanes() {
87             return mPlanes;
88         }
89 
90         @Override
close()91         public void close() {
92             mImage.close();
93         }
94 
95         private static class PlaneWrapper extends CodecImage.Plane {
96             private final Image.Plane mPlane;
97 
PlaneWrapper(Image.Plane plane)98             PlaneWrapper(Image.Plane plane) {
99                 mPlane = plane;
100             }
101 
102             @Override
getRowStride()103             public int getRowStride() {
104                 return mPlane.getRowStride();
105             }
106 
107            @Override
getPixelStride()108             public int getPixelStride() {
109                return mPlane.getPixelStride();
110             }
111 
112             @Override
getBuffer()113             public ByteBuffer getBuffer() {
114                 return mPlane.getBuffer();
115             }
116         }
117     }
118 
119     /* two native image checksum functions */
getImageChecksumAdler32(CodecImage image)120     public native static int getImageChecksumAdler32(CodecImage image);
getImageChecksumMD5(CodecImage image)121     public native static String getImageChecksumMD5(CodecImage image);
122 
copyFlexYUVImage(CodecImage target, CodecImage source)123     public native static void copyFlexYUVImage(CodecImage target, CodecImage source);
124 
copyFlexYUVImage(Image target, CodecImage source)125     public static void copyFlexYUVImage(Image target, CodecImage source) {
126         copyFlexYUVImage(ImageWrapper.createFromImage(target), source);
127     }
copyFlexYUVImage(Image target, Image source)128     public static void copyFlexYUVImage(Image target, Image source) {
129         copyFlexYUVImage(
130                 ImageWrapper.createFromImage(target),
131                 ImageWrapper.createFromImage(source));
132     }
133 
fillImageRectWithYUV( CodecImage image, Rect area, int y, int u, int v)134     public native static void fillImageRectWithYUV(
135             CodecImage image, Rect area, int y, int u, int v);
136 
fillImageRectWithYUV(Image image, Rect area, int y, int u, int v)137     public static void fillImageRectWithYUV(Image image, Rect area, int y, int u, int v) {
138         fillImageRectWithYUV(ImageWrapper.createFromImage(image), area, y, u, v);
139     }
140 
getRawStats(CodecImage image, Rect area)141     public native static long[] getRawStats(CodecImage image, Rect area);
142 
getRawStats(Image image, Rect area)143     public static long[] getRawStats(Image image, Rect area) {
144         return getRawStats(ImageWrapper.createFromImage(image), area);
145     }
146 
getYUVStats(CodecImage image, Rect area)147     public native static float[] getYUVStats(CodecImage image, Rect area);
148 
getYUVStats(Image image, Rect area)149     public static float[] getYUVStats(Image image, Rect area) {
150         return getYUVStats(ImageWrapper.createFromImage(image), area);
151     }
152 
Raw2YUVStats(long[] rawStats)153     public native static float[] Raw2YUVStats(long[] rawStats);
154 
155     /**
156      * This method reads the binarybar code on the top row of a bitmap. Each 16x16
157      * block is one digit, with black=0 and white=1. LSB is on the left.
158      */
readBinaryCounterFromBitmap(Bitmap bitmap)159     public static int readBinaryCounterFromBitmap(Bitmap bitmap) {
160         int numDigits = bitmap.getWidth() / 16;
161         int counter = 0;
162         for (int i = 0; i < numDigits; i++) {
163             int rgb = bitmap.getPixel(i * 16 + 8, 8);
164             if (Color.red(rgb) > 128) {
165                 counter |= (1 << i);
166             }
167         }
168         return counter;
169     }
170 
171     /**
172      * This method verifies the rotation degrees of a bitmap by reading the colors on its corners.
173      * The bitmap without rotation (rotation degree == 0) looks like
174      * red    |    green
175      * -----------------
176      * blue   |    white
177      * with resolution equals to 320x240.
178      */
VerifyFrameRotationFromBitmap(Bitmap bitmap, int targetRotation)179     public static boolean VerifyFrameRotationFromBitmap(Bitmap bitmap, int targetRotation) {
180         if (targetRotation == 0 || targetRotation == 180) {
181             if (bitmap.getWidth() != 320 || bitmap.getHeight() != 240) {
182                 return false;
183             }
184             Color left_top = Color.valueOf(bitmap.getPixel(10, 10));
185             Color right_top = Color.valueOf(bitmap.getPixel(310, 10));
186             Color left_bottom = Color.valueOf(bitmap.getPixel(10, 230));
187             Color right_bottom = Color.valueOf(bitmap.getPixel(310, 230));
188             if (targetRotation == 0) {
189                 if (!isRed(left_top) || !isGreen(right_top)
190                         || !isBlue(left_bottom) || !isWhite(right_bottom)) {
191                     return false;
192                 }
193             } else {
194                 if (!isWhite(left_top) || !isBlue(right_top)
195                         || !isGreen(left_bottom) || !isRed(right_bottom)) {
196                     return false;
197                 }
198             }
199         } else if (targetRotation == 90 || targetRotation == 270) {
200             if (bitmap.getWidth() != 240 || bitmap.getHeight() != 320) {
201                 return false;
202             }
203             Color left_top = Color.valueOf(bitmap.getPixel(10, 10));
204             Color right_top = Color.valueOf(bitmap.getPixel(230, 10));
205             Color left_bottom = Color.valueOf(bitmap.getPixel(10, 310));
206             Color right_bottom = Color.valueOf(bitmap.getPixel(230, 310));
207             if (targetRotation == 90) {
208                 if (!isBlue(left_top) || !isRed(right_top)
209                         || !isWhite(left_bottom) || !isGreen(right_bottom)) {
210                     return false;
211                 }
212             } else {
213                 if (!isGreen(left_top) || !isWhite(right_top)
214                         || !isRed(left_bottom) || !isBlue(right_bottom)) {
215                     return false;
216                 }
217             }
218         }
219         return true;
220     }
221 
isRed(Color color)222     private static boolean isRed(Color color) {
223         return color.red() > 0.95 && color.green() < 0.05 && color.blue() < 0.05;
224     }
225 
isGreen(Color color)226     private static boolean isGreen(Color color) {
227         return color.red() < 0.05 && color.green() > 0.95 && color.blue() < 0.05;
228     }
229 
isBlue(Color color)230     private static boolean isBlue(Color color) {
231         return color.red() < 0.05 && color.green() < 0.05 && color.blue() > 0.95;
232     }
233 
isWhite(Color color)234     private static boolean isWhite(Color color) {
235         return color.red() > 0.95 && color.green() > 0.95 && color.blue() > 0.95;
236     }
237 
saveBitmapToFile(Bitmap bitmap, String filename)238     public static void saveBitmapToFile(Bitmap bitmap, String filename) {
239         try {
240             File outputFile = new File(Environment.getExternalStorageDirectory(), filename);
241 
242             Log.d(TAG, "Saving bitmap to: " + outputFile);
243             FileOutputStream outputStream = new FileOutputStream(outputFile);
244             bitmap.compress(Bitmap.CompressFormat.JPEG, 90, outputStream);
245             outputStream.flush();
246             outputStream.close();
247         } catch(Exception e) {
248             Log.e(TAG, "Failed to save to file: " + e);
249         }
250     }
251 }
252 
253