768 строки
20 KiB
C#
768 строки
20 KiB
C#
//
|
|
// Bindings for SKBitmap
|
|
//
|
|
// Author:
|
|
// Matthew Leibowitz
|
|
//
|
|
// Copyright 2016 Xamarin Inc
|
|
//
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace SkiaSharp
|
|
{
|
|
// public delegates
|
|
public delegate void SKBitmapReleaseDelegate (IntPtr address, object context);
|
|
|
|
// internal proxy delegates
|
|
[UnmanagedFunctionPointer (CallingConvention.Cdecl)]
|
|
internal delegate void SKBitmapReleaseDelegateInternal (IntPtr address, IntPtr context);
|
|
|
|
// TODO: keep in mind SKBitmap may be going away (according to Google)
|
|
// TODO: `ComputeIsOpaque` may be useful
|
|
// TODO: `GenerationID` may be useful
|
|
// TODO: `GetAddr` and `GetPixel` are confusing
|
|
|
|
public class SKBitmap : SKObject
|
|
{
|
|
private const string UnsupportedColorTypeMessage = "Setting the ColorTable is only supported for bitmaps with ColorTypes of Index8.";
|
|
private const string UnableToAllocatePixelsMessage = "Unable to allocate pixels for the bitmap.";
|
|
|
|
// so the GC doesn't collect the delegate
|
|
private static readonly SKBitmapReleaseDelegateInternal releaseDelegateInternal;
|
|
private static readonly IntPtr releaseDelegate;
|
|
static SKBitmap ()
|
|
{
|
|
releaseDelegateInternal = new SKBitmapReleaseDelegateInternal (SKBitmapReleaseInternal);
|
|
releaseDelegate = Marshal.GetFunctionPointerForDelegate (releaseDelegateInternal);
|
|
}
|
|
|
|
[Preserve]
|
|
internal SKBitmap (IntPtr handle, bool owns)
|
|
: base (handle, owns)
|
|
{
|
|
}
|
|
|
|
public SKBitmap ()
|
|
: this (SkiaApi.sk_bitmap_new (), true)
|
|
{
|
|
if (Handle == IntPtr.Zero) {
|
|
throw new InvalidOperationException ("Unable to create a new SKBitmap instance.");
|
|
}
|
|
}
|
|
|
|
public SKBitmap (int width, int height, bool isOpaque = false)
|
|
: this (width, height, SKImageInfo.PlatformColorType, isOpaque ? SKAlphaType.Opaque : SKAlphaType.Premul)
|
|
{
|
|
}
|
|
|
|
public SKBitmap (int width, int height, SKColorType colorType, SKAlphaType alphaType)
|
|
: this (new SKImageInfo (width, height, colorType, alphaType))
|
|
{
|
|
}
|
|
|
|
public SKBitmap (SKImageInfo info)
|
|
: this (info, info.RowBytes)
|
|
{
|
|
}
|
|
|
|
public SKBitmap (SKImageInfo info, int rowBytes)
|
|
: this ()
|
|
{
|
|
var cinfo = SKImageInfoNative.FromManaged (ref info);
|
|
if (!SkiaApi.sk_bitmap_try_alloc_pixels (Handle, ref cinfo, (IntPtr)rowBytes)) {
|
|
throw new Exception (UnableToAllocatePixelsMessage);
|
|
}
|
|
}
|
|
|
|
public SKBitmap (SKImageInfo info, SKColorTable ctable, SKBitmapAllocFlags flags)
|
|
: this ()
|
|
{
|
|
if (!TryAllocPixels (info, ctable, flags)) {
|
|
throw new Exception (UnableToAllocatePixelsMessage);
|
|
}
|
|
}
|
|
|
|
public SKBitmap (SKImageInfo info, SKColorTable ctable)
|
|
: this (info, ctable, SKBitmapAllocFlags.None)
|
|
{
|
|
}
|
|
|
|
private bool TryAllocPixels (SKImageInfo info, SKColorTable ctable, SKBitmapAllocFlags flags = SKBitmapAllocFlags.None)
|
|
{
|
|
var cinfo = SKImageInfoNative.FromManaged (ref info);
|
|
return SkiaApi.sk_bitmap_try_alloc_pixels_with_color_table (Handle, ref cinfo, ctable != null ? ctable.Handle : IntPtr.Zero, flags);
|
|
}
|
|
|
|
protected override void Dispose (bool disposing)
|
|
{
|
|
if (Handle != IntPtr.Zero && OwnsHandle) {
|
|
SkiaApi.sk_bitmap_destructor (Handle);
|
|
}
|
|
|
|
base.Dispose (disposing);
|
|
}
|
|
|
|
public void Reset ()
|
|
{
|
|
SkiaApi.sk_bitmap_reset (Handle);
|
|
}
|
|
|
|
public void SetImmutable ()
|
|
{
|
|
SkiaApi.sk_bitmap_set_immutable (Handle);
|
|
}
|
|
|
|
public void Erase (SKColor color)
|
|
{
|
|
SkiaApi.sk_bitmap_erase (Handle, color);
|
|
}
|
|
|
|
public void Erase (SKColor color, SKRectI rect)
|
|
{
|
|
SkiaApi.sk_bitmap_erase_rect (Handle, color, ref rect);
|
|
}
|
|
|
|
public byte GetAddr8(int x, int y) => SkiaApi.sk_bitmap_get_addr_8 (Handle, x, y);
|
|
public UInt16 GetAddr16(int x, int y) => SkiaApi.sk_bitmap_get_addr_16 (Handle, x, y);
|
|
public UInt32 GetAddr32(int x, int y) => SkiaApi.sk_bitmap_get_addr_32 (Handle, x, y);
|
|
public IntPtr GetAddr(int x, int y) => SkiaApi.sk_bitmap_get_addr (Handle, x, y);
|
|
|
|
public SKPMColor GetIndex8Color (int x, int y)
|
|
{
|
|
return SkiaApi.sk_bitmap_get_index8_color (Handle, x, y);
|
|
}
|
|
|
|
public SKColor GetPixel (int x, int y)
|
|
{
|
|
return SkiaApi.sk_bitmap_get_pixel_color (Handle, x, y);
|
|
}
|
|
|
|
public void SetPixel (int x, int y, SKColor color)
|
|
{
|
|
if (ColorType == SKColorType.Index8)
|
|
{
|
|
throw new NotSupportedException ("This method is not supported for bitmaps with ColorTypes of Index8.");
|
|
}
|
|
SkiaApi.sk_bitmap_set_pixel_color (Handle, x, y, color);
|
|
}
|
|
|
|
public bool CanCopyTo (SKColorType colorType)
|
|
{
|
|
var srcCT = ColorType;
|
|
|
|
if (srcCT == SKColorType.Unknown) {
|
|
return false;
|
|
}
|
|
if (srcCT == SKColorType.Alpha8 && colorType != SKColorType.Alpha8) {
|
|
return false; // can't convert from alpha to non-alpha
|
|
}
|
|
|
|
bool sameConfigs = (srcCT == colorType);
|
|
switch (colorType) {
|
|
case SKColorType.Alpha8:
|
|
case SKColorType.Rgb565:
|
|
case SKColorType.Rgba8888:
|
|
case SKColorType.Bgra8888:
|
|
case SKColorType.RgbaF16:
|
|
break;
|
|
case SKColorType.Gray8:
|
|
if (!sameConfigs) {
|
|
return false;
|
|
}
|
|
break;
|
|
case SKColorType.Argb4444:
|
|
return
|
|
sameConfigs ||
|
|
srcCT == SKImageInfo.PlatformColorType ||
|
|
srcCT == SKColorType.Index8;
|
|
default:
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public SKBitmap Copy ()
|
|
{
|
|
return Copy (ColorType);
|
|
}
|
|
|
|
public SKBitmap Copy (SKColorType colorType)
|
|
{
|
|
var destination = new SKBitmap ();
|
|
if (!CopyTo (destination, colorType)) {
|
|
destination.Dispose ();
|
|
destination = null;
|
|
}
|
|
return destination;
|
|
}
|
|
|
|
public bool CopyTo (SKBitmap destination)
|
|
{
|
|
if (destination == null) {
|
|
throw new ArgumentNullException (nameof (destination));
|
|
}
|
|
return CopyTo (destination, ColorType);
|
|
}
|
|
|
|
public bool CopyTo (SKBitmap destination, SKColorType colorType)
|
|
{
|
|
// TODO: instead of working on `destination` directly, we should
|
|
// create a temporary bitmap and then inject the data
|
|
|
|
if (destination == null) {
|
|
throw new ArgumentNullException (nameof (destination));
|
|
}
|
|
|
|
if (!CanCopyTo (colorType)) {
|
|
return false;
|
|
}
|
|
|
|
SKPixmap srcPM = PeekPixels ();
|
|
if (srcPM == null) {
|
|
return false;
|
|
}
|
|
|
|
SKImageInfo dstInfo = srcPM.Info.WithColorType (colorType);
|
|
switch (colorType) {
|
|
case SKColorType.Rgb565:
|
|
// CopyTo() is not strict on alpha type. Here we set the src to opaque to allow
|
|
// the call to ReadPixels() to succeed and preserve this lenient behavior.
|
|
if (srcPM.AlphaType != SKAlphaType.Opaque) {
|
|
srcPM = srcPM.WithAlphaType (SKAlphaType.Opaque);
|
|
}
|
|
dstInfo.AlphaType = SKAlphaType.Opaque;
|
|
break;
|
|
case SKColorType.RgbaF16:
|
|
// The caller does not have an opportunity to pass a dst color space.
|
|
// Assume that they want linear sRGB.
|
|
dstInfo.ColorSpace = SKColorSpace.CreateSrgbLinear ();
|
|
if (srcPM.ColorSpace == null) {
|
|
// We can't do a sane conversion to F16 without a dst color space.
|
|
// Guess sRGB in this case.
|
|
srcPM = srcPM.WithColorSpace (SKColorSpace.CreateSrgb ());
|
|
}
|
|
break;
|
|
}
|
|
|
|
destination.Reset (); // TODO: is this needed?
|
|
if (!destination.TryAllocPixels (dstInfo, colorType == SKColorType.Index8 ? ColorTable : null)) {
|
|
return false;
|
|
}
|
|
|
|
SKPixmap dstPM = destination.PeekPixels ();
|
|
if (dstPM == null) {
|
|
return false;
|
|
}
|
|
|
|
// We can't do a sane conversion from F16 without a src color space. Guess sRGB in this case.
|
|
if (srcPM.ColorType == SKColorType.RgbaF16 && dstPM.ColorSpace == null) {
|
|
dstPM = dstPM.WithColorSpace (SKColorSpace.CreateSrgb ());
|
|
}
|
|
|
|
// ReadPixels does not yet support color spaces with parametric transfer functions. This
|
|
// works around that restriction when the color spaces are equal.
|
|
if (colorType != SKColorType.RgbaF16 && srcPM.ColorType != SKColorType.RgbaF16 && dstPM.ColorSpace == srcPM.ColorSpace) {
|
|
dstPM = dstPM.WithColorSpace (null);
|
|
srcPM = srcPM.WithColorSpace (null);
|
|
}
|
|
|
|
if (!srcPM.ReadPixels (dstPM)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public bool ExtractSubset(SKBitmap destination, SKRectI subset)
|
|
{
|
|
if (destination == null) {
|
|
throw new ArgumentNullException (nameof (destination));
|
|
}
|
|
return SkiaApi.sk_bitmap_extract_subset (Handle, destination.Handle, ref subset);
|
|
}
|
|
|
|
public bool ExtractAlpha(SKBitmap destination)
|
|
{
|
|
SKPointI offset;
|
|
return ExtractAlpha (destination, null, out offset);
|
|
}
|
|
|
|
public bool ExtractAlpha(SKBitmap destination, out SKPointI offset)
|
|
{
|
|
return ExtractAlpha (destination, null, out offset);
|
|
}
|
|
|
|
public bool ExtractAlpha(SKBitmap destination, SKPaint paint)
|
|
{
|
|
SKPointI offset;
|
|
return ExtractAlpha (destination, paint, out offset);
|
|
}
|
|
|
|
public bool ExtractAlpha(SKBitmap destination, SKPaint paint, out SKPointI offset)
|
|
{
|
|
if (destination == null) {
|
|
throw new ArgumentNullException (nameof (destination));
|
|
}
|
|
return SkiaApi.sk_bitmap_extract_alpha (Handle, destination.Handle, paint == null ? IntPtr.Zero : paint.Handle, out offset);
|
|
}
|
|
|
|
public bool ReadyToDraw => SkiaApi.sk_bitmap_ready_to_draw (Handle);
|
|
|
|
public SKImageInfo Info {
|
|
get {
|
|
SKImageInfoNative cinfo;
|
|
SkiaApi.sk_bitmap_get_info (Handle, out cinfo);
|
|
return SKImageInfoNative.ToManaged (ref cinfo);
|
|
}
|
|
}
|
|
|
|
public int Width {
|
|
get { return Info.Width; }
|
|
}
|
|
|
|
public int Height {
|
|
get { return Info.Height; }
|
|
}
|
|
|
|
public SKColorType ColorType {
|
|
get { return Info.ColorType; }
|
|
}
|
|
|
|
public SKAlphaType AlphaType {
|
|
get { return Info.AlphaType; }
|
|
}
|
|
|
|
public SKColorSpace ColorSpace {
|
|
get { return Info.ColorSpace; }
|
|
}
|
|
|
|
public int BytesPerPixel {
|
|
get { return Info.BytesPerPixel; }
|
|
}
|
|
|
|
public int RowBytes {
|
|
get { return (int)SkiaApi.sk_bitmap_get_row_bytes (Handle); }
|
|
}
|
|
|
|
public int ByteCount {
|
|
get { return (int)SkiaApi.sk_bitmap_get_byte_count (Handle); }
|
|
}
|
|
|
|
public IntPtr GetPixels ()
|
|
{
|
|
IntPtr length;
|
|
return GetPixels (out length);
|
|
}
|
|
|
|
public IntPtr GetPixels (out IntPtr length)
|
|
{
|
|
return SkiaApi.sk_bitmap_get_pixels (Handle, out length);
|
|
}
|
|
|
|
public void SetPixels(IntPtr pixels)
|
|
{
|
|
SetPixels (pixels, ColorTable);
|
|
}
|
|
|
|
public void SetPixels(IntPtr pixels, SKColorTable ct)
|
|
{
|
|
SkiaApi.sk_bitmap_set_pixels (Handle, pixels, ct != null ? ct.Handle : IntPtr.Zero);
|
|
}
|
|
|
|
public void SetColorTable(SKColorTable ct)
|
|
{
|
|
SetPixels (GetPixels (), ct);
|
|
}
|
|
|
|
public byte[] Bytes {
|
|
get {
|
|
IntPtr length;
|
|
var pixelsPtr = GetPixels (out length);
|
|
byte [] bytes = new byte [(int)length];
|
|
Marshal.Copy (pixelsPtr, bytes, 0, (int)length);
|
|
return bytes;
|
|
}
|
|
}
|
|
|
|
public SKColor[] Pixels {
|
|
get {
|
|
var info = Info;
|
|
var pixels = new SKColor [info.Width * info.Height];
|
|
SkiaApi.sk_bitmap_get_pixel_colors (Handle, pixels);
|
|
return pixels;
|
|
}
|
|
set {
|
|
SkiaApi.sk_bitmap_set_pixel_colors (Handle, value);
|
|
}
|
|
}
|
|
|
|
public bool IsEmpty {
|
|
get { return Info.IsEmpty; }
|
|
}
|
|
|
|
public bool IsNull {
|
|
get { return SkiaApi.sk_bitmap_is_null (Handle); }
|
|
}
|
|
|
|
public bool DrawsNothing {
|
|
get { return IsEmpty || IsNull; }
|
|
}
|
|
|
|
public bool IsImmutable {
|
|
get { return SkiaApi.sk_bitmap_is_immutable (Handle); }
|
|
}
|
|
|
|
public bool IsVolatile {
|
|
get { return SkiaApi.sk_bitmap_is_volatile (Handle); }
|
|
set { SkiaApi.sk_bitmap_set_volatile (Handle, value); }
|
|
}
|
|
|
|
public SKColorTable ColorTable {
|
|
get { return GetObject<SKColorTable> (SkiaApi.sk_bitmap_get_colortable (Handle), false); }
|
|
}
|
|
|
|
public static SKImageInfo DecodeBounds (Stream stream)
|
|
{
|
|
if (stream == null) {
|
|
throw new ArgumentNullException (nameof (stream));
|
|
}
|
|
return DecodeBounds (WrapManagedStream (stream));
|
|
}
|
|
|
|
public static SKImageInfo DecodeBounds (SKStream stream)
|
|
{
|
|
if (stream == null) {
|
|
throw new ArgumentNullException (nameof (stream));
|
|
}
|
|
using (var codec = SKCodec.Create (stream)) {
|
|
return codec.Info;
|
|
}
|
|
}
|
|
|
|
public static SKImageInfo DecodeBounds (SKData data)
|
|
{
|
|
if (data == null) {
|
|
throw new ArgumentNullException (nameof (data));
|
|
}
|
|
using (var codec = SKCodec.Create (data)) {
|
|
return codec.Info;
|
|
}
|
|
}
|
|
|
|
public static SKImageInfo DecodeBounds (string filename)
|
|
{
|
|
if (filename == null) {
|
|
throw new ArgumentNullException (nameof (filename));
|
|
}
|
|
using (var stream = OpenStream (filename)) {
|
|
return DecodeBounds (stream);
|
|
}
|
|
}
|
|
|
|
public static SKImageInfo DecodeBounds (byte[] buffer)
|
|
{
|
|
if (buffer == null) {
|
|
throw new ArgumentNullException (nameof (buffer));
|
|
}
|
|
using (var stream = new SKMemoryStream (buffer)) {
|
|
return DecodeBounds (stream);
|
|
}
|
|
}
|
|
|
|
public static SKBitmap Decode (SKCodec codec, SKImageInfo bitmapInfo)
|
|
{
|
|
if (codec == null) {
|
|
throw new ArgumentNullException (nameof (codec));
|
|
}
|
|
|
|
// construct a color table for the decode if necessary
|
|
SKColorTable colorTable = null;
|
|
int colorCount = 0;
|
|
if (bitmapInfo.ColorType == SKColorType.Index8)
|
|
{
|
|
colorTable = new SKColorTable ();
|
|
}
|
|
|
|
// read the pixels and color table
|
|
var bitmap = new SKBitmap (bitmapInfo, colorTable);
|
|
IntPtr length;
|
|
var result = codec.GetPixels (bitmapInfo, bitmap.GetPixels (out length), colorTable, ref colorCount);
|
|
if (result != SKCodecResult.Success && result != SKCodecResult.IncompleteInput) {
|
|
bitmap.Dispose ();
|
|
bitmap = null;
|
|
}
|
|
return bitmap;
|
|
}
|
|
|
|
public static SKBitmap Decode (SKCodec codec)
|
|
{
|
|
if (codec == null) {
|
|
throw new ArgumentNullException (nameof (codec));
|
|
}
|
|
var info = codec.Info;
|
|
if (info.AlphaType == SKAlphaType.Unpremul) {
|
|
info.AlphaType = SKAlphaType.Premul;
|
|
}
|
|
// for backwards compatibility, remove the colorspace
|
|
info.ColorSpace = null;
|
|
return Decode (codec, info);
|
|
}
|
|
|
|
public static SKBitmap Decode (Stream stream)
|
|
{
|
|
if (stream == null) {
|
|
throw new ArgumentNullException (nameof (stream));
|
|
}
|
|
return Decode (WrapManagedStream (stream));
|
|
}
|
|
|
|
public static SKBitmap Decode (Stream stream, SKImageInfo bitmapInfo)
|
|
{
|
|
if (stream == null) {
|
|
throw new ArgumentNullException (nameof (stream));
|
|
}
|
|
return Decode (WrapManagedStream (stream), bitmapInfo);
|
|
}
|
|
|
|
public static SKBitmap Decode (SKStream stream)
|
|
{
|
|
if (stream == null) {
|
|
throw new ArgumentNullException (nameof (stream));
|
|
}
|
|
using (var codec = SKCodec.Create (stream)) {
|
|
if (codec == null) {
|
|
return null;
|
|
}
|
|
return Decode (codec);
|
|
}
|
|
}
|
|
|
|
public static SKBitmap Decode (SKStream stream, SKImageInfo bitmapInfo)
|
|
{
|
|
if (stream == null) {
|
|
throw new ArgumentNullException (nameof (stream));
|
|
}
|
|
using (var codec = SKCodec.Create (stream)) {
|
|
if (codec == null) {
|
|
return null;
|
|
}
|
|
return Decode (codec, bitmapInfo);
|
|
}
|
|
}
|
|
|
|
public static SKBitmap Decode (SKData data)
|
|
{
|
|
if (data == null) {
|
|
throw new ArgumentNullException (nameof (data));
|
|
}
|
|
using (var codec = SKCodec.Create (data)) {
|
|
if (codec == null) {
|
|
return null;
|
|
}
|
|
return Decode (codec);
|
|
}
|
|
}
|
|
|
|
public static SKBitmap Decode (SKData data, SKImageInfo bitmapInfo)
|
|
{
|
|
if (data == null) {
|
|
throw new ArgumentNullException (nameof (data));
|
|
}
|
|
using (var codec = SKCodec.Create (data)) {
|
|
if (codec == null) {
|
|
return null;
|
|
}
|
|
return Decode (codec, bitmapInfo);
|
|
}
|
|
}
|
|
|
|
public static SKBitmap Decode (string filename)
|
|
{
|
|
if (filename == null) {
|
|
throw new ArgumentNullException (nameof (filename));
|
|
}
|
|
using (var stream = OpenStream (filename)) {
|
|
return Decode (stream);
|
|
}
|
|
}
|
|
|
|
public static SKBitmap Decode (string filename, SKImageInfo bitmapInfo)
|
|
{
|
|
if (filename == null) {
|
|
throw new ArgumentNullException (nameof (filename));
|
|
}
|
|
using (var stream = OpenStream (filename)) {
|
|
return Decode (stream, bitmapInfo);
|
|
}
|
|
}
|
|
|
|
public static SKBitmap Decode (byte[] buffer)
|
|
{
|
|
if (buffer == null) {
|
|
throw new ArgumentNullException (nameof (buffer));
|
|
}
|
|
using (var stream = new SKMemoryStream (buffer)) {
|
|
return Decode(stream);
|
|
}
|
|
}
|
|
|
|
public static SKBitmap Decode (byte[] buffer, SKImageInfo bitmapInfo)
|
|
{
|
|
if (buffer == null) {
|
|
throw new ArgumentNullException (nameof (buffer));
|
|
}
|
|
using (var stream = new SKMemoryStream (buffer)) {
|
|
return Decode (stream, bitmapInfo);
|
|
}
|
|
}
|
|
|
|
public bool InstallPixels (SKImageInfo info, IntPtr pixels)
|
|
{
|
|
return InstallPixels (info, pixels, info.RowBytes);
|
|
}
|
|
|
|
public bool InstallPixels (SKImageInfo info, IntPtr pixels, int rowBytes)
|
|
{
|
|
return InstallPixels (info, pixels, rowBytes, null);
|
|
}
|
|
|
|
public bool InstallPixels (SKImageInfo info, IntPtr pixels, int rowBytes, SKColorTable ctable)
|
|
{
|
|
return InstallPixels (info, pixels, rowBytes, ctable, null, null);
|
|
}
|
|
|
|
public bool InstallPixels (SKImageInfo info, IntPtr pixels, int rowBytes, SKColorTable ctable, SKBitmapReleaseDelegate releaseProc, object context)
|
|
{
|
|
var cinfo = SKImageInfoNative.FromManaged (ref info);
|
|
IntPtr ct = ctable == null ? IntPtr.Zero : ctable.Handle;
|
|
if (releaseProc == null) {
|
|
return SkiaApi.sk_bitmap_install_pixels (Handle, ref cinfo, pixels, (IntPtr)rowBytes, ct, IntPtr.Zero, IntPtr.Zero);
|
|
} else {
|
|
var ctx = new NativeDelegateContext (context, releaseProc);
|
|
return SkiaApi.sk_bitmap_install_pixels (Handle, ref cinfo, pixels, (IntPtr)rowBytes, ct, releaseDelegate, ctx.NativeContext);
|
|
}
|
|
}
|
|
|
|
public bool InstallPixels (SKPixmap pixmap)
|
|
{
|
|
return SkiaApi.sk_bitmap_install_pixels_with_pixmap (Handle, pixmap.Handle);
|
|
}
|
|
|
|
public bool InstallMaskPixels(SKMask mask)
|
|
{
|
|
return SkiaApi.sk_bitmap_install_mask_pixels(Handle, ref mask);
|
|
}
|
|
|
|
public void NotifyPixelsChanged()
|
|
{
|
|
SkiaApi.sk_bitmap_notify_pixels_changed(Handle);
|
|
}
|
|
|
|
public SKPixmap PeekPixels ()
|
|
{
|
|
SKPixmap pixmap = new SKPixmap ();
|
|
var result = PeekPixels (pixmap);
|
|
if (result) {
|
|
return pixmap;
|
|
} else {
|
|
pixmap.Dispose ();
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public bool PeekPixels (SKPixmap pixmap)
|
|
{
|
|
if (pixmap == null) {
|
|
throw new ArgumentNullException (nameof (pixmap));
|
|
}
|
|
return SkiaApi.sk_bitmap_peek_pixels (Handle, pixmap.Handle);
|
|
}
|
|
|
|
public SKBitmap Resize (SKImageInfo info, SKBitmapResizeMethod method)
|
|
{
|
|
var dst = new SKBitmap (info);
|
|
var result = Resize (dst, this, method);
|
|
if (result) {
|
|
return dst;
|
|
} else {
|
|
dst.Dispose ();
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public bool Resize (SKBitmap dst, SKBitmapResizeMethod method)
|
|
{
|
|
return Resize (dst, this, method);
|
|
}
|
|
|
|
public static bool Resize (SKBitmap dst, SKBitmap src, SKBitmapResizeMethod method)
|
|
{
|
|
using (var srcPix = src.PeekPixels ())
|
|
using (var dstPix = dst.PeekPixels ()) {
|
|
return SKPixmap.Resize (dstPix, srcPix, method);// && dst.InstallPixels (dstPix);
|
|
}
|
|
}
|
|
|
|
public static SKBitmap FromImage (SKImage image)
|
|
{
|
|
if (image == null) {
|
|
throw new ArgumentNullException (nameof (image));
|
|
}
|
|
|
|
var info = new SKImageInfo (image.Width, image.Height, SKImageInfo.PlatformColorType, image.AlphaType);
|
|
var bmp = new SKBitmap (info);
|
|
if (!image.ReadPixels (info, bmp.GetPixels (), info.RowBytes, 0, 0))
|
|
{
|
|
bmp.Dispose ();
|
|
bmp = null;
|
|
}
|
|
return bmp;
|
|
}
|
|
|
|
public bool Encode (SKWStream dst, SKEncodedImageFormat format, int quality)
|
|
{
|
|
using (var pixmap = new SKPixmap ()) {
|
|
return PeekPixels (pixmap) && pixmap.Encode (dst, format, quality);
|
|
}
|
|
}
|
|
|
|
private static SKStream WrapManagedStream (Stream stream)
|
|
{
|
|
if (stream == null) {
|
|
throw new ArgumentNullException (nameof (stream));
|
|
}
|
|
|
|
// we will need a seekable stream, so buffer it if need be
|
|
if (stream.CanSeek) {
|
|
return new SKManagedStream (stream, true);
|
|
} else {
|
|
return new SKFrontBufferedManagedStream (stream, SKCodec.MinBufferedBytesNeeded, true);
|
|
}
|
|
}
|
|
|
|
private static SKStream OpenStream (string path)
|
|
{
|
|
if (!SKFileStream.IsPathSupported (path)) {
|
|
// due to a bug (https://github.com/mono/SkiaSharp/issues/390)
|
|
return WrapManagedStream (File.OpenRead (path));
|
|
} else {
|
|
return new SKFileStream (path);
|
|
}
|
|
}
|
|
|
|
// internal proxy
|
|
|
|
[MonoPInvokeCallback (typeof (SKBitmapReleaseDelegateInternal))]
|
|
private static void SKBitmapReleaseInternal (IntPtr address, IntPtr context)
|
|
{
|
|
using (var ctx = NativeDelegateContext.Unwrap (context)) {
|
|
ctx.GetDelegate<SKBitmapReleaseDelegate> () (address, ctx.ManagedContext);
|
|
}
|
|
}
|
|
}
|
|
}
|