画图板的几种方法
1 使用有界面的qt 2 使用mfc 3 c# winform 4 c# wpf 5 使用html5 6 使用opencv
1 是可以跨平台的
2 是不可以跨平台的,和3,4 一起说不清楚微软为什么不做跨平台的界面库 winform 和wpf 一样不能跨,但是在Visual Studio 2019 16.6版中,针对 .NET Core 平台推出 Winform 设计器,因此使用vs2019 c# 是可以跨平台的。
5 可以跨平台
6 可以跨平台
作为技术选型,如果考虑成熟度,还是先不要激进,做产品以成熟的东西为主。因此在windows平台上,可以选择directx 作为首选,他速度是最快的。同时主要平台上使用html5 制作了画板,但是要和有界面的同步,分别使用directx9 因此使用sdl 来画图 ,本质上来说,directx9和sdl 并没有多大区别,sdl 可以使用directx9 或者 opengl 来画。 需要和ffmpeg等联合画和编解码,最好的方式就是使用sdl 和 opencv。这其实应该是首选,opencv无论是成熟度还是处理图形图像都是高质量的,后面我们会补充这种方式来做
1、使用sdl来画// testsdl2.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include
#include
#include
#define __STDC_CONSTANT_MACROS
#define SDL_MAIN_HANDLED
extern "C"
{
#include "SDL2/SDL.h"
#include "SDL2/SDL_mouse.h"
#include "SDL2/SDL_surface.h"
#include
//if you want sdl image
//#include "SDL2/SDL_image.h"
};
#include "object.h"
#ifdef _WIN32
#pragma comment(lib,"sdl2.lib")
#pragma comment(lib,"opengl32")
#endif
class c_drawing {
SDL_Window *v_pWindow = NULL;
SDL_Renderer *v_pRender = NULL;
SDL_Texture * v_pTexture = NULL;
bool v_Run = false;
s_drawrop v_save;
//s_point v_end;
bool v_bstart = false;
s_canvas v_canvas;
public:
bool func_running()
{
return v_Run;
}
void draw_all_objs()
{
#define L(x) v_canvas.v_lines[i]
SDL_SetRenderDrawColor(v_pRender, 255, 255, 255, 0);
for (size_t i = 0; i m_height*(float)newHeight
class LineOverlay : public Overlay
{
public:
LineOverlay(IDirect3DDevice9* device, POINT p1, POINT p2, INT width, D3DCOLOR color, BYTE opacity,INT SWidth,INT SHeight)
: Overlay(device, color, opacity,SWidth,SHeight)
{
HRESULT hr = D3DXCreateLine(m_device, &m_line);
m_lineWidth = width;
m_vectors[0].x = p1.x;
m_vectors[0].y = p1.y;
m_vectors[1].x = p2.x;
m_vectors[1].y = p2.y;
m_line->SetWidth(width);
m_line->SetAntialias(TRUE);
//m_per_x = (float)p1.x / m_width
m_type = D_LINE;
}
LineOverlay():Overlay(NULL, 0, 0,0,0)
{
}
//释放资源和占有的表面
void ReleaseObject()
{
SafeRelease(m_line);
}
HRESULT ReCreate(IDirect3DDevice9* device,int width,int height)
{
if(device!=NULL)
{
m_device = device;
//释放
SafeRelease(m_line);
HRESULT hr = D3DXCreateLine(m_device, &m_line);
m_line->SetWidth(m_lineWidth);
m_line->SetAntialias(TRUE);
m_type = D_LINE;
m_vectors[0].x= ((float)m_vectors[0].x)/(float)m_width * (float)width;
m_vectors[0].y= ((float)m_vectors[0].y)/(float)m_height* (float)height;
m_vectors[1].x= ((float)m_vectors[1].x)/(float)m_width * (float)width;
m_vectors[1].y= ((float)m_vectors[1].y)/(float)m_height* (float)height;
//CalcNewPt_x(1);
//CalcNewPt_y(1);
m_width = width;
m_height = height;
return S_OK;
}
return S_FALSE;
}
virtual ~LineOverlay()
{
SafeRelease(m_line);
}
virtual HRESULT Draw(void)
{
HR(m_line->Begin());
HR(m_line->Draw(m_vectors, 2, m_color));
return m_line->End();
}
private:
CComPtr m_line;
public:
D3DXVECTOR2 m_vectors[2];
};
class RectangleOverlay : public Overlay
{
public:
RectangleOverlay(IDirect3DDevice9* device, RECT rectangle, INT width, D3DCOLOR color, BYTE opacity,INT SWidth,INT SHeight)
: Overlay(device, color, opacity,SWidth,SHeight)
{
D3DXCreateLine(m_device, &m_line);
m_lineWidth = width;
m_line->SetWidth(m_lineWidth);
m_line->SetAntialias(TRUE);
m_vectors[0].x = rectangle.left;
m_vectors[0].y = rectangle.top;
m_vectors[1].x = rectangle.right;
m_vectors[1].y = rectangle.top;
m_vectors[2].x = rectangle.right;
m_vectors[2].y = rectangle.bottom;
m_vectors[3].x = rectangle.left;
m_vectors[3].y = rectangle.bottom;
m_vectors[4].x = rectangle.left;
m_vectors[4].y = rectangle.top;
m_type = D_RENTANGLE;
}
void ReleaseObject()
{
SafeRelease(m_line);
}
HRESULT ReCreate(IDirect3DDevice9* device,int newWidth,int newHeight)
{
if(device!=NULL)
{
SafeRelease(m_line);
m_device = device;
D3DXCreateLine(m_device, &m_line);
m_line->SetWidth(m_lineWidth);
m_line->SetAntialias(TRUE);
for(int i =0;iBegin());
HR(m_line->Draw(m_vectors, 5, m_color));
return m_line->End();
}
private:
CComPtr m_line;
D3DXVECTOR2 m_vectors[5];
};
class PolygonOverlay : public Overlay
{
public:
PolygonOverlay(IDirect3DDevice9* device, POINT* points, INT pointsLen, INT width, D3DCOLOR color, BYTE opacity,INT SWidth,INT SHeight)
: Overlay(device, color, opacity,SWidth,SHeight)
{
HRESULT hr = D3DXCreateLine(m_device, &m_line);
m_lineWidth = width;
m_vectors = new D3DXVECTOR2[pointsLen + 1];
for(int i = 0 ; i SetWidth(width);
m_line->SetAntialias(TRUE);
m_type = D_POLYGON;
}
PolygonOverlay():Overlay(NULL, 0, 0,0,0)
{
m_vectors = NULL;
}
void ReleaseObject()
{
SafeRelease(m_line);
}
HRESULT ReCreate(IDirect3DDevice9* device,int newWidth,int newHeight)
{
if(device!=NULL)
{
m_device = device;
HRESULT hr = D3DXCreateLine(m_device, &m_line);
m_line->SetWidth(m_lineWidth);
m_line->SetAntialias(TRUE);
//重新计算
for(int i = 0 ; i Draw(m_vectors, m_numOfVectors, m_color));
return m_line->End();
}
private:
CComPtr m_line;
public:
D3DXVECTOR2* m_vectors;
INT m_numOfVectors;
};
class D3DFont
{
public:
D3DFont(IDirect3DDevice9* device, LPCWSTR fontName, INT fontSize, UINT weight)
{
D3DXCreateFontW( device, fontSize, 0, weight, 0, FALSE, 0, 0, 0, 0, fontName, &m_font );
m_fontsize = fontSize;
}
virtual ~D3DFont()
{
SafeRelease(m_font);
}
HRESULT PrepareText(LPCWSTR pText)
{
return m_font->PreloadTextW(pText, -1);
}
HRESULT DrawText(LPCWSTR pText, LPRECT rect, D3DCOLOR color)
{
return m_font->DrawTextW(NULL,pText, -1, rect, 0, color);
}
public:
INT m_fontsize;
private:
CComPtr m_font;
};
//class FontFactory
//{
//private:
// FontFactory() { };
//
// typedef map FactoryMap;
// //typedef map FactoryMap;
// FactoryMap m_FactoryMap;
//public:
// ~FontFactory() { Clear(); }
//
// static FontFactory *Get()
// {
// static FontFactory instance;
// return &instance;
// }
//
// void Clear()
// {
// if(m_FactoryMap.empty())
// {
// return;
// }
//
// for (FactoryMap::iterator i = m_FactoryMap.begin(); i != m_FactoryMap.end(); ++i)
// {
// delete i->second;
// }
//
// m_FactoryMap.clear();
// }
//
// //D3DFont* GetFont(IDirect3DDevice9* device, LPCTSTR fontName, INT fontSize, UINT weight)
// D3DFont* GetFont(IDirect3DDevice9* device, LPCWSTR fontName, INT fontSize, UINT weight)
// {
// D3DFont* font;
// wstring fName(fontName);
// FactoryMap::iterator it = m_FactoryMap.find(fName);
// if( it == m_FactoryMap.end() )
// {
// font = new D3DFont(device, fontName, fontSize, weight);
// m_FactoryMap[fName] = font;
// }
// else
// {
// font = it->second;
// if(font->m_fontsize != fontSize)
// {
// delete it->second;
//
// D3DFont *newFont = new D3DFont(device, fontName, fontSize, weight);
// m_FactoryMap[fName] = newFont;
// font = newFont;
// //delete oldFont;
// }
// }
//
// return font;
// }
//};
//
class TextOverlay : public Overlay
{
public:
TextOverlay(IDirect3DDevice9* device, LPCWSTR text, RECT pos, INT size, D3DCOLOR color, LPCWSTR font, BYTE opacity,INT SWidth,INT SHeight)
: Overlay(device, color, opacity,SWidth,SHeight)
{
m_pos.left = pos.left;
m_pos.top = pos.top;
m_pos.right = SWidth;//pos.right;
m_pos.bottom = SHeight;//pos.bottom;
/*char buffer[256];
sprintf(buffer,"nw-h is %d %d,source is %d-%d-%d-%d\n"
,SWidth
,SHeight
,m_pos.left
,m_pos.right
,m_pos.top
,m_pos.bottom);
OutputDebugStringA(buffer);*/
int len = wcslen(text);
m_text = new WCHAR[len + 1];
wmemcpy(m_text, text, len);
m_text[len] = '\0';
m_fontName = font;
m_pFont = new D3DFont(device, font, size, FW_NORMAL);// FontFactory::Get()->GetFont(device, font, size, FW_NORMAL);
m_pFont->PrepareText(m_text);
m_size = size;
m_type = D_TEXT;
}
TextOverlay():Overlay(NULL, 0, 0,0,0)
{
}
virtual ~TextOverlay()
{
if(m_text!=NULL)
{
delete m_text;
m_text = NULL;
}
if(m_pFont!=NULL)
{
delete m_pFont;
m_pFont = NULL;
}
}
virtual HRESULT Draw(void)
{
return m_pFont->DrawText(m_text, &m_pos, m_color );
}
void ReleaseObject()
{
delete m_pFont;
m_pFont = NULL;
}
HRESULT ReCreate(IDirect3DDevice9* device,int newWidth,int newHeight)
{
m_pos.left = ((float)m_pos.left)/(float)m_width* (float)newWidth+0.5;
m_pos.right = newWidth;
//m_pos.right = ((float)m_pos.right)/(float)m_width* (float)newWidth+0.5;
m_pos.top = ((float)m_pos.top)/(float)m_height* (float)newHeight+0.5;
m_pos.bottom= newHeight;//((float)m_pos.bottom)/(float)m_height* (float)newHeight+0.5;
m_size = (float)m_size /(float)m_width* (float)newWidth;
m_width = newWidth;
m_height = newHeight;
m_pFont = new D3DFont(device, m_fontName.c_str(), m_size, FW_NORMAL);//FontFactory::Get()->GetFont(device, m_fontName.c_str(), m_size, FW_NORMAL);
m_pFont->PrepareText(m_text);
//m_type = D_TEXT;
return S_OK;
}
public:
WCHAR* m_text;
//char* m_text;
wstring m_fontName;
//string m_fontName;
RECT m_pos;
D3DFont* m_pFont;
float m_size;
};
class BaseBitmapOverlay : public Overlay
{
public:
BaseBitmapOverlay(IDirect3DDevice9* device, RECT rectangle, BYTE opacity)
: Overlay(device, D3DCOLOR_ARGB(0xff, 0, 0, 0), opacity,0,0), m_applyShaders(false)
{
m_rectangle = rectangle;
HRESULT hr = m_device->CreateVertexBuffer(sizeof(VERTEX) * 4, D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &m_pVertexBuffer, NULL);
}
virtual ~BaseBitmapOverlay()
{
SafeRelease(m_pVertexBuffer);
SafeRelease(m_pTexture);
}
virtual HRESULT Draw(void)
{
HRESULT hr = S_OK;
VERTEX vertexArray[] =
{
{ D3DXVECTOR3(m_rectangle.left, m_rectangle.top, 0), D3DCOLOR_ARGB(m_opacity, 255, 255, 255), D3DXVECTOR2(0, 0) }, // top left
{ D3DXVECTOR3(m_rectangle.right, m_rectangle.top, 0), D3DCOLOR_ARGB(m_opacity, 255, 255, 255), D3DXVECTOR2(1, 0) }, // top right
{ D3DXVECTOR3(m_rectangle.right, m_rectangle.bottom, 0), D3DCOLOR_ARGB(m_opacity, 255, 255, 255), D3DXVECTOR2(1, 1) }, // bottom right
{ D3DXVECTOR3(m_rectangle.left, m_rectangle.bottom, 0), D3DCOLOR_ARGB(m_opacity, 255, 255, 255), D3DXVECTOR2(0, 1) }, // bottom left
};
VERTEX *vertices;
hr = m_pVertexBuffer->Lock(0, 0, (void**)&vertices, D3DLOCK_DISCARD);
memcpy(vertices, vertexArray, sizeof(vertexArray));
hr = m_pVertexBuffer->Unlock();
if(!m_applyShaders)
{
m_device->SetPixelShader(NULL);
m_device->SetVertexShader(NULL);
}
hr = m_device->SetStreamSource(0, m_pVertexBuffer, 0, sizeof(VERTEX));
hr = m_device->SetFVF(D3DFVF_CUSTOMVERTEX);
hr = m_device->SetTexture(0, m_pTexture);
hr = m_device->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2);
hr = m_device->SetTexture(0, NULL);
return hr;
}
virtual HRESULT Update(BYTE* pYplane, BYTE* pVplane, BYTE* pUplane)
{
return S_OK;
}
void EnablePixelShaders(bool bEnable)
{
m_applyShaders = bEnable;
}
void UpdateOpacity(BYTE opacity)
{
m_opacity = opacity;
}
protected:
CComPtr m_pTexture;
bool m_applyShaders;
RECT m_rectangle;
private:
CComPtr m_pVertexBuffer;
};
class FileOverlay : public BaseBitmapOverlay
{
public:
//FileOverlay(IDirect3DDevice9* device, RECT rectangle, LPCWSTR path, D3DCOLOR colorKey, BYTE opacity)
// : BaseBitmapOverlay(device, rectangle, opacity)
FileOverlay(IDirect3DDevice9* device, RECT rectangle, LPCTSTR path, D3DCOLOR colorKey, BYTE opacity)
: BaseBitmapOverlay(device, rectangle, opacity)
{
HRESULT hr = D3DXCreateTextureFromFileEx(m_device,
path,
D3DX_DEFAULT, // default width
D3DX_DEFAULT, // default height
D3DX_DEFAULT, // no mip mapping
NULL, // regular usage
D3DFMT_A8R8G8B8, // 32-bit pixels with alpha
D3DPOOL_MANAGED, // typical memory handling
D3DX_DEFAULT, // no filtering
D3DX_DEFAULT, // no mip filtering
colorKey, // color key
NULL, // no image info struct
NULL, // not using 256 colors
&m_pTexture); // load to texture
if(FAILED(hr))
{
char msg[200];
sprintf_s(msg, "Failed to load texture from file, error %d", hr);
throw msg;
}
}
};
class MemoryBitmapOverlay : public BaseBitmapOverlay
{
public:
MemoryBitmapOverlay(IDirect3DDevice9* device, RECT rectangle, BYTE* pixelData, UINT stride, INT width, INT height, D3DCOLOR colorKey, BYTE opacity)
: BaseBitmapOverlay(device, rectangle, opacity)
{
CComPtr surface;
HRESULT hr = m_device->CreateOffscreenPlainSurface(width, height, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &surface, NULL);
D3DLOCKED_RECT rect;
hr = surface->LockRect(&rect, NULL, 0);
for (int z = 0; z CreateTexture(w, h, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &m_pTexture, NULL);
CComPtr pTextureSurface;
hr = m_pTexture->GetSurfaceLevel(0, &pTextureSurface);
hr = m_device->ColorFill(pTextureSurface, NULL, D3DCOLOR_ARGB(0xFF, 0, 0, 0));
hr = m_device->StretchRect(surface, NULL, pTextureSurface, NULL, D3DTEXF_LINEAR);
}
};
class VideoOverlay : public BaseBitmapOverlay
{
public:
VideoOverlay(IDirect3DDevice9* device, int videoWidth, int videoHeight, D3DFORMAT videoFormat, RECT targetRect, BYTE opacity)
: BaseBitmapOverlay(device, targetRect, opacity)
{
HRESULT hr = m_device->CreateOffscreenPlainSurface(videoWidth, videoHeight, videoFormat, D3DPOOL_DEFAULT, &m_pSurface, NULL);
m_width = videoWidth;
m_height = videoHeight;
m_format = videoFormat;
int w = targetRect.right - targetRect.left;
int h = targetRect.bottom - targetRect.top;
hr = m_device->CreateTexture(w, h, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &m_pTexture, NULL);
hr = m_pTexture->GetSurfaceLevel(0, &m_pTextureSurface);
hr = m_device->ColorFill(m_pTextureSurface, NULL, D3DCOLOR_ARGB(0xFF, 0, 0, 0));
}
virtual HRESULT Update(BYTE* pYplane, BYTE* pVplane, BYTE* pUplane)
{
int newHeight = m_height;
int newWidth = m_width;
D3DLOCKED_RECT d3drect;
HR(m_pSurface->LockRect(&d3drect, NULL, 0));
BYTE* pict = (BYTE*)d3drect.pBits;
BYTE* Y = pYplane;
BYTE* V = pVplane;
BYTE* U = pUplane;
switch(m_format)
{
case D3DFMT_YV12:
for (int y = 0 ; y 1 ; y++)
{
memcpy(pict, V, newWidth >> 1);
pict += d3drect.Pitch >> 1;
V += newWidth >> 1;
}
for (int y = 0 ; y > 1; y++)
{
memcpy(pict, U, newWidth >> 1);
pict += d3drect.Pitch >> 1;
U += newWidth >> 1;
}
break;
case D3DFMT_NV12:
for (int y = 0 ; y 1 ; y++)
{
memcpy(pict, V, newWidth);
pict += d3drect.Pitch;
V += newWidth;
}
break;
case D3DFMT_YUY2:
case D3DFMT_UYVY:
case D3DFMT_R5G6B5:
case D3DFMT_X1R5G5B5:
case D3DFMT_A8R8G8B8:
case D3DFMT_X8R8G8B8:
memcpy(pict, Y, d3drect.Pitch * newHeight);
break;
}
m_pSurface->UnlockRect();
return m_device->StretchRect(m_pSurface, NULL, m_pTextureSurface, NULL, D3DTEXF_LINEAR);
}
~VideoOverlay()
{
SafeRelease(m_pTextureSurface);
SafeRelease(m_pSurface);
}
private:
CComPtr m_pSurface;
CComPtr m_pTextureSurface;
int m_width;
int m_height;
D3DFORMAT m_format;
};
class OverlayStore
{
typedef map OverlayMap;
public:
OverlayStore()
{
}
virtual ~OverlayStore()
{
RemoveAll();
}
void AddOverlay(Overlay* pOverlay, SHORT id)
{
CAutoLock lock(&m_lock);
OverlayMap::iterator i = m_overlays.find(id);
if (i != m_overlays.end())
{
delete i->second;
}
pOverlay->m_id = id; //记住id号码
m_overlays[id] = pOverlay;
}
HRESULT UpdateOverlay(SHORT key, BYTE* pYplane, BYTE* pVplane, BYTE* pUplane)
{
CAutoLock lock(&m_lock);
OverlayMap::iterator i = m_overlays.find(key);
if (i != m_overlays.end())
{
BaseBitmapOverlay* baseBitamp = dynamic_cast(i->second);
return baseBitamp->Update(pYplane, pVplane, pUplane);
}
return E_INVALIDARG;
}
void RemoveOverlay(SHORT id)
{
CAutoLock lock(&m_lock);
OverlayMap::iterator i = m_overlays.find(id);
if (i != m_overlays.end())
{
delete i->second;
m_overlays.erase(i);
}
}
void Draw()
{
CAutoLock lock(&m_lock);
if(IsEmpty())
{
return;
}
for each(pair pair in m_overlays )
{
pair.second->Draw();
}
}
bool IsEmpty()
{
//CAutoLock lock(&m_lock);
return m_overlays.empty();
}
void RemoveAll()
{
CAutoLock lock(&m_lock);
if(m_overlays.empty())
return;
//FontFactory::Get()->Clear();
for (OverlayMap::iterator i = m_overlays.begin(); i != m_overlays.end(); ++i)
{
delete i->second;
}
m_overlays.clear();
}
HRESULT UpdateOverlayOpacity( SHORT key, BYTE opacity )
{
CAutoLock lock(&m_lock);
OverlayMap::iterator i = m_overlays.find(key);
if (i != m_overlays.end())
{
BaseBitmapOverlay* baseBitamp = dynamic_cast(i->second);
baseBitamp->UpdateOpacity(opacity);
return S_OK;
}
return E_INVALIDARG;
}
//释放所有资源
void ReleaseOverlay()
{
CAutoLock lock(&m_lock);
OverlayMap::iterator iter = m_overlays.begin();
while(iter!=m_overlays.end())
{
iter->second->ReleaseObject();
iter++;
}
}
//恢复所有资源
void RestoreOverlay(IDirect3DDevice9* device,int newWidth,int newHeight)
{
CAutoLock lock(&m_lock);
OverlayMap::iterator iter = m_overlays.begin();
while(iter!=m_overlays.end())
{
iter->second->ReCreate(device,newWidth,newHeight);
iter++;
}
}
private:
OverlayMap m_overlays;
CCritSec m_lock;
};