|
@@ -0,0 +1,471 @@
|
|
|
+package com.jme3.scene.plugins.blender.textures;
|
|
|
+
|
|
|
+import java.awt.color.ColorSpace;
|
|
|
+import java.awt.geom.AffineTransform;
|
|
|
+import java.awt.image.AffineTransformOp;
|
|
|
+import java.awt.image.BufferedImage;
|
|
|
+import java.awt.image.ColorConvertOp;
|
|
|
+import java.nio.ByteBuffer;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.List;
|
|
|
+
|
|
|
+import jme3tools.converters.ImageToAwt;
|
|
|
+import jme3tools.converters.RGB565;
|
|
|
+
|
|
|
+import com.jme3.math.ColorRGBA;
|
|
|
+import com.jme3.math.Vector3f;
|
|
|
+import com.jme3.scene.plugins.blender.textures.io.PixelIOFactory;
|
|
|
+import com.jme3.scene.plugins.blender.textures.io.PixelInputOutput;
|
|
|
+import com.jme3.texture.Image;
|
|
|
+import com.jme3.texture.Image.Format;
|
|
|
+import com.jme3.util.BufferUtils;
|
|
|
+
|
|
|
+/**
|
|
|
+ * This utility class has the methods that deal with images.
|
|
|
+ *
|
|
|
+ * @author Marcin Roguski (Kaelthas)
|
|
|
+ */
|
|
|
+public final class ImageUtils {
|
|
|
+ /**
|
|
|
+ * Creates an image of the given size and depth.
|
|
|
+ * @param format
|
|
|
+ * the image format
|
|
|
+ * @param width
|
|
|
+ * the image width
|
|
|
+ * @param height
|
|
|
+ * the image height
|
|
|
+ * @param depth
|
|
|
+ * the image depth
|
|
|
+ * @return the new image instance
|
|
|
+ */
|
|
|
+ public static Image createEmptyImage(Format format, int width, int height, int depth) {
|
|
|
+ int bufferSize = width * height * (format.getBitsPerPixel() >> 3);
|
|
|
+ if (depth < 2) {
|
|
|
+ return new Image(format, width, height, BufferUtils.createByteBuffer(bufferSize));
|
|
|
+ }
|
|
|
+ ArrayList<ByteBuffer> data = new ArrayList<ByteBuffer>(depth);
|
|
|
+ for (int i = 0; i < depth; ++i) {
|
|
|
+ data.add(BufferUtils.createByteBuffer(bufferSize));
|
|
|
+ }
|
|
|
+ return new Image(Format.RGB8, width, height, depth, data);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * The method sets a color for the given pixel by merging the two given colors.
|
|
|
+ * The lowIntensityColor will be most visible when the pixel has low intensity.
|
|
|
+ * The highIntensityColor will be most visible when the pixel has high intensity.
|
|
|
+ *
|
|
|
+ * @param pixel
|
|
|
+ * the pixel that will have the colors altered
|
|
|
+ * @param lowIntensityColor
|
|
|
+ * the low intensity color
|
|
|
+ * @param highIntensityColor
|
|
|
+ * the high intensity color
|
|
|
+ * @return the altered pixel (the same instance)
|
|
|
+ */
|
|
|
+ public static TexturePixel color(TexturePixel pixel, ColorRGBA lowIntensityColor, ColorRGBA highIntensityColor) {
|
|
|
+ float intensity = pixel.intensity;
|
|
|
+ pixel.fromColor(lowIntensityColor);
|
|
|
+ pixel.mult(1 - pixel.intensity);
|
|
|
+ pixel.add(highIntensityColor.mult(intensity));
|
|
|
+ return pixel;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * This method merges two given images. The result is stored in the
|
|
|
+ * 'target' image.
|
|
|
+ *
|
|
|
+ * @param targetImage
|
|
|
+ * the target image
|
|
|
+ * @param sourceImage
|
|
|
+ * the source image
|
|
|
+ */
|
|
|
+ public static void merge(Image targetImage, Image sourceImage) {
|
|
|
+ if (sourceImage.getDepth() != targetImage.getDepth()) {
|
|
|
+ throw new IllegalArgumentException("The given images should have the same depth to merge them!");
|
|
|
+ }
|
|
|
+ if (sourceImage.getWidth() != targetImage.getWidth()) {
|
|
|
+ throw new IllegalArgumentException("The given images should have the same width to merge them!");
|
|
|
+ }
|
|
|
+ if (sourceImage.getHeight() != targetImage.getHeight()) {
|
|
|
+ throw new IllegalArgumentException("The given images should have the same height to merge them!");
|
|
|
+ }
|
|
|
+
|
|
|
+ PixelInputOutput sourceIO = PixelIOFactory.getPixelIO(sourceImage.getFormat());
|
|
|
+ PixelInputOutput targetIO = PixelIOFactory.getPixelIO(targetImage.getFormat());
|
|
|
+ TexturePixel sourcePixel = new TexturePixel();
|
|
|
+ TexturePixel targetPixel = new TexturePixel();
|
|
|
+ int depth = targetImage.getDepth() == 0 ? 1 : targetImage.getDepth();
|
|
|
+
|
|
|
+ for (int layerIndex = 0; layerIndex < depth; ++layerIndex) {
|
|
|
+ for (int x = 0; x < sourceImage.getWidth(); ++x) {
|
|
|
+ for (int y = 0; y < sourceImage.getHeight(); ++y) {
|
|
|
+ sourceIO.read(sourceImage, layerIndex, sourcePixel, x, y);
|
|
|
+ targetIO.read(targetImage, layerIndex, targetPixel, x, y);
|
|
|
+ targetPixel.merge(sourcePixel);
|
|
|
+ targetIO.write(targetImage, layerIndex, targetPixel, x, y);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * This method merges two given images. The result is stored in the
|
|
|
+ * 'target' image.
|
|
|
+ *
|
|
|
+ * @param targetImage
|
|
|
+ * the target image
|
|
|
+ * @param sourceImage
|
|
|
+ * the source image
|
|
|
+ */
|
|
|
+ public static void mix(Image targetImage, Image sourceImage) {
|
|
|
+ if (sourceImage.getDepth() != targetImage.getDepth()) {
|
|
|
+ throw new IllegalArgumentException("The given images should have the same depth to merge them!");
|
|
|
+ }
|
|
|
+ if (sourceImage.getWidth() != targetImage.getWidth()) {
|
|
|
+ throw new IllegalArgumentException("The given images should have the same width to merge them!");
|
|
|
+ }
|
|
|
+ if (sourceImage.getHeight() != targetImage.getHeight()) {
|
|
|
+ throw new IllegalArgumentException("The given images should have the same height to merge them!");
|
|
|
+ }
|
|
|
+
|
|
|
+ PixelInputOutput sourceIO = PixelIOFactory.getPixelIO(sourceImage.getFormat());
|
|
|
+ PixelInputOutput targetIO = PixelIOFactory.getPixelIO(targetImage.getFormat());
|
|
|
+ TexturePixel sourcePixel = new TexturePixel();
|
|
|
+ TexturePixel targetPixel = new TexturePixel();
|
|
|
+ int depth = targetImage.getDepth() == 0 ? 1 : targetImage.getDepth();
|
|
|
+
|
|
|
+ for (int layerIndex = 0; layerIndex < depth; ++layerIndex) {
|
|
|
+ for (int x = 0; x < sourceImage.getWidth(); ++x) {
|
|
|
+ for (int y = 0; y < sourceImage.getHeight(); ++y) {
|
|
|
+ sourceIO.read(sourceImage, layerIndex, sourcePixel, x, y);
|
|
|
+ targetIO.read(targetImage, layerIndex, targetPixel, x, y);
|
|
|
+ targetPixel.mix(sourcePixel);
|
|
|
+ targetIO.write(targetImage, layerIndex, targetPixel, x, y);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Resizes the image to the given width and height.
|
|
|
+ * @param source
|
|
|
+ * the source image (this remains untouched, the new image instance is created)
|
|
|
+ * @param width
|
|
|
+ * the target image width
|
|
|
+ * @param height
|
|
|
+ * the target image height
|
|
|
+ * @return the resized image
|
|
|
+ */
|
|
|
+ public static Image resizeTo(Image source, int width, int height) {
|
|
|
+ BufferedImage sourceImage = ImageToAwt.convert(source, false, false, 0);
|
|
|
+
|
|
|
+ double scaleX = width / (double) sourceImage.getWidth();
|
|
|
+ double scaleY = height / (double) sourceImage.getHeight();
|
|
|
+ AffineTransform scaleTransform = AffineTransform.getScaleInstance(scaleX, scaleY);
|
|
|
+ AffineTransformOp bilinearScaleOp = new AffineTransformOp(scaleTransform, AffineTransformOp.TYPE_BILINEAR);
|
|
|
+
|
|
|
+ BufferedImage scaledImage = bilinearScaleOp.filter(sourceImage, new BufferedImage(width, height, sourceImage.getType()));
|
|
|
+ return ImageUtils.toJmeImage(scaledImage, source.getFormat());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * This method converts the given texture into normal-map texture.
|
|
|
+ *
|
|
|
+ * @param source
|
|
|
+ * the source texture
|
|
|
+ * @param strengthFactor
|
|
|
+ * the normal strength factor
|
|
|
+ * @return normal-map texture
|
|
|
+ */
|
|
|
+ public static Image convertToNormalMapTexture(Image source, float strengthFactor) {
|
|
|
+ BufferedImage sourceImage = ImageToAwt.convert(source, false, false, 0);
|
|
|
+
|
|
|
+ BufferedImage heightMap = new BufferedImage(sourceImage.getWidth(), sourceImage.getHeight(), BufferedImage.TYPE_INT_ARGB);
|
|
|
+ BufferedImage bumpMap = new BufferedImage(sourceImage.getWidth(), sourceImage.getHeight(), BufferedImage.TYPE_INT_ARGB);
|
|
|
+ ColorConvertOp gscale = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null);
|
|
|
+ gscale.filter(sourceImage, heightMap);
|
|
|
+
|
|
|
+ Vector3f S = new Vector3f();
|
|
|
+ Vector3f T = new Vector3f();
|
|
|
+ Vector3f N = new Vector3f();
|
|
|
+
|
|
|
+ for (int x = 0; x < bumpMap.getWidth(); ++x) {
|
|
|
+ for (int y = 0; y < bumpMap.getHeight(); ++y) {
|
|
|
+ // generating bump pixel
|
|
|
+ S.x = 1;
|
|
|
+ S.y = 0;
|
|
|
+ S.z = strengthFactor * ImageUtils.getHeight(heightMap, x + 1, y) - strengthFactor * ImageUtils.getHeight(heightMap, x - 1, y);
|
|
|
+ T.x = 0;
|
|
|
+ T.y = 1;
|
|
|
+ T.z = strengthFactor * ImageUtils.getHeight(heightMap, x, y + 1) - strengthFactor * ImageUtils.getHeight(heightMap, x, y - 1);
|
|
|
+
|
|
|
+ float den = (float) Math.sqrt(S.z * S.z + T.z * T.z + 1);
|
|
|
+ N.x = -S.z;
|
|
|
+ N.y = -T.z;
|
|
|
+ N.z = 1;
|
|
|
+ N.divideLocal(den);
|
|
|
+
|
|
|
+ // setting thge pixel in the result image
|
|
|
+ bumpMap.setRGB(x, y, ImageUtils.vectorToColor(N.x, N.y, N.z));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return ImageUtils.toJmeImage(bumpMap, source.getFormat());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * This method converts the given texture into black and whit (grayscale) texture.
|
|
|
+ *
|
|
|
+ * @param source
|
|
|
+ * the source texture
|
|
|
+ * @return grayscale texture
|
|
|
+ */
|
|
|
+ public static Image convertToGrayscaleTexture(Image source) {
|
|
|
+ BufferedImage sourceImage = ImageToAwt.convert(source, false, false, 0);
|
|
|
+ ColorConvertOp op = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null);
|
|
|
+ op.filter(sourceImage, sourceImage);
|
|
|
+ return ImageUtils.toJmeImage(sourceImage, source.getFormat());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * This method decompresses the given image. If the given image is already
|
|
|
+ * decompressed nothing happens and it is simply returned.
|
|
|
+ *
|
|
|
+ * @param image
|
|
|
+ * the image to decompress
|
|
|
+ * @return the decompressed image
|
|
|
+ */
|
|
|
+ public static Image decompress(Image image) {
|
|
|
+ Format format = image.getFormat();
|
|
|
+ int depth = image.getDepth();
|
|
|
+ if (depth == 0) {
|
|
|
+ depth = 1;
|
|
|
+ }
|
|
|
+ ArrayList<ByteBuffer> dataArray = new ArrayList<ByteBuffer>(depth);
|
|
|
+ int[] sizes = image.getMipMapSizes() != null ? image.getMipMapSizes() : new int[1];
|
|
|
+ int[] newMipmapSizes = image.getMipMapSizes() != null ? new int[image.getMipMapSizes().length] : null;
|
|
|
+
|
|
|
+ for (int dataLayerIndex = 0; dataLayerIndex < depth; ++dataLayerIndex) {
|
|
|
+ ByteBuffer data = image.getData(dataLayerIndex);
|
|
|
+ data.rewind();
|
|
|
+ if (sizes.length == 1) {
|
|
|
+ sizes[0] = data.remaining();
|
|
|
+ }
|
|
|
+ float widthToHeightRatio = image.getWidth() / image.getHeight();// this should always be constant for each mipmap
|
|
|
+ List<DDSTexelData> texelDataList = new ArrayList<DDSTexelData>(sizes.length);
|
|
|
+ int maxPosition = 0, resultSize = 0;
|
|
|
+
|
|
|
+ for (int sizeIndex = 0; sizeIndex < sizes.length; ++sizeIndex) {
|
|
|
+ maxPosition += sizes[sizeIndex];
|
|
|
+ DDSTexelData texelData = new DDSTexelData(sizes[sizeIndex], widthToHeightRatio, format);
|
|
|
+ texelDataList.add(texelData);
|
|
|
+ switch (format) {
|
|
|
+ case DXT1:// BC1
|
|
|
+ case DXT1A:
|
|
|
+ while (data.position() < maxPosition) {
|
|
|
+ TexturePixel[] colors = new TexturePixel[] { new TexturePixel(), new TexturePixel(), new TexturePixel(), new TexturePixel() };
|
|
|
+ short c0 = data.getShort();
|
|
|
+ short c1 = data.getShort();
|
|
|
+ int col0 = RGB565.RGB565_to_ARGB8(c0);
|
|
|
+ int col1 = RGB565.RGB565_to_ARGB8(c1);
|
|
|
+ colors[0].fromARGB8(col0);
|
|
|
+ colors[1].fromARGB8(col1);
|
|
|
+
|
|
|
+ if (col0 > col1) {
|
|
|
+ // creating color2 = 2/3color0 + 1/3color1
|
|
|
+ colors[2].fromPixel(colors[0]);
|
|
|
+ colors[2].mult(2);
|
|
|
+ colors[2].add(colors[1]);
|
|
|
+ colors[2].divide(3);
|
|
|
+
|
|
|
+ // creating color3 = 1/3color0 + 2/3color1;
|
|
|
+ colors[3].fromPixel(colors[1]);
|
|
|
+ colors[3].mult(2);
|
|
|
+ colors[3].add(colors[0]);
|
|
|
+ colors[3].divide(3);
|
|
|
+ } else {
|
|
|
+ // creating color2 = 1/2color0 + 1/2color1
|
|
|
+ colors[2].fromPixel(colors[0]);
|
|
|
+ colors[2].add(colors[1]);
|
|
|
+ colors[2].mult(0.5f);
|
|
|
+
|
|
|
+ colors[3].fromARGB8(0);
|
|
|
+ }
|
|
|
+ int indexes = data.getInt();// 4-byte table with color indexes in decompressed table
|
|
|
+ texelData.add(colors, indexes);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case DXT3:// BC2
|
|
|
+ while (data.position() < maxPosition) {
|
|
|
+ TexturePixel[] colors = new TexturePixel[] { new TexturePixel(), new TexturePixel(), new TexturePixel(), new TexturePixel() };
|
|
|
+ long alpha = data.getLong();
|
|
|
+ float[] alphas = new float[16];
|
|
|
+ long alphasIndex = 0;
|
|
|
+ for (int i = 0; i < 16; ++i) {
|
|
|
+ alphasIndex |= i << i * 4;
|
|
|
+ byte a = (byte) ((alpha >> i * 4 & 0x0F) << 4);
|
|
|
+ alphas[i] = a >= 0 ? a / 255.0f : 1.0f - ~a / 255.0f;
|
|
|
+ }
|
|
|
+
|
|
|
+ short c0 = data.getShort();
|
|
|
+ short c1 = data.getShort();
|
|
|
+ int col0 = RGB565.RGB565_to_ARGB8(c0);
|
|
|
+ int col1 = RGB565.RGB565_to_ARGB8(c1);
|
|
|
+ colors[0].fromARGB8(col0);
|
|
|
+ colors[1].fromARGB8(col1);
|
|
|
+
|
|
|
+ // creating color2 = 2/3color0 + 1/3color1
|
|
|
+ colors[2].fromPixel(colors[0]);
|
|
|
+ colors[2].mult(2);
|
|
|
+ colors[2].add(colors[1]);
|
|
|
+ colors[2].divide(3);
|
|
|
+
|
|
|
+ // creating color3 = 1/3color0 + 2/3color1;
|
|
|
+ colors[3].fromPixel(colors[1]);
|
|
|
+ colors[3].mult(2);
|
|
|
+ colors[3].add(colors[0]);
|
|
|
+ colors[3].divide(3);
|
|
|
+
|
|
|
+ int indexes = data.getInt();// 4-byte table with color indexes in decompressed table
|
|
|
+ texelData.add(colors, indexes, alphas, alphasIndex);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case DXT5:// BC3
|
|
|
+ float[] alphas = new float[8];
|
|
|
+ while (data.position() < maxPosition) {
|
|
|
+ TexturePixel[] colors = new TexturePixel[] { new TexturePixel(), new TexturePixel(), new TexturePixel(), new TexturePixel() };
|
|
|
+ alphas[0] = data.get() * 255.0f;
|
|
|
+ alphas[1] = data.get() * 255.0f;
|
|
|
+ long alphaIndices = data.get() | data.get() << 8 | data.get() << 16 | data.get() << 24 | data.get() << 32 | data.get() << 40;
|
|
|
+ if (alphas[0] > alphas[1]) {// 6 interpolated alpha values.
|
|
|
+ alphas[2] = (6 * alphas[0] + alphas[1]) / 7;
|
|
|
+ alphas[3] = (5 * alphas[0] + 2 * alphas[1]) / 7;
|
|
|
+ alphas[4] = (4 * alphas[0] + 3 * alphas[1]) / 7;
|
|
|
+ alphas[5] = (3 * alphas[0] + 4 * alphas[1]) / 7;
|
|
|
+ alphas[6] = (2 * alphas[0] + 5 * alphas[1]) / 7;
|
|
|
+ alphas[7] = (alphas[0] + 6 * alphas[1]) / 7;
|
|
|
+ } else {
|
|
|
+ alphas[2] = (4 * alphas[0] + alphas[1]) * 0.2f;
|
|
|
+ alphas[3] = (3 * alphas[0] + 2 * alphas[1]) * 0.2f;
|
|
|
+ alphas[4] = (2 * alphas[0] + 3 * alphas[1]) * 0.2f;
|
|
|
+ alphas[5] = (alphas[0] + 4 * alphas[1]) * 0.2f;
|
|
|
+ alphas[6] = 0;
|
|
|
+ alphas[7] = 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ short c0 = data.getShort();
|
|
|
+ short c1 = data.getShort();
|
|
|
+ int col0 = RGB565.RGB565_to_ARGB8(c0);
|
|
|
+ int col1 = RGB565.RGB565_to_ARGB8(c1);
|
|
|
+ colors[0].fromARGB8(col0);
|
|
|
+ colors[1].fromARGB8(col1);
|
|
|
+
|
|
|
+ // creating color2 = 2/3color0 + 1/3color1
|
|
|
+ colors[2].fromPixel(colors[0]);
|
|
|
+ colors[2].mult(2);
|
|
|
+ colors[2].add(colors[1]);
|
|
|
+ colors[2].divide(3);
|
|
|
+
|
|
|
+ // creating color3 = 1/3color0 + 2/3color1;
|
|
|
+ colors[3].fromPixel(colors[1]);
|
|
|
+ colors[3].mult(2);
|
|
|
+ colors[3].add(colors[0]);
|
|
|
+ colors[3].divide(3);
|
|
|
+
|
|
|
+ int indexes = data.getInt();// 4-byte table with color indexes in decompressed table
|
|
|
+ texelData.add(colors, indexes, alphas, alphaIndices);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ throw new IllegalStateException("Unknown compressed format: " + format);
|
|
|
+ }
|
|
|
+ newMipmapSizes[sizeIndex] = texelData.getSizeInBytes();
|
|
|
+ resultSize += texelData.getSizeInBytes();
|
|
|
+ }
|
|
|
+ byte[] bytes = new byte[resultSize];
|
|
|
+ int offset = 0;
|
|
|
+ byte[] pixelBytes = new byte[4];
|
|
|
+ for (DDSTexelData texelData : texelDataList) {
|
|
|
+ for (int i = 0; i < texelData.getPixelWidth(); ++i) {
|
|
|
+ for (int j = 0; j < texelData.getPixelHeight(); ++j) {
|
|
|
+ if (texelData.getRGBA8(i, j, pixelBytes)) {
|
|
|
+ bytes[offset + (j * texelData.getPixelWidth() + i) * 4] = pixelBytes[0];
|
|
|
+ bytes[offset + (j * texelData.getPixelWidth() + i) * 4 + 1] = pixelBytes[1];
|
|
|
+ bytes[offset + (j * texelData.getPixelWidth() + i) * 4 + 2] = pixelBytes[2];
|
|
|
+ bytes[offset + (j * texelData.getPixelWidth() + i) * 4 + 3] = pixelBytes[3];
|
|
|
+ } else {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ offset += texelData.getSizeInBytes();
|
|
|
+ }
|
|
|
+ dataArray.add(BufferUtils.createByteBuffer(bytes));
|
|
|
+ }
|
|
|
+
|
|
|
+ Image result = depth > 1 ? new Image(Format.RGBA8, image.getWidth(), image.getHeight(), depth, dataArray) : new Image(Format.RGBA8, image.getWidth(), image.getHeight(), dataArray.get(0));
|
|
|
+ if (newMipmapSizes != null) {
|
|
|
+ result.setMipMapSizes(newMipmapSizes);
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * This method returns the height represented by the specified pixel in the
|
|
|
+ * given texture. The given texture should be a height-map.
|
|
|
+ *
|
|
|
+ * @param image
|
|
|
+ * the height-map texture
|
|
|
+ * @param x
|
|
|
+ * pixel's X coordinate
|
|
|
+ * @param y
|
|
|
+ * pixel's Y coordinate
|
|
|
+ * @return height reprezented by the given texture in the specified location
|
|
|
+ */
|
|
|
+ private static int getHeight(BufferedImage image, int x, int y) {
|
|
|
+ if (x < 0) {
|
|
|
+ x = 0;
|
|
|
+ } else if (x >= image.getWidth()) {
|
|
|
+ x = image.getWidth() - 1;
|
|
|
+ }
|
|
|
+ if (y < 0) {
|
|
|
+ y = 0;
|
|
|
+ } else if (y >= image.getHeight()) {
|
|
|
+ y = image.getHeight() - 1;
|
|
|
+ }
|
|
|
+ return image.getRGB(x, y) & 0xff;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * This method transforms given vector's coordinates into ARGB color (A is
|
|
|
+ * always = 255).
|
|
|
+ *
|
|
|
+ * @param x
|
|
|
+ * X factor of the vector
|
|
|
+ * @param y
|
|
|
+ * Y factor of the vector
|
|
|
+ * @param z
|
|
|
+ * Z factor of the vector
|
|
|
+ * @return color representation of the given vector
|
|
|
+ */
|
|
|
+ private static int vectorToColor(float x, float y, float z) {
|
|
|
+ int r = Math.round(255 * (x + 1f) / 2f);
|
|
|
+ int g = Math.round(255 * (y + 1f) / 2f);
|
|
|
+ int b = Math.round(255 * (z + 1f) / 2f);
|
|
|
+ return (255 << 24) + (r << 16) + (g << 8) + b;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Converts java awt image to jme image.
|
|
|
+ * @param bufferedImage
|
|
|
+ * the java awt image
|
|
|
+ * @param format
|
|
|
+ * the result image format
|
|
|
+ * @return the jme image
|
|
|
+ */
|
|
|
+ private static Image toJmeImage(BufferedImage bufferedImage, Format format) {
|
|
|
+ ByteBuffer byteBuffer = BufferUtils.createByteBuffer(bufferedImage.getWidth() * bufferedImage.getHeight() * 3);
|
|
|
+ ImageToAwt.convert(bufferedImage, format, byteBuffer);
|
|
|
+ return new Image(format, bufferedImage.getWidth(), bufferedImage.getHeight(), byteBuffer);
|
|
|
+ }
|
|
|
+}
|