学习笔记 - 深究Bitmap压缩避免OOM的核心inSampleSize的最佳取值
/** * 测试代码,通过在SDCard根目录放置几种不同大小的图片, 来自动测试压缩方式是否有效同时看是否会内存不够. * * @since * By:AsionTang * At:2014年3月20日 * */ public static final void test() { //常用照片分辨率 final int[][] list = new int[][] { { 15000, 13600 }, { 14000, 12600 }, { 13000, 11600 }, { 12000, 10600 }, { 11000, 9600 }, { 10000, 8600 }, { 9000, 7600 }, { 8000, 6000 }, { 7000, 6000 }, { 8000, 4600 }, { 4080, 2720 }, { 3264, 2488 }, { 3000, 2000 }, { 2560, 1920 }, { 2272, 1704 }, { 2048, 1536 }, { 1600, 1200 }, { 1280, 960 }, { 1024, 768 }, { 800, 600 }, { 640, 480 }, { 480, 320 } }; for (int i = 0; i < list.length; i++) { final int w = list[i][0]; // final int h = list[i][1]; final File f = new File(Environment.getExternalStorageDirectory(), w + ".jpg"); if (!f.exists()) continue; final Bitmap b = getResizedImage(f.getAbsolutePath(), 640); saveBitmapToSd(b, 50, f.getAbsolutePath().replace(".jpg", ".png")); recycleQuietly(b); } } /** * 这里因为使用的是二次Bitmap编码,所以使用prevPowerOf2往小的值取 * * @param w * @param h * @param maxSize * @param saveMemory * 省内存模式:
* true: 计算SampleSize时,往上取值尽可能大.则最终图片最长边 尺寸 * 小于等于maxSize(小概率会大于maxSize,如输入某些特殊尺寸时,见 * {@link #nextPowerOf2(int)})
* false: 计算SampleSize时,往下取值尽可能小.则最终图片最长边 尺寸 * 大于等于maxSize(没见过小于maxSize的情况) * @return */ @SuppressWarnings("unused") private static final int computeSampleSize(final int w, final int h, final double maxSize, final boolean saveMemory) { final int initialSize = (int) Math.max(w / maxSize, h / maxSize); //原来: 这里因为使用的是二次Bitmap编码,所以使用prevPowerOf2往小的值取,这样第一次取到的Bitmap大小肯定大于maxSize //现在: 这里因为使用的是二次Bitmap编码[支持缩小和扩大图片],所以使用 nextPowerOf2 取尽量小的值,肯定[小于]maxSize,这时,再将其[扩大]maxSize即可! // 经过测试test,最终生成的图片: // 小于3000以上的像素原图: 生成的新图文件[几乎等于]使用prevPowerOf2生成的新图片,即两文件MD5值大概率情况下都会相等. // 大于3000以上的像素原图: 生成的新图文件大小略小于使用prevPowerOf2生成的新图片,使用BCompare图片对比容差25时,看不出多少差异. if (saveMemory) return nextPowerOf2(initialSize); return prevPowerOf2(initialSize); } /** * 在不进行二次Bitmap编码大小的情况下,可直接使用此nextPowerOf2获得更高的采样比值,以便缩小为更小的Bitmap * 但是可能不太稳定,如原图为1024x768时,生成的目标尺寸也大的,小的都有. * * * initialSize:23 trueSize 32:15000x13600 468x425 * initialSize:21 trueSize 32:14000x12600 437x393 * initialSize:20 trueSize 32:13000x11600 406x362 * initialSize:18 trueSize 32:12000x10600 375x331 * initialSize:17 trueSize 32:11000x9600 343x300 * initialSize:15 trueSize 16:10000x8600 625x537 * initialSize:14 trueSize 16:9000x7600 562x475 * initialSize:12 trueSize 16:8000x6000 500x375 * initialSize:10 trueSize 16:7000x6000 437x375 * initialSize:12 trueSize 16:8000x4600 500x287 * initialSize:06 trueSize 08:4080x2720 510x340 * initialSize:05 trueSize 08:3264x2488 408x311 * initialSize:04 trueSize 04:3000x2000 750x500 * initialSize:04 trueSize 04:2560x1920 640x480 * initialSize:03 trueSize 04:2272x1704 568x426 * initialSize:03 trueSize 04:2048x1536 512x384 * initialSize:02 trueSize 02:1600x1200 800x600 * initialSize:02 trueSize 02:1280x960 640x480 * initialSize:01 trueSize 01:1024x768 1024x768 * initialSize:01 trueSize 01:800x600 800x600 * initialSize:01 trueSize 01:640x480 640x480 * * * * // Returns the next power of two. * // Returns the input if it is already power of 2. * // Throws IllegalArgumentException if the input is <= 0 or * // the answer overflows. * * * 最终值往大的取(重采样得的图片尺寸越小) * * @param n * @return */ private static final int nextPowerOf2(int n) { if (n <= 0 || n > (1 << 30)) return 1; n -= 1; n |= n >> 16; n |= n >> 8; n |= n >> 4; n |= n >> 2; n |= n >> 1; return n + 1; } /** * 1.这里追求的目标是:尽可能的接近目标MaxSize的大小,可以大于等于MaxSize大小
* 2.第二步再将接近MaxSize大小的Bitmap再次真正编码为边长为MaxSize大小的Bitmap! * * * initialSize:23 trueSize 16:15000x13600 937x850 * initialSize:21 trueSize 16:14000x12600 875x787 * initialSize:20 trueSize 16:13000x11600 812x725 * initialSize:18 trueSize 16:12000x10600 750x662 * initialSize:17 trueSize 16:11000x9600 687x600 * initialSize:15 trueSize 08:10000x8600 1250x1075 * initialSize:14 trueSize 08:9000x7600 1125x950 * initialSize:12 trueSize 08:8000x6000 1000x750 * initialSize:10 trueSize 08:7000x6000 875x750 * initialSize:12 trueSize 08:8000x4600 1000x575 * initialSize:06 trueSize 04:4080x2720 1020x680 * initialSize:05 trueSize 04:3264x2488 816x622 * initialSize:04 trueSize 04:3000x2000 750x500 * initialSize:04 trueSize 04:2560x1920 640x480 * initialSize:03 trueSize 02:2272x1704 1136x852 * initialSize:03 trueSize 02:2048x1536 1024x768 * initialSize:02 trueSize 02:1600x1200 800x600 * initialSize:02 trueSize 02:1280x960 640x480 * initialSize:01 trueSize 01:1024x768 1024x768 * initialSize:01 trueSize 01:800x600 800x600 * initialSize:01 trueSize 01:640x480 640x480 * * * * // Returns the previous power of two. * // Returns the input if it is already power of 2. * // Throws IllegalArgumentException if the input is <= 0 * * * 最终值往小的取(重采样得的图片尺寸越大) * * @param n * @return */ private static final int prevPowerOf2(final int n) { if (n <= 0) return 1; return Integer.highestOneBit(n); } 原始图最长边:1600px 目标图最长边:52px 图片缩放比例:原始图最长边 / 目标图最长边 = 30.77 系统默认: options.inSampleSize = 图片缩放比例 实际inSampleSize: 24 实际读取出的图片最长边:原宽 / 24 = 66(与目标图最长边相差:14px) 内存占用尽量小: options.inSampleSize = 系统默认SampleSize + 大一级别加8 实际inSampleSize: = 32 实际读取出的图片最长边:原宽 / 32 = 50(与目标图最长边相差:-2px)【最优解】(允许实际图小于目标图时) 原始图最长边:1600px 目标图最长边:65px 图片缩放比例:原始图最长边 / 目标图最长边 = 24.62 系统默认: options.inSampleSize = 图片缩放比例 实际inSampleSize: 24 实际读取出的图片最长边:原宽 / 24 = 66(与目标图最长边相差:1px)【最优解】 内存占用尽量小: options.inSampleSize = 系统默认SampleSize + 大一级别加8 实际inSampleSize: = 32 实际读取出的图片最长边:原宽 / 32 = 50(与目标图最长边相差:-15px) 原始图最长边:1600px 目标图最长边:58px 图片缩放比例:原始图最长边 / 目标图最长边 = 27.58 系统默认: options.inSampleSize = 图片缩放比例 实际inSampleSize: 24 实际读取出的图片最长边:原宽 / 24 = 66(与目标图最长边相差:8px)【最优解】 内存占用尽量小: options.inSampleSize = 系统默认SampleSize + 大一级别加8 实际inSampleSize: = 32 实际读取出的图片最长边:原宽 / 32 = 50(与目标图最长边相差:-8px)【最优解】(允许实际图小于目标图时,且低内存运行时) 新算法只有在inSampleSize 大于 24时,才有价值。例如当想从一张分辨率最长边大于 10240 的图片,取一张640的小缩略图时, 缩放比为:10304/640=16.1,此时设置给inSampleSize时,系统会将其“向” // 找到真正使用inSampleSize的地方了,但是换了一个变量名名字为scale_denom // Cross Reference: /external/jpeg/jdmaster.c // http://androidxref.com/2.2.3/xref/external/jpeg/jdmaster.c#97 /* 90 * Compute output image dimensions and related values. 91 * NOTE: this is exported for possible use by application. 92 * Hence it mustn't do anything that can't be done twice. 93 * Also note that it may be called before the master module is initialized! 94 */ 95 96GLOBAL(void) 97jpeg_calc_output_dimensions (j_decompress_ptr cinfo) 98/* Do computations that are needed before master selection phase */ 99{ 100#ifdef IDCT_SCALING_SUPPORTED 101 int ci; 102 jpeg_component_info *compptr; 103#endif 104 105 /* Prevent application from calling me at wrong times */ 106 if (cinfo->global_state != DSTATE_READY) 107 ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); 108 109#ifdef IDCT_SCALING_SUPPORTED 110 111 /* Compute actual output image dimensions and DCT scaling choices. */ 112 if (cinfo->scale_num * 8 <= cinfo->scale_denom) { 113 /* Provide 1/8 scaling */ 114 cinfo->output_width = (JDIMENSION) 115 jdiv_round_up((long) cinfo->image_width, 8L); 116 cinfo->output_height = (JDIMENSION) 117 jdiv_round_up((long) cinfo->image_height, 8L); 118 cinfo->min_DCT_scaled_size = 1; 119 } else if (cinfo->scale_num * 4 <= cinfo->scale_denom) { 120 /* Provide 1/4 scaling */ 121 cinfo->output_width = (JDIMENSION) 122 jdiv_round_up((long) cinfo->image_width, 4L); 123 cinfo->output_height = (JDIMENSION) 124 jdiv_round_up((long) cinfo->image_height, 4L); 125 cinfo->min_DCT_scaled_size = 2; 126 } else if (cinfo->scale_num * 2 <= cinfo->scale_denom) { 127 /* Provide 1/2 scaling */ 128 cinfo->output_width = (JDIMENSION) 129 jdiv_round_up((long) cinfo->image_width, 2L); 130 cinfo->output_height = (JDIMENSION) 131 jdiv_round_up((long) cinfo->image_height, 2L); 132 cinfo->min_DCT_scaled_size = 4; 133 } else { 134 /* Provide 1/1 scaling */ 135 cinfo->output_width = cinfo->image_width; 136 cinfo->output_height = cinfo->image_height; 137 cinfo->min_DCT_scaled_size = DCTSIZE; 138 } 139 /* In selecting the actual DCT scaling for each component, we try to 140 * scale up the chroma components via IDCT scaling rather than upsampling. 141 * This saves time if the upsampler gets to use 1:1 scaling. 142 * Note this code assumes that the supported DCT scalings are powers of 2. 143 */ 144 for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; 145 ci++, compptr++) { 146 int ssize = cinfo->min_DCT_scaled_size; 147 while (ssize < DCTSIZE && 148 (compptr->h_samp_factor * ssize * 2 <= 149 cinfo->max_h_samp_factor * cinfo->min_DCT_scaled_size) && 150 (compptr->v_samp_factor * ssize * 2 <= 151 cinfo->max_v_samp_factor * cinfo->min_DCT_scaled_size)) { 152 ssize = ssize * 2; 153 } 154 compptr->DCT_scaled_size = ssize; 155 } 156 157 /* Recompute downsampled dimensions of components; 158 * application needs to know these if using raw downsampled data. 159 */ 160 for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; 161 ci++, compptr++) { 162 /* Size in samples, after IDCT scaling */ 163 compptr->downsampled_width = (JDIMENSION) 164 jdiv_round_up((long) cinfo->image_width * 165 (long) (compptr->h_samp_factor * compptr->DCT_scaled_size), 166 (long) (cinfo->max_h_samp_factor * DCTSIZE)); 167 compptr->downsampled_height = (JDIMENSION) 168 jdiv_round_up((long) cinfo->image_height * 169 (long) (compptr->v_samp_factor * compptr->DCT_scaled_size), 170 (long) (cinfo->max_v_samp_factor * DCTSIZE)); 171 } 172 173#else /* !IDCT_SCALING_SUPPORTED */ 174 175 /* Hardwire it to "no scaling" */ 176 cinfo->output_width = cinfo->image_width; 177 cinfo->output_height = cinfo->image_height; 178 /* jdinput.c has already initialized DCT_scaled_size to DCTSIZE, 179 * and has computed unscaled downsampled_width and downsampled_height. 180 */ 181 182#endif /* IDCT_SCALING_SUPPORTED */ 183 184 /* Report number of components in selected colorspace. */ 185 /* Probably this should be in the color conversion module... */ 186 switch (cinfo->out_color_space) { 187 case JCS_GRAYSCALE: 188 cinfo->out_color_components = 1; 189 break; 190 case JCS_RGB: 191#if RGB_PIXELSIZE != 3 192 cinfo->out_color_components = RGB_PIXELSIZE; 193 break; 194#endif /* else share code with YCbCr */ 195#ifdef ANDROID_RGB 196 case JCS_RGB_565: 197#endif 198 case JCS_YCbCr: 199 cinfo->out_color_components = 3; 200 break; 201 case JCS_CMYK: 202 case JCS_YCCK: 203#ifdef ANDROID_RGB 204 case JCS_RGBA_8888: 205#endif 206 cinfo->out_color_components = 4; 207 break; 208 default: /* else must be same colorspace as in file */ 209 cinfo->out_color_components = cinfo->num_components; 210 break; 211 } 212 cinfo->output_components = (cinfo->quantize_colors ? 1 : 213 cinfo->out_color_components); 214 215 /* See if upsampler will want to emit more than one row at a time */ 216 if (use_merged_upsample(cinfo)) 217 cinfo->rec_outbuf_height = cinfo->max_v_samp_factor; 218 else 219 cinfo->rec_outbuf_height = 1; 220} 221 // Cross Reference: /external/jpeg/jutils.c // http://androidxref.com/2.2.3/xref/external/jpeg/jutils.c 67/* 68 * Arithmetic utilities 69 */ 70 71 GLOBAL(long) 72 jdiv_round_up (long a, long b) 73/* Compute a/b rounded up to next integer, ie, ceil(a/b) */ 74/* Assumes a >= 0, b > 0 */ 75{ 76 return (a + b - 1L) / b; 77} 78 79 //Cross Reference: /packages/apps/Camera/src/com/android/camera/Camera.java //http://androidxref.com/2.2.3/xref/packages/apps/Camera/src/com/android/camera/Camera.java //相机拍照完毕 居然还发送广播sendBroadcast(new Intent("com.android.camera.NEW_PICTURE", mLastContentUri)); //那么现在就有3种方式获取相机图片的方法了 // 在这里能找到inSampleSize 真正使用的地方。 // 1.Cross Reference: /frameworks/base/core/jni/android/graphics/BitmapFactory.cpp // http://androidxref.com/2.2.3/xref/frameworks/base/core/jni/android/graphics/BitmapFactory.cpp // 2. // 官网的DEMO例子里计算inSampleSize的方法 // Loading Large Bitmaps Efficiently | Android Developers // http://developer.android.com/intl/zh-cn/training/displaying-bitmaps/load-bitmap.html public static int calculateInSampleSize( BitmapFactory.Options options, int reqWidth, int reqHeight) { // Raw height and width of image final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { final int halfHeight = height / 2; final int halfWidth = width / 2; // Calculate the largest inSampleSize value that is a power of 2 and keeps both // height and width larger than the requested height and width. while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) { inSampleSize *= 2; } } return inSampleSize; } private static final void test2(final String imagePath) { final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(imagePath, options); double w = options.outWidth; for (int i = 1; i < 129; i++) { options.inSampleSize = i; BitmapFactory.decodeFile(imagePath, options); double b = w / options.outWidth; android.util.Log.e("size:" + i + " w:" + options.outWidth, "h:" + options.outHeight + "trueSize:" + b); } } 缩放比:1 w:15560 h:9725 计算后对应的inSampleSize:1.0 缩放比:2 w:7780 h:4863 计算后对应的inSampleSize:2.0 缩放比:3 w:7780 h:4863 计算后对应的inSampleSize:2.0 缩放比:4 w:3890 h:2432 计算后对应的inSampleSize:4.0 缩放比:5 w:3890 h:2432 计算后对应的inSampleSize:4.0 缩放比:6 w:3890 h:2432 计算后对应的inSampleSize:4.0 缩放比:7 w:3890 h:2432 计算后对应的inSampleSize:4.0 缩放比:8 w:1945 h:1216 计算后对应的inSampleSize:8.0 缩放比:9 w:1945 h:1216 计算后对应的inSampleSize:8.0 缩放比:10 w:1945 h:1216 计算后对应的inSampleSize:8.0 缩放比:11 w:1945 h:1216 计算后对应的inSampleSize:8.0 缩放比:12 w:1945 h:1216 计算后对应的inSampleSize:8.0 缩放比:13 w:1945 h:1216 计算后对应的inSampleSize:8.0 缩放比:14 w:1945 h:1216 计算后对应的inSampleSize:8.0 缩放比:15 w:1945 h:1216 计算后对应的inSampleSize:8.0 缩放比:16 w:972 h:608 计算后对应的inSampleSize:16.008230452674898 缩放比:17 w:972 h:608 计算后对应的inSampleSize:16.008230452674898 缩放比:18 w:972 h:608 计算后对应的inSampleSize:16.008230452674898 缩放比:19 w:972 h:608 计算后对应的inSampleSize:16.008230452674898 缩放比:20 w:972 h:608 计算后对应的inSampleSize:16.008230452674898 缩放比:21 w:972 h:608 计算后对应的inSampleSize:16.008230452674898 缩放比:22 w:972 h:608 计算后对应的inSampleSize:16.008230452674898 缩放比:23 w:972 h:608 计算后对应的inSampleSize:16.008230452674898 缩放比:24 w:648 h:405 计算后对应的inSampleSize:24.012345679012345 缩放比:25 w:648 h:405 计算后对应的inSampleSize:24.012345679012345 缩放比:26 w:648 h:405 计算后对应的inSampleSize:24.012345679012345 缩放比:27 w:648 h:405 计算后对应的inSampleSize:24.012345679012345 缩放比:28 w:648 h:405 计算后对应的inSampleSize:24.012345679012345 缩放比:29 w:648 h:405 计算后对应的inSampleSize:24.012345679012345 缩放比:30 w:648 h:405 计算后对应的inSampleSize:24.012345679012345 缩放比:31 w:648 h:405 计算后对应的inSampleSize:24.012345679012345 缩放比:32 w:486 h:304 计算后对应的inSampleSize:32.016460905349795 缩放比:33 w:486 h:304 计算后对应的inSampleSize:32.016460905349795 缩放比:34 w:486 h:304 计算后对应的inSampleSize:32.016460905349795 缩放比:35 w:486 h:304 计算后对应的inSampleSize:32.016460905349795 缩放比:36 w:486 h:304 计算后对应的inSampleSize:32.016460905349795 缩放比:37 w:486 h:304 计算后对应的inSampleSize:32.016460905349795 缩放比:38 w:486 h:304 计算后对应的inSampleSize:32.016460905349795 缩放比:39 w:486 h:304 计算后对应的inSampleSize:32.016460905349795 缩放比:40 w:389 h:243 计算后对应的inSampleSize:40.0 缩放比:41 w:389 h:243 计算后对应的inSampleSize:40.0 缩放比:42 w:389 h:243 计算后对应的inSampleSize:40.0 缩放比:43 w:389 h:243 计算后对应的inSampleSize:40.0 缩放比:44 w:389 h:243 计算后对应的inSampleSize:40.0 缩放比:45 w:389 h:243 计算后对应的inSampleSize:40.0 缩放比:46 w:389 h:243 计算后对应的inSampleSize:40.0 缩放比:47 w:389 h:243 计算后对应的inSampleSize:40.0 缩放比:48 w:324 h:202 计算后对应的inSampleSize:48.02469135802469 缩放比:49 w:324 h:202 计算后对应的inSampleSize:48.02469135802469 缩放比:50 w:324 h:202 计算后对应的inSampleSize:48.02469135802469 缩放比:51 w:324 h:202 计算后对应的inSampleSize:48.02469135802469 缩放比:52 w:324 h:202 计算后对应的inSampleSize:48.02469135802469 缩放比:53 w:324 h:202 计算后对应的inSampleSize:48.02469135802469 缩放比:54 w:324 h:202 计算后对应的inSampleSize:48.02469135802469 缩放比:55 w:324 h:202 计算后对应的inSampleSize:48.02469135802469 缩放比:56 w:277 h:173 计算后对应的inSampleSize:56.17328519855596 缩放比:57 w:277 h:173 计算后对应的inSampleSize:56.17328519855596 缩放比:58 w:277 h:173 计算后对应的inSampleSize:56.17328519855596 缩放比:59 w:277 h:173 计算后对应的inSampleSize:56.17328519855596 缩放比:60 w:277 h:173 计算后对应的inSampleSize:56.17328519855596 缩放比:61 w:277 h:173 计算后对应的inSampleSize:56.17328519855596 缩放比:62 w:277 h:173 计算后对应的inSampleSize:56.17328519855596 缩放比:63 w:277 h:173 计算后对应的inSampleSize:56.17328519855596 缩放比:64 w:243 h:152 计算后对应的inSampleSize:64.03292181069959 缩放比:65 w:243 h:152 计算后对应的inSampleSize:64.03292181069959 缩放比:66 w:243 h:152 计算后对应的inSampleSize:64.03292181069959 缩放比:67 w:243 h:152 计算后对应的inSampleSize:64.03292181069959 缩放比:68 w:243 h:152 计算后对应的inSampleSize:64.03292181069959 缩放比:69 w:243 h:152 计算后对应的inSampleSize:64.03292181069959 缩放比:70 w:243 h:152 计算后对应的inSampleSize:64.03292181069959 缩放比:71 w:243 h:152 计算后对应的inSampleSize:64.03292181069959 缩放比:72 w:216 h:135 计算后对应的inSampleSize:72.03703703703704 缩放比:73 w:216 h:135 计算后对应的inSampleSize:72.03703703703704 缩放比:74 w:216 h:135 计算后对应的inSampleSize:72.03703703703704 缩放比:75 w:216 h:135 计算后对应的inSampleSize:72.03703703703704 缩放比:76 w:216 h:135 计算后对应的inSampleSize:72.03703703703704 缩放比:77 w:216 h:135 计算后对应的inSampleSize:72.03703703703704 缩放比:78 w:216 h:135 计算后对应的inSampleSize:72.03703703703704 缩放比:79 w:216 h:135 计算后对应的inSampleSize:72.03703703703704 缩放比:80 w:194 h:121 计算后对应的inSampleSize:80.20618556701031 缩放比:81 w:194 h:121 计算后对应的inSampleSize:80.20618556701031 缩放比:82 w:194 h:121 计算后对应的inSampleSize:80.20618556701031 缩放比:83 w:194 h:121 计算后对应的inSampleSize:80.20618556701031 缩放比:84 w:194 h:121 计算后对应的inSampleSize:80.20618556701031 缩放比:85 w:194 h:121 计算后对应的inSampleSize:80.20618556701031 缩放比:86 w:194 h:121 计算后对应的inSampleSize:80.20618556701031 缩放比:87 w:194 h:121 计算后对应的inSampleSize:80.20618556701031 缩放比:88 w:176 h:110 计算后对应的inSampleSize:88.4090909090909 缩放比:89 w:176 h:110 计算后对应的inSampleSize:88.4090909090909 缩放比:90 w:176 h:110 计算后对应的inSampleSize:88.4090909090909 缩放比:91 w:176 h:110 计算后对应的inSampleSize:88.4090909090909 缩放比:92 w:176 h:110 计算后对应的inSampleSize:88.4090909090909 缩放比:93 w:176 h:110 计算后对应的inSampleSize:88.4090909090909 缩放比:94 w:176 h:110 计算后对应的inSampleSize:88.4090909090909 缩放比:95 w:176 h:110 计算后对应的inSampleSize:88.4090909090909 缩放比:96 w:162 h:101 计算后对应的inSampleSize:96.04938271604938 缩放比:97 w:162 h:101 计算后对应的inSampleSize:96.04938271604938 缩放比:98 w:162 h:101 计算后对应的inSampleSize:96.04938271604938 缩放比:99 w:162 h:101 计算后对应的inSampleSize:96.04938271604938 缩放比:100 w:162 h:101 计算后对应的inSampleSize:96.04938271604938 缩放比:101 w:162 h:101 计算后对应的inSampleSize:96.04938271604938 缩放比:102 w:162 h:101 计算后对应的inSampleSize:96.04938271604938 缩放比:103 w:162 h:101 计算后对应的inSampleSize:96.04938271604938 缩放比:104 w:149 h:93 计算后对应的inSampleSize:104.42953020134229 缩放比:105 w:149 h:93 计算后对应的inSampleSize:104.42953020134229 缩放比:106 w:149 h:93 计算后对应的inSampleSize:104.42953020134229 缩放比:107 w:149 h:93 计算后对应的inSampleSize:104.42953020134229 缩放比:108 w:149 h:93 计算后对应的inSampleSize:104.42953020134229 缩放比:109 w:149 h:93 计算后对应的inSampleSize:104.42953020134229 缩放比:110 w:149 h:93 计算后对应的inSampleSize:104.42953020134229 缩放比:111 w:149 h:93 计算后对应的inSampleSize:104.42953020134229 缩放比:112 w:138 h:86 计算后对应的inSampleSize:112.7536231884058 缩放比:113 w:138 h:86 计算后对应的inSampleSize:112.7536231884058 缩放比:114 w:138 h:86 计算后对应的inSampleSize:112.7536231884058 缩放比:115 w:138 h:86 计算后对应的inSampleSize:112.7536231884058 缩放比:116 w:138 h:86 计算后对应的inSampleSize:112.7536231884058 缩放比:117 w:138 h:86 计算后对应的inSampleSize:112.7536231884058 缩放比:118 w:138 h:86 计算后对应的inSampleSize:112.7536231884058 缩放比:119 w:138 h:86 计算后对应的inSampleSize:112.7536231884058 缩放比:120 w:129 h:81 计算后对应的inSampleSize:120.62015503875969 缩放比:121 w:129 h:81 计算后对应的inSampleSize:120.62015503875969 缩放比:122 w:129 h:81 计算后对应的inSampleSize:120.62015503875969 缩放比:123 w:129 h:81 计算后对应的inSampleSize:120.62015503875969 缩放比:124 w:129 h:81 计算后对应的inSampleSize:120.62015503875969 缩放比:125 w:129 h:81 计算后对应的inSampleSize:120.62015503875969 缩放比:126 w:129 h:81 计算后对应的inSampleSize:120.62015503875969 缩放比:127 w:129 h:81 计算后对应的inSampleSize:120.62015503875969 缩放比:128 w:121 h:76 计算后对应的inSampleSize:128.59504132231405 //TO DO:这里根据inJustDecodeBounds=True时,看是否能设置inSampleSise在不编码图像数据情况下拿到新取样后的大小?? 【经过测试】可以在设置 inJustDecodeBounds = true;后,再设置 inSampleSize ,的确可以再获得待缩小后的 长和宽 //TO DO:根据之前的真实测试数据,存在一种非正常的SimpleSize 24!所以是否现在的取SimpleSize算法还不是真正的算法 ??? 【经过测试】的确存在24的情况,说明算法不是仅仅的2的指数次值。 重采样大小:24 原图分辨率:15000x13600 目标分辨率: 625x566 重采样大小:24 原图分辨率:14000x12600 目标分辨率:583x525 重采样大小:24 原图分辨率:13000x11600 目标分辨率:541x483 重采样大小:24 原图分辨率:12000x10600 目标分辨率:500x441 重采样大小:16 原图分辨率:11000x9600 目标分辨率: 687x600 重采样大小:16 原图分辨率:10000x8600 目标分辨率:625x537 重采样大小:16 原图分辨率:9000x7600 目标分辨率:562x475 重采样大小:16 原图分辨率:8000x6000 目标分辨率:500x375 重采样大小:16 原图分辨率:7000x6000 目标分辨率:437x375 重采样大小:8 原图分辨率:8000x4600 目标分辨率:1000x575 重采样大小:4 原图分辨率:4080x2720 目标分辨率:1020x680 重采样大小:4 原图分辨率:3264x2488 目标分辨率:816x622 重采样大小:4 原图分辨率:3000x2000 目标分辨率:750x500 重采样大小:4 原图分辨率:2560x1920 目标分辨率:640x480 重采样大小:2 原图分辨率:2272x1704 目标分辨率:1136x852 重采样大小:2 原图分辨率:2048x1536 目标分辨率:1024x768 重采样大小:2 原图分辨率:1600x1200 目标分辨率:800x600 重采样大小:1 原图分辨率:1280x960 目标分辨率: 1280x960 重采样大小:1 原图分辨率:1024x768 目标分辨率: 1024x768 重采样大小:1 原图分辨率:800x600 目标分辨率: 800x600 重采样大小:1 原图分辨率:640x480 目标分辨率:640x480 public static void test() { //常用照片分辨率 int[][] list = new int[][] { { 15000, 13600 }, { 14000, 12600 }, { 13000, 11600 }, { 12000, 10600 }, { 11000, 9600 }, { 10000, 8600 }, { 9000, 7600 }, { 8000, 6000 }, { 7000, 6000 }, { 8000, 4600 }, { 4080, 2720 }, { 3264, 2488 }, { 3000, 2000 }, { 2560, 1920 }, { 2272, 1704 }, { 2048, 1536 }, { 1600, 1200 }, { 1280, 960 }, { 1024, 768 }, { 800, 600 }, { 640, 480 } }; for (int i = 0; i < list.length; i++) { int w = list[i][0]; int h = list[i][1]; int s = BitmapUtils.computeSampleSize(w, h, 600, -1); LogEx.e("重采样大小:"+s + "\t原图分辨率:" + w + "\t目标分辨率:" + h, w / s + "x" + h / s); } } /* * Compute the sample size as a function of minSideLength and * maxNumOfPixels. minSideLength is used to specify that minimal width or * height of a bitmap. maxNumOfPixels is used to specify the maximal size in * pixels that is tolerable in terms of memory usage. * * The function returns a sample size based on the constraints. Both size * and minSideLength can be passed in as UNCONSTRAINED, which indicates no * care of the corresponding constraint. The functions prefers returning a * sample size that generates a smaller bitmap, unless minSideLength = * UNCONSTRAINED. * * Also, the function rounds up the sample size to a power of 2 or multiple * of 8 because BitmapFactory only honors sample size this way. For example, * BitmapFactory downsamples an image by 2 even though the request is 3. So * we round up the sample size to avoid OOM. */ private static int computeSampleSize(int width, int height, int minSideLength, int maxNumOfPixels) { int initialSize = computeInitialSampleSize(width, height, minSideLength, maxNumOfPixels); return initialSize <= 8 ? nextPowerOf2(initialSize) : (initialSize + 7) / 8 * 8; } private static int computeInitialSampleSize(int w, int h, int minSideLength, int maxNumOfPixels) { if (maxNumOfPixels == UNCONSTRAINED && minSideLength == UNCONSTRAINED) return 1; int lowerBound = (maxNumOfPixels == UNCONSTRAINED) ? 1 : (int) FloatMath.ceil(FloatMath.sqrt((float) (w * h) / maxNumOfPixels)); if (minSideLength == UNCONSTRAINED) { return lowerBound; } else { int sampleSize = Math.min(w / minSideLength, h / minSideLength); return Math.max(sampleSize, lowerBound); } } // Returns the next power of two. // Returns the input if it is already power of 2. // Throws IllegalArgumentException if the input is <= 0 or // the answer overflows. private static int nextPowerOf2(int n) { if (n <= 0 || n > (1 << 30)) throw new IllegalArgumentException(); n -= 1; n |= n >> 16; n |= n >> 8; n |= n >> 4; n |= n >> 2; n |= n >> 1; return n + 1; }
作者:Asion Tang
凡是没有注明[转载]的文章,本Blog发表的文章版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。