Home | History | Annotate | Download | only in data
      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 
     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 
     74     public static int getTargetSize(MediaSize size) {
     75         return (size == MediaSize.Thumbnail) ? mTargetThumbnailSize : mTargetPreviewSize;
     76     }
     77 
     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 
     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 
    115     public static boolean extractImageFromVideo(File inVideo, File outBitmap) {
    116         Bitmap bitmap = BitmapUtils.createVideoThumbnail(inVideo.getPath());
    117         return writeAndRecycle(bitmap, outBitmap);
    118     }
    119 
    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 
    129     public static boolean writeAndRecycle(Bitmap bitmap, File outBitmap) {
    130         boolean success = writeToFile(bitmap, outBitmap);
    131         bitmap.recycle();
    132         return success;
    133     }
    134 
    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 
    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