dotnet-gdi-rendering

Using GDI+ Bitmap as surface can be as simple as that (given in managed C++):

// Create the target bitmap to draw to.
Drawing::Bitmap contentBitmap= new Drawing::Bitmap(Width, Height);

// Create a Graphics object to have a device context we can pass to cairo.
Graphics^ g = Graphics::FromImage(contentBitmap);
g->SmoothingMode = SmoothingMode::HighQuality;

// Do some drawing in GDI+ to have some initial content, like:
// Fill interior.
Brush^ brush = gcnew SolidBrush(Color::FromArgb(191, 1, 0, 0));
g->FillPath(brush, innerPath);
delete brush;

// Get the device context handle from this graphics object and create a Win32 cairo surface from it.
IntPtr hdc= g->GetHdc();
cairo_surface_t* surface= cairo_win32_surface_create((HDC) hdc.ToPointer());

// For drawing we need a cairo context.
cairo_t* cr= cairo_create(surface);

// Now you are ready to draw to that using any of the cairo calls.
...

// Don't forget the cleanup.
cairo_destroy(cr);
cairo_surface_destroy(surface);

g->ReleaseHdc(hdc);

This works quite well but has one big disadvantage: cairo_win32_surface_create() always returns a 24bit surface. So what if we have a bitmap with an alpha channel (like a png image we loaded and want to add something to it)? With the code above the alpha channel used in your cairo commands is lost.

One could create a 32bit DIB surface (using cairo_win32_surface_create_with_dib()), but that creates a separate surface, which needs to be merged later to your actual target. Using an API like AlphaBlend (via pinvoke) produces exactly the same result as in the code above. So this is no help either.

The solution for this dilemma is an image surface (e.g. you get an image surface when you load png images in cairo).

// Instead of getting an HDC and and use cairo_win32_surface we get directly
// to the pixels in the bitmap and create the image surface from that.
Imaging::BitmapData^ bitmapData= contentBitmap->LockBits(Drawing::Rectangle(0, 0, Width, Height),
  Imaging::ImageLockMode::ReadWrite, contentBitmap->PixelFormat);
unsigned char* data= (unsigned char*) bitmapData->Scan0.ToPointer();
cairo_surface_t* surface= cairo_image_surface_create_for_data(data, CAIRO_FORMAT_ARGB32, Width, Height,
  bitmapData->Stride);

// The rest is the same as above, except we have to unlock the bits after we finished drawing.
contentBitmap->UnlockBits(bitmapData);