高级Win32映像文件I/O

High level Win32 image file I/O?

本文关键字:文件 Win32 映像 高级      更新时间:2023-10-16

我想在Windows C++应用程序中将图像文件读入内存。什么是一个相当简单的解决方案,也许类似于IOS在UIImage中提供的解决方案?

我希望有一个合理数量的文件格式得到支持。

我需要为位图提供一些低级别的访问权限,以便进行图像处理。

我在互联网上读了很多书,看起来Windows DIB可能是一种合理的内存表示。除此之外,我在内存中找不到将JPEG或PNG读入DIB的示例代码。

谢谢你的建议。

我刚刚检查了LoadImage确实支持通过标志LR_LOADFROMFILE从文件加载。

所以这将是我的第一选择。

第二选择,GDI+(请注意,它需要大量的init操作才能获得普通的高质量,而且至少在几年前,它仍然依赖于<windows.h>中的minmax(。第三种选择,在对问题的评论中提到的Windows映像组件。第四选择,OleLoadPicturePath和家族。


附录:如注释所述,LoadImage仅限于“位图”用于加载图像文件。在我的系统上,下面的测试程序报告.bmp文件加载正常,.gif、.png和.jpg.加载失败

#undef UNICODE
#define UNICODE
#include <windows.h>
#include <assert.h>         // assert
#include <iostream>         // std::wcout
#include <string>           // std::wstring
using namespace std;
auto get_exe_path()
    -> wstring
{
    int const buffer_size = MAX_PATH;
    wstring result( buffer_size, L'#' );
    int const n_characters = GetModuleFileName( 0, &result[0], buffer_size );
    assert( 0 < n_characters && n_characters < buffer_size );
    result.resize( n_characters );
    return result;
}
auto get_exe_folder_path()
    -> wstring
{
    wstring const exe_path = get_exe_path();
    int const i = exe_path.rfind( L'' );
    return exe_path.substr( 0, i + 1 );
}
void test( wstring const& image_name )
{
    wstring const image_file_name = get_exe_folder_path() + image_name;
    wcout << image_file_name << endl;
    HANDLE const image = ::LoadImage(
        0,              // HINSTANCE hinst,
        image_file_name.c_str(),
        IMAGE_BITMAP,
        0, 0,   // int cxDesired, int cyDesired,
        LR_LOADFROMFILE
        );
    wcout << image << endl;
    DeleteObject( image );
}
auto main()
    -> int
{
    test( L"test.bmp" );  wcout << endl;
    test( L"test.png" );  wcout << endl;
    test( L"test.gif" );  wcout << endl;
    test( L"test.jpg" );
}

附录2:为了验证它,我还测试了Windows Imaging Component功能,它确实可以处理以上所有四种图像类型。以下代码的大部分大小是由于对COM的可重复使用的一次写入支持(我只是再次从头开始编写,所以它有点粗略,只是这里需要的(。尽管如此,这段代码并没有显示图像,也没有对它做任何其他事情,而对于WIC来说,这是非常复杂的…

虽然这段代码有部分g++支持,但我已经用g++对它进行了测试。我记得g++只支持Windows API,就像它在Windows XP时一样。我不确定WIC是什么时候引入的(尽管它可以与Windows XP一起使用(。

#undef UNICODE
#define UNICODE
#include <windows.h>
#include <wincodec.h>       // IWICImagingFactory
#include <algorithm>        // std::swap
#include <assert.h>         // assert
#include <iostream>         // std::wcout
#include <stdlib.h>         // EXIT_FAILURE, EXIT_SUCCESS
#include <string>           // std::string, std::wstring
#include <utility>          // std::move
#ifndef CPPX_NOEXCEPT
#   if defined( _MSC_VER )
#       define CPPX_NOEXCEPT   throw()
#   else
#       define  CPPX_NOEXCEPT   noexcept
#   endif
#endif
#ifndef CPPX_NORETURN
#   if defined( _MSC_VER )
#       define CPPX_NORETURN   __declspec( noreturn )
#       pragma warning( disable: 4646 )     // "has non-void return type"
#   elif defined( __GNUC__ )
#       define CPPX_NORETURN    __attribute__((noreturn))
#   else
#       define CPPX_NORETURN    [[noreturn]]
#   endif
#endif
namespace cppx {
    using std::string;
    using std::runtime_error;
    auto hopefully( bool const condition )
        CPPX_NOEXCEPT
        -> bool
    { return condition; }
    CPPX_NORETURN
    auto fail( string const& s )
        -> bool
    { throw runtime_error( s ); }
}  // namespace cppx
namespace process {
    using std::wstring;
    auto get_exe_path()
        -> wstring
    {
        int const buffer_size = MAX_PATH;
        wstring result( buffer_size, L'#' );
        int const n_characters = GetModuleFileName( 0, &result[0], buffer_size );
        assert( 0 < n_characters && n_characters < buffer_size );
        result.resize( n_characters );
        return result;
    }
    auto get_exe_folder_path()
        -> wstring
    {
        wstring const exe_path = get_exe_path();
        int const i = exe_path.rfind( L'' );
        return exe_path.substr( 0, i + 1 );
    }
}  // namespace process
namespace com {
    using cppx::fail;
    using std::move;
    enum Success { success };
    auto operator>>( HRESULT const hr, Success )
        -> bool
    { return SUCCEEDED( hr ); }
    struct Library
    {
        ~Library()
        { CoUninitialize(); }
        Library()
        { CoInitialize( nullptr ); }
    };
    template< class Interface >
    class Ptr
    {
    private:
        Interface*  p_;
    public:
        auto raw() -> Interface* { return p_; }
        auto operator->() -> Interface* { return p_; }
        void clear() { Ptr null; swap( *this, null ); }
        auto as_value_receiver()
            -> Interface**
        {
            clear();
            return &p_;
        }
        auto as_untyped_value_receiver()
            -> void**
        { return reinterpret_cast<void**>( as_value_receiver() ); }
        friend void swap( Ptr& a, Ptr& b )
            CPPX_NOEXCEPT
        { std::swap( a.p_, b.p_ ); }
        void operator=( Ptr other )
        { swap( *this, other ); }
        void operator=( Ptr&& other )
        {
            Ptr temp( move( other ) );
            swap( temp, *this );
        }
        ~Ptr()
        { if( p_ != nullptr ) { p_->Release(); } }
        explicit Ptr( Interface* p = nullptr )
            CPPX_NOEXCEPT
            : p_( p )
        {}
        Ptr( Ptr const& other )
            : p_( other.p_ )
        { if( p != nullptr ) { p_->AddRef(); } }
        Ptr( Ptr&& other )
            CPPX_NOEXCEPT
            : p_( other.p_ )
        { other.p_ = nullptr; }
        static
        auto create_local( CLSID const& class_id )
            -> Ptr<Interface>
        {
            Ptr<Interface>  result;
            ::CoCreateInstance(
                class_id, nullptr, CLSCTX_INPROC_SERVER,
                __uuidof( Interface ),
                result.as_untyped_value_receiver()
                )
                >> success
                || fail( "CoCreateInstance" );
            return move( result );
        }
    };
}  // namespace com
namespace app {
    using cppx::fail;
    using std::wstring;
    using std::wcout; using std::endl;
    void test( wstring const& image_name )
    {
        wstring const image_file_name =
            process::get_exe_folder_path() + image_name;
        wcout << image_file_name << endl;
        auto                            p_factory   =
            com::Ptr<IWICImagingFactory>::create_local( CLSID_WICImagingFactory );
        com::Ptr< IWICBitmapDecoder>    p_decoder;
        p_factory->CreateDecoderFromFilename(
            image_file_name.c_str(),
            nullptr,
            GENERIC_READ,
            WICDecodeMetadataCacheOnDemand, // Cache metadata when needed
            p_decoder.as_value_receiver()
            )
            >> com::success
            || fail( "IWICImagingFactory::CreateDecoderFromFilename" );
        com::Ptr<IWICBitmapFrameDecode> p_frame;
        p_decoder->GetFrame( 0, p_frame.as_value_receiver() )
            >> com::success
            || fail( "IWICBitmapFrameDecode::GetFrame");
        UINT w, h;
        p_frame->GetSize( &w, &h )
            >> com::success
            || fail( "IWICBitmapFrameDecode::GetSize" );
        wcout << "(w, h) = (" << w << ", " << h << ")" << endl;
    }
    void cpp_main()
    {
        com::Library const  com_usage;
        test( L"test.bmp" );  wcout << endl;
        test( L"test.png" );  wcout << endl;
        test( L"test.gif" );  wcout << endl;
        test( L"test.jpg" );  wcout << endl;
        test( L"test.bogus" );
    }
}  // namespace app
auto main()
    -> int
{
    using namespace std;
    try
    {
        app::cpp_main();
        return EXIT_SUCCESS;
    }
    catch( exception const& x )
    {
        wcout << "!" << x.what() << endl;
    }
    return EXIT_FAILURE;
}

我会使用GDI+位图类。它支持BMP、GIF、JPEG、PNG、TIFF、Exif、WMF和EMF。


编辑:

示例:

标题

#include <windows.h>
#include <objidl.h>
#include <gdiplus.h>
using namespace Gdiplus;
...
class App {
    ULONG_PTR gditoken;
    App::App() : gditoken(0) { }
...

来源

bool App::Init() {
    if(Status::Ok != GdiplusStartup(&gditoken, &GdiplusStartupInput(), NULL))
        return false;
    ...
    return true;
}
void App::Destroy() {
    if(gditoken) GdiplusShutdown(gditoken);
    ...
}
void App::DoStuffWithBitmap(WCHAR* filename) {
    Bitmap bm(filename);
    if(Status::Ok == bm.GetLastStatus()) {
        ...
    }
}

FromFile()方法也可用于

void App::DoStuffWithBitmap(WCHAR* filename) {
    Bitmap* bm = Bitmap::FromFile(filename);
    //Note: If the file is not found, FromFile returns an object anyway.
    if(bm && Status::Ok == bm->GetLastStatus()) {
        ...
    }
    delete bm;
}

为了访问比特,你可以使用(尚未测试(

int GetDIBits(
  HDC hdc,           // handle to DC
  HBITMAP hbmp,      // handle to bitmap
  UINT uStartScan,   // first scan line to set
  UINT cScanLines,   // number of scan lines to copy
  LPVOID lpvBits,    // array for bitmap bits
  LPBITMAPINFO lpbi, // bitmap data buffer
  UINT uUsage        // RGB or palette index
);

要获取HBITMAP,请使用

Status Bitmap::GetHBITMAP(
    const Color &colorBackground,
    HBITMAP *hbmReturn
);

在位图对象上。

#include <gdiplus.h>
unsigned long gdiToken;
Gdiplus::GdiplusStartupInput gdiStart;
gdiStart.SuppressBackgroundThread = FALSE;
gdiStart.GdiplusVersion = 1;
gdiStart.DebugEventCallback = NULL;
gdiStart.SuppressExternalCodecs = NULL;
if (GdiplusStartup(&gdiToken, &gdiStart, NULL) == Gdiplus::Ok)
{
    Gdiplus::Bitmap *gdiBm = Gdiplus::Bitmap::FromFile("myImage.xxx");
    //Do some stuff with your bitmap
    Gdiplus::GdiplusShutdown(gdiToken);
}

这是一个使用Gdi+读取位图的不完整示例,但应该让您开始。

它支持最常见的图像格式,如JPEG、GIF、PNG、TIFF、BMP。你可以在这里阅读更多。

如果您想对加载的图像进行图像处理,为什么不使用openCV?

它可以将最常见的图像格式加载到独立于系统的矩阵格式中,并提供大量的图像处理例程。