public override void FloodFill(System.Drawing.Point pt) { pts.Clear(); watch.Reset(); watch.Start(); PrepareForFloodFill(pt); unsafe { bitmapStride = bitmap.Stride; scan0 = (byte *)bitmap.Iptr; int x = pt.X; int y = pt.Y; int loc = CoordsToIndex(ref x, ref y); byte *colorPtr = ((byte *)(scan0 + loc)); startColor = new byte[] { colorPtr[0], colorPtr[1], colorPtr[2] }; LinearFloodFill4(ref x, ref y); bool[] pixelsChecked = this.pixelsChecked; while (ranges.Count > 0) { FloodFillRange range = ranges.Dequeue(); //START THE LOOP UPWARDS AND DOWNWARDS int upY = range.Y - 1;//so we can pass the y coord by ref int downY = range.Y + 1; byte *upPtr = (byte *)(scan0 + CoordsToIndex(ref range.StartX, ref upY)); byte *downPtr = (byte *)(scan0 + CoordsToIndex(ref range.StartX, ref downY)); int downPxIdx = (bitmapWidth * (range.Y + 1)) + range.StartX; //CoordsToPixelIndex(range.StartX,range.Y+1); int upPxIdx = (bitmapWidth * (range.Y - 1)) + range.StartX; //CoordsToPixelIndex(range.StartX, range.Y - 1); for (int i = range.StartX; i <= range.EndX; i++) { //START LOOP UPWARDS //if we're not above the top of the bitmap and the pixel above this one is within the color tolerance if (range.Y > 0 && CheckPixel(ref upPtr) && (!(pixelsChecked[upPxIdx]))) { LinearFloodFill4(ref i, ref upY); } //START LOOP DOWNWARDS if (range.Y < (bitmapHeight - 1) && CheckPixel(ref downPtr) && (!(pixelsChecked[downPxIdx]))) { LinearFloodFill4(ref i, ref downY); } upPtr += bitmapPixelFormatSize; downPtr += bitmapPixelFormatSize; downPxIdx++; upPxIdx++; } } } watch.Stop(); }
/// <summary>Removes and returns the <see cref="FloodFillRange"/> at the beginning of the queue.</summary> public FloodFillRange Dequeue() { FloodFillRange range = new FloodFillRange(); if (size > 0) { range = array[head]; array[head] = new FloodFillRange(); head++; //advance head position size--; //update size to exclude dequeued item } return(range); }
/// <summary> /// Fills the specified point on the bitmap with the fill value. /// </summary> /// <param name="pt">The starting point for the fill.</param> public override FillData FloodFill(Vector2Int pt) { ranges = new FloodFillRangeQueue(((gridWidth + gridHeight) / 2) * 5); int x = pt.x; int y = pt.y; //***Do first call to floodfill. LinearFill(ref x, ref y); //***Call floodfill routine while floodfill ranges still exist on the queue while (ranges.Count > 0) { //**Get Next Range Off the Queue FloodFillRange range = ranges.Dequeue(); //**Check Above and Below Each cell in the Floodfill Range int upY = range.Y - 1;//so we can pass the y coord by ref int downY = range.Y + 1; int i = range.StartX; if (range.StartX > 0) { i--; //check the diagonals } int downPxIdx = (gridWidth * (range.Y + 1)) + i; int upPxIdx = (gridWidth * (range.Y - 1)) + i; while (i <= range.EndX + 1 && i < gridWidth) { //*Start Fill Upwards //if we're not above the top of the grid and the cell above this one is free if (range.Y > 0 && (!gridCells[upPxIdx]) && CheckCell(ref upPxIdx)) { LinearFill(ref i, ref upY); } //*Start Fill Downwards //if we're not below the bottom of the grid and the pixel below this one is free if (range.Y < (gridHeight - 1) && (!gridCells[downPxIdx]) && CheckCell(ref downPxIdx)) { LinearFill(ref i, ref downY); } downPxIdx++; upPxIdx++; i++; } } return(new FillData(gridWidth, gridCells)); }
/// <summary> /// Finds the furthermost left and right boundaries of the fill area /// on a given y coordinate, starting from a given x coordinate, filling as it goes. /// Adds the resulting horizontal range to the queue of floodfill ranges, /// to be processed in the main loop. /// </summary> /// <param name="x">The x coordinate to start from.</param> /// <param name="y">The y coordinate to check at.</param> void LinearFill(ref int x, ref int y) { //***Find Left Edge of fill area int lFillLoc = x; //the location to check/fill on the left int idx = CoordsToIndex(ref x, ref y); //the index of the current location while (true) { //**fill with the fillValue gridCells[idx] = true; //**de-increment lFillLoc--; //de-increment counter idx -= 1; //de-increment index //**exit loop if we're at edge of fill area if (lFillLoc <= 0 || (gridCells[idx]) || !CheckCell(ref idx)) { break; } } lFillLoc++; //***Find Right Edge of free area int rFillLoc = x; //the location to check/fill on the right idx = CoordsToIndex(ref x, ref y); while (true) { //**fill with the fillValue gridCells[idx] = true; //**increment rFillLoc++; //increment counter idx += 1; //increment index //**exit loop if we're at edge of fill area if (rFillLoc >= gridWidth || gridCells[idx] || !CheckCell(ref idx)) { break; } } rFillLoc--; //add range to queue FloodFillRange r = new FloodFillRange(lFillLoc, rFillLoc, y); ranges.Enqueue(ref r); }
/// <summary> /// Finds the furthermost left and right boundaries of the fill area /// on a given y coordinate, starting from a given x coordinate, filling as it goes. /// Adds the resulting horizontal range to the queue of floodfill ranges, /// to be processed in the main loop. /// </summary> /// <param name="x">The x coordinate to start from.</param> /// <param name="y">The y coordinate to check at.</param> void LinearFill(ref int x, ref int y) { //cache some bitmap and fill info in local variables for a little extra speed byte[] bitmapBits = this.bitmapBits; bool[] pixelsChecked = this.pixelsChecked; byte[] byteFillColor = this.byteFillColor; int bitmapPixelFormatSize = this.bitmapPixelFormatSize; int bitmapWidth = this.bitmapWidth; //***Find Left Edge of Color Area int lFillLoc = x; //the location to check/fill on the left int idx = CoordsToByteIndex(ref x, ref y); //the byte index of the current location int pxIdx = (bitmapWidth * y) + x; //CoordsToPixelIndex(x,y); while (true) { //**fill with the color bitmapBits[idx] = byteFillColor[0]; bitmapBits[idx + 1] = byteFillColor[1]; bitmapBits[idx + 2] = byteFillColor[2]; //**indicate that this pixel has already been checked and filled pixelsChecked[pxIdx] = true; //**screen update for 'slow' fill if (slow) { UpdateScreen(ref lFillLoc, ref y); } //**de-increment lFillLoc--; //de-increment counter pxIdx--; //de-increment pixel index idx -= bitmapPixelFormatSize; //de-increment byte index //**exit loop if we're at edge of bitmap or color area if (lFillLoc <= 0 || (pixelsChecked[pxIdx]) || !CheckPixel(ref idx)) { break; } } lFillLoc++; //***Find Right Edge of Color Area int rFillLoc = x; //the location to check/fill on the left idx = CoordsToByteIndex(ref x, ref y); pxIdx = (bitmapWidth * y) + x; while (true) { //**fill with the color bitmapBits[idx] = byteFillColor[0]; bitmapBits[idx + 1] = byteFillColor[1]; bitmapBits[idx + 2] = byteFillColor[2]; //**indicate that this pixel has already been checked and filled pixelsChecked[pxIdx] = true; //**screen update for 'slow' fill if (slow) { UpdateScreen(ref rFillLoc, ref y); } //**increment rFillLoc++; //increment counter pxIdx++; //increment pixel index idx += bitmapPixelFormatSize; //increment byte index //**exit loop if we're at edge of bitmap or color area if (rFillLoc >= bitmapWidth || pixelsChecked[pxIdx] || !CheckPixel(ref idx)) { break; } } rFillLoc--; //add range to queue FloodFillRange r = new FloodFillRange(lFillLoc, rFillLoc, y); ranges.Enqueue(ref r); }
unsafe void LinearFloodFill4(ref int x, ref int y) { //offset the pointer to the point passed in byte *p = (byte *)(scan0 + (CoordsToIndex(ref x, ref y))); //cache some bitmap and fill info in local variables for a little extra speed bool[] pixelsChecked = this.pixelsChecked; byte[] byteFillColor = this.byteFillColor; int bitmapPixelFormatSize = this.bitmapPixelFormatSize; int bitmapWidth = this.bitmapWidth; //FIND LEFT EDGE OF COLOR AREA int lFillLoc = x; //the location to check/fill on the left byte *ptr = p; //the pointer to the current location int pxIdx = (bitmapWidth * y) + x; while (true) { ptr[0] = byteFillColor[0]; //fill with the color ptr[1] = byteFillColor[1]; ptr[2] = byteFillColor[2]; if (bitmapPixelFormatSize == 4) { ptr[3] = byteFillColor[3]; } pixelsChecked[pxIdx] = true; lFillLoc--; //de-increment counter ptr -= bitmapPixelFormatSize; //de-increment pointer pxIdx--; if (lFillLoc <= 0 || !CheckPixel(ref ptr) || (pixelsChecked[pxIdx])) { break; //exit loop if we're at edge of bitmap or color area } } lFillLoc++; //FIND RIGHT EDGE OF COLOR AREA int rFillLoc = x; //the location to check/fill on the left ptr = p; pxIdx = (bitmapWidth * y) + x; while (true) { ptr[0] = byteFillColor[0]; //fill with the color ptr[1] = byteFillColor[1]; ptr[2] = byteFillColor[2]; if (bitmapPixelFormatSize == 4) { ptr[3] = byteFillColor[3]; } pixelsChecked[pxIdx] = true; rFillLoc++; //increment counter ptr += bitmapPixelFormatSize; //increment pointer pxIdx++; if (rFillLoc >= bitmapWidth || !CheckPixel(ref ptr) || (pixelsChecked[pxIdx])) { break; //exit loop if we're at edge of bitmap or color area } } rFillLoc--; FloodFillRange r = new FloodFillRange(lFillLoc, rFillLoc, y); ranges.Enqueue(ref r); }