asp.net生成透明gif的准完美方案

       我最近在实现我的WMS服务器的时候,发现了一个超级郁闷的问题,问题描述如下:客户需要动态叠加一个透明层到底图上,比如说公交线路层,这个层有透明背景,于是可以叠加到其他图层上去。使用openlayer动态叠加图层很方便,我想这还不容易,使用透明png不就OK了,测试结果表明,在我机器的IE8上,非常正常。我正暗自得意,另一位兄弟用IE6一看,完蛋了,IE6不支持透明PNG,汗,这下要了命了,赶紧翻openlayers的资料,发现可以针对IE6特别使用透明滤镜。使用方法很方便,只要设置要透明的层 属性 alpha=true即可。测试后发现,透明是透明了,但是由于图片瓦块数过多,导致IE6被拖得半死,反应缓慢。汗,于是我想,那就对路线层使用gif格式呗,gif不也可以透明背景么。于是,悲剧出现了。为了解说清楚,我以一个PLMM为模特,嘿嘿

              首先呢,我们准备一个PLMM,嘿嘿:

       大家看到PLMM帽子边上的那个黄色圆圈了吗?一会我们就要将它变成透明的,生成一张可以在IE6中透明的GIF图。

      首先呢,我们使用常规方式:

1Dim gif1 As New Bitmap("plmm.jpg")
 2        gif1.MakeTransparent(Color.Yellow)
 3        PictureBox1.Image = gif1
 4
 5        '这里我将它保存为gif到流里,这里是内存流,在asp.net中则是outputstream ,再显示在picturebox2里
 6        Dim ms As New System.IO.MemoryStream()
 7        gif1.Save(ms, Imaging.ImageFormat.Gif)
 8
 9        Dim gif2 = Bitmap.FromStream(ms)
10        ms.Dispose()
11
12        PictureBox2.Image = gif2
13
14        '***变黑了****

这个结果是十分令人沮丧的:那个圆圈非但没透明,甚至还变成了黑色:

那么如果我直接保存为文件呢?图我就不贴了,和上面一样,还是黑的。

1        Dim gif1 As New Bitmap("plmm.jpg")
2        gif1.MakeTransparent(Color.Yellow)
3        PictureBox1.Image = gif1
4
5        '***直接保存为文件:***
6        gif1.Save("out1.gif", Imaging.ImageFormat.Gif)
7
8        '***还是黑的**********
9        PictureBox2.ImageLocation = "out1.gif"

 这是我网上进行了一番搜索,大致上可以搜到这么一个版本:先修改调色板,然后再另外拷贝数据。

大致原理是:GIF是一种索引图像,最大色彩数256色 (PS:其实这个色彩数不少了,想当年,我上学时候经常逃课去玩

世嘉五代 MD游戏机,那个同屏发色数才16色,同期的超级任天堂的同屏发色数也不过256色,呵呵,这些都是从<<电子游戏软件>>里看来的)

GIF有一个调色板,最大有256个颜色索引,然后每个像素保存的是调色板的颜色索引值。故此,只需修改调色板,即可调整

图像的颜色。

OK,那么我们来改下调色板:

1        '***我们读取上一步生成的不透明gif
 2        Dim gif As New Bitmap("out1.gif")
 3        PictureBox1.Image = gif
 4
 5        '***获取色板***
 6        Dim pal = gif.Palette
 7
 8        For i As Integer = 0 To pal.Entries.Length - 1
 9            Dim color = pal.Entries(i)
10            '***将黑色改为透明,并且玩个反色特效,嘿嘿***
11            If color.R = 0 And color.G = 0 And color.B = 0 Then
12                pal.Entries(i) = color.Transparent
13            Else
14                pal.Entries(i) = color.FromArgb(255, color.B, color.G, color.R)
15            End If
16
17        Next
18
19        Dim gif2 As Bitmap = gif.Clone()
20        gif2.Palette = pal
21
22        PictureBox2.Image = gif2

结果如图:

看起来效果不错,纯黑色部分被透明了!而且么,还出来个反色特效,HOHO

不过你不要高兴得太早,如果这时候你保存的话,它还是黑的!!

这是我们需要使用拷贝位图数据的办法来处理:

1'***我们读取上一步生成的不透明gif
 2        Dim gif As New Bitmap("out1.gif")
 3        PictureBox1.Image = gif
 4
 5        '***获取色板***
 6        Dim pal = gif.Palette
 7
 8        For i As Integer = 0 To pal.Entries.Length - 1
 9            Dim color = pal.Entries(i)
