From 22032a69ab00f3a08881650b75dd8ae5928494b3 Mon Sep 17 00:00:00 2001 From: Matthew Leibowitz Date: Mon, 17 Dec 2018 23:50:29 +0200 Subject: [PATCH] Support the case of measuring empty strings. Fixes #734 - previous logic: did not check for nulls, the update did - new logic: still check for nulls, but now only if we have indicated that there _should_ be text (there is a length) - added overloads for text pointers that accept an int as opposed to just an IntPtr --- binding/Binding/SKCanvas.cs | 6 +- binding/Binding/SKPaint.cs | 138 ++++++++++++++++++++++------------ binding/Binding/SKTypeface.cs | 4 +- tests/Tests/SKCanvasTest.cs | 44 +++++++++++ tests/Tests/SKPaintTest.cs | 74 ++++++++++++++++++ 5 files changed, 214 insertions(+), 52 deletions(-) diff --git a/binding/Binding/SKCanvas.cs b/binding/Binding/SKCanvas.cs index 1df39b82..e9abd6c4 100644 --- a/binding/Binding/SKCanvas.cs +++ b/binding/Binding/SKCanvas.cs @@ -516,7 +516,7 @@ namespace SkiaSharp public void DrawTextOnPath (IntPtr buffer, int length, SKPath path, float hOffset, float vOffset, SKPaint paint) { - if (buffer == IntPtr.Zero) + if (buffer == IntPtr.Zero && length != 0) throw new ArgumentNullException (nameof (buffer)); if (paint == null) throw new ArgumentNullException (nameof (paint)); @@ -533,7 +533,7 @@ namespace SkiaSharp public void DrawText (IntPtr buffer, int length, float x, float y, SKPaint paint) { - if (buffer == IntPtr.Zero) + if (buffer == IntPtr.Zero && length != 0) throw new ArgumentNullException (nameof (buffer)); if (paint == null) throw new ArgumentNullException (nameof (paint)); @@ -543,7 +543,7 @@ namespace SkiaSharp public void DrawPositionedText (IntPtr buffer, int length, SKPoint[] points, SKPaint paint) { - if (buffer == IntPtr.Zero) + if (buffer == IntPtr.Zero && length != 0) throw new ArgumentNullException (nameof (buffer)); if (paint == null) throw new ArgumentNullException (nameof (paint)); diff --git a/binding/Binding/SKPaint.cs b/binding/Binding/SKPaint.cs index b1c338e5..65229d1a 100644 --- a/binding/Binding/SKPaint.cs +++ b/binding/Binding/SKPaint.cs @@ -221,9 +221,12 @@ namespace SkiaSharp } } + public float MeasureText (IntPtr buffer, int length) => + MeasureText (buffer, (IntPtr)length); + public float MeasureText (IntPtr buffer, IntPtr length) { - if (buffer == IntPtr.Zero) + if (buffer == IntPtr.Zero && length != IntPtr.Zero) throw new ArgumentNullException (nameof (buffer)); return SkiaApi.sk_paint_measure_text (Handle, buffer, length, IntPtr.Zero); @@ -250,9 +253,12 @@ namespace SkiaSharp } } + public float MeasureText (IntPtr buffer, int length, ref SKRect bounds) => + MeasureText (buffer, (IntPtr)length, ref bounds); + public float MeasureText (IntPtr buffer, IntPtr length, ref SKRect bounds) { - if (buffer == IntPtr.Zero) + if (buffer == IntPtr.Zero && length != IntPtr.Zero) throw new ArgumentNullException (nameof (buffer)); return SkiaApi.sk_paint_measure_text (Handle, buffer, length, ref bounds); @@ -261,10 +267,10 @@ namespace SkiaSharp // BreakText public long BreakText (string text, float maxWidth) => - BreakText (text, maxWidth, out var measuredWidth); + BreakText (text, maxWidth, out _, out _); public long BreakText (string text, float maxWidth, out float measuredWidth) => - BreakText (text, maxWidth, out measuredWidth, out var measuredText); + BreakText (text, maxWidth, out measuredWidth, out _); public long BreakText (string text, float maxWidth, out float measuredWidth, out string measuredText) { @@ -286,7 +292,7 @@ namespace SkiaSharp } public long BreakText (byte[] text, float maxWidth) => - BreakText (text, maxWidth, out var measuredWidth); + BreakText (text, maxWidth, out _); public long BreakText (byte[] text, float maxWidth, out float measuredWidth) { @@ -300,12 +306,18 @@ namespace SkiaSharp } } + public long BreakText (IntPtr buffer, int length, float maxWidth) => + BreakText (buffer, (IntPtr)length, maxWidth, out _); + public long BreakText (IntPtr buffer, IntPtr length, float maxWidth) => - BreakText (buffer, length, maxWidth, out var measuredWidth); + BreakText (buffer, length, maxWidth, out _); + + public long BreakText (IntPtr buffer, int length, float maxWidth, out float measuredWidth) => + BreakText (buffer, (IntPtr)length, maxWidth, out measuredWidth); public long BreakText (IntPtr buffer, IntPtr length, float maxWidth, out float measuredWidth) { - if (buffer == IntPtr.Zero) + if (buffer == IntPtr.Zero && length != IntPtr.Zero) throw new ArgumentNullException (nameof (buffer)); return (long)SkiaApi.sk_paint_break_text (Handle, buffer, length, maxWidth, out measuredWidth); @@ -334,9 +346,12 @@ namespace SkiaSharp } } + public SKPath GetTextPath (IntPtr buffer, int length, float x, float y) => + GetTextPath (buffer, (IntPtr)length, x, y); + public SKPath GetTextPath (IntPtr buffer, IntPtr length, float x, float y) { - if (buffer == IntPtr.Zero) + if (buffer == IntPtr.Zero && length != IntPtr.Zero) throw new ArgumentNullException (nameof (buffer)); return GetObject (SkiaApi.sk_paint_get_text_path (Handle, buffer, length, x, y)); @@ -363,9 +378,12 @@ namespace SkiaSharp } } + public SKPath GetTextPath (IntPtr buffer, int length, SKPoint[] points) => + GetTextPath (buffer, (IntPtr)length, points); + public SKPath GetTextPath (IntPtr buffer, IntPtr length, SKPoint[] points) { - if (buffer == IntPtr.Zero) + if (buffer == IntPtr.Zero && length != IntPtr.Zero) throw new ArgumentNullException (nameof (buffer)); return GetObject (SkiaApi.sk_paint_get_pos_text_path (Handle, buffer, length, points)); @@ -447,17 +465,20 @@ namespace SkiaSharp unsafe { fixed (byte* p = text) { - return CountGlyphs ((IntPtr)p, text.Length); + return CountGlyphs ((IntPtr)p, (IntPtr)text.Length); } } } - public int CountGlyphs (IntPtr text, int length) + public int CountGlyphs (IntPtr text, int length) => + CountGlyphs (text, (IntPtr)length); + + public int CountGlyphs (IntPtr text, IntPtr length) { - if (text == IntPtr.Zero) + if (text == IntPtr.Zero && length != IntPtr.Zero) throw new ArgumentNullException (nameof (text)); - return SkiaApi.sk_paint_count_text (Handle, text, (IntPtr)length); + return SkiaApi.sk_paint_count_text (Handle, text, length); } // GetGlyphs @@ -478,18 +499,21 @@ namespace SkiaSharp unsafe { fixed (byte* p = text) { - return GetGlyphs ((IntPtr)p, text.Length); + return GetGlyphs ((IntPtr)p, (IntPtr)text.Length); } } } - public ushort[] GetGlyphs (IntPtr text, int length) + public ushort[] GetGlyphs (IntPtr text, int length) => + GetGlyphs (text, (IntPtr)length); + + public ushort[] GetGlyphs (IntPtr text, IntPtr length) { - if (text == IntPtr.Zero) + if (text == IntPtr.Zero && length != IntPtr.Zero) throw new ArgumentNullException (nameof (text)); unsafe { - var n = SkiaApi.sk_paint_text_to_glyphs (Handle, text, (IntPtr)length, (ushort*)IntPtr.Zero); + var n = SkiaApi.sk_paint_text_to_glyphs (Handle, text, length, (ushort*)IntPtr.Zero); if (n <= 0) { return new ushort[0]; @@ -497,7 +521,7 @@ namespace SkiaSharp var glyphs = new ushort[n]; fixed (ushort* gp = glyphs) { - SkiaApi.sk_paint_text_to_glyphs (Handle, text, (IntPtr)length, gp); + SkiaApi.sk_paint_text_to_glyphs (Handle, text, length, gp); } return glyphs; } @@ -521,17 +545,20 @@ namespace SkiaSharp unsafe { fixed (byte* p = text) { - return ContainsGlyphs ((IntPtr)p, text.Length); + return ContainsGlyphs ((IntPtr)p, (IntPtr)text.Length); } } } - public bool ContainsGlyphs (IntPtr text, int length) + public bool ContainsGlyphs (IntPtr text, int length) => + ContainsGlyphs (text, (IntPtr)length); + + public bool ContainsGlyphs (IntPtr text, IntPtr length) { - if (text == IntPtr.Zero) + if (text == IntPtr.Zero && length != IntPtr.Zero) throw new ArgumentNullException (nameof (text)); - return SkiaApi.sk_paint_contains_text (Handle, text, (IntPtr)length); + return SkiaApi.sk_paint_contains_text (Handle, text, length); } // GetGlyphWidths @@ -552,18 +579,21 @@ namespace SkiaSharp unsafe { fixed (byte* p = text) { - return GetGlyphWidths ((IntPtr)p, text.Length); + return GetGlyphWidths ((IntPtr)p, (IntPtr)text.Length); } } } - public float[] GetGlyphWidths (IntPtr text, int length) + public float[] GetGlyphWidths (IntPtr text, int length) => + GetGlyphWidths (text, (IntPtr)length); + + public float[] GetGlyphWidths (IntPtr text, IntPtr length) { - if (text == IntPtr.Zero) + if (text == IntPtr.Zero && length != IntPtr.Zero) throw new ArgumentNullException (nameof (text)); unsafe { - var n = SkiaApi.sk_paint_get_text_widths (Handle, text, (IntPtr)length, (float*)IntPtr.Zero, (SKRect*)IntPtr.Zero); + var n = SkiaApi.sk_paint_get_text_widths (Handle, text, length, (float*)IntPtr.Zero, (SKRect*)IntPtr.Zero); if (n <= 0) { return new float[0]; @@ -571,7 +601,7 @@ namespace SkiaSharp var widths = new float[n]; fixed (float* wp = widths) { - SkiaApi.sk_paint_get_text_widths (Handle, text, (IntPtr)length, wp, (SKRect*)IntPtr.Zero); + SkiaApi.sk_paint_get_text_widths (Handle, text, length, wp, (SKRect*)IntPtr.Zero); } return widths; } @@ -593,18 +623,21 @@ namespace SkiaSharp unsafe { fixed (byte* p = text) { - return GetGlyphWidths ((IntPtr)p, text.Length, out bounds); + return GetGlyphWidths ((IntPtr)p, (IntPtr)text.Length, out bounds); } } } - public float[] GetGlyphWidths (IntPtr text, int length, out SKRect[] bounds) + public float[] GetGlyphWidths (IntPtr text, int length, out SKRect[] bounds) => + GetGlyphWidths (text, (IntPtr)length, out bounds); + + public float[] GetGlyphWidths (IntPtr text, IntPtr length, out SKRect[] bounds) { - if (text == IntPtr.Zero) + if (text == IntPtr.Zero && length != IntPtr.Zero) throw new ArgumentNullException (nameof (text)); unsafe { - var n = SkiaApi.sk_paint_get_text_widths (Handle, text, (IntPtr)length, (float*)IntPtr.Zero, (SKRect*)IntPtr.Zero); + var n = SkiaApi.sk_paint_get_text_widths (Handle, text, length, (float*)IntPtr.Zero, (SKRect*)IntPtr.Zero); if (n <= 0) { bounds = new SKRect[0]; @@ -615,7 +648,7 @@ namespace SkiaSharp bounds = new SKRect[n]; fixed (float* wp = widths) fixed (SKRect* bp = bounds) { - SkiaApi.sk_paint_get_text_widths (Handle, text, (IntPtr)length, wp, bp); + SkiaApi.sk_paint_get_text_widths (Handle, text, length, wp, bp); } return widths; } @@ -639,20 +672,24 @@ namespace SkiaSharp unsafe { fixed (byte* p = text) { - return GetTextIntercepts ((IntPtr)p, text.Length, x, y, upperBounds, lowerBounds); + return GetTextIntercepts ((IntPtr)p, (IntPtr)text.Length, x, y, upperBounds, lowerBounds); } } } - public float[] GetTextIntercepts (IntPtr text, int length, float x, float y, float upperBounds, float lowerBounds) + public float[] GetTextIntercepts (IntPtr text, int length, float x, float y, float upperBounds, float lowerBounds) => + GetTextIntercepts (text, (IntPtr)length, x, y, upperBounds, lowerBounds); + + + public float[] GetTextIntercepts (IntPtr text, IntPtr length, float x, float y, float upperBounds, float lowerBounds) { - if (text == IntPtr.Zero) + if (text == IntPtr.Zero && length != IntPtr.Zero) throw new ArgumentNullException (nameof (text)); unsafe { var bounds = new[] { upperBounds, lowerBounds }; - var n = SkiaApi.sk_paint_get_text_intercepts (Handle, text, (IntPtr)length, x, y, bounds, (float*)IntPtr.Zero); + var n = SkiaApi.sk_paint_get_text_intercepts (Handle, text, length, x, y, bounds, (float*)IntPtr.Zero); if (n <= 0) { return new float[0]; @@ -660,7 +697,7 @@ namespace SkiaSharp var intervals = new float[n]; fixed (float* ip = intervals) { - SkiaApi.sk_paint_get_text_intercepts (Handle, text, (IntPtr)length, x, y, bounds, ip); + SkiaApi.sk_paint_get_text_intercepts (Handle, text, length, x, y, bounds, ip); } return intervals; } @@ -708,20 +745,24 @@ namespace SkiaSharp unsafe { fixed (byte* p = text) { - return GetPositionedTextIntercepts ((IntPtr)p, text.Length, positions, upperBounds, lowerBounds); + return GetPositionedTextIntercepts ((IntPtr)p, (IntPtr)text.Length, positions, upperBounds, lowerBounds); } } } - public float[] GetPositionedTextIntercepts (IntPtr text, int length, SKPoint[] positions, float upperBounds, float lowerBounds) + public float[] GetPositionedTextIntercepts (IntPtr text, int length, SKPoint[] positions, float upperBounds, float lowerBounds) => + GetPositionedTextIntercepts (text, (IntPtr)length, positions, upperBounds, lowerBounds); + + + public float[] GetPositionedTextIntercepts (IntPtr text, IntPtr length, SKPoint[] positions, float upperBounds, float lowerBounds) { - if (text == IntPtr.Zero) + if (text == IntPtr.Zero && length != IntPtr.Zero) throw new ArgumentNullException (nameof (text)); unsafe { var bounds = new[] { upperBounds, lowerBounds }; - var n = SkiaApi.sk_paint_get_pos_text_intercepts (Handle, text, (IntPtr)length, positions, bounds, (float*)IntPtr.Zero); + var n = SkiaApi.sk_paint_get_pos_text_intercepts (Handle, text, length, positions, bounds, (float*)IntPtr.Zero); if (n <= 0) { return new float[0]; @@ -729,7 +770,7 @@ namespace SkiaSharp var intervals = new float[n]; fixed (float* ip = intervals) { - SkiaApi.sk_paint_get_pos_text_intercepts (Handle, text, (IntPtr)length, positions, bounds, ip); + SkiaApi.sk_paint_get_pos_text_intercepts (Handle, text, length, positions, bounds, ip); } return intervals; } @@ -753,20 +794,23 @@ namespace SkiaSharp unsafe { fixed (byte* p = text) { - return GetHorizontalTextIntercepts ((IntPtr)p, text.Length, xpositions, y, upperBounds, lowerBounds); + return GetHorizontalTextIntercepts ((IntPtr)p, (IntPtr)text.Length, xpositions, y, upperBounds, lowerBounds); } } } - public float[] GetHorizontalTextIntercepts (IntPtr text, int length, float[] xpositions, float y, float upperBounds, float lowerBounds) + public float[] GetHorizontalTextIntercepts (IntPtr text, int length, float[] xpositions, float y, float upperBounds, float lowerBounds) => + GetHorizontalTextIntercepts (text, (IntPtr)length, xpositions, y, upperBounds, lowerBounds); + + public float[] GetHorizontalTextIntercepts (IntPtr text, IntPtr length, float[] xpositions, float y, float upperBounds, float lowerBounds) { - if (text == IntPtr.Zero) + if (text == IntPtr.Zero && length != IntPtr.Zero) throw new ArgumentNullException (nameof (text)); unsafe { var bounds = new[] { upperBounds, lowerBounds }; - var n = SkiaApi.sk_paint_get_pos_text_h_intercepts (Handle, text, (IntPtr)length, xpositions, y, bounds, (float*)IntPtr.Zero); + var n = SkiaApi.sk_paint_get_pos_text_h_intercepts (Handle, text, length, xpositions, y, bounds, (float*)IntPtr.Zero); if (n <= 0) { return new float[0]; @@ -774,7 +818,7 @@ namespace SkiaSharp var intervals = new float[n]; fixed (float* ip = intervals) { - SkiaApi.sk_paint_get_pos_text_h_intercepts (Handle, text, (IntPtr)length, xpositions, y, bounds, ip); + SkiaApi.sk_paint_get_pos_text_h_intercepts (Handle, text, length, xpositions, y, bounds, ip); } return intervals; } diff --git a/binding/Binding/SKTypeface.cs b/binding/Binding/SKTypeface.cs index d2cb352e..c961561c 100644 --- a/binding/Binding/SKTypeface.cs +++ b/binding/Binding/SKTypeface.cs @@ -218,7 +218,7 @@ namespace SkiaSharp public int CountGlyphs (IntPtr str, int strLen, SKEncoding encoding) { - if (str == IntPtr.Zero) + if (str == IntPtr.Zero && strLen != 0) throw new ArgumentNullException (nameof (str)); unsafe { @@ -251,7 +251,7 @@ namespace SkiaSharp public int GetGlyphs (IntPtr text, int length, SKEncoding encoding, out ushort [] glyphs) { - if (text == IntPtr.Zero) + if (text == IntPtr.Zero && length != 0) throw new ArgumentNullException (nameof (text)); unsafe { diff --git a/tests/Tests/SKCanvasTest.cs b/tests/Tests/SKCanvasTest.cs index 6ec74053..09472eee 100644 --- a/tests/Tests/SKCanvasTest.cs +++ b/tests/Tests/SKCanvasTest.cs @@ -80,6 +80,50 @@ namespace SkiaSharp.Tests Assert.Equal(textPixels, glyphsPixels); } + [SkippableFact] + public void CanDrawText() + { + using (var bmp = new SKBitmap(new SKImageInfo(300, 300))) + using (var canvas = new SKCanvas(bmp)) + using (var paint = new SKPaint()) + { + canvas.DrawText("text", 150, 175, paint); + } + } + + [SkippableFact] + public void CanDrawEmptyText() + { + using (var bmp = new SKBitmap(new SKImageInfo(300, 300))) + using (var canvas = new SKCanvas(bmp)) + using (var paint = new SKPaint()) + { + canvas.DrawText("", 150, 175, paint); + } + } + + [SkippableFact] + public void CanDrawNullPointerZeroLengthText() + { + using (var bmp = new SKBitmap(new SKImageInfo(300, 300))) + using (var canvas = new SKCanvas(bmp)) + using (var paint = new SKPaint()) + { + canvas.DrawText(IntPtr.Zero, 0, 150, 175, paint); + } + } + + [SkippableFact] + public void ThrowsOnDrawNullPointerText() + { + using (var bmp = new SKBitmap(new SKImageInfo(300, 300))) + using (var canvas = new SKCanvas(bmp)) + using (var paint = new SKPaint()) + { + Assert.Throws(() => canvas.DrawText(IntPtr.Zero, 123, 150, 175, paint)); + } + } + [SkippableFact] public void CanvasCanClipRoundRect() { diff --git a/tests/Tests/SKPaintTest.cs b/tests/Tests/SKPaintTest.cs index 0e07b08a..7e5f9764 100644 --- a/tests/Tests/SKPaintTest.cs +++ b/tests/Tests/SKPaintTest.cs @@ -188,6 +188,38 @@ namespace SkiaSharp.Tests Assert.Equal(4, paint.BreakText(StringUtilities.GetEncodedText("a", paint.TextEncoding), 50.0f)); } + [SkippableFact] + public void BreakTextSucceedsForEmtptyString() + { + var paint = new SKPaint(); + + paint.TextEncoding = SKTextEncoding.Utf8; + + Assert.Equal(0, paint.BreakText("", 50.0f)); + } + + [SkippableFact] + public void BreakTextSucceedsForNullPointerZeroLength() + { + var paint = new SKPaint(); + + paint.TextEncoding = SKTextEncoding.Utf8; + + Assert.Equal(0, paint.BreakText(IntPtr.Zero, IntPtr.Zero, 50.0f)); + Assert.Equal(0, paint.BreakText(IntPtr.Zero, 0, 50.0f)); + } + + [SkippableFact] + public void BreakTextThrowsForNullPointer() + { + var paint = new SKPaint(); + + paint.TextEncoding = SKTextEncoding.Utf8; + + Assert.Throws(() => paint.BreakText(IntPtr.Zero, (IntPtr)123, 50.0f)); + Assert.Throws(() => paint.BreakText(IntPtr.Zero, 123, 50.0f)); + } + [SkippableFact] public void BreakTextReturnsTheCorrectNumberOfCharacters() { @@ -300,6 +332,38 @@ namespace SkiaSharp.Tests Assert.Equal(bounds8, bounds32); } + [SkippableFact] + public void MeasureTextSucceedsForEmtptyString() + { + var paint = new SKPaint(); + + paint.TextEncoding = SKTextEncoding.Utf8; + + Assert.Equal(0, paint.MeasureText("")); + } + + [SkippableFact] + public void MeasureTextSucceedsForNullPointerZeroLength() + { + var paint = new SKPaint(); + + paint.TextEncoding = SKTextEncoding.Utf8; + + Assert.Equal(0, paint.MeasureText(IntPtr.Zero, IntPtr.Zero)); + Assert.Equal(0, paint.MeasureText(IntPtr.Zero, 0)); + } + + [SkippableFact] + public void MeasureTextThrowsForNullPointer() + { + var paint = new SKPaint(); + + paint.TextEncoding = SKTextEncoding.Utf8; + + Assert.Throws(() => paint.MeasureText(IntPtr.Zero, (IntPtr)123)); + Assert.Throws(() => paint.MeasureText(IntPtr.Zero, 123)); + } + [SkippableFact] public void GetGlyphWidthsReturnsTheCorrectAmount() { @@ -352,5 +416,15 @@ namespace SkiaSharp.Tests Assert.Equal(pathWidth, diff, 2); } + + [SkippableFact] + public void GetTextPathSucceedsForEmtptyString() + { + var paint = new SKPaint(); + + paint.TextEncoding = SKTextEncoding.Utf8; + + Assert.NotNull(paint.GetTextPath("", 0, 0)); + } } }