libpng使用例子

本例使用libpng,将一系列单个的序列帧png文件合并成一个大的png, 去除无用的空白,计算每个系列的最小有效范围。

请看源码吧,libpng用法有不明白的,可以参考一下,有抄到的到方请保留版权

 

 

//@author aerror 
//2011/12/02
#include "stdafx.h"
#include <Windows.h>
#include <stdio.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <tchar.h>
#include <vector>
#include <string>
#include <list>


extern "C"{
#include "png.h"
}


	
#define EnsureVectorSize(v,ds)  {if(v.size()<(unsigned int)ds){ v.resize(ds);}}

typedef struct _RECTANGLE
{
	int top;
	int left;
	int bottom;
	int right;
}TRECTANGLE;



class PNG_SRC_FILE
{
public:
	std::string filename;
	png_bytep   bmp;
	unsigned int rawWidth  ;
	unsigned int rawHeight  ;
	PNG_SRC_FILE(const char * src)
	{
		filename =src;
		bmp = NULL;
	}
	virtual ~PNG_SRC_FILE()
	{
		if(bmp!=NULL)
		{
			delete []bmp;
		}
	}

};

typedef std::list<  PNG_SRC_FILE *> TPNG_SRC_FILE_LIST;
typedef std::list<   PNG_SRC_FILE *>::iterator TPNG_SRC_FILE_LIST_ITER;

bool filename_sort(PNG_SRC_FILE * x1,PNG_SRC_FILE * x2)
{
	return x1->filename < x2->filename;
}

typedef struct _tanimation_info
{

	const char *actionName;
	const char *actionDir;
	unsigned rowHeight;
	unsigned frameWidth;
	unsigned frameHeight;
	unsigned totalFrame;
	unsigned fps;
	TPNG_SRC_FILE_LIST  * filelist;
	unsigned int minTop     ;
	unsigned int maxBottom  ;
	unsigned int minLeft    ;
	unsigned int maxRight   ;

}TANIMATION_INFO;

	std::vector<BYTE>				s_srcDibBitsBuffer;
	std::vector<BYTE>				s_dstDibBitsBuffer;
	std::vector<BYTE>				s_dstPngRowsBuffer;
	std::vector<BYTE>				s_dstPngTempBuffer;
	
	

	int outputToPngFileNormal(const char *szFileName,unsigned int numDibBtis,const unsigned char *pDibBytes,unsigned int _width, unsigned int _height)
	{
		FILE *fp;
		png_structp png_ptr;
		png_infop info_ptr;
	

		/* open the file */
		fp = fopen(szFileName, "wb");
		if (fp == NULL)
		  return (ERROR);

		png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,NULL, NULL, NULL);

		if (png_ptr == NULL)
		{
		  fclose(fp);
		  return (ERROR);
		}

		/* Allocate/initialize the image information data.  REQUIRED */
		info_ptr = png_create_info_struct(png_ptr);
		if (info_ptr == NULL)
		{
		  fclose(fp);
		  png_destroy_write_struct(&png_ptr,  png_infopp_NULL);
		  return (ERROR);
		}

		/* Set error handling.  REQUIRED if you aren't supplying your own
		* error handling functions in the png_create_write_struct() call.
		*/
		if (setjmp(png_jmpbuf(png_ptr)))
		{
		  /* If we get here, we had a problem reading the file */
		  fclose(fp);
		  png_destroy_write_struct(&png_ptr, &info_ptr);
		  return (ERROR);
		}

		png_init_io(png_ptr, fp);


		/* Set the image information here.  Width and height are up to 2^31,
		* bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
		* the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
		* PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
		* or PNG_COLOR_TYPE_RGB_ALPHA.  interlace is either PNG_INTERLACE_NONE or
		* PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
		* currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
		*/
		

		
		int src_bytes_per_pixel	= numDibBtis/_width/_height;	
		png_uint_32 k, height, width;

		height = _height;	
		width = _width;


		png_set_IHDR(png_ptr, info_ptr, width, height, 8, 
		   PNG_COLOR_TYPE_RGB_ALPHA,
		  PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);

		png_text text_ptr[2]={0,0};

		char szDetail[255]="pvr";
		/*sprintf(szDetail,"type=%d method=%d size=%dx%d lefttop=%d,%d frame=%dx%d\n",
						pInfo->type,
						pInfo->method,
						(int)(pInfo->width),
						(int)(pInfo->height), 
						(int)(pInfo->left),
						(int)(pInfo->top),
						(int)(pInfo->frmWidth),
						(int)(pInfo->frmHeith) );
*/
		text_ptr[0].key = "Author";
		text_ptr[0].text = "dany21cn@21cn.com";
		text_ptr[0].compression = PNG_TEXT_COMPRESSION_NONE;
		text_ptr[1].key = "Description";
		text_ptr[1].text = szDetail;
		text_ptr[1].compression = PNG_TEXT_COMPRESSION_NONE;
		png_set_text(png_ptr, info_ptr, text_ptr, 2);


		/* Write the file header information.  REQUIRED */
		png_write_info(png_ptr, info_ptr);

		if (height > PNG_UINT_32_MAX/png_sizeof(png_bytep))
		 png_error (png_ptr, "Image is too tall to process in memory");


		png_bytep image =  NULL;
		png_bytep imagerow =  NULL;
		
		
		EnsureVectorSize(s_dstPngRowsBuffer,width*4);
		imagerow =	&s_dstPngRowsBuffer[0];
		
		
		

		for(int y=0;y<_height;y++)
		{
			for(int x=0;x<_width;x++)
			{
				int dx = x;//+pInfo->left;
				int dy = y;//+pInfo->top;

				if(dx>width || dy>height)
				{
					printf("invalid offset operation\n");
					return ERROR;
				}

				const unsigned char* psrc = pDibBytes + y*_width*src_bytes_per_pixel+ x*src_bytes_per_pixel;
				png_bytep pdst = NULL;
				
				pdst = imagerow  +dx*4;
				
				if(src_bytes_per_pixel==4)
				{
					/*
					pdst[0] = psrc[2];
					pdst[1] = psrc[1];
					pdst[2] = psrc[0];
					pdst[3] = psrc[3];
					*/

					pdst[0] = psrc[0];
					pdst[1] = psrc[1];
					pdst[2] = psrc[2];
					pdst[3] = psrc[3];
				}
			
			}
			
			png_write_row(png_ptr,imagerow);

		}
		
		
		/* You can write optional chunks like tEXt, zTXt, and tIME at the end
		* as well.  Shouldn't be necessary in 1.1.0 and up as all the public
		* chunks are supported and you can use png_set_unknown_chunks() to
		* register unknown chunks into the info structure to be written out.
		*/

		/* It is REQUIRED to call this to finish writing the rest of the file */
		png_write_end(png_ptr, info_ptr);

		/* clean up after the write, and free any memory allocated */
		png_destroy_write_struct(&png_ptr, &info_ptr);

		fclose(fp);

		return 0;
	}


