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.
Converting BufferedImage to Pixmap
26 Views Asked by robbie.huffman At
2
There are 2 best solutions below
1
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 + "\".");
}
}
}
Turns out the answer is to create a
BufferedImagewith the correct type, create acom.sun.jna.Memorybuffer, create anXImagebased 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