Created
June 9, 2025 18:10
-
-
Save UserUNP/845b6b1c461a8d043891e80400fb4f93 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// requires jdk 24 with --enable-preview | |
import java.awt.image.DirectColorModel; | |
import java.awt.image.ColorModel; | |
import java.lang.foreign.MemorySegment; | |
import java.lang.foreign.ValueLayout; | |
import org.jetbrains.annotations.NotNull; | |
import it.unimi.dsi.fastutil.ints.IntArrayList; | |
import net.kyori.adventure.text.Component; | |
import net.kyori.adventure.text.TextComponent; | |
import net.kyori.adventure.text.format.TextColor; | |
public class Renderer { | |
private static final String PIXEL_STR = "⏹ "; | |
private static final int CHAR_PIXELS = 5; | |
private static final int LINE_WIDTH = CHAR_PIXELS * 2; | |
private static final float CHAR_LENGTH = CHAR_PIXELS / 40f; | |
private final ColorModel rgb; | |
public Renderer(ColorModel rgb) { | |
this.rgb = rgb; | |
} | |
public Renderer(int[] bitMasks) { | |
this(new DirectColorModel(32, bitMasks[0], bitMasks[1], bitMasks[2])); | |
} | |
public Surface surface(Supplier supplier) { | |
return new Surface(supplier); | |
} | |
public int getColor(int pixel) { | |
return rgb.getRGB(pixel); | |
} | |
public boolean renderNextField(Surface surface) { | |
while (!surface.next()) {} | |
return surface.unloaded; | |
} | |
public class Surface { | |
private volatile MemorySegment pixels; | |
private volatile boolean unloaded; | |
private int width, height; | |
private int fixedWidth, fixedHeight, x, y; | |
private final SurfaceField[] fields = new SurfaceField[4]; | |
public Surface(Supplier supplier) { | |
fields[0] = new SurfaceField(supplier, 0, 1); | |
fields[1] = new SurfaceField(supplier, 1, 1); | |
fields[2] = new SurfaceField(supplier, 0, 0); | |
fields[3] = new SurfaceField(supplier, 1, 0); | |
} | |
public int width() { | |
return width; | |
} | |
public int height() { | |
return height; | |
} | |
public int fieldsCount() { | |
return fields.length; | |
} | |
public void update(@NotNull MemorySegment pixelsPtr, int width, int height) { | |
long byteSize = (long) width * height * 4; | |
if (byteSize > Integer.MAX_VALUE) throw new RuntimeException("Surface too large"); | |
if (pixelsPtr.isNative()) pixels = pixelsPtr.reinterpret(byteSize); | |
else pixels = pixelsPtr; | |
this.width = width; | |
this.height = height; | |
fixedWidth = width % 2 == 0 ? width : width + 1; | |
fixedHeight = height % 2 == 0 ? height : height + 1; | |
for (SurfaceField field : fields) field.update(fixedWidth / 2, fixedHeight / 2); | |
x = y = 0; | |
unloaded = false; | |
} | |
public void stop() { | |
unloaded = true; | |
pixels = null; | |
} | |
private boolean next() { | |
if (unloaded || pixels == null) return true; | |
if (y >= fixedHeight) x = y = 0; | |
int color = (x < width && y < height) ? (getColor(pixels.getAtIndex(ValueLayout.JAVA_INT, y * width + x))) : 0; | |
boolean fieldFinished = fields[(y % 2) * 2 + (x % 2)].next(color); | |
if (++x >= fixedWidth) { | |
x = 0; | |
y++; | |
} | |
return fieldFinished; | |
} | |
} | |
@FunctionalInterface | |
public interface Supplier { | |
Entity get(float x, float y); | |
} | |
public interface Entity { | |
void update(int lineWidth); | |
void render(Component text); | |
} | |
static class SurfaceField { | |
int index, offX, offY; | |
final Entity entity; | |
final IntArrayList pixmap = new IntArrayList(); | |
SurfaceField(Supplier supplier, int offX, int offY) { | |
this.offX = offX; | |
this.offY = offY; | |
entity = supplier.get(offX * CHAR_LENGTH / 2, offY * CHAR_LENGTH / 2); | |
} | |
boolean next(int color) { | |
boolean reset = index >= pixmap.size(); | |
if (reset) { | |
entity.render(pixmap.intStream() | |
.mapToObj(c -> Component.text(PIXEL_STR, TextColor.color(c))) | |
.collect(Component::text, TextComponent.Builder::append, TextComponent.Builder::append) | |
.build()); | |
index = 0; | |
} | |
if (index < pixmap.size()) pixmap.set(index++, color); | |
return reset; | |
} | |
void update(int width, int height) { | |
int newSize = width * height; | |
pixmap.size(newSize); | |
pixmap.trim(); | |
entity.update(width * LINE_WIDTH); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment