批处理之家's Archiver

happy886rr 发表于 2017-9-8 21:11

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

[i=s] 本帖最后由 happy886rr 于 2017-9-8 21:22 编辑 [/i]

[gifx.exe] -- version 1.0 (批处理 游戏制作必备)
Windows平台下的gif拼接、解包、格式转换、透明、裁剪工具。建议使用vs编译源码以获得不依赖dll的独立版本。对于不方便编译的坛友,请直接下载7KB附件。[attach]10851[/attach]
(外部链接效果图)
[img]http://i4.bvimg.com/604745/308cb9a24a44e00d.jpg[/img]
[quote]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:
      [url]http://www.bathome.net/thread-16666-6-6.html[/url]
-------------------------------------------------------------------------
FRI SEP 08 2017 17:28:16 GMT+0800
[/quote]
原创代码:[code]/*
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);
}
[/code]

523066680 发表于 2017-9-8 22:52

厉害

页: [1]

Powered by Discuz! Archiver 7.2  © 2001-2009 Comsenz Inc.