From 0d3d642371888137c26490fed438cbc7aeb499b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 5 Jun 2017 17:32:49 +0200 Subject: [PATCH] It works! --- UI/GameSettingsScreen.cpp | 2 +- UI/MiscScreens.cpp | 6 +- android/jni/app-android.cpp | 4 +- android/jni/app-android.h | 3 +- .../src/org/ppsspp/ppsspp/TextRenderer.java | 73 ++++++++++--------- ext/native/gfx_es2/draw_text.cpp | 4 +- ext/native/gfx_es2/draw_text_android.cpp | 67 +++++++++-------- ext/native/gfx_es2/draw_text_android.h | 5 +- ext/native/thin3d/thin3d_gl.cpp | 1 + 9 files changed, 89 insertions(+), 76 deletions(-) diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index cce054af0f..03c8f8a21a 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -676,7 +676,7 @@ void GameSettingsScreen::CreateViews() { systemSettings->Add(new ItemHeader(sy->T("General"))); -#ifdef __ANDROID__ +#if PPSSPP_PLATFORM(ANDROID) if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_MOBILE) { static const char *screenRotation[] = {"Auto", "Landscape", "Portrait", "Landscape Reversed", "Portrait Reversed"}; PopupMultiChoice *rot = systemSettings->Add(new PopupMultiChoice(&g_Config.iScreenRotation, co->T("Screen Rotation"), screenRotation, 0, ARRAY_SIZE(screenRotation), co->GetName(), screenManager())); diff --git a/UI/MiscScreens.cpp b/UI/MiscScreens.cpp index 2a3a927a4f..b49f919e84 100644 --- a/UI/MiscScreens.cpp +++ b/UI/MiscScreens.cpp @@ -345,16 +345,16 @@ NewLanguageScreen::NewLanguageScreen(const std::string &title) : ListPopupScreen continue; } -#ifndef _WIN32 - // ar_AE only works on Windows. +#if !(defined(USING_QT_UI) || PPSSPP_PLATFORM(WINDOWS) || PPSSPP_PLATFORM(ANDROID)) if (tempLangs[i].name.find("ar_AE") != std::string::npos) { continue; } - // Farsi also only works on Windows. + if (tempLangs[i].name.find("fa_IR") != std::string::npos) { continue; } #endif + FileInfo lang = tempLangs[i]; langs_.push_back(lang); diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index b5762e3469..9d2acf8275 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -659,7 +659,7 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_shutdown(JNIEnv *, jclass) { // JavaEGL extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayInit(JNIEnv * env, jobject obj) { // Need to get the local JNI env for the graphics thread. Used later in draw_text_android. - int res = javaVM->GetEnv((void **)&jniEnvGraphics, JNI_VERSION_1_4); + int res = javaVM->GetEnv((void **)&jniEnvGraphics, JNI_VERSION_1_6); if (res != JNI_OK) { ELOG("GetEnv failed: %d", res); } @@ -1031,7 +1031,7 @@ extern "C" bool JNICALL Java_org_ppsspp_ppsspp_NativeActivity_runEGLRenderLoop(J ANativeWindow *wnd = ANativeWindow_fromSurface(env, _surf); // Need to get the local JNI env for the graphics thread. Used later in draw_text_android. - int res = javaVM->GetEnv((void **)&jniEnvGraphics, JNI_VERSION_1_4); + int res = javaVM->GetEnv((void **)&jniEnvGraphics, JNI_VERSION_1_6); if (res != JNI_OK) { ELOG("GetEnv failed: %d", res); } diff --git a/android/jni/app-android.h b/android/jni/app-android.h index 00eba6e6ef..04a849b1bc 100644 --- a/android/jni/app-android.h +++ b/android/jni/app-android.h @@ -8,5 +8,6 @@ extern JNIEnv *jniEnvMain; extern JNIEnv *jniEnvGraphics; +extern JavaVM *javaVM; -#endif \ No newline at end of file +#endif diff --git a/android/src/org/ppsspp/ppsspp/TextRenderer.java b/android/src/org/ppsspp/ppsspp/TextRenderer.java index cd8ced4cd4..87631afe4f 100644 --- a/android/src/org/ppsspp/ppsspp/TextRenderer.java +++ b/android/src/org/ppsspp/ppsspp/TextRenderer.java @@ -4,43 +4,46 @@ import android.graphics.*; import java.nio.ByteBuffer; public class TextRenderer { - public static int measureText(String string, float textSize) { - Paint p; - p = new Paint(Paint.ANTI_ALIAS_FLAG); - Rect bound = new Rect(); - p.setTextSize(textSize); - p.getTextBounds(string, 0, string.length(), bound); - return (bound.width() << 16) | bound.height(); - } - public static short[] renderText(String string, float textSize) { - Paint p; - p = new Paint(Paint.ANTI_ALIAS_FLAG); - Rect bound = new Rect(); - p.setTextSize(textSize); - p.getTextBounds(string, 0, string.length(), bound); - float baseline = -p.ascent(); - Bitmap bmp = Bitmap.createBitmap(bound.width(), bound.height(), Bitmap.Config.ARGB_4444); - Canvas canvas = new Canvas(bmp); + private static Paint p; + private static Paint bg; + static { + p = new Paint(Paint.SUBPIXEL_TEXT_FLAG | Paint.ANTI_ALIAS_FLAG); p.setColor(Color.WHITE); - canvas.drawText(string, 0, baseline, p); + bg = new Paint(); + bg.setColor(Color.BLACK); + } + public static int measureText(String string, double textSize) { + Rect bound = new Rect(); + p.setTextSize((float)textSize); + p.getTextBounds(string, 0, string.length(), bound); + int w = bound.width(); + int h = bound.height(); + // Round width up to even already here to avoid annoyances from odd-width 16-bit textures which + // OpenGL does not like - each line must be 4-byte aligned + w = (w + 3) & ~1; + h += 2; + return (w << 16) | h; + } + public static int[] renderText(String string, double textSize) { + Rect bound = new Rect(); + p.setTextSize((float)textSize); + p.getTextBounds(string, 0, string.length(), bound); + int w = bound.width(); + int h = bound.height(); + // Round width up to even already here to avoid annoyances from odd-width 16-bit textures which + // OpenGL does not like - each line must be 4-byte aligned + w = (w + 3) & ~1; + h += 2; - int bufSize = bmp.getRowBytes() * bmp.getHeight() * 2; // 2 = sizeof(ARGB_4444) - ByteBuffer buf = ByteBuffer.allocate(bufSize); - bmp.copyPixelsFromBuffer(buf); - byte[] bytes = buf.array(); + float baseline = -p.ascent(); + Bitmap bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bmp); + canvas.drawRect(0.0f, 0.0f, w, h, bg); + p.setColor(Color.WHITE); + canvas.drawText(string, 1, -bound.top + 1, p); - // Output array size must match return value of measureText - short[] output = new short[bound.width() * bound.height()]; - - // 16-bit pixels but stored as bytes. - for (int y = 0; y < bound.height(); y++) { - int srcOffset = y * bmp.getRowBytes(); - int dstOffset = y * bound.width(); - for (int x = 0; x < bound.width(); x++) { - int val = bytes[srcOffset + x * 2]; - output[dstOffset + x] = (short)val; - } - } - return output; + int [] pixels = new int[w * h]; + bmp.getPixels(pixels, 0, w, 0, 0, w, h); + return pixels; } } diff --git a/ext/native/gfx_es2/draw_text.cpp b/ext/native/gfx_es2/draw_text.cpp index b99baec6b3..70f9052faf 100644 --- a/ext/native/gfx_es2/draw_text.cpp +++ b/ext/native/gfx_es2/draw_text.cpp @@ -35,9 +35,11 @@ void TextDrawer::SetFontScale(float xscale, float yscale) { float TextDrawer::CalculateDPIScale() { float scale = g_dpi_scale; +#if !PPSSPP_PLATFORM(ANDROID) if (scale >= 1.0f) { scale = 1.0f; } +#endif return scale; } @@ -55,4 +57,4 @@ TextDrawer *TextDrawer::Create(Draw::DrawContext *draw) { drawer = nullptr; } return drawer; -} \ No newline at end of file +} diff --git a/ext/native/gfx_es2/draw_text_android.cpp b/ext/native/gfx_es2/draw_text_android.cpp index 253cf29cc4..434d459ac3 100644 --- a/ext/native/gfx_es2/draw_text_android.cpp +++ b/ext/native/gfx_es2/draw_text_android.cpp @@ -9,6 +9,7 @@ #include "gfx_es2/draw_text_android.h" #include "android/jni/app-android.h" +#include #if PPSSPP_PLATFORM(ANDROID) @@ -17,20 +18,25 @@ TextDrawerAndroid::TextDrawerAndroid(Draw::DrawContext *draw) : TextDrawer(draw) { env_ = jniEnvGraphics; const char *textRendererClassName = "org/ppsspp/ppsspp/TextRenderer"; - cls_textRenderer = env_->FindClass(textRendererClassName); + jclass localClass = env_->FindClass(textRendererClassName); + cls_textRenderer = reinterpret_cast(env_->NewGlobalRef(localClass)); ILOG("cls_textRender: %p", cls_textRenderer); if (cls_textRenderer) { - method_measureText = env_->GetStaticMethodID(cls_textRenderer, "measureText", "(Ljava/lang/String;F)I"); + method_measureText = env_->GetStaticMethodID(cls_textRenderer, "measureText", "(Ljava/lang/String;D)I"); ILOG("method_measureText: %p", method_measureText); - method_renderText = env_->GetStaticMethodID(cls_textRenderer, "renderText", "(Ljava/lang/String;F)[S"); + method_renderText = env_->GetStaticMethodID(cls_textRenderer, "renderText", "(Ljava/lang/String;D)[I"); ILOG("method_renderText: %p", method_renderText); } else { - ELOG("Failed to find class: %s", textRendererClassName); + ELOG("Failed to find class: '%s'", textRendererClassName); } curSize_ = 12; + dpiScale_ = 1.0f; } TextDrawerAndroid::~TextDrawerAndroid() { + // Not sure why we can't do this but it crashes. + // At worst we leak one ref... + // env_->DeleteGlobalRef(cls_textRenderer); ClearCache(); } @@ -50,9 +56,10 @@ uint32_t TextDrawerAndroid::SetFont(const char *fontName, int size, int flags) { return fontHash; } - curSize_ = size; + curSize_ = (float)((6 + size) / dpiScale_) * 96.0f / 72.f; AndroidFontEntry entry; entry.size = curSize_; + fontMap_[fontHash] = entry; fontHash_ = fontHash; return fontHash; @@ -62,7 +69,7 @@ void TextDrawerAndroid::SetFont(uint32_t fontHandle) { uint32_t fontHash = fontHandle; auto iter = fontMap_.find(fontHash); if (iter != fontMap_.end()) { - curSize_ = iter->second.size; + curSize_ = iter->second.size / dpiScale_; } } @@ -74,9 +81,9 @@ void TextDrawerAndroid::MeasureString(const char *str, size_t len, float *w, flo std::string stdstring(str, len); jstring jstr = env_->NewStringUTF(stdstring.c_str()); uint32_t size = env_->CallStaticIntMethod(cls_textRenderer, method_measureText, jstr, curSize_); - *w = (size >> 16) * fontScaleX_; - *h = (size & 0xFFFF) * fontScaleY_; env_->DeleteLocalRef(jstr); + *w = (size >> 16) * fontScaleX_ * dpiScale_; + *h = (size & 0xFFFF) * fontScaleY_ * dpiScale_; } void TextDrawerAndroid::MeasureStringRect(const char *str, size_t len, const Bounds &bounds, float *w, float *h, int align) { @@ -88,15 +95,18 @@ void TextDrawerAndroid::MeasureStringRect(const char *str, size_t len, const Bou jstring jstr = env_->NewStringUTF(toMeasure.c_str()); uint32_t size = env_->CallStaticIntMethod(cls_textRenderer, method_measureText, jstr, curSize_); - *w = (size >> 16) * fontScaleX_; - *h = (size & 0xFFFF) * fontScaleY_; env_->DeleteLocalRef(jstr); + *w = (size >> 16) * fontScaleX_ * dpiScale_; + *h = (size & 0xFFFF) * fontScaleY_ * dpiScale_; } void TextDrawerAndroid::DrawString(DrawBuffer &target, const char *str, float x, float y, uint32_t color, int align) { using namespace Draw; if (!strlen(str)) return; + JNIEnv *env; + int result = javaVM->GetEnv((void **)&env, JNI_VERSION_1_6); + assert(env == env_); uint32_t stringHash = hash::Fletcher((const uint8_t *)str, strlen(str)); uint32_t entryHash = stringHash ^ fontHash_ ^ (align << 24); @@ -112,10 +122,12 @@ void TextDrawerAndroid::DrawString(DrawBuffer &target, const char *str, float x, draw_->BindTexture(0, entry->texture); } else { jstring jstr = env_->NewStringUTF(str); + int len = (int)env_->GetStringUTFLength(jstr); + ILOG("UTF len: %d", len); uint32_t size = env_->CallStaticIntMethod(cls_textRenderer, method_measureText, jstr, curSize_); - int imageWidth = (size >> 16) * fontScaleX_; - int imageHeight = (size & 0xFFFF) * fontScaleY_; - jshortArray imageData = (jshortArray)env_->CallStaticObjectMethod(cls_textRenderer, method_renderText, jstr, curSize_); + int imageWidth = (size >> 16); + int imageHeight = (size & 0xFFFF); + jintArray imageData = (jintArray)env_->CallStaticObjectMethod(cls_textRenderer, method_renderText, jstr, curSize_); env_->DeleteLocalRef(jstr); entry = new TextStringEntry(); @@ -132,23 +144,25 @@ void TextDrawerAndroid::DrawString(DrawBuffer &target, const char *str, float x, desc.mipLevels = 1; uint16_t *bitmapData = new uint16_t[entry->bmWidth * entry->bmHeight]; - jshort* jimage = env_->GetShortArrayElements(imageData, nullptr); + jint* jimage = env_->GetIntArrayElements(imageData, nullptr); + int arraySize = env_->GetArrayLength(imageData); + assert(arraySize == imageWidth * imageHeight); for (int x = 0; x < entry->bmWidth; x++) { for (int y = 0; y < entry->bmHeight; y++) { - int v = jimage[imageWidth * y + x]; - v = (v << 4) | v; - v = (v << 8) | v; - bitmapData[entry->bmWidth * y + x] = v; + uint32_t v = jimage[imageWidth * y + x]; + v = 0xFFF0 | ((v >> 12) & 0xF); // Just grab some bits from the green channel. + bitmapData[entry->bmWidth * y + x] = (uint16_t)v; } } - env_->ReleaseShortArrayElements(imageData, jimage, 0); + env_->ReleaseIntArrayElements(imageData, jimage, 0); desc.initData.push_back((uint8_t *)bitmapData); entry->texture = draw_->CreateTexture(desc); delete[] bitmapData; cache_[entryHash] = std::unique_ptr(entry); + draw_->BindTexture(0, entry->texture); } - float w = entry->bmWidth * fontScaleX_; - float h = entry->bmHeight * fontScaleY_; + float w = entry->bmWidth * fontScaleX_ * dpiScale_; + float h = entry->bmHeight * fontScaleY_ * dpiScale_; DrawBuffer::DoAlign(align, &x, &y, &w, &h); target.DrawTexRect(x, y, x + w, y + h, 0.0f, 0.0f, 1.0f, 1.0f, color); target.Flush(true); @@ -160,7 +174,7 @@ void TextDrawerAndroid::ClearCache() { iter.second->texture->Release(); } cache_.clear(); - sizeCache_.clear(); + fontMap_.clear(); } void TextDrawerAndroid::DrawStringRect(DrawBuffer &target, const char *str, const Bounds &bounds, uint32_t color, int align) { @@ -191,6 +205,7 @@ void TextDrawerAndroid::OncePerFrame() { // If DPI changed (small-mode, future proper monitor DPI support), drop everything. float newDpiScale = CalculateDPIScale(); if (newDpiScale != dpiScale_) { + ILOG("Scale changed - recreating fonts"); dpiScale_ = newDpiScale; ClearCache(); RecreateFonts(); @@ -207,14 +222,6 @@ void TextDrawerAndroid::OncePerFrame() { iter++; } } - - for (auto iter = sizeCache_.begin(); iter != sizeCache_.end(); ) { - if (frameCount_ - iter->second->lastUsedFrame > 100) { - sizeCache_.erase(iter++); - } else { - iter++; - } - } } } diff --git a/ext/native/gfx_es2/draw_text_android.h b/ext/native/gfx_es2/draw_text_android.h index a03b5d5919..ad9f37c516 100644 --- a/ext/native/gfx_es2/draw_text_android.h +++ b/ext/native/gfx_es2/draw_text_android.h @@ -37,7 +37,7 @@ protected: jclass cls_textRenderer; jmethodID method_measureText; jmethodID method_renderText; - float curSize_; + double curSize_; uint32_t fontHash_; @@ -45,7 +45,6 @@ protected: // The key is the CityHash of the string xor the fontHash_. std::map> cache_; - std::map> sizeCache_; }; -#endif \ No newline at end of file +#endif diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index 584eaeb20d..7aeba44140 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -675,6 +675,7 @@ OpenGLTexture::OpenGLTexture(const TextureDesc &desc) { type_ = desc.type; target_ = TypeToTarget(desc.type); canWrap_ = isPowerOf2(width_) && isPowerOf2(height_); + mipLevels_ = desc.mipLevels; if (!desc.initData.size()) return;