Get the FULL version

Android: Changing image color saturation

Android: Changing image color saturation thumbnail

It’s the 100th post on 41 Post! This Android programming tutorial shows how to control the color saturation of an image using a SeekBar. The code featured in this post has been tested at an emulated and on a real device both running Android 2.1 . An example project with the code featured below is available at the end of the post.

As previously described, a SeekBar is going to be used to control the saturation of an Bitmap object that is rendered at the screen using a ImageView. Since a screenshot is far more descriptive then trying to explain how the application user interface will end up looking like, here’s a screenshot:

Changing Image Saturation Screenshot

The XML code for this layout can be found at the example project available for download at the end of the post.

To control the saturation of the Bitmap, a ColorMatrix, a ColorMatrixColorFilter and a Paint object are going to be required. Then, the saturation is set by the ColorMatrix which is passed as a parameter to the ColorMatrixColorFilter that, in turn, is set as the color filter to the Paint object. After that, the paint is applied when rendering the Bitmap. Here’s the code:

package fortyonepost.com.cis;

import android.os.Bundle;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;

public class CISactivity extends Activity 
{
	//An ImageView and a SeekBar
	private ImageView ivImage;
	private SeekBar sbSeekBar;

	//These member variables responsible for modifying the color saturation
	private ColorMatrix colorMatrix;
	private ColorMatrixColorFilter cmFilter;
	private Paint cmPaint;

	//These member variables are responsible for drawing the Bitmap
	private Canvas cv;
	private Bitmap imgBitmap;
	private Bitmap canvasBitmap;

    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_cisactivity);

        //Initialize the ColorMatrix object
        colorMatrix = new ColorMatrix();
        //Initialize the ColorMatrixColorFilter object
		cmFilter = new ColorMatrixColorFilter(this.colorMatrix);

		//Initialize the cmPaint
		cmPaint = new Paint();
		//Set 'cmFilter' as the color filter of this paint
		cmPaint.setColorFilter(cmFilter);

		//Initialize the 'imgBitmap' by decoding the 'exampleimage.png' file
		imgBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.exampleimage);
		//Create a new mutable Bitmap, with the same width and height as the 'imgBitmap'
		canvasBitmap = Bitmap.createBitmap(128, 128, Bitmap.Config.ARGB_8888);
		//Initialize the canvas assigning the mutable Bitmap to it
		cv = new Canvas(canvasBitmap);

		//Initialize the ImageView and the SeekBar objects by inflating them 'activity_cisactivity.xml' layout file
        ivImage = (ImageView) findViewById(R.id.iv_exampleimage);
        sbSeekBar = (SeekBar) findViewById(R.id.sb_seekbar);

        //Sets the range between 0 and 100
        sbSeekBar.setMax(100);
        //Set the seek bar increments to 1
        sbSeekBar.setKeyProgressIncrement(1);
        //Set the progress to 100
        sbSeekBar.setProgress(100);

        sbSeekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() 
		{
			@Override
			public void onStopTrackingTouch(SeekBar seekBar) 
			{
			}

			@Override
			public void onStartTrackingTouch(SeekBar seekBar) 
			{
			}

			//This method is called every time the SeekBar thumb is dragged
			@Override
			public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) 
			{
				//Set the ColorMatrix saturation according to the SeekBar progress
				colorMatrix.setSaturation(progress/(float)100);
				//Create a new ColorMatrixColorFilter with the recently altered colorMatrix
				cmFilter = new ColorMatrixColorFilter(colorMatrix);

				//Assign the ColorMatrix to the paint object again
				cmPaint.setColorFilter(cmFilter);

				//Draw the Bitmap into the mutable Bitmap using the canvas. Don't forget to pass the Paint as the last parameter
				cv.drawBitmap(imgBitmap, 0, 0, cmPaint);
				//Set the mutalbe Bitmap to be rendered by the ImageView
				ivImage.setImageBitmap(canvasBitmap);				
			}
		});	
    }
}

