Dalla visualizzazione di una funzione tridimensionale
al classico Mandelbrot' set explorer
Questo è stato il mio primo (e probabilmente ultimo)
programma in VB..
Nato inizialmente per la visualizzazione di una funzione tridimensionale, si è poi naturalmente sviluppato per permettere di "esplorare" oggetti più interessanti, come l'insieme di Mandelbrot1 (di cui è riportata sopra un'immagine inconsueta) o altri frattali.
Un explorer dell'insieme di Mandelbrot in versione online (CGI) si trova sulle pagine di David E. Joyce,della Clark University, in Pennsylvania.
 | il sorgente | (the source code) |
 | l'eseguibile per windows | (windows binaries) |
 | runtime libraries per MS VB | (Msvbvm50 & dlls) |
 | alcune immagini | (some pictures) |
come si usa
Se non si ha installato un VB (almeno v. 5), oltre all'eseguibile occorre scaricare le runtime libraries del VB...(il link è a una ricerca preimpostata su google che riporta alcuni siti dove trovarle).
Una volta lanciato il programma, appare la form su cui verrà rappresentata graficamente la funzione:
- selezionando col mouse una parte dell'area viene (ri)avviato il calcolo della funzione per l'intervallo di coordinate scelto;
- si possono modificare le dimensioni della form e ottenere un grafico più o meno dettagliato;
- le dimensioni possono essere modificate anche durante la produzione del grafico (ma i limiti non vengono reimpostati dinamicamente per evitare incongruenze negli "ingrandimenti" successivi ).
- per default vengono creati i file .html e .jpg (per poter rivedere le immagini) in c:\temp; si possono modificare queste impostazioni facendo un doppio-click sulla form;
come funziona
Il funzionamento del programma è molto semplice: crea una form e rappresenta graficamente la funzione fnx(x,y)
definita all'inizio del programma;
la funzione viene quindi campionata su 5000 punti aventi coordinate scelte a caso
all'interno del campo di definizione (da xmin a xmax e da ymin a ymax) per determinare approssimativamente i punti di massimo e minimo;
ad ogni punto del grafico viene quindi assegnato un colore a seconda del valore assunto dalla funzione e proporzionalmente ai valori di massimo e minimo prima calcolati, colore convertito poi da RGB in HSV
(ho tradotto le procedure di conversione RGB-HSV e viceversa dal C : un sorgente si trova ad esempio qui ).
Per modificare il programma basta copiare il sorgente in un file .frm (form VB), avendo ovviamente un compilatore VB recente (io ho usato la vers. 6, ma dovrebbe funzionare anche con versioni precedenti);
Le fasi del progetto, in ordine cronologico, sono state grossomodo queste:
- visualizzazione di una funzione (inizialmente trigonometrica)
- conversione RGB - HSV e vari tentativi di ottimizzare la resa dei colori
- calcolo della successione di Mandelbrot e gestione degli ingrandimenti
- salvataggio immagini in formato JPG
- creazione di files HTML per poter rivedere le immagini in sequenza
- opzioni
il problema del salvataggio delle immagini
Una volta riusciti a visualizzare una funzione, nasce subito anche la necessità di poter salvare l'immagine su un file (possibilmente in un formato compresso), evitando di fare la stampa della schermata e poi "incollarla" in M$ Paint..
ho cercato un po' con altavista e ho trovato un progetto VB che permette di fare una cosa simile..
Nel seguito ho riportato la parte principale dell'articolo che ne spiega l'utilizzo.
Ci sono da notare alcune cose interessanti:
- in VB non c'è un modo per salvare un'immagine in un formato diverso da bitmap;
- esistono invece dei VB controls di vari produttori che permettono di farlo, venduti però a prezzi esorbitanti..
- Steve McMahon, autore dell'articolo, suggerisce di usare una free DLL;
intendendo con "free" ciò che solo un utilizzatore di VB può intendere: "gratis".. non mancando poi di stupirsi perchè qualcuno possa far pagare per del "free code"..
-
nonostante la spiegazione successiva sia chiara e dettagliata appare evidente che l'uso della dll non è affatto semplice e indolore; ad esempio, l'allocazione e utilizzo diretto di un buffer in memoria dimensionato "a occhio" a 1/4 dell'originale potrebbe produrre un po' di classiche exception dovute ad access violation.. (anche se, devo ammettere, nei numerosi test che ho fatto ciò non si è mai verificato);
Saving Pictures to JPG Files from VB
excerpt from
Steve McMahon's page (also copyright owner of the article)
Introduction
Whilst Visual Basic provides support for loading
graphic files in various formats into a StdPicture object, it sadly
forgets about all of these when it comes to saving the file again. You
normally only have one choice of file format for writing: a BMP at the
system colour depth.
Whilst there are various third party controls
available to save files in other formats, these often have
unacceptable 'per seat' licensing policies and can be incredibly
expensive. This article shows how to use a free JPEG DLL library to save
VB pictures.
JPEG File Format
The JPEG file format is
aimed at providing very high compression levels for photographic quality
images. Before deciding whether you want to use JPEG files in any
application you write, there are points to consider about how JPEG
operates.
- High compression ratio
- Wide support - thanks to the free availability of source code to
implement JPEG [qui nel documento originale è erroneamente indicato il formato GIF] read and write, many applications support it. Windows 98
and 2000 even include native support for reading JPG files.
- No license requirements, unlike GIF.
Disadvantages:
- The JPEG format is lossy: colour accuracy is affected when you
save the file. Images saved to JPEG many times loose quality in a
similar way that multiple generations of analogue recordings do.
- It is difficult to encode - not a task you would like to perform
in VB code!
- Works on true colour (24 bit images); not suitable as a format for
storing files aimed at paletised systems.
The Independent
JPEG Group have done a great deal to make the JPEG format much more
accessible to developers by providing free, platform independent C source
code to implement JPEG load and save functions. Their library is used in
Internet Explorer *, Netscape * Navigator
and countless other projects (some of which have even have the audacity to
charge for free code!), including a JPEG Library DLL, ijl11.dll, available from the Intel*
Developer Site.
IJL11.DLL
This DLL makes conversion
to and from JPG simpler by working on a DIB byte format. This makes it
easier to use for Windows programmers than the Independent JPEG Group's C
code, since the input and output format in memory is a standard Windows
format. Conversion between VB StdPicture objects and a DIB is achieved
using the cDIBSection
Class.
From VB
Download the sample project. This
contains all the files required. Firstly, unzip Ijl11.zip and place
ijl11.dll in your system's path (ideally in the Windows\System
directory). Read the licensing agreement included in the zip. If you are
going to use this file for a distributed or commercial project you must
download a registered copy (free) from Intel at their JPEG
Library page. Then there are two VB files needed:
mIJL.bas contains the declares for ijl11.dll
and four wrapper functions:
- LoadJPG
- LoadJPGFromPtr
- SaveJPG
- SaveJPGToPtr
All these function accept an instance of
the cDIBSection Class. The standard versions of the functions
accept a file name as an argument, while the Ptr versions accept a
long value pointing to a memory buffer and a variable indicating the
buffer size.
Although VB can already load a JPG, LoadJPG is
provided as it gives a shortcut for loading a JPG file directly into a
cDIBSection class without requiring a StdPicture object.
LoadJPGFromPtr allows you to load a JPG directly from a byte array
containing the JPG, a function which can't be otherwise be achieved easily
in VB.
To save a StdPicture object directly, you need to do
this:
Dim c As New cDibSection
' Convert
Picture object to DIBSection:
c.CreateFromPicture
picThis.Picture
' Save
it:
SaveJPG c, sFileName
Saving and Loading From Memory Buffers
To Load a JPG from
a memory buffer, first you create a byte array containing all the bytes in
the JPG, or use the Shell's
IMalloc implementation to allocate memory directly and have that
contain the JPG bytes. Then you pass the pointer to the data and the size
of the buffer into the LoadJPGFromPtr method. If you use a byte
array, the pointer is obtained using VB's VarPtr function:
' Assuming the byte array is
b():
lPtr =
VarPtr(b(0))
To save a JPG to a memory buffer directly, you need to create a byte
array or allocate sufficient memory to hold the resultant JPG. Normally
this means creating a larger buffer than the resulting JPG. Since JPG
always compresses an image, the size is always going to be smaller than
the size of an equivalent DIB buffer to hold the bytes. In the sample code
I chose 1/4 of the bytes in the DIBSection (m_cDib.Height *
m_cDib.BytesPerScanLine / 4). Then you pass a pointer to this buffer and
its size to the SaveJPGFromPtr. The method modifies the size
variable passed in to reflect the actual size of the JPG created which you
can then use to trim down the buffer to size.
In pratica?
Quali sono i passi fondamentali per salvare un'immagine in formato JPG?
Una volta scaricato il progetto SaveJPEG (da cui occorre estrarre la ijl11.dll, mIJL.bas e cDIBSection.cls):
- importare il modulo mIntelJPEGLibrary (da mIJL.bas)
- importare la classe cDIBSection (da cDIBSection.cls)
- dichiarare un oggetto di tipo cDIBSection:
Dim c As New cDibSection
- convertire l'oggetto picture in DIB
c.CreateFromPicture picThis.Picture
- salvare la DIB
SaveJPG c, sFileName
...Sembra facile, ma non lo è... (ho impiegato alcune ore per riuscire a salvare perfettamente una JPG):
prima di tutto, cosa si intende per oggetto picture?
La mia completa ignoranza di VB mi ha fatto inizialmente pensare che si potesse semplicemente scrivere
c.CreateFromPicture form1.Picture
cioè creare un oggetto DIB a partire dalla proprità Picture della form che ho usato (forse impropriamente) come "piano di lavoro" per il disegno;
ma in questo modo si ottiene soltanto un'immagine completamente nera..
le form hanno però, oltre alla Picture, una proprietà Image: ho provato quindi
c.CreateFromPicture form1.Image
con identico risultato!
Eppure l'immagine è sulla form (e si vede).. ho provato quindi a "copiare" form1.picture in un oggetto Image:
Image1.Picture = Form1.Image
c.CreateFromPicture Image1.Picture
In questo modo sono finalmente riuscito a salvare la JPG, ma la dimensione dell'immagine era sempre 800*600 pixel (uguale alla definizione impostata per lo schermo);
per ottenere un'immagine della dimesione corretta ho dovuto modificare la classe cDIBSection e aggiungere alla CreateFromPicture i parametri per altezza e larghezza:
Public Function CreateFromPicture( ByRef picThis As StdPicture, xWidth As Long, xHeight As Long )
quelli che dovevano teoricamente essere passati come proprietà dell'oggetto picture risultavano sempre a zero (e successivamente impostati al default, 800*600 )
Conclusione
Credo che, pur nella sua limitatezza, questo progetto sia già un buon esempio della tecnica di programmazione "per compromessi" tipica di Windows, che offre un rapido ambiente di sviluppo fin quando si tratta di applicazioni semplici e comuni, ma che per applicazioni appena diverse ti lascia (nel caso migliore) in balia di librerie undocumented;
l'articolo trovato su vbaccelerator (come del resto se ne possono trovare su moltissimi altri siti) mostra comunque la tendenza in atto verso l'opensource, anche su piattaforme come Windows, strettamente legate al mondo commerciale.
Un evidente limite del programma consiste nel numero di ingrandimenti possibile (nell'ordine della decina), ed è dovuto alla precisione (grande ma limitata) del tipo numerico Double;
l'alternativa più ovvia è l'utilizzo della libreria bcmath di Philip A. Nelson, che consente una precisione limitata solo dalle risorse di sistema;
Altri successivi sviluppi potrebbero essere (quando avrò tempo...):
- una automatizzazione del processo di scelta dell'area da ingrandire (che, per risultare interessante, dovrà essere una zona "di frontiera"), basata ad es. sulla presenza di un sufficiente numero di colori (quindi di diversi valori della funzione) in un'area ristretta;
- il porting in php dell'intero progetto (per il quale tra l'altro la libreria bcmath è già disponibile); un'alternativa interessante alla quale ho pensato potrebbe essere interfacciare il programma VB con uno script php che esegue l'elaborazione completa (compresa la creazione della JPG) sulle coordinate che gli vengono passate (il VB rimarrebbe solo come interfaccia di visualizzazione);
Glossario
DIB = device-independent bitmap
HSV = hue, saturation, value
RGB = red, green, blue
HLS = hue, lightness, saturation
HLS è un sinonimo meno comune di HSV;
JPEG = Joint Photographic Expert Group, vedi lo standard ISO JPEG, l'Independent JPEG Group
GIF = Graphics Interchange Format, vedi anche burnallgifs
PNG = Portable Network Graphics, vedi l'home page
note
- In realtà il cosiddetto insieme "di Mandelbrot" è stato scoperto (e visualizzato graficamente) dal monaco Benedettino Udo di Aachen nel tredicesimo secolo!