Project1

标题: Bitmap 类储存为 .bmp 档案(高速= =) [打印本页]

作者: 一箭烂YiJL    时间: 2011-5-28 17:43
标题: Bitmap 类储存为 .bmp 档案(高速= =)
本帖最后由 一箭烂YiJL 于 2011-5-28 17:45 编辑

0.序
这是将Bitmap 类(挺)高速的保存为bmp档案。因为之前想用柳之一的《Bitmap高速Marshal》和保存png结合,
但其实是上下倒置,而且png格式每行都要插进"0",获取每点的颜色值比起用数组"搞定"上下倒置和插"0"要快!
今天我研究了一下bmp档案,bmp格式是上下倒置,而且不用插"0",所以将拿到的数据直接写入bmp的颜色值数据流中。
然而这样比png的每点扫要快得多,VX的屏幕载图储存bmp,fps不会降!

1.脚本
脚本如下(XP/VX),柳之一的《Bitmap高速Marshal》已经不是那个样了(而且不能Marshal= =):

  1. #==============================================================================
  2. # ■ BmpWriter             by 一箭烂YiJL (获取颜色值部分 by 柳之一)
  3. #------------------------------------------------------------------------------
  4. #  使用手册:
  5. #
  6. #     BmpWriter.write(bitmap, [name[, path]])
  7. #       bitmap    : 位图
  8. #       name      : 保存文件名
  9. #       path      : 保存路径
  10. #
  11. #     自动创建路径!
  12. #==============================================================================

  13. module BmpWriter
  14.   #--------------------------------------------------------------------------
  15.   # ● 传送到内存的API函数
  16.   #--------------------------------------------------------------------------
  17.   RtlMoveMemory_pi = Win32API.new('kernel32', 'RtlMoveMemory', 'pii', 'i')
  18.   #--------------------------------------------------------------------------
  19.   # ● 制作bmp档案
  20.   #--------------------------------------------------------------------------
  21.   def self.write(bitmap, name = "Bitmap.bmp", path = "Pictures/")
  22.     ## 先要工作
  23.     Dir.mkdir(path) unless FileTest.directory?(path)
  24.     length = bitmap.width * bitmap.height
  25.     file = File.open(path + name, "wb")
  26.    
  27.     ## BitmapFileHeader
  28.     size = 54 + length * 4
  29.     chunk = "BM" + [size, 0, 54].pack("L*")
  30.     file.write(chunk)
  31.    
  32.     ## BitmapInfoHeader
  33.     chunk = [40, bitmap.width, bitmap.height, 1, 32, 0, 0, 0xEC4, 0xEC4, 256, 0].pack("L3S2L*")
  34.     file.write(chunk)
  35.    
  36.     ## Byte
  37.     data = "rgba" * bitmap.width * bitmap.height
  38.     RtlMoveMemory_pi.call(data, bitmap.address, data.length)
  39.     chunk = data
  40.     file.write(chunk)
  41.    
  42.     file.close
  43.   end
  44. end

  45. class Bitmap
  46.   #--------------------------------------------------------------------------
  47.   # ● 传送到内存的API函数
  48.   #--------------------------------------------------------------------------
  49.   RtlMoveMemory_pi = Win32API.new('kernel32', 'RtlMoveMemory', 'pii', 'i')
  50.   #--------------------------------------------------------------------------
  51.   # ● Bitmap地址
  52.   #--------------------------------------------------------------------------
  53. # [[[bitmap.object_id * 2 + 16] + 8] + 16] == 数据的开头
  54.   def address
  55.     buffer, ad = "rgba", object_id * 2 + 16
  56.     RtlMoveMemory_pi.call(buffer, ad, 4)
  57.     ad = buffer.unpack("L")[0] + 8
  58.     RtlMoveMemory_pi.call(buffer, ad, 4)
  59.     ad = buffer.unpack("L")[0] + 16
  60.     RtlMoveMemory_pi.call(buffer, ad, 4)
  61.     return buffer.unpack("L")[0]
  62.   end
  63. end
复制代码

2.范例
只有VX的: BmpWriter(VX).zip (280.85 KB, 下载次数: 248)
在任何场景按F8就可以载图,保存在 Pictures/Bitmap.bmp
没有XP的是因为XP还要找XP的Graphics.snap_to_bitmap,如果真的很想要的话就回帖伸手吧~

