BufferedImage ARGB transparency only works when drawn image is moving - java

I just converted my project from RGB to ARGB. The opaque objects all draw fine but when I add transparency to the 'color' (set by hex: 0x55ff0000) of any drawn object they only draw the correct transparency if they are moving.
The problem: Alpha drawn objects seem to 'stack' up every step until they are opaque.
While they remain stationary they quickly become more and more opaque over a second. I'm still learning how the BufferedImages work but I believe i'm missing a piece of code that I require for ARGB instead of just RGB.
public class Window
{
private JFrame frame;
static BufferedImage image;
private Canvas canvas;
private BufferStrategy bs;
private Graphics g;
public Window(Game game)
{
image = new BufferedImage(game.getWidth(), game.getHeight(), BufferedImage.TYPE_INT_ARGB);
canvas = new Canvas();
Dimension s = new Dimension(game.getWidth() * game.getScale(),game.getHeight() * game.getScale());
canvas.setPreferredSize(s);
canvas.setMaximumSize(s);
canvas.setMinimumSize(s);
frame = new JFrame(game.getName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(canvas, BorderLayout.CENTER); //Add canvas to the frame
frame.pack(); //Set frame to size of canvas
//Removed some other 'frame.xxx' lines of code which don't assist the problem.
//frame.repaint();
canvas.createBufferStrategy(2);
bs = canvas.getBufferStrategy();
g = bs.getDrawGraphics();
}
public void update()
{
g.drawImage(image, 0, 0, canvas.getWidth(), canvas.getHeight(), null); //Draws everything to buffer strategy.
bs.show(); //Draws from buffer strategy to canvas.
}
public JFrame getFrame()
{
return frame;
}
}
The only other code I changed to convert to ARGB was this which was 0 beforehand:
public Renderer(Game game)
{
display = ((DataBufferInt)game.getWindow().getImage().getRaster().getDataBuffer()).getData(); //Displays all the data drawn to screen.
}
public void clear()
{
for(int i = 0; i < display.length; i++) //Loops through the screens pixels and clears them.
{
display[i] = 0xff000000; //Clear the screen with opaque black.
}
}
When I change display[i] = 0xff000000; to 0x0f000000 the problem is exacerbated and it has a slow motion effect - but i'm not sure how to increase an already full alpha of "ff" higher.
I make a GIF of the problem, however it's distorted a fair bit: https://giphy.com/gifs/java-RF6stHznOiR9e
Note how it fades back in after I stop moving, and where the circle overlaps it stays opaque.

Related

How to avoid black lines when using BufferStrategy in java

Following a guide that was released in Nov, 2015. I have copied his code verbatim at this point and it still won't work for me. Has something been deprecated?
I have 3 buffers (call them 1,2, and 3). When 2 and 3 are drawn to the screen they have black lines on the top and left sides of the screen. This same code works fine with two buffers.
Bug footage: https://gfycat.com/gifs/detail/GraveCompetentArmyworm
package field;
import javax.swing.JFrame;
import java.awt.*;
import java.awt.image.BufferStrategy;
public class Main extends JFrame{
private Canvas canvas=new Canvas();
public Main() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
setBounds(0,0,1000,1000);
setLocationRelativeTo(null);
add(canvas);
setVisible(true);
canvas.createBufferStrategy(3);
BufferStrategy buffert = canvas.getBufferStrategy();
int p=0;
int ap=0;
while(p<1000) {
if (ap==100){
p++;
ap=0;
}
ap++;
buffert=canvas.getBufferStrategy();
Graphics g = buffert.getDrawGraphics();
super.paint(g);
g.setColor(Color.GREEN);
g.fillOval(p+100, 200, 50, 50);
buffert.show();
}
}
// public void paint(Graphics graphics) {
// super.paint(graphics);
// graphics.setColor(Color.RED);
// graphics.fillOval(100, 100, 100, 100);
//
// }
public static void main(String[] args){
new Main();
}
}
You need to go read the JavaDocs for BufferStrategy and Full-Screen Exclusive Mode API, which a number of important tutorials and examples on BufferStrategy
A BufferStrategy is a means to perform "page flipping", which is independent of the regular painting system. This provides you with "active" control over the painting process. Each buffer is updated off screen and the pushed onto the screen when it's ready.
This generally does not involve the component's own painting system and the intention is to avoid it.
This means you should NOT be calling super.paint(g) on the JFrame or canvas.paint. In fact, as a general rule, you should NEVER call paint manually.
Each time you want to update a buffer, you will be required to "prepare" it. This typically means filling it with some base color
So, based on the example from the JavaDocs, you could do something like...
// Check the capabilities of the GraphicsConfiguration
...
// Create our component
Window w = new Window(gc);
// Show our window
w.setVisible(true);
// Create a general double-buffering strategy
w.createBufferStrategy(2);
BufferStrategy strategy = w.getBufferStrategy();
// Main loop
while (!done) {
// Prepare for rendering the next frame
// ...
// Render single frame
do {
// The following loop ensures that the contents of the drawing buffer
// are consistent in case the underlying surface was recreated
do {
// Get a new graphics context every time through the loop
// Determine the current width and height of the
// output
int width = ...;
int height = ...l
// to make sure the strategy is validated
Graphics graphics = strategy.getDrawGraphics();
graphics.setColor(Color.WHITE);
graphics.fillRect(0, 0, width, height);
// Render to graphics
// ...
// Dispose the graphics
graphics.dispose();
// Repeat the rendering if the drawing buffer contents
// were restored
} while (strategy.contentsRestored());
// Display the buffer
strategy.show();
// Repeat the rendering if the drawing buffer was lost
} while (strategy.contentsLost());
}
// Dispose the window
w.setVisible(false);
w.dispose();
Now, personally, I'd prefer to use Canvas as the base, as it gives a more re-usable solution and it's easier to determine dimensions from

