Как взаимодействовать с изображением, отрытым в IWICFormatConverter?

Я пишу приложение для обработки изображений (Преимущественно dds формата) на .NET Core. Для декодирования форматов я с пользую wincodecs, полученный результат конвертирую в HBTIMAP для дальнейшего вывода изображение в PictureBox. Изображение без проблем выводится, однако при попытке начать с ним работать - программа сообщает об ошибке доступа, поскольку оно уже используется в классе отвечающем за декодирование форматов.

Часть заголовочного документа с интерфейсом, в котором я этим пользуюсь:

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)
        {
            ...

            IMGReader IWICReader((HplMaterial.getFileDirectory() + HplMaterial.getDiffuse()).c_str());
            pictureBox1->Image = pictureBox1->Image->FromHbitmap((IntPtr)IWICReader.IWICBitmapToHBITMAP());
            HplMaterial.setResolution(IWICReader.getBitmapHeight(), IWICReader.getBitmapWidth());
            
            ...
        }
    }
    catch (const std::exception& ex)
    {
        MessageBox::Show(StdToSys(ex.what()), "Error", MessageBoxButtons::OK, MessageBoxIcon::Error);
    }

}

Заголовочный файл читающего класса:

#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;

    BOOLEAN pathExist(LPCWSTR path);

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

    void load(LPCWSTR path);
    template <class T> void SafeRelease(T** ppT)
    {
        if (*ppT)
        {
            (*ppT)->Release();
            *ppT = NULL;
        }
    }

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

    HBITMAP IWICBitmapToHBITMAP();
};

Имплементационный файл класса:

#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;
}

Существует ли какой-то аналог Image->Clone() для подобного подхода? Так же было замечено, что рри удалении IWIC элементов через Release() в методе load(), в PictureBox изображение не выводится. Спасибо.


Ответы (1 шт):

Автор решения: Nikita

Итак, я решил проблему. Проблема оказалась довольно банальной - я не хранил данные об изображении, каждый раз создавая новые методы IWIC из-за чего прошлые просто уходили в память. Всё заработало, как только перенёс их в класс.

Заголовочный:

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);
}
 
→ Ссылка