【问题标题】:Convert bitmap to PNG in-memory in C++ (win32)在 C++ (win32) 中将位图转换为内存中的 PNG
【发布时间】:2010-09-26 21:02:05
【问题描述】:

我能否仅使用 Platform SDK 将内存中的位图转换为 PNG(即不写入文件)? (即没有 libpng 等)。

我还希望能够为此图像定义透明颜色(不是 Alpha 通道)。

GdiPlus 解决方案似乎仅限于宽度可被 4 整除的图像。在调用 Save() 期间,其他任何事情都会失败。有谁知道这个限制的原因以及我如何/是否可以解决它?

更新:赏金

我正在开始赏金(我真的希望它能够工作)。我实现了 GDI+ 解决方案,但正如我所说,它仅限于四边形宽度的图像。赏金将颁发给能够解决此宽度问题(不更改图像尺寸)或能够提供可行的替代非 GDI+ 解决方案的任何人。

【问题讨论】:

  • 为帮助您入门,我找到了一个使用 gdi+ 将位图保存为 png 的示例:dotnet-snippets.de/dns/gdi-speichern-eines-png-SID814.aspx 也许有用
  • 这个解决方案对我有用,即使是宽度不能被 4 整除的图像。我在 Windows Vista 上并使用 Visual C++ 2008。
  • 为什么不想使用 libpng?它有一个非常宽松的许可证。
  • libpng 是否进行内存编码? (或只是文件?)
  • 你可以使用 png_set_write_fn 钩住 libpng 的输出,然后用它做任何你想做的事情。

标签: c++ winapi png


【解决方案1】:

LodePNG (GitHub) 是一个无库的 PNG 编码器/解码器。

【讨论】:

    【解决方案2】:

    我使用libpng 读写PNG,它似乎可以处理我扔给它的所有东西(我在单元测试中使用过它,比如257x255 图像,它们不会造成任何麻烦)。我相信API 足够灵活,不会被绑定到文件 I/O(或者至少你可以覆盖它的默认行为,例如参见 customization 部分中的 png_set_write_fn

    在实践中,我总是通过更简洁的boost::gilPNG IO extension 使用它,但不幸的是,这需要char* 文件名,如果你深入研究它的实现中的png_writerfile_mgr 类,它似乎非常相关到FILE*(虽然如果你在Linux上使用fmemopen和内存缓冲区的版本可能很容易制作出来)。

    【讨论】:

    • +1 谢谢。我可以知道如何将 Boost::GIL 与 PNG IO 一起使用吗?我对集成和利用它一无所知。
    • Boost GIL 包含一个“IO 扩展”,目前包括对 PNG 文件的基本读取和写入的支持,但没有提供足够的挂钩来读取/写入内存中的流,因为原始发布者询问.似乎有一个新的 IO 扩展(谷歌类似“boost GIL 新 IO 扩展”来查找各种帖子)正在开发中,看起来它将在这个领域提供更多的灵活性。
    • libpng 虽然是一个很好的解决方案,但似乎违反了问题的“仅使用平台 SDK”的约束。
    • 请问如何安装这个库?还是我需要简单地获取源代码?
    • @Lucie:我也在努力寻找 Windows 版本;见我的另一个问题stackoverflow.com/questions/8669163/…。与 Linux 相比,Windows 开发的这些东西让我发狂,在 Linux 中,您只需 apt-get install libpng-dev 就可以在几秒钟内完成。
    【解决方案3】:

    在此站点上,代码显示了如何将位图转换为 PNG 并将其写入文件:http://dotnet-snippets.de/dns/gdi-speichern-eines-png-SID814.aspx。除了写入文件,Bitmap 的Save 方法还支持写入IStream (http://msdn.microsoft.com/en-us/library/ms535406%28VS.85%29.aspx)。您可以使用CreateStreamOnHGlobal API 函数创建由内存备份的 Stream。 (http://msdn.microsoft.com/en-us/library/aa378980%28VS.85%29.aspx)。使用的库 GDI+ 包含在 Windows XP 以上的 Windows 中,并且可以在 Windows98 以上的 Windows 中运行。我从来没有用它做过什么,只是用谷歌搜索了一下。不过,看起来你可以使用它。

    【讨论】:

    • 我在原问题中提到的GDI+问题呢?
    【解决方案4】:

    CImage 类 (ATL/MFC) 支持保存为 PNG 格式。与 GDI+ 解决方案一样,它也支持保存到流。这是我用来将其保存到 CByteArray 的一些代码:

    CByteArray baPicture;
    IStream *pStream = NULL;
    if (CreateStreamOnHGlobal(NULL, TRUE, &pStream) == S_OK)
    {
        if (image.Save(pStream, Gdiplus::ImageFormatPNG) == S_OK)
        {
        ULARGE_INTEGER ulnSize;
            LARGE_INTEGER lnOffset;
            lnOffset.QuadPart = 0;
            if (pStream->Seek(lnOffset, STREAM_SEEK_END, &ulnSize) == S_OK)
            {
                if (pStream->Seek(lnOffset, STREAM_SEEK_SET, NULL) == S_OK)
                {                       
                    baPicture.SetSize(ulnSize.QuadPart);
                    ULONG ulBytesRead;
                    pStream->Read(baPicture.GetData(), ulnSize.QuadPart, &ulBytesRead);
                }
            }
        }
    }
    pStream->Release();
    

    不过,我不知道你是想使用 ATL 还是 MFC。

    【讨论】:

    • 唉,我的项目中没有 ATL/MFC。
    • 好的...也许流保存部分可能有用,它应该类似于使用 GDI+ 的部分
    • 这对我正在做的事情有所帮助,至少为此 +1。
    • 我很确定这使用了 libpng
    【解决方案5】:

    我使用 GDI+ 将位图作为 PNG 保存到文件中。您可能应该查看有关 GDI+ here 的 MSDN 信息,尤其是此功能 GdipSaveImageToStream

    本教程here 可能也会提供一些帮助。

    【讨论】:

      【解决方案6】:

      GDI(老派,非加号)有一个 GetDIBits 方法,可以要求使用 PNG 压缩(BITMAPINFOHEADER::biCompression == BI_PNG)输出位。我想知道这是否可以用来创建PNG文件?使用GetDIBits 编写标准位图文件已经够复杂了——所以我怀疑这会更加困难。

      【讨论】:

      【解决方案7】:

      如果您只想使用 Windows API,WIC 是实现此目的的方法,它同时支持位图和 PNG。

      【讨论】:

      • 你确定它不会像 GDI+ 那样受到奇怪的宽度限制吗?
      • 不,不确定 - 但 WIC 绝对比 GDI+ 更现代且与时俱进
      【解决方案8】:

      使用库而不是自己重新发明轮子可能会更好。

      查看freeImage

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-04-13
        • 2020-11-26
        • 1970-01-01
        • 2014-01-30
        • 1970-01-01
        • 2012-04-13
        • 1970-01-01
        • 2013-02-10
        相关资源
        最近更新 更多