Android 安卓:这个框架怎么画?

Android 安卓:这个框架怎么画?,android,android-canvas,draw,flood-fill,Android,Android Canvas,Draw,Flood Fill,我有一些静态图像,如下所示: 现在,我想要的是,当我在脸上或手上触摸时,选择的颜色应该填充在皮肤部分 请参见下面的结果图像: 那么如何得到上述结果呢?? 重做和撤消功能也应该存在 我尝试过泛光填充颜色,但这样做我只能在适当的部分进行着色。因为泛光填充只填充颜色,直到相同的pixwl颜色出现。如果触摸屏像素颜色发生变化,它将不会在其上填充颜色 所以使用泛光填充我得到的结果如下图所示,如果我按下手,那么只有手的部分会填充颜色,而不是我想填充另一只手和另一张脸的颜色。 所以请帮我解决这个问题 已

我有一些静态图像,如下所示:

现在,我想要的是,当我在脸上或手上触摸时,选择的颜色应该填充在皮肤部分

请参见下面的结果图像:

那么如何得到上述结果呢?? 重做和撤消功能也应该存在

我尝试过泛光填充颜色,但这样做我只能在适当的部分进行着色。因为泛光填充只填充颜色,直到相同的pixwl颜色出现。如果触摸屏像素颜色发生变化,它将不会在其上填充颜色

所以使用泛光填充我得到的结果如下图所示,如果我按下手,那么只有手的部分会填充颜色,而不是我想填充另一只手和另一张脸的颜色。

所以请帮我解决这个问题

已编辑

经过一番回答,我得到了这样的解决方案


但仍然存在内存问题。绘制颜色会消耗大量内存。那么有谁能帮我吗?

一个基本的方法是类似的。 维基百科的文章很好地描述了算法及其变体


您可以在上面找到一个实现。但是,根据您的具体需要,必须对该图像进行修改。

您可以按实际方式对完整图像进行着色,当您使用颜色填充某个区域时,该区域将替换该颜色指定的所有区域

