如何访问在 IWICBitmap 解码器中打开的图像?

How can I access the image opened in the IWICBitmap Decoder?

我正在制作一个用于图像编辑的 .NET 应用程序。为了在 PictureBox 中显示图像,我使用 IWIC 打开它以转换为 HBITMAP,然后使用 FromHbitmap 方法将其粘贴到 PictureBox 中。这意味着虽然 Image 实例存在于应用程序中,但我无法使用它。那么如何更改 reader class 以访问图像?

IMGReader.h

#pragma once

#include <wincodec.h> // WIC codec header for decoding image from file

#include "StringConverter.h"

#pragma comment (lib, "windowscodecs")

class IMGReader
{
    IWICFormatConverter* wConverter;

    UINT BitHeight;
    UINT BitWidth;

    IMGReader() {};
    
    BOOLEAN pathExist(LPCWSTR path);
public:
    IMGReader(LPCWSTR path) { load(path); };
    ~IMGReader() { wConverter->Release(); };

    void load(LPCWSTR path);

    UINT getBitmapHeight()  { return BitHeight; };
    UINT getBitmapWidth()   { return BitWidth; };

    HBITMAP IWICBitmapToHBITMAP();
};

IMGReader.cpp

#include "IMGReader.h"
#include "MTTErrorLogger.h"

#include <filesystem>
#include <vector>

void IMGReader::load(LPCWSTR path)
{
    HRESULT hr;     // For checking errors

    if (!pathExist(path)) { path = L"nodata.jpg"; }; // Check if image really exist in this derectory

    // Craete WIC factory
    IWICImagingFactory* wicFactory = NULL;
    hr = CoCreateInstance(
        CLSID_WICImagingFactory,    // CLS ID of the object making
        NULL,                       // Not part of agregate 
        CLSCTX_INPROC_SERVER,       // DLL runs in the same process
        IID_IWICImagingFactory,     // Ref to interface that communicates with the object
        (LPVOID*)&wicFactory        // The pointer that'll contain factory
    );

    // Create decoder
    IWICBitmapDecoder* wicDecoder = NULL;
    hr = wicFactory->CreateDecoderFromFilename(
        path,                           // Path to reading file
        NULL,                           // No preferred vendor
        GENERIC_READ,                   // Reading file
        WICDecodeMetadataCacheOnLoad,   // Cache on load 
        &wicDecoder                     // Making decoder
    );

    // Read frame from the image
    IWICBitmapFrameDecode* wicFrame = NULL;
    hr = wicDecoder->GetFrame(0, &wicFrame);

    //Create converter
    IWICFormatConverter* wicConverter = NULL;
    hr = wicFactory->CreateFormatConverter(&wicConverter);

    // Setup the converter
    hr = wicConverter->Initialize(
        wicFrame,                       // Frame
        GUID_WICPixelFormat32bppPBGRA,  // Pixel format
        WICBitmapDitherTypeNone,        // Irrelevant
        NULL,                           // No palette needed, irrelevant
        0.0,                            // Alpha transparency % irrelevant
        WICBitmapPaletteTypeCustom      // Irrelevant
    );

    wicConverter->GetSize(&BitHeight, &BitWidth);

    wConverter = wicConverter;
}

HBITMAP IMGReader::IWICBitmapToHBITMAP()
{
    UINT height = BitHeight;    // Bitmap height
    UINT width  = BitWidth;     // Bitmap width
    wConverter->GetSize(&width, &height);

    std::vector<BYTE> buffer(width * height * 4);
    wConverter->CopyPixels(0, width * 4, buffer.size(), buffer.data());

    HBITMAP bitmap = CreateBitmap(width, height, 1, 32, buffer.data()); // Create bitmap from IWICBitmap data

    return bitmap;
}

BOOLEAN IMGReader::pathExist(LPCWSTR path)
{
    if (std::filesystem::exists(path)) {
        return true;
    }
    return false;
}

在主窗体中使用:

