Tuesday, November 9, 2010


Old House by Yves




















Not my house, but nice composition anyway.

BufferedImage transparency

BufferedImage class is a very useful tool for certain animations. Not only you could double buffer with these images, but you could organise a complicated animated scene of many layered BufferedImages on top of each other. In my Sudoku program, when mouse is over a cell it gets highlighted in yellow like so:
I could think of 2 ways to achieve this effect: 
1. clear the whole image -> paint yellow square in its position -> paint table and digits
2. paint yellow square over table -> paint digit over it -> clear cell when mouse is relocated and paint digit again
However, the third easier and more efficient, in my opinion, way that I used is use BufferedImage. The first Buffered image contains the table and digits; the second image contains the highlight of the cell with the mouse over it. In doing this, all I need to do is print the table and digits once, then paint a yellow square on the second BufferedImage, under the current cell. If mouse is moved I can easily clear the whole second image with the yellow square on it without affecting the sudoku table. Here is how to do it:

/* This is still in my ...extends JPanel class from the last tutorial*/
private BufferedImage img, backImg; //these are the two images
Graphics2D g, gBack;           //and these graphics objects will be used to paint on the two images
/...
/*in Constructor among other initializations*/
        //initialize all buffered images used for the drawing of sudoku and the graphics obj connected to them
        img = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.BITMASK);
        backImg = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.BITMASK);
        g = (Graphics2D) img.getGraphics();  //now g can be used to paint on the img BufferedImage
        gBack = (Graphics2D) backImg.getGraphics();

So up to this point we have 2 transparent BufferedImages with the size of the current container. 
ATTENTION! The container's size has to be already set and I even said setVisible(true); before initializing the images. If the container's size is still 0 then the images won't initialize properly.
BufferedImage.BITMASK type is essential for this problem, since it makes the images transparent.
Now in a method that I do not want to bother you with, I paint the grid on the img object. With Graphics2D you can do things like:

g.setStroke(new BasicStroke(thickStroke)); /*to set the width of painted lines where thickStroke is an integer indicating stroke in pixels*/
g.drawLine(x, inset, x, y); // to draw a line with the stroke specified
g.setColor(Color.BLACK); //sets the colour to use in painting 
, etc.

Going to my highlight method I have:

    /**
     * Highlights a cell on the graphics component provided.
     * @param row
     * @param column
     * @param g
     * @param highlight If set to null, this method simply makes everything transparent.
     */
    private void highlightCell(int row, int column, Graphics2D g, Color highlight) {

        Composite c = g.getComposite(); //save original composite
        g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR)); //set to draw transparent
        g.fillRect(0, 0, getWidth(), getHeight()); //clear everything with transparent rectangle
        //determine x and y coordinates of upper-right corner of inticated cell
        int highlightX = inset + (column * squareWidth);
        int highlightY = inset + (row * squareWidth);

        //draw a square at the specific cell
        g.setComposite(c); //reset the composite to normal drawing
        if (highlight != null) {
            g.setColor(highlight);
            g.fillRect(highlightX, highlightY, squareWidth, squareWidth);
        }
        repaint();
    }
}

Since I am not very familiar with Composite class, I decided to backup the normal Composite used before I change it into transparent drawing mode by the second line of code in the method. When I call the method, I pass gBack as the second parameter in order to paint the highlight on the backImg object. The method is easy to understand and in the end you can see the repaint(); command. This command calls the paintComponent(Graphics g) method of JPanel class. Here is how I've overridden it:
    @Override
    public void paintComponent(Graphics g) {

        super.paintComponent(g);
        //paint from bottom to top backImg and the sudoku itself on top
        if (backImg!= null) {
            g.drawImage(backImg, 0, 0, this);
        }
        if (img != null) {
            g.drawImage(img, 0, 0, this);
        }
    }

As you should already know, everything that gets painted on the JPanel has to be painted here in this method. super.paintComponent(g); is there to clean the last things painted on the JPanel. After that I paint the new things I would like to see on my JPanel which is the highlight on backImg and on top of it the sudoku table on img. 
Notice that in the case of my sudoku application, in the highlight method, I could simply clear the BufferedImage with the background colour (white) instead of doing all the Composite stuff to clear it with transparency. I did it because in my real application I actually have a third BufferedImage under the other 2 so I need to paint with transparency in order to see it. Besides you would need to know this in order to use this technique efficiently. It could be used for any animations where you have something in front that needs to be independent from something on the back that is still visible. 

