giflib5.2.2 在Qt与VS C++中实现Gif缩放示例
giflib5.2.2 在Qt与VS C++中实现Gif缩放示例
giflib下载地址
# QT中的用法:
DEFINES += _CRT_SECURE_NO_WARNINGS
DEFINES += _CRT_NONSTDC_NO_WARNINGSINCLUDEPATH += $$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
#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
#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;// 解析 GCEfor (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;
}