public void draw(Graphics2D g) {
// draw background
g.drawImage(bgImage, 0, 0, null);
g.drawImage(bgImage, 700, 0, null);
g.drawImage(bgImage, 0, 500, null);
g.drawImage(bgImage, 700, 500, null);
AffineTransform transform = new AffineTransform();
for (int i = 0; i < NUM_SPRITES; i++) {
Sprite sprite = sprites[i];
// translate the sprite
transform.setToTranslation(sprite.getX(),
sprite.getY());
// if the sprite is moving left, flip the image
if (sprite.getVelocityX() < 0) {
transform.scale(-1, 1);
transform.translate(-sprite.getWidth(), 0);
}
// draw it
g.drawImage(sprite.getImage(), transform, null);
}
}
}
I was looking at a draw method in this class. The method is called with an animation loop, it draws a set of animated sprites on the screen that pass the method the correct frame in their animation depending on a timer. The sprites are also moving around the screen at a randomly generated velocity bouncing off the edges of the frame. Depending on whether they are traveling left or right the draw method transforms the image so the sprite is facing in the correct direction. All that transform information is contained in the transform for the drawImage method which I'm used to seeing as g.drawImage(Image, x, y, null); though obviously you can't do that here as you would lose the image flipping capabilities. Is there a way to do it though? Is there a way to transform the image scale but set its location by coordinate?
I ask because transforming is a more processors intensive activity and if lots of sprites need to be active at once it might really slow everything down. BONUS QUESTION: Is this a legitimate concern or should I not be too worried considering the strength of modern systems.
Related
This problem seemed very obvious for me to solve, but whatever I try, it doesn't work. What I'm trying to do is to incorporate a mini-version of my PlayScreen in a ScrollPane as a tutorial where you can read text and try it out immediately.
Because I didn't find any better solution to add this to the Table inside the ScrollPane, I edited the draw() method of the PlayScreen to take the ScrollPane.getScrollPercentY() and offset the camera of the PlayScreen accordingly.
What I want to do now is to only render only part of the viewport that would be normally visible in the real game. Subsequently, I want to be able to control the size and position of this "window".
I also want to be able to resize and move the content, while cutting off the edges that are not visible to the camera. This is what I tried inside the PlayScreenDraw:
public void draw(final float yOffset,
final int xTiles,
final int yTiles) {
view.getCamera().position.y = yTiles / 2f - yOffset * yTiles / HEIGHT; // HEIGHT = 800
view.getCamera().position.x = xTiles / 2f;
view.setWorldSize(xTiles, yTiles); //Do i even need to change the world size?
b.setProjectionMatrix(view.getCamera().combined);
b.begin();
...
b.end();
view.update(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
}
What this gives me, in terms of the picture above, is this
How do I need to change the viewport and/or the camera? Btw., this is how i set the two up:
cam = new OrthographicCamera();
cam.setToOrtho(false, WIDTH, HEIGHT); // WIDTH = 8, HEIGHT = 16
batch.setProjectionMatrix(cam.combined);
view = new FitViewport(WIDTH, HEIGHT, cam);
The Pixmap class can help you achieve what you want since you stated that you wanted to "cut off" the parts outside of the green selection box.
You need to render what the camera sees to an FBO and then get the pixmap from the FBO itself.
Framebuffer Objects are OpenGL Objects, which allow for the creation of user-defined Framebuffers. With them, one can render to non-Default Framebuffer locations, and thus render without disturbing the main screen.
-- OpenGL wiki
// Construct an FBO and keep a reference to it. Remember to dispose of it.
FrameBuffer fbo = new FrameBuffer(Format.RGBA8888, width, height, false);
public void render() {
//Start rendering to the fbo.
fbo.begin();
//From the camera's perspective.
batch.setProjectionMatrix(camera.combined);
batch.begin();
//Draw whatever you want to draw with the camera.
batch.end();
// Finished drawing, get pixmap.
Pixmap pixmap = ScreenUtils.getFrameBufferPixmap(0, 0, width, height);
//Stop drawing to your fbo.
fbo.end();
}
After getting the pixmap you can iterate through the pixels and set the alpha of the pixels outside your green selection window to 0 making them invisible or "cutting them off"
I am currently profiling my Java-2d-Application (Game-Engine for learning purposes).
Since I cannot guarantee that each frame is overwritten completely, I have to clear the background to a solid color (i.e. Color.BLACK) each frame.
The way I do it is SLOW (about 40% of drawing-time in my environment goes to just clearing the background).
First I get a graphics-context from the bufferStrategy, then I draw a [PickYourColor]-Rectangle in full resolution on it before drawing the actual content.
// fill background with solid color
graphics.setColor(Color.BLACK);
graphics.fillRect(
0,
0,
(int) bounds.getWidth(),
(int) bounds.getHeight());
Is there a more efficient, platform-independant, way to clear the background to a solid color each frame using Java-2D (this is not a LWJGL-question)?
What I'm looking for is a graphics.clearBackgroundToSolidColor(Color color) - Method...
By request: here the full rendering method (it's not an SSCCE, but it's pretty short and self explanatory)
/**
* Create a new graphics context to draw on and
* notify all RenderListeners about rendering.
*/
public void render() {
///// abort drawing if we don't have focus /////
if (!this.windowJFrame.hasFocus()) {
return;
}
///// draw and create new graphics context /////
Graphics2D graphics = null;
do {
try {
graphics = (Graphics2D) this.bufferStrategy.getDrawGraphics();
Rectangle2 bounds = this.getBounds();
// set an inexpensive, yet pretty nice looking, rendering directives
graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
// fill background with solid color
graphics.setColor(Color.BLACK);
graphics.fillRect(
0,
0,
(int) bounds.getWidth(),
(int) bounds.getHeight());
// notify all listeners that they can draw now
synchronized (this.renderListeners) {
for (RenderInterface r : this.renderListeners) {
r.render(graphics, bounds);
}
}
// show buffer
graphics.dispose();
this.bufferStrategy.show();
} catch (Exception e) {
Logger.saveMessage("window", Logger.WARNING, "Caught exception while drawing frame. Exception: " + e.toString());
}
} while (this.bufferStrategy.contentsLost());
}
I can't say why the fillRect is slow, but you can try creating an Image and draw it as bg. not sure if it will be faster though.
try:
BufferedImage bi = new BufferedImage(500, 500, BufferedImage.TYPE_INT_RGB);
int[] imageData =((DataBufferInt)bi.getRaster().getDataBuffer()).getData();
Arrays.fill(imageData, 0);
then instead of fillRect draw the Image:
graphics.drawImage(bi, 0, 0, null);
Tell me how it went(I have my doubts about this).
If you would like to clear the entire background than try canvas.drawColor(color, PorterDuff.Mode.CLEAR). Should be a bit faster
I'm working on a drawing application and am pretty close to release but I'm having issues with the eraser part of the app. I have 2 main screens (fragments) one is just a blank white canvas that the user can draw on with some options and so on. The other is a note taking fragment. This note taking fragment looks like notebook paper. So for erasing on the drawing fragment, I can simply use the background of the canvas and the user wont know the difference. On the note fragment though I cannot do this beacuse I need to keep the background in tact. I have looked into PorterDuffer modes and have tried the clear version and tried to draw onto a separate bitmap but nothing has worked. If there was a way to control what gets draw ontop of what then that would be useful. I'm open to any suggestions, I can't seem to get anything to work.
Ive also played with enabling a drawing cache before erasing and that doesn't work. In addition I tried hardware enabling and that made my custom view behave oddly. Below is the relavent code. My on draw methods goes through a lot of paths because I am querying them in order to allow for some other functionallity.
#Override
protected void onDraw(Canvas canvas) {
//draw the backgroumd type
if(mDrawBackground) {
//draw the background
//if the bitmap is not null draw it as the background, otherwise we are in a note view
if(mBackgroundBitmap != null) {
canvas.drawBitmap(mBackgroundBitmap, 0, 0, backPaint);
} else {
drawBackgroundType(mBackgroundType, canvas);
}
}
for (int i = 0; i < paths.size(); i++ ) {
//Log.i("DRAW", "On draw: " + i);
//draw each previous path.
mDrawPaint.setStrokeWidth(strokeSizes.get(i));
mDrawPaint.setColor(colors.get(i));
canvas.drawPath(paths.get(i), mDrawPaint);
}
//set paint attributes to the current value
mDrawPaint.setStrokeWidth(strokeSize);
mDrawPaint.setColor(mDrawColor);
canvas.drawPath(mPath, mDrawPaint);
}
and my draw background method
/**
* Method that actually draws the notebook paper background
* #param canvas the {#code Canvas} to draw on.
*/
private void drawNoteBookPaperBackground(Canvas canvas) {
//create bitmap for the background and a temporary canvas.
mBackgroundBitmap = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBackgroundBitmap);
//set the color to white.
mBackgroundBitmap.eraseColor(Color.WHITE);
//get the height and width of the view minus padding.
int height = getHeight() - getPaddingTop() - getPaddingBottom();
int width = getWidth() - getPaddingLeft() - getPaddingRight();
//figure out how many lines we can draw given a certain line width.
int lineWidth = 50;
int numOfLines = Math.round(height / lineWidth);
Log.i("DRAWVIEW", "" + numOfLines);
//iterate through the number of lines and draw them.
for(int i = 0; i < numOfLines * lineWidth; i+=lineWidth) {
mCanvas.drawLine(0+getPaddingLeft(), i+getPaddingTop(), width, i+getPaddingTop(), mNoteBookPaperLinePaint);
}
//now we need to draw the vertical lines on the left side of the view.
float startPoint = 30;
//set the color to be red.
mNoteBookPaperLinePaint.setColor(getResources().getColor(R.color.notebook_paper_vertical_line_color));
//draw first line
mCanvas.drawLine(startPoint, 0, startPoint, getHeight(), mNoteBookPaperLinePaint);
//space the second line next to the first one.
startPoint+=20;
//draw the second line
mCanvas.drawLine(startPoint, 0, startPoint, getHeight(), mNoteBookPaperLinePaint);
//reset the paint color.
mNoteBookPaperLinePaint.setColor(getResources().getColor(R.color.notebook_paper_horizontal_line_color));
canvas.drawBitmap(mBackgroundBitmap, 0, 0, backPaint);
}
To all who see this question I thought I would add how I solved the problem. What I'm doing is creating a background bitmap in my custom view and then passing that to my hosting fragment. The fragment then sets that bitmap as its background for the containing view group so that when I use the PorterDuff.CLEAR Xfermode, the drawn paths are cleared but the background in the fragment parent remains untouched.
I have a JPanel surrounded with JScrollPane. This JPanel is used to display an image. I need to provide functionality like zoomIn, zoomOut, clockwiseRotate and antiClockwiseRotate. All these functionalities are working fine individually. For zoom, I call scale of graphics object. It happens on top left to bottom right basis. For rotation, I reset the scale, and translate, rotate & translate back the graphics object. But when I combine zoom with rotate, it behaves differently. For instance, I rotate clockwise and the image gets rotated to 1.57079633 radians (approx 90 degree). Now when I press zoom, the image gets zoomed but the zooming happens based on top right and bottom left basis instead of top left and bottom right basis. If I again rotate the image in clockwise direction, I reset the zoom, that is I scale the image to its original size and call translate, rotate and translate back on graphics object. Now if I press zoom again, it zooms in based on bottom right and top left basis.
Hence the problem is the Image's coordinates are not getting changed when panels coordinates are changed. Can somebody help me out in changing the coordinates of the image?
public void paint(Graphics g)
{
Graphics2D g2d = (Graphics2D)g;
g2d.translate((tempWidth/2), (tempHeight/2));
g2d.rotate(m_rotate);
g2d.translate(-(tempWidth/2), -(tempHeight/2));
g2d.scale(m_zoom, m_zoom);
if(this.image != null && this.image.getHeight(null) > 0)
{
g2d.drawImage(this.image, 0, 0,302,312,this);
}
else
{
g2d.drawString("View Image Here!. ", 20, 20);
}
}
Thanks for your help. It made me think in a new dimension which helped me to solve the issue. All I did is just scaling the image before rotating and it runs well now.
public void paint(Graphics g)
{
Graphics2D g2d = (Graphics2D)g;
g2d.scale(m_zoom, m_zoom);//This is what made the magic
g2d.translate((tempWidth/2), (tempHeight/2));
g2d.rotate(m_rotate);
g2d.translate(-(tempWidth/2), -(tempHeight/2));
/*g2d.scale(m_zoom, m_zoom); removed this and placed above */
if(this.image != null && this.image.getHeight(null) > 0)
{
g2d.drawImage(this.image, 0, 0,302,312,this);
}
else
{
g2d.drawString("View Image Here!. ", 20, 20);
}
}
I'm new to graphics programming. I'm trying to create a program that allows you to draw directed graphs. For a start I have managed to draw a set of rectangles (representing the nodes) and have made pan and zoom capabilities by overriding the paint method in Java.
This all seems to work reasonably well while there aren't too many nodes. My problem is when it comes to trying to draw a dot grid. I used a simple bit of test code at first that overlayed a dot grid using two nested for loops:
int iPanX = (int) panX;
int iPanY = (int) panY;
int a = this.figure.getWidth() - iPanX;
int b = this.figure.getHeight() - (int) iPanY;
for (int i = -iPanX; i < a; i += 10) {
for (int j = -iPanY; j < b; j += 10) {
g.drawLine(i, j, i, j);
}
}
This allows me to pan the grid but not zoom. However, the performance when panning is terrible! I've done a lot of searching but I feel that I must be missing something obvious because I can't find anything on the subject.
Any help or pointers would be greatly appreciated.
--Stephen
Use a BufferedImage for the dot grid. Initialize it once and later only paint the image instead of drawing the grid over and over.
private init(){
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics g = image.getGraphics();
// then draw your grid into g
}
public void paint(Graphics g) {
g.drawImage(image, 0, 0, null);
// then draw the graphs
}
And zooming is easily achieved using this:
g.drawImage(image, 0, 0, null); // so you paint the grid at a 1:1 resolution
Graphics2D g2 = (Graphics2D) g;
g2.scale(zoom, zoom);
// then draw the rest into g2 instead of g
Drawing into the zoomed Graphics will lead to proportionally larger line width, etc.
I think re-drawing all your dots every time the mouse moves is going to give you performance problems. Perhaps you should look into taking a snapshot of the view as a bitmap and panning that around, redrawing the view 'properly' when the user releases the mouse button?