作者: fux2    时间: 2011-5-28 18:34
在手机上不能回复,在电脑上竟然还没人回复= =
能详细说一下RtlMoveMemory函数的作用吗?看了半天度娘没懂= =
作者: 苏小脉    时间: 2011-5-28 22:33
32 位设备相关的 BMP 格式确实是这样的。扫描行倒置是和老式输出设备处理扫描线的效率有关。至于“插0”,那是提高 CPU 内存效率的对齐机制,多余位以 0 填充使得整个扫描行刚好占据整 4 字节的倍数那么大的一个内存块。这个在 24 位及以下的 BMP 标准中也是有的,每个扫描行必须始于 32 位内存区域的分界处。32 位无论如何都是 32 位对齐的,所以不会出现填充位。
作者: 一箭烂YiJL    时间: 2011-5-28 22:50
苏小脉 发表于 2011-5-28 22:33
32 位设备相关的 BMP 格式确实是这样的。扫描行倒置是和老式输出设备处理扫描线的效率有关。至于“插0”, ...

最初32位的时候我是扫点的,然后"插0",但是出现移位和颜色值错误...(加上倒置)
24位好像是没有了 a 通道。但是png是图片的每一行"插0",并不是每个颜色值。
bmp貌似是Window的位图档案。

不解柳之一用的RtlMoveMemory,虽然我知道这是将内存移来移去= =,
也知道object_id * 2就是内存位置的起始点(?),"p"所依靠的 Object#inspect 输出:
<ClassName 0x?????>  # 0x????? 正是Object#object_id * 2
所以这个和内存位置有关,但是为啥+16后函数+8函数+16函数呢?(fux2来看了...)
作者: 苏小脉    时间: 2011-5-29 01:09
一箭烂YiJL 发表于 2011-5-28 22:50
最初32位的时候我是扫点的,然后"插0",但是出现移位和颜色值错误...(加上倒置)
24位好像是没有了 a 通道 ...

最初32位的时候我是扫点的,然后"插0",但是出现移位和颜色值错误...(加上倒置)

32 位不用填充,3 楼已述。
24位好像是没有了 a 通道。

然也。
但是png是图片的每一行"插0",并不是每个颜色值。

BMP 也是,只要扫描行是 32 位对齐即可。
bmp貌似是Window的位图档案。

然也。
不解柳之一用的RtlMoveMemory,虽然我知道这是将内存移来移去= =,
也知道object_id * 2就是内存位置的起始点(?),"p"所依靠的 Object#inspect 输出:
<ClassName 0x?????>  # 0x????? 正是Object#object_id * 2
所以这个和内存位置有关,但是为啥+16后函数+8函数+16函数呢?(fux2来看了...)

