27

I am creating different custom views with two different colors. According to my app features user will drag those objects on the screen, when dragging objects will overlap each other. I want to differentiated the overlapped area, how to set the combined color for overlapped Area. Look at the below image. Here I am using canvas for creating those custom views two circles are two different views.

enter image description here

EDIT: If I use opacity 128 I am able to see the background color, but I want the combination color of overlapped objects colors.

Graham Borland
  • 60,055
  • 21
  • 138
  • 179
RajaReddy PolamReddy
  • 22,428
  • 19
  • 115
  • 166
  • I would say thats an alpha blending answer. The problem is that you want only the OVERLAP to be alpha blended. Interesting question!!! – trumpetlicks Jul 03 '12 at 14:12
  • I think he will have to do the calculations himself. Check which pixels overlap ant then update output according to that. – Audrius Jul 05 '12 at 14:26
  • Are you able to draw & colour arbitrary shapes? Try computing the size/shape of the overlapping area, mix the colours manually and draw that new shape on top of the two previous ones. As long as it's in the right place it should look correct. – Alex Jul 05 '12 at 14:29
  • @Audrius i can calculate the overlapped area if when i use only two circles, but if they use more circles (or polygons) its difficult to calculate overlapped area... – RajaReddy PolamReddy Jul 06 '12 at 11:00

6 Answers6

20

The Color mixing you are looking for is sometimes called Intuitive Color Mixing, or the RYB Color System:

RYB:

enter image description here CC license

A quote from this paper by Nathan Gossett and Baoquan Chen on algorithms for intuitive color mixing summarizes how the intuitive color system works:

"In this model, Red, Yellow and Blue are used as pure primary colors. Red and Yellow mix to form Orange, Yellow and Blue mix to form Green, and Blue and Red mix to form Purple [...]. These are the colors an untrained viewer would expect to obtain using children's paint [...]. In addition, many people do not think of White as the mixture of all colors, but instead as the absence of color (a blank canvas). A more common assumption would be that mixing many colors together would result in a muddy dark brown color."

RYB is not implemented in Android's blend modes and can't really be simulated by mixing alpha/different blend modes.

Most computer graphics applications make use of the RGB or CMYK color spaces:

CMYK:

enter image description hereCC license

CMY is based on subtractive color. Subtractive color mixing means that, starting with white, as we add color, the result gets darker. CMYK is used for mixing colors in images intended for printing in e.g. Photoshop and Illustrator.

RGB:

enter image description here CC license

RGB is based on Additive Color. The colors on a computer screen are created with light using the additive color method. Additive color mixing begins with black and as more color is added, the result gets lighter and ends in white.

This site discusses CMYK and RGB in more detail.

In neither RGB nor CMYK does mixing blue and yellow produce green, or generally speaking, intuitive color mixes. To implement a RYB color system on Android would be quite involved. The paper by Nathan Gossett and Baoquan Chen quoted above proposes a solution with an algorithm implemented in C at the very end of the paper. This algorithm could be implemented in a custom blend on Android. Drawable.setColorFilter() uses PorterDuffColorfilter which extends ColorFilter. Subclassing ColorFilter as discussed in this SO question would have to be done in native code using the NDK.

CMYK Color Mixing Workaround

In case you are interested in using a CMYK color mixing as a workaround, I've included a basic example of how it can be done below. In this example, A cyan-color circle and a yellow-color circle will be blended to produce a green intersection.

Starting with these .png image files created in Adobe Illustrator:

enter image description here

enter image description here

with hex color value 0x00ffff (cyan) and 0xffff00 (yellow).

Add them to your drawable folder with names cyancircle.png and yellowcircle.png.

Configure your main.xml layout as follows:

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" 
    android:background="#ffffff"
    android:padding="30dp">

<ImageView
    android:id="@+id/bluecircle"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/cyancircle">
</ImageView>

<ImageView
    android:id="@+id/yellowcircle"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/yellowcircle" 
    android:layout_marginTop="30dp">
</ImageView>     
</RelativeLayout>

Create your Activity:

import android.app.Activity;
import android.graphics.PorterDuff;
import android.os.Bundle;
import android.widget.ImageView;