TRECTANGLE cutTransparent(png_bytep bmp, unsigned int width, unsigned int height )
{
	TRECTANGLE  t={0,0,height,width};

	//row by row from top
	//
	for(int y=0;y<height;y++)
	{
		bool thisLineEmpty = true;
		for(int x=0;x<width;x++)
		{
			unsigned char* psrc = bmp + y*width*4+ x*4;
			unsigned  int v =  *(unsigned int* )psrc;
			if(v &0xFF000000)
			{
				thisLineEmpty = false;
				break;
			}
		}

		if(thisLineEmpty)
		{
			t.top ++ ;
		}
		else
		{
			break;
		}
	}

	for(int y=height-1;y>=0;y--)
	{
		bool thisLineEmpty = true;
		for(int x=0;x<width;x++)
		{
			unsigned char* psrc = bmp + y*width*4+ x*4;
			unsigned  int v =  *(unsigned int* )psrc;
			if(v &0xFF000000)
			{
				thisLineEmpty = false;
				break;
			}
		}

		if(thisLineEmpty)
		{
			t.bottom --;
		}
		else
		{
			break;
		}
	}

	
	for(int x=0;x<width;x++)
	{
		bool thisLineEmpty = true;
		for(int y=0;y<height;y++)
		{
			unsigned char* psrc = bmp + y*width*4+ x*4;
			unsigned  int v =  *(unsigned int* )psrc;
			if(v &0xFF000000)
			{
				thisLineEmpty = false;
				break;
			}
		}

		if(thisLineEmpty)
		{
			t.left ++ ;
		}
		else
		{
			break;
		}
	}


	for(int x=width-1;x>=0;x--)
	{
		bool thisLineEmpty = true;
		for(int y=0;y<height;y++)
		{
			unsigned char* psrc = bmp + y*width*4+ x*4;
			unsigned  int v =  *(unsigned int* )psrc;
			if(v &0xFF000000)
			{
				thisLineEmpty = false;
				break;
			}
		}

		if(thisLineEmpty)
		{
			t.right --;
		}
		else
		{
			break;
		}
	}


	return t;
}


/**
stand=standby
attack=Normal attack
squat=Morale attack
attacked=under attack
defense=????
*/


