找回密码
 点一下
查看: 3555|回复: 4

[解决]可以问编程的问题吗?读取BLP图片时遇到小麻烦

[复制链接]
发表于 2008-3-26 13:43:23 | 显示全部楼层 |阅读模式
大家好^_^

最近在网络上搜索BLP图片的相关资料,但大多都只是对文件头部的说明,对JPG压缩部分介绍则很简略。我只好下载了War3ModelEditor的源代码去看。
经过几天的摆弄,现在我的程序已经可以显示BLP图片了。但是我还有一点问题不明白。文件说明里面提到,BLP图片内部是有mipmap的,就是说还有一些小的图片。比如一幅256*256的图片,其实文件中除了本身的256*256图片外,还有128*128, 64*64, 32*32, 16*16, 8*8, 4*4, 2*2, 1*1,总共9幅图片,每一幅图片都是上一幅缩小一半而得。War3ModelEditor的代码中,只读取了最大的一幅256*256的图片,那么剩余的小图片应该如何读取?

正常的JPG文件是由一个JPG信息头和一组压缩后的像素数据组成。BLP则是一个JPG信息头和多组压缩后的像素数据组成,每组像素都是一幅图片,所有的图片共用一个JPG信息头。但是JPG信息头中记录了图片的大小,而每幅图片的大小是不同的,如何达到共用?望达人教我。
发表于 2008-3-26 17:59:05 | 显示全部楼层
恩,没研究过..
等待达人回答..
回复

使用道具 举报

发表于 2008-3-26 19:11:52 | 显示全部楼层
把公用的文件头中的大小依次除以2。
顺便说下。JPG压缩的部分之所以略过,是因为主题是“BLP格式”而不是"JPEG格式"
JPEG压缩牵涉到余弦变换,霍夫曼编码等,和BLP这种简单的几句话就能描述出来格式相比复杂太多老。
回复

使用道具 举报

 楼主| 发表于 2008-3-26 21:20:25 | 显示全部楼层
没想到今天才来就召唤到大神鸟~
谢谢白银的回答。我看War3ModelEditor的代码,是用IJG的库做jpeg解码的,我也是跟着来。只是那个JPEG公用的文件头,如何找到大小的数据呢?我一直没有查到相关资料。现在倒好,找了个取巧的办法,但是也行的通了。

[code=cpp]
        // 读取JPEG文件头
        GLint jpeg_header_size;
        is.read(jpeg_header_size);
        std::vector<GLubyte> jpeg_header(jpeg_header_size);
        is.checkedRead(&jpeg_header[0], jpeg_header_size);
        // 读取mipmap
        std::vector<GLubyte> mipmap_pixels[BLP_MAX_MIPMAP];
        int levels = 0;
        for(int i=0; i<BLP_MAX_MIPMAP; ++i) {
            if( header.MipmapSize <= 0 )
                break;
            ++levels;
            is.seekFromStart(header.MipmapOffset);
            mipmap_pixels = std::vector<GLubyte>(header.MipmapSize);
            is.checkedRead(&mipmap_pixels[0], header.MipmapSize);
        }
        // JPEG解压缩
        jpeg_decompress_struct cinfo;
        jpeg_error_mgr jerr;
        cinfo.err = jpeg_std_error(&jerr);
        jpeg_create_decompress(&cinfo);

        GLint width = header.Width;
        GLint height = header.Height;
        // 本该循环解压各mipmap,但是这里仅解第一层
        for(int i=0; i<levels; ++i, width=max(width/2, 1), height=max(height/2, 1)) {
        // for(int i=0; i<1; ++i, width=max(width/2, 1), height=max(height/2, 1)) {
            // 构造一个JPEG数据
            MemoryInputStreamAdapter
                is_header(&jpeg_header[0], jpeg_header_size),
                is_pixels(&mipmap_pixels[0], header.MipmapSize);
            InputStream* stream_list[] = {&is_header, &is_pixels};
            ConcatInputStreamAdapter is_concat(
                std::vector<InputStream*>(stream_list, stream_list+2));
            JpegSourceMgr sm(is_concat);
            cinfo.src = &sm;

            // 读取JPEG头(注意每次重新设置高度和宽度)
            jpeg_read_header(&cinfo, TRUE);
            cinfo.output_width = width;
            cinfo.output_height = height;

            // 解压缩
            jpeg_start_decompress(&cinfo);
            const GLint components = cinfo.output_components;
            const int line_bytes = (width * components+3)/4*4; // 每行像素使用的字节数,四字节对齐
            std::vector<unsigned char> pixels(line_bytes * height);
            for(int j=0; j<height; ++j) { // 逐行解压缩,注意垂直方向反转
                unsigned char* read_out_ptr = &pixels[(height-j-1)*line_bytes];
                JSAMPROW pixel_rows[] = {(JSAMPROW)read_out_ptr};
                jpeg_read_scanlines(&cinfo, pixel_rows, 1);
            }
            jpeg_finish_decompress(&cinfo);

            // 用解压后的数据覆盖原来的数据
            mipmap_pixels = pixels;
        }

        jpeg_destroy_decompress(&cinfo);