10            '***将黑色改为透明***
11            If color.R = 0 And color.G = 0 And color.B = 0 Then
12                pal.Entries(i) = color.Transparent 
13            End If
14        Next
15
16        '***另外创建一个位图,格式为8位索引色**
17        Dim gif2 As New Bitmap(gif.Width, gif.Height, Imaging.PixelFormat.Format8bppIndexed)
18        '***设置修改后的调色板***
19        gif2.Palette = pal
20        '***拷贝内存
21
22        Dim src = gif.LockBits(New Rectangle(0, 0, gif.Width, gif.Height), Imaging.ImageLockMode.ReadOnly, gif.PixelFormat)
23        Dim trg = gif2.LockBits(New Rectangle(0, 0, gif2.Width, gif2.Height), Imaging.ImageLockMode.WriteOnly, gif2.PixelFormat)
24
25        Dim bits(src.Stride * src.Height - 1) As Byte
26        System.Runtime.InteropServices.Marshal.Copy(src.Scan0, bits, 0, bits.Length)
27        System.Runtime.InteropServices.Marshal.Copy(bits, 0, trg.Scan0, bits.Length)
28
29        gif.UnlockBits(src)
30        gif2.UnlockBits(trg)
31
32
33        '***这时再保存,看看吧!***
34        gif2.Save("out3.gif")
35
36        Me.PictureBox2.ImageLocation = "out3.gif"
37

这时,我们保存了一个透明背景的gif图像!这个out3.gif,是背景透明的!

不过,如果你这时候认为大功告成的话,那可就错了,嘿嘿,这事情就是这么麻烦,请看:

1'***我们读取上一步生成的不透明gif
 2        Dim gif As New Bitmap("out1.gif")
 3        PictureBox1.Image = gif
 4
 5        '***获取色板***
 6        Dim pal = gif.Palette
 7
 8        For i As Integer = 0 To pal.Entries.Length - 1
 9            Dim color = pal.Entries(i)
10            '***将黑色改为透明***
11            If color.R = 0 And color.G = 0 And color.B = 0 Then
12                pal.Entries(i) = color.Transparent
13            End If
14        Next
15
16        '***另外创建一个位图,格式为8位索引色**
17        Dim gif2 As New Bitmap(gif.Width, gif.Height, Imaging.PixelFormat.Format8bppIndexed)
18        '***设置修改后的调色板***
19        gif2.Palette = pal
20        '***拷贝内存
21
22        Dim src = gif.LockBits(New Rectangle(0, 0, gif.Width, gif.Height), Imaging.ImageLockMode.ReadOnly, gif.PixelFormat)
23        Dim trg = gif2.LockBits(New Rectangle(0, 0, gif2.Width, gif2.Height), Imaging.ImageLockMode.WriteOnly, gif2.PixelFormat)
24
25        Dim bits(src.Stride * src.Height - 1) As Byte
26        System.Runtime.InteropServices.Marshal.Copy(src.Scan0, bits, 0, bits.Length)
27        System.Runtime.InteropServices.Marshal.Copy(bits, 0, trg.Scan0, bits.Length)
28
29        gif.UnlockBits(src)
30        gif2.UnlockBits(trg)
31
32
33        '***OK,这时候,我把它保存到流里***
34        Dim ms As New System.IO.MemoryStream()
35        gif2.Save(ms, Imaging.ImageFormat.Gif)
36
37        gif2.Dispose()
38
39        Dim gif3 As Bitmap = Bitmap.FromStream(ms)
40        ms.Dispose()
41
42        Me.PictureBox2.Image = gif3
43
44        '这时候你看到了,透明色又顽固地被去掉了,只是原来是黑色,现在成了白色
45

 透明色又消失了,只是这次变成了白色-_____________________-

那么为啥保存为文件就可以,而保存到流却不行呢?

事情到了这一步,只好祭出Reflector了,看看M$到底是怎么保存的,实在不行我把保存为文件的代码拷贝一份出来再往流里写

按照Reflector的说法,M$在保存文件时,使用的是RawFormat:

1public void Save(string filename)
2{
3    this.Save(filename, this.RawFormat);
4}

好吧,那我也传个 RawFormat进去

1'***我们读取上一步生成的不透明gif
 2        Dim gif As New Bitmap("out1.gif")
 3        PictureBox1.Image = gif
 4
 5        '***获取色板***
 6        Dim pal = gif.Palette
 7
 8        For i As Integer = 0 To pal.Entries.Length - 1
 9            Dim color = pal.Entries(i)
