分享
 
 
 

用lex将xpm文件转换成为自定义的OpenGL使用的纹理文件格式

王朝other·作者佚名  2006-06-26
窄屏简体版  字體: |||超大  

在进行游戏编程的过程中经常会遇到图像文件的读取需求,但是如果直接使用其它的

图像库,要么没有源代码,要么非常难以使用,要么就会出现版权纠纷。实际上经常的情

况就是仅仅只是需要一种纹理数据,至于具体的文件格式根本就没有需要,完全可以自定

义一种文件格式,使得读取尽可能的简洁明了。听起来似乎很难,其实不然,本人在多年

的游戏开发过程中总结出来了一种简洁明了的方法,因为主要是用于OpenGL程序的,所以

我将其命名为glt格式,也就是OpenGL的Texture格式。本文给出了XPM文件转GLT文件的转

换程序。

这个转换程序采用了Lex来书写扫描器,最主要的原因就是GNU的lex和bison产生的代

码可以直接进行商用而不必付出任何的费用。另外一个重要原因就是用lex书写的代码,本

身就是一种文档。因此本文也直接采用GNU的flex了。

实际上我写这个文档的目的如下:

(1)推荐lex的使用

(2)解释自定义的纹理文件格式和相关读取和写入程序

(3)用代码来直接表达设计信息,实际上就是用尽量少的文字来表达尽可能多的信息

可以从网络上直接下载UnixCmd包在Windows下面安装,实际上安装过程就是简单的解

压缩过程,只不过需要设置路径指向该解压缩目录,如果要使用bison那么还需要设置两个

环境变量BISON_HAIRY和BISON_SIMPLE分别指向bison.hairy和bison.simple文件。

xpm2glt.l %{

// 这个程序主要实现了XPM格式的图像文件转换成OpenGL可以直接使用的数组格式的功能

#include <iostream>

#include <fstream>

#include <string>

#include <vector>

#include <iterator>

#include <map>

#include "glt.hpp"

#include "glt.cpp"

std::string g_InputFileName;// 输入的文件名

std::string g_InputFileNameHeader;// 输入的文件名(不包括扩展名)

std::string g_Buffer;// 用来缓存读取的字符串信息

std::string g_ArrayName;//xpm文件采用的数组名称

unsigned int g_Info[4];//xpm文件的信息:图像宽、高、索引宽度以及索引表长度

typedef std::map<std::string,std::string> COLORDICT;

typedef std::vector<std::string> IMAGETABLE;

COLORDICT g_ColorDict;// 颜色词典

IMAGETABLE g_ImageTable;// 图像表

texture_t g_texture;// OpenGL使用的纹理文件类,用来读取和写入图像数据

%}

%x STRING CNOTE

D [0-9]

L [A-Za-z]

X {D}|[A-Fa-f]

S " "

WS [ \t\n]

array {L}({L}|{D})+"_xpm"

info {D}+{S}{D}+{S}{D}+{S}{D}+

palettecolor .+\tc{S}({L}|{D})+

rgbcolor .+\tc{S}#{X}{X}{X}{X}{X}{X}

%%

\" {BEGIN STRING;g_Buffer.clear();}

<STRING>{S} {g_Buffer.append(" ");}

<STRING>\\n {g_Buffer.append("\n");}

<STRING>\\t {g_Buffer.append("\t");}

<STRING>\\\" {g_Buffer.append("\"");}

<STRING>\" {

BEGIN INITIAL;

if(!g_Buffer.empty())//必须是图像索引表才进行处理

{

for(size_t i=0;i<g_Info[0];++i)

{

std::string idx = g_Buffer.substr(i*g_Info[3],g_Info[3]);

g_ImageTable.push_back(idx);

}

}

}

<STRING>\n std::cerr<<"字符串错误"<<std::endl;

<STRING>{info}\" {

unput('\"');

sscanf(yytext,"%d %d %d %d",&g_Info[0],&g_Info[1],&g_Info[2],&g_Info[3]);

}

<STRING>{palettecolor} {

//std::string idx(&yytext[0],g_Info[3]);

std::string clr(&yytext[g_Info[3]+3],yyleng-g_Info[3]-3);

//g_ColorDict.insert(std::make_pair(idx,clr));

std::cerr<< "不允许使用调色版模式的颜色:["+clr+"]" << std::endl;

exit(1);

}

