返回列表 发帖

gif拼接、解包工具 -- gifx.exe

[gifx.exe] -- version 1.0 (批处理 游戏制作必备)
Windows平台下的gif拼接、解包、格式转换、透明、裁剪工具。建议使用vs编译源码以获得不依赖dll的独立版本。对于不方便编译的坛友,请直接下载7KB附件。
链接: https://pan.baidu.com/s/1D7qKZB_8MWKkpOgWz4CM4Q?pwd=t8m3
(外部链接效果图)

GIFX.EXE (CONSOLE GIF JOIN TOOL, COPYRIGHT@2017~2019 BY HAPPY, VERSION 1.0)
摘要:
=========================================================================
GIFX.EXE,是一款奇异的命令行gif拼接、解包利器。 能将gif动态图的每一帧拼接
为一整张静态图,亦能将gif文件解包。同时也支持其他图片格式的转换、裁剪、透
明色处理等操作。
=========================================================================

版本:
VERSION 1.0

用法:
-------------------------------------------------------------------------
gifx   [输入文件]
    -o [输出文件]
    -c [[裁剪区域X坐标],[裁剪区域Y坐标],[裁剪区域宽度],[裁剪区域高度]]
    -s [[显示宽度],[显示高度]]
    -i [帧间隔]
    -j [跳过的帧]
    -t [[R],[G],[B]]
    -m [l|v|x]
-------------------------------------------------------------------------

示例:
-------------------------------------------------------------------------
REM 解包test.gif并设白色为透明色,输出为png格式
gifx test.gif -o.png -t255,255,255 -mx

REM 解包test.gif并拼接每一帧为一整张纵向图,输出为jpg格式
gifx test.gif -o.\out.jpg -t255,255,255 -mv

REM 解包test.gif每帧为宽100、高300的位图并拼接每一帧为一整张横向图,输出为bmp格式
gifx test.gif -o.\out.jpg -s100,300 -ml   

REM 图片格式转换
gifx test.png -o.\test.jpg

REM 图片裁剪
gifx test.png -c20,30,100,300 -s100,600 -o.\out.png

REM 设置某色为透明色,颜色为RGB格式,中间用英文逗号隔开
gifx test.png -t255,255,255 -o.\out.png

...
-------------------------------------------------------------------------

英译:
-------------------------------------------------------------------------
Gifx v1.0 - Console gif tool - Copyright (C) 2017-2019 by Happy
Usage:
    gifx [input_file] -c[cut_area]        -s[image_size]
                      -i[interval_number] -j[jump_number]
                      -t[transparent_color]
                      -m[mode_flag]
                      -o[output_file]

General options:
    -c[[x],[y],[width],[height]]  Cut area
    -s[[width],[height]]  Image size
    -i[interval]  The number of intervals
    -j[skip]  Skip the number of sheets
    -o[path]  Output file
    -t[[R],[G],[B]]  Transparent color
    -m[l|v|x]  Set the gif processing mode
    -h  Show help information

Official website:
      http://www.bathome.net/thread-16666-6-6.html
-------------------------------------------------------------------------
FRI SEP 08 2017 17:28:16 GMT+0800

原创代码:
/*
CONSOLE GIF JOIN TOOL, GIFX.EXE  COPYRIGHT@2017~2019 BY HAPPY, VERSION 1.0
FRI SEP 08 2017 17:28:16 GMT+0800
**************************************************************************
g++ gifx.cpp -lgdiplus -lgdi32 -lole32 -municode -O2 -static REM For MINGW
cl  gifx.cpp /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /MT    REM For VS
**************************************************************************
*/
#if !defined(_UNICODE) && !defined(UNICODE)
#define _UNICODE USED
#define  UNICODE USED
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <tchar.h>
#if !defined(WIN32) && !defined(__WIN32__)
#error Only run on windows system
#endif
#if defined(_MSC_VER)
#include <Gdiplus.h>
#pragma comment(lib, "Gdi32.lib")
#pragma comment(lib, "Gdiplus.lib")
#pragma comment(lib, "Ole32.lib")
#else
#include <gdiplus\gdiplus.h>
#ifndef bool
#include <stdbool.h>
#endif
#endif
// 使用GDI+命名空间
using namespace Gdiplus;
// 定义帮助说明
#define HELP_INFORMATION _T("\
Gifx v1.0 - Console gif tool - Copyright (C) 2017-2019 by Happy\n\
Usage:\n\
    gifx [input_file] -c[cut_area]        -s[image_size]\n\
                      -i[interval_number] -j[jump_number]\n\
                      -t[transparent_color]\n\
                      -m[mode_flag]\n\
                      -o[output_file]\n\
