E:\Src\java\netBeans\Mandelbrot\src\mandelbrot\Mandelbrot.java
package mandelbrot;

import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Calendar;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;

/**
 *
 * @author Rolf
 */
public class Mandelbrot extends JPanel implements MouseListener, KeyListener, MouseWheelListener {

    static int width = 800;             // pixel size of our image
    static int height = 600;            // pixel size of our image
    double centreX = 0;                 // center of our image
    double centreY = 0;                 // center of our image
    double dx = 4;                      // real size betwenn max,min of X
    int max_iteration = 50;
    int printScale = 5;                 // x time larger than screen for printing
    int mouse_X = 0;
    int mouse_Y = 0;
    boolean busy = false;
    BufferedImage img = null;
    JProgressBar progressBar;
    int regOffset = 0;

    public Mandelbrot(JFrame frame) {
        // constructor: Add mouse Listener
        addMouseListener(this);
        frame.addKeyListener(this);
        addMouseWheelListener(this);

        progressBar = new JProgressBar(0, width);
        progressBar.setValue(0);
        progressBar.setStringPainted(true);
        add(progressBar);
        img = createMyImage(width, height, max_iteration, 1);

    }

    private void saveFile(BufferedImage img) {
        Calendar now = Calendar.getInstance();
        File outputfile = new File("c:\\temp\\MandelBrot " + 
                now.get(Calendar.YEAR) +
                "." + now.get(Calendar.MONTH) + 
                "." + now.get(Calendar.DAY_OF_MONTH) + 
                " " + now.get(Calendar.HOUR_OF_DAY) + 
                "_" + now.get(Calendar.MINUTE) + ".png");
        System.out.println("Start writing to file: " + outputfile.getAbsolutePath());
        try {
            //ImageIO.write(img, "jpg", outputfile);
            BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(outputfile));
            ImageIO.write(img, "png", outputStream);
            outputStream.close();

        } catch (IOException ex) {
            Logger.getLogger(Mandelbrot.class.getName()).log(Level.SEVERE, null, ex);
        }

