Java BufferedImage / JPanel not updating new black pixel rows using JButton - java

Basically I have a (width x height) size of pixels in a buffered image and would like to draw simple horizontal BLACK lines, one after another into the Buffered Image / JPanel. Each click of the button should create a new horizontal line.
Although the button event reaches the updateImage() function, AND the Log output indicates that paintComponent(Graphics g) is called by the system. There is no update happening. The only time a Line is drawn is on the constructor initialisation. Nothing is being updated to the screen on each button click; only the initial one line is drawn from a test call in the constructor.
The end product is to simulate a printer.
Thank You.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JPanel;
public class ViewPaper extends JPanel {
private BufferedImage buffer = null;
private final int width = 384;
private int height = 500;
private final static Logger LOG = Logger.getLogger(ViewPaper.class
.getName());
public ViewPaper() {
super();
buffer = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
printDotLine(50);
LOG.log(Level.INFO, "ViewPaper: Load Successful.");
}
/* Update BufferedImage then repaint to screen.
* Note. A Top Level repaint() is also called
* and according to log the paintComponent is
* called but nothing is happening!
*/
public void updateImage(int y) {
printDotLine(y);
repaint();
LOG.log(Level.INFO, "ViewPaper: updateImage(); Called.");
}
/* Update JPanel with updated buffer. */
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(buffer, 0, 0, this);
g2d.dispose();
LOG.log(Level.INFO, "ViewPaper: paintComponent(); Called.");
}
/* Sets row of pixels in buffer to black.*/
public void printDotLine(int y) {
for (int x = 0; x < width; x++) {
buffer.setRGB(x, y, Color.BLACK.getRGB());
}
LOG.log(Level.INFO, "ViewPaper: printDotLine(); Called.");
}
}

You almost answered your own question but forgot to include actual button.
Here is how it should be (almost - no static should be used -it's just simple example/improvement of your code):
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ViewPaper extends JPanel {
static ViewPaper viewPaper;
static int line=50;
public static void main(String args[])
{
JFrame frame=new JFrame("View Paper");
viewPaper=new ViewPaper();
frame.add(viewPaper);
JButton button=new JButton("Click Me!");
frame.add(button,BorderLayout.SOUTH);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
viewPaper.updateImage(line++);
}
});
frame.setSize(300,500);
frame.setVisible(true);
}
private BufferedImage buffer = null;
private final int width = 384;
private int height = 500;
private final static Logger LOG = Logger.getLogger(ViewPaper.class
.getName());
public ViewPaper() {
super();
buffer = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
printDotLine(50);
LOG.log(Level.INFO, "ViewPaper: Load Successful.");
}
/* Update BufferedImage then repaint to screen.
* Note. A Top Level repaint() is also called
* and according to log the paintComponent is
* called but nothing is happening!
*/
public void updateImage(int y) {
printDotLine(y);
repaint();
}
/* Update JPanel with updated buffer. */
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(buffer, 0, 0, this);
g2d.dispose();
LOG.log(Level.INFO, "ViewPaper: paintComponent(); Called.");
}
/* Sets row of pixels in buffer to black.*/
public void printDotLine(int y) {
for (int x = 0; x < width; x++) {
buffer.setRGB(x, y, Color.BLACK.getRGB());
}
LOG.log(Level.INFO, "ViewPaper: Update Received.");
}
}

Related

Heart sprite not being drawn