\n\
General options:\n\
    -c[[x],[y],[width],[height]]  Cut area\n\
    -s[[width],[height]]  Image size\n\
    -i[interval]  The number of intervals\n\
    -j[skip]  Skip the number of sheets\n\
    -o[path]  Output file\n\
    -t[[R],[G],[B]]  Transparent color\n\
    -m[l|v|x]  Set the gif processing mode\n\
    -h  Show help information\n\
\n\
Official website:\n\
      http://www.bathome.net/thread-16666-6-6.html\n\
")
// 开关解析宏名
#define _OPT_TEOF -1
#define _OPT_TILL -2
#define _OPT_TERR -3
// 开关解析变量
int OPTIND=1, OPTOPT, UNOPTIND=-1;
TCHAR* OPTARG;
#if defined(_UNICODE) || defined(UNICODE)
#define TCHARFORMAT WCHAR
#else
#define TCHARFORMAT CHAR
#endif
// 判断小写字母宏TCHAR版本
#define ISLOWERLETTER(x) ((_T('a') <= (x)) && ((x) <= _T('z')))
// 图片格式关键词
static const TCHAR* IMAGE_TYPE[] = {_T("image/bmp"), _T("image/jpeg"), _T("image/gif"), _T("image/tiff"), _T("image/png"), NULL};
static const TCHAR* IMAGE_SUFFIX[] = {_T(".bmp"), _T(".jpg"), _T(".gif"), _T(".tif"), _T(".png"), NULL};
// 引入WINAPI
extern "C" {
WINBASEAPI
HWND
APIENTRY
GetConsoleWindow(
    VOID
);
}
// 开关解析模块
int _tgetopt(int nargc, TCHAR* nargv[], TCHAR* ostr)
{
static TCHAR* place = (TCHAR*)_T("");
static TCHAR* lastostr = NULL;
register TCHAR* oli;
if(ostr != lastostr)
{
lastostr = ostr;
place=(TCHAR*)_T("");
}
if(!*place)
{
if
(
    (OPTIND >= nargc)                          ||
    (*(place=nargv[OPTIND]) != (TCHAR)_T('-')) ||
    (!*(++place))
)
{
if(*place != (TCHAR)_T('-') && OPTIND <nargc)
{
place = (TCHAR*)_T("");
if(UNOPTIND == -1)
{
UNOPTIND = OPTIND++;
return _OPT_TILL;
}
else
{
return _OPT_TERR;
}
}
place = (TCHAR*)_T("");
return _OPT_TEOF;
}
if (*place == (TCHAR)_T('-') && *(place+1) == (TCHAR)_T('\0'))
{
++OPTIND;
return _OPT_TEOF;
}
}
if (
    (OPTOPT=*place++) == (TCHAR)_T(':') ||
    !(oli=(TCHAR*)_tcschr((TCHARFORMAT*)ostr, (TCHAR)OPTOPT))
)
{
if(!*place)
{
++OPTIND;
}
}
if ((oli != NULL) && (*(++oli) != (TCHAR)_T(':')))
{
OPTARG=NULL;
if(!*place)
{
++OPTIND;
}
}
else
{
if((*place != _T('\0')))
{
OPTARG = place;
}
else if(nargc <= ++OPTIND)
{
place = (TCHAR*)_T("");
}
else
{
OPTARG = nargv[OPTIND];
}
place = (TCHAR*)_T("");
++OPTIND;
}
if(*OPTARG == _T('-')){
OPTARG = NULL;
}
return OPTOPT;
}
// 通用关键词识别函数
int IdentifyKey(TCHAR* inStr, TCHAR** inKeyWords)
{
if (inStr == NULL)
{
return -1;
}
int SN = 0;
while(inKeyWords[SN] != NULL)
{
TCHAR *op=inStr, *kp=inKeyWords[SN];
while(*kp != _T('\0'))
{
if(
    ((ISLOWERLETTER(*op))?(*op-32):(*op)) != ((ISLOWERLETTER(*kp))?(*kp-32):(*kp))
)
{
break;
}
op++;
kp++;
}
if(*kp == _T('\0'))
{
if(*op == _T('\0'))
{
return SN;
}
}
SN ++;
}
return -1;
}
// 获取编码器CLSID
BOOL GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
UINT j, n=0, s=0;
ImageCodecInfo* pInfo=NULL;
GetImageEncodersSize(&n, &s);
if(s == 0)
{
return FALSE;
}
pInfo=(ImageCodecInfo*)(malloc(s));
if(pInfo == NULL)
{
return FALSE;
}
GetImageEncoders(n, s, pInfo);
for(j=0; j<n; j++)
{
if(wcscmp(pInfo[j].MimeType, format) == 0)
{
*pClsid = pInfo[j].Clsid;
free(pInfo);
return TRUE;
}
}
free(pInfo);
return FALSE;
}
// gif拼接函数
int JoinGif(TCHAR* inFile, TCHAR* outFile, int modeFlag, bool useTransparentColor, Color transparentColor, int cut_x,  int cut_y, int cutWidth, int cutHeight, int showWidth, int showHeight, int intervalNumber, int skipNumber)
{
// 备份输出文件名
TCHAR outFileS[MAX_PATH];
_tcscpy(outFileS, outFile);
// 识别扩展名
TCHAR* outFileSuffix = _tcsrchr(outFile, _T('.'));
int SN = IdentifyKey(outFileSuffix, (TCHAR**)IMAGE_SUFFIX);
if(SN == -1)
{
_ftprintf(stderr, _T("Error output image suffix '%s'\n"), outFileSuffix);
exit(1);
}
// 切分扩展名
*(outFileSuffix++) = _T('\0');
// 初始化GdiPlus
ULONG_PTR gdipludToken;
GdiplusStartupInput gdiplusInput;
GdiplusStartup(&gdipludToken,&gdiplusInput,NULL);
// 读取gif文件数据
Image* gifImage = Image::FromFile((WCHAR*)inFile);
// 获取gif原始宽高
UINT gifWidth = gifImage->GetWidth();
UINT gifHeight = gifImage->GetHeight();
// 判断文件读取失败
if((gifWidth & gifHeight) == 0)
{
_ftprintf(stderr, _T("Read gif file failed\n"));
exit(1);
}
// 修正传递的参数
if(cut_x < 0 || cut_x > gifWidth)
{
cut_x = 0;
}
if(cut_y < 0 || cut_y > gifHeight)
{
cut_y = 0;
}
if(cutWidth <= 0 || cutWidth >= gifWidth)
{
cutWidth = gifWidth;
}
if(cutHeight <= 0 || cutHeight >= gifHeight)
{
cutHeight = gifHeight;
}
if(showWidth <= 0)
{
showWidth = gifWidth;
}
if(showHeight <= 0)
{
showHeight = gifHeight;
}
UINT count = gifImage->GetFrameDimensionsCount();
GUID* pDimensionIDs = (GUID*)new GUID[count];
gifImage->GetFrameDimensionsList(pDimensionIDs,count);
WCHAR strGuid[39];
StringFromGUID2(pDimensionIDs[0], strGuid,39);
// 获取gif帧数
UINT FPSCount = gifImage->GetFrameCount(&pDimensionIDs[0]);
delete []pDimensionIDs;
// 如果gif仅含单帧,则直接退出
if(FPSCount < 1)
{
_ftprintf(stderr, _T("The gif file has no FPS\n"));
exit(1);
}
int size = gifImage->GetPropertyItemSize(PropertyTagFrameDelay);
PropertyItem* pItem = (PropertyItem*)malloc(size);
gifImage->GetPropertyItem(PropertyTagFrameDelay,size, pItem);
GUID Guid = FrameDimensionTime;
UINT FPSIndex = ((0 < skipNumber) && (skipNumber < FPSCount-1)) ?skipNumber :0;
intervalNumber = (intervalNumber < 2) ?1 :intervalNumber;
// 获取拼接位图宽高
int joinWdith, joinHeight;
if(modeFlag == 0)
{
joinWdith = (int)((FPSCount - FPSIndex) / intervalNumber +0.5f) * showWidth;
joinHeight = showHeight;
}
else if(modeFlag == 1)
{
joinWdith = showWidth;
joinHeight = (int)((FPSCount - FPSIndex) / intervalNumber +0.5f) * showHeight;
}
else if(modeFlag == 2)
{
joinWdith = showWidth;
joinHeight = showHeight;
}
// 创建拼接位图
Bitmap* pJoinGifMap = new Bitmap(joinWdith, joinHeight, PixelFormat32bppARGB);
Graphics* pGraphics = new Graphics(pJoinGifMap);
// 设置背景色透明
Color bkColor(0,0,0,0);
pGraphics->Clear(bkColor);
// 设置ImageAttributes颜色属性
ImageAttributes* pAttributes = new ImageAttributes();
// 设置透明颜色高低色位
if(useTransparentColor)
{
pAttributes->SetColorKey(transparentColor, transparentColor, ColorAdjustTypeBitmap);
}
/*
// 创建颜色矩阵
ColorMatrix colorMatrix = {
0.3f,     0.3f,     0.3f,     0.0f,     0.0f,
0.59f,    0.59f,    0.59f,    0.0f,     0.0f,
0.11f,    0.11f,    0.11f,    0.0f,     0.0f,
0.0f,     0.0f,     0.0f,     0.3f,     0.0f,
0.0f,     0.0f,     0.0f,     0.0f,     1.0f
};
pAttributes->SetColorMatrix(&colorMatrix);
// 为ColorAdjustTypeBitmap设置颜色阈值
pAttributes->SetThreshold(0.7f, ColorAdjustTypeBitmap);
*/
// 设置每帧输出尺寸
Rect* pRect = new Rect(0, 0, showWidth, showHeight);
// 获取编码器ID
CLSID clsid;
if(! GetEncoderClsid((WCHAR*)IMAGE_TYPE[SN], &clsid))
{
_ftprintf(stderr, _T("Get the CLSID failed\n"));
exit(1);
}
// 循环获取帧
int countNumber = 0;
while(true)
{
if(modeFlag == 0)
{
pRect->X = countNumber * showWidth;
}
else if(modeFlag == 1)
{
pRect->Y = countNumber * showHeight;
}
else if(modeFlag == 2)
{
pGraphics->Clear(bkColor);
_stprintf(outFileS, _T("%s%02d.%s"), outFile, countNumber+1, outFileSuffix);
}
// 拼接帧位图
pGraphics->DrawImage(gifImage, *pRect, cut_x, cut_y, cutWidth, cutHeight, UnitPixel, pAttributes);
// 保存拼接图
if(pJoinGifMap->Save((WCHAR*)outFileS, &clsid, NULL) != Gdiplus::Ok)
{
_ftprintf(stderr, _T("Save the image: '%s' failed\n"), outFileS);
exit(1);
}
gifImage->SelectActiveFrame(&Guid, FPSIndex);
// 累加帧间隔
FPSIndex += intervalNumber;
countNumber ++;
// 判断帧越界
if (FPSIndex >= FPSCount)
{
break;
}
}
//清理绘图工具
DeleteObject(pGraphics);
delete gifImage;
delete pJoinGifMap;
delete pAttributes;
// 关闭Gdiplus
GdiplusShutdown(gdipludToken);
return 0;
}
// 主函数入口
int _tmain(int argc, TCHAR** argv)
{
if(argc<2)
{
// 无参数则退出
_ftprintf(stdout, HELP_INFORMATION);
return 0;
}
// 初始化传递参数
int modeFlag = 0;
int cut_rect[4] = {0};
int image_size[2] = {0};
int intervalNumber = 0, skipNumber = 0;
// 启用透明色过滤
bool useTransparentColor = false;
byte tRGB[3] = {0};
// IO文件
TCHAR *inFile=NULL, *outFile=NULL;
// 开关解析
int K = _OPT_TEOF;
while((K=_tgetopt(argc, argv, (TCHAR*)_T("c:s:i:j:o:t:m:hC:S:I:J:O:T:M:H"))) != _OPT_TEOF)
{
switch(K)
{
case _T('c'):
case _T('C'):
if(OPTARG == NULL)
{
_ftprintf(stderr, _T("The switch '-c' needs a cut area\n"));
exit(1);
}
{
TCHAR* pTcstr = _tcstok(OPTARG, _T(","));
for(int i=0; i<4 && pTcstr; i++)
{
cut_rect[i] = _ttoi((TCHARFORMAT*)pTcstr);
pTcstr = _tcstok(NULL, _T(","));
}
}
break;
case _T('s'):
case _T('S'):
if(OPTARG == NULL)
{
_ftprintf(stderr, _T("The switch '-s' needs image width and height\n"));
exit(1);
}
{
TCHAR* pTcstr = _tcstok(OPTARG, _T(","));
for(int i=0; i<2 && pTcstr; i++)
{
image_size[i] = _ttoi((TCHARFORMAT*)pTcstr);
pTcstr = _tcstok(NULL, _T(","));
}
}
break;
case _T('i'):
case _T('I'):
if(OPTARG == NULL)
{
_ftprintf(stderr, _T("The switch '-i' needs a interval number\n"));
exit(1);
}
{
intervalNumber = _ttoi((TCHARFORMAT*)OPTARG);
}
break;
case _T('j'):
case _T('J'):
if(OPTARG == NULL)
{
_ftprintf(stderr, _T("The switch '-j' needs a skip number\n"));
exit(1);
}
{
skipNumber = _ttoi((TCHARFORMAT*)OPTARG);
}
break;
case _T('o'):
case _T('O'):
if(OPTARG != NULL)
{
outFile = OPTARG;
}
break;
case _T('t'):
case _T('T'):
if(OPTARG == NULL)
{
_ftprintf(stderr, _T("The switch '-t' needs a transparent RGB color like [R,G,B]\n"));
exit(1);
}
{
TCHAR* pTcstr = _tcstok(OPTARG, _T(","));
for(int i=0; i<3 && pTcstr; i++)
{
tRGB[i] = (byte)_ttoi((TCHARFORMAT*)pTcstr);
pTcstr = _tcstok(NULL, _T(","));
}
useTransparentColor = true;
}
break;
case _T('m'):
case _T('M'):
if(OPTARG != NULL)
{
switch(*((TCHARFORMAT*)OPTARG))
{
case _T('v'):
case _T('V'):
modeFlag = 0;
break;
case _T('l'):
case _T('L'):
modeFlag = 1;
break;
case _T('x'):
case _T('X'):
modeFlag = 2;
break;
default:
break;
}
if(*((TCHARFORMAT*)OPTARG + 1) != _T('\0'))
{
_ftprintf(stderr, _T("Unknown gif mode '-m%c'\n"), *((TCHARFORMAT*)OPTARG));
exit(1);
}
}
break;
case _T('h'):
case _T('H'):
_ftprintf(stdout, HELP_INFORMATION);
return 0;
case _OPT_TILL:
// 第一个无选项的参数识别为输入文件
inFile = argv[UNOPTIND];
break;
case _OPT_TERR:
_ftprintf(stderr, _T("Extra parameters \"%s\"\n"), argv[OPTIND]);
exit(1);
default:
_ftprintf(stderr, _T("Unknown switch '-%c'\n"), K);
exit(1);
}
}
if(inFile == NULL)
{
_ftprintf(stderr, _T("Needs input file\n"));
exit(1);
}
if(outFile == NULL)
{
_ftprintf(stderr, _T("Needs output file\n"));
exit(1);
}
// 调用gif拼接手函数
return JoinGif(inFile, outFile, modeFlag, useTransparentColor, Color(tRGB[0],tRGB[1],tRGB[2]), cut_rect[0], cut_rect[1], cut_rect[2], cut_rect[3], image_size[0], image_size[1], intervalNumber, skipNumber);
}COPY
1

评分人数

厉害
[url=][/url]

TOP

返回列表