A few changes and improvements (#1153)

A few changes and improvements:
- add some helper members
- improved the SKBitmap.CopyTo and related members
- added Encode(...) to SKBitmap
- added Encode(...) with a .NET Stream to SKBitmap, SKImage and SKPixmap
- start the move from SKEncoding to SKTextEncoding
- added more tests
- generator now has a validator for ensuring all declarations have implementations
- added equality members to all structs

Bugs Fixed
- Bitmaps copied with SKBitmap.Copy sometimes have incorrect colors
- The GC would sometimes collect a stream that was being used by a PDF document
This commit is contained in:
Matthew Leibowitz 2020-02-27 15:12:00 +02:00 коммит произвёл GitHub
Родитель a38b665127
Коммит fc9f030a08
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
70 изменённых файлов: 2417 добавлений и 973 удалений

Просмотреть файл

@ -58,6 +58,58 @@ namespace SkiaSharp
public static bool IsHorizontal (this SKPixelGeometry pg) =>
pg == SKPixelGeometry.BgrHorizontal || pg == SKPixelGeometry.RgbHorizontal;
public static SKTextEncoding ToTextEncoding (this SKEncoding encoding) =>
encoding switch
{
SKEncoding.Utf8 => SKTextEncoding.Utf8,
SKEncoding.Utf16 => SKTextEncoding.Utf16,
SKEncoding.Utf32 => SKTextEncoding.Utf32,
_ => throw new ArgumentOutOfRangeException (nameof (encoding)),
};
internal static SKEncoding ToEncoding (this SKTextEncoding encoding) =>
encoding switch
{
SKTextEncoding.Utf8 => SKEncoding.Utf8,
SKTextEncoding.Utf16 => SKEncoding.Utf16,
SKTextEncoding.Utf32 => SKEncoding.Utf32,
_ => throw new ArgumentOutOfRangeException (nameof (encoding)),
};
public static int GetBytesPerPixel (this SKColorType colorType) =>
colorType switch
{
SKColorType.Unknown => 0,
SKColorType.Alpha8 => 1,
SKColorType.Gray8 => 1,
SKColorType.Rgb565 => 2,
SKColorType.Argb4444 => 2,
SKColorType.Bgra8888 => 4,
SKColorType.Rgba8888 => 4,
SKColorType.Rgb888x => 4,
SKColorType.Rgba1010102 => 4,
SKColorType.Rgb101010x => 4,
SKColorType.RgbaF16 => 8,
_ => throw new ArgumentOutOfRangeException (nameof (colorType)),
};
public static SKAlphaType GetAlphaType (this SKColorType colorType, SKAlphaType alphaType = SKAlphaType.Premul) =>
colorType switch
{
SKColorType.Unknown => SKAlphaType.Unknown,
SKColorType.Alpha8 => SKAlphaType.Premul,
SKColorType.Gray8 => SKAlphaType.Opaque,
SKColorType.Rgb565 => SKAlphaType.Opaque,
SKColorType.Argb4444 => alphaType,
SKColorType.Bgra8888 => alphaType,
SKColorType.Rgba8888 => alphaType,
SKColorType.Rgb888x => SKAlphaType.Opaque,
SKColorType.Rgba1010102 => alphaType,
SKColorType.Rgb101010x => SKAlphaType.Opaque,
SKColorType.RgbaF16 => alphaType,
_ => throw new ArgumentOutOfRangeException (nameof (colorType)),
};
}
[EditorBrowsable (EditorBrowsableState.Never)]

Просмотреть файл

@ -75,6 +75,7 @@ namespace SkiaSharp
}
}
[Obsolete]
[StructLayout (LayoutKind.Sequential)]
internal struct GRTextureInfoObsolete
{

Просмотреть файл

@ -14,6 +14,8 @@ namespace SkiaSharp
protected override void Dispose (bool disposing) =>
base.Dispose (disposing);
// Create
public static GRContext Create (GRBackend backend) =>
backend switch
{
@ -43,12 +45,16 @@ namespace SkiaSharp
_ => throw new ArgumentOutOfRangeException (nameof (backend)),
};
// CreateGl
public static GRContext CreateGl () =>
CreateGl (null);
public static GRContext CreateGl (GRGlInterface backendContext) =>
GetObject<GRContext> (SkiaApi.gr_context_make_gl (backendContext == null ? IntPtr.Zero : backendContext.Handle));
//
public GRBackend Backend => SkiaApi.gr_context_get_backend (Handle);
public void AbandonContext (bool releaseResources = false)
@ -96,7 +102,7 @@ namespace SkiaSharp
SkiaApi.gr_context_get_max_surface_sample_count_for_color_type (Handle, colorType);
[EditorBrowsable (EditorBrowsableState.Never)]
[Obsolete ("Use GetMaxSurfaceSampleCount(SKColorType) instead.")]
[Obsolete]
public int GetRecommendedSampleCount (GRPixelConfig config, float dpi) => 0;
}
}

Просмотреть файл

@ -22,3 +22,10 @@ using System.Runtime.CompilerServices;
"3fbe2ff9c979ce998475e506e8ce82dd5b0f350dc10e93bf2eeecf874b24770c5081dbea7447fd" +
"dafa277b22de47d6ffea449674a4f9fccf84d15069089380284dbdd35f46cdff12a1bd78e4ef00" +
"65d016df")]
[assembly: InternalsVisibleTo("SkiaSharp.HarfBuzz, PublicKey=" +
"002400000480000094000000060200000024000052534131000400000100010079159977d2d03a" +
"8e6bea7a2e74e8d1afcc93e8851974952bb480a12c9134474d04062447c37e0e68c080536fcf3c" +
"3fbe2ff9c979ce998475e506e8ce82dd5b0f350dc10e93bf2eeecf874b24770c5081dbea7447fd" +
"dafa277b22de47d6ffea449674a4f9fccf84d15069089380284dbdd35f46cdff12a1bd78e4ef00" +
"65d016df")]

Просмотреть файл

@ -111,6 +111,8 @@ namespace SkiaSharp
protected override void DisposeNative () =>
SkiaApi.sk_bitmap_destructor (Handle);
// TryAllocPixels
public bool TryAllocPixels (SKImageInfo info)
{
return TryAllocPixels (info, info.RowBytes);
@ -128,16 +130,22 @@ namespace SkiaSharp
return SkiaApi.sk_bitmap_try_alloc_pixels_with_flags (Handle, &cinfo, (uint)flags);
}
// Reset
public void Reset ()
{
SkiaApi.sk_bitmap_reset (Handle);
}
// SetImmutable
public void SetImmutable ()
{
SkiaApi.sk_bitmap_set_immutable (Handle);
}
// Erase
public void Erase (SKColor color)
{
SkiaApi.sk_bitmap_erase (Handle, (uint)color);
@ -148,11 +156,18 @@ namespace SkiaSharp
SkiaApi.sk_bitmap_erase_rect (Handle, (uint)color, &rect);
}
// GetAddr*
public byte GetAddr8 (int x, int y) => SkiaApi.sk_bitmap_get_addr_8 (Handle, x, y);
public UInt16 GetAddr16 (int x, int y) => SkiaApi.sk_bitmap_get_addr_16 (Handle, x, y);
public UInt32 GetAddr32 (int x, int y) => SkiaApi.sk_bitmap_get_addr_32 (Handle, x, y);
public IntPtr GetAddr (int x, int y) => (IntPtr)SkiaApi.sk_bitmap_get_addr (Handle, x, y);
// Pixels (color)
[EditorBrowsable (EditorBrowsableState.Never)]
[Obsolete ("The Index8 color type and color table is no longer supported. Use GetPixel(int, int) instead.")]
public SKPMColor GetIndex8Color (int x, int y)
@ -170,41 +185,21 @@ namespace SkiaSharp
SkiaApi.sk_bitmap_set_pixel_color (Handle, x, y, (uint)color);
}
// Copy
public bool CanCopyTo (SKColorType colorType)
{
var srcCT = ColorType;
// TODO: optimize as this does more work that we really want
if (srcCT == SKColorType.Unknown) {
if (colorType == SKColorType.Unknown)
return false;
}
if (srcCT == SKColorType.Alpha8 && colorType != SKColorType.Alpha8) {
return false; // can't convert from alpha to non-alpha
}
bool sameConfigs = (srcCT == colorType);
switch (colorType) {
case SKColorType.Alpha8:
case SKColorType.Rgb565:
case SKColorType.Rgba8888:
case SKColorType.Bgra8888:
case SKColorType.Rgb888x:
case SKColorType.Rgba1010102:
case SKColorType.Rgb101010x:
case SKColorType.RgbaF16:
break;
case SKColorType.Gray8:
if (!sameConfigs) {
return false;
}
break;
case SKColorType.Argb4444:
return
sameConfigs ||
srcCT == SKImageInfo.PlatformColorType;
default:
return false;
}
return true;
using var bmp = new SKBitmap ();
var info = Info
.WithColorType (colorType)
.WithSize (1, 1);
return bmp.TryAllocPixels (info);
}
public SKBitmap Copy ()
@ -232,72 +227,37 @@ namespace SkiaSharp
public bool CopyTo (SKBitmap destination, SKColorType colorType)
{
if (destination == null) {
if (destination == null)
throw new ArgumentNullException (nameof (destination));
}
if (!CanCopyTo (colorType)) {
if (colorType == SKColorType.Unknown)
return false;
}
var srcPM = PeekPixels ();
if (srcPM == null) {
using var srcPixmap = PeekPixels ();
if (srcPixmap == null)
return false;
}
var dstInfo = srcPM.Info.WithColorType (colorType);
switch (colorType) {
case SKColorType.Rgb565:
// CopyTo() is not strict on alpha type. Here we set the src to opaque to allow
// the call to ReadPixels() to succeed and preserve this lenient behavior.
if (srcPM.AlphaType != SKAlphaType.Opaque) {
srcPM = srcPM.WithAlphaType (SKAlphaType.Opaque);
}
dstInfo.AlphaType = SKAlphaType.Opaque;
break;
case SKColorType.RgbaF16:
// The caller does not have an opportunity to pass a dst color space.
// Assume that they want linear sRGB.
dstInfo.ColorSpace = SKColorSpace.CreateSrgbLinear ();
if (srcPM.ColorSpace == null) {
// We can't do a sane conversion to F16 without a dst color space.
// Guess sRGB in this case.
srcPM = srcPM.WithColorSpace (SKColorSpace.CreateSrgb ());
}
break;
}
using var temp = new SKBitmap ();
var tmpDst = new SKBitmap ();
if (!tmpDst.TryAllocPixels (dstInfo)) {
var dstInfo = srcPixmap.Info.WithColorType (colorType);
if (!temp.TryAllocPixels (dstInfo))
return false;
}
var dstPM = tmpDst.PeekPixels ();
if (dstPM == null) {
return false;
}
using var canvas = new SKCanvas (temp);
// We can't do a sane conversion from F16 without a src color space. Guess sRGB in this case.
if (srcPM.ColorType == SKColorType.RgbaF16 && dstPM.ColorSpace == null) {
dstPM = dstPM.WithColorSpace (SKColorSpace.CreateSrgb ());
}
using var paint = new SKPaint {
Shader = ToShader (),
BlendMode = SKBlendMode.Src
};
// ReadPixels does not yet support color spaces with parametric transfer functions. This
// works around that restriction when the color spaces are equal.
if (colorType != SKColorType.RgbaF16 && srcPM.ColorType != SKColorType.RgbaF16 && dstPM.ColorSpace == srcPM.ColorSpace) {
dstPM = dstPM.WithColorSpace (null);
srcPM = srcPM.WithColorSpace (null);
}
if (!srcPM.ReadPixels (dstPM)) {
return false;
}
destination.Swap (tmpDst);
canvas.DrawPaint (paint);
destination.Swap (temp);
return true;
}
// ExtractSubset
public bool ExtractSubset (SKBitmap destination, SKRectI subset)
{
if (destination == null) {
@ -306,6 +266,8 @@ namespace SkiaSharp
return SkiaApi.sk_bitmap_extract_subset (Handle, destination.Handle, &subset);
}
// ExtractAlpha
public bool ExtractAlpha (SKBitmap destination)
{
return ExtractAlpha (destination, null, out var offset);
@ -331,6 +293,8 @@ namespace SkiaSharp
}
}
// properties
public bool ReadyToDraw => SkiaApi.sk_bitmap_ready_to_draw (Handle);
public SKImageInfo Info {
@ -373,6 +337,8 @@ namespace SkiaSharp
get { return (int)SkiaApi.sk_bitmap_get_byte_count (Handle); }
}
// *Pixels*
public IntPtr GetPixels () =>
GetPixels (out _);
@ -400,6 +366,8 @@ namespace SkiaSharp
SetPixels (pixels);
}
// SetColorTable
[EditorBrowsable (EditorBrowsableState.Never)]
[Obsolete ("The Index8 color type and color table is no longer supported.")]
public void SetColorTable (SKColorTable ct)
@ -407,6 +375,8 @@ namespace SkiaSharp
// no-op due to unsupperted action
}
// more properties
public byte[] Bytes {
get {
var array = GetPixelSpan ().ToArray ();
@ -456,6 +426,8 @@ namespace SkiaSharp
[Obsolete ("The Index8 color type and color table is no longer supported.")]
public SKColorTable ColorTable => null;
// DecodeBounds
public static SKImageInfo DecodeBounds (Stream stream)
{
if (stream == null) {
@ -496,20 +468,20 @@ namespace SkiaSharp
}
}
public static SKImageInfo DecodeBounds (byte[] buffer)
{
if (buffer == null) {
throw new ArgumentNullException (nameof (buffer));
}
public static SKImageInfo DecodeBounds (byte[] buffer) =>
DecodeBounds (buffer.AsSpan ());
public static SKImageInfo DecodeBounds (ReadOnlySpan<byte> buffer)
{
fixed (byte* b = buffer) {
using (var skdata = SKData.Create ((IntPtr)b, buffer.Length))
using (var codec = SKCodec.Create (skdata)) {
return codec?.Info ?? SKImageInfo.Empty;
}
using var skdata = SKData.Create ((IntPtr)b, buffer.Length);
using var codec = SKCodec.Create (skdata);
return codec?.Info ?? SKImageInfo.Empty;
}
}
// Decode
public static SKBitmap Decode (SKCodec codec)
{
if (codec == null) {
@ -643,30 +615,32 @@ namespace SkiaSharp
}
}
public static SKBitmap Decode (byte[] buffer)
{
if (buffer == null) {
throw new ArgumentNullException (nameof (buffer));
}
using (var stream = new SKMemoryStream (buffer)) {
return Decode (stream);
}
}
public static SKBitmap Decode (byte[] buffer) =>
Decode (buffer.AsSpan ());
public static SKBitmap Decode (byte[] buffer, SKImageInfo bitmapInfo)
{
if (buffer == null) {
throw new ArgumentNullException (nameof (buffer));
}
public static SKBitmap Decode (byte[] buffer, SKImageInfo bitmapInfo) =>
Decode (buffer.AsSpan (), bitmapInfo);
public static SKBitmap Decode (ReadOnlySpan<byte> buffer)
{
fixed (byte* b = buffer) {
using (var skdata = SKData.Create ((IntPtr)b, buffer.Length))
using (var codec = SKCodec.Create (skdata)) {
return Decode (codec, bitmapInfo);
}
using var skdata = SKData.Create ((IntPtr)b, buffer.Length);
using var codec = SKCodec.Create (skdata);
return Decode (codec);
}
}
public static SKBitmap Decode (ReadOnlySpan<byte> buffer, SKImageInfo bitmapInfo)
{
fixed (byte* b = buffer) {
using var skdata = SKData.Create ((IntPtr)b, buffer.Length);
using var codec = SKCodec.Create (skdata);
return Decode (codec, bitmapInfo);
}
}
// InstallPixels
public bool InstallPixels (SKImageInfo info, IntPtr pixels)
{
return InstallPixels (info, pixels, info.RowBytes, null, null);
@ -711,16 +685,22 @@ namespace SkiaSharp
return SkiaApi.sk_bitmap_install_pixels_with_pixmap (Handle, pixmap.Handle);
}
// InstallMaskPixels
public bool InstallMaskPixels (SKMask mask)
{
return SkiaApi.sk_bitmap_install_mask_pixels (Handle, &mask);
}
// NotifyPixelsChanged
public void NotifyPixelsChanged ()
{
SkiaApi.sk_bitmap_notify_pixels_changed (Handle);
}
// PeekPixels
public SKPixmap PeekPixels ()
{
SKPixmap pixmap = new SKPixmap ();
@ -741,6 +721,8 @@ namespace SkiaSharp
return SkiaApi.sk_bitmap_peek_pixels (Handle, pixmap.Handle);
}
// Resize
[EditorBrowsable (EditorBrowsableState.Never)]
[Obsolete ("Use Resize(SKImageInfo, SKFilterQuality) instead.")]
public SKBitmap Resize (SKImageInfo info, SKBitmapResizeMethod method) =>
@ -767,6 +749,11 @@ namespace SkiaSharp
}
}
public SKBitmap Resize (SKSizeI size, SKFilterQuality quality) =>
Resize (Info.WithSize (size), quality);
// ScalePixels
public bool ScalePixels (SKBitmap destination, SKFilterQuality quality)
{
if (destination == null) {
@ -789,6 +776,8 @@ namespace SkiaSharp
}
}
// From/ToImage
public static SKBitmap FromImage (SKImage image)
{
if (image == null) {
@ -804,13 +793,31 @@ namespace SkiaSharp
return bmp;
}
[EditorBrowsable (EditorBrowsableState.Never)]
[Obsolete ("Use SKPixmap.Encode instead.")]
// Encode
public SKData Encode (SKEncodedImageFormat format, int quality)
{
using var pixmap = PeekPixels ();
return pixmap?.Encode (format, quality);
}
public bool Encode (Stream dst, SKEncodedImageFormat format, int quality)
{
using var wrapped = new SKManagedWStream (dst);
return Encode (wrapped, format, quality);
}
public bool Encode (SKWStream dst, SKEncodedImageFormat format, int quality)
{
return SKPixmap.Encode (dst, this, format, quality);
if (dst == null)
throw new ArgumentNullException (nameof (dst));
using var pixmap = PeekPixels ();
return pixmap?.Encode (dst, format, quality) ?? false;
}
// Swap
private void Swap (SKBitmap other)
{
SkiaApi.sk_bitmap_swap (Handle, other.Handle);

Просмотреть файл

@ -32,6 +32,8 @@ namespace SkiaSharp
public void Discard () =>
SkiaApi.sk_canvas_discard (Handle);
// QuickReject
public bool QuickReject (SKRect rect)
{
return SkiaApi.sk_canvas_quick_reject (Handle, &rect);
@ -44,6 +46,8 @@ namespace SkiaSharp
return path.IsEmpty || QuickReject (path.Bounds);
}
// Save*
public int Save ()
{
if (Handle == IntPtr.Zero)
@ -61,11 +65,18 @@ namespace SkiaSharp
return SkiaApi.sk_canvas_save_layer (Handle, null, paint == null ? IntPtr.Zero : paint.Handle);
}
public int SaveLayer () =>
SaveLayer (null);
// DrawColor
public void DrawColor (SKColor color, SKBlendMode mode = SKBlendMode.Src)
{
SkiaApi.sk_canvas_draw_color (Handle, (uint)color, mode);
}
// DrawLine
public void DrawLine (SKPoint p0, SKPoint p1, SKPaint paint)
{
DrawLine (p0.X, p0.Y, p1.X, p1.Y, paint);
@ -78,6 +89,8 @@ namespace SkiaSharp
SkiaApi.sk_canvas_draw_line (Handle, x0, y0, x1, y1, paint.Handle);
}
// Clear
public void Clear ()
{
DrawColor (SKColors.Empty, SKBlendMode.Src);
@ -88,6 +101,8 @@ namespace SkiaSharp
DrawColor (color, SKBlendMode.Src);
}
// Restore*
public void Restore ()
{
SkiaApi.sk_canvas_restore (Handle);
@ -98,6 +113,8 @@ namespace SkiaSharp
SkiaApi.sk_canvas_restore_to_count (Handle, count);
}
// Translate
public void Translate (float dx, float dy)
{
SkiaApi.sk_canvas_translate (Handle, dx, dy);
@ -107,7 +124,9 @@ namespace SkiaSharp
{
SkiaApi.sk_canvas_translate (Handle, point.X, point.Y);
}
// Scale
public void Scale (float s)
{
SkiaApi.sk_canvas_scale (Handle, s, s);
@ -130,6 +149,8 @@ namespace SkiaSharp
Translate (-px, -py);
}
// Rotate*
public void RotateDegrees (float degrees)
{
SkiaApi.sk_canvas_rotate_degrees (Handle, degrees);
@ -147,13 +168,15 @@ namespace SkiaSharp
Translate (-px, -py);
}
public void RotateRadians(float radians, float px, float py)
public void RotateRadians (float radians, float px, float py)
{
Translate (px, py);
RotateRadians (radians);
Translate (-px, -py);
}
// Skew
public void Skew (float sx, float sy)
{
SkiaApi.sk_canvas_skew (Handle, sx, sy);
@ -164,6 +187,8 @@ namespace SkiaSharp
SkiaApi.sk_canvas_skew (Handle, skew.X, skew.Y);
}
// Concat
public void Concat (ref SKMatrix m)
{
fixed (SKMatrix* ptr = &m) {
@ -171,6 +196,8 @@ namespace SkiaSharp
}
}
// Clip*
public void ClipRect (SKRect rect, SKClipOperation operation = SKClipOperation.Intersect, bool antialias = false)
{
SkiaApi.sk_canvas_clip_rect_with_operation (Handle, &rect, operation, antialias);
@ -188,7 +215,7 @@ namespace SkiaSharp
{
if (path == null)
throw new ArgumentNullException (nameof (path));
SkiaApi.sk_canvas_clip_path_with_operation (Handle, path.Handle, operation, antialias);
}
@ -232,6 +259,8 @@ namespace SkiaSharp
}
}
// DrawPaint
public void DrawPaint (SKPaint paint)
{
if (paint == null)
@ -239,6 +268,8 @@ namespace SkiaSharp
SkiaApi.sk_canvas_draw_paint (Handle, paint.Handle);
}
// DrawRegion
public void DrawRegion (SKRegion region, SKPaint paint)
{
if (region == null)
@ -248,6 +279,8 @@ namespace SkiaSharp
SkiaApi.sk_canvas_draw_region (Handle, region.Handle, paint.Handle);
}
// DrawRect
public void DrawRect (float x, float y, float w, float h, SKPaint paint)
{
DrawRect (SKRect.Create (x, y, w, h), paint);
@ -260,6 +293,8 @@ namespace SkiaSharp
SkiaApi.sk_canvas_draw_rect (Handle, &rect, paint.Handle);
}
// DrawRoundRect
public void DrawRoundRect (SKRoundRect rect, SKPaint paint)
{
if (rect == null)
@ -286,6 +321,8 @@ namespace SkiaSharp
DrawRoundRect (rect, r.Width, r.Height, paint);
}
// DrawOval
public void DrawOval (float cx, float cy, float rx, float ry, SKPaint paint)
{
DrawOval (new SKRect (cx - rx, cy - ry, cx + rx, cy + ry), paint);
@ -303,6 +340,8 @@ namespace SkiaSharp
SkiaApi.sk_canvas_draw_oval (Handle, &rect, paint.Handle);
}
// DrawCircle
public void DrawCircle (float cx, float cy, float radius, SKPaint paint)
{
if (paint == null)
@ -314,7 +353,9 @@ namespace SkiaSharp
{
DrawCircle (c.X, c.Y, radius, paint);
}
// DrawPath
public void DrawPath (SKPath path, SKPaint paint)
{
if (paint == null)
@ -324,7 +365,9 @@ namespace SkiaSharp
SkiaApi.sk_canvas_draw_path (Handle, path.Handle, paint.Handle);
}
public void DrawPoints (SKPointMode mode, SKPoint [] points, SKPaint paint)
// DrawPoints
public void DrawPoints (SKPointMode mode, SKPoint[] points, SKPaint paint)
{
if (paint == null)
throw new ArgumentNullException (nameof (paint));
@ -335,6 +378,8 @@ namespace SkiaSharp
}
}
// DrawPoint
public void DrawPoint (SKPoint p, SKPaint paint)
{
DrawPoint (p.X, p.Y, paint);
@ -359,6 +404,8 @@ namespace SkiaSharp
}
}
// DrawImage
public void DrawImage (SKImage image, SKPoint p, SKPaint paint = null)
{
DrawImage (image, p.X, p.Y, paint);
@ -385,6 +432,8 @@ namespace SkiaSharp
SkiaApi.sk_canvas_draw_image_rect (Handle, image.Handle, &source, &dest, paint == null ? IntPtr.Zero : paint.Handle);
}
// DrawPicture
public void DrawPicture (SKPicture picture, float x, float y, SKPaint paint = null)
{
var matrix = SKMatrix.MakeTranslation (x, y);
@ -412,6 +461,8 @@ namespace SkiaSharp
SkiaApi.sk_canvas_draw_picture (Handle, picture.Handle, null, paint == null ? IntPtr.Zero : paint.Handle);
}
// DrawDrawable
public void DrawDrawable (SKDrawable drawable, ref SKMatrix matrix)
{
if (drawable == null)
@ -437,6 +488,8 @@ namespace SkiaSharp
DrawDrawable (drawable, ref matrix);
}
// DrawBitmap
public void DrawBitmap (SKBitmap bitmap, SKPoint p, SKPaint paint = null)
{
DrawBitmap (bitmap, p.X, p.Y, paint);
@ -463,6 +516,8 @@ namespace SkiaSharp
SkiaApi.sk_canvas_draw_bitmap_rect (Handle, bitmap.Handle, &source, &dest, paint == null ? IntPtr.Zero : paint.Handle);
}
// DrawSurface
public void DrawSurface (SKSurface surface, SKPoint p, SKPaint paint = null)
{
DrawSurface (surface, p.X, p.Y, paint);
@ -476,6 +531,8 @@ namespace SkiaSharp
surface.Draw (this, x, y, paint);
}
// DrawText (SKTextBlob)
public void DrawText (SKTextBlob text, float x, float y, SKPaint paint)
{
if (text == null)
@ -486,6 +543,8 @@ namespace SkiaSharp
SkiaApi.sk_canvas_draw_text_blob (Handle, text.Handle, x, y, paint.Handle);
}
// DrawText
public void DrawText (string text, SKPoint p, SKPaint paint)
{
DrawText (text, p.X, p.Y, paint);
@ -519,7 +578,24 @@ namespace SkiaSharp
}
}
public void DrawPositionedText (string text, SKPoint [] points, SKPaint paint)
public void DrawText (IntPtr buffer, int length, SKPoint p, SKPaint paint)
{
DrawText (buffer, length, p.X, p.Y, paint);
}
public void DrawText (IntPtr buffer, int length, float x, float y, SKPaint paint)
{
if (buffer == IntPtr.Zero && length != 0)
throw new ArgumentNullException (nameof (buffer));
if (paint == null)
throw new ArgumentNullException (nameof (paint));
SkiaApi.sk_canvas_draw_text (Handle, (void*)buffer, (IntPtr)length, x, y, paint.Handle);
}
// DrawPositionedText
public void DrawPositionedText (string text, SKPoint[] points, SKPaint paint)
{
if (text == null)
throw new ArgumentNullException (nameof (text));
@ -532,7 +608,7 @@ namespace SkiaSharp
DrawPositionedText (bytes, points, paint);
}
public void DrawPositionedText (byte[] text, SKPoint [] points, SKPaint paint)
public void DrawPositionedText (byte[] text, SKPoint[] points, SKPaint paint)
{
if (text == null)
throw new ArgumentNullException (nameof (text));
@ -547,38 +623,6 @@ namespace SkiaSharp
}
}
public void DrawTextOnPath (IntPtr buffer, int length, SKPath path, SKPoint offset, SKPaint paint)
{
DrawTextOnPath (buffer, length, path, offset.X, offset.Y, paint);
}
public void DrawTextOnPath (IntPtr buffer, int length, SKPath path, float hOffset, float vOffset, SKPaint paint)
{
if (buffer == IntPtr.Zero && length != 0)
throw new ArgumentNullException (nameof (buffer));
if (paint == null)
throw new ArgumentNullException (nameof (paint));
if (paint == null)
throw new ArgumentNullException (nameof (paint));
SkiaApi.sk_canvas_draw_text_on_path (Handle, (void*)buffer, (IntPtr)length, path.Handle, hOffset, vOffset, paint.Handle);
}
public void DrawText (IntPtr buffer, int length, SKPoint p, SKPaint paint)
{
DrawText (buffer, length, p.X, p.Y, paint);
}
public void DrawText (IntPtr buffer, int length, float x, float y, SKPaint paint)
{
if (buffer == IntPtr.Zero && length != 0)
throw new ArgumentNullException (nameof (buffer));
if (paint == null)
throw new ArgumentNullException (nameof (paint));
SkiaApi.sk_canvas_draw_text (Handle, (void*)buffer, (IntPtr)length, x, y, paint.Handle);
}
public void DrawPositionedText (IntPtr buffer, int length, SKPoint[] points, SKPaint paint)
{
if (buffer == IntPtr.Zero && length != 0)
@ -593,6 +637,8 @@ namespace SkiaSharp
}
}
// DrawTextOnPath
public void DrawTextOnPath (string text, SKPath path, SKPoint offset, SKPaint paint)
{
DrawTextOnPath (text, path, offset.X, offset.Y, paint);
@ -602,8 +648,8 @@ namespace SkiaSharp
{
if (text == null)
throw new ArgumentNullException (nameof (text));
if (paint == null)
throw new ArgumentNullException (nameof (paint));
if (path == null)
throw new ArgumentNullException (nameof (path));
if (paint == null)
throw new ArgumentNullException (nameof (paint));
@ -620,8 +666,8 @@ namespace SkiaSharp
{
if (text == null)
throw new ArgumentNullException (nameof (text));
if (paint == null)
throw new ArgumentNullException (nameof (paint));
if (path == null)
throw new ArgumentNullException (nameof (path));
if (paint == null)
throw new ArgumentNullException (nameof (paint));
@ -630,11 +676,32 @@ namespace SkiaSharp
}
}
public void DrawTextOnPath (IntPtr buffer, int length, SKPath path, SKPoint offset, SKPaint paint)
{
DrawTextOnPath (buffer, length, path, offset.X, offset.Y, paint);
}
public void DrawTextOnPath (IntPtr buffer, int length, SKPath path, float hOffset, float vOffset, SKPaint paint)
{
if (buffer == IntPtr.Zero && length != 0)
throw new ArgumentNullException (nameof (buffer));
if (path == null)
throw new ArgumentNullException (nameof (path));
if (paint == null)
throw new ArgumentNullException (nameof (paint));
SkiaApi.sk_canvas_draw_text_on_path (Handle, (void*)buffer, (IntPtr)length, path.Handle, hOffset, vOffset, paint.Handle);
}
// Flush
public void Flush ()
{
SkiaApi.sk_canvas_flush (Handle);
}
// Draw*Annotation
public void DrawAnnotation (SKRect rect, string key, SKData value)
{
var bytes = StringUtilities.GetEncodedText (key, SKTextEncoding.Utf8);
@ -679,6 +746,8 @@ namespace SkiaSharp
return data;
}
// Draw*NinePatch
public void DrawBitmapNinePatch (SKBitmap bitmap, SKRectI center, SKRect dst, SKPaint paint = null)
{
if (bitmap == null)
@ -701,6 +770,8 @@ namespace SkiaSharp
SkiaApi.sk_canvas_draw_image_nine (Handle, image.Handle, &center, &dst, paint == null ? IntPtr.Zero : paint.Handle);
}
// Draw*Lattice
public void DrawBitmapLattice (SKBitmap bitmap, int[] xDivs, int[] yDivs, SKRect dst, SKPaint paint = null)
{
var lattice = new SKLattice {
@ -718,7 +789,7 @@ namespace SkiaSharp
};
DrawImageLattice (image, lattice, dst, paint);
}
public void DrawBitmapLattice (SKBitmap bitmap, SKLattice lattice, SKRect dst, SKPaint paint = null)
{
if (bitmap == null)
@ -757,7 +828,7 @@ namespace SkiaSharp
throw new ArgumentNullException (nameof (lattice.XDivs));
if (lattice.YDivs == null)
throw new ArgumentNullException (nameof (lattice.YDivs));
fixed (int* x = lattice.XDivs)
fixed (int* y = lattice.YDivs)
fixed (SKLatticeRectType* r = lattice.RectTypes)
@ -779,6 +850,8 @@ namespace SkiaSharp
}
}
// *Matrix
public void ResetMatrix ()
{
SkiaApi.sk_canvas_reset_matrix (Handle);
@ -791,30 +864,34 @@ namespace SkiaSharp
public SKMatrix TotalMatrix {
get {
SKMatrix matrix = new SKMatrix();
SKMatrix matrix;
SkiaApi.sk_canvas_get_total_matrix (Handle, &matrix);
return matrix;
}
}
// SaveCount
public int SaveCount => SkiaApi.sk_canvas_get_save_count (Handle);
// DrawVertices
public void DrawVertices (SKVertexMode vmode, SKPoint[] vertices, SKColor[] colors, SKPaint paint)
{
var vert = SKVertices.CreateCopy(vmode, vertices, colors);
DrawVertices(vert, SKBlendMode.Modulate, paint);
var vert = SKVertices.CreateCopy (vmode, vertices, colors);
DrawVertices (vert, SKBlendMode.Modulate, paint);
}
public void DrawVertices (SKVertexMode vmode, SKPoint[] vertices, SKPoint[] texs, SKColor[] colors, SKPaint paint)
{
var vert = SKVertices.CreateCopy(vmode, vertices, texs, colors);
DrawVertices(vert, SKBlendMode.Modulate, paint);
var vert = SKVertices.CreateCopy (vmode, vertices, texs, colors);
DrawVertices (vert, SKBlendMode.Modulate, paint);
}
public void DrawVertices (SKVertexMode vmode, SKPoint[] vertices, SKPoint[] texs, SKColor[] colors, UInt16[] indices, SKPaint paint)
{
var vert = SKVertices.CreateCopy(vmode, vertices, texs, colors, indices);
DrawVertices(vert, SKBlendMode.Modulate, paint);
var vert = SKVertices.CreateCopy (vmode, vertices, texs, colors, indices);
DrawVertices (vert, SKBlendMode.Modulate, paint);
}
public void DrawVertices (SKVertexMode vmode, SKPoint[] vertices, SKPoint[] texs, SKColor[] colors, SKBlendMode mode, UInt16[] indices, SKPaint paint)
@ -832,6 +909,8 @@ namespace SkiaSharp
SkiaApi.sk_canvas_draw_vertices (Handle, vertices.Handle, mode, paint.Handle);
}
// DrawArc
public void DrawArc (SKRect oval, float startAngle, float sweepAngle, bool useCenter, SKPaint paint)
{
if (paint == null)
@ -839,6 +918,8 @@ namespace SkiaSharp
SkiaApi.sk_canvas_draw_arc (Handle, &oval, startAngle, sweepAngle, useCenter, paint.Handle);
}
// DrawRoundRectDifference
public void DrawRoundRectDifference (SKRoundRect outer, SKRoundRect inner, SKPaint paint)
{
if (outer == null)
@ -851,6 +932,8 @@ namespace SkiaSharp
SkiaApi.sk_canvas_draw_drrect (Handle, outer.Handle, inner.Handle, paint.Handle);
}
// DrawAtlas
public void DrawAtlas (SKImage atlas, SKRect[] sprites, SKRotationScaleMatrix[] transforms, SKPaint paint) =>
DrawAtlas (atlas, sprites, transforms, null, SKBlendMode.Dst, null, paint);
@ -881,6 +964,8 @@ namespace SkiaSharp
}
}
// DrawPatch
public void DrawPatch (SKPoint[] cubics, SKColor[] colors, SKPoint[] texCoords, SKPaint paint) =>
DrawPatch (cubics, colors, texCoords, SKBlendMode.Modulate, paint);
@ -949,4 +1034,3 @@ namespace SkiaSharp
}
}
}

