C# UWP-如何平铺背景图像?
在通用Windows应用程序中,我尝试使用背景图像(来自ImageSource)并将其平铺到控件上 XAML 根据,ImageBrush继承自TileBrush。它甚至说: 用于图像笔刷,包括文本或平铺的装饰效果 控件或布局容器的背景C# UWP-如何平铺背景图像?,c#,.net,xaml,win-universal-app,C#,.net,Xaml,Win Universal App,在通用Windows应用程序中,我尝试使用背景图像(来自ImageSource)并将其平铺到控件上 XAML 根据,ImageBrush继承自TileBrush。它甚至说: 用于图像笔刷,包括文本或平铺的装饰效果 控件或布局容器的背景 我假定如果拉伸被禁用,这将瓦解图像,但唉,它只是在控件中间绘制图像。我没有看到任何实际的属性使它平铺 在WPF中,有一个属性,可以设置视口以指定平铺的尺寸。但在通用平台下,这似乎是不存在的 A指的是WinRT(Windows8),但我希望有一个基于画笔的解决方案,
我假定如果拉伸被禁用,这将瓦解图像,但唉,它只是在控件中间绘制图像。我没有看到任何实际的属性使它平铺
在WPF中,有一个属性,可以设置视口以指定平铺的尺寸。但在通用平台下,这似乎是不存在的 A指的是WinRT(Windows8),但我希望有一个基于画笔的解决方案,而不是用图像填充画布 如何使用UWP平铺背景图像 前面的一个问题涉及WinRT(Windows8),但我希望有一个基于笔刷的解决方案,而不是用图像填充画布 目前,UWP应用程序中只有两种以平铺模式显示背景图像的解决方案,您知道的第一种解决方案是填充画布 我使用的第二个方法是创建一个面板并在上面绘制图像,这个想法来源于 这种方法所做的是,它滥用了这样一个事实,即我们正在绘制一个矩形形状的重复线集。首先,它尝试在顶部绘制一个与瓷砖高度相同的块。然后它复制那个块直到它到达底部 我修改了一些代码并修复了一些问题:public class TiledBackground : Panel
{
public ImageSource BackgroundImage
{
get { return (ImageSource)GetValue(BackgroundImageProperty); }
set { SetValue(BackgroundImageProperty, value); }
}
// Using a DependencyProperty as the backing store for BackgroundImage. This enables animation, styling, binding, etc...
public static readonly DependencyProperty BackgroundImageProperty =
DependencyProperty.Register("BackgroundImage", typeof(ImageSource), typeof(TiledBackground), new PropertyMetadata(null, BackgroundImageChanged));
private static void BackgroundImageChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((TiledBackground)d).OnBackgroundImageChanged();
}
private static void DesignDataChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((TiledBackground)d).OnDesignDataChanged();
}
private ImageBrush backgroundImageBrush = null;
private bool tileImageDataRebuildNeeded = true;
private byte[] tileImagePixels = null;
private int tileImageWidth = 0;
private int tileImageHeight = 0;
private readonly BitmapPixelFormat bitmapPixelFormat = BitmapPixelFormat.Bgra8;
private readonly BitmapTransform bitmapTransform = new BitmapTransform();
private readonly BitmapAlphaMode bitmapAlphaMode = BitmapAlphaMode.Straight;
private readonly ExifOrientationMode exifOrientationMode = ExifOrientationMode.IgnoreExifOrientation;
private readonly ColorManagementMode coloManagementMode = ColorManagementMode.ColorManageToSRgb;
public TiledBackground()
{
this.backgroundImageBrush = new ImageBrush();
this.Background = backgroundImageBrush;
this.SizeChanged += TiledBackground_SizeChanged;
}
private async void TiledBackground_SizeChanged(object sender, SizeChangedEventArgs e)
{
await this.Render((int)e.NewSize.Width, (int)e.NewSize.Height);
}
private async void OnBackgroundImageChanged()
{
tileImageDataRebuildNeeded = true;
await Render((int)this.ActualWidth, (int)this.ActualHeight);
}
private async void OnDesignDataChanged()
{
tileImageDataRebuildNeeded = true;
await Render((int)this.ActualWidth, (int)this.ActualHeight);
}
private async Task RebuildTileImageData()
{
BitmapImage image = BackgroundImage as BitmapImage;
if ((image != null) && (!DesignMode.DesignModeEnabled))
{
string imgUri = image.UriSource.OriginalString;
if (!imgUri.Contains("ms-appx:///"))
{
imgUri += "ms-appx:///";
}
var imageSource = new Uri(imgUri);
StorageFile storageFile = await StorageFile.GetFileFromApplicationUriAsync(imageSource);
using (var imageStream = await storageFile.OpenAsync(FileAccessMode.Read))
{
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(imageStream);
var pixelDataProvider = await decoder.GetPixelDataAsync(this.bitmapPixelFormat, this.bitmapAlphaMode,
this.bitmapTransform, this.exifOrientationMode, this.coloManagementMode
);
this.tileImagePixels = pixelDataProvider.DetachPixelData();
this.tileImageHeight = (int)decoder.PixelHeight;
this.tileImageWidth = (int)decoder.PixelWidth;
}
}
}
private byte[] CreateBackgroud(int width, int height)
{
int bytesPerPixel = this.tileImagePixels.Length / (this.tileImageWidth * this.tileImageHeight);
byte[] data = new byte[width * height * bytesPerPixel];
int y = 0;
int fullTileInRowCount = width / tileImageWidth;
int tileRowLength = tileImageWidth * bytesPerPixel;
//Stage 1: Go line by line and create a block of our pattern
//Stop when tile image height or required height is reached
while ((y < height) && (y < tileImageHeight))
{
int tileIndex = y * tileImageWidth * bytesPerPixel;
int dataIndex = y * width * bytesPerPixel;
//Copy the whole line from tile at once
for (int i = 0; i < fullTileInRowCount; i++)
{
Array.Copy(tileImagePixels, tileIndex, data, dataIndex, tileRowLength);
dataIndex += tileRowLength;
}
//Copy the rest - if there is any
//Length will evaluate to 0 if all lines were copied without remainder
Array.Copy(tileImagePixels, tileIndex, data, dataIndex,
(width - fullTileInRowCount * tileImageWidth) * bytesPerPixel);
y++; //Next line
}
//Stage 2: Now let's copy those whole blocks from top to bottom
//If there is not enough space to copy the whole block, skip to stage 3
int rowLength = width * bytesPerPixel;
int blockLength = this.tileImageHeight * rowLength;
while (y <= (height - tileImageHeight))
{
int dataBaseIndex = y * width * bytesPerPixel;
Array.Copy(data, 0, data, dataBaseIndex, blockLength);
y += tileImageHeight;
}
//Copy the rest line by line
//Use previous lines as source
for (int row = y; row < height; row++)
Array.Copy(data, (row - tileImageHeight) * rowLength, data, row * rowLength, rowLength);
return data;
}
private async Task Render(int width, int height)
{
Stopwatch fullsw = Stopwatch.StartNew();
if (tileImageDataRebuildNeeded)
await RebuildTileImageData();
if ((height > 0) && (width > 0))
{
using (var randomAccessStream = new InMemoryRandomAccessStream())
{
Stopwatch sw = Stopwatch.StartNew();
var backgroundPixels = CreateBackgroud(width, height);
sw.Stop();
Debug.WriteLine("Background generation finished: {0} ticks - {1} ms", sw.ElapsedTicks, sw.ElapsedMilliseconds);
BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, randomAccessStream);
encoder.SetPixelData(this.bitmapPixelFormat, this.bitmapAlphaMode, (uint)width, (uint)height, 96, 96, backgroundPixels);
await encoder.FlushAsync();
if (this.backgroundImageBrush.ImageSource == null)
{
BitmapImage bitmapImage = new BitmapImage();
randomAccessStream.Seek(0);
bitmapImage.SetSource(randomAccessStream);
this.backgroundImageBrush.ImageSource = bitmapImage;
}
else ((BitmapImage)this.backgroundImageBrush.ImageSource).SetSource(randomAccessStream);
}
}
else this.backgroundImageBrush.ImageSource = null;
fullsw.Stop();
Debug.WriteLine("Background rendering finished: {0} ticks - {1} ms", fullsw.ElapsedTicks, fullsw.ElapsedMilliseconds);
}
}
公共类平铺背景:面板
{
公共图像源背景图像
{
get{return(ImageSource)GetValue(BackgroundImageProperty);}
set{SetValue(BackgroundImageProperty,value);}
}
//使用DependencyProperty作为BackgroundImage的后台存储。这将启用动画、样式、绑定等。。。
公共静态只读从属属性BackgroundImageProperty=
DependencyProperty.Register(“BackgroundImage”、typeof(ImageSource)、typeof(TiledBackground)、new PropertyMetadata(null、BackgroundImageChanged));
私有静态void BackgroundImageChanged(DependencyObject d,DependencyPropertyChangedEventArgs e)
{
((TiledBackground)d).OnBackgroundImageChanged();
}
私有静态void DesignDataChanged(DependencyObject d、DependencyPropertyChangedEventArgs e)
{
((TiledBackground)d).OnDesignDataChanged();
}
私有ImageBrush backgroundImageBrush=null;
private bool tileImageDataRebuildNeeded=true;
私有字节[]tileImagePixels=null;
私有int tileImageWidth=0;
私有int tileImageHeight=0;
私有只读BitmapPixelFormat BitmapPixelFormat=BitmapPixelFormat.Bgra8;
私有只读BitmapTransform BitmapTransform=新BitmapTransform();
私有只读BitmapAlphaMode BitmapAlphaMode=BitmapAlphaMode.Straight;
私有只读ExifOrientationMode ExifOrientationMode=ExifOrientationMode.IgnoreExifOrientation;
私有只读ColorManagementMode ColorManagementMode=ColorManagementMode.ColorManagetoRGB;
公共平铺背景()
{
this.backgroundImageBrush=新的ImageBrush();
this.Background=backgroundImageBrush;
this.SizeChanged+=平铺背景_SizeChanged;
}
私有异步void TiledBackground\u SizeChanged(对象发送方,SizeChangedEventArgs e)
{
等待这个.Render((int)e.NewSize.Width,(int)e.NewSize.Height);
}
私有异步void OnBackgroundImageChanged()
{
tileImageDataRebuildNeeded=true;
等待渲染((int)this.ActualWidth,(int)this.ActualHeight);
}
私有异步void OnDesignDataChanged()
{
tileImageDataRebuildNeeded=true;
等待渲染((int)this.ActualWidth,(int)this.ActualHeight);
}
专用异步任务重建文件ImageData()
{
BitmapImage=作为BitmapImage的背景图像;
如果((image!=null)&&(!DesignMode.DesignModeEnabled))
{
字符串imgUri=image.UriSource.OriginalString;
如果(!imgUri.Contains(“ms appx://”)
{
imgUri+=“ms appx://”;
}
var imageSource=新Uri(imgUri);
StorageFile StorageFile=等待StorageFile.GetFileFromApplicationUriAsync(imageSource);
使用(var imageStream=await-storageFile.OpenAsync(FileAccessMode.Read))
{
BitmapDecoder decoder=等待BitmapDecoder.CreateAsync(imageStream);
var pixelDataProvider=await decoder.GetPixelDataAsync(this.bitmapPixelFormat,this.bitmapAlphaMode,
this.bitmapTransform、this.exifOrientationMode、this.coloManagementMode
);
this.tileImagePixels=pixelDataProvider.DetachPixelData();
this.tileImageHeight=(int)decoder.PixelHeight;
this.tileImageWidth=(int)decoder.PixelWidth;
}
}
}
专用字节[]CreateBackground(整数宽度、整数高度)
{
int bytesperpoixel=this.tileImagePixels.Length/(this.tileImageWidth*this.tileImageHeight);
字节[]数据=新字节[宽度*高度*字节/像素];
int y=0;
int fullTileInRowCount=宽度/tileImageWidth;
int tileRowLength=tileImageWidth*字节/像素;
//第一阶段:一行一行地创建一个模式块
//达到平铺图像高度或所需高度时停止
而((y<高度)和&(yvoid UpdateBackground(ImageSource source)
{
// ...
gridBackground.Background = new ImageBrush {
ImageSource = source,
Stretch = Stretch.None
};
}
public class TiledBackground : Panel
{
public ImageSource BackgroundImage
{
get { return (ImageSource)GetValue(BackgroundImageProperty); }
set { SetValue(BackgroundImageProperty, value); }
}
// Using a DependencyProperty as the backing store for BackgroundImage. This enables animation, styling, binding, etc...
public static readonly DependencyProperty BackgroundImageProperty =
DependencyProperty.Register("BackgroundImage", typeof(ImageSource), typeof(TiledBackground), new PropertyMetadata(null, BackgroundImageChanged));
private static void BackgroundImageChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((TiledBackground)d).OnBackgroundImageChanged();
}
private static void DesignDataChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((TiledBackground)d).OnDesignDataChanged();
}
private ImageBrush backgroundImageBrush = null;
private bool tileImageDataRebuildNeeded = true;
private byte[] tileImagePixels = null;
private int tileImageWidth = 0;
private int tileImageHeight = 0;
private readonly BitmapPixelFormat bitmapPixelFormat = BitmapPixelFormat.Bgra8;
private readonly BitmapTransform bitmapTransform = new BitmapTransform();
private readonly BitmapAlphaMode bitmapAlphaMode = BitmapAlphaMode.Straight;
private readonly ExifOrientationMode exifOrientationMode = ExifOrientationMode.IgnoreExifOrientation;
private readonly ColorManagementMode coloManagementMode = ColorManagementMode.ColorManageToSRgb;
public TiledBackground()
{
this.backgroundImageBrush = new ImageBrush();
this.Background = backgroundImageBrush;
this.SizeChanged += TiledBackground_SizeChanged;
}
private async void TiledBackground_SizeChanged(object sender, SizeChangedEventArgs e)
{
await this.Render((int)e.NewSize.Width, (int)e.NewSize.Height);
}
private async void OnBackgroundImageChanged()
{
tileImageDataRebuildNeeded = true;
await Render((int)this.ActualWidth, (int)this.ActualHeight);
}
private async void OnDesignDataChanged()
{
tileImageDataRebuildNeeded = true;
await Render((int)this.ActualWidth, (int)this.ActualHeight);
}
private async Task RebuildTileImageData()
{
BitmapImage image = BackgroundImage as BitmapImage;
if ((image != null) && (!DesignMode.DesignModeEnabled))
{
string imgUri = image.UriSource.OriginalString;
if (!imgUri.Contains("ms-appx:///"))
{
imgUri += "ms-appx:///";
}
var imageSource = new Uri(imgUri);
StorageFile storageFile = await StorageFile.GetFileFromApplicationUriAsync(imageSource);
using (var imageStream = await storageFile.OpenAsync(FileAccessMode.Read))
{
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(imageStream);
var pixelDataProvider = await decoder.GetPixelDataAsync(this.bitmapPixelFormat, this.bitmapAlphaMode,
this.bitmapTransform, this.exifOrientationMode, this.coloManagementMode
);
this.tileImagePixels = pixelDataProvider.DetachPixelData();
this.tileImageHeight = (int)decoder.PixelHeight;
this.tileImageWidth = (int)decoder.PixelWidth;
}
}
}
private byte[] CreateBackgroud(int width, int height)
{
int bytesPerPixel = this.tileImagePixels.Length / (this.tileImageWidth * this.tileImageHeight);
byte[] data = new byte[width * height * bytesPerPixel];
int y = 0;
int fullTileInRowCount = width / tileImageWidth;
int tileRowLength = tileImageWidth * bytesPerPixel;
//Stage 1: Go line by line and create a block of our pattern
//Stop when tile image height or required height is reached
while ((y < height) && (y < tileImageHeight))
{
int tileIndex = y * tileImageWidth * bytesPerPixel;
int dataIndex = y * width * bytesPerPixel;
//Copy the whole line from tile at once
for (int i = 0; i < fullTileInRowCount; i++)
{
Array.Copy(tileImagePixels, tileIndex, data, dataIndex, tileRowLength);
dataIndex += tileRowLength;
}
//Copy the rest - if there is any
//Length will evaluate to 0 if all lines were copied without remainder
Array.Copy(tileImagePixels, tileIndex, data, dataIndex,
(width - fullTileInRowCount * tileImageWidth) * bytesPerPixel);
y++; //Next line
}
//Stage 2: Now let's copy those whole blocks from top to bottom
//If there is not enough space to copy the whole block, skip to stage 3
int rowLength = width * bytesPerPixel;
int blockLength = this.tileImageHeight * rowLength;
while (y <= (height - tileImageHeight))
{
int dataBaseIndex = y * width * bytesPerPixel;
Array.Copy(data, 0, data, dataBaseIndex, blockLength);
y += tileImageHeight;
}
//Copy the rest line by line
//Use previous lines as source
for (int row = y; row < height; row++)
Array.Copy(data, (row - tileImageHeight) * rowLength, data, row * rowLength, rowLength);
return data;
}
private async Task Render(int width, int height)
{
Stopwatch fullsw = Stopwatch.StartNew();
if (tileImageDataRebuildNeeded)
await RebuildTileImageData();
if ((height > 0) && (width > 0))
{
using (var randomAccessStream = new InMemoryRandomAccessStream())
{
Stopwatch sw = Stopwatch.StartNew();
var backgroundPixels = CreateBackgroud(width, height);
sw.Stop();
Debug.WriteLine("Background generation finished: {0} ticks - {1} ms", sw.ElapsedTicks, sw.ElapsedMilliseconds);
BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, randomAccessStream);
encoder.SetPixelData(this.bitmapPixelFormat, this.bitmapAlphaMode, (uint)width, (uint)height, 96, 96, backgroundPixels);
await encoder.FlushAsync();
if (this.backgroundImageBrush.ImageSource == null)
{
BitmapImage bitmapImage = new BitmapImage();
randomAccessStream.Seek(0);
bitmapImage.SetSource(randomAccessStream);
this.backgroundImageBrush.ImageSource = bitmapImage;
}
else ((BitmapImage)this.backgroundImageBrush.ImageSource).SetSource(randomAccessStream);
}
}
else this.backgroundImageBrush.ImageSource = null;
fullsw.Stop();
Debug.WriteLine("Background rendering finished: {0} ticks - {1} ms", fullsw.ElapsedTicks, fullsw.ElapsedMilliseconds);
}
}
<Grid x:Name="rootGrid" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<tileCtrl:TiledBackground
BackgroundImage="Assets/avatar1.png"
Width="{Binding ActualWidth, ElementName=rootGrid}" Height="{Binding ActualHeight, ElementName=rootGrid}"/>
</Grid>
var compositor = ElementCompositionPreview.GetElementVisual(this).Compositor;
var canvasDevice = CanvasDevice.GetSharedDevice();
var graphicsDevice = CanvasComposition.CreateCompositionGraphicsDevice(compositor, canvasDevice);
var bitmap = await CanvasBitmap.LoadAsync(canvasDevice, new Uri("ms-appx:///YourProject/Assets/texture.jpg"));
var drawingSurface = graphicsDevice.CreateDrawingSurface(bitmap.Size,
DirectXPixelFormat.B8G8R8A8UIntNormalized, DirectXAlphaMode.Premultiplied);
using (var ds = CanvasComposition.CreateDrawingSession(drawingSurface))
{
ds.Clear(Colors.Transparent);
ds.DrawImage(bitmap);
}
var surfaceBrush = compositor.CreateSurfaceBrush(drawingSurface);
surfaceBrush.Stretch = CompositionStretch.None;
var border = new BorderEffect
{
ExtendX = CanvasEdgeBehavior.Wrap,
ExtendY = CanvasEdgeBehavior.Wrap,
Source = new CompositionEffectSourceParameter("source")
};
var fxFactory = compositor.CreateEffectFactory(border);
var fxBrush = fxFactory.CreateBrush();
fxBrush.SetSourceParameter("source", surfaceBrush);
var sprite = compositor.CreateSpriteVisual();
sprite.Size = new Vector2(1000000);
sprite.Brush = fxBrush;
ElementCompositionPreview.SetElementChildVisual(YourCanvas, sprite);
public class TiledBrush : XamlCompositionBrushBase
{
protected override void OnConnected()
{
var surface = LoadedImageSurface.StartLoadFromUri(ImageSourceUri);
var surfaceBrush = Compositor.CreateSurfaceBrush(surface);
surfaceBrush.Stretch = CompositionStretch.None;
var borderEffect = new BorderEffect()
{
Source = new CompositionEffectSourceParameter("source"),
ExtendX = Microsoft.Graphics.Canvas.CanvasEdgeBehavior.Wrap,
ExtendY = Microsoft.Graphics.Canvas.CanvasEdgeBehavior.Wrap
};
var borderEffectFactory = Compositor.CreateEffectFactory(borderEffect);
var borderEffectBrush = borderEffectFactory.CreateBrush();
borderEffectBrush.SetSourceParameter("source", surfaceBrush);
}
}
<Grid>
<Grid.Background>
<local:TiledBrush ImageSourceUri="Assets/Texture.jpg" />
</Grid.Background>
</Grid>