DELPHI盒子
!实时搜索: 盒子论坛 | 注册用户 | 修改信息 | 退出
检举帖 | 全文检索 | 关闭广告 | 捐赠
技术论坛
 用户名
 密  码
自动登陆(30天有效)
忘了密码
≡技术区≡
DELPHI技术
lazarus/fpc/Free Pascal
移动应用开发
Web应用开发
数据库专区
报表专区
网络通讯
开源项目
论坛精华贴
≡发布区≡
发布代码
发布控件
文档资料
经典工具
≡事务区≡
网站意见
盒子之家
招聘应聘
信息交换
论坛信息
最新加入: jsuguo
今日帖子: 29
在线用户: 11
导航: 论坛 -> DELPHI技术 斑竹:liumazi,sephil  
作者:
男 draculamx (draculamx) ▲▲▲▲△ -
普通会员
2018/6/11 0:24:12
标题:
有没有用过Direct2D的朋友,有个问题要请教,折磨了两天了。。 浏览:2135
加入我的收藏
楼主: 由于项目需求,需要在一边画图的时候,一遍加载其他图片,这个功能其实不难,我开始是准备用GDI做的,也做出来了,但是贴图效果很差,因为其中图片需要缩放显示。而且每张图片比较大,最小的一张也是2048*8000.总共是60多张图片,要像走马灯一样播放,功能很简单。后来我用Direct2D来做,画图效果非常好,移动非常平滑。但是有个问题,由于图片很多,所以不可能一次加载完。我是和GDI一样的做法,边显示图片,边在后台用线程在硬盘里读图片。由于图片很大,如果临时去读的话,画面显示就会卡顿。但是这个D2D有个问题,我在后台用线程读取普片的时候,前台画图居然会卡顿。。。。我也创建了离屏页面,还是不行。。不知道有没有高手知道原因
----------------------------------------------
C++ builder 用户前来摸鱼。。。
作者:
男 wang_80919 (Flying Wang) ★☆☆☆☆ -
普通会员
2018/6/11 9:02:37
1楼: 高手,您的代码,我们可以学习吗?
----------------------------------------------
(C)(P)Flying Wang
作者:
男 bahamut8348 (leonna) ★☆☆☆☆ -
普通会员
2018/6/11 9:43:51
2楼: 可以开个线程,把图片加载到内存里。
在程序启动的时候就可以做这事。
不影响你其他功能。

io操作一直都是串行的,卡是很正常的。
----------------------------------------------
--
作者:
男 hhjj3388 (谁杀了我的牛) ▲▲▲▲△ -
普通会员
2018/6/11 10:29:27
3楼: 可以放下这部分的代码出来学习学习吗·
----------------------------------------------
-
作者:
男 draculamx (draculamx) ▲▲▲▲△ -
普通会员
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 用户前来摸鱼。。。
作者:
男 draculamx (draculamx) ▲▲▲▲△ -
普通会员
2018/6/11 11:29:29
5楼:
高手,您的代码,我们可以学习吗?


大锅,莫笑我。。。

EMB本来就对Direct2D做了封装,只不过对我这个项目而言,有的封装不合适,需要自己重新用API来做。。

https://www.cnblogs.com/del/category/290814.html
这里讲得很好了
----------------------------------------------
C++ builder 用户前来摸鱼。。。
作者:
男 draculamx (draculamx) ▲▲▲▲△ -
普通会员
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 用户前来摸鱼。。。
作者:
男 draculamx (draculamx) ▲▲▲▲△ -
普通会员
2018/6/11 11:37:39
7楼: 整个函数中的所有代码,都没有占用到任何UI线程的资源,也不需要和UI线程同步什么的,但是就那一句给卡住了我的绘图大概10毫秒吧。。

所以我现在怀疑,在Direct2D中,占用某个Target时间过长,不管这个Target是不是和显示窗口关联了,都会造成其他Target等待,也就是说在Direct2D中,所有Target是按队列处理的???
----------------------------------------------
C++ builder 用户前来摸鱼。。。
作者:
男 bahamut8348 (leonna) ★☆☆☆☆ -
普通会员
2018/6/11 12:09:22
8楼: 工控机也是windows系统么?理论上,是windows这类高级系统就可以用硬盘换内存。

另外,你可以缓存wic图像,等到用的时候再解码也是一样的。

或者,我没记错的话,d2d是可以从istream里装载图像数据的。你可以先把图像装载到istream对象里
----------------------------------------------
--
作者:
男 draculamx (draculamx) ▲▲▲▲△ -
普通会员
2018/6/11 12:16:46
9楼: 代码我已经发在上面了,现在卡顿的原因,不在IO读取上,我把IO读取放在线程中,对UI线程没有任何影响。

不论用什么方式去装载图像,从硬盘也好,从内存也好,istream也好。如果想把图像绘制出来,必须经过下面这句
m_pRenderTarget->CreateBitmapFromWicBitmap(iFormatConverter, NULL, &m_pBitmap);

