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 beused.
      *            </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); // 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 [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