這篇文章給大家介紹怎么在Android應用中利用Bitmap對圖片進行優(yōu)化,內(nèi)容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。
成都創(chuàng)新互聯(lián)公司專注于周村網(wǎng)站建設服務及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗。 熱誠為您提供周村營銷型網(wǎng)站建設,周村網(wǎng)站制作、周村網(wǎng)頁設計、周村網(wǎng)站官網(wǎng)定制、微信小程序服務,打造周村網(wǎng)絡公司原創(chuàng)品牌,更為您提供周村網(wǎng)站排名全網(wǎng)營銷落地服務。
前言
在Android開發(fā)過程中,Bitmap往往會給開發(fā)者帶來一些困擾,因為對Bitmap操作不慎,就容易造成OOM(Java.lang.OutofMemoryError
- 內(nèi)存溢出),本篇博客,我們將一起探討B(tài)itmap的性能優(yōu)化。
為什么Bitmap會導致OOM?
1.每個機型在編譯ROM時都設置了一個應用堆內(nèi)存VM值上限dalvik.vm.heapgrowthlimit
,用來限定每個應用可用的最大內(nèi)存,超出這個最大值將會報OOM。這個閥值,一般根據(jù)手機屏幕dpi大小遞增,dpi越小的手機,每個應用可用最大內(nèi)存就越低。所以當加載圖片的數(shù)量很多時,就很容易超過這個閥值,造成OOM。
2.圖片分辨率越高,消耗的內(nèi)存越大,當加載高分辨率圖片的時候,將會非常占用內(nèi)存,一旦處理不當就會OOM。例如,一張分辨率為:1920x1080的圖片。如果Bitmap使用 ARGB_8888 32位來平鋪顯示的話,占用的內(nèi)存是1920x1080x4個字節(jié),占用將近8M內(nèi)存,可想而知,如果不對圖片進行處理的話,就會OOM。
3.在使用ListView, GridView等這些大量加載view的組件時,如果沒有合理的處理緩存,大量加載Bitmap的時候,也將容易引發(fā)OOM
Bitmap基礎知識
一張圖片Bitmap所占用的內(nèi)存 = 圖片長度 x 圖片寬度 x 一個像素點占用的字節(jié)數(shù)
而Bitmap.Config
,正是指定單位像素占用的字節(jié)數(shù)的重要參數(shù)。
其中,A代表透明度;R代表紅色;G代表綠色;B代表藍色。
ALPHA_8
表示8位Alpha位圖,即A=8,一個像素點占用1個字節(jié),它沒有顏色,只有透明度
ARGB_4444
表示16位ARGB位圖,即A=4,R=4,G=4,B=4,一個像素點占4+4+4+4=16位,2個字節(jié)
ARGB_8888
表示32位ARGB位圖,即A=8,R=8,G=8,B=8,一個像素點占8+8+8+8=32位,4個字節(jié)
RGB_565
表示16位RGB位圖,即R=5,G=6,B=5,它沒有透明度,一個像素點占5+6+5=16位,2個字節(jié)
一張圖片Bitmap所占用的內(nèi)存 = 圖片長度 x 圖片寬度 x 一個像素點占用的字節(jié)數(shù)
根據(jù)以上的算法,可以計算出圖片占用的內(nèi)存,以100*100像素的圖片為例
BitmapFactory解析Bitmap的原理
BitmapFactory提供的解析Bitmap的靜態(tài)工廠方法有以下五種:
Bitmap decodeFile(...) Bitmap decodeResource(...) Bitmap decodeByteArray(...) Bitmap decodeStream(...) Bitmap decodeFileDescriptor(...)
其中常用的三個:decodeFile、decodeResource、decodeStream。
decodeFile和decodeResource其實最終都是調(diào)用decodeStream方法來解析Bitmap
decodeFile方法代碼:
public static Bitmap decodeFile(String pathName, Options opts) { Bitmap bm = null; InputStream stream = null; try { stream = new FileInputStream(pathName); bm = decodeStream(stream, null, opts); } catch (Exception e) { /* do nothing. If the exception happened on open, bm will be null. */ Log.e("BitmapFactory", "Unable to decode stream: " + e); } finally { if (stream != null) { try { stream.close(); } catch (IOException e) { // do nothing here } } }
decodeResource方法的代碼:
public static Bitmap decodeResource(Resources res, int id, Options opts) { Bitmap bm = null; InputStream is = null; try { final TypedValue value = new TypedValue(); is = res.openRawResource(id, value); bm = decodeResourceStream(res, value, is, null, opts); } catch (Exception e) { /* do nothing. If the exception happened on open, bm will be null. If it happened on close, bm is still valid. */ } finally { try { if (is != null) is.close(); } catch (IOException e) { // Ignore } } if (bm == null && opts != null && opts.inBitmap != null) { throw new IllegalArgumentException("Problem decoding into existing bitmap"); } return bm; }
decodeStream的邏輯如下:
public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) { // we don't throw in this case, thus allowing the caller to only check // the cache, and not force the image to be decoded. if (is == null) { return null; } Bitmap bm = null; Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeBitmap"); try { if (is instanceof AssetManager.AssetInputStream) { final long asset = ((AssetManager.AssetInputStream) is).getNativeAsset(); bm = nativeDecodeAsset(asset, outPadding, opts); } else { bm = decodeStreamInternal(is, outPadding, opts); } if (bm == null && opts != null && opts.inBitmap != null) { throw new IllegalArgumentException("Problem decoding into existing bitmap"); } setDensityFromOptions(bm, opts); } finally { Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS); } return bm; } private static Bitmap decodeStreamInternal(InputStream is, Rect outPadding, Options opts) { // ASSERT(is != null); byte [] tempStorage = null; if (opts != null) tempStorage = opts.inTempStorage; if (tempStorage == null) tempStorage = new byte[DECODE_BUFFER_SIZE]; return nativeDecodeStream(is, tempStorage, outPadding, opts); }
從上面的代碼可以看出,decodeStream的代碼最終會調(diào)用以下兩個native方法之一
nativeDecodeAsset() nativeDecodeStream()
這兩個native方法只是對應decodeFile和decodeResource、decodeStream來解析的,像decodeByteArray、decodeFileDescriptor也有專門的native方法負責解析Bitmap。
decodeFile、decodeResource的區(qū)別在于他們方法的調(diào)用路徑不同:
decodeFile->decodeStream decodeResource->decodeResourceStream->decodeStream
decodeResource在解析時多調(diào)用了一個decodeResourceStream方法,而這個decodeResourceStream方法代碼如下:
public static Bitmap decodeResourceStream(Resources res, TypedValue value, InputStream is, Rect pad, Options opts) { if (opts == null) { opts = new Options(); } if (opts.inDensity == 0 && value != null) { final int density = value.density; if (density == TypedValue.DENSITY_DEFAULT) { opts.inDensity = DisplayMetrics.DENSITY_DEFAULT; } else if (density != TypedValue.DENSITY_NONE) { opts.inDensity = density; } } if (opts.inTargetDensity == 0 && res != null) { opts.inTargetDensity = res.getDisplayMetrics().densityDpi; } return decodeStream(is, pad, opts); }
其中對Options進行處理了,在得到opts.inDensity
屬性的前提下,如果我們沒有對該屬性設定值,那么將opts.inDensity = DisplayMetrics.DENSITY_DEFAULT;
賦定這個默認的Density值,這個默認值為160,為標準的dpi比例,即在Density=160
的設備上1dp=1px,這個方法中還有這么一行
opts.inTargetDensity = res.getDisplayMetrics().densityDpi;
對opts.inTargetDensity
進行了賦值,該值為當前設備的densityDpi值,所以說在decodeResourceStream方法中主要做了兩件事:
1.對opts.inDensity
賦值,沒有則賦默認值160
2.對opts.inTargetDensity
賦值,沒有則賦當前設備的densityDpi值
之后參數(shù)將傳入decodeStream方法,該方法中在調(diào)用native方法進行解析Bitmap后會調(diào)用這個方法setDensityFromOptions(bm, opts);
:
private static void setDensityFromOptions(Bitmap outputBitmap, Options opts) { if (outputBitmap == null || opts == null) return; final int density = opts.inDensity; if (density != 0) { outputBitmap.setDensity(density); final int targetDensity = opts.inTargetDensity; if (targetDensity == 0 || density == targetDensity || density == opts.inScreenDensity) { return; } byte[] np = outputBitmap.getNinePatchChunk(); final boolean isNinePatch = np != null && NinePatch.isNinePatchChunk(np); if (opts.inScaled || isNinePatch) { outputBitmap.setDensity(targetDensity); } } else if (opts.inBitmap != null) { // bitmap was reused, ensure density is reset outputBitmap.setDensity(Bitmap.getDefaultDensity()); } }
主要就是把剛剛賦值過的兩個屬性inDensity和inTargetDensity給Bitmap進行賦值,不過并不是直接賦給Bitmap就完了,中間有個判斷,當inDensity的值與inTargetDensity或與設備的屏幕Density不相等時,則將應用inTargetDensity的值,如果相等則應用inDensity的值。
所以總結(jié)來說,setDensityFromOptions方法就是把inTargetDensity的值賦給Bitmap,不過前提是opts.inScaled = true
;
進過上面的分析,結(jié)論如下:
在不配置Options的情況下:
1.decodeFile、decodeStream在解析時不會對Bitmap進行一系列的屏幕適配,解析出來的將是原始大小的圖
2.decodeResource在解析時會對Bitmap根據(jù)當前設備屏幕像素密度densityDpi的值進行縮放適配操作,使得解析出來的Bitmap與當前設備的分辨率匹配,達到一個最佳的顯示效果,并且Bitmap的大小將比原始的大
Bitmap的優(yōu)化策略
經(jīng)過上面的分析,我們可以得出Bitmap優(yōu)化的思路:
1、BitmapConfig的配置
2、使用decodeFile、decodeResource、decodeStream進行解析Bitmap時,配置inDensity和inTargetDensity,兩者應該相等,值可以等于屏幕像素密度*0.75f
3、使用inJustDecodeBounds預判斷Bitmap的大小及使用inSampleSize進行壓縮
4、對Density>240的設備進行Bitmap的適配(縮放Density)
5、2.3版本inNativeAlloc的使用
6、4.4以下版本inPurgeable、inInputShareable的使用
7、Bitmap的回收
所以我們根據(jù)以上的思路,我們將Bitmap優(yōu)化的策略總結(jié)為以下3種:
1.對圖片質(zhì)量進行壓縮
2.對圖片尺寸進行壓縮
3.使用libjpeg.so庫進行壓縮
對圖片質(zhì)量進行壓縮
public static Bitmap compressImage(Bitmap bitmap){ ByteArrayOutputStream baos = new ByteArrayOutputStream(); //質(zhì)量壓縮方法,這里100表示不壓縮,把壓縮后的數(shù)據(jù)存放到baos中 bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos); int options = 100; //循環(huán)判斷如果壓縮后圖片是否大于50kb,大于繼續(xù)壓縮 while ( baos.toByteArray().length / 1024>50) { //清空baos baos.reset(); bitmap.compress(Bitmap.CompressFormat.JPEG, options, baos); options -= 10;//每次都減少10 } //把壓縮后的數(shù)據(jù)baos存放到ByteArrayInputStream中 ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray()); //把ByteArrayInputStream數(shù)據(jù)生成圖片 Bitmap newBitmap = BitmapFactory.decodeStream(isBm, null, null); return newBitmap; }
對圖片尺寸進行壓縮
/** * 按圖片尺寸壓縮 參數(shù)是bitmap * @param bitmap * @param pixelW * @param pixelH * @return */ public static Bitmap compressImageFromBitmap(Bitmap bitmap, int pixelW, int pixelH) { ByteArrayOutputStream os = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os); if( os.toByteArray().length / 1024>512) {//判斷如果圖片大于0.5M,進行壓縮避免在生成圖片(BitmapFactory.decodeStream)時溢出 os.reset(); bitmap.compress(Bitmap.CompressFormat.JPEG, 50, os);//這里壓縮50%,把壓縮后的數(shù)據(jù)存放到baos中 } ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray()); BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; options.inPreferredConfig = Bitmap.Config.RGB_565; BitmapFactory.decodeStream(is, null, options); options.inJustDecodeBounds = false; options.inSampleSize = computeSampleSize(options , pixelH > pixelW ? pixelW : pixelH ,pixelW * pixelH ); is = new ByteArrayInputStream(os.toByteArray()); Bitmap newBitmap = BitmapFactory.decodeStream(is, null, options); return newBitmap; } /** * 動態(tài)計算出圖片的inSampleSize * @param options * @param minSideLength * @param maxNumOfPixels * @return */ public 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; } } else { roundedSize = (initialSize + 7) / 8 * 8; } return roundedSize; } private static int computeInitialSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) { double w = options.outWidth; double h = options.outHeight; int lowerBound = (maxNumOfPixels == -1) ? 1 : (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels)); int upperBound = (minSideLength == -1) ? 128 :(int) Math.min(Math.floor(w / minSideLength), Math.floor(h / minSideLength)); if (upperBound < lowerBound) { return lowerBound; } if ((maxNumOfPixels == -1) && (minSideLength == -1)) { return 1; } else if (minSideLength == -1) { return lowerBound; } else { return upperBound; } }
使用libjpeg.so庫進行壓縮
除了通過設置simpleSize根據(jù)圖片尺寸壓縮圖片和通過Bitmap.compress
方法通過壓縮圖片質(zhì)量兩種方法外,我們還可以使用libjpeg.so這個庫來進行壓縮。
libjpeg是廣泛使用的開源JPEG圖像庫,Android所用的是skia的壓縮算法,而Skia對libjpeg進行了的封裝。
libjpeg在壓縮圖像時,有一個參數(shù)叫optimize_coding,關于這個參數(shù),libjpeg.doc有如下解釋:
boolean optimize_coding TRUE causes the compressor to compute optimal Huffman coding tables for the image. This requires an extra pass over the data and therefore costs a good deal of space and time. The default is FALSE, which tells the compressor to use the supplied or default Huffman tables. In most cases optimal tables save only a few percent of file size compared to the default tables. Note that when this is TRUE, you need not supply Huffman tables at all, and any you do supply will be overwritten.
如果設置optimize_coding為TRUE,將會使得壓縮圖像過程中基于圖像數(shù)據(jù)計算哈弗曼表,由于這個計算會顯著消耗空間和時間,默認值被設置為FALSE。
谷歌的Skia項目工程師們最終沒有設置這個參數(shù),optimize_coding在Skia中默認的等于了FALSE,但是問題就隨之出現(xiàn)了,如果我們想在FALSE和TRUE時壓縮成相同大小的JPEG 圖片,F(xiàn)ALSE的品質(zhì)將大大遜色于TRUE的,盡管谷歌工程師沒有將該值設置為true,但是我們可以自己編譯libjpeg進行圖片的壓縮。
libjpeg的官網(wǎng)下載地址:http://www.ijg.org/
從官網(wǎng)下載之后,我們必須自己對其進行編譯。
編譯libjpeg
下載最新的源碼,解壓后將所有文件放到jni目錄中,準備用ndk編譯
1、新建config.sh,將ndk中的交叉編譯工具加入其中,內(nèi)容如下:
NDK=/opt/ndk/android-ndk-r10e/ PLATFORM=$NDK/platforms/android-9/arch-arm/ PREBUILT=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86/ CC=$PREBUILT/bin/arm-linux-androideabi-gcc ./configure --prefix=/home/linc/jpeg-9b/jni/dist --host=arm CC="$CC --sysroot=$PLATFORM"
2、執(zhí)行此腳本
$ sh config.sh ... checking whether to build shared libraries... no checking whether to build static libraries... yes ... config.status: creating Makefile config.status: creating jconfig.h
首先,它生成了Makefile,我們可以直接使用此Makefile進行編譯;其次,它生成了重要的頭文件,jconfig.h.
但是這個Makefile是編譯static庫而不是共享庫的。
此時,我們可以執(zhí)行構(gòu)建命令進行編譯:
jni$ make install-libLTLIBRARIES libtool: install: ranlib /home/linc/jpeg-9b/jni/dist/lib/libjpeg.a
3、Android.mk
使用ndk-build指令編譯,需要手動編寫Android.mk文件,內(nèi)容如下:
LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_ARM_MODE := arm LOCAL_SRC_FILES :=jaricom.c jcapimin.c jcapistd.c jcarith.c jccoefct.c jccolor.c \ jcdctmgr.c jchuff.c jcinit.c jcmainct.c jcmarker.c jcmaster.c \ jcomapi.c jcparam.c jcprepct.c jcsample.c jctrans.c jdapimin.c \ jdapistd.c jdarith.c jdatadst.c jdatasrc.c jdcoefct.c jdcolor.c \ jddctmgr.c jdhuff.c jdinput.c jdmainct.c jdmarker.c jdmaster.c \ jdmerge.c jdpostct.c jdsample.c jdtrans.c jerror.c jfdctflt.c \ jfdctfst.c jfdctint.c jidctflt.c jidctfst.c jidctint.c jquant1.c \ jquant2.c jutils.c jmemmgr.c jmemnobs.c LOCAL_C_INCLUDES := $(LOCAL_PATH) LOCAL_CFLAGS += -O3 -fstrict-aliasing -fprefetch-loop-arrays \ -DANDROID -DANDROID_TILE_BASED_DECODE -DENABLE_ANDROID_NULL_CONVERT LOCAL_MODULE := libjpeg LOCAL_MODULE_TAGS := optional # unbundled branch, built against NDK. LOCAL_SDK_VERSION := 17 include $(BUILD_SHARED_LIBRARY)
其中LOCAL_SRC_FILES后面的源文件可以參考剛剛生成的Makefile。
在jni目錄上一級使用ndk-build編譯即可。
$ ndk-build [armeabi] Compile arm : jpeg <= jaricom.c ... [armeabi] Compile arm : jpeg <= jmemnobs.c [armeabi] SharedLibrary : libjpeg.so [armeabi] Install : libjpeg.so => libs/armeabi/libjpeg.so
在Android項目引入編譯好的libjpeg
首先把so庫加載到libs中,然后將編譯好的頭文件拷貝到項目的jni文件夾下,就可以使用Android的具體函數(shù)了,具體使用分為如下幾步:
1、將Android的bitmap解碼并轉(zhuǎn)換為RGB數(shù)據(jù)
2、為JPEG對象分配空間并初始化
3、指定壓縮數(shù)據(jù)源
4、獲取文件信息
5、為壓縮設定參數(shù),包括圖像大小,顏色空間
6、開始壓縮
7、壓縮完畢
8、釋放資源
#include#include #include #include #include #include #include #include #include #include "jpeglib.h" #include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ #include "jversion.h" /* for version message */ #include "config.h" #define LOG_TAG "jni" #define LOGW(...) __android_log_write(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__) #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) #define true 1 #define false 0 typedef uint8_t BYTE; char *error; struct my_error_mgr { struct jpeg_error_mgr pub; jmp_buf setjmp_buffer; }; typedef struct my_error_mgr * my_error_ptr; METHODDEF(void) my_error_exit (j_common_ptr cinfo) { my_error_ptr myerr = (my_error_ptr) cinfo->err; (*cinfo->err->output_message) (cinfo); error=myerr->pub.jpeg_message_table[myerr->pub.msg_code]; LOGE("jpeg_message_table[%d]:%s", myerr->pub.msg_code,myerr->pub.jpeg_message_table[myerr->pub.msg_code]); // LOGE("addon_message_table:%s", myerr->pub.addon_message_table); // LOGE("SIZEOF:%d",myerr->pub.msg_parm.i[0]); // LOGE("sizeof:%d",myerr->pub.msg_parm.i[1]); longjmp(myerr->setjmp_buffer, 1); } //圖片壓縮方法 int generateJPEG(BYTE* data, int w, int h, int quality, const char* outfilename, jboolean optimize) { int nComponent = 3; struct jpeg_compress_struct jcs; struct my_error_mgr jem; jcs.err = jpeg_std_error(&jem.pub); jem.pub.error_exit = my_error_exit; if (setjmp(jem.setjmp_buffer)) { return 0; } //為JPEG對象分配空間并初始化 jpeg_create_compress(&jcs); //獲取文件信息 FILE* f = fopen(outfilename, "wb"); if (f == NULL) { return 0; } //指定壓縮數(shù)據(jù)源 jpeg_stdio_dest(&jcs, f); jcs.image_width = w; jcs.image_height = h; if (optimize) { LOGI("optimize==ture"); } else { LOGI("optimize==false"); } jcs.arith_code = false; jcs.input_components = nComponent; if (nComponent == 1) jcs.in_color_space = JCS_GRAYSCALE; else jcs.in_color_space = JCS_RGB; jpeg_set_defaults(&jcs); jcs.optimize_coding = optimize; //為壓縮設定參數(shù),包括圖像大小,顏色空間 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]; //寫入數(shù)據(jù) jpeg_write_scanlines(&jcs, row_pointer, 1); } if (jcs.optimize_coding) { LOGI("optimize==ture"); } else { LOGI("optimize==false"); } //壓縮完畢 jpeg_finish_compress(&jcs); //釋放資源 jpeg_destroy_compress(&jcs); fclose(f); return 1; } typedef struct { uint8_t r; uint8_t g; uint8_t b; } rgb; //將java string轉(zhuǎn)換為char* char* jstrinTostring(JNIEnv* env, jbyteArray barr) { char* rtn = NULL; jsize alen = (*env)->GetArrayLength(env, barr); jbyte* ba = (*env)->GetByteArrayElements(env, barr, 0); if (alen > 0) { rtn = (char*) malloc(alen + 1); memcpy(rtn, ba, alen); rtn[alen] = 0; } (*env)->ReleaseByteArrayElements(env, barr, ba, 0); return rtn; } //jni方法入口 jstring Java_net_bither_util_NativeUtil_compressBitmap(JNIEnv* env, jobject thiz, jobject bitmapcolor, int w, int h, int quality, jbyteArray fileNameStr, jboolean optimize) { AndroidBitmapInfo infocolor; BYTE* pixelscolor; int ret; BYTE * data; BYTE *tmpdata; char * fileName = jstrinTostring(env, fileNameStr); //解碼Android bitmap信息,并存儲值infocolor中 if ((ret = AndroidBitmap_getInfo(env, bitmapcolor, &infocolor)) < 0) { LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret); return (*env)->NewStringUTF(env, "0");; } if ((ret = AndroidBitmap_lockPixels(env, bitmapcolor, &pixelscolor)) < 0) { LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret); } BYTE r, g, b; data = NULL; data = malloc(w * h * 3); tmpdata = data; int j = 0, i = 0; int color; //將bitmap轉(zhuǎn)換為rgb數(shù)據(jù) for (i = 0; i < h; i++) { for (j = 0; j < w; j++) { color = *((int *) pixelscolor); r = ((color & 0x00FF0000) >> 16); g = ((color & 0x0000FF00) >> 8); b = color & 0x000000FF; *data = b; *(data + 1) = g; *(data + 2) = r; data = data + 3; pixelscolor += 4; } } AndroidBitmap_unlockPixels(env, bitmapcolor); //進行壓縮 int resultCode= generateJPEG(tmpdata, w, h, quality, fileName, optimize); free(tmpdata); if(resultCode==0){ jstring result=(*env)->NewStringUTF(env, error); error=NULL; return result; } return (*env)->NewStringUTF(env, "1"); //success }
新建Android.mk,生成可執(zhí)行文件:
LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= jpeg_compress.cpp LOCAL_MODULE:= jtest LOCAL_LDLIBS :=-llog LOCAL_LDLIBS += $(LOCAL_PATH)/libjpeg.so LOCAL_C_INCLUDES := $(LOCAL_PATH) LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) LOCAL_MODULE_TAGS := debug include $(BUILD_EXECUTABLE)
關于怎么在Android應用中利用Bitmap對圖片進行優(yōu)化就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。