您好,登錄后才能下訂單哦!
這篇文章主要介紹“Android性能優(yōu)化之圖片大小,尺寸壓縮的方法”,在日常操作中,相信很多人在Android性能優(yōu)化之圖片大小,尺寸壓縮的方法問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Android性能優(yōu)化之圖片大小,尺寸壓縮的方法”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!
在Android中我們經(jīng)常會遇到圖片壓縮的場景,比如給服務(wù)端上傳圖片,包括個人信息的用戶頭像,有時候人臉識別也需要捕獲圖片等等。這種情況下,我們都需要對圖片做一定的處理,比如大小,尺寸等的壓縮。
質(zhì)量壓縮
尺寸壓縮
libjpeg
首先我們要介紹一個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);這個方法有三個參數(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 () }
先來看看一個屬性O(shè)ptions
屬性 inJustDecodeBounds,如果該值為true,那么將不返回實際的 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 //先獲取原始圖片的寬高,不會將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 } //計算最大的inSampleSize值,該值為2的冪次方,并同時保持這兩個值高度和寬度大于請求的高度和寬度。 //原始圖片的寬高要大于要求的寬高 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 給我們提供了一個解析圖片大小的參數(shù)類 BitmapFactory.Options ,把這個類的對象的 inJustDecodeBounds 參數(shù)設(shè)置為 true,這樣解析出來的 Bitmap 雖然是個 null,但是 options 中可以得到圖片的寬和高以及圖片的類型。得到了圖片實際的寬和高之后我們就可以進行壓縮設(shè)置了,主要是計算圖片的采樣率。
第一次采樣已經(jīng)結(jié)束,我們已經(jīng)成功的計算出了sampleSize的大小
第二次采樣時我需要將圖片加載出來顯示,不能只加載圖片的框架,因此inJustDecodeBounds屬性要設(shè)置為false
libjpeg是一個完全用C語言編寫的庫,包含了被廣泛使用的JPEG解碼、JPEG編碼和其他的JPEG功能的實現(xiàn)。
libjpeg-turbo圖像編解碼器,使用了SIMD指令來加速x86、x86-64、ARM和 PowerPC系統(tǒng)上的JPEG壓縮和解壓縮,libjpeg-turbo 的速度通常是libjpeg 的2-6倍。
可以使用采用哈夫曼
微信采用的方式
其實最重要的是把ARGB轉(zhuǎn)換為RBG,也就是把每個像素4個字節(jié),轉(zhuǎn)換為每個像素3個字節(jié)。
導(dǎo)入對應(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)讀完整個文件的時候回回調(diào) struct my_error_mgr jem; jcs.err = jpeg_std_error(&jem.pub); jem.pub.error_exit = my_error_exit; //setjmp是一個系統(tǒng)級函數(shù),是一個回調(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,三個 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); //開始壓縮 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; //移動步長4個字節(jié) pixelsColor +- 4 ; }else { return -2; } // 是否采用哈夫曼 jcs.optimize_coding = optimize;
至此,三種圖片壓縮的方法已經(jīng)介紹完畢了。
經(jīng)過圖片壓縮實踐,質(zhì)量壓縮和libjpeg最后的圖片的大小一樣,效果也和原圖差不多。
其實,經(jīng)過我翻查原碼發(fā)現(xiàn),新版本的Bitmap.compress() 會調(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);
其實最后也會調(diào)用到nativeCompress的壓縮,也會采用哈夫曼算法,提高壓縮效率。
既然這樣,那么這里為什么還要介紹libjpeg的方法呢?
兼容低版本,早起的compress沒有采用哈夫曼算法
大廠的跨平臺算法
到此,關(guān)于“Android性能優(yōu)化之圖片大小,尺寸壓縮的方法”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。