游戏开发工具之纹理打包器-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;
}

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

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

上一篇:Effective C++ Item 40 明智而审慎地使用多重继承


下一篇:yourphp的eq作用