Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 2D Rendering Ripple Artifacts

Tags:

java

graphics

2d

I'm working on an isometric 2D game in Java and I noticed that there is a grid of lines on the screen where the rendering seems to repeat a pixel horizontally and vertically. Here's a screenshot to show what I mean:

Screenshot

I checked the loop in my Render method and compared each pixel's color value to the previous value in the array to see if there was any occurrence of two black pixels in a row and no such occurrence was found. I'm at a loss as to what could be causing this effect. What can I do to fix this? Here's the relevant code:

Game class variables:

private BufferedImage m_imgImage = new BufferedImage( intCANVAS_WIDTH, intCANVAS_HEIGHT, BufferedImage.TYPE_INT_RGB );
private int[ ] m_aintPixels = ( ( DataBufferInt ) m_imgImage.getRaster( ).getDataBuffer( ) ).getData( );

Game class Render method:

// Object to organize data on the canvas
BufferStrategy bfsBufferStrategy = getBufferStrategy( );

if( bfsBufferStrategy == null )
{
    createBufferStrategy( 3 );
    return;
}

for( int intY = 0; intY < intCANVAS_HEIGHT; intY += 1 )
{
    for( int intX = 0; intX < intCANVAS_WIDTH; intX += 1 )
    {
        m_aintPixels[ intX + ( intY * intCANVAS_WIDTH ) ] = m_Screen.m_aintPixels[ intX + ( intY * m_Screen.m_intWidth ) ];
    }
}

Graphics gfxGraphics = bfsBufferStrategy.getDrawGraphics( );
gfxGraphics.drawImage( m_imgImage, 0, 0, getWidth( ), getHeight( ), null );


gfxGraphics.dispose( );
bfsBufferStrategy.show( );

Screen class Render method to populate the pixel array:

// Render a tile with the specified tile ID
public void RenderTile( int intPositionX, int intPositionY, CSpriteSheet SpriteSheet, int intTileID )
{

    int intPixelIndex = 0;
    int intSheetX = 0;
    int intSheetY = 0;
    int intTileOffsetX = 0;
    int intTileOffsetY = 0;
    int intPixelColor = 0;

    // Center the Tile
    intPositionX -= m_intOffsetX;
    intPositionY -= m_intOffsetY;

    intSheetX = ( intTileID % SpriteSheet.m_intTileColumns ) * CTile.TILE_WIDTH;
    intSheetY = ( intTileID / SpriteSheet.m_intTileRows ) * CTile.TILE_HEIGHT;

    intPixelIndex = intPositionX + ( intPositionY * m_intWidth );

    // Rows
    for( int intTileRow = intSheetY; intTileRow != ( intSheetY + CTile.TILE_HEIGHT ); intTileRow += 1 )
    {
        // Columns
        for( int intTileColumn = intSheetX; intTileColumn != ( intSheetX + CTile.TILE_WIDTH ); intTileColumn += 1 )
        {
            intPixelColor = SpriteSheet.m_aintPixels[ intTileColumn + ( intTileRow * m_SpriteSheet.m_intWidth ) ];

            // Boundary checking
            if( intPositionX + intTileOffsetX >= 0 && intPositionX + intTileOffsetX < m_intWidth &&
            intPositionY + intTileOffsetY >= 0 && intPositionY + intTileOffsetY < m_intHeight &&
            CColor.Transparent( intPixelColor ) == false )
            {

                m_aintPixels[ intPixelIndex ] = SpriteSheet.m_aintPixels[ intTileColumn + ( intTileRow * m_SpriteSheet.m_intWidth ) ];
            }

            // Increment
            intTileOffsetX += 1;
            intPixelIndex += 1;
        }

        // Increment
        intTileOffsetY += 1;
        intPixelIndex += m_intWidth - ( CTile.TILE_WIDTH );
        intTileOffsetX = 0;
    }
}
like image 321
Joe Boris Avatar asked Aug 13 '26 14:08

Joe Boris


1 Answers

This is a classic artifact of non-interpolated scaling. You have:

private BufferedImage m_imgImage = new BufferedImage( intCANVAS_WIDTH, intCANVAS_HEIGHT, BufferedImage.TYPE_INT_RGB );
...
gfxGraphics.drawImage( m_imgImage, 0, 0, getWidth( ), getHeight( ), null );

The effect you are seeing is because getWidth() and getHeight() are slightly larger than intCANVAS_WIDTH and intCANVAS_HEIGHT. When you draw the graphic, it scales it larger. It's not using any kind of interpolation for scaling, just picking the nearest pixel from the source; therefore you see duplicate rows or columns at regular intervals.

You do not see this when you check your source data because it is not present in your source data, it's an artifact introduced in the output when the data is rendered.

Print out the image and panel dimensions and you will see a difference. Judging by the count of duplicate rows I see (I see 10 horizontal and 10 vertical), I guess you will find that getWidth() is 10 more than intCANVAS_WIDTH, and same for height.

You could pass the dimensions of m_imgImage to .drawImage() instead. That will get rid of the artifact but will leave a gap on the right and bottom since the image is smaller than the component.

If your Graphics is actually a Graphics2D, you can set the KEY_INTERPOLATION rendering hint to use interpolation when scaling (it may not obey that hint):

Graphics2D g = (Graphics2D)gfxGraphics;
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);

If you really want the image to be drawn exactly, without scaling, and without a gap on the sides, then disable resizing on your main window and set the size to match the image.

like image 98
Jason C Avatar answered Aug 16 '26 14:08

Jason C



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!