wgc-python 2.0.0__tar.gz → 2.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,733 @@
1
+ Metadata-Version: 2.4
2
+ Name: wgc_python
3
+ Version: 2.1.0
4
+ Summary: Windows Graphics Capture 窗口捕获库 — BGRA numpy 帧、按需捕获、零拷贝 GPU 路径
5
+ Author: XuanChenxuan
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/XuanChenxuan/wgc_python
8
+ Project-URL: Repository, https://github.com/XuanChenxuan/wgc_python
9
+ Project-URL: BugTracker, https://github.com/XuanChenxuan/wgc_python/issues
10
+ Keywords: wgc,windows-graphics-capture,screen-capture,automation,game-capture
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: Microsoft :: Windows
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.8
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Multimedia :: Graphics :: Capture
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Requires-Python: >=3.8
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Requires-Dist: numpy
28
+ Requires-Dist: opencv-python
29
+ Dynamic: license-file
30
+
31
+ # wgc_python
32
+
33
+ [English](#english) | 简体中文
34
+
35
+ [![PyPI](https://img.shields.io/pypi/v/wgc-python)](https://pypi.org/project/wgc-python/)
36
+ [![Python](https://img.shields.io/pypi/pyversions/wgc-python)](https://pypi.org/project/wgc-python/)
37
+ [![License](https://img.shields.io/pypi/l/wgc-python)](LICENSE)
38
+
39
+ > **🚀 为 Python 自动化而生的窗口捕获库**
40
+ > 高帧率捕获 · 零资源待机 · 无视遮挡 · API 极简
41
+
42
+ ---
43
+
44
+ ## 为什么选择 wgc_python?
45
+
46
+ ### 🎯 专为自动化场景设计
47
+
48
+ 你是否在为以下问题困扰?
49
+
50
+ - **mss/BitBlt**:无法捕获被遮挡或后台窗口
51
+ - **PrintWindow**:性能瓶颈,固定 26ms+ 延迟
52
+ - **其他 WGC 封装**:持续运行占用资源,频繁启停开销巨大(50ms+)
53
+
54
+ **wgc_python 通过 Pause/Resume 机制解决了这个矛盾:**
55
+
56
+ ```python
57
+ # 传统方式:要么持续空转浪费资源,要么频繁启停承受延迟
58
+ start_capture() # 50ms 开销
59
+ get_frame() # 获取截图
60
+ stop_capture() # 销毁会话(50ms)
61
+ # 下次截图又要重新开始...
62
+
63
+ # wgc_python 方式:一次启动,按需截图,零开销待机
64
+ with WindowCapture("窗口", "类名") as cap:
65
+ while running:
66
+ frame = cap.capture_one() # auto Resume → 等待帧 → 拷贝 → Pause
67
+ # 处理图像...
68
+ ```
69
+
70
+ ### 📊 性能对比
71
+
72
+ | 方案 | FPS | 后台捕获 | CPU 占用 | 频繁切换开销 | 暂停后 GPU 占用 |
73
+ |------|-----|---------|---------|-------------|----------------|
74
+ | python-mss / BitBlt | ~60 | ❌ | 高 | 低 | N/A (无暂停概念) |
75
+ | PrintWindow | ~38 | ✅ | 中 | 低 | N/A (每次调用即捕获) |
76
+ | 其他 WGC 封装 | 高 | ✅ | 高(持续空转) | 高 (启停会话开销大) | 高 (无法真正暂停) |
77
+ | **wgc_python** | **高** | ✅ | **极低(Pause时归零)** | **极低(原子标志位)** | **归零(无 D3D 操作)** |
78
+
79
+ > 表中为定性对比,具体数值因硬件、窗口内容与场景而异,建议以自己的实测为准。
80
+
81
+ ### ✨ 核心优势
82
+
83
+ #### 1. 高帧率
84
+ - WGC 直接捕获 GPU 合成输出,不逐帧截屏,帧率上限远高于 PrintWindow 等 GDI 方案
85
+ - **双缓冲 Staging 纹理**:GPU 异步拷贝,读写互不阻塞
86
+ - **零拷贝友好**:`np.ndarray(strides=...)` 直接从 GPU 映射内存构造视图
87
+
88
+ #### 2. 智能资源管理
89
+ - **Pause/Resume 软暂停**:不销毁不重建 WGC session,仅原子标志位跳过帧处理
90
+ - **capture_one() 自动管理**:Resume → 等待帧 → 拷贝 → Pause,间隙 GPU 驱动零开销
91
+ - **会话复用**:避免频繁创建/销毁 D3D 设备的开销
92
+
93
+ #### 3. 极简 API
94
+ - **capture_one()**:一行代码完成按需捕获,返回 numpy 数组
95
+ - **get_frame()**:零拷贝裸指针路径(高级使用)
96
+ - **线程安全**:C++ 层处理所有多线程复杂性
97
+
98
+ #### 4. 多开并发
99
+ - 同一进程内可同时创建多个捕获会话,互不干扰
100
+ - 每个会话独立 D3D11 设备 + 独立纹理 + 独立 WinRT session,完全隔离
101
+ - 支持同窗口多路并发捕获
102
+
103
+ #### 5. 客户区精准裁剪(默认不截取标题栏/边框)
104
+ - **默认 `client_area_only=True`**:只捕获窗口客户区内容,自动裁剪标题栏和边框,直接输出有效像素
105
+ - **设置 `client_area_only=False`**:捕获整个窗口(含标题栏和边框),满足 UI 记录场景
106
+ - DPI 感知:自动修正高 DPI 缩放偏移,裁剪精度像素级
107
+ - GPU 级裁剪:`CopySubresourceRegion` 在 GPU 上完成裁剪,不浪费带宽和 CPU
108
+
109
+ #### 6. 光标捕获开关
110
+ - **默认 `capture_cursor=True`**:画面包含鼠标光标,与常规录屏行为一致
111
+ - **设置 `capture_cursor=False`**:画面不含鼠标指针,适合自动化 / 数据采集场景(也可用 `set_cursor_capture_enabled()` 运行时切换)
112
+ - 需 Windows 10 2004 (19041) 及以上系统,旧系统自动忽略该选项
113
+
114
+ #### 7. 无视遮挡
115
+ - 支持捕获被遮挡、最小化、后台窗口
116
+ - 完美适配游戏、桌面应用等各种场景
117
+
118
+ ---
119
+
120
+ ## 快速开始
121
+
122
+ ### 安装
123
+
124
+ 已发布至 PyPI,直接 pip 安装即可:
125
+
126
+ ```bash
127
+ pip install wgc_python
128
+ ```
129
+
130
+ ### 基础用法
131
+
132
+ ```python
133
+ from wgc_python import WindowCapture, enumerate_windows
134
+
135
+ # 枚举所有窗口
136
+ for title, class_name in enumerate_windows():
137
+ print(f"{title} ({class_name})")
138
+
139
+ # 按需捕获(推荐 —— 零开销待机)
140
+ with WindowCapture("窗口标题", "窗口类名") as cap:
141
+ frame = cap.capture_one() # BGRA numpy 数组,shape (h, w, 4)
142
+ if frame is not None:
143
+ print(f"捕获成功: {frame.shape}")
144
+
145
+ # 客户区裁剪演示
146
+ # 默认 client_area_only=True:只截取客户区,不含标题栏/边框
147
+ cap_client = WindowCapture("记事本", "Notepad") # 只截内容
148
+ cap_full = WindowCapture("记事本", "Notepad", client_area_only=False) # 含标题栏
149
+ frame_client = cap_client.capture_one() # 只有编辑区
150
+ frame_full = cap_full.capture_one() # 含标题栏 + 菜单 + 编辑区
151
+ cap_client.close()
152
+ cap_full.close()
153
+
154
+ # 不捕获鼠标光标(默认 capture_cursor=True,保持旧版行为)
155
+ cap = WindowCapture("记事本", "Notepad", capture_cursor=False)
156
+ frame = cap.capture_one() # 画面不含鼠标指针
157
+ cap.set_cursor_capture_enabled(True) # 也支持运行时切换
158
+ cap.close()
159
+ ```
160
+
161
+ ### 自动化最佳实践
162
+
163
+ ```python
164
+ from wgc_python import WindowCapture
165
+
166
+ cap = WindowCapture("游戏窗口", "UnityWndClass")
167
+
168
+ while True:
169
+ frame = cap.capture_one(timeout=1.0)
170
+ if frame is not None:
171
+ # frame 是 BGRA numpy 数组,直接用于 OpenCV/模板匹配
172
+ pass
173
+ time.sleep(1)
174
+
175
+ cap.close()
176
+ ```
177
+
178
+ ### 显示器捕获(推荐全屏游戏场景使用)
179
+
180
+ 捕获目标窗口**所在的显示器**而非窗口本身:捕获会话绑定显示器,**目标窗口在全屏/窗口之间切换不会使捕获会话失效**(窗口捕获模式下此场景会冻结于最后一帧)。
181
+
182
+ ```python
183
+ from wgc_python import WindowCaptureMonitor
184
+
185
+ # 定位目标窗口所在的显示器并捕获其全部内容(无客户区裁剪)
186
+ cap = WindowCaptureMonitor("游戏窗口", "UnityWndClass")
187
+ frame = cap.capture_one() # 整屏 BGRA 帧
188
+ cap.close()
189
+ ```
190
+
191
+ 适用场景:全屏游戏的长期自动化采集、目标窗口显示模式频繁变化的场景。需要裁剪客户区或排除其他窗口内容时,仍请使用 `WindowCapture`。
192
+
193
+ ### 零拷贝高级用法
194
+
195
+ ```python
196
+ from wgc_python import WindowCapture
197
+ import numpy as np
198
+ import ctypes
199
+
200
+ with WindowCapture("窗口", "类名") as cap:
201
+ cap.resume()
202
+ r = cap.get_frame() # (ptr, w, h, row_pitch) — GPU 映射裸指针
203
+ if r:
204
+ ptr, w, h, rp = r
205
+ arr = np.ndarray((h, w, 4), dtype=np.uint8,
206
+ buffer=(ctypes.c_ubyte * (h * rp)).from_address(ptr),
207
+ strides=(rp, 4, 1))
208
+ # arr 是 GPU 内存的零拷贝视图
209
+ cap.release_frame()
210
+ cap.pause()
211
+ ```
212
+
213
+ ### 实时显示
214
+
215
+ ```python
216
+ from wgc_python import WindowCapture
217
+ import cv2
218
+
219
+ with WindowCapture("窗口标题", "窗口类名") as cap:
220
+ while True:
221
+ frame = cap.capture_one()
222
+ if frame is not None:
223
+ cv2.imshow("Capture", cv2.cvtColor(frame, cv2.COLOR_BGRA2BGR))
224
+ if cv2.waitKey(1) & 0xFF == ord('q'):
225
+ break
226
+ cv2.destroyAllWindows()
227
+ ```
228
+
229
+ ---
230
+
231
+ ## API 参考
232
+
233
+ ```python
234
+ from wgc_python import (
235
+ WindowCapture, # 窗口捕获类(上下文管理器支持)
236
+ enumerate_windows, # 枚举所有可见窗口
237
+ get_last_error, # 获取最后错误信息(线程安全)
238
+ get_active_capture_count, # 获取活跃捕获数
239
+ )
240
+
241
+ # WindowCapture 类方法:
242
+ # cap = WindowCapture(title, class_name, client_area_only=True, capture_cursor=True)
243
+ #
244
+ # cap.capture_one(timeout=0.5) -> np.ndarray | None ★ 推荐
245
+ # 自动 Resume → 等待帧 → 拷贝为 numpy → Pause
246
+ # 捕获间隙 WGC 完全休眠,GPU 驱动零开销
247
+ #
248
+ # cap.get_frame() -> (ptr, w, h, row_pitch) | None
249
+ # cap.release_frame() # 释放 GPU 映射
250
+ # cap.pause() # 暂停捕获(零资源待机)
251
+ # cap.resume() # 恢复捕获
252
+ # cap.set_cursor_capture_enabled(enabled) # 运行时切换光标捕获
253
+ # cap.stop() # 停止帧到达
254
+ # cap.close() # 销毁会话
255
+ # cap.is_capturing() -> bool
256
+ # cap.is_paused() -> bool
257
+ # cap.get_frame_count() -> int
258
+ # cap.handle -> int (DLL handle)
259
+ ```
260
+
261
+ ---
262
+
263
+ ## 技术架构
264
+
265
+ ```
266
+ WGC捕获 → GPU Surface纹理
267
+ │
268
+ ┌────────▼────────┐
269
+ │ FrameArrived │
270
+ │ if pausing → ↑ │ ← Pause时直接返回,零 D3D 操作
271
+ └────────┬─────────┘
272
+ │
273
+ CopyResource (GPU异步复制)
274
+ ↓
275
+ ┌─────────────────────────┐
276
+ │ 双缓冲Staging纹理 │
277
+ │ [0] 写入 ←→ [1] 读取 │
278
+ │ m_textureInUse 防冲撞 │
279
+ └─────────────────────────┘
280
+ ↓
281
+ Map (永久映射 GPU 内存)
282
+ ↓
283
+ ┌────── 零拷贝输出 ───────┐
284
+ │ get_frame() │
285
+ │ 返回裸指针 → numpy零拷贝 │
286
+ │ 需手动 release_frame() │
287
+ └──────────────────────────┘
288
+
289
+ ┌────── 一键捕获 ──────────┐
290
+ │ capture_one() │
291
+ │ auto Pause/Resume │
292
+ │ 返回 numpy 数组 │
293
+ │ 间隙 GPU 驱动零开销 │
294
+ └──────────────────────────┘
295
+ ```
296
+
297
+ ### Pause/Resume 工作原理
298
+
299
+ ```
300
+ 用户调用 cap.pause()
301
+ │
302
+ m_isPaused = true ◄──── 原子标志位,微秒级
303
+ m_readableStagingIndex = -1
304
+ │
305
+ ┌────▼────────────────────────────────────────────┐
306
+ │ FrameArrived 回调(WGC 仍会触发) │
307
+ │ │
308
+ │ lock(mutex); │
309
+ │ if (m_isPaused) return; // ← 纯CPU判断,跳过│
310
+ │ // ↓ 以下只在 resume 后执行 ↓ │
311
+ │ CopyResource(staging, frame); │
312
+ │ m_readableStagingIndex = idx; │
313
+ │ unlock(mutex); │
314
+ └────▲────────────────┬───────────────────────────┘
315
+ │ │
316
+ 用户调用 cap.resume() MapFrame 检查 readableStagingIndex
317
+ m_isPaused = false <0 → 最近帧尚未就绪,返回 false
318
+
319
+ 不销毁 WGC session / 不重建 D3D 设备 / 不重新注册回调
320
+ → 恢复零延迟,无突刺
321
+ ```
322
+
323
+ ---
324
+
325
+ ## 文件结构
326
+
327
+ ```
328
+ wgc_python/
329
+ ├── wgc_python/ # Python 包
330
+ │ ├── __init__.py # Python API(ctypes FFI)
331
+ │ └── wgc_python.dll # 编译后的 DLL
332
+ ├── wgc_python_dll/ # C++ DLL 项目
333
+ │ ├── WGCWindowCapture.h/cpp # WGC 捕获核心(双缓冲 + 零拷贝)
334
+ │ ├── WGCExport.h/cpp # DLL 导出(含线程安全错误处理)
335
+ │ ├── D3DInterop.cpp # D3D11 设备互操作
336
+ │ ├── WindowEnumerator.h/cpp # 窗口枚举
337
+ │ ├── pch.h # 预编译头
338
+ │ └── packages/ # NuGet 包
339
+ ├── test.py # 功能测试
340
+ ├── demon.py # 多线程实时显示示例
341
+ ├── pyproject.toml # pip 构建配置
342
+ ├── BUILD.md / BUILD_EN.md # 构建说明(中/英)
343
+ ├── README.md / README_EN.md # 使用文档(中/英)
344
+ ├── CONTRIBUTING.md # 贡献指南
345
+ ├── CODE_OF_CONDUCT.md # 行为准则
346
+ ├── LICENSE # MIT 许可证
347
+ └── requirements.txt # Python 依赖
348
+ ```
349
+
350
+ ---
351
+
352
+ ## 系统要求
353
+
354
+ - Windows 10 1903+ (Build 18362),光标捕获开关需 2004+ (Build 19041)
355
+ - Python 3.8+
356
+
357
+ ---
358
+
359
+ ## 构建 DLL
360
+
361
+ 详见 [BUILD.md](BUILD.md)
362
+
363
+ ---
364
+
365
+ ## 故障排除
366
+
367
+ | 问题 | 解决方案 |
368
+ |------|---------|
369
+ | DLL 未找到 | 确保 `wgc_python.dll` 在正确位置 |
370
+ | 捕获失败 | 检查窗口是否可见,Windows 版本 >= 1903 |
371
+ | 中文路径保存失败 | 使用 `cv2.imencode` + `open().write()` 代替 `cv2.imwrite` |
372
+ | 依赖缺失 | `pip install numpy opencv-python` |
373
+
374
+ ---
375
+
376
+ ## 适用场景
377
+
378
+ - ✅ 游戏 AI / 自动化脚本
379
+ - ✅ RPA 流程自动化
380
+ - ✅ 屏幕录制 / 直播
381
+ - ✅ UI 自动化测试
382
+ - ✅ 计算机视觉应用
383
+
384
+ ---
385
+
386
+ ## 鸣谢
387
+
388
+ 本项目基于 [robmikh/Win32CaptureSample](https://github.com/robmikh/Win32CaptureSample) 开发。
389
+
390
+ ---
391
+
392
+ ## License
393
+
394
+ MIT License
395
+
396
+ ---
397
+
398
+ <a id="english"></a>
399
+ ## English
400
+
401
+ ### 🎯 Designed for Automation Scenarios
402
+
403
+ Are you struggling with these problems?
404
+
405
+ - **mss/BitBlt**: Cannot capture occluded or background windows
406
+ - **PrintWindow**: Performance bottleneck, fixed 26ms+ latency
407
+ - **Other WGC wrappers**: Continuous resource consumption, huge overhead for frequent start/stop (50ms+)
408
+
409
+ **wgc_python solves this dilemma with its Pause/Resume mechanism:**
410
+
411
+ ```python
412
+ # Traditional approach: Either waste resources or suffer latency
413
+ start_capture() # 50ms overhead
414
+ get_frame() # Get screenshot
415
+ stop_capture() # Destroy session (50ms)
416
+
417
+ # wgc_python approach: One-time init, on-demand capture, zero-overhead standby
418
+ with WindowCapture("Window", "Class") as cap:
419
+ while running:
420
+ frame = cap.capture_one() # auto Resume → wait → copy → Pause
421
+ # Process image...
422
+ ```
423
+
424
+ ### 📊 Performance Comparison
425
+
426
+ | Solution | FPS | Background Capture | CPU Usage | Toggle Overhead | GPU When Paused |
427
+ |----------|-----|-------------------|-----------|----------------|-----------------|
428
+ | python-mss / BitBlt | ~60 | ❌ | High | Low | N/A (no pause) |
429
+ | PrintWindow | ~38 | ✅ | Medium | Low | N/A (per-call) |
430
+ | Other WGC wrappers | High | ✅ | High (continuous) | High (session start/stop) | High (can't truly pause) |
431
+ | **wgc_python** | **High** | ✅ | **Near Zero (when paused)** | **Very Low (atomic flag)** | **Zero (no D3D ops)** |
432
+
433
+ > Qualitative comparison only; actual numbers vary by hardware, window content, and workload — benchmark on your own setup.
434
+
435
+ ### ✨ Core Advantages
436
+
437
+ #### 1. High Frame Rate
438
+ - WGC captures GPU composition output directly instead of per-frame GDI screenshots — frame rate ceiling far above PrintWindow-style approaches
439
+ - **Double-buffered Staging Texture**: GPU async copy, read/write non-blocking
440
+ - **Zero-copy path**: `np.ndarray(strides=...)` directly from GPU-mapped memory
441
+
442
+ #### 2. Smart Resource Management
443
+ - **Pause/Resume soft-pause**: Atomic flag only, no WGC session teardown
444
+ - **capture_one() auto management**: Resume → wait → copy → Pause, zero GPU driver overhead between captures
445
+ - **Session Reuse**: No frequent D3D device creation/destruction
446
+
447
+ #### 3. Minimalist API
448
+ - **capture_one()**: One-line on-demand capture, returns numpy array
449
+ - **get_frame()**: Zero-copy raw pointer path (advanced)
450
+ - **Thread safe**: C++ handles all multi-threading complexity
451
+
452
+ #### 4. Multi-Instance Capture
453
+ - Create multiple capture sessions simultaneously within one process
454
+ - Each session has its own D3D11 device, staging textures, and WinRT session — fully isolated
455
+ - Supports concurrent capture of the same window
456
+
457
+ #### 5. Client Area Precision (No Title Bar by Default)
458
+ - **Default `client_area_only=True`**: captures only window client area, automatically crops title bar and borders
459
+ - **Set `client_area_only=False`**: captures entire window including title bar and borders, for UI recording
460
+ - DPI-aware: automatic high-DPI scaling correction for pixel-perfect cropping
461
+ - GPU-level cropping via `CopySubresourceRegion`, no wasted bandwidth or CPU
462
+
463
+ #### 6. Cursor Capture Toggle
464
+ - **Default `capture_cursor=True`**: frame includes the mouse cursor, same as typical screen recording
465
+ - **Set `capture_cursor=False`**: frame excludes the mouse pointer, ideal for automation / data collection (also toggleable at runtime via `set_cursor_capture_enabled()`)
466
+ - Requires Windows 10 2004 (19041) or later; silently ignored on older systems
467
+
468
+ #### 7. Capture Behind Windows
469
+ - Supports capturing occluded, minimized, and background windows
470
+ - Perfect for games, desktop apps, and various scenarios
471
+
472
+ ---
473
+
474
+ ## Quick Start
475
+
476
+ ### Installation
477
+
478
+ Published on PyPI — install directly with pip:
479
+
480
+ ```bash
481
+ pip install wgc_python
482
+ ```
483
+
484
+ ### Basic Usage
485
+
486
+ ```python
487
+ from wgc_python import WindowCapture, enumerate_windows
488
+
489
+ # Enumerate all windows
490
+ for title, class_name in enumerate_windows():
491
+ print(f"{title} ({class_name})")
492
+
493
+ # On-demand capture (recommended — zero-resource standby)
494
+ with WindowCapture("Window Title", "WindowClass") as cap:
495
+ frame = cap.capture_one() # BGRA numpy array, shape (h, w, 4)
496
+ if frame is not None:
497
+ print(f"Captured: {frame.shape}")
498
+
499
+ # Client area demo
500
+ # Default client_area_only=True: content only, no title bar/borders
501
+ cap_client = WindowCapture("Notepad", "Notepad") # content only
502
+ cap_full = WindowCapture("Notepad", "Notepad", client_area_only=False) # with title bar
503
+ frame_client = cap_client.capture_one() # edit area only
504
+ frame_full = cap_full.capture_one() # title bar + menu + edit area
505
+ cap_client.close()
506
+ cap_full.close()
507
+
508
+ # Disable mouse cursor capture (default capture_cursor=True, same as previous versions)
509
+ cap = WindowCapture("Notepad", "Notepad", capture_cursor=False)
510
+ frame = cap.capture_one() # frame does not contain the mouse pointer
511
+ cap.set_cursor_capture_enabled(True) # runtime toggle is also supported
512
+ cap.close()
513
+ ```
514
+
515
+ ### Best Practice for Automation
516
+
517
+ ```python
518
+ from wgc_python import WindowCapture
519
+
520
+ cap = WindowCapture("Game Window", "UnityWndClass")
521
+
522
+ while True:
523
+ frame = cap.capture_one(timeout=1.0)
524
+ if frame is not None:
525
+ # frame is BGRA numpy array, ready for OpenCV/template matching
526
+ pass
527
+ time.sleep(1)
528
+
529
+ cap.close()
530
+ ```
531
+
532
+ ### Zero-Copy Advanced Usage
533
+
534
+ ```python
535
+ from wgc_python import WindowCapture
536
+ import numpy as np
537
+ import ctypes
538
+
539
+ with WindowCapture("Window", "Class") as cap:
540
+ cap.resume()
541
+ r = cap.get_frame() # (ptr, w, h, row_pitch) — GPU mapped pointer
542
+ if r:
543
+ ptr, w, h, rp = r
544
+ arr = np.ndarray((h, w, 4), dtype=np.uint8,
545
+ buffer=(ctypes.c_ubyte * (h * rp)).from_address(ptr),
546
+ strides=(rp, 4, 1))
547
+ # arr is a zero-copy view into GPU-mapped memory
548
+ cap.release_frame()
549
+ cap.pause()
550
+ ```
551
+
552
+ ### Real-time Display
553
+
554
+ ```python
555
+ from wgc_python import WindowCapture
556
+ import cv2
557
+
558
+ with WindowCapture("Window Title", "WindowClass") as cap:
559
+ while True:
560
+ frame = cap.capture_one()
561
+ if frame is not None:
562
+ cv2.imshow("Capture", cv2.cvtColor(frame, cv2.COLOR_BGRA2BGR))
563
+ if cv2.waitKey(1) & 0xFF == ord('q'):
564
+ break
565
+ cv2.destroyAllWindows()
566
+ ```
567
+
568
+ ---
569
+
570
+ ## API Reference
571
+
572
+ ```python
573
+ from wgc_python import (
574
+ WindowCapture, # Window capture class (context manager support)
575
+ enumerate_windows, # Enumerate all visible windows
576
+ get_last_error, # Get last error message (thread-safe)
577
+ get_active_capture_count, # Get active capture count
578
+ )
579
+
580
+ # WindowCapture methods:
581
+ # cap = WindowCapture(title, class_name, client_area_only=True, capture_cursor=True)
582
+ #
583
+ # cap.capture_one(timeout=0.5) -> np.ndarray | None ★ recommended
584
+ # Auto Resume → wait for frame → copy to numpy → Pause
585
+ # WGC fully dormant between captures, zero GPU driver overhead
586
+ #
587
+ # cap.get_frame() -> (ptr, w, h, row_pitch) | None
588
+ # cap.release_frame() # Release GPU mapping
589
+ # cap.pause() # Pause capture (zero-resource standby)
590
+ # cap.resume() # Resume capture
591
+ # cap.set_cursor_capture_enabled(enabled) # Toggle cursor capture at runtime
592
+ # cap.stop() # Stop frame arrival
593
+ # cap.close() # Destroy session
594
+ # cap.is_capturing() -> bool
595
+ # cap.is_paused() -> bool
596
+ # cap.get_frame_count() -> int
597
+ # cap.handle -> int (DLL handle)
598
+ ```
599
+
600
+ ---
601
+
602
+ ## Technical Architecture
603
+
604
+ ```
605
+ WGC Capture → GPU Surface Texture
606
+ │
607
+ ┌─────────▼──────────┐
608
+ │ FrameArrived │
609
+ │ if pausing → ↑ │ ← Paused: return directly, zero D3D ops
610
+ └─────────┬──────────┘
611
+ │
612
+ CopyResource (GPU async copy)
613
+ │
614
+ ┌─────────────────────────────┐
615
+ │ Double-buffered Staging │
616
+ │ [0] Write ←→ [1] Read │
617
+ │ m_textureInUse anti-collide │
618
+ └─────────────────────────────┘
619
+ │
620
+ Map (permanently mapped GPU memory)
621
+ │
622
+ ┌────── Zero-Copy Output ────┐
623
+ │ get_frame() │
624
+ │ raw ptr → numpy zero-copy │
625
+ │ requires release_frame() │
626
+ └─────────────────────────────┘
627
+
628
+ ┌────── One-Click Capture ───┐
629
+ │ capture_one() │
630
+ │ auto Pause/Resume │
631
+ │ returns numpy array │
632
+ │ zero GPU overhead when idle │
633
+ └─────────────────────────────┘
634
+ ```
635
+
636
+ ### How Pause/Resume Works
637
+
638
+ ```
639
+ User calls cap.pause()
640
+ │
641
+ m_isPaused = true ◄──── atomic flag, microsecond-level
642
+ m_readableStagingIndex = -1
643
+ │
644
+ ┌────▼────────────────────────────────────────────┐
645
+ │ FrameArrived Callback (WGC still fires)│
646
+ │ │
647
+ │ lock(mutex); │
648
+ │ if (m_isPaused) return; // ← pure CPU, skip│
649
+ │ // ↓ below runs only after resume ↓ │
650
+ │ CopyResource(staging, frame); │
651
+ │ m_readableStagingIndex = idx; │
652
+ │ unlock(mutex); │
653
+ └────▲────────────────┬───────────────────────────┘
654
+ │ │
655
+ User calls cap.resume() MapFrame checks readableStagingIndex
656
+ m_isPaused = false <0 → no frame ready → returns false
657
+
658
+ No WGC session destroy / no D3D device recreate / no callback re-register
659
+ → zero-latency resume, no spikes
660
+ ```
661
+
662
+ ---
663
+
664
+ ## File Structure
665
+
666
+ ```
667
+ wgc_python/
668
+ ├── wgc_python/ # Python package
669
+ │ ├── __init__.py # Python API (ctypes FFI)
670
+ │ └── wgc_python.dll # Compiled DLL
671
+ ├── wgc_python_dll/ # C++ DLL Project
672
+ │ ├── WGCWindowCapture.h/cpp # Capture core (double-buffered + zero-copy)
673
+ │ ├── WGCExport.h/cpp # DLL exports (thread-safe error handling)
674
+ │ ├── D3DInterop.cpp # D3D11 device interop
675
+ │ ├── WindowEnumerator.h/cpp # Window enumeration
676
+ │ ├── pch.h # Precompiled header
677
+ │ └── packages/ # NuGet packages
678
+ ├── test.py # Functional tests
679
+ ├── demon.py # Threaded realtime display example
680
+ ├── pyproject.toml # pip build config
681
+ ├── BUILD.md / BUILD_EN.md # Build instructions (CN/EN)
682
+ ├── README.md / README_EN.md # Usage docs (CN/EN)
683
+ ├── CONTRIBUTING.md # Contributing guide
684
+ ├── CODE_OF_CONDUCT.md # Code of conduct
685
+ ├── LICENSE # MIT License
686
+ └── requirements.txt # Python dependencies
687
+ ```
688
+
689
+ ---
690
+
691
+ ## Build DLL
692
+
693
+ See [BUILD.md](BUILD.md)
694
+
695
+ ---
696
+
697
+ ## Requirements
698
+
699
+ - Windows 10 1903+ (Build 18362); cursor capture toggle requires 2004+ (Build 19041)
700
+ - Python 3.8+
701
+
702
+ ---
703
+
704
+ ## Troubleshooting
705
+
706
+ | Issue | Solution |
707
+ |-------|----------|
708
+ | DLL not found | Ensure `wgc_python.dll` is in the correct location |
709
+ | Capture failed | Check if window is visible, Windows version >= 1903 |
710
+ | Non-ASCII path save failed | Use `cv2.imencode` + `open().write()` instead of `cv2.imwrite` |
711
+ | Missing dependencies | `pip install numpy opencv-python` |
712
+
713
+ ---
714
+
715
+ ## Use Cases
716
+
717
+ - ✅ Game AI / Automation Scripts
718
+ - ✅ RPA Process Automation
719
+ - ✅ Screen Recording / Streaming
720
+ - ✅ UI Automation Testing
721
+ - ✅ Computer Vision Applications
722
+
723
+ ---
724
+
725
+ ## Acknowledgments
726
+
727
+ This project is based on [robmikh/Win32CaptureSample](https://github.com/robmikh/Win32CaptureSample).
728
+
729
+ ---
730
+
731
+ ## License
732
+
733
+ MIT License