游戏开发工具之纹理打包器-3.使用GDI+绘图

上一次我们实现了把我们要的图片添加到CTreeCtrl控件里去,并显示图片的缩略图,现在开始我们要讲比较重要的部分--绘图区。为了实现能编辑图片的功能,绘图区应该具有如下功能。

1.  添加删除图片。

2.  放大缩小绘图区。

3.  选中一张图片,移动一张图片。

4.  绘制图片

5.  给图片添加点击事件

为了更好的实现这些功能,我模仿了cocos2d的内存管理机制以及节点结构,写了一个静态库VALib,它使用GID+渲染图片,以及实现观察者模式来监听鼠标事件。源码可一从这里下载。当然,你也可以使用cocos2d 来实现绘图区的功能。这里我使用我自己写的VALib 库来实现。

下面开始上码。

首先我们需要先继承VALib库里的VASprite类,来写一个符合我们自己需求的Image类

.h文件

#pragma once
#pragma comment(lib, "../debug/valib.lib")
#include "valib.h"
#include "SelectHandler.h"
#include "RectPlacement.h"

US_VA_NS
class VaImage :
	public VASprite , public SelectHandler ,public CRectPlacement::TRect
{
private:
	bool m_TouchFlag;
	bool m_selectFlag;
	VAPoint* m_lastPoing;
	
	void init();
public:
	VaImage(const char* m_name);
	~VaImage(void);

	/************************************************************************/
	/* 使用一个图片创建一个VASprite
	/* fileName: 图片路径
	/************************************************************************/
	static VaImage* FromFile(const WCHAR* filename);
	bool intersectPoint(VAPoint* pt);
	/************************************************************************/
	/* 重写registerTouchDispatcher函数,使图片吞并touch事件,图片下方的图片不响应touch事件                                                                     */
	/************************************************************************/
	virtual void registerTouchDispatcher();
	virtual bool vaTouchBegan(VATouch* m_pTouch, VAEvent* m_pEvent);
	virtual void vaTouchMoved(VATouch* m_pTouch, VAEvent* m_pEvent);
	virtual void vaTouchEnded(VATouch* m_pTouch, VAEvent* m_pEvent);
	virtual void vaTouchCancelled(VATouch* m_pTouch, VAEvent* m_pEvent);
	virtual bool select(CPoint* pt);
	virtual void unselect();
	virtual void draw();
};
.cpp文件

#include "StdAfx.h"
#include "VaImage.h"
#include <regex>

US_VA_NS

VaImage::VaImage(const char* m_name):VASprite(m_name)
, m_TouchFlag(false)
, m_selectFlag(false)
, m_lastPoing(NULL)
{
	init();
}

VaImage::~VaImage(void)
{
}

VaImage* VaImage::FromFile( const WCHAR* filename )
{
	Bitmap* bitmap = Bitmap::FromFile( filename );
	//截取文件名
	CString tempName = filename;
	int m_index = tempName.Find(L"\\");
	while ( m_index!=-1 )
	{
		tempName = tempName.Right(tempName.GetLength()-(m_index+1));
		m_index = tempName.Find(L"\\");
	}

	USES_CONVERSION;
	const char *name = W2A(tempName.GetBuffer(tempName.GetLength()));//LPSTR)(LPCTSTR)tempName;
	//新建VAImage并贴上图片
	VaImage* vaImage = new VaImage(name);
	vaImage->setBitmap(bitmap);
	return vaImage;
}

void VaImage::init()
{
	registryDispatch();
}

bool VaImage::intersectPoint( VAPoint* pt )
{
	VARect rect  = getRect();
	VAPoint m_pt = VAPoint(pt->x, pt->y);
	return rect.containsPoint(m_pt);
}

void VaImage::registerTouchDispatcher()
{
	registerWithTouchDispatcher(NULL, true);
}

bool VaImage::vaTouchBegan( VATouch* m_pTouch, VAEvent* m_pEvent )
{
	VAPoint* pt = new VAPoint(*(m_pTouch->getLocation()));
	if(intersectPoint(pt)){
		m_TouchFlag = true;
		if(m_lastPoing != NULL){
			//setPosition(new VAPoint( getPosition()->x + (pt->x - m_lastPoing->x), getPosition()->y + (pt->y - m_lastPoing->y) ));
		}
		m_lastPoing = pt;
		pt->release();
		return true;
	}
	pt->release();
	return false;
}

void VaImage::vaTouchMoved( VATouch* m_pTouch, VAEvent* m_pEvent )
{
	if(m_TouchFlag){
		VAPoint* pt = new VAPoint(*(m_pTouch->getLocation()));
		VAPoint tempPT = VAPoint( getPosition().x + (pt->x - m_lastPoing->x), getPosition().y + (pt->y - m_lastPoing->y) );
		setPosition(tempPT);
		m_lastPoing = pt;
		tempPT.release();
		pt->release();
	}
}

void VaImage::vaTouchEnded( VATouch* m_pTouch, VAEvent* m_pEvent )
{
	m_TouchFlag = false;
}

void VaImage::vaTouchCancelled( VATouch* m_pTouch, VAEvent* m_pEvent )
{

}

bool VaImage::select( CPoint* pt )
{
	VAPoint m_pt = VAPoint(pt->x, pt->y);
	if(intersectPoint(&m_pt)){
		m_selectFlag = true;
		m_pt.release();
		return true;
	}
	m_pt.release();
	unselect();
	return false;
}

void VaImage::unselect()
{
	m_selectFlag = false;
}

void VaImage::draw()
{
	VASprite::draw();
	//绘制边框
	if(m_selectFlag){
		VADirector* director = VADirector::sharedDirector();

		vertex vertex = cloneVertex();
		int minX = min(min(min(vertex.leftTop.X, vertex.rightTop.X), vertex.rightBottom.X), vertex.leftBottmo.X);
		int minY = min(min(min(vertex.leftTop.Y, vertex.rightTop.Y), vertex.rightBottom.Y), vertex.leftBottmo.Y);
		int maxX = max(max(max(vertex.leftTop.X, vertex.rightTop.X), vertex.rightBottom.X), vertex.leftBottmo.X);
		int maxY = max(max(max(vertex.leftTop.Y, vertex.rightTop.Y), vertex.rightBottom.Y), vertex.leftBottmo.Y);

		VARect rect  = getRect();
		Gdiplus::Rect r(rect.getMinX(), rect.getMinY(), rect.getMaxX()-rect.getMinX(), rect.getMaxY()-rect.getMinY());
		
		director->DrawBorder(&r);
	}
}

完后我们需要继承CWnd来创建一个自定义组件ImageView

.h文件

#pragma once
#include "valib.h"
#pragma comment(lib, "../debug/valib.lib")
#include "stdafx.h"
#include "ImgsTool.h"
#include "VaImage.h"

// ImageView
US_VA_NS//使用valib命名空间

typedef std::vector<VaImage*> VaImageArray;
class ImageView : public CWnd
{
	DECLARE_DYNAMIC(ImageView)
private:

	VADirector* m_vaDirector;
	VATouchDispatcher* m_vaTouchDispatcher;
	SelectDispatcher* m_pSelectDispatcher;
	
	Bitmap* m_canva;
	Bitmap* m_bgImg;
	float m_scale;
	
	VaImageArray imgList;
	float m_tagArrange;
	bool m_drawTage;
public:
	VAScene* m_scene;
	ImageView();
	virtual ~ImageView();

	void saveImg(CString filePath, CString type, Bitmap* bitmap = NULL);
	void savePlis(CString filePath, CString ImageType);
	void addImage(const WCHAR* filename);
	void setAutoArrange(float isArrange);
	void autoArrange();
protected:
	DECLARE_MESSAGE_MAP()
	virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);
	
	void init();
	void drawBackground();
	void update();
	void draw(CDC* pDC);

	int GetEncoderClsid(const WCHAR* format, CLSID *pClsid);
};


.cpp 文件

// ImageView.cpp : implementation file
//
#include "stdafx.h"
#include "ImgsTool.h"
#include "ImageView.h"
#include <algorithm>
#include "PublishPlist.h"

// ImageView
US_VA_NS//使用valib命名空间
IMPLEMENT_DYNAMIC(ImageView, CWnd)
enum{
	viewInterval,
};
ImageView::ImageView()
{

}

ImageView::~ImageView()
{
}

