c++mfchbitmap

convert SVG to HBITMAP using NanoSVG color issue


I tried converting SVG to HBITMAP using NanoSVG. Everything works fine but the color of the Bitamp or saved png is different then the original svg. Please help me to fix this

I tried below code

// Load the SVG file
NSVGimage* image = nsvgParseFromFile(filename, "px", 96);

// Create a bitmap with the same dimensions as the SVG image
BITMAPINFO bmpinfo = { 0 };
bmpinfo.bmiHeader.biSize = sizeof(bmpinfo.bmiHeader);
bmpinfo.bmiHeader.biWidth = static_cast<LONG>(image->width);
bmpinfo.bmiHeader.biHeight = -static_cast<LONG>(image->height);
bmpinfo.bmiHeader.biPlanes = 1;
bmpinfo.bmiHeader.biBitCount = 32;
bmpinfo.bmiHeader.biCompression = BI_RGB;

void* bits = nullptr;   
HBITMAP hbitmap = CreateDIBSection(NULL, &bmpinfo, DIB_RGB_COLORS, &bits, nullptr, 0);

// Render the SVG image to the bitmap
NSVGrasterizer* rast = nsvgCreateRasterizer();

nsvgRasterize(rast, image, 0, 0, 1, (unsigned char*)bits, static_cast<int>(image->width), static_cast<int>(image->height), static_cast<int>(image->width * 4));
nsvgDeleteRasterizer(rast);

// Clean up
nsvgDelete(image);

Solution

  • The problem is the red and blue channels are swapped because CreateDIBSection expects BGR ordered color and NanoSVG is outputting RGB. The documentation of CreateDIBSection is less than clear on this point but if you look at the definition of RGBQUAD you can see it is layed out as blue-green-red.

    Anyway I don't see a way to control the byte order coming out of NanoSVG, so I don't think you can do better than just manually swapping the bytes. As below ... ( I also fixed a bug in your code having to do with the casts from floating point to integer dimensions. The bug was that stride should be four times the integer width, not four times the width as a floating point value.)

    void rgb_to_bgr(unsigned char* bits, int n) {
        for (int i = 0; i < n; i += 4) {
            std::swap(bits[i], bits[i + 2]);
        }
    }
    
    HBITMAP paint_svg(const char* filename) {
        // Load the SVG file
        NSVGimage* image = nsvgParseFromFile(filename, "px", 96);
    
        int wd = static_cast<int>(std::ceil(image->width));
        int hgt = static_cast<int>(std::ceil(image->height));
        int stride = wd * 4;
    
        // Create a bitmap with the same dimensions as the SVG image
        BITMAPINFO bmpinfo = { 0 };
        bmpinfo.bmiHeader.biSize = sizeof(bmpinfo.bmiHeader);
        bmpinfo.bmiHeader.biWidth = wd;
        bmpinfo.bmiHeader.biHeight = -hgt;
        bmpinfo.bmiHeader.biPlanes = 1;
        bmpinfo.bmiHeader.biBitCount = 32;
        bmpinfo.bmiHeader.biCompression = BI_RGB;
    
        unsigned char* bits = nullptr;
        HBITMAP hbitmap = CreateDIBSection(NULL, &bmpinfo, DIB_RGB_COLORS, 
            reinterpret_cast<void**>(&bits), nullptr, 0);
    
        // Render the SVG image to the bitmap
        NSVGrasterizer* rast = nsvgCreateRasterizer();
    
        nsvgRasterize(rast, image, 0, 0, 1, bits, wd, hgt, stride);
        rgb_to_bgr(bits, hgt * stride);
    
        nsvgDeleteRasterizer(rast);
    
        // Clean up
        nsvgDelete(image);
        return hbitmap
    }