Finding white rectangle in an image - java

I'm trying to find a white rectangle in an image. The rectangle size is fixed. This is what I've come up as of yet:
BufferedImage bImage = bufferedImage;
int height = bufferedImage.getHeight(); //~1100px
int width = bufferedImage.getWidth(); //~1600px
int neededWidth = width / 2;
int neededHeight = 150;
int x = 0;
int y = 0;
boolean breaker = false;
boolean found = false;
int rgb = 0xFF00FF00;
int fx, fy;
fx = fy = 0;
JavaLogger.log.info("width, height: " + w + ", " + h);
while ((x != (width / 2) || y != (height - neededHeight)) && found == false) {
for (int i = y; i - y < neededHeight + 1; i++) {
for (int j = x; j - x < neededWidth + 1; j++) { //Vareetu buut, ka +1 vajadziigs
//JavaLogger.log.info("x,y: " + j + ", " + i);
long pixel = bImage.getRGB(j, i);
if (pixel != colorWhite && pixel != -1) {
//bImage.setRGB(j, i, rgb);
//JavaLogger.log.info("x,y: " + (j+x) + ", " + (i+y));
breaker = true;
break;
} else {
//bImage.setRGB(j, i, 0xFFFFFF00);
}
//printPixelARGB(pixel);
if ((i - y == neededHeight-10) && j - x == neededWidth-10) {
JavaLogger.log.info("width, height: " + x + ", " + y + "," + j + ", " + i);
fx = j;
fy = i;
found = true;
breaker = true;
break;
}
}
if (breaker) {
breaker = false;
break;
}
}
if (x < (width / 2)) {
x++;
} else {
if (y < (height - neededHeight)) {
y++;
x = 0;
} else {
break;
}
}
//JavaLogger.log.info("width, height: " + x + ", " + y);
}
if (found == true) {
for (int i = y; i < fy; i++) {
for (int j = x; j < fx; j++) {
bImage.setRGB(j, i, 0xFF00FF3F);
}
}
}
JavaLogger.log.info("width, height: " + w + ", " + h);
This works ok, if the rectangle I need is close to the begining of (0;0), but as it get further away, the performance degrades quite severely. I'm wondering, if there's something that can be done?
For example, this search took nearly 8s, which is quite a lot.
I'm thinking, that this can deffinitely be done more effectively. Maybe some blob finding? Read about it, but I've no idea how to apply it.
Also, I'm new to both Java and Image processing, so any help is appreciated.

This is very rough, but successfully finds all the white pixels in the image, more checking can be done to ensure it is the size you want and everything is there but the basics are there.
PS: I have not tested with your image. r and this.rc is picture size and p and this.px is the inner rectangle size
public static void main(String[] args) {
JFrame frame = new JFrame();
final int r = 100;
final int p = 10;
NewJPanel pan = new NewJPanel(r, p, new A() {
#Override
public void doImage(BufferedImage i) {
int o = 0;
for (int j = 0; j < i.getWidth() - p; j++) {
for (int k = 0; k < i.getHeight() - p; k++) {
PixelGrabber pix2 = new PixelGrabber(
i, j, k, p, p, false);
try {
pix2.grabPixels();
} catch (InterruptedException ex) {}
int pixelColor = pix2.getColorModel()
.getRGB(pix2.getPixels());
Color c = new Color(pixelColor);
if (c.equals(Color.WHITE)) {
System.out.println("Found at : x:" + j + ",y:" + k);
}
}
}
}
});
frame.getContentPane().add(pan);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500, 500);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private interface A {
void doImage(BufferedImage i);
}
private static class NewJPanel extends JPanel {
private static final long serialVersionUID = -5348356640373105209L;
private BufferedImage image = null;
private int px;
private int rc;
private A a;
public NewJPanel(int r, int p, A a) {
this.px = p;
this.rc = r;
this.a = a;
}
public BufferedImage getImage() {
return image;
}
#Override public void paint(Graphics g) {
super.paint(g);
image = new BufferedImage(this.rc, this.rc,
BufferedImage.TYPE_INT_ARGB);
java.awt.Graphics2D g2 = image.createGraphics();
g2.setColor(Color.BLACK);
g2.fillRect(0, 0, this.rc, this.rc);
g2.setColor(Color.WHITE);
g2.fillRect(
new Random().nextInt(this.rc - this.px),
new Random().nextInt(this.rc - this.px),
this.px, this.px);
g.drawImage(image, this.rc, this.rc, this);
this.a.doImage(this.image);
}
}

