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.
Related
Should I expect the color of the pixel to be black if
image.getRGB(x, y) returns 0?
My assumption: I would expect 0 because the bit values of each of the values (Red, Green, Blue) would be zero. Am I correct in thinking this?
No, BufferedImage#getRGB() returns hex number. See this unit test:
public class TestRgb {
#Test
public void testBlack(){
BufferedImage bufferedImage = new BufferedImage(1,1, TYPE_BYTE_BINARY);
Graphics2D graphics2D = bufferedImage.createGraphics();
graphics2D.setPaint(new Color(0,0,0)); //black
graphics2D.fillRect(0,0,1,1);
// pass - alpha channel set by default, even on all black pixels
TestCase.assertTrue(bufferedImage.getRGB(0,0)==0xFF000000);
// pass - when looking at just the color values (last 24 bits) the value is 0
TestCase.assertTrue((bufferedImage.getRGB(0,0) & 0x00FFFFFF)==0);
// fail - see above
TestCase.assertTrue(bufferedImage.getRGB(0,0)==0);
}
}
"Returns the RGB value representing the color in the default sRGB ColorModel. (Bits 24-31 are alpha, 16-23 are red, 8-15 are green, 0-7 are =blue)."
That is, the packing (in hex locations) is as follows, where each component can have a value of 0 (0x00) .. 255 (0xFF).
AARRGGBB
Thus, the final value is not only dependent upon RGB, when all color components are zero:
AA000000
In fact, AA will be 0xFF ("100% opaque") by default unless it has explicitly been set to a different value in a buffer / model that supports an alpha channel.
I am currently using the .setRGB() method. It appears that the input for the int[] rgbArray is a hex-code value, which are very large integer values. I currently have a set of integers that range from 0 - 255, so whenever I input them using .setRGB, the image is just pure black, which makes sense given the range of hex values versus RGB values.
I was wondering is there a way for me to use non-hex values?
It's not Hex; it's bit-fiddled RGB. I would use java.awt.Color and then call getRGB(). So,
int rgb = new Color(red, green, blue).getRGB();
As mentioned in the other answer, the "large values" that you are talking about are the combined R,G,B components of the color, and possibly an "Alpha" component.
Setting a value of 255 with the setRGB method should thus cause the pixel to become perfeclty blue - unless it is an image that has an alpha channel. In this case it would become a perfectly transparent blue (aka "black").
However, when you have a value between 0 and 255, you can convert it into an RGB value (with 0=black and 255=white) as follows:
int value = ...
int rgb = (255 << 24) | (value << 16) | (value << 8) | value;
(The alpha component here is set to 255. For images without alpha channel, this will not change anything. But for images that have an alpha channel, this is crucial to make sure that the color is actually visible)
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.
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?
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().