欧美一级特黄大片做受成人-亚洲成人一区二区电影-激情熟女一区二区三区-日韩专区欧美专区国产专区

怎么在Android應(yīng)用中利用Bitmap對(duì)圖片進(jìn)行優(yōu)化

這篇文章給大家介紹怎么在Android應(yīng)用中利用Bitmap對(duì)圖片進(jìn)行優(yōu)化,內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對(duì)大家能有所幫助。

成都創(chuàng)新互聯(lián)公司專(zhuān)注于周村網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗(yàn)。 熱誠(chéng)為您提供周村營(yíng)銷(xiāo)型網(wǎng)站建設(shè),周村網(wǎng)站制作、周村網(wǎng)頁(yè)設(shè)計(jì)、周村網(wǎng)站官網(wǎng)定制、微信小程序服務(wù),打造周村網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供周村網(wǎng)站排名全網(wǎng)營(yíng)銷(xiāo)落地服務(wù)。

前言

在Android開(kāi)發(fā)過(guò)程中,Bitmap往往會(huì)給開(kāi)發(fā)者帶來(lái)一些困擾,因?yàn)閷?duì)Bitmap操作不慎,就容易造成OOM(Java.lang.OutofMemoryError - 內(nèi)存溢出),本篇博客,我們將一起探討B(tài)itmap的性能優(yōu)化。

為什么Bitmap會(huì)導(dǎo)致OOM?

1.每個(gè)機(jī)型在編譯ROM時(shí)都設(shè)置了一個(gè)應(yīng)用堆內(nèi)存VM值上限dalvik.vm.heapgrowthlimit,用來(lái)限定每個(gè)應(yīng)用可用的最大內(nèi)存,超出這個(gè)最大值將會(huì)報(bào)OOM。這個(gè)閥值,一般根據(jù)手機(jī)屏幕dpi大小遞增,dpi越小的手機(jī),每個(gè)應(yīng)用可用最大內(nèi)存就越低。所以當(dāng)加載圖片的數(shù)量很多時(shí),就很容易超過(guò)這個(gè)閥值,造成OOM。

2.圖片分辨率越高,消耗的內(nèi)存越大,當(dāng)加載高分辨率圖片的時(shí)候,將會(huì)非常占用內(nèi)存,一旦處理不當(dāng)就會(huì)OOM。例如,一張分辨率為:1920x1080的圖片。如果Bitmap使用 ARGB_8888 32位來(lái)平鋪顯示的話,占用的內(nèi)存是1920x1080x4個(gè)字節(jié),占用將近8M內(nèi)存,可想而知,如果不對(duì)圖片進(jìn)行處理的話,就會(huì)OOM。

3.在使用ListView, GridView等這些大量加載view的組件時(shí),如果沒(méi)有合理的處理緩存,大量加載Bitmap的時(shí)候,也將容易引發(fā)OOM

Bitmap基礎(chǔ)知識(shí)

一張圖片Bitmap所占用的內(nèi)存 = 圖片長(zhǎng)度 x 圖片寬度 x 一個(gè)像素點(diǎn)占用的字節(jié)數(shù)

Bitmap.Config,正是指定單位像素占用的字節(jié)數(shù)的重要參數(shù)。

怎么在Android應(yīng)用中利用Bitmap對(duì)圖片進(jìn)行優(yōu)化

其中,A代表透明度;R代表紅色;G代表綠色;B代表藍(lán)色。

ALPHA_8

表示8位Alpha位圖,即A=8,一個(gè)像素點(diǎn)占用1個(gè)字節(jié),它沒(méi)有顏色,只有透明度

ARGB_4444

表示16位ARGB位圖,即A=4,R=4,G=4,B=4,一個(gè)像素點(diǎn)占4+4+4+4=16位,2個(gè)字節(jié)

ARGB_8888

表示32位ARGB位圖,即A=8,R=8,G=8,B=8,一個(gè)像素點(diǎn)占8+8+8+8=32位,4個(gè)字節(jié)

