Explanation of the method getPixels for a Bitmap in Android - java

How do I interpret the returned array from build-in method getPixels for a Bitmap?
Here is my code:
public void foo() {
int[] pixels;
Bitmap bitmapFoo = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.test2);
int height = bitmapFoo.getHeight();
int width = bitmapFoo.getWidth();
pixels = new int[height * width];
bitmapFoo.getPixels(pixels, 0, width, 1, 1, width - 1, height - 1);
}
The array "pixels" gets returned with values from -988,602,635 to 1,242,635,509 and that was just from a few colors on a simple PNG file I made. How can I interpret the numbers that get returned from this method?
Edit: I realize this single integer represents a color. I just don't understand how to interpret this single integer into the RBG and alpha values that make up the color.
Thanks.
PS. If your asking yourself, "what is he trying to do?" I am trying to figure out a way to dynamically modify the color of a bitmap.

You can also do the following to retrieve colors from
an int :
int mColor = 0xffffffff;
int alpha = Color.alpha(mColor);
int red = Color.red(mColor);
int green = Color.green(mColor);
int blue = Color.blue(mColor);

It returns an int for the Color class.
The Color class defines methods for
creating and converting color ints.
Colors are represented as packed ints,
made up of 4 bytes: alpha, red, green,
blue. The values are unpremultiplied,
meaning any transparency is stored
solely in the alpha component, and not
in the color components. The
components are stored as follows
(alpha << 24) | (red << 16) | (green
<< 8) | blue. Each component ranges
between 0..255 with 0 meaning no
contribution for that component, and
255 meaning 100% contribution. Thus
opaque-black would be 0xFF000000 (100%
opaque but no contributes from red,
gree, blue, and opaque-white would be
0xFFFFFFFF
For example, when you use the Paint object:
Paint pRed = new Paint();
pRed.setColor(Color.RED);
setColor expects an int. Color.RED is that int value for their pre-defined meaning of "red".

Even more:
int alpha=argb>>24;
int red=(argb & 0x00FF0000)>>16;
int green=(argb & 0x0000FF00)>>8;
int blue=(argb & 0x000000FF);

If you have your alpha, red, green and blue values, your in color is equal to (alpha << 24) + (red << 16) + (green << 8) + blue.
To retrieve your alpha, red, green and blue values from an int, say argb:
int alpha=argb>>24;
int red=(argb-alpha)>>16;
int green=(argb-(alpha+red))>>8;
int blue=(argb-(alpha+red+green));

Besides that, I think it should be
bitmapFoo.getPixels(pixels, 0, width, 0, 0, width, height);
The coordinates start from 0, right?

Related

Understanding BufferedImage.getRGB output values

I'm getting an integer value for the pixel in an image using this method:
int colour = img.getRGB(x, y);
Then I'm printing out the values and I see that black pixels correspond to a value like "-16777216", a kind of blue to something like "-16755216", etc. Can someone please explain me the logic behind this value?
The RGB int color contains the Red, Green, Blue components of the color in its bits. You have to look at its binary or hexadecimal representation and not look at it as a whole integer number (not look at its decimal representation).
An int has 32 bits, 3x8 = 24 is used to store the RGB components (8 bits for each) in the following format:
2 1 0
bitpos 32109876 54321098 76543210
------ --+--------+--------+--------+
bits ..|RRRRRRRR|GGGGGGGG|BBBBBBBB|
You can extract or set the components using bitmasks:
int color = img.getRGB(x, y);
// Components will be in the range of 0..255:
int blue = color & 0xff;
int green = (color & 0xff00) >> 8;
int red = (color & 0xff0000) >> 16;
If the color also has an alpha component (transparency) ARGB, it gets the last remaining 8 bits.
3 2 1 0
bitpos 10987654 32109876 54321098 76543210
------ +--------+--------+--------+--------+
bits |AAAAAAAA|RRRRRRRR|GGGGGGGG|BBBBBBBB|
And the value:
int alpha = (color & 0xff000000) >>> 24; // Note the >>> shift
// required due to sign bit
An alpha value of 255 means that a color is completely opaque and a value of 0 means that the color is completely transparent.
Your color:
Your color is color = -16755216 which has:
blue : 240 // Strong blue
green: 85 // A little green mixed in
red : 0 // No red component at all
alpha: 255 // Completely opaque
getRGB(int x, int y) return you the value of color pixel at location (x,y).
You are misinterpreting the returned value.
It is in the binary format.
like 11...11010101 and that is given to you as int value.
If you want to get RGB (i.e. Red, Green, Blue) components of that value use Color class. e.g.
Color mycolor = new Color(img.getRGB(x, y));
Then you can get the Red, Green, Blue, or Alpha values by using getRed(), getGreen(), getBlue(), getAlpha().
Then an int value will be returned by these methods in familiar format having value 0 < value < 255
int red = mycolor.getRed();
If you don't want to use Color class then you will need to use bitwise operations to get its value.
See the implementation of ColorModel.getRgb:
589 public int getRGB(int pixel) {
590 return (getAlpha(pixel) << 24)
591 | (getRed(pixel) << 16)
592 | (getGreen(pixel) << 8)
593 | (getBlue(pixel) << 0);
594 }
It's explained in the docs:
Returns an integer pixel in the default RGB color model (TYPE_INT_ARGB) [...]
So you get 8 bits alpha channel, 8 bits red, 8 bits green, 8 bits blue.
A simple (and slow way) to examine the value is to use new java.awt.Color(colour, true); and then call the getters.
Or you can print the value as an unsigned 32bit hex value: Integer.toString(colour, 16). Each two characters of the output will be one part of the ARGB set.
Actually,You can transform the int to binary string by Integer.toBinaryString(-16755216),which is 11111111000000000101010111110000.it made up of 4 bytes: alpha, red, green, blue. The values are unpremultiplied, meaning any transparency is stored solely in the alpha component, and not in the color components. The components are stored as follows (alpha << 24) | (red << 16) | (green << 8) | blue. Each component ranges between 0..255 with 0 meaning no contribution for that component, and 255 meaning 100% contribution. Thus opaque-black would be 0xFF000000 (100% opaque but no contributions from red, green, or blue), and opaque-white would be 0xFFFFFFFF.

Convert integer color value to RGB

I am trying to modify a third party software. I want to use a color which is returned by some methods (which I cant modifiy) as an integer. However, I would like to use RGB format, like #FF00FF. How can I make a conversion?
Here is an HTML example http://www.shodor.org/stella2java/rgbint.html
I would like to archive same thing in Java, on Android.
What I found to be the simplest and best solution for me was to directly use the Color class as follows:
int red = Color.red(intColor);
int green = Color.green(intColor);
int blue = Color.blue(intColor);
int alpha = Color.alpha(intColor);
This way I could already deal with the integer values without having to handle strings. If on the other hand the string representing the rgb color is what you need, Pankaj Kumar's answer is the best. I hope this is useful to someone.
Use this
String hexColor = String.format("#%06X", (0xFFFFFF & intColor));
We know lenght of color value in HEX is 6. So you see 6 here. %06X matches the result coming from (0xFFFFFF & intColor) and if length is less than 6, it makes result with 6 by appending ZERO to left side of result. And you see #, so this # char gets appended to result and finally you get a HEX COLOR value.
Update since API 26
Since API 26, new methods Color.valueOf(....) has been introduced to convert colors for similar reason. you can use it like
// sRGB
Color opaqueRed = Color.valueOf(0xffff0000); // from a color int
Color translucentRed = Color.valueOf(1.0f, 0.0f, 0.0f, 0.5f);
// Wide gamut color
ColorSpace sRgb = ColorSpace.get(ColorSpace.Named.SRGB);
#ColorLong long p3 = Color.pack(1.0f, 1.0f, 0.0f, 1.0f, sRgb);
Color opaqueYellow = Color.valueOf(p3); // from a color long
// CIE L*a*b* color space
ColorSpace lab = ColorSpace.get(Named.CIE_LAB);
Color green = Color.valueOf(100.0f, -128.0f, 128.0f, 1.0f, lab);
mView.setBackgroundColor(opaqueRed.toArgb());
mView2.setBackgroundColor(green.toArgb());
mView3.setBackgroundColor(translucentRed.toArgb());
mView4.setBackgroundColor(opaqueYellow.toArgb());
Since SDK 26 you can just use
Color c = Color.valueOf(colorInt);
apart from that it does not seem to possible to create a Color instance from arbitrary argb. The underlying code uses a private constructor:
/**
* Creates a new <code>Color</code> instance from an ARGB color int.
* The resulting color is in the {#link ColorSpace.Named#SRGB sRGB}
* color space.
*
* #param color The ARGB color int to create a <code>Color</code> from
* #return A non-null instance of {#link Color}
*/
#NonNull
public static Color valueOf(#ColorInt int color) {
float r = ((color >> 16) & 0xff) / 255.0f;
float g = ((color >> 8) & 0xff) / 255.0f;
float b = ((color ) & 0xff) / 255.0f;
float a = ((color >> 24) & 0xff) / 255.0f;
return new Color(r, g, b, a, ColorSpace.get(ColorSpace.Named.SRGB));
}
RGB uses hexa decimal number format,.
if you have integer value, convert it to hexa,.
It seems the value being quoted and the format desired are mismatched. The value is hexadecimal, while RGB would read 255, 0, 255 and the integer is a composite color representation. Since it is unclear what is being accomplished, here are all three variations:
If you have an integer for the composite color, then most color endpoints will accept it unmodified. This would be something like setBackgroundColor(colorInt)
If you have the hexadecimal value, then Color.parseColor(#colorHex) would convert it to a color object.
Likewise, Color.rgb(redInt, greenInt, blueInt) would convert the red, green, and blue values to a color object.
If you need to restore the composite integer into a color object, that is even simpler with Color.valueOf(colorInt)

Image Pixel Replacing

I am looking to replace pixels in an image that are black to some degree (semi-black) to become fully black.
The method to do this is setRGB(int x, int y, int rgb). I know this. What I do not know is how to detect pixels that are semi-black.
I have tried (i is a BufferedImage):
final int rgb = i.getRGB(x, y);
if (rgb == -16777216) {
i.setRGB(x, y, -16777216);
}
To do this, but it only replaces the pixels that are pure black with pure black.
I have also tried dimming the image, but that does not work either.
Any ideas on how I test for generic blackness?
My goal: the image I am reading is thin text. I wish to make this bolder text by this.
The integer that you receive represents the combined red, green, blue and alpha values. Essentially, you need to:
break that integer down into its component red, green, blue values
from those values, assess the overall "brightness" of the pixel
As a rough implementation, you could do something like this:
int pixVal = ... getRGB() as you have
int red = (pixVal >>> 16);
int green = (pixVal >>> 8) & 0xff;
int blue = pixVal & 0xff;
int brightness = (red + green + blue) / 3;
if (brightness < 16) {
// pixel is black
}
Now, the value 16 is a rough value: ideally, you would tailor this to the particular image.
Purists might also whinge that the perceived "brightness" of a pixel isn't literally the mean of the red/green/blue pixels (because the human eye is not equally sensitive to these components). But that's the rough idea to work from.

Remove the alpha from a colour but retain its texture

I have a colour #6A8F6509
I wish to remove the Alpha part of that colour and be left with only RGB components (i.e. #RRGGBB).
The resulting colour must look identical to the initial one without the transparency.
How do I go about this on Android's Java?
Update: The initial background is always white (#FFF)
int newColor = oldColor | 0xFF000000;
If you want to do it with the code you can try following code:
static int stripAlpha(int color){
return Color.rgb(Color.red(color), Color.green(color), Color.blue(color));
}
For each color:
C' = C(a/255) + 255(1-(a/255))
So for #6A8F6509:
R' = 143(106/255) + 255(1-(106/255) = (approx) 208
G' = 101(106/255) + 255(1-(106/255) = (approx) 191
B' = 9(106/255) + 255(1-(106/255) = (approx) 153
So your answer should be: #D0BF99, if my math is correct. This only applies to an all white background as well - for a non-white background, the second addend should have the appropriate corresponding color value of the background instead of 255.
-- EDIT --
Looking at the formula again, I'm not entirely sure whether the formula gets applied to each color or the entire color (a quick visual test should be able to tell you - I'm guessing per color). I should point out that this is the formula direct from the Alpha Compositing wiki page:
http://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending
try
#FF8F6509
the first two digits indicate the alpha value of any color.
Calculate the way a semi-transparent color will look when placed on top of another color.
Kotlin color extension:
#ColorInt
fun #receiver:ColorInt Int.removeAlpha(#ColorInt underlyingColor: Int): Int {
return ColorUtils.blendARGB(this, underlyingColor, alpha / 255F)
}
Usage:
Color.parseColor("#6A8F6509").removeAlpha(underlyingColor = Color.WHITE)
Jetpack Compose version is
Color(0x6A8F6509).compositeOver(Color.White)
I'll admit to not knowing Android's Java, but can't you just change the color from #6A8F6509 to #FF8F6509? The in most modern programming languages, color is typically encoded as ##AARRGGBB.
AA = Hex of the Alpha, ranging from 0 (hex 00), full transparent, to 255 (hex FF), no transparency.
RR = Hex of the red part of the color. Similar to above, 0 is no red, 255 is full red.
GG = Hex of green.
BB = Hex of blue.
-Edit-
Based on your update, you can do this to get the color:
For each of the red, green, and blue:
get the difference between the transparent color and the background color and multiply that by the alpha value. Divide that by 255 (hex FF, if you prefer). Finally, add that number to the transparent color's value.
In your case:
int transparentR = 0x8F;
int transparentG = 0x65;
int transparentB = 0x09;
int backgroundR = 0xFF;
int backgroundG = 0xFF;
int backgroundB = 0xFF;
int alpha = 0x6A;
int newR = transparentR + ( ( (backgroundR - transparentR) * alpha ) / 255 );
int newG = transparentR + ( ( (backgroundG - transparentG) * alpha ) / 255 );
int newB = transparentR + ( ( (backgroundB - transparentB) * alpha ) / 255 );
-Edit, again-
Ok, I've redone the formula based on LJ2's answer using Wikipedia's Alpha Blending function. and had a colleague confirm this with Paint.Net. This is the version where the background Color is fully opaque, and it's in C#, sorry:
int transparentColor2(int intFgColor, int intBgColor, int intAlpha)
{
double dAlpha = (Convert.ToDouble(intAlpha)/255.0);
double dFirst = Convert.ToDouble(intFgColor) * dAlpha;
double dSecond = Convert.ToDouble(intBgColor) * (1 - dAlpha);
return Convert.ToInt32(dFirst + dSecond);
}
do this for each R, G, and B value, and you should get the right answer. Incase Android's Java has different, double could be Double/Single/Float, and int could be Int32/Int16/Int64/Byte. I don't remember how to Convert between the two in Java, unfortunately.

Is there an equivalence of MapRGB for a BufferedImage in Java?

I'm trying to color individual pixels in a BufferedImage (TYPE_INT_RGB) using setRGB(), but I'm not sure how to format the RGB values. I want the result as a single integer. Is there a method that will take three int values (red, green, blue) and return a correctly formatted integer for setRGB()?
new Color(red, green, blue).getRGB();
Assuming you have ints r, g, and b, you can do:
int pixel = (r << 16) | (g << 8) | b;
This is because pixels in a BufferedImage are 4-byte ints. The 4-bytes represent Alpha, Red, Green, and Blue, in that order. So, if you shift red left by two bytes and green left by one byte, then bitwise-or r, g, and b, you will get a valid pixel to use with setRGB().

Categories