Chinaunix首页 | 论坛 | 博客
  • 博客访问: 6642670
  • 博文数量: 915
  • 博客积分: 17977
  • 博客等级: 上将
  • 技术积分: 8846
  • 用 户 组: 普通用户
  • 注册时间: 2005-08-26 09:59
个人简介

一个好老好老的老程序员了。

文章分类

全部博文(915)

文章存档

2022年(9)

2021年(13)

2020年(10)

2019年(40)

2018年(88)

2017年(130)

2015年(5)

2014年(12)

2013年(41)

2012年(36)

2011年(272)

2010年(1)

2009年(53)

2008年(65)

2007年(47)

2006年(81)

2005年(12)

分类: Android平台

2018-08-22 22:16:57

在运行时生成位图
所有这三个平台都支持BMP文件格式,该格式可以追溯到Microsoft Windows的最开始。尽管它具有古老的传统,但BMP文件格式现在已经相当标准化,具有更多的扩展标题信息。
虽然有一些BMP选项允许一些基本压缩,但大多数BMP文件都是未压缩的。这种缺乏压缩通常被视为BMP文件的缺点,但在某些情况下它根本不是缺点。例如,如果要在运行时以算法方式生成位图,则生成未压缩的位图而不是其中一种压缩文件格式要容易得多。 (实际上,即使你有一个库函数来创建JPEG或PNG文件,你也可以
将该函数应用于未压缩的像素数据。)
您可以通过使用BMP文件头和像素数据填充MemoryStream,然后将该MemoryStream传递给ImageSource.FromStream方法,在运行时以算法方式创建位图。 Xamarin.FormsBook.Toolkit库中的BmpMaker类演示了这一点。它使用32位像素格式在内存中创建BMP,每个格式为8位,用于红色,绿色,蓝色和alpha(不透明度)通道。 BmpMaker类在编写时考虑了性能,希望它可以用于动画。也许有一天它会成为,但在本章中,唯一的演示是一个简单的颜色渐变。
构造函数创建一个名为buffer的字节数组,该数组存储整个BMP文件,以头信息开头,后跟像素位。然后,构造函数使用MemoryStream将头信息写入此缓冲区的开头:

点击(此处)折叠或打开

  1. public class BmpMaker
  2. {
  3.     const int headerSize = 54;
  4.     readonly byte[] buffer;
  5.     public BmpMaker(int width, int height)
  6.     {
  7.         Width = width;
  8.         Height = height;
  9.         int numPixels = Width * Height;
  10.         int numPixelBytes = 4 * numPixels;
  11.         int fileSize = headerSize + numPixelBytes;
  12.         buffer = new byte[fileSize];
  13.         // Write headers in MemoryStream and hence the buffer.
  14.         using (MemoryStream memoryStream = new MemoryStream(buffer))
  15.         {
  16.             using (BinaryWriter writer = new BinaryWriter(memoryStream, Encoding.UTF8))
  17.             {
  18.                 // Construct BMP header (14 bytes).
  19.                 writer.Write(new char[] { 'B', 'M' }); // Signature
  20.                 writer.Write(fileSize); // File size
  21.                 writer.Write((short)0); // Reserved
  22.                 writer.Write((short)0); // Reserved
  23.                 writer.Write(headerSize); // Offset to pixels
  24.                 // Construct BitmapInfoHeader (40 bytes).
  25.                 writer.Write(40); // Header size
  26.                 writer.Write(Width); // Pixel width
  27.                 writer.Write(Height); // Pixel height
  28.                 writer.Write((short)1); // Planes
  29.                 writer.Write((short)32); // Bits per pixel
  30.                 writer.Write(0); // Compression
  31.                 writer.Write(numPixelBytes); // Image size in bytes
  32.                 writer.Write(0); // X pixels per meter
  33.                 writer.Write(0); // Y pixels per meter
  34.                 writer.Write(0); // Number colors in color table
  35.                 writer.Write(0); // Important color count
  36.             }
  37.         }
  38.     }
  39.     public int Width
  40.     {
  41.         private set;
  42.         get;
  43.     }
  44.     public int Height
  45.     {
  46.         private set;
  47.         get;
  48.     }
  49.     public void SetPixel(int row, int col, Color color)
  50.     {
  51.         SetPixel(row, col, (int)(255 * color.R),
  52.                            (int)(255 * color.G),
  53.                            (int)(255 * color.B),
  54.                            (int)(255 * color.A));
  55.     }
  56.     public void SetPixel(int row, int col, int r, int g, int b, int a = 255)
  57.     {
  58.         int index = (row * Width + col) * 4 + headerSize;
  59.         buffer[index + 0] = (byte)b;
  60.         buffer[index + 1] = (byte)g;
  61.         buffer[index + 2] = (byte)r;
  62.         buffer[index + 3] = (byte)a;
  63.     }
  64.     public ImageSource Generate()
  65.     {
  66.         // Create MemoryStream from buffer with bitmap.
  67.         MemoryStream memoryStream = new MemoryStream(buffer);
  68.         // Convert to StreamImageSource.
  69.         ImageSource imageSource = ImageSource.FromStream(() =>
  70.         {
  71.             return memoryStream;
  72.         });
  73.         return imageSource;
  74.     }
  75. }

创建BmpMaker对象后,程序可以调用两个SetPixel方法之一来设置特定行和列的颜色。 进行非常多次调用时,使用Color值的SetPixel调用明显慢于接受显式红色,绿色和蓝色值的调用。
最后一步是调用Generate方法。 此方法基于缓冲区数组实例化另一个MemoryStream对象,并使用它来创建FileImageSource对象。 设置新的像素数据后,您可以多次调用Gener?ate。 该方法每次都会创建一个新的MemoryStream,因为ImageSource.FromStream在完成后会关闭Stream对象。
DiyGradientBitmap程序 - “DIY”代表“自己动手” - 演示如何使用BmpMaker制作具有简单渐变的位图并显示它以填充页面。 XAML文件包含Image元素:

点击(此处)折叠或打开

  1. <ContentPage xmlns=""
  2.              xmlns:x=""
  3.              x:Class="DiyGradientBitmap.DiyGradientBitmapPage">
  4.     <ContentPage.Padding>
  5.         <OnPlatform x:TypeArguments="Thickness"
  6.                     iOS="0, 20, 0, 0" />
  7.     </ContentPage.Padding>
  8.     <Image x:Name="image"
  9.            Aspect="Fill" />
  10.  
  11. </ContentPage>

代码隐藏文件实例化一个BmpMaker并循环通过bit?map的行和列来创建一个渐变,范围从顶部的红色到底部的蓝色:

点击(此处)折叠或打开

  1. public partial class DiyGradientBitmapPage : ContentPage
  2. {
  3.     public DiyGradientBitmapPage()
  4.     {
  5.         InitializeComponent();
  6.         int rows = 128;
  7.         int cols = 64;
  8.         BmpMaker bmpMaker = new BmpMaker(cols, rows);
  9.         for (int row = 0; row < rows; row++)
  10.             for (int col = 0; col < cols; col++)
  11.             {
  12.                 bmpMaker.SetPixel(row, col, 2 * row, 0, 2 * (128 - row));
  13.             }
  14.         ImageSource imageSource = bmpMaker.Generate();
  15.         image.Source = imageSource;
  16.     }
  17. }
这是结果:

现在运用你的想象力,看看你能用BmpMaker做些什么。

阅读(2637) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~