RGB_565

表示16位RGB位圖,即R=5,G=6,B=5,它沒(méi)有透明度,一個(gè)像素點(diǎn)占5+6+5=16位,2個(gè)字節(jié)

一張圖片Bitmap所占用的內(nèi)存 = 圖片長(zhǎng)度 x 圖片寬度 x 一個(gè)像素點(diǎn)占用的字節(jié)數(shù)

根據(jù)以上的算法,可以計(jì)算出圖片占用的內(nèi)存,以100*100像素的圖片為例

怎么在Android應(yīng)用中利用Bitmap對(duì)圖片進(jìn)行優(yōu)化

BitmapFactory解析Bitmap的原理

BitmapFactory提供的解析Bitmap的靜態(tài)工廠方法有以下五種:

Bitmap decodeFile(...)
Bitmap decodeResource(...)
Bitmap decodeByteArray(...)
Bitmap decodeStream(...)
Bitmap decodeFileDescriptor(...)

其中常用的三個(gè):decodeFile、decodeResource、decodeStream。

decodeFile和decodeResource其實(shí)最終都是調(diào)用decodeStream方法來(lái)解析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的代碼最終會(huì)調(diào)用以下兩個(gè)native方法之一

nativeDecodeAsset()
nativeDecodeStream()

這兩個(gè)native方法只是對(duì)應(yīng)decodeFile和decodeResource、decodeStream來(lái)解析的,像decodeByteArray、decodeFileDescriptor也有專(zhuān)門(mén)的native方法負(fù)責(zé)解析Bitmap。

decodeFile、decodeResource的區(qū)別在于他們方法的調(diào)用路徑不同:

decodeFile->decodeStream
decodeResource->decodeResourceStream->decodeStream

decodeResource在解析時(shí)多調(diào)用了一個(gè)decodeResourceStream方法,而這個(gè)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);
 }

其中對(duì)Options進(jìn)行處理了,在得到opts.inDensity屬性的前提下,如果我們沒(méi)有對(duì)該屬性設(shè)定值,那么將opts.inDensity = DisplayMetrics.DENSITY_DEFAULT;賦定這個(gè)默認(rèn)的Density值,這個(gè)默認(rèn)值為160,為標(biāo)準(zhǔn)的dpi比例,即在Density=160的設(shè)備上1dp=1px,這個(gè)方法中還有這么一行

opts.inTargetDensity = res.getDisplayMetrics().densityDpi;

對(duì)opts.inTargetDensity進(jìn)行了賦值,該值為當(dāng)前設(shè)備的densityDpi值,所以說(shuō)在decodeResourceStream方法中主要做了兩件事:

     1.對(duì)opts.inDensity賦值,沒(méi)有則賦默認(rèn)值160

     2.對(duì)opts.inTargetDensity賦值,沒(méi)有則賦當(dāng)前設(shè)備的densityDpi值

之后參數(shù)將傳入decodeStream方法,該方法中在調(diào)用native方法進(jìn)行解析Bitmap后會(huì)調(diào)用這個(gè)方法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());
 }
 }

主要就是把剛剛賦值過(guò)的兩個(gè)屬性inDensity和inTargetDensity給Bitmap進(jìn)行賦值,不過(guò)并不是直接賦給Bitmap就完了,中間有個(gè)判斷,當(dāng)inDensity的值與inTargetDensity或與設(shè)備的屏幕Density不相等時(shí),則將應(yīng)用inTargetDensity的值,如果相等則應(yīng)用inDensity的值。

所以總結(jié)來(lái)說(shuō),setDensityFromOptions方法就是把inTargetDensity的值賦給Bitmap,不過(guò)前提是opts.inScaled = true

進(jìn)過(guò)上面的分析,結(jié)論如下:

在不配置Options的情況下:

      1.decodeFile、decodeStream在解析時(shí)不會(huì)對(duì)Bitmap進(jìn)行一系列的屏幕適配,解析出來(lái)的將是原始大小的圖

      2.decodeResource在解析時(shí)會(huì)對(duì)Bitmap根據(jù)當(dāng)前設(shè)備屏幕像素密度densityDpi的值進(jìn)行縮放適配操作,使得解析出來(lái)的Bitmap與當(dāng)前設(shè)備的分辨率匹配,達(dá)到一個(gè)最佳的顯示效果,并且Bitmap的大小將比原始的大

Bitmap的優(yōu)化策略

經(jīng)過(guò)上面的分析,我們可以得出Bitmap優(yōu)化的思路:

      1、BitmapConfig的配置

      2、使用decodeFile、decodeResource、decodeStream進(jìn)行解析Bitmap時(shí),配置inDensity和inTargetDensity,兩者應(yīng)該相等,值可以等于屏幕像素密度*0.75f

      3、使用inJustDecodeBounds預(yù)判斷Bitmap的大小及使用inSampleSize進(jìn)行壓縮

      4、對(duì)Density>240的設(shè)備進(jìn)行Bitmap的適配(縮放Density)

      5、2.3版本inNativeAlloc的使用

      6、4.4以下版本inPurgeable、inInputShareable的使用

      7、Bitmap的回收

所以我們根據(jù)以上的思路,我們將Bitmap優(yōu)化的策略總結(jié)為以下3種:

      1.對(duì)圖片質(zhì)量進(jìn)行壓縮

      2.對(duì)圖片尺寸進(jìn)行壓縮

      3.使用libjpeg.so庫(kù)進(jìn)行壓縮

對(duì)圖片質(zhì)量進(jìn)行壓縮

 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; 
 } 

對(duì)圖片尺寸進(jìn)行壓縮

 /**
  * 按圖片尺寸壓縮 參數(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,進(jìn)行壓縮避免在生成圖片(BitmapFactory.decodeStream)時(shí)溢出
   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;
 }


 /**
  * 動(dòng)態(tài)計(jì)算出圖片的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) &#63; 1 : (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));
  int upperBound = (minSideLength == -1) &#63; 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庫(kù)進(jìn)行壓縮

除了通過(guò)設(shè)置simpleSize根據(jù)圖片尺寸壓縮圖片和通過(guò)Bitmap.compress方法通過(guò)壓縮圖片質(zhì)量?jī)煞N方法外,我們還可以使用libjpeg.so這個(gè)庫(kù)來(lái)進(jìn)行壓縮。

libjpeg是廣泛使用的開(kāi)源JPEG圖像庫(kù),Android所用的是skia的壓縮算法,而Skia對(duì)libjpeg進(jìn)行了的封裝。
libjpeg在壓縮圖像時(shí),有一個(gè)參數(shù)叫optimize_coding,關(guān)于這個(gè)參數(shù),libjpeg.doc有如下解釋?zhuān)?/p>

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.

如果設(shè)置optimize_coding為T(mén)RUE,將會(huì)使得壓縮圖像過(guò)程中基于圖像數(shù)據(jù)計(jì)算哈弗曼表,由于這個(gè)計(jì)算會(huì)顯著消耗空間和時(shí)間,默認(rèn)值被設(shè)置為FALSE。

谷歌的Skia項(xiàng)目工程師們最終沒(méi)有設(shè)置這個(gè)參數(shù),optimize_coding在Skia中默認(rèn)的等于了FALSE,但是問(wèn)題就隨之出現(xiàn)了,如果我們想在FALSE和TRUE時(shí)壓縮成相同大小的JPEG 圖片,F(xiàn)ALSE的品質(zhì)將大大遜色于TRUE的,盡管谷歌工程師沒(méi)有將該值設(shè)置為true,但是我們可以自己編譯libjpeg進(jìn)行圖片的壓縮。

libjpeg的官網(wǎng)下載地址:http://www.ijg.org/

從官網(wǎng)下載之后,我們必須自己對(duì)其進(jìn)行編譯。

編譯libjpeg

下載最新的源碼,解壓后將所有文件放到j(luò)ni目錄中,準(zhǔn)備用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進(jìn)行編譯;其次,它生成了重要的頭文件,jconfig.h.
但是這個(gè)Makefile是編譯static庫(kù)而不是共享庫(kù)的。

此時(shí),我們可以執(zhí)行構(gòu)建命令進(jìn)行編譯:

jni$ make install-libLTLIBRARIES
libtool: install: ranlib /home/linc/jpeg-9b/jni/dist/lib/libjpeg.a

3、Android.mk

使用ndk-build指令編譯,需要手動(dòng)編寫(xiě)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目錄上一級(jí)使用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項(xiàng)目引入編譯好的libjpeg

首先把so庫(kù)加載到libs中,然后將編譯好的頭文件拷貝到項(xiàng)目的jni文件夾下,就可以使用Android的具體函數(shù)了,具體使用分為如下幾步:

     1、將Android的bitmap解碼并轉(zhuǎn)換為RGB數(shù)據(jù)

     2、為JPEG對(duì)象分配空間并初始化

     3、指定壓縮數(shù)據(jù)源

     4、獲取文件信息

     5、為壓縮設(shè)定參數(shù),包括圖像大小,顏色空間

     6、開(kāi)始?jí)嚎s

     7、壓縮完畢

     8、釋放資源

#include <string.h>
#include <android/bitmap.h>
#include <android/log.h>
#include <jni.h>
#include <stdio.h>
#include <setjmp.h>
#include <math.h>
#include <stdint.h>
#include <time.h>
#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對(duì)象分配空間并初始化
 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è)定參數(shù),包括圖像大小,顏色空間
 jpeg_set_quality(&jcs, quality, true);
 //開(kāi)始?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];
  //寫(xiě)入數(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信息,并存儲(chǔ)值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);
 //進(jìn)行壓縮
 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)

關(guān)于怎么在Android應(yīng)用中利用Bitmap對(duì)圖片進(jìn)行優(yōu)化就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。

網(wǎng)站題目:怎么在Android應(yīng)用中利用Bitmap對(duì)圖片進(jìn)行優(yōu)化
本文URL:http://aaarwkj.com/article0/iggdoo.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供標(biāo)簽優(yōu)化、品牌網(wǎng)站設(shè)計(jì)網(wǎng)站排名、商城網(wǎng)站網(wǎng)站營(yíng)銷(xiāo)、品牌網(wǎng)站制作

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

網(wǎng)站建設(shè)網(wǎng)站維護(hù)公司
另类视频网站在线观看| 亚洲国产精品青青草| 亚洲精品国产精品乱码不卞| 久久re这里只有精品6| 亚洲精品熟女av影院| 国产自拍成人精品视频| 欧美日韩精品一区二区在线| 国产无套内射三级视频| 亚洲熟女av综合网五月| 91免费版在线观看网址| 亚洲美女毛茸茸的逼逼| 少妇精品偷拍高潮少妇在线观看 | 欧美劲爆三级免费观看| 久久精品国产一区二区三区91| 日本久久高清免费观看| 久久精品一本久久99精品| 亚洲六月丁香六月婷婷| 日本91免费在线观看| 精品人妻少妇免费久久蜜臀av| 91久久国产综合精品| 亚洲精品日韩国产av| 欧美内射专区精品一区| 97久久精品亚洲中文字幕| 国产高清毛片区1区二区三区| 欧美一区二区三区四区久久| 久久久亚洲熟妇熟女一区二区| 亚洲黄色录像特级生活片| 欧美日本一区二区四区| 国产超码片内射在线| 91成人大片在线观看| 日本国产美女精品一区二区| 日本特黄高清免费大片| 欧美亚洲综合另类色妞| 草嫩av一区二区三区| 免费av在线网址网站| 欧美黄片视频在线免费看| 青青青在线视频观看华人| 丁香婷婷麻豆激情综合网| 亚洲国产成人精品福利| 亚洲午夜福利天堂社区| 国产精品白浆大屁股一区二区三|