用汉字库16X16实现输入文字和图片生成,像素文字图片。java,html

用汉字库16X16实现输入文字和图片生成,像素文字图片。
https://download.csdn.net/download/qq_33729058/89661391
hzk16.dat 下载地址
Java代码

package com.yk.javalearn.day1;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

/**
 * 该类用于从 HZK16 字库文件中提取汉字的点阵数据。
 * 通过 GB2312 编码找到汉字在字库中的位置,并提取该汉字的 16x16 点阵图。
 * 
 * @Author: 61
 */
public class HZK16 {

    /**
     * 通过内容(汉字)获取其对应的点阵图,点阵图以 List<List<Integer>> 的形式返回。
     * 每个汉字的点阵图大小为 16x16 像素。
     * 
     * @param content 汉字内容
     * @return 汉字的点阵图
     */
    public static List<List<Integer>> getBitMapByContent(String content) {
        try {
            // 获取 HZK16 字库文件的数据
            byte[] hzk16Data = loadHZK16Data();
            // 将输入的汉字转换为 GB2312 编码格式
            byte[] gb2312Bytes = content.getBytes(Charset.forName("GB2312"));
            // 计算该汉字在字库中的索引
            int charIndex = getCharIndex(gb2312Bytes);
            // 根据索引从字库中获取对应汉字的点阵数据
            byte[] charData = getCharData(hzk16Data, charIndex);
            // 将点阵数据转化为 List<List<Integer>> 形式并返回
            return convertArrayToList(extractBitmap(charData));
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 如果发生异常,返回空的点阵列表
        return new ArrayList<>();
    }

    /**
     * 从类路径中读取 HZK16 字库文件,并返回其字节数组。
     * 
     * @return HZK16 字库文件的字节数据
     * @throws IOException 如果文件不存在或读取错误
     */
    private static byte[] loadHZK16Data() throws IOException {
        // 获取 HZK16 字库文件的输入流
        try (InputStream inputStream = ClassLoader.getSystemResourceAsStream("hzk/HZK16.dat")) {
            if (inputStream == null) {
                // 如果文件未找到,抛出异常
                throw new IOException("Resource not found: " + "hzk/HZK16.dat");
            }
            // 使用 ByteArrayOutputStream 来存储读取的数据
            ByteArrayOutputStream buffer = new ByteArrayOutputStream();
            byte[] data = new byte[1024];  // 每次读取 1KB 的数据
            int bytesRead;
            // 循环读取数据,直到文件末尾
            while ((bytesRead = inputStream.read(data, 0, data.length)) != -1) {
                buffer.write(data, 0, bytesRead);
            }
            // 返回文件的字节数组
            return buffer.toByteArray();
        }
    }

    /**
     * 根据 GB2312 编码计算出汉字在字库中的索引位置。
     * 
     * @param gb2312Bytes GB2312 编码格式的字节数组
     * @return 汉字在字库中的索引
     */
    private static int getCharIndex(byte[] gb2312Bytes) {
        if (gb2312Bytes.length != 2) {
            // GB2312 编码的汉字应该由 2 个字节组成,如果不是则抛出异常
            throw new IllegalArgumentException("GB2312 encoding should have exactly 2 bytes.");
        }
        // 获取高位字节和低位字节
        int highByte = gb2312Bytes[0] & 0xFF; // 高位字节
        int lowByte = gb2312Bytes[1] & 0xFF;  // 低位字节
        // 计算汉字在字库中的索引位置
        int index = (highByte - 0xA1) * 94 + (lowByte - 0xA1); // 根据 GB2312 编码计算索引
        return index;
    }

    // 每个汉字的点阵图大小为 16x16 像素,因此需要 32 个字节来存储。
    private static final int CHAR_SIZE = 32; // 16x16 bits = 32 bytes

    /**
     * 根据索引从字库数据中提取特定汉字的点阵数据。
     * 
     * @param data 字库的字节数据
     * @param index 汉字在字库中的索引
     * @return 汉字的点阵字节数组
     */
    private static byte[] getCharData(byte[] data, int index) {
        // 计算汉字在字库中的起始位置
        int start = index * CHAR_SIZE;
        int end = start + CHAR_SIZE;
        // 创建字节数组来存储该汉字的点阵数据
        byte[] charData = new byte[CHAR_SIZE];
        // 从字库数据中复制对应的点阵数据到 charData 数组
        System.arraycopy(data, start, charData, 0, CHAR_SIZE);
        return charData;
    }

    /**
     * 将汉字的点阵字节数据转换为二维数组,表示为 16x16 的点阵图。
     * 
     * @param charData 汉字的点阵字节数组
     * @return 16x16 的点阵图二维数组
     */
    private static int[][] extractBitmap(byte[] charData) {
        // 创建 16x16 的二维数组来表示点阵图
        int[][] matrix = new int[16][16];

        // 遍历 16x16 的每一个像素
        for (int i = 0; i < 16; i++) {
            for (int j = 0; j < 16; j++) {
                // 计算当前像素所在的字节索引和位索引
                int byteIndex = i * 2 + j / 8;
                int bitIndex = j % 8;
                // 检查该像素是否为 1(使用位操作来提取具体的位)
                if ((charData[byteIndex] & (0x80 >> bitIndex)) != 0) {
                    matrix[i][j] = 1; // 点亮的像素用 1 表示
                }
            }
        }
        return matrix;
    }

    /**
     * 将 16x16 的二维数组转换为 List<List<Integer>> 格式,以方便后续操作。
     * 
     * @param array 16x16 的点阵图二维数组
     * @return 转换后的 List<List<Integer>> 格式
     */
    private static List<List<Integer>> convertArrayToList(int[][] array) {
        // 创建 List<List<Integer>> 来存储结果
        List<List<Integer>> list = new ArrayList<>();

        // 遍历二维数组,并将每一行转换为 List<Integer>
        for (int[] row : array) {
            List<Integer> rowList = new ArrayList<>();
            for (int element : row) {
                rowList.add(element); // 将每一个像素添加到行列表中
            }
            // 将行列表添加到最终列表中
            list.add(rowList);
        }

        return list;
    }

    /**
     * 将 List<List<Integer>> 格式的点阵图转换为字符串,并在每一行后添加换行符。
     * 
     * @param list 点阵图的 List<List<Integer>> 格式
     * @return 点阵图的字符串表示
     */
    public static String listToString(List<List<Integer>> list) {
        // 使用 StringBuilder 来拼接字符串
        StringBuilder sb = new StringBuilder();

        // 遍历 List<List<Integer>> 的每一行
        for (List<Integer> row : list) {
            sb.append(row.toString()); // 将行转换为字符串并添加到 StringBuilder 中
            sb.append(System.lineSeparator()); // 添加换行符
        }

        // 返回最终的字符串
        return sb.toString();
    }

}

代码详细说明:

  • getBitMapByContent:获取指定汉字的点阵数据,并将其转换为 List<List> 形式返回。
  • loadHZK16Data:从类路径中加载 HZK16 字库文件,将其读取为字节数组。
  • getCharIndex:根据汉字的 GB2312 编码计算该汉字在字库中的索引。
  • getCharData:根据索引从字库中提取汉字的点阵数据。
  • extractBitmap:将汉字的点阵字节数据转化为 16x16 的二维数组,表示汉字的点阵图。
  • convertArrayToList:将二维数组转换为 List<List> 形式。
  • listToString:将 List<List> 转换为带有换行符的字符串形式,方便打印和显示。

html代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Image Concatenation with AJAX</title>
    <style>
        /* 样式可以根据实际需求调整 */
        img {
            max-width: 100%;
            max-height: 100px;
            margin-top: 10px;
        }

        .container {
            display: grid;
            grid-template-columns: 1fr 1fr; /* 两列,每列占据相同的宽度 */
            gap: 10px; /* 列之间的间距 */
        }

        .box {
            padding: 20px;
            box-sizing: border-box;
        }

        .box1 {
            background-color: #4cd508;
        }

        .box2 {
            background-color: #33a7cc;
        }
    </style>
</head>
<body>
<div class="container">
    <!-- 文件上传输入框 -->
    <div class="box box1">
        <input id="fileInput" type="file" multiple/>
    </div>
    <!-- 中文字符输入框 -->
    <div class="box box2">
        <input id="textInput" type="text" value="" maxlength="1" placeholder="输入一个中文字符"/>
        <p id="errorMessage" style="color: red; display: none;">请输入一个中文字符</p>
        <button id="concatenateBtn">拼接图片</button>
    </div>

