giflib5.2.2 在Qt与VS C++中实现Gif缩放示例

giflib5.2.2 在Qt与VS C++中实现Gif缩放示例

giflib下载地址

cpp 复制代码
# QT中的用法:
DEFINES += _CRT_SECURE_NO_WARNINGS
DEFINES += _CRT_NONSTDC_NO_WARNINGS

INCLUDEPATH += $$PWD/giflib-5.2.2/

SOURCES += \
    $$PWD/giflib-5.2.2/dgif_lib.c \
    $$PWD/giflib-5.2.2/egif_lib.c \
    $$PWD/giflib-5.2.2/gif_err.c \
    $$PWD/giflib-5.2.2/gif_hash.c \
    $$PWD/giflib-5.2.2/gifalloc.c \
    $$PWD/giflib-5.2.2/openbsd-reallocarray.c \

GifScaler.h

cpp 复制代码
#pragma once

#include <QByteArray>
#include <QBuffer>
#include <QMovie>
#include <QImage>
#include <QImageWriter>
#include <QPainter>
#include <QFile>
#include <QString>
#include <Windows.h>

// GIF Disposal Method 定义 (来自 GIF89a 规范)
// 指定该帧显示完毕后,画布的处理方式
#define DISPOSAL_UNSPECIFIED 0  // 未指定,通常等同于 DISPOSAL_NONE
#define DISPOSAL_NONE        1  // 不清除,直接保留画布
#define DISPOSAL_BACKGROUND  2  // 恢复为背景色
#define DISPOSAL_PREVIOUS    3  // 恢复到上一帧的画布内容

bool scaleGif(const QString& inputFile, const QString& outputFile, int newWidth, int newHeight);

GifScaler.cpp

cpp 复制代码
#include "GifScaler.h"
#include <vector>
#include <iostream>
#include <cstring>
#include <cmath>
#include <fcntl.h>
#include <io.h>

extern "C" {
    #include "gif_lib.h"
}

// 最近邻缩放整幅图像
static std::vector<GifByteType> scaleFrame(
    const std::vector<GifByteType>& src,
    int width, int height,
    int newW, int newH)
{
    std::vector<GifByteType> dst(newW * newH);
    for (int y = 0; y < newH; y++) {
        int sy = y * height / newH;
        for (int x = 0; x < newW; x++) {
            int sx = x * width / newW;
            dst[y * newW + x] = src[sy * width + sx];
        }
    }
    return dst;
}

// 将一帧合成到全局画布
static void blendFrame(std::vector<GifByteType>& canvas, int canvasW, int canvasH,
    const GifByteType* raster, int left, int top, int width, int height,
    int transparentIndex)
{
    for (int y = 0; y < height; y++) {
        int dstY = top + y;
        if (dstY < 0 || dstY >= canvasH) continue;
        for (int x = 0; x < width; x++) {
            int dstX = left + x;
            if (dstX < 0 || dstX >= canvasW) continue;
            GifByteType c = raster[y * width + x];
            if (c == transparentIndex) continue; // 跳过透明像素
            canvas[dstY * canvasW + dstX] = c;
        }
    }
}

