使用Java實現(xiàn)簡單的墨水屏點陣圖效果
前言
點陣圖是由稱作像素(圖片元素)的單個點組成的。這些點可以進行不同的排列和染色以構(gòu)成圖樣。當放大位圖時,可以看見賴以構(gòu)成整個圖像的無數(shù)單個方塊。點陣圖的格式包括PNG、TIFF、BMP、JPEG等,通常在放大時會顯得參差不齊,但從遠處觀看時顏色和形狀又顯得是連續(xù)的
點陣圖
點陣圖分為行列式和列行式取值,如圖所示

大致的點陣圖為
優(yōu)(0) 秀(1)
{0x00,0x80,0x60,0xF8,0x07,0x10,0x10,0x10,0xFF,0x10,0xF0,0x11,0x16,0x10,0x10,0x00},
{0x01,0x00,0x00,0xFF,0x00,0x80,0x60,0x1C,0x03,0x00,0x3F,0x40,0x40,0x40,0x78,0x00},/*"優(yōu)",0*/
{0x00,0x10,0x90,0x92,0x52,0x32,0x12,0xFE,0x12,0x31,0x51,0x91,0x90,0x10,0x00,0x00},
{0x01,0x81,0x80,0x40,0x31,0x0F,0x01,0x01,0x01,0x4D,0x8B,0x48,0x38,0x01,0x01,0x00},/*"秀",1*/
一個數(shù)字分為兩個點陣,分別是16*16(寬度和高度),一個字節(jié)代表8個像素,如優(yōu)中的第一個字節(jié)0x01,

