Mostrare una immagine JPEG in una finestra con le funzioni del API di X11 e di Libjpeg

Da Gambas-it.org - Wikipedia.

Per mostrare una immagine JPEG in una finestra creata con le risorse della libreria del server grafico X11, sarà necessario decodificare il file immagine.
Per effettuare tale decodifica ci serviremo delle risorse esterne della libreria: "Libjpeg ".

Per maggiore sicurezza la maggior parte della procedura di decodifica sarà effettuata utilizzando istruzioni di codice in linguaggio C poste all'interno di una libreria condivisa .so, da noi realizzata, che sarà chiamata dal programma principale scritto in Gambas.

Per poter utilizzare le risorse esterne del API di X11 sarà necessario richiamare la libreria condivisa: "libX11.so.6.4.0 ".

Pertanto, il codice C contenuto dalla libreria condivisa .so, da noi appositamente realizzata e che porremo nella cartella Dati dell'applicazione principale Gambas, supporta solo una profondità di colori non inferiore a 24 bit e sarà il seguente:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <jpeglib.h>
#include <jerror.h>

#ifndef u_char
#define u_char unsigned char
#endif


/*Ritorna un vettore per un'immagine a 24 bit.*/
u_char *Decodifica_jpeg (char *nomefile, int *widthP, int *heightP) {
 register JSAMPARRAY buffer;    //--> occupa 8 byte
 struct jpeg_decompress_struct cinfo;
 struct jpeg_error_mgr err_mgr;
 int bytePerPixel, x, y;
 FILE *fl;
 u_char *BufferRitorno;
 
 fl = fopen (nomefile, "rb");
 if (NULL == fl) { 
   perror (NULL);
   return NULL;
 }

 cinfo.err = jpeg_std_error (&err_mgr);

 jpeg_create_decompress (&cinfo);
 jpeg_stdio_src (&cinfo, fl);
 jpeg_read_header (&cinfo, 1);
 cinfo.do_fancy_upsampling = 0;
 cinfo.do_block_smoothing = 0;
 jpeg_start_decompress (&cinfo);

 *widthP = cinfo.output_width;
 *heightP = cinfo.output_height;
 bytePerPixel = cinfo.output_components;

 buffer = cinfo.mem->alloc_sarray ((j_common_ptr) &cinfo, JPOOL_IMAGE, (*widthP * bytePerPixel), 1);
 
 BufferRitorno = malloc (3 * (*widthP * *heightP));
   
 if (NULL == BufferRitorno) {
   perror (NULL);
   return NULL;
 }
   
 if (3 == bytePerPixel) {
   int scorrLinea = (*widthP * 3);
   
   for (y = 0; y < cinfo.output_height; ++y) {
     jpeg_read_scanlines (&cinfo, buffer, 1);
       
     for (x = 0; x < scorrLinea; ++x) {
       BufferRitorno[(scorrLinea * y) + x] = buffer[0][x];
       ++x;
       BufferRitorno[(scorrLinea * y) + x] = buffer[0][x];
       ++x;
       BufferRitorno[(scorrLinea * y) + x] = buffer[0][x];
     }
   }
 } else if (1 == bytePerPixel) { 
   unsigned int col;
   int scorrLinea = (*widthP * 3);
   int indiceBuff;
           
   for (y = 0; y < cinfo.output_height; ++y) {
     jpeg_read_scanlines (&cinfo, buffer, 1);
       
     indiceBuff = 0;
     for (x = 0; x < scorrLinea; ++x) {
       col = buffer[0][indiceBuff];
     
       BufferRitorno[(scorrLinea * y) + x] = col;
       ++x;
       BufferRitorno[(scorrLinea * y) + x] = col;
       ++x;
       BufferRitorno[(scorrLinea * y) + x] = col;
       
       ++indiceBuff;
     }      
   }
 } else {
   fprintf (stderr, "Errore: il numero dei canali del colore è %d. Questa applicazione può gestire soltanto 1 o 3\n", bytePerPixel);
   return NULL;
 }
 jpeg_finish_decompress (&cinfo);
 jpeg_destroy_decompress (&cinfo);
 fclose (fl);
     
 return BufferRitorno;
}


Il codice Gambas potrà essere il seguente:

Private disp As Pointer

Library "libX11:6.4.0"

Public Struct XEventStruct
  type As Integer
  serial As Long
  send_event As Boolean
  display As Pointer
  windowL As Long
  root As Long
  subwindow As Long
  timeL As Long
  x As Integer
  y As Integer
  x_root As Integer
  y_root As Integer
  state As Integer
  keycode As Integer
  same_screen As Boolean
End Struct

Public Struct Visual
  XExtData As Pointer
  visualid As Integer
  classe As Long
  red_mask As Long
  green_mask As Long
  blue_mask As Long
  map_entries As Integer
End Struct

