Я предполагаю, что вас беспокоит выделение ненужного объекта. Я считаю, что такая озабоченность заслуживает похвалы.
Возможно, что вы, вероятно, после этого - это API-интерфейс Bitmap, в котором байты PNG-изображения предоставляются через Stream<byte>
, а затем Bitmap API создает, когда это необходимо, сокетом.
System.Drawing
похоже, что не поддерживает такое поведение, возможно WIC
делает (оболочки .NET существуют через отличную библиотеку SharpDX
).
Однако это будет означать сохранение потенциально дорогостоящих объектов (растровых изображений, кистей и т. Д.) В течение всего периода передачи. Байт-массив может быть более эффективным способом хранения результата.
Другой подход во избежание ненужного выделения объектов - это их кеширование. Это немного сложнее, потому что объекты System.Drawing
являются изменяемыми и небезопасными для использования из нескольких потоков. Тем не менее вы можете создать кеш на поток с помощью ThreadLocal
.
В примере кода большинство объектов кэшируется в Thread
. Единственный объект, созданный за один вызов до draw
, - это возвращаемый массив байтов, но это, вероятно, эффективное хранилище данных PNG (возможно, вызовы System.Drawing
выделяют объекты, но мы не можем контролировать их). Поскольку я не понял способ прослушать «смерть» Thread, это означает, что важно вручную уничтожить объекты с помощью метода dispose
, когда Thread больше не нуждается в объектах.
Надеется, что это было интересно
open System
open System.Drawing
open System.Drawing.Imaging
open System.IO
open System.Threading
module BitmapCreator =
module internal Details =
let dispose (d : IDisposable) =
if d <> null then
try
d.Dispose()
with
| e ->() // TODO: log
// state is ThreadLocal, it means the resources gets initialized once per thread
let state =
let initializer() =
// Allocate all objects needed for work
let font = new Font("Courier", 24.0F)
let red = new SolidBrush(Color.Red)
let white = new SolidBrush(Color.White)
let bitmap = new Bitmap(600,400)
let g = Graphics.FromImage bitmap
let ms = new MemoryStream 1024
// disposer should be called when Thread is terminating to reclaim
// resources as fast as possible
let disposer() =
dispose ms
dispose g
dispose bitmap
dispose white
dispose red
dispose font
font, red, white, bitmap, g, ms, disposer
new ThreadLocal<_>(initializer)
// Draws text on a bitmap and returns that as a byte array
let draw text =
// Grab the state for the current thread
let font, red, white, bitmap, g, ms, _ = Details.state.Value
g.FillRectangle(white, 0.0F, 0.0F, 600.0F, 400.0F)
g.DrawString(text, font, red, 10.0F, 40.0F)
g.Flush()
// Resets the memory stream
// The capacity is preserved meaning as long as the generated
// images is equal or smaller in size no realloc is needed
ms.Seek (0L, SeekOrigin.Begin) |> ignore
ms.SetLength 0L
bitmap.Save(ms, ImageFormat.Png)
// Here a new array is allocated per call
// Depending on how FillRectangle/DrawString works this is hopefully our
// only allocation
ms.ToArray()
// Disposes all BitmapCreator resources held by the current thread
let dispose() =
let _, _, _, _, _, _, disposer = Details.state.Value
disposer()
[<EntryPoint>]
let main argv =
// Saves some bitmaps to file, the name include the thread pid in order
// to not overwrite other threads' images
let save() =
let texts = [|"Hello"; "There"|]
let tid = Thread.CurrentThread.ManagedThreadId
for text in texts do
File.WriteAllBytes (sprintf "%s_%d.png" text tid, BitmapCreator.draw text)
// Runs a in other thread, disposes BitmapCreator resources when done
let runInOtherThread (a : unit -> unit) =
let a() =
try
a()
finally
BitmapCreator.dispose()
let thread = Thread a
thread.Start()
thread.Join()
Environment.CurrentDirectory <- AppDomain.CurrentDomain.BaseDirectory
try
save() // Here we allocate BitmapCreator resources
save() // Since the same thread is calling the resources will reused
runInOtherThread save // New thread, new resources
runInOtherThread save // New thread, new resources
finally
BitmapCreator.dispose()
0
Привет @henrik, то 'MemoryStream' Я хотел avoid.Basically моего ПОДХОДА работает. Делается это после создания образа 'Save' на« MemoryStream », который затем преобразуется в массив и затем отправляется как ответ. Я чувствовал, что так или иначе это может быть отправлено непосредственно в ответ. – Adrian
Вы можете либо передать Suave поток, как я показал вам выше, либо вы сами можете написать байты в сокет, используя функции sibling для 'transferStream'. Это зависит от вашей реализации Save. – Henrik
Это не моя реализация 'Save', это метод класса System.Drawing.Image. '.net' api. Я не знаю, как получить к нему доступ. – Adrian