Board logo

标题: gif拼接、解包工具 -- gifx.exe [打印本页]

作者: happy886rr    时间: 2017-9-8 21:11     标题: 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

原创代码:
  1. /*
  2. CONSOLE GIF JOIN TOOL, GIFX.EXE  COPYRIGHT@2017~2019 BY HAPPY, VERSION 1.0
  3. FRI SEP 08 2017 17:28:16 GMT+0800
  4. **************************************************************************
  5. g++ gifx.cpp -lgdiplus -lgdi32 -lole32 -municode -O2 -static REM For MINGW
  6. cl  gifx.cpp /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /MT    REM For VS
  7. **************************************************************************
  8. */
  9. #if !defined(_UNICODE) && !defined(UNICODE)
  10. #define _UNICODE USED
  11. #define  UNICODE USED
  12. #endif
  13. #include <stdio.h>
  14. #include <stdlib.h>
  15. #include <string.h>
  16. #include <windows.h>
  17. #include <tchar.h>
  18. #if !defined(WIN32) && !defined(__WIN32__)
  19. #error Only run on windows system
  20. #endif
  21. #if defined(_MSC_VER)
  22. #include <Gdiplus.h>
  23. #pragma comment(lib, "Gdi32.lib")
  24. #pragma comment(lib, "Gdiplus.lib")
  25. #pragma comment(lib, "Ole32.lib")
  26. #else
  27. #include <gdiplus\gdiplus.h>
  28. #ifndef bool
  29. #include <stdbool.h>
  30. #endif
  31. #endif
  32. // 使用GDI+命名空间
  33. using namespace Gdiplus;
  34. // 定义帮助说明
  35. #define HELP_INFORMATION _T("\
  36. Gifx v1.0 - Console gif tool - Copyright (C) 2017-2019 by Happy\n\
  37. Usage:\n\
  38.     gifx [input_file] -c[cut_area]        -s[image_size]\n\
  39.                       -i[interval_number] -j[jump_number]\n\
  40.                       -t[transparent_color]\n\
  41.                       -m[mode_flag]\n\
  42.                       -o[output_file]\n\
  43. \n\
  44. General options:\n\
  45.     -c[[x],[y],[width],[height]]  Cut area\n\
  46.     -s[[width],[height]]  Image size\n\
  47.     -i[interval]  The number of intervals\n\
  48.     -j[skip]  Skip the number of sheets\n\
  49.     -o[path]  Output file\n\
  50.     -t[[R],[G],[B]]  Transparent color\n\
  51.     -m[l|v|x]  Set the gif processing mode\n\
  52.     -h  Show help information\n\
  53. \n\
  54. Official website:\n\
  55.       http://www.bathome.net/thread-16666-6-6.html\n\
  56. ")
  57. // 开关解析宏名
  58. #define _OPT_TEOF -1
  59. #define _OPT_TILL -2
  60. #define _OPT_TERR -3
  61. // 开关解析变量
  62. int OPTIND=1, OPTOPT, UNOPTIND=-1;
  63. TCHAR* OPTARG;
  64. #if defined(_UNICODE) || defined(UNICODE)
  65. #define TCHARFORMAT WCHAR
  66. #else
  67. #define TCHARFORMAT CHAR
  68. #endif
  69. // 判断小写字母宏TCHAR版本
  70. #define ISLOWERLETTER(x) ((_T('a') <= (x)) && ((x) <= _T('z')))
  71. // 图片格式关键词
  72. static const TCHAR* IMAGE_TYPE[] = {_T("image/bmp"), _T("image/jpeg"), _T("image/gif"), _T("image/tiff"), _T("image/png"), NULL};
  73. static const TCHAR* IMAGE_SUFFIX[] = {_T(".bmp"), _T(".jpg"), _T(".gif"), _T(".tif"), _T(".png"), NULL};
  74. // 引入WINAPI
  75. extern "C" {
  76. WINBASEAPI
  77. HWND
  78. APIENTRY
  79. GetConsoleWindow(
  80.     VOID
  81. );
  82. }
  83. // 开关解析模块
  84. int _tgetopt(int nargc, TCHAR* nargv[], TCHAR* ostr)
  85. {
  86. static TCHAR* place = (TCHAR*)_T("");
  87. static TCHAR* lastostr = NULL;
  88. register TCHAR* oli;
  89. if(ostr != lastostr)
  90. {
  91. lastostr = ostr;
  92. place=(TCHAR*)_T("");
  93. }
  94. if(!*place)
  95. {
  96. if
  97. (
  98.     (OPTIND >= nargc)                          ||
  99.     (*(place=nargv[OPTIND]) != (TCHAR)_T('-')) ||
  100.     (!*(++place))
  101. )
  102. {
  103. if(*place != (TCHAR)_T('-') && OPTIND <nargc)
  104. {
  105. place = (TCHAR*)_T("");
  106. if(UNOPTIND == -1)
  107. {
  108. UNOPTIND = OPTIND++;
  109. return _OPT_TILL;
  110. }
  111. else
  112. {
  113. return _OPT_TERR;
  114. }
  115. }
  116. place = (TCHAR*)_T("");
  117. return _OPT_TEOF;
  118. }
  119. if (*place == (TCHAR)_T('-') && *(place+1) == (TCHAR)_T('\0'))
  120. {
  121. ++OPTIND;
  122. return _OPT_TEOF;
  123. }
  124. }
  125. if (
  126.     (OPTOPT=*place++) == (TCHAR)_T(':') ||
  127.     !(oli=(TCHAR*)_tcschr((TCHARFORMAT*)ostr, (TCHAR)OPTOPT))
  128. )
  129. {
  130. if(!*place)
  131. {
  132. ++OPTIND;
  133. }
  134. }
  135. if ((oli != NULL) && (*(++oli) != (TCHAR)_T(':')))
  136. {
  137. OPTARG=NULL;
  138. if(!*place)
  139. {
  140. ++OPTIND;
  141. }
  142. }
  143. else
  144. {
  145. if((*place != _T('\0')))
  146. {
  147. OPTARG = place;
  148. }
  149. else if(nargc <= ++OPTIND)
  150. {
  151. place = (TCHAR*)_T("");
  152. }
  153. else
  154. {
  155. OPTARG = nargv[OPTIND];
  156. }
  157. place = (TCHAR*)_T("");
  158. ++OPTIND;
  159. }
  160. if(*OPTARG == _T('-')){
  161. OPTARG = NULL;
  162. }
  163. return OPTOPT;
  164. }
  165. // 通用关键词识别函数
  166. int IdentifyKey(TCHAR* inStr, TCHAR** inKeyWords)
  167. {
  168. if (inStr == NULL)
  169. {
  170. return -1;
  171. }
  172. int SN = 0;
  173. while(inKeyWords[SN] != NULL)
  174. {
  175. TCHAR *op=inStr, *kp=inKeyWords[SN];
  176. while(*kp != _T('\0'))
  177. {
  178. if(
  179.     ((ISLOWERLETTER(*op))?(*op-32):(*op)) != ((ISLOWERLETTER(*kp))?(*kp-32):(*kp))
  180. )
  181. {
  182. break;
  183. }
  184. op++;
  185. kp++;
  186. }
  187. if(*kp == _T('\0'))
  188. {
  189. if(*op == _T('\0'))
  190. {
  191. return SN;
  192. }
  193. }
  194. SN ++;
  195. }
  196. return -1;
  197. }
  198. // 获取编码器CLSID
  199. BOOL GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
  200. {
  201. UINT j, n=0, s=0;
  202. ImageCodecInfo* pInfo=NULL;
  203. GetImageEncodersSize(&n, &s);
  204. if(s == 0)
  205. {
  206. return FALSE;
  207. }
  208. pInfo=(ImageCodecInfo*)(malloc(s));
  209. if(pInfo == NULL)
  210. {
  211. return FALSE;
  212. }
  213. GetImageEncoders(n, s, pInfo);
  214. for(j=0; j<n; j++)
  215. {
  216. if(wcscmp(pInfo[j].MimeType, format) == 0)
  217. {
  218. *pClsid = pInfo[j].Clsid;
  219. free(pInfo);
  220. return TRUE;
  221. }
  222. }
  223. free(pInfo);
  224. return FALSE;
  225. }
  226. // gif拼接函数
  227. 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)
  228. {
  229. // 备份输出文件名
  230. TCHAR outFileS[MAX_PATH];
  231. _tcscpy(outFileS, outFile);
  232. // 识别扩展名
  233. TCHAR* outFileSuffix = _tcsrchr(outFile, _T('.'));
  234. int SN = IdentifyKey(outFileSuffix, (TCHAR**)IMAGE_SUFFIX);
  235. if(SN == -1)
  236. {
  237. _ftprintf(stderr, _T("Error output image suffix '%s'\n"), outFileSuffix);
  238. exit(1);
  239. }
  240. // 切分扩展名
  241. *(outFileSuffix++) = _T('\0');
  242. // 初始化GdiPlus
  243. ULONG_PTR gdipludToken;
  244. GdiplusStartupInput gdiplusInput;
  245. GdiplusStartup(&gdipludToken,&gdiplusInput,NULL);
  246. // 读取gif文件数据
  247. Image* gifImage = Image::FromFile((WCHAR*)inFile);
  248. // 获取gif原始宽高
  249. UINT gifWidth = gifImage->GetWidth();
  250. UINT gifHeight = gifImage->GetHeight();
  251. // 判断文件读取失败
  252. if((gifWidth & gifHeight) == 0)
  253. {
  254. _ftprintf(stderr, _T("Read gif file failed\n"));
  255. exit(1);
  256. }
  257. // 修正传递的参数
  258. if(cut_x < 0 || cut_x > gifWidth)
  259. {
  260. cut_x = 0;
  261. }
  262. if(cut_y < 0 || cut_y > gifHeight)
  263. {
  264. cut_y = 0;
  265. }
  266. if(cutWidth <= 0 || cutWidth >= gifWidth)
  267. {
  268. cutWidth = gifWidth;
  269. }
  270. if(cutHeight <= 0 || cutHeight >= gifHeight)
  271. {
  272. cutHeight = gifHeight;
  273. }
  274. if(showWidth <= 0)
  275. {
  276. showWidth = gifWidth;
  277. }
  278. if(showHeight <= 0)
  279. {
  280. showHeight = gifHeight;
  281. }
  282. UINT count = gifImage->GetFrameDimensionsCount();
  283. GUID* pDimensionIDs = (GUID*)new GUID[count];
  284. gifImage->GetFrameDimensionsList(pDimensionIDs,count);
  285. WCHAR strGuid[39];
  286. StringFromGUID2(pDimensionIDs[0], strGuid,39);
  287. // 获取gif帧数
  288. UINT FPSCount = gifImage->GetFrameCount(&pDimensionIDs[0]);
  289. delete []pDimensionIDs;
  290. // 如果gif仅含单帧,则直接退出
  291. if(FPSCount < 1)
  292. {
  293. _ftprintf(stderr, _T("The gif file has no FPS\n"));
  294. exit(1);
  295. }
  296. int size = gifImage->GetPropertyItemSize(PropertyTagFrameDelay);
  297. PropertyItem* pItem = (PropertyItem*)malloc(size);
  298. gifImage->GetPropertyItem(PropertyTagFrameDelay,size, pItem);
  299. GUID Guid = FrameDimensionTime;
  300. UINT FPSIndex = ((0 < skipNumber) && (skipNumber < FPSCount-1)) ?skipNumber :0;
  301. intervalNumber = (intervalNumber < 2) ?1 :intervalNumber;
  302. // 获取拼接位图宽高
  303. int joinWdith, joinHeight;
  304. if(modeFlag == 0)
  305. {
  306. joinWdith = (int)((FPSCount - FPSIndex) / intervalNumber +0.5f) * showWidth;
  307. joinHeight = showHeight;
  308. }
  309. else if(modeFlag == 1)
  310. {
  311. joinWdith = showWidth;
  312. joinHeight = (int)((FPSCount - FPSIndex) / intervalNumber +0.5f) * showHeight;
  313. }
  314. else if(modeFlag == 2)
  315. {
  316. joinWdith = showWidth;
  317. joinHeight = showHeight;
  318. }
  319. // 创建拼接位图
  320. Bitmap* pJoinGifMap = new Bitmap(joinWdith, joinHeight, PixelFormat32bppARGB);
  321. Graphics* pGraphics = new Graphics(pJoinGifMap);
  322. // 设置背景色透明
  323. Color bkColor(0,0,0,0);
  324. pGraphics->Clear(bkColor);
  325. // 设置ImageAttributes颜色属性
  326. ImageAttributes* pAttributes = new ImageAttributes();
  327. // 设置透明颜色高低色位
  328. if(useTransparentColor)
  329. {
  330. pAttributes->SetColorKey(transparentColor, transparentColor, ColorAdjustTypeBitmap);
  331. }
  332. /*
  333. // 创建颜色矩阵
  334. ColorMatrix colorMatrix = {
  335. 0.3f,     0.3f,     0.3f,     0.0f,     0.0f,
  336. 0.59f,    0.59f,    0.59f,    0.0f,     0.0f,
  337. 0.11f,    0.11f,    0.11f,    0.0f,     0.0f,
  338. 0.0f,     0.0f,     0.0f,     0.3f,     0.0f,
  339. 0.0f,     0.0f,     0.0f,     0.0f,     1.0f
  340. };
  341. pAttributes->SetColorMatrix(&colorMatrix);
  342. // 为ColorAdjustTypeBitmap设置颜色阈值
  343. pAttributes->SetThreshold(0.7f, ColorAdjustTypeBitmap);
  344. */
  345. // 设置每帧输出尺寸
  346. Rect* pRect = new Rect(0, 0, showWidth, showHeight);
  347. // 获取编码器ID
  348. CLSID clsid;
  349. if(! GetEncoderClsid((WCHAR*)IMAGE_TYPE[SN], &clsid))
  350. {
  351. _ftprintf(stderr, _T("Get the CLSID failed\n"));
  352. exit(1);
  353. }
  354. // 循环获取帧
  355. int countNumber = 0;
  356. while(true)
  357. {
  358. if(modeFlag == 0)
  359. {
  360. pRect->X = countNumber * showWidth;
  361. }
  362. else if(modeFlag == 1)
  363. {
  364. pRect->Y = countNumber * showHeight;
  365. }
  366. else if(modeFlag == 2)
  367. {
  368. pGraphics->Clear(bkColor);
  369. _stprintf(outFileS, _T("%s%02d.%s"), outFile, countNumber+1, outFileSuffix);
  370. }
  371. // 拼接帧位图
  372. pGraphics->DrawImage(gifImage, *pRect, cut_x, cut_y, cutWidth, cutHeight, UnitPixel, pAttributes);
  373. // 保存拼接图
  374. if(pJoinGifMap->Save((WCHAR*)outFileS, &clsid, NULL) != Gdiplus::Ok)
  375. {
  376. _ftprintf(stderr, _T("Save the image: '%s' failed\n"), outFileS);
  377. exit(1);
  378. }
  379. gifImage->SelectActiveFrame(&Guid, FPSIndex);
  380. // 累加帧间隔
  381. FPSIndex += intervalNumber;
  382. countNumber ++;
  383. // 判断帧越界
  384. if (FPSIndex >= FPSCount)
  385. {
  386. break;
  387. }
  388. }
  389. //清理绘图工具
  390. DeleteObject(pGraphics);
  391. delete gifImage;
  392. delete pJoinGifMap;
  393. delete pAttributes;
  394. // 关闭Gdiplus
  395. GdiplusShutdown(gdipludToken);
  396. return 0;
  397. }
  398. // 主函数入口
  399. int _tmain(int argc, TCHAR** argv)
  400. {
  401. if(argc<2)
  402. {
  403. // 无参数则退出
  404. _ftprintf(stdout, HELP_INFORMATION);
  405. return 0;
  406. }
  407. // 初始化传递参数
  408. int modeFlag = 0;
  409. int cut_rect[4] = {0};
  410. int image_size[2] = {0};
  411. int intervalNumber = 0, skipNumber = 0;
  412. // 启用透明色过滤
  413. bool useTransparentColor = false;
  414. byte tRGB[3] = {0};
  415. // IO文件
  416. TCHAR *inFile=NULL, *outFile=NULL;
  417. // 开关解析
  418. int K = _OPT_TEOF;
  419. while((K=_tgetopt(argc, argv, (TCHAR*)_T("c:s:i:j:o:t:m:hC:S:I:J:O:T:M:H"))) != _OPT_TEOF)
  420. {
  421. switch(K)
  422. {
  423. case _T('c'):
  424. case _T('C'):
  425. if(OPTARG == NULL)
  426. {
  427. _ftprintf(stderr, _T("The switch '-c' needs a cut area\n"));
  428. exit(1);
  429. }
  430. {
  431. TCHAR* pTcstr = _tcstok(OPTARG, _T(","));
  432. for(int i=0; i<4 && pTcstr; i++)
  433. {
  434. cut_rect[i] = _ttoi((TCHARFORMAT*)pTcstr);
  435. pTcstr = _tcstok(NULL, _T(","));
  436. }
  437. }
  438. break;
  439. case _T('s'):
  440. case _T('S'):
  441. if(OPTARG == NULL)
  442. {
  443. _ftprintf(stderr, _T("The switch '-s' needs image width and height\n"));
  444. exit(1);
  445. }
  446. {
  447. TCHAR* pTcstr = _tcstok(OPTARG, _T(","));
  448. for(int i=0; i<2 && pTcstr; i++)
  449. {
  450. image_size[i] = _ttoi((TCHARFORMAT*)pTcstr);
  451. pTcstr = _tcstok(NULL, _T(","));
  452. }
  453. }
  454. break;
  455. case _T('i'):
  456. case _T('I'):
  457. if(OPTARG == NULL)
  458. {
  459. _ftprintf(stderr, _T("The switch '-i' needs a interval number\n"));
  460. exit(1);
  461. }
  462. {
  463. intervalNumber = _ttoi((TCHARFORMAT*)OPTARG);
  464. }
  465. break;
  466. case _T('j'):
  467. case _T('J'):
  468. if(OPTARG == NULL)
  469. {
  470. _ftprintf(stderr, _T("The switch '-j' needs a skip number\n"));
  471. exit(1);
  472. }
  473. {
  474. skipNumber = _ttoi((TCHARFORMAT*)OPTARG);
  475. }
  476. break;
  477. case _T('o'):
  478. case _T('O'):
  479. if(OPTARG != NULL)
  480. {
  481. outFile = OPTARG;
  482. }
  483. break;
  484. case _T('t'):
  485. case _T('T'):
  486. if(OPTARG == NULL)
  487. {
  488. _ftprintf(stderr, _T("The switch '-t' needs a transparent RGB color like [R,G,B]\n"));
  489. exit(1);
  490. }
  491. {
  492. TCHAR* pTcstr = _tcstok(OPTARG, _T(","));
  493. for(int i=0; i<3 && pTcstr; i++)
  494. {
  495. tRGB[i] = (byte)_ttoi((TCHARFORMAT*)pTcstr);
  496. pTcstr = _tcstok(NULL, _T(","));
  497. }
  498. useTransparentColor = true;
  499. }
  500. break;
  501. case _T('m'):
  502. case _T('M'):
  503. if(OPTARG != NULL)
  504. {
  505. switch(*((TCHARFORMAT*)OPTARG))
  506. {
  507. case _T('v'):
  508. case _T('V'):
  509. modeFlag = 0;
  510. break;
  511. case _T('l'):
  512. case _T('L'):
  513. modeFlag = 1;
  514. break;
  515. case _T('x'):
  516. case _T('X'):
  517. modeFlag = 2;
  518. break;
  519. default:
  520. break;
  521. }
  522. if(*((TCHARFORMAT*)OPTARG + 1) != _T('\0'))
  523. {
  524. _ftprintf(stderr, _T("Unknown gif mode '-m%c'\n"), *((TCHARFORMAT*)OPTARG));
  525. exit(1);
  526. }
  527. }
  528. break;
  529. case _T('h'):
  530. case _T('H'):
  531. _ftprintf(stdout, HELP_INFORMATION);
  532. return 0;
  533. case _OPT_TILL:
  534. // 第一个无选项的参数识别为输入文件
  535. inFile = argv[UNOPTIND];
  536. break;
  537. case _OPT_TERR:
  538. _ftprintf(stderr, _T("Extra parameters \"%s\"\n"), argv[OPTIND]);
  539. exit(1);
  540. default:
  541. _ftprintf(stderr, _T("Unknown switch '-%c'\n"), K);
  542. exit(1);
  543. }
  544. }
  545. if(inFile == NULL)
  546. {
  547. _ftprintf(stderr, _T("Needs input file\n"));
  548. exit(1);
  549. }
  550. if(outFile == NULL)
  551. {
  552. _ftprintf(stderr, _T("Needs output file\n"));
  553. exit(1);
  554. }
  555. // 调用gif拼接手函数
  556. 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);
  557. }
复制代码

作者: 523066680    时间: 2017-9-8 22:52

厉害




欢迎光临 批处理之家 (http://bbs.bathome.net/) Powered by Discuz! 7.2