亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Android性能優(yōu)化之圖片大小,尺寸壓縮綜合解決方案

 更新時(shí)間:2022年04月18日 11:15:24   作者:拖鞋王子豬  
隨著Android手機(jī)的越來越先進(jìn),給我們開發(fā)者而言傳遞的圖片也是越來越大,這個(gè)時(shí)候我們可以對(duì)一些沒有必要原圖展示的圖片進(jìn)行壓縮,這篇文章主要給大家介紹了關(guān)于Android性能優(yōu)化之圖片大小,尺寸壓縮的綜合解決方案,需要的朋友可以參考下

前言

在Android中我們經(jīng)常會(huì)遇到圖片壓縮的場(chǎng)景,比如給服務(wù)端上傳圖片,包括個(gè)人信息的用戶頭像,有時(shí)候人臉識(shí)別也需要捕獲圖片等等。這種情況下,我們都需要對(duì)圖片做一定的處理,比如大小,尺寸等的壓縮。

常見的圖片壓縮方法

  • 質(zhì)量壓縮
  • 尺寸壓縮
  • libjpeg

質(zhì)量壓縮

首先我們要介紹一個(gè)api--Bitmap.compress()

@WorkerThread
public boolean compress(CompressFormat format, int quality, OutputStream stream) {
    checkRecycled("Can't compress a recycled bitmap");
    // do explicit check before calling the native method
    if (stream == null) {
        throw new NullPointerException();
    }
    if (quality < 0 || quality > 100) {
        throw new IllegalArgumentException("quality must be 0..100");
    }
    StrictMode.noteSlowCall("Compression of a bitmap is slow");
    Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "Bitmap.compress");
    boolean result = nativeCompress(mNativePtr, format.nativeInt,
            quality, stream, new byte[WORKING_COMPRESS_STORAGE]);
    Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
    return result;
}

compress()是系統(tǒng)的API,也是質(zhì)量和尺寸壓縮常用的方法。

public boolean compress(Bitmap.CompressFormat format, int quality, OutputStream stream);這個(gè)方法有三個(gè)參數(shù):

  • Bitmap.CompressFormat format圖像的壓縮格式;
  • int quality圖像壓縮率,O-100。0壓縮100%,100意味著不壓縮;
  • OutputStream stream 寫入壓縮數(shù)據(jù)的輸出流;

返回值:如果成功地把壓縮數(shù)據(jù)寫入輸出流,則返回true。

偽代碼

val baos= ByteArrayoutputstream ()
    try {
        var quality = 50
        do {
            quality -= 10
            baos.reset()
            bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos)
        } while (baos.toByteArray().size / 1024 > 100)
        fos.write(baos.toByteArray(o))
    }catch (ex : Throwable) {
        ex.printStackTrace ()} finally {
        fos.apply i this: FileOutputStream
        flush ()
        close ()
    }

尺寸壓縮

先來看看一個(gè)屬性O(shè)ptions

  • 屬性 inJustDecodeBounds,如果該值為true,那么將不返回實(shí)際的 bitmap,也不給其分配內(nèi)存空間這樣就避免內(nèi)存溢出了。
  • 允許我們查詢圖片的信息,這其中就包括圖片大小信息,options.outHeight(圖片原始高度)和option.outWidth(圖片原始寬度)。

兩次decode,傳入不同的options配置:

部分偽代碼

    val reqWidth = 500
    val reqHeight = 300
    val bitmap = decodeSampledBitmapFromFile(imageFile, reqWidth, reqHeight)
    val fos = Fileoutputstream(
            File(applicationContext.filesDir,
                    child: "$ {system.currentTimeMillis() }_scale.jpg")
    )
    try {
        val quality = 50
        bitmap.compress(Bitmap.CompressFormat.JPEG, quality, fos)
        catch(ex: Throwable) {
            ex.printstackTrace() finally {
                fos.apply {
                    flush()
                    close()

                }
            }
        }
    }
}

