OsmAnd/Osmand-kernel/osmand/common.cpp
2012-04-30 01:51:51 +02:00

347 lines
12 KiB
C++

#include <android/log.h>
#include <string>
#include <vector>
#include <hash_map>
#include <SkPath.h>
#include <SkBitmap.h>
#include <SkImageDecoder.h>
#include "common.h"
const char* const LOG_TAG = "net.osmand:native";
JavaVM* globalJVM = NULL;
// Forward declarations
void loadJniCommon(JNIEnv* env);
void loadJniRendering(JNIEnv* env);
void loadJniRenderingRules(JNIEnv* env);
extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
{
JNIEnv* globalJniEnv;
if(vm->GetEnv((void **)&globalJniEnv, JNI_VERSION_1_6))
return JNI_ERR; /* JNI version not supported */
globalJVM = vm;
loadJniCommon(globalJniEnv);
loadJniRendering(globalJniEnv);
loadJniRenderingRules(globalJniEnv);
__android_log_print(ANDROID_LOG_INFO, LOG_TAG, "JNI_OnLoad completed");
return JNI_VERSION_1_6;
}
void throwNewException(JNIEnv* env, const char* msg)
{
__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, msg);
env->ThrowNew(env->FindClass("java/lang/Exception"), msg);
}
jclass findClass(JNIEnv* env, const char* className, bool mustHave/* = true*/)
{
jclass javaClass = env->FindClass(className);
if(!javaClass && mustHave)
throwNewException(env, (std::string("Failed to find class ") + className).c_str());
return (jclass)newGlobalRef(env, javaClass);
}
jobject newGlobalRef(JNIEnv* env, jobject o)
{
return env->NewGlobalRef(o);
}
jfieldID getFid(JNIEnv* env, jclass cls, const char* fieldName, const char* sig)
{
jfieldID jfield = env->GetFieldID(cls, fieldName, sig);
if(!jfield)
throwNewException(env, (std::string("Failed to find field ") + fieldName + std::string(" with signature ") + sig).c_str());
return jfield;
}
std::string getStringField(JNIEnv* env, jobject o, jfieldID fid)
{
jstring jstr = (jstring)env->GetObjectField(o, fid);
if(!jstr)
{
throwNewException(env, "Failed to get object from field");
return std::string();
}
const char* utfBytes = env->GetStringUTFChars(jstr, NULL);
//TODO: I'm not quite sure that if real unicode will happen here, this will work as expected
std::string result(utfBytes);
env->ReleaseStringUTFChars(jstr, utfBytes);
env->DeleteLocalRef(jstr);
return result;
}
std::string getString(JNIEnv* env, jstring jstr)
{
if(!jstr)
{
throwNewException(env, "NULL jstring passed in");
return std::string();
}
const char* utfBytes = env->GetStringUTFChars(jstr, NULL);
//TODO: I'm not quite sure that if real unicode will happen here, this will work as expected
std::string result(utfBytes);
env->ReleaseStringUTFChars(jstr, utfBytes);
return result;
}
std::string getStringMethod(JNIEnv* env, jobject o, jmethodID fid)
{
return getString(env, (jstring)env->CallObjectMethod(o, fid));
}
std::string getStringMethod(JNIEnv* env, jobject o, jmethodID fid, int i)
{
return getString(env, (jstring)env->CallObjectMethod(o, fid, i));
}
jclass jclass_RenderingContext = NULL;
jfieldID jfield_RenderingContext_interrupted = NULL;
jclass jclass_RenderingIcons = NULL;
jmethodID jmethod_RenderingIcons_getIconRawData = NULL;
jfieldID jfield_RenderingContext_leftX = NULL;
jfieldID jfield_RenderingContext_topY = NULL;
jfieldID jfield_RenderingContext_width = NULL;
jfieldID jfield_RenderingContext_height = NULL;
jfieldID jfield_RenderingContext_zoom = NULL;
jfieldID jfield_RenderingContext_rotate = NULL;
jfieldID jfield_RenderingContext_tileDivisor = NULL;
jfieldID jfield_RenderingContext_pointCount = NULL;
jfieldID jfield_RenderingContext_pointInsideCount = NULL;
jfieldID jfield_RenderingContext_visible = NULL;
jfieldID jfield_RenderingContext_allObjects = NULL;
jfieldID jfield_RenderingContext_cosRotateTileSize = NULL;
jfieldID jfield_RenderingContext_sinRotateTileSize = NULL;
jfieldID jfield_RenderingContext_density = NULL;
jfieldID jfield_RenderingContext_highResMode = NULL;
jfieldID jfield_RenderingContext_mapTextSize = NULL;
jfieldID jfield_RenderingContext_shadowRenderingMode = NULL;
jfieldID jfield_RenderingContext_shadowLevelMin = NULL;
jfieldID jfield_RenderingContext_shadowLevelMax = NULL;
jfieldID jfield_RenderingContext_ctx = NULL;
jfieldID jfield_RenderingContext_textRenderingTime = NULL;
jfieldID jfield_RenderingContext_lastRenderedKey = NULL;
void loadJniCommon(JNIEnv* env)
{
jclass_RenderingContext = findClass(env, "net/osmand/plus/render/OsmandRenderer$RenderingContext");
jfield_RenderingContext_interrupted = getFid(env, jclass_RenderingContext, "interrupted", "Z");
jfield_RenderingContext_leftX = getFid(env, jclass_RenderingContext, "leftX", "F" );
jfield_RenderingContext_topY = getFid(env, jclass_RenderingContext, "topY", "F" );
jfield_RenderingContext_width = getFid(env, jclass_RenderingContext, "width", "I" );
jfield_RenderingContext_height = getFid(env, jclass_RenderingContext, "height", "I" );
jfield_RenderingContext_zoom = getFid(env, jclass_RenderingContext, "zoom", "I" );
jfield_RenderingContext_rotate = getFid(env, jclass_RenderingContext, "rotate", "F" );
jfield_RenderingContext_tileDivisor = getFid(env, jclass_RenderingContext, "tileDivisor", "F" );
jfield_RenderingContext_pointCount = getFid(env, jclass_RenderingContext, "pointCount", "I" );
jfield_RenderingContext_pointInsideCount = getFid(env, jclass_RenderingContext, "pointInsideCount", "I" );
jfield_RenderingContext_visible = getFid(env, jclass_RenderingContext, "visible", "I" );
jfield_RenderingContext_allObjects = getFid(env, jclass_RenderingContext, "allObjects", "I" );
jfield_RenderingContext_cosRotateTileSize = getFid(env, jclass_RenderingContext, "cosRotateTileSize", "F" );
jfield_RenderingContext_sinRotateTileSize = getFid(env, jclass_RenderingContext, "sinRotateTileSize", "F" );
jfield_RenderingContext_density = getFid(env, jclass_RenderingContext, "density", "F" );
jfield_RenderingContext_highResMode = getFid(env, jclass_RenderingContext, "highResMode", "Z" );
jfield_RenderingContext_mapTextSize = getFid(env, jclass_RenderingContext, "mapTextSize", "F" );
jfield_RenderingContext_shadowRenderingMode = getFid(env, jclass_RenderingContext, "shadowRenderingMode", "I" );
jfield_RenderingContext_shadowLevelMin = getFid(env, jclass_RenderingContext, "shadowLevelMin", "I" );
jfield_RenderingContext_shadowLevelMax = getFid(env, jclass_RenderingContext, "shadowLevelMax", "I" );
jfield_RenderingContext_ctx = getFid(env, jclass_RenderingContext, "ctx", "Landroid/content/Context;" );
jfield_RenderingContext_textRenderingTime = getFid(env, jclass_RenderingContext, "textRenderingTime", "I" );
jfield_RenderingContext_lastRenderedKey = getFid(env, jclass_RenderingContext, "lastRenderedKey", "I" );
jclass_RenderingIcons = findClass(env, "net/osmand/plus/render/RenderingIcons");
jmethod_RenderingIcons_getIconRawData = env->GetStaticMethodID(jclass_RenderingIcons,
"getIconRawData",
"(Landroid/content/Context;Ljava/lang/String;)[B");
}
void pullFromJavaRenderingContext(JNIEnv* env, jobject jrc, RenderingContext* rc)
{
rc->env = env;
rc->leftX = env->GetFloatField( jrc, jfield_RenderingContext_leftX );
rc->topY = env->GetFloatField( jrc, jfield_RenderingContext_topY );
rc->width = env->GetIntField( jrc, jfield_RenderingContext_width );
rc->height = env->GetIntField( jrc, jfield_RenderingContext_height );
rc->zoom = env->GetIntField( jrc, jfield_RenderingContext_zoom );
rc->rotate = env->GetFloatField( jrc, jfield_RenderingContext_rotate );
rc->tileDivisor = env->GetFloatField( jrc, jfield_RenderingContext_tileDivisor );
rc->pointCount = env->GetIntField( jrc, jfield_RenderingContext_pointCount );
rc->pointInsideCount = env->GetIntField( jrc, jfield_RenderingContext_pointInsideCount );
rc->visible = env->GetIntField( jrc, jfield_RenderingContext_visible );
rc->allObjects = env->GetIntField( jrc, jfield_RenderingContext_allObjects );
rc->cosRotateTileSize = env->GetFloatField( jrc, jfield_RenderingContext_cosRotateTileSize );
rc->sinRotateTileSize = env->GetFloatField( jrc, jfield_RenderingContext_sinRotateTileSize );
rc->density = env->GetFloatField( jrc, jfield_RenderingContext_density );
rc->highResMode = env->GetBooleanField( jrc, jfield_RenderingContext_highResMode );
rc->mapTextSize = env->GetFloatField( jrc, jfield_RenderingContext_mapTextSize );
rc->shadowRenderingMode = env->GetIntField( jrc, jfield_RenderingContext_shadowRenderingMode );
rc->shadowLevelMin = env->GetIntField( jrc, jfield_RenderingContext_shadowLevelMin );
rc->shadowLevelMax = env->GetIntField( jrc, jfield_RenderingContext_shadowLevelMax );
rc->androidContext = env->GetObjectField(jrc, jfield_RenderingContext_ctx );
rc->lastRenderedKey = 0;
rc->javaRenderingContext = jrc;
}
void pushToJavaRenderingContext(JNIEnv* env, jobject jrc, RenderingContext* rc)
{
env->SetIntField( jrc, jfield_RenderingContext_pointCount, rc->pointCount);
env->SetIntField( jrc, jfield_RenderingContext_pointInsideCount, rc->pointInsideCount);
env->SetIntField( jrc, jfield_RenderingContext_visible, rc->visible);
env->SetIntField( jrc, jfield_RenderingContext_allObjects, rc->allObjects);
env->SetIntField( jrc, jfield_RenderingContext_textRenderingTime, rc->textRendering.getElapsedTime());
env->SetIntField( jrc, jfield_RenderingContext_lastRenderedKey, rc->lastRenderedKey);
env->DeleteLocalRef(rc->androidContext);
}
TextDrawInfo::TextDrawInfo(std::string itext)
: text(itext)
, drawOnPath(false)
, path(NULL)
, pathRotate(0)
{
}
TextDrawInfo::~TextDrawInfo()
{
if (path)
delete path;
}
IconDrawInfo::IconDrawInfo()
: bmp(NULL)
{
}
RenderingContext::RenderingContext()
: javaRenderingContext(NULL)
, androidContext(NULL)
{
}
RenderingContext::~RenderingContext()
{
std::vector<TextDrawInfo*>::iterator itTextToDraw;
for(itTextToDraw = textToDraw.begin(); itTextToDraw != textToDraw.end(); ++itTextToDraw)
delete (*itTextToDraw);
}
bool RenderingContext::interrupted()
{
return env->GetBooleanField(javaRenderingContext, jfield_RenderingContext_interrupted);
}
float getDensityValue(RenderingContext* rc, float val)
{
if (rc->highResMode && rc->density > 1)
return val * rc->density * rc->mapTextSize;
else
return val * rc->mapTextSize;
}
ElapsedTimer::ElapsedTimer()
: elapsedTime(0)
, enableFlag(true)
, run(false)
{
}
void ElapsedTimer::enable()
{
enableFlag = true;
}
void ElapsedTimer::disable()
{
pause();
enableFlag = false;
}
void ElapsedTimer::start()
{
if (!enableFlag)
return;
if (!run)
clock_gettime(CLOCK_MONOTONIC, &startInit);
run = true;
}
void ElapsedTimer::pause()
{
if (!run)
return;
clock_gettime(CLOCK_MONOTONIC, &endInit);
int sec = endInit.tv_sec - startInit.tv_sec;
if (sec > 0)
elapsedTime += 1e9 * sec;
elapsedTime += endInit.tv_nsec - startInit.tv_nsec;
run = false;
}
int ElapsedTimer::getElapsedTime()
{
pause();
return elapsedTime / 1e6;
}
std::hash_map<std::string, SkBitmap*> cachedBitmaps;
SkBitmap* getCachedBitmap(RenderingContext* rc, const std::string& bitmapResource)
{
JNIEnv* env = rc->env;
if(bitmapResource.size() == 0)
return NULL;
// Try to find previously cached
std::hash_map<std::string, SkBitmap*>::iterator itPreviouslyCachedBitmap = cachedBitmaps.find(bitmapResource);
if (itPreviouslyCachedBitmap != cachedBitmaps.end())
return itPreviouslyCachedBitmap->second;
rc->nativeOperations.pause();
jstring jstr = env->NewStringUTF(bitmapResource.c_str());
jbyteArray javaIconRawData = (jbyteArray)env->CallStaticObjectMethod(jclass_RenderingIcons, jmethod_RenderingIcons_getIconRawData, rc->androidContext, jstr);
if(!javaIconRawData)
return NULL;
jbyte* bitmapBuffer = env->GetByteArrayElements(javaIconRawData, NULL);
size_t bufferLen = env->GetArrayLength(javaIconRawData);
// Decode bitmap
SkBitmap* iconBitmap = new SkBitmap();
//TODO: JPEG is badly supported! At the moment it needs sdcard to be present (sic). Patch that
if(!SkImageDecoder::DecodeMemory(bitmapBuffer, bufferLen, iconBitmap))
{
// Failed to decode
delete iconBitmap;
rc->nativeOperations.start();
env->ReleaseByteArrayElements(javaIconRawData, bitmapBuffer, JNI_ABORT);
env->DeleteLocalRef(javaIconRawData);
env->DeleteLocalRef(jstr);
throwNewException(env, (std::string("Failed to decode ") + bitmapResource).c_str());
return NULL;
}
cachedBitmaps[bitmapResource] = iconBitmap;
rc->nativeOperations.start();
env->ReleaseByteArrayElements(javaIconRawData, bitmapBuffer, JNI_ABORT);
env->DeleteLocalRef(javaIconRawData);
env->DeleteLocalRef(jstr);
return iconBitmap;
}