MediaImageItem.java revision ce03445c320042ca144bd4efd8c0356bb7775f9d
1/*
2 * Copyright (C) 2010 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 android.media.videoeditor;
18
19import java.io.IOException;
20
21import android.graphics.Bitmap;
22import android.graphics.BitmapFactory;
23import android.graphics.Canvas;
24import android.graphics.Paint;
25import android.graphics.Rect;
26import android.util.Log;
27import android.util.Pair;
28
29/**
30 * This class represents an image item on the storyboard. Note that images are
31 * scaled down to the maximum supported resolution by preserving the native
32 * aspect ratio. To learn the scaled image dimensions use
33 * {@link #getScaledWidth()} and {@link #getScaledHeight()} respectively.
34 *
35 * {@hide}
36 */
37public class MediaImageItem extends MediaItem {
38    // Logging
39    private static final String TAG = "MediaImageItem";
40
41    // The resize paint
42    private static final Paint sResizePaint = new Paint(Paint.FILTER_BITMAP_FLAG);
43
44    // Instance variables
45    private final int mWidth;
46    private final int mHeight;
47    private final int mAspectRatio;
48    private long mDurationMs;
49    private int mScaledWidth, mScaledHeight;
50
51    /**
52     * This class cannot be instantiated by using the default constructor
53     */
54    @SuppressWarnings("unused")
55    private MediaImageItem() throws IOException {
56        this(null, null, null, 0, RENDERING_MODE_BLACK_BORDER);
57    }
58
59    /**
60     * Constructor
61     *
62     * @param editor The video editor reference
63     * @param mediaItemId The media item id
64     * @param filename The image file name
65     * @param durationMs The duration of the image on the storyboard
66     * @param renderingMode The rendering mode
67     *
68     * @throws IOException
69     */
70    public MediaImageItem(VideoEditor editor, String mediaItemId, String filename, long durationMs,
71            int renderingMode)
72            throws IOException {
73        super(editor, mediaItemId, filename, renderingMode);
74
75        // Determine the dimensions of the image
76        final BitmapFactory.Options dbo = new BitmapFactory.Options();
77        dbo.inJustDecodeBounds = true;
78        BitmapFactory.decodeFile(filename, dbo);
79
80        mWidth = dbo.outWidth;
81        mHeight = dbo.outHeight;
82        mDurationMs = durationMs;
83
84        // TODO: Determine the aspect ratio from the width and height
85        mAspectRatio = MediaProperties.ASPECT_RATIO_4_3;
86
87        // Images are stored in memory scaled to the maximum resolution to
88        // save memory.
89        final Pair<Integer, Integer>[] resolutions =
90            MediaProperties.getSupportedResolutions(mAspectRatio);
91        // Get the highest resolution
92        final Pair<Integer, Integer> maxResolution = resolutions[resolutions.length - 1];
93        if (mHeight > maxResolution.second) {
94            // We need to scale the image
95            scaleImage(filename, maxResolution.first, maxResolution.second);
96            mScaledWidth = maxResolution.first;
97            mScaledHeight = maxResolution.second;
98        } else {
99            mScaledWidth = mWidth;
100            mScaledHeight = mHeight;
101        }
102    }
103
104    /*
105     * {@inheritDoc}
106     */
107    @Override
108    public int getFileType() {
109        if (mFilename.endsWith(".jpg") || mFilename.endsWith(".jpeg")) {
110            return MediaProperties.FILE_JPEG;
111        } else if (mFilename.endsWith(".png")) {
112            return MediaProperties.FILE_PNG;
113        } else {
114            return MediaProperties.FILE_UNSUPPORTED;
115        }
116    }
117
118    /*
119     * {@inheritDoc}
120     */
121    @Override
122    public int getWidth() {
123        return mWidth;
124    }
125
126    /*
127     * {@inheritDoc}
128     */
129    @Override
130    public int getHeight() {
131        return mHeight;
132    }
133
134    /**
135     * @return The scaled width of the image.
136     */
137    public int getScaledWidth() {
138        return mScaledWidth;
139    }
140
141    /**
142     * @return The scaled height of the image.
143     */
144    public int getScaledHeight() {
145        return mScaledHeight;
146    }
147
148    /*
149     * {@inheritDoc}
150     */
151    @Override
152    public int getAspectRatio() {
153        return mAspectRatio;
154    }
155
156    /**
157     * This method will adjust the duration of bounding transitions, effects
158     * and overlays if the current duration of the transactions become greater
159     * than the maximum allowable duration.
160     *
161     * @param durationMs The duration of the image in the storyboard timeline
162     */
163    public void setDuration(long durationMs) {
164        mDurationMs = durationMs;
165
166        adjustTransitions();
167        adjustOverlays();
168        adjustEffects();
169    }
170
171    /*
172     * {@inheritDoc}
173     */
174    @Override
175    public long getTimelineDuration() {
176        return mDurationMs;
177    }
178
179    /*
180     * {@inheritDoc}
181     */
182    @Override
183    public Bitmap getThumbnail(int width, int height, long timeMs) throws IOException {
184        return scaleImage(mFilename, width, height);
185    }
186
187    /*
188     * {@inheritDoc}
189     */
190    @Override
191    public Bitmap[] getThumbnailList(int width, int height, long startMs, long endMs,
192            int thumbnailCount) throws IOException {
193        final Bitmap thumbnail = scaleImage(mFilename, width, height);
194        final Bitmap[] thumbnailArray = new Bitmap[thumbnailCount];
195        for (int i = 0; i < thumbnailCount; i++) {
196            thumbnailArray[i] = thumbnail;
197        }
198        return thumbnailArray;
199    }
200
201    /**
202     * Resize a bitmap to the specified width and height
203     *
204     * @param filename The filename
205     * @param width The thumbnail width
206     * @param height The thumbnail height
207     *
208     * @return The resized bitmap
209     */
210    private Bitmap scaleImage(String filename, int width, int height)
211            throws IOException {
212        final BitmapFactory.Options dbo = new BitmapFactory.Options();
213        dbo.inJustDecodeBounds = true;
214        BitmapFactory.decodeFile(filename, dbo);
215
216        final int nativeWidth = dbo.outWidth;
217        final int nativeHeight = dbo.outHeight;
218        if (Log.isLoggable(TAG, Log.DEBUG)) {
219            Log.d(TAG, "generateThumbnail: Input: " + nativeWidth + "x" + nativeHeight
220                    + ", resize to: " + width + "x" + height);
221        }
222
223        final Bitmap srcBitmap;
224        float bitmapWidth, bitmapHeight;
225        if (nativeWidth > width || nativeHeight > height) {
226            float dx = ((float)nativeWidth) / ((float)width);
227            float dy = ((float)nativeHeight) / ((float)height);
228            if (dx > dy) {
229                bitmapWidth = width;
230                bitmapHeight = nativeHeight / dx;
231            } else {
232                bitmapWidth = nativeWidth / dy;
233                bitmapHeight = height;
234            }
235            // Create the bitmap from file
236            if (nativeWidth / bitmapWidth > 1) {
237                final BitmapFactory.Options options = new BitmapFactory.Options();
238                options.inSampleSize = nativeWidth / (int)bitmapWidth;
239                srcBitmap = BitmapFactory.decodeFile(filename, options);
240            } else {
241                srcBitmap = BitmapFactory.decodeFile(filename);
242            }
243        } else {
244            bitmapWidth = width;
245            bitmapHeight = height;
246            srcBitmap = BitmapFactory.decodeFile(filename);
247        }
248
249        if (srcBitmap == null) {
250            Log.e(TAG, "generateThumbnail: Cannot decode image bytes");
251            throw new IOException("Cannot decode file: " + mFilename);
252        }
253
254        // Create the canvas bitmap
255        final Bitmap bitmap = Bitmap.createBitmap((int)bitmapWidth, (int)bitmapHeight,
256                Bitmap.Config.ARGB_8888);
257        final Canvas canvas = new Canvas(bitmap);
258        canvas.drawBitmap(srcBitmap, new Rect(0, 0, srcBitmap.getWidth(), srcBitmap.getHeight()),
259                new Rect(0, 0, (int)bitmapWidth, (int)bitmapHeight), sResizePaint);
260        // Release the source bitmap
261        srcBitmap.recycle();
262        return bitmap;
263    }
264}
265