public class PorterDuffTestActivity extends Activity {
/** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        ImageView yellowCircle = (ImageView)findViewById(R.id.yellowcircle);

        yellowCircle.getDrawable().setColorFilter(0x88ffff00, PorterDuff.Mode.MULTIPLY);
    }
}

Output:

enter image description here

The limitation to this method is that the top shape's alpha has to be set to 50% (the "88" in "0x88ffff00"). For yellow this works reasonable well but for other colors the alpha effect may not be acceptable (the color may appear to be another color, e.g. red becomes pink with low alpha values on white background). Which blend mode is ultimately acceptable for you depends on the set of colors you are going to use and will take some experimentation. Also note the background color may affect the circles' colors in blend mode. In this example the background is set to white.

Community
  • 1
  • 1
Gunnar Karlsson
  • 28,350
  • 10
  • 68
  • 71
  • Hi, Thanks for answered my Question i am checking this process let you know about the solution. – RajaReddy PolamReddy Jul 06 '12 at 07:46
  • i am not using any drawables here, i am drawing those circles by using canvas on the customview, i have feature to move those objects on the screen at this time it will be overlaps, so i want to change that overlapped color automatically without any calculations when it overlaps. – RajaReddy PolamReddy Jul 06 '12 at 10:40
  • Excellent answer. If I could upvote more than once, I would ;) – Knossos Jun 03 '13 at 08:28
5

I have done another example for 6 objects.

enter image description here

Key Points:

  1. onDraw method will not be override for Object Views, and background also will set to transparence

    setBackgroundColor(Color.TRANSPARENT);

  2. But, the onDraw method will be renamed as onDrawEx will be called from Overlay View.

    public void onDrawEx(Canvas canvas) {

  3. Overlay view will pass a custom canvas to draw into. before pass to object view it will do necessary translate.

        mOverlayView = new View(this){
        @Override
        protected void onDraw(Canvas canvas) {
            Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Config.ARGB_8888);
            Canvas canvasBitmap = new Canvas(bitmap);
            ViewGroup viewGroup = (ViewGroup)getParent();
            for(int i = 0 ; i < viewGroup.getChildCount()-1;i++){
                ObjectView objectView = (ObjectView) viewGroup.getChildAt(i);
                canvasBitmap.save();
                canvasBitmap.translate(objectView.getTranslationX(), objectView.getTranslationY());
                objectView.onDrawEx(canvasBitmap);
                canvasBitmap.restore();
            }
            canvas.drawBitmap(bitmap, 0, 0, new Paint());
        }
    };
    
  4. use the mPaint.setXfermode(new PorterDuffXfermode(Mode.ADD)); to add colors. But all objects should use colors like 0xFF000030,0xFF0000C0,0xFF003000,0xFF00C000,0xFF300000,0xC00000 then only for all posible overlapping we can get different colors. this is depends on you max number of object.

        int k = 0 ;
    for(int i = 0 ; i < 2;i++,k++){
        int color = 0xFF000000|(0x000030<<i*2);
        frameLayout.addView(new ObjectView(this,color,k*50,k*50,k), new FrameLayout.LayoutParams(50, 50));
    }
    for(int i = 0 ; i < 2;i++,k++){
        int color = 0xFF000000|(0x003000<<i*2);
        frameLayout.addView(new ObjectView(this,color,k*50,k*50,k), new FrameLayout.LayoutParams(50, 50));
    }
    for(int i = 0 ; i < 2;i++,k++){
        int color = 0xFF000000|(0x300000<<i*2);
        frameLayout.addView(new ObjectView(this,color,k*50,k*50,k), new FrameLayout.LayoutParams(50, 50));
    }
    

Here I have modified to support version 8

use

mPaint.setXfermode(new PixelXorXfermode(0x00000000));

for

mPaint.setXfermode(new PorterDuffXfermode(Mode.ADD));

I used layout parameter for translation.

Sudar Nimalan
  • 3,962
  • 2
  • 20
  • 28
  • there is no property like this PorterDuffXfermode(Mode.ADD) in paint. i tried like this Inner_Paint.setXfermode(new PorterDuffXfermode(Mode.ADD)); un-success.. – RajaReddy PolamReddy Jul 07 '12 at 10:52
  • your minimum sdk version should be >= 11, if less than that try mPaint.setXfermode(new PorterDuffXfermode(Mode.ADD)); but this is depreciated in latest SDK. – Sudar Nimalan Jul 07 '12 at 13:36
  • sorry try changing mPaint.setXfermode(new PorterDuffXfermode(Mode.ADD));--> mPaint.setXfermode(new PixelXorXfermode(0xFFFFFFFF)); – Sudar Nimalan Jul 07 '12 at 14:54
  • I modified same for version 8 and uploaded http://dl.dropbox.com/u/7717254/MainActivity08.java – Sudar Nimalan Jul 08 '12 at 07:21
  • It's not changing the overlapped to combination color, it forms just a 50% transparent on the another another object. look at the image in my Question how it changes the overlapped color. – RajaReddy PolamReddy Jul 08 '12 at 09:20
  • i want to change the overlapped area to a different combination color. – RajaReddy PolamReddy Jul 08 '12 at 09:32
  • Could you please confirm me you use mPaint.setXfermode(new PixelXorXfermode(0x00000000));, you are getting 0xFF0000F0 for overlapping 0xFF000030 and 0xFF0000C0, 0xFF0030f0 for overlapping 0xFF000030,0xFF0000C0 and 0xFF003000? if this color result is wrong please let me know an example of color combination logic. – Sudar Nimalan Jul 08 '12 at 11:03
  • i am using that code for painting, not sure about the color combination, i will check it clearly let you know.. – RajaReddy PolamReddy Jul 08 '12 at 11:13
3

The simplest solution I can think would be to simply use the alpha channel by setting each to 0.5 opacity, either in code or xml. Then the colours should fade into each other when they overlap. This does the mean the colours in the non-overlapping sections will be a little faded, though, and depending on what background you have behind the shapes it may not look good to have them be at all transparent.

Cathal Comerford
  • 1,020
  • 1
  • 9
  • 12
  • Thats what I said in a comment, however I don't think that is what he is going for, I think he wants to maintain opacity in the regions not overlapping!!! – trumpetlicks Jul 03 '12 at 17:26
  • if i used i am able to see the background object color transparently, but i want the combined color of overlapped objects colors.. – RajaReddy PolamReddy Jul 04 '12 at 10:39
3

I have created a sample Activity with two views DemoDrawShapeActivity where in the view2 i use canvas.clipPath and canvas.translate

to work this i set minimum sdk version 4

enter image description here

protected void onDraw(Canvas canvas) {
    Paint paint = new Paint();
    paint.setColor(Color.RED);
    path2.addCircle(getWidth()/2, getHeight()/2, getWidth()/2, Direction.CCW);
    canvas.drawPath(path2, paint);
    canvas.clipPath(path2);
    canvas.save();
    canvas.translate(-getTranslationX()+view1.getTranslationX(), -getTranslationY()+view1.getTranslationY());

    paint.setColor(Color.BLUE|Color.RED);
    canvas.drawPath(path1, paint);
    canvas.restore();

}

you can edit paint.setColor(Color.BLUE|Color.RED); to get necessary color according to your logic.

i am using the setTranslationX setTranslationY to move the views.

        public boolean onTouchEvent(MotionEvent event) {
                switch(event.getActionMasked()){
                case MotionEvent.ACTION_UP:
                    touched[0]=touched[1]=false;
                case MotionEvent.ACTION_DOWN:
                case MotionEvent.ACTION_MOVE:
                    if(touched[1]){
                        view2.setTranslationX(event.getX()-view2.getWidth()/2);
                        view2.setTranslationY(event.getY()-view2.getHeight()/2);
                    }else if(touched[0]){
                        view1.setTranslationX(event.getX()-view1.getWidth()/2);
                        view1.setTranslationY(event.getY()-view1.getHeight()/2);
                        view2.invalidate();
                    }
                }
                return true;
            }
Sudar Nimalan
  • 3,962
  • 2
  • 20
  • 28
  • i use the setTranslationX , setTranslationY to move the view on the screen. so getTranslationX will give us where is the view is located. – Sudar Nimalan Jul 06 '12 at 07:45
  • Here i'm creating custom view having different onDraw() functions, if i use like what you said i can't give some more features for app, is it possible to achieve by using customview. i tried this facing problem at setColor() for previous one. – RajaReddy PolamReddy Jul 06 '12 at 11:17
  • Could you post a sample code of your onDraw method, you have to separate defining shape logic and encapsulate in separate method, and should be able to refer from other view. – Sudar Nimalan Jul 06 '12 at 13:06
  • i have one framelayout and creating custom views and adding to that framelayout dynamically, so every custom view having different onDraw() function's. i am not creating all objects in one customview. – RajaReddy PolamReddy Jul 06 '12 at 13:16
  • please confirm, 1. You are going to have more than one objects, each from different view types. 2. But all will be in a particular order. If so still you can have multiple .clipPaths, if you check my sample i have two different views both has two different onDraw methods. in your case up views should worry about views in down and draw overlapping part accordingly. For this you should keep a reference list in order of which are added. – Sudar Nimalan Jul 06 '12 at 13:38
  • Yes i have more than one objects those are adding dynamically, and here your are using this paint.setColor(Color.BLUE|Color.RED) static bur i want this to be dynamic depending on the overlapped colors..if you not understand my point let me know.. – RajaReddy PolamReddy Jul 07 '12 at 03:55
  • The link is dead. Can you please share it on Github? – android developer Apr 04 '17 at 13:00
2

I think you may be looking to draw in a blend mode. Android will let you do this, just look at this first link. Composite operations in Android Canvas

Here are all the compositing options http://developer.android.com/reference/android/graphics/PorterDuffXfermode.html

And their explanations from Mozilla https://developer.mozilla.org/en/Canvas_tutorial/Compositing

Community
  • 1
  • 1
sebsebmc
  • 112
  • 4
-1

You may take the help from this -

http://www.learnopengles.com/android-lesson-five-an-introduction-to-blending/

and

www.pushing-pixels.org/category/android/page/6

Kumar Shorav
  • 531
  • 4
  • 16