Graphics and Grayscale Images - Maximum luninance is 252 ?

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 <i>renderer</i> is responsible for successfully rendering images to a
 * given <i>destination</i>.
 *
 */
public interface IImageRenderer {

    /**
     * Render an image.
     *
     * @param sourceImage
     *            the image to render
     *            <p>
     *            If a <code>null</code> image is passed, no rendering will be
     *            performed.
     *            </p>
     * @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 <code>Graphics2D</code>.
 * <p>
 * The areas of the graphics not covered by the image will be filled with a
 * custom background color.
 * </p>
 *
 * @see GraphicsImageRendererTest
 */
public final class GraphicsImageRenderer implements IImageRenderer, Runnable {

    /**
     * Build a renderer for a specified <code>Graphics</code>.
     *
     * @param targetGraphics
     *            the graphics where the images are to be rendered
     * @param backgroundColor
     *            the background color to use
     *            <p>
     *            If <code>null</code>, <code>Color.BLACK</code> will be
     *            used.
     *            </p>
     */
    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);
            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 [EMAIL PROTECTED] and include in the body of the message "signoff JAVA2D-INTEREST". For general help, send email to [EMAIL PROTECTED] and include in the body of the message "help".

Reply via email to