在運作時生成位圖
所有這三個平台都支援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;
}
}
這是結果:
現在運用你的想象力,看看你能用BmpMaker做些什麼。