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

如何在Kotlin/Native中使用GetIconInfo返回的HBITMAP获取图像字节?

使用Kotlin/Native从HBITMAP提取图像字节

你的方向是对的,GetDIBits确实是Windows API中提取位图像素数据的标准方式,但之前的代码存在几个关键问题,导致无法正确获取图像字节。以下是修正后的完整实现步骤及代码:

关键修正点说明

  • 必须先通过GetObject获取BITMAP结构,拿到位图的宽、高、位深等核心信息
  • BITMAPINFO需要根据位图信息正确初始化,尤其是BITMAPINFOHEADER部分
  • 要为图像字节分配足够的内存空间,不能仅声明指针变量
  • 调用GetDIBits时需传入正确的参数(如实际位图高度,而非UInt.MAX_VALUE)
  • 必须及时释放Windows资源(DC、位图句柄等),避免内存泄漏

完整实现代码

memScoped {
    CoInitialize(null, COINIT_APARTMENTTHREADED)
    defer { CoUninitialize() }

    val fileInfo = alloc<SHFILEINFOA>()
    // 组合所需的SHGFI_*常量,保留你原有的标志
    val flags = SHGFI_ICON or SHGFI_DISPLAYNAME or SHGFI_TYPENAME or SHGFI_ICONLOCATION or SHGFI_SMALLICON
    SHGetFileInfo("C:\\path\\to.file", 0, fileInfo.ptr, sizeOf<SHFILEINFOA>().toUInt(), flags)

    println(fileInfo.szDisplayName.toKString())
    println(fileInfo.szTypeName.toKString())

    val iconInfo = alloc<ICONINFO>()
    GetIconInfo(fileInfo.hIcon, iconInfo.ptr)
    defer { DeleteObject(iconInfo.hbmColor) }
    defer { DeleteObject(iconInfo.hbmMask) }
    defer { DestroyIcon(fileInfo.hIcon) }

    // 第一步:获取BITMAP结构,了解位图基本信息
    val bitmap = alloc<BITMAP>()
    GetObject(iconInfo.hbmColor, sizeOf<BITMAP>().toUInt(), bitmap.ptr)

    // 第二步:初始化BITMAPINFO和BITMAPINFOHEADER
    val bmi = alloc<BITMAPINFO>()
    val bmiHeader = bmi.bmiHeader
    bmiHeader.biSize = sizeOf<BITMAPINFOHEADER>().toUInt()
    bmiHeader.biWidth = bitmap.bmWidth
    bmiHeader.biHeight = bitmap.bmHeight
    bmiHeader.biPlanes = 1
    bmiHeader.biBitCount = bitmap.bmBitsPixel.toUInt()
    bmiHeader.biCompression = BI_RGB // 不压缩格式
    bmiHeader.biSizeImage = 0 // 让系统自动计算大小

    // 第三步:计算所需内存并分配(按Windows位图4字节对齐要求)
    val bytesPerLine = ((bitmap.bmWidth * bitmap.bmBitsPixel + 31) / 32) * 4
    val totalBytes = bytesPerLine * bitmap.bmHeight
    val pixelBytes = allocArray<Byte>(totalBytes)

    // 第四步:调用GetDIBits提取像素数据
    val hdc = GetDC(null)
    defer { ReleaseDC(null, hdc) }

    val bitsCopied = GetDIBits(
        hdc,
        iconInfo.hbmColor,
        0,
        bitmap.bmHeight.toUInt(),
        pixelBytes,
        bmi.ptr,
        DIB_RGB_COLORS
    )

    if (bitsCopied == 0u) {
        println("GetDIBits调用失败")
        return@memScoped
    }

    // 现在pixelBytes就是图像的像素字节数组,可进行后续处理
    println("成功获取${totalBytes}字节的图像数据")
    // 示例:打印前10个字节
    repeat(minOf(10, totalBytes)) {
        println("字节${it}: ${pixelBytes[it]}")
    }
}

重要注意事项

  • 所有Windows API句柄(如HICON、HBITMAP、HDC)必须在使用完毕后释放,这里用defer确保memScoped块结束时自动释放
  • 位图扫描线是倒序存储(从底部到顶部),若需正序图像需自行反转每行数据
  • 32位ARGB位图的像素格式为BGRA(而非RGBA),注意颜色通道顺序
  • CoInitialize和CoUninitialize必须配对调用,确保COM环境正确初始化

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 06:06:45