More android support

This commit is contained in:
rdb 2013-01-24 20:17:08 +00:00
parent 0631441b20
commit bc85e1a4f2
21 changed files with 1679 additions and 3 deletions

View File

@ -0,0 +1,49 @@
// Filename: NativeIStream.java
// Created by: rdb (22Jan13)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
package org.panda3d.android;
import java.io.InputStream;
////////////////////////////////////////////////////////////////////
// Class : NativeIStream
// Description : An implementation of InputStream that gets its
// data from a C++ istream pointer, passed as long.
////////////////////////////////////////////////////////////////////
public class NativeIStream extends InputStream {
private long streamPtr = 0;
public NativeIStream(long ptr) {
streamPtr = ptr;
}
@Override
public int read() {
return nativeGet(streamPtr);
}
@Override
public int read(byte[] buffer, int offset, int length) {
return nativeRead(streamPtr, buffer, offset, length);
}
@Override
public long skip(long n) {
return nativeIgnore(streamPtr, n);
}
private static native int nativeGet(long ptr);
private static native int nativeRead(long ptr, byte[] buffer, int offset, int length);
private static native long nativeIgnore(long ptr, long offset);
}

View File

@ -0,0 +1,58 @@
// Filename: PandaActivity.java
// Created by: rdb (22Jan13)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
package org.panda3d.android;
import android.app.NativeActivity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import org.panda3d.android.NativeIStream;
////////////////////////////////////////////////////////////////////
// Class : PandaActivity
// Description : The entry point for a Panda-based activity. Loads
// the Panda libraries and also provides some utility
// functions.
////////////////////////////////////////////////////////////////////
public class PandaActivity extends NativeActivity {
protected static BitmapFactory.Options readBitmapSize(long istreamPtr) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
options.inScaled = false;
NativeIStream stream = new NativeIStream(istreamPtr);
BitmapFactory.decodeStream(stream, null, options);
return options;
}
protected static Bitmap readBitmap(long istreamPtr, int sampleSize) {
BitmapFactory.Options options = new BitmapFactory.Options();
//options.inPreferredConfig = Bitmap.Config.RGBA_8888;
options.inScaled = false;
options.inSampleSize = sampleSize;
NativeIStream stream = new NativeIStream(istreamPtr);
return BitmapFactory.decodeStream(stream, null, options);
}
static {
System.loadLibrary("gnustl_shared");
System.loadLibrary("p3dtool");
System.loadLibrary("p3dtoolconfig");
System.loadLibrary("pandaexpress");
System.loadLibrary("panda");
System.loadLibrary("p3android");
System.loadLibrary("p3framework");
System.loadLibrary("pandaegg");
System.loadLibrary("pandagles");
}
}

View File

@ -0,0 +1,76 @@
// Filename: android_main.cxx
// Created by: rdb (12Jan13)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#include "config_android.h"
#include "config_util.h"
#include "virtualFileMountAndroidAsset.h"
#include "virtualFileSystem.h"
#include "config_display.h"
//#define OPENGLES_1
//#include "config_androiddisplay.h"
#include <android_native_app_glue.h>
//struct android_app* panda_android_app = NULL;
extern int main(int argc, char **argv);
////////////////////////////////////////////////////////////////////
// Function: android_main
// Description: This function is called by native_app_glue to
// initialize the program. It simply stores the
// android_app object and calls main() normally.
////////////////////////////////////////////////////////////////////
void android_main(struct android_app* app) {
panda_android_app = app;
// Attach the current thread to the JVM.
JNIEnv *env;
ANativeActivity* activity = app->activity;
int status = activity->vm->AttachCurrentThread(&env, NULL);
if (status < 0 || env == NULL) {
android_cat.error() << "Failed to attach thread to JVM!\n";
return;
}
// Get the path to the APK.
jclass clazz = env->GetObjectClass(activity->clazz);
jmethodID methodID = env->GetMethodID(clazz, "getPackageCodePath", "()Ljava/lang/String;");
jstring code_path = (jstring) env->CallObjectMethod(activity->clazz, methodID);
const char* apk_path;
apk_path = env->GetStringUTFChars(code_path, NULL);
android_cat.info() << "Path to APK: " << apk_path << "\n";
// Mount the assets directory.
PT(VirtualFileMountAndroidAsset) asset_mount;
asset_mount = new VirtualFileMountAndroidAsset(app->activity->assetManager, apk_path);
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
vfs->mount(asset_mount, "/android_asset", 0);
// Release the apk_path.
env->ReleaseStringUTFChars(code_path, apk_path);
// Now add the asset directory to the model-path.
get_model_path().append_directory("/android_asset");
// Create bogus argc and argv, then call our main function.
char *argv[] = {NULL};
int argc = 0;
main(argc, argv);
// Detach the thread before exiting.
activity->vm->DetachCurrentThread();
}

View File