private fun decodeSampledBitmapFromFile(imageFile: File,reqWidth: Int,reqHeight: Int): Bitmap
{
    return BitmapFactory.Options().run {
        inJustDecodeBounds = true
        //先獲取原始圖片的寬高,不會(huì)將Bitmap加載到內(nèi)存中,返回null
        BitmapFactory.decodeFile(imageFile.absolutePath, opts: this)
        inSamplesize = calculateInSampleSize(options: this, reqWidth,reqHeight)
        inJustDecodeBounds - false
        BitmapFactory.decodeFile(imageFile.absolutePath, opts : this)
    }
}

private fun calculateInSampleSize(context: BitmapFactory, reqWidth: Int, reqHeight: Int): Int {
    //解構(gòu)語法,獲取原始圖片的寬高
    val (height: Int, width: Int) = options.run { outHeight to outwidth }
    //計(jì)算最大的inSampleSize值,該值為2的冪次方,并同時(shí)保持這兩個(gè)值高度和寬度大于請(qǐng)求的高度和寬度。
    //原始圖片的寬高要大于要求的寬高
    var inSampleSize = 1
    if (height > reqHeight || width > reqWidth) {
        val halfHeight: Int = height / 2
        val halfwidth: Int = width / 2
        while (halfHeight / inSampleSize >= reqHeight && halfwidth / inSampleSize >= reqWidth) {
            inSampleSize *= 2
        }
    }
    return inSampleSize
}

inSampleSize都是2的倍數(shù) .

BitmapFactory 給我們提供了一個(gè)解析圖片大小的參數(shù)類 BitmapFactory.Options ,把這個(gè)類的對(duì)象的 inJustDecodeBounds 參數(shù)設(shè)置為 true,這樣解析出來的 Bitmap 雖然是個(gè) null,但是 options 中可以得到圖片的寬和高以及圖片的類型。得到了圖片實(shí)際的寬和高之后我們就可以進(jìn)行壓縮設(shè)置了,主要是計(jì)算圖片的采樣率。

  • 第一次采樣已經(jīng)結(jié)束,我們已經(jīng)成功的計(jì)算出了sampleSize的大小
  • 第二次采樣時(shí)我需要將圖片加載出來顯示,不能只加載圖片的框架,因此inJustDecodeBounds屬性要設(shè)置為false

libjpeg

  • libjpeg是一個(gè)完全用C語言編寫的庫,包含了被廣泛使用的JPEG解碼、JPEG編碼和其他的JPEG功能的實(shí)現(xiàn)。
  • libjpeg-turbo圖像編解碼器,使用了SIMD指令來加速x86、x86-64、ARM和 PowerPC系統(tǒng)上的JPEG壓縮和解壓縮,libjpeg-turbo 的速度通常是libjpeg 的2-6倍。
  • 可以使用采用哈夫曼
  • 微信采用的方式

圖片壓縮流程

其實(shí)最重要的是把ARGB轉(zhuǎn)換為RBG,也就是把每個(gè)像素4個(gè)字節(jié),轉(zhuǎn)換為每個(gè)像素3個(gè)字節(jié)。

導(dǎo)入對(duì)應(yīng)的so庫文件即可編寫C的代碼 jpeg.so 和 jpeg-turbo.so

編寫這部分的代碼需要NDK的環(huán)境和C語言的基礎(chǔ)

偽代碼