10            '***将黑色改为透明***
11            If color.R = 0 And color.G = 0 And color.B = 0 Then
12                pal.Entries(i) = color.Transparent
13            End If
14        Next
15
16        '***另外创建一个位图,格式为8位索引色**
17        Dim gif2 As New Bitmap(gif.Width, gif.Height, Imaging.PixelFormat.Format8bppIndexed)
18        '***设置修改后的调色板***
19        gif2.Palette = pal
20        '***拷贝内存
21
22        Dim src = gif.LockBits(New Rectangle(0, 0, gif.Width, gif.Height), Imaging.ImageLockMode.ReadOnly, gif.PixelFormat)
23        Dim trg = gif2.LockBits(New Rectangle(0, 0, gif2.Width, gif2.Height), Imaging.ImageLockMode.WriteOnly, gif2.PixelFormat)
24
25        Dim bits(src.Stride * src.Height - 1) As Byte
26        System.Runtime.InteropServices.Marshal.Copy(src.Scan0, bits, 0, bits.Length)
27        System.Runtime.InteropServices.Marshal.Copy(bits, 0, trg.Scan0, bits.Length)
28
29        gif.UnlockBits(src)
30        gif2.UnlockBits(trg)
31
32
33        '***按照reflector的说法,它使用的是RawFormat***
34        Dim ms As New System.IO.MemoryStream()
35        Try
36            gif2.Save(ms, gif2.RawFormat)
37        Catch ex As Exception
38            MsgBox(ex.ToString(), MsgBoxStyle.Critical)
39        Finally
40
41            gif2.Dispose()
42
43        End Try
44
45

结果如图:这真是太BT了,我受不了了!!!

经过比较发现,M$的两个保存是不一样的,保存为文件时,多了一个判断!保存到文件时,M$会调用png编码器,而保存到流则不会

1public void Save(string filename, ImageFormat format)
 2{
 3    if (format == null)
 4    {
 5        throw new ArgumentNullException("format");
 6    }
 7    ImageCodecInfo encoder = format.FindEncoder();
 8    if (encoder == null)  //这里比保存到流多了个判断!
 9    {
10        encoder = ImageFormat.Png.FindEncoder();
11    }
12    this.Save(filename, encoder, null);
13}
14
15public void Save(Stream stream, ImageFormat format)
16{
17    if (format == null)
18    {
19        throw new ArgumentNullException("format");
20    }
21    ImageCodecInfo encoder = format.FindEncoder();
22    //这里没有那个获取默认编码器的步骤!!!
23   
24    this.Save(stream, encoder, null);
25}
26
27 
28
29 
30

OK,既然是这样,那么我就在外面调用一下那个传说中的 ImageFormat.Png.FindEncoder(); 不就得了吗?你要是这样想那就太天真了:

1internal ImageCodecInfo FindEncoder()
 2{
 3    foreach (ImageCodecInfo info in ImageCodecInfo.GetImageEncoders())
 4    {
 5        if (info.FormatID.Equals(this.guid))
 6        {
 7            return info;
 8        }
 9    }
10    return null;
11}

看到了吧,FindEncoder()方法是internal的 -______-,瀑布汗啊瀑布汗

不过还好,天无绝人之路,Save还有一个重载:

public void Save(Stream stream, ImageCodecInfo encoder, EncoderParameters encoderParams) 我们可以直接传Png的ImageCodecInfo进去,嘿嘿,我们可以把png的ImageCodeInfo给穷举找出来!

1Private Shared Function GetEncoderInfo()Function GetEncoderInfo(ByVal mimeType As String) As Imaging.ImageCodecInfo
2            For Each ecoder In Imaging.ImageCodecInfo.GetImageEncoders()
3                If ecoder.MimeType = mimeType Then
4                    Return ecoder
5                End If
6            Next
7            Return Nothing
8        End Function

至此,我们终于制作了一个可以在IE6中拥有透明背景色的"gif"图像,不过实际上,这个其实不是gif格式的,实际上

是8位色png格式的,不过不管怎么说,这个好歹可以在IE6里透明了,而且使用上没有区别。除了不能动画,汗。

最后的实现代码:

1'***我们读取上一步生成的不透明gif
 2        Dim gif As New Bitmap("out1.gif")
 3        PictureBox1.Image = gif
 4
 5        '***获取色板***
 6        Dim pal = gif.Palette
 7
 8        For i As Integer = 0 To pal.Entries.Length - 1
 9            Dim color = pal.Entries(i)
