/**
*
*/
package model;
import gr.zdimensions.jsquish.Squish;
import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.nio.ByteBuffer;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import jogl.DDSImage;
import ddsutil.ByteBufferedImage;
import ddsutil.ImageRescaler;
import ddsutil.MipMapsUtil;
import ddsutil.NonCubicDimensionException;
import ddsutil.Rescaler;
/**
* MipMap Texture contains several layers of MipMaps, each is 1/4 the size of the one above.
* @author Daniel Senff
*
*/
public class MipMaps extends AbstractTextureMap implements Iterable<BufferedImage> {
/**
* Topmost MipMap Index
*/
public static final int TOP_MOST_MIP_MAP = 0;
List<BufferedImage> mipmaps;
/**
* {@link Rescaler} providing the scaling algorithm.
*/
protected Rescaler rescaler;
private int numMipMaps;
/**
* @param topmost
*
*/
public MipMaps() {
this(0);
}
public MipMaps(final int numMipMaps) {
this.numMipMaps = numMipMaps;
this.rescaler = new ImageRescaler();
this.mipmaps = new Vector<BufferedImage>(numMipMaps);
}
/**
* Populate this MipMap-Object based on the given topmost Map.
* @param topmost
*/
public void generateMipMaps(BufferedImage topmost) {
addMipMap(topmost);
System.out.println("Generate Mipmaps");
if(!DDSFile.isPowerOfTwo(topmost.getWidth())
&& !DDSFile.isPowerOfTwo(topmost.getHeight()))
throw new NonCubicDimensionException();
generateMipMapArray();
}
private void generateMipMapArray() {
BufferedImage topmost = getMipMaps().get(0);
// dimensions of first map
int mipmapWidth = topmost.getWidth();
int mipmapHeight = topmost.getHeight();
this.numMipMaps = MipMapsUtil.calculateMaxNumberOfMipMaps(mipmapWidth, mipmapHeight);
BufferedImage previousMap = topmost;
BufferedImage mipMapBi;
for (int i = 1; i < this.numMipMaps; i++) {
// calculation for next map
mipmapWidth = MipMaps.calculateMipMapSize(mipmapWidth);
mipmapHeight = MipMaps.calculateMipMapSize(mipmapHeight);
mipMapBi = rescaler.rescaleBI(previousMap, mipmapWidth, mipmapHeight);
addMipMap(mipMapBi);
// by using this map in the next MipMap generation step, we increase
// performance, since we don't always scale from the biggest image.
// however this might also increase errors over generations
previousMap = mipMapBi;
}
}
/**
* Returns the highest MipMap in the original resolution.
* @return
*/
public BufferedImage getTopMostMipMap() {
return getMipMap(TOP_MOST_MIP_MAP);
}
/**
* @return
*/
public int getNumMipMaps() {
return this.numMipMaps;
}
@Override
public int getHeight() {
return getMipMap(TOP_MOST_MIP_MAP).getHeight();
}
@Override
public int getWidth() {
return getMipMap(TOP_MOST_MIP_MAP).getWidth();
}
/**
* Returns a Map of the given level.
* @param index
* @return
*/
public BufferedImage getMipMap(final int index) {
return getMipMaps().get(index);
}
/**
* Set the given {@link BufferedImage} as MipMap in the index.
* @param mipmapIndex
* @param image
*/
public void setMipMap(int mipmapIndex, BufferedImage image) {
if(getMipMaps().size() == mipmapIndex)
addMipMap(mipmapIndex, image);
else
getMipMaps().set(mipmapIndex, image);
}
private List<BufferedImage> getMipMaps() {
return this.mipmaps;
}
/**
* @param image
*/
public void addMipMap(final BufferedImage image) {
getMipMaps().add(image);
}
private void addMipMap(final int mipmapIndex, final BufferedImage image) {
getMipMaps().add(mipmapIndex, image);
}
/**
* All contained MipMaps compressed with DXT in {@link ByteBuffer}
* Squishes each mipmap and store in a {@link DDSImage} compatible {@link ByteBuffer}-Array.
* @param compressionType
* @return
*/
@Override
public ByteBuffer[] getDXTCompressedBuffer(final Squish.CompressionType compressionType) {
ByteBuffer[] mipmapBuffer = new ByteBuffer[this.numMipMaps];
for (int j = 0; j < this.numMipMaps; j++) {
System.out.println("compress mipmap " + j);
mipmapBuffer[j] = compress(getMipMap(j), compressionType);
}
return mipmapBuffer;
}
/**
* Returns a Vector with all MipMaps
* @return
*/
public List<BufferedImage> getAllMipMaps() {
return getMipMaps();
}
/**
* Returns an Array of {@link BufferedImage}s of MipMaps.
* @return
*/
public BufferedImage[] getAllMipMapsArray() {
return (BufferedImage[]) getMipMaps().toArray();
}
/* (non-Javadoc)
* @see DDSUtil.AbstractTextureMap#getUncompressedBuffer()
*/
public ByteBuffer[] getUncompressedBuffer() {
ByteBuffer[] mipmapBuffer = new ByteBuffer[numMipMaps];
for (int i = 0; i < numMipMaps; i++) {
mipmapBuffer[i] = ByteBuffer.wrap(ByteBufferedImage.convertBIintoARGBArray(getMipMap(i)));
}
return mipmapBuffer;
}
/**
* @param topmost
* @param mipmapWidth
* @param mipmapHeight
* @param mipmapBI
* @return
*/
/*public static BufferedImage[] generateMipMaps(final BufferedImage topmost,
int mipmapWidth,
int mipmapHeight,
final BufferedImage[] mipmapBI) {
int i = 0; // cause the first already is set
ImageRescaler rescaler = new ImageRescaler();
while(true) {
mipmapBI[i] = rescaler.rescaleBI(mipmapBI[i], mipmapWidth, mipmapHeight);
if (mipmapWidth == 1 || mipmapHeight == 1)
break;
i++;
mipmapWidth = calculateMipMapSize(mipmapWidth);
mipmapHeight = calculateMipMapSize(mipmapHeight);
}
return mipmapBI;
}*/
/* (non-Javadoc)
* @see java.lang.Iterable#iterator()
*/
@Override
public Iterator<BufferedImage> iterator() {
return new Iterator<BufferedImage>() {
int count=0;
@Override
public boolean hasNext() {
boolean b = count++ < mipmaps.size()-1;
return b;
}
@Override
public BufferedImage next() {
return mipmaps.get(count);
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
/**
* Get the {@link Rescaler}.
* @return
*/
public Rescaler getRescaler() {
return this.rescaler;
}
/**
* Set the {@link Rescaler}.
* @param rescaler
*/
public void setRescaler(Rescaler rescaler) {
this.rescaler = rescaler;
}
/**
* returns the new size for the next iteration of a generated MipMap
* Usually half the current value, unless current value is 1
* @param currentValue
* @return
*/
public static int calculateMipMapSize(final int currentValue) {
return (currentValue > 1) ? currentValue/2 : 1;
}
/**
* Returns the size of the MipMap at the requested index based on the original value
* @param targetIndex
* @param original size at index 0
* @return
*/
public static int getMipMapSizeAtIndex(final int targetIndex, final int original) {
int newValue = original;
for (int i = 0; i < targetIndex; i++) {
newValue = MipMaps.calculateMipMapSize(newValue);
}
return newValue;
}
/**
* Width of the MipMap on the specified index.
* @param index
* @return
*/
public int getMipMapWidth(int index) {
return getMipMap(index).getWidth();
}
/**
* Width of the MipMap on the specified index.
* @param index
* @return
*/
public int getMipMapHeight(int index) {
return getMipMap(index).getHeight();
}
/**
* Returns the {@link Dimension} of the MipMap at the specified index.
* @param index
* @return
*/
public Dimension getMipMapDimension(final int index) {
return new Dimension(getMipMapWidth(index), getMipMapHeight(index));
}
}