BEGIN_MESSAGE_MAP(ImageView, CWnd)
END_MESSAGE_MAP()
// ImageView message handlers

LRESULT ImageView::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
	// TODO: Add your specialized code here and/or call the base class
	VATouch* pTouch = new VATouch();
	CPoint pt = (CPoint)lParam;
	pTouch->setTouchInfo((float)pt.x, (float)pt.y);
	switch (message)
	{
	case WM_CREATE:
		init();
		break;
	case WM_PAINT:
		if(m_drawTage){
			update();
		}
		break;
	case WM_TIMER:
		Invalidate(FALSE);
		break;
	case WM_LBUTTONDOWN:
		m_pSelectDispatcher->callAllHandler(&pt);
		m_vaTouchDispatcher->touchesBegan(pTouch, NULL);
		break;
	case WM_MOUSEMOVE:
		m_vaTouchDispatcher->touchesMoved(pTouch, NULL);
		break;
	case WM_LBUTTONUP:
		m_vaTouchDispatcher->touchesEnded(pTouch, NULL);
		break;
// 	case WM_COMMAND://接收控件发送来的消息的
// 		break;
	}
	return CWnd::WindowProc(message, wParam, lParam);
}

void ImageView::init()
{
	m_tagArrange = true;
	CRect rect;
	this->GetClientRect(rect);
	m_bgImg = new Bitmap(rect.Width(), rect.Height());
	drawBackground();//绘制背景
	m_canva = new Bitmap(rect.Width(), rect. Height());//创建画布

	m_pSelectDispatcher = ToolsCenter::getInstance()->getSelectDispatcher();

	m_vaDirector = VADirector::sharedDirector();
	m_vaDirector->init(this->m_hWnd);
	m_vaTouchDispatcher = m_vaDirector->getTouchDispatcher();
	m_scene = new VAScene();

	SetTimer(viewInterval, 5, NULL);
	Invalidate(FALSE);
}

void ImageView::drawBackground()
{
	CRect rect;
	this->GetClientRect(rect);
	int size = 20;
	Graphics* bgG = Graphics::FromImage(m_bgImg);
	
	Bitmap* m_bgtexture = new Bitmap(size,size);
	Graphics* txG = Graphics::FromImage(m_bgtexture);
	txG->FillRectangle(&SolidBrush(Color(255,255,255)), 0, 0, size, size);
	txG->FillRectangle(&SolidBrush(Color(192,192,192)), size/2, 0, size/2 , size/2);
	txG->FillRectangle(&SolidBrush(Color(192,192,192)), 0, size/2, size/2 , size/2);
	
	bgG->FillRectangle(&TextureBrush(m_bgtexture), rect.left, rect.top, rect.right, rect.bottom);
	//saveImg(m_bgImg);
}
//更新窗口
void ImageView::update()
{
	CDC* dc = this->GetDC();
	draw(dc);
}
//绘制窗口
void ImageView::draw(CDC* pDC)
{
	CDC MemDC;				//首先定义一个显示设备对象
	CBitmap MemBitmap;	//定义一个位图对象
	CRect rect;
	this->GetClientRect(rect);

	MemDC.CreateCompatibleDC(NULL);//随后建立与屏幕显示兼容的内存显示设备
	MemBitmap.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height()); //建立一个与屏幕显示兼容的位图,至于位图的大小嘛,可以用窗口的大小

	//将位图选入到内存显示设备中
	//只有选入了位图的内存显示设备才有地方绘图,画到指定的位图上
	CBitmap *pOldBit=MemDC.SelectObject(&MemBitmap);

	Graphics* memG = Graphics::FromHDC(MemDC);
	Rect destinationRect(0, 0, rect.Width(), rect.Height());
	memG->DrawImage(m_bgImg, destinationRect, 0, 0, rect.Width(), rect.Height(), Gdiplus::UnitPixel);

	m_canva = new Bitmap(rect.Width(), rect.Height());
	Graphics* canvaG = Graphics::FromImage(m_canva);
	//m_scene->update();
	VADirector::sharedDirector()->initDraw(memG);
 	m_scene->draw();//绘制valib的场景里的各个图片
 	memG->DrawImage(m_canva, destinationRect, 0, 0, rect.Width(), rect.Height(), Gdiplus::UnitPixel);

	delete canvaG;
	delete m_canva;
	
	//绘图后将内存中的图拷贝到屏幕上进行显示
	pDC->BitBlt(0,0, rect.Width(), rect.Height(), &MemDC,0, 0,SRCCOPY);

	//绘图完成后清理临时对象
	MemBitmap.DeleteObject();
	delete memG;
	MemDC.DeleteDC();
	ReleaseDC(pDC);
}

