Android 颜色更平滑

Android color smoother

很难解释我的问题,所以我会用几张图片来展示它。我做了这个位图:

我想在里面画一圈光,使用这个代码:

 Rect bounds1 = new Rect(x, y, x+bit.getWidth(), y+bit.getHeight());
        for (int i = bounds1.left; i < bounds1.right; i++) {
            for (int j = bounds1.top; j < bounds1.bottom; j++) {
                int result=(int) Math.sqrt((x2-i)*(x2-i)+(y2-j)*(y2-j));
                if(result<=120){
                      int pixel = (bit.getPixel(i-x,j-y));

                      int a,r, g, b;

                      int different=result/10;
                      //change the value of each component
                      a=Color.alpha(pixel);

                      r = Color.red(pixel) + rn;
                      g = Color.green(pixel) + gn;
                      b = Color.blue(pixel) + bn;

                      //r,g,b values which are out of the range 0 to 255 should set to 0 or 255
                      if (r >= 256) {
                          r = 255;
                      } else if (r < 0) {
                          r = 0;
                      }

                      if (g >= 256) {
                          g = 255;
                      } else if (g < 0) {
                          g = 0;
                      }

                      if (b >= 256) {
                          b = 255;
                      } else if (b < 0) {
                          b = 0;
                      }

                    bit.setPixel(i-x, j-y, Color.argb(a,r, g, b));
                }
            }

我成功创建了这个:

现在,我正在尝试让光球变得更光滑而不是太粗糙,这样它看起来就不会像一个圆圈,有人知道我该怎么做吗?我尝试了很多事情,但没有成功。

整个绘图代码:

public class CreatorView extends View

{

Context c;


boolean quickfix=false;
boolean bin_isEmpty=true;
boolean iftrue=false;
boolean stopnow=false;
boolean buttonpressed=false;
String arrowcheck="";
int screenw;
int screenh;
int pixelx;
int pixely;
int smallpixelx;
int smallpixely;
Point arrowsp;
Paint paintblock;
int currentColor=0;
int x2=120;
int y2=120;

int rn=60;
int gn=45;
int bn=30;

 Bitmap tileBitmap;
 Bitmap lightBitmap;
 RectF lightRect;
 Paint paint0 = new Paint(Paint.ANTI_ALIAS_FLAG);
 Paint paint1 = new Paint(Paint.ANTI_ALIAS_FLAG);   

Bitmap mountaintop;
Bitmap mountainbottom;


Bitmap grass;
Bitmap dirt;
Bitmap dirt2;
Bitmap dirt3;
Bitmap stonegrass;
Bitmap stone;
Bitmap stone2;
Bitmap stone3;
Bitmap stone4;
Bitmap cloud;
Bitmap bin_Empty;
Bitmap bin_Full;
Bitmap arrowno;
Bitmap arrown;
Bitmap arrowl;
Bitmap arrowr;
Bitmap arrowu;
Bitmap arrowd;
Bitmap arrowul;
Bitmap arrowur;
Bitmap save;
Bitmap fliph;
Bitmap flipv;
Bitmap Rotatef;
Bitmap Rotateb;
Bitmap arrowdl;
Bitmap arrowdr;
Bitmap grassSide;
Bitmap grassTop;
Bitmap orange;
Bitmap blue;
Bitmap smallpixel;
Bitmap PixelDetect;
Bitmap colorpick;
Bitmap brush;
Bitmap brushcolor;
Bitmap torch;

Map.Entry<CreatorBlock,Point>currentBlock;
Map.Entry<CreatorBlock,Point>lastBlock;
Map<CreatorBlock, Point> grassblock = new HashMap<CreatorBlock, Point>();
Map<CreatorBlock, Point> stonegrassblock = new HashMap<CreatorBlock, Point>();
Map<CreatorBlock, Point> dirtblock = new HashMap<CreatorBlock, Point>();
Map<CreatorBlock, Point> stoneblock = new HashMap<CreatorBlock, Point>();
Map<CreatorBlock, Point> grassSideBlock = new HashMap<CreatorBlock, Point>();
Map<CreatorBlock, Point> grassTopBlock = new HashMap<CreatorBlock, Point>();
Map<CreatorBlock, Point> orangeBlock = new HashMap<CreatorBlock, Point>();
Map<CreatorBlock, Point> blueBlock = new HashMap<CreatorBlock, Point>();

Map<BackBlock, Point> mountaintopBack = new HashMap<BackBlock, Point>();
Map<BackBlock, Point> mountainbottomBack = new HashMap<BackBlock, Point>();

Map<BackBlock, Point> torchBack = new HashMap<BackBlock, Point>();

Map<CreatorBlock, Point> levelMap = new HashMap<CreatorBlock, Point>();
List<MenuButton>menuButtonList=new ArrayList<MenuButton>();


public CreatorView(Context c) {
    super(c);
    this.c=c;


    WindowManager wm = (WindowManager) c.getSystemService(Context.WINDOW_SERVICE);
    Display display = wm.getDefaultDisplay();
    this.screenw= display.getWidth();
    this.screenh=display.getHeight();

    this.PixelDetect   = BitmapFactory.decodeResource(   getResources(),   R.drawable.custom_pixel);
    this.smallpixel   = Bitmap.createScaledBitmap(PixelDetect, 3, 3, false);


    this.grass=BitmapFactory.decodeResource(getResources(), R.drawable.block_grass);
    this.grassSide=BitmapFactory.decodeResource(getResources(), R.drawable.block_grassside);
    this.grassTop=BitmapFactory.decodeResource(getResources(), R.drawable.block_grasstop);
    this.orange=BitmapFactory.decodeResource(getResources(), R.drawable.block_cube1);
    this.blue=BitmapFactory.decodeResource(getResources(), R.drawable.block_bluecube);
    this.dirt=BitmapFactory.decodeResource(getResources(), R.drawable.block_dirt);
    this.dirt2=BitmapFactory.decodeResource(getResources(), R.drawable.block_dirt2);
    this.dirt3=BitmapFactory.decodeResource(getResources(), R.drawable.block_dirt3);
    this.stonegrass=BitmapFactory.decodeResource(getResources(), R.drawable.block_stonegrass);
    this.stone=BitmapFactory.decodeResource(getResources(), R.drawable.block_stone);
    this.stone2=BitmapFactory.decodeResource(getResources(), R.drawable.block_stone2);
    this.stone3=BitmapFactory.decodeResource(getResources(), R.drawable.block_stone3);
    this.stone4=BitmapFactory.decodeResource(getResources(), R.drawable.block_stone4);
    this.mountaintop=BitmapFactory.decodeResource(getResources(), R.drawable.back_mountaintop);
    this.mountainbottom=BitmapFactory.decodeResource(getResources(), R.drawable.back_mountainbottom);
    this.arrowno=BitmapFactory.decodeResource(getResources(), R.drawable.arrow_noclick);
    this.arrown=BitmapFactory.decodeResource(getResources(), R.drawable.arrow_normal);
    this.arrowl=BitmapFactory.decodeResource(getResources(), R.drawable.arrow_left);
    this.arrowr=BitmapFactory.decodeResource(getResources(), R.drawable.arrow_right);
    this.arrowu=BitmapFactory.decodeResource(getResources(), R.drawable.arrow_up);
    this.arrowd=BitmapFactory.decodeResource(getResources(), R.drawable.arrow_down);
    this.arrowul=BitmapFactory.decodeResource(getResources(), R.drawable.arrow_upperleft);
    this.arrowur=BitmapFactory.decodeResource(getResources(), R.drawable.arrow_upperright);
    this.arrowdl=BitmapFactory.decodeResource(getResources(), R.drawable.arrow_downleft);
    this.arrowdr=BitmapFactory.decodeResource(getResources(), R.drawable.arrow_downright);
    this.arrowno=Bitmap.createScaledBitmap(arrowno, arrowno.getWidth()*3, arrowno.getHeight()*3, false);
    this.save=BitmapFactory.decodeResource(getResources(), R.drawable.button_save);
    this.brush=BitmapFactory.decodeResource(getResources(), R.drawable.menu_brush);
    this.brushcolor=BitmapFactory.decodeResource(getResources(), R.drawable.menu_brush_color);
    this.colorpick=BitmapFactory.decodeResource(getResources(), R.drawable.menu_colorpicker);
    this.torch=BitmapFactory.decodeResource(getResources(), R.drawable.item_torch);
    this.save=BitmapFactory.decodeResource(getResources(), R.drawable.button_save);
    this.fliph=BitmapFactory.decodeResource(getResources(), R.drawable.menu_fliph);
    this.flipv=BitmapFactory.decodeResource(getResources(), R.drawable.menu_flipv);
    this.Rotatef=BitmapFactory.decodeResource(getResources(), R.drawable.menu_rotatef);
    this.Rotateb=BitmapFactory.decodeResource(getResources(), R.drawable.menu_rotateb);
    this.bin_Empty=BitmapFactory.decodeResource(getResources(), R.drawable.bin_empty);
    this.bin_Full=BitmapFactory.decodeResource(getResources(), R.drawable.bin_full);
    this.bin_Empty=Bitmap.createScaledBitmap(bin_Empty, bin_Empty.getWidth()*3, bin_Empty.getHeight()*3, false);
    this.bin_Full=Bitmap.createScaledBitmap(bin_Full, bin_Full.getWidth()*3, bin_Full.getHeight()*3, false);
    this.brush=Bitmap.createScaledBitmap(brush, brush.getWidth()*3, brush.getHeight()*3, false);
    this.brushcolor=Bitmap.createScaledBitmap(brushcolor, brushcolor.getWidth()*3, brushcolor.getHeight()*3, false);
    this.colorpick=Bitmap.createScaledBitmap(colorpick, colorpick.getWidth()*3, colorpick.getHeight()*3, false);
    this.fliph=Bitmap.createScaledBitmap(fliph, fliph.getWidth()*3, fliph.getHeight()*3, false);
    this.flipv=Bitmap.createScaledBitmap(flipv, flipv.getWidth()*3, flipv.getHeight()*3, false);
    this.Rotateb=Bitmap.createScaledBitmap(Rotateb, Rotateb.getWidth()*3, Rotateb.getHeight()*3, false);
    this.Rotatef=Bitmap.createScaledBitmap(Rotatef, Rotatef.getWidth()*3, Rotatef.getHeight()*3, false);
    this.arrown=Bitmap.createScaledBitmap(arrown, arrown.getWidth()*3, arrown.getHeight()*3, false);
    this.arrowl=Bitmap.createScaledBitmap(arrowl, arrowl.getWidth()*3, arrowl.getHeight()*3, false);
    this.arrowr=Bitmap.createScaledBitmap(arrowr, arrowr.getWidth()*3, arrowr.getHeight()*3, false);
    this.arrowu=Bitmap.createScaledBitmap(arrowu, arrowu.getWidth()*3, arrowu.getHeight()*3, false);
    this.arrowd=Bitmap.createScaledBitmap(arrowd, arrowd.getWidth()*3, arrowd.getHeight()*3, false);
    this.arrowul=Bitmap.createScaledBitmap(arrowul, arrowul.getWidth()*3, arrowul.getHeight()*3, false);
    this.arrowur=Bitmap.createScaledBitmap(arrowur, arrowur.getWidth()*3, arrowur.getHeight()*3, false);
    this.arrowdl=Bitmap.createScaledBitmap(arrowdl, arrowdl.getWidth()*3, arrowdl.getHeight()*3, false);
    this.arrowdr=Bitmap.createScaledBitmap(arrowdr, arrowdr.getWidth()*3, arrowdr.getHeight()*3, false);

    Menu_Add(arrowno,0,true,"arrows");
    Menu_Add(bin_Empty,1,false,"bin");
    Menu_Add(brush,2,false,"brush");
    Menu_Add(brushcolor,2,false,"brushcolor");
    Menu_Add(colorpick,3,false,"colorpick");
    Menu_Add(Rotateb,4,false,"rotateb");
    Menu_Add(Rotatef,5,false,"rotatef");
    Menu_Add(fliph,6,false,"fliph");
    Menu_Add(flipv,7,false,"flipv");
    Menu_Add(grassTop,1,true,"grasstop");
    Menu_Add(grassSide,2,true,"grassside");
    Menu_Add(grass,3,true,"grass");
    Menu_Add(dirt,4,true,"dirt");
    Menu_Add(stonegrass,5,true,"stonegrass");
    Menu_Add(stone,6,true,"stone");
    Menu_Add(orange,7,true,"orange");
    Menu_Add(blue,8,true,"blue");
    Menu_Add(mountaintop,9,true,"mountaintop");
    Menu_Add(mountainbottom,10,true,"mountainbottom");
    Menu_Add(torch,11,true,"torch");
    arrowsp=new Point();
    arrowsp.x=0;
    arrowsp.y=0;
      tileBitmap = grass;

}
private void Menu_Add(Bitmap b,int order,boolean vertical,String name)
{
    Point p=new Point();
    if(order==0){
        p.x=0;
        p.y=0;
        MenuButton m=new MenuButton(order,b , vertical, p,name);
        menuButtonList.add(m);
    }
    else{
        for (MenuButton m : menuButtonList) {
            if((m.isVertical()==vertical||order==1)&&m.getOrder()+1==order ){
                if(vertical){
                    p.x=0;
                    p.y=m.getP().y+m.getBit().getHeight()+2;
                }
                else{
                    p.x=m.getP().x+m.getBit().getWidth()+2;
                    p.y=0;
                }
                MenuButton m2=new MenuButton(order,b , vertical, p,name);
                menuButtonList.add(m2);
                return;
            }
        }
    }
}
public void drawLight(){
    lightBitmap = Bitmap.createBitmap(1500, 15000, Bitmap.Config.ARGB_8888);
    Canvas cc = new Canvas(lightBitmap);
    Paint pp = new Paint(Paint.ANTI_ALIAS_FLAG);
    pp.setMaskFilter(new BlurMaskFilter(20, BlurMaskFilter.Blur.NORMAL));
    for (Map.Entry<BackBlock, Point> entry : torchBack.entrySet()) {
         cc.drawCircle(entry.getValue().x+6, entry.getValue().y+55, 25, pp);
        }
    lightRect = new RectF(-256, -256, lightBitmap.getWidth(), lightBitmap.getHeight());
    lightRect.offset(200, 200);
    float[] array = {
            1.2f, 0, 0, 0, 1,
            0,1.2f, 0, 0, 1,
            0, 0, 1.2f, 0, 1,
            0, 0, 0, 1, 0,
    };
    ColorMatrix ccc=new ColorMatrix(array);
    ccc.setScale(2, 1.5f, 1f, 1f);
    ColorMatrixColorFilter c4=new ColorMatrixColorFilter(ccc);
    paint0.setColorFilter(c4);
    paint1.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));

}
@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    drawLight();
    Paint paintAlpha = new Paint();
    paintAlpha.setAlpha(200);
    canvas.drawARGB(255, 86, 194, 243);
      drawTiles(canvas, paint0);

      drawTiles(canvas, null);

        for(MenuButton m : menuButtonList){
            switch(m.getName()){
            case "bin":
                if(bin_isEmpty){
                    canvas.drawBitmap(bin_Empty, m.getP().x, m.getP().y,paintAlpha);
                    }
                    else{
                        canvas.drawBitmap(bin_Full, m.getP().x, m.getP().y,paintAlpha);
                    }
                break;
            case "brushcolor":
                canvas.drawBitmap(m.getBit(), m.getP().x,m.getP().y, changeBitmapColor(m.getBit(), currentColor));
            break;
            case "arrows":
                canvas.drawBitmap(m.getBit(),m.getP().x,m.getP().y,paintAlpha);
                switch (arrowcheck) {
                case "normal":
                    canvas.drawBitmap(arrown, arrowsp.x, arrowsp.y,paintAlpha);
                    break;
                case "left":
                    canvas.drawBitmap(arrowl, arrowsp.x, arrowsp.y,paintAlpha);
                    break;
                case "right":
                    canvas.drawBitmap(arrowr, arrowsp.x, arrowsp.y,paintAlpha);
                    break;
                case "down":
                    canvas.drawBitmap(arrowd, arrowsp.x, arrowsp.y,paintAlpha);
                    break;
                case "up":
                    canvas.drawBitmap(arrowu, arrowsp.x, arrowsp.y,paintAlpha);
                    break;
                case "upleft":
                    canvas.drawBitmap(arrowul, arrowsp.x, arrowsp.y,paintAlpha);
                    break;
                case "upright":
                    canvas.drawBitmap(arrowur, arrowsp.x, arrowsp.y,paintAlpha);
                    break;
                case "downleft":
                    canvas.drawBitmap(arrowdl, arrowsp.x, arrowsp.y,paintAlpha);
                    break;
                case "downright":
                    canvas.drawBitmap(arrowdr, arrowsp.x, arrowsp.y,paintAlpha);
                    break;
                }
                break;
            default:
                canvas.drawBitmap(m.getBit(),m.getP().x,m.getP().y,paintAlpha);
                break;

            }
            Paint paintRec = new Paint();
            paintRec.setColor(Color.RED);
            paintRec.setStyle(Style.STROKE);
            paintRec.setStrokeWidth(4);
            paintRec.setStrokeCap(Cap.ROUND);
            paintRec.setStrokeJoin(Join.ROUND);
            paintRec.setPathEffect(new DashPathEffect(new float[] {30,40}, 0));
            paintRec.setAlpha(5);
            if(currentBlock!=null)
            canvas.drawRect(currentBlock.getValue().x,currentBlock.getValue().y,currentBlock.getValue().x+currentBlock.getKey().getBit().getWidth(),currentBlock.getValue().y+currentBlock.getKey().getBit().getHeight(),paintRec);


        }
        canvas.saveLayer(lightRect, null, Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
        canvas.clipRect(lightRect);
        drawTiles(canvas, paint0);
        canvas.drawBitmap(lightBitmap, null, lightRect, paint1);
        canvas.restore();

}
  void drawTiles(Canvas canvas, Paint p) {
      for (Map.Entry<BackBlock, Point> entry : mountainbottomBack.entrySet()) {
            //Bitmap lighten=Bitmap.createScaledBitmap(entry.getKey().getBit(), entry.getKey().getBit().getWidth(), entry.getKey().getBit().getHeight(), false);
            canvas.drawBitmap(entry.getKey().getBit(), entry.getValue().x,entry.getValue().y, p);
            }
        for (Map.Entry<BackBlock, Point> entry : mountaintopBack.entrySet()) {
            canvas.drawBitmap(entry.getKey().getBit(), entry.getValue().x,entry.getValue().y, p);
            }
        for (Map.Entry<CreatorBlock, Point> entry : grassblock.entrySet()) {
            canvas.drawBitmap(entry.getKey().getBit(), entry.getValue().x,entry.getValue().y, p);
        }
        for (Map.Entry<CreatorBlock, Point> entry : stonegrassblock.entrySet()) {
            canvas.drawBitmap(entry.getKey().getBit(), entry.getValue().x,entry.getValue().y, p);
            }
        for (Map.Entry<CreatorBlock, Point> entry : dirtblock.entrySet()) {
            canvas.drawBitmap(entry.getKey().getBit(), entry.getValue().x,entry.getValue().y, p);
            }
        for (Map.Entry<CreatorBlock, Point> entry : stoneblock.entrySet()) {
            canvas.drawBitmap(entry.getKey().getBit(), entry.getValue().x,entry.getValue().y, p);
            }
        for (Map.Entry<CreatorBlock, Point> entry : grassSideBlock.entrySet()) {
            canvas.drawBitmap(entry.getKey().getBit(), entry.getValue().x,entry.getValue().y, p);
            }
        for (Map.Entry<CreatorBlock, Point> entry : grassTopBlock.entrySet()) {
            canvas.drawBitmap(entry.getKey().getBit(), entry.getValue().x,entry.getValue().y, p);
            }
        for (Map.Entry<CreatorBlock, Point> entry : orangeBlock.entrySet()) {
            canvas.drawBitmap(entry.getKey().getBit(), entry.getValue().x,entry.getValue().y, p);
            }
        for (Map.Entry<CreatorBlock, Point> entry : blueBlock.entrySet()) {
            canvas.drawBitmap(entry.getKey().getBit(), entry.getValue().x,entry.getValue().y, p);
            }
        for (Map.Entry<BackBlock, Point> entry : torchBack.entrySet()) {
            canvas.drawBitmap(entry.getKey().getBit(), entry.getValue().x,entry.getValue().y, p);
            }
    }

