吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 579|回复: 11
收起左侧

[其他原创] gif图片转TFT_eSPI使用的hex数组小工具

[复制链接]
红叶落尽 发表于 2024-12-13 17:02
本小工具专门用于嵌入式开发中的gif图片转换,使用方法,编译后直接命令行  exefile **.gif 高 宽 R G B 透明度 (如果不加后面的参数,默认是75*75 透明背景,RGB是背景颜色的值),代码如下,自己可以编译修改。

[Golang] 纯文本查看 复制代码
package main

import (
	"bytes"
	"fmt"
	"image"
	"image/color"
	"image/draw"
	"image/gif"
	"log"
	"os"
	"strconv"
	"strings"

	"github.com/disintegration/imaging"
	//"github.com/nfnt/resize"
)

// RGB565 converts a color.Color to a 16-bit RGB565 value.
func RGB565(c color.Color) uint16 {
	r, g, b, _ := c.RGBA()

	// Convert to 5/6/5 bits
	r5 := uint16((r >> 11) & 0x1F)
	g6 := uint16((g >> 10) & 0x3F)
	b5 := uint16((b >> 11) & 0x1F)

	return (r5 << 11) | (g6 << 5) | b5
}

func convertPalettedToRGBA(p image.Paletted) *image.RGBA { // Convert a paletted image to an RGBA image.
	rgba := image.NewRGBA(p.Bounds())
	for y := p.Bounds().Min.Y; y < p.Bounds().Max.Y; y++ {
		for x := p.Bounds().Min.X; x < p.Bounds().Max.X; x++ {
			rgba.Set(x, y, p.At(x, y))
		}
	}
	return rgba
}

// ConvertGIFToHeader converts each frame of a GIF to a C header file with a multi-dimensional array.
func ConvertGIFToHeader(inputFile, outputFile, arrayName string, newWidth, newHeight int, background color.Color) error {
	file, err := os.Open(inputFile)
	if err != nil {
		return err
	}
	defer file.Close()

	gifImage, err := gif.DecodeAll(file)
	if err != nil {
		return err
	}

	originalBounds := gifImage.Image[0].Bounds()

	var buffer bytes.Buffer
	//buffer.WriteString(fmt.Sprintf("#ifndef %s_H\n#define %s_H\n\n", arrayName, arrayName))
	buffer.WriteString(fmt.Sprintf("const unsigned short PROGMEM %s[%d][%d] = {\n", arrayName, len(gifImage.Image), newWidth*newHeight))

	for _, frame := range gifImage.Image {
		// Resize the new image
		//resizedImage := resize.Resize(newWidth, newHeight, frame, resize.Lanczos3)
		resizedImg := imaging.Resize(frame, newWidth, newHeight, imaging.Lanczos)

		newImage := image.NewRGBA(image.Rect(0, 0, originalBounds.Dx(), originalBounds.Dy()))
		draw.Draw(newImage, newImage.Bounds(), &image.Uniform{C: background}, image.Point{}, draw.Src)

		// Draw the frame over the background
		draw.Draw(newImage, newImage.Bounds(), resizedImg, originalBounds.Min, draw.Over)
	

		buffer.WriteString("{\n")
		for y := 0; y < int(newHeight); y++ {
			for x := 0; x < int(newWidth); x++ {
				color := newImage.At(x, y)
				rgb565 := RGB565(color)
				buffer.WriteString(fmt.Sprintf("0x%04X, ", rgb565))
			}
			buffer.WriteString("\n")
		}
		buffer.WriteString("},\n")
	}

	buffer.WriteString("};\n")

	//buffer.WriteString("};\n\n#endif\n")

	return os.WriteFile(outputFile, buffer.Bytes(), 0644)
}

