Applet Java con Effetto Fuoco

Di applet Java che hanno fatto la storia ce ne sono molte ed oggi, anche se passano via via in disuso per essere sostituite da animazioni flash, questo tipo di programmi offrono sempre lo spunto per nuovi giochi divertenti che possono essere inseriti in una pagina web. In questo articolo vedremo, a puro scopo didattico per spiegare come funziona un’applet Java ed in particolare la manipolazione dei pixel, come è possibile creare una famosissima, vecchissima e conosciutissima applet Java che ci consente di incendiare un’immagine che inseriremo come parametro, effetto fuoco molto carino con le scritte, in particolare testo chiaro su sfondo scuro.
L’applet che andremo a scrivere manipolerà un’immagine appiccando le fiamme a ciò che vi è contenuto, ecco i passi che implementeremo:

  1. L’immagine, il cui indirizzo è fornito come argomento, deve essere caricata in memoria.
  2. La matrice di pixel che costituisce la figura deve essere estratta ed analizzata per vedere i pixel più brillanti che servono per incendiare gli altri.
  3. Una volta ottenuta la mappa dei punti caldi è necessario incendiarli sfruttando una routine sulla brillantezza di ciascuno e di quelli ad essi adiacenti.
  4. Prima di disegnare l’immagine occorre variare il modello di rappresentazione dei colori, finora incentrato sulla loro brillantezza, andando dal giallo fino al nero. La sfumatura dovrà passare anche per il rosso e l’arancione e le loro gradazioni, fino a quelle più scure.
  5. Occorre estrapolare le informazioni dall’immagine ogni volta che questa viene mostrata, finché l’applet risulta essere in esecuzione, questo affinché il frame successivo sia calcolato in vista di un passaggio fluido e non repentino.

Ecco il risultato a cui arriveremo:

Applet Fuoco

Testo infuocato nell'applet Java

ed ecco il codice che serve per arrivare a tale risultato:

import java.awt.*;
import java.awt.image.*;
import java.applet.*;

public class Fuoco extends Applet implements Runnable
{
    Image buffer;
    Thread thread;
    ColorModel colorModel;

    int w, h, p;
    int[] mappa, destinazione, lavoro;
    int[] valoriRandom;

    public void init()
    {
        createColorModel();
        valoriRandom = new int[600];
        for (int i = 0; i < valoriRandom.length; i++)
            valoriRandom[i] = (int)Math.floor(Math.random() * 255);
    }

    private void createColorModel()
    {
        byte ind1[] = new byte[255];
        byte ind2[] = new byte[255];
        byte ind3[] = new byte[255];

        for (int i = 0; i < 255; i++)
            ind1[i] = (byte)i;
        for (int i = 0; i < 255; i++)
            ind2[i] = (i <= 128) ? 0 : (byte)(2 * (i - 128));
        for (int i = 0; i < 255; i++)
            ind3[i] = (i <= 192) ? 0 : (byte)(2 * (i - 64));

        colorModel = new IndexColorModel(8, 255, ind1, ind2, ind3);
    }

    public void start()
    {
        if (thread == null)
        {
            thread = new Thread(this);
            thread.start();
        }
    }

    public void stop()
    {
        thread = null;
    }

    public void run()
    {
        String imageName = getParameter("image");
        if (imageName == null)
        {
            notificaErrore("Parametro 'image' non specificato");
            return;
        }

        Image image;
        try
        {
            MediaTracker mt = new MediaTracker(this);
            image = getImage(getDocumentBase(), imageName);
            mt.addImage(image, 0);
            mt.waitForID(0);
        }
        catch (Exception e)
        {
            notificaErrore("Impossibile caricare l'immagine");
            return;
        }

        w = image.getWidth(null);
        h = image.getHeight(null);
        p = w * h;

        int[] origine = new int[p];
        try
        {
            new PixelGrabber(image, 0, 0, w, h,
                    origine, 0, w).grabPixels();
        }
        catch (Exception e)
        {
            return;
        }

        mappa = new int[p];
        for (int i = 0; i < p; i++)
        {
            int r = (origine[i] & 0xff0000) >> 16;
            int g = (origine[i] & 0xff00) >> 8;
            int b = (origine[i] & 0xff);
            int media = (r + g + b) / 3;
            if (media > 128)
                mappa[i] = (int)(Math.random() * 128) + 127;
        }

        destinazione = new int[p];
        for (int i = 0; i < p; i++)
            destinazione[i] = 0;

        lavoro = new int[p];
        for (int i = 0; i < p; i++)
            lavoro[i] = mappa[i];

        try
        {
            int i = 0;
            while (thread != null)
            {
                long t = System.currentTimeMillis();
                brucia();
                buffer = createImage(new MemoryImageSource(w, h,
                        colorModel, destinazione, 0, w));
                repaint();
                i++;
                if (i > 50)
                {
                    System.gc();
                    i = 0;
                }
                Thread.sleep(Math.max(25 -
                      (System.currentTimeMillis() - t), 0));
            }
        }
        catch (Exception e) { }
    }

