I'm trying to rotate a Bitmap where the pixels are stored in an Array int pixels[]. I got the following method:
public void rotate(double angle) {
double radians = Math.toRadians(angle);
double cos, sin;
cos = Math.cos(radians);
sin = Math.sin(radians);
int[] pixels2 = pixels;
for (int x = 0; x < width; x++)
for (int y = 0; y < height; y++) {
int centerx = this.width / 2, centery = this.height / 2;
int m = x - centerx;
int n = y - centery;
int j = (int) (m * cos + n * sin);
int k = (int) (n * cos - m * sin);
j += centerx;
k += centery;
if (!((j < 0) || (j > this.width - 1) || (k < 0) || (k > this.height - 1)))
try {
pixels2[(x * this.width + y)] = pixels[(k * this.width + j)];
} catch (Exception e) {
e.printStackTrace();
}
}
pixels = pixels2;
}
But it just gives me crazy results. Does anyone know where the error is?
The line
int[] pixels2 = pixels;
is supposed to copy the array, but you are just copying the reference to it. Use pixels.clone(). In fact, you just need a new, empty array, so new int[pixels.lenght] is enough. In the end you need System.arraycopy to copy the new content into the old array.
There are other problems in your code -- you are mixing up rows and columns. Some expressions are written as though the image is stored row by row, others as if column by column. If row-by-row (my assumption), then this doesn't make sense: x*width + y. It should read y*width + x -- you are skipping y rows down and then moving x columns to the right. All in all, I have this code that works OK:
import static java.lang.System.arraycopy;
public class Test
{
private final int width = 5, height = 5;
private int[] pixels = {0,0,1,0,0,
0,0,1,0,0,
0,0,1,0,0,
0,0,1,0,0,
0,0,1,0,0};
public Test rotate(double angle) {
final double radians = Math.toRadians(angle),
cos = Math.cos(radians), sin = Math.sin(radians);
final int[] pixels2 = new int[pixels.length];
for (int x = 0; x < width; x++)
for (int y = 0; y < height; y++) {
final int
centerx = this.width / 2, centery = this.height / 2,
m = x - centerx,
n = y - centery,
j = ((int) (m * cos + n * sin)) + centerx,
k = ((int) (n * cos - m * sin)) + centery;
if (j >= 0 && j < width && k >= 0 && k < this.height)
pixels2[(y * width + x)] = pixels[(k * width + j)];
}
arraycopy(pixels2, 0, pixels, 0, pixels.length);
return this;
}
public Test print() {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++)
System.out.print(pixels[width*y + x]);
System.out.println();
}
System.out.println();
return this;
}
public static void main(String[] args) {
new Test().print().rotate(-45).print();
}
}
public void render(float nx, float ny, float nz, float size, float rotate) {
int wid = (int) ((width - nz) * size);
int hgt = (int) ((height - nz) * size);
if (wid < 0 || hgt < 0) {
wid = 0;
hgt = 0;
}
for (int x = 0; x < wid; x++) {
for (int y = 0; y < hgt; y++) {
double simple = Math.PI;
int xp = (int) (nx +
Math.cos(rotate) * ((x / simple) - (wid / simple) / 2) + Math
.cos(rotate + Math.PI / 2)
* ((y / simple) - (hgt / simple) / 2));
int yp = (int) (ny + Math.sin(rotate)
* ((x / simple) - (wid / simple) / 2) + Math.sin(rotate
+ Math.PI / 2)
* ((y / simple) - (hgt / simple) / 2));
if (xp + width < 0 || yp + height < 0 || xp >= Main.width
|| yp >= Main.height) {
break;
}
if (xp < 0
|| yp < 0
|| pixels[(width / wid) * x + ((height / hgt) * y)
* width] == 0xFFFF00DC) {
continue;
}
Main.pixels[xp + yp * Main.width] = pixels[(width / wid) * x
+ ((height / hgt) * y) * width];
}
}
}
This is only a new to rotating for me, but the process of this is that of a normal rotation. It still needs much fixing -- it's inefficient and slow. But in a small program, this code works. I'm posting this so you can take it, and make it better. :)
Related
I wrote a function which takes two parameters:
JPG image as 3D array
rotation degrees given by alpha
My approach was:
public static int[][] rotate(int[][] img, double alpha) {
double rad = Math.toRadians(alpha);
double sin = Math.sin(rad);
double cos = Math.cos(rad);
int height = img.length;
int width = img[0].length;
int[][] rotate = new int[height][width];
for(int i = 0; i < height; i++) {
for(int j = height - i - 1; j < width; j++) {
if(j < height && i < width) {
double i_new = Math.floor(cos * (img[i].length - i) - sin * (img[j].length - j)) + i;
double j_new = Math.floor(sin * (img[i].length - i) + cos * (img[j].length - j)) + j;
rotate[i][j] = img[(int)j_new][(int)i_new];
}
}
}
return rotate;
}
While fixing the index range, the output is a black image. What am I missing?
After a while I got to a solution.
Caution: Its not using any special pre-defined libraries.
The global function which run`s over the matrice:
public static int[][] rotate(int[][] img, double alpha) {
double rad = Math.toRadians(alpha); //construct of the relevant angles
double sin = Math.sin(rad);
double cos = Math.cos(rad);
int height = img.length;
int width = img[0].length;
int[][] rotate = new int[height][width];
int a = height / 2; //we will use the area of a and b to compare coordinates by the formula given
int b = width / 2;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
double i_new = Math.floor(cos * (i - a) - sin * (j - b)) + a; // following the conversion function
double j_new = Math.floor(sin * (i - a) + cos * (j - b)) + b;
if (i_new >= rotate.length || i_new < 0 || j_new >= rotate[0].length || j_new < rotate[0][0]) { // if out of scope of the conversion --> print none
System.out.print(""); //mainly cause 'continue' statements are not necessary in java and JS
} else {
rotate[(int) i_new][(int) j_new] = img[i][j]; //convert
}
}
}
return rotate;
}
The global function which rotates each 2D matrice:
public static int[][][] rotate_alpha(int[][][] img, double alpha) {
int height = img[0].length;
int width = img[0][0].length;
int[][][] rotated = new int[3][height][width];
for (int k = 0; k < 3; k++) {
rotated[k] = rotate(img[k], alpha);
}
return rotated;
}
Hope this topic is solved by now, and stands by all the standards of the clean code.
The image rotates with code below, but wrong, some black dots appears on original image. I believe it's something with rotation code. Any solution? Thanks. The image dimensions is 32x32 pixels loaded on center of screen (320x240).
public class RendPanel extends JPanel {
private static final long serialVersionUID = 1L;
int widthe = 320;
int heighte = 240;
double angle = Math.toRadians(220);
double sin = Math.sin(angle);
double cos = Math.cos(angle);
double x0 = 0.5 * (widthe - 1); // point to rotate about
double y0 = 0.5 * (heighte - 1); // center of image
public static BufferedImage fbuffer;
public RendPanel(int width, int height) {
fbuffer = new BufferedImage(320, 240, BufferedImage.TYPE_INT_RGB);
BufferedImage in = null;
try { in = ImageIO.read(new File("square.png")); } //32x32 square .png
catch (IOException e) { e.printStackTrace(); }
for (int i = 0; i < in.getWidth(); i++) {
for (int j = 0; j < in.getHeight(); j++) {
fbuffer.setRGB(i + (320 / 2) - 16, j + (240 / 2) - 16, in.getRGB(i, j));
}
}
BufferedImage neww = new BufferedImage(320, 240, BufferedImage.TYPE_INT_RGB);
for (int x = 0; x < widthe; x++) {
for (int y = 0; y < heighte; y++) {
if(x >= x0 - 32 && x <= x0 + 32 && y >= y0 - 32 && y <= y0 + 32){
double a = x - x0;
double b = y - y0;
int xx = (int) (+a * cos - b * sin + x0);
int yy = (int) (+a * sin + b * cos + y0);
// plot pixel (x, y) the same color as (xx, yy) if it's in bounds
if (xx >= 0 && xx < width && yy >= 0 && yy < height) {
neww.setRGB(xx, yy, fbuffer.getRGB(x, y));
}
}
}
}
fbuffer = neww;
repaint();
setPreferredSize(new Dimension(width, height));
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(fbuffer, 0, 0, null);
}
}
A beginner's mistake (sorry).
Taking every source pixel in turn, transforming the coordinates to the destination and copying the pixel value is not the right way.because the regular input grid will not map to a regular grid, and there will be voids (and overlaps).
The correct way is to scan the destination image (so that every destination pixel is reached) and counter-transform the coordinates to fetch the pixel value from the source.
As a refinement, you can use the four neighboring pixel from where you land in the source and perform bilinear interpolation, to reduce aliasing.
Man, it's strange, because in this code it works properly!
Heres a working code:
public class RendPanel extends JPanel {
private static final long serialVersionUID = 1L;
int widthe = 320;
int heighte = 240;
int ang = 0;
double x0 = 0.5 * (widthe - 1); // point to rotate about
double y0 = 0.5 * (heighte - 1); // center of image
public static BufferedImage fbuffer;
public RendPanel(int width, int height) {
fbuffer = new BufferedImage(320, 240, BufferedImage.TYPE_INT_RGB);
BufferedImage in = null;
try { in = ImageIO.read(new File("square.png")); } //32x32 square .png
catch (IOException e) { e.printStackTrace(); }
for (int i = 0; i < in.getWidth(); i++) {
for (int j = 0; j < in.getHeight(); j++) {
fbuffer.setRGB(i + (320 / 2) - 16, j + (240 / 2) - 16, in.getRGB(i, j));
}
}
setPreferredSize(new Dimension(width, height));
}
BufferedImage neww;
public void r(){
neww = new BufferedImage(320, 240, BufferedImage.TYPE_INT_RGB);
double angle = Math.toRadians(ang);
double sin = Math.sin(angle);
double cos = Math.cos(angle);
for (int x = 0; x < widthe; x++) {
for (int y = 0; y < heighte; y++) {
if(x >= x0 - 32 && x <= x0 + 32 && y >= y0 - 32 && y <= y0 + 32){
double a = x - x0;
double b = y - y0;
int xx = (int) (+a * cos - b * sin + x0);
int yy = (int) (+a * sin + b * cos + y0);
// plot pixel (x, y) the same color as (xx, yy) if it's in bounds
if (xx >= 0 && xx < widthe && yy >= 0 && yy < heighte) {
neww.setRGB(x, y, fbuffer.getRGB(xx, yy));
}
}
}
}
ang++;
repaint();
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(neww, 0, 0, null);
}
}
Thanks for:
https://introcs.cs.princeton.edu/java/31datatype/Rotation.java.html
EDIT:
You have to invert the vars on bf2.setRGB(x, y, fbuffer.getRGB(xx, yy)); to the rotated coordinate.
I am doing my program using jfeaturelib for searching glcm features. I am using this haralick.java and already run my program perfectly using this demo HaralickDemo.java. All I want to know is how to use horizontal neigbhor 0 degree only or 90 degree only. This is the code:
private void calculate() {
calculateGreyValues();
final int imageWidth = image.getWidth();
final int imageHeight = image.getHeight();
final int d = HARALICK_DIST;
int i, j, pos;
// image is not empty per default
for (int y = 0; y < imageHeight; y++) {
for (int x = 0; x < imageWidth; x++) {
pos = imageWidth * y + x;
// horizontal neighbor: 0 degrees
i = x - d;
// j = y;
if (!(i < 0)) {
increment(grayValue[pos], grayValue[pos - d]);
}
// vertical neighbor: 90 degree
// i = x;
j = y - d;
if (!(j < 0)) {
increment(grayValue[pos], grayValue[pos - d * imageWidth]);
}
// 45 degree diagonal neigbor
i = x + d;
j = y - d;
if (i < imageWidth && !(j < 0)) {
increment(grayValue[pos], grayValue[pos + d - d * imageWidth]);
}
// 135 vertical neighbor
i = x - d;
j = y - d;
if (!(i < 0) && !(j < 0)) {
increment(grayValue[pos], grayValue[pos - d - d * imageWidth]);
}
}
}
And can you explain me detail of this haralick.java program?
This is a little bit of my project code:
public void render(int[] pixels, int offset, int row) {
for (int yTile = yOffset <<3; yTile <= (yOffset + height) >>3; yTile++) {
int yMin = yTile * 8 - yOffset;
int yMax = yMin + 8;
if (yMin < 0) yMin = 0;
if (yMax > height) yMax = height;
for (int xTile = xOffset <<3; xTile <= (xOffset + width) >>3; xTile++) {
int xMin = xTile * 8 - xOffset;
int xMax = xMin + 8;
if (xMin < 0) xMin = 0;
if (xMax > width) xMax = width;
int tileIndex = (xTile &(MAP_WIDTH_MASK)) + (yTile &(MAP_WIDTH_MASK)) * MAP_WIDTH;
for (int y = yMin; y <yMax; y++) {
int sheetPixel = ((y + yOffset) & 7) * sheet.width + ((xMin + xOffset) & 7);
int tilePixel = offset + xMin + y * row;
for (int x = xMin; x < xMax; x++) {
int colour = tileIndex * 4 + sheet.pixels[sheetPixel++];
pixels[tilePixel++] = colours[colour];
}
}
}
}
}
}
When I run my project, it gives me an error on this line:
int colour = tileIndex * 4 + sheet.pixels[sheetPixel++];
Can you please tell me how can I can fix this error?
In your code, I did not see where sheet is defined.
Try change it
int colour = tileIndex * 4 + pixels[sheetPixel++];
I assume you are trying to access the value from int[] pixels passing as a parameter in the function call.
I am trying to add some texture to my game. I am running into some problems getting the image to display properly.
This is what the texture should look like, just a boring black square:
And this is what I get. A little bit of black with blue lines.
This is the code I used to import the image. The BufferedImage is set to Type_INT_RGB:
package com.mime.minefront.graphics;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
public class Texture {
public static Render floor = loadBitmap("/textures/floorb.png");
public static Render loadBitmap(String fileName) {
try {
BufferedImage image = ImageIO.read(Texture.class.getResource(fileName));
int width = image.getWidth();
int height = image.getHeight();
Render result = new Render(width, height);
image.getRGB(0, 0, width, height, result.pixels, 0, width);
return result;
} catch (Exception e) {
System.out.println("CRASH!");
throw new RuntimeException(e);
}
}
}
Any help or advice would be great. I have tried to search for the answer but with no luck.
This is my Render class.
package com.mime.minefront.graphics;
public class Render {
public final int width;
public final int height;
public final int[] pixels;
public Render(int width, int height) {
this.width = width;
this.height = height;
pixels = new int[width * height];
}
public void draw(Render render, int xOffset, int yOffset) {
for (int y = 0; y < render.height; y++) {
int yPix = y + yOffset;
if (yPix < 0 || yPix >= height) {
continue;
}
for (int x = 0; x < render.width; x++) {
int xPix = x + xOffset;
if (xPix < 0 || xPix >= width) {
continue;
}
int aplha = render.pixels[x + y * render.width];
if (aplha > 0) {
pixels[xPix + yPix * width] = aplha;
}
}
}
}
}
and this is my Render3D class
package com.mime.minefront.graphics;
import com.mime.minefront.Game;
import com.mimi.minefront.input.Controller;
import com.mimi.minefront.input.InputHandler;
import java.awt.Robot;
import java.util.Random;
public class Render3D extends Render {
public double[] zBuffer;
private double renderDistance = 5000;
private double forward, right, up, cosine, sine;
public Render3D(int width, int height) {
super(width, height);
zBuffer = new double[width * height];
}
public void floor(Game game) {
double floorPosition = 8;
double cellingPosition = 8;
forward = game.controls.z;
right = game.controls.x;
up = game.controls.y;
double walking = Math.sin(game.time / 6.0) * 0.5;
if (Controller.crouchWalk) {
walking = Math.sin(game.time / 6.0) * 0.25;
}
if (Controller.runWalk) {
walking = Math.sin(game.time / 6.0) * 0.8;
}
double rotation = 0;//Math.sin(game.time / 20) * 0.5; //game.controls.rotation;
cosine = Math.cos(rotation);
sine = Math.sin(rotation);
for (int y = 0; y < height; y++) {
double celling = (y - height / 2.0) / height;
double z = (floorPosition + up) / celling;
if (Controller.walk) {
z = (floorPosition + up + walking) / celling;
}
if (celling < 0) {
z = (cellingPosition - up) / -celling;
if (Controller.walk) {
z = (cellingPosition - up - walking) / -celling;
}
}
for (int x = 0; x < width; x++) {
double depth = (x - width / 2.0) / height;
depth *= z;
double xx = depth * cosine + z * sine;
double yy = z * cosine - depth * sine;
int xPix = (int) (xx + right);
int yPix = (int) (yy + forward);
zBuffer[x + y * width] = z;
pixels[x + y * width] = //((xPix & 15) * 16 | ((yPix % 15) * 16) << 8);
Texture.floor.pixels[xPix & 7] + (yPix & 7) * 8;
if (z > 500) {
pixels[x + y * width] = 0;
}
}
}
}
public void renderWall(double xLeft, double xRight, double zDistance, double yHeight) {
double xcLeft = ((xLeft) - right) * 2;
double zcLeft = ((zDistance) - forward) * 2;
double rotLeftSideX = xcLeft * cosine - zcLeft * sine;
double yCornerTL = ((-yHeight) - up) * 2;
double yCornerBL = ((+0.5 - yHeight) - up) * 2;
double rotLeftSideZ = zcLeft * cosine + xcLeft * sine;
double xcRight = ((xRight) - right) * 2;
double zcRight = ((zDistance) - forward) * 2;
double rotRightSideX = xcRight * cosine - zcLeft * sine;
double yCornerTR = ((-yHeight) - up) * 2;
double yCornerBR = ((+0.5 - yHeight) - up) * 2;
double rotRightSideZ = zcRight * cosine + xcRight * sine;
double xPixelLeft = (rotLeftSideX / rotLeftSideZ * height + width / 2);
double xPixelRight = (rotRightSideX / rotRightSideZ * height + width / 2);
if (xPixelLeft >= xPixelRight) {
return;
}
int xPixelLeftInt = (int) (xPixelLeft);
int xPixelRightInt = (int) (xPixelRight);
if (xPixelLeftInt < 0) {
xPixelLeftInt = 0;
}
if (xPixelRightInt > width) {
xPixelRightInt = width;
}
double yPixelLeftTop = (yCornerTL / rotLeftSideZ * height + height / 2);
double yPixelLeftBottom = (yCornerBL / rotLeftSideZ * height + height / 2);
double yPixelRightTop = (yCornerTR / rotRightSideZ * height + height / 2);
double yPixelRightBottom = (yCornerBR / rotRightSideZ * height + height / 2);
double tex1 = 1 / rotLeftSideZ;
double tex2 = 1 / rotRightSideZ;
double tex3 = 0 / rotLeftSideZ;
double tex4 = 8 / rotRightSideZ - tex3;
for (int x = xPixelLeftInt; x < xPixelRightInt; x++) {
double pixelRotation = (x - xPixelLeft) / (xPixelRight - xPixelLeft);
double xTexture= (int) ((tex3+tex4*pixelRotation)/tex1+(tex2-tex1)*pixelRotation);
double yPixelTop = yPixelLeftTop + (yPixelRightTop - yPixelLeftTop) * pixelRotation;
double yPixelBottom = yPixelLeftBottom + (yPixelRightBottom - yPixelLeftBottom) * pixelRotation;
int yPixelTopInt = (int) (yPixelTop);
int yPixelBottomInt = (int) (yPixelBottom);
if (yPixelTopInt < 0) {
yPixelTopInt = 0;
}
if (yPixelBottomInt > height) {
yPixelBottomInt = height;
}
for (int y = yPixelTopInt; y < yPixelBottomInt; y++) {
pixels[x + y * width] = (int) xTexture*100;
zBuffer[x + y * width] = 0;
}
}
}
public void renderDistanceLimiter() {
for (int i = 0; i < width * height; i++) {
int colour = pixels[i];
int brightness = (int) (renderDistance / (zBuffer[i]));
if (brightness < 0) {
brightness = 0;
}
if (brightness > 255) {
brightness = 255;
}
int r = (colour >> 16) & 0xff;
int g = (colour >> 8) & 0xff;
int b = (colour) & 0xff;
r = r * brightness / 255;
g = g * brightness / 255;
b = b * brightness / 255;
pixels[i] = r << 16 | g << 8 | b;
}
}
}
From getRGB() :
Returns an array of integer pixels in the default RGB color model
(TYPE_INT_ARGB) and default sRGB color space, from a portion of the
image data. Color conversion takes place if the default model does not
match the image ColorModel
See if using TYPE_INT_ARGB instead of TYPE_INT_RGB works.