Просмотреть файл

@ -100,8 +100,8 @@ namespace SkiaSharp
public readonly bool Equals (SKColor obj) =>
obj.color == color;
public readonly override bool Equals (object obj) =>
obj is SKColor f && Equals (f);
public readonly override bool Equals (object other) =>
other is SKColor f && Equals (f);
public static bool operator == (SKColor left, SKColor right) =>
left.Equals (right);

Просмотреть файл

@ -29,6 +29,8 @@ namespace SkiaSharp
protected override void Dispose (bool disposing) =>
base.Dispose (disposing);
// properties
public bool GammaIsCloseToSrgb =>
SkiaApi.sk_colorspace_gamma_close_to_srgb (Handle);
@ -57,10 +59,16 @@ namespace SkiaSharp
return SkiaApi.sk_colorspace_equals (left.Handle, right.Handle);
}
// CreateSrgb
public static SKColorSpace CreateSrgb () => srgb;
// CreateSrgbLinear
public static SKColorSpace CreateSrgbLinear () => srgbLinear;
// CreateIcc
public static SKColorSpace CreateIcc (IntPtr input, long length)
{
if (input == IntPtr.Zero)
@ -89,6 +97,8 @@ namespace SkiaSharp
}
}
// CreateRgb
[EditorBrowsable (EditorBrowsableState.Never)]
[Obsolete ("Use CreateRgb(SKColorSpaceRenderTargetGamma, SKMatrix44) instead.")]
public static SKColorSpace CreateRgb (SKColorSpaceRenderTargetGamma gamma, SKMatrix44 toXyzD50, SKColorSpaceFlags flags) =>
@ -142,13 +152,7 @@ namespace SkiaSharp
public static SKColorSpace CreateRgb (SKNamedGamma gamma, SKColorSpaceGamut gamut) =>
GetObject<SKColorSpace> (SkiaApi.sk_colorspace_new_rgb_with_gamma_named_and_gamut (gamma, gamut));
public bool ToXyzD50 (SKMatrix44 toXyzD50)
{
if (toXyzD50 == null)
throw new ArgumentNullException (nameof (toXyzD50));
return SkiaApi.sk_colorspace_to_xyzd50 (Handle, toXyzD50.Handle);
}
// GetNumericalTransferFunction
public bool GetNumericalTransferFunction (out SKColorSpaceTransferFn fn)
{
@ -157,12 +161,24 @@ namespace SkiaSharp
}
}
// *XyzD50
public SKMatrix44 ToXyzD50 () =>
GetObject<SKMatrix44> (SkiaApi.sk_colorspace_as_to_xyzd50 (Handle), false);
public bool ToXyzD50 (SKMatrix44 toXyzD50)
{
if (toXyzD50 == null)
throw new ArgumentNullException (nameof (toXyzD50));
return SkiaApi.sk_colorspace_to_xyzd50 (Handle, toXyzD50.Handle);
}
public SKMatrix44 FromXyzD50 () =>
GetObject<SKMatrix44> (SkiaApi.sk_colorspace_as_from_xyzd50 (Handle), false);
//
private sealed class SKColorSpaceStatic : SKColorSpace
{
internal SKColorSpaceStatic (IntPtr x)

Просмотреть файл

@ -3,6 +3,7 @@ using System.Runtime.InteropServices;
using System.IO;
using System.Text;
using System.ComponentModel;
using System.Buffers;
namespace SkiaSharp
{
@ -41,6 +42,8 @@ namespace SkiaSharp
public static SKData Empty => empty.Value;
// CreateCopy
public static SKData CreateCopy (IntPtr bytes, ulong length)
{
if (SizeOf <IntPtr> () == 4 && length > UInt32.MaxValue)
@ -65,6 +68,8 @@ namespace SkiaSharp
}
}
// Create
public static SKData Create (int size)
{
return GetObject<SKData> (SkiaApi.sk_data_new_uninitialized ((IntPtr) size));
@ -83,7 +88,7 @@ namespace SkiaSharp
if (string.IsNullOrEmpty (filename))
throw new ArgumentException ("The filename cannot be empty.", nameof (filename));
var utf8path = StringUtilities.GetEncodedText (filename, SKEncoding.Utf8);
var utf8path = StringUtilities.GetEncodedText (filename, SKTextEncoding.Utf8);
fixed (byte* u = utf8path) {
return GetObject<SKData> (SkiaApi.sk_data_new_from_file (u));
}
@ -180,6 +185,8 @@ namespace SkiaSharp
return SKData.CreateCopy (bytes, (ulong)(bytes.Length + 1)); // + 1 for the terminating char
}
// Subset
public SKData Subset (ulong offset, ulong length)
{
if (SizeOf <IntPtr> () == 4) {
@ -191,6 +198,8 @@ namespace SkiaSharp
return GetObject<SKData> (SkiaApi.sk_data_new_subset (Handle, (IntPtr) offset, (IntPtr) length));
}
// ToArray
public byte[] ToArray ()
{
var array = AsSpan ().ToArray ();
@ -198,41 +207,54 @@ namespace SkiaSharp
return array;
}
// properties
public bool IsEmpty => Size == 0;
public long Size => (long)SkiaApi.sk_data_get_size (Handle);
public IntPtr Data => (IntPtr)SkiaApi.sk_data_get_data (Handle);
// AsStream
public Stream AsStream () =>
new SKDataStream (this, false);
public Stream AsStream (bool streamDisposesData) =>
new SKDataStream (this, streamDisposesData);
// AsSpan
public ReadOnlySpan<byte> AsSpan ()
{
return new ReadOnlySpan<byte> ((void*)Data, (int)Size);
}
// SaveTo
public void SaveTo (Stream target)
{
if (target == null)
throw new ArgumentNullException (nameof (target));
var buffer = new byte [CopyBufferSize];
var ptr = Data;
var total = Size;
for (var left = total; left > 0; ) {
var copyCount = (int) Math.Min (CopyBufferSize, left);
Marshal.Copy (ptr, buffer, 0, copyCount);
left -= copyCount;
ptr += copyCount;
target.Write (buffer, 0, copyCount);
var buffer = ArrayPool<byte>.Shared.Rent (CopyBufferSize);
try {
for (var left = total; left > 0;) {
var copyCount = (int)Math.Min (CopyBufferSize, left);
Marshal.Copy (ptr, buffer, 0, copyCount);
left -= copyCount;
ptr += copyCount;
target.Write (buffer, 0, copyCount);
}
} finally {
ArrayPool<byte>.Shared.Return (buffer);
}
}
//
private class SKDataStream : UnmanagedMemoryStream
{
private SKData host;

Просмотреть файл

@ -8,9 +8,6 @@ namespace SkiaSharp
{
public const float DefaultRasterDpi = 72.0f;
// keep the stream alive for as long as the document exists
private SKWStream underlyingStream;
[Preserve]
internal SKDocument (IntPtr handle, bool owns)
: base (handle, owns)
@ -111,7 +108,7 @@ namespace SkiaSharp
throw new ArgumentNullException (nameof (stream));
}
return GetObject<SKDocument> (SkiaApi.sk_document_create_pdf_from_stream (stream.Handle));
return Referenced (GetObject<SKDocument> (SkiaApi.sk_document_create_pdf_from_stream (stream.Handle)), stream);
}
public static SKDocument CreatePdf (string path, float dpi) =>
@ -167,37 +164,19 @@ namespace SkiaSharp
fEncodingQuality = metadata.EncodingQuality,
};
SKTimeDateTimeInternal creation;
if (metadata.Creation != null) {
var creation = SKTimeDateTimeInternal.Create (metadata.Creation.Value);
creation = SKTimeDateTimeInternal.Create (metadata.Creation.Value);
cmetadata.fCreation = &creation;
}
SKTimeDateTimeInternal modified;
if (metadata.Modified != null) {
var modified = SKTimeDateTimeInternal.Create (metadata.Modified.Value);
modified = SKTimeDateTimeInternal.Create (metadata.Modified.Value);
cmetadata.fModified = &modified;
}
return Referenced (GetObject<SKDocument> (SkiaApi.sk_document_create_pdf_from_stream_with_metadata (stream.Handle, &cmetadata)), stream);
}
}
private static SKDocument Owned (SKDocument doc, SKWStream stream)
{
if (stream != null) {
if (doc != null)
doc.SetDisposeChild (stream);
else
stream.Dispose ();
}
return doc;
}
private static SKDocument Referenced (SKDocument doc, SKWStream stream)
{
if (stream != null && doc != null)
doc.underlyingStream = stream;
return doc;
}
}
}

Просмотреть файл

@ -85,7 +85,7 @@ namespace SkiaSharp
if (path == null)
throw new ArgumentNullException (nameof (path));
var utf8path = StringUtilities.GetEncodedText (path, SKEncoding.Utf8);
var utf8path = StringUtilities.GetEncodedText (path, SKTextEncoding.Utf8);
fixed (byte* u = utf8path) {
return GetObject<SKTypeface> (SkiaApi.sk_fontmgr_create_from_file (Handle, u, index));
}

Просмотреть файл

