845 lines
19 KiB
Java
845 lines
19 KiB
Java
package inf101.v18.gfx.textmode;
|
||
|
||
import java.util.ArrayList;
|
||
import java.util.Arrays;
|
||
import java.util.List;
|
||
import java.util.function.BiFunction;
|
||
|
||
import inf101.v18.gfx.IPaintLayer;
|
||
import inf101.v18.gfx.Screen;
|
||
import javafx.scene.canvas.Canvas;
|
||
import javafx.scene.canvas.GraphicsContext;
|
||
import javafx.scene.effect.BlendMode;
|
||
import javafx.scene.paint.Color;
|
||
import javafx.scene.paint.Paint;
|
||
|
||
public class Printer implements IPaintLayer {
|
||
private static class Char {
|
||
public int mode;
|
||
public String s;
|
||
public Color fill;
|
||
public Color stroke;
|
||
public Paint bg;
|
||
|
||
public Char(String s, Color fill, Color stroke, Paint bg, int mode) {
|
||
this.s = s;
|
||
this.fill = fill;
|
||
this.stroke = stroke;
|
||
this.bg = bg;
|
||
this.mode = mode;
|
||
}
|
||
}
|
||
|
||
public static final TextFont FONT_MONOSPACED = new TextFont("Monospaced", 27.00, TextMode.CHAR_BOX_SIZE, 3.4000,
|
||
-6.7000, 1.5000, 1.0000, true);
|
||
public static final TextFont FONT_LMMONO = new TextFont("lmmono10-regular.otf", 30.00, TextMode.CHAR_BOX_SIZE,
|
||
4.0000, -8.5000, 1.5000, 1.0000, true);
|
||
public static final TextFont FONT_ZXSPECTRUM7 = new TextFont("ZXSpectrum-7.otf", 22.00, TextMode.CHAR_BOX_SIZE,
|
||
3.1000, -3.8000, 1.0000, 1.0000, true);
|
||
|
||
/**
|
||
* TTF file can be found here: http://users.teilar.gr/~g1951d/ in this ZIP file:
|
||
* http://users.teilar.gr/~g1951d/Symbola.zip
|
||
* <p>
|
||
* (Put the extracted Symbola.ttf in src/inf101/v18/gfx/fonts/)
|
||
*/
|
||
public static final TextFont FONT_SYMBOLA = new TextFont("Symbola.ttf", 26.70, TextMode.CHAR_BOX_SIZE, -0.4000,
|
||
-7.6000, 1.35000, 1.0000, true);
|
||
|
||
/**
|
||
* TTF file can be found here:
|
||
* http://www.kreativekorp.com/software/fonts/c64.shtml
|
||
*/
|
||
public static final TextFont FONT_GIANA = new TextFont("Giana.ttf", 25.00, TextMode.CHAR_BOX_SIZE, 4.6000, -5.0000,
|
||
1.0000, 1.0000, true);
|
||
|
||
/**
|
||
* TTF file can be found here:
|
||
* http://www.kreativekorp.com/software/fonts/c64.shtml
|
||
*/
|
||
public static final TextFont FONT_C64 = new TextFont("PetMe64.ttf", 31.50, TextMode.CHAR_BOX_SIZE, 0.0000, -4.000,
|
||
1.0000, 1.0000, true);
|
||
private static final Paint DEFAULT_BACKGROUND = Color.TRANSPARENT;
|
||
private static final TextMode DEFAULT_MODE = TextMode.MODE_40X22;
|
||
private static final boolean DEBUG_REDRAW = false;
|
||
|
||
public static String center(String s, int width) {
|
||
for (; s.length() < width; s = " " + s + " ")
|
||
;
|
||
return s;
|
||
}
|
||
|
||
public static String repeat(String s, int width) {
|
||
String r = s;
|
||
for (; r.length() < width; r += s)
|
||
;
|
||
return r;
|
||
}
|
||
|
||
Color DEFAULT_FILL = Color.WHITE;
|
||
|
||
Color DEFAULT_STROKE = Color.TRANSPARENT;
|
||
|
||
private TextMode textMode;
|
||
private Color fill;
|
||
private Color stroke;
|
||
private Paint background;
|
||
private Screen screen;
|
||
private List<Char[]> lineBuffer = new ArrayList<>();
|
||
private boolean autoscroll = true;
|
||
private final Canvas textPage;
|
||
private int x = 1, y = 1, savedX = 1, savedY = 1;
|
||
// private int pageWidth = LINE_WIDTHS[resMode], pageHeight =
|
||
// PAGE_HEIGHTS[resMode];
|
||
private int leftMargin = 1, topMargin = 1;
|
||
private TextFont font = FONT_SYMBOLA;
|
||
private int videoAttrs = 0;
|
||
|
||
private String csiSeq = null;
|
||
|
||
private boolean csiEnabled = true;
|
||
|
||
private int csiMode = 0;
|
||
|
||
private final double width;
|
||
private final double height;
|
||
|
||
private int dirtyX0 = Integer.MAX_VALUE;
|
||
private int dirtyX1 = Integer.MIN_VALUE;
|
||
private int dirtyY0 = Integer.MAX_VALUE;
|
||
private int dirtyY1 = Integer.MIN_VALUE;
|
||
private boolean useBuffer = true;
|
||
|
||
public Printer(double width, double height) {
|
||
this.screen = null;
|
||
this.textPage = null;
|
||
this.width = width;
|
||
this.height = height;
|
||
for (int i = 0; i < TextMode.PAGE_HEIGHT_MAX; i++) {
|
||
lineBuffer.add(new Char[TextMode.LINE_WIDTH_MAX]);
|
||
}
|
||
resetFull();
|
||
}
|
||
|
||
public Printer(Screen screen, Canvas page) {
|
||
this.screen = screen;
|
||
this.textPage = page;
|
||
this.width = page.getWidth();
|
||
this.height = page.getHeight();
|
||
for (int i = 0; i < TextMode.PAGE_HEIGHT_MAX; i++) {
|
||
lineBuffer.add(new Char[TextMode.LINE_WIDTH_MAX]);
|
||
}
|
||
resetFull();
|
||
}
|
||
|
||
public void addToCharBuffer(String string) {
|
||
string.codePoints().mapToObj((int i) -> String.valueOf(Character.toChars(i))).forEach((String s) -> {
|
||
if (csiMode != 0) {
|
||
s = addToCsiBuffer(s);
|
||
}
|
||
switch (s) {
|
||
case "\r":
|
||
moveTo(leftMargin, y);
|
||
break;
|
||
case "\n":
|
||
moveTo(leftMargin, y + 1);
|
||
break;
|
||
case "\f":
|
||
moveTo(leftMargin, topMargin);
|
||
for (Char[] line : lineBuffer)
|
||
Arrays.fill(line, null);
|
||
if(textPage != null) {
|
||
GraphicsContext context = textPage.getGraphicsContext2D();
|
||
if (background != null && background != Color.TRANSPARENT) {
|
||
context.setFill(background);
|
||
context.fillRect(0.0, 0.0, textPage.getWidth(), textPage.getHeight());
|
||
} else
|
||
context.clearRect(0.0, 0.0, textPage.getWidth(), textPage.getHeight());
|
||
}
|
||
break;
|
||
case "\b":
|
||
moveHoriz(-1);
|
||
break;
|
||
case "\t":
|
||
moveTo((x + 8) % 8, y);
|
||
break;
|
||
case "\u001b":
|
||
if (csiEnabled) {
|
||
csiSeq = s;
|
||
csiMode = 1;
|
||
}
|
||
break;
|
||
default:
|
||
if (s.length() > 0 && s.codePointAt(0) >= 0x20) {
|
||
drawChar(x, y, setChar(x, y, s));
|
||
moveHoriz(1);
|
||
}
|
||
break;
|
||
}
|
||
});
|
||
}
|
||
|
||
private String addToCsiBuffer(String s) {
|
||
if (csiMode == 1) {
|
||
switch (s) {
|
||
case "[":
|
||
csiMode = 2;
|
||
csiSeq += s;
|
||
break;
|
||
case "c":
|
||
csiMode = 0;
|
||
resetFull();
|
||
break;
|
||
default:
|
||
csiReset();
|
||
return s;
|
||
}
|
||
} else if (csiMode == 2) {
|
||
int c = s.codePointAt(0);
|
||
if (c >= 0x30 && c <= 0x3f) {
|
||
csiSeq += s;
|
||
} else if (c >= 0x20 && c <= 0x2f) {
|
||
csiMode = 3;
|
||
csiSeq += s;
|
||
} else if (c >= 0x40 && c <= 0x7e) {
|
||
csiSeq += s;
|
||
csiFinish();
|
||
} else {
|
||
csiReset();
|
||
return s;
|
||
}
|
||
|
||
} else if (csiMode == 3) {
|
||
int c = s.codePointAt(0);
|
||
if (c >= 0x20 && c <= 0x2f) {
|
||
csiSeq += s;
|
||
} else if (c >= 0x40 && c <= 0x7e) {
|
||
csiSeq += s;
|
||
csiFinish();
|
||
} else {
|
||
csiReset();
|
||
return s;
|
||
}
|
||
}
|
||
return "";
|
||
}
|
||
|
||
public void beginningOfLine() {
|
||
x = leftMargin;
|
||
}
|
||
|
||
public void beginningOfPage() {
|
||
x = leftMargin;
|
||
y = topMargin;
|
||
}
|
||
|
||
@Override
|
||
public void clear() {
|
||
print("\f");
|
||
}
|
||
|
||
public void clearAt(int x, int y) {
|
||
printAt(x, y, " ");
|
||
}
|
||
|
||
public void clearLine(int y) {
|
||
y = constrainY(y);
|
||
if (y > 0 && y <= TextMode.PAGE_HEIGHT_MAX) {
|
||
Arrays.fill(lineBuffer.get(y - 1), null);
|
||
dirty(1, y);
|
||
dirty(getLineWidth(), y);
|
||
if (!useBuffer)
|
||
redrawDirty();
|
||
}
|
||
}
|
||
|
||
public void clearRegion(int x, int y, int width, int height) {
|
||
if (x > getLineWidth() || y > getPageHeight())
|
||
return;
|
||
int x2 = Math.min(x + width - 1, getLineWidth());
|
||
int y2 = Math.min(y + height - 1, getPageHeight());
|
||
if (x2 < 1 || y2 < 1)
|
||
return;
|
||
int x1 = Math.max(1, x);
|
||
int y1 = Math.max(1, y);
|
||
// Char fillWith = new Char("*", Color.BLACK, Color.GREEN, Color.TRANSPARENT,
|
||
// 0);
|
||
for (int i = y1; i <= y2; i++) {
|
||
Arrays.fill(lineBuffer.get(i - 1), x1 - 1, x2, null);
|
||
}
|
||
dirty(x1, y1);
|
||
dirty(x2, y2);
|
||
if (!useBuffer)
|
||
redrawDirty();
|
||
}
|
||
|
||
private int constrainX(int x) {
|
||
return x; // Math.min(LINE_WIDTH_HIRES, Math.max(1, x));
|
||
}
|
||
|
||
public int constrainY(int y) {
|
||
return y; // Math.min(pageHeight, Math.max(1, y));
|
||
}
|
||
|
||
public int constrainYOrScroll(int y) {
|
||
if (autoscroll) {
|
||
if (y < 1) {
|
||
scroll(y - 1);
|
||
return 1;
|
||
} else if (y > getPageHeight()) {
|
||
scroll(y - getPageHeight());
|
||
return getPageHeight();
|
||
}
|
||
}
|
||
|
||
return y;// Math.min(pageHeight, Math.max(1, y));
|
||
}
|
||
|
||
private void csiFinish() {
|
||
ControlSequences.applyCsi(this, csiSeq);
|
||
csiReset();
|
||
}
|
||
|
||
private void csiReset() {
|
||
csiMode = 0;
|
||
csiSeq = null;
|
||
}
|
||
|
||
public void cycleMode(boolean adjustDisplayAspect) {
|
||
textMode = textMode.nextMode();
|
||
if (adjustDisplayAspect && screen != null)
|
||
screen.setAspect(textMode.getAspect());
|
||
dirty(1, 1);
|
||
dirty(getLineWidth(), getPageHeight());
|
||
if (!useBuffer)
|
||
redrawDirty();
|
||
}
|
||
|
||
private void drawChar(int x, int y, Char c) {
|
||
if (useBuffer) {
|
||
dirty(x, y);
|
||
} else if (c != null && textPage != null) {
|
||
GraphicsContext context = textPage.getGraphicsContext2D();
|
||
|
||
context.setFill(c.fill);
|
||
context.setStroke(c.stroke);
|
||
font.drawTextAt(context, (x - 1) * getCharWidth(), y * getCharHeight(), c.s,
|
||
textMode.getCharWidth() / textMode.getCharBoxSize(), c.mode, c.bg);
|
||
}
|
||
}
|
||
|
||
public void drawCharCells() {
|
||
if (screen != null) {
|
||
GraphicsContext context = screen.getBackgroundContext();
|
||
screen.clearBackground();
|
||
double w = getCharWidth();
|
||
double h = getCharHeight();
|
||
context.save();
|
||
context.setGlobalBlendMode(BlendMode.EXCLUSION);
|
||
context.setFill(Color.WHITE.deriveColor(0.0, 1.0, 1.0, 0.1));
|
||
for (int x = 0; x < getLineWidth(); x++) {
|
||
for (int y = 0; y < getPageHeight(); y++) {
|
||
if ((x + y) % 2 == 0)
|
||
context.fillRect(x * w, y * h, w, h);
|
||
}
|
||
}
|
||
context.restore();
|
||
}
|
||
}
|
||
|
||
public Color getBackground(int x, int y) {
|
||
Char c = null;
|
||
if (x > 0 && x <= TextMode.LINE_WIDTH_MAX && y > 0 && y <= TextMode.PAGE_HEIGHT_MAX) {
|
||
c = lineBuffer.get(y - 1)[x - 1];
|
||
}
|
||
Color bg = Color.TRANSPARENT;
|
||
if (c != null && c.bg instanceof Color)
|
||
bg = (Color) c.bg;
|
||
else if (background instanceof Color)
|
||
bg = (Color) background;
|
||
return bg;
|
||
}
|
||
|
||
public boolean getBold() {
|
||
return (videoAttrs & TextFont.ATTR_BRIGHT) != 0;
|
||
}
|
||
|
||
public String getChar(int x, int y) {
|
||
Char c = null;
|
||
if (x > 0 && x <= TextMode.LINE_WIDTH_MAX && y > 0 && y <= TextMode.PAGE_HEIGHT_MAX) {
|
||
c = lineBuffer.get(y - 1)[x - 1];
|
||
}
|
||
if (c != null)
|
||
return c.s;
|
||
else
|
||
return " ";
|
||
}
|
||
|
||
public double getCharHeight() {
|
||
return textMode.getCharHeight();
|
||
}
|
||
|
||
public double getCharWidth() {
|
||
return textMode.getCharWidth();
|
||
}
|
||
|
||
public Color getColor(int x, int y) {
|
||
Char c = null;
|
||
if (x > 0 && x <= TextMode.LINE_WIDTH_MAX && y > 0 && y <= TextMode.PAGE_HEIGHT_MAX) {
|
||
c = lineBuffer.get(y - 1)[x - 1];
|
||
}
|
||
if (c != null)
|
||
return c.fill;
|
||
else
|
||
return fill;
|
||
}
|
||
|
||
public TextFont getFont() {
|
||
return font;
|
||
}
|
||
|
||
public boolean getItalics() {
|
||
return (videoAttrs & TextFont.ATTR_ITALIC) != 0;
|
||
}
|
||
|
||
/**
|
||
* @return the leftMargin
|
||
*/
|
||
public int getLeftMargin() {
|
||
return leftMargin;
|
||
}
|
||
|
||
public int getLineWidth() {
|
||
return textMode.getLineWidth();
|
||
}
|
||
|
||
public int getPageHeight() {
|
||
return textMode.getPageHeight();
|
||
}
|
||
|
||
public boolean getReverseVideo() {
|
||
return (videoAttrs & TextFont.ATTR_INVERSE) != 0;
|
||
}
|
||
|
||
public TextMode getTextMode() {
|
||
return textMode;
|
||
}
|
||
|
||
/**
|
||
* @return the topMargin
|
||
*/
|
||
public int getTopMargin() {
|
||
return topMargin;
|
||
}
|
||
|
||
public int getVideoMode() {
|
||
return videoAttrs;
|
||
}
|
||
|
||
public int getX() {
|
||
return x;
|
||
}
|
||
|
||
public int getY() {
|
||
return y;
|
||
}
|
||
|
||
public boolean isFilled(int x, int y) {
|
||
return !getChar(x, y).equals(" ");
|
||
}
|
||
|
||
@Override
|
||
public void layerToBack() {
|
||
if (screen != null) {
|
||
screen.moveToBack(this);
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void layerToFront() {
|
||
if (screen != null) {
|
||
screen.moveToFront(this);
|
||
}
|
||
}
|
||
|
||
public void move(int deltaX, int deltaY) {
|
||
x = constrainX(x + deltaX);
|
||
y = constrainYOrScroll(y + deltaY);
|
||
}
|
||
|
||
public void moveHoriz(int dist) {
|
||
x = constrainX(x + dist);
|
||
}
|
||
|
||
public void moveTo(int newX, int newY) {
|
||
x = constrainX(newX);
|
||
y = constrainYOrScroll(newY);
|
||
}
|
||
|
||
public void moveVert(int dist) {
|
||
y = constrainYOrScroll(y + dist);
|
||
}
|
||
|
||
public void plot(int x, int y) {
|
||
plot(x, y, (a, b) -> a | b);
|
||
}
|
||
|
||
public void plot(int x, int y, BiFunction<Integer, Integer, Integer> op) {
|
||
int textX = (x) / 2 + 1;
|
||
int textY = (y) / 2 + 1;
|
||
int bitPos = (x + 1) % 2 + ((y + 1) % 2) * 2;
|
||
String blockChar = BlocksAndBoxes.unicodeBlocks[1 << bitPos];
|
||
// System.out.println(blockChar + ", " + bitPos + ", ("+ (x) + ", " + (y) + ")"+
|
||
// ", (" + (textX) + ", " + (textY) + ")");
|
||
String s = BlocksAndBoxes.blockComposeOrOverwrite(getChar(textX, textY), blockChar, op);
|
||
// System.out.println("Merge '" + getChar(textX, textY) + "' + '" + blockChar +
|
||
// "' = '" + s + "'");
|
||
printAt(textX, textY, s);
|
||
}
|
||
|
||
public void print(String s) {
|
||
addToCharBuffer(s);
|
||
}
|
||
|
||
public void print(String s, Color paint) {
|
||
Color tmp = fill;
|
||
fill = paint;
|
||
addToCharBuffer(s);
|
||
fill = tmp;
|
||
}
|
||
|
||
public void printAt(int atX, int atY, String s) {
|
||
moveTo(atX, atY);
|
||
print(s);
|
||
}
|
||
|
||
public void printAt(int atX, int atY, String s, Color ink) {
|
||
moveTo(atX, atY);
|
||
print(s, ink);
|
||
}
|
||
|
||
public void println() {
|
||
print("\n");
|
||
}
|
||
|
||
public void println(String s) {
|
||
print(s);
|
||
print("\n");
|
||
}
|
||
|
||
public void redrawTextPage() {
|
||
redrawTextPage(1, 1, getLineWidth(), getPageHeight());
|
||
clean();
|
||
}
|
||
|
||
private void redrawTextPage(int x0, int y0, int x1, int y1) {
|
||
/*
|
||
* System.out.printf("redrawTextPage benchmark");
|
||
* System.out.printf(" %5s %5s %7s %4s %5s %5s %5s%n", "ms", "chars",
|
||
* "ms/char", "mode", "indir", "inv", "fake"); for (int m = -1; m < 8; m++) {
|
||
* long t0 = System.currentTimeMillis(); int n = 0;
|
||
*/
|
||
if(textPage == null)
|
||
return;
|
||
GraphicsContext context = textPage.getGraphicsContext2D();
|
||
|
||
double px0 = (x0 - 1) * getCharWidth(), py0 = (y0 - 1) * getCharHeight();
|
||
double px1 = x1 * getCharWidth(), py1 = y1 * getCharHeight();
|
||
if (DEBUG_REDRAW)
|
||
System.out.printf("redrawTextPage(): Area to clear: (%2f,%2f)–(%2f,%2f)%n", px0, py0, px1, py1);
|
||
if (background != null && background != Color.TRANSPARENT) {
|
||
context.setFill(background);
|
||
context.fillRect(px0, py0, px1 - px0, py1 - py0);
|
||
} else {
|
||
context.clearRect(px0, py0, px1 - px0, py1 - py0);
|
||
}
|
||
for (int tmpY = y0; tmpY <= y1; tmpY++) {
|
||
Char[] line = lineBuffer.get(tmpY - 1);
|
||
for (int tmpX = x0; tmpX <= x1; tmpX++) {
|
||
Char c = line[tmpX - 1];
|
||
if (c != null) {
|
||
context.save();
|
||
context.setFill(c.fill);
|
||
context.setStroke(c.stroke);
|
||
Paint bg = c.bg == background ? null : c.bg;
|
||
font.drawTextNoClearAt(context, (tmpX - 1) * getCharWidth(), tmpY * getCharHeight(), c.s,
|
||
textMode.getCharWidth() / textMode.getCharBoxSize(), c.mode/* m */, bg);
|
||
context.restore();
|
||
// n++;
|
||
|
||
}
|
||
}
|
||
}
|
||
/*
|
||
* long t = System.currentTimeMillis() - t0; if (m >= 0)
|
||
* System.out.printf(" %5d %5d %7.4f %4d %5b %5b %5b%n", t, n, ((double) t) /
|
||
* n, m, (m & 3) != 0, (m & 1) != 0, (m & 4) != 0); } System.out.println();
|
||
*/
|
||
}
|
||
|
||
public void resetAttrs() {
|
||
this.fill = DEFAULT_FILL;
|
||
this.stroke = DEFAULT_STROKE;
|
||
this.background = DEFAULT_BACKGROUND;
|
||
this.videoAttrs = 0;
|
||
this.csiSeq = null;
|
||
this.csiMode = 0;
|
||
}
|
||
|
||
public void resetFull() {
|
||
resetAttrs();
|
||
beginningOfPage();
|
||
this.autoscroll = true;
|
||
this.textMode = DEFAULT_MODE;
|
||
redrawTextPage();
|
||
}
|
||
|
||
public void restoreCursor() {
|
||
x = savedX;
|
||
y = savedY;
|
||
}
|
||
|
||
public void saveCursor() {
|
||
savedX = x;
|
||
savedY = y;
|
||
}
|
||
|
||
void scroll(int i) {
|
||
while (i < 0) {
|
||
scrollDown();
|
||
i++;
|
||
}
|
||
while (i > 0) {
|
||
scrollUp();
|
||
i--;
|
||
}
|
||
}
|
||
|
||
public void scrollDown() {
|
||
Char[] remove = lineBuffer.remove(lineBuffer.size() - 1);
|
||
Arrays.fill(remove, null);
|
||
lineBuffer.add(0, remove);
|
||
dirty(1, 1);
|
||
dirty(getLineWidth(), getPageHeight());
|
||
if (!useBuffer)
|
||
redrawDirty();
|
||
}
|
||
|
||
public void scrollUp() {
|
||
Char[] remove = lineBuffer.remove(0);
|
||
Arrays.fill(remove, null);
|
||
lineBuffer.add(remove);
|
||
dirty(1, 1);
|
||
dirty(getLineWidth(), getPageHeight());
|
||
if (!useBuffer)
|
||
redrawDirty();
|
||
}
|
||
|
||
public boolean setAutoScroll(boolean autoScroll) {
|
||
boolean old = autoscroll;
|
||
autoscroll = autoScroll;
|
||
return old;
|
||
}
|
||
|
||
public void setBackground(int x, int y, Paint bg) {
|
||
Char c = null;
|
||
if (x > 0 && x <= TextMode.LINE_WIDTH_MAX && y > 0 && y <= TextMode.PAGE_HEIGHT_MAX) {
|
||
c = lineBuffer.get(y - 1)[x - 1];
|
||
}
|
||
if (c != null) {
|
||
c.bg = bg;
|
||
drawChar(x, y, c);
|
||
}
|
||
}
|
||
|
||
public void setBackground(Paint bgColor) {
|
||
this.background = bgColor != null ? bgColor : DEFAULT_BACKGROUND;
|
||
}
|
||
|
||
public void setBold(boolean enabled) {
|
||
if (enabled)
|
||
videoAttrs |= TextFont.ATTR_BRIGHT;
|
||
else
|
||
videoAttrs &= ~TextFont.ATTR_BRIGHT;
|
||
}
|
||
|
||
public Char setChar(int x, int y, String s) {
|
||
if (x > 0 && x <= TextMode.LINE_WIDTH_MAX && y > 0 && y <= TextMode.PAGE_HEIGHT_MAX) {
|
||
Char c = new Char(s, fill, stroke, background, videoAttrs);
|
||
lineBuffer.get(y - 1)[x - 1] = c;
|
||
return c;
|
||
}
|
||
return null;
|
||
}
|
||
|
||
public void setColor(int x, int y, Color fill) {
|
||
Char c = null;
|
||
if (x > 0 && x <= TextMode.LINE_WIDTH_MAX && y > 0 && y <= TextMode.PAGE_HEIGHT_MAX) {
|
||
c = lineBuffer.get(y - 1)[x - 1];
|
||
}
|
||
if (c != null) {
|
||
c.fill = fill;
|
||
drawChar(x, y, c);
|
||
}
|
||
}
|
||
|
||
public void setFill(Color fill) {
|
||
this.fill = fill != null ? fill : DEFAULT_FILL;
|
||
}
|
||
|
||
public void setFont(TextFont font) {
|
||
this.font = font;
|
||
}
|
||
|
||
public void setInk(Color ink) {
|
||
fill = ink != null ? ink : DEFAULT_FILL;
|
||
stroke = ink != null ? ink : DEFAULT_STROKE;
|
||
}
|
||
|
||
public void setItalics(boolean enabled) {
|
||
if (enabled)
|
||
videoAttrs |= TextFont.ATTR_ITALIC;
|
||
else
|
||
videoAttrs &= ~TextFont.ATTR_ITALIC;
|
||
}
|
||
|
||
/**
|
||
*/
|
||
public void setLeftMargin() {
|
||
this.leftMargin = x;
|
||
}
|
||
|
||
/**
|
||
* @param leftMargin
|
||
* the leftMargin to set
|
||
*/
|
||
public void setLeftMargin(int leftMargin) {
|
||
this.leftMargin = constrainX(leftMargin);
|
||
}
|
||
|
||
public void setReverseVideo(boolean enabled) {
|
||
if (enabled)
|
||
videoAttrs |= TextFont.ATTR_INVERSE;
|
||
else
|
||
videoAttrs &= ~TextFont.ATTR_INVERSE;
|
||
}
|
||
|
||
public void setStroke(Color stroke) {
|
||
this.stroke = stroke != null ? stroke : DEFAULT_STROKE;
|
||
}
|
||
|
||
public void setTextMode(TextMode mode) {
|
||
setTextMode(mode, false);
|
||
}
|
||
|
||
public void setTextMode(TextMode mode, boolean adjustDisplayAspect) {
|
||
if (mode == null)
|
||
throw new IllegalArgumentException();
|
||
textMode = mode;
|
||
if (adjustDisplayAspect && screen != null)
|
||
screen.setAspect(textMode.getAspect());
|
||
dirty(1, 1);
|
||
dirty(getLineWidth(), getPageHeight());
|
||
if (!useBuffer)
|
||
redrawDirty();
|
||
}
|
||
|
||
public void setTopMargin() {
|
||
this.topMargin = y;
|
||
}
|
||
|
||
/**
|
||
* @param topMargin
|
||
* the topMargin to set
|
||
*/
|
||
public void setTopMargin(int topMargin) {
|
||
this.topMargin = constrainY(topMargin);
|
||
}
|
||
|
||
public void setVideoAttrDisabled(int attr) {
|
||
videoAttrs &= ~attr;
|
||
}
|
||
|
||
public void setVideoAttrEnabled(int attr) {
|
||
videoAttrs |= attr;
|
||
}
|
||
|
||
public void setVideoAttrs(int attr) {
|
||
videoAttrs = attr;
|
||
}
|
||
|
||
public void unplot(int x, int y) {
|
||
plot(x, y, (a, b) -> a & ~b);
|
||
}
|
||
|
||
@Override
|
||
public double getWidth() {
|
||
return width;
|
||
}
|
||
|
||
@Override
|
||
public double getHeight() {
|
||
return height;
|
||
}
|
||
|
||
private boolean isDirty() {
|
||
return dirtyX0 <= dirtyX1 || dirtyY0 <= dirtyY1;
|
||
}
|
||
|
||
/**
|
||
* Expand the dirty region (area that should be redrawn) to include the given
|
||
* position
|
||
*
|
||
* @param x
|
||
* @param y
|
||
*/
|
||
private void dirty(int x, int y) {
|
||
dirtyX0 = Math.max(Math.min(x, dirtyX0), 1);
|
||
dirtyX1 = Math.min(Math.max(x, dirtyX1), getLineWidth());
|
||
dirtyY0 = Math.max(Math.min(y, dirtyY0), 1);
|
||
dirtyY1 = Math.min(Math.max(y, dirtyY1), getPageHeight());
|
||
}
|
||
|
||
/**
|
||
* Redraw the part of the page that has changed since last redraw.
|
||
*/
|
||
public void redrawDirty() {
|
||
if (isDirty()) {
|
||
if (DEBUG_REDRAW)
|
||
System.out.printf("redrawDirty(): Dirty region is (%d,%d)–(%d,%d)%n", dirtyX0, dirtyY0, dirtyX1,
|
||
dirtyY1);
|
||
redrawTextPage(dirtyX0, dirtyY0, dirtyX1, dirtyY1);
|
||
clean();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Mark the entire page as clean
|
||
*/
|
||
private void clean() {
|
||
dirtyX0 = Integer.MAX_VALUE;
|
||
dirtyX1 = Integer.MIN_VALUE;
|
||
dirtyY0 = Integer.MAX_VALUE;
|
||
dirtyY1 = Integer.MIN_VALUE;
|
||
}
|
||
|
||
/**
|
||
* With buffered printing, nothing is actually drawn until
|
||
* {@link #redrawDirty()} or {@link #redrawTextPage()} is called.
|
||
*
|
||
* @param buffering
|
||
* Whether to use buffering
|
||
*/
|
||
public void setBuffering(boolean buffering) {
|
||
useBuffer = buffering;
|
||
}
|
||
|
||
/**
|
||
* @return True if buffering is enabled
|
||
* @see #setBuffering(boolean)
|
||
*/
|
||
public boolean getBuffering() {
|
||
return useBuffer;
|
||
}
|
||
}
|