Private Const ExposureMask As Integer = 32768
Private Const KeyPressMask As Byte = 1
Private Const ButtonPressMask As Byte = 4
Private Const ZPixmap As Integer = 2
Private Enum KeyPress = 2, KeyRelease, ButtonPress, ButtonRelease, MotionNotify, EnterNotify,
             LeaveNotify, FocusIn, FocusOut, KeymapNotify, Expose, GraphicsExpose, NoExpose

' Display *XOpenDisplay(char *display_name)
' Opens a connection to the X server that controls a display.
Private Extern XOpenDisplay(display$ As Pointer) As Pointer

' XCloseDisplay(Display *display)
' Closes the connection to the X server for the display specified in the Display structure and destroys all windows.
Private Extern XCloseDisplay(displayP As Pointer)

' int XDefaultScreen (Display *display)
' returns the default screen number referenced by the XOpenDisplay function.
Private Extern XDefaultScreen(displayP As Pointer) As Integer

' unsigned long XWhitePixel (Display *display, int screen_number)
' returns the white pixel value for the specified screen.
Private Extern XWhitePixel(displayP As Pointer, screen_number As Integer) As Long

' unsigned long XBlackPixel (Display *display, int screen_number)
' returns the black pixel value for the specified screen.
Private Extern XBlackPixel(displayP As Pointer, screen_number As Integer) As Long

' Window XDefaultRootWindow(Display *display)
' Return the root window for the default screen.
Private Extern XDefaultRootWindow(displayP As Pointer) As Integer

' Window XCreateSimpleWindow(Display *display, Window parent, int x, int y, unsigned int width, unsigned int height, unsigned int border_width, unsigned long border, unsigned long background)
' Creates an unmapped InputOutput subwindow for a specified parent window, returns the window ID of the created window.
Private Extern XCreateSimpleWindow(displayP As Pointer, parent As Long, x As Integer, y As Integer, width As Integer, height As Integer, border_width As Integer, border As Integer, background As Long) As Integer 

' XSetStandardProperties (Display *display, Window w, char *window_name, char *icon_name, Pixmap icon_pixmap, char **argv, int argc, XSizeHints *hints)
' Specifies a minimum set of properties describing the simplest application.
Private Extern XSetStandardProperties(displayP As Pointer, w As Long, window_name As String, icon_name As String, icon_pixmap As Integer, argv As String, argc As Integer, hints As Pointer)

' XSelectInput (Display *display, Window w, long event_mask)
' Requests that the X server report the events associated with the specified Event mask.
Private Extern XSelectInput(displayP As Pointer, w As Long, event_mask As Long)

' GC XCreateGC(Display *display, Drawable d, unsigned long valuemask, XGCValues *values)
' Creates a graphics context and returns a GC.
Private Extern XCreateGC(displayP As Pointer, w As Long, valuemask As Long, values As Pointer) As Pointer

' XClearWindow(Display *display, Window w)
' Clears the entire area in the specified window.
Private Extern XClearWindow(displayP As Pointer, w As Long)

' XMapRaised (Display *display, Window w)
' Raises the specified window to the top of the stack.
Private Extern XMapRaised(displayP As Pointer, w As Long)

' XNextEvent (Display *display, XEvent *event_return)
' Gets the next event and remove it from the queue.
Private Extern XNextEvent(displayP As Pointer, event_return As XEventStruct)

' XFreeGC(Display *display, GC gc)
' Destroys the specified GC as well as all the associated storage.
Private Extern XFreeGC(displayP As Pointer, gcP As Pointer)

' XDestroyWindow(Display *display, Window w)
' Destroys the specified window as well as all of its subwindows.
Private Extern XDestroyWindow(displayP As Pointer, w As Long)

' XPutImage(Display *display, Drawable d, GC gc, XImage *image, int src_x, int src_y, int dest_x, int dest_y, unsigned int width, unsigned int height)
' Combines an image with a rectangle of the specified drawable.
Private Extern XPutImage(displayP As Pointer, w As Long, gcP As Pointer, image As Pointer, src_x As Integer, src_y As Integer, dest_x As Integer, dest_y As Integer, width As Integer, height As Integer)

' int XDefaultDepth(Display *display, int screen_number)
' Returns the depth (number of planes) of the default root window for the specified screen.
Private Extern XDefaultDepth(displayP As Pointer, screen_number As Integer) As Integer

' Visual *XDefaultVisual(Display *display, int screen_number)
' Returns the default visual type for the specified screen.
Private Extern XDefaultVisual(displayP As Pointer, screen_number As Integer) As Visual

' XImage *XCreateImage(Display *display, Visual *visual, unsigned int depth, int format, int offset, char *data, unsigned int width, unsigned int height, int bitmap_pad, int bytes_per_line)
' Allocates the memory needed for an XImage structure for the specified display but does not allocate space for the image itself.
Private Extern XCreateImage(displayP As Pointer, visu As Visual, depth As Integer, formatI As Integer, offset As Integer, data As Integer[], width As Integer, height As Integer, bitmap_pad As Integer, bytes_per_line As Integer) As Pointer