func main() {
	var newWidth, newHeight, rv, gv, bv, av = 75, 75, 0, 0, 0, 0
	inputFilename := os.Args[1]
	//flag,_ := strconv.Atoi(os.Args[2])
	if len(os.Args) > 2 {
		newWidth, _ = strconv.Atoi(os.Args[2])
		newHeight, _ = strconv.Atoi(os.Args[3])
		rv, _ = strconv.Atoi(os.Args[4])
		gv, _ = strconv.Atoi(os.Args[5])
		bv, _ = strconv.Atoi(os.Args[6])
	}
	name := strings.Split(inputFilename, ".")
	if name[1] != "gif" {
		fmt.Printf("Error: Input file must be a GIF image\n")
	} else {
		outputFilename := "output.h"
		arrayName := "myGifFrames"
		// Example new dimensions
		background := color.RGBA{R: uint8(rv), G: uint8(gv), B: uint8(bv), A: uint8(av)} // Example background color

		err := ConvertGIFToHeader(inputFilename, outputFilename, arrayName, newWidth, newHeight, background)
		if err != nil {
			log.Fatalf("Error: %v", err)
		}

		fmt.Printf("Successfully converted %s to %s\n", inputFilename, outputFilename)
	}
}

免费评分

参与人数 1吾爱币 +7 热心值 +1 收起 理由
苏紫方璇 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

superychen 发表于 2024-12-13 18:24
参考大佬代码用cursor撸了个html的,浏览器直接打开使用,方便点

[HTML] 纯文本查看 复制代码
<!DOCTYPE html>
<html lang="zh">

<head>
    <meta charset="UTF-8">
    <title>GIF转TFT_eSPI</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 800px;
            margin: 0 auto;
            padding: 20px;
        }

        .container {
            border: 1px solid #ccc;
            padding: 20px;
            border-radius: 5px;
        }

        .input-group {
            margin-bottom: 15px;
        }

        label {
            display: block;
            margin-bottom: 5px;
        }

        input[type="number"] {
            width: 80px;
        }

        button {
            padding: 10px 20px;
            background-color: #4CAF50;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            margin-right: 10px;
        }

        button:disabled {
            background-color: #cccccc;
            cursor: not-allowed;
        }

        #preview {
            margin-top: 20px;
        }

        #downloadBtn {
            display: none;
        }

        .progress-container {
            margin: 20px 0;
            background-color: #f0f0f0;
            border-radius: 4px;
            overflow: hidden;
        }

        .progress-bar {
            width: 0;
            height: 20px;
            background-color: #4CAF50;
            transition: width 0.3s ease;
        }

        .preview-container {
            margin-top: 20px;
            text-align: center;
        }

        .preview-container canvas {
            max-width: 100%;
            border: 1px solid #ccc;
            margin-top: 10px;
        }

        .frames-container {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
            gap: 10px;
            margin-top: 20px;
        }

        .frame-item {
            position: relative;
            border: 1px solid #ccc;
            padding: 5px;
            cursor: pointer;
            transition: all 0.3s ease;
        }

        .frame-item canvas {
            width: 100%;
            height: auto;
            display: block;
        }

        .frame-item .progress-status {
            position: absolute;
            top: -8px;
            right: -8px;
            background: #4CAF50;
            color: white;
            padding: 2px 6px;
            border-radius: 10px;
            font-size: 12px;
            display: none;
        }

        .frame-item.processing {
            border-color: #FFA500;
            box-shadow: 0 0 0 2px #FFA500;
        }

        .frame-item.completed {
            border-color: #4CAF50;
            box-shadow: 0 0 0 2px #4CAF50;
        }

        .frame-item .download-hint {
            display: none;
            position: absolute;
            bottom: 0;
            left: 0;
            right: 0;
            background: rgba(0, 0, 0, 0.7);
            color: white;
            font-size: 12px;
            padding: 2px;
            text-align: center;
        }

        .frame-item:hover .download-hint {
            display: block;
        }

        .preview-grid {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 10px;
            margin-top: 20px;
        }

        .preview-item {
            text-align: center;
        }

        .preview-item h3 {
            margin: 5px 0;
            font-size: 14px;
        }

        .preview-item canvas {
            max-width: 100%;
            height: auto;
            border: 1px solid #ccc;
        }

        .preview-dialog {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: white;
            padding: 20px;
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
            z-index: 1000;
            max-width: 90%;
            max-height: 90vh;
            overflow: auto;
            border-radius: 8px;
        }

        .preview-dialog-overlay {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: rgba(0, 0, 0, 0.5);
            z-index: 999;
        }

        input[type="range"] {
            width: 200px;
            margin: 0 10px;
            vertical-align: middle;
        }

        #scalePercent {
            display: inline-block;
            min-width: 50px;
        }

        input[type="number"]:disabled {
            background-color: #f5f5f5;
            color: #666;
        }

        input[type="color"] {
            width: 50px;
            height: 30px;
            padding: 0;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            vertical-align: middle;
        }

        #alphaSlider {
            width: 150px;
            margin: 0 10px;
            vertical-align: middle;
        }

        #alphaValue {
            display: inline-block;
            min-width: 80px;
            vertical-align: middle;
        }

        #colorPreview {
            display: inline-block;
            width: 40px;
            height: 40px;
            border: 1px solid #ccc;
            vertical-align: middle;
            margin-left: 10px;
            background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEwAACxMBAJqcGAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAAYdEVYdFRpdGxlAENhbnZhcyBJbWFnZSBQcmV2aWV3e0fDaAAAABV0RVh0QXV0aG9yAERhbmllbCBCZXJuZXKEXtk4AAAAGHRFWHRDcmVhdGlvbiBUaW1lADIwMTAtMDEtMDgVeXRLAAAAIXRFWHRTb3VyY2UAaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvxnML+gAAABh0RVh0U291cmNlX1VSTABodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy9pbmtzY2FwZS5vcmeb7jwaAAAAKklEQVQ4jWNgGAWjYBSMAkbG/////2fAh0FyjIyMjP+JwaNgFIyCUUAJAADR4AYJh7zW1AAAAABJRU5ErkJggg==');
        }
    </style>
    <script src="https://cdn.jsdelivr.net/npm/omggif@1.0.10/omggif.min.js"></script>
