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;
}
相关推荐
qiu_zhongya2 小时前
iree 用C++来运行Qwen 2.5 0.5b
开发语言·c++·人工智能
啊?啊?2 小时前
C/C++练手小项目之倒计时与下载进度条模拟
c语言·开发语言·c++
求一个demo2 小时前
Qt5.14.2配置MSVC2017
开发语言·qt
西阳未落2 小时前
C++基础(22)——模板的进阶
开发语言·c++
waves浪游2 小时前
C++模板进阶
开发语言·c++
青草地溪水旁3 小时前
设计模式(C++)详解——迭代器模式(1)
c++·设计模式·迭代器模式
你的电影很有趣3 小时前
lesson68:JavaScript 操作 HTML 元素、属性与样式全指南
开发语言·前端·javascript
青草地溪水旁3 小时前
设计模式(C++)详解——迭代器模式(2)
java·c++·设计模式·迭代器模式
SamsongSSS3 小时前
《C++ Primer Plus》读书笔记 第二章 开始学习C++
c++·后端