点击或拖拽改变大小

Functions.LoadPicture 方法

X#
-- todo --
创建位图、图标或Windows元文件的对象引用。

命名空间:  XSharp.VFP
程序集:  XSharp.VFP (在 XSharp.VFP.dll 中) 版本:2.22 GA
语法
 FUNCTION LoadPicture(
	cFileName
) AS Object CLIPPER
查看代码

参数

cFileName (Optional)
类型:__Usual
指定要为其创建对象的磁盘上的图像文件。
支持的图像类型列在备注部分

返回值

类型:Object
Object LoadPicture() 函数返回一个Picture类型的COM对象引用,可以分配给ActiveX控件和VFP的Image对象的PictureVal属性。 然而,真正的主要接口iPicture只能使用如下代码检索:
 
X#
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对象的标准实现。 它的主要接口是iPictureiPictureDisp。 Picture对象是通过OleCreatePictureIndirect创建的,并支持iPictureiPictureDisp接口。 OLE提供的Picture对象实现了iPictureiPictureDisp接口的完整语义。 换句话说,除了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图像对象:
X#
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 的命令窗口
X#
 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!
要看看会发生什么,请尝试以下代码:
X#
 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 图像实例的两个接口(PictureIPicture) 都可以分配给 VFP 的 Image.PictureVal 属性
X#
 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 // "Picture" 接口
 9ENDWITH
10WITH _SCREEN.oPic2
11.LEFT = 110
12.VISIBLE = .T.
13.PICTUREVAL = m.goIPic // "IPicture" 接口
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 错误。 你需要用不同的图像类型多次运行这个代码片段才能看到它们。
X#
 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 图像对象时很有用的支持函数和声明的集合。
X#
 1* 支持函数和定义
 2#DEFINE INCH2MILLIMETER 25.4 // 1英寸 = 25.4毫米
 3#DEFINE INCH2HIMETRICS (INCH2MILLIMETER * 100) // 1 HIMETRIC = 0.01毫米
 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 // X方向的逻辑像素/英寸
28    #DEFINE LOGPIXELSY 90 // Y方向的逻辑像素/英寸
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 // 默认为96 DPI
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 = 271    * 在外部窗口内部有一个次级窗口。
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
参见