Skip to main content

[JAVA2D] Graphics and Grayscale Images - Maximum luninance is 252 ?

2 replies [Last post]
Anonymous

Hi everybody,

I am working with 8 bit Grayscale images and am in the process of
implementing a rendering interface for an imaging program I work on.

The interface of the "renderer" is quite simple:

import java.awt.image.BufferedImage;

/**
* A renderer is responsible for successfully rendering images to a
* given destination.
*
*/
public interface IImageRenderer {

/**
* Render an image.
*
* @param sourceImage
* the image to render
*
* If a null image is passed, no rendering will be performed.
*
* @param horizontalOffset
* the horizontal offset to apply
* @param verticalOffset
* the vertical offset to apply
*/
public void renderFrame(BufferedImage sourceImage, int horizontalOffset, int
verticalOffset);
}

I came up with the following implementation:

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;

import javax.swing.SwingUtilities ;

import IImageRenderer;

/**
* Render images to a given Graphics2D.
*
* The areas of the graphics not covered by the image will be filled with a
custom background color.
*
*
* @see GraphicsImageRendererTest
*/
public final class GraphicsImageRenderer implements IImageRenderer, Runnable
{

/**
* Build a renderer for a specified Graphics.
*
* @param targetGraphics
* the graphics where the images are to be rendered
* @param backgroundColor
* the background color to use
*
* If null, Color.BLACK will beused.
*
*/
public GraphicsImageRenderer(Graphics2D targetGraphics, Color
backgroundColor) {
if (backgroundColor != null) {
this.currentBackgroundColor = backgroundColor;
} else {
this.currentBackgroundColor = Color.BLACK;
}
if (targetGraphics != null) {
this.destinationGraphics = targetGraphics;
this.destinationGraphics.setBackground(backgroundColor);
} else {
this.destinationGraphics = null;
}
}

/*
* (non-Javadoc)
*
* @see IImageRenderer#renderFrame( java.awt.image.BufferedImage, int, int)
*/
public final void renderFrame(BufferedImage sourceImage, int
horizontalOffset, int verticalOffset) {
this.currentSourceImage = sourceImage;
this.currentHorizontalOffset = horizontalOffset;
this.currentVerticalOffset = verticalOffset;
if (!SwingUtilities.isEventDispatchThread()) {
try {
SwingUtilities.invokeAndWait(this);
} catch (Exception exception) {
// Oops..
}
} else {
run();
}
}

/*
* (non-Javadoc)
*
* @see java.lang.Runnable#run()
*/
public final void run() {
if (SwingUtilities.isEventDispatchThread() && this.destinationGraphics !=
null && this.currentSourceImage != null) {
this.destinationGraphics.clearRect(0, 0, Integer.MAX_VALUE,Integer.MAX_VALUE);
// Ugly...
this.destinationGraphics.drawImage(this.currentSourceImage,
this.currentHorizontalOffset, this.currentVerticalOffset,
this.currentBackgroundColor, null);
}
}

private BufferedImage currentSourceImage = null;

private int currentHorizontalOffset = 0;

private int currentVerticalOffset = 0;

private final Graphics2D destinationGraphics;

private final Color currentBackgroundColor;
}

My first questions are:

1/ Is my handling of the call to drawImage() appropriate or are the "Swing
related" precautions irrelevant (i.e. I should not care about the EDT at
this point) ?

2/ How can I retrieve the dimensions of my Graphics2D in order to avoid
having to call clearRect() on the largest possible values (no laughing
please ;-) ), should I pass them to my constructor (since I will probably
have access to the dimensions when retrieving/creating the graphics) ?

Now for the weird part (at least from my point of view).

I have writtent the following JUnit TestCase to see how the background was
handled:

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.color.ColorSpace ;
import java.awt.image.BufferedImage;

import junit.framework.TestCase;
import GraphicsImageRenderer;

