diff --git a/CMakeLists.txt b/CMakeLists.txt index 975d3917a2..4e74cf34b2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1271,6 +1271,12 @@ if(OPENXR AND NOT ARMV7_DEVICE) set(nativeExtraLibs ${nativeExtraLibs} openxr_loader) endif() +if(IOS OR MACOSX) + set(nativeExtra ${nativeExtra} + Common/Render/Text/draw_text_cocoa.mm + Common/Render/Text/draw_text_cocoa.h) +endif() + if(ANDROID) set(NativeAppSource ${NativeAppSource} android/jni/app-android.cpp @@ -1317,7 +1323,7 @@ elseif(IOS AND NOT LIBRETRO) Common/Battery/AppleBatteryClient.m ) - 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" ) + 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" ) if(EXISTS "${CMAKE_IOS_SDK_ROOT}/System/Library/Frameworks/GameController.framework") set(nativeExtraLibs ${nativeExtraLibs} "-weak_framework GameController") endif() diff --git a/Common/Math/geom2d.h b/Common/Math/geom2d.h index f319d7c79d..bfbe950c83 100644 --- a/Common/Math/geom2d.h +++ b/Common/Math/geom2d.h @@ -2,19 +2,19 @@ #include -struct Point { - Point() : x(0.0f), y(0.0f) {} - Point(float x_, float y_) : x(x_), y(y_) {} +struct Point2D { + Point2D() : x(0.0f), y(0.0f) {} + Point2D(float x_, float y_) : x(x_), y(y_) {} float x; float y; - float distanceTo(const Point &other) const { + float distanceTo(const Point2D &other) const { float dx = other.x - x, dy = other.y - y; return sqrtf(dx*dx + dy*dy); } - bool operator ==(const Point &other) const { + bool operator ==(const Point2D &other) const { return x == other.x && y == other.y; } @@ -60,8 +60,8 @@ struct Bounds { float y2() const { return y + h; } float centerX() const { return x + w * 0.5f; } float centerY() const { return y + h * 0.5f; } - Point Center() const { - return Point(centerX(), centerY()); + Point2D Center() const { + return Point2D(centerX(), centerY()); } Bounds Expand(float amount) const { return Bounds(x - amount, y - amount, w + amount * 2, h + amount * 2); diff --git a/Common/Render/Text/draw_text.cpp b/Common/Render/Text/draw_text.cpp index 6b59ab0181..7cdefb4072 100644 --- a/Common/Render/Text/draw_text.cpp +++ b/Common/Render/Text/draw_text.cpp @@ -8,6 +8,7 @@ #include "Common/Render/Text/draw_text.h" #include "Common/Render/Text/draw_text_win.h" +#include "Common/Render/Text/draw_text_cocoa.h" #include "Common/Render/Text/draw_text_uwp.h" #include "Common/Render/Text/draw_text_qt.h" #include "Common/Render/Text/draw_text_android.h" @@ -70,7 +71,7 @@ void TextDrawer::DrawStringRect(DrawBuffer &target, std::string_view str, const DrawString(target, toDraw.c_str(), x, y, color, align); } -void TextDrawer::DrawStringBitmapRect(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, const Bounds &bounds, int align) { +bool TextDrawer::DrawStringBitmapRect(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, const Bounds &bounds, int align) { std::string toDraw(str); int wrap = align & (FLAG_WRAP_TEXT | FLAG_ELLIPSIZE_TEXT); if (wrap) { @@ -78,7 +79,7 @@ void TextDrawer::DrawStringBitmapRect(std::vector &bitmapData, TextStri WrapString(toDraw, str, rotated ? bounds.h : bounds.w, wrap); } - DrawStringBitmap(bitmapData, entry, texFormat, toDraw.c_str(), align); + return DrawStringBitmap(bitmapData, entry, texFormat, toDraw.c_str(), align); } TextDrawer *TextDrawer::Create(Draw::DrawContext *draw) { @@ -89,6 +90,8 @@ TextDrawer *TextDrawer::Create(Draw::DrawContext *draw) { drawer = new TextDrawerWin32(draw); #elif PPSSPP_PLATFORM(UWP) drawer = new TextDrawerUWP(draw); +#elif PPSSPP_PLATFORM(MAC) || PPSSPP_PLATFORM(IOS) + drawer = new TextDrawerCocoa(draw); #elif defined(USING_QT_UI) drawer = new TextDrawerQt(draw); #elif PPSSPP_PLATFORM(ANDROID) diff --git a/Common/Render/Text/draw_text.h b/Common/Render/Text/draw_text.h index a6734b7e12..acbe15b21f 100644 --- a/Common/Render/Text/draw_text.h +++ b/Common/Render/Text/draw_text.h @@ -12,6 +12,7 @@ #include #include +#include #include "Common/Data/Text/WrapText.h" #include "Common/Render/DrawBuffer.h" @@ -33,6 +34,7 @@ struct TextStringEntry { struct TextMeasureEntry { int width; int height; + int leading; // only used with Cocoa int lastUsedFrame; }; @@ -46,10 +48,14 @@ public: void SetFontScale(float xscale, float yscale); virtual void MeasureString(std::string_view str, float *w, float *h) = 0; virtual void MeasureStringRect(std::string_view str, const Bounds &bounds, float *w, float *h, int align = ALIGN_TOPLEFT) = 0; + + // TODO: This one we should be able to make a default implementation for, calling the specialized DrawBitmap. + // Only problem is that we need to make sure that the texFormats are all supported by all the backends, or we explicitly limit. virtual void DrawString(DrawBuffer &target, std::string_view str, float x, float y, uint32_t color, int align = ALIGN_TOPLEFT) = 0; + void DrawStringRect(DrawBuffer &target, std::string_view str, const Bounds &bounds, uint32_t color, int align); - virtual void DrawStringBitmap(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align = ALIGN_TOPLEFT) = 0; - void DrawStringBitmapRect(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, const Bounds &bounds, int align); + virtual bool DrawStringBitmap(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align = ALIGN_TOPLEFT) = 0; + bool DrawStringBitmapRect(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, const Bounds &bounds, int align); // Use for housekeeping like throwing out old strings. virtual void OncePerFrame() = 0; @@ -86,6 +92,9 @@ protected: float fontScaleY_ = 1.0f; float dpiScale_ = 1.0f; bool ignoreGlobalDpi_ = false; + + std::map> cache_; + std::map> sizeCache_; }; class TextDrawerWordWrapper : public WordWrapper { diff --git a/Common/Render/Text/draw_text_android.cpp b/Common/Render/Text/draw_text_android.cpp index 5abb8471a3..67e065cbca 100644 --- a/Common/Render/Text/draw_text_android.cpp +++ b/Common/Render/Text/draw_text_android.cpp @@ -174,10 +174,10 @@ void TextDrawerAndroid::MeasureStringRect(std::string_view str, const Bounds &bo *h = total_h * fontScaleY_ * dpiScale_; } -void TextDrawerAndroid::DrawStringBitmap(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align) { +bool TextDrawerAndroid::DrawStringBitmap(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align) { if (str.empty()) { bitmapData.clear(); - return; + return false; } double size = 0.0; @@ -244,6 +244,7 @@ void TextDrawerAndroid::DrawStringBitmap(std::vector &bitmapData, TextS } env->ReleaseIntArrayElements(imageData, jimage, 0); env->DeleteLocalRef(imageData); + return true; } void TextDrawerAndroid::DrawString(DrawBuffer &target, std::string_view str, float x, float y, uint32_t color, int align) { diff --git a/Common/Render/Text/draw_text_android.h b/Common/Render/Text/draw_text_android.h index 430db90d2b..d13bf6ad55 100644 --- a/Common/Render/Text/draw_text_android.h +++ b/Common/Render/Text/draw_text_android.h @@ -24,7 +24,7 @@ public: void MeasureString(std::string_view str, float *w, float *h) override; void MeasureStringRect(std::string_view str, const Bounds &bounds, float *w, float *h, int align = ALIGN_TOPLEFT) override; void DrawString(DrawBuffer &target, std::string_view str, float x, float y, uint32_t color, int align = ALIGN_TOPLEFT) override; - void DrawStringBitmap(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align = ALIGN_TOPLEFT) override; + bool DrawStringBitmap(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align = ALIGN_TOPLEFT) override; // Use for housekeeping like throwing out old strings. void OncePerFrame() override; @@ -43,9 +43,6 @@ private: bool use4444Format_ = false; std::map fontMap_; - - std::map> cache_; - std::map> sizeCache_; }; #endif diff --git a/Common/Render/Text/draw_text_cocoa.h b/Common/Render/Text/draw_text_cocoa.h new file mode 100644 index 0000000000..9af640785a --- /dev/null +++ b/Common/Render/Text/draw_text_cocoa.h @@ -0,0 +1,40 @@ +#pragma once + +#include "ppsspp_config.h" + +#if PPSSPP_PLATFORM(MAC) || PPSSPP_PLATFORM(IOS) + +#include +#include "Common/Render/Text/draw_text.h" + +struct TextDrawerContext; +// Internal struct but all details in .cpp file (pimpl to avoid pulling in excessive headers here) +class TextDrawerFontContext; + +class TextDrawerCocoa : public TextDrawer { +public: + TextDrawerCocoa(Draw::DrawContext *draw); + ~TextDrawerCocoa(); + + 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(std::string_view str, float *w, float *h) override; + void MeasureStringRect(std::string_view str, const Bounds &bounds, float *w, float *h, int align = ALIGN_TOPLEFT) override; + void DrawString(DrawBuffer &target, std::string_view str, float x, float y, uint32_t color, int align = ALIGN_TOPLEFT) override; + bool DrawStringBitmap(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align = ALIGN_TOPLEFT) override; + // Use for housekeeping like throwing out old strings. + void OncePerFrame() override; + +protected: + void ClearCache() override; + void RecreateFonts(); // On DPI change + + TextDrawerContext *ctx_; + std::map> fontMap_; + + uint32_t fontHash_; + std::map> cache_; + std::map> sizeCache_; +}; + +#endif diff --git a/Common/Render/Text/draw_text_cocoa.mm b/Common/Render/Text/draw_text_cocoa.mm new file mode 100644 index 0000000000..927d94beb8 --- /dev/null +++ b/Common/Render/Text/draw_text_cocoa.mm @@ -0,0 +1,443 @@ +#include "ppsspp_config.h" + +#import "draw_text_cocoa.h" + +#if PPSSPP_PLATFORM(MAC) || PPSSPP_PLATFORM(IOS) + +#import +#import +#import + +#if PPSSPP_PLATFORM(MAC) +#import +#define ColorType NSColor +#else +#import +#define ColorType UIColor +#endif + +#include "Common/System/Display.h" +#include "Common/GPU/thin3d.h" +#include "Common/Data/Hash/Hash.h" +#include "Common/Data/Text/WrapText.h" +#include "Common/Data/Encoding/Utf8.h" +#include "Common/Render/Text/draw_text.h" +#include "Common/Render/Text/draw_text_cocoa.h" + +#include "Common/Log.h" +#include "Common/StringUtils.h" + +enum { + MAX_TEXT_WIDTH = 4096, + MAX_TEXT_HEIGHT = 512 +}; + +#define APPLE_FONT "Helvetica" + +class TextDrawerFontContext { +public: + ~TextDrawerFontContext() { + Destroy(); + } + + void Create() { + // Create an attributed string with string and font information + CGFloat fontSize = ceilf((height / dpiScale) * 1.25f); + INFO_LOG(G3D, "Creating cocoa typeface '%s' size %d (effective size %0.1f)", APPLE_FONT, height, fontSize); + // CTFontRef font = CTFontCreateWithName(CFSTR(APPLE_FONT), fontSize, nil); + CTFontRef font = CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, fontSize, nil); + attributes = [NSDictionary dictionaryWithObjectsAndKeys: + (id)font, kCTFontAttributeName, + kCFBooleanTrue, kCTForegroundColorFromContextAttributeName, // Lets us specify the color later. + nil]; + } + void Destroy() { + //CFRelease(font); + font = {}; + } + + NSDictionary* attributes = nil; + CTFontRef font = nil; + std::string fname; + int height; + int bold; + float dpiScale; +}; + +TextDrawerCocoa::TextDrawerCocoa(Draw::DrawContext *draw) : TextDrawer(draw) { +} + +TextDrawerCocoa::~TextDrawerCocoa() { + ClearCache(); + + fontMap_.clear(); +} + +// TODO: Share with other backends. +uint32_t TextDrawerCocoa::SetFont(const char *fontName, int size, int flags) { + uint32_t fontHash = fontName ? hash::Adler32((const uint8_t *)fontName, strlen(fontName)) : 0; + fontHash ^= size; + fontHash ^= flags << 10; + + auto iter = fontMap_.find(fontHash); + if (iter != fontMap_.end()) { + fontHash_ = fontHash; + return fontHash; + } + + std::string fname; + if (fontName) + fname = fontName; + else + fname = APPLE_FONT; + + TextDrawerFontContext *font = new TextDrawerFontContext(); + font->bold = false; + font->height = size; + font->fname = fname; + font->dpiScale = dpiScale_; + font->Create(); + + fontMap_[fontHash] = std::unique_ptr(font); + fontHash_ = fontHash; + return fontHash; +} + +void TextDrawerCocoa::SetFont(uint32_t fontHandle) { + auto iter = fontMap_.find(fontHandle); + if (iter != fontMap_.end()) { + fontHash_ = fontHandle; + } +} + +void TextDrawerCocoa::ClearCache() { + for (auto &iter : cache_) { + if (iter.second->texture) + iter.second->texture->Release(); + } + cache_.clear(); + sizeCache_.clear(); +} + +void TextDrawerCocoa::RecreateFonts() { + for (auto &iter : fontMap_) { + iter.second->dpiScale = dpiScale_; + iter.second->Create(); + } +} + +void TextDrawerCocoa::MeasureString(std::string_view str, float *w, float *h) { + CacheKey key{ std::string(str), fontHash_ }; + + TextMeasureEntry *entry; + auto iter = sizeCache_.find(key); + if (iter != sizeCache_.end()) { + entry = iter->second.get(); + } else { + auto iter = fontMap_.find(fontHash_); + NSDictionary *attributes = nil; + if (iter != fontMap_.end()) { + attributes = iter->second->attributes; + } + + std::string toMeasure = ReplaceAll(std::string(str), "&&", "&"); + + std::vector lines; + SplitString(toMeasure, '\n', lines); + + int extW = 0, extH = 0; + for (auto &line : lines) { + NSString *string = [[NSString alloc] initWithBytes:line.data() length:line.size() encoding: NSUTF8StringEncoding]; + NSAttributedString* as = [[NSAttributedString alloc] initWithString:string attributes:attributes]; + CTLineRef ctline = CTLineCreateWithAttributedString((CFAttributedStringRef)as); + CGFloat ascent, descent, leading; + double fWidth = CTLineGetTypographicBounds(ctline, &ascent, &descent, &leading); + + size_t width = (size_t)ceilf(fWidth); + size_t height = (size_t)ceilf(ascent + descent); + + if (width > extW) + extW = width; + extH += height; + } + + entry = new TextMeasureEntry(); + entry->width = extW; + entry->height = extH; + sizeCache_[key] = std::unique_ptr(entry); + } + + entry->lastUsedFrame = frameCount_; + *w = entry->width * fontScaleX_ * dpiScale_; + *h = entry->height * fontScaleY_ * dpiScale_; +} + + +void TextDrawerCocoa::MeasureStringRect(std::string_view str, const Bounds &bounds, float *w, float *h, int align) { + auto iter = fontMap_.find(fontHash_); + NSDictionary *attributes = nil; + if (iter != fontMap_.end()) { + attributes = iter->second->attributes; + } + + std::string toMeasure = std::string(str); + int wrap = align & (FLAG_WRAP_TEXT | FLAG_ELLIPSIZE_TEXT); + if (wrap) { + bool rotated = (align & (ROTATE_90DEG_LEFT | ROTATE_90DEG_RIGHT)) != 0; + WrapString(toMeasure, toMeasure.c_str(), rotated ? bounds.h : bounds.w, wrap); + } + + std::vector lines; + SplitString(toMeasure, '\n', lines); + int total_w = 0; + int total_h = 0; + CacheKey key{ "", fontHash_}; + for (size_t i = 0; i < lines.size(); i++) { + key.text = lines[i]; + TextMeasureEntry *entry; + auto iter = sizeCache_.find(key); + if (iter != sizeCache_.end()) { + entry = iter->second.get(); + } else { + std::string line = lines[i].empty() ? " " : ReplaceAll(lines[i], "&&", "&"); + NSString *string = [[NSString alloc] initWithBytes:line.data() length:line.size() encoding: NSUTF8StringEncoding]; + NSAttributedString* as = [[NSAttributedString alloc] initWithString:string attributes:attributes]; + CTLineRef ctline = CTLineCreateWithAttributedString((CFAttributedStringRef)as); + CGFloat ascent, descent, leading; + double fWidth = CTLineGetTypographicBounds(ctline, &ascent, &descent, &leading); + + size_t width = (size_t)ceilf(fWidth); + size_t height = (size_t)ceilf(ascent + descent); + + entry = new TextMeasureEntry(); + entry->width = width; + entry->height = height; + entry->leading = leading; + sizeCache_[key] = std::unique_ptr(entry); + } + entry->lastUsedFrame = frameCount_; + + if (total_w < entry->width) { + total_w = entry->width; + } + int h = i == lines.size() - 1 ? entry->height : (entry->height + entry->leading); + total_h += h; + } + + *w = total_w * fontScaleX_ * dpiScale_; + *h = total_h * fontScaleY_ * dpiScale_; +} + +void TextDrawerCocoa::DrawString(DrawBuffer &target, std::string_view str, float x, float y, uint32_t color, int align) { + using namespace Draw; + if (str.empty()) { + return; + } + + CacheKey key{ std::string(str), fontHash_ }; + target.Flush(true); + + TextStringEntry *entry; + + auto iter = cache_.find(key); + if (iter != cache_.end()) { + entry = iter->second.get(); + entry->lastUsedFrame = frameCount_; + } else { + DataFormat texFormat; + // For our purposes these are equivalent, so just choose the supported one. D3D can emulate them. + if (draw_->GetDataFormatSupport(Draw::DataFormat::A4R4G4B4_UNORM_PACK16) & FMT_TEXTURE) + texFormat = Draw::DataFormat::A4R4G4B4_UNORM_PACK16; + else if (draw_->GetDataFormatSupport(Draw::DataFormat::R4G4B4A4_UNORM_PACK16) & FMT_TEXTURE) + texFormat = Draw::DataFormat::R4G4B4A4_UNORM_PACK16; + else if (draw_->GetDataFormatSupport(Draw::DataFormat::B4G4R4A4_UNORM_PACK16) & FMT_TEXTURE) + texFormat = Draw::DataFormat::B4G4R4A4_UNORM_PACK16; + else + texFormat = Draw::DataFormat::R8G8B8A8_UNORM; + + entry = new TextStringEntry(); + + bool emoji = AnyEmojiInString(key.text.c_str(), key.text.size()); + if (emoji) + texFormat = Draw::DataFormat::R8G8B8A8_UNORM; + + // Convert the bitmap to a Thin3D compatible array of 16-bit pixels. Can't use a single channel format + // because we need white. Well, we could using swizzle, but not all our backends support that. + TextureDesc desc{}; + std::vector bitmapData; + if (!DrawStringBitmap(bitmapData, *entry, texFormat, str, align)) { + return; + } + desc.initData.push_back(&bitmapData[0]); + + desc.type = TextureType::LINEAR2D; + desc.format = texFormat; + desc.width = entry->bmWidth; + desc.height = entry->bmHeight; + desc.depth = 1; + desc.mipLevels = 1; + desc.tag = "TextDrawer"; + entry->texture = draw_->CreateTexture(desc); + cache_[key] = std::unique_ptr(entry); + } + + if (entry->texture) { + draw_->BindTexture(0, entry->texture); + } + + // Okay, the texture is bound, let's draw. + float w = entry->width * fontScaleX_ * dpiScale_; + float h = entry->height * fontScaleY_ * dpiScale_; + float u = entry->width / (float)entry->bmWidth; + float v = entry->height / (float)entry->bmHeight; + DrawBuffer::DoAlign(align, &x, &y, &w, &h); + if (entry->texture) { + target.DrawTexRect(x, y, x + w, y + h, 0.0f, 0.0f, u, v, color); + target.Flush(true); + } +} + +bool TextDrawerCocoa::DrawStringBitmap(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align) { + if (str.empty()) { + bitmapData.clear(); + return false; + } + + std::string printable = ReplaceAll(str, "&&", "&"); + NSString* string = [[NSString alloc] initWithBytes:printable.data() length:printable.length() encoding: NSUTF8StringEncoding]; + + auto iter = fontMap_.find(fontHash_); + if (iter == fontMap_.end()) { + return false; + } + NSDictionary* attributes = iter->second->attributes; + NSAttributedString* as = [[NSAttributedString alloc] initWithString:string attributes:attributes]; + + // Figure out how big an image we need + CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)as); + CGFloat ascent, descent, leading; + double fWidth = CTLineGetTypographicBounds(line, &ascent, &descent, &leading); + + // On iOS 4.0 and Mac OS X v10.6 you can pass null for data + size_t width = (size_t)ceilf(fWidth) + 1; + size_t height = (size_t)ceilf(ascent + descent) + 1; + + // Round width and height upwards to the closest multiple of 4. + width = (width + 3) & ~3; + height = (height + 3) & ~3; + + if (!width || !height) { + WARN_LOG(G3D, "Text '%.*s' caused a zero size image", (int)str.length(), str.data()); + return false; + } + + uint32_t *bitmap = new uint32_t[width * height]; + memset(bitmap, 0, width * height * 4); + + // Create the context and fill it with white background + CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB(); + CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast; + CGContextRef ctx = CGBitmapContextCreate(bitmap, width, height, 8, width*4, space, bitmapInfo); + CGColorSpaceRelease(space); + // CGContextSetRGBFillColor(ctx, 1.0, 1.0, 1.0, 0.0); // white background + // CGContextFillRect(ctx, CGRectMake(0.0, 0.0, width, height)); + // CGContextSetRGBFillColor(ctx, 1.0, 1.0, 1.0, 1.0); // white background + // CGContextSetRGBStrokeColor(ctx, 1.0, 1.0, 1.0, 1.0); // white background + CGContextSetStrokeColorWithColor(ctx, [ColorType whiteColor].CGColor); + CGContextSetFillColorWithColor(ctx, [ColorType whiteColor].CGColor); + + // Draw the text + CGFloat x = 0.0; + CGFloat y = descent; + CGContextSetTextPosition(ctx, x, y); + CTLineDraw(line, ctx); + CFRelease(line); + + entry.texture = nullptr; + entry.width = width; + entry.height = height; + entry.bmWidth = width; + entry.bmHeight = height; + entry.lastUsedFrame = frameCount_; + + // data now contains the bytes in RGBA, presumably. + // Convert the bitmap to a Thin3D compatible array of 16-bit pixels. Can't use a single channel format + // because we need white. Well, we could using swizzle, but not all our backends support that. + if (texFormat == Draw::DataFormat::R8G8B8A8_UNORM || texFormat == Draw::DataFormat::B8G8R8A8_UNORM) { + bitmapData.resize(entry.bmWidth * entry.bmHeight * sizeof(uint32_t)); + // If we chose this format, emoji are involved. Pass straight through. + uint32_t *bitmapData32 = (uint32_t *)&bitmapData[0]; + for (int y = 0; y < entry.bmHeight; y++) { + for (int x = 0; x < entry.bmWidth; x++) { + uint32_t color = bitmap[width * y + x]; + bitmapData32[entry.bmWidth * y + x] = color; + } + } + } else if (texFormat == Draw::DataFormat::B4G4R4A4_UNORM_PACK16 || texFormat == Draw::DataFormat::R4G4B4A4_UNORM_PACK16) { + bitmapData.resize(entry.bmWidth * entry.bmHeight * sizeof(uint16_t)); + uint16_t *bitmapData16 = (uint16_t *)&bitmapData[0]; + for (int y = 0; y < entry.bmHeight; y++) { + for (int x = 0; x < entry.bmWidth; x++) { + uint8_t bAlpha = (uint8_t)((bitmap[width * y + x] & 0xff) >> 4); + bitmapData16[entry.bmWidth * y + x] = (bAlpha) | 0xfff0; + } + } + } else if (texFormat == Draw::DataFormat::A4R4G4B4_UNORM_PACK16) { + bitmapData.resize(entry.bmWidth * entry.bmHeight * sizeof(uint16_t)); + uint16_t *bitmapData16 = (uint16_t *)&bitmapData[0]; + for (int y = 0; y < entry.bmHeight; y++) { + for (int x = 0; x < entry.bmWidth; x++) { + uint8_t bAlpha = (uint8_t)((bitmap[width * y + x] & 0xff) >> 4); + bitmapData16[entry.bmWidth * y + x] = (bAlpha << 12) | 0x0fff; + } + } + } else if (texFormat == Draw::DataFormat::R8_UNORM) { + bitmapData.resize(entry.bmWidth * entry.bmHeight); + for (int y = 0; y < entry.bmHeight; y++) { + for (int x = 0; x < entry.bmWidth; x++) { + uint8_t bAlpha = bitmap[width * y + x] & 0xff; + bitmapData[entry.bmWidth * y + x] = bAlpha; + } + } + } else { + _assert_msg_(false, "Bad TextDrawer format"); + } + + delete [] bitmap; + return true; +} + +void TextDrawerCocoa::OncePerFrame() { + frameCount_++; + // If DPI changed (small-mode, future proper monitor DPI support), drop everything. + float newDpiScale = CalculateDPIScale(); + if (newDpiScale != dpiScale_) { + INFO_LOG(G3D, "TextDrawerCocoa: DPI scale: %0.1f", newDpiScale); + 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/Common/Render/Text/draw_text_qt.cpp b/Common/Render/Text/draw_text_qt.cpp index ebcddcc1b7..60e863c63a 100644 --- a/Common/Render/Text/draw_text_qt.cpp +++ b/Common/Render/Text/draw_text_qt.cpp @@ -89,10 +89,10 @@ void TextDrawerQt::MeasureStringRect(std::string_view str, const Bounds &bounds, *h = (float)size.height() * fontScaleY_ * dpiScale_; } -void TextDrawerQt::DrawStringBitmap(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align) { +bool TextDrawerQt::DrawStringBitmap(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align) { if (str.empty()) { bitmapData.clear(); - return; + return false; } QFont *font = fontMap_.find(fontHash_)->second; @@ -101,7 +101,7 @@ void TextDrawerQt::DrawStringBitmap(std::vector &bitmapData, TextString QImage image((size.width() + 3) & ~3, (size.height() + 3) & ~3, QImage::Format_ARGB32_Premultiplied); if (image.isNull()) { bitmapData.clear(); - return; + return false; } image.fill(0); @@ -136,6 +136,7 @@ void TextDrawerQt::DrawStringBitmap(std::vector &bitmapData, TextString } else { _assert_msg_(false, "Bad TextDrawer format"); } + return true; } void TextDrawerQt::DrawString(DrawBuffer &target, std::string_view str, float x, float y, uint32_t color, int align) { diff --git a/Common/Render/Text/draw_text_qt.h b/Common/Render/Text/draw_text_qt.h index 453c00f1e8..33de3578a4 100644 --- a/Common/Render/Text/draw_text_qt.h +++ b/Common/Render/Text/draw_text_qt.h @@ -19,7 +19,7 @@ public: void MeasureString(std::string_view str, float *w, float *h) override; void MeasureStringRect(std::string_view str, const Bounds &bounds, float *w, float *h, int align = ALIGN_TOPLEFT) override; void DrawString(DrawBuffer &target, std::string_view str, float x, float y, uint32_t color, int align = ALIGN_TOPLEFT) override; - void DrawStringBitmap(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align = ALIGN_TOPLEFT) override; + bool DrawStringBitmap(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align = ALIGN_TOPLEFT) override; // Use for housekeeping like throwing out old strings. void OncePerFrame() override; @@ -28,9 +28,6 @@ protected: uint32_t fontHash_; std::map fontMap_; - - std::map> cache_; - std::map> sizeCache_; }; #endif diff --git a/Common/Render/Text/draw_text_sdl.cpp b/Common/Render/Text/draw_text_sdl.cpp index b8db152c47..7923386f04 100644 --- a/Common/Render/Text/draw_text_sdl.cpp +++ b/Common/Render/Text/draw_text_sdl.cpp @@ -416,10 +416,10 @@ void TextDrawerSDL::DrawString(DrawBuffer &target, std::string_view str, float x } } -void TextDrawerSDL::DrawStringBitmap(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align) { +bool TextDrawerSDL::DrawStringBitmap(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align) { if (str.empty()) { bitmapData.clear(); - return; + return false; } // Replace "&&" with "&" @@ -492,6 +492,7 @@ void TextDrawerSDL::DrawStringBitmap(std::vector &bitmapData, TextStrin SDL_UnlockSurface(text); SDL_FreeSurface(text); + return true; } void TextDrawerSDL::OncePerFrame() { diff --git a/Common/Render/Text/draw_text_sdl.h b/Common/Render/Text/draw_text_sdl.h index 7b960be64e..30b44ca191 100644 --- a/Common/Render/Text/draw_text_sdl.h +++ b/Common/Render/Text/draw_text_sdl.h @@ -22,7 +22,7 @@ public: void MeasureString(std::string_view str, float *w, float *h) override; void MeasureStringRect(std::string_view str, const Bounds &bounds, float *w, float *h, int align = ALIGN_TOPLEFT) override; void DrawString(DrawBuffer &target, std::string_view str, float x, float y, uint32_t color, int align = ALIGN_TOPLEFT) override; - void DrawStringBitmap(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align = ALIGN_TOPLEFT) override; + bool DrawStringBitmap(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align = ALIGN_TOPLEFT) override; // Use for housekeeping like throwing out old strings. void OncePerFrame() override; @@ -35,9 +35,6 @@ protected: uint32_t fontHash_; std::map fontMap_; - std::map> cache_; - std::map> sizeCache_; - std::vector<_TTF_Font *> fallbackFonts_; std::vector> fallbackFontPaths_; // path and font face index diff --git a/Common/Render/Text/draw_text_uwp.cpp b/Common/Render/Text/draw_text_uwp.cpp index 64cc154b85..82b52a8546 100644 --- a/Common/Render/Text/draw_text_uwp.cpp +++ b/Common/Render/Text/draw_text_uwp.cpp @@ -312,10 +312,10 @@ void TextDrawerUWP::MeasureStringRect(std::string_view str, const Bounds &bounds *h = total_h * fontScaleY_ * dpiScale_; } -void TextDrawerUWP::DrawStringBitmap(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align) { +bool TextDrawerUWP::DrawStringBitmap(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align) { if (!str.empty()) { bitmapData.clear(); - return; + return false; } std::wstring wstr = ConvertUTF8ToWString(ReplaceAll(ReplaceAll(str, "\n", "\r\n"), "&&", "&")); @@ -328,7 +328,7 @@ void TextDrawerUWP::DrawStringBitmap(std::vector &bitmapData, TextStrin } if (!format) { bitmapData.clear(); - return; + return false; } if (align & ALIGN_HCENTER) @@ -437,6 +437,7 @@ void TextDrawerUWP::DrawStringBitmap(std::vector &bitmapData, TextStrin } ctx_->mirror_bmp->Unmap(); + return true; } void TextDrawerUWP::DrawString(DrawBuffer &target, std::string_view str, float x, float y, uint32_t color, int align) { diff --git a/Common/Render/Text/draw_text_uwp.h b/Common/Render/Text/draw_text_uwp.h index 704fea832e..fa5b8998df 100644 --- a/Common/Render/Text/draw_text_uwp.h +++ b/Common/Render/Text/draw_text_uwp.h @@ -24,7 +24,7 @@ public: void MeasureString(std::string_view str, float *w, float *h) override; void MeasureStringRect(std::string_view str, const Bounds &bounds, float *w, float *h, int align = ALIGN_TOPLEFT) override; void DrawString(DrawBuffer &target, std::string_view str, float x, float y, uint32_t color, int align = ALIGN_TOPLEFT) override; - void DrawStringBitmap(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align = ALIGN_TOPLEFT) override; + bool DrawStringBitmap(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align = ALIGN_TOPLEFT) override; // Use for housekeeping like throwing out old strings. void OncePerFrame() override; @@ -36,9 +36,6 @@ protected: std::map> fontMap_; uint32_t fontHash_; - std::map> cache_; - std::map> sizeCache_; - // Direct2D drawing components. ID2D1Factory5* m_d2dFactory; diff --git a/Common/Render/Text/draw_text_win.cpp b/Common/Render/Text/draw_text_win.cpp index 59e5339cad..0d65fe5503 100644 --- a/Common/Render/Text/draw_text_win.cpp +++ b/Common/Render/Text/draw_text_win.cpp @@ -211,10 +211,10 @@ void TextDrawerWin32::MeasureStringRect(std::string_view str, const Bounds &boun *h = total_h * fontScaleY_ * dpiScale_; } -void TextDrawerWin32::DrawStringBitmap(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align) { +bool TextDrawerWin32::DrawStringBitmap(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align) { if (str.empty()) { bitmapData.clear(); - return; + return false; } std::wstring wstr = ConvertUTF8ToWString(ReplaceAll(str, "\n", "\r\n")); @@ -302,6 +302,7 @@ void TextDrawerWin32::DrawStringBitmap(std::vector &bitmapData, TextStr } else { _assert_msg_(false, "Bad TextDrawer format"); } + return true; } void TextDrawerWin32::DrawString(DrawBuffer &target, std::string_view str, float x, float y, uint32_t color, int align) { diff --git a/Common/Render/Text/draw_text_win.h b/Common/Render/Text/draw_text_win.h index 468eb25328..f53ae7084b 100644 --- a/Common/Render/Text/draw_text_win.h +++ b/Common/Render/Text/draw_text_win.h @@ -24,7 +24,7 @@ public: void MeasureString(std::string_view str, float *w, float *h) override; void MeasureStringRect(std::string_view str, const Bounds &bounds, float *w, float *h, int align = ALIGN_TOPLEFT) override; void DrawString(DrawBuffer &target, std::string_view str, float x, float y, uint32_t color, int align = ALIGN_TOPLEFT) override; - void DrawStringBitmap(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align = ALIGN_TOPLEFT) override; + bool DrawStringBitmap(std::vector &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align = ALIGN_TOPLEFT) override; // Use for housekeeping like throwing out old strings. void OncePerFrame() override; @@ -36,8 +36,6 @@ protected: std::map> fontMap_; uint32_t fontHash_; - std::map> cache_; - std::map> sizeCache_; }; #endif diff --git a/Common/UI/ScrollView.cpp b/Common/UI/ScrollView.cpp index 728bbc7278..c18566d518 100644 --- a/Common/UI/ScrollView.cpp +++ b/Common/UI/ScrollView.cpp @@ -288,7 +288,7 @@ bool ScrollView::SubviewFocused(View *view) { return true; } -NeighborResult ScrollView::FindScrollNeighbor(View *view, const Point &target, FocusDirection direction, NeighborResult best) { +NeighborResult ScrollView::FindScrollNeighbor(View *view, const Point2D &target, FocusDirection direction, NeighborResult best) { if (ContainsSubview(view) && views_[0]->IsViewGroup()) { ViewGroup *vg = static_cast(views_[0]); int found = -1; @@ -315,7 +315,7 @@ NeighborResult ScrollView::FindScrollNeighbor(View *view, const Point &target, F } // Okay, now where is our ideal target? - Point targetPos = view->GetBounds().Center(); + Point2D targetPos = view->GetBounds().Center(); if (orientation_ == ORIENT_VERTICAL) targetPos.y += mult * bounds_.h; else diff --git a/Common/UI/ScrollView.h b/Common/UI/ScrollView.h index a5c31a2feb..99c7304e56 100644 --- a/Common/UI/ScrollView.h +++ b/Common/UI/ScrollView.h @@ -44,7 +44,7 @@ public: alignOpposite_ = alignOpposite; } - NeighborResult FindScrollNeighbor(View *view, const Point &target, FocusDirection direction, NeighborResult best) override; + NeighborResult FindScrollNeighbor(View *view, const Point2D &target, FocusDirection direction, NeighborResult best) override; private: float HardClampedScrollPos(float pos) const; diff --git a/Common/UI/Tween.cpp b/Common/UI/Tween.cpp index bbe819e915..bf7e29026e 100644 --- a/Common/UI/Tween.cpp +++ b/Common/UI/Tween.cpp @@ -67,7 +67,7 @@ void TweenBase::PersistData(PersistStatus status, std::string anonId, Per template void TweenBase::PersistData(PersistStatus status, std::string anonId, PersistMap &storage); template void TweenBase::PersistData(PersistStatus status, std::string anonId, PersistMap &storage); -template void TweenBase::PersistData(PersistStatus status, std::string anonId, PersistMap &storage); +template void TweenBase::PersistData(PersistStatus status, std::string anonId, PersistMap &storage); uint32_t ColorTween::Current(float pos) { return colorBlend(to_, from_, pos); @@ -99,7 +99,7 @@ Visibility VisibilityTween::Current(float p) { } void AnchorTranslateTween::DoApply(View *view, float pos) { - Point cur = Current(pos); + Point2D cur = Current(pos); auto prev = view->GetLayoutParams()->As(); auto lp = new AnchorLayoutParams(prev ? *prev : AnchorLayoutParams(FILL_PARENT, FILL_PARENT)); @@ -108,9 +108,9 @@ void AnchorTranslateTween::DoApply(View *view, float pos) { view->ReplaceLayoutParams(lp); } -Point AnchorTranslateTween::Current(float p) { +Point2D AnchorTranslateTween::Current(float p) { float inv = 1.0f - p; - return Point(from_.x * inv + to_.x * p, from_.y * inv + to_.y * p); + return Point2D(from_.x * inv + to_.x * p, from_.y * inv + to_.y * p); } } // namespace diff --git a/Common/UI/Tween.h b/Common/UI/Tween.h index 2786963d55..c3e4c1181e 100644 --- a/Common/UI/Tween.h +++ b/Common/UI/Tween.h @@ -182,14 +182,14 @@ protected: Visibility Current(float pos) override; }; -class AnchorTranslateTween : public TweenBase { +class AnchorTranslateTween : public TweenBase { public: using TweenBase::TweenBase; protected: void DoApply(View *view, float pos) override; - Point Current(float pos) override; + Point2D Current(float pos) override; }; } // namespace diff --git a/Common/UI/UIScreen.h b/Common/UI/UIScreen.h index 701fe4e42d..2918d2676e 100644 --- a/Common/UI/UIScreen.h +++ b/Common/UI/UIScreen.h @@ -144,7 +144,7 @@ private: int finishFrame_ = -1; DialogResult finishResult_ = DR_CANCEL; bool hasPopupOrigin_ = false; - Point popupOrigin_; + Point2D popupOrigin_; float offsetY_ = 0.0f; bool alignTop_ = false; diff --git a/Common/UI/View.cpp b/Common/UI/View.cpp index 8333c514bf..d6f8e27490 100644 --- a/Common/UI/View.cpp +++ b/Common/UI/View.cpp @@ -168,25 +168,25 @@ void View::PersistData(PersistStatus status, std::string anonId, PersistMap &sto } } -Point View::GetFocusPosition(FocusDirection dir) const { +Point2D View::GetFocusPosition(FocusDirection dir) const { // The +2/-2 is some extra fudge factor to cover for views sitting right next to each other. // Distance zero yields strange results otherwise. switch (dir) { - case FOCUS_LEFT: return Point(bounds_.x + 2, bounds_.centerY()); - case FOCUS_RIGHT: return Point(bounds_.x2() - 2, bounds_.centerY()); - case FOCUS_UP: return Point(bounds_.centerX(), bounds_.y + 2); - case FOCUS_DOWN: return Point(bounds_.centerX(), bounds_.y2() - 2); + case FOCUS_LEFT: return Point2D(bounds_.x + 2, bounds_.centerY()); + case FOCUS_RIGHT: return Point2D(bounds_.x2() - 2, bounds_.centerY()); + case FOCUS_UP: return Point2D(bounds_.centerX(), bounds_.y + 2); + case FOCUS_DOWN: return Point2D(bounds_.centerX(), bounds_.y2() - 2); default: return bounds_.Center(); } } -Point CollapsibleHeader::GetFocusPosition(FocusDirection dir) const { +Point2D CollapsibleHeader::GetFocusPosition(FocusDirection dir) const { // Bias the focus position to the left. switch (dir) { - case FOCUS_UP: return Point(bounds_.x + 50, bounds_.y + 2); - case FOCUS_DOWN: return Point(bounds_.x + 50, bounds_.y2() - 2); + case FOCUS_UP: return Point2D(bounds_.x + 50, bounds_.y + 2); + case FOCUS_DOWN: return Point2D(bounds_.x + 50, bounds_.y2() - 2); default: return View::GetFocusPosition(dir); } diff --git a/Common/UI/View.h b/Common/UI/View.h index 97923f2819..c75b96fcfe 100644 --- a/Common/UI/View.h +++ b/Common/UI/View.h @@ -468,7 +468,7 @@ public: virtual bool IsViewGroup() const { return false; } virtual bool ContainsSubview(const View *view) const { return false; } - virtual Point GetFocusPosition(FocusDirection dir) const; + virtual Point2D GetFocusPosition(FocusDirection dir) const; template T *AddTween(T *t) { @@ -904,7 +904,7 @@ public: void GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert, float &w, float &h) const override; void GetContentDimensions(const UIContext &dc, float &w, float &h) const override; - Point GetFocusPosition(FocusDirection dir) const override; + Point2D GetFocusPosition(FocusDirection dir) const override; void SetHasSubitems(bool hasSubItems) { hasSubItems_ = hasSubItems; } void SetOpenPtr(bool *open) { diff --git a/Common/UI/ViewGroup.cpp b/Common/UI/ViewGroup.cpp index 0120f87419..fd7881ea39 100644 --- a/Common/UI/ViewGroup.cpp +++ b/Common/UI/ViewGroup.cpp @@ -284,7 +284,7 @@ static float VerticalOverlap(const Bounds &a, const Bounds &b) { return std::min(1.0f, overlap / minH); } -float GetTargetScore(const Point &originPos, int originIndex, const View *origin, const View *destination, FocusDirection direction) { +float GetTargetScore(const Point2D &originPos, int originIndex, const View *origin, const View *destination, FocusDirection direction) { // Skip labels and things like that. if (!destination->CanBeFocused()) return 0.0f; @@ -293,7 +293,7 @@ float GetTargetScore(const Point &originPos, int originIndex, const View *origin if (destination->GetVisibility() != V_VISIBLE) return 0.0f; - Point destPos = destination->GetFocusPosition(Opposite(direction)); + Point2D destPos = destination->GetFocusPosition(Opposite(direction)); float dx = destPos.x - originPos.x; float dy = destPos.y - originPos.y; @@ -385,7 +385,7 @@ float GetTargetScore(const Point &originPos, int originIndex, const View *origin } static float GetDirectionScore(int originIndex, const View *origin, View *destination, FocusDirection direction) { - Point originPos = origin->GetFocusPosition(direction); + Point2D originPos = origin->GetFocusPosition(direction); return GetTargetScore(originPos, originIndex, origin, destination, direction); } @@ -444,7 +444,7 @@ NeighborResult ViewGroup::FindNeighbor(View *view, FocusDirection direction, Nei } case FOCUS_PREV_PAGE: case FOCUS_NEXT_PAGE: - return FindScrollNeighbor(view, Point(INFINITY, INFINITY), direction, result); + return FindScrollNeighbor(view, Point2D(INFINITY, INFINITY), direction, result); case FOCUS_PREV: // If view not found, no neighbor to find. if (num == -1) @@ -462,7 +462,7 @@ NeighborResult ViewGroup::FindNeighbor(View *view, FocusDirection direction, Nei } } -NeighborResult ViewGroup::FindScrollNeighbor(View *view, const Point &target, FocusDirection direction, NeighborResult best) { +NeighborResult ViewGroup::FindScrollNeighbor(View *view, const Point2D &target, FocusDirection direction, NeighborResult best) { if (!IsEnabled()) return best; if (GetVisibility() != V_VISIBLE) @@ -1015,21 +1015,21 @@ void TabHolder::SetCurrentTab(int tab, bool skipTween) { // Currently displayed, so let's reset it. if (skipTween) { tabs_[currentTab_]->SetVisibility(V_GONE); - tabTweens_[tab]->Reset(Point(0.0f, 0.0f)); + tabTweens_[tab]->Reset(Point2D(0.0f, 0.0f)); tabTweens_[tab]->Apply(tabs_[tab]); } else { - tabTweens_[currentTab_]->Reset(Point(0.0f, 0.0f)); + tabTweens_[currentTab_]->Reset(Point2D(0.0f, 0.0f)); if (orient == ORIENT_HORIZONTAL) { - tabTweens_[tab]->Reset(Point(bounds_.w * dir, 0.0f)); - tabTweens_[currentTab_]->Divert(Point(bounds_.w * -dir, 0.0f)); + tabTweens_[tab]->Reset(Point2D(bounds_.w * dir, 0.0f)); + tabTweens_[currentTab_]->Divert(Point2D(bounds_.w * -dir, 0.0f)); } else { - tabTweens_[tab]->Reset(Point(0.0f, bounds_.h * dir)); - tabTweens_[currentTab_]->Divert(Point(0.0f, bounds_.h * -dir)); + tabTweens_[tab]->Reset(Point2D(0.0f, bounds_.h * dir)); + tabTweens_[currentTab_]->Divert(Point2D(0.0f, bounds_.h * -dir)); } // Actually move it to the initial position now, just to avoid any flicker. tabTweens_[tab]->Apply(tabs_[tab]); - tabTweens_[tab]->Divert(Point(0.0f, 0.0f)); + tabTweens_[tab]->Divert(Point2D(0.0f, 0.0f)); } tabs_[tab]->SetVisibility(V_VISIBLE); diff --git a/Common/UI/ViewGroup.h b/Common/UI/ViewGroup.h index d54b59a754..9ccde20aec 100644 --- a/Common/UI/ViewGroup.h +++ b/Common/UI/ViewGroup.h @@ -60,7 +60,7 @@ public: // Assumes that layout has taken place. NeighborResult FindNeighbor(View *view, FocusDirection direction, NeighborResult best); - virtual NeighborResult FindScrollNeighbor(View *view, const Point &target, FocusDirection direction, NeighborResult best); + virtual NeighborResult FindScrollNeighbor(View *view, const Point2D &target, FocusDirection direction, NeighborResult best); bool CanBeFocused() const override { return false; } bool IsViewGroup() const override { return true; } diff --git a/UI/TouchControlLayoutScreen.cpp b/UI/TouchControlLayoutScreen.cpp index 0f66931b2e..367fee910d 100644 --- a/UI/TouchControlLayoutScreen.cpp +++ b/UI/TouchControlLayoutScreen.cpp @@ -370,8 +370,8 @@ public: int mode_ = 0; }; -static Point ClampTo(const Point &p, const Bounds &b) { - return Point(clamp_value(p.x, b.x, b.x + b.w), clamp_value(p.y, b.y, b.y + b.h)); +static Point2D ClampTo(const Point2D &p, const Bounds &b) { + return Point2D(clamp_value(p.x, b.x, b.x + b.w), clamp_value(p.y, b.y, b.y + b.h)); } bool ControlLayoutView::Touch(const TouchInput &touch) { @@ -393,7 +393,7 @@ bool ControlLayoutView::Touch(const TouchInput &touch) { //validRange.y += controlBounds.h * 0.5f; //validRange.h -= controlBounds.h; - Point newPos; + Point2D newPos; newPos.x = startObjectX_ + (touch.x - startDragX_); newPos.y = startObjectY_ + (touch.y - startDragY_); if (g_Config.bTouchSnapToGrid) {