c++-cxlumia-imaging-sdk

How do I point a Lumia::Imaging::Bitmap at a byte buffer in C++?


For some context, we have a Visual Studio solution that has three projects. The main project is a C# application, we have a C++ library that is essentially an image rendering pipeline, and we have a c++/cx WinRT component as the bridge between the two.

We are offloading some of our image filter chains (in the C++ library) to the Lumia Imaging SDK in the c++/cx WinRT project. Since we are working with byte buffers, we were unsure as to how to point a Lumia::Imaging::Bitmap^ at our buffers without doing a copy. (We can't call AsBuffer() on the uchar* (i.e., our byte array) because that extension method is unavailable to us in C++ land.)

So the question is this: given an input uchar* to some method that will implement a Lumia filter chain, how do we create a Bitmap (or BitmapImageSource) that doesn't result in copying the buffer?

Here is some sample code that we need to fill in the blanks on:

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

Solution

  • Ok, so here was some hacking around we did that does not achieve what we need, because it creates a copy:

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

    But after some thought, we decided to try subclassing IBuffer with our own implementation. Kudos to jmorrill for his help on this post on MSDN:

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

    Here is our implementation of 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;
    };
    

    And we use interop do create the ImageBuffer instance:

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

    Hope this is of help to someone!