From b5ed8de9e5446b5f57767cb5eb1325044bae8835 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 5 Jun 2017 13:39:06 +0200 Subject: [PATCH 01/11] Warning fix --- GPU/Debugger/Record.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GPU/Debugger/Record.cpp b/GPU/Debugger/Record.cpp index e5a85b6a44..7140e0b4a6 100644 --- a/GPU/Debugger/Record.cpp +++ b/GPU/Debugger/Record.cpp @@ -966,7 +966,7 @@ static bool ExecuteCommands() { break; default: - ERROR_LOG(SYSTEM, "Unsupported GE dump command: %d", cmd.type); + ERROR_LOG(SYSTEM, "Unsupported GE dump command: %d", (int)cmd.type); return false; } } From 871260fcc4db07d85897ecaac4d568f2e00db17c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sat, 3 Jun 2017 21:18:50 +0200 Subject: [PATCH 02/11] Start sketching native text rendering for Android --- CMakeLists.txt | 2 + android/jni/app-android.cpp | 3 +- android/jni/app-android.h | 13 +- .../src/org/ppsspp/ppsspp/TextRenderer.java | 48 ++++ ext/native/gfx_es2/draw_text.cpp | 3 + ext/native/gfx_es2/draw_text_android.cpp | 208 ++++++++++++++++++ ext/native/gfx_es2/draw_text_android.h | 50 +++++ ext/native/native.vcxproj | 2 + ext/native/native.vcxproj.filters | 6 + 9 files changed, 329 insertions(+), 6 deletions(-) create mode 100644 android/src/org/ppsspp/ppsspp/TextRenderer.java create mode 100644 ext/native/gfx_es2/draw_text_android.cpp create mode 100644 ext/native/gfx_es2/draw_text_android.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 6985fa0f12..4d79c4444a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -867,6 +867,8 @@ add_library(native STATIC ext/native/gfx_es2/draw_text_win.h ext/native/gfx_es2/draw_text_qt.cpp ext/native/gfx_es2/draw_text_qt.h + ext/native/gfx_es2/draw_text_android.cpp + ext/native/gfx_es2/draw_text_android.h ext/native/gfx_es2/gpu_features.cpp ext/native/gfx_es2/gpu_features.h ext/native/gfx_es2/glsl_program.cpp diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 64f2892074..24ba1eaf33 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -28,6 +28,7 @@ #include "thread/threadutil.h" #include "file/zip_read.h" #include "input/input_state.h" +#include "input/keycodes.h" #include "profiler/profiler.h" #include "math/math_util.h" #include "net/resolve.h" @@ -49,7 +50,7 @@ #include "app-android.h" -static JNIEnv *jniEnvUI; +JNIEnv *jniEnvUI; enum { ANDROID_VERSION_GINGERBREAD = 9, diff --git a/android/jni/app-android.h b/android/jni/app-android.h index bf842ac54a..a17be4b8b0 100644 --- a/android/jni/app-android.h +++ b/android/jni/app-android.h @@ -1,8 +1,11 @@ #pragma once -#include "input/keycodes.h" +#include "ppsspp_config.h" -// Compatability we alias the keycodes -// since native's keycodes are based on -// android keycodes. -typedef enum _keycode_t AndroidKeyCodes; +#if PPSSPP_PLATFORM(ANDROID) + +#include + +extern JNIEnv *jniEnvUI; + +#endif \ No newline at end of file diff --git a/android/src/org/ppsspp/ppsspp/TextRenderer.java b/android/src/org/ppsspp/ppsspp/TextRenderer.java new file mode 100644 index 0000000000..78873535c8 --- /dev/null +++ b/android/src/org/ppsspp/ppsspp/TextRenderer.java @@ -0,0 +1,48 @@ +package org.ppsspp.ppsspp; +import android.graphics.*; +import android.graphics.drawable.*; + +import java.nio.ByteBuffer; + +public class TextRenderer { + 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); + int packedBounds = (bound.width() << 16) | bound.height(); + return packedBounds; + } + 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); + Bitmap bmp = Bitmap.createBitmap(bound.width(), bound.height(), Bitmap.Config.ARGB_4444); + Canvas canvas = new Canvas(bmp); + p.setColor(Color.WHITE); + canvas.drawText(string, 0, 0, p); + + int bufSize = bmp.getRowBytes() * bmp.getHeight() * 2; // 2 = sizeof(ARGB_4444) + ByteBuffer buf = ByteBuffer.allocate(bufSize); + bmp.copyPixelsFromBuffer(buf); + byte[] bytes = buf.array(); + + // 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]; + val = (val << 12) | 0xFFF; + output[dstOffset + x] = (short)val; + } + } + return output; + } +} diff --git a/ext/native/gfx_es2/draw_text.cpp b/ext/native/gfx_es2/draw_text.cpp index 1a49c2f0ab..9cde47ec47 100644 --- a/ext/native/gfx_es2/draw_text.cpp +++ b/ext/native/gfx_es2/draw_text.cpp @@ -8,6 +8,7 @@ #include "gfx_es2/draw_text.h" #include "gfx_es2/draw_text_win.h" #include "gfx_es2/draw_text_qt.h" +#include "gfx_es2/draw_text_android.h" TextDrawer::TextDrawer(Draw::DrawContext *draw) : draw_(draw) { // These probably shouldn't be state. @@ -45,6 +46,8 @@ TextDrawer *TextDrawer::Create(Draw::DrawContext *draw) { return new TextDrawerWin32(draw); #elif defined(USING_QT_UI) return new TextDrawerQt(draw); +#elif PPSSPP_PLATFORM(ANDROID) + return new TextDrawerAndroid(draw); #else return nullptr; #endif diff --git a/ext/native/gfx_es2/draw_text_android.cpp b/ext/native/gfx_es2/draw_text_android.cpp new file mode 100644 index 0000000000..5ab16a3583 --- /dev/null +++ b/ext/native/gfx_es2/draw_text_android.cpp @@ -0,0 +1,208 @@ +#include "base/display.h" +#include "base/logging.h" +#include "base/stringutil.h" +#include "thin3d/thin3d.h" +#include "util/hash/hash.h" +#include "util/text/wrap_text.h" +#include "util/text/utf8.h" +#include "gfx_es2/draw_text.h" +#include "gfx_es2/draw_text_android.h" + +#include "android/jni/app-android.h" + +#if PPSSPP_PLATFORM(ANDROID) + +#include + +TextDrawerAndroid::TextDrawerAndroid(Draw::DrawContext *draw) : TextDrawer(draw) { + env_ = jniEnvUI; + cls_textRenderer = env_->FindClass("org/ppsspp/ppsspp/TextRenderer"); + method_measureText = env_->GetStaticMethodID(cls_textRenderer, "measureText", "(ILjava/lang/String;F"); + method_renderText = env_->GetStaticMethodID(cls_textRenderer, "renderText", "([SLjava/lang/String;F"); + ILOG("method_measureText: %p", method_measureText); + ILOG("method_renderText: %p", method_renderText); + curSize_ = 12; +} + +TextDrawerAndroid::~TextDrawerAndroid() { + ClearCache(); +} + +uint32_t TextDrawerAndroid::SetFont(const char *fontName, int size, int flags) { + // We will only use the default font + uint32_t fontHash = 0; //hash::Fletcher((const uint8_t *)fontName, strlen(fontName)); + fontHash ^= size; + fontHash ^= flags << 10; + + auto iter = fontMap_.find(fontHash); + if (iter != fontMap_.end()) { + fontHash_ = fontHash; + return fontHash; + } + + curSize_ = size; + AndroidFontEntry entry; + entry.size = curSize_; + fontMap_[fontHash] = entry; + fontHash_ = fontHash; + return fontHash; +} + +void TextDrawerAndroid::SetFont(uint32_t fontHandle) { + uint32_t fontHash = fontHandle; + auto iter = fontMap_.find(fontHash); + if (iter != fontMap_.end()) { + curSize_ = iter->second.size; + } +} + +void TextDrawerAndroid::RecreateFonts() { + +} + +void TextDrawerAndroid::MeasureString(const char *str, size_t len, float *w, float *h) { + 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); +} + +void TextDrawerAndroid::MeasureStringRect(const char *str, size_t len, const Bounds &bounds, float *w, float *h, int align) { + std::string toMeasure = std::string(str, len); + if (align & FLAG_WRAP_TEXT) { + bool rotated = (align & (ROTATE_90DEG_LEFT | ROTATE_90DEG_RIGHT)) != 0; + WrapString(toMeasure, toMeasure.c_str(), rotated ? bounds.h : bounds.w); + } + + 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); +} + +void TextDrawerAndroid::DrawString(DrawBuffer &target, const char *str, float x, float y, uint32_t color, int align) { + using namespace Draw; + if (!strlen(str)) + return; + + uint32_t stringHash = hash::Fletcher((const uint8_t *)str, strlen(str)); + uint32_t entryHash = stringHash ^ fontHash_ ^ (align << 24); + + target.Flush(true); + + TextStringEntry *entry; + + auto iter = cache_.find(entryHash); + if (iter != cache_.end()) { + entry = iter->second.get(); + entry->lastUsedFrame = frameCount_; + draw_->BindTexture(0, entry->texture); + } else { + jstring jstr = env_->NewStringUTF(str); + 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_->CallObjectMethod(cls_textRenderer, method_renderText, jstr, curSize_); + env_->DeleteLocalRef(jstr); + + entry = new TextStringEntry(); + entry->bmWidth = entry->width = imageWidth; + entry->bmHeight = entry->height = imageHeight; + entry->lastUsedFrame = frameCount_; + + TextureDesc desc{}; + desc.type = TextureType::LINEAR2D; + desc.format = Draw::DataFormat::B4G4R4A4_UNORM_PACK16; + desc.width = entry->bmWidth; + desc.height = entry->bmHeight; + desc.depth = 1; + desc.mipLevels = 1; + + uint16_t *bitmapData = new uint16_t[entry->bmWidth * entry->bmHeight]; + jshort* jimage = env_->GetShortArrayElements(imageData, nullptr); + for (int x = 0; x < entry->bmWidth; x++) { + for (int y = 0; y < entry->bmHeight; y++) { + bitmapData[entry->bmWidth * y + x] = jimage[imageWidth * y + x]; + } + } + env_->ReleaseShortArrayElements(imageData, jimage, 0); + desc.initData.push_back((uint8_t *)bitmapData); + entry->texture = draw_->CreateTexture(desc); + delete[] bitmapData; + cache_[entryHash] = std::unique_ptr(entry); + } + float w = entry->bmWidth * fontScaleX_; + float h = entry->bmHeight * fontScaleY_; + 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); +} + +void TextDrawerAndroid::ClearCache() { + for (auto &iter : cache_) { + if (iter.second->texture) + iter.second->texture->Release(); + } + cache_.clear(); + sizeCache_.clear(); +} + +void TextDrawerAndroid::DrawStringRect(DrawBuffer &target, const char *str, const Bounds &bounds, uint32_t color, int align) { + float x = bounds.x; + float y = bounds.y; + if (align & ALIGN_HCENTER) { + x = bounds.centerX(); + } else if (align & ALIGN_RIGHT) { + x = bounds.x2(); + } + if (align & ALIGN_VCENTER) { + y = bounds.centerY(); + } else if (align & ALIGN_BOTTOM) { + y = bounds.y2(); + } + + std::string toDraw = str; + if (align & FLAG_WRAP_TEXT) { + bool rotated = (align & (ROTATE_90DEG_LEFT | ROTATE_90DEG_RIGHT)) != 0; + WrapString(toDraw, str, rotated ? bounds.h : bounds.w); + } + + DrawString(target, toDraw.c_str(), x, y, color, align); +} + +void TextDrawerAndroid::OncePerFrame() { + frameCount_++; + // If DPI changed (small-mode, future proper monitor DPI support), drop everything. + float newDpiScale = CalculateDPIScale(); + if (newDpiScale != dpiScale_) { + dpiScale_ = newDpiScale; + ClearCache(); + RecreateFonts(); + } + + // Drop old strings. Use a prime number to reduce clashing with other rhythms + if (frameCount_ % 23 == 0) { + for (auto iter = cache_.begin(); iter != cache_.end();) { + if (frameCount_ - iter->second->lastUsedFrame > 100) { + if (iter->second->texture) + iter->second->texture->Release(); + cache_.erase(iter++); + } else { + iter++; + } + } + + for (auto iter = sizeCache_.begin(); iter != sizeCache_.end(); ) { + if (frameCount_ - iter->second->lastUsedFrame > 100) { + sizeCache_.erase(iter++); + } else { + iter++; + } + } + } +} + +#endif diff --git a/ext/native/gfx_es2/draw_text_android.h b/ext/native/gfx_es2/draw_text_android.h new file mode 100644 index 0000000000..3a51b2f34e --- /dev/null +++ b/ext/native/gfx_es2/draw_text_android.h @@ -0,0 +1,50 @@ +#pragma once + +#include "ppsspp_config.h" + +#include +#include "gfx_es2/draw_text.h" + +#if PPSSPP_PLATFORM(ANDROID) + +#include + +struct AndroidFontEntry { + float size; +}; + +class TextDrawerAndroid : public TextDrawer { +public: + TextDrawerAndroid(Draw::DrawContext *draw); + ~TextDrawerAndroid(); + + uint32_t SetFont(const char *fontName, int size, int flags) override; + void SetFont(uint32_t fontHandle) override; // Shortcut once you've set the font once. + void MeasureString(const char *str, size_t len, float *w, float *h) override; + void MeasureStringRect(const char *str, size_t len, const Bounds &bounds, float *w, float *h, int align = ALIGN_TOPLEFT) override; + void DrawString(DrawBuffer &target, const char *str, float x, float y, uint32_t color, int align = ALIGN_TOPLEFT) override; + void DrawStringRect(DrawBuffer &target, const char *str, const Bounds &bounds, uint32_t color, int align) override; + // Use for housekeeping like throwing out old strings. + void OncePerFrame() override; + +protected: + void ClearCache() override; + void RecreateFonts() override; // On DPI change + + // JNI functions + JNIEnv *env_; + jclass cls_textRenderer; + jmethodID method_measureText; + jmethodID method_renderText; + float curSize_; + + uint32_t fontHash_; + + std::map fontMap_; + + // The key is the CityHash of the string xor the fontHash_. + std::map> cache_; + std::map> sizeCache_; +}; + +#endif \ No newline at end of file diff --git a/ext/native/native.vcxproj b/ext/native/native.vcxproj index 12cd0e4b3d..34f5e2aeb8 100644 --- a/ext/native/native.vcxproj +++ b/ext/native/native.vcxproj @@ -237,6 +237,7 @@ + @@ -692,6 +693,7 @@ + diff --git a/ext/native/native.vcxproj.filters b/ext/native/native.vcxproj.filters index b1718027c9..ee8f08f807 100644 --- a/ext/native/native.vcxproj.filters +++ b/ext/native/native.vcxproj.filters @@ -320,6 +320,9 @@ gfx + + gfx + @@ -778,6 +781,9 @@ gfx + + gfx + From d9c43642c2c6563046a87e2deb733270e4f1a4af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 5 Jun 2017 13:04:30 +0200 Subject: [PATCH 03/11] Fix assorted JNI issues, the calls work now. --- android/jni/app-android.cpp | 20 ++++++++++++++-- android/jni/app-android.h | 3 ++- .../src/org/ppsspp/ppsspp/TextRenderer.java | 8 +++---- ext/native/gfx_es2/draw_text.cpp | 14 +++++++---- ext/native/gfx_es2/draw_text.h | 1 + ext/native/gfx_es2/draw_text_android.cpp | 24 +++++++++++++------ ext/native/gfx_es2/draw_text_android.h | 1 + 7 files changed, 51 insertions(+), 20 deletions(-) diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 24ba1eaf33..b5762e3469 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -50,7 +50,9 @@ #include "app-android.h" -JNIEnv *jniEnvUI; +JNIEnv *jniEnvMain; +JNIEnv *jniEnvGraphics; +JavaVM *javaVM; enum { ANDROID_VERSION_GINGERBREAD = 9, @@ -531,7 +533,9 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_init (JNIEnv *env, jclass, jstring jmodel, jint jdeviceType, jstring jlangRegion, jstring japkpath, jstring jdataDir, jstring jexternalDir, jstring jlibraryDir, jstring jcacheDir, jstring jshortcutParam, jint jAndroidVersion, jstring jboard) { - jniEnvUI = env; + jniEnvMain = env; + env->GetJavaVM(&javaVM); + setCurrentThreadName("androidInit"); ILOG("NativeApp.init() -- begin"); @@ -654,6 +658,12 @@ 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); + if (res != JNI_OK) { + ELOG("GetEnv failed: %d", res); + } + if (javaGL && !graphicsContext) { graphicsContext = new AndroidJavaEGLGraphicsContext(); } @@ -1020,6 +1030,12 @@ static void ProcessFrameCommands(JNIEnv *env) { extern "C" bool JNICALL Java_org_ppsspp_ppsspp_NativeActivity_runEGLRenderLoop(JNIEnv *env, jobject obj, jobject _surf) { 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); + if (res != JNI_OK) { + ELOG("GetEnv failed: %d", res); + } + WLOG("runEGLRenderLoop. display_xres=%d display_yres=%d", display_xres, display_yres); if (wnd == nullptr) { diff --git a/android/jni/app-android.h b/android/jni/app-android.h index a17be4b8b0..00eba6e6ef 100644 --- a/android/jni/app-android.h +++ b/android/jni/app-android.h @@ -6,6 +6,7 @@ #include -extern JNIEnv *jniEnvUI; +extern JNIEnv *jniEnvMain; +extern JNIEnv *jniEnvGraphics; #endif \ No newline at end of file diff --git a/android/src/org/ppsspp/ppsspp/TextRenderer.java b/android/src/org/ppsspp/ppsspp/TextRenderer.java index 78873535c8..db8f6929da 100644 --- a/android/src/org/ppsspp/ppsspp/TextRenderer.java +++ b/android/src/org/ppsspp/ppsspp/TextRenderer.java @@ -1,20 +1,18 @@ package org.ppsspp.ppsspp; import android.graphics.*; -import android.graphics.drawable.*; import java.nio.ByteBuffer; public class TextRenderer { - static int measureText(String string, float textSize) { + 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); - int packedBounds = (bound.width() << 16) | bound.height(); - return packedBounds; + return (bound.width() << 16) | bound.height(); } - static short[] renderText(String string, float textSize) { + public static short[] renderText(String string, float textSize) { Paint p; p = new Paint(Paint.ANTI_ALIAS_FLAG); Rect bound = new Rect(); diff --git a/ext/native/gfx_es2/draw_text.cpp b/ext/native/gfx_es2/draw_text.cpp index 9cde47ec47..b99baec6b3 100644 --- a/ext/native/gfx_es2/draw_text.cpp +++ b/ext/native/gfx_es2/draw_text.cpp @@ -42,13 +42,17 @@ float TextDrawer::CalculateDPIScale() { } TextDrawer *TextDrawer::Create(Draw::DrawContext *draw) { + TextDrawer *drawer = nullptr; #if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) - return new TextDrawerWin32(draw); + drawer = new TextDrawerWin32(draw); #elif defined(USING_QT_UI) - return new TextDrawerQt(draw); + drawer = new TextDrawerQt(draw); #elif PPSSPP_PLATFORM(ANDROID) - return new TextDrawerAndroid(draw); -#else - return nullptr; + drawer = new TextDrawerAndroid(draw); #endif + if (drawer && !drawer->IsReady()) { + delete drawer; + drawer = nullptr; + } + return drawer; } \ No newline at end of file diff --git a/ext/native/gfx_es2/draw_text.h b/ext/native/gfx_es2/draw_text.h index 186e43d900..4751d12a67 100644 --- a/ext/native/gfx_es2/draw_text.h +++ b/ext/native/gfx_es2/draw_text.h @@ -51,6 +51,7 @@ class TextDrawer { public: virtual ~TextDrawer(); + virtual bool IsReady() const { return true; } virtual uint32_t SetFont(const char *fontName, int size, int flags) = 0; virtual void SetFont(uint32_t fontHandle) = 0; // Shortcut once you've set the font once. void SetFontScale(float xscale, float yscale); diff --git a/ext/native/gfx_es2/draw_text_android.cpp b/ext/native/gfx_es2/draw_text_android.cpp index 5ab16a3583..885a51e35a 100644 --- a/ext/native/gfx_es2/draw_text_android.cpp +++ b/ext/native/gfx_es2/draw_text_android.cpp @@ -15,12 +15,18 @@ #include TextDrawerAndroid::TextDrawerAndroid(Draw::DrawContext *draw) : TextDrawer(draw) { - env_ = jniEnvUI; - cls_textRenderer = env_->FindClass("org/ppsspp/ppsspp/TextRenderer"); - method_measureText = env_->GetStaticMethodID(cls_textRenderer, "measureText", "(ILjava/lang/String;F"); - method_renderText = env_->GetStaticMethodID(cls_textRenderer, "renderText", "([SLjava/lang/String;F"); - ILOG("method_measureText: %p", method_measureText); - ILOG("method_renderText: %p", method_renderText); + env_ = jniEnvGraphics; + const char *textRendererClassName = "org/ppsspp/ppsspp/TextRenderer"; + cls_textRenderer = env_->FindClass(textRendererClassName); + ILOG("cls_textRender: %p", cls_textRenderer); + if (cls_textRenderer) { + method_measureText = env_->GetStaticMethodID(cls_textRenderer, "measureText", "(Ljava/lang/String;F)I"); + ILOG("method_measureText: %p", method_measureText); + method_renderText = env_->GetStaticMethodID(cls_textRenderer, "renderText", "(Ljava/lang/String;F)[S"); + ILOG("method_renderText: %p", method_renderText); + } else { + ELOG("Failed to find class: %s", textRendererClassName); + } curSize_ = 12; } @@ -28,6 +34,10 @@ TextDrawerAndroid::~TextDrawerAndroid() { ClearCache(); } +bool TextDrawerAndroid::IsReady() const { + return cls_textRenderer != nullptr && method_measureText != nullptr && method_renderText != nullptr; +} + uint32_t TextDrawerAndroid::SetFont(const char *fontName, int size, int flags) { // We will only use the default font uint32_t fontHash = 0; //hash::Fletcher((const uint8_t *)fontName, strlen(fontName)); @@ -105,7 +115,7 @@ void TextDrawerAndroid::DrawString(DrawBuffer &target, const char *str, float x, 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_->CallObjectMethod(cls_textRenderer, method_renderText, jstr, curSize_); + jshortArray imageData = (jshortArray)env_->CallStaticObjectMethod(cls_textRenderer, method_renderText, jstr, curSize_); env_->DeleteLocalRef(jstr); entry = new TextStringEntry(); diff --git a/ext/native/gfx_es2/draw_text_android.h b/ext/native/gfx_es2/draw_text_android.h index 3a51b2f34e..a03b5d5919 100644 --- a/ext/native/gfx_es2/draw_text_android.h +++ b/ext/native/gfx_es2/draw_text_android.h @@ -18,6 +18,7 @@ public: TextDrawerAndroid(Draw::DrawContext *draw); ~TextDrawerAndroid(); + bool IsReady() const override; uint32_t SetFont(const char *fontName, int size, int flags) override; void SetFont(uint32_t fontHandle) override; // Shortcut once you've set the font once. void MeasureString(const char *str, size_t len, float *w, float *h) override; From 0bf92a4fa2b0dbd8abef5bc7ce46e8092b5e7e24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 5 Jun 2017 13:22:01 +0200 Subject: [PATCH 04/11] Font rendering code runs but it sure ain't right --- android/src/org/ppsspp/ppsspp/TextRenderer.java | 4 ++-- ext/native/gfx_es2/draw_text_android.cpp | 5 ++++- ext/native/thin3d/thin3d_vulkan.cpp | 3 +++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/android/src/org/ppsspp/ppsspp/TextRenderer.java b/android/src/org/ppsspp/ppsspp/TextRenderer.java index db8f6929da..cd8ced4cd4 100644 --- a/android/src/org/ppsspp/ppsspp/TextRenderer.java +++ b/android/src/org/ppsspp/ppsspp/TextRenderer.java @@ -18,10 +18,11 @@ public class TextRenderer { 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); p.setColor(Color.WHITE); - canvas.drawText(string, 0, 0, p); + canvas.drawText(string, 0, baseline, p); int bufSize = bmp.getRowBytes() * bmp.getHeight() * 2; // 2 = sizeof(ARGB_4444) ByteBuffer buf = ByteBuffer.allocate(bufSize); @@ -37,7 +38,6 @@ public class TextRenderer { int dstOffset = y * bound.width(); for (int x = 0; x < bound.width(); x++) { int val = bytes[srcOffset + x * 2]; - val = (val << 12) | 0xFFF; output[dstOffset + x] = (short)val; } } diff --git a/ext/native/gfx_es2/draw_text_android.cpp b/ext/native/gfx_es2/draw_text_android.cpp index 885a51e35a..253cf29cc4 100644 --- a/ext/native/gfx_es2/draw_text_android.cpp +++ b/ext/native/gfx_es2/draw_text_android.cpp @@ -135,7 +135,10 @@ void TextDrawerAndroid::DrawString(DrawBuffer &target, const char *str, float x, jshort* jimage = env_->GetShortArrayElements(imageData, nullptr); for (int x = 0; x < entry->bmWidth; x++) { for (int y = 0; y < entry->bmHeight; y++) { - bitmapData[entry->bmWidth * y + x] = jimage[imageWidth * y + x]; + int v = jimage[imageWidth * y + x]; + v = (v << 4) | v; + v = (v << 8) | v; + bitmapData[entry->bmWidth * y + x] = v; } } env_->ReleaseShortArrayElements(imageData, jimage, 0); diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index dc31b63459..69c24fa1bc 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -675,6 +675,9 @@ enum class TextureState { }; bool VKTexture::Create(const TextureDesc &desc) { + // Zero-sized textures not allowed. + if (desc.width * desc.height * desc.depth == 0) + return false; format_ = desc.format; mipLevels_ = desc.mipLevels; width_ = desc.width; 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 05/11] 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; From 0af19719cbccb393bef39e794045d551ae9f06e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 5 Jun 2017 23:35:26 +0200 Subject: [PATCH 06/11] Fix vertical positioning, eliminate double-& --- android/src/org/ppsspp/ppsspp/TextRenderer.java | 8 +++----- ext/native/gfx_es2/draw_text_android.cpp | 17 +++++++++++------ ext/native/gfx_es2/draw_text_android.h | 3 +++ 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/android/src/org/ppsspp/ppsspp/TextRenderer.java b/android/src/org/ppsspp/ppsspp/TextRenderer.java index 87631afe4f..ae359a563d 100644 --- a/android/src/org/ppsspp/ppsspp/TextRenderer.java +++ b/android/src/org/ppsspp/ppsspp/TextRenderer.java @@ -17,11 +17,10 @@ public class TextRenderer { p.setTextSize((float)textSize); p.getTextBounds(string, 0, string.length(), bound); int w = bound.width(); - int h = bound.height(); + int h = (int)(p.descent() - p.ascent() + 2.0f); // 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) { @@ -29,18 +28,17 @@ public class TextRenderer { p.setTextSize((float)textSize); p.getTextBounds(string, 0, string.length(), bound); int w = bound.width(); - int h = bound.height(); + int h = (int)(p.descent() - p.ascent() + 2.0f); // 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; 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); + canvas.drawText(string, 1, -p.ascent() + 1, p); int [] pixels = new int[w * h]; bmp.getPixels(pixels, 0, w, 0, 0, w, h); diff --git a/ext/native/gfx_es2/draw_text_android.cpp b/ext/native/gfx_es2/draw_text_android.cpp index 434d459ac3..a6a4ef6941 100644 --- a/ext/native/gfx_es2/draw_text_android.cpp +++ b/ext/native/gfx_es2/draw_text_android.cpp @@ -73,13 +73,17 @@ void TextDrawerAndroid::SetFont(uint32_t fontHandle) { } } +std::string TextDrawerAndroid::NormalizeString(std::string str) { + return ReplaceAll(str, "&&", "&"); +} + void TextDrawerAndroid::RecreateFonts() { } void TextDrawerAndroid::MeasureString(const char *str, size_t len, float *w, float *h) { - std::string stdstring(str, len); - jstring jstr = env_->NewStringUTF(stdstring.c_str()); + std::string text(NormalizeString(std::string(str, len))); + jstring jstr = env_->NewStringUTF(text.c_str()); uint32_t size = env_->CallStaticIntMethod(cls_textRenderer, method_measureText, jstr, curSize_); env_->DeleteLocalRef(jstr); *w = (size >> 16) * fontScaleX_ * dpiScale_; @@ -87,7 +91,7 @@ void TextDrawerAndroid::MeasureString(const char *str, size_t len, float *w, flo } void TextDrawerAndroid::MeasureStringRect(const char *str, size_t len, const Bounds &bounds, float *w, float *h, int align) { - std::string toMeasure = std::string(str, len); + std::string toMeasure(NormalizeString(std::string(str, len))); if (align & FLAG_WRAP_TEXT) { bool rotated = (align & (ROTATE_90DEG_LEFT | ROTATE_90DEG_RIGHT)) != 0; WrapString(toMeasure, toMeasure.c_str(), rotated ? bounds.h : bounds.w); @@ -102,13 +106,14 @@ void TextDrawerAndroid::MeasureStringRect(const char *str, size_t len, const Bou void TextDrawerAndroid::DrawString(DrawBuffer &target, const char *str, float x, float y, uint32_t color, int align) { using namespace Draw; - if (!strlen(str)) + std::string text(NormalizeString(std::string(str))); + if (text.empty()) 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 stringHash = hash::Fletcher((const uint8_t *)text.data(), text.size()); uint32_t entryHash = stringHash ^ fontHash_ ^ (align << 24); target.Flush(true); @@ -121,7 +126,7 @@ void TextDrawerAndroid::DrawString(DrawBuffer &target, const char *str, float x, entry->lastUsedFrame = frameCount_; draw_->BindTexture(0, entry->texture); } else { - jstring jstr = env_->NewStringUTF(str); + jstring jstr = env_->NewStringUTF(text.c_str()); int len = (int)env_->GetStringUTFLength(jstr); ILOG("UTF len: %d", len); uint32_t size = env_->CallStaticIntMethod(cls_textRenderer, method_measureText, jstr, curSize_); diff --git a/ext/native/gfx_es2/draw_text_android.h b/ext/native/gfx_es2/draw_text_android.h index ad9f37c516..6d3962a921 100644 --- a/ext/native/gfx_es2/draw_text_android.h +++ b/ext/native/gfx_es2/draw_text_android.h @@ -32,6 +32,9 @@ protected: void ClearCache() override; void RecreateFonts() override; // On DPI change +private: + std::string NormalizeString(std::string str); + // JNI functions JNIEnv *env_; jclass cls_textRenderer; From 62c747bc004946fd53447ac6e68ea61fd0c62f98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 6 Jun 2017 00:12:13 +0200 Subject: [PATCH 07/11] Buildfix buildbot --- ext/native/Android.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/ext/native/Android.mk b/ext/native/Android.mk index 785b68d8c4..0920922d4a 100644 --- a/ext/native/Android.mk +++ b/ext/native/Android.mk @@ -74,6 +74,7 @@ LOCAL_SRC_FILES :=\ gfx_es2/gl3stub.c \ gfx_es2/draw_buffer.cpp.arm \ gfx_es2/draw_text.cpp.arm \ + gfx_es2/draw_text_android.cpp.arm \ gfx/GLStateCache.cpp.arm \ gfx/gl_debug_log.cpp \ gfx/gl_lost_manager.cpp \ From 3a640ff8aa057e7b0aefa34721f511c09ba58859 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 6 Jun 2017 11:30:20 +0200 Subject: [PATCH 08/11] Address comments, make things closer to the Win32 code. Things work better. --- UI/MiscScreens.cpp | 2 + android/jni/app-android.cpp | 1 + .../src/org/ppsspp/ppsspp/TextRenderer.java | 26 ++-- ext/native/Android.mk | 2 +- ext/native/gfx_es2/draw_text.cpp | 2 - ext/native/gfx_es2/draw_text.h | 1 - ext/native/gfx_es2/draw_text_android.cpp | 135 +++++++++++++----- ext/native/gfx_es2/draw_text_android.h | 9 +- ext/native/gfx_es2/draw_text_qt.cpp | 5 - ext/native/gfx_es2/draw_text_qt.h | 1 - ext/native/gfx_es2/draw_text_win.h | 2 +- 11 files changed, 125 insertions(+), 61 deletions(-) diff --git a/UI/MiscScreens.cpp b/UI/MiscScreens.cpp index b49f919e84..293bf9b4b3 100644 --- a/UI/MiscScreens.cpp +++ b/UI/MiscScreens.cpp @@ -345,6 +345,8 @@ NewLanguageScreen::NewLanguageScreen(const std::string &title) : ListPopupScreen continue; } + // We only support Arabic on platforms where we have support for the native text rendering + // APIs, as proper Arabic support is way too difficult to implement ourselves. #if !(defined(USING_QT_UI) || PPSSPP_PLATFORM(WINDOWS) || PPSSPP_PLATFORM(ANDROID)) if (tempLangs[i].name.find("ar_AE") != std::string::npos) { continue; diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 9d2acf8275..29ab1ffeb4 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -1110,6 +1110,7 @@ retry: graphicsContext = nullptr; renderLoopRunning = false; WLOG("Render loop function exited."); + jniEnvGraphics = nullptr; return true; } diff --git a/android/src/org/ppsspp/ppsspp/TextRenderer.java b/android/src/org/ppsspp/ppsspp/TextRenderer.java index ae359a563d..00d682b311 100644 --- a/android/src/org/ppsspp/ppsspp/TextRenderer.java +++ b/android/src/org/ppsspp/ppsspp/TextRenderer.java @@ -12,7 +12,7 @@ public class TextRenderer { bg = new Paint(); bg.setColor(Color.BLACK); } - public static int measureText(String string, double textSize) { + private static Point measure(String string, double textSize) { Rect bound = new Rect(); p.setTextSize((float)textSize); p.getTextBounds(string, 0, string.length(), bound); @@ -20,20 +20,22 @@ public class TextRenderer { int h = (int)(p.descent() - p.ascent() + 2.0f); // 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; - return (w << 16) | h; + w = (w + 5) & ~1; + Point p = new Point(); + p.x = w; + p.y = h; + return p; + } + public static int measureText(String string, double textSize) { + Point s = measure(string, textSize); + return (s.x << 16) | s.y; } 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 = (int)(p.descent() - p.ascent() + 2.0f); - // 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; + Point s = measure(string, textSize); + + int w = s.x; + int h = s.y; - 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); diff --git a/ext/native/Android.mk b/ext/native/Android.mk index 0920922d4a..089f49d192 100644 --- a/ext/native/Android.mk +++ b/ext/native/Android.mk @@ -75,7 +75,7 @@ LOCAL_SRC_FILES :=\ gfx_es2/draw_buffer.cpp.arm \ gfx_es2/draw_text.cpp.arm \ gfx_es2/draw_text_android.cpp.arm \ - gfx/GLStateCache.cpp.arm \ + gfx/GLStateCache.cpp.arm \ gfx/gl_debug_log.cpp \ gfx/gl_lost_manager.cpp \ gfx/texture_atlas.cpp \ diff --git a/ext/native/gfx_es2/draw_text.cpp b/ext/native/gfx_es2/draw_text.cpp index 70f9052faf..3666a765b7 100644 --- a/ext/native/gfx_es2/draw_text.cpp +++ b/ext/native/gfx_es2/draw_text.cpp @@ -35,11 +35,9 @@ 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; } diff --git a/ext/native/gfx_es2/draw_text.h b/ext/native/gfx_es2/draw_text.h index 4751d12a67..482e3912b7 100644 --- a/ext/native/gfx_es2/draw_text.h +++ b/ext/native/gfx_es2/draw_text.h @@ -72,7 +72,6 @@ protected: Draw::DrawContext *draw_; virtual void ClearCache() = 0; - virtual void RecreateFonts() = 0; // On DPI change void WrapString(std::string &out, const char *str, float maxWidth); int frameCount_; diff --git a/ext/native/gfx_es2/draw_text_android.cpp b/ext/native/gfx_es2/draw_text_android.cpp index a6a4ef6941..9e229008f0 100644 --- a/ext/native/gfx_es2/draw_text_android.cpp +++ b/ext/native/gfx_es2/draw_text_android.cpp @@ -29,12 +29,11 @@ TextDrawerAndroid::TextDrawerAndroid(Draw::DrawContext *draw) : TextDrawer(draw) } else { 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. + // Not sure why we can't do this but it crashes. Likely some deeper threading issue. // At worst we leak one ref... // env_->DeleteGlobalRef(cls_textRenderer); ClearCache(); @@ -45,8 +44,9 @@ bool TextDrawerAndroid::IsReady() const { } uint32_t TextDrawerAndroid::SetFont(const char *fontName, int size, int flags) { - // We will only use the default font - uint32_t fontHash = 0; //hash::Fletcher((const uint8_t *)fontName, strlen(fontName)); + // We will only use the default font but just for consistency let's still involve + // the font name. + uint32_t fontHash = hash::Fletcher((const uint8_t *)fontName, strlen(fontName)); fontHash ^= size; fontHash ^= flags << 10; @@ -56,10 +56,9 @@ uint32_t TextDrawerAndroid::SetFont(const char *fontName, int size, int flags) { return fontHash; } - curSize_ = (float)((6 + size) / dpiScale_) * 96.0f / 72.f; + // Just chose a factor that looks good, don't know what unit size is in anyway. AndroidFontEntry entry; - entry.size = curSize_; - + entry.size = (float)(size * 1.4f) / dpiScale_; fontMap_[fontHash] = entry; fontHash_ = fontHash; return fontHash; @@ -69,7 +68,9 @@ void TextDrawerAndroid::SetFont(uint32_t fontHandle) { uint32_t fontHash = fontHandle; auto iter = fontMap_.find(fontHash); if (iter != fontMap_.end()) { - curSize_ = iter->second.size / dpiScale_; + fontHash_ = fontHandle; + } else { + ELOG("Invalid font handle %08x", fontHandle); } } @@ -77,31 +78,85 @@ std::string TextDrawerAndroid::NormalizeString(std::string str) { return ReplaceAll(str, "&&", "&"); } -void TextDrawerAndroid::RecreateFonts() { - -} - void TextDrawerAndroid::MeasureString(const char *str, size_t len, float *w, float *h) { - std::string text(NormalizeString(std::string(str, len))); - jstring jstr = env_->NewStringUTF(text.c_str()); - uint32_t size = env_->CallStaticIntMethod(cls_textRenderer, method_measureText, jstr, curSize_); - env_->DeleteLocalRef(jstr); - *w = (size >> 16) * fontScaleX_ * dpiScale_; - *h = (size & 0xFFFF) * fontScaleY_ * dpiScale_; + uint32_t stringHash = hash::Fletcher((const uint8_t *)str, len); + uint32_t entryHash = stringHash ^ fontHash_; + + TextMeasureEntry *entry; + auto iter = sizeCache_.find(entryHash); + if (iter != sizeCache_.end()) { + entry = iter->second.get(); + } else { + float scaledSize = 14; + auto iter = fontMap_.find(fontHash_); + if (iter != fontMap_.end()) { + scaledSize = iter->second.size; + } else { + ELOG("Missing font"); + } + std::string text(NormalizeString(std::string(str, len))); + jstring jstr = env_->NewStringUTF(text.c_str()); + uint32_t size = env_->CallStaticIntMethod(cls_textRenderer, method_measureText, jstr, scaledSize); + env_->DeleteLocalRef(jstr); + + entry = new TextMeasureEntry(); + entry->width = (size >> 16); + entry->height = (size & 0xFFFF); + sizeCache_[entryHash] = std::unique_ptr(entry); + } + entry->lastUsedFrame = frameCount_; + *w = entry->width * fontScaleX_ * dpiScale_; + *h = entry->height * fontScaleY_ * dpiScale_; } void TextDrawerAndroid::MeasureStringRect(const char *str, size_t len, const Bounds &bounds, float *w, float *h, int align) { - std::string toMeasure(NormalizeString(std::string(str, len))); + double scaledSize = 14; + auto iter = fontMap_.find(fontHash_); + if (iter != fontMap_.end()) { + scaledSize = iter->second.size; + } else { + ELOG("Missing font"); + } + + std::string toMeasure = std::string(str, len); if (align & FLAG_WRAP_TEXT) { bool rotated = (align & (ROTATE_90DEG_LEFT | ROTATE_90DEG_RIGHT)) != 0; WrapString(toMeasure, toMeasure.c_str(), rotated ? bounds.h : bounds.w); } - jstring jstr = env_->NewStringUTF(toMeasure.c_str()); - uint32_t size = env_->CallStaticIntMethod(cls_textRenderer, method_measureText, jstr, curSize_); - env_->DeleteLocalRef(jstr); - *w = (size >> 16) * fontScaleX_ * dpiScale_; - *h = (size & 0xFFFF) * fontScaleY_ * dpiScale_; + std::vector lines; + SplitString(toMeasure, '\n', lines); + float total_w = 0.0f; + float total_h = 0.0f; + for (size_t i = 0; i < lines.size(); i++) { + uint32_t stringHash = hash::Fletcher((const uint8_t *)&lines[i][0], lines[i].length()); + uint32_t entryHash = stringHash ^ fontHash_; + + TextMeasureEntry *entry; + auto iter = sizeCache_.find(entryHash); + if (iter != sizeCache_.end()) { + entry = iter->second.get(); + } else { + std::string text(NormalizeString(lines[i])); + jstring jstr = env_->NewStringUTF(text.c_str()); + uint32_t size = env_->CallStaticIntMethod(cls_textRenderer, method_measureText, jstr, scaledSize); + env_->DeleteLocalRef(jstr); + int sizecx = size >> 16; + int sizecy = size & 0xFFFF; + entry = new TextMeasureEntry(); + entry->width = sizecx; + entry->height = sizecy; + sizeCache_[entryHash] = std::unique_ptr(entry); + } + entry->lastUsedFrame = frameCount_; + + if (total_w < entry->width * fontScaleX_) { + total_w = entry->width * fontScaleX_; + } + total_h += entry->height * fontScaleY_; + } + *w = total_w * dpiScale_; + *h = total_h * dpiScale_; } void TextDrawerAndroid::DrawString(DrawBuffer &target, const char *str, float x, float y, uint32_t color, int align) { @@ -126,13 +181,19 @@ void TextDrawerAndroid::DrawString(DrawBuffer &target, const char *str, float x, entry->lastUsedFrame = frameCount_; draw_->BindTexture(0, entry->texture); } else { + double size; + auto iter = fontMap_.find(fontHash_); + if (iter != fontMap_.end()) { + size = iter->second.size; + } else { + ELOG("Missing font"); + } + jstring jstr = env_->NewStringUTF(text.c_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); - int imageHeight = (size & 0xFFFF); - jintArray imageData = (jintArray)env_->CallStaticObjectMethod(cls_textRenderer, method_renderText, jstr, curSize_); + uint32_t textSize = env_->CallStaticIntMethod(cls_textRenderer, method_measureText, jstr, size); + int imageWidth = (textSize >> 16); + int imageHeight = (textSize & 0xFFFF); + jintArray imageData = (jintArray)env_->CallStaticObjectMethod(cls_textRenderer, method_renderText, jstr, size); env_->DeleteLocalRef(jstr); entry = new TextStringEntry(); @@ -179,7 +240,7 @@ void TextDrawerAndroid::ClearCache() { iter.second->texture->Release(); } cache_.clear(); - fontMap_.clear(); + sizeCache_.clear(); } void TextDrawerAndroid::DrawStringRect(DrawBuffer &target, const char *str, const Bounds &bounds, uint32_t color, int align) { @@ -210,10 +271,10 @@ 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"); + ILOG("Scale changed - wiping cache"); dpiScale_ = newDpiScale; ClearCache(); - RecreateFonts(); + fontMap_.clear(); // size is precomputed using dpiScale_. } // Drop old strings. Use a prime number to reduce clashing with other rhythms @@ -227,6 +288,14 @@ 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 6d3962a921..b65dc4880e 100644 --- a/ext/native/gfx_es2/draw_text_android.h +++ b/ext/native/gfx_es2/draw_text_android.h @@ -10,7 +10,7 @@ #include struct AndroidFontEntry { - float size; + double size; }; class TextDrawerAndroid : public TextDrawer { @@ -30,7 +30,6 @@ public: protected: void ClearCache() override; - void RecreateFonts() override; // On DPI change private: std::string NormalizeString(std::string str); @@ -40,14 +39,14 @@ private: jclass cls_textRenderer; jmethodID method_measureText; jmethodID method_renderText; - double curSize_; - uint32_t fontHash_; + uint32_t fontHash_; // Just the size. std::map fontMap_; - // The key is the CityHash of the string xor the fontHash_. + // The key is the CityHash of the string xor the fontHash_ (though the fontHash_ is just the size). std::map> cache_; + std::map> sizeCache_; }; #endif diff --git a/ext/native/gfx_es2/draw_text_qt.cpp b/ext/native/gfx_es2/draw_text_qt.cpp index ad33e8168e..859a807c62 100644 --- a/ext/native/gfx_es2/draw_text_qt.cpp +++ b/ext/native/gfx_es2/draw_text_qt.cpp @@ -15,7 +15,6 @@ #include #include - TextDrawerQt::TextDrawerQt(Draw::DrawContext *draw) : TextDrawer(draw) { } @@ -46,10 +45,6 @@ void TextDrawerQt::SetFont(uint32_t fontHandle) { } -void TextDrawerQt::RecreateFonts() { - -} - void TextDrawerQt::MeasureString(const char *str, size_t len, float *w, float *h) { QFont* font = fontMap_.find(fontHash_)->second; QFontMetrics fm(*font); diff --git a/ext/native/gfx_es2/draw_text_qt.h b/ext/native/gfx_es2/draw_text_qt.h index 2a24b7cad5..815a76a13a 100644 --- a/ext/native/gfx_es2/draw_text_qt.h +++ b/ext/native/gfx_es2/draw_text_qt.h @@ -23,7 +23,6 @@ public: protected: void ClearCache() override; - void RecreateFonts() override; // On DPI change uint32_t fontHash_; std::map fontMap_; diff --git a/ext/native/gfx_es2/draw_text_win.h b/ext/native/gfx_es2/draw_text_win.h index 7461afaf63..a8d35744b5 100644 --- a/ext/native/gfx_es2/draw_text_win.h +++ b/ext/native/gfx_es2/draw_text_win.h @@ -30,7 +30,7 @@ public: protected: void ClearCache() override; - void RecreateFonts() override; // On DPI change + void RecreateFonts(); // On DPI change TextDrawerContext *ctx_; std::map> fontMap_; From 28984ceef4310f63996924fb11c349ab3929bba0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 6 Jun 2017 11:31:52 +0200 Subject: [PATCH 09/11] Fix some comments --- ext/native/gfx_es2/draw_text_android.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/native/gfx_es2/draw_text_android.h b/ext/native/gfx_es2/draw_text_android.h index b65dc4880e..010cb8c8b5 100644 --- a/ext/native/gfx_es2/draw_text_android.h +++ b/ext/native/gfx_es2/draw_text_android.h @@ -40,11 +40,11 @@ private: jmethodID method_measureText; jmethodID method_renderText; - uint32_t fontHash_; // Just the size. + uint32_t fontHash_; std::map fontMap_; - // The key is the CityHash of the string xor the fontHash_ (though the fontHash_ is just the size). + // The key is the CityHash of the string xor the fontHash_. std::map> cache_; std::map> sizeCache_; }; From e4d2c95968b5ed04ec248149bf6ee98aefede2f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 6 Jun 2017 12:25:40 +0200 Subject: [PATCH 10/11] Buildfix qt --- ext/native/gfx_es2/draw_text_qt.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/ext/native/gfx_es2/draw_text_qt.cpp b/ext/native/gfx_es2/draw_text_qt.cpp index 859a807c62..d0d8773dd8 100644 --- a/ext/native/gfx_es2/draw_text_qt.cpp +++ b/ext/native/gfx_es2/draw_text_qt.cpp @@ -172,7 +172,6 @@ void TextDrawerQt::OncePerFrame() { if (newDpiScale != dpiScale_) { dpiScale_ = newDpiScale; ClearCache(); - RecreateFonts(); } // Drop old strings. Use a prime number to reduce clashing with other rhythms From 751c61bd69eaa86506d02e4ce244d5d3ae6990ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 6 Jun 2017 15:45:43 +0200 Subject: [PATCH 11/11] We are already shipping Roboto-Condensed.ttf, let's use it --- android/src/org/ppsspp/ppsspp/NativeActivity.java | 1 + android/src/org/ppsspp/ppsspp/TextRenderer.java | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index bb0513561a..f96d70713c 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -406,6 +406,7 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + TextRenderer.init(this); shuttingDown = false; registerCallbacks(); diff --git a/android/src/org/ppsspp/ppsspp/TextRenderer.java b/android/src/org/ppsspp/ppsspp/TextRenderer.java index 00d682b311..8eff130e86 100644 --- a/android/src/org/ppsspp/ppsspp/TextRenderer.java +++ b/android/src/org/ppsspp/ppsspp/TextRenderer.java @@ -1,17 +1,30 @@ package org.ppsspp.ppsspp; +import android.content.Context; import android.graphics.*; +import android.util.Log; import java.nio.ByteBuffer; public class TextRenderer { private static Paint p; private static Paint bg; + private static Typeface robotoCondensed; + private static final String TAG = "TextRenderer"; static { p = new Paint(Paint.SUBPIXEL_TEXT_FLAG | Paint.ANTI_ALIAS_FLAG); p.setColor(Color.WHITE); bg = new Paint(); bg.setColor(Color.BLACK); } + public static void init(Context ctx) { + robotoCondensed = Typeface.createFromAsset(ctx.getAssets(), "Roboto-Condensed.ttf"); + if (robotoCondensed != null) { + Log.i(TAG, "Successfully loaded Roboto Condensed"); + p.setTypeface(robotoCondensed); + } else { + Log.e(TAG, "Failed to load Roboto Condensed"); + } + } private static Point measure(String string, double textSize) { Rect bound = new Rect(); p.setTextSize((float)textSize);