前几天写的 ​​Bitmap图片压缩,大图加载防止OOM​​​ 文章中讲到了图片加载到内存之前先获取图片的宽高,然后判断是否加载后会占用内存过大。这种加载前获取图片宽高的是使用 ​​Android自带的Bitmap​​​ 提供的功能,底层是 ​​C++​​ 的实现。

今天看到一篇文章使用 ​​Java​​ 基础类解析除图片宽高,实测没问题之后。欣喜之后记录下来~!

我们对 ​​dex​​​ 做过分析的都知道文件的头部字节都有着对应的含义,其他其他文件也一样。
使用一个文本编辑工具打开文件(16进制模式),然后看文件头是什么字符,以下是常见文件类型的文件头字符(16进制):

JPEG (jpg),文件头:FFD8FF 
PNG (png),文件头:89504E47
GIF (gif),文件头:47494638
TIFF (tif),文件头:49492A00
Windows Bitmap (bmp),文件头:424D
CAD (dwg),文件头:41433130
Adobe Photoshop (psd),文件头:38425053
Rich Text Format (rtf),文件头:7B5C727466
XML (xml),文件头:3C3F786D6C
HTML (html),文件头:68746D6C3E
Email [thorough only] (eml),文件头:44656C69766572792D646174653A
Outlook Express (dbx),文件头:CFAD12FEC5FD746F
Outlook (pst),文件头:2142444E
MS Word/Excel (xls.or.doc),文件头:D0CF11E0
MS Access (mdb),文件头:5374616E64617264204A
WordPerfect (wpd),文件头:FF575043
Postscript (eps.or.ps),文件头:252150532D41646F6265
Adobe Acrobat (pdf),文件头:255044462D312E
Quicken (qdf),文件头:AC9EBD8F
Windows Password (pwl),文件头:E3828596
ZIP Archive (zip),文件头:504B0304
RAR Archive (rar),文件头:52617221
Wave (wav),文件头:57415645
AVI (avi),文件头:41564920
Real Audio (ram),文件头:2E7261FD
Real Media (rm),文件头:2E524D46
MPEG (mpg),文件头:000001BA
MPEG (mpg),文件头:000001B3
Quicktime (mov),文件头:6D6F6F76
Windows Media (asf),文件头:3026B2758E66CF11
MIDI (mid),文件头:4D546864

根据上面的文件头类型,下面代码只需要读取数据流的最前面一段数据就能从中解析出图片的 ​​mimeType\width\height​​ :

private void processStream(InputStream is) throws IOException {
int c1 = is.read();
int c2 = is.read();
int c3 = is.read();
String mimeType = null;
int width = -1;
int height = -1;
if (c1 == 'G' && c2 == 'I' && c3 == 'F') { // GIF
is.skip(3);
width = readInt(is,2,false);
height = readInt(is,2,false);
mimeType = "image/gif";
} else if (c1 == 0xFF && c2 == 0xD8) { // JPG
while (c3 == 255) {
int marker = is.read();
int len = readInt(is,2,true);
if (marker == 192 || marker == 193 || marker == 194) {
is.skip(1);
height = readInt(is,2,true);
width = readInt(is,2,true);
mimeType = "image/jpeg";
break;
}
is.skip(len - 2);
c3 = is.read();
}
} else if (c1 == 137 && c2 == 80 && c3 == 78) { // PNG
is.skip(15);
width = readInt(is,2,true);
is.skip(2);
height = readInt(is,2,true);
mimeType = "image/png";
} else if (c1 == 66 && c2 == 77) { // BMP
is.skip(15);
width = readInt(is,2,false);
is.skip(2);
height = readInt(is,2,false);
mimeType = "image/bmp";
} else {
int c4 = is.read();
if ((c1 == 'M' && c2 == 'M' && c3 == 0 && c4 == 42)
|| (c1 == 'I' && c2 == 'I' && c3 == 42 && c4 == 0)) { //TIFF
boolean bigEndian = c1 == 'M';
int ifd = 0;
int entries;
ifd = readInt(is,4,bigEndian);
is.skip(ifd - 8);
entries = readInt(is,2,bigEndian);
for (int i = 1; i <= entries; i++) {
int tag = readInt(is,2,bigEndian);
int fieldType = readInt(is,2,bigEndian);
int valOffset;
if ((fieldType == 3 || fieldType == 8)) {
valOffset = readInt(is,2,bigEndian);
is.skip(2);
} else {
valOffset = readInt(is,4,bigEndian);
}
if (tag == 256) {
width = valOffset;
} else if (tag == 257) {
height = valOffset;
}
if (width != -1 && height != -1) {
mimeType = "image/tiff";
break;
}
}
}
}
if (mimeType == null) {
throw new IOException("Unsupported image type");
}
System.out.println("MIME Type : " + mimeType + "\t Width : " + width + "\t Height : " + height);
}
private int readInt(InputStream is, int noOfBytes, boolean bigEndian) throws IOException {
int ret = 0;
int sv = bigEndian ? ((noOfBytes - 1) * 8) : 0;
int cnt = bigEndian ? -8 : 8;
for(int i=0;i<noOfBytes;i++) {
ret |= is.read() << sv;
sv += cnt;
}
return ret;
}

文章到这里就全部讲述完啦,若有其他需要交流的可以留言哦

想阅读作者的更多文章,可以查看我 ​​个人博客​​ 和公共号:

图片不需要完全加载就能获取宽高_加载