Android: How to do this framing paint?

前端 未结 3 1408
广开言路
广开言路 2020-12-03 00:37

I Have Some static images like below:

\"enter

Now, I want is, when i touch on

3条回答
  •  萌比男神i
    2020-12-03 00:57

    Use a FloodFill Algorithm. Fill the complete canvas but keep the bound fill area as it is like circle, rectangle. You can also check this link. Android: How to fill color to the specific part of the Image only?. The general idea get the x and y co-ordinates on click.

     final Point p1 = new Point(); 
     p1.x=(int) x; p1.y=(int) y; X and y are co-ordinates when user clicks on the screen
     final int sourceColor= mBitmap.getPixel((int)x,(int) y);
     final int targetColor =mPaint.getColor();
     new TheTask(mDrawingManager.mDrawingUtilities.mBitmap, p1, sourceColor, targetColor).execute(); //Use AsyncTask and do floodfillin the doinBackground().
    

    Check the above links for floodfill algorithmin android. This should help you achieve what you want. Android FingerPaint Undo/Redo implementation. This should help you modify according to your needs regarding undo and redo.

    Edit:

    A post on stackoverflow led me to a efficient way of using flood fill algorithm without delay and OOM.

    Picking from the SO Post

    Filling a small closed area works fine with the above flood fill algorithm. However for large area the algorithm works slow and consumes lot of memory. Recently i came across a post which uses QueueLinear Flood Fill which is way faster that the above.

    Source :

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

    Code :

    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 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();
        }
    
        // 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;
            }
        }
    }
    

提交回复
热议问题