@ -0,0 +1,88 @@
// Filename: config_android.cxx
// Created by: rdb (12Jan13)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#include "config_android.h"
#include "pnmFileTypeAndroid.h"
#include "pnmFileTypeRegistry.h"
#include "dconfig.h"
#include "pandaSystem.h"
NotifyCategoryDef(android, "");
struct android_app *panda_android_app = NULL;
jclass jni_PandaActivity;
jmethodID jni_PandaActivity_readBitmapSize;
jmethodID jni_PandaActivity_readBitmap;
jclass jni_BitmapFactory_Options;
jfieldID jni_BitmapFactory_Options_outWidth;
jfieldID jni_BitmapFactory_Options_outHeight;
////////////////////////////////////////////////////////////////////
// Function: init_libandroid
// Description: Initializes the library. This must be called at
// least once before any of the functions or classes
// in this library can be used. Normally, this is
// called by JNI_OnLoad.
////////////////////////////////////////////////////////////////////
void
init_libandroid() {
PNMFileTypeRegistry *tr = PNMFileTypeRegistry::get_global_ptr();
PNMFileTypeAndroid::init_type();
PNMFileTypeAndroid::register_with_read_factory();
tr->register_type(new PNMFileTypeAndroid);
}
////////////////////////////////////////////////////////////////////
// Function: JNI_OnLoad
// Description: Called by Java when loading this library.
// Initializes the global class references and the
// method IDs.
////////////////////////////////////////////////////////////////////
jint JNI_OnLoad(JavaVM *jvm, void *reserved) {
init_libandroid();
JNIEnv *env = get_jni_env();
assert(env != NULL);
jni_PandaActivity = env->FindClass("org/panda3d/android/PandaActivity");
jni_PandaActivity = (jclass) env->NewGlobalRef(jni_PandaActivity);
jni_PandaActivity_readBitmapSize = env->GetStaticMethodID(jni_PandaActivity,
"readBitmapSize", "(J)Landroid/graphics/BitmapFactory$Options;");
jni_PandaActivity_readBitmap = env->GetStaticMethodID(jni_PandaActivity,
"readBitmap", "(JI)Landroid/graphics/Bitmap;");
jni_BitmapFactory_Options = env->FindClass("android/graphics/BitmapFactory$Options");
jni_BitmapFactory_Options = (jclass) env->NewGlobalRef(jni_BitmapFactory_Options);
jni_BitmapFactory_Options_outWidth = env->GetFieldID(jni_BitmapFactory_Options, "outWidth", "I");
jni_BitmapFactory_Options_outHeight = env->GetFieldID(jni_BitmapFactory_Options, "outHeight", "I");
return JNI_VERSION_1_4;
}
////////////////////////////////////////////////////////////////////
// Function: JNI_OnUnload
// Description: Called by Java when unloading this library.
// Destroys the global class references.
////////////////////////////////////////////////////////////////////
void JNI_OnUnload(JavaVM *jvm, void *reserved) {
JNIEnv *env = get_jni_env();
env->DeleteGlobalRef(jni_PandaActivity);
env->DeleteGlobalRef(jni_BitmapFactory_Options);
}

View File

@ -0,0 +1,39 @@
// Filename: config_android.h
// Created by: rdb (12Jan13)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#ifndef CONFIG_ANDROID_H
#define CONFIG_ANDROID_H
#include "pandabase.h"
#include "notifyCategoryProxy.h"
#include "configVariableString.h"
#include "configVariableBool.h"
#include "configVariableInt.h"
#include <jni.h>
NotifyCategoryDeclNoExport(android);
extern void init_libandroid();
extern struct android_app* panda_android_app;
extern jclass jni_PandaActivity;
extern jmethodID jni_PandaActivity_readBitmapHeader;
extern jmethodID jni_PandaActivity_readBitmap;
extern jclass jni_BitmapFactory_Options;
extern jfieldID jni_BitmapFactory_Options_outWidth;
extern jfieldID jni_BitmapFactory_Options_outHeight;
#endif

View File

@ -0,0 +1,71 @@
// Filename: jni_NativeIStream.cxx
// Created by: rdb (22Jan13)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#include <jni.h>
#include <istream>
////////////////////////////////////////////////////////////////////
// Function: NativeIStream::nativeGet
// Access: Private, Static
// Description: Reads a single character from the istream.
// Should return -1 on EOF.
////////////////////////////////////////////////////////////////////
extern "C" jint
Java_org_panda3d_android_NativeIStream_nativeGet(JNIEnv *env, jclass clazz, jlong ptr) {
std::istream *stream = (std::istream *) ptr;
int ch = stream->get();
return stream->good() ? ch : -1;
}
////////////////////////////////////////////////////////////////////
// Function: NativeIStream::nativeRead
// Access: Private, Static
// Description: Reads an array of bytes from the istream. Returns
// the actual number of bytes that were read.
// Should return -1 on EOF.
////////////////////////////////////////////////////////////////////
extern "C" jint
Java_org_panda3d_android_NativeIStream_nativeRead(JNIEnv *env, jclass clazz, jlong ptr, jbyteArray byte_array, jint offset, jint length) {
std::istream *stream = (std::istream *) ptr;
jbyte *buffer = (jbyte *) env->GetPrimitiveArrayCritical(byte_array, NULL);
if (buffer == NULL) {
return -1;
}
stream->read((char*) buffer + offset, length);
env->ReleasePrimitiveArrayCritical(byte_array, buffer, 0);
// We have to return -1 on EOF, otherwise it will keep trying to read.
size_t count = stream->gcount();
if (count == 0 && stream->eof()) {
return -1;
} else {
return count;
}
}
////////////////////////////////////////////////////////////////////
// Function: NativeIStream::nativeIgnore
// Access: Private, Static
// Description: Skips ahead N bytes in the stream. Returns the
// actual number of skipped bytes.
////////////////////////////////////////////////////////////////////
extern "C" jlong
Java_org_panda3d_android_NativeIStream_nativeIgnore(JNIEnv *env, jclass clazz, jlong ptr, jlong offset) {
std::istream *stream = (std::istream *) ptr;
stream->ignore(offset);
return stream->gcount();
}

View File

@ -0,0 +1,4 @@
#include "config_android.cxx"
#include "jni_NativeIStream.cxx"
#include "pnmFileTypeAndroid.cxx"
#include "pnmFileTypeAndroidReader.cxx"

View File

