Android rotate bitmap around center without resizing - java

I'm struggling to draw a rotating bitmap around its center and do the rotating without resizing the bitmap. I'm drawing all my sprites to the screen via a game thread, so I'm looking for a solution that incorporates the original bitmap and not the canvas.
Thanks in advance.
This is my code so far, it turns a bitmap around its center, yet resizes it.
i = i + 2;
transform.postRotate(i, Assets.scoresScreen_LevelStar.getWidth()/2, Assets.scoresScreen_LevelStar.getHeight()/2);
Bitmap resizedBitmap = Bitmap.createBitmap(Assets.scoresScreen_LevelStar, 0, 0, Assets.scoresScreen_LevelStar.getWidth(), Assets.scoresScreen_LevelStar.getHeight(), transform, true);
game.getGraphics().getCanvasGameScreen().drawBitmap(resizedBitmap, null, this.levelStar.getHolderPolygons().get(0), null);
Update:
I've noticed this isn't as easy as it sounds. My rotating code is not the problem. The bitmap rotates, yet the dst rect will also have to increase/decrease depending on the angle of rotation, or else the bimap will appear smaller, since it's drawn into a fixed dst rect.
So I'm guessing I'll have to develop some method that will return a dst rect.
So the methods needed to rotate a bitmap without appeared resizing:
public static Bitmap rotateBitmap(Bitmap bitmap, int rotation) // I've got this method working
and
public static Rect rotateRect(Rect currentDst, int rotation) // Don't got this
I understand this will require some math (trig), anyone up for the challenge? :P

You should draw the bitmap using the Matrix class. Below is a very basic idea assuming that you want to rotate an image inside of a "Ship" class. You update the current position Matrix inside the update method. In the onDraw() you draw the bitmap using the newly updated position Matrix. This will draw the rotated bitmap without resizing it.
public class Ship extends View {
private float x, y;
private int rotation;
private Matrix position;
private Bitmap bitmap;
...
#Override
public void onDraw(Canvas canvas) {
// Draw the Bitmap using the current position
canvas.drawBitmap(bitmap, position, null);
}
public void update() {
// Generate a new matrix based off of the current rotation and x and y coordinates.
Matrix m = new Matrix();
m.postRotate(rotation, bitmap.getWidth()/2, bitmap.getHeight()/2);
m.postTranslate(x, y);
// Set the current position to the updated rotation
position.set(m);
rotation += 2;
}
....
}
Hope that helps!
Also keep in mind that generating a new Bitmap object inside you game loop will be resource intensive.

This worked for me!
I created a method that returns a matrix. The matrix can be used in the following drawing method:
public void drawBitmap (Bitmap bitmap, Matrix matrix, Paint paint)
Here you go! (The parameter shape can be replaced with ease, if you would like that, just leave a comment):
public static Matrix rotateMatrix(Bitmap bitmap, Shape shape, int rotation) {
float scaleWidth = ((float) shape.getWidth()) / bitmap.getWidth();
float scaleHeight = ((float) shape.getHeight()) / bitmap.getHeight();
Matrix rotateMatrix = new Matrix();
rotateMatrix.postScale(scaleWidth, scaleHeight);
rotateMatrix.postRotate(rotation, shape.getWidth()/2, shape.getHeight()/2);
rotateMatrix.postTranslate(shape.getX(), shape.getY());
return rotateMatrix;
}
NB: If you want an animated rotation, the rotation parameter will have to be updated with new values every frame eg. 1 then 2 then 3 ...

Related

Texture Stretching when using a FrameBuffer and SpriteBatch

