This commit is contained in:
@@ -423,23 +423,19 @@ public class QueryReplyBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private byte[] buildGraphics(int maxCols, int maxRows) {
|
private byte[] buildGraphics(int maxCols, int maxRows) {
|
||||||
int width = maxCols * 9;
|
|
||||||
int height = maxRows * 12;
|
|
||||||
return new byte[]{
|
return new byte[]{
|
||||||
(byte) 0x80, 0x02,
|
(byte) 0x80, 0x02,
|
||||||
(byte) ((width >> 8) & 0xFF), (byte) (width & 0xFF),
|
0x00, 0x00,
|
||||||
(byte) ((height >> 8) & 0xFF), (byte) (height & 0xFF),
|
0x00, (byte) 0xFC,
|
||||||
0x00
|
0x00
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] buildGImage(int maxCols, int maxRows) {
|
private byte[] buildGImage(int maxCols, int maxRows) {
|
||||||
int width = maxCols * 9;
|
|
||||||
int height = maxRows * 12;
|
|
||||||
return new byte[]{
|
return new byte[]{
|
||||||
0x00, 0x01,
|
0x00, 0x01,
|
||||||
(byte) ((width >> 8) & 0xFF), (byte) (width & 0xFF),
|
0x00, 0x00,
|
||||||
(byte) ((height >> 8) & 0xFF), (byte) (height & 0xFF),
|
0x00, (byte) 0xFC,
|
||||||
0x00,
|
0x00,
|
||||||
0x06, 0x40, 0x06, 0x40, 0x06, 0x01,
|
0x06, 0x40, 0x06, 0x40, 0x06, 0x01,
|
||||||
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xF0
|
(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xF0
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ public class GocaDecoder {
|
|||||||
public synchronized int findPickedSegment(int gx, int gy) {
|
public synchronized int findPickedSegment(int gx, int gy) {
|
||||||
for (int i = activeSegmentsInOrder.size() - 1; i >= 0; i--) {
|
for (int i = activeSegmentsInOrder.size() - 1; i >= 0; i--) {
|
||||||
SegmentBounds sb = activeSegmentsInOrder.get(i);
|
SegmentBounds sb = activeSegmentsInOrder.get(i);
|
||||||
if (sb.contains(gx, gy, 12)) {
|
if (sb.contains(gx, gy, 25)) {
|
||||||
System.err.println(String.format(
|
System.err.println(String.format(
|
||||||
"findPickedSegment: goca=(%d, %d) HIT segId=%d bounds=[%d..%d, %d..%d] tag=%d",
|
"findPickedSegment: goca=(%d, %d) HIT segId=%d bounds=[%d..%d, %d..%d] tag=%d",
|
||||||
gx, gy, sb.segId, sb.minX, sb.maxX, sb.minY, sb.maxY, sb.tag
|
gx, gy, sb.segId, sb.minX, sb.maxX, sb.minY, sb.maxY, sb.tag
|
||||||
@@ -171,6 +171,15 @@ public class GocaDecoder {
|
|||||||
return sb.segId;
|
return sb.segId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (SegmentBounds sb : segmentBoundsMap.values()) {
|
||||||
|
if (sb.contains(gx, gy, 25)) {
|
||||||
|
System.err.println(String.format(
|
||||||
|
"findPickedSegment (fallback): goca=(%d, %d) HIT segId=%d bounds=[%d..%d, %d..%d] tag=%d",
|
||||||
|
gx, gy, sb.segId, sb.minX, sb.maxX, sb.minY, sb.maxY, sb.tag
|
||||||
|
));
|
||||||
|
return sb.segId;
|
||||||
|
}
|
||||||
|
}
|
||||||
System.err.println(String.format("findPickedSegment: goca=(%d, %d) NO HIT (defaulting to 0/canvas)", gx, gy));
|
System.err.println(String.format("findPickedSegment: goca=(%d, %d) NO HIT (defaulting to 0/canvas)", gx, gy));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -798,7 +807,10 @@ public class GocaDecoder {
|
|||||||
}
|
}
|
||||||
case GocaConstants.P_ERASE: { // 0x0A: Erase Graphics Presentation Space
|
case GocaConstants.P_ERASE: { // 0x0A: Erase Graphics Presentation Space
|
||||||
plane.clear();
|
plane.clear();
|
||||||
resetDefaults();
|
resetAttributes();
|
||||||
|
curX = 0;
|
||||||
|
curY = 0;
|
||||||
|
activeSegmentsInOrder.clear();
|
||||||
idx += 2;
|
idx += 2;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -816,6 +828,26 @@ public class GocaDecoder {
|
|||||||
int endSeg = (idx + 7 < end) ? (((data[idx + 6] & 0xFF) << 8) | (data[idx + 7] & 0xFF)) : startSeg;
|
int endSeg = (idx + 7 < end) ? (((data[idx + 6] & 0xFF) << 8) | (data[idx + 7] & 0xFF)) : startSeg;
|
||||||
logger.info(String.format("GOCA P_SCUDEF: flags0=0x%02x flags1=0x%02x startSeg=%d endSeg=%d",
|
logger.info(String.format("GOCA P_SCUDEF: flags0=0x%02x flags1=0x%02x startSeg=%d endSeg=%d",
|
||||||
flags0, flags1, startSeg, endSeg));
|
flags0, flags1, startSeg, endSeg));
|
||||||
|
// Redraw all stored base segments not in dynamic range [startSeg..endSeg]
|
||||||
|
for (int segId : segmentOrderList) {
|
||||||
|
if (segId < startSeg || segId > endSeg) {
|
||||||
|
if (!chainedTargets.contains(segId)) {
|
||||||
|
byte[] segBytes = segmentStore.get(segId);
|
||||||
|
if (segBytes != null) {
|
||||||
|
int savedSegId = currentSegId;
|
||||||
|
currentSegId = segId;
|
||||||
|
SegmentBounds sb = segmentBoundsMap.computeIfAbsent(segId, SegmentBounds::new);
|
||||||
|
activeSegmentsInOrder.remove(sb);
|
||||||
|
activeSegmentsInOrder.add(sb);
|
||||||
|
callDepth++;
|
||||||
|
decodeStreamDirect(segBytes, 0, segBytes.length);
|
||||||
|
callDepth--;
|
||||||
|
currentSegId = savedSegId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (int s = startSeg; s <= endSeg; s++) {
|
for (int s = startSeg; s <= endSeg; s++) {
|
||||||
byte[] segBytes = segmentStore.get(s);
|
byte[] segBytes = segmentStore.get(s);
|
||||||
if (segBytes != null) {
|
if (segBytes != null) {
|
||||||
|
|||||||
@@ -35,31 +35,19 @@ public class GraphicInputBuilder {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds the 56-byte Graphic Input Structured Field with picked segment ID and correlation tag.
|
* Builds the 56-byte Graphic Input Structured Field with picked segment ID and correlation tag.
|
||||||
*
|
|
||||||
* IMPORTANT ARCHITECTURE NOTE:
|
|
||||||
* Per IBM GA23-0059 / GDDM specifications:
|
|
||||||
* - Bytes 0-1: Total structured field length (0x0038 = 56 bytes).
|
|
||||||
* - Bytes 24-27: (gocaX, gocaY) cursor coordinates.
|
|
||||||
* - Bytes 28-31: Picked Segment Identifier (32-bit big-endian). When clicking on a menu item or
|
|
||||||
* interactive element (e.g. DRAW button = Segment 2, EXIT button = Segment 5), GDDM requires
|
|
||||||
* the exact segment ID in bytes 28-31. If mismatched, GDDM rejects the click with a WCC 0xF7 alarm beep.
|
|
||||||
* - Bytes 32-33: Pick Correlation Tag (16-bit big-endian) set by G_GSETAG (0x39).
|
|
||||||
* - Byte 34: Modifier flags (Shift = 0x80, Ctrl = 0x40, 0xFF for keyboard AID).
|
|
||||||
* - Byte 35: Button ID (0x01 = Button 1 / Pick, 0x02 = Button 2 / Action) or 3270 AID code.
|
|
||||||
*
|
|
||||||
* @param gocaX GOCA signed X coordinate (-xMax..+xMax)
|
|
||||||
* @param gocaY GOCA signed Y coordinate (-yMax..+yMax)
|
|
||||||
* @param buttonOrAidCode The mouse button number (1=Pick, 2=Action) or 3270 AID code for keyboard
|
|
||||||
* @param isMouseAction true if triggered directly by mouse button press, false for keyboard AID
|
|
||||||
* @param isShift true if shift key was down
|
|
||||||
* @param isCtrl true if ctrl key was down
|
|
||||||
* @param pickedSegId Picked GOCA segment ID (0 if none)
|
|
||||||
* @param pickTag Pick correlation tag
|
|
||||||
* @return 56-byte payload
|
|
||||||
*/
|
*/
|
||||||
public static byte[] buildGraphicInput(int gocaX, int gocaY, int buttonOrAidCode,
|
public static byte[] buildGraphicInput(int gocaX, int gocaY, int buttonOrAidCode,
|
||||||
boolean isMouseAction, boolean isShift, boolean isCtrl,
|
boolean isMouseAction, boolean isShift, boolean isCtrl,
|
||||||
int pickedSegId, int pickTag) {
|
int pickedSegId, int pickTag) {
|
||||||
|
return buildGraphicInput(gocaX, gocaY, 0, 0, buttonOrAidCode, isMouseAction, isShift, isCtrl, pickedSegId, pickTag);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the 56-byte Graphic Input Structured Field with cursor row/col, picked segment ID and correlation tag.
|
||||||
|
*/
|
||||||
|
public static byte[] buildGraphicInput(int gocaX, int gocaY, int row, int col, int buttonOrAidCode,
|
||||||
|
boolean isMouseAction, boolean isShift, boolean isCtrl,
|
||||||
|
int pickedSegId, int pickTag) {
|
||||||
byte[] sf = new byte[MASK.length];
|
byte[] sf = new byte[MASK.length];
|
||||||
System.arraycopy(MASK, 0, sf, 0, MASK.length);
|
System.arraycopy(MASK, 0, sf, 0, MASK.length);
|
||||||
|
|
||||||
@@ -67,6 +55,14 @@ public class GraphicInputBuilder {
|
|||||||
sf[0] = (byte) ((MASK.length >> 8) & 0xFF);
|
sf[0] = (byte) ((MASK.length >> 8) & 0xFF);
|
||||||
sf[1] = (byte) (MASK.length & 0xFF);
|
sf[1] = (byte) (MASK.length & 0xFF);
|
||||||
|
|
||||||
|
// Bytes 16-19: Cursor Row & Column
|
||||||
|
if (row > 0 || col > 0) {
|
||||||
|
sf[16] = (byte) (row & 0xFF);
|
||||||
|
sf[17] = (byte) (col & 0xFF);
|
||||||
|
sf[18] = (byte) (row & 0xFF);
|
||||||
|
sf[19] = (byte) (col & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
// Byte 24-25: GOCA X coordinate (signed 16-bit big-endian)
|
// Byte 24-25: GOCA X coordinate (signed 16-bit big-endian)
|
||||||
sf[24] = (byte) ((gocaX >> 8) & 0xFF);
|
sf[24] = (byte) ((gocaX >> 8) & 0xFF);
|
||||||
sf[25] = (byte) (gocaX & 0xFF);
|
sf[25] = (byte) (gocaX & 0xFF);
|
||||||
|
|||||||
@@ -115,11 +115,6 @@ public class GraphicsPlane {
|
|||||||
public synchronized void setScreenDimensions(int cols, int rows) {
|
public synchronized void setScreenDimensions(int cols, int rows) {
|
||||||
this.screenCols = cols > 0 ? cols : 80;
|
this.screenCols = cols > 0 ? cols : 80;
|
||||||
this.screenRows = rows > 0 ? rows : 24;
|
this.screenRows = rows > 0 ? rows : 24;
|
||||||
int targetW = this.screenCols * 9;
|
|
||||||
int targetH = this.screenRows * 12;
|
|
||||||
if (this.canvasWidth != targetW || this.canvasHeight != targetH) {
|
|
||||||
resize(targetW, targetH);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getScreenCols() {
|
public int getScreenCols() {
|
||||||
@@ -130,29 +125,50 @@ public class GraphicsPlane {
|
|||||||
return screenRows;
|
return screenRows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getTotalWidth() {
|
||||||
|
int cols = screenCols > 0 ? screenCols : 80;
|
||||||
|
return cols * 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTotalHeight() {
|
||||||
|
int rows = screenRows > 0 ? screenRows : 24;
|
||||||
|
return rows * 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getXMax() {
|
||||||
|
int totalW = getTotalWidth();
|
||||||
|
return (totalW - 1) / 2 + (totalW - 1) % 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getYMax() {
|
||||||
|
int totalH = getTotalHeight();
|
||||||
|
return (totalH - 1) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps a 3179G / GOCA signed coordinate (centered at screen midpoint) to canvas pixel X as a double.
|
* Maps a 3179G / GOCA signed coordinate (centered at screen midpoint) to canvas pixel X as a double.
|
||||||
|
* IBM 3179G / HOD presentation space coordinate range is [-xMax .. +xMax] (width = cols * 9).
|
||||||
*/
|
*/
|
||||||
public double mapXDouble(double gocaX) {
|
public double mapXDouble(double gocaX) {
|
||||||
int nominalWidth = screenCols * 9;
|
int totalW = getTotalWidth();
|
||||||
int xMax = (nominalWidth - 1) / 2 + ((nominalWidth - 1) % 2 != 0 ? 1 : 0);
|
int xMax = getXMax();
|
||||||
double nx = gocaX + xMax;
|
double nx = gocaX + xMax;
|
||||||
return (nx * canvasWidth) / (double) (nominalWidth > 0 ? nominalWidth : 1);
|
return (nx * canvasWidth) / (double) totalW;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps a 3179G / GOCA signed coordinate (centered at screen midpoint, bottom-up) to canvas pixel Y (top-down) as a double.
|
* Maps a 3179G / GOCA signed coordinate (centered at screen midpoint, bottom-up) to canvas pixel Y (top-down) as a double.
|
||||||
|
* IBM 3179G / HOD presentation space coordinate range is [-yMax .. +yMax] (height = rows * 12).
|
||||||
*/
|
*/
|
||||||
public double mapYDouble(double gocaY) {
|
public double mapYDouble(double gocaY) {
|
||||||
int nominalHeight = screenRows * 12;
|
int totalH = getTotalHeight();
|
||||||
int yMax = (nominalHeight - 1) / 2;
|
int yMax = getYMax();
|
||||||
double ny = yMax - gocaY;
|
double ny = yMax - gocaY;
|
||||||
return (ny * canvasHeight) / (double) (nominalHeight > 0 ? nominalHeight : 1);
|
return (ny * canvasHeight) / (double) totalH;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps a 3179G / GOCA signed coordinate (centered at screen midpoint) to canvas pixel X.
|
* Maps a 3179G / GOCA signed coordinate (centered at screen midpoint) to canvas pixel X.
|
||||||
* Coordinate space is symmetric: -xMax to +xMax, where nominalWidth = cols * 9 (e.g. 720 for 80 cols).
|
|
||||||
*/
|
*/
|
||||||
public int mapX(int gocaX) {
|
public int mapX(int gocaX) {
|
||||||
return (int) Math.round(mapXDouble((double) gocaX));
|
return (int) Math.round(mapXDouble((double) gocaX));
|
||||||
@@ -160,8 +176,6 @@ public class GraphicsPlane {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps a 3179G / GOCA signed coordinate (centered at screen midpoint, bottom-up) to canvas pixel Y (top-down).
|
* Maps a 3179G / GOCA signed coordinate (centered at screen midpoint, bottom-up) to canvas pixel Y (top-down).
|
||||||
* Coordinate space is symmetric: -yMax to +yMax, where nominalHeight = rows * 12 (e.g. 516 for 43 rows).
|
|
||||||
* NOTE: Do not apply arbitrary offsets here. The GOCA coordinate system is 1:1 synchronized with host GDDM.
|
|
||||||
*/
|
*/
|
||||||
public int mapY(int gocaY) {
|
public int mapY(int gocaY) {
|
||||||
return (int) Math.round(mapYDouble((double) gocaY));
|
return (int) Math.round(mapYDouble((double) gocaY));
|
||||||
@@ -172,20 +186,20 @@ public class GraphicsPlane {
|
|||||||
* Invariant: unmapX(mapX(x)) == x for all valid canvas pixels.
|
* Invariant: unmapX(mapX(x)) == x for all valid canvas pixels.
|
||||||
*/
|
*/
|
||||||
public int unmapX(int px) {
|
public int unmapX(int px) {
|
||||||
int nominalWidth = screenCols * 9;
|
int totalW = getTotalWidth();
|
||||||
int xMax = (nominalWidth - 1) / 2 + ((nominalWidth - 1) % 2 != 0 ? 1 : 0);
|
int xMax = getXMax();
|
||||||
int nx = (int) Math.round((double) px * nominalWidth / (canvasWidth > 0 ? canvasWidth : 1));
|
int nx = (int) Math.round((double) px * totalW / (canvasWidth > 0 ? canvasWidth : 1));
|
||||||
return nx - xMax;
|
return nx - xMax;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps a canvas pixel Y coordinate (top-down) back to GOCA signed coordinate (bottom-up).
|
* Maps a canvas pixel Y coordinate (top-down) back to GOCA signed coordinate (bottom-up, -yMax..+yMax).
|
||||||
* Invariant: unmapY(mapY(y)) == y for all valid canvas pixels.
|
* Invariant: unmapY(mapY(y)) == y for all valid canvas pixels.
|
||||||
*/
|
*/
|
||||||
public int unmapY(int py) {
|
public int unmapY(int py) {
|
||||||
int nominalHeight = screenRows * 12;
|
int totalH = getTotalHeight();
|
||||||
int yMax = (nominalHeight - 1) / 2;
|
int yMax = getYMax();
|
||||||
int ny = (int) Math.round((double) py * nominalHeight / (canvasHeight > 0 ? canvasHeight : 1));
|
int ny = (int) Math.round((double) py * totalH / (canvasHeight > 0 ? canvasHeight : 1));
|
||||||
return yMax - ny;
|
return yMax - ny;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -324,12 +324,27 @@ public class InputProcessor {
|
|||||||
int gy = gocaDecoder.getGraphicCursorY();
|
int gy = gocaDecoder.getGraphicCursorY();
|
||||||
int pickedSeg = gocaDecoder.findPickedSegment(gx, gy);
|
int pickedSeg = gocaDecoder.findPickedSegment(gx, gy);
|
||||||
int pickTag = gocaDecoder.getSegmentTag(pickedSeg);
|
int pickTag = gocaDecoder.getSegmentTag(pickedSeg);
|
||||||
|
int cursorAddr = screen != null ? screen.getCursorAddress() : 0;
|
||||||
|
int cols = (screen != null && screen.getCols() > 0) ? screen.getCols() : 80;
|
||||||
|
int row = cursorAddr / cols;
|
||||||
|
int col = cursorAddr % cols;
|
||||||
|
|
||||||
|
if (gocaDecoder.getGraphicsPlane() != null) {
|
||||||
|
int px = gocaDecoder.getGraphicsPlane().mapX(gx);
|
||||||
|
int py = gocaDecoder.getGraphicsPlane().mapY(gy);
|
||||||
|
int canvasW = gocaDecoder.getGraphicsPlane().getCanvasWidth();
|
||||||
|
int canvasH = gocaDecoder.getGraphicsPlane().getCanvasHeight();
|
||||||
|
int numRows = (screen != null && screen.getRows() > 0) ? screen.getRows() : 43;
|
||||||
|
if (canvasH > 0) row = (py * numRows) / canvasH;
|
||||||
|
if (canvasW > 0) col = (px * cols) / canvasW;
|
||||||
|
}
|
||||||
|
|
||||||
byte[] sf = haus.nightmare.lib3270j.graphics.GraphicInputBuilder.buildGraphicInput(
|
byte[] sf = haus.nightmare.lib3270j.graphics.GraphicInputBuilder.buildGraphicInput(
|
||||||
gx, gy, button, true, isShift, isCtrl, pickedSeg, pickTag
|
gx, gy, row, col, button, true, isShift, isCtrl, pickedSeg, pickTag
|
||||||
);
|
);
|
||||||
System.err.println(String.format(
|
System.err.println(String.format(
|
||||||
"sendGraphicMouseAid: goca=(%d, %d) pickedSeg=%d pickTag=%d btn=%d shift=%b ctrl=%b",
|
"sendGraphicMouseAid: goca=(%d, %d) row=%d col=%d pickedSeg=%d pickTag=%d btn=%d shift=%b ctrl=%b",
|
||||||
gx, gy, pickedSeg, pickTag, button, isShift, isCtrl
|
gx, gy, row, col, pickedSeg, pickTag, button, isShift, isCtrl
|
||||||
));
|
));
|
||||||
out.write(AID_SF);
|
out.write(AID_SF);
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -218,22 +218,22 @@ public class GocaDecoderTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test3179GCoordinateMapping() {
|
public void test3179GCoordinateMapping() {
|
||||||
GraphicsPlane plane = new GraphicsPlane(720, 516);
|
GraphicsPlane plane = new GraphicsPlane(1000, 750);
|
||||||
plane.setScreenDimensions(80, 43);
|
plane.setScreenDimensions(80, 43);
|
||||||
|
|
||||||
// Screen center (0, 0) should map to canvas center (360, 257)
|
// Screen center (0, 0) should map to canvas center
|
||||||
assertEquals(360, plane.mapX(0));
|
assertEquals(500, plane.mapX(0));
|
||||||
assertEquals(257, plane.mapY(0));
|
assertEquals(374, plane.mapY(0));
|
||||||
|
|
||||||
// Left edge (-360) should map to 0
|
// Left edge (-xMax) should map to 0
|
||||||
assertEquals(0, plane.mapX(-360));
|
assertEquals(0, plane.mapX(-plane.getXMax()));
|
||||||
// Right edge (+359) should map to 719
|
// Right edge (+xMax) should map to 1000
|
||||||
assertEquals(719, plane.mapX(359));
|
assertEquals(1000, plane.mapX(plane.getXMax()));
|
||||||
|
|
||||||
// Top edge (+257) should map to 0
|
// Top edge (+yMax) should map to 0
|
||||||
assertEquals(0, plane.mapY(257));
|
assertEquals(0, plane.mapY(plane.getYMax()));
|
||||||
// Bottom edge (-258) should map to 515
|
// Bottom edge (yMax - totalHeight) should map to 750
|
||||||
assertEquals(515, plane.mapY(-258));
|
assertEquals(750, plane.mapY(plane.getYMax() - plane.getTotalHeight()));
|
||||||
|
|
||||||
// Bidirectional roundtrip mapping must be exact
|
// Bidirectional roundtrip mapping must be exact
|
||||||
assertEquals(0, plane.unmapX(plane.mapX(0)));
|
assertEquals(0, plane.unmapX(plane.mapX(0)));
|
||||||
@@ -512,7 +512,7 @@ public class GocaDecoderTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMultiPolygonAreaFilling() {
|
public void testMultiPolygonAreaFilling() {
|
||||||
GraphicsPlane plane = new GraphicsPlane(200, 200);
|
GraphicsPlane plane = new GraphicsPlane(1000, 750);
|
||||||
GocaDecoder decoder = new GocaDecoder(plane);
|
GocaDecoder decoder = new GocaDecoder(plane);
|
||||||
|
|
||||||
// Sequence: GBAR (0x68) short form 0x80 (bounded, always filled in GOCA)
|
// Sequence: GBAR (0x68) short form 0x80 (bounded, always filled in GOCA)
|
||||||
@@ -786,11 +786,18 @@ public class GocaDecoderTest {
|
|||||||
}
|
}
|
||||||
assertTrue(foundBlue, "Slide body must contain stippled blue pixels");
|
assertTrue(foundBlue, "Slide body must contain stippled blue pixels");
|
||||||
|
|
||||||
// Verify that the boundary (e.g. at (10, 50)) is drawn in White!
|
// Verify that the boundary (e.g. at X=10) is drawn in White!
|
||||||
int whiteArgb = GocaConstants.GOCA_COLORS[7];
|
int whiteArgb = GocaConstants.GOCA_COLORS[7];
|
||||||
int borderX = plane.mapX(10);
|
int borderX = plane.mapX(10);
|
||||||
int borderY = plane.mapY(50);
|
boolean foundWhite = false;
|
||||||
int borderPixel = plane.getRgbBuffer()[borderY * plane.getCanvasWidth() + borderX];
|
int y1 = Math.min(plane.mapY(10), plane.mapY(90));
|
||||||
assertEquals(whiteArgb, borderPixel, "Slide border outline must be drawn in White");
|
int y2 = Math.max(plane.mapY(10), plane.mapY(90));
|
||||||
|
for (int y = y1; y <= y2; y++) {
|
||||||
|
if (plane.getRgbBuffer()[y * plane.getCanvasWidth() + borderX] == whiteArgb) {
|
||||||
|
foundWhite = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertTrue(foundWhite, "Slide border outline must be drawn in White");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user