@ -0,0 +1,127 @@
// Filename: pnmFileTypeAndroid.cxx
// Created by: rdb (11Jan13)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#include "pnmFileTypeAndroid.h"
#ifdef ANDROID
#include "config_pnmimagetypes.h"
#include "pnmFileTypeRegistry.h"
#include "bamReader.h"
static const char * const extensions_android[] = {
"jpg", "jpeg", "gif", "png",//"webp" (android 4.0+)
};
static const int num_extensions_android = sizeof(extensions_android) / sizeof(const char *);
TypeHandle PNMFileTypeAndroid::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: PNMFileTypeAndroid::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
PNMFileTypeAndroid::
PNMFileTypeAndroid() {
}
////////////////////////////////////////////////////////////////////
// Function: PNMFileTypeAndroid::get_name
// Access: Public, Virtual
// Description: Returns a few words describing the file type.
////////////////////////////////////////////////////////////////////
string PNMFileTypeAndroid::
get_name() const {
return "Android Bitmap";
}
////////////////////////////////////////////////////////////////////
// Function: PNMFileTypeAndroid::get_num_extensions
// Access: Public, Virtual
// Description: Returns the number of different possible filename
// extensions associated with this particular file type.
////////////////////////////////////////////////////////////////////
int PNMFileTypeAndroid::
get_num_extensions() const {
return num_extensions_android;
}
////////////////////////////////////////////////////////////////////
// Function: PNMFileTypeAndroid::get_extension
// Access: Public, Virtual
// Description: Returns the nth possible filename extension
// associated with this particular file type, without a
// leading dot.
////////////////////////////////////////////////////////////////////
string PNMFileTypeAndroid::
get_extension(int n) const {
nassertr(n >= 0 && n < num_extensions_android, string());
return extensions_android[n];
}
////////////////////////////////////////////////////////////////////
// Function: PNMFileTypeAndroid::has_magic_number
// Access: Public, Virtual
// Description: Returns true if this particular file type uses a
// magic number to identify it, false otherwise.
////////////////////////////////////////////////////////////////////
bool PNMFileTypeAndroid::
has_magic_number() const {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: PNMFileTypeAndroid::make_reader
// Access: Public, Virtual
// Description: Allocates and returns a new PNMReader suitable for
// reading from this file type, if possible. If reading
// from this file type is not supported, returns NULL.
////////////////////////////////////////////////////////////////////
PNMReader *PNMFileTypeAndroid::
make_reader(istream *file, bool owns_file, const string &magic_number) {
init_pnm();
return new Reader(this, file, owns_file, magic_number);
}
////////////////////////////////////////////////////////////////////
// Function: PNMFileTypeAndroid::register_with_read_factory
// Access: Public, Static
// Description: Registers the current object as something that can be
// read from a Bam file.
////////////////////////////////////////////////////////////////////
void PNMFileTypeAndroid::
register_with_read_factory() {
BamReader::get_factory()->
register_factory(get_class_type(), make_PNMFileTypeAndroid);
}
////////////////////////////////////////////////////////////////////
// Function: PNMFileTypeAndroid::make_PNMFileTypeAndroid
// Access: Protected, Static
// Description: This method is called by the BamReader when an object
// of this type is encountered in a Bam file; it should
// allocate and return a new object with all the data
// read.
//
// In the case of the PNMFileType objects, since these
// objects are all shared, we just pull the object from
// the registry.
////////////////////////////////////////////////////////////////////
TypedWritable *PNMFileTypeAndroid::
make_PNMFileTypeAndroid(const FactoryParams &params) {
return PNMFileTypeRegistry::get_global_ptr()->get_type_by_handle(get_class_type());
}
#endif // ANDROID

View File

@ -0,0 +1,93 @@
// Filename: pnmFileTypeAndroid.h
// Created by: rdb (11Jan13)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#ifndef PNMFILETYPEANDROID_H
#define PNMFILETYPEANDROID_H
#ifdef ANDROID
#include "pandabase.h"
#include "pnmFileType.h"
#include "pnmReader.h"
#include "pnmWriter.h"
#include <jni.h>
////////////////////////////////////////////////////////////////////
// Class : PNMFileTypeAndroid
// Description : Wrapper class around the Android Bitmap mechanism
// to allow loading images on Android without needing
// libpng or libjpeg.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA_PNMIMAGETYPES PNMFileTypeAndroid : public PNMFileType {
public:
PNMFileTypeAndroid();
virtual string get_name() const;
virtual int get_num_extensions() const;
virtual string get_extension(int n) const;
virtual bool has_magic_number() const;
virtual PNMReader *make_reader(istream *file, bool owns_file = true,
const string &magic_number = string());
public:
class Reader : public PNMReader {
public:
Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number);
virtual ~Reader();
virtual void prepare_read();
virtual int read_data(xel *array, xelval *alpha);
private:
// It is assumed that the Reader is only used within a single thread.
JNIEnv *_env;
jobject _bitmap;
int _sample_size;
uint32_t _stride;
int32_t _format;
};
// The TypedWritable interface follows.
public:
static void register_with_read_factory();
protected:
static TypedWritable *make_PNMFileTypeAndroid(const FactoryParams &params);
public:
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
PNMFileType::init_type();
register_type(_type_handle, "PNMFileTypeAndroid",
PNMFileType::get_class_type());
}
virtual TypeHandle get_type() const {
return get_class_type();
}
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
private:
static TypeHandle _type_handle;
};
#endif // ANDROID
#endif

View File