I have been working on this for 2 days and still cant figure out what is going on. I am working on a Game Framework and i have migrated from Slick2D to LibGDX and would like to render once to a texture as to save CPU/GPU cycles and improve performance. I have created a class called DrawSurface and its main goal is to allow me to draw to a offscreen texture and then just draw that using LibGDXs Built in Image class.
public final class DrawSurface {
public static interface DrawCall{
void draw(SpriteBatch b, int width, int height);
}
public static Texture offscreenDraw(DrawCall c, int canvasWidth, int canvasHeight){
FrameBuffer fbo = new FrameBuffer(Pixmap.Format.RGBA8888,(canvasWidth),(canvasHeight),true);
SpriteBatch batch = new SpriteBatch();
batch.setProjectionMatrix(new Matrix4().setToOrtho2D(0,0,fbo.getWidth(),fbo.getHeight()));
fbo.begin();
batch.begin();
c.draw(batch,fbo.getWidth(),fbo.getHeight());
batch.end();
Pixmap map = ScreenUtils.getFrameBufferPixmap(0,0,fbo.getWidth(),fbo.getHeight());
fbo.end();
Texture t = new Texture(map);
map.dispose();
return t;
}
}
Which Gives me This Result When Drawing Images:
The "DrawCall" that is used to get this image is:
Texture t = DrawSurface.offscreenDraw(new DrawSurface.DrawCall() {
#Override
public void draw(SpriteBatch b, int w, int h) {
Image img = new Image(0x0000FF,w,h);
img.setLocation(0,0);
img.draw(b);
for(int i = 0; i < w; i += 64){
for(int j = 0; j < h; j += 64){
Blocks.GRASS_BLOCK.setLocation(i,j);
Blocks.GRASS_BLOCK.draw(b);
}
}
}
},512,512);
The Image should Render as a blue Square 512x512 pixels in size, with small "Grass" block images that should also be square. Sized 16x16. Unfortunately i get a warped result, and i do not understand why. As the Large White thing (a software JoyStick i created) is not stretched while the "DrawsSurface" is. If you would like a look at my camera code:
// Constructor Above.
this.gameWidth = gameWidth;
this.gameHeight = gameHeight;
this.camera = new OrthographicCamera();
FlatPixelGame.gameCamera = this.camera;
this.viewport = new StretchViewport(gameWidth,gameHeight,camera);
this.viewport.apply();
LogBot.log("Game Instance Created Size [%s,%s]",gameWidth,gameHeight);
InputManager.getInstance().addListener(this);
this.camera.position.set(camera.viewportWidth / 2, camera.viewportHeight / 2, 0);
Any Help Would Be Appreciated, i have scoured Forums, StackOverflow and LibGDX Documentation and still cannot seem to fix this issue.
Again, Thank you in Advance :)
After a little work under the hood with LibGDX source code from BadLogic, as well as reading bug threads it seems there is an issue when converting a PixMap to a texture. Rendering a image derived from a file works fine, but generated images that come from a Pixmap can show warping due to the off-screen view-port not being the same as the current rendering view-port.
A quick way around this is using TextureRegions and Sprites.
public static Image offscreenDraw(DrawCall c, int canvasWidth, int canvasHeight){
FrameBuffer fbo = new FrameBuffer(Pixmap.Format.RGBA8888,canvasWidth,canvasHeight,true);
SpriteBatch batch = new SpriteBatch();
Matrix4 matrix = new Matrix4();
matrix.setToOrtho2D(0, 0, canvasWidth,canvasHeight);
batch.setProjectionMatrix(matrix);
fbo.begin();
batch.begin();
c.draw(batch,canvasWidth,canvasHeight);
batch.end();
Texture t = fbo.getColorBufferTexture();
t.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest);
if(!t.getTextureData().isPrepared()){
t.getTextureData().prepare();
}
fbo.end();
TextureRegion r = new TextureRegion(t,0,0,canvasWidth,canvasHeight);
return new Image(new com.badlogic.gdx.scenes.scene2d.ui.Image(new Sprite(r)));
}

Place frame when taking photo Android Studio