    private void brucia()
    {
        int c = (int)Math.floor(Math.random() * 255);
        for (int i = 1; i < p - w; i++)
        {
            if (c > 490)
                c = 0;
            int sotto = i + w;
            int sinistra = i - 1;
            int destra = i + 1;
            if (lavoro[sotto] != 0 || lavoro[sinistra] != 0 ||
                    lavoro[destra] != 0)
            {
                int disc;
                int aux = valoriRandom[c++] % 5;
                if (aux > 2)
                    disc = 0;
                else if (aux > 0)
                    disc = 1;
                else
                    disc = -1;

                int p1 = i + disc;
                int p2 = (valoriRandom[c++] % 20) + 235;
                int p3 = valoriRandom[c++] % 5;

                switch (p3)
                {
                case 0:
                    lavoro[p1] = (lavoro[destra] * p2) / 255;
                    break;
                case 1:
                    lavoro[p1] = (lavoro[sinistra] * p2) / 255;
                    break;
                default:
                    lavoro[p1] = (lavoro[sotto] * p2) / 255;
                    break;
                }

                if (lavoro[p1] * 3 < mappa[p1] << 1)
                    lavoro[p1] = mappa[p1];
            }
        }

        for (int i = 0; i < p; i++)
            destinazione[i] = destinazione[i] + lavoro[i] >> 1;
    }

    public void repaint()
    {
        paint(getGraphics());
    }

    public void paint(Graphics g)
    {
        if (buffer != null)
            g.drawImage(buffer, 0, 0, null);
    }

    private void notificaErrore(String s)
    {
        showStatus(s);
    }
}

Cercate di non fare il classico copia e incolla, questo articolo è scritto a solo scopo didattico, questa applet la trovate ovunque in Internet, cercate piuttosto di capire i singoli passaggi e poi cercate di riproporre lo stesso risultato anche con vostre implementazioni. All’interno del metodo init, che tutte le applet hanno come funzione di avvio, creiamo il modello di rappresentazione dei colori con il metodo createColorModel, questo dovrà essere applicato ad ogni fotogramma. Ciascun pixel verrà infuocato solo se la media delle sue componenti RGB supera il valore di 128, variando questo parametro potete ottenere aree infuocate più o meno vaste. Sicuramente la funzione più complicata è brucia, che passa in rassegna i dati conservati in lavoro, per ogni pixel vengono valutati i punti adiacenti, il colore delle aree più calde viene intensificato. A calcolo concluso tutto viene trasferito su destinazione, utilizzato per la generazione dell’immagine definitiva.
Per visualizzare l’applet in azione avremo bisogno anche di una pagina web il cui codice è:

<html>
<head><title>Fuoco!</title></head>
<body bgcolor="#000000">
<div align="center">
   <applet code="Fuoco.class" width="300" height="200">
      <param name="image" value="Fuoco.gif">
   </applet>
</div>
</body>
</html>

Dopo aver compilato l’applet e creato la pagina web, inserite un’immagine, nello stesso percorso, con una scritta chiara su fondo scuro e poi aprite la pagina web, avrete così il risultato che vi ho mostrato in precedenza.

Informazioni su Giampaolo Rossi

Sviluppatore di software gestionale da oltre 28 anni.
Questa voce è stata pubblicata in Grafica, Java e contrassegnata con . Contrassegna il permalink.