I'm no expert but I don't think the code is the problem - you need to change your algorithm. I would start by recursively searching for a single white pixel on the 2d plane, something like:
findWhitePixel(square){
look at pixel in the middle of 'square' - if it's white return it, otherwise:
findWhitePixel(top-right-quarter of 'square')
findWhitePixel(top-left-quarter of 'square')
findWhitePixel(bottom-right-quarter of 'square')
findWhitePixel(bottom-left-quarter of 'square')
}
after you find a white pixel try travesing up, down, left and right from it to find the borders on you shape. if it's a given that there can only be rectangles - your done. if there might be other shapes (triangles, circles, etc.) you'll need some verification here.

What you are asking can be solved by the operation known as "erosion". The erosion replaces every pixel by the darkest of all pixels in the rectangle of the requested size at that location (top-left corner). Here, darkest means that non-white supersedes white.
The output of erosion is an image with W-1 columns and H-1 rows less. Any white pixel in it corresponds to a solution.
In the lucky case of a rectangle shape, erosion is a separable operation. This means that you can erode first using an horizontal segment shape, then a vertical segment shape on the output of the first erosion. For a W x H restangle size, this replaces W * H operations by W + H, a significant saving.
In the also lucky case of a binary image (non-white or white), erosion by a segment can be done extremely efficiently: in every row independently, find all contiguous runs of white pixels, and turn the W-1 rightmost ones to non-white. Do the same to all columns, shortening the white runs by H-1 pixels.
Example: find all 3x2 rectangles:
####....####
##.....#..##
#..######...
.....###....
After 3x1 erosion:
####..####
##...#####
#########.
...#####..
After 1x2 erosion:
####.#####
##########
#########.
This algorithms takes constant time per pixel (regardless the rectangle size). Properly implemented, should take a handful of milliseconds.

Related

Recursive Division Maze Java

I'm having problems with completing the algorithm based on this link.
After building a wall I choose the upper or left part of the maze and it seems to create itself only to the point where it needs to break the recursion and enter another divide method call. I'm not sure if I understand the values needed to be passed to the last call of the divide method correctly.
public void divide(int x, int y, int width, int hight) {
if (width< 2 || hight< 2) ;
else {
boolean horizontal = chooseOrientation(width,hight);
if (horizontal) {
int randomNumber = r.nextInt(hight - 1);
wallY = randomNumber + y;
for (int i = x; i < width; i++) {
fields[wallY][i].setHorizontalWall();
}
fields[wallY][r.nextInt(width- 1)].deleteHorizontalWall();
hight = wallY - y + 1;
divide(x, y, width, hight);
}
else {
int randomNumber = r.nextInt(width- 1);
WallX = randomNumber + x;
for (int i = y; i < hight; i++) {
fields[i][WallX].setVerticalWall();
}
fields[r.nextInt(hight - 1) + y][WallX].deleteVerticalWall();
width = WallX - x + 1;
}
if(horizontal){
hight = y + hight + WallY-1;
y = WallY + 1;
}
else {
width = WallX - 1 + width + x;
x = WallX + 1;
}
divide(x, y, width, hight);
}
}
In the "recursive-division" algorithm you start with a full 2-dimensional grid graph and you then start removing edges (= building "walls") alternating in horizontal and vertical "stripes". In every "stripe" only a single edge ("door") is left.
A graph-based version of this algorithm can be found here:
https://github.com/armin-reichert/mazes
https://github.com/armin-reichert/mazes/blob/master/mazes-algorithms/src/main/java/de/amr/maze/alg/others/RecursiveDivision.java

Calculating 'color distance' between 2 points in a 3-dimensional space

I have a homework task where I have to write a class responsible for contour detection. It is essentially an image processing operation, using the definition of euclidean distance between 2 points in the 3-dimensional space. Formula given to us to use is:
Math.sqrt(Math.pow(pix1.red - pix2.red,2) + Math.pow(pix1.green- pix2.green,2) + Math.pow(pix1.blue- pix2.blue,2));
We need to consider each entry of the two dimensional array storing the colors of the pixels of an image, and if some pixel, pix, the color distance between p and any of its neighbors is more than 70, change the color of the pixel to black, else change it to white.
We are given a seperate class as well responsible for choosing an image, and selecting an output, for which method operationContouring is applied to. Java syntax and convention is very new to me having started with python. Conceptually, I'm struggling to understand what the difference between pix1 and pix2 is, and how to define them. This is my code so far.
Given:
import java.awt.Color;
/* Interface for ensuring all image operations invoked in same manner */
public interface operationImage {
public Color[][] operationDo(Color[][] imageArray);
}
My code:
import java.awt.Color;
public class operationContouring implements operationImage {
public Color[][] operationDo(Color[][] imageArray) {
int numberOfRows = imageArray.length;
int numberOfColumns = imageArray[0].length;
Color[][] results = new Color[numberOfRows][numberOfColumns];
for (int i = 0; i < numberOfRows; i++)
for (int j = 0; j < numberOfColumns; j++) {
int red = imageArray[i][j].getRed();
int green = imageArray[i][j].getGreen();
int blue = imageArray[i][j].getBlue();
double DistanceColor = Math.sqrt(Math.pow(pix1.red - pix2.red,2) + Math.pow(pix1.green- pix2.green,2) + Math.pow(pix1.blue- pix2.blue,2));
int LIMIT = 70;
if (DistanceColor> LIMIT ) {
results[i][j] = new Color((red=0), (green=0), (blue=0));
}
else {
results[i][j] = new Color((red=255), (green=255), (blue=255));
}
}
return results;
}
}
This is a solution I wrote that uses BufferedImages. I tested it and it should work. Try changing it such that it uses your data format (Color[][]) and it should work for you too. Note that "pix1" is nothing more than a description of the color of some pixel, and "pix2" is the description of the color of the pixel you are comparing it to (determining whether the color distance > 70).
public static boolean tooDifferent(Color c1, Color c2) {
return Math.sqrt(Math.pow(c1.getRed() - c2.getRed(),2) + Math.pow(c1.getGreen()- c2.getGreen(),2) + Math.pow(c1.getBlue()- c2.getBlue(),2)) > 70;
}
public static Color getColor(int x, int y, BufferedImage img) {
return new Color(img.getRGB(x, y));
}
public static BufferedImage operationDo(BufferedImage img) {
int numberOfRows = img.getHeight();
int numberOfColumns = img.getWidth();
BufferedImage results = new BufferedImage(numberOfColumns, numberOfRows, BufferedImage.TYPE_INT_ARGB);
for (int y = 0; y < numberOfRows; y++) {
for (int x = 0; x < numberOfColumns; x++) {
Color color = new Color(img.getRGB(x, y));
boolean aboveExists = y > 0;
boolean belowExists = y < numberOfRows - 1;
boolean leftExists = x > 0;
boolean rightExists = x < numberOfColumns - 1;
if ((aboveExists && tooDifferent(color, getColor(x, y - 1, img))) ||
(belowExists && tooDifferent(color, getColor(x, y + 1, img))) ||
(leftExists && tooDifferent(color, getColor(x - 1, y, img))) ||
(rightExists && tooDifferent(color, getColor(x + 1, y, img)))) {
results.setRGB(x, y, Color.black.getRGB());
} else {
results.setRGB(x, y, Color.white.getRGB());
}
}
}
return results;
}

How to keep a color in a fill() the same after mousePressed?

