之前使用ESP32 来控制TFT屏幕显示动图时,找到现有的工具,需要先将动图gif格式一帧帧转为jpg格式,再将一帧帧的jgp转为hex十六进制格式,整个过程好麻烦。现用python写了脚本,将gif动图,一步生成hex16进制格式.h文件,可以直接使用,下面介绍脚本,和基于arduino框架使用TFT_eSPI库动图显示功能。
1. gif动图转十六进制文件(一步到位)在函数修改动图路径,运行即可
- python脚本
from PIL import Image
import sys
import os
from io import BytesIO
import binascii
import traceback
'''
https://stackoverflow.com/questions/10269099/pil-convert-gif-frames-to-jpg
'''
curdir = "./"
os.chdir(curdir)
def processImage(in_file, saveImg=True):
try:
im = Image.open(in_file)
except IOError:
print("Cant load", in_file)
sys.exit(1)
# 截取文件名
filename = in_file.split('.')[0]
i = 0
mypalette = im.getpalette()
arr_name_all = '' # 存取数组
arr_size_all = '' # 存储数组容量
try:
with open(filename + '.h', 'w', encoding='utf-8') as f: # 写入文件
f.write('#include nn')
while 1:
print('.', end="")
im.putpalette(mypalette)
new_im = Image.new("RGB", im.size)
new_im.paste(im)
# 缩放图像,
width = new_im.size[0] # 获取原始图像宽度
height = new_im.size[1] # 获取原始图像高度
new_height = 82 # 等比例缩放后的图像高度,根据实际需要调整
# print(width, " ", height)
if height > new_height:
ratio = round(new_height / height, 3) # 缩放系数
new_im = new_im.resize((int(width * ratio), int(height * ratio)), Image.ANTIALIAS)
# 获取图像字节流,转16进制格式
img_byte = BytesIO() # 获取字节流
new_im.save(img_byte, format='jpeg')
# print(img_byte.getvalue())
# 16进制字符串
img_hex = binascii.hexlify(img_byte.getvalue()).decode('utf-8')
arr_name = filename + '_' + str(i)
arr_size = 0 # 记录数组长度
arr_name_all += arr_name + ','
# 将ac --> 0xac
f.write('const uint8_t ' + arr_name + '[] PROGMEM = { n') # 写前
for index, x in zip(range(len(img_hex)), range(0, len(img_hex), 2)):
temp_hex = '0x' + img_hex[x:x + 2] + ', '
# 30个数据换行
if (index + 1) % 30 == 0:
temp_hex += 'n'
f.write(temp_hex) # 写入文件
arr_size += 1
f.write('n};nn') # 写结尾
i += 1
arr_size_all += str(arr_size) + ','
# 保存一帧帧图像
if saveImg:
if not os.path.exists('./out_img'):
os.mkdir('./out_img')
if not os.path.exists('./out_img/' + filename):
os.mkdir('./out_img/' + filename)
new_im.save('./out_img/' + filename + '/' + str(i) + '.jpg')
try:
im.seek(im.tell() + 1)
except EOFError:
# 动图读取结束
f.write('const uint8_t *' + filename + '[' + str(i) + '] PROGMEM { ' + arr_name_all + '};n')
f.write('const uint32_t ' + filename + '_size[' + str(i) + '] PROGMEM { ' + arr_size_all + '};')
print("成功保存文件为:" + filename + '.h')
break
except EOFError as e:
print(e.args)
print('=============')
print(traceback.format_exc())
pass # end of sequence
if __name__ == '__main__':
processImage("sm6.gif", True)
# im=Image.open("foo0.bmp")
# print ("img info:",im.format,im.size)
运行及生成相应的.h文件
.h文件内容格式
-将一帧帧图像数据存放到数组,程序要用到
2.如何使用
数组名 sm1
数组大小 sm1_size
#include3.显示效果#include #include #include #include "sm6.h" //引入gif生成的.h文件 // LCD屏幕相关设置,是什么屏幕,什么驱动, // 引脚需配置tft_espi库中的 User_Setup.h文件 TFT_eSPI tft = TFT_eSPI(); //动画更新时间记录 int Amimate_reflash_Time = 0; //从动图数组里面获取一帧图像数据 void imgAnim(const uint8_t **Animate_value, uint32_t *Animate_size) { int Animate_key = -1; //初始化图标显示帧数 //记得修改 //sm1 是生成的文件中,数组名 //sm1_size 数组容量名 *Animate_value = sm1[Animate_key]; *Animate_size = sm1_size[Animate_key]; if (Animate_key >= (sizeof(sm1)/sizeof(sm1[0])-1)) //gif动图帧数 25-1 Animate_key = -1; } // TFT屏幕输出函数 bool tft_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t *bitmap) { if (y >= tft.height()) return 0; tft.pushImage(x, y, w, h, bitmap); // Return 1 to decode next block return 1; } void setup() { Serial.begin(115200); tft.begin(); tft.invertDisplay(1); //反转所有显示颜色:1反转,0正常 tft.setRotation(0); tft.fillScreen(0x0000); TJpgDec.setJpgScale(1); TJpgDec.setSwapBytes(true); TJpgDec.setCallback(tft_output); tft.fillScreen(TFT_BLACK); } void loop() { const uint8_t *Animate_value; //指向关键帧的指针 uint32_t Animate_size; //指向关键帧大小的指针 if (millis() - Amimate_reflash_Time > 100) // 100 ms切换一次 { Amimate_reflash_Time = millis(); imgAnim(&Animate_value, &Animate_size); TJpgDec.drawJpg(0, 0, Animate_value, Animate_size); } }



