天天看點

android讀取sd卡圖檔并進行縮放操作

在android中對大圖檔進行縮放真的很不盡如人意,不知道是不是我的方法不對。下面我列出3種對圖檔縮放的方法,并給出相應速度。請高人指教。

第一種是bitmapfactory和bitmapfactory.options。

首先,bitmapfactory.options有幾個fields很有用:

injustdecodebounds:if set to true, the decoder will return null (no bitmap), but the out...

也就是說,當injustdecodebounds設成true時,bitmap并不加載到記憶體,這樣效率很高哦。而這時,你可以獲得bitmap的高、寬等資訊。

outheight:the resulting height of the bitmap, set independent of the state of injustdecodebounds.

outwidth:the resulting width of the bitmap, set independent of the state of injustdecodebounds. 

看到了吧,上面3個變量是相關聯的哦。

insamplesize : if set to a value > 1, requests the decoder to subsample the original image, returning a smaller image to save memory.

這就是用來做縮放比的。這裡有個技巧:

insamplesize=(outheight/height+outwidth/width)/2

實踐證明,這樣縮放出來的圖檔還是很好的。

最後用bitmapfactory.decodefile(path, options)生成。

由于隻是對bitmap加載到記憶體一次,是以效率比較高。解析速度快。

第二種是使用bitmap加matrix來縮放。

首先要獲得原bitmap,再從原bitmap的基礎上生成新圖檔。這樣效率很低。

第三種是用2.2新加的類thumbnailutils來做。

讓我們新看看這個類,從api中來看,此類就三個靜态方法:createvideothumbnail、extractthumbnail(bitmap source, int width, int height, int options)、extractthumbnail(bitmap source, int width, int height)。

我這裡使用了第三個方法。再看看它的源碼,下面會附上。是上面我們用到的bitmapfactory.options和matrix等經過人家一陣加工而成。

效率好像比第二種方法高一點點。

下面是我的例子:

<?xml version="1.0" encoding="utf-8"?>  

<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"  

    android:orientation="vertical"  

    android:layout_width="fill_parent"  

    android:layout_height="fill_parent"  

    >  

<imageview  

    android:id="@+id/imageshow"  

    android:layout_width="wrap_content"   

    android:layout_height="wrap_content"   

/>     

    android:id="@+id/image2"  

/>    

<textview    

    android:id="@+id/text"  

    android:layout_width="fill_parent"   

    android:text="@string/hello"  

    />  

</linearlayout>  

package com.linc.resolvepicture;  

import java.io.file;  

import java.io.filenotfoundexception;  

import java.io.fileoutputstream;  

import java.io.ioexception;  

import android.app.activity;  

import android.graphics.bitmap;  

import android.graphics.bitmapfactory;  

import android.graphics.matrix;  

import android.graphics.drawable.bitmapdrawable;  

import android.graphics.drawable.drawable;  

import android.media.thumbnailutils;  

import android.os.bundle;  

import android.util.log;  

import android.widget.imageview;  

import android.widget.textview;  

public class resolvepicture extends activity {  

    private static string tag="resolvepicture";  

    drawable bmimg;    

    imageview imview;   

    imageview imview2;   

    textview text;  

    string thetime;  

    long start, stop;   

    /** called when the activity is first created. */  

    @override  

    public void oncreate(bundle savedinstancestate) {  

        super.oncreate(savedinstancestate);  

        setcontentview(r.layout.main);  

        text=(textview)findviewbyid(r.id.text);  

        imview=(imageview) findviewbyid(r.id.imageshow);  

        imview2=(imageview) findviewbyid(r.id.image2);  

        bitmap bitmap = bitmapfactory.decoderesource(getresources(),     

                r.drawable.pic);  

        start=system.currenttimemillis();  

//        imview.setimagedrawable(resizeimage(bitmap, 300, 100));   

        imview2.setimagedrawable(resizeimage2("/sdcard/2.jpeg", 200, 100));   

        stop=system.currenttimemillis();  

        string thetime= string.format("\n1 iterative: (%d msec)",    

                stop - start);    

        imview.setimagebitmap(thumbnailutils.extractthumbnail(bitmap,200,100));//2.2才加進來的新類,簡單易用  

//        imview.setimagedrawable(resizeimage(bitmap, 30, 30));   

         thetime+= string.format("\n2 iterative: (%d msec)",    

                stop - start);   

        text.settext(thetime);  

    }  

    //使用bitmap加matrix來縮放  

