您当前的位置: 首页 > 

qianbo_insist

暂无认证

  • 0浏览

    0关注

    399博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

directx和opengl 电子白板

qianbo_insist 发布时间:2021-06-08 08:10:28 ,浏览量:0

画图板的几种方法

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; 
};
关注
打赏
1663161521
查看更多评论
立即登录/注册

微信扫码登录

0.0457s