10            '***将黑色改为透明***
11            If color.R = 0 And color.G = 0 And color.B = 0 Then
12                pal.Entries(i) = color.Transparent
13            End If
14        Next
15
16        '***另外创建一个位图,格式为8位索引色**
17        Dim gif2 As New Bitmap(gif.Width, gif.Height, Imaging.PixelFormat.Format8bppIndexed)
18        '***设置修改后的调色板***
19        gif2.Palette = pal
20        '***拷贝内存
21
22        Dim src = gif.LockBits(New Rectangle(0, 0, gif.Width, gif.Height), Imaging.ImageLockMode.ReadOnly, gif.PixelFormat)
23        Dim trg = gif2.LockBits(New Rectangle(0, 0, gif2.Width, gif2.Height), Imaging.ImageLockMode.WriteOnly, gif2.PixelFormat)
24
25        Dim bits(src.Stride * src.Height - 1) As Byte
26        System.Runtime.InteropServices.Marshal.Copy(src.Scan0, bits, 0, bits.Length)
27        System.Runtime.InteropServices.Marshal.Copy(bits, 0, trg.Scan0, bits.Length)
28
29        gif.UnlockBits(src)
30        gif2.UnlockBits(trg)
31
32
33
34        Dim ms As New System.IO.MemoryStream()
35
36        gif2.Save(ms, GetEncoderInfo("image/png"), Nothing)
37        gif2.Dispose()
38
39        Dim gif3 = Bitmap.FromStream(ms)
40
41        Me.PictureBox2.Image = gif3
42

最终正确的结果:

代码下载

最后,这就是我开发的山寨WMS生成的透明gif图层+openlayers的效果,哈哈,上次是

谁说最低限度也要supmap,山寨一定是不可能完成的任务来着?这是啥咧,HOHO,纯VB.NET的WMS哦,呵呵

学GIS,还是要自己做东西提高的才快,用商业的东西,又贵,还没啥提高,不就是用人家的组件么.

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏码农阿宇

利用GDI+在Winfrom绘制验证码

string yzm; private void yangzhengma() { Bitma...

3027
来自专栏菩提树下的杨过

silverlight:如何在图片上挖个洞?

一、不写代码的方法:用Blend 看图说话: 这是待处理的图片win7 ? 在win7上,画一个矩形,再用钢笔随便画个封闭的path ? 将矩形与path合...

19910
来自专栏林德熙的博客

win10 uwp 分治法

算法涉及到了一个平面几何的知识。就是三角形p1p2p3的面积等于以下行列式的二分之一: % <![CDATA[ \begin{array}{cccc} | ...

821
来自专栏老司机的简书

CoreText实现图文混排

也好久没来写博客了,主要是最近也工作了,手头的事有点多,一时间也就断了,闲下来了我就来补博客了,刚好最近也做了很多东西,放在这里也算给自己做个笔记吧。

1942
来自专栏HT

基于 HTML5 Canvas 的 3D WebGL 机房创建

对于 3D 机房来说,监控已经不是什么难事,不同的人有不同的做法,今天试着用 HT 写了一个基于 HTML5 的机房,发现果然 HT 简单好用。本例是将灯光、雾...

3297
来自专栏JadePeng的技术博客

易企秀前端压缩源码分析与还原

你是否想知道易企秀炫酷的H5是如何实现的,原理是什么,本文会为你揭秘并还原压缩过的源代码。 易企秀是一款h5页面制作工具,因方便易用成为业界标杆。后续一个项目会...

9867
来自专栏HT

HT for Web基于HTML5的图像操作(一)

HT for Web独创的矢量图片设计架构,使其具有强大丰富的动态图形呈现能力,但从最近热议的“Adobe Photoshop 是否已经过时?”的话题,大家能体...

3319
来自专栏前端杂货铺

ReactJS分析之入口函数render

前言   在使用React进行构建应用时,我们总会有一个步骤将组建或者虚拟DOM元素渲染到真实的DOM上,将任务交给浏览器,进而进行layout和paint等...

3919
来自专栏緣來來來

Python爬虫 --- 2.2 Scrapy 选择器的介绍

Scrapy提取数据有自己的一套机制,被称作选择器(selectors),通过特定的Xpath或者CSS表达式来选择HTML文件的某个部分 Xpath是专门在X...

1222
来自专栏一个会写诗的程序员的博客

MySQL 直接存储图片并在 html 页面中展示,点击下载

2052

扫码关注云+社区

领取腾讯云代金券