使用libgif库解码全过程(C语言)-包括扩展块的处理
我看到的所有例程,都把扩展部分的处理跳过了,而我的动画是有透明度的,这就导致解码后的图像在有透明色的像素部分,呈现了很多的黑点,或者闪白的情况出现。经过调试,终于成功。
文件格式
先了解一下GIF的文件格式:
GIF文件格式说明 - 猛回头 - 博客园
扩展块的处理
// 扩展块解析if (currentImage.ExtensionBlockCount > 0) // 存在扩展块{switch (currentImage.ExtensionBlocks[0].Function){case CONTINUE_EXT_FUNC_CODE:case COMMENT_EXT_FUNC_CODE: // 注释扩展块case PLAINTEXT_EXT_FUNC_CODE: // 图形文本扩展块case APPLICATION_EXT_FUNC_CODE: // 应用程序扩展块break;case GRAPHICS_EXT_FUNC_CODE: // 图形控制扩展块{if (DGifExtensionToGCB(currentImage.ExtensionBlocks[0].ByteCount, currentImage.ExtensionBlocks[0].Bytes, &gcb) == GIF_ERROR){continue;}// 透明色索引值,如果没有透明色,该值为-1transparent_color = gcb.TransparentColor;// 延迟时间的单位为0.01秒,转换为微秒delay_us = gcb.DelayTime * 10 * 1000;}default:break;}}//转换//显示if (gcb.DisposalMode == DISPOSE_BACKGROUND) // 回复到背景色{memset(gif_buffer, fp->SBackGroundColor, size);}else if (gcb.DisposalMode == DISPOSE_PREVIOUS) // 回复到先前状态(上一帧){for (int i = 0; i < size; i++){prev_gif_buffer[i] = gif_buffer[i];}}
透明度处理
在有透明的的情况下,GIF图像转为RGB需要进行以下判断:
// 颜色索引等于透明度索引,进行透明处理,这里直接跳过,保持原像素值(上一帧此索引位置的像素)不变if (color_index == transparent_index){rgb_index += 3;}else{// 将颜色项的红色分量写入输出缓冲区rgb[rgb_index++] = ColorMap->Colors[color_index].Red;// 将颜色项的绿色分量写入输出缓冲区rgb[rgb_index++] = ColorMap->Colors[color_index].Green;// 将颜色项的蓝色分量写入输出缓冲区rgb[rgb_index++] = ColorMap->Colors[color_index].Blue;}
如果使用了透明色,要确定在解析时,解析到这一个像素时,不要对这个值进行任何操作,跳过即可。跳过后,你会发现上一帧这个位置的图像又出现在这个位置,符合预期,显示正常。
全部代码
全部代码如下:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/fb.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdint.h>
#include <stdlib.h>
#include <giflib/gif_lib.h>int fd = 0;/** GIF图像转换为RGB888图像字节处理)* @name: Gif_To_Rgb888* @param {ColorMapObject} *ColorMap 颜色表* @param {uint8_t} *rgb 解码后的rgb图像(输出)* @param {GifByteType} *gif_buf gif图像* @param {int} gif_width gif图像宽度* @param {int} gif_height gif图像高度* @param {int} transparent_index gif透明度索引值* @return {*}*/
static void Gif_To_Rgb888(ColorMapObject *ColorMap, uint8_t *rgb, GifByteType *gif_buf, int gif_width, int gif_height, int transparent_index)
{int index_h = 0; // 循环变量,用于遍历图像的高度方向int index_w = 0; // 循环变量,用于遍历图像的宽度方向// 外层循环遍历图像的每一行for (index_h = 0; index_h < gif_height; index_h++){// rgb_buf数据位置索引int rgb_index = index_h * gif_width * 3;// 内层循环遍历当前行的每一个像素for (index_w = 0; index_w < gif_width; index_w++){// 如果存在颜色表,进行转换操作if (ColorMap){// 获取当前像素在GIF缓冲区中的索引int color_index = gif_buf[index_h * gif_width + index_w];// 颜色索引等于透明度索引,进行透明处理,这里直接跳过,保持原像素值(上一帧此索引位置的像素)不变if (color_index == transparent_index){rgb_index += 3;}else{// 将颜色项的红色分量写入输出缓冲区rgb[rgb_index++] = ColorMap->Colors[color_index].Red;// 将颜色项的绿色分量写入输出缓冲区rgb[rgb_index++] = ColorMap->Colors[color_index].Green;// 将颜色项的蓝色分量写入输出缓冲区rgb[rgb_index++] = ColorMap->Colors[color_index].Blue;}}}}
}/*** @name: Display_RGB_From_Buffer* @param {uint8_t} *buffer* @param {int} fb_fd* @param {int} xres* @param {int} yres* @return {*}*/
static void Display_RGB_From_Buffer(uint8_t *buffer, int gif_width, int gif_height)
{fd = open("/dev/fb0", O_RDWR);if (fd < 0){perror("设备文件打开失败");return;}// 屏幕分辨率,固定不变int screen_size = 480 * 800 * 4;char *fb_data = malloc(screen_size);memset(fb_data, 0, screen_size);for (int y = 0; y < gif_height; y++){for (int x = 0; x < gif_width; x++){uint8_t r = buffer[(y * gif_width + x) * 3 + 0];uint8_t g = buffer[(y * gif_width + x) * 3 + 1];uint8_t b = buffer[(y * gif_width + x) * 3 + 2];// 这个索引值是显示位置的变化,现在的显示位置是在屏幕的(50,50)int index = (y + 50) * 480 * 4 + (x + 50) * 4;fb_data[index + 0] = b;fb_data[index + 1] = g;fb_data[index + 2] = r;fb_data[index + 3] = 255;}}ssize_t bytes_written = write(fd, fb_data, screen_size);if (bytes_written != screen_size){perror("Error writing to framebuffer");}free(fb_data);close(fd);
}int GIF_Decode(char *file)
{int error; // 错误码int size = 0; // GIF数据大小int transparent_color = -1; // 透明色索引int delay_us; // 延时时间,单位微秒GifFileType *fp; // gif文件标识符GifByteType *gif_buffer = NULL; // GIF数据GifByteType *prev_gif_buffer = NULL; // GIF上一帧数据GraphicsControlBlock gcb = { // GIF图像控制块.DelayTime = 0,.DisposalMode = 0,.TransparentColor = -1,.UserInputFlag = 0};// 定义交错图像的读取偏移量和步长数组int InterlacedOffset[] = {0, 4, 2, 1};int InterlacedJumps[] = {8, 8, 4, 2};// 打开GIF文件fp = DGifOpenFileName(file, &error);if (fp == NULL){printf("打开GIF文件失败: %s\n", GifErrorString(error));return -1;}// 读取GIF文件的完整信息(一次性读取所有GIF信息,存放在fp结构体内)if (DGifSlurp(fp) == GIF_ERROR){printf("解析GIF详细信息错误: %s\n", GifErrorString(fp->Error));// 关闭GIF文件DGifCloseFile(fp, &error);return -1;}printf("GIF分辨率: %d*%d\n", fp->SWidth, fp->SHeight);printf("总帧数: %d\n", fp->ImageCount);// 内存空间申请、初始化size = fp->SWidth * fp->SHeight;uint8_t rgb[size * 3];memset(rgb, 0, sizeof(rgb));gif_buffer = (GifByteType *)malloc(size * sizeof(GifByteType));if (gif_buffer == NULL){printf("内存空间申请失败\n");DGifCloseFile(fp, &error);return -1;}prev_gif_buffer = (GifByteType *)malloc(size * sizeof(GifByteType));if (prev_gif_buffer == NULL){printf("内存空间申请失败\n");DGifCloseFile(fp, &error);return -1;}// 填充背景颜色for (int i = 0; i < size; i++){gif_buffer[i] = fp->SBackGroundColor;prev_gif_buffer[i] = fp->SBackGroundColor;}// 遍历读取到的每一帧图像数据进行处理for (int ImageNum = 0; ImageNum < fp->ImageCount; ImageNum++){// 当前图像的信息SavedImage currentImage = fp->SavedImages[ImageNum];// 获取图像的位置和尺寸信息int row = currentImage.ImageDesc.Top; // 图像位置-行int col = currentImage.ImageDesc.Left; // 图像位置-列int width = currentImage.ImageDesc.Width; // 图像数据宽度int height = currentImage.ImageDesc.Height; // 图像数据高度// 检测是否超过逻辑屏幕边界if (currentImage.ImageDesc.Left + currentImage.ImageDesc.Width > fp->SWidth || currentImage.ImageDesc.Top + currentImage.ImageDesc.Height > fp->SHeight){printf("第%d帧图像超过了屏幕边界,无法解码.\n", ImageNum);continue;}// 如果图像是交错的if (currentImage.ImageDesc.Interlace){// 按照交错图像的读取方式逐行读取像素数据for (int iH = 0; iH < 4; iH++){for (int iW = row + InterlacedOffset[iH]; iW < row + height; iW += InterlacedJumps[iH]){// 计算当前像素在Buffer中的索引位置int index = (iW * fp->SWidth + col);for (int x = 0; x < width; x++){gif_buffer[index + x] = currentImage.RasterBits[(iW - row) * width + x];}}}}else{// 如果不是交错图像,则顺序读取像素数据for (int iH = 0; iH < height; iH++){// 计算当前像素在Buffer中的索引位置int index = ((row + iH) * fp->SWidth + col);for (int iW = 0; iW < width; iW++){gif_buffer[index + iW] = currentImage.RasterBits[iH * width + iW];}}}// 扩展块解析if (currentImage.ExtensionBlockCount > 0) // 存在扩展块{switch (currentImage.ExtensionBlocks[0].Function){case CONTINUE_EXT_FUNC_CODE:case COMMENT_EXT_FUNC_CODE: // 注释扩展块case PLAINTEXT_EXT_FUNC_CODE: // 图形文本扩展块case APPLICATION_EXT_FUNC_CODE: // 应用程序扩展块break;case GRAPHICS_EXT_FUNC_CODE: // 图形控制扩展块{if (DGifExtensionToGCB(currentImage.ExtensionBlocks[0].ByteCount, currentImage.ExtensionBlocks[0].Bytes, &gcb) == GIF_ERROR){continue;}// 透明色索引值,如果没有透明色,该值为-1transparent_color = gcb.TransparentColor;// 延迟时间的单位为0.01秒,转换为微秒delay_us = gcb.DelayTime * 10 * 1000;}default:break;}}// 获取颜色映射表,如果没有颜色映射表则退出,有局部颜色表先用局部颜色表,否则使用全局颜色表ColorMapObject *colorMap = (currentImage.ImageDesc.ColorMap ? currentImage.ImageDesc.ColorMap : fp->SColorMap);if (colorMap == NULL){continue;}// 将 GIF 图像数据转换为 RGB888 格式Gif_To_Rgb888(colorMap, rgb, gif_buffer, fp->SWidth, fp->SHeight, transparent_color);#if 1// LCD显示解码后的图像Display_RGB_From_Buffer(rgb, fp->SWidth, fp->SHeight);// 帧间隔时间usleep(delay_us);
#endifif (gcb.DisposalMode == DISPOSE_BACKGROUND) // 回复到背景色{memset(gif_buffer, fp->SBackGroundColor, size);}else if (gcb.DisposalMode == DISPOSE_PREVIOUS) // 回复到先前状态(上一帧){for (int i = 0; i < size; i++){prev_gif_buffer[i] = gif_buffer[i];}}}// 释放为Buffer分配的内存空间free(gif_buffer);free(prev_gif_buffer);// 关闭GIF文件,释放资源DGifCloseFile(fp, &error);return 0;
}
相关文章:

使用libgif库解码全过程(C语言)-包括扩展块的处理
我看到的所有例程,都把扩展部分的处理跳过了,而我的动画是有透明度的,这就导致解码后的图像在有透明色的像素部分,呈现了很多的黑点,或者闪白的情况出现。经过调试,终于成功。 文件格式 先了解一下GIF的文…...

blazor实现ASP.NET网站用户批量注册方法
ASP.NET网站用户批量注册是许多使用blazor系统开发遇到的问题,为了解决这个问题,我们提出比较完善的解决方法,通过代码实现了一个批量用户注册功能,用于解析一份用户名列表,并通过后台服务注册用户,同时对成功和失败的注册进行记录和反馈。以下是实现功能的详细工作原理描…...

SpringCloud 入门(4)—— 网关
上一篇:SpringCloud 入门(3)—— Nacos配置中心-CSDN博客 Spring Cloud Gateway 作为 Spring Cloud 生态系统的一部分,主要在微服务架构中充当 API 网关的角色。它提供了统一的入口点来处理所有的 HTTP 请求,并将这些请…...

什么是WebAssembly?怎么使用?
一、简述 WebAssembly,也称为Wasm,是基于堆栈的虚拟机的二进制指令格式。它被设计为一个可移植的目标,用于编译C、C和Rust等高级编程语言,允许代码以接近本机速度在web浏览器中运行。WebAssembly于2015年由包括谷歌、微软、Mozill…...