我同意其他发帖者的说法,他们说使用内置的 Android 图形函数是解决这个问题的更好、更有效的方法,但如果这只是为了学习目的,我不会查看直接操作原始像素值的危害。我只是给你一些想法,而不是提供一个完整的解决方案。

目前,您正在有效地对纹理和恒定的浅色 (rn, gn, bn) 进行叠加混合。为了更顺利地做到这一点,引入一个 alpha 值,这样你就可以将 (rn * a, gn * a, bn * a) 添加到你的纹理中,并根据径向距离改变 alpha。

请注意,这不是您从具有 Color.alpha(像素)的纹理中获得的 alpha 值,而是另一个代表光线强度的值。

隐含地,如果 result(您计算出的与光中心的距离)<= 120,则您当前的 alpha 值定义为 1,否则为零:它是全或-没什么。

您可以根据 result 定义一个函数,以给出介于这些极端值之间的 alpha 值。例如,如果半径小于 90,您可以将 alpha 值设置为 1,然后将其从 1 逐渐降低到 0,从 radius = 90 到 radius = 150,然后将其设置为零。

为了编码方便,您还可以将 alpha 值缩放为从 0(无光)到 256(全光强度)而不是 0 到 1。

根据以上规则计算alpha值函数如下:

int result=(int) Math.sqrt((x2-i)*(x2-i)+(y2-j)*(y2-j));
int alpha = 0;
if(result <= 90)
    alpha = 256;
