分类

载入中...

日历

载入中...

登陆

载入中...

最新文章

载入中...

回复

载入中...

站点统计

载入中...

友情链接

Broland C++ Builder 中的 Gdiplus(GDI+)浅析2008-4-3 15:16:00
花了半个月时间,将GDI+熟悉了一下。GDI+是 Windows XP 中新增加的 GDI 加强版。GDI 是 Windows 中的图形设备接口,所有的绘图动作都通过它完成,由于老的 GDI 功能太落后,微软在 Windows XP 中使用了基于 .net 的新图形设备接口 GDI+ ,不仅扩充了强大的绘图能力,还封装了比较易用的 C++ 对象。这套标准的C++对象实际上是对 Windows XP 系统下的 Gdiplus.dll 的调用提供了一个封装接口。假如你熟悉 Gdiplus.dll 内部的 API 函数,则直接可以使用 GDI+ 功能而不需要这套 C++ 封装的标准对象。

    我熟悉 BCB6,一直在此环境下工作,原先又不熟悉GDI+,所以使用 GDI+ 只能靠着微软提供的标准 C++ 对象。经过很多试验,发现 BCB6 的编译器对微软的这套 C++ 封装水土不服,不仅产生许多冲突,很多地方根本就无法通过编译。

    在熟悉了一点之后,找到问题所在:微软提供的 GDI+ 的 C++ 封装和 BCB 的 VCL 有名称定义冲突,典型的就是 VCL 中有个 Graphics 集合,而 GDI+ 中有个 Graphics 对象,作为名字空间的 VCL 的 Graphics 集合让编译器区分不出源代码中的对象和集合的区别,所以产生无法避免的错误。这不能怪 Broland 的编译器不好,毕竟那时出了 GDI+ 没多久,C++ 的新标准颁布也不长,微软又爱赶时髦,经常出些别出心裁的东西,导致 Broland 的编译器赶不上了,也不知道是不是微软故意这样干的?

    熟悉GDI+之后,掌握它的工作原理就好办了。GDI+ 中的 Graphics 对象是个绘图工具,仅仅用来绘图,它不包含任何图形数据,创建它的目的就是利用它来进行绘图操作。

    它进行绘制操作的目标就是它所关联的一个图形对象,而这个对象既可以是窗体也可以是图片等图形数据的载体。所以 Graphics 对象的创建过程必须要和这个载体关联起来,然后这个新创建的 Graphics  对象的所有绘制动作都是针对它创建时关联的这个载体进行的。

    和微软的 VS 环境不同,在 BCB 中可以直接用 C++ 的标准 new 语法创建此对象。例如,创建一个窗体的Graphics 对象可以是这样的:Gdiplus::Graphics * myGP = new Gdiplus::Graphics(Form1->Handle, false); 。

    如此一来,这个新创建的 Graphics  对象就可以在 Form1 上乱涂乱画了。利用 Graphics  对象的方法,可以画圆、矩形、线条、图片(包括支持 Alpha 特性的高级图片)等等,这是 BCB 原来的 VCL 无法做到的。而 BCB 中原有的 VCL 中的图形载体组件,都可以利用它自己创建关联的 Graphics 对象来扩充自己的绘图能力。

    GDI+ 中的 Image 和 Bitmap 对象不仅仅支持了新的图形格式:PNG、GIF等,更加支持了 64bit 的彩色特性。仅 32bit 的彩色格式就可让每个象素带有自己专用的 Alpha 数据,这样每个象素点都能带有不同的透明度,绘图时很玄的!

   熟悉了GDI+之后,我们就可以改装 GDI+ 的C++封装包了。例如:有个 BCB 不能识别的问题,我么们看看 GDI+ Graphics 对象的构造函数申明定义:

    Graphics(IN HDC hdc)
    {
        GpGraphics *graphics = NULL;

        lastResult = DllExports::GdipCreateFromHDC(hdc, &graphics);

        SetNativeGraphics(graphics);
    };

    Graphics(IN HWND hwnd, IN BOOL icm = FALSE)
    {
        GpGraphics *graphics = NULL;
        if (icm)
        {
            lastResult = DllExports::GdipCreateFromHWNDICM(hwnd, &graphics);
        }
        else
        {
            lastResult = DllExports::GdipCreateFromHWND(hwnd, &graphics);
        }
        SetNativeGraphics(graphics);
    }

    在第二个声明中,使用了缺省值 icm = FALSE ,这样的话,在 BCB 中,编译器将 GDI+ 的 C++ 包装展开后,这两个构造函数就变成了:

    Graphics ( int hdc )

    Graphics ( int hwnd, bool icm = false )

    这是由于 HWND 和 HDC 原来都是 int 型数据的别名,一展开后,恢复了本质, BCB 就无法识别你的这句源码:

    Gdiplus::Graphics * myGP = new Gdiplus::Graphics(Form1->Handle);

    它不知道你想调用Graphics ( (int ) hdc ) 还是 Graphics ( (int) hwnd,  false )。我是在失败了很多次之后,才在跟踪执行中发现这个问题。所以,我将定义文件中的这句定义改成了:    Graphics(IN HWND hwnd, IN BOOL icm)才解决了这个问题。

    由此可见,类似的问题在 GDI+ 的 C++ 封装中还有很多。所以,在 BCB 中使用 GDI+ 时,不要使用函数的默认参数,否则编译器可能会调错函数。

    一个好的方法是将微软的 GDI+ 的标准 C++ 封装修改成 BCB 专用的定义封装。将所有 GDI+ 对象的名称前加一个前缀,这样就和 BCB 中的定义区分开了。然后将所有的缺省值取消掉,避免 BCB 的编译器无法识别它们。不过,这个工作量可是很大的哦!等有时间再慢慢来吧!

发表评论:
载入中...