I want to develop an application that takes a photograph and allows you to select a frame for photography.
I already have developed the interfaces, like taking the photo and storing it.
But I put a frame to the photo taken I could not.
Any recommendation?
In what format do you store taken photos and your frame images? If you plan to insert your picture into a simple frame, I'd suggest to first draw your taken picture on a Canvas, and then draw your frame over it. Keep in mind the sizing - you don't want your picture be too small or too big.
I'm providing an example snippet here:
public Bitmap Output(Bitmap takenPhoto, Bitmap frameImage)
{
int width, height, x, y;
height = ### // your desired height of the whole output image
width = ### // your desired width of the whole output image
x = ### // specify indentation for the picture
y = ###
Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(result);
Paint paint = new Paint();
c.drawBitmap(takenPhoto, x, y, paint); // draw your photo over canvas, keep indentation in mind (x and y)
c.drawBitmap(frameImage, 0, 0, paint); // now draw your frame on top of the image
return result;
}
Keep in mind that I have not tested the code and you might (actually, you'll have to) apply corrections.
Thanks for the help, I put my solution :
ImageView Resultado;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Bitmap miBitmap = Bitmap.createBitmap(1000, 1000, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(miBitmap);
Paint paint = new Paint();
c.drawBitmap(drawableToBitmap(R.mipmap.ic_launcher), 500, 500, paint); // draw your photo over canvas, keep indentation in mind (x and y)
c.drawBitmap(drawableToBitmap(R.drawable.silver_frame), 0, 0, paint); // now draw your frame on top of the image
Resultado.setImageBitmap(miBitmap);
}
public Bitmap drawableToBitmap(int imagen){
return BitmapFactory.decodeResource(getApplicationContext().getResources(), imagen);
}

libgdx create texture from overlay using pixmap

I am trying to create a method which returns a texture modified by an overlay using libgdx and PixMap.
Assuming I have 2 images:
A Base Image in FileHandle textureInput
And an overlay image in FileHandle overLay
It should produce this texture:
So it should use the RGB values from the textureInput and the alpha values from the overLay and create the final image. I believe I can do this using the Pixmap class but I just can't seem to find exactly how.
Here is what I gather should be the structure of the method:
public Texture getOverlayTexture(FileHandle overLay, FileHandle textureInput){
Pixmap inputPix = new Pixmap(textureInput);
Pixmap overlayPix = new Pixmap(overLay);
Pixmap outputPix = new Pixmap(inputPix.getWidth(), inputPix.getHeight(), Format.RGBA8888);
// go over the inputPix and add each byte to the outputPix
// but only where the same byte is not alpha in the overlayPix
Texture outputTexture = new Texture(outputPix, Format.RGBA8888, false);
inputPix.dispose();
outputPix.dispose();
overlayPix.dispose();
return outputTexture;
}
I am just looking for a bit of direction as to where to go from here. Any help is really appreciated. I apologize if this question is too vague or if my approach is entirely off.
Thanks!
I finally found the way to do this.
How my game is setup is that each item draws itself. They are handed a spritebatch and can do stuff with it. I did it that way various reasons. There is an item manager containing a list of items. Each item has various attributes. Each item has it's own render method along with other independent methods. Here is what finally worked:
A normal item's render method which does not use any alpha masking:
public void render(SpriteBatch batch, int renderLayer) {
if(renderLayer == Integer.parseInt(render_layer)){ // be in the correct render layer
batch.draw(item.region,
item.position.x, // position.x
item.position.y, // position.y
0, //origin x
0, //origin y
item.region.getRegionWidth() , //w
item.region.getRegionHeight(), //h
item.t_scale, //scale x
item.t_scale, //scale y
item.manager.radiansToDegrees(item.rotation)); //angle
}
}
So it is handed a spritebatch that it draws to with the correct image, location, scale, and rotation, and that is that.
After playing around with what I found here: https://gist.github.com/mattdesl/6076846 for a while, this finally worked for an item who needs to use alpha masking:
public void render(SpriteBatch batch, int renderLayer) {
if(renderLayer == Integer.parseInt(render_layer)){
batch.enableBlending();
//draw the alpha mask
drawAlphaMask(batch, item.position.x, item.position.y, item.region.getRegionWidth(), item.region.getRegionHeight());
//draw our foreground elements
drawForeground(batch, item.position.x, item.position.y, item.region.getRegionWidth(), item.region.getRegionHeight());
batch.disableBlending();
}
}
There is a TextureRegion named alphaMask which contains a black shape.
It can be any image, but let's say in this instance its this shape / image:
Here is the function called above that uses that image:
private void drawAlphaMask(SpriteBatch batch, float x, float y, float width, float height) {
//disable RGB color, only enable ALPHA to the frame buffer
Gdx.gl.glColorMask(false, false, false, true);
// Get these values so I can be sure I set them back to how it was
dst = batch.getBlendDstFunc();
src = batch.getBlendSrcFunc();
//change the blending function for our alpha map
batch.setBlendFunction(GL10.GL_SRC_ALPHA, GL10.GL_ZERO);
//draw alpha mask sprite
batch.draw(alphaRegion,
x, // position.x
y, // position.y
0, // origin x
0, // origin y
alphaRegion.getRegionWidth(), // w
alphaRegion.getRegionHeight(), // h
item.t_scale, // scale x
item.t_scale, // scale y
item.manager.radiansToDegrees(item.rotation)); // angle
//flush the batch to the GPU
batch.flush();
}
There are a variety of "materials" to apply to any shape. In any instance one of them is assigned to the spriteRegion variable. Let's say right now it is this:
So the drawForeground method called above uses that image like this:
private void drawForeground(SpriteBatch batch, float clipX, float clipY, float clipWidth, float clipHeight) {
//now that the buffer has our alpha, we simply draw the sprite with the mask applied
Gdx.gl.glColorMask(true, true, true, true);
batch.setBlendFunction(GL10.GL_DST_ALPHA, GL10.GL_ONE_MINUS_DST_ALPHA);
batch.draw(spriteRegion,
clipX, // corrected center position.x
clipY, // corrected center position.y
0, //origin x
0, //origin y
spriteRegion.getRegionWidth() , //w
spriteRegion.getRegionHeight(), //h
item.t_scale, //scale x
item.t_scale, //scale y
item.manager.radiansToDegrees(item.rotation)); //angle
//remember to flush before changing GL states again
batch.flush();
// set it back to however it was before
batch.setBlendFunction(src, dst);
}
That all worked right away in the desktop build, and can produce "Brick Beams" (or whatever) in the game nicely:
However in Android and GWT builds (because after all, I am using libgdx) it did not incorporate the alpha mask, and instead rendered the full brick square.
After a lot of looking around I found this: https://github.com/libgdx/libgdx/wiki/Integrating-libgdx-and-the-device-camera
And so to fix this in Android I modified the MainActivity.java onCreate method like this:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AndroidApplicationConfiguration cfg = new AndroidApplicationConfiguration();
cfg.useGL20 = false;
cfg.r = 8;
cfg.g = 8;
cfg.b = 8;
cfg.a = 8;
initialize(new SuperContraption("android"), cfg);
if (graphics.getView() instanceof SurfaceView) {
SurfaceView glView = (SurfaceView) graphics.getView();
// force alpha channel - I'm not sure we need this as the GL surface
// is already using alpha channel
glView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
}
}
And that fixes it for Android.
I still cannot figure out how to make it work properly in gwt, as I cannot figure out how to tell libgdx to tell GWT to tell webGl to go ahead and pay attention to the alpha channel. I'm interested in how to do something like this in an easier or less expensive way (though this seems to work fine).
If anyone knows how to make this work with GWT, please post as another answer.
Here is the non-working GWT build if you want to see the texture issue:
https://supercontraption.com/assets/play/index.html