Good luck and comment if you have any recommendations, ideas or questions.

Friday, November 5, 2010

 

Montreal again, this is the first time I get a decent result from merging photos in HDR image. The problem is that my camera is quite old, so the bracketing setting is pathetic. Still, it's a nice shot.

MouseListener, MouseMotionListener & KeyListener

For a Sudoku game I am currently working on, I decided that I want the user to click on a cell that I have drawn on a JPanel and enter a digit using the keyboard. This is an Event Driven Programming challenge. To solve the problem I decided to use a MouseListener in order to capture the click event, a MouseMotionListener to locate the mouse pointer and a KeyListener for the digit input by pressing a key on the keyboard.

Lets start with the MouseMotionListener class. It is an inner class for the public class ClassSDK extends JPanel {} class.
private class MouseTracker implements MouseMotionListener {

        public void mouseDragged(MouseEvent e) {/*do nothing*/

        }

        public void mouseMoved(MouseEvent e) {

            //check if mouse is over the sudoku table
            if (e.getX() > inset && e.getX() < (inset + (9 * squareWidth))
                    //mouse in horizontal range; check vertical
                    && e.getY() > inset && e.getY() < (inset + (9 * squareWidth))) {
                //in sudoku table...
                //convert mouse coordinates to cell coordinates
                mouseCol = (e.getX() - inset) / squareWidth;
                mouseRow = (e.getY() - inset) / squareWidth;

                highlightCell(mouseRow, mouseCol, gBack, Color.ORANGE);

            } else {
                //set mouseCol and mouseRow to -1 identifying mouse is out of table
                mouseRow = -1;
                mouseCol = -1;
                //clear the backImg highlight effect
                highlightCell(0, 0, gBack, null);
            }
        }
    }
So in this class I am tracking the mouse over the JPanel in which I have drawn the sudoku table. Once the mouse pointer is above the sudoku table (insets is a variable that contains the size of my empty border around the table) I start converting the coordinates of the pointer to coordinates of a cell in the sudoku table. In case you don't know, a sudoku table consists of 9 rows and 9 columns. highlightCell() is a method which can either paint a specified cell in certain colour, or clear it from the colour. I will not describe how it works now though. It will be a part of my BufferedImages use blog. You will see where I initialize and add this listener to the ClassSDK JPanel in my MouseListener class:

private class ClickListen implements MouseListener {

        public void mouseClicked(MouseEvent e) {/*do nothing*/

        }

        public void mousePressed(MouseEvent e) {/*do nothing*/

        }

        public void mouseReleased(MouseEvent e) {

            //check if click was inside the sudoku
            if (mouseRow == -1 || mouseCol == -1) {
                return; //click outside sudoku table
            }
            /*check if there is an entry process already initiated (flag -1 on lockRow and lockCol means no entry process)*/
            if (lockRow != -1 || lockCol != -1) {
                //if so unload the input listener for that process
                KeyListener[] l = getKeyListeners();
                if (l.length > 0) { //if there was a key listener which should be the case
                    removeKeyListener(l[0]); //remove it
                    System.out.println("Removed previous input listener.");
                }

            }
            //ready to start input method
            createSDK();
        }

        public void mouseEntered(MouseEvent e) {

            //start tracking the mouse once entered in the component
            addMouseMotionListener(new MouseTracker());
            requestFocus(); //automatically focust the panel with the mouse over it
        }

        public void mouseExited(MouseEvent e) {

            //remove motion listener if mouse is out of panel to save ressources
            MouseMotionListener[] l = getMouseMotionListeners();
            removeMouseMotionListener(l[0]);
        }
    }