v3s点RGB屏 40pin 800x480,不一样的点屏,不通过chosen。
一、背景、目的、简介。 一般来说,通过uboot将屏幕参数传给kernel,是通过修改设备树。 uboot和kernel都需要屏幕点亮。uboot侧重于显示一张图片。而kernel则多是动画。 在这里,我先是找到了一个裸机点屏的代码。将其编译成静态库后&#x…...

某科技局国产服务器PVE虚拟化技术文档
环境介绍 硬件配置 服务器品牌:黄河 型号:Huanghe 2280 V2 Cpu型号:kunpeng-920 磁盘信息 :480SSD * 2 ,4T*4 网卡:板载四口千兆 如下表 四台服务器同等型号配置,均做单节点虚拟化,数据保护采用底层r…...

中科岩创边坡自动化监测解决方案
行业现状 由于边坡不稳定性因素,可能会造成斜坡上的岩土体沿着某个面不均匀向下向外滑动,形成滑坡;陡峭山坡上岩土体在重力作用下,发生陡然倾落运动,造成崩塌;在沟谷或山坡上产生的夹带大量泥沙、石块等固体…...

GPT-O3:简单介绍
GPT-O3:人工智能领域的重大突破 近日,OpenAI发布了其最新的AI模型GPT-O3,这一模型在AGI评估中取得了惊人的成绩,展现出强大的能力和潜力。GPT-O3的出现标志着人工智能领域的重大进步,预计将在2025年实现更大的突破。 …...