bool scaleGif(const QString& inputFile, const QString& outputFile, int newWidth, int newHeight)
{
    HANDLE hIn = CreateFileW(inputFile.toStdWString().c_str(), GENERIC_READ,
        FILE_SHARE_READ, nullptr, OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL, nullptr);
    if (hIn == INVALID_HANDLE_VALUE) return false;

    int fdIn = _open_osfhandle(reinterpret_cast<intptr_t>(hIn), _O_RDONLY | _O_BINARY);
    if (fdIn == -1) { CloseHandle(hIn); return false; }

    int err = 0;
    GifFileType* gifIn = DGifOpenFileHandle(fdIn, &err);
    if (!gifIn) { CloseHandle(hIn); return false; }

    if (DGifSlurp(gifIn) == GIF_ERROR) {
        DGifCloseFile(gifIn, &err);
        return false;
    }

    if (newWidth == gifIn->SWidth && newHeight == gifIn->SHeight) {
        DGifCloseFile(gifIn, &err);
        if (QFile::exists(outputFile)) QFile::remove(outputFile);
        return QFile::copy(inputFile, outputFile);
    }

    HANDLE hOut = CreateFileW(outputFile.toStdWString().c_str(), GENERIC_WRITE,
        0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
    if (hOut == INVALID_HANDLE_VALUE) {
        DGifCloseFile(gifIn, &err);
        return false;
    }

    int fdOut = _open_osfhandle(reinterpret_cast<intptr_t>(hOut), _O_WRONLY | _O_BINARY);
    if (fdOut == -1) { DGifCloseFile(gifIn, &err); CloseHandle(hOut); return false; }

    GifFileType* gifOut = EGifOpenFileHandle(fdOut, &err);
    if (!gifOut) { DGifCloseFile(gifIn, &err); CloseHandle(hOut); return false; }

    ColorMapObject* colorMap = gifIn->SColorMap;
    if (!colorMap && gifIn->ImageCount > 0)
        colorMap = gifIn->SavedImages[0].ImageDesc.ColorMap;

    if (EGifPutScreenDesc(gifOut, newWidth, newHeight,
        colorMap ? colorMap->BitsPerPixel : 8, 0, colorMap) == GIF_ERROR) {
        DGifCloseFile(gifIn, &err);
        EGifCloseFile(gifOut, &err);
        return false;
    }

    // 全局画布(原始大小)
    std::vector<GifByteType> canvas(gifIn->SWidth * gifIn->SHeight, gifIn->SBackGroundColor);
    std::vector<GifByteType> canvasBackup; // 用于 disposal=3 的快照

    for (int i = 0; i < gifIn->ImageCount; i++) {
        SavedImage& frame = gifIn->SavedImages[i];

        int transparentIndex = -1;
        int disposalMethod = 0;

        // 解析 GCE
        for (int j = 0; j < frame.ExtensionBlockCount; j++) {
            ExtensionBlock& ext = frame.ExtensionBlocks[j];
            if (ext.Function == GRAPHICS_EXT_FUNC_CODE && ext.ByteCount >= 4) {
                disposalMethod = (ext.Bytes[0] >> 2) & 7;
                if (ext.Bytes[0] & 0x01) {
                    transparentIndex = (unsigned char)ext.Bytes[3];
                }
                EGifPutExtension(gifOut, ext.Function, ext.ByteCount, ext.Bytes);
            }
        }

        // 在合成前保存快照 (用于 disposal=3)
        canvasBackup = canvas;

        // 合成当前帧到画布
        blendFrame(canvas, gifIn->SWidth, gifIn->SHeight,
            frame.RasterBits,
            frame.ImageDesc.Left, frame.ImageDesc.Top,
            frame.ImageDesc.Width, frame.ImageDesc.Height,
            transparentIndex);

        // 缩放整个画布
        std::vector<GifByteType> scaled = scaleFrame(canvas, gifIn->SWidth, gifIn->SHeight, newWidth, newHeight);

        // 写出全屏帧
        if (EGifPutImageDesc(gifOut, 0, 0, newWidth, newHeight, false, frame.ImageDesc.ColorMap) == GIF_ERROR) {
            DGifCloseFile(gifIn, &err);
            EGifCloseFile(gifOut, &err);
            return false;
        }

        for (int y = 0; y < newHeight; y++) {
            if (EGifPutLine(gifOut, &scaled[y * newWidth], newWidth) == GIF_ERROR) {
                DGifCloseFile(gifIn, &err);
                EGifCloseFile(gifOut, &err);
                return false;
            }
        }

        // 处理处置方法
        if (disposalMethod == 2) {
            if (i < gifIn->ImageCount - 1) {
                std::fill(canvas.begin(), canvas.end(), gifIn->SBackGroundColor);
            }
        }
        else if (disposalMethod == 3) {
            // 恢复到上一帧前的画布
            canvas.swap(canvasBackup);
        }
        // disposal=0/1 表示保留画布,无需清除
    }

    DGifCloseFile(gifIn, &err);
    EGifCloseFile(gifOut, &err);
    return true;
}
相关推荐
小天源4 分钟前
Error 1053 Error 1067 服务“启动后立即停止” Java / Python 程序无法后台运行 windows nssm注册器下载与报错处理
开发语言·windows·python·nssm·error 1053·error 1067
肉包_51136 分钟前
两个数据库互锁,用全局变量互锁会偶发软件卡死
开发语言·数据库·c++
大空大地202642 分钟前
流程控制语句--if语句
开发语言
毕设源码-邱学长1 小时前
【开题答辩全过程】以 基于PHP的发热病人管理平台的设计与实现为例,包含答辩的问题和答案
开发语言·php
Trouvaille ~1 小时前
【Linux】UDP Socket编程实战(一):Echo Server从零到一
linux·运维·服务器·网络·c++·websocket·udp
HellowAmy2 小时前
我的C++规范 - 线程池
开发语言·c++·代码规范
独自破碎E2 小时前
【BISHI9】田忌赛马
android·java·开发语言
czy87874752 小时前
const 在 C/C++ 中的全面用法(C/C++ 差异+核心场景+实战示例)
c语言·开发语言·c++
十五年专注C++开发2 小时前
MinHook:Windows 平台下轻量级、高性能的钩子库
c++·windows·钩子技术·minhook
范纹杉想快点毕业2 小时前
实战级ZYNQ中断状态机FIFO设计
java·开发语言·驱动开发·设计模式·架构·mfc