The createSDK() method is a method which controls the input together with my KeyListener class. The part where I check the variables lockRow and lockCol for a flag of -1 is that, if the user clicks on a cell and starts the entry process but does not enter a value and clicks on another cell instead, the keylistener that listens for entry in the previous cell has to be removed, else with one click of a button the value would be entered in both cells. lockRow and lockCol save the coordinates of the cell where the click occurred until the point when a value is entered and the entry process finishes, when they switch value back to -1.
    /**
     * Called by createSDK, this class waits for a digit to be entered and then
     * saves it to the mainG array, paints it on the grid.
     */
    private class InputListen implements KeyListener {

        public void keyTyped(KeyEvent e) {

            System.out.println("key typed");
            if (lockRow == -1 || lockCol == -1) //there is no lock on any cell
            {
                return;
            }

            char input = e.getKeyChar();
            //check if input is a digit
            if (Character.isDigit(input)) {
                //save input to sudoku array and
                //show input on the sudoku table
                addDigitToMainG(lockRow, lockCol, Integer.parseInt(Character.toString(input)));

                //clear visual highlight of cell and reset lock variables
                lockCol = -1;
                lockRow = -1;
                highlightCell(0, 0, gLock, null);
                //remove the key listener
                KeyListener[] l = getKeyListeners();
                removeKeyListener(l[0]);
            }
        }

        public void keyPressed(KeyEvent e) {/*do nothing*/

        }

        public void keyReleased(KeyEvent e) {/*do nothing*/

        }
    }  
 This is how the key listener works. It unloads itself once the process has finished. addDigitToMainG(int,int,int) is a method that adds a number to specific row and column in my array and then prints it on the screen as well. 

It's a lot of code and explanation and I try to be detailed in my comments, so if you have any questions please comment. This is a good exercise on event driven programming combining 3 different types of listeners.

Wednesday, November 3, 2010

Montreal Sunset by Yves
Off topic:
Taking photographs is kind of a hobby that I have.

I find this photo particularly good as the yellow illumination of the sunset on the yellow trees produced an incredibly bright colour.

I had to react fast as I saw the train approaching in order to capture it too. I will publish photos as well as my Java experiences, since I find both of them very interesting.

Tuesday, November 2, 2010

Apple to stop supporting Java? It is a pitty that we have to suffer because of the self-centered decisions dictated by a company that has a temporary pinnacle of global power. How could it be for the better? Well, I guess we'll have to study objective C guys :) . Here check CNET's article:


Add an image background to a JFrame

This semester we started studying GUI and the first thing I wanted to know is how to make these dull JFrame windows look better. Here is a simple class that I created to change the JFrame's background to any image. It's a good idea to make it a package since you can use it with any JFrame.

/**
 * A panel with the background image on it.
 * @author Yves
 */
class Background extends JPanel {

    private Image img;

    public Background(String path) {

        this(new ImageIcon(path).getImage());  //this loads the image from a file (path) to an Image object
    }
    public Background(Image img) {

        this.img = img;
       
        Dimension size = new Dimension(img.getWidth(null), img.getHeight(null));
        setPreferredSize(size);
        setSize(size);
        setVisible(true);
        System.out.println("background constructor done");
    }

    @Override
    public void paintComponent(Graphics g) {

        System.out.println("background painted");
        g.drawImage(img, 0, 0, this); //draw the image on the JPanel
    }
}

The idea is that this class represents a JPanel and it loads an image from an image file that you specify via the constructor. Afterwards, it sets the size of the JPanel equal to the size of the image and paints it on the JPanel.

Now to use this JPanel as the background for a JFrame all you have to do is:
 (Lets say this is a class that extends JFrame)
1. final String BACKGROUND_IMAGE = ".\\img\\rough_edge.png";  /*determine the path to the image file*/
pay attention to the double slashes, since a single slash indicates an escape code e.g. \n \t \b
the image is in my project folder in a folder called img, if you run the application from a jar file the img folder has to be in the same folder as the jar

2. initialize a Background object
private Background bg; //a field for the background object
/* in the constructor*/
bg = new Background(BACKGROUND_IMAGE); //initialize in constructor

3. set the JFrame's ContentPane to the JPanel with the background on it. ContentPane is the layer of the JFrame where your components (Buttons, TextFields, etc.) are located.
this.setContentPane(bg);

4. do not forget to set the layout manager for your JFrame AFTER you change the content pane to the Background object, since it changes to the layout set in the Background class, which I believe is FlowLayout (default layout for JPanel)
e.g.  this.setLayout(new FlowLayout(FlowLayout.CENTER,2,0)); //set a layout for the new content pane

Another small thing, if you want to simply change the background color of a JFrame and it doesn't work, try calling the content pane of the JFrame: this.getContentPane().setColor(Color col);