|
| 1 | +package com.easyclaims.map; |
| 2 | + |
| 3 | +/** |
| 4 | + * 5x7 bitmap font for rendering text on map tiles. |
| 5 | + * Each character is stored as an array of 7 rows, where each row is a 5-bit bitmask. |
| 6 | + */ |
| 7 | +public class BitmapFont { |
| 8 | + |
| 9 | + public static final int CHAR_WIDTH = 5; |
| 10 | + public static final int CHAR_HEIGHT = 7; |
| 11 | + public static final int CHAR_SPACING = 1; |
| 12 | + |
| 13 | + // Font data: 5x7 pixels per character |
| 14 | + private static final int[][] GLYPHS = new int[128][]; |
| 15 | + |
| 16 | + static { |
| 17 | + // Letters A-Z (5x7) |
| 18 | + GLYPHS['A'] = new int[]{0b01110, 0b10001, 0b10001, 0b11111, 0b10001, 0b10001, 0b10001}; |
| 19 | + GLYPHS['B'] = new int[]{0b11110, 0b10001, 0b10001, 0b11110, 0b10001, 0b10001, 0b11110}; |
| 20 | + GLYPHS['C'] = new int[]{0b01110, 0b10001, 0b10000, 0b10000, 0b10000, 0b10001, 0b01110}; |
| 21 | + GLYPHS['D'] = new int[]{0b11110, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b11110}; |
| 22 | + GLYPHS['E'] = new int[]{0b11111, 0b10000, 0b10000, 0b11110, 0b10000, 0b10000, 0b11111}; |
| 23 | + GLYPHS['F'] = new int[]{0b11111, 0b10000, 0b10000, 0b11110, 0b10000, 0b10000, 0b10000}; |
| 24 | + GLYPHS['G'] = new int[]{0b01110, 0b10001, 0b10000, 0b10111, 0b10001, 0b10001, 0b01110}; |
| 25 | + GLYPHS['H'] = new int[]{0b10001, 0b10001, 0b10001, 0b11111, 0b10001, 0b10001, 0b10001}; |
| 26 | + GLYPHS['I'] = new int[]{0b11111, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b11111}; |
| 27 | + GLYPHS['J'] = new int[]{0b00111, 0b00010, 0b00010, 0b00010, 0b00010, 0b10010, 0b01100}; |
| 28 | + GLYPHS['K'] = new int[]{0b10001, 0b10010, 0b10100, 0b11000, 0b10100, 0b10010, 0b10001}; |
| 29 | + GLYPHS['L'] = new int[]{0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b11111}; |
| 30 | + GLYPHS['M'] = new int[]{0b10001, 0b11011, 0b10101, 0b10101, 0b10001, 0b10001, 0b10001}; |
| 31 | + GLYPHS['N'] = new int[]{0b10001, 0b11001, 0b10101, 0b10101, 0b10011, 0b10001, 0b10001}; |
| 32 | + GLYPHS['O'] = new int[]{0b01110, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b01110}; |
| 33 | + GLYPHS['P'] = new int[]{0b11110, 0b10001, 0b10001, 0b11110, 0b10000, 0b10000, 0b10000}; |
| 34 | + GLYPHS['Q'] = new int[]{0b01110, 0b10001, 0b10001, 0b10001, 0b10101, 0b10010, 0b01101}; |
| 35 | + GLYPHS['R'] = new int[]{0b11110, 0b10001, 0b10001, 0b11110, 0b10100, 0b10010, 0b10001}; |
| 36 | + GLYPHS['S'] = new int[]{0b01110, 0b10001, 0b10000, 0b01110, 0b00001, 0b10001, 0b01110}; |
| 37 | + GLYPHS['T'] = new int[]{0b11111, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100}; |
| 38 | + GLYPHS['U'] = new int[]{0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b01110}; |
| 39 | + GLYPHS['V'] = new int[]{0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b01010, 0b00100}; |
| 40 | + GLYPHS['W'] = new int[]{0b10001, 0b10001, 0b10001, 0b10101, 0b10101, 0b11011, 0b10001}; |
| 41 | + GLYPHS['X'] = new int[]{0b10001, 0b10001, 0b01010, 0b00100, 0b01010, 0b10001, 0b10001}; |
| 42 | + GLYPHS['Y'] = new int[]{0b10001, 0b10001, 0b01010, 0b00100, 0b00100, 0b00100, 0b00100}; |
| 43 | + GLYPHS['Z'] = new int[]{0b11111, 0b00001, 0b00010, 0b00100, 0b01000, 0b10000, 0b11111}; |
| 44 | + |
| 45 | + // Lowercase maps to uppercase |
| 46 | + for (char c = 'a'; c <= 'z'; c++) { |
| 47 | + GLYPHS[c] = GLYPHS[Character.toUpperCase(c)]; |
| 48 | + } |
| 49 | + |
| 50 | + // Numbers 0-9 |
| 51 | + GLYPHS['0'] = new int[]{0b01110, 0b10001, 0b10011, 0b10101, 0b11001, 0b10001, 0b01110}; |
| 52 | + GLYPHS['1'] = new int[]{0b00100, 0b01100, 0b00100, 0b00100, 0b00100, 0b00100, 0b01110}; |
| 53 | + GLYPHS['2'] = new int[]{0b01110, 0b10001, 0b00001, 0b00110, 0b01000, 0b10000, 0b11111}; |
| 54 | + GLYPHS['3'] = new int[]{0b01110, 0b10001, 0b00001, 0b00110, 0b00001, 0b10001, 0b01110}; |
| 55 | + GLYPHS['4'] = new int[]{0b00010, 0b00110, 0b01010, 0b10010, 0b11111, 0b00010, 0b00010}; |
| 56 | + GLYPHS['5'] = new int[]{0b11111, 0b10000, 0b11110, 0b00001, 0b00001, 0b10001, 0b01110}; |
| 57 | + GLYPHS['6'] = new int[]{0b00110, 0b01000, 0b10000, 0b11110, 0b10001, 0b10001, 0b01110}; |
| 58 | + GLYPHS['7'] = new int[]{0b11111, 0b00001, 0b00010, 0b00100, 0b01000, 0b01000, 0b01000}; |
| 59 | + GLYPHS['8'] = new int[]{0b01110, 0b10001, 0b10001, 0b01110, 0b10001, 0b10001, 0b01110}; |
| 60 | + GLYPHS['9'] = new int[]{0b01110, 0b10001, 0b10001, 0b01111, 0b00001, 0b00010, 0b01100}; |
| 61 | + |
| 62 | + // Special characters |
| 63 | + GLYPHS[' '] = new int[]{0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000}; |
| 64 | + GLYPHS['.'] = new int[]{0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b01100, 0b01100}; |
| 65 | + GLYPHS[','] = new int[]{0b00000, 0b00000, 0b00000, 0b00000, 0b00100, 0b00100, 0b01000}; |
| 66 | + GLYPHS[':'] = new int[]{0b00000, 0b01100, 0b01100, 0b00000, 0b01100, 0b01100, 0b00000}; |
| 67 | + GLYPHS['-'] = new int[]{0b00000, 0b00000, 0b00000, 0b11111, 0b00000, 0b00000, 0b00000}; |
| 68 | + GLYPHS['_'] = new int[]{0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111}; |
| 69 | + GLYPHS['+'] = new int[]{0b00000, 0b00100, 0b00100, 0b11111, 0b00100, 0b00100, 0b00000}; |
| 70 | + GLYPHS['\''] = new int[]{0b00100, 0b00100, 0b01000, 0b00000, 0b00000, 0b00000, 0b00000}; |
| 71 | + GLYPHS['!'] = new int[]{0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b00000, 0b00100}; |
| 72 | + GLYPHS['?'] = new int[]{0b01110, 0b10001, 0b00010, 0b00100, 0b00100, 0b00000, 0b00100}; |
| 73 | + } |
| 74 | + |
| 75 | + /** |
| 76 | + * Calculate the width in pixels needed to render a string. |
| 77 | + */ |
| 78 | + public static int getTextWidth(String text) { |
| 79 | + if (text == null || text.isEmpty()) return 0; |
| 80 | + return text.length() * CHAR_WIDTH + (text.length() - 1) * CHAR_SPACING; |
| 81 | + } |
| 82 | + |
| 83 | + /** |
| 84 | + * Draw text onto an image data array. |
| 85 | + */ |
| 86 | + public static void drawText(int[] imageData, int imageWidth, int imageHeight, |
| 87 | + String text, int startX, int startY, int color) { |
| 88 | + if (text == null) return; |
| 89 | + |
| 90 | + int x = startX; |
| 91 | + for (int i = 0; i < text.length(); i++) { |
| 92 | + char c = text.charAt(i); |
| 93 | + drawChar(imageData, imageWidth, imageHeight, c, x, startY, color); |
| 94 | + x += CHAR_WIDTH + CHAR_SPACING; |
| 95 | + } |
| 96 | + } |
| 97 | + |
| 98 | + /** |
| 99 | + * Draw text centered horizontally within the image. |
| 100 | + */ |
| 101 | + public static void drawTextCentered(int[] imageData, int imageWidth, int imageHeight, |
| 102 | + String text, int centerY, int color) { |
| 103 | + int textWidth = getTextWidth(text); |
| 104 | + int startX = (imageWidth - textWidth) / 2; |
| 105 | + drawText(imageData, imageWidth, imageHeight, text, startX, centerY, color); |
| 106 | + } |
| 107 | + |
| 108 | + /** |
| 109 | + * Draw text with an outline for better visibility. |
| 110 | + */ |
| 111 | + public static void drawTextWithOutline(int[] imageData, int imageWidth, int imageHeight, |
| 112 | + String text, int startX, int startY, |
| 113 | + int textColor, int outlineColor) { |
| 114 | + // Draw outline in all 8 directions |
| 115 | + for (int dx = -1; dx <= 1; dx++) { |
| 116 | + for (int dy = -1; dy <= 1; dy++) { |
| 117 | + if (dx != 0 || dy != 0) { |
| 118 | + drawText(imageData, imageWidth, imageHeight, text, startX + dx, startY + dy, outlineColor); |
| 119 | + } |
| 120 | + } |
| 121 | + } |
| 122 | + // Draw main text on top |
| 123 | + drawText(imageData, imageWidth, imageHeight, text, startX, startY, textColor); |
| 124 | + } |
| 125 | + |
| 126 | + /** |
| 127 | + * Draw text with a shadow/outline for better visibility. |
| 128 | + */ |
| 129 | + public static void drawTextWithShadow(int[] imageData, int imageWidth, int imageHeight, |
| 130 | + String text, int startX, int startY, |
| 131 | + int textColor, int shadowColor) { |
| 132 | + // Draw shadow offset by 1 pixel |
| 133 | + drawText(imageData, imageWidth, imageHeight, text, startX + 1, startY + 1, shadowColor); |
| 134 | + // Draw main text on top |
| 135 | + drawText(imageData, imageWidth, imageHeight, text, startX, startY, textColor); |
| 136 | + } |
| 137 | + |
| 138 | + /** |
| 139 | + * Draw text centered with outline. |
| 140 | + */ |
| 141 | + public static void drawTextCenteredWithOutline(int[] imageData, int imageWidth, int imageHeight, |
| 142 | + String text, int centerY, |
| 143 | + int textColor, int outlineColor) { |
| 144 | + int textWidth = getTextWidth(text); |
| 145 | + int startX = (imageWidth - textWidth) / 2; |
| 146 | + drawTextWithOutline(imageData, imageWidth, imageHeight, text, startX, centerY, textColor, outlineColor); |
| 147 | + } |
| 148 | + |
| 149 | + /** |
| 150 | + * Draw text centered with shadow. |
| 151 | + */ |
| 152 | + public static void drawTextCenteredWithShadow(int[] imageData, int imageWidth, int imageHeight, |
| 153 | + String text, int centerY, |
| 154 | + int textColor, int shadowColor) { |
| 155 | + int textWidth = getTextWidth(text); |
| 156 | + int startX = (imageWidth - textWidth) / 2; |
| 157 | + drawTextWithShadow(imageData, imageWidth, imageHeight, text, startX, centerY, textColor, shadowColor); |
| 158 | + } |
| 159 | + |
| 160 | + /** |
| 161 | + * Draw a single character. |
| 162 | + */ |
| 163 | + private static void drawChar(int[] imageData, int imageWidth, int imageHeight, |
| 164 | + char c, int x, int y, int color) { |
| 165 | + int[] glyph = (c < GLYPHS.length) ? GLYPHS[c] : null; |
| 166 | + if (glyph == null) { |
| 167 | + glyph = GLYPHS[' ']; // Default to space for unknown chars |
| 168 | + } |
| 169 | + |
| 170 | + for (int row = 0; row < CHAR_HEIGHT; row++) { |
| 171 | + int rowBits = glyph[row]; |
| 172 | + for (int col = 0; col < CHAR_WIDTH; col++) { |
| 173 | + // Check if this pixel should be drawn |
| 174 | + boolean pixelOn = (rowBits & (1 << (CHAR_WIDTH - 1 - col))) != 0; |
| 175 | + if (pixelOn) { |
| 176 | + int px = x + col; |
| 177 | + int py = y + row; |
| 178 | + if (px >= 0 && px < imageWidth && py >= 0 && py < imageHeight) { |
| 179 | + imageData[py * imageWidth + px] = color; |
| 180 | + } |
| 181 | + } |
| 182 | + } |
| 183 | + } |
| 184 | + } |
| 185 | + |
| 186 | + /** |
| 187 | + * Pack RGBA values into an int. |
| 188 | + */ |
| 189 | + public static int packColor(int r, int g, int b, int a) { |
| 190 | + return (r & 255) << 24 | (g & 255) << 16 | (b & 255) << 8 | (a & 255); |
| 191 | + } |
| 192 | + |
| 193 | + /** |
| 194 | + * Common colors for map text. |
| 195 | + */ |
| 196 | + public static final int WHITE = packColor(255, 255, 255, 255); |
| 197 | + public static final int BLACK = packColor(0, 0, 0, 255); |
| 198 | + public static final int SHADOW = packColor(30, 30, 30, 255); |
| 199 | + public static final int YELLOW = packColor(255, 255, 100, 255); |
| 200 | + public static final int GREEN = packColor(100, 255, 100, 255); |
| 201 | + public static final int RED = packColor(255, 100, 100, 255); |
| 202 | +} |
0 commit comments