How to ignore JFrame opacity?

I need to achieve the effect of "lights off" while displaying animation. It's currently been done with transparent Jframe, black background with 50% opacity the size of the monitor. then, there is this canvas component which should draw RGBA buffered-image.
Problems pops up when the the JFrame opacity effects the Canvas as well, making it semi transparent. That's what i'm trying to avoid.
//** Window class extends Canvas
public Window(){
GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
int hostMonitorWidth = gd.getDisplayMode().getWidth();
int hostMonitorHeight = gd.getDisplayMode().getHeight();
Dimension dimension = new Dimension(hostMonitorWidth, hostMonitorHeight);
super.setPreferredSize(dimension);
window = new JFrame();
window.setUndecorated(true);
window.setOpacity(0.55f);
window.setLayout(new GridLayout());
window.setSize(hostMonitorWidth, hostMonitorHeight);
window.add(this);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setLocationRelativeTo(null);
window.setVisible(true);
window.requestFocus();
window.setFocusableWindowState(true);
super.createBufferStrategy(3);
}
public void draw(){
BufferStrategy buffer = super.getBufferStrategy();
java.awt.Graphics g = buffer.getDrawGraphics();
g.setColor(Color.BLACK);
g.fillRect(0,0, super.getWidth(), super.getHeight());
g.drawImage(batch.getImage(), 0, 0, super.getWidth(), super.getHeight(), null);
g.dispose();
buffer.show();
}
I have tried couple combinations of the following code with Jpanel, Layered Panes, Jlabel and what not. It's always seems to keep the opacity / throwing unexplainable exceptions / not working for any reason.
Am i doing it the correct way? what am i missing here?
Don't use setOpacity, use setBackground on the JFrame and pass it a alpha based color. This will allow the frame to become transparent without affecting the other components.
You may, however, find that Canvas doesn't like alpha based colors (as it only has an opaque state)

Java graphic.drawImage not appearing

