1/*
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;
24
25import com.android.camera.debug.Log;
26
27import java.io.InputStream;
28
29import javax.microedition.khronos.opengles.GL11;
30
31/**
32 * An utility class for data in content provider.
33 */
34public class FilmstripItemUtils {
35
36    private static final Log.Tag TAG = new Log.Tag("LocalDataUtil");
37
38    /**
39     * @param mimeType The MIME type to check.
40     * @return Whether the MIME is a video type.
41     */
42    public static boolean isMimeTypeVideo(String mimeType) {
43        return mimeType != null && mimeType.startsWith("video/");
44    }
45
46    /**
47     * Checks whether the MIME type represents an image media item.
48     *
49     * @param mimeType The MIME type to check.
50     * @return Whether the MIME is a image type.
51     */
52    public static boolean isMimeTypeImage(String mimeType) {
53        return mimeType != null && mimeType.startsWith("image/");
54    }
55
56
57    /**
58     * Decodes the dimension of a bitmap.
59     *
60     * @param is An input stream with the data of the bitmap.
61     * @return The decoded width/height is stored in Point.x/Point.y
62     *         respectively.
63     */
64    public static Point decodeBitmapDimension(InputStream is) {
65        Point size = null;
66        BitmapFactory.Options justBoundsOpts = new BitmapFactory.Options();
67        justBoundsOpts.inJustDecodeBounds = true;
68        BitmapFactory.decodeStream(is, null, justBoundsOpts);
69        if (justBoundsOpts.outWidth > 0 && justBoundsOpts.outHeight > 0) {
70            size = new Point(justBoundsOpts.outWidth, justBoundsOpts.outHeight);
71        } else {
72            Log.e(TAG, "Bitmap dimension decoding failed");
73        }
74        return size;
75    }
76
77    /**
78     * Load the thumbnail of an image from an {@link java.io.InputStream}.
79     *
80     * @param stream The input stream of the image.
81     * @param imageWidth Image width.
82     * @param imageHeight Image height.
83     * @param widthBound The bound of the width of the decoded image.
84     * @param heightBound The bound of the height of the decoded image.
85     * @param orientation The orientation of the image. The image will be rotated
86     *                    clockwise in degrees.
87     * @param maximumPixels The bound for the number of pixels of the decoded image.
88     * @return {@code null} if the decoding failed.
89     */
90    public static Bitmap loadImageThumbnailFromStream(InputStream stream, int imageWidth,
91            int imageHeight, int widthBound, int heightBound, int orientation,
92            int maximumPixels) {
93
94        /** 32K buffer. */
95        byte[] decodeBuffer = new byte[32 * 1024];
96
97        if (orientation % 180 != 0) {
98            int dummy = imageHeight;
99            imageHeight = imageWidth;
100            imageWidth = dummy;
101        }
102
103        // Generate Bitmap of maximum size that fits into widthBound x heightBound.
104        // Algorithm: start with full size and step down in powers of 2.
105        int targetWidth = imageWidth;
106        int targetHeight = imageHeight;
107        int sampleSize = 1;
108        while (targetHeight > heightBound || targetWidth > widthBound ||
109                targetHeight > GL11.GL_MAX_TEXTURE_SIZE || targetWidth > GL11.GL_MAX_TEXTURE_SIZE ||
110                targetHeight * targetWidth > maximumPixels) {
111            sampleSize <<= 1;
112            targetWidth = imageWidth / sampleSize;
113            targetHeight = imageWidth / sampleSize;
114        }
115
116        // For large (> MAXIMUM_TEXTURE_SIZE) high aspect ratio (panorama)
117        // Bitmap requests:
118        //   Step 1: ask for double size.
119        //   Step 2: scale maximum edge down to MAXIMUM_TEXTURE_SIZE.
120        //
121        // Here's the step 1: double size.
122        if ((heightBound > GL11.GL_MAX_TEXTURE_SIZE || widthBound > GL11.GL_MAX_TEXTURE_SIZE) &&
123                targetWidth * targetHeight < maximumPixels / 4 && sampleSize > 1) {
124            sampleSize >>= 2;
125        }
126
127        BitmapFactory.Options opts = new BitmapFactory.Options();
128        opts.inSampleSize = sampleSize;
129        opts.inTempStorage = decodeBuffer;
130        Bitmap b = BitmapFactory.decodeStream(stream, null, opts);
131
132        if (b == null) {
133            return null;
134        }
135
136        // Step 2: scale maximum edge down to maximum texture size.
137        // If Bitmap maximum edge > MAXIMUM_TEXTURE_SIZE, which can happen for panoramas,
138        // scale to fit in MAXIMUM_TEXTURE_SIZE.
139        if (b.getWidth() > GL11.GL_MAX_TEXTURE_SIZE || b.getHeight() >
140                GL11.GL_MAX_TEXTURE_SIZE) {
141            int maxEdge = Math.max(b.getWidth(), b.getHeight());
142            b = Bitmap.createScaledBitmap(b, b.getWidth() * GL11.GL_MAX_TEXTURE_SIZE / maxEdge,
143                    b.getHeight() * GL11.GL_MAX_TEXTURE_SIZE / maxEdge, false);
144        }
145
146        // Not called often because most modes save image data non-rotated.
147        if (orientation != 0 && b != null) {
148            Matrix m = new Matrix();
149            m.setRotate(orientation);
150            b = Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(), m, false);
151        }
152
153        return b;
154    }
155
156    /**
157     * Loads the thumbnail of a video.
158     *
159     * @param path The path to the video file.
160     * @return {@code null} if the loading failed.
161     */
162    public static Bitmap loadVideoThumbnail(String path) {
163        Bitmap bitmap = null;
164        MediaMetadataRetriever retriever = new MediaMetadataRetriever();
165        try {
166            retriever.setDataSource(path);
167            byte[] data = retriever.getEmbeddedPicture();
168            if (data != null) {
169                bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
170            }
171            if (bitmap == null) {
172                bitmap = retriever.getFrameAtTime();
173            }
174        } catch (IllegalArgumentException e) {
175            Log.e(TAG, "MediaMetadataRetriever.setDataSource() fail:" + e.getMessage());
176        }
177        retriever.release();
178        return bitmap;
179    }
180}
181