[silverlight] silverlight3新增功能2:WriteableBitmap

      来学习WriteableBitmap吧。看看參考文檔中的描述:

      使用 WriteableBitmap 类基于每个框架来更新和呈现位图。这对于拍摄正播放视频的快照、生成算法内容(如分形图像)和数据可视化(如音乐可视化应用程序)很有用。

      SL3新增的功能中这个还算比较重要,它继承BitmapSource,使用构造函数WriteableBitmap(UIElement, Transform)可以将传入的UIElement保存为一张图片。不过在文档中找不到设置要保存为图片的UIElement的方法,所以搞不明白另两个构造函数 (Int32, Int32)和(BitmapSource)有什么用。另外,“Invalidate”方法的作用是“请求绘制整个位图”也搞不懂是做什么的,请高手指教。

      先来测试一下吧。
      首先摆一个TextBlock,把它做成图片,代码如下:

WriteableBitmap bitmap  =   new  WriteableBitmap(text,  null );
img.Source 
=  bitmap;
txt1.Text 
=   string .Format( " {0} * {1} " , bitmap.PixelWidth, bitmap.PixelHeight);

 

ContractedBlock.gif ExpandedBlockStart.gif Xaml
<Grid x:Name="LayoutRoot">
        
<TextBlock Text="sdfsdfsdfsdfsd"  x:Name="text"  HorizontalAlignment="Left" VerticalAlignment="Top"/>
        
<Button Height="25" Width="100" Click="Button_Click" Content="截图"/>
        
<StackPanel HorizontalAlignment="Left" VerticalAlignment="Bottom">
            
<TextBlock x:Name="txt1"/>
            
<Border BorderBrush="Black" Width="Auto" Height="Auto" BorderThickness="1">
                
<Image Height="Auto" Stretch="None" HorizontalAlignment="Center" VerticalAlignment="Center" Width="Auto"   
x:Name
="img"/>
            
</Border>
        
</StackPanel>
        
<StackPanel HorizontalAlignment="Right" VerticalAlignment="Bottom">
            
<TextBlock x:Name="txt2"/>
            
<Border BorderBrush="Black" Width="Auto" Height="Auto" BorderThickness="1">
                
<Image    Height="Auto"  Stretch="None"  HorizontalAlignment="Center" VerticalAlignment="Center"   
Width
="Auto"  x:Name="img2"/>
            
</Border>
        
</StackPanel>
</Grid>

效果图:


      虽然能正确地显示图片,但有个问题,在Loaded事件中调用,以及自己点击按钮调用,出来的效果是不一样的(左下角是Loaded事件中的效果,右下角是点击按钮后出来的效果)。在Loaded事件中TextBlock的ActualHeight是16,但图片的高度是12。不过实际应用不太可能在Loaded事件中使用这个功能,暂时忽略吧。
      题外话,SL3中BitmapSource的PixelWidth和PixelHeight可以很方便地取得图片的大小。以前为了实现这个功能,我还试过把图片放在一个ScrollViewer中让它自由拉伸再取它的实际大小,以后再也不需要做这种麻烦事了。

实际应用一:
      WriteableBitmap其中一个很激动人心的应用是,终于可以保存用SL生成的图表了。WriteableBitmap可以将对象的Clip、Effect、Opacity、OpacityMask、Children呈现出来,连Projection也不例外。本来打算用这种方法获取对象的截图:
      WriteableBitmap bitmap = new WriteableBitmap(rectangle, rectangle.RenderTransform);
      但后来发先这种方法不可行,因为RenderTransformOrigin="0.5,0.5"这个属性不可以获取到,最终出来的截图和实际效果不符。但是获取父元素的截图就没问题了。
      最终效果:
获取 Microsoft Silverlight

      在高分辨率下截太多图内存消耗是很大的,请小心(1280分辨率下几M一张图,现在的分辨率是500*500左右)。
      XAML:
ContractedBlock.gif ExpandedBlockStart.gif Code
 <Grid x:Name="grid">
            
<Rectangle x:Name="rectangle" Fill="Red" RenderTransformOrigin="0.5,0.5" StrokeThickness="2" Stroke="Black"  
HorizontalAlignment
="Center" VerticalAlignment="Center" Width="300" Height="300">
                
<Rectangle.RenderTransform>
                    
<TransformGroup>
                        
<ScaleTransform/>
                        
<SkewTransform/>
                        
<RotateTransform/>
                        
<TranslateTransform/>
                    
</TransformGroup>
                
</Rectangle.RenderTransform>
                
<Rectangle.Projection>
                    
<PlaneProjection/>
                
</Rectangle.Projection>
            
</Rectangle>
        
</Grid>
        
<ListBox x:Name="listBox" Grid.Column="1" Style="{StaticResource ListBoxStyle}"  
ItemContainerStyle
="{StaticResource ListBoxItemStyle}" />

      截图的代码:
ContractedBlock.gif ExpandedBlockStart.gif Code
Transform tf = rectangle.RenderTransform;
WriteableBitmap bitmap 
= new WriteableBitmap(grid,null);
Image img 
= new Image();
img.Height 
= 150;
img.Width 
= 150;
img.Source 
= bitmap;
img.Stretch 
= Stretch.Uniform;
ListBoxItem item 
= new ListBoxItem();
item.Content 
= img;
listBox.Items.Add(item);
      既然截图没问题了,那就考虑保存为PNG,使用了这个网站的PngEncoder:
http://blogs.msdn.com/jstegman/archive/2008/04/21/dynamic-image-generation-in-silverlight.aspx
      获取了png的stream,再多做一步,用SaveFileDialog把这个stream直接保存到硬盘吧,代码如下:
ContractedBlock.gif ExpandedBlockStart.gif Code
                SaveFileDialog dialog = new SaveFileDialog();
                dialog.Filter 
= "支持的图像文文件(*.png)|*.png";
                
if (dialog.ShowDialog() != true)
                {
                    
return;
                }
                WriteableBitmap bitmap 
= new WriteableBitmap(grid, null);
                
int[] i = bitmap.Pixels;
                EditableImage imageData 
= new EditableImage(bitmap.PixelWidth, bitmap.PixelHeight);


                
for (int y = 0; y < bitmap.PixelHeight; ++y)
                {
                    
for (int x = 0; x < bitmap.PixelWidth; ++x)
                    {

                        
int pixel = i[bitmap.PixelWidth * y + x];

                        imageData.SetPixel(x, y,
                                    (
byte)((pixel >> 16& 0xFF),
                                    (
byte)((pixel >> 8& 0xFF),
                                    (
byte)(pixel & 0xFF),
                                    (
byte)((pixel >> 24& 0xFF)
                                    );
                    }
                }
                Stream pngStream 
= imageData.GetStream();

                
byte[] buffer = new byte[pngStream.Length];
                pngStream.Read(buffer, 
0, (int)pngStream.Length);
                pngStream.Dispose();

                Stream st 
= dialog.OpenFile();
                st.Write(buffer, 
0, buffer.Length);
                st.Close();

      直接操作dialog.OpenFile()这个流好像会出好多问题,譬如直接Close这个流居然会提示没打开文件,但把dialog.OpenFile()赋值到另一个流再操作就没问题了。我对流操作很没信心,如果做得不好请高手指教。
       还有一点没考虑清楚,就是那个png是没有经过压缩的,最终出来的文件很巨大,如果哪位高手有PNG的压缩方法,请务必告诉我。

实际应用二:
      WriteableBitmap的另一个应该用,是提高动画的性能。当要做动画的元素子元素太多的时候,动画的效果是很差的,恐怕是因为StoryBoard需要计算里面全部的子元素。这时候如果把全部子元素做成一张图片,StoryBoard说计算的量就会大大减小。下面这个SL中,左边和右边的框里面加了100个Grid和TextBox,而中间那个什么都没有加。
      構造函數中的代碼:
ContractedBlock.gif ExpandedBlockStart.gif Code
Grid grid;
TextBox text;
for (int i = 0; i < 100; i++)
   {
    grid 
= new Grid { Background = new SolidColorBrush(Colors.Transparent) };
    text 
= new TextBox { Text = i.ToString() };
    grid.Children.Add(text);
    grid1.Children.Add(grid);
    grid 
= new Grid { Background = new SolidColorBrush(Colors.Transparent) };
    text 
= new TextBox { Text = i.ToString() };
    grid.Children.Add(text);
    grid3.Children.Add(grid);
    }
      但使用了WtiteableBitmap后,右边那个的动画效果和中间那个是非常接近的,即使把WriteableBitmap的构造函数写在动画开始之前。
ContractedBlock.gif ExpandedBlockStart.gif Code
  private void OnButton3Click(object sender, RoutedEventArgs e)
        {
            ToggleButton button 
= sender as ToggleButton;
            
if (button.IsChecked == true)
            {

                WriteableBitmap bitmap 
= new WriteableBitmap(grid3, null);
                Image img 
= new Image();
                img.Source 
= bitmap;
                border3.Child 
= img;
                Storyboard3.Begin();
            }
            
else
            {
                Storyboard3.Stop();
                border3.Child 
= grid3;
            }
        }

實際效果:

获取 Microsoft Silverlight

      关于WriteableBitmap的实际应用,目前我也只是想到这两个,期待补充。 
      測試代碼:
/Files/dino623/WriteableBitmapTest.rar

转载于:https://www.cnblogs.com/dino623/archive/2009/09/04/Silverlight.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值