当前位置: 首页 > 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; 一、定义与概念 线程安…...

web vue 项目 Docker化部署

Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段&#xff1a; 构建阶段&#xff08;Build Stage&#xff09;&#xff1a…...

k8s从入门到放弃之Ingress七层负载

k8s从入门到放弃之Ingress七层负载 在Kubernetes&#xff08;简称K8s&#xff09;中&#xff0c;Ingress是一个API对象&#xff0c;它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress&#xff0c;你可…...

c++ 面试题(1)-----深度优先搜索(DFS)实现

操作系统&#xff1a;ubuntu22.04 IDE:Visual Studio Code 编程语言&#xff1a;C11 题目描述 地上有一个 m 行 n 列的方格&#xff0c;从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子&#xff0c;但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...

linux 错误码总结

1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

04-初识css

一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...

【笔记】WSL 中 Rust 安装与测试完整记录

#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统&#xff1a;Ubuntu 24.04 LTS (WSL2)架构&#xff1a;x86_64 (GNU/Linux)Rust 版本&#xff1a;rustc 1.87.0 (2025-05-09)Cargo 版本&#xff1a;cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...

ubuntu22.04 安装docker 和docker-compose

首先你要确保没有docker环境或者使用命令删掉docker sudo apt-get remove docker docker-engine docker.io containerd runc安装docker 更新软件环境 sudo apt update sudo apt upgrade下载docker依赖和GPG 密钥 # 依赖 apt-get install ca-certificates curl gnupg lsb-rel…...

C++_哈希表

本篇文章是对C学习的哈希表部分的学习分享 相信一定会对你有所帮助~ 那咱们废话不多说&#xff0c;直接开始吧&#xff01; 一、基础概念 1. 哈希核心思想&#xff1a; 哈希函数的作用&#xff1a;通过此函数建立一个Key与存储位置之间的映射关系。理想目标&#xff1a;实现…...

js 设置3秒后执行

如何在JavaScript中延迟3秒执行操作 在JavaScript中&#xff0c;要设置一个操作在指定延迟后&#xff08;例如3秒&#xff09;执行&#xff0c;可以使用 setTimeout 函数。setTimeout 是JavaScript的核心计时器方法&#xff0c;它接受两个参数&#xff1a; 要执行的函数&…...