the Flagship blog & community for App Developers with main focus on Samsung's bada and cross-platform technologies
Bada Tutorials

Create a Photo-Editing App

This tutorial is going to demonstrate the basics of photo editing with Bada. I am going to create a photo filter that converts colour photo to grayscale photo using popular mathematical formula.

With some mathematical knowledge, you can apply the same technique to create cool photo filters like the one you use in GIMP or photoshop.

To create grayscale filter in Bada we need to:

1- Add FMedia.h and IMAGE privilege to the manifest.xml
2- Load the photo using Image class to a Bitmap object
3- Use BufferInfo class gives you the opportunity to access Bitmap pixels directly in the memory to modify them.

BufferInfo does not have any member functions but has six public attributes which are self explanatory. The most interesting attribute is the (void pointer) void * pPixels which allows you to access the pixels directly in the memory. Void pointer can point to any type of data type but you have to cast the address of it to some other pointer type to be able to manipulate its value or increment the pointer.
 

I have used a modified nice piece of code posted by Yuuki at the forums here that traverses through the bitmap pixel by pixel/column by column.

The original code posted by Yuuki gets the value of the pixel as shown below

//assuming 4 bytes/pixel
 int color = *((int *)((byte *) info.pPixels + y *
                                     info.pitch + x * 4));

For the grayscale filter, I want to access the Red/Green/Blue values of the pixel instead of getting the total value of the RGBA pixel. Therefore, I changed the code to

int* color = ((int *)((byte *) info.pPixels + y *
                            info.pitch + x * bytesPerPixel));

 
Instead of receiving the integer value, I modified the code to receive a pointer to the integer. This way I can access the integer value byte by byte to manipulate the Red/Green/Blue components of the pixel. You can create a byte pointer that points to the first byte of (int* color) and then increment the byte pointer to access the next byte and so on.

Maybe you can use Hexadecimal/Bitwise operations to retrieve the values of each byte directly instead of creating byte pointer. However, I used this technique for easier explanation but it is up to you to find the best/fast solution.
 

The mathematical formula used to convert RGB photo to grayscale is shown below

gray = (Red * 0.3) + (Green * 0.59) + (Blue * 0.11)
Red = gray
Green = gray
Blue = gray

 
One last thing you need to be aware about that RGBA is stored as BGRA if you choose BITMAP_PIXEL_FORMAT_ARGB8888 parameter when decoding the photo to a bitmap.
 
The output of applying the filter to the rose photo:

The full code of GrayScale filter application:

void PhotoEdit::GrayScaleFilter() {
	result r = E_SUCCESS;
	Image image;
	Bitmap* pBitmap = null;
	BufferInfo info;
	int width = 0;
	int height = 0;
	int bitsPerPixel = 0;
	int bytesPerPixel = 0;
	String photo(L"/Res/rose.jpg");
	String output(L"/Home/rose.jpg");

	r = image.Construct(); //check if fails

	pBitmap = image.DecodeN(photo, BITMAP_PIXEL_FORMAT_ARGB8888 );
	//check if pBitmap is null

	pBitmap->Lock(info);

	width = info.width;
	height = info.height;
	bitsPerPixel = info.bitsPerPixel;

	if(bitsPerPixel == 32)
	bytesPerPixel = 4;
	else if(bitsPerPixel == 24)
	bytesPerPixel =3;

	for(int x=0;x<width;x++) {
		for (int y=0;y<height;y++) {
			int* color = ((int *)((byte *) info.pPixels + y * info.pitch + x *bytesPerPixel));
			byte* blue = (byte*) color;
			byte* green = blue+ 1;
			byte* red = green+ 1;
			byte gray = ((*blue) * 0.11) + ((*green) * 0.59) + ((*red) *0.3);
			*red = gray;
			*green = gray;
			*blue =gray;
	   }
	}

	pBitmap->Unlock();

	r = image.EncodeToFile(*pBitmap,IMG_FORMAT_JPG,output,true);
	//check if fails

	delete pBitmap;
}

