BufferedImage getScaledInstance changes brightness of picture - java

I use this piece of code to draw a Image into a Graphics component. It should size the image to the maximum space available, if it is big enough:
// getWidth() = component width, image.getWidth() = image width
double w = getWidth() * 1.0 / image.getWidth();
double h = getHeight() * 1.0 / image.getHeight();
if (w < 1 || h < 1) {
double d = Math.min(Math.min(w, h), 1);
g.drawImage(bi.getScaledInstance((int) (d * image.getWidth()), (int) (d * image.getHeight()), Image.SCALE_REPLICATE), 0, 0, null);
} else {
g.drawImage(bi, 0, 0, null);
}
The code works, the image gets scaled correctly. But unfortunately when the image is scaled, the brightness of the images changes as well on the Graphics!
Has anyone an idea where this could come from?
I attached the scaled (first) and the not scaled version of the screen.
I hope someone can help me with that!
Cheers!
Sebastian

This seems to be an issue with Image.getScaledInstance (and may also be related to gray scaled images). I tried several other hints but had the same result.
Instead, I employed my own scaling algorithm (which I stole from the Internet), which uses a divide and conquer approach, which generally produces better results...
So, original ontop, Image#getScaledInstance on the left, custom scaled on the right
nb: This uses some of my own personal library code, so it might not be entirely suitable for you, but this presents the basics...
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class ImageScaleTest {
public static void main(String[] args) {
new ImageScaleTest();
}
public ImageScaleTest() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static enum RenderQuality {
High,
Medium,
Low
}
public class TestPane extends JPanel {
private BufferedImage original;
private BufferedImage scaled2;
private Image scaled;
public TestPane() {
try {
original = ImageIO.read(new File("/path/to/image"));
scaled = original.getScaledInstance(original.getWidth() / 2, original.getHeight() / 2, Image.SCALE_DEFAULT);
scaled2 = getScaledInstance(original, 0.5d, RenderQuality.High);
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 600);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(original, 0, 0, this);
g2d.drawImage(scaled, 0, original.getHeight(), this);
g2d.drawImage(scaled2, scaled.getWidth(this), original.getHeight(), this);
g2d.dispose();
}
}
public static BufferedImage getScaledInstance(BufferedImage img, double dScaleFactor, RenderQuality quality) {
BufferedImage imgBuffer = null;
if (quality == RenderQuality.High) {
// System.out.println("Scale high quality...");
imgBuffer = getScaledInstance(img, dScaleFactor, RenderingHints.VALUE_INTERPOLATION_BILINEAR, true);
} else if (quality == RenderQuality.Medium) {
imgBuffer = getScaledInstance(img, dScaleFactor, RenderingHints.VALUE_INTERPOLATION_BILINEAR, false);
} else {
// System.out.println("Scale low quality...");
imgBuffer = getScaledInstance(img, dScaleFactor, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR, false);
}
return imgBuffer;
}
protected static BufferedImage getScaledInstance(BufferedImage img, double dScaleFactor, Object hint, boolean bHighQuality) {
BufferedImage imgScale = img;
int iImageWidth = (int) Math.round(img.getWidth() * dScaleFactor);
int iImageHeight = (int) Math.round(img.getHeight() * dScaleFactor);
if (dScaleFactor <= 1.0d) {
imgScale = getScaledDownInstance(img, iImageWidth, iImageHeight, hint, bHighQuality);
} else {
imgScale = getScaledUpInstance(img, iImageWidth, iImageHeight, hint, bHighQuality);
}
return imgScale;
}
protected static BufferedImage getScaledDownInstance(BufferedImage img,
int targetWidth,
int targetHeight,
Object hint,
boolean higherQuality) {
int type = (img.getTransparency() == Transparency.OPAQUE)
? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
BufferedImage ret = (BufferedImage) img;
if (targetHeight > 0 || targetWidth > 0) {
int w, h;
if (higherQuality) {
// Use multi-step technique: start with original size, then
// scale down in multiple passes with drawImage()
// until the target size is reached
w = img.getWidth();
h = img.getHeight();
} else {
// Use one-step technique: scale directly from original
// size to target size with a single drawImage() call
w = targetWidth;
h = targetHeight;
}
do {
if (higherQuality && w > targetWidth) {
w /= 2;
if (w < targetWidth) {
w = targetWidth;
}
}
if (higherQuality && h > targetHeight) {
h /= 2;
if (h < targetHeight) {
h = targetHeight;
}
}
BufferedImage tmp = new BufferedImage(Math.max(w, 1), Math.max(h, 1), type);
Graphics2D g2 = tmp.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
g2.drawImage(ret, 0, 0, w, h, null);
g2.dispose();
ret = tmp;
} while (w != targetWidth || h != targetHeight);
} else {
ret = new BufferedImage(1, 1, type);
}
return ret;
}
protected static BufferedImage getScaledUpInstance(BufferedImage img,
int targetWidth,
int targetHeight,
Object hint,
boolean higherQuality) {
int type = BufferedImage.TYPE_INT_ARGB;
BufferedImage ret = (BufferedImage) img;
int w, h;
if (higherQuality) {
// Use multi-step technique: start with original size, then
// scale down in multiple passes with drawImage()
// until the target size is reached
w = img.getWidth();
h = img.getHeight();
} else {
// Use one-step technique: scale directly from original
// size to target size with a single drawImage() call
w = targetWidth;
h = targetHeight;
}
do {
if (higherQuality && w < targetWidth) {
w *= 2;
if (w > targetWidth) {
w = targetWidth;
}
}
if (higherQuality && h < targetHeight) {
h *= 2;
if (h > targetHeight) {
h = targetHeight;
}
}
BufferedImage tmp = new BufferedImage(w, h, type);
Graphics2D g2 = tmp.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
g2.drawImage(ret, 0, 0, w, h, null);
g2.dispose();
ret = tmp;
tmp = null;
} while (w != targetWidth || h != targetHeight);
return ret;
}
}
May, also, like to take a look at The Perils of Image.getScaledInstance()
ps- I did a quick bit of searching and this seems to be a bug (or feature) in the API

