Я рад знать, что вы пытались в javascript медленно, потому что я думал о том, чтобы попробовать это.
У меня есть пакет Nuget с именем DataJuggler.PixelDatabase, и я делаю то же самое как вы, но в C# коде, тогда я просто сохраняю изображение под новым именем файла.
https://github.com/DataJuggler/PixelDatabase
В настоящее время я его рефакторинг, потому что моя первая версия использовала 7 гигабайт памяти, когда я пытался хранить список с более чем 20 миллионами элементов.
Этот класс здесь делает то же самое, что и ваш JavaScript. Кто-то опубликовал это здесь несколько лет go, и я sh Я сохранил их информацию, чтобы отдать им должное.
#region using statements
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
#endregion
namespace DataJuggler.PixelDatabase
{
#region class DirectBitmap
/// <summary>
/// This class is used as a faster alternative to GetPixel and SetPixel
/// </summary>
public class DirectBitmap : IDisposable
{
#region Constructor
/// <summary>
/// Create a new instance of a 'DirectBitmap' object.
/// </summary>
public DirectBitmap(int width, int height)
{
Width = width;
Height = height;
Bits = new Int32[width * height];
BitsHandle = GCHandle.Alloc(Bits, GCHandleType.Pinned);
Bitmap = new Bitmap(width, height, width * 4, PixelFormat.Format32bppPArgb, BitsHandle.AddrOfPinnedObject());
}
#endregion
#region Methods
#region Dispose()
/// <summary>
/// method Dispose
/// </summary>
public void Dispose()
{
if (Disposed) return;
Disposed = true;
Bitmap.Dispose();
BitsHandle.Free();
}
#endregion
#region GetPixel(int x, int y)
/// <summary>
/// method Get Pixel
/// </summary>
public Color GetPixel(int x, int y)
{
int index = x + (y * Width);
int col = Bits[index];
Color result = Color.FromArgb(col);
return result;
}
#endregion
#region SetPixel(int x, int y, Color color)
/// <summary>
/// method Set Pixel
/// </summary>
public void SetPixel(int x, int y, Color color)
{
int index = x + (y * Width);
int col = color.ToArgb();
Bits[index] = col;
}
#endregion
#endregion
#region Properties
#region Bitmap
/// <summary>
/// method [Enter Method Description]
/// </summary>
public Bitmap Bitmap { get; private set; }
#endregion
#region Bits
/// <summary>
/// method [Enter Method Description]
/// </summary>
public Int32[] Bits { get; private set; }
#endregion
#region BitsHandle
/// <summary>
/// This is a ptr to the garbage collector
/// </summary>
protected GCHandle BitsHandle { get; private set; }
#endregion
#region Disposed
/// <summary>
/// method [Enter Method Description]
/// </summary>
public bool Disposed { get; private set; }
#endregion
#region Height
/// <summary>
/// method [Enter Method Description]
/// </summary>
public int Height { get; private set; }
#endregion
#region Width
/// <summary>
/// method [Enter Method Description]
/// </summary>
public int Width { get; private set; }
#endregion
#endregion
}
#endregion
}
Затем в этом классе приведен пример загрузки DirectBitmap. Кое-что из этого указано c для моего приложения, но вы можете взять то, что вам нужно.
#region using statements
using DataJuggler.UltimateHelper.Core;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
#endregion
namespace DataJuggler.PixelDatabase
{
#region class PixelDatabaseLoader
/// <summary>
/// This class is used to load PixelDatabases and their DirectBitmaps
/// </summary>
public class PixelDatabaseLoader
{
#region Methods
#region LoadPixelDatabase(Image original, StatusUpdate updateCallback)
/// <summary>
/// This method is used to load a PixelDatabase and its DirectBitmap object.
/// </summary>
/// <param name="bitmap"></param>
/// <returns></returns>
public static PixelDatabase LoadPixelDatabase(Image original, StatusUpdate updateCallback)
{
// initial valule
PixelDatabase pixelDatabase = null;
try
{
// convert to a bitmap
Bitmap bitmap = (Bitmap) original;
pixelDatabase = LoadPixelDatabase(bitmap, updateCallback);
}
catch (Exception error)
{
// write to console for now
DebugHelper.WriteDebugError("LoadPixelDatabase", "PixelDatabaseLoader", error);
}
finally
{
}
// return value
return pixelDatabase;
}
#endregion
#region LoadPixelDatabase(string imagePath, StatusUpdate updateCallback)
/// <summary>
/// This method is used to load a PixelDatabase and its DirectBitmap object from an imagePath
/// </summary>
/// <param name="bitmap"></param>
/// <returns></returns>
public static PixelDatabase LoadPixelDatabase(string imagePath, StatusUpdate updateCallback)
{
// initial valule
PixelDatabase pixelDatabase = null;
try
{
// if we have an imagePath
if (TextHelper.Exists(imagePath))
{
// create the Bitmap
using (Bitmap bitmap = (Bitmap) Bitmap.FromFile(imagePath))
{
// load the pixelDatabase
pixelDatabase = LoadPixelDatabase(bitmap, updateCallback);
}
}
}
catch (Exception error)
{
// write to console for now
DebugHelper.WriteDebugError("LoadPixelDatabase", "PixelDatabaseLoader", error);
}
finally
{
}
// return value
return pixelDatabase;
}
#endregion
#region LoadPixelDatabase(Bitmap original, StatusUpdate updateCallback)
/// <summary>
/// This method is used to load a PixelDatabase and its DirectBitmap object.
/// </summary>
/// <param name="bitmap"></param>
/// <returns></returns>
public static PixelDatabase LoadPixelDatabase(Bitmap original, StatusUpdate updateCallback)
{
// initial valule
PixelDatabase pixelDatabase = null;
// locals
int max = 0;
try
{
// if we have an image
if (NullHelper.Exists(original))
{
// create a new bitmap
using (Bitmap source = new Bitmap(original))
{
// Create a new instance of a 'PixelDatabase' object.
pixelDatabase = new PixelDatabase();
// Create a DirectBitmap
pixelDatabase.DirectBitmap = new DirectBitmap(source.Width, source.Height);
// Code To Lockbits
BitmapData bitmapData = source.LockBits(new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadWrite, source.PixelFormat);
IntPtr pointer = bitmapData.Scan0;
int size = Math.Abs(bitmapData.Stride) * source.Height;
byte[] pixels = new byte[size];
Marshal.Copy(pointer, pixels, 0, size);
// End Code To Lockbits
// Marshal.Copy(pixels,0,pointer, size);
source.UnlockBits(bitmapData);
// locals
Color color = Color.FromArgb(0, 0, 0);
int red = 0;
int green = 0;
int blue = 0;
int alpha = 0;
// variables to hold height and width
int width = source.Width;
int height = source.Height;
int x = -1;
int y = 0;
// if the UpdateCallback exists
if (NullHelper.Exists(updateCallback))
{
// Set the value for max
max = height * width;
// Set the graph max
updateCallback("SetGraphMax", max);
}
// Iterating the pixel array, every 4th byte is a new pixel, much faster than GetPixel
for (int a = 0; a < pixels.Length; a = a + 4)
{
// increment the value for x
x++;
// every new column
if (x >= width)
{
// reset x
x = 0;
// Increment the value for y
y++;
}
// get the values for r, g, and blue
blue = pixels[a];
green = pixels[a + 1];
red = pixels[a + 2];
alpha = pixels[a + 3];
// create a color
color = Color.FromArgb(alpha, red, green, blue);
// Set the pixel at this spot
pixelDatabase.DirectBitmap.SetPixel(x, y, color);
}
}
// Create the MaskManager
pixelDatabase.MaskManager = new MaskManager();
}
}
catch (Exception error)
{
// write to console for now
DebugHelper.WriteDebugError("LoadPixelDatabase", "PixelDatabaseLoader", error);
}
// return value
return pixelDatabase;
}
#endregion
#endregion
}
#endregion
}
Так что после того, как я загружаю свое изображение и применяю изменения, я обновляю сохранение следующим образом:
// get the bitmap
Bitmap bitmap = PixelDatabase.DirectBitmap.Bitmap;
// Get a fileInfo of the oldPath
FileInfo fileInfo = new FileInfo(FullImagePath);
// Get the index of the period
int index = fileInfo.Name.IndexOf(".");
// get the name
string name = fileInfo.Name.Substring(0, index);
// Get the directory
DirectoryInfo directory = fileInfo.Directory;
// get the directoryFullname
string fullPath = directory.FullName;
// newFileName
string newFileName = name + "." + Guid.NewGuid().ToString().Substring(0, 12) + ".png";
// Get the newPath
string newPath = Path.Combine(fullPath, newFileName);
// Save
bitmap.Save(newPath, ImageFormat.Png);
Это может загрузить и сохранить 20 мегабайт изображений за несколько секунд.
Мы должны объединиться, похоже, мы работаем над тем же. Я подумываю о написании Windows сервиса, который может напрямую обновлять файлы и убирать обработку изображений из Blazor.