mirror of
https://github.com/ElectronNET/Electron.NET.git
synced 2026-07-13 12:18:08 +00:00
Add full NativeImage support for Electron.NET
This commit is contained in:
@@ -4,6 +4,7 @@ using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
using Console = System.Console;
|
||||
|
||||
namespace ElectronNET.API
|
||||
{
|
||||
@@ -251,10 +252,7 @@ namespace ElectronNET.API
|
||||
{
|
||||
BridgeConnector.Socket.Off("clipboard-readImage-Completed");
|
||||
|
||||
var b64 = image.ToString();
|
||||
var bytes = Convert.FromBase64String(b64);
|
||||
|
||||
var nativeImage = NativeImage.CreateFromBuffer(bytes);
|
||||
var nativeImage = ((JObject)image).ToObject<NativeImage>();
|
||||
|
||||
taskCompletionSource.SetResult(nativeImage);
|
||||
|
||||
@@ -272,7 +270,7 @@ namespace ElectronNET.API
|
||||
/// <param name="type"></param>
|
||||
public void WriteImage(NativeImage image, string type = "")
|
||||
{
|
||||
BridgeConnector.Socket.Emit("clipboard-writeImage", JsonConvert.SerializeObject(image.GetBytes()), type);
|
||||
BridgeConnector.Socket.Emit("clipboard-writeImage", JsonConvert.SerializeObject(image), type);
|
||||
}
|
||||
|
||||
private JsonSerializer _jsonSerializer = new JsonSerializer()
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace ElectronNET.API.Entities
|
||||
{
|
||||
public int? Width { get; set; }
|
||||
public int? Height { get; set; }
|
||||
public int ScaleFactor { get; set; }
|
||||
public float ScaleFactor { get; set; } = 1.0f;
|
||||
public byte[] Buffer { get; set; }
|
||||
public string DataUrl { get; set; }
|
||||
}
|
||||
|
||||
@@ -6,6 +6,6 @@ namespace ElectronNET.API.Entities
|
||||
{
|
||||
public class BitmapOptions
|
||||
{
|
||||
public double ScaleFactor { get; set; } = 1.0d;
|
||||
public float ScaleFactor { get; set; } = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
13
ElectronNET.API/Entities/CreateFromBitmapOptions.cs
Normal file
13
ElectronNET.API/Entities/CreateFromBitmapOptions.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace ElectronNET.API.Entities
|
||||
{
|
||||
public class CreateFromBitmapOptions
|
||||
{
|
||||
public int? Width { get; set; }
|
||||
public int? Height { get; set; }
|
||||
public float ScaleFactor { get; set; } = 1.0f;
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,8 @@ namespace ElectronNET.API.Entities
|
||||
{
|
||||
public class CreateFromBufferOptions
|
||||
{
|
||||
public int Width { get; set; }
|
||||
public int Height { get; set; }
|
||||
public int ScaleFactor { get; set; }
|
||||
public int? Width { get; set; }
|
||||
public int? Height { get; set; }
|
||||
public float ScaleFactor { get; set; } = 1.0f;
|
||||
}
|
||||
}
|
||||
@@ -1,139 +1,265 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ElectronNET.API.Entities
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// Native Image handler for Electron.NET
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(NativeImageJsonConverter))]
|
||||
public class NativeImage
|
||||
{
|
||||
private Image _image;
|
||||
private bool _isTemplateImage = false;
|
||||
private readonly Dictionary<float, Image> _images = new Dictionary<float, Image>();
|
||||
private bool _isTemplateImage;
|
||||
|
||||
private static readonly Dictionary<string, float> ScaleFactorPairs = new Dictionary<string, float>
|
||||
{
|
||||
{"@2x", 2.0f}, {"@3x", 3.0f}, {"@1x", 1.0f}, {"@4x", 4.0f},
|
||||
{"@5x", 5.0f}, {"@1.25x", 1.25f}, {"@1.33x", 1.33f}, {"@1.4x", 1.4f},
|
||||
{"@1.5x", 1.5f}, {"@1.8x", 1.8f}, {"@2.5x", 2.5f}
|
||||
};
|
||||
|
||||
private static float? ExtractDpiFromFilePath(string filePath)
|
||||
{
|
||||
var withoutExtension = Path.GetFileNameWithoutExtension(filePath);
|
||||
return ScaleFactorPairs
|
||||
.Where(p => withoutExtension.EndsWith(p.Key))
|
||||
.Select(p => p.Value)
|
||||
.FirstOrDefault();
|
||||
}
|
||||
private static Image BytesToImage(byte[] bytes)
|
||||
{
|
||||
var ms = new MemoryStream(bytes);
|
||||
return Image.FromStream(ms);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an empty NativeImage
|
||||
/// </summary>
|
||||
public static NativeImage CreateEmpty()
|
||||
{
|
||||
return new NativeImage();
|
||||
}
|
||||
|
||||
public static NativeImage CreateFromBuffer(byte[] buffer)
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public static NativeImage CreateFromBitmap(Bitmap bitmap, CreateFromBitmapOptions options = null)
|
||||
{
|
||||
return new NativeImage(buffer);
|
||||
if (options is null)
|
||||
{
|
||||
options = new CreateFromBitmapOptions();
|
||||
}
|
||||
|
||||
return new NativeImage(bitmap, options.ScaleFactor);
|
||||
}
|
||||
|
||||
public static NativeImage CreateFromBuffer(byte[] buffer, CreateFromBufferOptions options)
|
||||
/// <summary>
|
||||
/// Creates a NativeImage from a byte array.
|
||||
/// </summary>
|
||||
public static NativeImage CreateFromBuffer(byte[] buffer, CreateFromBufferOptions options = null)
|
||||
{
|
||||
return new NativeImage(buffer);
|
||||
if (options is null)
|
||||
{
|
||||
options = new CreateFromBufferOptions();
|
||||
}
|
||||
|
||||
var ms = new MemoryStream(buffer);
|
||||
var image = Image.FromStream(ms);
|
||||
|
||||
return new NativeImage(image, options.ScaleFactor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a NativeImage from a base64 encoded data URL.
|
||||
/// </summary>
|
||||
/// <param name="dataUrl">A data URL with a base64 encoded image.</param>
|
||||
public static NativeImage CreateFromDataURL(string dataUrl)
|
||||
{
|
||||
var images = new Dictionary<float,Image>();
|
||||
var parsedDataUrl = Regex.Match(dataUrl, @"data:image/(?<type>.+?),(?<data>.+)");
|
||||
var actualData = parsedDataUrl.Groups["data"].Value;
|
||||
var type = parsedDataUrl.Groups["type"].Value;
|
||||
var binData = Convert.FromBase64String(actualData);
|
||||
|
||||
var imageFormat = type switch
|
||||
{
|
||||
"jpeg" => ImageFormat.Jpeg,
|
||||
"jpg" => ImageFormat.Jpeg,
|
||||
"png" => ImageFormat.Png,
|
||||
"gif" => ImageFormat.Gif,
|
||||
"x-icon" => ImageFormat.Icon,
|
||||
"bmp" => ImageFormat.Bmp,
|
||||
_ => ImageFormat.Png
|
||||
};
|
||||
var image = BytesToImage(binData);
|
||||
|
||||
return new NativeImage(binData, imageFormat);
|
||||
images.Add(1.0f, image);
|
||||
|
||||
return new NativeImage(images);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a NativeImage from an image on the disk.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the image</param>
|
||||
public static NativeImage CreateFromPath(string path)
|
||||
{
|
||||
return new NativeImage(path);
|
||||
var images = new Dictionary<float,Image>();
|
||||
if (Regex.IsMatch(path, "(@.+?x)"))
|
||||
{
|
||||
var dpi = ExtractDpiFromFilePath(path);
|
||||
if (dpi == null)
|
||||
{
|
||||
throw new Exception($"Invalid scaling factor for '{path}'.");
|
||||
}
|
||||
|
||||
images[dpi.Value] = Image.FromFile(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path);
|
||||
var extension = Path.GetExtension(path);
|
||||
// Load as 1x dpi
|
||||
images[1.0f] = Image.FromFile(path);
|
||||
|
||||
foreach (var scale in ScaleFactorPairs)
|
||||
{
|
||||
var fileName = $"{fileNameWithoutExtension}{scale}.{extension}";
|
||||
if (File.Exists(fileName))
|
||||
{
|
||||
var dpi = ExtractDpiFromFilePath(fileName);
|
||||
if (dpi != null)
|
||||
{
|
||||
images[dpi.Value] = Image.FromFile(fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new NativeImage(images);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an empty NativeImage
|
||||
/// </summary>
|
||||
public NativeImage()
|
||||
{
|
||||
}
|
||||
|
||||
public NativeImage(Bitmap bitmap)
|
||||
/// <summary>
|
||||
/// Creates a NativeImage from a bitmap and scale factor
|
||||
/// </summary>
|
||||
public NativeImage(Image bitmap, float scaleFactor = 1.0f)
|
||||
{
|
||||
_image = bitmap;
|
||||
}
|
||||
public NativeImage(string path)
|
||||
{
|
||||
_image = Image.FromFile(path);
|
||||
_images.Add(scaleFactor, bitmap);
|
||||
}
|
||||
|
||||
|
||||
public NativeImage(byte[] buffer)
|
||||
/// <summary>
|
||||
/// Creates a NativeImage from a dictionary of scales and images.
|
||||
/// </summary>
|
||||
public NativeImage(Dictionary<float, Image> imageDictionary)
|
||||
{
|
||||
var ms = new MemoryStream(buffer);
|
||||
_image = Image.FromStream(ms);
|
||||
}
|
||||
|
||||
public NativeImage(byte[] buffer, ImageFormat imageFormat)
|
||||
{
|
||||
_image = BytesToImage(buffer, imageFormat);
|
||||
}
|
||||
|
||||
public Image GetBitmap()
|
||||
{
|
||||
return _image;
|
||||
_images = imageDictionary;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Crops the image specified by the input rectangle and computes scale factor
|
||||
/// </summary>
|
||||
public NativeImage Crop(Rectangle rect)
|
||||
{
|
||||
using (var g = Graphics.FromImage(_image))
|
||||
var images = new Dictionary<float,Image>();
|
||||
foreach (var image in _images)
|
||||
{
|
||||
var bmp = new Bitmap(rect.Width, rect.Height);
|
||||
g.DrawImage(bmp, new System.Drawing.Rectangle(0, 0, _image.Width, _image.Height), new System.Drawing.Rectangle(rect.X, rect.Y, rect.Width, rect.Height), GraphicsUnit.Pixel);
|
||||
|
||||
return new NativeImage(bmp);
|
||||
images.Add(image.Key, Crop(rect.X, rect.Y, rect.Width, rect.Height, image.Key));
|
||||
}
|
||||
|
||||
return new NativeImage(images);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resizes the image and computes scale factor
|
||||
/// </summary>
|
||||
public NativeImage Resize(ResizeOptions options)
|
||||
{
|
||||
var images = new Dictionary<float, Image>();
|
||||
foreach (var image in _images)
|
||||
{
|
||||
images.Add(image.Key, Resize(options.Width, options.Height, image.Key));
|
||||
}
|
||||
|
||||
return new NativeImage(images);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add an image representation for a specific scale factor.
|
||||
/// </summary>
|
||||
/// <param name="options"></param>
|
||||
public void AddRepresentation(AddRepresentationOptions options)
|
||||
{
|
||||
if (options.Buffer.Length > 0)
|
||||
{
|
||||
_image = CreateFromBuffer(options.Buffer).GetBitmap();
|
||||
_images[options.ScaleFactor] =
|
||||
CreateFromBuffer(options.Buffer, new CreateFromBufferOptions {ScaleFactor = options.ScaleFactor})
|
||||
.GetScale(options.ScaleFactor);
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(options.DataUrl))
|
||||
{
|
||||
_image = CreateFromDataURL(options.DataUrl).GetBitmap();
|
||||
_images[options.ScaleFactor] = CreateFromDataURL(options.DataUrl).GetScale(options.ScaleFactor);
|
||||
}
|
||||
}
|
||||
|
||||
public double GetAspectRatio => _image.Width / _image.Height;
|
||||
/// <summary>
|
||||
/// Gets the aspect ratio for the image based on scale factor
|
||||
/// </summary>
|
||||
/// <param name="scaleFactor">Optional</param>
|
||||
public float GetAspectRatio(float scaleFactor = 1.0f)
|
||||
{
|
||||
var image = GetScale(scaleFactor);
|
||||
if (image != null)
|
||||
{
|
||||
return image.Width / image.Height;
|
||||
}
|
||||
|
||||
return 0f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a byte array that contains the image's raw bitmap pixel data.
|
||||
/// </summary>
|
||||
public byte[] GetBitmap(BitmapOptions options)
|
||||
{
|
||||
return ImageToBytes(_image.RawFormat);
|
||||
return ToBitmap(new ToBitmapOptions{ ScaleFactor = options.ScaleFactor });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a byte array that contains the image's raw bitmap pixel data.
|
||||
/// </summary>
|
||||
public byte[] GetNativeHandle()
|
||||
{
|
||||
return ImageToBytes(_image.RawFormat);
|
||||
return ToBitmap(new ToBitmapOptions());
|
||||
}
|
||||
|
||||
public Size GetSize()
|
||||
/// <summary>
|
||||
/// Gets the size of the specified image based on scale factor
|
||||
/// </summary>
|
||||
public Size GetSize(float scaleFactor = 1.0f)
|
||||
{
|
||||
return new Size
|
||||
if (_images.ContainsKey(scaleFactor))
|
||||
{
|
||||
Width = _image.Width,
|
||||
Height = _image.Height
|
||||
};
|
||||
var image = _images[scaleFactor];
|
||||
return new Size
|
||||
{
|
||||
Width = image.Width,
|
||||
Height = image.Height
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks to see if the NativeImage instance is empty.
|
||||
/// </summary>
|
||||
public bool IsEmpty()
|
||||
{
|
||||
return _image == null;
|
||||
return _images.Count <= 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -150,78 +276,102 @@ namespace ElectronNET.API.Entities
|
||||
_isTemplateImage = option;
|
||||
}
|
||||
|
||||
|
||||
public NativeImage Resize(ResizeOptions options)
|
||||
{
|
||||
return new NativeImage((Bitmap)Resize(options.Width, options.Height));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Outputs a bitmap based on the scale factor
|
||||
/// </summary>
|
||||
public byte[] ToBitmap(ToBitmapOptions options)
|
||||
{
|
||||
return ImageToBytes(_image.RawFormat);
|
||||
return ImageToBytes(ImageFormat.Bmp, options.ScaleFactor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Outputs a data URL based on the scale factor
|
||||
/// </summary>
|
||||
public string ToDataURL(ToDataUrlOptions options)
|
||||
{
|
||||
var mimeType = ImageCodecInfo.GetImageEncoders().FirstOrDefault(x => x.FormatID == _image.RawFormat.Guid)?.MimeType;
|
||||
if (!_images.ContainsKey(options.ScaleFactor))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var image = _images[options.ScaleFactor];
|
||||
var mimeType = ImageCodecInfo.GetImageEncoders().FirstOrDefault(x => x.FormatID == image.RawFormat.Guid)?.MimeType;
|
||||
if (mimeType is null)
|
||||
{
|
||||
mimeType = "image/png";
|
||||
}
|
||||
|
||||
var bytes = ImageToBytes(_image.RawFormat);
|
||||
var bytes = ImageToBytes(image.RawFormat, options.ScaleFactor);
|
||||
var base64 = Convert.ToBase64String(bytes);
|
||||
|
||||
return $"data:{mimeType};base64,{base64}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Outputs a JPEG for the default scale factor
|
||||
/// </summary>
|
||||
public byte[] ToJPEG(int quality)
|
||||
{
|
||||
return ImageToBytes(ImageFormat.Jpeg, 1.0d, quality);
|
||||
return ImageToBytes(ImageFormat.Jpeg, 1.0f, quality);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Outputs a PNG for the specified scale factor
|
||||
/// </summary>
|
||||
public byte[] ToPNG(ToPNGOptions options)
|
||||
{
|
||||
return ImageToBytes(ImageFormat.Png, options.ScaleFactor);
|
||||
}
|
||||
|
||||
private byte[] ImageToBytes(ImageFormat imageFormat, double scaleFactor = 1.0d, int quality = 100)
|
||||
|
||||
private byte[] ImageToBytes(ImageFormat imageFormat = null, float scaleFactor = 1.0f, int quality = 100)
|
||||
{
|
||||
using (var ms = new MemoryStream())
|
||||
using var ms = new MemoryStream();
|
||||
|
||||
if (_images.ContainsKey(scaleFactor))
|
||||
{
|
||||
Image img = _image;
|
||||
var image = _images[scaleFactor];
|
||||
var encoderCodecInfo = GetEncoder(imageFormat ?? image.RawFormat);
|
||||
var encoder = Encoder.Quality;
|
||||
|
||||
if (Math.Abs(scaleFactor - 1.0d) > 0.0)
|
||||
var encoderParameters = new EncoderParameters(1)
|
||||
{
|
||||
img = Resize(_image.Width, _image.Height, scaleFactor);
|
||||
}
|
||||
|
||||
ImageCodecInfo encoderCodecInfo = GetEncoder(imageFormat);
|
||||
Encoder encoder = Encoder.Quality;
|
||||
|
||||
EncoderParameters encoderParameters = new EncoderParameters(1)
|
||||
{
|
||||
Param = new []
|
||||
Param = new[]
|
||||
{
|
||||
new EncoderParameter(encoder, quality)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
img.Save(ms, encoderCodecInfo, encoderParameters);
|
||||
|
||||
image.Save(ms, encoderCodecInfo, encoderParameters);
|
||||
|
||||
return ms.ToArray();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Image Resize(int? width, int? height, double scaleFactor = 1.0d)
|
||||
private Image Resize(int? width, int? height, float scaleFactor = 1.0f)
|
||||
{
|
||||
using (var g = Graphics.FromImage(_image))
|
||||
if (!_images.ContainsKey(scaleFactor) || (width is null && height is null))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var image = _images[scaleFactor];
|
||||
using (var g = Graphics.FromImage(image))
|
||||
{
|
||||
g.CompositingQuality = CompositingQuality.HighQuality;
|
||||
|
||||
var bmp = new Bitmap((int)Math.Round(width ?? _image.Width * scaleFactor), (int)Math.Round(height ?? _image.Height * scaleFactor));
|
||||
var aspect = GetAspectRatio(scaleFactor);
|
||||
|
||||
width ??= Convert.ToInt32(image.Width * aspect);
|
||||
height ??= Convert.ToInt32(image.Height * aspect);
|
||||
|
||||
width = Convert.ToInt32(width * scaleFactor);
|
||||
height = Convert.ToInt32(height * scaleFactor);
|
||||
|
||||
var bmp = new Bitmap(width.Value, height.Value);
|
||||
g.DrawImage(bmp,
|
||||
new System.Drawing.Rectangle(0, 0, _image.Width, _image.Height),
|
||||
new System.Drawing.Rectangle(0, 0, image.Width, image.Height),
|
||||
new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height),
|
||||
GraphicsUnit.Pixel);
|
||||
|
||||
@@ -229,15 +379,40 @@ namespace ElectronNET.API.Entities
|
||||
}
|
||||
}
|
||||
|
||||
private Image BytesToImage(byte[] bytes, ImageFormat imageFormat)
|
||||
private Image Crop(int? x, int? y, int? width, int? height, float scaleFactor = 1.0f)
|
||||
{
|
||||
var ms = new MemoryStream(bytes);
|
||||
return Image.FromStream(ms);
|
||||
if (!_images.ContainsKey(scaleFactor))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var image = _images[scaleFactor];
|
||||
using (var g = Graphics.FromImage(image))
|
||||
{
|
||||
g.CompositingQuality = CompositingQuality.HighQuality;
|
||||
|
||||
x ??= 0;
|
||||
y ??= 0;
|
||||
|
||||
x = Convert.ToInt32(x * scaleFactor);
|
||||
y = Convert.ToInt32(y * scaleFactor);
|
||||
|
||||
width ??= image.Width;
|
||||
height ??= image.Height;
|
||||
|
||||
width = Convert.ToInt32(width * scaleFactor);
|
||||
height = Convert.ToInt32(height * scaleFactor);
|
||||
|
||||
var bmp = new Bitmap(width.Value, height.Value);
|
||||
g.DrawImage(bmp, new System.Drawing.Rectangle(0, 0, image.Width, image.Height), new System.Drawing.Rectangle(x.Value, y.Value, width.Value, height.Value), GraphicsUnit.Pixel);
|
||||
|
||||
return bmp;
|
||||
}
|
||||
}
|
||||
|
||||
private ImageCodecInfo GetEncoder(ImageFormat format)
|
||||
{
|
||||
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
|
||||
var codecs = ImageCodecInfo.GetImageDecoders();
|
||||
foreach (ImageCodecInfo codec in codecs)
|
||||
{
|
||||
if (codec.FormatID == format.Guid)
|
||||
@@ -248,9 +423,32 @@ namespace ElectronNET.API.Entities
|
||||
return null;
|
||||
}
|
||||
|
||||
internal byte[] GetBytes()
|
||||
internal Dictionary<float,string> GetAllScaledImages()
|
||||
{
|
||||
return ImageToBytes(_image.RawFormat);
|
||||
var dict = new Dictionary<float,string>();
|
||||
try
|
||||
{
|
||||
foreach (var (scale, image) in _images)
|
||||
{
|
||||
dict.Add(scale, Convert.ToBase64String(ImageToBytes(null, scale)));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
internal Image GetScale(float scaleFactor)
|
||||
{
|
||||
if (_images.ContainsKey(scaleFactor))
|
||||
{
|
||||
return _images[scaleFactor];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
34
ElectronNET.API/Entities/NativeImageJsonConverter.cs
Normal file
34
ElectronNET.API/Entities/NativeImageJsonConverter.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ElectronNET.API.Entities
|
||||
{
|
||||
internal class NativeImageJsonConverter : JsonConverter
|
||||
{
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
if (value is NativeImage nativeImage)
|
||||
{
|
||||
var scaledImages = nativeImage.GetAllScaledImages();
|
||||
serializer.Serialize(writer, scaledImages);
|
||||
}
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
var dict = serializer.Deserialize<Dictionary<float, string>>(reader);
|
||||
var newDictionary = new Dictionary<float, Image>();
|
||||
foreach (var item in dict)
|
||||
{
|
||||
var bytes = Convert.FromBase64String(item.Value);
|
||||
newDictionary.Add(item.Key, Image.FromStream(new MemoryStream(bytes)));
|
||||
}
|
||||
return new NativeImage(newDictionary);
|
||||
}
|
||||
|
||||
public override bool CanConvert(Type objectType) => objectType == typeof(NativeImage);
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,6 @@ namespace ElectronNET.API.Entities
|
||||
{
|
||||
public class ToBitmapOptions
|
||||
{
|
||||
public double ScaleFactor { get; set; } = 1.0d;
|
||||
public float ScaleFactor { get; set; } = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,6 @@ namespace ElectronNET.API.Entities
|
||||
{
|
||||
public class ToDataUrlOptions
|
||||
{
|
||||
public double ScaleFactor { get; set; } = 1.0d;
|
||||
public float ScaleFactor { get; set; } = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,6 @@ namespace ElectronNET.API.Entities
|
||||
{
|
||||
public class ToPNGOptions
|
||||
{
|
||||
public double ScaleFactor { get; set; } = 1.0d;
|
||||
public float ScaleFactor { get; set; } = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,11 +50,18 @@ module.exports = (socket) => {
|
||||
});
|
||||
socket.on('clipboard-readImage', (type) => {
|
||||
const image = electron_1.clipboard.readImage(type);
|
||||
electronSocket.emit('clipboard-readImage-Completed', image.getNativeHandle().buffer);
|
||||
electronSocket.emit('clipboard-readImage-Completed', { 1: image.toPNG().toString('base64') });
|
||||
});
|
||||
socket.on('clipboard-writeImage', (data, type) => {
|
||||
const buff = Buffer.from(JSON.parse(data), 'base64');
|
||||
const ni = electron_1.nativeImage.createFromBuffer(buff);
|
||||
var data = JSON.parse(data);
|
||||
const ni = electron_1.nativeImage.createEmpty();
|
||||
for (var i in data) {
|
||||
var scaleFactor = i;
|
||||
var bytes = data[i];
|
||||
var buff = Buffer.from(bytes, 'base64');
|
||||
ni.addRepresentation({ scaleFactor: scaleFactor, buffer: buff });
|
||||
}
|
||||
|
||||
electron_1.clipboard.writeImage(ni, type);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -63,13 +63,19 @@ export = (socket: SocketIO.Socket) => {
|
||||
|
||||
socket.on('clipboard-readImage', (type) => {
|
||||
var image = clipboard.readImage(type);
|
||||
var b64 = image.getNativeHandle().buffer.toString('base64');
|
||||
electronSocket.emit('clipboard-readImage-Completed', b64);
|
||||
electronSocket.emit('clipboard-readImage-Completed', { 1: image.toPNG().toString('base64') });
|
||||
});
|
||||
|
||||
socket.on('clipboard-writeImage', (data, type) => {
|
||||
var buff = Buffer.from(JSON.parse(data), 'base64');
|
||||
const ni = nativeImage.createFromBuffer(buff);
|
||||
var data = JSON.parse(data);
|
||||
const ni = nativeImage.createEmpty();
|
||||
for (var i in data) {
|
||||
var scaleFactor = i;
|
||||
var bytes = data[i];
|
||||
var buff = Buffer.from(bytes, 'base64');
|
||||
ni.addRepresentation({ scaleFactor: +scaleFactor, buffer: buff });
|
||||
}
|
||||
|
||||
clipboard.writeImage(ni, type);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using ElectronNET.API;
|
||||
using System.Linq;
|
||||
using ElectronNET.API.Entities;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ElectronNET.WebApp.Controllers
|
||||
{
|
||||
@@ -33,6 +34,13 @@ namespace ElectronNET.WebApp.Controllers
|
||||
var nativeImage = NativeImage.CreateFromDataURL(test.ToString());
|
||||
Electron.Clipboard.WriteImage(nativeImage);
|
||||
});
|
||||
|
||||
Electron.IpcMain.On("paste-image-to", async test =>
|
||||
{
|
||||
var nativeImage = await Electron.Clipboard.ReadImageAsync();
|
||||
var mainWindow = Electron.WindowManager.BrowserWindows.First();
|
||||
Electron.IpcMain.Send(mainWindow, "paste-image-from", JsonConvert.SerializeObject(nativeImage));
|
||||
});
|
||||
}
|
||||
|
||||
return View();
|
||||
|
||||
@@ -101,7 +101,6 @@ pasteBtn.addEventListener('click', () => {
|
||||
<button class="demo-button" id="copy-image-to">Copy</button>
|
||||
<textarea id="paste-image-div" onpaste="console.log('onpastefromhtml')"></textarea>
|
||||
<div class="demo-image-box" id="image-paste-div"></div>
|
||||
<!--<input class="demo-input" id="copy-to-input" aria-label="Click copy" placeholder="Click copy."></input>-->
|
||||
</div>
|
||||
<p>In this example we copy an image to the Clipboard. After clicking 'Copy' use the text area to paste (CMD + V or CTRL + V) the image from the clipboard.</p>
|
||||
<h5>Main Process (C#)</h5>
|
||||
@@ -129,9 +128,45 @@ copyBtn.addEventListener('click', () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="demo">
|
||||
<div class="demo-wrapper">
|
||||
<button id="copy-to-demo-toggle" class="js-container-target demo-toggle-button">
|
||||
Paste Image
|
||||
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Both</div>
|
||||
</button>
|
||||
<div class="demo-box">
|
||||
<div class="demo-controls">
|
||||
<button class="demo-button" id="paste-image-from">Paste</button>
|
||||
<img class="demo-image-box" id="paste-image-from-div"></img>
|
||||
</div>
|
||||
<p>In this example we paste an image from the Clipboard. After clicking 'Paste', if your clipboard contained an image, the image will show up next to the paste button.</p>
|
||||
<h5>Main Process (C#)</h5>
|
||||
<pre><code class="csharp">Electron.IpcMain.On("paste-image-to", async test =>
|
||||
{
|
||||
var nativeImage = await Electron.Clipboard.ReadImageAsync();
|
||||
var mainWindow = Electron.WindowManager.BrowserWindows.First();
|
||||
Electron.IpcMain.Send(mainWindow, "paste-image-from", JsonConvert.SerializeObject(nativeImage));
|
||||
});</code></pre>
|
||||
<div class="demo-protip">
|
||||
<h2>ProTip</h2>
|
||||
<strong>Electron.js Support in Electron.NET.</strong>
|
||||
<p>The <code>clipboard</code> module is built into Electron.js (therefore you can use this in the renderer processes).</p>
|
||||
<pre><code class="language-js">const { nativeImage, clipboard } = require('electron');
|
||||
|
||||
const pasteBtn = document.getElementById('paste-to-image');
|
||||
|
||||
copyBtn.addEventListener('click', () => {
|
||||
const image = clipboard.readImage();
|
||||
document.getElementById('image-holder').src = image.toDataURL();
|
||||
})</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
const { ipcRenderer } = require("electron");
|
||||
const { ipcRenderer, nativeImage } = require("electron");
|
||||
|
||||
document.getElementById("copy-to").addEventListener("click", () => {
|
||||
document.getElementById('copy-to-input').placeholder = 'Copied! Paste here to see.';
|
||||
@@ -146,11 +181,29 @@ copyBtn.addEventListener('click', () => {
|
||||
ipcRenderer.send("copy-image-to", 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACmklEQVR42oVTX0hTURj/zr2b07utMR0st2m30UxZJRHpWNDjRkUPvgUZQdGD4Esq6yWf9EVx9iL4EAiBVNDDHqxs9RBI+ZcorLVSkemcNdmct827Oe92Ot1b270i9MHHOfc73/f7fvd3zofggGU/OD1k8RJ3E2/6Gw4TnyYerDoXei3PR/82uwtOliydlJbtoA1OhtY7AFVZxDOc3YRiegkELsRjPjKKMYxoz3+NlAAyc00sBuhTm9ztavNFoBizhIqVbYrZOOzHp0DYmhkHhHt1reGIePTrfeOQ2tzaranzAqI1cmJlw1IYF3KwF30F+a0Fv8Ed7kHbU40elc4SYOxXGJoxibmDl++D70W/AkeMvewX94VsEvjV57yQjrWhxNuTfsbW0lVlaynxHbw6AL4Jn9QSkdb4QIzQ4TfmIbsxP4zibxpmjE6vS33EfCjzEn+MFHrkuTikQsFZFJt07BxtvWSgVBXwX8MyQYU8xOcmObQ+cWLH6rpAANQy2RU3LKssX0tR2IfY7DsOrQbsM7VnHK5Kg04S69o8+J62HEpAfpbjMvBjcXkWrTw77jeyxq7qeqOUdH0J2u7pwNFsUbyF5cVNCAxkwPe4QfxORVKQXNsZRt+esJ5KvSpQ26hnKrQ0JBI5GLvLSSCntFLxl12x+NYDA5hqNJDnC/Dze4bPpgtt4o+Gxo8NGS10t9lOA6VCkEhViCByE4ur96C4j2BrVYDtWMHvvLHWIwJ8flTPEqp91dZie40Ng0ZbFIVKxCT+JqsEsscjSEYpUkyRpwy9p2+uR0pSfxqrYxFGnYxB6NDXCIzOKEDlHyCSkctQkEmpIJ1U8TxHj5L0kebb0fIwye3jQ5uH9PUiQIpxxoCnSSx49k5UMc6/ARiuBRgyrEC3AAAAAElFTkSuQmCC');
|
||||
});
|
||||
|
||||
document.getElementById("paste-image-from").addEventListener("click", () => {
|
||||
ipcRenderer.send("paste-image-to");
|
||||
});
|
||||
|
||||
ipcRenderer.on("paste-from", (sender, text) => {
|
||||
const message = `Clipboard contents: ${text}`;
|
||||
document.getElementById("paste-from").innerText = message;
|
||||
});
|
||||
|
||||
ipcRenderer.on("paste-image-from", (sender, data) => {
|
||||
console.log(data);
|
||||
var data = JSON.parse(data);
|
||||
const ni = nativeImage.createEmpty();
|
||||
for (var i in data) {
|
||||
var scaleFactor = i;
|
||||
var bytes = data[i];
|
||||
var buff = Buffer.from(bytes, 'base64');
|
||||
ni.addRepresentation({ scaleFactor: scaleFactor, buffer: buff });
|
||||
}
|
||||
|
||||
document.getElementById("paste-image-from-div").src = ni.toDataURL();
|
||||
});
|
||||
|
||||
|
||||
var PasteImage = function (el) {
|
||||
this._el = el;
|
||||
|
||||
Reference in New Issue
Block a user