Converting BufferedImage to Pixmap

26 views Asked by At

Is there some way to convert a BufferedImage to X11 Pixmap? It looks like JNA has com.sun.jna.platform.unix.X11.Pixmap but I can't find a way to convert anything to that format.

2

There are 2 answers

0
robbie.huffman On BEST ANSWER

Turns out the answer is to create a BufferedImage with the correct type, create a com.sun.jna.Memory buffer, create an XImage based on that buffer, and then write the BufferedImage's data into the buffer.

The correct type is based on the target visual of the XImage.

I wrote up a demo in Clojure and might still be playing with it. It should be easily conveted back to Java code for anyone interested.

https://github.com/robbieh/clj-xscreensaver-basic-demo/tree/dev

1
VGR On

The XPM format is pretty simple. You do need to create a colormap, which requires examining every pixel of the image if the image doesn’t use an IndexColorModel.

import java.io.IOException;
import java.io.Writer;

import java.nio.file.Path;
import java.nio.file.Files;

import java.util.Set;
import java.util.HashSet;
import java.util.Map;
import java.util.HashMap;

import java.util.stream.IntStream;

import java.awt.image.BufferedImage;
import java.awt.image.IndexColorModel;

import javax.imageio.ImageIO;

// Reference: https://en.wikipedia.org/wiki/X_PixMap

public class XPixmapConverter {
    public void convertToXPixmap(BufferedImage image,
                                 Writer destination)
    throws IOException {
        int width = image.getWidth();
        int height = image.getHeight();

        int[] rgbValues;

        if (image.getColorModel() instanceof IndexColorModel ic) {
            int size = ic.getMapSize();
            rgbValues = IntStream.range(0, size).map(ic::getRGB).toArray();
        } else {
            Set<Integer> rgb = new HashSet<>();
            for (int y = 0; y < height; y++) {
                for (int x = 0; x < width; x++) {
                    rgb.add(image.getRGB(x, y));
                }
            }

            rgbValues = rgb.stream().mapToInt(Integer::intValue).toArray();
        }

        int colorCount = rgbValues.length;

        String allChars =
            "abcdefghijklmnopqrstuvwxyz" +
            "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
            "0123456789.-";

        int charsPerPixel = (int)
            Math.ceil(Math.log(colorCount) / Math.log(allChars.length()));

        destination.write("/* XPM */\n");
        destination.write("static char * image[] = {\n");
        destination.write("\"" + width + " " + height + " "
            + colorCount + " " + charsPerPixel + "\",\n");

        Map<Integer, String> pixelReps = new HashMap<>();
        int[] charIndices = new int[charsPerPixel];
        StringBuilder builder = new StringBuilder(charsPerPixel);
        for (int i = 0; i < colorCount; i++) {
            int rgb = rgbValues[i];

            builder.setLength(0);
            for (int j = 0; j < charsPerPixel; j++) {
                builder.append(allChars.charAt(charIndices[j]));
            }
            for (int j = 0; j < charsPerPixel; j++) {
                if (++charIndices[j] < allChars.length()) {
                    break;
                }
                charIndices[j] = 0;
            }

            String pixelRep = builder.toString();
            pixelReps.put(rgb, pixelRep);

            boolean transparent = (rgb >>> 24) == 0;
            destination.write("\"" + pixelRep + " c "
                + (transparent ? "None" :
                    String.format("#%06x", rgb & 0xffffff))
                + "\",\n");
        }

        for (int y = 0; y < height; y++) {
            destination.write("\"");
            for (int x = 0; x < width; x++) {
                destination.write(pixelReps.get(image.getRGB(x, y)));
            }
            destination.write("\"");
            if (y < height - 1) {
                destination.write(",");
            }
            destination.write("\n");
        }

        destination.write("};");
    }

    public void convertToXPixmap(BufferedImage image,
                                 Path destination)
    throws IOException {
        try (Writer writer = Files.newBufferedWriter(destination)) {
            convertToXPixmap(image, writer);
        }
    }

    public static void main(String[] args)
    throws IOException {
        XPixmapConverter converter = new XPixmapConverter();

        for (String arg : args) {
            Path source = Path.of(arg);
            Path dest = Path.of(arg + ".xpm");

            BufferedImage img = ImageIO.read(source.toFile());
            converter.convertToXPixmap(img, dest);
            System.out.println("Wrote \"" + dest + "\".");
        }
    }
}