
// ImagesTests.java
// Andrew Davison, April 2005, ad@fivedots.coe.psu.ac.th

/* Zobrazí obrazovku plnou obrázků a předvede nejrůznější animační efekty.

   Obrázky se načítají pomocí objektu typu ImagesLoader, jsou tedy
   definovány jako 'o'-, 'n'-, 's'- nebo 'g'- obrázky.

   Jediný obrázek ('o'-obrázek) může mít na sobě aplikované nejrůznější 
   speciální efekty. Provádí se to tak, že programátor ručně upraví 
   metodu paintComponent(). Program bohužel nemá žádné pěknoučké GUI ;-)

   Speciální efekty se aplikují během intervalu několika 'tiknutí'
   časovače panelu a pak se opakují. Metody efektů:

      * resizingImage():   obrázek roste,
      * flippingImage():   převrací obrázek horizontálně a vertikálně,
      * fadingImage():     obrázek se plynule vytrácí,
      * rotatingImage():   otáčí obrázkem ve směru hodinových ručiček,
      * blurringImage():   rozmazává obrázek,
      * reddenImage():     způsobí zčervenání obrázku,
      * brighteningImage():  obrázek se stává světlejším,
      * negatingImage():   přepíná mezi původním obrázkem a jeho negativem,
      * mixedImage();      míchá barvy obrázku,
      * teleportImage():   obrázek se vytrácí pixel za pixelem,
      * zapImage():        změní obrázek ve směsici žlutých a červenýc pixelů.

   ------
   'n'-, 's'- a 'g'- obrázky se animují cyklickým zobrazováním jejich
   obrázkových kompontent.
   'n'- a 's'- obrázky používají k tomuto účelu objekt typu ImagesPlayer.

   'g'-obrázek je animován pomocí počítadla a metody updateFighter() této třídy. 

   Pomocí časovače knihovny Swing se každých PERIOD milisekund spouští 
   aktualizace a překreslení obrázků.

   Některé obrázky ('cats', 'kaboom', 'cars' a 'fighter') pocházejí z
   knihovny SpriteLib Ariho Feldmana umístěné na adrese
   http://www.arifeldman.com/games/spritelib.html

   Obrázky basn6a08.png a basn6a16.png pocházejí z balíku PNG Suite Willema van
   Schaika z adresy http://www.schaik.com/pngsuite/pngsuite.html
*/

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;
import javax.imageio.*;
import java.io.*;
import java.text.DecimalFormat;


