Files
j3270/lib3270j/src/main/java/org/lib3270j/graphics/ProgramSymbolSet.java
T
rudi 1212b2cceb
Release j3270 / Build & Publish Release (push) Successful in 42s
Build and Test j3270 / Build JAR & Run Tests (push) Successful in 1m10s
Add tn3270(no e) and fix animations
2026-08-21 16:32:48 -04:00

190 lines
6.7 KiB
Java

package org.lib3270j.graphics;
/**
* Represents a set of up to 191 custom bitmapped symbols (LCID 0x40 - 0xFE).
* Supports both Single-Plane (monochrome) and Triple-Plane (7-color RGB composite).
*/
public class ProgramSymbolSet {
public static final int NUM_SLOTS = 191; // Code points 0x40 - 0xFE (0..190)
private int lcid = 0;
private final boolean isTriplePlane;
private final SymbolSlot[] slots = new SymbolSlot[NUM_SLOTS];
public ProgramSymbolSet(boolean isTriplePlane) {
this.isTriplePlane = isTriplePlane;
}
public int getLcid() {
return lcid;
}
public void setLcid(int lcid) {
this.lcid = lcid;
}
public boolean isTriplePlane() {
return isTriplePlane;
}
public void clear() {
for (int i = 0; i < NUM_SLOTS; i++) {
slots[i] = null;
}
}
public void clearSlot(int index) {
if (index >= 0 && index < NUM_SLOTS) {
slots[index] = null;
}
}
public void setSlot(int index, SymbolSlot slot) {
if (index >= 0 && index < NUM_SLOTS) {
slots[index] = slot;
}
}
public SymbolSlot getSlot(int index) {
if (index >= 0 && index < NUM_SLOTS) {
return slots[index];
}
return null;
}
/**
* Represents a single custom symbol bitmap.
*/
public static class SymbolSlot {
private final int width;
private final int height;
private final byte[] pixelData; // 1 byte per pixel: 0 = background, 1..7 = color index (or 1 for monochrome)
private final boolean isTriplePlane;
private int[] cachedRgbArray;
private int cachedFgRgb = -1;
private int cachedBgRgb = -1;
private java.awt.image.BufferedImage cachedImage;
private java.awt.image.BufferedImage cachedScaledImage;
private int cachedTargetW = 0;
private int cachedTargetH = 0;
private int cachedScaledFgRgb = -1;
private int cachedScaledBgRgb = -1;
public SymbolSlot(int width, int height, byte[] pixelData, boolean isTriplePlane) {
this.width = width > 0 ? width : 9;
this.height = height > 0 ? height : 16;
this.pixelData = pixelData;
this.isTriplePlane = isTriplePlane;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public byte[] getPixelData() {
return pixelData;
}
public boolean isTriplePlane() {
return isTriplePlane;
}
/**
* Returns an image scaled directly to target dimensions (cellWidth x cellHeight).
* Enables unscaled 1:1 hardware blitting in Java2D.
*/
public synchronized java.awt.image.BufferedImage getScaledImage(int targetW, int targetH, int fgArgb, int bgArgb) {
if (targetW <= 0 || targetH <= 0) {
return getImage(fgArgb, bgArgb);
}
if (targetW == width && targetH == height) {
return getImage(fgArgb, bgArgb);
}
if (cachedScaledImage != null && cachedTargetW == targetW && cachedTargetH == targetH
&& cachedScaledFgRgb == fgArgb && cachedScaledBgRgb == bgArgb) {
return cachedScaledImage;
}
int[] srcRgb = getRgbPixels(fgArgb, bgArgb);
java.awt.image.BufferedImage scaled = new java.awt.image.BufferedImage(targetW, targetH, java.awt.image.BufferedImage.TYPE_INT_ARGB);
int[] dstRgb = ((java.awt.image.DataBufferInt) scaled.getRaster().getDataBuffer()).getData();
for (int dy = 0; dy < targetH; dy++) {
int sy = dy * height / targetH;
int srcRowOffset = sy * width;
int dstRowOffset = dy * targetW;
for (int dx = 0; dx < targetW; dx++) {
int sx = dx * width / targetW;
dstRgb[dstRowOffset + dx] = srcRgb[srcRowOffset + sx];
}
}
this.cachedScaledImage = scaled;
this.cachedTargetW = targetW;
this.cachedTargetH = targetH;
this.cachedScaledFgRgb = fgArgb;
this.cachedScaledBgRgb = bgArgb;
return scaled;
}
/**
* Computes and returns the cached BufferedImage for this symbol glyph.
* Eliminates per-cell heap allocations during high frame rate rendering.
*/
public synchronized java.awt.image.BufferedImage getImage(int fgArgb, int bgArgb) {
if (cachedImage != null && cachedFgRgb == fgArgb && cachedBgRgb == bgArgb) {
return cachedImage;
}
int[] rgb = getRgbPixels(fgArgb, bgArgb);
java.awt.image.BufferedImage img = new java.awt.image.BufferedImage(width, height, java.awt.image.BufferedImage.TYPE_INT_ARGB);
int[] imgData = ((java.awt.image.DataBufferInt) img.getRaster().getDataBuffer()).getData();
System.arraycopy(rgb, 0, imgData, 0, rgb.length);
this.cachedImage = img;
this.cachedFgRgb = fgArgb;
this.cachedBgRgb = bgArgb;
return img;
}
/**
* Computes and returns the 32-bit ARGB pixel array for this symbol.
* The returned array has length (width * height).
*/
public synchronized int[] getRgbPixels(int fgArgb, int bgArgb) {
if (cachedRgbArray != null && cachedFgRgb == fgArgb && cachedBgRgb == bgArgb) {
return cachedRgbArray;
}
int[] rgbArray = new int[width * height];
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int idx = y * width + x;
int val = (pixelData != null && idx < pixelData.length) ? (pixelData[idx] & 0xFF) : 0;
if (val == 0) {
rgbArray[idx] = bgArgb;
} else if (!isTriplePlane) {
rgbArray[idx] = fgArgb;
} else {
// Triple-Plane RGB composite:
// val is bitmask: bit 0 (0x01) = Red, bit 1 (0x02) = Green, bit 2 (0x04) = Blue
int r = (val & 0x01) != 0 ? 255 : 0;
int g = (val & 0x02) != 0 ? 255 : 0;
int b = (val & 0x04) != 0 ? 255 : 0;
rgbArray[idx] = (0xFF << 24) | (r << 16) | (g << 8) | b;
}
}
}
this.cachedRgbArray = rgbArray;
this.cachedFgRgb = fgArgb;
this.cachedBgRgb = bgArgb;
return rgbArray;
}
}
}