* ImageRaster now supports Android. However the constructor can no longer be used. Instead user should create it by using ImageRaster.create() which automatically defers the handling to JmeSystem.

* TestImageRaster tests both reading and writing from various types of formats now by chaining the image conversions
 * 

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@9710 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
Sha..rd 12 years ago
parent 5aca23f9b3
commit 2a11aae3a4
  1. 18
      engine/src/android/com/jme3/asset/AndroidImageInfo.java
  2. 21
      engine/src/android/com/jme3/system/android/JmeAndroidSystem.java
  3. 7
      engine/src/core/com/jme3/system/JmeSystem.java
  4. 4
      engine/src/core/com/jme3/system/JmeSystemDelegate.java
  5. 32
      engine/src/core/com/jme3/texture/image/BitMaskImageCodec.java
  6. 114
      engine/src/core/com/jme3/texture/image/DefaultImageRaster.java
  7. 111
      engine/src/core/com/jme3/texture/image/ImageRaster.java
  8. 9
      engine/src/desktop/com/jme3/system/JmeDesktopSystem.java
  9. 50
      engine/src/test/jme3test/texture/TestImageRaster.java

@ -3,8 +3,10 @@ package com.jme3.asset;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import com.jme3.math.ColorRGBA;
import com.jme3.texture.Image;
import com.jme3.texture.Image.Format;
import com.jme3.texture.image.ImageRaster;
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.Level;
@ -18,7 +20,7 @@ import java.util.logging.Logger;
*
* @author Kirill Vainer
*/
public class AndroidImageInfo {
public class AndroidImageInfo extends ImageRaster {
private static final Logger logger = Logger.getLogger(AndroidImageInfo.class.getName());
@ -57,6 +59,20 @@ public class AndroidImageInfo {
return format;
}
@Override
public void setPixel(int x, int y, ColorRGBA color) {
getBitmap().setPixel(x, y, color.asIntARGB());
}
@Override
public ColorRGBA getPixel(int x, int y, ColorRGBA store) {
if (store == null) {
store = new ColorRGBA();
}
store.fromIntARGB(getBitmap().getPixel(x, y));
return store;
}
/**
* Loads the bitmap directly from the asset info, possibly updating
* or creating the image object.

@ -5,11 +5,15 @@ import android.app.AlertDialog;
import android.graphics.Bitmap;
import android.os.Environment;
import com.jme3.asset.AndroidAssetManager;
import com.jme3.asset.AndroidImageInfo;
import com.jme3.asset.AssetManager;
import com.jme3.audio.AudioRenderer;
import com.jme3.audio.android.AndroidAudioRenderer;
import com.jme3.system.*;
import com.jme3.system.JmeContext.Type;
import com.jme3.texture.Image;
import com.jme3.texture.image.DefaultImageRaster;
import com.jme3.texture.image.ImageRaster;
import com.jme3.util.AndroidScreenshots;
import com.jme3.util.JmeFormatter;
import java.io.File;
@ -25,6 +29,13 @@ public class JmeAndroidSystem extends JmeSystemDelegate {
private static Activity activity;
static {
try {
System.loadLibrary("bulletjme");
} catch (UnsatisfiedLinkError e) {
}
}
@Override
public void writeImageFile(OutputStream outStream, String format, ByteBuffer imageData, int width, int height) throws IOException {
Bitmap bitmapImage = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
@ -41,10 +52,12 @@ public class JmeAndroidSystem extends JmeSystemDelegate {
bitmapImage.recycle();
}
static {
try {
System.loadLibrary("bulletjme");
} catch (UnsatisfiedLinkError e) {
@Override
public ImageRaster createImageRaster(Image image, int slice) {
if (image.getEfficentData() != null) {
return (AndroidImageInfo) image.getEfficentData();
} else {
return new DefaultImageRaster(image, slice);
}
}

@ -34,6 +34,8 @@ package com.jme3.system;
import com.jme3.asset.AssetManager;
import com.jme3.audio.AudioRenderer;
import com.jme3.input.SoftTextDialogInput;
import com.jme3.texture.Image;
import com.jme3.texture.image.ImageRaster;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@ -130,6 +132,11 @@ public class JmeSystem {
checkDelegate();
return systemDelegate.newAudioRenderer(settings);
}
public static ImageRaster createImageRaster(Image image, int slice) {
checkDelegate();
return systemDelegate.createImageRaster(image, slice);
}
/**
* Displays an error message to the user in whichever way the context

@ -34,6 +34,8 @@ package com.jme3.system;
import com.jme3.asset.AssetManager;
import com.jme3.audio.AudioRenderer;
import com.jme3.input.SoftTextDialogInput;
import com.jme3.texture.Image;
import com.jme3.texture.image.ImageRaster;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@ -153,4 +155,6 @@ public abstract class JmeSystemDelegate {
public abstract AudioRenderer newAudioRenderer(AppSettings settings);
public abstract void initialize(AppSettings settings);
public abstract ImageRaster createImageRaster(Image image, int slice);
}

@ -26,27 +26,41 @@ class BitMaskImageCodec extends ImageCodec {
}
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);
//idx += bpp;
//int original = buf.get(--idx) & 0xff;
//while ((--bpp) > 0) {
// original = (original << 8) | (buf.get(--idx) & 0xff);
//}
//return original;
//return buf.getInt(idx) & (0xFFFFFFFF >>> (32 - bpp));
int pixel = 0;
buf.position(idx);
for (int i = 0; i < bpp; i++) {
pixel = pixel | (buf.get() & 0xff) << (i * 8);
}
return original;
return pixel;
}
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);
}
// This works:
// 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);
// }
// }
buf.position(idx);
for (int i = 0; i < bpp; i++) {
buf.put( (byte)((pixel >> (8 * i)) & 0xff) );
}
}
@Override

@ -0,0 +1,114 @@
package com.jme3.texture.image;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.texture.Image;
import java.nio.ByteBuffer;
public class DefaultImageRaster extends 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;
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");
}
}
public DefaultImageRaster(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;
}
}
@Override
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();
}
@Override
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;
}
}

@ -2,6 +2,7 @@ package com.jme3.texture.image;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.system.JmeSystem;
import com.jme3.texture.Image;
import java.nio.ByteBuffer;
@ -26,22 +27,14 @@ import java.nio.ByteBuffer;
* Usage example:<br>
* <code>
* Image myImage = ...
* ImageRaster raster = new ImageRaster(myImage);
* ImageRaster raster = ImageRaster.create(myImage);
* raster.setPixel(1, 5, ColorRGBA.Green);
* System.out.println( raster.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;
public abstract class ImageRaster {
/**
* Create new image reader / writer.
@ -50,17 +43,8 @@ public final class ImageRaster {
* @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;
}
public static ImageRaster create(Image image, int slices) {
return JmeSystem.createImageRaster(image, slices);
}
/**
@ -68,17 +52,14 @@ public final class ImageRaster {
*
* @param image The image to read / write to.
*/
public ImageRaster(Image image) {
this(image, 0);
public static ImageRaster create(Image image) {
if (image.getData().size() > 1) {
throw new IllegalStateException("Use constructor that takes slices argument to read from multislice image");
}
return JmeSystem.createImageRaster(image, 0);
}
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");
}
public ImageRaster() {
}
/**
@ -108,37 +89,7 @@ public final class ImageRaster {
* @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();
}
public abstract void setPixel(int x, int y, ColorRGBA color);
/**
* Retrieve the color at the given coordinate.
@ -168,49 +119,7 @@ public final class ImageRaster {
* 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;
}
public abstract ColorRGBA getPixel(int x, int y, ColorRGBA store);
/**
* Retrieve the color at the given coordinate.

@ -38,6 +38,9 @@ import com.jme3.asset.AssetNotFoundException;
import com.jme3.asset.DesktopAssetManager;
import com.jme3.audio.AudioRenderer;
import com.jme3.system.JmeContext.Type;
import com.jme3.texture.Image;
import com.jme3.texture.image.DefaultImageRaster;
import com.jme3.texture.image.ImageRaster;
import com.jme3.util.Screenshots;
import java.awt.EventQueue;
import java.awt.image.BufferedImage;
@ -70,6 +73,12 @@ public class JmeDesktopSystem extends JmeSystemDelegate {
ImageIO.write(awtImage, format, outStream);
}
@Override
public ImageRaster createImageRaster(Image image, int slice) {
assert image.getEfficentData() == null;
return new DefaultImageRaster(image, slice);
}
@Override
public AssetManager newAssetManager() {
return new DesktopAssetManager(null);

@ -8,8 +8,6 @@ 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;
@ -31,8 +29,8 @@ public class TestImageRaster extends SimpleApplication {
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);
ImageRaster sourceReader = ImageRaster.create(image);
ImageRaster targetWriter = ImageRaster.create(convertedImage);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
ColorRGBA color = sourceReader.getPixel(x, y);
@ -70,7 +68,7 @@ public class TestImageRaster extends SimpleApplication {
private Image createTestImage() {
Image testImage = new Image(Format.BGR8, 4, 3, BufferUtils.createByteBuffer(4 * 4 * 3));
ImageRaster io = new ImageRaster(testImage);
ImageRaster io = ImageRaster.create(testImage);
io.setPixel(0, 0, ColorRGBA.Black);
io.setPixel(1, 0, ColorRGBA.Gray);
io.setPixel(2, 0, ColorRGBA.White);
@ -88,7 +86,7 @@ public class TestImageRaster extends SimpleApplication {
return testImage;
}
@Override
public void simpleInitApp() {
cam.setLocation(new Vector3f(16, 6, 36));
@ -101,64 +99,64 @@ public class TestImageRaster extends SimpleApplication {
Image image = convertImage(originalImage, Format.RGBA32F);
convertAndPutImage(image, 0, 0);
image = convertImage(originalImage, Format.RGB32F);
image = convertImage(image, Format.RGB32F);
convertAndPutImage(image, 5, 0);
image = convertImage(originalImage, Format.RGBA16F);
image = convertImage(image, Format.RGBA16F);
convertAndPutImage(image, 10, 0);
image = convertImage(originalImage, Format.RGB16F);
image = convertImage(image, Format.RGB16F);
convertAndPutImage(image, 15, 0);
image = convertImage(originalImage, Format.RGB16F_to_RGB9E5);
image = convertImage(image, Format.RGB16F_to_RGB9E5);
convertAndPutImage(image, 20, 0);
image = convertImage(originalImage, Format.RGB16F_to_RGB111110F);
image = convertImage(image, Format.RGB16F_to_RGB111110F);
convertAndPutImage(image, 25, 0);
image = convertImage(originalImage, Format.RGBA16);
image = convertImage(image, Format.RGBA16);
convertAndPutImage(image, 0, 5);
image = convertImage(originalImage, Format.RGB16);
image = convertImage(image, Format.RGB16);
convertAndPutImage(image, 5, 5);
image = convertImage(originalImage, Format.RGBA8);
image = convertImage(image, Format.RGBA8);
convertAndPutImage(image, 10, 5);
image = convertImage(originalImage, Format.RGB8);
image = convertImage(image, Format.RGB8);
convertAndPutImage(image, 15, 5);
image = convertImage(originalImage, Format.ABGR8);
image = convertImage(image, Format.ABGR8);
convertAndPutImage(image, 20, 5);
image = convertImage(originalImage, Format.BGR8);
image = convertImage(image, Format.BGR8);
convertAndPutImage(image, 25, 5);
image = convertImage(originalImage, Format.RGB5A1);
image = convertImage(image, Format.RGB5A1);
convertAndPutImage(image, 0, 10);
image = convertImage(originalImage, Format.ARGB4444);
image = convertImage(image, Format.ARGB4444);
convertAndPutImage(image, 5, 10);
image = convertImage(originalImage, Format.Luminance32F);
image = convertImage(image, Format.Luminance32F);
convertAndPutImage(image, 0, 15);
image = convertImage(originalImage, Format.Luminance16FAlpha16F);
image = convertImage(image, Format.Luminance16FAlpha16F);
convertAndPutImage(image, 5, 15);
image = convertImage(originalImage, Format.Luminance16F);
image = convertImage(image, Format.Luminance16F);
convertAndPutImage(image, 10, 15);
image = convertImage(originalImage, Format.Luminance16Alpha16);
image = convertImage(image, Format.Luminance16Alpha16);
convertAndPutImage(image, 15, 15);
image = convertImage(originalImage, Format.Luminance16);
image = convertImage(image, Format.Luminance16);
convertAndPutImage(image, 20, 15);
image = convertImage(originalImage, Format.Luminance8Alpha8);
image = convertImage(image, Format.Luminance8Alpha8);
convertAndPutImage(image, 25, 15);
image = convertImage(originalImage, Format.Luminance8);
image = convertImage(image, Format.Luminance8);
convertAndPutImage(image, 30, 15);
}

Loading…
Cancel
Save