[/code]
回复

使用道具 举报

 楼主| 发表于 2008-3-26 21:28:51 | 显示全部楼层
-_- 代码贴错鸟……
再试一次
[codes=cpp]
        // 读取JPEG文件头
        GLint jpeg_header_size;
        is.read(jpeg_header_size);
        std::vector<GLubyte> jpeg_header(jpeg_header_size);
        is.checkedRead(&jpeg_header[0], jpeg_header_size);
        // 读取mipmap
        std::vector<GLubyte> mipmap_pixels[BLP_MAX_MIPMAP];
        int levels = 0;
        for(int i=0; i<BLP_MAX_MIPMAP; ++i) {
            if( header.MipmapSize <= 0 )
                break;
            ++levels;
            is.seekFromStart(header.MipmapOffset);
            mipmap_pixels = std::vector<GLubyte>(header.MipmapSize);
            is.checkedRead(&mipmap_pixels[0], header.MipmapSize);
        }
        // JPEG解压缩
        jpeg_decompress_struct cinfo;
        jpeg_error_mgr jerr;
        cinfo.err = jpeg_std_error(&jerr);
        jpeg_create_decompress(&cinfo);

        GLint width = header.Width;
        GLint height = header.Height;
        // 循环解压各层mipmap
        for(int i=0; i<levels; ++i, width=max(width/2, 1), height=max(height/2, 1)) {
            // 构造一个JPEG数据
            MemoryInputStreamAdapter
                is_header(&jpeg_header[0], jpeg_header_size),
                is_pixels(&mipmap_pixels[0], header.MipmapSize);
            InputStream* stream_list[] = {&is_header, &is_pixels};
            ConcatInputStreamAdapter is_concat(
                std::vector<InputStream*>(stream_list, stream_list+2));
            JpegSourceMgr sm(is_concat);
            cinfo.src = &sm;

            // 读取JPEG头(注意每次重新设置高度和宽度)
            jpeg_read_header(&cinfo, TRUE);
            cinfo.output_width = width;
            cinfo.output_height = height;

            // 解压缩
            jpeg_start_decompress(&cinfo);
            const GLint components = cinfo.output_components;
            const int line_bytes = (width * components+3)/4*4; // 每行像素使用的字节数,四字节对齐
            std::vector<unsigned char> pixels(line_bytes * height);
            for(int j=0; j<height; ++j) { // 逐行解压缩,注意垂直方向反转
                unsigned char* read_out_ptr = &pixels[(height-j-1)*line_bytes];
                JSAMPROW pixel_rows[] = {(JSAMPROW)read_out_ptr};
                jpeg_read_scanlines(&cinfo, pixel_rows, 1);
            }
            jpeg_finish_decompress(&cinfo);

            // 用解压后的数据覆盖原来的数据
            mipmap_pixels = pixels;
        }

        jpeg_destroy_decompress(&cinfo);
[/codes]
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 点一下

本版积分规则

Archiver|移动端|小黑屋|地精研究院

GMT+8, 2024-12-22 18:29 , Processed in 0.033544 second(s), 18 queries .

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表