EVALUATION
The bug isn't in printing. Its in image code that is used by printing.
The application prepares an image and draws it to the printer.
The image is a 1 bit image in BYTE_BINARY BufferedImage,
with an indexed colormodel where we have color index 0 == WHITE,
and color index 1 = BLACK.
The printing implementation re-draws that into its own copy
of the image using the same size, image type and colormodel
as the original.
This should be a simple blit but it replicates many of the
black pixels on the scan line. The same doesn't happen if the
indexed color model reverses the order of the colours.
The problem is demonstrated by this test program which simply
creates a small image and then re-draws it. The results differ
based on the colour table.
I believe we are tripping over a > 10 year old bug in the color cube initialisation
in dither.c that is used to look up a pixel given an RGB value.
This is used for each pixel in the inner loop for this indexed color model.
Because of the way the color cube is initialised, for RGB=0x0 it assigns pixel value 255.
This is nonsense since there are only pixel values 0 and 1.
The loop however takes that value and ors it into the output, and depending
on where in the output byte we are corruption is variable.
I think the cause of the problem is that the code pays no attention to the cmap_len
parameter (in this case 2) and assumes 256.
ie it grabs the rgb value from cmap[255-i] which could be garbage.
When a color map is in increasing order of pixel and RGB value this bug isn't
likely to be observed since entries in the cube that are already assigned are not overwritten
but reverse order will hit this case.
Here's a simple test case which creates two GIF files, in good.gif a single pixel
is drawn. In bad.gif its replicated.
import java.awt.*;
import java.awt.color.*;
import java.awt.image.*;
import static java.awt.image.BufferedImage.*;
import java.io.*;
import javax.imageio.*;
public class DrawBB {
public static void main(String args[]) throws IOException {
int w = 100, h = 30;
byte[] arr = {(byte)0xff, (byte)0x0};
IndexColorModel newCM = new IndexColorModel(1, 2, arr, arr, arr);
BufferedImage orig = new BufferedImage(w, h, TYPE_BYTE_BINARY, newCM);
Graphics2D g2d = orig.createGraphics();
g2d.setColor(Color.white);
g2d.fillRect(0, 0,w,h);
g2d.setColor(Color.black);
g2d.drawLine(10, 20, 10, 20); // draw a single pixel.
g2d.dispose();
BufferedImage good = new BufferedImage(w, h, TYPE_BYTE_BINARY);
g2d = good.createGraphics();
g2d.drawImage(orig, 0, 0, null);
g2d.dispose();
ImageIO.write(good, "gif", new File("good.gif"));
IndexColorModel origCM = (IndexColorModel)orig.getColorModel();
BufferedImage bad = new BufferedImage(w, h, TYPE_BYTE_BINARY,origCM);
g2d = bad.createGraphics();
g2d.drawImage(orig, 0, 0, null);
g2d.dispose();
ImageIO.write(bad, "gif", new File("bad.gif"));
}
}
|