Swift: Double Buffering like in Java - java

In Java, I can write pixel data to an image that is then printed to the screen by overridden methods (paint and paintComponent). I can refresh the screen easily by updating the image and calling refresh(), which calls the paint/paintComponent cycle.
I'm wanting to do this in swift (3) with a UIImage. I can update the image, but I can't figure out how to repeatedly project the image onto the screen, print the screen, and repeat as I need to refresh the screen.
Firstly, what type of swift project (Single view app., Game, etc.) is best for repeatedly refreshing the screen?
And secondly how do I go about creating a (regulated) loop in Swift that refreshes the screen every so often?
In java this would look like (in a class that extends Frame):
static int width = 1440;
static int height = 900;
private BufferedImage canvas;
static Color[][] RGBMap = new Color[width][height];
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setBackground(Color.WHITE);
g2.clearRect(0, 0, width, height);
g2.drawImage(this.canvas, null, null);
paint();
}
public void paint() {
int i = 0;
while (i < width) {
int t = 0;
while (t < height) {
this.canvas.setRGB(i, t, RGBMap[i][t].getRGB());
t++;
}
i++;
}
//Refreshes RGBMap
iterate();
repaint();
}

Related

Overriding paintComponent() in JButton produces unexpected results

I've never written a program with anything more than a bare-bones GUI, so I've undertaken a personal project to write a chess application.
One of my goals with this project was to make the board rescale to fit the window, and I managed to do this without too much trouble. However, in the process I ran into the issue that my pieces (which were represented as Icons on JButtons) did not rescale with the rest of the board.
I decided to represent them with the Image class instead, and made a custom class called ScalingJButton which overrode paintComponent. This actually worked quite well... for the last piece to be drawn. The rest of the pieces are not drawn, and as a result the program is broken. Here is my ScalingJButton class:
public class ScalingJButton extends JButton{
private Image image;
ScalingJButton (){
this.image = null;
}
ScalingJButton (Image image){
this.image = image;
}
public void setImage(Image image){
this.image = image;
}
public Image getImage(){
return image;
}
#Override
public void paintComponent(Graphics g){
//super.paintComponent(g);
int x = getX();
int y = getY();
int width = getWidth();
int height = getHeight();
if (image != null) {
g.drawImage(image, x, y, width, height, this);
}
}}
Additionally, here is the code responsible for instantiating the ScalingJButtons (VisualBoard is a class extending JPanel and this is its constructor).
public VisualBoard (){
white = Color.WHITE;
black = Color.GRAY;
loadPieceImages();
setLayout(new GridLayout(8, 8));
for (int i = 0; i < 8; i++){
for (int j = 0; j < 8; j++){
squares[i][j] = new ScalingJButton();
if ((i + j) % 2 != 0) {
squares[i][j].setBackground(white);
}
else{
squares[i][j].setBackground(black);
}
add(squares[i][j]);
}
}
initializeStandardBoard();
}
Finally, since the layout may be relevant, here is the code that makes the board autoscale:
public class Chess {
public static void main (String[] args){
final VisualBoard board = new VisualBoard();
final JPanel container = new JPanel(new GridBagLayout());
container.add(board);
container.addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
drawResizedBoard(board, container);
}
});
JFrame frame = new JFrame("Chess");
frame.setSize(1000,1000);
frame.add(container);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
private static void drawResizedBoard(JPanel innerPanel, JPanel container) {
int w = (int)Math.round(container.getWidth()*0.9);
int h = (int)Math.round(container.getHeight()*0.9);
int size = Math.min(w, h);
innerPanel.setPreferredSize(new Dimension(size, size));
container.revalidate();
}}
I have done extensive debugging, but most of it has lead nowhere. One thing to note is that the "image" variable in ScalingJButton holds the correct picture when drawImage is called, so that's not the issue. Another interesting point is that the square with the piece in it is the the last square to be drawn, and depending on what order I add squares to the Board, different pieces will be drawn (so there is no issue loading pieces).
Strangely, if I do away with super in paintComponent, rolling my mouse over the drawn pieces causes the other squares to fill with that piece when I roll my mouse over them.
I'm completely lost and don't know what to do, so any help would be greatly appreciated!
First, you need to keep the super.paintComponent(g); line. If you don’t, artifacts will appear at seemingly random times.
Second, getX() and getY() return to the component’s position in its parent. When you paint, you are given a coordinate system where 0,0 is the top left corner of the component. So you should ignore getX() and getY().
The simplest alternative is to use the upper-left corner of the button:
int x = 0;
int y = 0;
You may want to account for the button’s border:
if (image != null) {
Rectangle inner = SwingUtilities.calculateInnerArea(this, null);
g.drawImage(image, inner.x, inner.y, inner.width, inner.height, this);
}
And if you want the button to really look like a normal button, you can also account for its margin:
if (image != null) {
Rectangle inner = SwingUtilities.calculateInnerArea(this, null);
Insets margin = getMargin();
inner.x += margin.left;
inner.y += margin.top;
inner.width -= (margin.left + margin.right);
inner.height -= (margin.top + margin.bottom);
g.drawImage(image, inner.x, inner.y, inner.width, inner.height, this);
}
However, in the process I ran into the issue that my pieces (which were represented as Icons on JButtons) did not rescale
The Stretch Icon may be a simpler solution. You can use this Icon on any component that can display an Icon. The image will dynamically change size to fill the space available to the Icon.
There are two suspicious lines in your code. First one is
frame.add(container);
This line adds your Swing element to an AWT container. Instead your should replace it with
frame.setContentPane(container);
The second line is the method
public void paintComponent(Graphics g) {
Try replacing it with
public void paint(Graphics g) {
Hope this helps.
EDIT:
Also change this
g.drawImage(image, x, y, width, height, this);
to
public void paint(Graphics g) {
super.paint(g);
int width = getWidth();
int height = getHeight();
if (image != null) {
g.drawImage(image, 0, 0, width, height, this);
}
}
Your images were simply outside of the grid.

Graphics of JPanel flickering on left side [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I have a java program that's should be doing something very simple. It contains a JPanel, on which repaint() is called 30 times each second. This JPanel overrides the paintComponent() method, and in this overwritten method, I take a BufferedImage and draw it to the JPanel.
This BufferedImage consists of a black image with a somewhat smaller blue rectangle inside of it. This displays, but the problem is that the left side, 50-80 pixels or so, of the screen flickers. On the leftmost part of what should be the blue rectangle, some of the pixels will sometimes appear black instead, as though there's some black overlay extending from the left side of the screen covering it, that flickers a bit each frame.
I wouldn't think just drawing a rectangle would be so consuming that it would cause graphical bugs with something like this; is it? I can't figure out why this would be happening, so do any of you have any idea what would cause a black "flicker" on the left of either a BufferedImage or a Graphics2D?
'Runnable example(please add the imports)':
public class Panel extends JPanel{
public int width, height;
public long lastTime;
public BufferedImage canvas;
public Panel(int a, int b){
width = a;
height = b;
canvas = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
lastTime = System.currentTimeMillis();
}
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g.drawImage(canvas, 0, 0, this);
}
public void drawRect(int startX, int startY, int w, int h, int color){
for(int i=0; i<w; i++){
for(int j=0; j<h; j++){
canvas.setRGB(i + startX, j + startY, color);
}
}
}
public void render(){
drawRect(0, 0, width, height, 0x000000);
drawRect(10, 10, width - 20, height - 20, 0x0000ff);
}
public void update(){
int delta = (int)(System.currentTimeMillis() - lastTime);
if(delta >= 1000 / 30){
render();
lastTime = System.currentTimeMillis();
}
}
//in a different class, contains main()
public class Main{
public static Panel pan;
public static void main(String[] args){
JFrame frame = new JFrame();
Container c = frame.getContentPane();
c.setPreferredSize(500, 500);
pan = new Panel(500, 500);
frame.add(pan);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
new runThread().run();
}
class runThread extends Thread{
public void run(){
while(true){
pan.update();
}
}
}
}
Because your program is incorrectly synchronized in several ways, the result is indeterminate. Swing GUI objects must be constructed and manipulated only on the event dispatch thread; this is required on all supported platforms. As discussed in How to Use Swing Timers, this example runs at 50 Hz without flicker.
I have a 15-class program…
In larger programs, you can search for EDT violations using one of the approaches cited here. Also review the animation techniques suggested here.

JPanel repaint from another class

I have a JPanel which displays an image. In a separate class, I'm reading from an xml file points. I am firstly creating an arraylist of triangles from these points. However I need to show the triangles on the image, i.e. draw them on! (yes this should be simple). But as these points and triangles are created in another class, I do not seem to be able to draw them on the already-displayed image within the GUI class. I have tried creating a ArrayList in the JPanel itself, which I update and then want to repaint, although it will not let me do this as shown below:
Class
triangles = clips.getTriangles();
tempPanel.setTriangles(triangles){
JPanel
public void settriangles(ArrayList<Point[]> t){
triangles = t;
repaint();
}
My only other idea is for the JPanel to have a listener waiting for when triangles are returned, updating the field and hence then repainting.
Any ideas?
Thanks
Edit: Code for Drawing
public void settriangles(ArrayList<Point[]> t){
triangles = t;
repaint();
}
public void paintComponent(Graphics g) {
System.out.println("in paint component");
if (g != null) {
Graphics2D graphics = (Graphics2D) g;
try {
Rectangle back_rect = new Rectangle(0, 0, getWidth(),
getHeight());
graphics.setColor(GuiComponentGenerator.GUI_BACKGROUND_COLOUR);
graphics.fill(back_rect);
if (image != null) {
int width = Math.round(image.getWidth() * magnification);
int height = Math.round(image.getHeight() * magnification);
Rectangle image_rect = new Rectangle(offset.x, offset.y,
width, height);
graphics.setColor(Color.BLACK);
graphics.draw(image_rect);
graphics.drawImage(image, offset.x, offset.y, width,
height, null);
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
for(int pos = 0; pos < triangles.size(); pos++){
Point[] current = triangles.get(pos);
ArrayList<Point> current_triangle = new ArrayList<Point>();
current_triangle.add(current[0]);
current_triangle.add(current[1]);
current_triangle.add(current[2]);
drawRegion(graphics, current_triangle);
}
}
}
finally {
graphics.dispose();
}
}
private void drawRegion(Graphics2D graphics, ArrayList<Point> points) {
graphics.setColor(trans_grey);
Area area = getArea(points);
graphics.fill(area);
graphics.setStroke(new BasicStroke(2));
graphics.setColor(Color.BLACK);
graphics.draw(area);
}
private Area getArea(ArrayList<Point> points) {
Area area = new Area(getPath(points, true));
return area;
}
private GeneralPath getPath(ArrayList<Point> points, boolean close_path) {
GeneralPath path = new GeneralPath();
Point current_screen_point = calculateScreenPoint(points.get(0));
path.moveTo(current_screen_point.x, current_screen_point.y);
for (int point_num = 1; point_num < points.size(); point_num++) {
current_screen_point = calculateScreenPoint(points.get(point_num));
path.lineTo(current_screen_point.x, current_screen_point.y);
}
if (close_path)
path.closePath();
return path;
}
public Point calculateScreenPoint(Point image_point) {
float h_proportion = (float) image_point.x / (float) image.getWidth();
float v_proportion = (float) image_point.y / (float) image.getHeight();
float image_width_in_panel = (float) image.getWidth() * magnification;
float image_height_in_panel = (float) image.getHeight() * magnification;
Point on_screen_point = new Point(0, 0);
on_screen_point.x = offset.x
+ Math.round(h_proportion * image_width_in_panel);
on_screen_point.y = offset.y
+ Math.round(v_proportion * image_height_in_panel);
return on_screen_point;
}
Your paintComponent leaves a little to be desired ;)
Firstly, you should never get a null graphics unless the paint method has been called in correctly, which in case they deserve for it to fail.
You should try and use Graphics.create to create a copy of the incoming Graphics context. This allows you to mess with the Graphics properties (such as transforms etc) without adversly effecting the original
I don't know what the image is all about, but basically, if its null, your triangles will never paint (don't know if this is what you want or not).
I don't know what the offset relates to, but just in case, the 0x0 point is always the top, left corner of your component.
public void paintComponent(Graphics g) {
// This is important, you will to have a very good reason not to call this
super.paintComponent(g);
System.out.println("in paint component");
// Should never need this. You should never call the paintComponent
// directly.
// if (g != null) {
// Create a copy of the graphics, with which you can play...
Graphics2D graphics = (Graphics2D) g.create();
try {
Rectangle back_rect = new Rectangle(0, 0, getWidth(),
getHeight());
graphics.setColor(Color.GREEN);
graphics.fill(back_rect);
// What's this trying to do...
// Where do you produce this???
// Because I didn't know where the image was been produced
// I commented out the code, but you should be aware
// That if the image is null, you triangles will never paint...
// if (image != null) {
// int width = Math.round(image.getWidth() * magnification);
// int height = Math.round(image.getHeight() * magnification);
//
// Rectangle image_rect = new Rectangle(offset.x, offset.y,
// width, height);
// graphics.setColor(Color.BLACK);
// graphics.draw(image_rect);
// graphics.drawImage(image, offset.x, offset.y, width,
// height, null);
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
for (int pos = 0; pos < triangles.size(); pos++) {
Point[] current = triangles.get(pos);
ArrayList<Point> current_triangle = new ArrayList<Point>(3);
current_triangle.add(current[0]);
current_triangle.add(current[1]);
current_triangle.add(current[2]);
drawRegion(graphics, current_triangle);
}
//} // From the image != null
} finally {
graphics.dispose();
}
}
I'd also suggest you have a read through
2D Graphics
Performing Custom Painting in Swing
If you haven't already ;)
This article will give you all the info you need http://java.sun.com/products/jfc/tsc/articles/painting/
but I think you are missing -
super.paintComponent(g);
public class MyPanel extends JPanel {
protected void paintComponent(Graphics g) {
// Let UI delegate paint first
// (including background filling, if I'm opaque)
super.paintComponent(g);
// paint my contents next....
}
}
I worked out how to draw triangles on the image, when passing through an arrayList, where each Point[] represents the points of the triangle.
Note that this is now in a single entire class which is passed the information, rather than trying to call repaint from another class.
public AnnotatedDisplayTriangles(BufferedImage image, String image_path, ArrayList<Point[]> triangles) {
this.image = image;
this.image_path = image_path;
this.triangles = triangles;
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// Draw image centered.
int x = (getWidth() - image.getWidth())/2;
int y = (getHeight() - image.getHeight())/2;
g.drawImage(image, x, y, this);
Stroke drawingStroke = new BasicStroke(2);
Graphics2D graph = (Graphics2D)g;
graph.setStroke(drawingStroke);
graph.setPaint(Color.black);
for(int p = 0; p < triangles.size(); p++){
Point[] current_triangles = triangles.get(p);
for(int triangle = 0; triangle < current_triangles.length; triangle++ ){
Point current = current_triangles[triangle];
Point next;
if(triangle == current_triangles.length -1 )
next = current_triangles[0];
else
next = current_triangles[triangle + 1];
Line2D line = new Line2D.Double(current.x, current.y, next.x, next.y);
graph.draw(line);
}
}
}
public static void main(String image_path,ArrayList<Point[]> triangles, String panel_name) throws IOException {
String path = image_path;
BufferedImage image = ImageIO.read(new File(path));
AnnotatedDisplayTriangles contentPane = new AnnotatedDisplayTriangles(image, path, triangles);
// You'll want to be sure this component is opaque
// since it is required for contentPanes. Some
// LAFs may use non-opaque components.
contentPane.setOpaque(true);
int w = image.getWidth();
int h = image.getHeight();
JFrame f = new JFrame(panel_name);
// f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setContentPane(contentPane);
f.setSize(w,h);
f.setLocation(200,200);
f.setVisible(true);
}

Display image from 2D array data in java

I have some data that is loaded from the text file that corresponds to my image file. this data is now in a 2D array. I want to show this image. apparently the image show be of format bufferedimage. but mine is just simple 2D double format.
Also how is possible to resize the image, meaning to make it twice bigger ( requiring interpolation in between of course)
In other words how can we do the "imshow" and " imresize" Matlab equilvalent in java?
There is no simple method for converting between an array based intensity matrix to a renderable image in Java, at least not that I am aware of. Nor is there any simple one line method for displaying an image on the screen etc.
It is however correct that a BufferedImage would be a viable solution in this case. What you would have to do is to create a BufferedImage of the desired size and then loop through your 2D intensity matrix and fill in the colors in the resulting image.
Once you have the data in form of a BufferedImage you can use it directly for rendering. For example you could create a JFrame with a custom JPanel component for displaying the image. The following sample code illustrates that procedure: (Note that this assumes that your image data in the 2D array is scaled to fit in the interval [0,1]. If it is not then it will have to be re-scaled prior to filling in the BufferedImage.)
public class ImageTest {
private static final int HEIGHT = 250;
private static final int WIDTH = 250;
public static void main(String[] args) {
double[][] data = new double[WIDTH][HEIGHT];
Random r = new Random();
for(int i = 0; i < WIDTH; i++) {
for(int j = 0; j < HEIGHT; j++) {
data[i][j] = r.nextDouble();
}
}
final BufferedImage img = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
Graphics2D g = (Graphics2D)img.getGraphics();
for(int i = 0; i < WIDTH; i++) {
for(int j = 0; j < HEIGHT; j++) {
float c = (float) data[i][j];
g.setColor(new Color(c, c, c));
g.fillRect(i, j, 1, 1);
data[i][j] = r.nextDouble();
}
}
JFrame frame = new JFrame("Image test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D)g;
g2d.clearRect(0, 0, getWidth(), getHeight());
g2d.setRenderingHint(
RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
// Or _BICUBIC
g2d.scale(2, 2);
g2d.drawImage(img, 0, 0, this);
}
};
panel.setPreferredSize(new Dimension(WIDTH*2, HEIGHT*2));
frame.getContentPane().add(panel);
frame.pack();
frame.setVisible(true);
}
}
If you are happy with resizing and interpolating in the resulting output image then you can achieve it by simply scaling the graphics context and enabling the interpolation rendering hint on it, as the above example shows when displaying/rendering the image. (This can of course be done directly in the BufferedImage as well in a similar fashion.
Have a look at MemoryImageSource. It might not do exactly what you want (because Java tends to use int / byte for image data) but it will at least send you down the right route.
http://www.javaworld.com/javatips/jw-javatip16.html

Drawing on Java heavyweight swing components

I'm working with an application in which I add a heavyweight (Canvas) to a JFrame. The Canvas is a 3rd party component, so I am required to keep it heavyweight. I'd like to add capabilities for the user to draw on the canvas and paint a selection rectangle.
I don't think I can do this with the glass pane since the heavyweight canvas will be displayed over the glass pane. I've tried adding a mouse listener to the canvas and drawing directly on its graphics, but that seems to give the "flicker" effect since it's not a lightweight double-buffered component.
Is there a way to achieve this smooth drawing on heavyweight components?
This is my current attempt in the paint method of the heavyweight component, but there is still the flashing.
#Override
public void paint(Graphics g)
{
super.paint(g);
if (showUserSelection)
{
Point startDrawPoint = new Point(Math.min(startSelectPoint.x,
endSelectPoint.x), Math.min(startSelectPoint.y,
endSelectPoint.y));
Point endDrawPoint = new Point(Math.max(startSelectPoint.x,
endSelectPoint.x), Math.max(startSelectPoint.y,
endSelectPoint.y));
int w = endDrawPoint.x - startDrawPoint.x;
int h = endDrawPoint.y - startDrawPoint.y;
if (w > 0 && h > 0)
{
BufferedImage img = new BufferedImage(w, h,
BufferedImage.TYPE_INT_ARGB);
Graphics2D imgGraphics = img.createGraphics();
imgGraphics.fillRect(0, 0, w, h);
g.drawImage(img, startDrawPoint.x, startDrawPoint.y, w, h,
null);
}
}
}
I'm not aware of a drop in "Host AWT/SWT in Swing" component, but you should be able to build a work around yourself.
Have you considered implementing double buffering yourself?
You're 90% of the way there with your code, just at a glance.
#Override
public void paint(Graphics g)
{
BufferedImage buffer = new BufferedImage(COMPONENT_WIDTH, COMPONENT_HEIGHT, BufferedImage.TYPE_INT_ARGB);
Graphics bufferG = buffer.getGraphics();
super.paint(bufferG);
if (showUserSelection)
{
Point startDrawPoint = new Point(Math.min(startSelectPoint.x,
endSelectPoint.x), Math.min(startSelectPoint.y,
endSelectPoint.y));
Point endDrawPoint = new Point(Math.max(startSelectPoint.x,
endSelectPoint.x), Math.max(startSelectPoint.y,
endSelectPoint.y));
int w = endDrawPoint.x - startDrawPoint.x;
int h = endDrawPoint.y - startDrawPoint.y;
if (w > 0 && h > 0)
{
bufferG.fillRect(startDrawPoint.x, startDrawPoint.y, w, h);
}
}
g.drawImage(buffer, 0, 0, null);
}

Categories

Resources