【问题标题】:Embed PNG (Or Bitmap with alpha channel) in executable在可执行文件中嵌入 PNG(或带有 alpha 通道的位图)
【发布时间】:2021-11-27 22:52:14
【问题描述】:

TL;DR:我想创建一个嵌入了图像资源(带有 Alpha 通道)的独立应用程序。执行此操作和提取数据的现代/标准方法是什么?

我目前正在用 C++ 编写一个使用 OpenGL 作为图形引擎的应用程序(与 Vulkan 相比,我选择它是因为开发时间更快,尽管我也很想学习 Vulkan),在这个应用程序中我使用 Signed Distance呈现我的文本的字段。这需要带有 alpha 通道的 Font Atlas 来绘制字符。

我希望能够在可执行文件中嵌入这个字体图集(目前它是一个 .PNG 文件)。还有其他图像资源,但我提到这个场景是为了说明为什么我真的需要 alpha 通道。


我使用 Win32 API 执行以下操作:

获取当前模块。

HMODULE getCurrentModule()
{
    HMODULE hModule = NULL;
    GetModuleHandleEx(
        GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
        (LPCTSTR)getCurrentModule,
        &hModule);
    return hModule;
}

为着色器嵌入文本文件。

std::string loadShaderFromResource(int shaderID) 
{
    // Load resource from executable.
    HRSRC shaderResource = FindResource(getCurrentModule(), MAKEINTRESOURCE(shaderID), MAKEINTRESOURCE(TEXTFILE));
    HGLOBAL resourceData = LoadResource(getCurrentModule(), shaderResource);
    DWORD resourceSize = SizeofResource(getCurrentModule(), shaderResource);
    char* resourceFinal = (char*)LockResource(resourceData);
    std::string shaderSource;
    shaderSource.assign(resourceFinal, resourceSize);
    return shaderSource;
}

我的 resource.h 文件。

有点乱,但我把它包括在内是为了提供尽可能多的信息。

/*=======================================================================================================================================*/
/* Setup by Visual Studio.                                                                                                               */
/*=======================================================================================================================================*/

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        116
#define _APS_NEXT_COMMAND_VALUE         40003
#define _APS_NEXT_CONTROL_VALUE         1001
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

/*=======================================================================================================================================*/
/* Resources.                                                                                                                            */
/*=======================================================================================================================================*/

//-------------------------------------------------------------------------------------------------
// Resource Types
#define TEXTFILE 255
#define PNG 254
//-------------------------------------------------------------------------------------------------
// Shaders
#define BASIC_SHADER 253
#define STATIC_SHADER 252
#define TEXTURE_SHADER 251
//-------------------------------------------------------------------------------------------------
// ImGui images.
#define COMPONENT_PNG 250               
#define DRAW_CIRCUIT_BUCKETS 249
#define DRAW_MCC_PNG 248                
//-------------------------------------------------------------------------------------------------
// OpenGL Textures.
#define CIRCUIT_TREE_PNG 247
//-------------------------------------------------------------------------------------------------
// OpenGL Fonts. 
#define ARIAL_SDF_FNT 246       
#define ARIAL_SDF_PNG 245       
#define ARIAL_SDF_BMP 244
#define ARIAL_SDF_MIN_FNT 243
#define ARIAL_SDF_MIN_PNG 242   
//-------------------------------------------------------------------------------------------------
// Application icon.
#define IDI_ICON1 116           // Exe icon.
#define ICON_BMP 115            // GLFW icon.
#define PANDA 114
//-------------------------------------------------------------------------------------------------

/*=======================================================================================================================================*/
/* Includes.                                                                                                                             */
/*=======================================================================================================================================*/

// Windows api for the exe.
#include <Windows.h>
#include <string>

/*=======================================================================================================================================*/
/* Data.                                                                                                                                 */
/*=======================================================================================================================================*/

// Struct that stores the bitmap data.
struct Bitmap {
    void* pixelData;
    int width = 0;
    int height = 0;
};

/*=======================================================================================================================================*/
/* Functions.                                                                                                                            */
/*=======================================================================================================================================*/

// Load the current module.
HMODULE getCurrentModule();

// Load shader from resource.
std::string loadShaderFromResource(int shaderID);

// Loading bitmaps.
Bitmap loadBitmapFromResource(int bitmapID);
unsigned int loadBitmapToGL(Bitmap bitmap);


/*=======================================================================================================================================*/
/* EOF.                                                                                                                                  */
/*=======================================================================================================================================*/

