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:
Родитель
a38b665127
Коммит
fc9f030a08
|
@ -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, ¢er, &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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче