I'm getting some strange values for the width/height in the following code, resulting in either a stretched picture, or no picture at all.
public void onSurfaceChanged(GL10 gl, int w, int h) {
screenHeight = h; // actual height in pixels
screenWidth = w; // actual width in pixels
worldWidth = 20; // width of the projection (20 units)
worldHeight = (int) (worldWidth * (screenHeight / screenWidth));
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glViewport(0, 0, screenWidth, screenHeight); //new viewport
gl.glLoadIdentity();
GLU.gluOrtho2D(gl, 0, worldWidth, 0, worldHeight); set the 2D projection
So I logged the variables, these are from portrait mode:
screenHeight = 455
screenWidth = 320
worldHeight = 20
worldWidth = 20
while (worldWidth * (screenHeight / screenWidth) should give 20*455/320=28 for worldHeight.
It gets even stranger in landscape mode, where worldHeight suddenly equals 0.
What am I doing wrong?
Let me guess, screenHeight and screenWidth are both ints? In this case the division will be an integer division, resulting in a rounded/truncated integer and therefore being 0 if the ratio is <1. Cast at least one of the operands of the division to a floating point number to perform a real floating point division:
worldHeight = (int) (worldWidth * ((float)screenHeight / (float)screenWidth));
Related
This question already has answers here:
Rotate a buffered image in Java
(4 answers)
Closed 1 year ago.
I'm trying to rotate the image around a reference point in Java using BufferedImage and AffineTransform, at first it seemed exactly what I needed, but it turns out it doesn't behave as expected. I need to do some rudimentary rotations, in multiples of 90, so I tried to do getQuadrantRotateInstance, but, if the reference point is at 0,0 then I get a RasterFormatException: Transformed height (0) is less than or equal to 0.
var rotation = switch (transform) {
case TRANS_NONE -> 0;
case TRANS_ROT90 -> 1;
case TRANS_ROT180 -> 2;
case TRANS_ROT270 -> 3;
default -> throw new NotImplementedException();
};
var transform = AffineTransform.getQuadrantRotateInstance(rotation, referenceX, referenceY);
var operation = new AffineTransformOp(transform, AffineTransformOp.TYPE_BILINEAR);
var rotated = operation.filter(source, null);
By the looks of it the image gets rotated out of the canvas (into negative coordinates), resulting in exception above.
What would be the proper solution to create a rotated variant of the image without cropping or rotating around a center point like existing solutions do?
Rotating an image by an angle around the center point:
private BufferedImage rotateImage(BufferedImage buffImage, double angle) {
double radian = Math.toRadians(angle);
double sin = Math.abs(Math.sin(radian));
double cos = Math.abs(Math.cos(radian));
int width = buffImage.getWidth();
int height = buffImage.getHeight();
int nWidth = (int) Math.floor((double) width * cos + (double) height * sin);
int nHeight = (int) Math.floor((double) height * cos + (double) width * sin);
BufferedImage rotatedImage = new BufferedImage(nWidth, nHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = rotatedImage.createGraphics();
graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
graphics.translate((nWidth - width) / 2, (nHeight - height) / 2);
// This is the rotation around the center point - change this line
graphics.rotate(radian, (double) (width / 2), (double) (height / 2));
graphics.drawImage(buffImage, 0, 0, null);
graphics.dispose();
return rotatedImage;
}
To change the origin point of the rotation see javadoc of the method rotate.
Source: Creating simple captcha.
I am just about to have this app ready to distribute. However i still haven't found out a way to make a app suitable for whatever screen. In fact I am using pngs file for background and buttons (mainly) into JLabels, and this is a huge problem i think. What i would like to achieve is to have the JFrame and its components resizable but maintaining the proportions, just like for a responsive website!
Other ways i was planning of creating pngs of the originals but smaller and then create new frames and while booting the app ask the system what frame should suite the screen best and use it (but i know i would not be great and then of course os sometimes use screen resolution in different ways making is appear smaller or larger, if you know what i mean).
(I am using Netbeans).
Thank you a lot, i am looking foreword to discuss with you this issue that i am sure will concern many others.
Toolkit.getDefaultToolkit() has methods that provide you with what you need, including getting the current screen size:
private void makeFrameFullSize(JFrame aFrame)
{
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
aFrame.setSize(screenSize.width, screenSize.height);
}
This post discusses Resize image while keeping aspect ratio in Java.
Now that you have the screen size, you can calculate the ratio of image height/width to current frame size and scale on the % difference of current frame size to full screen size.
private Dimension get getFrameToScreenRatio(Frame aFrame){
Dimension dimension = Toolkit.getDefaultToolkit().getScreenSize();
return dimension.setSize(aFrame.getWidth()/dimension.getWidth(), aFrame.getHeight()/dimension.getHeight());
}
Here is an example utility class that can scale an image to fit a canvas (like a background image) and scales it to fit:
package net.codejava.graphics;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Image;
/**
* This utility class draws and scales an image to fit canvas of a component.
* if the image is smaller than the canvas, it is kept as it is.
*
* #author www.codejava.net
*
*/
public class ImageDrawer {
public static void drawScaledImage(Image image, Component canvas, Graphics g) {
int imgWidth = image.getWidth(null);
int imgHeight = image.getHeight(null);
double imgAspect = (double) imgHeight / imgWidth;
int canvasWidth = canvas.getWidth();
int canvasHeight = canvas.getHeight();
double canvasAspect = (double) canvasHeight / canvasWidth;
int x1 = 0; // top left X position
int y1 = 0; // top left Y position
int x2 = 0; // bottom right X position
int y2 = 0; // bottom right Y position
if (imgWidth < canvasWidth && imgHeight < canvasHeight) {
// the image is smaller than the canvas
x1 = (canvasWidth - imgWidth) / 2;
y1 = (canvasHeight - imgHeight) / 2;
x2 = imgWidth + x1;
y2 = imgHeight + y1;
} else {
if (canvasAspect > imgAspect) {
y1 = canvasHeight;
// keep image aspect ratio
canvasHeight = (int) (canvasWidth * imgAspect);
y1 = (y1 - canvasHeight) / 2;
} else {
x1 = canvasWidth;
// keep image aspect ratio
canvasWidth = (int) (canvasHeight / imgAspect);
x1 = (x1 - canvasWidth) / 2;
}
x2 = canvasWidth + x1;
y2 = canvasHeight + y1;
}
g.drawImage(image, x1, y1, x2, y2, 0, 0, imgWidth, imgHeight, null);
}
I am drawing some 2Dgraphics to JPanel.
I don't understant how to use scale if i change JPanelsize?
I have an image, which is composed of multiple lines and circles.
Is possible to compute a transformation and that multiply the values that miscalculation? How? Can you give me an example?
A neat way to do it (but not the best) is to have a final variable for the Width and Height, and everything is calculated based on those variables. then you could have a scale variable that is the size of the (JPanel / final Variable) and when you do g.draw() you can set the width to width * wScale, and the height to height * hScale. here is an example
public final WIDTH = 1080;
public final HEIGHT = WIDTH * 16 / 9;
private float wScale = 1;
private float hScale = 1;
public static void mainLoop()
{
wScale = jpanelWidth / (WIDTH * 1.0);
hScale = jpanelHeight / (HEIGHT * 1.0);
//makes the image to draw using the final width and height for calculations
someclass.makeImage(WIDTH, HEIGHT);
//gets an image that has the dimentions of WIDTH and HEIGHT
someimage = someclass.getImage();
//draws the image using its width and scales it to the actual size by multiplying it by the scaler
//the two zeros are at position (0, 0), and null is just the observer.
somegraphics.draw(someimage, 0, 0, (WIDTH * wScale), (HEIGHT * hScale), null);
}
if you need more information or have questions, you can ask for clarification, or contact me personally.
I try to find the way to rotate the LinearGradient object nested into e.g. Rectangle object, say:
Rectangle rect = new Rectangle(0, 0, 200, 200);
LinearGradient lg = new LinearGradient(0, 0, 100, 0, false, CycleMethod.REPEAT, new Stop[] {
new Stop(0, Color.BLACK);
new Stop(0.5, Color.WHITE);
new Stop(1, Color.BLACK);
});
rect.setFill(lg);
Now, I try to rotate this lg object, for example for 45 degrees to the left, but without rotating the whole rect. Is there any way to achieve that?
The first parameters that are given to the LinearGradient constructor are the coordinates of the start- and end point of the gradient axis, respectively. This means that you can achieve a "rotated" gradient simply by passing in an appropriately rotated axis.
In the simplest form, for the example that you described, you can use the following pattern:
double angleInRadians = Math.toRadians(45);
double length = 100;
double endX = Math.cos(angleInRadians) * length;
double endY = Math.sin(angleInRadians) * length;
LinearGradient lg = new LinearGradient(0, 0, endX, endY, ...);
This will result in a gradient rotated by 45 degrees.
The fixed values here will affect the final appearance of the gradient, together with the other parameters. Referring to your example, this gradient with the same "wave length" as before (namely 100), and start with the same color at the upper left corner (i.e. Color.BLACK will be at coordinates (0,0)).
Trig ratios can be used for a more flexible gradient angle. Please note: It does not implement repeat, hence add more stops in the gradient object.
private fun createGradient(width: Float, height: Float): LinearGradient {
val mode = TileMode.CLAMP
val angleInRadians = Math.toRadians(mAngle.toDouble())
val halfWidth = width / 2
val halfHeight = height / 2
val sinAngle = sin(angleInRadians)
val cosAngle = cos(angleInRadians)
val x0 = (halfWidth * (1 + sinAngle)).toFloat()
val y0 = (halfHeight * (1 - cosAngle)).toFloat()
val x1 = (halfWidth * (1 - sinAngle)).toFloat()
val y1 = (halfHeight * (1 + cosAngle)).toFloat()
return LinearGradient(x0, y0, x1, y1, mGradient, null, mode)
}
I keep in memory a list of java.Awt Images and have the need to rotate them. I've read some solutions but they deal with changing the way the image is shown, not really rotating the image itself.
I need to rotate an image itself, not to draw in in a rotated way. How can this be attained?
The following code will rotate an image by an arbitrary angle in degrees.
Positive values for degrees will rotate the image clockwise, negative values counterclockwise.
The resulting image will be adjusted in size, so that the rotated image fits exactly into it.
I have tested it with jpg and png image files as input.
public static BufferedImage rotateImage(BufferedImage src, double degrees) {
double radians = Math.toRadians(degrees);
int srcWidth = src.getWidth();
int srcHeight = src.getHeight();
/*
* Calculate new image dimensions
*/
double sin = Math.abs(Math.sin(radians));
double cos = Math.abs(Math.cos(radians));
int newWidth = (int) Math.floor(srcWidth * cos + srcHeight * sin);
int newHeight = (int) Math.floor(srcHeight * cos + srcWidth * sin);
/*
* Create new image and rotate it
*/
BufferedImage result = new BufferedImage(newWidth, newHeight,
src.getType());
Graphics2D g = result.createGraphics();
g.translate((newWidth - srcWidth) / 2, (newHeight - srcHeight) / 2);
g.rotate(radians, srcWidth / 2, srcHeight / 2);
g.drawRenderedImage(src, null);
return result;
}