Search code examples
javaimageimage-processingpngjavax.imageio

Java, how to make a PNG out of list of x,y coordinates


I have data (coordinates (doubles) ultimately) that look like this

x,y
===
1.1,2.3
2.9,5.4
7.9,8.1

What I need to do is generate a simple PNG out of these coords that shows these coords as black dots on a white background (then I'll tweak colors later).

I have googled aplenty, and I'm not super familiar with Java's image "stuff," so I was wondering if anyone could help.

This is what I have found so far, but I can't use doubles with this, and I don't see how to get around that at this point. I'm trying to stay away from external libraries due to environment reasons.

final BufferedImage image = new BufferedImage(256, 256, BufferedImage.TYPE_INT_ARGB);   

 final Graphics2D graphics2D = image.createGraphics();
    graphics2D.setPaint(Color.WHITE);
    graphics2D.fillRect(0, 0, 1000, 1000);
    graphics2D.setPaint(Color.BLACK);
    graphics2D.drawOval(0, 0, 1000, 1000);
    graphics2D.dispose();

Anyone have any advice?


Solution

  • I would suggest you to use a boolean[][] to store your pixels. Using doubles to store your coordinates is a horrendous waste of memory. I would recommend the following approach:

    boolean[][] pixBool;
    BufferedImage img;
    
    for(int a = 0; a < pixBool.length;a++){
        for(int b = 0; b < pixBool[0].length;b++){
            boolean pix = pixBool[a][b];
            if(pix){
                img.setRGB(a,b,0xff000000);  
            }
        }
    }
    

    In this case a white (0xffffffff) pixel would be stored in the pixBool array as false, whilst black (0xff000000) pixels would be stored as true. Thus after initialization of the array, the whole image would be white (background). The image will be created by manipulating the pixels of the given BufferedImage via the setRGB() method.

    If really have to work with doubles, you could convert them and then use the above mentioned method:

    double[] coordinates;
    for(int c = 0; c < coordinates.length; c++){
        double coord = coordinates[c];
        BigDecimal x = BigDecimal.valueOf((int)coord);
        BigDecimal y = BigDecimal.valueOf(coord);
        y = y.subtract(x);
        y = y.scaleByPowerOfTen(y.scale);
        pixBool[(int)x.longValue()][(int)y.longValue()] = true;
    }
    

    To extract the x coordinate, you simply have to extract the whole part of the double which can be easily done via converting it to int and thus losing the fractional part. The y coordinate is the tricky part. You have to use the BigDecimal class because of the uncertainty of the double. At first you have to extract the fractional part of the number, because that's were your coordinate is stored. That can be accomplished via subtracting the whole part of the number. After that, you have to scale the extracted fractional part to be a whole number. That is done via multiplication with 10 to the power of the number of digits after the point (the scale method). After that, you simply have to extract the calculated numbers from the BigDecimal class and you are finished.

    You could also use a more blunt approach utilizing the String class:

    double[] coordinates;
    for(int c = 0; c < coordinates.length; c++){
        double coord = coordinates[c];
        String temp = String.valueOf(coord);
        String [] temp2 = temp.split(".");
        int x = Integer.valueOf(temp2[0]);
        int y = Integer.valueOf(temp[1]);
        pixBool[x][y] = true;
    }
    

    In this case, you first convert your coordinate to String and then split it around the decimal point. That will grant you an String[] with two entries: The whole part of your number (aka x-coordinate) and the fractional part(aka y coordinate). You then simply have to convert the Strings back to int and then use them to fill the boolean[].