void concat(const char *packsDir, const char *outputDir)
{
	WIN32_FIND_DATAA FindFileData;
	HANDLE hFind = INVALID_HANDLE_VALUE;
	char DirSpec[MAX_PATH];  // directory specification
	char DirBase[MAX_PATH];  // directory specification
	char filename_dest[MAX_PATH];
	char npc_name[MAX_PATH];

	DWORD dwError;


	TANIMATION_INFO aniInfo[4]={
		{ "stand","standby",0,0,0,0,0,0,0,0,0,0},
		{ "attack","Normal attack",0,0,0,0,0,0,0,0,0,0},
		{ "squat","Morale attack",0,0,0,0,0,0,0,0,0,0},
		{ "attacked","under attack",0,0,0,0,0,0,0,0,0,0}
	};

	
	strcpy(DirSpec,packsDir);
	int rlen = strlen(DirSpec);
	if(DirSpec[rlen -1]=='\\')
	{
		DirSpec[rlen-1]='\0';
	}
	strcpy(DirBase,DirSpec);
	char *last = strrchr(DirBase,'\\');
	strcpy(npc_name,last+1);


	char szCurPath[MAX_PATH];


	GetCurrentDirectoryA(MAX_PATH,szCurPath);
	
	unsigned int currentRowHeight = 0;
	unsigned int output_width=0;
	unsigned int output_height=0;
	for(int n=0;n<4;n++)
	{
	
		strcpy(DirSpec,DirBase);
		strcat(DirSpec,"\\序列\\");
		strcat(DirSpec,aniInfo[n].actionDir);
		SetCurrentDirectoryA(DirSpec);
		strcat(DirSpec,"\\*.png");
		
		aniInfo[n].filelist = new TPNG_SRC_FILE_LIST();
		TPNG_SRC_FILE_LIST &filelist = *aniInfo[n].filelist;

		hFind = FindFirstFileA(DirSpec, &FindFileData);
		if (hFind != INVALID_HANDLE_VALUE) 
		{
			while (FindNextFileA(hFind, &FindFileData) != 0) 
			{
				filelist.push_back(new PNG_SRC_FILE(FindFileData.cFileName));
			}
			dwError = GetLastError();
			FindClose(hFind);
		}

		if(filelist.empty())
		{
			continue;
		}

		filelist.sort(filename_sort);


		//剪切
		//
		unsigned int minTop    =0 ;
		unsigned int maxBottom =0 ;
		unsigned int minLeft   =0 ;
		unsigned int maxRight  =0 ;

		for(TPNG_SRC_FILE_LIST_ITER  iter = filelist.begin(); 
			iter !=filelist.end(); iter++)
		{
		
			png_structp png_ptr=NULL;
			png_infop info_ptr=NULL;
			png_infop end_info_ptr=NULL;

			/* open the file */
			FILE *fp = fopen((*iter)->filename.c_str(), "rb");
			if (fp == NULL)
				return ;
			png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,NULL, NULL, NULL);
			info_ptr = png_create_info_struct(png_ptr);
			end_info_ptr = png_create_info_struct(png_ptr);
			png_init_io(png_ptr, fp);
			png_read_info(png_ptr,info_ptr);

			png_bytep dibbytes = new png_byte[info_ptr->width * info_ptr->height * 4];
			
			(*iter)->bmp = dibbytes ;
			(*iter)->rawWidth = info_ptr->width;
			(*iter)->rawHeight = info_ptr->height;
			
			
			for(int y=0;y<info_ptr->height;y++)
			{
				png_bytep pdest = dibbytes + y*info_ptr->width*4;
				png_read_row(png_ptr,pdest,NULL);
			}

			TRECTANGLE cut = cutTransparent(dibbytes,info_ptr->width,info_ptr->height);
			if(minTop==0 || minTop> cut.top)
			{
				minTop = cut.top;
			}
			if(minLeft==0 || minLeft> cut.left)
			{
				minLeft = cut.left;
			}

			if(maxRight < cut.right)
			{
				maxRight = cut.right;
			}
			
			if(maxBottom < cut.bottom)
			{
				maxBottom  = cut.bottom;
			}
			

			png_destroy_read_struct(&png_ptr,&info_ptr,&end_info_ptr);
			fclose(fp);
		}

		
		aniInfo[n].fps = 10;
		aniInfo[n].frameWidth = maxRight - minLeft;
		aniInfo[n].frameHeight  = maxBottom-minTop;
		aniInfo[n].totalFrame= filelist.size();
		aniInfo[n].rowHeight = currentRowHeight;
		aniInfo[n].minTop     =minTop     ;
		aniInfo[n].maxBottom  =maxBottom  ;
		aniInfo[n].minLeft    =minLeft    ;
		aniInfo[n].maxRight   =maxRight   ;

		currentRowHeight += aniInfo[n].frameHeight;

		if(output_width  < aniInfo[n].frameWidth * aniInfo[n].totalFrame)
		{
			output_width   = aniInfo[n].frameWidth * aniInfo[n].totalFrame;
		}

		output_height = currentRowHeight;
	}
	
	EnsureVectorSize(s_srcDibBitsBuffer, output_height * output_width * 4);

	png_bytep  bigmap = &s_srcDibBitsBuffer[0];
	currentRowHeight = 0;
	
	std::string xml;
	const char *headline_fmt =  "<SWFLoader isBattle=\"true\" name=\"%s\" url=\"assets/soldiers/%s.swf\" load=\"false\">\n";
	const char *actionline_fmt =  "  <action name=\"%s\" rowHeight=\"%d\" frameWidth=\"%d\" frameHeight=\"%d\" fps=\"120\" totalFrame=\"%d\" attackFrame=\"0\" attackedFrame=\"0\" isRight=\"true\" offsetX=\"115\" offsetY=\"-268\"/>\n";
	const char *endline_fmt =  "</SWFLoader>\n";
	sprintf(DirSpec,headline_fmt,npc_name,npc_name);
	xml = DirSpec;

	for(int n=0;n<4;n++)
	{
		
		TPNG_SRC_FILE_LIST &filelist = *aniInfo[n].filelist;
		if(filelist.empty())
		{
			continue;
		}

		sprintf(DirSpec,actionline_fmt,aniInfo[n].actionName,aniInfo[n].rowHeight,aniInfo[n].frameWidth,aniInfo[n].frameHeight,aniInfo[n].totalFrame);
		xml +=DirSpec;

		int curCol= 0;
		for(TPNG_SRC_FILE_LIST_ITER   iter = filelist.begin(); 
			iter !=filelist.end(); iter++)
		{
			int yy = 0;
			for(int y=aniInfo[n].minTop;y<aniInfo[n].maxBottom;y++)
			{
				int xx = 0;
				for(int x=aniInfo[n].minLeft;x<aniInfo[n].maxRight;x++)
				{
					unsigned int* psrc = (unsigned int*)((*iter)->bmp + y*(*iter)->rawWidth *4+ x*4);
					int dx =  curCol * aniInfo[n].frameWidth  +  xx;
					int dy =  currentRowHeight +yy;
					unsigned int* dest = (unsigned int*)(bigmap + dy * output_width * 4 + dx*4);
					xx++;
					*dest = *psrc;
				}

				yy ++;
			}
			
			curCol ++;
			PNG_SRC_FILE * f = *iter;
			delete f;
		}

		delete aniInfo[n].filelist;

		currentRowHeight += aniInfo[n].frameHeight;
	}
	xml += endline_fmt;


	SetCurrentDirectoryA(szCurPath);

	strcpy(filename_dest,outputDir);
	if(outputDir[strlen(outputDir)-1]!='\\')
		strcat(filename_dest,"\\");
	strcat(filename_dest,npc_name);
	strcat(filename_dest,".png");
	outputToPngFileNormal(filename_dest,4*output_width * output_height, &s_srcDibBitsBuffer[0],output_width,output_height);

	strcpy(filename_dest,outputDir);
	
	if(outputDir[strlen(outputDir)-1]!='\\')
		strcat(filename_dest,"\\");
	strcat(filename_dest,"AnimationConfig.xml");

	FILE *xmlFile = fopen(filename_dest,"a");
	
	if(xmlFile==NULL)
	{
		return ;
	}

	fwrite(xml.c_str(),1,xml.length(),xmlFile);
	fclose(xmlFile);
}