<STRING>{rgbcolor} {

std::string idx(&yytext[0],g_Info[3]);

std::string clr(&yytext[g_Info[3]+3],yyleng-g_Info[3]-3);

// 需要将#FFFFFF颜色换成三个字节表示的16进制格式0xFF,0xFF,0xFF

std::string R("0x"+clr.substr(1,2));

std::string G("0x"+clr.substr(3,2));

std::string B("0x"+clr.substr(5,2));

g_ColorDict.insert(std::make_pair(idx,R+","+G+","+B));

}

<STRING>. {g_Buffer.append(yytext);}

\/\* {BEGIN CNOTE;}

<CNOTE>.|\n ;

<CNOTE>\*\/ {BEGIN INITIAL;}

{array} {g_ArrayName.append(yytext,yyleng);}

.|\n ;

%%

int yywrap()

{

return 1;

}

int main(int argc,const char*argv[])

{

if(argc==1){//从管道获取数据

g_InputFileName = "Texture";

}else if(argc==2){//从输入文件获取数据

g_InputFileNameHeader = g_InputFileName = argv[1];

g_InputFileNameHeader.erase(g_InputFileNameHeader.rfind('.'));

extern FILE*yyin;

yyin = fopen(argv[1],"r");

}else{

std::cerr<<"该程序必须从XPM文件或者管道输入数据!"<<std::endl;

std::cerr<<"eg:"<<std::endl;

std::cerr<<"\txpm2glt < File.xpm"<<std::endl;

std::cerr<<"\txpm2glt File.xpm"<<std::endl;

exit(0);

}

yylex();// lex产生的词法扫描程序

std::clog <<"xpm文件的宽:["<<g_Info[0]<<"]"<<std::endl;

std::clog <<"xpm文件的高:["<<g_Info[1]<<"]"<<std::endl;

std::clog <<"xpm颜色数量:["<<g_Info[2]<<"]"<<std::endl;

std::clog <<"xpm索引宽度:["<<g_Info[3]<<"]"<<std::endl;

std::clog<<g_ColorDict.size()<<","<<g_ImageTable.size()<<std::endl;

g_texture.width = g_Info[0];

g_texture.height = g_Info[1];

g_texture.depth = 3 ;// 目前只处理RGB格式的图像数据

for(size_t i=0;i<g_Info[1];++i)

{

for(size_t j=0;j<g_Info[0];++j)

{

std::string tmp = g_ColorDict[g_ImageTable[i*g_Info[0]+j]];

unsigned int R,G,B;

sscanf(tmp.c_str(),"%x,%x,%x",&R,&G,&B);

g_texture.data.push_back(R);

g_texture.data.push_back(G);

g_texture.data.push_back(B);

}

}

std::ofstream binary((g_InputFileNameHeader+".glt.bin").c_str(),std::ios_base::binary);

g_texture.write(binary);// 写成二进制格式的纹理文件

std::ofstream text((g_InputFileNameHeader+".glt").c_str());

text << g_texture ;// 写成文本格式的纹理文件

std::clog<<"文件["<<argv[1]<<"]转换成功!"<<std::endl;

return 0;

}

glt.hpp

#ifndef GLT_HPP

#define GLT_HPP

#include <vector>

#include <iostream>

struct texture_t

{

unsigned int width;// 图像宽度

unsigned int height;// 图像高度

unsigned int depth;// 图像深度

std::vector<unsigned char> data;// 图像数据

void read (std::istream&s);// 读取二进制格式的纹理文件

void write(std::ostream&s);// 写出二进制格式的纹理文件

void clear();// 清理内存中所有的图象数据

void horizontal_flip(); // 垂直翻转

// 读取文本格式的纹理文件

friend std::istream&operator>>(std::istream&s,texture_t&o);

// 写出文本格式的纹理文件

friend std::ostream&operator<<(std::ostream&s,const texture_t&o);

};

std::istream&operator>>(std::istream&s,texture_t&o);

std::ostream&operator<<(std::ostream&s,const texture_t&o);

#endif//GLT_HPP

glt.cpp

#include "glt.hpp"

#include <algorithm>

void texture_t::read (std::istream&s)

