SkiaSharp 位图的分段显示
SkiaSharp SKCanvas
对象定义一个名为 DrawBitmapNinePatch
的方法,以及两个非常相似的名为 DrawBitmapLattice
的方法。 这两个方法都将位图渲染为目标矩形的大小,但它们不是均匀拉伸位图,而是以其像素大小显示位图的一部分,并拉伸位图的其他部分以使其适合矩形:
这些方法通常用于渲染构成用户界面对象(例如按钮)一部分的位图。 设计按钮时,通常你希望按钮的大小基于按钮的内容,但可能希望按钮的边框宽度相同,而不管按钮的内容如何。 这是 DrawBitmapNinePatch
的理想应用方案。
DrawBitmapNinePatch
是 DrawBitmapLattice
的一种特例,但它是两个方法中更容易使用和理解的一个。
九宫格显示
从概念上讲,DrawBitmapNinePatch
将位图划分为九个矩形:
四个角处的矩形以其像素大小显示。 如箭头所示,位图边缘的其他区域将水平或垂直拉伸到目标矩形的区域。 中心的矩形同时朝水平和垂直方向拉伸。
如果目标矩形中没有足够的空间来显示其像素大小的四个角,则它们会缩小到可用大小,并且只显示四个角。
若要将位图划分为这九个矩形,只需指定位于中心的矩形即可。 这是 DrawBitmapNinePatch
方法的语法:
canvas.DrawBitmapNinePatch(bitmap, centerRectangle, destRectangle, paint);
中心矩形相对于位图。 它是一个 SKRectI
值(SKRect
的整数版本),所有坐标和大小均以像素为单位。 目标矩形相对于显示表面。 paint
参数是可选的。
示例中的“Nine Patch 显示”页首先使用静态构造函数创建类型为 SKBitmap
的公共静态属性:
public partial class NinePatchDisplayPage : ContentPage
{
static NinePatchDisplayPage()
{
using (SKCanvas canvas = new SKCanvas(FiveByFiveBitmap))
using (SKPaint paint = new SKPaint
{
Style = SKPaintStyle.Stroke,
Color = SKColors.Red,
StrokeWidth = 10
})
{
for (int x = 50; x < 500; x += 100)
for (int y = 50; y < 500; y += 100)
{
canvas.DrawCircle(x, y, 40, paint);
}
}
}
public static SKBitmap FiveByFiveBitmap { get; } = new SKBitmap(500, 500);
···
}
本文中的其他两个页使用相同的位图。 位图是 500 像素的正方形,由 25 个圆的数组构成,所有圆大小相同,每个圆占据 100 像素的正方形区域:
程序的实例构造函数创建一个带有 PaintSurface
处理程序的 SKCanvasView
,该处理程序使用 DrawBitmapNinePatch
显示拉伸到其整个显示表面的位图:
public class NinePatchDisplayPage : ContentPage
{
···
public NinePatchDisplayPage()
{
Title = "Nine-Patch Display";
SKCanvasView canvasView = new SKCanvasView();
canvasView.PaintSurface += OnCanvasViewPaintSurface;
Content = canvasView;
}
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
SKRectI centerRect = new SKRectI(100, 100, 400, 400);
canvas.DrawBitmapNinePatch(FiveByFiveBitmap, centerRect, info.Rect);
}
}
centerRect
矩形包含 16 个圆的中心数组。 位于角处的圆以其像素大小显示,其他所有内容会相应地拉伸:
UWP 页的宽度恰好为 500 像素,因此将顶行和底行显示为一系列相同大小的圆。 否则,所有不在角处的圆都会拉伸以形成椭圆。
如果由圆和椭圆的组合所构成的对象的显示比较奇怪,请尝试定义中心矩形,使其与圆的行和列重叠:
SKRectI centerRect = new SKRectI(150, 150, 350, 350);
格子显示
两个 DrawBitmapLattice
方法与 DrawBitmapNinePatch
类似,但它们已针对任意数量的水平或垂直区格进行通用化。 这些区格由与像素对应的整数数组定义。
具有这些整数数组参数的 DrawBitmapLattice
方法似乎不起作用。 具有类型为 SKLattice
的参数的 DrawBitmapLattice
方法确实有效,这就是下面所示示例中使用的方法。
SKLattice
结构定义四个属性:
XDivs
数组将位图的宽度划分为垂直条带。 第一个条带从左侧的像素 0 延伸到 XDivs[0]
。 该条带以其像素宽度进行渲染。 第二个条带从 XDivs[0]
延伸到 XDivs[1]
,并被拉伸。 第三个条带从 XDivs[1]
延伸到 XDivs[2]
,并以其像素宽度进行渲染。 最后一个条带从数组的最后一个元素延伸到位图的右边缘。 如果数组有偶数个元素,则以其像素宽度显示。 否则将其拉伸。 垂直条带的总数比数组中的元素数量多 1 个。
YDivs
数组类似。 它将数组的高度划分为水平条带。
XDivs
和 YDivs
数组一起将位图划分为矩形。 矩形数量等于水平条带数量和垂直条带数量的乘积。
根据 Skia 文档,Flags
数组包含每个矩形的一个元素,首先是矩形的顶行,然后是第二行,依此类推。 Flags
数组的类型为 SKLatticeFlags
,它是一个具有以下成员的枚举:
Default
,值为 0Transparent
,值为 1
但是,这些标志似乎并没有按预期工作,最好忽略它们。 但不要将 Flags
属性设置为 null
。 请将其设置为足以包含矩形总数的 SKLatticeFlags
值数组即可。
“九宫格”页使用 DrawBitmapLattice
来模仿 DrawBitmapNinePatch
。 它使用在 NinePatchDisplayPage
中创建的相同位图:
public class LatticeNinePatchPage : ContentPage
{
SKBitmap bitmap = NinePatchDisplayPage.FiveByFiveBitmap;
public LatticeNinePatchPage ()
{
Title = "Lattice Nine-Patch";
SKCanvasView canvasView = new SKCanvasView();
canvasView.PaintSurface += OnCanvasViewPaintSurface;
Content = canvasView;
}
`
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
SKLattice lattice = new SKLattice();
lattice.XDivs = new int[] { 100, 400 };
lattice.YDivs = new int[] { 100, 400 };
lattice.Flags = new SKLatticeFlags[9];
canvas.DrawBitmapLattice(bitmap, lattice, info.Rect);
}
}
XDivs
和 YDivs
属性均设置为仅包含两个整数的数组,将位图水平和垂直划分为三个条带:从像素 0 到像素 100(以像素大小渲染)、从像素 100 到像素 400(拉伸),从像素 400 到像素 500(像素大小)。 XDivs
和 YDivs
一起定义了总共 9 个矩形,这是 Flags
数组的大小。 只需创建该数组就足以创建 SKLatticeFlags.Default
值的数组。
显示内容与之前的程序相同:
“格子显示”页将位图划分为 16 个矩形:
public class LatticeDisplayPage : ContentPage
{
SKBitmap bitmap = NinePatchDisplayPage.FiveByFiveBitmap;
public LatticeDisplayPage()
{
Title = "Lattice Display";
SKCanvasView canvasView = new SKCanvasView();
canvasView.PaintSurface += OnCanvasViewPaintSurface;
Content = canvasView;
}
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
SKLattice lattice = new SKLattice();
lattice.XDivs = new int[] { 100, 200, 400 };
lattice.YDivs = new int[] { 100, 300, 400 };
int count = (lattice.XDivs.Length + 1) * (lattice.YDivs.Length + 1);
lattice.Flags = new SKLatticeFlags[count];
canvas.DrawBitmapLattice(bitmap, lattice, info.Rect);
}
}
XDivs
和 YDivs
数组有些不同,导致显示内容不像以上示例那样对称:
在左侧的 iOS 和 Android 图像中,仅以像素大小渲染较小的圆。 其他所有内容都拉伸了。
“格子显示”页通用化了 Flags
数组的创建,使你可以更轻松地使用 XDivs
和 YDivs
进行试验。 具体而言,需要了解将 XDivs
或 YDivs
数组的第一个元素设置为 0 时会发生什么情况。