From bc85e1a4f205319e75a446e507be986b2dfaa2e8 Mon Sep 17 00:00:00 2001 From: rdb Date: Thu, 24 Jan 2013 20:17:08 +0000 Subject: [PATCH] More android support --- panda/src/android/NativeIStream.java | 49 ++ panda/src/android/PandaActivity.java | 58 +++ panda/src/android/android_main.cxx | 76 ++++ panda/src/android/config_android.cxx | 88 ++++ panda/src/android/config_android.h | 39 ++ panda/src/android/jni_NativeIStream.cxx | 71 +++ panda/src/android/p3android_composite1.cxx | 4 + panda/src/android/pnmFileTypeAndroid.cxx | 127 ++++++ panda/src/android/pnmFileTypeAndroid.h | 93 ++++ .../src/android/pnmFileTypeAndroidReader.cxx | 303 +++++++++++++ panda/src/android/pview.cxx | 100 +++++ panda/src/android/pview_manifest.xml | 24 + .../androiddisplay/androidGraphicsWindow.cxx | 4 +- panda/src/express/config_express.cxx | 45 ++ panda/src/express/config_express.h | 9 + panda/src/express/p3express_composite2.cxx | 1 + .../express/virtualFileMountAndroidAsset.I | 35 ++ .../express/virtualFileMountAndroidAsset.cxx | 420 ++++++++++++++++++ .../express/virtualFileMountAndroidAsset.h | 107 +++++ panda/src/gobj/vertexDataSaveFile.cxx | 4 + panda/src/pipeline/threadPosixImpl.cxx | 25 +- 21 files changed, 1679 insertions(+), 3 deletions(-) create mode 100755 panda/src/android/NativeIStream.java create mode 100755 panda/src/android/PandaActivity.java create mode 100644 panda/src/android/android_main.cxx create mode 100644 panda/src/android/config_android.cxx create mode 100644 panda/src/android/config_android.h create mode 100644 panda/src/android/jni_NativeIStream.cxx create mode 100755 panda/src/android/p3android_composite1.cxx create mode 100644 panda/src/android/pnmFileTypeAndroid.cxx create mode 100644 panda/src/android/pnmFileTypeAndroid.h create mode 100644 panda/src/android/pnmFileTypeAndroidReader.cxx create mode 100644 panda/src/android/pview.cxx create mode 100755 panda/src/android/pview_manifest.xml create mode 100644 panda/src/express/virtualFileMountAndroidAsset.I create mode 100644 panda/src/express/virtualFileMountAndroidAsset.cxx create mode 100644 panda/src/express/virtualFileMountAndroidAsset.h diff --git a/panda/src/android/NativeIStream.java b/panda/src/android/NativeIStream.java new file mode 100755 index 0000000000..8487dadafb --- /dev/null +++ b/panda/src/android/NativeIStream.java @@ -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); +} diff --git a/panda/src/android/PandaActivity.java b/panda/src/android/PandaActivity.java new file mode 100755 index 0000000000..8bf8ff4612 --- /dev/null +++ b/panda/src/android/PandaActivity.java @@ -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"); + } +} diff --git a/panda/src/android/android_main.cxx b/panda/src/android/android_main.cxx new file mode 100644 index 0000000000..215c0abf44 --- /dev/null +++ b/panda/src/android/android_main.cxx @@ -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 + +//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(); +} diff --git a/panda/src/android/config_android.cxx b/panda/src/android/config_android.cxx new file mode 100644 index 0000000000..c8c22d1f03 --- /dev/null +++ b/panda/src/android/config_android.cxx @@ -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); +} diff --git a/panda/src/android/config_android.h b/panda/src/android/config_android.h new file mode 100644 index 0000000000..201e631df5 --- /dev/null +++ b/panda/src/android/config_android.h @@ -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 + +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 diff --git a/panda/src/android/jni_NativeIStream.cxx b/panda/src/android/jni_NativeIStream.cxx new file mode 100644 index 0000000000..d6684dd7e7 --- /dev/null +++ b/panda/src/android/jni_NativeIStream.cxx @@ -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 + +#include + +//////////////////////////////////////////////////////////////////// +// 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(); +} diff --git a/panda/src/android/p3android_composite1.cxx b/panda/src/android/p3android_composite1.cxx new file mode 100755 index 0000000000..14b48ab99f --- /dev/null +++ b/panda/src/android/p3android_composite1.cxx @@ -0,0 +1,4 @@ +#include "config_android.cxx" +#include "jni_NativeIStream.cxx" +#include "pnmFileTypeAndroid.cxx" +#include "pnmFileTypeAndroidReader.cxx" \ No newline at end of file diff --git a/panda/src/android/pnmFileTypeAndroid.cxx b/panda/src/android/pnmFileTypeAndroid.cxx new file mode 100644 index 0000000000..68840a1b83 --- /dev/null +++ b/panda/src/android/pnmFileTypeAndroid.cxx @@ -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 ¶ms) { + return PNMFileTypeRegistry::get_global_ptr()->get_type_by_handle(get_class_type()); +} + +#endif // ANDROID diff --git a/panda/src/android/pnmFileTypeAndroid.h b/panda/src/android/pnmFileTypeAndroid.h new file mode 100644 index 0000000000..a98aefec29 --- /dev/null +++ b/panda/src/android/pnmFileTypeAndroid.h @@ -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 + +//////////////////////////////////////////////////////////////////// +// 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 ¶ms); + +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 diff --git a/panda/src/android/pnmFileTypeAndroidReader.cxx b/panda/src/android/pnmFileTypeAndroidReader.cxx new file mode 100644 index 0000000000..8ff2d78a6e --- /dev/null +++ b/panda/src/android/pnmFileTypeAndroidReader.cxx @@ -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 +#include + +// 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 diff --git a/panda/src/android/pview.cxx b/panda/src/android/pview.cxx new file mode 100644 index 0000000000..3f4077830a --- /dev/null +++ b/panda/src/android/pview.cxx @@ -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); +} diff --git a/panda/src/android/pview_manifest.xml b/panda/src/android/pview_manifest.xml new file mode 100755 index 0000000000..133fa523aa --- /dev/null +++ b/panda/src/android/pview_manifest.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + diff --git a/panda/src/androiddisplay/androidGraphicsWindow.cxx b/panda/src/androiddisplay/androidGraphicsWindow.cxx index fc1ad87ded..a48a85107d 100644 --- a/panda/src/androiddisplay/androidGraphicsWindow.cxx +++ b/panda/src/androiddisplay/androidGraphicsWindow.cxx @@ -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); diff --git a/panda/src/express/config_express.cxx b/panda/src/express/config_express.cxx index 7bb47565d9..d5a0abcf58 100644 --- a/panda/src/express/config_express.cxx +++ b/panda/src/express/config_express.cxx @@ -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 diff --git a/panda/src/express/config_express.h b/panda/src/express/config_express.h index 9964bd678b..06203eff08 100644 --- a/panda/src/express/config_express.h +++ b/panda/src/express/config_express.h @@ -28,6 +28,10 @@ // Include this so interrogate can find it. #include "executionEnvironment.h" +#ifdef ANDROID +#include +#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__ */ diff --git a/panda/src/express/p3express_composite2.cxx b/panda/src/express/p3express_composite2.cxx index f1b2c17828..95c8b0e5fb 100644 --- a/panda/src/express/p3express_composite2.cxx +++ b/panda/src/express/p3express_composite2.cxx @@ -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" diff --git a/panda/src/express/virtualFileMountAndroidAsset.I b/panda/src/express/virtualFileMountAndroidAsset.I new file mode 100644 index 0000000000..66b65713d4 --- /dev/null +++ b/panda/src/express/virtualFileMountAndroidAsset.I @@ -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)) { +} diff --git a/panda/src/express/virtualFileMountAndroidAsset.cxx b/panda/src/express/virtualFileMountAndroidAsset.cxx new file mode 100644 index 0000000000..311e127530 --- /dev/null +++ b/panda/src/express/virtualFileMountAndroidAsset.cxx @@ -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 +#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 &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 diff --git a/panda/src/express/virtualFileMountAndroidAsset.h b/panda/src/express/virtualFileMountAndroidAsset.h new file mode 100644 index 0000000000..731702cc75 --- /dev/null +++ b/panda/src/express/virtualFileMountAndroidAsset.h @@ -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 + +//////////////////////////////////////////////////////////////////// +// 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 &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 diff --git a/panda/src/gobj/vertexDataSaveFile.cxx b/panda/src/gobj/vertexDataSaveFile.cxx index 0f3ec5c464..f3ee44cbb2 100644 --- a/panda/src/gobj/vertexDataSaveFile.cxx +++ b/panda/src/gobj/vertexDataSaveFile.cxx @@ -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. diff --git a/panda/src/pipeline/threadPosixImpl.cxx b/panda/src/pipeline/threadPosixImpl.cxx index 34e2b119c5..74e535c8e2 100644 --- a/panda/src/pipeline/threadPosixImpl.cxx +++ b/panda/src/pipeline/threadPosixImpl.cxx @@ -22,6 +22,11 @@ #include "config_pipeline.h" #include +#ifdef ANDROID +#include "config_express.h" +#include +#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.