mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-02 09:52:27 -04:00
android: support writing png/jpg/webp via android.graphics.Bitmap
This commit is contained in:
parent
854d736882
commit
6bd1976892
@ -5100,7 +5100,8 @@ if (not RTDIST and not RUNTIME and PkgSkip("PVIEW")==0 and GetTarget() != 'andro
|
|||||||
if (not RUNTIME and GetTarget() == 'android'):
|
if (not RUNTIME and GetTarget() == 'android'):
|
||||||
OPTS=['DIR:panda/src/android']
|
OPTS=['DIR:panda/src/android']
|
||||||
TargetAdd('org/panda3d/android/NativeIStream.class', opts=OPTS, input='NativeIStream.java')
|
TargetAdd('org/panda3d/android/NativeIStream.class', opts=OPTS, input='NativeIStream.java')
|
||||||
TargetAdd('org/panda3d/android/PandaActivity.class', opts=OPTS, input='PandaActivity.java', dep='org/panda3d/android/NativeIStream.class')
|
TargetAdd('org/panda3d/android/NativeOStream.class', opts=OPTS, input='NativeOStream.java')
|
||||||
|
TargetAdd('org/panda3d/android/PandaActivity.class', opts=OPTS, input='PandaActivity.java')
|
||||||
|
|
||||||
TargetAdd('p3android_composite1.obj', opts=OPTS, input='p3android_composite1.cxx')
|
TargetAdd('p3android_composite1.obj', opts=OPTS, input='p3android_composite1.cxx')
|
||||||
TargetAdd('libp3android.dll', input='p3android_composite1.obj')
|
TargetAdd('libp3android.dll', input='p3android_composite1.obj')
|
||||||
|
52
panda/src/android/NativeOStream.java
Normal file
52
panda/src/android/NativeOStream.java
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/**
|
||||||
|
* 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."
|
||||||
|
*
|
||||||
|
* @file NativeOStream.java
|
||||||
|
* @author rdb
|
||||||
|
* @date 2018-02-10
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.panda3d.android;
|
||||||
|
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An implementation of OutputStream that puts its data into a C++ ostream
|
||||||
|
* pointer, passed as long.
|
||||||
|
*/
|
||||||
|
public class NativeOStream extends OutputStream {
|
||||||
|
private long streamPtr = 0;
|
||||||
|
|
||||||
|
public NativeOStream(long ptr) {
|
||||||
|
streamPtr = ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void flush() {
|
||||||
|
nativeFlush(streamPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(int b) {
|
||||||
|
nativePut(streamPtr, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(byte[] buffer) {
|
||||||
|
nativeWrite(streamPtr, buffer, 0, buffer.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(byte[] buffer, int offset, int length) {
|
||||||
|
nativeWrite(streamPtr, buffer, offset, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static native void nativeFlush(long ptr);
|
||||||
|
private static native void nativePut(long ptr, int b);
|
||||||
|
private static native void nativeWrite(long ptr, byte[] buffer, int offset, int length);
|
||||||
|
}
|
@ -20,12 +20,29 @@ import android.widget.Toast;
|
|||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
import org.panda3d.android.NativeIStream;
|
import org.panda3d.android.NativeIStream;
|
||||||
|
import org.panda3d.android.NativeOStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The entry point for a Panda-based activity. Loads the Panda libraries and
|
* The entry point for a Panda-based activity. Loads the Panda libraries and
|
||||||
* also provides some utility functions.
|
* also provides some utility functions.
|
||||||
*/
|
*/
|
||||||
public class PandaActivity extends NativeActivity {
|
public class PandaActivity extends NativeActivity {
|
||||||
|
private static final Bitmap.Config sConfigs[] = {
|
||||||
|
null,
|
||||||
|
Bitmap.Config.ALPHA_8,
|
||||||
|
null,
|
||||||
|
Bitmap.Config.RGB_565,
|
||||||
|
Bitmap.Config.ARGB_4444,
|
||||||
|
Bitmap.Config.ARGB_8888,
|
||||||
|
null, //Bitmap.Config.RGBA_F16,
|
||||||
|
null, //Bitmap.Config.HARDWARE,
|
||||||
|
};
|
||||||
|
private static final Bitmap.CompressFormat sFormats[] = {
|
||||||
|
Bitmap.CompressFormat.JPEG,
|
||||||
|
Bitmap.CompressFormat.PNG,
|
||||||
|
Bitmap.CompressFormat.WEBP,
|
||||||
|
};
|
||||||
|
|
||||||
protected static BitmapFactory.Options readBitmapSize(long istreamPtr) {
|
protected static BitmapFactory.Options readBitmapSize(long istreamPtr) {
|
||||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||||
options.inJustDecodeBounds = true;
|
options.inJustDecodeBounds = true;
|
||||||
@ -44,6 +61,15 @@ public class PandaActivity extends NativeActivity {
|
|||||||
return BitmapFactory.decodeStream(stream, null, options);
|
return BitmapFactory.decodeStream(stream, null, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected static Bitmap createBitmap(int width, int height, int config, boolean hasAlpha) {
|
||||||
|
return Bitmap.createBitmap(width, height, sConfigs[config]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static boolean compressBitmap(Bitmap bitmap, int format, int quality, long ostreamPtr) {
|
||||||
|
NativeOStream stream = new NativeOStream(ostreamPtr);
|
||||||
|
return bitmap.compress(sFormats[format], quality, stream);
|
||||||
|
}
|
||||||
|
|
||||||
protected static String getCurrentThreadName() {
|
protected static String getCurrentThreadName() {
|
||||||
return Thread.currentThread().getName();
|
return Thread.currentThread().getName();
|
||||||
}
|
}
|
||||||
|
@ -24,12 +24,24 @@ struct android_app *panda_android_app = NULL;
|
|||||||
jclass jni_PandaActivity;
|
jclass jni_PandaActivity;
|
||||||
jmethodID jni_PandaActivity_readBitmapSize;
|
jmethodID jni_PandaActivity_readBitmapSize;
|
||||||
jmethodID jni_PandaActivity_readBitmap;
|
jmethodID jni_PandaActivity_readBitmap;
|
||||||
|
jmethodID jni_PandaActivity_createBitmap;
|
||||||
|
jmethodID jni_PandaActivity_compressBitmap;
|
||||||
jmethodID jni_PandaActivity_showToast;
|
jmethodID jni_PandaActivity_showToast;
|
||||||
|
|
||||||
jclass jni_BitmapFactory_Options;
|
jclass jni_BitmapFactory_Options;
|
||||||
jfieldID jni_BitmapFactory_Options_outWidth;
|
jfieldID jni_BitmapFactory_Options_outWidth;
|
||||||
jfieldID jni_BitmapFactory_Options_outHeight;
|
jfieldID jni_BitmapFactory_Options_outHeight;
|
||||||
|
|
||||||
|
#ifndef HAVE_JPEG
|
||||||
|
static PNMFileTypeAndroid file_type_jpeg(PNMFileTypeAndroid::CF_jpeg);
|
||||||
|
#endif
|
||||||
|
#ifndef HAVE_PNG
|
||||||
|
static PNMFileTypeAndroid file_type_png(PNMFileTypeAndroid::CF_png);
|
||||||
|
#endif
|
||||||
|
#if __ANDROID_API__ >= 14
|
||||||
|
static PNMFileTypeAndroid file_type_webp(PNMFileTypeAndroid::CF_webp);
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the library. This must be called at least once before any of
|
* 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
|
* the functions or classes in this library can be used. Normally, this is
|
||||||
@ -37,10 +49,6 @@ jfieldID jni_BitmapFactory_Options_outHeight;
|
|||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
init_libandroid() {
|
init_libandroid() {
|
||||||
PNMFileTypeRegistry *tr = PNMFileTypeRegistry::get_global_ptr();
|
|
||||||
PNMFileTypeAndroid::init_type();
|
|
||||||
PNMFileTypeAndroid::register_with_read_factory();
|
|
||||||
tr->register_type(new PNMFileTypeAndroid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -48,7 +56,7 @@ init_libandroid() {
|
|||||||
* references and the method IDs.
|
* references and the method IDs.
|
||||||
*/
|
*/
|
||||||
jint JNI_OnLoad(JavaVM *jvm, void *reserved) {
|
jint JNI_OnLoad(JavaVM *jvm, void *reserved) {
|
||||||
init_libandroid();
|
//init_libandroid();
|
||||||
|
|
||||||
Thread *thread = Thread::get_current_thread();
|
Thread *thread = Thread::get_current_thread();
|
||||||
JNIEnv *env = thread->get_jni_env();
|
JNIEnv *env = thread->get_jni_env();
|
||||||
@ -63,6 +71,12 @@ jint JNI_OnLoad(JavaVM *jvm, void *reserved) {
|
|||||||
jni_PandaActivity_readBitmap = env->GetStaticMethodID(jni_PandaActivity,
|
jni_PandaActivity_readBitmap = env->GetStaticMethodID(jni_PandaActivity,
|
||||||
"readBitmap", "(JI)Landroid/graphics/Bitmap;");
|
"readBitmap", "(JI)Landroid/graphics/Bitmap;");
|
||||||
|
|
||||||
|
jni_PandaActivity_createBitmap = env->GetStaticMethodID(jni_PandaActivity,
|
||||||
|
"createBitmap", "(IIIZ)Landroid/graphics/Bitmap;");
|
||||||
|
|
||||||
|
jni_PandaActivity_compressBitmap = env->GetStaticMethodID(jni_PandaActivity,
|
||||||
|
"compressBitmap", "(Landroid/graphics/Bitmap;IIJ)Z");
|
||||||
|
|
||||||
jni_PandaActivity_showToast = env->GetMethodID(jni_PandaActivity,
|
jni_PandaActivity_showToast = env->GetMethodID(jni_PandaActivity,
|
||||||
"showToast", "(Ljava/lang/String;I)V");
|
"showToast", "(Ljava/lang/String;I)V");
|
||||||
|
|
||||||
@ -72,6 +86,25 @@ jint JNI_OnLoad(JavaVM *jvm, void *reserved) {
|
|||||||
jni_BitmapFactory_Options_outWidth = env->GetFieldID(jni_BitmapFactory_Options, "outWidth", "I");
|
jni_BitmapFactory_Options_outWidth = env->GetFieldID(jni_BitmapFactory_Options, "outWidth", "I");
|
||||||
jni_BitmapFactory_Options_outHeight = env->GetFieldID(jni_BitmapFactory_Options, "outHeight", "I");
|
jni_BitmapFactory_Options_outHeight = env->GetFieldID(jni_BitmapFactory_Options, "outHeight", "I");
|
||||||
|
|
||||||
|
nassertr(jni_PandaActivity_readBitmapSize, -1);
|
||||||
|
nassertr(jni_PandaActivity_readBitmap, -1);
|
||||||
|
nassertr(jni_PandaActivity_createBitmap, -1);
|
||||||
|
nassertr(jni_PandaActivity_compressBitmap, -1);
|
||||||
|
nassertr(jni_PandaActivity_showToast, -1);
|
||||||
|
|
||||||
|
// We put this in JNI_OnLoad because it relies on Java classes, which
|
||||||
|
// are only available when launched from the Java VM.
|
||||||
|
PNMFileTypeRegistry *tr = PNMFileTypeRegistry::get_global_ptr();
|
||||||
|
#ifndef HAVE_JPEG
|
||||||
|
tr->register_type(&file_type_jpeg);
|
||||||
|
#endif
|
||||||
|
#ifndef HAVE_PNG
|
||||||
|
tr->register_type(&file_type_png);
|
||||||
|
#endif
|
||||||
|
#if __ANDROID_API__ >= 14
|
||||||
|
tr->register_type(&file_type_webp);
|
||||||
|
#endif
|
||||||
|
|
||||||
return JNI_VERSION_1_4;
|
return JNI_VERSION_1_4;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,6 +119,20 @@ void JNI_OnUnload(JavaVM *jvm, void *reserved) {
|
|||||||
|
|
||||||
env->DeleteGlobalRef(jni_PandaActivity);
|
env->DeleteGlobalRef(jni_PandaActivity);
|
||||||
env->DeleteGlobalRef(jni_BitmapFactory_Options);
|
env->DeleteGlobalRef(jni_BitmapFactory_Options);
|
||||||
|
|
||||||
|
// These will no longer work without JNI, so unregister them.
|
||||||
|
PNMFileTypeRegistry *tr = PNMFileTypeRegistry::get_global_ptr();
|
||||||
|
if (tr != nullptr) {
|
||||||
|
#ifndef HAVE_JPEG
|
||||||
|
tr->unregister_type(&file_type_jpeg);
|
||||||
|
#endif
|
||||||
|
#ifndef HAVE_PNG
|
||||||
|
tr->unregister_type(&file_type_png);
|
||||||
|
#endif
|
||||||
|
#if __ANDROID_API__ >= 14
|
||||||
|
tr->unregister_type(&file_type_webp);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,6 +31,8 @@ extern EXPORT_CLASS struct android_app* panda_android_app;
|
|||||||
extern jclass jni_PandaActivity;
|
extern jclass jni_PandaActivity;
|
||||||
extern jmethodID jni_PandaActivity_readBitmapHeader;
|
extern jmethodID jni_PandaActivity_readBitmapHeader;
|
||||||
extern jmethodID jni_PandaActivity_readBitmap;
|
extern jmethodID jni_PandaActivity_readBitmap;
|
||||||
|
extern jmethodID jni_PandaActivity_createBitmap;
|
||||||
|
extern jmethodID jni_PandaActivity_compressBitmap;
|
||||||
extern jmethodID jni_PandaActivity_showToast;
|
extern jmethodID jni_PandaActivity_showToast;
|
||||||
|
|
||||||
extern jclass jni_BitmapFactory_Options;
|
extern jclass jni_BitmapFactory_Options;
|
||||||
|
54
panda/src/android/jni_NativeOStream.cxx
Normal file
54
panda/src/android/jni_NativeOStream.cxx
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/**
|
||||||
|
* 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."
|
||||||
|
*
|
||||||
|
* @file jni_NativeOStream.cxx
|
||||||
|
* @author rdb
|
||||||
|
* @date 2018-02-10
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <jni.h>
|
||||||
|
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
|
#if __GNUC__ >= 4
|
||||||
|
#define EXPORT_JNI extern "C" __attribute__((visibility("default")))
|
||||||
|
#else
|
||||||
|
#define EXPORT_JNI extern "C"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flushes the stream.
|
||||||
|
*/
|
||||||
|
EXPORT_JNI void
|
||||||
|
Java_org_panda3d_android_NativeOStream_nativeFlush(JNIEnv *env, jclass clazz, jlong ptr) {
|
||||||
|
std::ostream *stream = (std::ostream *)ptr;
|
||||||
|
|
||||||
|
stream->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a single character to the ostream.
|
||||||
|
*/
|
||||||
|
EXPORT_JNI void
|
||||||
|
Java_org_panda3d_android_NativeOStream_nativePut(JNIEnv *env, jclass clazz, jlong ptr, int b) {
|
||||||
|
std::ostream *stream = (std::ostream *)ptr;
|
||||||
|
|
||||||
|
stream->put((char)(b & 0xff));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes an array of bytes to the ostream.
|
||||||
|
*/
|
||||||
|
EXPORT_JNI void
|
||||||
|
Java_org_panda3d_android_NativeOStream_nativeWrite(JNIEnv *env, jclass clazz, jlong ptr, jbyteArray byte_array, jint offset, jint length) {
|
||||||
|
std::ostream *stream = (std::ostream *)ptr;
|
||||||
|
|
||||||
|
jbyte *buffer = (jbyte *)alloca(length);
|
||||||
|
env->GetByteArrayRegion(byte_array, offset, length, buffer);
|
||||||
|
stream->write((char *)buffer, length);
|
||||||
|
}
|
@ -1,4 +1,6 @@
|
|||||||
#include "config_android.cxx"
|
#include "config_android.cxx"
|
||||||
#include "jni_NativeIStream.cxx"
|
#include "jni_NativeIStream.cxx"
|
||||||
|
#include "jni_NativeOStream.cxx"
|
||||||
#include "pnmFileTypeAndroid.cxx"
|
#include "pnmFileTypeAndroid.cxx"
|
||||||
#include "pnmFileTypeAndroidReader.cxx"
|
#include "pnmFileTypeAndroidReader.cxx"
|
||||||
|
#include "pnmFileTypeAndroidWriter.cxx"
|
||||||
|
@ -17,24 +17,11 @@
|
|||||||
|
|
||||||
#include "config_pnmimagetypes.h"
|
#include "config_pnmimagetypes.h"
|
||||||
|
|
||||||
#include "pnmFileTypeRegistry.h"
|
|
||||||
#include "bamReader.h"
|
|
||||||
|
|
||||||
static const char * const extensions_android[] = {
|
|
||||||
"jpg", "jpeg", "gif", "png",
|
|
||||||
#if __ANDROID_API__ >= 14
|
|
||||||
"webp"
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
static const int num_extensions_android = sizeof(extensions_android) / sizeof(const char *);
|
|
||||||
|
|
||||||
TypeHandle PNMFileTypeAndroid::_type_handle;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
PNMFileTypeAndroid::
|
PNMFileTypeAndroid::
|
||||||
PNMFileTypeAndroid() {
|
PNMFileTypeAndroid(CompressFormat format) : _format(format) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -51,7 +38,16 @@ get_name() const {
|
|||||||
*/
|
*/
|
||||||
int PNMFileTypeAndroid::
|
int PNMFileTypeAndroid::
|
||||||
get_num_extensions() const {
|
get_num_extensions() const {
|
||||||
return num_extensions_android;
|
switch (_format) {
|
||||||
|
case CF_jpeg:
|
||||||
|
return 3;
|
||||||
|
case CF_png:
|
||||||
|
return 1;
|
||||||
|
case CF_webp:
|
||||||
|
return 1;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -60,8 +56,17 @@ get_num_extensions() const {
|
|||||||
*/
|
*/
|
||||||
string PNMFileTypeAndroid::
|
string PNMFileTypeAndroid::
|
||||||
get_extension(int n) const {
|
get_extension(int n) const {
|
||||||
nassertr(n >= 0 && n < num_extensions_android, string());
|
static const char *const jpeg_extensions[] = {"jpg", "jpeg", "jpe"};
|
||||||
return extensions_android[n];
|
switch (_format) {
|
||||||
|
case CF_jpeg:
|
||||||
|
return jpeg_extensions[n];
|
||||||
|
case CF_png:
|
||||||
|
return "png";
|
||||||
|
case CF_webp:
|
||||||
|
return "webp";
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -80,30 +85,17 @@ has_magic_number() const {
|
|||||||
*/
|
*/
|
||||||
PNMReader *PNMFileTypeAndroid::
|
PNMReader *PNMFileTypeAndroid::
|
||||||
make_reader(istream *file, bool owns_file, const string &magic_number) {
|
make_reader(istream *file, bool owns_file, const string &magic_number) {
|
||||||
init_pnm();
|
|
||||||
return new Reader(this, file, owns_file, magic_number);
|
return new Reader(this, file, owns_file, magic_number);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers the current object as something that can be read from a Bam file.
|
* Allocates and returns a new PNMWriter suitable for reading from this file
|
||||||
|
* type, if possible. If writing files of this type is not supported, returns
|
||||||
|
* NULL.
|
||||||
*/
|
*/
|
||||||
void PNMFileTypeAndroid::
|
PNMWriter *PNMFileTypeAndroid::
|
||||||
register_with_read_factory() {
|
make_writer(ostream *file, bool owns_file) {
|
||||||
BamReader::get_factory()->
|
return new Writer(this, file, owns_file, _format);
|
||||||
register_factory(get_class_type(), make_PNMFileTypeAndroid);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
#endif // ANDROID
|
||||||
|
@ -30,7 +30,13 @@
|
|||||||
*/
|
*/
|
||||||
class EXPCL_PANDA_PNMIMAGETYPES PNMFileTypeAndroid : public PNMFileType {
|
class EXPCL_PANDA_PNMIMAGETYPES PNMFileTypeAndroid : public PNMFileType {
|
||||||
public:
|
public:
|
||||||
PNMFileTypeAndroid();
|
enum CompressFormat : jint {
|
||||||
|
CF_jpeg = 0,
|
||||||
|
CF_png = 1,
|
||||||
|
CF_webp = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
PNMFileTypeAndroid(CompressFormat format);
|
||||||
|
|
||||||
virtual string get_name() const;
|
virtual string get_name() const;
|
||||||
|
|
||||||
@ -41,6 +47,7 @@ public:
|
|||||||
|
|
||||||
virtual PNMReader *make_reader(istream *file, bool owns_file = true,
|
virtual PNMReader *make_reader(istream *file, bool owns_file = true,
|
||||||
const string &magic_number = string());
|
const string &magic_number = string());
|
||||||
|
virtual PNMWriter *make_writer(ostream *file, bool owns_file = true);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
class Reader : public PNMReader {
|
class Reader : public PNMReader {
|
||||||
@ -60,29 +67,20 @@ public:
|
|||||||
int32_t _format;
|
int32_t _format;
|
||||||
};
|
};
|
||||||
|
|
||||||
// The TypedWritable interface follows.
|
class Writer : public PNMWriter {
|
||||||
public:
|
public:
|
||||||
static void register_with_read_factory();
|
Writer(PNMFileType *type, ostream *file, bool owns_file,
|
||||||
|
CompressFormat format);
|
||||||
|
|
||||||
protected:
|
virtual int write_data(xel *array, xelval *alpha);
|
||||||
static TypedWritable *make_PNMFileTypeAndroid(const FactoryParams ¶ms);
|
virtual bool supports_grayscale() const;
|
||||||
|
|
||||||
public:
|
private:
|
||||||
static TypeHandle get_class_type() {
|
CompressFormat _format;
|
||||||
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:
|
private:
|
||||||
static TypeHandle _type_handle;
|
CompressFormat _format;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ANDROID
|
#endif // ANDROID
|
||||||
|
146
panda/src/android/pnmFileTypeAndroidWriter.cxx
Normal file
146
panda/src/android/pnmFileTypeAndroidWriter.cxx
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
/**
|
||||||
|
* 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."
|
||||||
|
*
|
||||||
|
* @file pnmFileTypeAndroidWriter.cxx
|
||||||
|
* @author rdb
|
||||||
|
* @date 2018-02-10
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "pnmFileTypeAndroid.h"
|
||||||
|
|
||||||
|
#ifdef ANDROID
|
||||||
|
|
||||||
|
#include "config_pnmimagetypes.h"
|
||||||
|
|
||||||
|
#include <android/bitmap.h>
|
||||||
|
#include <jni.h>
|
||||||
|
|
||||||
|
// See android/graphics/Bitmap.java
|
||||||
|
enum class BitmapConfig : jint {
|
||||||
|
ALPHA_8 = 1,
|
||||||
|
RGB_565 = 3,
|
||||||
|
ARGB_4444 = 4,
|
||||||
|
ARGB_8888 = 5,
|
||||||
|
RGBA_F16 = 6,
|
||||||
|
HARDWARE = 7,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
PNMFileTypeAndroid::Writer::
|
||||||
|
Writer(PNMFileType *type, ostream *file, bool owns_file,
|
||||||
|
CompressFormat format) :
|
||||||
|
PNMWriter(type, file, owns_file),
|
||||||
|
_format(format)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes out an entire image all at once, including the header, based on the
|
||||||
|
* image data stored in the given _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 written.
|
||||||
|
*
|
||||||
|
* It is the user's responsibility to fill in the header data via calls to
|
||||||
|
* set_x_size(), set_num_channels(), etc., or copy_header_from(), before
|
||||||
|
* calling write_data().
|
||||||
|
*
|
||||||
|
* It is important to delete the PNMWriter class after successfully writing
|
||||||
|
* the data. Failing to do this may result in some data not getting flushed!
|
||||||
|
*
|
||||||
|
* Derived classes need not override this if they instead provide
|
||||||
|
* supports_streaming() and write_row(), below.
|
||||||
|
*/
|
||||||
|
int PNMFileTypeAndroid::Writer::
|
||||||
|
write_data(xel *array, xelval *alpha) {
|
||||||
|
size_t num_pixels = (size_t)_x_size * (size_t)_y_size;
|
||||||
|
|
||||||
|
Thread *current_thread = Thread::get_current_thread();
|
||||||
|
JNIEnv *env = current_thread->get_jni_env();
|
||||||
|
nassertr(env != nullptr, 0);
|
||||||
|
|
||||||
|
// Create a Bitmap object.
|
||||||
|
jobject bitmap =
|
||||||
|
env->CallStaticObjectMethod(jni_PandaActivity,
|
||||||
|
jni_PandaActivity_createBitmap,
|
||||||
|
(jint)_x_size, (jint)_y_size,
|
||||||
|
BitmapConfig::ARGB_8888,
|
||||||
|
(jboolean)has_alpha());
|
||||||
|
nassertr(bitmap != nullptr, 0);
|
||||||
|
|
||||||
|
// Get a writable pointer to write our pixel data to.
|
||||||
|
uint32_t *out;
|
||||||
|
int rc = AndroidBitmap_lockPixels(env, bitmap, (void **)&out);
|
||||||
|
if (rc != 0) {
|
||||||
|
android_cat.error()
|
||||||
|
<< "Could not lock bitmap pixels (result code " << rc << ")\n";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_maxval == 255) {
|
||||||
|
if (has_alpha() && alpha != nullptr) {
|
||||||
|
for (size_t i = 0; i < num_pixels; ++i) {
|
||||||
|
out[i] = (array[i].r)
|
||||||
|
| (array[i].g << 8u)
|
||||||
|
| (array[i].b << 16u)
|
||||||
|
| (alpha[i] << 24u);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (size_t i = 0; i < num_pixels; ++i) {
|
||||||
|
out[i] = (array[i].r)
|
||||||
|
| (array[i].g << 8u)
|
||||||
|
| (array[i].b << 16u)
|
||||||
|
| 0xff000000u;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
double ratio = 255.0 / _maxval;
|
||||||
|
if (has_alpha() && alpha != nullptr) {
|
||||||
|
for (size_t i = 0; i < num_pixels; ++i) {
|
||||||
|
out[i] = ((uint32_t)(array[i].r * ratio))
|
||||||
|
| ((uint32_t)(array[i].g * ratio) << 8u)
|
||||||
|
| ((uint32_t)(array[i].b * ratio) << 16u)
|
||||||
|
| ((uint32_t)(alpha[i] * ratio) << 24u);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (size_t i = 0; i < num_pixels; ++i) {
|
||||||
|
out[i] = ((uint32_t)(array[i].r * ratio))
|
||||||
|
| ((uint32_t)(array[i].g * ratio) << 8u)
|
||||||
|
| ((uint32_t)(array[i].b * ratio) << 16u)
|
||||||
|
| 0xff000000u;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, unlock the pixel data and compress it to the ostream.
|
||||||
|
AndroidBitmap_unlockPixels(env, bitmap);
|
||||||
|
jboolean res =
|
||||||
|
env->CallStaticBooleanMethod(jni_PandaActivity,
|
||||||
|
jni_PandaActivity_compressBitmap,
|
||||||
|
bitmap, _format, 85, (jlong)_file);
|
||||||
|
if (!res) {
|
||||||
|
android_cat.error()
|
||||||
|
<< "Failed to compress bitmap.\n";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return _y_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if this particular PNMWriter understands grayscale images. If
|
||||||
|
* this is false, then the rgb values of the xel array will be pre-filled with
|
||||||
|
* the same value across all three channels, to allow the writer to simply
|
||||||
|
* write out RGB data for a grayscale image.
|
||||||
|
*/
|
||||||
|
bool PNMFileTypeAndroid::Writer::
|
||||||
|
supports_grayscale() const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // ANDROID
|
@ -6,6 +6,7 @@
|
|||||||
android:versionName="1.0">
|
android:versionName="1.0">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
<uses-sdk android:minSdkVersion="21" />
|
<uses-sdk android:minSdkVersion="21" />
|
||||||
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
|
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
|
||||||
|
|
||||||
|
@ -49,16 +49,19 @@ register_type(PNMFileType *type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Make sure we haven't already registered this type.
|
// Make sure we haven't already registered this type.
|
||||||
Handles::iterator hi = _handles.find(type->get_type());
|
TypeHandle handle = type->get_type();
|
||||||
if (hi != _handles.end()) {
|
if (handle != PNMFileType::get_class_type()) {
|
||||||
pnmimage_cat->warning()
|
Handles::iterator hi = _handles.find(handle);
|
||||||
<< "Attempt to register PNMFileType " << type->get_name()
|
if (hi != _handles.end()) {
|
||||||
<< " (" << type->get_type() << ") more than once.\n";
|
pnmimage_cat->warning()
|
||||||
return;
|
<< "Attempt to register PNMFileType " << type->get_name()
|
||||||
|
<< " (" << type->get_type() << ") more than once.\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_handles.insert(Handles::value_type(handle, type));
|
||||||
}
|
}
|
||||||
|
|
||||||
_types.push_back(type);
|
_types.push_back(type);
|
||||||
_handles.insert(Handles::value_type(type->get_type(), type));
|
|
||||||
|
|
||||||
// Collect the unique extensions associated with the type.
|
// Collect the unique extensions associated with the type.
|
||||||
pset<string> unique_extensions;
|
pset<string> unique_extensions;
|
||||||
@ -82,6 +85,37 @@ register_type(PNMFileType *type) {
|
|||||||
_requires_sort = true;
|
_requires_sort = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a PNMFileType previously passed to register_type.
|
||||||
|
*/
|
||||||
|
void PNMFileTypeRegistry::
|
||||||
|
unregister_type(PNMFileType *type) {
|
||||||
|
if (pnmimage_cat->is_debug()) {
|
||||||
|
pnmimage_cat->debug()
|
||||||
|
<< "Unregistering image type " << type->get_name() << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeHandle handle = type->get_type();
|
||||||
|
if (handle != PNMFileType::get_class_type()) {
|
||||||
|
Handles::iterator hi = _handles.find(handle);
|
||||||
|
if (hi != _handles.end()) {
|
||||||
|
_handles.erase(hi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_types.erase(std::remove(_types.begin(), _types.end(), type),
|
||||||
|
_types.end());
|
||||||
|
|
||||||
|
Extensions::iterator ei;
|
||||||
|
for (ei = _extensions.begin(); ei != _extensions.end(); ++ei) {
|
||||||
|
Types &types = ei->second;
|
||||||
|
types.erase(std::remove(types.begin(), types.end(), type),
|
||||||
|
types.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
_requires_sort = true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the total number of types registered.
|
* Returns the total number of types registered.
|
||||||
*/
|
*/
|
||||||
|
@ -33,6 +33,7 @@ public:
|
|||||||
~PNMFileTypeRegistry();
|
~PNMFileTypeRegistry();
|
||||||
|
|
||||||
void register_type(PNMFileType *type);
|
void register_type(PNMFileType *type);
|
||||||
|
void unregister_type(PNMFileType *type);
|
||||||
|
|
||||||
PUBLISHED:
|
PUBLISHED:
|
||||||
int get_num_types() const;
|
int get_num_types() const;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user