else if((result > 90) && (result < 150))
    alpha = ((60 - (result - 90)) * 256) / 60;

显然,您可以随意调整这些值以获得您喜欢的效果。

然后,而不是针对

进行测试
if(result<=120)

像这样针对 alpha 值进行测试

if(alpha > 0)

最后,在你的循环中,将 rn、gn 和 bn 添加到像素颜色值,这样当 alpha = 0 时你不添加任何东西,当 alpha = 256 时你添加全部量,并且你为每个添加部分量之间的值。我会让你自己弄清楚如何做到这一点。

使用这个自定义View,让它更黄见float[] array initialization中的注释:

class V extends View {
    Paint paint0;
    Paint paint1;

    public V(Context context) {
        super(context);
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        paint0 = new Paint();
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.fykgf);
        Shader shader = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
        paint0.setShader(shader);

        paint1 = new Paint();
        paint1.setMaskFilter(new BlurMaskFilter(50, BlurMaskFilter.Blur.NORMAL));
        float[] array = {
                2, 0, 0, 0, 0,
                0, 2, 0, 0, 0,
                0, 0, 2, 0, 0, // change 2 to: something between [1..2] to get it more yellow
                0, 0, 0, 1, 0,
        };
        paint1.setColorFilter(new ColorMatrixColorFilter(array));
        paint1.setShader(shader);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawRect(0, 0, getWidth(), getHeight(), paint0);
        canvas.drawCircle(300, 300, 150, paint1);
    }
}

