Drawing specific parts of Graphics2D - java

I have written a game, and now I would like to learn how to manipulate camera.
My drawing consists of this:
private void render()
{
// CREATE THE GRAPHICS
Graphics2D g = (Graphics2D) bufferStrategy.getDrawGraphics();
// MAKE THE PICTURE WHERE WE WILL PAINT EVERYTHING AT ONCE
g.clearRect(0, 0, WIDTH, HEIGHT);
// PAINT ANYTHING WE NEED HERE
render(g);
g.dispose();
// SHOW THE WHOLE IMAGE RATHER THAN PAININT ONE OBJECT AT A TIME
bufferStrategy.show();
}
My BufferStrategy is created like so:
canvas = new Canvas();
// once again I add 1 because java is stupid that's why
canvas.setBounds(0, 0, WIDTH+1, HEIGHT+1);
//canvas.setBounds(bounds);
canvas.setIgnoreRepaint(true);
// SET GRAPHICS DEVICE
canvas.createBufferStrategy(2);
bufferStrategy = canvas.getBufferStrategy();
In the render function I draw my game world with all the platforms where they should be. Now if the game world is bigger than the window size, and I would like to be able to focus on the character and draw the part of the world where character is, how would I do that? So let's say my window is 300 x 300, and my game world is 900 x 900, and my character is located in the middle so that I would like to display the part of the graphics where x is 150 y is 150 and the width and height is 300 x 300.
So how would I go about taking that Graphics2D and shifting it over by specified values? I am very sorry if this question has been answered or is easy, but I am new to this and really would like some help.
Thanks for any help in advance.
I have tried all I know to no avail.

So how would I go about taking that Graphics2D and shifting it over by specified values?
Why must you shift the Graphics2D? Why not create an image that extends beyond the viewport and simply shift the viewport. Then if the user gets close to the edge of the viewport, add an image to the leading edge and lop off a portion on the trailing edge.

This is what affine transformations are for.
http://docs.oracle.com/javase/1.4.2/docs/api/java/awt/geom/AffineTransform.html
Shift the character to the center of the screen by applying a shift.
In your paint component, save the current transformation.
Create a new transformation that will then shift the screen to where you would like it to be.
Set Graphics2D to use your new transformation.
Draw the character at the original coordinates
Set Graphics2D to use the original transformation.

Related

Rotate text present on JLabel without rotating border

I want to rotate the text present on my JComponent to vertical, JComponent also contains border painted in paintComponent method, I don't want to rotate that border, only text.
I already used graphics2D rotate function, but it rotate component border as well, which fails when my component is rectangle.
Please suggest me any approch to rotate only text.
This is currently my JComponent :
And what I want :
Actually it's not duplicate. I already used below code:
Graphics2D g2 = (Graphics2D) g;
g2.rotate(Math.PI / 4, bi.getWidth() / 2, bi.getHeight() / 2);
but problem is that, it also rotate border, I don't want that.
All the solutions given are not working, it rotate the border as well. I don't want to rotate the border, only text.
This is what I get after rotate with some angle:
Remember that the Graphics object has a lot of state in it. This includes the current transformation. Your current code modifies the state and does not restore it to the original situation.
After drawing your image, you should "undo" the rotation. This can be done either by rotating in the other the direction or by creating a new graphics object (g2.create()) specifically to draw the rotated content. If you do the latter, make sure to dispose() the temporary graphics object you created.

Alternative to using Graphics2D Area?

