Search code examples
mergebufferedimagegraphics2doverlapping

How to mask a grid BufferedImage displayed on top of a background BufferedImage


First question here, quite new at java (and english not my native language), please be indulgent :) Didn't find any similar issue.

I am trying to make a 2D game (turn by turn, so no real time issue). My map is displayed in a JPanel with a mix of images for background, grid, and movable objects. All images are loaded and stored once before displayed. I have a BufferedImage for the background, an other one on which I draw the grid, and lots of images for other objects. In the paintComponent(), I draw all BufferedImages on the Graphics2D (cast from the Graphics parameter). My issue is to mask the grid when the player choose to (or when scale is too big, with variables "ruleGrid" and "zoom" respectively). The test text output is correctly logged but the grid is visible anyway. Both images seems to be merged and I can't mask the 2nd one. I have tried to display the grid elsewhere (other coordinates) and it works well. But if both images are overlapping, then the grid part on the other image stays (as if drawn on the first one, and not on the JPanel). Sorry if it isn't clear enough...

Some screenshots might help : Grid and background with same coordinates

Grid and background with different coordinates

When scrolling and zooming out : here is the problem. The overlapping part of the grid is still 'printed' on the background image and the rest of the grid is shown 'under' the background.

Why is it happening ? What did I do wrong ? Is it due to an optimization/render from the Graphics2D Class ? Should I rather use Layered Panes ?

For both BufferedImages, I use :

BufferedImage.TYPE_INT_ARGB

.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);

Here is a simplication of my code :

BufferedImage mapZones;
BufferedImage mapGrid;

@Override
public void paintComponent(Graphics g1){

    Graphics2D g = (Graphics2D)g1;

    //Clear the map
    clearBackground(g);

    //Display Background
    displayMap(g, mapZones);

    //Grid
    if (Options.ruleGrid && Options.zoom > 4f) {
        displayMap(g, mapGrid);
        System.out.println("Test if grid should be displayed"); 
    }  
}

/*********************************************************************************************************/
private void displayMap(Graphics2D g, BufferedImage bufI) {

    g.drawImage(bufI, -x0, -y0, width, height, null);

}

/*********************************************************************************************************/
private void clearBackground(Graphics2D g1) {
    g1.setColor(Color.WHITE);   
    int max = 500000;       
    g1.clearRect(-max, -max, max*2, max*2);
}
/*********************************************************************************************************/

Any help would be appreciated. Thanks.


Solution

  • Find the reason (though, not the 'why ?'). I had a 3rd call to 'displayMap' with an empty image

        //Display Elements
        displayMap(g, mapElements);
    

    which is created by

    mapElements = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
    

    and I have not yet drawn onto it.

    When I comment the call to 'displayMap(g, mapElements);', I finally have the desired behaviour.

    But I still don't know why ? I think it's the way the Graphics Class and the 'drawn' functions are coded :

    This method returns immediately in all cases, even if the complete image has not yet been loaded, and it has not been dithered and converted for the current output device.

    I guess the JVM somehow 'pools' (?) the drawings in the same area and my maps were merged... If anyone could explain this in a simple manner...