What would be the easiest and simplest way to keep the fill() the same after clicking (that's when it changes) and then unclicking, and leaving hover?
In this project, I simply made a grid. When the mouse hovers over a specific rect (at x,y) it changes color based on the state it is in. fill(50) is the default, fill(75) is when the mouse is hovering, and fill(100) is when the mouse clicks. But here when the mouse is unclicked it returns to hover fill until the mouse leaves the rectangle. Thanks.
int cols, rows;
int scl = 20;
void setup() {
size(400, 400);
int w = 400;
int h = 400;
cols = w / scl;
rows = h / scl;
}
void draw() {
background(255);
for (int x = 0; x < cols; x++) {
for (int y = 0; y < rows; y++) {
int xpos = x*scl;
int ypos = y*scl;
stroke(55);
if((mouseX >= xpos && mouseX <= xpos+scl) &&
(mouseY >= ypos && mouseY <= ypos+scl)){
fill(75);
if (mousePressed == true){
println("Clicked at: " + xpos + " and " + ypos);
fill(100);
//here is the desired location for the fill to remain constant even
//after unclicking and leaving hover
}
println("Mouse at: " + xpos + " and " + ypos);
}else{
fill(50);
}
rect(xpos, ypos, scl, scl);
}
}
}
Stack Overflow isn't really designed for general "how do I do this" type questions. It's for specific "I tried X, expected Y, but got Z instead" type questions. But I'll try to help in a general sense:
You need to store the state of each cell in a data structure, and then use that data structure to draw your scene.
You could do this with a 2D array, where each cell in the array represents a cell in the grid. You could store the state of the cell, or the color directly.
As Kevin said, you should keep the state of your application in a matrix.
boolean[][] matrix = new boolean[21][21];
When you click on a cell, toggle it
if(!matrix[xpos/scl][ypos/scl]) {
matrix[xpos/scl][ypos/scl] = true;
} else {
matrix[xpos/scl][ypos/scl] = false;
}
Inside this loop, check if your current position can be drawn or not
if(matrix[x][y]) {
fill(204, 102, 0); // an orange color
rect(xpos, ypos, scl, scl);
}
So your draw() method should look like this
void draw() {
background(255);
for (int x = 0; x < cols; x++) {
for (int y = 0; y < rows; y++) {
int xpos = x*scl;
int ypos = y*scl;
stroke(55);
if((mouseX >= xpos && mouseX <= xpos+scl) &&
(mouseY >= ypos && mouseY <= ypos+scl)){
fill(75);
if (mousePressed == true){
println("Clicked at: " + xpos + " and " + ypos);
if(!matrix[xpos/scl][ypos/scl]) {
matrix[xpos/scl][ypos/scl] = true;
} else {
matrix[xpos/scl][ypos/scl] = false;
}
fill(100);
//here is the desired location for the fill to remain constant even
//after unclicking and leaving hover
}
println("Mouse at: " + xpos + " and " + ypos);
}else{
fill(50);
}
if(matrix[x][y]) {
fill(204, 102, 0);
rect(xpos, ypos, scl, scl);
}
rect(xpos, ypos, scl, scl);
}
}
}

Java - Detecting collisions via Array-stored collision

I am currently working on collision for my 2D game. I did some research and deducted that I should use the method of storing alpha value pixels into a "mask" of the image of the entity, and the same for the other. Then, I take both entitys' x & y co-ords, as well as height and width, and make a Rectangle object and use the method Rectangle.intersects(Rectangle r) to check if they do infact collide inorder to make it more efficent instead of going through 2 for loops.
IF they intersect, I then make a new Array with the dimensions :
int maxLengthY = Math.max(thisEntity.getMask().length, e.getMask().length);
int maxLengthX = Math.max(thisEntity.getMask()[0].length, thisEntity.getMask()[0].length);
int minX = Math.min(thisEntity.getX(), e.getX());
int minY = Math.min(thisEntity.getY(), e.getY());
int[][] map = new int[maxLengthX + minX][maxLengthY + minY];
and then add the other two masks onto this one with their corresponding y & x "boundaries", like so:
for(int curX = 0; curX < maxLengthX + minX; curX++) { //only loop through the co-ords that area affected
for(int curY = 0; curY < maxLengthY + minY; curY++) {
int this_x = thisEntity.getX();
int this_width = thisEntity.getImage().getWidth();
int this_y = thisEntity.getY();
int this_height = thisEntity.getImage().getHeight();
int[][] this_mask = thisEntity.getMask();
if(curX < (this_x + this_width) && curX < this_x) {//check that the co-ords used are relevant for thisEntity's mask
if(curY < (this_y + this_height) && curY < this_y) {
map[curX][curY] = this_mask[Math.abs(curX - this_x)][Math.abs(curY - this_y)]; // store data from mask to map
}
}
int other_x = e.getX();
int other_width = e.getImage().getWidth();
int other_y = e.getY();
int other_height = e.getImage().getHeight();
int[][] other_mask = e.getMask();
if(curX < (other_x + other_width) && curX > other_x) { //check that the co-ords used are relevant for e's mask
if(curY < (other_y + other_height) && curY > other_y) {
if(map[curX][curY] == 1) { //check if this segment is already written by thisEntity
map[curX][curY] = 2; //if yes, set to 2 instead of e's value to show collision
} else {
map[curX][curY] = other_mask[curX][curY]; // the minus to nullify minX and minY "requirements"
}
}
}
}
}
resulting in the Array "map" looking like SO:
(excuse my 1337 paint skills)
This is the code in all it's beauty:
public Entity[] collisions(Entity thisEntity) {
ArrayList<Entity> list = new ArrayList<Entity>();
try {
for (Entity e : getLevel().getEntities()) {
System.out.println("rect contains = "+thisEntity.getRect().contains(e.getRect()));
if (!thisEntity.equals(e)) {
Rectangle r = e.getRect();
r = thisEntity.getRect();
if (thisEntity.getRect().intersects(e.getRect())) {
//get variables to create a space designated for the intersection areas involved
int maxLengthY = Math.max(thisEntity.getMask().length, e.getMask().length);
int maxLengthX = Math.max(thisEntity.getMask()[0].length, thisEntity.getMask()[0].length);
int minX = Math.min(thisEntity.getX(), e.getX());
int minY = Math.min(thisEntity.getY(), e.getY());
int[][] map = new int[maxLengthX + minX][maxLengthY + minY]; //create a matrix which merges both Entity's mask's to compare
for(int curX = 0; curX < maxLengthX + minX; curX++) { //only loop through the co-ords that area affected
for(int curY = 0; curY < maxLengthY + minY; curY++) {
int this_x = thisEntity.getX();
int this_width = thisEntity.getImage().getWidth();
int this_y = thisEntity.getY();
int this_height = thisEntity.getImage().getHeight();
int[][] this_mask = thisEntity.getMask();
if(curX < (this_x + this_width) && curX > this_x) {//check that the co-ords used are relevant for thisEntity's mask
if(curY < (this_y + this_height) && curY > this_y) {
map[curX][curY] = this_mask[Math.abs(curX - this_x)][Math.abs(curY - this_y)]; // store data from mask to map
}
}
int other_x = e.getX();
int other_width = e.getImage().getWidth();
int other_y = e.getY();
int other_height = e.getImage().getHeight();
int[][] other_mask = e.getMask();
if(curX < (other_x + other_width) && curX > other_x) { //check that the co-ords used are relevant for e's mask
if(curY < (other_y + other_height) && curY > other_y) {
if(map[curX][curY] == 1) { //check if this segment is already written by thisEntity
map[curX][curY] = 2; //if yes, set to 2 instead of e's value to show collision
} else {
map[curX][curY] = other_mask[curX][curY]; // the minus to nullify minX and minY "requirements"
}
}
}
}
}
}
}
}
} catch (Exception excp) {
excp.printStackTrace();
}
return list.toArray(new Entity[1]);
}
Also, here is the method getMask() :
public int[][] getMask() {
return mask;
}
...
private void createMask(BufferedImage image) {
final int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
final int width = image.getWidth();
final int height = image.getHeight();
final boolean hasAlphaChannel = image.getAlphaRaster() != null;
int[][] result = new int[height][width];
if (hasAlphaChannel) {
for (int pixel = 0, row = 0, col = 0; pixel < pixels.length; pixel += 4) {
int alpha = pixels[pixel];
if(alpha != 0) {
result[row][col] = 1;
} else {
result[row][col] = 0;
}
if (col == width) {
col = 0;
row++;
}
}
}
mask = result;
}
However... this code does not work as intended, and in some cases at all as when adding the individual masks to the map I get IndexOutOfBounds even though it should work so it's probably just me overlooking something...
So, to conclude, I need help with my code:
What is wrong with it?
How can I fix it?
Is there a more efficent way of doing this type of collision?
Do you recommend other types of collision? If so, what are they?
When you create Entities, do you create their masks from images of the exact same size? Because otherwise entity's mask and image map would be using different coordinate systems (entity.mask[0][0] might be is at its corner, when map[0][0] is at the corner of the "world"), and you're comparing same indices at the line:
map[curX][curY] = other_mask[curX][curY];
(above in the code you're actually getting those to the same coordinate system with Math.abs(a-b))
As for more efficient ways to detect collisions, you can look into binary space partitioning and more on collision detection in general on Wikipedia.

Wave generation with the "Hugo Elias" algorithm please! Java

I appear to have hit a wall in my most recent project involving wave/ripple generation over an image. I made one that works with basic colors on a grid that works perfectly; heck, I even added shades to the colors depending on the height of the wave.
However, my overall goal was to make this effect work over an image like you would see here. I was following an algorithm that people are calling the Hugo Elias method (though idk if he truly came up with the design). His tutorial can be found here!
When following that tutorial I found his pseudo code challenging to follow. I mean the concept for the most part makes sense until I hit the height map portion over an image. The problem being the x and y offsets throw an ArrayIndexOutOfBoundsException due to him adding the offset to the corresponding x or y. If the wave is too big (i.e. in my case 512) it throws an error; yet, if it is too small you can't see it.
Any ideas or fixes to my attempted implementation of his algorithm?
So I can't really make a compile-able version that is small and shows the issue, but I will give the three methods I'm using in the algorithm. Also keep in mind that the buffer1 and buffer2 are the height maps for the wave (current and previous) and imgArray is a bufferedImage represented by a int[img.getWidth() * img.getHeight()] full of ARGB values.
Anyways here you go:
public class WaveRippleAlgorithmOnImage extends JPanel implements Runnable, MouseListener, MouseMotionListener
{
private int[] buffer1;
private int[] buffer2;
private int[] imgArray;
private int[] movedImgArray;
private static double dampening = 0.96;
private BufferedImage img;
public WaveRippleAlgorithmOnImage(BufferedImage img)
{
this.img = img;
imgArray = new int[img.getHeight()*img.getWidth()];
movedImgArray = new int[img.getHeight()*img.getWidth()];
imgArray = img.getRGB(0, 0,
img.getWidth(), img.getHeight(),
null, 0, img.getWidth());
//OLD CODE
/*for(int y = 0; y < img.getHeight(); y++)
{
for(int x = 0; x < img.getWidth(); x++)
{
imgArray[y][x] = temp[0 + (y-0)*img.getWidth() + (x-0)];
}
}*/
buffer1 = new int[img.getHeight()*img.getWidth()];
buffer2 = new int[img.getHeight()*img.getWidth()];
buffer1[buffer1.length/2] = (img.getWidth() <= img.getHeight() ? img.getWidth() / 3 : img.getHeight() / 3);
//buffer1[25][25] = 10;
back = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_ARGB);
this.addMouseListener(this);
this.addMouseMotionListener(this);
}
//<editor-fold defaultstate="collapsed" desc="Used Methods">
#Override
public void run()
{
while(true)
{
this.update();
this.repaint();
this.swap();
}
}
//Called from Thread to update movedImgArray prior to being drawn.
private void update()
{
//This is my attempt of trying to convert his code to java.
for (int i=img.getWidth(); i < imgArray.length - 1; i++)
{
if(i % img.getWidth() == 0 || i >= imgArray.length - img.getWidth())
continue;
buffer2[i] = (
((buffer1[i-1]+
buffer1[i+1]+
buffer1[i-img.getWidth()]+
buffer1[i+img.getWidth()]) >> 1)) - buffer2[i];
buffer2[i] -= (buffer2[i] >> 5);
}
//Still my version of his code, because of the int[] instead of int[][].
for (int y = 1; y < img.getHeight() - 2; y++)
{
for(int x = 1; x < img.getWidth() - 2; x++)
{
int xOffset = buffer1[((y)*img.getWidth()) + (x-1)] - buffer1[((y)*img.getWidth()) + (x+1)];
int yOffset = buffer1[((y-1)*img.getWidth()) + (x)] - buffer1[((y+1)*img.getWidth()) + (x)];
int shading = xOffset;
//Here is where the error occurs (after a click or wave started), because yOffset becomes -512; which in turn gets
//multiplied by y... Not good... -_-
movedImgArray[(y*img.getWidth()) + x] = imgArray[((y+yOffset)*img.getWidth()) + (x+xOffset)] + shading;
}
}
//This is my OLD code that kidna worked...
//I threw in here to show you how I was doing it before I switched to images.
/*
for(int y = 1; y < img.getHeight() - 1; y++)
{
for(int x = 1; x < img.getWidth() - 1; x++)
{
//buffer2[y][x] = ((buffer1[y][x-1] +
//buffer1[y][x+1] +
//buffer1[y+1][x] +
//buffer1[y-1][x]) / 4) - buffer2[y][x];
buffer2[y][x] = ((buffer1[y][x-1] +
buffer1[y][x+1] +
buffer1[y+1][x] +
buffer1[y-1][x] +
buffer1[y + 1][x-1] +
buffer1[y + 1][x+1] +
buffer1[y - 1][x - 1] +
buffer1[y - 1][x + 1]) / 4) - buffer2[y][x];
buffer2[y][x] = (int)(buffer2[y][x] * dampening);
}
}*/
}
//Swaps buffers
private void swap()
{
int[] temp;
temp = buffer2;
buffer2 = buffer1;
buffer1 = temp;
}
//This creates a wave upon clicking. It also is where that 512 is coming from.
//512 was about right in my OLD code shown above, but helps to cause the Exeception now.
#Override
public void mouseClicked(MouseEvent e)
{
if(e.getX() > 0 && e.getY() > 0 && e.getX() < img.getWidth() && e.getY() < img.getHeight())
buffer2[((e.getY())*img.getWidth()) + (e.getX())] = 512;
}
private BufferedImage back;
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
back.setRGB(0, 0, img.getWidth(), img.getHeight(), movedImgArray, 0, img.getWidth());
g.drawImage(back, 0, 0, null);
}
}
P.S. Here are two images of the old code working.
Looking at my original pseudocode, I assume the Array Out Of Bounds error is happening when you try to look up the texture based on the offset. The problem happens because the refraction in the water is allowing us to see outside of the texture.
for every pixel (x,y) in the buffer
Xoffset = buffer(x-1, y) - buffer(x+1, y)
Yoffset = buffer(x, y-1) - buffer(x, y+1)
Shading = Xoffset
t = texture(x+Xoffset, y+Yoffset) // Array out of bounds?
p = t + Shading
plot pixel at (x,y) with colour p
end loop
The way to fix this is simply to either clamp the texture coordinates, or let them wrap. Also, if you find that the amount of refraction is too much, you can reduce it by bit-shifting the Xoffset and Yoffset values a little bit.
int clamp(int x, int min, int max)
{
if (x < min) return min;
if (x > max) return max;
return x;
}
int wrap(int x, int min, int max)
{
while (x<min)
x += (1+max-min);
while (x>max)
x -= (1+max-min);
return x;
}
for every pixel (x,y) in the buffer
Xoffset = buffer(x-1, y) - buffer(x+1, y)
Yoffset = buffer(x, y-1) - buffer(x, y+1)
Shading = Xoffset
Xoffset >>= 1 // Halve the amount of refraction
Yoffset >>= 1 // if you want.
Xcoordinate = clamp(x+Xoffset, 0, Xmax) // Use clamp() or wrap() here
Ycoordinate = clamp(y+Yoffset, 0, Ymax) //
t = texture(Xcoordinate, Ycoordinate)
p = t + Shading
plot pixel at (x,y) with colour p
end loop

Categories