public class GraphicsImageRendererTest extends TestCase {

public static void main(String[] args) {
junit.swingui.TestRunner
.main(new String[] { "GraphicsImageRendererTest" });
}

public GraphicsImageRendererTest(String name) {
super(name);
}

public void testBackgroundColor_colorImagesCustomColor() {
try {
BufferedImage testDestinationImage = new BufferedImage(10, 10,
BufferedImage.TYPE_3BYTE_BGR);
Graphics2D testDestinationGraphics = testDestinationImage.createGraphics();

BufferedImage testSourceImage = new BufferedImage(1, 1,
BufferedImage.TYPE_3BYTE_BGR);

Color customColor = new Color(10, 20, 30);
GraphicsImageRenderer testCustomColorRenderer = new
GraphicsImageRenderer(testDestinationGraphics, customColor);

testCustomColorRenderer.renderFrame(testSourceImage, 0, 0);
assertEquals(customColor.getRed(), testDestinationImage.getData().getSample(9,
9, 0));
assertEquals(customColor.getGreen(),
testDestinationImage.getData().getSample(9,
9, 1));
assertEquals(customColor.getBlue(), testDestinationImage.getData().getSample(9,
9, 2));
} catch (Exception exception) {
fail("Exception: " + exception);
}
}

public void testBackgroundColor_grayScaleImagesMinimumLuminance() {
try {
BufferedImage testDestinationImage = new BufferedImage(10, 10,
BufferedImage.TYPE_BYTE_GRAY);
Graphics2D testDestinationGraphics = testDestinationImage.createGraphics();

BufferedImage testSourceImage = new BufferedImage(1, 1,
BufferedImage.TYPE_BYTE_GRAY);

ColorSpace grayColorSpace = ColorSpace.getInstance(ColorSpace.CS_GRAY);

Color black = new Color(grayColorSpace, new float[] {
grayColorSpace.getMinValue(0) }, 1f);
GraphicsImageRenderer testCustomBlackRenderer = new
GraphicsImageRenderer(testDestinationGraphics, black);

testCustomBlackRenderer.renderFrame(testSourceImage, 0, 0);
assertEquals(0, testDestinationImage.getData().getSample(9, 9, 0));
} catch (Exception exception) {
fail("Exception: " + exception);
}
}

public void testBackgroundColor_grayScaleImagesMaximumLuminance() {
try {
BufferedImage testDestinationImage = new BufferedImage(10, 10,
BufferedImage.TYPE_BYTE_GRAY);
Graphics2D testDestinationGraphics = testDestinationImage.createGraphics();

BufferedImage testSourceImage = new BufferedImage(1, 1,
BufferedImage.TYPE_BYTE_GRAY);

ColorSpace grayColorSpace = ColorSpace.getInstance(ColorSpace.CS_GRAY);

Color white = new Color(grayColorSpace, new float[] {
grayColorSpace.getMaxValue(0) }, 1f);
GraphicsImageRenderer testCustomWhiteRenderer = new
GraphicsImageRenderer(testDestinationGraphics, white);

testCustomWhiteRenderer.renderFrame(testSourceImage, 0, 0);
assertEquals(255, testDestinationImage.getData().getSample(9, 9, 0));
} catch (Exception exception) {
fail("Exception: " + exception);
}
}

}

The first two tests pass smoothly but for the last one I get a failure on
the "white color" test:
junit.framework.AssertionFailedError: expected:<255> but was:<252>

Why are the luminance values only covering the [0..252] range ?

Any extra comment on what I am doing wrong (Java2D, Code conventions, OO,
design...) is more that welcome.

Yours,

D.

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".
[att1.html]

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
Jerry Evans

> The first two tests pass smoothly but for the last one I get a failure
> on the "white color" test:
> junit.framework.AssertionFailedError: expected:<255> but was:<252>
>
> Why are the luminance values only covering the [0..252] range ?

It looks like you're hitting a precision issue in the particular Color
constructor you're
using. If you use the static Color.WHITE from the Color class, you
should be
able to avoid this problem, as the following code shows:

% more Test.java
import java.awt.Color;
import java.awt.color.ColorSpace;

public class Test {

public static void main(String argv[]) {
ColorSpace gcs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
Color white = new Color(gcs, new float[] {gcs.getMaxValue(0)}, 1f);
System.out.println("sRGB components for constructed white:");
System.out.println("Red: " + white.getRed());
System.out.println("Green: " + white.getGreen());
System.out.println("Blue: " + white.getBlue());
System.out.println("Alpha: " + white.getAlpha());
System.out.println("");
System.out.println("sRGB components for Color.WHITE:");
System.out.println("Red: " + Color.WHITE.getRed());
System.out.println("Green: " + Color.WHITE.getGreen());
System.out.println("Blue: " + Color.WHITE.getBlue());
System.out.println("Alpha: " + Color.WHITE.getAlpha());
}
}
%java Test
sRGB components for constructed white:
Red: 252
Green: 254
Blue: 254
Alpha: 255

sRGB components for Color.WHITE:
Red: 255
Green: 255
Blue: 255
Alpha: 255
%

Jerry

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

Java Questions

Thanks!

I wasn't sure about using Color.WHITE with grayscale images (noob paranoïa I
guess :-) ).

D.

2005/7/20, Jerry Evans :
>
> > The first two tests pass smoothly but for the last one I get a failure
> > on the "white color" test:
> > junit.framework.AssertionFailedError: expected:<255> but was:<252>
> >
> > Why are the luminance values only covering the [0..252] range ?
>
>
> It looks like you're hitting a precision issue in the particular Color
> constructor you're
> using. If you use the static Color.WHITE from the Color class, you
> should be
> able to avoid this problem, as the following code shows:
>
> % more Test.java
> import java.awt.Color;
> import java.awt.color.ColorSpace;
>
>
> public class Test {
>
> public static void main(String argv[]) {
> ColorSpace gcs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
> Color white = new Color(gcs, new float[] {gcs.getMaxValue(0)}, 1f);
> System.out.println("sRGB components for constructed white:");
> System.out.println("Red: " + white.getRed());
> System.out.println("Green: " + white.getGreen());
> System.out.println("Blue: " + white.getBlue());
> System.out.println("Alpha: " + white.getAlpha());
> System.out.println("");
> System.out.println("sRGB components for Color.WHITE:");
> System.out.println("Red: " + Color.WHITE.getRed());
> System.out.println("Green: " + Color.WHITE.getGreen());
> System.out.println("Blue: " + Color.WHITE.getBlue());
> System.out.println("Alpha: " + Color.WHITE.getAlpha());
> }
> }
> %java Test
> sRGB components for constructed white:
> Red: 252
> Green: 254
> Blue: 254
> Alpha: 255
>
> sRGB components for Color.WHITE:
> Red: 255
> Green: 255
> Blue: 255
> Alpha: 255
> %
>
>
> Jerry
>
>
>
>
>

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".
[att1.html]