I'm making a game where you start by drawing your map. At the moment it works by having an Area instance variable and then when the player clicks/drags the mouse it adds Ellipse2Ds to it. Here's what I mean:
Area land = new Area();
And then in the MouseDragged method
Point2D mouse = e.getPoint();
Ellipse2D ter = new Ellipse2D.Double(mouse.getX() - drawRad, mouse.getY() - drawRad, drawRad*2, drawRad*2);
land.add(new Area(ter));
And then basically the same except land.subtract(new Area(ter)) for erasing.
My problem with this is that after doing a lot of drawing it becomes very slow to draw the Area. the other problem is that I would like to be able to easily get the outline of the drawing, and I haven't found a good way of doing that using Areas. Using area.getPathIterator is not nearly accurate enough.
So I'm wondering what other ways of saving drawings are? I can't just have an array of Ellipses because then erasing wouldn't work.
Thanks!
If you are drawing the same thing over and over it may be worth while to draw it to an image once or load it from a file at start up and then just paint the image rather than painting all the individual shapes.
To load from a file, put your picture file in the same directory as your .java file and load:
BufferedImage img = ImageIO.read(this.getClass().getResource("picture.png"));
To draw on the image once:
BufferedImage img = new BufferedImage(width,height, BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D g = img.createGraphics();
In your paintComponent method you draw the image somewhere.
g.drawImage(img, 0, 0, this);
If you have something moving over a background, instead of adding and subtracting the moving item. Just draw the background and then draw the thing that moves, drawing the background will effectively erase the old position with less computation.

How to detect mouse over on drawn image with awt.Graphics.drawImage() when transformed

In a Java Swing panel I draw an image with arbitrary transfomation e.g.:
public void paintComponent(Graphics g){
AffineTransform transform = AffineTransform.getTranslateInstance(x, y);
AffineTransform rotateInstance AffineTransform.getRotateInstance(rotX, rotY);
transform.concatenate(rotateInstance);
g.setTransform(transform);
g.drawImage(image, 0, 0 , null);
}
What is the best way to detect mouse over on such an image?
Even better: the best way to detect mouse over only on non-transparent pixels?
Construct a Shape that encloses the image, and use the AffineTransform method createTransformedShape() to rotate the Shape along with the image. The Shape method contains() will allow for position testing in Component relative coordinates. Complete examples are seen here and here. Two approaches to inverse coordinate transformation are cited here.

How do I rotate a graphics object, without changing it's location?

I got the following code:
import java.awt.*;
import javax.swing.*;
public class GraphicPanel extends JPanel {
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setFont(new Font("Arial",Font.BOLD,24));
g2d.drawString("A",300,250);
g2d.rotate(45 * Math.PI/180);
g2d.drawString("B",300,250);
}
}
It should rotate B and put it in the same place A is, but for some reason, B apears at a totally different location.
Pretty sure I understand why that is. It's because the entire coordination system is manipulated when scaling, rotating etc (am I correct?)
Anyway, I'd like to know how to rotate or scale an object, and still have it appear where it was before. For example in a game that keeps updating and displaying itself every 10 milliseconds, where a sprite is being rotated, but still stays where it was 10 millisceonds before.
EDIT: Tried to search Google beforehand, guess the wording for this question is kind of tricky because I found nothing helpful.
Thanks a lot :)
When you call the rotate method on a Graphics2D object, you're rotating the entire canvas by that angle around the origin. So, drawing a shape at (300, 250) after the rotation will draw it at whatever that point maps to if you started at (300, 250) and then rotated it 45 degrees around (0, 0).
You should use the other form of rotate that takes the angle as well as the x- and y-coordinates of the rotation pivot point, and pass your point (300, 250) into it. (Although, if you want to rotate around the center of the character or string, you'll need to adjust that point a bit.)
The rotation is done around the origin, e.g. 0,0 of the coordinate system. If you want to rotate around a different point you need to translate the origin, rotate, and translate back, e.g.:
g2d.translate(300, 250);
g2d.rotate(45 * Math.PI/180);
g2d.translate(-300, -250);
g2d.drawString("B",300,250);
Of course you have to take the size of the object you want to rotate in account. This rotates around the point where drawString would paint, which is the left baseline of the string. Probably you would want to rotate around the center; then you have to measure the object before and add those values to the translation.

Java: Filling a BufferedImage with transparent pixels

I have an off-screen BufferedImage, constructed with the type BufferedImage.TYPE_INT_ARGB. It can contain anything, and I'm looking for a way to (fairly efficiently) completely overwrite the image with transparent pixels, resulting in an 'invisible' image.
Using something like this:
(bufimg.getGraphics()).setColor(new Color(10, 10, 100, 0));
(bufimg.getGraphics()).fillRect (0, 0, x, y);
Has no effect. One possible method might be just to write over every pixel in the BufferedImage, but I'm not sure this is the best solution. How would you do it?
[edit]
The Graphics documentation advises against using clearRect for off-screen images, but I have tried it with the same results as above.
[edit2]
After experimenting with MeBigFatGuy's code (thanks!), it does clear an image. But it also stops further painting to that image (or appears to). This code for example:
BufferedImage img = new BufferedImage (600, 600, BufferedImage.TYPE_INT_ARGB);
Graphics g = img.createGraphics ()
g.drawLine (100, 100, 500, 500);
AlphaComposite composite = AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f);
g.setComposite(composite);
g.setColor(new Color(0, 0, 0, 0));
g.fillRect(0, 0, 600, 600);
graphicsAI.setColor(new Color (10, 10, 10, 255));
graphicsAI.drawLine (100, 100, 500, 500);
Results in nothing seen on the image (I'm drawing the image to a JPanel). Is this something to do with the addition of alpha values?
After you clear the background with the CLEAR composite, you need to set it back to SRC_OVER to draw normally again. ex:
//clear
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR));
g2.fillRect(0,0,256,256);
//reset composite
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
//draw
g2.setPaint(Color.RED);
g2.fillOval(50,50,100,100);
You could get the underlying int[] array of your BufferedImage (make sure to use a compatible format: that is, one that is backed by an int[]).
Then fill the int[] with ints whose alpha value are 0 (0 will do ; )
A System.arraycopy will be very fast.
You have to know that directly writing in the int[] is a lot faster than using setRGB.
Now BufferedImage are a bit of a black art in Java: depending on what you're doing and on which platform/JVM you're doing it, you may lose hardware acceleration (which may never have been there in the first place anyway). In addition to that, you may very well not care at all about hardware acceleration anyway because you may not be working on, say, a game requiring 60+ FPS to be playable etc.
This is a very complicated topic and there's more than one way to skin the BufferedImage cat. As far as I'm concerned I work directly in the int[] when I've got to mess at the pixel level because I think it makes much more sense than trying to use higher-level drawing primitives and I do really don't care about the potential lost of hardware acceleration.
If you cast the Graphics object to a Graphics2D object, you can set a Composite object thru
AlphaComposite composite = AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f);
Graphics2D g2d = (Graphics2D) image.getGraphics();
g2d.setComposite(composite);
g2d.setColor(new Color(0, 0, 0, 0));
g2d.fillRect(0, 0, 10, 10);
For the sake of completeness, here is a working, testing, and fast function that is cross-platform compliant.
static public BufferedImage createTransparentBufferedImage(int width, int height) {
// BufferedImage is actually already transparent on my system, but that isn't
// guaranteed across platforms.
BufferedImage bufferedImage = new BufferedImage(width, height,
BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = bufferedImage.createGraphics();
// To be sure, we use clearRect, which will (unlike fillRect) totally replace
// the current pixels with the desired color, even if it's fully transparent.
graphics.setBackground(new Color(0, true));
graphics.clearRect(0, 0, width, height);
graphics.dispose();
return bufferedImage;
}
Despite you saying it doesn't work, I used clearRect quite fine.
Clears the specified rectangle by filling it with the background color
of the current drawing surface. This operation does not use the
current paint mode.
Beginning with Java 1.1, the background color of offscreen images may
be system dependent. Applications should use setColor followed by
fillRect to ensure that an offscreen image is cleared to a specific
color.
Fills the specified rectangle. The left and right edges of the
rectangle are at x and x + width - 1. The top and bottom edges are at
y and y + height - 1. The resulting rectangle covers an area width
pixels wide by height pixels tall. The rectangle is filled using the
graphics context's current color.
It is not clearly stated here that one will set the rectangle to the background color, while the other will paint with the foreground color on top of the current colors, but it's what it seems to do.
This is pure speculation, but I think the note about offscreen images relates to Graphics objects obtained from offscreen AWT components, as they are native. I can hardly imagine how the background color of a BufferedImage could be system dependent. As the API doc is for Graphics, this could be a generalization not applying to the BufferedImage case.
My testing code:
JFrame jf = new JFrame();
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
BufferedImage img = new BufferedImage(200, 300, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = img.createGraphics();
//fill right half with opaque white
g.setColor(Color.WHITE);
g.fillRect(100, 0, 100, 300);
//leave top third as it is
//fill middle third with transparent color
g.setColor(new Color(0, true));
g.fillRect(0, 100, 200, 100);
//clear bottom third with transparent color
g.setBackground(new Color(0, true));
g.clearRect(0, 200, 200, 100);
g.dispose();
jf.add(new JLabel(new ImageIcon(img)));
jf.pack();
jf.setVisible(true);
the result is two white squares, top right. Where no white was painted, or clearRect was used to overwrite the white, the result is a light gray, the frame's default background color.
Performance-wise, it's regular drawing. arraycopy might well be faster, I don't know, but at least this is likely hardware accelerated just as any other drawing operation.
A plus point versus the array solution is a) no additional memory and b) independence from the color model; this should work no matter how the image was set up.
A minus point versus the Composite solution is that it only allows clearing rectangles; setting the composite allows you to clear any kind of shape.
Setting the background of the graphics Object seems to do the job:
g.setBackground(new Color(0, 0, 0, 0));
(at least when drawing images for scaling purposes)

Categories

Resources