Как указать Lumia::Imaging::Bitmap на байтовый буфер в C++?

Для некоторого контекста у нас есть решение Visual Studio с тремя проектами. Основной проект — это приложение C#, у нас есть библиотека C++, которая по сути является конвейером рендеринга изображений, и у нас есть компонент WinRT c++/cx в качестве связующего звена между ними.

Мы переносим некоторые из наших цепочек фильтров изображений (в библиотеке C++) в Lumia Imaging SDK в проекте c++/cx WinRT. Поскольку мы работаем с байтовыми буферами, мы не знали, как указать Lumia::Imaging::Bitmap^ на наши буферы, не копируя их. (Мы не можем вызвать AsBuffer() для uchar* (т. е. нашего массива байтов), потому что этот метод расширения недоступен для нас в стране C++.)

Итак, вопрос заключается в следующем: имея входной uchar* для некоторого метода, который будет реализовывать цепочку фильтров Lumia, как мы создадим Bitmap (или BitmapImageSource), который не приведет к копированию буфера?

Вот пример кода, который нам нужно заполнить пробелами:

Bitmap^ MyClass::GetBitmapImageDestination(uchar *imageBytes, int imageWidth, int imageHeight, ColorMode colorMode) {
    int channels = colorMode == ColorMode::Gray8 ? 1 : 4;

    IBuffer^ byteBuffer = ..... ????

    return ref new Bitmap(Windows::Foundation::Size((float)imageWidth, (float)imageHeight), colorMode, (uint)(imageWidth * channels), byteBuffer); 
}

person joelg    schedule 04.11.2015    source источник


Ответы (1)


Итак, вот некоторые хакерские действия, которые мы сделали, которые не достигают того, что нам нужно, потому что они создают копию:

Bitmap^ MyClass::GetBitmapImageDestination(uchar *imageBytes, int imageWidth, int imageHeight, ColorMode colorMode) {
    int channels = colorMode == ColorMode::Gray8 ? 1 : 4;

    DataWriter^ writer = ref new DataWriter();
    Platform::ArrayReference<uchar, 1> tempArray(imageBytes, imageWidth * imageHeight * 4);
    writer->WriteBytes(tempArray);
    IBuffer^ byteBuffer = writer->DetachBuffer();   

    return ref new Bitmap(Windows::Foundation::Size((float)imageWidth, (float)imageHeight), colorMode, (uint)(imageWidth * channels), byteBuffer);
}

Но после некоторых размышлений мы решили попробовать создать подкласс IBuffer с нашей собственной реализацией. Престижность jmorrill за его помощь в этом посте на MSDN:

https://social.msdn.microsoft.com/Forums/en-US/816e5718-224d-4bb7-bf06-230e9c6cda5b/how-to-create-an-ibuffer-from-scratch?forum=winappswithnativecode

Вот наша реализация IBuffer:

class ImageBuffer : public Microsoft::WRL::RuntimeClass<
                           Microsoft::WRL::RuntimeClassFlags< Microsoft::WRL::RuntimeClassType::WinRtClassicComMix >,
                           ABI::Windows::Storage::Streams::IBuffer,
                           Windows::Storage::Streams::IBufferByteAccess >
    {

    public:
        virtual ~ImageBuffer()
        {
        }

        STDMETHODIMP RuntimeClassInitialize(UINT totalSize, UCHAR* data)
        {
            _imageLength = totalSize;
            _imageData = data;
            return S_OK;
        }

        STDMETHODIMP Buffer( byte **value)
        {
            *value = &_imageData[0];
            return S_OK;
        }

         STDMETHODIMP get_Capacity(UINT32 *value)
         {
             *value = _imageLength;
             return S_OK;
         }

        STDMETHODIMP get_Length(UINT32 *value)
        {
            *value = _imageLength;
            return S_OK;
        }

        STDMETHODIMP put_Length(UINT32 value)
        {
            if(value > _imageLength)
                return E_INVALIDARG;
            _imageLength = value;
            return S_OK;
        }
    private:
        UINT32 _imageLength;
        UCHAR *_imageData;
};

И мы используем взаимодействие, чтобы создать экземпляр ImageBuffer:

Bitmap^ MyClass::GetBitmapImageDestination(uchar *imageBytes, int imageWidth, int imageHeight, ColorMode colorMode) {
    int channels = colorMode == ColorMode::Gray8 ? 1 : 4;

    ComPtr<ImageBuffer> imageBuffer;
    MakeAndInitialize<ImageBuffer>(&imageBuffer, imageWidth * imageHeight * channels, imageBytes);
    auto iinspectable = (IInspectable*)reinterpret_cast<IInspectable*>(imageBuffer.Get());
    IBuffer^ byteBuffer = reinterpret_cast<IBuffer^>(iinspectable);

    return ref new Bitmap(Windows::Foundation::Size((float)imageWidth, (float)imageHeight), colorMode, (uint)(imageWidth * channels), byteBuffer);
}

Надеюсь, это поможет кому-то!

person joelg    schedule 04.11.2015