Android: 动态创建遮罩

Android: dynamically create a mask

在我的 android 应用程序中,我有一辆用户可以点击的汽车和 select 不同的面板。该图像相对复杂(与此处粘贴的图像相反),因此很难将按钮覆盖在正确的位置。此外还有很多不同的图像。 我想尝试的解决方案:

(第一个图像代表用于确定哪个面板被点击的颜色,第二个图像代表生成的蒙版,最后一个图像 'result')。

我遇到的唯一问题是:如何动态创建遮罩?我想到了使用 floodfill 类型的方法来创建一个新的 canvas 和 selected 面板的 'mask'。但是,我担心它可能计算量太大。有没有更简单的建议? [

更新:好的,我已经走了很远。正如预期的那样,创建蒙版的时间太长了(小图像需要 2-4 秒)。但是,后来我发现了 RenderScripts!!我想我仍然可以让它工作。我现在遇到的唯一小问题是:如何传递已按下的颜色?

我当前的代码如下所示:

// create a bitmap for the mask.
ImageView img = (ImageView) findViewById (mask);
img.setDrawingCacheEnabled(true);
Bitmap bitmap = Bitmap.createBitmap(img.getDrawingCache());

// Create a tiny bitmap to store the colours of the panels that are 
//'selected'
Bitmap.Config conf = Bitmap.Config.ARGB_8888; // see other conf types
Bitmap myBitmap = Bitmap.createBitmap(pickedPanels.size(), 1, conf);
int [] myInts = new int[pickedPanels.size()];
for (int i = 0; i<pickedPanels.size(); i++){
    myInts[i] = pickedPanels.get(i).intValue();
}
myBitmap.setPixels(myInts, 0, myBitmap.getWidth(), 0, 0, 
myBitmap.getWidth(),0);

//Run thescript and set the output
final RenderScript rs = RenderScript.create(this);
final Allocation input = Allocation.createFromBitmap(rs, bitmap, 
Allocation.MipmapControl.MIPMAP_NONE,Allocation.USAGE_SCRIPT);
final Allocation output = Allocation.createTyped(rs, input.getType());
        final ScriptC_singlesource script = new 
ScriptC_singlesource(rs);
script.set_image(Allocation.createFromBitmap(rs, myBitmap, 
Allocation.MipmapControl.MIPMAP_NONE,Allocation.USAGE_SCRIPT));
script.set_imgWidth(pickedPanels.size());
script.forEach_root(input, output);
output.copyTo(bitmap);
img.setImageBitmap(bitmap);

ImageView destim = (ImageView) findViewById (dest);
destim.setDrawingCacheEnabled(true);
destim.setImageBitmap(bitmap);

这是脚本:

#pragma version(1)
#pragma rs java_package_name(za.co.overtake)

rs_allocation image;
int imgWidth;

uchar4 RS_KERNEL root(uchar4 in, uint32_t x, uint32_t y) {

for(int col = 0; col < imgWidth; col++){
    const uchar4 colour = *(const uchar4*)rsGetElementAt(image, col,0);
     if (in.r == colour.r && in.g == colour.g && in.b == colour.b){
        in.r = 255;
        in.g = 0;
        in.b = 0;
        break;
    } else {
       in.r = 0;
       in.g = 255;
       in.b = 0;
       rsDebug("HELLLLLP>>", colour);
    }

}
return in;
}

但是,当我尝试从 myBitmap(或脚本中的图像)读取像素值时,RGB 始终为 0。

(抱歉命名错误等。我一直在疯狂地想办法解决这个问题)

好的,终于明白了。 在我的 renderscript 代码中,我有:

#pragma version(1)
#pragma rs java_package_name(za.co.overtake)

int*reds;
int*greens;    
int*blues;
int imgWidth;

uchar4 RS_KERNEL root(uchar4 in, uint32_t x, uint32_t y) {
   bool colourme = false;
   for(int col = 0; col < imgWidth; col++){

        const int red = reds[col];
        const int green = greens[col];
        const int blue = blues[col];

         if (in.r == red && in.g == green && in.b == blue){
            colourme = true;
        } 
    }
     if (colourme) {
        in.r = 255;
        in.g = 0;
        in.b = 0;
        in.a = 50;
     } else {
         in.r = 0;
         in.g = 0;
         in.b = 0;
         in.a = 0;
     }
    return in;
}

然后在Java

public void showDamagedPanels(int dest, int mask) {

    int noOfColours = pickedPanels.size();
    if (noOfColours > 0) {
        ImageView img = (ImageView) findViewById (mask);
        img.setDrawingCacheEnabled(true);
        Bitmap bitmap = Bitmap.createBitmap(img.getDrawingCache());
        img.setDrawingCacheEnabled(false);

        int [] reds = new int[noOfColours];
        int [] greens = new int[noOfColours];
        int [] blues = new int[noOfColours];

        for (int i = 0; i< noOfColours; i++){
            int colour = pickedPanels.get(i);
            reds[i] = (colour >> 16) & 0xFF;
            greens[i] = (colour >> 8) & 0xFF;
            blues[i] = (colour >> 0) & 0xFF;
        }

        final RenderScript rs = RenderScript.create(this);
        final Allocation input = Allocation.createFromBitmap(rs, bitmap, Allocation.MipmapControl.MIPMAP_NONE,
                Allocation.USAGE_SCRIPT);
        final Allocation output = Allocation.createTyped(rs, input.getType());
        final ScriptC_singlesource script = new ScriptC_singlesource(rs);

        Allocation red = Allocation.createSized(rs, Element.I32(rs), reds.length);
        red.copyFrom(reds);
        script.bind_reds(red);

        Allocation green = Allocation.createSized(rs, Element.I32(rs), greens.length);
        green.copyFrom(greens);
        script.bind_greens(green);

        Allocation blue = Allocation.createSized(rs, Element.I32(rs), blues.length);
        blue.copyFrom(blues);
        script.bind_blues(blue);

        script.set_imgWidth(pickedPanels.size());
        script.forEach_root(input, output);
        output.copyTo(bitmap);

        ImageView destim = (ImageView) findViewById (dest);
        destim.setDrawingCacheEnabled(true);
        destim.setImageBitmap(bitmap);
    } else {
        ImageView destim = (ImageView) findViewById (dest);
        destim.setImageBitmap(null);
    }


}

其中 dest 是叠加图像,图像中的遮罩充当遮罩。所以基本上,当一个面板被点击时——将它的颜色放在 pickedPanels 中。然后调用调用脚本的 showPanels 方法。该脚本检查颜色并将生成的图像设置为红色或透明。

更新:对于尝试使用它但遇到一些问题的任何人:可以在没有渲染脚本代码的情况下执行此操作,但它 运行 有点慢 - 尽管在我的情况下没问题对于小图像。

private Bitmap changeColor(Bitmap src, Set<Integer> pickedPanelsList) {
        int fine = getResources().getColor(R.color.colorAccent);

        int width = src.getWidth();
        int height = src.getHeight();
        int[] pixels = new int[width * height];
        // get pixel array from source
        src.getPixels(pixels, 0, width, 0, 0, width, height);

        Bitmap bmOut = Bitmap.createBitmap(width, height, src.getConfig());

        int AGood = 100, RGood = Color.red(fine), GGood = Color.green(fine), BGood = Color.blue(fine);
        int ABad = 100, RBad = Color.red(Color.RED), GBad = Color.green(Color.RED), BBad = Color.blue(Color.RED);
        int pixel;

        // iteration through pixels
        for (int y = 0; y < height; ++y) {
            for (int x = 0; x < width; ++x) {
                // get current index in 2D-matrix
                int index = y * width + x;
                pixel = pixels[index];
                if(pickedPanelsList.contains(pixel)){
                    pixels[index] = Color.argb(ABad, RBad, GBad, BBad);
                } else if (Color.alpha(pixel) > 0){
                    pixels[index] = Color.argb(AGood, RGood, GGood, BGood);
                }
            }
        }
        bmOut.setPixels(pixels, 0, width, 0, 0, width, height);
        return bmOut;
    }

在这里,picked panel set是所有应该被染成红色(或被​​选中)的颜色,位图是mask(如果我没记错的话,我刚才做过)。我还发现对结果进行轻微模糊处理会使图像看起来更好 - 因为它显然会减少锯齿。