Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set combined color for overlapped area of two different color objects?

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.

like image 269
RajaReddy PolamReddy Avatar asked Jul 03 '12 14:07

RajaReddy PolamReddy


2 Answers

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

RYB:

enter image description hereCC 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 hereCC 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.

like image 122
onosendai Avatar answered Sep 30 '22 07:09

onosendai


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.

like image 37
Sudar Nimalan Avatar answered Sep 30 '22 08:09

Sudar Nimalan