是这一句给卡住了,这个问题不是IO,是Direct2D的问题,我必须要找另外的途径去完成这条语句所完成的工作。。。
----------------------------------------------
C++ builder 用户前来摸鱼。。。
作者:
男 dbyoung (dbyoung) ★☆☆☆☆ -
普通会员
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);

供参考。
----------------------------------------------
武汉天气不好
作者:
男 draculamx (draculamx) ▲▲▲▲△ -
普通会员
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 用户前来摸鱼。。。
作者:
男 bahamut8348 (leonna) ★☆☆☆☆ -
普通会员
2018/6/11 13:58:49
12楼: CreateBitmapFromWicBitmap这个是调用wic的解码器解码图片的。。。
fmx如果不会的话,那估计要么就是用的gdi+要么就是参数问题,兰州要对比就直接找对应的代码就是了。
----------------------------------------------
--
作者:
男 bahamut8348 (leonna) ★☆☆☆☆ -
普通会员
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;



兰州试试这个代码看看效果。
----------------------------------------------
--
作者:
男 draculamx (draculamx) ▲▲▲▲△ -
普通会员
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 用户前来摸鱼。。。
作者:
男 looper (keyo) ★☆☆☆☆ -
盒子活跃会员
2018/6/12 13:49:51
15楼: 函数定义:
function CreateBitmap(const RenderTarget: ID2D1RenderTarget): ID2D1Bitmap;

调用方式:
TD2DBitmapHandle(ABitmap.Handle).CreateBitmap(FTarget);

这没毛病啊。。。
----------------------------------------------
虽千万人吾往矣!
作者:
男 draculamx (draculamx) ▲▲▲▲△ -
普通会员
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
----------------------------------------------
-
作者:
男 draculamx (draculamx) ▲▲▲▲△ -
普通会员
2018/6/12 15:53:36
18楼: 明白了,继续看源代码。。。
有个问题,我按F7跟踪的时候,总是跳到很多汇编代码的地方,有没有办法让跟踪只跳转到我指定的某个文件?

比如跟踪的时候,只查看在fmx.canvas.d2d.pas运行的代码,其他文件的不看??
----------------------------------------------
C++ builder 用户前来摸鱼。。。
作者:
男 draculamx (draculamx) ▲▲▲▲△ -
普通会员
2018/6/12 20:56:47
19楼: 我特么是个SB啊~~~~~~~~~~
----------------------------------------------
C++ builder 用户前来摸鱼。。。
作者:
男 draculamx (draculamx) ▲▲▲▲△ -
普通会员
2018/6/12 20:56:58
20楼: 解决了。。。
----------------------------------------------
C++ builder 用户前来摸鱼。。。
作者:
男 bbnn38 (伟大的咸鱼) ★☆☆☆☆ -
普通会员
2018/6/12 22:09:22
21楼: 解决就好,棒棒哒!
----------------------------------------------
-
作者:
男 wang_80919 (Flying Wang) ★☆☆☆☆ -
普通会员
2018/6/12 22:59:32
22楼: 怎么解决要保密。
----------------------------------------------
(C)(P)Flying Wang
作者:
男 draculamx (draculamx) ▲▲▲▲△ -
普通会员
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 用户前来摸鱼。。。
作者:
男 draculamx (draculamx) ▲▲▲▲△ -
普通会员
2018/6/12 23:30:16
24楼: 在线程中,创建新的线程,会影响到主线程,这个我原来是真的没想到。。。
----------------------------------------------
C++ builder 用户前来摸鱼。。。
作者:
男 draculamx (draculamx) ▲▲▲▲△ -
普通会员
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 用户前来摸鱼。。。
作者:
男 draculamx (draculamx) ▲▲▲▲△ -
普通会员
2018/6/13 11:29:36
26楼: 你们在DELPHI里面用同样的方法做这个实验,UI界面会卡住吗?
----------------------------------------------
C++ builder 用户前来摸鱼。。。
作者:
男 littlestone08 (littlestone08) ★☆☆☆☆ -
普通会员
2018/6/13 14:05:21
27楼: 按说不会吧 一会试下
----------------------------------------------
我和我追逐的梦,擦肩而过
作者:
男 littlestone08 (littlestone08) ★☆☆☆☆ -
普通会员
2018/6/14 14:14:11
28楼: 在D10.2.3中测试了,还真和楼主说的一样,但我后来发现,如果你不在IDE下运行,是没有问题的,应该是IDE的调试信息占用了被调试程序的CPU资源,造成卡顿,楼主不妨脱离IDE运行下试试你的代码?

还好,虚惊一场,要不,这真算是大BUG了
----------------------------------------------
我和我追逐的梦,擦肩而过
作者:
男 draculamx (draculamx) ▲▲▲▲△ -
普通会员
2018/6/14 17:24:55
29楼: 我刚在cb里面也试过了,确实,关闭IDE,然后运行程序,不管是debug还是release版本,就不会有卡顿了,被坑得不轻。。。难道以后写程序还要关闭IDE去看效果。。。。
----------------------------------------------
C++ builder 用户前来摸鱼。。。
信息
登陆以后才能回复
Copyright © 2CCC.Com 盒子论坛 v3.0.1 版权所有 页面执行54.6875毫秒 RSS