Files
Any2MIF/core/converter.py
2025-03-07 15:32:58 +08:00

256 lines
9.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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