How to resize a texture maintaining aspect ratio in libgdx - libgdx

I have a .png image "circle.png".
The problem is when I try to resize this in sb.draw() method it disorientates (stretches). It works fine when I draw it with it's default size;
// inside constructor
{
Texture texture = new Texture("circle.png");
...
.
}
// render method
public void render (SpriteBatch sb){
sb.begin();
sb.setProjectionMatrix(camera.combined);
sb.draw(texture,x,y,100,100);// 100 for both width and height
sb.end();
}
//camera setup
camera = new OrthographicCamera();
camera.setToOrtho(false, width / 2, height / 2);
// width n height is 480x640
First image is one without resizing
Second image is while resizing

Your image is probably not a square. To maintain the aspect ratio, use math:
100 = texture.getWidth()/a
a = texture.getWidth()/100
So use:
sb.draw(texture,x,y,100,texture.getHeight()/(texture.getWidth()/100);
I hope it helps!
staticcasty

Related

Libgdx Rendering Bitmap font to pixmap texture causes extremely slow rendering

I'm using libgdx scene2d to render 2d actors. Some of these actors originally included scene2d Label actors for rendering static text. The Labels work fine but drawing ~20 of them on the screen at once drops the frame rate by 10-15 frames, resulting in noticeably poor rendering while dragging.
I'm attempting to avoid the Labels by pre-drawing the text to textures, and rendering the textures as scene2d Image actors. I'm creating the texture using the code below:
BitmapFont font = manager.get(baseNameFont,BitmapFont.class);
GlyphLayout gl = new GlyphLayout(font,"Test Text");
int textWidth = (int)gl.width;
int textHeight = (int)gl.height;
LOGGER.info("textHeight: {}",textHeight);
//int width = Gdx.graphics.getWidth();
int width = textWidth;
//int height = 500;
int height = textHeight;
SpriteBatch spriteBatch = new SpriteBatch();
FrameBuffer m_fbo = new FrameBuffer(Pixmap.Format.RGB565, width,height, false);
m_fbo.begin();
Gdx.gl.glClearColor(1f,1f,1f,0f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
Matrix4 normalProjection = new Matrix4()
.setToOrtho2D(0, 0, width, height);
spriteBatch.setProjectionMatrix(normalProjection);
spriteBatch.begin();
font.draw(spriteBatch,gl,0,height);
spriteBatch.end();//finish write to buffer
Pixmap pm = ScreenUtils.getFrameBufferPixmap(0, 0, (int) width, (int) height);//write frame buffer to Pixmap
m_fbo.end();
m_fbo.dispose();
m_fbo = null;
spriteBatch.dispose();
Texture texture = new Texture(pm);
textTexture = new TextureRegion(texture);
textTexture.flip(false,true);
manager.add(texture);
I assumed, and have read, that textures are often faster. However when I replaced the Labels with the texture, it had the same, if not worse, affect on the frame rate. Oddly, I'm not experiencing this when adding textures from a file, which makes me think I'm doing something wrong in my code. Is there a different way I should be pre-rendering these pieces of text?
I have not tested this, but I think you can enable culling for the whole Stage by setting its root view to use a cullingArea matching the world width and height of the viewport. I would do this in resize after updating the Stage Viewport just in case the update affects the world width and height of the viewport.
#Override
public void resize(int width, int height) {
//...
stage.getViewport().update(width, height, true);
stage.getRoot().setCullingArea(
new Rectangle(0f, 0f, stage.getViewport().getWorldWidth(), stage.getViewport().getWorldHeight())
);
}
It will only be able to cull Actors that have their x, y, width, and height set properly. This is true I think of anything in the scene2d UI package, but for your own custom Actors you will need to do it yourself.
If a child of your root view is a Group that encompasses more than the screen and many actors, you might want to cull its children, too, such that even if the group as a whole is not culled by the root view, the group can still cull a portion of its own children. To figure out the culling rectangle for this, I think you would intersect a rectangle of the group's size with the viewport's world size rectangle offset by -x and -y of the group's position (since the culling rectangle is relative to the position of the group it's set on).

Pixi.js - Unable to set sprite width to full width of canvas

Ultimately I want to have a canvas that fills the entire viewport width of the browser and have an image on the canvas fill the entire width of that canvas. I'm taking advantage of pixi.js's displacement filter so that I can create a pseudo 3d effect using a depth map underneath it. The code I'm currently using is
let app = new PIXI.Application();
var w = window.innerWidth;
var h = w / ratio;
app.view.style.width = w + 'px';
app.view.style.height = h + 'px';
document.body.appendChild(app.view);
let img = new PIXI.Sprite.from("images/horses.png");
img.width = w;
img.height = h;
app.stage.addChild(img);
depthMap = new PIXI.Sprite.from("images/horses-depth.png");
depthMap.renderable = false;
depthMap.width = img.width;
depthMap.height = img.height;
img.addChild(depthMap);
app.stage.addChild(depthMap);
displacementFilter = new PIXI.filters.DisplacementFilter(depthMap, 0);
app.stage.filters = [displacementFilter];
Here's a screenshot of it in action.
I even tried manually setting the width to the viewport pixel width on the canvas and the sprite to the actual pixel width of the viewport width and it still wasn't the right size. Manually setting the width for the viewport and sprite to the exact same number also doesn't work; the sprite is inexplicably smaller than the canvas.
I tried to look at the documentation of the sprite class and see if there is something unusual about how sprites handle widths but I couldn't find anything https://pixijs.download/dev/docs/PIXI.Sprite.html
How can I create a pixi.js sprite that fills the entire width of the canvas in order to make both the canvas and the sprite fill the entire viewport width?
pixijs.download
PixiJS API Documentation
Documentation for PixiJS library

Draw sprite on Body - camera View Port and resize

I'm drawing sprites uppon bodies, with this method (inside my body wrapper):
private void drawBaseSprite(Batch batch){
Sprite baseSprite = this.getBaseSprite();
Vector3 bodyPixelPos = camera.project(new Vector3(body.getPosition().x, body.getPosition().y, 0));
// TODO: 17/11/16 Review this
float w = scale * (baseSprite.getTexture().getWidth())/camera.zoom;
float h = scale * (baseSprite.getTexture().getHeight())/camera.zoom ;
baseSprite.setSize(w,h);
baseSprite.setOrigin(w/2, h/2);
baseSprite.setPosition(bodyPixelPos.x -w/2, bodyPixelPos.y - h/2);
baseSprite.setRotation(body.getAngle() * MathUtils.radiansToDegrees);
baseSprite.draw(batch);
}
Everything is good, until I try resizing the windows. Well, I'm following this resize logic (implements Screen) :
#Override
public void resize(int width, int height) {
stage.getViewport().update(width, height, true);
stage.camera.setToOrtho(false, Constants.VIEWPORT_HEIGHT *width/(float)height, Constants.VIEWPORT_HEIGHT);
}
Before resize:
After resize (larger width):
I find this absurd because this:
float w = scale * (baseSprite.getTexture().getWidth())/camera.zoom;
float h = scale * (baseSprite.getTexture().getHeight())/camera.zoom ;
doesn't change, while the images are x-scaled.
Am I correct in understanding that your problem is the image not staying on top of the body?
If so, the reason why it doesn't is that you are setting the size of the image relative to the camera, not to the body it is supposed to cover. When your camera's zoom is changed, you adjust the size of the image, but not the body, and they get out of sync.
I would recommend changing your logic such that the image height/width are determined purely by the body, and your body is scaled based on the camera zoom.
Well, this solved my problem, but I still don't deeply-know why.
private void drawBaseSprite(Batch batch){
Sprite baseSprite = this.getBaseSprite();
batch.setProjectionMatrix(camera.combined);
float someScale = 0.1f;
float w = scale * (baseSprite.getTexture().getWidth())/camera.zoom *someScale;
float h = scale * (baseSprite.getTexture().getHeight())/camera.zoom *someScale;
Vector3 bodyPixelPos = camera.project(new Vector3(body.getPosition().x, body.getPosition().y, 0))
.scl(someScale*camera.viewportHeight/(Gdx.graphics.getHeight()/20f)).sub(w/2, h/2, 0);
baseSprite.setSize(w,h);
baseSprite.setOrigin(w/2, h/2);
baseSprite.setPosition(bodyPixelPos.x, bodyPixelPos.y);
baseSprite.setRotation(body.getAngle() * MathUtils.radiansToDegrees);
baseSprite.draw(batch);
}

libgdx mesh screenshot original resolution

I have a mesh on which I have bound a texture in Render loop using texture.bind();
The original resolution of the texture is 1024 x 1024. My desktop screen resolution is 1366 x 768.
So I make some changes in the mesh triangles to modify the texture's look. I move some triangles here and there to get a final image. Now, I want to save that image in its original resolution, but If i do so using the image's original pixmap, it saves the original image before modification in 1024x1024. But I want to save the modified image in 1024x1024.
I also used the screenshot code:
byte[] pixels = ScreenUtils.getFrameBufferPixels(0, 0, Gdx.graphics.getBackBufferWidth(), Gdx.graphics.getBackBufferHeight(), true);
Pixmap pixmap = new Pixmap(Gdx.graphics.getBackBufferWidth(), Gdx.graphics.getBackBufferHeight(), Pixmap.Format.RGBA8888);
BufferUtils.copy(pixels, 0, pixmap.getPixels(), pixels.length);
PixmapIO.writePNG(Gdx.files.local("saved/screenshot.png"), pixmap);
pixmap.dispose();
But it saves the image in 1024x750 resolution because of the lower monitor resolution (getBackBufferHeight becomes 750 in this case).
I now want to capture/save the modified image(modified using mesh triangles). What exactly do I need to do now to get the modified 1024x1024 image? Should I use a ScrollPane to display the image in its original resolution so that when I take a screenshot, I get 1024x1024?
Do your modifications on a correctly sized frame buffer, instead of the screen's back buffer. But be wary of whether the device supports RGBA8888 for frame buffers (it's not required for OpenGL ES 2.0 so some GPUs don't support it).
int width = 1024;
int height = 1024;
FrameBuffer frameBuffer;
try {
frameBuffer = new FrameBuffer(Pixmap.Format.RGBA8888, width, height, false);
} catch (GdxRuntimeException e) {
frameBuffer = new FrameBuffer(Pixmap.Format.RGB565, width, height, false);
}
frameBuffer.begin();
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
//draw your texture and do your manipulations.
byte[] pixels = ScreenUtils.getFrameBufferPixels(0, 0, width, height, true);
Pixmap pixmap = new Pixmap(width, height, Pixmap.Format.RGBA8888);
BufferUtils.copy(pixels, 0, pixmap.getPixels(), pixels.length);
PixmapIO.writePNG(Gdx.files.local("saved/screenshot.png"), pixmap);
pixmap.dispose();
frameBuffer.end();
frameBuffer.dispose();

Scaling up viewport

My game is 200 x 320 pixels and I want to scale this up to fit any screen. The problem is I need to scale this up in integer multiples of these dimensions so the upscaled pixels don't look uneven.
What should I put in my constructor, render() and resize() methods to achieve this?
In the constructor I now have:
camera = new OrthographicCamera();
camera.setToOrtho(false,200,320);
stage = new Stage(new ScalingViewport(Scaling.fill,200,320, camera));
In render():
camera.update();
game.batch.setProjectionMatrix(camera.combined);
And in resize():
int widthScale = width/200;
int heightScale = height/320;
int newScale = Math.max(widthScale, heightScale);
int multiplesWidth = newScale*200;
int multiplesHeight = newScale*320;
stage.getViewport().update(multiplesWidth, multiplesHeight, true);
camera.setToOrtho(false, 200, 320);
My problem is that the content isn't centered. How and where can I center whatever is on the screen? I tried with:
camera.position.set(Gdx.graphics.getWidth()/2f, Gdx.graphics.getHeight()/2f, 0);
but the screen is blank!
I think the problem is that you are trying to center the screen using the screen dimensions and not the world dimensions.
Try replacing
camera.position.set(Gdx.graphics.getWidth()/2f, Gdx.graphics.getHeight()/2f, 0);
with
camera.position.set(stage.getViewport().getWorldWidth()/2f, stage.getViewport().getWorldHeight()/2f, 0);
EDIT:
Okay, so here is an explanation why I proposed you set the camera to (width/2, height/2). (I will leave out the third z-Coordinate from now on as it will always be 0.)
camera.position.set() sets the center point of the camera. At (0,0) the camera is positioned right at the center of the screen, depicted by the red rectangle below. What libgdx does by default, and what I proposed offsets the camera by width/2(the blue line) and height/2(the green line) which translates to the magenta rectangle.
Side note: the reason camera.position.set(Gdx.graphics.getWidth()/2f, Gdx.graphics.getHeight()/2f, 0); didn't work is because Gdx.graphics.getX() returns the width and height of the window in pixels, not in the measurement system the Viewport uses. This offsets the camera about a meter up and to the right, where you obviously didn't draw anything.
If you want the red rectangle as camera, you can change the last argument of
stage.getViewport().update(multiplesWidth, multiplesHeight, true);
to false. Or use camera.position.set(0,0,0);