├── example ├── .gitignore ├── Makefile ├── Project.xcconfig ├── bin │ └── data │ │ └── Roboto-Regular.ttf ├── config.make ├── example.xcodeproj │ └── project.pbxproj ├── openFrameworks-Info.plist └── src │ └── ofApp.cpp ├── libs └── nanovg │ └── src │ ├── fontstash.h │ ├── nanovg.c │ ├── nanovg.h │ ├── nanovg_gl.h │ ├── nanovg_gl_utils.h │ ├── stb_image.h │ └── stb_truetype.h └── src ├── ofxNanoVG.cpp └── ofxNanoVG.h /example/.gitignore: -------------------------------------------------------------------------------- 1 | .svn 2 | .hg 3 | .cvs 4 | 5 | # osx 6 | .DS_Store 7 | .AppleDouble 8 | .LSOverride 9 | Icon 10 | *.app 11 | ._* 12 | 13 | # xcode3 14 | *.mode1v3 15 | *.pbxuser 16 | build/ 17 | 18 | # xcode4 19 | *.xcodeproj/* 20 | !*.xcodeproj/project.pbxproj 21 | !*.xcodeproj/default.* 22 | **/*.xcodeproj/* 23 | !**/*.xcodeproj/project.pbxproj 24 | !**/*.xcodeproj/default.* 25 | *.xcworkspace/* 26 | !*.xcworkspace/contents.xcworkspacedata 27 | 28 | # windows 29 | *.exe 30 | Thumbs.db 31 | ehthumbs.db 32 | 33 | # vs 34 | ipch/ 35 | [Bb]in/ 36 | [Oo]bj/ 37 | *.aps 38 | *.ncb 39 | *.opensdf 40 | *.sdf 41 | *.cachefile 42 | *.suo 43 | *.user 44 | *.sln.docstates 45 | 46 | # Object files 47 | *.o 48 | 49 | # Libraries 50 | *.lib 51 | *.a 52 | 53 | # Shared objects (inc. Windows DLLs) 54 | *.dll 55 | *.so 56 | *.so.* 57 | *.dylib 58 | 59 | -------------------------------------------------------------------------------- /example/Makefile: -------------------------------------------------------------------------------- 1 | # Attempt to load a config.make file. 2 | # If none is found, project defaults in config.project.make will be used. 3 | ifneq ($(wildcard config.make),) 4 | include config.make 5 | endif 6 | 7 | # make sure the the OF_ROOT location is defined 8 | ifndef OF_ROOT 9 | OF_ROOT=../../.. 10 | endif 11 | 12 | # call the project makefile! 13 | include $(OF_ROOT)/libs/openFrameworksCompiled/project/makefileCommon/compile.project.mk 14 | -------------------------------------------------------------------------------- /example/Project.xcconfig: -------------------------------------------------------------------------------- 1 | //THE PATH TO THE ROOT OF OUR OF PATH RELATIVE TO THIS PROJECT. 2 | //THIS NEEDS TO BE DEFINED BEFORE CoreOF.xcconfig IS INCLUDED 3 | OF_PATH = ../../.. 4 | 5 | //THIS HAS ALL THE HEADER AND LIBS FOR OF CORE 6 | #include "../../../libs/openFrameworksCompiled/project/osx/CoreOF.xcconfig" 7 | 8 | //ICONS - NEW IN 0072 9 | ICON_NAME_DEBUG = icon-debug.icns 10 | ICON_NAME_RELEASE = icon.icns 11 | ICON_FILE_PATH = $(OF_PATH)/libs/openFrameworksCompiled/project/osx/ 12 | 13 | //IF YOU WANT AN APP TO HAVE A CUSTOM ICON - PUT THEM IN YOUR DATA FOLDER AND CHANGE ICON_FILE_PATH to: 14 | //ICON_FILE_PATH = bin/data/ 15 | 16 | OTHER_LDFLAGS = $(OF_CORE_LIBS) 17 | HEADER_SEARCH_PATHS = $(OF_CORE_HEADERS) 18 | -------------------------------------------------------------------------------- /example/bin/data/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/satoruhiga/ofxNanoVG/b5c00be39c33665b17e178312d5488170dcaeabd/example/bin/data/Roboto-Regular.ttf -------------------------------------------------------------------------------- /example/config.make: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # CONFIGURE PROJECT MAKEFILE (optional) 3 | # This file is where we make project specific configurations. 4 | ################################################################################ 5 | 6 | ################################################################################ 7 | # OF ROOT 8 | # The location of your root openFrameworks installation 9 | # (default) OF_ROOT = ../../.. 10 | ################################################################################ 11 | # OF_ROOT = ../../.. 12 | 13 | ################################################################################ 14 | # PROJECT ROOT 15 | # The location of the project - a starting place for searching for files 16 | # (default) PROJECT_ROOT = . (this directory) 17 | # 18 | ################################################################################ 19 | # PROJECT_ROOT = . 20 | 21 | ################################################################################ 22 | # PROJECT SPECIFIC CHECKS 23 | # This is a project defined section to create internal makefile flags to 24 | # conditionally enable or disable the addition of various features within 25 | # this makefile. For instance, if you want to make changes based on whether 26 | # GTK is installed, one might test that here and create a variable to check. 27 | ################################################################################ 28 | # None 29 | 30 | ################################################################################ 31 | # PROJECT EXTERNAL SOURCE PATHS 32 | # These are fully qualified paths that are not within the PROJECT_ROOT folder. 33 | # Like source folders in the PROJECT_ROOT, these paths are subject to 34 | # exlclusion via the PROJECT_EXLCUSIONS list. 35 | # 36 | # (default) PROJECT_EXTERNAL_SOURCE_PATHS = (blank) 37 | # 38 | # Note: Leave a leading space when adding list items with the += operator 39 | ################################################################################ 40 | # PROJECT_EXTERNAL_SOURCE_PATHS = 41 | 42 | ################################################################################ 43 | # PROJECT EXCLUSIONS 44 | # These makefiles assume that all folders in your current project directory 45 | # and any listed in the PROJECT_EXTERNAL_SOURCH_PATHS are are valid locations 46 | # to look for source code. The any folders or files that match any of the 47 | # items in the PROJECT_EXCLUSIONS list below will be ignored. 48 | # 49 | # Each item in the PROJECT_EXCLUSIONS list will be treated as a complete 50 | # string unless teh user adds a wildcard (%) operator to match subdirectories. 51 | # GNU make only allows one wildcard for matching. The second wildcard (%) is 52 | # treated literally. 53 | # 54 | # (default) PROJECT_EXCLUSIONS = (blank) 55 | # 56 | # Will automatically exclude the following: 57 | # 58 | # $(PROJECT_ROOT)/bin% 59 | # $(PROJECT_ROOT)/obj% 60 | # $(PROJECT_ROOT)/%.xcodeproj 61 | # 62 | # Note: Leave a leading space when adding list items with the += operator 63 | ################################################################################ 64 | # PROJECT_EXCLUSIONS = 65 | 66 | ################################################################################ 67 | # PROJECT LINKER FLAGS 68 | # These flags will be sent to the linker when compiling the executable. 69 | # 70 | # (default) PROJECT_LDFLAGS = -Wl,-rpath=./libs 71 | # 72 | # Note: Leave a leading space when adding list items with the += operator 73 | ################################################################################ 74 | 75 | # Currently, shared libraries that are needed are copied to the 76 | # $(PROJECT_ROOT)/bin/libs directory. The following LDFLAGS tell the linker to 77 | # add a runtime path to search for those shared libraries, since they aren't 78 | # incorporated directly into the final executable application binary. 79 | # TODO: should this be a default setting? 80 | # PROJECT_LDFLAGS=-Wl,-rpath=./libs 81 | 82 | ################################################################################ 83 | # PROJECT DEFINES 84 | # Create a space-delimited list of DEFINES. The list will be converted into 85 | # CFLAGS with the "-D" flag later in the makefile. 86 | # 87 | # (default) PROJECT_DEFINES = (blank) 88 | # 89 | # Note: Leave a leading space when adding list items with the += operator 90 | ################################################################################ 91 | # PROJECT_DEFINES = 92 | 93 | ################################################################################ 94 | # PROJECT CFLAGS 95 | # This is a list of fully qualified CFLAGS required when compiling for this 96 | # project. These CFLAGS will be used IN ADDITION TO the PLATFORM_CFLAGS 97 | # defined in your platform specific core configuration files. These flags are 98 | # presented to the compiler BEFORE the PROJECT_OPTIMIZATION_CFLAGS below. 99 | # 100 | # (default) PROJECT_CFLAGS = (blank) 101 | # 102 | # Note: Before adding PROJECT_CFLAGS, note that the PLATFORM_CFLAGS defined in 103 | # your platform specific configuration file will be applied by default and 104 | # further flags here may not be needed. 105 | # 106 | # Note: Leave a leading space when adding list items with the += operator 107 | ################################################################################ 108 | # PROJECT_CFLAGS = 109 | 110 | ################################################################################ 111 | # PROJECT OPTIMIZATION CFLAGS 112 | # These are lists of CFLAGS that are target-specific. While any flags could 113 | # be conditionally added, they are usually limited to optimization flags. 114 | # These flags are added BEFORE the PROJECT_CFLAGS. 115 | # 116 | # PROJECT_OPTIMIZATION_CFLAGS_RELEASE flags are only applied to RELEASE targets. 117 | # 118 | # (default) PROJECT_OPTIMIZATION_CFLAGS_RELEASE = (blank) 119 | # 120 | # PROJECT_OPTIMIZATION_CFLAGS_DEBUG flags are only applied to DEBUG targets. 121 | # 122 | # (default) PROJECT_OPTIMIZATION_CFLAGS_DEBUG = (blank) 123 | # 124 | # Note: Before adding PROJECT_OPTIMIZATION_CFLAGS, please note that the 125 | # PLATFORM_OPTIMIZATION_CFLAGS defined in your platform specific configuration 126 | # file will be applied by default and further optimization flags here may not 127 | # be needed. 128 | # 129 | # Note: Leave a leading space when adding list items with the += operator 130 | ################################################################################ 131 | # PROJECT_OPTIMIZATION_CFLAGS_RELEASE = 132 | # PROJECT_OPTIMIZATION_CFLAGS_DEBUG = 133 | 134 | ################################################################################ 135 | # PROJECT COMPILERS 136 | # Custom compilers can be set for CC and CXX 137 | # (default) PROJECT_CXX = (blank) 138 | # (default) PROJECT_CC = (blank) 139 | # Note: Leave a leading space when adding list items with the += operator 140 | ################################################################################ 141 | # PROJECT_CXX = 142 | # PROJECT_CC = 143 | -------------------------------------------------------------------------------- /example/example.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | BBAB23CB13894F3D00AA2426 /* GLUT.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = BBAB23BE13894E4700AA2426 /* GLUT.framework */; }; 11 | E4328149138ABC9F0047C5CB /* openFrameworksDebug.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E4328148138ABC890047C5CB /* openFrameworksDebug.a */; }; 12 | E45BE97B0E8CC7DD009D7055 /* AGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E45BE9710E8CC7DD009D7055 /* AGL.framework */; }; 13 | E45BE97C0E8CC7DD009D7055 /* ApplicationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E45BE9720E8CC7DD009D7055 /* ApplicationServices.framework */; }; 14 | E45BE97D0E8CC7DD009D7055 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E45BE9730E8CC7DD009D7055 /* AudioToolbox.framework */; }; 15 | E45BE97E0E8CC7DD009D7055 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E45BE9740E8CC7DD009D7055 /* Carbon.framework */; }; 16 | E45BE97F0E8CC7DD009D7055 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E45BE9750E8CC7DD009D7055 /* CoreAudio.framework */; }; 17 | E45BE9800E8CC7DD009D7055 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E45BE9760E8CC7DD009D7055 /* CoreFoundation.framework */; }; 18 | E45BE9810E8CC7DD009D7055 /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E45BE9770E8CC7DD009D7055 /* CoreServices.framework */; }; 19 | E45BE9830E8CC7DD009D7055 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E45BE9790E8CC7DD009D7055 /* OpenGL.framework */; }; 20 | E45BE9840E8CC7DD009D7055 /* QuickTime.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E45BE97A0E8CC7DD009D7055 /* QuickTime.framework */; }; 21 | E4B69E210A3A1BDC003C02F2 /* ofApp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E4B69E1E0A3A1BDC003C02F2 /* ofApp.cpp */; }; 22 | E4C2424710CC5A17004149E2 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E4C2424410CC5A17004149E2 /* AppKit.framework */; }; 23 | E4C2424810CC5A17004149E2 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E4C2424510CC5A17004149E2 /* Cocoa.framework */; }; 24 | E4C2424910CC5A17004149E2 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E4C2424610CC5A17004149E2 /* IOKit.framework */; }; 25 | E4EB6799138ADC1D00A09F29 /* GLUT.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BBAB23BE13894E4700AA2426 /* GLUT.framework */; }; 26 | E7892E2B196BF90400185B0D /* nanovg.c in Sources */ = {isa = PBXBuildFile; fileRef = E7892E20196BF90400185B0D /* nanovg.c */; }; 27 | E7892E2D196BF90400185B0D /* ofxNanoVG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E7892E29196BF90400185B0D /* ofxNanoVG.cpp */; }; 28 | E7E077E515D3B63C0020DFD4 /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E7E077E415D3B63C0020DFD4 /* CoreVideo.framework */; }; 29 | E7E077E815D3B6510020DFD4 /* QTKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E7E077E715D3B6510020DFD4 /* QTKit.framework */; }; 30 | E7F985F815E0DEA3003869B5 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E7F985F515E0DE99003869B5 /* Accelerate.framework */; }; 31 | /* End PBXBuildFile section */ 32 | 33 | /* Begin PBXContainerItemProxy section */ 34 | E4328147138ABC890047C5CB /* PBXContainerItemProxy */ = { 35 | isa = PBXContainerItemProxy; 36 | containerPortal = E4328143138ABC890047C5CB /* openFrameworksLib.xcodeproj */; 37 | proxyType = 2; 38 | remoteGlobalIDString = E4B27C1510CBEB8E00536013; 39 | remoteInfo = openFrameworks; 40 | }; 41 | E4EEB9AB138B136A00A80321 /* PBXContainerItemProxy */ = { 42 | isa = PBXContainerItemProxy; 43 | containerPortal = E4328143138ABC890047C5CB /* openFrameworksLib.xcodeproj */; 44 | proxyType = 1; 45 | remoteGlobalIDString = E4B27C1410CBEB8E00536013; 46 | remoteInfo = openFrameworks; 47 | }; 48 | /* End PBXContainerItemProxy section */ 49 | 50 | /* Begin PBXCopyFilesBuildPhase section */ 51 | E4C2427710CC5ABF004149E2 /* CopyFiles */ = { 52 | isa = PBXCopyFilesBuildPhase; 53 | buildActionMask = 2147483647; 54 | dstPath = ""; 55 | dstSubfolderSpec = 10; 56 | files = ( 57 | BBAB23CB13894F3D00AA2426 /* GLUT.framework in CopyFiles */, 58 | ); 59 | runOnlyForDeploymentPostprocessing = 0; 60 | }; 61 | /* End PBXCopyFilesBuildPhase section */ 62 | 63 | /* Begin PBXFileReference section */ 64 | BBAB23BE13894E4700AA2426 /* GLUT.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GLUT.framework; path = ../../../libs/glut/lib/osx/GLUT.framework; sourceTree = ""; }; 65 | E4328143138ABC890047C5CB /* openFrameworksLib.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = openFrameworksLib.xcodeproj; path = ../../../libs/openFrameworksCompiled/project/osx/openFrameworksLib.xcodeproj; sourceTree = SOURCE_ROOT; }; 66 | E45BE9710E8CC7DD009D7055 /* AGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AGL.framework; path = /System/Library/Frameworks/AGL.framework; sourceTree = ""; }; 67 | E45BE9720E8CC7DD009D7055 /* ApplicationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ApplicationServices.framework; path = /System/Library/Frameworks/ApplicationServices.framework; sourceTree = ""; }; 68 | E45BE9730E8CC7DD009D7055 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = /System/Library/Frameworks/AudioToolbox.framework; sourceTree = ""; }; 69 | E45BE9740E8CC7DD009D7055 /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = /System/Library/Frameworks/Carbon.framework; sourceTree = ""; }; 70 | E45BE9750E8CC7DD009D7055 /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = /System/Library/Frameworks/CoreAudio.framework; sourceTree = ""; }; 71 | E45BE9760E8CC7DD009D7055 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = /System/Library/Frameworks/CoreFoundation.framework; sourceTree = ""; }; 72 | E45BE9770E8CC7DD009D7055 /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = /System/Library/Frameworks/CoreServices.framework; sourceTree = ""; }; 73 | E45BE9790E8CC7DD009D7055 /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = /System/Library/Frameworks/OpenGL.framework; sourceTree = ""; }; 74 | E45BE97A0E8CC7DD009D7055 /* QuickTime.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuickTime.framework; path = /System/Library/Frameworks/QuickTime.framework; sourceTree = ""; }; 75 | E4B69B5B0A3A1756003C02F2 /* exampleDebug.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = exampleDebug.app; sourceTree = BUILT_PRODUCTS_DIR; }; 76 | E4B69E1E0A3A1BDC003C02F2 /* ofApp.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = ofApp.cpp; path = src/ofApp.cpp; sourceTree = SOURCE_ROOT; }; 77 | E4B6FCAD0C3E899E008CF71C /* openFrameworks-Info.plist */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.plist.xml; path = "openFrameworks-Info.plist"; sourceTree = ""; }; 78 | E4C2424410CC5A17004149E2 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; 79 | E4C2424510CC5A17004149E2 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; 80 | E4C2424610CC5A17004149E2 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = /System/Library/Frameworks/IOKit.framework; sourceTree = ""; }; 81 | E4EB691F138AFCF100A09F29 /* CoreOF.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = CoreOF.xcconfig; path = ../../../libs/openFrameworksCompiled/project/osx/CoreOF.xcconfig; sourceTree = SOURCE_ROOT; }; 82 | E4EB6923138AFD0F00A09F29 /* Project.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Project.xcconfig; sourceTree = ""; }; 83 | E7892E1F196BF90400185B0D /* fontstash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fontstash.h; sourceTree = ""; }; 84 | E7892E20196BF90400185B0D /* nanovg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nanovg.c; sourceTree = ""; }; 85 | E7892E21196BF90400185B0D /* nanovg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nanovg.h; sourceTree = ""; }; 86 | E7892E22196BF90400185B0D /* nanovg_gl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nanovg_gl.h; sourceTree = ""; }; 87 | E7892E23196BF90400185B0D /* nanovg_gl_utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nanovg_gl_utils.h; sourceTree = ""; }; 88 | E7892E25196BF90400185B0D /* stb_image.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stb_image.h; sourceTree = ""; }; 89 | E7892E27196BF90400185B0D /* stb_truetype.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stb_truetype.h; sourceTree = ""; }; 90 | E7892E29196BF90400185B0D /* ofxNanoVG.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ofxNanoVG.cpp; sourceTree = ""; }; 91 | E7892E2A196BF90400185B0D /* ofxNanoVG.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ofxNanoVG.h; sourceTree = ""; }; 92 | E7E077E415D3B63C0020DFD4 /* CoreVideo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreVideo.framework; path = /System/Library/Frameworks/CoreVideo.framework; sourceTree = ""; }; 93 | E7E077E715D3B6510020DFD4 /* QTKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QTKit.framework; path = /System/Library/Frameworks/QTKit.framework; sourceTree = ""; }; 94 | E7F985F515E0DE99003869B5 /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = /System/Library/Frameworks/Accelerate.framework; sourceTree = ""; }; 95 | /* End PBXFileReference section */ 96 | 97 | /* Begin PBXFrameworksBuildPhase section */ 98 | E4B69B590A3A1756003C02F2 /* Frameworks */ = { 99 | isa = PBXFrameworksBuildPhase; 100 | buildActionMask = 2147483647; 101 | files = ( 102 | E7F985F815E0DEA3003869B5 /* Accelerate.framework in Frameworks */, 103 | E7E077E815D3B6510020DFD4 /* QTKit.framework in Frameworks */, 104 | E4EB6799138ADC1D00A09F29 /* GLUT.framework in Frameworks */, 105 | E4328149138ABC9F0047C5CB /* openFrameworksDebug.a in Frameworks */, 106 | E45BE97B0E8CC7DD009D7055 /* AGL.framework in Frameworks */, 107 | E45BE97C0E8CC7DD009D7055 /* ApplicationServices.framework in Frameworks */, 108 | E45BE97D0E8CC7DD009D7055 /* AudioToolbox.framework in Frameworks */, 109 | E45BE97E0E8CC7DD009D7055 /* Carbon.framework in Frameworks */, 110 | E45BE97F0E8CC7DD009D7055 /* CoreAudio.framework in Frameworks */, 111 | E45BE9800E8CC7DD009D7055 /* CoreFoundation.framework in Frameworks */, 112 | E45BE9810E8CC7DD009D7055 /* CoreServices.framework in Frameworks */, 113 | E45BE9830E8CC7DD009D7055 /* OpenGL.framework in Frameworks */, 114 | E45BE9840E8CC7DD009D7055 /* QuickTime.framework in Frameworks */, 115 | E4C2424710CC5A17004149E2 /* AppKit.framework in Frameworks */, 116 | E4C2424810CC5A17004149E2 /* Cocoa.framework in Frameworks */, 117 | E4C2424910CC5A17004149E2 /* IOKit.framework in Frameworks */, 118 | E7E077E515D3B63C0020DFD4 /* CoreVideo.framework in Frameworks */, 119 | ); 120 | runOnlyForDeploymentPostprocessing = 0; 121 | }; 122 | /* End PBXFrameworksBuildPhase section */ 123 | 124 | /* Begin PBXGroup section */ 125 | BB4B014C10F69532006C3DED /* addons */ = { 126 | isa = PBXGroup; 127 | children = ( 128 | E7892E1B196BF8FB00185B0D /* ofxNanoVG */, 129 | ); 130 | name = addons; 131 | sourceTree = ""; 132 | }; 133 | BBAB23C913894ECA00AA2426 /* system frameworks */ = { 134 | isa = PBXGroup; 135 | children = ( 136 | E7F985F515E0DE99003869B5 /* Accelerate.framework */, 137 | E4C2424410CC5A17004149E2 /* AppKit.framework */, 138 | E4C2424510CC5A17004149E2 /* Cocoa.framework */, 139 | E4C2424610CC5A17004149E2 /* IOKit.framework */, 140 | E45BE9710E8CC7DD009D7055 /* AGL.framework */, 141 | E45BE9720E8CC7DD009D7055 /* ApplicationServices.framework */, 142 | E45BE9730E8CC7DD009D7055 /* AudioToolbox.framework */, 143 | E45BE9740E8CC7DD009D7055 /* Carbon.framework */, 144 | E45BE9750E8CC7DD009D7055 /* CoreAudio.framework */, 145 | E45BE9760E8CC7DD009D7055 /* CoreFoundation.framework */, 146 | E45BE9770E8CC7DD009D7055 /* CoreServices.framework */, 147 | E45BE9790E8CC7DD009D7055 /* OpenGL.framework */, 148 | E45BE97A0E8CC7DD009D7055 /* QuickTime.framework */, 149 | E7E077E415D3B63C0020DFD4 /* CoreVideo.framework */, 150 | E7E077E715D3B6510020DFD4 /* QTKit.framework */, 151 | ); 152 | name = "system frameworks"; 153 | sourceTree = ""; 154 | }; 155 | BBAB23CA13894EDB00AA2426 /* 3rd party frameworks */ = { 156 | isa = PBXGroup; 157 | children = ( 158 | BBAB23BE13894E4700AA2426 /* GLUT.framework */, 159 | ); 160 | name = "3rd party frameworks"; 161 | sourceTree = ""; 162 | }; 163 | E4328144138ABC890047C5CB /* Products */ = { 164 | isa = PBXGroup; 165 | children = ( 166 | E4328148138ABC890047C5CB /* openFrameworksDebug.a */, 167 | ); 168 | name = Products; 169 | sourceTree = ""; 170 | }; 171 | E45BE5980E8CC70C009D7055 /* frameworks */ = { 172 | isa = PBXGroup; 173 | children = ( 174 | BBAB23CA13894EDB00AA2426 /* 3rd party frameworks */, 175 | BBAB23C913894ECA00AA2426 /* system frameworks */, 176 | ); 177 | name = frameworks; 178 | sourceTree = ""; 179 | }; 180 | E4B69B4A0A3A1720003C02F2 = { 181 | isa = PBXGroup; 182 | children = ( 183 | E4B6FCAD0C3E899E008CF71C /* openFrameworks-Info.plist */, 184 | E4EB6923138AFD0F00A09F29 /* Project.xcconfig */, 185 | E4B69E1C0A3A1BDC003C02F2 /* src */, 186 | E4EEC9E9138DF44700A80321 /* openFrameworks */, 187 | BB4B014C10F69532006C3DED /* addons */, 188 | E45BE5980E8CC70C009D7055 /* frameworks */, 189 | E4B69B5B0A3A1756003C02F2 /* exampleDebug.app */, 190 | ); 191 | sourceTree = ""; 192 | }; 193 | E4B69E1C0A3A1BDC003C02F2 /* src */ = { 194 | isa = PBXGroup; 195 | children = ( 196 | E4B69E1E0A3A1BDC003C02F2 /* ofApp.cpp */, 197 | ); 198 | path = src; 199 | sourceTree = SOURCE_ROOT; 200 | }; 201 | E4EEC9E9138DF44700A80321 /* openFrameworks */ = { 202 | isa = PBXGroup; 203 | children = ( 204 | E4EB691F138AFCF100A09F29 /* CoreOF.xcconfig */, 205 | E4328143138ABC890047C5CB /* openFrameworksLib.xcodeproj */, 206 | ); 207 | name = openFrameworks; 208 | sourceTree = ""; 209 | }; 210 | E7892E1B196BF8FB00185B0D /* ofxNanoVG */ = { 211 | isa = PBXGroup; 212 | children = ( 213 | E7892E1C196BF90400185B0D /* libs */, 214 | E7892E28196BF90400185B0D /* src */, 215 | ); 216 | name = ofxNanoVG; 217 | sourceTree = ""; 218 | }; 219 | E7892E1C196BF90400185B0D /* libs */ = { 220 | isa = PBXGroup; 221 | children = ( 222 | E7892E1D196BF90400185B0D /* nanovg */, 223 | ); 224 | name = libs; 225 | path = ../libs; 226 | sourceTree = ""; 227 | }; 228 | E7892E1D196BF90400185B0D /* nanovg */ = { 229 | isa = PBXGroup; 230 | children = ( 231 | E7892E1E196BF90400185B0D /* src */, 232 | ); 233 | path = nanovg; 234 | sourceTree = ""; 235 | }; 236 | E7892E1E196BF90400185B0D /* src */ = { 237 | isa = PBXGroup; 238 | children = ( 239 | E7892E1F196BF90400185B0D /* fontstash.h */, 240 | E7892E20196BF90400185B0D /* nanovg.c */, 241 | E7892E21196BF90400185B0D /* nanovg.h */, 242 | E7892E22196BF90400185B0D /* nanovg_gl.h */, 243 | E7892E23196BF90400185B0D /* nanovg_gl_utils.h */, 244 | E7892E25196BF90400185B0D /* stb_image.h */, 245 | E7892E27196BF90400185B0D /* stb_truetype.h */, 246 | ); 247 | path = src; 248 | sourceTree = ""; 249 | }; 250 | E7892E28196BF90400185B0D /* src */ = { 251 | isa = PBXGroup; 252 | children = ( 253 | E7892E29196BF90400185B0D /* ofxNanoVG.cpp */, 254 | E7892E2A196BF90400185B0D /* ofxNanoVG.h */, 255 | ); 256 | name = src; 257 | path = ../src; 258 | sourceTree = ""; 259 | }; 260 | /* End PBXGroup section */ 261 | 262 | /* Begin PBXNativeTarget section */ 263 | E4B69B5A0A3A1756003C02F2 /* example */ = { 264 | isa = PBXNativeTarget; 265 | buildConfigurationList = E4B69B5F0A3A1757003C02F2 /* Build configuration list for PBXNativeTarget "example" */; 266 | buildPhases = ( 267 | E4B69B580A3A1756003C02F2 /* Sources */, 268 | E4B69B590A3A1756003C02F2 /* Frameworks */, 269 | E4B6FFFD0C3F9AB9008CF71C /* ShellScript */, 270 | E4C2427710CC5ABF004149E2 /* CopyFiles */, 271 | ); 272 | buildRules = ( 273 | ); 274 | dependencies = ( 275 | E4EEB9AC138B136A00A80321 /* PBXTargetDependency */, 276 | ); 277 | name = example; 278 | productName = myOFApp; 279 | productReference = E4B69B5B0A3A1756003C02F2 /* exampleDebug.app */; 280 | productType = "com.apple.product-type.application"; 281 | }; 282 | /* End PBXNativeTarget section */ 283 | 284 | /* Begin PBXProject section */ 285 | E4B69B4C0A3A1720003C02F2 /* Project object */ = { 286 | isa = PBXProject; 287 | attributes = { 288 | LastUpgradeCheck = 0460; 289 | }; 290 | buildConfigurationList = E4B69B4D0A3A1720003C02F2 /* Build configuration list for PBXProject "example" */; 291 | compatibilityVersion = "Xcode 3.2"; 292 | developmentRegion = English; 293 | hasScannedForEncodings = 0; 294 | knownRegions = ( 295 | English, 296 | Japanese, 297 | French, 298 | German, 299 | ); 300 | mainGroup = E4B69B4A0A3A1720003C02F2; 301 | productRefGroup = E4B69B4A0A3A1720003C02F2; 302 | projectDirPath = ""; 303 | projectReferences = ( 304 | { 305 | ProductGroup = E4328144138ABC890047C5CB /* Products */; 306 | ProjectRef = E4328143138ABC890047C5CB /* openFrameworksLib.xcodeproj */; 307 | }, 308 | ); 309 | projectRoot = ""; 310 | targets = ( 311 | E4B69B5A0A3A1756003C02F2 /* example */, 312 | ); 313 | }; 314 | /* End PBXProject section */ 315 | 316 | /* Begin PBXReferenceProxy section */ 317 | E4328148138ABC890047C5CB /* openFrameworksDebug.a */ = { 318 | isa = PBXReferenceProxy; 319 | fileType = archive.ar; 320 | path = openFrameworksDebug.a; 321 | remoteRef = E4328147138ABC890047C5CB /* PBXContainerItemProxy */; 322 | sourceTree = BUILT_PRODUCTS_DIR; 323 | }; 324 | /* End PBXReferenceProxy section */ 325 | 326 | /* Begin PBXShellScriptBuildPhase section */ 327 | E4B6FFFD0C3F9AB9008CF71C /* ShellScript */ = { 328 | isa = PBXShellScriptBuildPhase; 329 | buildActionMask = 2147483647; 330 | files = ( 331 | ); 332 | inputPaths = ( 333 | ); 334 | outputPaths = ( 335 | ); 336 | runOnlyForDeploymentPostprocessing = 0; 337 | shellPath = /bin/sh; 338 | shellScript = "cp -f ../../../libs/fmodex/lib/osx/libfmodex.dylib \"$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/MacOS/libfmodex.dylib\"; install_name_tool -change ./libfmodex.dylib @executable_path/libfmodex.dylib \"$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/MacOS/$PRODUCT_NAME\";\nmkdir -p \"$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/Resources/\"\ncp -f \"$ICON_FILE\" \"$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/Resources/\"\n"; 339 | }; 340 | /* End PBXShellScriptBuildPhase section */ 341 | 342 | /* Begin PBXSourcesBuildPhase section */ 343 | E4B69B580A3A1756003C02F2 /* Sources */ = { 344 | isa = PBXSourcesBuildPhase; 345 | buildActionMask = 2147483647; 346 | files = ( 347 | E7892E2D196BF90400185B0D /* ofxNanoVG.cpp in Sources */, 348 | E7892E2B196BF90400185B0D /* nanovg.c in Sources */, 349 | E4B69E210A3A1BDC003C02F2 /* ofApp.cpp in Sources */, 350 | ); 351 | runOnlyForDeploymentPostprocessing = 0; 352 | }; 353 | /* End PBXSourcesBuildPhase section */ 354 | 355 | /* Begin PBXTargetDependency section */ 356 | E4EEB9AC138B136A00A80321 /* PBXTargetDependency */ = { 357 | isa = PBXTargetDependency; 358 | name = openFrameworks; 359 | targetProxy = E4EEB9AB138B136A00A80321 /* PBXContainerItemProxy */; 360 | }; 361 | /* End PBXTargetDependency section */ 362 | 363 | /* Begin XCBuildConfiguration section */ 364 | E4B69B4E0A3A1720003C02F2 /* Debug */ = { 365 | isa = XCBuildConfiguration; 366 | baseConfigurationReference = E4EB6923138AFD0F00A09F29 /* Project.xcconfig */; 367 | buildSettings = { 368 | ARCHS = "$(NATIVE_ARCH)"; 369 | CONFIGURATION_BUILD_DIR = "$(SRCROOT)/bin/"; 370 | COPY_PHASE_STRIP = NO; 371 | DEAD_CODE_STRIPPING = YES; 372 | GCC_AUTO_VECTORIZATION = YES; 373 | GCC_ENABLE_SSE3_EXTENSIONS = YES; 374 | GCC_ENABLE_SUPPLEMENTAL_SSE3_INSTRUCTIONS = YES; 375 | GCC_INLINES_ARE_PRIVATE_EXTERN = NO; 376 | GCC_OPTIMIZATION_LEVEL = 0; 377 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 378 | GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = YES; 379 | GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO; 380 | GCC_WARN_ALLOW_INCOMPLETE_PROTOCOL = NO; 381 | GCC_WARN_UNINITIALIZED_AUTOS = NO; 382 | GCC_WARN_UNUSED_VALUE = NO; 383 | GCC_WARN_UNUSED_VARIABLE = NO; 384 | MACOSX_DEPLOYMENT_TARGET = 10.6; 385 | OTHER_CPLUSPLUSFLAGS = ( 386 | "-D__MACOSX_CORE__", 387 | "-lpthread", 388 | "-mtune=native", 389 | ); 390 | SDKROOT = macosx; 391 | }; 392 | name = Debug; 393 | }; 394 | E4B69B4F0A3A1720003C02F2 /* Release */ = { 395 | isa = XCBuildConfiguration; 396 | baseConfigurationReference = E4EB6923138AFD0F00A09F29 /* Project.xcconfig */; 397 | buildSettings = { 398 | ARCHS = "$(NATIVE_ARCH)"; 399 | CONFIGURATION_BUILD_DIR = "$(SRCROOT)/bin/"; 400 | COPY_PHASE_STRIP = YES; 401 | DEAD_CODE_STRIPPING = YES; 402 | GCC_AUTO_VECTORIZATION = YES; 403 | GCC_ENABLE_SSE3_EXTENSIONS = YES; 404 | GCC_ENABLE_SUPPLEMENTAL_SSE3_INSTRUCTIONS = YES; 405 | GCC_INLINES_ARE_PRIVATE_EXTERN = NO; 406 | GCC_OPTIMIZATION_LEVEL = 3; 407 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 408 | GCC_UNROLL_LOOPS = YES; 409 | GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = YES; 410 | GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO = NO; 411 | GCC_WARN_ALLOW_INCOMPLETE_PROTOCOL = NO; 412 | GCC_WARN_UNINITIALIZED_AUTOS = NO; 413 | GCC_WARN_UNUSED_VALUE = NO; 414 | GCC_WARN_UNUSED_VARIABLE = NO; 415 | MACOSX_DEPLOYMENT_TARGET = 10.6; 416 | OTHER_CPLUSPLUSFLAGS = ( 417 | "-D__MACOSX_CORE__", 418 | "-lpthread", 419 | "-mtune=native", 420 | ); 421 | SDKROOT = macosx; 422 | }; 423 | name = Release; 424 | }; 425 | E4B69B600A3A1757003C02F2 /* Debug */ = { 426 | isa = XCBuildConfiguration; 427 | buildSettings = { 428 | COMBINE_HIDPI_IMAGES = YES; 429 | COPY_PHASE_STRIP = NO; 430 | FRAMEWORK_SEARCH_PATHS = ( 431 | "$(inherited)", 432 | "$(FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_1)", 433 | ); 434 | FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_1 = "\"$(SRCROOT)/../../../libs/glut/lib/osx\""; 435 | GCC_DYNAMIC_NO_PIC = NO; 436 | GCC_GENERATE_DEBUGGING_SYMBOLS = YES; 437 | GCC_MODEL_TUNING = NONE; 438 | ICON = "$(ICON_NAME_DEBUG)"; 439 | ICON_FILE = "$(ICON_FILE_PATH)$(ICON)"; 440 | INFOPLIST_FILE = "openFrameworks-Info.plist"; 441 | INSTALL_PATH = "$(HOME)/Applications"; 442 | LIBRARY_SEARCH_PATHS = ( 443 | "$(inherited)", 444 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_1)", 445 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_2)", 446 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_3)", 447 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_4)", 448 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_5)", 449 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_6)", 450 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_7)", 451 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_8)", 452 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_9)", 453 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_10)", 454 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_11)", 455 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_12)", 456 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_13)", 457 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_14)", 458 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_15)", 459 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_2)", 460 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_3)", 461 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_7)", 462 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_8)", 463 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_9)", 464 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_10)", 465 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_11)", 466 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_12)", 467 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_13)", 468 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_16)", 469 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_17)", 470 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_18)", 471 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_19)", 472 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_20)", 473 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_21)", 474 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_22)", 475 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_23)", 476 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_24)", 477 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_25)", 478 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_26)", 479 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_27)", 480 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_28)", 481 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_29)", 482 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_30)", 483 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_31)", 484 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_32)", 485 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_33)", 486 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_34)", 487 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_35)", 488 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_36)", 489 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_37)", 490 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_38)", 491 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_39)", 492 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_40)", 493 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_41)", 494 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_42)", 495 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_43)", 496 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_44)", 497 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_45)", 498 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_46)", 499 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_47)", 500 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_48)", 501 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_49)", 502 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_50)", 503 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_51)", 504 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_52)", 505 | ); 506 | PRODUCT_NAME = exampleDebug; 507 | WRAPPER_EXTENSION = app; 508 | }; 509 | name = Debug; 510 | }; 511 | E4B69B610A3A1757003C02F2 /* Release */ = { 512 | isa = XCBuildConfiguration; 513 | buildSettings = { 514 | COMBINE_HIDPI_IMAGES = YES; 515 | COPY_PHASE_STRIP = YES; 516 | FRAMEWORK_SEARCH_PATHS = ( 517 | "$(inherited)", 518 | "$(FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_1)", 519 | ); 520 | FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_1 = "\"$(SRCROOT)/../../../libs/glut/lib/osx\""; 521 | GCC_GENERATE_DEBUGGING_SYMBOLS = YES; 522 | GCC_MODEL_TUNING = NONE; 523 | ICON = "$(ICON_NAME_RELEASE)"; 524 | ICON_FILE = "$(ICON_FILE_PATH)$(ICON)"; 525 | INFOPLIST_FILE = "openFrameworks-Info.plist"; 526 | INSTALL_PATH = "$(HOME)/Applications"; 527 | LIBRARY_SEARCH_PATHS = ( 528 | "$(inherited)", 529 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_1)", 530 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_2)", 531 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_3)", 532 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_4)", 533 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_5)", 534 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_6)", 535 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_7)", 536 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_8)", 537 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_9)", 538 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_10)", 539 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_11)", 540 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_12)", 541 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_13)", 542 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_14)", 543 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_15)", 544 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_2)", 545 | "$(LIBRARY_SEARCH_PATHS_QUOTED_1)", 546 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_3)", 547 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_7)", 548 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_8)", 549 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_9)", 550 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_10)", 551 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_11)", 552 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_12)", 553 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_13)", 554 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_16)", 555 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_17)", 556 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_18)", 557 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_19)", 558 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_20)", 559 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_21)", 560 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_22)", 561 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_23)", 562 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_24)", 563 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_25)", 564 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_26)", 565 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_27)", 566 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_28)", 567 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_29)", 568 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_30)", 569 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_31)", 570 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_32)", 571 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_33)", 572 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_34)", 573 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_35)", 574 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_36)", 575 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_37)", 576 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_38)", 577 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_39)", 578 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_40)", 579 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_41)", 580 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_42)", 581 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_43)", 582 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_44)", 583 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_45)", 584 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_46)", 585 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_47)", 586 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_48)", 587 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_49)", 588 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_50)", 589 | "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_51)", 590 | ); 591 | PRODUCT_NAME = example; 592 | WRAPPER_EXTENSION = app; 593 | }; 594 | name = Release; 595 | }; 596 | /* End XCBuildConfiguration section */ 597 | 598 | /* Begin XCConfigurationList section */ 599 | E4B69B4D0A3A1720003C02F2 /* Build configuration list for PBXProject "example" */ = { 600 | isa = XCConfigurationList; 601 | buildConfigurations = ( 602 | E4B69B4E0A3A1720003C02F2 /* Debug */, 603 | E4B69B4F0A3A1720003C02F2 /* Release */, 604 | ); 605 | defaultConfigurationIsVisible = 0; 606 | defaultConfigurationName = Release; 607 | }; 608 | E4B69B5F0A3A1757003C02F2 /* Build configuration list for PBXNativeTarget "example" */ = { 609 | isa = XCConfigurationList; 610 | buildConfigurations = ( 611 | E4B69B600A3A1757003C02F2 /* Debug */, 612 | E4B69B610A3A1757003C02F2 /* Release */, 613 | ); 614 | defaultConfigurationIsVisible = 0; 615 | defaultConfigurationName = Release; 616 | }; 617 | /* End XCConfigurationList section */ 618 | }; 619 | rootObject = E4B69B4C0A3A1720003C02F2 /* Project object */; 620 | } 621 | -------------------------------------------------------------------------------- /example/openFrameworks-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.yourcompany.openFrameworks 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | APPL 15 | CFBundleSignature 16 | ???? 17 | CFBundleVersion 18 | 1.0 19 | CFBundleIconFile 20 | ${ICON} 21 | 22 | 23 | -------------------------------------------------------------------------------- /example/src/ofApp.cpp: -------------------------------------------------------------------------------- 1 | #include "ofMain.h" 2 | 3 | #include "ofxNanoVG.h" 4 | 5 | class ofApp : public ofBaseApp 6 | { 7 | public: 8 | ofxNanoVG::Canvas canvas; 9 | 10 | void setup() 11 | { 12 | ofSetVerticalSync(true); 13 | ofBackground(0); 14 | 15 | canvas.allocate(1280, 720); 16 | 17 | assert(canvas.loadFont("Roboto-Regular.ttf", "sans")); 18 | } 19 | 20 | void update() 21 | { 22 | ofxNanoVG::Canvas& c = canvas; 23 | 24 | c.begin(); 25 | 26 | ofColor color; 27 | if (ofGetMousePressed()) 28 | color.set(255, 0, 0); 29 | else 30 | color.set(255); 31 | 32 | c.fillColor(color); 33 | c.strokeColor(color); 34 | 35 | canvas.textFont("sans"); 36 | 37 | c.textSize(140); 38 | c.textAlign(ofxNanoVG::TextAlign::LEFT | ofxNanoVG::TextAlign::TOP); 39 | string text = " TEXT"; 40 | 41 | float X = ofGetMouseX(); 42 | float Y = ofGetMouseY(); 43 | 44 | ofRectangle r = c.textBounds(text, 0, 0, 400); 45 | 46 | c.translate(X, Y); // offset mouse pos 47 | c.rotate(ofGetElapsedTimef() * 10); 48 | c.translate(r.width * -0.5, r.height * -0.5); // offset text rectangle 49 | c.text(text, 0, 0, 400); 50 | 51 | r.scaleFromCenter(1.2); 52 | 53 | c.beginPath(); 54 | { 55 | c.roundedRect(r, 30); 56 | c.lineWidth(5 + sin(fmodf(ofGetElapsedTimef() * 5, PI)) * 10); 57 | } 58 | c.strokePath(); 59 | 60 | c.beginPath(); 61 | { 62 | c.lineCap(ofxNanoVG::LineCap::ROUND); 63 | c.lineJoin(ofxNanoVG::LineCap::ROUND); 64 | 65 | c.lineWidth(20); 66 | 67 | float t = ofGetElapsedTimef() * 2; 68 | for (int i = 0; i < 64; i++) 69 | { 70 | float x = ofSignedNoise(1, 0, t + i * 0.02) * 500; 71 | float y = ofSignedNoise(0, 1, t + i * 0.02) * 500; 72 | 73 | if (i == 0) c.moveTo(x, y); 74 | else c.lineTo(x, y); 75 | } 76 | } 77 | c.strokePath(); 78 | 79 | c.end(); 80 | } 81 | 82 | void draw() { canvas.draw(0, 0); } 83 | 84 | void keyPressed(int key) {} 85 | 86 | void keyReleased(int key) {} 87 | 88 | void mouseMoved(int x, int y) {} 89 | 90 | void mouseDragged(int x, int y, int button) {} 91 | 92 | void mousePressed(int x, int y, int button) {} 93 | 94 | void mouseReleased(int x, int y, int button) {} 95 | 96 | void windowResized(int w, int h) {} 97 | }; 98 | 99 | #include "ofAppGLFWWindow.h" 100 | int main(int argc, const char** argv) 101 | { 102 | ofAppGLFWWindow window; 103 | window.setNumSamples(0); 104 | ofSetupOpenGL(&window, 1280, 720, OF_WINDOW); 105 | ofRunApp(new ofApp); 106 | return 0; 107 | } 108 | -------------------------------------------------------------------------------- /libs/nanovg/src/fontstash.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2009-2013 Mikko Mononen memon@inside.org 3 | // 4 | // This software is provided 'as-is', without any express or implied 5 | // warranty. In no event will the authors be held liable for any damages 6 | // arising from the use of this software. 7 | // Permission is granted to anyone to use this software for any purpose, 8 | // including commercial applications, and to alter it and redistribute it 9 | // freely, subject to the following restrictions: 10 | // 1. The origin of this software must not be misrepresented; you must not 11 | // claim that you wrote the original software. If you use this software 12 | // in a product, an acknowledgment in the product documentation would be 13 | // appreciated but is not required. 14 | // 2. Altered source versions must be plainly marked as such, and must not be 15 | // misrepresented as being the original software. 16 | // 3. This notice may not be removed or altered from any source distribution. 17 | // 18 | 19 | #ifndef FONS_H 20 | #define FONS_H 21 | 22 | #define FONS_INVALID -1 23 | 24 | enum FONSflags { 25 | FONS_ZERO_TOPLEFT = 1, 26 | FONS_ZERO_BOTTOMLEFT = 2, 27 | }; 28 | 29 | enum FONSalign { 30 | // Horizontal align 31 | FONS_ALIGN_LEFT = 1<<0, // Default 32 | FONS_ALIGN_CENTER = 1<<1, 33 | FONS_ALIGN_RIGHT = 1<<2, 34 | // Vertical align 35 | FONS_ALIGN_TOP = 1<<3, 36 | FONS_ALIGN_MIDDLE = 1<<4, 37 | FONS_ALIGN_BOTTOM = 1<<5, 38 | FONS_ALIGN_BASELINE = 1<<6, // Default 39 | }; 40 | 41 | enum FONSerrorCode { 42 | // Font atlas is full. 43 | FONS_ATLAS_FULL = 1, 44 | // Scratch memory used to render glyphs is full, requested size reported in 'val', you may need to bump up FONS_SCRATCH_BUF_SIZE. 45 | FONS_SCRATCH_FULL = 2, 46 | // Calls to fonsPushState has craeted too large stack, if you need deep state stack bump up FONS_MAX_STATES. 47 | FONS_STATES_OVERFLOW = 3, 48 | // Trying to pop too many states fonsPopState(). 49 | FONS_STATES_UNDERFLOW = 4, 50 | }; 51 | 52 | struct FONSparams { 53 | int width, height; 54 | unsigned char flags; 55 | void* userPtr; 56 | int (*renderCreate)(void* uptr, int width, int height); 57 | int (*renderResize)(void* uptr, int width, int height); 58 | void (*renderUpdate)(void* uptr, int* rect, const unsigned char* data); 59 | void (*renderDraw)(void* uptr, const float* verts, const float* tcoords, const unsigned int* colors, int nverts); 60 | void (*renderDelete)(void* uptr); 61 | }; 62 | typedef struct FONSparams FONSparams; 63 | 64 | struct FONSquad 65 | { 66 | float x0,y0,s0,t0; 67 | float x1,y1,s1,t1; 68 | }; 69 | typedef struct FONSquad FONSquad; 70 | 71 | struct FONStextIter { 72 | float x, y, nextx, nexty, scale, spacing; 73 | unsigned int codepoint; 74 | short isize, iblur; 75 | struct FONSfont* font; 76 | int prevGlyphIndex; 77 | const char* str; 78 | const char* next; 79 | const char* end; 80 | unsigned int utf8state; 81 | }; 82 | typedef struct FONStextIter FONStextIter; 83 | 84 | typedef struct FONScontext FONScontext; 85 | 86 | // Contructor and destructor. 87 | FONScontext* fonsCreateInternal(FONSparams* params); 88 | void fonsDeleteInternal(FONScontext* s); 89 | 90 | void fonsSetErrorCallback(FONScontext* s, void (*callback)(void* uptr, int error, int val), void* uptr); 91 | // Returns current atlas size. 92 | void fonsGetAtlasSize(FONScontext* s, int* width, int* height); 93 | // Expands the atlas size. 94 | int fonsExpandAtlas(FONScontext* s, int width, int height); 95 | // Reseta the whole stash. 96 | int fonsResetAtlas(FONScontext* stash, int width, int height); 97 | 98 | // Add fonts 99 | int fonsAddFont(FONScontext* s, const char* name, const char* path); 100 | int fonsAddFontMem(FONScontext* s, const char* name, unsigned char* data, int ndata, int freeData); 101 | int fonsGetFontByName(FONScontext* s, const char* name); 102 | 103 | // State handling 104 | void fonsPushState(FONScontext* s); 105 | void fonsPopState(FONScontext* s); 106 | void fonsClearState(FONScontext* s); 107 | 108 | // State setting 109 | void fonsSetSize(FONScontext* s, float size); 110 | void fonsSetColor(FONScontext* s, unsigned int color); 111 | void fonsSetSpacing(FONScontext* s, float spacing); 112 | void fonsSetBlur(FONScontext* s, float blur); 113 | void fonsSetAlign(FONScontext* s, int align); 114 | void fonsSetFont(FONScontext* s, int font); 115 | 116 | // Draw text 117 | float fonsDrawText(FONScontext* s, float x, float y, const char* string, const char* end); 118 | 119 | // Measure text 120 | float fonsTextBounds(FONScontext* s, float x, float y, const char* string, const char* end, float* bounds); 121 | void fonsLineBounds(FONScontext* s, float y, float* miny, float* maxy); 122 | void fonsVertMetrics(FONScontext* s, float* ascender, float* descender, float* lineh); 123 | 124 | // Text iterator 125 | int fonsTextIterInit(FONScontext* stash, FONStextIter* iter, float x, float y, const char* str, const char* end); 126 | int fonsTextIterNext(FONScontext* stash, FONStextIter* iter, struct FONSquad* quad); 127 | 128 | // Pull texture changes 129 | const unsigned char* fonsGetTextureData(FONScontext* stash, int* width, int* height); 130 | int fonsValidateTexture(FONScontext* s, int* dirty); 131 | 132 | // Draws the stash texture for debugging 133 | void fonsDrawDebug(FONScontext* s, float x, float y); 134 | 135 | #endif // FONTSTASH_H 136 | 137 | 138 | #ifdef FONTSTASH_IMPLEMENTATION 139 | 140 | #define FONS_NOTUSED(v) (void)sizeof(v) 141 | 142 | #ifdef FONS_USE_FREETYPE 143 | 144 | #include 145 | #include FT_FREETYPE_H 146 | #include FT_ADVANCES_H 147 | #include 148 | 149 | struct FONSttFontImpl { 150 | FT_Face font; 151 | }; 152 | typedef struct FONSttFontImpl FONSttFontImpl; 153 | 154 | static FT_Library ftLibrary; 155 | 156 | int fons__tt_init(FONScontext *context) 157 | { 158 | FT_Error ftError; 159 | FONS_NOTUSED(context); 160 | ftError = FT_Init_FreeType(&ftLibrary); 161 | return ftError == 0; 162 | } 163 | 164 | int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, unsigned char *data, int dataSize) 165 | { 166 | FT_Error ftError; 167 | FONS_NOTUSED(context); 168 | 169 | //font->font.userdata = stash; 170 | ftError = FT_New_Memory_Face(ftLibrary, (const FT_Byte*)data, dataSize, 0, &font->font); 171 | return ftError == 0; 172 | } 173 | 174 | void fons__tt_getFontVMetrics(FONSttFontImpl *font, int *ascent, int *descent, int *lineGap) 175 | { 176 | *ascent = font->font->ascender; 177 | *descent = font->font->descender; 178 | *lineGap = font->font->height - (*ascent - *descent); 179 | } 180 | 181 | float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size) 182 | { 183 | return size / (font->font->ascender - font->font->descender); 184 | } 185 | 186 | int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint) 187 | { 188 | return FT_Get_Char_Index(font->font, codepoint); 189 | } 190 | 191 | int fons__tt_buildGlyphBitmap(FONSttFontImpl *font, int glyph, float size, float scale, 192 | int *advance, int *lsb, int *x0, int *y0, int *x1, int *y1) 193 | { 194 | FT_Error ftError; 195 | FT_GlyphSlot ftGlyph; 196 | FONS_NOTUSED(scale); 197 | 198 | ftError = FT_Set_Pixel_Sizes(font->font, 0, (FT_UInt)(size * (float)font->font->units_per_EM / (float)(font->font->ascender - font->font->descender))); 199 | if (ftError) return 0; 200 | ftError = FT_Load_Glyph(font->font, glyph, FT_LOAD_RENDER); 201 | if (ftError) return 0; 202 | ftError = FT_Get_Advance(font->font, glyph, FT_LOAD_NO_SCALE, (FT_Fixed*)advance); 203 | if (ftError) return 0; 204 | ftGlyph = font->font->glyph; 205 | *lsb = ftGlyph->metrics.horiBearingX; 206 | *x0 = ftGlyph->bitmap_left; 207 | *x1 = *x0 + ftGlyph->bitmap.width; 208 | *y0 = -ftGlyph->bitmap_top; 209 | *y1 = *y0 + ftGlyph->bitmap.rows; 210 | return 1; 211 | } 212 | 213 | void fons__tt_renderGlyphBitmap(FONSttFontImpl *font, unsigned char *output, int outWidth, int outHeight, int outStride, 214 | float scaleX, float scaleY, int glyph) 215 | { 216 | FT_GlyphSlot ftGlyph = font->font->glyph; 217 | int ftGlyphOffset = 0; 218 | int x, y; 219 | FONS_NOTUSED(outWidth); 220 | FONS_NOTUSED(outHeight); 221 | FONS_NOTUSED(scaleX); 222 | FONS_NOTUSED(scaleY); 223 | FONS_NOTUSED(glyph); // glyph has already been loaded by fons__tt_buildGlyphBitmap 224 | 225 | for ( y = 0; y < ftGlyph->bitmap.rows; y++ ) { 226 | for ( x = 0; x < ftGlyph->bitmap.width; x++ ) { 227 | output[(y * outStride) + x] = ftGlyph->bitmap.buffer[ftGlyphOffset++]; 228 | } 229 | } 230 | } 231 | 232 | int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2) 233 | { 234 | FT_Vector ftKerning; 235 | FT_Get_Kerning(font->font, glyph1, glyph2, FT_KERNING_DEFAULT, &ftKerning); 236 | return ftKerning.x; 237 | } 238 | 239 | #else 240 | 241 | #define STB_TRUETYPE_IMPLEMENTATION 242 | static void* fons__tmpalloc(size_t size, void* up); 243 | static void fons__tmpfree(void* ptr, void* up); 244 | #define STBTT_malloc(x,u) fons__tmpalloc(x,u) 245 | #define STBTT_free(x,u) fons__tmpfree(x,u) 246 | #include "stb_truetype.h" 247 | 248 | struct FONSttFontImpl { 249 | stbtt_fontinfo font; 250 | }; 251 | typedef struct FONSttFontImpl FONSttFontImpl; 252 | 253 | int fons__tt_init(FONScontext *context) 254 | { 255 | FONS_NOTUSED(context); 256 | return 1; 257 | } 258 | 259 | int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, unsigned char *data, int dataSize) 260 | { 261 | int stbError; 262 | FONS_NOTUSED(dataSize); 263 | 264 | font->font.userdata = context; 265 | stbError = stbtt_InitFont(&font->font, data, 0); 266 | return stbError; 267 | } 268 | 269 | void fons__tt_getFontVMetrics(FONSttFontImpl *font, int *ascent, int *descent, int *lineGap) 270 | { 271 | stbtt_GetFontVMetrics(&font->font, ascent, descent, lineGap); 272 | } 273 | 274 | float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size) 275 | { 276 | return stbtt_ScaleForPixelHeight(&font->font, size); 277 | } 278 | 279 | int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint) 280 | { 281 | return stbtt_FindGlyphIndex(&font->font, codepoint); 282 | } 283 | 284 | int fons__tt_buildGlyphBitmap(FONSttFontImpl *font, int glyph, float size, float scale, 285 | int *advance, int *lsb, int *x0, int *y0, int *x1, int *y1) 286 | { 287 | FONS_NOTUSED(size); 288 | stbtt_GetGlyphHMetrics(&font->font, glyph, advance, lsb); 289 | stbtt_GetGlyphBitmapBox(&font->font, glyph, scale, scale, x0, y0, x1, y1); 290 | return 1; 291 | } 292 | 293 | void fons__tt_renderGlyphBitmap(FONSttFontImpl *font, unsigned char *output, int outWidth, int outHeight, int outStride, 294 | float scaleX, float scaleY, int glyph) 295 | { 296 | stbtt_MakeGlyphBitmap(&font->font, output, outWidth, outHeight, outStride, scaleX, scaleY, glyph); 297 | } 298 | 299 | int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2) 300 | { 301 | return stbtt_GetGlyphKernAdvance(&font->font, glyph1, glyph2); 302 | } 303 | 304 | #endif 305 | 306 | #ifndef FONS_SCRATCH_BUF_SIZE 307 | # define FONS_SCRATCH_BUF_SIZE 16000 308 | #endif 309 | #ifndef FONS_HASH_LUT_SIZE 310 | # define FONS_HASH_LUT_SIZE 256 311 | #endif 312 | #ifndef FONS_INIT_FONTS 313 | # define FONS_INIT_FONTS 4 314 | #endif 315 | #ifndef FONS_INIT_GLYPHS 316 | # define FONS_INIT_GLYPHS 256 317 | #endif 318 | #ifndef FONS_INIT_ATLAS_NODES 319 | # define FONS_INIT_ATLAS_NODES 256 320 | #endif 321 | #ifndef FONS_VERTEX_COUNT 322 | # define FONS_VERTEX_COUNT 1024 323 | #endif 324 | #ifndef FONS_MAX_STATES 325 | # define FONS_MAX_STATES 20 326 | #endif 327 | 328 | static unsigned int fons__hashint(unsigned int a) 329 | { 330 | a += ~(a<<15); 331 | a ^= (a>>10); 332 | a += (a<<3); 333 | a ^= (a>>6); 334 | a += ~(a<<11); 335 | a ^= (a>>16); 336 | return a; 337 | } 338 | 339 | static int fons__mini(int a, int b) 340 | { 341 | return a < b ? a : b; 342 | } 343 | 344 | static int fons__maxi(int a, int b) 345 | { 346 | return a > b ? a : b; 347 | } 348 | 349 | struct FONSglyph 350 | { 351 | unsigned int codepoint; 352 | int index; 353 | int next; 354 | short size, blur; 355 | short x0,y0,x1,y1; 356 | short xadv,xoff,yoff; 357 | }; 358 | typedef struct FONSglyph FONSglyph; 359 | 360 | struct FONSfont 361 | { 362 | FONSttFontImpl font; 363 | char name[64]; 364 | unsigned char* data; 365 | int dataSize; 366 | unsigned char freeData; 367 | float ascender; 368 | float descender; 369 | float lineh; 370 | FONSglyph* glyphs; 371 | int cglyphs; 372 | int nglyphs; 373 | int lut[FONS_HASH_LUT_SIZE]; 374 | }; 375 | typedef struct FONSfont FONSfont; 376 | 377 | struct FONSstate 378 | { 379 | int font; 380 | int align; 381 | float size; 382 | unsigned int color; 383 | float blur; 384 | float spacing; 385 | }; 386 | typedef struct FONSstate FONSstate; 387 | 388 | struct FONSatlasNode { 389 | short x, y, width; 390 | }; 391 | typedef struct FONSatlasNode FONSatlasNode; 392 | 393 | struct FONSatlas 394 | { 395 | int width, height; 396 | FONSatlasNode* nodes; 397 | int nnodes; 398 | int cnodes; 399 | }; 400 | typedef struct FONSatlas FONSatlas; 401 | 402 | struct FONScontext 403 | { 404 | FONSparams params; 405 | float itw,ith; 406 | unsigned char* texData; 407 | int dirtyRect[4]; 408 | FONSfont** fonts; 409 | FONSatlas* atlas; 410 | int cfonts; 411 | int nfonts; 412 | float verts[FONS_VERTEX_COUNT*2]; 413 | float tcoords[FONS_VERTEX_COUNT*2]; 414 | unsigned int colors[FONS_VERTEX_COUNT]; 415 | int nverts; 416 | unsigned char* scratch; 417 | int nscratch; 418 | FONSstate states[FONS_MAX_STATES]; 419 | int nstates; 420 | void (*handleError)(void* uptr, int error, int val); 421 | void* errorUptr; 422 | }; 423 | 424 | static void* fons__tmpalloc(size_t size, void* up) 425 | { 426 | unsigned char* ptr; 427 | FONScontext* stash = (FONScontext*)up; 428 | 429 | // 16-byte align the returned pointer 430 | size = (size + 0xf) & ~0xf; 431 | 432 | if (stash->nscratch+(int)size > FONS_SCRATCH_BUF_SIZE) { 433 | if (stash->handleError) 434 | stash->handleError(stash->errorUptr, FONS_SCRATCH_FULL, stash->nscratch+(int)size); 435 | return NULL; 436 | } 437 | ptr = stash->scratch + stash->nscratch; 438 | stash->nscratch += (int)size; 439 | return ptr; 440 | } 441 | 442 | static void fons__tmpfree(void* ptr, void* up) 443 | { 444 | (void)ptr; 445 | (void)up; 446 | // empty 447 | } 448 | 449 | // Copyright (c) 2008-2010 Bjoern Hoehrmann 450 | // See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. 451 | 452 | #define FONS_UTF8_ACCEPT 0 453 | #define FONS_UTF8_REJECT 12 454 | 455 | static unsigned int fons__decutf8(unsigned int* state, unsigned int* codep, unsigned int byte) 456 | { 457 | static const unsigned char utf8d[] = { 458 | // The first part of the table maps bytes to character classes that 459 | // to reduce the size of the transition table and create bitmasks. 460 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 461 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 462 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 463 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 464 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 465 | 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 466 | 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 467 | 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, 468 | 469 | // The second part is a transition table that maps a combination 470 | // of a state of the automaton and a character class to a state. 471 | 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12, 472 | 12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12, 473 | 12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12, 474 | 12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12, 475 | 12,36,12,12,12,12,12,12,12,12,12,12, 476 | }; 477 | 478 | unsigned int type = utf8d[byte]; 479 | 480 | *codep = (*state != FONS_UTF8_ACCEPT) ? 481 | (byte & 0x3fu) | (*codep << 6) : 482 | (0xff >> type) & (byte); 483 | 484 | *state = utf8d[256 + *state + type]; 485 | return *state; 486 | } 487 | 488 | // Atlas based on Skyline Bin Packer by Jukka Jylänki 489 | 490 | static void fons__deleteAtlas(FONSatlas* atlas) 491 | { 492 | if (atlas == NULL) return; 493 | if (atlas->nodes != NULL) free(atlas->nodes); 494 | free(atlas); 495 | } 496 | 497 | static FONSatlas* fons__allocAtlas(int w, int h, int nnodes) 498 | { 499 | FONSatlas* atlas = NULL; 500 | 501 | // Allocate memory for the font stash. 502 | atlas = (FONSatlas*)malloc(sizeof(FONSatlas)); 503 | if (atlas == NULL) goto error; 504 | memset(atlas, 0, sizeof(FONSatlas)); 505 | 506 | atlas->width = w; 507 | atlas->height = h; 508 | 509 | // Allocate space for skyline nodes 510 | atlas->nodes = (FONSatlasNode*)malloc(sizeof(FONSatlasNode) * nnodes); 511 | if (atlas->nodes == NULL) goto error; 512 | memset(atlas->nodes, 0, sizeof(FONSatlasNode) * nnodes); 513 | atlas->nnodes = 0; 514 | atlas->cnodes = nnodes; 515 | 516 | // Init root node. 517 | atlas->nodes[0].x = 0; 518 | atlas->nodes[0].y = 0; 519 | atlas->nodes[0].width = (short)w; 520 | atlas->nnodes++; 521 | 522 | return atlas; 523 | 524 | error: 525 | if (atlas) fons__deleteAtlas(atlas); 526 | return NULL; 527 | } 528 | 529 | static int fons__atlasInsertNode(FONSatlas* atlas, int idx, int x, int y, int w) 530 | { 531 | int i; 532 | // Insert node 533 | if (atlas->nnodes+1 > atlas->cnodes) { 534 | atlas->cnodes = atlas->cnodes == 0 ? 8 : atlas->cnodes * 2; 535 | atlas->nodes = (FONSatlasNode*)realloc(atlas->nodes, sizeof(FONSatlasNode) * atlas->cnodes); 536 | if (atlas->nodes == NULL) 537 | return 0; 538 | } 539 | for (i = atlas->nnodes; i > idx; i--) 540 | atlas->nodes[i] = atlas->nodes[i-1]; 541 | atlas->nodes[idx].x = (short)x; 542 | atlas->nodes[idx].y = (short)y; 543 | atlas->nodes[idx].width = (short)w; 544 | atlas->nnodes++; 545 | 546 | return 1; 547 | } 548 | 549 | static void fons__atlasRemoveNode(FONSatlas* atlas, int idx) 550 | { 551 | int i; 552 | if (atlas->nnodes == 0) return; 553 | for (i = idx; i < atlas->nnodes-1; i++) 554 | atlas->nodes[i] = atlas->nodes[i+1]; 555 | atlas->nnodes--; 556 | } 557 | 558 | static void fons__atlasExpand(FONSatlas* atlas, int w, int h) 559 | { 560 | // Insert node for empty space 561 | if (w > atlas->width) 562 | fons__atlasInsertNode(atlas, atlas->nnodes, atlas->width, 0, w - atlas->width); 563 | atlas->width = w; 564 | atlas->height = h; 565 | } 566 | 567 | static void fons__atlasReset(FONSatlas* atlas, int w, int h) 568 | { 569 | atlas->width = w; 570 | atlas->height = h; 571 | atlas->nnodes = 0; 572 | 573 | // Init root node. 574 | atlas->nodes[0].x = 0; 575 | atlas->nodes[0].y = 0; 576 | atlas->nodes[0].width = (short)w; 577 | atlas->nnodes++; 578 | } 579 | 580 | static int fons__atlasAddSkylineLevel(FONSatlas* atlas, int idx, int x, int y, int w, int h) 581 | { 582 | int i; 583 | 584 | // Insert new node 585 | if (fons__atlasInsertNode(atlas, idx, x, y+h, w) == 0) 586 | return 0; 587 | 588 | // Delete skyline segments that fall under the shaodw of the new segment. 589 | for (i = idx+1; i < atlas->nnodes; i++) { 590 | if (atlas->nodes[i].x < atlas->nodes[i-1].x + atlas->nodes[i-1].width) { 591 | int shrink = atlas->nodes[i-1].x + atlas->nodes[i-1].width - atlas->nodes[i].x; 592 | atlas->nodes[i].x += (short)shrink; 593 | atlas->nodes[i].width -= (short)shrink; 594 | if (atlas->nodes[i].width <= 0) { 595 | fons__atlasRemoveNode(atlas, i); 596 | i--; 597 | } else { 598 | break; 599 | } 600 | } else { 601 | break; 602 | } 603 | } 604 | 605 | // Merge same height skyline segments that are next to each other. 606 | for (i = 0; i < atlas->nnodes-1; i++) { 607 | if (atlas->nodes[i].y == atlas->nodes[i+1].y) { 608 | atlas->nodes[i].width += atlas->nodes[i+1].width; 609 | fons__atlasRemoveNode(atlas, i+1); 610 | i--; 611 | } 612 | } 613 | 614 | return 1; 615 | } 616 | 617 | static int fons__atlasRectFits(FONSatlas* atlas, int i, int w, int h) 618 | { 619 | // Checks if there is enough space at the location of skyline span 'i', 620 | // and return the max height of all skyline spans under that at that location, 621 | // (think tetris block being dropped at that position). Or -1 if no space found. 622 | int x = atlas->nodes[i].x; 623 | int y = atlas->nodes[i].y; 624 | int spaceLeft; 625 | if (x + w > atlas->width) 626 | return -1; 627 | spaceLeft = w; 628 | while (spaceLeft > 0) { 629 | if (i == atlas->nnodes) return -1; 630 | y = fons__maxi(y, atlas->nodes[i].y); 631 | if (y + h > atlas->height) return -1; 632 | spaceLeft -= atlas->nodes[i].width; 633 | ++i; 634 | } 635 | return y; 636 | } 637 | 638 | static int fons__atlasAddRect(FONSatlas* atlas, int rw, int rh, int* rx, int* ry) 639 | { 640 | int besth = atlas->height, bestw = atlas->width, besti = -1; 641 | int bestx = -1, besty = -1, i; 642 | 643 | // Bottom left fit heuristic. 644 | for (i = 0; i < atlas->nnodes; i++) { 645 | int y = fons__atlasRectFits(atlas, i, rw, rh); 646 | if (y != -1) { 647 | if (y + rh < besth || (y + rh == besth && atlas->nodes[i].width < bestw)) { 648 | besti = i; 649 | bestw = atlas->nodes[i].width; 650 | besth = y + rh; 651 | bestx = atlas->nodes[i].x; 652 | besty = y; 653 | } 654 | } 655 | } 656 | 657 | if (besti == -1) 658 | return 0; 659 | 660 | // Perform the actual packing. 661 | if (fons__atlasAddSkylineLevel(atlas, besti, bestx, besty, rw, rh) == 0) 662 | return 0; 663 | 664 | *rx = bestx; 665 | *ry = besty; 666 | 667 | return 1; 668 | } 669 | 670 | static void fons__addWhiteRect(FONScontext* stash, int w, int h) 671 | { 672 | int x, y, gx, gy; 673 | unsigned char* dst; 674 | if (fons__atlasAddRect(stash->atlas, w, h, &gx, &gy) == 0) 675 | return; 676 | 677 | // Rasterize 678 | dst = &stash->texData[gx + gy * stash->params.width]; 679 | for (y = 0; y < h; y++) { 680 | for (x = 0; x < w; x++) 681 | dst[x] = 0xff; 682 | dst += stash->params.width; 683 | } 684 | 685 | stash->dirtyRect[0] = fons__mini(stash->dirtyRect[0], gx); 686 | stash->dirtyRect[1] = fons__mini(stash->dirtyRect[1], gy); 687 | stash->dirtyRect[2] = fons__maxi(stash->dirtyRect[2], gx+w); 688 | stash->dirtyRect[3] = fons__maxi(stash->dirtyRect[3], gy+h); 689 | } 690 | 691 | FONScontext* fonsCreateInternal(FONSparams* params) 692 | { 693 | FONScontext* stash = NULL; 694 | 695 | // Allocate memory for the font stash. 696 | stash = (FONScontext*)malloc(sizeof(FONScontext)); 697 | if (stash == NULL) goto error; 698 | memset(stash, 0, sizeof(FONScontext)); 699 | 700 | stash->params = *params; 701 | 702 | // Allocate scratch buffer. 703 | stash->scratch = (unsigned char*)malloc(FONS_SCRATCH_BUF_SIZE); 704 | if (stash->scratch == NULL) goto error; 705 | 706 | // Initialize implementation library 707 | if (!fons__tt_init(stash)) goto error; 708 | 709 | if (stash->params.renderCreate != NULL) { 710 | if (stash->params.renderCreate(stash->params.userPtr, stash->params.width, stash->params.height) == 0) 711 | goto error; 712 | } 713 | 714 | stash->atlas = fons__allocAtlas(stash->params.width, stash->params.height, FONS_INIT_ATLAS_NODES); 715 | if (stash->atlas == NULL) goto error; 716 | 717 | // Allocate space for fonts. 718 | stash->fonts = (FONSfont**)malloc(sizeof(FONSfont*) * FONS_INIT_FONTS); 719 | if (stash->fonts == NULL) goto error; 720 | memset(stash->fonts, 0, sizeof(FONSfont*) * FONS_INIT_FONTS); 721 | stash->cfonts = FONS_INIT_FONTS; 722 | stash->nfonts = 0; 723 | 724 | // Create texture for the cache. 725 | stash->itw = 1.0f/stash->params.width; 726 | stash->ith = 1.0f/stash->params.height; 727 | stash->texData = (unsigned char*)malloc(stash->params.width * stash->params.height); 728 | if (stash->texData == NULL) goto error; 729 | memset(stash->texData, 0, stash->params.width * stash->params.height); 730 | 731 | stash->dirtyRect[0] = stash->params.width; 732 | stash->dirtyRect[1] = stash->params.height; 733 | stash->dirtyRect[2] = 0; 734 | stash->dirtyRect[3] = 0; 735 | 736 | // Add white rect at 0,0 for debug drawing. 737 | fons__addWhiteRect(stash, 2,2); 738 | 739 | fonsPushState(stash); 740 | fonsClearState(stash); 741 | 742 | return stash; 743 | 744 | error: 745 | fonsDeleteInternal(stash); 746 | return NULL; 747 | } 748 | 749 | static FONSstate* fons__getState(FONScontext* stash) 750 | { 751 | return &stash->states[stash->nstates-1]; 752 | } 753 | 754 | void fonsSetSize(FONScontext* stash, float size) 755 | { 756 | fons__getState(stash)->size = size; 757 | } 758 | 759 | void fonsSetColor(FONScontext* stash, unsigned int color) 760 | { 761 | fons__getState(stash)->color = color; 762 | } 763 | 764 | void fonsSetSpacing(FONScontext* stash, float spacing) 765 | { 766 | fons__getState(stash)->spacing = spacing; 767 | } 768 | 769 | void fonsSetBlur(FONScontext* stash, float blur) 770 | { 771 | fons__getState(stash)->blur = blur; 772 | } 773 | 774 | void fonsSetAlign(FONScontext* stash, int align) 775 | { 776 | fons__getState(stash)->align = align; 777 | } 778 | 779 | void fonsSetFont(FONScontext* stash, int font) 780 | { 781 | fons__getState(stash)->font = font; 782 | } 783 | 784 | void fonsPushState(FONScontext* stash) 785 | { 786 | if (stash->nstates >= FONS_MAX_STATES) { 787 | if (stash->handleError) 788 | stash->handleError(stash->errorUptr, FONS_STATES_OVERFLOW, 0); 789 | return; 790 | } 791 | if (stash->nstates > 0) 792 | memcpy(&stash->states[stash->nstates], &stash->states[stash->nstates-1], sizeof(FONSstate)); 793 | stash->nstates++; 794 | } 795 | 796 | void fonsPopState(FONScontext* stash) 797 | { 798 | if (stash->nstates <= 1) { 799 | if (stash->handleError) 800 | stash->handleError(stash->errorUptr, FONS_STATES_UNDERFLOW, 0); 801 | return; 802 | } 803 | stash->nstates--; 804 | } 805 | 806 | void fonsClearState(FONScontext* stash) 807 | { 808 | FONSstate* state = fons__getState(stash); 809 | state->size = 12.0f; 810 | state->color = 0xffffffff; 811 | state->font = 0; 812 | state->blur = 0; 813 | state->spacing = 0; 814 | state->align = FONS_ALIGN_LEFT | FONS_ALIGN_BASELINE; 815 | } 816 | 817 | static void fons__freeFont(FONSfont* font) 818 | { 819 | if (font == NULL) return; 820 | if (font->glyphs) free(font->glyphs); 821 | if (font->freeData && font->data) free(font->data); 822 | free(font); 823 | } 824 | 825 | static int fons__allocFont(FONScontext* stash) 826 | { 827 | FONSfont* font = NULL; 828 | if (stash->nfonts+1 > stash->cfonts) { 829 | stash->cfonts = stash->cfonts == 0 ? 8 : stash->cfonts * 2; 830 | stash->fonts = (FONSfont**)realloc(stash->fonts, sizeof(FONSfont*) * stash->cfonts); 831 | if (stash->fonts == NULL) 832 | return -1; 833 | } 834 | font = (FONSfont*)malloc(sizeof(FONSfont)); 835 | if (font == NULL) goto error; 836 | memset(font, 0, sizeof(FONSfont)); 837 | 838 | font->glyphs = (FONSglyph*)malloc(sizeof(FONSglyph) * FONS_INIT_GLYPHS); 839 | if (font->glyphs == NULL) goto error; 840 | font->cglyphs = FONS_INIT_GLYPHS; 841 | font->nglyphs = 0; 842 | 843 | stash->fonts[stash->nfonts++] = font; 844 | return stash->nfonts-1; 845 | 846 | error: 847 | fons__freeFont(font); 848 | 849 | return FONS_INVALID; 850 | } 851 | 852 | int fonsAddFont(FONScontext* stash, const char* name, const char* path) 853 | { 854 | FILE* fp = 0; 855 | int dataSize = 0; 856 | unsigned char* data = NULL; 857 | 858 | // Read in the font data. 859 | fp = fopen(path, "rb"); 860 | if (fp == NULL) goto error; 861 | fseek(fp,0,SEEK_END); 862 | dataSize = (int)ftell(fp); 863 | fseek(fp,0,SEEK_SET); 864 | data = (unsigned char*)malloc(dataSize); 865 | if (data == NULL) goto error; 866 | fread(data, 1, dataSize, fp); 867 | fclose(fp); 868 | fp = 0; 869 | 870 | return fonsAddFontMem(stash, name, data, dataSize, 1); 871 | 872 | error: 873 | if (data) free(data); 874 | if (fp) fclose(fp); 875 | return FONS_INVALID; 876 | } 877 | 878 | int fonsAddFontMem(FONScontext* stash, const char* name, unsigned char* data, int dataSize, int freeData) 879 | { 880 | int i, ascent, descent, fh, lineGap; 881 | FONSfont* font; 882 | 883 | int idx = fons__allocFont(stash); 884 | if (idx == FONS_INVALID) 885 | return FONS_INVALID; 886 | 887 | font = stash->fonts[idx]; 888 | 889 | strncpy(font->name, name, sizeof(font->name)); 890 | font->name[sizeof(font->name)-1] = '\0'; 891 | 892 | // Init hash lookup. 893 | for (i = 0; i < FONS_HASH_LUT_SIZE; ++i) 894 | font->lut[i] = -1; 895 | 896 | // Read in the font data. 897 | font->dataSize = dataSize; 898 | font->data = data; 899 | font->freeData = (unsigned char)freeData; 900 | 901 | // Init font 902 | stash->nscratch = 0; 903 | if (!fons__tt_loadFont(stash, &font->font, data, dataSize)) goto error; 904 | 905 | // Store normalized line height. The real line height is got 906 | // by multiplying the lineh by font size. 907 | fons__tt_getFontVMetrics( &font->font, &ascent, &descent, &lineGap); 908 | fh = ascent - descent; 909 | font->ascender = (float)ascent / (float)fh; 910 | font->descender = (float)descent / (float)fh; 911 | font->lineh = (float)(fh + lineGap) / (float)fh; 912 | 913 | return idx; 914 | 915 | error: 916 | fons__freeFont(font); 917 | stash->nfonts--; 918 | return FONS_INVALID; 919 | } 920 | 921 | int fonsGetFontByName(FONScontext* s, const char* name) 922 | { 923 | int i; 924 | for (i = 0; i < s->nfonts; i++) { 925 | if (strcmp(s->fonts[i]->name, name) == 0) 926 | return i; 927 | } 928 | return FONS_INVALID; 929 | } 930 | 931 | 932 | static FONSglyph* fons__allocGlyph(FONSfont* font) 933 | { 934 | if (font->nglyphs+1 > font->cglyphs) { 935 | font->cglyphs = font->cglyphs == 0 ? 8 : font->cglyphs * 2; 936 | font->glyphs = (FONSglyph*)realloc(font->glyphs, sizeof(FONSglyph) * font->cglyphs); 937 | if (font->glyphs == NULL) return NULL; 938 | } 939 | font->nglyphs++; 940 | return &font->glyphs[font->nglyphs-1]; 941 | } 942 | 943 | 944 | // Based on Exponential blur, Jani Huhtanen, 2006 945 | 946 | #define APREC 16 947 | #define ZPREC 7 948 | 949 | static void fons__blurCols(unsigned char* dst, int w, int h, int dstStride, int alpha) 950 | { 951 | int x, y; 952 | for (y = 0; y < h; y++) { 953 | int z = 0; // force zero border 954 | for (x = 1; x < w; x++) { 955 | z += (alpha * (((int)(dst[x]) << ZPREC) - z)) >> APREC; 956 | dst[x] = (unsigned char)(z >> ZPREC); 957 | } 958 | dst[w-1] = 0; // force zero border 959 | z = 0; 960 | for (x = w-2; x >= 0; x--) { 961 | z += (alpha * (((int)(dst[x]) << ZPREC) - z)) >> APREC; 962 | dst[x] = (unsigned char)(z >> ZPREC); 963 | } 964 | dst[0] = 0; // force zero border 965 | dst += dstStride; 966 | } 967 | } 968 | 969 | static void fons__blurRows(unsigned char* dst, int w, int h, int dstStride, int alpha) 970 | { 971 | int x, y; 972 | for (x = 0; x < w; x++) { 973 | int z = 0; // force zero border 974 | for (y = dstStride; y < h*dstStride; y += dstStride) { 975 | z += (alpha * (((int)(dst[y]) << ZPREC) - z)) >> APREC; 976 | dst[y] = (unsigned char)(z >> ZPREC); 977 | } 978 | dst[(h-1)*dstStride] = 0; // force zero border 979 | z = 0; 980 | for (y = (h-2)*dstStride; y >= 0; y -= dstStride) { 981 | z += (alpha * (((int)(dst[y]) << ZPREC) - z)) >> APREC; 982 | dst[y] = (unsigned char)(z >> ZPREC); 983 | } 984 | dst[0] = 0; // force zero border 985 | dst++; 986 | } 987 | } 988 | 989 | 990 | static void fons__blur(FONScontext* stash, unsigned char* dst, int w, int h, int dstStride, int blur) 991 | { 992 | int alpha; 993 | float sigma; 994 | (void)stash; 995 | 996 | if (blur < 1) 997 | return; 998 | // Calculate the alpha such that 90% of the kernel is within the radius. (Kernel extends to infinity) 999 | sigma = (float)blur * 0.57735f; // 1 / sqrt(3) 1000 | alpha = (int)((1< 20) iblur = 20; 1023 | pad = iblur+2; 1024 | 1025 | // Reset allocator. 1026 | stash->nscratch = 0; 1027 | 1028 | // Find code point and size. 1029 | h = fons__hashint(codepoint) & (FONS_HASH_LUT_SIZE-1); 1030 | i = font->lut[h]; 1031 | while (i != -1) { 1032 | if (font->glyphs[i].codepoint == codepoint && font->glyphs[i].size == isize && font->glyphs[i].blur == iblur) 1033 | return &font->glyphs[i]; 1034 | i = font->glyphs[i].next; 1035 | } 1036 | 1037 | // Could not find glyph, create it. 1038 | scale = fons__tt_getPixelHeightScale(&font->font, size); 1039 | g = fons__tt_getGlyphIndex(&font->font, codepoint); 1040 | fons__tt_buildGlyphBitmap(&font->font, g, size, scale, &advance, &lsb, &x0, &y0, &x1, &y1); 1041 | gw = x1-x0 + pad*2; 1042 | gh = y1-y0 + pad*2; 1043 | 1044 | // Find free spot for the rect in the atlas 1045 | added = fons__atlasAddRect(stash->atlas, gw, gh, &gx, &gy); 1046 | if (added == 0 && stash->handleError != NULL) { 1047 | // Atlas is full, let the user to resize the atlas (or not), and try again. 1048 | stash->handleError(stash->errorUptr, FONS_ATLAS_FULL, 0); 1049 | added = fons__atlasAddRect(stash->atlas, gw, gh, &gx, &gy); 1050 | } 1051 | if (added == 0) return NULL; 1052 | 1053 | // Init glyph. 1054 | glyph = fons__allocGlyph(font); 1055 | glyph->codepoint = codepoint; 1056 | glyph->size = isize; 1057 | glyph->blur = iblur; 1058 | glyph->index = g; 1059 | glyph->x0 = (short)gx; 1060 | glyph->y0 = (short)gy; 1061 | glyph->x1 = (short)(glyph->x0+gw); 1062 | glyph->y1 = (short)(glyph->y0+gh); 1063 | glyph->xadv = (short)(scale * advance * 10.0f); 1064 | glyph->xoff = (short)(x0 - pad); 1065 | glyph->yoff = (short)(y0 - pad); 1066 | glyph->next = 0; 1067 | 1068 | // Insert char to hash lookup. 1069 | glyph->next = font->lut[h]; 1070 | font->lut[h] = font->nglyphs-1; 1071 | 1072 | // Rasterize 1073 | dst = &stash->texData[(glyph->x0+pad) + (glyph->y0+pad) * stash->params.width]; 1074 | fons__tt_renderGlyphBitmap(&font->font, dst, gw-pad*2,gh-pad*2, stash->params.width, scale,scale, g); 1075 | 1076 | // Make sure there is one pixel empty border. 1077 | dst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width]; 1078 | for (y = 0; y < gh; y++) { 1079 | dst[y*stash->params.width] = 0; 1080 | dst[gw-1 + y*stash->params.width] = 0; 1081 | } 1082 | for (x = 0; x < gw; x++) { 1083 | dst[x] = 0; 1084 | dst[x + (gh-1)*stash->params.width] = 0; 1085 | } 1086 | 1087 | // Debug code to color the glyph background 1088 | /* unsigned char* fdst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width]; 1089 | for (y = 0; y < gh; y++) { 1090 | for (x = 0; x < gw; x++) { 1091 | int a = (int)fdst[x+y*stash->params.width] + 20; 1092 | if (a > 255) a = 255; 1093 | fdst[x+y*stash->params.width] = a; 1094 | } 1095 | }*/ 1096 | 1097 | // Blur 1098 | if (iblur > 0) { 1099 | stash->nscratch = 0; 1100 | bdst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width]; 1101 | fons__blur(stash, bdst, gw,gh, stash->params.width, iblur); 1102 | } 1103 | 1104 | stash->dirtyRect[0] = fons__mini(stash->dirtyRect[0], glyph->x0); 1105 | stash->dirtyRect[1] = fons__mini(stash->dirtyRect[1], glyph->y0); 1106 | stash->dirtyRect[2] = fons__maxi(stash->dirtyRect[2], glyph->x1); 1107 | stash->dirtyRect[3] = fons__maxi(stash->dirtyRect[3], glyph->y1); 1108 | 1109 | return glyph; 1110 | } 1111 | 1112 | static void fons__getQuad(FONScontext* stash, FONSfont* font, 1113 | int prevGlyphIndex, FONSglyph* glyph, 1114 | float scale, float spacing, float* x, float* y, FONSquad* q) 1115 | { 1116 | float rx,ry,xoff,yoff,x0,y0,x1,y1; 1117 | 1118 | if (prevGlyphIndex != -1) { 1119 | float adv = fons__tt_getGlyphKernAdvance(&font->font, prevGlyphIndex, glyph->index) * scale; 1120 | *x += (int)(adv + spacing + 0.5f); 1121 | } 1122 | 1123 | // Each glyph has 2px border to allow good interpolation, 1124 | // one pixel to prevent leaking, and one to allow good interpolation for rendering. 1125 | // Inset the texture region by one pixel for corret interpolation. 1126 | xoff = (short)(glyph->xoff+1); 1127 | yoff = (short)(glyph->yoff+1); 1128 | x0 = (float)(glyph->x0+1); 1129 | y0 = (float)(glyph->y0+1); 1130 | x1 = (float)(glyph->x1-1); 1131 | y1 = (float)(glyph->y1-1); 1132 | 1133 | if (stash->params.flags & FONS_ZERO_TOPLEFT) { 1134 | rx = (float)(int)(*x + xoff); 1135 | ry = (float)(int)(*y + yoff); 1136 | 1137 | q->x0 = rx; 1138 | q->y0 = ry; 1139 | q->x1 = rx + x1 - x0; 1140 | q->y1 = ry + y1 - y0; 1141 | 1142 | q->s0 = x0 * stash->itw; 1143 | q->t0 = y0 * stash->ith; 1144 | q->s1 = x1 * stash->itw; 1145 | q->t1 = y1 * stash->ith; 1146 | } else { 1147 | rx = (float)(int)(*x + xoff); 1148 | ry = (float)(int)(*y - yoff); 1149 | 1150 | q->x0 = rx; 1151 | q->y0 = ry; 1152 | q->x1 = rx + x1 - x0; 1153 | q->y1 = ry - y1 + y0; 1154 | 1155 | q->s0 = x0 * stash->itw; 1156 | q->t0 = y0 * stash->ith; 1157 | q->s1 = x1 * stash->itw; 1158 | q->t1 = y1 * stash->ith; 1159 | } 1160 | 1161 | *x += (int)(glyph->xadv / 10.0f + 0.5f); 1162 | } 1163 | 1164 | static void fons__flush(FONScontext* stash) 1165 | { 1166 | // Flush texture 1167 | if (stash->dirtyRect[0] < stash->dirtyRect[2] && stash->dirtyRect[1] < stash->dirtyRect[3]) { 1168 | if (stash->params.renderUpdate != NULL) 1169 | stash->params.renderUpdate(stash->params.userPtr, stash->dirtyRect, stash->texData); 1170 | // Reset dirty rect 1171 | stash->dirtyRect[0] = stash->params.width; 1172 | stash->dirtyRect[1] = stash->params.height; 1173 | stash->dirtyRect[2] = 0; 1174 | stash->dirtyRect[3] = 0; 1175 | } 1176 | 1177 | // Flush triangles 1178 | if (stash->nverts > 0) { 1179 | if (stash->params.renderDraw != NULL) 1180 | stash->params.renderDraw(stash->params.userPtr, stash->verts, stash->tcoords, stash->colors, stash->nverts); 1181 | stash->nverts = 0; 1182 | } 1183 | } 1184 | 1185 | static __inline void fons__vertex(FONScontext* stash, float x, float y, float s, float t, unsigned int c) 1186 | { 1187 | stash->verts[stash->nverts*2+0] = x; 1188 | stash->verts[stash->nverts*2+1] = y; 1189 | stash->tcoords[stash->nverts*2+0] = s; 1190 | stash->tcoords[stash->nverts*2+1] = t; 1191 | stash->colors[stash->nverts] = c; 1192 | stash->nverts++; 1193 | } 1194 | 1195 | static float fons__getVertAlign(FONScontext* stash, FONSfont* font, int align, short isize) 1196 | { 1197 | if (stash->params.flags & FONS_ZERO_TOPLEFT) { 1198 | if (align & FONS_ALIGN_TOP) { 1199 | return font->ascender * (float)isize/10.0f; 1200 | } else if (align & FONS_ALIGN_MIDDLE) { 1201 | return (font->ascender + font->descender) / 2.0f * (float)isize/10.0f; 1202 | } else if (align & FONS_ALIGN_BASELINE) { 1203 | return 0.0f; 1204 | } else if (align & FONS_ALIGN_BOTTOM) { 1205 | return font->descender * (float)isize/10.0f; 1206 | } 1207 | } else { 1208 | if (align & FONS_ALIGN_TOP) { 1209 | return -font->ascender * (float)isize/10.0f; 1210 | } else if (align & FONS_ALIGN_MIDDLE) { 1211 | return -(font->ascender + font->descender) / 2.0f * (float)isize/10.0f; 1212 | } else if (align & FONS_ALIGN_BASELINE) { 1213 | return 0.0f; 1214 | } else if (align & FONS_ALIGN_BOTTOM) { 1215 | return -font->descender * (float)isize/10.0f; 1216 | } 1217 | } 1218 | return 0.0; 1219 | } 1220 | 1221 | float fonsDrawText(FONScontext* stash, 1222 | float x, float y, 1223 | const char* str, const char* end) 1224 | { 1225 | FONSstate* state = fons__getState(stash); 1226 | unsigned int codepoint; 1227 | unsigned int utf8state = 0; 1228 | FONSglyph* glyph = NULL; 1229 | FONSquad q; 1230 | int prevGlyphIndex = -1; 1231 | short isize = (short)(state->size*10.0f); 1232 | short iblur = (short)state->blur; 1233 | float scale; 1234 | FONSfont* font; 1235 | float width; 1236 | 1237 | if (stash == NULL) return x; 1238 | if (state->font < 0 || state->font >= stash->nfonts) return x; 1239 | font = stash->fonts[state->font]; 1240 | if (font->data == NULL) return x; 1241 | 1242 | scale = fons__tt_getPixelHeightScale(&font->font, (float)isize/10.0f); 1243 | 1244 | if (end == NULL) 1245 | end = str + strlen(str); 1246 | 1247 | // Align horizontally 1248 | if (state->align & FONS_ALIGN_LEFT) { 1249 | // empty 1250 | } else if (state->align & FONS_ALIGN_RIGHT) { 1251 | width = fonsTextBounds(stash, x,y, str, end, NULL); 1252 | x -= width; 1253 | } else if (state->align & FONS_ALIGN_CENTER) { 1254 | width = fonsTextBounds(stash, x,y, str, end, NULL); 1255 | x -= width * 0.5f; 1256 | } 1257 | // Align vertically. 1258 | y += fons__getVertAlign(stash, font, state->align, isize); 1259 | 1260 | for (; str != end; ++str) { 1261 | if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str)) 1262 | continue; 1263 | glyph = fons__getGlyph(stash, font, codepoint, isize, iblur); 1264 | if (glyph != NULL) { 1265 | fons__getQuad(stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q); 1266 | 1267 | if (stash->nverts+6 > FONS_VERTEX_COUNT) 1268 | fons__flush(stash); 1269 | 1270 | fons__vertex(stash, q.x0, q.y0, q.s0, q.t0, state->color); 1271 | fons__vertex(stash, q.x1, q.y1, q.s1, q.t1, state->color); 1272 | fons__vertex(stash, q.x1, q.y0, q.s1, q.t0, state->color); 1273 | 1274 | fons__vertex(stash, q.x0, q.y0, q.s0, q.t0, state->color); 1275 | fons__vertex(stash, q.x0, q.y1, q.s0, q.t1, state->color); 1276 | fons__vertex(stash, q.x1, q.y1, q.s1, q.t1, state->color); 1277 | } 1278 | prevGlyphIndex = glyph != NULL ? glyph->index : -1; 1279 | } 1280 | fons__flush(stash); 1281 | 1282 | return x; 1283 | } 1284 | 1285 | int fonsTextIterInit(FONScontext* stash, FONStextIter* iter, 1286 | float x, float y, const char* str, const char* end) 1287 | { 1288 | FONSstate* state = fons__getState(stash); 1289 | float width; 1290 | 1291 | memset(iter, 0, sizeof(*iter)); 1292 | 1293 | if (stash == NULL) return 0; 1294 | if (state->font < 0 || state->font >= stash->nfonts) return 0; 1295 | iter->font = stash->fonts[state->font]; 1296 | if (iter->font->data == NULL) return 0; 1297 | 1298 | iter->isize = (short)(state->size*10.0f); 1299 | iter->iblur = (short)state->blur; 1300 | iter->scale = fons__tt_getPixelHeightScale(&iter->font->font, (float)iter->isize/10.0f); 1301 | 1302 | // Align horizontally 1303 | if (state->align & FONS_ALIGN_LEFT) { 1304 | // empty 1305 | } else if (state->align & FONS_ALIGN_RIGHT) { 1306 | width = fonsTextBounds(stash, x,y, str, end, NULL); 1307 | x -= width; 1308 | } else if (state->align & FONS_ALIGN_CENTER) { 1309 | width = fonsTextBounds(stash, x,y, str, end, NULL); 1310 | x -= width * 0.5f; 1311 | } 1312 | // Align vertically. 1313 | y += fons__getVertAlign(stash, iter->font, state->align, iter->isize); 1314 | 1315 | if (end == NULL) 1316 | end = str + strlen(str); 1317 | 1318 | iter->x = iter->nextx = x; 1319 | iter->y = iter->nexty = y; 1320 | iter->spacing = state->spacing; 1321 | iter->str = str; 1322 | iter->next = str; 1323 | iter->end = end; 1324 | iter->codepoint = 0; 1325 | iter->prevGlyphIndex = -1; 1326 | 1327 | return 1; 1328 | } 1329 | 1330 | int fonsTextIterNext(FONScontext* stash, FONStextIter* iter, FONSquad* quad) 1331 | { 1332 | FONSglyph* glyph = NULL; 1333 | const char* str = iter->next; 1334 | iter->str = iter->next; 1335 | 1336 | if (str == iter->end) 1337 | return 0; 1338 | 1339 | for (; str != iter->end; str++) { 1340 | if (fons__decutf8(&iter->utf8state, &iter->codepoint, *(const unsigned char*)str)) 1341 | continue; 1342 | str++; 1343 | // Get glyph and quad 1344 | iter->x = iter->nextx; 1345 | iter->y = iter->nexty; 1346 | glyph = fons__getGlyph(stash, iter->font, iter->codepoint, iter->isize, iter->iblur); 1347 | if (glyph != NULL) 1348 | fons__getQuad(stash, iter->font, iter->prevGlyphIndex, glyph, iter->scale, iter->spacing, &iter->nextx, &iter->nexty, quad); 1349 | iter->prevGlyphIndex = glyph != NULL ? glyph->index : -1; 1350 | break; 1351 | } 1352 | iter->next = str; 1353 | 1354 | return 1; 1355 | } 1356 | 1357 | void fonsDrawDebug(FONScontext* stash, float x, float y) 1358 | { 1359 | int i; 1360 | int w = stash->params.width; 1361 | int h = stash->params.height; 1362 | float u = w == 0 ? 0 : (1.0f / w); 1363 | float v = h == 0 ? 0 : (1.0f / h); 1364 | 1365 | if (stash->nverts+6+6 > FONS_VERTEX_COUNT) 1366 | fons__flush(stash); 1367 | 1368 | // Draw background 1369 | fons__vertex(stash, x+0, y+0, u, v, 0x0fffffff); 1370 | fons__vertex(stash, x+w, y+h, u, v, 0x0fffffff); 1371 | fons__vertex(stash, x+w, y+0, u, v, 0x0fffffff); 1372 | 1373 | fons__vertex(stash, x+0, y+0, u, v, 0x0fffffff); 1374 | fons__vertex(stash, x+0, y+h, u, v, 0x0fffffff); 1375 | fons__vertex(stash, x+w, y+h, u, v, 0x0fffffff); 1376 | 1377 | // Draw texture 1378 | fons__vertex(stash, x+0, y+0, 0, 0, 0xffffffff); 1379 | fons__vertex(stash, x+w, y+h, 1, 1, 0xffffffff); 1380 | fons__vertex(stash, x+w, y+0, 1, 0, 0xffffffff); 1381 | 1382 | fons__vertex(stash, x+0, y+0, 0, 0, 0xffffffff); 1383 | fons__vertex(stash, x+0, y+h, 0, 1, 0xffffffff); 1384 | fons__vertex(stash, x+w, y+h, 1, 1, 0xffffffff); 1385 | 1386 | // Drawbug draw atlas 1387 | for (i = 0; i < stash->atlas->nnodes; i++) { 1388 | FONSatlasNode* n = &stash->atlas->nodes[i]; 1389 | 1390 | if (stash->nverts+6 > FONS_VERTEX_COUNT) 1391 | fons__flush(stash); 1392 | 1393 | fons__vertex(stash, x+n->x+0, y+n->y+0, u, v, 0xc00000ff); 1394 | fons__vertex(stash, x+n->x+n->width, y+n->y+1, u, v, 0xc00000ff); 1395 | fons__vertex(stash, x+n->x+n->width, y+n->y+0, u, v, 0xc00000ff); 1396 | 1397 | fons__vertex(stash, x+n->x+0, y+n->y+0, u, v, 0xc00000ff); 1398 | fons__vertex(stash, x+n->x+0, y+n->y+1, u, v, 0xc00000ff); 1399 | fons__vertex(stash, x+n->x+n->width, y+n->y+1, u, v, 0xc00000ff); 1400 | } 1401 | 1402 | fons__flush(stash); 1403 | } 1404 | 1405 | float fonsTextBounds(FONScontext* stash, 1406 | float x, float y, 1407 | const char* str, const char* end, 1408 | float* bounds) 1409 | { 1410 | FONSstate* state = fons__getState(stash); 1411 | unsigned int codepoint; 1412 | unsigned int utf8state = 0; 1413 | FONSquad q; 1414 | FONSglyph* glyph = NULL; 1415 | int prevGlyphIndex = -1; 1416 | short isize = (short)(state->size*10.0f); 1417 | short iblur = (short)state->blur; 1418 | float scale; 1419 | FONSfont* font; 1420 | float startx, advance; 1421 | float minx, miny, maxx, maxy; 1422 | 1423 | if (stash == NULL) return 0; 1424 | if (state->font < 0 || state->font >= stash->nfonts) return 0; 1425 | font = stash->fonts[state->font]; 1426 | if (font->data == NULL) return 0; 1427 | 1428 | scale = fons__tt_getPixelHeightScale(&font->font, (float)isize/10.0f); 1429 | 1430 | // Align vertically. 1431 | y += fons__getVertAlign(stash, font, state->align, isize); 1432 | 1433 | minx = maxx = x; 1434 | miny = maxy = y; 1435 | startx = x; 1436 | 1437 | if (end == NULL) 1438 | end = str + strlen(str); 1439 | 1440 | for (; str != end; ++str) { 1441 | if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str)) 1442 | continue; 1443 | glyph = fons__getGlyph(stash, font, codepoint, isize, iblur); 1444 | if (glyph != NULL) { 1445 | fons__getQuad(stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q); 1446 | if (q.x0 < minx) minx = q.x0; 1447 | if (q.x1 > maxx) maxx = q.x1; 1448 | if (stash->params.flags & FONS_ZERO_TOPLEFT) { 1449 | if (q.y0 < miny) miny = q.y0; 1450 | if (q.y1 > maxy) maxy = q.y1; 1451 | } else { 1452 | if (q.y1 < miny) miny = q.y1; 1453 | if (q.y0 > maxy) maxy = q.y0; 1454 | } 1455 | } 1456 | prevGlyphIndex = glyph != NULL ? glyph->index : -1; 1457 | } 1458 | 1459 | advance = x - startx; 1460 | 1461 | // Align horizontally 1462 | if (state->align & FONS_ALIGN_LEFT) { 1463 | // empty 1464 | } else if (state->align & FONS_ALIGN_RIGHT) { 1465 | minx -= advance; 1466 | maxx -= advance; 1467 | } else if (state->align & FONS_ALIGN_CENTER) { 1468 | minx -= advance * 0.5f; 1469 | maxx -= advance * 0.5f; 1470 | } 1471 | 1472 | if (bounds) { 1473 | bounds[0] = minx; 1474 | bounds[1] = miny; 1475 | bounds[2] = maxx; 1476 | bounds[3] = maxy; 1477 | } 1478 | 1479 | return advance; 1480 | } 1481 | 1482 | void fonsVertMetrics(FONScontext* stash, 1483 | float* ascender, float* descender, float* lineh) 1484 | { 1485 | FONSfont* font; 1486 | FONSstate* state = fons__getState(stash); 1487 | short isize; 1488 | 1489 | if (stash == NULL) return; 1490 | if (state->font < 0 || state->font >= stash->nfonts) return; 1491 | font = stash->fonts[state->font]; 1492 | isize = (short)(state->size*10.0f); 1493 | if (font->data == NULL) return; 1494 | 1495 | if (ascender) 1496 | *ascender = font->ascender*isize/10.0f; 1497 | if (descender) 1498 | *descender = font->descender*isize/10.0f; 1499 | if (lineh) 1500 | *lineh = font->lineh*isize/10.0f; 1501 | } 1502 | 1503 | void fonsLineBounds(FONScontext* stash, float y, float* miny, float* maxy) 1504 | { 1505 | FONSfont* font; 1506 | FONSstate* state = fons__getState(stash); 1507 | short isize; 1508 | 1509 | if (stash == NULL) return; 1510 | if (state->font < 0 || state->font >= stash->nfonts) return; 1511 | font = stash->fonts[state->font]; 1512 | isize = (short)(state->size*10.0f); 1513 | if (font->data == NULL) return; 1514 | 1515 | y += fons__getVertAlign(stash, font, state->align, isize); 1516 | 1517 | if (stash->params.flags & FONS_ZERO_TOPLEFT) { 1518 | *miny = y - font->ascender * (float)isize/10.0f; 1519 | *maxy = *miny + font->lineh*isize/10.0f; 1520 | } else { 1521 | *maxy = y + font->descender * (float)isize/10.0f; 1522 | *miny = *maxy - font->lineh*isize/10.0f; 1523 | } 1524 | } 1525 | 1526 | const unsigned char* fonsGetTextureData(FONScontext* stash, int* width, int* height) 1527 | { 1528 | if (width != NULL) 1529 | *width = stash->params.width; 1530 | if (height != NULL) 1531 | *height = stash->params.height; 1532 | return stash->texData; 1533 | } 1534 | 1535 | int fonsValidateTexture(FONScontext* stash, int* dirty) 1536 | { 1537 | if (stash->dirtyRect[0] < stash->dirtyRect[2] && stash->dirtyRect[1] < stash->dirtyRect[3]) { 1538 | dirty[0] = stash->dirtyRect[0]; 1539 | dirty[1] = stash->dirtyRect[1]; 1540 | dirty[2] = stash->dirtyRect[2]; 1541 | dirty[3] = stash->dirtyRect[3]; 1542 | // Reset dirty rect 1543 | stash->dirtyRect[0] = stash->params.width; 1544 | stash->dirtyRect[1] = stash->params.height; 1545 | stash->dirtyRect[2] = 0; 1546 | stash->dirtyRect[3] = 0; 1547 | return 1; 1548 | } 1549 | return 0; 1550 | } 1551 | 1552 | void fonsDeleteInternal(FONScontext* stash) 1553 | { 1554 | int i; 1555 | if (stash == NULL) return; 1556 | 1557 | if (stash->params.renderDelete) 1558 | stash->params.renderDelete(stash->params.userPtr); 1559 | 1560 | for (i = 0; i < stash->nfonts; ++i) 1561 | fons__freeFont(stash->fonts[i]); 1562 | 1563 | if (stash->atlas) fons__deleteAtlas(stash->atlas); 1564 | if (stash->fonts) free(stash->fonts); 1565 | if (stash->texData) free(stash->texData); 1566 | if (stash->scratch) free(stash->scratch); 1567 | free(stash); 1568 | } 1569 | 1570 | void fonsSetErrorCallback(FONScontext* stash, void (*callback)(void* uptr, int error, int val), void* uptr) 1571 | { 1572 | if (stash == NULL) return; 1573 | stash->handleError = callback; 1574 | stash->errorUptr = uptr; 1575 | } 1576 | 1577 | void fonsGetAtlasSize(FONScontext* stash, int* width, int* height) 1578 | { 1579 | if (stash == NULL) return; 1580 | *width = stash->params.width; 1581 | *height = stash->params.height; 1582 | } 1583 | 1584 | int fonsExpandAtlas(FONScontext* stash, int width, int height) 1585 | { 1586 | int i, maxy = 0; 1587 | unsigned char* data = NULL; 1588 | if (stash == NULL) return 0; 1589 | 1590 | width = fons__maxi(width, stash->params.width); 1591 | height = fons__maxi(height, stash->params.height); 1592 | 1593 | if (width == stash->params.width && height == stash->params.height) 1594 | return 1; 1595 | 1596 | // Flush pending glyphs. 1597 | fons__flush(stash); 1598 | 1599 | // Create new texture 1600 | if (stash->params.renderResize != NULL) { 1601 | if (stash->params.renderResize(stash->params.userPtr, width, height) == 0) 1602 | return 0; 1603 | } 1604 | // Copy old texture data over. 1605 | data = (unsigned char*)malloc(width * height); 1606 | if (data == NULL) 1607 | return 0; 1608 | for (i = 0; i < stash->params.height; i++) { 1609 | unsigned char* dst = &data[i*width]; 1610 | unsigned char* src = &stash->texData[i*stash->params.width]; 1611 | memcpy(dst, src, stash->params.width); 1612 | if (width > stash->params.width) 1613 | memset(dst+stash->params.width, 0, width - stash->params.width); 1614 | } 1615 | if (height > stash->params.height) 1616 | memset(&data[stash->params.height * width], 0, (height - stash->params.height) * width); 1617 | 1618 | free(stash->texData); 1619 | stash->texData = data; 1620 | 1621 | // Increase atlas size 1622 | fons__atlasExpand(stash->atlas, width, height); 1623 | 1624 | // Add axisting data as dirty. 1625 | for (i = 0; i < stash->atlas->nnodes; i++) 1626 | maxy = fons__maxi(maxy, stash->atlas->nodes[i].y); 1627 | stash->dirtyRect[0] = 0; 1628 | stash->dirtyRect[1] = 0; 1629 | stash->dirtyRect[2] = stash->params.width; 1630 | stash->dirtyRect[3] = maxy; 1631 | 1632 | stash->params.width = width; 1633 | stash->params.height = height; 1634 | stash->itw = 1.0f/stash->params.width; 1635 | stash->ith = 1.0f/stash->params.height; 1636 | 1637 | return 1; 1638 | } 1639 | 1640 | int fonsResetAtlas(FONScontext* stash, int width, int height) 1641 | { 1642 | int i, j; 1643 | if (stash == NULL) return 0; 1644 | 1645 | // Flush pending glyphs. 1646 | fons__flush(stash); 1647 | 1648 | // Create new texture 1649 | if (stash->params.renderResize != NULL) { 1650 | if (stash->params.renderResize(stash->params.userPtr, width, height) == 0) 1651 | return 0; 1652 | } 1653 | 1654 | // Reset atlas 1655 | fons__atlasReset(stash->atlas, width, height); 1656 | 1657 | // Clear texture data. 1658 | stash->texData = (unsigned char*)realloc(stash->texData, width * height); 1659 | if (stash->texData == NULL) return 0; 1660 | memset(stash->texData, 0, width * height); 1661 | 1662 | // Reset dirty rect 1663 | stash->dirtyRect[0] = width; 1664 | stash->dirtyRect[1] = height; 1665 | stash->dirtyRect[2] = 0; 1666 | stash->dirtyRect[3] = 0; 1667 | 1668 | // Reset cached glyphs 1669 | for (i = 0; i < stash->nfonts; i++) { 1670 | FONSfont* font = stash->fonts[i]; 1671 | font->nglyphs = 0; 1672 | for (j = 0; j < FONS_HASH_LUT_SIZE; j++) 1673 | font->lut[j] = -1; 1674 | } 1675 | 1676 | stash->params.width = width; 1677 | stash->params.height = height; 1678 | stash->itw = 1.0f/stash->params.width; 1679 | stash->ith = 1.0f/stash->params.height; 1680 | 1681 | // Add white rect at 0,0 for debug drawing. 1682 | fons__addWhiteRect(stash, 2,2); 1683 | 1684 | return 1; 1685 | } 1686 | 1687 | 1688 | #endif 1689 | -------------------------------------------------------------------------------- /libs/nanovg/src/nanovg.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2013 Mikko Mononen memon@inside.org 3 | // 4 | // This software is provided 'as-is', without any express or implied 5 | // warranty. In no event will the authors be held liable for any damages 6 | // arising from the use of this software. 7 | // Permission is granted to anyone to use this software for any purpose, 8 | // including commercial applications, and to alter it and redistribute it 9 | // freely, subject to the following restrictions: 10 | // 1. The origin of this software must not be misrepresented; you must not 11 | // claim that you wrote the original software. If you use this software 12 | // in a product, an acknowledgment in the product documentation would be 13 | // appreciated but is not required. 14 | // 2. Altered source versions must be plainly marked as such, and must not be 15 | // misrepresented as being the original software. 16 | // 3. This notice may not be removed or altered from any source distribution. 17 | // 18 | 19 | #ifndef NANOVG_H 20 | #define NANOVG_H 21 | 22 | #ifdef __cplusplus 23 | extern "C" { 24 | #endif 25 | 26 | #define NVG_PI 3.14159265358979323846264338327f 27 | 28 | #ifdef _MSC_VER 29 | #pragma warning(push) 30 | #pragma warning(disable: 4201) // nonstandard extension used : nameless struct/union 31 | #endif 32 | 33 | typedef struct NVGcontext NVGcontext; 34 | 35 | struct NVGcolor { 36 | union { 37 | float rgba[4]; 38 | struct { 39 | float r,g,b,a; 40 | }; 41 | }; 42 | }; 43 | typedef struct NVGcolor NVGcolor; 44 | 45 | struct NVGpaint { 46 | float xform[6]; 47 | float extent[2]; 48 | float radius; 49 | float feather; 50 | NVGcolor innerColor; 51 | NVGcolor outerColor; 52 | int image; 53 | }; 54 | typedef struct NVGpaint NVGpaint; 55 | 56 | enum NVGwinding { 57 | NVG_CCW = 1, // Winding for solid shapes 58 | NVG_CW = 2, // Winding for holes 59 | }; 60 | 61 | enum NVGsolidity { 62 | NVG_SOLID = 1, // CCW 63 | NVG_HOLE = 2, // CW 64 | }; 65 | 66 | enum NVGlineCap { 67 | NVG_BUTT, 68 | NVG_ROUND, 69 | NVG_SQUARE, 70 | NVG_BEVEL, 71 | NVG_MITER, 72 | }; 73 | 74 | enum NVGalign { 75 | // Horizontal align 76 | NVG_ALIGN_LEFT = 1<<0, // Default, align text horizontally to left. 77 | NVG_ALIGN_CENTER = 1<<1, // Align text horizontally to center. 78 | NVG_ALIGN_RIGHT = 1<<2, // Align text horizontally to right. 79 | // Vertical align 80 | NVG_ALIGN_TOP = 1<<3, // Align text vertically to top. 81 | NVG_ALIGN_MIDDLE = 1<<4, // Align text vertically to middle. 82 | NVG_ALIGN_BOTTOM = 1<<5, // Align text vertically to bottom. 83 | NVG_ALIGN_BASELINE = 1<<6, // Default, align text vertically to baseline. 84 | }; 85 | 86 | struct NVGglyphPosition { 87 | const char* str; // Position of the glyph in the input string. 88 | float x; // The x-coordinate of the logical glyph position. 89 | float minx, maxx; // The bounds of the glyph shape. 90 | }; 91 | typedef struct NVGglyphPosition NVGglyphPosition; 92 | 93 | struct NVGtextRow { 94 | const char* start; // Pointer to the input text where the row starts. 95 | const char* end; // Pointer to the input text where the row ends (one past the last character). 96 | const char* next; // Pointer to the beginning of the next row. 97 | float width; // Logical width of the row. 98 | float minx, maxx; // Actual bounds of the row. Logical with and bounds can differ because of kerning and some parts over extending. 99 | }; 100 | typedef struct NVGtextRow NVGtextRow; 101 | 102 | enum NVGimageFlags { 103 | NVG_IMAGE_GENERATE_MIPMAPS = 1<<0, // Generate mipmaps during creation of the image. 104 | NVG_IMAGE_REPEATX = 1<<1, // Repeat image in X direction. 105 | NVG_IMAGE_REPEATY = 1<<2, // Repeat image in Y direction. 106 | NVG_IMAGE_FLIPY = 1<<3, // Flips (inverses) image in Y direction when rendered. 107 | NVG_IMAGE_PREMULTIPLIED = 1<<4, // Image data has premultiplied alpha. 108 | }; 109 | 110 | // Begin drawing a new frame 111 | // Calls to nanovg drawing API should be wrapped in nvgBeginFrame() & nvgEndFrame() 112 | // nvgBeginFrame() defines the size of the window to render to in relation currently 113 | // set viewport (i.e. glViewport on GL backends). Device pixel ration allows to 114 | // control the rendering on Hi-DPI devices. 115 | // For example, GLFW returns two dimension for an opened window: window size and 116 | // frame buffer size. In that case you would set windowWidth/Height to the window size 117 | // devicePixelRatio to: frameBufferWidth / windowWidth. 118 | void nvgBeginFrame(NVGcontext* ctx, int windowWidth, int windowHeight, float devicePixelRatio); 119 | 120 | // Cancels drawing the current frame. 121 | void nvgCancelFrame(NVGcontext* ctx); 122 | 123 | // Ends drawing flushing remaining render state. 124 | void nvgEndFrame(NVGcontext* ctx); 125 | 126 | // 127 | // Color utils 128 | // 129 | // Colors in NanoVG are stored as unsigned ints in ABGR format. 130 | 131 | // Returns a color value from red, green, blue values. Alpha will be set to 255 (1.0f). 132 | NVGcolor nvgRGB(unsigned char r, unsigned char g, unsigned char b); 133 | 134 | // Returns a color value from red, green, blue values. Alpha will be set to 1.0f. 135 | NVGcolor nvgRGBf(float r, float g, float b); 136 | 137 | 138 | // Returns a color value from red, green, blue and alpha values. 139 | NVGcolor nvgRGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a); 140 | 141 | // Returns a color value from red, green, blue and alpha values. 142 | NVGcolor nvgRGBAf(float r, float g, float b, float a); 143 | 144 | 145 | // Linearly interpoaltes from color c0 to c1, and returns resulting color value. 146 | NVGcolor nvgLerpRGBA(NVGcolor c0, NVGcolor c1, float u); 147 | 148 | // Sets transparency of a color value. 149 | NVGcolor nvgTransRGBA(NVGcolor c0, unsigned char a); 150 | 151 | // Sets transparency of a color value. 152 | NVGcolor nvgTransRGBAf(NVGcolor c0, float a); 153 | 154 | // Returns color value specified by hue, saturation and lightness. 155 | // HSL values are all in range [0..1], alpha will be set to 255. 156 | NVGcolor nvgHSL(float h, float s, float l); 157 | 158 | // Returns color value specified by hue, saturation and lightness and alpha. 159 | // HSL values are all in range [0..1], alpha in range [0..255] 160 | NVGcolor nvgHSLA(float h, float s, float l, unsigned char a); 161 | 162 | // 163 | // State Handling 164 | // 165 | // NanoVG contains state which represents how paths will be rendered. 166 | // The state contains transform, fill and stroke styles, text and font styles, 167 | // and scissor clipping. 168 | 169 | // Pushes and saves the current render state into a state stack. 170 | // A matching nvgRestore() must be used to restore the state. 171 | void nvgSave(NVGcontext* ctx); 172 | 173 | // Pops and restores current render state. 174 | void nvgRestore(NVGcontext* ctx); 175 | 176 | // Resets current render state to default values. Does not affect the render state stack. 177 | void nvgReset(NVGcontext* ctx); 178 | 179 | // 180 | // Render styles 181 | // 182 | // Fill and stroke render style can be either a solid color or a paint which is a gradient or a pattern. 183 | // Solid color is simply defined as a color value, different kinds of paints can be created 184 | // using nvgLinearGradient(), nvgBoxGradient(), nvgRadialGradient() and nvgImagePattern(). 185 | // 186 | // Current render style can be saved and restored using nvgSave() and nvgRestore(). 187 | 188 | // Sets current stroke style to a solid color. 189 | void nvgStrokeColor(NVGcontext* ctx, NVGcolor color); 190 | 191 | // Sets current stroke style to a paint, which can be a one of the gradients or a pattern. 192 | void nvgStrokePaint(NVGcontext* ctx, NVGpaint paint); 193 | 194 | // Sets current fill cstyle to a solid color. 195 | void nvgFillColor(NVGcontext* ctx, NVGcolor color); 196 | 197 | // Sets current fill style to a paint, which can be a one of the gradients or a pattern. 198 | void nvgFillPaint(NVGcontext* ctx, NVGpaint paint); 199 | 200 | // Sets the miter limit of the stroke style. 201 | // Miter limit controls when a sharp corner is beveled. 202 | void nvgMiterLimit(NVGcontext* ctx, float limit); 203 | 204 | // Sets the stroke witdth of the stroke style. 205 | void nvgStrokeWidth(NVGcontext* ctx, float size); 206 | 207 | // Sets how the end of the line (cap) is drawn, 208 | // Can be one of: NVG_BUTT (default), NVG_ROUND, NVG_SQUARE. 209 | void nvgLineCap(NVGcontext* ctx, int cap); 210 | 211 | // Sets how sharp path corners are drawn. 212 | // Can be one of NVG_MITER (default), NVG_ROUND, NVG_BEVEL. 213 | void nvgLineJoin(NVGcontext* ctx, int join); 214 | 215 | // Sets the transparency applied to all rendered shapes. 216 | // Alreade transparent paths will get proportionally more transparent as well. 217 | void nvgGlobalAlpha(NVGcontext* ctx, float alpha); 218 | 219 | // 220 | // Transforms 221 | // 222 | // The paths, gradients, patterns and scissor region are transformed by an transformation 223 | // matrix at the time when they are passed to the API. 224 | // The current transformation matrix is a affine matrix: 225 | // [sx kx tx] 226 | // [ky sy ty] 227 | // [ 0 0 1] 228 | // Where: sx,sy define scaling, kx,ky skewing, and tx,ty translation. 229 | // The last row is assumed to be 0,0,1 and is not stored. 230 | // 231 | // Apart from nvgResetTransform(), each transformation function first creates 232 | // specific transformation matrix and pre-multiplies the current transformation by it. 233 | // 234 | // Current coordinate system (transformation) can be saved and restored using nvgSave() and nvgRestore(). 235 | 236 | // Resets current transform to a identity matrix. 237 | void nvgResetTransform(NVGcontext* ctx); 238 | 239 | // Premultiplies current coordinate system by specified matrix. 240 | // The parameters are interpreted as matrix as follows: 241 | // [a c e] 242 | // [b d f] 243 | // [0 0 1] 244 | void nvgTransform(NVGcontext* ctx, float a, float b, float c, float d, float e, float f); 245 | 246 | // Translates current coordinate system. 247 | void nvgTranslate(NVGcontext* ctx, float x, float y); 248 | 249 | // Rotates current coordinate system. Angle is specifid in radians. 250 | void nvgRotate(NVGcontext* ctx, float angle); 251 | 252 | // Skews the current coordinate system along X axis. Angle is specifid in radians. 253 | void nvgSkewX(NVGcontext* ctx, float angle); 254 | 255 | // Skews the current coordinate system along Y axis. Angle is specifid in radians. 256 | void nvgSkewY(NVGcontext* ctx, float angle); 257 | 258 | // Scales the current coordinat system. 259 | void nvgScale(NVGcontext* ctx, float x, float y); 260 | 261 | // Stores the top part (a-f) of the current transformation matrix in to the specified buffer. 262 | // [a c e] 263 | // [b d f] 264 | // [0 0 1] 265 | // There should be space for 6 floats in the return buffer for the values a-f. 266 | void nvgCurrentTransform(NVGcontext* ctx, float* xform); 267 | 268 | 269 | // The following functions can be used to make calculations on 2x3 transformation matrices. 270 | // A 2x3 matrix is representated as float[6]. 271 | 272 | // Sets the transform to identity matrix. 273 | void nvgTransformIdentity(float* dst); 274 | 275 | // Sets the transform to translation matrix matrix. 276 | void nvgTransformTranslate(float* dst, float tx, float ty); 277 | 278 | // Sets the transform to scale matrix. 279 | void nvgTransformScale(float* dst, float sx, float sy); 280 | 281 | // Sets the transform to rotate matrix. Angle is specifid in radians. 282 | void nvgTransformRotate(float* dst, float a); 283 | 284 | // Sets the transform to skew-x matrix. Angle is specifid in radians. 285 | void nvgTransformSkewX(float* dst, float a); 286 | 287 | // Sets the transform to skew-y matrix. Angle is specifid in radians. 288 | void nvgTransformSkewY(float* dst, float a); 289 | 290 | // Sets the transform to the result of multiplication of two transforms, of A = A*B. 291 | void nvgTransformMultiply(float* dst, const float* src); 292 | 293 | // Sets the transform to the result of multiplication of two transforms, of A = B*A. 294 | void nvgTransformPremultiply(float* dst, const float* src); 295 | 296 | // Sets the destination to inverse of specified transform. 297 | // Returns 1 if the inverse could be calculated, else 0. 298 | int nvgTransformInverse(float* dst, const float* src); 299 | 300 | // Transform a point by given transform. 301 | void nvgTransformPoint(float* dstx, float* dsty, const float* xform, float srcx, float srcy); 302 | 303 | // Converts degress to radians and vice versa. 304 | float nvgDegToRad(float deg); 305 | float nvgRadToDeg(float rad); 306 | 307 | // 308 | // Images 309 | // 310 | // NanoVG allows you to load jpg, png, psd, tga, pic and gif files to be used for rendering. 311 | // In addition you can upload your own image. The image loading is provided by stb_image. 312 | // The parameter imageFlags is combination of flags defined in NVGimageFlags. 313 | 314 | // Creates image by loading it from the disk from specified file name. 315 | // Returns handle to the image. 316 | int nvgCreateImage(NVGcontext* ctx, const char* filename, int imageFlags); 317 | 318 | // Creates image by loading it from the specified chunk of memory. 319 | // Returns handle to the image. 320 | int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, unsigned char* data, int ndata); 321 | 322 | // Creates image from specified image data. 323 | // Returns handle to the image. 324 | int nvgCreateImageRGBA(NVGcontext* ctx, int w, int h, int imageFlags, const unsigned char* data); 325 | 326 | // Updates image data specified by image handle. 327 | void nvgUpdateImage(NVGcontext* ctx, int image, const unsigned char* data); 328 | 329 | // Returns the domensions of a created image. 330 | void nvgImageSize(NVGcontext* ctx, int image, int* w, int* h); 331 | 332 | // Deletes created image. 333 | void nvgDeleteImage(NVGcontext* ctx, int image); 334 | 335 | // 336 | // Paints 337 | // 338 | // NanoVG supports four types of paints: linear gradient, box gradient, radial gradient and image pattern. 339 | // These can be used as paints for strokes and fills. 340 | 341 | // Creates and returns a linear gradient. Parameters (sx,sy)-(ex,ey) specify the start and end coordinates 342 | // of the linear gradient, icol specifies the start color and ocol the end color. 343 | // The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint(). 344 | NVGpaint nvgLinearGradient(NVGcontext* ctx, float sx, float sy, float ex, float ey, 345 | NVGcolor icol, NVGcolor ocol); 346 | 347 | // Creates and returns a box gradient. Box gradient is a feathered rounded rectangle, it is useful for rendering 348 | // drop shadows or hilights for boxes. Parameters (x,y) define the top-left corner of the rectangle, 349 | // (w,h) define the size of the rectangle, r defines the corner radius, and f feather. Feather defines how blurry 350 | // the border of the rectangle is. Parameter icol specifies the inner color and ocol the outer color of the gradient. 351 | // The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint(). 352 | NVGpaint nvgBoxGradient(NVGcontext* ctx, float x, float y, float w, float h, 353 | float r, float f, NVGcolor icol, NVGcolor ocol); 354 | 355 | // Creates and returns a radial gradient. Parameters (cx,cy) specify the center, inr and outr specify 356 | // the inner and outer radius of the gradient, icol specifies the start color and ocol the end color. 357 | // The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint(). 358 | NVGpaint nvgRadialGradient(NVGcontext* ctx, float cx, float cy, float inr, float outr, 359 | NVGcolor icol, NVGcolor ocol); 360 | 361 | // Creates and returns an image patter. Parameters (ox,oy) specify the left-top location of the image pattern, 362 | // (ex,ey) the size of one image, angle rotation around the top-left corner, image is handle to the image to render. 363 | // The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint(). 364 | NVGpaint nvgImagePattern(NVGcontext* ctx, float ox, float oy, float ex, float ey, 365 | float angle, int image, float alpha); 366 | 367 | // 368 | // Scissoring 369 | // 370 | // Scissoring allows you to clip the rendering into a rectangle. This is useful for varius 371 | // user interface cases like rendering a text edit or a timeline. 372 | 373 | // Sets the current scissor rectangle. 374 | // The scissor rectangle is transformed by the current transform. 375 | void nvgScissor(NVGcontext* ctx, float x, float y, float w, float h); 376 | 377 | // Intersects current scissor rectangle with the specified rectangle. 378 | // The scissor rectangle is transformed by the current transform. 379 | // Note: in case the rotation of previous scissor rect differs from 380 | // the current one, the intersection will be done between the specified 381 | // rectangle and the previous scissor rectangle transformed in the current 382 | // transform space. The resulting shape is always rectangle. 383 | void nvgIntersectScissor(NVGcontext* ctx, float x, float y, float w, float h); 384 | 385 | // Reset and disables scissoring. 386 | void nvgResetScissor(NVGcontext* ctx); 387 | 388 | // 389 | // Paths 390 | // 391 | // Drawing a new shape starts with nvgBeginPath(), it clears all the currently defined paths. 392 | // Then you define one or more paths and sub-paths which describe the shape. The are functions 393 | // to draw common shapes like rectangles and circles, and lower level step-by-step functions, 394 | // which allow to define a path curve by curve. 395 | // 396 | // NanoVG uses even-odd fill rule to draw the shapes. Solid shapes should have counter clockwise 397 | // winding and holes should have counter clockwise order. To specify winding of a path you can 398 | // call nvgPathWinding(). This is useful especially for the common shapes, which are drawn CCW. 399 | // 400 | // Finally you can fill the path using current fill style by calling nvgFill(), and stroke it 401 | // with current stroke style by calling nvgStroke(). 402 | // 403 | // The curve segments and sub-paths are transformed by the current transform. 404 | 405 | // Clears the current path and sub-paths. 406 | void nvgBeginPath(NVGcontext* ctx); 407 | 408 | // Starts new sub-path with specified point as first point. 409 | void nvgMoveTo(NVGcontext* ctx, float x, float y); 410 | 411 | // Adds line segment from the last point in the path to the specified point. 412 | void nvgLineTo(NVGcontext* ctx, float x, float y); 413 | 414 | // Adds cubic bezier segment from last point in the path via two control points to the specified point. 415 | void nvgBezierTo(NVGcontext* ctx, float c1x, float c1y, float c2x, float c2y, float x, float y); 416 | 417 | // Adds quadratic bezier segment from last point in the path via a control point to the specified point. 418 | void nvgQuadTo(NVGcontext* ctx, float cx, float cy, float x, float y); 419 | 420 | // Adds an arc segment at the corner defined by the last path point, and two specified points. 421 | void nvgArcTo(NVGcontext* ctx, float x1, float y1, float x2, float y2, float radius); 422 | 423 | // Closes current sub-path with a line segment. 424 | void nvgClosePath(NVGcontext* ctx); 425 | 426 | // Sets the current sub-path winding, see NVGwinding and NVGsolidity. 427 | void nvgPathWinding(NVGcontext* ctx, int dir); 428 | 429 | // Creates new circle arc shaped sub-path. The arc center is at cx,cy, the arc radius is r, 430 | // and the arc is drawn from angle a0 to a1, and swept in direction dir (NVG_CCW, or NVG_CW). 431 | // Angles are specified in radians. 432 | void nvgArc(NVGcontext* ctx, float cx, float cy, float r, float a0, float a1, int dir); 433 | 434 | // Creates new rectangle shaped sub-path. 435 | void nvgRect(NVGcontext* ctx, float x, float y, float w, float h); 436 | 437 | // Creates new rounded rectangle shaped sub-path. 438 | void nvgRoundedRect(NVGcontext* ctx, float x, float y, float w, float h, float r); 439 | 440 | // Creates new ellipse shaped sub-path. 441 | void nvgEllipse(NVGcontext* ctx, float cx, float cy, float rx, float ry); 442 | 443 | // Creates new circle shaped sub-path. 444 | void nvgCircle(NVGcontext* ctx, float cx, float cy, float r); 445 | 446 | // Fills the current path with current fill style. 447 | void nvgFill(NVGcontext* ctx); 448 | 449 | // Fills the current path with current stroke style. 450 | void nvgStroke(NVGcontext* ctx); 451 | 452 | 453 | // 454 | // Text 455 | // 456 | // NanoVG allows you to load .ttf files and use the font to render text. 457 | // 458 | // The appearance of the text can be defined by setting the current text style 459 | // and by specifying the fill color. Common text and font settings such as 460 | // font size, letter spacing and text align are supported. Font blur allows you 461 | // to create simple text effects such as drop shadows. 462 | // 463 | // At render time the font face can be set based on the font handles or name. 464 | // 465 | // Font measure functions return values in local space, the calculations are 466 | // carried in the same resolution as the final rendering. This is done because 467 | // the text glyph positions are snapped to the nearest pixels sharp rendering. 468 | // 469 | // The local space means that values are not rotated or scale as per the current 470 | // transformation. For example if you set font size to 12, which would mean that 471 | // line height is 16, then regardless of the current scaling and rotation, the 472 | // returned line height is always 16. Some measures may vary because of the scaling 473 | // since aforementioned pixel snapping. 474 | // 475 | // While this may sound a little odd, the setup allows you to always render the 476 | // same way regardless of scaling. I.e. following works regardless of scaling: 477 | // 478 | // const char* txt = "Text me up."; 479 | // nvgTextBounds(vg, x,y, txt, NULL, bounds); 480 | // nvgBeginPath(vg); 481 | // nvgRoundedRect(vg, bounds[0],bounds[1], bounds[2]-bounds[0], bounds[3]-bounds[1]); 482 | // nvgFill(vg); 483 | // 484 | // Note: currently only solid color fill is supported for text. 485 | 486 | // Creates font by loading it from the disk from specified file name. 487 | // Returns handle to the font. 488 | int nvgCreateFont(NVGcontext* ctx, const char* name, const char* filename); 489 | 490 | // Creates image by loading it from the specified memory chunk. 491 | // Returns handle to the font. 492 | int nvgCreateFontMem(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData); 493 | 494 | // Finds a loaded font of specified name, and returns handle to it, or -1 if the font is not found. 495 | int nvgFindFont(NVGcontext* ctx, const char* name); 496 | 497 | // Sets the font size of current text style. 498 | void nvgFontSize(NVGcontext* ctx, float size); 499 | 500 | // Sets the blur of current text style. 501 | void nvgFontBlur(NVGcontext* ctx, float blur); 502 | 503 | // Sets the letter spacing of current text style. 504 | void nvgTextLetterSpacing(NVGcontext* ctx, float spacing); 505 | 506 | // Sets the proportional line height of current text style. The line height is specified as multiple of font size. 507 | void nvgTextLineHeight(NVGcontext* ctx, float lineHeight); 508 | 509 | // Sets the text align of current text style, see NVGaling for options. 510 | void nvgTextAlign(NVGcontext* ctx, int align); 511 | 512 | // Sets the font face based on specified id of current text style. 513 | void nvgFontFaceId(NVGcontext* ctx, int font); 514 | 515 | // Sets the font face based on specified name of current text style. 516 | void nvgFontFace(NVGcontext* ctx, const char* font); 517 | 518 | // Draws text string at specified location. If end is specified only the sub-string up to the end is drawn. 519 | float nvgText(NVGcontext* ctx, float x, float y, const char* string, const char* end); 520 | 521 | // Draws multi-line text string at specified location wrapped at the specified width. If end is specified only the sub-string up to the end is drawn. 522 | // White space is stripped at the beginning of the rows, the text is split at word boundaries or when new-line characters are encountered. 523 | // Words longer than the max width are slit at nearest character (i.e. no hyphenation). 524 | void nvgTextBox(NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end); 525 | 526 | // Measures the specified text string. Parameter bounds should be a pointer to float[4], 527 | // if the bounding box of the text should be returned. The bounds value are [xmin,ymin, xmax,ymax] 528 | // Returns the horizontal advance of the measured text (i.e. where the next character should drawn). 529 | // Measured values are returned in local coordinate space. 530 | float nvgTextBounds(NVGcontext* ctx, float x, float y, const char* string, const char* end, float* bounds); 531 | 532 | // Measures the specified multi-text string. Parameter bounds should be a pointer to float[4], 533 | // if the bounding box of the text should be returned. The bounds value are [xmin,ymin, xmax,ymax] 534 | // Measured values are returned in local coordinate space. 535 | void nvgTextBoxBounds(NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end, float* bounds); 536 | 537 | // Calculates the glyph x positions of the specified text. If end is specified only the sub-string will be used. 538 | // Measured values are returned in local coordinate space. 539 | int nvgTextGlyphPositions(NVGcontext* ctx, float x, float y, const char* string, const char* end, NVGglyphPosition* positions, int maxPositions); 540 | 541 | // Returns the vertical metrics based on the current text style. 542 | // Measured values are returned in local coordinate space. 543 | void nvgTextMetrics(NVGcontext* ctx, float* ascender, float* descender, float* lineh); 544 | 545 | // Breaks the specified text into lines. If end is specified only the sub-string will be used. 546 | // White space is stripped at the beginning of the rows, the text is split at word boundaries or when new-line characters are encountered. 547 | // Words longer than the max width are slit at nearest character (i.e. no hyphenation). 548 | int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, float breakRowWidth, NVGtextRow* rows, int maxRows); 549 | 550 | // 551 | // Internal Render API 552 | // 553 | enum NVGtexture { 554 | NVG_TEXTURE_ALPHA = 0x01, 555 | NVG_TEXTURE_RGBA = 0x02, 556 | }; 557 | 558 | struct NVGscissor { 559 | float xform[6]; 560 | float extent[2]; 561 | }; 562 | typedef struct NVGscissor NVGscissor; 563 | 564 | struct NVGvertex { 565 | float x,y,u,v; 566 | }; 567 | typedef struct NVGvertex NVGvertex; 568 | 569 | struct NVGpath { 570 | int first; 571 | int count; 572 | unsigned char closed; 573 | int nbevel; 574 | NVGvertex* fill; 575 | int nfill; 576 | NVGvertex* stroke; 577 | int nstroke; 578 | int winding; 579 | int convex; 580 | }; 581 | typedef struct NVGpath NVGpath; 582 | 583 | struct NVGparams { 584 | void* userPtr; 585 | int edgeAntiAlias; 586 | int (*renderCreate)(void* uptr); 587 | int (*renderCreateTexture)(void* uptr, int type, int w, int h, int imageFlags, const unsigned char* data); 588 | int (*renderDeleteTexture)(void* uptr, int image); 589 | int (*renderUpdateTexture)(void* uptr, int image, int x, int y, int w, int h, const unsigned char* data); 590 | int (*renderGetTextureSize)(void* uptr, int image, int* w, int* h); 591 | void (*renderViewport)(void* uptr, int width, int height); 592 | void (*renderCancel)(void* uptr); 593 | void (*renderFlush)(void* uptr); 594 | void (*renderFill)(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe, const float* bounds, const NVGpath* paths, int npaths); 595 | void (*renderStroke)(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe, float strokeWidth, const NVGpath* paths, int npaths); 596 | void (*renderTriangles)(void* uptr, NVGpaint* paint, NVGscissor* scissor, const NVGvertex* verts, int nverts); 597 | void (*renderDelete)(void* uptr); 598 | }; 599 | typedef struct NVGparams NVGparams; 600 | 601 | // Contructor and destructor, called by the render back-end. 602 | NVGcontext* nvgCreateInternal(NVGparams* params); 603 | void nvgDeleteInternal(NVGcontext* ctx); 604 | 605 | NVGparams* nvgInternalParams(NVGcontext* ctx); 606 | 607 | // Debug function to dump cached path data. 608 | void nvgDebugDumpPathCache(NVGcontext* ctx); 609 | 610 | #ifdef _MSC_VER 611 | #pragma warning(pop) 612 | #endif 613 | 614 | #define NVG_NOTUSED(v) for (;;) { (void)(1 ? (void)0 : ( (void)(v) ) ); break; } 615 | 616 | #ifdef __cplusplus 617 | } 618 | #endif 619 | 620 | #endif // NANOVG_H 621 | -------------------------------------------------------------------------------- /libs/nanovg/src/nanovg_gl.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2009-2013 Mikko Mononen memon@inside.org 3 | // 4 | // This software is provided 'as-is', without any express or implied 5 | // warranty. In no event will the authors be held liable for any damages 6 | // arising from the use of this software. 7 | // Permission is granted to anyone to use this software for any purpose, 8 | // including commercial applications, and to alter it and redistribute it 9 | // freely, subject to the following restrictions: 10 | // 1. The origin of this software must not be misrepresented; you must not 11 | // claim that you wrote the original software. If you use this software 12 | // in a product, an acknowledgment in the product documentation would be 13 | // appreciated but is not required. 14 | // 2. Altered source versions must be plainly marked as such, and must not be 15 | // misrepresented as being the original software. 16 | // 3. This notice may not be removed or altered from any source distribution. 17 | // 18 | #ifndef NANOVG_GL_H 19 | #define NANOVG_GL_H 20 | 21 | #ifdef __cplusplus 22 | extern "C" { 23 | #endif 24 | 25 | // Create flags 26 | 27 | enum NVGcreateFlags { 28 | // Flag indicating if geometry based anti-aliasing is used (may not be needed when using MSAA). 29 | NVG_ANTIALIAS = 1<<0, 30 | // Flag indicating if strokes should be drawn using stencil buffer. The rendering will be a little 31 | // slower, but path overlaps (i.e. self-intersecting or sharp turns) will be drawn just once. 32 | NVG_STENCIL_STROKES = 1<<1, 33 | // Flag indicating that additional debug checks are done. 34 | NVG_DEBUG = 1<<2, 35 | }; 36 | 37 | #if defined NANOVG_GL2_IMPLEMENTATION 38 | # define NANOVG_GL2 1 39 | # define NANOVG_GL_IMPLEMENTATION 1 40 | #elif defined NANOVG_GL3_IMPLEMENTATION 41 | # define NANOVG_GL3 1 42 | # define NANOVG_GL_IMPLEMENTATION 1 43 | # define NANOVG_GL_USE_UNIFORMBUFFER 1 44 | #elif defined NANOVG_GLES2_IMPLEMENTATION 45 | # define NANOVG_GLES2 1 46 | # define NANOVG_GL_IMPLEMENTATION 1 47 | #elif defined NANOVG_GLES3_IMPLEMENTATION 48 | # define NANOVG_GLES3 1 49 | # define NANOVG_GL_IMPLEMENTATION 1 50 | #endif 51 | 52 | #define NANOVG_GL_USE_STATE_FILTER (1) 53 | 54 | // Creates NanoVG contexts for different OpenGL (ES) versions. 55 | // Flags should be combination of the create flags above. 56 | 57 | #if defined NANOVG_GL2 58 | 59 | NVGcontext* nvgCreateGL2(int flags); 60 | void nvgDeleteGL2(NVGcontext* ctx); 61 | 62 | #endif 63 | 64 | #if defined NANOVG_GL3 65 | 66 | NVGcontext* nvgCreateGL3(int flags); 67 | void nvgDeleteGL3(NVGcontext* ctx); 68 | 69 | #endif 70 | 71 | #if defined NANOVG_GLES2 72 | 73 | NVGcontext* nvgCreateGLES2(int flags); 74 | void nvgDeleteGLES2(NVGcontext* ctx); 75 | 76 | #endif 77 | 78 | #if defined NANOVG_GLES3 79 | 80 | NVGcontext* nvgCreateGLES3(int flags); 81 | void nvgDeleteGLES3(NVGcontext* ctx); 82 | 83 | #endif 84 | 85 | // These are additional flags on top of NVGimageFlags. 86 | enum NVGimageFlagsGL { 87 | NVG_IMAGE_NODELETE = 1<<16, // Do not delete GL texture handle. 88 | }; 89 | 90 | int nvglCreateImageFromHandle(NVGcontext* ctx, GLuint textureId, int w, int h, int flags); 91 | GLuint nvglImageHandle(NVGcontext* ctx, int image); 92 | 93 | 94 | #ifdef __cplusplus 95 | } 96 | #endif 97 | 98 | #endif /* NANOVG_GL_H */ 99 | 100 | #ifdef NANOVG_GL_IMPLEMENTATION 101 | 102 | #include 103 | #include 104 | #include 105 | #include 106 | #include "nanovg.h" 107 | 108 | enum GLNVGuniformLoc { 109 | GLNVG_LOC_VIEWSIZE, 110 | GLNVG_LOC_TEX, 111 | GLNVG_LOC_FRAG, 112 | GLNVG_MAX_LOCS 113 | }; 114 | 115 | enum GLNVGshaderType { 116 | NSVG_SHADER_FILLGRAD, 117 | NSVG_SHADER_FILLIMG, 118 | NSVG_SHADER_SIMPLE, 119 | NSVG_SHADER_IMG 120 | }; 121 | 122 | #if NANOVG_GL_USE_UNIFORMBUFFER 123 | enum GLNVGuniformBindings { 124 | GLNVG_FRAG_BINDING = 0, 125 | }; 126 | #endif 127 | 128 | struct GLNVGshader { 129 | GLuint prog; 130 | GLuint frag; 131 | GLuint vert; 132 | GLint loc[GLNVG_MAX_LOCS]; 133 | }; 134 | typedef struct GLNVGshader GLNVGshader; 135 | 136 | struct GLNVGtexture { 137 | int id; 138 | GLuint tex; 139 | int width, height; 140 | int type; 141 | int flags; 142 | }; 143 | typedef struct GLNVGtexture GLNVGtexture; 144 | 145 | enum GLNVGcallType { 146 | GLNVG_NONE = 0, 147 | GLNVG_FILL, 148 | GLNVG_CONVEXFILL, 149 | GLNVG_STROKE, 150 | GLNVG_TRIANGLES, 151 | }; 152 | 153 | struct GLNVGcall { 154 | int type; 155 | int image; 156 | int pathOffset; 157 | int pathCount; 158 | int triangleOffset; 159 | int triangleCount; 160 | int uniformOffset; 161 | }; 162 | typedef struct GLNVGcall GLNVGcall; 163 | 164 | struct GLNVGpath { 165 | int fillOffset; 166 | int fillCount; 167 | int strokeOffset; 168 | int strokeCount; 169 | }; 170 | typedef struct GLNVGpath GLNVGpath; 171 | 172 | struct GLNVGfragUniforms { 173 | #if NANOVG_GL_USE_UNIFORMBUFFER 174 | float scissorMat[12]; // matrices are actually 3 vec4s 175 | float paintMat[12]; 176 | struct NVGcolor innerCol; 177 | struct NVGcolor outerCol; 178 | float scissorExt[2]; 179 | float scissorScale[2]; 180 | float extent[2]; 181 | float radius; 182 | float feather; 183 | float strokeMult; 184 | float strokeThr; 185 | int texType; 186 | int type; 187 | #else 188 | // note: after modifying layout or size of uniform array, 189 | // don't forget to also update the fragment shader source! 190 | #define NANOVG_GL_UNIFORMARRAY_SIZE 11 191 | union { 192 | struct { 193 | float scissorMat[12]; // matrices are actually 3 vec4s 194 | float paintMat[12]; 195 | struct NVGcolor innerCol; 196 | struct NVGcolor outerCol; 197 | float scissorExt[2]; 198 | float scissorScale[2]; 199 | float extent[2]; 200 | float radius; 201 | float feather; 202 | float strokeMult; 203 | float strokeThr; 204 | float texType; 205 | float type; 206 | }; 207 | float uniformArray[NANOVG_GL_UNIFORMARRAY_SIZE][4]; 208 | }; 209 | #endif 210 | }; 211 | typedef struct GLNVGfragUniforms GLNVGfragUniforms; 212 | 213 | struct GLNVGcontext { 214 | GLNVGshader shader; 215 | GLNVGtexture* textures; 216 | float view[2]; 217 | int ntextures; 218 | int ctextures; 219 | int textureId; 220 | GLuint vertBuf; 221 | #if defined NANOVG_GL3 222 | GLuint vertArr; 223 | #endif 224 | #if NANOVG_GL_USE_UNIFORMBUFFER 225 | GLuint fragBuf; 226 | #endif 227 | int fragSize; 228 | int flags; 229 | 230 | // Per frame buffers 231 | GLNVGcall* calls; 232 | int ccalls; 233 | int ncalls; 234 | GLNVGpath* paths; 235 | int cpaths; 236 | int npaths; 237 | struct NVGvertex* verts; 238 | int cverts; 239 | int nverts; 240 | unsigned char* uniforms; 241 | int cuniforms; 242 | int nuniforms; 243 | 244 | // cached state 245 | #if NANOVG_GL_USE_STATE_FILTER 246 | GLuint boundTexture; 247 | GLuint stencilMask; 248 | GLenum stencilFunc; 249 | GLint stencilFuncRef; 250 | GLuint stencilFuncMask; 251 | #endif 252 | }; 253 | typedef struct GLNVGcontext GLNVGcontext; 254 | 255 | static int glnvg__maxi(int a, int b) { return a > b ? a : b; } 256 | 257 | #ifdef NANOVG_GLES2 258 | static unsigned int glnvg__nearestPow2(unsigned int num) 259 | { 260 | unsigned n = num > 0 ? num - 1 : 0; 261 | n |= n >> 1; 262 | n |= n >> 2; 263 | n |= n >> 4; 264 | n |= n >> 8; 265 | n |= n >> 16; 266 | n++; 267 | return n; 268 | } 269 | #endif 270 | 271 | static void glnvg__bindTexture(GLNVGcontext* gl, GLuint tex) 272 | { 273 | #if NANOVG_GL_USE_STATE_FILTER 274 | if (gl->boundTexture != tex) { 275 | gl->boundTexture = tex; 276 | glBindTexture(GL_TEXTURE_2D, tex); 277 | } 278 | #else 279 | glBindTexture(GL_TEXTURE_2D, tex); 280 | #endif 281 | } 282 | 283 | static void glnvg__stencilMask(GLNVGcontext* gl, GLuint mask) 284 | { 285 | #if NANOVG_GL_USE_STATE_FILTER 286 | if (gl->stencilMask != mask) { 287 | gl->stencilMask = mask; 288 | glStencilMask(mask); 289 | } 290 | #else 291 | glStencilMask(mask); 292 | #endif 293 | } 294 | 295 | static void glnvg__stencilFunc(GLNVGcontext* gl, GLenum func, GLint ref, GLuint mask) 296 | { 297 | #if NANOVG_GL_USE_STATE_FILTER 298 | if ((gl->stencilFunc != func) || 299 | (gl->stencilFuncRef != ref) || 300 | (gl->stencilFuncMask != mask)) { 301 | 302 | gl->stencilFunc = func; 303 | gl->stencilFuncRef = ref; 304 | gl->stencilFuncMask = mask; 305 | glStencilFunc(func, ref, mask); 306 | } 307 | #else 308 | glStencilFunc(func, ref, mask); 309 | #endif 310 | } 311 | 312 | static GLNVGtexture* glnvg__allocTexture(GLNVGcontext* gl) 313 | { 314 | GLNVGtexture* tex = NULL; 315 | int i; 316 | 317 | for (i = 0; i < gl->ntextures; i++) { 318 | if (gl->textures[i].id == 0) { 319 | tex = &gl->textures[i]; 320 | break; 321 | } 322 | } 323 | if (tex == NULL) { 324 | if (gl->ntextures+1 > gl->ctextures) { 325 | GLNVGtexture* textures; 326 | int ctextures = glnvg__maxi(gl->ntextures+1, 4) + gl->ctextures/2; // 1.5x Overallocate 327 | textures = (GLNVGtexture*)realloc(gl->textures, sizeof(GLNVGtexture)*ctextures); 328 | if (textures == NULL) return NULL; 329 | gl->textures = textures; 330 | gl->ctextures = ctextures; 331 | } 332 | tex = &gl->textures[gl->ntextures++]; 333 | } 334 | 335 | memset(tex, 0, sizeof(*tex)); 336 | tex->id = ++gl->textureId; 337 | 338 | return tex; 339 | } 340 | 341 | static GLNVGtexture* glnvg__findTexture(GLNVGcontext* gl, int id) 342 | { 343 | int i; 344 | for (i = 0; i < gl->ntextures; i++) 345 | if (gl->textures[i].id == id) 346 | return &gl->textures[i]; 347 | return NULL; 348 | } 349 | 350 | static int glnvg__deleteTexture(GLNVGcontext* gl, int id) 351 | { 352 | int i; 353 | for (i = 0; i < gl->ntextures; i++) { 354 | if (gl->textures[i].id == id) { 355 | if (gl->textures[i].tex != 0 && (gl->textures[i].flags & NVG_IMAGE_NODELETE) == 0) 356 | glDeleteTextures(1, &gl->textures[i].tex); 357 | memset(&gl->textures[i], 0, sizeof(gl->textures[i])); 358 | return 1; 359 | } 360 | } 361 | return 0; 362 | } 363 | 364 | static void glnvg__dumpShaderError(GLuint shader, const char* name, const char* type) 365 | { 366 | char str[512+1]; 367 | int len = 0; 368 | glGetShaderInfoLog(shader, 512, &len, str); 369 | if (len > 512) len = 512; 370 | str[len] = '\0'; 371 | printf("Shader %s/%s error:\n%s\n", name, type, str); 372 | } 373 | 374 | static void glnvg__dumpProgramError(GLuint prog, const char* name) 375 | { 376 | char str[512+1]; 377 | int len = 0; 378 | glGetProgramInfoLog(prog, 512, &len, str); 379 | if (len > 512) len = 512; 380 | str[len] = '\0'; 381 | printf("Program %s error:\n%s\n", name, str); 382 | } 383 | 384 | static void glnvg__checkError(GLNVGcontext* gl, const char* str) 385 | { 386 | GLenum err; 387 | if ((gl->flags & NVG_DEBUG) == 0) return; 388 | err = glGetError(); 389 | if (err != GL_NO_ERROR) { 390 | printf("Error %08x after %s\n", err, str); 391 | return; 392 | } 393 | } 394 | 395 | static int glnvg__createShader(GLNVGshader* shader, const char* name, const char* header, const char* opts, const char* vshader, const char* fshader) 396 | { 397 | GLint status; 398 | GLuint prog, vert, frag; 399 | const char* str[3]; 400 | str[0] = header; 401 | str[1] = opts != NULL ? opts : ""; 402 | 403 | memset(shader, 0, sizeof(*shader)); 404 | 405 | prog = glCreateProgram(); 406 | vert = glCreateShader(GL_VERTEX_SHADER); 407 | frag = glCreateShader(GL_FRAGMENT_SHADER); 408 | str[2] = vshader; 409 | glShaderSource(vert, 3, str, 0); 410 | str[2] = fshader; 411 | glShaderSource(frag, 3, str, 0); 412 | 413 | glCompileShader(vert); 414 | glGetShaderiv(vert, GL_COMPILE_STATUS, &status); 415 | if (status != GL_TRUE) { 416 | glnvg__dumpShaderError(vert, name, "vert"); 417 | return 0; 418 | } 419 | 420 | glCompileShader(frag); 421 | glGetShaderiv(frag, GL_COMPILE_STATUS, &status); 422 | if (status != GL_TRUE) { 423 | glnvg__dumpShaderError(frag, name, "frag"); 424 | return 0; 425 | } 426 | 427 | glAttachShader(prog, vert); 428 | glAttachShader(prog, frag); 429 | 430 | glBindAttribLocation(prog, 0, "vertex"); 431 | glBindAttribLocation(prog, 1, "tcoord"); 432 | 433 | glLinkProgram(prog); 434 | glGetProgramiv(prog, GL_LINK_STATUS, &status); 435 | if (status != GL_TRUE) { 436 | glnvg__dumpProgramError(prog, name); 437 | return 0; 438 | } 439 | 440 | shader->prog = prog; 441 | shader->vert = vert; 442 | shader->frag = frag; 443 | 444 | return 1; 445 | } 446 | 447 | static void glnvg__deleteShader(GLNVGshader* shader) 448 | { 449 | if (shader->prog != 0) 450 | glDeleteProgram(shader->prog); 451 | if (shader->vert != 0) 452 | glDeleteShader(shader->vert); 453 | if (shader->frag != 0) 454 | glDeleteShader(shader->frag); 455 | } 456 | 457 | static void glnvg__getUniforms(GLNVGshader* shader) 458 | { 459 | shader->loc[GLNVG_LOC_VIEWSIZE] = glGetUniformLocation(shader->prog, "viewSize"); 460 | shader->loc[GLNVG_LOC_TEX] = glGetUniformLocation(shader->prog, "tex"); 461 | 462 | #if NANOVG_GL_USE_UNIFORMBUFFER 463 | shader->loc[GLNVG_LOC_FRAG] = glGetUniformBlockIndex(shader->prog, "frag"); 464 | #else 465 | shader->loc[GLNVG_LOC_FRAG] = glGetUniformLocation(shader->prog, "frag"); 466 | #endif 467 | } 468 | 469 | static int glnvg__renderCreate(void* uptr) 470 | { 471 | GLNVGcontext* gl = (GLNVGcontext*)uptr; 472 | int align = 4; 473 | 474 | // TODO: mediump float may not be enough for GLES2 in iOS. 475 | // see the following discussion: https://github.com/memononen/nanovg/issues/46 476 | static const char* shaderHeader = 477 | #if defined NANOVG_GL2 478 | "#define NANOVG_GL2 1\n" 479 | #elif defined NANOVG_GL3 480 | "#version 150 core\n" 481 | "#define NANOVG_GL3 1\n" 482 | #elif defined NANOVG_GLES2 483 | "#version 100\n" 484 | "#define NANOVG_GL2 1\n" 485 | #elif defined NANOVG_GLES3 486 | "#version 300 es\n" 487 | "#define NANOVG_GL3 1\n" 488 | #endif 489 | 490 | #if NANOVG_GL_USE_UNIFORMBUFFER 491 | "#define USE_UNIFORMBUFFER 1\n" 492 | #else 493 | "#define UNIFORMARRAY_SIZE 11\n" 494 | #endif 495 | "\n"; 496 | 497 | static const char* fillVertShader = 498 | "#ifdef NANOVG_GL3\n" 499 | " uniform vec2 viewSize;\n" 500 | " in vec2 vertex;\n" 501 | " in vec2 tcoord;\n" 502 | " out vec2 ftcoord;\n" 503 | " out vec2 fpos;\n" 504 | "#else\n" 505 | " uniform vec2 viewSize;\n" 506 | " attribute vec2 vertex;\n" 507 | " attribute vec2 tcoord;\n" 508 | " varying vec2 ftcoord;\n" 509 | " varying vec2 fpos;\n" 510 | "#endif\n" 511 | "void main(void) {\n" 512 | " ftcoord = tcoord;\n" 513 | " fpos = vertex;\n" 514 | " gl_Position = vec4(2.0*vertex.x/viewSize.x - 1.0, 1.0 - 2.0*vertex.y/viewSize.y, 0, 1);\n" 515 | "}\n"; 516 | 517 | static const char* fillFragShader = 518 | "#ifdef GL_ES\n" 519 | "#if defined(GL_FRAGMENT_PRECISION_HIGH) || defined(NANOVG_GL3)\n" 520 | " precision highp float;\n" 521 | "#else\n" 522 | " precision mediump float;\n" 523 | "#endif\n" 524 | "#endif\n" 525 | "#ifdef NANOVG_GL3\n" 526 | "#ifdef USE_UNIFORMBUFFER\n" 527 | " layout(std140) uniform frag {\n" 528 | " mat3 scissorMat;\n" 529 | " mat3 paintMat;\n" 530 | " vec4 innerCol;\n" 531 | " vec4 outerCol;\n" 532 | " vec2 scissorExt;\n" 533 | " vec2 scissorScale;\n" 534 | " vec2 extent;\n" 535 | " float radius;\n" 536 | " float feather;\n" 537 | " float strokeMult;\n" 538 | " float strokeThr;\n" 539 | " int texType;\n" 540 | " int type;\n" 541 | " };\n" 542 | "#else\n" // NANOVG_GL3 && !USE_UNIFORMBUFFER 543 | " uniform vec4 frag[UNIFORMARRAY_SIZE];\n" 544 | "#endif\n" 545 | " uniform sampler2D tex;\n" 546 | " in vec2 ftcoord;\n" 547 | " in vec2 fpos;\n" 548 | " out vec4 outColor;\n" 549 | "#else\n" // !NANOVG_GL3 550 | " uniform vec4 frag[UNIFORMARRAY_SIZE];\n" 551 | " uniform sampler2D tex;\n" 552 | " varying vec2 ftcoord;\n" 553 | " varying vec2 fpos;\n" 554 | "#endif\n" 555 | "#ifndef USE_UNIFORMBUFFER\n" 556 | " #define scissorMat mat3(frag[0].xyz, frag[1].xyz, frag[2].xyz)\n" 557 | " #define paintMat mat3(frag[3].xyz, frag[4].xyz, frag[5].xyz)\n" 558 | " #define innerCol frag[6]\n" 559 | " #define outerCol frag[7]\n" 560 | " #define scissorExt frag[8].xy\n" 561 | " #define scissorScale frag[8].zw\n" 562 | " #define extent frag[9].xy\n" 563 | " #define radius frag[9].z\n" 564 | " #define feather frag[9].w\n" 565 | " #define strokeMult frag[10].x\n" 566 | " #define strokeThr frag[10].y\n" 567 | " #define texType int(frag[10].z)\n" 568 | " #define type int(frag[10].w)\n" 569 | "#endif\n" 570 | "\n" 571 | "float sdroundrect(vec2 pt, vec2 ext, float rad) {\n" 572 | " vec2 ext2 = ext - vec2(rad,rad);\n" 573 | " vec2 d = abs(pt) - ext2;\n" 574 | " return min(max(d.x,d.y),0.0) + length(max(d,0.0)) - rad;\n" 575 | "}\n" 576 | "\n" 577 | "// Scissoring\n" 578 | "float scissorMask(vec2 p) {\n" 579 | " vec2 sc = (abs((scissorMat * vec3(p,1.0)).xy) - scissorExt);\n" 580 | " sc = vec2(0.5,0.5) - sc * scissorScale;\n" 581 | " return clamp(sc.x,0.0,1.0) * clamp(sc.y,0.0,1.0);\n" 582 | "}\n" 583 | "#ifdef EDGE_AA\n" 584 | "// Stroke - from [0..1] to clipped pyramid, where the slope is 1px.\n" 585 | "float strokeMask() {\n" 586 | " return min(1.0, (1.0-abs(ftcoord.x*2.0-1.0))*strokeMult) * min(1.0, ftcoord.y);\n" 587 | "}\n" 588 | "#endif\n" 589 | "\n" 590 | "void main(void) {\n" 591 | " vec4 result;\n" 592 | " float scissor = scissorMask(fpos);\n" 593 | "#ifdef EDGE_AA\n" 594 | " float strokeAlpha = strokeMask();\n" 595 | "#else\n" 596 | " float strokeAlpha = 1.0;\n" 597 | "#endif\n" 598 | " if (type == 0) { // Gradient\n" 599 | " // Calculate gradient color using box gradient\n" 600 | " vec2 pt = (paintMat * vec3(fpos,1.0)).xy;\n" 601 | " float d = clamp((sdroundrect(pt, extent, radius) + feather*0.5) / feather, 0.0, 1.0);\n" 602 | " vec4 color = mix(innerCol,outerCol,d);\n" 603 | " // Combine alpha\n" 604 | " color *= strokeAlpha * scissor;\n" 605 | " result = color;\n" 606 | " } else if (type == 1) { // Image\n" 607 | " // Calculate color fron texture\n" 608 | " vec2 pt = (paintMat * vec3(fpos,1.0)).xy / extent;\n" 609 | "#ifdef NANOVG_GL3\n" 610 | " vec4 color = texture(tex, pt);\n" 611 | "#else\n" 612 | " vec4 color = texture2D(tex, pt);\n" 613 | "#endif\n" 614 | " if (texType == 1) color = vec4(color.xyz*color.w,color.w);" 615 | " if (texType == 2) color = vec4(color.x);" 616 | " // Apply color tint and alpha.\n" 617 | " color *= innerCol;\n" 618 | " // Combine alpha\n" 619 | " color *= strokeAlpha * scissor;\n" 620 | " result = color;\n" 621 | " } else if (type == 2) { // Stencil fill\n" 622 | " result = vec4(1,1,1,1);\n" 623 | " } else if (type == 3) { // Textured tris\n" 624 | "#ifdef NANOVG_GL3\n" 625 | " vec4 color = texture(tex, ftcoord);\n" 626 | "#else\n" 627 | " vec4 color = texture2D(tex, ftcoord);\n" 628 | "#endif\n" 629 | " if (texType == 1) color = vec4(color.xyz*color.w,color.w);" 630 | " if (texType == 2) color = vec4(color.x);" 631 | " color *= scissor;\n" 632 | " result = color * innerCol;\n" 633 | " }\n" 634 | "#ifdef EDGE_AA\n" 635 | " if (strokeAlpha < strokeThr) discard;\n" 636 | "#endif\n" 637 | "#ifdef NANOVG_GL3\n" 638 | " outColor = result;\n" 639 | "#else\n" 640 | " gl_FragColor = result;\n" 641 | "#endif\n" 642 | "}\n"; 643 | 644 | glnvg__checkError(gl, "init"); 645 | 646 | if (gl->flags & NVG_ANTIALIAS) { 647 | if (glnvg__createShader(&gl->shader, "shader", shaderHeader, "#define EDGE_AA 1\n", fillVertShader, fillFragShader) == 0) 648 | return 0; 649 | } else { 650 | if (glnvg__createShader(&gl->shader, "shader", shaderHeader, NULL, fillVertShader, fillFragShader) == 0) 651 | return 0; 652 | } 653 | 654 | glnvg__checkError(gl, "uniform locations"); 655 | glnvg__getUniforms(&gl->shader); 656 | 657 | // Create dynamic vertex array 658 | #if defined NANOVG_GL3 659 | glGenVertexArrays(1, &gl->vertArr); 660 | #endif 661 | glGenBuffers(1, &gl->vertBuf); 662 | 663 | #if NANOVG_GL_USE_UNIFORMBUFFER 664 | // Create UBOs 665 | glUniformBlockBinding(gl->shader.prog, gl->shader.loc[GLNVG_LOC_FRAG], GLNVG_FRAG_BINDING); 666 | glGenBuffers(1, &gl->fragBuf); 667 | glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &align); 668 | #endif 669 | gl->fragSize = sizeof(GLNVGfragUniforms) + align - sizeof(GLNVGfragUniforms) % align; 670 | 671 | glnvg__checkError(gl, "create done"); 672 | 673 | glFinish(); 674 | 675 | return 1; 676 | } 677 | 678 | static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int imageFlags, const unsigned char* data) 679 | { 680 | GLNVGcontext* gl = (GLNVGcontext*)uptr; 681 | GLNVGtexture* tex = glnvg__allocTexture(gl); 682 | 683 | if (tex == NULL) return 0; 684 | 685 | #ifdef NANOVG_GLES2 686 | // Check for non-power of 2. 687 | if (glnvg__nearestPow2(w) != (unsigned int)w || glnvg__nearestPow2(h) != (unsigned int)h) { 688 | // No repeat 689 | if ((imageFlags & NVG_IMAGE_REPEATX) != 0 || (imageFlags & NVG_IMAGE_REPEATY) != 0) { 690 | printf("Repeat X/Y is not supported for non power-of-two textures (%d x %d)\n", w, h); 691 | imageFlags &= ~(NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY); 692 | } 693 | // No mips. 694 | if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) { 695 | printf("Mip-maps is not support for non power-of-two textures (%d x %d)\n", w, h); 696 | imageFlags &= ~NVG_IMAGE_GENERATE_MIPMAPS; 697 | } 698 | } 699 | #endif 700 | 701 | glGenTextures(1, &tex->tex); 702 | tex->width = w; 703 | tex->height = h; 704 | tex->type = type; 705 | tex->flags = imageFlags; 706 | glnvg__bindTexture(gl, tex->tex); 707 | 708 | glPixelStorei(GL_UNPACK_ALIGNMENT,1); 709 | #ifndef NANOVG_GLES2 710 | glPixelStorei(GL_UNPACK_ROW_LENGTH, tex->width); 711 | glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); 712 | glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); 713 | #endif 714 | 715 | #if defined (NANOVG_GL2) 716 | // GL 1.4 and later has support for generating mipmaps using a tex parameter. 717 | if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) { 718 | glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); 719 | } 720 | #endif 721 | 722 | if (type == NVG_TEXTURE_RGBA) 723 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); 724 | else 725 | #if defined(NANOVG_GLES2) 726 | glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); 727 | #elif defined(NANOVG_GLES3) 728 | glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data); 729 | #else 730 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data); 731 | #endif 732 | 733 | if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) { 734 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 735 | } else { 736 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 737 | } 738 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 739 | 740 | if (imageFlags & NVG_IMAGE_REPEATX) 741 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 742 | else 743 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 744 | 745 | if (imageFlags & NVG_IMAGE_REPEATY) 746 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 747 | else 748 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 749 | 750 | glPixelStorei(GL_UNPACK_ALIGNMENT, 4); 751 | #ifndef NANOVG_GLES2 752 | glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); 753 | glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); 754 | glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); 755 | #endif 756 | 757 | // The new way to build mipmaps on GLES and GL3 758 | #if !defined(NANOVG_GL2) 759 | if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) { 760 | glGenerateMipmap(GL_TEXTURE_2D); 761 | } 762 | #endif 763 | 764 | glnvg__checkError(gl, "create tex"); 765 | glnvg__bindTexture(gl, 0); 766 | 767 | return tex->id; 768 | } 769 | 770 | 771 | static int glnvg__renderDeleteTexture(void* uptr, int image) 772 | { 773 | GLNVGcontext* gl = (GLNVGcontext*)uptr; 774 | return glnvg__deleteTexture(gl, image); 775 | } 776 | 777 | static int glnvg__renderUpdateTexture(void* uptr, int image, int x, int y, int w, int h, const unsigned char* data) 778 | { 779 | GLNVGcontext* gl = (GLNVGcontext*)uptr; 780 | GLNVGtexture* tex = glnvg__findTexture(gl, image); 781 | 782 | if (tex == NULL) return 0; 783 | glnvg__bindTexture(gl, tex->tex); 784 | 785 | glPixelStorei(GL_UNPACK_ALIGNMENT,1); 786 | 787 | #ifndef NANOVG_GLES2 788 | glPixelStorei(GL_UNPACK_ROW_LENGTH, tex->width); 789 | glPixelStorei(GL_UNPACK_SKIP_PIXELS, x); 790 | glPixelStorei(GL_UNPACK_SKIP_ROWS, y); 791 | #else 792 | // No support for all of skip, need to update a whole row at a time. 793 | if (tex->type == NVG_TEXTURE_RGBA) 794 | data += y*tex->width*4; 795 | else 796 | data += y*tex->width; 797 | x = 0; 798 | w = tex->width; 799 | #endif 800 | 801 | if (tex->type == NVG_TEXTURE_RGBA) 802 | glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RGBA, GL_UNSIGNED_BYTE, data); 803 | else 804 | #ifdef NANOVG_GLES2 805 | glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_LUMINANCE, GL_UNSIGNED_BYTE, data); 806 | #else 807 | glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RED, GL_UNSIGNED_BYTE, data); 808 | #endif 809 | 810 | glPixelStorei(GL_UNPACK_ALIGNMENT, 4); 811 | #ifndef NANOVG_GLES2 812 | glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); 813 | glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); 814 | glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); 815 | #endif 816 | 817 | glnvg__bindTexture(gl, 0); 818 | 819 | return 1; 820 | } 821 | 822 | static int glnvg__renderGetTextureSize(void* uptr, int image, int* w, int* h) 823 | { 824 | GLNVGcontext* gl = (GLNVGcontext*)uptr; 825 | GLNVGtexture* tex = glnvg__findTexture(gl, image); 826 | if (tex == NULL) return 0; 827 | *w = tex->width; 828 | *h = tex->height; 829 | return 1; 830 | } 831 | 832 | static void glnvg__xformToMat3x4(float* m3, float* t) 833 | { 834 | m3[0] = t[0]; 835 | m3[1] = t[1]; 836 | m3[2] = 0.0f; 837 | m3[3] = 0.0f; 838 | m3[4] = t[2]; 839 | m3[5] = t[3]; 840 | m3[6] = 0.0f; 841 | m3[7] = 0.0f; 842 | m3[8] = t[4]; 843 | m3[9] = t[5]; 844 | m3[10] = 1.0f; 845 | m3[11] = 0.0f; 846 | } 847 | 848 | static NVGcolor glnvg__premulColor(NVGcolor c) 849 | { 850 | c.r *= c.a; 851 | c.g *= c.a; 852 | c.b *= c.a; 853 | return c; 854 | } 855 | 856 | static int glnvg__convertPaint(GLNVGcontext* gl, GLNVGfragUniforms* frag, NVGpaint* paint, 857 | NVGscissor* scissor, float width, float fringe, float strokeThr) 858 | { 859 | GLNVGtexture* tex = NULL; 860 | float invxform[6]; 861 | 862 | memset(frag, 0, sizeof(*frag)); 863 | 864 | frag->innerCol = glnvg__premulColor(paint->innerColor); 865 | frag->outerCol = glnvg__premulColor(paint->outerColor); 866 | 867 | if (scissor->extent[0] < -0.5f || scissor->extent[1] < -0.5f) { 868 | memset(frag->scissorMat, 0, sizeof(frag->scissorMat)); 869 | frag->scissorExt[0] = 1.0f; 870 | frag->scissorExt[1] = 1.0f; 871 | frag->scissorScale[0] = 1.0f; 872 | frag->scissorScale[1] = 1.0f; 873 | } else { 874 | nvgTransformInverse(invxform, scissor->xform); 875 | glnvg__xformToMat3x4(frag->scissorMat, invxform); 876 | frag->scissorExt[0] = scissor->extent[0]; 877 | frag->scissorExt[1] = scissor->extent[1]; 878 | frag->scissorScale[0] = sqrtf(scissor->xform[0]*scissor->xform[0] + scissor->xform[2]*scissor->xform[2]) / fringe; 879 | frag->scissorScale[1] = sqrtf(scissor->xform[1]*scissor->xform[1] + scissor->xform[3]*scissor->xform[3]) / fringe; 880 | } 881 | 882 | memcpy(frag->extent, paint->extent, sizeof(frag->extent)); 883 | frag->strokeMult = (width*0.5f + fringe*0.5f) / fringe; 884 | frag->strokeThr = strokeThr; 885 | 886 | if (paint->image != 0) { 887 | tex = glnvg__findTexture(gl, paint->image); 888 | if (tex == NULL) return 0; 889 | if ((tex->flags & NVG_IMAGE_FLIPY) != 0) { 890 | float flipped[6]; 891 | nvgTransformScale(flipped, 1.0f, -1.0f); 892 | nvgTransformMultiply(flipped, paint->xform); 893 | nvgTransformInverse(invxform, flipped); 894 | } else { 895 | nvgTransformInverse(invxform, paint->xform); 896 | } 897 | frag->type = NSVG_SHADER_FILLIMG; 898 | 899 | if (tex->type == NVG_TEXTURE_RGBA) 900 | frag->texType = (tex->flags & NVG_IMAGE_PREMULTIPLIED) ? 0 : 1; 901 | else 902 | frag->texType = 2; 903 | // printf("frag->texType = %d\n", frag->texType); 904 | } else { 905 | frag->type = NSVG_SHADER_FILLGRAD; 906 | frag->radius = paint->radius; 907 | frag->feather = paint->feather; 908 | nvgTransformInverse(invxform, paint->xform); 909 | } 910 | 911 | glnvg__xformToMat3x4(frag->paintMat, invxform); 912 | 913 | return 1; 914 | } 915 | 916 | static GLNVGfragUniforms* nvg__fragUniformPtr(GLNVGcontext* gl, int i); 917 | 918 | static void glnvg__setUniforms(GLNVGcontext* gl, int uniformOffset, int image) 919 | { 920 | #if NANOVG_GL_USE_UNIFORMBUFFER 921 | glBindBufferRange(GL_UNIFORM_BUFFER, GLNVG_FRAG_BINDING, gl->fragBuf, uniformOffset, sizeof(GLNVGfragUniforms)); 922 | #else 923 | GLNVGfragUniforms* frag = nvg__fragUniformPtr(gl, uniformOffset); 924 | glUniform4fv(gl->shader.loc[GLNVG_LOC_FRAG], NANOVG_GL_UNIFORMARRAY_SIZE, &(frag->uniformArray[0][0])); 925 | #endif 926 | 927 | if (image != 0) { 928 | GLNVGtexture* tex = glnvg__findTexture(gl, image); 929 | glnvg__bindTexture(gl, tex != NULL ? tex->tex : 0); 930 | glnvg__checkError(gl, "tex paint tex"); 931 | } else { 932 | glnvg__bindTexture(gl, 0); 933 | } 934 | } 935 | 936 | static void glnvg__renderViewport(void* uptr, int width, int height) 937 | { 938 | GLNVGcontext* gl = (GLNVGcontext*)uptr; 939 | gl->view[0] = (float)width; 940 | gl->view[1] = (float)height; 941 | } 942 | 943 | static void glnvg__fill(GLNVGcontext* gl, GLNVGcall* call) 944 | { 945 | GLNVGpath* paths = &gl->paths[call->pathOffset]; 946 | int i, npaths = call->pathCount; 947 | 948 | // Draw shapes 949 | glEnable(GL_STENCIL_TEST); 950 | glnvg__stencilMask(gl, 0xff); 951 | glnvg__stencilFunc(gl, GL_ALWAYS, 0, 0xff); 952 | glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); 953 | 954 | // set bindpoint for solid loc 955 | glnvg__setUniforms(gl, call->uniformOffset, 0); 956 | glnvg__checkError(gl, "fill simple"); 957 | 958 | glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR_WRAP); 959 | glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_DECR_WRAP); 960 | glDisable(GL_CULL_FACE); 961 | for (i = 0; i < npaths; i++) 962 | glDrawArrays(GL_TRIANGLE_FAN, paths[i].fillOffset, paths[i].fillCount); 963 | glEnable(GL_CULL_FACE); 964 | 965 | // Draw anti-aliased pixels 966 | glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); 967 | 968 | glnvg__setUniforms(gl, call->uniformOffset + gl->fragSize, call->image); 969 | glnvg__checkError(gl, "fill fill"); 970 | 971 | if (gl->flags & NVG_ANTIALIAS) { 972 | glnvg__stencilFunc(gl, GL_EQUAL, 0x00, 0xff); 973 | glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); 974 | // Draw fringes 975 | for (i = 0; i < npaths; i++) 976 | glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); 977 | } 978 | 979 | // Draw fill 980 | glnvg__stencilFunc(gl, GL_NOTEQUAL, 0x0, 0xff); 981 | glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); 982 | glDrawArrays(GL_TRIANGLES, call->triangleOffset, call->triangleCount); 983 | 984 | glDisable(GL_STENCIL_TEST); 985 | } 986 | 987 | static void glnvg__convexFill(GLNVGcontext* gl, GLNVGcall* call) 988 | { 989 | GLNVGpath* paths = &gl->paths[call->pathOffset]; 990 | int i, npaths = call->pathCount; 991 | 992 | glnvg__setUniforms(gl, call->uniformOffset, call->image); 993 | glnvg__checkError(gl, "convex fill"); 994 | 995 | for (i = 0; i < npaths; i++) 996 | glDrawArrays(GL_TRIANGLE_FAN, paths[i].fillOffset, paths[i].fillCount); 997 | if (gl->flags & NVG_ANTIALIAS) { 998 | // Draw fringes 999 | for (i = 0; i < npaths; i++) 1000 | glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); 1001 | } 1002 | } 1003 | 1004 | static void glnvg__stroke(GLNVGcontext* gl, GLNVGcall* call) 1005 | { 1006 | GLNVGpath* paths = &gl->paths[call->pathOffset]; 1007 | int npaths = call->pathCount, i; 1008 | 1009 | if (gl->flags & NVG_STENCIL_STROKES) { 1010 | 1011 | glEnable(GL_STENCIL_TEST); 1012 | glnvg__stencilMask(gl, 0xff); 1013 | 1014 | // Fill the stroke base without overlap 1015 | glnvg__stencilFunc(gl, GL_EQUAL, 0x0, 0xff); 1016 | glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); 1017 | glnvg__setUniforms(gl, call->uniformOffset + gl->fragSize, call->image); 1018 | glnvg__checkError(gl, "stroke fill 0"); 1019 | for (i = 0; i < npaths; i++) 1020 | glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); 1021 | 1022 | // Draw anti-aliased pixels. 1023 | glnvg__setUniforms(gl, call->uniformOffset, call->image); 1024 | glnvg__stencilFunc(gl, GL_EQUAL, 0x00, 0xff); 1025 | glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); 1026 | for (i = 0; i < npaths; i++) 1027 | glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); 1028 | 1029 | // Clear stencil buffer. 1030 | glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); 1031 | glnvg__stencilFunc(gl, GL_ALWAYS, 0x0, 0xff); 1032 | glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); 1033 | glnvg__checkError(gl, "stroke fill 1"); 1034 | for (i = 0; i < npaths; i++) 1035 | glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); 1036 | glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); 1037 | 1038 | glDisable(GL_STENCIL_TEST); 1039 | 1040 | // glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset + gl->fragSize), paint, scissor, strokeWidth, fringe, 1.0f - 0.5f/255.0f); 1041 | 1042 | } else { 1043 | glnvg__setUniforms(gl, call->uniformOffset, call->image); 1044 | glnvg__checkError(gl, "stroke fill"); 1045 | // Draw Strokes 1046 | for (i = 0; i < npaths; i++) 1047 | glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount); 1048 | } 1049 | } 1050 | 1051 | static void glnvg__triangles(GLNVGcontext* gl, GLNVGcall* call) 1052 | { 1053 | glnvg__setUniforms(gl, call->uniformOffset, call->image); 1054 | glnvg__checkError(gl, "triangles fill"); 1055 | 1056 | glDrawArrays(GL_TRIANGLES, call->triangleOffset, call->triangleCount); 1057 | } 1058 | 1059 | static void glnvg__renderCancel(void* uptr) { 1060 | GLNVGcontext* gl = (GLNVGcontext*)uptr; 1061 | gl->nverts = 0; 1062 | gl->npaths = 0; 1063 | gl->ncalls = 0; 1064 | gl->nuniforms = 0; 1065 | } 1066 | 1067 | static void glnvg__renderFlush(void* uptr) 1068 | { 1069 | GLNVGcontext* gl = (GLNVGcontext*)uptr; 1070 | int i; 1071 | 1072 | if (gl->ncalls > 0) { 1073 | 1074 | // Setup require GL state. 1075 | glUseProgram(gl->shader.prog); 1076 | 1077 | glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); 1078 | glEnable(GL_CULL_FACE); 1079 | glCullFace(GL_BACK); 1080 | glFrontFace(GL_CCW); 1081 | glEnable(GL_BLEND); 1082 | glDisable(GL_DEPTH_TEST); 1083 | glDisable(GL_SCISSOR_TEST); 1084 | glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); 1085 | glStencilMask(0xffffffff); 1086 | glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); 1087 | glStencilFunc(GL_ALWAYS, 0, 0xffffffff); 1088 | glActiveTexture(GL_TEXTURE0); 1089 | glBindTexture(GL_TEXTURE_2D, 0); 1090 | #if NANOVG_GL_USE_STATE_FILTER 1091 | gl->boundTexture = 0; 1092 | gl->stencilMask = 0xffffffff; 1093 | gl->stencilFunc = GL_ALWAYS; 1094 | gl->stencilFuncRef = 0; 1095 | gl->stencilFuncMask = 0xffffffff; 1096 | #endif 1097 | 1098 | #if NANOVG_GL_USE_UNIFORMBUFFER 1099 | // Upload ubo for frag shaders 1100 | glBindBuffer(GL_UNIFORM_BUFFER, gl->fragBuf); 1101 | glBufferData(GL_UNIFORM_BUFFER, gl->nuniforms * gl->fragSize, gl->uniforms, GL_STREAM_DRAW); 1102 | #endif 1103 | 1104 | // Upload vertex data 1105 | #if defined NANOVG_GL3 1106 | glBindVertexArray(gl->vertArr); 1107 | #endif 1108 | glBindBuffer(GL_ARRAY_BUFFER, gl->vertBuf); 1109 | glBufferData(GL_ARRAY_BUFFER, gl->nverts * sizeof(NVGvertex), gl->verts, GL_STREAM_DRAW); 1110 | glEnableVertexAttribArray(0); 1111 | glEnableVertexAttribArray(1); 1112 | glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(NVGvertex), (const GLvoid*)(size_t)0); 1113 | glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(NVGvertex), (const GLvoid*)(0 + 2*sizeof(float))); 1114 | 1115 | // Set view and texture just once per frame. 1116 | glUniform1i(gl->shader.loc[GLNVG_LOC_TEX], 0); 1117 | glUniform2fv(gl->shader.loc[GLNVG_LOC_VIEWSIZE], 1, gl->view); 1118 | 1119 | #if NANOVG_GL_USE_UNIFORMBUFFER 1120 | glBindBuffer(GL_UNIFORM_BUFFER, gl->fragBuf); 1121 | #endif 1122 | 1123 | for (i = 0; i < gl->ncalls; i++) { 1124 | GLNVGcall* call = &gl->calls[i]; 1125 | if (call->type == GLNVG_FILL) 1126 | glnvg__fill(gl, call); 1127 | else if (call->type == GLNVG_CONVEXFILL) 1128 | glnvg__convexFill(gl, call); 1129 | else if (call->type == GLNVG_STROKE) 1130 | glnvg__stroke(gl, call); 1131 | else if (call->type == GLNVG_TRIANGLES) 1132 | glnvg__triangles(gl, call); 1133 | } 1134 | 1135 | glDisableVertexAttribArray(0); 1136 | glDisableVertexAttribArray(1); 1137 | #if defined NANOVG_GL3 1138 | glBindVertexArray(0); 1139 | #endif 1140 | glDisable(GL_CULL_FACE); 1141 | glBindBuffer(GL_ARRAY_BUFFER, 0); 1142 | glUseProgram(0); 1143 | glnvg__bindTexture(gl, 0); 1144 | } 1145 | 1146 | // Reset calls 1147 | gl->nverts = 0; 1148 | gl->npaths = 0; 1149 | gl->ncalls = 0; 1150 | gl->nuniforms = 0; 1151 | } 1152 | 1153 | static int glnvg__maxVertCount(const NVGpath* paths, int npaths) 1154 | { 1155 | int i, count = 0; 1156 | for (i = 0; i < npaths; i++) { 1157 | count += paths[i].nfill; 1158 | count += paths[i].nstroke; 1159 | } 1160 | return count; 1161 | } 1162 | 1163 | static GLNVGcall* glnvg__allocCall(GLNVGcontext* gl) 1164 | { 1165 | GLNVGcall* ret = NULL; 1166 | if (gl->ncalls+1 > gl->ccalls) { 1167 | GLNVGcall* calls; 1168 | int ccalls = glnvg__maxi(gl->ncalls+1, 128) + gl->ccalls/2; // 1.5x Overallocate 1169 | calls = (GLNVGcall*)realloc(gl->calls, sizeof(GLNVGcall) * ccalls); 1170 | if (calls == NULL) return NULL; 1171 | gl->calls = calls; 1172 | gl->ccalls = ccalls; 1173 | } 1174 | ret = &gl->calls[gl->ncalls++]; 1175 | memset(ret, 0, sizeof(GLNVGcall)); 1176 | return ret; 1177 | } 1178 | 1179 | static int glnvg__allocPaths(GLNVGcontext* gl, int n) 1180 | { 1181 | int ret = 0; 1182 | if (gl->npaths+n > gl->cpaths) { 1183 | GLNVGpath* paths; 1184 | int cpaths = glnvg__maxi(gl->npaths + n, 128) + gl->cpaths/2; // 1.5x Overallocate 1185 | paths = (GLNVGpath*)realloc(gl->paths, sizeof(GLNVGpath) * cpaths); 1186 | if (paths == NULL) return -1; 1187 | gl->paths = paths; 1188 | gl->cpaths = cpaths; 1189 | } 1190 | ret = gl->npaths; 1191 | gl->npaths += n; 1192 | return ret; 1193 | } 1194 | 1195 | static int glnvg__allocVerts(GLNVGcontext* gl, int n) 1196 | { 1197 | int ret = 0; 1198 | if (gl->nverts+n > gl->cverts) { 1199 | NVGvertex* verts; 1200 | int cverts = glnvg__maxi(gl->nverts + n, 4096) + gl->cverts/2; // 1.5x Overallocate 1201 | verts = (NVGvertex*)realloc(gl->verts, sizeof(NVGvertex) * cverts); 1202 | if (verts == NULL) return -1; 1203 | gl->verts = verts; 1204 | gl->cverts = cverts; 1205 | } 1206 | ret = gl->nverts; 1207 | gl->nverts += n; 1208 | return ret; 1209 | } 1210 | 1211 | static int glnvg__allocFragUniforms(GLNVGcontext* gl, int n) 1212 | { 1213 | int ret = 0, structSize = gl->fragSize; 1214 | if (gl->nuniforms+n > gl->cuniforms) { 1215 | unsigned char* uniforms; 1216 | int cuniforms = glnvg__maxi(gl->nuniforms+n, 128) + gl->cuniforms/2; // 1.5x Overallocate 1217 | uniforms = (unsigned char*)realloc(gl->uniforms, structSize * cuniforms); 1218 | if (uniforms == NULL) return -1; 1219 | gl->uniforms = uniforms; 1220 | gl->cuniforms = cuniforms; 1221 | } 1222 | ret = gl->nuniforms * structSize; 1223 | gl->nuniforms += n; 1224 | return ret; 1225 | } 1226 | 1227 | static GLNVGfragUniforms* nvg__fragUniformPtr(GLNVGcontext* gl, int i) 1228 | { 1229 | return (GLNVGfragUniforms*)&gl->uniforms[i]; 1230 | } 1231 | 1232 | static void glnvg__vset(NVGvertex* vtx, float x, float y, float u, float v) 1233 | { 1234 | vtx->x = x; 1235 | vtx->y = y; 1236 | vtx->u = u; 1237 | vtx->v = v; 1238 | } 1239 | 1240 | static void glnvg__renderFill(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe, 1241 | const float* bounds, const NVGpath* paths, int npaths) 1242 | { 1243 | GLNVGcontext* gl = (GLNVGcontext*)uptr; 1244 | GLNVGcall* call = glnvg__allocCall(gl); 1245 | NVGvertex* quad; 1246 | GLNVGfragUniforms* frag; 1247 | int i, maxverts, offset; 1248 | 1249 | if (call == NULL) return; 1250 | 1251 | call->type = GLNVG_FILL; 1252 | call->pathOffset = glnvg__allocPaths(gl, npaths); 1253 | if (call->pathOffset == -1) goto error; 1254 | call->pathCount = npaths; 1255 | call->image = paint->image; 1256 | 1257 | if (npaths == 1 && paths[0].convex) 1258 | call->type = GLNVG_CONVEXFILL; 1259 | 1260 | // Allocate vertices for all the paths. 1261 | maxverts = glnvg__maxVertCount(paths, npaths) + 6; 1262 | offset = glnvg__allocVerts(gl, maxverts); 1263 | if (offset == -1) goto error; 1264 | 1265 | for (i = 0; i < npaths; i++) { 1266 | GLNVGpath* copy = &gl->paths[call->pathOffset + i]; 1267 | const NVGpath* path = &paths[i]; 1268 | memset(copy, 0, sizeof(GLNVGpath)); 1269 | if (path->nfill > 0) { 1270 | copy->fillOffset = offset; 1271 | copy->fillCount = path->nfill; 1272 | memcpy(&gl->verts[offset], path->fill, sizeof(NVGvertex) * path->nfill); 1273 | offset += path->nfill; 1274 | } 1275 | if (path->nstroke > 0) { 1276 | copy->strokeOffset = offset; 1277 | copy->strokeCount = path->nstroke; 1278 | memcpy(&gl->verts[offset], path->stroke, sizeof(NVGvertex) * path->nstroke); 1279 | offset += path->nstroke; 1280 | } 1281 | } 1282 | 1283 | // Quad 1284 | call->triangleOffset = offset; 1285 | call->triangleCount = 6; 1286 | quad = &gl->verts[call->triangleOffset]; 1287 | glnvg__vset(&quad[0], bounds[0], bounds[3], 0.5f, 1.0f); 1288 | glnvg__vset(&quad[1], bounds[2], bounds[3], 0.5f, 1.0f); 1289 | glnvg__vset(&quad[2], bounds[2], bounds[1], 0.5f, 1.0f); 1290 | 1291 | glnvg__vset(&quad[3], bounds[0], bounds[3], 0.5f, 1.0f); 1292 | glnvg__vset(&quad[4], bounds[2], bounds[1], 0.5f, 1.0f); 1293 | glnvg__vset(&quad[5], bounds[0], bounds[1], 0.5f, 1.0f); 1294 | 1295 | // Setup uniforms for draw calls 1296 | if (call->type == GLNVG_FILL) { 1297 | call->uniformOffset = glnvg__allocFragUniforms(gl, 2); 1298 | if (call->uniformOffset == -1) goto error; 1299 | // Simple shader for stencil 1300 | frag = nvg__fragUniformPtr(gl, call->uniformOffset); 1301 | memset(frag, 0, sizeof(*frag)); 1302 | frag->strokeThr = -1.0f; 1303 | frag->type = NSVG_SHADER_SIMPLE; 1304 | // Fill shader 1305 | glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset + gl->fragSize), paint, scissor, fringe, fringe, -1.0f); 1306 | } else { 1307 | call->uniformOffset = glnvg__allocFragUniforms(gl, 1); 1308 | if (call->uniformOffset == -1) goto error; 1309 | // Fill shader 1310 | glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset), paint, scissor, fringe, fringe, -1.0f); 1311 | } 1312 | 1313 | return; 1314 | 1315 | error: 1316 | // We get here if call alloc was ok, but something else is not. 1317 | // Roll back the last call to prevent drawing it. 1318 | if (gl->ncalls > 0) gl->ncalls--; 1319 | } 1320 | 1321 | static void glnvg__renderStroke(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe, 1322 | float strokeWidth, const NVGpath* paths, int npaths) 1323 | { 1324 | GLNVGcontext* gl = (GLNVGcontext*)uptr; 1325 | GLNVGcall* call = glnvg__allocCall(gl); 1326 | int i, maxverts, offset; 1327 | 1328 | if (call == NULL) return; 1329 | 1330 | call->type = GLNVG_STROKE; 1331 | call->pathOffset = glnvg__allocPaths(gl, npaths); 1332 | if (call->pathOffset == -1) goto error; 1333 | call->pathCount = npaths; 1334 | call->image = paint->image; 1335 | 1336 | // Allocate vertices for all the paths. 1337 | maxverts = glnvg__maxVertCount(paths, npaths); 1338 | offset = glnvg__allocVerts(gl, maxverts); 1339 | if (offset == -1) goto error; 1340 | 1341 | for (i = 0; i < npaths; i++) { 1342 | GLNVGpath* copy = &gl->paths[call->pathOffset + i]; 1343 | const NVGpath* path = &paths[i]; 1344 | memset(copy, 0, sizeof(GLNVGpath)); 1345 | if (path->nstroke) { 1346 | copy->strokeOffset = offset; 1347 | copy->strokeCount = path->nstroke; 1348 | memcpy(&gl->verts[offset], path->stroke, sizeof(NVGvertex) * path->nstroke); 1349 | offset += path->nstroke; 1350 | } 1351 | } 1352 | 1353 | if (gl->flags & NVG_STENCIL_STROKES) { 1354 | // Fill shader 1355 | call->uniformOffset = glnvg__allocFragUniforms(gl, 2); 1356 | if (call->uniformOffset == -1) goto error; 1357 | 1358 | glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset), paint, scissor, strokeWidth, fringe, -1.0f); 1359 | glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset + gl->fragSize), paint, scissor, strokeWidth, fringe, 1.0f - 0.5f/255.0f); 1360 | 1361 | } else { 1362 | // Fill shader 1363 | call->uniformOffset = glnvg__allocFragUniforms(gl, 1); 1364 | if (call->uniformOffset == -1) goto error; 1365 | glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset), paint, scissor, strokeWidth, fringe, -1.0f); 1366 | } 1367 | 1368 | return; 1369 | 1370 | error: 1371 | // We get here if call alloc was ok, but something else is not. 1372 | // Roll back the last call to prevent drawing it. 1373 | if (gl->ncalls > 0) gl->ncalls--; 1374 | } 1375 | 1376 | static void glnvg__renderTriangles(void* uptr, NVGpaint* paint, NVGscissor* scissor, 1377 | const NVGvertex* verts, int nverts) 1378 | { 1379 | GLNVGcontext* gl = (GLNVGcontext*)uptr; 1380 | GLNVGcall* call = glnvg__allocCall(gl); 1381 | GLNVGfragUniforms* frag; 1382 | 1383 | if (call == NULL) return; 1384 | 1385 | call->type = GLNVG_TRIANGLES; 1386 | call->image = paint->image; 1387 | 1388 | // Allocate vertices for all the paths. 1389 | call->triangleOffset = glnvg__allocVerts(gl, nverts); 1390 | if (call->triangleOffset == -1) goto error; 1391 | call->triangleCount = nverts; 1392 | 1393 | memcpy(&gl->verts[call->triangleOffset], verts, sizeof(NVGvertex) * nverts); 1394 | 1395 | // Fill shader 1396 | call->uniformOffset = glnvg__allocFragUniforms(gl, 1); 1397 | if (call->uniformOffset == -1) goto error; 1398 | frag = nvg__fragUniformPtr(gl, call->uniformOffset); 1399 | glnvg__convertPaint(gl, frag, paint, scissor, 1.0f, 1.0f, -1.0f); 1400 | frag->type = NSVG_SHADER_IMG; 1401 | 1402 | return; 1403 | 1404 | error: 1405 | // We get here if call alloc was ok, but something else is not. 1406 | // Roll back the last call to prevent drawing it. 1407 | if (gl->ncalls > 0) gl->ncalls--; 1408 | } 1409 | 1410 | static void glnvg__renderDelete(void* uptr) 1411 | { 1412 | GLNVGcontext* gl = (GLNVGcontext*)uptr; 1413 | int i; 1414 | if (gl == NULL) return; 1415 | 1416 | glnvg__deleteShader(&gl->shader); 1417 | 1418 | #if NANOVG_GL3 1419 | #if NANOVG_GL_USE_UNIFORMBUFFER 1420 | if (gl->fragBuf != 0) 1421 | glDeleteBuffers(1, &gl->fragBuf); 1422 | #endif 1423 | if (gl->vertArr != 0) 1424 | glDeleteVertexArrays(1, &gl->vertArr); 1425 | #endif 1426 | if (gl->vertBuf != 0) 1427 | glDeleteBuffers(1, &gl->vertBuf); 1428 | 1429 | for (i = 0; i < gl->ntextures; i++) { 1430 | if (gl->textures[i].tex != 0 && (gl->textures[i].flags & NVG_IMAGE_NODELETE) == 0) 1431 | glDeleteTextures(1, &gl->textures[i].tex); 1432 | } 1433 | free(gl->textures); 1434 | 1435 | free(gl->paths); 1436 | free(gl->verts); 1437 | free(gl->uniforms); 1438 | free(gl->calls); 1439 | 1440 | free(gl); 1441 | } 1442 | 1443 | 1444 | #if defined NANOVG_GL2 1445 | NVGcontext* nvgCreateGL2(int flags) 1446 | #elif defined NANOVG_GL3 1447 | NVGcontext* nvgCreateGL3(int flags) 1448 | #elif defined NANOVG_GLES2 1449 | NVGcontext* nvgCreateGLES2(int flags) 1450 | #elif defined NANOVG_GLES3 1451 | NVGcontext* nvgCreateGLES3(int flags) 1452 | #endif 1453 | { 1454 | NVGparams params; 1455 | NVGcontext* ctx = NULL; 1456 | GLNVGcontext* gl = (GLNVGcontext*)malloc(sizeof(GLNVGcontext)); 1457 | if (gl == NULL) goto error; 1458 | memset(gl, 0, sizeof(GLNVGcontext)); 1459 | 1460 | memset(¶ms, 0, sizeof(params)); 1461 | params.renderCreate = glnvg__renderCreate; 1462 | params.renderCreateTexture = glnvg__renderCreateTexture; 1463 | params.renderDeleteTexture = glnvg__renderDeleteTexture; 1464 | params.renderUpdateTexture = glnvg__renderUpdateTexture; 1465 | params.renderGetTextureSize = glnvg__renderGetTextureSize; 1466 | params.renderViewport = glnvg__renderViewport; 1467 | params.renderCancel = glnvg__renderCancel; 1468 | params.renderFlush = glnvg__renderFlush; 1469 | params.renderFill = glnvg__renderFill; 1470 | params.renderStroke = glnvg__renderStroke; 1471 | params.renderTriangles = glnvg__renderTriangles; 1472 | params.renderDelete = glnvg__renderDelete; 1473 | params.userPtr = gl; 1474 | params.edgeAntiAlias = flags & NVG_ANTIALIAS ? 1 : 0; 1475 | 1476 | gl->flags = flags; 1477 | 1478 | ctx = nvgCreateInternal(¶ms); 1479 | if (ctx == NULL) goto error; 1480 | 1481 | return ctx; 1482 | 1483 | error: 1484 | // 'gl' is freed by nvgDeleteInternal. 1485 | if (ctx != NULL) nvgDeleteInternal(ctx); 1486 | return NULL; 1487 | } 1488 | 1489 | #if defined NANOVG_GL2 1490 | void nvgDeleteGL2(NVGcontext* ctx) 1491 | #elif defined NANOVG_GL3 1492 | void nvgDeleteGL3(NVGcontext* ctx) 1493 | #elif defined NANOVG_GLES2 1494 | void nvgDeleteGLES2(NVGcontext* ctx) 1495 | #elif defined NANOVG_GLES3 1496 | void nvgDeleteGLES3(NVGcontext* ctx) 1497 | #endif 1498 | { 1499 | nvgDeleteInternal(ctx); 1500 | } 1501 | 1502 | int nvglCreateImageFromHandle(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags) 1503 | { 1504 | GLNVGcontext* gl = (GLNVGcontext*)nvgInternalParams(ctx)->userPtr; 1505 | GLNVGtexture* tex = glnvg__allocTexture(gl); 1506 | 1507 | if (tex == NULL) return 0; 1508 | 1509 | tex->type = NVG_TEXTURE_RGBA; 1510 | tex->tex = textureId; 1511 | tex->flags = imageFlags; 1512 | tex->width = w; 1513 | tex->height = h; 1514 | 1515 | return tex->id; 1516 | } 1517 | 1518 | GLuint nvglImageHandle(NVGcontext* ctx, int image) 1519 | { 1520 | GLNVGcontext* gl = (GLNVGcontext*)nvgInternalParams(ctx)->userPtr; 1521 | GLNVGtexture* tex = glnvg__findTexture(gl, image); 1522 | return tex->tex; 1523 | } 1524 | 1525 | #endif /* NANOVG_GL_IMPLEMENTATION */ 1526 | -------------------------------------------------------------------------------- /libs/nanovg/src/nanovg_gl_utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2009-2013 Mikko Mononen memon@inside.org 3 | // 4 | // This software is provided 'as-is', without any express or implied 5 | // warranty. In no event will the authors be held liable for any damages 6 | // arising from the use of this software. 7 | // Permission is granted to anyone to use this software for any purpose, 8 | // including commercial applications, and to alter it and redistribute it 9 | // freely, subject to the following restrictions: 10 | // 1. The origin of this software must not be misrepresented; you must not 11 | // claim that you wrote the original software. If you use this software 12 | // in a product, an acknowledgment in the product documentation would be 13 | // appreciated but is not required. 14 | // 2. Altered source versions must be plainly marked as such, and must not be 15 | // misrepresented as being the original software. 16 | // 3. This notice may not be removed or altered from any source distribution. 17 | // 18 | #ifndef NANOVG_GL_UTILS_H 19 | #define NANOVG_GL_UTILS_H 20 | 21 | struct NVGLUframebuffer { 22 | NVGcontext* ctx; 23 | GLuint fbo; 24 | GLuint rbo; 25 | GLuint texture; 26 | int image; 27 | }; 28 | typedef struct NVGLUframebuffer NVGLUframebuffer; 29 | 30 | // Helper function to create GL frame buffer to render to. 31 | void nvgluBindFramebuffer(NVGLUframebuffer* fb); 32 | NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imageFlags); 33 | void nvgluDeleteFramebuffer(NVGcontext* ctx, NVGLUframebuffer* fb); 34 | 35 | #endif // NANOVG_GL_UTILS_H 36 | 37 | #ifdef NANOVG_GL_IMPLEMENTATION 38 | 39 | #if defined(NANOVG_GL3) || defined(NANOVG_GLES2) || defined(NANOVG_GLES3) 40 | // FBO is core in OpenGL 3>. 41 | # define NANOVG_FBO_VALID 1 42 | #elif defined(NANOVG_GL2) 43 | // On OS X including glext defines FBO on GL2 too. 44 | # ifdef __APPLE__ 45 | # include 46 | # define NANOVG_FBO_VALID 1 47 | # endif 48 | #endif 49 | 50 | static GLint defaultFBO = -1; 51 | 52 | NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imageFlags) 53 | { 54 | #ifdef NANOVG_FBO_VALID 55 | GLint defaultFBO; 56 | GLint defaultRBO; 57 | NVGLUframebuffer* fb = NULL; 58 | 59 | glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFBO); 60 | glGetIntegerv(GL_RENDERBUFFER_BINDING, &defaultRBO); 61 | 62 | fb = (NVGLUframebuffer*)malloc(sizeof(NVGLUframebuffer)); 63 | if (fb == NULL) goto error; 64 | memset(fb, 0, sizeof(NVGLUframebuffer)); 65 | 66 | fb->image = nvgCreateImageRGBA(ctx, w, h, imageFlags | NVG_IMAGE_FLIPY | NVG_IMAGE_PREMULTIPLIED, NULL); 67 | fb->texture = nvglImageHandle(ctx, fb->image); 68 | 69 | // frame buffer object 70 | glGenFramebuffers(1, &fb->fbo); 71 | glBindFramebuffer(GL_FRAMEBUFFER, fb->fbo); 72 | 73 | // render buffer object 74 | glGenRenderbuffers(1, &fb->rbo); 75 | glBindRenderbuffer(GL_RENDERBUFFER, fb->rbo); 76 | glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, w, h); 77 | 78 | // combine all 79 | glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb->texture, 0); 80 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb->rbo); 81 | 82 | if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) goto error; 83 | 84 | glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); 85 | glBindRenderbuffer(GL_RENDERBUFFER, defaultRBO); 86 | return fb; 87 | error: 88 | glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); 89 | glBindRenderbuffer(GL_RENDERBUFFER, defaultRBO); 90 | nvgluDeleteFramebuffer(ctx, fb); 91 | return NULL; 92 | #else 93 | NVG_NOTUSED(ctx); 94 | NVG_NOTUSED(w); 95 | NVG_NOTUSED(h); 96 | NVG_NOTUSED(imageFlags); 97 | return NULL; 98 | #endif 99 | } 100 | 101 | void nvgluBindFramebuffer(NVGLUframebuffer* fb) 102 | { 103 | #ifdef NANOVG_FBO_VALID 104 | if (defaultFBO == -1) glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFBO); 105 | glBindFramebuffer(GL_FRAMEBUFFER, fb != NULL ? fb->fbo : defaultFBO); 106 | #else 107 | NVG_NOTUSED(fb); 108 | #endif 109 | } 110 | 111 | void nvgluDeleteFramebuffer(NVGcontext* ctx, NVGLUframebuffer* fb) 112 | { 113 | #ifdef NANOVG_FBO_VALID 114 | if (fb == NULL) return; 115 | if (fb->fbo != 0) 116 | glDeleteFramebuffers(1, &fb->fbo); 117 | if (fb->rbo != 0) 118 | glDeleteRenderbuffers(1, &fb->rbo); 119 | if (fb->image >= 0) 120 | nvgDeleteImage(ctx, fb->image); 121 | fb->fbo = 0; 122 | fb->rbo = 0; 123 | fb->texture = 0; 124 | fb->image = -1; 125 | free(fb); 126 | #else 127 | NVG_NOTUSED(ctx); 128 | NVG_NOTUSED(fb); 129 | #endif 130 | } 131 | 132 | #endif // NANOVG_GL_IMPLEMENTATION 133 | -------------------------------------------------------------------------------- /src/ofxNanoVG.cpp: -------------------------------------------------------------------------------- 1 | #include "ofxNanoVG.h" 2 | 3 | #define NANOVG_GL2_IMPLEMENTATION 4 | #include "nanovg_gl.h" 5 | 6 | OFX_NANOVG_BEGIN_NAMESPACE 7 | 8 | #pragma mark - FrameBuffer 9 | 10 | class FrameBuffer 11 | { 12 | public: 13 | 14 | FrameBuffer(int w, int h) 15 | : width(w) 16 | , height(h) 17 | , framebuffer(0) 18 | , color(0) 19 | , stencil(0) 20 | { 21 | { 22 | glGenTextures(1, &color); 23 | 24 | glEnable(GL_TEXTURE_RECTANGLE); 25 | 26 | glBindTexture(GL_TEXTURE_RECTANGLE, color); 27 | glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MAG_FILTER, 28 | GL_NEAREST); 29 | glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MIN_FILTER, 30 | GL_NEAREST); 31 | glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_S, 32 | GL_CLAMP_TO_EDGE); 33 | glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_T, 34 | GL_CLAMP_TO_EDGE); 35 | glTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_RGBA, w, h, 0, GL_RGBA, 36 | GL_UNSIGNED_BYTE, 0); 37 | glBindTexture(GL_TEXTURE_RECTANGLE, 0); 38 | 39 | glDisable(GL_TEXTURE_RECTANGLE); 40 | 41 | checkError(); 42 | } 43 | 44 | { 45 | glGenRenderbuffers(1, &stencil); 46 | glBindRenderbuffer(GL_RENDERBUFFER, stencil); 47 | glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX, w, h); 48 | glBindRenderbuffer(GL_RENDERBUFFER, 0); 49 | 50 | checkError(); 51 | } 52 | 53 | { 54 | glGenFramebuffers(1, &framebuffer); 55 | glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); 56 | { 57 | glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 58 | GL_TEXTURE_RECTANGLE, color, 0); 59 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, 60 | GL_RENDERBUFFER, stencil); 61 | } 62 | glBindFramebuffer(GL_FRAMEBUFFER, 0); 63 | 64 | checkError(); 65 | } 66 | } 67 | 68 | ~FrameBuffer() 69 | { 70 | if (framebuffer) 71 | { 72 | glDeleteFramebuffers(1, &framebuffer); 73 | framebuffer = 0; 74 | } 75 | 76 | if (color) 77 | { 78 | glDeleteTextures(1, &color); 79 | color = 0; 80 | } 81 | 82 | if (stencil) 83 | { 84 | glDeleteRenderbuffers(1, &stencil); 85 | stencil = 0; 86 | } 87 | } 88 | 89 | void clear(float r, float g, float b, float a = 1) 90 | { 91 | glClearColor(r, g, b, a); 92 | glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); 93 | } 94 | 95 | void bind() 96 | { 97 | glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); 98 | } 99 | 100 | void unbind() { 101 | glBindFramebuffer(GL_FRAMEBUFFER, 0); 102 | } 103 | 104 | void draw(float x, float y, float w, float h) 105 | { 106 | glPushMatrix(); 107 | { 108 | glTranslatef(x, y, 0); 109 | 110 | bool e = glIsEnabled(GL_TEXTURE_RECTANGLE); 111 | glEnable(GL_TEXTURE_RECTANGLE); 112 | 113 | glBindTexture(GL_TEXTURE_RECTANGLE, color); 114 | glBegin(GL_QUADS); 115 | glTexCoord2f(0, height); 116 | glVertex2f(0, 0); 117 | 118 | glTexCoord2f(width, height); 119 | glVertex2f(w, 0); 120 | 121 | glTexCoord2f(width, 0); 122 | glVertex2f(w, h); 123 | 124 | glTexCoord2f(0, 0); 125 | glVertex2f(0, h); 126 | glEnd(); 127 | glBindTexture(GL_TEXTURE_RECTANGLE, 0); 128 | 129 | if (!e) glDisable(GL_TEXTURE_RECTANGLE); 130 | } 131 | glPopMatrix(); 132 | } 133 | 134 | public: 135 | 136 | float getWidth() const { return width; } 137 | float getHeight() const { return height; } 138 | 139 | GLuint getFrameBufferID() const { return framebuffer; } 140 | GLuint getColorID() const { return color; } 141 | GLuint getStencilID() const { return stencil; } 142 | 143 | private: 144 | GLuint framebuffer; 145 | GLuint color; 146 | GLuint stencil; 147 | 148 | int width, height; 149 | 150 | void checkError() 151 | { 152 | GLenum err = glGetError(); 153 | if (err != GL_NO_ERROR) 154 | { 155 | cout << glewGetErrorString(err) << endl; 156 | throw; 157 | } 158 | } 159 | }; 160 | 161 | #pragma mark - Paint 162 | 163 | void LinearGradient::fill(Canvas& canvas) const 164 | { 165 | NVGcontext* c = canvas.getContext(); 166 | NVGpaint o = nvgLinearGradient(c, from.position.x, from.position.y, to.position.x, to.position.y, *(NVGcolor*)&from.color.r, *(NVGcolor*)&to.color.r); 167 | nvgFillPaint(c, o); 168 | } 169 | 170 | void LinearGradient::stroke(Canvas& canvas) const 171 | { 172 | NVGcontext* c = canvas.getContext(); 173 | NVGpaint o = nvgLinearGradient(c, from.position.x, from.position.y, to.position.x, to.position.y, *(NVGcolor*)&from.color.r, *(NVGcolor*)&to.color.r); 174 | nvgStrokePaint(c, o); 175 | } 176 | 177 | void BoxGradient::fill(Canvas& canvas) const 178 | { 179 | NVGcontext* c = canvas.getContext(); 180 | NVGpaint o = nvgBoxGradient(c, rect.x, rect.y, rect.width, rect.height, corner_radius, feather, *(NVGcolor*)&inner.r, *(NVGcolor*)&outer.r); 181 | nvgFillPaint(c, o); 182 | } 183 | 184 | void BoxGradient::stroke(Canvas& canvas) const 185 | { 186 | NVGcontext* c = canvas.getContext(); 187 | NVGpaint o = nvgBoxGradient(c, rect.x, rect.y, rect.width, rect.height, corner_radius, feather, *(NVGcolor*)&inner.r, *(NVGcolor*)&outer.r); 188 | nvgStrokePaint(c, o); 189 | } 190 | 191 | void RadialGradient::fill(Canvas& canvas) const 192 | { 193 | NVGcontext* c = canvas.getContext(); 194 | NVGpaint o = nvgRadialGradient(c, center.x, center.y, inner.radius, outer.radius, *(NVGcolor*)&inner.color.r, *(NVGcolor*)&outer.color.r); 195 | nvgFillPaint(c, o); 196 | } 197 | 198 | void RadialGradient::stroke(Canvas& canvas) const 199 | { 200 | NVGcontext* c = canvas.getContext(); 201 | NVGpaint o = nvgRadialGradient(c, center.x, center.y, inner.radius, outer.radius, *(NVGcolor*)&inner.color.r, *(NVGcolor*)&outer.color.r); 202 | nvgStrokePaint(c, o); 203 | } 204 | 205 | Image::~Image() 206 | {} 207 | 208 | void Image::upload(NVGcontext* ctx) const 209 | { 210 | if (image) nvgDeleteImage(ctx, image); 211 | if (tex->getTextureData().textureTarget != GL_TEXTURE_2D) 212 | ofLogError("ofxNanoVG::Image") << "texture target should be GL_TEXTURE_2D"; 213 | 214 | image = nvglCreateImageFromHandle(ctx, tex->getTextureData().textureID, tex->getWidth(), tex->getHeight(), flags); 215 | } 216 | 217 | void Image::fill(Canvas& canvas) const 218 | { 219 | NVGcontext* c = canvas.getContext(); 220 | upload(c); 221 | 222 | NVGpaint o = nvgImagePattern(c, rect.x, rect.y, rect.width, rect.height, angle, image, alpha); 223 | nvgFillPaint(c, o); 224 | } 225 | 226 | void Image::stroke(Canvas& canvas) const 227 | { 228 | NVGcontext* c = canvas.getContext(); 229 | upload(c); 230 | 231 | NVGpaint o = nvgImagePattern(c, rect.x, rect.y, rect.width, rect.height, angle, image, alpha); 232 | nvgStrokePaint(c, o); 233 | } 234 | 235 | #pragma mark - Canvas 236 | 237 | void Canvas::allocate(int width, int height) 238 | { 239 | this->width = width; 240 | this->height = height; 241 | 242 | release(); 243 | 244 | vg = nvgCreateGL2(NVG_ANTIALIAS | NVG_STENCIL_STROKES); 245 | 246 | FrameBuffer *o = new FrameBuffer(width, height); 247 | framebuffer = shared_ptr(o); 248 | 249 | background_color.set(0, 0); 250 | 251 | framebuffer->bind(); 252 | framebuffer->clear(background_color.r, background_color.g, background_color.b, background_color.a); 253 | framebuffer->unbind(); 254 | } 255 | 256 | void Canvas::release() 257 | { 258 | if (vg) 259 | { 260 | nvgDeleteGL2(vg); 261 | vg = NULL; 262 | } 263 | 264 | framebuffer.reset(); 265 | } 266 | 267 | void Canvas::begin() 268 | { 269 | glPushAttrib(GL_ALL_ATTRIB_BITS); 270 | ofPushView(); 271 | ofViewport(0, ofGetViewportHeight() - height, width, height); 272 | 273 | glDisable(GL_DEPTH_TEST); 274 | glDisable(GL_LIGHTING); 275 | 276 | framebuffer->bind(); 277 | framebuffer->clear(background_color.r, background_color.g, background_color.b, background_color.a); 278 | 279 | nvgBeginFrame(vg, width, height, 1); 280 | 281 | resetState(); 282 | 283 | textLineHeight(1); 284 | fillColor(ofColor(127)); 285 | strokeColor(ofColor(127)); 286 | } 287 | 288 | void Canvas::end() 289 | { 290 | nvgEndFrame(vg); 291 | 292 | // {{{ quick fix for nanovg vbo unbind bug 293 | glBindBuffer(GL_ARRAY_BUFFER, 0); 294 | // }}} 295 | 296 | framebuffer->unbind(); 297 | 298 | ofPopView(); 299 | glPopAttrib(); 300 | } 301 | 302 | void Canvas::draw(float x, float y, float w, float h) 303 | { 304 | if (w == 0) w = width; 305 | if (h == 0) h = height; 306 | 307 | framebuffer->draw(x, y, w, h); 308 | } 309 | 310 | void Canvas::fillColor(const ofFloatColor& c) 311 | { 312 | nvgFillColor(vg, nvgRGBAf(c.r, c.g, c.b, c.a)); 313 | } 314 | 315 | void Canvas::strokeColor(const ofFloatColor& c) 316 | { 317 | nvgStrokeColor(vg, nvgRGBAf(c.r, c.g, c.b, c.a)); 318 | } 319 | 320 | void Canvas::lineWidth(float w) 321 | { 322 | nvgStrokeWidth(vg, w); 323 | } 324 | 325 | void Canvas::lineCap(LineCap::Type type) 326 | { 327 | nvgLineCap(vg, type); 328 | } 329 | 330 | void Canvas::lineJoin(LineCap::Type type) 331 | { 332 | nvgLineJoin(vg, type); 333 | } 334 | 335 | // 336 | 337 | void Canvas::resetState() 338 | { 339 | nvgReset(vg); 340 | } 341 | 342 | void Canvas::push() 343 | { 344 | nvgSave(vg); 345 | } 346 | 347 | void Canvas::pop() 348 | { 349 | nvgRestore(vg); 350 | } 351 | 352 | void Canvas::identity() 353 | { 354 | nvgResetTransform(vg); 355 | } 356 | 357 | // 358 | 359 | void Canvas::beginPath() 360 | { 361 | nvgBeginPath(vg); 362 | } 363 | 364 | void Canvas::closePath() 365 | { 366 | nvgClosePath(vg); 367 | } 368 | 369 | void Canvas::fillPath() 370 | { 371 | nvgFill(vg); 372 | } 373 | 374 | void Canvas::fillPath(const PaintStyle& paint) 375 | { 376 | paint.fill(*this); 377 | nvgFill(vg); 378 | } 379 | 380 | void Canvas::strokePath() 381 | { 382 | nvgStroke(vg); 383 | } 384 | 385 | void Canvas::strokePath(const PaintStyle& paint) 386 | { 387 | paint.stroke(*this); 388 | nvgStroke(vg); 389 | } 390 | 391 | void Canvas::moveTo(float x, float y) 392 | { 393 | nvgMoveTo(vg, x, y); 394 | } 395 | 396 | void Canvas::lineTo(float x, float y) 397 | { 398 | nvgLineTo(vg, x, y); 399 | } 400 | 401 | void Canvas::bezierTo(float c1x, float c1y, float c2x, float c2y, float x, float y) 402 | { 403 | nvgBezierTo(vg, c1x, c1y, c2x, c2y, x, y); 404 | } 405 | 406 | void Canvas::arcTo(float x1, float y1, float x2, float y2, float radius) 407 | { 408 | nvgArcTo(vg, x1, y1, x2, y2, radius); 409 | } 410 | 411 | // 412 | 413 | void Canvas::arc(float cx, float cy, float r, float a0, float a1, int dir) 414 | { 415 | nvgArc(vg, cx, cy, r, a0, a1, dir); 416 | } 417 | 418 | void Canvas::rect(float x, float y, float w, float h) 419 | { 420 | nvgRect(vg, x, y, w, h); 421 | } 422 | 423 | void Canvas::rect(const ofRectangle& rect) 424 | { 425 | nvgRect(vg, rect.x, rect.y, rect.width, rect.height); 426 | } 427 | 428 | void Canvas::roundedRect(float x, float y, float w, float h, float r) 429 | { 430 | nvgRoundedRect(vg, x, y, w, h, r); 431 | } 432 | 433 | void Canvas::roundedRect(const ofRectangle& rect, float r) 434 | { 435 | nvgRoundedRect(vg, rect.x, rect.y, rect.width, rect.height, r); 436 | } 437 | 438 | void Canvas::circle(float cx, float cy, float r) 439 | { 440 | nvgEllipse(vg, cx, cy, r, r); 441 | } 442 | 443 | void Canvas::circle(const ofPoint& p, float r) 444 | { 445 | nvgEllipse(vg, p.x, p.y, r, r); 446 | } 447 | 448 | void Canvas::ellipse(float cx, float cy, float rx, float ry) 449 | { 450 | nvgEllipse(vg, cx, cy, rx, ry); 451 | } 452 | 453 | void Canvas::ellipse(const ofPoint& p, float rx, float ry) 454 | { 455 | nvgEllipse(vg, p.x, p.y, rx, ry); 456 | } 457 | 458 | // 459 | 460 | void Canvas::translate(float x, float y) 461 | { 462 | nvgTranslate(vg, x, y); 463 | } 464 | 465 | void Canvas::rotate(float angle) 466 | { 467 | nvgRotate(vg, ofDegToRad(angle)); 468 | } 469 | 470 | void Canvas::scale(float x, float y) 471 | { 472 | nvgScale(vg, x, y); 473 | } 474 | 475 | // 476 | 477 | bool Canvas::loadFont(const string& path, const string& name) 478 | { 479 | int n = nvgCreateFont(vg, name.c_str(), ofToDataPath(path).c_str()); 480 | if (n == -1) 481 | { 482 | ofLogError("Canvas") << "font not found: " << path; 483 | } 484 | return n != -1; 485 | } 486 | 487 | bool Canvas::fontStyle(const FontStyle& font_style) 488 | { 489 | if (!textFont(font_style.getName())) return false; 490 | textSize(font_style.getSize()); 491 | textBlur(font_style.getBlur()); 492 | textLetterSpaceing(font_style.getLetterSpaceing()); 493 | textLineHeight(font_style.getLineHeight()); 494 | textAlign(font_style.getAlign()); 495 | return true; 496 | } 497 | 498 | bool Canvas::textFont(const string& name) 499 | { 500 | if (nvgFindFont(vg, name.c_str()) == -1) 501 | { 502 | ofLogError("Canvas") << "font not loaded: " << name; 503 | return false; 504 | } 505 | nvgFontFace(vg, name.c_str()); 506 | return true; 507 | } 508 | 509 | void Canvas::text(const string& text, float x, float y, float line_break_width) 510 | { 511 | if (line_break_width == 0) 512 | { 513 | nvgText(vg, x, y, text.c_str(), text.c_str() + text.size()); 514 | } 515 | else 516 | { 517 | nvgTextBox(vg, x, y, line_break_width, text.c_str(), text.c_str() + text.size()); 518 | } 519 | } 520 | 521 | ofRectangle Canvas::textBounds(const string& text, float x, float y, float line_break_width) 522 | { 523 | float r[4]; 524 | if (line_break_width == 0) 525 | { 526 | nvgTextBounds(vg, x, y, text.c_str(), text.c_str() + text.size(), r); 527 | } 528 | else 529 | { 530 | nvgTextBoxBounds(vg, x, y, line_break_width, text.c_str(), text.c_str() + text.size(), r); 531 | } 532 | return ofRectangle(r[0], r[1], r[2] - r[0], r[3] - r[1]); 533 | } 534 | 535 | void Canvas::textSize(float size) 536 | { 537 | nvgFontSize(vg, size); 538 | } 539 | 540 | void Canvas::textBlur(float blur) 541 | { 542 | nvgFontBlur(vg, blur); 543 | } 544 | 545 | void Canvas::textLetterSpaceing(float letter_spaceing) 546 | { 547 | nvgTextLetterSpacing(vg, letter_spaceing); 548 | } 549 | 550 | void Canvas::textLineHeight(float line_height) 551 | { 552 | nvgTextLineHeight(vg, line_height); 553 | } 554 | 555 | void Canvas::textAlign(int align) 556 | { 557 | nvgTextAlign(vg, align); 558 | } 559 | 560 | 561 | 562 | 563 | 564 | OFX_NANOVG_END_NAMESPACE -------------------------------------------------------------------------------- /src/ofxNanoVG.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ofMain.h" 4 | 5 | #include "nanovg.h" 6 | 7 | #define OFX_NANOVG_BEGIN_NAMESPACE namespace ofx { namespace NanoVG { 8 | #define OFX_NANOVG_END_NAMESPACE } } 9 | 10 | OFX_NANOVG_BEGIN_NAMESPACE 11 | 12 | class FrameBuffer; 13 | class Canvas; 14 | 15 | struct TextAlign { 16 | enum { 17 | LEFT = NVG_ALIGN_LEFT, 18 | CENTER = NVG_ALIGN_CENTER, 19 | RIGHT = NVG_ALIGN_RIGHT, 20 | TOP = NVG_ALIGN_TOP, 21 | MIDDLE = NVG_ALIGN_MIDDLE, 22 | BOTTOM = NVG_ALIGN_BOTTOM, 23 | BASELINE = NVG_ALIGN_BASELINE 24 | }; 25 | }; 26 | 27 | struct LineCap 28 | { 29 | enum Type { 30 | BUTT = NVG_BUTT, 31 | ROUND = NVG_ROUND, 32 | SQUARE = NVG_SQUARE, 33 | BEVEL = NVG_BEVEL, 34 | MITER = NVG_MITER 35 | }; 36 | }; 37 | 38 | class FontStyle 39 | { 40 | string name; 41 | float size; 42 | float blur; 43 | float letter_spaceing; 44 | float line_height; 45 | int align; 46 | 47 | public: 48 | 49 | FontStyle(const string& name = "", 50 | float size = 14, 51 | float blur = 0, 52 | float letter_spaceing = 0, 53 | float line_height = 0, 54 | int align = TextAlign::TOP | TextAlign::LEFT) 55 | : name(name) 56 | , size(size) 57 | , blur(blur) 58 | , letter_spaceing(letter_spaceing) 59 | , line_height(line_height) 60 | , align(align) {} 61 | 62 | void setFace(const string& name) { this->name = name; } 63 | const string& getName() const { return name; } 64 | 65 | void setSize(float size) { this->size = size; } 66 | float getSize() const { return size; } 67 | 68 | void setBlur(float blur) { this->blur = blur; } 69 | float getBlur() const { return blur; } 70 | 71 | void setLetterSpaceing(float letter_spaceing) { this->letter_spaceing = letter_spaceing; } 72 | float getLetterSpaceing() const { return letter_spaceing; } 73 | 74 | void setLineHeight(float line_height) { this->line_height = line_height; } 75 | float getLineHeight() const { return line_height; } 76 | 77 | void setAligh(int align) { this->align = align; } 78 | int getAlign() const { return align; } 79 | }; 80 | 81 | struct PaintStyle { 82 | virtual void fill(Canvas& canvas) const = 0; 83 | virtual void stroke(Canvas& canvas) const = 0; 84 | }; 85 | 86 | class LinearGradient : public PaintStyle 87 | { 88 | public: 89 | 90 | struct ControlPoint { 91 | ofVec2f position; 92 | ofFloatColor color; 93 | }; 94 | 95 | ControlPoint from, to; 96 | 97 | LinearGradient() {} 98 | 99 | LinearGradient(const ofVec2f& from_pos, const ofFloatColor& from_color, 100 | const ofVec2f& to_pos, const ofFloatColor& to_color) 101 | { 102 | from.position = from_pos; 103 | from.color = from_color; 104 | to.position = to_pos; 105 | to.color = to_color; 106 | } 107 | 108 | void fill(Canvas& canvas) const; 109 | void stroke(Canvas& canvas) const; 110 | }; 111 | 112 | class BoxGradient : public PaintStyle 113 | { 114 | public: 115 | 116 | ofRectangle rect; 117 | float corner_radius, feather; 118 | ofFloatColor inner, outer; 119 | 120 | BoxGradient() {} 121 | 122 | BoxGradient(const ofRectangle& rect, float corner_radius, float feather, 123 | const ofFloatColor& inner, const ofFloatColor& outer) 124 | : rect(rect) 125 | , corner_radius(corner_radius) 126 | , feather(feather) 127 | , inner(inner) 128 | , outer(outer) 129 | {} 130 | 131 | void fill(Canvas& canvas) const; 132 | void stroke(Canvas& canvas) const; 133 | }; 134 | 135 | class RadialGradient : public PaintStyle 136 | { 137 | public: 138 | 139 | ofVec2f center; 140 | 141 | struct ControlPoint { 142 | float radius; 143 | ofFloatColor color; 144 | }; 145 | 146 | ControlPoint inner, outer; 147 | 148 | RadialGradient() {} 149 | 150 | RadialGradient(const ofVec2f& center, 151 | float inner_radius, const ofFloatColor& inner_color, 152 | float outer_radius, const ofFloatColor& outer_color) 153 | { 154 | this->center = center; 155 | inner.radius = inner_radius; 156 | inner.color = inner_color; 157 | outer.radius = outer_radius; 158 | outer.color = outer_color; 159 | } 160 | 161 | void fill(Canvas& canvas) const; 162 | void stroke(Canvas& canvas) const; 163 | }; 164 | 165 | class Image : public PaintStyle 166 | { 167 | public: 168 | 169 | ofRectangle rect; 170 | float angle; 171 | float alpha; 172 | 173 | Image() {} 174 | ~Image(); 175 | 176 | Image(ofTexture* tex, const ofRectangle& rect, float angle = 0, float alpha = 1, int flags = 0) 177 | : tex(tex) 178 | , rect(rect) 179 | , angle(angle) 180 | , alpha(alpha) 181 | , image(0) 182 | , flags(flags) 183 | {} 184 | 185 | void fill(Canvas& canvas) const; 186 | void stroke(Canvas& canvas) const; 187 | 188 | protected: 189 | 190 | mutable int image; 191 | int flags; 192 | ofTexture* tex; 193 | 194 | void upload(NVGcontext* ctx) const; 195 | }; 196 | 197 | class Canvas 198 | { 199 | public: 200 | 201 | Canvas() : vg(NULL) {} 202 | 203 | void allocate(int width, int height); 204 | 205 | void resetState(); 206 | 207 | void begin(); 208 | void end(); 209 | 210 | public: 211 | 212 | // draw commands 213 | 214 | void background(float r, float g, float b, float a = 1) { background_color.set(r, g, b, a); } 215 | 216 | void fillColor(const ofFloatColor& c); 217 | void strokeColor(const ofFloatColor& c); 218 | 219 | void lineWidth(float w); 220 | void lineCap(LineCap::Type type); 221 | void lineJoin(LineCap::Type type); 222 | 223 | // path 224 | 225 | void beginPath(); 226 | void closePath(); 227 | void fillPath(); 228 | void fillPath(const PaintStyle& paint); 229 | void strokePath(); 230 | void strokePath(const PaintStyle& paint); 231 | 232 | void arc(float cx, float cy, float r, float a0, float a1, int dir); 233 | 234 | void rect(float x, float y, float w, float h); 235 | void rect(const ofRectangle& rect); 236 | 237 | void roundedRect(float x, float y, float w, float h, float r); 238 | void roundedRect(const ofRectangle& rect, float r); 239 | 240 | void circle(float cx, float cy, float r); 241 | void circle(const ofPoint& p, float r); 242 | 243 | void ellipse(float cx, float cy, float rx, float ry); 244 | void ellipse(const ofPoint& p, float rx, float ry); 245 | 246 | void moveTo(float x, float y); 247 | void lineTo(float x, float y); 248 | void bezierTo(float c1x, float c1y, float c2x, float c2y, float x, float y); 249 | void arcTo(float x1, float y1, float x2, float y2, float radius); 250 | 251 | // text 252 | 253 | bool loadFont(const string& path, const string& name); 254 | bool textFont(const string& name); 255 | 256 | void text(const string& text, float x, float y, float line_break_width = 0); 257 | ofRectangle textBounds(const string& text, float x, float y, float line_break_width = 0); 258 | 259 | void textSize(float size); 260 | void textBlur(float blur); 261 | void textLetterSpaceing(float letter_spaceing); 262 | void textLineHeight(float line_height); 263 | void textAlign(int align); 264 | 265 | bool fontStyle(const FontStyle& font_style); 266 | 267 | // transform 268 | 269 | void push(); 270 | void pop(); 271 | 272 | void identity(); 273 | 274 | void translate(float x, float y); 275 | void rotate(float angle); 276 | void scale(float x, float y); 277 | 278 | public: 279 | 280 | float getWidth() const { return width; } 281 | float getHeight() const { return height; } 282 | 283 | void draw(float x, float y, float w = 0, float h = 0); 284 | 285 | public: 286 | 287 | NVGcontext* getContext() const { return vg; } 288 | 289 | private: 290 | 291 | struct NVGcontext* vg; 292 | 293 | float width, height; 294 | shared_ptr framebuffer; 295 | 296 | ofFloatColor background_color; 297 | 298 | void release(); 299 | }; 300 | 301 | OFX_NANOVG_END_NAMESPACE 302 | 303 | namespace ofxNanoVG = ofx::NanoVG; --------------------------------------------------------------------------------