Related

Resize Buffered Image without creating new instance (java)

I was wondering if there is a way to resize a BufferedImage without creating a new instance of another image. I am wondering this because I think that it will be inefficient to create a new image each time I want to resize a BufferedImage for my application. Here is some code I've seen that explains what I don't want:
public static BufferedImage resize(BufferedImage img, int newW, int newH) {
Image tmp = img.getScaledInstance(newW, newH, Image.SCALE_SMOOTH);
BufferedImage dimg = new BufferedImage(newW, newH, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = dimg.createGraphics();
g2d.drawImage(tmp, 0, 0, null);
g2d.dispose();
return dimg;
}
public static BufferedImage scale(BufferedImage src, int w, int h)
{
BufferedImage img =
new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
int x, y;
int ww = src.getWidth();
int hh = src.getHeight();
int[] ys = new int[h];
for (y = 0; y < h; y++)
ys[y] = y * hh / h;
for (x = 0; x < w; x++) {
int newX = x * ww / w;
for (y = 0; y < h; y++) {
int col = src.getRGB(newX, ys[y]);
img.setRGB(x, y, col);
}
}
return img;
}
private BufferedImage resize(BufferedImage src, int targetSize) {
if (targetSize <= 0) {
return src; //this can't be resized
}
int targetWidth = targetSize;
int targetHeight = targetSize;
float ratio = ((float) src.getHeight() / (float) src.getWidth());
if (ratio <= 1) { //square or landscape-oriented image
targetHeight = (int) Math.ceil((float) targetWidth * ratio);
} else { //portrait image
targetWidth = Math.round((float) targetHeight / ratio);
}
BufferedImage bi = new BufferedImage(targetWidth, targetHeight, src.getTransparency() == Transparency.OPAQUE ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = bi.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); //produces a balanced resizing (fast and decent quality)
g2d.drawImage(src, 0, 0, targetWidth, targetHeight, null);
g2d.dispose();
return bi;
}
Thank you for any responses!

Where to/ how to call my resizeimage method

Ok so I created a method that resizes an ImageIcon my question is how/ where do I call it in order to make the ImageIcon I want to resize to that size here it is thanks, and the method should work so anyone looking for a method that does resize you should be able to use it :)
public static void resizeIcon(ImageIcon icon, int Width, int Height){
Image geticon = icon.getImage();
BufferedImage bi = new BufferedImage(geticon.getWidth(null), geticon.getHeight(null), BufferedImage.TYPE_INT_RGB);
Graphics g = bi.createGraphics();
g.drawImage(geticon, 0, 0, Width, Height, null);
ImageIcon resizedicon = new ImageIcon(bi);
icon = resizedicon;
}
you save the resized image first .Then set the imageicon by resource as the resized image.
public static Boolean resizeImage(String sourceImage, String destinationImage, Integer Width, Integer Height) {
BufferedImage origImage;
try {
origImage = ImageIO.read(new File(sourceImage));
int type = origImage.getType() == 0? BufferedImage.TYPE_INT_ARGB : origImage.getType();
//*Special* if the width or height is 0 use image src dimensions
if (Width == 0) {
Width = origImage.getWidth();
}
if (Height == 0) {
Height = origImage.getHeight();
}
int fHeight = Height;
int fWidth = Width;
//Work out the resized width/height
if (origImage.getHeight() > Height || origImage.getWidth() > Width) {
fHeight = Height;
int wid = Width;
float sum = (float)origImage.getWidth() / (float)origImage.getHeight();
fWidth = Math.round(fHeight * sum);
if (fWidth > wid) {
//rezise again for the width this time
fHeight = Math.round(wid/sum);
fWidth = wid;
}
}
BufferedImage resizedImage = new BufferedImage(fWidth, fHeight, type);
Graphics2D g = resizedImage.createGraphics();
g.setComposite(AlphaComposite.Src);
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.drawImage(origImage, 0, 0, fWidth, fHeight, null);
g.dispose();
ImageIO.write(resizedImage, "png", new File(destinationImage));
} catch (IOException ex) {
System.out.println(""+ex);
return false;
}
return true;
}
Then call
ImageIcon ico = new ImageIcon(destinationImage);
labelforIcon.setIcon(ico);