int generateCompressJPEG(BYTE *data, int w, int h, int quality, const char *outfileName, jboolean optimize) {
    //結(jié)構(gòu)體相當(dāng)于java的類
    struct jpeg_compress_struct jcs;
    //當(dāng)讀完整個(gè)文件的時(shí)候回回調(diào)
    struct my_error_mgr jem;
    jcs.err = jpeg_std_error(&jem.pub);
    jem.pub.error_exit = my_error_exit;
    //setjmp是一個(gè)系統(tǒng)級(jí)函數(shù),是一個(gè)回調(diào)
    if (setjmp(jem.setjmp_buffer)) {
        return 0;
    }
    //初始化jsc結(jié)構(gòu)體
    jpeg_create_compress(&jcs);
    //打開輸出文件  wb可寫  rb可讀
    FILE *f = fopen(outfileName, "wb");
    if (f == NULL) {
        return 0;
    }
    //設(shè)置結(jié)構(gòu)體的文件路徑,以及寬高
    jpeg_stdio_dest(&jcs, f);
    jcs.image_width = w;
    jcs.image_height = h;
    //TRUE=arithmetic coding, FALSE=Huffman
    jcs.arith_code = false;
    int nComponent = 3;
    // 顏色的組成rgb,三個(gè) of color components in input image
    jcs.input_components = nComponent;
    // 設(shè)置顏色空間為rgb
    jcs.in_color_space = JCS_RGB;
    jpeg_set_defaults(&jcs);
    // 是否采用哈夫曼
    jcs.optimize_coding = optimize;
    //設(shè)置質(zhì)量
    jpeg_set_quality(&jcs, quality, true);
    //開始?jí)嚎s
    jpeg_start_compress(&jcs, TRUE);
    JSAMPROW row_pointer[1];
    int row_stride;
    row_stride = jcs.image_width * nComponent;
    while (jcs.next_scanline < jcs.image_height) {
        //得到一行的首地址
        row_pointer[0] = &data[jcs.next_scanline * row_stride];
        jpeg_write_scanlines(&jcs, row_pointer, 1);
    }
    // 壓縮結(jié)束
    jpeg_finish_compress(&jcs);
    // 銷毀回收內(nèi)存
    jpeg_destroy_compress(&jcs);
    //關(guān)閉文件
    fclose(f);
    return 1;
}
for (int i = 0; i < bitmapInfo.height; ++i) {
    for (int j= 0; j < bitmapInfo.width; ++j){
        if (bitmapInfo.format == ANDROID_BITMAP_FORMAT_RGBA_8888){
            //0x2312faff ->588446463
            color = *(int *) (pixelsColor);
            // 從color值中讀取RGBA的值/ /ABGR
            b = (color >> 16)& 0xFE;
            g = (color >> 8)& OxFF;
            r = (color >> 0) & OxFF;
            *data = r;
            * (data + 1) =g;
            *(data + 2) = b;
            data += 3;
            //移動(dòng)步長4個(gè)字節(jié)
            pixelsColor +- 4 ;
        }else {
            return -2;
        }
        
    // 是否采用哈夫曼
     jcs.optimize_coding = optimize;

至此,三種圖片壓縮的方法已經(jīng)介紹完畢了。

總結(jié)

經(jīng)過圖片壓縮實(shí)踐,質(zhì)量壓縮和libjpeg最后的圖片的大小一樣,效果也和原圖差不多。

其實(shí),經(jīng)過我翻查原碼發(fā)現(xiàn),新版本的Bitmap.compress() 會(huì)調(diào)用

boolean result = nativeCompress(mNativePtr, format.nativeInt,
        quality, stream, new byte[WORKING_COMPRESS_STORAGE]);

private static native boolean nativeCompress(long nativeBitmap, int format,
                                        int quality, OutputStream stream,
                                        byte[] tempStorage);

其實(shí)最后也會(huì)調(diào)用到nativeCompress的壓縮,也會(huì)采用哈夫曼算法,提高壓縮效率。

既然這樣,那么這里為什么還要介紹libjpeg的方法呢?

  • 兼容低版本,早起的compress沒有采用哈夫曼算法
  • 大廠的跨平臺(tái)算法

到此這篇關(guān)于Android性能優(yōu)化之圖片大小,尺寸壓縮綜合解決方案的文章就介紹到這了,更多相關(guān)Android圖片壓縮解決內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Flutter實(shí)現(xiàn)不同縮放動(dòng)畫效果詳解

    Flutter實(shí)現(xiàn)不同縮放動(dòng)畫效果詳解

    這篇文章主要為大家詳細(xì)介紹了Flutter利用不同組件(ScaleTransition、SizeTransition、AnimatedSize和AnimatedBuilder)實(shí)現(xiàn)不同縮放動(dòng)畫效果,感興趣的可以動(dòng)手嘗試一下
    2022-06-06
  • Android實(shí)現(xiàn)簡單計(jì)算器

    Android實(shí)現(xiàn)簡單計(jì)算器

    這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)簡單計(jì)算器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-10-10
  • Android開發(fā)框架MVC-MVP-MVVM-MVI的演變Demo

    Android開發(fā)框架MVC-MVP-MVVM-MVI的演變Demo

    這篇文章主要為大家介紹了Android開發(fā)框架MVC-MVP-MVVM-MVI的演變Demo,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10
  • Android 實(shí)現(xiàn)的下拉刷新效果

    Android 實(shí)現(xiàn)的下拉刷新效果

    最近在使用趕集網(wǎng)的時(shí)候,發(fā)現(xiàn)他的下拉刷新十分有趣,是一頭飛行的小毛驢,作為開發(fā)者自然心里癢癢打算把它做出來順便鍛煉下自己的動(dòng)手能力。本文講解如何仿照實(shí)現(xiàn)該效果
    2021-06-06
  • 一步步實(shí)現(xiàn)Viewpager卡片翻頁效果

    一步步實(shí)現(xiàn)Viewpager卡片翻頁效果

    一步步實(shí)現(xiàn)Viewpager卡片翻頁效果,文章很精彩,實(shí)現(xiàn)步驟很詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-08-08
  • Android編程實(shí)現(xiàn)圖片平鋪的方法分析

    Android編程實(shí)現(xiàn)圖片平鋪的方法分析

    這篇文章主要介紹了Android編程實(shí)現(xiàn)圖片平鋪的方法,結(jié)合具體實(shí)例形式總結(jié)分析了Android實(shí)現(xiàn)圖片平鋪效果的三種常用操作技巧,需要的朋友可以參考下
    2017-06-06
  • Android中獲得正在運(yùn)行的程序和系統(tǒng)服務(wù)的方法

    Android中獲得正在運(yùn)行的程序和系統(tǒng)服務(wù)的方法

    這篇文章主要介紹了Android中獲得正在運(yùn)行的程序和系統(tǒng)服務(wù)的方法,分別是對(duì)ActivityManager.RunningAppProcessInfo類和ActivityManager.RunningServiceInfo類的使用,需要的朋友可以參考下
    2016-02-02
  • 教你一文搞懂Kotlin中的Jvm注解

    教你一文搞懂Kotlin中的Jvm注解

    這篇文章主要介紹了教你一文搞懂Kotlin中的Jvm注解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-10-10
  • Android代碼檢查規(guī)則Lint的自定義與應(yīng)用詳解

    Android代碼檢查規(guī)則Lint的自定義與應(yīng)用詳解

    本文主要介紹了Android代碼檢查規(guī)則Lint的自定義與應(yīng)用詳解,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • 解析Android應(yīng)用啟動(dòng)后自動(dòng)創(chuàng)建桌面快捷方式的實(shí)現(xiàn)方法

    解析Android應(yīng)用啟動(dòng)后自動(dòng)創(chuàng)建桌面快捷方式的實(shí)現(xiàn)方法

    和IOS開發(fā)和Windows Phone開發(fā)相比,Android是開放的,Android上的開發(fā)也相對(duì)更加靈活,能夠做很多事情。有的朋友會(huì)發(fā)現(xiàn),在某些Android應(yīng)用安裝以后,第一次運(yùn)行,就會(huì)在桌面創(chuàng)建快捷方式。這是如何做到的呢
    2013-05-05

最新評(píng)論