Ruby 对象的 `object_id' 是内存地址右移一位得到的,舍弃第 0 位。由于在 32 位取址的模型下,地址永远是 32 位对齐的,所以地址之间最小间距是 4。右移一位后最小间距变为了 2,也就是所有偶数的 ID 都被分配给了动态对象,而单数的 ID 就可以尽然分配给直接值使用(Fixnum、Symbol、TrueClass、FalseClass、NilClass)。因此,Ruby 的非直接对象底层的结构首地址就是 object_id << 1,相当于乘 2。

以前国外有人逆向分析出 Bitmap 的结构,大致如此:

  1. typedef struct rgss_bitmap_t rgss_bitmap_t;
  2. typedef struct rgss_bminfo_t rgss_bminfo_t;
  3. typedef struct bitmap_t bitmap_t;

  4. struct rgss_bitmap_t
  5. {
  6.     uint32_t flags;  
  7.     uint32_t klass;  
  8.     void (*dmark)(void*);  
  9.     void (*dfree)(void*);  
  10.     bitmap_t* bm;  
  11. }

  12. struct bitmap_t
  13. {
  14.     uint32_t unk1;  
  15.     uint32_t unk2;  
  16.     rgss_bminfo_t *bminfo;  
  17. }

  18. struct rgss_bminfo_t
  19. {
  20.     uint32_t unk1;
  21.     uint32_t unk2;
  22.     BITMAPINFOHEADER *infoheader;  
  23.     RGBQUAD *first;
  24.     RGBQUAD *last;
  25. }
复制代码
Object#object_id 拿到的是 rgss_bitmap_t 结构的地址,从该地址偏移 16 之后得到 rgss_bitmap_t#bm 这个指针域的地址,反引用之后寻址到 bitmap_t 结构的地址,偏移 8 后得到 bminfo 指针域地址,反引用后寻址到 rgss_bminfo_t 结构的地址,最后偏移 16 得到 last 这个指针域地址,而该指针指向的是位图像素第一行的地址,但实际上却是最后一个扫描行,所以命名为 last。

可以把 RtlMoveMemory 理解为纯 Ruby 反引用地址的一个工具。我们在 Ruby 中通过 API 拿到的内存地址并不能让我们直接操作其标识的内存,我们需要以某种方式把这段内存传输到 Ruby 可操纵的缓冲区中,也就是 Ruby 字符串。RtlMoveMemory 可以将一块内存整体拷贝到另一块缓冲区中,其第一个参数指定了目的地缓冲区的地址(你要拷贝的数据的目的地址),而第二个参数指定了源内存地址(你要拷贝的数据来源地址),第三个参数是长度。

Bitmap#address 里:
  1. RtlMoveMemory_pi.call(buffer, ad, 4)
复制代码
ad 是 object_id * 2 + 16,也就是上述 rgss_bitmap_t 结构体中 bm 域的地址;buffer 是一个 4 字节的 Ruby 字符串,接收了 rgss_bitmap_t#bm 所指向的内存的数据(也就是 bitmap_t 结构的地址,这里就是一个反引用地址的过程)。
  1. ad = buffer.unpack("L")[0] + 8
复制代码
现在 buffer 已经保存了 bitmap_t 结构首地址,偏移 8 之后得到 bitmap_t#bminfo 域的地址,赋给 ad。
  1. RtlMoveMemory_pi.call(buffer, ad, 4)
复制代码
ad 现在是 bitmap_t#bminfo 的地址,再次反引用地址,得到 rgss_bminfo_t 结构的首地址,拷贝到 buffer 中。
  1. ad = buffer.unpack("L")[0] + 16
复制代码
偏移 16,得到 rgss_bminfo_t#last 地址。
  1. RtlMoveMemory_pi.call(buffer, ad, 4)
复制代码
反引用,得到位图像素首地址。
  1. return buffer.unpack("L")[0]
复制代码
返回像素首地址。

好像在哪里说过实际的内存地址编号是左右调转的...(其实和上面的没什么关系)

你说的应该是小端序。小端序是一种字节(存储)序,表示最先储存的是最低有效字节,最后储存最高有效字节。i386 架构使用是小端序。

硬件使用小端序可以简化某些进行整数运算的电路设计。整数运算需要从最低有效位开始,进位时才转向更高的有效位,使用小端序就可以让整个运算不涉及到“回溯”,而是一直保持增加地址。大端序就需要先找到最低有效位,然后再“回溯”运算到最高有效位。

小端序还可以在不改变地址的情况下,获取到不同长度的相同整数值。比如——

01 00 00 00

这是小端序的“1”。读取 4 个字节传输到 32 位寄存器中,得到 01 00 00 00,值为 1;读取 2 个四节传输到 16 位寄存器中,得到 01 00,值为 1;读取 1 个字节传输到 8 位寄存器中,得到 01,值还为 1。

大端序的“1”则是:

00 00 00 01

无论寄存器长度,都需要把四个字节读入。

大端序的优势主要是在自然语言层面——今人所用之印度-阿拉伯数字体系,高位在左,低位在右。同时,很多人类口头语言也是“大端序”,如汉语的“九十四”,先提“九”,后提“四”。
作者: 一箭烂YiJL    时间: 2011-5-29 09:42
苏小脉 发表于 2011-5-29 01:09
32 位不用填充,3 楼已述。

然也。

谢谢~现在明白怎么能够拿到颜色值数据地址了,原来都是这些指针(和结构体的位偏移),
但为什么要让结构体里的指针指来指去?(这个出于好奇而已...)
还有为什么要 RGBQUAD 定义两个指针(first 和 last)?真奇怪
这样最后的一次RtlMoveMemory之前的偏移 16 改成偏移 12 也一样啊~

然后我根据国外分析的结构,在最后 RtlMoveMemory 之前的偏移 16 改成偏移 8 ,( BITMAPINFOHEADER 的指针)
之后拿 40 个位,就像 bmp 格式的 BitmapInfoHeader (主楼的那个)的方法来 unpack("L3S2L*"),
得出[40, Bitmap#width, Bitmap#height, 1, 32, 0, 0, 0, 0, 0, 0],(其实可以不 unpack ,写进bmp档案中)
我想苏应该知道我主楼那个的 BitmapInfoHeader 的某些值不同是无所谓的吧~

这说明了在 RM 的 Bitmap 类和 bmp 格式没什么分别的。


其实昨天本来是想研究 GIF 格式的,但网上的说明讲解不清楚,
而且有些几个关键数据弄成一个位,这个很麻烦。(又不知道那些数据有什么用)
之后想研究jpg,但jpg的延伸格式多的很...最后就研究了bmp了= =
作者: 苏小脉    时间: 2011-5-29 12:17
一箭烂YiJL 发表于 2011-5-29 09:42
谢谢~现在明白怎么能够拿到颜色值数据地址了,原来都是这些指针(和结构体的位偏移),
但为什么要让结构体 ...
但为什么要让结构体里的指针指来指去?

做面向对象编程的时候,为什么要设计这么多类,又让类的成员引用来引用去?

为什么要 RGBQUAD 定义两个指针(first 和 last)?

rgss_bminfo_t#last 是实际的像素数据开头,方便用户从这里直接拷贝整块数据;rgss_bminfo_t#first 是实际的像素数据的最后一行开头,不过在映射到输出设备(显示器)后就会变成第一行,这方便了用户直接通过 rgss_bminfo_t#first 指针进行扫描行的翻转,也就是从 rgss_bminfo_t#first 开始扫描一行,每次偏移负的一行那么多字节(first -= 宽度 until first == last)。

这样最后的一次RtlMoveMemory之前的偏移 16 改成偏移 12 也一样啊~

看要做什么了。像这个脚本就可以在 rgss_bminfo_t#first 这个位置开始拷贝数据,然后逆向偏移 rgss_bminfo_t#first。这里做了翻转就是因为 GDI 返回的位图数据是设备相关的,没有像设备无关的 BMP 格式那样被翻转。

然后我根据国外分析的结构,在最后 RtlMoveMemory 之前的偏移 16 改成偏移 8 ,( BITMAPINFOHEADER 的指针)
之后拿 40 个位,就像 bmp 格式的 BitmapInfoHeader (主楼的那个)的方法来 unpack("L3S2L*"),
得出[40, Bitmap#width, Bitmap#height, 1, 32, 0, 0, 0, 0, 0, 0],(其实可以不 unpack ,写进bmp档案中)
我想苏应该知道我主楼那个的 BitmapInfoHeader 的某些值不同是无所谓的吧~

应该是可以直接存进 BMP 的。

这说明了在 RM 的 Bitmap 类和 bmp 格式没什么分别的。

和 32 位格式无压缩 BMP 格式没有区别。BMP 支持所有的色深模式,比如 24 位真彩色,16 位高彩色,8 位 256 索引色,4 位 16 色,2 位 4 色,1 位单色(黑白)等等。同时 BMP 还有自己的压缩标准,支持简单的 RLE、霍夫曼压缩,当然基本没什么实用价值。

其实昨天本来是想研究 GIF 格式的,但网上的说明讲解不清楚,
而且有些几个关键数据弄成一个位,这个很麻烦。(又不知道那些数据有什么用)
之后想研究jpg,但jpg的延伸格式多的很...最后就研究了bmp了= =

写解码器的工作量还是挺大的,像 ffmpeg 这样的工程基本上不可能一个人完成。不过,在钻研的过程中确实能学到不少知识 :)
作者: 一箭烂YiJL    时间: 2011-5-29 16:29
苏小脉 发表于 2011-5-29 12:17
做面向对象编程的时候,为什么要设计这么多类,又让类的成员引用来引用去?
做面向对象编程的时候,为什么要设计这么多类,又让类的成员引用来引用去?

但我认为非 Ruby 的话,不需要这样指来指去。(Ruby 接驳的 C 就不知道了)

rgss_bminfo_t#first 是实际的像素数据的最后一行开头

可能是我不明白吧~...我只能这样说:
最后的一次 RtlMoveMemory 之前的偏移 16 和 12 得出的指针位置是一样的,
所以我认为没分别 rgss_bminfo_t#last 和 rgss_bminfo_t#first...

同时 BMP 还有自己的压缩标准,支持简单的 RLE、霍夫曼压缩,当然基本没什么实用价值。

BMP 原意似乎是不压缩。但BitmapInfoHeader中[40, w, h, 1, 32, 0, 0, ......]
(红色部分)就决定了压缩法(与否),而橙色部分就是跟压缩后大小什么的有关(没记错的话)。
作者: summer92    时间: 2011-5-29 20:58
强大的东西
作者: 苏小脉    时间: 2011-5-30 02:25
标题: RE: Bitmap 类储存为 .bmp 档案(高速= =)
一箭烂YiJL 发表于 2011-5-29 16:29
但我认为非 Ruby 的话,不需要这样指来指去。(Ruby 接驳的 C 就不知道了)
但我认为非 Ruby 的话,不需要这样指来指去。(Ruby 接驳的 C 就不知道了)

先声明一点,“引用”的概念是计算机科学中的一个通用概念,并非面向对象的语言的专利。只不过当我们在面向对象语言上下文中提起引用时,通常是特指被 GC 管理的引用。下文的“引用”均泛指间接访问另一个实体的名称绑定的概念,而非 GC 管理的特殊引用。

在面向对象的语言中,一个类的成员可以引用(绑定)一个类的实例,建立 has-a 关系。同样地,在 C 中一个结构也可以和一个实体(可以是原始数据类型结构体联合体或是枚举的实例)建立 has-a 关系,而 C 引用实体的方式就是指针。当使用了引用后,就有了以下好处:

1、引用的实体的存储位置更加灵活

结构的成员引用的实体所占据的内存可以是在任何有效的位置分配的。之后即便是结构本身迁移了,只要引用没有改变,我们仍然可以间接访问到实体,而不需要连实体本身也一并迁移。比如:你来到办公大楼南门的接待处,要找张三(实体),接待员(引用)告诉你张三的房间号(地址)。两个月后,接待处搬迁到北门,但张三的房间号没变,你只要能去北门的接待处,仍然可以访问到同一个张三。

2、引用的实体的分配方式更加灵活

结构的成员引用的实体所占据的内存可以是以任何有效的方式分配的——可以分配为局部栈内存、动态堆内存、静态全局内存甚至是常量内存。我们的结构只是引用它,我们并没有创造它、分配它。比如:接待处的接待员可能根本就不认识张三,也不知道他是什么时候来这幢楼上班的,但总之他有(从上司那里拿到的)张三的楼层和房间号,能让来宾间接访问到他。

3、轻量化

结构的成员引用的实体本身可能是很庞大的,如果某个成员不是结构的引用而直接是结构的值,那么相应的包容了这个结构的容器结构也会变得很庞大。每次对容器结构进行传递或是赋值操作时,都会涉及到整个结构的拷贝,其开销是和结构体大小成正比的。比如:

  1. struct A
  2. {
  3.     int a, b, c, d, e, f, g, h;
  4. };

  5. struct B
  6. {
  7.     struct A a;
  8.     struct B b;
  9. };

  10. struct B b;
  11. struct A a;
  12. b.a = a;
复制代码
sizeof (A) = 32 字节。由于 B#a 和 B#b 都不是指针类型,而是结构实体(值)本身,所以 sizeof (B) = 64 字节。`b.a = a;' 这一句,就要涉及到 32 个字节的拷贝。如果让 B#a 和 B#b 都变为指针,sizeof (A) 就可以是 8 字节,而 `b.a = a;' 也仅仅是 8 个字节的拷贝而已。在函数传参的时候,也需要考虑类似的问题——传值还是传引用。

