Skip to main content

BufferedImage from TIFF (through JAI) drawing blocky

1 reply [Last post]
Anonymous

[There is a complete test case below my sig. It requires downloading an image.]

I'm getting blocky/splotchy drawing when translating subregions of a
BufferedImage onto other parts of the same image. This only happens, it seems,
when the BufferedImage is created by PlanarImage.getAsBufferedImage(), when the
PlanarImage instance is read from a particular kind of bilevel, compression
group 4 TIFF file. (The "particular kind" of TIFF file is my way of saying that
some TIFF files don't exhibit this behavior, and others do.)

The test case below requires a particular TIFF file, available from:
http://churn.ath.cx/blank3.tif (I would have attached but don't know if there's
a size limit for this list.) It also requires JAI, to load the TIFF file.

Put the image in the same directory as the java file below, then:

$ javac Aliasing.java
$ java Aliasing

The two lines drawn at (0,150) and (0,300) both should be have weird jagged
edges (big ones) instead of looking like the lines drawn from the top of the frame.

What should I do?

-Denis

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.geom.Line2D;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.awt.image.IndexColorModel;
import java.awt.image.WritableRaster;
import javax.media.jai.JAI;
import javax.media.jai.PlanarImage;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Aliasing extends JPanel {

private static BufferedImage img;

static {
final PlanarImage planar = JAI.create("FileLoad", "blank3.tif");
img = planar.getAsBufferedImage();
assert (img.getColorModel() instanceof IndexColorModel);

final Graphics2D graphics = img.createGraphics();

// Draw a line.
graphics.setColor(Color.black);
graphics.setStroke(new BasicStroke(2f));
graphics.draw(new Line2D.Double(0, 0, 100, 100));

// Get the subimage.
BufferedImage subimage = getSubimage(img, new Rectangle(0, 0, 20, 20));

// Blit that subimage elsewhere.
graphics.drawImage(img,
0, 300, 0+20, 300+20,
0, 0, 20, 20,
null);

// Draw a subimage elsewhere.
graphics.drawImage(subimage,
new AffineTransformOp(new AffineTransform(),
graphics.getRenderingHints()),
0, 150);
}

public Aliasing() {
setPreferredSize(new Dimension(800, 600));
}

@Override
protected void paintComponent(final Graphics g) {
Graphics2D g2 = (Graphics2D) g;

g2.clearRect(0, 0, getWidth(), getHeight());

g2.drawImage(img, new AffineTransformOp(new AffineTransform(),
g2.getRenderingHints()),
0, 0);

g2.setColor(Color.black);
g2.setStroke(new BasicStroke(2f));
g2.draw(new Line2D.Double(50, 0, 150, 100));
g2.drawString("I'm the panel line", 70, 20);
}

@Override
public void update(final Graphics g) {
paintComponent(g);
}

public static BufferedImage getSubimage(BufferedImage image, Rectangle r) {
final BufferedImage ret = new BufferedImage(r.width, r.height,
image.getType());
// Hashtable props = new Hashtable();
// for (String name : Arrays.asList(ret.getPropertyNames())) {
// props.put(name, image.getProperty(name));
// }

Graphics2D retGraphics = ret.createGraphics();

retGraphics.drawImage(image,
0, 0, r.width, r.height,
r.x, r.y, r.x+r.width, r.y+r.height,
null);
retGraphics.dispose();

return ret;
}

private static BufferedImage copyBufferedImage(final BufferedImage source) {
final WritableRaster raster = source.copyData(null);
final BufferedImage copy = new BufferedImage(source.getColorModel(),
raster,

source.isAlphaPremultiplied(), null);
return copy;
}

public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setSize(new Dimension(800, 600));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

Aliasing aliasing = new Aliasing();

frame.add(aliasing);

frame.pack();
frame.setVisible(true);
}

}

===========================================================================
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".

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
kellystevens
Offline
Joined: 2006-08-18
Points: 0

Denis,

Upon examining the image that JAI creates I noticed that the color model is not bilevel for this bilevel image. I added the following code to your class between the line that creates the planar image and the line that fetches the planar image as a buffered image.

if (planar.getColorModel() instanceof IndexColorModel) {

IndexColorModel icm = (IndexColorModel) planar.getColorModel();
int numBands = icm.hasAlpha() ? 4 : 3;
byte[][] data = new byte[numBands][icm.getMapSize()];

icm.getReds(data[0]);
icm.getGreens(data[1]);
icm.getBlues(data[2]);

if (numBands == 4) {
icm.getAlphas(data[3]);
}

planar = JAI.create("lookup", planar, new LookupTableJAI(data));
}

This makes the blocky lines come out smooth and thin.

I've learned that sometimes JAI's tiff codec creates an image with ColorModel of type IndexColorModel and sometimes it creates an image with a ComponentColorModel. It seems that that latter type succeeds on your text code and the former type fails. The above code snippet moves your image from using an IndexColorModel to using a ComponentColorModel.

Cheers!

-Kelly