我的 resource.rc 文件。

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
// English (United States) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE
BEGIN
"resource.h\0"
END

2 TEXTINCLUDE
BEGIN
"#include ""winres.h""\r\n"
"\0"
END

3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END

#endif    // APSTUDIO_INVOKED

/*=======================================================================================================================================*/
/* Resources.                                                                                                                            */
/*=======================================================================================================================================*/

//-------------------------------------------------------------------------------------------------
// Resource ID                  Resource Type               Resource Path
//-------------------------------------------------------------------------------------------------
// Application icon.
IDI_ICON1                       ICON                        "Icons\\circuitIcon128.ico"
ICON_BMP                        BITMAP                      "Icons\\circuitIcon.bmp"
PANDA                           BITMAP                      "Icons\\panda.bmp"
//-------------------------------------------------------------------------------------------------
// Shaders.
BASIC_SHADER                    TEXTFILE                    "Shaders\\basicShader.shader"
STATIC_SHADER                   TEXTFILE                    "Shaders\\staticShader.shader"
TEXTURE_SHADER                  TEXTFILE                    "Shaders\\textureShader.shader"
//-------------------------------------------------------------------------------------------------
// ImGui images.
COMPONENT_PNG                   PNG                         "Icons\\component.png"
DRAW_CIRCUIT_BUCKETS_PNG        PNG                         "Icons\\Draw_Circuit_Buckets.png"
DRAW_MCC_PNG                    PNG                         "Icons\\Draw_MCC.png"
//-------------------------------------------------------------------------------------------------
// OpenGL Textures.
CIRCUIT_TREE_PNG                PNG                         "Textures\\circuitTree.png"
//-------------------------------------------------------------------------------------------------
// OpenGL Fonts. 
ARIAL_SDF_FNT                   TEXTFILE                    "Fonts\\Arial_SDF.fnt"
ARIAL_SDF_PNG                   PNG                         "Fonts\\Arial_SDF_PNG.png"
ARIAL_SDF_BMP                   BITMAP                      "Fonts\\Arial_SDF_BMP_32.bmp"
ARIAL_SDF_MIN_FNT               TEXTFILE                    "Fonts\\Arial_SDF_Min.fnt"
ARIAL_SDF_MIN_PNG               PNG                         "Fonts\\Arial_SDF_Min.png"
//-------------------------------------------------------------------------------------------------

#endif    // English (United States) resources

//=================================================================================================

#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//


/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED
 /*=======================================================================================================================================*/
/* EOF.                                                                                                                                  */
/*=======================================================================================================================================*/

我注意到大多数关于 Win32 API 的论坛和这类问题都非常非常古老。还在用吗?有没有更现代的方法呢?我看到有人在使用 CMake。


编辑 1

按照@Barmak 的建议,我尝试添加以下内容并进行了一些修改:

HBITMAP loadImageFromResource(int resourceID)
{
    HBITMAP hbitmap = NULL;
    ULONG_PTR token;
    Gdiplus::GdiplusStartupInput tmp;
    Gdiplus::GdiplusStartup(&token, &tmp, NULL);
    if (auto hres = FindResource(getCurrentModule(), MAKEINTRESOURCE(resourceID), RT_RCDATA))
        if (auto size = SizeofResource(getCurrentModule(), hres))
            if (auto data = LockResource(LoadResource(getCurrentModule(), hres)))
                if (auto stream =  SHCreateMemStream((BYTE*)data, size))
                {
                    Gdiplus::Bitmap bmp(stream);
                    stream->Release();
                    bmp.GetHBITMAP(Gdiplus::Color::Transparent, &hbitmap);
                }
    Gdiplus::GdiplusShutdown(token);
    return hbitmap;
}

但是,在调试模式下查看hbitmap 时,它会显示0xffffffffa00513d7 {unused=???} 并且不起作用。

resource.h:

#define ICON_PNG 240            // GLFW icon.

resource.rc:

ICON_PNG                        RCDATA                      "Icons\\circuitImage.png"

编辑 2

我犯了一个简单的错误。我在函数中添加了这些行:

BITMAP bitmap;
GetObject(hbitmap, sizeof(BITMAP), &bitmap);

现在它通过返回 BITMAP 而不是 HBITMAP 来工作。但是,它是颠倒的,当与glfwSetWindowIcon() 一起使用时,某些颜色似乎不正常。它在其他地方也能完美运行!