public class ImagesTests extends JPanel
                           implements ActionListener,
                                      ImagesPlayerWatcher 
{
  private final static String IMS_FILE = "imsInfo.txt";
    /* Soubor uchovávající údaje o 'o'-, 'n'-, 's'- a 'g'- obrázcích,
       které extrahujeme pomocí třídy ImagesLoader. */

  private static final int PERIOD = 100;    // 0,1 sekund
      /* Každých PERIOD milisekund se spouští časovač knihovny Swing
         vykreslí obrázek. */

  private static final int PWIDTH = 850;     // velikost tohoto panelu
  private static final int PHEIGHT = 400;


  private ImagesLoader imsLoader;   // objekt pro načítání obrázků
  private int counter;
  private boolean justStarted;
  private ImageSFXs imageSfx;    // třída pro speciální efekty

  private GraphicsDevice gd;   // pro výpis využití paměti
  private int accelMemory;   
  private DecimalFormat df;

  // jednoduché o-obrázky
  private BufferedImage atomic, balls, bee, cheese, eyeChart,
                        house, pumpkin, scooter,
                        fighter, ufo, owl, basn8, basn16;

  // pro manipulasi s n- a s-obrázky
  private ImagesPlayer numbersPlayer, figurePlayer, carsPlayer, 
                       catsPlayer, kaboomPlayer;

  // dočasné objekty typu BufferedImage se používají pro efekty 'teleport' a 'zapping'
  private BufferedImage teleImage = null;
  private BufferedImage zapImage = null;


  public ImagesTests()
  {
    df = new DecimalFormat("0.0");  // 1 dm

    GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
    gd = ge.getDefaultScreenDevice();

    accelMemory = gd.getAvailableAcceleratedMemory();  // v bajtech
    System.out.println("Počáteční akcelerovaná paměť: " + 
                   df.format( ((double)accelMemory)/(1024*1024) ) + " MB" );

    setBackground(Color.white);
    setPreferredSize( new Dimension(PWIDTH, PHEIGHT) );

    // načti a inicializuj obrázky
    imsLoader = new ImagesLoader(IMS_FILE); 
    imageSfx = new ImageSFXs();
    initImages();

    counter = 0;
    justStarted = true;

    new Timer(PERIOD, this).start();    // spusť časovač knihovny Swing
  } // konec metody: ImagesTests()


  private void initImages()
  {
    // inicializace proměnných reprezentujícich o-obrázky
    atomic = imsLoader.getImage("atomic");
    balls = imsLoader.getImage("balls");
    bee = imsLoader.getImage("bee");
    cheese = imsLoader.getImage("cheese");
    eyeChart = imsLoader.getImage("eyeChart");
    house = imsLoader.getImage("house");
    pumpkin = imsLoader.getImage("pumpkin");
    scooter = imsLoader.getImage("scooter");
    ufo = imsLoader.getImage("ufo");
    owl = imsLoader.getImage("owl");
    basn8 = imsLoader.getImage("basn6a08");
    basn16 = imsLoader.getImage("basn6a16");

    /* Inicializuj objekt typu ImagesPlayer pro n- a s-obrázky.
      Sekvence 'numbers' se nepřehrává cyklicky, ostatní ano.
    */
    numbersPlayer = 
         new ImagesPlayer("numbers", PERIOD, 1, false, imsLoader);
    numbersPlayer.setWatcher(this);     // konec sekvence ohlaš zde

    figurePlayer = 
         new ImagesPlayer("figure", PERIOD, 2, true, imsLoader);
    carsPlayer =
         new ImagesPlayer("cars", PERIOD, 1, true, imsLoader);
    catsPlayer =
         new ImagesPlayer("cats", PERIOD, 0.5, true, imsLoader);
    kaboomPlayer = 
         new ImagesPlayer("kaboom", PERIOD, 1.5, true, imsLoader);

    // g-obrázek připravíme pomocí prefixu jmén souborů
    fighter = imsLoader.getImage("fighter", "left");
  }  // konec metody: initImages()


  public void sequenceEnded(String imageName)
  // voláno instancí třídy ImagesPlayer při dokončení sekvence obrázků
  {  System.out.println( imageName + " sekvence skončila");  }



  public void actionPerformed(ActionEvent e)
  // spuštěno časovačem: aktualizuj, překresli
  { 
    if (justStarted)   // neaktualizuj při prvním průchodu
      justStarted = false;
    else
      imagesUpdate();

    repaint();  
  } // konec metody: actionPerformed()



  private void imagesUpdate()
  { 
    // očíslované obrázky (n-obrázky); používáme objekt typu ImagesPlayer
    numbersPlayer.updateTick();
    if (counter%30 == 0)     // periodicky restartuj sekvenci obrázků
      numbersPlayer.restartAt(2);

    figurePlayer.updateTick();

    // skupinové obrázky (g-obrázky)i
    carsPlayer.updateTick();
    catsPlayer.updateTick(); 
    kaboomPlayer.updateTick();

    // skupinové obrázky ('g' images)
    // Obrázky 'fighter' jsou jedinými skupinovými obrázky v této ukázkové aplikaci.
    updateFighter();
  } // konec metody: imagesUpdate()



  private void updateFighter()
  /* Infomrace v souboru Images/imsInfo.txt pro obrázek 'fighter' jsou:
        g fighter  left.gif right.gif still.gif up.gif

     Počítadlo převádíme na hodnotu, která prochází přes čtyři prefixy
     jmen souborů: left, right, still, up.

     Obrázky se zobrazují pomocí svých prefixů jmen souborů (ačkoliv
     bychom mohli také použít jejich pozice, čímž bychom využili objekt
     typu ImagesPlayer).
  */
  {
    int posn = counter % 4;  // počet obrázků sekvence fighter
          // mohli bychom použít imsLoader.numImages("fighter")
    switch(posn) {
      case 0:
        fighter = imsLoader.getImage("fighter", "left");
        break;
      case 1:
        fighter = imsLoader.getImage("fighter", "right");
        break;
      case 2:
        fighter = imsLoader.getImage("fighter", "still");
        break;
      case 3:
        fighter = imsLoader.getImage("fighter", "up");
        break;
      default:
        System.out.println("Neznámá skupina sekvence fighter");
        fighter = imsLoader.getImage("fighter", "left");
        break;
    }
  }  // konec metody: updateFighter()



  public void paintComponent(Graphics g)
   /* Speciální efekty se aplikují na jednotlivé obrázky 
      uvnitř metody paintComponent().

      jednotlivé obrázky jsou:
         * atomic  : obrázek typu GIF atomu
         * balls   : obrázek typu  JPEG skupiny barvných míčů
         * bee     : obrázek typu GIF včely
         * cheese  : obrázek typu GIF kousku sýra
         * eyeChart : obrázek typu GIF černého a bílého grafu
         * house   : obrázek typu GIF domu

         * pumpkin : neprůhledný obrázek typu PNG halloweenové dýně

         * scooter : obrázek typu GIF koloběžky
         * ufo     : obrázek typu GIF létajícího talíře

         * owl     : průsvitný obrázek typu PNG sovy
         * basn8   : průsvitný obrázek typu PNG používající 8-bitový alfa kanál
         * basn16  : průsvitný obrázek typu PNG používající 16-bitový alfa kanál
     
      Všechny obrázky typu GIF mají průhledné prvky, kromě obrázku eyeChart.
   */
   {
     super.paintComponent(g);
     Graphics2D g2d = (Graphics2D)g; 

     // použij anti-aliasing
     g2d.setRenderingHint(RenderingHints.KEY_anti-aliasing,
                          RenderingHints.VALUE_ANTIALIAS_ON);

     // hladeší (and pomalejší) transformace obrázku (např. pro změnu velikosti)
     g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                          RenderingHints.VALUE_INTERPOLATION_BILINEAR);

     // vyčisti pozadí
     g2d.setColor(Color.blue);
     g2d.fillRect(0, 0, PWIDTH, PHEIGHT);

     // zobraz aktuální obrázky
     // ------------------ jednotlivé o-obrázky ---------------------

     /* Programátor musí ručně upravit níže uvedený kód, pokud 
        chce vykresli o-obrázky s různými speciálními efekty. */

     // drawImage(g2d, atomic, 10, 25);
     rotatingImage(g2d, atomic, 10, 25);
     // imageSfx.drawBlurredImage(g2d, atomic, 10, 25);

     // drawImage(g2d, balls, 110, 25);
     // blurringImage(g2d, balls, 110, 25);
     // negatingImage(g2d, balls, 110, 25);
     mixedImage(g2d, balls, 110, 25);

     // drawImage(g2d, bee, 210, 25);
     //rotatingImage(g2d, bee, 210, 25);
     teleImage = teleportImage(g2d, bee, teleImage, 210, 25);

     // drawImage(g2d, cheese, 310, 25);
     flippingImage(g2d, cheese, 310, 25);
     // mixedImage(g2d, cheese, 310, 25);

     // drawImage(g2d, eyeChart, 410, 25);
     // červenádenImage(g2d, eyeChart, 410, 25);
     blurringImage(g2d, eyeChart, 410, 25);
     // teleImage = teleportImage(g2d, eyeChart, teleImage, 410, 25);

     // drawImage(g2d, house, 540, 25);
     reddenImage(g2d, house, 540, 25);
     // imageSfx.drawReddenImage(g2d, house, 540, 25, 5.0f);

     // drawImage(g2d, pumpkin, 710, 25);
     // imageSfx.drawBrighterImage(g2d, pumpkin, 710, 25, 2.0f);
     // brighteningImage(g2d, pumpkin, 710, 25);
     zapImage = zapImage(g2d, pumpkin, zapImage, 710, 25);

     // drawImage(g2d, scooter, 10, 160);
     // flippingImage(g2d, scooter, 10, 160);
     // imageSfx.drawNegatedImage(g2d, scooter, 10, 160);
     // blurringImage(g2d, scooter, 10, 160);
     brighteningImage(g2d, scooter, 10, 160);

     // drawImage(g2d, ufo, 110, 140);
     fadingImage(g2d, ufo, 110, 140);
     // zapImage = zapImage(g2d, ufo, zapImage, 110, 140);

     // drawImage(g2d, owl, 450, 250);
     negatingImage(g2d, owl, 450, 250);
     // brighteningImage(g2d, owl, 450, 250);
     // resizingImage(g2d, owl, 450, 250);

     // drawImage(g2d, basn8, 650, 250);
     // resizingImage(g2d, basn8, 650, 250);
     mixedImage(g2d, basn8, 650, 250);

     // drawImage(g2d, basn16, 750, 250);
     resizingImage(g2d, basn16, 750, 250);
     // imageSfx.drawNegatedImage(g2d, basn16, 750, 250);


     /* Následující řádky není třeba upravovat. */

     // --------------- číslované obrázky -------------------

     drawImage(g2d, numbersPlayer.getCurrentImage(), 280, 140);
     drawImage(g2d, figurePlayer.getCurrentImage(), 550, 140);

     // --------------- pásmové obrázky ----------------------

     drawImage(g2d, catsPlayer.getCurrentImage(), 10, 235);
     drawImage(g2d, kaboomPlayer.getCurrentImage(), 150, 250);
     drawImage(g2d, carsPlayer.getCurrentImage(), 250, 250);

     // --------------- skupinové obrázky --------------------

     drawImage(g2d, fighter, 350, 250);

     // -----------
     reportAccelMemory();

     // zvyš počítadlo, modulo 100
     counter = (counter + 1)% 100;    // 0-99 je dostatečně široký rozsah
   } // konec metody: paintComponent()



   private void drawImage(Graphics2D g2d, BufferedImage im, int x, int y)
   /* nakresli obrázek nebo a žlutý box s ??, pokud neexistuje. */
   { 
     if (im == null) {
       // System.out.println("Předaný obrázek má hodnotu null");
       g2d.setColor(Color.yellow);
       g2d.fillRect(x, y, 20, 20);
       g2d.setColor(Color.black);
       g2d.drawString("??", x+10, y+10);
     }
     else
       g2d.drawImage(im, x, y, this);
   } // konec metody: drawImage()



  private void reportAccelMemory()
  // hlas jakoukoli změnu v množství dostupné paměti VRAM
  {
    int mem = gd.getAvailableAcceleratedMemory();   // v bajtech
    int memChange = mem - accelMemory;

    if (memChange != 0) 
      System.out.println(counter + ". akcelerovaná paměť: " + 
                df.format( ((double)accelMemory)/(1024*1024) ) + " MB; Změna: " +
                df.format( ((double)memChange)/1024 ) + " KB");
    accelMemory = mem;
  }  // konec metody: reportAcceleMemory()



  // ----------------- metody, jež používají třídu ImageSFXs ---------------------

  /* Většina zde uvedených metod podporuje animované efekty: efekty,
     jež se během několika tiků časovače (tj. animačních snímků) postupně mění.

     Skutečný efekt (např. mizení) získáme voláním relevantní metody
     třídy ImagesSFXs.

     Animace je spíchnutá prozkoumáním aktální hodnoty počítadla (které
     je při každém tiknutí sníženo modulo 100). Hodnota počítadla se
     používá jistým způsobem tak, aby lehce pozměnila vstupní argumenty
     metod třídy ImageSFXs.
  */


  private void resizingImage(Graphics2D g2d, BufferedImage im, int x, int y)
  /* 
     Postupně mění velikost obrázku, mění jeho měřítko stejně pro obě osy x a y.
     Obrázek zůstává nedotčen, neboť operace zapisuje přímo na obrazovku.

     Použitá metoda třídy ImageSFXs je drawResizedImage(). Tato metoda
     používá místo třídy AffineTransforms metodu drawImage(), takže je rychlejší.
  */
  {
    double sizeChange = (counter%6)/2.0 + 0.5;    // dostaneme 0,5 -- 3
    imageSfx.drawResizedImage(g2d, im, x, y, sizeChange, sizeChange);
  }  // konec metody: resizingImage()



  private void flippingImage(Graphics2D g2d, BufferedImage im, int x, int y)
  /* 
     Převrátí obrázek horizontálně, vertikálně nebo oběma směry.
     Metoda cyklicky prochází tyto možnosti na základě hodntoy počítadla.

     Použitou metodou třídy ImageSFXs je getFlippedImage(). Tato metoda používá 
     místo třídy AffineTransforms metodu drawImage(), takže je rychlejší.
      
     Metoda getFlippedImage() vrací nový objekt typu BufferedImage, v naší
     ukázkové aplikaci jej však globálně neuchováváme.
  */
  {
    BufferedImage flipIm = null;
    if (counter%4 == 0)
      flipIm = im;    // žádné překlápění
    else if (counter%4 == 1)
      flipIm = imageSfx.getFlippedImage(im, ImageSFXs.HORIZONTAL_FLIP);
    else if (counter%4 == 2)
      flipIm = imageSfx.getFlippedImage(im, ImageSFXs.VERTICAL_FLIP);
    else 
      flipIm = imageSfx.getFlippedImage(im, ImageSFXs.DOUBLE_FLIP);

    drawImage(g2d, flipIm, x, y);
  }  // konec metody: flippingImage()



  private void fadingImage(Graphics2D g2d, BufferedImage im, int x, int y)
  /* 
     Způsobí postupné zmizení obrázku v průběhu 25 snímků a poté jeho
     opětovné navrácení. Mizení probíhá plynule přes celý obrázek a ne jen 
     v po jednotlivých pixelech, jako je tomu v případě metody teleportImage()
     (viz níže). Původní obrázek je nedotčen, operace totiž zapisuje přímo na obrazovku.

     Použitou metodou třídy ImageSFXs je drawFadedImage(), která používá operaci
     AlphaComposite. Je-li alpha == 0 bude obrázek neviditelný.
  */
  {
    float alpha = 1.0f - (((counter*4)%100)/100.0f);
    imageSfx.drawFadedImage(g2d, ufo, x, y, alpha);
  }  // konec metody: fadingImage()



  private void rotatingImage(Graphics2D g2d, BufferedImage im, int x, int y)
  /* 
    Otáčí obrázkem v krocích po 10 stupních ve směru hodinových ručiček,
    se středem ve středu obrázku. Otáčí se kopie obrázku.

    Může dojít k ořezání obrázku, pokud kolem sebe nemá dostatečně velký
    prostor.

    Použitá metoda třídy ImageSFXs je getRotatedImage(), která používá
    operace objektu typu AffineTransform.
  */
  {
    int angle = (counter * 10) % 360;
    // System.out.println("šířka/výška obrázku: " + im.getWidth() + ", " + 
    //                                          im.getHeight() );
    BufferedImage rotIm = imageSfx.getRotatedImage(im, angle);
    drawImage(g2d, rotIm, x, y);
  }  // konec metody: rotatingImage()



  private void blurringImage(Graphics2D g2d, BufferedImage im, int x, int y)
  /* 
     Zvyšuje rozmazání obrázku v průběhu intervalu 8 snímků,
     poté začně znovu s původním obrázkem. 
     Původní obrázek je nedotčen, operace totiž zapisuje přímo na obrazovku.

     Žádná alfa-složka nezůstává nedotčena.

     Použitou metodou třídy ImageSFXs je drawBlurredImage(), která
     používá operaci ConvolveOp. Zvýšeného rozmazání dosahujeme zvětšením
     matice operace ConvolveOp. Velikost matice určuje hodnota fadeSize.
  */
  {
    int fadeSize = (counter%8)*2 + 1;    // dává 1,3,5,7,9,11,13,15
    if (fadeSize == 1)
      drawImage(g2d, im, x, y);     // začni znovu s původním obrázkem
    else 
      imageSfx.drawBlurredImage(g2d, im, x, y, fadeSize);
  }  // konec metody: blurringImage()



  private void reddenImage(Graphics2D g2d, BufferedImage im, int x, int y)
  /* 
     Zvýší červenost obrázku po dobu 21 snímků,
     načež začne znovu s původními barvami. 
     Původní obrázek je nedotčen, operace totiž zapisuje přímo na obrazovku.

     Jak se červenost obrázku zvyšuje, množství modré a zelené se snižuje.
     Žádná alfa-složka nezůstává nedotčena.

     Použitou metodou třídy ImageSFXs je drawReddenImage().
     Existují dvě verze: jedna používá operaci LookupOp a druhá RescaleOp.
     Poslední jmenovaná je deaktivovaná komentářem.
     Co se funkce týče není mezi nimi žádný rozdíl.
  */
  {
    float brightness = 1.0f + (((float) counter%21)/10.0f);    
             // dává hodnoty v rozsahu 1.0-3.0, po krocích velikosti 0.1
    if (brightness == 1.0f)
      drawImage(g2d, im, x, y);   // začni znovu s původním obrázkem
    else 
      imageSfx.drawReddenImage(g2d, im, x, y, (float) brightness);
  }  // konec metody: reddenImage()



  private void brighteningImage(Graphics2D g2d, BufferedImage im, int x, int y)
  /* 
     Zvýší světlost obrázku v průběhu 9 snímků, a poté začne znovu s původními
     barvami. 
     Původní obrázek je nedotčen, operace totiž zapisuje přímo na obrazovku.
     Žádná alfa-složka nezůstává nedotčena.

     Použitou metodou třídy ImageSFXs je drawBrighterImage(), která používá
     operaci RescaleOp.
  */
  { int brightness = counter%9;    // dává 0-8
    if (brightness == 0)
      drawImage(g2d, im, x, y);   // začni znovu s původním obrázkem
    else 
      imageSfx.drawBrighterImage(g2d, im, x, y, (float) brightness);
  }  // konec metody: brighteningImage()



  private void negatingImage(Graphics2D g2d, BufferedImage im, int x, int y)
  /* 
     Přepíná mezi původním obrázkem a jeho negativem v závislosti na 
     aktuální hodnotě počítadla.
     Použitou metodou třídy ImageSFXs je drawNegatedImage().
  */
  { if (counter%10 < 5)   // zobraz negativ
      imageSfx.drawNegatedImage(g2d, im, x, y);
    else  // zobraz původní obrázek
      drawImage(g2d, im, x, y);
  }  // konec metody: negatingImage()



  private void mixedImage(Graphics2D g2d, BufferedImage im, int x, int y)
  /* 
     Přepíná mezi původním obrázkem a jeho verzí s pomíchanými barvami 
     v závislosti na aktuální hodnotě počítadla.
     Použitou metodou třídy ImageSFXs je drawMixedColouredImage().
  */
  { if (counter%10 < 5)   // proveď smíchání 
      imageSfx.drawMixedColouredImage(g2d, im, x, y);
    else  // zobraz původní obrázek for a while
      drawImage(g2d, im, x, y);
  }  // konec metody: mixedImage()




  private BufferedImage teleportImage(Graphics2D g2d,
                 BufferedImage im, BufferedImage teleIm, int x, int y)
  /* 
     Efekt s názvem 'teleport' způsobí postupné zmizení obrázku 
     během 7 snímků (po nich se opakuje od začátku).
     Jednotlivé pixely jsou nastaveny na 0, čímž se stanou průhlednými.

     Tento efekt lze srovnávat s plynulejším mizením, jaké nabízí
     metoda fadingIamge (viz výše).

     Změny se aplikují na kopii obrázku (uložné v globální proměnné teleImage).
     Kopie má přiřazený alfa kanál, pokud jej původní obrázek neměl,
     aby se zajistilo, že se obrázek skutečně stane průhledným (a ne černým).

     Použitou metodou třídy ImageSFXs je eraseImageParts().
     Její druhý argument říká, že ovlivněné pixely
     jsou umístěny v poli pixelů obrázku na pozicích, které jsou násobkem
     zadaného čísla.
  */
  {
    if (teleIm == null) {    // spusť efekt
      if (imageSfx.hasAlpha(im))
        teleIm = imageSfx.copyImage(im); 
      else   // žádný alfa kanál
        teleIm = imageSfx.makeTransImage(im);  // přiděl kopii alfa kanál channel
    }

    int eraseSteps = counter%7;    // rozsah je 0 až 6
    switch(eraseSteps) {
      case 0:      // respusť efekt
        if (imageSfx.hasAlpha(im))
          teleIm = imageSfx.copyImage(im); 
        else  // neprůsvitné
          teleIm = imageSfx.makeTransImage(im); 
        break;
      case 1:
        imageSfx.eraseImageParts(teleIm, 11);  // každý 11. pixel
        break;
      case 2:
        imageSfx.eraseImageParts(teleIm, 7);  // každý 7. pixel
        break;
      case 3:
        imageSfx.eraseImageParts(teleIm, 5);  // 5.
        break;
      case 4:
        imageSfx.eraseImageParts(teleIm, 3);  // 3.
        break;
      case 5:
        imageSfx.eraseImageParts(teleIm, 2);  // 2. pixel
        break;
      case 6:
        imageSfx.eraseImageParts(teleIm, 1);  
        break;               // každý pixel, tj. úplné vymazání
      default:
        System.out.println("Pro teleport neznámá hodnota počítadla");
        break;
    } // konec: switch

    drawImage(g2d, teleIm, x, y);
    return teleIm;
  }  // konec metody: teleportImage()



  private BufferedImage zapImage(Graphics2D g2d, BufferedImage im, 
                              BufferedImage zapIm, int x, int y)
  /* 
     'zap' znamená postupnou změnu viditelných částí obrázku na 
     náhodnou směs červených a žlutých pixelů. Množství změn se v průběhu
     11 snímků zvyšuje.

     Změny se aplikují na kopii obrázku (uloženou v globální proměnné zapImage).
     Po 11 snímcích je obrázek obnoven do původní podoby a začne se znovu.

     Míra aplikace efektu je ovládána hodnotou pravděpodonosti, která
     roste od 0 do 1.

     Použitou metodou třídy ImageSFXs je zapImageParts()
  */
  {  if ((zapIm == null) || (counter%11 == 0))
      zapIm = imageSfx.copyImage(im);    // respusť efekt
    else {
      double likelihood = (counter%11)/10.0;    // dá rozsah 0 až 1
      imageSfx.zapImageParts(zapIm, likelihood);
    }
    drawImage(g2d, zapIm, x, y);
    return zapIm;
  }  // konec metody: zapImage()



   // --------------------------------------------

   public static void main(String args[])
   {
     // ve Windows zapne akceleraci průsvitnosti
    System.setProperty("sun.java2d.translaccel", "true"); 
    // System.setProperty("sun.java2d.ddforcevram", "true"); 

     // používáme-li OpenGL s p-buffery, zapne hardwarovou akceleraci
     // System.setProperty("sun.java2d.opengl", "true"); 

     ImagesTests ttPanel = new ImagesTests();

     // vytvoř okno JFrame pro testovací panel JPanel
     JFrame app = new JFrame("Image Tests");
     app.getContentPane().add(ttPanel, BorderLayout.CENTER);
     app.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

     app.pack();
     app.setResizable(false);  
     app.setVisible(true);
  } // konec metody: main()

} // konec třídy: ImagesTests