4、共享内存

两个结构可能都有一个某类型的成员,但希望能共享其实体,而不是各自拥有一份拷贝。这时使用引用,就可以实现共享。比如:张三(实体)可能是一位知名的经纪人,并且全市独一无二。客户无论是去城西的介绍所和还是去城东的介绍所都能通过当地的工作人员获取到张三的引用并间接找到张三真正的位置。这样一来,就不需要在两个介绍所都安插自己的相互独立的经纪人。

当然,以上只是为了说明为什么要在结构中用引用。实践时合理地在结构体里直接包含另一个结构的值也是可能的。比如一个结构体本身就需要这么多值域,其中一部分可以单独被包装到另一个合理的结构(命名空间)中统一管理,但实际上整个大结构仍然是连续的内存。

很多编程思想其实并不限于语言,像 C 就完全可以写出带面向对象味道的代码,只是很多人不承认这一点而已。

可能是我不明白吧~...我只能这样说:
最后的一次 RtlMoveMemory 之前的偏移 16 和 12 得出的指针位置是一样的,
所以我认为没分别 rgss_bminfo_t#last 和 rgss_bminfo_t#first...

指针位置(指针的地址)肯定是不同的,除非 16 == 12。如果你是说指针指向的地址(指针的值),我这里测试是不同的,不知道你那边是如何测试的。

