Concepts / Using the Color Class in Java

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.

  • Programming

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?

  • 0

  • 127

  • 255

Reveal answer
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.

java
Notes on the code
  • 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.

java
Notes on the code
  • getWidth and getHeight give 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.

Alpha bytebits 31 to 24Red bytebits 23 to 16Green bytebits 15 to 8Blue bytebits 7 to 0
Where does each color channel sit inside the 32-bit int, and which bits belong to alpha, red, green, and blue?

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.

Packed int32 bits from getRGBShift right 16red byte moves to bits 7 to0AND with 0xFFkeeps only the lowest 8bitsRed value0 to 255shiftmaskresult
After shifting right by 16 and masking with 0xFF, which bits of the original int end up as the red value?
java
Notes on the code
  • 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.
Worked example

Splitting one pixel into its components

A pixel's packed value is 0xFF1080F0. Find its red, green, and blue values.

  1. 1
    Find red

    0xFF1080F0 shifted right by 16 bits becomes 0xFF10. Masking with 0xFF keeps 0x10, which is 16 in decimal. Red is 16.

  2. 2
    Find green

    0xFF1080F0 shifted right by 8 bits becomes 0xFF1080. Masking with 0xFF keeps 0x80, which is 128 in decimal. Green is 128.

  3. 3
    Find blue

    0xFF1080F0 masked with 0xFF keeps 0xF0, which is 240 in decimal. Blue is 240.

Result

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.

java
Notes on the code
  • 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.
Before
int rgb = image.getRGB(2, 1);
int red = (rgb >> 16) & 0xFF;

Red is 16 and the packed value still holds the original red bits.

After
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.

getRGB(x, y)read packed intSplit into R G Bshifts and masksChange redred set to 200Combine with ORshifts then ORsetRGB(x, y, packed)write backpacked intcomponentsnew rednew packed int
In what order is the pixel value read, split into components, changed, recombined, and written back to the BufferedImage?
java
Output
16 128 240
ffc880f0
Notes on the code
  • 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.

FeaturePacked intColor object
What it isA single int that holds the alpha, red, green, and blue bytesA Java object that wraps a color
How it is obtainedReturned directly by image.getRGB(x, y)Built from an int with new Color(rgb)
How it goes back to the imagePassed directly to setRGBConverted back to an int with c.getRGB(), then passed to setRGB
Best used forBit-level work with shifts, masks, and ORWorking 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 like
    image.getRGB(10, 0) on a 4 by 4 image
    Why it fails

    Valid 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 instead

    Compare 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 like
    int packed = red | green | blue;
    Why it fails

    Without 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 instead

    Shift 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 like
    int packed = (red << 16) | (green << 8) | blue; with the top byte left at zero
    Why it fails

    The three-component rebuild leaves the top byte empty, so the alpha value becomes zero and the pixel can end up transparent.

    Do this instead

    Include 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

MEDIUM

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 getWidth and getHeight before 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.