190 lines
6.7 KiB
Java
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;
|
|
}
|
|
}
|
|
}
|