cudnn版本gpu架构
nvcc --help 可以看 --gpu-architecture 写到的支持的架构 NVIDIA 的 GPU 架构是按代次发布的,以下是这些架构的对应说明: NVIDIA Hopper: 这是 NVIDIA 于 2022 年推出的架构之一,面向高性能计算(HPC)和人工智能&…...

数据库安全-redisCouchdb
1.redis未授权访问 默认端口:6379 1.1 Redis沙盒逃逸漏洞RCE-CVE-2022-0543 介绍:Redis 是一套开源的使用 ANSI C编写、支持网络、可基于内存亦可持久化的日志型、键值存储数据库,并提供多种语言的API。Redis 如果在没有开启认证的情况下,…...

ubuntu22.04安装PaddleX3
PaddleOCR 安装过程可以参考PaddleX本地安装教程 我的电脑环境配置: ubuntu22.04 cuda11.8(之前安装的是12.4没有匹配的paddle-gpu;这里改成11.8) 一、安装基础环境 1、 conda create -n ppx1 python3.10 2、 conda activate ppx1 3、…...

Flutter 实现全局悬浮按钮学习
Flutter 代码如何实现了一个全局悬浮按钮,当点击按钮时,会显示一个可以拖动并且通过长按可以移除的悬浮控件。 前置知识点学习 Offset Offset 是 Flutter 中的一个类,用于表示二维平面中的位置或位移。它通常用于描述坐标系中的一个点&…...

14-C语言多文件编程
一、各种变量 在学习多文件编程之前,先要了解清楚各种变量的作用范围以及生命周期。 1.普通变量 1.1普通局部变量 定义形式:在复合语句{}里面定义的变量为普通局部变量;作用范围:在复合语句{}里面有效;生命周期&am…...

基于Springboot的在线问卷调查系统【附源码】
基于Springboot的在线问卷调查系统 效果如下: 系统主页面 问卷列表页面 个人中心页面 系统登陆页面 管理员主页面 问卷管理页面 研究背景 随着互联网技术的飞速发展,传统的问卷调查方式因其时间和地点的限制,难以高效地收集到足够的数据。…...

Redis热点数据管理全解析:从MySQL同步到高效缓存的完整解决方案
1. 引言 1.1 背景介绍:MySQL与Redis在高性能场景下的结合 在现代互联网应用中,MySQL作为关系型数据库,承担了大量业务数据的存储任务。然而,随着业务的增长,海量数据的查询性能成为一个瓶颈。为了应对高并发和低延迟…...