I have a loaded image, but when I try to display it nothing is appearing.
public class JComponentButton extends JComponent implements MouseListener {
BufferedImage image;
public static void main(String[] args) {
JFrame mainFrame = new JFrame("Test Title3");
mainFrame.setSize(400,400);
mainFrame.setLayout(new GridLayout(1,1));
mainFrame.addWindowListener(new WindowAdapter() {
#Override public void windowClosing(WindowEvent windowEvent){
System.exit(0);
}
});
JPanel controlPanel = new JPanel();
controlPanel.setLayout(new FlowLayout());
JComponentButton button = new JComponentButton();
button.setSize(64,64);
controlPanel.add(button);
mainFrame.add(controlPanel);
mainFrame.setVisible(true);
System.out.println("finishing");
}
public JComponentButton(){
super();
try {
this.image = ImageIO.read(new File("resources/default.png"));
} catch (IOException e) {
System.out.println("did not load image");
System.exit(1);
}
enableInputMethods(true);
addMouseListener(this);
}
#Override public void paintComponent(Graphics g){
super.paintComponent(g);
System.out.println(image.getHeight() + "," + image.getWidth());
g.drawImage(image, 64, 64, null);
}
The image seems to be loaded as 64,64 is printed into the console. However where it should be appearing on the window is blank. I drew a square there without a problem using g.fillRect. So it seems like the problem has to be with g.drawImage. I also tried changing the perspective from null to this, but nothing changed.
In your code the method g.drawImage(image, 64, 64, null) will draw your image at it's full resolution starting at the offset at 64, 64.
Javadoc for drawImage:
Draws as much of the specified image as is currently available. The image is drawn with its top-left corner at (x, y) in this graphics context's coordinate space. Transparent pixels in the image do not affect whatever pixels are already there.
This means that whilst your image is indeed being drawn, it's being drawn outside the visible co-ordinate space of your button.
To fix the issue, replace the 64, 64 with 0, 0 if the image is sized at the component's size or use the more explicit method drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) to specify what the image should be resized to whilst in the button.
E.g.
g.drawImage(image, 0, 0, null); //draws image at full resolution
g.drawImage(image, 0, 0, 64, 64, null); //draws image at offset 0, for max resolution of 64x64
You can't view the image is because the image is drawn outside of the button.
since you don't have any panel outside the button,it cannot be drawn.
The reason is,the x and y axis of the image that you have specified,i.e.Top left corner,are 64,64 and the button is of size 64,64.Thus,this image will be drawn at bottom right of the button,that is the corner point of the button.
Thus you should specify image points as 0,0 (these are the top-left corner of image) so that it will cover the 0,0(these are the top-left corner of button) point of the button.
Also,you must specify the width and height of the image.So,that it will not go out side of bounds of button.If you don't want to specify the width and height then you must decrease the pixels of the image to the size of button(at least).
you can rewrite the code as:
g.drawImage(image,0,0,64,64,null);

How to display the whole contents of a large JPanel in a small JFrame?

I have a JPanel with a size of 816 x 1056 pixels (size of a standard typewriting paper). Is there any way of displaying the whole JPanel and its content without altering its actual size, in a small JFrame? Is there a way of like zooming it out?
I can't think of an easy method to zoom a JPanel out, but you can do it via double-buffering:
private void paintPanel (Graphics2D g)
{
// here goes the code of your paint() or paintComponent() method
}
/** Draw content into an Image, shrink it, and display it. */
protected void paintComponent (Graphics g)
{
// paint image
BufferedImage img = new BufferedImage(MAX_WIDTH, MAX_HEIGHT, BufferedImage.TYPE_INT_ARGB);
paintPanel(img.createGraphics());
// shrink image and paint it
g.drawImage(img, 0,0, WIDTH,HEIGHT, 0,0, MAX_WIDTH,MAX_HEIGHT, null);
}
This will paint everything on a BufferedImage with the size MAX_WIDTH x MAX_HEIGHT. Then, the Image will be shrinked into the size WIDTH x HEIGHT and then displayed.

How to resize image Height to JFrame Height?

I have an image that is too tall for my JFrame even when it is maximized. I want to dynamically resize it so that the image will never be clipped by the top or bottom of the JFrame. I have inserted the image within a JLabel as an ImageIcon. I have tried setting the maximum size to no avail. How do I ensure that the height of the image will never be larger than the JFrame? I would ideally like to keep the ratio of height to width constant. The image is in a portrait orientation. Any ideas?
public class myClass extends JFrame {
private void initGUI(){
pane = getContentPane();
pane.setLayout(new BorderLayout());
next = new JButton("Next");
previous = new JButton("Previous");
page = new JLabel(loadImg());
page.setMaximumSize(this.getSize());
pane.add(next, BorderLayout.EAST);
pane.add(previous, BorderLayout.WEST);
pane.add(page, BorderLayout.CENTER);
}
}
I would ideally like to keep the ratio of height to width constant.
Check out Darryl's Stretch Icon. It will shrink/grow depending on the space available, while maintaining the width/height ratio.
You can try overriding the paintComponent(Graphics g) method and drawing the resized image yourself.
page = new JLabel(loadIMG()){
#Override
paintComponent(Graphics g)
{
//So we don't kill default behaviour
super.paintComponent(g);
int scaledWidth, scaledHeight;
//pseudo-code
scale and store into scaledWidth and scaledHeight;
render with g.drawImage(icon, x, y, scaledWidth, scaledHeight, null);
}
};

Categories

Resources