上一次我们实现了把我们要的图片添加到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); } }
.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); };
// 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; }
这样我们就可以正常显示我们的图片了!
工具的完整代码可以从这里下载