读书人

问个关于多层图片叠加的有关问题

发布时间: 2012-03-08 13:30:13 作者: rapoo

问个关于多层图片叠加的问题
图层数目不固定,而且各个图层要有叠加顺序,
除了一张背景图(24位)外,其余各个图层均为32位的,并要在这些图层上作操作,比如在某个图层上输出文字,或是再叠加其它的小图片(PNG格式),
最关键的是,以什么方式将每个图层都以透明的方式叠加到背景图片上?
而且各个图层的操作是独立的,不会影响到其它的图层,
我现在采用的是 :将 24位 的背景图与32位的图层经过混算后达到透明,再生成一张24位的图片,然后再在paintbox上画的方法,
但是这种方法 在修改任何一个图层的图像时,都要求重新生成一次背景图,再与32位的图层进行混算,操作太麻烦,而且如果图层太多时,速度也太慢,不知哪位大侠有过类似的经验?

[解决办法]
没有什么办法了。
优化下代码,再改成多线程的吧。
[解决办法]
这个只能这样,多图层的显示你只能先计算混合后的效果,在输出,但是对于大图你没有必要计算全部范围,只需要计算需要显示的那部分。
[解决办法]
这个就是你的优化不够了
我的imageshop也是每一个层一个类,然后输出时候计算混合结果,但是在每层都是桌面大小的情况下,5层之类还算是不怎么卡的。
[解决办法]
还是说说你是怎么画透明图上去的吧,更新一张1k*1k图20ms怎么都够了,就算7、8年前的老机器40ms也足够了,小图每个时间应该更短才对

另外不知道是不是许多人觉得多线程就一定更快
多线程只在cpu有闲置的时候才起作用,在密集运算的时候除非是多核cpu且线程数不多于核数,否则只会让程序变得更慢

[解决办法]
不是png么?既然都用了gdip了,那就直接用gdip一个个画出来不就完了么?
gdip除了画单个点有点儿麻烦外,剩下的操作都不比vcl的gdi封装更复杂啊

[解决办法]
关于多层图形操作,不用多线程就不会很流畅,一般的做法是一个线程管一层。

上面也有人说了,多线程的总体效率未必比单一线程好,这是对的,但问题在于:单一线程影响其他操作,比如主界面更新问题(某段时间类似死机),甚至导致想切换到其他程序,都困难。

所以,用多线程不是为了时髦,也不是为了效率,而是为了更好的应用性能!
[解决办法]
你的bmp不是作为背景图层么?它透明不透明没意义啊
刚才试了一下,在我的 AMD Athlon 64 2800+(05年配的)机器上,在TImage上画一个1280*1024的图片,把一个240*200的带alpha通道的png图片随机画10次,都是在100ms内完成的。对于用户体验来说,100ms基本上不会造成显著的延迟感

Delphi(Pascal) code
  tm  := GetTickCount; { tm: DWORD }  Image1.Canvas.Draw(0, 0, bmp); { bmp: TBitmap; PixelFormat=pf24bit, Width=1280, Height=1024 前面已经准备好了 }  g := TGPGraphics.Create(Image1.Canvas.Handle); { g: TGPGraphics; Image1: TImage; Width=1280, Height=1024 }  for i:=1 to 10 do  with Image1 do    g.DrawImage(gp, Random(Width), Random(Height)); { gp: TGPBitmap 装入一张240*200的png图片,前面已经准备好了 }  Caption := IntToStr(GetTickCount - tm);
[解决办法]
楼上的,多图层混合一般请情况下不能用DrawImage函数的,因为每个图层可能带有不同的属性,比如某个层的整体透明度是40%,他的混合方式是屏幕,这些都不是DrawImage所能完成的,都只有通过自己的计算才能得到最终的结果。
[解决办法]
思路有问题,不应该与背景混算透明叠加,
直接用GDI+的DrawImage方法画到有背景图的PaintBox上就行了,
只需要记住每个图层的坐标
[解决办法]
1: layer里的小图片不能用单纯擦除技术而要全部重绘?
2: 小图片是不规则的且每次帖上layer时都要和layer原图作透明运算?
3: 小图片是动态生成的,而不是load文件load出来的?
4: 所有的一切都要实时计算,而不能预先存为文件,用空间换时间?


按你所说,其实你只要解决了小图贴上那两个layer的速度,你就可以解决你的问题了?
[解决办法]
下面给一个32位图片合成代码,应先调用InitSysthesisAplhaMask,一次性的,不必每次调用,最后还应GlobalFreePtr(SysthesisAplhaMask)释放它。
注意:我这段代码是从一个类方法改的,没作测试。如果图层一样大,且没有局部合成,TImageData里的Offset可以不用,如果你要合成24位和32位,得修改。

