public SkiaDrawingContext(int width, int height, SKColor background, float scale, SKTypeface typeface) { this.typeface = typeface; surface = SKSurface.Create(new SKImageInfo((int)(width * scale), (int)(height * scale), SKImageInfo.PlatformColorType, SKAlphaType.Opaque)); surface.Canvas.Clear(background); surface.Canvas.Scale(scale); }
/// <summary> /// 创建验证码的图片 /// </summary> public byte[] CreateValidationCodeGraphic(string validationCode) { if (validationCode.IsNullOrWhiteSpace()) { throw new Exception($"验证码参数 {nameof(validationCode)} 为空。"); } var rand = new Random(Guid.NewGuid().GetHashCode()); var randAngle = 40; var mapWidth = validationCode.Length * 18; var mapHeight = 28; using (var bitmap = new SKBitmap(mapWidth, mapHeight)) { using (var canvas = new SKCanvas(bitmap)) { canvas.Clear(SKColors.AliceBlue); var paint = new SKPaint() { Color = SKColors.LightGray, }; for (int i = 0; i < 50; i++) { int x = rand.Next(0, bitmap.Width); int y = rand.Next(0, bitmap.Height); canvas.DrawRect(new SKRect(x, y, x + 1, y + 1), paint); } var chars = validationCode.ToCharArray(); var colors = new[] { SKColors.Black, SKColors.Red, SKColors.DarkBlue, SKColors.Green, SKColors.Orange, SKColors.Brown, SKColors.DarkCyan, SKColors.Purple }; var fonts = new[] { SKTypeface.FromFamilyName("Verdana"), SKTypeface.FromFamilyName("Microsoft Sans Serif"), SKTypeface.FromFamilyName("Comic Sans MS"), SKTypeface.FromFamilyName("Arial") }; canvas.Translate(-4, 0); for (int i = 0; i < chars.Length; i++) { int colorIndex = rand.Next(colors.Length); int fontIndex = rand.Next(fonts.Length); var fontColor = colors[colorIndex]; var foneSize = rand.Next(18, 25); float angle = rand.Next(-randAngle, randAngle); SKPoint point = new SKPoint(16, 28 / 2 + 4); canvas.Translate(point); canvas.RotateDegrees(angle); var textPaint = new SKPaint() { TextAlign = SKTextAlign.Center, Color = fontColor, TextSize = foneSize, Typeface = fonts[fontIndex], //IsAntialias = rand.Next(1) == 1 ? true : false, //FakeBoldText = true, //FilterQuality = SKFilterQuality.High, //HintingLevel = SKPaintHinting.Full, //IsEmbeddedBitmapText = true, //LcdRenderText = true, //Style = SKPaintStyle.StrokeAndFill, //TextEncoding = SKTextEncoding.Utf8, }; canvas.DrawText(chars[i].ToString(), new SKPoint(0, 0), textPaint); canvas.RotateDegrees(-angle); canvas.Translate(0, -point.Y); } using (var image = SKImage.FromBitmap(bitmap)) { using (var ms = new MemoryStream()) { image.Encode(SKEncodedImageFormat.Png, 90).SaveTo(ms); return(ms.ToArray()); } } } } }
public FormattedTextImpl( string text, Typeface typeface, TextAlignment textAlignment, TextWrapping wrapping, Size constraint, IReadOnlyList <FormattedTextStyleSpan> spans) { Text = text ?? string.Empty; // Replace 0 characters with zero-width spaces (200B) Text = Text.Replace((char)0, (char)0x200B); SKTypeface skiaTypeface = TypefaceCache.Default; if (typeface.FontFamily.Key != null) { var typefaces = SKTypefaceCollectionCache.GetOrAddTypefaceCollection(typeface.FontFamily); skiaTypeface = typefaces.GetTypeFace(typeface); } else { if (typeface.FontFamily.FamilyNames.HasFallbacks) { foreach (var familyName in typeface.FontFamily.FamilyNames) { skiaTypeface = TypefaceCache.GetTypeface( familyName, typeface.Style, typeface.Weight); if (skiaTypeface != TypefaceCache.Default) { break; } } } else { skiaTypeface = TypefaceCache.GetTypeface( typeface.FontFamily.Name, typeface.Style, typeface.Weight); } } _paint = new SKPaint(); //currently Skia does not measure properly with Utf8 !!! //Paint.TextEncoding = SKTextEncoding.Utf8; _paint.TextEncoding = SKTextEncoding.Utf16; _paint.IsStroke = false; _paint.IsAntialias = true; _paint.LcdRenderText = true; _paint.SubpixelText = true; _paint.Typeface = skiaTypeface; _paint.TextSize = (float)typeface.FontSize; _paint.TextAlign = textAlignment.ToSKTextAlign(); _wrapping = wrapping; _constraint = constraint; if (spans != null) { foreach (var span in spans) { if (span.ForegroundBrush != null) { SetForegroundBrush(span.ForegroundBrush, span.StartIndex, span.Length); } } } Rebuild(); }
public override void DrawView(SKSurface surface) { var canvas = surface.Canvas; // Fill color for Group Style var GroupStyleFillColor = SKColors.Aqua; // New Group Style fill paint var GroupStyleFillPaint = new SKPaint() { Style = SKPaintStyle.Fill, Color = GroupStyleFillColor, BlendMode = SKBlendMode.SrcOver, IsAntialias = true }; // Frame color for Group Style var GroupStyleFrameColor = SKColors.Black; // New Group Style frame paint var GroupStyleFramePaint = new SKPaint() { Style = SKPaintStyle.Stroke, Color = GroupStyleFrameColor, BlendMode = SKBlendMode.SrcOver, IsAntialias = true, StrokeWidth = 1f, StrokeMiter = 4f, StrokeJoin = SKStrokeJoin.Miter, StrokeCap = SKStrokeCap.Butt }; //----------------------------------------------------------------------------- // Draw Group shape group // Fill color for Round Rectangle Style var RoundRectangleStyleFillColor = _touched ? SKColors.Purple : SKColors.Aqua; // New Round Rectangle Style fill paint var RoundRectangleStyleFillPaint = new SKPaint() { Style = SKPaintStyle.Fill, Color = RoundRectangleStyleFillColor, BlendMode = SKBlendMode.SrcOver, IsAntialias = true }; // Frame color for Round Rectangle Style var RoundRectangleStyleFrameColor = SKColors.Black; // New Round Rectangle Style frame paint var RoundRectangleStyleFramePaint = new SKPaint() { Style = SKPaintStyle.Stroke, Color = RoundRectangleStyleFrameColor, BlendMode = SKBlendMode.SrcOver, IsAntialias = true, StrokeWidth = 1f, StrokeMiter = 4f, StrokeJoin = SKStrokeJoin.Miter, StrokeCap = SKStrokeCap.Butt }; // Draw Round Rectangle shape canvas.DrawRoundRect(new SKRect(100, 300, 600, 200), 32f, 32f, RoundRectangleStyleFillPaint); canvas.DrawRoundRect(new SKRect(100, 300, 600, 200), 32f, 32f, RoundRectangleStyleFramePaint); // Fill color for Text Style var TextStyleFillColor = SKColors.Black; // New Text Style fill paint var TextStyleFillPaint = new SKPaint() { Style = SKPaintStyle.Fill, Color = TextStyleFillColor, BlendMode = SKBlendMode.SrcOver, IsAntialias = true, Typeface = SKTypeface.FromFamilyName("System", SKTypefaceStyle.Normal), TextSize = 36f, TextAlign = SKTextAlign.Center, IsVerticalText = false, TextScaleX = 1f, TextSkewX = 0f }; // Draw Text shape canvas.DrawText(Element.Text, 340, 265, TextStyleFillPaint); }
private static SKTypeface CreateDefaultTypeface() { var defaultTypeface = SKTypeface.FromFamilyName(FontFamily.Default.Name) ?? SKTypeface.FromFamilyName(null); return(defaultTypeface); }
public static void DrawCaptionLabels(this SKCanvas canvas, string label, SKColor labelColor, string value, SKColor valueColor, float textSize, SKPoint point, SKTextAlign horizontalAlignment, SKTypeface typeface, out SKRect totalBounds) { var hasLabel = !string.IsNullOrEmpty(label); var hasValueLabel = !string.IsNullOrEmpty(value); totalBounds = new SKRect(); if (hasLabel || hasValueLabel) { var hasOffset = hasLabel && hasValueLabel; var captionMargin = textSize * 0.60f; var space = hasOffset ? captionMargin : 0; if (hasLabel) { using (var paint = new SKPaint { TextSize = textSize, IsAntialias = true, Color = labelColor, IsStroke = false, TextAlign = horizontalAlignment, Typeface = typeface }) { var bounds = new SKRect(); var text = label; paint.MeasureText(text, ref bounds); var y = point.Y - ((bounds.Top + bounds.Bottom) / 2) - space; canvas.DrawText(text, point.X, point.Y + space / 2, paint); var labelBounds = GetAbsolutePositionRect(point.X, point.Y + space / 2, bounds, horizontalAlignment); totalBounds = labelBounds.Standardized; } } if (hasValueLabel) { using (var paint = new SKPaint() { TextSize = textSize, IsAntialias = true, FakeBoldText = true, Color = valueColor, IsStroke = false, TextAlign = horizontalAlignment, Typeface = typeface }) { var bounds = new SKRect(); var text = value; paint.MeasureText(text, ref bounds); var y = point.Y - ((bounds.Top + bounds.Bottom) / 2) + space; canvas.DrawText(text, point.X + textSize * 3 + space, point.Y + space / 2, paint); var valueBounds = GetAbsolutePositionRect(point.X + textSize * 3, point.Y + space / 2, bounds, horizontalAlignment); if (totalBounds.IsEmpty) { totalBounds = valueBounds; } else { totalBounds.Union(valueBounds); } } } } }
public IGlyphTypefaceImpl CreateGlyphTypeface(Typeface typeface) { return(new GlyphTypefaceImpl(SKTypeface.FromFamilyName(_defaultTypeface.FontFamily.Name))); }
/// <summary> /// Shape an array of utf-32 code points /// </summary> /// <param name="bufferSet">A re-usable text shaping buffer set that results will be allocated from</param> /// <param name="codePoints">The utf-32 code points to be shaped</param> /// <param name="style">The user style for the text</param> /// <param name="direction">LTR or RTL direction</param> /// <param name="clusterAdjustment">A value to add to all reported cluster numbers</param> /// <param name="asFallbackFor">The type face this font is a fallback for</param> /// <returns>A TextShaper.Result representing the shaped text</returns> public Result Shape(ResultBufferSet bufferSet, Slice <int> codePoints, IStyle style, TextDirection direction, int clusterAdjustment, SKTypeface asFallbackFor) { // Work out if we need to force this to a fixed pitch and if // so the unscale character width we need to use float forceFixedPitchWidth = 0; if (asFallbackFor != _typeface && asFallbackFor != null) { var originalTypefaceShaper = ForTypeface(asFallbackFor); if (originalTypefaceShaper._isFixedPitch) { forceFixedPitchWidth = originalTypefaceShaper._fixedCharacterWidth; } } using (var buffer = new HarfBuzzSharp.Buffer()) { // Setup buffer unsafe { fixed(int *pCodePoints = codePoints.Underlying) { hb_buffer_add_utf32(buffer.Handle, (IntPtr)(pCodePoints + codePoints.Start), codePoints.Length, 0, -1); } } // Setup directionality (if supplied) switch (direction) { case TextDirection.LTR: buffer.Direction = Direction.LeftToRight; break; case TextDirection.RTL: buffer.Direction = Direction.RightToLeft; break; default: throw new ArgumentException(nameof(direction)); } // Guess other attributes buffer.GuessSegmentProperties(); // Shape it _font.Shape(buffer); // RTL? bool rtl = buffer.Direction == Direction.RightToLeft; // Work out glyph scaling and offsetting for super/subscript float glyphScale = style.FontSize / overScale; float glyphVOffset = 0; if (style.FontVariant == FontVariant.SuperScript) { glyphScale *= 0.65f; glyphVOffset -= style.FontSize * 0.35f; } if (style.FontVariant == FontVariant.SubScript) { glyphScale *= 0.65f; glyphVOffset += style.FontSize * 0.1f; } // Create results and get buffes var r = new Result(); r.GlyphIndicies = bufferSet.GlyphIndicies.Add((int)buffer.Length, false); r.GlyphPositions = bufferSet.GlyphPositions.Add((int)buffer.Length, false); r.Clusters = bufferSet.Clusters.Add((int)buffer.Length, false); r.CodePointXCoords = bufferSet.CodePointXCoords.Add(codePoints.Length, false); // Convert points var gp = buffer.GlyphPositions; var gi = buffer.GlyphInfos; float cursorX = 0; float cursorY = 0; float cursorXCluster = 0; for (int i = 0; i < buffer.Length; i++) { r.GlyphIndicies[i] = (ushort)gi[i].Codepoint; r.Clusters[i] = (int)gi[i].Cluster + clusterAdjustment; // Update code point positions if (!rtl) { // First cluster, different cluster, or same cluster with lower x-coord if (i == 0 || (r.Clusters[i] != r.Clusters[i - 1]) || (cursorX < r.CodePointXCoords[r.Clusters[i] - clusterAdjustment])) { r.CodePointXCoords[r.Clusters[i] - clusterAdjustment] = cursorX; } } // Get the position var pos = gp[i]; // Update glyph position r.GlyphPositions[i] = new SKPoint( cursorX + pos.XOffset * glyphScale, cursorY - pos.YOffset * glyphScale + glyphVOffset ); // Update cursor position cursorX += pos.XAdvance * glyphScale; cursorY += pos.YAdvance * glyphScale; // Are we falling back for a fixed pitch font and is the next character a // new cluster? If so advance by the width of the original font, not this // fallback font if (forceFixedPitchWidth != 0) { // New cluster? if (i + 1 >= buffer.Length || gi[i].Cluster != gi[i + 1].Cluster) { // Work out fixed pitch position of next cluster cursorXCluster += forceFixedPitchWidth * glyphScale; if (cursorXCluster > cursorX) { // Nudge characters to center them in the fixed pitch width if (i == 0 || gi[i - 1].Cluster != gi[i].Cluster) { r.GlyphPositions[i].X += (cursorXCluster - cursorX) / 2; } // Use fixed width character position cursorX = cursorXCluster; } else { // Character is wider (probably an emoji) so we // allow it to exceed the fixed pitch character width cursorXCluster = cursorX; } } } // Store RTL cursor position if (rtl) { // First cluster, different cluster, or same cluster with lower x-coord if (i == 0 || (r.Clusters[i] != r.Clusters[i - 1]) || (cursorX > r.CodePointXCoords[r.Clusters[i] - clusterAdjustment])) { r.CodePointXCoords[r.Clusters[i] - clusterAdjustment] = cursorX; } } } // Finalize cursor positions by filling in any that weren't // referenced by a cluster if (rtl) { r.CodePointXCoords[0] = cursorX; for (int i = codePoints.Length - 2; i >= 0; i--) { if (r.CodePointXCoords[i] == 0) { r.CodePointXCoords[i] = r.CodePointXCoords[i + 1]; } } } else { for (int i = 1; i < codePoints.Length; i++) { if (r.CodePointXCoords[i] == 0) { r.CodePointXCoords[i] = r.CodePointXCoords[i - 1]; } } } // Also return the end cursor position r.EndXCoord = new SKPoint(cursorX, cursorY); // And some other useful metrics r.Ascent = _fontMetrics.Ascent * style.FontSize / overScale; r.Descent = _fontMetrics.Descent * style.FontSize / overScale; r.XMin = _fontMetrics.XMin * style.FontSize / overScale; // Done return(r); } }
static private SKPaint MakeDefaultPaint(Color pColor, float pStrokeWidth, float pFontSize, SKTypeface pTypeface) { return(new SKPaint() { Color = pColor.ToSKColor(), StrokeWidth = pStrokeWidth, Typeface = pTypeface, TextSize = pFontSize, StrokeCap = SKStrokeCap.Round, BlendMode = SKBlendMode.Src, IsAntialias = true }); }
public TypefaceCollectionEntry(Typeface typeface, SKTypeface skTypeface) { Typeface = typeface; SKTypeface = skTypeface; }
/// <summary> /// Creates a bitmap of the graph. /// </summary> /// <param name="Settings">Graph settings.</param> /// <param name="States">State object(s) that contain graph-specific information about its inner states. /// These can be used in calls back to the graph object to make actions on the generated graph.</param> /// <returns>Bitmap</returns> public override SKImage CreateBitmap(GraphSettings Settings, out object[] States) { using (SKSurface Surface = SKSurface.Create(Settings.Width, Settings.Height, SKImageInfo.PlatformColorType, SKAlphaType.Premul)) { SKCanvas Canvas = Surface.Canvas; States = new object[0]; Canvas.Clear(Settings.BackgroundColor); int x1, y1, x2, y2, x3, y3, w, h; x1 = Settings.MarginLeft; x2 = Settings.Width - Settings.MarginRight; y1 = Settings.MarginTop; y2 = Settings.Height - Settings.MarginBottom; if (!string.IsNullOrEmpty(this.labelY)) { x1 += (int)(Settings.LabelFontSize * 2 + 0.5); } if (!string.IsNullOrEmpty(this.labelX)) { y2 -= (int)(Settings.LabelFontSize * 2 + 0.5); } if (!string.IsNullOrEmpty(this.title)) { y1 += (int)(Settings.LabelFontSize * 2 + 0.5); } IVector YLabels = GetLabels(ref this.minY, ref this.maxY, this.y, Settings.ApproxNrLabelsY, out LabelType YLabelType); SKPaint Font = new SKPaint() { FilterQuality = SKFilterQuality.High, HintingLevel = SKPaintHinting.Full, SubpixelText = true, IsAntialias = true, Style = SKPaintStyle.Fill, Color = Settings.AxisColor, Typeface = SKTypeface.FromFamilyName(Settings.FontName, SKTypefaceStyle.Normal), TextSize = (float)Settings.LabelFontSize }; float Size; double MaxSize = 0; if (this.showYAxis) { foreach (IElement Label in YLabels.ChildElements) { Size = Font.MeasureText(LabelString(Label, YLabelType)); if (Size > MaxSize) { MaxSize = Size; } } } x3 = (int)Math.Ceiling(x1 + MaxSize) + Settings.MarginLabel; IVector XLabels = GetLabels(ref this.minX, ref this.maxX, this.x, Settings.ApproxNrLabelsX, out LabelType XLabelType); MaxSize = 0; if (this.showXAxis) { foreach (IElement Label in XLabels.ChildElements) { Size = Font.MeasureText(LabelString(Label, XLabelType)); if (Size > MaxSize) { MaxSize = Size; } } } y3 = (int)Math.Floor(y2 - MaxSize) - Settings.MarginLabel; w = x2 - x3; h = y3 - y1; SKPaint AxisBrush = new SKPaint() { FilterQuality = SKFilterQuality.High, IsAntialias = true, Style = SKPaintStyle.Fill, Color = Settings.AxisColor }; SKPaint GridBrush = new SKPaint() { FilterQuality = SKFilterQuality.High, IsAntialias = true, Style = SKPaintStyle.Fill, Color = Settings.GridColor }; SKPaint AxisPen = new SKPaint() { FilterQuality = SKFilterQuality.High, IsAntialias = true, Style = SKPaintStyle.Stroke, Color = Settings.AxisColor, StrokeWidth = Settings.AxisWidth }; SKPaint GridPen = new SKPaint() { FilterQuality = SKFilterQuality.High, IsAntialias = true, Style = SKPaintStyle.Stroke, Color = Settings.GridColor, StrokeWidth = Settings.GridWidth }; double OrigoX; double OrigoY; if (this.minX.AssociatedSet is IAbelianGroup AgX) { OrigoX = Scale(new ObjectVector(AgX.AdditiveIdentity), this.minX, this.maxX, x3, w)[0]; } else { OrigoX = 0; } if (this.minY.AssociatedSet is IAbelianGroup AgY) { OrigoY = Scale(new ObjectVector(AgY.AdditiveIdentity), this.minY, this.maxY, y3, -h)[0]; } else { OrigoY = 0; } DrawingArea DrawingArea = new DrawingArea(this.minX, this.maxX, this.minY, this.maxY, x3, y3, w, -h, (float)OrigoX, (float)OrigoY); double[] LabelYY = DrawingArea.ScaleY(YLabels); int i = 0; float f; string s; foreach (IElement Label in YLabels.ChildElements) { Size = Font.MeasureText(s = LabelString(Label, YLabelType)); f = (float)LabelYY[i++]; if (this.showGrid) { if (Label is DoubleNumber && ((DoubleNumber)Label).Value == 0) { Canvas.DrawLine(x3, f, x2, f, AxisPen); } else { Canvas.DrawLine(x3, f, x2, f, GridPen); } } if (this.showYAxis) { f += (float)Settings.LabelFontSize * 0.5f; Canvas.DrawText(s, x3 - Size - Settings.MarginLabel, f, Font); } } double[] LabelXX = DrawingArea.ScaleX(XLabels); i = 0; foreach (IElement Label in XLabels.ChildElements) { Size = Font.MeasureText(s = LabelString(Label, XLabelType)); f = (float)LabelXX[i++]; if (this.showGrid) { if (Label is DoubleNumber && ((DoubleNumber)Label).Value == 0) { Canvas.DrawLine(f, y1, f, y3, AxisPen); } else { Canvas.DrawLine(f, y1, f, y3, GridPen); } } if (this.showXAxis) { f -= Size * 0.5f; if (f < x3) { f = x3; } else if (f + Size > x3 + w) { f = x3 + w - Size; } Canvas.DrawText(s, f, y3 + Settings.MarginLabel + (float)Settings.LabelFontSize, Font); } } Font.Dispose(); Font = null; Font = new SKPaint() { FilterQuality = SKFilterQuality.High, HintingLevel = SKPaintHinting.Full, SubpixelText = true, IsAntialias = true, Style = SKPaintStyle.Fill, Color = Settings.AxisColor, Typeface = SKTypeface.FromFamilyName(Settings.FontName, SKTypefaceStyle.Bold), TextSize = (float)(Settings.LabelFontSize * 1.5) }; if (!string.IsNullOrEmpty(this.title)) { Size = Font.MeasureText(this.title); f = x3 + (x2 - x3 - Size) * 0.5f; if (f < x3) { f = x3; } else if (f + Size > x3 + w) { f = x3 + w - Size; } Canvas.DrawText(this.title, f, (float)(Settings.MarginTop + 1.5 * Settings.LabelFontSize), Font); } if (!string.IsNullOrEmpty(this.labelX)) { Size = Font.MeasureText(this.labelX); f = x3 + (x2 - x3 - Size) * 0.5f; if (f < x3) { f = x3; } else if (f + Size > x3 + w) { f = x3 + w - Size; } Canvas.DrawText(this.labelX, f, (float)(y2 + 0.45 * Settings.LabelFontSize), Font); } if (!string.IsNullOrEmpty(this.labelY)) { Size = Font.MeasureText(this.labelY); f = y3 - (y3 - y1 - Size) * 0.5f; if (f - Size < y1) { f = y1 + Size; } else if (f > y3 + h) { f = y3 + h; } Canvas.Translate((float)(Settings.MarginLeft + 0.05 * Settings.LabelFontSize), f); Canvas.RotateDegrees(-90); Canvas.DrawText(this.labelY, 0, 0, Font); Canvas.ResetMatrix(); } IEnumerator <IVector> ex = this.x.GetEnumerator(); IEnumerator <IVector> ey = this.y.GetEnumerator(); IEnumerator <object[]> eParameters = this.parameters.GetEnumerator(); IEnumerator <DrawCallback> eCallbacks = this.callbacks.GetEnumerator(); SKPoint[] Points; SKPoint[] PrevPoints = null; object[] PrevParameters = null; DrawCallback PrevCallback = null; while (ex.MoveNext() && ey.MoveNext() && eParameters.MoveNext() && eCallbacks.MoveNext()) { Points = DrawingArea.Scale(ex.Current, ey.Current); if (PrevCallback != null && eCallbacks.Current.Target.GetType() == PrevCallback.Target.GetType()) { eCallbacks.Current(Canvas, Points, eParameters.Current, PrevPoints, PrevParameters, DrawingArea); } else { eCallbacks.Current(Canvas, Points, eParameters.Current, null, null, DrawingArea); } PrevPoints = Points; PrevParameters = eParameters.Current; PrevCallback = eCallbacks.Current; } SKImage Result = Surface.Snapshot(); if (Font != null) { Font.Dispose(); } AxisBrush.Dispose(); GridBrush.Dispose(); GridPen.Dispose(); AxisPen.Dispose(); States = new object[] { DrawingArea }; return(Result); } }
public SKFontInfo(SKTypeface face, float pFontSize) { TypeFace = face; FontSize = pFontSize; }
public static void ClassInit(TestContext context) { Typeface = SKTypeface.FromFile(Path.Combine("simsun.ttf")); }
private static int GetCacheKey(SKTypeface typeface, float fontSize) { return(typeface.GetHashCode() + (int)(fontSize * 100f)); }
/// <summary> /// Shape an array of utf-32 code points /// </summary> /// <param name="bufferSet">A re-usable text shaping buffer set that results will be allocated from</param> /// <param name="codePoints">The utf-32 code points to be shaped</param> /// <param name="style">The user style for the text</param> /// <param name="direction">LTR or RTL direction</param> /// <param name="clusterAdjustment">A value to add to all reported cluster numbers</param> /// <param name="asFallbackFor">The type face this font is a fallback for</param> /// <param name="textAlignment">The text alignment of the paragraph, used to control placement of glyphs within character cell when letter spacing used</param> /// <returns>A TextShaper.Result representing the shaped text</returns> public Result Shape(ResultBufferSet bufferSet, Slice <int> codePoints, IStyle style, TextDirection direction, int clusterAdjustment, SKTypeface asFallbackFor, TextAlignment textAlignment) { // Work out if we need to force this to a fixed pitch and if // so the unscale character width we need to use float forceFixedPitchWidth = 0; if (asFallbackFor != _typeface && asFallbackFor != null) { var originalTypefaceShaper = ForTypeface(asFallbackFor); if (originalTypefaceShaper._isFixedPitch) { forceFixedPitchWidth = originalTypefaceShaper._fixedCharacterWidth; } } // Work out how much to shift glyphs in the character cell when using letter spacing // The idea here is to align the glyphs within the character cell the same way as the // text block alignment so that left/right aligned text still aligns with the margin // and centered text is still centered (and not shifted slightly due to the extra // space that would be at the right with normal letter spacing). float glyphLetterSpacingAdjustment = 0; switch (textAlignment) { case TextAlignment.Right: glyphLetterSpacingAdjustment = style.LetterSpacing; break; case TextAlignment.Center: glyphLetterSpacingAdjustment = style.LetterSpacing / 2; break; } using (var buffer = new HarfBuzzSharp.Buffer()) { // Setup buffer buffer.AddUtf32(codePoints.AsSpan(), 0, -1); // Setup directionality (if supplied) switch (direction) { case TextDirection.LTR: buffer.Direction = Direction.LeftToRight; break; case TextDirection.RTL: buffer.Direction = Direction.RightToLeft; break; default: throw new ArgumentException(nameof(direction)); } // Guess other attributes buffer.GuessSegmentProperties(); // Shape it _font.Shape(buffer); // RTL? bool rtl = buffer.Direction == Direction.RightToLeft; // Work out glyph scaling and offsetting for super/subscript float glyphScale = style.FontSize / overScale; float glyphVOffset = 0; if (style.FontVariant == FontVariant.SuperScript) { glyphScale *= 0.65f; glyphVOffset -= style.FontSize * 0.35f; } if (style.FontVariant == FontVariant.SubScript) { glyphScale *= 0.65f; glyphVOffset += style.FontSize * 0.1f; } // Create results and get buffes var r = new Result(); r.GlyphIndicies = bufferSet.GlyphIndicies.Add((int)buffer.Length, false); r.GlyphPositions = bufferSet.GlyphPositions.Add((int)buffer.Length, false); r.Clusters = bufferSet.Clusters.Add((int)buffer.Length, false); r.CodePointXCoords = bufferSet.CodePointXCoords.Add(codePoints.Length, false); r.CodePointXCoords.Fill(0); // Convert points var gp = buffer.GlyphPositions; var gi = buffer.GlyphInfos; float cursorX = 0; float cursorY = 0; float cursorXCluster = 0; for (int i = 0; i < buffer.Length; i++) { r.GlyphIndicies[i] = (ushort)gi[i].Codepoint; r.Clusters[i] = (int)gi[i].Cluster + clusterAdjustment; // Update code point positions if (!rtl) { // First cluster, different cluster, or same cluster with lower x-coord if (i == 0 || (r.Clusters[i] != r.Clusters[i - 1]) || (cursorX < r.CodePointXCoords[r.Clusters[i] - clusterAdjustment])) { r.CodePointXCoords[r.Clusters[i] - clusterAdjustment] = cursorX; } } // Get the position var pos = gp[i]; // Update glyph position r.GlyphPositions[i] = new SKPoint( cursorX + pos.XOffset * glyphScale + glyphLetterSpacingAdjustment, cursorY - pos.YOffset * glyphScale + glyphVOffset ); // Update cursor position cursorX += pos.XAdvance * glyphScale; cursorY += pos.YAdvance * glyphScale; // Ensure paragraph separator character (0x2029) has some // width so it can be seen as part of the selection in the editor. if (pos.XAdvance == 0 && codePoints[(int)gi[i].Cluster] == 0x2029) { cursorX += style.FontSize * 2 / 3; } if (i + 1 == gi.Length || gi[i].Cluster != gi[i + 1].Cluster) { cursorX += style.LetterSpacing; } // Are we falling back for a fixed pitch font and is the next character a // new cluster? If so advance by the width of the original font, not this // fallback font if (forceFixedPitchWidth != 0) { // New cluster? if (i + 1 >= buffer.Length || gi[i].Cluster != gi[i + 1].Cluster) { // Work out fixed pitch position of next cluster cursorXCluster += forceFixedPitchWidth * glyphScale; if (cursorXCluster > cursorX) { // Nudge characters to center them in the fixed pitch width if (i == 0 || gi[i - 1].Cluster != gi[i].Cluster) { r.GlyphPositions[i].X += (cursorXCluster - cursorX) / 2; } // Use fixed width character position cursorX = cursorXCluster; } else { // Character is wider (probably an emoji) so we // allow it to exceed the fixed pitch character width cursorXCluster = cursorX; } } } // Store RTL cursor position if (rtl) { // First cluster, different cluster, or same cluster with lower x-coord if (i == 0 || (r.Clusters[i] != r.Clusters[i - 1]) || (cursorX > r.CodePointXCoords[r.Clusters[i] - clusterAdjustment])) { r.CodePointXCoords[r.Clusters[i] - clusterAdjustment] = cursorX; } } } // Finalize cursor positions by filling in any that weren't // referenced by a cluster if (rtl) { r.CodePointXCoords[0] = cursorX; for (int i = codePoints.Length - 2; i >= 0; i--) { if (r.CodePointXCoords[i] == 0) { r.CodePointXCoords[i] = r.CodePointXCoords[i + 1]; } } } else { for (int i = 1; i < codePoints.Length; i++) { if (r.CodePointXCoords[i] == 0) { r.CodePointXCoords[i] = r.CodePointXCoords[i - 1]; } } } // Also return the end cursor position r.EndXCoord = new SKPoint(cursorX, cursorY); // And some other useful metrics ApplyFontMetrics(ref r, style.FontSize); // Done return(r); } }
public void Update(PageChatRender pageChatRender) { try { this.Width = Int32.Parse(pageChatRender.textWidth.Text) + 10; this.Height = Int32.Parse(pageChatRender.textHeight.Text) + 38; imgPreview.Height = Int32.Parse(pageChatRender.textHeight.Text); imgPreview.Width = Int32.Parse(pageChatRender.textWidth.Text); PreviewData previewData = JsonConvert.DeserializeObject <PreviewData>(File.ReadAllText("preview_data.json")); BlockingCollection <TwitchCommentPreview> finalComments = new BlockingCollection <TwitchCommentPreview>(); SKBitmap previewBitmap = new SKBitmap((int)this.Width, (int)imgPreview.Height); SKColor backgroundColor = new SKColor(pageChatRender.colorBackground.SelectedColor.Value.R, pageChatRender.colorBackground.SelectedColor.Value.G, pageChatRender.colorBackground.SelectedColor.Value.B); SKColor messageColor = new SKColor(pageChatRender.colorFont.SelectedColor.Value.R, pageChatRender.colorFont.SelectedColor.Value.G, pageChatRender.colorFont.SelectedColor.Value.B); RenderOptions renderOptions = new RenderOptions(pageChatRender.textJson.Text, "", backgroundColor, Int32.Parse(pageChatRender.textHeight.Text), Int32.Parse(pageChatRender.textWidth.Text), (bool)pageChatRender.checkBTTV.IsChecked, (bool)pageChatRender.checkFFZ.IsChecked, (bool)pageChatRender.checkOutline.IsChecked, (string)pageChatRender.comboFont.SelectedItem, Double.Parse(pageChatRender.textFontSize.Text), Double.Parse(pageChatRender.textUpdateTime.Text), (bool)pageChatRender.checkTimestamp.IsChecked, messageColor, Int32.Parse(pageChatRender.textFramerate.Text), Settings.Default.FfmpegInputArgs, Settings.Default.FfmpegOutputArgs); Size canvasSize = new Size(renderOptions.chat_width, renderOptions.text_height); SKPaint nameFont = new SKPaint() { Typeface = SKTypeface.FromFamilyName(renderOptions.font, SKFontStyle.Bold), LcdRenderText = true, SubpixelText = true, TextSize = (float)renderOptions.font_size, IsAntialias = true, HintingLevel = SKPaintHinting.Full, FilterQuality = SKFilterQuality.High }; SKPaint messageFont = new SKPaint() { Typeface = SKTypeface.FromFamilyName(renderOptions.font, SKFontStyle.Normal), LcdRenderText = true, SubpixelText = true, TextSize = (float)renderOptions.font_size, IsAntialias = true, HintingLevel = SKPaintHinting.Full, FilterQuality = SKFilterQuality.High, Color = renderOptions.message_color }; List <ThirdPartyEmote> thirdPartyEmotes = new List <ThirdPartyEmote>(); Dictionary <string, SKBitmap> chatEmotes = new Dictionary <string, SKBitmap>(); Dictionary <string, SKBitmap> emojiCache = new Dictionary <string, SKBitmap>(); string emojiRegex = "[#*0-9]\uFE0F\u20E3|[\u00A9\u00AE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618]|\u261D(?:\uD83C[\uDFFB-\uDFFF])?|[\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692-\u2697\u2699\u269B\u269C\u26A0\u26A1\u26AA\u26AB\u26B0\u26B1\u26BD\u26BE\u26C4\u26C5\u26C8\u26CE\u26CF\u26D1\u26D3\u26D4\u26E9\u26EA\u26F0-\u26F5\u26F7\u26F8]|\u26F9(?:\uD83C[\uDFFB-\uDFFF](?:\u200D[\u2640\u2642]\uFE0F)?|\uFE0F\u200D[\u2640\u2642]\uFE0F)?|[\u26FA\u26FD\u2702\u2705\u2708\u2709]|[\u270A-\u270D](?:\uD83C[\uDFFB-\uDFFF])?|[\u270F\u2712\u2714\u2716\u271D\u2721\u2728\u2733\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763\u2764\u2795-\u2797\u27A1\u27B0\u27BF\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299]|\uD83C(?:[\uDC04\uDCCF\uDD70\uDD71\uDD7E\uDD7F\uDD8E\uDD91-\uDD9A]|\uDDE6\uD83C[\uDDE8-\uDDEC\uDDEE\uDDF1\uDDF2\uDDF4\uDDF6-\uDDFA\uDDFC\uDDFD\uDDFF]|\uDDE7\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEF\uDDF1-\uDDF4\uDDF6-\uDDF9\uDDFB\uDDFC\uDDFE\uDDFF]|\uDDE8\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDEE\uDDF0-\uDDF5\uDDF7\uDDFA-\uDDFF]|\uDDE9\uD83C[\uDDEA\uDDEC\uDDEF\uDDF0\uDDF2\uDDF4\uDDFF]|\uDDEA\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDED\uDDF7-\uDDFA]|\uDDEB\uD83C[\uDDEE-\uDDF0\uDDF2\uDDF4\uDDF7]|\uDDEC\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEE\uDDF1-\uDDF3\uDDF5-\uDDFA\uDDFC\uDDFE]|\uDDED\uD83C[\uDDF0\uDDF2\uDDF3\uDDF7\uDDF9\uDDFA]|\uDDEE\uD83C[\uDDE8-\uDDEA\uDDF1-\uDDF4\uDDF6-\uDDF9]|\uDDEF\uD83C[\uDDEA\uDDF2\uDDF4\uDDF5]|\uDDF0\uD83C[\uDDEA\uDDEC-\uDDEE\uDDF2\uDDF3\uDDF5\uDDF7\uDDFC\uDDFE\uDDFF]|\uDDF1\uD83C[\uDDE6-\uDDE8\uDDEE\uDDF0\uDDF7-\uDDFB\uDDFE]|\uDDF2\uD83C[\uDDE6\uDDE8-\uDDED\uDDF0-\uDDFF]|\uDDF3\uD83C[\uDDE6\uDDE8\uDDEA-\uDDEC\uDDEE\uDDF1\uDDF4\uDDF5\uDDF7\uDDFA\uDDFF]|\uDDF4\uD83C\uDDF2|\uDDF5\uD83C[\uDDE6\uDDEA-\uDDED\uDDF0-\uDDF3\uDDF7-\uDDF9\uDDFC\uDDFE]|\uDDF6\uD83C\uDDE6|\uDDF7\uD83C[\uDDEA\uDDF4\uDDF8\uDDFA\uDDFC]|\uDDF8\uD83C[\uDDE6-\uDDEA\uDDEC-\uDDF4\uDDF7-\uDDF9\uDDFB\uDDFD-\uDDFF]|\uDDF9\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDED\uDDEF-\uDDF4\uDDF7\uDDF9\uDDFB\uDDFC\uDDFF]|\uDDFA\uD83C[\uDDE6\uDDEC\uDDF2\uDDF3\uDDF8\uDDFE\uDDFF]|\uDDFB\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDEE\uDDF3\uDDFA]|\uDDFC\uD83C[\uDDEB\uDDF8]|\uDDFD\uD83C\uDDF0|\uDDFE\uD83C[\uDDEA\uDDF9]|\uDDFF\uD83C[\uDDE6\uDDF2\uDDFC]|[\uDE01\uDE02\uDE1A\uDE2F\uDE32-\uDE3A\uDE50\uDE51\uDF00-\uDF21\uDF24-\uDF84]|\uDF85(?:\uD83C[\uDFFB-\uDFFF])?|[\uDF86-\uDF93\uDF96\uDF97\uDF99-\uDF9B\uDF9E-\uDFC1]|\uDFC2(?:\uD83C[\uDFFB-\uDFFF])?|[\uDFC3\uDFC4](?:\u200D[\u2640\u2642]\uFE0F|\uD83C[\uDFFB-\uDFFF](?:\u200D[\u2640\u2642]\uFE0F)?)?|[\uDFC5\uDFC6]|\uDFC7(?:\uD83C[\uDFFB-\uDFFF])?|[\uDFC8\uDFC9]|\uDFCA(?:\u200D[\u2640\u2642]\uFE0F|\uD83C[\uDFFB-\uDFFF](?:\u200D[\u2640\u2642]\uFE0F)?)?|[\uDFCB\uDFCC](?:\uD83C[\uDFFB-\uDFFF](?:\u200D[\u2640\u2642]\uFE0F)?|\uFE0F\u200D[\u2640\u2642]\uFE0F)?|[\uDFCD-\uDFF0]|\uDFF3(?:\uFE0F\u200D\uD83C\uDF08)?|\uDFF4(?:\u200D\u2620\uFE0F|\uDB40\uDC67\uDB40\uDC62\uDB40(?:\uDC65\uDB40\uDC6E\uDB40\uDC67|\uDC73\uDB40\uDC63\uDB40\uDC74|\uDC77\uDB40\uDC6C\uDB40\uDC73)\uDB40\uDC7F)?|[\uDFF5\uDFF7-\uDFFF])|\uD83D(?:[\uDC00-\uDC14]|\uDC15(?:\u200D\uD83E\uDDBA)?|[\uDC16-\uDC40]|\uDC41(?:\uFE0F\u200D\uD83D\uDDE8\uFE0F)?|[\uDC42\uDC43](?:\uD83C[\uDFFB-\uDFFF])?|[\uDC44\uDC45]|[\uDC46-\uDC50](?:\uD83C[\uDFFB-\uDFFF])?|[\uDC51-\uDC65]|[\uDC66\uDC67](?:\uD83C[\uDFFB-\uDFFF])?|\uDC68(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F|\u2764\uFE0F\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?|[\uDC68\uDC69]\u200D\uD83D(?:\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?)|[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92])|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD]))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:\uDD1D\u200D\uD83D\uDC68\uD83C\uDFFB|[\uDDAF-\uDDB3\uDDBC\uDDBD])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFB\uDFFC]|[\uDDAF-\uDDB3\uDDBC\uDDBD])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFB-\uDFFD]|[\uDDAF-\uDDB3\uDDBC\uDDBD])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFB-\uDFFE]|[\uDDAF-\uDDB3\uDDBC\uDDBD])))?))?|\uDC69(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F|\u2764\uFE0F\u200D\uD83D(?:\uDC8B\u200D\uD83D)?[\uDC68\uDC69]|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?|\uDC69\u200D\uD83D(?:\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?)|[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92])|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFC-\uDFFF]|[\uDDAF-\uDDB3\uDDBC\uDDBD])))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:\uDD1D\u200D\uD83D(?:\uDC68\uD83C[\uDFFB\uDFFD-\uDFFF]|\uDC69\uD83C\uDFFB)|[\uDDAF-\uDDB3\uDDBC\uDDBD])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:\uDD1D\u200D\uD83D(?:\uDC68\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF]|\uDC69\uD83C[\uDFFB\uDFFC])|[\uDDAF-\uDDB3\uDDBC\uDDBD])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:\uDD1D\u200D\uD83D(?:\uDC68\uD83C[\uDFFB-\uDFFD\uDFFF]|\uDC69\uD83C[\uDFFB-\uDFFD])|[\uDDAF-\uDDB3\uDDBC\uDDBD])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB-\uDFFE]|[\uDDAF-\uDDB3\uDDBC\uDDBD])))?))?|\uDC6A|[\uDC6B-\uDC6D](?:\uD83C[\uDFFB-\uDFFF])?|\uDC6E(?:\u200D[\u2640\u2642]\uFE0F|\uD83C[\uDFFB-\uDFFF](?:\u200D[\u2640\u2642]\uFE0F)?)?|\uDC6F(?:\u200D[\u2640\u2642]\uFE0F)?|\uDC70(?:\uD83C[\uDFFB-\uDFFF])?|\uDC71(?:\u200D[\u2640\u2642]\uFE0F|\uD83C[\uDFFB-\uDFFF](?:\u200D[\u2640\u2642]\uFE0F)?)?|\uDC72(?:\uD83C[\uDFFB-\uDFFF])?|\uDC73(?:\u200D[\u2640\u2642]\uFE0F|\uD83C[\uDFFB-\uDFFF](?:\u200D[\u2640\u2642]\uFE0F)?)?|[\uDC74-\uDC76](?:\uD83C[\uDFFB-\uDFFF])?|\uDC77(?:\u200D[\u2640\u2642]\uFE0F|\uD83C[\uDFFB-\uDFFF](?:\u200D[\u2640\u2642]\uFE0F)?)?|\uDC78(?:\uD83C[\uDFFB-\uDFFF])?|[\uDC79-\uDC7B]|\uDC7C(?:\uD83C[\uDFFB-\uDFFF])?|[\uDC7D-\uDC80]|[\uDC81\uDC82](?:\u200D[\u2640\u2642]\uFE0F|\uD83C[\uDFFB-\uDFFF](?:\u200D[\u2640\u2642]\uFE0F)?)?|\uDC83(?:\uD83C[\uDFFB-\uDFFF])?|\uDC84|\uDC85(?:\uD83C[\uDFFB-\uDFFF])?|[\uDC86\uDC87](?:\u200D[\u2640\u2642]\uFE0F|\uD83C[\uDFFB-\uDFFF](?:\u200D[\u2640\u2642]\uFE0F)?)?|[\uDC88-\uDCA9]|\uDCAA(?:\uD83C[\uDFFB-\uDFFF])?|[\uDCAB-\uDCFD\uDCFF-\uDD3D\uDD49-\uDD4E\uDD50-\uDD67\uDD6F\uDD70\uDD73]|\uDD74(?:\uD83C[\uDFFB-\uDFFF])?|\uDD75(?:\uD83C[\uDFFB-\uDFFF](?:\u200D[\u2640\u2642]\uFE0F)?|\uFE0F\u200D[\u2640\u2642]\uFE0F)?|[\uDD76-\uDD79]|\uDD7A(?:\uD83C[\uDFFB-\uDFFF])?|[\uDD87\uDD8A-\uDD8D]|[\uDD90\uDD95\uDD96](?:\uD83C[\uDFFB-\uDFFF])?|[\uDDA4\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA-\uDE44]|[\uDE45-\uDE47](?:\u200D[\u2640\u2642]\uFE0F|\uD83C[\uDFFB-\uDFFF](?:\u200D[\u2640\u2642]\uFE0F)?)?|[\uDE48-\uDE4A]|\uDE4B(?:\u200D[\u2640\u2642]\uFE0F|\uD83C[\uDFFB-\uDFFF](?:\u200D[\u2640\u2642]\uFE0F)?)?|\uDE4C(?:\uD83C[\uDFFB-\uDFFF])?|[\uDE4D\uDE4E](?:\u200D[\u2640\u2642]\uFE0F|\uD83C[\uDFFB-\uDFFF](?:\u200D[\u2640\u2642]\uFE0F)?)?|\uDE4F(?:\uD83C[\uDFFB-\uDFFF])?|[\uDE80-\uDEA2]|\uDEA3(?:\u200D[\u2640\u2642]\uFE0F|\uD83C[\uDFFB-\uDFFF](?:\u200D[\u2640\u2642]\uFE0F)?)?|[\uDEA4-\uDEB3]|[\uDEB4-\uDEB6](?:\u200D[\u2640\u2642]\uFE0F|\uD83C[\uDFFB-\uDFFF](?:\u200D[\u2640\u2642]\uFE0F)?)?|[\uDEB7-\uDEBF]|\uDEC0(?:\uD83C[\uDFFB-\uDFFF])?|[\uDEC1-\uDEC5\uDECB]|\uDECC(?:\uD83C[\uDFFB-\uDFFF])?|[\uDECD-\uDED2\uDED5\uDEE0-\uDEE5\uDEE9\uDEEB\uDEEC\uDEF0\uDEF3-\uDEFA\uDFE0-\uDFEB])|\uD83E(?:[\uDD0D\uDD0E]|\uDD0F(?:\uD83C[\uDFFB-\uDFFF])?|[\uDD10-\uDD17]|[\uDD18-\uDD1C](?:\uD83C[\uDFFB-\uDFFF])?|\uDD1D|[\uDD1E\uDD1F](?:\uD83C[\uDFFB-\uDFFF])?|[\uDD20-\uDD25]|\uDD26(?:\u200D[\u2640\u2642]\uFE0F|\uD83C[\uDFFB-\uDFFF](?:\u200D[\u2640\u2642]\uFE0F)?)?|[\uDD27-\uDD2F]|[\uDD30-\uDD36](?:\uD83C[\uDFFB-\uDFFF])?|\uDD37(?:\u200D[\u2640\u2642]\uFE0F|\uD83C[\uDFFB-\uDFFF](?:\u200D[\u2640\u2642]\uFE0F)?)?|[\uDD38\uDD39](?:\u200D[\u2640\u2642]\uFE0F|\uD83C[\uDFFB-\uDFFF](?:\u200D[\u2640\u2642]\uFE0F)?)?|\uDD3A|\uDD3C(?:\u200D[\u2640\u2642]\uFE0F)?|[\uDD3D\uDD3E](?:\u200D[\u2640\u2642]\uFE0F|\uD83C[\uDFFB-\uDFFF](?:\u200D[\u2640\u2642]\uFE0F)?)?|[\uDD3F-\uDD45\uDD47-\uDD71\uDD73-\uDD76\uDD7A-\uDDA2\uDDA5-\uDDAA\uDDAE-\uDDB4]|[\uDDB5\uDDB6](?:\uD83C[\uDFFB-\uDFFF])?|\uDDB7|[\uDDB8\uDDB9](?:\u200D[\u2640\u2642]\uFE0F|\uD83C[\uDFFB-\uDFFF](?:\u200D[\u2640\u2642]\uFE0F)?)?|\uDDBA|\uDDBB(?:\uD83C[\uDFFB-\uDFFF])?|[\uDDBC-\uDDCA]|[\uDDCD-\uDDCF](?:\u200D[\u2640\u2642]\uFE0F|\uD83C[\uDFFB-\uDFFF](?:\u200D[\u2640\u2642]\uFE0F)?)?|\uDDD0|\uDDD1(?:\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83C(?:\uDFFB(?:\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1\uD83C\uDFFB)?|\uDFFC(?:\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB\uDFFC])?|\uDFFD(?:\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFD])?|\uDFFE(?:\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFE])?|\uDFFF(?:\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])?))?|[\uDDD2-\uDDD5](?:\uD83C[\uDFFB-\uDFFF])?|\uDDD6(?:\u200D[\u2640\u2642]\uFE0F|\uD83C[\uDFFB-\uDFFF](?:\u200D[\u2640\u2642]\uFE0F)?)?|[\uDDD7-\uDDDD](?:\u200D[\u2640\u2642]\uFE0F|\uD83C[\uDFFB-\uDFFF](?:\u200D[\u2640\u2642]\uFE0F)?)?|[\uDDDE\uDDDF](?:\u200D[\u2640\u2642]\uFE0F)?|[\uDDE0-\uDDFF\uDE70-\uDE73\uDE78-\uDE7A\uDE80-\uDE82\uDE90-\uDE95])"; using (SKCanvas previewCanvas = new SKCanvas(previewBitmap)) { previewCanvas.Clear(backgroundColor); foreach (PreviewEmote previewDataEmote in previewData.emotes) { byte[] imageBytes = Convert.FromBase64String(previewDataEmote.image); SKBitmap emoteBitmap = SKBitmap.Decode(imageBytes); SKCodec emoteCodec; using (MemoryStream ms = new MemoryStream(imageBytes)) emoteCodec = SKCodec.Create(ms); ThirdPartyEmote emote = new ThirdPartyEmote(new List <SKBitmap>() { emoteBitmap }, emoteCodec, previewDataEmote.name, ".png", "0", 1); thirdPartyEmotes.Add(emote); } foreach (PreviewComment previewComment in previewData.comments) { int default_x = 2; Point drawPos = new Point(default_x, 0); string userName = previewComment.name; SKColor userColor = new SKColor(Convert.ToByte(previewComment.color.Substring(0, 2), 16), Convert.ToByte(previewComment.color.Substring(2, 2), 16), Convert.ToByte(previewComment.color.Substring(4, 2), 16)); List <SKBitmap> imageList = new List <SKBitmap>(); SKBitmap sectionImage = new SKBitmap((int)canvasSize.Width, (int)canvasSize.Height); List <GifEmote> currentGifEmotes = new List <GifEmote>(); List <SKBitmap> emoteList = new List <SKBitmap>(); List <CheerEmote> cheerEmotes = new List <CheerEmote>(); List <SKRect> emotePositionList = new List <SKRect>(); new SKCanvas(sectionImage).Clear(renderOptions.background_color); Comment comment = new Comment(); comment.message = new Message(); Fragment msg = new Fragment(); msg.text = previewComment.message; comment.message.fragments = new List <Fragment>(); comment.message.fragments.Add(msg); if (renderOptions.chat_timestamp) { sectionImage = pageChatRender.DrawTimestamp(sectionImage, imageList, messageFont, renderOptions, comment, canvasSize, ref drawPos, ref default_x); } if (previewComment.badges != null) { sectionImage = DrawBadges(sectionImage, imageList, renderOptions, canvasSize, ref drawPos, previewComment); } sectionImage = pageChatRender.DrawUsername(sectionImage, imageList, renderOptions, nameFont, userName, userColor, canvasSize, ref drawPos); sectionImage = pageChatRender.DrawMessage(sectionImage, imageList, renderOptions, currentGifEmotes, messageFont, emojiCache, chatEmotes, thirdPartyEmotes, cheerEmotes, comment, canvasSize, ref drawPos, emojiRegex, ref default_x, emoteList, emotePositionList); int finalHeight = 0; foreach (var img in imageList) { finalHeight += img.Height; } SKBitmap finalImage = new SKBitmap((int)canvasSize.Width, finalHeight); SKCanvas finalImageCanvas = new SKCanvas(finalImage); finalHeight = 0; foreach (var img in imageList) { finalImageCanvas.DrawBitmap(img, 0, finalHeight); finalHeight += img.Height; img.Dispose(); } finalComments.Add(new TwitchCommentPreview(finalImage, Double.Parse(comment.content_offset_seconds.ToString()), currentGifEmotes, emoteList, emotePositionList)); } int y = 0; int tempHeight = 0; foreach (TwitchCommentPreview twitchCommentPreview in finalComments) { tempHeight += twitchCommentPreview.section.Height; } SKBitmap tempBitmap = new SKBitmap((int)this.Width, tempHeight); using (SKCanvas tempCanvas = new SKCanvas(tempBitmap)) { foreach (TwitchCommentPreview twitchCommentPreview in finalComments) { tempCanvas.DrawBitmap(twitchCommentPreview.section, 0, y, imagePaint); for (int i = 0; i < twitchCommentPreview.normalEmotes.Count; i++) { SKRect refrenceRect = twitchCommentPreview.normalEmotesPositions[i]; tempCanvas.DrawBitmap(twitchCommentPreview.normalEmotes[i], new SKRect(refrenceRect.Left, refrenceRect.Top + y, refrenceRect.Right, refrenceRect.Bottom + y), imagePaint); } y += twitchCommentPreview.section.Height; } } previewCanvas.DrawBitmap(tempBitmap, 0, previewBitmap.Height - tempBitmap.Height); } using (var stream = new MemoryStream()) { SKImage.FromBitmap(previewBitmap).Encode(SKEncodedImageFormat.Png, 100).SaveTo(stream); var bitmap = new BitmapImage(); bitmap.BeginInit(); bitmap.StreamSource = stream; bitmap.CacheOption = BitmapCacheOption.OnLoad; bitmap.EndInit(); bitmap.Freeze(); imgPreview.Source = bitmap; } } catch { try { this.Width = 500; this.Height = 338; imgPreview.Width = 500; imgPreview.Height = 300; SKBitmap errorBitmap = new SKBitmap(500, 300); using (SKCanvas skCanvas = new SKCanvas(errorBitmap)) { skCanvas.DrawText("ERROR, UNABLE TO RENDER CHAT", 40, 150, new SKPaint() { Typeface = SKTypeface.FromFamilyName("Arial", SKFontStyle.Bold), TextSize = 18, IsAntialias = true, FilterQuality = SKFilterQuality.High }); SKBitmap peepo = SKBitmap.Decode(Application .GetResourceStream(new Uri("pack://application:,,,/Images/peepoSad.png")).Stream); skCanvas.DrawBitmap(peepo, 370, 132); } using (var stream = new MemoryStream()) { SKImage.FromBitmap(errorBitmap).Encode(SKEncodedImageFormat.Png, 100).SaveTo(stream); var bitmap = new BitmapImage(); bitmap.BeginInit(); bitmap.StreamSource = stream; bitmap.CacheOption = BitmapCacheOption.OnLoad; bitmap.EndInit(); bitmap.Freeze(); imgPreview.Source = bitmap; } } catch { } } }
}//end write JPEG public bool writeJpeg(CloudCoin cc, string tag, string filePath, string targetPath, string printMessage) { // Console.Out.WriteLine("Writing jpeg " + cc.sn); // CoinUtils cu = new CoinUtils(cc); filePath = filePath.Replace("\\\\", "\\"); bool fileSavedSuccessfully = true; /* BUILD THE CLOUDCOIN STRING */ String cloudCoinStr = "01C34A46494600010101006000601D05"; //THUMBNAIL HEADER BYTES for (int i = 0; (i < 25); i++) { cloudCoinStr = cloudCoinStr + cc.an[i]; } // end for each an //cloudCoinStr += "204f42455920474f4420262044454645415420545952414e545320";// Hex for " OBEY GOD & DEFEAT TYRANTS " //cloudCoinStr += "20466f756e6465727320372d352d3137";// Founders 7-5-17 cloudCoinStr += "4c6976652046726565204f7220446965"; // Live Free or Die cloudCoinStr += "00000000000000000000000000"; //Set to unknown so program does not export user data // for (int i =0; i < 25; i++) { // switch () { }//end switch pown char // }//end for each pown cloudCoinStr += "00"; // HC: Has comments. 00 = No cc.CalcExpirationDate(); cloudCoinStr += cc.edHex; // 01;//Expiration date Sep 2016 (one month after zero month) cloudCoinStr += "01"; // cc.nn;//network number String hexSN = cc.sn.ToString("X6"); String fullHexSN = ""; switch (hexSN.Length) { case 1: fullHexSN = ("00000" + hexSN); break; case 2: fullHexSN = ("0000" + hexSN); break; case 3: fullHexSN = ("000" + hexSN); break; case 4: fullHexSN = ("00" + hexSN); break; case 5: fullHexSN = ("0" + hexSN); break; case 6: fullHexSN = hexSN; break; } cloudCoinStr = (cloudCoinStr + fullHexSN); /* BYTES THAT WILL GO FROM 04 to 454 (Inclusive)*/ byte[] ccArray = this.hexStringToByteArray(cloudCoinStr); /* READ JPEG TEMPLATE*/ byte[] jpegBytes = null; //jpegBytes = readAllBytes(filePath); jpegBytes = File.ReadAllBytes(filePath); /* WRITE THE SERIAL NUMBER ON THE JPEG */ //Bitmap bitmapimage; SKBitmap bitmapimage; //using (var ms = new MemoryStream(jpegBytes)) { //bitmapimage = new Bitmap(ms); bitmapimage = SKBitmap.Decode(jpegBytes); } SKCanvas canvas = new SKCanvas(bitmapimage); //Graphics graphics = Graphics.FromImage(bitmapimage); //graphics.SmoothingMode = SmoothingMode.AntiAlias; //graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; SKPaint textPaint = new SKPaint() { IsAntialias = true, Color = SKColors.White, TextSize = 14, Typeface = SKTypeface.FromFamilyName("Arial") }; //PointF drawPointAddress = new PointF(30.0F, 25.0F); canvas.DrawText(printMessage, 30, 40, textPaint); //graphics.DrawString(String.Format("{0:N0}", cc.sn) + " of 16,777,216 on Network: 1", new Font("Arial", 10), Brushes.White, drawPointAddress); //ImageConverter converter = new ImageConverter(); //byte[] snBytes = (byte[])converter.ConvertTo(bitmapimage, typeof(byte[])); SKImage image = SKImage.FromBitmap(bitmapimage); SKData data = image.Encode(SKEncodedImageFormat.Jpeg, 100); byte[] snBytes = data.ToArray(); List <byte> b1 = new List <byte>(snBytes); List <byte> b2 = new List <byte>(ccArray); b1.InsertRange(4, b2); if (tag == "random") { Random r = new Random(); int rInt = r.Next(100000, 1000000); //for ints tag = rInt.ToString(); } string fileName = targetPath; File.WriteAllBytes(fileName, b1.ToArray()); Console.Out.WriteLine("Writing to " + fileName); //CoreLogger.Log("Writing to " + fileName); return(fileSavedSuccessfully); }//end write JPEG
public void Update(PageChatRender pageChatRender) { try { this.Width = Int32.Parse(pageChatRender.textWidth.Text) + 10; this.Height = Int32.Parse(pageChatRender.textHeight.Text) + 38; imgPreview.Height = Int32.Parse(pageChatRender.textHeight.Text); imgPreview.Width = Int32.Parse(pageChatRender.textWidth.Text); PreviewData previewData = JsonConvert.DeserializeObject<PreviewData>(File.ReadAllText("preview_data.json")); BlockingCollection<TwitchCommentPreview> finalComments = new BlockingCollection<TwitchCommentPreview>(); SKBitmap previewBitmap = new SKBitmap((int)this.Width, (int)imgPreview.Height); SKColor backgroundColor = new SKColor(pageChatRender.colorBackground.SelectedColor.Value.R, pageChatRender.colorBackground.SelectedColor.Value.G, pageChatRender.colorBackground.SelectedColor.Value.B); SKColor messageColor = new SKColor(pageChatRender.colorFont.SelectedColor.Value.R, pageChatRender.colorFont.SelectedColor.Value.G, pageChatRender.colorFont.SelectedColor.Value.B); ChatRenderOptions renderOptions = new ChatRenderOptions() { InputFile = pageChatRender.textJson.Text, OutputFile = "", BackgroundColor = backgroundColor, ChatHeight = Int32.Parse(pageChatRender.textHeight.Text), ChatWidth = Int32.Parse(pageChatRender.textWidth.Text), BttvEmotes = (bool)pageChatRender.checkBTTV.IsChecked, FfzEmotes = (bool)pageChatRender.checkFFZ.IsChecked, Outline = (bool)pageChatRender.checkOutline.IsChecked, Font = (string)pageChatRender.comboFont.SelectedItem, FontSize = Double.Parse(pageChatRender.textFontSize.Text), UpdateRate = Double.Parse(pageChatRender.textUpdateTime.Text), Timestamp = (bool)pageChatRender.checkTimestamp.IsChecked, MessageColor = messageColor, Framerate = Int32.Parse(pageChatRender.textFramerate.Text), InputArgs = Settings.Default.FfmpegInputArgs, OutputArgs = Settings.Default.FfmpegOutputArgs, MessageFontStyle = SKFontStyle.Normal, UsernameFontStyle = SKFontStyle.Bold }; System.Drawing.Size canvasSize = new System.Drawing.Size(renderOptions.ChatWidth, renderOptions.SectionHeight); SKPaint nameFont = new SKPaint() { Typeface = SKTypeface.FromFamilyName(renderOptions.Font, renderOptions.UsernameFontStyle), LcdRenderText = true, SubpixelText = true, TextSize = (float)renderOptions.FontSize, IsAntialias = true, HintingLevel = SKPaintHinting.Full, FilterQuality = SKFilterQuality.High }; SKPaint messageFont = new SKPaint() { Typeface = SKTypeface.FromFamilyName(renderOptions.Font, renderOptions.MessageFontStyle), LcdRenderText = true, SubpixelText = true, TextSize = (float)renderOptions.FontSize, IsAntialias = true, HintingLevel = SKPaintHinting.Full, FilterQuality = SKFilterQuality.High, Color = renderOptions.MessageColor }; List<TwitchEmote> thirdPartyEmotes = new List<TwitchEmote>(); List<TwitchEmote> chatEmotes = new List<TwitchEmote>(); Dictionary<string, SKBitmap> emojiCache = new Dictionary<string, SKBitmap>(); using (SKCanvas previewCanvas = new SKCanvas(previewBitmap)) { previewCanvas.Clear(backgroundColor); foreach (PreviewEmote previewDataEmote in previewData.emotes) { byte[] imageBytes = Convert.FromBase64String(previewDataEmote.image); SKBitmap emoteBitmap = SKBitmap.Decode(imageBytes); SKCodec emoteCodec; using (MemoryStream ms = new MemoryStream(imageBytes)) emoteCodec = SKCodec.Create(ms); TwitchEmote emote = new TwitchEmote(new List<SKBitmap>() { emoteBitmap }, emoteCodec, previewDataEmote.name, ".png", "0", 1, imageBytes); thirdPartyEmotes.Add(emote); } foreach (PreviewComment previewComment in previewData.comments) { int default_x = 2; System.Drawing.Point drawPos = new System.Drawing.Point(default_x, 0); string userName = previewComment.name; SKColor userColor = new SKColor(Convert.ToByte(previewComment.color.Substring(0, 2), 16), Convert.ToByte(previewComment.color.Substring(2, 2), 16), Convert.ToByte(previewComment.color.Substring(4, 2), 16)); List<SKBitmap> imageList = new List<SKBitmap>(); SKBitmap sectionImage = new SKBitmap(canvasSize.Width, canvasSize.Height); List<GifEmote> currentGifEmotes = new List<GifEmote>(); List<SKBitmap> emoteList = new List<SKBitmap>(); List<CheerEmote> cheerEmotes = new List<CheerEmote>(); List<SKRect> emotePositionList = new List<SKRect>(); new SKCanvas(sectionImage).Clear(renderOptions.BackgroundColor); Comment comment = new Comment(); comment.message = new Message(); Fragment msg = new Fragment(); msg.text = previewComment.message; comment.message.fragments = new List<Fragment>(); comment.message.fragments.Add(msg); if (renderOptions.Timestamp) sectionImage = ChatRenderer.DrawTimestamp(sectionImage, imageList, messageFont, renderOptions, comment, canvasSize, ref drawPos, ref default_x); if (previewComment.badges != null) sectionImage = DrawBadges(sectionImage, imageList, renderOptions, canvasSize, ref drawPos, previewComment); sectionImage = ChatRenderer.DrawUsername(sectionImage, imageList, renderOptions, nameFont, userName, userColor, canvasSize, ref drawPos); sectionImage = ChatRenderer.DrawMessage(sectionImage, imageList, renderOptions, currentGifEmotes, messageFont, emojiCache, chatEmotes, thirdPartyEmotes, cheerEmotes, comment, canvasSize, ref drawPos, ref default_x, emoteList, emotePositionList); int finalHeight = 0; foreach (var img in imageList) finalHeight += img.Height; SKBitmap finalImage = new SKBitmap(canvasSize.Width, finalHeight); SKCanvas finalImageCanvas = new SKCanvas(finalImage); finalHeight = 0; foreach (var img in imageList) { finalImageCanvas.DrawBitmap(img, 0, finalHeight); finalHeight += img.Height; img.Dispose(); } finalComments.Add(new TwitchCommentPreview(finalImage, Double.Parse(comment.content_offset_seconds.ToString()), currentGifEmotes, emoteList, emotePositionList)); } int y = 0; int tempHeight = 0; foreach (TwitchCommentPreview twitchCommentPreview in finalComments) tempHeight += twitchCommentPreview.section.Height; SKBitmap tempBitmap = new SKBitmap((int)this.Width, tempHeight); using (SKCanvas tempCanvas = new SKCanvas(tempBitmap)) { foreach (TwitchCommentPreview twitchCommentPreview in finalComments) { tempCanvas.DrawBitmap(twitchCommentPreview.section, 0, y, imagePaint); for (int i = 0; i < twitchCommentPreview.normalEmotes.Count; i++) { SKRect refrenceRect = twitchCommentPreview.normalEmotesPositions[i]; tempCanvas.DrawBitmap(twitchCommentPreview.normalEmotes[i], new SKRect(refrenceRect.Left, refrenceRect.Top + y, refrenceRect.Right, refrenceRect.Bottom + y), imagePaint); } y += twitchCommentPreview.section.Height; } } previewCanvas.DrawBitmap(tempBitmap, 0, previewBitmap.Height - tempBitmap.Height); } using (var stream = new MemoryStream()) { SKImage.FromBitmap(previewBitmap).Encode(SKEncodedImageFormat.Png, 100).SaveTo(stream); var bitmap = new BitmapImage(); bitmap.BeginInit(); bitmap.StreamSource = stream; bitmap.CacheOption = BitmapCacheOption.OnLoad; bitmap.EndInit(); bitmap.Freeze(); imgPreview.Source = bitmap; } } catch { try { this.Width = 500; this.Height = 338; imgPreview.Width = 500; imgPreview.Height = 300; SKBitmap errorBitmap = new SKBitmap(500, 300); using (SKCanvas skCanvas = new SKCanvas(errorBitmap)) { skCanvas.DrawText("ERROR, UNABLE TO RENDER CHAT", 40, 150, new SKPaint() { Typeface = SKTypeface.FromFamilyName("Arial", SKFontStyle.Bold), TextSize = 18, IsAntialias = true, FilterQuality = SKFilterQuality.High }); SKBitmap peepo = SKBitmap.Decode(Application .GetResourceStream(new Uri("pack://application:,,,/Images/peepoSad.png")).Stream); skCanvas.DrawBitmap(peepo, 370, 132); } using (var stream = new MemoryStream()) { SKImage.FromBitmap(errorBitmap).Encode(SKEncodedImageFormat.Png, 100).SaveTo(stream); var bitmap = new BitmapImage(); bitmap.BeginInit(); bitmap.StreamSource = stream; bitmap.CacheOption = BitmapCacheOption.OnLoad; bitmap.EndInit(); bitmap.Freeze(); imgPreview.Source = bitmap; } } catch { } } }
public async Task drawGaugeAsync() { // Radial Gauge Constants int uPadding = 150; int side = 500; int radialGaugeWidth = 55; // Line TextSize inside Radial Gauge int lineSize1 = 220; int lineSize2 = 70; int lineSize3 = 80; int lineSize4 = 100; // Line Y Coordinate inside Radial Gauge int lineHeight1 = 70; int lineHeight2 = 170; int lineHeight3 = 270; int lineHeight4 = 370; int lineHeight5 = 530; // Start & End Angle for Radial Gauge float startAngle = -220; float sweepAngle = 260; try { // Getting Canvas Info SKImageInfo info = args.Info; SKSurface surface = args.Surface; SKCanvas canvas = surface.Canvas; progressUtils.setDevice(info.Height, info.Width); canvas.Clear(); SKBlendMode blend = SKBlendMode.SrcIn; canvas.DrawColor(Color.White.ToSKColor(), blend); // Getting Device Specific Screen Values // ------------------------------------------------- // Top Padding for Radial Gauge float upperPading = progressUtils.getFactoredHeight(uPadding); /* Coordinate Plotting for Radial Gauge * * (X1,Y1) ------------ * | (XC,YC) | * | . | * Y | | * | | * ------------ (X2,Y2)) * X * * To fit a perfect Circle inside --> X==Y * i.e It should be a Square */ // Xc & Yc are center of the Circle int Xc = info.Width / 2; float Yc = progressUtils.getFactoredHeight(side); // X1 Y1 are lefttop cordiates of rectange int X1 = (int)(Xc - Yc); int Y1 = (int)(Yc - Yc + upperPading); // X2 Y2 are rightbottom cordiates of rectange int X2 = (int)(Xc + Yc); int Y2 = (int)(Yc + Yc + upperPading); //Loggig Screen Specific Calculated Values Debug.WriteLine("INFO " + info.Width + " - " + info.Height); Debug.WriteLine(" C : " + upperPading + " " + info.Height); Debug.WriteLine(" C : " + Xc + " " + Yc); Debug.WriteLine("XY : " + X1 + " " + Y1); Debug.WriteLine("XY : " + X2 + " " + Y2); // Empty Gauge Styling SKPaint paint1 = new SKPaint { Style = SKPaintStyle.Stroke, Color = Color.FromHex("#e0dfdf").ToSKColor(), // Colour of Radial Gauge StrokeWidth = progressUtils.getFactoredWidth(radialGaugeWidth), // Width of Radial Gauge StrokeCap = SKStrokeCap.Round // Round Corners for Radial Gauge }; // Filled Gauge Styling SKPaint paint2 = new SKPaint { Style = SKPaintStyle.Stroke, Color = Color.FromHex("#05c782").ToSKColor(), // Overlay Colour of Radial Gauge StrokeWidth = progressUtils.getFactoredWidth(radialGaugeWidth), // Overlay Width of Radial Gauge StrokeCap = SKStrokeCap.Round // Round Corners for Radial Gauge }; // Defining boundaries for Gauge SKRect rect = new SKRect(X1, Y1, X2, Y2); //canvas.DrawRect(rect, paint1); //canvas.DrawOval(rect, paint1); // Rendering Empty Gauge SKPath path1 = new SKPath(); path1.AddArc(rect, startAngle, sweepAngle); canvas.DrawPath(path1, paint1); // Rendering Filled Gauge SKPath path2 = new SKPath(); path2.AddArc(rect, startAngle, (float)sweepAngleSlider.Value); canvas.DrawPath(path2, paint2); //---------------- Drawing Text Over Gauge --------------------------- // Achieved Minutes using (SKPaint skPaint = new SKPaint()) { skPaint.Style = SKPaintStyle.Fill; skPaint.IsAntialias = true; skPaint.Color = SKColor.Parse("#676a69"); skPaint.TextAlign = SKTextAlign.Center; skPaint.TextSize = progressUtils.getFactoredHeight(lineSize1); skPaint.Typeface = SKTypeface.FromFamilyName( "Arial", SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright); // Drawing Achieved Minutes Over Radial Gauge // if (sw_listToggle.IsToggled) // canvas.DrawText(chartvalue + "", Xc, Yc + progressUtils.getFactoredHeight(lineHeight1), skPaint); // else // canvas.DrawText(dailyWorkout + "", Xc, Yc + progressUtils.getFactoredHeight(lineHeight1), skPaint); } // Achieved Minutes Text Styling using (SKPaint skPaint = new SKPaint()) { skPaint.Style = SKPaintStyle.Fill; skPaint.IsAntialias = true; skPaint.Color = SKColor.Parse("#676a69"); skPaint.TextAlign = SKTextAlign.Center; skPaint.TextSize = progressUtils.getFactoredHeight(lineSize2); // canvas.DrawText("Seconds", Xc, Yc + progressUtils.getFactoredHeight(lineHeight2), skPaint); } // Goal Minutes Text Styling using (SKPaint skPaint = new SKPaint()) { skPaint.Style = SKPaintStyle.Fill; skPaint.IsAntialias = true; skPaint.Color = SKColor.Parse("#e2797a"); skPaint.TextAlign = SKTextAlign.Center; skPaint.TextSize = progressUtils.getFactoredHeight(lineSize3); skPaint.Typeface = SKTypeface.FromFamilyName( "Arial", SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright); // Drawing Text Over Radial Gauge // if (sw_listToggle.IsToggled) // canvas.DrawText(DateTime.Now.ToString("dddd, dd'-'MM'-'yyyy"), Xc, Yc + progressUtils.getFactoredHeight(lineHeight3), skPaint); //else { // canvas.DrawText("Goal " + goal / 30 + " Min", Xc, Yc + progressUtils.getFactoredHeight(lineHeight3), skPaint); } } // Goal Minutes Text Styling using (SKPaint skPaint = new SKPaint()) { skPaint.Style = SKPaintStyle.Fill; skPaint.IsAntialias = true; skPaint.Color = SKColor.Parse("#e2797a"); skPaint.TextAlign = SKTextAlign.Center; skPaint.TextSize = progressUtils.getFactoredHeight(lineSize3); skPaint.Typeface = SKTypeface.FromFamilyName( "Arial", SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright); // Drawing Text Over Radial Gauge // if (sw_listToggle.IsToggled) // canvas.DrawText(DateTime.Now.ToString("HH:mm:ss"), Xc, Yc + progressUtils.getFactoredHeight(lineHeight4), skPaint); //else { // canvas.DrawText("Goal " + goal / 30 + " Min", Xc, Yc + progressUtils.getFactoredHeight(lineHeight3), skPaint); } } // Goal Minutes Text Styling using (SKPaint skPaint = new SKPaint()) { skPaint.Style = SKPaintStyle.Fill; skPaint.IsAntialias = true; skPaint.Color = SKColor.Parse("#676a69"); skPaint.TextAlign = SKTextAlign.Center; skPaint.TextSize = progressUtils.getFactoredHeight(lineSize4); skPaint.Typeface = SKTypeface.FromFamilyName( "Arial", SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright); // Drawing Text Over Radial Gauge // if (sw_listToggle.IsToggled) canvas.DrawText("Battery " + Chartvalue + "%", Xc, Yc + progressUtils.getFactoredHeight(lineHeight5), skPaint); //else { // canvas.DrawText("Goal " + goal / 30 + " Min", Xc, Yc + progressUtils.getFactoredHeight(lineHeight3), skPaint); } } } catch (Exception e) { Debug.WriteLine(e.StackTrace); } }
public static void DrawCaptionLabels(this SKCanvas canvas, string label, SKColor labelColor, bool labelIsUnicode, char unicodeLang, string value, SKColor valueColor, float textSize, SKPoint point, SKTextAlign horizontalAlignment, SKTypeface typeface, out SKRect totalBounds) { var hasLabel = !string.IsNullOrEmpty(label); var hasValueLabel = !string.IsNullOrEmpty(value); totalBounds = new SKRect(); if (hasLabel || hasValueLabel) { var hasOffset = hasLabel && hasValueLabel; var captionMargin = textSize * 0.60f; var space = hasOffset ? captionMargin : 0; if (hasLabel) { using (var paint = new SKPaint { TextSize = textSize, IsAntialias = true, Color = labelColor, IsStroke = false, TextAlign = horizontalAlignment, Typeface = typeface }) { var bounds = new SKRect(); var text = label; paint.MeasureText(text, ref bounds); var y = point.Y - ((bounds.Top + bounds.Bottom) / 2) - space; if (labelIsUnicode) { using (var tf = SKFontManager.Default.MatchCharacter(unicodeLang)) using (var shaper = new SKShaper(tf)) { canvas.DrawShapedText(shaper, text, 0, 0, paint); } } else { canvas.DrawText(text, point.X, y, paint); } var labelBounds = GetAbsolutePositionRect(point.X, y, bounds, horizontalAlignment); totalBounds = labelBounds.Standardized; } } if (hasValueLabel) { using (var paint = new SKPaint() { TextSize = textSize, IsAntialias = true, FakeBoldText = true, Color = valueColor, IsStroke = false, TextAlign = horizontalAlignment, Typeface = typeface }) { var bounds = new SKRect(); var text = value; paint.MeasureText(text, ref bounds); var y = point.Y - ((bounds.Top + bounds.Bottom) / 2) + space; canvas.DrawText(text, point.X, y, paint); var valueBounds = GetAbsolutePositionRect(point.X, y, bounds, horizontalAlignment); if (totalBounds.IsEmpty) { totalBounds = valueBounds; } else { totalBounds.Union(valueBounds); } } } } }
/// <summary> /// Load a font from a file /// </summary> /// <param name="fileName">The path to the input file</param> /// <returns>The loaded font</returns> public static SKTypeface LoadFont(string fileName) { using (var stream = File.OpenRead(fileName)) using (var data = SKData.Create(stream)) return(SKTypeface.FromData(data)); }
public static SKTypeface SFBold() { var customFontPath = NSBundle.MainBundle.PathForResource("SanFranciscoDisplay-Bold", ".otf", "Fonts"); return(SKTypeface.FromFile(customFontPath)); }
internal static void DrawLabel(SKCanvas canvas, Orientation orientation, YPositionBehavior yPositionBehavior, SKSize itemSize, SKPoint point, SKColor color, SKRect bounds, string text, float textSize, SKTypeface typeface) { using (new SKAutoCanvasRestore(canvas)) { using (var paint = new SKPaint()) { paint.TextSize = textSize; paint.IsAntialias = true; paint.Color = color; paint.IsStroke = false; paint.Typeface = typeface; if (orientation == Orientation.Vertical) { var y = point.Y; switch (yPositionBehavior) { case YPositionBehavior.UpToElementHeight: y -= bounds.Width; break; case YPositionBehavior.UpToElementMiddle: y -= bounds.Width / 2; break; case YPositionBehavior.DownToElementMiddle: y += bounds.Width / 2; break; case YPositionBehavior.None: default: break; } canvas.RotateDegrees(90); canvas.Translate(y, -point.X + (bounds.Height / 2)); } else { if (bounds.Width > itemSize.Width) { text = text.Substring(0, Math.Min(3, text.Length)); paint.MeasureText(text, ref bounds); } if (bounds.Width > itemSize.Width) { text = text.Substring(0, Math.Min(1, text.Length)); paint.MeasureText(text, ref bounds); } var y = point.Y; switch (yPositionBehavior) { case YPositionBehavior.UpToElementHeight: y -= bounds.Height; break; case YPositionBehavior.UpToElementMiddle: y -= bounds.Height / 2; break; case YPositionBehavior.DownToElementMiddle: y += bounds.Height / 2; break; case YPositionBehavior.None: default: break; } canvas.Translate(point.X - (bounds.Width / 2), y); } canvas.DrawText(text, 0, 0, paint); } } }
private void OnPainting(object sender, SKPaintSurfaceEventArgs e) { var surface = e.Surface; var canvas = surface.Canvas; canvas.Clear(SKColors.White); var fontAwesome = "I {{fa-heart-o color=ff0000}} to {{fa-code}} on {{fa-windows color=1BA1E2}}!"; var ionIcons = "{{ion-ios-cloud-download-outline color=0000ff}} the SkiaSharp source from {{ion-social-github}}."; var materialDesignIcons = "SkiaSharp works on {{mdi-apple}}, {{mdi-android color=a4c639}}, {{mdi-windows}} and {{mdi-linux}}!"; var materialIcons = "SkiaSharp supports {{brush}} and {{photo color=006400}}!"; var meteocons = "We love the {{mc-sun color=f9d71c}} and some {{mc-cloud-double-o}} s."; var simple = "We all {{icon-heart color=ff0000}} a {{icon-present}}!"; var typicons = "SkiaSharp runs on {{typcn-device-desktop}}, {{typcn-device-laptop}}, {{typcn-device-phone}} and {{typcn-device-tablet}} devices!"; var weather = "An {{wi-solar-eclipse}} is when the {{wi-day-sunny color=f9d71c}} is hidden."; using (var lookup = new SKTextRunLookup()) using (var textPaint = new SKPaint()) { // the lookup does not have to be re-created on each draw // instead, it can be created and re-used FontAwesome.AddTo(lookup); IonIcons.AddTo(lookup); MaterialDesignIcons.AddTo(lookup); MaterialIcons.AddTo(lookup); Meteocons.AddTo(lookup); SimpleLineIcons.AddTo(lookup); Typicons.AddTo(lookup); WeatherIcons.AddTo(lookup); textPaint.IsAntialias = true; textPaint.TextSize = 48; textPaint.Typeface = SKTypeface.FromFamilyName("Arial"); // the DrawIconifiedText method will re-calculate the text runs // it may be better to cache this using the: // var runs = SKTextRun.Create(text, lookup); // and then drawing it using the DrawText method. var padding = 24; var yOffset = padding + textPaint.TextSize; canvas.DrawIconifiedText(fontAwesome, padding, yOffset, lookup, textPaint); yOffset += padding + textPaint.TextSize; canvas.DrawIconifiedText(ionIcons, padding, yOffset, lookup, textPaint); yOffset += padding + textPaint.TextSize; canvas.DrawIconifiedText(materialDesignIcons, padding, yOffset, lookup, textPaint); yOffset += padding + textPaint.TextSize; canvas.DrawIconifiedText(materialIcons, padding, yOffset, lookup, textPaint); yOffset += padding + textPaint.TextSize; canvas.DrawIconifiedText(meteocons, padding, yOffset, lookup, textPaint); yOffset += padding + textPaint.TextSize; canvas.DrawIconifiedText(simple, padding, yOffset, lookup, textPaint); yOffset += padding + textPaint.TextSize; canvas.DrawIconifiedText(typicons, padding, yOffset, lookup, textPaint); yOffset += padding + textPaint.TextSize; canvas.DrawIconifiedText(weather, padding, yOffset, lookup, textPaint); yOffset += padding + textPaint.TextSize; } }
internal static void DrawLabel(SKCanvas canvas, Orientation orientation, bool isTop, SKSize itemSize, SKPoint point, SKColor color, SKRect bounds, string text, float textSize, SKTypeface typeface) { using (new SKAutoCanvasRestore(canvas)) { using (var paint = new SKPaint()) { paint.TextSize = textSize; paint.IsAntialias = true; paint.Color = color; paint.IsStroke = false; paint.Typeface = typeface; if (orientation == Orientation.Vertical) { var y = point.Y; if (isTop) { y -= bounds.Width; } canvas.RotateDegrees(90); canvas.Translate(y, -point.X + (bounds.Height / 2)); } else { if (bounds.Width > itemSize.Width) { text = text.Substring(0, Math.Min(3, text.Length)); paint.MeasureText(text, ref bounds); } if (bounds.Width > itemSize.Width) { text = text.Substring(0, Math.Min(1, text.Length)); paint.MeasureText(text, ref bounds); } var y = point.Y; if (isTop) { y -= bounds.Height; } canvas.Translate(point.X - (bounds.Width / 2), y); } canvas.DrawText(text, 0, 0, paint); } } }
static CustomFontMapper() { typeface = SKTypeface.FromFile("NotoSansCJKsc-Regular.otf"); typefaceLight = SKTypeface.FromFile("NotoSansCJKsc-Light.otf"); }
private SKBitmap BuildThumbCollageBitmap(string[] paths, int width, int height, string?libraryName) { var bitmap = new SKBitmap(width, height); using var canvas = new SKCanvas(bitmap); canvas.Clear(SKColors.Black); using var backdrop = GetNextValidImage(paths, 0, out _); if (backdrop == null) { return(bitmap); } // resize to the same aspect as the original var backdropHeight = Math.Abs(width * backdrop.Height / backdrop.Width); using var residedBackdrop = SkiaEncoder.ResizeImage(backdrop, new SKImageInfo(width, backdropHeight, backdrop.ColorType, backdrop.AlphaType, backdrop.ColorSpace)); // draw the backdrop canvas.DrawImage(residedBackdrop, 0, 0); // draw shadow rectangle var paintColor = new SKPaint { Color = SKColors.Black.WithAlpha(0x78), Style = SKPaintStyle.Fill }; canvas.DrawRect(0, 0, width, height, paintColor); var typeFace = SKTypeface.FromFamilyName("sans-serif", SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright) ?? SKTypeface.CreateDefault(); // use the system fallback to find a typeface for the given CJK character var nonCjkPattern = @"[^\p{IsCJKUnifiedIdeographs}\p{IsCJKUnifiedIdeographsExtensionA}\p{IsKatakana}\p{IsHiragana}\p{IsHangulSyllables}\p{IsHangulJamo}]"; var filteredName = Regex.Replace(libraryName ?? string.Empty, nonCjkPattern, string.Empty); if (!string.IsNullOrEmpty(filteredName)) { typeFace = SKFontManager.Default.MatchCharacter(null, SKFontStyleWeight.Bold, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright, null, filteredName[0]); } // draw library name var textPaint = new SKPaint { Color = SKColors.White, Style = SKPaintStyle.Fill, TextSize = 112, TextAlign = SKTextAlign.Center, Typeface = typeFace, IsAntialias = true }; // scale down text to 90% of the width if text is larger than 95% of the width var textWidth = textPaint.MeasureText(libraryName); if (textWidth > width * 0.95) { textPaint.TextSize = 0.9f * width * textPaint.TextSize / textWidth; } canvas.DrawText(libraryName, width / 2f, (height / 2f) + (textPaint.FontMetrics.XHeight / 2), textPaint); return(bitmap); }
public SKData DrawHistogram(string referenceName, int zoom, int chunk) { var refer = _service._references.FirstOrDefault(r => r.Name == referenceName); if (chunk == 0 && zoom == 10) { LoadReference(referenceName); } using var surface = SKSurface.Create(new SKImageInfo(refer.Length * zoom, 220)); using SKCanvas canvas = surface.Canvas; canvas.Clear(); using SKPaint paint = new SKPaint { Style = SKPaintStyle.StrokeAndFill, Color = SKColors.Blue, StrokeWidth = 1 }; using SKPaint linePaint = new SKPaint { Style = SKPaintStyle.Stroke, Color = SKColors.Black, StrokeWidth = 1 }; using SKPaint dashPaint = new SKPaint { Style = SKPaintStyle.Stroke, Color = SKColors.Black, StrokeWidth = 1, PathEffect = SKPathEffect.CreateDash(new[] { 20f, 5f }, 0) }; var histData = _service._histograms[refer.Name]; var scale = (float)(100 / histData.Average()); for (int i = 0; i < refer.Length; i++) { canvas.DrawRect(i * zoom + 5, 200 - (histData[i] * scale), zoom, histData[i] * scale, paint); } canvas.DrawLine(5, 0, 5, 200, linePaint); canvas.DrawLine(5, 199, refer.Length * zoom, 199, linePaint); var average = (int)(Math.Round(histData.Average())); canvas.DrawLine(5, average * scale, refer.Length * zoom, average * scale, dashPaint); using SKPaint textPaint = new SKPaint { Style = SKPaintStyle.StrokeAndFill, Color = SKColors.Black, StrokeWidth = 1, TextSize = 20, Typeface = SKTypeface.FromFamilyName("Courier New"), SubpixelText = true }; canvas.DrawText(average.ToString(), 6, average * scale - 2, textPaint); var cnt = textPaint.MeasureText("ACGT"); if (zoom == 10) { textPaint.TextSize = textPaint.TextSize * 40 / cnt; var shaper = new SKShaper(SKTypeface.FromFamilyName("Courier New")); canvas.DrawShapedText(shaper, reference, 5, 215, textPaint); } var width = (chunk + 1) * WIDTH * zoom > refer.Length * zoom ? refer.Length * zoom % WIDTH : WIDTH; SKPixmap pixmap = surface.Snapshot().Subset(SKRectI.Create(chunk * WIDTH, 0, width, canvas.DeviceClipBounds.Height)).PeekPixels(); var options = new SKWebpEncoderOptions(SKWebpEncoderCompression.Lossless, 100); return(pixmap.Encode(options)); }
protected override void OnDrawSample(SKCanvas canvas, int width, int height) { canvas.DrawColor(SKColors.White); var basePaint = new SKPaint(); basePaint.IsAntialias = true; basePaint.TextSize = 60; // first, we can just use a font (system default if not exists) using (var paint = basePaint.Clone()) using (var courier = SKTypeface.FromFamilyName("Courier New")) { paint.Typeface = courier; canvas.DrawText("'A', 'ݐ', '年'", 40, 100, paint); } // the font manager finds fonts var fontManager = SKFontManager.Default; // or, we can try and make sure that we have the character, and hope it has the others using (var paint = basePaint.Clone()) using (var courier = fontManager.MatchCharacter("Courier New", 'ݐ')) { paint.Typeface = courier; canvas.DrawText("'A', 'ݐ', '年'", 40, 200, paint); } // if we know the font doesn't have the character, or don't want to fall back using (var paint = basePaint.Clone()) using (var courier = SKTypeface.FromFamilyName("Courier New")) using (var arabic = fontManager.MatchCharacter("Courier New", 'ݐ')) using (var japanese = fontManager.MatchCharacter("Courier New", '年')) { var first = "'A', '"; var arabChar = "ݐ"; var mid = "', '"; var japChar = "年"; var last = "'"; float x = 40; // draw the first bit paint.Typeface = courier; canvas.DrawText(first, x, 300, paint); x += paint.MeasureText(first); // the arab character paint.Typeface = arabic; canvas.DrawText(arabChar, x, 300, paint); x += paint.MeasureText(arabChar); // draw the next bit paint.Typeface = courier; canvas.DrawText(mid, x, 300, paint); x += paint.MeasureText(mid); // the japanese character paint.Typeface = japanese; canvas.DrawText(japChar, x, 300, paint); x += paint.MeasureText(japChar); // the end paint.Typeface = courier; canvas.DrawText(last, x, 300, paint); } // let's draw some emojis (UTF-32 characters) var emojiChar = StringUtilities.GetUnicodeCharacterCode("🚀", SKTextEncoding.Utf32); using (var paint = basePaint.Clone()) using (var emoji = fontManager.MatchCharacter(emojiChar)) { paint.Typeface = emoji; canvas.DrawText("🌐 🍪 🍕 🚀", 40, 400, paint); } }
public async Task <Stream> DrawProfile(User user) { // get bitmaps background = await Utils.GetBitmapFromUrl(user.BackgroundUrl); avatar = await Utils.GetBitmapFromUrl(user.AvatarUrl); // clear the canvas canvas.Clear(clearColor); // draw background { using (SKPaint paint = new SKPaint()) { // setup paint paint.FilterQuality = SKFilterQuality.High; // resize, crop and covert to image background SKImage BackgroundImage = PrepareBackground(background); // draw background canvas.DrawImage(BackgroundImage, SKRect.Create(0, 0, profileWidth, profileHeight), paint); } } // draw panels { // setups paint and canvas panel.Open(); // main panel { var position = new SKPoint(20 * scale, 150 * scale); var size = new SKSize(472 * scale, 342 * scale); var roundRect = new SKRoundRect(SKRect.Create(position, size), 32 * scale); panel.DrawRounRect(roundRect); } // avatar border { var position = new SKPoint(x: 192 * scale, y: 82 * scale); var radius = 64 * scale; var border = 16; panel.DrawCircle(position.X - border, position.Y - border, radius + border); } // draw panels panel.Close(); } // draw avatar { // avatar information var position = new SKPoint(x: 192 * scale, y: 82 * scale); var size = new SKSize(width: 128 * scale, height: 128 * scale); var radius = 64 * scale; using (var paint = new SKPaint()) { // setup paint paint.IsAntialias = true; paint.FilterQuality = SKFilterQuality.High; // save canvas canvas.Save(); // create clip and aplay it to canvas using (SKPath clip = new SKPath()) { clip.AddCircle(radius + position.X, radius + position.Y, radius); canvas.ClipPath(clip, SKClipOperation.Intersect, true); } // draw avatar canvas.DrawBitmap(avatar, SKRect.Create(position, size), paint); // restore canvas canvas.Restore(); } } // draw username { using (var paint = new SKPaint()) { // setup paint paint.TextSize = (int)(48 * scale); paint.Color = SKColors.White; paint.IsAntialias = true; // calculate position float UsernameLength = paint.MeasureText($"{ user.Username }#{ user.Discriminator }"); float x = ((profileWidth - UsernameLength) / 2); float y = 250 * scale; // draw username paint.Typeface = SKTypeface.FromFile("Common/Assets/Fonts/Roboto-Regular.ttf"); canvas.DrawText(user.Username, x, y, paint); // recalculate position for discriminator float offset = paint.MeasureText($"{ user.Username }"); x += offset; // draw discriminator paint.Typeface = SKTypeface.FromFile("Common/Assets/Fonts/Roboto-Thin.ttf"); canvas.DrawText($"#{ user.Discriminator }", x, y, paint); } } // return surface as stream return(surface.Snapshot().Encode().AsStream()); }