BITMAP loadImageFromResource(int resourceID)
{
    HBITMAP hbitmap = NULL;
    ULONG_PTR token;
    Gdiplus::GdiplusStartupInput tmp;
    Gdiplus::GdiplusStartup(&token, &tmp, NULL);
    if (auto hres = FindResource(getCurrentModule(), MAKEINTRESOURCE(resourceID), RT_RCDATA))
        if (auto size = SizeofResource(getCurrentModule(), hres))
            if (auto data = LockResource(LoadResource(getCurrentModule(), hres)))
                if (auto stream =  SHCreateMemStream((BYTE*)data, size))
                {
                    Gdiplus::Bitmap bmp(stream);
                    stream->Release();
                    bmp.GetHBITMAP(Gdiplus::Color::Transparent, &hbitmap);
                }
    Gdiplus::GdiplusShutdown(token);
    BITMAP bitmap;
    GetObject(hbitmap, sizeof(BITMAP), &bitmap);
    return bitmap;
}

【问题讨论】:

  • 我不知道如何从资源中直接读取 OpenCV。在这里查看stackoverflow.com/a/58754216/4603670 以将 png 从资源中读取到 Gdi+。此方法将 PNG 读入数据,然后将数据传递给IStream 并将其读入 Gdi+,然后您可以从 Gdi+ 中获取位和尺寸。或者你可以将 *.png 保存为 *.ico,将图标放在资源中。
  • 将资源存储在 PE 映像的资源部分是将(二进制)数据链接到可执行映像的标准工具。使用它,除非您有特定的理由不这样做。您可能希望从资源提取实现中返回 vector&lt;byte&gt; 而不是 string
  • 我不明白您所说的“在 PE 图像的资源部分中存储资源”是什么意思?
  • Portable Executable 文件格式专门用于保存资源。这些被放入文件图像中的专用部分。这就是你正在做的,已经。资源编译器 (rc.exe) 将资源脚本 (.rc) 转换为二进制表示,链接器在链接最终的可执行映像时获取该表示。

标签: c++ image winapi opengl executable


【解决方案1】:

信用:Barmak Shemirani

首先,通过将以下行添加到以下位置来存储 .PNG 文件:

资源.h

#define ICON_PNG 240            // GLFW icon.

资源.rc

ICON_PNG                        RCDATA                      "Icons\\circuitImage.png"

然后像这样将 .PNG 加载为位图:

BITMAP loadImageFromResource(int resourceID)
{
    HBITMAP hbitmap = NULL;
    ULONG_PTR token;
    Gdiplus::GdiplusStartupInput tmp;
    Gdiplus::GdiplusStartup(&token, &tmp, NULL);
    if (auto hres = FindResource(getCurrentModule(), MAKEINTRESOURCE(resourceID), RT_RCDATA))
        if (auto size = SizeofResource(getCurrentModule(), hres))
            if (auto data = LockResource(LoadResource(getCurrentModule(), hres)))
                if (auto stream =  SHCreateMemStream((BYTE*)data, size))
                {
                    Gdiplus::Bitmap bmp(stream);
                    stream->Release();
                    bmp.GetHBITMAP(Gdiplus::Color::Transparent, &hbitmap);
                }
    Gdiplus::GdiplusShutdown(token);
    BITMAP bitmap;
    if(!hbitmap) return NULL;
    GetObject(hbitmap, sizeof(BITMAP), &bitmap);
    return bitmap;
}

这样称呼它:

BITMAP image = loadImageFromResource(ICON_PNG);

编辑 1

我使用这个函数将位图加载到 OpenGL 中:

unsigned int loadBitmapToGL(BITMAP bitmap) 
{
    unsigned int textureID = 0;
    glCreateTextures(GL_TEXTURE_2D, 1, &textureID);
    glBindTexture(GL_TEXTURE_2D, textureID);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap.bmWidth, bitmap.bmHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)bitmap.bmBits);
    return textureID;
}

还按照 Barmak 的建议添加了 if(!hbitmap) return NULL;

【讨论】:

  • OpenGL 可能具有可以从文件中读取 PNG 的功能。是否有一些OpenGL函数可以从datasize(从内存中读取)读取PNG?这样您就可以跳过 gdi+ 和 gdi 转换,这会使位图上下颠倒并可能丢失其他信息。你也应该添加这个测试if(!hbitmap) return NULL; GetObject(hbitmap...)
猜你喜欢
  • 2016-07-26
  • 1970-01-01
  • 2011-03-07
  • 1970-01-01
  • 2018-06-26
  • 2016-04-03
  • 1970-01-01
  • 2018-10-24
  • 2012-05-30
相关资源
最近更新 更多