    public static drawable resizeimage(bitmap bitmap, int w, int h)   

    {    

        bitmap bitmaporg = bitmap;    

        int width = bitmaporg.getwidth();    

        int height = bitmaporg.getheight();    

        int newwidth = w;    

        int newheight = h;    

        float scalewidth = ((float) newwidth) / width;    

        float scaleheight = ((float) newheight) / height;    

        matrix matrix = new matrix();    

        matrix.postscale(scalewidth, scaleheight);    

        // if you want to rotate the bitmap     

        // matrix.postrotate(45);     

        bitmap resizedbitmap = bitmap.createbitmap(bitmaporg, 0, 0, width,    

                        height, matrix, true);    

        return new bitmapdrawable(resizedbitmap);    

    //使用bitmapfactory.options的insamplesize參數來縮放  

    public static drawable resizeimage2(string path,  

            int width,int height)   

    {  

        bitmapfactory.options options = new bitmapfactory.options();  

        options.injustdecodebounds = true;//不加載bitmap到記憶體中  

        bitmapfactory.decodefile(path,options);   

        int outwidth = options.outwidth;  

        int outheight = options.outheight;  

        options.indither = false;  

        options.inpreferredconfig = bitmap.config.argb_8888;  

        options.insamplesize = 1;  

        if (outwidth != 0 && outheight != 0 && width != 0 && height != 0)   

        {  

            int samplesize=(outwidth/width+outheight/height)/2;  

            log.d(tag, "samplesize = " + samplesize);  

            options.insamplesize = samplesize;  

        }  

        options.injustdecodebounds = false;  

        return new bitmapdrawable(bitmapfactory.decodefile(path, options));       

    //圖檔儲存  

    private void savethepicture(bitmap bitmap)  

        file file=new file("/sdcard/2.jpeg");  

        try  

            fileoutputstream fos=new fileoutputstream(file);  

            if(bitmap.compress(bitmap.compressformat.jpeg, 100, fos))  

            {  

                fos.flush();  

                fos.close();  

            }  

        catch(filenotfoundexception e1)  

            e1.printstacktrace();  

        catch(ioexception e2)  

            e2.printstacktrace();  

}  

thumbnailutils源碼:

/* 

 * copyright (c) 2009 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 android.media;  

import android.content.contentresolver;  

import android.content.contenturis;  

import android.content.contentvalues;  

import android.database.cursor;  

import android.graphics.canvas;  

import android.graphics.rect;  

import android.media.mediametadataretriever;  

import android.media.mediafile.mediafiletype;  

import android.net.uri;  

import android.os.parcelfiledescriptor;  

import android.provider.basecolumns;  

import android.provider.mediastore.images;  

import android.provider.mediastore.images.thumbnails;  

import java.io.fileinputstream;  

import java.io.filedescriptor;  

import java.io.outputstream;  

/** 

 * thumbnail generation routines for media provider. 

public class thumbnailutils {  

    private static final string tag = "thumbnailutils";  

    /* maximum pixels size for created bitmap. */  

    private static final int max_num_pixels_thumbnail = 512 * 384;  

    private static final int max_num_pixels_micro_thumbnail = 128 * 128;  

    private static final int unconstrained = -1;  

    /* options used internally. */  

    private static final int options_none = 0x0;  

    private static final int options_scale_up = 0x1;  

    /** 

     * constant used to indicate we should recycle the input in 

     * {@link #extractthumbnail(bitmap, int, int, int)} unless the output is the input. 

     */  

    public static final int options_recycle_input = 0x2;  

     * constant used to indicate the dimension of mini thumbnail. 

     * @hide only used by media framework and media provider internally. 

    public static final int target_size_mini_thumbnail = 320;  

     * constant used to indicate the dimension of micro thumbnail. 

    public static final int target_size_micro_thumbnail = 96;  

     * this method first examines if the thumbnail embedded in exif is bigger than our target 

     * size. if not, then it'll create a thumbnail from original image. due to efficiency 

     * consideration, we want to let mediathumbrequest avoid calling this method twice for 

     * both kinds, so it only requests for micro_kind and set saveimage to true. 

     * 

     * this method always returns a "square thumbnail" for micro_kind thumbnail. 

     * @param filepath the path of image file 

     * @param kind could be mini_kind or micro_kind 

     * @return bitmap 

     * @hide this method is only used by media framework and media provider internally. 

    public static bitmap createimagethumbnail(string filepath, int kind) {  

        boolean wantmini = (kind == images.thumbnails.mini_kind);  

        int targetsize = wantmini  

                ? target_size_mini_thumbnail  

                : target_size_micro_thumbnail;  

        int maxpixels = wantmini  

                ? max_num_pixels_thumbnail  

                : max_num_pixels_micro_thumbnail;  

        sizedthumbnailbitmap sizedthumbnailbitmap = new sizedthumbnailbitmap();  

        bitmap bitmap = null;  

        mediafiletype filetype = mediafile.getfiletype(filepath);  

        if (filetype != null && filetype.filetype == mediafile.file_type_jpeg) {  

            createthumbnailfromexif(filepath, targetsize, maxpixels, sizedthumbnailbitmap);  

            bitmap = sizedthumbnailbitmap.mbitmap;  

        if (bitmap == null) {  

            try {  

                filedescriptor fd = new fileinputstream(filepath).getfd();  

                bitmapfactory.options options = new bitmapfactory.options();  

                options.insamplesize = 1;  

                options.injustdecodebounds = true;  

                bitmapfactory.decodefiledescriptor(fd, null, options);  

                if (options.mcancel || options.outwidth == -1  

                        || options.outheight == -1) {  

                    return null;  

                }  

                options.insamplesize = computesamplesize(  

                        options, targetsize, maxpixels);  

                options.injustdecodebounds = false;  

                options.indither = false;  

                options.inpreferredconfig = bitmap.config.argb_8888;  

                bitmap = bitmapfactory.decodefiledescriptor(fd, null, options);  

            } catch (ioexception ex) {  

                log.e(tag, "", ex);  

        if (kind == images.thumbnails.micro_kind) {  

            // now we make it a "square thumbnail" for micro_kind thumbnail  

            bitmap = extractthumbnail(bitmap,  

                    target_size_micro_thumbnail,  

                    target_size_micro_thumbnail, options_recycle_input);  

        return bitmap;  

     * create a video thumbnail for a video. may return null if the video is 

     * corrupt or the format is not supported. 

     * @param filepath the path of video file 

    public static bitmap createvideothumbnail(string filepath, int kind) {  

        mediametadataretriever retriever = new mediametadataretriever();  

        try {  

            retriever.setmode(mediametadataretriever.mode_capture_frame_only);  

            retriever.setdatasource(filepath);  

            bitmap = retriever.captureframe();  

        } catch (illegalargumentexception ex) {  

            // assume this is a corrupt video file  

        } catch (runtimeexception ex) {  

            // assume this is a corrupt video file.  

        } finally {  

                retriever.release();  

            } catch (runtimeexception ex) {  

                // ignore failures while cleaning up.  

        if (kind == images.thumbnails.micro_kind && bitmap != null) {  

                    options_recycle_input);  

     * creates a centered bitmap of the desired size. 

     * @param source original bitmap source 

     * @param width targeted width 

     * @param height targeted height 

    public static bitmap extractthumbnail(  

            bitmap source, int width, int height) {  

        return extractthumbnail(source, width, height, options_none);  

     * @param options options used during thumbnail extraction 

            bitmap source, int width, int height, int options) {  

        if (source == null) {  

            return null;  

        float scale;  

        if (source.getwidth() < source.getheight()) {  

            scale = width / (float) source.getwidth();  

        } else {  

            scale = height / (float) source.getheight();  

        matrix matrix = new matrix();  

        matrix.setscale(scale, scale);  

        bitmap thumbnail = transform(matrix, source, width, height,  

                options_scale_up | options);  

        return thumbnail;  

    /* 

     * compute the sample size as a function of minsidelength 

     * and maxnumofpixels. 

     * minsidelength is used to specify that minimal width or height of a 

     * bitmap. 

     * maxnumofpixels is used to specify the maximal size in pixels that is 

     * tolerable in terms of memory usage. 

     * the function returns a sample size based on the constraints. 

     * both size and minsidelength can be passed in as iimage.unconstrained, 

     * which indicates no care of the corresponding constraint. 

     * the functions prefers returning a sample size that 

     * generates a smaller bitmap, unless minsidelength = iimage.unconstrained. 

     * also, the function rounds up the sample size to a power of 2 or multiple 

     * of 8 because bitmapfactory only honors sample size this way. 

     * for example, bitmapfactory downsamples an image by 2 even though the 

     * request is 3. so we round up the sample size to avoid oom. 

    private static int computesamplesize(bitmapfactory.options options,  

            int minsidelength, int maxnumofpixels) {  

        int initialsize = computeinitialsamplesize(options, minsidelength,  

                maxnumofpixels);  

        int roundedsize;  

        if (initialsize <= 8 ) {  

            roundedsize = 1;  

            while (roundedsize < initialsize) {  

                roundedsize <<= 1;  

            roundedsize = (initialsize + 7) / 8 * 8;  

        return roundedsize;  

    private static int computeinitialsamplesize(bitmapfactory.options options,  

        double w = options.outwidth;  

        double h = options.outheight;  

        int lowerbound = (maxnumofpixels == unconstrained) ? 1 :  

                (int) math.ceil(math.sqrt(w * h / maxnumofpixels));  

        int upperbound = (minsidelength == unconstrained) ? 128 :  

                (int) math.min(math.floor(w / minsidelength),  

                math.floor(h / minsidelength));  

        if (upperbound < lowerbound) {  

            // return the larger one when there is no overlapping zone.  

            return lowerbound;  

        if ((maxnumofpixels == unconstrained) &&  

                (minsidelength == unconstrained)) {  

            return 1;  

        } else if (minsidelength == unconstrained) {  

            return upperbound;  

     * make a bitmap from a given uri, minimal side length, and maximum number of pixels. 

     * the image data will be read from specified pfd if it's not null, otherwise 

     * a new input stream will be created using specified contentresolver. 

     * clients are allowed to pass their own bitmapfactory.options used for bitmap decoding. a 

     * new bitmapfactory.options will be created if options is null. 

    private static bitmap makebitmap(int minsidelength, int maxnumofpixels,  

            uri uri, contentresolver cr, parcelfiledescriptor pfd,  

            bitmapfactory.options options) {  

            bitmap b = null;  

            if (pfd == null) pfd = makeinputstream(uri, cr);  

            if (pfd == null) return null;  

            if (options == null) options = new bitmapfactory.options();  

            filedescriptor fd = pfd.getfiledescriptor();  

            options.insamplesize = 1;  

            options.injustdecodebounds = true;  

            bitmapfactory.decodefiledescriptor(fd, null, options);  

            if (options.mcancel || options.outwidth == -1  

                    || options.outheight == -1) {  

                return null;  

            options.insamplesize = computesamplesize(  

                    options, minsidelength, maxnumofpixels);  

            options.injustdecodebounds = false;  

            options.indither = false;  

            options.inpreferredconfig = bitmap.config.argb_8888;  

            b = bitmapfactory.decodefiledescriptor(fd, null, options);  

        } catch (outofmemoryerror ex) {  

            log.e(tag, "got oom exception ", ex);  

            closesilently(pfd);  

        return b;  

    private static void closesilently(parcelfiledescriptor c) {  

      if (c == null) return;  

      try {  

          c.close();  

      } catch (throwable t) {  

          // do nothing  

      }  

    private static parcelfiledescriptor makeinputstream(  

            uri uri, contentresolver cr) {  

            return cr.openfiledescriptor(uri, "r");  

        } catch (ioexception ex) {  

     * transform source bitmap to targeted width and height. 

    private static bitmap transform(matrix scaler,  

            bitmap source,  

            int targetwidth,  

            int targetheight,  

            int options) {  

        boolean scaleup = (options & options_scale_up) != 0;  

        boolean recycle = (options & options_recycle_input) != 0;  

        int deltax = source.getwidth() - targetwidth;  

        int deltay = source.getheight() - targetheight;  

        if (!scaleup && (deltax < 0 || deltay < 0)) {  

            /* 

            * in this case the bitmap is smaller, at least in one dimension, 

            * than the target.  transform it by placing as much of the image 

            * as possible into the target and leaving the top/bottom or 

            * left/right (or both) black. 

            */  

            bitmap b2 = bitmap.createbitmap(targetwidth, targetheight,  

            bitmap.config.argb_8888);  

            canvas c = new canvas(b2);  

            int deltaxhalf = math.max(0, deltax / 2);  

            int deltayhalf = math.max(0, deltay / 2);  

            rect src = new rect(  

            deltaxhalf,  

            deltayhalf,  

            deltaxhalf + math.min(targetwidth, source.getwidth()),  

            deltayhalf + math.min(targetheight, source.getheight()));  

            int dstx = (targetwidth  - src.width())  / 2;  

            int dsty = (targetheight - src.height()) / 2;  

            rect dst = new rect(  

                    dstx,  

                    dsty,  

                    targetwidth - dstx,  

                    targetheight - dsty);  

            c.drawbitmap(source, src, dst, null);  

            if (recycle) {  

                source.recycle();  

            return b2;  

        float bitmapwidthf = source.getwidth();  

        float bitmapheightf = source.getheight();  

        float bitmapaspect = bitmapwidthf / bitmapheightf;  

        float viewaspect   = (float) targetwidth / targetheight;  

        if (bitmapaspect > viewaspect) {  

            float scale = targetheight / bitmapheightf;  

            if (scale < .9f || scale > 1f) {  

                scaler.setscale(scale, scale);  

            } else {  

                scaler = null;  

            float scale = targetwidth / bitmapwidthf;  

        bitmap b1;  

        if (scaler != null) {  

            // this is used for minithumb and crop, so we want to filter here.  

            b1 = bitmap.createbitmap(source, 0, 0,  

            source.getwidth(), source.getheight(), scaler, true);  

            b1 = source;  

        if (recycle && b1 != source) {  

            source.recycle();  

        int dx1 = math.max(0, b1.getwidth() - targetwidth);  

        int dy1 = math.max(0, b1.getheight() - targetheight);  

        bitmap b2 = bitmap.createbitmap(  

                b1,  

                dx1 / 2,  

                dy1 / 2,  

                targetwidth,  

                targetheight);  

        if (b2 != b1) {  

            if (recycle || b1 != source) {  

                b1.recycle();  

        return b2;  

     * sizedthumbnailbitmap contains the bitmap, which is downsampled either from 

     * the thumbnail in exif or the full image. 

     * mthumbnaildata, mthumbnailwidth and mthumbnailheight are set together only if mthumbnail 

     * is not null. 

     * the width/height of the sized bitmap may be different from mthumbnailwidth/mthumbnailheight. 

    private static class sizedthumbnailbitmap {  

        public byte[] mthumbnaildata;  

        public bitmap mbitmap;  

        public int mthumbnailwidth;  

        public int mthumbnailheight;  

     * creates a bitmap by either downsampling from the thumbnail in exif or the full image. 

     * the functions returns a sizedthumbnailbitmap, 

     * which contains a downsampled bitmap and the thumbnail data in exif if exists. 

    private static void createthumbnailfromexif(string filepath, int targetsize,  

            int maxpixels, sizedthumbnailbitmap sizedthumbbitmap) {  

        if (filepath == null) return;  

        exifinterface exif = null;  

        byte [] thumbdata = null;  

            exif = new exifinterface(filepath);  

            if (exif != null) {  

                thumbdata = exif.getthumbnail();  

            log.w(tag, ex);  

        bitmapfactory.options fulloptions = new bitmapfactory.options();  

        bitmapfactory.options exifoptions = new bitmapfactory.options();  

        int exifthumbwidth = 0;  

        int fullthumbwidth = 0;  

        // compute exifthumbwidth.  

        if (thumbdata != null) {  

            exifoptions.injustdecodebounds = true;  

            bitmapfactory.decodebytearray(thumbdata, 0, thumbdata.length, exifoptions);  

            exifoptions.insamplesize = computesamplesize(exifoptions, targetsize, maxpixels);  

            exifthumbwidth = exifoptions.outwidth / exifoptions.insamplesize;  

        // compute fullthumbwidth.  

        fulloptions.injustdecodebounds = true;  

        bitmapfactory.decodefile(filepath, fulloptions);  

        fulloptions.insamplesize = computesamplesize(fulloptions, targetsize, maxpixels);  

        fullthumbwidth = fulloptions.outwidth / fulloptions.insamplesize;  

        // choose the larger thumbnail as the returning sizedthumbbitmap.  

        if (thumbdata != null && exifthumbwidth >= fullthumbwidth) {  

            int width = exifoptions.outwidth;  

            int height = exifoptions.outheight;  

            exifoptions.injustdecodebounds = false;  

            sizedthumbbitmap.mbitmap = bitmapfactory.decodebytearray(thumbdata, 0,  

                    thumbdata.length, exifoptions);  

            if (sizedthumbbitmap.mbitmap != null) {  

                sizedthumbbitmap.mthumbnaildata = thumbdata;  

                sizedthumbbitmap.mthumbnailwidth = width;  

                sizedthumbbitmap.mthumbnailheight = height;  

            fulloptions.injustdecodebounds = false;  

            sizedthumbbitmap.mbitmap = bitmapfactory.decodefile(filepath, fulloptions);  

繼續閱讀