void ImageView::saveImg(CString filePath, CString type,  Bitmap* bitmap /*= NULL*/)
{
	//m_drawTage = false;
	CRect rect;
	this->GetClientRect(rect);
	Gdiplus::Bitmap* _canva = new Bitmap(rect.Width(), rect.Height());
	Gdiplus::Graphics* canvaG = Gdiplus::Graphics::FromImage(_canva);
	Rect destinationRect(10, 0, rect.Width(), rect.Height());	

	VADirector::sharedDirector()->initDraw(canvaG);
	m_scene->draw();

	Bitmap* _bitmap;
	if(!bitmap)
		_bitmap = _canva;
	else
		_bitmap = bitmap;
	CLSID encoderClsid;
	this->GetParent();
	CString t = type.Right(type.GetLength()-1);
	if(t == "jpg") t = "jpeg";
	GetEncoderClsid(L"image/" + t, &encoderClsid);
	_bitmap->Save(filePath+type, &encoderClsid, NULL);
}

void ImageView::addImage(const WCHAR* filename){
	VaImage* img = VaImage::FromFile(filename);
	m_scene->addChild(img);
	imgList.push_back(img);

	if(m_tagArrange){
		autoArrange();
	}
}

void ImageView::savePlis( CString filePath, CString ImageType)
{
	filePath += ".plist";
	const wchar_t* ffd = filePath.GetBuffer(filePath.GetLength());
	USES_CONVERSION;
	const char* file = W2A(ffd);
	const char* _type = W2A(ImageType.GetBuffer(ImageType.GetLength()));
	PublishPlist* plist = new PublishPlist(file,_type, "100, 100");
	for(int i = 0; i< (int)imgList.size(); i++){
		VaImage* img = imgList.at(i);
		plist->addItem( img->getName(), int(img->getPosition().x), int(img->getPosition().y), int(img->getSize().width), int(img->getSize().height) );
	}
	plist->publish();
}

void ImageView::setAutoArrange( float isArrange )
{
	m_tagArrange = isArrange;
}

void ImageView::autoArrange(){
	//排序,由大小
	std::sort(imgList.begin(), imgList.end(), CRectPlacement::TRect::Greaters);
	CRectPlacement crp = CRectPlacement(imgList.front()->getSize().width, imgList.front()->getSize().height);
	for(int i = 0; i< (int)imgList.size(); i++){
		CRectPlacement::TRect r(0, 0, imgList.at(i)->getSize().width, imgList.at(i)->getSize().height);
		bool bPlaced = false;
		bPlaced = crp.AddAtEmptySpotAutoGrow(&r, 100000, 100000);
		imgList.at(i)->setPosition(VAPoint(r.x, r.y));
	}
}

/*获取Image编码
* format: image/png, image/jpeg, image/gif
* pClsid: CLSID
*/
int ImageView::GetEncoderClsid( const WCHAR* format, CLSID *pClsid )
{
	UINT num = 0;	//number of image encoder;
	UINT size = 0;	//size of the image encoder array in bytes;
	ImageCodecInfo *pImageCodecInfo = NULL;
	GetImageEncodersSize(&num, &size);
	if(size ==0)
		return -1;	//Failure
	pImageCodecInfo = (ImageCodecInfo *)(malloc(size));
	if(pImageCodecInfo==NULL)
		return -1;
	GetImageEncoders(num, size, pImageCodecInfo);
	for(UINT j=0; j< num; ++j){
		if(wcscmp(pImageCodecInfo[j].MimeType, format)==0){
			*pClsid = pImageCodecInfo[j].Clsid;
			free(pImageCodecInfo);
			return j;
		}
	}
	free(pImageCodecInfo);
	return -1;
}


这样我们就可以正常显示我们的图片了!

工具的完整代码可以从这里下载


你可能感兴趣的:(游戏开发工具之纹理打包器-3.使用GDI+绘图)