</head>

<body>
    <div class="container">
        <h1>GIF转TFT_eSPI</h1>
        <div class="input-group">
            <label for="gifFile">选择GIF文件:</label>
            <input type="file" id="gifFile" accept=".gif">
        </div>
        <div class="input-group">
            <label>尺寸设置:</label>
            <input type="number" id="width" min="1" disabled> x
            <input type="number" id="height" min="1" disabled>
            <input type="range" id="sizeSlider" min="10" max="500" value="100">
            <span id="scalePercent">100%</span>
        </div>
        <div class="input-group">
            <label>背景颜色:</label>
            <input type="color" id="colorPicker" value="#000000">
            <input type="range" id="alphaSlider" min="0" max="100" value="100">
            <span id="alphaValue">透明度: 100%</span>
            <div id="colorPreview"></div>
        </div>
        <button id="convertBtn">转换</button>
        <button id="downloadBtn">下载header文件</button>
        <div class="progress-container">
            <div class="progress-bar" id="progressBar"></div>
        </div>
        <div class="preview-container">
            <div id="preview"></div>
            <canvas id="previewCanvas"></canvas>
            <div id="framesContainer" class="frames-container"></div>
        </div>
    </div>

    <script>
        let headerContent = '';
        let frameCanvases = [];
        let frameContents = [];
        let originalWidth = 0;
        let originalHeight = 0;

        // RGB565 颜色转换函数
        function RGB565(r, g, b) {
            r = Math.round((r / 255) * 31);
            g = Math.round((g / 255) * 63);
            b = Math.round((b / 255) * 31);
            return ((r << 11) | (g << 5) | b).toString(16).padStart(4, '0').toUpperCase();
        }

        // 解析GIF文件的所有帧
        async function parseGif(file) {
            const arrayBuffer = await file.arrayBuffer();
            const buffer = new Uint8Array(arrayBuffer);
            const reader = new GifReader(buffer);
            const frames = [];

            // 创建临时canvas用于渲染帧
            const tempCanvas = document.createElement('canvas');
            tempCanvas.width = reader.width;
            tempCanvas.height = reader.height;
            const tempCtx = tempCanvas.getContext('2d');
            const imageData = tempCtx.createImageData(reader.width, reader.height);

            // 处理每一帧
            for (let i = 0; i < reader.numFrames(); i++) {
                // 渲染当前帧
                reader.decodeAndBlitFrameRGBA(i, imageData.data);
                tempCtx.putImageData(imageData, 0, 0);

                // 创建新canvas存储当前帧
                const canvas = document.createElement('canvas');
                canvas.width = reader.width;
                canvas.height = reader.height;
                const ctx = canvas.getContext('2d');
                ctx.drawImage(tempCanvas, 0, 0);

                frames.push({
                    dims: {
                        width: reader.width,
                        height: reader.height,
                        left: 0,
                        top: 0
                    },
                    canvas: canvas
                });
            }

            return frames;
        }

        // 更新尺寸的函数
        function updateSize(percent) {
            const width = Math.round(originalWidth * percent / 100);
            const height = Math.round(originalHeight * percent / 100);
            document.getElementById('width').value = width;
            document.getElementById('height').value = height;
            document.getElementById('scalePercent').textContent = `${percent}%`;
        }

        // 添加滑块事件监听
        document.getElementById('sizeSlider').addEventListener('input', function (e) {
            updateSize(e.target.value);
        });

        // 文件选择事件处理
        document.getElementById('gifFile').addEventListener('change', async function (e) {
            const file = e.target.files[0];
            if (!file) return;

            document.getElementById('preview').textContent = '正在解析GIF文件...';
            const framesContainer = document.getElementById('framesContainer');
            framesContainer.innerHTML = '';
            frameCanvases = [];

            try {
                const frames = await parseGif(file);

                // 设置原始尺寸
                originalWidth = frames[0].dims.width;
                originalHeight = frames[0].dims.height;
                // 重置滑块和尺寸
                document.getElementById('sizeSlider').value = 100;
                updateSize(100);

                // 为每一帧创建预览
                frames.forEach((frame, index) => {
                    const frameDiv = document.createElement('div');
                    frameDiv.className = 'frame-item';

                    const canvas = document.createElement('canvas');
                    canvas.width = frame.dims.width;
                    canvas.height = frame.dims.height;
                    const ctx = canvas.getContext('2d');
                    ctx.drawImage(frame.canvas, 0, 0);

                    const status = document.createElement('div');
                    status.className = 'progress-status';

                    const downloadHint = document.createElement('div');
                    downloadHint.className = 'download-hint';
                    downloadHint.textContent = '点击下载此帧';

                    frameDiv.appendChild(canvas);
                    frameDiv.appendChild(status);
                    frameDiv.appendChild(downloadHint);
                    framesContainer.appendChild(frameDiv);
                    frameCanvases.push({ canvas, status, frameDiv });
                });

                document.getElementById('preview').textContent =
                    `GIF解析完成,共 ${frames.length} 帧,原始尺寸 ${originalWidth}x${originalHeight}。使用滑块调整尺寸,点击转换按钮开始处理。`;

            } catch (error) {
                document.getElementById('preview').textContent = '解析GIF文件时出错:' + error.message;
            }
        });

        async function convertGif() {
            const file = document.getElementById('gifFile').files[0];
            if (!file) {
                alert('请选择GIF文件');
                return;
            }

            const width = parseInt(document.getElementById('width').value);
            const height = parseInt(document.getElementById('height').value);
            // 从颜色选择器获取RGB值
            const color = document.getElementById('colorPicker').value;
            const r = parseInt(color.substr(1, 2), 16);
            const g = parseInt(color.substr(3, 2), 16);
            const b = parseInt(color.substr(5, 2), 16);
            // 从透明度滑块获取alpha值
            const alphaPercent = parseInt(document.getElementById('alphaSlider').value);
            const a = Math.round((100 - alphaPercent) * 255 / 100);

            // 显示加载提示
            document.getElementById('preview').textContent = '正在处理GIF文件,请稍候...';
            document.getElementById('convertBtn').disabled = true;
            document.getElementById('progressBar').style.width = '0%';

            try {
                const frames = await parseGif(file);
                const frameCount = frames.length;
                frameContents = [];

                const previewCanvas = document.getElementById('previewCanvas');
                previewCanvas.width = width;
                previewCanvas.height = height;
                const previewCtx = previewCanvas.getContext('2d');

                // 创建离屏canvas
                const canvas = document.createElement('canvas');
                const ctx = canvas.getContext('2d');
                canvas.width = width;
                canvas.height = height;

                // 生成header文件内容
                headerContent = `const unsigned short PROGMEM myGifFrames[${frameCount}][${width * height}] = {\n`;

                // 处理单个帧的函数
                async function processFrame(frameIndex) {
                    const frame = frames[frameIndex];

                    // 清除布并填充背景色
                    ctx.fillStyle = `rgba(${r},${g},${b},${a / 255})`;
                    ctx.fillRect(0, 0, width, height);

                    // 绘制到主画布并调整大小
                    ctx.drawImage(frame.canvas, 0, 0, width, height);

                    // 更新预览
                    previewCtx.clearRect(0, 0, width, height);
                    previewCtx.drawImage(canvas, 0, 0);

                    // 获取像素数据
                    const imageData = ctx.getImageData(0, 0, width, height);
                    const pixels = imageData.data;

                    headerContent += '{\n';
                    let frameContent = `const unsigned short PROGMEM myGifFrame${frameIndex}[${width * height}] = {\n`;

                    // 每20行让出一次主线程
                    const rowsPerBatch = 20;
                    for (let y = 0; y < height; y++) {
                        if (y % rowsPerBatch === 0 && y !== 0) {
                            await new Promise(resolve => setTimeout(resolve, 0));
                        }
                        for (let x = 0; x < width; x++) {
                            const i = (y * width + x) * 4;
                            const alpha = a === 0 ? pixels[i + 3] : a;
                            const blendedR = (pixels[i] * alpha + r * (255 - alpha)) / 255;
                            const blendedG = (pixels[i + 1] * alpha + g * (255 - alpha)) / 255;
                            const blendedB = (pixels[i + 2] * alpha + b * (255 - alpha)) / 255;
                            const rgb565 = RGB565(blendedR, blendedG, blendedB);
                            headerContent += `0x${rgb565}, `;
                            frameContent += `0x${rgb565}, `;
                        }
                        headerContent += '\n';
                        frameContent += '\n';
                    }

                    headerContent += '},\n';
                    frameContent += '};\n';
                    frameContents[frameIndex] = frameContent;

                    // 更新进度
                    const progress = ((frameIndex + 1) / frameCount * 100).toFixed(1);
                    document.getElementById('progressBar').style.width = `${progress}%`;
                    document.getElementById('preview').textContent =
                        `正在处理第 ${frameIndex + 1}/${frameCount} 帧 (${progress}%)`;

                    // 更新帧状态
                    frameCanvases.forEach((item, idx) => {
                        const status = item.status;
                        if (idx < frameIndex) {
                            updateFrameStatus(item, idx, 'completed');
                        } else if (idx === frameIndex) {
                            updateFrameStatus(item, idx, 'processing');
                        } else {
                            updateFrameStatus(item, idx, 'pending');
                        }
                    });
                }

                // 逐帧处理
                for (let frameIndex = 0; frameIndex < frameCount; frameIndex++) {
                    await processFrame(frameIndex);
                }

                headerContent += '};\n';

                // 转换完成后更新所有帧的状态
                frameCanvases.forEach((item, idx) => {
                    updateFrameStatus(item, idx, 'completed');
                });

                // 显示下载按钮
                document.getElementById('downloadBtn').style.display = 'inline-block';
                document.getElementById('preview').textContent = `转换完成!共处理 ${frameCount} 帧`;
            } catch (error) {
                document.getElementById('preview').textContent = '处理GIF文件时出错:' + error.message;
                document.getElementById('progressBar').style.width = '0%';
                frameCanvases.forEach(item => {
                    item.frameDiv.className = 'frame-item';
                    item.status.style.display = 'none';
                });
            } finally {
                document.getElementById('convertBtn').disabled = false;
            }
        }

        function downloadHeader() {
            const blob = new Blob([headerContent], { type: 'text/plain' });
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = 'output.h';
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
            URL.revokeObjectURL(url);
        }

        // RGB565 转回 RGB 函数
        function RGB565ToRGB(rgb565) {
            const r = ((rgb565 >> 11) & 0x1F) * 255 / 31;
            const g = ((rgb565 >> 5) & 0x3F) * 255 / 63;
            const b = (rgb565 & 0x1F) * 255 / 31;
            return [Math.round(r), Math.round(g), Math.round(b)];
        }

        // 创建验证预览
        function createVerificationPreview(frameIndex, width, height, rgb565Data) {
            const canvas = document.createElement('canvas');
            canvas.width = width;
            canvas.height = height;
            const ctx = canvas.getContext('2d');
            const imageData = ctx.createImageData(width, height);

            // 解析RGB565数据
            const matches = rgb565Data.match(/0x[0-9A-F]{4}/g);
            if (!matches) return null;

            // 转换回RGB并填充imageData
            for (let i = 0; i < matches.length; i++) {
                const rgb565 = parseInt(matches[i], 16);
                const [r, g, b] = RGB565ToRGB(rgb565);
                const idx = i * 4;
                imageData.data[idx] = r;
                imageData.data[idx + 1] = g;
                imageData.data[idx + 2] = b;
                imageData.data[idx + 3] = 255;
            }

            ctx.putImageData(imageData, 0, 0);
            return canvas;
        }

        // 下载单个帧的函数
        function downloadSingleFrame(frameIndex) {
            if (!frameContents[frameIndex]) return;

            // 直接下载文件,不显示预览
            const blob = new Blob([frameContents[frameIndex]], { type: 'text/plain' });
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = `frame_${frameIndex}.h`;
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
            URL.revokeObjectURL(url);
        }

        // 显示比对预览
        function showComparison(frameIndex) {
            if (!frameContents[frameIndex]) return;

            // 创建背景遮罩
            const overlay = document.createElement('div');
            overlay.className = 'preview-dialog-overlay';
            document.body.appendChild(overlay);

            // 创建预览对话框
            const dialog = document.createElement('div');
            dialog.className = 'preview-dialog';

            const closeBtn = document.createElement('button');
            closeBtn.textContent = '关闭';
            closeBtn.style.position = 'absolute';
            closeBtn.style.right = '10px';
            closeBtn.style.top = '10px';
            closeBtn.onclick = () => {
                document.body.removeChild(dialog);
                document.body.removeChild(overlay);
            };
            overlay.onclick = closeBtn.onclick;

            const previewGrid = document.createElement('div');
            previewGrid.className = 'preview-grid';

            // 始预览
            const originalPreview = document.createElement('div');
            originalPreview.className = 'preview-item';
            originalPreview.innerHTML = '<h3>原始图像</h3>';
            const originalCanvas = document.createElement('canvas');
            originalCanvas.width = frameCanvases[frameIndex].canvas.width;
            originalCanvas.height = frameCanvases[frameIndex].canvas.height;
            const originalCtx = originalCanvas.getContext('2d');
            originalCtx.drawImage(frameCanvases[frameIndex].canvas, 0, 0);
            originalPreview.appendChild(originalCanvas);

            // RGB565预览
            const rgb565Preview = document.createElement('div');
            rgb565Preview.className = 'preview-item';
            rgb565Preview.innerHTML = '<h3>转换结果</h3>';

            const width = parseInt(document.getElementById('width').value);
            const height = parseInt(document.getElementById('height').value);
            const verificationCanvas = createVerificationPreview(frameIndex, width, height, frameContents[frameIndex]);
            if (verificationCanvas) {
                rgb565Preview.appendChild(verificationCanvas);
            }

            previewGrid.appendChild(originalPreview);
            previewGrid.appendChild(rgb565Preview);
            dialog.appendChild(closeBtn);
            dialog.appendChild(previewGrid);
            document.body.appendChild(dialog);
        }

        // 更新帧状态的辅助函数
        function updateFrameStatus(item, idx, status) {
            switch (status) {
                case 'completed':
                    item.frameDiv.className = 'frame-item completed';
                    item.status.style.display = 'block';
                    item.status.textContent = `#${idx + 1}`;
                    addFrameButtons(item, idx);
                    break;
                case 'processing':
                    item.frameDiv.className = 'frame-item processing';
                    item.status.style.display = 'block';
                    item.status.textContent = `处理中 #${idx + 1}`;
                    break;
                default:
                    item.frameDiv.className = 'frame-item';
                    item.status.style.display = 'none';
            }
        }

        // 添加帧按钮的辅助函数
        function addFrameButtons(item, idx) {
            const downloadHint = item.frameDiv.querySelector('.download-hint');
            if (downloadHint) downloadHint.style.display = 'none';

            if (!item.frameDiv.querySelector('.frame-buttons')) {
                const buttonsDiv = document.createElement('div');
                buttonsDiv.className = 'frame-buttons';
                buttonsDiv.style.position = 'absolute';
                buttonsDiv.style.bottom = '0';
                buttonsDiv.style.left = '0';
                buttonsDiv.style.right = '0';
                buttonsDiv.style.display = 'none';
                buttonsDiv.style.justifyContent = 'space-around';
                buttonsDiv.style.background = 'rgba(0, 0, 0, 0.7)';
                buttonsDiv.style.padding = '2px';

                const previewBtn = document.createElement('button');
                previewBtn.textContent = '比对';
                previewBtn.style.padding = '2px 5px';
                previewBtn.style.fontSize = '12px';
                previewBtn.onclick = (e) => {
                    e.stopPropagation();
                    showComparison(idx);
                };

                const downloadBtn = document.createElement('button');
                downloadBtn.textContent = '下载';
                downloadBtn.style.padding = '2px 5px';
                downloadBtn.style.fontSize = '12px';
                downloadBtn.onclick = (e) => {
                    e.stopPropagation();
                    downloadSingleFrame(idx);
                };

                buttonsDiv.appendChild(previewBtn);
                buttonsDiv.appendChild(downloadBtn);
                item.frameDiv.appendChild(buttonsDiv);

                // 鼠标悬停时显示按钮
                item.frameDiv.onmouseenter = () => buttonsDiv.style.display = 'flex';
                item.frameDiv.onmouseleave = () => buttonsDiv.style.display = 'none';
            }
        }

        // 添加按钮事件监听器
        document.getElementById('convertBtn').addEventListener('click', convertGif);
        document.getElementById('downloadBtn').onclick = downloadHeader;

        // 更新颜色预览的函数
        function updateColorPreview() {
            const color = document.getElementById('colorPicker').value;
            const alpha = (100 - parseInt(document.getElementById('alphaSlider').value)) / 100;
            document.getElementById('colorPreview').style.backgroundColor = color;
            document.getElementById('colorPreview').style.opacity = alpha;
        }

        // 颜色选择器和透明度滑块事件监听
        document.getElementById('colorPicker').addEventListener('input', updateColorPreview);
        document.getElementById('alphaSlider').addEventListener('input', function (e) {
            document.getElementById('alphaValue').textContent = `透明度: ${e.target.value}%`;
            updateColorPreview();
        });

        // 初始化颜色预览
        updateColorPreview();
    </script>