【图书介绍】】几本Linux C\C++编程图书
Linux C\C编程,是IT领域比较稳定的职业发展方向,本文介绍几本Linux开发方面的图书。 《Linux C与C一线开发实践(第2版)》 《Linux C与C一线开发实践(第2版)(Linux技术丛书)》(朱文…...

MFC/C++学习系列之简单记录7
MFC/C学习系列之简单记录7 前言句柄的介绍句柄的使用AFX开头的函数都是干什么用的?总结 前言 在MFC的使用中发现了句柄,今天来详细学习一下MFC中如何使用句柄吧! 句柄的介绍 句柄的使用是资源管理和传递的关键机制,通过句柄将系…...

使用GPT进行SCI论文润色常用语句
声明:本文仅作为本人记录学习使用。 You are now a professional academic touch-up specialist. Please polish the English draft I am sending you next. After analyzing the paragraph, give suggestions for polishing in terms of sentence structure, gram…...

Redis密码设置与访问限制(网络安全)
现在用redis缓存热数据越来越常见了,甚至一些配置,开关等等的东西也写到redis里。原因就是redis简单高效。redis里的数据也越来越重要了,例如一些业务的中间数据会暂时存放在redis里,所以限制redis的访问还是很有必要。 本文通过…...

php的线程安全与非线程安全版本的区别
PHP的线程安全(Thread Safe,简称TS)与非线程安全(Non-Thread Safe,简称NTS)版本主要在多线程环境下的行为特性、性能、以及适用场景上存在差异。以下是两者的详细对比: 一、定义与概念 线程安…...

标贝科技受邀出席2024ADD数据应用场景大会 共议数据要素发展新契机
12月13日,由北京市通州区人民政府主办,通州区经济和信息化局、通州区台湖镇人民政府承办的2024-ADD数据应用场景大会成功举办。标贝科技作为AI数据领域代表企业受邀出席大会,与数据要素创业者、投资人一起走进通州台湖,共话数据要…...

electron-vite打包后图标不生效问题
在electron-builder.yml中,通过icon配置自己的图标,以下是正确代码 win:executableName: 名称icon: build/icon.ico nsis:artifactName: ${name}-${version}.${ext}shortcutName: ${productName}uninstallDisplayName: ${productName}createDesktopShor…...

systemverilog中的unique if
1 基本概念 在 SystemVerilog 中,unique if是一种条件判断结构。它用于检查多个互斥的条件,以确保在给定的情况下只有一个条件分支被执行。这有助于提高代码的可读性和可维护性,同时也能帮助发现潜在的逻辑错误,报错原因有以下两个…...

【MySQL篇】事务的认识以及四大特性
何为事务? 事务(Transaction)是指一组操作的集合,这些操作要么全部执行成功,要么全部不执行。事务通常用于保证数据库的一致性、完整性和可靠性,确保数据的完整性与正确性。 有效避免部分执行࿰…...

Windows 11 安装 Dify 完整指南 非docker环境
# Windows 11 安装 Dify 完整指南## 前置要求- Python 3.11 - Node.js 18 - PostgreSQL 14 - Redis for Windows - Git - Ollama (可选,用于本地模型)## 详细安装步骤### 1. 安装必要软件1. **Python 3.11**- 从 https://www.python.org/downloads/release/python-…...

电子电气架构 --- 什么是EPS?
我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 所谓鸡汤,要么蛊惑你认命,要么怂恿你拼命,但都是回避问题的根源&…...

12寸半导体厂等保安全的设计思路
等级保护(等保)二级和三级的主要区别在于安全要求的严格程度、所需部署的安全措施和设备、以及对安全事件响应和处理的能力。以下是等保二级和三级之间的一些关键区别: 一、 安全要求严格程度: - 等保二级:适用于需要较高安全保护的信息系统,要求能够防范轻微的恶意攻击…...

【Chrome Extension】一、CSDN计时扩展设计
【Chrome Extension】一、CSDN计时扩展设计 重点内容内容脚本 content_scripts 文件目录1、整体目录2、manifest.json3、scripts/content.js4、css/content.css 重点内容 内容脚本 content_scripts 1、manifest.json文件配置 {"manifest_version": 3, # *依赖Chro…...

C语言——数据在内存中的存储
目录 前言 一数据类型 类型归类 二整形在内存中的存储 原反补码 大小端 相关练习题 三浮点数在内存中的储存 浮点数储存规则 前言 只有取学习数据在内存中的存储,我们在以后才能定义好(用好)各种类型的数据! 一数据类型…...

Python(二)str、list、tuple、dict、set
string name abcdefprint(name[0]) #a # 切片:取部分数据 print(name[0:3]) # 取 下标为0,1,2的字符 abc print(name[2:]) # 取 下标为2开始到最后的字符 cdef print(name…...