当前位置:   article > 正文

Android 超清大尺寸图片压缩转Base64中卡顿/速度优化问题整理(在子线程压缩Bitmap卡的主线程进度条走不动了。。。)_android drawbitmap bitmap过大导致慢

android drawbitmap bitmap过大导致慢

        最近遇到需求是前后端传输图片使用的是Base64,但是前端(Android 端)图片很大(尺寸很大4480 × 2520,质量也很大7-10M),需要压缩到一定尺寸(1280 × 960,当然还可以压得更小),然后传给后端,本来认为是一个简单的压缩,可是在子线程压缩过程中发现很卡,卡的主线程的进度条都走不动了,一开始用的模拟器测试的,以为模拟器性能比较差,可是换成手机测试,还是很卡,卡的主线程的进度条都走不动了。为了解决这个问题,请看以下步骤:

以下代码在项目里,项目地址:https://gitee.com/urasaki/RxJava2AndRetrofit2

1.先申请权限,关于如何申请权限请查看RxPermissions的使用(简单实用)_ErwinNakajima的博客-CSDN博客_rxpermissions

2.添加bitmap管理类。

  1. package com.phone.base64_and_file.manager;
  2. import android.content.Context;
  3. import android.content.res.AssetManager;
  4. import android.graphics.Bitmap;
  5. import android.graphics.BitmapFactory;
  6. import android.graphics.Canvas;
  7. import android.graphics.ImageFormat;
  8. import android.graphics.LinearGradient;
  9. import android.graphics.Matrix;
  10. import android.graphics.Paint;
  11. import android.graphics.PorterDuff;
  12. import android.graphics.PorterDuffXfermode;
  13. import android.graphics.Rect;
  14. import android.graphics.RectF;
  15. import android.graphics.Shader;
  16. import android.graphics.YuvImage;
  17. import android.net.Uri;
  18. import android.text.TextUtils;
  19. import android.util.Base64;
  20. import android.util.Log;
  21. import com.phone.common_library.manager.LogManager;
  22. import java.io.BufferedInputStream;
  23. import java.io.BufferedOutputStream;
  24. import java.io.ByteArrayInputStream;
  25. import java.io.ByteArrayOutputStream;
  26. import java.io.File;
  27. import java.io.FileInputStream;
  28. import java.io.FileNotFoundException;
  29. import java.io.FileOutputStream;
  30. import java.io.IOException;
  31. import java.io.InputStream;
  32. import java.lang.ref.SoftReference;
  33. import java.nio.ByteBuffer;
  34. import java.text.DecimalFormat;
  35. import id.zelory.compressor.Compressor;
  36. public class BitmapManager {
  37. private static final String TAG = "BitmapManager";
  38. /**
  39. * 图像的旋转方向是0
  40. */
  41. public static final int ROTATE0 = 0;
  42. /**
  43. * 图像的旋转方向是90
  44. */
  45. public static final int ROTATE90 = 90;
  46. /**
  47. * 图像的旋转方向是180
  48. */
  49. public static final int ROTATE180 = 180;
  50. /**
  51. * 图像的旋转方向是270
  52. */
  53. public static final int ROTATE270 = 270;
  54. /**
  55. * 图像的旋转方向是360
  56. */
  57. public static final int ROTATE360 = 360;
  58. /**
  59. * 图片太大内存溢出后压缩的比例
  60. */
  61. public static final int PIC_COMPRESS_SIZE = 4;
  62. /**
  63. * 图像压缩边界
  64. */
  65. public static final int IMAGEBOUND = 128;
  66. /**
  67. * 图片显示最大边的像素
  68. */
  69. public static final int MAXLENTH = 1024;
  70. /**
  71. * Log switch
  72. */
  73. private static final boolean DEBUG = false;
  74. /**
  75. * 保存图片的质量:100
  76. */
  77. private static final int QUALITY = 100;
  78. /**
  79. * 默认的最大尺寸
  80. */
  81. private static final int DEFAULT_MAX_SIZE_CELL_NETWORK = 600;
  82. /**
  83. * 题编辑wifi环境下压缩的最大尺寸
  84. */
  85. private static final int QUESTION_MAX_SIZE_CELL_NETWORK = 1024;
  86. /**
  87. * 图片压缩的质量
  88. */
  89. private static final int QUESTION_IMAGE_JPG_QUALITY = 75;
  90. /**
  91. * 默认的图片压缩的质量
  92. */
  93. private static final int DEFAULT_IMAGE_JPG_QUALITY = 50;
  94. /**
  95. * 网络请求超时时间
  96. */
  97. private static final int CONNECTTIMEOUT = 3000;
  98. /**
  99. * Private constructor to prohibit nonsense instance creation.
  100. */
  101. private BitmapManager() {
  102. }
  103. /**
  104. * 图片合成
  105. *
  106. * @param bitmap 位图1
  107. * @param mark 位图2
  108. * @return Bitmap
  109. */
  110. public static Bitmap createBitmap(Bitmap bitmap, Bitmap mark) {
  111. int w = bitmap.getWidth();
  112. int h = bitmap.getHeight();
  113. int mW = mark.getWidth();
  114. int mH = mark.getHeight();
  115. Bitmap newbitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);// 创建一个长宽一样的位图
  116. Canvas cv = new Canvas(newbitmap);
  117. cv.drawBitmap(bitmap, 0, 0, null);//00坐标开始画入bitmap
  118. cv.drawBitmap(mark, w - mW, h - mH, null);// 在右下角画入水印mark
  119. cv.save();// 保存
  120. cv.restore();// 存储
  121. return newbitmap;
  122. }
  123. /**
  124. * 放大缩小图片
  125. *
  126. * @param bitmap 位图
  127. * @param w 新的宽度
  128. * @param h 新的高度
  129. * @return Bitmap
  130. */
  131. public static Bitmap zoomBitmap(Bitmap bitmap, int w, int h) {
  132. int width = bitmap.getWidth();
  133. int height = bitmap.getHeight();
  134. Matrix matrix = new Matrix();
  135. float scaleWidht = ((float) w / width);
  136. float scaleHeight = ((float) h / height);
  137. matrix.postScale(scaleWidht, scaleHeight);
  138. return Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
  139. }
  140. /**
  141. * 旋转图片
  142. *
  143. * @param bitmap 要旋转的图片
  144. * @param angle 旋转角度
  145. * @return bitmap
  146. */
  147. public static Bitmap rotate(Bitmap bitmap, int angle) {
  148. Matrix matrix = new Matrix();
  149. matrix.postRotate(angle);
  150. return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
  151. bitmap.getHeight(), matrix, true);
  152. }
  153. /**
  154. * 圆形图片
  155. *
  156. * @param source 位图
  157. * @param strokeWidth 裁剪范围 0表示最大
  158. * @param bl 是否需要描边
  159. * @param bl 画笔粗细
  160. * @param bl 颜色代码
  161. * @return bitmap
  162. */
  163. public static Bitmap createCircleBitmap(Bitmap source, int strokeWidth, boolean bl, int edge, int color) {
  164. int diameter = source.getWidth() < source.getHeight() ? source.getWidth() : source.getHeight();
  165. Bitmap target = Bitmap.createBitmap(diameter, diameter, Bitmap.Config.ARGB_8888);
  166. Canvas canvas = new Canvas(target);//创建画布
  167. Paint paint = new Paint();
  168. paint.setAntiAlias(true);
  169. canvas.drawCircle(diameter / 2, diameter / 2, diameter / 2, paint);//绘制圆形
  170. paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));//取相交裁剪
  171. canvas.drawBitmap(source, strokeWidth, strokeWidth, paint);
  172. if (bl) {
  173. if (color == 0) color = 0xFFFEA248;//默认橘黄色
  174. paint.setColor(color);
  175. paint.setStyle(Paint.Style.STROKE);//描边
  176. paint.setStrokeWidth(edge);
  177. canvas.drawCircle(diameter / 2, diameter / 2, diameter / 2, paint);
  178. }
  179. return target;
  180. }
  181. /**
  182. * 圆角图片
  183. *
  184. * @param bitmap 位图
  185. * @param rx x方向上的圆角半径
  186. * @param ry y方向上的圆角半径
  187. * @param bl 是否需要描边
  188. * @param bl 画笔粗细
  189. * @param bl 颜色代码
  190. * @return bitmap
  191. */
  192. public static Bitmap createCornerBitmap(Bitmap bitmap, int rx, int ry, boolean bl, int edge, int color) {
  193. Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
  194. Canvas canvas = new Canvas(output);//创建画布
  195. Paint paint = new Paint();
  196. paint.setAntiAlias(true);
  197. Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
  198. RectF rectF = new RectF(rect);
  199. canvas.drawRoundRect(rectF, rx, ry, paint);//绘制圆角矩形
  200. paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));//取相交裁剪
  201. canvas.drawBitmap(bitmap, rect, rect, paint);
  202. if (bl) {
  203. if (color == 0) color = 0xFFFEA248;//默认橘黄色
  204. paint.setColor(color);
  205. paint.setColor(color);
  206. paint.setStyle(Paint.Style.STROKE);//描边
  207. paint.setStrokeWidth(edge);
  208. canvas.drawRoundRect(rectF, rx, ry, paint);
  209. }
  210. return output;
  211. }
  212. /**
  213. * 按比例裁剪图片
  214. *
  215. * @param bitmap 位图
  216. * @param wScale 裁剪宽 0~100%
  217. * @param hScale 裁剪高 0~100%
  218. * @return bitmap
  219. */
  220. public static Bitmap cropBitmap(Bitmap bitmap, float wScale, float hScale) {
  221. int w = bitmap.getWidth();
  222. int h = bitmap.getHeight();
  223. int wh = (int) (w * wScale);
  224. int hw = (int) (h * hScale);
  225. int retX = (int) (w * (1 - wScale) / 2);
  226. int retY = (int) (h * (1 - hScale) / 2);
  227. return Bitmap.createBitmap(bitmap, retX, retY, wh, hw, null, false);
  228. }
  229. /**
  230. * 获得带倒影的图片方法
  231. *
  232. * @param bitmap 位图
  233. * @param region 倒影区域 0.1~1
  234. * @return bitmap
  235. */
  236. public static Bitmap createReflectionBitmap(Bitmap bitmap, float region) {
  237. int width = bitmap.getWidth();
  238. int height = bitmap.getHeight();
  239. Matrix matrix = new Matrix();
  240. matrix.preScale(1, -1);//镜像缩放
  241. Bitmap reflectionBitmap = Bitmap.createBitmap(
  242. bitmap, 0
  243. , (int) (height * (1 - region))//从哪个点开始绘制
  244. , width
  245. , (int) (height * region)//绘制多高
  246. , matrix, false);
  247. Bitmap reflectionWithBitmap = Bitmap.createBitmap(width, height + (int) (height * region),
  248. Bitmap.Config.ARGB_8888);
  249. Canvas canvas = new Canvas(reflectionWithBitmap);
  250. canvas.drawBitmap(bitmap, 0, 0, null);
  251. canvas.drawBitmap(reflectionBitmap, 0, height, null);
  252. LinearGradient shader = new LinearGradient(0, bitmap.getHeight(), 0,
  253. reflectionWithBitmap.getHeight()
  254. , 0x70ffffff, 0x00ffffff, Shader.TileMode.CLAMP);
  255. Paint paint = new Paint();
  256. paint.setShader(shader);
  257. paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));//取两层绘制交集。显示下层。
  258. canvas.drawRect(0, height, width, reflectionWithBitmap.getHeight(), paint);
  259. return reflectionWithBitmap;
  260. }
  261. /**
  262. * 图片质量压缩
  263. *
  264. * @param bitmap
  265. * @param many 百分比
  266. * @return
  267. */
  268. public static Bitmap compressBitmap(Bitmap bitmap, float many) {
  269. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  270. bitmap.compress(Bitmap.CompressFormat.JPEG, (int) many * 100, baos);
  271. ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());
  272. return BitmapFactory.decodeStream(isBm, null, null);
  273. }
  274. /**
  275. * 高级图片质量压缩
  276. *
  277. * @param bitmap 位图
  278. * @param maxSize 压缩后的大小,单位kb
  279. */
  280. public static Bitmap imageZoom(Bitmap bitmap, double maxSize) {
  281. // 将bitmap放至数组中,意在获得bitmap的大小(与实际读取的原文件要大)
  282. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  283. // 格式、质量、输出流
  284. bitmap.compress(Bitmap.CompressFormat.PNG, 70, baos);
  285. byte[] b = baos.toByteArray();
  286. // 将字节换成KB
  287. double mid = b.length / 1024;
  288. // 获取bitmap大小 是允许最大大小的多少倍
  289. double i = mid / maxSize;
  290. // 判断bitmap占用空间是否大于允许最大空间 如果大于则压缩 小于则不压缩
  291. doRecycledIfNot(bitmap);
  292. if (i > 1) {
  293. // 缩放图片 此处用到平方根 将宽带和高度压缩掉对应的平方根倍
  294. // (保持宽高不变,缩放后也达到了最大占用空间的大小)
  295. return scaleWithWH(bitmap, bitmap.getWidth() / Math.sqrt(i),
  296. bitmap.getHeight() / Math.sqrt(i));
  297. }
  298. return null;
  299. }
  300. /***
  301. * 图片缩放
  302. *@param bitmap 位图
  303. * @param w 新的宽度
  304. * @param h 新的高度
  305. * @return Bitmap
  306. */
  307. public static Bitmap scaleWithWH(Bitmap bitmap, double w, double h) {
  308. if (w == 0 || h == 0 || bitmap == null) {
  309. return bitmap;
  310. } else {
  311. int width = bitmap.getWidth();
  312. int height = bitmap.getHeight();
  313. Matrix matrix = new Matrix();
  314. float scaleWidth = (float) (w / width);
  315. float scaleHeight = (float) (h / height);
  316. matrix.postScale(scaleWidth, scaleHeight);
  317. return Bitmap.createBitmap(bitmap, 0, 0, width, height,
  318. matrix, true);
  319. }
  320. }
  321. /**
  322. * YUV视频流格式转bitmap
  323. *
  324. * @param data YUV视频流格式
  325. * @return width 设置高度
  326. */
  327. public static Bitmap getBitmap(byte[] data, int width, int height) {
  328. Bitmap bitmap;
  329. YuvImage yuvimage = new YuvImage(data, ImageFormat.NV21, width, height, null);
  330. //data是onPreviewFrame参数提供
  331. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  332. yuvimage.compressToJpeg(new Rect(0, 0, yuvimage.getWidth(), yuvimage.getHeight()), 100, baos);//
  333. // 80--JPG图片的质量[0-100],100最高
  334. byte[] rawImage = baos.toByteArray();
  335. BitmapFactory.Options options = new BitmapFactory.Options();
  336. SoftReference<Bitmap> softRef = new SoftReference<Bitmap>(BitmapFactory.decodeByteArray(rawImage, 0, rawImage
  337. .length, options));
  338. bitmap = softRef.get();
  339. return bitmap;
  340. }
  341. /**
  342. * 图片资源文件转bitmap
  343. *
  344. * @param context
  345. * @param resId
  346. * @return bitmap
  347. */
  348. public static Bitmap getBitmapResources(Context context, int resId) {
  349. return BitmapFactory.decodeResource(context.getResources(), resId);
  350. }
  351. /**
  352. * 将图片路径转Bitmap
  353. *
  354. * @return Bitmap
  355. * @Param path 图片路径
  356. */
  357. public static Bitmap getBitmapPath(String path) {
  358. return BitmapFactory.decodeFile(path);
  359. }
  360. /**
  361. * bitmap保存到指定路径
  362. *
  363. * @param path 图片的绝对路径
  364. * @param bmp 位图
  365. * @return bitmap
  366. */
  367. public static boolean saveFile(String path, Bitmap bmp) {
  368. if (TextUtils.isEmpty(path) || bmp == null) return false;
  369. File file = new File(path);
  370. if (file.exists()) {
  371. file.delete();
  372. } else {
  373. File p = file.getParentFile();
  374. if (!p.exists()) {
  375. p.mkdirs();
  376. }
  377. try {
  378. file.createNewFile();
  379. } catch (IOException e) {
  380. e.printStackTrace();
  381. }
  382. }
  383. FileOutputStream fos = null;
  384. BufferedOutputStream bos = null;
  385. try {
  386. fos = new FileOutputStream(file);
  387. bos = new BufferedOutputStream(fos);
  388. bmp.compress(Bitmap.CompressFormat.JPEG, 100, bos);
  389. bos.flush();
  390. } catch (IOException e) {
  391. // TODO Auto-generated catch block
  392. e.printStackTrace();
  393. } finally {
  394. if (bos != null) {
  395. try {
  396. bos.close();
  397. } catch (IOException e) {
  398. e.printStackTrace();
  399. }
  400. }
  401. }
  402. return true;
  403. }
  404. /**
  405. * 回收一个未被回收的Bitmap
  406. *
  407. * @param bitmap
  408. */
  409. public static void doRecycledIfNot(Bitmap bitmap) {
  410. if (!bitmap.isRecycled()) {
  411. bitmap.recycle();
  412. }
  413. }
  414. /**
  415. * 等比压缩图片
  416. *
  417. * @param resBitmap 原图
  418. * @param desWidth 压缩后图片的宽度
  419. * @param desHeight 压缩后图片的高度
  420. * @return 压缩后的图片
  421. */
  422. public static Bitmap scaleImage(Bitmap resBitmap, int desWidth, int desHeight) {
  423. int resWidth = resBitmap.getWidth();
  424. int resHeight = resBitmap.getHeight();
  425. if (resHeight > desHeight || resWidth > desWidth) {
  426. // 计算出实际宽高和目标宽高的比率
  427. final float heightRatio = (float) desHeight / (float) resHeight;
  428. final float widthRatio = (float) desWidth / (float) resWidth;
  429. float scale = heightRatio < widthRatio ? heightRatio : widthRatio;
  430. LogManager.i(TAG, "scaleImage scale*****" + scale);
  431. //開平方會有一點損失精度
  432. float prescription = (float) Math.sqrt(scale);
  433. //再次开平方
  434. float prescription2 = (float) Math.sqrt(prescription);
  435. // LogManager.i(TAG, "scaleImage prescription*****" + prescription);
  436. LogManager.i(TAG, "scaleImage prescription2*****" + prescription2);
  437. Bitmap bitmap = null;
  438. //循環壓縮bitmap,防止一次壓縮bitmap卡頓問題(子線程把主線成卡頓了)
  439. for (int i = 0; i < 4; i++) {
  440. if (bitmap != null) {
  441. // bitmap = scale(bitmap, prescription);
  442. bitmap = scale(bitmap, prescription2);
  443. } else {
  444. // bitmap = scale(resBitmap, prescription);
  445. bitmap = scale(resBitmap, prescription2);
  446. }
  447. // LogManager.i(TAG, i + "scaleImage prescription*****" + prescription);
  448. LogManager.i(TAG, i + "scaleImage prescription2*****" + prescription2);
  449. }
  450. return bitmap;
  451. }
  452. return resBitmap;
  453. }
  454. /**
  455. * 等比压缩图片
  456. *
  457. * @param bitmap 原图
  458. * @param scale 压缩因子
  459. * @return 压缩后的图片
  460. */
  461. private static Bitmap scale(Bitmap bitmap, float scale) {
  462. Matrix matrix = new Matrix();
  463. matrix.postScale(scale, scale);
  464. return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
  465. }
  466. /**
  467. * 尺寸缩放
  468. *
  469. * @param bitmap bitmap
  470. * @param w width
  471. * @param h height
  472. * @return scaleBitmap
  473. */
  474. public static Bitmap scale(Bitmap bitmap, int w, int h) {
  475. if (bitmap == null) {
  476. return null;
  477. }
  478. int width = bitmap.getWidth();
  479. int height = bitmap.getHeight();
  480. Matrix matrix = new Matrix();
  481. float scaleWidth = ((float) w / width);
  482. float scaleHeight = ((float) h / height);
  483. matrix.postScale(scaleWidth, scaleHeight);
  484. return Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
  485. }
  486. /**
  487. * byte转Uri
  488. *
  489. * @param context
  490. * @param data
  491. * @return
  492. */
  493. public static Uri saveTakePictureImage(Context context, byte[] data) {
  494. File file = context.getExternalFilesDir("file1");
  495. file = new File(file.getAbsolutePath() + File.separator + System.currentTimeMillis() + ".jpg");
  496. FileOutputStream fos = null;
  497. try {
  498. fos = new FileOutputStream(file);
  499. fos.write(data);
  500. fos.flush();
  501. } catch (Exception e) {
  502. e.printStackTrace();
  503. // 异常时删除保存失败的文件
  504. try {
  505. if (file != null && file.exists() && file.isFile()) {
  506. file.delete();
  507. }
  508. } catch (Exception ex) {
  509. ex.printStackTrace();
  510. }
  511. return null;
  512. } finally {
  513. if (fos != null) {
  514. try {
  515. fos.close();
  516. } catch (IOException e) {
  517. e.printStackTrace();
  518. }
  519. }
  520. }
  521. Log.e(TAG, file.getAbsolutePath());
  522. return Uri.fromFile(file);
  523. }
  524. /**
  525. * byte转成bitmap
  526. *
  527. * @param data
  528. * @param width
  529. * @param height
  530. * @return
  531. */
  532. public static Bitmap yuvToBitmap(byte[] data, int width, int height) {
  533. int frameSize = width * height;
  534. int[] rgba = new int[frameSize];
  535. for (int i = 0; i < height; i++) {
  536. for (int j = 0; j < width; j++) {
  537. int y = (0xff & ((int) data[i * width + j]));
  538. int u = (0xff & ((int) data[frameSize + (i >> 1) * width + (j & ~1) + 0]));
  539. int v = (0xff & ((int) data[frameSize + (i >> 1) * width + (j & ~1) + 1]));
  540. y = y < 16 ? 16 : y;
  541. int r = Math.round(1.164f * (y - 16) + 1.596f * (v - 128));
  542. int g = Math.round(1.164f * (y - 16) - 0.813f * (v - 128) - 0.391f * (u - 128));
  543. int b = Math.round(1.164f * (y - 16) + 2.018f * (u - 128));
  544. r = r < 0 ? 0 : (r > 255 ? 255 : r);
  545. g = g < 0 ? 0 : (g > 255 ? 255 : g);
  546. b = b < 0 ? 0 : (b > 255 ? 255 : b);
  547. rgba[i * width + j] = 0xff000000 + (b << 16) + (g << 8) + r;
  548. }
  549. }
  550. Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
  551. bmp.setPixels(rgba, 0, width, 0, 0, width, height);
  552. return bmp;
  553. }
  554. /**
  555. * byte转成bitmap
  556. *
  557. * @param depthBytes
  558. * @param width
  559. * @param height
  560. * @return
  561. */
  562. public static Bitmap depthToBitmap(byte[] depthBytes, int width, int height) {
  563. Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
  564. int[] argbData = new int[width * height];
  565. for (int i = 0; i < width * height; i++) {
  566. argbData[i] = (((int) depthBytes[i * 2] + depthBytes[i * 2 + 1] * 256) / 10 & 0x000000ff)
  567. | ((((int) depthBytes[i * 2] + depthBytes[i * 2 + 1] * 256) / 10) & 0x000000ff) << 8
  568. | ((((int) depthBytes[i * 2] + depthBytes[i * 2 + 1] * 256) / 10) & 0x000000ff) << 16
  569. | 0xff000000;
  570. }
  571. bitmap.setPixels(argbData, 0, width, 0, 0, width, height);
  572. return bitmap;
  573. }
  574. /**
  575. * byte转bitmap
  576. *
  577. * @param bytes
  578. * @param width
  579. * @param height
  580. * @return
  581. */
  582. public static Bitmap rGBToBitmap(byte[] bytes, int width, int height) {
  583. // use Bitmap.Config.ARGB_8888 instead of type is OK
  584. Bitmap stitchBmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
  585. byte[] rgba = new byte[width * height * 4];
  586. for (int i = 0; i < width * height; i++) {
  587. byte b1 = bytes[i * 3 + 0];
  588. byte b2 = bytes[i * 3 + 1];
  589. byte b3 = bytes[i * 3 + 2];
  590. // set value
  591. rgba[i * 4 + 0] = b1;
  592. rgba[i * 4 + 1] = b2;
  593. rgba[i * 4 + 2] = b3;
  594. rgba[i * 4 + 3] = (byte) 255;
  595. }
  596. stitchBmp.copyPixelsFromBuffer(ByteBuffer.wrap(rgba));
  597. return stitchBmp;
  598. }
  599. /**
  600. * byte转bitmap
  601. *
  602. * @param bytes
  603. * @param width
  604. * @param height
  605. * @return
  606. */
  607. public static Bitmap bGRToBitmap(byte[] bytes, int width, int height) {
  608. // use Bitmap.Config.ARGB_8888 instead of type is OK
  609. Bitmap stitchBmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
  610. byte[] rgba = new byte[width * height * 4];
  611. for (int i = 0; i < width * height; i++) {
  612. byte b1 = bytes[i * 3 + 0];
  613. byte b2 = bytes[i * 3 + 1];
  614. byte b3 = bytes[i * 3 + 2];
  615. // set value
  616. rgba[i * 4 + 0] = b3;
  617. rgba[i * 4 + 1] = b2;
  618. rgba[i * 4 + 2] = b1;
  619. rgba[i * 4 + 3] = (byte) 255;
  620. }
  621. stitchBmp.copyPixelsFromBuffer(ByteBuffer.wrap(rgba));
  622. return stitchBmp;
  623. }
  624. public static byte[] aRGBToR(byte[] bytes, int width, int height) {
  625. byte[] IR = new byte[width * height];
  626. for (int i = 0; i < width * height; i++) {
  627. IR[i] = bytes[i * 4];
  628. }
  629. return IR;
  630. }
  631. // /**
  632. // * 百度人脸图片转换
  633. // *
  634. // * @param newInstance
  635. // * @return
  636. // */
  637. // public static Bitmap getInstaceBmp(BDFaceImageInstance newInstance) {
  638. // Bitmap transBmp = null;
  639. // if (newInstance.imageType == BDFaceSDKCommon.BDFaceImageType.BDFACE_IMAGE_TYPE_RGBA) {
  640. // transBmp = Bitmap.createBitmap(newInstance.width, newInstance.height, Bitmap.Config.ARGB_8888);
  641. // transBmp.copyPixelsFromBuffer(ByteBuffer.wrap(newInstance.data));
  642. // } else if (newInstance.imageType == BDFaceSDKCommon.BDFaceImageType.BDFACE_IMAGE_TYPE_BGR) {
  643. // transBmp = BitmapManager.BGR2Bitmap(newInstance.data, newInstance.width, newInstance.height);
  644. // } else if (newInstance.imageType == BDFaceSDKCommon.BDFaceImageType.BDFACE_IMAGE_TYPE_YUV_NV21) {
  645. // transBmp = BitmapManager.yuv2Bitmap(newInstance.data, newInstance.width, newInstance.height);
  646. // } else if (newInstance.imageType == BDFaceSDKCommon.BDFaceImageType.BDFACE_IMAGE_TYPE_GRAY) {
  647. // transBmp = Depth2Bitmap(newInstance.data, newInstance.width, newInstance.height);
  648. // }
  649. // return transBmp;
  650. // }
  651. /**
  652. * 获取图片数据
  653. *
  654. * @param path
  655. * @return
  656. */
  657. public static Bitmap getBitmap(String path) {
  658. FileInputStream fis = null;
  659. BufferedInputStream bis = null;
  660. Bitmap bm = null;
  661. try {
  662. fis = new FileInputStream(path);
  663. BitmapFactory.Options options = new BitmapFactory.Options();
  664. // options.inSampleSize = 8;//图片的长宽都是原来的1/8
  665. bis = new BufferedInputStream(fis);
  666. bm = BitmapFactory.decodeStream(bis, null, options);
  667. } catch (FileNotFoundException e) {
  668. e.printStackTrace();
  669. } finally {
  670. try {
  671. bis.close();
  672. } catch (IOException e) {
  673. e.printStackTrace();
  674. }
  675. }
  676. return bm;
  677. }
  678. /**
  679. * @param bitmap
  680. * @return
  681. */
  682. public static InputStream bitmapToInputStream(Bitmap bitmap) {
  683. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  684. bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
  685. ByteArrayInputStream byteArrInputStream = new ByteArrayInputStream(
  686. baos.toByteArray());
  687. return byteArrInputStream;
  688. }
  689. /**
  690. * 这个是把文件变成二进制流
  691. *
  692. * @param imagepath
  693. * @return
  694. * @throws Exception
  695. */
  696. public static byte[] readStream(String imagepath) {
  697. FileInputStream fis = null;
  698. BufferedInputStream bis = null;
  699. ByteArrayOutputStream baos = null;
  700. try {
  701. fis = new FileInputStream(imagepath);
  702. bis = new BufferedInputStream(fis);
  703. baos = new ByteArrayOutputStream();
  704. byte[] buffer = new byte[1024];
  705. int len = 0;
  706. while (-1 != (len = bis.read(buffer))) {
  707. baos.write(buffer, 0, len);
  708. }
  709. //刷新此输出流并强制写出所有缓冲的输出字节
  710. baos.flush();
  711. } catch (IOException e) {
  712. e.printStackTrace();
  713. } finally {
  714. try {
  715. if (bis != null) {
  716. bis.close();
  717. }
  718. if (baos != null) {
  719. baos.close();
  720. }
  721. } catch (IOException e) {
  722. e.printStackTrace();
  723. }
  724. }
  725. return baos.toByteArray();
  726. }
  727. /**
  728. * 将Bitmap转换成文件
  729. * 保存文件
  730. *
  731. * @param bitmap
  732. * @param fileName
  733. * @throws IOException
  734. */
  735. public static File saveFile(Bitmap bitmap, String dirsPath, String fileName) {
  736. File dirs = new File(dirsPath);
  737. if (!dirs.exists()) {
  738. dirs.mkdirs();
  739. }
  740. File captureFile = new File(dirs, fileName);
  741. BufferedOutputStream bos = null;
  742. try {
  743. bos = new BufferedOutputStream(new FileOutputStream(captureFile));
  744. //刷新此输出流并强制写出所有缓冲的输出字节
  745. bos.flush();
  746. bitmap.compress(Bitmap.CompressFormat.JPEG, 80, bos);
  747. } catch (IOException e) {
  748. e.printStackTrace();
  749. } finally {
  750. if (bos != null) {
  751. try {
  752. bos.close();
  753. } catch (IOException e) {
  754. e.printStackTrace();
  755. }
  756. }
  757. }
  758. return captureFile;
  759. }
  760. /**
  761. * 读取文件的大小
  762. */
  763. public static long getFileSize(File file) {
  764. long size = 0;
  765. if (file.exists()) {
  766. FileInputStream fis = null;
  767. BufferedInputStream bis = null;
  768. try {
  769. fis = new FileInputStream(file);
  770. bis = new BufferedInputStream(fis);
  771. size = bis.available();
  772. } catch (IOException e) {
  773. e.printStackTrace();
  774. } finally {
  775. if (bis != null) {
  776. try {
  777. bis.close();
  778. } catch (IOException e) {
  779. e.printStackTrace();
  780. }
  781. }
  782. }
  783. return size;
  784. }
  785. return 0;
  786. }
  787. /**
  788. * 返回byte的数据大小对应的文本
  789. *
  790. * @param size
  791. * @return
  792. */
  793. public static String getDataSize(long size) {
  794. DecimalFormat formater = new DecimalFormat("####.00");
  795. if (size < 1024) {
  796. return size + "bytes";
  797. } else if (size < 1024 * 1024) {
  798. float kbsize = size / 1024f;
  799. return formater.format(kbsize) + "KB";
  800. } else if (size < 1024 * 1024 * 1024) {
  801. float mbsize = size / 1024f / 1024f;
  802. return formater.format(mbsize) + "MB";
  803. } else if (size < 1024 * 1024 * 1024 * 1024) {
  804. float gbsize = size / 1024f / 1024f / 1024f;
  805. return formater.format(gbsize) + "GB";
  806. } else {
  807. return "size: error";
  808. }
  809. }
  810. /**
  811. * 使用Compressor IO模式自定义压缩
  812. *
  813. * @param path .setMaxWidth(640).setMaxHeight(480)这两个数值越高,压缩力度越小,图片也不清晰
  814. * .setQuality(75)这个方法只是设置图片质量,并不影响压缩图片的大小KB
  815. * .setCompressFormat(Bitmap.CompressFormat.WEBP) WEBP图片格式是Google推出的 压缩强,质量 高,但是IOS不识别,需要把图片转为字节流然后转PNG格式
  816. * .setCompressFormat(Bitmap.CompressFormat.PNG)PNG格式的压缩,会导致图片变大,并耗过大的内 存,手机反应缓慢
  817. * .setCompressFormat(Bitmap.CompressFormat.JPEG)JPEG压缩;压缩速度比PNG快,质量一般,基本上属于1/10的压缩比例
  818. */
  819. public static File initCompressorIO(Context context, String path, String dirsPath) {
  820. File dirs2 = new File(dirsPath);
  821. if (!dirs2.exists()) {
  822. dirs2.mkdirs();
  823. }
  824. File file = null;
  825. try {
  826. file = new Compressor(context)
  827. // .setMaxWidth(1280)
  828. // .setMaxHeight(960)
  829. .setQuality(50)
  830. .setCompressFormat(Bitmap.CompressFormat.PNG)
  831. .setDestinationDirectoryPath(dirsPath)
  832. .compressToFile(new File(path));
  833. } catch (IOException e) {
  834. e.printStackTrace();
  835. }
  836. return file;
  837. }
  838. public static File getAssetFile(Context context, String dirsPath, String assetFileName) {
  839. AssetManager assetManager = context.getAssets();
  840. File dirs = new File(dirsPath);
  841. if (!dirs.exists()) {
  842. dirs.mkdirs();
  843. }
  844. File file = new File(dirs, assetFileName);
  845. if (!file.exists()) {
  846. try {
  847. file.createNewFile();
  848. } catch (IOException e) {
  849. e.printStackTrace();
  850. }
  851. }
  852. InputStream is = null;
  853. BufferedInputStream bis = null;
  854. FileOutputStream fos = null;
  855. BufferedOutputStream bos = null;
  856. try {
  857. is = assetManager.open(assetFileName);
  858. bis = new BufferedInputStream(is);
  859. fos = new FileOutputStream(file);
  860. bos = new BufferedOutputStream(fos);
  861. byte[] buffer = new byte[1024];
  862. int len = 0;
  863. while (-1 != (len = bis.read(buffer))) {
  864. bos.write(buffer, 0, len);
  865. }
  866. //刷新此输出流并强制写出所有缓冲的输出字节
  867. bos.flush();
  868. } catch (IOException e) {
  869. e.printStackTrace();
  870. } finally {
  871. if (bis != null) {
  872. try {
  873. bis.close();
  874. } catch (IOException e) {
  875. e.printStackTrace();
  876. }
  877. }
  878. if (bos != null) {
  879. try {
  880. bos.close();
  881. } catch (IOException e) {
  882. e.printStackTrace();
  883. }
  884. }
  885. }
  886. return file;
  887. }
  888. /**
  889. * base64转bitmap
  890. *
  891. * @param base64String
  892. * @return
  893. */
  894. public static Bitmap base64ToBitmap(String base64String) {
  895. base64String = Uri.decode(base64String);
  896. byte[] decode = Base64.decode(base64String, Base64.DEFAULT);
  897. Bitmap bitmap = BitmapFactory.decodeByteArray(decode, 0, decode.length);
  898. return bitmap;
  899. }
  900. }