</body>

</html>

免费评分

参与人数 2吾爱币 +4 热心值 +1 收起 理由
苏紫方璇 + 3 + 1 用心讨论,共获提升!
红叶落尽 + 1 用心讨论,共获提升!

查看全部评分

applepv 发表于 2024-12-13 22:32
superychen 发表于 2024-12-13 18:24
参考大佬代码用cursor撸了个html的,浏览器直接打开使用,方便点

[mw_shl_code=html,true]

我这边转换变成这样,,,
微信图片_20241213223140.png
qi1990 发表于 2024-12-13 17:11
 楼主| 红叶落尽 发表于 2024-12-13 17:23

不复杂,就是对gif进行逐帧处理,然后再按照图片进行转换,最后输出数组
fanliansuo1 发表于 2024-12-13 17:24
顶顶顶,顶顶顶
头像被屏蔽
pomxion 发表于 2024-12-13 17:42
提示: 作者被禁止或删除 内容自动屏蔽
Akasiki 发表于 2024-12-13 20:48
感谢分享
applepv 发表于 2024-12-13 22:35
applepv 发表于 2024-12-13 22:32
我这边转换变成这样,,,

不知道为啥,,试了几次都不行,,刚发完贴再试又正常了
superychen 发表于 2024-12-14 07:01
applepv 发表于 2024-12-13 22:35
不知道为啥,,试了几次都不行,,刚发完贴再试又正常了

你这应该是网络问题一开始js文件没拉下来,后来拉成功了
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2025-1-6 05:29

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表