補足8位是00000001,按照0顯示暗,1顯示亮
**備注:**根據(jù)字體大小和高度這些,生成的字節(jié)不一樣
package cn.com.ut.semp.pic;
import cn.hutool.core.util.HexUtil;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
/**
* 墨水屏點陣計算工具
* 支持文字轉(zhuǎn)點陣、圖形繪制及點陣數(shù)據(jù)處理
*/
public class EInkDotMatrix {
// 點陣寬度(墨水屏橫向像素數(shù))
private final int width;
// 點陣高度(墨水屏縱向像素數(shù))
private final int height;
// 點陣數(shù)據(jù)(true表示黑色,false表示白色)
private boolean[][] matrix;
/**
* 初始化指定尺寸的點陣
* @param width 寬度(像素)
* @param height 高度(像素)
*/
public EInkDotMatrix(int width, int height) {
this.width = width;
this.height = height;
this.matrix = new boolean[height][width];
}
/**
* 將文字轉(zhuǎn)換為點陣
* @param text 要轉(zhuǎn)換的文字
* @param font 字體
* @param x 起始X坐標
* @param y 起始Y坐標(基線位置)
* @param align 對齊方式(0-左對齊,1-居中,2-右對齊)
*/
public void drawText(String text, Font font, int x, int y, int align) {
// 創(chuàng)建緩沖圖像用于繪制文字
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY);
Graphics2D g2d = image.createGraphics();
// 設(shè)置背景為白色
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, width, height);
// 設(shè)置文字顏色為黑色
g2d.setColor(Color.BLACK);
g2d.setFont(font);
// 計算文字寬度用于對齊
FontRenderContext frc = g2d.getFontRenderContext();
Rectangle2D bounds = font.getStringBounds(text, frc);
int textWidth = (int) bounds.getWidth();
int textHeight = (int) bounds.getHeight();
// 根據(jù)對齊方式調(diào)整X坐標
int drawX = x;
switch (align) {
case 1: // 居中對齊
drawX -= textWidth / 2;
break;
case 2: // 右對齊
drawX -= textWidth;
break;
default: // 左對齊
break;
}
// 繪制文字(y是基線位置,需要調(diào)整以垂直居中)
int drawY = y + (textHeight / 2) - 3; // 減去字體 descent
g2d.drawString(text, drawX, drawY);
// 提取像素數(shù)據(jù)到點陣
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
// 二進制圖像中,0表示黑色,1表示白色
int pixel = image.getRGB(col, row) & 0xFF;
matrix[row][col] = (pixel == 0);
}
}
g2d.dispose();
}
/**
* 繪制直線(Bresenham算法)
* @param x1 起點X
* @param y1 起點Y
* @param x2 終點X
* @param y2 終點Y
*/
public void drawLine(int x1, int y1, int x2, int y2) {
int dx = Math.abs(x2 - x1);
int dy = Math.abs(y2 - y1);
int sx = x1 < x2 ? 1 : -1;
int sy = y1 < y2 ? 1 : -1;
int err = dx - dy;
while (true) {
setPoint(x1, y1, true);
if (x1 == x2 && y1 == y2) break;
int e2 = 2 * err;
if (e2 > -dy) {
err -= dy;
x1 += sx;
}
if (e2 < dx) {
err += dx;
y1 += sy;
}
}
}
/**
* 繪制矩形
* @param x 左上角X
* @param y 左上角Y
* @param w 寬度
* @param h 高度
*/
public void drawRectangle(int x, int y, int w, int h) {
drawLine(x, y, x + w, y); // 上邊緣
drawLine(x + w, y, x + w, y + h); // 右邊緣
drawLine(x + w, y + h, x, y + h); // 下邊緣
drawLine(x, y + h, x, y); // 左邊緣
}
/**
* 設(shè)置單個點的狀態(tài)
* @param x X坐標
* @param y Y坐標
* @param isBlack 是否為黑色
*/
public void setPoint(int x, int y, boolean isBlack) {
if (x >= 0 && x < width && y >= 0 && y < height) {
matrix[y][x] = isBlack;
}
}
/**
* 清除所有點(全部設(shè)為白色)
*/
public void clear() {
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
matrix[i][j] = false;
}
}
}
/**
* 將點陣數(shù)據(jù)轉(zhuǎn)換為字節(jié)數(shù)組(適合硬件傳輸)
* 每字節(jié)表示8個點,高位在前
* @return 字節(jié)數(shù)組
*/
public byte[] toByteArray() {
int byteCount = (width * height + 7) / 8; // 向上取整
byte[] result = new byte[byteCount];
int index = 0;
byte currentByte = 0;
int bitPos = 7; // 從高位開始
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
if (matrix[y][x]) {
currentByte |= (1 << bitPos);
}
bitPos--;
// 填滿一個字節(jié)
if (bitPos < 0) {
result[index++] = currentByte;
currentByte = 0;
bitPos = 7;
}
}
}
// 處理最后一個不完整的字節(jié)
if (bitPos < 7) {
result[index] = currentByte;
}
return result;
}
/**
* 在控制臺打印點陣(調(diào)試用)
*/
public void printMatrix() {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
System.out.print(matrix[y][x] ? "■ " : "□ ");
}
System.out.println();
}
}
// Getters
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public boolean[][] getMatrix() {
return matrix;
}
}
測試例子
package cn.com.ut.semp.pic;
import cn.hutool.core.util.HexUtil;
import java.io.IOException;
public class EInkExample {
public static void main(String[] args) throws IOException {
String str = "優(yōu)秀";
String[] data = str.split("\n");
for (int a = 0; a < data.length; a++) {
// 單行文本轉(zhuǎn)換
byte[][] dotMatrix = EInkDisplaySDK.textToDotMatrix(
data[a],
16,
EInkDisplaySDK.FontStyle.PLAIN,
128,
16
);
for (int i = 0; i < dotMatrix.length; i++) {
for (int j = 0; j < dotMatrix[i].length; j++) {
System.out.print("0x" + HexUtil.toHex(dotMatrix[i][j] & 0xFF) + ",");
}
System.out.println();
}
}
}
}
這個是列行式
package cn.com.ut.semp.hello;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
/**
* 模擬PCtoLCD2002列行式取模功能
*/
public class PCToLCD2002Emulator {
// 取模方向常量
public static final int MODE_COLUMN_MAJOR = 0; // 列行式
public static final int MODE_ROW_MAJOR = 1; // 行列式
// 字體設(shè)置
private Font font;
private boolean antiAliasing = false;
private int mode = MODE_COLUMN_MAJOR;
private boolean reverseByte = false; // 字節(jié)內(nèi)位順序是否反轉(zhuǎn)
private boolean reverseOrder = false; // 整體數(shù)據(jù)順序是否反轉(zhuǎn)
public PCToLCD2002Emulator() {
this("宋體", 16, Font.PLAIN);
}
public PCToLCD2002Emulator(String fontName, int fontSize, int fontStyle) {
this.font = new Font(fontName, fontStyle, fontSize);
}
/**
* 設(shè)置取模方向
*
* @param mode MODE_COLUMN_MAJOR 或 MODE_ROW_MAJOR
*/
public void setMode(int mode) {
this.mode = mode;
}
/**
* 設(shè)置是否反轉(zhuǎn)字節(jié)內(nèi)位順序
*/
public void setReverseByte(boolean reverseByte) {
this.reverseByte = reverseByte;
}
/**
* 設(shè)置是否反轉(zhuǎn)整體數(shù)據(jù)順序
*/
public void setReverseOrder(boolean reverseOrder) {
this.reverseOrder = reverseOrder;
}
/**
* 設(shè)置是否啟用抗鋸齒
*/
public void setAntiAliasing(boolean antiAliasing) {
this.antiAliasing = antiAliasing;
}
/**
* 生成字模數(shù)據(jù)
*
* @param text 要取模的文本
* @return 字模數(shù)據(jù)字節(jié)數(shù)組
*/
public byte[] generateFontData(String text) {
Dimension size = calculateTextSize(text);
BufferedImage image = createTextImage(text, 16, 16);
try {
ImageIO.write(image, "png", new File("d:/hello/text.png"));
} catch (IOException e) {
throw new RuntimeException(e);
}
return mode == MODE_COLUMN_MAJOR ?
extractColumnMajorData(image, 16, 16) :
extractRowMajorData(image, size.width, size.height);
}
/**
* 生成字模數(shù)據(jù)并格式化為C語言數(shù)組格式
*/
public String generateFontDataAsCArray(String text, String arrayName) {
byte[] data = generateFontData(text);
Dimension size = calculateTextSize(text);
StringBuilder sb = new StringBuilder();
sb.append("http:// ").append(text).append(" 字模數(shù)據(jù)\n");
sb.append("http:// 寬度: ").append(size.width).append(" 像素, 高度: ").append(size.height).append(" 像素\n");
sb.append("const unsigned char ").append(arrayName).append("[] = {");
for (int i = 0; i < data.length; i++) {
if (i % 16 == 0) {
sb.append("\n ");
}
sb.append(String.format("0x%02X", data[i] & 0xFF));
if (i < data.length - 1) {
sb.append(", ");
}
}
sb.append("\n};\n");
return sb.toString();
}
/**
* 計算文本尺寸
*/
private Dimension calculateTextSize(String text) {
BufferedImage dummyImage = new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_BINARY);
Graphics2D g2d = dummyImage.createGraphics();
setupRenderingHints(g2d);
FontMetrics metrics = g2d.getFontMetrics(font);
int width = metrics.stringWidth(text);
int height = metrics.getHeight();
g2d.dispose();
return new Dimension(width, height);
}
/**
* 創(chuàng)建文本圖像
*/
private BufferedImage createTextImage(String text, int width, int height) {
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY);
Graphics2D g2d = image.createGraphics();
setupRenderingHints(g2d);
// 白色背景
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, width, height);
// 黑色文本
g2d.setColor(Color.BLACK);
g2d.setFont(font);
FontMetrics metrics = g2d.getFontMetrics();
int y = metrics.getAscent();
g2d.drawString(text, 0, y);
g2d.dispose();
return image;
}
/**
* 設(shè)置渲染參數(shù)
*/
private void setupRenderingHints(Graphics2D g2d) {
if (antiAliasing) {
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
} else {
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
}
}
/**
* 提取列主序點陣數(shù)據(jù)(PCtoLCD2002列行式)
*/
private byte[] extractColumnMajorData(BufferedImage image, int width, int height) {
// 計算需要的字節(jié)數(shù) (width * ceil(height/8))
int bytesPerColumn = (int) Math.ceil(height / 8.0);
int totalBytes = width * bytesPerColumn;
ByteBuffer buffer = ByteBuffer.allocate(totalBytes);
// 是否反轉(zhuǎn)列順序
int columnStart = reverseOrder ? width - 1 : 0;
int columnStep = reverseOrder ? -1 : 1;
for (int x = columnStart; reverseOrder ? x >= 0 : x < width; x += columnStep) {
for (int byteIdx = 0; byteIdx < bytesPerColumn; byteIdx++) {
byte columnByte = 0;
for (int bit = 0; bit < 8; bit++) {
int pixelY = byteIdx * 8 + bit;
if (pixelY < height) {
int rgb = image.getRGB(x, pixelY);
// 黑色像素設(shè)置對應(yīng)位
if (rgb == Color.BLACK.getRGB()) {
int actualBit = reverseByte ? (7 - bit) : bit;
columnByte |= (1 << actualBit);
}
}
}
buffer.put(columnByte);
}
}
return buffer.array();
}
/**
* 提取行主序點陣數(shù)據(jù)(PCtoLCD2002行列式)
*/
private byte[] extractRowMajorData(BufferedImage image, int width, int height) {
// 計算每行需要的字節(jié)數(shù) (ceil(width/8))
int bytesPerRow = (int) Math.ceil(width / 8.0);
int totalBytes = bytesPerRow * height;
ByteBuffer buffer = ByteBuffer.allocate(totalBytes);
// 是否反轉(zhuǎn)行順序
int rowStart = reverseOrder ? height - 1 : 0;
int rowStep = reverseOrder ? -1 : 1;
for (int y = rowStart; reverseOrder ? y >= 0 : y < height; y += rowStep) {
for (int byteIdx = 0; byteIdx < bytesPerRow; byteIdx++) {
byte rowByte = 0;
for (int bit = 0; bit < 8; bit++) {
int pixelX = byteIdx * 8 + bit;
if (pixelX < width) {
int rgb = image.getRGB(pixelX, y);
// 黑色像素設(shè)置對應(yīng)位
if (rgb == Color.BLACK.getRGB()) {
int actualBit = reverseByte ? (7 - bit) : bit;
rowByte |= (1 << actualBit);
}
}
}
buffer.put(rowByte);
}
}
return buffer.array();
}
/**
* 生成字模數(shù)據(jù)并格式化為十六進制字符串
*/
public String generateHexString(String text) {
byte[] data = generateFontData(text);
StringBuilder sb = new StringBuilder();
for (byte b : data) {
sb.append(String.format("%02X ", b & 0xFF));
}
return sb.toString().trim();
}
/**
* 生成字模數(shù)據(jù)并格式化為二進制字符串
*/
public String generateBinaryString(String text) {
byte[] data = generateFontData(text);
StringBuilder sb = new StringBuilder();
for (byte b : data) {
String binary = String.format("%8s", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');
if (reverseByte) {
binary = new StringBuilder(binary).reverse().toString();
}
sb.append(binary).append(" ");
}
return sb.toString().trim();
}
}
package cn.com.ut.semp.hello;
import cn.hutool.core.util.HexUtil;
import java.awt.*;
import java.util.Arrays;
public class PCToLCD2002Example {
public static void main(String[] args) {
// 創(chuàng)建取模器實例(模擬PCtoLCD2002)
PCToLCD2002Emulator emulator = new PCToLCD2002Emulator("宋體", 16, Font.PLAIN);
// 配置取模參數(shù)(與PCtoLCD2002設(shè)置一致)
emulator.setMode(PCToLCD2002Emulator.MODE_COLUMN_MAJOR); // 列行式
emulator.setReverseByte(false); // 字節(jié)內(nèi)位反轉(zhuǎn)
emulator.setReverseOrder(false); // 不反轉(zhuǎn)整體順序
emulator.setAntiAliasing(false); // 禁用抗鋸齒
// 取模示例
String text = "優(yōu)";
//
// // 生成字模數(shù)據(jù)
byte[] fontData = emulator.generateFontData(text);
System.out.println(Arrays.toString(fontData));
for (int i = 0; i < fontData.length; i++) {
System.out.print("0x" + HexUtil.toHex(fontData[i] & 0xFF) + ",");
if ((i + 1) % 16 == 0) {
System.out.println();
}
}
System.out.println("字模數(shù)據(jù)字節(jié)數(shù): " + fontData.length);
}
}
總結(jié)
可以使用PCToLCD工具熟悉點陣圖
到此這篇關(guān)于使用Java實現(xiàn)簡單的墨水屏點陣圖效果的文章就介紹到這了,更多相關(guān)Java墨水屏點陣圖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java實現(xiàn)ftp文件上傳下載解決慢中文亂碼多個文件下載等問題
這篇文章主要介紹了Java實現(xiàn)ftp文件上傳下載解決慢中文亂碼多個文件下載等問題的相關(guān)資料,非常不錯具有參考借鑒價值,需要的朋友可以參考下2016-10-10
Spring AOP面向切面編程實現(xiàn)原理方法詳解
這篇文章主要介紹了Spring AOP面向切面編程實現(xiàn)原理方法詳解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-08-08
spring-boot @Component和@Bean的區(qū)別詳解
這篇文章主要介紹了spring-boot @Component和@Bean的區(qū)別詳解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-07-07
基于SpringBoot使用Tika實現(xiàn)文檔解析
Apache?Tika是開源內(nèi)容分析工具,支持多格式文本提取與元數(shù)據(jù)解析,具備語言檢測和MIME類型識別功能,適用于搜索引擎、數(shù)據(jù)分析等場景,在SpringBoot中集成需注意性能及配置問題,支持流式處理和自定義擴展,下面介紹SpringBoot使用Tika實現(xiàn)文檔解析,感興趣的朋友一起看看吧2025-07-07