Rotating image on top of another image

I have a method that I want to rotate an image when the user enters the number of degrees to rotate it. The method does that, but the thing is it's rotating the image so that the new image lays on top of the old one (which I don't want it to do; I just want the new image by itself). The RotateTest is just an abbreviated form of the class with all of the methods needed in the ButtonListener class that should be relevant to rotating the image.
public class RotateTest {
public Rotate() {
try {
String path = "default.jpg";
File imageFile = new File(path);
imageURL = imageFile.toURI().toURL();
image = ImageIO.read(imageURL);
this.imageLabel = new JLabel(imageLabel);
} catch (IOException e) { }
}
public void setAngle(double angle) {
this.angle = angle;
}
public void setImage(BufferedImage img) {
this.image = img;
}
private class ButtonListener implements ActionListener {
public void actionPerformed(ActionEvent event) {
boolean doAgain = true;
Artsy artsy = new Artsy();
if(event.getSource() == rotate) {
//need something for cancel!
do {
String angleString = JOptionPane.showInputDialog("Please enter your angle in degrees.");
double angle = Double.parseDouble(angleString);
if(angle < 0) {
angle = 360 + angle;
}
setAngle(getAngle() + angle);
setImage(artsy.doRotate(image, angle));
revalidate();
repaint();
System.out.println("The angle is " + getAngle());
} while(JOptionPane.OK_OPTION == 1);
}
else {
if(doAgain) {
setImage(artsy.doRotate(image, 360 - getAngle()));
doAgain = false;
setAngle(0);
}
revalidate();
repaint();
System.out.println("The angle is " + getAngle());
}
}
}
And this is the other class with the method that rotates the image:
public class Artsy {
public BufferedImage doRotate(BufferedImage src, double angle) {
angle = Math.toRadians(angle);
Graphics2D g = (Graphics2D) src.getGraphics();
int w = src.getWidth();
int h = src.getHeight();
AffineTransform trans = new AffineTransform();
trans.rotate(angle, w / 2, h / 2);
AffineTransformOp scaleOp = new AffineTransformOp(trans, AffineTransformOp.TYPE_BILINEAR);
g.drawImage(scaleOp.filter(src, null), 0, 0, null);
g.dispose();
return src;
}
}
Thank you!!
Your code seems correct to me, i can't see an obvious mistake. Nevertheless the following function is working for my to rotate images so it should also be a solution for you:
public static BufferedImage rotate(BufferedImage srcImage, double angle)
{
double sin = Math.abs(Math.sin(Math.toRadians(angle))), cos = Math.abs(Math.cos(Math.toRadians(angle)));
int originWidth = srcImage.getWidth(), originHeight = srcImage.getHeight();
int newWidth = (int) Math.floor(originWidth * cos + originHeight * sin), newHeight = (int) Math.floor(originHeight * cos + originWidth * sin);
BufferedImage newImage = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = newImage.createGraphics();
g.translate((newWidth - originWidth) / 2, (newHeight - originHeight) / 2);
g.rotate(Math.toRadians(angle), originWidth / 2, originHeight / 2);
g.drawImage(srcImage, 0, 0, null);
g.dispose();
return newImage;
}
A helper function:
/**
* Converts an Icon to an Image
*/
public static Image iconToImage(Icon icon) {
if (icon instanceof ImageIcon) {
return ((ImageIcon) icon).getImage();
}
else {
int w = icon.getIconWidth();
int h = icon.getIconHeight();
GraphicsEnvironment ge =
GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gd = ge.getDefaultScreenDevice();
GraphicsConfiguration gc = gd.getDefaultConfiguration();
BufferedImage image = gc.createCompatibleImage(w, h);
Graphics2D g = image.createGraphics();
icon.paintIcon(null, g, 0, 0);
g.dispose();
return image;
}
}
Example usage to rotate the JLabels icon for 90 degrees clockwise:
BufferedImage buImg = new BufferedImage(imageLabel.getIcon().getIconWidth(), imageLabel.getIcon().getIconHeight(), BufferedImage.TYPE_INT_ARGB);
buImg.getGraphics().drawImage(iconToImage(imageLabel.getIcon()), 0, 0, null);
imageLabel.setIcon(new ImageIcon(rotate(buImg, 90)));

Java Partition Surface into Little Squares

I would like to know if there is any algorithm that does something like this:
Given a specific surface it divides it into smaller rectangles of the same size.
Something like this example figure:
The grey area is the surface, and the red squares is the partition itself.
I am thinking if there is a optimized way to do this.
A very bad approach would be a for loop in all the pixels and check if there is a rectangle for that specific spot, if not, would create a rectangle, and so on..
Maybe someone knows a algorithm already done? or a better solution?
Thanks alot in advance ;)
Here's one way to go about it.
Create a mask of the image. (I just used Photoshop)
Steal AndrewThompson's code for Creating an Area from an Image and use it to create an Area of the image.
Area imageArea = getOutline(Color.BLACK, imageMask);
Create a grid Rectangle2D objects for the entirety of the image.
Rectangle2D[][] grid = new Rectangle2D[rows][cols];
for (int i = 0; i < grid.length; i++) {
int y = i * CELL_SIZE;
for (int j = 0; j < grid[i].length; j++) {
int x = j * CELL_SIZE;
grid[i][j] = new Rectangle2D.Double(x, y, cellSize, cellSize);
}
}
Once you have the grid, you can just traverse the Rectangle2D objects and check if the Area.contains each individual Rectangle2D in the grid, and you can just add it to a List<Rectangle2D>. Only rectangles contained in the area will be added, giving you your final grid of rectangles to draw. In the example below, I just painted to rectangles as a visual.
for (Rectangle2D[] rects : imageGrid) {
for (Rectangle2D rect : rects) {
if (imageArea.contains(rect)) {
g2.drawRect((int) rect.getX(), (int) rect.getY(),
(int) rect.getWidth(), (int) rect.getHeight());
}
}
}
Full example
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Area;
import java.awt.geom.GeneralPath;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class SquaresInArea extends JPanel {
private static final int CELL_SIZE = 30;
BufferedImage image;
BufferedImage imageMask;
Area imageArea;
Rectangle2D[][] imageGrid;
public SquaresInArea() {
try {
image = ImageIO.read(getClass().getResource("/resources/floorplan.png"));
imageMask = ImageIO.read(getClass().getResource("/resources/floorplan-black.png"));
} catch (IOException ex) {
Logger.getLogger(SquaresInArea.class.getName()).log(Level.SEVERE, null, ex);
}
imageArea = getOutline(Color.BLACK, imageMask);
imageGrid = createGrid();
}
private Rectangle2D[][] createGrid() {
int width = image.getWidth();
int height = image.getHeight();
int rows = height / CELL_SIZE;
int cols = width / CELL_SIZE;
Rectangle2D[][] grid = new Rectangle2D[rows][cols];
for (int i = 0; i < grid.length; i++) {
int y = i * CELL_SIZE;
for (int j = 0; j < grid[i].length; j++) {
int x = j * CELL_SIZE;
grid[i][j] = new Rectangle2D.Double(x, y, CELL_SIZE, CELL_SIZE);
}
}
return grid;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.drawImage(image, 0, 0, this);
g2.setColor(Color.YELLOW);
g2.setStroke(new BasicStroke(3f));
for (Rectangle2D[] rects : imageGrid) {
for (Rectangle2D rect : rects) {
if (imageArea.contains(rect)) {
g2.drawRect((int) rect.getX(), (int) rect.getY(),
(int) rect.getWidth(), (int) rect.getHeight());
}
}
}
}
#Override
public Dimension getPreferredSize() {
return image == null ? new Dimension(300, 300)
: new Dimension(image.getWidth(), image.getHeight());
}
private Area getOutline(Color target, BufferedImage bi) {
// construct the GeneralPath
GeneralPath gp = new GeneralPath();
boolean cont = false;
int targetRGB = target.getRGB();
for (int xx = 0; xx < bi.getWidth(); xx++) {
for (int yy = 0; yy < bi.getHeight(); yy++) {
if (bi.getRGB(xx, yy) == targetRGB) {
if (cont) {
gp.lineTo(xx, yy);
gp.lineTo(xx, yy + 1);
gp.lineTo(xx + 1, yy + 1);
gp.lineTo(xx + 1, yy);
gp.lineTo(xx, yy);
} else {
gp.moveTo(xx, yy);
}
cont = true;
} else {
cont = false;
}
}
cont = false;
}
gp.closePath();
// construct the Area from the GP & return it
return new Area(gp);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.add(new SquaresInArea());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
here's another view for clarity
private final BasicStroke thin = new BasicStroke(1f);
private final BasicStroke thick = new BasicStroke(4f);
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.drawImage(image, 0, 0, this);
for (Rectangle2D[] rects : imageGrid) {
for (Rectangle2D rect : rects) {
if (imageArea.contains(rect)) {
g2.setStroke(thick);
g2.setColor(Color.GREEN);
g2.draw(rect);
} else {
g2.setStroke(thin);
g2.setColor(Color.RED);
g2.draw(rect);
}
}
}
}
Do you just want to fill it with squares - or do you want to fill it with the optimal number of squares?
An algorithm for the second is harder.
For the first just step through the image a square-size at a time. If the pixel at that point is filled then scan the full square, if it's all filled in then draw the square. If not then step to the next point.
i.e. if squares are 10*10 pixels:
for (int x=0;x<width;x+=SQUARE_SIZE) {
for (int y=0;y<height;y+=SQUARE_SIZE) {
// Now check if you can put a valid square here, if so draw it
}
}

Using PrinterJob to print an Image (Graphics2D)

Is there a way I can rig a PrinterJob in Java to NOT actually print to a printer so that I can get the graphics objects for each page? I tried setting the PrintService to null, but Java wouldn't allow that.
This is so that I can retrieve an accurate Print Preview for the document without essentially rebuilding PrinterJobs functions from the ground-up in a different context.
Here's the code for the print function in my program:
public int print(Graphics graphics, PageFormat pageFormat, int page) throws PrinterException {
deepCopyString = string;
FontMetrics metrics = graphics.getFontMetrics(font);
int lineHeight = metrics.getHeight();
arrangePage(graphics, pageFormat, metrics);
if (page > pageBreaks.length){
return NO_SUCH_PAGE;
}
Graphics2D g = (Graphics2D) graphics;
g.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
g.setFont(font);
int begin = (page == 0) ? 0 : pageBreaks[page-1];
int end = (page == pageBreaks.length) ? lines.length : pageBreaks[page];
int y = 0;
int x = 0;
for (int line = begin; line < end; line++){
x = 0;
y += lineHeight;
checkSyntax(line);
String l = lines[line];
for (int c = 0; c < l.length(); c++){
applySyntax(c, line);
metrics = graphics.getFontMetrics(font);
String ch = Character.toString(l.charAt(c));
g.setFont(font);
g.drawString(ch, x, y);
x += metrics.charWidth(l.charAt(c));
//System.out.println(c + "/"+l.length());
}
//g.drawString(lines[line], 0, y);
}
reset();
records.add(g);
return PAGE_EXISTS;
}
You can already see that the Graphics objects are recorded so that I can paint them in another component, but it's rather useless seeing as it will go ahead and send these to my printer before the record can be completed.
This may be a bad idea in general, and I'm pretty new to printing. If this is seriously a bad way to go about this, feel free to direct me to a source that'll explain a better way.
Basically, you want to create you own Graphics context to which you can paint. You also need to construct a PageFormat that can be past to the print method.
public class TestPrint implements Printable {
private BufferedImage background;
public static final float DPI = 72;
public static void main(String[] args) {
new TestPrint();
}
public TestPrint() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
try {
background = ImageIO.read(new File("C:/Users/shane/Dropbox/MegaTokyo/MgkGrl_Yuki_by_fredrin.jpg"));
} catch (IOException ex) {
ex.printStackTrace();
}
float width = cmToPixel(21f, DPI);
float height = cmToPixel(29.7f, DPI);
Paper paper = new Paper();
float margin = cmToPixel(1, DPI);
paper.setImageableArea(margin, margin, width - (margin * 2), height - (margin * 2));
PageFormat pf = new PageFormat();
pf.setPaper(paper);
BufferedImage img = new BufferedImage(Math.round(width), Math.round(height), BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = img.createGraphics();
g2d.setColor(Color.WHITE);
g2d.fill(new Rectangle2D.Float(0, 0, width, height));
try {
g2d.setClip(new Rectangle2D.Double(pf.getImageableX(), pf.getImageableY(), pf.getImageableWidth(), pf.getImageableHeight()));
print(g2d, pf, 0);
} catch (PrinterException ex) {
ex.printStackTrace();
}
g2d.dispose();
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JLabel(new ImageIcon(img)));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public float cmToPixel(float cm, float dpi) {
return (dpi / 2.54f) * cm;
}
public int print(Graphics graphics, PageFormat pageFormat, int page) throws PrinterException {
if (page > 0) {
return NO_SUCH_PAGE;
}
Graphics2D g = (Graphics2D) graphics;
g.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
if (background != null) {
int x = (int)Math.round((pageFormat.getImageableWidth() - background.getWidth()) / 2f);
int y = (int)Math.round((pageFormat.getImageableHeight() - background.getHeight()) / 2f);
g.drawImage(background, x, y, null);
}
g.setColor(Color.BLACK);
g.draw(new Rectangle2D.Double(0, 0, pageFormat.getImageableWidth() - 1, pageFormat.getImageableHeight() - 1));
return PAGE_EXISTS;
}
}
Now, obviously, there are going to be difference to what is printed to the screen and what's printed to the printer, because we're not actually using the same hardware device, but the basic concept applies

Categories