There are a total of eight member variables being declared at the above Activity. The first two are just handles to the View widgets that need to be manipulated (lines 18 and 19). Namely, the image the ImageView renders needs to be changed and the current SeekBar progress has to be obtained.

The next three variables are the ones that are going to control the saturation of the image. They consist of a ColorMatrix, a ColorMatrixColorFilter and a Paint object (line 22 through 24). They will later create a Paint that controls the saturation of the image. The last three member variables are the ones that will actually render the Bitmap on the screen. So, a pair of Bitmaps and a Canvas object are required. One Bitmap will hold the pixels decode from an image file, the other will act as a render target to the Canvas (line 27 through 29).

Inside the onCreate() method, the ColorMatrix object is initialized. This is the object that can change the image’s saturation by manipulating the values of a 5×4 matrix (line 38). With that, the ColorMatrixColorFilter object can be initialized (line 40). As the name suggests, this object filters the colors of an image. However, in order to make it work, this ColorFilter has to be associated with a Paint. This being achieved at line 45. By doing so, all that is necessary to do is to pass cmPaint as a parameter when rendering the Bitmap.

One Bitmap object will hold the decoded image placed at the res/drawable-hdpi folder. The other Bitmap is going to be set as the render target of the Canvas. This is required, since the Bitmap that holds the image (imgBitmap) needs to be rendered using the Paint and the most straightforward approach to do it is by calling the Canvas.DrawBitmap() method. Not only that, but to render a decoded image using a ImageView, a Bitmap object needs to be passed as a parameter to the ImageView.setImageBitmap() method which is going to be set as the target Bitmap that the Canvas is going to be rendering at.

With that in mind, it’s now possible to initialize the two Bitmap objects at lines 48 and 50. The first Bitmap initialized is the imgBitmap that holds the decoded ‘exampleimage.png‘ file placedinside the res/drawable-hdpi folder (line 48). The second Bitmap being initialized is the canvasBitmap that will serve as render target to the canvas. Note that a mutable Bitmap must be created in order to make this work. This is done by calling the Bitmap.createBitmap() method, passing the desired width and height as well as the encoding as parameters (line 50). The width and height are conveniently set as the width and height of the ‘exampleimage.png‘, in pixels. Right after that, the canvas is initialized taking the canvasBitmap as the single parameter (line 52).

Moving on, both ImageView and SeekBar methods are initialized at lines 55 and 56 by inflating them from the ‘activity_cisactivity.xml‘ layout xml file (lines 55 and 56). With everything set up to change the image saturation, now it’s just a matter of setting the SeekBar properties and rendering the decoded Bitmap using the cmPaint. The SeekBar object maximum progress value is set to 100, the increment is set to 1 and the current progress is set to 100 (lines 59, 61 and 63). This means that the SeekBar can set 100 levels of color saturation, and by having the initial at 100, the image is initially unchanged, with its original saturation.

Finally, the OnSeekBarChangeListener interface is being defined for to the seekBar (line 79). It must implement three methods, however, for this tutorial, only the onProgressChanged() is going to be implemented (lines 79 through 93). It is called every time the SeekBar thumb is dragged, so all changes made to the image saturation can be previewed instantly. Inside this method, the colorMatrix.setSaturation() method is called, taking a float as a parameter (line 82). It has to be a value between the 0.0 (grayscale) and the 1.0 (color). Since the seekBar maximum progress has been set to 100 the current progress must be divided by 100 so it can be clamped at the 0.0 – 1.0 range.

Then, a new ColorMatrixColorFilter is created at line 84, receiving the recently modified ColorMatrix object. Again, this filter is assigned to the Paint object (line 87). The imgBitmap is rendered using the Canvas object and the cmPaint at line 90, by calling the drawBitmap() from the Canvas object passing cmPaint as the last parameter. The canvasBitmap now holds a rendered imgBitmap with the specified saturation defined by the ColorMatrix object. The last thing to do is to render it on the screen by calling the setImageBitmap() method from the ImageView object, passing the canvasBitmap as the single parameter (line 92).

That’s it!

Downloads

No Comments to “Android: Changing image color saturation”

Leave a Comment

Post Comments RSS