Re: Questions about PBuffers
Posted by Gene on Dec 14, 2011; 10:11am
URL: https://forum.jogamp.org/Questions-about-PBuffers-tp3581441p3585047.html
It's not so complicated. I've appended code to print a BufferedImage to one full page. The printer's Graphics2D works almost exactly like any Component's. However, you'll notice that the smaller you make the buffered image in my code, the fuzzier the printout will be as the Graphic2D smoothing algorithm spreads one image pixel over more printer pixels. So the point of the earlier post is that if you choose to render at a very high resolution to make the printed image high quality, you may not be able to get a buffer big enough to hold the whole image. Producing tiles is just a question of setting OpenGL's viewport to the correct rectangle and re-rendering.
The tiling logic would work something like this:
0. Assume the image is Mi rows and Ni columns and the printer's writable area is Mp by Np.
1. Determine the biggest PBuffer you can get. Say this is Mb by Nb (normally these are equal).
2. Let nRows = ceiling(Mi / Mb), the number of rows of tiles. Similarly, nCols = ceiling(Ni / Nb)
3. Let float Hp = Mp / nRows and Wp = Np / nCols be the height and width of a tile on the printer. Floats limit roundoff error accumulation.
4. Draw as follows:
for (iTile = 0; iTile < nRows; iTile++)
for (jTile = 0; jTile < nCols; jTile++) {
Render into the PBuffer with viewport (jTile * Nb, iTile * Mb, Mb, Nb);
Capture the Pbuffer in a BufferedImage.
DrawImage the BufferedImage to the printer in the rectangle
[jTile * Wp, iTile * Hp, Wp, Hp]
using the example of the included code.
Note that if the PBuffer is big enough to hold the whole image, the loops each execute just once.
Note also that it's possible the Printable will call your print() routine multiple times, even to render one page. It will be doing it's own tiling in this case!
import java.awt.*;
import java.awt.image.*;
import java.awt.print.*;
import javax.print.attribute.*;
import javax.print.attribute.standard.JobName;
import javax.swing.*;
public class Test2 {
public class BufferedImagePrintable implements Printable {
final private BufferedImage image;
public BufferedImagePrintable(BufferedImage image) {
this.image = image;
}
public int print(Graphics graphics, PageFormat pf, int pageIndex) throws PrinterException {
if (pageIndex != 0) {
return NO_SUCH_PAGE;
}
final int x = (int)pf.getImageableX();
final int y = (int)pf.getImageableY();
final int w = (int)pf.getImageableWidth();
final int h = (int)pf.getImageableHeight();
final int s = Math.min(w, h); // maintain aspect
// The image icon constructor is there just to block until scaling is complete.
// There might be a way to do this without allocating the intermediate
// scaled instance, which could be large. Check drawImage(BufferedImage, ... ).
// But then you need a media tracker to block until completion. Printable is not
// a Component and doesn't implement ImageObserver, a disconnect in the API.
new ImageIcon(image.getScaledInstance(s, s, BufferedImage.SCALE_SMOOTH))
.paintIcon(null, graphics, x, y);
return PAGE_EXISTS;
}
}
public void run() {
// Make an image.
final BufferedImage bufferedImage = new BufferedImage(4096, 4096, BufferedImage.TYPE_3BYTE_BGR);
// Draw something. In place of this, you can grab the OpenGL surface.
Graphics2D g = (Graphics2D)bufferedImage.getGraphics();
int x = 0;
int y = 0;
int w = bufferedImage.getWidth();
int h = bufferedImage.getHeight();
g.setColor(Color.white);
g.fillRect(x, y, w, h);
x += 100; y += 100;
w -= 2 * x; h -= 2 * y;
g.setStroke(new BasicStroke(8f));
g.setColor(Color.red);
g.drawRect(x, y, w, h);
g.drawOval(x, y, w, h);
g.setColor(Color.black);
g.drawLine(x, y, x+w, y+h);
g.drawLine(x+h, y, x, y+h);
g.dispose();
PrinterJob job = PrinterJob.getPrinterJob();
job.setPrintable(new BufferedImagePrintable(bufferedImage));
HashPrintRequestAttributeSet attr = new HashPrintRequestAttributeSet();
attr.add(new JobName("BufferedImage", null));
// You can add attributes here to alter the printer dialog.
boolean printAccepted = job.printDialog(attr);
if (printAccepted) {
try {
job.print(attr);
} catch (PrinterException ex) {
JOptionPane.showMessageDialog(null,
"Printing has been canceled!", "Printing halted",
JOptionPane.INFORMATION_MESSAGE);
}
}
}
public static void main (String [] args) {
new Test2().run();
}
}