You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python实现RotBlt时调用SetPixel触发pywintypes.error报错排查

问题现象

使用Python基于pywin32实现支持自定义旋转角度的RotBlt位块传输函数时,调用SetPixel绘制旋转后像素触发运行时异常,报错追踪如下:

Traceback (most recent call last):
  File "C:\Users\MY\Desktop\bckup\Folder rafi\New folder\python-recorder\rotblt.py", line 50, in <module>
    RotBlt(hdc, 0, 0, x, y, hdc, 0,0, 10, SRCCOPY)
  File "C:\Users\MY\Desktop\bckup\Folder rafi\New folder\python-recorder\rotblt.py", line 37, in RotBlt
    SetPixel(memDC, nX+nC, nY+nC, c)
pywintypes.error: (0, 'SetPixel', 'No error message is available')

系统未返回有效错误描述,异常抛出位置为内存DC的像素写入操作。

根因定位

代码存在4个核心问题,直接触发SetPixel调用失败:

  • 坐标越界:这是触发异常的直接原因。首先内存DC的画布尺寸计算错误:代码强行将画布调整为正方形,且没有计算旋转后图像的外接矩形尺寸,画布大小不足以容纳旋转后的完整画面;其次偏移量nC计算逻辑完全错误,原有逻辑nC = int(float(sx1)/2)在传入sx1=0时取值为0,旋转后产生的负坐标没有被正向偏移到画布有效范围内,传给SetPixel的坐标要么小于0,要么大于等于画布宽高,触发GDI调用失败。
  • 旋转映射逻辑错误:原有实现采用源像素到目标像素的正向映射,这种写法会导致目标画布出现像素空洞、像素覆盖错乱,正确的旋转逻辑应该是反向映射:遍历目标画布的每个像素点,反推其对应源图的像素坐标取值,才能保证画面完整。
  • 变量名笔误导致GDI资源泄漏:资源释放阶段写的DeleteObject(ombp),但前面选入DC的位图变量名为obmp,变量名拼写错误会导致创建的位图资源无法被正常释放,运行时间稍长就会耗尽系统GDI资源。
  • 同DC读写冲突:代码将桌面DC同时作为源DC和目标DC传入,旋转过程中边读取源DC像素边写入目标DC,会因为DC内容被实时修改出现画面错乱、像素读取异常的问题。
修复要点
  1. 先将源图像完整拷贝到独立的临时源DC中,避免同DC读写冲突
  2. 计算旋转后图像的外接矩形尺寸,创建足够大的内存画布容纳旋转后的内容
  3. 修正坐标偏移逻辑:旋转后的坐标需要偏移画布宽高的一半,保证所有像素坐标落在画布[0, 宽-1]、[0, 高-1]的有效范围内,写入像素前增加坐标范围校验,避免越界
  4. 改用反向映射逻辑计算像素位置
  5. 修正变量名笔误,确保所有GDI资源被正常释放
修正后的参考实现
from win32api import *
from win32gui import *
from win32con import *
from math import *

def RotBlt(destDC, sx1, sy1, sx2, sy2, srcDC, dx1, dy1, deg, mode):
    theta = radians(deg)
    w = sx2 - sx1
    h = sy2 - sy1
    cX = w / 2
    cY = h / 2

    # 计算旋转后外接矩形尺寸
    cos_theta = cos(theta)
    sin_theta = sin(theta)
    new_w = int(abs(w * cos_theta) + abs(h * sin_theta))
    new_h = int(abs(w * sin_theta) + abs(h * cos_theta))
    nCx = new_w // 2
    nCy = new_h // 2

    # 先把源内容拷贝到独立临时DC,避免读写冲突
    src_memDC = CreateCompatibleDC(srcDC)
    src_bmp = CreateCompatibleBitmap(srcDC, w, h)
    src_obmp = SelectObject(src_memDC, src_bmp)
    BitBlt(src_memDC, 0, 0, w, h, srcDC, sx1, sy1, SRCCOPY)

    # 创建目标内存DC
    memDC = CreateCompatibleDC(destDC)
    membmp = CreateCompatibleBitmap(destDC, new_w, new_h)
    obmp = SelectObject(memDC, membmp)

    # 反向映射遍历目标像素
    for nX in range(new_w):
        for nY in range(new_h):
            # 反算对应源图坐标
            x = (nX - nCx) * cos_theta + (nY - nCy) * sin_theta + cX
            y = -(nX - nCx) * sin_theta + (nY - nCy) * cos_theta + cY
            # 坐标校验,只取源图范围内的像素
            if 0 <= int(x) < w and 0 <= int(y) < h:
                c = GetPixel(src_memDC, int(x), int(y))
                SetPixel(memDC, nX, nY, c)

    # 输出到目标DC
    BitBlt(destDC, dx1, dy1, new_w, new_h, memDC, 0, 0, mode)

    # 释放所有GDI资源
    SelectObject(src_memDC, src_obmp)
    DeleteDC(src_memDC)
    DeleteObject(src_bmp)
    SelectObject(memDC, obmp)
    DeleteDC(memDC)
    DeleteObject(membmp)

if __name__ == "__main__":
    while True:
        x, y = GetSystemMetrics(0), GetSystemMetrics(1)
        hwnd = GetDesktopWindow()
        hdc = GetWindowDC(hwnd)
        RotBlt(hdc, 0, 0, x, y, hdc, 0, 0, 10, SRCCOPY)
        ReleaseDC(hwnd, hdc)
        Sleep(10)

注:逐像素GetPixel/SetPixel的实现性能很低,仅适合功能验证,实际使用建议采用矩阵运算或者GDI+、OpenCV等图形库实现旋转逻辑。

内容的提问来源于stack exchange,提问作者Bulieme

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 08:06:23