    <!-- 图片预览和下载按钮 -->
    <div>
        <img id="concatenatedImage" alt="Concatenated Image" style="display: none;"/>
        <a id="downloadBtn" style="display: none;">下载图片</a>
    </div>
    <div id="previewImages"></div> <!-- 图片预览区域 -->
</div>

<script>
    const selectedFiles = [];  // 存储用户选择的图片文件
    let points = [];  // 存储字库点阵图的坐标信息

    document.getElementById('fileInput').addEventListener('change', handleFileChange); // 监听文件输入框的改变事件
    document.getElementById('concatenateBtn').addEventListener('click', concatenateImages); // 监听拼接按钮点击事件

    // 发送 AJAX 请求从服务器获取汉字点阵图数据
    function postData(key) {
        return new Promise((resolve, reject) => {
            const url = `/api/lattice/bitmap?content=${encodeURIComponent(key)}`;
            const xhr = new XMLHttpRequest();
            xhr.open('GET', url, true);
            xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
            xhr.onreadystatechange = function () {
                if (xhr.readyState === XMLHttpRequest.DONE) {
                    if (xhr.status === 200) {
                        points = JSON.parse(xhr.responseText); // 获取汉字点阵图数据
                        resolve(points); // 返回点阵图数据
                    } else {
                        reject(xhr.statusText); // 请求失败时返回错误
                    }
                }
            };
            xhr.send(); // 发送请求
        });
    }

    // 处理文件输入的事件
    function handleFileChange(event) {
        selectedFiles.length = 0; // 清空之前选择的文件
        const files = Array.from(event.target.files); // 将文件列表转换为数组
        getPointMap().then(() => {
            // 调整图片尺寸并显示预览
            Promise.all(files.map(resizeImage)).then(resizedFiles => {
                selectedFiles.push(...resizedFiles); // 将调整后的文件保存到 selectedFiles
                previewImages(); // 显示图片预览
            });
        });
    }

    // 将图片调整为 100x100 像素大小
    function resizeImage(file) {
        return new Promise((resolve, reject) => {
            const img = new Image();
            img.onload = () => {
                const canvas = document.createElement('canvas');
                const ctx = canvas.getContext('2d');
                canvas.width = 100;
                canvas.height = 100;
                ctx.drawImage(img, 0, 0, 100, 100); // 绘制图片到 100x100 的 canvas 上
                canvas.toBlob(blob => {
                    resolve(new File([blob], file.name, {type: file.type})); // 将 canvas 转换为 Blob 对象并返回新文件
                }, file.type);
            };
            img.onerror = reject;
            img.src = URL.createObjectURL(file); // 加载用户上传的图片
        });
    }

    // 显示图片预览
    function previewImages() {
        const previewContainer = document.getElementById('previewImages');
        previewContainer.innerHTML = ''; // 清空之前的预览
        selectedFiles.forEach(file => {
            const reader = new FileReader();
            reader.onload = e => {
                const img = document.createElement('img');
                img.src = e.target.result; // 设置图片预览的源
                previewContainer.appendChild(img); // 将图片添加到预览区域
            };
            reader.readAsDataURL(file); // 读取文件数据并生成预览
        });
    }