BMP 原意似乎是不压缩。但BitmapInfoHeader中[40, w, h, 1, 32, 0, 0, ......]
(红色部分)就决定了压缩法(与否),而橙色部分就是跟压缩后大小什么的有关(没记错的话)。

http://msdn.microsoft.com/en-us/library/dd183376(v=vs.85).aspx
第六个域是压缩方式,第七个域是图像(字节)大小。未经压缩的不需要这个值也能确定大小,所以如果格式为 BI_RGB,biSizeImage 就为 0。

summer92  话说同格式的文件类型jpg,png,bmp文件头应该一样的把,音乐格式也是,- -

很多都不同。16 位以上 BMP 格式是简单的头 + 像素数据;8 位及 8 位以下的 BMP 格式是头 + 调色板 + 索引表。JPEG 格式是分段的,每一段都有各自的作用,像图像开头、结尾、霍夫曼表、注释等等都自成一段;每段由一个标记开头,每个标记两个字节,由 0xff 开头。GIF 也是分段的,只不过段落开头的标记是一个字节(即没有像 JPEG 那样固定的 0xff)。PNG 在开头会有 8 个固定字节的签名。音频格式泰半也都不同。
作者: 一箭烂YiJL    时间: 2011-5-30 18:27
苏小脉 发表于 2011-5-30 02:25
先声明一点,“引用”的概念是计算机科学中的一个通用概念,并非面向对象的语言的专利。只不过当我们在 ...
“引用”

