How to draw 'biohazard' with swing - java

I'm practicing my swing abilities for the upcoming test, and fried gave me idea to draw biohazard sign like this :
alt text http://img62.imageshack.us/img62/8372/lab6b.gif
I could draw the circles with Elipse2D, but then I somehow need to cut those 3 triangles. Any ideas how I can do that ?

You can use Java2D and canvas for this. The things that you may be using are, Circle and Arc. You should have three arcs with 30 degrees.
I tried using simple graphics over the frame.
Here is an attempt
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JComponent;
import javax.swing.JFrame;
public class Biohazard {
public static void main(String[] args) {
new Biohazard();
}
public Biohazard() {
JFrame frame = new JFrame("Biohazard");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new MyComponent());
frame.setSize(260, 280);
frame.setVisible(true);
}
public class MyComponent extends JComponent {
public void paint(Graphics g) {
int height = 120;
int width = 120;
g.setColor(Color.yellow);
g.fillOval(60, 60, height, width);
g.setColor(Color.black);
g.drawOval(60, 60, height, width);
int swivels = 6;
int commonx, commony, commonh, commonw;
for(int i=0;i<swivels;i++){
commonx = commony = 120-i*10;
commonh = commonw = i*20;
g.drawArc(commonx, commony, commonh, commonw, 60 , 60);
g.drawArc(commonx, commony, commonh, commonw, 180 , 60);
g.drawArc(commonx, commony, commonh, commonw, 300 , 60);
}
}
}
}
The original one : source code can be found at http://pastebin.com/HSNFx7Gq

You can use the Arc2D class to draw each line by specifying the start and extent parameters in degrees.

Maybe this is actually quite easy (I'm not sure how the Swing API handles lines). Draw lines coming out from the center to the points on the circumference of a circle, and just skip those portions for line drawing.

Related

Creating diagram for an extremely immense tree using Java

The problem is simple. I want to create a vertical tree of values, where as you descend down the levels, the amount of values gets exponentially larger. Let's say the 1st level has 1 numerical value, the next has 10, then the next has 100, then the next has 1000, and so on. The first level is connected to the 2nd level with the use of lines, and the 2nd to the 3rd, and so on, much like a game tree. These values are also evenly spaced, so let's say you have a JPanel which is 500x500. At a y of 100, you have 4 values, and so to evenly space them out, you would have a value at 100, one at 200, and so on.
I've tried incorporating drawString, and connecting them with the drawLine method, and placing this so called diagram on a JPanel. That is actually quite simple, and it works if you only have as many as about 50~ values in a singular level. However, when you only have a 1600x900 screen, you can't fit 100 values (on the x axis, which is 1600) without having a big jumbled up mess.
I was thinking there could be 2 possible solutions for this.
One is that the JPanel is not limited to a set resolution (a.k.a the size of your screen) and is scrollable. If it was 10000 x 900 then making this gigantic tree diagram would actually be quite simple, and I could easily fit the 100 values with enough space between them for the values to actually be discernable. However, as far as I know, it's not possible.
The second solution is that I write these values into a file, but I'm not sure how to go about this.
Does anyone know, theoretically speaking, what could be the simplest solution for properly displaying a large tree diagram filled with hundreds of values in a single level?
Oracle has a helpful tutorial, Creating a GUI With Swing. Skip the Learning Swing with the NetBeans IDE section. Pay particular attention to the Performing Custom Painting section.
It turns out it's possible to create one 10000 x 900 drawing JPanel. Adjust the JScrollPane preferred size to the size you want to display. The height should be at least 950 pixels to allow room for the horizontal scroll bar.
I created a checkerboard pattern so you can see that the drawing JPanel does scroll.
Here's the complete runnable code.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
public class LargeDrawingJPanel implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new LargeDrawingJPanel());
}
#Override
public void run() {
JFrame frame = new JFrame("Large Drawing JPanel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel drawingPanel = new DrawingPanel();
JScrollPane scrollPane = new JScrollPane(drawingPanel);
scrollPane.setPreferredSize(new Dimension(1400, 950));
frame.add(scrollPane, BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
public DrawingPanel() {
this.setPreferredSize(new Dimension(10000, 900));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
Color[] colors = { Color.RED, Color.BLACK };
int colorIndex = 0;
int x = 0;
int y = 0;
for (int i = 0; i < 100; i++) {
for (int j = 0; j < 9; j++) {
g2d.setColor(colors[colorIndex]);
colorIndex = (colorIndex == 0) ? 1 : 0;
g2d.fillRect(x, y, 100, 100);
y += 100;
}
x += 100;
y = 0;
}
}
}
}

How to make rounded corners of JTabbedPane in Java Swing

I need make a JTabbedPane like this (I made the image in Photoshop):
But in my look and feel (based on TabbedPaneUI: javax.swing.plaf.basic.BasicTabbedPaneUI) looks like this:
How can I do it?
I’ve tried change LAF properties, but I didn't find a solution.
If I use setBorder method the swing make this:
jtabbedpane1.setBorder(BorderFactory.createLineBorder(Color.WHITE, 1, true));
Java changed only the upper left corner as outer border as image above shows.
I need a solution that might use the Paint method on an extended JTabbedPane class, but I really don't know if this is correct or how do this.
I read the tutorial above and tried override paintComponent method in my extended JTabbedPane class, see:
public class MyTabbedPane extends JTabbedPane {
[...]
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.YELLOW);
g.drawRoundRect(getX()-12, getY()-11, getWidth()-4, getHeight()-22, 6, 6);
}
}
The result:
https://i.imgur.com/YLXkVRS.jpg
Rounded corners are actually a boolean argument when instantiating a border, as can be seen here with BorderFactory.
So what we can do is something like this:
pane.setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY, 2, true));
Where "true" refers to rounded corners.
If you are interested in customizing the border further, you will most likely have to paint it yourself, in which case I would look here for a further read.
Edit regarding your code:
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.YELLOW);
g.drawPolyLine(new int[]{getX(), getX() getX() + 12}, new int[]{getY() + 12, getY(), getY()});
g.drawPolyLine(.....); // next corner
g.drawPolyLine(.....); // next corner
}
etc. where you repeat for each corner that you want your L shape at.
Here is the start of an answer.
import javax.swing.*;
import java.awt.Dimension;
import javax.swing.plaf.TabbedPaneUI;
import javax.swing.plaf.metal.MetalTabbedPaneUI;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Insets;
public class Bordered{
public static void main(String[] args){
JFrame frame = new JFrame("border check");
JPanel content = new JPanel();
JTabbedPane tabs = new JTabbedPane();
JPanel one = new JPanel();
one.add(new JLabel("first tab"));
one.setOpaque(true);
one.setBackground(Color.WHITE);
JPanel two = new JPanel();
two.add(new JLabel("second tab"));
tabs.add("one", one);
tabs.add("two", two);
tabs.setUI( new MetalTabbedPaneUI(){
#Override
protected void paintContentBorder(Graphics g, int placement, int selectedIndex){
int width = tabPane.getWidth();
int height = tabPane.getHeight();
Insets insets = tabPane.getInsets();
Insets tabAreaInsets = getTabAreaInsets(placement);
int x = insets.left;
int y = insets.top;
int w = width - insets.right - insets.left;
int h = height - insets.top - insets.bottom;
y += calculateTabAreaHeight(placement, runCount, maxTabHeight);
h -= (y - insets.top);
//g.fillRoundRect(x, y, w, h, 5, 5);
}
});
tabs.setPreferredSize(new Dimension(400, 200));
content.add(tabs);
frame.setContentPane(content);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
Somethings to note, The inner panel the ones holding the jlabel have square corners. I've shown this by making one white. I've taken some of the boundary code from BasicTabbedPaneUI source code.
They really did not make this easy to manage, but looking at the source for the MetalTabbedPaneUI you can see they draw each border as a line, and it would need to be modified to draw a curve at the ends.

Visual Custom Grid

I am new to Swing in Java and am trying to make a visual grid. I have some simple questions as to how to do the following. Here are some characteristics of the grid:
For each square of the grid, I should be having an equivalent index(i,j) using which I can identify the square and place a text.
Edit - (i,j) is the row/column value - the index of the square in the grid.
The diagonals of the squares should be drawn and each of the four divisions of the square should be having a different color.
Any suggestions as to how to do so.
Given row and column you will need to know the number of columns per row. With this information you can simply do (row * columns) + column which will return the index of the square.
For example, with 8 columns per row, a request for
row = 0, column = 4, will return 4
row = 1, column = 4, will return 12
row = 0, column = 0, will return 0
Rendering the sections of the square is more complex and can be achieved in at least two ways. You could use a Polygon which generates a triangle for each section or Shape and simply use Graphics2D#fill to fill it.
This will come down to how you physically render each square...
Take a look at 2D Graphics for more details and this for an example of both.
A Shape would be much easier to rotate and position and would only require you to have a single instance (or a single instance for each square based on your needs), where as you would require at least 4 Polygons or do some fun rotation...
Updated with simple example
All done with rotating triangles...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class DividedSquare {
public static void main(String[] args) {
new DividedSquare();
}
public DividedSquare() {
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 class TestPane extends JPanel {
private TriangleShape baseTriangle;
private Color[] colors;
public TestPane() {
colors = new Color[]{Color.RED, Color.GREEN, Color.BLUE, Color.MAGENTA};
}
#Override
public void invalidate() {
super.invalidate();
baseTriangle = new TriangleShape(
new Point(0, 0),
new Point(getWidth(), 0),
new Point(getWidth() / 2, getHeight() / 2));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
String text[] = new String[]{
"123.123",
"456.789",
"012.315",
"678.921"
};
FontMetrics fm = g2d.getFontMetrics();
double angel = 0;
for (int index = 0; index < 4; index++) {
g2d.setColor(colors[index]);
Path2D rotated = rotate(baseTriangle, angel);
g2d.fill(rotated);
Rectangle bounds = rotated.getBounds();
int x = bounds.x + ((bounds.width - fm.stringWidth(text[0])) / 2);
int y = bounds.y + (((bounds.height - fm.getHeight()) / 2) + fm.getAscent());
g2d.setColor(Color.WHITE);
g2d.drawString(text[index], x, y);
angel += 90;
}
g2d.setColor(Color.BLACK);
g2d.drawLine(0, 0, getWidth(), getHeight());
g2d.drawLine(getWidth(), 0, 0, getHeight());
g2d.dispose();
}
public Path2D rotate(TriangleShape shape, double angel) {
Rectangle bounds = shape.getBounds();
int x = bounds.width / 2;
int y = bounds.width / 2;
return new Path2D.Float(shape, AffineTransform.getRotateInstance(
Math.toRadians(angel),
x,
y));
}
}
public class TriangleShape extends Path2D.Double {
public TriangleShape(Point2D... points) {
moveTo(points[0].getX(), points[0].getY());
lineTo(points[1].getX(), points[1].getY());
lineTo(points[2].getX(), points[2].getY());
closePath();
}
}
}
Yes, see Making a robust, resizable Chess GUI for a GUI that uses a GridLayout to contain JButton objects. The buttons are held in an 8x8 array (the chessboard), while displayed in a 9 row x 9 column grid layout to accommodate the row (8-1) & column (A-H) identifiers of the board.
Buttons will accept an image that might represent the '4 color diagonal' and text. In the chess board I only use images (the chess pieces).
Edit 1
What is the actual difference between the 4 colored areas (besides the color). E.G. is there supposed to be different functionality depending on which of the diagonal areas the user clicks in?
Each of the 4 colored areas in the square has a value from a table based on some algorithm. The color depends upon the value. Currently, it is just used as an indicator. But the user needs to select an initial square in the grid where I place a mark that it is initial.
And the text. Should it be on top of the '4 color diagonal', beside it, below it..?
I also plan to place a text in each of the region indicating that value. Hence, there is one text field to be placed in each of the four partitions of the square.
Text field or label? A label can show text, but a text field makes it possible to copy the text or change it.
I need a text field because I need to change the text during the course of the algorithm.
By 'I' DYM the program, or the user? The program can change text in a label or text field, but the text field is user editable.
I mean the program
OK - Draw the design and text on an image. Use the image as a button icon. The user can select an initial square by clicking the button. If the text changes, generate a new image and set it to the button.
Edit 2
So do you mean that I have to create an image with 4 colors? Would that not be difficult. I would like to change the colors from the program itself. My algorithm would generate the values and at certain points of the algorithm, I would like to see the visualization.
Not if you generate the image at run-time.
How do I generate an image at run-time?
See the answer to Example images for code and mark-up Q&As The first set of Icons..
...
..As well as the Sprite sheet..
..were generated at run-time in Java code. Each set of images links to the question that includes the code that generated them.
I believe you're describing the SetGridLayout feature in swing. If you want a tutorial on how to set up such a window, you can find it here:
http://docs.oracle.com/javase/tutorial/uiswing/layout/grid.html
After reading your question a second time... I think you plan on drawing a grid...
I'd look into the Draw.java library
Maybe you can try to do this adding a jTable object, this object contains methods that can put values in every value i and j respectively like:
jTable1.setValueAt(Value, i, j);
this will allows you to change the value in every cell.

Java - opaque color

i am trying to draw some lines. Problem is about colors. For example. I have several lines of red color, and than i draw one line of blue color (or reversed). And sometimes, that lines those is more, is opaque for that last one.
I tried to make new color and set color with alpha composite 0.7 - for those more lines, and one color i left default - opaque (alpha 1.0). At first i draw more lines, and than last one. But that lines "overwrite" that one. Is there some solution to fix this problem?
I draw that lines on glasspane.
edit: that code is robust, so it is difficult to post it, and it is one part of thesis.
principle is 2 color for example
Color basicColor;
Color similarColor;
than i have paint method and 2 hashmaps as attributes - some points are stored.
i iterate over this map, remember that one point and similar to him, all other connect with
graphics2D.drawLine(x1,y1,x2,y2) and than change color and paint last one line with another color. I am modifying stroke too, to make it more significant.
I hope it will be enough...
edit2:
i have some Point similarPoint than some robust paint method and here is graphics modifying
iterator iterate over list of points' lists.
Point similar = null;
Iterator<Point> secondIterator;
graphics.setColor(colorOfSimilar);
while (iterator.hasNext()) {
Point point = iterator.next();
if (point.equals(similarPoint)) {
similar = similarPoint;
} else {
secondIterator = secondMap.get(point).iterator();
while (secondIterator.hasNext()) {
Point secondPoint = secondIterator.next();
graphics2D.drawLine(point.getX(), point.getY(),
secondPoint.getX(), secondPoint.getY());
}
}
}
if (similar != null) {
secondIterator = secondMap.get(similar);
graphics2D.setColor(hooverColor);
graphics2D.setStroke(new BasicStroke(2.5f));
while (secondIterator.hasNext()) {
Point secondPoint = secondIterator.next();
graphics2D.drawLine(similar.getX(), similar.getY(),
secondPoint.getX(), secondPoint.getY());
}
graphics2D.setColor(colorOfSimilar);
graphics2D.setStroke(new BasicStroke(1.0f));
}
i wrote it in notepad so sorry about some mistakes (i think brackets etc.), but this is mechanism of modifying, around that is other methods for iterate and other, but it is not important. Problem with stroke doesn´t exist, because at first i did it without stroke.
Thanks for any idea.
The result depends on which compositing rule is specified in the graphics context using setComposite(). This utility may be useful in understanding the various modes. It may also help you in preparing an sscce that exhibits the problem you describe.
Addendum: Here's an example that shows how one might use AlphaComposite.Src mode for this.
import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
/** #see http://stackoverflow.com/questions/7823631 */
public class X extends JPanel {
private static final int SIZE = 300;
private static final int INSET = 64;
private static final AlphaComposite OVER_HALF =
AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f);
private boolean src;
public X(boolean src) {
this.src = src;
this.setBackground(Color.lightGray);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(SIZE, SIZE);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
Line2D line1 = new Line2D.Double(INSET, INSET,
getWidth() - INSET, getHeight() - INSET);
Line2D line2 = new Line2D.Double(getWidth() - INSET,
INSET, INSET, getHeight() - INSET);
g2.setStroke(new BasicStroke(64,
BasicStroke.CAP_ROUND,
BasicStroke.JOIN_BEVEL));
g2.setComposite(OVER_HALF);
g2.setColor(Color.red);
g2.draw(line1);
if (src) {
g2.setComposite(AlphaComposite.Src);
}
g2.setColor(Color.blue);
g2.draw(line2);
}
public static void main(String[] args) {
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridLayout(1, 0));
frame.add(new X(false));
frame.add(new X(true));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}

Java: how to do double-buffering in Swing?

EDIT TWO
To prevent snarky comments and one-line answers missing the point: IFF it is as simple as calling setDoubleBuffered(true), then how do I get access to the current offline buffer so that I can start messing with the BufferedImage's underlying pixel databuffer?
I took the time to write a running piece of code (which looks kinda fun too) so I'd really appreciate answers actually answering (what a shock ;) my question and explaining what/how this is working instead of one-liners and snarky comments ;)
Here's a working piece of code that bounces a square across a JFrame. I'd like to know about the various ways that can be used to transform this piece of code so that it uses double-buffering.
Note that the way I clear the screen and redraw the square ain't the most efficient but this is really not what this question is about (in a way, it's better for the sake of this example that it is somewhat slow).
Basically, I need to constantly modify a lot pixels in a BufferedImage (as to have some kind of animation) and I don't want to see the visual artifacts due to single-buffering on screen.
I've got a JLabel whose Icon is an ImageIcon wrapping a BufferedImage. I want to modify that BufferedImage.
What has to be done so that this becomes double-buffered?
I understand that somehow "image 1" will be shown while I'll be drawing on "image 2". But then once I'm done drawing on "image 2", how do I "quickly" replace "image 1" by "image 2"?
Is this something I should be doing manually, like, say, by swapping the JLabel's ImageIcon myself?
Should I be always drawing in the same BufferedImage then do a fast 'blit' of that BufferedImage's pixels in the JLabel's ImageIcon's BufferedImage? (I guess no and I don't see how I could "synch" this with the monitor's "vertical blank line" [or equivalent in flat-screen: I mean, to 'synch' without interfering with the moment the monitor itselfs refreshes its pixels, as to prevent shearing]).
What about the "repaint" orders? Am I suppose to trigger these myself? Which/when exactly should I call repaint() or something else?
The most important requirement is that I should be modifying pixels directly in the images's pixel databuffer.
import javax.swing.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
public class DemosDoubleBuffering extends JFrame {
private static final int WIDTH = 600;
private static final int HEIGHT = 400;
int xs = 3;
int ys = xs;
int x = 0;
int y = 0;
final int r = 80;
final BufferedImage bi1;
public static void main( final String[] args ) {
final DemosDoubleBuffering frame = new DemosDoubleBuffering();
frame.addWindowListener(new WindowAdapter() {
public void windowClosing( WindowEvent e) {
System.exit(0);
}
});
frame.setSize( WIDTH, HEIGHT );
frame.pack();
frame.setVisible( true );
}
public DemosDoubleBuffering() {
super( "Trying to do double buffering" );
final JLabel jl = new JLabel();
bi1 = new BufferedImage( WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB );
final Thread t = new Thread( new Runnable() {
public void run() {
while ( true ) {
move();
drawSquare( bi1 );
jl.repaint();
try {Thread.sleep(10);} catch (InterruptedException e) {}
}
}
});
t.start();
jl.setIcon( new ImageIcon( bi1 ) );
getContentPane().add( jl );
}
private void drawSquare( final BufferedImage bi ) {
final int[] buf = ((DataBufferInt) bi.getRaster().getDataBuffer()).getData();
for (int i = 0; i < buf.length; i++) {
buf[i] = 0xFFFFFFFF; // clearing all white
}
for (int xx = 0; xx < r; xx++) {
for (int yy = 0; yy < r; yy++) {
buf[WIDTH*(yy+y)+xx+x] = 0xFF000000;
}
}
}
private void move() {
if ( !(x + xs >= 0 && x + xs + r < bi1.getWidth()) ) {
xs = -xs;
}
if ( !(y + ys >= 0 && y + ys + r < bi1.getHeight()) ) {
ys = -ys;
}
x += xs;
y += ys;
}
}
EDIT
This is not for a full-screen Java application, but a regular Java application, running in its own (somewhat small) window.
---- Edited to address per pixel setting ----
The item blow addresses double buffering, but there's also an issue on how to get pixels into a BufferedImage.
If you call
WriteableRaster raster = bi.getRaster()
on the BufferedImage it will return a WriteableRaster. From there you can use
int[] pixels = new int[WIDTH*HEIGHT];
// code to set array elements here
raster.setPixel(0, 0, pixels);
Note that you would probably want to optimize the code to not actually create a new array for each rendering. In addition, you would probably want to optimized the array clearing code to not use a for loop.
Arrays.fill(pixels, 0xFFFFFFFF);
would probably outperform your loop setting the background to white.
---- Edited after response ----
The key is in your original setup of the JFrame and inside the run rendering loop.
First you need to tell SWING to stop Rasterizing whenever it wants to; because, you'll be telling it when you're done drawing to the buffered image you want to swap out in full. Do this with JFrame's
setIgnoreRepaint(true);
Then you'll want to create a buffer strategy. Basically it specifies how many buffers you want to use
createBufferStrategy(2);
Now that you tried to create the buffer strategy, you need to grab the BufferStrategy object as you will need it later to switch buffers.
final BufferStrategy bufferStrategy = getBufferStrategy();
Inside your Thread modify the run() loop to contain:
...
move();
drawSqure(bi1);
Graphics g = bufferStrategy.getDrawGraphics();
g.drawImage(bi1, 0, 0, null);
g.dispose();
bufferStrategy.show();
...
The graphics grabbed from the bufferStrategy will be the off-screen Graphics object, when creating triple buffering, it will be the "next" off-screen Graphics object in a round-robin fashion.
The image and the Graphics context are not related in a containment scenario, and you told Swing you'd do the drawing yourself, so you have to draw the image manually. This is not always a bad thing, as you can specify the buffer flipping when the image is fully drawn (and not before).
Disposing of the graphics object is just a good idea as it helps in garbage collection. Showing the bufferStrategy will flip buffers.
While there might have been a misstep somewhere in the above code, this should get you 90% of the way there. Good luck!
---- Original post follows ----
It might seem silly to refer such a question to a javase tutorial, but have you looked into BufferStrategy and BufferCapatbilites?
The main issue I think you are encountering is that you are fooled by the name of the Image. A BufferedImage has nothing to do with double buffering, it has to do with "buffering the data (typically from disk) in memory." As such, you will need two BufferedImages if you wish to have a "double buffered image"; as it is unwise to alter pixels in image which is being shown (it might cause repainting issues).
In your rendering code, you grab the graphics object. If you set up double buffering according to the tutorial above, this means you will grab (by default) the off-screen Graphics object, and all drawing will be off-screen. Then you draw your image (the right one of course) to the off-screen object. Finally, you tell the strategy to show() the buffer, and it will do the replacement of the Graphics context for you.
Generally we use Canvas class which is suitable for animation in Java. Anyhoo, following is how you achieve double buffering:
class CustomCanvas extends Canvas {
private Image dbImage;
private Graphics dbg;
int x_pos, y_pos;
public CustomCanvas () {
}
public void update (Graphics g) {
// initialize buffer
if (dbImage == null) {
dbImage = createImage (this.getSize().width, this.getSize().height);
dbg = dbImage.getGraphics ();
}
// clear screen in background
dbg.setColor (getBackground ());
dbg.fillRect (0, 0, this.getSize().width, this.getSize().height);
// draw elements in background
dbg.setColor (getForeground());
paint (dbg);
// draw image on the screen
g.drawImage (dbImage, 0, 0, this);
}
public void paint (Graphics g)
{
g.setColor (Color.red);
g.fillOval (x_pos - radius, y_pos - radius, 2 * radius, 2 * radius);
}
}
Now you can update the x_pos and y_pos from a thread, followed by the 'repaint' call on the canvas object. The same technique should work on a JPanel as well.
What you want is basically impossible in windowed mode with Swing. There is no support for raster synchronization for window repaints, this is only available in fullscreen mode (and even then may not be supported by all platforms).
Swing components are double-buffered by default, that is they will do all the rendering to an intermediate buffer and that buffer is then finally copied to the screen, avoiding flicker from background clearing and then painting on top of it.
And thats the only strategy that is reasonable well supported on all underlying platforms. It avoids only repaint flickering, but not visual tearing from moving graphic elements.
A reasonably simple way of having access to the raw pixels of an area fully under you control would be to extend a custom component from JComponent and overwrite its paintComponent()-method to paint the area from a BufferedImage (from memory):
public class PixelBufferComponent extends JComponent {
private BufferedImage bufferImage;
public PixelBufferComponent(int width, int height) {
bufferImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
setPreferredSize(new Dimension(width, height));
}
public void paintComponent(Graphics g) {
g.drawImage(bufferImage, 0, 0, null);
}
}
You can then manipulate you buffered image whichever way you desire. To get your changes made visible on screen, simply call repaint() on it. If you do the pixel manipulation from a thread other than the EDT, you need TWO buffered images to cope with race conditions between the actual repaint and your manipulation thread.
Note that this skeleton will not paint the entire area of the component when used with a layout manager that stretches the component beyond its preferred size.
Note also, the buffered image approach mostly only makes sense if you do real low level pixel manipulation via setRGB(...) on the image or if you directly access the underlying DataBuffer directly. If you can do all the manipulations using Graphics2D's methods, you could do all the stuff in the paintComponent method using the provided graphics (which is actually a Graphics2D and can be simply casted).
Here's a variation in which all drawing takes place on the event dispatch thread.
Addendum:
Basically, I need to constantly modify a lot pixels in a BufferedImage…
This kinetic model illustrates several approaches to pixel animation.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import java.awt.image.BufferedImage;
/** #see http://stackoverflow.com/questions/4430356 */
public class DemosDoubleBuffering extends JPanel implements ActionListener {
private static final int W = 600;
private static final int H = 400;
private static final int r = 80;
private int xs = 3;
private int ys = xs;
private int x = 0;
private int y = 0;
private final BufferedImage bi;
private final JLabel jl = new JLabel();
private final Timer t = new Timer(10, this);
public static void main(final String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new DemosDoubleBuffering());
frame.pack();
frame.setVisible(true);
}
});
}
public DemosDoubleBuffering() {
super(true);
this.setLayout(new GridLayout());
this.setPreferredSize(new Dimension(W, H));
bi = new BufferedImage(W, H, BufferedImage.TYPE_INT_ARGB);
jl.setIcon(new ImageIcon(bi));
this.add(jl);
t.start();
}
#Override
public void actionPerformed(ActionEvent e) {
move();
drawSquare(bi);
jl.repaint();
}
private void drawSquare(final BufferedImage bi) {
Graphics2D g = bi.createGraphics();
g.setColor(Color.white);
g.fillRect(0, 0, W, H);
g.setColor(Color.blue);
g.fillRect(x, y, r, r);
g.dispose();
}
private void move() {
if (!(x + xs >= 0 && x + xs + r < bi.getWidth())) {
xs = -xs;
}
if (!(y + ys >= 0 && y + ys + r < bi.getHeight())) {
ys = -ys;
}
x += xs;
y += ys;
}
}

Categories