@ -0,0 +1,303 @@
// Filename: pnmFileTypeAndroidReader.cxx
// Created by: rdb (22Jan13)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#include "pnmFileTypeAndroid.h"
#ifdef ANDROID
#include "config_pnmimagetypes.h"
#include "config_express.h"
#include <android/bitmap.h>
#include <jni.h>
// These tables linearly map 4-bit, 5-bit or 6-bit to 8-bit values.
static uint8_t scale_table_4[16];
static uint8_t scale_table_5[32];
static uint8_t scale_table_6[64];
static void init_scale_tables() {
static bool initialized = false;
if (!initialized) {
int i;
for (i = 0; i < 16; ++i) {
scale_table_4[i] = 255 * i / 15;
}
for (i = 0; i < 32; ++i) {
scale_table_5[i] = 255 * i / 31;
}
for (i = 0; i < 64; ++i) {
scale_table_6[i] = 255 * i / 63;
}
initialized = true;
}
}
static void conv_rgb565(uint16_t in, xel &out) {
out.r = scale_table_5[(in >> 11) & 31];
out.g = scale_table_6[(in >> 5) & 63];
out.b = scale_table_5[in & 31];
}
static void conv_rgba4444(uint16_t in, xel &rgb, xelval &alpha) {
rgb.r = scale_table_4[(in >> 12) & 0xF];
rgb.g = scale_table_4[(in >> 8) & 0xF];
rgb.b = scale_table_4[(in >> 4) & 0xF];
alpha = scale_table_4[in & 0xF];
}
////////////////////////////////////////////////////////////////////
// Function: PNMFileTypeAndroid::Reader::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
PNMFileTypeAndroid::Reader::
Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number) :
PNMReader(type, file, owns_file), _bitmap(NULL)
{
// Hope we can putback() more than one character.
for (string::reverse_iterator mi = magic_number.rbegin();
mi != magic_number.rend(); ++mi) {
_file->putback(*mi);
};
if (_file->fail()) {
android_cat.error()
<< "Unable to put back magic number.\n";
_is_valid = false;
return;
}
streampos pos = _file->tellg();
_env = get_jni_env();
jobject opts = _env->CallStaticObjectMethod(jni_PandaActivity,
jni_PandaActivity_readBitmapSize,
(jlong) _file);
_file->seekg(pos);
if (_file->tellg() != pos) {
android_cat.error()
<< "Unable to seek back to beginning.\n";
_is_valid = false;
return;
}
_x_size = _env->GetIntField(opts, jni_BitmapFactory_Options_outWidth);
_y_size = _env->GetIntField(opts, jni_BitmapFactory_Options_outHeight);
if (_x_size < 0 || _y_size < 0) {
android_cat.error()
<< "Failed to read header of " << *this << "\n";
_is_valid = false;
}
// Apparently we have to know this even though we don't yet.
_num_channels = 4;
_maxval = 255;
if (android_cat.is_debug()) {
android_cat.debug()
<< "Reading " << *this << "\n";
}
}
////////////////////////////////////////////////////////////////////
// Function: PNMFileTypeAndroid::Reader::Destructor
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
PNMFileTypeAndroid::Reader::
~Reader() {
if (_bitmap != NULL) {
_env->DeleteGlobalRef(_bitmap);
}
}
////////////////////////////////////////////////////////////////////
// Function: PNMFileTypeAndroid::Reader::prepare_read
// Access: Public, Virtual
// Description: This method will be called before read_data() or
// read_row() is called. It instructs the reader to
// initialize its data structures as necessary to
// actually perform the read operation.
//
// After this call, _x_size and _y_size should reflect
// the actual size that will be filled by read_data()
// (as possibly modified by set_read_size()).
////////////////////////////////////////////////////////////////////
void PNMFileTypeAndroid::Reader::
prepare_read() {
_sample_size = 2;
_orig_x_size = _x_size;
_orig_y_size = _y_size;
if (_has_read_size && _read_x_size != 0 && _read_y_size != 0) {
int x_reduction = _orig_x_size / _read_x_size;
int y_reduction = _orig_y_size / _read_y_size;
_sample_size = max(min(x_reduction, y_reduction), 1);
}
_bitmap = _env->CallStaticObjectMethod(jni_PandaActivity,
jni_PandaActivity_readBitmap,
(jlong) _file, _sample_size);
if (_bitmap == NULL) {
android_cat.error()
<< "Failed to read " << *this << "\n";
_is_valid = false;
return;
}
_bitmap = _env->NewGlobalRef(_bitmap);
AndroidBitmapInfo info;
if (AndroidBitmap_getInfo(_env, _bitmap, &info) < 0) {
android_cat.error()
<< "Failed to get info of " << *this << "\n";
_is_valid = false;
return;
}
_x_size = info.width;
_y_size = info.height;
_format = info.format;
_stride = info.stride;
// Note: we could be setting maxval more appropriately,
// but this only causes texture.cxx to end up rescaling it later.
// Best to do the scaling ourselves, using efficient tables.
_maxval = 255;
switch (info.format) {
case ANDROID_BITMAP_FORMAT_RGBA_8888:
_num_channels = 4;
android_cat.debug()
<< "Bitmap has format RGBA_8888\n";
break;
case ANDROID_BITMAP_FORMAT_RGB_565:
_num_channels = 3;
android_cat.debug()
<< "Bitmap has format RGB_565\n";
break;
case ANDROID_BITMAP_FORMAT_RGBA_4444:
_num_channels = 4;
android_cat.debug()
<< "Bitmap has format RGBA_4444\n";
break;
case ANDROID_BITMAP_FORMAT_A_8:
_num_channels = 1;
android_cat.debug()
<< "Bitmap has format A_8\n";
break;
default:
android_cat.error()
<< "Unsupported bitmap format!\n";
_num_channels = 0;
_is_valid = false;
break;
}
}
////////////////////////////////////////////////////////////////////
// Function: PNMFileTypeAndroid::Reader::read_data
// Access: Public, Virtual
// Description: Reads in an entire image all at once, storing it in
// the pre-allocated _x_size * _y_size array and alpha
// pointers. (If the image type has no alpha channel,
// alpha is ignored.) Returns the number of rows
// correctly read.
//
// Derived classes need not override this if they
// instead provide supports_read_row() and read_row(),
// below.
////////////////////////////////////////////////////////////////////
int PNMFileTypeAndroid::Reader::
read_data(xel *rgb, xelval *alpha) {
if (!_is_valid) {
return 0;
}
void *ptr;
if (AndroidBitmap_lockPixels(_env, _bitmap, &ptr) < 0) {
android_cat.error()
<< "Failed to lock bitmap for reading.\n";
return 0;
}
switch (_format) {
case ANDROID_BITMAP_FORMAT_RGBA_8888: {
nassertr(_stride == _x_size * 4, 0);
uint8_t *data = (uint8_t *) ptr;
for (int y = 0; y < _y_size; ++y) {
for (int x = 0; x < _x_size; ++x) {
rgb[x].r = data[0];
rgb[x].g = data[1];
rgb[x].b = data[2];
alpha[x] = data[3];
data += 4;
}
rgb += _x_size;
alpha += _y_size;
}
break;
}
case ANDROID_BITMAP_FORMAT_RGB_565: {
nassertr(_stride == _x_size * 2, 0);
init_scale_tables();
uint16_t *data = (uint16_t *) ptr;
for (int y = 0; y < _y_size; ++y) {
for (int x = 0; x < _x_size; ++x) {
conv_rgb565(data[x], rgb[x]);
}
data += _x_size;
rgb += _x_size;
}
break;
}
case ANDROID_BITMAP_FORMAT_RGBA_4444: {
nassertr(_stride == _x_size * 2, 0);
init_scale_tables();
uint16_t *data = (uint16_t *) ptr;
for (int y = 0; y < _y_size; ++y) {
for (int x = 0; x < _x_size; ++x) {
conv_rgba4444(data[x], rgb[x], alpha[x]);
}
data += _x_size;
rgb += _x_size;
alpha += _x_size;
}
break;
}
case ANDROID_BITMAP_FORMAT_A_8: {
nassertr(_stride == _x_size, 0);
uint8_t *data = (uint8_t *) ptr;
for (int y = 0; y < _y_size; ++y) {
for (int x = 0; x < _x_size; ++x) {
alpha[x] = data[x];
}
data += _x_size;
alpha += _x_size;
}
break;
}
default:
AndroidBitmap_unlockPixels(_env, _bitmap);
return 0;
}
AndroidBitmap_unlockPixels(_env, _bitmap);
return _y_size;
}
#endif // ANDROID

100
panda/src/android/pview.cxx Normal file
View File

@ -0,0 +1,100 @@
// Filename: pview.cxx
// Created by: rdb (12Jan13)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#include "pandaFramework.h"
#include "pandaSystem.h"
#include "pystub.h"
#include "texturePool.h"
#include "multitexReducer.h"
#include "sceneGraphReducer.h"
#include "partGroup.h"
#include "cardMaker.h"
#include "bamCache.h"
#include "virtualFileSystem.h"
// By including checkPandaVersion.h, we guarantee that runtime
// attempts to run pview will fail if it inadvertently links with the
// wrong version of libdtool.so/.dll.
#include "checkPandaVersion.h"
int main(int argc, char **argv) {
// A call to pystub() to force libpystub.so to be linked in.
pystub();
PandaFramework framework;
framework.open_framework(argc, argv);
framework.set_window_title("Panda Viewer");
int hierarchy_match_flags = PartGroup::HMF_ok_part_extra |
PartGroup::HMF_ok_anim_extra;
WindowFramework *window = framework.open_window();
if (window != (WindowFramework *)NULL) {
// We've successfully opened a window.
NodePath loading_np;
if (true) {
// Put up a "loading" message for the user's benefit.
NodePath aspect_2d = window->get_aspect_2d();
PT(TextNode) loading = new TextNode("loading");
loading_np = aspect_2d.attach_new_node(loading);
loading_np.set_scale(0.125f);
loading->set_text_color(1.0f, 1.0f, 1.0f, 1.0f);
loading->set_shadow_color(0.0f, 0.0f, 0.0f, 1.0f);
loading->set_shadow(0.04, 0.04);
loading->set_align(TextNode::A_center);
loading->set_text("Loading...");
// Allow a couple of frames to go by so the window will be fully
// created and the text will be visible.
Thread *current_thread = Thread::get_current_thread();
framework.do_frame(current_thread);
framework.do_frame(current_thread);
}
window->enable_keyboard();
window->setup_trackball();
framework.get_models().instance_to(window->get_render());
//if (argc < 2) {
// If we have no arguments, get that trusty old triangle out.
//window->load_default_model(framework.get_models());
//} else {
// window->load_models(framework.get_models(), argc, argv);
//}
window->load_model(framework.get_models(), "panda-model.egg");
window->load_model(framework.get_models(), "panda-walk4.egg");
window->loop_animations(hierarchy_match_flags);
// Make sure the textures are preloaded.
framework.get_models().prepare_scene(window->get_graphics_output()->get_gsg());
loading_np.remove_node();
window->center_trackball(framework.get_models());
window->set_anim_controls(true);
framework.enable_default_keys();
framework.main_loop();
framework.report_frame_rate(nout);
} else {
assert(false);
}
framework.close_framework();
return (0);
}

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- BEGIN_INCLUDE(manifest) -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.panda3d.sdk"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="9" />
<application android:label="Panda Viewer" android:hasCode="true">
<activity android:name="org.panda3d.android.PandaActivity"
android:label="Panda Viewer" android:theme="@android:style/Theme.NoTitleBar"
android:configChanges="orientation|keyboardHidden">
<meta-data android:name="android.app.lib_name"
android:value="pview" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
<!-- END_INCLUDE(manifest) -->

View File

@ -566,8 +566,8 @@ handle_motion_event(const AInputEvent *event) {
_input_devices[0].button_up(MouseButton::one());
}
float x = AMotionEvent_getX(event, 0);
float y = AMotionEvent_getY(event, 0);
float x = AMotionEvent_getX(event, 0) - _app->contentRect.left;
float y = AMotionEvent_getY(event, 0) - _app->contentRect.top;
_input_devices[0].set_pointer_in_window(x, y);

View File