我想 Bitmap 指针可能的其中一个原因是 Bitmap#get_pixel、set_pixel、blur......
就可以引用位图的("同一个")颜色值数据作修改、读取......

如果让 B#a 和 B#b 都变为指针,sizeof (A) 就可以是 8 字节

"sizeof (B) 就可以是 8 字节"?

而 `b.a = a;' 也仅仅是 8 个字节的拷贝而已。

8 个字节?不解...

我这里测试是不同的,不知道你那边是如何测试的。

抱歉...我傻呆呆的用了 Bitmap.new(1, 1) 来测试~

http://msdn.microsoft.com/en-us/library/dd183376(v=vs.85).aspx

然全不知道 MSDN 有记载 Bmp 格式,但是没有给 压缩方式的域 的值(数字)讲解,
但是我记得顺序是 0~5 。我之前想压缩的时候用 png 的压缩法(Zlib 的那个),
将 biSizeImage 设置为压缩后的字节的 size。(可能是测试出错或是方法错误什么的),
出来的 bmp 说是损坏档案......
作者: 苏小脉    时间: 2011-5-31 08:34
一箭烂YiJL 发表于 2011-5-30 18:27
我想 Bitmap 指针可能的其中一个原因是 Bitmap#get_pixel、set_pixel、blur......
就可以引用位图的("同 ...

什么 Bitmap 指针?

"sizeof (B) 就可以是 8 字节"?

是的,我前一帖有 typo。

8 个字节?不解...

typo,32 位取址模型下应该是 4 个字节。在 64 位取址模型下自然就是 8 个字节了。

“而 `b.a = a;' 也仅仅是 8 个字节的拷贝而已。”

这句话应该改为:

“而 `b.a = &a;' 也仅仅是 4 个字节的拷贝而已。”

然全不知道 MSDN 有记载 Bmp 格式,但是没有给 压缩方式的域 的值(数字)讲解,
但是我记得顺序是 0~5 。我之前想压缩的时候用 png 的压缩法(Zlib 的那个),
将 biSizeImage 设置为压缩后的字节的 size。(可能是测试出错或是方法错误什么的),
出来的 bmp 说是损坏档案......

微软的文档做得还是比较彻底的,微软自己的格式当然要有文档。

又不是想用什么方法压缩就用什么方法压缩,得看 BMP 的标准。你编码端用 Zlib 压缩,人家解码端是根据标准来的,自然不会尝试去用 Zlib 解码。根据微软的文档,NT 之后似乎只有 RLE 压缩,霍夫曼的都是遗留系统(OS/2)的标准了。
作者: 一箭烂YiJL    时间: 2011-5-31 16:51
苏小脉 发表于 2011-5-31 08:34
什么 Bitmap 指针?
什么 Bitmap 指针?

我有语法问题,应该是Bitmap 的那些结构体用指针指来指去。

又不是想用什么方法压缩就用什么方法压缩

我用的是 Zlib 的 DEFLATE (LZ77 + 霍夫曼) 算法,这样能够在 png 里解读。(研究 PNG 的时候发现 Zlib 还有 CRC 校验的算法)
作者: 苏小脉    时间: 2011-5-31 20:00
一箭烂YiJL 发表于  
我有语法问题,应该是Bitmap 的那些结构体用指针指来指去。
可能的其中一个原因是 Bitmap#get_pixel、set_pixel、blur......
就可以引用位图的("同一个")颜色值数据作修改、读取......

那这是什么意思呢?

我用的是 Zlib 的 DEFLATE (LZ77 + 霍夫曼) 算法,这样能够在 png 里解读。(研究 PNG 的时候发现 Zlib 还有 CRC 校验的算法)

PNG 是另一种格式,标准自然不同。
作者: 一箭烂YiJL    时间: 2011-5-31 20:14
苏小脉 发表于 2011-5-31 20:00
那这是什么意思呢?
那这是什么意思呢?

于是我想说...算了...其实我那句也应该是错的...

PNG 是另一种格式,标准自然不同。

微软那里说 BMP 可以用 PNG 的压缩法,PNG 的压缩法就是 DEFLATE ( / Gzip)....
作者: 苏小脉    时间: 2011-6-1 08:16
一箭烂YiJL 发表于  
于是我想说...算了...其实我那句也应该是错的...

微软哪里说的?我查阅的材料都只提到了 RLE,霍夫曼都仅仅是存在于 OS/2 的 BMP 标准中。
作者: 一箭烂YiJL    时间: 2011-6-2 21:40
苏小脉 发表于 2011-6-1 08:16
微软哪里说的?我查阅的材料都只提到了 RLE,霍夫曼都仅仅是存在于 OS/2 的 BMP 标准中。 ...

应该是我误会了吧~
MSDN 指出 BitmapInfoHeader 的第六个域(压缩方式)可以为 BI_PNG ,描述的译文:
表示这是一幅 PNG 图像。

于是我就用了 PNG 的压缩方式,可能是我误会其中的意思吧~
作者: 苏小脉    时间: 2011-6-3 10:52
一箭烂YiJL 发表于 2011-6-2 21:40
应该是我误会了吧~
MSDN 指出 BitmapInfoHeader 的第六个域(压缩方式)可以为 BI_PNG ,描述的译文:

这两个域是用在打印机硬件加速时的。

http://msdn.microsoft.com/en-us/library/dd145023(VS.85).aspx

可以把 PNG 或 JPEG 和头一起直接传给硬件支持 JPEG 和 PNG 的打印机,硬件渲染。

These compression values are only valid for SetDIBitsToDevice and StretchDIBits when the hdc parameter specifies a printer device.

翻译成汉文:
这些压缩值仅对指定了 hDC 参数为打印机设备的 SetDIBitsToDevice 和 StretchDIBits 有效。
作者: 一箭烂YiJL    时间: 2011-6-4 15:17
苏小脉 发表于 2011-6-3 10:52
这两个域是用在打印机硬件加速时的。

http://msdn.microsoft.com/en-us/library/dd145023(VS.85).aspx

果然是我误会了 orz ,但是这两个域为什么叫压缩方式呢?
作者: 苏小脉    时间: 2011-6-5 00:18
一箭烂YiJL 发表于  
果然是我误会了 orz ,但是这两个域为什么叫压缩方式呢?

JPG 和 PNG 都是压缩格式,在这个域指定 JPG 或者 PNG 就告诉了打印机,接下来的数据不是 BMP,而是 JPG 或 PNG。
作者: API_Studier    时间: 2011-6-19 14:35
本人还是得纠个错,这样得到的事实上是BGRA四个通道 ,而并非RGBA(真不知道Bitmap中怎么什么东西都是倒过来的)……
本人现在正在研究PNG格式,想将体积大的BMP转为小巧又相对简单的PNG格式(不像JPG那么烦),可惜通过将每一个BGRA数组都转化为RGBA的时间实在太长,不知有否高人相助!




欢迎光临 Project1 (https://rpg.blue/) Powered by Discuz! X3.1