├── .travis.yml ├── dist └── asciiPanel.jar ├── src ├── main │ ├── resources │ │ ├── cp437.png │ │ ├── cp437_8x8.png │ │ ├── cp437_10x10.png │ │ ├── cp437_12x12.png │ │ ├── cp437_16x16.png │ │ ├── cp437_9x16.png │ │ ├── drake_10x10.png │ │ ├── qbicfeet_10x10.png │ │ ├── taffer_10x10.png │ │ └── talryth_square_15x15.png │ └── java │ │ └── asciiPanel │ │ ├── TileTransformer.java │ │ ├── AsciiCharacterData.java │ │ ├── AsciiFont.java │ │ └── AsciiPanel.java └── test │ ├── resources │ └── cp437.png │ └── java │ └── asciiPanel │ └── AsciiPanelTest.java ├── LICENSE.md ├── .gitignore ├── pom.xml └── README.MD /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk8 4 | -------------------------------------------------------------------------------- /dist/asciiPanel.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trystan/AsciiPanel/HEAD/dist/asciiPanel.jar -------------------------------------------------------------------------------- /src/main/resources/cp437.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trystan/AsciiPanel/HEAD/src/main/resources/cp437.png -------------------------------------------------------------------------------- /src/test/resources/cp437.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trystan/AsciiPanel/HEAD/src/test/resources/cp437.png -------------------------------------------------------------------------------- /src/main/resources/cp437_8x8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trystan/AsciiPanel/HEAD/src/main/resources/cp437_8x8.png -------------------------------------------------------------------------------- /src/main/resources/cp437_10x10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trystan/AsciiPanel/HEAD/src/main/resources/cp437_10x10.png -------------------------------------------------------------------------------- /src/main/resources/cp437_12x12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trystan/AsciiPanel/HEAD/src/main/resources/cp437_12x12.png -------------------------------------------------------------------------------- /src/main/resources/cp437_16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trystan/AsciiPanel/HEAD/src/main/resources/cp437_16x16.png -------------------------------------------------------------------------------- /src/main/resources/cp437_9x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trystan/AsciiPanel/HEAD/src/main/resources/cp437_9x16.png -------------------------------------------------------------------------------- /src/main/resources/drake_10x10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trystan/AsciiPanel/HEAD/src/main/resources/drake_10x10.png -------------------------------------------------------------------------------- /src/main/resources/qbicfeet_10x10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trystan/AsciiPanel/HEAD/src/main/resources/qbicfeet_10x10.png -------------------------------------------------------------------------------- /src/main/resources/taffer_10x10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trystan/AsciiPanel/HEAD/src/main/resources/taffer_10x10.png -------------------------------------------------------------------------------- /src/main/resources/talryth_square_15x15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trystan/AsciiPanel/HEAD/src/main/resources/talryth_square_15x15.png -------------------------------------------------------------------------------- /src/main/java/asciiPanel/TileTransformer.java: -------------------------------------------------------------------------------- 1 | package asciiPanel; 2 | 3 | public interface TileTransformer { 4 | public void transformTile(int x, int y, AsciiCharacterData data); 5 | } -------------------------------------------------------------------------------- /src/main/java/asciiPanel/AsciiCharacterData.java: -------------------------------------------------------------------------------- 1 | package asciiPanel; 2 | 3 | import java.awt.Color; 4 | 5 | public class AsciiCharacterData { 6 | public AsciiCharacterData() {} 7 | 8 | public AsciiCharacterData(char character, Color foregroundColor, Color backgroundColor) { 9 | this.character = character; 10 | this.foregroundColor = foregroundColor; 11 | this.backgroundColor = backgroundColor; 12 | } 13 | 14 | public char character; 15 | public Color foregroundColor; 16 | public Color backgroundColor; 17 | } 18 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Trystan Spangler 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Mobile Tools for Java (J2ME) 4 | .mtj.tmp/ 5 | 6 | # Package Files # 7 | *.jar 8 | *.war 9 | *.ear 10 | 11 | # Maven build directory 12 | target/ 13 | 14 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 15 | hs_err_pid* 16 | 17 | # Eclipse created files and directories 18 | .classpath 19 | .project 20 | .settings 21 | 22 | # ========================= 23 | # Operating System Files 24 | # ========================= 25 | 26 | # OSX 27 | # ========================= 28 | 29 | .DS_Store 30 | .AppleDouble 31 | .LSOverride 32 | 33 | # Thumbnails 34 | ._* 35 | 36 | # Files that might appear in the root of a volume 37 | .DocumentRevisions-V100 38 | .fseventsd 39 | .Spotlight-V100 40 | .TemporaryItems 41 | .Trashes 42 | .VolumeIcon.icns 43 | 44 | # Directories potentially created on remote AFP share 45 | .AppleDB 46 | .AppleDesktop 47 | Network Trash Folder 48 | Temporary Items 49 | .apdisk 50 | 51 | # Windows 52 | # ========================= 53 | 54 | # Windows image file caches 55 | Thumbs.db 56 | ehthumbs.db 57 | 58 | # Folder config file 59 | Desktop.ini 60 | 61 | # Recycle Bin used on file shares 62 | $RECYCLE.BIN/ 63 | 64 | # Windows Installer files 65 | *.cab 66 | *.msi 67 | *.msm 68 | *.msp 69 | 70 | # Windows shortcuts 71 | *.lnk 72 | -------------------------------------------------------------------------------- /src/main/java/asciiPanel/AsciiFont.java: -------------------------------------------------------------------------------- 1 | package asciiPanel; 2 | 3 | /** 4 | * This class holds provides all available Fonts for the AsciiPanel. 5 | * Some graphics are from the Dwarf Fortress Tileset Wiki Page 6 | * 7 | * @author zn80 8 | * 9 | */ 10 | public class AsciiFont { 11 | 12 | public static final AsciiFont CP437_8x8 = new AsciiFont("cp437_8x8.png", 8, 8); 13 | public static final AsciiFont CP437_10x10 = new AsciiFont("cp437_10x10.png", 10, 10); 14 | public static final AsciiFont CP437_12x12 = new AsciiFont("cp437_12x12.png", 12, 12); 15 | public static final AsciiFont CP437_16x16 = new AsciiFont("cp437_16x16.png", 16, 16); 16 | public static final AsciiFont CP437_9x16 = new AsciiFont("cp437_9x16.png", 9, 16); 17 | public static final AsciiFont DRAKE_10x10 = new AsciiFont("drake_10x10.png", 10, 10); 18 | public static final AsciiFont TAFFER_10x10 = new AsciiFont("taffer_10x10.png", 10, 10); 19 | public static final AsciiFont QBICFEET_10x10 = new AsciiFont("qbicfeet_10x10.png", 10, 10); 20 | public static final AsciiFont TALRYTH_15_15 = new AsciiFont("talryth_square_15x15.png", 15, 15); 21 | 22 | private String fontFilename; 23 | 24 | public String getFontFilename() { 25 | return fontFilename; 26 | } 27 | 28 | private int width; 29 | 30 | public int getWidth() { 31 | return width; 32 | } 33 | 34 | private int height; 35 | 36 | public int getHeight() { 37 | return height; 38 | } 39 | 40 | public AsciiFont(String filename, int width, int height) { 41 | this.fontFilename = filename; 42 | this.width = width; 43 | this.height = height; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | net.trystan 7 | ascii-panel 8 | 1.2-SNAPSHOT 9 | 10 | 11 | UTF-8 12 | 1.8 13 | 1.8 14 | 15 | 16 | 17 | 18 | junit 19 | junit 20 | 4.13.1 21 | test 22 | 23 | 24 | org.powermock 25 | powermock-module-junit4 26 | 1.6.5 27 | test 28 | 29 | 30 | org.powermock 31 | powermock-api-mockito 32 | 1.6.5 33 | test 34 | 35 | 36 | 37 | 38 | 39 | 40 | org.apache.maven.plugins 41 | maven-source-plugin 42 | 3.0.1 43 | 44 | 45 | attach-sources 46 | 47 | jar 48 | 49 | 50 | 51 | 52 | 53 | org.apache.maven.plugins 54 | maven-javadoc-plugin 55 | 3.0.0-M1 56 | 57 | -Xdoclint:none 58 | true 59 | 60 | 61 | 62 | attach-javadocs 63 | 64 | jar 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # AsciiPanel 2 | 3 | [![Build Status](https://travis-ci.org/roddy/MavenizedAsciiPanel.svg)](https://travis-ci.org/roddy/MavenizedAsciiPanel) 4 | 5 | AsciiPanel simulates a [code page 437](https://en.wikipedia.org/wiki/Code_page_437) ASCII terminal display. It supports all 256 characters of codepage 437, arbitrary foreground colors, arbitrary background colors, and arbitrary terminal sizes. 6 | 7 | The default terminal size is 80x24 characters and default colors matching the Windows Command Prompt are provided. The default font is 9x16 pixel CP437 (`CP437_9x16`.) 8 | 9 | This should be useful to roguelike developers. 10 | 11 | ## Usage 12 | 13 | AsciiPanel supports the customization of fonts. The following system fonts are provided: 14 | - CP437_9x16 15 | - CP437_8x8 16 | - CP437_10x10 17 | - CP437_12x12 18 | - CP437_16x16 19 | 20 | In addition, the following fontsets from the [Dwarf Fortress Tileset](http://dwarffortresswiki.org/Tileset_repository) are also included: 21 | - DRAKE_10x10 22 | - QBICFEET_10x10 23 | - TALRYTH_15x15 24 | 25 | The AsciiPanel class provides a special three-parameter constructor to support font customization. Simply pass the desired AsciiFont as the third parameter as follows. 26 | 27 | ```java 28 | AsciiPanel myPanel = new AsciiPanel(80, 24, AsciiFont.DRAKE_10x10); 29 | 30 | ``` 31 | 32 | ## Demo 33 | 34 | Small demo of AsciiPanel. 35 | ```java 36 | import asciiPanel.AsciiFont; 37 | import asciiPanel.AsciiPanel; 38 | 39 | import javax.swing.*; 40 | import java.awt.*; 41 | import java.util.Random; 42 | 43 | public class Demo { 44 | // Declare variables for the AsciiPanel terminal, JFrame, random generator, and the title string 45 | private static AsciiPanel terminal; 46 | private static JFrame frame; 47 | private static Random rand; 48 | private static String TITLE = "Demo AsciiPanel"; 49 | 50 | public static void main(String[] args) { 51 | // Initialize the frame, terminal, and random number generator 52 | // JFrame is the container for the terminal 53 | frame = new JFrame(TITLE); // Create a new JFrame with the title "Demo AsciiPanel" 54 | terminal = new AsciiPanel(); // Create a new AsciiPanel instance (the terminal) 55 | rand = new Random(); // Initialize the random generator for background color selection 56 | 57 | // Write the title in the center of the terminal at row 2 58 | terminal.writeCenter(TITLE, 2); 59 | 60 | // Write the text "Red color" in red at row 5 61 | terminal.writeCenter("Red color", 5, AsciiPanel.red); 62 | 63 | // Random generation loop: write spaces with random background colors 64 | // Iterate through a specific region (from x = 15 to 45, y = 10 to 20) 65 | for (int x = 15; x < 45; x++) { 66 | for (int y = 10; y < 20; y++) { 67 | // Randomly choose a background color (either blue or green) 68 | if (rand.nextInt(2) == 0) { 69 | terminal.setDefaultBackgroundColor(AsciiPanel.blue); // Set background color to blue 70 | } else { 71 | terminal.setDefaultBackgroundColor(AsciiPanel.green); // Set background color to green 72 | } 73 | 74 | // Write a space character (" ") at the current position (x, y) with the selected background color 75 | terminal.write(" ", x, y); 76 | } 77 | } 78 | 79 | // Add the terminal to the frame 80 | // Set the frame properties: make it non-resizable, pack it to fit the terminal, and set default close operation 81 | frame.add(terminal); 82 | frame.setResizable(false); // Disable resizing of the window 83 | frame.pack(); // Adjust the window size to fit the terminal size 84 | frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Ensure the application exits when the window is closed 85 | frame.setVisible(true); // Make the window visible to the user 86 | } 87 | } 88 | ``` 89 | 90 | ## Build instructions 91 | 92 | AsciiPanel is a [Maven](https://maven.apache.org/) project, compatible with Maven 2 and Maven 3. It can be built using the following command: 93 | 94 | ``` 95 | mvn install 96 | ``` 97 | 98 | This will build the project, run the unit tests, and copy the resulting jar into your local Maven repository. Once the jar is deployed to your repository, you can include it in your projects by including the following dependency in your pom: 99 | 100 | ```xml 101 | 102 | net.trystan 103 | ascii-panel 104 | 1.1 105 | 106 | ``` 107 | 108 | Or you can add the jitpack repository to your pom: 109 | 110 | ```xml 111 | 112 | jitpack.io 113 | https://jitpack.io 114 | 115 | ``` 116 | 117 | which provides AsciiPanel as dependency at: 118 | 119 | ```xml 120 | 121 | com.github.trystan 122 | AsciiPanel 123 | 31bc98d 124 | 125 | ``` 126 | 127 | where `` describes the git commit you want to use. 128 | 129 | For Gradle users: 130 | 131 | ```gradle 132 | repositories { 133 | //... 134 | allprojects { 135 | repositories { 136 | maven { url 'https://jitpack.io' } 137 | } 138 | } 139 | } 140 | 141 | dependencies { 142 | //... 143 | compile 'com.github.trystan:AsciiPanel:master-SNAPSHOT' 144 | } 145 | ``` 146 | 147 | ## Notes 148 | 149 | This project is built with Java 8. However the code itself does not *require* Java 8. If you are supporting a project running an earlier version of Java, you can change the pom file and rebuild the jar using your chosen version of Java without having to modify the code. 150 | -------------------------------------------------------------------------------- /src/test/java/asciiPanel/AsciiPanelTest.java: -------------------------------------------------------------------------------- 1 | package asciiPanel; 2 | 3 | import java.awt.*; 4 | import java.awt.image.BufferedImage; 5 | import java.io.File; 6 | import java.io.IOException; 7 | import javax.imageio.ImageIO; 8 | import javax.swing.JComponent; 9 | import javax.swing.UIManager; 10 | import javax.swing.plaf.PanelUI; 11 | import org.junit.runner.RunWith; 12 | import org.junit.Before; 13 | import org.junit.Test; 14 | import org.powermock.api.mockito.PowerMockito; 15 | import org.powermock.core.classloader.annotations.PowerMockIgnore; 16 | import org.powermock.core.classloader.annotations.PrepareForTest; 17 | import org.powermock.modules.junit4.PowerMockRunner; 18 | 19 | import static org.junit.Assert.*; 20 | import static org.mockito.Mockito.*; 21 | 22 | @RunWith( PowerMockRunner.class ) 23 | @PrepareForTest({ UIManager.class, ImageIO.class }) 24 | @PowerMockIgnore("javax.swing.*") 25 | public class AsciiPanelTest { 26 | 27 | @Before 28 | public void initMocks() throws IOException { 29 | PowerMockito.mockStatic(UIManager.class); 30 | PowerMockito.mockStatic(ImageIO.class); 31 | 32 | BufferedImage mockImage = mock(BufferedImage.class); 33 | PanelUI mockPanelUi = mock(PanelUI.class); 34 | 35 | when(UIManager.getUI(any(JComponent.class))) 36 | .thenReturn(mockPanelUi); 37 | 38 | when(ImageIO.read(any(File.class))) 39 | .thenReturn(mockImage); 40 | } 41 | 42 | @Test( expected = IllegalArgumentException.class ) 43 | public void testSetNegativeCursorX() { 44 | AsciiPanel panel = new AsciiPanel(); 45 | panel.setCursorX(-1); 46 | 47 | fail("Should have thrown an IllegalArgumentException."); 48 | } 49 | 50 | @Test( expected = IllegalArgumentException.class ) 51 | public void testSetCursorXOutsideOfMax() { 52 | AsciiPanel panel = new AsciiPanel(); 53 | panel.setCursorX(Integer.MAX_VALUE); 54 | 55 | fail("Should have thrown an IllegalArgumentException."); 56 | } 57 | 58 | @Test( expected = IllegalArgumentException.class ) 59 | public void testSetNegativeCursorY() { 60 | AsciiPanel panel = new AsciiPanel(); 61 | panel.setCursorY(-1); 62 | 63 | fail("Should have thrown an IllegalArgumentException."); 64 | } 65 | 66 | @Test( expected = IllegalArgumentException.class ) 67 | public void testSetCursorYOutsideOfMax() { 68 | AsciiPanel panel = new AsciiPanel(); 69 | panel.setCursorY(Integer.MAX_VALUE); 70 | 71 | fail("Should have thrown an IllegalArgumentException."); 72 | } 73 | 74 | @Test 75 | public void testSetCursorX() { 76 | AsciiPanel panel = new AsciiPanel(1, 1); 77 | panel.setCursorX(0); 78 | } 79 | 80 | @Test 81 | public void testSetCursorY() { 82 | AsciiPanel panel = new AsciiPanel(1, 1); 83 | panel.setCursorY(0); 84 | } 85 | 86 | @Test( expected = NullPointerException.class ) 87 | public void testSetNullDefaultBackgroundColor() { 88 | AsciiPanel panel = new AsciiPanel(); 89 | panel.setDefaultBackgroundColor(null); 90 | 91 | fail("Should have thrown a NullPointerException."); 92 | } 93 | 94 | @Test( expected = NullPointerException.class ) 95 | public void testSetNullDefaultForegroundColor() { 96 | AsciiPanel panel = new AsciiPanel(); 97 | panel.setDefaultForegroundColor(null); 98 | 99 | fail("Should have thrown a NullPointerException."); 100 | } 101 | 102 | @Test( expected = IllegalArgumentException.class ) 103 | public void testConstructorZeroWidth() { 104 | new AsciiPanel(0, 24); 105 | 106 | fail("Should have thrown an IllegalArgumentException."); 107 | } 108 | 109 | @Test( expected = IllegalArgumentException.class ) 110 | public void testConstructorZeroHeight() { 111 | new AsciiPanel(80, 0); 112 | 113 | fail("Should have thrown an IllegalArgumentException."); 114 | } 115 | 116 | @Test( expected = NullPointerException.class ) 117 | public void testWriteNullFail() { 118 | AsciiPanel panel = new AsciiPanel(80, 1); 119 | panel.write(null); 120 | fail("Should have thrown a NullPointerException."); 121 | } 122 | 123 | @Test( expected = IllegalArgumentException.class ) 124 | public void testWriteInvalidLengthFail() { 125 | AsciiPanel panel = new AsciiPanel(80, 1); 126 | String superLongString = String.format("%0100d", 1); 127 | panel.write(superLongString); 128 | fail("Should have thrown an IllegalArgumentException."); 129 | } 130 | 131 | @Test 132 | public void testWriteChar() { 133 | AsciiPanel panel = new AsciiPanel(1, 1); 134 | panel.write(' '); 135 | } 136 | 137 | @Test 138 | public void testWriteCharFG() { 139 | AsciiPanel panel = new AsciiPanel(1, 1); 140 | panel.write(' ', AsciiPanel.white); 141 | } 142 | 143 | @Test 144 | public void testWriteCharFGBG() { 145 | AsciiPanel panel = new AsciiPanel(1, 1); 146 | panel.write(' ', AsciiPanel.white, AsciiPanel.black); 147 | } 148 | 149 | @Test 150 | public void testWriteCharXY() { 151 | AsciiPanel panel = new AsciiPanel(1, 1); 152 | panel.write(' ', 0, 0); 153 | } 154 | 155 | @Test 156 | public void testWriteCharXYFG() { 157 | AsciiPanel panel = new AsciiPanel(1, 1); 158 | panel.write(' ', 0, 0, AsciiPanel.white); 159 | } 160 | 161 | @Test 162 | public void testWriteCharXYFGBG() { 163 | AsciiPanel panel = new AsciiPanel(1, 1); 164 | panel.write(' ', 0, 0, AsciiPanel.white, AsciiPanel.black); 165 | } 166 | 167 | @Test 168 | public void testWriteString() { 169 | AsciiPanel panel = new AsciiPanel(1, 1); 170 | panel.write(" "); 171 | } 172 | 173 | @Test 174 | public void testWriteStringFG() { 175 | AsciiPanel panel = new AsciiPanel(1, 1); 176 | panel.write(" ", AsciiPanel.white); 177 | } 178 | 179 | @Test 180 | public void testWriteStringFGBG() { 181 | AsciiPanel panel = new AsciiPanel(1, 1); 182 | panel.write(" ", AsciiPanel.white, AsciiPanel.black); 183 | } 184 | 185 | @Test 186 | public void testWriteStringXY() { 187 | AsciiPanel panel = new AsciiPanel(1, 1); 188 | panel.write(" ", 0, 0); 189 | } 190 | 191 | @Test 192 | public void testWriteStringXYFG() { 193 | AsciiPanel panel = new AsciiPanel(1, 1); 194 | panel.write(" ", 0, 0, AsciiPanel.white); 195 | } 196 | 197 | @Test 198 | public void testWriteStringXYFGBG() { 199 | AsciiPanel panel = new AsciiPanel(1, 1); 200 | panel.write(" ", 0, 0, AsciiPanel.white, AsciiPanel.black); 201 | } 202 | 203 | @Test 204 | public void testWriteCenter() { 205 | AsciiPanel panel = new AsciiPanel(1, 1); 206 | panel.writeCenter(" ", 0); 207 | } 208 | 209 | @Test 210 | public void testWriteCenterFG() { 211 | AsciiPanel panel = new AsciiPanel(1, 1); 212 | panel.writeCenter(" ", 0, AsciiPanel.white); 213 | } 214 | 215 | @Test 216 | public void testWriteCenterFGBG() { 217 | AsciiPanel panel = new AsciiPanel(1, 1); 218 | panel.writeCenter(" ", 0, AsciiPanel.white, AsciiPanel.black); 219 | } 220 | 221 | @Test 222 | public void testSetAsciiFont() 223 | { 224 | AsciiPanel panel = new AsciiPanel(1, 1, AsciiFont.CP437_9x16); 225 | Dimension oldDimensions = panel.getPreferredSize(); 226 | 227 | panel.setAsciiFont(AsciiFont.TALRYTH_15_15); 228 | Dimension newDimensions = panel.getPreferredSize(); 229 | 230 | assertNotEquals(oldDimensions, newDimensions); 231 | } 232 | 233 | @Test 234 | public void testWrite() { 235 | int width = 100; 236 | int height = 100; 237 | AsciiPanel panel = new AsciiPanel(width, height); 238 | 239 | // write out characters in a specific pattern such that we can verify each is written correctly to the specified 240 | // position 241 | Color oddColumnForeground = new Color(255, 255, 255); 242 | Color evenColumnForeground = new Color(0, 0, 0); 243 | Color oddRowBackground = new Color(255, 255, 0); 244 | Color evenRowBackground = new Color(0, 255, 255); 245 | 246 | AsciiCharacterData[][] expectedCharacterData = new AsciiCharacterData[width][height]; 247 | 248 | for (int x = 0; x < width; x++) { 249 | for (int y = 0; y < width; y++) { 250 | AsciiCharacterData characterData = new AsciiCharacterData(); 251 | characterData.character = (char)(x + y); 252 | if (x % 2 == 0) { 253 | characterData.foregroundColor = evenColumnForeground; 254 | } else { 255 | characterData.foregroundColor = oddColumnForeground; 256 | } 257 | 258 | if (y % 2 == 0) { 259 | characterData.backgroundColor = evenRowBackground; 260 | } else { 261 | characterData.backgroundColor = oddRowBackground; 262 | } 263 | panel.write(characterData, x, y); 264 | expectedCharacterData[x][y] = characterData; 265 | } 266 | } 267 | 268 | // now validate that it was written as expected 269 | AsciiCharacterData[][] writtenData = panel.getCharacters(); 270 | for (int x = 0; x < width; x++) { 271 | for (int y = 0; y < height; y++) { 272 | AsciiCharacterData expectedCharData = expectedCharacterData[x][y]; 273 | AsciiCharacterData writtenCharData = writtenData[x][y]; 274 | assertEquals(expectedCharData.character, writtenCharData.character); 275 | assertEquals(expectedCharData.foregroundColor, writtenCharData.foregroundColor); 276 | assertEquals(expectedCharData.backgroundColor, writtenCharData.backgroundColor); 277 | } 278 | } 279 | } 280 | } 281 | -------------------------------------------------------------------------------- /src/main/java/asciiPanel/AsciiPanel.java: -------------------------------------------------------------------------------- 1 | package asciiPanel; 2 | 3 | import java.awt.Color; 4 | import java.awt.Dimension; 5 | import java.awt.Graphics; 6 | import java.awt.Image; 7 | import java.awt.image.BufferedImage; 8 | import java.awt.image.LookupOp; 9 | import java.awt.image.ShortLookupTable; 10 | import java.io.IOException; 11 | 12 | import javax.imageio.ImageIO; 13 | import javax.swing.JPanel; 14 | 15 | /** 16 | * This simulates a code page 437 ASCII terminal display. 17 | * @author Trystan Spangler 18 | */ 19 | public class AsciiPanel extends JPanel { 20 | private static final long serialVersionUID = -4167851861147593092L; 21 | 22 | /** 23 | * The color black (pure black). 24 | */ 25 | public static Color black = new Color(0, 0, 0); 26 | 27 | /** 28 | * The color red. 29 | */ 30 | public static Color red = new Color(128, 0, 0); 31 | 32 | /** 33 | * The color green. 34 | */ 35 | public static Color green = new Color(0, 128, 0); 36 | 37 | /** 38 | * The color yellow. 39 | */ 40 | public static Color yellow = new Color(128, 128, 0); 41 | 42 | /** 43 | * The color blue. 44 | */ 45 | public static Color blue = new Color(0, 0, 128); 46 | 47 | /** 48 | * The color magenta. 49 | */ 50 | public static Color magenta = new Color(128, 0, 128); 51 | 52 | /** 53 | * The color cyan. 54 | */ 55 | public static Color cyan = new Color(0, 128, 128); 56 | 57 | /** 58 | * The color white (light gray). 59 | */ 60 | public static Color white = new Color(192, 192, 192); 61 | 62 | /** 63 | * A brighter black (dark gray). 64 | */ 65 | public static Color brightBlack = new Color(128, 128, 128); 66 | 67 | /** 68 | * A brighter red. 69 | */ 70 | public static Color brightRed = new Color(255, 0, 0); 71 | 72 | /** 73 | * A brighter green. 74 | */ 75 | public static Color brightGreen = new Color(0, 255, 0); 76 | 77 | /** 78 | * A brighter yellow. 79 | */ 80 | public static Color brightYellow = new Color(255, 255, 0); 81 | 82 | /** 83 | * A brighter blue. 84 | */ 85 | public static Color brightBlue = new Color(0, 0, 255); 86 | 87 | /** 88 | * A brighter magenta. 89 | */ 90 | public static Color brightMagenta = new Color(255, 0, 255); 91 | 92 | /** 93 | * A brighter cyan. 94 | */ 95 | public static Color brightCyan = new Color(0, 255, 255); 96 | 97 | /** 98 | * A brighter white (pure white). 99 | */ 100 | public static Color brightWhite = new Color(255, 255, 255); 101 | 102 | private Image offscreenBuffer; 103 | private Graphics offscreenGraphics; 104 | private int widthInCharacters; 105 | private int heightInCharacters; 106 | private int charWidth = 9; 107 | private int charHeight = 16; 108 | private String terminalFontFile = "cp437_9x16.png"; 109 | private Color defaultBackgroundColor; 110 | private Color defaultForegroundColor; 111 | private int cursorX; 112 | private int cursorY; 113 | private BufferedImage glyphSprite; 114 | private BufferedImage[] glyphs; 115 | private AsciiCharacterData[][] characters; 116 | private AsciiCharacterData[][] previousCharacters; 117 | private AsciiFont asciiFont; 118 | 119 | /** 120 | * Gets the height, in pixels, of a character. 121 | * @return 122 | */ 123 | public int getCharHeight() { 124 | return charHeight; 125 | } 126 | 127 | /** 128 | * Gets the width, in pixels, of a character. 129 | * @return 130 | */ 131 | public int getCharWidth() { 132 | return charWidth; 133 | } 134 | 135 | /** 136 | * Gets the height in characters. 137 | * A standard terminal is 24 characters high. 138 | * @return 139 | */ 140 | public int getHeightInCharacters() { 141 | return heightInCharacters; 142 | } 143 | 144 | /** 145 | * Gets the width in characters. 146 | * A standard terminal is 80 characters wide. 147 | * @return 148 | */ 149 | public int getWidthInCharacters() { 150 | return widthInCharacters; 151 | } 152 | 153 | /** 154 | * Gets the distance from the left new text will be written to. 155 | * @return 156 | */ 157 | public int getCursorX() { 158 | return cursorX; 159 | } 160 | 161 | /** 162 | * Sets the distance from the left new text will be written to. 163 | * This should be equal to or greater than 0 and less than the the width in characters. 164 | * @param cursorX the distance from the left new text should be written to 165 | */ 166 | public void setCursorX(int cursorX) { 167 | if (cursorX < 0 || cursorX >= widthInCharacters) 168 | throw new IllegalArgumentException("cursorX " + cursorX + " must be within range [0," + widthInCharacters + ")." ); 169 | 170 | this.cursorX = cursorX; 171 | } 172 | 173 | /** 174 | * Gets the distance from the top new text will be written to. 175 | * @return 176 | */ 177 | public int getCursorY() { 178 | return cursorY; 179 | } 180 | 181 | /** 182 | * Sets the distance from the top new text will be written to. 183 | * This should be equal to or greater than 0 and less than the the height in characters. 184 | * @param cursorY the distance from the top new text should be written to 185 | */ 186 | public void setCursorY(int cursorY) { 187 | if (cursorY < 0 || cursorY >= heightInCharacters) 188 | throw new IllegalArgumentException("cursorY " + cursorY + " must be within range [0," + heightInCharacters + ")." ); 189 | 190 | this.cursorY = cursorY; 191 | } 192 | 193 | /** 194 | * Sets the x and y position of where new text will be written to. The origin (0,0) is the upper left corner. 195 | * The x should be equal to or greater than 0 and less than the the width in characters. 196 | * The y should be equal to or greater than 0 and less than the the height in characters. 197 | * @param x the distance from the left new text should be written to 198 | * @param y the distance from the top new text should be written to 199 | */ 200 | public void setCursorPosition(int x, int y) { 201 | setCursorX(x); 202 | setCursorY(y); 203 | } 204 | 205 | /** 206 | * Gets the default background color that is used when writing new text. 207 | * @return 208 | */ 209 | public Color getDefaultBackgroundColor() { 210 | return defaultBackgroundColor; 211 | } 212 | 213 | /** 214 | * Sets the default background color that is used when writing new text. 215 | * @param defaultBackgroundColor 216 | */ 217 | public void setDefaultBackgroundColor(Color defaultBackgroundColor) { 218 | if (defaultBackgroundColor == null) 219 | throw new NullPointerException("defaultBackgroundColor must not be null."); 220 | 221 | this.defaultBackgroundColor = defaultBackgroundColor; 222 | } 223 | 224 | /** 225 | * Gets the default foreground color that is used when writing new text. 226 | * @return 227 | */ 228 | public Color getDefaultForegroundColor() { 229 | return defaultForegroundColor; 230 | } 231 | 232 | /** 233 | * Sets the default foreground color that is used when writing new text. 234 | * @param defaultForegroundColor 235 | */ 236 | public void setDefaultForegroundColor(Color defaultForegroundColor) { 237 | if (defaultForegroundColor == null) 238 | throw new NullPointerException("defaultForegroundColor must not be null."); 239 | 240 | this.defaultForegroundColor = defaultForegroundColor; 241 | } 242 | 243 | /** 244 | * Gets the currently selected font 245 | * @return 246 | */ 247 | public AsciiFont getAsciiFont() { 248 | return asciiFont; 249 | } 250 | 251 | /** 252 | * Sets the used font. It is advisable to make sure the parent component is properly sized after setting the font 253 | * as the panel dimensions will most likely change 254 | * @param font 255 | */ 256 | public void setAsciiFont(AsciiFont font) 257 | { 258 | if(this.asciiFont == font) 259 | { 260 | return; 261 | } 262 | this.asciiFont = font; 263 | 264 | this.charHeight = font.getHeight(); 265 | this.charWidth = font.getWidth(); 266 | this.terminalFontFile = font.getFontFilename(); 267 | 268 | Dimension panelSize = new Dimension(charWidth * widthInCharacters, charHeight * heightInCharacters); 269 | setPreferredSize(panelSize); 270 | 271 | glyphs = new BufferedImage[256]; 272 | 273 | offscreenBuffer = new BufferedImage(panelSize.width, panelSize.height, BufferedImage.TYPE_INT_RGB); 274 | offscreenGraphics = offscreenBuffer.getGraphics(); 275 | 276 | loadGlyphs(); 277 | 278 | previousCharacters = new AsciiCharacterData[widthInCharacters][heightInCharacters]; 279 | } 280 | 281 | /** 282 | * Gets the AsciiCharacterDataValues which are currently written 283 | * @return 284 | */ 285 | public AsciiCharacterData[][] getCharacters() { 286 | return characters; 287 | } 288 | 289 | /** 290 | * Class constructor. 291 | * Default size is 80x24. 292 | */ 293 | public AsciiPanel() { 294 | this(80, 24); 295 | } 296 | 297 | /** 298 | * Class constructor specifying the width and height in characters. 299 | * @param width 300 | * @param height 301 | */ 302 | public AsciiPanel(int width, int height) { 303 | this(width, height, null); 304 | } 305 | 306 | /** 307 | * Class constructor specifying the width and height in characters and the AsciiFont 308 | * @param width 309 | * @param height 310 | * @param font if passing null, standard font CP437_9x16 will be used 311 | */ 312 | public AsciiPanel(int width, int height, AsciiFont font) { 313 | super(); 314 | 315 | if (width < 1) { 316 | throw new IllegalArgumentException("width " + width + " must be greater than 0." ); 317 | } 318 | 319 | if (height < 1) { 320 | throw new IllegalArgumentException("height " + height + " must be greater than 0." ); 321 | } 322 | 323 | widthInCharacters = width; 324 | heightInCharacters = height; 325 | 326 | defaultBackgroundColor = black; 327 | defaultForegroundColor = white; 328 | 329 | characters = new AsciiCharacterData[widthInCharacters][heightInCharacters]; 330 | 331 | if(font == null) { 332 | font = AsciiFont.CP437_9x16; 333 | } 334 | setAsciiFont(font); 335 | clear(); 336 | } 337 | 338 | @Override 339 | public void update(Graphics g) { 340 | paint(g); 341 | } 342 | 343 | @Override 344 | public void paint(Graphics g) { 345 | if (g == null) 346 | throw new NullPointerException(); 347 | 348 | for (int x = 0; x < widthInCharacters; x++) { 349 | for (int y = 0; y < heightInCharacters; y++) { 350 | AsciiCharacterData previousCharacterData = previousCharacters[x][y]; 351 | AsciiCharacterData newCharacterData = characters[x][y]; 352 | 353 | if (previousCharacterData != null 354 | && newCharacterData.backgroundColor == previousCharacterData.backgroundColor 355 | && newCharacterData.foregroundColor == previousCharacterData.foregroundColor 356 | && newCharacterData.character == previousCharacterData.character) 357 | continue; 358 | 359 | LookupOp op = setColors(newCharacterData.backgroundColor, newCharacterData.foregroundColor); 360 | BufferedImage img = op.filter(glyphs[newCharacterData.character], null); 361 | offscreenGraphics.drawImage(img, x * charWidth, y * charHeight, null); 362 | 363 | previousCharacters[x][y] = newCharacterData; 364 | } 365 | } 366 | 367 | g.drawImage(offscreenBuffer,0,0,this); 368 | } 369 | 370 | private void loadGlyphs() { 371 | try { 372 | glyphSprite = ImageIO.read(AsciiPanel.class.getClassLoader().getResource(terminalFontFile)); 373 | } catch (IOException e) { 374 | System.err.println("loadGlyphs(): " + e.getMessage()); 375 | } 376 | 377 | for (int i = 0; i < 256; i++) { 378 | int sx = (i % 16) * charWidth; 379 | int sy = (i / 16) * charHeight; 380 | 381 | glyphs[i] = new BufferedImage(charWidth, charHeight, BufferedImage.TYPE_INT_ARGB); 382 | glyphs[i].getGraphics().drawImage(glyphSprite, 0, 0, charWidth, charHeight, sx, sy, sx + charWidth, sy + charHeight, null); 383 | } 384 | } 385 | 386 | /** 387 | * Create a LookupOp object (lookup table) mapping the original 388 | * pixels to the background and foreground colors, respectively. 389 | * @param bgColor the background color 390 | * @param fgColor the foreground color 391 | * @return the LookupOp object (lookup table) 392 | */ 393 | private LookupOp setColors(Color bgColor, Color fgColor) { 394 | short[] a = new short[256]; 395 | short[] r = new short[256]; 396 | short[] g = new short[256]; 397 | short[] b = new short[256]; 398 | 399 | byte bga = (byte) (bgColor.getAlpha()); 400 | byte bgr = (byte) (bgColor.getRed()); 401 | byte bgg = (byte) (bgColor.getGreen()); 402 | byte bgb = (byte) (bgColor.getBlue()); 403 | 404 | byte fga = (byte) (fgColor.getAlpha()); 405 | byte fgr = (byte) (fgColor.getRed()); 406 | byte fgg = (byte) (fgColor.getGreen()); 407 | byte fgb = (byte) (fgColor.getBlue()); 408 | 409 | for (int i = 0; i < 256; i++) { 410 | if (i == 0) { 411 | a[i] = bga; 412 | r[i] = bgr; 413 | g[i] = bgg; 414 | b[i] = bgb; 415 | } else { 416 | a[i] = fga; 417 | r[i] = fgr; 418 | g[i] = fgg; 419 | b[i] = fgb; 420 | } 421 | } 422 | 423 | short[][] table = {r, g, b, a}; 424 | return new LookupOp(new ShortLookupTable(0, table), null); 425 | } 426 | 427 | /** 428 | * Clear the entire screen to whatever the default background color is. 429 | * @return this for convenient chaining of method calls 430 | */ 431 | public AsciiPanel clear() { 432 | return clear(' ', 0, 0, widthInCharacters, heightInCharacters, defaultForegroundColor, defaultBackgroundColor); 433 | } 434 | 435 | /** 436 | * Clear the entire screen with the specified character and whatever the default foreground and background colors are. 437 | * @param character the character to write 438 | * @return this for convenient chaining of method calls 439 | */ 440 | public AsciiPanel clear(char character) { 441 | return clear(character, 0, 0, widthInCharacters, heightInCharacters, defaultForegroundColor, defaultBackgroundColor); 442 | } 443 | 444 | /** 445 | * Clear the entire screen with the specified character and whatever the specified foreground and background colors are. 446 | * @param character the character to write 447 | * @param foreground the foreground color or null to use the default 448 | * @param background the background color or null to use the default 449 | * @return this for convenient chaining of method calls 450 | */ 451 | public AsciiPanel clear(char character, Color foreground, Color background) { 452 | return clear(character, 0, 0, widthInCharacters, heightInCharacters, foreground, background); 453 | } 454 | 455 | /** 456 | * Clear the section of the screen with the specified character and whatever the default foreground and background colors are. 457 | * The cursor position will not be modified. 458 | * @param character the character to write 459 | * @param x the distance from the left to begin writing from 460 | * @param y the distance from the top to begin writing from 461 | * @param width the height of the section to clear 462 | * @param height the width of the section to clear 463 | * @return this for convenient chaining of method calls 464 | */ 465 | public AsciiPanel clear(char character, int x, int y, int width, int height) { 466 | return clear(new AsciiCharacterData(character, defaultForegroundColor, defaultBackgroundColor), x, y, width, height); 467 | } 468 | 469 | /** 470 | * Clear the section of the screen with the specified character and whatever the specified foreground and background colors are. 471 | * @param character the character to write 472 | * @param x the distance from the left to begin writing from 473 | * @param y the distance from the top to begin writing from 474 | * @param width the height of the section to clear 475 | * @param height the width of the section to clear 476 | * @param foreground the foreground color or null to use the default 477 | * @param background the background color or null to use the default 478 | * @return this for convenient chaining of method calls 479 | */ 480 | public AsciiPanel clear(char character, int x, int y, int width, int height, Color foreground, Color background) { 481 | return clear(new AsciiCharacterData(character, foreground, background), x, y, width, height); 482 | } 483 | 484 | /** 485 | * Clear the section of the screen with the specified character and whatever the specified foreground and background colors are. 486 | * @param characterData the AsciiCharacterData to write 487 | * @param x the distance from the left to begin writing from 488 | * @param y the distance from the top to begin writing from 489 | * @param width the height of the section to clear 490 | * @param height the width of the section to clear 491 | * @return this for convenient chaining of method calls 492 | */ 493 | public AsciiPanel clear(AsciiCharacterData characterData, int x, int y, int width, int height) { 494 | if (characterData.character < 0 || characterData.character >= glyphs.length) 495 | throw new IllegalArgumentException("character " + characterData.character + " must be within range [0," + glyphs.length + "]." ); 496 | 497 | if (x < 0 || x >= widthInCharacters) 498 | throw new IllegalArgumentException("x " + x + " must be within range [0," + widthInCharacters + ")" ); 499 | 500 | if (y < 0 || y >= heightInCharacters) 501 | throw new IllegalArgumentException("y " + y + " must be within range [0," + heightInCharacters + ")" ); 502 | 503 | if (width < 1) 504 | throw new IllegalArgumentException("width " + width + " must be greater than 0." ); 505 | 506 | if (height < 1) 507 | throw new IllegalArgumentException("height " + height + " must be greater than 0." ); 508 | 509 | if (x + width > widthInCharacters) 510 | throw new IllegalArgumentException("x + width " + (x + width) + " must be less than " + (widthInCharacters + 1) + "." ); 511 | 512 | if (y + height > heightInCharacters) 513 | throw new IllegalArgumentException("y + height " + (y + height) + " must be less than " + (heightInCharacters + 1) + "." ); 514 | 515 | int originalCursorX = cursorX; 516 | int originalCursorY = cursorY; 517 | for (int xo = x; xo < x + width; xo++) { 518 | for (int yo = y; yo < y + height; yo++) { 519 | write(characterData, xo, yo); 520 | } 521 | } 522 | cursorX = originalCursorX; 523 | cursorY = originalCursorY; 524 | 525 | return this; 526 | } 527 | 528 | /** 529 | * Write a character to the cursor's position. 530 | * This updates the cursor's position. 531 | * @param character the character to write 532 | * @return this for convenient chaining of method calls 533 | */ 534 | public AsciiPanel write(char character) { 535 | return write(character, cursorX, cursorY, defaultForegroundColor, defaultBackgroundColor); 536 | } 537 | 538 | /** 539 | * Write a character to the cursor's position with the specified foreground color. 540 | * This updates the cursor's position but not the default foreground color. 541 | * @param character the character to write 542 | * @param foreground the foreground color or null to use the default 543 | * @return this for convenient chaining of method calls 544 | */ 545 | public AsciiPanel write(char character, Color foreground) { 546 | return write(character, cursorX, cursorY, foreground, defaultBackgroundColor); 547 | } 548 | 549 | /** 550 | * Write a character to the cursor's position with the specified foreground and background colors. 551 | * This updates the cursor's position but not the default foreground or background colors. 552 | * @param character the character to write 553 | * @param foreground the foreground color or null to use the default 554 | * @param background the background color or null to use the default 555 | * @return this for convenient chaining of method calls 556 | */ 557 | public AsciiPanel write(char character, Color foreground, Color background) { 558 | return write(character, cursorX, cursorY, foreground, background); 559 | } 560 | 561 | /** 562 | * Write a character to the specified position. 563 | * This updates the cursor's position. 564 | * @param character the character to write 565 | * @param x the distance from the left to begin writing from 566 | * @param y the distance from the top to begin writing from 567 | * @return this for convenient chaining of method calls 568 | */ 569 | public AsciiPanel write(char character, int x, int y) { 570 | return write(character, x, y, defaultForegroundColor, defaultBackgroundColor); 571 | } 572 | 573 | /** 574 | * Write a character to the specified position with the specified foreground color. 575 | * This updates the cursor's position but not the default foreground color. 576 | * @param character the character to write 577 | * @param x the distance from the left to begin writing from 578 | * @param y the distance from the top to begin writing from 579 | * @param foreground the foreground color or null to use the default 580 | * @return this for convenient chaining of method calls 581 | */ 582 | public AsciiPanel write(char character, int x, int y, Color foreground) { 583 | return write(character, x, y, foreground, defaultBackgroundColor); 584 | } 585 | 586 | /** 587 | * Write a character to the specified position with the specified foreground and background colors. 588 | * This updates the cursor's position but not the default foreground or background colors. 589 | * @param character the character to write 590 | * @param x the distance from the left to begin writing from 591 | * @param y the distance from the top to begin writing from 592 | * @param foreground the foreground color or null to use the default 593 | * @param background the background color or null to use the default 594 | * @return this for convenient chaining of method calls 595 | */ 596 | public AsciiPanel write(char character, int x, int y, Color foreground, Color background) { 597 | return write(new AsciiCharacterData(character, foreground, background), x, y); 598 | } 599 | 600 | /** 601 | * Write an AsciiCharacterData to the specified position. 602 | * This updates the cursor's position but not the default foreground or background colors. 603 | * @param characterData the AsciiCharacterData to write 604 | * @param x the distance from the left to begin writing from 605 | * @param y the distance from the top to begin writing from 606 | * @return this for convenient chaining of method calls 607 | */ 608 | public AsciiPanel write(AsciiCharacterData characterData, int x, int y) { 609 | if (characterData.character < 0 || characterData.character >= glyphs.length) 610 | throw new IllegalArgumentException("character " + characterData.character + " must be within range [0," + glyphs.length + "]." ); 611 | 612 | if (x < 0 || x >= widthInCharacters) 613 | throw new IllegalArgumentException("x " + x + " must be within range [0," + widthInCharacters + ")" ); 614 | 615 | if (y < 0 || y >= heightInCharacters) 616 | throw new IllegalArgumentException("y " + y + " must be within range [0," + heightInCharacters + ")" ); 617 | 618 | if (characterData.foregroundColor == null) { 619 | characterData.foregroundColor = defaultForegroundColor; 620 | } 621 | 622 | if (characterData.backgroundColor == null) { 623 | characterData.backgroundColor = defaultBackgroundColor; 624 | } 625 | 626 | characters[x][y] = characterData; 627 | cursorX = x + 1; 628 | cursorY = y; 629 | return this; 630 | } 631 | 632 | /** 633 | * Write a string to the cursor's position. 634 | * This updates the cursor's position. 635 | * @param string the string to write 636 | * @return this for convenient chaining of method calls 637 | */ 638 | public AsciiPanel write(String string) { 639 | if (string == null) 640 | throw new NullPointerException("string must not be null" ); 641 | 642 | if (cursorX + string.length() > widthInCharacters) 643 | throw new IllegalArgumentException("cursorX + string.length() " + (cursorX + string.length()) + " must be less than " + widthInCharacters + "."); 644 | 645 | return write(string, cursorX, cursorY, defaultForegroundColor, defaultBackgroundColor); 646 | } 647 | 648 | /** 649 | * Write a string to the cursor's position with the specified foreground color. 650 | * This updates the cursor's position but not the default foreground color. 651 | * @param string the string to write 652 | * @param foreground the foreground color or null to use the default 653 | * @return this for convenient chaining of method calls 654 | */ 655 | public AsciiPanel write(String string, Color foreground) { 656 | if (string == null) 657 | throw new NullPointerException("string must not be null" ); 658 | 659 | if (cursorX + string.length() > widthInCharacters) 660 | throw new IllegalArgumentException("cursorX + string.length() " + (cursorX + string.length()) + " must be less than " + widthInCharacters + "." ); 661 | 662 | return write(string, cursorX, cursorY, foreground, defaultBackgroundColor); 663 | } 664 | 665 | /** 666 | * Write a string to the cursor's position with the specified foreground and background colors. 667 | * This updates the cursor's position but not the default foreground or background colors. 668 | * @param string the string to write 669 | * @param foreground the foreground color or null to use the default 670 | * @param background the background color or null to use the default 671 | * @return this for convenient chaining of method calls 672 | */ 673 | public AsciiPanel write(String string, Color foreground, Color background) { 674 | if (string == null) 675 | throw new NullPointerException("string must not be null" ); 676 | 677 | if (cursorX + string.length() > widthInCharacters) 678 | throw new IllegalArgumentException("cursorX + string.length() " + (cursorX + string.length()) + " must be less than " + widthInCharacters + "." ); 679 | 680 | return write(string, cursorX, cursorY, foreground, background); 681 | } 682 | 683 | /** 684 | * Write a string to the specified position. 685 | * This updates the cursor's position. 686 | * @param string the string to write 687 | * @param x the distance from the left to begin writing from 688 | * @param y the distance from the top to begin writing from 689 | * @return this for convenient chaining of method calls 690 | */ 691 | public AsciiPanel write(String string, int x, int y) { 692 | if (string == null) 693 | throw new NullPointerException("string must not be null" ); 694 | 695 | if (x + string.length() > widthInCharacters) 696 | throw new IllegalArgumentException("x + string.length() " + (x + string.length()) + " must be less than " + widthInCharacters + "." ); 697 | 698 | if (x < 0 || x >= widthInCharacters) 699 | throw new IllegalArgumentException("x " + x + " must be within range [0," + widthInCharacters + ")" ); 700 | 701 | if (y < 0 || y >= heightInCharacters) 702 | throw new IllegalArgumentException("y " + y + " must be within range [0," + heightInCharacters + ")" ); 703 | 704 | return write(string, x, y, defaultForegroundColor, defaultBackgroundColor); 705 | } 706 | 707 | /** 708 | * Write a string to the specified position with the specified foreground color. 709 | * This updates the cursor's position but not the default foreground color. 710 | * @param string the string to write 711 | * @param x the distance from the left to begin writing from 712 | * @param y the distance from the top to begin writing from 713 | * @param foreground the foreground color or null to use the default 714 | * @return this for convenient chaining of method calls 715 | */ 716 | public AsciiPanel write(String string, int x, int y, Color foreground) { 717 | if (string == null) 718 | throw new NullPointerException("string must not be null" ); 719 | 720 | if (x + string.length() > widthInCharacters) 721 | throw new IllegalArgumentException("x + string.length() " + (x + string.length()) + " must be less than " + widthInCharacters + "." ); 722 | 723 | if (x < 0 || x >= widthInCharacters) 724 | throw new IllegalArgumentException("x " + x + " must be within range [0," + widthInCharacters + ")" ); 725 | 726 | if (y < 0 || y >= heightInCharacters) 727 | throw new IllegalArgumentException("y " + y + " must be within range [0," + heightInCharacters + ")" ); 728 | 729 | return write(string, x, y, foreground, defaultBackgroundColor); 730 | } 731 | 732 | /** 733 | * Write a string to the specified position with the specified foreground and background colors. 734 | * This updates the cursor's position but not the default foreground or background colors. 735 | * @param string the string to write 736 | * @param x the distance from the left to begin writing from 737 | * @param y the distance from the top to begin writing from 738 | * @param foreground the foreground color or null to use the default 739 | * @param background the background color or null to use the default 740 | * @return this for convenient chaining of method calls 741 | */ 742 | public AsciiPanel write(String string, int x, int y, Color foreground, Color background) { 743 | if (string == null) 744 | throw new NullPointerException("string must not be null." ); 745 | 746 | if (x + string.length() > widthInCharacters) 747 | throw new IllegalArgumentException("x + string.length() " + (x + string.length()) + " must be less than " + widthInCharacters + "." ); 748 | 749 | if (x < 0 || x >= widthInCharacters) 750 | throw new IllegalArgumentException("x " + x + " must be within range [0," + widthInCharacters + ")." ); 751 | 752 | if (y < 0 || y >= heightInCharacters) 753 | throw new IllegalArgumentException("y " + y + " must be within range [0," + heightInCharacters + ")." ); 754 | 755 | if (foreground == null) 756 | foreground = defaultForegroundColor; 757 | 758 | if (background == null) 759 | background = defaultBackgroundColor; 760 | 761 | for (int i = 0; i < string.length(); i++) { 762 | write(string.charAt(i), x + i, y, foreground, background); 763 | } 764 | return this; 765 | } 766 | 767 | /** 768 | * Write a string to the center of the panel at the specified y position. 769 | * This updates the cursor's position. 770 | * @param string the string to write 771 | * @param y the distance from the top to begin writing from 772 | * @return this for convenient chaining of method calls 773 | */ 774 | public AsciiPanel writeCenter(String string, int y) { 775 | if (string == null) 776 | throw new NullPointerException("string must not be null" ); 777 | 778 | if (string.length() > widthInCharacters) 779 | throw new IllegalArgumentException("string.length() " + string.length() + " must be less than " + widthInCharacters + "." ); 780 | 781 | int x = (widthInCharacters - string.length()) / 2; 782 | 783 | if (y < 0 || y >= heightInCharacters) 784 | throw new IllegalArgumentException("y " + y + " must be within range [0," + heightInCharacters + ")" ); 785 | 786 | return write(string, x, y, defaultForegroundColor, defaultBackgroundColor); 787 | } 788 | 789 | /** 790 | * Write a string to the center of the panel at the specified y position with the specified foreground color. 791 | * This updates the cursor's position but not the default foreground color. 792 | * @param string the string to write 793 | * @param y the distance from the top to begin writing from 794 | * @param foreground the foreground color or null to use the default 795 | * @return this for convenient chaining of method calls 796 | */ 797 | public AsciiPanel writeCenter(String string, int y, Color foreground) { 798 | if (string == null) 799 | throw new NullPointerException("string must not be null" ); 800 | 801 | if (string.length() > widthInCharacters) 802 | throw new IllegalArgumentException("string.length() " + string.length() + " must be less than " + widthInCharacters + "." ); 803 | 804 | int x = (widthInCharacters - string.length()) / 2; 805 | 806 | if (y < 0 || y >= heightInCharacters) 807 | throw new IllegalArgumentException("y " + y + " must be within range [0," + heightInCharacters + ")" ); 808 | 809 | return write(string, x, y, foreground, defaultBackgroundColor); 810 | } 811 | 812 | /** 813 | * Write a string to the center of the panel at the specified y position with the specified foreground and background colors. 814 | * This updates the cursor's position but not the default foreground or background colors. 815 | * @param string the string to write 816 | * @param y the distance from the top to begin writing from 817 | * @param foreground the foreground color or null to use the default 818 | * @param background the background color or null to use the default 819 | * @return this for convenient chaining of method calls 820 | */ 821 | public AsciiPanel writeCenter(String string, int y, Color foreground, Color background) { 822 | if (string == null) 823 | throw new NullPointerException("string must not be null." ); 824 | 825 | if (string.length() > widthInCharacters) 826 | throw new IllegalArgumentException("string.length() " + string.length() + " must be less than " + widthInCharacters + "." ); 827 | 828 | int x = (widthInCharacters - string.length()) / 2; 829 | 830 | if (y < 0 || y >= heightInCharacters) 831 | throw new IllegalArgumentException("y " + y + " must be within range [0," + heightInCharacters + ")." ); 832 | 833 | if (foreground == null) 834 | foreground = defaultForegroundColor; 835 | 836 | if (background == null) 837 | background = defaultBackgroundColor; 838 | 839 | for (int i = 0; i < string.length(); i++) { 840 | write(string.charAt(i), x + i, y, foreground, background); 841 | } 842 | return this; 843 | } 844 | 845 | public void withEachTile(TileTransformer transformer){ 846 | withEachTile(0, 0, widthInCharacters, heightInCharacters, transformer); 847 | } 848 | 849 | public void withEachTile(int left, int top, int width, int height, TileTransformer transformer){ 850 | for (int x0 = 0; x0 < width; x0++) { 851 | for (int y0 = 0; y0 < height; y0++) { 852 | int x = left + x0; 853 | int y = top + y0; 854 | 855 | if (x < 0 || y < 0 || x >= widthInCharacters || y >= heightInCharacters) 856 | continue; 857 | 858 | transformer.transformTile(x, y, characters[x][y]); 859 | } 860 | } 861 | } 862 | } 863 | --------------------------------------------------------------------------------