当前位置: 首页 > news >正文

使用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语言)-包括扩展块的处理

我看到的所有例程&#xff0c;都把扩展部分的处理跳过了&#xff0c;而我的动画是有透明度的&#xff0c;这就导致解码后的图像在有透明色的像素部分&#xff0c;呈现了很多的黑点&#xff0c;或者闪白的情况出现。经过调试&#xff0c;终于成功。 文件格式 先了解一下GIF的文…...

blazor实现ASP.NET网站用户批量注册方法

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

SpringCloud 入门(4)—— 网关

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

什么是WebAssembly?怎么使用?

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

v3s点RGB屏 40pin 800x480,不一样的点屏,不通过chosen。

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

某科技局国产服务器PVE虚拟化技术文档

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

中科岩创边坡自动化监测解决方案

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

GPT-O3:简单介绍

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

cudnn版本gpu架构

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

数据库安全-redisCouchdb

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

ubuntu22.04安装PaddleX3

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

Flutter 实现全局悬浮按钮学习

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

14-C语言多文件编程

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

基于Springboot的在线问卷调查系统【附源码】

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

Redis热点数据管理全解析:从MySQL同步到高效缓存的完整解决方案

1. 引言 1.1 背景介绍&#xff1a;MySQL与Redis在高性能场景下的结合 在现代互联网应用中&#xff0c;MySQL作为关系型数据库&#xff0c;承担了大量业务数据的存储任务。然而&#xff0c;随着业务的增长&#xff0c;海量数据的查询性能成为一个瓶颈。为了应对高并发和低延迟…...

【图书介绍】】几本Linux C\C++编程图书

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

MFC/C++学习系列之简单记录7

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

使用GPT进行SCI论文润色常用语句

声明&#xff1a;本文仅作为本人记录学习使用。 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缓存热数据越来越常见了&#xff0c;甚至一些配置&#xff0c;开关等等的东西也写到redis里。原因就是redis简单高效。redis里的数据也越来越重要了&#xff0c;例如一些业务的中间数据会暂时存放在redis里&#xff0c;所以限制redis的访问还是很有必要。 本文通过…...

php的线程安全与非线程安全版本的区别

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

BMS81M001超低功耗震动唤醒模块技术解析

1. BMS81M001 唤醒式震动检测模块深度技术解析1.1 模块定位与工程价值BMS81M001 是由 BEST MODULES CORP 推出的专用低功耗唤醒型震动检测模块&#xff0c;其核心设计目标是解决嵌入式系统中“持续监听机械扰动”带来的能耗瓶颈问题。在工业状态监测、智能穿戴设备、防盗报警系…...

广州邮科选型指南:挑选可调电源必须关注的四个核心参数

在电子工程师的工作台上&#xff0c;有一种设备兼具了灵活性与智能保护——它就是可调稳压恒流开关电源。这种电源不仅是供电工具&#xff0c;更像是懂得自我保护的"智能能源管家"。它如何同时实现稳压与恒流&#xff1f;传统电源往往只能固定输出&#xff0c;而这类…...

Blazor WebAssembly AOT编译踩坑实录(含.NET 9 RTM正式版12类崩溃场景+符号映射调试秘钥)

第一章&#xff1a;Blazor WebAssembly AOT编译的核心价值与2026演进定位Blazor WebAssembly 的 AOT&#xff08;Ahead-of-Time&#xff09;编译自 .NET 6 起引入&#xff0c;并在 .NET 7/8 中持续优化&#xff0c;其核心价值在于将 C# 代码直接编译为高度优化的 WebAssembly 二…...

【Charles 抓包工具笔记】(自用复盘版)

&#x1f4e1; Charles 抓包工具笔记&#xff08;自用复盘版&#xff09;适用场景&#xff1a;HTTP/HTTPS 抓包分析、接口调试、弱网测试、移动端测试 最后更新&#xff1a;2026-04-09一、Charles 简介 1.1 基本信息 Charles&#xff1a;中文名“青花瓷”&#xff0c;是一款基于…...

AI全自动解析复杂工程图纸与防造假质检知识库实战

工程结构的物理坍塌&#xff0c;往往始于底层数据范式的崩塌。 在近年来的多起重大桥梁垮塌事故&#xff08;如黄河某公路大桥局部坍塌事件&#xff09;的事后调查中&#xff0c;一个非常残酷的“文档黑洞”反复暴露在调查报告中&#xff1a;工程图纸的版本错乱、施工材料的质…...

别再数据线了!用FastAPI 分钟搭个局域网文件+剪贴板神器俚

为 HagiCode 添加 GitHub Pages 自动部署支持 本项目早期代号为 PCode&#xff0c;现已正式更名为 HagiCode。本文记录了如何为项目引入自动化静态站点部署能力&#xff0c;让内容发布像喝水一样简单。 背景/引言 在 HagiCode 的开发过程中&#xff0c;我们遇到了一个很现实的问…...

案例分析:学术文献综述 Agent Harness

案例分析&#xff1a;学术文献综述 Agent Harness——从手动“文献堆沙”到智能“知识城堡”的AI构建器关键词&#xff1a;学术文献综述 Agent、Agent Harness、多智能体协作、大语言模型应用、学术自动化、知识图谱构建、文献检索-筛选-总结流水线摘要&#xff1a;本文以Chatb…...

RPC数据集整理与 Scalabel 标注说明

RPC数据集整理与 Scalabel 标注说明 1. RPC 数据集简介 RPC&#xff08;Retail Product Checkout Dataset&#xff09;是一个面向自动结算&#xff08;ACO, Automatic Checkout&#xff09;场景的大规模零售商品数据集&#xff0c;核心目标是从结算台图像中自动识别商品并生成购…...

Labview与汇川PLC通讯:官方协议下的报文读取与配置辅助,安全稳定的命令帧读写程序源码

Labview 与汇川AM400 AM600 AM800 AC800 PLC 通讯 官方协议&#xff0c;报文读取&#xff0c;安全稳定。 通讯配置&#xff0c;辅助测试。 无程序网络通讯实现。 常用功能一网打尽。 1.命令帧读写。 2.支持 I16 I32 Float 批量读写。 3.支持字符串读写。 4.支持Bool批量读写。 …...

番茄小说下载器:5种格式批量下载与Web界面管理完全指南

番茄小说下载器&#xff1a;5种格式批量下载与Web界面管理完全指南 【免费下载链接】fanqienovel-downloader 下载番茄小说 项目地址: https://gitcode.com/gh_mirrors/fa/fanqienovel-downloader 番茄小说下载器是一个功能强大的开源工具&#xff0c;专为小说爱好者和技…...