How can I scale my bitmap in Android based on the width and height pixels?

I want to be able to scale my image based on the screen size. In a normal java applet I would do something like the following....
int windowWidth = 1280;
int windowHeight = 720;
Image image;
public void paint(Graphics g)
{
g.drawImage(image, x, y, windowWidth / 4, windowHeight / 16, null);
}
I've been searching for an answer for a while and everything I find seems to turn up some weird result. From what I read I might need to do something with Resolution Independent Pixels but I'm not %100 sure.
The thing I am trying to avoid is having to create a whole new set of images and icons just for different screen densities. The method I showed above works for resizing desktop apps without a problem.
Edit:
This is what I have been using to draw an image in android.
Matrix matrix = new Matrix();
Bitmap image;
Constuctor....()
{
image = BitmapFactory.decodeResource(context.getResources(), R.drawable.play);
}
public void render(Canvas c)
{
c.drawBitmap(image, matrix, null);
}
Hi see thsi question I have posted scale bitmap
If you are using canvas get the width and height of the canvas. or if you want to have it formal normal layouts then get the width and height by using
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
dispWidth=metrics.widthPixels;
dispheight=metrics.heightPixels;
and then scale our bitmap according to your requirement like this. In this I Have to have 8 bricks so I have taken the width by dividing with the Number of columns
String strwidth=String.valueOf(((float)(bmp.getWidth())/NO_COLUMNS));
if(strwidth.contains("."))
{
scalebit=Bitmap.createScaledBitmap(bmp, (int)(Math.ceil(((float)bmp.getWidth())/NO_COLUMNS))*NO_COLUMNS, bmp.getHeight(), true);
}
else
{
scalebit=bmp;
}

Android Rotatable ImageView

I am trying to create a Rotatable an ImageView to which I will specify certain angle and pivot point and see it rotated around that pivot point. I tried something like this:
Matrix matrix = new Matrix();
matrix.postRotate(45, imageView.getWidth(), imageView.getHeight());
imageView.setScaleType(ScaleType.MATRIX);
imageView.setImageMatrix(matrix);
but the parameters of postRotate method (the second and third - the pivot points) make NO CHANGE at all. even if they are 0, 0 - it's the same thing.
So I wanna create a ImageView that would be rotated by certain angle when initialized. In this example 45 degrees. I tried setting the bounds and staff.. no help.
How do I do that? :/
You can rotate a ImageView by using setRotation(int);
// rotate imageView 45 around center pivot point
imageView.setPivotX(imageView.getWidth()/2);
imageView.setPivotY(imageView.getHeight()/2);
imageView.setRotation(45);
Reference: http://goo.gl/WhhGM
Edit: I had to shorten the link because of a ) in the url, some browsers don't like that.
This is how I use view.setRotation(float angle) in my apps, hope it will be helpful:
//to make rotation use next code
imageView.setPivotX(imageView.getWidth() / 2);
imageView.setPivotY(imageView.getHeight() / 2);
imageView.setRotation(45);
//to reset rotate state to initial position
imageView.setPivotX(imageView.getWidth() / 2);
imageView.setPivotY(imageView.getHeight() / 2);
imageView.setRotation(0);
Based on answer from Spencer
This function works for me.
public static Bitmap rotateImage (Bitmap srcBitmap, int width, int height, int rotation)
{
// create rotated image
Matrix matrix = new Matrix();
rotation = (rotation +1 ) % 3;
rotation = rotation * 90;
matrix.postRotate( rotation,
width,
height );
Bitmap rotatedBmp = Bitmap.createBitmap( srcBitmap,
0,
0,
srcBitmap.getWidth(),
srcBitmap.getHeight(),
matrix,
false );
return rotatedBmp;
}

Categories