blob: f029f456ab89dba17a98a442479fa7fb5e7389f8 [file] [log] [blame]
Angus Kong50521172014-01-17 17:23:59 -08001/*
2 * Copyright (C) 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
17package com.android.camera.data;
18
19import android.graphics.Bitmap;
20import android.graphics.BitmapFactory;
21import android.graphics.Matrix;
22import android.graphics.Point;
23import android.media.MediaMetadataRetriever;
Angus Kong50521172014-01-17 17:23:59 -080024
Angus Kong2bca2102014-03-11 16:27:30 -070025import com.android.camera.debug.Log;
Angus Kong50521172014-01-17 17:23:59 -080026
Sam Judd2666dc82014-03-21 16:02:12 -070027import java.io.FileInputStream;
28import java.io.FileNotFoundException;
29import java.io.IOException;
Angus Kong50521172014-01-17 17:23:59 -080030import java.io.InputStream;
31
32import javax.microedition.khronos.opengles.GL11;
33
34/**
35 * An utility class for data in content provider.
36 */
37public class LocalDataUtil {
38
Angus Kong2bca2102014-03-11 16:27:30 -070039 private static final Log.Tag TAG = new Log.Tag("LocalDataUtil");
Angus Kong50521172014-01-17 17:23:59 -080040
41 /**
42 * @param mimeType The MIME type to check.
43 * @return Whether the MIME is a video type.
44 */
45 public static boolean isMimeTypeVideo(String mimeType) {
46 return mimeType.startsWith("video/");
47 }
48
49 /**
50 * @param mimeType The MIME type to check.
51 * @return Whether the MIME is a image type.
52 */
53 public static boolean isMimeTypeImage(String mimeType) {
54 return mimeType.startsWith("image/");
55 }
56
57 /**
Angus Kong50521172014-01-17 17:23:59 -080058 * Decodes the dimension of a bitmap.
59 *
60 * @param path The path to the bitmap.
61 * @return The decoded width/height is stored in Point.x/Point.y
62 * respectively.
63 */
64 public static Point decodeBitmapDimension(String path) {
Sam Judd2666dc82014-03-21 16:02:12 -070065 Point size = null;
66 InputStream is = null;
67 try {
68 is = new FileInputStream(path);
69 size = decodeBitmapDimension(is);
70 } catch (FileNotFoundException e) {
71 e.printStackTrace();
72 } finally {
73 if (is != null) {
74 try {
75 is.close();
76 } catch (IOException e) { }
77 }
78 }
79 return size;
80 }
81
82 /**
83 * Decodes the dimension of a bitmap.
84 *
85 * @param is An input stream with the data of the bitmap.
86 * @return The decoded width/height is stored in Point.x/Point.y
87 * respectively.
88 */
89 public static Point decodeBitmapDimension(InputStream is) {
90 Point size = null;
Angus Kong50521172014-01-17 17:23:59 -080091 BitmapFactory.Options justBoundsOpts = new BitmapFactory.Options();
92 justBoundsOpts.inJustDecodeBounds = true;
Sam Judd2666dc82014-03-21 16:02:12 -070093 BitmapFactory.decodeStream(is, null, justBoundsOpts);
Angus Kong50521172014-01-17 17:23:59 -080094 if (justBoundsOpts.outWidth > 0 && justBoundsOpts.outHeight > 0) {
Sam Judd2666dc82014-03-21 16:02:12 -070095 size = new Point(justBoundsOpts.outWidth, justBoundsOpts.outHeight);
Sam Juddde3e9ab2014-03-17 13:07:22 -070096 } else {
Sam Judd2666dc82014-03-21 16:02:12 -070097 Log.e(TAG, "Bitmap dimension decoding failed");
Angus Kong50521172014-01-17 17:23:59 -080098 }
99 return size;
100 }
101
102 /**
103 * Load the thumbnail of an image from an {@link java.io.InputStream}.
104 *
105 * @param stream The input stream of the image.
106 * @param imageWidth Image width.
107 * @param imageHeight Image height.
108 * @param widthBound The bound of the width of the decoded image.
109 * @param heightBound The bound of the height of the decoded image.
110 * @param orientation The orientation of the image. The image will be rotated
111 * clockwise in degrees.
112 * @param maximumPixels The bound for the number of pixels of the decoded image.
113 * @return {@code null} if the decoding failed.
114 */
115 public static Bitmap loadImageThumbnailFromStream(InputStream stream, int imageWidth,
116 int imageHeight, int widthBound, int heightBound, int orientation,
117 int maximumPixels) {
118
119 /** 32K buffer. */
120 byte[] decodeBuffer = new byte[32 * 1024];
121
Angus Kongc195e7a2014-02-20 16:56:37 -0800122 if (orientation % 180 != 0) {
123 int dummy = imageHeight;
124 imageHeight = imageWidth;
125 imageWidth = dummy;
126 }
127
Angus Kong50521172014-01-17 17:23:59 -0800128 // Generate Bitmap of maximum size that fits into widthBound x heightBound.
129 // Algorithm: start with full size and step down in powers of 2.
130 int targetWidth = imageWidth;
131 int targetHeight = imageHeight;
132 int sampleSize = 1;
133 while (targetHeight > heightBound || targetWidth > widthBound ||
134 targetHeight > GL11.GL_MAX_TEXTURE_SIZE || targetWidth > GL11.GL_MAX_TEXTURE_SIZE ||
135 targetHeight * targetWidth > maximumPixels) {
136 sampleSize <<= 1;
137 targetWidth = imageWidth / sampleSize;
138 targetHeight = imageWidth / sampleSize;
139 }
140
141 // For large (> MAXIMUM_TEXTURE_SIZE) high aspect ratio (panorama)
142 // Bitmap requests:
143 // Step 1: ask for double size.
144 // Step 2: scale maximum edge down to MAXIMUM_TEXTURE_SIZE.
145 //
146 // Here's the step 1: double size.
147 if ((heightBound > GL11.GL_MAX_TEXTURE_SIZE || widthBound > GL11.GL_MAX_TEXTURE_SIZE) &&
148 targetWidth * targetHeight < maximumPixels / 4 && sampleSize > 1) {
149 sampleSize >>= 2;
150 }
151
152 BitmapFactory.Options opts = new BitmapFactory.Options();
153 opts.inSampleSize = sampleSize;
154 opts.inTempStorage = decodeBuffer;
155 Bitmap b = BitmapFactory.decodeStream(stream, null, opts);
156
157 if (b == null) {
158 return null;
159 }
160
161 // Step 2: scale maximum edge down to maximum texture size.
162 // If Bitmap maximum edge > MAXIMUM_TEXTURE_SIZE, which can happen for panoramas,
163 // scale to fit in MAXIMUM_TEXTURE_SIZE.
164 if (b.getWidth() > GL11.GL_MAX_TEXTURE_SIZE || b.getHeight() >
165 GL11.GL_MAX_TEXTURE_SIZE) {
166 int maxEdge = Math.max(b.getWidth(), b.getHeight());
167 b = Bitmap.createScaledBitmap(b, b.getWidth() * GL11.GL_MAX_TEXTURE_SIZE / maxEdge,
168 b.getHeight() * GL11.GL_MAX_TEXTURE_SIZE / maxEdge, false);
169 }
170
171 // Not called often because most modes save image data non-rotated.
172 if (orientation != 0 && b != null) {
173 Matrix m = new Matrix();
174 m.setRotate(orientation);
175 b = Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(), m, false);
176 }
177
178 return b;
179 }
180
181 /**
182 * Loads the thumbnail of a video.
183 *
184 * @param path The path to the video file.
185 * @return {@code null} if the loading failed.
186 */
187 public static Bitmap loadVideoThumbnail(String path) {
188 Bitmap bitmap = null;
189 MediaMetadataRetriever retriever = new MediaMetadataRetriever();
190 try {
191 retriever.setDataSource(path);
192 byte[] data = retriever.getEmbeddedPicture();
193 if (data != null) {
194 bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
195 }
196 if (bitmap == null) {
197 bitmap = retriever.getFrameAtTime();
198 }
199 } catch (IllegalArgumentException e) {
200 Log.e(TAG, "MediaMetadataRetriever.setDataSource() fail:" + e.getMessage());
201 }
202 retriever.release();
203 return bitmap;
204 }
205}