第十三章:位图(七)

简介:

在运行时生成位图
所有这三个平台都支持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将头信息写入此缓冲区的开头:

public class BmpMaker
{
    const int headerSize = 54;
    readonly byte[] buffer;
    public BmpMaker(int width, int height)
    {
        Width = width;
        Height = height;
        int numPixels = Width * Height;
        int numPixelBytes = 4 * numPixels;
        int fileSize = headerSize + numPixelBytes;
        buffer = new byte[fileSize];
        // Write headers in MemoryStream and hence the buffer.
        using (MemoryStream memoryStream = new MemoryStream(buffer))
        {
            using (BinaryWriter writer = new BinaryWriter(memoryStream, Encoding.UTF8))
            {
                // Construct BMP header (14 bytes).
                writer.Write(new char[] { 'B', 'M' }); // Signature
                writer.Write(fileSize); // File size
                writer.Write((short)0); // Reserved
                writer.Write((short)0); // Reserved
                writer.Write(headerSize); // Offset to pixels
                // Construct BitmapInfoHeader (40 bytes).
                writer.Write(40); // Header size
                writer.Write(Width); // Pixel width
                writer.Write(Height); // Pixel height
                writer.Write((short)1); // Planes
                writer.Write((short)32); // Bits per pixel
                writer.Write(0); // Compression
                writer.Write(numPixelBytes); // Image size in bytes
                writer.Write(0); // X pixels per meter
                writer.Write(0); // Y pixels per meter
                writer.Write(0); // Number colors in color table
                writer.Write(0); // Important color count
            }
        }
    }
    public int Width
    {
        private set;
        get;
    }
    public int Height
    {
        private set;
        get;
    }
    public void SetPixel(int row, int col, Color color)
    {
        SetPixel(row, col, (int)(255 * color.R), 
                           (int)(255 * color.G), 
                           (int)(255 * color.B), 
                           (int)(255 * color.A));
    }
    public void SetPixel(int row, int col, int r, int g, int b, int a = 255)
    {
        int index = (row * Width + col) * 4 + headerSize;
        buffer[index + 0] = (byte)b;
        buffer[index + 1] = (byte)g;
        buffer[index + 2] = (byte)r;
        buffer[index + 3] = (byte)a;
    }
    public ImageSource Generate()
    {
        // Create MemoryStream from buffer with bitmap.
        MemoryStream memoryStream = new MemoryStream(buffer);
        // Convert to StreamImageSource.
        ImageSource imageSource = ImageSource.FromStream(() =>
        {
            return memoryStream;
        });
        return imageSource;
    }
}

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

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="DiyGradientBitmap.DiyGradientBitmapPage">
    <ContentPage.Padding>
        <OnPlatform x:TypeArguments="Thickness"
                    iOS="0, 20, 0, 0" />
    </ContentPage.Padding>
    <Image x:Name="image"
           Aspect="Fill" />
 
</ContentPage>

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

public partial class DiyGradientBitmapPage : ContentPage
{
    public DiyGradientBitmapPage()
    {
        InitializeComponent();
        int rows = 128;
        int cols = 64;
        BmpMaker bmpMaker = new BmpMaker(cols, rows);
        for (int row = 0; row < rows; row++)
            for (int col = 0; col < cols; col++)
            {
                bmpMaker.SetPixel(row, col, 2 * row, 0, 2 * (128 - row));
            }
        ImageSource imageSource = bmpMaker.Generate();
        image.Source = imageSource;
    }
}

这是结果:
201808222207090392
现在运用你的想象力,看看你能用BmpMaker做些什么。

目录
相关文章
|
Java
如何用Java实现位图转矢量图?
通过前面几篇图片转字符、灰度图的文章介绍之后,接下来我们再来看一个有意思的东西,基于前文的基础,实现位图转矢量图的功能
1381 0
如何用Java实现位图转矢量图?
|
7月前
|
存储 算法 C++
【C++入门到精通】位图 | 位图的实现[ C++入门 ]
【C++入门到精通】位图 | 位图的实现[ C++入门 ]
78 0
|
7月前
|
XML 算法 Java
Android App开发之位图加工Bitmap中转换位图的像素色彩、裁剪内部区域、利用矩阵变换位图的讲解及实战(附源码和演示)
Android App开发之位图加工Bitmap中转换位图的像素色彩、裁剪内部区域、利用矩阵变换位图的讲解及实战(附源码和演示)
117 0
|
算法 NoSQL C#
C#位图BitArray 小试牛刀
难缠的布隆过滤器,这次终于通透了
C#位图BitArray 小试牛刀
Halcon把8位图转换为24位图的方法
Halcon把8位图转换为24位图的方法
479 0
|
Android开发 Windows iOS开发
|
JavaScript Android开发 iOS开发
|
存储 编解码 Android开发
|
存储 JavaScript Android开发
|
JSON Android开发 数据格式