编辑

如果你想绘制多个 Bitmapts 使用这个:

class V extends View {
    Bitmap tileBitmap;
    Bitmap lightBitmap;
    RectF lightRect;
    Paint paint0 = new Paint(Paint.ANTI_ALIAS_FLAG);
    Paint paint1 = new Paint(Paint.ANTI_ALIAS_FLAG);

    public V(Context context) {
        super(context);
//        setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        tileBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.fykgf);
        lightBitmap = Bitmap.createBitmap(256, 256, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(lightBitmap);
        Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
        p.setMaskFilter(new BlurMaskFilter(20, BlurMaskFilter.Blur.NORMAL));
        c.drawCircle(128, 128, 100, p);
        lightRect = new RectF(0, 0, lightBitmap.getWidth(), lightBitmap.getHeight());
        lightRect.offset(200, 200);

        float[] array = {
                4, 0, 0, 0, 0,
                0, 4, 0, 0, 0,
                0, 0, 0, 0, 0,
                0, 0, 0, 1, 0,
        };
        paint0.setColorFilter(new ColorMatrixColorFilter(array));
        paint1.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        drawTiles(canvas, null);

        canvas.saveLayer(lightRect, null, Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
        canvas.clipRect(lightRect);
        drawTiles(canvas, paint0);
        canvas.drawBitmap(lightBitmap, null, lightRect, paint1);
        canvas.restore();
    }

    void drawTiles(Canvas canvas, Paint p) {
        // draw your Bitmaps here...
    }
}