giflib5.2.2 在Qt与VS C++中实现Gif缩放示例
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;
}