Skip to content

Commit 1f7310d

Browse files
authored
Merge pull request #19184 from hrydgard/native-text-draw-mac-ios
Native text drawing on macOS/iOS
2 parents d017be5 + c02e5b3 commit 1f7310d

26 files changed

+571
-79
lines changed

CMakeLists.txt

+7-1
Original file line numberDiff line numberDiff line change
@@ -1271,6 +1271,12 @@ if(OPENXR AND NOT ARMV7_DEVICE)
12711271
set(nativeExtraLibs ${nativeExtraLibs} openxr_loader)
12721272
endif()
12731273

1274+
if(IOS OR MACOSX)
1275+
set(nativeExtra ${nativeExtra}
1276+
Common/Render/Text/draw_text_cocoa.mm
1277+
Common/Render/Text/draw_text_cocoa.h)
1278+
endif()
1279+
12741280
if(ANDROID)
12751281
set(NativeAppSource ${NativeAppSource}
12761282
android/jni/app-android.cpp
@@ -1317,7 +1323,7 @@ elseif(IOS AND NOT LIBRETRO)
13171323
Common/Battery/AppleBatteryClient.m
13181324
)
13191325

1320-
set(nativeExtraLibs ${nativeExtraLibs} "-framework Foundation -framework MediaPlayer -framework AudioToolbox -framework CoreGraphics -framework QuartzCore -framework UIKit -framework GLKit -framework OpenAL -framework AVFoundation -framework CoreLocation -framework CoreVideo -framework CoreMedia -framework CoreServices -framework Metal -framework IOSurface" )
1326+
set(nativeExtraLibs ${nativeExtraLibs} "-framework Foundation -framework MediaPlayer -framework AudioToolbox -framework CoreGraphics -framework QuartzCore -framework UIKit -framework GLKit -framework OpenAL -framework AVFoundation -framework CoreLocation -framework CoreText -framework CoreVideo -framework CoreMedia -framework CoreServices -framework Metal -framework IOSurface" )
13211327
if(EXISTS "${CMAKE_IOS_SDK_ROOT}/System/Library/Frameworks/GameController.framework")
13221328
set(nativeExtraLibs ${nativeExtraLibs} "-weak_framework GameController")
13231329
endif()

Common/Math/geom2d.h

+7-7
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,19 @@
22

33
#include <cmath>
44

