mirror of
https://github.com/HChaZZY/Any2MIF.git
synced 2025-12-06 10:33:49 +08:00
256 lines
9.5 KiB
Python
256 lines
9.5 KiB
Python
#!/usr/bin/env python
|
||
# -*- coding: utf-8 -*-
|
||
# Copyright (c) 2025 Any2MIF Project
|
||
# All rights reserved.
|
||
|
||
"""
|
||
Any2MIF - MIF格式转换器
|
||
负责将任意文件转换为MIF格式
|
||
"""
|
||
|
||
import os
|
||
import math
|
||
from datetime import datetime
|
||
|
||
class MifConverter:
|
||
"""MIF格式转换器"""
|
||
|
||
def __init__(self):
|
||
"""初始化转换器"""
|
||
pass
|
||
|
||
def convert_from_image(self, image, output_file, params):
|
||
"""
|
||
将PIL图像对象直接转换为MIF格式
|
||
|
||
参数:
|
||
image (PIL.Image): PIL图像对象
|
||
output_file (str): 输出文件路径
|
||
params (dict): 转换参数
|
||
width (int): 数据宽度 (8/16/32)
|
||
auto_depth (bool): 是否自动计算深度
|
||
depth (int): 深度 (如果auto_depth为False)
|
||
radix (str): 数据格式 (HEX/BIN/DEC)
|
||
offset (int): 地址偏移量
|
||
header (bool): 是否添加文件头注释
|
||
addr_format (str): 地址格式 (standard/compact)
|
||
data_per_line (int): 每行数据数量
|
||
|
||
返回:
|
||
bool: 转换是否成功
|
||
"""
|
||
try:
|
||
# 将图像转换为字节数据
|
||
if image.mode != 'L':
|
||
image = image.convert('L') # 转换为灰度图像
|
||
|
||
# 将图像转换为字节数组
|
||
data = bytearray()
|
||
for y in range(image.height):
|
||
for x in range(image.width):
|
||
pixel = image.getpixel((x, y))
|
||
data.append(pixel)
|
||
|
||
# 计算深度 - 使用图像的当前尺寸(可能是标准化后的尺寸)
|
||
if params['auto_depth']:
|
||
# 如果启用了自动计算深度,根据图像的当前尺寸和数据宽度计算深度
|
||
bytes_per_word = params['width'] // 8
|
||
# 使用图像的实际尺寸计算深度
|
||
depth = math.ceil((image.width * image.height) / bytes_per_word)
|
||
else:
|
||
depth = params['depth']
|
||
|
||
# 打开输出文件
|
||
with open(output_file, 'w', encoding='utf-8') as f:
|
||
# 根据参数决定是否写入文件头
|
||
if params.get('header', True):
|
||
self._write_header(f, "内存中图像", params, depth)
|
||
# 写入数据
|
||
self._write_data(f, data, params, depth)
|
||
|
||
return True
|
||
except Exception as e:
|
||
print(f"转换错误: {str(e)}")
|
||
return False
|
||
|
||
def convert(self, input_file, output_file, params):
|
||
"""
|
||
将输入文件转换为MIF格式
|
||
|
||
参数:
|
||
input_file (str): 输入文件路径
|
||
output_file (str): 输出文件路径
|
||
params (dict): 转换参数
|
||
width (int): 数据宽度 (8/16/32)
|
||
auto_depth (bool): 是否自动计算深度
|
||
depth (int): 深度 (如果auto_depth为False)
|
||
radix (str): 数据格式 (HEX/BIN/DEC)
|
||
offset (int): 地址偏移量
|
||
header (bool): 是否添加文件头注释
|
||
addr_format (str): 地址格式 (standard/compact)
|
||
data_per_line (int): 每行数据数量
|
||
|
||
返回:
|
||
bool: 转换是否成功
|
||
"""
|
||
try:
|
||
# 读取输入文件
|
||
with open(input_file, 'rb') as f:
|
||
data = f.read()
|
||
|
||
# 计算深度
|
||
if params['auto_depth']:
|
||
bytes_per_word = params['width'] // 8
|
||
depth = math.ceil(len(data) / bytes_per_word)
|
||
else:
|
||
depth = params['depth']
|
||
|
||
# 打开输出文件
|
||
with open(output_file, 'w', encoding='utf-8') as f:
|
||
# 根据参数决定是否写入文件头
|
||
if params.get('header', True):
|
||
self._write_header(f, input_file, params, depth)
|
||
# 写入数据
|
||
self._write_data(f, data, params, depth)
|
||
|
||
return True
|
||
except Exception as e:
|
||
print(f"转换错误: {str(e)}")
|
||
return False
|
||
|
||
def _write_header(self, file, input_file, params, depth):
|
||
"""
|
||
写入MIF文件头
|
||
|
||
参数:
|
||
file: 输出文件对象
|
||
input_file: 输入文件路径
|
||
params: 转换参数
|
||
depth: 深度
|
||
"""
|
||
file.write("WIDTH=%d;\n" % params['width'])
|
||
file.write("DEPTH=%d;\n" % depth)
|
||
file.write("\n")
|
||
file.write("ADDRESS_RADIX=%s;\n" % params.get('addr_radix', 'HEX'))
|
||
file.write("DATA_RADIX=%s;\n" % params['radix'])
|
||
file.write("\n")
|
||
file.write("CONTENT BEGIN\n")
|
||
|
||
def _write_data(self, file, data, params, depth):
|
||
"""写入MIF数据部分"""
|
||
bytes_per_word = params['width'] // 8
|
||
words_per_line = params['data_per_line']
|
||
|
||
# 处理每个地址
|
||
for addr in range(depth):
|
||
# 计算实际地址(加上偏移量)
|
||
actual_addr = addr + params['offset']
|
||
|
||
# 计算数据索引
|
||
start_idx = addr * bytes_per_word
|
||
end_idx = start_idx + bytes_per_word
|
||
|
||
# 如果超出数据范围,填充0
|
||
if start_idx >= len(data):
|
||
word_data = 0
|
||
else:
|
||
# 读取数据
|
||
word_bytes = data[start_idx:min(end_idx, len(data))]
|
||
|
||
# 如果数据不足,填充0
|
||
if len(word_bytes) < bytes_per_word:
|
||
word_bytes = word_bytes + b'\x00' * (bytes_per_word - len(word_bytes))
|
||
|
||
# 转换为整数
|
||
word_data = int.from_bytes(word_bytes, byteorder='little')
|
||
|
||
# 格式化地址
|
||
addr_radix = params.get('addr_radix', 'HEX')
|
||
|
||
if addr_radix == 'HEX':
|
||
addr_format = "@%X" if params['addr_format'] == 'standard' else "%X:"
|
||
addr_str = addr_format % actual_addr
|
||
elif addr_radix == 'BIN':
|
||
# 计算二进制位数(根据深度和偏移量)
|
||
max_addr = depth + params['offset'] - 1
|
||
bin_digits = len(bin(max_addr)[2:])
|
||
if params['addr_format'] == 'standard':
|
||
addr_str = "@" + bin(actual_addr)[2:].zfill(bin_digits)
|
||
else: # compact
|
||
addr_str = bin(actual_addr)[2:].zfill(bin_digits) + ":"
|
||
else: # DEC
|
||
if params['addr_format'] == 'standard':
|
||
addr_str = "@%d" % actual_addr
|
||
else: # compact
|
||
addr_str = "%d:" % actual_addr
|
||
|
||
# 格式化数据
|
||
if params['radix'] == 'HEX':
|
||
# 计算十六进制位数(每4位二进制对应1位十六进制)
|
||
hex_digits = math.ceil(params['width'] / 4)
|
||
data_str = format(word_data, 'X').zfill(hex_digits)
|
||
elif params['radix'] == 'BIN':
|
||
data_str = bin(word_data)[2:].zfill(params['width'])
|
||
else: # DEC
|
||
# 对于十进制,根据位宽计算最大可能的位数
|
||
max_dec_digits = len(str(2**params['width'] - 1))
|
||
data_str = str(word_data).zfill(max_dec_digits)
|
||
|
||
# 写入一行
|
||
if (addr + 1) % words_per_line == 0 or addr == depth - 1:
|
||
file.write(f"{addr_str} {data_str};\n")
|
||
else:
|
||
file.write(f"{addr_str} {data_str},\n")
|
||
|
||
# 写入文件尾
|
||
file.write("END;\n")
|
||
|
||
def estimate_output_size(self, input_file, params):
|
||
"""
|
||
估计输出文件大小
|
||
|
||
参数:
|
||
input_file (str): 输入文件路径
|
||
params (dict): 转换参数
|
||
|
||
返回:
|
||
int: 估计的输出文件大小(字节)
|
||
"""
|
||
try:
|
||
# 获取输入文件大小
|
||
input_size = os.path.getsize(input_file)
|
||
|
||
# 计算深度
|
||
bytes_per_word = params['width'] // 8
|
||
if params['auto_depth']:
|
||
depth = math.ceil(input_size / bytes_per_word)
|
||
else:
|
||
depth = params['depth']
|
||
|
||
# 估计每行平均长度
|
||
if params['radix'] == 'HEX':
|
||
avg_data_len = params['width'] // 4
|
||
elif params['radix'] == 'BIN':
|
||
avg_data_len = params['width']
|
||
else: # DEC
|
||
avg_data_len = len(str(2 ** params['width'] - 1))
|
||
|
||
# 估计地址长度
|
||
addr_radix = params.get('addr_radix', 'HEX')
|
||
if addr_radix == 'HEX':
|
||
addr_len = len(hex(depth + params['offset'])[2:]) + 2
|
||
elif addr_radix == 'BIN':
|
||
addr_len = len(bin(depth + params['offset'])[2:]) + 2
|
||
else: # DEC
|
||
addr_len = len(str(depth + params['offset'])) + 2
|
||
|
||
# 估计每行长度
|
||
line_len = addr_len + avg_data_len + 3 # 包括空格、分隔符和换行符
|
||
|
||
# 估计总大小(不包含文件头)
|
||
total_size = depth * line_len
|
||
|
||
return total_size
|
||
except Exception:
|
||
# 如果估计失败,返回一个保守的估计值
|
||
return input_size * 5 |