上一次我们实现了把我们要的图片添加到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+绘图的更多相关文章

  1. 5 个最好的3D游戏开发工具(转)

    转自:http://www.open-open.com/news/view/33a4f0 5 个最好的3D游戏开发工具 jopen 2012-11-19 22:56:21 • 发布 摘要:UDK(th ...

  2. Unity 4.2.0 官方最新破解版(Unity3D 最新破解版,3D游戏开发工具和游戏引擎套件)

    Unity是一款跨平台的游戏开发工具,从一开始就被设计成易于使用的产品.作为一个完全集成的专业级应用,Unity还包含了价值数百万美元的功能强大的游戏引擎.Unity作为一个游戏开发工具,它的设计主旨 ...

  3. 优秀工具推荐:两款很棒的 HTML5 游戏开发工具

    HTML5 众多强大特性让我们不需要多么高深技术就能创建好玩的网页游戏,同时证明了开放的 Web 技术能与任何其他在游戏开发中使用的技术竞争.正如标题所说,这篇文章推荐的几款很棒 HTML5 游戏开发 ...

  4. Unity3D ——强大的跨平台3D游戏开发工具(六)

    第十一章 制作炮台的旋转 大家知道,炮台需要向四周不同的角度发射炮弹,这就需要我们将炮台设置成为会旋转的物体,接下来我们就一起制作一个会旋转的炮台. 第一步:给炮台的炮筒添加旋转函数. 给炮台的炮筒部 ...

  5. Unity3D ——强大的跨平台3D游戏开发工具(一)

    众所周知,Unity3D是一个能够实现轻松创作的多平台的游戏开发工具,是一个全面整合的专业游戏引擎.在现有的版本中,其强大的游戏制作功能已 经达到让人瞠目结舌的地步.尤其是它在3.0版本里面制作的那款 ...

  6. 强大的游戏开发工具Unity3D推出2D开发工具,unity将混合3D与2D开发

    2013 Unity全球开发者大会(Unite 2013)于2013年8月28日在温哥华隆重开幕,会上Unity全球CEO David Helgason在Keynote上宣布Unity 4.3版本即将 ...

  7. python+pygame游戏开发之使用Py2exe打包游戏

    最近在用python+pygame 开发游戏,写完以后在分享给朋友玩的时候遇到了很大的问题,只有搭建了环境才能运行python脚本. 这会吓退99%以上的人……所以把我们的游戏打包(注意是打包而不是编 ...

  8. 常用的coco2d-x游戏开发工具(转)

    物理编辑工具Physics Editing ToolsMekanimo 网址:http://www.mekanimo.net/PhysicsBench 网址:http://www.cocos2d-ip ...

  9. Unity3D ——强大的跨平台3D游戏开发工具(四)

    第六章 Unity3D中的C#Script编程的注意事项 也许您在学习Unity3D之前,已经是一位C#的编程高手了.但在Unity3D中的C#并不像真正的C#那般强大,在Unity3D的C#中必须全 ...

随机推荐

  1. Leetcode 190 Reverse Bits 位运算

    反转二进制 class Solution { public: uint32_t reverseBits(uint32_t n) { uint32_t ans = ; ; i<; ++i,n &g ...

  2. Python面试题(一)

    **晚上在公司的论坛上看到一道面试题,题目如下:随机给定一字符串和字符,要求重排,比如:'abde','c'.重排之后变成'abcde' **看到他们给的答案很多都是二分法重排,既然是字符类的处理,当 ...

  3. 详解eNSP下的单臂路由模拟实验配置

    不同VLAN之间的通信可以通过两种方式:单臂路由和三层交换机.其中,单臂路由是通过路由子接口,交换机的某个端口以trunk的方式与路由器的某个端口相连,同时路由器的链接端口配置子接口,配置子接口承载的 ...

  4. springJDBC学习笔记和实例

    前言:相对于Mybatis(ibatis),个人感觉springJDBC更灵活,主要实现类JdbcTemplate:它替我们完成了资源的创建以及释放工作,从而简化了我们对JDBC的使用.它还可以帮助我 ...

  5. SQLSERVER新建表的时候页面分配情况是怎样的?

    SQLSERVER新建表的时候页面分配情况是怎样的? 再次感谢sqlskill网站和转载sqlskill网站文章并翻译的人,因为您们的转载和翻译让小弟又学习到新的东西o(∩_∩)o 文章中用到的工具: ...

  6. SQL Server 2008 Datetime Cast 成 Date 类型可以使用索引(转载)

    很久没写blog,不是懒,实在是最近我这的访问速度不好,用firefox经常上传不了图片 ....... 今天无意发现了SQL Server 2008 Datetime Cast 成 Date 类型可 ...

  7. linux环境内存分配原理 mallocinfo

    Linux的虚拟内存管理有几个关键概念: Linux 虚拟地址空间如何分布?malloc和free是如何分配和释放内存?如何查看堆内内存的碎片情况?既然堆内内存brk和sbrk不能直接释放,为什么不全 ...

  8. 解决Visual Studio 2010/2012的RC4011 warnings

    如果在vc10/11工程的rc文件中有以下任意一行代码: #include <winuser.h> #include <richedit.h> 那么vc将会给出一对警告: C: ...

  9. 解决VS2010中在项目上右键鼠标,无“添加STS引用”菜单的问题

    解决方法:将Windows Identity Foundation SDK文件夹C:\Program Files (x86)\Windows Identity Foundation SDK\v3.5\ ...

  10. SSH在Jenkins中的使用

    我们今天在迁移Jenkins的时候又出现无法调用私钥来获取oschina的git代码和使用scp拷贝无法验证的问题.我发现主要的问题实际上是关于ssh的问题,因为git和scp都是通过ssh来实现与远 ...