I'm working on a project to create a screen saver that draws random shapes. I have a few questions, but my main concern right now is how to get the shapes to stay on the screen and not just disappear after they are created. Here is my code. I cannot use any loops and I am not looking to change any of the functionality of what I have yet (other than possibly shapesDrawn).
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class ScreenSaver2 extends JPanel implements ActionListener {
private JFrame frame = new JFrame("FullSize");
private Rectangle rectangle;
boolean full;
protected void paintComponent(Graphics g) {
int r = (int)(Math.random() * 255);
int gr = (int)(Math.random() * 255);
int b = (int)(Math.random() * 255);
Color color = new Color(r, gr, b);
int width = 10 + (int)(Math.random() * 40);
int height = 10 + (int)(Math.random() * 40);
int x = (int)(Math.random() * (getWidth() - width));
int y = (int)(Math.random() * (getHeight() - height));
int whichShape = (int)(Math.random() * 3);
int shapesDrawn = 0;
super.paintComponent(g);
if (shapesDrawn >= 30) {
shapesDrawn = 0;
}
switch (whichShape) {
case 0:
g.setColor(color);
g.drawLine(x, y, x + width, y + height);
shapesDrawn++;
break;
case 1:
g.setColor(color);
g.drawRect(x, y, width, height);
shapesDrawn++;
break;
case 2:
g.setColor(color);
g.drawRoundRect(x, y, width, height, 25, 25);
shapesDrawn++;
break;
case 3:
g.setColor(color);
g.drawOval(x, y, width, height);
shapesDrawn++;
break;
}
}
ScreenSaver2() {
// Remove the title bar, min, max, close stuff
frame.setUndecorated(true);
// Add a Key Listener to the frame
frame.addKeyListener(new KeyHandler());
// Add this panel object to the frame
frame.add(this);
// Get the dimensions of the screen
rectangle = GraphicsEnvironment.getLocalGraphicsEnvironment()
.getDefaultScreenDevice().getDefaultConfiguration().getBounds();
// Set the size of the frame to the size of the screen
frame.setSize(rectangle.width, rectangle.height);
frame.setVisible(true);
// Remember that we are currently at full size
full = true;
// set and initialize timer
Timer t = new Timer(500, this);
t.setDelay(500);
t.start();
}
// This method will run when any key is pressed in the window
class KeyHandler extends KeyAdapter {
public void keyPressed(KeyEvent e) {
// Terminate the program.
if (e.getKeyChar() == 'x') {
System.out.println("Exiting");
System.exit(0);
}
// Change background color
else if (e.getKeyChar() == 'r') {
System.out.println("Change background color");
setBackground(new Color((int)(Math.random() * 256), (int)(Math.random() * 256), (int)(Math.random() * 256)));
repaint();
}
// Resize to half-screen
else if (e.getKeyChar() == 'z') {
System.out.println("Resizing");
frame.setSize((int)rectangle.getWidth() / 2, (int)rectangle.getHeight());
}
}
}
public void actionPerformed(ActionEvent e) {
repaint();
}
public static void main(String[] args) {
ScreenSaver2 obj = new ScreenSaver2();
}
}
Rather than painting each new shape within the paintComponent directly to the Graphics context, use a backing buffer to render the shapes to first and paint this to the Graphics context.
This way, you would simply need to maintain a simple counter each time a new shape is rendered.
This is also troublesome, because paintComponent may be called for any number of reasons, which, you don't control many, meaning that it would be possible for your program to paint more shapes before any timer ticks have actually occurred...
Updated with example
The only choice you have left is to create a backing buffer, onto which you can paint each change as required. This will "store" each change between paint cycles. You could then simply paint this to the screen...
private BufferedImage img;
//...
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
// Create the backing buffer
// This is a little cheat, creating a new image when the number of shapes
// exceeds the requirements, but it saves messing about with clearing
// a alpha image ;)
if (img == null || shapesDrawn >= 30) {
img = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
shapesDrawn = 0;
} else if (img.getWidth() != getWidth() || img.getHeight() != img.getHeight()) {
// Update the backing buffer to meet the requirements of the changed screen size...
BufferedImage buffer = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D gbuffer = buffer.createGraphics();
gbuffer.drawImage(img, 0, 0, this);
gbuffer.dispose();
img = buffer;
}
// Get a reference to the backing buffers graphics context...
Graphics2D gbuffer = img.createGraphics();
// Paint the shapes to the backing buffer...
int r = (int) (Math.random() * 255);
int gr = (int) (Math.random() * 255);
int b = (int) (Math.random() * 255);
Color color = new Color(r, gr, b);
int width = 10 + (int) (Math.random() * 40);
int height = 10 + (int) (Math.random() * 40);
int x = (int) (Math.random() * (getWidth() - width));
int y = (int) (Math.random() * (getHeight() - height));
int whichShape = (int) (Math.random() * 3);
int shapesDrawn = 0;
switch (whichShape) {
case 0:
gbuffer.setColor(color);
gbuffer.drawLine(x, y, x + width, y + height);
shapesDrawn++;
break;
case 1:
gbuffer.setColor(color);
gbuffer.drawRect(x, y, width, height);
shapesDrawn++;
break;
case 2:
gbuffer.setColor(color);
gbuffer.drawRoundRect(x, y, width, height, 25, 25);
shapesDrawn++;
break;
case 3:
gbuffer.setColor(color);
gbuffer.drawOval(x, y, width, height);
shapesDrawn++;
break;
}
// Dispose of the buffers graphics context, this frees up memory for us
gbuffer.dispose();
// Paint the image to the screen...
g2d.drawImage(img, 0, 0, this);
g2d.dispose();
}
Possibly the worst advice I've ever had to give
Change...
super.paintComponent(g);
if (shapesDrawn >= 30) {
shapesDrawn = 0;
}
To...
if (shapesDrawn >= 30) {
super.paintComponent(g);
shapesDrawn = 0;
}
Related
I'm trying to make a cylinder using Java Graphics and the paintComponent() method.
The user of the application can select which shape they want (in this case it's a cylinder) and input the dimensions they want for the shape.
After they input the dimensions and click the submit button, another window will open with the image drawn on it.
My current issue is getting the shape made correctly. I'm currently trying to make two ovals and connect them using two lines. The base will be a red oval and everything else will have no color.
When you submit the dimensions for the cylinder, the sides are never the correct length or in the correct position on the Y-Axis. An example can be view here:
The dimensions for this cylinder: 200 height, 50 radius.
What the cylinder should look like:
300 height, 300 radius
I'll be working on adding the minimal version of the program for testing. However, for right now I'll be providing what the code is for the cylinder itself and the paintComponent() method.
Cylinder:
import java.awt.Color;
public class Cylinder extends Circle {
private int length;
public Cylinder(int radius, int length, Color color) {
super(radius, length, color);
this.length = length;
this.radius = radius;
}
public Cylinder(int newX, int newY, int newRadius, int newLength) {
super(newX, newY, newRadius);
length = newLength;
}
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
public int calcArea() {
return (int) Math.ceil( 2 * 3.14 * radius * radius + 2 * 3.14 * radius * length);
}
public int calcVolume() {
return (int) Math.ceil(3.14 * radius * radius * length);
}
public DrawFigure drawFigure() {
DrawFigure cylinder1 = new DrawFigure(4, getRadius(), length);
return cylinder1;
}
public String toString() {
return "Length = " + length + " " + super.toString();
}
}
DrawFigure (paintComponent is the last method):
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
public class DrawFigure extends JPanel {
int type;
int length, width, height, radius;
public DrawFigure() {
super();
type = 5;
}
public DrawFigure(int myType, int myWidth, int myLength, int myHeight) { // Box and Rectangle
super();
type = myType;
length = myLength;
width = myWidth;
height = myHeight;
}
public DrawFigure(int x, int y, int myType, int myWidth, int myLength, int myHeight) {
super();
type = myType;
length = myLength;
width = myWidth;
height = myHeight;
}
public DrawFigure(int myType, int myRadius, int myHeight) {
super();
type = myType;
radius = myRadius;
height = myHeight;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (type == 1) { // Draw Rectangle
} else if (type == 2) { // Draw Box
} else if(type == 3) { // Draw Circle
} else if(type == 4) { // Draw Cylinder
g.setColor(Color.BLACK);
g.drawOval(135, 65, radius, radius - radius / 2);
// Base
g.setColor(Color.RED);
g.fillOval(135, 65 + height, radius, radius / 2);
g.setColor(Color.BLACK);
g.drawLine(135, 65 + height + (height /4), 135, 135);
g.setColor(Color.BLACK);
g.drawLine(135 + radius, 65 + height + (height /4), 135 + radius, 135);
return;
}
}
}
Full code for the program:
Point.java https://pastebin.com/iVgN47e3
Lab6GUI.java https://pastebin.com/bKM790iQ
Rectangle.java https://pastebin.com/MdCrJYeA
Box.java https://pastebin.com/iZCZpUi7
Circle.java https://pastebin.com/aui1NgJi
Cylinder.java https://pastebin.com/fHDNmBXT
DrawFigure.java https://pastebin.com/z8t31put
LessThanOrEqualToZeroException.java https://pastebin.com/4ELEmsNX
LessThanOrGreaterThanException.java https://pastebin.com/1avRUudN
Okay, so drawing an oval extends from the x/y position, with a positive width/height, that would make the oval draw right/down from the x/y position, for example...
So, assuming we start at 0x0, this would mean that the lines would need to start at a y position of radius / 4, given that you're using radius for the width and radius / 2 for the height (I'm not going to mention how that is confusing). This will allow the lines to "appear" that they join the outer edge of the oval (and draw down)
The lines would then be height long. This means the bottom oval would then need to start at height - (radius / 4) ... okay, I had to go and double check this, but remember, the line ends at (radius / 4) + height, this also means that the cylinder is the height + (radius / 2) high in total.
🤪🤯
height=200, radius=50
height=300, radius=300
This prevents scenario where radius / 4 is greater than height, because that would just be a mess
Runnable example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new DrawPane(300, 300));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class DrawPane extends JPanel {
int height, radius;
public DrawPane(int myRadius, int myHeight) {
super();
radius = myRadius;
height = myHeight;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int x = (getWidth() - radius) / 2;
int y = (getHeight() - (height + (radius / 4))) / 2;
g2d.translate(x, y);
g2d.setColor(Color.LIGHT_GRAY);
g2d.drawRect(0, 0, radius, height + (radius / 4));
// Base
g2d.setColor(Color.RED);
g2d.fillOval(0, height - (radius / 4), radius, radius / 2);
g2d.setColor(Color.BLACK);
g2d.drawOval(0, 0, radius, radius / 2);
g2d.setColor(Color.BLACK);
g2d.drawLine(0, radius / 4, 0, height);
g2d.drawLine(radius, radius / 4, radius, height);
g2d.dispose();
}
}
}
I am new to programming and I've been trying to get and set the icon to circle but it's not being updated. I want to set every icon that was set to JLabel will become circular.
class CircleLabel extends JLabel {
private Icon icon;
private int borderSize;
#Override
protected void paintComponent(Graphics g) {
if (getIcon() != null) {
icon = getIcon();
int width = getWidth();
int height = getHeight();
int diameter = Math.min(width, height);
int x = width / 2 - diameter / 2;
int y = height / 2 - diameter / 2;
int border = borderSize * 2;
diameter -= border;
Dimension size = getAutoSize(icon, diameter);
BufferedImage img = new BufferedImage(size.width, size.height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2_img = img.createGraphics();
g2_img.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2_img.fillOval(0, 0, diameter, diameter);
Composite composite = g2_img.getComposite();
g2_img.setComposite(AlphaComposite.SrcIn);
g2_img.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2_img.drawImage(toImage(icon), 0, 0, size.width, size.height, null);
g2_img.setComposite(composite);
g2_img.dispose();
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(new Color(255, 255, 255));
diameter += border;
g2.fillOval(borderSize, borderSize, diameter, diameter);
g2.drawImage(img, x - borderSize, y + borderSize, null);
} else {
System.out.println("No Icon!");
}
super.paintComponent(g);
}
private Dimension getAutoSize(Icon image, int size) {
int w = size;
int h = size;
int iw = image.getIconWidth();
int ih = image.getIconHeight();
double xScale = (double) w / iw;
double yScale = (double) h / iw;
System.out.println(xScale);
System.out.println(yScale);
double scale = Math.max(xScale, yScale);
int width = (int) (scale * iw);
int height = (int) (scale * ih);
if (width < 1) {
width = 1;
}
if (height < 1) {
height = 1;
}
return new Dimension(width, height);
}
private Image toImage(Icon icon) {
return ((ImageIcon) icon).getImage();
}
}
If I set the icon manually and removed the icon from the label, it will work but when I add the icon to the label through properties, it will not work.
icon = new ImageIcon(getClass().getResource("/Attachments/user.png"));
I found this on YouTube and wanted to apply it to the application I'm trying to build.
I am implementing captcha feature in our project. Its basically Tapestry framework application. I am generating random alfa-numeric string and convert it to image and display it in web page.
Now what i need is, i want to add random noise like dots lines etc to make image with text unclear. How to proceed please help.
keeping the code for reference .
-- This method gives text captcha.
`
public String generateCaptcha() {
Random random = new Random();
int min = 4; // Inclusive
int max = 9; // Exclusive
int length = random.nextInt(max-min) + min;
StringBuilder captchaStringBuffer = new StringBuilder();
for (int i = 0; i < length; i++) {
int captchaNumber = Math.abs(random.nextInt()) % 60;
int charNumber = 0;
if (captchaNumber < 26) {
charNumber = 65 + captchaNumber;
}
else if (captchaNumber < 52){
charNumber = 97 + (captchaNumber - 26);
}
else {
charNumber = 48 + (captchaNumber - 52);
}
captchaStringBuffer.append((char)charNumber);
}
return captchaStringBuffer.toString();
}
`
-- This method converts generated captcha to Image with out any noise.
`
public void textToImage(String displayCode){
String text = displayCode;
BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
Font font = new Font("Arial", Font.PLAIN, 48);
g2d.setFont(font);
FontMetrics fm = g2d.getFontMetrics();
int width = fm.stringWidth(text);
int height = fm.getHeight();
g2d.dispose();
img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
g2d = img.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION,
RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_OFF);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING,
RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING,
RenderingHints.VALUE_DITHER_DISABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
RenderingHints.VALUE_STROKE_DEFAULT);
g2d.setFont(font);
fm = g2d.getFontMetrics();
g2d.setColor(Color.BLACK);
g2d.drawString(text, 0, fm.getAscent());
g2d.dispose();
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(img, "png", baos);
byte[] res=baos.toByteArray();
setBinaryImage("data:image/png;base64,"+Base64.encode(res));
} catch (IOException e) {
}
}
`
I am newbie so please tell in clear what ever you say.
Thanks in Advance :-)
When trying to obstruct the text that is being displayed, you can use:
Dots/Circles, spread randomly over the image with varying size and color
Lines, coming from a random point on the edge of the image to another point on the edge of the image. These can also vary in color and thickness.
As far as i know, you can use g2d.drawLine(x1, y1, x2, y2) to draw a line. Since you want it to go from the edge to another point on the edge, you have to limit your random-point-generation. You can use this approach:
public Point pointOnEdge(int width, int height) {
int side = (int) (Math.random() * 3); //0=top, 1=bot, 2=left, 3=right
int x = 0;
int y = 0;
switch(side) {
case 0:
//when on top, y is at the top of the image (0) and x is something in [0, width]
y = 0;
x = (int) (Math.random() * width);
break;
case 1:
//when on bottom, y is at the bottom of the image (image height) and x is something in [0, width]
y = height;
x = (int) (Math.random() * width);
case 2:
//when on left, x is at the left side (0) of the image and y is something in [0, height]
y = (int) (Math.random() * height);
x = 0;
break;
case 3:
//when on left, x is at the left side (0) of the image and y is something in [0, height]
y = (int) (Math.random() * height);
x = width;
break;
}
return new Point(x, y);
}
If you create two Points like that, and connect them with a line, then you have a pretty simple way of obstructing your Image Partially, thus distorting it.
Now to the Circles:
public void drawCircles(Graphics2D g2d, int width, int height) {
//draw 10 of them
for(int i = 0; i < 10; i++) {
//select a random size
int x = 10 + (int) (Math.random() * 10);
//draw circle at random position with the created size
g2d.fillOval((int) (Math.random() * width), (int) (Math.random() * height), x, x);
}
}
And like that you are now able to distort your image to make it hard to read.
I hope you have enough common code understanding to know where to put these function calls. If not, I can add it if nescessary.
EDIT 1
If you want a dotted Background for your Captcha, you can use this code before rendering the String or anything else:
boolean r = false;
boolean g = false;
for(int y = 0; y < height; y++) {
r = !r;
g = r;
for(int x = 0; x < width; x++) {
g = !g;
if(g) {
g2d.setColor(Color.GRAY);
}else {
g2d.setColor(Color.WHITE);
}
g2d.drawLine(x, y, x, y);
}
}
EDIT 2
I would recommend, that you use a different font. Then you dont have to do any streching. Good fonts for that are e.g. Gigi. You could also select a font randomly by using GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames() which returns all Fonts that Java has.
I'm tyring to link to circles with drawline , but I have a problem here is my code :
import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.Graphics;
import javax.swing.JPanel;
public class Panneau extends JPanel {
public void paintComponent(Graphics g){
// declaration
String text = "test";
int x = 250, y = 200;
int height = 50, width = 50;
g.setColor(Color.yellow);
g.fillOval(x-height/2, y-width/2,width, height);
g.fillOval((x-height/2)+100, (y-width/2)+50,width, height);
FontMetrics fm = g.getFontMetrics();
double textWidth = fm.getStringBounds(text, g).getWidth();
g.setColor(Color.black);
g.drawString(text, (int) (x - textWidth/2),(int) (y + fm.getMaxAscent() / 2));
g.drawString(text, (int) (x - textWidth/2)+100,(int) (y + fm.getMaxAscent() / 2)+50);
g.setColor(Color.black);
g.drawLine(x,y,x+100,y+50);
}
}
the problem , the line I drawed start from center of circle , I want to draw Line from circle (like Graph node!) thanks for helping ! :)
Actually, I realized there was a way to 'hack it' by drawing the graphic elements in a different order. This still draws the entire line, but then effectively 'erases the unwanted bits' by ..drawing over the top of them!
import java.awt.*;
import javax.swing.*;
public class Panneau extends JPanel {
public void paintComponent(Graphics g){
// declaration
String text = "test";
int x = 250, y = 200;
int height = 50, width = 50;
g.setColor(Color.black);
g.drawLine(x,y,x+100,y+50);
g.setColor(Color.yellow);
g.fillOval(x-height/2, y-width/2,width, height);
g.fillOval((x-height/2)+100, (y-width/2)+50,width, height);
FontMetrics fm = g.getFontMetrics();
double textWidth = fm.getStringBounds(text, g).getWidth();
g.setColor(Color.black);
g.drawString(text, (int) (x - textWidth/2),(int) (y + fm.getMaxAscent() / 2));
g.drawString(text, (int) (x - textWidth/2)+100,(int) (y + fm.getMaxAscent() / 2)+50);
}
public Dimension getPreferredSize() {
return new Dimension(400,280);
}
public static void main(String[] args) {
Runnable r = new Runnable() {
public void run() {
Panneau p = new Panneau();
JOptionPane.showMessageDialog(null, p);
}
};
SwingUtilities.invokeLater(r);
}
}
I've started to take interest with making animations(slideshows, backgrounds etc) in Java. I know that JavaFX is much better for doing this, but I'm just to stubborn to bother switching over.
Here is what I got so far.
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class BlurredLightCells extends JPanel {
private static final long serialVersionUID = 4610174943257637060L;
private Random random = new Random();
private ArrayList<LightCell> lightcells;
private float[] blurData = new float[500];
public static void main(String[] args) {
JFrame frame = new JFrame("Swing animated bubbles");
frame.setSize(1000, 750);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new BlurredLightCells(60));
frame.setVisible(true);
}
public BlurredLightCells(int amtOfBCells) {
setSize(1000, 750);
/**
* Below we initiate all the cells that are going to be drawn on screen
*/
Arrays.fill(blurData, 1f / 20f);
lightcells = new ArrayList<LightCell>(amtOfBCells);
for (int i = 0; i < amtOfBCells; i++) {
/**
* Below we generate all the values for each cell(SHOULD be random for each one)
*/
int baseSpeed = random(0, 3);
int xSpeed = (int) Math.floor((Math.random() * (baseSpeed - -baseSpeed + baseSpeed)) + -baseSpeed);
int ySpeed = (int) Math.round((Math.random() * baseSpeed) + 0.5);
int radius = random(25, 100);
int x = (int) Math.floor(Math.random() * getWidth());
int y = (int) Math.floor(Math.random() * getHeight());
int blurrAmount = (int) (Math.floor(Math.random() * 10) + 5);
int alpha = (int) ((Math.random() * 15) + 3);
/**
* Now we draw a image, and apply transparency and a slight blur to it
*/
Kernel kernel = new Kernel(blurrAmount, blurrAmount, blurData);
BufferedImageOp op = new ConvolveOp(kernel);
BufferedImage circle = new BufferedImage(150, 150, BufferedImage.TYPE_INT_ARGB);
Graphics2D circlegfx = circle.createGraphics();
circlegfx.setColor(new Color(255, 255, 255, alpha));
circlegfx.fillOval(20, 20, radius, radius);
circle = op.filter(circle, null);
LightCell bubble = new LightCell(x, y, xSpeed, ySpeed, radius, getDirection(random.nextInt(3)), circle);
lightcells.add(bubble);
}
}
public int random(int min, int max) {
final int n = Math.abs(max - min);
return Math.min(min, max) + (n == 0 ? 0 : random.nextInt(n));
}
#Override
public void paint(Graphics g) {
int w = getWidth();
int h = getHeight();
final Graphics2D g2 = (Graphics2D) g;
GradientPaint gp = new GradientPaint(-w, -h, Color.LIGHT_GRAY, w, h, Color.DARK_GRAY);
g2.setPaint(gp);
g2.fillRect(0, 0, w, h);
long start = System.currentTimeMillis();
for (int i = 0; i < lightcells.size(); i++) {
LightCell cell = lightcells.get(i);
cell.process(g2);
}
System.out.println("Took " + (System.currentTimeMillis() - start) + " milliseconds to draw ALL cells.");
repaint();
}
public String getDirection(int i) {
switch (i) {
case 0:
return "right";
case 1:
return "left";
case 2:
return "up";
case 3:
return "down";
}
return "";
}
private class LightCell {
private int x, y, xSpeed, ySpeed, radius;
private String direction;
private BufferedImage image;
public LightCell(int x, int y, int xSpeed, int ySpeed, int radius, String direction, BufferedImage image) {
this.x = x;
this.y = y;
this.xSpeed = xSpeed;
this.ySpeed = ySpeed;
this.radius = radius;
this.direction = direction;
this.image = image;
}
public void process(Graphics g) {
switch (direction) {
case "right":
moveRight();
break;
case "left":
moveLeft();
break;
case "up":
moveUp();
break;
case "down":
moveDown();
break;
}
g.drawImage(image, x, y, null);
}
private void moveUp() {
x += xSpeed;
y -= ySpeed;
if (y + (radius / 2) < 0) {
y = getHeight() + (radius / 2);
x = (int) Math.floor(Math.random() * getWidth());
}
if ((x + radius / 2) < 0 || (x - radius / 2) > getWidth()) {
y = radius + (radius / 2);
x = (int) Math.floor(Math.random() * getWidth());
}
}
private void moveDown() {
x += xSpeed;
y += ySpeed;
if (y - (radius / 2) > getHeight()) {
y = 0 - (radius / 2);
x = (int) Math.floor(Math.random() * getWidth());
}
if ((x + radius / 2) < 0 || (x - radius / 2) > getWidth()) {
y = getHeight() + (radius / 2);
x = (int) Math.floor(Math.random() * getWidth());
}
}
private void moveRight() {
x += ySpeed;
y += xSpeed;
if (y - (radius / 2) > getHeight() || y + (radius / 2) < 0) {
x = 0 - (radius / 2);
y = (int) Math.floor(Math.random() * getHeight());
}
if ((x - radius / 2) > getWidth()) {
x = 0 - (radius / 2);
y = (int) Math.floor(Math.random() * getWidth());
}
}
private void moveLeft() {
x -= ySpeed;
y -= xSpeed;
if (y - (radius / 2) > getHeight() || y + (radius / 2) < 0) {
x = getWidth() + (radius / 2);
y = (int) Math.floor(Math.random() * getHeight());
}
if ((x + radius / 2) < 0) {
x = getWidth() + (radius / 2);
y = (int) Math.floor(Math.random() * getWidth());
}
}
}
}
If you run that, you will see the cells move at a very high speed, and if you look through the code, you see I call repaint() in the paint method in which I override. I know thats not good to do. But my question is, is their any other way in which I could draw each cell outside of the repaint() loop I have right now, because that causes other components to flash/flicker when I use this in a JFrame with other components.
FYI: Aventually I'd like to achieve something similar to this: Click Here
Thanks!
The issue of flicker is to do with the fact that top level containers are not double buffered. Instead of extending from JFrame (or other top level containers), you should consider using something more like JPanel and override it's paintComponent.
nb- Had it in my head that the OP was extending from JFrame...
Two issues could be causing the flickering. The first is overriding paint, the second is not calling super.paint(g) and the time between the updates. A better solution would be to override paintComponent and make sure you are calling super.paintComponent. Also using something like a javax.swing.Timer to schedule updates and regular intervals would also help...
Only call repaint when you want to encourage the RepaintManager to update you component. Don't call repaint from within any paintXxx method, this will cause a never ending loop of paint requests to schedule onto the event queue, eventually consuiming your CPU
I would avoid doing anything in your paintXxx methods that might take time to perform, this will slow down the rendering process. Instead, I would use a javax.swing.Timer for simple updates or for more complicated processing, a Thread which could be used to update the model before it is rendered to the screen.
This is an example of some simple optimisation process I did to take animation of 500 objects to 4500 with only a slight degration in the overall performance.
Updated
I changed you code slight and it works fine...
I changed your paint method to paintComponent and added super.paintComponent(g)
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int w = getWidth();
int h = getHeight();
final Graphics2D g2 = (Graphics2D) g;
GradientPaint gp = new GradientPaint(-w, -h, Color.LIGHT_GRAY, w, h, Color.DARK_GRAY);
g2.setPaint(gp);
g2.fillRect(0, 0, w, h);
for (int i = 0; i < lightcells.size(); i++) {
LightCell cell = lightcells.get(i);
cell.process(g2);
}
}
And at the end of your constructor I added...
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
});
timer.start();
To update the UI on a regular bases...