{

s.read(reinterpret_cast<char*>(&width),sizeof(unsigned int));

s.read(reinterpret_cast<char*>(&height),sizeof(unsigned int));

s.read(reinterpret_cast<char*>(&depth),sizeof(unsigned int));

unsigned int size = 0;

s.read(reinterpret_cast<char*>(&size),sizeof(unsigned int));

data.resize(size);

s.read(reinterpret_cast<char*>(&data[0]),size*sizeof(unsigned char));

}

void texture_t::write(std::ostream&s)

{

s.write(reinterpret_cast<const char*>(&width),sizeof(unsigned int));

s.write(reinterpret_cast<const char*>(&height),sizeof(unsigned int));

s.write(reinterpret_cast<const char*>(&depth),sizeof(unsigned int));

unsigned int size = data.size();

s.write(reinterpret_cast<const char*>(&size),sizeof(unsigned int));

s.write(reinterpret_cast<const char*>(&data[0]),size*sizeof(unsigned char));

}

void texture_t::clear()

{

data.clear();

}

void texture_t::horizontal_flip()

{

if(width*height == 0) return;

for(unsigned int i=0;i<height/2;++i)

{

std::swap_ranges(&data[(i*width)*3+0],&data[(i*width+width)*3-1],&data[(height-i-1)*width*3+0]);

}

}

std::istream&operator>>(std::istream&s,texture_t&o)

{

s >> o.width >> o.height >> o.depth ;

std::copy(std::istream_iterator<unsigned char>(s),std::istream_iterator<unsigned char>(),std::back_inserter(o.data));

return s;

}

std::ostream&operator<<(std::ostream&s,const texture_t&o)

{

s << o.width << "\t" << o.height << "\t" << o.depth << std::endl;

std::copy(o.data.begin(),o.data.end(),std::ostream_iterator<unsigned int>(s,"\t"));

return s;

}

Makefile

LEX=flex

YACC=bison

CXX=g++

CXXFLAGS=

xpm2glt.exe:xpm2glt.l

$(LEX) xpm2glt.l

$(CXX) $(CXXFLAGS) lex.yy.c -o $@

clean:

rm *.c *.h xpm2glt.exe

sample.xpm

/* XPM */

static char * sample1_xpm[] = {

"32 32 6 1",

" c #000000",

". c #000000",

"+ c #000080",

"@ c #FFFFFF",

"# c #FFFF00",

"$ c #FF0000",

" ",

" ",

" ",

" ",

" ",

" .............. ",

" .++++++++++++. ",

" .++++++++++++. ",

" .+@@+++++++++. ",

" .+@@+++++++++. ",

" .+@@+++++++++. ",

" .+@@++++++..............",

" .+@@++++++.############.",

".........+@@++++++.############.",

".$.+@@++++++.#@@#########.",

".$.+@@++++++.#@@#########.",

".$@@$.+++++++++.#@@#########.",

".$@@$.+++++++++.#@@#########.",

".$@@$...........#@@#########.",

".$@@$. .#@@#########.",

".$@@$. .#@@#########.",

".$@@$. .#@@#########.",

".$@@$. .############.",

".$@@$. .############.",

".$. ..............",

".$. ",

".............. ",

" ",

" ",

" ",

" ",

" "};

将上面的转换程序xpm2glt.exe编译成功之后,就可以直接使用了,下面给出使用方法

xpm2glt.exe sample.xpm

在OpenGL程序中使用glt的texture_t类的方法如下:

std::ifstream in("sample.glt.bin",std::ios_base::binary);

texture_t t; t.read(in); t.horizontal_flip();

glBindTexture(GL_TEXTURE_2D,TEXTURE1);

gluBuild2DMipmaps(GL_TEXTURE_2D,t.depth,t.width,t.height,GL_RGB,GL_UNSIGNED_BYTE,&t.data[0]);

从上面的xpm2glt.l代码中可以看见用lex来书写词法扫描程序确实非常容易,在代码

中就可以记录设计信息,因此代码就是文档,现在还不是很成熟的方案,但是已经向这个

方面迈出了一大步了:)

关于xpm文件的来源,可以使用GIMP图像处理软件直接生成,这样就将格式转换的过程

交给了GIMP而不是游戏设计者自己来实现繁琐的图像文件管理,这样就可以尽可能的利用

已有的工具来实现自己的目标。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有