mirror of
https://github.com/love2d/love.git
synced 2026-08-19 20:19:42 +02:00
Merge branch 'text-shaping' into 12.0-development
This commit is contained in:
@@ -103,6 +103,7 @@ if(MEGA)
|
|||||||
|
|
||||||
set(LOVE_LINK_LIBRARIES
|
set(LOVE_LINK_LIBRARIES
|
||||||
${MEGA_FREETYPE}
|
${MEGA_FREETYPE}
|
||||||
|
${MEGA_HARFBUZZ}
|
||||||
${MEGA_LIBOGG}
|
${MEGA_LIBOGG}
|
||||||
${MEGA_LIBVORBISFILE}
|
${MEGA_LIBVORBISFILE}
|
||||||
${MEGA_LIBVORBIS}
|
${MEGA_LIBVORBIS}
|
||||||
@@ -173,6 +174,7 @@ Please see https://github.com/love2d/megasource
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
find_package(Freetype REQUIRED)
|
find_package(Freetype REQUIRED)
|
||||||
|
find_package(harfbuzz REQUIRED)
|
||||||
find_package(ModPlug REQUIRED)
|
find_package(ModPlug REQUIRED)
|
||||||
find_package(OpenAL REQUIRED)
|
find_package(OpenAL REQUIRED)
|
||||||
find_package(OpenGL REQUIRED)
|
find_package(OpenGL REQUIRED)
|
||||||
@@ -200,6 +202,7 @@ Please see https://github.com/love2d/megasource
|
|||||||
${OPENGL_gl_LIBRARY}
|
${OPENGL_gl_LIBRARY}
|
||||||
${SDL2_LIBRARY}
|
${SDL2_LIBRARY}
|
||||||
${FREETYPE_LIBRARY}
|
${FREETYPE_LIBRARY}
|
||||||
|
${HARFBUZZ_LIBRARY}
|
||||||
${OPENAL_LIBRARY}
|
${OPENAL_LIBRARY}
|
||||||
${MODPLUG_LIBRARY}
|
${MODPLUG_LIBRARY}
|
||||||
${THEORA_LIBRARY}
|
${THEORA_LIBRARY}
|
||||||
@@ -477,12 +480,16 @@ set(LOVE_SRC_MODULE_FONT_ROOT
|
|||||||
src/modules/font/BMFontRasterizer.h
|
src/modules/font/BMFontRasterizer.h
|
||||||
src/modules/font/Font.cpp
|
src/modules/font/Font.cpp
|
||||||
src/modules/font/Font.h
|
src/modules/font/Font.h
|
||||||
|
src/modules/font/GenericShaper.cpp
|
||||||
|
src/modules/font/GenericShaper.h
|
||||||
src/modules/font/GlyphData.cpp
|
src/modules/font/GlyphData.cpp
|
||||||
src/modules/font/GlyphData.h
|
src/modules/font/GlyphData.h
|
||||||
src/modules/font/ImageRasterizer.cpp
|
src/modules/font/ImageRasterizer.cpp
|
||||||
src/modules/font/ImageRasterizer.h
|
src/modules/font/ImageRasterizer.h
|
||||||
src/modules/font/Rasterizer.cpp
|
src/modules/font/Rasterizer.cpp
|
||||||
src/modules/font/Rasterizer.h
|
src/modules/font/Rasterizer.h
|
||||||
|
src/modules/font/TextShaper.cpp
|
||||||
|
src/modules/font/TextShaper.h
|
||||||
src/modules/font/TrueTypeRasterizer.cpp
|
src/modules/font/TrueTypeRasterizer.cpp
|
||||||
src/modules/font/TrueTypeRasterizer.h
|
src/modules/font/TrueTypeRasterizer.h
|
||||||
src/modules/font/wrap_Font.cpp
|
src/modules/font/wrap_Font.cpp
|
||||||
@@ -496,6 +503,8 @@ set(LOVE_SRC_MODULE_FONT_ROOT
|
|||||||
set(LOVE_SRC_MODULE_FONT_FREETYPE
|
set(LOVE_SRC_MODULE_FONT_FREETYPE
|
||||||
src/modules/font/freetype/Font.cpp
|
src/modules/font/freetype/Font.cpp
|
||||||
src/modules/font/freetype/Font.h
|
src/modules/font/freetype/Font.h
|
||||||
|
src/modules/font/freetype/HarfbuzzShaper.cpp
|
||||||
|
src/modules/font/freetype/HarfbuzzShaper.h
|
||||||
src/modules/font/freetype/TrueTypeRasterizer.cpp
|
src/modules/font/freetype/TrueTypeRasterizer.cpp
|
||||||
src/modules/font/freetype/TrueTypeRasterizer.h
|
src/modules/font/freetype/TrueTypeRasterizer.h
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -67,7 +67,10 @@ ACLOVE_DEP_PTHREAD
|
|||||||
|
|
||||||
# Conditional dependencies
|
# Conditional dependencies
|
||||||
AS_VAR_IF([enable_module_audio], [yes], [ACLOVE_DEP_OPENAL], [])
|
AS_VAR_IF([enable_module_audio], [yes], [ACLOVE_DEP_OPENAL], [])
|
||||||
AS_VAR_IF([enable_module_font], [yes], [ACLOVE_DEP_FREETYPE2], [])
|
AS_VAR_IF([enable_module_font], [yes], [
|
||||||
|
ACLOVE_DEP_FREETYPE2
|
||||||
|
ACLOVE_DEP_HARFBUZZ
|
||||||
|
], [])
|
||||||
AS_VAR_IF([enable_module_sound], [yes], [
|
AS_VAR_IF([enable_module_sound], [yes], [
|
||||||
ACLOVE_DEP_LIBMODPLUG
|
ACLOVE_DEP_LIBMODPLUG
|
||||||
ACLOVE_DEP_VORBISFILE
|
ACLOVE_DEP_VORBISFILE
|
||||||
|
|||||||
@@ -8,12 +8,13 @@ Build-Depends: debhelper (>= 9),
|
|||||||
libtool,
|
libtool,
|
||||||
g++ (>= 4.7.0),
|
g++ (>= 4.7.0),
|
||||||
libfreetype6-dev,
|
libfreetype6-dev,
|
||||||
|
libharfbuzz-dev,
|
||||||
luajit,
|
luajit,
|
||||||
libluajit-5.1-dev,
|
libluajit-5.1-dev,
|
||||||
libmodplug-dev,
|
libmodplug-dev,
|
||||||
libopenal-dev,
|
libopenal-dev,
|
||||||
libphysfs-dev,
|
libphysfs-dev,
|
||||||
libsdl2-dev (>= 2.0.1),
|
libsdl2-dev (>= 2.0.9),
|
||||||
libogg-dev,
|
libogg-dev,
|
||||||
libvorbis-dev,
|
libvorbis-dev,
|
||||||
libtheora-dev,
|
libtheora-dev,
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
AC_DEFUN([ACLOVE_DEP_FREETYPE2], [
|
AC_DEFUN([ACLOVE_DEP_FREETYPE2], [
|
||||||
PKG_CHECK_MODULES([freetype2], [freetype2], [], [LOVE_MSG_ERROR([FreeType2])])])
|
PKG_CHECK_MODULES([freetype2], [freetype2], [], [LOVE_MSG_ERROR([FreeType2])])])
|
||||||
|
|
||||||
|
AC_DEFUN([ACLOVE_DEP_HARFBUZZ], [
|
||||||
|
PKG_CHECK_MODULES([harfbuzz], [harfbuzz], [], [LOVE_MSG_ERROR([Harfbuzz])])])
|
||||||
|
|
||||||
AC_DEFUN([ACLOVE_DEP_OPENAL], [
|
AC_DEFUN([ACLOVE_DEP_OPENAL], [
|
||||||
PKG_CHECK_MODULES([openal], [openal], [], [LOVE_MSG_ERROR([OpenAL])])])
|
PKG_CHECK_MODULES([openal], [openal], [], [LOVE_MSG_ERROR([OpenAL])])])
|
||||||
|
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ inc_libraries="$inc_current/libraries"
|
|||||||
|
|
||||||
cat > src/Makefile.am << EOF
|
cat > src/Makefile.am << EOF
|
||||||
AM_CPPFLAGS = -I$inc_current -I$inc_modules -I$inc_libraries -I$inc_libraries/enet/libenet/include -I$inc_libraries/box2d \$(LOVE_INCLUDES) \$(FILE_OFFSET)\
|
AM_CPPFLAGS = -I$inc_current -I$inc_modules -I$inc_libraries -I$inc_libraries/enet/libenet/include -I$inc_libraries/box2d \$(LOVE_INCLUDES) \$(FILE_OFFSET)\
|
||||||
\$(SDL_CFLAGS) \$(lua_CFLAGS) \$(freetype2_CFLAGS)\
|
\$(SDL_CFLAGS) \$(lua_CFLAGS) \$(freetype2_CFLAGS) \$(harfbuzz_CFLAGS)\
|
||||||
\$(openal_CFLAGS) \$(zlib_CFLAGS) \$(libmodplug_CFLAGS)\
|
\$(openal_CFLAGS) \$(zlib_CFLAGS) \$(libmodplug_CFLAGS)\
|
||||||
\$(vorbisfile_CFLAGS) \$(theora_CFLAGS)
|
\$(vorbisfile_CFLAGS) \$(theora_CFLAGS)
|
||||||
AUTOMAKE_OPTIONS = subdir-objects
|
AUTOMAKE_OPTIONS = subdir-objects
|
||||||
@@ -139,7 +139,7 @@ endif
|
|||||||
lib_LTLIBRARIES = liblove${love_suffix}.la
|
lib_LTLIBRARIES = liblove${love_suffix}.la
|
||||||
liblove${love_amsuffix}_la_LDFLAGS = -module -export-dynamic \$(LDFLAGS) -release \$(PACKAGE_VERSION)
|
liblove${love_amsuffix}_la_LDFLAGS = -module -export-dynamic \$(LDFLAGS) -release \$(PACKAGE_VERSION)
|
||||||
liblove${love_amsuffix}_la_LIBADD = \
|
liblove${love_amsuffix}_la_LIBADD = \
|
||||||
\$(SDL_LIBS) \$(freetype2_LIBS) \$(lua_LIBS)\
|
\$(SDL_LIBS) \$(freetype2_LIBS) \$(harfbuzz_LIBS) \$(lua_LIBS)\
|
||||||
\$(openal_LIBS) \$(zlib_LIBS) \$(libmodplug_LIBS)\
|
\$(openal_LIBS) \$(zlib_LIBS) \$(libmodplug_LIBS)\
|
||||||
\$(vorbisfile_LIBS) \$(theora_LIBS)
|
\$(vorbisfile_LIBS) \$(theora_LIBS)
|
||||||
|
|
||||||
|
|||||||
@@ -50,6 +50,17 @@
|
|||||||
217DFC101D9F6D490055D849 /* url.lua.h in Headers */ = {isa = PBXBuildFile; fileRef = 217DFBD41D9F6D490055D849 /* url.lua.h */; };
|
217DFC101D9F6D490055D849 /* url.lua.h in Headers */ = {isa = PBXBuildFile; fileRef = 217DFBD41D9F6D490055D849 /* url.lua.h */; };
|
||||||
217DFC111D9F6D490055D849 /* usocket.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFBD51D9F6D490055D849 /* usocket.c */; };
|
217DFC111D9F6D490055D849 /* usocket.c in Sources */ = {isa = PBXBuildFile; fileRef = 217DFBD51D9F6D490055D849 /* usocket.c */; };
|
||||||
217DFC121D9F6D490055D849 /* usocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 217DFBD61D9F6D490055D849 /* usocket.h */; };
|
217DFC121D9F6D490055D849 /* usocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 217DFBD61D9F6D490055D849 /* usocket.h */; };
|
||||||
|
D923E7D3296B85B9002FF1B3 /* harfbuzz.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = D923E7D2296B85B9002FF1B3 /* harfbuzz.xcframework */; };
|
||||||
|
D9DAB9222961F0EE00C64820 /* HarfbuzzShaper.h in Headers */ = {isa = PBXBuildFile; fileRef = D9DAB9202961F0EE00C64820 /* HarfbuzzShaper.h */; };
|
||||||
|
D9DAB9232961F0EE00C64820 /* HarfbuzzShaper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D9DAB9212961F0EE00C64820 /* HarfbuzzShaper.cpp */; };
|
||||||
|
D9DAB9242961F0EE00C64820 /* HarfbuzzShaper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D9DAB9212961F0EE00C64820 /* HarfbuzzShaper.cpp */; };
|
||||||
|
D9DAB9292961F10000C64820 /* GenericShaper.h in Headers */ = {isa = PBXBuildFile; fileRef = D9DAB9252961F0FF00C64820 /* GenericShaper.h */; };
|
||||||
|
D9DAB92A2961F10000C64820 /* GenericShaper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D9DAB9262961F0FF00C64820 /* GenericShaper.cpp */; };
|
||||||
|
D9DAB92B2961F10000C64820 /* GenericShaper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D9DAB9262961F0FF00C64820 /* GenericShaper.cpp */; };
|
||||||
|
D9DAB92C2961F10000C64820 /* TextShaper.h in Headers */ = {isa = PBXBuildFile; fileRef = D9DAB9272961F0FF00C64820 /* TextShaper.h */; };
|
||||||
|
D9DAB92D2961F10000C64820 /* TextShaper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D9DAB9282961F10000C64820 /* TextShaper.cpp */; };
|
||||||
|
D9DAB92E2961F10000C64820 /* TextShaper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D9DAB9282961F10000C64820 /* TextShaper.cpp */; };
|
||||||
|
D9DAB9322963CD7500C64820 /* harfbuzz.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D9DAB9312963CD7500C64820 /* harfbuzz.framework */; };
|
||||||
FA0A3A5F23366CE9001C269E /* floattypes.h in Headers */ = {isa = PBXBuildFile; fileRef = FA0A3A5D23366CE9001C269E /* floattypes.h */; };
|
FA0A3A5F23366CE9001C269E /* floattypes.h in Headers */ = {isa = PBXBuildFile; fileRef = FA0A3A5D23366CE9001C269E /* floattypes.h */; };
|
||||||
FA0A3A6023366CE9001C269E /* floattypes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0A3A5E23366CE9001C269E /* floattypes.cpp */; };
|
FA0A3A6023366CE9001C269E /* floattypes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0A3A5E23366CE9001C269E /* floattypes.cpp */; };
|
||||||
FA0A3A6123366CE9001C269E /* floattypes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0A3A5E23366CE9001C269E /* floattypes.cpp */; };
|
FA0A3A6123366CE9001C269E /* floattypes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA0A3A5E23366CE9001C269E /* floattypes.cpp */; };
|
||||||
@@ -1391,6 +1402,14 @@
|
|||||||
217DFBD41D9F6D490055D849 /* url.lua.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = url.lua.h; sourceTree = "<group>"; };
|
217DFBD41D9F6D490055D849 /* url.lua.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = url.lua.h; sourceTree = "<group>"; };
|
||||||
217DFBD51D9F6D490055D849 /* usocket.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = usocket.c; sourceTree = "<group>"; };
|
217DFBD51D9F6D490055D849 /* usocket.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = usocket.c; sourceTree = "<group>"; };
|
||||||
217DFBD61D9F6D490055D849 /* usocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = usocket.h; sourceTree = "<group>"; };
|
217DFBD61D9F6D490055D849 /* usocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = usocket.h; sourceTree = "<group>"; };
|
||||||
|
D923E7D2296B85B9002FF1B3 /* harfbuzz.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = harfbuzz.xcframework; path = ios/libraries/harfbuzz.xcframework; sourceTree = "<group>"; };
|
||||||
|
D9DAB9202961F0EE00C64820 /* HarfbuzzShaper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HarfbuzzShaper.h; sourceTree = "<group>"; };
|
||||||
|
D9DAB9212961F0EE00C64820 /* HarfbuzzShaper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HarfbuzzShaper.cpp; sourceTree = "<group>"; };
|
||||||
|
D9DAB9252961F0FF00C64820 /* GenericShaper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GenericShaper.h; sourceTree = "<group>"; };
|
||||||
|
D9DAB9262961F0FF00C64820 /* GenericShaper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GenericShaper.cpp; sourceTree = "<group>"; };
|
||||||
|
D9DAB9272961F0FF00C64820 /* TextShaper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextShaper.h; sourceTree = "<group>"; };
|
||||||
|
D9DAB9282961F10000C64820 /* TextShaper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextShaper.cpp; sourceTree = "<group>"; };
|
||||||
|
D9DAB9312963CD7500C64820 /* harfbuzz.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = harfbuzz.framework; path = macosx/Frameworks/harfbuzz.framework; sourceTree = "<group>"; };
|
||||||
FA08F5AE16C7525600F007B5 /* liblove-macosx.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "liblove-macosx.plist"; path = "macosx/liblove-macosx.plist"; sourceTree = "<group>"; };
|
FA08F5AE16C7525600F007B5 /* liblove-macosx.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "liblove-macosx.plist"; path = "macosx/liblove-macosx.plist"; sourceTree = "<group>"; };
|
||||||
FA0A3A5D23366CE9001C269E /* floattypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = floattypes.h; sourceTree = "<group>"; };
|
FA0A3A5D23366CE9001C269E /* floattypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = floattypes.h; sourceTree = "<group>"; };
|
||||||
FA0A3A5E23366CE9001C269E /* floattypes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = floattypes.cpp; sourceTree = "<group>"; };
|
FA0A3A5E23366CE9001C269E /* floattypes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = floattypes.cpp; sourceTree = "<group>"; };
|
||||||
@@ -2288,6 +2307,7 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
FACFB751276D7E3B0089F78D /* freetype.xcframework in Frameworks */,
|
FACFB751276D7E3B0089F78D /* freetype.xcframework in Frameworks */,
|
||||||
|
D923E7D3296B85B9002FF1B3 /* harfbuzz.xcframework in Frameworks */,
|
||||||
FA84DE7A277D4C88002674C6 /* modplug.xcframework in Frameworks */,
|
FA84DE7A277D4C88002674C6 /* modplug.xcframework in Frameworks */,
|
||||||
FA84DE7C277E045E002674C6 /* ogg.xcframework in Frameworks */,
|
FA84DE7C277E045E002674C6 /* ogg.xcframework in Frameworks */,
|
||||||
FACFB753276D7F860089F78D /* Lua.xcframework in Frameworks */,
|
FACFB753276D7F860089F78D /* Lua.xcframework in Frameworks */,
|
||||||
@@ -2311,6 +2331,7 @@
|
|||||||
FA577AC516C7513400860150 /* libmodplug.framework in Frameworks */,
|
FA577AC516C7513400860150 /* libmodplug.framework in Frameworks */,
|
||||||
FADF4CC62663D0EC004F95C1 /* libz.tbd in Frameworks */,
|
FADF4CC62663D0EC004F95C1 /* libz.tbd in Frameworks */,
|
||||||
FA577AC816C7513C00860150 /* ogg.framework in Frameworks */,
|
FA577AC816C7513C00860150 /* ogg.framework in Frameworks */,
|
||||||
|
D9DAB9322963CD7500C64820 /* harfbuzz.framework in Frameworks */,
|
||||||
FA577ACA16C7514100860150 /* OpenGL.framework in Frameworks */,
|
FA577ACA16C7514100860150 /* OpenGL.framework in Frameworks */,
|
||||||
FA577ACD16C7514C00860150 /* vorbis.framework in Frameworks */,
|
FA577ACD16C7514C00860150 /* vorbis.framework in Frameworks */,
|
||||||
);
|
);
|
||||||
@@ -2810,6 +2831,8 @@
|
|||||||
FA0B7B731A95902C000E1D17 /* Font.cpp */,
|
FA0B7B731A95902C000E1D17 /* Font.cpp */,
|
||||||
FA0B7B741A95902C000E1D17 /* Font.h */,
|
FA0B7B741A95902C000E1D17 /* Font.h */,
|
||||||
FA0B7B751A95902C000E1D17 /* freetype */,
|
FA0B7B751A95902C000E1D17 /* freetype */,
|
||||||
|
D9DAB9262961F0FF00C64820 /* GenericShaper.cpp */,
|
||||||
|
D9DAB9252961F0FF00C64820 /* GenericShaper.h */,
|
||||||
FA0B7B7A1A95902C000E1D17 /* GlyphData.cpp */,
|
FA0B7B7A1A95902C000E1D17 /* GlyphData.cpp */,
|
||||||
FA0B7B7B1A95902C000E1D17 /* GlyphData.h */,
|
FA0B7B7B1A95902C000E1D17 /* GlyphData.h */,
|
||||||
FA0B7B7C1A95902C000E1D17 /* ImageRasterizer.cpp */,
|
FA0B7B7C1A95902C000E1D17 /* ImageRasterizer.cpp */,
|
||||||
@@ -2817,6 +2840,8 @@
|
|||||||
FA522D5923FA5ED40059EE3C /* NotoSans-Regular.ttf.gzip.h */,
|
FA522D5923FA5ED40059EE3C /* NotoSans-Regular.ttf.gzip.h */,
|
||||||
FA0B7B7E1A95902C000E1D17 /* Rasterizer.cpp */,
|
FA0B7B7E1A95902C000E1D17 /* Rasterizer.cpp */,
|
||||||
FA0B7B7F1A95902C000E1D17 /* Rasterizer.h */,
|
FA0B7B7F1A95902C000E1D17 /* Rasterizer.h */,
|
||||||
|
D9DAB9282961F10000C64820 /* TextShaper.cpp */,
|
||||||
|
D9DAB9272961F0FF00C64820 /* TextShaper.h */,
|
||||||
FAB2D5A81AABDD8A008224A4 /* TrueTypeRasterizer.cpp */,
|
FAB2D5A81AABDD8A008224A4 /* TrueTypeRasterizer.cpp */,
|
||||||
FAB2D5A91AABDD8A008224A4 /* TrueTypeRasterizer.h */,
|
FAB2D5A91AABDD8A008224A4 /* TrueTypeRasterizer.h */,
|
||||||
FA0B7B811A95902C000E1D17 /* wrap_Font.cpp */,
|
FA0B7B811A95902C000E1D17 /* wrap_Font.cpp */,
|
||||||
@@ -2834,6 +2859,8 @@
|
|||||||
children = (
|
children = (
|
||||||
FA0B7B761A95902C000E1D17 /* Font.cpp */,
|
FA0B7B761A95902C000E1D17 /* Font.cpp */,
|
||||||
FA0B7B771A95902C000E1D17 /* Font.h */,
|
FA0B7B771A95902C000E1D17 /* Font.h */,
|
||||||
|
D9DAB9212961F0EE00C64820 /* HarfbuzzShaper.cpp */,
|
||||||
|
D9DAB9202961F0EE00C64820 /* HarfbuzzShaper.h */,
|
||||||
FA0B7B781A95902C000E1D17 /* TrueTypeRasterizer.cpp */,
|
FA0B7B781A95902C000E1D17 /* TrueTypeRasterizer.cpp */,
|
||||||
FA0B7B791A95902C000E1D17 /* TrueTypeRasterizer.h */,
|
FA0B7B791A95902C000E1D17 /* TrueTypeRasterizer.h */,
|
||||||
);
|
);
|
||||||
@@ -3366,6 +3393,7 @@
|
|||||||
FA577A7916C71A1700860150 /* Cocoa.framework */,
|
FA577A7916C71A1700860150 /* Cocoa.framework */,
|
||||||
FAA627CD18E7E1560080752D /* CoreServices.framework */,
|
FAA627CD18E7E1560080752D /* CoreServices.framework */,
|
||||||
FAD43ECB1FF312D800831BB8 /* freetype.framework */,
|
FAD43ECB1FF312D800831BB8 /* freetype.framework */,
|
||||||
|
D9DAB9312963CD7500C64820 /* harfbuzz.framework */,
|
||||||
FA577A8216C71A5300860150 /* libmodplug.framework */,
|
FA577A8216C71A5300860150 /* libmodplug.framework */,
|
||||||
FADF4CC52663D0EC004F95C1 /* libz.tbd */,
|
FADF4CC52663D0EC004F95C1 /* libz.tbd */,
|
||||||
FA577A6D16C719EA00860150 /* Lua.framework */,
|
FA577A6D16C719EA00860150 /* Lua.framework */,
|
||||||
@@ -3533,6 +3561,7 @@
|
|||||||
FA5D24A31A96D2C300C6FC8F /* ios */ = {
|
FA5D24A31A96D2C300C6FC8F /* ios */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
D923E7D2296B85B9002FF1B3 /* harfbuzz.xcframework */,
|
||||||
FACFB750276D7E2B0089F78D /* freetype.xcframework */,
|
FACFB750276D7E2B0089F78D /* freetype.xcframework */,
|
||||||
FACFB752276D7F6F0089F78D /* Lua.xcframework */,
|
FACFB752276D7F6F0089F78D /* Lua.xcframework */,
|
||||||
FA84DE79277D4C88002674C6 /* modplug.xcframework */,
|
FA84DE79277D4C88002674C6 /* modplug.xcframework */,
|
||||||
@@ -4100,6 +4129,7 @@
|
|||||||
FABDA9B82552448300B5C523 /* b2_motor_joint.h in Headers */,
|
FABDA9B82552448300B5C523 /* b2_motor_joint.h in Headers */,
|
||||||
FA0B7AC11A958EA3000E1D17 /* callbacks.h in Headers */,
|
FA0B7AC11A958EA3000E1D17 /* callbacks.h in Headers */,
|
||||||
FA3C5E491F8D80CA0003C579 /* ShaderStage.h in Headers */,
|
FA3C5E491F8D80CA0003C579 /* ShaderStage.h in Headers */,
|
||||||
|
D9DAB9292961F10000C64820 /* GenericShaper.h in Headers */,
|
||||||
FA0B7D8F1A95902C000E1D17 /* ddsHandler.h in Headers */,
|
FA0B7D8F1A95902C000E1D17 /* ddsHandler.h in Headers */,
|
||||||
FAB2D5AC1AABDD8A008224A4 /* TrueTypeRasterizer.h in Headers */,
|
FAB2D5AC1AABDD8A008224A4 /* TrueTypeRasterizer.h in Headers */,
|
||||||
FABDAA042552448300B5C523 /* b2_edge_shape.h in Headers */,
|
FABDAA042552448300B5C523 /* b2_edge_shape.h in Headers */,
|
||||||
@@ -4137,6 +4167,7 @@
|
|||||||
FA0B7EDD1A95902D000E1D17 /* Touch.h in Headers */,
|
FA0B7EDD1A95902D000E1D17 /* Touch.h in Headers */,
|
||||||
FA0B7EDE1A95902D000E1D17 /* Touch.h in Headers */,
|
FA0B7EDE1A95902D000E1D17 /* Touch.h in Headers */,
|
||||||
FAC7CD861FE35E95006A60C7 /* physfs.h in Headers */,
|
FAC7CD861FE35E95006A60C7 /* physfs.h in Headers */,
|
||||||
|
D9DAB92C2961F10000C64820 /* TextShaper.h in Headers */,
|
||||||
FAF6C9E523C2DE2900D7B5BC /* spirv.hpp in Headers */,
|
FAF6C9E523C2DE2900D7B5BC /* spirv.hpp in Headers */,
|
||||||
FA522D4F23F9FE380059EE3C /* MP3Decoder.h in Headers */,
|
FA522D4F23F9FE380059EE3C /* MP3Decoder.h in Headers */,
|
||||||
217DFBEE1D9F6D490055D849 /* luasocket.h in Headers */,
|
217DFBEE1D9F6D490055D849 /* luasocket.h in Headers */,
|
||||||
@@ -4181,6 +4212,7 @@
|
|||||||
FAF6C9E823C2DE2900D7B5BC /* GLSL.ext.EXT.h in Headers */,
|
FAF6C9E823C2DE2900D7B5BC /* GLSL.ext.EXT.h in Headers */,
|
||||||
FACA02F71F5E396B0084B28F /* wrap_DataModule.h in Headers */,
|
FACA02F71F5E396B0084B28F /* wrap_DataModule.h in Headers */,
|
||||||
FABDA9BE2552448300B5C523 /* Box2D.h in Headers */,
|
FABDA9BE2552448300B5C523 /* Box2D.h in Headers */,
|
||||||
|
D9DAB9222961F0EE00C64820 /* HarfbuzzShaper.h in Headers */,
|
||||||
FA56AA3A1FAFF02000A43D5F /* memory.h in Headers */,
|
FA56AA3A1FAFF02000A43D5F /* memory.h in Headers */,
|
||||||
FA0B7E441A95902C000E1D17 /* wrap_CircleShape.h in Headers */,
|
FA0B7E441A95902C000E1D17 /* wrap_CircleShape.h in Headers */,
|
||||||
FA0B7EB41A95902C000E1D17 /* System.h in Headers */,
|
FA0B7EB41A95902C000E1D17 /* System.h in Headers */,
|
||||||
@@ -4660,6 +4692,7 @@
|
|||||||
FADF540E1E3D7CDD00012CC0 /* wrap_Video.cpp in Sources */,
|
FADF540E1E3D7CDD00012CC0 /* wrap_Video.cpp in Sources */,
|
||||||
FA0B7D4C1A95902C000E1D17 /* Shader.cpp in Sources */,
|
FA0B7D4C1A95902C000E1D17 /* Shader.cpp in Sources */,
|
||||||
FA0B792A1A958E3B000E1D17 /* Matrix.cpp in Sources */,
|
FA0B792A1A958E3B000E1D17 /* Matrix.cpp in Sources */,
|
||||||
|
D9DAB92B2961F10000C64820 /* GenericShaper.cpp in Sources */,
|
||||||
FAF140981E20934C00F898D2 /* PpTokens.cpp in Sources */,
|
FAF140981E20934C00F898D2 /* PpTokens.cpp in Sources */,
|
||||||
FAF140AA1E20934C00F898D2 /* SymbolTable.cpp in Sources */,
|
FAF140AA1E20934C00F898D2 /* SymbolTable.cpp in Sources */,
|
||||||
FABDA9892552448300B5C523 /* b2_contact.cpp in Sources */,
|
FABDA9892552448300B5C523 /* b2_contact.cpp in Sources */,
|
||||||
@@ -4778,6 +4811,7 @@
|
|||||||
FA59A2D31C06481400328DBA /* ParticleSystem.cpp in Sources */,
|
FA59A2D31C06481400328DBA /* ParticleSystem.cpp in Sources */,
|
||||||
FA0B7E131A95902C000E1D17 /* GearJoint.cpp in Sources */,
|
FA0B7E131A95902C000E1D17 /* GearJoint.cpp in Sources */,
|
||||||
FABDA99B2552448300B5C523 /* b2_polygon_contact.cpp in Sources */,
|
FABDA99B2552448300B5C523 /* b2_polygon_contact.cpp in Sources */,
|
||||||
|
D9DAB9242961F0EE00C64820 /* HarfbuzzShaper.cpp in Sources */,
|
||||||
FA0B7DC21A95902C000E1D17 /* wrap_Joystick.cpp in Sources */,
|
FA0B7DC21A95902C000E1D17 /* wrap_Joystick.cpp in Sources */,
|
||||||
FA0B7CD41A95902C000E1D17 /* Source.cpp in Sources */,
|
FA0B7CD41A95902C000E1D17 /* Source.cpp in Sources */,
|
||||||
FAA3A9AF1B7D465A00CED060 /* android.cpp in Sources */,
|
FAA3A9AF1B7D465A00CED060 /* android.cpp in Sources */,
|
||||||
@@ -4926,6 +4960,7 @@
|
|||||||
FABDA9FE2552448300B5C523 /* b2_edge_shape.cpp in Sources */,
|
FABDA9FE2552448300B5C523 /* b2_edge_shape.cpp in Sources */,
|
||||||
FA0B7D7A1A95902C000E1D17 /* Quad.cpp in Sources */,
|
FA0B7D7A1A95902C000E1D17 /* Quad.cpp in Sources */,
|
||||||
FA620A3B1AA305F6005DB4C2 /* types.cpp in Sources */,
|
FA620A3B1AA305F6005DB4C2 /* types.cpp in Sources */,
|
||||||
|
D9DAB92E2961F10000C64820 /* TextShaper.cpp in Sources */,
|
||||||
FA0B7DD41A95902C000E1D17 /* BezierCurve.cpp in Sources */,
|
FA0B7DD41A95902C000E1D17 /* BezierCurve.cpp in Sources */,
|
||||||
FA0B7E7C1A95902C000E1D17 /* wrap_World.cpp in Sources */,
|
FA0B7E7C1A95902C000E1D17 /* wrap_World.cpp in Sources */,
|
||||||
FAF6C9F923C2DE2900D7B5BC /* doc.cpp in Sources */,
|
FAF6C9F923C2DE2900D7B5BC /* doc.cpp in Sources */,
|
||||||
@@ -5087,6 +5122,7 @@
|
|||||||
FA0B7EA31A95902C000E1D17 /* SoundData.cpp in Sources */,
|
FA0B7EA31A95902C000E1D17 /* SoundData.cpp in Sources */,
|
||||||
FA0B79291A958E3B000E1D17 /* Matrix.cpp in Sources */,
|
FA0B79291A958E3B000E1D17 /* Matrix.cpp in Sources */,
|
||||||
FA8951A21AA2EDF300EC385A /* wrap_Event.cpp in Sources */,
|
FA8951A21AA2EDF300EC385A /* wrap_Event.cpp in Sources */,
|
||||||
|
D9DAB92A2961F10000C64820 /* GenericShaper.cpp in Sources */,
|
||||||
FAF140691E20934C00F898D2 /* glslang_tab.cpp in Sources */,
|
FAF140691E20934C00F898D2 /* glslang_tab.cpp in Sources */,
|
||||||
FA0B7ABF1A958EA3000E1D17 /* host.c in Sources */,
|
FA0B7ABF1A958EA3000E1D17 /* host.c in Sources */,
|
||||||
FA0B7D4B1A95902C000E1D17 /* Shader.cpp in Sources */,
|
FA0B7D4B1A95902C000E1D17 /* Shader.cpp in Sources */,
|
||||||
@@ -5205,6 +5241,7 @@
|
|||||||
FAF140551E20934C00F898D2 /* Link.cpp in Sources */,
|
FAF140551E20934C00F898D2 /* Link.cpp in Sources */,
|
||||||
FABDA9792552448200B5C523 /* b2_joint.cpp in Sources */,
|
FABDA9792552448200B5C523 /* b2_joint.cpp in Sources */,
|
||||||
FAF140841E20934C00F898D2 /* ParseHelper.cpp in Sources */,
|
FAF140841E20934C00F898D2 /* ParseHelper.cpp in Sources */,
|
||||||
|
D9DAB9232961F0EE00C64820 /* HarfbuzzShaper.cpp in Sources */,
|
||||||
FA0B7D7F1A95902C000E1D17 /* Volatile.cpp in Sources */,
|
FA0B7D7F1A95902C000E1D17 /* Volatile.cpp in Sources */,
|
||||||
FA1BA0B11E16FD0800AA2803 /* Shader.cpp in Sources */,
|
FA1BA0B11E16FD0800AA2803 /* Shader.cpp in Sources */,
|
||||||
FABDA99A2552448300B5C523 /* b2_polygon_contact.cpp in Sources */,
|
FABDA99A2552448300B5C523 /* b2_polygon_contact.cpp in Sources */,
|
||||||
@@ -5353,6 +5390,7 @@
|
|||||||
FA0B7E881A95902C000E1D17 /* Decoder.cpp in Sources */,
|
FA0B7E881A95902C000E1D17 /* Decoder.cpp in Sources */,
|
||||||
FA0B7E3C1A95902C000E1D17 /* wrap_Body.cpp in Sources */,
|
FA0B7E3C1A95902C000E1D17 /* wrap_Body.cpp in Sources */,
|
||||||
FA0B7D791A95902C000E1D17 /* Quad.cpp in Sources */,
|
FA0B7D791A95902C000E1D17 /* Quad.cpp in Sources */,
|
||||||
|
D9DAB92D2961F10000C64820 /* TextShaper.cpp in Sources */,
|
||||||
FABDA9FD2552448300B5C523 /* b2_edge_shape.cpp in Sources */,
|
FABDA9FD2552448300B5C523 /* b2_edge_shape.cpp in Sources */,
|
||||||
FAC756F51E4F99B400B91289 /* Effect.cpp in Sources */,
|
FAC756F51E4F99B400B91289 /* Effect.cpp in Sources */,
|
||||||
FA620A3A1AA305F6005DB4C2 /* types.cpp in Sources */,
|
FA620A3A1AA305F6005DB4C2 /* types.cpp in Sources */,
|
||||||
@@ -5716,6 +5754,7 @@
|
|||||||
"$(PROJECT_DIR)/macosx/Frameworks/freetype.framework/Headers",
|
"$(PROJECT_DIR)/macosx/Frameworks/freetype.framework/Headers",
|
||||||
"$(PROJECT_DIR)/macosx/Frameworks/Lua.framework/Headers",
|
"$(PROJECT_DIR)/macosx/Frameworks/Lua.framework/Headers",
|
||||||
"$(PROJECT_DIR)/macosx/Frameworks/SDL2.framework/Headers",
|
"$(PROJECT_DIR)/macosx/Frameworks/SDL2.framework/Headers",
|
||||||
|
"$(PROJECT_DIR)/macosx/Frameworks/harfbuzz.framework/Headers",
|
||||||
);
|
);
|
||||||
INFOPLIST_FILE = "macosx/liblove-macosx.plist";
|
INFOPLIST_FILE = "macosx/liblove-macosx.plist";
|
||||||
LD_DYLIB_INSTALL_NAME = "@rpath/$(EXECUTABLE_PATH)";
|
LD_DYLIB_INSTALL_NAME = "@rpath/$(EXECUTABLE_PATH)";
|
||||||
@@ -5751,6 +5790,7 @@
|
|||||||
"$(PROJECT_DIR)/macosx/Frameworks/freetype.framework/Headers",
|
"$(PROJECT_DIR)/macosx/Frameworks/freetype.framework/Headers",
|
||||||
"$(PROJECT_DIR)/macosx/Frameworks/Lua.framework/Headers",
|
"$(PROJECT_DIR)/macosx/Frameworks/Lua.framework/Headers",
|
||||||
"$(PROJECT_DIR)/macosx/Frameworks/SDL2.framework/Headers",
|
"$(PROJECT_DIR)/macosx/Frameworks/SDL2.framework/Headers",
|
||||||
|
"$(PROJECT_DIR)/macosx/Frameworks/harfbuzz.framework/Headers",
|
||||||
);
|
);
|
||||||
INFOPLIST_FILE = "macosx/liblove-macosx.plist";
|
INFOPLIST_FILE = "macosx/liblove-macosx.plist";
|
||||||
LD_DYLIB_INSTALL_NAME = "@rpath/$(EXECUTABLE_PATH)";
|
LD_DYLIB_INSTALL_NAME = "@rpath/$(EXECUTABLE_PATH)";
|
||||||
@@ -5787,6 +5827,7 @@
|
|||||||
"$(PROJECT_DIR)/macosx/Frameworks/freetype.framework/Headers",
|
"$(PROJECT_DIR)/macosx/Frameworks/freetype.framework/Headers",
|
||||||
"$(PROJECT_DIR)/macosx/Frameworks/Lua.framework/Headers",
|
"$(PROJECT_DIR)/macosx/Frameworks/Lua.framework/Headers",
|
||||||
"$(PROJECT_DIR)/macosx/Frameworks/SDL2.framework/Headers",
|
"$(PROJECT_DIR)/macosx/Frameworks/SDL2.framework/Headers",
|
||||||
|
"$(PROJECT_DIR)/macosx/Frameworks/harfbuzz.framework/Headers",
|
||||||
);
|
);
|
||||||
INFOPLIST_FILE = "macosx/liblove-macosx.plist";
|
INFOPLIST_FILE = "macosx/liblove-macosx.plist";
|
||||||
LD_DYLIB_INSTALL_NAME = "@rpath/$(EXECUTABLE_PATH)";
|
LD_DYLIB_INSTALL_NAME = "@rpath/$(EXECUTABLE_PATH)";
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
A93E6EED10420BA8007D418B /* love.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A93E6A3410420AC0007D418B /* love.cpp */; };
|
A93E6EED10420BA8007D418B /* love.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A93E6A3410420AC0007D418B /* love.cpp */; };
|
||||||
A9F169AD109E825000FC83D1 /* libmodplug.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = A9F16926109E7BAD00FC83D1 /* libmodplug.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
A9F169AD109E825000FC83D1 /* libmodplug.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = A9F16926109E7BAD00FC83D1 /* libmodplug.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||||
CE73F8001EEB64150052DAB3 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE73F7FF1EEB64150052DAB3 /* AVFoundation.framework */; };
|
CE73F8001EEB64150052DAB3 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE73F7FF1EEB64150052DAB3 /* AVFoundation.framework */; };
|
||||||
|
D9DAB9372963CF6900C64820 /* harfbuzz.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = D9DAB9352963CF5F00C64820 /* harfbuzz.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||||
FA0797991BF480A200034B7C /* GameController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA0797981BF480A200034B7C /* GameController.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
|
FA0797991BF480A200034B7C /* GameController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA0797981BF480A200034B7C /* GameController.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
|
||||||
FA08F69616C766E000F007B5 /* love.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA08F69116C765A200F007B5 /* love.framework */; };
|
FA08F69616C766E000F007B5 /* love.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA08F69116C765A200F007B5 /* love.framework */; };
|
||||||
FA08F69716C766E700F007B5 /* love.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = FA08F69116C765A200F007B5 /* love.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
FA08F69716C766E700F007B5 /* love.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = FA08F69116C765A200F007B5 /* love.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||||
@@ -87,6 +88,7 @@
|
|||||||
FAD4B1731C1F50A3004CF150 /* theora.framework in Copy Frameworks */,
|
FAD4B1731C1F50A3004CF150 /* theora.framework in Copy Frameworks */,
|
||||||
FAAFF04716CB120000CCDE45 /* OpenAL-Soft.framework in Copy Frameworks */,
|
FAAFF04716CB120000CCDE45 /* OpenAL-Soft.framework in Copy Frameworks */,
|
||||||
FAD43ED01FF3136500831BB8 /* freetype.framework in Copy Frameworks */,
|
FAD43ED01FF3136500831BB8 /* freetype.framework in Copy Frameworks */,
|
||||||
|
D9DAB9372963CF6900C64820 /* harfbuzz.framework in Copy Frameworks */,
|
||||||
A9F169AD109E825000FC83D1 /* libmodplug.framework in Copy Frameworks */,
|
A9F169AD109E825000FC83D1 /* libmodplug.framework in Copy Frameworks */,
|
||||||
A9255F58104324E100BA1496 /* ogg.framework in Copy Frameworks */,
|
A9255F58104324E100BA1496 /* ogg.framework in Copy Frameworks */,
|
||||||
A9255E031043195A00BA1496 /* vorbis.framework in Copy Frameworks */,
|
A9255E031043195A00BA1496 /* vorbis.framework in Copy Frameworks */,
|
||||||
@@ -108,6 +110,7 @@
|
|||||||
A97E3842132A9EDE00198A2F /* love-macosx.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "love-macosx.plist"; path = "macosx/love-macosx.plist"; sourceTree = "<group>"; };
|
A97E3842132A9EDE00198A2F /* love-macosx.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "love-macosx.plist"; path = "macosx/love-macosx.plist"; sourceTree = "<group>"; };
|
||||||
A9F16926109E7BAD00FC83D1 /* libmodplug.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = libmodplug.framework; path = macosx/Frameworks/libmodplug.framework; sourceTree = "<group>"; };
|
A9F16926109E7BAD00FC83D1 /* libmodplug.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = libmodplug.framework; path = macosx/Frameworks/libmodplug.framework; sourceTree = "<group>"; };
|
||||||
CE73F7FF1EEB64150052DAB3 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/System/Library/Frameworks/AVFoundation.framework; sourceTree = DEVELOPER_DIR; };
|
CE73F7FF1EEB64150052DAB3 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/System/Library/Frameworks/AVFoundation.framework; sourceTree = DEVELOPER_DIR; };
|
||||||
|
D9DAB9352963CF5F00C64820 /* harfbuzz.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = harfbuzz.framework; path = macosx/Frameworks/harfbuzz.framework; sourceTree = "<group>"; };
|
||||||
FA0797981BF480A200034B7C /* GameController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameController.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.1.sdk/System/Library/Frameworks/GameController.framework; sourceTree = DEVELOPER_DIR; };
|
FA0797981BF480A200034B7C /* GameController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameController.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.1.sdk/System/Library/Frameworks/GameController.framework; sourceTree = DEVELOPER_DIR; };
|
||||||
FA08F69116C765A200F007B5 /* love.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = love.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
FA08F69116C765A200F007B5 /* love.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = love.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
FA0B7F061A95AAF3000E1D17 /* love.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = love.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
FA0B7F061A95AAF3000E1D17 /* love.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = love.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
@@ -233,6 +236,7 @@
|
|||||||
children = (
|
children = (
|
||||||
1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */,
|
1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */,
|
||||||
FAD43ECF1FF3133700831BB8 /* freetype.framework */,
|
FAD43ECF1FF3133700831BB8 /* freetype.framework */,
|
||||||
|
D9DAB9352963CF5F00C64820 /* harfbuzz.framework */,
|
||||||
A9F16926109E7BAD00FC83D1 /* libmodplug.framework */,
|
A9F16926109E7BAD00FC83D1 /* libmodplug.framework */,
|
||||||
FA08F69116C765A200F007B5 /* love.framework */,
|
FA08F69116C765A200F007B5 /* love.framework */,
|
||||||
A93E6E5310420B57007D418B /* Lua.framework */,
|
A93E6E5310420B57007D418B /* Lua.framework */,
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
// LOVE
|
// LOVE
|
||||||
#include "BMFontRasterizer.h"
|
#include "BMFontRasterizer.h"
|
||||||
|
#include "GenericShaper.h"
|
||||||
#include "filesystem/Filesystem.h"
|
#include "filesystem/Filesystem.h"
|
||||||
#include "image/Image.h"
|
#include "image/Image.h"
|
||||||
|
|
||||||
@@ -164,6 +165,14 @@ BMFontRasterizer::~BMFontRasterizer()
|
|||||||
|
|
||||||
void BMFontRasterizer::parseConfig(const std::string &configtext)
|
void BMFontRasterizer::parseConfig(const std::string &configtext)
|
||||||
{
|
{
|
||||||
|
{
|
||||||
|
BMFontCharacter nullchar = {};
|
||||||
|
nullchar.page = -1;
|
||||||
|
nullchar.glyph = 0;
|
||||||
|
characters.push_back(nullchar);
|
||||||
|
characterIndices[0] = (int)characters.size() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
std::stringstream ss(configtext);
|
std::stringstream ss(configtext);
|
||||||
std::string line;
|
std::string line;
|
||||||
|
|
||||||
@@ -237,7 +246,10 @@ void BMFontRasterizer::parseConfig(const std::string &configtext)
|
|||||||
c.metrics.bearingY = -cline.getAttributeInt("yoffset");
|
c.metrics.bearingY = -cline.getAttributeInt("yoffset");
|
||||||
c.metrics.advance = cline.getAttributeInt("xadvance");
|
c.metrics.advance = cline.getAttributeInt("xadvance");
|
||||||
|
|
||||||
characters[id] = c;
|
c.glyph = id;
|
||||||
|
|
||||||
|
characters.push_back(c);
|
||||||
|
characterIndices[id] = (int) characters.size() - 1;
|
||||||
}
|
}
|
||||||
else if (tag == "kerning")
|
else if (tag == "kerning")
|
||||||
{
|
{
|
||||||
@@ -257,13 +269,15 @@ void BMFontRasterizer::parseConfig(const std::string &configtext)
|
|||||||
bool guessheight = lineHeight == 0;
|
bool guessheight = lineHeight == 0;
|
||||||
|
|
||||||
// Verify the glyph character attributes.
|
// Verify the glyph character attributes.
|
||||||
for (const auto &cpair : characters)
|
for (const auto &c : characters)
|
||||||
{
|
{
|
||||||
const BMFontCharacter &c = cpair.second;
|
if (c.glyph == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
int width = c.metrics.width;
|
int width = c.metrics.width;
|
||||||
int height = c.metrics.height;
|
int height = c.metrics.height;
|
||||||
|
|
||||||
if (!unicode && cpair.first > 127)
|
if (!unicode && c.glyph > 127)
|
||||||
throw love::Exception("Invalid BMFont character id (only unicode and ASCII are supported)");
|
throw love::Exception("Invalid BMFont character id (only unicode and ASCII are supported)");
|
||||||
|
|
||||||
if (c.page < 0 || images[c.page].get() == nullptr)
|
if (c.page < 0 || images[c.page].get() == nullptr)
|
||||||
@@ -272,13 +286,13 @@ void BMFontRasterizer::parseConfig(const std::string &configtext)
|
|||||||
const image::ImageData *id = images[c.page].get();
|
const image::ImageData *id = images[c.page].get();
|
||||||
|
|
||||||
if (!id->inside(c.x, c.y))
|
if (!id->inside(c.x, c.y))
|
||||||
throw love::Exception("Invalid coordinates for BMFont character %u.", cpair.first);
|
throw love::Exception("Invalid coordinates for BMFont character %u.", c.glyph);
|
||||||
|
|
||||||
if (width > 0 && !id->inside(c.x + width - 1, c.y))
|
if (width > 0 && !id->inside(c.x + width - 1, c.y))
|
||||||
throw love::Exception("Invalid width %d for BMFont character %u.", width, cpair.first);
|
throw love::Exception("Invalid width %d for BMFont character %u.", width, c.glyph);
|
||||||
|
|
||||||
if (height > 0 && !id->inside(c.x, c.y + height - 1))
|
if (height > 0 && !id->inside(c.x, c.y + height - 1))
|
||||||
throw love::Exception("Invalid height %d for BMFont character %u.", height, cpair.first);
|
throw love::Exception("Invalid height %d for BMFont character %u.", height, c.glyph);
|
||||||
|
|
||||||
if (guessheight)
|
if (guessheight)
|
||||||
lineHeight = std::max(lineHeight, c.metrics.height);
|
lineHeight = std::max(lineHeight, c.metrics.height);
|
||||||
@@ -292,22 +306,37 @@ int BMFontRasterizer::getLineHeight() const
|
|||||||
return lineHeight;
|
return lineHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
GlyphData *BMFontRasterizer::getGlyphData(uint32 glyph) const
|
int BMFontRasterizer::getGlyphSpacing(uint32 glyph) const
|
||||||
{
|
{
|
||||||
auto it = characters.find(glyph);
|
auto it = characterIndices.find(glyph);
|
||||||
|
if (it == characterIndices.end())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return characters[it->second].metrics.advance;
|
||||||
|
}
|
||||||
|
|
||||||
|
int BMFontRasterizer::getGlyphIndex(uint32 glyph) const
|
||||||
|
{
|
||||||
|
auto it = characterIndices.find(glyph);
|
||||||
|
if (it == characterIndices.end())
|
||||||
|
return 0;
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
GlyphData *BMFontRasterizer::getGlyphDataForIndex(int index) const
|
||||||
|
{
|
||||||
// Return an empty GlyphData if we don't have the glyph character.
|
// Return an empty GlyphData if we don't have the glyph character.
|
||||||
if (it == characters.end())
|
if (index < 0 || index >= (int) characters.size())
|
||||||
return new GlyphData(glyph, GlyphMetrics(), PIXELFORMAT_RGBA8_UNORM);
|
return new GlyphData(0, GlyphMetrics(), PIXELFORMAT_RGBA8_UNORM);
|
||||||
|
|
||||||
const BMFontCharacter &c = it->second;
|
const BMFontCharacter& c = characters[index];
|
||||||
const auto &imagepair = images.find(c.page);
|
const auto &imagepair = images.find(c.page);
|
||||||
|
|
||||||
if (imagepair == images.end())
|
if (imagepair == images.end())
|
||||||
return new GlyphData(glyph, GlyphMetrics(), PIXELFORMAT_RGBA8_UNORM);
|
return new GlyphData(c.glyph, GlyphMetrics(), PIXELFORMAT_RGBA8_UNORM);
|
||||||
|
|
||||||
image::ImageData *imagedata = imagepair->second.get();
|
image::ImageData *imagedata = imagepair->second.get();
|
||||||
GlyphData *g = new GlyphData(glyph, c.metrics, PIXELFORMAT_RGBA8_UNORM);
|
GlyphData *g = new GlyphData(c.glyph, c.metrics, PIXELFORMAT_RGBA8_UNORM);
|
||||||
|
|
||||||
size_t pixelsize = imagedata->getPixelSize();
|
size_t pixelsize = imagedata->getPixelSize();
|
||||||
|
|
||||||
@@ -333,7 +362,7 @@ int BMFontRasterizer::getGlyphCount() const
|
|||||||
|
|
||||||
bool BMFontRasterizer::hasGlyph(uint32 glyph) const
|
bool BMFontRasterizer::hasGlyph(uint32 glyph) const
|
||||||
{
|
{
|
||||||
return characters.find(glyph) != characters.end();
|
return characterIndices.find(glyph) != characterIndices.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
float BMFontRasterizer::getKerning(uint32 leftglyph, uint32 rightglyph) const
|
float BMFontRasterizer::getKerning(uint32 leftglyph, uint32 rightglyph) const
|
||||||
@@ -352,6 +381,11 @@ Rasterizer::DataType BMFontRasterizer::getDataType() const
|
|||||||
return DATA_IMAGE;
|
return DATA_IMAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextShaper *BMFontRasterizer::newTextShaper()
|
||||||
|
{
|
||||||
|
return new GenericShaper(this);
|
||||||
|
}
|
||||||
|
|
||||||
bool BMFontRasterizer::accepts(love::filesystem::FileData *fontdef)
|
bool BMFontRasterizer::accepts(love::filesystem::FileData *fontdef)
|
||||||
{
|
{
|
||||||
const char *data = (const char *) fontdef->getData();
|
const char *data = (const char *) fontdef->getData();
|
||||||
|
|||||||
@@ -47,11 +47,14 @@ public:
|
|||||||
|
|
||||||
// Implements Rasterizer.
|
// Implements Rasterizer.
|
||||||
int getLineHeight() const override;
|
int getLineHeight() const override;
|
||||||
GlyphData *getGlyphData(uint32 glyph) const override;
|
int getGlyphSpacing(uint32 glyph) const override;
|
||||||
|
int getGlyphIndex(uint32 glyph) const override;
|
||||||
|
GlyphData *getGlyphDataForIndex(int index) const override;
|
||||||
int getGlyphCount() const override;
|
int getGlyphCount() const override;
|
||||||
bool hasGlyph(uint32 glyph) const override;
|
bool hasGlyph(uint32 glyph) const override;
|
||||||
float getKerning(uint32 leftglyph, uint32 rightglyph) const override;
|
float getKerning(uint32 leftglyph, uint32 rightglyph) const override;
|
||||||
DataType getDataType() const override;
|
DataType getDataType() const override;
|
||||||
|
TextShaper *newTextShaper() override;
|
||||||
|
|
||||||
static bool accepts(love::filesystem::FileData *fontdef);
|
static bool accepts(love::filesystem::FileData *fontdef);
|
||||||
|
|
||||||
@@ -63,6 +66,7 @@ private:
|
|||||||
int y;
|
int y;
|
||||||
int page;
|
int page;
|
||||||
GlyphMetrics metrics;
|
GlyphMetrics metrics;
|
||||||
|
uint32 glyph;
|
||||||
};
|
};
|
||||||
|
|
||||||
void parseConfig(const std::string &config);
|
void parseConfig(const std::string &config);
|
||||||
@@ -72,8 +76,10 @@ private:
|
|||||||
// Image pages, indexed by their page id.
|
// Image pages, indexed by their page id.
|
||||||
std::unordered_map<int, StrongRef<image::ImageData>> images;
|
std::unordered_map<int, StrongRef<image::ImageData>> images;
|
||||||
|
|
||||||
// Glyph characters, indexed by their glyph id.
|
std::vector<BMFontCharacter> characters;
|
||||||
std::unordered_map<uint32, BMFontCharacter> characters;
|
|
||||||
|
// Glyph character indices, indexed by their glyph id.
|
||||||
|
std::unordered_map<uint32, int> characterIndices;
|
||||||
|
|
||||||
// Kerning information, indexed by two (packed) characters.
|
// Kerning information, indexed by two (packed) characters.
|
||||||
std::unordered_map<uint64, int> kerning;
|
std::unordered_map<uint64, int> kerning;
|
||||||
|
|||||||
@@ -0,0 +1,203 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2006-2023 LOVE Development Team
|
||||||
|
*
|
||||||
|
* This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty. In no event will the authors be held liable for any damages
|
||||||
|
* arising from the use of this software.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it
|
||||||
|
* freely, subject to the following restrictions:
|
||||||
|
*
|
||||||
|
* 1. The origin of this software must not be misrepresented; you must not
|
||||||
|
* claim that you wrote the original software. If you use this software
|
||||||
|
* in a product, an acknowledgment in the product documentation would be
|
||||||
|
* appreciated but is not required.
|
||||||
|
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
* misrepresented as being the original software.
|
||||||
|
* 3. This notice may not be removed or altered from any source distribution.
|
||||||
|
**/
|
||||||
|
|
||||||
|
// LOVE
|
||||||
|
#include "GenericShaper.h"
|
||||||
|
#include "Rasterizer.h"
|
||||||
|
#include "common/Optional.h"
|
||||||
|
|
||||||
|
namespace love
|
||||||
|
{
|
||||||
|
namespace font
|
||||||
|
{
|
||||||
|
|
||||||
|
GenericShaper::GenericShaper(Rasterizer *rasterizer)
|
||||||
|
: TextShaper(rasterizer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
GenericShaper::~GenericShaper()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void GenericShaper::computeGlyphPositions(const ColoredCodepoints &codepoints, Range range, Vector2 offset, float extraspacing, std::vector<GlyphPosition> *positions, std::vector<IndexedColor> *colors, TextInfo *info)
|
||||||
|
{
|
||||||
|
if (!range.isValid())
|
||||||
|
range = Range(0, codepoints.cps.size());
|
||||||
|
|
||||||
|
if (rasterizers[0]->getDataType() == Rasterizer::DATA_TRUETYPE)
|
||||||
|
offset.y += getBaseline();
|
||||||
|
|
||||||
|
// Spacing counter and newline handling.
|
||||||
|
Vector2 curpos = offset;
|
||||||
|
|
||||||
|
int maxwidth = 0;
|
||||||
|
uint32 prevglyph = 0;
|
||||||
|
|
||||||
|
if (positions)
|
||||||
|
positions->reserve(range.getSize());
|
||||||
|
|
||||||
|
int colorindex = 0;
|
||||||
|
int ncolors = (int) codepoints.colors.size();
|
||||||
|
Optional<Colorf> colorToAdd;
|
||||||
|
|
||||||
|
// Make sure the right color is applied to the start of the glyph list,
|
||||||
|
// when the start isn't 0.
|
||||||
|
if (colors && range.getOffset() > 0 && !codepoints.colors.empty())
|
||||||
|
{
|
||||||
|
for (; colorindex < ncolors; colorindex++)
|
||||||
|
{
|
||||||
|
if (codepoints.colors[colorindex].index >= (int) range.getOffset())
|
||||||
|
break;
|
||||||
|
colorToAdd.set(codepoints.colors[colorindex].color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = (int) range.getMin(); i <= (int) range.getMax(); i++)
|
||||||
|
{
|
||||||
|
uint32 g = codepoints.cps[i];
|
||||||
|
|
||||||
|
// Do this before anything else so we don't miss colors corresponding
|
||||||
|
// to newlines. The actual add to the list happens after newline
|
||||||
|
// handling, to make sure the resulting index is valid in the positions
|
||||||
|
// array.
|
||||||
|
if (colors && colorindex < ncolors && codepoints.colors[colorindex].index == i)
|
||||||
|
{
|
||||||
|
colorToAdd.set(codepoints.colors[colorindex].color);
|
||||||
|
colorindex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g == '\n')
|
||||||
|
{
|
||||||
|
if (curpos.x > maxwidth)
|
||||||
|
maxwidth = (int)curpos.x;
|
||||||
|
|
||||||
|
// Wrap newline, but do not output a position for it.
|
||||||
|
curpos.y += floorf(getHeight() * getLineHeight() + 0.5f);
|
||||||
|
curpos.x = offset.x;
|
||||||
|
prevglyph = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore carriage returns
|
||||||
|
if (g == '\r')
|
||||||
|
{
|
||||||
|
prevglyph = g;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (colorToAdd.hasValue && colors && positions)
|
||||||
|
{
|
||||||
|
IndexedColor c = {colorToAdd.value, (int) positions->size()};
|
||||||
|
colors->push_back(c);
|
||||||
|
colorToAdd.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add kerning to the current horizontal offset.
|
||||||
|
curpos.x += getKerning(prevglyph, g);
|
||||||
|
|
||||||
|
GlyphIndex glyphindex;
|
||||||
|
int advance = getGlyphAdvance(g, &glyphindex);
|
||||||
|
|
||||||
|
if (positions)
|
||||||
|
positions->push_back({ Vector2(curpos.x, curpos.y), glyphindex });
|
||||||
|
|
||||||
|
// Advance the x position for the next glyph.
|
||||||
|
curpos.x += advance;
|
||||||
|
|
||||||
|
// Account for extra spacing given to space characters.
|
||||||
|
if (g == ' ' && extraspacing != 0.0f)
|
||||||
|
curpos.x = floorf(curpos.x + extraspacing);
|
||||||
|
|
||||||
|
prevglyph = g;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (curpos.x > maxwidth)
|
||||||
|
maxwidth = (int)curpos.x;
|
||||||
|
|
||||||
|
if (info != nullptr)
|
||||||
|
{
|
||||||
|
info->width = maxwidth - offset.x;
|
||||||
|
info->height = curpos.y - offset.y;
|
||||||
|
if (curpos.x > offset.x)
|
||||||
|
info->height += floorf(getHeight() * getLineHeight() + 0.5f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int GenericShaper::computeWordWrapIndex(const ColoredCodepoints &codepoints, Range range, float wraplimit, float *width)
|
||||||
|
{
|
||||||
|
if (!range.isValid())
|
||||||
|
range = Range(0, codepoints.cps.size());
|
||||||
|
|
||||||
|
uint32 prevglyph = 0;
|
||||||
|
|
||||||
|
float w = 0.0f;
|
||||||
|
float outwidth = 0.0f;
|
||||||
|
float widthbeforelastspace = 0.0f;
|
||||||
|
int wrapindex = -1;
|
||||||
|
int lastspaceindex = -1;
|
||||||
|
|
||||||
|
for (int i = (int)range.getMin(); i <= (int)range.getMax(); i++)
|
||||||
|
{
|
||||||
|
uint32 g = codepoints.cps[i];
|
||||||
|
|
||||||
|
if (g == '\r')
|
||||||
|
{
|
||||||
|
prevglyph = g;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
float newwidth = w + getKerning(prevglyph, g) + getGlyphAdvance(g);
|
||||||
|
|
||||||
|
// Only wrap when there's a non-space character.
|
||||||
|
if (newwidth > wraplimit && !isWhitespace(g))
|
||||||
|
{
|
||||||
|
// Rewind to the last seen space when wrapping.
|
||||||
|
if (lastspaceindex != -1)
|
||||||
|
{
|
||||||
|
wrapindex = lastspaceindex;
|
||||||
|
outwidth = widthbeforelastspace;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't count trailing spaces in the output width.
|
||||||
|
if (isWhitespace(g))
|
||||||
|
{
|
||||||
|
lastspaceindex = i;
|
||||||
|
if (!isWhitespace(prevglyph))
|
||||||
|
widthbeforelastspace = w;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
outwidth = newwidth;
|
||||||
|
|
||||||
|
w = newwidth;
|
||||||
|
prevglyph = g;
|
||||||
|
wrapindex = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (width)
|
||||||
|
*width = outwidth;
|
||||||
|
|
||||||
|
return wrapindex;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // font
|
||||||
|
} // love
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2006-2023 LOVE Development Team
|
||||||
|
*
|
||||||
|
* This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty. In no event will the authors be held liable for any damages
|
||||||
|
* arising from the use of this software.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it
|
||||||
|
* freely, subject to the following restrictions:
|
||||||
|
*
|
||||||
|
* 1. The origin of this software must not be misrepresented; you must not
|
||||||
|
* claim that you wrote the original software. If you use this software
|
||||||
|
* in a product, an acknowledgment in the product documentation would be
|
||||||
|
* appreciated but is not required.
|
||||||
|
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
* misrepresented as being the original software.
|
||||||
|
* 3. This notice may not be removed or altered from any source distribution.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// LOVE
|
||||||
|
#include "TextShaper.h"
|
||||||
|
|
||||||
|
namespace love
|
||||||
|
{
|
||||||
|
namespace font
|
||||||
|
{
|
||||||
|
|
||||||
|
class GenericShaper : public love::font::TextShaper
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
GenericShaper(Rasterizer *rasterizer);
|
||||||
|
virtual ~GenericShaper();
|
||||||
|
|
||||||
|
void computeGlyphPositions(const ColoredCodepoints &codepoints, Range range, Vector2 offset, float extraspacing, std::vector<GlyphPosition> *positions, std::vector<IndexedColor> *colors, TextInfo *info) override;
|
||||||
|
int computeWordWrapIndex(const ColoredCodepoints &codepoints, Range range, float wraplimit, float *width) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
}; // GenericShaper
|
||||||
|
|
||||||
|
} // font
|
||||||
|
} // love
|
||||||
@@ -24,10 +24,6 @@
|
|||||||
// UTF-8
|
// UTF-8
|
||||||
#include "libraries/utf8/utf8.h"
|
#include "libraries/utf8/utf8.h"
|
||||||
|
|
||||||
// stdlib
|
|
||||||
#include <iostream>
|
|
||||||
#include <cstddef>
|
|
||||||
|
|
||||||
namespace love
|
namespace love
|
||||||
{
|
{
|
||||||
namespace font
|
namespace font
|
||||||
|
|||||||
@@ -20,8 +20,9 @@
|
|||||||
|
|
||||||
// LOVE
|
// LOVE
|
||||||
#include "ImageRasterizer.h"
|
#include "ImageRasterizer.h"
|
||||||
|
#include "GenericShaper.h"
|
||||||
#include "common/Exception.h"
|
#include "common/Exception.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
namespace love
|
namespace love
|
||||||
@@ -31,10 +32,9 @@ namespace font
|
|||||||
|
|
||||||
static_assert(sizeof(Color32) == 4, "sizeof(Color32) must equal 4 bytes!");
|
static_assert(sizeof(Color32) == 4, "sizeof(Color32) must equal 4 bytes!");
|
||||||
|
|
||||||
ImageRasterizer::ImageRasterizer(love::image::ImageData *data, uint32 *glyphs, int numglyphs, int extraspacing, float dpiscale)
|
ImageRasterizer::ImageRasterizer(love::image::ImageData *data, const uint32 *glyphs, int numglyphs, int extraspacing, float dpiscale)
|
||||||
: imageData(data)
|
: imageData(data)
|
||||||
, glyphs(glyphs)
|
, numglyphs(numglyphs + 1) // Always have a null glyph at the start of the array.
|
||||||
, numglyphs(numglyphs)
|
|
||||||
, extraSpacing(extraspacing)
|
, extraSpacing(extraspacing)
|
||||||
{
|
{
|
||||||
this->dpiScale = dpiscale;
|
this->dpiScale = dpiscale;
|
||||||
@@ -42,7 +42,7 @@ ImageRasterizer::ImageRasterizer(love::image::ImageData *data, uint32 *glyphs, i
|
|||||||
if (data->getFormat() != PIXELFORMAT_RGBA8_UNORM)
|
if (data->getFormat() != PIXELFORMAT_RGBA8_UNORM)
|
||||||
throw love::Exception("Only 32-bit RGBA images are supported in Image Fonts!");
|
throw love::Exception("Only 32-bit RGBA images are supported in Image Fonts!");
|
||||||
|
|
||||||
load();
|
load(glyphs, numglyphs);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageRasterizer::~ImageRasterizer()
|
ImageRasterizer::~ImageRasterizer()
|
||||||
@@ -54,16 +54,33 @@ int ImageRasterizer::getLineHeight() const
|
|||||||
return getHeight();
|
return getHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
GlyphData *ImageRasterizer::getGlyphData(uint32 glyph) const
|
int ImageRasterizer::getGlyphSpacing(uint32 glyph) const
|
||||||
|
{
|
||||||
|
auto it = glyphIndices.find(glyph);
|
||||||
|
if (it == glyphIndices.end())
|
||||||
|
return 0;
|
||||||
|
return imageGlyphs[it->second].width + extraSpacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ImageRasterizer::getGlyphIndex(uint32 glyph) const
|
||||||
|
{
|
||||||
|
auto it = glyphIndices.find(glyph);
|
||||||
|
if (it == glyphIndices.end())
|
||||||
|
return 0;
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
GlyphData *ImageRasterizer::getGlyphDataForIndex(int index) const
|
||||||
{
|
{
|
||||||
GlyphMetrics gm = {};
|
GlyphMetrics gm = {};
|
||||||
|
uint32 glyph = 0;
|
||||||
|
|
||||||
// Set relevant glyph metrics if the glyph is in this ImageFont
|
// Set relevant glyph metrics if the glyph is in this ImageFont
|
||||||
std::map<uint32, ImageGlyphData>::const_iterator it = imageGlyphs.find(glyph);
|
if (index >= 0 && index < (int) imageGlyphs.size())
|
||||||
if (it != imageGlyphs.end())
|
|
||||||
{
|
{
|
||||||
gm.width = it->second.width;
|
gm.width = imageGlyphs[index].width;
|
||||||
gm.advance = it->second.width + extraSpacing;
|
gm.advance = imageGlyphs[index].width + extraSpacing;
|
||||||
|
glyph = imageGlyphs[index].glyph;
|
||||||
}
|
}
|
||||||
|
|
||||||
gm.height = metrics.height;
|
gm.height = metrics.height;
|
||||||
@@ -82,7 +99,7 @@ GlyphData *ImageRasterizer::getGlyphData(uint32 glyph) const
|
|||||||
// copy glyph pixels from imagedata to glyphdata
|
// copy glyph pixels from imagedata to glyphdata
|
||||||
for (int i = 0; i < g->getWidth() * g->getHeight(); i++)
|
for (int i = 0; i < g->getWidth() * g->getHeight(); i++)
|
||||||
{
|
{
|
||||||
Color32 p = imagepixels[it->second.x + (i % gm.width) + (imageData->getWidth() * (i / gm.width))];
|
Color32 p = imagepixels[imageGlyphs[index].x + (i % gm.width) + (imageData->getWidth() * (i / gm.width))];
|
||||||
|
|
||||||
// Use transparency instead of the spacer color
|
// Use transparency instead of the spacer color
|
||||||
if (p == spacer)
|
if (p == spacer)
|
||||||
@@ -94,7 +111,7 @@ GlyphData *ImageRasterizer::getGlyphData(uint32 glyph) const
|
|||||||
return g;
|
return g;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageRasterizer::load()
|
void ImageRasterizer::load(const uint32 *glyphs, int glyphcount)
|
||||||
{
|
{
|
||||||
auto pixels = (const Color32 *) imageData->getData();
|
auto pixels = (const Color32 *) imageData->getData();
|
||||||
|
|
||||||
@@ -113,7 +130,16 @@ void ImageRasterizer::load()
|
|||||||
int start = 0;
|
int start = 0;
|
||||||
int end = 0;
|
int end = 0;
|
||||||
|
|
||||||
for (int i = 0; i < numglyphs; ++i)
|
{
|
||||||
|
ImageGlyphData nullglyph;
|
||||||
|
nullglyph.x = 0;
|
||||||
|
nullglyph.width = 0;
|
||||||
|
nullglyph.glyph = 0;
|
||||||
|
imageGlyphs.push_back(nullglyph);
|
||||||
|
glyphIndices[0] = (int) imageGlyphs.size() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < glyphcount; ++i)
|
||||||
{
|
{
|
||||||
start = end;
|
start = end;
|
||||||
|
|
||||||
@@ -133,8 +159,10 @@ void ImageRasterizer::load()
|
|||||||
ImageGlyphData imageGlyph;
|
ImageGlyphData imageGlyph;
|
||||||
imageGlyph.x = start;
|
imageGlyph.x = start;
|
||||||
imageGlyph.width = end - start;
|
imageGlyph.width = end - start;
|
||||||
|
imageGlyph.glyph = glyphs[i];
|
||||||
|
|
||||||
imageGlyphs[glyphs[i]] = imageGlyph;
|
imageGlyphs.push_back(imageGlyph);
|
||||||
|
glyphIndices[glyphs[i]] = (int) imageGlyphs.size() - 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,7 +173,7 @@ int ImageRasterizer::getGlyphCount() const
|
|||||||
|
|
||||||
bool ImageRasterizer::hasGlyph(uint32 glyph) const
|
bool ImageRasterizer::hasGlyph(uint32 glyph) const
|
||||||
{
|
{
|
||||||
return imageGlyphs.find(glyph) != imageGlyphs.end();
|
return glyphIndices.find(glyph) != glyphIndices.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
Rasterizer::DataType ImageRasterizer::getDataType() const
|
Rasterizer::DataType ImageRasterizer::getDataType() const
|
||||||
@@ -153,5 +181,10 @@ Rasterizer::DataType ImageRasterizer::getDataType() const
|
|||||||
return DATA_IMAGE;
|
return DATA_IMAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextShaper *ImageRasterizer::newTextShaper()
|
||||||
|
{
|
||||||
|
return new GenericShaper(this);
|
||||||
|
}
|
||||||
|
|
||||||
} // font
|
} // font
|
||||||
} // love
|
} // love
|
||||||
|
|||||||
@@ -39,15 +39,18 @@ namespace font
|
|||||||
class ImageRasterizer : public Rasterizer
|
class ImageRasterizer : public Rasterizer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ImageRasterizer(love::image::ImageData *imageData, uint32 *glyphs, int numglyphs, int extraspacing, float dpiscale);
|
ImageRasterizer(love::image::ImageData *imageData, const uint32 *glyphs, int numglyphs, int extraspacing, float dpiscale);
|
||||||
virtual ~ImageRasterizer();
|
virtual ~ImageRasterizer();
|
||||||
|
|
||||||
// Implement Rasterizer
|
// Implement Rasterizer
|
||||||
int getLineHeight() const override;
|
int getLineHeight() const override;
|
||||||
GlyphData *getGlyphData(uint32 glyph) const override;
|
int getGlyphSpacing(uint32 glyph) const override;
|
||||||
|
int getGlyphIndex(uint32 glyph) const override;
|
||||||
|
GlyphData *getGlyphDataForIndex(int index) const override;
|
||||||
int getGlyphCount() const override;
|
int getGlyphCount() const override;
|
||||||
bool hasGlyph(uint32 glyph) const override;
|
bool hasGlyph(uint32 glyph) const override;
|
||||||
DataType getDataType() const override;
|
DataType getDataType() const override;
|
||||||
|
TextShaper *newTextShaper() override;
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -57,23 +60,23 @@ private:
|
|||||||
{
|
{
|
||||||
int x;
|
int x;
|
||||||
int width;
|
int width;
|
||||||
|
uint32 glyph;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Load all the glyph positions into memory
|
// Load all the glyph positions into memory
|
||||||
void load();
|
void load(const uint32 *glyphs, int glyphcount);
|
||||||
|
|
||||||
// The image data
|
// The image data
|
||||||
StrongRef<love::image::ImageData> imageData;
|
StrongRef<love::image::ImageData> imageData;
|
||||||
|
|
||||||
// The glyphs in the font
|
|
||||||
uint32 *glyphs;
|
|
||||||
|
|
||||||
// Number of glyphs in the font
|
// Number of glyphs in the font
|
||||||
int numglyphs;
|
int numglyphs;
|
||||||
|
|
||||||
int extraSpacing;
|
int extraSpacing;
|
||||||
|
|
||||||
std::map<uint32, ImageGlyphData> imageGlyphs;
|
|
||||||
|
std::vector<ImageGlyphData> imageGlyphs;
|
||||||
|
std::map<uint32, int> glyphIndices;
|
||||||
|
|
||||||
// Color used to identify glyph separation in the source ImageData
|
// Color used to identify glyph separation in the source ImageData
|
||||||
Color32 spacer;
|
Color32 spacer;
|
||||||
|
|||||||
@@ -55,6 +55,11 @@ int Rasterizer::getDescent() const
|
|||||||
return metrics.descent;
|
return metrics.descent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GlyphData *Rasterizer::getGlyphData(uint32 glyph) const
|
||||||
|
{
|
||||||
|
return getGlyphDataForIndex(getGlyphIndex(glyph));
|
||||||
|
}
|
||||||
|
|
||||||
GlyphData *Rasterizer::getGlyphData(const std::string &text) const
|
GlyphData *Rasterizer::getGlyphData(const std::string &text) const
|
||||||
{
|
{
|
||||||
uint32 codepoint = 0;
|
uint32 codepoint = 0;
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ namespace love
|
|||||||
namespace font
|
namespace font
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class TextShaper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds the specific font metrics.
|
* Holds the specific font metrics.
|
||||||
**/
|
**/
|
||||||
@@ -84,17 +86,32 @@ public:
|
|||||||
**/
|
**/
|
||||||
virtual int getLineHeight() const = 0;
|
virtual int getLineHeight() const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the spacing of the given unicode glyph.
|
||||||
|
**/
|
||||||
|
virtual int getGlyphSpacing(uint32 glyph) const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a rasterizer-specific index associated with the given glyph.
|
||||||
|
**/
|
||||||
|
virtual int getGlyphIndex(uint32 glyph) const = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a specific glyph.
|
* Gets a specific glyph.
|
||||||
* @param glyph The (UNICODE) glyph codepoint to get data for.
|
* @param glyph The (UNICODE) glyph codepoint to get data for.
|
||||||
**/
|
**/
|
||||||
virtual GlyphData *getGlyphData(uint32 glyph) const = 0;
|
GlyphData *getGlyphData(uint32 glyph) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a specific glyph.
|
* Gets a specific glyph.
|
||||||
* @param text The (UNICODE) glyph character to get the data for.
|
* @param text The (UNICODE) glyph character to get the data for.
|
||||||
**/
|
**/
|
||||||
virtual GlyphData *getGlyphData(const std::string &text) const;
|
GlyphData *getGlyphData(const std::string &text) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a specific glyph for the given rasterizer glyph index.
|
||||||
|
**/
|
||||||
|
virtual GlyphData *getGlyphDataForIndex(int index) const = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the number of glyphs the rasterizer has data for.
|
* Gets the number of glyphs the rasterizer has data for.
|
||||||
@@ -120,6 +137,10 @@ public:
|
|||||||
|
|
||||||
virtual DataType getDataType() const = 0;
|
virtual DataType getDataType() const = 0;
|
||||||
|
|
||||||
|
virtual ptrdiff_t getHandle() const { return 0; }
|
||||||
|
|
||||||
|
virtual TextShaper *newTextShaper() = 0;
|
||||||
|
|
||||||
float getDPIScale() const;
|
float getDPIScale() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
@@ -0,0 +1,381 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2006-2023 LOVE Development Team
|
||||||
|
*
|
||||||
|
* This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty. In no event will the authors be held liable for any damages
|
||||||
|
* arising from the use of this software.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it
|
||||||
|
* freely, subject to the following restrictions:
|
||||||
|
*
|
||||||
|
* 1. The origin of this software must not be misrepresented; you must not
|
||||||
|
* claim that you wrote the original software. If you use this software
|
||||||
|
* in a product, an acknowledgment in the product documentation would be
|
||||||
|
* appreciated but is not required.
|
||||||
|
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
* misrepresented as being the original software.
|
||||||
|
* 3. This notice may not be removed or altered from any source distribution.
|
||||||
|
**/
|
||||||
|
|
||||||
|
// LOVE
|
||||||
|
#include "TextShaper.h"
|
||||||
|
#include "Rasterizer.h"
|
||||||
|
#include "common/Exception.h"
|
||||||
|
|
||||||
|
#include "libraries/utf8/utf8.h"
|
||||||
|
|
||||||
|
namespace love
|
||||||
|
{
|
||||||
|
namespace font
|
||||||
|
{
|
||||||
|
|
||||||
|
void getCodepointsFromString(const std::string &text, std::vector<uint32> &codepoints)
|
||||||
|
{
|
||||||
|
codepoints.reserve(text.size());
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
utf8::iterator<std::string::const_iterator> i(text.begin(), text.begin(), text.end());
|
||||||
|
utf8::iterator<std::string::const_iterator> end(text.end(), text.begin(), text.end());
|
||||||
|
|
||||||
|
while (i != end)
|
||||||
|
{
|
||||||
|
uint32 g = *i++;
|
||||||
|
codepoints.push_back(g);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (utf8::exception &e)
|
||||||
|
{
|
||||||
|
throw love::Exception("UTF-8 decoding error: %s", e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void getCodepointsFromString(const std::vector<ColoredString> &strs, ColoredCodepoints &codepoints)
|
||||||
|
{
|
||||||
|
if (strs.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
codepoints.cps.reserve(strs[0].str.size());
|
||||||
|
|
||||||
|
for (const ColoredString &cstr : strs)
|
||||||
|
{
|
||||||
|
// No need to add the color if the string is empty anyway, and the code
|
||||||
|
// further on assumes no two colors share the same starting position.
|
||||||
|
if (cstr.str.size() == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
IndexedColor c = { cstr.color, (int)codepoints.cps.size() };
|
||||||
|
codepoints.colors.push_back(c);
|
||||||
|
|
||||||
|
getCodepointsFromString(cstr.str, codepoints.cps);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (codepoints.colors.size() == 1)
|
||||||
|
{
|
||||||
|
IndexedColor c = codepoints.colors[0];
|
||||||
|
|
||||||
|
if (c.index == 0 && c.color == Colorf(1.0f, 1.0f, 1.0f, 1.0f))
|
||||||
|
codepoints.colors.pop_back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
love::Type TextShaper::type("TextShaper", &Object::type);
|
||||||
|
|
||||||
|
TextShaper::TextShaper(Rasterizer *rasterizer)
|
||||||
|
: rasterizers{rasterizer}
|
||||||
|
, dpiScales{rasterizer->getDPIScale()}
|
||||||
|
, height(floorf(rasterizer->getHeight() / rasterizer->getDPIScale() + 0.5f))
|
||||||
|
, lineHeight(1)
|
||||||
|
, useSpacesForTab(false)
|
||||||
|
{
|
||||||
|
if (!rasterizer->hasGlyph('\t'))
|
||||||
|
useSpacesForTab = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
TextShaper::~TextShaper()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
float TextShaper::getHeight() const
|
||||||
|
{
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextShaper::setLineHeight(float h)
|
||||||
|
{
|
||||||
|
lineHeight = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
float TextShaper::getLineHeight() const
|
||||||
|
{
|
||||||
|
return lineHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
int TextShaper::getAscent() const
|
||||||
|
{
|
||||||
|
return floorf(rasterizers[0]->getAscent() / rasterizers[0]->getDPIScale() + 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
int TextShaper::getDescent() const
|
||||||
|
{
|
||||||
|
return floorf(rasterizers[0]->getDescent() / rasterizers[0]->getDPIScale() + 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
float TextShaper::getBaseline() const
|
||||||
|
{
|
||||||
|
float ascent = getAscent();
|
||||||
|
if (ascent != 0.0f)
|
||||||
|
return ascent;
|
||||||
|
else if (rasterizers[0]->getDataType() == font::Rasterizer::DATA_TRUETYPE)
|
||||||
|
return floorf(getHeight() / 1.25f + 0.5f); // 1.25 is magic line height for true type fonts
|
||||||
|
else
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextShaper::hasGlyph(uint32 glyph) const
|
||||||
|
{
|
||||||
|
for (const StrongRef<Rasterizer> &r : rasterizers)
|
||||||
|
{
|
||||||
|
if (r->hasGlyph(glyph))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextShaper::hasGlyphs(const std::string &text) const
|
||||||
|
{
|
||||||
|
if (text.size() == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
utf8::iterator<std::string::const_iterator> i(text.begin(), text.begin(), text.end());
|
||||||
|
utf8::iterator<std::string::const_iterator> end(text.end(), text.begin(), text.end());
|
||||||
|
|
||||||
|
while (i != end)
|
||||||
|
{
|
||||||
|
uint32 codepoint = *i++;
|
||||||
|
|
||||||
|
if (!hasGlyph(codepoint))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (utf8::exception &e)
|
||||||
|
{
|
||||||
|
throw love::Exception("UTF-8 decoding error: %s", e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
float TextShaper::getKerning(uint32 leftglyph, uint32 rightglyph)
|
||||||
|
{
|
||||||
|
uint64 packedglyphs = ((uint64)leftglyph << 32) | (uint64)rightglyph;
|
||||||
|
|
||||||
|
const auto it = kerning.find(packedglyphs);
|
||||||
|
if (it != kerning.end())
|
||||||
|
return it->second;
|
||||||
|
|
||||||
|
float k = 0.0f;
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
for (const auto &r : rasterizers)
|
||||||
|
{
|
||||||
|
if (r->hasGlyph(leftglyph) && r->hasGlyph(rightglyph))
|
||||||
|
{
|
||||||
|
found = true;
|
||||||
|
k = floorf(r->getKerning(leftglyph, rightglyph) / r->getDPIScale() + 0.5f);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
k = floorf(rasterizers[0]->getKerning(leftglyph, rightglyph) / rasterizers[0]->getDPIScale() + 0.5f);
|
||||||
|
|
||||||
|
kerning[packedglyphs] = k;
|
||||||
|
return k;
|
||||||
|
}
|
||||||
|
|
||||||
|
float TextShaper::getKerning(const std::string &leftchar, const std::string &rightchar)
|
||||||
|
{
|
||||||
|
uint32 left = 0;
|
||||||
|
uint32 right = 0;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
left = utf8::peek_next(leftchar.begin(), leftchar.end());
|
||||||
|
right = utf8::peek_next(rightchar.begin(), rightchar.end());
|
||||||
|
}
|
||||||
|
catch (utf8::exception &e)
|
||||||
|
{
|
||||||
|
throw love::Exception("UTF-8 decoding error: %s", e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
return getKerning(left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
int TextShaper::getGlyphAdvance(uint32 glyph, GlyphIndex *glyphindex)
|
||||||
|
{
|
||||||
|
const auto it = glyphAdvances.find(glyph);
|
||||||
|
if (it != glyphAdvances.end())
|
||||||
|
{
|
||||||
|
if (glyphindex)
|
||||||
|
*glyphindex = it->second.second;
|
||||||
|
return it->second.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rasterizeri = 0;
|
||||||
|
uint32 realglyph = glyph;
|
||||||
|
|
||||||
|
if (glyph == '\t' && isUsingSpacesForTab())
|
||||||
|
realglyph = ' ';
|
||||||
|
|
||||||
|
for (size_t i = 0; i < rasterizers.size(); i++)
|
||||||
|
{
|
||||||
|
if (rasterizers[i]->hasGlyph(realglyph))
|
||||||
|
{
|
||||||
|
rasterizeri = (int) i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto &r = rasterizers[rasterizeri];
|
||||||
|
int advance = floorf(r->getGlyphSpacing(realglyph) / r->getDPIScale() + 0.5f);
|
||||||
|
|
||||||
|
if (glyph == '\t' && realglyph == ' ')
|
||||||
|
advance *= SPACES_PER_TAB;
|
||||||
|
|
||||||
|
GlyphIndex glyphi = {r->getGlyphIndex(realglyph), rasterizeri};
|
||||||
|
|
||||||
|
glyphAdvances[glyph] = std::make_pair(advance, glyphi);
|
||||||
|
if (glyphindex)
|
||||||
|
*glyphindex = glyphi;
|
||||||
|
return advance;
|
||||||
|
}
|
||||||
|
|
||||||
|
int TextShaper::getWidth(const std::string &str)
|
||||||
|
{
|
||||||
|
if (str.size() == 0) return 0;
|
||||||
|
|
||||||
|
ColoredCodepoints codepoints;
|
||||||
|
getCodepointsFromString(str, codepoints.cps);
|
||||||
|
|
||||||
|
TextInfo info;
|
||||||
|
computeGlyphPositions(codepoints, Range(), Vector2(0.0f, 0.0f), 0.0f, nullptr, nullptr, &info);
|
||||||
|
|
||||||
|
return info.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t findNewline(const ColoredCodepoints &codepoints, size_t start)
|
||||||
|
{
|
||||||
|
for (size_t i = start; i < codepoints.cps.size(); i++)
|
||||||
|
{
|
||||||
|
if (codepoints.cps[i] == '\n')
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return codepoints.cps.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextShaper::getWrap(const ColoredCodepoints &codepoints, float wraplimit, std::vector<Range> &lineranges, std::vector<int> *linewidths)
|
||||||
|
{
|
||||||
|
size_t nextnewline = findNewline(codepoints, 0);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < codepoints.cps.size();)
|
||||||
|
{
|
||||||
|
if (nextnewline < i)
|
||||||
|
nextnewline = findNewline(codepoints, i);
|
||||||
|
|
||||||
|
if (nextnewline == i) // Empty line.
|
||||||
|
{
|
||||||
|
lineranges.push_back(Range());
|
||||||
|
if (linewidths)
|
||||||
|
linewidths->push_back(0);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Range r(i, nextnewline - i);
|
||||||
|
float width = 0.0f;
|
||||||
|
int wrapindex = computeWordWrapIndex(codepoints, r, wraplimit, &width);
|
||||||
|
|
||||||
|
if (wrapindex >= (int) i)
|
||||||
|
{
|
||||||
|
r = Range(i, (size_t) wrapindex + 1 - i);
|
||||||
|
i = (size_t)wrapindex + 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
r = Range();
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We've already handled this line, skip the newline character.
|
||||||
|
if (nextnewline == i)
|
||||||
|
i++;
|
||||||
|
|
||||||
|
lineranges.push_back(r);
|
||||||
|
if (linewidths)
|
||||||
|
linewidths->push_back(width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextShaper::getWrap(const std::vector<ColoredString> &text, float wraplimit, std::vector<std::string> &lines, std::vector<int> *linewidths)
|
||||||
|
{
|
||||||
|
ColoredCodepoints cps;
|
||||||
|
getCodepointsFromString(text, cps);
|
||||||
|
|
||||||
|
std::vector<Range> codepointranges;
|
||||||
|
getWrap(cps, wraplimit, codepointranges, linewidths);
|
||||||
|
|
||||||
|
std::string line;
|
||||||
|
|
||||||
|
for (const auto &range : codepointranges)
|
||||||
|
{
|
||||||
|
line.clear();
|
||||||
|
|
||||||
|
if (range.isValid())
|
||||||
|
{
|
||||||
|
line.reserve(range.getSize());
|
||||||
|
|
||||||
|
for (size_t i = range.getMin(); i <= range.getMax(); i++)
|
||||||
|
{
|
||||||
|
char character[5] = { '\0' };
|
||||||
|
char *end = utf8::unchecked::append(cps.cps[i], character);
|
||||||
|
line.append(character, end - character);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.push_back(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextShaper::setFallbacks(const std::vector<Rasterizer*> &fallbacks)
|
||||||
|
{
|
||||||
|
for (Rasterizer *r : fallbacks)
|
||||||
|
{
|
||||||
|
if (r->getDataType() != rasterizers[0]->getDataType())
|
||||||
|
throw love::Exception("Font fallbacks must be of the same font type.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear caches.
|
||||||
|
kerning.clear();
|
||||||
|
glyphAdvances.clear();
|
||||||
|
|
||||||
|
rasterizers.resize(1);
|
||||||
|
dpiScales.resize(1);
|
||||||
|
|
||||||
|
for (Rasterizer *r : fallbacks)
|
||||||
|
{
|
||||||
|
rasterizers.push_back(r);
|
||||||
|
dpiScales.push_back(r->getDPIScale());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // font
|
||||||
|
} // love
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2006-2023 LOVE Development Team
|
||||||
|
*
|
||||||
|
* This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty. In no event will the authors be held liable for any damages
|
||||||
|
* arising from the use of this software.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it
|
||||||
|
* freely, subject to the following restrictions:
|
||||||
|
*
|
||||||
|
* 1. The origin of this software must not be misrepresented; you must not
|
||||||
|
* claim that you wrote the original software. If you use this software
|
||||||
|
* in a product, an acknowledgment in the product documentation would be
|
||||||
|
* appreciated but is not required.
|
||||||
|
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
* misrepresented as being the original software.
|
||||||
|
* 3. This notice may not be removed or altered from any source distribution.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// LOVE
|
||||||
|
#include "common/Object.h"
|
||||||
|
#include "common/Vector.h"
|
||||||
|
#include "common/int.h"
|
||||||
|
#include "common/Color.h"
|
||||||
|
#include "common/Range.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace love
|
||||||
|
{
|
||||||
|
namespace font
|
||||||
|
{
|
||||||
|
|
||||||
|
class Rasterizer;
|
||||||
|
|
||||||
|
struct ColoredString
|
||||||
|
{
|
||||||
|
std::string str;
|
||||||
|
Colorf color;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IndexedColor
|
||||||
|
{
|
||||||
|
Colorf color;
|
||||||
|
int index;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ColoredCodepoints
|
||||||
|
{
|
||||||
|
std::vector<uint32> cps;
|
||||||
|
std::vector<IndexedColor> colors;
|
||||||
|
};
|
||||||
|
|
||||||
|
void getCodepointsFromString(const std::string &str, std::vector<uint32> &codepoints);
|
||||||
|
void getCodepointsFromString(const std::vector<ColoredString> &strs, ColoredCodepoints &codepoints);
|
||||||
|
|
||||||
|
class TextShaper : public Object
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
struct GlyphIndex
|
||||||
|
{
|
||||||
|
int index;
|
||||||
|
int rasterizerIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GlyphPosition
|
||||||
|
{
|
||||||
|
Vector2 position;
|
||||||
|
GlyphIndex glyphIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TextInfo
|
||||||
|
{
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
};
|
||||||
|
|
||||||
|
// This will be used if the Rasterizer doesn't have a tab character itself.
|
||||||
|
static const int SPACES_PER_TAB = 4;
|
||||||
|
|
||||||
|
static love::Type type;
|
||||||
|
|
||||||
|
virtual ~TextShaper();
|
||||||
|
|
||||||
|
const std::vector<StrongRef<Rasterizer>> &getRasterizers() const { return rasterizers; }
|
||||||
|
bool isUsingSpacesForTab() const { return useSpacesForTab; }
|
||||||
|
|
||||||
|
float getHeight() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the line height (which should be a number to multiply the font size by,
|
||||||
|
* example: line height = 1.2 and size = 12 means that rendered line height = 12*1.2)
|
||||||
|
* @param height The new line height.
|
||||||
|
**/
|
||||||
|
void setLineHeight(float height);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the line height.
|
||||||
|
**/
|
||||||
|
float getLineHeight() const;
|
||||||
|
|
||||||
|
// Extra font metrics
|
||||||
|
int getAscent() const;
|
||||||
|
int getDescent() const;
|
||||||
|
float getBaseline() const;
|
||||||
|
|
||||||
|
bool hasGlyph(uint32 glyph) const;
|
||||||
|
bool hasGlyphs(const std::string &text) const;
|
||||||
|
|
||||||
|
float getKerning(uint32 leftglyph, uint32 rightglyph);
|
||||||
|
float getKerning(const std::string &leftchar, const std::string &rightchar);
|
||||||
|
|
||||||
|
int getGlyphAdvance(uint32 glyph, GlyphIndex *glyphindex = nullptr);
|
||||||
|
|
||||||
|
int getWidth(const std::string &str);
|
||||||
|
|
||||||
|
void getWrap(const std::vector<ColoredString> &text, float wraplimit, std::vector<std::string> &lines, std::vector<int> *linewidths = nullptr);
|
||||||
|
void getWrap(const ColoredCodepoints &codepoints, float wraplimit, std::vector<Range> &lineranges, std::vector<int> *linewidths = nullptr);
|
||||||
|
|
||||||
|
virtual void setFallbacks(const std::vector<Rasterizer *> &fallbacks);
|
||||||
|
|
||||||
|
virtual void computeGlyphPositions(const ColoredCodepoints &codepoints, Range range, Vector2 offset, float extraspacing, std::vector<GlyphPosition> *positions, std::vector<IndexedColor> *colors, TextInfo *info) = 0;
|
||||||
|
virtual int computeWordWrapIndex(const ColoredCodepoints &codepoints, Range range, float wraplimit, float *width) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
TextShaper(Rasterizer *rasterizer);
|
||||||
|
|
||||||
|
static inline bool isWhitespace(uint32 codepoint) { return codepoint == ' ' || codepoint == '\t'; }
|
||||||
|
|
||||||
|
std::vector<StrongRef<Rasterizer>> rasterizers;
|
||||||
|
std::vector<float> dpiScales;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
int height;
|
||||||
|
float lineHeight;
|
||||||
|
|
||||||
|
bool useSpacesForTab;
|
||||||
|
|
||||||
|
// maps glyphs to advance and glyph+rasterizer index.
|
||||||
|
std::unordered_map<uint32, std::pair<int, GlyphIndex>> glyphAdvances;
|
||||||
|
|
||||||
|
// map of left/right glyph pairs to horizontal kerning.
|
||||||
|
std::unordered_map<uint64, float> kerning;
|
||||||
|
|
||||||
|
}; // TextShaper
|
||||||
|
|
||||||
|
} // font
|
||||||
|
} // love
|
||||||
@@ -0,0 +1,409 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2006-2023 LOVE Development Team
|
||||||
|
*
|
||||||
|
* This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty. In no event will the authors be held liable for any damages
|
||||||
|
* arising from the use of this software.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it
|
||||||
|
* freely, subject to the following restrictions:
|
||||||
|
*
|
||||||
|
* 1. The origin of this software must not be misrepresented; you must not
|
||||||
|
* claim that you wrote the original software. If you use this software
|
||||||
|
* in a product, an acknowledgment in the product documentation would be
|
||||||
|
* appreciated but is not required.
|
||||||
|
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
* misrepresented as being the original software.
|
||||||
|
* 3. This notice may not be removed or altered from any source distribution.
|
||||||
|
**/
|
||||||
|
|
||||||
|
// LOVE
|
||||||
|
#include "HarfbuzzShaper.h"
|
||||||
|
#include "TrueTypeRasterizer.h"
|
||||||
|
#include "common/Optional.h"
|
||||||
|
|
||||||
|
// harfbuzz
|
||||||
|
#include <hb.h>
|
||||||
|
#include <hb-ft.h>
|
||||||
|
|
||||||
|
namespace love
|
||||||
|
{
|
||||||
|
namespace font
|
||||||
|
{
|
||||||
|
namespace freetype
|
||||||
|
{
|
||||||
|
|
||||||
|
HarfbuzzShaper::HarfbuzzShaper(TrueTypeRasterizer *rasterizer)
|
||||||
|
: TextShaper(rasterizer)
|
||||||
|
, spaceGlyphIndex()
|
||||||
|
, tabSpacesAdvanceX(0)
|
||||||
|
, tabSpacesAdvanceY(0)
|
||||||
|
{
|
||||||
|
hbFonts.push_back(hb_ft_font_create_referenced((FT_Face)rasterizer->getHandle()));
|
||||||
|
hbBuffers.push_back(hb_buffer_create());
|
||||||
|
|
||||||
|
if (hbFonts[0] == nullptr || hbFonts[0] == hb_font_get_empty())
|
||||||
|
throw love::Exception("Could not create Harfbuzz font object.");
|
||||||
|
|
||||||
|
if (hbBuffers[0] == nullptr || hbBuffers[0] == hb_buffer_get_empty())
|
||||||
|
throw love::Exception("Could not create Harfbuzz buffer object.");
|
||||||
|
|
||||||
|
updateSpacesForTabInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
HarfbuzzShaper::~HarfbuzzShaper()
|
||||||
|
{
|
||||||
|
for (hb_buffer_t *buffer : hbBuffers)
|
||||||
|
hb_buffer_destroy(buffer);
|
||||||
|
for (hb_font_t *font : hbFonts)
|
||||||
|
hb_font_destroy(font);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HarfbuzzShaper::setFallbacks(const std::vector<Rasterizer*> &fallbacks)
|
||||||
|
{
|
||||||
|
for (size_t i = 1; i < rasterizers.size(); i++)
|
||||||
|
{
|
||||||
|
hb_buffer_destroy(hbBuffers[i]);
|
||||||
|
hb_font_destroy(hbFonts[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
TextShaper::setFallbacks(fallbacks);
|
||||||
|
|
||||||
|
hbFonts.resize(rasterizers.size());
|
||||||
|
hbBuffers.resize(rasterizers.size());
|
||||||
|
|
||||||
|
for (size_t i = 1; i < rasterizers.size(); i++)
|
||||||
|
{
|
||||||
|
hbFonts[i] = hb_ft_font_create_referenced((FT_Face)rasterizers[i]->getHandle());
|
||||||
|
hbBuffers[i] = hb_buffer_create();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSpacesForTabInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HarfbuzzShaper::updateSpacesForTabInfo()
|
||||||
|
{
|
||||||
|
if (!isUsingSpacesForTab())
|
||||||
|
return;
|
||||||
|
|
||||||
|
hb_codepoint_t glyphid = 0;
|
||||||
|
for (size_t i = 0; i < hbFonts.size(); i++)
|
||||||
|
{
|
||||||
|
hb_font_t *hbfont = hbFonts[i];
|
||||||
|
if (hb_font_get_glyph(hbfont, ' ', 0, &glyphid))
|
||||||
|
{
|
||||||
|
spaceGlyphIndex.index = glyphid;
|
||||||
|
spaceGlyphIndex.rasterizerIndex = i;
|
||||||
|
tabSpacesAdvanceX = hb_font_get_glyph_h_advance(hbfont, glyphid) * SPACES_PER_TAB;
|
||||||
|
tabSpacesAdvanceY = hb_font_get_glyph_v_advance(hbfont, glyphid) * SPACES_PER_TAB;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HarfbuzzShaper::isValidGlyph(uint32 glyphindex, const std::vector<uint32> &codepoints, uint32 codepointindex)
|
||||||
|
{
|
||||||
|
if (glyphindex != 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
uint32 codepoint = codepoints[codepointindex];
|
||||||
|
if (codepoint == '\n' || codepoint == '\r' || (codepoint == '\t' && isUsingSpacesForTab()))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HarfbuzzShaper::computeBufferRanges(const ColoredCodepoints &codepoints, Range range, std::vector<BufferRange> &bufferranges)
|
||||||
|
{
|
||||||
|
bufferranges.clear();
|
||||||
|
|
||||||
|
// Less computation for the typical case (no fallback fonts).
|
||||||
|
if (rasterizers.size() == 1)
|
||||||
|
{
|
||||||
|
hb_buffer_reset(hbBuffers[0]);
|
||||||
|
hb_buffer_add_codepoints(hbBuffers[0], codepoints.cps.data(), codepoints.cps.size(), (unsigned int)range.getOffset(), (int)range.getSize());
|
||||||
|
|
||||||
|
// TODO: Expose APIs for direction and script?
|
||||||
|
hb_buffer_guess_segment_properties(hbBuffers[0]);
|
||||||
|
|
||||||
|
hb_shape(hbFonts[0], hbBuffers[0], nullptr, 0);
|
||||||
|
|
||||||
|
bufferranges.push_back({0, (int) range.first, Range(0, hb_buffer_get_length(hbBuffers[0]))});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Range> fallbackranges = { range };
|
||||||
|
|
||||||
|
// For each font, figure out the ranges of valid glyphs in the given string,
|
||||||
|
// and add the rest to a list to be shaped by the next fallback font.
|
||||||
|
// Harfbuzz doesn't have its own fallback API.
|
||||||
|
for (size_t rasti = 0; rasti < rasterizers.size(); rasti++)
|
||||||
|
{
|
||||||
|
hb_buffer_t *hbb = hbBuffers[rasti];
|
||||||
|
hb_buffer_reset(hbb);
|
||||||
|
|
||||||
|
for (Range r : fallbackranges)
|
||||||
|
hb_buffer_add_codepoints(hbb, codepoints.cps.data(), codepoints.cps.size(), (unsigned int)r.getOffset(), (int)r.getSize());
|
||||||
|
|
||||||
|
hb_buffer_guess_segment_properties(hbb);
|
||||||
|
|
||||||
|
hb_shape(hbFonts[rasti], hbb, nullptr, 0);
|
||||||
|
|
||||||
|
int glyphcount = (int)hb_buffer_get_length(hbb);
|
||||||
|
const hb_glyph_info_t *glyphinfos = hb_buffer_get_glyph_infos(hbb, nullptr);
|
||||||
|
|
||||||
|
fallbackranges.clear();
|
||||||
|
|
||||||
|
for (int i = 0; i < glyphcount; i++)
|
||||||
|
{
|
||||||
|
if (isValidGlyph(glyphinfos[i].codepoint, codepoints.cps, glyphinfos[i].cluster))
|
||||||
|
{
|
||||||
|
if (bufferranges.empty() || bufferranges.back().index != rasti || bufferranges.back().range.getMax() != i)
|
||||||
|
bufferranges.push_back({(int)rasti, (int)glyphinfos[i].cluster, Range(i, 1)});
|
||||||
|
else
|
||||||
|
bufferranges.back().range.last++;
|
||||||
|
}
|
||||||
|
else if (rasti == rasterizers.size() - 1)
|
||||||
|
{
|
||||||
|
// Use the first font for remaining invalid glyphs when no
|
||||||
|
// fallback font supports them.
|
||||||
|
if (bufferranges.empty() || bufferranges.back().index != 0 || bufferranges.back().range.getMax() != i)
|
||||||
|
bufferranges.push_back({0, (int)glyphinfos[i].cluster, Range(i, 1)});
|
||||||
|
else
|
||||||
|
bufferranges.back().range.last++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (fallbackranges.empty() || fallbackranges.back().getMax() != glyphinfos[i - 1].cluster)
|
||||||
|
fallbackranges.push_back(Range(glyphinfos[i].cluster, 1));
|
||||||
|
else
|
||||||
|
fallbackranges.back().encapsulate(glyphinfos[i].cluster);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(bufferranges.begin(), bufferranges.end(), [](const BufferRange &a, const BufferRange &b)
|
||||||
|
{
|
||||||
|
if (a.codepointStart != b.codepointStart)
|
||||||
|
return a.codepointStart < b.codepointStart;
|
||||||
|
if (a.index != b.index)
|
||||||
|
return a.index < b.index;
|
||||||
|
return a.range.first < b.range.first;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void HarfbuzzShaper::computeGlyphPositions(const ColoredCodepoints &codepoints, Range range, Vector2 offset, float extraspacing, std::vector<GlyphPosition> *positions, std::vector<IndexedColor> *colors, TextInfo *info)
|
||||||
|
{
|
||||||
|
if (!range.isValid())
|
||||||
|
range = Range(0, codepoints.cps.size());
|
||||||
|
|
||||||
|
offset.y += getBaseline();
|
||||||
|
Vector2 curpos = offset;
|
||||||
|
|
||||||
|
int colorindex = 0;
|
||||||
|
int ncolors = (int)codepoints.colors.size();
|
||||||
|
Optional<Colorf> colorToAdd;
|
||||||
|
|
||||||
|
// Make sure the right color is applied to the start of the glyph list,
|
||||||
|
// when the start isn't 0.
|
||||||
|
if (colors && range.getOffset() > 0 && !codepoints.colors.empty())
|
||||||
|
{
|
||||||
|
for (; colorindex < ncolors; colorindex++)
|
||||||
|
{
|
||||||
|
if (codepoints.colors[colorindex].index >= (int) range.getOffset())
|
||||||
|
break;
|
||||||
|
colorToAdd.set(codepoints.colors[colorindex].color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<BufferRange> bufferranges;
|
||||||
|
computeBufferRanges(codepoints, range, bufferranges);
|
||||||
|
|
||||||
|
int maxwidth = (int)curpos.x;
|
||||||
|
|
||||||
|
for (const auto &bufferrange : bufferranges)
|
||||||
|
{
|
||||||
|
if (positions)
|
||||||
|
positions->reserve(positions->size() + bufferrange.range.getSize());
|
||||||
|
|
||||||
|
hb_buffer_t *hbbuffer = hbBuffers[bufferrange.index];
|
||||||
|
|
||||||
|
const hb_glyph_info_t *glyphinfos = hb_buffer_get_glyph_infos(hbbuffer, nullptr);
|
||||||
|
hb_glyph_position_t *glyphpositions = hb_buffer_get_glyph_positions(hbbuffer, nullptr);
|
||||||
|
hb_direction_t direction = hb_buffer_get_direction(hbbuffer);
|
||||||
|
|
||||||
|
for (size_t i = bufferrange.range.first; i <= bufferrange.range.last; i++)
|
||||||
|
{
|
||||||
|
const hb_glyph_info_t &info = glyphinfos[i];
|
||||||
|
hb_glyph_position_t &glyphpos = glyphpositions[i];
|
||||||
|
|
||||||
|
// TODO: this doesn't handle situations where the user inserted a color
|
||||||
|
// change in the middle of some characters that get combined into a single
|
||||||
|
// cluster.
|
||||||
|
if (colors && colorindex < ncolors && codepoints.colors[colorindex].index == info.cluster)
|
||||||
|
{
|
||||||
|
colorToAdd.set(codepoints.colors[colorindex].color);
|
||||||
|
colorindex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 clustercodepoint = codepoints.cps[info.cluster];
|
||||||
|
|
||||||
|
// Harfbuzz doesn't handle newlines itself, but it does leave them in
|
||||||
|
// the glyph list so we can do it manually.
|
||||||
|
if (clustercodepoint == '\n')
|
||||||
|
{
|
||||||
|
if (curpos.x > maxwidth)
|
||||||
|
maxwidth = (int)curpos.x;
|
||||||
|
|
||||||
|
// Wrap newline, but do not output a position for it.
|
||||||
|
curpos.y += floorf(getHeight() * getLineHeight() + 0.5f);
|
||||||
|
curpos.x = offset.x;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore carriage returns
|
||||||
|
if (clustercodepoint == '\r')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// This is a glyph index at this point, despite the name.
|
||||||
|
GlyphIndex gindex = { (int) info.codepoint, bufferrange.index };
|
||||||
|
|
||||||
|
if (clustercodepoint == '\t' && isUsingSpacesForTab())
|
||||||
|
{
|
||||||
|
gindex = spaceGlyphIndex;
|
||||||
|
|
||||||
|
// This should be safe to overwrite.
|
||||||
|
// TODO: RTL support?
|
||||||
|
glyphpos.x_offset = 0;
|
||||||
|
glyphpos.y_offset = 0;
|
||||||
|
glyphpos.x_advance = HB_DIRECTION_IS_HORIZONTAL(direction) ? tabSpacesAdvanceX : 0;
|
||||||
|
glyphpos.y_advance = HB_DIRECTION_IS_VERTICAL(direction) ? tabSpacesAdvanceY : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (colorToAdd.hasValue && colors && positions)
|
||||||
|
{
|
||||||
|
IndexedColor c = {colorToAdd.value, (int) positions->size()};
|
||||||
|
colors->push_back(c);
|
||||||
|
colorToAdd.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (positions)
|
||||||
|
{
|
||||||
|
GlyphPosition p = { curpos, gindex };
|
||||||
|
|
||||||
|
// Harfbuzz position coordinate systems are based on the given font.
|
||||||
|
// Freetype uses 26.6 fixed point coordinates, so harfbuzz does too.
|
||||||
|
p.position.x += floorf((glyphpos.x_offset >> 6) / dpiScales[0] + 0.5f);
|
||||||
|
p.position.y += floorf((glyphpos.y_offset >> 6) / dpiScales[0] + 0.5f);
|
||||||
|
|
||||||
|
positions->push_back(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
curpos.x += floorf((glyphpos.x_advance >> 6) / dpiScales[0] + 0.5f);
|
||||||
|
curpos.y += floorf((glyphpos.y_advance >> 6) / dpiScales[0] + 0.5f);
|
||||||
|
|
||||||
|
// Account for extra spacing given to space characters.
|
||||||
|
if (clustercodepoint == ' ' && extraspacing != 0.0f)
|
||||||
|
curpos.x = floorf(curpos.x + extraspacing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (curpos.x > maxwidth)
|
||||||
|
maxwidth = (int)curpos.x;
|
||||||
|
|
||||||
|
if (info != nullptr)
|
||||||
|
{
|
||||||
|
info->width = maxwidth - offset.x;
|
||||||
|
info->height = curpos.y - offset.y;
|
||||||
|
if (curpos.x > offset.x)
|
||||||
|
info->height += floorf(getHeight() * getLineHeight() + 0.5f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int HarfbuzzShaper::computeWordWrapIndex(const ColoredCodepoints &codepoints, Range range, float wraplimit, float *width)
|
||||||
|
{
|
||||||
|
if (!range.isValid())
|
||||||
|
range = Range(0, codepoints.cps.size());
|
||||||
|
|
||||||
|
float w = 0.0f;
|
||||||
|
float outwidth = 0.0f;
|
||||||
|
float widthbeforelastspace = 0.0f;
|
||||||
|
int wrapindex = -1;
|
||||||
|
int lastspaceindex = -1;
|
||||||
|
|
||||||
|
uint32 prevcodepoint = 0;
|
||||||
|
|
||||||
|
std::vector<BufferRange> bufferranges;
|
||||||
|
computeBufferRanges(codepoints, range, bufferranges);
|
||||||
|
|
||||||
|
for (const auto &bufferrange : bufferranges)
|
||||||
|
{
|
||||||
|
hb_buffer_t *hbbuffer = hbBuffers[bufferrange.index];
|
||||||
|
|
||||||
|
const hb_glyph_info_t *glyphinfos = hb_buffer_get_glyph_infos(hbbuffer, nullptr);
|
||||||
|
hb_glyph_position_t *glyphpositions = hb_buffer_get_glyph_positions(hbbuffer, nullptr);
|
||||||
|
hb_direction_t direction = hb_buffer_get_direction(hbbuffer);
|
||||||
|
|
||||||
|
for (size_t i = bufferrange.range.first; i <= bufferrange.range.last; i++)
|
||||||
|
{
|
||||||
|
const hb_glyph_info_t &info = glyphinfos[i];
|
||||||
|
hb_glyph_position_t &glyphpos = glyphpositions[i];
|
||||||
|
|
||||||
|
uint32 clustercodepoint = codepoints.cps[info.cluster];
|
||||||
|
|
||||||
|
if (clustercodepoint == '\r')
|
||||||
|
{
|
||||||
|
prevcodepoint = clustercodepoint;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clustercodepoint == '\t' && isUsingSpacesForTab())
|
||||||
|
{
|
||||||
|
// This should be safe to overwrite.
|
||||||
|
// TODO: RTL support?
|
||||||
|
glyphpos.x_offset = 0;
|
||||||
|
glyphpos.y_offset = 0;
|
||||||
|
glyphpos.x_advance = HB_DIRECTION_IS_HORIZONTAL(direction) ? tabSpacesAdvanceX : 0;
|
||||||
|
glyphpos.y_advance = HB_DIRECTION_IS_VERTICAL(direction) ? tabSpacesAdvanceY : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
float newwidth = w + floorf((glyphpos.x_advance >> 6) / dpiScales[0] + 0.5f);
|
||||||
|
|
||||||
|
// Only wrap when there's a non-space character.
|
||||||
|
if (newwidth > wraplimit && !isWhitespace(clustercodepoint))
|
||||||
|
{
|
||||||
|
// Rewind to the last seen space when wrapping.
|
||||||
|
if (lastspaceindex != -1)
|
||||||
|
{
|
||||||
|
wrapindex = lastspaceindex;
|
||||||
|
outwidth = widthbeforelastspace;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't count trailing spaces in the output width.
|
||||||
|
if (isWhitespace(clustercodepoint))
|
||||||
|
{
|
||||||
|
lastspaceindex = info.cluster;
|
||||||
|
if (!isWhitespace(prevcodepoint))
|
||||||
|
widthbeforelastspace = w;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
outwidth = newwidth;
|
||||||
|
|
||||||
|
w = newwidth;
|
||||||
|
prevcodepoint = clustercodepoint;
|
||||||
|
wrapindex = info.cluster;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (width)
|
||||||
|
*width = outwidth;
|
||||||
|
|
||||||
|
return wrapindex;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // freetype
|
||||||
|
} // font
|
||||||
|
} // love
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2006-2023 LOVE Development Team
|
||||||
|
*
|
||||||
|
* This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty. In no event will the authors be held liable for any damages
|
||||||
|
* arising from the use of this software.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it
|
||||||
|
* freely, subject to the following restrictions:
|
||||||
|
*
|
||||||
|
* 1. The origin of this software must not be misrepresented; you must not
|
||||||
|
* claim that you wrote the original software. If you use this software
|
||||||
|
* in a product, an acknowledgment in the product documentation would be
|
||||||
|
* appreciated but is not required.
|
||||||
|
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
* misrepresented as being the original software.
|
||||||
|
* 3. This notice may not be removed or altered from any source distribution.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// LOVE
|
||||||
|
#include "font/TextShaper.h"
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
typedef struct hb_font_t hb_font_t;
|
||||||
|
typedef struct hb_buffer_t hb_buffer_t;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace love
|
||||||
|
{
|
||||||
|
namespace font
|
||||||
|
{
|
||||||
|
namespace freetype
|
||||||
|
{
|
||||||
|
|
||||||
|
class TrueTypeRasterizer;
|
||||||
|
|
||||||
|
class HarfbuzzShaper : public love::font::TextShaper
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
HarfbuzzShaper(TrueTypeRasterizer *rasterizer);
|
||||||
|
virtual ~HarfbuzzShaper();
|
||||||
|
|
||||||
|
void setFallbacks(const std::vector<Rasterizer *> &fallbacks) override;
|
||||||
|
void computeGlyphPositions(const ColoredCodepoints &codepoints, Range range, Vector2 offset, float extraspacing, std::vector<GlyphPosition> *positions, std::vector<IndexedColor> *colors, TextInfo *info) override;
|
||||||
|
int computeWordWrapIndex(const ColoredCodepoints &codepoints, Range range, float wraplimit, float *width) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
struct BufferRange
|
||||||
|
{
|
||||||
|
int index;
|
||||||
|
int codepointStart;
|
||||||
|
Range range;
|
||||||
|
};
|
||||||
|
|
||||||
|
void updateSpacesForTabInfo();
|
||||||
|
bool isValidGlyph(uint32 glyphindex, const std::vector<uint32> &codepoints, uint32 codepointindex);
|
||||||
|
void computeBufferRanges(const ColoredCodepoints &codepoints, Range range, std::vector<BufferRange> &bufferranges);
|
||||||
|
|
||||||
|
std::vector<hb_font_t *> hbFonts;
|
||||||
|
std::vector<hb_buffer_t *> hbBuffers;
|
||||||
|
|
||||||
|
GlyphIndex spaceGlyphIndex;
|
||||||
|
int tabSpacesAdvanceX;
|
||||||
|
int tabSpacesAdvanceY;
|
||||||
|
|
||||||
|
}; // HarfbuzzShaper
|
||||||
|
|
||||||
|
} // freetype
|
||||||
|
} // font
|
||||||
|
} // love
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
// LOVE
|
// LOVE
|
||||||
#include "TrueTypeRasterizer.h"
|
#include "TrueTypeRasterizer.h"
|
||||||
|
#include "HarfbuzzShaper.h"
|
||||||
#include "common/Exception.h"
|
#include "common/Exception.h"
|
||||||
|
|
||||||
// C
|
// C
|
||||||
@@ -75,7 +76,30 @@ int TrueTypeRasterizer::getLineHeight() const
|
|||||||
return (int)(getHeight() * 1.25);
|
return (int)(getHeight() * 1.25);
|
||||||
}
|
}
|
||||||
|
|
||||||
GlyphData *TrueTypeRasterizer::getGlyphData(uint32 glyph) const
|
int TrueTypeRasterizer::getGlyphSpacing(uint32 glyph) const
|
||||||
|
{
|
||||||
|
FT_Glyph ftglyph;
|
||||||
|
FT_Error err = FT_Err_Ok;
|
||||||
|
FT_UInt loadoption = hintingToLoadOption(hinting);
|
||||||
|
|
||||||
|
// Initialize
|
||||||
|
err = FT_Load_Glyph(face, FT_Get_Char_Index(face, glyph), FT_LOAD_DEFAULT | loadoption);
|
||||||
|
if (err != FT_Err_Ok)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err = FT_Get_Glyph(face->glyph, &ftglyph);
|
||||||
|
if (err != FT_Err_Ok)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return (int)(ftglyph->advance.x >> 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
int TrueTypeRasterizer::getGlyphIndex(uint32 glyph) const
|
||||||
|
{
|
||||||
|
return FT_Get_Char_Index(face, glyph);
|
||||||
|
}
|
||||||
|
|
||||||
|
GlyphData *TrueTypeRasterizer::getGlyphDataForIndex(int index) const
|
||||||
{
|
{
|
||||||
love::font::GlyphMetrics glyphMetrics = {};
|
love::font::GlyphMetrics glyphMetrics = {};
|
||||||
FT_Glyph ftglyph;
|
FT_Glyph ftglyph;
|
||||||
@@ -84,7 +108,7 @@ GlyphData *TrueTypeRasterizer::getGlyphData(uint32 glyph) const
|
|||||||
FT_UInt loadoption = hintingToLoadOption(hinting);
|
FT_UInt loadoption = hintingToLoadOption(hinting);
|
||||||
|
|
||||||
// Initialize
|
// Initialize
|
||||||
err = FT_Load_Glyph(face, FT_Get_Char_Index(face, glyph), FT_LOAD_DEFAULT | loadoption);
|
err = FT_Load_Glyph(face, index, FT_LOAD_DEFAULT | loadoption);
|
||||||
|
|
||||||
if (err != FT_Err_Ok)
|
if (err != FT_Err_Ok)
|
||||||
throw love::Exception("TrueType Font glyph error: FT_Load_Glyph failed (0x%x)", err);
|
throw love::Exception("TrueType Font glyph error: FT_Load_Glyph failed (0x%x)", err);
|
||||||
@@ -104,7 +128,7 @@ GlyphData *TrueTypeRasterizer::getGlyphData(uint32 glyph) const
|
|||||||
throw love::Exception("TrueType Font glyph error: FT_Glyph_To_Bitmap failed (0x%x)", err);
|
throw love::Exception("TrueType Font glyph error: FT_Glyph_To_Bitmap failed (0x%x)", err);
|
||||||
|
|
||||||
FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph) ftglyph;
|
FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph) ftglyph;
|
||||||
FT_Bitmap &bitmap = bitmap_glyph->bitmap; //just to make things easier
|
const FT_Bitmap &bitmap = bitmap_glyph->bitmap; //just to make things easier
|
||||||
|
|
||||||
// Get metrics
|
// Get metrics
|
||||||
glyphMetrics.bearingX = bitmap_glyph->left;
|
glyphMetrics.bearingX = bitmap_glyph->left;
|
||||||
@@ -113,7 +137,8 @@ GlyphData *TrueTypeRasterizer::getGlyphData(uint32 glyph) const
|
|||||||
glyphMetrics.width = bitmap.width;
|
glyphMetrics.width = bitmap.width;
|
||||||
glyphMetrics.advance = (int) (ftglyph->advance.x >> 16);
|
glyphMetrics.advance = (int) (ftglyph->advance.x >> 16);
|
||||||
|
|
||||||
GlyphData *glyphData = new GlyphData(glyph, glyphMetrics, PIXELFORMAT_LA8_UNORM);
|
// TODO: https://stackoverflow.com/questions/60526004/how-to-get-glyph-unicode-using-freetype/69730502#69730502
|
||||||
|
GlyphData *glyphData = new GlyphData(0, glyphMetrics, PIXELFORMAT_LA8_UNORM);
|
||||||
|
|
||||||
const uint8 *pixels = bitmap.buffer;
|
const uint8 *pixels = bitmap.buffer;
|
||||||
uint8 *dest = (uint8 *) glyphData->getData();
|
uint8 *dest = (uint8 *) glyphData->getData();
|
||||||
@@ -186,6 +211,11 @@ Rasterizer::DataType TrueTypeRasterizer::getDataType() const
|
|||||||
return DATA_TRUETYPE;
|
return DATA_TRUETYPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextShaper *TrueTypeRasterizer::newTextShaper()
|
||||||
|
{
|
||||||
|
return new HarfbuzzShaper(this);
|
||||||
|
}
|
||||||
|
|
||||||
bool TrueTypeRasterizer::accepts(FT_Library library, love::Data *data)
|
bool TrueTypeRasterizer::accepts(FT_Library library, love::Data *data)
|
||||||
{
|
{
|
||||||
const FT_Byte *fbase = (const FT_Byte *) data->getData();
|
const FT_Byte *fbase = (const FT_Byte *) data->getData();
|
||||||
|
|||||||
@@ -49,11 +49,16 @@ public:
|
|||||||
|
|
||||||
// Implement Rasterizer
|
// Implement Rasterizer
|
||||||
int getLineHeight() const override;
|
int getLineHeight() const override;
|
||||||
GlyphData *getGlyphData(uint32 glyph) const override;
|
int getGlyphSpacing(uint32 glyph) const override;
|
||||||
|
int getGlyphIndex(uint32 glyph) const override;
|
||||||
|
GlyphData *getGlyphDataForIndex(int index) const override;
|
||||||
int getGlyphCount() const override;
|
int getGlyphCount() const override;
|
||||||
bool hasGlyph(uint32 glyph) const override;
|
bool hasGlyph(uint32 glyph) const override;
|
||||||
float getKerning(uint32 leftglyph, uint32 rightglyph) const override;
|
float getKerning(uint32 leftglyph, uint32 rightglyph) const override;
|
||||||
DataType getDataType() const override;
|
DataType getDataType() const override;
|
||||||
|
TextShaper *newTextShaper() override;
|
||||||
|
|
||||||
|
ptrdiff_t getHandle() const override { return (ptrdiff_t) face; }
|
||||||
|
|
||||||
static bool accepts(FT_Library library, love::Data *data);
|
static bool accepts(FT_Library library, love::Data *data);
|
||||||
|
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ void Deprecations::draw(Graphics *gfx)
|
|||||||
int maxcount = 4;
|
int maxcount = 4;
|
||||||
int remaining = std::max(0, total - maxcount);
|
int remaining = std::max(0, total - maxcount);
|
||||||
|
|
||||||
std::vector<Font::ColoredString> strings;
|
std::vector<font::ColoredString> strings;
|
||||||
Colorf white(1, 1, 1, 1);
|
Colorf white(1, 1, 1, 1);
|
||||||
|
|
||||||
// Grab the newest deprecation notices first.
|
// Grab the newest deprecation notices first.
|
||||||
|
|||||||
+114
-501
@@ -21,8 +21,6 @@
|
|||||||
#include "Font.h"
|
#include "Font.h"
|
||||||
#include "font/GlyphData.h"
|
#include "font/GlyphData.h"
|
||||||
|
|
||||||
#include "libraries/utf8/utf8.h"
|
|
||||||
|
|
||||||
#include "common/math.h"
|
#include "common/math.h"
|
||||||
#include "common/Matrix.h"
|
#include "common/Matrix.h"
|
||||||
#include "Graphics.h"
|
#include "Graphics.h"
|
||||||
@@ -42,20 +40,27 @@ static inline uint16 normToUint16(double n)
|
|||||||
return (uint16) (n * LOVE_UINT16_MAX);
|
return (uint16) (n * LOVE_UINT16_MAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline uint64 packGlyphIndex(love::font::TextShaper::GlyphIndex glyphindex)
|
||||||
|
{
|
||||||
|
return ((uint64)glyphindex.rasterizerIndex << 32) | (uint64)glyphindex.index;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline love::font::TextShaper::GlyphIndex unpackGlyphIndex(uint64 packedindex)
|
||||||
|
{
|
||||||
|
return {(int) (packedindex & 0xFFFFFFFF), (int) (packedindex >> 32)};
|
||||||
|
}
|
||||||
|
|
||||||
love::Type Font::type("Font", &Object::type);
|
love::Type Font::type("Font", &Object::type);
|
||||||
int Font::fontCount = 0;
|
int Font::fontCount = 0;
|
||||||
|
|
||||||
const CommonFormat Font::vertexFormat = CommonFormat::XYf_STus_RGBAub;
|
const CommonFormat Font::vertexFormat = CommonFormat::XYf_STus_RGBAub;
|
||||||
|
|
||||||
Font::Font(love::font::Rasterizer *r, const SamplerState &s)
|
Font::Font(love::font::Rasterizer *r, const SamplerState &s)
|
||||||
: rasterizers({r})
|
: shaper(r->newTextShaper(), Acquire::NORETAIN)
|
||||||
, height(r->getHeight())
|
|
||||||
, lineHeight(1)
|
|
||||||
, textureWidth(128)
|
, textureWidth(128)
|
||||||
, textureHeight(128)
|
, textureHeight(128)
|
||||||
, samplerState()
|
, samplerState()
|
||||||
, dpiScale(r->getDPIScale())
|
, dpiScale(r->getDPIScale())
|
||||||
, useSpacesAsTab(false)
|
|
||||||
, textureCacheID(0)
|
, textureCacheID(0)
|
||||||
{
|
{
|
||||||
samplerState.minFilter = s.minFilter;
|
samplerState.minFilter = s.minFilter;
|
||||||
@@ -66,7 +71,7 @@ Font::Font(love::font::Rasterizer *r, const SamplerState &s)
|
|||||||
// largest texture size if no rough match is found.
|
// largest texture size if no rough match is found.
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
if ((height * 0.8) * height * 30 <= textureWidth * textureHeight)
|
if ((shaper->getHeight() * 0.8) * shaper->getHeight() * 30 <= textureWidth * textureHeight)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
TextureSize nextsize = getNextTextureSize();
|
TextureSize nextsize = getNextTextureSize();
|
||||||
@@ -86,9 +91,6 @@ Font::Font(love::font::Rasterizer *r, const SamplerState &s)
|
|||||||
if (pixelFormat == PIXELFORMAT_LA8_UNORM && !gfx->isPixelFormatSupported(pixelFormat, PIXELFORMATUSAGEFLAGS_SAMPLE))
|
if (pixelFormat == PIXELFORMAT_LA8_UNORM && !gfx->isPixelFormatSupported(pixelFormat, PIXELFORMATUSAGEFLAGS_SAMPLE))
|
||||||
pixelFormat = PIXELFORMAT_RGBA8_UNORM;
|
pixelFormat = PIXELFORMAT_RGBA8_UNORM;
|
||||||
|
|
||||||
if (!r->hasGlyph(9)) // No tab character in the Rasterizer.
|
|
||||||
useSpacesAsTab = true;
|
|
||||||
|
|
||||||
loadVolatile();
|
loadVolatile();
|
||||||
++fontCount;
|
++fontCount;
|
||||||
}
|
}
|
||||||
@@ -170,7 +172,7 @@ void Font::createTexture()
|
|||||||
// and transparent black otherwise.
|
// and transparent black otherwise.
|
||||||
std::vector<uint8> emptydata(datasize, 0);
|
std::vector<uint8> emptydata(datasize, 0);
|
||||||
|
|
||||||
if (rasterizers[0]->getDataType() == font::Rasterizer::DATA_TRUETYPE)
|
if (shaper->getRasterizers()[0]->getDataType() == font::Rasterizer::DATA_TRUETYPE)
|
||||||
{
|
{
|
||||||
if (pixelFormat == PIXELFORMAT_LA8_UNORM)
|
if (pixelFormat == PIXELFORMAT_LA8_UNORM)
|
||||||
{
|
{
|
||||||
@@ -204,15 +206,15 @@ void Font::createTexture()
|
|||||||
{
|
{
|
||||||
textureCacheID++;
|
textureCacheID++;
|
||||||
|
|
||||||
std::vector<uint32> glyphstoadd;
|
std::vector<love::font::TextShaper::GlyphIndex> glyphstoadd;
|
||||||
|
|
||||||
for (const auto &glyphpair : glyphs)
|
for (const auto &glyphpair : glyphs)
|
||||||
glyphstoadd.push_back(glyphpair.first);
|
glyphstoadd.push_back(unpackGlyphIndex(glyphpair.first));
|
||||||
|
|
||||||
glyphs.clear();
|
glyphs.clear();
|
||||||
|
|
||||||
for (uint32 g : glyphstoadd)
|
for (auto glyphindex : glyphstoadd)
|
||||||
addGlyph(g);
|
addGlyph(glyphindex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,42 +224,17 @@ void Font::unloadVolatile()
|
|||||||
textures.clear();
|
textures.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
love::font::GlyphData *Font::getRasterizerGlyphData(uint32 glyph, float &dpiscale)
|
love::font::GlyphData *Font::getRasterizerGlyphData(love::font::TextShaper::GlyphIndex glyphindex, float &dpiscale)
|
||||||
{
|
{
|
||||||
// Use spaces for the tab 'glyph'.
|
const auto &r = shaper->getRasterizers()[glyphindex.rasterizerIndex];
|
||||||
if (glyph == 9 && useSpacesAsTab)
|
dpiscale = r->getDPIScale();
|
||||||
{
|
return r->getGlyphDataForIndex(glyphindex.index);
|
||||||
love::font::GlyphData *spacegd = rasterizers[0]->getGlyphData(32);
|
|
||||||
PixelFormat fmt = spacegd->getFormat();
|
|
||||||
|
|
||||||
love::font::GlyphMetrics gm = {};
|
|
||||||
gm.advance = spacegd->getAdvance() * SPACES_PER_TAB;
|
|
||||||
gm.bearingX = spacegd->getBearingX();
|
|
||||||
gm.bearingY = spacegd->getBearingY();
|
|
||||||
|
|
||||||
spacegd->release();
|
|
||||||
|
|
||||||
dpiscale = rasterizers[0]->getDPIScale();
|
|
||||||
return new love::font::GlyphData(glyph, gm, fmt);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const StrongRef<love::font::Rasterizer> &r : rasterizers)
|
|
||||||
{
|
|
||||||
if (r->hasGlyph(glyph))
|
|
||||||
{
|
|
||||||
dpiscale = r->getDPIScale();
|
|
||||||
return r->getGlyphData(glyph);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dpiscale = rasterizers[0]->getDPIScale();
|
|
||||||
return rasterizers[0]->getGlyphData(glyph);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const Font::Glyph &Font::addGlyph(uint32 glyph)
|
const Font::Glyph &Font::addGlyph(love::font::TextShaper::GlyphIndex glyphindex)
|
||||||
{
|
{
|
||||||
float glyphdpiscale = getDPIScale();
|
float glyphdpiscale = getDPIScale();
|
||||||
StrongRef<love::font::GlyphData> gd(getRasterizerGlyphData(glyph, glyphdpiscale), Acquire::NORETAIN);
|
StrongRef<love::font::GlyphData> gd(getRasterizerGlyphData(glyphindex, glyphdpiscale), Acquire::NORETAIN);
|
||||||
|
|
||||||
int w = gd->getWidth();
|
int w = gd->getWidth();
|
||||||
int h = gd->getHeight();
|
int h = gd->getHeight();
|
||||||
@@ -279,15 +256,13 @@ const Font::Glyph &Font::addGlyph(uint32 glyph)
|
|||||||
|
|
||||||
// Makes sure the above code for checking if the glyph can fit at
|
// Makes sure the above code for checking if the glyph can fit at
|
||||||
// the current position in the texture is run again for this glyph.
|
// the current position in the texture is run again for this glyph.
|
||||||
return addGlyph(glyph);
|
return addGlyph(glyphindex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Glyph g;
|
Glyph g;
|
||||||
|
|
||||||
g.texture = 0;
|
g.texture = nullptr;
|
||||||
g.spacing = floorf(gd->getAdvance() / glyphdpiscale + 0.5f);
|
|
||||||
|
|
||||||
memset(g.vertices, 0, sizeof(GlyphVertex) * 4);
|
memset(g.vertices, 0, sizeof(GlyphVertex) * 4);
|
||||||
|
|
||||||
// Don't waste space for empty glyphs.
|
// Don't waste space for empty glyphs.
|
||||||
@@ -357,151 +332,77 @@ const Font::Glyph &Font::addGlyph(uint32 glyph)
|
|||||||
rowHeight = std::max(rowHeight, h + TEXTURE_PADDING);
|
rowHeight = std::max(rowHeight, h + TEXTURE_PADDING);
|
||||||
}
|
}
|
||||||
|
|
||||||
glyphs[glyph] = g;
|
uint64 packedindex = packGlyphIndex(glyphindex);
|
||||||
return glyphs[glyph];
|
glyphs[packedindex] = g;
|
||||||
|
return glyphs[packedindex];
|
||||||
}
|
}
|
||||||
|
|
||||||
const Font::Glyph &Font::findGlyph(uint32 glyph)
|
const Font::Glyph &Font::findGlyph(love::font::TextShaper::GlyphIndex glyphindex)
|
||||||
{
|
{
|
||||||
const auto it = glyphs.find(glyph);
|
uint64 packedindex = packGlyphIndex(glyphindex);
|
||||||
|
const auto it = glyphs.find(packedindex);
|
||||||
|
|
||||||
if (it != glyphs.end())
|
if (it != glyphs.end())
|
||||||
return it->second;
|
return it->second;
|
||||||
|
|
||||||
return addGlyph(glyph);
|
return addGlyph(glyphindex);
|
||||||
}
|
}
|
||||||
|
|
||||||
float Font::getKerning(uint32 leftglyph, uint32 rightglyph)
|
float Font::getKerning(uint32 leftglyph, uint32 rightglyph)
|
||||||
{
|
{
|
||||||
uint64 packedglyphs = ((uint64) leftglyph << 32) | (uint64) rightglyph;
|
return shaper->getKerning(leftglyph, rightglyph);
|
||||||
|
|
||||||
const auto it = kerning.find(packedglyphs);
|
|
||||||
if (it != kerning.end())
|
|
||||||
return it->second;
|
|
||||||
|
|
||||||
float k = floorf(rasterizers[0]->getKerning(leftglyph, rightglyph) / dpiScale + 0.5f);
|
|
||||||
|
|
||||||
for (const auto &r : rasterizers)
|
|
||||||
{
|
|
||||||
if (r->hasGlyph(leftglyph) && r->hasGlyph(rightglyph))
|
|
||||||
{
|
|
||||||
k = floorf(r->getKerning(leftglyph, rightglyph) / r->getDPIScale() + 0.5f);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
kerning[packedglyphs] = k;
|
|
||||||
return k;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float Font::getKerning(const std::string &leftchar, const std::string &rightchar)
|
float Font::getKerning(const std::string &leftchar, const std::string &rightchar)
|
||||||
{
|
{
|
||||||
uint32 left = 0;
|
return shaper->getKerning(leftchar, rightchar);
|
||||||
uint32 right = 0;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
left = utf8::peek_next(leftchar.begin(), leftchar.end());
|
|
||||||
right = utf8::peek_next(rightchar.begin(), rightchar.end());
|
|
||||||
}
|
|
||||||
catch (utf8::exception &e)
|
|
||||||
{
|
|
||||||
throw love::Exception("UTF-8 decoding error: %s", e.what());
|
|
||||||
}
|
|
||||||
|
|
||||||
return getKerning(left, right);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Font::getCodepointsFromString(const std::string &text, Codepoints &codepoints)
|
|
||||||
{
|
|
||||||
codepoints.reserve(text.size());
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
utf8::iterator<std::string::const_iterator> i(text.begin(), text.begin(), text.end());
|
|
||||||
utf8::iterator<std::string::const_iterator> end(text.end(), text.begin(), text.end());
|
|
||||||
|
|
||||||
while (i != end)
|
|
||||||
{
|
|
||||||
uint32 g = *i++;
|
|
||||||
codepoints.push_back(g);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (utf8::exception &e)
|
|
||||||
{
|
|
||||||
throw love::Exception("UTF-8 decoding error: %s", e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Font::getCodepointsFromString(const std::vector<ColoredString> &strs, ColoredCodepoints &codepoints)
|
|
||||||
{
|
|
||||||
if (strs.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
codepoints.cps.reserve(strs[0].str.size());
|
|
||||||
|
|
||||||
for (const ColoredString &cstr : strs)
|
|
||||||
{
|
|
||||||
// No need to add the color if the string is empty anyway, and the code
|
|
||||||
// further on assumes no two colors share the same starting position.
|
|
||||||
if (cstr.str.size() == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
IndexedColor c = {cstr.color, (int) codepoints.cps.size()};
|
|
||||||
codepoints.colors.push_back(c);
|
|
||||||
|
|
||||||
getCodepointsFromString(cstr.str, codepoints.cps);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (codepoints.colors.size() == 1)
|
|
||||||
{
|
|
||||||
IndexedColor c = codepoints.colors[0];
|
|
||||||
|
|
||||||
if (c.index == 0 && c.color == Colorf(1.0f, 1.0f, 1.0f, 1.0f))
|
|
||||||
codepoints.colors.pop_back();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float Font::getHeight() const
|
float Font::getHeight() const
|
||||||
{
|
{
|
||||||
return (float) floorf(height / dpiScale + 0.5f);
|
return shaper->getHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Font::DrawCommand> Font::generateVertices(const ColoredCodepoints &codepoints, const Colorf &constantcolor, std::vector<GlyphVertex> &vertices, float extra_spacing, Vector2 offset, TextInfo *info)
|
std::vector<Font::DrawCommand> Font::generateVertices(const love::font::ColoredCodepoints &codepoints, Range range, const Colorf &constantcolor, std::vector<GlyphVertex> &vertices, float extra_spacing, Vector2 offset, love::font::TextShaper::TextInfo *info)
|
||||||
{
|
{
|
||||||
// Spacing counter and newline handling.
|
std::vector<love::font::TextShaper::GlyphPosition> glyphpositions;
|
||||||
float dx = offset.x;
|
std::vector<love::font::IndexedColor> colors;
|
||||||
float dy = offset.y;
|
shaper->computeGlyphPositions(codepoints, range, offset, extra_spacing, &glyphpositions, &colors, info);
|
||||||
|
|
||||||
float heightoffset = 0.0f;
|
size_t vertstartsize = vertices.size();
|
||||||
|
vertices.reserve(vertstartsize + glyphpositions.size() * 4);
|
||||||
|
|
||||||
if (rasterizers[0]->getDataType() == font::Rasterizer::DATA_TRUETYPE)
|
Colorf linearconstantcolor = gammaCorrectColor(constantcolor);
|
||||||
heightoffset = getBaseline();
|
Color32 curcolor = toColor32(constantcolor);
|
||||||
|
|
||||||
int maxwidth = 0;
|
int curcolori = 0;
|
||||||
|
int ncolors = (int)colors.size();
|
||||||
|
|
||||||
// Keeps track of when we need to switch textures in our vertex array.
|
// Keeps track of when we need to switch textures in our vertex array.
|
||||||
std::vector<DrawCommand> commands;
|
std::vector<DrawCommand> commands;
|
||||||
|
|
||||||
// Pre-allocate space for the maximum possible number of vertices.
|
for (int i = 0; i < (int) glyphpositions.size(); i++)
|
||||||
size_t vertstartsize = vertices.size();
|
|
||||||
vertices.reserve(vertstartsize + codepoints.cps.size() * 4);
|
|
||||||
|
|
||||||
uint32 prevglyph = 0;
|
|
||||||
|
|
||||||
Colorf linearconstantcolor = gammaCorrectColor(constantcolor);
|
|
||||||
|
|
||||||
Color32 curcolor = toColor32(constantcolor);
|
|
||||||
int curcolori = -1;
|
|
||||||
int ncolors = (int) codepoints.colors.size();
|
|
||||||
|
|
||||||
for (int i = 0; i < (int) codepoints.cps.size(); i++)
|
|
||||||
{
|
{
|
||||||
uint32 g = codepoints.cps[i];
|
const auto &info = glyphpositions[i];
|
||||||
|
|
||||||
if (curcolori + 1 < ncolors && codepoints.colors[curcolori + 1].index == i)
|
uint32 cacheid = textureCacheID;
|
||||||
|
|
||||||
|
const Glyph &glyph = findGlyph(info.glyphIndex);
|
||||||
|
|
||||||
|
// If findGlyph invalidates the texture cache, restart the loop.
|
||||||
|
if (cacheid != textureCacheID)
|
||||||
{
|
{
|
||||||
Colorf c = codepoints.colors[++curcolori].color;
|
i = -1; // The next iteration will increment this to 0.
|
||||||
|
commands.clear();
|
||||||
|
vertices.resize(vertstartsize);
|
||||||
|
curcolori = 0;
|
||||||
|
curcolor = toColor32(constantcolor);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (curcolori < ncolors && colors[curcolori].index == i)
|
||||||
|
{
|
||||||
|
Colorf c = colors[curcolori].color;
|
||||||
|
|
||||||
c.r = std::min(std::max(c.r, 0.0f), 1.0f);
|
c.r = std::min(std::max(c.r, 0.0f), 1.0f);
|
||||||
c.g = std::min(std::max(c.g, 0.0f), 1.0f);
|
c.g = std::min(std::max(c.g, 0.0f), 1.0f);
|
||||||
@@ -513,54 +414,17 @@ std::vector<Font::DrawCommand> Font::generateVertices(const ColoredCodepoints &c
|
|||||||
unGammaCorrectColor(c);
|
unGammaCorrectColor(c);
|
||||||
|
|
||||||
curcolor = toColor32(c);
|
curcolor = toColor32(c);
|
||||||
|
curcolori++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g == '\n')
|
|
||||||
{
|
|
||||||
if (dx > maxwidth)
|
|
||||||
maxwidth = (int) dx;
|
|
||||||
|
|
||||||
// Wrap newline, but do not print it.
|
|
||||||
dy += floorf(getHeight() * getLineHeight() + 0.5f);
|
|
||||||
dx = offset.x;
|
|
||||||
prevglyph = 0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore carriage returns
|
|
||||||
if (g == '\r')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
uint32 cacheid = textureCacheID;
|
|
||||||
|
|
||||||
const Glyph &glyph = findGlyph(g);
|
|
||||||
|
|
||||||
// If findGlyph invalidates the texture cache, re-start the loop.
|
|
||||||
if (cacheid != textureCacheID)
|
|
||||||
{
|
|
||||||
i = -1; // The next iteration will increment this to 0.
|
|
||||||
maxwidth = 0;
|
|
||||||
dx = offset.x;
|
|
||||||
dy = offset.y;
|
|
||||||
commands.clear();
|
|
||||||
vertices.resize(vertstartsize);
|
|
||||||
prevglyph = 0;
|
|
||||||
curcolori = -1;
|
|
||||||
curcolor = toColor32(constantcolor);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add kerning to the current horizontal offset.
|
|
||||||
dx += getKerning(prevglyph, g);
|
|
||||||
|
|
||||||
if (glyph.texture != nullptr)
|
if (glyph.texture != nullptr)
|
||||||
{
|
{
|
||||||
// Copy the vertices and set their colors and relative positions.
|
// Copy the vertices and set their colors and relative positions.
|
||||||
for (int j = 0; j < 4; j++)
|
for (int j = 0; j < 4; j++)
|
||||||
{
|
{
|
||||||
vertices.push_back(glyph.vertices[j]);
|
vertices.push_back(glyph.vertices[j]);
|
||||||
vertices.back().x += dx;
|
vertices.back().x += info.position.x;
|
||||||
vertices.back().y += dy + heightoffset;
|
vertices.back().y += info.position.y;
|
||||||
vertices.back().color = curcolor;
|
vertices.back().color = curcolor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -569,7 +433,7 @@ std::vector<Font::DrawCommand> Font::generateVertices(const ColoredCodepoints &c
|
|||||||
{
|
{
|
||||||
// Add a new draw command if the texture has changed.
|
// Add a new draw command if the texture has changed.
|
||||||
DrawCommand cmd;
|
DrawCommand cmd;
|
||||||
cmd.startvertex = (int) vertices.size() - 4;
|
cmd.startvertex = (int)vertices.size() - 4;
|
||||||
cmd.vertexcount = 0;
|
cmd.vertexcount = 0;
|
||||||
cmd.texture = glyph.texture;
|
cmd.texture = glyph.texture;
|
||||||
commands.push_back(cmd);
|
commands.push_back(cmd);
|
||||||
@@ -577,15 +441,6 @@ std::vector<Font::DrawCommand> Font::generateVertices(const ColoredCodepoints &c
|
|||||||
|
|
||||||
commands.back().vertexcount += 4;
|
commands.back().vertexcount += 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Advance the x position for the next glyph.
|
|
||||||
dx += glyph.spacing;
|
|
||||||
|
|
||||||
// Account for extra spacing given to space characters.
|
|
||||||
if (g == ' ' && extra_spacing != 0.0f)
|
|
||||||
dx = floorf(dx + extra_spacing);
|
|
||||||
|
|
||||||
prevglyph = g;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto drawsort = [](const DrawCommand &a, const DrawCommand &b) -> bool
|
const auto drawsort = [](const DrawCommand &a, const DrawCommand &b) -> bool
|
||||||
@@ -599,19 +454,10 @@ std::vector<Font::DrawCommand> Font::generateVertices(const ColoredCodepoints &c
|
|||||||
|
|
||||||
std::sort(commands.begin(), commands.end(), drawsort);
|
std::sort(commands.begin(), commands.end(), drawsort);
|
||||||
|
|
||||||
if (dx > maxwidth)
|
|
||||||
maxwidth = (int) dx;
|
|
||||||
|
|
||||||
if (info != nullptr)
|
|
||||||
{
|
|
||||||
info->width = maxwidth - offset.x;
|
|
||||||
info->height = (int) dy + (dx > 0.0f ? floorf(getHeight() * getLineHeight() + 0.5f) : 0) - offset.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
return commands;
|
return commands;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Font::DrawCommand> Font::generateVerticesFormatted(const ColoredCodepoints &text, const Colorf &constantcolor, float wrap, AlignMode align, std::vector<GlyphVertex> &vertices, TextInfo *info)
|
std::vector<Font::DrawCommand> Font::generateVerticesFormatted(const love::font::ColoredCodepoints &text, const Colorf &constantcolor, float wrap, AlignMode align, std::vector<GlyphVertex> &vertices, love::font::TextShaper::TextInfo *info)
|
||||||
{
|
{
|
||||||
wrap = std::max(wrap, 0.0f);
|
wrap = std::max(wrap, 0.0f);
|
||||||
|
|
||||||
@@ -620,17 +466,22 @@ std::vector<Font::DrawCommand> Font::generateVerticesFormatted(const ColoredCode
|
|||||||
std::vector<DrawCommand> drawcommands;
|
std::vector<DrawCommand> drawcommands;
|
||||||
vertices.reserve(text.cps.size() * 4);
|
vertices.reserve(text.cps.size() * 4);
|
||||||
|
|
||||||
|
std::vector<Range> ranges;
|
||||||
std::vector<int> widths;
|
std::vector<int> widths;
|
||||||
std::vector<ColoredCodepoints> lines;
|
shaper->getWrap(text, wrap, ranges, &widths);
|
||||||
|
|
||||||
getWrap(text, wrap, lines, &widths);
|
|
||||||
|
|
||||||
float y = 0.0f;
|
float y = 0.0f;
|
||||||
float maxwidth = 0.0f;
|
float maxwidth = 0.0f;
|
||||||
|
|
||||||
for (int i = 0; i < (int) lines.size(); i++)
|
for (int i = 0; i < (int)ranges.size(); i++)
|
||||||
{
|
{
|
||||||
const auto &line = lines[i];
|
const auto& range = ranges[i];
|
||||||
|
|
||||||
|
if (!range.isValid())
|
||||||
|
{
|
||||||
|
y += getHeight() * getLineHeight();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
float width = (float) widths[i];
|
float width = (float) widths[i];
|
||||||
love::Vector2 offset(0.0f, floorf(y));
|
love::Vector2 offset(0.0f, floorf(y));
|
||||||
@@ -648,7 +499,9 @@ std::vector<Font::DrawCommand> Font::generateVerticesFormatted(const ColoredCode
|
|||||||
break;
|
break;
|
||||||
case ALIGN_JUSTIFY:
|
case ALIGN_JUSTIFY:
|
||||||
{
|
{
|
||||||
float numspaces = (float) std::count(line.cps.begin(), line.cps.end(), ' ');
|
auto start = text.cps.begin() + range.getOffset();
|
||||||
|
auto end = start + range.getSize();
|
||||||
|
float numspaces = std::count(start, end, ' ');
|
||||||
if (width < wrap && numspaces >= 1)
|
if (width < wrap && numspaces >= 1)
|
||||||
extraspacing = (wrap - width) / numspaces;
|
extraspacing = (wrap - width) / numspaces;
|
||||||
else
|
else
|
||||||
@@ -660,7 +513,7 @@ std::vector<Font::DrawCommand> Font::generateVerticesFormatted(const ColoredCode
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<DrawCommand> newcommands = generateVertices(line, constantcolor, vertices, extraspacing, offset);
|
std::vector<DrawCommand> newcommands = generateVertices(text, range, constantcolor, vertices, extraspacing, offset);
|
||||||
|
|
||||||
if (!newcommands.empty())
|
if (!newcommands.empty())
|
||||||
{
|
{
|
||||||
@@ -724,21 +577,21 @@ void Font::printv(graphics::Graphics *gfx, const Matrix4 &t, const std::vector<D
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Font::print(graphics::Graphics *gfx, const std::vector<ColoredString> &text, const Matrix4 &m, const Colorf &constantcolor)
|
void Font::print(graphics::Graphics *gfx, const std::vector<love::font::ColoredString> &text, const Matrix4 &m, const Colorf &constantcolor)
|
||||||
{
|
{
|
||||||
ColoredCodepoints codepoints;
|
love::font::ColoredCodepoints codepoints;
|
||||||
getCodepointsFromString(text, codepoints);
|
love::font::getCodepointsFromString(text, codepoints);
|
||||||
|
|
||||||
std::vector<GlyphVertex> vertices;
|
std::vector<GlyphVertex> vertices;
|
||||||
std::vector<DrawCommand> drawcommands = generateVertices(codepoints, constantcolor, vertices);
|
std::vector<DrawCommand> drawcommands = generateVertices(codepoints, Range(), constantcolor, vertices);
|
||||||
|
|
||||||
printv(gfx, m, drawcommands, vertices);
|
printv(gfx, m, drawcommands, vertices);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Font::printf(graphics::Graphics *gfx, const std::vector<ColoredString> &text, float wrap, AlignMode align, const Matrix4 &m, const Colorf &constantcolor)
|
void Font::printf(graphics::Graphics *gfx, const std::vector<love::font::ColoredString> &text, float wrap, AlignMode align, const Matrix4 &m, const Colorf &constantcolor)
|
||||||
{
|
{
|
||||||
ColoredCodepoints codepoints;
|
love::font::ColoredCodepoints codepoints;
|
||||||
getCodepointsFromString(text, codepoints);
|
love::font::getCodepointsFromString(text, codepoints);
|
||||||
|
|
||||||
std::vector<GlyphVertex> vertices;
|
std::vector<GlyphVertex> vertices;
|
||||||
std::vector<DrawCommand> drawcommands = generateVerticesFormatted(codepoints, constantcolor, wrap, align, vertices);
|
std::vector<DrawCommand> drawcommands = generateVerticesFormatted(codepoints, constantcolor, wrap, align, vertices);
|
||||||
@@ -748,241 +601,32 @@ void Font::printf(graphics::Graphics *gfx, const std::vector<ColoredString> &tex
|
|||||||
|
|
||||||
int Font::getWidth(const std::string &str)
|
int Font::getWidth(const std::string &str)
|
||||||
{
|
{
|
||||||
if (str.size() == 0) return 0;
|
return shaper->getWidth(str);
|
||||||
|
|
||||||
std::istringstream iss(str);
|
|
||||||
std::string line;
|
|
||||||
int max_width = 0;
|
|
||||||
|
|
||||||
while (getline(iss, line, '\n'))
|
|
||||||
{
|
|
||||||
int width = 0;
|
|
||||||
uint32 prevglyph = 0;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
utf8::iterator<std::string::const_iterator> i(line.begin(), line.begin(), line.end());
|
|
||||||
utf8::iterator<std::string::const_iterator> end(line.end(), line.begin(), line.end());
|
|
||||||
|
|
||||||
while (i != end)
|
|
||||||
{
|
|
||||||
uint32 c = *i++;
|
|
||||||
|
|
||||||
// Ignore carriage returns
|
|
||||||
if (c == '\r')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const Glyph &g = findGlyph(c);
|
|
||||||
width += g.spacing + getKerning(prevglyph, c);
|
|
||||||
|
|
||||||
prevglyph = c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (utf8::exception &e)
|
|
||||||
{
|
|
||||||
throw love::Exception("UTF-8 decoding error: %s", e.what());
|
|
||||||
}
|
|
||||||
|
|
||||||
max_width = std::max(max_width, width);
|
|
||||||
}
|
|
||||||
|
|
||||||
return max_width;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int Font::getWidth(uint32 glyph)
|
int Font::getWidth(uint32 glyph)
|
||||||
{
|
{
|
||||||
const Glyph &g = findGlyph(glyph);
|
return shaper->getGlyphAdvance(glyph);
|
||||||
return g.spacing;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Font::getWrap(const ColoredCodepoints &codepoints, float wraplimit, std::vector<ColoredCodepoints> &lines, std::vector<int> *linewidths)
|
void Font::getWrap(const love::font::ColoredCodepoints &codepoints, float wraplimit, std::vector<Range> &ranges, std::vector<int> *linewidths)
|
||||||
{
|
{
|
||||||
// Per-line info.
|
shaper->getWrap(codepoints, wraplimit, ranges, linewidths);
|
||||||
float width = 0.0f;
|
|
||||||
float widthbeforelastspace = 0.0f;
|
|
||||||
float widthoftrailingspace = 0.0f;
|
|
||||||
uint32 prevglyph = 0;
|
|
||||||
|
|
||||||
int lastspaceindex = -1;
|
|
||||||
|
|
||||||
// Keeping the indexed colors "in sync" is a bit tricky, since we split
|
|
||||||
// things up and we might skip some glyphs but we don't want to skip any
|
|
||||||
// color which starts at those indices.
|
|
||||||
Colorf curcolor(1.0f, 1.0f, 1.0f, 1.0f);
|
|
||||||
bool addcurcolor = false;
|
|
||||||
int curcolori = -1;
|
|
||||||
int endcolori = (int) codepoints.colors.size() - 1;
|
|
||||||
|
|
||||||
// A wrapped line of text.
|
|
||||||
ColoredCodepoints wline;
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
while (i < (int) codepoints.cps.size())
|
|
||||||
{
|
|
||||||
uint32 c = codepoints.cps[i];
|
|
||||||
|
|
||||||
// Determine the current color before doing anything else, to make sure
|
|
||||||
// it's still applied to future glyphs even if this one is skipped.
|
|
||||||
if (curcolori < endcolori && codepoints.colors[curcolori + 1].index == i)
|
|
||||||
{
|
|
||||||
curcolor = codepoints.colors[curcolori + 1].color;
|
|
||||||
curcolori++;
|
|
||||||
addcurcolor = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Split text at newlines.
|
|
||||||
if (c == '\n')
|
|
||||||
{
|
|
||||||
lines.push_back(wline);
|
|
||||||
|
|
||||||
// Ignore the width of any trailing spaces, for individual lines.
|
|
||||||
if (linewidths)
|
|
||||||
linewidths->push_back(width - widthoftrailingspace);
|
|
||||||
|
|
||||||
// Make sure the new line keeps any color that was set previously.
|
|
||||||
addcurcolor = true;
|
|
||||||
|
|
||||||
width = widthbeforelastspace = widthoftrailingspace = 0.0f;
|
|
||||||
prevglyph = 0; // Reset kerning information.
|
|
||||||
lastspaceindex = -1;
|
|
||||||
wline.cps.clear();
|
|
||||||
wline.colors.clear();
|
|
||||||
i++;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore carriage returns
|
|
||||||
if (c == '\r')
|
|
||||||
{
|
|
||||||
i++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Glyph &g = findGlyph(c);
|
|
||||||
float charwidth = g.spacing + getKerning(prevglyph, c);
|
|
||||||
float newwidth = width + charwidth;
|
|
||||||
|
|
||||||
// Wrap the line if it exceeds the wrap limit. Don't wrap yet if we're
|
|
||||||
// processing a newline character, though.
|
|
||||||
if (c != ' ' && newwidth > wraplimit)
|
|
||||||
{
|
|
||||||
// If this is the first character in the line and it exceeds the
|
|
||||||
// limit, skip it completely.
|
|
||||||
if (wline.cps.empty())
|
|
||||||
i++;
|
|
||||||
else if (lastspaceindex != -1)
|
|
||||||
{
|
|
||||||
// 'Rewind' to the last seen space, if the line has one.
|
|
||||||
// FIXME: This could be more efficient...
|
|
||||||
while (!wline.cps.empty() && wline.cps.back() != ' ')
|
|
||||||
wline.cps.pop_back();
|
|
||||||
|
|
||||||
while (!wline.colors.empty() && wline.colors.back().index >= (int) wline.cps.size())
|
|
||||||
wline.colors.pop_back();
|
|
||||||
|
|
||||||
// Also 'rewind' to the color that the last character is using.
|
|
||||||
for (int colori = curcolori; colori >= 0; colori--)
|
|
||||||
{
|
|
||||||
if (codepoints.colors[colori].index <= lastspaceindex)
|
|
||||||
{
|
|
||||||
curcolor = codepoints.colors[colori].color;
|
|
||||||
curcolori = colori;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore the width of trailing spaces in wrapped lines.
|
|
||||||
width = widthbeforelastspace;
|
|
||||||
|
|
||||||
i = lastspaceindex;
|
|
||||||
i++; // Start the next line after the space.
|
|
||||||
}
|
|
||||||
|
|
||||||
lines.push_back(wline);
|
|
||||||
|
|
||||||
if (linewidths)
|
|
||||||
linewidths->push_back(width);
|
|
||||||
|
|
||||||
addcurcolor = true;
|
|
||||||
|
|
||||||
prevglyph = 0;
|
|
||||||
width = widthbeforelastspace = widthoftrailingspace = 0.0f;
|
|
||||||
wline.cps.clear();
|
|
||||||
wline.colors.clear();
|
|
||||||
lastspaceindex = -1;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prevglyph != ' ' && c == ' ')
|
|
||||||
widthbeforelastspace = width;
|
|
||||||
|
|
||||||
width = newwidth;
|
|
||||||
prevglyph = c;
|
|
||||||
|
|
||||||
if (addcurcolor)
|
|
||||||
{
|
|
||||||
wline.colors.push_back({curcolor, (int) wline.cps.size()});
|
|
||||||
addcurcolor = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
wline.cps.push_back(c);
|
|
||||||
|
|
||||||
// Keep track of the last seen space, so we can "rewind" to it when
|
|
||||||
// wrapping.
|
|
||||||
if (c == ' ')
|
|
||||||
{
|
|
||||||
lastspaceindex = i;
|
|
||||||
widthoftrailingspace += charwidth;
|
|
||||||
}
|
|
||||||
else if (c != '\n')
|
|
||||||
widthoftrailingspace = 0.0f;
|
|
||||||
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Push the last line.
|
|
||||||
lines.push_back(wline);
|
|
||||||
|
|
||||||
// Ignore the width of any trailing spaces, for individual lines.
|
|
||||||
if (linewidths)
|
|
||||||
linewidths->push_back(width - widthoftrailingspace);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Font::getWrap(const std::vector<ColoredString> &text, float wraplimit, std::vector<std::string> &lines, std::vector<int> *linewidths)
|
void Font::getWrap(const std::vector<love::font::ColoredString> &text, float wraplimit, std::vector<std::string> &lines, std::vector<int> *linewidths)
|
||||||
{
|
{
|
||||||
ColoredCodepoints cps;
|
shaper->getWrap(text, wraplimit, lines, linewidths);
|
||||||
getCodepointsFromString(text, cps);
|
|
||||||
|
|
||||||
std::vector<ColoredCodepoints> codepointlines;
|
|
||||||
getWrap(cps, wraplimit, codepointlines, linewidths);
|
|
||||||
|
|
||||||
std::string line;
|
|
||||||
|
|
||||||
for (const ColoredCodepoints &codepoints : codepointlines)
|
|
||||||
{
|
|
||||||
line.clear();
|
|
||||||
line.reserve(codepoints.cps.size());
|
|
||||||
|
|
||||||
for (uint32 codepoint : codepoints.cps)
|
|
||||||
{
|
|
||||||
char character[5] = {'\0'};
|
|
||||||
char *end = utf8::unchecked::append(codepoint, character);
|
|
||||||
line.append(character, end - character);
|
|
||||||
}
|
|
||||||
|
|
||||||
lines.push_back(line);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Font::setLineHeight(float height)
|
void Font::setLineHeight(float height)
|
||||||
{
|
{
|
||||||
lineHeight = height;
|
shaper->setLineHeight(height);
|
||||||
}
|
}
|
||||||
|
|
||||||
float Font::getLineHeight() const
|
float Font::getLineHeight() const
|
||||||
{
|
{
|
||||||
return lineHeight;
|
return shaper->getLineHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Font::setSamplerState(const SamplerState &s)
|
void Font::setSamplerState(const SamplerState &s)
|
||||||
@@ -1002,75 +646,44 @@ const SamplerState &Font::getSamplerState() const
|
|||||||
|
|
||||||
int Font::getAscent() const
|
int Font::getAscent() const
|
||||||
{
|
{
|
||||||
return floorf(rasterizers[0]->getAscent() / dpiScale + 0.5f);
|
return shaper->getAscent();
|
||||||
}
|
}
|
||||||
|
|
||||||
int Font::getDescent() const
|
int Font::getDescent() const
|
||||||
{
|
{
|
||||||
return floorf(rasterizers[0]->getDescent() / dpiScale + 0.5f);
|
return shaper->getDescent();
|
||||||
}
|
}
|
||||||
|
|
||||||
float Font::getBaseline() const
|
float Font::getBaseline() const
|
||||||
{
|
{
|
||||||
float ascent = getAscent();
|
return shaper->getBaseline();
|
||||||
if (ascent != 0.0f)
|
|
||||||
return ascent;
|
|
||||||
else if (rasterizers[0]->getDataType() == font::Rasterizer::DATA_TRUETYPE)
|
|
||||||
return floorf(getHeight() / 1.25f + 0.5f); // 1.25 is magic line height for true type fonts
|
|
||||||
else
|
|
||||||
return 0.0f;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Font::hasGlyph(uint32 glyph) const
|
bool Font::hasGlyph(uint32 glyph) const
|
||||||
{
|
{
|
||||||
for (const StrongRef<love::font::Rasterizer> &r : rasterizers)
|
return shaper->hasGlyph(glyph);
|
||||||
{
|
|
||||||
if (r->hasGlyph(glyph))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Font::hasGlyphs(const std::string &text) const
|
bool Font::hasGlyphs(const std::string &text) const
|
||||||
{
|
{
|
||||||
if (text.size() == 0)
|
return shaper->hasGlyphs(text);
|
||||||
return false;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
utf8::iterator<std::string::const_iterator> i(text.begin(), text.begin(), text.end());
|
|
||||||
utf8::iterator<std::string::const_iterator> end(text.end(), text.begin(), text.end());
|
|
||||||
|
|
||||||
while (i != end)
|
|
||||||
{
|
|
||||||
uint32 codepoint = *i++;
|
|
||||||
|
|
||||||
if (!hasGlyph(codepoint))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (utf8::exception &e)
|
|
||||||
{
|
|
||||||
throw love::Exception("UTF-8 decoding error: %s", e.what());
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Font::setFallbacks(const std::vector<Font *> &fallbacks)
|
void Font::setFallbacks(const std::vector<Font *> &fallbacks)
|
||||||
{
|
{
|
||||||
for (const Font *f : fallbacks)
|
std::vector<love::font::Rasterizer*> rasterizerfallbacks;
|
||||||
{
|
for (const Font* f : fallbacks)
|
||||||
if (f->rasterizers[0]->getDataType() != this->rasterizers[0]->getDataType())
|
rasterizerfallbacks.push_back(f->shaper->getRasterizers()[0]);
|
||||||
throw love::Exception("Font fallbacks must be of the same font type.");
|
|
||||||
}
|
|
||||||
|
|
||||||
rasterizers.resize(1);
|
shaper->setFallbacks(rasterizerfallbacks);
|
||||||
|
|
||||||
// NOTE: this won't invalidate already-rasterized glyphs.
|
// Invalidate existing textures.
|
||||||
for (const Font *f : fallbacks)
|
textureCacheID++;
|
||||||
rasterizers.push_back(f->rasterizers[0]);
|
glyphs.clear();
|
||||||
|
while (textures.size() > 1)
|
||||||
|
textures.pop_back();
|
||||||
|
|
||||||
|
rowHeight = textureX = textureY = TEXTURE_PADDING;
|
||||||
}
|
}
|
||||||
|
|
||||||
float Font::getDPIScale() const
|
float Font::getDPIScale() const
|
||||||
|
|||||||
+16
-54
@@ -33,6 +33,7 @@
|
|||||||
#include "common/Vector.h"
|
#include "common/Vector.h"
|
||||||
|
|
||||||
#include "font/Rasterizer.h"
|
#include "font/Rasterizer.h"
|
||||||
|
#include "font/TextShaper.h"
|
||||||
#include "Texture.h"
|
#include "Texture.h"
|
||||||
#include "vertex.h"
|
#include "vertex.h"
|
||||||
#include "Volatile.h"
|
#include "Volatile.h"
|
||||||
@@ -64,30 +65,6 @@ public:
|
|||||||
ALIGN_MAX_ENUM
|
ALIGN_MAX_ENUM
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ColoredString
|
|
||||||
{
|
|
||||||
std::string str;
|
|
||||||
Colorf color;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct IndexedColor
|
|
||||||
{
|
|
||||||
Colorf color;
|
|
||||||
int index;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ColoredCodepoints
|
|
||||||
{
|
|
||||||
std::vector<uint32> cps;
|
|
||||||
std::vector<IndexedColor> colors;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TextInfo
|
|
||||||
{
|
|
||||||
int width;
|
|
||||||
int height;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Used to determine when to change textures in the generated vertex array.
|
// Used to determine when to change textures in the generated vertex array.
|
||||||
struct DrawCommand
|
struct DrawCommand
|
||||||
{
|
{
|
||||||
@@ -100,20 +77,17 @@ public:
|
|||||||
|
|
||||||
virtual ~Font();
|
virtual ~Font();
|
||||||
|
|
||||||
std::vector<DrawCommand> generateVertices(const ColoredCodepoints &codepoints, const Colorf &constantColor, std::vector<GlyphVertex> &vertices,
|
std::vector<DrawCommand> generateVertices(const love::font::ColoredCodepoints &codepoints, Range range, const Colorf &constantColor, std::vector<GlyphVertex> &vertices,
|
||||||
float extra_spacing = 0.0f, Vector2 offset = {}, TextInfo *info = nullptr);
|
float extra_spacing = 0.0f, Vector2 offset = {}, love::font::TextShaper::TextInfo *info = nullptr);
|
||||||
|
|
||||||
std::vector<DrawCommand> generateVerticesFormatted(const ColoredCodepoints &text, const Colorf &constantColor, float wrap, AlignMode align,
|
std::vector<DrawCommand> generateVerticesFormatted(const love::font::ColoredCodepoints &text, const Colorf &constantColor, float wrap, AlignMode align,
|
||||||
std::vector<GlyphVertex> &vertices, TextInfo *info = nullptr);
|
std::vector<GlyphVertex> &vertices, love::font::TextShaper::TextInfo *info = nullptr);
|
||||||
|
|
||||||
static void getCodepointsFromString(const std::string &str, Codepoints &codepoints);
|
|
||||||
static void getCodepointsFromString(const std::vector<ColoredString> &strs, ColoredCodepoints &codepoints);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws the specified text.
|
* Draws the specified text.
|
||||||
**/
|
**/
|
||||||
void print(graphics::Graphics *gfx, const std::vector<ColoredString> &text, const Matrix4 &m, const Colorf &constantColor);
|
void print(graphics::Graphics *gfx, const std::vector<love::font::ColoredString> &text, const Matrix4 &m, const Colorf &constantColor);
|
||||||
void printf(graphics::Graphics *gfx, const std::vector<ColoredString> &text, float wrap, AlignMode align, const Matrix4 &m, const Colorf &constantColor);
|
void printf(graphics::Graphics *gfx, const std::vector<love::font::ColoredString> &text, float wrap, AlignMode align, const Matrix4 &m, const Colorf &constantColor);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the height of the font.
|
* Returns the height of the font.
|
||||||
@@ -141,8 +115,8 @@ public:
|
|||||||
* @param max_width Optional output of the maximum width
|
* @param max_width Optional output of the maximum width
|
||||||
* Returns a vector with the lines.
|
* Returns a vector with the lines.
|
||||||
**/
|
**/
|
||||||
void getWrap(const std::vector<ColoredString> &text, float wraplimit, std::vector<std::string> &lines, std::vector<int> *line_widths = nullptr);
|
void getWrap(const std::vector<love::font::ColoredString> &text, float wraplimit, std::vector<std::string> &lines, std::vector<int> *line_widths = nullptr);
|
||||||
void getWrap(const ColoredCodepoints &codepoints, float wraplimit, std::vector<ColoredCodepoints> &lines, std::vector<int> *line_widths = nullptr);
|
void getWrap(const love::font::ColoredCodepoints &codepoints, float wraplimit, std::vector<Range> &ranges, std::vector<int> *line_widths = nullptr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the line height (which should be a number to multiply the font size by,
|
* Sets the line height (which should be a number to multiply the font size by,
|
||||||
@@ -191,7 +165,6 @@ private:
|
|||||||
struct Glyph
|
struct Glyph
|
||||||
{
|
{
|
||||||
Texture *texture;
|
Texture *texture;
|
||||||
int spacing;
|
|
||||||
GlyphVertex vertices[4];
|
GlyphVertex vertices[4];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -204,26 +177,20 @@ private:
|
|||||||
void createTexture();
|
void createTexture();
|
||||||
|
|
||||||
TextureSize getNextTextureSize() const;
|
TextureSize getNextTextureSize() const;
|
||||||
love::font::GlyphData *getRasterizerGlyphData(uint32 glyph, float &dpiscale);
|
love::font::GlyphData *getRasterizerGlyphData(love::font::TextShaper::GlyphIndex glyphindex, float &dpiscale);
|
||||||
const Glyph &addGlyph(uint32 glyph);
|
const Glyph &addGlyph(love::font::TextShaper::GlyphIndex glyphindex);
|
||||||
const Glyph &findGlyph(uint32 glyph);
|
const Glyph &findGlyph(love::font::TextShaper::GlyphIndex glyphindex);
|
||||||
void printv(Graphics *gfx, const Matrix4 &t, const std::vector<DrawCommand> &drawcommands, const std::vector<GlyphVertex> &vertices);
|
void printv(Graphics *gfx, const Matrix4 &t, const std::vector<DrawCommand> &drawcommands, const std::vector<GlyphVertex> &vertices);
|
||||||
|
|
||||||
std::vector<StrongRef<love::font::Rasterizer>> rasterizers;
|
StrongRef<love::font::TextShaper> shaper;
|
||||||
|
|
||||||
int height;
|
|
||||||
float lineHeight;
|
|
||||||
|
|
||||||
int textureWidth;
|
int textureWidth;
|
||||||
int textureHeight;
|
int textureHeight;
|
||||||
|
|
||||||
std::vector<StrongRef<love::graphics::Texture>> textures;
|
std::vector<StrongRef<Texture>> textures;
|
||||||
|
|
||||||
// maps glyphs to glyph texture information
|
// maps packed glyph index values to glyph texture information
|
||||||
std::unordered_map<uint32, Glyph> glyphs;
|
std::unordered_map<uint64, Glyph> glyphs;
|
||||||
|
|
||||||
// map of left/right glyph pairs to horizontal kerning.
|
|
||||||
std::unordered_map<uint64, float> kerning;
|
|
||||||
|
|
||||||
PixelFormat pixelFormat;
|
PixelFormat pixelFormat;
|
||||||
|
|
||||||
@@ -234,8 +201,6 @@ private:
|
|||||||
int textureX, textureY;
|
int textureX, textureY;
|
||||||
int rowHeight;
|
int rowHeight;
|
||||||
|
|
||||||
bool useSpacesAsTab;
|
|
||||||
|
|
||||||
// ID which is incremented when the texture cache is invalidated.
|
// ID which is incremented when the texture cache is invalidated.
|
||||||
uint32 textureCacheID;
|
uint32 textureCacheID;
|
||||||
|
|
||||||
@@ -244,9 +209,6 @@ private:
|
|||||||
// use, for edge antialiasing.
|
// use, for edge antialiasing.
|
||||||
static const int TEXTURE_PADDING = 2;
|
static const int TEXTURE_PADDING = 2;
|
||||||
|
|
||||||
// This will be used if the Rasterizer doesn't have a tab character itself.
|
|
||||||
static const int SPACES_PER_TAB = 4;
|
|
||||||
|
|
||||||
static StringMap<AlignMode, ALIGN_MAX_ENUM>::Entry alignModeEntries[];
|
static StringMap<AlignMode, ALIGN_MAX_ENUM>::Entry alignModeEntries[];
|
||||||
static StringMap<AlignMode, ALIGN_MAX_ENUM> alignModes;
|
static StringMap<AlignMode, ALIGN_MAX_ENUM> alignModes;
|
||||||
|
|
||||||
|
|||||||
@@ -439,7 +439,7 @@ Mesh *Graphics::newMesh(const std::vector<Mesh::BufferAttribute> &attributes, Pr
|
|||||||
return new Mesh(attributes, drawmode);
|
return new Mesh(attributes, drawmode);
|
||||||
}
|
}
|
||||||
|
|
||||||
love::graphics::TextBatch *Graphics::newTextBatch(graphics::Font *font, const std::vector<Font::ColoredString> &text)
|
love::graphics::TextBatch *Graphics::newTextBatch(graphics::Font *font, const std::vector<love::font::ColoredString> &text)
|
||||||
{
|
{
|
||||||
return new TextBatch(font, text);
|
return new TextBatch(font, text);
|
||||||
}
|
}
|
||||||
@@ -1893,7 +1893,7 @@ void Graphics::drawShaderVertices(Buffer *indexbuffer, int indexcount, int insta
|
|||||||
draw(cmd);
|
draw(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Graphics::print(const std::vector<Font::ColoredString> &str, const Matrix4 &m)
|
void Graphics::print(const std::vector<love::font::ColoredString> &str, const Matrix4 &m)
|
||||||
{
|
{
|
||||||
checkSetDefaultFont();
|
checkSetDefaultFont();
|
||||||
|
|
||||||
@@ -1901,12 +1901,12 @@ void Graphics::print(const std::vector<Font::ColoredString> &str, const Matrix4
|
|||||||
print(str, states.back().font.get(), m);
|
print(str, states.back().font.get(), m);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Graphics::print(const std::vector<Font::ColoredString> &str, Font *font, const Matrix4 &m)
|
void Graphics::print(const std::vector<love::font::ColoredString> &str, Font *font, const Matrix4 &m)
|
||||||
{
|
{
|
||||||
font->print(this, str, m, states.back().color);
|
font->print(this, str, m, states.back().color);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Graphics::printf(const std::vector<Font::ColoredString> &str, float wrap, Font::AlignMode align, const Matrix4 &m)
|
void Graphics::printf(const std::vector<love::font::ColoredString> &str, float wrap, Font::AlignMode align, const Matrix4 &m)
|
||||||
{
|
{
|
||||||
checkSetDefaultFont();
|
checkSetDefaultFont();
|
||||||
|
|
||||||
@@ -1914,7 +1914,7 @@ void Graphics::printf(const std::vector<Font::ColoredString> &str, float wrap, F
|
|||||||
printf(str, states.back().font.get(), wrap, align, m);
|
printf(str, states.back().font.get(), wrap, align, m);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Graphics::printf(const std::vector<Font::ColoredString> &str, Font *font, float wrap, Font::AlignMode align, const Matrix4 &m)
|
void Graphics::printf(const std::vector<love::font::ColoredString> &str, Font *font, float wrap, Font::AlignMode align, const Matrix4 &m)
|
||||||
{
|
{
|
||||||
font->printf(this, str, wrap, align, m, states.back().color);
|
font->printf(this, str, wrap, align, m, states.back().color);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -460,7 +460,7 @@ public:
|
|||||||
Mesh *newMesh(const std::vector<Buffer::DataDeclaration> &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, BufferDataUsage usage);
|
Mesh *newMesh(const std::vector<Buffer::DataDeclaration> &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, BufferDataUsage usage);
|
||||||
Mesh *newMesh(const std::vector<Mesh::BufferAttribute> &attributes, PrimitiveType drawmode);
|
Mesh *newMesh(const std::vector<Mesh::BufferAttribute> &attributes, PrimitiveType drawmode);
|
||||||
|
|
||||||
TextBatch *newTextBatch(Font *font, const std::vector<Font::ColoredString> &text = {});
|
TextBatch *newTextBatch(Font *font, const std::vector<love::font::ColoredString> &text = {});
|
||||||
|
|
||||||
data::ByteData *readbackBuffer(Buffer *buffer, size_t offset, size_t size, data::ByteData *dest, size_t destoffset);
|
data::ByteData *readbackBuffer(Buffer *buffer, size_t offset, size_t size, data::ByteData *dest, size_t destoffset);
|
||||||
GraphicsReadback *readbackBufferAsync(Buffer *buffer, size_t offset, size_t size, data::ByteData *dest, size_t destoffset);
|
GraphicsReadback *readbackBufferAsync(Buffer *buffer, size_t offset, size_t size, data::ByteData *dest, size_t destoffset);
|
||||||
@@ -702,14 +702,14 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Draws text at the specified coordinates
|
* Draws text at the specified coordinates
|
||||||
**/
|
**/
|
||||||
void print(const std::vector<Font::ColoredString> &str, const Matrix4 &m);
|
void print(const std::vector<love::font::ColoredString> &str, const Matrix4 &m);
|
||||||
void print(const std::vector<Font::ColoredString> &str, Font *font, const Matrix4 &m);
|
void print(const std::vector<love::font::ColoredString> &str, Font *font, const Matrix4 &m);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws formatted text on screen at the specified coordinates.
|
* Draws formatted text on screen at the specified coordinates.
|
||||||
**/
|
**/
|
||||||
void printf(const std::vector<Font::ColoredString> &str, float wrap, Font::AlignMode align, const Matrix4 &m);
|
void printf(const std::vector<love::font::ColoredString> &str, float wrap, Font::AlignMode align, const Matrix4 &m);
|
||||||
void printf(const std::vector<Font::ColoredString> &str, Font *font, float wrap, Font::AlignMode align, const Matrix4 &m);
|
void printf(const std::vector<love::font::ColoredString> &str, Font *font, float wrap, Font::AlignMode align, const Matrix4 &m);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws a series of points at the specified positions.
|
* Draws a series of points at the specified positions.
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ namespace graphics
|
|||||||
|
|
||||||
love::Type TextBatch::type("TextBatch", &Drawable::type);
|
love::Type TextBatch::type("TextBatch", &Drawable::type);
|
||||||
|
|
||||||
TextBatch::TextBatch(Font *font, const std::vector<Font::ColoredString> &text)
|
TextBatch::TextBatch(Font *font, const std::vector<love::font::ColoredString> &text)
|
||||||
: font(font)
|
: font(font)
|
||||||
, vertexAttributes(Font::vertexFormat, 0)
|
, vertexAttributes(Font::vertexFormat, 0)
|
||||||
, vertexData(nullptr)
|
, vertexData(nullptr)
|
||||||
@@ -112,13 +112,13 @@ void TextBatch::addTextData(const TextData &t)
|
|||||||
std::vector<Font::GlyphVertex> vertices;
|
std::vector<Font::GlyphVertex> vertices;
|
||||||
std::vector<Font::DrawCommand> newcommands;
|
std::vector<Font::DrawCommand> newcommands;
|
||||||
|
|
||||||
Font::TextInfo textinfo;
|
love::font::TextShaper::TextInfo textinfo;
|
||||||
|
|
||||||
Colorf constantcolor = Colorf(1.0f, 1.0f, 1.0f, 1.0f);
|
Colorf constantcolor = Colorf(1.0f, 1.0f, 1.0f, 1.0f);
|
||||||
|
|
||||||
// We only have formatted text if the align mode is valid.
|
// We only have formatted text if the align mode is valid.
|
||||||
if (t.align == Font::ALIGN_MAX_ENUM)
|
if (t.align == Font::ALIGN_MAX_ENUM)
|
||||||
newcommands = font->generateVertices(t.codepoints, constantcolor, vertices, 0.0f, Vector2(0.0f, 0.0f), &textinfo);
|
newcommands = font->generateVertices(t.codepoints, Range(), constantcolor, vertices, 0.0f, Vector2(0.0f, 0.0f), &textinfo);
|
||||||
else
|
else
|
||||||
newcommands = font->generateVerticesFormatted(t.codepoints, constantcolor, t.wrap, t.align, vertices, &textinfo);
|
newcommands = font->generateVerticesFormatted(t.codepoints, constantcolor, t.wrap, t.align, vertices, &textinfo);
|
||||||
|
|
||||||
@@ -172,31 +172,31 @@ void TextBatch::addTextData(const TextData &t)
|
|||||||
regenerateVertices();
|
regenerateVertices();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextBatch::set(const std::vector<Font::ColoredString> &text)
|
void TextBatch::set(const std::vector<love::font::ColoredString> &text)
|
||||||
{
|
{
|
||||||
return set(text, -1.0f, Font::ALIGN_MAX_ENUM);
|
return set(text, -1.0f, Font::ALIGN_MAX_ENUM);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextBatch::set(const std::vector<Font::ColoredString> &text, float wrap, Font::AlignMode align)
|
void TextBatch::set(const std::vector<love::font::ColoredString> &text, float wrap, Font::AlignMode align)
|
||||||
{
|
{
|
||||||
if (text.empty() || (text.size() == 1 && text[0].str.empty()))
|
if (text.empty() || (text.size() == 1 && text[0].str.empty()))
|
||||||
return clear();
|
return clear();
|
||||||
|
|
||||||
Font::ColoredCodepoints codepoints;
|
love::font::ColoredCodepoints codepoints;
|
||||||
Font::getCodepointsFromString(text, codepoints);
|
love::font::getCodepointsFromString(text, codepoints);
|
||||||
|
|
||||||
addTextData({codepoints, wrap, align, {}, false, false, Matrix4()});
|
addTextData({codepoints, wrap, align, {}, false, false, Matrix4()});
|
||||||
}
|
}
|
||||||
|
|
||||||
int TextBatch::add(const std::vector<Font::ColoredString> &text, const Matrix4 &m)
|
int TextBatch::add(const std::vector<love::font::ColoredString> &text, const Matrix4 &m)
|
||||||
{
|
{
|
||||||
return addf(text, -1.0f, Font::ALIGN_MAX_ENUM, m);
|
return addf(text, -1.0f, Font::ALIGN_MAX_ENUM, m);
|
||||||
}
|
}
|
||||||
|
|
||||||
int TextBatch::addf(const std::vector<Font::ColoredString> &text, float wrap, Font::AlignMode align, const Matrix4 &m)
|
int TextBatch::addf(const std::vector<love::font::ColoredString> &text, float wrap, Font::AlignMode align, const Matrix4 &m)
|
||||||
{
|
{
|
||||||
Font::ColoredCodepoints codepoints;
|
love::font::ColoredCodepoints codepoints;
|
||||||
Font::getCodepointsFromString(text, codepoints);
|
love::font::getCodepointsFromString(text, codepoints);
|
||||||
|
|
||||||
addTextData({codepoints, wrap, align, {}, true, true, m});
|
addTextData({codepoints, wrap, align, {}, true, true, m});
|
||||||
|
|
||||||
|
|||||||
@@ -40,14 +40,14 @@ public:
|
|||||||
|
|
||||||
static love::Type type;
|
static love::Type type;
|
||||||
|
|
||||||
TextBatch(Font *font, const std::vector<Font::ColoredString> &text = {});
|
TextBatch(Font *font, const std::vector<love::font::ColoredString> &text = {});
|
||||||
virtual ~TextBatch();
|
virtual ~TextBatch();
|
||||||
|
|
||||||
void set(const std::vector<Font::ColoredString> &text);
|
void set(const std::vector<love::font::ColoredString> &text);
|
||||||
void set(const std::vector<Font::ColoredString> &text, float wrap, Font::AlignMode align);
|
void set(const std::vector<love::font::ColoredString> &text, float wrap, Font::AlignMode align);
|
||||||
|
|
||||||
int add(const std::vector<Font::ColoredString> &text, const Matrix4 &m);
|
int add(const std::vector<love::font::ColoredString> &text, const Matrix4 &m);
|
||||||
int addf(const std::vector<Font::ColoredString> &text, float wrap, Font::AlignMode align, const Matrix4 &m);
|
int addf(const std::vector<love::font::ColoredString> &text, float wrap, Font::AlignMode align, const Matrix4 &m);
|
||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
@@ -71,10 +71,10 @@ private:
|
|||||||
|
|
||||||
struct TextData
|
struct TextData
|
||||||
{
|
{
|
||||||
Font::ColoredCodepoints codepoints;
|
love::font::ColoredCodepoints codepoints;
|
||||||
float wrap;
|
float wrap;
|
||||||
Font::AlignMode align;
|
Font::AlignMode align;
|
||||||
Font::TextInfo textInfo;
|
love::font::TextShaper::TextInfo textInfo;
|
||||||
bool useMatrix;
|
bool useMatrix;
|
||||||
bool appendVertices;
|
bool appendVertices;
|
||||||
Matrix4 matrix;
|
Matrix4 matrix;
|
||||||
|
|||||||
@@ -30,9 +30,9 @@ namespace love
|
|||||||
namespace graphics
|
namespace graphics
|
||||||
{
|
{
|
||||||
|
|
||||||
void luax_checkcoloredstring(lua_State *L, int idx, std::vector<Font::ColoredString> &strings)
|
void luax_checkcoloredstring(lua_State *L, int idx, std::vector<love::font::ColoredString> &strings)
|
||||||
{
|
{
|
||||||
Font::ColoredString coloredstr;
|
love::font::ColoredString coloredstr;
|
||||||
coloredstr.color = Colorf(1.0f, 1.0f, 1.0f, 1.0f);
|
coloredstr.color = Colorf(1.0f, 1.0f, 1.0f, 1.0f);
|
||||||
|
|
||||||
if (lua_istable(L, idx))
|
if (lua_istable(L, idx))
|
||||||
@@ -103,7 +103,7 @@ int w_Font_getWrap(lua_State *L)
|
|||||||
{
|
{
|
||||||
Font *t = luax_checkfont(L, 1);
|
Font *t = luax_checkfont(L, 1);
|
||||||
|
|
||||||
std::vector<Font::ColoredString> text;
|
std::vector<love::font::ColoredString> text;
|
||||||
luax_checkcoloredstring(L, 2, text);
|
luax_checkcoloredstring(L, 2, text);
|
||||||
|
|
||||||
float wrap = (float) luaL_checknumber(L, 3);
|
float wrap = (float) luaL_checknumber(L, 3);
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ namespace graphics
|
|||||||
{
|
{
|
||||||
|
|
||||||
Font *luax_checkfont(lua_State *L, int idx);
|
Font *luax_checkfont(lua_State *L, int idx);
|
||||||
void luax_checkcoloredstring(lua_State *L, int idx, std::vector<Font::ColoredString> &strings);
|
void luax_checkcoloredstring(lua_State *L, int idx, std::vector<love::font::ColoredString> &strings);
|
||||||
extern "C" int luaopen_font(lua_State *L);
|
extern "C" int luaopen_font(lua_State *L);
|
||||||
|
|
||||||
} // graphics
|
} // graphics
|
||||||
|
|||||||
@@ -2112,7 +2112,7 @@ int w_newTextBatch(lua_State *L)
|
|||||||
luax_catchexcept(L, [&](){ t = instance()->newTextBatch(font); });
|
luax_catchexcept(L, [&](){ t = instance()->newTextBatch(font); });
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::vector<Font::ColoredString> text;
|
std::vector<love::font::ColoredString> text;
|
||||||
luax_checkcoloredstring(L, 2, text);
|
luax_checkcoloredstring(L, 2, text);
|
||||||
|
|
||||||
luax_catchexcept(L, [&](){ t = instance()->newTextBatch(font, text); });
|
luax_catchexcept(L, [&](){ t = instance()->newTextBatch(font, text); });
|
||||||
@@ -3132,7 +3132,7 @@ int w_drawShaderVertices(lua_State *L)
|
|||||||
|
|
||||||
int w_print(lua_State *L)
|
int w_print(lua_State *L)
|
||||||
{
|
{
|
||||||
std::vector<Font::ColoredString> str;
|
std::vector<love::font::ColoredString> str;
|
||||||
luax_checkcoloredstring(L, 1, str);
|
luax_checkcoloredstring(L, 1, str);
|
||||||
|
|
||||||
if (luax_istype(L, 2, Font::type))
|
if (luax_istype(L, 2, Font::type))
|
||||||
@@ -3157,7 +3157,7 @@ int w_print(lua_State *L)
|
|||||||
|
|
||||||
int w_printf(lua_State *L)
|
int w_printf(lua_State *L)
|
||||||
{
|
{
|
||||||
std::vector<Font::ColoredString> str;
|
std::vector<love::font::ColoredString> str;
|
||||||
luax_checkcoloredstring(L, 1, str);
|
luax_checkcoloredstring(L, 1, str);
|
||||||
|
|
||||||
Font *font = nullptr;
|
Font *font = nullptr;
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ int w_TextBatch_set(lua_State *L)
|
|||||||
{
|
{
|
||||||
TextBatch *t = luax_checktextbatch(L, 1);
|
TextBatch *t = luax_checktextbatch(L, 1);
|
||||||
|
|
||||||
std::vector<Font::ColoredString> newtext;
|
std::vector<love::font::ColoredString> newtext;
|
||||||
luax_checkcoloredstring(L, 2, newtext);
|
luax_checkcoloredstring(L, 2, newtext);
|
||||||
|
|
||||||
luax_catchexcept(L, [&](){ t->set(newtext); });
|
luax_catchexcept(L, [&](){ t->set(newtext); });
|
||||||
@@ -54,7 +54,7 @@ int w_TextBatch_setf(lua_State *L)
|
|||||||
if (!Font::getConstant(alignstr, align))
|
if (!Font::getConstant(alignstr, align))
|
||||||
return luax_enumerror(L, "align mode", Font::getConstants(align), alignstr);
|
return luax_enumerror(L, "align mode", Font::getConstants(align), alignstr);
|
||||||
|
|
||||||
std::vector<Font::ColoredString> newtext;
|
std::vector<love::font::ColoredString> newtext;
|
||||||
luax_checkcoloredstring(L, 2, newtext);
|
luax_checkcoloredstring(L, 2, newtext);
|
||||||
|
|
||||||
luax_catchexcept(L, [&](){ t->set(newtext, wraplimit, align); });
|
luax_catchexcept(L, [&](){ t->set(newtext, wraplimit, align); });
|
||||||
@@ -68,7 +68,7 @@ int w_TextBatch_add(lua_State *L)
|
|||||||
|
|
||||||
int index = 0;
|
int index = 0;
|
||||||
|
|
||||||
std::vector<Font::ColoredString> text;
|
std::vector<love::font::ColoredString> text;
|
||||||
luax_checkcoloredstring(L, 2, text);
|
luax_checkcoloredstring(L, 2, text);
|
||||||
|
|
||||||
if (luax_istype(L, 3, math::Transform::type))
|
if (luax_istype(L, 3, math::Transform::type))
|
||||||
@@ -102,7 +102,7 @@ int w_TextBatch_addf(lua_State *L)
|
|||||||
|
|
||||||
int index = 0;
|
int index = 0;
|
||||||
|
|
||||||
std::vector<Font::ColoredString> text;
|
std::vector<love::font::ColoredString> text;
|
||||||
luax_checkcoloredstring(L, 2, text);
|
luax_checkcoloredstring(L, 2, text);
|
||||||
|
|
||||||
float wrap = (float) luaL_checknumber(L, 3);
|
float wrap = (float) luaL_checknumber(L, 3);
|
||||||
|
|||||||
Reference in New Issue
Block a user