Creare una Image direttamente dai dati di un file immagine scaricato da internet senza ricreare il file nella memoria di massa

Da Gambas-it.org - Wikipedia.

Il problema qui posto è quello di come creare una immagine di tipo Image (da assegnare convertita in una Picture ad una PictureBox) direttamente dai dati di un file immagine scaricato da internet, senza che tale file sia stato salvato in un percorso di una cartella nella memoria di massa, e quindi caricato con l'apposito metodo .Load() della Classe Image.

Per scaricare il file immagine ci serviremo delle risorse della Classe HttpClient, attivando i Componenti Gambas gb.net e gb.net.curl. Però tale Classe consente di scaricare in dati formato Stringa il file immagine "così com'è". Vengono, cioè scaricati i dati del file immagine nel suo formato in quanto tale, e non già i suoi dati immagine grezzi. Ciò significa che se viene scaricato un file immagine compresso non potremo fare altro che salvarlo dapprima in memoria dura e solo successivamente caricarlo in memoria RAM con le apposite risorse della Classe Image.

Ricapitolando: l'unica cosa, che abbiamo nella RAM immediatamente dopo lo scarico, è una variabile di tipo Stringa contenente tutti i dati del file immagine. La parte difficile è come assegnare ad una PictureBox i dati del file immagine "così com'è" contenuti dalla variabile di tipo Stringa senza passare preliminarmente per il metodo .Load() della Classe Image.

Il problema può essere risolto, purché l'immagine da scaricare abbia il canale Alfa e dunque sia di formato a 32-bit - RGBA. Bisognerà, però, utilizzare alcune funzioni esterne di gdk_pixbuf richiamando nel progetto Gambas la libreria condivisa: "libgtk-3.so.0.2404.16 ".

Mostriamo di seguito il codice con un esistente indirizzo web di un'immagine PNG da scaricare:

Private myhttp As New HttpClient As "myhttp"  


Library "libgtk-3:0.2404.16"

' GdkPixbufLoader * gdk_pixbuf_loader_new(void)
' Creates a new pixbuf loader object.
Private Extern gdk_pixbuf_loader_new() As Pointer

' gboolean gdk_pixbuf_loader_write (GdkPixbufLoader *loader, const guchar *buf, gsize count, GError **error)'
' Cause a pixbuf loader to parse the next count bytes of an image.
Private Extern gdk_pixbuf_loader_write(lo As Pointer, buf As Pointer, i As Integer, po As Pointer)

' gboolean gdk_pixbuf_loader_close (GdkPixbufLoader *loader, GError **error)'
' Informs a pixbuf loader that no further writes.
Private Extern gdk_pixbuf_loader_close(lo As Pointer, po As Pointer)

' GdkPixbuf * gdk_pixbuf_loader_get_pixbuf (GdkPixbufLoader *loader)'
' Queries the GdkPixbuf that a pixbuf loader is currently creating.
Private Extern gdk_pixbuf_loader_get_pixbuf(lo As Pointer) As Pointer

' guchar * gdk_pixbuf_get_pixels (const GdkPixbuf *pixbuf)'
' Queries a pointer to the pixel data of a pixbuf.
Private Extern gdk_pixbuf_get_pixels(GdkPixbuf As Pointer) As Pointer

' int gdk_pixbuf_get_width (const GdkPixbuf *pixbuf)'
' Queries the width of a pixbuf.
Private Extern gdk_pixbuf_get_width(GdkPixbuf As Pointer) As Integer

' int gdk_pixbuf_get_height (const GdkPixbuf *pixbuf)'
' Queries the height of a pixbuf.
Private Extern gdk_pixbuf_get_height(GdkPixbuf As Pointer) As Integer

' int gdk_pixbuf_get_n_channels (const GdkPixbuf *pixbuf)'
' Queries the number of channels of a pixbuf.
Private Extern gdk_pixbuf_get_n_channels(GdkPixbuf As Pointer) As Integer

