Using the Color Class in Java
A BufferedImage lets you read and write individual pixels, and getRGB(x,y) returns an int that encodes the pixel's color.
Reading a Pixel's Color
A BufferedImage lets you work with individual pixels rather than whole images. Each pixel has a color, and you can ask the image for the color at any position. The method image.getRGB(x,y) looks up the pixel at coordinates (x,y) and returns a single int that encodes that pixel's color. Before learning how that int works, notice the overall shape of the task: a position goes in, and a color comes out. This article explains how the color is stored inside that int, how to pull the red, green, and blue parts back out, and how to rebuild the int after changing a part and write it back to the image.
What do you think happens?
If one pixel is pure red with no green or blue, what is its red value on the scale used for each color component?
Reveal answer
255
Each red, green, and blue component runs from 0 to 255. Pure red means the red component is at full intensity, which is 255, while green and blue are 0.
Asking a BufferedImage for a Pixel
The source shows the basic pattern in two steps. First, the image returns the packed color number for a position. Second, that number can be wrapped in a Color object so that you work with the color through the Color class. The code below does both steps for the pixel at column 1, row 2.
- The image is a 4 by 4 grid of pixels, so valid x and y values are 0 through 3.
getRGB(1, 2)returns one int that encodes the color of the pixel at x = 1, y = 2.- new
Color(rgb)wraps that int in a Color object.
Notice that getRGB returns a single number, not three numbers. The red, green, and blue information is packed into that one int, so it must be unpacked when you need the separate components. Keep this in mind, because it explains almost every step that follows.
Keeping x and y Inside the Image
The x value selects a column and the y value selects a row. Both must lie inside the image. The coordinates range from 0 up to one less than the image width for x, and from 0 up to one less than the image height for y. If you ask for a pixel outside that range, Java throws an exception, and the program stops unless you handle it.
getWidthandgetHeightgive the image size, so the check compares x and y against the real bounds.- On the 4 by 4 image above, x = 10 is outside the image, so the getRGB call is skipped instead of throwing an exception.
One Int Holding Three Intensities
The int returned by getRGB holds 32 bits. Three of the four bytes in that int store the color intensities: red, green, and blue. Each of those bytes can hold a value from 0 to 255, because one byte is eight bits and eight bits can represent 256 different values. The diagram shows where each byte sits inside the 32 bits. The top byte is the alpha byte, which controls transparency in the standard layout; the operations in this article focus on the lower three bytes that hold the three color components.
Because red sits in bits 16 through 23, green in bits 8 through 15, and blue in bits 0 through 7, you can recover each component with a shift that moves its byte down to the lowest eight positions, followed by a mask that keeps only those eight bits. The next section does exactly that.
Pulling Out Each Component
Extraction works as a sequence of operations. For red, the source describes a shift that moves the red bits down into the lowest eight positions, then a mask that discards everything above those eight bits. The result is a number from 0 to 255. Green uses the same pattern with a shift of 8. Blue needs no shift, because it already sits in the lowest eight bits, so masking alone is enough.
- The >> operator shifts bits right. Shifting by 16 moves the red byte into the lowest eight bits.
- The & operator with 0xFF is the mask. It keeps the lowest eight bits and clears everything above them.
- Blue has no shift because it already occupies bits 0 through 7.
Splitting one pixel into its components
A pixel's packed value is 0xFF1080F0. Find its red, green, and blue values.
- 1Find red
0xFF1080F0 shifted right by 16 bits becomes 0xFF10. Masking with 0xFF keeps 0x10, which is 16 in decimal. Red is 16.
- 2Find green
0xFF1080F0 shifted right by 8 bits becomes 0xFF1080. Masking with 0xFF keeps 0x80, which is 128 in decimal. Green is 128.
- 3Find blue
0xFF1080F0 masked with 0xFF keeps 0xF0, which is 240 in decimal. Blue is 240.
The pixel has red 16, green 128, and blue 240. The top byte, 0xFF, is the alpha byte and is not one of the three color values.
Packing Components Back Together
The reverse process rebuilds the packed int. Each component is shifted into its own byte position: red moves up 16 bits, green moves up 8 bits, and blue stays where it is. The bitwise OR then combines the three positions into one int. Because each component is in the range 0 to 255, no two components overlap, so the OR does not mix their bits together. This means the two directions are inverses: you can split a color into components and then rebuild the same color from them.
- red << 16 moves the red value up into bits 16 through 23.
- green << 8 moves the green value up into bits 8 through 15.
- blue stays in bits 0 through 7.
- The vertical bar is OR, which combines the three positions into one int.
int rgb = image.getRGB(2, 1);
int red = (rgb >> 16) & 0xFF;Red is 16 and the packed value still holds the original red bits.
red = 200;
int packed = (red << 16) | (green << 8) | blue;Red is 200, and the rebuilt packed value carries the new red intensity in bits 16 through 23.
Read, Change, Write Back
Putting the pieces together gives a read-modify-write cycle. You read the pixel with getRGB, split the int into components, change the component you want, recombine the components into one int, and write that int back with setRGB. The diagram shows the order of these steps. The change affects only the chosen component, because the other components are carried over unchanged during the split and recombine.
16 128 240
ffc880f0- setRGB stores a known pixel first so the example has a predictable starting color.
- The value 0xFF000000 keeps the alpha byte fully opaque when the three color bytes are rebuilt.
- The second printed line shows the new packed value after the red change.
Color Objects Versus Packed Ints
The source shows two routes that lead to the same kind of packed value. In the first route, you work with the raw int and use shifts and masks directly. In the second route, you build a Color object from the int returned by getRGB, and then call c.getRGB() to get the int back so you can write it with setRGB. Both routes pass the same packed information, so the choice depends on whether you want to work with the Color class or with raw bit operations.
| Feature | Packed int | Color object |
|---|---|---|
| What it is | A single int that holds the alpha, red, green, and blue bytes | A Java object that wraps a color |
| How it is obtained | Returned directly by image.getRGB(x, y) | Built from an int with new Color(rgb) |
| How it goes back to the image | Passed directly to setRGB | Converted back to an int with c.getRGB(), then passed to setRGB |
| Best used for | Bit-level work with shifts, masks, and OR | Working with colors through the Color class |
Two routes to the same packed color value
Mistakes That Corrupt Pixels
Common mistake Calling getRGB or setRGB with coordinates outside the image
What it looks likeimage.getRGB(10, 0) on a 4 by 4 imageWhy it failsValid x values run from 0 to width minus 1, and valid y values run from 0 to height minus 1. Anything outside that range makes Java throw an exception.
Do this insteadCompare x and y with getWidth and getHeight before calling getRGB or setRGB.
Common mistake Rebuilding the int without shifting red and green into position
What it looks likeint packed = red | green | blue;Why it failsWithout shifts, all three values sit in the lowest eight bits and overlap each other, so the result is not the color that was intended.
Do this insteadShift red left by 16 and green left by 8 before combining: (red << 16) | (green << 8) | blue.
Common mistake Forgetting the alpha byte when writing the pixel back
What it looks likeint packed = (red << 16) | (green << 8) | blue; with the top byte left at zeroWhy it failsThe three-component rebuild leaves the top byte empty, so the alpha value becomes zero and the pixel can end up transparent.
Do this insteadInclude the alpha byte, for example by OR-ing with 0xFF000000 for a fully opaque pixel.
Common mistake Masking the wrong number of bits, such as using 0xF instead of 0xFF, which keeps only four bits and loses part of the component value
Try a Pixel Change
A pixel at x = 0, y = 0 has the packed value 0xFF00FF00. Split it into red, green, and blue, then write back a version with the green value set to 0 while red and blue stay the same. Explain each step, including the bounds check you would use before reading the pixel.
Hints
- Use the shift and mask pattern from the extraction section to find each component first.
- Green sits in bits 8 through 15, so shift it left by 8 when you rebuild the int.
- Remember to keep the alpha byte, and check x and y against
getWidthandgetHeightbefore the read.
Key Takeaways
- A BufferedImage lets you read and write individual pixels. The call
getRGB(x,y)returns one int that encodes the pixel's color, and setRGB writes a packed int back. - Valid x and y values must lie inside the image, or Java throws an exception.
- Red, green, and blue are packed into one int as bytes. Shifting and masking with 0xFF extracts each component as a value from 0 to 255.
- Shifting each component into its byte position and combining them with the bitwise OR rebuilds the packed int, which is then written with setRGB.
- You can work with the raw packed int or wrap it in a Color object and convert back with
c.getRGB(). Both routes produce the same kind of packed value.