/// <inheritdoc/> public string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, ImageOrientation?orientation, int quality, ImageProcessingOptions options, ImageFormat selectedOutputFormat) { if (inputPath.Length == 0) { throw new ArgumentException("String can't be empty.", nameof(inputPath)); } if (outputPath.Length == 0) { throw new ArgumentException("String can't be empty.", nameof(outputPath)); } var skiaOutputFormat = GetImageFormat(selectedOutputFormat); var hasBackgroundColor = !string.IsNullOrWhiteSpace(options.BackgroundColor); var hasForegroundColor = !string.IsNullOrWhiteSpace(options.ForegroundLayer); var blur = options.Blur ?? 0; var hasIndicator = options.AddPlayedIndicator || options.UnplayedCount.HasValue || !options.PercentPlayed.Equals(0); using (var bitmap = GetBitmap(inputPath, options.CropWhiteSpace, autoOrient, orientation)) { if (bitmap == null) { throw new InvalidDataException($"Skia unable to read image {inputPath}"); } var originalImageSize = new ImageDimensions(bitmap.Width, bitmap.Height); if (!options.CropWhiteSpace && options.HasDefaultOptions(inputPath, originalImageSize) && !autoOrient) { // Just spit out the original file if all the options are default return(inputPath); } var newImageSize = ImageHelper.GetNewImageSize(options, originalImageSize); var width = newImageSize.Width; var height = newImageSize.Height; using (var resizedBitmap = new SKBitmap(width, height, bitmap.ColorType, bitmap.AlphaType)) { // scale image bitmap.ScalePixels(resizedBitmap, SKFilterQuality.High); // If all we're doing is resizing then we can stop now if (!hasBackgroundColor && !hasForegroundColor && blur == 0 && !hasIndicator) { Directory.CreateDirectory(Path.GetDirectoryName(outputPath)); using (var outputStream = new SKFileWStream(outputPath)) using (var pixmap = new SKPixmap(new SKImageInfo(width, height), resizedBitmap.GetPixels())) { pixmap.Encode(outputStream, skiaOutputFormat, quality); return(outputPath); } } // create bitmap to use for canvas drawing used to draw into bitmap using (var saveBitmap = new SKBitmap(width, height)) // , bitmap.ColorType, bitmap.AlphaType)) using (var canvas = new SKCanvas(saveBitmap)) { // set background color if present if (hasBackgroundColor) { canvas.Clear(SKColor.Parse(options.BackgroundColor)); } // Add blur if option is present if (blur > 0) { // create image from resized bitmap to apply blur using (var paint = new SKPaint()) using (var filter = SKImageFilter.CreateBlur(blur, blur)) { paint.ImageFilter = filter; canvas.DrawBitmap(resizedBitmap, SKRect.Create(width, height), paint); } } else { // draw resized bitmap onto canvas canvas.DrawBitmap(resizedBitmap, SKRect.Create(width, height)); } // If foreground layer present then draw if (hasForegroundColor) { if (!double.TryParse(options.ForegroundLayer, out double opacity)) { opacity = .4; } canvas.DrawColor(new SKColor(0, 0, 0, (byte)((1 - opacity) * 0xFF)), SKBlendMode.SrcOver); } if (hasIndicator) { DrawIndicator(canvas, width, height, options); } Directory.CreateDirectory(Path.GetDirectoryName(outputPath)); using (var outputStream = new SKFileWStream(outputPath)) { using (var pixmap = new SKPixmap(new SKImageInfo(width, height), saveBitmap.GetPixels())) { pixmap.Encode(outputStream, skiaOutputFormat, quality); } } } } } return(outputPath); }
public SavePhotoPage(SKPixmap skPixmap) : this() { pixmap = skPixmap; }
public static UIImage ToUIImage(this SKPixmap skiaPixmap, nfloat scale, UIImageOrientation orientation) { var cgImage = skiaPixmap.ToCGImage(); return(new UIImage(cgImage, scale, orientation)); }
public static UIImage ToUIImage(this SKPixmap skiaPixmap) { var cgImage = skiaPixmap.ToCGImage(); return(new UIImage(cgImage)); }
public static unsafe SKPixmap OtsuThreshold(this SKImage image) { SKPixmap pixmap = image.PeekPixels(); byte * bmpPtr = (byte *)pixmap.GetPixels().ToPointer(); int width = image.Width; int height = image.Height; int[] intHistogram = new int[256]; double[] histogram = new double[256]; int threshold = 0; // Build a histogram for (int row = 0; row < height; row++) { for (int col = 0; col < width; col++) { byte red = *bmpPtr++; byte green = *bmpPtr++; byte blue = *bmpPtr++; // Assuming SKColorType.Rgba8888 - used by iOS and Android // (UWP uses SKColorType.Bgra8888) int result = (byte)(0.2126 * red + 0.7152 * green + 0.0722 * blue); intHistogram[result]++; } } int pixelCount = width * height; double imageMean = 0; for (int i = 0; i < 256; i++) { histogram[i] = (double)intHistogram[i] / pixelCount; imageMean += histogram[i] * i; } double max = double.MinValue; double c1Prob = 0; double c2Prob = 1; double c1MeanInit = 0; for (int i = 0; i < 256 && c2Prob > 0; i++) { double c1Mean = c1MeanInit; double c2Mean = (imageMean - (c1Mean * c1Prob)) / c2Prob; double classVariance = c1Prob * (1.0 - c1Prob) * Math.Pow(c1Mean - c2Mean, 2); if (classVariance > max) { max = classVariance; threshold = i; } c1MeanInit *= c1Prob; c1Prob += histogram[i]; c2Prob -= histogram[i]; c1MeanInit += (double)i * (double)histogram[i]; if (Math.Abs(c1Prob) > 0) { c1MeanInit /= c1Prob; } } bmpPtr = (byte *)pixmap.GetPixels().ToPointer(); // Apply threshold for (int row = 0; row < height; row++) { for (int col = 0; col < width; col++) { if (*bmpPtr <= threshold) { *bmpPtr++ = 0; // red *bmpPtr++ = 0; // green *bmpPtr++ = 0; // blue } else { *bmpPtr++ = 255; // red *bmpPtr++ = 255; // green *bmpPtr++ = 255; // blue } bmpPtr += 1; } } return(pixmap); }
public static CIImage ToCIImage(this SKPixmap skiaPixmap) { return(skiaPixmap.ToCGImage()); }
private static void CompareVisualResult(double totalWidth, double totalHeight, List <RenderFinishedEventArgs> result, string referenceFileName, Uint8Array referenceFileData, string?message) { // TODO: get Skia to render like Chrome // https://github.com/mono/SkiaSharp/issues/1253 return; // ReSharper disable once HeuristicUnreachableCode #pragma warning disable 162 SKBitmap finalBitmap; using (var finalImageSurface = SKSurface.Create(new SKImageInfo((int)totalWidth, (int)totalHeight, SKImageInfo.PlatformColorType, SKAlphaType.Premul))) { var point = new SKPoint(); var rowHeight = 0; foreach (var partialResult in result) { var partialCanvas = partialResult.RenderResult; if (partialCanvas is SKImage img) { finalImageSurface.Canvas.DrawImage(img, point); if (partialResult.Height > rowHeight) { rowHeight = img.Height; } point.X += img.Width; if (point.X >= totalWidth) { point.X = 0; point.Y += rowHeight; rowHeight = 0; } } } using var finalImage = finalImageSurface.Snapshot(); finalBitmap = SKBitmap.FromImage(finalImage); } var finalImageFileName = Path.ChangeExtension(referenceFileName, ".new.png"); using (finalBitmap) { var dir = Path.GetDirectoryName(finalImageFileName); Directory.CreateDirectory(dir); using (var fileStream = new SKFileWStream(finalImageFileName)) { SKPixmap.Encode(fileStream, finalBitmap, SKEncodedImageFormat.Png, 100); } SKBitmap referenceBitmap; using (var data = SKData.CreateCopy(referenceFileData.Buffer.Raw)) { referenceBitmap = SKBitmap.Decode(data); } using (referenceBitmap) { var compareResult = PixelMatch.Run(finalBitmap, referenceBitmap, new PixelMatchOptions { Threshold = 0.8, IncludeAntiAlias = false, IgnoreTransparent = true, CreateOutputImage = true }); using (compareResult.Output) { Assert.IsTrue(compareResult.SizesMatch, "Dimensions differ"); if (compareResult.Mismatch > 0.01) { var diffImageName = Path.ChangeExtension(referenceFileName, ".diff.png"); using (var fileStream = new SKFileWStream(diffImageName)) { SKPixmap.Encode(fileStream, compareResult.Output, SKEncodedImageFormat.Png, 100); } Assert.Fail( $"Difference between original and new image is too big: {compareResult.Mismatch:P}, {compareResult.DifferentPixels}/{compareResult.TotalPixels}"); } } } } File.Delete(finalImageFileName); #pragma warning restore 162 }
static unsafe SKPixmap ToRGBAPixmap(this SKImage image, float[,] y, float[,] cb, float[,] cr, float[,] a) { if (y == null) { throw new ArgumentException("y can't be null"); } if (cb == null) { throw new ArgumentException("cb can't be null"); } if (cr == null) { throw new ArgumentException("cr can't be null"); } if (a == null) { throw new ArgumentException("a can't be null"); } SKPixmap pixmap = image.PeekPixels(); byte * bmpPtr = (byte *)pixmap.GetPixels().ToPointer(); int width = image.Width; int height = image.Height; for (int row = 0; row < height; row++) { for (int col = 0; col < width; col++) { float yValue = y[col, row]; float cbValue = cb[col, row] - 127.5f; float crValue = cr[col, row] - 127.5f; float aValue = a[col, row]; int red = (int)(yValue + 1.40200f * crValue + 0.5f); int green = (int)(yValue - 0.34414f * cbValue - 0.71417f * crValue + 0.5f); int blue = (int)(yValue + 1.77200f * cbValue + 0.5f); int alpha = (int)aValue; if (red < 0) { red = 0; } else if (red > 255) { red = 255; } if (green < 0) { green = 0; } else if (green > 255) { green = 255; } if (blue < 0) { blue = 0; } else if (blue > 255) { blue = 255; } if (alpha < 0) { alpha = 0; } else if (alpha > 255) { alpha = 255; } *bmpPtr++ = (byte)red; *bmpPtr++ = (byte)green; *bmpPtr++ = (byte)blue; *bmpPtr++ = (byte)alpha; } } return(pixmap); }
public static IHtmlContent Render(this SKPixmap pixmap) { using var image = SKImage.FromPixels(pixmap); return(Render(image)); }
private void UpdateMarkerIcon(SKPin pin, Marker marker) { SKPixmap markerBitmap = DrawMarker(pin); marker.SetIcon(BitmapDescriptorFactory.FromBitmap(markerBitmap.ToBitmap())); }
public async Task <DataResolverResult> Resolve(string identifier, TaskParameter parameters, CancellationToken token) { ImageSource source = parameters.Source; if (!string.IsNullOrWhiteSpace(parameters.LoadingPlaceholderPath) && parameters.LoadingPlaceholderPath == identifier) { source = parameters.LoadingPlaceholderSource; } else if (!string.IsNullOrWhiteSpace(parameters.ErrorPlaceholderPath) && parameters.ErrorPlaceholderPath == identifier) { source = parameters.ErrorPlaceholderSource; } var resolvedData = await(Configuration.DataResolverFactory ?? new DataResolverFactory()) .GetResolver(identifier, source, parameters, Configuration) .Resolve(identifier, parameters, token).ConfigureAwait(false); if (resolvedData?.Stream == null) { throw new FileNotFoundException(identifier); } var svg = new SKSvg() { ThrowOnUnsupportedElement = false, }; SKPicture picture; if (ReplaceStringMap == null || ReplaceStringMap.Count == 0) { using (var svgStream = resolvedData.Stream) { picture = svg.Load(svgStream); } } else { using (var svgStream = resolvedData.Stream) using (var reader = new StreamReader(svgStream)) { var inputString = await reader.ReadToEndAsync(); foreach (var map in ReplaceStringMap .Where(v => v.Key.StartsWith("regex:"))) { inputString = Regex.Replace(inputString, map.Key.Substring(6), map.Value); } var builder = new StringBuilder(inputString); foreach (var map in ReplaceStringMap .Where(v => !v.Key.StartsWith("regex:", StringComparison.OrdinalIgnoreCase))) { builder.Replace(map.Key, map.Value); } using (var svgFinalStream = new MemoryStream(Encoding.UTF8.GetBytes(builder.ToString()))) { picture = svg.Load(svgFinalStream); } } } double sizeX = 0; double sizeY = 0; if (VectorWidth <= 0 && VectorHeight <= 0) { if (picture.CullRect.Width > 0) { sizeX = picture.CullRect.Width; } else { sizeX = 300; } if (picture.CullRect.Height > 0) { sizeY = picture.CullRect.Height; } else { sizeY = 300; } } else if (VectorWidth > 0 && VectorHeight > 0) { sizeX = VectorWidth; sizeY = VectorHeight; } else if (VectorWidth > 0) { sizeX = VectorWidth; sizeY = (VectorWidth / picture.CullRect.Width) * picture.CullRect.Height; } else { sizeX = (VectorHeight / picture.CullRect.Height) * picture.CullRect.Width; sizeY = VectorHeight; } if (UseDipUnits) { sizeX = sizeX.DpToPixels(); sizeY = sizeY.DpToPixels(); } resolvedData.ImageInformation.SetType(ImageInformation.ImageType.SVG); using (var bitmap = new SKBitmap(new SKImageInfo((int)sizeX, (int)sizeY))) using (var canvas = new SKCanvas(bitmap)) using (var paint = new SKPaint()) { canvas.Clear(SKColors.Transparent); float scaleX = (float)sizeX / picture.CullRect.Width; float scaleY = (float)sizeY / picture.CullRect.Height; var matrix = SKMatrix.MakeScale(scaleX, scaleY); canvas.DrawPicture(picture, ref matrix, paint); canvas.Flush(); #if __IOS__ var info = bitmap.Info; CGImage cgImage; IntPtr size; using (var provider = new CGDataProvider(bitmap.GetPixels(out size), size.ToInt32())) using (var colorSpace = CGColorSpace.CreateDeviceRGB()) using (cgImage = new CGImage(info.Width, info.Height, 8, info.BitsPerPixel, info.RowBytes, colorSpace, CGBitmapFlags.PremultipliedLast | CGBitmapFlags.ByteOrder32Big, provider, null, false, CGColorRenderingIntent.Default)) { IDecodedImage <object> container = new DecodedImage <object>() { Image = new UIImage(cgImage), }; return(new DataResolverResult(container, resolvedData.LoadingResult, resolvedData.ImageInformation)); } #elif __MACOS__ var info = bitmap.Info; CGImage cgImage; IntPtr size; using (var provider = new CGDataProvider(bitmap.GetPixels(out size), size.ToInt32())) using (var colorSpace = CGColorSpace.CreateDeviceRGB()) using (cgImage = new CGImage(info.Width, info.Height, 8, info.BitsPerPixel, info.RowBytes, colorSpace, CGBitmapFlags.PremultipliedLast | CGBitmapFlags.ByteOrder32Big, provider, null, false, CGColorRenderingIntent.Default)) { IDecodedImage <object> container = new DecodedImage <object>() { Image = new NSImage(cgImage, CGSize.Empty), }; return(new DataResolverResult(container, resolvedData.LoadingResult, resolvedData.ImageInformation)); } #elif __ANDROID__ using (var skiaPixmap = bitmap.PeekPixels()) { var info = skiaPixmap.Info; // destination values var config = Bitmap.Config.Argb8888; var dstInfo = new SKImageInfo(info.Width, info.Height); // try keep the pixel format if we can switch (info.ColorType) { case SKColorType.Alpha8: config = Bitmap.Config.Alpha8; dstInfo.ColorType = SKColorType.Alpha8; break; case SKColorType.Rgb565: config = Bitmap.Config.Rgb565; dstInfo.ColorType = SKColorType.Rgb565; dstInfo.AlphaType = SKAlphaType.Opaque; break; case SKColorType.Argb4444: config = Bitmap.Config.Argb4444; dstInfo.ColorType = SKColorType.Argb4444; break; } // destination bitmap var bmp = Bitmap.CreateBitmap(info.Width, info.Height, config); var ptr = bmp.LockPixels(); // copy var success = skiaPixmap.ReadPixels(dstInfo, ptr, dstInfo.RowBytes); // confirm bmp.UnlockPixels(); if (!success) { bmp.Recycle(); bmp.Dispose(); bmp = null; } IDecodedImage <object> container = new DecodedImage <object>() { Image = bmp, }; return(new DataResolverResult(container, resolvedData.LoadingResult, resolvedData.ImageInformation)); } #elif __WINDOWS__ if (parameters.Transformations == null || parameters.Transformations.Count == 0) { WriteableBitmap writeableBitmap = null; await Configuration.MainThreadDispatcher.PostAsync(async() => { using (var skiaImage = SKImage.FromPixels(bitmap.PeekPixels())) { var info = new SKImageInfo(skiaImage.Width, skiaImage.Height); writeableBitmap = new WriteableBitmap(info.Width, info.Height); var buffer = writeableBitmap.PixelBuffer as IBufferByteAccess; if (buffer == null) { throw new InvalidCastException("Unable to convert WriteableBitmap.PixelBuffer to IBufferByteAccess."); } IntPtr ptr; var hr = buffer.Buffer(out ptr); if (hr < 0) { throw new InvalidCastException("Unable to retrieve pixel address from WriteableBitmap.PixelBuffer."); } using (var pixmap = new SKPixmap(info, ptr)) { skiaImage.ReadPixels(pixmap, 0, 0); } writeableBitmap.Invalidate(); } }); IDecodedImage <object> container = new DecodedImage <object>() { Image = writeableBitmap, }; return(new DataResolverResult(container, resolvedData.LoadingResult, resolvedData.ImageInformation)); } #endif lock (_encodingLock) { using (var image = SKImage.FromBitmap(bitmap)) //using (var data = image.Encode(SKImageEncodeFormat.Png, 100)) //TODO disabled because of https://github.com/mono/SkiaSharp/issues/285 using (var data = image.Encode()) { var stream = new MemoryStream(); data.SaveTo(stream); stream.Position = 0; return(new DataResolverResult(stream, resolvedData.LoadingResult, resolvedData.ImageInformation)); } } } }
public void DrawImage(Image img, Rectangle rectangle, float srcX, float srcY, float srcWidth, float srcHeight, GraphicsUnit graphicsUnit, ImageAttributes tileFlipXYAttributes) { var coltype = SKColorType.Bgra8888; ((Bitmap)img).MakeTransparent(Color.Transparent); var data = ((Bitmap)img).LockBits(new Rectangle(0, 0, img.Width, img.Height), ImageLockMode.ReadOnly, SKColorType.Bgra8888); if (CompositingMode == CompositingMode.SourceOver) { _paint.BlendMode = SKBlendMode.SrcOver; } if (CompositingMode == CompositingMode.SourceCopy) { _paint.BlendMode = SKBlendMode.Src; } //if(img.PixelFormat == PixelFormat.Format32bppArgb) _paint.Color = SKColors.Black; var imginfo = new SKImageInfo(img.Width, img.Height, coltype, SKAlphaType.Premul); var pxmap = new SKPixmap(imginfo, data.Scan0, data.Stride); var image = SKImage.FromPixels(pxmap); if (image == null) { return; } _image.DrawImage(image, new SKRect(srcX, srcY, srcX + srcWidth, srcY + srcHeight), new SKRect(rectangle.X, rectangle.Y, rectangle.Right, rectangle.Bottom), _paint); ((Bitmap)img).UnlockBits(data); data = null; return; try { using (var skbmp = new SKBitmap(new SKImageInfo(img.Width, img.Height, coltype, SKAlphaType.Unpremul))) { skbmp.SetPixels(data.Scan0); _image.DrawBitmap(skbmp, new SKRect(srcX, srcY, srcX + srcWidth, srcY + srcHeight), new SKRect(rectangle.X, rectangle.Y, rectangle.Right, rectangle.Bottom), _paint); } } catch { } ((Bitmap)img).UnlockBits(data); data = null; }
public static string Serialize(SKPixmap image, int componentsX, int componentsY) { using var wrapper = SKImage.FromPixels(image); return(Serialize(wrapper, componentsX, componentsY)); }
public byte[] GetImage(string referenceName, int zoomLevel, int chunk) { var refer = _service._references.First(r => r.Name == referenceName); var height = (_service._histograms[refer.Name].Max() + 1) * 15; using var surface = SKSurface.Create(new SKImageInfo(WIDTH, height)); using SKCanvas canvas = surface.Canvas; canvas.Clear(); using SKPaint paint = new SKPaint { Style = SKPaintStyle.Stroke, Color = SKColors.Black, StrokeWidth = 1 }; using SKPaint fillPaint = new SKPaint { Style = SKPaintStyle.Fill, Color = SKColors.Blue, StrokeWidth = 1 }; using SKPaint reverseFillPaint = new SKPaint { Style = SKPaintStyle.Fill, Color = SKColors.Red, StrokeWidth = 1 }; var startY = 7; var list = new List <long> { 0 }; using var reader = new StreamReader($"{_service._references.IndexOf(refer)}.tmp"); string line; while ((line = reader.ReadLine()) != null) { var splitLine = line.Split('\t'); var read = new SamFile { QueryName = splitLine[0], ReferenceName = splitLine[1], StartingPos = int.Parse(splitLine[2]), DrawExpressions = DataService.GetDrawExpressions(splitLine[3]), Reverse = bool.Parse(splitLine[4]) }; var realStartingX = zoomLevel * read.StartingPos; if (realStartingX >= (chunk + 1) * WIDTH) { break; } var index = list.FindIndex(i => realStartingX > i + 1); var y = startY * (index != -1 ? index : list.Count); var lineLength = read.DrawExpressions.Sum(de => de.Length) * zoomLevel; if (index == -1) { list.Add(realStartingX + lineLength); } else { list[index] = realStartingX + lineLength; } realStartingX -= chunk * WIDTH; if (realStartingX < 0 && realStartingX + lineLength < 0) { continue; } canvas.DrawLine(new SKPoint(realStartingX, y + 3), new SKPoint(realStartingX + lineLength, y + 3), paint); var currentX = realStartingX; foreach (var draw in read.DrawExpressions) { if (draw.Type == DrawType.Rectangle) { canvas.DrawRect(currentX, y, draw.Length * zoomLevel, 5, read.Reverse ? reverseFillPaint : fillPaint); canvas.DrawRect(currentX, y, draw.Length * zoomLevel, 5, paint); } currentX += draw.Length * zoomLevel; } canvas.Flush(); } var width = (chunk + 1) * WIDTH * zoomLevel > refer.Length * zoomLevel ? refer.Length * zoomLevel % WIDTH : WIDTH; var realHeight = 7 * (list.Count + 1); SKPixmap pixmap = surface.Snapshot().Subset(SKRectI.Create(0, 0, width, realHeight)).PeekPixels(); SKData data; if (realHeight > 15000) { data = pixmap.Encode(SKPngEncoderOptions.Default); } else { var options = new SKWebpEncoderOptions(SKWebpEncoderCompression.Lossless, 100); data = pixmap.Encode(options); } return(data.ToArray()); }
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)); }