3.把本地图片转化成bitmap。

  1. String dirsPath = Environment.getExternalStorageDirectory().getAbsolutePath()
  2. + File.separator + "Pictures";
  3. String dirsPathCompressed = Environment.getExternalStorageDirectory().getAbsolutePath()
  4. + File.separator + "PicturesCompressed";
  5. String dirsPathCompressedRecover = Environment.getExternalStorageDirectory().getAbsolutePath()
  6. + File.separator + "PicturesCompressedRecover";
  7. Base64AndFileBean base64AndFileBean = new Base64AndFileBean();
  8. base64AndFileBean.setDirsPath(dirsPath);
  9. base64AndFileBean.setDirsPathCompressed(dirsPathCompressed);
  10. base64AndFileBean.setDirsPathCompressedRecover(dirsPathCompressedRecover);
  11. Bitmap bitmap = BitmapManager.getBitmap(base64AndFileBean.getFile().getAbsolutePath());
  12. LogManager.i(TAG, "bitmap mWidth*****" + bitmap.getWidth());
  13. LogManager.i(TAG, "bitmap mHeight*****" + bitmap.getHeight());
  14. base64AndFileBean.setBitmap(bitmap);

4.再压缩bitmap,循環壓縮bitmap(分成4次压缩了),防止一次壓縮bitmap卡頓問題,如果图片太大,就分成更多次压缩。

  1. Bitmap bitmapCompressed = BitmapManager.scaleImage(base64AndFileBean.getBitmap(), 1280, 960);
  2. LogManager.i(TAG, "bitmapCompressed mWidth*****" + bitmapCompressed.getWidth());
  3. LogManager.i(TAG, "bitmapCompressed mHeight*****" + bitmapCompressed.getHeight());
  4. base64AndFileBean.setBitmapCompressed(bitmapCompressed);