void print_uasge()
{
	printf("USEAGE:\n"
			"   pngconcat  x:\\path\\to\\yournpcdirectory x:\\path\\to\\output \n"
			"EXAMPLE:\n"
			"   pngconcat  D:\\hero_xxxxxx D:\\temp \n"
			);
}

int checkDir(const char * szdir)
{
	 struct _stat buf;
   int result;
   char timebuf[26];
   const char* filename = szdir;
   errno_t err;

   // Get data associated with "crt_stat.c": 
   result = _stat( filename, &buf );

   // Check if statistics are valid: 
   if( result != 0 )
   {
      perror( "Problem getting information" );
      switch (errno)
      {
         case ENOENT:
           printf("File %s not found.\n", filename);
           break;
         case EINVAL:
           printf("Invalid parameter to _stat.\n");
           break;
         default:
           /* Should never be reached. */
           printf("Unexpected error in _stat.\n");
      }
	  return -1;
   }
   else
   {
	   if( (buf.st_mode & _S_IFDIR)==0)
	   {
		     printf("input %s is not a directory.\n", filename);
			 return -1;
	   }
		
   }
   return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
	
	if(argc != 3 || checkDir(argv[1])  || checkDir(argv[2]) )
	{
		print_uasge();
		return -1;
	}

	concat(argv[1],argv[2]);
	return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值