Android native image loader rewritten from scratch
* Now supports reading directly from Java InputStream instead of having to read image file into memory first * Optimized native code - reduced unneccessary memory copies
This commit is contained in:
parent
b83603cd8f
commit
7057e9cb18
@ -8,6 +8,7 @@ LOCAL_MODULE := stbijme
|
|||||||
|
|
||||||
LOCAL_C_INCLUDES += $(LOCAL_PATH)
|
LOCAL_C_INCLUDES += $(LOCAL_PATH)
|
||||||
|
|
||||||
|
LOCAL_CFLAGS := -std=c99
|
||||||
LOCAL_LDLIBS := -lz -llog -Wl,-s
|
LOCAL_LDLIBS := -lz -llog -Wl,-s
|
||||||
|
|
||||||
LOCAL_SRC_FILES := com_jme3_texture_plugins_AndroidNativeImageLoader.c
|
LOCAL_SRC_FILES := com_jme3_texture_plugins_AndroidNativeImageLoader.c
|
||||||
|
@ -1,25 +1,332 @@
|
|||||||
#include "com_jme3_texture_plugins_AndroidNativeImageLoader.h"
|
#include "com_jme3_texture_plugins_AndroidNativeImageLoader.h"
|
||||||
// for __android_log_print(ANDROID_LOG_INFO, "YourApp", "formatted message");
|
|
||||||
#include <android/log.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string.h>
|
|
||||||
#include <time.h>
|
#ifdef DEBUG
|
||||||
|
#include <android/log.h>
|
||||||
|
#define LOGI(fmt, ...) __android_log_print(ANDROID_LOG_INFO, \
|
||||||
|
"NativeImageLoader", fmt, ##__VA_ARGS__);
|
||||||
|
#else
|
||||||
|
#define LOGI(fmt, ...)
|
||||||
|
#endif
|
||||||
|
|
||||||
#define STB_IMAGE_IMPLEMENTATION
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
|
#define STBI_NO_STDIO
|
||||||
|
#define STBI_NO_HDR
|
||||||
#include "stb_image.h"
|
#include "stb_image.h"
|
||||||
|
|
||||||
typedef unsigned int uint32;
|
typedef struct
|
||||||
|
{
|
||||||
|
JNIEnv* env;
|
||||||
|
jbyteArray tmp;
|
||||||
|
int tmpSize;
|
||||||
|
jobject isObject;
|
||||||
|
jmethodID isReadMethod;
|
||||||
|
jmethodID isSkipMethod;
|
||||||
|
int isEOF;
|
||||||
|
char* errorMsg;
|
||||||
|
}
|
||||||
|
JavaInputStreamWrapper;
|
||||||
|
|
||||||
|
static void throwIOException(JNIEnv* env, const char* message)
|
||||||
|
{
|
||||||
|
jclass ioExClazz = (*env)->FindClass(env, "java/io/IOException");
|
||||||
|
(*env)->ThrowNew(env, ioExClazz, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int InputStream_read(void *user, char *nativeData, int nativeSize) {
|
||||||
|
JavaInputStreamWrapper* wrapper = (JavaInputStreamWrapper*) user;
|
||||||
|
JNIEnv* env = wrapper->env;
|
||||||
|
|
||||||
|
if (nativeSize <= 0)
|
||||||
|
{
|
||||||
|
wrapper->isEOF = 1;
|
||||||
|
wrapper->errorMsg = "read() requested negative or zero size";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
jbyteArray tmp = wrapper->tmp;
|
||||||
|
jint tmpSize = wrapper->tmpSize;
|
||||||
|
jint remaining = nativeSize;
|
||||||
|
jint offset = 0;
|
||||||
|
|
||||||
|
while (offset < nativeSize)
|
||||||
|
{
|
||||||
|
// Read data into Java array.
|
||||||
|
jint toRead = tmpSize < remaining ? tmpSize : remaining;
|
||||||
|
jint read = (*env)->CallIntMethod(env, wrapper->isObject,
|
||||||
|
wrapper->isReadMethod,
|
||||||
|
tmp, (jint)0, (jint)toRead);
|
||||||
|
|
||||||
|
// Check IOException
|
||||||
|
if ((*env)->ExceptionCheck(env))
|
||||||
|
{
|
||||||
|
wrapper->isEOF = 1;
|
||||||
|
wrapper->errorMsg = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGI("InputStream->read(tmp, 0, %d) = %d", toRead, read);
|
||||||
|
|
||||||
|
// Read -1 bytes = EOF.
|
||||||
|
if (read < 0)
|
||||||
|
{
|
||||||
|
wrapper->isEOF = 1;
|
||||||
|
wrapper->errorMsg = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (read == 0)
|
||||||
|
{
|
||||||
|
// Read 0 bytes, give it another try.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read 1 byte or more.
|
||||||
|
|
||||||
|
LOGI("memcpy(native[%d], java, %d)", offset, read);
|
||||||
|
|
||||||
|
// Copy contents of Java array to native array.
|
||||||
|
jbyte* nativeTmp = (*env)->GetPrimitiveArrayCritical(env, tmp, 0);
|
||||||
|
|
||||||
|
if (nativeTmp == NULL)
|
||||||
|
{
|
||||||
|
wrapper->isEOF = 1;
|
||||||
|
wrapper->errorMsg = "Failed to acquire Java array contents";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(&nativeData[offset], nativeTmp, read);
|
||||||
|
|
||||||
|
(*env)->ReleasePrimitiveArrayCritical(env, tmp, nativeTmp, 0);
|
||||||
|
|
||||||
|
offset += read;
|
||||||
|
remaining -= read;
|
||||||
|
|
||||||
|
assert(remaining >= 0);
|
||||||
|
assert(offset <= nativeSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void InputStream_skip(void *user, int n) {
|
||||||
|
JavaInputStreamWrapper* wrapper = (JavaInputStreamWrapper*) user;
|
||||||
|
JNIEnv* env = wrapper->env;
|
||||||
|
|
||||||
|
if (n < 0)
|
||||||
|
{
|
||||||
|
wrapper->isEOF = 1;
|
||||||
|
wrapper->errorMsg = "Negative seek attempt detected";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (n == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// InputStream.skip(n);
|
||||||
|
jlong result = (*env)->CallLongMethod(env, wrapper->isObject,
|
||||||
|
wrapper->isSkipMethod, (jlong)n);
|
||||||
|
|
||||||
|
LOGI("InputStream->skip(%lld) = %lld", (jlong)n, result);
|
||||||
|
|
||||||
|
// IOException
|
||||||
|
if ((*env)->ExceptionCheck(env))
|
||||||
|
{
|
||||||
|
wrapper->isEOF = 1;
|
||||||
|
wrapper->errorMsg = NULL;
|
||||||
|
}
|
||||||
|
else if ((int)result != n)
|
||||||
|
{
|
||||||
|
wrapper->isEOF = 1;
|
||||||
|
wrapper->errorMsg = "Could not skip requested number of bytes";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int InputStream_eof(void *user) {
|
||||||
|
JavaInputStreamWrapper* wrapper = (JavaInputStreamWrapper*) user;
|
||||||
|
LOGI("InputStream->eof() = %s", wrapper->isEOF ? "true" : "false");
|
||||||
|
return wrapper->isEOF;
|
||||||
|
}
|
||||||
|
|
||||||
|
static stbi_io_callbacks JavaInputStreamCallbacks ={
|
||||||
|
InputStream_read,
|
||||||
|
InputStream_skip,
|
||||||
|
InputStream_eof,
|
||||||
|
};
|
||||||
|
|
||||||
|
static JavaInputStreamWrapper createInputStreamWrapper(JNIEnv* env, jobject is, jbyteArray tmpArray)
|
||||||
|
{
|
||||||
|
JavaInputStreamWrapper wrapper;
|
||||||
|
jclass inputStreamClass = (*env)->FindClass(env, "java/io/InputStream");
|
||||||
|
|
||||||
|
wrapper.env = env;
|
||||||
|
wrapper.isObject = is;
|
||||||
|
wrapper.isEOF = 0;
|
||||||
|
wrapper.errorMsg = NULL;
|
||||||
|
wrapper.isReadMethod = (*env)->GetMethodID(env, inputStreamClass, "read", "([BII)I");
|
||||||
|
wrapper.isSkipMethod = (*env)->GetMethodID(env, inputStreamClass, "skip", "(J)J");
|
||||||
|
wrapper.tmp = (jbyteArray) tmpArray;
|
||||||
|
wrapper.tmpSize = (*env)->GetArrayLength(env, tmpArray);
|
||||||
|
|
||||||
|
return wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
static jobject createJmeImage(JNIEnv* env, int width, int height, int comps, char* data)
|
||||||
|
{
|
||||||
|
// Convert # of components to jME format.
|
||||||
|
jclass formatClass = (*env)->FindClass(env, "com/jme3/texture/Image$Format");
|
||||||
|
jfieldID formatFieldID;
|
||||||
|
|
||||||
|
switch (comps)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
formatFieldID = (*env)->GetStaticFieldID(env, formatClass,
|
||||||
|
"Luminance8", "Lcom/jme3/texture/Image$Format;");
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
formatFieldID = (*env)->GetStaticFieldID(env, formatClass,
|
||||||
|
"Luminance8Alpha8", "Lcom/jme3/texture/Image$Format;");
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
formatFieldID = (*env)->GetStaticFieldID(env, formatClass,
|
||||||
|
"RGB8", "Lcom/jme3/texture/Image$Format;");
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
formatFieldID = (*env)->GetStaticFieldID(env, formatClass,
|
||||||
|
"RGBA8", "Lcom/jme3/texture/Image$Format;");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throwIOException(env, "Unrecognized number of components");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
jobject formatVal = (*env)->GetStaticObjectField(env, formatClass, formatFieldID);
|
||||||
|
|
||||||
|
// Get colorspace sRGB
|
||||||
|
jclass colorSpaceClass = (*env)->FindClass(env, "com/jme3/texture/image/ColorSpace");
|
||||||
|
jfieldID sRGBFieldID = (*env)->GetStaticFieldID(env, colorSpaceClass,
|
||||||
|
"sRGB", "Lcom/jme3/texture/image/ColorSpace;");
|
||||||
|
jobject sRGBVal = (*env)->GetStaticObjectField(env, colorSpaceClass, sRGBFieldID);
|
||||||
|
|
||||||
|
int size = width * height * comps;
|
||||||
|
|
||||||
|
// Stick it in a ByteBuffer
|
||||||
|
jobject directBuffer = (*env)->NewDirectByteBuffer(env, data, size);
|
||||||
|
|
||||||
|
if (directBuffer == NULL)
|
||||||
|
{
|
||||||
|
throwIOException(env, "Failed to allocate ByteBuffer");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create JME image.
|
||||||
|
jclass jmeImageClass = (*env)->FindClass(env, "com/jme3/texture/Image");
|
||||||
|
|
||||||
|
// Image(Format format, int width, int height, ByteBuffer data, ColorSpace colorSpace)
|
||||||
|
jmethodID newImageMethod = (*env)->GetMethodID(env, jmeImageClass, "<init>",
|
||||||
|
"(Lcom/jme3/texture/Image$Format;IILjava/nio/ByteBuffer;Lcom/jme3/texture/image/ColorSpace;)V");
|
||||||
|
|
||||||
|
jobject jmeImage = (*env)->NewObject(env, jmeImageClass, newImageMethod,
|
||||||
|
formatVal, (jint)width, (jint)height,
|
||||||
|
directBuffer, sRGBVal);
|
||||||
|
|
||||||
|
return jmeImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void flipImage(int scanline, int height, char* data)
|
||||||
|
{
|
||||||
|
char tmp[scanline];
|
||||||
|
|
||||||
|
for (int y = 0; y < height / 2; y++)
|
||||||
|
{
|
||||||
|
int oppY = height - y - 1;
|
||||||
|
int yOff = y * scanline;
|
||||||
|
int oyOff = oppY * scanline;
|
||||||
|
// Copy scanline at Y to tmp
|
||||||
|
memcpy(tmp, &data[yOff], scanline);
|
||||||
|
// Copy data at opposite Y to Y
|
||||||
|
memcpy(&data[yOff], &data[oyOff], scanline);
|
||||||
|
// Copy tmp to opposite Y
|
||||||
|
memcpy(&data[oyOff], tmp, scanline);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jobject JNICALL Java_com_jme3_texture_plugins_AndroidNativeImageLoader_load
|
||||||
|
(JNIEnv * env, jobject thisObj, jobject inputStream, jboolean flipY, jbyteArray tmpArray)
|
||||||
|
{
|
||||||
|
JavaInputStreamWrapper wrapper = createInputStreamWrapper(env, inputStream, tmpArray);
|
||||||
|
stbi_uc* imageData;
|
||||||
|
int width, height, comps;
|
||||||
|
|
||||||
|
LOGI("stbi_load_from_callbacks");
|
||||||
|
|
||||||
|
imageData = stbi_load_from_callbacks(&JavaInputStreamCallbacks, &wrapper, &width, &height, &comps, STBI_default);
|
||||||
|
|
||||||
|
if ((*env)->ExceptionCheck(env))
|
||||||
|
{
|
||||||
|
// IOException
|
||||||
|
goto problems;
|
||||||
|
}
|
||||||
|
else if (wrapper.errorMsg != NULL)
|
||||||
|
{
|
||||||
|
// Misc error
|
||||||
|
throwIOException(env, wrapper.errorMsg);
|
||||||
|
goto problems;
|
||||||
|
}
|
||||||
|
else if (imageData == NULL)
|
||||||
|
{
|
||||||
|
// STBI error
|
||||||
|
throwIOException(env, stbi_failure_reason());
|
||||||
|
goto problems;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No IOExceptions or errors encountered. We have image data!
|
||||||
|
|
||||||
|
// Maybe we need to flip it.
|
||||||
|
LOGI("Flipping image");
|
||||||
|
if (flipY)
|
||||||
|
{
|
||||||
|
flipImage(width * comps, height, imageData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the jME3 image.
|
||||||
|
LOGI("Creating jME3 image");
|
||||||
|
return createJmeImage(env, width, height, comps, imageData);
|
||||||
|
|
||||||
|
problems:
|
||||||
|
if (imageData != NULL)
|
||||||
|
{
|
||||||
|
stbi_image_free(imageData);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jobject JNICALL Java_com_jme3_texture_plugins_AndroidNativeImageLoader_getFailureReason
|
||||||
|
(JNIEnv * env, jclass clazz)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jint JNICALL Java_com_jme3_texture_plugins_AndroidNativeImageLoader_getImageInfo
|
||||||
|
(JNIEnv * env, jclass clazz, jobject inBuffer, jint bufSize, jobject outBuffer, jint outSize)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jint JNICALL Java_com_jme3_texture_plugins_AndroidNativeImageLoader_decodeBuffer
|
||||||
|
(JNIEnv * env, jclass clazz, jobject inBuffer, jint inSize, jboolean flipY, jobject outBuffer, jint outSize)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
JNIEXPORT jobject JNICALL Java_com_jme3_texture_plugins_AndroidNativeImageLoader_getFailureReason
|
JNIEXPORT jobject JNICALL Java_com_jme3_texture_plugins_AndroidNativeImageLoader_getFailureReason
|
||||||
(JNIEnv * env, jclass clazz)
|
(JNIEnv * env, jclass clazz)
|
||||||
{
|
{
|
||||||
return stbi_failure_reason();
|
return stbi_failure_reason();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
JNIEXPORT jint JNICALL Java_com_jme3_texture_plugins_AndroidNativeImageLoader_getImageInfo
|
JNIEXPORT jint JNICALL Java_com_jme3_texture_plugins_AndroidNativeImageLoader_getImageInfo
|
||||||
(JNIEnv * env, jclass clazz, jobject inBuffer, jint bufSize, jobject outBuffer, jint outSize)
|
(JNIEnv * env, jclass clazz, jobject inBuffer, jint bufSize, jobject outBuffer, jint outSize)
|
||||||
{
|
{
|
||||||
@ -124,3 +431,4 @@ JNIEXPORT jint JNICALL Java_com_jme3_texture_plugins_AndroidNativeImageLoader_de
|
|||||||
|
|
||||||
// stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
|
// stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
@ -5,13 +5,8 @@ import com.jme3.asset.AssetLoadException;
|
|||||||
import com.jme3.asset.AssetLoader;
|
import com.jme3.asset.AssetLoader;
|
||||||
import com.jme3.asset.TextureKey;
|
import com.jme3.asset.TextureKey;
|
||||||
import com.jme3.texture.Image;
|
import com.jme3.texture.Image;
|
||||||
import com.jme3.texture.image.ColorSpace;
|
|
||||||
import com.jme3.util.BufferUtils;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -20,145 +15,28 @@ import java.util.logging.Logger;
|
|||||||
* loading. This loader does not.
|
* loading. This loader does not.
|
||||||
*
|
*
|
||||||
* @author iwgeric
|
* @author iwgeric
|
||||||
|
* @author Kirill Vainer
|
||||||
*/
|
*/
|
||||||
public class AndroidNativeImageLoader implements AssetLoader {
|
public class AndroidNativeImageLoader implements AssetLoader {
|
||||||
private static final Logger logger = Logger.getLogger(AndroidNativeImageLoader.class.getName());
|
|
||||||
|
|
||||||
public Image load(InputStream in, boolean flipY) throws IOException{
|
private final byte[] tmpArray = new byte[1024];
|
||||||
int result;
|
|
||||||
byte[] bytes = getBytes(in);
|
|
||||||
int origSize = bytes.length;
|
|
||||||
// logger.log(Level.INFO, "png file length: {0}", size);
|
|
||||||
|
|
||||||
ByteBuffer origDataBuffer = BufferUtils.createByteBuffer(origSize);
|
static {
|
||||||
origDataBuffer.clear();
|
System.loadLibrary("stbijme");
|
||||||
origDataBuffer.put(bytes, 0, origSize);
|
|
||||||
origDataBuffer.flip();
|
|
||||||
|
|
||||||
int headerSize = 12;
|
|
||||||
ByteBuffer headerDataBuffer = BufferUtils.createByteBuffer(headerSize);
|
|
||||||
headerDataBuffer.asIntBuffer();
|
|
||||||
headerDataBuffer.clear();
|
|
||||||
|
|
||||||
result = getImageInfo(origDataBuffer, origSize, headerDataBuffer, headerSize);
|
|
||||||
if (result != 0) {
|
|
||||||
logger.log(Level.SEVERE, "Image header could not be read: {0}", getFailureReason());
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
headerDataBuffer.rewind();
|
|
||||||
|
|
||||||
// logger.log(Level.INFO, "image header size: {0}", headerDataBuffer.capacity());
|
|
||||||
// int position = 0;
|
|
||||||
// while (headerDataBuffer.position() < headerDataBuffer.capacity()) {
|
|
||||||
// int value = headerDataBuffer.getInt();
|
|
||||||
// logger.log(Level.INFO, "position: {0}, value: {1}",
|
|
||||||
// new Object[]{position, value});
|
|
||||||
// position++;
|
|
||||||
// }
|
|
||||||
// headerDataBuffer.rewind();
|
|
||||||
|
|
||||||
|
|
||||||
int width = headerDataBuffer.getInt();
|
|
||||||
int height = headerDataBuffer.getInt();
|
|
||||||
int numComponents = headerDataBuffer.getInt();
|
|
||||||
int imageDataSize = width * height * numComponents;
|
|
||||||
// logger.log(Level.INFO, "width: {0}, height: {1}, numComponents: {2}, imageDataSize: {3}",
|
|
||||||
// new Object[]{width, height, numComponents, imageDataSize});
|
|
||||||
|
|
||||||
ByteBuffer imageDataBuffer = BufferUtils.createByteBuffer(imageDataSize);
|
|
||||||
imageDataBuffer.clear();
|
|
||||||
|
|
||||||
result = decodeBuffer(origDataBuffer, origSize, flipY, imageDataBuffer, imageDataSize);
|
|
||||||
if (result != 0) {
|
|
||||||
logger.log(Level.SEVERE, "Image could not be decoded: {0}", getFailureReason());
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
imageDataBuffer.rewind();
|
|
||||||
|
|
||||||
// logger.log(Level.INFO, "png outSize: {0}", imageDataBuffer.capacity());
|
|
||||||
// int pixelNum = 0;
|
|
||||||
// while (imageDataBuffer.position() < imageDataBuffer.capacity()) {
|
|
||||||
// short r = (short) (imageDataBuffer.get() & 0xFF);
|
|
||||||
// short g = (short) (imageDataBuffer.get() & 0xFF);
|
|
||||||
// short b = (short) (imageDataBuffer.get() & 0xFF);
|
|
||||||
// short a = (short) (imageDataBuffer.get() & 0xFF);
|
|
||||||
// logger.log(Level.INFO, "pixel: {0}, r: {1}, g: {2}, b: {3}, a: {4}",
|
|
||||||
// new Object[]{pixelNum, r, g, b, a});
|
|
||||||
// pixelNum++;
|
|
||||||
// }
|
|
||||||
// imageDataBuffer.rewind();
|
|
||||||
|
|
||||||
BufferUtils.destroyDirectBuffer(origDataBuffer);
|
|
||||||
BufferUtils.destroyDirectBuffer(headerDataBuffer);
|
|
||||||
|
|
||||||
Image img = new Image(getImageFormat(numComponents), width, height, imageDataBuffer, ColorSpace.sRGB);
|
|
||||||
|
|
||||||
return img;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static native Image load(InputStream in, boolean flipY, byte[] tmpArray) throws IOException;
|
||||||
|
|
||||||
public Image load(AssetInfo info) throws IOException {
|
public Image load(AssetInfo info) throws IOException {
|
||||||
// logger.log(Level.INFO, "Loading texture: {0}", ((TextureKey)info.getKey()).toString());
|
|
||||||
boolean flip = ((TextureKey) info.getKey()).isFlipY();
|
boolean flip = ((TextureKey) info.getKey()).isFlipY();
|
||||||
InputStream in = null;
|
InputStream in = null;
|
||||||
try {
|
try {
|
||||||
in = info.openStream();
|
in = info.openStream();
|
||||||
Image img = load(in, flip);
|
return load(info.openStream(), flip, tmpArray);
|
||||||
if (img == null){
|
|
||||||
throw new AssetLoadException("The given image cannot be loaded " + info.getKey());
|
|
||||||
}
|
|
||||||
return img;
|
|
||||||
} finally {
|
} finally {
|
||||||
if (in != null){
|
if (in != null){
|
||||||
in.close();
|
in.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Image.Format getImageFormat(int stbiNumComponents) {
|
|
||||||
// stb_image always returns 8 bit components
|
|
||||||
// N=#comp components
|
|
||||||
// 1 grey
|
|
||||||
// 2 grey, alpha
|
|
||||||
// 3 red, green, blue
|
|
||||||
// 4 red, green, blue, alpha
|
|
||||||
Image.Format format = null;
|
|
||||||
|
|
||||||
if (stbiNumComponents == 1) {
|
|
||||||
format = Image.Format.Luminance8;
|
|
||||||
} else if (stbiNumComponents == 2) {
|
|
||||||
format = Image.Format.Luminance8Alpha8;
|
|
||||||
} else if (stbiNumComponents == 3) {
|
|
||||||
format = Image.Format.RGB8;
|
|
||||||
} else if (stbiNumComponents == 4) {
|
|
||||||
format = Image.Format.RGBA8;
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException("Format returned by stbi is not valid. Returned value: " + stbiNumComponents);
|
|
||||||
}
|
|
||||||
|
|
||||||
return format;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static byte[] getBytes(InputStream input) throws IOException {
|
|
||||||
byte[] buffer = new byte[32768];
|
|
||||||
int bytesRead;
|
|
||||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
|
||||||
while ((bytesRead = input.read(buffer)) != -1)
|
|
||||||
{
|
|
||||||
os.write(buffer, 0, bytesRead);
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] output = os.toByteArray();
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** Load jni .so on initialization */
|
|
||||||
static {
|
|
||||||
System.loadLibrary("stbijme");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static native int getImageInfo(ByteBuffer inBuffer, int inSize, ByteBuffer outBuffer, int outSize);
|
|
||||||
private static native int decodeBuffer(ByteBuffer inBuffer, int inSize, boolean flipY, ByteBuffer outBuffer, int outSize);
|
|
||||||
private static native String getFailureReason();
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user