1

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);
latosvarun
  • 93
  • 6

1 Answers1

1

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
}
jwezorek
  • 8,592
  • 1
  • 29
  • 46
  • How can I change the color of svg to Greyscale Bitmap or other colors? – latosvarun Apr 25 '23 at 10:30
  • you could convert to gray scale in the same function where you convert to BGR. Like leave it as a three-channel image but just use grays instead of colors. There is a formula you can google for converting an RGB value to a gray value. – jwezorek Apr 25 '23 at 15:40
  • I have issue using the generated Bitmap( see https://stackoverflow.com/questions/76095127/issue-adding-a-hbitmap-in-cmfctoolbar ) . Can you help me out – latosvarun Apr 26 '23 at 16:09