Metro 中的图像处理






3.35/5 (14投票s)
如何在新的 Metro 风格应用中进行第三方图像处理。
引言
我终于有机会试用 Windows 8 开发者预览版和 Metro 应用了。在试用新的开始菜单和应用之后,我决定尝试一个简单的图像显示和处理 Metro 应用。
Using the Code
首先,我使用 2 个按钮、一个图像和一个标签创建了一个基本的布局,用于显示一些元数据。我很高兴看到他们坚持使用 XAML,因此学习曲线非常小。
<Grid x:Name="LayoutRoot" Background="#FF0C0C0C">
<StackPanel>
<StackPanel Orientation="Horizontal">
<Button Content="Load" FontSize="24" Width="200" Height="60" Click="Load_Click" />
<Button Content="Process" FontSize="24" Width="200" Height="60"
Click="Process_Click" />
</StackPanel>
<Image x:Name="Image1" Width="800" Height="600" Stretch="Uniform"
Source="Windows8Logo.png"/>
<TextBlock x:Name="DisplayText" FontSize="48" Foreground="White" />
</StackPanel>
</Grid>
为了加载图像,我使用了新的 FileOpenPicker
。我非常喜欢这个控件的外观和感觉,并且认为它的文件过滤器比旧 WinForms 使用单个 string
方式有所改进。
FileOpenPicker openPicker = new FileOpenPicker();
openPicker.FileTypeFilter.Add(".jpg");
openPicker.FileTypeFilter.Add(".cmp");
openPicker.FileTypeFilter.Add(".png");
openPicker.FileTypeFilter.Add(".tif");
openPicker.FileTypeFilter.Add(".gif");
openPicker.FileTypeFilter.Add(".bmp");
StorageFile file = await openPicker.PickSingleFileAsync();
int loadBitsPerPixel = 0;
RasterCodecs codecs = new RasterCodecs();
if (file != null)
{
string fileName = file.Path;
var winRTStream = await file.OpenAsync(FileAccessMode.ReadWrite);
IInputStream inputStream = winRTStream.GetInputStreamAt(0);
DataReader dataReader = new DataReader(inputStream);
await dataReader.LoadAsync((uint)winRTStream.Size);
var buffer = dataReader.ReadBuffer((uint)winRTStream.Size);
using (System.IO.Stream oldRTStream = buffer.AsStream())
{
using (RasterImage rasterImage = codecs.Load(oldRTStream, loadBitsPerPixel,
CodecsLoadByteOrder.BgrOrGray, 1, 1))
{
DisplayText.Text = string.Format("Filename: {0} - {1}x{2}x{3} - {4}",
file.FileName,
rasterImage.Width,
rasterImage.Height,
rasterImage.BitsPerPixel,
rasterImage.OriginalFormat.ToString());
Image1.Source = RasterImageConverter.ConvertToSource(rasterImage,
ConvertToSourceOptions.None);
}
}
}
我一直觉得原生图像处理功能不足,并且不介意使用第三方库,所以我尝试了之前项目中的 LEADTOOLS 评估版,看看它是否适用于新的 Windows 8。 .NET 4 库可以工作,但是为了将 LEADTOOLS RasterImage
类转换为新的 Metro ImageSource
,我使用了他们的支持部门提供的预发布 DLL。
using (RasterImage image = RasterImageConverter.ConvertFromSource(Image1.Source,
ConvertFromSourceOptions.None))
{
BricksTextureCommand cmd = new BricksTextureCommand(60, 20, 4, 3);
cmd.Run(image);
Image1.Source = RasterImageConverter.ConvertToSource(image,
ConvertToSourceOptions.None);
}
总的来说,我喜欢新的 Metro 风格应用。试用开发者预览版很有趣,看到学习曲线很小,并且第三方组件也已经开始参与进来,而不是等待 Windows 8 “正式”发布,这让我感到欣慰。
关于 Visual Studio 11 Beta 和 Windows 8 消费者预览版的说明
当我最初编写这篇文章时,我使用了 Visual Studio 11 开发者预览版。 自那以后,我尝试了相同的示例,使用了 Visual Studio 11 Beta 和 Windows 8 消费者预览版,但由于 Metro 应用现在使用 .Netcore 而不是接受 .NET Framework 4.0 库,它不再起作用(有关更多详细信息,请参阅此论坛帖子)。
原生代码示例
应一些用户的要求,我附上了一个不使用任何第三方工具的图像处理应用。
这是我使用 FileOpenPicker
以编程方式加载图像的方式
FileOpenPicker openPicker = new FileOpenPicker();
openPicker.FileTypeFilter.Add(".jpg");
openPicker.FileTypeFilter.Add(".cmp");
openPicker.FileTypeFilter.Add(".png");
openPicker.FileTypeFilter.Add(".tif");
openPicker.FileTypeFilter.Add(".gif");
openPicker.FileTypeFilter.Add(".bmp");
StorageFile file = await openPicker.PickSingleFileAsync();
IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.Read);
BitmapImage bmp = new BitmapImage();
bmp.SetSource(stream);
Image1.Source = bmp;
这是翻转图像的代码。第一步是通过创建一个 WriteableBitmap
来访问像素数据。然后从图像的顶部和底部读取并交换行。
// Cast the image in the viewer as a WriteableBitmap
WriteableBitmap wb = Image1.Source as WriteableBitmap;
// If it's a WriteableBitmap, just use it, otherwise create one
if (wb == null)
{
BitmapSource bs = Image1.Source as BitmapSource;
wb = new WriteableBitmap(bs);
}
// Assume 32 bits since that is what Metro currently supports
int stride = wb.PixelWidth * 4;
// extension method defined in System.Runtime.InteropServices.WindowsRuntime
System.IO.Stream pixelStream = wb.PixelBuffer.AsStream();
byte[] row1 = new byte[stride];
byte[] row2 = new byte[stride];
// flip the image
for (int row = 0; row < wb.PixelHeight / 2; row++)
{
pixelStream.Read(row2, 0, stride);
pixelStream.Seek((wb.PixelHeight - row - 1) * stride, SeekOrigin.Begin);
pixelStream.Read(row1, 0, stride);
pixelStream.Seek(-stride, SeekOrigin.Current);
pixelStream.Write(row2, 0, stride);
pixelStream.Seek(row * stride, SeekOrigin.Begin);
pixelStream.Write(row1, 0, stride);
}
wb.Invalidate();
Image1.Source = wb;
历史
- 2011 年 11 月 7 日:初始发布
- 2011 年 11 月 11 日:添加了非第三方示例
- 2012 年 4 月 4 日:添加了关于消费者预览版的说明