外行术语:

  • 用户将点击手边的大纲
  • 点击位置将与另一个具有完美颜色编码区域的图像进行检查。让我们称之为这种情况下的面具。所有蒙皮区域将具有相同的颜色。衬衫区域将是另一种颜色
  • 无论用户单击何处,用户选择的颜色都将应用于遮罩中具有类似颜色的每个像素,但不是直接在遮罩上绘制,而是在轮廓的像素上绘制 我希望这有帮助

    如果你想要一个例子,请随意评论,然后我可以用它更新答案,但我想你可以从这里得到它

    编辑:

    基本上从这样一个简单的图像开始。我们可以称之为大纲

    然后作为开发人员,您必须做一些工作。在这里,您可以对轮廓进行颜色编码。我们称之为掩码的结果。为了实现这一点,我们使用您想要的相同颜色对区域进行颜色编码。这可以在油漆上或其他任何地方完成。我用Photoshop做的很酷,哈哈

    然后是算法让它在手机上工作。在阅读代码之前,请查看此变量

    int ANTILAISING_TOLERANCE = 70; //Larger better coloring, reduced sensing
    
    如果你放大图像,特别注意到边框的黑色区域,你实际上可以看到,有时候,电脑会稍微混合一些颜色。为了解释这种变化,我们使用这个公差值

    COLORINGANDROIDACTIVITY.JAVA

    package mk.coloring;
    
    import android.app.Activity;
    import android.graphics.Bitmap;
    import android.graphics.Bitmap.Config;
    import android.graphics.BitmapFactory;
    import android.graphics.Canvas;
    import android.os.Bundle;
    import android.view.MotionEvent;
    import android.view.View;
    import android.widget.ImageView;
    import android.view.View.OnTouchListener;
    
    public class ColoringAndroidActivity extends Activity implements OnTouchListener{
        /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        findViewById(R.id.imageView1).setOnTouchListener(this);
    }
    
    int ANTILAISING_TOLERANCE = 70;
    public boolean onTouch(View arg0, MotionEvent arg1) {
        Bitmap mask = BitmapFactory.decodeResource(getResources(), R.drawable.mask);
        int selectedColor = mask.getPixel((int)arg1.getX(),(int)arg1.getY());           
        int sG = (selectedColor & 0x0000FF00) >> 8;
        int sR = (selectedColor & 0x00FF0000) >> 16;
        int sB = (selectedColor & 0x000000FF);
    
        Bitmap original = BitmapFactory.decodeResource(getResources(), R.drawable.empty);       
        Bitmap colored = Bitmap.createBitmap(mask.getWidth(), mask.getHeight(), Config.ARGB_8888);
        Canvas cv = new Canvas(colored);
        cv.drawBitmap(original, 0,0, null);
    
        for(int x = 0; x<mask.getWidth();x++){
            for(int y = 0; y<mask.getHeight();y++){
                int g = (mask.getPixel(x,y) & 0x0000FF00) >> 8;
                int r = (mask.getPixel(x,y) & 0x00FF0000) >> 16;
                int b = (mask.getPixel(x,y) & 0x000000FF);
                if(Math.abs(sR - r) < ANTILAISING_TOLERANCE && Math.abs(sG - g) < ANTILAISING_TOLERANCE && Math.abs(sB - b) < ANTILAISING_TOLERANCE)
                    colored.setPixel(x, y, (colored.getPixel(x, y) & 0xFF000000) | 0x00458414);
            }
        }
        ((ImageView)findViewById(R.id.imageView1)).setImageBitmap(colored);
    
        return true;
    }
    
    包装mk.coloring;
    导入android.app.Activity;
    导入android.graphics.Bitmap;
    导入android.graphics.Bitmap.Config;
    导入android.graphics.BitmapFactory;
    导入android.graphics.Canvas;
    导入android.os.Bundle;
    导入android.view.MotionEvent;
    导入android.view.view;
    导入android.widget.ImageView;
    导入android.view.view.OnTouchListener;
    公共类ColoringAndroidActivity将活动实现扩展到TouchListener{
    /**在首次创建活动时调用*/
    @凌驾
    创建时的公共void(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    findviewbyd(R.id.imageView1).setOnTouchListener(this);
    }
    int抗滑移公差=70;
    公共布尔onTouch(视图arg0、运动事件arg1){
    位图掩码=BitmapFactory.decodeResource(getResources(),R.drawable.mask);
    int-selectedColor=mask.getPixel((int)arg1.getX(),(int)arg1.getY());
    int sG=(selectedColor&0x0000FF00)>>8;
    int sR=(selectedColor&0x00FF0000)>>16;
    int sB=(selectedColor&0x000000FF);
    位图original=BitmapFactory.decodeResource(getResources(),R.drawable.empty);
    Bitmap colored=Bitmap.createBitmap(mask.getWidth()、mask.getHeight()、Config.ARGB_8888);
    画布cv=新画布(彩色);
    cv.drawBitmap(原始,0,0,空);
    对于(int x=0;x 8;
    intr=(mask.getPixel(x,y)&0x00FF0000)>>16;
    intb=(mask.getPixel(x,y)&0x000000FF);
    if(数学abs(sR-r)<抗滑移公差和数学abs(sG-g)<抗滑移公差和数学abs(sB-b)<抗滑移公差)
    有色.setPixel(x,y,(有色.getPixel(x,y)&0xFF000000)| 0x00458414);
    }
    }
    ((ImageView)findViewById(R.id.imageView1)).setImageBitmap(彩色);
    返回true;
    }
    
    }

    此代码不会为用户提供很多颜色选择。相反,如果用户触摸某个区域,它将查看遮罩,并相应地绘制轮廓。但是,您可以制作出真正有趣的交互式图像

    结果

    当我触摸那个男人的头发时,它不仅把头发染成了颜色,而且把他的衬衫和手染成了同样的颜色。将它与面具进行比较,以了解发生了什么

    这只是一个基本的想法。我创建了多个位图,但实际上并不需要。我使用它进行测试,占用了不必要的内存。而且你不需要在每次单击时重新创建掩码,等等

    我希望这对你有帮助:D


    祝你好运。

    使用泛洪填充算法。你也可以检查此链接。.总体思路是单击鼠标即可获得x和y坐标

     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().
    
    请查看上面的链接,了解android中的泛洪填充算法。这将帮助您实现所需。这将帮助您根据需要修改有关撤消和重做的内容

    编辑:

    邮递员
    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;
            }
        }
    }