' gboolean gdk_pixbuf_get_has_alpha (const GdkPixbuf *pixbuf)'
' Queries whether a pixbuf has an alpha channel (opacity information).
Private Extern gdk_pixbuf_get_has_alpha(GdkPixbuf As Pointer) As Boolean


Public Sub Button1_Click()
 
' Invia la richiesta al server per scaricare il file immagine:
  myhttp.URL = "http://f.tqn.com/y/painting/1/W/4/k/2/RGB_color_wheel_36.jpg.png"
  myhttp.Timeout = 15

' Lasciando il secondo parametro vuoto, i dati dell'immagine vengono salvati soltanto in memoria:
  myhttp.Get()
    
End

Public Sub MyHTTP_Finished()

 Dim s As String

 MyHTTP.Close
 
' Trasferiamo in una variabile stringa i dati dell'immagine scaricata e salvata in memoria:
 s = myhttp.Peek()
 Print "Immagine scaricata !\n\nDimensione: "; Len(s); " byte"
 
 Immagine(s)
  
End

Private Procedure Immagine(im As String)
 
 Dim bb, bb2 As Byte[]
 Dim w, h As Integer
 Dim loader, pixbuf, dati As Pointer
 Dim c As Byte
 Dim bo As Boolean
 Dim st As Stream
 Dim imago As Image
  
 bb = Byte[].FromString(im)
 
 loader = gdk_pixbuf_loader_new()
 If loader == 0 Then Error.Raise("Errore !")
 
 gdk_pixbuf_loader_write(loader, bb.data, bb.count, 0)
 
 gdk_pixbuf_loader_close(loader, 0)
 
 pixbuf = gdk_pixbuf_loader_get_pixbuf(loader)
 If pixbuf == 0 Then Error.Raise("Errore !")
 
 w = gdk_pixbuf_get_width(Pixbuf)
 Print "Larghezza: "; w; " pixel"
 h = gdk_pixbuf_get_height(Pixbuf)
 Print "Altezza:   "; h; " pixel"
 c = gdk_pixbuf_get_n_channels(Pixbuf)
 Print "Canali:    "; c
  
' Verifichiamo che l'immagine scaricata abbia il canale Alfa:
 bo = gdk_pixbuf_get_has_alpha(Pixbuf)
 If bo = False Then Error.Raise("L'immagine scaricata è priva del canale Alfa !")
  
 dati = gdk_pixbuf_get_pixels(Pixbuf)
 If dati == 0 Then Error.Raise("Impossibile ottenere un 'Puntatore' ai dati dei pixel dell'immagine !")
  
' Carichiamo i dati nel vettore "bb[]" per gestire successivamente i dati grezzi dei pixel:
 st = Memory dati For Read
  
 With bb = New Byte[w * h * c]
   .Read(st, 0, .count)
 End With
 
' Effettua la correzione della disposizione dei dati RGB dei pixel dell'immagine scaricata:
 bb2 = Corregge(bb)
  
 st.Close
 
' Viene preparata la variabile di tipo Image per la gestione dei dati del futuro nuovo file immagine:
 With imago = New Image(w, h, 0, 0)
   st = Memory .Data For Write
 End With
 
' Scrive i dati presenti nel vettore "bb[]" nell'area di memoria puntata dal Puntatore ".Data" della variabile di tipo "Image":
 bb2.Write(st, 0, bb2.Count)
 
 st.Close
 
 With PictureBox1
   .W = w
   .H = h
   .Picture = imago.Picture
 End With
 
End

Private Function Corregge(vett As Byte[]) As Byte[]
 
 Dim i As Integer
 Dim bb As Byte[]
 
 bb = New Byte[vett.Count]
  
 For i = 0 To vett.Max Step 4
   bb[i + 1] = vett[i + 1]
   bb[i + 2] = vett[i]
   bb[i] = vett[i + 2]
   bb[i + 3] = vett[i + 3]
 Next
  
 Return bb
  
End