win_wrap/
graphic.rs

1/*
2 * Copyright (c) 2024. The RigelA open source project team and
3 * its contributors reserve all rights.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software distributed under the
10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 * See the License for the specific language governing permissions and limitations under the License.
12 */
13
14use windows::Win32::Graphics::Gdi::{
15    BitBlt, CreateCompatibleBitmap, CreateCompatibleDC, DeleteDC, DeleteObject, GetDC, GetDIBits,
16    ReleaseDC, SelectObject,
17};
18pub use windows::Win32::Graphics::Gdi::{
19    BITMAPFILEHEADER, BITMAPINFO, BITMAPINFOHEADER, BI_BITFIELDS, BI_COMPRESSION, BI_JPEG, BI_PNG,
20    BI_RGB, BI_RLE4, BI_RLE8, BLACKNESS, CAPTUREBLT, COMPLEXREGION, DIB_PAL_COLORS, DIB_RGB_COLORS,
21    DIB_USAGE, DSTINVERT, HBITMAP, HDC, HGDIOBJ, MERGECOPY, MERGEPAINT, NOMIRRORBITMAP, NOTSRCCOPY,
22    NOTSRCERASE, NULLREGION, PATCOPY, PATINVERT, PATPAINT, RGBQUAD, ROP_CODE, SIMPLEREGION, SRCAND,
23    SRCCOPY, SRCERASE, SRCINVERT, SRCPAINT, WHITENESS,
24};
25
26use crate::{common::HWND, ext::ToBytesExt, memory::HeapMemory};
27
28impl ToBytesExt for BITMAPFILEHEADER {
29    type Input = Self;
30}
31
32impl ToBytesExt for BITMAPINFOHEADER {
33    type Input = Self;
34}
35
36impl ToBytesExt for RGBQUAD {
37    type Input = Self;
38}
39
40/**
41get_dc函数查询指定窗口的工作区或整个屏幕的设备上下文(DC)的句柄。可以在后续GDI函数中使用返回的句柄在 DC 中绘制。 设备上下文是一种不透明的数据结构,其值由 GDI 在内部使用。
42get_dc_ex 函数是 get_dc 的扩展,它使应用程序能够更好地控制在工作区中发生剪裁的方式和是否发生。
43如果函数成功,则返回值是指定窗口工作区的 DC 的句柄。
44如果函数失败,则返回值为 NULL。
45get_dc函数根据指定窗口的类样式检索公共、类或专用DC。对于类和专用DC,get_dc保留以前分配的属性不变。但是,对于常见的DC,get_dc在每次检索DC时都会将默认属性分配给DC。例如,默认字体为System,即位图字体。因此,get_dc返回的通用DC的句柄不会告诉你在绘制窗口时使用了哪种字体、颜色或画笔。若要确定字体,请调用get_text_face。
46请注意,DC 的句柄一次只能由单个线程使用。使用通用 DC 进行绘制后,必须调用 release_dc 函数来释放 DC。 类和专用 DC 不必释放。 release_dc 必须从调用 get_dc 的同一线程调用。 DC 的数量仅受可用内存的限制。
47*/
48pub fn get_dc(h_wnd: Option<HWND>) -> HDC {
49    unsafe { GetDC(h_wnd) }
50}
51
52/**
53create_compatible_dc 函数创建与指定设备兼容的内存设备上下文 (DC)。
54如果函数成功,则返回值是内存 DC 的句柄。
55如果函数失败,则返回值为 NULL。
56内存DC仅存在于内存中。创建内存DC时,其显示表面正好是一个单色像素宽和一个单色像素高。在应用程序可以使用内存DC进行绘图操作之前,它必须在DC中选择正确宽度和高度的位图。若要在DC中选择位图,请使用create_compatible_bitmap函数,指定所需的高度、宽度和颜色组织。
57创建内存 DC 时,所有属性都设置为正常默认值。存储器 DC 可以用作普通 DC。您可以设置属性;获取其属性的当前设置,然后选择钢笔、画笔和区域。
58create_compatible_dc 函数只能用于支持栅格操作的设备。应用程序可以通过调用 get_device_caps 函数来确定设备是否支持这些操作。
59当您不再需要内存 DC 时,请调用 delete_dc 函数。建议您调用 delete_dc 删除 DC。但是,您也可以使用 HDC 调用 delete_object 来删除 DC。
60如果 hdc 为 NULL,则调用 create_compatible_dc 的线程拥有创建的 HDC。当此线程被销毁时,HDC 将不再有效。因此,如果创建 HDC 并将其传递给另一个线程,然后退出第一个线程,则第二个线程将无法使用 HDC。
61国际化学(ICM):如果为图像颜色管理 (ICM) 启用了传递给此函数的 DC,则该函数创建的 DC 将启用 ICM。源和目标颜色空间在 DC 中指定。
62`h_dc` 现有 DC 的句柄。如果此句柄为 NULL,则该函数将创建与应用程序当前屏幕兼容的内存 DC。
63*/
64pub fn create_compatible_dc(h_dc: Option<HDC>) -> HDC {
65    unsafe { CreateCompatibleDC(h_dc) }
66}
67
68/**
69create_compatible_bitmap 函数创建与设备兼容的位图,该位图与指定的设备上下文相关联。
70如果函数成功,则返回值是兼容位图 (DDB) 的句柄。
71如果函数失败,则返回值为 NULL。
72create_compatible_bitmap 函数创建的位图的颜色格式与 h_dc 参数标识的设备的颜色格式匹配。 可以在与原始设备兼容的任何内存设备上下文中选择此位图。
73由于内存设备上下文同时允许彩色位图和单色位图,因此当指定的设备上下文为内存设备上下文时, create_compatible_bitmap 函数返回的位图格式会有所不同。 但是,为非内存设备上下文创建的兼容位图始终具有相同的颜色格式,并使用与指定设备上下文相同的调色板。
74注意:创建内存设备上下文时,它最初会选择一个 1 乘 1 的单色位图。 如果在 create_compatible_bitmap 中使用此内存设备上下文,则创建的位图是 单色 位图。 若要创建颜色位图,请使用用于创建内存设备上下文的 HDC ,如以下代码所示:
75```
76use win_wrap::graphic::{create_compatible_dc, get_dc, create_compatible_bitmap, select_object};
77let h_dc = get_dc(Default::default());
78let mem_dc = create_compatible_dc (Some(h_dc));
79let (n_width, n_height) = Default::default();
80let mem_bm = create_compatible_bitmap ( h_dc, n_width, n_height );
81select_object ( mem_dc, mem_bm.into() );
82```
83如果应用程序将 n_width 或 n_height 参数设置为零, create_compatible_bitmap 会将句柄返回到 1 x 1 像素的单色位图。
84如果将 DIB 节(由 create_dib_section 函数创建的位图)选入 h_dc 参数标识的设备上下文中, 则 create_compatible_bitmap 将创建 DIB 节。
85如果不再需要位图,请调用 delete_object 函数将其删除。
86`h_dc` 设备上下文的句柄。
87`cx` 位图宽度(以像素为单位)。
88`cy` 位图高度(以像素为单位)。
89*/
90pub fn create_compatible_bitmap(h_dc: HDC, cx: i32, cy: i32) -> HBITMAP {
91    unsafe { CreateCompatibleBitmap(h_dc, cx, cy) }
92}
93
94//noinspection SpellCheckingInspection
95/**
96select_object 函数在指定设备上下文(DC)中选择对象。 新 对象替换同一类型的上一个对象。
97如果所选对象不是区域且函数成功,则返回值是所替换对象的句柄。 如果所选对象是区域且函数成功,则返回值是以下值之一。
98SIMPLEREGION | 区域由单个矩形组成。
99COMPLEXREGION | 区域由多个矩形组成。
100NULLREGION | 区域为空。
101如果发生错误,并且所选对象不是区域,则返回值为 NULL。 否则,它将发生HGDI_ERROR。
102此函数返回指定类型的以前选择的对象。 应用程序在用新对象完成绘图后,应始终将新对象替换为原始的默认对象。
103应用程序不能一次选择一个位图进入多个 DC。
104Icm: 如果要选择的对象是画笔或笔,则执行颜色管理。
105`h_dc` DC 的句柄。
106`h` 要选择的对象的句柄。 指定的对象必须已使用以下函数之一创建。
107Bitmap | create_bitmap、 create_bitmap_indirect、 create_compatible_bitmap、 create_dib_bitmap、 create_dib_section | 位图只能选择到内存 DC 中。 不能将单个位图同时选入多个 DC。
108Brush | create_brush_indirect、 create_dib_pattern_brush、 create_dib_pattern_brush_pt、 create_hatch_brush、 create_pattern_brush、 create_solid_brush |
109Font | create_font、 create_font_indirect |
110Pen | create_pen、 create_pen_indirect |
111Region | combine_rgn、 create_elliptic_rgn、 create_elliptic_rgn_indirect、 create_polygon_rgn、 create_rect_rgn、 create_rect_rgn_indirect |
112*/
113pub fn select_object(h_dc: HDC, h: HGDIOBJ) -> HGDIOBJ {
114    unsafe { SelectObject(h_dc, h) }
115}
116
117//noinspection SpellCheckingInspection
118/**
119bit_blt函数执行与像素矩形相对应的颜色数据的位块传输,从指定的源设备上下文传输到目标设备上下文。
120bit_blt仅在目标 DC 上执行剪裁。
121如果旋转或剪切转换在源设备上下文中生效, bit_blt 将返回错误。 如果源设备上下文中存在其他转换 (并且匹配转换在目标设备上下文) 中无效,则根据需要拉伸、压缩或旋转目标设备上下文中的矩形。
122如果源和目标设备上下文的颜色格式不匹配, bit_blt 函数会将源颜色格式转换为与目标格式匹配。
123记录增强型图元文件时,如果源设备上下文标识增强型图元文件设备上下文,则会发生错误。
124并非所有设备都支持bit_blt函数。有关详细信息,请参阅get_device_caps函数中的RC_BITBLT光栅功能条目以及以下函数:mask_blt、plg_blt和stretch_blt。
125如果源和目标设备上下文表示不同的设备,bit_blt将返回错误。若要在不同设备的DC之间传输数据,请通过调用get_di_bits将内存位图转换为DIB。若要向第二台设备显示DIB,请调用set_di_bits或stretch_di_bits。
126Icm: 发生 blits 时,不执行颜色管理。
127`h_dc` 目标设备上下文的句柄。
128`x` 目标矩形左上角的 x 坐标(以逻辑单位为单位)。
129`y` 目标矩形左上角的 y 坐标(以逻辑单位为单位)。
130`cx` 源矩形和目标矩形的宽度(以逻辑单位为单位)。
131`cy` 源矩形和目标矩形的高度(以逻辑单位为单位)。
132`h_dc_src` 源设备上下文的句柄。
133`x1` 源矩形左上角的 x 坐标(以逻辑单位为单位)。
134`y1` 源矩形左上角的 y 坐标(以逻辑单位为单位)。
135`rop` 以下列表显示了一些常见的光栅操作代码。
136BLACKNESS | 使用与物理调色板中的索引 0 关联的颜色填充目标矩形。 (对于默认的物理调色板,该颜色为黑色。)
137CAPTUREBLT | 包括生成图像中窗口顶部分层的任何窗口。 默认情况下,图像仅包含你的窗口。 请注意,这通常无法用于打印设备上下文。
138DSTINVERT | 反转目标矩形。
139MERGECOPY | 使用布尔 AND 运算符将源矩形的颜色与 hdc_dest 中当前选择的画笔合并。
140MERGEPAINT | 使用布尔 OR 运算符将倒置源矩形的颜色与目标矩形的颜色合并。
141NOMIRRORBITMAP | 防止位图镜像。
142NOTSRCCOPY | 将反转源矩形复制到目标。
143NOTSRCERASE | 使用布尔 OR 运算符组合源矩形和目标矩形的颜色,然后反转生成的颜色。
144PATCOPY | 将 hdc_dest 中当前选择的画笔复制到目标位图中。
145PATINVERT | 使用布尔 XOR 运算符将 hdc_dest 中当前选择的画笔的颜色与目标矩形的颜色组合在一起。
146PATPAINT | 使用布尔 OR 运算符将 hdc_dest 中当前选择的画笔的颜色与反转源矩形的颜色相结合。 此操作的结果通过使用布尔 OR 运算符与目标矩形的颜色相结合。
147SRCAND | 使用布尔 AND 运算符组合源矩形和目标矩形的颜色。
148SRCCOPY | 将源矩形直接复制到目标矩形。
149SRCERASE | 使用布尔 AND 运算符将目标矩形的反转颜色与源矩形的颜色组合在一起。
150SRCINVERT | 使用布尔 XOR 运算符组合源矩形和目标矩形的颜色。
151SRCPAINT | 使用布尔 OR 运算符组合源矩形和目标矩形的颜色。
152WHITENESS | 使用与物理调色板中的索引 1 关联的颜色填充目标矩形。 (对于默认的物理调色板,该颜色为白色。)
153*/
154pub fn bit_blt(
155    h_dc: HDC,
156    x: i32,
157    y: i32,
158    cx: i32,
159    cy: i32,
160    h_dc_src: Option<HDC>,
161    x1: i32,
162    y1: i32,
163    rop: ROP_CODE,
164) -> bool {
165    unsafe { BitBlt(h_dc, x, y, cx, cy, h_dc_src, x1, y1, rop) }.is_ok()
166}
167
168/**
169release_dc 函数 (DC) 释放设备上下文,释放它供其他应用程序使用。 release_dc 函数的效果取决于 DC 的类型。 它仅释放公用 DC 和窗口 DC。 它对类或专用 DC 没有影响。
170返回值指示是否释放了 DC。 如果释放 DC,则返回值为 1。如果未释放 DC,则返回值为零。
171对于每次调用 get_window_dc 函数和每次调用查询公用 DC 的 get_dc 函数,应用程序都必须调用 release_dc 函数。
172应用程序不能使用 release_dc 函数释放通过调用 create_dc 函数创建的 DC;相反,它必须使用 delete_dc 函数。 release_dc 必须从调用 get_dc 的同一线程调用。
173`h_wnd` 要释放其 DC 的窗口的句柄。
174`h_dc` 要释放的 DC 的句柄。
175*/
176pub fn release_dc(h_wnd: Option<HWND>, h_dc: HDC) -> i32 {
177    unsafe { ReleaseDC(h_wnd, h_dc) }
178}
179
180/**
181delete_dc函数 (DC) 删除指定的设备上下文。
182应用程序不得删除通过调用 get_dc 函数获取其句柄的 DC。 相反,它必须调用 release_dc 函数来释放 DC。
183`h_dc` 设备上下文的句柄。
184*/
185pub fn delete_dc(h_dc: HDC) -> bool {
186    unsafe { DeleteDC(h_dc) }.as_bool()
187}
188
189/**
190delete_object函数删除逻辑笔、画笔、字体、位图、区域或调色板,从而释放与该对象关联的所有系统资源。删除对象后,指定的句柄将不再有效。
191当绘图对象(钢笔或画笔)仍处于DC中时,请勿将其删除。
192删除图案画笔时,不会删除与该画笔关联的位图。必须单独删除位图。
193`ho` 逻辑笔、画笔、字体、位图、区域或调色板的句柄。
194*/
195pub fn delete_object(ho: HGDIOBJ) -> bool {
196    unsafe { DeleteObject(ho) }.as_bool()
197}
198
199//noinspection SpellCheckingInspection
200/**
201get_di_bits函数查询指定兼容位图的位,并使用指定格式将其作为DIB复制到缓冲区中。
202如果DIB请求的格式与其内部格式匹配,则会复制位图的RGB值。如果请求的格式与内部格式不匹配,则会合成颜色表。下表描述了针对每种格式合成的颜色表。
2031_BPP | 颜色表由黑色和白色条目组成。
2044_BPP | 颜色表由标准 VGA 调色板相同的颜色组合组成。
2058_BPP | 颜色表由 GDI 定义的 256 种颜色的常规混合组成。 (包含在这 256 种颜色中的是默认逻辑调色板中的 20 种颜色。)
20624_BPP | 不返回颜色表。
207自下而上 DIB 是通过将高度设置为正数来指定的,而自上而下 DIB 是通过将高度设置为负数来指定的。 位图颜色表将追加到 BITMAPINFO 结构中。
208应用程序调用此函数时,不得在设备上下文中选择 由 h_bmp 参数标识的位图。
209自下而上 DIB 的原点是位图的左下角;自上而下 DIB 的原点为左上角。
210`h_dc` 设备上下文的句柄。
211`h_bm` 位图的句柄。 这必须是兼容位图 (DDB) 。
212`start` 要查询的第一个扫描行。
213`c_lines` 要检索的扫描行数。
214`usage` BITMAPINFO 结构的 bmiColors 成员的格式。 必须是以下值之一。
215DIB_PAL_COLORS | 颜色表应包含当前逻辑调色板中的 16 位索引数组。
216DIB_RGB_COLORS | 颜色表应包含文本红色、绿色、蓝色 (RGB) 值。
217*/
218pub fn get_di_bits(
219    h_dc: HDC,
220    h_bm: HBITMAP,
221    start: u32,
222    c_lines: u32,
223    usage: DIB_USAGE,
224) -> (Vec<Vec<RGBQUAD>>, BITMAPINFOHEADER, Option<Vec<RGBQUAD>>) {
225    unsafe {
226        let mut bmi: BITMAPINFO = std::mem::zeroed();
227        bmi.bmiHeader.biSize = size_of::<BITMAPINFOHEADER>() as u32;
228        let (pixels, color_table) = if GetDIBits(h_dc, h_bm, start, c_lines, None, &mut bmi, usage)
229            != 0
230        {
231            let heap = HeapMemory::default();
232            let ptr = heap.alloc(bmi.bmiHeader.biSizeImage as usize);
233            ptr.write_bytes(0, bmi.bmiHeader.biSizeImage as usize);
234            let bmi_ptr = heap.alloc(
235                (bmi.bmiHeader.biSize + bmi.bmiHeader.biClrUsed) as usize * size_of::<RGBQUAD>(),
236            );
237            bmi_ptr.write_bytes(
238                0,
239                (bmi.bmiHeader.biSize + bmi.bmiHeader.biClrUsed) as usize * size_of::<RGBQUAD>(),
240            );
241            let bmi_ptr2 = bmi_ptr as *mut BITMAPINFO;
242            bmi_ptr2.write(bmi);
243            let lines = GetDIBits(h_dc, h_bm, start, c_lines, Some(ptr), bmi_ptr2, usage);
244            let bmi = bmi_ptr2.read();
245            let color_table = if bmi.bmiHeader.biClrUsed > 0 {
246                let mut bmi_ptr2 = (bmi_ptr as *const u8)
247                    .wrapping_add(bmi.bmiHeader.biSize as usize)
248                    as *const RGBQUAD;
249                let mut arr = Vec::with_capacity(bmi.bmiHeader.biClrUsed as usize);
250                for _ in 0..bmi.bmiHeader.biClrUsed {
251                    arr.push(bmi_ptr2.read());
252                    bmi_ptr2 = bmi_ptr2.wrapping_add(1);
253                }
254                Some(arr)
255            } else {
256                None
257            };
258            heap.free(bmi_ptr);
259            let mut ptr2 = ptr as *mut RGBQUAD;
260            let mut data = Vec::with_capacity(lines as usize);
261            for _ in 0..lines {
262                let mut line = Vec::with_capacity(bmi.bmiHeader.biWidth as usize);
263                for _ in 0..bmi.bmiHeader.biWidth as usize {
264                    line.push(ptr2.read());
265                    ptr2 = ptr2.wrapping_add(1);
266                }
267                data.push(line);
268            }
269            heap.free(ptr);
270            (data, color_table)
271        } else {
272            (vec![], None)
273        };
274        (pixels, bmi.bmiHeader, color_table)
275    }
276}