Delphi(Pascal) code
  TImageData = packed record    Width: Integer;         // 图像宽度    Height: Integer;        // 图像高度    Stride: Integer;        // 图像扫描线字节长度    Offset: Integer;        // 扫描线偏移量    Scan0: Pointer;         // 图像数据地址  end;var  SysthesisAplhaMask: Pointer;procedure InitSysthesisAplhaMask;begin  SysthesisAplhaMask := GlobalAllocPtr(GMEM_MOVEABLE, 256 * 2 * 8);  asm    push      esi    pcmpeqb   mm7, mm7    psrlw     mm7, 8          // mm7 = 00 ff 00 ff 00 ff 00 ff    mov       esi, SysthesisAplhaMask    xor       ecx, ecx  @SumLoop:    mov       eax, ecx    shl       eax, 16    or        eax, ecx    movd      mm0, eax    punpcklwd mm0, mm0        // mm0 = 00 sA 00 sA 00 sA 00 sA    movq      [esi], mm0    movq      mm1, mm7        // mm1 = 00 ff 00 ff 00 ff 00 ff    pxor      mm1, mm0        // mm1 = 00 dA 00 dA 00 dA 00 dA    movq      [esi + 8], mm1    add       esi, 16    inc       ecx    cmp       ecx, 256    jl        @SumLoop    emms    pop       esi  end;end;procedure PixelSysthesis(Dest, Source: TImageData);var  srcOffset, dstOffset, Width: LongWord;asm    push      esi    push      edi    push      ebx    mov       esi, [edx].TImageOperate.Scan0    mov       edi, [eax].TImageOperate.Scan0    mov       ecx, [edx].TImageOperate.Width    mov       ebx, [edx].TImageOperate.Offset    mov       eax, [eax].TImageOperate.Offset    mov       edx, [edx].TImageOperate.Height    mov       Width, ecx    test      ecx, 1    jz        @@1    add       ebx, 4    add       eax, 4    jz        @@1  @@1:    pxor      mm7, mm7        // mm7 = 00 00 00 00 00 00 00 00    pcmpeqb   mm6, mm6    psrlw     mm6, 8          // mm6 = 00 ff 00 ff 00 ff 00 ff    mov       srcOffset, ebx    mov       dstOffset, eax    mov       ebx, SysthesisAplhaMask    shr       ecx, 1          // 每次合成2个像素    cld  @yLoop:    push      ecx  @xLoop:    Dec       ecx    js        @@3    movq      mm0, [esi]      // mm0 = As1 Rs1 Gs1 Bs1 As0 Rs0 Gs0 Bs0    movq      mm2, [edi]      // mm1 = Ad1 Rd1 Gd1 Bd1 Ad0 Rd0 Gd0 Bd0    movq      mm1, mm0    movq      mm3, mm2    punpcklbw mm0, mm7        // mm0 = 00 As0 00 Rs0 00 Gs0 00 Bs0    punpcklbw mm2, mm7        // mm2 = 00 Ad0 00 Rd0 00 Gd0 00 Bd0    punpckhbw mm1, mm7        // mm1 = 00 As1 00 Rs1 00 Gs1 00 Bs1    punpckhbw mm3, mm7        // mm3 = 00 Ad1 00 Rd1 00 Gd1 00 Bd1    movzx     eax, [esi + 3]    shl       eax, 4    pmullw    mm0, [ebx + eax]    // mm0 = As0*sA Rs0*sA Gs0*sA Bs0*sA    pmullw    mm2, [ebx + eax + 8]// mm2 = Ad0*dA Rd0*dA Gd0*dA Bd0*dA    movzx     eax, [esi + 7]    shl       eax, 4    pmullw    mm1, [ebx + eax]    // mm1 = As1*sA Rs1*sA Gs1*sA Bs1*sA    pmullw    mm3, [ebx + eax + 8]// mm3 = Ad1*dA Rd1*dA Gd1*dA Bd1*dA    paddw     mm0, mm2        // mm0 = 00 An0 00 Rn0 00 Gn0 00 Bn0    paddw     mm1, mm3        // mm1 = 00 An1 00 Rn1 00 Gn1 00 Bn1    paddw     mm0, mm6        // mm0 = An0+ff Rn0+ff Gn0+ff Bn0+ff    paddw     mm1, mm6        // mm1 = An1+ff Rn0+ff Gn0+ff Bn0+ff    psrlw     mm0, 8          // mm0 = An0/256 Rn0/256 Gn0/256 Bn0/256    psrlw     mm1, 8          // mm0 = An1/256 Rn1/256 Gn1/256 Bn1/256    packuswb  mm0, mm1        // mm0 = An1 Rn1 Gn1 Bn1 An0 Rn0 Gn0 Bn0    movq      [edi], mm0    add       esi, 8    add       edi, 8    jmp       @xLoop  @@3:    test      Width, 1    jz        @@2    movzx     eax, [esi + 3]    shl       eax, 4    movd      mm0, [esi]      // mm0 = 00 00 00 00 As Rs Gs Bs    movd      mm1, [edi]      // mm1 = 00 00 00 00 Ad Rd Gd Bd    punpcklbw mm0, mm7        // mm0 = 00 As 00 Rs 00 Gs 00 Bs    punpcklbw mm1, mm7        // mm1 = 00 Ad 00 Rd 00 Gd 00 Bd    pmullw    mm0, [ebx + eax]    // mm0 = As*sA Rs*sA Gs*sA Bs*sA    pmullw    mm1, [ebx + eax + 8]// mm1 = Ad*dA Rd*dA Gd*dA Bd*dA    paddw     mm0, mm1        // mm0 = 00 An 00 Rn 00 Gn 00 Bn    paddw     mm0, mm6        // mm0 = An+ff Rn+ff Gn+ff Bn+ff    psrlw     mm0, 8          // mm0 = An/256 Rn/256 Gn/256 Bn/256    packuswb  mm0, mm0        // mm0 = 00 00 00 00 An Rn Gn Bn    movd      [edi], mm0  @@2:    add       esi, srcOffset    add       edi, dstOffset    pop       ecx    dec       edx    jnz       @yLoop    emms    pop       ebx    pop       edi    pop       esiend; 


[解决办法]
想了半天,与其给你一大堆理论建议,不如直接推荐一个支持图层和Alpha通道的库:P:http://graphics32.org
[解决办法]
哦,那用简单擦除ms就可以不用全部重新往BG上计算帖图啊.

例如要擦除某个元素,只需要往该区域矩形把原来的BG图该区域帖回去不就擦除了?
而假如还有元素在上面覆盖着,那也只需要计算该区域的合成运算而不用整个图重来啊?

[解决办法]
操作图层时,应该只需要处理某个区域内的数据,而不需要处理所有的区域,这样速度会快很多

读书人网 >.NET

热点推荐