This commit is contained in:
@@ -74,7 +74,7 @@ public class DataStreamProcessorTest {
|
||||
input.setLastAid(AID_ENTER);
|
||||
|
||||
java.util.concurrent.atomic.AtomicReference<byte[]> sentData = new java.util.concurrent.atomic.AtomicReference<>();
|
||||
processor.setOutputCallback(sentData::set);
|
||||
processor.setOutputSender(sentData::set);
|
||||
|
||||
byte[] rbRecord = new byte[] { (byte) CMD_RB };
|
||||
processor.processRecord(rbRecord, 0, rbRecord.length, true);
|
||||
|
||||
@@ -335,4 +335,224 @@ public class GocaDecoderTest {
|
||||
assertEquals(0x40, decoder.getCharSet());
|
||||
assertTrue(plane.hasContent(), "Expected plane to have content after GCALL segment execution");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAntialiasedLineRendering() {
|
||||
GraphicsPlane plane = new GraphicsPlane(100, 100);
|
||||
plane.clear();
|
||||
int red = 0xFFFF0000;
|
||||
|
||||
// Draw a diagonal line with Xiaolin Wu anti-aliasing
|
||||
plane.drawLine(10.0, 10.0, 50.0, 30.0, red, GocaConstants.LT_SOLID, GocaConstants.LW_NORMAL);
|
||||
assertTrue(plane.hasContent());
|
||||
|
||||
int[] buffer = plane.getRgbBuffer();
|
||||
boolean hasIntermediateAlpha = false;
|
||||
int nonZeroPixels = 0;
|
||||
|
||||
for (int y = 0; y < 100; y++) {
|
||||
for (int x = 0; x < 100; x++) {
|
||||
int pixel = buffer[y * 100 + x];
|
||||
if (pixel != 0) {
|
||||
nonZeroPixels++;
|
||||
int alpha = (pixel >>> 24) & 0xFF;
|
||||
int r = (pixel >>> 16) & 0xFF;
|
||||
assertEquals(255, r, "Red channel must be preserved");
|
||||
if (alpha > 0 && alpha < 255) {
|
||||
hasIntermediateAlpha = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertTrue(nonZeroPixels > 30, "Expected non-zero pixels along the line");
|
||||
assertTrue(hasIntermediateAlpha, "Expected Xiaolin Wu anti-aliasing to produce fractional alpha coverage");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubpixelArcAndAlphaBlending() {
|
||||
GraphicsPlane plane = new GraphicsPlane(100, 100);
|
||||
plane.clear();
|
||||
int green = 0xFF00FF00;
|
||||
|
||||
plane.drawArc(50.0, 50.0, 30.0, 30.0, 0.0, 360.0, green, GocaConstants.LT_SOLID, GocaConstants.LW_NORMAL, true);
|
||||
assertTrue(plane.hasContent());
|
||||
|
||||
int[] buffer = plane.getRgbBuffer();
|
||||
boolean hasIntermediateAlpha = false;
|
||||
int nonZeroPixels = 0;
|
||||
|
||||
for (int p : buffer) {
|
||||
if (p != 0) {
|
||||
nonZeroPixels++;
|
||||
int alpha = (p >>> 24) & 0xFF;
|
||||
if (alpha > 0 && alpha < 255) {
|
||||
hasIntermediateAlpha = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertTrue(nonZeroPixels > 50, "Expected arc pixels");
|
||||
assertTrue(hasIntermediateAlpha, "Expected anti-aliased arc edges with smooth alpha");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVectorTextAntialiasedDrawing() {
|
||||
GraphicsPlane plane = new GraphicsPlane(200, 100);
|
||||
plane.clear();
|
||||
int yellow = 0xFFFFFF00;
|
||||
|
||||
plane.drawVectorText(10.0, 10.0, "8% 1985 TAX", yellow, 12.0, 20.0, GocaConstants.CD_LR, 0.0);
|
||||
assertTrue(plane.hasContent());
|
||||
|
||||
int[] buffer = plane.getRgbBuffer();
|
||||
boolean hasIntermediateAlpha = false;
|
||||
int nonZeroPixels = 0;
|
||||
|
||||
for (int p : buffer) {
|
||||
if (p != 0) {
|
||||
nonZeroPixels++;
|
||||
int alpha = (p >>> 24) & 0xFF;
|
||||
if (alpha > 0 && alpha < 255) {
|
||||
hasIntermediateAlpha = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertTrue(nonZeroPixels > 40, "Expected stroked vector text pixels");
|
||||
assertTrue(hasIntermediateAlpha, "Expected anti-aliased vector text strokes with fractional alpha");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTextRendererPrecisionSwitching() {
|
||||
GraphicsPlane plane = new GraphicsPlane(200, 100);
|
||||
GocaDecoder decoder = new GocaDecoder(plane);
|
||||
|
||||
final boolean[] textRendererCalled = new boolean[1];
|
||||
plane.setTextRenderer((p, x, y, text, color, cw, ch, dir, angle) -> {
|
||||
textRendererCalled[0] = true;
|
||||
});
|
||||
|
||||
// Test String Precision (Default): GCHST (0xC3) with "TAX"
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
out.write(GocaConstants.G_GCHST);
|
||||
out.write(0x07); // length = 7 (4 bytes pos + 3 bytes text)
|
||||
out.write(0x00); out.write(0x00); // x = 0
|
||||
out.write(0x00); out.write(0x00); // y = 0
|
||||
out.write(0xE3); // 'T' in EBCDIC
|
||||
out.write(0xC1); // 'A' in EBCDIC
|
||||
out.write(0xE7); // 'X' in EBCDIC
|
||||
|
||||
byte[] stream = out.toByteArray();
|
||||
decoder.decodeStream(stream, 0, stream.length);
|
||||
|
||||
assertTrue(textRendererCalled[0], "Expected pluggable TextRenderer to be called for String precision");
|
||||
|
||||
// Now set Stroke precision (G_GSCC with 3):
|
||||
textRendererCalled[0] = false;
|
||||
ByteArrayOutputStream outStroke = new ByteArrayOutputStream();
|
||||
outStroke.write(GocaConstants.G_GSCC);
|
||||
outStroke.write(0x01);
|
||||
outStroke.write(GocaConstants.CP_STROKE); // 3
|
||||
outStroke.write(GocaConstants.G_GCHST);
|
||||
outStroke.write(0x07);
|
||||
outStroke.write(0x00); outStroke.write(0x00);
|
||||
outStroke.write(0x00); outStroke.write(0x00);
|
||||
outStroke.write(0xE3); outStroke.write(0xC1); outStroke.write(0xE7);
|
||||
|
||||
byte[] strokeStream = outStroke.toByteArray();
|
||||
decoder.decodeStream(strokeStream, 0, strokeStream.length);
|
||||
|
||||
assertFalse(textRendererCalled[0], "Expected drawVectorText (not textRenderer) when precision is CP_STROKE");
|
||||
assertTrue(plane.hasContent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThickLineRendering() {
|
||||
GraphicsPlane plane = new GraphicsPlane(100, 100);
|
||||
plane.clear();
|
||||
|
||||
plane.drawLine(10.0, 10.0, 50.0, 50.0, 0xFF00FF00, GocaConstants.LT_SOLID, GocaConstants.LW_THICK);
|
||||
assertTrue(plane.hasContent());
|
||||
|
||||
int[] buffer = plane.getRgbBuffer();
|
||||
int nonZero = 0;
|
||||
for (int p : buffer) {
|
||||
if (p != 0) nonZero++;
|
||||
}
|
||||
assertTrue(nonZero > 60, "Expected thick line to occupy more pixels than standard 1px line");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShortFormAttributeOrderParsing() {
|
||||
GraphicsPlane plane = new GraphicsPlane(200, 200);
|
||||
GocaDecoder decoder = new GocaDecoder(plane);
|
||||
|
||||
// Sequence: GBAR (0x68) short form with 0x80 flag (boundary=true, fill=false),
|
||||
// followed immediately by GLINE (0xC1 len=12 for 3 points) and GEAR (0x60)
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
out.write(GocaConstants.G_GBAR);
|
||||
out.write(0x80); // Short 2-byte form with 0x80 flag! Must NOT be treated as length=128!
|
||||
out.write(GocaConstants.G_GLINE);
|
||||
out.write(0x0C); // 3 points = 12 bytes
|
||||
out.write(0x00); out.write(0x00);
|
||||
out.write(0x00); out.write(0x00);
|
||||
out.write(0x00); out.write(0x32);
|
||||
out.write(0x00); out.write(0x00);
|
||||
out.write(0x00); out.write(0x32);
|
||||
out.write(0x00); out.write(0x32);
|
||||
out.write(GocaConstants.G_GEAR);
|
||||
out.write(0x00);
|
||||
|
||||
byte[] stream = out.toByteArray();
|
||||
decoder.decodeStream(stream, 0, stream.length);
|
||||
|
||||
assertTrue(plane.hasContent(), "Expected GLINE and GEAR inside GBAR short-form 0x80 to be decoded properly");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiPolygonAreaFilling() {
|
||||
GraphicsPlane plane = new GraphicsPlane(200, 200);
|
||||
GocaDecoder decoder = new GocaDecoder(plane);
|
||||
|
||||
// Sequence: GBAR (0x68) short form 0x80 (bounded, always filled in GOCA)
|
||||
// Polygon 1 (e.g. Letter 'T' bar): (10,10) to (30,10) to (30,20) to (10,20) to (10,10)
|
||||
// Polygon 2 (e.g. Letter 'T' stem): (18,20) to (22,20) to (22,40) to (18,40) to (18,20)
|
||||
// GEAR (0x60)
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
out.write(GocaConstants.G_GBAR);
|
||||
out.write(0x80);
|
||||
|
||||
// Polygon 1
|
||||
out.write(GocaConstants.G_GLINE);
|
||||
out.write(0x14); // 5 points = 20 bytes
|
||||
out.write(0x00); out.write(10); out.write(0x00); out.write(10);
|
||||
out.write(0x00); out.write(30); out.write(0x00); out.write(10);
|
||||
out.write(0x00); out.write(30); out.write(0x00); out.write(20);
|
||||
out.write(0x00); out.write(10); out.write(0x00); out.write(20);
|
||||
out.write(0x00); out.write(10); out.write(0x00); out.write(10);
|
||||
|
||||
// Polygon 2 (disconnected start -> triggers new subpath)
|
||||
out.write(GocaConstants.G_GLINE);
|
||||
out.write(0x14); // 5 points = 20 bytes
|
||||
out.write(0x00); out.write(18); out.write(0x00); out.write(20);
|
||||
out.write(0x00); out.write(22); out.write(0x00); out.write(20);
|
||||
out.write(0x00); out.write(22); out.write(0x00); out.write(40);
|
||||
out.write(0x00); out.write(18); out.write(0x00); out.write(40);
|
||||
out.write(0x00); out.write(18); out.write(0x00); out.write(20);
|
||||
|
||||
out.write(GocaConstants.G_GEAR);
|
||||
out.write(0x00);
|
||||
|
||||
byte[] stream = out.toByteArray();
|
||||
decoder.decodeStream(stream, 0, stream.length);
|
||||
|
||||
assertTrue(plane.hasContent());
|
||||
int[] buffer = plane.getRgbBuffer();
|
||||
int nonZero = 0;
|
||||
for (int p : buffer) {
|
||||
if (p != 0) nonZero++;
|
||||
}
|
||||
assertTrue(nonZero > 50, "Expected both filled subpath polygons to render filled pixels");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.lib3270j.input;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.lib3270j.TerminalModel;
|
||||
import org.lib3270j.charset.EbcdicTranslator;
|
||||
import org.lib3270j.datastream.DataStreamProcessor;
|
||||
import org.lib3270j.screen.ScreenBuffer;
|
||||
import org.lib3270j.telnet.TelnetFSM;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
@@ -184,7 +185,7 @@ public class InputProcessorTest {
|
||||
// Let's verify DataStreamProcessor ReadModified behavior with the same buffer
|
||||
DataStreamProcessor dsp = new DataStreamProcessor(screen, translator);
|
||||
dsp.setInputProcessor(input);
|
||||
dsp.setOutputCallback(sent::set);
|
||||
dsp.setOutputSender(sent::set);
|
||||
|
||||
byte[] rmRecord = new byte[] { (byte) CMD_RM };
|
||||
dsp.processRecord(rmRecord, 0, rmRecord.length, true);
|
||||
|
||||
Reference in New Issue
Block a user