My goal is that I have an image which spits out a bitmap. Now I want display the average color of the image as one giant pixel. This is a fairly simple task, just use bufferImage and get the bitmap which I take each red, green, and blue value, add it all up and then divide by the picture's resolution.
The thing is after doing this, I want to break the image into four quadrants and take the average color for each quadrant and display it. And again break each of the four quadrants and do the same. The issue I am facing is that I am using a recursive statement that does the following:
private static void getBlockAverage(int startHeight, int endHeight, int startWidth, int endWidth, BufferedImage img, BufferedImage blockImg, Color oldAvg) {
if(endHeight <= startHeight || endWidth <= startWidth) {
counter++;
return;
}
// get quadrant pixel average and display, I deleted this portion of the code just to keep things compact
getBlockAverage(startHeight, (startHeight + endHeight)/2, startWidth, (startWidth + endWidth)/2, img, blockImg, color);
getBlockAverage((startHeight + endHeight)/2, endHeight, startWidth, (startWidth + endWidth)/2, img, blockImg, color);
getBlockAverage(startHeight, (startHeight + endHeight)/2, (startWidth+endWidth)/2, endWidth, img, blockImg, color);
getBlockAverage((startHeight+endHeight)/2, endHeight, (startWidth+endWidth)/2, endWidth, img, blockImg, color);
}
It is quite easy to see that this is not what I want as the recursive statement will keep executing getBlockAverage(startHeight, (startHeight + endHeight)/2, startWidth, (startWidth + endWidth)/2, img, blockImg, color); first until it is done and then move onto the next one. This is not what I want. I want the image to be broken down into 4 quadrants and then each of those quadrants gets broken down until all quadrants are broken down and continue.
So for example:
Starting off with 1 quadrants, break into 4. Now for quadrant 1, break that into 4, now quadrant 2, break that into 4, now quadrant 3, break that into 4, now quadrant 4, break that into 4.
Now that I am thinking about it, I feel like I should use some sort of for loop with a cap on the number of iterations but I am not sure how to do that.
I tend to agree with you. I think I would place this method into loop as well but also make the method return the average color for each quadrant into an single dimensional Array with the thought of each Array index is a quadrant number and and the actual element for that index contains the color for that particular quadrant. This way you can work with all the pertinent information that is acquired later on. I would at least get it going and then optimize it once it's working the way I want. Well, that's how I do it anyways :P
Of course I'm assuming throughout all this that the Quadrant dissection flow is something similar to what I show in the image below:
Here is what I would do:
Change the getBlockAverage() method so that it returns a Color...
private static Color getBlockAverage(int startHeight, int endHeight, int startWidth,
int endWidth, BufferedImage img, BufferedImage blockImg, Color oldAvg) {
// get quadrant pixel average color and return it
// with whatever code you've been using....
return theQuadrantAverageColor;
}
then I would create another method which contains our loop, image quadrants dissectional dimensions, and calls to the getBlockAverage() method while the loop is well...looping and for every loop cycle place the returned color from the getBlockAverage() method into a per-established Color Array:
private static void getQuadrantsColorAverages(Color[] quadrantColors, BufferedImage img) {
// Decalre and Initialize required variables.
BufferedImage wrkImg = img;
BufferedImage blockImg = null; //?????
int imgWidth = wrkImg.getWidth();
int imgHeight = wrkImg.getHeight();
int startHeight = 0;
int endHeight = 0;
int startWidth = 0;
int endWidth = 0;
Color oldAvg = null;
int quadCount = 1;
// Start our loop and and continue it until our counter
// variable named quadCount goes over 20....
while (quadCount <= 20) {
// Handle dissectional dimensions (in pixels)
// for quadrants 1 to 20 as layed out within
// the supplied image to this forum post.
switch (quadCount) {
// Quadrant 1
case 1:
startHeight = 0; endHeight = (imgHeight / 2);
startWidth = 0; endWidth = (imgWidth / 2);
// Quadrant 2
case 2:
startWidth = (endWidth + 1); endWidth = imgWidth;
break;
// Quadrant 3
case 3:
startHeight = (endHeight + 1); endHeight = imgHeight;
startWidth = 0; endWidth = (imgWidth / 2);
break;
// Quadrant 4
case 4:
startWidth = (endWidth + 1); endWidth = imgWidth;
break;
// Quadrant 5
case 5:
startHeight = 0; endHeight = (imgHeight / 4);
startWidth = 0; endWidth = (imgWidth / 4);
break;
// Quadrant 6
case 6:
startWidth = (endWidth + 1); endWidth = (imgWidth / 2);
break;
// Quadrant 7
case 7:
startHeight = (endHeight + 1); endHeight = (imgHeight / 2);
startWidth = 0; endWidth = (imgWidth / 4);
break;
// Quadrant 8
case 8:
startWidth = (endWidth + 1); endWidth = (imgWidth / 2);
break;
// Quadrant 9
case 9:
startHeight = 0; endHeight = (imgHeight / 4);
startWidth = (endWidth + 1); endWidth = ((imgWidth / 4) * 3);
break;
// Quadrant 10
case 10:
startWidth = (endWidth + 1); endWidth = imgWidth;
break;
// Quadrant 11
case 11:
startHeight = (imgHeight / 4); endHeight = (imgHeight / 2);
startWidth = (imgWidth / 2); endWidth = ((imgWidth / 4) * 3);
break;
// Quadrant 12
case 12:
startWidth = (endWidth + 1); endWidth = imgWidth;
break;
// Quadrant 13
case 13:
startHeight = (imgHeight / 2); endHeight = ((imgHeight / 4) * 3);
startWidth = 0; endWidth = (imgWidth / 4);
break;
// Quadrant 14
case 14:
startWidth = (endWidth + 1); endWidth = (imgWidth / 2);
break;
// Quadrant 15
case 15:
startHeight = (endHeight + 1); endHeight = imgHeight;
startWidth = 0; endWidth = (imgWidth / 4);
break;
// Quadrant 16
case 16:
startWidth = (endWidth + 1); endWidth = (imgWidth / 2);
break;
// Quadrant 17
case 17:
startHeight = (imgHeight / 2); endHeight = ((imgHeight / 4) * 3);
startWidth = (imgWidth / 2); endWidth = ((imgWidth / 4) * 3);
break;
// Quadrant 18
case 18:
startWidth = (endWidth + 1); endWidth = imgWidth;
break;
// Quadrant 19
case 19:
startHeight = (endHeight + 1); endHeight = imgHeight;
startWidth = (imgWidth / 2); endWidth = ((imgWidth / 4) * 3);
break;
// Quadrant 20
case 20:
startWidth = (endWidth + 1); endWidth = imgWidth;
break;
}
// Maintain the oldAvg Color variable
oldAvg = getBlockAverage(startHeight, endHeight, startWidth,
endWidth, img, blockImg, oldAvg);
// We subtract 1 from quadCount below to accomodate
// our Array indexing which must start at 0.
quadrantColors[quadCount - 1] = oldAvg;
// increment our quadrant counter by 1.
quadCount++;
}
}
Then from somewhere in your application I would initiate all this like so:
// We declare our array to handle 20 elements since
// the image will be dissected into 20 quadrants.
Color[] quadrantColors = new Color[20];
BufferedImage img = null;
// Fill our Color Array...
getQuadrantsColorAverages(quadrantColors, img);
// Let's see what we managed to get....
for (int i = 0; i < quadrantColors.length; i++) {
Color clr = quadrantColors[i];
int red = clr.getRed();
int green = clr.getGreen();
int blue = clr.getBlue();
System.out.println("The average color for Quadrant #" +
(i + 1) + " is: RGB[" + red + "," + green + "," + blue + "]");
}
Well...that's it QQCompi. I hope it sort of helps you out a little.
Related
I've been trying to implement a BC1 (DXT1) decompression algorithm in Java. Everything seems to work pretty precise but I've ran into problem with some blocks around transparent ones. I've been trying to resolve it for a few hours without success.
In short, after decompressing all blocks everything looks good except for the blocks whose are around transparent ones. During the development I've been checking results with results from DirectXTex (texconv) which is written in C++.
This is my result compared to DirectXTex one:
Here is the code I'm using:
BufferedImage decompress(byte[] buffer, int width, int height)
and implementation:
BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
int[] scanline = new int[4 * width]; //stores 4 horizontal lines (width/4 blocks)
RGBA[] blockPalette = new RGBA[4]; //stores RGBA values of current block
int bufferOffset = 0;
for (int row = 0; row < height / 4; row++) {
for (int col = 0; col < width / 4; col++) {
short rgb0 = Short.reverseBytes(Bytes.getShort(buffer, bufferOffset));
short rgb1 = Short.reverseBytes(Bytes.getShort(buffer, bufferOffset + 2));
int bitmap = Integer.reverseBytes(Bytes.getInt(buffer, bufferOffset + 4));
bufferOffset += 8;
blockPalette[0] = R5G6B5.decode(rgb0);
blockPalette[1] = R5G6B5.decode(rgb1);
if(rgb0 <= rgb1) {
int c2r = (blockPalette[0].getRed() + blockPalette[1].getRed()) / 2;
int c2g = (blockPalette[0].getGreen() + blockPalette[1].getGreen()) / 2;
int c2b = (blockPalette[0].getBlue() + blockPalette[1].getBlue()) / 2;
blockPalette[2] = new RGBA(c2r, c2g, c2b, 255);
blockPalette[3] = new RGBA(0, 0, 0, 0);
} else {
int c2r = (2 * blockPalette[0].getRed() + blockPalette[1].getRed()) / 3;
int c2g = (2 * blockPalette[0].getGreen() + blockPalette[1].getGreen()) / 3;
int c2b = (2 * blockPalette[0].getBlue() + blockPalette[1].getBlue()) / 3;
int c3r = (blockPalette[0].getRed() + 2 * blockPalette[1].getRed()) / 3;
int c3g = (blockPalette[0].getGreen() + 2 * blockPalette[1].getGreen()) / 3;
int c3b = (blockPalette[0].getBlue() + 2 * blockPalette[1].getBlue()) / 3;
blockPalette[2] = new RGBA(c2r, c2g, c2b, 255);
blockPalette[3] = new RGBA(c3r, c3g, c3b, 255);
}
for (int i = 0; i < 16; i++, bitmap >>= 2) {
int pi = (i / 4) * width + (col * 4 + i % 4);
int index = bitmap & 3;
scanline[pi] = A8R8G8B8.encode(blockPalette[index]);
}
}
//copy scanline to buffered image
result.setRGB(0, row * 4, width, 4, scanline, 0, width);
}
return result;
Does anyone have idea where is the problem? I've been doing exactly the same steps as specification says: Block Compression (Direct3D 10)
Is it that blockPalette[2].set(c2r, c2g, c2b); should be blockPalette[2].set(c2r, c2g, c2b, 255);? (in two locations)
For those who are interested, I've found that the problem was in comparing short values.
I've just changed:
if(rgb0 <= rgb1) {
to either
if(Short.compareUnsigned(rgb0, rgb1) <= 0) {
or
if((rgb0 & 0xffff) <= (rgb1 & 0xffff)) {
and this ensures that color values are compared as unsigned shorts (positive integers).
Here's my code that the error points to:
shipX = shipOne.getX();
EDIT2: Specifically it cannot find shipOne.
The Ship object, shipOne, is initialized inside a Switch statement not ten lines prior to the above code. I'm fairly certain that the error is appearing due to the object being initialized inside the Switch statement, and I don't know what to do about that. There are a total of eight Ship objects that are initialized via a loop and as such the above code needs to either be in a Switch statement or an If statement, both of which I'm sure would cause the same error. Is there an exception I can throw or something that would cause my program to run? The Ship objects do get initialized 100% of the time so there's no worry there.
EDIT: I was asked for the rest of the code so here it be:
(This isn't the entirety of it, as you can't see the return statement but the remainder isn't relevant to my problem).
public static String[][] createFleet()
{
//Initialize Variables
//Ship Variables
boolean isHoriz = true; //Is true if ship is Horizontal, false if Vertical
char ID;
int posX = 0; //Origin of the ship (x coordinate [row])
int posY = 0; //Origin of the ship (y coordinate [column])
int size = 0; //Either 2, 3, or 4
//Associated Variables
Random randomGen = new Random(); //Random number generator
String[][] board = new String[10][10]; //Gameboard, populated with ships
int orientationRand = 0; //If 0, Ship Horizontal. If 1, Ship Vertical
int posRand = 0; //Assigned to posX or posY
int sizeRand = 0; //If 0 <= x < 10, size = 2
//If 10 <= x < 40, size = 3
//If 40 <= x < 100, size = 4
//Create Ships
for (int i = 0; i < 8; i++)
{
//Size
sizeRand = randomGen.nextInt(100);
if (sizeRand < 10)
{
size = 2;
}
else if (sizeRand < 40)
{
size = 3;
}
else size = 4;
//Orientation & Position
orientationRand = randomGen.nextInt(2);
if (orientationRand == 0)
{
//Orientation
isHoriz = true;
//Position (horizontal)
//x position
switch (size)
{
case 2:
posRand = randomGen.nextInt(9);
posX = posRand;
break;
case 3:
posRand = randomGen.nextInt(8);
posX = posRand;
break;
case 4:
posRand = randomGen.nextInt(7);
posX = posRand;
break;
}
//y position
posRand = randomGen.nextInt(10);
posY = posRand;
}
else
{
//Orientation
isHoriz = false;
//Position (vertical)
//x position
posRand = randomGen.nextInt(10);
posX = posRand;
//y position
switch (size)
{
case 2:
posRand = randomGen.nextInt(9);
posY = posRand;
break;
case 3:
posRand = randomGen.nextInt(8);
posY = posRand;
break;
case 4:
posRand = randomGen.nextInt(7);
posY = posRand;
break;
}
}
//Assign Size, Orientation, and Position to each ship
switch (i)
{
case 0:
ID = 'A';
Ship shipOne = new Ship(posX, posY, size, isHoriz, ID);
break;
case 1:
ID = 'B';
Ship shipTwo = new Ship(posX, posY, size, isHoriz, ID);
break;
case 2:
ID = 'C';
Ship shipThree = new Ship(posX, posY, size, isHoriz, ID);
break;
case 3:
ID = 'D';
Ship shipFour = new Ship(posX, posY, size, isHoriz, ID);
break;
case 4:
ID = 'E';
Ship shipFive = new Ship(posX, posY, size, isHoriz, ID);
break;
case 5:
ID = 'F';
Ship shipSix = new Ship(posX, posY, size, isHoriz, ID);
break;
case 6:
ID = 'G';
Ship shipSeven = new Ship(posX, posY, size, isHoriz, ID);
break;
case 7:
ID = 'H';
Ship shipEight = new Ship(posX, posY, size, isHoriz, ID);
break;
}
}
In your code, 'ID' variable is not getting declared anywhere. You must declare variable before using it.
Initialize the objects to null before the switch or if statements. I think this should solve your problem.
It will be possible to provide a better solution if the entire code was posted.
I am rendering terrain using a quad-tree system. I need to split a height-map into four sections using the method splitHeightmap(float[] originalMap, int quadrant) with the quadrant being a number from 0-3. The map needs to be split into quarters so if 0 is passed as the quadrant, the bottom left quarter of the array is returned as a new float array. I have a little base code, but I'm not sure how to actually sample the map depending on the desired quadrant:
protected float[] splitHeightMap(float[] heightMap, TerrainQuadrant quadrant) {
float[] newHeightMap = new float[size >> 1];
int newSize = size >> 1;
for (int i = 0; i < newSize; i++)
for (int j = 0; j < newSize; j++)
newHeightMap[i * newSize + j] = sampleHeightAt(heightMap, i, j);
return newHeightMap;
}
protected float sampleHeightAt(float[] heightMap, int x, int z) {
return heightMap[z + x * size];
}
Edit: I have written what I think should work, but am getting a ArrayIndexOutOfBoundsException for index 65792 (with a original heightmap of 512x512):
protected float[] splitHeightMap(float[] heightMap, TerrainQuadrant quadrant) {
float[] newHeightMap = new float[(size >> 1) * (size >> 1)];
int newSize = size >> 1;
int xOffset = 0, zOffset = 0;
int xCount = 0, yCount = 0;
switch (quadrant) {
case BottomRight:
xOffset = newSize;
break;
case TopLeft:
zOffset = newSize;
break;
case TopRight:
xOffset = newSize;
zOffset = newSize;
break;
default:
break;
}
for (int x = xOffset; x < xOffset + newSize; x++)
for (int z = zOffset; z < zOffset + newSize; z++) {
newHeightMap[xCount + yCount * newSize] = heightMap[z + x * size]; // should this be 'z + x * size' or 'x + z * size'?
xCount++;
yCount++;
}
return newHeightMap;
}
If I understand you correctly, then you have to use two-dimensional array. With such type of array it's easy to fetch any area of the array.
Your heightMap now gona be float[][] type, so you must correct your code, where you fill this array.
I have implemented one examlpe for you and used 4x4 array:
| 2 2 3 3 |
| 2 2 3 3 |
| 0 0 1 1 |
| 0 0 1 1 |
As I understand you want to fetch areas like all '0', all '1', all '2' and all '3'.
public static void main ( String[] args )
{
//setting up initial array 'heightMap' (you can name it like you want)
float[][] f = { { 2, 2, 3, 3 }, { 2, 2, 3, 3 }, { 0, 0, 1, 1 }, { 0, 0, 1, 1 } };
float[][] f2 = splitHeightMap ( f, TerrainQuadrant.BotttomRight );
for ( float[] floats : f2 )
{
System.out.println ( Arrays.toString ( floats ) );
}
}
protected static float[][] splitHeightMap ( float[][] heightMap, TerrainQuadrant quadrant )
{
//this gives you half of the 'heightMap' length
int newSize = heightMap.length >> 1;
float[][] newHeightMap = new float[ newSize ][ newSize ];
//its your offsets, indicating from what place to start iteration
int xOffset = 0;
int yOffset = newSize;
//its max values to reach while iterating
int xRestriction = newSize;
int yRestriction = heightMap.length;
//setting up params according to 'quadrant'
switch ( quadrant )
{
case BottomRight:
xOffset = newSize;
yOffset = newSize;
xRestriction = heightMap.length;
break;
case TopLeft:
yOffset = 0;
yRestriction = newSize;
break;
case TopRight:
yOffset = 0;
xOffset = newSize;
xRestriction = heightMap.length;
yRestriction = newSize;
break;
default:
break;
}
//counters not to reach new array bounds
int xCount = 0, yCount = 0;
for ( int y = yOffset; y < yRestriction; y++ )
{
//taking row at 'y' position
float[] row = heightMap[ y ];
for ( int x = xOffset; x < xRestriction; x++ )
{
//taking value from 'y' row at 'x' position.
float value = row[ x ];
//set fetched value to new map.
newHeightMap[ yCount ][ xCount ] = value;
//increase x position, but do not touch row
xCount++;
}
//new row - new 'x' position
xCount = 0;
yCount++;
}
return newHeightMap;
}
This implementation shows you :
| 1 1 |
| 1 1 |
To change it - change main method.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
I'm trying to get a random HSV colour then convert it to RGB to be used. Does anyone have any ideas as to how I can do this? Thanks, Sam. What I've got so far: First method converts HSV to RGB with the specified values, the second method is for getting a random colour.
public static float[] HSVtoRGB(float h, float s, float v) {
float m, n, f;
int i;
float[] hsv = new float[3];
float[] rgb = new float[3];
hsv[0] = h;
hsv[1] = s;
hsv[2] = v;
if (hsv[0] == -1) {
rgb[0] = rgb[1] = rgb[2] = hsv[2];
return rgb;
}
i = (int) (Math.floor(hsv[0]));
f = hsv[0] - i;
if (i % 2 == 0) {
f = 1 - f; // if i is even
}
m = hsv[2] * (1 - hsv[1]);
n = hsv[2] * (1 - hsv[1] * f);
switch (i) {
case 6:
case 0:
rgb[0] = hsv[2];
rgb[1] = n;
rgb[2] = m;
break;
case 1:
rgb[0] = n;
rgb[1] = hsv[2];
rgb[2] = m;
break;
case 2:
rgb[0] = m;
rgb[1] = hsv[2];
rgb[2] = n;
break;
case 3:
rgb[0] = m;
rgb[1] = n;
rgb[2] = hsv[2];
break;
case 4:
rgb[0] = n;
rgb[1] = m;
rgb[2] = hsv[2];
break;
case 5:
rgb[0] = hsv[2];
rgb[1] = m;
rgb[2] = n;
break;
}
return rgb;
}
public static int randomColor() {
int hue = (int) (Math.random() * 6.0f);
int saturation = (int) (Math.random());
int brightness = (int) (Math.random());
float[] rgb = HSVtoRGB(hue, saturation, brightness);
int red = (int) (rgb[0] * 255.0f);
int green = (int) (rgb[1] * 255.0f);
int blue = (int) (rgb[2] * 255.0f);
return (red << 16) | (green << 8) | blue;
}
You can use java.awt.Color.RGBtoHSB(...) You can find the relevant documentation for it here: http://docs.oracle.com/javase/1.4.2/docs/api/java/awt/Color.html
Then it just becomes trivial of generating a random color.
int red = (int) (Math.random() * 256)
int green = (int) (Math.random() * 256)
int blue = (int) (Math.random() * 256)
Then convert directly. Note that there is also a HSBtoRGB(...) function in the same class.
Some time ago I asked this question about how to make a 2D terrain with opengl vertices. I got a good answer, but when trying it out it didn't draw anything, and I can't figure out what's wrong, or how to fix it.
I have this now :
public class Terrain extends Actor {
Mesh mesh;
private final int LENGTH = 1500; //length of the whole terrain
public Terrain(int res) {
Random r = new Random();
//res (resolution) is the number of height-points
//minimum is 2, which will result in a box (under each height-point there is another vertex)
if (res < 2)
res = 2;
mesh = new Mesh(VertexDataType.VertexArray, true, 2 * res, 50, new VertexAttribute(Usage.Position, 2, "a_position"));
float x = 0f; //current position to put vertices
float med = 100f; //starting y
float y = med;
float slopeWidth = (float) (LENGTH / ((float) (res - 1))); //horizontal distance between 2 heightpoints
// VERTICES
float[] tempVer = new float[2*2*res]; //hold vertices before setting them to the mesh
int offset = 0; //offset to put it in tempVer
for (int i = 0; i<res; i++) {
tempVer[offset+0] = x; tempVer[offset+1] = 0f; // below height
tempVer[offset+2] = x; tempVer[offset+3] = y; // height
//next position:
x += slopeWidth;
y += (r.nextFloat() - 0.5f) * 50;
offset +=4;
}
mesh.setVertices(tempVer);
// INDICES
short[] tempIn = new short[(res-1)*6];
offset = 0;
for (int i = 0; i<res; i+=2) {
tempIn[offset + 0] = (short) (i); // below height
tempIn[offset + 1] = (short) (i + 2); // below next height
tempIn[offset + 2] = (short) (i + 1); // height
tempIn[offset + 3] = (short) (i + 1); // height
tempIn[offset + 4] = (short) (i + 2); // below next height
tempIn[offset + 5] = (short) (i + 3); // next height
offset+=6;
}
}
#Override
public void draw(SpriteBatch batch, float delta) {
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
mesh.render(GL10.GL_TRIANGLES);
}
This is being rendered by Libgdx, which also provides the class Mesh, but this is not really relevant since I believe that works fine. My problems lay by the vertex and indices generation. I don't really know how to debug it either, so could anyone please look at it, and help me to find why nothing is being rendered?
After a full day has passed, and I have tried everything to solve it, it seemed I forgot to actually set the indices to the mesh.
mesh.setIndices(tempIn);
One missing line, hours of pain... i'm an idiot :)