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;
}
Related
In the code below, I'm trying to replace a sticker with an imageView keeping the same [Width, Height, TranslateX, TranslateY, and Rotation] of the sticker to give it to the imageView :
transX = selectedSticker.getTransX();
transY = selectedSticker.getTransY();
rotation = selectedSticker.getCurrentAngle();
ImageView mHoverView = findViewById(R.id.drawingView);
mHoverView.setLayoutParams(new FrameLayout.LayoutParams((int) stickerWidth, (int) stickerHeight));
mHoverView.setTranslationX(transX);
mHoverView.setTranslationY(transY);
mHoverView.setRotation(rotation);
mHoverView.setImageResource(R.drawable.dog);
mHoverView.setBackgroundColor(Color.BLUE);
When I click on REPLACE button to replace the sticker with imageView, everything works just fine, the problem starts when I rotate the sticker and clicks on the REPLACE button, the imageView takes the same rotation as the sticker, but it set in the wrong position (the imageView is not rotated around it center) :
e.g.
NO ROTATION :
WITH ROTATION :
Perhaps, you have to obtain correct translation values by re-calculating the transform matrix that is compatible to the View's transform.
//mHoverView.setTranslationX(transX);
//mHoverView.setTranslationY(transY);
Matrix m = new Matrix();
m.setTranslate(transX, transY);
m.postRotate(rotation);
float[] offset = { 0.0F, 0.0F };
m.mapPoints(offset);
mHoverView.setTranslationX(offset[0]);
mHoverView.setTranslationY(offset[1]);
The issue was I didn't set the PivotX and PivotY so the view is not rotated around the center, here's the full code :
float[] values = new float[9];
selectedSticker.getMatrix().getValues(values);
float dx = values[2];
float dy = values[5];
mHoverView.setPivotX(0.0f);
mHoverView.setPivotY(0.0f);
mHoverView.setTranslationX(dx);
mHoverView.setTranslationY(dy);
mHoverView.setRotation(rotation);
mHoverView.invalidate();
double degPi = degrees * Math.PI / 180;
double a = Math.cos(degPi)*tImgCover.getScaledHeight();
double b = Math.sin(degPi)*tImgCover.getScaledWidth();
double c = -Math.sin(degPi) * tImgCover.getScaledHeight();
double d = Math.cos(degPi)* tImgCover.getScaledWidth();
double e = absX;
double f = absY;
contentByte.addImage(imgae, a, b, c, d, e, f);/*add image*/
How to rotate around the image center by itext?
If we have an Image image and coordinates x, y, we can draw the image without rotation with its lower left corner at the given coordinates like this
contentByte.addImage(image, image.getWidth(), 0, 0, image.getHeight(), x, y);
A bitmap image from the resources has a size of 1x1 with the coordinate origin at its lower left. Thus, this operation stretches the image to its correct size and moves it so its lower left is at the given coordinates.
If we want to draw the same image as if the one drawn above was rotated around its center by an angle rotate, therefore, we can do this by moving the 1x1 image so that the origin is in its center, stretch it to its correct size, rotate it, and then move the origin (which still is at the center of the rotated image) to the center of the unrotated image. These operations are easier to express using AffineTransform instances (from package com.itextpdf.awt.geom) instead number tupels. Thus:
// Draw image as if the previous image was rotated around its center
// Image starts out being 1x1 with origin in lower left
// Move origin to center of image
AffineTransform A = AffineTransform.getTranslateInstance(-0.5, -0.5);
// Stretch it to its dimensions
AffineTransform B = AffineTransform.getScaleInstance(image.getWidth(), image.getHeight());
// Rotate it
AffineTransform C = AffineTransform.getRotateInstance(rotate);
// Move it to have the same center as above
AffineTransform D = AffineTransform.getTranslateInstance(x + image.getWidth()/2, y + image.getHeight()/2);
// Concatenate
AffineTransform M = (AffineTransform) A.clone();
M.preConcatenate(B);
M.preConcatenate(C);
M.preConcatenate(D);
//Draw
contentByte.addImage(image, M);
(AddRotatedImage.java test method testAddRotatedImage)
For example drawing both images using
int x = 200;
int y = 300;
float rotate = (float) Math.PI / 3;
results in something like this:
With a Flip
The OP asked in a comment
how to add rotate and flip image?
For this you simply insert a mirroring affine transformation into the sequence of transformations above.
Unfortunately the OP did not mention which he meant a horizontal or a vertical flip. But as changing the rotation angle accordingly transforms one in the other, that isn't really necessary, either.
// Draw image as if the previous image was flipped and rotated around its center
// Image starts out being 1x1 with origin in lower left
// Move origin to center of image
AffineTransform A = AffineTransform.getTranslateInstance(-0.5, -0.5);
// Flip it horizontally
AffineTransform B = new AffineTransform(-1, 0, 0, 1, 0, 0);
// Stretch it to its dimensions
AffineTransform C = AffineTransform.getScaleInstance(image.getWidth(), image.getHeight());
// Rotate it
AffineTransform D = AffineTransform.getRotateInstance(rotate);
// Move it to have the same center as above
AffineTransform E = AffineTransform.getTranslateInstance(x + image.getWidth()/2, y + image.getHeight()/2);
// Concatenate
AffineTransform M = (AffineTransform) A.clone();
M.preConcatenate(B);
M.preConcatenate(C);
M.preConcatenate(D);
M.preConcatenate(E);
//Draw
contentByte.addImage(image, M);
(AddRotatedImage.java test method testAddRotatedFlippedImage)
The result with the same image as above:
With Interpolation
The OP asked in a yet another comment
How anti aliasing ?
The iText Image class knows an Interpolation property. By setting it to true (before adding the image to the document, obviously),
image.setInterpolation(true);
low resolution images are subject to interpolation when drawn.
E.g. using a 2x2 image with differently colored pixels instead of the image of Willi, you get the following results, first without interpolation, then with interpolation:
Confer the AddRotatedImage.java test testAddRotatedInterpolatedImage which adds this image:
Beware: iText Image property Interpolation effectively sets the Interpolate entry in the PDF image dictionary. The PDF specification notes in this context:
NOTE A conforming Reader may choose to not implement this feature of PDF, or may use any specific implementation of interpolation that it wishes.
Thus, on some viewers interpolation may occur differently than in your viewer, maybe even not at all. If you need a specific kind of interpolation on every viewer, upscale the image with the desired amount of interpolation / anti-aliasing before loading it into an iText Image.
public static BufferedImage rotateClockwise90( BufferedImage inputImage ){
int width = inputImage.getWidth();
int height = inputImage.getHeight();
BufferedImage returnImage = new BufferedImage( height, width , inputImage.getType() );
for( int x = 0; x < width; x++ ) {
for( int y = 0; y < height; y++ ) {
returnImage.setRGB( height-y-1, x, inputImage.getRGB( x, y ) );
}
}
return returnImage;
}
Trying to resize a bitmap and set to a specific part of an imageview. The imageview is square and I wish to have the bitmap in the bottom right corner. Width to be 10% of imageview and height to be 30%.
int w = imageview.getWidth();
Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.vertical_bar_green);
imageview.setImageBitmap(Bitmap.createScaledBitmap(bm, w/10, w*30/100, false));
imageview.setScaleType(ScaleType.FIT_END);
The result is the bitmap is the full height of the imageview and the width is much larger.
How can I set specific points to place the bitmap?
From the documentation for END (the matrix used by FIT_END).
Compute a scale that will maintain the original src aspect ratio, but
will also ensure that src fits entirely inside dst. At least one axis
(X or Y) will fit exactly. END aligns the result to the right and
bottom edges of dst.
You will probable want to use a custom matrix for this, probably built with setRectToRect().
For example:
Matrix matrix = new Matrix();
RectF from = new RectF(0, 0, bm.getIntrinsicWidth(), bm.getIntrinsicHeight());
RectF to = new RectF(view.getWidth() * 0.9, view.getHeight() * 0.7, view.getWidth(), view.getHeight());
matrix.setRectToRect(from, to, Matrix.ScaleToFit.FILL);
view.setScaleType(ScaleType.MATRIX);
view.setImageMatrix(matrix);
(I'm not sure if you wanted to keep the original proportions or not, if you want it then use FIT_END for setRectToRect()).
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
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 ...