javacolorsaccessibilitymacos-darkmode

Mimic JavaFX's ColorAdjust brightness using pure Java


I'm attempting to convert a color image to a useable monochrome image, but without the "jagged" edges.

From a similar question asking to convert an image from color to black and white, one of the accepted answers provides a simple trick from JavaFX's ColorAdjust class using a setBrightness(-1) technique. This technique has the benefit of maintaining the soft edges between black and white, such as supporting a high-contrast theme without creating all-new icons set.

Note: I do understand the inaccuracy of the word "monochrome" here (some grayscaling will occur) but I'm not sure how else to describe this technique.

What's a way to mimic the ColorAdust technique using pure Java?

Desired:

soft edges


NOT Desired:

jagged edges


Solution

  • This is a pure Java approach. The Swing code is not needed to create the image. Instead of changing the image to black and white, we are changing the image to black and transparent. That is how we preserve those feathered edges.

    result: enter image description here

    If you want a true grayscale image with no alpha, make a graphics2d object, fill it with the desired background color, then draw the image onto it.

    As for preserving whites as white, this can be done, but one of two thing must be conceded. Either you give up the black and white aspect and adopt a true grayscale image, or you keep your black and white, but get a jagged edge where white feathers into any other color. This occurs because once we hit a light color pixel, how do we know whether it is a light colored feature, or a transition pixel between white and another color. I don't know of a way to fix that without edge detection.

    public class Main {
        private static void createAndShowGUI() {
            //swing stuff
            JFrame.setDefaultLookAndFeelDecorated(true);
            JFrame frame = new JFrame("Alpha Mask");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.getContentPane().setLayout(new BoxLayout(frame.getContentPane(), BoxLayout.PAGE_AXIS));
    
            JLabel picLabel = new JLabel(new ImageIcon(getImg()));
            frame.getContentPane().add(picLabel);
    
            BufferedImage alphaMask = createAlphaMask(getImg());
    
            JLabel maskLabel = new JLabel(new ImageIcon(alphaMask));
            frame.getContentPane().add(maskLabel);
    
            //Display the window.
            frame.pack();
            frame.setVisible(true);
        }
    
        public static BufferedImage getImg() {
            try {
                return ImageIO.read(new URL("https://i.sstatic.net/UPmqE.png"));
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }
    
        public static BufferedImage createAlphaMask(BufferedImage img) {
            //TODO: deep copy img here if you actually use this
            int width = img.getWidth();
            int[] data = new int[width];
    
            for (int y = 0; y < img.getHeight(); y++) {
                // pull down a line if argb data
                img.getRGB(0, y, width, 1, data, 0, 1);
                for (int x = 0; x < width; x++) {
                    //set color data to black, but preserve alpha, this will prevent harsh edges
                    int color = data[x] & 0xFF000000;
                    data[x] = color;
                }
                img.setRGB(0, y, width, 1, data, 0, 1);
            }
            return img;
        }
    
        public static void main(String[] args) {
            javax.swing.SwingUtilities.invokeLater(() -> createAndShowGUI());
        }
    }