@ -43,7 +43,7 @@ namespace SkiaSharp
if (pixels == null)
throw new ArgumentNullException (nameof (pixels));
using (var data = SKData.Create (pixels)) {
return FromPixelData (info, data, rowBytes);
return FromPixels (info, data, rowBytes);
}
}
@ -55,7 +55,7 @@ namespace SkiaSharp
if (pixels == null)
throw new ArgumentNullException (nameof (pixels));
using (var data = SKData.Create (pixels)) {
return FromPixelData (info, data, rowBytes);
return FromPixels (info, data, rowBytes);
}
}
@ -67,7 +67,7 @@ namespace SkiaSharp
if (pixels == null)
throw new ArgumentNullException (nameof (pixels));
using (var data = SKData.CreateCopy (pixels)) {
return FromPixelData (info, data, rowBytes);
return FromPixels (info, data, rowBytes);
}
}
@ -103,12 +103,14 @@ namespace SkiaSharp
if (pixels == null)
throw new ArgumentNullException (nameof (pixels));
using (var data = SKData.CreateCopy (pixels)) {
return FromPixelData (info, data, rowBytes);
return FromPixels (info, data, rowBytes);
}
}
// create a new image around existing pixel data
[EditorBrowsable (EditorBrowsableState.Never)]
[Obsolete ("Use FromPixels (SKImageInfo, SKData, int) instead.")]
public static SKImage FromPixelData (SKImageInfo info, SKData data, int rowBytes)
{
if (data == null)
@ -117,6 +119,9 @@ namespace SkiaSharp
return GetObject<SKImage> (SkiaApi.sk_image_new_raster_data (&cinfo, data.Handle, (IntPtr)rowBytes));
}
public static SKImage FromPixels (SKImageInfo info, SKData data) =>
FromPixels (info, data, info.RowBytes);
public static SKImage FromPixels (SKImageInfo info, SKData data, int rowBytes)
{
if (data == null)

Просмотреть файл

@ -19,11 +19,15 @@ namespace SkiaSharp
protected override void Dispose (bool disposing) =>
base.Dispose (disposing);
// CreateMatrix
public static SKImageFilter CreateMatrix(SKMatrix matrix, SKFilterQuality quality, SKImageFilter input = null)
{
return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_matrix(&matrix, quality, input == null ? IntPtr.Zero : input.Handle));
}
// CreateAlphaThreshold
public static SKImageFilter CreateAlphaThreshold(SKRectI region, float innerThreshold, float outerThreshold, SKImageFilter input = null)
{
var reg = new SKRegion ();
@ -39,11 +43,15 @@ namespace SkiaSharp
return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_alpha_threshold(region.Handle, innerThreshold, outerThreshold, input == null ? IntPtr.Zero : input.Handle));
}
// CreateBlur
public static SKImageFilter CreateBlur(float sigmaX, float sigmaY, SKImageFilter input = null, SKImageFilter.CropRect cropRect = null)
{
return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_blur(sigmaX, sigmaY, input == null ? IntPtr.Zero : input.Handle, cropRect == null ? IntPtr.Zero : cropRect.Handle));
}
// CreateColorFilter
public static SKImageFilter CreateColorFilter(SKColorFilter cf, SKImageFilter input = null, SKImageFilter.CropRect cropRect = null)
{
if (cf == null)
@ -51,6 +59,8 @@ namespace SkiaSharp
return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_color_filter(cf.Handle, input == null ? IntPtr.Zero : input.Handle, cropRect == null ? IntPtr.Zero : cropRect.Handle));
}
// CreateCompose
public static SKImageFilter CreateCompose(SKImageFilter outer, SKImageFilter inner)
{
if (outer == null)
@ -60,6 +70,8 @@ namespace SkiaSharp
return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_compose(outer.Handle, inner.Handle));
}
// CreateDisplacementMapEffect
public static SKImageFilter CreateDisplacementMapEffect(SKDisplacementMapEffectChannelSelectorType xChannelSelector, SKDisplacementMapEffectChannelSelectorType yChannelSelector, float scale, SKImageFilter displacement, SKImageFilter input = null, SKImageFilter.CropRect cropRect = null)
{
if (displacement == null)
@ -67,11 +79,15 @@ namespace SkiaSharp
return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_displacement_map_effect(xChannelSelector, yChannelSelector, scale, displacement.Handle, input == null ? IntPtr.Zero : input.Handle, cropRect == null ? IntPtr.Zero : cropRect.Handle));
}
// CreateDropShadow
public static SKImageFilter CreateDropShadow(float dx, float dy, float sigmaX, float sigmaY, SKColor color, SKDropShadowImageFilterShadowMode shadowMode, SKImageFilter input = null, SKImageFilter.CropRect cropRect = null)
{
return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_drop_shadow(dx, dy, sigmaX, sigmaY, (uint)color, shadowMode, input == null ? IntPtr.Zero : input.Handle, cropRect == null ? IntPtr.Zero : cropRect.Handle));
}
// Create*LitDiffuse
public static SKImageFilter CreateDistantLitDiffuse(SKPoint3 direction, SKColor lightColor, float surfaceScale, float kd, SKImageFilter input = null, SKImageFilter.CropRect cropRect = null)
{
return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_distant_lit_diffuse(&direction, (uint)lightColor, surfaceScale, kd, input == null ? IntPtr.Zero : input.Handle, cropRect == null ? IntPtr.Zero : cropRect.Handle));
@ -87,6 +103,8 @@ namespace SkiaSharp
return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_spot_lit_diffuse(&location, &target, specularExponent, cutoffAngle, (uint)lightColor, surfaceScale, kd, input == null ? IntPtr.Zero : input.Handle, cropRect == null ? IntPtr.Zero : cropRect.Handle));
}
// Create*LitSpecular
public static SKImageFilter CreateDistantLitSpecular(SKPoint3 direction, SKColor lightColor, float surfaceScale, float ks, float shininess, SKImageFilter input = null, SKImageFilter.CropRect cropRect = null)
{
return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_distant_lit_specular(&direction, (uint)lightColor, surfaceScale, ks, shininess, input == null ? IntPtr.Zero : input.Handle, cropRect == null ? IntPtr.Zero : cropRect.Handle));
@ -102,11 +120,15 @@ namespace SkiaSharp
return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_spot_lit_specular(&location, &target, specularExponent, cutoffAngle, (uint)lightColor, surfaceScale, ks, shininess, input == null ? IntPtr.Zero : input.Handle, cropRect == null ? IntPtr.Zero : cropRect.Handle));
}
// CreateMagnifier
public static SKImageFilter CreateMagnifier(SKRect src, float inset, SKImageFilter input = null, SKImageFilter.CropRect cropRect = null)
{
return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_magnifier(&src, inset, input == null ? IntPtr.Zero : input.Handle, cropRect == null ? IntPtr.Zero : cropRect.Handle));
}
// CreateMatrixConvolution
public static SKImageFilter CreateMatrixConvolution(SKSizeI kernelSize, float[] kernel, float gain, float bias, SKPointI kernelOffset, SKMatrixConvolutionTileMode tileMode, bool convolveAlpha, SKImageFilter input = null, SKImageFilter.CropRect cropRect = null)
{
if (kernel == null)
@ -118,11 +140,13 @@ namespace SkiaSharp
}
}
// CreateMerge
[EditorBrowsable (EditorBrowsableState.Never)]
[Obsolete("Use CreateMerge(SKImageFilter, SKImageFilter, SKImageFilter.CropRect) instead.")]
public static SKImageFilter CreateMerge(SKImageFilter first, SKImageFilter second, SKBlendMode mode, SKImageFilter.CropRect cropRect = null)
{
return CreateMerge(new [] { first, second }, new [] { mode, mode }, cropRect);
return CreateMerge(new [] { first, second }, cropRect);
}
public static SKImageFilter CreateMerge(SKImageFilter first, SKImageFilter second, SKImageFilter.CropRect cropRect = null)
@ -151,21 +175,29 @@ namespace SkiaSharp
}
}
// CreateDilate
public static SKImageFilter CreateDilate(int radiusX, int radiusY, SKImageFilter input = null, SKImageFilter.CropRect cropRect = null)
{
return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_dilate(radiusX, radiusY, input == null ? IntPtr.Zero : input.Handle, cropRect == null ? IntPtr.Zero : cropRect.Handle));
}
// CreateErode
public static SKImageFilter CreateErode(int radiusX, int radiusY, SKImageFilter input = null, SKImageFilter.CropRect cropRect = null)
{
return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_erode(radiusX, radiusY, input == null ? IntPtr.Zero : input.Handle, cropRect == null ? IntPtr.Zero : cropRect.Handle));
}
// CreateOffset
public static SKImageFilter CreateOffset(float dx, float dy, SKImageFilter input = null, SKImageFilter.CropRect cropRect = null)
{
return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_offset(dx, dy, input == null ? IntPtr.Zero : input.Handle, cropRect == null ? IntPtr.Zero : cropRect.Handle));
}
// CreatePicture
public static SKImageFilter CreatePicture(SKPicture picture)
{
if (picture == null)
@ -180,6 +212,8 @@ namespace SkiaSharp
return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_picture_with_croprect(picture.Handle, &cropRect));
}
// CreateTile
public static SKImageFilter CreateTile(SKRect src, SKRect dst, SKImageFilter input)
{
if (input == null)
@ -187,6 +221,8 @@ namespace SkiaSharp
return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_tile(&src, &dst, input.Handle));
}
// CreateBlendMode
public static SKImageFilter CreateBlendMode(SKBlendMode mode, SKImageFilter background, SKImageFilter foreground = null, SKImageFilter.CropRect cropRect = null)
{
if (background == null)
@ -194,6 +230,8 @@ namespace SkiaSharp
return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_xfermode(mode, background.Handle, foreground == null ? IntPtr.Zero : foreground.Handle, cropRect == null ? IntPtr.Zero : cropRect.Handle));
}
// CreateArithmetic
public static SKImageFilter CreateArithmetic(float k1, float k2, float k3, float k4, bool enforcePMColor, SKImageFilter background, SKImageFilter foreground = null, SKImageFilter.CropRect cropRect = null)
{
if (background == null)
@ -201,6 +239,8 @@ namespace SkiaSharp
return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_arithmetic(k1, k2, k3, k4, enforcePMColor, background.Handle, foreground == null ? IntPtr.Zero : foreground.Handle, cropRect == null ? IntPtr.Zero : cropRect.Handle));
}
// CreateImage
public static SKImageFilter CreateImage(SKImage image)
{
if (image == null)
@ -215,6 +255,8 @@ namespace SkiaSharp
return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_image_source(image.Handle, &src, &dst, filterQuality));
}
// CreatePaint
public static SKImageFilter CreatePaint(SKPaint paint, SKImageFilter.CropRect cropRect = null)
{
if (paint == null)
@ -222,6 +264,8 @@ namespace SkiaSharp
return GetObject<SKImageFilter>(SkiaApi.sk_imagefilter_new_paint(paint.Handle, cropRect == null ? IntPtr.Zero : cropRect.Handle));
}
//
public class CropRect : SKObject
{
internal CropRect(IntPtr handle, bool owns)

Просмотреть файл

@ -100,21 +100,7 @@ namespace SkiaSharp
}
public readonly int BytesPerPixel =>
ColorType switch
{
SKColorType.Unknown => 0,
SKColorType.Alpha8 => 1,
SKColorType.Gray8 => 1,
SKColorType.Rgb565 => 2,
SKColorType.Argb4444 => 2,
SKColorType.Bgra8888 => 4,
SKColorType.Rgba8888 => 4,
SKColorType.Rgb888x => 4,
SKColorType.Rgba1010102 => 4,
SKColorType.Rgb101010x => 4,
SKColorType.RgbaF16 => 8,
_ => throw new ArgumentOutOfRangeException (nameof (ColorType)),
};
ColorType.GetBytesPerPixel ();
public readonly int BitsPerPixel => BytesPerPixel * 8;
@ -134,6 +120,9 @@ namespace SkiaSharp
public readonly SKRectI Rect => SKRectI.Create (Width, Height);
public readonly SKImageInfo WithSize (SKSizeI size) =>
WithSize (size.Width, size.Height);
public readonly SKImageInfo WithSize (int width, int height)
{
var copy = this;

Просмотреть файл

@ -1,4 +1,5 @@
using System;
using System.Buffers;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
@ -33,12 +34,16 @@ namespace SkiaSharp
if (destination == null)
throw new ArgumentNullException (nameof (destination));
var buffer = new byte[SKData.CopyBufferSize];
var total = 0;
int len;
while ((len = stream.Read (buffer, 0, buffer.Length)) > 0) {
destination.Write (buffer, len);
total += len;
var buffer = ArrayPool<byte>.Shared.Rent (SKData.CopyBufferSize);
try {
while ((len = stream.Read (buffer, 0, buffer.Length)) > 0) {
destination.Write (buffer, len);
total += len;
}
} finally {
ArrayPool<byte>.Shared.Return (buffer);
}
destination.Flush ();
return total;

Просмотреть файл

@ -1,6 +1,7 @@
using System;
using System.Runtime.InteropServices;
using System.Buffers;
using System.IO;
using System.Runtime.InteropServices;
namespace SkiaSharp
{
@ -42,11 +43,15 @@ namespace SkiaSharp
protected override bool OnWrite (IntPtr buffer, IntPtr size)
{
var count = (int)size;
var managedBuffer = new byte[count];
if (buffer != IntPtr.Zero) {
Marshal.Copy (buffer, managedBuffer, 0, count);
var managedBuffer = ArrayPool<byte>.Shared.Rent (count);
try {
if (buffer != IntPtr.Zero) {
Marshal.Copy (buffer, managedBuffer, 0, count);
}
stream.Write (managedBuffer, 0, count);
} finally {
ArrayPool<byte>.Shared.Return (managedBuffer);
}
stream.Write (managedBuffer, 0, count);
return true;
}
@ -55,7 +60,7 @@ namespace SkiaSharp
stream.Flush ();
}
protected override IntPtr OnBytesWritten()
protected override IntPtr OnBytesWritten ()
{
return (IntPtr)stream.Position;
}

Просмотреть файл

@ -28,6 +28,9 @@ namespace SkiaSharp
set => fImage = (byte*)value;
}
public Span<byte> GetImageSpan () =>
new Span<byte> ((void*)Image, (int)ComputeTotalImageSize ());
public SKRectI Bounds {
readonly get => fBounds;
set => fBounds = value;
@ -131,22 +134,28 @@ namespace SkiaSharp
public static void FreeImage (IntPtr image) =>
SkiaApi.sk_mask_free_image ((byte*)image);
public static SKMask Create (byte[] image, SKRectI bounds, UInt32 rowBytes, SKMaskFormat format)
public static SKMask Create (byte[] image, SKRectI bounds, UInt32 rowBytes, SKMaskFormat format) =>
Create (image.AsSpan (), bounds, rowBytes, format);
public static SKMask Create (ReadOnlySpan<byte> image, SKRectI bounds, UInt32 rowBytes, SKMaskFormat format)
{
// create the mask
var mask = new SKMask (bounds, rowBytes, format);
// calculate the size
var imageSize = (int)mask.ComputeTotalImageSize ();
// is there the right amount of space in the mask
if (image.Length != mask.ComputeTotalImageSize ()) {
if (image.Length != imageSize) {
// Note: buffer.Length must match bounds.Height * rowBytes
var expectedHeight = bounds.Height * rowBytes;
var message = $"Length of image ({image.Length}) does not match the computed size of the mask ({expectedHeight}). Check the {nameof (bounds)} and {nameof (rowBytes)}.";
var expectedSize = bounds.Height * rowBytes;
var message = $"Length of image ({image.Length}) does not match the computed size of the mask ({expectedSize}). Check the {nameof (bounds)} and {nameof (rowBytes)}.";
throw new ArgumentException (message);
}
// copy the image data
// allocate and copy the image data
mask.AllocateImage ();
Marshal.Copy (image, 0, (IntPtr)mask.fImage, image.Length);
image.CopyTo (new Span<byte> ((void*)mask.Image, imageSize));
// return the mask
return mask;

Просмотреть файл

@ -26,6 +26,26 @@ namespace SkiaSharp
public const int Count = 9;
}
public SKMatrix (float[] values)
{
if (values == null)
throw new ArgumentNullException (nameof (values));
if (values.Length != Indices.Count)
throw new ArgumentException ($"The matrix array must have a length of {Indices.Count}.", nameof (values));
scaleX = values[Indices.ScaleX];
skewX = values[Indices.SkewX];
transX = values[Indices.TransX];
skewY = values[Indices.SkewY];
scaleY = values[Indices.ScaleY];
transY = values[Indices.TransY];
persp0 = values[Indices.Persp0];
persp1 = values[Indices.Persp1];
persp2 = values[Indices.Persp2];
}
public SKMatrix (
float scaleX, float skewX, float transX,
float skewY, float scaleY, float transY,
@ -42,6 +62,8 @@ namespace SkiaSharp
this.persp2 = persp2;
}
public readonly bool IsIdentity => Equals (Identity);
// Values
public float[] Values {
@ -457,6 +479,9 @@ namespace SkiaSharp
// MapVectors
public readonly SKPoint MapVector (SKPoint vector) =>
MapVector (vector.X, vector.Y);
public readonly SKPoint MapVector (float x, float y)
{
SKPoint result;

Просмотреть файл

@ -51,19 +51,12 @@ namespace SkiaSharp
}
public SKMatrix44 (SKMatrix src)
: this (CreateNew (ref src), true)
: this (SkiaApi.sk_matrix44_new_matrix (&src), true)
{
if (Handle == IntPtr.Zero)
throw new InvalidOperationException ("Unable to create a new SKMatrix44 instance.");
}
private static IntPtr CreateNew (ref SKMatrix src)
{
fixed (SKMatrix* s = &src) {
return SkiaApi.sk_matrix44_new_matrix (s);
}
}
// properties
public SKMatrix Matrix {
@ -220,6 +213,25 @@ namespace SkiaSharp
}
}
public void Set3x3ColumnMajor (float[] src)
{
if (src.Length != 9)
throw new ArgumentException ("The source array must be 9 entries.", nameof (src));
var row = stackalloc float[9] { src[0], src[3], src[6], src[1], src[4], src[7], src[2], src[5], src[8] };
SkiaApi.sk_matrix44_set_3x3_row_major (Handle, row);
}
public void Set3x3RowMajor (float[] src)
{
if (src.Length != 9)
throw new ArgumentException ("The source array must be 9 entries.", nameof (src));
fixed (float* s = src) {
SkiaApi.sk_matrix44_set_3x3_row_major (Handle, s);
}
}
public void SetTranslate (float dx, float dy, float dz) =>
SkiaApi.sk_matrix44_set_translate (Handle, dx, dy, dz);

Просмотреть файл

@ -1,6 +1,5 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
@ -18,6 +17,7 @@ namespace SkiaSharp
internal static readonly ConcurrentDictionary<IntPtr, WeakReference> instances;
internal readonly ConcurrentDictionary<IntPtr, SKObject> ownedObjects = new ConcurrentDictionary<IntPtr, SKObject> ();
internal readonly ConcurrentDictionary<IntPtr, SKObject> keepAliveObjects = new ConcurrentDictionary<IntPtr, SKObject> ();
static SKObject ()
{
@ -58,6 +58,7 @@ namespace SkiaSharp
child.Value.DisposeInternal ();
}
ownedObjects.Clear ();
keepAliveObjects.Clear ();
}
protected override void DisposeNative ()
@ -199,15 +200,6 @@ namespace SkiaSharp
return false;
}
// indicate that when this object is disposed on the managed side,
// dispose the child as well
internal void SetDisposeChild (SKObject child)
{
if (child == null)
return;
ownedObjects[child.Handle] = child;
}
// indicate that the ownership of this object is now in the hands of
// the native object
internal void RevokeOwnership (SKObject newOwner)
@ -218,19 +210,45 @@ namespace SkiaSharp
if (newOwner == null)
DisposeInternal ();
else
newOwner.SetDisposeChild (this);
newOwner.ownedObjects[Handle] = this;
}
// indicate that the child was created by the managed code and
// should be disposed when the owner is
internal static T Owned<T> (T owner, SKObject child)
where T : SKObject
{
if (child != null) {
if (owner != null)
owner.ownedObjects[child.Handle] = child;
else
child.Dispose ();
}
return owner;
}
// indicate that the chile should not be garbage collected while
// the owner still lives
internal static T Referenced<T> (T owner, SKObject child)
where T : SKObject
{
if (child != null && owner != null)
owner.keepAliveObjects[child.Handle] = child;
return owner;
}
internal static int SizeOf<T> () =>
#if WINDOWS_UWP || NET_STANDARD
Marshal.SizeOf <T> ();
Marshal.SizeOf<T> ();
#else
Marshal.SizeOf (typeof (T));
#endif
internal static T PtrToStructure<T> (IntPtr intPtr) =>
#if WINDOWS_UWP || NET_STANDARD
Marshal.PtrToStructure <T> (intPtr);
Marshal.PtrToStructure<T> (intPtr);
#else
(T)Marshal.PtrToStructure (intPtr, typeof (T));
#endif

Просмотреть файл

@ -58,8 +58,8 @@ namespace SkiaSharp
public readonly bool Equals (SKPMColor obj) =>
obj.color == color;
public readonly override bool Equals (object obj) =>
obj is SKPMColor f && Equals (f);
public readonly override bool Equals (object other) =>
other is SKPMColor f && Equals (f);
public static bool operator == (SKPMColor left, SKPMColor right) =>
left.Equals (right);

Просмотреть файл

@ -24,11 +24,15 @@ namespace SkiaSharp
protected override void DisposeNative () =>
SkiaApi.sk_paint_delete (Handle);
// Reset
public void Reset ()
{
SkiaApi.sk_paint_reset (Handle);
}
// properties
public bool IsAntialias {
get => SkiaApi.sk_paint_is_antialias (Handle);
set => SkiaApi.sk_paint_set_antialias (Handle, value);
@ -184,9 +188,13 @@ namespace SkiaSharp
set => SkiaApi.sk_paint_set_path_effect (Handle, value == null ? IntPtr.Zero : value.Handle);
}
// FontSpacing
public float FontSpacing =>
SkiaApi.sk_paint_get_fontmetrics (Handle, null, 0);
// FontMetrics
public SKFontMetrics FontMetrics {
get {
GetFontMetrics (out var metrics);
@ -201,6 +209,8 @@ namespace SkiaSharp
}
}
// Clone
public SKPaint Clone () =>
GetObject<SKPaint> (SkiaApi.sk_paint_clone (Handle));

Просмотреть файл

@ -323,6 +323,14 @@ namespace SkiaSharp
public void Transform (SKMatrix matrix) =>
SkiaApi.sk_path_transform (Handle, &matrix);
public void Transform (SKMatrix matrix, SKPath destination)
{
if (destination == null)
throw new ArgumentNullException (nameof (destination));
SkiaApi.sk_path_transform_to_dest (Handle, &matrix, destination.Handle);
}
public void AddPath (SKPath other, float dx, float dy, SKPathAddMode mode = SKPathAddMode.Append)
{
if (other == null)
@ -566,4 +574,3 @@ namespace SkiaSharp
}
}
}

Просмотреть файл

@ -32,6 +32,8 @@ namespace SkiaSharp
protected override void DisposeNative () =>
SkiaApi.sk_pathmeasure_destroy (Handle);
// properties
public float Length {
get {
return SkiaApi.sk_pathmeasure_get_length (Handle);
@ -44,11 +46,18 @@ namespace SkiaSharp
}
}
// SetPath
public void SetPath (SKPath path) =>
SetPath (path, false);
public void SetPath (SKPath path, bool forceClosed)
{
SkiaApi.sk_pathmeasure_set_path (Handle, path == null ? IntPtr.Zero : path.Handle, forceClosed);
}
// GetPositionAndTangent
public bool GetPositionAndTangent (float distance, out SKPoint position, out SKPoint tangent)
{
fixed (SKPoint* p = &position)
@ -57,6 +66,15 @@ namespace SkiaSharp
}
}
// GetPosition
public SKPoint GetPosition (float distance)
{
if (!GetPosition (distance, out var position))
position = SKPoint.Empty;
return position;
}
public bool GetPosition (float distance, out SKPoint position)
{
fixed (SKPoint* p = &position) {
@ -64,6 +82,15 @@ namespace SkiaSharp
}
}
// GetTangent
public SKPoint GetTangent (float distance)
{
if (!GetTangent (distance, out var tangent))
tangent = SKPoint.Empty;
return tangent;
}
public bool GetTangent (float distance, out SKPoint tangent)
{
fixed (SKPoint* t = &tangent) {
@ -71,6 +98,15 @@ namespace SkiaSharp
}
}
// GetMatrix
public SKMatrix GetMatrix (float distance, SKPathMeasureMatrixFlags flags)
{
if (!GetMatrix (distance, out var matrix, flags))
matrix = SKMatrix.Empty;
return matrix;
}
public bool GetMatrix (float distance, out SKMatrix matrix, SKPathMeasureMatrixFlags flags)
{
fixed (SKMatrix* m = &matrix) {
@ -78,6 +114,8 @@ namespace SkiaSharp
}
}
// GetSegment
public bool GetSegment (float start, float stop, SKPath dst, bool startWithMoveTo)
{
if (dst == null)
@ -85,10 +123,21 @@ namespace SkiaSharp
return SkiaApi.sk_pathmeasure_get_segment (Handle, start, stop, dst.Handle, startWithMoveTo);
}
public SKPath GetSegment (float start, float stop, bool startWithMoveTo)
{
var dst = new SKPath ();
if (!GetSegment (start, stop, dst, startWithMoveTo)) {
dst.Dispose ();
dst = null;
}
return dst;
}
// NextContour
public bool NextContour ()
{
return SkiaApi.sk_pathmeasure_next_contour (Handle);
}
}
}

Просмотреть файл

