Suppongo che tu sia preoccupato di allocare un oggetto non necessario. Penso che una tale preoccupazione sia lodevole.
Ciò che si è probabilmente dopo è un'API Bitmap in cui i byte dell'immagine PNG vengono forniti tramite Stream<byte>
e l'API Bitmap viene quindi prodotta quando necessario dal socket.
System.Drawing
non sembra supportare tale comportamento, possibilmente WIC
(i wrapper .NET esistono attraverso l'eccellente libreria SharpDX
).
Tuttavia, ciò significherebbe mantenere in vita oggetti potenzialmente costosi (bitmap, pennelli e così via) per la durata del trasferimento. L'array di byte potrebbe essere un modo più efficiente per archiviare il risultato.
Un altro approccio per evitare di allocare gli oggetti inutilmente è il loro caching. È reso un po 'più problematico perché gli oggetti System.Drawing
sono mutabili e non sono sicuri da utilizzare da più thread. È tuttavia possibile creare una cache per thread utilizzando ThreadLocal
.
Nel seguente codice di esempio, la maggior parte degli oggetti viene memorizzata nella cache per Thread
. L'unico oggetto creato per chiamata a draw
è l'array di byte restituito, ma probabilmente è un'archiviazione efficiente dei dati PNG (è possibile che le chiamate System.Drawing
assegnino gli oggetti ma non ne abbiamo il controllo). Poiché non ho trovato un modo per ascoltare la "morte" di una discussione, significa che è importante disporre manualmente gli oggetti usando il metodo dispose
quando il Thread non ha più bisogno degli oggetti.
Spero che questo è stato interessante
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
Hi @henrik, il 'MemoryStream' ho voluto avoid.Basically mia aproach sta lavorando. Quello che sta facendo è dopo aver generato l'immagine 'Salva' su un' MemoryStream' che poi viene convertito in array e quindi inviato come risposta. La mia sensazione è che in qualche modo possa essere inviato direttamente come risposta. – Adrian
Puoi mandare Suave in streaming, come ti ho mostrato sopra, oppure puoi scrivere byte nello zoccolo usando le funzioni di pari livello su 'transferStream'. Dipende dalla tua implementazione di Salva. – Henrik
Non è la mia implementazione di 'Save', è il metodo di classe' System.Drawing.Image'. L'API '.net'. Non so come accedervi. – Adrian