/*******************************************************************************
* Breakout Cave Survey Visualizer
*
* Copyright (C) 2014 James Edwards
*
* jedwards8 at fastmail dot fm
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*******************************************************************************/
package org.andork.jogl.awt;
import java.awt.Point;
import java.awt.Transparency;
import java.awt.color.ColorSpace;
import java.awt.image.BandedSampleModel;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DirectColorModel;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
import java.nio.ByteBuffer;
import java.util.Hashtable;
/**
* {@link DataBuffer} specialization using NIO direct buffer of type
* {@link DataBuffer#TYPE_INT} as storage.
*/
public final class DirectDataBufferByte extends DataBuffer {
public static class BufferedImageByte extends BufferedImage {
final int customImageType;
public BufferedImageByte(int customImageType, ColorModel cm, WritableRaster raster,
Hashtable<?, ?> properties) {
super(cm, raster, false /* isRasterPremultiplied */, properties);
this.customImageType = customImageType;
}
/**
* @return one of the custom image-type values
* {@link BufferedImage#TYPE_INT_ARGB TYPE_INT_ARGB},
* {@link BufferedImage#TYPE_INT_ARGB_PRE TYPE_INT_ARGB_PRE},
* {@link BufferedImage#TYPE_INT_RGB TYPE_INT_RGB} or
* {@link BufferedImage#TYPE_INT_BGR TYPE_INT_BGR}.
*/
public int getCustomType() {
return customImageType;
}
@Override
public String toString() {
return new String("BufferedImageByte@" + Integer.toHexString(hashCode())
+ ": custum/internal type = " + customImageType + "/" + getType()
+ " " + getColorModel() + " " + getRaster());
}
}
public static class DirectWritableRaster extends WritableRaster {
protected DirectWritableRaster(SampleModel sampleModel, DirectDataBufferByte dataBuffer, Point origin) {
super(sampleModel, dataBuffer, origin);
}
}
/**
* Creates a {@link BufferedImageByte} using a {@link DirectColorModel
* direct color model} in {@link ColorSpace#CS_sRGB sRGB color space}.<br>
* It uses a {@link DirectWritableRaster} utilizing
* {@link DirectDataBufferByte} storage.
* <p>
* Note that due to using the custom storage type
* {@link DirectDataBufferByte}, the resulting {@link BufferedImage}'s
* {@link BufferedImage#getType() image-type} is of
* {@link BufferedImage#TYPE_CUSTOM TYPE_CUSTOM}. We are not able to change
* this detail, since the AWT image implementation associates the
* {@link BufferedImage#getType() image-type} with a build-in storage-type.
* Use {@link BufferedImageByte#getCustomType()} to retrieve the custom
* image-type, which will return the <code>imageType</code> value passed
* here.
* </p>
*
* @param width
* @param height
* @param imageType
* one of {@link BufferedImage#TYPE_INT_ARGB TYPE_INT_ARGB},
* {@link BufferedImage#TYPE_INT_ARGB_PRE TYPE_INT_ARGB_PRE},
* {@link BufferedImage#TYPE_INT_RGB TYPE_INT_RGB} or
* {@link BufferedImage#TYPE_INT_BGR TYPE_INT_BGR}.
* @param location
* origin, if <code>null</code> 0/0 is assumed.
* @param properties
* <code>Hashtable</code> of <code>String</code>/
* <code>Object</code> pairs. Used for
* {@link BufferedImage#getProperty(String)} etc.
* @return
*/
public static BufferedImageByte createBufferedImage(int width, int height, int imageType, Point location,
Hashtable<?, ?> properties) {
final int[] bandOffsets = new int[imageType];
for (int i = 0; i < imageType; i++) {
bandOffsets[i] = i;
}
ColorModel colorModel;
SampleModel sampleModel;
switch (imageType) {
case BufferedImage.TYPE_BYTE_GRAY:
ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
int[] nBits = new int[] { 8 };
colorModel = new ComponentColorModel(cs, nBits, false, true,
Transparency.OPAQUE,
DataBuffer.TYPE_BYTE);
sampleModel = new BandedSampleModel(TYPE_BYTE, width, height, 1);
break;
default:
throw new IllegalArgumentException("Unsupported imageType, must be [BYTE_GRAY], has " + imageType);
}
final DirectDataBufferByte dataBuffer = new DirectDataBufferByte(width * height);
if (null == location) {
location = new Point(0, 0);
}
final WritableRaster raster = new DirectWritableRaster(sampleModel, dataBuffer, location);
return new BufferedImageByte(imageType, colorModel, raster, properties);
}
/** Default data bank. */
private ByteBuffer data;
/** All data banks */
private ByteBuffer bankdata[];
/**
* Constructs an nio integer-based {@link DataBuffer} with a single bank
* using the specified array.
* <p>
* Only the first <code>size</code> elements should be used by accessors of
* this {@link DataBuffer}. <code>dataArray</code> must be large enough to
* hold <code>size</code> elements.
* </p>
*
* @param dataArray
* The integer array for the {@link DataBuffer}.
* @param size
* The size of the {@link DataBuffer} bank.
*/
public DirectDataBufferByte(ByteBuffer dataArray, int size) {
super(TYPE_BYTE, size);
data = dataArray;
bankdata = new ByteBuffer[1];
bankdata[0] = data;
}
/**
* Constructs an nio integer-based {@link DataBuffer} with a single bank and
* the specified size.
*
* @param size
* The size of the {@link DataBuffer}.
*/
public DirectDataBufferByte(int size) {
super(TYPE_BYTE, size);
data = ByteBuffer.allocateDirect(size);
bankdata = new ByteBuffer[1];
bankdata[0] = data;
}
/**
* Constructs an nio integer-based {@link DataBuffer} with the specified
* number of banks, all of which are the specified size.
*
* @param size
* The size of the banks in the {@link DataBuffer}.
* @param numBanks
* The number of banks in the a{@link DataBuffer}.
*/
public DirectDataBufferByte(int size, int numBanks) {
super(TYPE_BYTE, size, numBanks);
bankdata = new ByteBuffer[numBanks];
for (int i = 0; i < numBanks; i++) {
bankdata[i] = ByteBuffer.allocateDirect(size);
}
data = bankdata[0];
}
/**
* Returns the default (first) int data array in {@link DataBuffer}.
*
* @return The first integer data array.
*/
public ByteBuffer getData() {
return data;
}
/**
* Returns the data array for the specified bank.
*
* @param bank
* The bank whose data array you want to get.
* @return The data array for the specified bank.
*/
public ByteBuffer getData(int bank) {
return bankdata[bank];
}
/**
* Returns the requested data array element from the first (default) bank.
*
* @param i
* The data array element you want to get.
* @return The requested data array element as an integer.
* @see #setElem(int, int)
* @see #setElem(int, int, int)
*/
@Override
public int getElem(int i) {
return data.get(i + offset);
}
/**
* Returns the requested data array element from the specified bank.
*
* @param bank
* The bank from which you want to get a data array element.
* @param i
* The data array element you want to get.
* @return The requested data array element as an integer.
* @see #setElem(int, int)
* @see #setElem(int, int, int)
*/
@Override
public int getElem(int bank, int i) {
return bankdata[bank].get(i + offsets[bank]);
}
/**
* Sets the requested data array element in the first (default) bank to the
* specified value.
*
* @param i
* The data array element you want to set.
* @param val
* The integer value to which you want to set the data array
* element.
* @see #getElem(int)
* @see #getElem(int, int)
*/
@Override
public void setElem(int i, int val) {
data.put(i + offset, (byte) (val & 0xff));
}
/**
* Sets the requested data array element in the specified bank to the
* integer value <code>i</code>.
*
* @param bank
* The bank in which you want to set the data array element.
* @param i
* The data array element you want to set.
* @param val
* The integer value to which you want to set the specified data
* array element.
* @see #getElem(int)
* @see #getElem(int, int)
*/
@Override
public void setElem(int bank, int i, int val) {
bankdata[bank].put(i + offsets[bank], (byte) (val & 0xff));
}
}