private static void GenerateMosaic(QuadrantMatchingTileProvider tileProvider, Stream inputStream, List <byte[]> tileImages, Stream outputStream) { SKBitmap[,] mosaicTileGrid; inputStream.Seek(0, SeekOrigin.Begin); using (var skStream = new SKManagedStream(inputStream)) using (var bitmap = SKBitmap.Decode(skStream)) { // use transparency for the source image overlay var srcImagePaint = new SKPaint() { Color = SKColors.White.WithAlpha(200) }; int xTileCount = bitmap.Width / MosaicBuilder.TileWidth; int yTileCount = bitmap.Height / MosaicBuilder.TileHeight; int tileCount = xTileCount * yTileCount; mosaicTileGrid = new SKBitmap[xTileCount, yTileCount]; int finalTileWidth = MosaicBuilder.TileWidth * MosaicBuilder.ScaleMultiplier; int finalTileHeight = MosaicBuilder.TileHeight * MosaicBuilder.ScaleMultiplier; int targetWidth = xTileCount * finalTileWidth; int targetHeight = yTileCount * finalTileHeight; var tileList = new List <(int, int)>(); // add coordinates for the left corner of each tile for (int x = 0; x < xTileCount; x++) { for (int y = 0; y < yTileCount; y++) { tileList.Add((x, y)); } } // create output surface var surface = SKSurface.Create(targetWidth, targetHeight, SKImageInfo.PlatformColorType, SKAlphaType.Premul); surface.Canvas.DrawColor(SKColors.White); // clear the canvas / fill with white surface.Canvas.DrawBitmap(bitmap, 0, 0, srcImagePaint); // using the Darken blend mode causes colors from the source image to come through var tilePaint = new SKPaint() { BlendMode = SKBlendMode.Darken }; surface.Canvas.SaveLayer(tilePaint); // save layer so blend mode is applied var random = new Random(); while (tileList.Count > 0) { // choose a new tile at random int nextIndex = random.Next(tileList.Count); var tileInfo = tileList[nextIndex]; tileList.RemoveAt(nextIndex); // get the tile image for this point //var exclusionList = GetExclusionList(mosaicTileGrid, tileInfo.Item1, tileInfo.Item2); var tileBitmap = tileProvider.GetImageForTile(tileInfo.Item1, tileInfo.Item2); mosaicTileGrid[tileInfo.Item1, tileInfo.Item2] = tileBitmap; // draw the tile on the surface at the coordinates SKRect tileRect = SKRect.Create(tileInfo.Item1 * TileWidth, tileInfo.Item2 * TileHeight, finalTileWidth, finalTileHeight); surface.Canvas.DrawBitmap(tileBitmap, tileRect); } surface.Canvas.Restore(); // merge layers surface.Canvas.Flush(); var imageBytes = surface.Snapshot().Encode(SKEncodedImageFormat.Jpeg, 80); imageBytes.SaveTo(outputStream); } }
public SKPaintSurfaceEventArgs(SKSurface surface, SKImageInfo info) { Surface = surface; Info = info; }
public virtual void DrawInSurface(SKSurface surface, GRBackendRenderTargetDesc renderTarget) { }
public SkRenderer(SKSurface surface, SKCanvas canvas) { _c = canvas; Surface = surface; Bus = new RenderBus(); }
public void OnDrawFrame(IGL10 gl) { GLES10.GlClear(GLES10.GlColorBufferBit | GLES10.GlDepthBufferBit | GLES10.GlStencilBufferBit); // create the contexts if not done already if (context == null) { var glInterface = GRGlInterface.Create(); context = GRContext.CreateGl(glInterface); } // manage the drawing surface if (renderTarget == null || lastSize != newSize || !renderTarget.IsValid) { // create or update the dimensions lastSize = newSize; // read the info from the buffer var buffer = new int[3]; GLES20.GlGetIntegerv(GLES20.GlFramebufferBinding, buffer, 0); GLES20.GlGetIntegerv(GLES20.GlStencilBits, buffer, 1); GLES20.GlGetIntegerv(GLES20.GlSamples, buffer, 2); var samples = buffer[2]; var maxSamples = context.GetMaxSurfaceSampleCount(colorType); if (samples > maxSamples) { samples = maxSamples; } glInfo = new GRGlFramebufferInfo((uint)buffer[0], colorType.ToGlSizedFormat()); // destroy the old surface surface?.Dispose(); surface = null; canvas = null; // re-create the render target renderTarget?.Dispose(); renderTarget = new GRBackendRenderTarget(newSize.Width, newSize.Height, samples, buffer[1], glInfo); } // create the surface if (surface == null) { surface = SKSurface.Create(context, renderTarget, surfaceOrigin, colorType); canvas = surface.Canvas; } using (new SKAutoCanvasRestore(canvas, true)) { // start drawing var e = new SKPaintGLSurfaceEventArgs(surface, renderTarget, surfaceOrigin, colorType, glInfo); OnPaintSurface(e); #pragma warning disable CS0618 // Type or member is obsolete OnDrawFrame(e.Surface, e.RenderTarget); #pragma warning restore CS0618 // Type or member is obsolete } // flush the SkiaSharp contents to GL canvas.Flush(); context.Flush(); }
public void SeriesRemoved() { var series = new LineSeries <int> { Values = new List <int> { 1, 6, 4, 2 } }; var seriesCollection = new List <ISeries> { series }; var chart = new TestCartesianChartView { Series = seriesCollection, XAxes = new[] { new Axis() }, YAxes = new[] { new Axis() }, }; var canvas = chart.CoreCanvas; void DrawChart() { while (!canvas.IsValid) { canvas.DrawFrame( new SkiaSharpDrawingContext( canvas, new SKImageInfo(100, 100), SKSurface.CreateNull(100, 100), new SKCanvas(new SKBitmap()))); } } chart.Core.Update(new ChartUpdateParams { Throttling = false }); DrawChart(); var drawables = canvas.DrawablesCount; var geometries = canvas.CountGeometries(); seriesCollection.Add(new LineSeries <int> { Values = new List <int> { 1, 6, 4, 2 } }); chart.Core.Update(new ChartUpdateParams { Throttling = false }); DrawChart(); seriesCollection.RemoveAt(0); chart.Core.Update(new ChartUpdateParams { Throttling = false }); DrawChart(); Assert.IsTrue( drawables == canvas.DrawablesCount && geometries == canvas.CountGeometries()); }
public SKPaintGLSurfaceEventArgs(SKSurface surface, GRBackendRenderTargetDesc renderTarget) { Surface = surface; RenderTarget = renderTarget; }
/// <summary> /// This is triggered whenever the canvas needs to redraw. /// </summary> /// <param name="sender"></param> /// <param name="args"></param> private void OnCanvasViewPaintSurface(object sender, SkiaSharp.Views.Forms.SKPaintSurfaceEventArgs args) { if (viewModel.ChartData == null) { return; } if (viewModel.ChartData.Count == 0) { return; } SKImageInfo info = args.Info; SKSurface surface = args.Surface; SKCanvas canvas = surface.Canvas; var y = info.Height / 2; canvas.Clear(); SKPoint center = new SKPoint(info.Width / 2, info.Height / 2); float radius = Math.Min(info.Width / 2, info.Height / 2) - 2 * Constants.ExplodeOffset; SKRect rect = new SKRect(center.X - radius, center.Y - radius, center.X + radius, center.Y + radius); float startAngle = -90; //This is alighed to the marker to make tracking the winning prize easier. //for text float xCenter = info.Width / 2; float yCenter = info.Height / 2; foreach (ChartData item in viewModel.ChartData) { float sweepAngle = 360f / viewModel.ChartData.Count; using (SKPath path = new SKPath()) using (SKPaint fillPaint = new SKPaint()) using (SKPaint outlinePaint = new SKPaint()) using (SKPaint textPaint = new SKPaint()) { path.MoveTo(center); path.ArcTo(rect, startAngle, sweepAngle, false); path.Close(); fillPaint.Style = SKPaintStyle.Fill; fillPaint.Color = item.Color; outlinePaint.Style = SKPaintStyle.Stroke; outlinePaint.StrokeWidth = 5; outlinePaint.Color = SKColors.White; #region Text Writer //write text to the screen textPaint.TextSize = 40; textPaint.StrokeWidth = 1; textPaint.Color = SKColors.White; //Adjust text size. SKRect textBounds = new SKRect(); textPaint.MeasureText(item.Text, ref textBounds); float yText = yCenter - textBounds.Height / 2 - textBounds.Top; // Adjust TextSize property so text is 95% of the ARC // float textWidth = textPaint.MeasureText(item.Text); // textPaint.TextSize = 0.95f * info.Width * textPaint.TextSize / textWidth; #endregion canvas.Save(); DrawRotatedWithMatrices(canvas, path, fillPaint, outlinePaint, item, _degrees, (int)center.X, y); //Writing Actual texts var test_angle = _degrees + (360 / viewModel.ChartData.Count / 2) - (360 / viewModel.ChartData.Count * 2); float sweepAngleText = 360f / viewModel.ChartData.Count; float startAngleText = sweepAngleText - sweepAngleText / 2; foreach (ChartData itemer in viewModel.ChartData) { canvas.Save(); canvas.RotateDegrees(startAngleText + _degrees - 90, xCenter, yCenter); if (itemer.Text.Trim().Length > 6) { textPaint.TextSize = 30; } else { textPaint.TextSize = 40; } canvas.DrawText(itemer.Text, xCenter, yText, textPaint); canvas.Restore(); test_angle += 360 / viewModel.ChartData.Count; if (test_angle > 360) { test_angle = test_angle - 360; } if (startAngleText > 360) { startAngleText = startAngleText - 360; } startAngleText += sweepAngleText; } canvas.Restore(); } startAngle += sweepAngle; } #region Marker //draw the Mark using (SKPaint fillMarkCirclePaint = new SKPaint()) using (SKPaint fillMarkCirclePaintOuter = new SKPaint()) using (SKPaint fillMarkTrianglePaint = new SKPaint()) { fillMarkCirclePaint.Style = SKPaintStyle.StrokeAndFill; fillMarkCirclePaintOuter.Style = SKPaintStyle.StrokeAndFill; fillMarkCirclePaintOuter.Color = Color.FromHex("#FFF180").ToSKColor(); // Define an array of rainbow colors List <SKColor> colors = new List <SKColor>(); foreach (var col in viewModel.Colors) { colors.Add(Color.FromHex(col).ToSKColor()); } //draw outer circle canvas.DrawCircle(args.Info.Width / 2, args.Info.Height / 2, 60, fillMarkCirclePaintOuter); //outer //draw triangle fillMarkTrianglePaint.Style = SKPaintStyle.StrokeAndFill; fillMarkTrianglePaint.Color = Color.FromHex("#FFF180").ToSKColor(); SKPath trianglePath = new SKPath(); trianglePath.MoveTo((args.Info.Width / 2) - 55, args.Info.Height / 2); trianglePath.LineTo((args.Info.Width / 2) - 55, args.Info.Height / 2); trianglePath.LineTo((args.Info.Width / 2) + 55, args.Info.Height / 2); trianglePath.LineTo(args.Info.Width / 2, (float)(args.Info.Height / 2.5)); trianglePath.Close(); canvas.DrawPath(trianglePath, fillMarkTrianglePaint); //draw inner circle SKPoint circle_center = new SKPoint(info.Rect.MidX, info.Rect.MidY); fillMarkCirclePaint.Shader = SKShader.CreateSweepGradient(circle_center, colors.ToArray()); canvas.DrawCircle(args.Info.Width / 2, args.Info.Height / 2, 50, fillMarkCirclePaint); //inner } #endregion //Get the current prize. float prize_degree = _degrees + (360 / viewModel.ChartData.Count / 2); if (_degrees == 0 || Math.Round(_degrees, MidpointRounding.AwayFromZero) == 360) { prize_degree = _degrees; } var segment = ((prize_degree / 360f) * viewModel.ChartData.Count); var int_segment2 = Math.Round(segment, MidpointRounding.AwayFromZero); var realIndex = viewModel.ChartData.Count == viewModel.ChartData.Count - (int)int_segment2 ? 0 : viewModel.ChartData.Count - (int)int_segment2; viewModel.Prize = viewModel.ChartData[realIndex].Sector; //add back resultBox.Text = viewModel.Prize?.Game; // currentBox.Text = ((int)realIndex).ToString(); if (viewModel.EnableHaptic) { TryHaptic(); } IncrementDegrees(); }
//METHODS /********************************************************************** *********************************************************************/ // do the drawing static void PaintSurface(object sender, SKPaintSurfaceEventArgs e) { //canvas object info = e.Info; SKSurface surface = e.Surface; SKCanvas canvas = surface.Canvas; //Important! Otherwise the drawing will look messed up in iOS if (canvas != null) { canvas.Clear(); } //calculate some stuff and make the paint CalculateNeededNumbers(canvas); MakeSKPaint(); //depends on xe and ye and therfore has to be called after they were initialized /*********************HERE GOES THE DRAWING************************/ int state = Int32.Parse(action[currentStep, 6]); DrawBackground(canvas, state); //draw fast recovery reno for (int i = 0; i <= currentStep; i++) { int round = Int32.Parse(action[i, 0]); String txt = action[i, 1] + action[i, 2]; String kind = action[i, 1]; String dupAck = action[i, 3]; String cwnd = action[i, 4]; String tresh = action[i, 5]; String part = action[i, 7]; switch (kind) { case "Ack ": DrawAckArrow(canvas, round, sk_PaintArrowAck, part, dupAck, cwnd, tresh); DrawTextNextToAckArrow(canvas, round, txt, sk_TextArrowAck); break; case "Pkt ": DrawPktArrow(canvas, round, sk_PaintArrowPkt, part); DrawTextNextToPktArrow(canvas, round, txt, sk_TextArrowPkt); break; case "!Pkt ": DrawPktArrow(canvas, round, sk_PaintArrowRed, part); DrawTextNextToPktArrow(canvas, round, txt.Substring(1), sk_TextArrowRed); break; case "Re-Pkt ": DrawPktArrow(canvas, round, sk_PaintArrowPkt, part); DrawTextNextToPktArrow(canvas, round, txt, sk_TextArrowPkt); break; } //show cwnd and dupAck count in very first round if (i == 0) { DrawTextTresh(canvas, round, tresh, sk_blackTextSmall); DrawTextDupAck(canvas, round, dupAck, sk_blackTextSmall); DrawTextCwnd(canvas, round, cwnd, sk_blackTextSmall); } } //execute all drawing actions canvas.Flush(); }
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args) { SKImageInfo info = args.Info; SKSurface surface = args.Surface; SKCanvas canvas = surface.Canvas; canvas.Clear(); if (info.Width > info.Height) { this.graphMinHeightPercentage = Graph.graphTopPaddingPercentage; } else { this.graphMinHeightPercentage = Graph.defaultGraphMinHeightPercentage; } DateTime startDate = new DateTime(2018, 11, 1); DateTime endDate = new DateTime(2018, 12, 8); bool success = this.graph.CalculatePoints(Graph.defaultGraphWidthPercentage, this.graphMinHeightPercentage * 100, info.Width, info.Height, startDate, endDate); List <List <SKPoint> > graphLines = this.graph.GetGraphLines(); List <List <SKPoint> > graphLinesFill = this.graph.GetGraphLinesFill(); List <List <SKPoint> > graphHelperLines = this.graph.GetGraphHelperLines(); SKPaint paint = new SKPaint { Color = SKColors.LightGray.WithAlpha(0xAA), StrokeWidth = 1, StrokeCap = SKStrokeCap.Square, Style = SKPaintStyle.Stroke, IsAntialias = true }; foreach (List <SKPoint> graphHelperLine in graphHelperLines) { canvas.DrawPoints(SKPointMode.Lines, graphHelperLine.ToArray(), paint); } paint.StrokeCap = SKStrokeCap.Round; for (int i = 0; i < graphLines.Count; ++i) { paint.Color = graph.colorList[i]; paint.Style = SKPaintStyle.Stroke; paint.StrokeWidth = 2; canvas.DrawPoints(SKPointMode.Polygon, graphLines[i].ToArray(), paint); paint.StrokeWidth = 6; canvas.DrawPoints(SKPointMode.Points, graphLines[i].ToArray(), paint); paint.Color = paint.Color.WithAlpha(0x50); paint.Style = SKPaintStyle.Fill; SKPath path = new SKPath(); path.AddPoly(graphLinesFill[i].ToArray()); canvas.DrawPath(path, paint); } paint.StrokeWidth = 4; paint.Color = SKColors.Black; float bottomY = (1 - this.graphMinHeightPercentage) * info.Height; float topY = (Graph.graphTopPaddingPercentage - axisExtensionPercentage) * info.Height; float leftX = Graph.graphLeftPaddingPercentage * info.Width; float rightX = (Graph.graphLeftPaddingPercentage + this.graphWidthPercentage + axisExtensionPercentage) * info.Width; List <SKPoint> horizontalAxis = new List <SKPoint> { //Axis new SKPoint(leftX, bottomY), new SKPoint(rightX, bottomY), //Arrow new SKPoint(rightX, bottomY), new SKPoint(rightX - arrowOffset, bottomY - arrowOffset), new SKPoint(rightX, bottomY), new SKPoint(rightX - arrowOffset, bottomY + arrowOffset), }; List <SKPoint> verticalAxis = new List <SKPoint> { //Axis new SKPoint(leftX, bottomY), new SKPoint(leftX, topY), //Arrow new SKPoint(leftX, topY), new SKPoint(leftX - arrowOffset, topY + arrowOffset), new SKPoint(leftX, topY), new SKPoint(leftX + arrowOffset, topY + arrowOffset), }; canvas.DrawPoints(SKPointMode.Lines, horizontalAxis.ToArray(), paint); canvas.DrawPoints(SKPointMode.Lines, verticalAxis.ToArray(), paint); if (success) { List <Graph.GraphValue> graphValues = this.graph.GetGraphValues(); List <Graph.GraphValue> xValues = this.graph.GetXValues(); SKPaint textPaint = new SKPaint { Color = SKColors.Blue, IsAntialias = true, }; float textWidth = textPaint.MeasureText("22.22"); textPaint.TextSize = Graph.graphLeftPaddingPercentage * info.Width * textScalePercentage * textPaint.TextSize / textWidth; SKRect textBounds = new SKRect(); foreach (Graph.GraphValue graphValue in graphValues) { textPaint.MeasureText(graphValue.Value, ref textBounds); float xText = info.Width * (Graph.graphLeftPaddingPercentage + Graph.graphLeftPaddingPercentage * textPaddingPercentage) / 2 - textBounds.MidX; canvas.DrawText(graphValue.Value, xText, (float)graphValue.anchor.Y - textBounds.MidY, textPaint); } foreach (Graph.GraphValue xValue in xValues) { textPaint.MeasureText(xValue.Value, ref textBounds); float yText = bottomY + textBounds.Height + 10; canvas.DrawText(xValue.Value, (float)xValue.anchor.X - textBounds.MidX, yText, textPaint); } } }
public override void DrawView(SKSurface canvas) { //do nothing }
public ResizeResult ResizeFolder(string directoryPath, string directoryExportPath, float width, float height) { if (!Directory.Exists(directoryPath)) { return(new ResizeResult() { Success = false, ErrorMessage = "Directory is not exists" }); } if (!Directory.Exists(directoryExportPath)) { if (Directory.CreateDirectory(directoryExportPath).Exists) { return(new ResizeResult() { Success = false, ErrorMessage = "Cannot create output folder" }); } } foreach (var file in Directory.GetFiles(directoryPath)) { if (file.EndsWith(".png") || file.EndsWith(".jpg")) { try { var bitmap = SKBitmap.Decode(file); Console.WriteLine("Image Path: " + file); var info = new SKImageInfo((int)width, (int)height); var surface = SKSurface.Create(info); // the the canvas and properties var canvas = surface.Canvas; // make sure the canvas is blank canvas.Clear(SKColors.Transparent); var resizeFactor = 1f; if (bitmap.Width * height / width > bitmap.Height) { resizeFactor = width / bitmap.Width; } else { resizeFactor = height / bitmap.Height; } Console.WriteLine($"OWidth: {bitmap.Width}, OHeight: {bitmap.Height}, ratio: {resizeFactor}, destWidth: {(int)(bitmap.Width * resizeFactor)}, destHeight: {(int)(bitmap.Height * resizeFactor)}"); var targetSize = (int)Math.Min(width, height); SKImageInfo nearest = new SKImageInfo((int)(bitmap.Width * resizeFactor), (int)(bitmap.Height * resizeFactor)); //SKImageInfo nearest = new SKImageInfo(targetSize, targetSize); var newImage = bitmap.Resize(nearest, SKFilterQuality.High); canvas.DrawBitmap(newImage, new SKPoint((width - newImage.Width) / 2f, (height - newImage.Height) / 2f)); var image = surface.Snapshot(); //var image = SKImage.FromBitmap(toBitmap); var data = image.Encode(SKEncodedImageFormat.Png, 90); using (var stream = new FileStream(Path.Combine(directoryExportPath, Path.ChangeExtension(Path.GetFileName(file), ".png")), FileMode.OpenOrCreate, FileAccess.Write)) data.SaveTo(stream); bitmap.Dispose(); } catch (Exception e) { return(new ResizeResult() { Success = false, ErrorMessage = "Error on file: " + file + "\n Exception: " + e.ToString() }); } } } return(new ResizeResult() { Success = true, ErrorMessage = "Resize Success" }); }
public SKPaintGLSurfaceEventArgs(SKSurface surface, GRBackendRenderTarget renderTarget) : this(surface, renderTarget, GRSurfaceOrigin.BottomLeft, SKColorType.Rgba8888) { }
public async Task <SKImage> GenerateChartAsync(ChartSettings chart) { try { await chart.Albums.ParallelForEachAsync(async album => { var encodedId = StringExtensions.ReplaceInvalidChars(album.Url.LocalPath.Replace("/music/", "")); var localAlbumId = StringExtensions.TruncateLongString(encodedId, 60); SKBitmap chartImage; var validImage = true; Color?primaryColor = null; var fileName = localAlbumId + ".png"; var localPath = FMBotUtil.GlobalVars.CacheFolder + fileName; if (File.Exists(localPath)) { chartImage = SKBitmap.Decode(localPath); Statistics.LastfmCachedImageCalls.Inc(); } else { if (album.Images.Any() && album.Images.Large != null) { var url = album.Images.Large.AbsoluteUri; SKBitmap bitmap; try { var httpClient = new System.Net.Http.HttpClient(); var bytes = await httpClient.GetByteArrayAsync(url); Statistics.LastfmImageCalls.Inc(); var stream = new MemoryStream(bytes); bitmap = SKBitmap.Decode(stream); } catch { bitmap = SKBitmap.Decode(FMBotUtil.GlobalVars.ImageFolder + "loading-error.png"); validImage = false; } chartImage = bitmap; if (validImage) { using var image = SKImage.FromBitmap(bitmap); using var data = image.Encode(SKEncodedImageFormat.Png, 100); await using var stream = File.OpenWrite(localPath); data.SaveTo(stream); } } else { chartImage = SKBitmap.Decode(FMBotUtil.GlobalVars.ImageFolder + "unknown.png"); validImage = false; } } switch (chart.TitleSetting) { case TitleSetting.Titles: AddTitleToChartImage(chartImage, album); break; case TitleSetting.ClassicTitles: AddClassicTitleToChartImage(chartImage, album); break; case TitleSetting.TitlesDisabled: break; default: throw new ArgumentOutOfRangeException(); } if (chart.RainbowSortingEnabled) { primaryColor = chartImage.GetAverageRgbColor(); } chart.ChartImages.Add(new ChartImage(chartImage, chart.Albums.IndexOf(album), validImage, primaryColor)); }); SKImage finalImage = null; using (var tempSurface = SKSurface.Create(new SKImageInfo(chart.ChartImages.First().Image.Width *chart.Width, chart.ChartImages.First().Image.Height *chart.Height))) { var canvas = tempSurface.Canvas; var offset = 0; var offsetTop = 0; var heightRow = 0; for (var i = 0; i < Math.Min(chart.ImagesNeeded, chart.ChartImages.Count); i++) { IOrderedEnumerable <ChartImage> imageList; if (chart.RainbowSortingEnabled) { imageList = chart.ChartImages .OrderBy(o => o.PrimaryColor.Value.GetHue()) .ThenBy(o => (o.PrimaryColor.Value.R * 3 + o.PrimaryColor.Value.G * 2 + o.PrimaryColor.Value.B * 1)); } else { imageList = chart.ChartImages.OrderBy(o => o.Index); } var image = imageList .Where(w => !chart.SkipArtistsWithoutImage || w.ValidImage) .ElementAt(i).Image; canvas.DrawBitmap(image, SKRect.Create(offset, offsetTop, image.Width, image.Height)); if (i == (chart.Width - 1) || i - (chart.Width) * heightRow == chart.Width - 1) { offsetTop += image.Height; heightRow += 1; offset = 0; } else { offset += image.Width; } } finalImage = tempSurface.Snapshot(); } return(finalImage); } finally { foreach (var image in chart.ChartImages.Select(s => s.Image)) { image.Dispose(); } } }
public void Animate(Action <float> reportProgress, SongConfiguration config, DirectoryInfo outputDirectory, string pngOutputPath = null) { var desiredReadingY = height * 3 / 4; if (!File.Exists(config.AudioFilePath)) { throw new FileNotFoundException($"Song file not found: '{config.AudioFilePath}'"); } using (var titleTypeface = SKTypeface.FromFamilyName(appConfig.TitleFont.Family, SKFontStyleWeight.Light, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright)) using (var lyricTypeface = SKTypeface.FromFamilyName(appConfig.LyricsFont.Family, SKFontStyleWeight.Light, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright)) using (var verseTypeface = SKTypeface.FromFamilyName(appConfig.VerseFont.Family, SKFontStyleWeight.Light, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright)) { var timingRegex = new Regex(@"^(?:\[(\d{1,2}:\d{1,2}:\d{1,2})\])?\s*(?:\{([^}]+)\})?\s*(.*)$"); var unprocessedLines = config.Lyrics; var speedChangeEasingFrames = appConfig.FramesPerSecond * 4; var processedLines = new List <(TimeSpan arrivalTime, TimeSpan nextArrivalTime, IEnumerable <string> lines)>(); var verseLabels = new List <VerseLabel>(); var segmentLines = new List <string>(); TimeSpan?segmentArrival = null; foreach (var line in unprocessedLines) { var match = timingRegex.Match(line); var nextArrivalGroup = match.Groups[1]; if (nextArrivalGroup.Success) { var nextArrivalTime = TimeSpan.Parse(nextArrivalGroup.Value); if (segmentLines.Any()) { processedLines.Add((segmentArrival.Value, nextArrivalTime, segmentLines)); segmentLines = new List <string>(); } segmentArrival = nextArrivalTime; // TODO: Make it clearer/explicit that verse text requires a paired segment timing if (match.Groups[2].Success) { var arrivalFrame = (int)(segmentArrival.Value.TotalSeconds * appConfig.FramesPerSecond); if (verseLabels.Any()) { verseLabels.Last().HiddenFrame = arrivalFrame - (int)(verseLabelHideBeforeVerseEnd.TotalSeconds * appConfig.FramesPerSecond); } verseLabels.Add(new VerseLabel { ArrivalFrame = arrivalFrame, Text = match.Groups[2].Value }); } } var lyricText = match.Groups[3].Value; segmentLines.Add(lyricText); } if (verseLabels.Any()) { verseLabels.Last().HiddenFrame = (int)(processedLines.Last().nextArrivalTime.TotalSeconds *appConfig.FramesPerSecond); } float?previousPixelsPerFrame = null; var speedChanges = new List <(int arrivalFrame, float fromPixelsPerFrame, float toPixelsPerFrame)>(); var lyrics = new List <Lyric>(); foreach (var timingSegment in processedLines) { // Calculate the speed of this segment, defined by (distance / duration) var segmentHeight = CalculateTextHeight( lyricTypeface, appConfig.LyricsFont.Size, timingSegment.lines, appConfig.LyricsFont.Size + appConfig.LyricsFont.LineMargin, width - sideMargin * 2 ); var segmentDuration = timingSegment.nextArrivalTime - timingSegment.arrivalTime; var pixelsPerFrame = (float)(segmentHeight / segmentDuration.TotalSeconds / appConfig.FramesPerSecond); var arrivalFrame = (int)(timingSegment.arrivalTime.TotalSeconds * appConfig.FramesPerSecond); speedChanges.Add(( arrivalFrame, previousPixelsPerFrame.GetValueOrDefault(pixelsPerFrame), pixelsPerFrame )); var segmentDurationFrames = segmentDuration.TotalSeconds * appConfig.FramesPerSecond; previousPixelsPerFrame = pixelsPerFrame; lyrics.Add(new Lyric { VisibleFrame = (int)(arrivalFrame - (appConfig.OutputDimensions.Height - desiredReadingY) / pixelsPerFrame - 100), HiddenFrame = (int)(arrivalFrame + segmentDurationFrames + appConfig.OutputDimensions.Height / pixelsPerFrame), ArrivalFrame = arrivalFrame, Lines = timingSegment.lines, Height = segmentHeight }); } var currentPixelsPerFrame = speedChanges.First().fromPixelsPerFrame; var currentSpeedChangeIndex = 1; var speedChangeStartFrame = speedChanges.Count > 1 ? speedChanges[1].arrivalFrame - speedChangeEasingFrames / 2 : new int?(); float?accelerationPerFrame = null; var endTransitionDissolveDurationFrames = (int)(endTransitionDuration.TotalSeconds * appConfig.FramesPerSecond); // TODO: Can we calculate total frames required from the song end time automatically? // Or do we need an explicit "end of audio" timestamp in the lyrics file? var duration = TimeSpan.Parse(config.Duration); var totalFramesRequired = duration.TotalSeconds * appConfig.FramesPerSecond; var outputFilePath = Path.Combine(outputDirectory.FullName, config.OutputFilename); File.Delete(outputFilePath); var ffmpegProcess = StartFfmpeg(appConfig, config.AudioFilePath, outputFilePath); var info = new SKImageInfo(width, height); using (var surface = SKSurface.Create(info)) { var canvas = surface.Canvas; for (var frame = 0; frame <= totalFramesRequired; frame++) { reportProgress(frame / (float)totalFramesRequired); canvas.Clear(SKColors.Black); if (speedChangeStartFrame.HasValue) { if (frame == speedChangeStartFrame.Value + speedChangeEasingFrames) { currentSpeedChangeIndex++; speedChangeStartFrame = speedChanges.Count > currentSpeedChangeIndex ? speedChanges[currentSpeedChangeIndex].arrivalFrame - speedChangeEasingFrames / 2 : new int?(); accelerationPerFrame = null; } else if (frame >= speedChangeStartFrame) { accelerationPerFrame = accelerationPerFrame ?? (speedChanges[currentSpeedChangeIndex].toPixelsPerFrame - currentPixelsPerFrame) / speedChangeEasingFrames; currentPixelsPerFrame += accelerationPerFrame.Value; } } for (var i = 0; i < lyrics.Count; i++) { var lyric = lyrics[i]; if (frame < lyric.VisibleFrame) { continue; } if (frame == lyric.VisibleFrame) { lyric.Y = i > 0 ? lyrics[i - 1].Y + lyrics[i - 1].Height : desiredReadingY + (lyric.ArrivalFrame - lyric.VisibleFrame) * currentPixelsPerFrame; } else { lyric.Y -= currentPixelsPerFrame; } DrawLyric( canvas, lyricTypeface, appConfig.LyricsFont.Size, appConfig.LyricsFont.Size + appConfig.LyricsFont.LineMargin, lyric.Lines, x: sideMargin, y: lyric.Y ); } DrawGradientOverlays(canvas); var framesToArrivalPointAtCurrentSpeed = (int)((appConfig.OutputDimensions.Height - desiredReadingY) / currentPixelsPerFrame); for (var i = 0; i < verseLabels.Count; i++) { var verseLabel = verseLabels[i]; var opacity = 1f; if (frame < verseLabel.ArrivalFrame - framesToArrivalPointAtCurrentSpeed) { continue; } if (frame == verseLabel.ArrivalFrame - framesToArrivalPointAtCurrentSpeed) { verseLabel.Y = appConfig.OutputDimensions.Height; } else if (frame > verseLabel.ArrivalFrame - framesToArrivalPointAtCurrentSpeed && verseLabel.Y > appConfig.OutputDimensions.HeaderHeight + appConfig.OutputDimensions.GradientHeight) { verseLabel.Y -= currentPixelsPerFrame; } else if (frame >= verseLabel.HiddenFrame) { opacity = Math.Max(0, 1 - (frame - verseLabel.HiddenFrame) / (float)DissolveAnimationDurationFrames); } DrawVerseLabel( canvas, verseTypeface, appConfig.VerseFont.Size, SKColor.Parse(appConfig.VerseFont.HexColor), verseLabel.Text, width - sideMargin, verseLabel.Y, opacity ); } DrawTitleAndFooterBars(canvas, titleTypeface, appConfig.TitleFont.Size, SKColor.Parse(appConfig.TitleFont.HexColor), config.SongTitle); if (totalFramesRequired - frame <= endTransitionDissolveDurationFrames) { var alpha = (1 - (totalFramesRequired - frame) / endTransitionDissolveDurationFrames) * 255; using (var paint = new SKPaint { Color = SKColors.Black.WithAlpha((byte)alpha) }) { canvas.DrawRect(new SKRect(0, 0, width, height), paint); } } using (var image = surface.Snapshot()) using (var data = image.Encode(SKEncodedImageFormat.Png, 100)) using (var ms = new MemoryStream(data.ToArray()) { Position = 0 }) { ms.WriteTo(ffmpegProcess.StandardInput.BaseStream); ms.Flush(); ffmpegProcess.StandardInput.BaseStream.Flush(); if (pngOutputPath != null) { using (var fs = new FileStream(Path.Combine(pngOutputPath, $"{frame:D5}.png"), FileMode.Create, FileAccess.Write)) { ms.Position = 0; ms.WriteTo(fs); ms.Flush(); fs.Close(); } } ms.Close(); } } } ffmpegProcess.StandardInput.BaseStream.Close(); ffmpegProcess.WaitForExit(); } }
public virtual void DrawInSurface(SKSurface surface, GRBackendRenderTargetDesc renderTarget) { PaintSurface?.Invoke(this, new SKPaintGLSurfaceEventArgs(surface, renderTarget)); }
private void Render(int cornerCount = 100, int maxVisibleDistance = 200) { // create the field if needed or if the size changed if (field == null || field.width != glControl1.Width || field.height != glControl1.Height) { field = new IntroAnimation.Field(glControl1.Width, glControl1.Height, cornerCount); } // step the field forward in time field.StepForward(3); // Create a Skia surface using the OpenGL control SKColorType colorType = SKColorType.Rgba8888; GRContext contextOpenGL = GRContext.Create(GRBackend.OpenGL, GRGlInterface.CreateNativeGlInterface()); GL.GetInteger(GetPName.FramebufferBinding, out var framebuffer); GRGlFramebufferInfo glInfo = new GRGlFramebufferInfo((uint)framebuffer, colorType.ToGlSizedFormat()); GL.GetInteger(GetPName.StencilBits, out var stencil); GRBackendRenderTarget renderTarget = new GRBackendRenderTarget(glControl1.Width, glControl1.Height, contextOpenGL.GetMaxSurfaceSampleCount(colorType), stencil, glInfo); SKSurface surface = SKSurface.Create(contextOpenGL, renderTarget, GRSurfaceOrigin.BottomLeft, colorType); SKCanvas canvas = surface.Canvas; // draw the stuff var bgColor = field.GetBackgroundColor(); canvas.Clear(new SKColor(bgColor.R, bgColor.G, bgColor.B)); // draw circles at every corner var paint = new SKPaint { Color = new SKColor(255, 255, 255), IsAntialias = true }; float radius = 2; for (int cornerIndex = 0; cornerIndex < field.corners.Length; cornerIndex++) { canvas.DrawCircle((float)field.corners[cornerIndex].X, (float)field.corners[cornerIndex].Y, radius, paint); } // draw lines between every corner and every other corner for (int i = 0; i < field.corners.Length; i++) { for (int j = 0; j < field.corners.Length; j++) { double distance = field.GetDistance(i, j); if (distance < maxVisibleDistance && distance != 0) { SKPoint pt1 = new SKPoint((float)field.corners[i].X, (float)field.corners[i].Y); SKPoint pt2 = new SKPoint((float)field.corners[j].X, (float)field.corners[j].Y); double distanceFraction = distance / maxVisibleDistance; byte alpha = (byte)(255 - distanceFraction * 256); var linePaint = new SKPaint { Color = new SKColor(255, 255, 255, alpha), IsAntialias = true }; canvas.DrawLine(pt1, pt2, linePaint); } } } // Force a display surface.Canvas.Flush(); glControl1.SwapBuffers(); // dispose to prevent memory access violations while exiting renderTarget?.Dispose(); contextOpenGL?.Dispose(); canvas?.Dispose(); surface?.Dispose(); // update the FPS display Text = field.GetBenchmarkMessage(); }
public SkiaPainter(SKSurface surface, IImageLoader imageLoader) { this.surface = surface; skCanvas = surface.Canvas; _imageLoader = imageLoader; }
private static void Main(string[] args) { if (args.Length != 1) { Console.WriteLine("Usage AlphaTab.Samples.PngDump.exe Path"); return; } // load score var score = ScoreLoader.LoadScoreFromBytes(File.ReadAllBytes(args[0])); // render score with svg engine and desired rendering width var settings = new Settings(); settings.Core.Engine = "skia"; var renderer = new ScoreRenderer(settings) { Width = 970 }; // iterate tracks for (var i = 0; i < score.Tracks.Count; i++) { var track = score.Tracks[i]; // render track Console.WriteLine("Rendering track {0} - {1}", i + 1, track.Name); var images = new List <SKImage>(); var totalWidth = 0; var totalHeight = 0; renderer.PartialRenderFinished.On(r => { images.Add((SKImage)r.RenderResult); }); renderer.RenderFinished.On(r => { totalWidth = (int)r.TotalWidth; totalHeight = (int)r.TotalHeight; }); renderer.RenderScore(score, new List <double> { track.Index }); // write png var info = new FileInfo(args[0]); var path = Path.Combine(info.DirectoryName, Path.GetFileNameWithoutExtension(info.Name) + "-" + i + ".png"); using var full = SKSurface.Create(new SKImageInfo(totalWidth, totalHeight, SKImageInfo.PlatformColorType, SKAlphaType.Premul)); var y = 0; foreach (var image in images) { full.Canvas.DrawImage(image, new SKRect(0, 0, image.Width, image.Height), new SKRect(0, y, image.Width, y + image.Height)); y += image.Height; } using var fullImage = full.Snapshot(); using var data = fullImage.Encode(SKEncodedImageFormat.Png, 100) .AsStream(true); using var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write); data.CopyTo(fileStream); } }
public virtual void Render() { if (glContext == null) { PrepareGLContexts(); } EAGLContext.SetCurrentContext(glContext); // get the new surface size var newSize = lastSize; if (recreateSurface) { Gles.glGetRenderbufferParameteriv(Gles.GL_RENDERBUFFER, Gles.GL_RENDERBUFFER_WIDTH, out var bufferWidth); Gles.glGetRenderbufferParameteriv(Gles.GL_RENDERBUFFER, Gles.GL_RENDERBUFFER_HEIGHT, out var bufferHeight); newSize = new SKSizeI(bufferWidth, bufferHeight); } // manage the drawing surface if (recreateSurface || renderTarget == null || lastSize != newSize || !renderTarget.IsValid) { // create or update the dimensions lastSize = newSize; // read the info from the buffer Gles.glGetIntegerv(Gles.GL_FRAMEBUFFER_BINDING, out var framebuffer); Gles.glGetIntegerv(Gles.GL_STENCIL_BITS, out var stencil); Gles.glGetIntegerv(Gles.GL_SAMPLES, out var samples); var maxSamples = context.GetMaxSurfaceSampleCount(colorType); if (samples > maxSamples) { samples = maxSamples; } glInfo = new GRGlFramebufferInfo((uint)framebuffer, colorType.ToGlSizedFormat()); // destroy the old surface surface?.Dispose(); surface = null; canvas = null; // re-create the render target renderTarget?.Dispose(); renderTarget = new GRBackendRenderTarget(newSize.Width, newSize.Height, samples, stencil, glInfo); } // create the surface if (surface == null) { surface = SKSurface.Create(context, renderTarget, surfaceOrigin, colorType); canvas = surface.Canvas; } using (new SKAutoCanvasRestore(canvas, true)) { // start drawing #pragma warning disable CS0618 // Type or member is obsolete var e = new SKPaintGLSurfaceEventArgs(surface, renderTarget, surfaceOrigin, colorType, glInfo); OnPaintSurface(e); DrawInSurface(e.Surface, e.RenderTarget); SKDelegate?.DrawInSurface(e.Surface, e.RenderTarget); #pragma warning restore CS0618 // Type or member is obsolete } // flush the SkiaSharp context to the GL context canvas.Flush(); context.Flush(); // present the GL buffers glContext.PresentRenderBuffer(Gles.GL_RENDERBUFFER); EAGLContext.SetCurrentContext(null); }
void OnPaintSurface(SKSurface surface, int width, int height) { // Get the canvas SKCanvas canvas = surface.Canvas; // These two dimensions should be the same. int canvasSize = Math.Min(width, height); // If bitmap does not exist, create it if (bitmap == null) { // Set three fields bitmapSize = canvasSize; bitmap = new SKBitmap(bitmapSize, bitmapSize); bitmapCanvas = new SKCanvas(bitmap); // Establishes circular clipping and colors background PrepBitmap(bitmapCanvas, bitmapSize); } // If the canvas has become larger, make a new bitmap of that size. else if (bitmapSize < canvasSize) { // New versions of the three fields int newBitmapSize = canvasSize; SKBitmap newBitmap = new SKBitmap(newBitmapSize, newBitmapSize); SKCanvas newBitmapCanvas = new SKCanvas(newBitmap); // New circular clipping and background PrepBitmap(newBitmapCanvas, newBitmapSize); // Copy old bitmap to new bitmap float diff = (newBitmapSize - bitmapSize) / 2f; newBitmapCanvas.DrawBitmap(bitmap, diff, diff); // Dispose old bitmap and its canvas bitmapCanvas.Dispose(); bitmap.Dispose(); // Set fields to new values bitmap = newBitmap; bitmapCanvas = newBitmapCanvas; bitmapSize = newBitmapSize; } // Clear the canvas canvas.Clear(SKColors.White); // Set the rotate transform float radius = canvasSize / 2; canvas.RotateDegrees(angle, radius, radius); // Set a circular clipping area clipPath.Reset(); clipPath.AddCircle(radius, radius, radius); canvas.ClipPath(clipPath); // Draw the bitmap float offset = (canvasSize - bitmapSize) / 2f; canvas.DrawBitmap(bitmap, offset, offset); // Draw the cross hairs canvas.DrawLine(radius, 0, radius, canvasSize, thinLinePaint); canvas.DrawLine(0, radius, canvasSize, radius, thinLinePaint); }
public SKPaintSurfaceEventArgs(SKSurface surface, SKImageInfo info) : this(surface, info, info) { }
protected virtual void OnDrawFrame(SKSurface surface, GRBackendRenderTargetDesc renderTarget) { }
public static Image buildBackground(string patternSource, Assembly assembly, int Width, int Height, double bottomAdjustment = GlobalStatusSingleton.PATTERN_PCT, double sideAdjustment = GlobalStatusSingleton.PATTERN_FULL_COVERAGE) { Image result = null; if ((Width == -1) || (Height == -1)) { return(result); } using (var resource = assembly.GetManifestResourceStream(patternSource)) using (var stream = new SKManagedStream(resource)) { var bitmap = SKBitmap.Decode(stream); int tilesWide = (int)((Width * sideAdjustment) / bitmap.Width); int tilesHigh = (int)((Height * bottomAdjustment) / bitmap.Height); try { using (var tempSurface = SKSurface.Create(new SKImageInfo((int)Width, (int)Height))) { var canvas = tempSurface.Canvas; canvas.Clear(SKColors.White); SKBitmap bottomEdge = new SKBitmap(); SKBitmap rightEdge = new SKBitmap(); SKBitmap corner = new SKBitmap(); int excessH = (int)(Height * bottomAdjustment) - (tilesHigh * bitmap.Height); int excessW = (int)(Width * sideAdjustment) - (tilesWide * bitmap.Width); if (excessH > 0) { bitmap.ExtractSubset(bottomEdge, new SKRectI(0, 0, bitmap.Width, excessH)); } if (excessW > 0) { bitmap.ExtractSubset(rightEdge, new SKRectI(0, 0, excessW, bitmap.Height)); } if ((excessH > 0) && (excessW > 0)) { bitmap.ExtractSubset(corner, new SKRectI(0, 0, excessW, excessH)); } for (int i = 0; i < tilesWide; i++) { for (int j = 0; j < tilesHigh; j++) { canvas.DrawBitmap(bitmap, SKRect.Create(i * bitmap.Width, j * bitmap.Height, bitmap.Width, bitmap.Height)); } // this covers the bottom except lower right corner. if (Height > tilesHigh * bitmap.Height) { canvas.DrawBitmap(bottomEdge, SKRect.Create(i * bitmap.Width, tilesHigh * bitmap.Height, bitmap.Width, excessH)); } } // this is the far side, but not lower right corner. if (Width > tilesWide * bitmap.Width) { for (int k = 0; k < tilesHigh; k++) { canvas.DrawBitmap(rightEdge, SKRect.Create(tilesWide * bitmap.Width, k * bitmap.Height, excessW, bitmap.Height)); } } // and finally the bottom right corner. if ((Height > tilesHigh * bitmap.Height) && (Width > tilesWide * bitmap.Width)) { canvas.DrawBitmap(corner, SKRect.Create(tilesWide * bitmap.Width, tilesHigh * bitmap.Height, excessW, excessH)); } SKImage skImage = tempSurface.Snapshot(); result = SKImageToXamarinImage(skImage); } } catch (Exception e) { string msg = e.ToString(); } } return(result); }
public MapInfo GetMapInfo(double x, double y, IReadOnlyViewport viewport, IEnumerable <ILayer> layers, int margin = 0) { // todo: use margin to increase the pixel area // todo: We will need to select on style instead of layer layers = layers.Where(l => l.IsMapInfoLayer); var list = new List <MapInfoRecord>(); var result = new MapInfo() { ScreenPosition = new Point(x, y), WorldPosition = viewport.ScreenToWorld(x, y), Resolution = viewport.Resolution }; try { var width = (int)viewport.Width; var height = (int)viewport.Height; var imageInfo = new SKImageInfo(width, height, SKImageInfo.PlatformColorType, SKAlphaType.Unpremul); var intX = (int)x; var intY = (int)y; using (var surface = SKSurface.Create(imageInfo)) { if (surface == null) { return(null); } surface.Canvas.ClipRect(new SKRect((float)(x - 1), (float)(y - 1), (float)(x + 1), (float)(y + 1))); surface.Canvas.Clear(SKColors.Transparent); var pixmap = surface.PeekPixels(); var color = pixmap.GetPixelColor(intX, intY); VisibleFeatureIterator.IterateLayers(viewport, layers, (v, layer, style, feature, opacity) => { // 1) Clear the entire bitmap surface.Canvas.Clear(SKColors.Transparent); // 2) Render the feature to the clean canvas RenderFeature(surface.Canvas, v, layer, style, feature, opacity); // 3) Check if the pixel has changed. if (color != pixmap.GetPixelColor(intX, intY)) { // 4) Add feature and style to result list.Add(new MapInfoRecord(feature, style, layer)); } }); } if (list.Count == 0) { return(result); } list.Reverse(); var itemDrawnOnTop = list.First(); result.Feature = itemDrawnOnTop.Feature; result.Style = itemDrawnOnTop.Style; result.Layer = itemDrawnOnTop.Layer; result.MapInfoRecords = list; } catch (Exception exception) { Logger.Log(LogLevel.Error, "Unexpected error in skia renderer", exception); } return(result); }
public static Image buildBackgroundFromBytes(byte[] patternSource, Assembly assembly, int Width, int Height, double bottomAdjustment = GlobalStatusSingleton.PATTERN_PCT, double sideAdjustment = GlobalStatusSingleton.PATTERN_FULL_COVERAGE) { Image result = null; if ((Width == -1) || (Height == -1)) { return(result); } if (patternSource == null) { return(result); } SKBitmap bitmap = new SKBitmap(new SKImageInfo(Width, Height)); var bitmapFullSize = buildFixedRotationSKBitmapFromBytes(patternSource); if ((bitmapFullSize.Width > Width) || (bitmapFullSize.Height > Height)) { // passed in image is larger than the screen. shrink to fit. bitmapFullSize.Resize(bitmap, SKBitmapResizeMethod.Box); } else { bitmap = bitmapFullSize; } int tilesWide = (int)((Width * sideAdjustment) / bitmap.Width); int tilesHigh = (int)((Height * bottomAdjustment) / bitmap.Height); try { using (var tempSurface = SKSurface.Create(new SKImageInfo((int)Width, (int)Height))) { var canvas = tempSurface.Canvas; canvas.Clear(SKColors.White); SKBitmap bottomEdge = new SKBitmap(); SKBitmap rightEdge = new SKBitmap(); SKBitmap corner = new SKBitmap(); int excessH = (int)(Height * bottomAdjustment) - (tilesHigh * bitmap.Height); int excessW = (int)(Width * sideAdjustment) - (tilesWide * bitmap.Width); if (excessH > 0) { bitmap.ExtractSubset(bottomEdge, new SKRectI(0, 0, bitmap.Width, excessH)); } if (excessW > 0) { bitmap.ExtractSubset(rightEdge, new SKRectI(0, 0, excessW, bitmap.Height)); } if ((excessH > 0) && (excessW > 0)) { bitmap.ExtractSubset(corner, new SKRectI(0, 0, excessW, excessH)); } for (int i = 0; i < tilesWide; i++) { for (int j = 0; j < tilesHigh; j++) { canvas.DrawBitmap(bitmap, SKRect.Create(i * bitmap.Width, j * bitmap.Height, bitmap.Width, bitmap.Height)); } // this covers the bottom except lower right corner. if (Height > tilesHigh * bitmap.Height) { canvas.DrawBitmap(bottomEdge, SKRect.Create(i * bitmap.Width, tilesHigh * bitmap.Height, bitmap.Width, excessH)); } } // this is the far side, but not lower right corner. if (Width > tilesWide * bitmap.Width) { for (int k = 0; k < tilesHigh; k++) { canvas.DrawBitmap(rightEdge, SKRect.Create(tilesWide * bitmap.Width, k * bitmap.Height, excessW, bitmap.Height)); } } // and finally the bottom right corner. if ((Height > tilesHigh * bitmap.Height) && (Width > tilesWide * bitmap.Width)) { canvas.DrawBitmap(corner, SKRect.Create(tilesWide * bitmap.Width, tilesHigh * bitmap.Height, excessW, excessH)); } SKImage skImage = tempSurface.Snapshot(); result = SKImageToXamarinImage(skImage); } } catch (Exception e) { string msg = e.ToString(); } return(result); }
public SkiaDrawingContext(int width, int height, SKColor background, SKTypeface typeface) { this.typeface = typeface; surface = SKSurface.Create(width, height, SKImageInfo.PlatformColorType, SKAlphaType.Opaque); surface.Canvas.Clear(background); }
/// <summary> /// Initializes a new instance of the <see cref="SkiaSharpDrawingContext"/> class. /// </summary> /// <param name="info">The information.</param> /// <param name="surface">The surface.</param> /// <param name="canvas">The canvas.</param> public SkiaSharpDrawingContext(SKImageInfo info, SKSurface surface, SKCanvas canvas) { Info = info; Surface = surface; Canvas = canvas; }
public new void DrawInRect(GLKView view, CGRect rect) { if (designMode) { return; } // create the contexts if not done already if (context == null) { var glInterface = GRGlInterface.Create(); context = GRContext.CreateGl(glInterface); } // get the new surface size var newSize = new SKSizeI((int)DrawableWidth, (int)DrawableHeight); // manage the drawing surface if (renderTarget == null || lastSize != newSize || !renderTarget.IsValid) { // create or update the dimensions lastSize = newSize; // read the info from the buffer Gles.glGetIntegerv(Gles.GL_FRAMEBUFFER_BINDING, out var framebuffer); Gles.glGetIntegerv(Gles.GL_STENCIL_BITS, out var stencil); Gles.glGetIntegerv(Gles.GL_SAMPLES, out var samples); var maxSamples = context.GetMaxSurfaceSampleCount(colorType); if (samples > maxSamples) { samples = maxSamples; } glInfo = new GRGlFramebufferInfo((uint)framebuffer, colorType.ToGlSizedFormat()); // destroy the old surface surface?.Dispose(); surface = null; canvas = null; // re-create the render target renderTarget?.Dispose(); renderTarget = new GRBackendRenderTarget(newSize.Width, newSize.Height, samples, stencil, glInfo); } // create the surface if (surface == null) { surface = SKSurface.Create(context, renderTarget, surfaceOrigin, colorType); canvas = surface.Canvas; } using (new SKAutoCanvasRestore(canvas, true)) { // start drawing var e = new SKPaintGLSurfaceEventArgs(surface, renderTarget, surfaceOrigin, colorType, glInfo); OnPaintSurface(e); #pragma warning disable CS0618 // Type or member is obsolete DrawInSurface(e.Surface, e.RenderTarget); #pragma warning restore CS0618 // Type or member is obsolete } // flush the SkiaSharp contents to GL canvas.Flush(); context.Flush(); }
protected abstract void PaintSurface(SKSurface surface);