I'm trying to paint a rectangle on my application in a red shade but I need to make it sort of transparent so that the component under it will still show. However I still want that some colour will still show. The method where I'm drawing is the following:
protected void paintComponent(Graphics g) {
if (point != null) {
int value = this.chooseColour(); // used to return how bright the red is needed
if(value !=0){
Color myColour = new Color(255, value,value );
g.setColor(myColour);
g.fillRect(point.x, point.y, this.width, this.height);
}
else{
Color myColour = new Color(value, 0,0 );
g.setColor(myColour);
g.fillRect(point.x, point.y, this.width, this.height);
}
}
}
Does anyone know how I can make the red shade a bit transparent? I don't need it completely transparent though.
int alpha = 127; // 50% transparent
Color myColour = new Color(255, value, value, alpha);
See the Color constructors that take 4 arguments (of either int or float) for further details.
Try this: (but it will works for Graphics2D objeccts not for Graphics)
protected void paintComponent(Graphics2D g) {
if (point != null) {
int value = this.chooseColour(); // used to return how bright the red is needed
g.setComposite(AlphaComposite.SrcOver.derive(0.8f));
if(value !=0){
Color myColour = new Color(255, value,value );
g.setColor(myColour);
g.fillRect(point.x, point.y, this.width, this.height);
}
else{
Color myColour = new Color(value, 0,0 );
g.setColor(myColour);
g.fillRect(point.x, point.y, this.width, this.height);
}
g.setComposite(AlphaComposite.SrcOver);
}
}
Related
I am currently in the process of coding a kind of drawing tool in Processing. I want to be able to draw a black line with the mouse, but have it fade away again. For this I first thought of working with a black ellipse that fades from black to white. This ellipse is then linked to the mouse pointer via mouseX and mouseY. Because I want each drawn ellipse to fade from black to white individually (to create the effect that the line fades again), I thought of working with objects. This works so far, but the fading does not work yet.
I want the ellipse at the mouse pointer to always be black and then only change to white once the mouse pointer has been moved further or drawn further... just like a fade...
Should I use an ArrayList for this? And say that a new object should be drawn at the MouseX and MouseY position per frame? Do I need PVector for this?
This is what I came up for this so far:
float counter;
PGraphics pg;
Brush myBrush;
void setup() {
size(600, 600);
pg = createGraphics(width, height);
}
void draw() {
//background(255);
color from = color(0);
color to = color(255);
color faded = lerpColor(from, to, counter);
myBrush = new Brush(faded, mouseX, mouseY);
myBrush.display();
}
class Brush {
color tempC;
float xpos;
float ypos;
color c;
Brush(color tempC, float tempXpos, float tempYpos) {
c = tempC;
xpos = tempXpos;
ypos = tempYpos;
}
void display() {
noStroke();
fill(c);
ellipse(xpos, ypos, 50, 50);
counter = counter + 0.01;
}
}
The issue you're struggling with here is that you can only know how to fade the line once the line has been drawn, but you draw it while the user move the mouse.
To deal with this issue, I would suggest to store every information needed to draw the line in an object, and update it as the user is drawing. As you guessed, you could totally use an ArrayList to achieve this.
I modified your code quite heavily to simplify my own life, and I commented the result so it would be easier for you to re-cast it into your own project on your own terms.
I used objects to keep everything tidy. The logic here goes as follow: The Canvas object contains the drawing. It's made of Line objects, which are themselves made of Dots.
Here's the commented code:
Canvas canvas;
void setup() {
size(600, 600);
canvas = new Canvas();
}
void draw() {
// instead of drawing only once, we're going to save every line the user draw in teh canvas object
background(255);
// the canvas object has to calculate stuff, then display itself
canvas.Update();
canvas.Display();
}
// setting up the mousePressed and mouseReleased events so the canvas object "knows"
void mousePressed() {
canvas.mouseDown = true;
}
void mouseReleased() {
canvas.mouseReleased = true;
canvas.mouseDown = false;
}
class Canvas {
boolean mouseDown;
boolean mouseReleased = true;
ArrayList<Line> lines = new ArrayList<Line>(); // every line will be stored in this list
Brush brush; // the brush object can be modified with different sizes or colors
Canvas() {
// here i use a default brush, but you can experiment different colors or sizes
brush = new Brush(color(0, 200, 0), color(0), color(255), 50);
}
void Update() {
brush.highlight = mouseDown; // so the user has visual feedback while drawing
if (mouseDown) {
if (mouseReleased) { // if this is a "new" line, add a line object to store it
lines.add(new Line(brush.colorFrom, brush.colorTo));
mouseReleased = false;
}
// add a dot at the mouse's current position, then update the fading
lines.get(lines.size()-1).Add(new Dot(mouseX, mouseY, brush.diameter, brush.colorFrom));
lines.get(lines.size()-1).ApplyFade();
}
}
void Display() {
// for every Line, draw every Dot... then don't forget to display the brush!
for (Line l : lines) {
for (Dot d : l.dots) {
d.Display();
}
}
brush.Display();
}
}
// A line is a bunch of dots and two colord (for the fade effect)
class Line {
ArrayList<Dot> dots = new ArrayList<Dot>();
color colorFrom, colorTo;
Line(color colorFrom, color colorTo) {
this.colorFrom = colorFrom;
this.colorTo = colorTo;
}
void Add(Dot d) {
dots.add(d);
}
// This method calculate how many dots there are in the line to better distribute the shades of the fade
void ApplyFade() {
for (int i=0; i<dots.size(); i++) {
Dot d = dots.get(i);
d.c = lerpColor(colorFrom, colorTo, (float) i/dots.size());
}
}
}
// a Dot has a size, a position and a color
class Dot {
float xpos;
float ypos;
float diameter;
color c;
Dot(float xpos, float ypos, float diameter, color c) {
this.xpos = xpos;
this.ypos = ypos;
this.diameter = diameter;
this.c = c;
}
void Display() {
noStroke();
fill(c);
ellipse(xpos, ypos, diameter, diameter);
}
}
// this class is overdesigned so in the future you can change the brush's characteristics like the fade'S colors or simply it's size
class Brush {
boolean highlight;
float diameter, xpos, ypos;
color circleColor, colorFrom, colorTo;
Brush(color circleColor, color colorFrom, color colorTo, float diameter) {
this.circleColor = circleColor;
this.colorFrom = colorFrom;
this.colorTo = colorTo;
this.diameter = diameter;
}
void Display() {
stroke(circleColor);
strokeWeight(5);
noFill();
ellipse(mouseX, mouseY, diameter, diameter);
if (highlight) { // if the mouse's button is down, give visual feedback about the brush
stroke(0);
strokeWeight(4);
ellipse(mouseX, mouseY, diameter, diameter);
stroke(255);
strokeWeight(3);
ellipse(mouseX, mouseY, diameter, diameter);
}
}
}
Hope this helps. I'll be around if you have questions about the code. Have fun!
Here is a version with an ArrayList and Object... The problem here is that all ellipse objects fade away at the same time... I would like to achieve that each ellipse is first drawn black and then after time X becomes white... Like a fingerprint on a sensor for example... A lot of pressure: black, little pressure (or no pressure): fade to white... Later on I want to draw everything on a PGraphics layer – therefore there is alyready this variable...
float counter;
PGraphics pg;
ArrayList<PVector> positionsList;
Brush myBrush;
void setup() {
size(600, 600);
positionsList = new ArrayList<PVector>();
pg = createGraphics(width, height);
}
void draw() {
background(255);
color from = color(0);
color to = color(255);
color faded = lerpColor(from, to, counter);
for (PVector p : positionsList) {
myBrush = new Brush(faded, p.x, p.y);
myBrush.display();
}
positionsList.add(new PVector(mouseX, mouseY));
counter = counter + 0.01;
}
class Brush {
color tempC;
float xpos;
float ypos;
color c;
Brush(color tempC, float tempXpos, float tempYpos) {
c = tempC;
xpos = tempXpos;
ypos = tempYpos;
}
void display() {
noStroke();
fill(c);
ellipse(xpos, ypos, 50, 50);
}
}
I want to render something on my GlassPane. The problem is, that if i move the rendered lines around, the previously rendered pixels have still the same color.
I can not use g.clearRect because it doesn`t clears the transparency.
Thats my rendering code:
Graphics2D g2 = (Graphics2D) g;
for(LinePath line : lines)
{
for(int i = 0; i < line.points.length; i+=2)
{
if(i != 0)
{
g.drawLine((int)line.points[i-2],(int)line.points[i-1],(int)line.points[i],(int)line.points[i+1]);
}
}
}
//Clearing alpha
Area area = new Area();
// This is the area that will filled...
area.add(new Area(new Rectangle2D.Float(0, 0, getWidth(), getHeight())));
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,0.0f));
g2.fill(area);
And here is the result:
clearRect should work but you have to reset your alpha composite before using it.
Ex:
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,1.0f));
I am using a paintComponent Class in a project of mine, and I am currently wondering how I can decrease the size of the rectangle from the top making it's way downwards.
This is the part of the code:
public Battery(){
super();
firstTime = true;
f = new Font("Helvetica", Font.BOLD, 14);
m = Toolkit.getDefaultToolkit().getFontMetrics(f);
}
public void paintComponent(Graphics g){
if(firstTime){
firstTime = false;
batteryLevel = 1 + this.getHeight();
decr = batteryLevel / 20;
}else{
g.setColor(Color.RED);
g.fillRect(1, 0, this.getWidth(), this.getHeight());
g.setColor(Color.GREEN);
g.fillRect(1, 0, this.getWidth(), batteryLevel);
g.setColor(Color.BLACK);
g.setFont(f);
g.drawString("TEST", (getWidth() - m.stringWidth("TEST")) / 2 , this.getHeight() / 2);
}
}
public void decreaseBatteryLevel(){
batteryLevel -= decr;
this.repaint();
}
PS. Sorry if I did something wrong, I'm new to this forum.
As you want the visible battery level to descend you will want to increase your Y co-ordinate in relation to the value of batteryLevel. You could use:
g.fillRect(1, getHeight() - batteryLevel, getWidth(), batteryLevel);
Instead
g.fillRect(1, 0, this.getWidth(), batteryLevel);
Do
g.fillRect(1, batteryLevel, this.getWidth(), getHeight() - batteryLevel);
Also maybe repaint(50L) instead of repaint().
If your question meant: how to animate a change in the battery level.
Use a javax.swing.Timer:
int toPaintBatteryLevel = batteryLevel;
// In the paintComponent paint upto toPaintBatteryLevel.
Timer timer = new Timer(100, new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (toPaintBatteryLevl == batteryLevel) {
return;
}
if (toPaintBatteryLevl > batteryLevel) {
--toPaintBatteryLevel; // Animate slowly
} else {
toPaintBatteryLevel = batteryLevel; // Change immediately
}
repaint(50L);
};
});
timer.start();
For ease of coding, there is a permanent timer. And externally one changes the batteryLevel,
and the time determines the toPaintBatteryLevel, which paintComponent uses to paint.
import javax.swing.*;
import java.awt.*;
public class JFrameAnimationTest extends JFrame {
public static void main(String[] args) throws Exception{
AnimationPanel animation = new AnimationPanel();
JFrameAnimationTest frame = new JFrameAnimationTest();
frame.setSize(600, 480);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(animation);
frame.setVisible(true);
for(int i = 0; i < 100; i++) {
animation.incX(1);
//animation.incY(1);
animation.repaint();
Thread.sleep(10);
}
}
}
class AnimationPanel extends JPanel {
int x = 10;
int y = 10;
public AnimationPanel() {
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
g.drawRect(x, y, 20, 20);
g.fillRect(x, y, 20, 20);
}
protected void incX(int X) {
x += X;
}
protected void incY(int Y) {
y += Y;
}
}
So anyways theres my code. It probably looks a bit jumbled as I am not used to stackoverflow just yet so I apologize.
Here's my question: This program makes this small rectangle slowly move to the right; how can I add rotation to the rectangles movement during that time period?
Note: I haven't actually compiled this code, but you get the gist.
public void paintComponent( Graphics g )
{
super.paintComponent( g );
Graphics2D g2d = (Graphics2D) g;
// The 20x20 rectangle that you want to draw
Rectangle2D rect = new Rectangle2D.Double( 0, 0, 20, 20 );
// This transform is used to modify the rectangle (an affine
// transform is a way to do operations like translations, rotations,
// scalings, etc...)
AffineTransform transform = new AffineTransform();
// 3rd operation performed: translate the rectangle to the desired
// x and y position
transform.translate( x + 10, y + 10 );
// 2nd operation performed: rotate the rectangle around the origin
transform.rotate( rotation );
// 1st operation performed: translate the rectangle such that it is
// centered on the origin
transform.translate( -10, -10 );
// Apply the affine transform
Shape s = transform.createTransformedShape( rect );
// Fill the shape with the current paint
g2d.fill( s );
// Stroke the edge of the shape with the current paint
g2d.draw( s );
}
Also note that you should really be using something like a javax.swing.Timer when you modify x, y, and rotation and when you call repaint(). That way all of it happens on the event dispatch thread.
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);
}