5.再把压缩后的bitmap保存到本地。

  1. File fileCompressed = BitmapManager.saveFile(base64AndFileBean.getBitmapCompressed(), base64AndFileBean.getDirsPathCompressed(), "picture_large_compressed.png");
  2. base64AndFileBean.setFileCompressed(fileCompressed);
  3. LogManager.i(TAG, "base64AndFileBean.getFileCompressed().getPath()*****" + base64AndFileBean.getFileCompressed().getPath());
  4. LogManager.i(TAG, "base64AndFileBean.getFileCompressed().getAbsolutePath()*****" + base64AndFileBean.getFileCompressed().getAbsolutePath());

6.最后把File转成Base64。

  1. String base64Str = null;
  2. if (base64AndFileBean.getFileCompressed().exists()) {
  3. base64Str = Base64AndFileManager.fileToBase64(base64AndFileBean.getFileCompressed());
  4. // base64Str = Base64AndFileManager.fileToBase64Test(file);
  5. // base64Str = Base64AndFileManager.fileToBase64Second(file);
  6. base64AndFileBean.setBase64Str(base64Str);
  7. }
  8. if (!TextUtils.isEmpty(base64Str)) {
  9. String fileName = "base64Str.txt";
  10. String txtFilePath = FileManager.writeStrToTextFile(base64Str, base64AndFileBean.getDirsPathCompressed(), fileName);
  11. base64AndFileBean.setTxtFilePath(txtFilePath);
  12. base64AndFileBean.setBase64StrList(Base64AndFileManager.getBase64StrList(txtFilePath));
  13. // base64Str = "";
  14. // StringBuilder stringBuilder = new StringBuilder();
  15. // for (int i = 0; i < base64StrList.size(); i++) {
  16. // stringBuilder.append(base64StrList.get(i));
  17. // }
  18. // base64Str = stringBuilder.toString();
  19. // //把字符串的最後一個打印出來,然後看看和RecyclerView顯示的最後一個字符串是否一致
  20. // LogManager.i(TAG, "base64StrList******" + base64StrList.get(base64StrList.size() - 1));
  21. }

以上是我总结的超清大尺寸图片压缩转Base64速度优化问题,重点在第3点,循環壓縮bitmap(分成4次压缩了),防止一次壓縮bitmap卡頓問題,如果图片太大,就分成更多次压缩。

如对此有疑问,请联系qq1164688204。

推荐Android开源项目

项目功能介绍:RxJava2和Retrofit2项目,添加自动管理token功能,添加RxJava2生命周期管理,使用App架构设计是MVP模式和MVVM模式,同时使用组件化,部分代码使用Kotlin,此项目持续维护中。

项目地址:https://gitee.com/urasaki/RxJava2AndRetrofit2

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/article/detail/48412
推荐阅读
相关标签
  

闽ICP备14008679号