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

转载地址:http://blog.jdk5.com/zh/java-get-image-size-without-loading-the-whole-data/

前几天写的 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;
}

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

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

振兴书城

©️2020 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值