高级Win32映像文件I / O?

时间:2014-05-06 13:35:58

标签: c++ windows image winapi io

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

我希望支持合理数量的文件格式。

我需要为位图提供一些低级访问以进行图像处理。

我在互联网上已经阅读了很多内容,看起来Windows DIB可能是一个合理的内存表示。除此之外,我无法找到示例代码,例如,将JPEG或PNG读入内存中的DIB。

感谢您的任何建议。

4 个答案:

答案 0 :(得分:2)

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

这将是我的第一选择。

次要选择,GDI +(请注意,它需要相当多的摆弄初级内容以获得普通的高质量,至少在几年前它仍然依赖于minmax <来自<windows.h>的em>宏。第三个选择,问题评论中提到的Windows Imaging Component。第四选择,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;
}

答案 1 :(得分:1)

我会使用GDI+ Bitmap class。它支持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
);

在位图对象上。

答案 2 :(得分:0)

#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。您可以阅读更多here

答案 3 :(得分:0)

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

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