@ -22,6 +22,7 @@
#include "virtualFile.h"
#include "virtualFileComposite.h"
#include "virtualFileMount.h"
#include "virtualFileMountAndroidAsset.h"
#include "virtualFileMountMultifile.h"
#include "virtualFileMountRamdisk.h"
#include "virtualFileMountSystem.h"
@ -106,6 +107,9 @@ init_libexpress() {
VirtualFile::init_type();
VirtualFileComposite::init_type();
VirtualFileMount::init_type();
#ifdef ANDROID
VirtualFileMountAndroidAsset::init_type();
#endif
VirtualFileMountMultifile::init_type();
VirtualFileMountRamdisk::init_type();
VirtualFileMountSystem::init_type();
@ -191,3 +195,44 @@ get_config_express() {
static DConfig config_express;
return config_express;
}
#ifdef ANDROID
static JavaVM *panda_jvm = NULL;
////////////////////////////////////////////////////////////////////
// Function: JNI_OnLoad
// Description: Called by Java when loading this library.
////////////////////////////////////////////////////////////////////
jint JNI_OnLoad(JavaVM *jvm, void *reserved) {
panda_jvm = jvm;
return JNI_VERSION_1_4;
}
////////////////////////////////////////////////////////////////////
// Function: get_java_vm
// Description: Returns a pointer to the JavaVM object.
////////////////////////////////////////////////////////////////////
JavaVM *get_java_vm() {
nassertr(panda_jvm != NULL, NULL);
return panda_jvm;
}
////////////////////////////////////////////////////////////////////
// Function: get_jni_env
// Description: Returns a JNIEnv object for the current thread.
// If it doesn't already exist, attaches the JVM
// to this thread.
////////////////////////////////////////////////////////////////////
JNIEnv *get_jni_env() {
nassertr(panda_jvm != NULL, NULL);
JNIEnv *env = NULL;
int status = panda_jvm->GetEnv((void**) &env, JNI_VERSION_1_4);
if (status < 0 || env == NULL) {
express_cat.error() << "JVM is not available in this thread!\n";
return NULL;
}
return env;
}
#endif

View File

@ -28,6 +28,10 @@
// Include this so interrogate can find it.
#include "executionEnvironment.h"
#ifdef ANDROID
#include <jni.h>
#endif
ConfigureDecl(config_express, EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS);
NotifyCategoryDecl(express, EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS);
NotifyCategoryDecl(clock, EXPCL_PANDAEXPRESS, EXPTP_PANDAEXPRESS);
@ -62,4 +66,9 @@ END_PUBLISH
extern EXPCL_PANDAEXPRESS void init_libexpress();
#ifdef ANDROID
extern EXPCL_PANDAEXPRESS JavaVM *get_java_vm();
extern EXPCL_PANDAEXPRESS JNIEnv *get_jni_env();
#endif
#endif /* __CONFIG_UTIL_H__ */

View File

@ -19,6 +19,7 @@
#include "virtualFileComposite.cxx"
#include "virtualFileList.cxx"
#include "virtualFileMount.cxx"
#include "virtualFileMountAndroidAsset.cxx"
#include "virtualFileMountMultifile.cxx"
#include "virtualFileMountRamdisk.cxx"
#include "virtualFileMountSystem.cxx"

View File

@ -0,0 +1,35 @@
// Filename: virtualFileMountAndroidAsset.cxx
// Created by: rdb (21Jan13)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountAndroidAsset::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
VirtualFileMountAndroidAsset::
VirtualFileMountAndroidAsset(AAssetManager *mgr, const string &apk_path) :
_asset_mgr(mgr), _apk_path(apk_path)
{
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountAndroidAsset::AssetStream::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE VirtualFileMountAndroidAsset::AssetStream::
AssetStream(AAsset *asset) :
istream(new AssetStreamBuf(asset)) {
}

View File

@ -0,0 +1,420 @@
// Filename: virtualFileMountAndroidAsset.cxx
// Created by: rdb (21Jan13)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#ifdef ANDROID
#include "virtualFileMountAndroidAsset.h"
#include "virtualFileSystem.h"
#ifndef NDEBUG
#include <sys/stat.h>
#endif
TypeHandle VirtualFileMountAndroidAsset::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountAndroidAsset::Destructor
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
VirtualFileMountAndroidAsset::
~VirtualFileMountAndroidAsset() {
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountAndroidAsset::get_fd
// Access: Public
// Description: Returns a file descriptor that can be used to read
// the asset if it was stored uncompressed and
// unencrypted. Returns a valid fd or -1.
////////////////////////////////////////////////////////////////////
int VirtualFileMountAndroidAsset::
get_fd(const Filename &file, off_t *start, off_t *length) const {
AAsset* asset;
asset = AAssetManager_open(_asset_mgr, file.c_str(), AASSET_MODE_UNKNOWN);
int fd = AAsset_openFileDescriptor(asset, start, length);
AAsset_close(asset);
return fd;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountAndroidAsset::has_file
// Access: Public, Virtual
// Description: Returns true if the indicated file exists within the
// mount system.
////////////////////////////////////////////////////////////////////
bool VirtualFileMountAndroidAsset::
has_file(const Filename &file) const {
return (file.empty() || is_directory(file) || is_regular_file(file));
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountAndroidAsset::is_directory
// Access: Public, Virtual
// Description: Returns true if the indicated file exists within the
// mount system and is a directory.
////////////////////////////////////////////////////////////////////
bool VirtualFileMountAndroidAsset::
is_directory(const Filename &file) const {
// This is the only way - AAssetManager_openDir also works for files.
//AAssetDir *dir = AAssetManager_openDir(_asset_mgr, file.c_str());
//express_cat.error() << "is_directory " << file << " - " << dir << "\n";
//if (dir == NULL) {
// return false;
//}
//AAssetDir_close(dir);
// openDir doesn't return NULL for ordinary files!
return !is_regular_file(file);
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountAndroidAsset::is_regular_file
// Access: Public, Virtual
// Description: Returns true if the indicated file exists within the
// mount system and is a regular file.
////////////////////////////////////////////////////////////////////
bool VirtualFileMountAndroidAsset::
is_regular_file(const Filename &file) const {
// I'm afraid the only way to see if it exists is to try and open it.
AAsset* asset;
asset = AAssetManager_open(_asset_mgr, file.c_str(), AASSET_MODE_UNKNOWN);
express_cat.error() << "is_regular_file " << file << " - " << asset << "\n";
if (asset == NULL) {
return false;
}
AAsset_close(asset);
return true;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountAndroidAsset::read_file
// Access: Public, Virtual
// Description: Fills up the indicated pvector with the contents of
// the file, if it is a regular file. Returns true on
// success, false otherwise.
////////////////////////////////////////////////////////////////////
bool VirtualFileMountAndroidAsset::
read_file(const Filename &file, bool do_uncompress,
pvector<unsigned char> &result) const {
if (do_uncompress) {
// If the file is to be decompressed, we'd better just use the
// higher-level implementation, which includes support for
// on-the-fly decompression.
return VirtualFileMount::read_file(file, do_uncompress, result);
}
// But if we're just reading a straight file, let's just read
// it here to avoid all of the streambuf nonsense.
result.clear();
AAsset* asset;
asset = AAssetManager_open(_asset_mgr, file.c_str(), AASSET_MODE_STREAMING);
if (asset == (AAsset *)NULL) {
express_cat.info()
<< "Unable to read " << file << "\n";
}
// Reserve enough space to hold the entire file.
off_t file_size = AAsset_getLength(asset);
if (file_size == 0) {
return true;
} else if (file_size > 0) {
result.reserve((size_t)file_size);
}
static const size_t buffer_size = 4096;
char buffer[buffer_size];
int count = AAsset_read(asset, buffer, buffer_size);
while (count > 0) {
thread_consider_yield();
result.insert(result.end(), buffer, buffer + count);
count = AAsset_read(asset, buffer, buffer_size);
}
AAsset_close(asset);
return (count == 0);
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountAndroidAsset::open_read_file
// Access: Public, Virtual
// Description: Opens the file for reading, if it exists. Returns a
// newly allocated istream on success (which you should
// eventually delete when you are done reading).
// Returns NULL on failure.
////////////////////////////////////////////////////////////////////
istream *VirtualFileMountAndroidAsset::
open_read_file(const Filename &file) const {
AAsset* asset;
asset = AAssetManager_open(_asset_mgr, file.c_str(), AASSET_MODE_UNKNOWN);
if (asset == (AAsset *)NULL) {
return NULL;
}
AssetStream *stream = new AssetStream(asset);
return (istream *) stream;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountAndroidAsset::get_file_size
// Access: Published, Virtual
// Description: Returns the current size on disk (or wherever it is)
// of the already-open file. Pass in the stream that
// was returned by open_read_file(); some
// implementations may require this stream to determine
// the size.
////////////////////////////////////////////////////////////////////
off_t VirtualFileMountAndroidAsset::
get_file_size(const Filename &file, istream *in) const {
// If it's already open, get the AAsset pointer from the streambuf.
const AssetStreamBuf *buf = (const AssetStreamBuf *) in->rdbuf();
off_t length = AAsset_getLength(buf->_asset);
return length;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountAndroidAsset::get_file_size
// Access: Published, Virtual
// Description: Returns the current size on disk (or wherever it is)
// of the file before it has been opened.
////////////////////////////////////////////////////////////////////
off_t VirtualFileMountAndroidAsset::
get_file_size(const Filename &file) const {
AAsset* asset = AAssetManager_open(_asset_mgr, file.c_str(), AASSET_MODE_UNKNOWN);
off_t length = AAsset_getLength(asset);
AAsset_close(asset);
return length;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountAndroidAsset::get_timestamp
// Access: Published, Virtual
// Description: Returns a time_t value that represents the time the
// file was last modified, to within whatever precision
// the operating system records this information (on a
// Windows95 system, for instance, this may only be
// accurate to within 2 seconds).
//
// If the timestamp cannot be determined, either because
// it is not supported by the operating system or
// because there is some error (such as file not found),
// returns 0.
////////////////////////////////////////////////////////////////////
time_t VirtualFileMountAndroidAsset::
get_timestamp(const Filename &file) const {
// There's no obvious way to get a timestamp from an Android asset.
return 0;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountAndroidAsset::get_system_info
// Access: Public, Virtual
// Description: Populates the SubfileInfo structure with the data
// representing where the file actually resides on disk,
// if this is knowable. Returns true if the file might
// reside on disk, and the info is populated, or false
// if it might not (or it is not known where the file
// resides), in which case the info is meaningless.
////////////////////////////////////////////////////////////////////
bool VirtualFileMountAndroidAsset::
get_system_info(const Filename &file, SubfileInfo &info) {
off_t start, length;
int fd = get_fd(file, &start, &length);
#ifndef NDEBUG
// Double-check that this fd actually points to the apk.
struct stat st1, st2;
nassertr(fstat(fd, &st1) == 0, false);
nassertr(stat(_apk_path.c_str(), &st2) == 0, false);
nassertr(st1.st_dev == st2.st_dev, false);
nassertr(st1.st_ino == st2.st_ino, false);
#endif
// We don't actually need the file descriptor, so close it.
close(fd);
info = SubfileInfo(_apk_path, start, length);
return true;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountAndroidAsset::scan_directory
// Access: Public, Virtual
// Description: Fills the given vector up with the list of filenames
// that are local to this directory, if the filename is
// a directory. Returns true if successful, or false if
// the file is not a directory or cannot be read.
////////////////////////////////////////////////////////////////////
bool VirtualFileMountAndroidAsset::
scan_directory(vector_string &contents, const Filename &dir) const {
AAssetDir *asset_dir = AAssetManager_openDir(_asset_mgr, dir.c_str());
if (asset_dir == NULL) {
return false;
}
// Note: this returns the full path.
const char *fullpath = AAssetDir_getNextFileName(asset_dir);
while (fullpath != NULL) {
express_cat.error() << fullpath << "\n"; // DEBUG
// Determine the basename and add it to the vector.
Filename fname (fullpath);
contents.push_back(fname.get_basename());
fullpath = AAssetDir_getNextFileName(asset_dir);
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountAndroidAsset::AssetStream::Destructor
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
VirtualFileMountAndroidAsset::AssetStream::
~AssetStream() {
delete rdbuf();
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountAndroidAsset::AssetStreamBuf::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
VirtualFileMountAndroidAsset::AssetStreamBuf::
AssetStreamBuf(AAsset *asset) :
_asset(asset) {
#ifdef PHAVE_IOSTREAM
char *buf = new char[4096];
char *ebuf = buf + 4096;
setg(buf, ebuf, ebuf);
#else
allocate();
setg(base(), ebuf(), ebuf());
#endif
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountAndroidAsset::AssetStreamBuf::Destructor
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
VirtualFileMountAndroidAsset::AssetStreamBuf::
~AssetStreamBuf() {
AAsset_close(_asset);
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountAndroidAsset::AssetStreamBuf::seekoff
// Access: Public, Virtual
// Description: Implements seeking within the stream.
////////////////////////////////////////////////////////////////////
streampos VirtualFileMountAndroidAsset::AssetStreamBuf::
seekoff(streamoff off, ios_seekdir dir, ios_openmode which) {
size_t n = egptr() - gptr();
int whence;
switch (dir) {
case ios_base::beg:
whence = SEEK_SET;
break;
case ios_base::cur:
if (off == 0) {
// Just requesting the current position,
// no need to void the buffer.
return AAsset_seek(_asset, 0, SEEK_CUR) - n;
} else if (gptr() + off >= eback() && gptr() + off < egptr()) {
// We can seek around within the buffer.
gbump(off);
return AAsset_seek(_asset, 0, SEEK_CUR) - n + off;
}
whence = SEEK_CUR;
break;
case ios_base::end:
whence = SEEK_END;
break;
default:
return pos_type(-1);
}
gbump(n);
return AAsset_seek(_asset, off, whence);
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountAndroidAsset::AssetStreamBuf::seekpos
// Access: Public, Virtual
// Description: A variant on seekoff() to implement seeking within a
// stream.
//
// The MSDN Library claims that it is only necessary to
// redefine seekoff(), and not seekpos() as well, as the
// default implementation of seekpos() is supposed to
// map to seekoff() exactly as I am doing here; but in
// fact it must do something else, because seeking
// didn't work on Windows until I redefined this
// function as well.
////////////////////////////////////////////////////////////////////
streampos VirtualFileMountAndroidAsset::AssetStreamBuf::
seekpos(streampos pos, ios_openmode which) {
size_t n = egptr() - gptr();
gbump(n);
return AAsset_seek(_asset, pos, SEEK_SET);
}
////////////////////////////////////////////////////////////////////
// Function: VirtualFileMountAndroidAsset::AssetStreamBuf::underflow
// Access: Protected, Virtual
// Description: Called by the system istream implementation when its
// internal buffer needs more characters.
////////////////////////////////////////////////////////////////////
int VirtualFileMountAndroidAsset::AssetStreamBuf::
underflow() {
// Sometimes underflow() is called even if the buffer is not empty.
if (gptr() >= egptr()) {
// Mark the buffer filled (with buffer_size bytes).
size_t buffer_size = egptr() - eback();
gbump(-(int)buffer_size);
streamsize read_count;
read_count = AAsset_read(_asset, gptr(), buffer_size);
if (read_count != buffer_size) {
// Oops, we didn't read what we thought we would.
if (read_count == 0) {
gbump(buffer_size);
return EOF;
}
// Slide what we did read to the top of the buffer.
nassertr(read_count < buffer_size, EOF);
size_t delta = buffer_size - read_count;
memmove(gptr() + delta, gptr(), read_count);
gbump(delta);
}
}
return (unsigned char)*gptr();
}
#endif // ANDROID

View File

@ -0,0 +1,107 @@
// Filename: virtualFileMountAndroidAsset.h
// Created by: rdb (21Jan13)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#ifndef VIRTUALFILEMOUNTANDROIDASSET_H
#define VIRTUALFILEMOUNTANDROIDASSET_H
#ifdef ANDROID
#include "pandabase.h"
#include "virtualFileMount.h"
#include "multifile.h"
#include "pointerTo.h"
#include <android/asset_manager.h>
////////////////////////////////////////////////////////////////////
// Class : VirtualFileMountAndroidAsset
// Description : Maps a Multifile's contents into the
// VirtualFileSystem.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDAEXPRESS VirtualFileMountAndroidAsset : public VirtualFileMount {
PUBLISHED:
INLINE VirtualFileMountAndroidAsset(AAssetManager *mgr, const string &apk_path);
virtual ~VirtualFileMountAndroidAsset();
public:
int get_fd(const Filename &file, off_t *start, off_t *end) const;
virtual bool has_file(const Filename &file) const;
virtual bool is_directory(const Filename &file) const;
virtual bool is_regular_file(const Filename &file) const;
virtual bool read_file(const Filename &file, bool do_uncompress,
pvector<unsigned char> &result) const;
virtual istream *open_read_file(const Filename &file) const;
virtual off_t get_file_size(const Filename &file, istream *stream) const;
virtual off_t get_file_size(const Filename &file) const;
virtual time_t get_timestamp(const Filename &file) const;
virtual bool get_system_info(const Filename &file, SubfileInfo &info);
virtual bool scan_directory(vector_string &contents,
const Filename &dir) const;
private:
AAssetManager *_asset_mgr;
string _apk_path;
class AssetStream : public istream {
public:
INLINE AssetStream(AAsset *asset);
virtual ~AssetStream();
};
class AssetStreamBuf : public streambuf {
public:
AssetStreamBuf(AAsset *asset);
virtual ~AssetStreamBuf();
virtual streampos seekoff(streamoff off, ios_seekdir dir, ios_openmode which);
virtual streampos seekpos(streampos pos, ios_openmode which);
protected:
virtual int underflow();
private:
AAsset *_asset;
off_t _offset;
friend class VirtualFileMountAndroidAsset;
};
public:
virtual TypeHandle get_type() const {
return get_class_type();
}
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
VirtualFileMount::init_type();
register_type(_type_handle, "VirtualFileMountAndroidAsset",
VirtualFileMount::get_class_type());
}
private:
static TypeHandle _type_handle;
};
#include "virtualFileMountAndroidAsset.I"
#endif // ANDROID
#endif

View File

@ -127,7 +127,11 @@ VertexDataSaveFile(const Filename &directory, const string &prefix,
// Now try to lock the file, so we can be sure that no other
// process is simultaneously writing to the same save file.
#ifdef HAVE_LOCKF
int result = lockf(_fd, F_TLOCK, 0);
#else
int result = flock(_fd, LOCK_EX | LOCK_NB);
#endif
if (result == 0) {
// We've got the file. Truncate it first, for good measure, in
// case there's an old version of the file we picked up.

View File

@ -22,6 +22,11 @@
#include "config_pipeline.h"
#include <sched.h>
#ifdef ANDROID
#include "config_express.h"
#include <jni.h>
#endif
pthread_key_t ThreadPosixImpl::_pt_ptr_index = 0;
bool ThreadPosixImpl::_got_pt_ptr_index = false;
@ -217,6 +222,18 @@ root_func(void *data) {
self->_status = S_running;
self->_mutex.release();
}
#ifdef ANDROID
// Attach the Java VM to allow calling Java functions in this thread.
JavaVM *jvm = get_java_vm();
JNIEnv *env;
if (jvm == NULL || jvm->AttachCurrentThread(&env, NULL) != 0) {
thread_cat.error()
<< "Failed to attach Java VM to thread "
<< self->_parent_obj->get_name() << "!\n";
env = NULL;
}
#endif
self->_parent_obj->thread_main();
@ -235,7 +252,13 @@ root_func(void *data) {
self->_status = S_finished;
self->_mutex.release();
}
#ifdef ANDROID
if (env != NULL) {
jvm->DetachCurrentThread();
}
#endif
// Now drop the parent object reference that we grabbed in start().
// This might delete the parent object, and in turn, delete the
// ThreadPosixImpl object.