private: System::Void newToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e) {
    try
    {
        OpenFileDialog^ ofd = gcnew OpenFileDialog();
        ofd->Filter = "HPL Materials(*.mat)|*.mat";
        if (ofd->ShowDialog() == System::Windows::Forms::DialogResult::OK)
        {
            String^ path = gcnew String(ofd->FileName);
            std::wstring directory = SystemToWide(System::IO::Path::GetDirectoryName(path) + "\"); 
            HplMaterial.setFileDirectory(directory);

            HplMaterial.setMaterialPath(SystemToWide(path));
            HPLMatReader::read(HplMaterial, SystemToWide(path));
            textBox1->Text = WideToSystem(HplMaterial.getDiffuse());
            textBox2->Text = WideToSystem(HplMaterial.getNMap());
            textBox3->Text = WideToSystem(HplMaterial.getSpecular());
            textBox4->Text = WideToSystem(HplMaterial.getHeight());
            textBox5->Text = WideToSystem(HplMaterial.getAlpha());
            textBox6->Text = WideToSystem(HplMaterial.getIllumination());
            textBox7->Text = WideToSystem(HplMaterial.getMaterialPath());
            textBox8->Text = WideToSystem(HplMaterial.getFileDirectory() + HplMaterial.getDiffuse());

            IMGReader IWICReader((HplMaterial.getFileDirectory() + HplMaterial.getDiffuse()).c_str());
            pictureBox1->Image = pictureBox1->Image->FromHbitmap((IntPtr)IWICReader.IWICBitmapToHBITMAP());
            HplMaterial.setResolution(IWICReader.getBitmapHeight(), IWICReader.getBitmapWidth());

            label9->Text = WideToSystem(HplMaterial.getMaterialRes());
            label7->Text = WideToSystem(HplMaterial.getPhysMaterial());
        }
    }
    catch (const std::exception& ex)
    {
        MessageBox::Show(StdToSys(ex.what()), "Error", MessageBoxButtons::OK, MessageBoxIcon::Error);
    }
}

谢谢。

所以我解决了这个问题。问题似乎是我没有将图像数据存储在特定位置,而是在加载方法中每次都创建新的。我在代码中做了以下修改:

Header:

#pragma once

#include <wincodec.h> // WIC codec header for decoding image from file
#include <atlcomcli.h>

    #include "StringConverter.h"
    
    #pragma comment (lib, "windowscodecs")
    
    class IMGReader
    {
        CComPtr<IWICImagingFactory>     m_pImagingFactory;
        CComPtr<IWICBitmapDecoder>      m_pBitmapDecoder;
        CComPtr<IWICBitmapFrameDecode>  m_pFrameDecoder;
        CComPtr<IWICFormatConverter>    m_pConverter;
    
        UINT BitHeight;
        UINT BitWidth;
    
        BOOLEAN pathExist(LPCWSTR path);
    
        IMGReader() {};
    public:
        IMGReader(LPCWSTR path) { load(path); };
        ~IMGReader() { SafeRelease(&m_pConverter); };
    
        void load(LPCWSTR path);
        void clean();
        template <class T> void SafeRelease(T** ppT)
        {
            if (*ppT)
            {
                (*ppT)->Release();
                *ppT = NULL;
            }
        }
    
        UINT getBitmapHeight()  { return BitHeight; };
        UINT getBitmapWidth()   { return BitWidth; };
    
        HBITMAP IWICBitmapToHBITMAP();
    };

实施:

void IMGReader::load(LPCWSTR path)
{
    HRESULT hr;

    m_pImagingFactory   = NULL;
    m_pBitmapDecoder    = NULL;
    m_pFrameDecoder     = NULL;
    m_pConverter        = NULL;

    hr = CoCreateInstance(
        CLSID_WICImagingFactory,    // CLS ID of the object making
        NULL,                       // Not part of agregate 
        CLSCTX_INPROC_SERVER,       // DLL runs in the same process
        IID_IWICImagingFactory,     // Ref to interface that communicates with the object
        (LPVOID*)&m_pImagingFactory // The pointer that'll contain factory
    );

    hr = m_pImagingFactory->CreateDecoderFromFilename(
        path,                           // Path to reading file
        NULL,                           // No preferred vendor
        GENERIC_READ,                   // Reading file
        WICDecodeMetadataCacheOnLoad,   // Cache on load 
        &m_pBitmapDecoder               // Making decoder
    );

    hr = m_pBitmapDecoder->GetFrame(0, &m_pFrameDecoder);

    hr = m_pImagingFactory->CreateFormatConverter(&m_pConverter);

    hr = m_pConverter->Initialize(
        m_pFrameDecoder,                // Frame
        GUID_WICPixelFormat32bppPBGRA,  // Pixel format
        WICBitmapDitherTypeNone,        // Irrelevant
        NULL,                           // No palette needed, irrelevant
        0.0,                            // Alpha transparency % irrelevant
        WICBitmapPaletteTypeCustom      // Irrelevant
    );

    this->m_pConverter->GetSize(&BitHeight, &BitWidth);
}