Related posts:

  1. How to create bada widgets
  2. Photo of a possible Samsung bada phone
  3. Bada Project: Simple Finger Drawing App
  1. 26 Responses to “Create a Photo-Editing App”

  2. By EatenBackToLife on Jun 30, 2010 | Reply

    i’d rather made that loop like this:

    byte* pixel( info.pPixels );
    static float coefs[] = { 0.11, 0.59, 0.3 };
    for( int count( width * height); 0 != count; –count ) {
    byte gray(0);
    for( int i(0); i < 3; ++i ) {
    gray += pixel[i] * coefs[i];
    }
    for( int i(0); i < 3; ++i ) {
    pixel[i] = gray;
    }
    pixel += bytesPerPixel;
    }

    this can save pixel address calculation on each iteration

  3. By EatenBackToLife on Jun 30, 2010 | Reply

    oops. don’t read my previous comment: i didn’t take info.pitch into account – it was off the screen :)

  4. By EatenBackToLife on Jun 30, 2010 | Reply

    here’s correct version. at least i hope it’s correct..

    static float coefs[] = { 0.11, 0.59, 0.3 };
    for( byte* rowPointer( info.pPixels ); –height != 0; rowPointer += info.pitch ) {
    byte* pixel( rowPointer );
    for( int x( width ); 0 != x; –x ) {
    byte gray(0);
    for( int i(0); i < 3; ++i ) {
    gray += pixel[i] * coefs[i];
    }
    for( int i(0); i < 3; ++i ) {
    pixel[i] = gray;
    }
    pixel += bytesPerPixel;
    }
    }

  5. By Nour on Jun 30, 2010 | Reply

    Thanks EatenBackToLife :)

    I’d like to give it a try but your code will be hard to debug IMO.

    Can you estimate how much this code reduces processing power or memory?

  6. By p.l on Jun 30, 2010 | Reply

    It is ridiculous that there is no API for manipulating bitmap as a canvas (so you can’t even put a custom text onto bitmap)

  7. By Nour on Jun 30, 2010 | Reply

    p.l

    There is Canvas API available!

    You can manipulate the bitmap in different ways and draw text on it.

    1- Canvas::DrawBitmap()
    2- Canvas::DrawText()
    3- Bitmap::Construct (Canvas, Rectangle)

  8. By caketuzz on Jun 30, 2010 | Reply

    Thanks for the tut.
    It may be useful to build photographers tools, such as histograms and levels treatment.
    However, do we know how quick is that method when you apply it to large images, such as images made with the camera ?

  9. By Nour on Jun 30, 2010 | Reply

    Hi caketuzz

    I have tried it on 5 megapixel taken by the phone camera [2560 x 1920] and the speed was good at the simulator.

    I think EatenBackToLife code is faster because it uses one large loop only.

    You can give it a try and tell us how it goes :)

  10. By malloth on Jun 30, 2010 | Reply

    It’s easier and much faster to use:

    GS = (R + G + B) / 3;
    R = GS; G = GS; B = GS;

    You don’t use floating points and get 8 bit grayscale image (image can be now flatten to 8 bits/color).

  11. By Nour on Jun 30, 2010 | Reply

    Thanks malloth :)

    I knew about this formula. I didn’t thought it will be much faster to use it.

    Thanks for sharing

  12. By caketuzz on Jun 30, 2010 | Reply

    Malloth: your grayscale conversion is somewhat wrong according to the human visual system.
    Nour: I wish I could test but I lack a Wave – I had to make a choice and purchase a Spica instead. (If someone at Samsung reads me, I’m looking for a test sample ;) )
    Still I’m not sure we can extrapolate the speed in the emulator to that on the real device.

  13. By Nour on Jun 30, 2010 | Reply

    @caketuzz

    I don’t have wave either. It has not arrive to my country and may not. I was trying to get for a month now but I failed…. so if they hear us both :)

    I think wave is a decent handset and can handle this solution.

  14. By malloth on Jun 30, 2010 | Reply

    This simple algorithm was tested on Windows Mobile based devices when I deveoped simillar application as in the title.

    Both algorithms are correct, but both have merits and flaws:

    one uses floating point numbers on each pixel which slows down the operation on big images, but looks very good (24bit grayscale),

    the other uses integers (bytes actually) which makes it faster to proceed big images, however flatterns the whole image to 8 bit grayscale – which can be seen inaccurate (by human eye) only if image contained gradients (choppy passing from one color to another).

    I suppose both algorithms have proper usage.

    PS. If You use other factors You can achieve Sepia effect:

    void Sepia(int* pixels, int width, int height)
    {
    	int pixelsLength = width * height;
    	int red, green, blue;
    	BYTE r, g, b;
    
    	for(int i = 0; i  255) red = 255;
            if(green>255) green = 255;
            if(blue>255) blue = 255;
    
    	if(red<0) red = 0;
            if(green<0) green = 0;
            if(blue<0) blue = 0;
    
    	pixels[i] = (int)RGB(blue, green, red);
    	}
    }
    

    Example is from WM but can be easily remade to Bada.

  15. By malloth on Jun 30, 2010 | Reply

    AGAIN! I can’t post from this line: “r = G e t R V a l u e ( p i x e l s [i] );”. We could use a decent code editor in theses comments…

  16. By Nour on Jun 30, 2010 | Reply

    Thanks malloth for the explanation. I will see the comments if I can modify them

    Thanks for the tip of Sepia effect

  17. By Nour on Jun 30, 2010 | Reply

    malloth, you can use tag to post nice code

  18. By malloth on Jul 1, 2010 | Reply

    Ok, now with proper tag (I hope it will show up well this time):

    void Sepia(int* pixels, int width, int height)
    {
    	int pixelsLength = width * height;
    	int red, green, blue;
    	BYTE r, g, b;
    
    	for(int i = 0; i < pixelsLength; i++)
    	{
    		r = GetRValue(pixels[i]);
    		g = GetGValue(pixels[i]);
    		b = GetBValue(pixels[i]);
    
    		red = ((b * 189) / 1000) + ((g * 769) / 1000) + ((r * 393) / 1000);
    		green = ((b * 168) / 1000) + ((g * 686) / 1000) + ((r * 349) / 1000);
    		blue = ((b * 131) / 1000) + ((g * 534) / 1000) + ((r * 272) / 1000);
    
    		if(red > 255) red = 255;
            if(green > 255) green = 255;
            if(blue > 255) blue = 255;
    
    		if(red < 0) red = 0;
            if(green < 0) green = 0;
            if(blue < 0) blue = 0;
    
    		pixels[i] = (int)RGB(blue, green, red);
    	}
    }
    
  19. By malloth on Jul 1, 2010 | Reply

    Thanks Nour. It works with that tag now :) .

    If You’d need any more image effects (like Solarize, Normalize, Pixelate, Sharpen, Blur etc.) or help with convolution filters, I’d be honored to assist You.

  20. By Nour on Jul 1, 2010 | Reply

    wow, thanks malloth for your generosity to share your code with us.

    I am not working on photo-effects application now, but I shall contact you if I need any assistant in future.

    thanks again :)

  21. By codenode on Jul 1, 2010 | Reply

    When you look for performance you should get rid of tempvariables if not needed:

    *green = *blue = *red = ((*blue) * 0.11) + ((*green) * 0.59) + ((*red) *0.3);
    //*green = *red;
    //*blue =*red;
    
  22. By Nour on Jul 2, 2010 | Reply

    Thanks codenode, but I don’t think removing extra byte will improve the performance. Anyway, I should have declared [byte gray] outside the loop but I didn’t pay much attention to the performance in this tutorial.

    Thanks for the tip :)

  23. By Phong Nguyen on Jul 21, 2010 | Reply

    dear all
    Image image;
    image.Construct();

    cannot compile and error happen on this

    please check your code

  24. By Nour on Jul 21, 2010 | Reply

    @Phong Nguyen

    search the forums to find the solution

  25. By Kien on Aug 23, 2010 | Reply

    How to rotate a image by BufferInfo ?

  26. By HFSDev on Jan 7, 2011 | Reply

    Hey guys, what’s up?

    First of all, Thx for the code!

    But how do you handle transparent images? I mean, how can one achieve the same effect as overlayed calls to Canvas::DrawBitmap(…) with direct memory access?
    How could one draw a bitmap with transparency directly into canvas buffer?

  1. 1 Trackback(s)

  2. Jul 13, 2010: BadaDev » Building a Flexible Image Viewer Control (part 1) – Scrolling

Post a Comment

Editor's picks

Copyright 2009-2010 BadaDev.com (unless otherwise stated). All rights reserved! Powered by Wordpress!