From a75902faaade579e3ee7fe68b0e18e49148410de Mon Sep 17 00:00:00 2001 From: Kevin Lamonte Date: Sat, 10 Aug 2019 19:09:09 -0500 Subject: [PATCH] expose sixel palette size --- src/jexer/TApplication.java | 4 +- src/jexer/TFontChooserWindow.java | 57 ++++++++++- src/jexer/TFontChooserWindow.properties | 6 +- src/jexer/backend/ECMA48Terminal.java | 126 +++++++++++++++++------- src/jexer/demos/DemoApplication.java | 5 +- src/jexer/menu/TMenu.java | 6 +- src/jexer/menu/TMenu.properties | 2 +- 7 files changed, 161 insertions(+), 45 deletions(-) diff --git a/src/jexer/TApplication.java b/src/jexer/TApplication.java index fdbc0e3..b21c066 100644 --- a/src/jexer/TApplication.java +++ b/src/jexer/TApplication.java @@ -929,7 +929,7 @@ public class TApplication implements Runnable { openImage(); return true; } - if (menu.getId() == TMenu.MID_CHANGE_FONT) { + if (menu.getId() == TMenu.MID_SCREEN_OPTIONS) { new TFontChooserWindow(this); return true; } @@ -3132,7 +3132,7 @@ public class TApplication implements Runnable { TMenu toolMenu = addMenu(i18n.getString("toolMenuTitle")); toolMenu.addDefaultItem(TMenu.MID_REPAINT); toolMenu.addDefaultItem(TMenu.MID_VIEW_IMAGE); - toolMenu.addDefaultItem(TMenu.MID_CHANGE_FONT); + toolMenu.addDefaultItem(TMenu.MID_SCREEN_OPTIONS); TStatusBar toolStatusBar = toolMenu.newStatusBar(i18n. getString("toolMenuStatus")); toolStatusBar.addShortcutKeypress(kbF1, cmHelp, i18n.getString("Help")); diff --git a/src/jexer/TFontChooserWindow.java b/src/jexer/TFontChooserWindow.java index 5878b59..62eabb6 100644 --- a/src/jexer/TFontChooserWindow.java +++ b/src/jexer/TFontChooserWindow.java @@ -35,6 +35,7 @@ import java.util.Arrays; import java.util.List; import java.util.ResourceBundle; +import jexer.backend.ECMA48Terminal; import jexer.backend.SwingTerminal; import jexer.bits.CellAttributes; import jexer.bits.GraphicsChars; @@ -62,6 +63,11 @@ public class TFontChooserWindow extends TWindow { */ private SwingTerminal terminal = null; + /** + * The ECMA48 screen. + */ + private ECMA48Terminal ecmaTerminal = null; + /** * The font name. */ @@ -92,6 +98,11 @@ public class TFontChooserWindow extends TWindow { */ private TField textAdjustWidth; + /** + * The sixel palette size. + */ + private TComboBox sixelPaletteSize; + /** * The original font size. */ @@ -122,6 +133,11 @@ public class TFontChooserWindow extends TWindow { */ private int oldTextAdjustWidth = 0; + /** + * The original sixel palette (number of colors) value. + */ + private int oldSixelPaletteSize = 1024; + // ------------------------------------------------------------------------ // Constructors ----------------------------------------------------------- // ------------------------------------------------------------------------ @@ -134,7 +150,7 @@ public class TFontChooserWindow extends TWindow { public TFontChooserWindow(final TApplication application) { // Register with the TApplication - super(application, i18n.getString("windowTitle"), 0, 0, 60, 18, MODAL); + super(application, i18n.getString("windowTitle"), 0, 0, 60, 21, MODAL); // Add shortcut text newStatusBar(i18n.getString("statusBar")); @@ -142,6 +158,9 @@ public class TFontChooserWindow extends TWindow { if (getScreen() instanceof SwingTerminal) { terminal = (SwingTerminal) getScreen(); } + if (getScreen() instanceof ECMA48Terminal) { + ecmaTerminal = (ECMA48Terminal) getScreen(); + } addLabel(i18n.getString("fontName"), 1, 1, "ttext", false); addLabel(i18n.getString("fontSize"), 1, 2, "ttext", false); @@ -149,8 +168,9 @@ public class TFontChooserWindow extends TWindow { addLabel(i18n.getString("textAdjustY"), 1, 5, "ttext", false); addLabel(i18n.getString("textAdjustHeight"), 1, 6, "ttext", false); addLabel(i18n.getString("textAdjustWidth"), 1, 7, "ttext", false); + addLabel(i18n.getString("sixelPaletteSize"), 1, 9, "ttext", false); - int col = 18; + int col = 21; if (terminal == null) { // Non-Swing case: we can't change anything addLabel(i18n.getString("unavailable"), col, 1); @@ -159,7 +179,32 @@ public class TFontChooserWindow extends TWindow { addLabel(i18n.getString("unavailable"), col, 5); addLabel(i18n.getString("unavailable"), col, 6); addLabel(i18n.getString("unavailable"), col, 7); - } else { + } + if (ecmaTerminal == null) { + addLabel(i18n.getString("unavailable"), col, 9); + } + if (ecmaTerminal != null) { + oldSixelPaletteSize = ecmaTerminal.getSixelPaletteSize(); + + String [] sixelSizes = { "2", "256", "512", "1024", "2048" }; + List sizes = new ArrayList(); + sizes.addAll(Arrays.asList(sixelSizes)); + sixelPaletteSize = addComboBox(col, 9, 10, sizes, 0, 6, + new TAction() { + public void DO() { + try { + ecmaTerminal.setSixelPaletteSize(Integer.parseInt( + sixelPaletteSize.getText())); + } catch (NumberFormatException e) { + // SQUASH + } + } + } + ); + sixelPaletteSize.setText(Integer.toString(oldSixelPaletteSize)); + } + + if (terminal != null) { oldFont = terminal.getFont(); oldFontSize = terminal.getFontSize(); oldTextAdjustX = terminal.getTextAdjustX(); @@ -514,6 +559,9 @@ public class TFontChooserWindow extends TWindow { terminal.setTextAdjustHeight(oldTextAdjustHeight); terminal.setTextAdjustWidth(oldTextAdjustWidth); } + if (ecmaTerminal != null) { + ecmaTerminal.setSixelPaletteSize(oldSixelPaletteSize); + } TFontChooserWindow.this.close(); } }); @@ -541,6 +589,9 @@ public class TFontChooserWindow extends TWindow { terminal.setFont(oldFont); terminal.setFontSize(oldFontSize); } + if (ecmaTerminal != null) { + ecmaTerminal.setSixelPaletteSize(oldSixelPaletteSize); + } getApplication().closeWindow(this); return; } diff --git a/src/jexer/TFontChooserWindow.properties b/src/jexer/TFontChooserWindow.properties index de30c1a..4ab274e 100644 --- a/src/jexer/TFontChooserWindow.properties +++ b/src/jexer/TFontChooserWindow.properties @@ -1,7 +1,7 @@ -windowTitle=Font +windowTitle=Screen okButton=\ \ &OK\ \ cancelButton=&Cancel -statusBar=Select Font Options +statusBar=Select Screen Options fontName=Font name: fontSize=Font size: @@ -10,6 +10,8 @@ textAdjustY=Y adjust: textAdjustHeight=Height adjust: textAdjustWidth=Width adjust: +sixelPaletteSize=Sixel Palette Size: + unavailable=Unavailable builtInTerminus=Built-In Terminus sample=\ Sample Window\ diff --git a/src/jexer/backend/ECMA48Terminal.java b/src/jexer/backend/ECMA48Terminal.java index ca1d499..cb9724c 100644 --- a/src/jexer/backend/ECMA48Terminal.java +++ b/src/jexer/backend/ECMA48Terminal.java @@ -81,14 +81,6 @@ public class ECMA48Terminal extends LogicalScreen MOUSE_SGR, } - /** - * Number of colors in the sixel palette. Xterm 335 defines the max as - * 1024. - */ - private static final int MAX_COLOR_REGISTERS = 1024; - // Black-and-white is possible too. - // private static final int MAX_COLOR_REGISTERS = 2; - // ------------------------------------------------------------------------ // Variables -------------------------------------------------------------- // ------------------------------------------------------------------------ @@ -200,6 +192,13 @@ public class ECMA48Terminal extends LogicalScreen */ private SixelCache sixelCache = null; + /** + * Number of colors in the sixel palette. Xterm 335 defines the max as + * 1024. Valid values are: 2 (black and white), 256, 512, 1024, and + * 2048. + */ + private int sixelPaletteSize = 1024; + /** * If true, then we changed System.in and need to change it back. */ @@ -234,7 +233,7 @@ public class ECMA48Terminal extends LogicalScreen /** * SixelPalette is used to manage the conversion of images between 24-bit - * RGB color and a palette of MAX_COLOR_REGISTERS colors. + * RGB color and a palette of sixelPaletteSize colors. */ private class SixelPalette { @@ -247,7 +246,7 @@ public class ECMA48Terminal extends LogicalScreen * Map of color palette index for sixel output, from the order it was * generated by makePalette() to rgbColors. */ - private int [] rgbSortedIndex = new int[MAX_COLOR_REGISTERS]; + private int [] rgbSortedIndex = new int[sixelPaletteSize]; /** * The color palette, organized by hue, saturation, and luminance. @@ -345,7 +344,7 @@ public class ECMA48Terminal extends LogicalScreen int green = (color >>> 8) & 0xFF; int blue = color & 0xFF; - if (MAX_COLOR_REGISTERS == 2) { + if (sixelPaletteSize == 2) { if (((red * red) + (green * green) + (blue * blue)) < 35568) { // Black return 0; @@ -427,7 +426,7 @@ public class ECMA48Terminal extends LogicalScreen ((255 - blue) * (255 - blue))) < diff) { // White is a closer match. - idx = MAX_COLOR_REGISTERS - 1; + idx = sixelPaletteSize - 1; } assert (idx != -1); return idx; @@ -450,7 +449,7 @@ public class ECMA48Terminal extends LogicalScreen } /** - * Dither an image to a MAX_COLOR_REGISTERS palette. The dithered + * Dither an image to a sixelPaletteSize palette. The dithered * image cells will contain indexes into the palette. * * @param image the image to dither @@ -473,7 +472,7 @@ public class ECMA48Terminal extends LogicalScreen imageY) & 0xFFFFFF; int colorIdx = matchColor(oldPixel); assert (colorIdx >= 0); - assert (colorIdx < MAX_COLOR_REGISTERS); + assert (colorIdx < sixelPaletteSize); int newPixel = rgbColors.get(colorIdx); ditheredImage.setRGB(imageX, imageY, colorIdx); @@ -671,11 +670,11 @@ public class ECMA48Terminal extends LogicalScreen private void makePalette() { // Generate the sixel palette. Because we have no idea at this // layer which image(s) will be shown, we have to use a common - // palette with MAX_COLOR_REGISTERS colors for everything, and + // palette with sixelPaletteSize colors for everything, and // map the BufferedImage colors to their nearest neighbor in RGB // space. - if (MAX_COLOR_REGISTERS == 2) { + if (sixelPaletteSize == 2) { rgbColors.add(0); rgbColors.add(0xFFFFFF); rgbSortedIndex[0] = 0; @@ -696,13 +695,13 @@ public class ECMA48Terminal extends LogicalScreen satBits = 2; lumBits = 1; - assert (MAX_COLOR_REGISTERS >= 256); - assert ((MAX_COLOR_REGISTERS == 256) - || (MAX_COLOR_REGISTERS == 512) - || (MAX_COLOR_REGISTERS == 1024) - || (MAX_COLOR_REGISTERS == 2048)); + assert (sixelPaletteSize >= 256); + assert ((sixelPaletteSize == 256) + || (sixelPaletteSize == 512) + || (sixelPaletteSize == 1024) + || (sixelPaletteSize == 2048)); - switch (MAX_COLOR_REGISTERS) { + switch (sixelPaletteSize) { case 512: hueBits = 5; satBits = 2; @@ -788,7 +787,7 @@ public class ECMA48Terminal extends LogicalScreen } // System.err.printf("\n\n"); - assert (rgbColors.size() == MAX_COLOR_REGISTERS); + assert (rgbColors.size() == sixelPaletteSize); /* * We need to sort rgbColors, so that toSixel() can know where @@ -800,19 +799,19 @@ public class ECMA48Terminal extends LogicalScreen Collections.sort(rgbColors); HashMap rgbColorIndices = null; rgbColorIndices = new HashMap(); - for (int i = 0; i < MAX_COLOR_REGISTERS; i++) { + for (int i = 0; i < sixelPaletteSize; i++) { rgbColorIndices.put(rgbColors.get(i), i); } - for (int i = 0; i < MAX_COLOR_REGISTERS; i++) { + for (int i = 0; i < sixelPaletteSize; i++) { int rawColor = rawRgbList.get(i); rgbSortedIndex[i] = rgbColorIndices.get(rawColor); } if (DEBUG) { - for (int i = 0; i < MAX_COLOR_REGISTERS; i++) { + for (int i = 0; i < sixelPaletteSize; i++) { assert (rawRgbList != null); int idx = rgbSortedIndex[i]; int rgbColor = rgbColors.get(idx); - if ((idx != 0) && (idx != MAX_COLOR_REGISTERS - 1)) { + if ((idx != 0) && (idx != sixelPaletteSize - 1)) { /* System.err.printf("%d %06x --> %d %06x\n", i, rawRgbList.get(i), idx, rgbColors.get(idx)); @@ -825,7 +824,7 @@ public class ECMA48Terminal extends LogicalScreen // Set the dimmest color as true black, and the brightest as true // white. rgbColors.set(0, 0); - rgbColors.set(MAX_COLOR_REGISTERS - 1, 0xFFFFFF); + rgbColors.set(sixelPaletteSize - 1, 0xFFFFFF); /* System.err.printf("\n"); @@ -850,7 +849,7 @@ public class ECMA48Terminal extends LogicalScreen public String emitPalette(final StringBuilder sb, final boolean [] used) { - for (int i = 0; i < MAX_COLOR_REGISTERS; i++) { + for (int i = 0; i < sixelPaletteSize; i++) { if (((used != null) && (used[i] == true)) || (used == null)) { int rgbColor = rgbColors.get(i); sb.append(String.format("#%d;2;%d;%d;%d", i, @@ -1380,6 +1379,27 @@ public class ECMA48Terminal extends LogicalScreen } else { sixel = false; } + + // Palette size + int paletteSize = 1024; + try { + paletteSize = Integer.parseInt(System.getProperty( + "jexer.ECMA48.sixelPaletteSize", "1024")); + switch (paletteSize) { + case 2: + case 256: + case 512: + case 1024: + case 2048: + sixelPaletteSize = paletteSize; + break; + default: + // Ignore value + break; + } + } catch (NumberFormatException e) { + // SQUASH + } } // ------------------------------------------------------------------------ @@ -2748,6 +2768,46 @@ public class ECMA48Terminal extends LogicalScreen // Sixel output support --------------------------------------------------- // ------------------------------------------------------------------------ + /** + * Get the number of colors in the sixel palette. + * + * @return the palette size + */ + public int getSixelPaletteSize() { + return sixelPaletteSize; + } + + /** + * Set the number of colors in the sixel palette. + * + * @param paletteSize the new palette size + */ + public void setSixelPaletteSize(final int paletteSize) { + if (paletteSize == sixelPaletteSize) { + return; + } + + switch (paletteSize) { + case 2: + case 256: + case 512: + case 1024: + case 2048: + break; + default: + throw new IllegalArgumentException("Unsupported sixel palette " + + " size: " + paletteSize); + } + + // Don't step on the screen refresh thread. + synchronized (this) { + sixelPaletteSize = paletteSize; + palette = null; + sixelCache = null; + clearPhysical(); + } + } + /** * Start a sixel string for display one row's worth of bitmap data. * @@ -2944,7 +3004,7 @@ public class ECMA48Terminal extends LogicalScreen // Emit the palette, but only for the colors actually used by these // cells. - boolean [] usedColors = new boolean[MAX_COLOR_REGISTERS]; + boolean [] usedColors = new boolean[sixelPaletteSize]; for (int imageX = 0; imageX < image.getWidth(); imageX++) { for (int imageY = 0; imageY < image.getHeight(); imageY++) { usedColors[image.getRGB(imageX, imageY)] = true; @@ -2964,13 +3024,13 @@ public class ECMA48Terminal extends LogicalScreen int colorIdx = image.getRGB(imageX, imageY + currentRow); assert (colorIdx >= 0); - assert (colorIdx < MAX_COLOR_REGISTERS); + assert (colorIdx < sixelPaletteSize); sixels[imageX][imageY] = colorIdx; } } - for (int i = 0; i < MAX_COLOR_REGISTERS; i++) { + for (int i = 0; i < sixelPaletteSize; i++) { boolean isUsed = false; for (int imageX = 0; imageX < image.getWidth(); imageX++) { for (int j = 0; j < 6; j++) { @@ -3047,7 +3107,7 @@ public class ECMA48Terminal extends LogicalScreen sb.append((char) oldData); } - } // for (int i = 0; i < MAX_COLOR_REGISTERS; i++) + } // for (int i = 0; i < sixelPaletteSize; i++) // Advance to the next scan line. sb.append("-"); diff --git a/src/jexer/demos/DemoApplication.java b/src/jexer/demos/DemoApplication.java index 8505703..3e4cbe9 100644 --- a/src/jexer/demos/DemoApplication.java +++ b/src/jexer/demos/DemoApplication.java @@ -135,7 +135,10 @@ public class DemoApplication extends TApplication { * @throws Exception if TApplication can't instantiate the Backend. */ public DemoApplication(final BackendType backendType) throws Exception { - super(backendType); + // For the Swing demo, use an initial size of 82x28 so that a + // terminal window precisely fits the window. + super(backendType, (backendType == BackendType.SWING ? 82 : -1), + (backendType == BackendType.SWING ? 28 : -1), 20); addAllWidgets(); getBackend().setTitle(i18n.getString("applicationTitle")); } diff --git a/src/jexer/menu/TMenu.java b/src/jexer/menu/TMenu.java index 58228f9..7580243 100644 --- a/src/jexer/menu/TMenu.java +++ b/src/jexer/menu/TMenu.java @@ -62,7 +62,7 @@ public class TMenu extends TWindow { // Tools menu public static final int MID_REPAINT = 1; public static final int MID_VIEW_IMAGE = 2; - public static final int MID_CHANGE_FONT = 3; + public static final int MID_SCREEN_OPTIONS = 3; // File menu public static final int MID_NEW = 10; @@ -565,8 +565,8 @@ public class TMenu extends TWindow { label = i18n.getString("menuViewImage"); break; - case MID_CHANGE_FONT: - label = i18n.getString("menuChangeFont"); + case MID_SCREEN_OPTIONS: + label = i18n.getString("menuScreenOptions"); break; case MID_NEW: diff --git a/src/jexer/menu/TMenu.properties b/src/jexer/menu/TMenu.properties index c31269a..1581d0b 100644 --- a/src/jexer/menu/TMenu.properties +++ b/src/jexer/menu/TMenu.properties @@ -58,4 +58,4 @@ menuTableFileSaveText=Save As &Text... menuRepaintDesktop=&Repaint desktop menuViewImage=&Open image... -menuChangeFont=Change &font... +menuScreenOptions=&Screen options... -- 2.27.0