/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.camera.data; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Matrix; import android.graphics.Point; import android.media.MediaMetadataRetriever; import com.android.camera.debug.Log; import java.io.InputStream; import javax.microedition.khronos.opengles.GL11; /** * An utility class for data in content provider. */ public class FilmstripItemUtils { private static final Log.Tag TAG = new Log.Tag("LocalDataUtil"); /** * @param mimeType The MIME type to check. * @return Whether the MIME is a video type. */ public static boolean isMimeTypeVideo(String mimeType) { return mimeType != null && mimeType.startsWith("video/"); } /** * Checks whether the MIME type represents an image media item. * * @param mimeType The MIME type to check. * @return Whether the MIME is a image type. */ public static boolean isMimeTypeImage(String mimeType) { return mimeType != null && mimeType.startsWith("image/"); } /** * Decodes the dimension of a bitmap. * * @param is An input stream with the data of the bitmap. * @return The decoded width/height is stored in Point.x/Point.y * respectively. */ public static Point decodeBitmapDimension(InputStream is) { Point size = null; BitmapFactory.Options justBoundsOpts = new BitmapFactory.Options(); justBoundsOpts.inJustDecodeBounds = true; BitmapFactory.decodeStream(is, null, justBoundsOpts); if (justBoundsOpts.outWidth > 0 && justBoundsOpts.outHeight > 0) { size = new Point(justBoundsOpts.outWidth, justBoundsOpts.outHeight); } else { Log.e(TAG, "Bitmap dimension decoding failed"); } return size; } /** * Load the thumbnail of an image from an {@link java.io.InputStream}. * * @param stream The input stream of the image. * @param imageWidth Image width. * @param imageHeight Image height. * @param widthBound The bound of the width of the decoded image. * @param heightBound The bound of the height of the decoded image. * @param orientation The orientation of the image. The image will be rotated * clockwise in degrees. * @param maximumPixels The bound for the number of pixels of the decoded image. * @return {@code null} if the decoding failed. */ public static Bitmap loadImageThumbnailFromStream(InputStream stream, int imageWidth, int imageHeight, int widthBound, int heightBound, int orientation, int maximumPixels) { /** 32K buffer. */ byte[] decodeBuffer = new byte[32 * 1024]; if (orientation % 180 != 0) { int dummy = imageHeight; imageHeight = imageWidth; imageWidth = dummy; } // Generate Bitmap of maximum size that fits into widthBound x heightBound. // Algorithm: start with full size and step down in powers of 2. int targetWidth = imageWidth; int targetHeight = imageHeight; int sampleSize = 1; while (targetHeight > heightBound || targetWidth > widthBound || targetHeight > GL11.GL_MAX_TEXTURE_SIZE || targetWidth > GL11.GL_MAX_TEXTURE_SIZE || targetHeight * targetWidth > maximumPixels) { sampleSize <<= 1; targetWidth = imageWidth / sampleSize; targetHeight = imageWidth / sampleSize; } // For large (> MAXIMUM_TEXTURE_SIZE) high aspect ratio (panorama) // Bitmap requests: // Step 1: ask for double size. // Step 2: scale maximum edge down to MAXIMUM_TEXTURE_SIZE. // // Here's the step 1: double size. if ((heightBound > GL11.GL_MAX_TEXTURE_SIZE || widthBound > GL11.GL_MAX_TEXTURE_SIZE) && targetWidth * targetHeight < maximumPixels / 4 && sampleSize > 1) { sampleSize >>= 2; } BitmapFactory.Options opts = new BitmapFactory.Options(); opts.inSampleSize = sampleSize; opts.inTempStorage = decodeBuffer; Bitmap b = BitmapFactory.decodeStream(stream, null, opts); if (b == null) { return null; } // Step 2: scale maximum edge down to maximum texture size. // If Bitmap maximum edge > MAXIMUM_TEXTURE_SIZE, which can happen for panoramas, // scale to fit in MAXIMUM_TEXTURE_SIZE. if (b.getWidth() > GL11.GL_MAX_TEXTURE_SIZE || b.getHeight() > GL11.GL_MAX_TEXTURE_SIZE) { int maxEdge = Math.max(b.getWidth(), b.getHeight()); b = Bitmap.createScaledBitmap(b, b.getWidth() * GL11.GL_MAX_TEXTURE_SIZE / maxEdge, b.getHeight() * GL11.GL_MAX_TEXTURE_SIZE / maxEdge, false); } // Not called often because most modes save image data non-rotated. if (orientation != 0 && b != null) { Matrix m = new Matrix(); m.setRotate(orientation); b = Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(), m, false); } return b; } /** * Loads the thumbnail of a video. * * @param path The path to the video file. * @return {@code null} if the loading failed. */ public static Bitmap loadVideoThumbnail(String path) { Bitmap bitmap = null; MediaMetadataRetriever retriever = new MediaMetadataRetriever(); try { retriever.setDataSource(path); byte[] data = retriever.getEmbeddedPicture(); if (data != null) { bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); } if (bitmap == null) { bitmap = retriever.getFrameAtTime(); } } catch (IllegalArgumentException e) { Log.e(TAG, "MediaMetadataRetriever.setDataSource() fail:" + e.getMessage()); } retriever.release(); return bitmap; } }