Functions.LoadPicture 方法 | |
-- todo --
创建位图、图标或Windows元文件的对象引用。
命名空间:
XSharp.VFP
程序集:
XSharp.VFP (在 XSharp.VFP.dll 中) 版本:2.22 GA
语法 FUNCTION LoadPicture(
cFileName
) AS Object CLIPPER
[ClipperCallingConventionAttribute(new string[] { ... })]
public static Object LoadPicture(
__Usual cFileName = default
)
查看代码参数
- cFileName (Optional)
- 类型:__Usual
指定要为其创建对象的磁盘上的图像文件。
支持的图像类型列在备注部分
返回值
类型:
Object
Object
LoadPicture() 函数返回一个
Picture类型的COM对象引用,可以分配给ActiveX控件和VFP的Image对象的
PictureVal属性。
然而,真正的主要接口
iPicture只能使用如下代码检索:
1oIPicture = GETINTERFACE(LoadPicture(GetPict()), "iPicture")
备注 支持的图像类型图像类型组 | 文件扩展名 |
---|
位图 | .bmp, .jpg, .jpeg, .jpe, .jfif, .gif, .giff, .gfa |
图标 | .ico |
Windows元文件 | .wmf |
Windows增强型元文件 | .emf |
光标 | .cur |
以下限制(在VFP 9 SP2和OlePro32.dll版本6.0.6002.18005上测试)存在:
已知限制图像类型组 | 限制和问题 |
---|
位图 |
加载.tif和.png格式会导致OLE错误。
|
图标 |
允许的图标大小最大为128x128,且颜色不得超过256色。即使图标文件中存储了多个图标,
始终只显示最小的图标。图标文件可以包含更多的图标 - 甚至可以有更多颜色和更大尺寸 - 只要其中至少有
一个符合上述规则,就不会抛出异常。
|
光标 |
光标文件的大小不得超过1K,否则会产生OLE错误。包含16色光标的光标文件可以完美加载,
但只支持单色输出。加载动画光标(.ani)会引发OLE错误。
|
空图片支持
如果省略cFileName,则返回"空图片"。
您可以将GetPict()作为cFileName包含在内,以显示"打开"对话框,从中可以选择位图文件。
Picture对象为位图、图标和元文件提供了一个与语言无关的抽象。
与标准字体对象一样,系统提供了Picture对象的标准实现。
它的主要接口是
iPicture和
iPictureDisp。
Picture对象是通过
OleCreatePictureIndirect创建的,并支持
iPicture和
iPictureDisp接口。
OLE提供的Picture对象实现了
iPicture和
iPictureDisp接口的完整语义。
换句话说,除了
iPicture之外,没有必要使用其他接口!
LoadPicture() 函数在内部封装了
OleAut32.dll中实现的
OleCreatePictureIndirect() 函数。
因此,您可以在网上读到的关于该函数的所有内容(上述和在线内容)也适用于VFP的
LoadPicture() 函数。
LoadPicture() 函数被添加到VFP的词汇表中,以便更容易加载具有COM接口的图像,
许多ActiveX控件的表现属性需要这些COM接口来设置。
例如,ActiveX Outline控件有一个
PictureOpen属性,需要COM对象图像引用来设置。
LoadPicture() 函数返回的COM对象隐藏了其主要接口
iPicture。
与
LoadPicture() 函数返回的引用相比,OLE图像的主要
iPicture接口是唯一完全功能的接口。
换句话说,只有
iPicture可以在VFP程序中使用而不会产生任何OLE错误。
因为
iPicture是Picture的超集,所以可能更好,它应该在任何地方使用,而不是使用
LoadPicture() 函数返回的接口!
下面示例部分中的示例#1证明了将哪个OLE图像接口分配给
oIMAGE.PICTUREVAL属性并没有区别。
iPicture接口的IID定义为"{7BF80980-BF32-101A-8BBB-00AA00300CAB}"。
以下两行代码都创建了一个空图片OLE图像对象:
1oIPicture1 = GETINTERFACE(LoadPicture(), "iPicture")
2oIPicture2 = CreateObjectEx("StdPicture","","{7BF80980-BF32-101A-8BBB-00AA00300CAB}")
接口成员
下表总结了
iPicture接口的PEM。
iPicture 接口成员PEM | 名称 | 用途 | 值类型 | 读/写 |
---|
属性 | Attributes | 图片当前的位属性集。 | DWORD (int) | 只读 |
属性 | CurDC | 此图片当前选入的设备上下文。 | HDC (long) | 只读 |
属性 | Handle | 此图片对象管理的图片句柄。 | OLE_Handle (int) | 只读 |
属性 | Height | 图片对象中图片的当前高度。 | OLE_XSIZE_HIMETRIC (long) | 只读 |
属性 | hPal | 图片的当前调色板(如果有)。 | OLE_Handle (int) | 读/写 |
属性 | KeepOriginalFormat | 图片对象的KeepOriginalFormat属性的当前值。 | Bool | 读/写 |
属性 | Type | 图片的当前类型。 | Short (int) | 只读 |
属性 | Width | 图片对象中图片的当前宽度。 | OLE_XSIZE_HIMETRIC (long) | 只读 |
方法 | PictureChanged() | 通知图片对象其图片资源已更改。 |
方法 | Render() | 在指定的设备上下文上绘制图片的指定部分,定位在指定位置。 |
方法 | SaveAsFile() | 以与保存到文件相同的格式将图片数据保存到流中。 |
方法 | SelectPicture() |
将位图图片选入给定的设备上下文,返回图片先前选入的设备上下文
以及图片的句柄。
|
Attributes属性据说保存了图片的位属性。实际上,只有两个可以单独设置或叠加的值。
下表列出了两个可能的值:
iPicture.Attributes 枚举常量 | 描述 | 值 |
---|
PICTURE_SCALABLE |
图片对象是可缩放的,这意味着它可以以与最初创建图片时不同的大小重新绘制。
基于元文件的图片被认为是可缩放的;图标和位图图片虽然可以缩放,但不表达此属性,因为两者都涉及位图
拉伸而不是真正的缩放。
| 1 |
PICTURE_TRANSPARENT |
图片对象包含具有透明区域的图像,因此绘制图片不一定会填充其占据的矩形中的所有空间。
元文件和图标图片具有此属性;位图图片没有。
| 2 |
下表列出了
iPicture.Type属性的所有可能值:
常量 | 描述 | 值 |
---|
PICTYPE_UNINITIALIZED | 图片对象当前未初始化。此值在VFP中永远不会返回。 | -1 |
PICTYPE_NONE | 要创建一个没有初始化状态的新图片对象。如果使用不带参数的LoadPicture(),VFP会返回此值。 | 0 |
PICTYPE_BITMAP | 图片类型是位图。 | 1 |
PICTYPE_METAFILE | 图片类型是元文件。 | 2 |
PICTYPE_ICON | 图片类型是图标。 | 3 |
PICTYPE_ENHMETAFILE | 图片类型是增强型元文件。 | 4 |
COM图像对象的
iPicture接口最有趣的方法是
render(),它只有在调用
iPicture接口时才能完美工作。
下表总结了
render()方法的参数:
参数 | 用途 | 比例单位 |
---|
hdc | 要在其上渲染图像的设备上下文的句柄。 |
x | 在 hdc 中放置渲染图像的水平坐标(输出矩形左上角的 X 位置)。 | 像素 |
y | 在 hdc 中放置渲染图像的垂直坐标(输出矩形左上角的 Y 位置)。 | 像素 |
cx | 目标矩形的水平尺寸(输出矩形的宽度)。 | 像素 |
cy | 目标矩形的垂直尺寸(输出矩形的高度)。 | 像素 |
xSrc | 源图片中开始复制的水平偏移量。 | HiMetric |
ySrc | 源图片中开始复制的垂直偏移量。 | HiMetric |
cxSrc | 从源图片复制的水平范围(图像源裁剪区域的宽度和读取方向)。 | HiMetric |
cySrc | 从源图片复制的垂直范围(图像源裁剪区域的高度和读取方向)。 | HiMetric |
lprcWBounds |
如果 hdc 是元文件设备上下文,lprcWBounds 参数指向一个 RECTL 结构,指定底层元文件中的边界矩形。
该矩形结构包含窗口范围和窗口原点。这些值对绘制元文件很有用。
lprcBounds 指示的矩形嵌套在这个 lprcWBounds 矩形内;它们在同一坐标空间中。
如果 hdcDraw 不是元文件设备上下文,lprcWBounds 将为 NULL。如果 hdcDraw 是元文件设备上下文,lprcWBounds 不能为 NULL!
|
该方法返回标准值,如 E_FAIL、E_INVALIDARG 和 E_OUToFMEMORY,以及 S_OK、E_POINTER 和 CTL_E_INVALIDPROPERTYVALUE。
这些值在 MSDN 上有描述。
应用
render 函数有很多参数。其中一些传递像素值,其他传递 HiMetric 值。
示例 #3 包含一些有用的转换以及其他支持函数和定义。
要了解
render() 如何工作,请尝试以下 VFP 代码;将其逐行输入到 VFP 的命令窗口中:
直接输入到 VFP 的命令窗口
1* 定位一个大约 100 x 100 像素的图像
2goPic = LoadPicture(GetPict())
3gIP = GETINTERFACE(m.goPic, "iPicture")
4goForm = CreateObject("Form")
5goForm.Show()
6* 声明访问窗口的_客户区域_
7DECLARE Integer GetDC IN User32 integer HWnd
8* hDC 应该为 0(否则就是错误)!
9hDC = GetDC(goForm.HWnd)
10* 直接在表单的客户区域上渲染图像
11gIP.Render(m.hDC,0,0,100,100,0,gIP.Height,gIP.Width,- gIP.Height,NULL)
12* 声明释放函数
13DECLARE Integer ReleaseDC IN User32 integer HWnd, integer hDC
14* 下一行应该在表单的背景上打印 1 >> "Okay"
15? ReleaseDC(m.goForm.HWnd, m.hDC)
16* 声明访问_整个_窗口
17DECLARE Integer GetWindowDC IN User32 integer HWnd
18* hDC 现在也引用表单的标题和边框区域!
19hDC = GetWindowDC(goForm.HWnd)
20* 渲染部分覆盖表单的边框和标题
21gIP.Render(m.hDC,0,0,100,100,0,gIP.Height,gIP.Width,- gIP.Height,NULL)
22* 永远不要忘记释放分配的设备上下文
23? ReleaseDC(m.goForm.HWnd, m.hDC)
iPicture.Render() 适用于在其他不可访问的表单区域(如标题栏或窗口边框)上绘画。
直接渲染的另一个有趣应用源于不需要 VFP 对象引用就可以进行绘画的事实。
render() 方法仅使用通用的 Windows 句柄。因此,可以在任何已知的设备上下文上进行渲染。
如果遇到所谓的沙漏问题,使用基于 COM 的图像可能是首选的解决方法。
操作系统在长时间的磁盘访问期间显示沙漏鼠标光标。
有时 VFP 不能正确重置沙漏鼠标光标。
因此,用户仍然看到"忙碌工作"图标,尽管 VFP 已经空闲,只要她不触摸鼠标。
这些磁盘访问最常见的原因是刷新使用
Image.Picture 属性加载到本机 Image-Objects 中的图片。
相反,将 COM 内存基础对象存储到 Image-Object 的
.PictureVal 属性中,永远不会导致任何磁盘访问。
因此,刷新后不会再出现沙漏鼠标光标!
缺点iPicture.Render() 在 VFP 引擎之外执行其工作,并且不被 VFP 引擎注意到。
这就是为什么 VFP 不知道在"行间"绘制了什么。
每次 VFP 刷新我们刚刚渲染图片的表单区域时,都会清除我们的图像。
要使渲染输出持久化,必须采取措施防止 VFP 将其清除!
使用上述沙漏问题解决方法时,还需要注意另一个 BUG!
要看看会发生什么,请尝试以下代码:
1LOCAL lnLoop, oComPic1, oComPic2
2oComPic1 = LoadPicture(GetFile())
3oComPic2 = LoadPicture(GetFile())
4TRY
5_Screen.Addobject("oImage","IMAGE")
6CATCH
7FINALLY
8_Screen.oImage.Visible = .T.
9ENDTRY
10For lnLoop = 1 to 100
11_Screen.oImage.PictureVal = m.oComPic1
12_Screen.oImage.PictureVal = m.oComPic2
13Next
14*
15*\\ 而下一个循环会在某个地方中断:
16For lnLoop = 1 to 100
17_Screen.oImage.PictureVal = m.oComPic1
18_Screen.oImage.PictureVal = m.oComPic1
19_Screen.oImage.PictureVal = m.oComPic1
20_Screen.oImage.PictureVal = m.oComPic2
21Next
可以看到,第一个循环完美执行,而第二个循环在仅几次循环后就会中断,并显示"属性值无效"的错误消息!
这个 bug 很难追踪,只有在有人试图连续多次分配相同的 COM 引用时才会发生!
解决这个问题的方法是跟踪实际分配给 Image 的 PictureVal 属性的 COM 引用。
然后永远不要第二次重新分配相同的引用(用自身的副本覆盖第一个)!
顺便说一下:你使用哪个接口并不重要。
错误似乎源于 VFP 的 Image 类实例。
示例
以下示例显示 COM 图像实例的两个接口(
Picture 和
IPicture)
都可以分配给 VFP 的
Image.PictureVal 属性:
1PUBLIC goPic AS Object, goIPic AS Object
2goPic = LoadPicture(GetPict())
3goIPic = GETINTERFACE(m.goPic, "iPicture")
4_SCREEN.AddObject("oPic1","IMAGE")
5_SCREEN.AddObject("oPic2","IMAGE")
6WITH _SCREEN.oPic1
7.VISIBLE = .T.
8.PICTUREVAL = m.goPic
9ENDWITH
10WITH _SCREEN.oPic2
11.LEFT = 110
12.VISIBLE = .T.
13.PICTUREVAL = m.goIPic
14ENDWITH
15HIDE WINDOWS ALL
16WAIT "按任意键..."
17_SCREEN.RemoveObject("oPic1")
18_SCREEN.RemoveObject("oPic2")
19STORE NULL TO goPic, goIPic
20Clear
21SHOW WINDOWS ALL
以下示例展示了如何查询 COM 图像实例的
iPicture 接口。
有意地,这个演示代码中没有 Try…Catch…Endtry 部分,所以可能会发生 OLE 错误。
你需要用不同的图像类型多次运行这个代码片段才能看到它们。
1goPic = LoadPicture(GetPict())
2IF VARTYPE(m.goPic) == "O"
3goIPic = GETINTERFACE(m.goPic, "iPicture")
4IF VARTYPE(m.goIPic) == "O"
5Clear
6WITH m.goIPic
7?
8? "'IPicture' 接口的属性:"
9? "Attributes" ,.Attributes
10*\\ 如果加载的是图标,下一行将失败
11? "CurDC" ,.CurDC
12? "Handle" ,.Handle
13? "Height" ,.Height
14*\\ 如果加载的是图标,下一行将失败
15? "hPal" ,.hPal
16? "KeepOriginalFormat",.KeepOriginalFormat
17? "Type" ,.Type
18? "Width" ,.Width
19?
20ENDWITH
21ENDIF
22ENDIF
以下代码是在编程 OLE 图像对象时很有用的支持函数和声明的集合。
1* 支持函数和定义
2#DEFINE INCH2MILLIMETER 25.4
3#DEFINE INCH2HIMETRICS (INCH2MILLIMETER * 100)
4
5FUNCTION PXL2HIME(tnPixel AS Integer) AS Integer
6 * 像素到HiMetric转换
7 LOCAL lnPixelsOnOneHiMetricUnit AS Integer
8 lnPixelsOnOneHiMetricUnit = INCH2HIMETRICS / GetDPI()
9 RETURN ROUND(lnPixelsOnOneHiMetricUnit * m.tnPixel, 0)
10ENDFUNC
11
12FUNCTION HIME2PXL(tnHimetric AS Integer) AS Integer
13 * HiMetric到像素转换
14 LOCAL lnPixelsOnOneHiMetricUnit AS Integer
15 lnPixelsOnOneHiMetricUnit = INCH2HIMETRICS / GetDPI()
16 RETURN ROUND(m.tnHimetric / m.lnPixelsOnOneHiMetricUnit, 0)
17ENDFUNC
18
19FUNCTION GetDPI(tnHDC AS Integer) AS Integer
20 * 获取每英寸点数分辨率
21 * 针对VFP的_SCREEN设备上下文
22 DECLARE Integer GetDeviceCaps IN GDI32 integer hdc, integer nIndex
23 DECLARE Integer GetWindowDC IN User32 integer HWnd
24 DECLARE Integer ReleaseDC IN User32 integer HWnd, integer hDC
25 * 为简单起见,我们假设X和Y维度
26 * 使用相同的DPI分辨率。
27 #DEFINE LOGPIXELSX 88
28 #DEFINE LOGPIXELSY 90
29 * 获取VFP的_Screen-hDC以计算分辨率:
30 LOCAL lhDC AS Integer, lnDPI AS Integer
31 STORE 0 TO lhDC, lnDPI
32 lhDC = GetWindowDC(_Screen.HWnd)
33
34 IF NOT m.lhDC = 0
35 lnDPI = GetDeviceCaps(m.lhDC, LOGPIXELSX)
36 ELSE
37 lnDPI = 96
38 ENDIF
39
40 * 释放设备上下文
41 = ReleaseDC(_Screen.HWnd, m.lhDC)
42 RETURN m.lnDPI
43ENDFUNC
44
45FUNCTION GetCanvas(tnHWND AS Integer, tlChild AS Boolean) AS Integer
46 * 获取窗口句柄的hDC(设备上下文句柄)
47 * tlChild = TRUE := 使用GetDC()
48 * tlChild = FALSE := 使用GetWindowDC()
49 DECLARE Integer GetDC IN User32 integer HWnd
50 DECLARE Integer GetWindowDC IN User32 integer HWnd
51 LOCAL lnHDC AS Integer
52
53 IF m.tlChild
54 lnHDC = GetDC(m.tnHWND)
55 ELSE
56 lnHDC = GetWindowDC(m.tnHWND)
57 ENDIF
58
59 RETURN m.lnHDC
60ENDFUNC
61
62FUNCTION ReleaseCanvas(tnHWND AS Integer, tnHDC AS Integer) AS Integer
63 * 释放借用的hDC
64 DECLARE Integer ReleaseDC IN User32 integer HWnd, integer hDC
65 RETURN ReleaseDC(m.tnHWND, m.tnHDC)
66ENDFUNC
67
68* 以下函数对于带滚动条的表单(oForm.Scrollbars > 0)不完全可靠
69FUNCTION GetChildAreaCanvas(tnhWnd AS Integer) AS Integer
70 * VFP的'顶级表单'(oForm.ShowWindow = 2)
71 * 在外部窗口内部有一个次级窗口。
72 * 这对于显示滚动条的表单也是如此!
73 LOCAL lnVfpHANDLE AS Integer, lnClienthWnd AS Integer, lnHDC AS Integer
74 * 将给定的Windows hWnd转换为内部VFP WHANDLE
75 lnVfpHANDLE = SYS(2326, m.tnhWnd)
76 * 获取指定X#父窗口的客户端窗口
77 * (WCLIENTWINDOW)的Windows hWnd
78 lnClienthWnd = SYS(2325, m.lnVfpHANDLE)
79
80 * 检查是否存在WCLIENTWINDOW
81 IF lnClienthWnd = lnVfpHANDLE
82 * 没有WCLIENTWINDOW,返回子窗口的客户区
83 lnHDC = GetCanvas(m.tnhWnd, .T.)
84 ELSE
85 * 存在WCLIENTWINDOW!
86 * 获取该窗口整个客户区的设备上下文句柄:
87 lnHDC = GetCanvas(lnClienthWnd)
88 ENDIF
89
90 RETURN m.lnHDC
91ENDFUNC
参见