So I've been trying to make a sprite object (called heart) that can be moved around by players on the JPanel "Battle box". Very similar code has worked for me in the past but weirdly not now. I need to clarify, this is PART of a project, not the whole thing. The BattleBox JPanel object gets called from a different part of the program, though technically it's possible the error isn't within these classes
Also btw not all the code is written for the new ones since I need to make it appear first before I can start making it move around and such xD Anyway any help people can give is appreciated and, just in case it wasn't obvious, I'm self taught and bad so sorry if the answer's super obvious
Also slight side note, the clock also doesn't show but I've kinda accepted fate with that one for now
Battle screen class
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
import MainMenu.MainMenu;
#SuppressWarnings("serial")
public class BattleScreen extends JPanel {
private Heart heart;
private Thread thread;
private Clock clock;
public BattleScreen() {
this.setBounds(0, 0, MainMenu.frame.getWidth(), MainMenu.frame.getHeight());
this.setBackground(Color.black);
clock = new Clock(this);
heart = new Heart();
heart.allowMovement(true);
MainMenu.con.add(this);
this.repaint();
this.setVisible(true);
thread = new Thread() {
public void run() {
repaint();
}
};
thread.start();
}
#Override
public void paintComponent (Graphics g) {
super.paintComponent(g);
heart.draw(g, this);
clock.draw(g, this);
}
}
Heart class
import java.awt.Component;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class Heart {
int xPos, yPos, width, height;
Thread thread;
BufferedImage image;
public Heart() {
width = 44;
height = 44;
xPos = (MainMenu.MainMenu.con.getWidth() - width) / 2;
yPos = (MainMenu.MainMenu.con.getHeight() - height) / 2;
try {
image = ImageIO.read(new File("src/BattleScreen/heart.png"));
} catch (IOException e) {
e.printStackTrace();
}
this.setBackground(Color.BLUE);
this.repaint();
thread = new Thread() {
public void run() {
updateMovement();
}};
}
void draw (Graphics g, Component c) {
g.drawImage(image, xPos, yPos, width, height, c);
}
}
Classes that worked in the past:
OLD BattleScreen
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import MainMenu.MainMenu;
#SuppressWarnings("serial")
public class BattleScreen extends JPanel implements KeyListener {
private static JFrame frame = MainMenu.frame;
static Heart sprite = new Heart(frame.getWidth()/2, frame.getHeight()/2, 20, 20);
public BattleScreen() {
addKeyListener(this);
setFocusable(true);
setBounds(0, 0, frame.getWidth(), frame.getHeight());
setBackground(Color.black);
MainMenu.con.add(this);
new Thread(() -> {
while (true) {
sprite.updateMovement();
repaint();
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
public void paintComponent (Graphics g) {
super.paintComponent(g);
sprite.draw(g, this);
}
}
OLD Heart class
package DEADBattleScreen;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Image;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class Heart {
Image img;
int xPos, yPos, width, height;
Thread thread;
Heart(int x, int y, int w, int h) {
this.xPos = x;
this.yPos = y;
this.width = w;
this.height = h;
try {
img = ImageIO.read(new File("src/battlescreen/heart.png"));
} catch (IOException e ) {
e.printStackTrace();
}
}
void draw (Graphics g, Component c) {
g.drawImage(img, xPos, yPos, width, height, c);
}
}

How can I add more than one paintComponent() to a frame?

So this is my main class:
package testgame;
import java.awt.EventQueue;
import javax.swing.JFrame;
public class Game extends JFrame {
public static JFrame frame = new JFrame("Just a test!");
public static void LoadUI() {
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
frame.setSize(550, 500);
frame.setLocationRelativeTo(null);
frame.setVisible(true); }
public static void main(String[] args) {
LoadUI();
frame.add(new Circles());
}
}
And this is the class that handles what I want to paint:
package testgame;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class Circles extends JPanel {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
drawBubbles(g); }
public void drawBubbles(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
RenderingHints rh
= new RenderingHints(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
rh.put(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHints(rh);
int x, y, size;
x = (int) (Math.random() * 500) + 15;
y = (int) (Math.random() * 450) + 15;
size = (int) (Math.random() * 50) + 25;
g2d.setColor(Color.GREEN);
g2d.drawOval(x, y, size, size);
g2d.fillOval(x, y, size, size); }
}
If I add another
frame.add(new Circles());
Nothing happens. I think it has to do with the layout of the frame, but the coordinates of the bubbles are random so I'm not sure how to work with this.
In this case I'm using a fixed-size array of 5, you may change it to a bigger fixed-size array or an ArrayList, as shown in this answer
For your particular case I would create a Circle class that may contain the data for each circle, being the coords and the size
Then create a CirclePane class that would paint all the Circles in a single paintComponent() method.
And finally, the Main class that would have a JFrame that may contain the CirclePane added to it.
With the above tips in mind, you could end up with something like this:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.geom.Ellipse2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class CircleDrawer {
private JFrame frame;
public static void main(String[] args) {
SwingUtilities.invokeLater(new CircleDrawer()::createAndShowGui); //We place our program on the EDT
}
private void createAndShowGui() {
frame = new JFrame(getClass().getSimpleName());
CirclePane circle = new CirclePane(5); //We want to create 5 circles, we may want to add more so we change it to 10, or whatever number we want
frame.add(circle);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
//Data class
class Circle {
private Point coords;
private int size;
public Circle(Point coords, int size) {
this.coords = coords;
this.size = size;
}
public Point getCoords() {
return coords;
}
public void setCoords(Point coords) {
this.coords = coords;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
}
//The drawing class
#SuppressWarnings("serial")
class CirclePane extends JPanel {
private int numberOfCircles;
private Circle[] circles;
public CirclePane(int numberOfCircles) {
this.numberOfCircles = numberOfCircles;
circles = new Circle[numberOfCircles];
for (int i = 0; i < numberOfCircles; i++) {
Point coords = new Point((int) (Math.random() * 500) + 15, (int) (Math.random() * 450) + 15); //We generate random coords
int size = (int) (Math.random() * 50) + 25; //And random sizes
circles[i] = new Circle(coords, size); //Finally we create a new Circle with these properties
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
for (int i = 0; i < numberOfCircles; i++) {
g2d.draw(new Ellipse2D.Double(circles[i].getCoords().getX(), circles[i].getCoords().getY(), circles[i].getSize(), circles[i].getSize())); //We iterate over each circle in the array and paint it according to its coords and sizes
}
}
#Override
public Dimension getPreferredSize() { //Never call JFrame.setSize(), instead override this method and call JFrame.pack()
return new Dimension(500, 500);
}
}
}
Which produces a similar output to this:
I hope this helps you to get a better idea, read about the MVC pattern as I made use of it for this answer.
Note:
In this answer I used the Shapes API, according to the recommendation of #MadProgrammer in this other answer. I used it in the g2d.draw(...) line.
For a deeper understanding in how custom painting works in Swing, check Oracle's Lesson: Performing Custom Painting and Painting in AWT and Swing tutorials.

How to make a continuously looping news Ticker in java

I dont know about you, but I tried so hard on how to create a news ticker. I found approaches that would append what was off the screen on to the back side of it. This caused the animation to jump by a whole character. Then I found methods that would not loop until the text was completely off the screen. After asking a ton of questions on this I thought it would be nice if I just posted my final solution for all the people wondering how to make it. Here you go.
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Scroll2 extends JPanel implements ActionListener{
private int x;
private int x2;
private int y;
private int width;
private String textIV;
private Color textColorIV;
private Color bGColorIV;
private Font f=(new Font("SansSerif", Font.BOLD,34));
Timer t;
// TODO font size
public Scroll2(String text, Color textColor, Color bGColor)
{
x=Integer.MIN_VALUE;
x2=Integer.MIN_VALUE;
y=0;
textIV=text;
this.setVisible(true);
Timer refreshTimer = new javax.swing.Timer(1000/50, this);
refreshTimer.start();
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g.setFont(f);
if ( x == Integer.MIN_VALUE ){
width=g.getFontMetrics().stringWidth(textIV);
x = 0;
y = (int) (getHeight()*.6);
}
if ( x2 == Integer.MIN_VALUE ){
x2 = g.getFontMetrics().stringWidth(textIV);
}
g.setColor(bGColorIV);
g.fillRect(this.getX(), this.getY(), (int)this.getBounds().getWidth(), (int)this.getBounds().getHeight());
g.setColor(textColorIV);
g.drawString(textIV, x, y);
g.drawString(textIV, x2, y);
}
public void refresh ()
{
if(x>=(-width)&&x2!=Integer.MIN_VALUE)
{
x=x-2;
x2=x2-2;
}
if(x2<=-width&&x!=Integer.MIN_VALUE)
{
x2=width;
}
if(x<-width&&x!=Integer.MIN_VALUE)
{
x=width;
}
repaint();
}
#Override
public void actionPerformed(ActionEvent e) {
this.refresh();
}
}

Wanting a type of grid for a pixel editor

I am currently trying to develop a basic pixel editor application to build up my programming experience with Java. I am designing it so the user has several colour options on, they click on an option and then they can drag over the cells in the grid and they change colour (like a typical image editor, but with a sort of snap on to each grid cell)
Any idea of what Java component, if any, is able to implement this type of grid in Java?
I had thought of each cell being a JButton, but this seemed terribly inefficient and I don't think it would be possible to change the colour of each cell(button) with out individually clicking on each one.
Any help appreciated.
More than a few hundred components is awkward. One easy way to get big pixels is to use drawImage() and scale the mouse coordinates as shown here and here. Here's a simple example.
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import javax.swing.Icon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
/** #see http://stackoverflow.com/questions/2900801 */
public class Grid extends JPanel implements MouseMotionListener {
private final BufferedImage img;
private int imgW, imgH, paneW, paneH;
public Grid(String name) {
super(true);
Icon icon = UIManager.getIcon(name);
imgW = icon.getIconWidth();
imgH = icon.getIconHeight();
this.setPreferredSize(new Dimension(imgW * 10, imgH * 10));
img = new BufferedImage(imgW, imgH, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = (Graphics2D) img.getGraphics();
icon.paintIcon(null, g2d, 0, 0);
g2d.dispose();
this.addMouseMotionListener(this);
}
#Override
protected void paintComponent(Graphics g) {
paneW = this.getWidth();
paneH = this.getHeight();
g.drawImage(img, 0, 0, paneW, paneH, null);
}
#Override
public void mouseMoved(MouseEvent e) {
Point p = e.getPoint();
int x = p.x * imgW / paneW;
int y = p.y * imgH / paneH;
int c = img.getRGB(x, y);
this.setToolTipText(x + "," + y + ": "
+ String.format("%08X", c));
}
#Override
public void mouseDragged(MouseEvent e) {
}
private static void create() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new Grid("Tree.closedIcon"));
f.pack();
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
create();
}
});
}
}
An option is to use a large Canvas, and intercept events on it. Draw whatever you need to in the paint(g) method.

Using Graphics2D to overlay text on a BufferedImage and return a BufferedImage

I have checked similarly named questions, but they don't answer this use case.
Basically, I was to overlay some text (text) at a given coordinate (x,y) I have the below function in a package;
protected BufferedImage Process2(BufferedImage image){
Graphics2D gO = image.createGraphics();
gO.setColor(Color.red);
gO.setFont(new Font( "SansSerif", Font.BOLD, 12 ));
gO.drawString(this.text, this.x, this.y);
System.err.println(this.text+this.x+this.y);
return image;
}
I feel like im missing something patently obvious; every reference to Graphics2D I can find is dealing with either games or writing directly to a file but I just want a BufferedImage returned. with the overlay 'rendered'
In the current code, the image appears out the end unchanged.
Thanks!
The method drawString() uses x and y for the leftmost character's baseline. Numbers typically have no descenders; if the same is true of text, a string drawn at position (0,0) will be rendered entirely outside the image. See this example.
Addendum: You may be having trouble with an incompatible color model in your image. One simple expedient is to render the image and then modify it in situ.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
/**
* #see https://stackoverflow.com/questions/2658663
*/
public class TextOverlay extends JPanel {
private BufferedImage image;
public TextOverlay() {
try {
image = ImageIO.read(new URL(
"http://cdn.sstatic.net/stackexchange/img/logos/so/so-logo.png"));
} catch (IOException e) {
e.printStackTrace();
}
image = process(image);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(image.getWidth(), image.getHeight());
}
private BufferedImage process(BufferedImage old) {
int w = old.getWidth() / 3;
int h = old.getHeight() / 3;
BufferedImage img = new BufferedImage(
w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
g2d.drawImage(old, 0, 0, w, h, this);
g2d.setPaint(Color.red);
g2d.setFont(new Font("Serif", Font.BOLD, 20));
String s = "Hello, world!";
FontMetrics fm = g2d.getFontMetrics();
int x = img.getWidth() - fm.stringWidth(s) - 5;
int y = fm.getHeight();
g2d.drawString(s, x, y);
g2d.dispose();
return img;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, null);
}
private static void create() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new TextOverlay());
f.pack();
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
create();
}
});
}
}

Categories

Resources