导航:
论坛 -> DELPHI技术
斑竹:liumazi,sephil
作者:
2018/6/11 0:24:12
标题:
有没有用过Direct2D的朋友,有个问题要请教,折磨了两天了。。
浏览:2135
加入我的收藏
楼主:
由于项目需求,需要在一边画图的时候,一遍加载其他图片,这个功能其实不难,我开始是准备用GDI做的,也做出来了,但是贴图效果很差,因为其中图片需要缩放显示。而且每张图片比较大,最小的一张也是2048*8000.总共是60多张图片,要像走马灯一样播放,功能很简单。后来我用Direct2D来做,画图效果非常好,移动非常平滑。但是有个问题,由于图片很多,所以不可能一次加载完。我是和GDI一样的做法,边显示图片,边在后台用线程在硬盘里读图片。由于图片很大,如果临时去读的话,画面显示就会卡顿。但是这个D2D有个问题,我在后台用线程读取普片的时候,前台画图居然会卡顿。。。。我也创建了离屏页面,还是不行。。不知道有没有高手知道原因
----------------------------------------------
C++ builder 用户前来摸鱼。。。
作者:
2018/6/11 9:02:37
1楼:
高手,您的代码,我们可以学习吗?
----------------------------------------------
(C)(P)Flying Wang
作者:
2018/6/11 9:43:51
2楼:
可以开个线程,把图片加载到内存里。 在程序启动的时候就可以做这事。 不影响你其他功能。 io操作一直都是串行的,卡是很正常的。
----------------------------------------------
--
作者:
2018/6/11 10:29:27
3楼:
可以放下这部分的代码出来学习学习吗·
----------------------------------------------
-
作者:
2018/6/11 11:22:47
4楼:
可以开个线程,把图片加载到内存里。 在程序启动的时候就可以做这事。 不影响你其他功能。 io操作一直都是串行的,卡是很正常的。 ----------
2048*8000*60,这个尺寸的JPG图片,如果把文件加载到内存,也没多大,如果把对应的BMP格式的数据,全部加载到内存,60张的话,要占用12G多的内存,这个我试过了,程序改成X64就行了,运行也没问题,但。。。现在买的工控机,就8G内存。。。。 图片加载和图片显示,如果只是从硬盘读取的话,是不会影响到前台显示的,这点我在用GDI作的时候就验证了,因为显示的图片,和后台加载的图片,根本就不是同一个JPG文件,不冲突。比如这时候我屏幕上滚动的是002.JPG,我就把001.JPG和003.JPG在硬盘上读出来,放到内存中,准备002.JPG画到边界的时候,好切换 还是我的D2D没有用好的,我昨天用FireMonkey作了一下实验,非常流畅,奇怪的是FMK也是用的D2D,我正在看它的源码。。。。
----------------------------------------------
C++ builder 用户前来摸鱼。。。
作者:
2018/6/11 11:29:29
5楼:
大锅,莫笑我。。。 EMB本来就对Direct2D做了封装,只不过对我这个项目而言,有的封装不合适,需要自己重新用API来做。。 https://www.cnblogs.com/del/category/290814.html 这里讲得很好了
----------------------------------------------
C++ builder 用户前来摸鱼。。。
作者:
2018/6/11 11:33:33
6楼:
void CHwndTarget::LoadPicFromFile() { HRESULT hr; IWICImagingFactory* iWicFactory; IWICBitmapDecoder* iWICDecoder; IWICBitmapFrameDecode* iWICFrameDecode; IWICFormatConverter* iFormatConverter; //{获取建立 WIC 的工厂} //CoCreateInstance(CLSID_WICImagingFactory,NULL,CLSCTX_INPROC_SERVER,IID_PPV_ARGS(&iWicFactory)); CoCreateInstance(CLSID_WICImagingFactory,NULL,CLSCTX_INPROC_SERVER,IID_IWICImagingFactory,(LPVOID*)&iWicFactory); //{打开并获取解码后的对象} iWicFactory->CreateDecoderFromFilename(m_bmpFileName.c_str(), NULL, GENERIC_READ, WICDecodeMetadataCacheOnLoad, &iWICDecoder); //{获取第一帧} iWICDecoder->GetFrame(0, &iWICFrameDecode); //{获取格式转换器} iWicFactory->CreateFormatConverter(&iFormatConverter); //{转换到与 D2D 兼容的格式} iFormatConverter->Initialize(iWICFrameDecode, GUID_WICPixelFormat32bppPBGRA, WICBitmapDitherTypeNone, NULL, 0, WICBitmapPaletteTypeMedianCut); UINT w,h; iFormatConverter->GetSize(&w,&h); m_bmpSize.width=w; m_bmpSize.height=h; //{获取 ID2D1Bitmap} m_pRenderTarget->CreateBitmapFromWicBitmap(iFormatConverter, NULL, &m_pBitmap); SAFE_RELEASE(iWicFactory); SAFE_RELEASE(iWICDecoder); SAFE_RELEASE(iWICFrameDecode); SAFE_RELEASE(iFormatConverter); } 这就是线程函数,读取一个硬盘上的图片,然后通过m_pRenderTarget,生成一个“ID2D1Bitmap”,画图的时候直接画这个ID2D1Bitmap就行了。 m_pRenderTarget->CreateBitmapFromWicBitmap(iFormatConverter, NULL, &m_pBitmap); 经过测试,这句话导致了我界面的卡顿,就这一句,但是我很奇怪,这代码是在线程里运行的,为什么会卡到我的UI界面。。。这行代码也没有占用任何UI线程的资源。。
----------------------------------------------
C++ builder 用户前来摸鱼。。。
作者:
2018/6/11 11:37:39
7楼:
整个函数中的所有代码,都没有占用到任何UI线程的资源,也不需要和UI线程同步什么的,但是就那一句给卡住了我的绘图大概10毫秒吧。。 所以我现在怀疑,在Direct2D中,占用某个Target时间过长,不管这个Target是不是和显示窗口关联了,都会造成其他Target等待,也就是说在Direct2D中,所有Target是按队列处理的???
----------------------------------------------
C++ builder 用户前来摸鱼。。。
作者:
2018/6/11 12:09:22
8楼:
工控机也是windows系统么?理论上,是windows这类高级系统就可以用硬盘换内存。 另外,你可以缓存wic图像,等到用的时候再解码也是一样的。 或者,我没记错的话,d2d是可以从istream里装载图像数据的。你可以先把图像装载到istream对象里
----------------------------------------------
--
作者:
2018/6/11 12:16:46
9楼:
代码我已经发在上面了,现在卡顿的原因,不在IO读取上,我把IO读取放在线程中,对UI线程没有任何影响。 不论用什么方式去装载图像,从硬盘也好,从内存也好,istream也好。如果想把图像绘制出来,必须经过下面这句 m_pRenderTarget->CreateBitmapFromWicBitmap(iFormatConverter, NULL, &m_pBitmap); 是这一句给卡住了,这个问题不是IO,是Direct2D的问题,我必须要找另外的途径去完成这条语句所完成的工作。。。
----------------------------------------------
C++ builder 用户前来摸鱼。。。
作者:
2018/6/11 12:48:49
10楼:
没有用过D2D,不知道啥原因。 屏幕就那么大,图片那么大,没有多少意义。 可以使用GDI+缩略图的方式: Graphics := TGPGraphics.Create(GetDC(Handle)); Image := TGPImage.Create(strImageFileName); pThumbnail:= Image.GetThumbnailImage(200, 200, nil, nil); Graphics.DrawImage(pThumbnail, rect); 供参考。
----------------------------------------------
武汉天气不好
作者:
2018/6/11 13:35:42
11楼:
没有用过D2D,不知道啥原因。 屏幕就那么大,图片那么大,没有多少意义。 可以使用GDI+缩略图的方式: Graphics := TGPGraphics.Create(GetDC(Handle)); Image := TGPImage.Create(strImageFileName); pThumbnail:= Image.GetThumbnailImage(200, 200, nil, nil); Graphics.DrawImage(pThumbnail, rect); 供参考。
谢代码。 图片不是我提供的,我只展示图片,图片很大的原因有很多,地图,卫星图等,都是大图片,还有其他应用也是,我想过了这关的话,以后大图片显示应该都没问题了,还是继续看FXM的源码吧,好多啊。。 能不能在VCL项目里,引用FXM的HPP文件?这样不是可以偷懒了??
----------------------------------------------
C++ builder 用户前来摸鱼。。。
作者:
2018/6/11 13:58:49
12楼:
CreateBitmapFromWicBitmap这个是调用wic的解码器解码图片的。。。 fmx如果不会的话,那估计要么就是用的gdi+要么就是参数问题,兰州要对比就直接找对应的代码就是了。
----------------------------------------------
--
作者:
2018/6/11 14:03:15
13楼:
HRESULT hr = S_OK; WCHAR * szFileName = new WCHAR[0x10000]; // Step 1: Create the open dialog box and locate the image file if (LocateImageFile(hWnd, szFileName, ARRAYSIZE(szFileName))) { // Step 2: Decode the source image // Create a decoder IWICBitmapDecoder *pDecoder = nullptr; hr = m_pIWICFactory->CreateDecoderFromFilename( szFileName, // Image to be decoded nullptr, // Do not prefer a particular vendor GENERIC_READ, // Desired read access to the file WICDecodeMetadataCacheOnDemand, // Cache metadata when needed &pDecoder // Pointer to the decoder ); // Retrieve the first frame of the image from the decoder IWICBitmapFrameDecode *pFrame = nullptr; if (SUCCEEDED(hr)) { hr = pDecoder->GetFrame(0, &pFrame); } //Step 3: Format convert the frame to 32bppPBGRA if (SUCCEEDED(hr)) { SafeRelease(m_pConvertedSourceBitmap); hr = m_pIWICFactory->CreateFormatConverter(&m_pConvertedSourceBitmap); } if (SUCCEEDED(hr)) { hr = m_pConvertedSourceBitmap->Initialize( pFrame, // Input bitmap to convert GUID_WICPixelFormat32bppPBGRA, // Destination pixel format WICBitmapDitherTypeNone, // Specified dither pattern nullptr, // Specify a particular palette 0.f, // Alpha threshold WICBitmapPaletteTypeCustom // Palette translation type ); } //Step 4: Create render target and D2D bitmap from IWICBitmapSource if (SUCCEEDED(hr)) { hr = CreateDeviceResources(hWnd); } if (SUCCEEDED(hr)) { // Need to release the previous D2DBitmap if there is one SafeRelease(m_pD2DBitmap); hr = m_pRT->CreateBitmapFromWicBitmap(m_pConvertedSourceBitmap, nullptr, &m_pD2DBitmap); } SafeRelease(pDecoder); SafeRelease(pFrame); } delete[] szFileName; return hr; 兰州试试这个代码看看效果。
----------------------------------------------
--
作者:
2018/6/12 12:44:19
14楼:
看FMX的代码看懵逼了。。有点眉目,但是有个关键的地方不明白啊。。 在fmx.canvas.d2d.pas中,有这样一个类: TD2DBitmapHandle = class private FTexture: ID3D10Texture2D; FSharedBitmap: ID2D1Bitmap; FAccess: TMapAccess; FMapBuffer: ID3D10Texture2D; FWidth: Integer; FHeight: Integer; FContextLostId: Integer; procedure ContextLostHandler(const Sender: TObject; const Msg: TMessage); public constructor Create(const AWidth, AHeight: Integer; const AAccess: TMapAccess); destructor Destroy; override; function CreateBitmap(const RenderTarget: ID2D1RenderTarget): ID2D1Bitmap; function Texture: ID3D10Texture2D; end; OK,这个类比较简单,没有从任何地方继承 后面关于这个类有一个调用,我就看不懂了。。 ABitmap: TBitmap; TD2DBitmapHandle(ABitmap.Handle).CreateBitmap(FTarget); TD2DBitmapHandle这是类名,后面括号里面的(ABitmap.Handle),这是什么意思啊?这个类的CReate不是需要3个参数吗??这里就用了一个参数,上面类的声明中,只有一个构造函数啊。。。 我delphi语法不行,谁帮我解释一下。。。
----------------------------------------------
C++ builder 用户前来摸鱼。。。
作者:
2018/6/12 13:49:51
15楼:
函数定义: function CreateBitmap(const RenderTarget: ID2D1RenderTarget): ID2D1Bitmap; 调用方式: TD2DBitmapHandle(ABitmap.Handle).CreateBitmap(FTarget); 这没毛病啊。。。
----------------------------------------------
虽千万人吾往矣!
作者:
2018/6/12 14:26:27
16楼:
CreateBitmap(FTarget);这我能理解,函数名(参数) TD2DBitmapHandle(ABitmap.Handle),这个我不是很明白,类名(参数),一般不是调用这个类的构造函数吗?但是这个类的构造函数有3个参数啊: constructor Create(const AWidth, AHeight: Integer; const AAccess: TMapAccess);
----------------------------------------------
C++ builder 用户前来摸鱼。。。
作者:
doubos (doubos)
★☆☆☆☆
-
盒子活跃会员
2018/6/12 15:45:07
17楼:
这是类型强转,在C++中的语法类似于(TD2DBitmapHandle)ABitmap.Handle
----------------------------------------------
-
作者:
2018/6/12 15:53:36
18楼:
明白了,继续看源代码。。。 有个问题,我按F7跟踪的时候,总是跳到很多汇编代码的地方,有没有办法让跟踪只跳转到我指定的某个文件? 比如跟踪的时候,只查看在fmx.canvas.d2d.pas运行的代码,其他文件的不看??
----------------------------------------------
C++ builder 用户前来摸鱼。。。
作者:
2018/6/12 20:56:47
19楼:
我特么是个SB啊~~~~~~~~~~
----------------------------------------------
C++ builder 用户前来摸鱼。。。
作者:
2018/6/12 20:56:58
20楼:
解决了。。。
----------------------------------------------
C++ builder 用户前来摸鱼。。。
作者:
2018/6/12 22:09:22
21楼:
解决就好,棒棒哒!
----------------------------------------------
-
作者:
2018/6/12 22:59:32
22楼:
怎么解决要保密。
----------------------------------------------
(C)(P)Flying Wang
作者:
2018/6/12 23:28:16
23楼:
createthread,问题出在这里,我原来在VC里直接用的,在我印象中,缩小了它对主线程的干扰,你们可以做个试验,在窗体上放一个timer,然后时间设置为1ms,虽然它的精度达不到,但是做这个试验够了。然后放一个Label,在定时器里更新label的数值,直接加1就行了,用来模拟画图。 放一个按钮,里面写上一个createthread,线程函数随便写点什么,也可以什么都不写。程序运行后,你可以快速点按钮,观察Label的变化,结果是非常流畅,没有什么变化。 关键来了,这时候尝试一下在线程函数中,再次调用线程,比如这样: void threadfun() { for(int i=0;i<10;i++) { createthread(); } } 在你创建的线程中,再次创建10个线程。 这时候再运行程序,快速点按钮,这次的效果是:你先创建一个线程,然后在这个线程中,分别再创建10个线程。可以看到label的变化很卡顿,你点按钮越快,卡顿越严重,即便是你线程函数中,什么都不写,直接return也是如此 我原来想当然的认为,这种操作,是不会影响到界面线程的,我一直认为createthread();是不占用任何主线程的。。。但是结果很悲催。。。 这个操作我还没有在VC里面试过,有空去试一下,也希望有人来解答一下这是为什么。。 解决办法就是,只创建一个线程,在这个线程中“串行”的去完成我要做的工作,就行了。。而不再每个任务分派一个线程了。。。
----------------------------------------------
C++ builder 用户前来摸鱼。。。
作者:
2018/6/12 23:30:16
24楼:
在线程中,创建新的线程,会影响到主线程,这个我原来是真的没想到。。。
----------------------------------------------
C++ builder 用户前来摸鱼。。。
作者:
2018/6/13 0:05:06
25楼:
刚发完帖子有点强迫症,去vc2008里面试了一下,好像没有发现这个问题。。。 void CMFCTestDlg::OnTimer(UINT_PTR nIDEvent) { CString t; static int p=0; t.Format(L"%d",p); p++; GetDlgItem(LBL_TEST)->SetWindowTextW(t); CDialog::OnTimer(nIDEvent); } void CMFCTestDlg::OnBnClickedButton1() { CreateThread(NULL,NULL,(LPTHREAD_START_ROUTINE)threadfun,this,NULL,NULL); } DWORD _stdcall CMFCTestDlg::threadfun() { for(int i=0;i<10;i++) { CreateThread(NULL,NULL,(LPTHREAD_START_ROUTINE)threadfun2,NULL,NULL,NULL); } return 0; } DWORD _stdcall CMFCTestDlg::threadfun2() { return 0; } 难道是c++ builder的内部对线程函数做了其他处理,导致主线程卡住了??
----------------------------------------------
C++ builder 用户前来摸鱼。。。
作者:
2018/6/13 11:29:36
26楼:
你们在DELPHI里面用同样的方法做这个实验,UI界面会卡住吗?
----------------------------------------------
C++ builder 用户前来摸鱼。。。
作者:
2018/6/13 14:05:21
27楼:
按说不会吧 一会试下
----------------------------------------------
我和我追逐的梦,擦肩而过
作者:
2018/6/14 14:14:11
28楼:
在D10.2.3中测试了,还真和楼主说的一样,但我后来发现,如果你不在IDE下运行,是没有问题的,应该是IDE的调试信息占用了被调试程序的CPU资源,造成卡顿,楼主不妨脱离IDE运行下试试你的代码? 还好,虚惊一场,要不,这真算是大BUG了
----------------------------------------------
我和我追逐的梦,擦肩而过
作者:
2018/6/14 17:24:55
29楼:
我刚在cb里面也试过了,确实,关闭IDE,然后运行程序,不管是debug还是release版本,就不会有卡顿了,被坑得不轻。。。难道以后写程序还要关闭IDE去看效果。。。。
----------------------------------------------
C++ builder 用户前来摸鱼。。。