    // 拼接图片并生成最终图像
    function concatenateImages() {
        if (selectedFiles.length === 0) {
            alert('请先上传图片'); // 如果没有上传图片,显示提示
            return;
        }
        const canvas = document.createElement('canvas');
        const ctx = canvas.getContext('2d');
        canvas.width = 1600; // 设置 canvas 的宽度为 1600 像素
        canvas.height = 1600; // 设置 canvas 的高度为 1600 像素

        // 加载所有选中的图片
        const loadImages = selectedFiles.map(file => {
            return new Promise((resolve, reject) => {
                const img = new Image();
                img.src = URL.createObjectURL(file); // 为每个文件生成 URL
                img.onload = () => {
                    resolve(img); // 当图片加载完成时,返回图片对象
                };
                img.onerror = reject;
            });
        });

        Promise.all(loadImages).then(images => {
            const imgLen = images.length - 1;
            let countImg = 0; // 用于循环图片
            return getPointMap().then(pMaps => {
                // 根据字库中的点阵数据将图片绘制到 canvas 上
                for (let i = 0; i < pMaps.length; i++) {
                    ctx.drawImage(images[countImg], pMaps[i][1], pMaps[i][0], 100, 100); // 按照点阵图的位置绘制图片
                    countImg = (countImg === imgLen) ? 0 : countImg + 1; // 轮流使用图片
                }
                // 将 canvas 生成的图片设置为 img 元素的 src
                const concatenatedImageElement = document.getElementById('concatenatedImage');
                const downloadBtn = document.getElementById('downloadBtn');
                const imageUrl = canvas.toDataURL('image/png'); // 生成最终拼接图片的 URL

                concatenatedImageElement.src = imageUrl;
                concatenatedImageElement.style.display = 'block'; // 显示拼接后的图片

                downloadBtn.href = imageUrl;
                downloadBtn.download = 'concatenated-image.png'; // 设置下载按钮的文件名
                downloadBtn.style.display = 'inline'; // 显示下载按钮
                // 清空预览图片
                document.getElementById('previewImages').innerHTML = '';

                // 清空已选择的文件
                selectedFiles.length = 0;
            });
        }).catch(error => {
            console.error('Failed to load images:', error); // 处理加载图片时的错误
        });
    }

    // 获取汉字点阵图的数据
    function getPointMap() {
        const key = document.getElementById('textInput').value; // 获取输入的汉字
        return postData(key).then(() => {
            const pLen = 100; // 每个点的大小为 100 像素
            const result = [];
            for (let i = 0; i < points.length; i++) {
                for (let j = 0; j < points[i].length; j++) {
                    if (points[i][j] === 1) {
                        result.push([i * pLen, j * pLen]); // 根据点阵图生成拼接图片的位置
                    }
                }
            }
            return result; // 返回图片绘制的坐标
        });
    }

    // 监听文本输入框的输入事件,确保输入为一个中文字符
    document.getElementById('textInput').addEventListener('input', function () {
        const input = this.value;
        const chineseCharRegex = /^[\u4e00-\u9fa5]$/; // 正则表达式匹配一个中文字符

        if (!chineseCharRegex.test(input)) {
            document.getElementById('errorMessage').style.display = 'block'; // 如果输入不是中文字符,显示错误提示
        } else {
            document.getElementById('errorMessage').style.display = 'none'; // 输入合法时隐藏错误提示
        }
    });
</script>
</body>
</html>

代码解释:

  1. 文件上传与图片处理:

    • 用户可以通过文件输入框上传多个图片。
    • 上传的图片会被缩放到 100x100 像素大小,并且会显示预览。
    • 图片会按上传的顺序排列和显示在网页中。
  2. 中文字符输入:

    • 用户可以在文本框中输入一个中文字符(例如汉字)。
    • 输入的字符将被用于请求服务器生成一个字符的点阵图,这个点阵图会用于拼接图片的布局。
  3. 图片拼接:

    • 当用户点击“拼接图片”按钮时,网页会根据生成的点阵图,将上传的图片按照点阵图中的位置拼接在一起。
    • 拼接完成后,生成的图像会显示在页面中,并提供一个下载链接。
  4. 表单验证与错误处理:

    • 如果用户输入的不是合法的中文字符,会显示错误提示信息。
    • 如果用户没有上传图片,会显示相应的提示。

在这里插入图片描述
html 、字库文件位置。 spring实现一个接口入口。
演示如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这只是一个实现思路,可优化地方很多。如果大家喜欢可以给我提需要什么功能我可以增加,如果有需要的我可以共享这个代码库做成一件安装使用的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值