* 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