C# Win API ExtFloodFill Issue
-
I was having trouble using the ExtFloodFill operation to fill an area on a bitmap. Either, the code was setting my entire image to a specific color (using type == 0 for borderfill), or the call to ExtFloodFill returned false because the color I sent it didn't match the color of the background (for type == 1). After searching for about two hours, I finally found some obscure site that had an answer for me. I have copied the working c# code below. I still feel like I need to call DeleteObject for one of the brushes, but I'm not sure. If anyone can add insight to this code (as far a memory management or why I cannot use ExtFloodFill on the DC retrieved from the graphics object), please do so. [DllImport("gdi32.dll")] private static bool BitBlt(IntPtr hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, int dwRop); [DllImport("gdi32.dll")] private static IntPtr CreateCompatibleDC(IntPtr hdc); [DllImport("gdi32.dll")] private static IntPtr CreateSolidBrush(int color); [DllImport("gdi32.dll")] private static bool DeleteDC(IntPtr hdc); [DllImport("gdi32.dll")] private static bool ExtFloodFill(IntPtr hdc, int x, int y, int color, uint type); [DllImport("gdi32.dll")] private static IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj); private static int ToRGB(Color c) { return (c.B << 16) | (c.G << 8) | c.R; } public enum TRO : uint { BLACKNESS = 0x00000042, NOTSRCERASE = 0x001100A6, NOTSRCCOPY = 0x00330008, SRCERASE = 0x00440328, DSTINVERT = 0x00550009, PATINVERT = 0x005A0049, SRCINVERT = 0x00660046, SRCAND = 0x008800C6, MERGEPAINT = 0x00BB0226, MERGECOPY = 0x00C000CA, SRCCOPY = 0x00CC0020, SRCPAINT = 0x00EE0086, PATCOPY = 0x00F00021, PATPAINT = 0x00FB0A09, WHITENESS = 0x00FF0062, CAPTUREBLT = 0x40000000, NOMIRRORBITMAP = 0x80000000, } public static bool FloodFill(Bitmap bitmap, Location location, Color backColor, color fillColor) { Graphics g = null; IntPtr hdc1 = IntPtr.Zero, hdc2 = IntPtr.Zero, oldBrush = IntPtr.Zero; try { g = Graphics.FromImage(bitmap); hdc1 = g.GetHDC(); hdc2 = CreateCompatibleDC(hdc1); SelectObject(hdc2, bitmap.GetHbitmap()); IntPtr newBrush = CreateSolidBrush(ToRGB(fillColor)); oldBrush = SelectObject(hdc2, newBrush); if (ExtFloodFill(hdc2, location.X, location.Y, ToRGB(backCo
-
I was having trouble using the ExtFloodFill operation to fill an area on a bitmap. Either, the code was setting my entire image to a specific color (using type == 0 for borderfill), or the call to ExtFloodFill returned false because the color I sent it didn't match the color of the background (for type == 1). After searching for about two hours, I finally found some obscure site that had an answer for me. I have copied the working c# code below. I still feel like I need to call DeleteObject for one of the brushes, but I'm not sure. If anyone can add insight to this code (as far a memory management or why I cannot use ExtFloodFill on the DC retrieved from the graphics object), please do so. [DllImport("gdi32.dll")] private static bool BitBlt(IntPtr hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, int dwRop); [DllImport("gdi32.dll")] private static IntPtr CreateCompatibleDC(IntPtr hdc); [DllImport("gdi32.dll")] private static IntPtr CreateSolidBrush(int color); [DllImport("gdi32.dll")] private static bool DeleteDC(IntPtr hdc); [DllImport("gdi32.dll")] private static bool ExtFloodFill(IntPtr hdc, int x, int y, int color, uint type); [DllImport("gdi32.dll")] private static IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj); private static int ToRGB(Color c) { return (c.B << 16) | (c.G << 8) | c.R; } public enum TRO : uint { BLACKNESS = 0x00000042, NOTSRCERASE = 0x001100A6, NOTSRCCOPY = 0x00330008, SRCERASE = 0x00440328, DSTINVERT = 0x00550009, PATINVERT = 0x005A0049, SRCINVERT = 0x00660046, SRCAND = 0x008800C6, MERGEPAINT = 0x00BB0226, MERGECOPY = 0x00C000CA, SRCCOPY = 0x00CC0020, SRCPAINT = 0x00EE0086, PATCOPY = 0x00F00021, PATPAINT = 0x00FB0A09, WHITENESS = 0x00FF0062, CAPTUREBLT = 0x40000000, NOMIRRORBITMAP = 0x80000000, } public static bool FloodFill(Bitmap bitmap, Location location, Color backColor, color fillColor) { Graphics g = null; IntPtr hdc1 = IntPtr.Zero, hdc2 = IntPtr.Zero, oldBrush = IntPtr.Zero; try { g = Graphics.FromImage(bitmap); hdc1 = g.GetHDC(); hdc2 = CreateCompatibleDC(hdc1); SelectObject(hdc2, bitmap.GetHbitmap()); IntPtr newBrush = CreateSolidBrush(ToRGB(fillColor)); oldBrush = SelectObject(hdc2, newBrush); if (ExtFloodFill(hdc2, location.X, location.Y, ToRGB(backCo
-
I didn't post this necessarily looking for an answer as to whether I need to delete the brush. The purpose of posting this is so when people search for ExtFloodFill, they can easily find out what needs to be done to get around the fact that the DC created from the graphics object cannot be used by this method. I only asked about the brush so that future users of the code can ensure on their own that there are no memory leaks, instead of copying the code verbatim. Jeff
-
I was having trouble using the ExtFloodFill operation to fill an area on a bitmap. Either, the code was setting my entire image to a specific color (using type == 0 for borderfill), or the call to ExtFloodFill returned false because the color I sent it didn't match the color of the background (for type == 1). After searching for about two hours, I finally found some obscure site that had an answer for me. I have copied the working c# code below. I still feel like I need to call DeleteObject for one of the brushes, but I'm not sure. If anyone can add insight to this code (as far a memory management or why I cannot use ExtFloodFill on the DC retrieved from the graphics object), please do so. [DllImport("gdi32.dll")] private static bool BitBlt(IntPtr hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, int dwRop); [DllImport("gdi32.dll")] private static IntPtr CreateCompatibleDC(IntPtr hdc); [DllImport("gdi32.dll")] private static IntPtr CreateSolidBrush(int color); [DllImport("gdi32.dll")] private static bool DeleteDC(IntPtr hdc); [DllImport("gdi32.dll")] private static bool ExtFloodFill(IntPtr hdc, int x, int y, int color, uint type); [DllImport("gdi32.dll")] private static IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj); private static int ToRGB(Color c) { return (c.B << 16) | (c.G << 8) | c.R; } public enum TRO : uint { BLACKNESS = 0x00000042, NOTSRCERASE = 0x001100A6, NOTSRCCOPY = 0x00330008, SRCERASE = 0x00440328, DSTINVERT = 0x00550009, PATINVERT = 0x005A0049, SRCINVERT = 0x00660046, SRCAND = 0x008800C6, MERGEPAINT = 0x00BB0226, MERGECOPY = 0x00C000CA, SRCCOPY = 0x00CC0020, SRCPAINT = 0x00EE0086, PATCOPY = 0x00F00021, PATPAINT = 0x00FB0A09, WHITENESS = 0x00FF0062, CAPTUREBLT = 0x40000000, NOMIRRORBITMAP = 0x80000000, } public static bool FloodFill(Bitmap bitmap, Location location, Color backColor, color fillColor) { Graphics g = null; IntPtr hdc1 = IntPtr.Zero, hdc2 = IntPtr.Zero, oldBrush = IntPtr.Zero; try { g = Graphics.FromImage(bitmap); hdc1 = g.GetHDC(); hdc2 = CreateCompatibleDC(hdc1); SelectObject(hdc2, bitmap.GetHbitmap()); IntPtr newBrush = CreateSolidBrush(ToRGB(fillColor)); oldBrush = SelectObject(hdc2, newBrush); if (ExtFloodFill(hdc2, location.X, location.Y, ToRGB(backCo
Try this
[DllImport("gdi32.dll", SetLastError = true)] public static extern bool DeleteObject(IntPtr hObject); public static uint FLOODFILLBORDER = 0; public static uint FLOODFILLSURFACE = 1; public static bool FloodFill(Bitmap bitmap, Location location, Color backColor, color fillColor) { bool succeed = false; using (Graphics ig = Graphics.FromImage(bitmap)) // ig point to your bitmap { IntPtr hdc1 = IntPtr.Zero, hdc2 = IntPtr.Zero; IntPtr hBitmap, hBrush, prevBrush; try { // the DC is your bitmap, so you don't need to select it hdc1 = ig.GetHdc(); // create your brush with "fillColor" hBrush = CreateSolidBrush( ColorTranslator.ToWin32(fillColor) ); // set your brush into the DC prevBrush = SelectObject(hdc1, hBrush); // fill the bitmap area with "backColor" succeed = ExtFloodFill(hdc1, location.X, location.Y, ColorTranslator.ToWin32(backColor), FLOODFILLSURFACE); // restore the previous brush SelectObject(hdc1, prevBrush); // delete your brush (memory usage) DeleteObject(hBrush); if (succeed) { hdc2 = CreateCompatibleDC(hdc1); succeed = BitBlt(hdc1, 0, 0, bitmap.Width, bitmap.Height, hdc2, 0, 0, (int)TRO.SRCCOPY); DeleteDC(hdc2); } } finally { if (IntPtr.Zero != hdc1) ig.ReleaseHdc(hdc1); } } return succeed; }
You should useSetLastError = true
in your p-invoke function sig, and then callint errorCode = Marshal.GetLastError()
to know if the function succeed (and if not, you have the win32 error). Hope this helps, G. -
Try this
[DllImport("gdi32.dll", SetLastError = true)] public static extern bool DeleteObject(IntPtr hObject); public static uint FLOODFILLBORDER = 0; public static uint FLOODFILLSURFACE = 1; public static bool FloodFill(Bitmap bitmap, Location location, Color backColor, color fillColor) { bool succeed = false; using (Graphics ig = Graphics.FromImage(bitmap)) // ig point to your bitmap { IntPtr hdc1 = IntPtr.Zero, hdc2 = IntPtr.Zero; IntPtr hBitmap, hBrush, prevBrush; try { // the DC is your bitmap, so you don't need to select it hdc1 = ig.GetHdc(); // create your brush with "fillColor" hBrush = CreateSolidBrush( ColorTranslator.ToWin32(fillColor) ); // set your brush into the DC prevBrush = SelectObject(hdc1, hBrush); // fill the bitmap area with "backColor" succeed = ExtFloodFill(hdc1, location.X, location.Y, ColorTranslator.ToWin32(backColor), FLOODFILLSURFACE); // restore the previous brush SelectObject(hdc1, prevBrush); // delete your brush (memory usage) DeleteObject(hBrush); if (succeed) { hdc2 = CreateCompatibleDC(hdc1); succeed = BitBlt(hdc1, 0, 0, bitmap.Width, bitmap.Height, hdc2, 0, 0, (int)TRO.SRCCOPY); DeleteDC(hdc2); } } finally { if (IntPtr.Zero != hdc1) ig.ReleaseHdc(hdc1); } } return succeed; }
You should useSetLastError = true
in your p-invoke function sig, and then callint errorCode = Marshal.GetLastError()
to know if the function succeed (and if not, you have the win32 error). Hope this helps, G.