您的位置:首页 > 其它

批量拆分图片透明通道

2015-07-05 22:17 183 查看
  最近项目中遇到图片内存占用太大的问题。一种思路是将一张带有透明通道的图片拆分成两张,一张只包含RGB三个通道,另外一张只记录原图的A通道信息(可以用RGB任意通道),这样在Unity中经过压缩后的两幅图的大小也远比原来的带透明的图片小。于是自己实现了个cpp版的批量处理工具,话说好久不写cpp了写起来好难过啊。

  这次用到的库是FreeImage。FreeImage是个轻量级的图像处理库,可以很方便地在像素级别处理图片。

#include <iostream>
#include <io.h>
#include <string>
#include <stdio.h>
#include <map>
#include "FreeImage.h"
#pragma comment(lib, "FreeImage.lib")
using namespace std;

//验证路径扩展名是否是png
bool ValidFilePath(string path)
{
    string::size_type dotPos = path.find_last_of('.');
    if (dotPos == string::npos)
        return false;
    string fileExtra = path.substr(dotPos);
    if (0 == fileExtra.compare(".png"))
        return true;
    return false;
}

//验证图片是否有透明
bool ValidTexture(FIBITMAP *texture)
{
    int width = FreeImage_GetWidth(texture);
    int height = FreeImage_GetHeight(texture);
    int bytespp = FreeImage_GetLine(texture) / width;
    if (bytespp != 4)
        return false;
    bool valid = false;
    FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(texture);
    for (int y = 0; y < height; y++)
    {
        BYTE *bits = FreeImage_GetScanLine(texture, y);
        for (int x = 0; x < width; x++)
        {
            if (bits[FI_RGBA_ALPHA] < 255)  //有透明度小于255的像素
            {
                valid = true;
            }
            bits += bytespp;
        }
    }
    return valid;
}
//生成新的文件名
string ChangeFileName(string path, string extra)
{
    string newName = path;
    string::size_type dotPos = path.find_last_of('.');
    if (dotPos == string::npos)
        return "";
    newName.insert(dotPos, extra);
    return newName;
}

string ChangeFilePath(string path, string path2)
{
    string::size_type linePos = path.find_last_of('\\');
    if (linePos == string::npos)
        return "";
    string fileName = path.substr(linePos);
    return path2.append(fileName);
}

bool Convert(string path, string dest)
{
    if (path == "")
        return false;
    FreeImage_Initialise(TRUE);

    FREE_IMAGE_FORMAT fif = FreeImage_GetFileType(path.c_str());

    if (FIF_UNKNOWN == fif)
    {
        fif = FreeImage_GetFIFFromFilename(path.c_str());
    }

    if (FIF_UNKNOWN == fif)
    {
        cout << "File Not Support :" << path << endl;
        return false;
    }

    FIBITMAP* sourceTexture = NULL;

    if (FreeImage_FIFSupportsReading(fif))
    {
        sourceTexture = FreeImage_Load(fif, path.c_str());
    }

    if (sourceTexture == NULL)
        return false;
    if (!ValidTexture(sourceTexture))
        return false;
    int width = FreeImage_GetWidth(sourceTexture);
    int height = FreeImage_GetHeight(sourceTexture);
    int bytespp = FreeImage_GetLine(sourceTexture) / width;

    //为生成的两幅图分配内存
    FIBITMAP *rgbTexture = FreeImage_Clone(sourceTexture);
    FIBITMAP *aTexture = FreeImage_Clone(sourceTexture);

    for (int y = 0; y < height; y++)
    {
        //逐行扫描图片
        BYTE *sourceBits = FreeImage_GetScanLine(sourceTexture, y);
        BYTE *rgbBits = FreeImage_GetScanLine(rgbTexture, y);
        BYTE *aBits = FreeImage_GetScanLine(aTexture, y);
        for (int x = 0; x < width; x++)
        {
            rgbBits[FI_RGBA_RED] = sourceBits[FI_RGBA_RED];
            rgbBits[FI_RGBA_GREEN] = sourceBits[FI_RGBA_GREEN];
            rgbBits[FI_RGBA_BLUE] = sourceBits[FI_RGBA_BLUE];
            rgbBits[FI_RGBA_ALPHA] = 255;

            aBits[FI_RGBA_RED] = sourceBits[FI_RGBA_ALPHA];
            aBits[FI_RGBA_GREEN] = sourceBits[FI_RGBA_ALPHA];
            aBits[FI_RGBA_BLUE] = sourceBits[FI_RGBA_ALPHA];
            aBits[FI_RGBA_ALPHA] = 255;

            //移动指针到下一个像素
            sourceBits += bytespp;
            rgbBits += bytespp;
            aBits += bytespp;
        }
    }
    path = ChangeFilePath(path, dest);
    if (false == FreeImage_Save(FIF_PNG, rgbTexture, ChangeFileName(path, "_RGB").c_str(), PNG_DEFAULT))
    {
        cout << "save RGB image error\n" << endl;
    }

    if (false == FreeImage_Save(FIF_PNG, aTexture, ChangeFileName(path, "_A").c_str(), PNG_DEFAULT))
    {
        cout << "save A image error\n" << endl;
    }
    //释放句柄
    FreeImage_Unload(sourceTexture);
    FreeImage_Unload(rgbTexture);
    FreeImage_Unload(aTexture);
    cout << "转换完成" << path << endl;
    return true;
}

void ConvertAll(string path, string dest)
{
    map<string, string> paths;
    //递归遍历文件夹
    _finddatai64_t file;
    intptr_t hFile;
    string curPath = path;
    curPath.append("\\*.*");
    hFile = _findfirst64(curPath.c_str(), &file);
    if (-1 == hFile)
    {
        cout << "Find First Failed."<<endl;
        return;
    }

    do
    {
        if (1 == strlen(file.name) && file.name[0] == '.' || 
            (strlen(file.name) == 2 && file.name[0] == '.' && file.name[1] == '.'))
        {
            //是当前目录或者上级目录
            continue;
        }
        string filePath = path;
        filePath.append("\\").append(file.name);
        if (file.attrib & _A_SUBDIR)
        {
            //是文件夹,递归转换
            ConvertAll(filePath, dest);
        }
        else
        {
            if (ValidFilePath(filePath))
            {
                //paths.insert(filePath, dest);
                if (paths.find(filePath) == paths.end())
                {
                    paths[filePath] = dest;
                }
            }
        }
    } while (_findnext64(hFile, &file) == 0);
    _findclose(hFile);

    for (auto iter = paths.begin(); iter != paths.end(); ++iter)
    {
        Convert(iter->first, iter->second);
    }
}

int main(int argc, char** argv)
{
    if (argc != 3)
    {
        cout << "命令行参数不对!" << endl;
        return -1;
    }
    cout << "开始处理所有图片,只能处理.png的图片" << endl;
    cout << argv[1] << " " << argv[2] << endl;
    ConvertAll(argv[1], argv[2]);
    cout << "所有图片处理完成" << endl;
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: