如何在Android中使用洪水填充algorithm?

我是Android编程新手,最近我尝试写一个简单的应用程序,只是为了练习! 在这一个,我想用户的水龙头上的图像上色,但我不知道如何启动它。 我读过不同的话题,说使用“洪水填充”algorithm。 我在网上find它,但是我不知道如何将它放到我的简单应用程序中。

我find的代码:

private void FloodFill(Bitmap bmp, Point pt, int targetColor, int replacementColor) { Queue<Point> q = new LinkedList<Point>(); q.add(pt); while (q.size() > 0) { Point n = q.poll(); if (bmp.getPixel(nx, ny) != targetColor) continue; Point w = n, e = new Point(nx + 1, ny); while ((wx > 0) && (bmp.getPixel(wx, wy) == targetColor)) { bmp.setPixel(wx, wy, replacementColor); if ((wy > 0) && (bmp.getPixel(wx, wy - 1) == targetColor)) q.add(new Point(wx, wy - 1)); if ((wy < bmp.getHeight() - 1) && (bmp.getPixel(wx, wy + 1) == targetColor)) q.add(new Point(wx, wy + 1)); wx--; } while ((ex < bmp.getWidth() - 1) && (bmp.getPixel(ex, ey) == targetColor)) { bmp.setPixel(ex, ey, replacementColor); if ((ey > 0) && (bmp.getPixel(ex, ey - 1) == targetColor)) q.add(new Point(ex, ey - 1)); if ((ey < bmp.getHeight() - 1) && (bmp.getPixel(ex, ey + 1) == targetColor)) q.add(new Point(ex, ey + 1)); e.x++; } } } 

我知道如何在用户的手指触摸事件之后在屏幕上画线,但是我也想知道如何用某种颜色填充给定的图像,例如这个:

一只小狮子!

我看到堆栈溢出的其他问题:

  • 第一个话题
  • 第二个话题
  • 第三个话题

这似乎很容易做,但我不能! 你能告诉我一个小例子吗? 我想知道如何设置canvas,图像的颜色,以及如何做到这一点。

Solutions Collecting From Web of "如何在Android中使用洪水填充algorithm?"

android使用洪水填充algorithm获取内存exception 。 检查链接有一个例子。

你需要x和y触摸的坐标,你可以使用asynctask来填充一个封闭的区域。 使用进度对话框,直到填充填充封闭区域的replace颜色。

注意:在着色较大的情况下,我遇到了问题。 花了很多时间。 我不确定是否使用asynctask是野兽的方式。 我希望有人能够澄清这一点

您可以根据您的需要修改以下内容。

 final Point p1 = new Point(); p1.x=(int) x; //x co-ordinate where the user touches on the screen p1.y=(int) y; //y co-ordinate where the user touches on the screen FloodFill f= new FloodFill(); f.floodFill(bmp,pt,targetColor,replacementColor); 

FloodFillalgorithm来填充封闭区域

  public class FloodFill { public void floodFill(Bitmap image, Point node, int targetColor, int replacementColor) { int width = image.getWidth(); int height = image.getHeight(); int target = targetColor; int replacement = replacementColor; if (target != replacement) { Queue<Point> queue = new LinkedList<Point>(); do { int x = node.x; int y = node.y; while (x > 0 && image.getPixel(x - 1, y) == target) { x--; } boolean spanUp = false; boolean spanDown = false; while (x < width && image.getPixel(x, y) == target) { image.setPixel(x, y, replacement); if (!spanUp && y > 0 && image.getPixel(x, y - 1) == target) { queue.add(new Point(x, y - 1)); spanUp = true; } else if (spanUp && y > 0 && image.getPixel(x, y - 1) != target) { spanUp = false; } if (!spanDown && y < height - 1 && image.getPixel(x, y + 1) == target) { queue.add(new Point(x, y + 1)); spanDown = true; } else if (spanDown && y < height - 1 && image.getPixel(x, y + 1) != target) { spanDown = false; } x++; } } while ((node = queue.poll()) != null); } } } 

在这里输入图像说明