@ -1,5 +1,6 @@
using System;
using System.ComponentModel;
using System.IO;
namespace SkiaSharp
{
@ -49,6 +50,8 @@ namespace SkiaSharp
protected override void DisposeNative () =>
SkiaApi.sk_pixmap_destructor (Handle);
// Reset
public void Reset ()
{
SkiaApi.sk_pixmap_reset (Handle);
@ -67,6 +70,8 @@ namespace SkiaSharp
SkiaApi.sk_pixmap_reset_with_params (Handle, &cinfo, (void*)addr, (IntPtr)rowBytes);
}
// properties
public SKImageInfo Info {
get {
SKImageInfoNative cinfo;
@ -95,6 +100,8 @@ namespace SkiaSharp
public int BytesSize => Info.BytesSize;
// pixels
public IntPtr GetPixels () =>
(IntPtr)SkiaApi.sk_pixmap_get_pixels (Handle);
@ -111,10 +118,14 @@ namespace SkiaSharp
return SkiaApi.sk_pixmap_get_pixel_color (Handle, x, y);
}
// ColorTable
[EditorBrowsable (EditorBrowsableState.Never)]
[Obsolete ("The Index8 color type and color table is no longer supported.")]
public SKColorTable ColorTable => null;
// Resize
[EditorBrowsable (EditorBrowsableState.Never)]
[Obsolete ("Use ScalePixels(SKPixmap, SKFilterQuality) instead.")]
public static bool Resize (SKPixmap dst, SKPixmap src, SKBitmapResizeMethod method)
@ -127,6 +138,8 @@ namespace SkiaSharp
return src.ScalePixels (dst, method.ToFilterQuality ());
}
// ScalePixels
public bool ScalePixels (SKPixmap destination, SKFilterQuality quality)
{
if (destination == null)
@ -135,6 +148,8 @@ namespace SkiaSharp
return SkiaApi.sk_pixmap_scale_pixels (Handle, destination.Handle, quality);
}
// ReadPixels
public bool ReadPixels (SKImageInfo dstInfo, IntPtr dstPixels, int dstRowBytes, int srcX, int srcY, SKTransferFunctionBehavior behavior)
{
var cinfo = SKImageInfoNative.FromManaged (ref dstInfo);
@ -161,19 +176,34 @@ namespace SkiaSharp
return ReadPixels (pixmap.Info, pixmap.GetPixels (), pixmap.RowBytes, 0, 0, SKTransferFunctionBehavior.Respect);
}
// Encode
public SKData Encode (SKEncodedImageFormat encoder, int quality)
{
using (var stream = new SKDynamicMemoryWStream ()) {
var result = Encode (stream, this, encoder, quality);
return result ? stream.DetachAsData () : null;
}
using var stream = new SKDynamicMemoryWStream ();
var result = Encode (stream, encoder, quality);
return result ? stream.DetachAsData () : null;
}
public bool Encode (Stream dst, SKEncodedImageFormat encoder, int quality)
{
if (dst == null)
throw new ArgumentNullException (nameof (dst));
using var wrapped = new SKManagedWStream (dst);
return Encode (wrapped, encoder, quality);
}
public bool Encode (SKWStream dst, SKEncodedImageFormat encoder, int quality)
{
return Encode (dst, this, encoder, quality);
if (dst == null)
throw new ArgumentNullException (nameof (dst));
return SkiaApi.sk_pixmap_encode_image (dst.Handle, Handle, encoder, quality);
}
[EditorBrowsable (EditorBrowsableState.Never)]
[Obsolete ("Use Encode(SKWStream, SKEncodedImageFormat, int) instead.")]
public static bool Encode (SKWStream dst, SKBitmap src, SKEncodedImageFormat format, int quality)
{
if (dst == null)
@ -181,11 +211,11 @@ namespace SkiaSharp
if (src == null)
throw new ArgumentNullException (nameof (src));
using (var pixmap = new SKPixmap ()) {
return src.PeekPixels (pixmap) && Encode (dst, pixmap, format, quality);
}
return src.Encode (dst, format, quality);
}
[EditorBrowsable (EditorBrowsableState.Never)]
[Obsolete ("Use Encode(SKWStream, SKEncodedImageFormat, int) instead.")]
public static bool Encode (SKWStream dst, SKPixmap src, SKEncodedImageFormat encoder, int quality)
{
if (dst == null)
@ -193,22 +223,37 @@ namespace SkiaSharp
if (src == null)
throw new ArgumentNullException (nameof (src));
return SkiaApi.sk_pixmap_encode_image (dst.Handle, src.Handle, encoder, quality);
return src.Encode (dst, encoder, quality);
}
// Encode (webp)
public SKData Encode (SKWebpEncoderOptions options)
{
using (var stream = new SKDynamicMemoryWStream ()) {
var result = Encode (stream, this, options);
return result ? stream.DetachAsData () : null;
}
using var stream = new SKDynamicMemoryWStream ();
var result = Encode (stream, options);
return result ? stream.DetachAsData () : null;
}
public bool Encode (Stream dst, SKWebpEncoderOptions options)
{
if (dst == null)
throw new ArgumentNullException (nameof (dst));
using var wrapped = new SKManagedWStream (dst);
return Encode (wrapped, options);
}
public bool Encode (SKWStream dst, SKWebpEncoderOptions options)
{
return Encode (dst, this, options);
if (dst == null)
throw new ArgumentNullException (nameof (dst));
return SkiaApi.sk_webpencoder_encode (dst.Handle, Handle, &options);
}
[EditorBrowsable (EditorBrowsableState.Never)]
[Obsolete ("Use Encode(SKWStream, SKWebpEncoderOptions) instead.")]
public static bool Encode (SKWStream dst, SKPixmap src, SKWebpEncoderOptions options)
{
if (dst == null)
@ -216,22 +261,37 @@ namespace SkiaSharp
if (src == null)
throw new ArgumentNullException (nameof (src));
return SkiaApi.sk_webpencoder_encode (dst.Handle, src.Handle, options);
return src.Encode (dst, options);
}
// Encode (jpeg)
public SKData Encode (SKJpegEncoderOptions options)
{
using (var stream = new SKDynamicMemoryWStream ()) {
var result = Encode (stream, this, options);
return result ? stream.DetachAsData () : null;
}
using var stream = new SKDynamicMemoryWStream ();
var result = Encode (stream, options);
return result ? stream.DetachAsData () : null;
}
public bool Encode (Stream dst, SKJpegEncoderOptions options)
{
if (dst == null)
throw new ArgumentNullException (nameof (dst));
using var wrapped = new SKManagedWStream (dst);
return Encode (wrapped, options);
}
public bool Encode (SKWStream dst, SKJpegEncoderOptions options)
{
return Encode (dst, this, options);
if (dst == null)
throw new ArgumentNullException (nameof (dst));
return SkiaApi.sk_jpegencoder_encode (dst.Handle, Handle, &options);
}
[EditorBrowsable (EditorBrowsableState.Never)]
[Obsolete ("Use Encode(SKWStream, SKJpegEncoderOptions) instead.")]
public static bool Encode (SKWStream dst, SKPixmap src, SKJpegEncoderOptions options)
{
if (dst == null)
@ -239,22 +299,37 @@ namespace SkiaSharp
if (src == null)
throw new ArgumentNullException (nameof (src));
return SkiaApi.sk_jpegencoder_encode (dst.Handle, src.Handle, options);
return src.Encode (dst, options);
}
// Encode (png)
public SKData Encode (SKPngEncoderOptions options)
{
using (var stream = new SKDynamicMemoryWStream ()) {
var result = Encode (stream, this, options);
return result ? stream.DetachAsData () : null;
}
using var stream = new SKDynamicMemoryWStream ();
var result = Encode (stream, options);
return result ? stream.DetachAsData () : null;
}
public bool Encode (Stream dst, SKPngEncoderOptions options)
{
if (dst == null)
throw new ArgumentNullException (nameof (dst));
using var wrapped = new SKManagedWStream (dst);
return Encode (wrapped, options);
}
public bool Encode (SKWStream dst, SKPngEncoderOptions options)
{
return Encode (dst, this, options);
if (dst == null)
throw new ArgumentNullException (nameof (dst));
return SkiaApi.sk_pngencoder_encode (dst.Handle, Handle, &options);
}
[EditorBrowsable (EditorBrowsableState.Never)]
[Obsolete ("Use Encode(SKWStream, SKPngEncoderOptions) instead.")]
public static bool Encode (SKWStream dst, SKPixmap src, SKPngEncoderOptions options)
{
if (dst == null)
@ -262,9 +337,11 @@ namespace SkiaSharp
if (src == null)
throw new ArgumentNullException (nameof (src));
return SkiaApi.sk_pngencoder_encode (dst.Handle, src.Handle, options);
return src.Encode (dst, options);
}
// ExtractSubset
public SKPixmap ExtractSubset (SKRectI subset)
{
var result = new SKPixmap ();
@ -283,6 +360,8 @@ namespace SkiaSharp
return SkiaApi.sk_pixmap_extract_subset (Handle, result.Handle, &subset);
}
// Erase
public bool Erase (SKColor color)
{
return Erase (color, Rect);
@ -299,6 +378,8 @@ namespace SkiaSharp
public bool Erase (SKColorF color, SKRectI subset) =>
SkiaApi.sk_pixmap_erase_color4f (Handle, &color, &subset);
// With*
public SKPixmap WithColorType (SKColorType newColorType)
{
return new SKPixmap (Info.WithColorType (newColorType), GetPixels (), RowBytes);

Просмотреть файл

@ -1,4 +1,6 @@
using System;
using System.ComponentModel;
using System.IO;
namespace SkiaSharp
{
@ -7,14 +9,38 @@ namespace SkiaSharp
private SKSvgCanvas ()
{
}
// Create
public static SKCanvas Create (SKRect bounds, Stream stream)
{
if (stream == null)
throw new ArgumentNullException (nameof (stream));
var managed = new SKManagedWStream (stream);
return SKObject.Owned (Create (bounds, managed), managed);
}
public static SKCanvas Create (SKRect bounds, SKWStream stream)
{
if (stream == null)
throw new ArgumentNullException (nameof (stream));
// TODO: there seems to be a memory issue with things getting destroyed in the incorrect order
//return SKObject.Referenced (SKObject.GetObject<SKCanvas> (SkiaApi.sk_svgcanvas_create_with_stream (&bounds, stream.Handle)), stream);
var writer = new SKXmlStreamWriter (stream);
return SKObject.Owned (SKObject.GetObject<SKCanvas> (SkiaApi.sk_svgcanvas_create_with_writer (&bounds, writer.Handle)), writer);
}
[EditorBrowsable (EditorBrowsableState.Never)]
[Obsolete ("Use Create(SKRect, Stream) instead.")]
public static SKCanvas Create (SKRect bounds, SKXmlWriter writer)
{
if (writer == null) {
if (writer == null)
throw new ArgumentNullException (nameof (writer));
}
return SKObject.GetObject<SKCanvas> (SkiaApi.sk_svgcanvas_create (&bounds, writer.Handle));
return SKObject.Referenced (SKObject.GetObject<SKCanvas> (SkiaApi.sk_svgcanvas_create_with_writer (&bounds, writer.Handle)), writer);
}
}
}

Просмотреть файл

@ -273,7 +273,7 @@ namespace SkiaSharp
private static IntPtr CreateNew (string path)
{
var bytes = StringUtilities.GetEncodedText (path, SKEncoding.Utf8);
var bytes = StringUtilities.GetEncodedText (path, SKTextEncoding.Utf8);
fixed (byte* p = bytes) {
return SkiaApi.sk_filestream_new (p);
}
@ -488,7 +488,7 @@ namespace SkiaSharp
private static IntPtr CreateNew (string path)
{
var bytes = StringUtilities.GetEncodedText (path, SKEncoding.Utf8);
var bytes = StringUtilities.GetEncodedText (path, SKTextEncoding.Utf8);
fixed (byte* p = bytes) {
return SkiaApi.sk_filewstream_new (p);
}
@ -553,6 +553,17 @@ namespace SkiaSharp
SkiaApi.sk_dynamicmemorywstream_copy_to (Handle, (void*)data);
}
public void CopyTo (Span<byte> data)
{
var size = BytesWritten;
if (data.Length < size)
throw new Exception ($"Not enough space to copy. Expected at least {size}, but received {data.Length}.");
fixed (void* d = data) {
SkiaApi.sk_dynamicmemorywstream_copy_to (Handle, d);
}
}
public bool CopyTo (SKWStream dst)
{
if (dst == null)
@ -560,6 +571,15 @@ namespace SkiaSharp
return SkiaApi.sk_dynamicmemorywstream_write_to_stream (Handle, dst.Handle);
}
public bool CopyTo (Stream dst)
{
if (dst == null)
throw new ArgumentNullException (nameof (dst));
using var wrapped = new SKManagedWStream (dst);
return CopyTo (wrapped);
}
protected override void Dispose (bool disposing) =>
base.Dispose (disposing);

Просмотреть файл

@ -48,7 +48,7 @@ namespace SkiaSharp
{
var cstr = SkiaApi.sk_string_get_c_str (Handle);
var clen = SkiaApi.sk_string_get_size (Handle);
return StringUtilities.GetString (cstr, (int)clen, SKTextEncoding.Utf8);
return StringUtilities.GetString ((IntPtr)cstr, (int)clen, SKTextEncoding.Utf8);
}
public static explicit operator string (SKString skString)

Просмотреть файл

@ -19,6 +19,9 @@ namespace SkiaSharp
SkiaApi.sk_swizzle_swap_rb ((uint*)dest, (uint*)src, count);
}
public static void SwapRedBlue (Span<byte> pixels) =>
SwapRedBlue (pixels, pixels, pixels.Length);
public static void SwapRedBlue (ReadOnlySpan<byte> pixels, int count) =>
SwapRedBlue (pixels, pixels, count);

Просмотреть файл

@ -56,13 +56,13 @@ namespace SkiaSharp
public void AddRun (SKPaint font, float x, float y, ushort[] glyphs, string text, uint[] clusters)
{
var utf8Text = StringUtilities.GetEncodedText (text, SKEncoding.Utf8);
var utf8Text = StringUtilities.GetEncodedText (text, SKTextEncoding.Utf8);
AddRun (font, x, y, glyphs, utf8Text, clusters, null);
}
public void AddRun (SKPaint font, float x, float y, ushort[] glyphs, string text, uint[] clusters, SKRect bounds)
{
var utf8Text = StringUtilities.GetEncodedText (text, SKEncoding.Utf8);
var utf8Text = StringUtilities.GetEncodedText (text, SKTextEncoding.Utf8);
AddRun (font, x, y, glyphs, utf8Text, clusters, (SKRect?)bounds);
}
@ -116,13 +116,13 @@ namespace SkiaSharp
public void AddHorizontalRun (SKPaint font, float y, ushort[] glyphs, float[] positions, string text, uint[] clusters)
{
var utf8Text = StringUtilities.GetEncodedText (text, SKEncoding.Utf8);
var utf8Text = StringUtilities.GetEncodedText (text, SKTextEncoding.Utf8);
AddHorizontalRun (font, y, glyphs, positions, utf8Text, clusters, null);
}
public void AddHorizontalRun (SKPaint font, float y, ushort[] glyphs, float[] positions, string text, uint[] clusters, SKRect bounds)
{
var utf8Text = StringUtilities.GetEncodedText (text, SKEncoding.Utf8);
var utf8Text = StringUtilities.GetEncodedText (text, SKTextEncoding.Utf8);
AddHorizontalRun (font, y, glyphs, positions, utf8Text, clusters, (SKRect?)bounds);
}
@ -181,13 +181,13 @@ namespace SkiaSharp
public void AddPositionedRun (SKPaint font, ushort[] glyphs, SKPoint[] positions, string text, uint[] clusters)
{
var utf8Text = StringUtilities.GetEncodedText (text, SKEncoding.Utf8);
var utf8Text = StringUtilities.GetEncodedText (text, SKTextEncoding.Utf8);
AddPositionedRun (font, glyphs, positions, utf8Text, clusters, null);
}
public void AddPositionedRun (SKPaint font, ushort[] glyphs, SKPoint[] positions, string text, uint[] clusters, SKRect bounds)
{
var utf8Text = StringUtilities.GetEncodedText (text, SKEncoding.Utf8);
var utf8Text = StringUtilities.GetEncodedText (text, SKTextEncoding.Utf8);
AddPositionedRun (font, glyphs, positions, utf8Text, clusters, (SKRect?)bounds);
}

Просмотреть файл

@ -36,6 +36,8 @@ namespace SkiaSharp
{
}
// Default
protected override void Dispose (bool disposing) =>
base.Dispose (disposing);
@ -46,6 +48,8 @@ namespace SkiaSharp
return GetObject<SKTypeface> (SkiaApi.sk_typeface_create_default ());
}
// FromFamilyName
[EditorBrowsable (EditorBrowsableState.Never)]
[Obsolete ("Use FromFamilyName(string, SKFontStyleWeight, SKFontStyleWidth, SKFontStyleSlant) instead.")]
public static SKTypeface FromFamilyName (string familyName, SKTypefaceStyle style)
@ -79,6 +83,8 @@ namespace SkiaSharp
return FromFamilyName (familyName, (int)weight, (int)width, slant);
}
// From*
[EditorBrowsable (EditorBrowsableState.Never)]
[Obsolete]
public static SKTypeface FromTypeface (SKTypeface typeface, SKTypefaceStyle style)
@ -98,7 +104,7 @@ namespace SkiaSharp
if (path == null)
throw new ArgumentNullException (nameof (path));
var utf8path = StringUtilities.GetEncodedText (path, SKEncoding.Utf8);
var utf8path = StringUtilities.GetEncodedText (path, SKTextEncoding.Utf8);
fixed (byte* u = utf8path) {
return GetObject<SKTypeface> (SkiaApi.sk_typeface_create_from_file (u, index));
}
@ -129,18 +135,25 @@ namespace SkiaSharp
public static SKTypeface FromData (SKData data, int index = 0)
{
return SKTypeface.FromStream (new SKMemoryStream (data), index);
if (data == null)
throw new ArgumentNullException (nameof (data));
return FromStream (new SKMemoryStream (data), index);
}
// CharsToGlyphs
[EditorBrowsable (EditorBrowsableState.Never)]
[Obsolete ("Use GetGlyphs(string, out ushort[]) instead.")]
public int CharsToGlyphs (string chars, out ushort[] glyphs)
=> GetGlyphs (chars, out glyphs);
public int CharsToGlyphs (string chars, out ushort[] glyphs) =>
GetGlyphs (chars, out glyphs);
[EditorBrowsable (EditorBrowsableState.Never)]
[Obsolete ("Use GetGlyphs(IntPtr, int, SKEncoding, out ushort[]) instead.")]
public int CharsToGlyphs (IntPtr str, int strlen, SKEncoding encoding, out ushort[] glyphs)
=> GetGlyphs (str, strlen, encoding, out glyphs);
[Obsolete ("Use GetGlyphs(IntPtr, int, SKTextEncoding, out ushort[]) instead.")]
public int CharsToGlyphs (IntPtr str, int strlen, SKEncoding encoding, out ushort[] glyphs) =>
GetGlyphs (str, strlen, encoding, out glyphs);
// Properties
public string FamilyName => (string)GetObject<SKString> (SkiaApi.sk_typeface_get_family_name (Handle));
@ -173,6 +186,10 @@ namespace SkiaSharp
public int UnitsPerEm => SkiaApi.sk_typeface_get_units_per_em (Handle);
public int GlyphCount => SkiaApi.sk_typeface_count_glyphs (Handle);
// GetTableTags
public int TableCount => SkiaApi.sk_typeface_count_tables (Handle);
public UInt32[] GetTableTags ()
@ -196,9 +213,13 @@ namespace SkiaSharp
return true;
}
// GetTableSize
public int GetTableSize (UInt32 tag) =>
(int)SkiaApi.sk_typeface_get_table_size (Handle, tag);
// GetTableData
public byte[] GetTableData (UInt32 tag)
{
if (!TryGetTableData (tag, out var result)) {
@ -227,102 +248,174 @@ namespace SkiaSharp
return actual != IntPtr.Zero;
}
public int CountGlyphs (string str) => CountGlyphs (str, SKEncoding.Utf16);
// CountGlyphs (string/char)
public int CountGlyphs (string str, SKEncoding encoding)
public int CountGlyphs (string str) =>
CountGlyphs (str.AsSpan ());
[EditorBrowsable (EditorBrowsableState.Never)]
[Obsolete ("Use CountGlyphs(string) instead.")]
public int CountGlyphs (string str, SKEncoding encoding) =>
CountGlyphs (str.AsSpan ());
public int CountGlyphs (ReadOnlySpan<char> str)
{
if (str == null)
throw new ArgumentNullException (nameof (str));
var bytes = StringUtilities.GetEncodedText (str, encoding);
return CountGlyphs (bytes, encoding);
var bytes = StringUtilities.GetEncodedText (str, SKTextEncoding.Utf16);
return CountGlyphs (bytes, SKTextEncoding.Utf16);
}
// CountGlyphs (byte[])
[EditorBrowsable (EditorBrowsableState.Never)]
[Obsolete ("Use CountGlyphs(byte[], SKTextEncoding) instead.")]
public int CountGlyphs (byte[] str, SKEncoding encoding) =>
CountGlyphs (new ReadOnlySpan<byte> (str), encoding);
CountGlyphs (str.AsSpan (), encoding.ToTextEncoding ());
public int CountGlyphs (ReadOnlySpan<byte> str, SKEncoding encoding)
public int CountGlyphs (byte[] str, SKTextEncoding encoding) =>
CountGlyphs (str.AsSpan (), encoding);
[EditorBrowsable (EditorBrowsableState.Never)]
[Obsolete ("Use CountGlyphs(ReadOnlySpan<byte>, SKTextEncoding) instead.")]
public int CountGlyphs (ReadOnlySpan<byte> str, SKEncoding encoding) =>
CountGlyphs (str, encoding.ToTextEncoding ());
public int CountGlyphs (ReadOnlySpan<byte> str, SKTextEncoding encoding)
{
if (str == null)
throw new ArgumentNullException (nameof (str));
fixed (byte* p = str) {
return CountGlyphs ((IntPtr)p, str.Length, encoding);
}
}
public int CountGlyphs (IntPtr str, int strLen, SKEncoding encoding)
// CountGlyphs (IntPtr)
[EditorBrowsable (EditorBrowsableState.Never)]
[Obsolete ("Use CountGlyphs(ReadOnlySpan<byte>, SKTextEncoding) instead.")]
public int CountGlyphs (IntPtr str, int strLen, SKEncoding encoding) =>
CountGlyphs (str, strLen, encoding.ToTextEncoding ());
public int CountGlyphs (IntPtr str, int strLen, SKTextEncoding encoding)
{
if (str == IntPtr.Zero && strLen != 0)
throw new ArgumentNullException (nameof (str));
return SkiaApi.sk_typeface_chars_to_glyphs (Handle, (byte*)str, encoding, null, strLen);
return SkiaApi.sk_typeface_chars_to_glyphs (Handle, (byte*)str, encoding.ToEncoding (), null, strLen);
}
public int GetGlyphs (string text, out ushort[] glyphs) => GetGlyphs (text, SKEncoding.Utf16, out glyphs);
// GetGlyphs (string/char, out)
public int GetGlyphs (string text, SKEncoding encoding, out ushort[] glyphs)
[EditorBrowsable (EditorBrowsableState.Never)]
[Obsolete ("Use GetGlyphs(string) instead.")]
public int GetGlyphs (string text, out ushort[] glyphs)
{
if (text == null)
throw new ArgumentNullException (nameof (text));
var bytes = StringUtilities.GetEncodedText (text, encoding);
return GetGlyphs (bytes, encoding, out glyphs);
glyphs = GetGlyphs (text);
return glyphs.Length;
}
public int GetGlyphs (byte[] text, SKEncoding encoding, out ushort[] glyphs) =>
GetGlyphs (new ReadOnlySpan<byte> (text), encoding, out glyphs);
[EditorBrowsable (EditorBrowsableState.Never)]
[Obsolete ("Use GetGlyphs(string) instead.")]
public int GetGlyphs (string text, SKEncoding encoding, out ushort[] glyphs) =>
GetGlyphs (text, out glyphs);
// GetGlyphs (byte[], out)
[EditorBrowsable (EditorBrowsableState.Never)]
[Obsolete ("Use GetGlyphs(byte[], SKTextEncoding) instead.")]
public int GetGlyphs (byte[] text, SKEncoding encoding, out ushort[] glyphs) =>
GetGlyphs (text.AsSpan (), encoding, out glyphs);
[EditorBrowsable (EditorBrowsableState.Never)]
[Obsolete ("Use GetGlyphs(ReadOnlySpan<byte>, SKTextEncoding) instead.")]
public int GetGlyphs (ReadOnlySpan<byte> text, SKEncoding encoding, out ushort[] glyphs)
{
if (text == null)
throw new ArgumentNullException (nameof (text));
glyphs = GetGlyphs (text, encoding);
return glyphs.Length;
}
fixed (byte* p = text) {
return GetGlyphs ((IntPtr)p, text.Length, encoding, out glyphs);
// GetGlyphs (IntPtr, out)
[EditorBrowsable (EditorBrowsableState.Never)]
[Obsolete ("Use GetGlyphs(IntPtr, int, SKTextEncoding) instead.")]
public int GetGlyphs (IntPtr text, int length, SKEncoding encoding, out ushort[] glyphs)
{
glyphs = GetGlyphs (text, length, encoding);
return glyphs.Length;
}
// GetGlyphs (string/char, out)
public ushort[] GetGlyphs (string text) =>
GetGlyphs (text.AsSpan ());
[EditorBrowsable (EditorBrowsableState.Never)]
[Obsolete ("Use GetGlyphs(string) instead.")]
public ushort[] GetGlyphs (string text, SKEncoding encoding) =>
GetGlyphs (text.AsSpan ());
public ushort[] GetGlyphs (ReadOnlySpan<char> text)
{
fixed (void* t = text) {
return GetGlyphs ((IntPtr)t, text.Length, SKTextEncoding.Utf16);
}
}
public int GetGlyphs (IntPtr text, int length, SKEncoding encoding, out ushort[] glyphs)
// GetGlyphs (byte[], out)
[EditorBrowsable (EditorBrowsableState.Never)]
[Obsolete ("Use GetGlyphs(ReadOnlySpan<byte>, SKTextEncoding) instead.")]
public ushort[] GetGlyphs (byte[] text, SKEncoding encoding) =>
GetGlyphs (text.AsSpan (), encoding.ToTextEncoding ());
[EditorBrowsable (EditorBrowsableState.Never)]
[Obsolete ("Use GetGlyphs(ReadOnlySpan<byte>, SKTextEncoding) instead.")]
public ushort[] GetGlyphs (ReadOnlySpan<byte> text, SKEncoding encoding) =>
GetGlyphs (text, encoding.ToTextEncoding ());
public ushort[] GetGlyphs (ReadOnlySpan<byte> text, SKTextEncoding encoding)
{
fixed (void* t = text) {
return GetGlyphs ((IntPtr)t, text.Length, encoding);
}
}
[EditorBrowsable (EditorBrowsableState.Never)]
[Obsolete ("Use GetGlyphs(IntPtr, int, SKTextEncoding) instead.")]
public ushort[] GetGlyphs (IntPtr text, int length, SKEncoding encoding) =>
GetGlyphs (text, length, encoding.ToTextEncoding ());
public ushort[] GetGlyphs (IntPtr text, int length, SKTextEncoding encoding)
{
if (text == IntPtr.Zero && length != 0)
throw new ArgumentNullException (nameof (text));
var n = SkiaApi.sk_typeface_chars_to_glyphs (Handle, (void*)text, encoding, null, length);
var n = SkiaApi.sk_typeface_chars_to_glyphs (Handle, (void*)text, encoding.ToEncoding (), null, length);
if (n <= 0)
return new ushort[0];
if (n <= 0) {
glyphs = new ushort[0];
return 0;
}
glyphs = new ushort[n];
var glyphs = new ushort[n];
fixed (ushort* gp = glyphs) {
return SkiaApi.sk_typeface_chars_to_glyphs (Handle, (void*)text, encoding, gp, n);
SkiaApi.sk_typeface_chars_to_glyphs (Handle, (void*)text, encoding.ToEncoding (), gp, n);
}
}
public ushort[] GetGlyphs (string text) => GetGlyphs (text, SKEncoding.Utf16);
public ushort[] GetGlyphs (string text, SKEncoding encoding)
{
GetGlyphs (text, encoding, out var glyphs);
return glyphs;
}
public ushort[] GetGlyphs (byte[] text, SKEncoding encoding) =>
GetGlyphs (new ReadOnlySpan<byte> (text), encoding);
// ContainsGlyphs
public ushort[] GetGlyphs (ReadOnlySpan<byte> text, SKEncoding encoding)
{
GetGlyphs (text, encoding, out var glyphs);
return glyphs;
}
public bool ContainsGlyphs (string text) =>
ContainsGlyphs (GetGlyphs (text));
public ushort[] GetGlyphs (IntPtr text, int length, SKEncoding encoding)
{
GetGlyphs (text, length, encoding, out var glyphs);
return glyphs;
}
public bool ContainsGlyphs (ReadOnlySpan<char> text) =>
ContainsGlyphs (GetGlyphs (text));
public bool ContainsGlyphs (ReadOnlySpan<byte> text, SKTextEncoding encoding) =>
ContainsGlyphs (GetGlyphs (text, encoding));
public bool ContainsGlyphs (IntPtr text, int length, SKTextEncoding encoding) =>
ContainsGlyphs (GetGlyphs (text, length, encoding));
private bool ContainsGlyphs (ushort[] glyphs) =>
Array.IndexOf (glyphs, 0) != -1;
// OpenStream
public SKStreamAsset OpenStream () =>
OpenStream (out _);
@ -334,6 +427,20 @@ namespace SkiaSharp
}
}
// GetKerningPairAdjustments
public int[] GetKerningPairAdjustments (ReadOnlySpan<ushort> glyphs)
{
var adjustments = new int[glyphs.Length];
fixed (ushort* gp = glyphs)
fixed (int* ap = adjustments) {
SkiaApi.sk_typeface_get_kerning_pair_adjustments (Handle, gp, glyphs.Length, ap);
}
return adjustments;
}
//
private sealed class SKTypefaceStatic : SKTypeface
{
internal SKTypefaceStatic (IntPtr x)

Просмотреть файл

@ -1658,6 +1658,10 @@ namespace SkiaSharp
[DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
internal static extern void sk_matrix44_set (sk_matrix44_t matrix, Int32 row, Int32 col, Single value);
// void sk_matrix44_set_3x3_row_major(sk_matrix44_t* matrix, float* dst)
[DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
internal static extern void sk_matrix44_set_3x3_row_major (sk_matrix44_t matrix, Single* dst);
// void sk_matrix44_set_col_major(sk_matrix44_t* matrix, float* dst)
[DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
internal static extern void sk_matrix44_set_col_major (sk_matrix44_t matrix, Single* dst);
@ -2318,6 +2322,10 @@ namespace SkiaSharp
[DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
internal static extern void sk_path_transform (sk_path_t cpath, SKMatrix* cmatrix);
// void sk_path_transform_to_dest(sk_path_t* cpath, const sk_matrix_t* cmatrix, sk_path_t* destination)
[DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
internal static extern void sk_path_transform_to_dest (sk_path_t cpath, SKMatrix* cmatrix, sk_path_t destination);
// void sk_pathmeasure_destroy(sk_pathmeasure_t* pathMeasure)
[DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
internal static extern void sk_pathmeasure_destroy (sk_pathmeasure_t pathMeasure);
@ -2490,10 +2498,10 @@ namespace SkiaSharp
[DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
internal static extern void sk_color_unpremultiply_array (UInt32* pmcolors, Int32 size, UInt32* colors);
// bool sk_jpegencoder_encode(sk_wstream_t* dst, const sk_pixmap_t* src, sk_jpegencoder_options_t options)
// bool sk_jpegencoder_encode(sk_wstream_t* dst, const sk_pixmap_t* src, const sk_jpegencoder_options_t* options)
[DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs (UnmanagedType.I1)]
internal static extern bool sk_jpegencoder_encode (sk_wstream_t dst, sk_pixmap_t src, SKJpegEncoderOptions options);
internal static extern bool sk_jpegencoder_encode (sk_wstream_t dst, sk_pixmap_t src, SKJpegEncoderOptions* options);
// void sk_pixmap_destructor(sk_pixmap_t* cpixmap)
[DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
@ -2565,19 +2573,19 @@ namespace SkiaSharp
[return: MarshalAs (UnmanagedType.I1)]
internal static extern bool sk_pixmap_scale_pixels (sk_pixmap_t cpixmap, sk_pixmap_t dst, SKFilterQuality quality);
// bool sk_pngencoder_encode(sk_wstream_t* dst, const sk_pixmap_t* src, sk_pngencoder_options_t options)
// bool sk_pngencoder_encode(sk_wstream_t* dst, const sk_pixmap_t* src, const sk_pngencoder_options_t* options)
[DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs (UnmanagedType.I1)]
internal static extern bool sk_pngencoder_encode (sk_wstream_t dst, sk_pixmap_t src, SKPngEncoderOptions options);
internal static extern bool sk_pngencoder_encode (sk_wstream_t dst, sk_pixmap_t src, SKPngEncoderOptions* options);
// void sk_swizzle_swap_rb(uint32_t* dest, const uint32_t* src, int count)
[DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
internal static extern void sk_swizzle_swap_rb (UInt32* dest, UInt32* src, Int32 count);
// bool sk_webpencoder_encode(sk_wstream_t* dst, const sk_pixmap_t* src, sk_webpencoder_options_t options)
// bool sk_webpencoder_encode(sk_wstream_t* dst, const sk_pixmap_t* src, const sk_webpencoder_options_t* options)
[DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs (UnmanagedType.I1)]
internal static extern bool sk_webpencoder_encode (sk_wstream_t dst, sk_pixmap_t src, SKWebpEncoderOptions options);
internal static extern bool sk_webpencoder_encode (sk_wstream_t dst, sk_pixmap_t src, SKWebpEncoderOptions* options);
#endregion
@ -3301,9 +3309,13 @@ namespace SkiaSharp
#region sk_svg.h
// sk_canvas_t* sk_svgcanvas_create(const sk_rect_t* bounds, sk_xmlwriter_t* writer)
// sk_canvas_t* sk_svgcanvas_create_with_stream(const sk_rect_t* bounds, sk_wstream_t* stream)
[DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
internal static extern sk_canvas_t sk_svgcanvas_create (SKRect* bounds, sk_xmlwriter_t writer);
internal static extern sk_canvas_t sk_svgcanvas_create_with_stream (SKRect* bounds, sk_wstream_t stream);
// sk_canvas_t* sk_svgcanvas_create_with_writer(const sk_rect_t* bounds, sk_xmlwriter_t* writer)
[DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
internal static extern sk_canvas_t sk_svgcanvas_create_with_writer (SKRect* bounds, sk_xmlwriter_t writer);
#endregion
@ -3453,6 +3465,10 @@ namespace SkiaSharp
[DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
internal static extern Int32 sk_typeface_chars_to_glyphs (sk_typeface_t typeface, /* char */ void* chars, SKEncoding encoding, UInt16* glyphs, Int32 glyphCount);
// int sk_typeface_count_glyphs(sk_typeface_t* typeface)
[DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
internal static extern Int32 sk_typeface_count_glyphs (sk_typeface_t typeface);
// int sk_typeface_count_tables(sk_typeface_t* typeface)
[DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
internal static extern Int32 sk_typeface_count_tables (sk_typeface_t typeface);
@ -3493,6 +3509,11 @@ namespace SkiaSharp
[DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
internal static extern sk_fontstyle_t sk_typeface_get_fontstyle (sk_typeface_t typeface);
// bool sk_typeface_get_kerning_pair_adjustments(sk_typeface_t* typeface, const uint16_t* glyphs, int count, int32_t* adjustments)
[DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs (UnmanagedType.I1)]
internal static extern bool sk_typeface_get_kerning_pair_adjustments (sk_typeface_t typeface, UInt16* glyphs, Int32 count, Int32* adjustments);
// size_t sk_typeface_get_table_data(sk_typeface_t* typeface, sk_font_table_tag_t tag, size_t offset, size_t length, void* data)
[DllImport (SKIA, CallingConvention = CallingConvention.Cdecl)]
internal static extern /* size_t */ IntPtr sk_typeface_get_table_data (sk_typeface_t typeface, UInt32 tag, /* size_t */ IntPtr offset, /* size_t */ IntPtr length, void* data);

Просмотреть файл

@ -1,112 +1,139 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Text;
namespace SkiaSharp
{
internal static class Utils
internal unsafe static class Utils
{
internal const float NearlyZero = (1.0f / (1 << 12));
internal const float NearlyZero = 1.0f / (1 << 12);
internal static bool NearlyEqual (float a, float b, float tolerance)
internal static Span<byte> AsSpan (this IntPtr ptr, int size) =>
new Span<byte> ((void*)ptr, size);
internal static ReadOnlySpan<byte> AsReadOnlySpan (this IntPtr ptr, int size) =>
new ReadOnlySpan<byte> ((void*)ptr, size);
internal static bool NearlyEqual (float a, float b, float tolerance) =>
Math.Abs (a - b) <= tolerance;
internal static byte[] GetBytes (this Encoding encoding, ReadOnlySpan<char> text)
{
return Math.Abs (a - b) <= tolerance;
if (text.Length == 0)
return new byte[0];
fixed (char* t = text) {
var byteCount = encoding.GetByteCount (t, text.Length);
if (byteCount == 0)
return new byte[0];
var bytes = new byte[byteCount];
fixed (byte* b = bytes) {
encoding.GetBytes (t, text.Length, b, byteCount);
}
return bytes;
}
}
}
public unsafe static class StringUtilities
{
private static int GetUnicodeStringLength(SKTextEncoding encoding)
{
switch (encoding)
// GetUnicodeStringLength
private static int GetUnicodeStringLength (SKTextEncoding encoding) =>
encoding switch
{
case SKTextEncoding.Utf8:
case SKTextEncoding.Utf16:
return 1;
case SKTextEncoding.Utf32:
return 2;
default:
throw new ArgumentOutOfRangeException(nameof(encoding), $"Encoding {encoding} is not supported.");
}
SKTextEncoding.Utf8 => 1,
SKTextEncoding.Utf16 => 1,
SKTextEncoding.Utf32 => 2,
_ => throw new ArgumentOutOfRangeException (nameof (encoding), $"Encoding {encoding} is not supported.")
};
// GetUnicodeCharacterCode
public static int GetUnicodeCharacterCode (string character, SKTextEncoding encoding)
{
if (character == null)
throw new ArgumentNullException (nameof (character));
if (GetUnicodeStringLength (encoding) != character.Length)
throw new ArgumentException (nameof (character), $"Only a single character can be specified.");
var bytes = GetEncodedText (character, encoding);
return BitConverter.ToInt32 (bytes, 0);
}
public static int GetUnicodeCharacterCode(string character, SKTextEncoding encoding)
{
if (GetUnicodeStringLength(encoding) != character.Length)
throw new ArgumentException(nameof(character), $"Only a single character can be specified.");
// GetEncodedText
var bytes = GetEncodedText(character, encoding);
return BitConverter.ToInt32(bytes, 0);
}
[EditorBrowsable (EditorBrowsableState.Never)]
[Obsolete ("Use GetEncodedText(string, SKTextEncoding) instead.")]
public static byte[] GetEncodedText (string text, SKEncoding encoding) =>
GetEncodedText (text.AsSpan (), encoding.ToTextEncoding ());
public static byte[] GetEncodedText(string text, SKEncoding encoding)
{
if (text == null)
throw new ArgumentNullException (nameof (text));
public static byte[] GetEncodedText (string text, SKTextEncoding encoding) =>
GetEncodedText (text.AsSpan (), encoding);
switch (encoding)
public static byte[] GetEncodedText (ReadOnlySpan<char> text, SKTextEncoding encoding) =>
encoding switch
{
case SKEncoding.Utf8:
return Encoding.UTF8.GetBytes(text);
case SKEncoding.Utf16:
return Encoding.Unicode.GetBytes(text);
case SKEncoding.Utf32:
return Encoding.UTF32.GetBytes(text);
default:
throw new ArgumentOutOfRangeException(nameof(encoding), $"Encoding {encoding} is not supported.");
}
}
SKTextEncoding.Utf8 => Encoding.UTF8.GetBytes (text),
SKTextEncoding.Utf16 => Encoding.Unicode.GetBytes (text),
SKTextEncoding.Utf32 => Encoding.UTF32.GetBytes (text),
_ => throw new ArgumentOutOfRangeException (nameof (encoding), $"Encoding {encoding} is not supported."),
};
public static byte[] GetEncodedText(string text, SKTextEncoding encoding)
// GetString
public static string GetString (IntPtr data, int dataLength, SKTextEncoding encoding) =>
GetString (data.AsReadOnlySpan (dataLength), 0, dataLength, encoding);
public static string GetString (byte[] data, SKTextEncoding encoding) =>
GetString (data, 0, data.Length, encoding);
public static string GetString (byte[] data, int index, int count, SKTextEncoding encoding)
{
if (text == null)
throw new ArgumentNullException (nameof (text));
if (data == null)
throw new ArgumentNullException (nameof (data));
switch (encoding)
return encoding switch
{
case SKTextEncoding.Utf8:
return Encoding.UTF8.GetBytes(text);
case SKTextEncoding.Utf16:
return Encoding.Unicode.GetBytes(text);
case SKTextEncoding.Utf32:
return Encoding.UTF32.GetBytes(text);
default:
throw new ArgumentOutOfRangeException(nameof(encoding), $"Encoding {encoding} is not supported.");
}
SKTextEncoding.Utf8 => Encoding.UTF8.GetString (data, index, count),
SKTextEncoding.Utf16 => Encoding.Unicode.GetString (data, index, count),
SKTextEncoding.Utf32 => Encoding.UTF32.GetString (data, index, count),
_ => throw new ArgumentOutOfRangeException (nameof (encoding), $"Encoding {encoding} is not supported."),
};
}
internal static string GetString(void* data, int dataLength, SKTextEncoding encoding) =>
GetString((IntPtr)data, dataLength, encoding);
public static string GetString (ReadOnlySpan<byte> data, SKTextEncoding encoding) =>
GetString (data, 0, data.Length, encoding);
public static string GetString(IntPtr data, int dataLength, SKTextEncoding encoding)
public static string GetString (ReadOnlySpan<byte> data, int index, int count, SKTextEncoding encoding)
{
if (data == IntPtr.Zero || dataLength <= 0)
return "";
data = data.Slice (index, count);
byte[] result = new byte[dataLength];
Marshal.Copy(data, result, 0, dataLength);
return GetString(result, encoding);
}
if (data.Length == 0)
return string.Empty;
public static string GetString(byte[] data, SKTextEncoding encoding)
{
return GetString(data, 0, data.Length, encoding);
}
public static string GetString(byte[] data, int index, int count, SKTextEncoding encoding)
{
switch (encoding)
#if __DESKTOP__
// TODO: improve this copy for old .NET 4.5
var array = data.ToArray ();
return encoding switch
{
case SKTextEncoding.Utf8:
return Encoding.UTF8.GetString(data, index, count);
case SKTextEncoding.Utf16:
return Encoding.Unicode.GetString(data, index, count);
case SKTextEncoding.Utf32:
return Encoding.UTF32.GetString(data, index, count);
default:
throw new ArgumentOutOfRangeException(nameof(encoding), $"Encoding {encoding} is not supported.");
SKTextEncoding.Utf8 => Encoding.UTF8.GetString (array),
SKTextEncoding.Utf16 => Encoding.Unicode.GetString (array),
SKTextEncoding.Utf32 => Encoding.UTF32.GetString (array),
_ => throw new ArgumentOutOfRangeException (nameof (encoding), $"Encoding {encoding} is not supported."),
};
#else
fixed (byte* bp = data) {
return encoding switch
{
SKTextEncoding.Utf8 => Encoding.UTF8.GetString (bp, data.Length),
SKTextEncoding.Utf16 => Encoding.Unicode.GetString (bp, data.Length),
SKTextEncoding.Utf32 => Encoding.UTF32.GetString (bp, data.Length),
_ => throw new ArgumentOutOfRangeException (nameof (encoding), $"Encoding {encoding} is not supported."),
};
}
#endif
}
}
}

Просмотреть файл

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard1.3;netstandard2.0;net45</TargetFrameworks>
<TargetFrameworks>netstandard1.3;netstandard2.0;net45;net462</TargetFrameworks>
<RootNamespace>HarfBuzzSharp</RootNamespace>
<AssemblyName>HarfBuzzSharp</AssemblyName>
<PackagingGroup>HarfBuzzSharp</PackagingGroup>
@ -9,7 +9,7 @@
<PropertyGroup Condition="$(TargetFramework.StartsWith('netstandard'))">
<DefineConstants>$(DefineConstants);NET_STANDARD</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="$(TargetFramework.StartsWith('net4'))">
<PropertyGroup Condition="$(TargetFramework.StartsWith('net45'))">
<DefineConstants>$(DefineConstants);__DESKTOP__</DefineConstants>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard1.3'">

Просмотреть файл

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard1.3;netstandard2.0;net45</TargetFrameworks>
<TargetFrameworks>netstandard1.3;netstandard2.0;net45;net462</TargetFrameworks>
<RootNamespace>SkiaSharp</RootNamespace>
<AssemblyName>SkiaSharp</AssemblyName>
<PackagingGroup>SkiaSharp</PackagingGroup>
@ -8,7 +8,7 @@
<PropertyGroup Condition="$(TargetFramework.StartsWith('netstandard'))">
<DefineConstants>$(DefineConstants);NET_STANDARD</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="$(TargetFramework.StartsWith('net4'))">
<PropertyGroup Condition="$(TargetFramework.StartsWith('net45'))">
<DefineConstants>$(DefineConstants);__DESKTOP__</DefineConstants>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard1.3'">

Просмотреть файл

@ -9,6 +9,10 @@
"include/c": [ "sk_*", "gr_*" ],
"include/xamarin": [ "sk_*" ]
},
"source": {
"src/c": [ "sk_*", "gr_*" ],
"src/xamarin": [ "sk_*" ]
},
"mappings": {
"types": {
// type aliases

Просмотреть файл

@ -23,8 +23,10 @@ void CopyChangelogs (DirectoryPath diffRoot, string id, string version)
var dllName = file.GetFilenameWithoutExtension ().GetFilenameWithoutExtension ().GetFilenameWithoutExtension ();
if (file.GetFilenameWithoutExtension ().GetExtension () == ".breaking") {
// skip over breaking changes without any breaking changes
if (!FindTextInFiles (file.FullPath, "###").Any ())
if (!FindTextInFiles (file.FullPath, "###").Any ()) {
DeleteFile (file);
continue;
}
dllName += ".breaking";
}
@ -68,9 +70,14 @@ Task ("docs-download-output")
Task ("docs-api-diff")
.Does (async () =>
{
// working version
var baseDir = $"{OUTPUT_NUGETS_PATH}/api-diff";
CleanDirectories (baseDir);
// pretty version
var diffDir = "./output/api-diff";
CleanDirectories (diffDir);
Information ($"Creating comparer...");
var comparer = await CreateNuGetDiffAsync ();
comparer.SaveAssemblyApiInfo = true;
@ -104,6 +111,16 @@ Task ("docs-api-diff")
}
CopyChangelogs (diffRoot, id, version);
// copy pretty version
foreach (var md in GetFiles ($"{diffRoot}/*/*.md")) {
var tfm = md.GetDirectory ().GetDirectoryName();
var prettyPath = ((DirectoryPath)diffDir).CombineWithFilePath ($"{id}/{tfm}/{md.GetFilename ()}");
if (!FindTextInFiles (md.FullPath, "No changes").Any ()) {
EnsureDirectoryExists (prettyPath.GetDirectory ());
CopyFile (md, prettyPath);
}
}
Information ($"Diff complete of '{id}'.");
}
});

Просмотреть файл

@ -35,7 +35,7 @@ void RunCake(FilePath cake, string target = null, Dictionary<string, string> arg
});
}
void RunProcess(FilePath process, string args)
void RunProcess(FilePath process, string args = "")
{
var result = StartProcess(process, args);
if (result != 0) {

2
externals/skia поставляемый

@ -1 +1 @@
Subproject commit 2411fe768b0e3d16a1e59839f65b4d802b74499a
Subproject commit 2eb536d04142b5fb3c7ce605f8efd4f08c7e6760

Просмотреть файл

@ -30,6 +30,9 @@ Please visit https://go.microsoft.com/fwlink/?linkid=868517 to view the release
<group targetFramework="net45">
<dependency id="System.Memory" version="4.5.3" />
</group>
<group targetFramework="net462">
<dependency id="System.Memory" version="4.5.3" />
</group>
<group targetFramework="netstandard1.3">
<dependency id="System.IO.UnmanagedMemoryStream" version="4.3.0" />
<dependency id="System.Memory" version="4.5.3" />
@ -64,6 +67,8 @@ Please visit https://go.microsoft.com/fwlink/?linkid=868517 to view the release
<!-- HarfBuzzSharp.dll -->
<file src="lib/net45/HarfBuzzSharp.dll" />
<file src="lib/net45/HarfBuzzSharp.xml" />
<file src="lib/net462/HarfBuzzSharp.dll" />
<file src="lib/net462/HarfBuzzSharp.xml" />
<file src="lib/netstandard1.3/HarfBuzzSharp.dll" />
<file src="lib/netstandard1.3/HarfBuzzSharp.xml" />
<file src="lib/netstandard2.0/HarfBuzzSharp.dll" />
@ -88,6 +93,8 @@ Please visit https://go.microsoft.com/fwlink/?linkid=868517 to view the release
<!-- the build bits -->
<file src="build/net45/HarfBuzzSharp.targets" />
<file src="build/net45/HarfBuzzSharp.targets" target="buildTransitive/net45/HarfBuzzSharp.targets" />
<file src="build/net462/HarfBuzzSharp.targets" />
<file src="build/net462/HarfBuzzSharp.targets" target="buildTransitive/net462/HarfBuzzSharp.targets" />
<file src="build/tizen40/HarfBuzzSharp.targets" />
<!-- harfbuzz.dll and other native files -->

Просмотреть файл

@ -31,6 +31,9 @@ Please visit https://go.microsoft.com/fwlink/?linkid=868517 to view the release
<group targetFramework="net45">
<dependency id="System.Memory" version="4.5.3" />
</group>
<group targetFramework="net462">
<dependency id="System.Memory" version="4.5.3" />
</group>
<group targetFramework="netstandard1.3">
<dependency id="System.IO.UnmanagedMemoryStream" version="4.3.0" />
<dependency id="System.Memory" version="4.5.3" />
@ -65,6 +68,8 @@ Please visit https://go.microsoft.com/fwlink/?linkid=868517 to view the release
<!-- SkiaSharp.dll -->
<file src="lib/net45/SkiaSharp.dll" />
<file src="lib/net45/SkiaSharp.xml" />
<file src="lib/net462/SkiaSharp.dll" />
<file src="lib/net462/SkiaSharp.xml" />
<file src="lib/netstandard1.3/SkiaSharp.dll" />
<file src="lib/netstandard1.3/SkiaSharp.xml" />
<file src="lib/netstandard2.0/SkiaSharp.dll" />
@ -89,6 +94,8 @@ Please visit https://go.microsoft.com/fwlink/?linkid=868517 to view the release
<!-- the build bits -->
<file src="build/net45/SkiaSharp.targets" />
<file src="build/net45/SkiaSharp.targets" target="buildTransitive/net45/SkiaSharp.targets" />
<file src="build/net462/SkiaSharp.targets" />
<file src="build/net462/SkiaSharp.targets" target="buildTransitive/net462/SkiaSharp.targets" />
<file src="build/tizen40/SkiaSharp.targets" />
<!-- libSkiaSharp.dll and other native files -->

Просмотреть файл

@ -24,9 +24,6 @@ variables:
VM_IMAGE_LINUX: Hosted Ubuntu 1604
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
# # To speed up builds when debugging DevOps
# DOWNLOAD_EXTERNALS: ''
resources:
repositories:
- repository: xamarin-templates
@ -299,7 +296,7 @@ stages:
vmImage: $(VM_IMAGE_WINDOWS)
target: nuget
additionalArgs: --packall=true --exclusive
shouldPublish: false
shouldPublish: true
requiredArtifacts:
- managed_linux
- managed_macos
@ -316,6 +313,35 @@ stages:
artifactName: nuget
pathToPublish: 'output/nugets'
- stage: api_diff
displayName: API Diff
dependsOn: package
jobs:
- template: azure-templates-bootstrapper.yml # API Diff
parameters:
name: api_diff_windows
displayName: API Diff
vmImage: $(VM_IMAGE_WINDOWS_NATIVE)
target: docs-api-diff
shouldPublish: false
requiredArtifacts:
- package_windows
preBuildSteps:
- pwsh: |
$p = "$env:BUILD_SOURCESDIRECTORY\output\nugets\api-diff"
New-Item -ItemType Directory -Force -Path $p | Out-Null
$uri = 'https://xamarin.azureedge.net/GTKforWindows/Windows/gtk-sharp-2.12.45.msi'
.\scripts\download-file.ps1 -Uri $uri -OutFile gtk-sharp.msi
msiexec /i gtk-sharp.msi /norestart /quiet /l* $p\gtk-sharp-install.log
displayName: Install GTK# 2.12
postBuildSteps:
- task: PublishBuildArtifacts@1
displayName: Publish the API diffs
condition: always()
inputs:
artifactName: api-diff
pathToPublish: '$(Build.SourcesDirectory)\output\api-diff'
- ${{ if eq(variables['System.TeamProject'], 'devdiv') }}:
- stage: signing
displayName: Sign NuGets

Просмотреть файл

@ -14,7 +14,7 @@ parameters:
condition: succeeded() # whether or not to run this template
shouldPublish: true # whether or not to publish the artifacts
configuration: $(CONFIGURATION) # the build configuration
buildExternals: $(DOWNLOAD_EXTERNALS) # the build number to download externals from
buildExternals: '' # the build number to download externals from
verbosity: $(VERBOSITY) # the level of verbosity to use when building
docker: '' # the Docker image to build and use
@ -26,6 +26,7 @@ jobs:
# displayName: ${{ parameters.displayName }}
# vmImage: ${{ parameters.vmImage }}
# condition: ${{ parameters.condition }}
# buildExternals: ${{ parameters.buildExternals }}
# - ${{ if or(eq(parameters.buildExternals, ''), not(startsWith(parameters.name, 'native_'))) }}:
- job: ${{ parameters.name }}

Просмотреть файл

@ -3,7 +3,7 @@ parameters:
displayName: '' # the human name
vmImage: '' # the VM image
condition: succeeded() # whether or not to run this template
buildExternals: $(DOWNLOAD_EXTERNALS) # the build number to download externals from
buildExternals: '' # the build number to download externals from
jobs:
- job: ${{ parameters.name }}

Просмотреть файл

@ -18,7 +18,7 @@
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\AnyCPU\Debug\</OutputPath>
<DefineConstants>$(DefineConstants);DEBUG;TRACE</DefineConstants>
<DefineConstants>$(DefineConstants);DEBUG;TRACE;SYSTEM_DRAWING</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
@ -26,14 +26,14 @@
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\AnyCPU\Release\</OutputPath>
<DefineConstants>$(DefineConstants);TRACE</DefineConstants>
<DefineConstants>$(DefineConstants);TRACE;SYSTEM_DRAWING</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>$(DefineConstants);DEBUG;TRACE</DefineConstants>
<DefineConstants>$(DefineConstants);DEBUG;TRACE;SYSTEM_DRAWING</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
@ -42,7 +42,7 @@
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>$(DefineConstants);TRACE</DefineConstants>
<DefineConstants>$(DefineConstants);TRACE;SYSTEM_DRAWING</DefineConstants>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
@ -50,7 +50,7 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>$(DefineConstants);DEBUG;TRACE</DefineConstants>
<DefineConstants>$(DefineConstants);DEBUG;TRACE;SYSTEM_DRAWING</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
@ -59,7 +59,7 @@
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>$(DefineConstants);TRACE</DefineConstants>
<DefineConstants>$(DefineConstants);TRACE;SYSTEM_DRAWING</DefineConstants>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>

Просмотреть файл

@ -19,6 +19,15 @@ namespace SkiaSharp.Tests
}
}
[SkippableFact]
public void ToGlSizedFormat()
{
foreach (GRPixelConfig value in Enum.GetValues(typeof(GRPixelConfig)))
{
value.ToGlSizedFormat();
}
}
[Trait(CategoryKey, GpuCategory)]
[SkippableFact]
public void CreateSpecificContextIsValid()

Просмотреть файл

@ -7,9 +7,6 @@ namespace SkiaSharp.Tests
{
public class SKBasicTypesTest : SKTest
{
private const float EPSILON = 0.0001f;
private const int PRECISION = 4;
[SkippableFact]
public void ImageInfoMethodsDoNotModifySource()
{

Просмотреть файл

@ -1,28 +1,107 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Xunit;
namespace SkiaSharp.Tests
{
public class SKBitmapTest : SKTest
{
[SkippableFact]
public void BitmapCanCopyIsCorrect()
public static IEnumerable<object[]> GetAllColorTypes()
{
var bmp = CreateTestBitmap();
foreach (SKColorType ct in Enum.GetValues(typeof(SKColorType)))
yield return new object[] { ct };
}
Assert.True(bmp.CanCopyTo(SKColorType.Alpha8));
Assert.True(bmp.CanCopyTo(SKColorType.Rgb565));
Assert.True(bmp.CanCopyTo(SKColorType.Argb4444));
Assert.True(bmp.CanCopyTo(SKColorType.Bgra8888));
Assert.True(bmp.CanCopyTo(SKColorType.Rgba8888));
Assert.True(bmp.CanCopyTo(SKColorType.RgbaF16));
[SkippableTheory]
[MemberData(nameof(GetAllColorTypes))]
public void CanCopyToIsCorrect(SKColorType colorType)
{
using var bmp = CreateTestBitmap();
Assert.False(bmp.CanCopyTo(SKColorType.Unknown));
Assert.False(bmp.CanCopyTo(SKColorType.Gray8));
var canCopy = bmp.CanCopyTo(colorType);
if (colorType == SKColorType.Unknown)
Assert.False(canCopy);
else
Assert.True(canCopy);
}
[SkippableTheory]
[MemberData(nameof(GetAllColorTypes))]
public void CopyToSucceeds(SKColorType colorType)
{
var alphaType = colorType.GetAlphaType();
using var bmp = CreateTestBitmap();
using var copy = bmp.Copy(colorType);
if (colorType == SKColorType.Unknown)
{
Assert.Null(copy);
}
else
{
Assert.NotNull(copy);
Assert.Equal(colorType, copy.ColorType);
Assert.Equal(alphaType, copy.AlphaType);
var color = copy.GetPixel(10, 10);
Assert.NotEqual(SKColors.Empty, color);
if (colorType == SKColorType.Gray8)
Assert.Equal(0xFF353535, color);
else if (colorType == SKColorType.Alpha8)
Assert.Equal(0xFF000000, color);
else
Assert.Equal(0xFFFF0000, color);
}
}
[SkippableTheory]
[MemberData(nameof(GetAllColorTypes))]
public void CopyWithAlphaToSucceeds(SKColorType colorType)
{
var alphaType = colorType.GetAlphaType();
using var bmp = CreateTestBitmap(170);
using var copy = bmp.Copy(colorType);
if (colorType == SKColorType.Unknown)
{
Assert.Null(copy);
}
else
{
Assert.NotNull(copy);
Assert.Equal(colorType, copy.ColorType);
Assert.Equal(alphaType, copy.AlphaType);
var color = copy.GetPixel(10, 10);
Assert.NotEqual(SKColors.Empty, color);
if (colorType == SKColorType.Gray8)
{
Assert.Equal((SKColor)0xFF232323, color);
}
else if (alphaType == SKAlphaType.Opaque)
{
Assert.True(color.Red > color.Green);
Assert.True(color.Red > color.Blue);
Assert.Equal(255, color.Alpha);
}
else if (colorType == SKColorType.Alpha8)
{
Assert.Equal((SKColor)0xAA000000, color);
}
else
{
Assert.Equal((SKColor)0xAAFF0000, color);
}
}
}
[SkippableFact]
@ -63,15 +142,21 @@ namespace SkiaSharp.Tests
var bmp = CreateTestBitmap();
var alpha8 = bmp.Copy(SKColorType.Alpha8);
Assert.Equal(SKColors.Black, alpha8.GetPixel(10, 10));
Assert.Equal(SKColors.Black, alpha8.GetPixel(30, 10));
Assert.Equal(SKColors.Black, alpha8.GetPixel(10, 30));
Assert.Equal(SKColors.Black, alpha8.GetPixel(30, 30));
Assert.Equal(SKColorType.Alpha8, alpha8.ColorType);
}
bmp = CreateTestBitmap(127);
[SkippableFact]
public void BitmapWithAlphaCopyToAlpha8PreservesData()
{
var bmp = CreateTestBitmap(127);
var alpha8 = bmp.Copy(SKColorType.Alpha8);
alpha8 = bmp.Copy(SKColorType.Alpha8);
Assert.Equal(SKColors.Black.WithAlpha(127), alpha8.GetPixel(10, 10));
Assert.Equal(SKColors.Black.WithAlpha(127), alpha8.GetPixel(30, 10));
Assert.Equal(SKColors.Black.WithAlpha(127), alpha8.GetPixel(10, 30));
@ -85,17 +170,23 @@ namespace SkiaSharp.Tests
var bmp = CreateTestBitmap();
var argb4444 = bmp.Copy(SKColorType.Argb4444);
Assert.Equal((SKColor)0xffff0000, argb4444.GetPixel(10, 10));
Assert.Equal((SKColor)0xff007700, argb4444.GetPixel(30, 10));
Assert.Equal((SKColor)0xff008800, argb4444.GetPixel(30, 10));
Assert.Equal((SKColor)0xff0000ff, argb4444.GetPixel(10, 30));
Assert.Equal((SKColor)0xffffff00, argb4444.GetPixel(30, 30));
Assert.Equal(SKColorType.Argb4444, argb4444.ColorType);
}
bmp = CreateTestBitmap(127);
[SkippableFact]
public void BitmapWithAlphaCopyToArgb4444PreservesData()
{
var bmp = CreateTestBitmap(127);
var argb4444 = bmp.Copy(SKColorType.Argb4444);
argb4444 = bmp.Copy(SKColorType.Argb4444);
Assert.Equal((SKColor)0x77ff0000, argb4444.GetPixel(10, 10));
Assert.Equal((SKColor)0x77006d00, argb4444.GetPixel(30, 10));
Assert.Equal((SKColor)0x77009200, argb4444.GetPixel(30, 10));
Assert.Equal((SKColor)0x770000ff, argb4444.GetPixel(10, 30));
Assert.Equal((SKColor)0x77ffff00, argb4444.GetPixel(30, 30));
Assert.Equal(SKColorType.Argb4444, argb4444.ColorType);
@ -107,15 +198,21 @@ namespace SkiaSharp.Tests
var bmp = CreateTestBitmap();
var rgb565 = bmp.Copy(SKColorType.Rgb565);
Assert.Equal((SKColor)0xffff0000, rgb565.GetPixel(10, 10));
Assert.Equal((SKColor)0xff008200, rgb565.GetPixel(31, 10));
Assert.Equal((SKColor)0xff0000ff, rgb565.GetPixel(10, 30));
Assert.Equal((SKColor)0xffffff00, rgb565.GetPixel(30, 30));
Assert.Equal(SKColorType.Rgb565, rgb565.ColorType);
}
bmp = CreateTestBitmap(127);
[SkippableFact]
public void BitmapWithAlphaCopyToRgb565PreservesData()
{
var bmp = CreateTestBitmap(127);
var rgb565 = bmp.Copy(SKColorType.Rgb565);
rgb565 = bmp.Copy(SKColorType.Rgb565);
Assert.Equal((SKColor)0xff7b0000, rgb565.GetPixel(10, 10));
Assert.Equal((SKColor)0xff004100, rgb565.GetPixel(31, 10));
Assert.Equal((SKColor)0xff00007b, rgb565.GetPixel(10, 30));
@ -129,19 +226,25 @@ namespace SkiaSharp.Tests
var bmp = CreateTestBitmap();
var rgbaF16 = bmp.Copy(SKColorType.RgbaF16);
Assert.Equal((SKColor)0xffff0000, rgbaF16.GetPixel(10, 10));
Assert.Equal((SKColor)0xff003700, rgbaF16.GetPixel(30, 10));
Assert.Equal((SKColor)0xff008000, rgbaF16.GetPixel(30, 10));
Assert.Equal((SKColor)0xff0000ff, rgbaF16.GetPixel(10, 30));
Assert.Equal((SKColor)0xffffff00, rgbaF16.GetPixel(30, 30));
Assert.Equal(SKColorType.RgbaF16, rgbaF16.ColorType);
}
bmp = CreateTestBitmap(127);
[SkippableFact]
public void BitmapWithAlphaCopyToRgbaF16PreservesData()
{
var bmp = CreateTestBitmap(127);
rgbaF16 = bmp.Copy(SKColorType.RgbaF16);
Assert.Equal((SKColor)0x7f6d0000, rgbaF16.GetPixel(10, 10));
Assert.Equal((SKColor)0x7f001a00, rgbaF16.GetPixel(30, 10));
Assert.Equal((SKColor)0x7f00006d, rgbaF16.GetPixel(10, 30));
Assert.Equal((SKColor)0x7f6d6d00, rgbaF16.GetPixel(30, 30));
var rgbaF16 = bmp.Copy(SKColorType.RgbaF16);
Assert.Equal((SKColor)0x7fff0000, rgbaF16.GetPixel(10, 10));
Assert.Equal((SKColor)0x7f008100, rgbaF16.GetPixel(30, 10));
Assert.Equal((SKColor)0x7f0000ff, rgbaF16.GetPixel(10, 30));
Assert.Equal((SKColor)0x7fffff00, rgbaF16.GetPixel(30, 30));
Assert.Equal(SKColorType.RgbaF16, rgbaF16.ColorType);
}
@ -154,12 +257,6 @@ namespace SkiaSharp.Tests
ValidateTestBitmap(bmp.Copy(SKImageInfo.PlatformColorType));
Assert.Null(bmp.Copy(SKColorType.Unknown));
Assert.Null(bmp.Copy(SKColorType.Gray8));
// alpha to non-alpha is not supported
Assert.Null(bmp
.Copy(SKColorType.Alpha8)
.Copy(SKImageInfo.PlatformColorType));
}
[SkippableFact]
@ -167,13 +264,15 @@ namespace SkiaSharp.Tests
{
bool released = false;
var onRelease = new SKBitmapReleaseDelegate((addr, ctx) => {
var onRelease = new SKBitmapReleaseDelegate((addr, ctx) =>
{
Marshal.FreeCoTaskMem(addr);
released = true;
Assert.Equal("RELEASING!", ctx);
});
using (var bitmap = new SKBitmap()) {
using (var bitmap = new SKBitmap())
{
var info = new SKImageInfo(1, 1);
var pixels = Marshal.AllocCoTaskMem(info.BytesSize);
@ -187,7 +286,8 @@ namespace SkiaSharp.Tests
public void ImageCreateDoesNotThrow()
{
var info = new SKImageInfo(1, 1);
using (var image = SKImage.Create(info)) {
using (var image = SKImage.Create(info))
{
Assert.False(image.IsTextureBacked);
Assert.Equal(image, image.ToRasterImage());
}
@ -199,7 +299,8 @@ namespace SkiaSharp.Tests
var info = new SKImageInfo(1, 1);
var pixels = Marshal.AllocCoTaskMem(info.BytesSize);
using (var bitmap = new SKBitmap()) {
using (var bitmap = new SKBitmap())
{
bitmap.InstallPixels(info, pixels, info.RowBytes);
}
@ -215,7 +316,8 @@ namespace SkiaSharp.Tests
var alpha = new SKBitmap();
SKPointI offset;
SKPaint paint = new SKPaint {
SKPaint paint = new SKPaint
{
MaskFilter = SKMaskFilter.CreateBlur(SKBlurStyle.Normal, 5.0f)
};
@ -230,13 +332,15 @@ namespace SkiaSharp.Tests
var path = Path.Combine(PathToImages, "color-wheel.png");
using (var bitmap = SKBitmap.Decode(path))
using (var surface = SKSurface.Create(new SKImageInfo(200, 200))) {
using (var surface = SKSurface.Create(new SKImageInfo(200, 200)))
{
var canvas = surface.Canvas;
canvas.Clear(SKColors.White);
canvas.DrawBitmap(bitmap, 0, 0);
using (var img = surface.Snapshot())
using (var bmp = SKBitmap.FromImage(img)) {
using (var bmp = SKBitmap.FromImage(img))
{
Assert.Equal(new SKColor(2, 255, 42), bmp.GetPixel(20, 20));
Assert.Equal(new SKColor(1, 83, 255), bmp.GetPixel(108, 20));
Assert.Equal(new SKColor(255, 166, 1), bmp.GetPixel(20, 108));
@ -249,7 +353,8 @@ namespace SkiaSharp.Tests
public void BitmapAndPixmapAreValid()
{
var info = new SKImageInfo(10, 10);
using (var bitmap = new SKBitmap(info)) {
using (var bitmap = new SKBitmap(info))
{
Assert.Equal(10, bitmap.Width);
Assert.Equal(10, bitmap.Height);
@ -275,7 +380,8 @@ namespace SkiaSharp.Tests
var srcBmp = new SKBitmap(srcInfo);
using (var canvas = new SKCanvas(srcBmp))
using (var paint = new SKPaint { Color = SKColors.Green }) {
using (var paint = new SKPaint { Color = SKColors.Green })
{
canvas.Clear(SKColors.Blue);
canvas.DrawRect(new SKRect(0, 0, 100, 200), paint);
}
@ -299,7 +405,8 @@ namespace SkiaSharp.Tests
var srcBmp = new SKBitmap(srcInfo);
using (var canvas = new SKCanvas(srcBmp))
using (var paint = new SKPaint { Color = SKColors.Green }) {
using (var paint = new SKPaint { Color = SKColors.Green })
{
canvas.Clear(SKColors.Blue);
canvas.DrawRect(new SKRect(0, 0, 100, 200), paint);
}
@ -351,7 +458,7 @@ namespace SkiaSharp.Tests
{
Assert.Equal(255, pixel.Alpha);
}
var maskBuffer = new byte[]
{
128, 127, 126, 125,
@ -461,5 +568,26 @@ namespace SkiaSharp.Tests
Assert.NotNull(bitmap);
}
}
[SkippableFact]
public void CanCreateUsingRowBytes()
{
using var src = CreateTestBitmap();
var xOffset = 19 * src.BytesPerPixel;
var yOffset = 19 * src.RowBytes;
var offset = yOffset + xOffset;
using var bmp = new SKBitmap();
bmp.InstallPixels(new SKImageInfo(2, 2), src.GetPixels() + offset, src.RowBytes);
Assert.Equal(2, bmp.Width);
Assert.Equal(2, bmp.Height);
Assert.Equal(SKColors.Red, bmp.GetPixel(0, 0));
Assert.Equal(SKColors.Green, bmp.GetPixel(1, 0));
Assert.Equal(SKColors.Blue, bmp.GetPixel(0, 1));
Assert.Equal(SKColors.Yellow, bmp.GetPixel(1, 1));
}
}
}

Просмотреть файл

@ -393,6 +393,47 @@ namespace SkiaSharp.Tests
{
var stream = new MemoryStream();
using (var svg = SKSvgCanvas.Create(SKRect.Create(100, 100), stream))
{
var paint = new SKPaint
{
Color = SKColors.Red,
Style = SKPaintStyle.Fill
};
svg.DrawRect(SKRect.Create(10, 10, 80, 80), paint);
}
stream.Position = 0;
using (var reader = new StreamReader(stream))
{
var xml = reader.ReadToEnd();
var xdoc = XDocument.Parse(xml);
var svg = xdoc.Root;
var ns = svg.Name.Namespace;
Assert.Equal(ns + "svg", svg.Name);
Assert.Equal("100", svg.Attribute("width")?.Value);
Assert.Equal("100", svg.Attribute("height")?.Value);
var rect = svg.Element(ns + "rect");
Assert.Equal(ns + "rect", rect.Name);
Assert.Equal("rgb(255,0,0)", rect.Attribute("fill")?.Value);
Assert.Equal("none", rect.Attribute("stroke")?.Value);
Assert.Equal("10", rect.Attribute("x")?.Value);
Assert.Equal("10", rect.Attribute("y")?.Value);
Assert.Equal("80", rect.Attribute("width")?.Value);
Assert.Equal("80", rect.Attribute("height")?.Value);
}
}
[Obsolete]
[SkippableFact]
public void SvgCanvasSavesFileUsingWriter()
{
var stream = new MemoryStream();
using (var wstream = new SKManagedWStream(stream))
using (var writer = new SKXmlStreamWriter(wstream))
using (var svg = SKSvgCanvas.Create(SKRect.Create(100, 100), writer))

Просмотреть файл

@ -0,0 +1,52 @@
using Xunit;
namespace SkiaSharp.Tests
{
public class SKColorSpaceStructsTest : SKTest
{
// SKColorSpacePrimaries
[SkippableFact]
public void PrimariesGoFullCircle()
{
var primaries = new float[] { 1, 2, 3, 4, 5, 6, 7, 8 };
var csp = new SKColorSpacePrimaries(primaries);
Assert.Equal(primaries, csp.Values);
}
[SkippableFact]
public void PrimariesToMatrix44()
{
var primaries = new float[] {
0.64f, 0.33f,
0.30f, 0.60f,
0.15f, 0.06f,
0.3127f, 0.3290f
};
var csp = new SKColorSpacePrimaries(primaries);
var matrix44 = csp.ToXyzD50();
Assert.NotNull(matrix44);
using var srgb = SKColorSpace.CreateSrgb();
var srgb44 = srgb.ToXyzD50();
AssertSimilar(srgb44.ToRowMajor(), matrix44.ToRowMajor(), PRECISION);
}
// SKColorSpaceTransferFn
[SkippableFact]
public void TransferFnIsFullCircle()
{
var values = new float[7] { 1, 2, 3, 4, 5, 6, 7 };
var tf = new SKColorSpaceTransferFn(values);
var tfValues = tf.Values;
Assert.Equal(values, tfValues);
}
}
}

Просмотреть файл

@ -177,7 +177,7 @@ namespace SkiaSharp.Tests
Assert.False(SKObject.GetInstance<SKDynamicMemoryWStream>(handle, out _));
void DoWork(out IntPtr streamHandle)
static void DoWork(out IntPtr streamHandle)
{
using (var document = CreateDocument(out streamHandle))
{
@ -188,15 +188,15 @@ namespace SkiaSharp.Tests
CollectGarbage();
Assert.True(SKObject.GetInstance<SKDynamicMemoryWStream>(handle, out _));
Assert.True(SKObject.GetInstance<SKDynamicMemoryWStream>(streamHandle, out _));
document.Close();
}
Assert.True(SKObject.GetInstance<SKDynamicMemoryWStream>(handle, out _));
Assert.True(SKObject.GetInstance<SKDynamicMemoryWStream>(streamHandle, out _));
}
SKDocument CreateDocument(out IntPtr streamHandle)
static SKDocument CreateDocument(out IntPtr streamHandle)
{
var stream = new SKDynamicMemoryWStream();
streamHandle = stream.Handle;

Просмотреть файл

@ -0,0 +1,152 @@
using System;
using System.IO;
using Xunit;
namespace SkiaSharp.Tests
{
public class SKManagedWStreamTest : SKTest
{
[SkippableFact]
public void DotNetStreamIsCollected()
{
var dotnet = new MemoryStream();
var stream = new SKManagedWStream(dotnet, true);
Assert.Equal(0, dotnet.Position);
stream.Dispose();
Assert.Throws<ObjectDisposedException>(() => dotnet.Position);
}
[SkippableFact]
public void DotNetStreamIsNotCollected()
{
var dotnet = new MemoryStream();
var stream = new SKManagedWStream(dotnet, false);
Assert.Equal(0, dotnet.Position);
stream.Dispose();
Assert.Equal(0, dotnet.Position);
}
[SkippableFact]
public unsafe void StreamIsNotDisposedWhenReferencedIsDisposed()
{
var stream = new SKManagedWStream(new MemoryStream(), true);
var handle = stream.Handle;
var document = SKDocument.CreatePdf(stream);
document.Dispose();
Assert.True(SKObject.GetInstance<SKManagedWStream>(handle, out _));
}
[SkippableFact]
public void StreamIsCollectedEvenWhenNotProperlyDisposed()
{
VerifyImmediateFinalizers();
var handle = DoWork();
CollectGarbage();
var exists = SKObject.GetInstance<SKManagedWStream>(handle, out _);
Assert.False(exists);
static IntPtr DoWork()
{
var dotnet = new MemoryStream();
var stream = new SKManagedWStream(dotnet, true);
return stream.Handle;
}
}
[SkippableFact]
public void ManagedStreamWritesByteCorrectly()
{
var dotnet = new MemoryStream();
var stream = new SKManagedWStream(dotnet);
Assert.Equal(0, dotnet.Position);
Assert.Equal(0, stream.BytesWritten);
stream.Write8(123);
Assert.Equal(1, dotnet.Position);
Assert.Equal(1, stream.BytesWritten);
Assert.Equal(new byte[] { 123 }, dotnet.ToArray());
stream.Write8(246);
Assert.Equal(2, dotnet.Position);
Assert.Equal(2, stream.BytesWritten);
Assert.Equal(new byte[] { 123, 246 }, dotnet.ToArray());
}
[SkippableFact]
public void ManagedStreamWritesChunkCorrectly()
{
var data = new byte[1024];
for (var i = 0; i < data.Length; i++)
{
data[i] = (byte)(i % byte.MaxValue);
}
var dotnet = new MemoryStream();
var stream = new SKManagedWStream(dotnet);
Assert.Equal(0, dotnet.Position);
Assert.Equal(0, stream.BytesWritten);
stream.Write(data, data.Length);
Assert.Equal(data.Length, dotnet.Position);
Assert.Equal(data.Length, stream.BytesWritten);
Assert.Equal(data, dotnet.ToArray());
}
[SkippableFact]
public unsafe void StreamIsReferencedAndNotDisposedPrematurely()
{
VerifyImmediateFinalizers();
DoWork(out var docH, out var streamH);
CollectGarbage();
Assert.False(SKObject.GetInstance<SKManagedWStream>(streamH, out _));
Assert.False(SKObject.GetInstance<SKDocument>(docH, out _));
static void DoWork(out IntPtr documentHandle, out IntPtr streamHandle)
{
var document = CreateDocument(out streamHandle);
documentHandle = document.Handle;
CollectGarbage();
Assert.NotNull(document.BeginPage(100, 100));
document.EndPage();
document.Close();
Assert.True(SKObject.GetInstance<SKManagedWStream>(streamHandle, out var stream));
Assert.True(stream.OwnsHandle);
Assert.False(stream.IgnorePublicDispose);
}
static SKDocument CreateDocument(out IntPtr streamHandle)
{
var stream = new SKManagedWStream(new MemoryStream(), true);
streamHandle = stream.Handle;
Assert.True(stream.OwnsHandle);
Assert.False(stream.IgnorePublicDispose);
Assert.True(SKObject.GetInstance<SKManagedWStream>(streamHandle, out _));
return SKDocument.CreatePdf(stream);
}
}
}
}

Просмотреть файл

@ -8,9 +8,6 @@ namespace SkiaSharp.Tests
{
public class SKMaskTest : SKTest
{
private const float EPSILON = 0.0001f;
private const int PRECISION = 4;
[SkippableFact]
public unsafe void FixedImageMaskIsHandledCorrectly()
{
@ -20,12 +17,11 @@ namespace SkiaSharp.Tests
UInt32 rowBytes = 1;
var format = SKMaskFormat.BW;
fixed (void* bufferPtr = buffer)
{
var mask = SKMask.Create(buffer, bounds, rowBytes, format);
var mask = SKMask.Create(buffer, bounds, rowBytes, format);
Assert.Equal(rawMask, mask.GetAddr1(0, 0));
}
Assert.Equal(rawMask, mask.GetAddr1(0, 0));
mask.FreeImage();
}
[SkippableFact]

Просмотреть файл

@ -0,0 +1,255 @@
using System;
using Xunit;
namespace SkiaSharp.Tests
{
public class SKMatrix44Test : SKTest
{
[SkippableFact]
public void Matrix44CreatesIdentity()
{
var matrix = SKMatrix44.CreateIdentity();
var expectedRowMajor = new float[] {
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
};
var rowMajor = matrix.ToRowMajor();
Assert.Equal(expectedRowMajor, rowMajor);
}
[SkippableFact]
public void RowMajorColumnMajorTransposes()
{
var rowMajor = new float[] {
1, 2, 3, 0,
0, 1, 4, 0,
5, 6, 1, 0,
0, 0, 0, 1,
};
var colMajor = new float[] {
1, 0, 5, 0,
2, 1, 6, 0,
3, 4, 1, 0,
0, 0, 0, 1,
};
var rowMajorMatrix = SKMatrix44.FromRowMajor(rowMajor);
var colMajorMatrix = rowMajorMatrix.ToColumnMajor();
Assert.Equal(colMajor, colMajorMatrix);
}
[SkippableFact]
public void MatrixGoesFullCircle()
{
var rowMajor = new float[] {
1, 2, 3, 0,
0, 1, 4, 0,
5, 6, 1, 0,
0, 0, 0, 1,
};
var matrix = SKMatrix44.FromRowMajor(rowMajor);
var result = matrix.ToRowMajor();
Assert.Equal(rowMajor, result);
}
[SkippableFact]
public void TransposeWorks()
{
var rowMajor = new float[] {
1, 2, 3, 0,
0, 1, 4, 0,
5, 6, 1, 0,
0, 0, 0, 1,
};
var colMajor = new float[] {
1, 0, 5, 0,
2, 1, 6, 0,
3, 4, 1, 0,
0, 0, 0, 1,
};
var rowMajorMatrix = SKMatrix44.FromRowMajor(rowMajor);
rowMajorMatrix.Transpose();
var colMajorMatrix = rowMajorMatrix.ToRowMajor();
Assert.Equal(colMajor, colMajorMatrix);
}
[SkippableFact]
public void ColumnMajorToRowMajorTransposes()
{
var rowMajor = new float[] {
1, 2, 3, 0,
0, 1, 4, 0,
5, 6, 1, 0,
0, 0, 0, 1,
};
var colMajor = new float[] {
1, 0, 5, 0,
2, 1, 6, 0,
3, 4, 1, 0,
0, 0, 0, 1,
};
var colMajorMatrix = SKMatrix44.FromColumnMajor(colMajor);
var rowMajorMatrix = colMajorMatrix.ToRowMajor();
Assert.Equal(rowMajor, rowMajorMatrix);
}
[SkippableFact]
public void Matrix44Inverts()
{
var rowMajor = new float[] {
1, 2, 3, 0,
0, 1, 4, 0,
5, 6, 1, 0,
0, 0, 0, 1,
};
var expectedRowMajor = new float[] {
-11.5f, 8, 2.5f, 0,
10, -7, -2, 0,
-2.5f, 2, 0.5f, 0,
0, 0, 0, 1,
};
var determinant = 2f;
var matrix = SKMatrix44.FromRowMajor(rowMajor);
Assert.Equal(rowMajor, matrix.ToRowMajor());
Assert.Equal(determinant, matrix.Determinant());
var inverted = matrix.Invert();
Assert.Equal(1f / determinant, inverted.Determinant());
var actualRowMajor = inverted.ToRowMajor();
Assert.Equal(expectedRowMajor, actualRowMajor);
}
[SkippableFact]
public void Matrix44ConvertsToMatrix()
{
var rowMajor44 = new float[] {
2, 3, 4, 5,
4, 6, 8, 10,
6, 9, 12, 15,
8, 12, 16, 20,
};
var rowMajor = new float[] {
rowMajor44[0], rowMajor44[1], rowMajor44[3],
rowMajor44[4], rowMajor44[5], rowMajor44[7],
rowMajor44[12], rowMajor44[13], rowMajor44[15],
};
var matrix44 = SKMatrix44.FromRowMajor(rowMajor44);
Assert.Equal(rowMajor, matrix44.Matrix.Values);
}
[SkippableFact]
public void TransformConvertsToMatrix()
{
var matrix44 = SKMatrix44.CreateRotationDegrees(0, 0, 1, 45);
var matrix = SKMatrix.CreateRotationDegrees(45);
Assert.Equal(matrix.Values, matrix44.Matrix.Values);
}
[SkippableFact]
public void ImplicitFromMatrix()
{
var matrix = SKMatrix.CreateRotationDegrees(45);
var matrix44 = (SKMatrix44)matrix;
Assert.Equal(matrix.Values, matrix44.Matrix.Values);
}
[SkippableFact]
public void ImplicitFromRotationScale()
{
var rs = SKRotationScaleMatrix.CreateRotationDegrees(45, 0, 0);
var matrix = SKMatrix.CreateRotationDegrees(45);
var matrix44 = (SKMatrix44)rs.ToMatrix();
Assert.Equal(matrix.Values, matrix44.Matrix.Values);
}
[SkippableFact]
public void TranslationMapsScalars()
{
var matrixTranslate = SKMatrix44.CreateTranslation(10, 20, 0);
var resultTranslateZero = matrixTranslate.MapScalars(0, 0, 0, 1);
var resultTranslateValue = matrixTranslate.MapScalars(5, 25, 0, 1);
Assert.Equal(new[] { 10f, 20f, 0f, 1f }, resultTranslateZero);
Assert.Equal(new[] { 15f, 45f, 0f, 1f }, resultTranslateValue);
}
[SkippableFact]
public void RotationMapsScalars()
{
var matrixRotate = SKMatrix44.CreateRotationDegrees(0, 1, 0, 90);
var resultRotateZero = matrixRotate.MapScalars(0, 0, 0, 1);
var resultRotateValue = matrixRotate.MapScalars(5, 25, 0, 1);
Assert.Equal(new[] { 0f, 0f, 0f, 1f }, resultRotateZero);
AssertSimilar(new[] { 0f, 25f, -5f, 1f }, resultRotateValue, PRECISION);
}
[SkippableFact]
public void TranslationMapsPoints()
{
var matrixTranslate = SKMatrix44.CreateTranslation(10, 20, 0);
var resultTranslateZero = matrixTranslate.MapPoint(SKPoint.Empty);
var resultTranslateValue = matrixTranslate.MapPoint(new SKPoint(5, 25));
Assert.Equal(new SKPoint(10f, 20f), resultTranslateZero);
Assert.Equal(new SKPoint(15f, 45f), resultTranslateValue);
}
[SkippableFact]
public void RotationMapsPoints()
{
var matrixRotate = SKMatrix44.CreateRotationDegrees(0, 1, 0, 90);
var resultRotateZero = matrixRotate.MapPoint(SKPoint.Empty);
var resultRotateValue = matrixRotate.MapPoint(new SKPoint(5, 25));
Assert.Equal(new SKPoint(0f, 0f), resultRotateZero);
Assert.Equal(0, resultRotateValue.X, PRECISION);
Assert.Equal(25, resultRotateValue.Y, PRECISION);
}
[SkippableFact]
public void MapsPointsBulk()
{
var rnd = new Random();
var matrixTranslate = SKMatrix44.CreateTranslation(10, 25, 0);
// generate some points
var points = new SKPoint[1000];
var results = new SKPoint[points.Length];
for (var i = 0; i < points.Length; i++)
{
points[i] = new SKPoint(rnd.Next(1000) / 10f, rnd.Next(1000) / 10f);
results[i] = new SKPoint(points[i].X + 10, points[i].Y + 25);
}
var actualResults = matrixTranslate.MapPoints(points);
Assert.Equal(results, actualResults);
}
}
}

Просмотреть файл

@ -7,144 +7,6 @@ namespace SkiaSharp.Tests
{
public class SKMatrixTest : SKTest
{
private const float EPSILON = 0.0001f;
private const int PRECISION = 4;
[SkippableFact]
public void Matrix44CreatesIdentity()
{
var matrix = SKMatrix44.CreateIdentity();
var expectedRowMajor = new float[] {
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
};
var rowMajor = matrix.ToRowMajor();
Assert.Equal(expectedRowMajor, rowMajor);
}
[SkippableFact]
public void Matrix44Inverts()
{
var rowMajor = new float[] {
1, 2, 3, 0,
0, 1, 4, 0,
5, 6, 1, 0,
0, 0, 0, 1,
};
var expectedRowMajor = new float[] {
-11.5f, 8, 2.5f, 0,
10, -7, -2, 0,
-2.5f, 2, 0.5f, 0,
0, 0, 0, 1,
};
var determinant = 2f;
var matrix = SKMatrix44.FromRowMajor(rowMajor);
Assert.Equal(rowMajor, matrix.ToRowMajor());
Assert.Equal(determinant, matrix.Determinant());
var inverted = matrix.Invert();
Assert.Equal(1f / determinant, inverted.Determinant());
var actualRowMajor = inverted.ToRowMajor();
Assert.Equal(expectedRowMajor, actualRowMajor);
}
[SkippableFact]
public void Matrix44ConvertsToMatrix()
{
var rowMajor44 = new float[] {
2, 3, 4, 5,
4, 6, 8, 10,
6, 9, 12, 15,
8, 12, 16, 20,
};
var rowMajor = new float[] {
rowMajor44[0], rowMajor44[1], rowMajor44[3],
rowMajor44[4], rowMajor44[5], rowMajor44[7],
rowMajor44[12], rowMajor44[13], rowMajor44[15],
};
var matrix44 = SKMatrix44.FromRowMajor(rowMajor44);
Assert.Equal(rowMajor, matrix44.Matrix.Values);
matrix44 = SKMatrix44.CreateRotationDegrees(0, 0, 1, 45);
Assert.Equal(SKMatrix.MakeRotationDegrees(45).Values, matrix44.Matrix.Values);
}
[SkippableFact]
public void Matrix44MapsScalars()
{
// translate
var matrixTranslate = SKMatrix44.CreateTranslate(10, 20, 0);
var resultTranslateZero = matrixTranslate.MapScalars(0, 0, 0, 1);
var resultTranslateValue = matrixTranslate.MapScalars(5, 25, 0, 1);
Assert.Equal(new[] { 10f, 20f, 0f, 1f }, resultTranslateZero);
Assert.Equal(new[] { 15f, 45f, 0f, 1f }, resultTranslateValue);
// rotate
var matrixRotate = SKMatrix44.CreateRotationDegrees(0, 1, 0, 90);
var resultRotateZero = matrixRotate.MapScalars(0, 0, 0, 1);
var resultRotateValue = matrixRotate.MapScalars(5, 25, 0, 1);
Assert.Equal(new[] { 0f, 0f, 0f, 1f }, resultRotateZero);
Assert.Equal(new[] { 0f, 25f, -5f, 1f }, resultRotateValue.Select(v => (int)(v / EPSILON) * EPSILON));
}
[SkippableFact]
public void Matrix44MapsPoints()
{
// translate
var matrixTranslate = SKMatrix44.CreateTranslate(10, 20, 0);
var resultTranslateZero = matrixTranslate.MapPoint(SKPoint.Empty);
var resultTranslateValue = matrixTranslate.MapPoint(new SKPoint(5, 25));
Assert.Equal(new SKPoint(10f, 20f), resultTranslateZero);
Assert.Equal(new SKPoint(15f, 45f), resultTranslateValue);
// rotate
var matrixRotate = SKMatrix44.CreateRotationDegrees(0, 1, 0, 90);
var resultRotateZero = matrixRotate.MapPoint(SKPoint.Empty);
var resultRotateValue = matrixRotate.MapPoint(new SKPoint(5, 25));
Assert.Equal(new SKPoint(0f, 0f), resultRotateZero);
Assert.Equal(0, resultRotateValue.X, PRECISION);
Assert.Equal(25, resultRotateValue.Y, PRECISION);
}
[SkippableFact]
public void Matrix44MapsPointsBulk()
{
var rnd = new Random();
var matrixTranslate = SKMatrix44.CreateTranslate(10, 25, 0);
// generate some points
var points = new SKPoint[1000];
var results = new SKPoint[points.Length];
for (int i = 0; i < points.Length; i++)
{
points[i] = new SKPoint(rnd.Next(1000) / 10f, rnd.Next(1000) / 10f);
results[i] = new SKPoint(points[i].X + 10, points[i].Y + 25);
}
var actualResults = matrixTranslate.MapPoints(points);
Assert.Equal(results, actualResults);
}
[SkippableFact]
public void MatrixCanInvert()
{
@ -160,7 +22,7 @@ namespace SkiaSharp.Tests
var b = SKMatrix.MakeTranslation(5, 7);
var c = SKMatrix.Concat(a, b);
Assert.Equal(SKMatrix.MakeTranslation(15, 27).Values, c.Values);
}
@ -171,7 +33,7 @@ namespace SkiaSharp.Tests
var b = SKMatrix.MakeTranslation(5, 7);
var c = a.PreConcat(b);
Assert.Equal(SKMatrix.MakeTranslation(15, 27).Values, c.Values);
}
@ -182,37 +44,10 @@ namespace SkiaSharp.Tests
var b = SKMatrix.MakeTranslation(5, 7);
var c = a.PostConcat(b);
Assert.Equal(SKMatrix.MakeTranslation(15, 27).Values, c.Values);
}
[SkippableFact]
public void SKRotationScaleMatrixTranslationToMatrixIsCorrect()
{
var m = SKMatrix.MakeTranslation(5, 7);
var rsm = SKRotationScaleMatrix.CreateTranslation(5, 7).ToMatrix();
Assert.Equal(m.Values, rsm.Values);
}
[SkippableFact]
public void SKRotationScaleMatrixRotationToMatrixIsCorrect()
{
var m = SKMatrix.MakeRotationDegrees(45);
var rsm = SKRotationScaleMatrix.CreateRotationDegrees(45, 0, 0).ToMatrix();
Assert.Equal(m.Values, rsm.Values);
}
[SkippableFact]
public void SKRotationScaleMatrixScaleToMatrixIsCorrect()
{
var m = SKMatrix.MakeScale(3.5f, 3.5f);
var rsm = SKRotationScaleMatrix.CreateScale(3.5f).ToMatrix();
Assert.Equal(m.Values, rsm.Values);
}
[SkippableFact]
public void MatrixMapsPoints()
{

Просмотреть файл

@ -0,0 +1,34 @@
using Xunit;
namespace SkiaSharp.Tests
{
public class SKRotationScaleMatrixTest : SKTest
{
[SkippableFact]
public void TranslationToMatrixIsCorrect()
{
var m = SKMatrix.CreateTranslation(5, 7);
var rsm = SKRotationScaleMatrix.CreateTranslation(5, 7).ToMatrix();
Assert.Equal(m.Values, rsm.Values);
}
[SkippableFact]
public void RotationToMatrixIsCorrect()
{
var m = SKMatrix.CreateRotationDegrees(45);
var rsm = SKRotationScaleMatrix.CreateRotationDegrees(45, 0, 0).ToMatrix();
Assert.Equal(m.Values, rsm.Values);
}
[SkippableFact]
public void ScaleToMatrixIsCorrect()
{
var m = SKMatrix.CreateScale(3.5f, 3.5f);
var rsm = SKRotationScaleMatrix.CreateScale(3.5f).ToMatrix();
Assert.Equal(m.Values, rsm.Values);
}
}
}

Просмотреть файл

@ -1,24 +1,11 @@
#if NET_STANDARD
#else
#define SYSTEM_DRAWING
#endif
using System;
using System;
using System.Runtime.InteropServices;
using Xunit;
#if SYSTEM_DRAWING
using System.Drawing;
using System.Drawing.Imaging;
#endif
namespace SkiaSharp.Tests
{
public class SKSurfaceTest : SKTest
{
private const int width = 100;
private const int height = 100;
private void DrawGpuSurface(Action<SKSurface, SKImageInfo> draw)
{
using (var ctx = CreateGlContext())
@ -485,60 +472,5 @@ namespace SkiaSharp.Tests
}
});
}
#if SYSTEM_DRAWING
private void DrawBitmap(Action<SKSurface, BitmapData> draw)
{
using (var bitmap = new Bitmap(width, height, PixelFormat.Format32bppPArgb))
{
var data = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, bitmap.PixelFormat);
var info = new SKImageInfo(width, height, SKImageInfo.PlatformColorType, SKAlphaType.Premul);
using (var surface = SKSurface.Create(info, data.Scan0, data.Stride))
{
Assert.NotNull(surface);
draw(surface, data);
}
bitmap.UnlockBits(data);
}
}
[SkippableFact]
public void SurfaceCanvasReturnTheSameInstance()
{
DrawBitmap((surface, data) =>
{
var skcanvas1 = surface.Canvas;
var skcanvas2 = surface.Canvas;
Assert.NotNull(skcanvas1);
Assert.NotNull(skcanvas2);
Assert.Equal(skcanvas1, skcanvas2);
Assert.True(skcanvas1 == skcanvas2);
Assert.Same(skcanvas1, skcanvas2);
});
}
[SkippableFact]
public void SecondSurfaceWasCreatedDifferent()
{
DrawBitmap((surface, data) =>
{
var info = new SKImageInfo(width, height, SKImageInfo.PlatformColorType, SKAlphaType.Premul);
var surface2 = SKSurface.Create(info, data.Scan0, data.Stride);
Assert.NotNull(surface2);
Assert.NotEqual(surface, surface2);
Assert.NotEqual(surface.Handle, surface2.Handle);
surface2.Dispose();
});
}
#endif
}
}

Просмотреть файл

@ -1,11 +1,15 @@
using System;
using System.IO;
using System.Linq;
using Xunit;
namespace SkiaSharp.Tests
{
public abstract class SKTest : BaseTest, IAssemblyFixture<GarbageCleanupFixture>
{
protected const float EPSILON = 0.0001f;
protected const int PRECISION = 4;
private static readonly Random random = new Random();
protected static Stream CreateTestStream(int length = 1024)
@ -15,6 +19,13 @@ namespace SkiaSharp.Tests
return new MemoryStream(bytes);
}
protected static byte[] CreateTestData(int length = 1024)
{
var bytes = new byte[length];
random.NextBytes(bytes);
return bytes;
}
protected static SKStreamAsset CreateTestSKStream(int length = 1024)
{
var bytes = new byte[length];
@ -92,6 +103,17 @@ namespace SkiaSharp.Tests
Assert.Equal(SKColors.Yellow.WithAlpha(alpha), pix.GetPixelColor(30, 30));
}
protected static void AssertSimilar(ReadOnlySpan<float> expected, ReadOnlySpan<float> actual, int precision = PRECISION)
{
var eTrimmed = expected.ToArray()
.Select(v => (int)(v * precision) / precision);
var aTrimmed = actual.ToArray()
.Select(v => (int)(v * precision) / precision);
Assert.Equal(eTrimmed, aTrimmed);
}
protected GlContext CreateGlContext()
{
try

Просмотреть файл

@ -216,6 +216,25 @@ namespace SkiaSharp.Tests
var typeface = SKFontManager.Default.MatchCharacter(emojiChar);
Assert.NotNull(typeface);
var count = typeface.CountGlyphs(text);
var glyphs = typeface.GetGlyphs(text);
Assert.True(count > 0);
Assert.True(glyphs.Length > 0);
Assert.DoesNotContain((ushort)0, glyphs);
}
[Obsolete]
[Trait(CategoryKey, MatchCharacterCategory)]
[SkippableFact]
public void UnicodeGlyphsReturnsTheCorrectNumberOfCharactersObsolete()
{
const string text = "🚀";
var emojiChar = StringUtilities.GetUnicodeCharacterCode(text, SKTextEncoding.Utf32);
var typeface = SKFontManager.Default.MatchCharacter(emojiChar);
Assert.NotNull(typeface);
Assert.True(typeface.CountGlyphs(text) > 0);
Assert.True(typeface.CountGlyphs(text, SKEncoding.Utf32) > 0);
Assert.True(typeface.GetGlyphs(text).Length > 0);

Просмотреть файл

@ -0,0 +1,67 @@
#if SYSTEM_DRAWING
using System;
using System.Drawing;
using System.Drawing.Imaging;
using Xunit;
namespace SkiaSharp.Tests
{
public class SystemDrawingTest : SKTest
{
private void DrawBitmap(Action<SKSurface, BitmapData> draw)
{
using (var bitmap = new Bitmap(100, 100, PixelFormat.Format32bppPArgb))
{
var data = bitmap.LockBits(new Rectangle(0, 0, 100, 100), ImageLockMode.WriteOnly, bitmap.PixelFormat);
var info = new SKImageInfo(100, 100, SKImageInfo.PlatformColorType, SKAlphaType.Premul);
using (var surface = SKSurface.Create(info, data.Scan0, data.Stride))
{
Assert.NotNull(surface);
draw(surface, data);
}
bitmap.UnlockBits(data);
}
}
[SkippableFact]
public void SurfaceCanvasReturnTheSameInstance()
{
DrawBitmap((surface, data) =>
{
var skcanvas1 = surface.Canvas;
var skcanvas2 = surface.Canvas;
Assert.NotNull(skcanvas1);
Assert.NotNull(skcanvas2);
Assert.Equal(skcanvas1, skcanvas2);
Assert.True(skcanvas1 == skcanvas2);
Assert.Same(skcanvas1, skcanvas2);
});
}
[SkippableFact]
public void SecondSurfaceWasCreatedDifferent()
{
DrawBitmap((surface, data) =>
{
var info = new SKImageInfo(100, 100, SKImageInfo.PlatformColorType, SKAlphaType.Premul);
var surface2 = SKSurface.Create(info, data.Scan0, data.Stride);
Assert.NotNull(surface2);
Assert.NotEqual(surface, surface2);
Assert.NotEqual(surface.Handle, surface2.Handle);
surface2.Dispose();
});
}
}
}
#endif

Просмотреть файл

@ -6,6 +6,8 @@ This directory contains a few tools to help with the binding and development of
This is a small set of tools that help with generating the p/invoke layer from the C header files.
### Generate
This can be run with:
```pwsh
@ -18,3 +20,18 @@ dotnet run --project=utils/SkiaSharpGenerator/SkiaSharpGenerator.csproj -- gener
The path to the root of the skia source.
* `--output binding/Binding/SkiaApi.generated.cs`
The path to the generated file.
### Verify
This can be run with:
```pwsh
dotnet run --project=utils/SkiaSharpGenerator/SkiaSharpGenerator.csproj -- verify --config binding/libSkiaSharp.json --skia externals/skia
```
* `--config binding/libSkiaSharp.json`
The path to the JSON file that help generate a useful set of p/invoke definions and structures.
* `--skia externals/skia`
The path to the root of the skia source.
* `--output binding/Binding/SkiaApi.generated.cs`
The path to the generated file.

Просмотреть файл

@ -0,0 +1,214 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using System.Threading.Tasks;
using CppAst;
namespace SkiaSharpGenerator
{
public abstract class BaseTool
{
protected readonly Dictionary<string, TypeMapping> typeMappings = new Dictionary<string, TypeMapping>();
protected readonly Dictionary<string, FunctionMapping> functionMappings = new Dictionary<string, FunctionMapping>();
protected readonly Dictionary<string, bool> skiaTypes = new Dictionary<string, bool>();
protected CppCompilation compilation = new CppCompilation();
protected Config config = new Config();
protected BaseTool(string skiaRoot, string configFile)
{
SkiaRoot = skiaRoot ?? throw new ArgumentNullException(nameof(skiaRoot));
ConfigFile = configFile ?? throw new ArgumentNullException(nameof(configFile));
}
public ILogger? Log { get; set; }
public string SkiaRoot { get; }
public string ConfigFile { get; }
public bool HasErrors =>
compilation?.Diagnostics?.HasErrors ?? false;
public IEnumerable<CppDiagnosticMessage> Messages =>
compilation?.Diagnostics?.Messages ?? Array.Empty<CppDiagnosticMessage>();
protected void ParseSkiaHeaders()
{
Log?.LogVerbose("Parsing skia headers...");
var options = new CppParserOptions();
foreach (var header in config.IncludeDirs)
{
var path = Path.Combine(SkiaRoot, header);
options.IncludeFolders.Add(path);
}
var headers = new List<string>();
foreach (var header in config.Headers)
{
var path = Path.Combine(SkiaRoot, header.Key);
options.IncludeFolders.Add(path);
foreach (var filter in header.Value)
{
headers.AddRange(Directory.EnumerateFiles(path, filter));
}
}
compilation = CppParser.ParseFiles(headers, options);
if (compilation == null || compilation.HasErrors)
{
Log?.LogError("Parsing headers failed.");
throw new Exception("Parsing headers failed.");
}
}
protected void LoadStandardMappings()
{
Log?.LogVerbose("Loading standard types...");
var standardMappings = new Dictionary<string, string>
{
// stdint.h types:
{ "uint8_t", nameof(Byte) },
{ "uint16_t", nameof(UInt16) },
{ "uint32_t", nameof(UInt32) },
{ "uint64_t", nameof(UInt64) },
{ "usize_t" , "/* usize_t */ " + nameof(UIntPtr) },
{ "uintptr_t" , nameof(UIntPtr) },
{ "int8_t", nameof(SByte) },
{ "int16_t", nameof(Int16) },
{ "int32_t", nameof(Int32) },
{ "int64_t", nameof(Int64) },
{ "size_t" , "/* size_t */ " + nameof(IntPtr) },
{ "intptr_t" , nameof(IntPtr) },
// standard types:
{ "bool", nameof(Byte) },
{ "char", "/* char */ void" },
{ "unsigned char", "/* unsigned char */ void" },
{ "signed char", "/* signed char */ void" },
{ "short", nameof(Int16) },
{ "short int", nameof(Int16) },
{ "signed short", nameof(Int16) },
{ "signed short int", nameof(Int16) },
{ "unsigned short", nameof(UInt16) },
{ "unsigned short int", nameof(UInt16) },
{ "int", nameof(Int32) },
{ "signed", nameof(Int32) },
{ "signed int", nameof(Int32) },
{ "unsigned", nameof(UInt32) },
{ "unsigned int", nameof(UInt32) },
{ "long", nameof(Int64) },
{ "long int", nameof(Int64) },
{ "signed long", nameof(Int64) },
{ "signed long int", nameof(Int64) },
{ "unsigned long", nameof(UInt64) },
{ "unsigned long int", nameof(UInt64) },
{ "float", nameof(Single) },
{ "double", nameof(Double) },
// TODO: long double, wchar_t ?
{ "void", "void" },
};
foreach (var mapping in standardMappings)
{
var map = new TypeMapping { CsType = mapping.Value };
typeMappings[mapping.Key] = map;
}
}
protected void UpdatingMappings()
{
// load all the classes/structs
var typedefs = compilation.Classes;
foreach (var klass in typedefs)
{
typeMappings[klass.GetDisplayName()] = new TypeMapping();
}
// load all the enums
var enums = compilation.Enums;
foreach (var enm in enums)
{
typeMappings[enm.GetDisplayName()] = new TypeMapping();
}
// load the mapping file
foreach (var mapping in config.Mappings.Types)
{
typeMappings[mapping.Key] = mapping.Value;
}
foreach (var mapping in config.Mappings.Functions)
{
functionMappings[mapping.Key] = mapping.Value;
}
}
protected async Task<Config> LoadConfigAsync(string configPath)
{
Log?.LogVerbose("Loading configuration...");
using var configJson = File.OpenRead(configPath);
return await JsonSerializer.DeserializeAsync<Config>(configJson, new JsonSerializerOptions
{
AllowTrailingCommas = true,
ReadCommentHandling = JsonCommentHandling.Skip,
});
}
protected string GetType(CppType type)
{
var typeName = GetCppType(type);
// split the type from the pointers
var pointerIndex = typeName.IndexOf("*");
var pointers = pointerIndex == -1 ? "" : typeName.Substring(pointerIndex);
var noPointers = pointerIndex == -1 ? typeName : typeName.Substring(0, pointerIndex);
if (skiaTypes.TryGetValue(noPointers, out var isStruct))
{
if (!isStruct)
return noPointers + pointers.Substring(1);
if (typeMappings.TryGetValue(noPointers, out var map))
return (map.CsType ?? Utils.CleanName(noPointers)) + pointers;
}
else
{
if (typeMappings.TryGetValue(typeName, out var map))
return map.CsType ?? Utils.CleanName(typeName);
if (typeMappings.TryGetValue(noPointers, out map))
return (map.CsType ?? Utils.CleanName(noPointers)) + pointers;
if (functionMappings.TryGetValue(typeName, out var funcMap))
return funcMap.CsType ?? Utils.CleanName(typeName);
if (functionMappings.TryGetValue(noPointers, out funcMap))
return (funcMap.CsType ?? Utils.CleanName(noPointers)) + pointers;
}
return Utils.CleanName(typeName);
}
protected static string GetCppType(CppType type)
{
var typeName = type.GetDisplayName();
// remove the const
typeName = typeName.Replace("const ", "");
// replace the [] with a *
int start;
while ((start = typeName.IndexOf("[")) != -1)
{
var end = typeName.IndexOf("]");
typeName = typeName.Substring(0, start) + "*" + typeName.Substring(end + 1);
}
return typeName;
}
}
}

Просмотреть файл

@ -11,6 +11,9 @@ namespace SkiaSharpGenerator
[JsonPropertyName("headers")]
public Dictionary<string, string[]> Headers { get; set; } = new Dictionary<string, string[]>();
[JsonPropertyName("source")]
public Dictionary<string, string[]> Source { get; set; } = new Dictionary<string, string[]>();
[JsonPropertyName("includeDirs")]
public List<string> IncludeDirs { get; set; } = new List<string>();

Просмотреть файл

@ -2,42 +2,21 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using CppAst;
namespace SkiaSharpGenerator
{
public class Generator
public class Generator : BaseTool
{
private readonly Dictionary<string, TypeMapping> typeMappings = new Dictionary<string, TypeMapping>();
private readonly Dictionary<string, FunctionMapping> functionMappings = new Dictionary<string, FunctionMapping>();
private readonly Dictionary<string, bool> skiaTypes = new Dictionary<string, bool>();
private CppCompilation compilation = new CppCompilation();
private Config config = new Config();
public Generator(string skiaRoot, string configFile, TextWriter outputWriter)
: base(skiaRoot, configFile)
{
SkiaRoot = skiaRoot ?? throw new ArgumentNullException(nameof(skiaRoot));
ConfigFile = configFile ?? throw new ArgumentNullException(nameof(configFile));
OutputWriter = outputWriter ?? throw new ArgumentNullException(nameof(outputWriter));
}
public ILogger? Log { get; set; }
public string SkiaRoot { get; }
public string ConfigFile { get; }
public TextWriter OutputWriter { get; }
public bool HasErrors =>
compilation?.Diagnostics?.HasErrors ?? false;
public IEnumerable<CppDiagnosticMessage> Messages =>
compilation?.Diagnostics?.Messages ?? Array.Empty<CppDiagnosticMessage>();
public async Task GenerateAsync()
{
Log?.Log("Starting C# API generation...");
@ -55,134 +34,6 @@ namespace SkiaSharpGenerator
Log?.Log("C# API generation complete.");
}
private void ParseSkiaHeaders()
{
Log?.LogVerbose("Parsing skia headers...");
var options = new CppParserOptions();
foreach (var header in config.IncludeDirs)
{
var path = Path.Combine(SkiaRoot, header);
options.IncludeFolders.Add(path);
}
var headers = new List<string>();
foreach (var header in config.Headers)
{
var path = Path.Combine(SkiaRoot, header.Key);
options.IncludeFolders.Add(path);
foreach (var filter in header.Value)
{
headers.AddRange(Directory.EnumerateFiles(path, filter));
}
}
compilation = CppParser.ParseFiles(headers, options);
if (compilation == null || compilation.HasErrors)
{
Log?.LogError("Parsing headers failed.");
throw new Exception("Parsing headers failed.");
}
}
private void LoadStandardMappings()
{
Log?.LogVerbose("Loading standard types...");
var standardMappings = new Dictionary<string, string>
{
// stdint.h types:
{ "uint8_t", nameof(Byte) },
{ "uint16_t", nameof(UInt16) },
{ "uint32_t", nameof(UInt32) },
{ "uint64_t", nameof(UInt64) },
{ "usize_t" , "/* usize_t */ " + nameof(UIntPtr) },
{ "uintptr_t" , nameof(UIntPtr) },
{ "int8_t", nameof(SByte) },
{ "int16_t", nameof(Int16) },
{ "int32_t", nameof(Int32) },
{ "int64_t", nameof(Int64) },
{ "size_t" , "/* size_t */ " + nameof(IntPtr) },
{ "intptr_t" , nameof(IntPtr) },
// standard types:
{ "bool", nameof(Byte) },
{ "char", "/* char */ void" },
{ "unsigned char", "/* unsigned char */ void" },
{ "signed char", "/* signed char */ void" },
{ "short", nameof(Int16) },
{ "short int", nameof(Int16) },
{ "signed short", nameof(Int16) },
{ "signed short int", nameof(Int16) },
{ "unsigned short", nameof(UInt16) },
{ "unsigned short int", nameof(UInt16) },
{ "int", nameof(Int32) },
{ "signed", nameof(Int32) },
{ "signed int", nameof(Int32) },
{ "unsigned", nameof(UInt32) },
{ "unsigned int", nameof(UInt32) },
{ "long", nameof(Int64) },
{ "long int", nameof(Int64) },
{ "signed long", nameof(Int64) },
{ "signed long int", nameof(Int64) },
{ "unsigned long", nameof(UInt64) },
{ "unsigned long int", nameof(UInt64) },
{ "float", nameof(Single) },
{ "double", nameof(Double) },
// TODO: long double, wchar_t ?
{ "void", "void" },
};
foreach (var mapping in standardMappings)
{
var map = new TypeMapping { CsType = mapping.Value };
typeMappings[mapping.Key] = map;
}
}
private void UpdatingMappings()
{
// load all the classes/structs
var typedefs = compilation.Classes;
foreach (var klass in typedefs)
{
typeMappings[klass.GetDisplayName()] = new TypeMapping();
}
// load all the enums
var enums = compilation.Enums;
foreach (var enm in enums)
{
typeMappings[enm.GetDisplayName()] = new TypeMapping();
}
// load the mapping file
foreach (var mapping in config.Mappings.Types)
{
typeMappings[mapping.Key] = mapping.Value;
}
foreach (var mapping in config.Mappings.Functions)
{
functionMappings[mapping.Key] = mapping.Value;
}
}
private async Task<Config> LoadConfigAsync(string configPath)
{
Log?.LogVerbose("Loading configuration...");
using var configJson = File.OpenRead(configPath);
return await JsonSerializer.DeserializeAsync<Config>(configJson, new JsonSerializerOptions
{
AllowTrailingCommas = true,
ReadCommentHandling = JsonCommentHandling.Skip,
});
}
private void WriteApi(TextWriter writer)
{
Log?.LogVerbose("Writing C# API...");
@ -520,54 +371,5 @@ namespace SkiaSharpGenerator
writer.WriteLine();
}
}
private string GetType(CppType type)
{
var typeName = GetCppType(type);
// split the type from the pointers
var pointerIndex = typeName.IndexOf("*");
var pointers = pointerIndex == -1 ? "" : typeName.Substring(pointerIndex);
var noPointers = pointerIndex == -1 ? typeName : typeName.Substring(0, pointerIndex);
if (skiaTypes.TryGetValue(noPointers, out var isStruct))
{
if (!isStruct)
return noPointers + pointers.Substring(1);
if (typeMappings.TryGetValue(noPointers, out var map))
return (map.CsType ?? Utils.CleanName(noPointers)) + pointers;
}
else
{
if (typeMappings.TryGetValue(typeName, out var map))
return map.CsType ?? Utils.CleanName(typeName);
if (typeMappings.TryGetValue(noPointers, out map))
return (map.CsType ?? Utils.CleanName(noPointers)) + pointers;
if (functionMappings.TryGetValue(typeName, out var funcMap))
return funcMap.CsType ?? Utils.CleanName(typeName);
if (functionMappings.TryGetValue(noPointers, out funcMap))
return (funcMap.CsType ?? Utils.CleanName(noPointers)) + pointers;
}
return Utils.CleanName(typeName);
}
private static string GetCppType(CppType type)
{
var typeName = type.GetDisplayName();
// remove the const
typeName = typeName.Replace("const ", "");
// replace the [] with a *
int start;
while ((start = typeName.IndexOf("[")) != -1)
{
var end = typeName.IndexOf("]");
typeName = typeName.Substring(0, start) + "*" + typeName.Substring(end + 1);
}
return typeName;
}
}
}

Просмотреть файл

@ -21,6 +21,7 @@ namespace SkiaSharpGenerator
"",
"Available commands:",
new GenerateCommand(),
new VerifyCommand(),
};
return commands.Run(args);
}

Просмотреть файл

@ -0,0 +1,89 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using CppAst;
namespace SkiaSharpGenerator
{
public class Verifier : BaseTool
{
private CppCompilation sourceCompilation = new CppCompilation();
public Verifier(string skiaRoot, string configFile)
: base(skiaRoot, configFile)
{
}
public async Task VerifyAsync()
{
Log?.Log("Starting C API verification...");
config = await LoadConfigAsync(ConfigFile);
ParseSkiaHeaders();
Verify();
Log?.Log("C API verification complete.");
}
private void Verify()
{
Log?.LogVerbose("Verifying C API...");
VerifyImplementations();
}
private void VerifyImplementations()
{
Log?.LogVerbose(" Making sure all declarations have an implementation...");
var functionGroups = compilation.Functions
.OrderBy(f => f.Name)
.GroupBy(f => f.Span.Start.File.ToLower().Replace("\\", "/"))
.OrderBy(g => Path.GetDirectoryName(g.Key) + "/" + Path.GetFileName(g.Key));
var allSources = new List<string>();
foreach (var source in config.Source)
{
var path = Path.Combine(SkiaRoot, source.Key);
foreach (var filter in source.Value)
{
allSources.AddRange(Directory.EnumerateFiles(path, filter));
}
}
var sourcesContents = new Dictionary<string, string>();
foreach (var group in functionGroups)
{
foreach (var function in group)
{
Log?.LogVerbose($" {function.Name}");
var found = false;
foreach (var source in allSources)
{
if (!sourcesContents.TryGetValue(source, out var contents))
{
contents = File.ReadAllText(source);
sourcesContents[source] = contents;
}
if (Regex.IsMatch(contents, $"\\s{function.Name}\\s*\\("))
{
found = true;
break;
}
}
if (!found)
Log?.LogWarning($"Missing implementation for {function}");
}
}
}
}
}

Просмотреть файл

@ -0,0 +1,80 @@
using System.Collections.Generic;
using System.IO;
using CppAst;
using Mono.Options;
namespace SkiaSharpGenerator
{
public class VerifyCommand : BaseCommand
{
public VerifyCommand()
: base("verify", "Verify the C definitions have implementations.")
{
}
public string? SourceRoot { get; set; }
public string? ConfigPath { get; set; }
protected override OptionSet OnCreateOptions() => new OptionSet
{
{ "s|skia=", "The root of the skia source", v => SourceRoot = v },
{ "c|config=", "The config file path", v => ConfigPath = v },
};
protected override bool OnValidateArguments(IEnumerable<string> extras)
{
var hasError = false;
if (string.IsNullOrEmpty(SourceRoot))
{
Program.Log.LogError($"{Program.Name}: Path to the skia source was not provided: `--skia=<path-to-skia>`.");
hasError = true;
}
else if (!Directory.Exists(SourceRoot))
{
Program.Log.LogError($"{Program.Name}: Path to the skia source does not exist: `{SourceRoot}`.");
hasError = true;
}
if (string.IsNullOrEmpty(ConfigPath))
{
Program.Log.LogError($"{Program.Name}: Path to config file was not provided: `--config=<path-to-config-json>`.");
hasError = true;
}
else if (!File.Exists(ConfigPath))
{
Program.Log.LogError($"{Program.Name}: Path to config file does not exist: `{ConfigPath}`.");
hasError = true;
}
return !hasError;
}
protected override bool OnInvoke(IEnumerable<string> extras)
{
var verifier = new Verifier(SourceRoot!, ConfigPath!);
verifier.Log = Program.Log;
try
{
verifier.VerifyAsync().Wait();
}
catch
{
if (verifier.HasErrors)
{
foreach (var dgn in verifier.Messages)
{
if (dgn.Type == CppLogMessageType.Error)
Program.Log.LogError($"{dgn.Text} at {dgn.Location}");
}
}
throw;
}
return true;
}
}
}