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