5-
struct Point {
6-
Point() : x(0.0f), y(0.0f) {}
7-
Point(float x_, float y_) : x(x_), y(y_) {}
5+
struct Point2D {
6+
Point2D() : x(0.0f), y(0.0f) {}
7+
Point2D(float x_, float y_) : x(x_), y(y_) {}
88

99
float x;
1010
float y;
1111

12-
float distanceTo(const Point &other) const {
12+
float distanceTo(const Point2D &other) const {
1313
float dx = other.x - x, dy = other.y - y;
1414
return sqrtf(dx*dx + dy*dy);
1515
}
1616

17-
bool operator ==(const Point &other) const {
17+
bool operator ==(const Point2D &other) const {
1818
return x == other.x && y == other.y;
1919
}
2020

@@ -60,8 +60,8 @@ struct Bounds {
6060
float y2() const { return y + h; }
6161
float centerX() const { return x + w * 0.5f; }
6262
float centerY() const { return y + h * 0.5f; }
63-
Point Center() const {
64-
return Point(centerX(), centerY());
63+
Point2D Center() const {
64+
return Point2D(centerX(), centerY());
6565
}
6666
Bounds Expand(float amount) const {
6767
return Bounds(x - amount, y - amount, w + amount * 2, h + amount * 2);

Common/Render/Text/draw_text.cpp

+5-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "Common/Render/Text/draw_text.h"
1010
#include "Common/Render/Text/draw_text_win.h"
11+
#include "Common/Render/Text/draw_text_cocoa.h"
1112
#include "Common/Render/Text/draw_text_uwp.h"
1213
#include "Common/Render/Text/draw_text_qt.h"
1314
#include "Common/Render/Text/draw_text_android.h"
@@ -70,15 +71,15 @@ void TextDrawer::DrawStringRect(DrawBuffer &target, std::string_view str, const
7071
DrawString(target, toDraw.c_str(), x, y, color, align);
7172
}
7273

73-
void TextDrawer::DrawStringBitmapRect(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, const Bounds &bounds, int align) {
74+
bool TextDrawer::DrawStringBitmapRect(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, const Bounds &bounds, int align) {
7475
std::string toDraw(str);
7576
int wrap = align & (FLAG_WRAP_TEXT | FLAG_ELLIPSIZE_TEXT);
7677
if (wrap) {
7778
bool rotated = (align & (ROTATE_90DEG_LEFT | ROTATE_90DEG_RIGHT)) != 0;
7879
WrapString(toDraw, str, rotated ? bounds.h : bounds.w, wrap);
7980
}
8081

81-
DrawStringBitmap(bitmapData, entry, texFormat, toDraw.c_str(), align);
82+
return DrawStringBitmap(bitmapData, entry, texFormat, toDraw.c_str(), align);
8283
}
8384

8485
TextDrawer *TextDrawer::Create(Draw::DrawContext *draw) {
@@ -89,6 +90,8 @@ TextDrawer *TextDrawer::Create(Draw::DrawContext *draw) {
8990
drawer = new TextDrawerWin32(draw);
9091
#elif PPSSPP_PLATFORM(UWP)
9192
drawer = new TextDrawerUWP(draw);
93+
#elif PPSSPP_PLATFORM(MAC) || PPSSPP_PLATFORM(IOS)
94+
drawer = new TextDrawerCocoa(draw);
9295
#elif defined(USING_QT_UI)
9396
drawer = new TextDrawerQt(draw);
9497
#elif PPSSPP_PLATFORM(ANDROID)

Common/Render/Text/draw_text.h

+11-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include <memory>
1414
#include <cstdint>
15+
#include <map>
1516

1617
#include "Common/Data/Text/WrapText.h"
1718
#include "Common/Render/DrawBuffer.h"
@@ -33,6 +34,7 @@ struct TextStringEntry {
3334
struct TextMeasureEntry {
3435
int width;
3536
int height;
37+
int leading; // only used with Cocoa
3638
int lastUsedFrame;
3739
};
3840

@@ -46,10 +48,14 @@ class TextDrawer {
4648
void SetFontScale(float xscale, float yscale);
4749
virtual void MeasureString(std::string_view str, float *w, float *h) = 0;
4850
virtual void MeasureStringRect(std::string_view str, const Bounds &bounds, float *w, float *h, int align = ALIGN_TOPLEFT) = 0;
51+
52+
// TODO: This one we should be able to make a default implementation for, calling the specialized DrawBitmap.
53+
// Only problem is that we need to make sure that the texFormats are all supported by all the backends, or we explicitly limit.
4954
virtual void DrawString(DrawBuffer &target, std::string_view str, float x, float y, uint32_t color, int align = ALIGN_TOPLEFT) = 0;
55+
5056
void DrawStringRect(DrawBuffer &target, std::string_view str, const Bounds &bounds, uint32_t color, int align);
51-
virtual void DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align = ALIGN_TOPLEFT) = 0;
52-
void DrawStringBitmapRect(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, const Bounds &bounds, int align);
57+
virtual bool DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align = ALIGN_TOPLEFT) = 0;
58+
bool DrawStringBitmapRect(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, const Bounds &bounds, int align);
5359
// Use for housekeeping like throwing out old strings.
5460
virtual void OncePerFrame() = 0;
5561

@@ -86,6 +92,9 @@ class TextDrawer {
8692
float fontScaleY_ = 1.0f;
8793
float dpiScale_ = 1.0f;
8894
bool ignoreGlobalDpi_ = false;
95+
96+
std::map<CacheKey, std::unique_ptr<TextStringEntry>> cache_;
97+
std::map<CacheKey, std::unique_ptr<TextMeasureEntry>> sizeCache_;
8998
};
9099

91100
class TextDrawerWordWrapper : public WordWrapper {

Common/Render/Text/draw_text_android.cpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -174,10 +174,10 @@ void TextDrawerAndroid::MeasureStringRect(std::string_view str, const Bounds &bo
174174
*h = total_h * fontScaleY_ * dpiScale_;
175175
}
176176

177-
void TextDrawerAndroid::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align) {
177+
bool TextDrawerAndroid::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align) {
178178
if (str.empty()) {
179179
bitmapData.clear();
180-
return;
180+
return false;
181181
}
182182

183183
double size = 0.0;
@@ -244,6 +244,7 @@ void TextDrawerAndroid::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextS
244244
}
245245
env->ReleaseIntArrayElements(imageData, jimage, 0);
246246
env->DeleteLocalRef(imageData);
247+
return true;
247248
}
248249

249250
void TextDrawerAndroid::DrawString(DrawBuffer &target, std::string_view str, float x, float y, uint32_t color, int align) {

Common/Render/Text/draw_text_android.h

+1-4
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class TextDrawerAndroid : public TextDrawer {
2424
void MeasureString(std::string_view str, float *w, float *h) override;
2525
void MeasureStringRect(std::string_view str, const Bounds &bounds, float *w, float *h, int align = ALIGN_TOPLEFT) override;
2626
void DrawString(DrawBuffer &target, std::string_view str, float x, float y, uint32_t color, int align = ALIGN_TOPLEFT) override;
27-
void DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align = ALIGN_TOPLEFT) override;
27+
bool DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align = ALIGN_TOPLEFT) override;
2828
// Use for housekeeping like throwing out old strings.
2929
void OncePerFrame() override;
3030

@@ -43,9 +43,6 @@ class TextDrawerAndroid : public TextDrawer {
4343
bool use4444Format_ = false;
4444

4545
std::map<uint32_t, AndroidFontEntry> fontMap_;
46-
47-
std::map<CacheKey, std::unique_ptr<TextStringEntry>> cache_;
48-
std::map<CacheKey, std::unique_ptr<TextMeasureEntry>> sizeCache_;
4946
};
5047

5148
#endif

Common/Render/Text/draw_text_cocoa.h

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#pragma once
2+
3+
#include "ppsspp_config.h"
4+
5+
#if PPSSPP_PLATFORM(MAC) || PPSSPP_PLATFORM(IOS)
6+
7+
#include <map>
8+
#include "Common/Render/Text/draw_text.h"
9+
10+
struct TextDrawerContext;
11+
// Internal struct but all details in .cpp file (pimpl to avoid pulling in excessive headers here)
12+
class TextDrawerFontContext;
13+
14+
class TextDrawerCocoa : public TextDrawer {
15+
public:
16+
TextDrawerCocoa(Draw::DrawContext *draw);
17+
~TextDrawerCocoa();
18+
19+
uint32_t SetFont(const char *fontName, int size, int flags) override;
20+
void SetFont(uint32_t fontHandle) override; // Shortcut once you've set the font once.
21+
void MeasureString(std::string_view str, float *w, float *h) override;
22+
void MeasureStringRect(std::string_view str, const Bounds &bounds, float *w, float *h, int align = ALIGN_TOPLEFT) override;
23+
void DrawString(DrawBuffer &target, std::string_view str, float x, float y, uint32_t color, int align = ALIGN_TOPLEFT) override;
24+
bool DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align = ALIGN_TOPLEFT) override;
25+
// Use for housekeeping like throwing out old strings.
26+
void OncePerFrame() override;
27+
28+
protected:
29+
void ClearCache() override;
30+
void RecreateFonts(); // On DPI change
31+
32+
TextDrawerContext *ctx_;
33+
std::map<uint32_t, std::unique_ptr<TextDrawerFontContext>> fontMap_;
34+
35+
uint32_t fontHash_;
36+
std::map<CacheKey, std::unique_ptr<TextStringEntry>> cache_;
37+
std::map<CacheKey, std::unique_ptr<TextMeasureEntry>> sizeCache_;
38+
};
39+
40+
#endif

0 commit comments

Comments
 (0)