• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 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 package com.android.photos.data;
17 
18 import android.content.Context;
19 import android.content.res.Resources;
20 import android.graphics.Bitmap;
21 import android.graphics.Bitmap.CompressFormat;
22 import android.graphics.BitmapFactory;
23 import android.util.Log;
24 import android.util.Pools.SimplePool;
25 import android.util.Pools.SynchronizedPool;
26 
27 import com.android.gallery3d.R;
28 import com.android.gallery3d.common.BitmapUtils;
29 import com.android.gallery3d.common.Utils;
30 import com.android.gallery3d.data.DecodeUtils;
31 import com.android.gallery3d.data.MediaItem;
32 import com.android.gallery3d.util.ThreadPool.CancelListener;
33 import com.android.gallery3d.util.ThreadPool.JobContext;
34 import com.android.photos.data.MediaRetriever.MediaSize;
35 
36 import java.io.File;
37 import java.io.FileOutputStream;
38 import java.io.IOException;
39 import java.io.InputStream;
40 import java.io.OutputStream;
41 
42 public class MediaCacheUtils {
43     private static final String TAG = MediaCacheUtils.class.getSimpleName();
44     private static int QUALITY = 80;
45     private static final int BUFFER_SIZE = 4096;
46     private static final SimplePool<byte[]> mBufferPool = new SynchronizedPool<byte[]>(5);
47 
48     private static final JobContext sJobStub = new JobContext() {
49 
50         @Override
51         public boolean isCancelled() {
52             return false;
53         }
54 
55         @Override
56         public void setCancelListener(CancelListener listener) {
57         }
58 
59         @Override
60         public boolean setMode(int mode) {
61             return true;
62         }
63     };
64 
65     private static int mTargetThumbnailSize;
66     private static int mTargetPreviewSize;
67 
initialize(Context context)68     public static void initialize(Context context) {
69         Resources resources = context.getResources();
70         mTargetThumbnailSize = resources.getDimensionPixelSize(R.dimen.size_thumbnail);
71         mTargetPreviewSize = resources.getDimensionPixelSize(R.dimen.size_preview);
72     }
73 
getTargetSize(MediaSize size)74     public static int getTargetSize(MediaSize size) {
75         return (size == MediaSize.Thumbnail) ? mTargetThumbnailSize : mTargetPreviewSize;
76     }
77 
downsample(File inBitmap, MediaSize targetSize, File outBitmap)78     public static boolean downsample(File inBitmap, MediaSize targetSize, File outBitmap) {
79         if (MediaSize.Original == targetSize) {
80             return false; // MediaCache should use the local path for this.
81         }
82         int size = getTargetSize(targetSize);
83         BitmapFactory.Options options = new BitmapFactory.Options();
84         options.inPreferredConfig = Bitmap.Config.ARGB_8888;
85         // TODO: remove unnecessary job context from DecodeUtils.
86         Bitmap bitmap = DecodeUtils.decodeThumbnail(sJobStub, inBitmap.getPath(), options, size,
87                 MediaItem.TYPE_THUMBNAIL);
88         boolean success = (bitmap != null);
89         if (success) {
90             success = writeAndRecycle(bitmap, outBitmap);
91         }
92         return success;
93     }
94 
downsample(Bitmap inBitmap, MediaSize size, File outBitmap)95     public static boolean downsample(Bitmap inBitmap, MediaSize size, File outBitmap) {
96         if (MediaSize.Original == size) {
97             return false; // MediaCache should use the local path for this.
98         }
99         int targetSize = getTargetSize(size);
100         boolean success;
101         if (!needsDownsample(inBitmap, size)) {
102             success = writeAndRecycle(inBitmap, outBitmap);
103         } else {
104             float maxDimension = Math.max(inBitmap.getWidth(), inBitmap.getHeight());
105             float scale = targetSize / maxDimension;
106             int targetWidth = Math.round(scale * inBitmap.getWidth());
107             int targetHeight = Math.round(scale * inBitmap.getHeight());
108             Bitmap scaled = Bitmap.createScaledBitmap(inBitmap, targetWidth, targetHeight, false);
109             success = writeAndRecycle(scaled, outBitmap);
110             inBitmap.recycle();
111         }
112         return success;
113     }
114 
extractImageFromVideo(File inVideo, File outBitmap)115     public static boolean extractImageFromVideo(File inVideo, File outBitmap) {
116         Bitmap bitmap = BitmapUtils.createVideoThumbnail(inVideo.getPath());
117         return writeAndRecycle(bitmap, outBitmap);
118     }
119 
needsDownsample(Bitmap bitmap, MediaSize size)120     public static boolean needsDownsample(Bitmap bitmap, MediaSize size) {
121         if (size == MediaSize.Original) {
122             return false;
123         }
124         int targetSize = getTargetSize(size);
125         int maxDimension = Math.max(bitmap.getWidth(), bitmap.getHeight());
126         return maxDimension > (targetSize * 4 / 3);
127     }
128 
writeAndRecycle(Bitmap bitmap, File outBitmap)129     public static boolean writeAndRecycle(Bitmap bitmap, File outBitmap) {
130         boolean success = writeToFile(bitmap, outBitmap);
131         bitmap.recycle();
132         return success;
133     }
134 
writeToFile(Bitmap bitmap, File outBitmap)135     public static boolean writeToFile(Bitmap bitmap, File outBitmap) {
136         boolean success = false;
137         try {
138             FileOutputStream out = new FileOutputStream(outBitmap);
139             success = bitmap.compress(CompressFormat.JPEG, QUALITY, out);
140             out.close();
141         } catch (IOException e) {
142             Log.w(TAG, "Couldn't write bitmap to cache", e);
143             // success is already false
144         }
145         return success;
146     }
147 
copyStream(InputStream in, OutputStream out)148     public static int copyStream(InputStream in, OutputStream out) throws IOException {
149         byte[] buffer = mBufferPool.acquire();
150         if (buffer == null) {
151             buffer = new byte[BUFFER_SIZE];
152         }
153         try {
154             int totalWritten = 0;
155             int bytesRead;
156             while ((bytesRead = in.read(buffer)) >= 0) {
157                 out.write(buffer, 0, bytesRead);
158                 totalWritten += bytesRead;
159             }
160             return totalWritten;
161         } finally {
162             Utils.closeSilently(in);
163             Utils.closeSilently(out);
164             mBufferPool.release(buffer);
165         }
166     }
167 }
168