' Status XInitImage(XImage *image)
' Initializes the internal image manipulation routines of an image structure, based on the values of the various structure members.
Private Extern XInitImage(image As Pointer)


' void exit(int status)
' Terminates the calling process immediately.Any Open file descriptors belonging To the process are closed And any children Of the process are inherited.
Private Extern exitus(status As Integer) In "libc:6" Exec "exit"

Private Extern Decodifica_jpeg(FileImmagine As String, wP As Pointer, hP As Pointer) As Pointer In "/tmp/libadhoc_jpeg"


Public Sub Main()

 Dim screen, prof, iW, iH As Integer
 Dim pxW, pxB, id As Long
 Dim ev As New XEventStruct
 Dim p, img, gc As Pointer
 Dim ImmBuffer As Integer[]

' Crea la nostra libreria condivisa ad hoc:
 If Exist("/tmp/libadhoc_jpeg.c") = False Then Copy "libadhoc_jpeg.c" To "/tmp/libadhoc_jpeg.c"
 Shell "gcc -o /tmp/libadhoc_jpeg.so /tmp/libadhoc_jpeg.c -shared -fPIC -lX11 -ljpeg" Wait

 p = Decodifica_jpeg("/percorso/del/file/immagine.jpg", VarPtr(iW), VarPtr(iH))
 If p == 0 Then Error.Raise("Errore !")

 disp = XOpenDisplay(0)
 If disp == 0 Then Error.Raise("Impossibile aprire il display !")

 screen = XDefaultScreen(disp)
 prof = XDefaultDepth(disp, screen)
  
 ImmBuffer = Crea_Dati_immagine(screen, prof, p, iW, iH)
  
 img = XCreateImage(disp, Null, prof, ZPixmap, 0, ImmBuffer, iW, iH, 32, 0)
 If img == 0 Then Error.Raise("Errore !")

 pxW = XWhitePixel(disp, screen)
 pxB = XBlackPixel(disp, screen)
   
 id = XCreateSimpleWindow(disp, XDefaultRootWindow(disp), 0, 0, iW, iH, 5, pxW, &FF0000)
 Print "ID della finestra creata: "; Hex(id, 6)
  
 XSetStandardProperties(disp, id, "Prova creazione finestra", "Nome icona finestra", 0, Null, 0, Null)
 XSelectInput(disp, id, ExposureMask Or KeyPressMask Or ButtonPressMask)
  
 gc = XCreateGC(disp, id, 0, Null)
 If gc == 0 Then Error.Raise("Impossibile creare un nuovo contesto grafico !")

 XMapRaised(disp, id)

 While True
   XNextEvent(disp, ev)
   Select Case ev.type
     Case Expose
       redraw(id)
       XInitImage(img)
       XPutImage(disp, id, gc, img, 0, 0, 0, 0, iW, IH)
     Case Else
       Exit
   End Select
 Wend
  
 chiude_X(id, gc)

End

Private Procedure redraw(idR As Integer)

' Pulisce la superficie della finestra da ogni elemento presente:
 XClearWindow(disp, idR)

End

Private Procedure chiude_X(idCh As Integer, gcont As Pointer)

 XFreeGC(disp, gcont)
' Chiude la finestra:
 XDestroyWindow(disp, idCh)
 XCloseDisplay(disp)
 exitus(0)

End

Private Function Crea_Dati_immagine(screen As Integer, profond As Integer, buf As Pointer, width As Integer, height As Integer) As Integer[]
 
 Dim r, g, b, scorr As Integer
 Dim vis As New Visual
 Dim rRatio, gRatio, bRatio As Float
 Dim vettI As Integer[]
 Dim st As Stream
 Dim buffer As Byte

 vis = XDefaultVisual(disp, screen)
 
 rRatio = CFloat(vis.red_mask) / 255.0
 gRatio = CFloat(vis.green_mask) / 255.0
 bRatio = CFloat(vis.blue_mask) / 255.0
 
 st = Memory buf For Read

 If profond >= 24 Then
   vettI = New Integer[](width * height)
   Repeat
     Read #st, buffer
     r = CInt(buffer * rRatio)
     
     Read #st, buffer
     g = CInt(buffer * gRatio)

     Read #st, buffer
     b = CInt(buffer * bRatio)

     r = r And vis.red_mask
     g = g And vis.green_mask
     b = b And vis.blue_mask

     vettI[scorr] = r Or g Or b
     Inc scorr
   Until scorr >= vettI.Max
 Else
   Error.Raise("Questa applicazione non supporta una profondità inferiore a 24 !")
 Endif

 st.Close

 Return vettI

End


Riferimenti