* Added new ImageRaster thing, it can let you read and write pixels on jME3 images without caring about the underlying format. NOTE: None of the jME3 internal classes use it yet, the code has yet to be ported.
git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9655 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
This commit is contained in:
parent
69cbad9ce9
commit
f5f3a85042
@ -0,0 +1,73 @@
|
||||
package com.jme3.texture.image;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
class BitMaskImageCodec extends ImageCodec {
|
||||
|
||||
// Shifts
|
||||
final int as, rs, gs, bs;
|
||||
boolean be = false;
|
||||
|
||||
public BitMaskImageCodec(int bpp, int flags, int ac, int rc, int gc, int bc, int as, int rs, int gs, int bs) {
|
||||
super(bpp, flags,
|
||||
(int) (((long) 1 << ac) - 1),
|
||||
(int) (((long) 1 << rc) - 1),
|
||||
(int) (((long) 1 << gc) - 1),
|
||||
(int) (((long) 1 << bc) - 1));
|
||||
|
||||
if (bpp > 4) {
|
||||
throw new UnsupportedOperationException("Use ByteAlignedImageCodec for codecs with pixel sizes larger than 4 bytes");
|
||||
}
|
||||
|
||||
this.as = as;
|
||||
this.rs = rs;
|
||||
this.gs = gs;
|
||||
this.bs = bs;
|
||||
}
|
||||
|
||||
private static int readPixelRaw(ByteBuffer buf, int idx, int bpp) {
|
||||
idx += bpp;
|
||||
int original = buf.get(--idx) & 0xff;
|
||||
while ((--bpp) > 0) {
|
||||
original = (original << 8) | (buf.get(--idx) & 0xff);
|
||||
}
|
||||
return original;
|
||||
}
|
||||
|
||||
private void writePixelRaw(ByteBuffer buf, int idx, int pixel, int bpp){
|
||||
// buf.position(idx);
|
||||
// if (!be){
|
||||
while ((--bpp) >= 0){
|
||||
byte bt = (byte) ((pixel >> (bpp * 8)) & 0xff);
|
||||
buf.put(idx + bpp, bt);
|
||||
}
|
||||
// } else {
|
||||
// for (int i = bpp - 1; i >= 0; i--) {
|
||||
// byte bt = (byte) ((pixel >> (i * 8)) & 0xff);
|
||||
// buf.put(idx + i, bt);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readComponents(ByteBuffer buf, int x, int y, int width, int[] components, byte[] tmp) {
|
||||
int inputPixel = readPixelRaw(buf, (x + y * width) * bpp, bpp);
|
||||
components[0] = (inputPixel >> as) & maxAlpha;
|
||||
components[1] = (inputPixel >> rs) & maxRed;
|
||||
components[2] = (inputPixel >> gs) & maxGreen;
|
||||
components[3] = (inputPixel >> bs) & maxBlue;
|
||||
}
|
||||
|
||||
public void writeComponents(ByteBuffer buf, int x, int y, int width, int[] components, byte[] tmp) {
|
||||
// Shift components then mask them
|
||||
// Map all components into a single bitspace
|
||||
int outputPixel = ((components[0] & maxAlpha) << as)
|
||||
| ((components[1] & maxRed) << rs)
|
||||
| ((components[2] & maxGreen) << gs)
|
||||
| ((components[3] & maxBlue) << bs);
|
||||
|
||||
// Find index in image where to write pixel.
|
||||
// Write the resultant bitspace into the pixel.
|
||||
writePixelRaw(buf, (x + y * width) * bpp, outputPixel, bpp);
|
||||
}
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
package com.jme3.texture.image;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
class ByteAlignedImageCodec extends ImageCodec {
|
||||
|
||||
private final int ap, az, rp, rz, gp, gz, bp, bz;
|
||||
boolean be;
|
||||
|
||||
public ByteAlignedImageCodec(int bpp, int flags, int az, int rz, int gz, int bz, int ap, int rp, int gp, int bp) {
|
||||
// Cast to long to compute max vals, since some components could be as high as 32 bits.
|
||||
super(bpp, flags,
|
||||
(int)(((long)1 << (az << 3)) - 1),
|
||||
(int)(((long)1 << (rz << 3)) - 1),
|
||||
(int)(((long)1 << (gz << 3)) - 1),
|
||||
(int)(((long)1 << (bz << 3)) - 1));
|
||||
|
||||
this.ap = ap;
|
||||
this.az = az;
|
||||
this.rp = rp;
|
||||
this.rz = rz;
|
||||
|
||||
this.gp = gp;
|
||||
this.gz = gz;
|
||||
this.bp = bp;
|
||||
this.bz = bz;
|
||||
}
|
||||
|
||||
private static void readPixelRaw(ByteBuffer buf, int idx, int bpp, byte[] result) {
|
||||
buf.position(idx);
|
||||
buf.get(result, 0, bpp);
|
||||
}
|
||||
|
||||
private static void writePixelRaw(ByteBuffer buf, int idx, byte[] pixel, int bpp) {
|
||||
// try {
|
||||
buf.position(idx);
|
||||
buf.put(pixel, 0, bpp);
|
||||
// } catch (IndexOutOfBoundsException ex) {
|
||||
// System.out.println("!");
|
||||
// }
|
||||
}
|
||||
|
||||
private static int readComponent(byte[] encoded, int position, int size) {
|
||||
// int component = encoded[position] & 0xff;
|
||||
// while ((--size) > 0){
|
||||
// component = (component << 8) | (encoded[++position] & 0xff);
|
||||
// }
|
||||
// return component;
|
||||
try {
|
||||
int component = 0;
|
||||
for (int i = size - 1; i >= 0; i--) {
|
||||
component = (component << 8) | (encoded[position + i] & 0xff);
|
||||
}
|
||||
return component;
|
||||
// position += size - 1;
|
||||
//
|
||||
// while ((--size) >= 0) {
|
||||
// component = (component << 8) | (encoded[position--] & 0xff);
|
||||
// }
|
||||
// return component;
|
||||
} catch (ArrayIndexOutOfBoundsException ex){
|
||||
ex.printStackTrace();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void writeComponent(int component, int position, int size, byte[] result) {
|
||||
// if (!be) {
|
||||
// while ((--size) >= 0){
|
||||
// byte bt = (byte) ((component >> (size * 8)) & 0xff);
|
||||
// result[position++] = bt;
|
||||
// }
|
||||
// } else {
|
||||
for (int i = 0; i < size; i++) {
|
||||
byte bt = (byte) ((component >> (i * 8)) & 0xff);
|
||||
result[position++] = bt;
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
public void readComponents(ByteBuffer buf, int x, int y, int width, int[] components, byte[] tmp) {
|
||||
readPixelRaw(buf, (x + y * width) * bpp, bpp, tmp);
|
||||
components[0] = readComponent(tmp, ap, az);
|
||||
components[1] = readComponent(tmp, rp, rz);
|
||||
components[2] = readComponent(tmp, gp, gz);
|
||||
components[3] = readComponent(tmp, bp, bz);
|
||||
}
|
||||
|
||||
public void writeComponents(ByteBuffer buf, int x, int y, int width, int[] components, byte[] tmp) {
|
||||
writeComponent(components[0], ap, az, tmp);
|
||||
writeComponent(components[1], rp, rz, tmp);
|
||||
writeComponent(components[2], gp, gz, tmp);
|
||||
writeComponent(components[3], bp, bz, tmp);
|
||||
writePixelRaw(buf, (x + y * width) * bpp, tmp, bpp);
|
||||
}
|
||||
}
|
140
engine/src/core/com/jme3/texture/image/ImageCodec.java
Normal file
140
engine/src/core/com/jme3/texture/image/ImageCodec.java
Normal file
@ -0,0 +1,140 @@
|
||||
package com.jme3.texture.image;
|
||||
|
||||
import com.jme3.texture.Image;
|
||||
import com.jme3.texture.Image.Format;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.EnumMap;
|
||||
|
||||
abstract class ImageCodec {
|
||||
|
||||
public static final int FLAG_F16 = 1, FLAG_F32 = 2, FLAG_GRAY = 4, FLAG_ALPHAONLY = 8, FLAG_SHAREDEXP = 16;
|
||||
private static final EnumMap<Image.Format, ImageCodec> params = new EnumMap<Image.Format, ImageCodec>(Image.Format.class);
|
||||
|
||||
protected final int bpp, flags, maxAlpha, maxRed, maxGreen, maxBlue;
|
||||
|
||||
public ImageCodec(int bpp, int flags, int maxAlpha, int maxRed, int maxGreen, int maxBlue) {
|
||||
this.bpp = bpp;
|
||||
this.flags = flags;
|
||||
this.maxAlpha = maxAlpha;
|
||||
this.maxRed = maxRed;
|
||||
this.maxGreen = maxGreen;
|
||||
this.maxBlue = maxBlue;
|
||||
}
|
||||
|
||||
static {
|
||||
// == ALPHA ==
|
||||
params.put(Format.Alpha8, new BitMaskImageCodec(1, 0, 8, 0, 0, 0,
|
||||
0, 0, 0, 0));
|
||||
|
||||
params.put(Format.Alpha16, new BitMaskImageCodec(2, 0, 16, 0, 0, 0,
|
||||
0, 0, 0, 0));
|
||||
|
||||
// == LUMINANCE ==
|
||||
params.put(Format.Luminance8, new BitMaskImageCodec(1, FLAG_GRAY, 0, 8, 0, 0,
|
||||
0, 0, 0, 0));
|
||||
params.put(Format.Luminance16, new BitMaskImageCodec(2, FLAG_GRAY, 0, 16, 0, 0,
|
||||
0, 0, 0, 0));
|
||||
params.put(Format.Luminance16F, new BitMaskImageCodec(2, FLAG_GRAY | FLAG_F16, 0, 16, 0, 0,
|
||||
0, 0, 0, 0));
|
||||
params.put(Format.Luminance32F, new BitMaskImageCodec(4, FLAG_GRAY | FLAG_F32, 0, 32, 0, 0,
|
||||
0, 0, 0, 0));
|
||||
|
||||
// == INTENSITY ==
|
||||
// ??
|
||||
|
||||
// == LUMINANCA ALPHA ==
|
||||
params.put(Format.Luminance8Alpha8, new BitMaskImageCodec(2, FLAG_GRAY,
|
||||
8, 8, 0, 0,
|
||||
8, 0, 0, 0));
|
||||
|
||||
params.put(Format.Luminance16Alpha16, new BitMaskImageCodec(4, FLAG_GRAY,
|
||||
16, 16, 0, 0,
|
||||
16, 0, 0, 0));
|
||||
|
||||
params.put(Format.Luminance16FAlpha16F, new BitMaskImageCodec(4, FLAG_GRAY | FLAG_F16,
|
||||
16, 16, 0, 0,
|
||||
16, 0, 0, 0));
|
||||
|
||||
// == RGB ==
|
||||
params.put(Format.BGR8, new BitMaskImageCodec(3, 0,
|
||||
0, 8, 8, 8,
|
||||
0, 16, 8, 0));
|
||||
|
||||
params.put(Format.RGB565, new BitMaskImageCodec(2, 0,
|
||||
0, 5, 6, 5,
|
||||
0, 11, 5, 0));
|
||||
|
||||
params.put(Format.RGB8, new BitMaskImageCodec(3, 0,
|
||||
0, 8, 8, 8,
|
||||
0, 0, 8, 16));
|
||||
|
||||
params.put(Format.RGB16, new ByteAlignedImageCodec(6, 0,
|
||||
0, 2, 2, 2,
|
||||
0, 0, 2, 4));
|
||||
|
||||
params.put(Format.RGB32F, new ByteAlignedImageCodec(12, FLAG_F32,
|
||||
0, 4, 4, 4,
|
||||
0, 0, 4, 8));
|
||||
|
||||
ByteAlignedImageCodec rgb16f = new ByteAlignedImageCodec(6, FLAG_F16,
|
||||
0, 2, 2, 2,
|
||||
0, 0, 2, 4);
|
||||
params.put(Format.RGB16F, rgb16f);
|
||||
params.put(Format.RGB16F_to_RGB111110F, rgb16f);
|
||||
params.put(Format.RGB16F_to_RGB9E5, rgb16f);
|
||||
|
||||
|
||||
// == RGBA ==
|
||||
params.put(Format.ABGR8, new BitMaskImageCodec(4, 0,
|
||||
0, 8, 8, 8,
|
||||
0, 24, 16, 8));
|
||||
|
||||
params.put(Format.ARGB4444, new BitMaskImageCodec(2, 0,
|
||||
4, 4, 4, 4,
|
||||
12, 0, 4, 8));
|
||||
|
||||
params.put(Format.RGB5A1, new BitMaskImageCodec(2, 0,
|
||||
1, 5, 5, 5,
|
||||
0, 11, 6, 1));
|
||||
((BitMaskImageCodec)params.get(Format.RGB5A1)).be = true;
|
||||
|
||||
params.put(Format.RGBA8, new ByteAlignedImageCodec(4, 0,
|
||||
0, 1, 1, 1,
|
||||
0, 0, 1, 2));
|
||||
|
||||
//new BitMaskImageCodec(4, 0,
|
||||
// 8, 8, 8, 8,
|
||||
// 24, 0, 8, 16));
|
||||
|
||||
params.put(Format.RGBA16, new ByteAlignedImageCodec(8, 0,
|
||||
2, 2, 2, 2,
|
||||
6, 0, 2, 4));
|
||||
|
||||
params.put(Format.RGBA16F, new ByteAlignedImageCodec(8, FLAG_F16,
|
||||
2, 2, 2, 2,
|
||||
6, 0, 2, 4));
|
||||
|
||||
params.put(Format.RGBA32F, new ByteAlignedImageCodec(16, FLAG_F32,
|
||||
4, 4, 4, 4,
|
||||
12, 0, 4, 8));
|
||||
}
|
||||
|
||||
public abstract void readComponents(ByteBuffer buf, int x, int y, int width, int[] components, byte[] tmp);
|
||||
|
||||
public abstract void writeComponents(ByteBuffer buf, int x, int y, int width, int[] components, byte[] tmp);
|
||||
|
||||
/**
|
||||
* Looks up the format in the codec registry.
|
||||
* The codec will be able to decode the given format.
|
||||
*
|
||||
* @param format The format to lookup.
|
||||
* @return The codec capable of decoding it, or null if not found.
|
||||
*/
|
||||
public static ImageCodec lookup(Format format) {
|
||||
ImageCodec codec = params.get(format);
|
||||
if (codec == null) {
|
||||
throw new UnsupportedOperationException("The format " + format + " is not supported");
|
||||
}
|
||||
return codec;
|
||||
}
|
||||
}
|
231
engine/src/core/com/jme3/texture/image/ImageRaster.java
Normal file
231
engine/src/core/com/jme3/texture/image/ImageRaster.java
Normal file
@ -0,0 +1,231 @@
|
||||
package com.jme3.texture.image;
|
||||
|
||||
import com.jme3.math.ColorRGBA;
|
||||
import com.jme3.math.FastMath;
|
||||
import com.jme3.texture.Image;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Utility class for reading and writing from jME3 {@link Image images}.
|
||||
* <br>
|
||||
* Allows directly manipulating pixels of the image by writing and
|
||||
* reading {@link ColorRGBA colors} at any coordinate, without
|
||||
* regard to the underlying {@link Image.Format format} of the image.
|
||||
* NOTE: compressed and depth formats are <strong>not supported</strong>.
|
||||
* Special RGB formats like RGB111110F and RGB9E5 are not supported
|
||||
* at the moment, but may be added later on. For now
|
||||
* use RGB16F_to_RGB111110F and RGB16F_to_RGB9E5 to handle
|
||||
* the conversion on the GPU.
|
||||
* <p>
|
||||
* If direct manipulations are done to the image, such as replacing
|
||||
* the image data, or changing the width, height, or format, then
|
||||
* all current instances of <code>ImageReadWrite</code> become invalid, and
|
||||
* new instances must be created in order to properly access
|
||||
* the image data.
|
||||
*
|
||||
* Usage example:<br>
|
||||
* <code>
|
||||
* Image myImage = ...
|
||||
* ImageReadWrite imageRW = new ImageReadWrite(myImage);
|
||||
* imageRW.setPixel(1, 5, ColorRGBA.Green);
|
||||
* System.out.println( imageRW.getPixel(1, 5) ); // Will print [0.0, 1.0, 0.0, 1.0].
|
||||
* </code>
|
||||
*
|
||||
* @author Kirill Vainer
|
||||
*/
|
||||
public final class ImageRaster {
|
||||
|
||||
private final int[] components = new int[4];
|
||||
private final ByteBuffer buffer;
|
||||
private final Image image;
|
||||
private final ImageCodec codec;
|
||||
private final int width;
|
||||
private final int height;
|
||||
private final byte[] temp;
|
||||
|
||||
/**
|
||||
* Create new image reader / writer.
|
||||
*
|
||||
* @param image The image to read / write to.
|
||||
* @param slice Which slice to use. Only applies to 3D images, 2D image
|
||||
* arrays or cubemaps.
|
||||
*/
|
||||
public ImageRaster(Image image, int slice) {
|
||||
this.image = image;
|
||||
this.buffer = image.getData(slice);
|
||||
this.codec = ImageCodec.lookup(image.getFormat());
|
||||
this.width = image.getWidth();
|
||||
this.height = image.getHeight();
|
||||
if (codec instanceof ByteAlignedImageCodec) {
|
||||
this.temp = new byte[codec.bpp];
|
||||
} else {
|
||||
this.temp = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new image reader / writer for 2D images.
|
||||
*
|
||||
* @param image The image to read / write to.
|
||||
*/
|
||||
public ImageRaster(Image image) {
|
||||
this(image, 0);
|
||||
if (image.getData().size() > 1) {
|
||||
throw new IllegalStateException("Use constructor that takes slices argument to read from multislice image");
|
||||
}
|
||||
}
|
||||
|
||||
private void rangeCheck(int x, int y) {
|
||||
if (x < 0 || y < 0 || x >= width || y >= height) {
|
||||
throw new IllegalArgumentException("x and y must be inside the image dimensions");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the pixel at the given coordinate to the given color.
|
||||
* <p>
|
||||
* For all integer based formats (those not ending in "F"), the
|
||||
* color is first clamped to 0.0 - 1.0 before converting it to
|
||||
* an integer to avoid overflow. For floating point based formats,
|
||||
* components larger than 1.0 can be represented, but components
|
||||
* lower than 0.0 are still not allowed (as all formats are unsigned).
|
||||
* <p>
|
||||
* If the underlying format is grayscale (e.g. one of the luminance formats,
|
||||
* such as {@link Image.Format#Luminance8}) then a color to grayscale
|
||||
* conversion is done first, before writing the result into the image.
|
||||
* <p>
|
||||
* If the image does not have some of the components in the color (such
|
||||
* as alpha, or any of the color components), then these components
|
||||
* will be ignored. The only exception to this is luminance formats
|
||||
* for which the color is converted to luminance first (see above).
|
||||
* <p>
|
||||
* After writing the color, the image shall be marked as requiring an
|
||||
* update. The next time it is used for rendering, all pixel changes
|
||||
* will be reflected when the image is rendered.
|
||||
*
|
||||
* @param x The x coordinate, from 0 to width - 1.
|
||||
* @param y The y coordinate, from 0 to height - 1.
|
||||
* @param color The color to write.
|
||||
* @throws IllegalArgumentException If x or y are outside the image dimensions.
|
||||
*/
|
||||
public void setPixel(int x, int y, ColorRGBA color) {
|
||||
rangeCheck(x, y);
|
||||
|
||||
// Check flags for grayscale
|
||||
if ((codec.flags & ImageCodec.FLAG_GRAY) != 0) {
|
||||
float gray = color.r * 0.27f + color.g * 0.67f + color.b * 0.06f;
|
||||
color = new ColorRGBA(gray, gray, gray, color.a);
|
||||
}
|
||||
|
||||
if ((codec.flags & ImageCodec.FLAG_F16) != 0) {
|
||||
components[0] = (int) FastMath.convertFloatToHalf(color.a);
|
||||
components[1] = (int) FastMath.convertFloatToHalf(color.r);
|
||||
components[2] = (int) FastMath.convertFloatToHalf(color.g);
|
||||
components[3] = (int) FastMath.convertFloatToHalf(color.b);
|
||||
} else if ((codec.flags & ImageCodec.FLAG_F32) != 0) {
|
||||
components[0] = (int) Float.floatToIntBits(color.a);
|
||||
components[1] = (int) Float.floatToIntBits(color.r);
|
||||
components[2] = (int) Float.floatToIntBits(color.g);
|
||||
components[3] = (int) Float.floatToIntBits(color.b);
|
||||
} else {
|
||||
// Convert color to bits by multiplying by size
|
||||
components[0] = Math.min( (int) (color.a * codec.maxAlpha + 0.5f), codec.maxAlpha);
|
||||
components[1] = Math.min( (int) (color.r * codec.maxRed + 0.5f), codec.maxRed);
|
||||
components[2] = Math.min( (int) (color.g * codec.maxGreen + 0.5f), codec.maxGreen);
|
||||
components[3] = Math.min( (int) (color.b * codec.maxBlue + 0.5f), codec.maxBlue);
|
||||
}
|
||||
|
||||
codec.writeComponents(buffer, x, y, width, components, temp);
|
||||
|
||||
image.setUpdateNeeded();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the color at the given coordinate.
|
||||
* <p>
|
||||
* Any components that are not defined in the image format
|
||||
* will be set to 1.0 in the returned color. For example,
|
||||
* reading from an {@link Image.Format#Alpha8} format will
|
||||
* return a ColorRGBA with the R, G, and B components set to 1.0, and
|
||||
* the A component set to the alpha in the image.
|
||||
* <p>
|
||||
* For grayscale or luminance formats, the luminance value is replicated
|
||||
* in the R, G, and B components.
|
||||
* <p>
|
||||
* Integer formats are converted to the range 0.0 - 1.0, based
|
||||
* on the maximum possible integer value that can be represented
|
||||
* by the number of bits the component has.
|
||||
* For example, the {@link Image.Format#RGB5A1} format can
|
||||
* contain the integer values 0 - 31, a conversion to floating point
|
||||
* is done by diving the integer value by 31 (done with floating point
|
||||
* precision).
|
||||
*
|
||||
* @param x The x coordinate, from 0 to width - 1.
|
||||
* @param y The y coordinate, from 0 to height - 1.
|
||||
* @param store Storage location for the read color, if <code>null</code>,
|
||||
* then a new ColorRGBA is created and returned with the read color.
|
||||
* @return The store parameter, if it is null, then a new ColorRGBA
|
||||
* with the read color.
|
||||
* @throws IllegalArgumentException If x or y are outside the image dimensions.
|
||||
*/
|
||||
public ColorRGBA getPixel(int x, int y, ColorRGBA store) {
|
||||
rangeCheck(x, y);
|
||||
|
||||
codec.readComponents(buffer, x, y, width, components, temp);
|
||||
|
||||
if (store == null) {
|
||||
store = new ColorRGBA();
|
||||
}
|
||||
if ((codec.flags & ImageCodec.FLAG_F16) != 0) {
|
||||
store.set(FastMath.convertHalfToFloat((short)components[1]),
|
||||
FastMath.convertHalfToFloat((short)components[2]),
|
||||
FastMath.convertHalfToFloat((short)components[3]),
|
||||
FastMath.convertHalfToFloat((short)components[0]));
|
||||
} else if ((codec.flags & ImageCodec.FLAG_F32) != 0) {
|
||||
store.set(Float.intBitsToFloat((int)components[1]),
|
||||
Float.intBitsToFloat((int)components[2]),
|
||||
Float.intBitsToFloat((int)components[3]),
|
||||
Float.intBitsToFloat((int)components[0]));
|
||||
} else {
|
||||
// Convert to float and divide by bitsize to get into range 0.0 - 1.0.
|
||||
store.set((float)components[1] / codec.maxRed,
|
||||
(float)components[2] / codec.maxGreen,
|
||||
(float)components[3] / codec.maxBlue,
|
||||
(float)components[0] / codec.maxAlpha);
|
||||
}
|
||||
if ((codec.flags & ImageCodec.FLAG_GRAY) != 0) {
|
||||
store.g = store.b = store.r;
|
||||
} else {
|
||||
if (codec.maxRed == 0) {
|
||||
store.r = 1;
|
||||
}
|
||||
if (codec.maxGreen == 0) {
|
||||
store.g = 1;
|
||||
}
|
||||
if (codec.maxBlue == 0) {
|
||||
store.b = 1;
|
||||
}
|
||||
if (codec.maxAlpha == 0) {
|
||||
store.a = 1;
|
||||
}
|
||||
}
|
||||
return store;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the color at the given coordinate.
|
||||
* <p>
|
||||
* Convenience method that does not take a store argument. Equivalent
|
||||
* to calling getPixel(x, y, null).
|
||||
* See {@link #getPixel(int, int, com.jme3.math.ColorRGBA) } for
|
||||
* more information.
|
||||
*
|
||||
* @param x The x coordinate, from 0 to width - 1.
|
||||
* @param y The y coordinate, from 0 to height - 1.
|
||||
* @return A new ColorRGBA with the read color.
|
||||
* @throws IllegalArgumentException If x or y are outside the image dimensions
|
||||
*/
|
||||
public ColorRGBA getPixel(int x, int y) {
|
||||
return getPixel(x, y, null);
|
||||
}
|
||||
}
|
169
engine/src/test/jme3test/texture/TestImageRaster.java
Normal file
169
engine/src/test/jme3test/texture/TestImageRaster.java
Normal file
@ -0,0 +1,169 @@
|
||||
package jme3test.texture;
|
||||
|
||||
|
||||
import com.jme3.app.SimpleApplication;
|
||||
import com.jme3.font.BitmapFont;
|
||||
import com.jme3.font.BitmapText;
|
||||
import com.jme3.font.Rectangle;
|
||||
import com.jme3.material.Material;
|
||||
import com.jme3.math.ColorRGBA;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.post.FilterPostProcessor;
|
||||
import com.jme3.post.filters.BloomFilter;
|
||||
import com.jme3.renderer.queue.RenderQueue;
|
||||
import com.jme3.scene.Geometry;
|
||||
import com.jme3.scene.shape.Quad;
|
||||
import com.jme3.texture.Image;
|
||||
import com.jme3.texture.Image.Format;
|
||||
import com.jme3.texture.Texture;
|
||||
import com.jme3.texture.Texture.MagFilter;
|
||||
import com.jme3.texture.Texture.MinFilter;
|
||||
import com.jme3.texture.Texture2D;
|
||||
import com.jme3.texture.image.ImageRaster;
|
||||
import com.jme3.util.BufferUtils;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class TestImageRaster extends SimpleApplication {
|
||||
|
||||
private Image convertImage(Image image, Format newFormat) {
|
||||
int width = image.getWidth();
|
||||
int height = image.getHeight();
|
||||
ByteBuffer data = BufferUtils.createByteBuffer( (int)Math.ceil(newFormat.getBitsPerPixel() / 8.0) * width * height);
|
||||
Image convertedImage = new Image(newFormat, width, height, data);
|
||||
|
||||
ImageRaster sourceReader = new ImageRaster(image);
|
||||
ImageRaster targetWriter = new ImageRaster(convertedImage);
|
||||
for (int x = 0; x < width; x++) {
|
||||
for (int y = 0; y < height; y++) {
|
||||
ColorRGBA color = sourceReader.getPixel(x, y);
|
||||
targetWriter.setPixel(x, y, color);
|
||||
}
|
||||
}
|
||||
|
||||
return convertedImage;
|
||||
}
|
||||
|
||||
private void convertAndPutImage(Image image, float posX, float posY) {
|
||||
Texture tex = new Texture2D(image);
|
||||
tex.setMagFilter(MagFilter.Nearest);
|
||||
tex.setMinFilter(MinFilter.NearestNoMipMaps);
|
||||
tex.setAnisotropicFilter(16);
|
||||
Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
||||
mat.setTexture("ColorMap", tex);
|
||||
|
||||
Quad q = new Quad(5, 5);
|
||||
Geometry g = new Geometry("quad", q);
|
||||
g.setLocalTranslation(posX, posY - 5, -0.0001f);
|
||||
g.setMaterial(mat);
|
||||
rootNode.attachChild(g);
|
||||
|
||||
BitmapFont fnt = assetManager.loadFont("Interface/Fonts/Default.fnt");
|
||||
BitmapText txt = new BitmapText(fnt);
|
||||
txt.setBox(new Rectangle(0, 0, 5, 5));
|
||||
txt.setQueueBucket(RenderQueue.Bucket.Transparent);
|
||||
txt.setSize(0.5f);
|
||||
txt.setText(image.getFormat().name());
|
||||
txt.setLocalTranslation(posX, posY, 0);
|
||||
rootNode.attachChild(txt);
|
||||
}
|
||||
|
||||
private Image createTestImage() {
|
||||
Image testImage = new Image(Format.BGR8, 4, 3, BufferUtils.createByteBuffer(4 * 4 * 3));
|
||||
|
||||
ImageRaster io = new ImageRaster(testImage);
|
||||
io.setPixel(0, 0, ColorRGBA.Black);
|
||||
io.setPixel(1, 0, ColorRGBA.Gray);
|
||||
io.setPixel(2, 0, ColorRGBA.White);
|
||||
io.setPixel(3, 0, ColorRGBA.White.mult(4)); // HDR color
|
||||
|
||||
io.setPixel(0, 1, ColorRGBA.Red);
|
||||
io.setPixel(1, 1, ColorRGBA.Green);
|
||||
io.setPixel(2, 1, ColorRGBA.Blue);
|
||||
io.setPixel(3, 1, new ColorRGBA(0, 0, 0, 0));
|
||||
|
||||
io.setPixel(0, 2, ColorRGBA.Yellow);
|
||||
io.setPixel(1, 2, ColorRGBA.Magenta);
|
||||
io.setPixel(2, 2, ColorRGBA.Cyan);
|
||||
io.setPixel(3, 2, new ColorRGBA(1, 1, 1, 0));
|
||||
|
||||
return testImage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void simpleInitApp() {
|
||||
cam.setLocation(new Vector3f(16, 6, 36));
|
||||
flyCam.setMoveSpeed(10);
|
||||
|
||||
Texture tex = assetManager.loadTexture("com/jme3/app/Monkey.png");
|
||||
// Texture tex = assetManager.loadTexture("Textures/HdrTest/Memorial.hdr");
|
||||
Image originalImage = tex.getImage();
|
||||
|
||||
Image image = convertImage(originalImage, Format.RGBA32F);
|
||||
convertAndPutImage(image, 0, 0);
|
||||
|
||||
image = convertImage(originalImage, Format.RGB32F);
|
||||
convertAndPutImage(image, 5, 0);
|
||||
|
||||
image = convertImage(originalImage, Format.RGBA16F);
|
||||
convertAndPutImage(image, 10, 0);
|
||||
|
||||
image = convertImage(originalImage, Format.RGB16F);
|
||||
convertAndPutImage(image, 15, 0);
|
||||
|
||||
image = convertImage(originalImage, Format.RGB16F_to_RGB9E5);
|
||||
convertAndPutImage(image, 20, 0);
|
||||
|
||||
image = convertImage(originalImage, Format.RGB16F_to_RGB111110F);
|
||||
convertAndPutImage(image, 25, 0);
|
||||
|
||||
image = convertImage(originalImage, Format.RGBA16);
|
||||
convertAndPutImage(image, 0, 5);
|
||||
|
||||
image = convertImage(originalImage, Format.RGB16);
|
||||
convertAndPutImage(image, 5, 5);
|
||||
|
||||
image = convertImage(originalImage, Format.RGBA8);
|
||||
convertAndPutImage(image, 10, 5);
|
||||
|
||||
image = convertImage(originalImage, Format.RGB8);
|
||||
convertAndPutImage(image, 15, 5);
|
||||
|
||||
image = convertImage(originalImage, Format.ABGR8);
|
||||
convertAndPutImage(image, 20, 5);
|
||||
|
||||
image = convertImage(originalImage, Format.BGR8);
|
||||
convertAndPutImage(image, 25, 5);
|
||||
|
||||
image = convertImage(originalImage, Format.RGB5A1);
|
||||
convertAndPutImage(image, 0, 10);
|
||||
|
||||
image = convertImage(originalImage, Format.ARGB4444);
|
||||
convertAndPutImage(image, 5, 10);
|
||||
|
||||
image = convertImage(originalImage, Format.Luminance32F);
|
||||
convertAndPutImage(image, 0, 15);
|
||||
|
||||
image = convertImage(originalImage, Format.Luminance16FAlpha16F);
|
||||
convertAndPutImage(image, 5, 15);
|
||||
|
||||
image = convertImage(originalImage, Format.Luminance16F);
|
||||
convertAndPutImage(image, 10, 15);
|
||||
|
||||
image = convertImage(originalImage, Format.Luminance16Alpha16);
|
||||
convertAndPutImage(image, 15, 15);
|
||||
|
||||
image = convertImage(originalImage, Format.Luminance16);
|
||||
convertAndPutImage(image, 20, 15);
|
||||
|
||||
image = convertImage(originalImage, Format.Luminance8Alpha8);
|
||||
convertAndPutImage(image, 25, 15);
|
||||
|
||||
image = convertImage(originalImage, Format.Luminance8);
|
||||
convertAndPutImage(image, 30, 15);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
TestImageRaster app = new TestImageRaster();
|
||||
app.start();
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user