编辑:

在这里输入图像说明

编辑8-7-2014:

用上面的洪水填充algorithm填充一个小的封闭区域可以很好地工作。 但是,对于大面积的algorithm工作缓慢,消耗大量的内存。 最近我遇到了一个使用QueueLinear Flood Fill的post,它的速度更快。

资源 :

http://www.codeproject.com/Articles/16405/Queue-Linear-Flood-Fill-A-Fast-Flood-Fill-Algorith

代码:

 public class QueueLinearFloodFiller { protected Bitmap image = null; protected int[] tolerance = new int[] { 0, 0, 0 }; protected int width = 0; protected int height = 0; protected int[] pixels = null; protected int fillColor = 0; protected int[] startColor = new int[] { 0, 0, 0 }; protected boolean[] pixelsChecked; protected Queue<FloodFillRange> ranges; // Construct using an image and a copy will be made to fill into, // Construct with BufferedImage and flood fill will write directly to // provided BufferedImage public QueueLinearFloodFiller(Bitmap img) { copyImage(img); } public QueueLinearFloodFiller(Bitmap img, int targetColor, int newColor) { useImage(img); setFillColor(newColor); setTargetColor(targetColor); } public void setTargetColor(int targetColor) { startColor[0] = Color.red(targetColor); startColor[1] = Color.green(targetColor); startColor[2] = Color.blue(targetColor); } public int getFillColor() { return fillColor; } public void setFillColor(int value) { fillColor = value; } public int[] getTolerance() { return tolerance; } public void setTolerance(int[] value) { tolerance = value; } public void setTolerance(int value) { tolerance = new int[] { value, value, value }; } public Bitmap getImage() { return image; } public void copyImage(Bitmap img) { // Copy data from provided Image to a BufferedImage to write flood fill // to, use getImage to retrieve // cache data in member variables to decrease overhead of property calls width = img.getWidth(); height = img.getHeight(); image = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); Canvas canvas = new Canvas(image); canvas.drawBitmap(img, 0, 0, null); pixels = new int[width * height]; image.getPixels(pixels, 0, width, 1, 1, width - 1, height - 1); } public void useImage(Bitmap img) { // Use a pre-existing provided BufferedImage and write directly to it // cache data in member variables to decrease overhead of property calls width = img.getWidth(); height = img.getHeight(); image = img; pixels = new int[width * height]; image.getPixels(pixels, 0, width, 1, 1, width - 1, height - 1); } protected void prepare() { // Called before starting flood-fill pixelsChecked = new boolean[pixels.length]; ranges = new LinkedList<FloodFillRange>(); } // Fills the specified point on the bitmap with the currently selected fill // color. // int x, int y: The starting coords for the fill public void floodFill(int x, int y) { // Setup prepare(); if (startColor[0] == 0) { // ***Get starting color. int startPixel = pixels[(width * y) + x]; startColor[0] = (startPixel >> 16) & 0xff; startColor[1] = (startPixel >> 8) & 0xff; startColor[2] = startPixel & 0xff; } // ***Do first call to floodfill. LinearFill(x, y); // ***Call floodfill routine while floodfill ranges still exist on the // queue FloodFillRange range; while (ranges.size() > 0) { // **Get Next Range Off the Queue range = ranges.remove(); // **Check Above and Below Each Pixel in the Floodfill Range int downPxIdx = (width * (range.Y + 1)) + range.startX; int upPxIdx = (width * (range.Y - 1)) + range.startX; int upY = range.Y - 1;// so we can pass the y coord by ref int downY = range.Y + 1; for (int i = range.startX; i <= range.endX; i++) { // *Start Fill 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 && (!pixelsChecked[upPxIdx]) && CheckPixel(upPxIdx)) LinearFill(i, upY); // *Start Fill Downwards // if we're not below the bottom of the bitmap and the pixel // below this one is within the color tolerance if (range.Y < (height - 1) && (!pixelsChecked[downPxIdx]) && CheckPixel(downPxIdx)) LinearFill(i, downY); downPxIdx++; upPxIdx++; } } image.setPixels(pixels, 0, width, 1, 1, width - 1, height - 1); } // 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. // int x, int y: The starting coords protected void LinearFill(int x, int y) { // ***Find Left Edge of Color Area int lFillLoc = x; // the location to check/fill on the left int pxIdx = (width * y) + x; while (true) { // **fill with the color pixels[pxIdx] = fillColor; // **indicate that this pixel has already been checked and filled pixelsChecked[pxIdx] = true; // **de-increment lFillLoc--; // de-increment counter pxIdx--; // de-increment pixel index // **exit loop if we're at edge of bitmap or color area if (lFillLoc < 0 || (pixelsChecked[pxIdx]) || !CheckPixel(pxIdx)) { break; } } lFillLoc++; // ***Find Right Edge of Color Area int rFillLoc = x; // the location to check/fill on the left pxIdx = (width * y) + x; while (true) { // **fill with the color pixels[pxIdx] = fillColor; // **indicate that this pixel has already been checked and filled pixelsChecked[pxIdx] = true; // **increment rFillLoc++; // increment counter pxIdx++; // increment pixel index // **exit loop if we're at edge of bitmap or color area if (rFillLoc >= width || pixelsChecked[pxIdx] || !CheckPixel(pxIdx)) { break; } } rFillLoc--; // add range to queue FloodFillRange r = new FloodFillRange(lFillLoc, rFillLoc, y); ranges.offer(r); } // Sees if a pixel is within the color tolerance range. protected boolean CheckPixel(int px) { int red = (pixels[px] >>> 16) & 0xff; int green = (pixels[px] >>> 8) & 0xff; int blue = pixels[px] & 0xff; return (red >= (startColor[0] - tolerance[0]) && red <= (startColor[0] + tolerance[0]) && green >= (startColor[1] - tolerance[1]) && green <= (startColor[1] + tolerance[1]) && blue >= (startColor[2] - tolerance[2]) && blue <= (startColor[2] + tolerance[2])); } // Represents a linear range to be filled and branched from. protected class FloodFillRange { public int startX; public int endX; public int Y; public FloodFillRange(int startX, int endX, int y) { this.startX = startX; this.endX = endX; this.Y = y; } } } 

感谢stackoverflow的用户,我已经得到正确的解决scheme!

我想知道如何使用洪水填充algorithm,并将其集成到一个简单的Android项目中,这就是我所做的:

Java代码:

 import java.util.LinkedList; import java.util.Queue; import android.app.Activity; import android.app.ProgressDialog; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.BitmapFactory.Options; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Point; import android.os.AsyncTask; import android.os.Bundle; import android.view.MotionEvent; import android.view.View; import android.widget.Button; import android.widget.ImageView; import android.widget.RelativeLayout; public class Main extends Activity { private RelativeLayout dashBoard; private MyView myView; public ImageView image; Button b_red, b_blue, b_green, b_orange, b_clear; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); myView = new MyView(this); setContentView(R.layout.activity_main); findViewById(R.id.dashBoard); b_red = (Button) findViewById(R.id.b_red); b_blue = (Button) findViewById(R.id.b_blue); b_green = (Button) findViewById(R.id.b_green); b_orange = (Button) findViewById(R.id.b_orange); b_red.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { myView.changePaintColor(0xFFFF0000); } }); b_blue.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { myView.changePaintColor(0xFF0000FF); } }); b_green.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { myView.changePaintColor(0xFF00FF00); } }); b_orange.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { myView.changePaintColor(0xFFFF9900); } }); dashBoard = (RelativeLayout) findViewById(R.id.dashBoard); dashBoard.addView(myView); } public class MyView extends View { private Paint paint; private Path path; public Bitmap mBitmap; public ProgressDialog pd; final Point p1 = new Point(); public Canvas canvas; //Bitmap mutableBitmap ; public MyView(Context context) { super(context); this.paint = new Paint(); this.paint.setAntiAlias(true); pd = new ProgressDialog(context); this.paint.setStyle(Paint.Style.STROKE); paint.setStrokeJoin(Paint.Join.ROUND); paint.setStrokeWidth(5f); mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.forme).copy(Bitmap.Config.ARGB_8888, true); this.path = new Path(); } @Override protected void onDraw(Canvas canvas) { this.canvas = canvas; this.paint.setColor(Color.RED); canvas.drawBitmap(mBitmap, 0, 0, paint); } @Override public boolean onTouchEvent(MotionEvent event) { float x = event.getX(); float y = event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: p1.x = (int) x; p1.y = (int) y; final int sourceColor = mBitmap.getPixel((int) x, (int) y); final int targetColor = paint.getColor(); new TheTask(mBitmap, p1, sourceColor, targetColor).execute(); invalidate(); } return true; } public void clear() { path.reset(); invalidate(); } public int getCurrentPaintColor() { return paint.getColor(); } public void changePaintColor(int color){ this.paint.setColor(color); } class TheTask extends AsyncTask<Void, Integer, Void> { Bitmap bmp; Point pt; int replacementColor, targetColor; public TheTask(Bitmap bm, Point p, int sc, int tc) { this.bmp = bm; this.pt = p; this.replacementColor = tc; this.targetColor = sc; pd.setMessage("Filling...."); pd.show(); } @Override protected void onPreExecute() { pd.show(); } @Override protected void onProgressUpdate(Integer... values) { } @Override protected Void doInBackground(Void... params) { FloodFill f = new FloodFill(); f.floodFill(bmp, pt, targetColor, replacementColor); return null; } @Override protected void onPostExecute(Void result) { pd.dismiss(); invalidate(); } } } // flood fill public class FloodFill { public void floodFill(Bitmap image, Point node, int targetColor, int replacementColor) { int width = image.getWidth(); int height = image.getHeight(); int target = targetColor; int replacement = replacementColor; if (target != replacement) { Queue<Point> queue = new LinkedList<Point>(); do { int x = node.x; int y = node.y; while (x > 0 && image.getPixel(x - 1, y) == target) { x--; } boolean spanUp = false; boolean spanDown = false; while (x < width && image.getPixel(x, y) == target) { image.setPixel(x, y, replacement); if (!spanUp && y > 0 && image.getPixel(x, y - 1) == target) { queue.add(new Point(x, y - 1)); spanUp = true; } else if (spanUp && y > 0 && image.getPixel(x, y - 1) != target) { spanUp = false; } if (!spanDown && y < height - 1 && image.getPixel(x, y + 1) == target) { queue.add(new Point(x, y + 1)); spanDown = true; } else if (spanDown && y < (height - 1) && image.getPixel(x, y + 1) != target) { spanDown = false; } x++; } } while ((node = queue.poll()) != null); } } } } 

这是XML代码:

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/drawingLayout" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".Main" > <RelativeLayout android:id="@+id/dashBoard" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_above="@+id/b_red" android:layout_alignParentLeft="true" android:layout_alignParentRight="true" android:layout_alignParentTop="true" android:layout_marginBottom="10dp" > </RelativeLayout> <Button android:id="@+id/b_red" android:layout_width="65dp" android:layout_height="40dp" android:layout_alignParentBottom="true" android:layout_alignParentLeft="true" android:background="#FF0000" /> <Button android:id="@+id/b_green" android:layout_width="65dp" android:layout_height="40dp" android:layout_alignParentBottom="true" android:layout_toRightOf="@+id/b_red" android:background="#00FF00" /> <Button android:id="@+id/b_blue" android:layout_width="65dp" android:layout_height="40dp" android:layout_alignParentBottom="true" android:layout_toRightOf="@+id/b_green" android:background="#0000FF" /> <Button android:id="@+id/b_orange" android:layout_width="65dp" android:layout_height="40dp" android:layout_alignParentBottom="true" android:layout_toRightOf="@+id/b_blue" android:background="#FF9900" /> <Button android:id="@+id/button5" android:layout_width="60dp" android:layout_height="40dp" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:text="Clear" /> </RelativeLayout> 

我希望它对你有帮助!

祝你今天愉快!!!