* 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-0572b91ccdca3.0
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); |
||||
} |
||||
} |
@ -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; |
||||
} |
||||
} |
@ -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); |
||||
} |
||||
} |
@ -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…
Reference in new issue