Bufferedimage Not Being Cleared Before Each Rendering

BufferedImage Sometimes Doesn't render

I had to modify most of the code to get something to work. I'm assuming that this is what you want.

Sample Image

Here are the changes I made.

  1. I added a main method that called SwingUtilities invokeLater to put the Swing components on the Event Dispatch thread.

  2. I split the code into 3 classes, DrawImage, DrawingPanel, and Snippet. DrawImage creates the four images. DrawingPanel draws the four images onto a JPanel. Snippet creates the JFrame and adds the drawing panel to the JFrame.

  3. I defined the size of the drawing panel to hold 4 slots. I packed the JFrame so that
    the JFrame would be the correct size to hold the drawing panel.

  4. I overrode the paintComponent method to draw the four images from the image list. These images were already created in the DrawImage class. I called super.paintComponent to make sure all of the Swing children components were drawn correctly.

  5. I created the images before I created the Swing GUI.

  6. I used a method I created, centerString, to center the text in the images. I left the scale method alone.

Here's the modified code. Unlike yours, it's runnable.

package snippet;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class Snippet implements Runnable {

private JFrame frame;

private List<BufferedImage> imageList;

public Snippet() {
imageList = new ArrayList<BufferedImage>();
new DrawImage().createImages();
}

@Override
public void run() {
frame = new JFrame();
frame.setTitle("ALevelUp 0.0.1 Alpha");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

DrawingPanel p = new DrawingPanel();
frame.add(p);

frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);

System.out.println(frame.getHeight() + "," + frame.getWidth());
}

public static void main(String[] args) {
SwingUtilities.invokeLater(new Snippet());
}

public class DrawingPanel extends JPanel {

private static final long serialVersionUID =
2535522354552193273L;

public DrawingPanel() {
this.setPreferredSize(new Dimension(550, 350));
}

@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);

int x = 50;
int y = 50;

for (int i = 0; i < 2; i++) {
BufferedImage image = null;
for (int j = 0; j < 2; j++) {
image = imageList.get(i * 2 + j);
g.drawImage(image, x, y, this);
x += image.getWidth() + 50;
}
x = 50;
y += image.getHeight() + 50;
}
}
}

public class DrawImage {

public void createImages() {
imageList.add(createImage("Slot 1"));
imageList.add(createImage("Slot 2"));
imageList.add(createImage("Slot 3"));
imageList.add(createImage("Slot 4"));
}

private BufferedImage createImage(String text) {
Rectangle r = new Rectangle(0, 0, 200, 100);
BufferedImage image = new BufferedImage(r.width, r.height,
BufferedImage.TYPE_INT_RGB);

Graphics2D g = (Graphics2D) image.getGraphics();
Font font = g.getFont();
g.setFont(scale(font, g, text, image));
g.setColor(Color.BLACK);
g.fillRect(0, 0, image.getWidth(), image.getHeight());
g.setColor(Color.YELLOW);
centerString(g, r, text, font);
g.dispose();

return image;
}

private Font scale(Font f, Graphics g, String text,
BufferedImage img) {
float ntry = 20.0f;
Font font = null;

while (2 < 3) {
font = f.deriveFont(ntry);
FontMetrics fm = g.getFontMetrics(font);
int width = fm.stringWidth(text);
if (width < img.getWidth()) {
return font;
}
}
}

/**
* This method centers a <code>String</code> in
* a bounding <code>Rectangle</code>.
* @param g - The <code>Graphics</code> instance.
* @param r - The bounding <code>Rectangle</code>.
* @param s - The <code>String</code> to center in the
* bounding rectangle.
* @param font - The display font of the <code>String</code>
*
* @see java.awt.Graphics
* @see java.awt.Rectangle
* @see java.lang.String
*/
private void centerString(Graphics g, Rectangle r, String s,
Font font) {
FontRenderContext frc =
new FontRenderContext(null, true, true);

Rectangle2D r2D = font.getStringBounds(s, frc);
int rWidth = (int) Math.round(r2D.getWidth());
int rHeight = (int) Math.round(r2D.getHeight());
int rX = (int) Math.round(r2D.getX());
int rY = (int) Math.round(r2D.getY());

int a = (r.width / 2) - (rWidth / 2) - rX;
int b = (r.height / 2) - (rHeight / 2) - rY;

g.setFont(font);
g.drawString(s, r.x + a, r.y + b);
}

}

}