        System.out.println(" created");

    }

    public void paint(Graphics g) {

        // try to get real point dimensions
        //Rectangle r = g.getClipBounds();
        if (img != null) {
            g.drawImage(img, 0, 0, this);
        }
    }

    private double getMinCx() {
        return centreX - dx / 2.0;

    }

    private double getMaxCx() {
        return centreX + dx / 2.0;
    }

    private double getMinCy() {
        return centreY - dx / 2.0 * (double) height / (double) width;
    }

    private double getMaxCy() {
        return centreY + dx / 2.0 * (double) height / (double) width;
    }

    private int getPixW(double p, int width, int height) {
        // get X Pixel for Point p, where real dimension are between min and max
        return (int) (width * (p - getMinCx()) / (getMaxCx() - getMinCx()));
    }

    private int getPixH(double p, int width, int height) {
        // get Y Pixel for Point p, where real dimension are between min and max
        return (int) (height * (p - getMinCy()) / (getMaxCy() - getMinCy()));
    }

    private double getResolution(int width) {
        return (getMaxCx() - getMinCx()) / width;
    }

    private BufferedImage createMyImage(int width, int height, int max_iteration, int zoom) {
        BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        double max_betrags_quadrat = 4;
        double point_dx = getResolution(width);

        progressBar.setMaximum(width);

        for (int pix_x = 0; pix_x < width; pix_x++) {
            progressBar.setValue(pix_x);

            double cx = getMinCx() + pix_x * point_dx;
            for (int pix_y = 0; pix_y < height; pix_y++) {
                double cy = getMinCy() + pix_y * point_dx;
                int iteration = punkt_iteration(cx, cy, max_betrags_quadrat, max_iteration);

                int rgb = set_rgb(iteration, max_iteration);
                bufferedImage.setRGB(pix_x, pix_y, rgb);
            }
        }

        // axis
        Graphics g = bufferedImage.getGraphics();
        g.setColor(Color.WHITE);
        g.setFont(new Font("TimesRoman", Font.PLAIN, 12 * zoom));

        //g.drawString("Z[n+1] = Z²[n] + c;  Z[0]=0;  c=complex;  n=0,1,2,...;  |Z[n]|>=2", 24 * zoom, 24 * zoom);
        g.drawString("Z[n+1] = Z²[n] + c;  Z[0]=0;  c=complex;  n=0,1,2,...;  |Z[n]|>=2", 32 * zoom, 32 * zoom);
        g.drawString("center: " + (getMinCx() + width / 2 * point_dx) + " " + 
                (-(getMinCy() + height / 2 * point_dx)) + "j", 32 * zoom, 55 * zoom);

        // axis
        int y = getPixH(0, width, height);
        g.drawLine(0, y, width, y);
        int x = getPixW(0, width, height);
        g.drawLine(x, 0, x, height);
        progressBar.setValue(width);

        //System.out.println("Ready x=" + minCx + "/" + maxCx + "    y=" + minCy + "/" + maxCy);
        return bufferedImage;
    }

    private int get_rgb(int Wert) {
        // Regenbogen Farbe
        /*
         * es gibt 3*2 Phasen
         *
         * 1.2. Phase: Grün steigt von 0..255 und dann wieder auf 0
         *
         * 3.4. Phase: Blau steigt und fällt
         *
         * 5.6. Phase: Rot steigt und fällt
         */

        Wert = Wert * 4;
        Wert = Wert % 254;
        int Red, Green, Blue = 0;
        int Phase = Wert / 51;
        int Level = Wert % 51;

        switch (Phase) {
            case 0:
                Red = 255;
                Green = Level * 5;
                Blue = 0;
                break;

            case 1:
                Red = 255 - Level * 5;
                Green = 255;
                Blue = 0;
                break;

            case 2:
                Red = 0;
                Green = 255;
                Blue = Level * 5;
                break;

            case 3:
                Red = 0;
                Green = 255 - Level * 5;
                Blue = 255;
                break;

            case 4:
                Red = Level * 5;
                Green = 0;
                Blue = 255;
                break;

            default:
                Red = Green = Blue = 255;
        }
        return Red * 256 * 256 + Green * 256 + Blue;
    }

    private int set_rgb(int iteration, int max_iteration) {
        if (iteration == max_iteration) {
            return Color.BLACK.getRGB();
        }

        return get_rgb(regOffset + iteration);
        //return iteration * 0x18;
    }

    private int punkt_iteration(double cx, double cy, double max_betrag_quadrat, int max_iter) {

        double betrag_quadrat = 0;
        int iter = 0;
        double x = 0;
        double y = 0;

        while (betrag_quadrat <= max_betrag_quadrat && iter < max_iter) {
            double xt = x * x - y * y + cx;
            double yt = 2 * x * y + cy;
            x = xt;
            y = yt;
            iter++;
            betrag_quadrat = x * x + y * y;
        }

        return iter;
    }

    //--------------------------------------------------------------------------
    // mouse Handler
    //--------------------------------------------------------------------------
    @Override
    public void mouseClicked(MouseEvent e) {

        if (!busy) {
            busy = true;
            mouse_X = e.getX();
            mouse_Y = e.getY();
            double zoom = 2;

            switch (e.getModifiers()) {
                case InputEvent.BUTTON1_MASK: {

                    new Thread(new Runnable() {

                        @Override
                        public void run() {
                            img = createMyImage(width, height, max_iteration, 1);
                            repaint();
                            busy = false;
                        }
                    }).start();
                    break;
                }
                case InputEvent.BUTTON2_MASK: {
                    zoom = 1.0;
                    mouse_X = width / 2;
                    mouse_Y = height / 2;
                    new Thread(new Runnable() {

                        @Override
                        public void run() {
                            saveFile(createMyImage(width * printScale, height * printScale, max_iteration, printScale));
                            busy = false;
                        }
                    }).start();

                    break;
                }
                case InputEvent.BUTTON3_MASK: {
                    zoom = 1 / zoom;
                    new Thread(new Runnable() {

                        @Override
                        public void run() {
                            img = createMyImage(width, height, max_iteration, 1);
                            repaint();
                            busy = false;
                        }
                    }).start();
                    break;
                }
            }

            centreX = getMinCx() + (dx * (double) (mouse_X) / (double) width);

            double dy = (getMaxCy() - getMinCy());
            centreY = getMinCy() + (dy * (double) (mouse_Y) / (double) height);
            dx = dx / zoom;
        }

    }

    @Override
    public void mousePressed(MouseEvent e) {
    }

    @Override
    public void mouseReleased(MouseEvent e) {
    }

    @Override
    public void mouseEntered(MouseEvent e) {
    }

    @Override
    public void mouseExited(MouseEvent e) {
        // throw new UnsupportedOperationException("Not supported yet.");
    }

    //--------------------------------------------------------------------------
    // Key Handler
    //--------------------------------------------------------------------------
    @Override
    public void keyTyped(KeyEvent e) {
        //throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public void keyPressed(KeyEvent e) {
        boolean repaint = true;
        switch (e.getKeyChar()) {
            case 'u':
                max_iteration = max_iteration * 2;
                break;
            case 'd':
                max_iteration = max_iteration / 2;
                break;
            default:
                System.out.println("not supported: " + e.getKeyChar());
                repaint = false;
        }

        if (!busy && repaint) {
            busy = true;
            new Thread(new Runnable() {

                @Override
                public void run() {
                    img = createMyImage(width, height, max_iteration, 1);
                    repaint();
                    busy = false;
                }
            }).start();
        }

    }

    @Override
    public void keyReleased(KeyEvent e) {
        //throw new UnsupportedOperationException("Not supported yet.");
    }
    //--------------------------------------------------------------------------
    //  MouseWheel
    //--------------------------------------------------------------------------

    @Override
    public void mouseWheelMoved(MouseWheelEvent e) {
        //throw new UnsupportedOperationException("Not supported yet.");
        if (!busy) {
            busy = true;
            int n = e.getUnitsToScroll();
            regOffset += n;
            regOffset = (regOffset + 256) % 256;

            new Thread(new Runnable() {

                @Override
                public void run() {
                    img = createMyImage(width, height, max_iteration, 1);
                    repaint();
                    busy = false;
                }
            }).start();

        }

    }

    //--------------------------------------------------------------------------
    //  Main
    //--------------------------------------------------------------------------
    public static void main(String[] args) {
        //1. Create the frame.
        JFrame frame = new JFrame("Mandelbrot");
        //2. Optional: What happens when the frame closes?
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //4. Size the frame.

        frame.setSize(width + 8, height + 27);
        // frame.pack();

        frame.getContentPane().add(new Mandelbrot(frame));

        //5. Show it.
        frame.setVisible(true);

        System.out.println("--------------------------------");
        System.out.println("Welcome to Mandelbrot 2014-11-17");
        System.out.println("--------------------------------");
        System.out.println("use mouse to rebuild picture");
        System.out.println("- left button: zoom in");
        System.out.println("- right button: zoom out");
        System.out.println("- middle button: save as file");
        System.out.println("- scroll wheel to change color\n");
        System.out.println("u key to increase max iteration");
        System.out.println("d key to decrease max iteration");

    }
}