Image Quality Loss When Drawing One BufferedImage to Another Using Graphics2D

The problem is this line from the clearLayers() method:

master = new BufferedImage((int)size.getWidth(), (int)size.getHeight(), frames[0].getType());

As the GIF uses a palette, the BufferedImage type will be TYPE_BYTE_INDEXED. However, if you pass this parameter to the BufferedImage constructor, it will use a default IndexColorModel (a built-in, fixed 256 color palette), not the palette from your GIF. Thus, the frames from the GIF will have to be dithered into the destination, as the colors doesn't match.

Instead, use TYPE_INT_RGB/TYPE_INT_ARGB for type, or use the constructor that also takes an IndexColorModel parameter and pass the IndexColorModel from the frames of the GIF.

In code:

master = new BufferedImage((int)size.getWidth(), (int)size.getHeight(), BufferedImage.TYPE_INT_ARGB);

Alternatively, the following should also work if all frames of the GIF uses the same palette (not necessarily the case):

master = new BufferedImage((int)size.getWidth(), (int)size.getHeight(), frames[0].getType(), (IndexColorModel) frames[0].getColorModel());

However, as the OP reports back the latter option doesn't work for him, the first option is probably safer. :-)

Drawing a Component to BufferedImage causes display corruption

Summary: The original JScrollNavigator uses the Swing opacity property to render a convenient green NavBox over a scaled thumbnail of the component in an adjacent JScrollPane. Because it extends JPanel, the (shared) UI delegate's use of opacity conflicts with that of the scrollable component. The images seen in edit 5 above typify the associated rendering artifact, also shown here. The solution is to let NavBox, JScrollNavigator and the scrollable component extend JComponent, as suggested in the second addendum below. Each component can then manage it's own properties individually.

Sample Image

I see no unusual rendering artifact with your code as posted on my platform, Mac OS X, Java 1.6. Sorry, I don't see any glaring portability violations.

image one

A few probably irrelevant, but perhaps useful, observations.

  • Even if you use setSize(), appropriately in this case, you should still pack() the enclosing Window.

    f.pack();
    f.setSize(300, 200);
  • For convenience, add() forwards the component to the content pane.

    f.add(nav, BorderLayout.WEST);
  • Prefer StringBuilder to StringBuffer.

  • Consider ComponentAdapter in place of ComponentListener.

Addendum: As suggested here, I got somewhat more flexible results using RenderingHints instead of getScaledInstance() as shown below. Adding a few icons makes it easier to see the disparate effect on images and text.

image two

editPane.insertIcon(UIManager.getIcon("OptionPane.errorIcon"));
editPane.insertIcon(UIManager.getIcon("OptionPane.warningIcon"));
...
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Component view = jScrollPane.getViewport().getView();
BufferedImage img = new BufferedImage(view.getWidth(),
view.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D off = img.createGraphics();
off.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
off.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BICUBIC);
view.paint(off);
Graphics2D on = (Graphics2D)g;
on.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
on.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BICUBIC);
on.drawImage(img, 0, 0, getWidth(), getHeight(), null);
}

Addendum secundum: It looks like the JPanel UI delegate is not cooperating. One workaround is to extend JComponent so that you can control opacity. It's only slightly more work to manage the backgroundColor. NavBox and JScrollNavigator are also candidates for a similar treatment.

Sample Image

jsp.setViewportView(new JComponent() {

{
setBackground(Color.red);
setBorder(BorderFactory.createLineBorder(Color.BLACK, 16));
}

@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(getBackground());
g.fillRect(0, 0, getWidth(), getHeight());
}

@Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
});

How do I fade a BufferedImage when manipulating it's pixel data?

Your game engine is drawing the image on top of itself in a loop. After drawing it enough many times the effect is the same as not having used transparency at all. The lower the alpha, the longer it takes though: with 50% alpha you need 7 frames to get 99% opacity, with 5% alpha you need about 90 frames.

For example, suppose you are drawing a pixel value of 100 on a screen that's intially 0 (black) with 50% opacity. After the first frame, the output pixel value is .5*100 + .5*0 = 50. The second frame is drawn on top of the first frame, so the output pixel value is .5*100 + .5*50 = 75. The third frame, drawn on top of the second frame, will show .5*100 + .5*75 = 87.5.

To avoid this, you need to fill a solid background color under the image in every frame.



Related Topics



Leave a reply



Submit