summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--CMakeLists.txt141
-rw-r--r--Embed.cmake66
-rw-r--r--res/FiraMono-Regular.ttfbin0 -> 132388 bytes
-rw-r--r--res/FiraSans-LightItalic.ttfbin0 -> 197112 bytes
-rw-r--r--res/FiraSans-Regular.ttfbin0 -> 194812 bytes
-rw-r--r--res/MacOSXBundleInfo.plist.in55
-rw-r--r--res/lagrange.rc.in32
-rw-r--r--src/app.c300
-rw-r--r--src/app.h29
-rw-r--r--src/main.c25
-rw-r--r--src/stb_image.h7656
-rw-r--r--src/stb_truetype.h5011
-rw-r--r--src/ui/color.c27
-rw-r--r--src/ui/color.h48
-rw-r--r--src/ui/command.c89
-rw-r--r--src/ui/command.h16
-rw-r--r--src/ui/inputwidget.c280
-rw-r--r--src/ui/inputwidget.h20
-rw-r--r--src/ui/labelwidget.c281
-rw-r--r--src/ui/labelwidget.h22
-rw-r--r--src/ui/macos.h9
-rw-r--r--src/ui/macos.m434
-rw-r--r--src/ui/metrics.c16
-rw-r--r--src/ui/metrics.h9
-rw-r--r--src/ui/paint.c58
-rw-r--r--src/ui/paint.h33
-rw-r--r--src/ui/text.c533
-rw-r--r--src/ui/text.h52
-rw-r--r--src/ui/util.c604
-rw-r--r--src/ui/util.h110
-rw-r--r--src/ui/widget.c618
-rw-r--r--src/ui/widget.h131
-rw-r--r--src/ui/window.c441
-rw-r--r--src/ui/window.h34
-rw-r--r--src/win32.c18
-rw-r--r--src/win32.h4
37 files changed, 17204 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..59577bd5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
1.DS_Store
2*.user
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 00000000..ec444865
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,141 @@
1cmake_minimum_required (VERSION 3.9)
2
3project (Lagrange
4 VERSION 1.0.0
5 DESCRIPTION "Beautiful Gemini Client"
6 LANGUAGES C
7)
8set (COPYRIGHT_YEAR 2020)
9
10include (Embed.cmake)
11find_package (the_Foundation REQUIRED)
12find_package (PkgConfig REQUIRED)
13pkg_check_modules (SDL2 REQUIRED sdl2)
14
15# Embedded resources are written to a generated source file.
16message (STATUS "Preparing embedded resources...")
17set (EMBED_RESOURCES
18 res/FiraSans-Regular.ttf
19 res/FiraSans-LightItalic.ttf
20 res/FiraMono-Regular.ttf
21)
22# if (UNIX AND NOT APPLE)
23# list (APPEND EMBED_RESOURCES res/appicon-64.png)
24# endif ()
25embed_make (${EMBED_RESOURCES})
26
27# Source files.
28set (SOURCES
29 ${CMAKE_CURRENT_BINARY_DIR}/embedded.c
30 ${CMAKE_CURRENT_BINARY_DIR}/embedded.h
31 src/main.c
32 src/app.c
33 src/app.h
34 src/stb_image.h
35 src/stb_truetype.h
36 # User interface:
37 src/ui/color.c
38 src/ui/color.h
39 src/ui/command.c
40 src/ui/command.h
41 src/ui/metrics.c
42 src/ui/metrics.h
43 src/ui/paint.c
44 src/ui/paint.h
45 src/ui/text.c
46 src/ui/text.h
47 src/ui/util.c
48 src/ui/util.h
49 src/ui/window.c
50 src/ui/window.h
51 # Widgets:
52 src/ui/widget.c
53 src/ui/widget.h
54 src/ui/inputwidget.c
55 src/ui/inputwidget.h
56 src/ui/labelwidget.c
57 src/ui/labelwidget.h
58)
59if (IOS)
60elseif (APPLE)
61 list (APPEND SOURCES src/ui/macos.m src/ui/macos.h)
62 # list (APPEND RESOURCES
63 # "res/Lagrange.icns"
64 # )
65endif ()
66if (MSYS)
67 set (WINRC_FILE_VERSION ${PROJECT_VERSION_MAJOR},${PROJECT_VERSION_MINOR},${PROJECT_VERSION_PATCH},0)
68 set (WINRC_PRODUCT_VERSION ${PROJECT_VERSION_MAJOR},${PROJECT_VERSION_MINOR},0,0)
69 configure_file (res/lagrange.rc.in ${CMAKE_CURRENT_BINARY_DIR}/lagrange.rc NEWLINE_STYLE WIN32)
70 list (APPEND SOURCES src/win32.c src/win32.h ${CMAKE_CURRENT_BINARY_DIR}/lagrange.rc)
71endif ()
72set_source_files_properties (${RESOURCES} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
73
74# Target.
75add_executable (app ${SOURCES} ${RESOURCES})
76set_target_properties (app PROPERTIES OUTPUT_NAME lagrange)
77target_include_directories (app PUBLIC
78 src
79 ${CMAKE_CURRENT_BINARY_DIR}
80 ${SDL2_INCLUDE_DIRS}
81)
82target_compile_options (app PUBLIC
83 -Werror=implicit-function-declaration
84 ${SDL2_CFLAGS}
85)
86target_compile_definitions (app PUBLIC LAGRANGE_APP_VERSION="${PROJECT_VERSION}")
87target_link_libraries (app PUBLIC the_Foundation::the_Foundation)
88target_link_libraries (app PUBLIC ${SDL2_LDFLAGS})
89if (APPLE)
90 if (IOS)
91 target_link_libraries (app PUBLIC "-framework UIKit")
92 else ()
93 target_link_libraries (app PUBLIC "-framework AppKit")
94 endif ()
95 set_target_properties (app PROPERTIES
96 OUTPUT_NAME "Lagrange"
97 MACOSX_BUNDLE YES
98 MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_LIST_DIR}/res/MacOSXBundleInfo.plist.in"
99 MACOSX_BUNDLE_BUNDLE_NAME "Lagrange"
100 MACOSX_BUNDLE_INFO_STRING "${PROJECT_VERSION}"
101 MACOSX_BUNDLE_ICON_FILE "Lagrange.icns"
102 MACOSX_BUNDLE_BUNDLE_VERSION "${PROJECT_VERSION}"
103 MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_VERSION}"
104 MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}"
105 MACOSX_BUNDLE_GUI_IDENTIFIER "fi.skyjake.Lagrange"
106 MACOSX_BUNDLE_COPYRIGHT "© ${COPYRIGHT_YEAR} Jaakko Keränen"
107 )
108endif ()
109if (UNIX)
110 target_link_libraries (app PUBLIC m)
111endif ()
112
113# Deployment.
114if (MSYS)
115 install (TARGETS app DESTINATION .)
116 install (PROGRAMS
117 ${SDL2_LIBDIR}/SDL2.dll
118 $<TARGET_FILE:the_Foundation::the_Foundation>
119 DESTINATION .
120 )
121elseif (UNIX AND NOT APPLE)
122 set_target_properties (app PROPERTIES
123 INSTALL_RPATH_USE_LINK_PATH YES
124 )
125 set (desktop ${CMAKE_CURRENT_BINARY_DIR}/fi.skyjake.lagrange.desktop)
126 file (WRITE ${desktop} "[Desktop Entry]
127Name=Lagrange
128Comment=${PROJECT_DESCRIPTION}
129;Categories=
130Exec=${CMAKE_INSTALL_PREFIX}/bin/lagrange
131Terminal=false
132Type=Application
133Icon=fi.skyjake.lagrange")
134 install (TARGETS app DESTINATION bin)
135 install (FILES ${desktop} DESTINATION share/applications)
136 # install (FILES res/appicon-128.png
137 # DESTINATION share/icons/hicolor/256x256/apps
138 # RENAME fi.skyjake.lagrange.png
139 # )
140endif ()
141
diff --git a/Embed.cmake b/Embed.cmake
new file mode 100644
index 00000000..9b506905
--- /dev/null
+++ b/Embed.cmake
@@ -0,0 +1,66 @@
1function (embed_getname output fn)
2 get_filename_component (name ${fn} NAME_WE)
3 string (REPLACE "-" "" name ${name})
4 string (SUBSTRING ${name} 0 1 first)
5 string (TOUPPER ${first} first)
6 string (SUBSTRING ${name} 1 -1 remainder)
7 set (name "${first}${remainder}")
8 get_filename_component (ext ${fn} EXT)
9 if (ext STREQUAL .ttf)
10 set (resName "font")
11 elseif (ext STREQUAL .png)
12 set (resName "image")
13 else ()
14 set (resName "blob")
15 endif ()
16 set (resName "${resName}${name}_Embedded" PARENT_SCOPE)
17endfunction (embed_getname)
18
19function (embed_write path name fnSource fnHeader)
20 message (STATUS "${path}")
21 file (READ ${path} fileData HEX)
22 string (REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1," cList ${fileData})
23 string (REGEX REPLACE
24 "(0x[0-9a-f][0-9a-f],0x[0-9a-f][0-9a-f],0x[0-9a-f][0-9a-f],0x[0-9a-f][0-9a-f],0x[0-9a-f][0-9a-f],0x[0-9a-f][0-9a-f],0x[0-9a-f][0-9a-f],0x[0-9a-f][0-9a-f],0x[0-9a-f][0-9a-f],0x[0-9a-f][0-9a-f],0x[0-9a-f][0-9a-f],0x[0-9a-f][0-9a-f],0x[0-9a-f][0-9a-f],0x[0-9a-f][0-9a-f],0x[0-9a-f][0-9a-f],0x[0-9a-f][0-9a-f],)"
25 "\\1\n " cList ${cList})
26 string (LENGTH ${fileData} len)
27 math (EXPR alen "${len} / 2")
28 set (src "static const uint8_t bytes_${name}_[] = {\n")
29 string (APPEND src " ${cList}\n")
30 string (APPEND src "};
31static iBlockData data_${name}_ = {
32 .refCount = 2, .data = iConstCast(char *, bytes_${name}_), .size = ${alen}, .allocSize = ${alen}
33};
34const iBlock ${name} = { &data_${name}_ };
35")
36 set (header "extern const iBlock ${name};\n")
37 # Output the results.
38 file (APPEND ${fnSource} "${src}")
39 file (APPEND ${fnHeader} "${header}")
40endfunction (embed_write)
41
42function (embed_make)
43 set (EMB_H ${CMAKE_CURRENT_BINARY_DIR}/embedded.h)
44 set (EMB_C ${CMAKE_CURRENT_BINARY_DIR}/embedded.c)
45 set (needGen NO)
46 if (NOT EXISTS ${EMB_H} OR NOT EXISTS ${EMB_C})
47 set (needGen YES)
48 else ()
49 file (TIMESTAMP ${EMB_H} genTime %s)
50 foreach (resPath ${ARGV})
51 set (fn "${CMAKE_CURRENT_LIST_DIR}/${resPath}")
52 file (TIMESTAMP ${fn} resTime %s)
53 if (${resTime} GREATER ${genTime})
54 set (needGen YES)
55 endif ()
56 endforeach (resPath)
57 endif ()
58 if (needGen)
59 file (WRITE ${EMB_H} "#include <the_Foundation/block.h>\n")
60 file (WRITE ${EMB_C} "#include \"embedded.h\"\n")
61 foreach (fn ${ARGV})
62 embed_getname (resName ${fn})
63 embed_write (${fn} ${resName} ${EMB_C} ${EMB_H})
64 endforeach (fn)
65 endif ()
66endfunction (embed_make)
diff --git a/res/FiraMono-Regular.ttf b/res/FiraMono-Regular.ttf
new file mode 100644
index 00000000..c4f34328
--- /dev/null
+++ b/res/FiraMono-Regular.ttf
Binary files differ
diff --git a/res/FiraSans-LightItalic.ttf b/res/FiraSans-LightItalic.ttf
new file mode 100644
index 00000000..3d3cc107
--- /dev/null
+++ b/res/FiraSans-LightItalic.ttf
Binary files differ
diff --git a/res/FiraSans-Regular.ttf b/res/FiraSans-Regular.ttf
new file mode 100644
index 00000000..a4e65638
--- /dev/null
+++ b/res/FiraSans-Regular.ttf
Binary files differ
diff --git a/res/MacOSXBundleInfo.plist.in b/res/MacOSXBundleInfo.plist.in
new file mode 100644
index 00000000..74b1662e
--- /dev/null
+++ b/res/MacOSXBundleInfo.plist.in
@@ -0,0 +1,55 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3<plist version="1.0">
4<dict>
5 <key>CFBundleDevelopmentRegion</key>
6 <string>English</string>
7 <key>CFBundleExecutable</key>
8 <string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
9 <key>CFBundleGetInfoString</key>
10 <string>${MACOSX_BUNDLE_INFO_STRING}</string>
11 <key>CFBundleIconFile</key>
12 <string>${MACOSX_BUNDLE_ICON_FILE}</string>
13 <key>CFBundleIdentifier</key>
14 <string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
15 <key>CFBundleInfoDictionaryVersion</key>
16 <string>6.0</string>
17 <key>CFBundleLongVersionString</key>
18 <string>${MACOSX_BUNDLE_LONG_VERSION_STRING}</string>
19 <key>CFBundleName</key>
20 <string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
21 <key>CFBundlePackageType</key>
22 <string>APPL</string>
23 <key>CFBundleShortVersionString</key>
24 <string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
25 <key>CFBundleSignature</key>
26 <string>????</string>
27 <key>CFBundleVersion</key>
28 <string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
29 <key>CSResourcesFileMapped</key>
30 <true/>
31 <key>NSHumanReadableCopyright</key>
32 <string>${MACOSX_BUNDLE_COPYRIGHT}</string>
33 <key>NSHighResolutionCapable</key>
34 <true/>
35 <key>CFBundleDocumentTypes</key>
36 <array>
37 <dict>
38 <key>CFBundleTypeExtensions</key>
39 <array>
40 <string>gmi</string>
41 </array>
42 <key>CFBundleTypeIconFile</key>
43 <string>text-gemini.icns</string>
44 <key>CFBundleTypeName</key>
45 <string>Gemini Text File</string>
46 <key>CFBundleTypeRole</key>
47 <string>Viewer</string>
48 <key>LSHandlerRank</key>
49 <string>Default</string>
50 <key>LSTypeIsPackage</key>
51 <false/>
52 </dict>
53 </array>
54</dict>
55</plist>
diff --git a/res/lagrange.rc.in b/res/lagrange.rc.in
new file mode 100644
index 00000000..7b9f0f65
--- /dev/null
+++ b/res/lagrange.rc.in
@@ -0,0 +1,32 @@
1#include <Windows.h>
2
3IDI_ICON1 ICON DISCARDABLE "${CMAKE_SOURCE_DIR}/res/lagrange.ico"
4
5VS_VERSION_INFO VERSIONINFO
6FILEVERSION ${WINRC_FILE_VERSION}
7PRODUCTVERSION ${WINRC_PRODUCT_VERSION}
8FILEFLAGSMASK 0
9FILEFLAGS 0
10FILEOS VOS_NT_WINDOWS32
11FILETYPE VFT_APP
12FILESUBTYPE VFT2_UNKNOWN
13BEGIN
14 BLOCK "StringFileInfo"
15 BEGIN
16 BLOCK "040904B0"
17 BEGIN
18 VALUE "CompanyName", "Jaakko Ker\xe4nen\0"
19 VALUE "FileDescription", "${PROJECT_DESCRIPTION}\0"
20 VALUE "FileVersion", "${PROJECT_VERSION}\0"
21 VALUE "InternalName", "fi.skyjake.lagrange\0"
22 VALUE "LegalCopyright", "(c) ${COPYRIGHT_YEAR} Jaakko Ker\xe4nen\0"
23 VALUE "OriginalFilename", "lagrange.exe\0"
24 VALUE "ProductName", "Lagrange\0"
25 VALUE "ProductVersion", "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}\0"
26 END
27 END
28 BLOCK "VarFileInfo"
29 BEGIN
30 VALUE "Translation", 0x409, 1200
31 END
32END
diff --git a/src/app.c b/src/app.c
new file mode 100644
index 00000000..019308be
--- /dev/null
+++ b/src/app.c
@@ -0,0 +1,300 @@
1#include "app.h"
2#include "ui/command.h"
3#include "ui/window.h"
4#include "ui/inputwidget.h"
5#include "ui/labelwidget.h"
6#include "ui/util.h"
7#include "ui/text.h"
8#include "ui/color.h"
9
10#include <the_Foundation/commandline.h>
11#include <the_Foundation/file.h>
12#include <the_Foundation/fileinfo.h>
13#include <the_Foundation/path.h>
14#include <the_Foundation/process.h>
15#include <the_Foundation/sortedarray.h>
16#include <the_Foundation/time.h>
17#include <SDL_events.h>
18#include <SDL_render.h>
19#include <SDL_video.h>
20
21#include <stdio.h>
22#include <stdarg.h>
23#include <errno.h>
24
25#if defined (iPlatformApple) && !defined (iPlatformIOS)
26# include "ui/macos.h"
27#endif
28
29iDeclareType(App)
30
31#if defined (iPlatformApple)
32static const char *dataDir_App_ = "~/Library/Application Support/fi.skyjake.Lagrange";
33#endif
34#if defined (iPlatformMsys)
35static const char *dataDir_App_ = "~/AppData/Roaming/fi.skyjake.Lagrange";
36#endif
37#if defined (iPlatformLinux)
38static const char *dataDir_App_ = "~/.config/lagrange";
39#endif
40static const char *prefsFileName_App_ = "prefs.cfg";
41
42struct Impl_App {
43 iCommandLine args;
44 iBool running;
45 iWindow * window;
46 iSortedArray tickers;
47 /* Preferences: */
48 iBool retainWindowSize;
49 float uiScale;
50};
51
52static iApp app_;
53
54iDeclareType(Ticker)
55
56struct Impl_Ticker {
57 iAny *context;
58 void (*callback)(iAny *);
59};
60
61static int cmp_Ticker_(const void *a, const void *b) {
62 const iTicker *elems[2] = { a, b };
63 return iCmp(elems[0]->context, elems[1]->context);
64}
65
66const iString *dateStr_(const iDate *date) {
67 return collectNewFormat_String("%d-%02d-%02d %02d:%02d:%02d",
68 date->year,
69 date->month,
70 date->day,
71 date->hour,
72 date->minute,
73 date->second);
74}
75
76static iString *serializePrefs_App_(const iApp *d) {
77 iString *str = new_String();
78 iWindow *win = get_Window();
79 if (d->retainWindowSize) {
80 int w, h, x, y;
81 SDL_GetWindowSize(d->window->win, &w, &h);
82 SDL_GetWindowPosition(d->window->win, &x, &y);
83 appendFormat_String(str, "restorewindow width:%d height:%d coord:%d %d\n", w, h, x, y);
84 }
85 appendFormat_String(str, "uiscale arg:%f\n", uiScale_Window(d->window));
86 return str;
87}
88
89static const iString *prefsFileName_(void) {
90 return collect_String(concatCStr_Path(&iStringLiteral(dataDir_App_), prefsFileName_App_));
91}
92
93static void loadPrefs_App_(iApp *d) {
94 iUnused(d);
95 /* Create the data dir if it doesn't exist yet. */
96 makeDirs_Path(collectNewCStr_String(dataDir_App_));
97 iFile *f = new_File(prefsFileName_());
98 if (open_File(f, readOnly_FileMode | text_FileMode)) {
99 iString *str = readString_File(f);
100 const iRangecc src = range_String(str);
101 iRangecc line = iNullRange;
102 while (nextSplit_Rangecc(&src, "\n", &line)) {
103 iString cmd;
104 initRange_String(&cmd, &line);
105 if (equal_Command(cstr_String(&cmd), "uiscale")) {
106 /* Must be handled before the window is created. */
107 setUiScale_Window(get_Window(), argf_Command(cstr_String(&cmd)));
108 }
109 else {
110 postCommandString_App(&cmd);
111 }
112 deinit_String(&cmd);
113 }
114 delete_String(str);
115 }
116 else {
117 /* default preference values */
118 }
119 iRelease(f);
120}
121
122static void savePrefs_App_(const iApp *d) {
123 iString *cfg = serializePrefs_App_(d);
124 iFile *f = new_File(prefsFileName_());
125 if (open_File(f, writeOnly_FileMode | text_FileMode)) {
126 write_File(f, &cfg->chars);
127 }
128 iRelease(f);
129 delete_String(cfg);
130}
131
132static void init_App_(iApp *d, int argc, char **argv) {
133 init_CommandLine(&d->args, argc, argv);
134 init_SortedArray(&d->tickers, sizeof(iTicker), cmp_Ticker_);
135 d->running = iFalse;
136 d->window = NULL;
137 d->retainWindowSize = iTrue;
138 loadPrefs_App_(d);
139 d->window = new_Window();
140 /* Widget state init. */ {
141 }
142}
143
144static void deinit_App(iApp *d) {
145 savePrefs_App_(d);
146 deinit_SortedArray(&d->tickers);
147 delete_Window(d->window);
148 d->window = NULL;
149 deinit_CommandLine(&d->args);
150}
151
152const iString *execPath_App(void) {
153 return executablePath_CommandLine(&app_.args);
154}
155
156void processEvents_App(void) {
157 iApp *d = &app_;
158 SDL_Event ev;
159 while (SDL_PollEvent(&ev)) {
160 switch (ev.type) {
161 case SDL_QUIT:
162 // if (isModified_Song(d->song)) {
163 // save_App_(d, autosavePath_App_(d));
164 // }
165 d->running = iFalse;
166 break;
167 case SDL_DROPFILE:
168 postCommandf_App("open url:file://%s", ev.drop.file);
169 break;
170 default: {
171 iBool wasUsed = processEvent_Window(d->window, &ev);
172 if (ev.type == SDL_USEREVENT && ev.user.code == command_UserEventCode) {
173#if defined (iPlatformApple) && !defined (iPlatformIOS)
174 handleCommand_MacOS(command_UserEvent(&ev));
175#endif
176 if (isCommand_UserEvent(&ev, "metrics.changed")) {
177 arrange_Widget(d->window->root);
178 }
179 if (!wasUsed) {
180 /* No widget handled the command, so we'll do it. */
181 handleCommand_App(ev.user.data1);
182 }
183 /* Allocated by postCommand_App(). */
184 free(ev.user.data1);
185 }
186 break;
187 }
188 }
189 }
190}
191
192static void runTickers_App_(iApp *d) {
193 /* Tickers may add themselves again, so we'll run off a copy. */
194 iSortedArray *pending = copy_SortedArray(&d->tickers);
195 clear_SortedArray(&d->tickers);
196 iConstForEach(Array, i, &pending->values) {
197 const iTicker *ticker = i.value;
198 if (ticker->callback) {
199 ticker->callback(ticker->context);
200 }
201 }
202 delete_SortedArray(pending);
203}
204
205static int run_App_(iApp *d) {
206 arrange_Widget(findWidget_App("root"));
207 d->running = iTrue;
208 SDL_EventState(SDL_DROPFILE, SDL_ENABLE); /* open files via drag'n'drop */
209 while (d->running) {
210 runTickers_App_(d);
211 processEvents_App();
212 destroyPending_Widget();
213 refresh_App();
214 }
215 return 0;
216}
217
218void refresh_App(void) {
219 iApp *d = &app_;
220 draw_Window(d->window);
221 recycle_Garbage();
222}
223
224int run_App(int argc, char **argv) {
225 init_App_(&app_, argc, argv);
226 const int rc = run_App_(&app_);
227 deinit_App(&app_);
228 return rc;
229}
230
231void postCommand_App(const char *command) {
232 SDL_Event ev;
233 ev.user.type = SDL_USEREVENT;
234 ev.user.code = command_UserEventCode;
235 ev.user.windowID = get_Window() ? SDL_GetWindowID(get_Window()->win) : 0;
236 ev.user.data1 = strdup(command);
237 ev.user.data2 = NULL;
238 SDL_PushEvent(&ev);
239 printf("[command] %s\n", command); fflush(stdout);
240}
241
242void postCommandf_App(const char *command, ...) {
243 iBlock chars;
244 init_Block(&chars, 0);
245 va_list args;
246 va_start(args, command);
247 vprintf_Block(&chars, command, args);
248 va_end(args);
249 postCommand_App(cstr_Block(&chars));
250 deinit_Block(&chars);
251}
252
253iAny *findWidget_App(const char *id) {
254 return findChild_Widget(app_.window->root, id);
255}
256
257void addTicker_App(void (*ticker)(iAny *), iAny *context) {
258 iApp *d = &app_;
259 insert_SortedArray(&d->tickers, &(iTicker){ context, ticker });
260}
261
262static iBool handlePrefsCommands_(iWidget *d, const char *cmd) {
263 if (equal_Command(cmd, "prefs.dismiss") || equal_Command(cmd, "preferences")) {
264 setUiScale_Window(get_Window(),
265 toFloat_String(text_InputWidget(findChild_Widget(d, "prefs.uiscale"))));
266 destroy_Widget(d);
267 return iTrue;
268 }
269 return iFalse;
270}
271
272iBool handleCommand_App(const char *cmd) {
273 iApp *d = &app_;
274 iWidget *root = d->window->root;
275 if (equal_Command(cmd, "open")) {
276 }
277 else if (equal_Command(cmd, "quit")) {
278 SDL_Event ev;
279 ev.type = SDL_QUIT;
280 SDL_PushEvent(&ev);
281 }
282 else if (equal_Command(cmd, "preferences")) {
283 iWindow *win = get_Window();
284 iWidget *dlg = makePreferences_Widget();
285 setToggle_Widget(findChild_Widget(dlg, "prefs.retainwindow"), d->retainWindowSize);
286 setText_InputWidget(findChild_Widget(dlg, "prefs.uiscale"),
287 collectNewFormat_String("%g", uiScale_Window(get_Window())));
288 setCommandHandler_Widget(dlg, handlePrefsCommands_);
289 }
290 else if (equal_Command(cmd, "restorewindow")) {
291 d->retainWindowSize = iTrue;
292 resize_Window(d->window, argLabel_Command(cmd, "width"), argLabel_Command(cmd, "height"));
293 const iInt2 pos = coord_Command(cmd);
294 SDL_SetWindowPosition(d->window->win, pos.x, pos.y);
295 }
296 else {
297 return iFalse;
298 }
299 return iTrue;
300}
diff --git a/src/app.h b/src/app.h
new file mode 100644
index 00000000..2c877d00
--- /dev/null
+++ b/src/app.h
@@ -0,0 +1,29 @@
1#pragma once
2
3/* Application core: event loop, base event processing, audio synth. */
4
5#include <the_Foundation/string.h>
6
7iDeclareType(Window)
8
9enum iUserEventCode {
10 command_UserEventCode = 1,
11};
12
13const iString *execPath_App (void);
14
15int run_App (int argc, char **argv);
16void processEvents_App (void);
17void refresh_App (void);
18
19iAny * findWidget_App (const char *id);
20void addTicker_App (void (*ticker)(iAny *), iAny *context);
21
22void postCommand_App (const char *command);
23void postCommandf_App (const char *command, ...);
24
25iLocalDef void postCommandString_App(const iString *command) {
26 postCommand_App(cstr_String(command));
27}
28
29iBool handleCommand_App (const char *cmd);
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 00000000..457cc1f2
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,25 @@
1#include <the_Foundation/commandline.h>
2#include <stdio.h>
3#if defined (iPlatformMsys)
4# define SDL_MAIN_HANDLED
5#endif
6#include <SDL.h>
7
8#include "app.h"
9
10int main(int argc, char **argv) {
11#if defined (iPlatformMsys)
12 /* MSYS runtime takes care of WinMain. */
13 SDL_SetMainReady();
14#endif
15 init_Foundation();
16 printf("Lagrange: A Beautiful Gemini Client\n");
17 /* Initialize SDL. */
18 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO)) {
19 fprintf(stderr, "SDL init failed: %s\n", SDL_GetError());
20 return -1;
21 }
22 run_App(argc, argv);
23 SDL_Quit();
24 return 0;
25}
diff --git a/src/stb_image.h b/src/stb_image.h
new file mode 100644
index 00000000..2857f05d
--- /dev/null
+++ b/src/stb_image.h
@@ -0,0 +1,7656 @@
1/* stb_image - v2.25 - public domain image loader - http://nothings.org/stb
2 no warranty implied; use at your own risk
3
4 Do this:
5 #define STB_IMAGE_IMPLEMENTATION
6 before you include this file in *one* C or C++ file to create the implementation.
7
8 // i.e. it should look like this:
9 #include ...
10 #include ...
11 #include ...
12 #define STB_IMAGE_IMPLEMENTATION
13 #include "stb_image.h"
14
15 You can #define STBI_ASSERT(x) before the #include to avoid using assert.h.
16 And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free
17
18
19 QUICK NOTES:
20 Primarily of interest to game developers and other people who can
21 avoid problematic images and only need the trivial interface
22
23 JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib)
24 PNG 1/2/4/8/16-bit-per-channel
25
26 TGA (not sure what subset, if a subset)
27 BMP non-1bpp, non-RLE
28 PSD (composited view only, no extra channels, 8/16 bit-per-channel)
29
30 GIF (*comp always reports as 4-channel)
31 HDR (radiance rgbE format)
32 PIC (Softimage PIC)
33 PNM (PPM and PGM binary only)
34
35 Animated GIF still needs a proper API, but here's one way to do it:
36 http://gist.github.com/urraka/685d9a6340b26b830d49
37
38 - decode from memory or through FILE (define STBI_NO_STDIO to remove code)
39 - decode from arbitrary I/O callbacks
40 - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON)
41
42 Full documentation under "DOCUMENTATION" below.
43
44
45LICENSE
46
47 See end of file for license information.
48
49RECENT REVISION HISTORY:
50
51 2.25 (2020-02-02) fix warnings
52 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically
53 2.23 (2019-08-11) fix clang static analysis warning
54 2.22 (2019-03-04) gif fixes, fix warnings
55 2.21 (2019-02-25) fix typo in comment
56 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs
57 2.19 (2018-02-11) fix warning
58 2.18 (2018-01-30) fix warnings
59 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings
60 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes
61 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC
62 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs
63 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes
64 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes
65 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64
66 RGB-format JPEG; remove white matting in PSD;
67 allocate large structures on the stack;
68 correct channel count for PNG & BMP
69 2.10 (2016-01-22) avoid warning introduced in 2.09
70 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED
71
72 See end of file for full revision history.
73
74
75 ============================ Contributors =========================
76
77 Image formats Extensions, features
78 Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info)
79 Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info)
80 Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG)
81 Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks)
82 Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG)
83 Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip)
84 Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD)
85 github:urraka (animated gif) Junggon Kim (PNM comments)
86 Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA)
87 socks-the-fox (16-bit PNG)
88 Jeremy Sawicki (handle all ImageNet JPGs)
89 Optimizations & bugfixes Mikhail Morozov (1-bit BMP)
90 Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query)
91 Arseny Kapoulkine
92 John-Mark Allen
93 Carmelo J Fdez-Aguera
94
95 Bug & warning fixes
96 Marc LeBlanc David Woo Guillaume George Martins Mozeiko
97 Christpher Lloyd Jerry Jansson Joseph Thomson Phil Jordan
98 Dave Moore Roy Eltham Hayaki Saito Nathan Reed
99 Won Chun Luke Graham Johan Duparc Nick Verigakis
100 the Horde3D community Thomas Ruf Ronny Chevalier github:rlyeh
101 Janez Zemva John Bartholomew Michal Cichon github:romigrou
102 Jonathan Blow Ken Hamada Tero Hanninen github:svdijk
103 Laurent Gomila Cort Stratton Sergio Gonzalez github:snagar
104 Aruelien Pocheville Thibault Reuille Cass Everitt github:Zelex
105 Ryamond Barbiero Paul Du Bois Engin Manap github:grim210
106 Aldo Culquicondor Philipp Wiesemann Dale Weiler github:sammyhw
107 Oriol Ferrer Mesia Josh Tobin Matthew Gregan github:phprus
108 Julian Raschke Gregory Mullen Baldur Karlsson github:poppolopoppo
109 Christian Floisand Kevin Schmidt JR Smith github:darealshinji
110 Brad Weinberger Matvey Cherevko github:Michaelangel007
111 Blazej Dariusz Roszkowski Alexander Veselov
112*/
113
114#ifndef STBI_INCLUDE_STB_IMAGE_H
115#define STBI_INCLUDE_STB_IMAGE_H
116
117// DOCUMENTATION
118//
119// Limitations:
120// - no 12-bit-per-channel JPEG
121// - no JPEGs with arithmetic coding
122// - GIF always returns *comp=4
123//
124// Basic usage (see HDR discussion below for HDR usage):
125// int x,y,n;
126// unsigned char *data = stbi_load(filename, &x, &y, &n, 0);
127// // ... process data if not NULL ...
128// // ... x = width, y = height, n = # 8-bit components per pixel ...
129// // ... replace '0' with '1'..'4' to force that many components per pixel
130// // ... but 'n' will always be the number that it would have been if you said 0
131// stbi_image_free(data)
132//
133// Standard parameters:
134// int *x -- outputs image width in pixels
135// int *y -- outputs image height in pixels
136// int *channels_in_file -- outputs # of image components in image file
137// int desired_channels -- if non-zero, # of image components requested in result
138//
139// The return value from an image loader is an 'unsigned char *' which points
140// to the pixel data, or NULL on an allocation failure or if the image is
141// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels,
142// with each pixel consisting of N interleaved 8-bit components; the first
143// pixel pointed to is top-left-most in the image. There is no padding between
144// image scanlines or between pixels, regardless of format. The number of
145// components N is 'desired_channels' if desired_channels is non-zero, or
146// *channels_in_file otherwise. If desired_channels is non-zero,
147// *channels_in_file has the number of components that _would_ have been
148// output otherwise. E.g. if you set desired_channels to 4, you will always
149// get RGBA output, but you can check *channels_in_file to see if it's trivially
150// opaque because e.g. there were only 3 channels in the source image.
151//
152// An output image with N components has the following components interleaved
153// in this order in each pixel:
154//
155// N=#comp components
156// 1 grey
157// 2 grey, alpha
158// 3 red, green, blue
159// 4 red, green, blue, alpha
160//
161// If image loading fails for any reason, the return value will be NULL,
162// and *x, *y, *channels_in_file will be unchanged. The function
163// stbi_failure_reason() can be queried for an extremely brief, end-user
164// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS
165// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly
166// more user-friendly ones.
167//
168// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized.
169//
170// ===========================================================================
171//
172// UNICODE:
173//
174// If compiling for Windows and you wish to use Unicode filenames, compile
175// with
176// #define STBI_WINDOWS_UTF8
177// and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert
178// Windows wchar_t filenames to utf8.
179//
180// ===========================================================================
181//
182// Philosophy
183//
184// stb libraries are designed with the following priorities:
185//
186// 1. easy to use
187// 2. easy to maintain
188// 3. good performance
189//
190// Sometimes I let "good performance" creep up in priority over "easy to maintain",
191// and for best performance I may provide less-easy-to-use APIs that give higher
192// performance, in addition to the easy-to-use ones. Nevertheless, it's important
193// to keep in mind that from the standpoint of you, a client of this library,
194// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all.
195//
196// Some secondary priorities arise directly from the first two, some of which
197// provide more explicit reasons why performance can't be emphasized.
198//
199// - Portable ("ease of use")
200// - Small source code footprint ("easy to maintain")
201// - No dependencies ("ease of use")
202//
203// ===========================================================================
204//
205// I/O callbacks
206//
207// I/O callbacks allow you to read from arbitrary sources, like packaged
208// files or some other source. Data read from callbacks are processed
209// through a small internal buffer (currently 128 bytes) to try to reduce
210// overhead.
211//
212// The three functions you must define are "read" (reads some bytes of data),
213// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end).
214//
215// ===========================================================================
216//
217// SIMD support
218//
219// The JPEG decoder will try to automatically use SIMD kernels on x86 when
220// supported by the compiler. For ARM Neon support, you must explicitly
221// request it.
222//
223// (The old do-it-yourself SIMD API is no longer supported in the current
224// code.)
225//
226// On x86, SSE2 will automatically be used when available based on a run-time
227// test; if not, the generic C versions are used as a fall-back. On ARM targets,
228// the typical path is to have separate builds for NEON and non-NEON devices
229// (at least this is true for iOS and Android). Therefore, the NEON support is
230// toggled by a build flag: define STBI_NEON to get NEON loops.
231//
232// If for some reason you do not want to use any of SIMD code, or if
233// you have issues compiling it, you can disable it entirely by
234// defining STBI_NO_SIMD.
235//
236// ===========================================================================
237//
238// HDR image support (disable by defining STBI_NO_HDR)
239//
240// stb_image supports loading HDR images in general, and currently the Radiance
241// .HDR file format specifically. You can still load any file through the existing
242// interface; if you attempt to load an HDR file, it will be automatically remapped
243// to LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1;
244// both of these constants can be reconfigured through this interface:
245//
246// stbi_hdr_to_ldr_gamma(2.2f);
247// stbi_hdr_to_ldr_scale(1.0f);
248//
249// (note, do not use _inverse_ constants; stbi_image will invert them
250// appropriately).
251//
252// Additionally, there is a new, parallel interface for loading files as
253// (linear) floats to preserve the full dynamic range:
254//
255// float *data = stbi_loadf(filename, &x, &y, &n, 0);
256//
257// If you load LDR images through this interface, those images will
258// be promoted to floating point values, run through the inverse of
259// constants corresponding to the above:
260//
261// stbi_ldr_to_hdr_scale(1.0f);
262// stbi_ldr_to_hdr_gamma(2.2f);
263//
264// Finally, given a filename (or an open file or memory block--see header
265// file for details) containing image data, you can query for the "most
266// appropriate" interface to use (that is, whether the image is HDR or
267// not), using:
268//
269// stbi_is_hdr(char *filename);
270//
271// ===========================================================================
272//
273// iPhone PNG support:
274//
275// By default we convert iphone-formatted PNGs back to RGB, even though
276// they are internally encoded differently. You can disable this conversion
277// by calling stbi_convert_iphone_png_to_rgb(0), in which case
278// you will always just get the native iphone "format" through (which
279// is BGR stored in RGB).
280//
281// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per
282// pixel to remove any premultiplied alpha *only* if the image file explicitly
283// says there's premultiplied data (currently only happens in iPhone images,
284// and only if iPhone convert-to-rgb processing is on).
285//
286// ===========================================================================
287//
288// ADDITIONAL CONFIGURATION
289//
290// - You can suppress implementation of any of the decoders to reduce
291// your code footprint by #defining one or more of the following
292// symbols before creating the implementation.
293//
294// STBI_NO_JPEG
295// STBI_NO_PNG
296// STBI_NO_BMP
297// STBI_NO_PSD
298// STBI_NO_TGA
299// STBI_NO_GIF
300// STBI_NO_HDR
301// STBI_NO_PIC
302// STBI_NO_PNM (.ppm and .pgm)
303//
304// - You can request *only* certain decoders and suppress all other ones
305// (this will be more forward-compatible, as addition of new decoders
306// doesn't require you to disable them explicitly):
307//
308// STBI_ONLY_JPEG
309// STBI_ONLY_PNG
310// STBI_ONLY_BMP
311// STBI_ONLY_PSD
312// STBI_ONLY_TGA
313// STBI_ONLY_GIF
314// STBI_ONLY_HDR
315// STBI_ONLY_PIC
316// STBI_ONLY_PNM (.ppm and .pgm)
317//
318// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still
319// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB
320//
321
322
323#ifndef STBI_NO_STDIO
324#include <stdio.h>
325#endif // STBI_NO_STDIO
326
327#define STBI_VERSION 1
328
329enum
330{
331 STBI_default = 0, // only used for desired_channels
332
333 STBI_grey = 1,
334 STBI_grey_alpha = 2,
335 STBI_rgb = 3,
336 STBI_rgb_alpha = 4
337};
338
339#include <stdlib.h>
340typedef unsigned char stbi_uc;
341typedef unsigned short stbi_us;
342
343#ifdef __cplusplus
344extern "C" {
345#endif
346
347#ifndef STBIDEF
348#ifdef STB_IMAGE_STATIC
349#define STBIDEF static
350#else
351#define STBIDEF extern
352#endif
353#endif
354
355//////////////////////////////////////////////////////////////////////////////
356//
357// PRIMARY API - works on images of any type
358//
359
360//
361// load image by filename, open file, or memory buffer
362//
363
364typedef struct
365{
366 int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read
367 void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative
368 int (*eof) (void *user); // returns nonzero if we are at end of file/data
369} stbi_io_callbacks;
370
371////////////////////////////////////
372//
373// 8-bits-per-channel interface
374//
375
376STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels);
377STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels);
378
379#ifndef STBI_NO_STDIO
380STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels);
381STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels);
382// for stbi_load_from_file, file pointer is left pointing immediately after image
383#endif
384
385#ifndef STBI_NO_GIF
386STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp);
387#endif
388
389#ifdef STBI_WINDOWS_UTF8
390STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input);
391#endif
392
393////////////////////////////////////
394//
395// 16-bits-per-channel interface
396//
397
398STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels);
399STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels);
400
401#ifndef STBI_NO_STDIO
402STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels);
403STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels);
404#endif
405
406////////////////////////////////////
407//
408// float-per-channel interface
409//
410#ifndef STBI_NO_LINEAR
411 STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels);
412 STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels);
413
414 #ifndef STBI_NO_STDIO
415 STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels);
416 STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels);
417 #endif
418#endif
419
420#ifndef STBI_NO_HDR
421 STBIDEF void stbi_hdr_to_ldr_gamma(float gamma);
422 STBIDEF void stbi_hdr_to_ldr_scale(float scale);
423#endif // STBI_NO_HDR
424
425#ifndef STBI_NO_LINEAR
426 STBIDEF void stbi_ldr_to_hdr_gamma(float gamma);
427 STBIDEF void stbi_ldr_to_hdr_scale(float scale);
428#endif // STBI_NO_LINEAR
429
430// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR
431STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user);
432STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len);
433#ifndef STBI_NO_STDIO
434STBIDEF int stbi_is_hdr (char const *filename);
435STBIDEF int stbi_is_hdr_from_file(FILE *f);
436#endif // STBI_NO_STDIO
437
438
439// get a VERY brief reason for failure
440// on most compilers (and ALL modern mainstream compilers) this is threadsafe
441STBIDEF const char *stbi_failure_reason (void);
442
443// free the loaded image -- this is just free()
444STBIDEF void stbi_image_free (void *retval_from_stbi_load);
445
446// get image dimensions & components without fully decoding
447STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp);
448STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp);
449STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len);
450STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user);
451
452#ifndef STBI_NO_STDIO
453STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp);
454STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp);
455STBIDEF int stbi_is_16_bit (char const *filename);
456STBIDEF int stbi_is_16_bit_from_file(FILE *f);
457#endif
458
459
460
461// for image formats that explicitly notate that they have premultiplied alpha,
462// we just return the colors as stored in the file. set this flag to force
463// unpremultiplication. results are undefined if the unpremultiply overflow.
464STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply);
465
466// indicate whether we should process iphone images back to canonical format,
467// or just pass them through "as-is"
468STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert);
469
470// flip the image vertically, so the first pixel in the output array is the bottom left
471STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip);
472
473// as above, but only applies to images loaded on the thread that calls the function
474// this function is only available if your compiler supports thread-local variables;
475// calling it will fail to link if your compiler doesn't
476STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip);
477
478// ZLIB client - used by PNG, available for other purposes
479
480STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen);
481STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header);
482STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen);
483STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen);
484
485STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen);
486STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen);
487
488
489#ifdef __cplusplus
490}
491#endif
492
493//
494//
495//// end header file /////////////////////////////////////////////////////
496#endif // STBI_INCLUDE_STB_IMAGE_H
497
498#ifdef STB_IMAGE_IMPLEMENTATION
499
500#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \
501 || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \
502 || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \
503 || defined(STBI_ONLY_ZLIB)
504 #ifndef STBI_ONLY_JPEG
505 #define STBI_NO_JPEG
506 #endif
507 #ifndef STBI_ONLY_PNG
508 #define STBI_NO_PNG
509 #endif
510 #ifndef STBI_ONLY_BMP
511 #define STBI_NO_BMP
512 #endif
513 #ifndef STBI_ONLY_PSD
514 #define STBI_NO_PSD
515 #endif
516 #ifndef STBI_ONLY_TGA
517 #define STBI_NO_TGA
518 #endif
519 #ifndef STBI_ONLY_GIF
520 #define STBI_NO_GIF
521 #endif
522 #ifndef STBI_ONLY_HDR
523 #define STBI_NO_HDR
524 #endif
525 #ifndef STBI_ONLY_PIC
526 #define STBI_NO_PIC
527 #endif
528 #ifndef STBI_ONLY_PNM
529 #define STBI_NO_PNM
530 #endif
531#endif
532
533#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB)
534#define STBI_NO_ZLIB
535#endif
536
537
538#include <stdarg.h>
539#include <stddef.h> // ptrdiff_t on osx
540#include <stdlib.h>
541#include <string.h>
542#include <limits.h>
543
544#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR)
545#include <math.h> // ldexp, pow
546#endif
547
548#ifndef STBI_NO_STDIO
549#include <stdio.h>
550#endif
551
552#ifndef STBI_ASSERT
553#include <assert.h>
554#define STBI_ASSERT(x) assert(x)
555#endif
556
557#ifdef __cplusplus
558#define STBI_EXTERN extern "C"
559#else
560#define STBI_EXTERN extern
561#endif
562
563
564#ifndef _MSC_VER
565 #ifdef __cplusplus
566 #define stbi_inline inline
567 #else
568 #define stbi_inline
569 #endif
570#else
571 #define stbi_inline __forceinline
572#endif
573
574#ifndef STBI_NO_THREAD_LOCALS
575 #if defined(__cplusplus) && __cplusplus >= 201103L
576 #define STBI_THREAD_LOCAL thread_local
577 #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
578 #define STBI_THREAD_LOCAL _Thread_local
579 #elif defined(__GNUC__)
580 #define STBI_THREAD_LOCAL __thread
581 #elif defined(_MSC_VER)
582 #define STBI_THREAD_LOCAL __declspec(thread)
583#endif
584#endif
585
586#ifdef _MSC_VER
587typedef unsigned short stbi__uint16;
588typedef signed short stbi__int16;
589typedef unsigned int stbi__uint32;
590typedef signed int stbi__int32;
591#else
592#include <stdint.h>
593typedef uint16_t stbi__uint16;
594typedef int16_t stbi__int16;
595typedef uint32_t stbi__uint32;
596typedef int32_t stbi__int32;
597#endif
598
599// should produce compiler error if size is wrong
600typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1];
601
602#ifdef _MSC_VER
603#define STBI_NOTUSED(v) (void)(v)
604#else
605#define STBI_NOTUSED(v) (void)sizeof(v)
606#endif
607
608#ifdef _MSC_VER
609#define STBI_HAS_LROTL
610#endif
611
612#ifdef STBI_HAS_LROTL
613 #define stbi_lrot(x,y) _lrotl(x,y)
614#else
615 #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y))))
616#endif
617
618#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED))
619// ok
620#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED)
621// ok
622#else
623#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)."
624#endif
625
626#ifndef STBI_MALLOC
627#define STBI_MALLOC(sz) malloc(sz)
628#define STBI_REALLOC(p,newsz) realloc(p,newsz)
629#define STBI_FREE(p) free(p)
630#endif
631
632#ifndef STBI_REALLOC_SIZED
633#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz)
634#endif
635
636// x86/x64 detection
637#if defined(__x86_64__) || defined(_M_X64)
638#define STBI__X64_TARGET
639#elif defined(__i386) || defined(_M_IX86)
640#define STBI__X86_TARGET
641#endif
642
643#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD)
644// gcc doesn't support sse2 intrinsics unless you compile with -msse2,
645// which in turn means it gets to use SSE2 everywhere. This is unfortunate,
646// but previous attempts to provide the SSE2 functions with runtime
647// detection caused numerous issues. The way architecture extensions are
648// exposed in GCC/Clang is, sadly, not really suited for one-file libs.
649// New behavior: if compiled with -msse2, we use SSE2 without any
650// detection; if not, we don't use it at all.
651#define STBI_NO_SIMD
652#endif
653
654#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD)
655// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET
656//
657// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the
658// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant.
659// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not
660// simultaneously enabling "-mstackrealign".
661//
662// See https://github.com/nothings/stb/issues/81 for more information.
663//
664// So default to no SSE2 on 32-bit MinGW. If you've read this far and added
665// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2.
666#define STBI_NO_SIMD
667#endif
668
669#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET))
670#define STBI_SSE2
671#include <emmintrin.h>
672
673#ifdef _MSC_VER
674
675#if _MSC_VER >= 1400 // not VC6
676#include <intrin.h> // __cpuid
677static int stbi__cpuid3(void)
678{
679 int info[4];
680 __cpuid(info,1);
681 return info[3];
682}
683#else
684static int stbi__cpuid3(void)
685{
686 int res;
687 __asm {
688 mov eax,1
689 cpuid
690 mov res,edx
691 }
692 return res;
693}
694#endif
695
696#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name
697
698#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2)
699static int stbi__sse2_available(void)
700{
701 int info3 = stbi__cpuid3();
702 return ((info3 >> 26) & 1) != 0;
703}
704#endif
705
706#else // assume GCC-style if not VC++
707#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16)))
708
709#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2)
710static int stbi__sse2_available(void)
711{
712 // If we're even attempting to compile this on GCC/Clang, that means
713 // -msse2 is on, which means the compiler is allowed to use SSE2
714 // instructions at will, and so are we.
715 return 1;
716}
717#endif
718
719#endif
720#endif
721
722// ARM NEON
723#if defined(STBI_NO_SIMD) && defined(STBI_NEON)
724#undef STBI_NEON
725#endif
726
727#ifdef STBI_NEON
728#include <arm_neon.h>
729// assume GCC or Clang on ARM targets
730#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16)))
731#endif
732
733#ifndef STBI_SIMD_ALIGN
734#define STBI_SIMD_ALIGN(type, name) type name
735#endif
736
737///////////////////////////////////////////////
738//
739// stbi__context struct and start_xxx functions
740
741// stbi__context structure is our basic context used by all images, so it
742// contains all the IO context, plus some basic image information
743typedef struct
744{
745 stbi__uint32 img_x, img_y;
746 int img_n, img_out_n;
747
748 stbi_io_callbacks io;
749 void *io_user_data;
750
751 int read_from_callbacks;
752 int buflen;
753 stbi_uc buffer_start[128];
754
755 stbi_uc *img_buffer, *img_buffer_end;
756 stbi_uc *img_buffer_original, *img_buffer_original_end;
757} stbi__context;
758
759
760static void stbi__refill_buffer(stbi__context *s);
761
762// initialize a memory-decode context
763static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len)
764{
765 s->io.read = NULL;
766 s->read_from_callbacks = 0;
767 s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer;
768 s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len;
769}
770
771// initialize a callback-based context
772static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user)
773{
774 s->io = *c;
775 s->io_user_data = user;
776 s->buflen = sizeof(s->buffer_start);
777 s->read_from_callbacks = 1;
778 s->img_buffer_original = s->buffer_start;
779 stbi__refill_buffer(s);
780 s->img_buffer_original_end = s->img_buffer_end;
781}
782
783#ifndef STBI_NO_STDIO
784
785static int stbi__stdio_read(void *user, char *data, int size)
786{
787 return (int) fread(data,1,size,(FILE*) user);
788}
789
790static void stbi__stdio_skip(void *user, int n)
791{
792 fseek((FILE*) user, n, SEEK_CUR);
793}
794
795static int stbi__stdio_eof(void *user)
796{
797 return feof((FILE*) user);
798}
799
800static stbi_io_callbacks stbi__stdio_callbacks =
801{
802 stbi__stdio_read,
803 stbi__stdio_skip,
804 stbi__stdio_eof,
805};
806
807static void stbi__start_file(stbi__context *s, FILE *f)
808{
809 stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f);
810}
811
812//static void stop_file(stbi__context *s) { }
813
814#endif // !STBI_NO_STDIO
815
816static void stbi__rewind(stbi__context *s)
817{
818 // conceptually rewind SHOULD rewind to the beginning of the stream,
819 // but we just rewind to the beginning of the initial buffer, because
820 // we only use it after doing 'test', which only ever looks at at most 92 bytes
821 s->img_buffer = s->img_buffer_original;
822 s->img_buffer_end = s->img_buffer_original_end;
823}
824
825enum
826{
827 STBI_ORDER_RGB,
828 STBI_ORDER_BGR
829};
830
831typedef struct
832{
833 int bits_per_channel;
834 int num_channels;
835 int channel_order;
836} stbi__result_info;
837
838#ifndef STBI_NO_JPEG
839static int stbi__jpeg_test(stbi__context *s);
840static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);
841static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp);
842#endif
843
844#ifndef STBI_NO_PNG
845static int stbi__png_test(stbi__context *s);
846static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);
847static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp);
848static int stbi__png_is16(stbi__context *s);
849#endif
850
851#ifndef STBI_NO_BMP
852static int stbi__bmp_test(stbi__context *s);
853static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);
854static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp);
855#endif
856
857#ifndef STBI_NO_TGA
858static int stbi__tga_test(stbi__context *s);
859static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);
860static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp);
861#endif
862
863#ifndef STBI_NO_PSD
864static int stbi__psd_test(stbi__context *s);
865static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc);
866static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp);
867static int stbi__psd_is16(stbi__context *s);
868#endif
869
870#ifndef STBI_NO_HDR
871static int stbi__hdr_test(stbi__context *s);
872static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);
873static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp);
874#endif
875
876#ifndef STBI_NO_PIC
877static int stbi__pic_test(stbi__context *s);
878static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);
879static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp);
880#endif
881
882#ifndef STBI_NO_GIF
883static int stbi__gif_test(stbi__context *s);
884static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);
885static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp);
886static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp);
887#endif
888
889#ifndef STBI_NO_PNM
890static int stbi__pnm_test(stbi__context *s);
891static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);
892static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp);
893#endif
894
895static
896#ifdef STBI_THREAD_LOCAL
897STBI_THREAD_LOCAL
898#endif
899const char *stbi__g_failure_reason;
900
901STBIDEF const char *stbi_failure_reason(void)
902{
903 return stbi__g_failure_reason;
904}
905
906#ifndef STBI_NO_FAILURE_STRINGS
907static int stbi__err(const char *str)
908{
909 stbi__g_failure_reason = str;
910 return 0;
911}
912#endif
913
914static void *stbi__malloc(size_t size)
915{
916 return STBI_MALLOC(size);
917}
918
919// stb_image uses ints pervasively, including for offset calculations.
920// therefore the largest decoded image size we can support with the
921// current code, even on 64-bit targets, is INT_MAX. this is not a
922// significant limitation for the intended use case.
923//
924// we do, however, need to make sure our size calculations don't
925// overflow. hence a few helper functions for size calculations that
926// multiply integers together, making sure that they're non-negative
927// and no overflow occurs.
928
929// return 1 if the sum is valid, 0 on overflow.
930// negative terms are considered invalid.
931static int stbi__addsizes_valid(int a, int b)
932{
933 if (b < 0) return 0;
934 // now 0 <= b <= INT_MAX, hence also
935 // 0 <= INT_MAX - b <= INTMAX.
936 // And "a + b <= INT_MAX" (which might overflow) is the
937 // same as a <= INT_MAX - b (no overflow)
938 return a <= INT_MAX - b;
939}
940
941// returns 1 if the product is valid, 0 on overflow.
942// negative factors are considered invalid.
943static int stbi__mul2sizes_valid(int a, int b)
944{
945 if (a < 0 || b < 0) return 0;
946 if (b == 0) return 1; // mul-by-0 is always safe
947 // portable way to check for no overflows in a*b
948 return a <= INT_MAX/b;
949}
950
951#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR)
952// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow
953static int stbi__mad2sizes_valid(int a, int b, int add)
954{
955 return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add);
956}
957#endif
958
959// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow
960static int stbi__mad3sizes_valid(int a, int b, int c, int add)
961{
962 return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) &&
963 stbi__addsizes_valid(a*b*c, add);
964}
965
966// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow
967#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR)
968static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add)
969{
970 return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) &&
971 stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add);
972}
973#endif
974
975#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR)
976// mallocs with size overflow checking
977static void *stbi__malloc_mad2(int a, int b, int add)
978{
979 if (!stbi__mad2sizes_valid(a, b, add)) return NULL;
980 return stbi__malloc(a*b + add);
981}
982#endif
983
984static void *stbi__malloc_mad3(int a, int b, int c, int add)
985{
986 if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL;
987 return stbi__malloc(a*b*c + add);
988}
989
990#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR)
991static void *stbi__malloc_mad4(int a, int b, int c, int d, int add)
992{
993 if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL;
994 return stbi__malloc(a*b*c*d + add);
995}
996#endif
997
998// stbi__err - error
999// stbi__errpf - error returning pointer to float
1000// stbi__errpuc - error returning pointer to unsigned char
1001
1002#ifdef STBI_NO_FAILURE_STRINGS
1003 #define stbi__err(x,y) 0
1004#elif defined(STBI_FAILURE_USERMSG)
1005 #define stbi__err(x,y) stbi__err(y)
1006#else
1007 #define stbi__err(x,y) stbi__err(x)
1008#endif
1009
1010#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL))
1011#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL))
1012
1013STBIDEF void stbi_image_free(void *retval_from_stbi_load)
1014{
1015 STBI_FREE(retval_from_stbi_load);
1016}
1017
1018#ifndef STBI_NO_LINEAR
1019static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp);
1020#endif
1021
1022#ifndef STBI_NO_HDR
1023static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp);
1024#endif
1025
1026static int stbi__vertically_flip_on_load_global = 0;
1027
1028STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip)
1029{
1030 stbi__vertically_flip_on_load_global = flag_true_if_should_flip;
1031}
1032
1033#ifndef STBI_THREAD_LOCAL
1034#define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global
1035#else
1036static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set;
1037
1038STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip)
1039{
1040 stbi__vertically_flip_on_load_local = flag_true_if_should_flip;
1041 stbi__vertically_flip_on_load_set = 1;
1042}
1043
1044#define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \
1045 ? stbi__vertically_flip_on_load_local \
1046 : stbi__vertically_flip_on_load_global)
1047#endif // STBI_THREAD_LOCAL
1048
1049static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc)
1050{
1051 memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields
1052 ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed
1053 ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order
1054 ri->num_channels = 0;
1055
1056 #ifndef STBI_NO_JPEG
1057 if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri);
1058 #endif
1059 #ifndef STBI_NO_PNG
1060 if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri);
1061 #endif
1062 #ifndef STBI_NO_BMP
1063 if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri);
1064 #endif
1065 #ifndef STBI_NO_GIF
1066 if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri);
1067 #endif
1068 #ifndef STBI_NO_PSD
1069 if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc);
1070 #else
1071 STBI_NOTUSED(bpc);
1072 #endif
1073 #ifndef STBI_NO_PIC
1074 if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri);
1075 #endif
1076 #ifndef STBI_NO_PNM
1077 if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri);
1078 #endif
1079
1080 #ifndef STBI_NO_HDR
1081 if (stbi__hdr_test(s)) {
1082 float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri);
1083 return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp);
1084 }
1085 #endif
1086
1087 #ifndef STBI_NO_TGA
1088 // test tga last because it's a crappy test!
1089 if (stbi__tga_test(s))
1090 return stbi__tga_load(s,x,y,comp,req_comp, ri);
1091 #endif
1092
1093 return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt");
1094}
1095
1096static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels)
1097{
1098 int i;
1099 int img_len = w * h * channels;
1100 stbi_uc *reduced;
1101
1102 reduced = (stbi_uc *) stbi__malloc(img_len);
1103 if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory");
1104
1105 for (i = 0; i < img_len; ++i)
1106 reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling
1107
1108 STBI_FREE(orig);
1109 return reduced;
1110}
1111
1112static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels)
1113{
1114 int i;
1115 int img_len = w * h * channels;
1116 stbi__uint16 *enlarged;
1117
1118 enlarged = (stbi__uint16 *) stbi__malloc(img_len*2);
1119 if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory");
1120
1121 for (i = 0; i < img_len; ++i)
1122 enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff
1123
1124 STBI_FREE(orig);
1125 return enlarged;
1126}
1127
1128static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel)
1129{
1130 int row;
1131 size_t bytes_per_row = (size_t)w * bytes_per_pixel;
1132 stbi_uc temp[2048];
1133 stbi_uc *bytes = (stbi_uc *)image;
1134
1135 for (row = 0; row < (h>>1); row++) {
1136 stbi_uc *row0 = bytes + row*bytes_per_row;
1137 stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row;
1138 // swap row0 with row1
1139 size_t bytes_left = bytes_per_row;
1140 while (bytes_left) {
1141 size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp);
1142 memcpy(temp, row0, bytes_copy);
1143 memcpy(row0, row1, bytes_copy);
1144 memcpy(row1, temp, bytes_copy);
1145 row0 += bytes_copy;
1146 row1 += bytes_copy;
1147 bytes_left -= bytes_copy;
1148 }
1149 }
1150}
1151
1152#ifndef STBI_NO_GIF
1153static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel)
1154{
1155 int slice;
1156 int slice_size = w * h * bytes_per_pixel;
1157
1158 stbi_uc *bytes = (stbi_uc *)image;
1159 for (slice = 0; slice < z; ++slice) {
1160 stbi__vertical_flip(bytes, w, h, bytes_per_pixel);
1161 bytes += slice_size;
1162 }
1163}
1164#endif
1165
1166static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp)
1167{
1168 stbi__result_info ri;
1169 void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8);
1170
1171 if (result == NULL)
1172 return NULL;
1173
1174 if (ri.bits_per_channel != 8) {
1175 STBI_ASSERT(ri.bits_per_channel == 16);
1176 result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp);
1177 ri.bits_per_channel = 8;
1178 }
1179
1180 // @TODO: move stbi__convert_format to here
1181
1182 if (stbi__vertically_flip_on_load) {
1183 int channels = req_comp ? req_comp : *comp;
1184 stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc));
1185 }
1186
1187 return (unsigned char *) result;
1188}
1189
1190static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp)
1191{
1192 stbi__result_info ri;
1193 void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16);
1194
1195 if (result == NULL)
1196 return NULL;
1197
1198 if (ri.bits_per_channel != 16) {
1199 STBI_ASSERT(ri.bits_per_channel == 8);
1200 result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp);
1201 ri.bits_per_channel = 16;
1202 }
1203
1204 // @TODO: move stbi__convert_format16 to here
1205 // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision
1206
1207 if (stbi__vertically_flip_on_load) {
1208 int channels = req_comp ? req_comp : *comp;
1209 stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16));
1210 }
1211
1212 return (stbi__uint16 *) result;
1213}
1214
1215#if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR)
1216static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp)
1217{
1218 if (stbi__vertically_flip_on_load && result != NULL) {
1219 int channels = req_comp ? req_comp : *comp;
1220 stbi__vertical_flip(result, *x, *y, channels * sizeof(float));
1221 }
1222}
1223#endif
1224
1225#ifndef STBI_NO_STDIO
1226
1227#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8)
1228STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide);
1229STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default);
1230#endif
1231
1232#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8)
1233STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input)
1234{
1235 return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL);
1236}
1237#endif
1238
1239static FILE *stbi__fopen(char const *filename, char const *mode)
1240{
1241 FILE *f;
1242#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8)
1243 wchar_t wMode[64];
1244 wchar_t wFilename[1024];
1245 if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)))
1246 return 0;
1247
1248 if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)))
1249 return 0;
1250
1251#if _MSC_VER >= 1400
1252 if (0 != _wfopen_s(&f, wFilename, wMode))
1253 f = 0;
1254#else
1255 f = _wfopen(wFilename, wMode);
1256#endif
1257
1258#elif defined(_MSC_VER) && _MSC_VER >= 1400
1259 if (0 != fopen_s(&f, filename, mode))
1260 f=0;
1261#else
1262 f = fopen(filename, mode);
1263#endif
1264 return f;
1265}
1266
1267
1268STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp)
1269{
1270 FILE *f = stbi__fopen(filename, "rb");
1271 unsigned char *result;
1272 if (!f) return stbi__errpuc("can't fopen", "Unable to open file");
1273 result = stbi_load_from_file(f,x,y,comp,req_comp);
1274 fclose(f);
1275 return result;
1276}
1277
1278STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
1279{
1280 unsigned char *result;
1281 stbi__context s;
1282 stbi__start_file(&s,f);
1283 result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp);
1284 if (result) {
1285 // need to 'unget' all the characters in the IO buffer
1286 fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR);
1287 }
1288 return result;
1289}
1290
1291STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp)
1292{
1293 stbi__uint16 *result;
1294 stbi__context s;
1295 stbi__start_file(&s,f);
1296 result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp);
1297 if (result) {
1298 // need to 'unget' all the characters in the IO buffer
1299 fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR);
1300 }
1301 return result;
1302}
1303
1304STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp)
1305{
1306 FILE *f = stbi__fopen(filename, "rb");
1307 stbi__uint16 *result;
1308 if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file");
1309 result = stbi_load_from_file_16(f,x,y,comp,req_comp);
1310 fclose(f);
1311 return result;
1312}
1313
1314
1315#endif //!STBI_NO_STDIO
1316
1317STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels)
1318{
1319 stbi__context s;
1320 stbi__start_mem(&s,buffer,len);
1321 return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels);
1322}
1323
1324STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels)
1325{
1326 stbi__context s;
1327 stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user);
1328 return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels);
1329}
1330
1331STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
1332{
1333 stbi__context s;
1334 stbi__start_mem(&s,buffer,len);
1335 return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp);
1336}
1337
1338STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp)
1339{
1340 stbi__context s;
1341 stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user);
1342 return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp);
1343}
1344
1345#ifndef STBI_NO_GIF
1346STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp)
1347{
1348 unsigned char *result;
1349 stbi__context s;
1350 stbi__start_mem(&s,buffer,len);
1351
1352 result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp);
1353 if (stbi__vertically_flip_on_load) {
1354 stbi__vertical_flip_slices( result, *x, *y, *z, *comp );
1355 }
1356
1357 return result;
1358}
1359#endif
1360
1361#ifndef STBI_NO_LINEAR
1362static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp)
1363{
1364 unsigned char *data;
1365 #ifndef STBI_NO_HDR
1366 if (stbi__hdr_test(s)) {
1367 stbi__result_info ri;
1368 float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri);
1369 if (hdr_data)
1370 stbi__float_postprocess(hdr_data,x,y,comp,req_comp);
1371 return hdr_data;
1372 }
1373 #endif
1374 data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp);
1375 if (data)
1376 return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp);
1377 return stbi__errpf("unknown image type", "Image not of any known type, or corrupt");
1378}
1379
1380STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
1381{
1382 stbi__context s;
1383 stbi__start_mem(&s,buffer,len);
1384 return stbi__loadf_main(&s,x,y,comp,req_comp);
1385}
1386
1387STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp)
1388{
1389 stbi__context s;
1390 stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user);
1391 return stbi__loadf_main(&s,x,y,comp,req_comp);
1392}
1393
1394#ifndef STBI_NO_STDIO
1395STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp)
1396{
1397 float *result;
1398 FILE *f = stbi__fopen(filename, "rb");
1399 if (!f) return stbi__errpf("can't fopen", "Unable to open file");
1400 result = stbi_loadf_from_file(f,x,y,comp,req_comp);
1401 fclose(f);
1402 return result;
1403}
1404
1405STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
1406{
1407 stbi__context s;
1408 stbi__start_file(&s,f);
1409 return stbi__loadf_main(&s,x,y,comp,req_comp);
1410}
1411#endif // !STBI_NO_STDIO
1412
1413#endif // !STBI_NO_LINEAR
1414
1415// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is
1416// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always
1417// reports false!
1418
1419STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len)
1420{
1421 #ifndef STBI_NO_HDR
1422 stbi__context s;
1423 stbi__start_mem(&s,buffer,len);
1424 return stbi__hdr_test(&s);
1425 #else
1426 STBI_NOTUSED(buffer);
1427 STBI_NOTUSED(len);
1428 return 0;
1429 #endif
1430}
1431
1432#ifndef STBI_NO_STDIO
1433STBIDEF int stbi_is_hdr (char const *filename)
1434{
1435 FILE *f = stbi__fopen(filename, "rb");
1436 int result=0;
1437 if (f) {
1438 result = stbi_is_hdr_from_file(f);
1439 fclose(f);
1440 }
1441 return result;
1442}
1443
1444STBIDEF int stbi_is_hdr_from_file(FILE *f)
1445{
1446 #ifndef STBI_NO_HDR
1447 long pos = ftell(f);
1448 int res;
1449 stbi__context s;
1450 stbi__start_file(&s,f);
1451 res = stbi__hdr_test(&s);
1452 fseek(f, pos, SEEK_SET);
1453 return res;
1454 #else
1455 STBI_NOTUSED(f);
1456 return 0;
1457 #endif
1458}
1459#endif // !STBI_NO_STDIO
1460
1461STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user)
1462{
1463 #ifndef STBI_NO_HDR
1464 stbi__context s;
1465 stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user);
1466 return stbi__hdr_test(&s);
1467 #else
1468 STBI_NOTUSED(clbk);
1469 STBI_NOTUSED(user);
1470 return 0;
1471 #endif
1472}
1473
1474#ifndef STBI_NO_LINEAR
1475static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f;
1476
1477STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; }
1478STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; }
1479#endif
1480
1481static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f;
1482
1483STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; }
1484STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; }
1485
1486
1487//////////////////////////////////////////////////////////////////////////////
1488//
1489// Common code used by all image loaders
1490//
1491
1492enum
1493{
1494 STBI__SCAN_load=0,
1495 STBI__SCAN_type,
1496 STBI__SCAN_header
1497};
1498
1499static void stbi__refill_buffer(stbi__context *s)
1500{
1501 int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen);
1502 if (n == 0) {
1503 // at end of file, treat same as if from memory, but need to handle case
1504 // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file
1505 s->read_from_callbacks = 0;
1506 s->img_buffer = s->buffer_start;
1507 s->img_buffer_end = s->buffer_start+1;
1508 *s->img_buffer = 0;
1509 } else {
1510 s->img_buffer = s->buffer_start;
1511 s->img_buffer_end = s->buffer_start + n;
1512 }
1513}
1514
1515stbi_inline static stbi_uc stbi__get8(stbi__context *s)
1516{
1517 if (s->img_buffer < s->img_buffer_end)
1518 return *s->img_buffer++;
1519 if (s->read_from_callbacks) {
1520 stbi__refill_buffer(s);
1521 return *s->img_buffer++;
1522 }
1523 return 0;
1524}
1525
1526#if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM)
1527// nothing
1528#else
1529stbi_inline static int stbi__at_eof(stbi__context *s)
1530{
1531 if (s->io.read) {
1532 if (!(s->io.eof)(s->io_user_data)) return 0;
1533 // if feof() is true, check if buffer = end
1534 // special case: we've only got the special 0 character at the end
1535 if (s->read_from_callbacks == 0) return 1;
1536 }
1537
1538 return s->img_buffer >= s->img_buffer_end;
1539}
1540#endif
1541
1542#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC)
1543// nothing
1544#else
1545static void stbi__skip(stbi__context *s, int n)
1546{
1547 if (n < 0) {
1548 s->img_buffer = s->img_buffer_end;
1549 return;
1550 }
1551 if (s->io.read) {
1552 int blen = (int) (s->img_buffer_end - s->img_buffer);
1553 if (blen < n) {
1554 s->img_buffer = s->img_buffer_end;
1555 (s->io.skip)(s->io_user_data, n - blen);
1556 return;
1557 }
1558 }
1559 s->img_buffer += n;
1560}
1561#endif
1562
1563#if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM)
1564// nothing
1565#else
1566static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n)
1567{
1568 if (s->io.read) {
1569 int blen = (int) (s->img_buffer_end - s->img_buffer);
1570 if (blen < n) {
1571 int res, count;
1572
1573 memcpy(buffer, s->img_buffer, blen);
1574
1575 count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen);
1576 res = (count == (n-blen));
1577 s->img_buffer = s->img_buffer_end;
1578 return res;
1579 }
1580 }
1581
1582 if (s->img_buffer+n <= s->img_buffer_end) {
1583 memcpy(buffer, s->img_buffer, n);
1584 s->img_buffer += n;
1585 return 1;
1586 } else
1587 return 0;
1588}
1589#endif
1590
1591#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC)
1592// nothing
1593#else
1594static int stbi__get16be(stbi__context *s)
1595{
1596 int z = stbi__get8(s);
1597 return (z << 8) + stbi__get8(s);
1598}
1599#endif
1600
1601#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC)
1602// nothing
1603#else
1604static stbi__uint32 stbi__get32be(stbi__context *s)
1605{
1606 stbi__uint32 z = stbi__get16be(s);
1607 return (z << 16) + stbi__get16be(s);
1608}
1609#endif
1610
1611#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF)
1612// nothing
1613#else
1614static int stbi__get16le(stbi__context *s)
1615{
1616 int z = stbi__get8(s);
1617 return z + (stbi__get8(s) << 8);
1618}
1619#endif
1620
1621#ifndef STBI_NO_BMP
1622static stbi__uint32 stbi__get32le(stbi__context *s)
1623{
1624 stbi__uint32 z = stbi__get16le(s);
1625 return z + (stbi__get16le(s) << 16);
1626}
1627#endif
1628
1629#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings
1630
1631#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM)
1632// nothing
1633#else
1634//////////////////////////////////////////////////////////////////////////////
1635//
1636// generic converter from built-in img_n to req_comp
1637// individual types do this automatically as much as possible (e.g. jpeg
1638// does all cases internally since it needs to colorspace convert anyway,
1639// and it never has alpha, so very few cases ). png can automatically
1640// interleave an alpha=255 channel, but falls back to this for other cases
1641//
1642// assume data buffer is malloced, so malloc a new one and free that one
1643// only failure mode is malloc failing
1644
1645static stbi_uc stbi__compute_y(int r, int g, int b)
1646{
1647 return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8);
1648}
1649#endif
1650
1651#if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM)
1652// nothing
1653#else
1654static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y)
1655{
1656 int i,j;
1657 unsigned char *good;
1658
1659 if (req_comp == img_n) return data;
1660 STBI_ASSERT(req_comp >= 1 && req_comp <= 4);
1661
1662 good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0);
1663 if (good == NULL) {
1664 STBI_FREE(data);
1665 return stbi__errpuc("outofmem", "Out of memory");
1666 }
1667
1668 for (j=0; j < (int) y; ++j) {
1669 unsigned char *src = data + j * x * img_n ;
1670 unsigned char *dest = good + j * x * req_comp;
1671
1672 #define STBI__COMBO(a,b) ((a)*8+(b))
1673 #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b)
1674 // convert source image with img_n components to one with req_comp components;
1675 // avoid switch per pixel, so use switch per scanline and massive macros
1676 switch (STBI__COMBO(img_n, req_comp)) {
1677 STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break;
1678 STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break;
1679 STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break;
1680 STBI__CASE(2,1) { dest[0]=src[0]; } break;
1681 STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break;
1682 STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break;
1683 STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break;
1684 STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break;
1685 STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break;
1686 STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break;
1687 STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break;
1688 STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break;
1689 default: STBI_ASSERT(0);
1690 }
1691 #undef STBI__CASE
1692 }
1693
1694 STBI_FREE(data);
1695 return good;
1696}
1697#endif
1698
1699#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD)
1700// nothing
1701#else
1702static stbi__uint16 stbi__compute_y_16(int r, int g, int b)
1703{
1704 return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8);
1705}
1706#endif
1707
1708#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD)
1709// nothing
1710#else
1711static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y)
1712{
1713 int i,j;
1714 stbi__uint16 *good;
1715
1716 if (req_comp == img_n) return data;
1717 STBI_ASSERT(req_comp >= 1 && req_comp <= 4);
1718
1719 good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2);
1720 if (good == NULL) {
1721 STBI_FREE(data);
1722 return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory");
1723 }
1724
1725 for (j=0; j < (int) y; ++j) {
1726 stbi__uint16 *src = data + j * x * img_n ;
1727 stbi__uint16 *dest = good + j * x * req_comp;
1728
1729 #define STBI__COMBO(a,b) ((a)*8+(b))
1730 #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b)
1731 // convert source image with img_n components to one with req_comp components;
1732 // avoid switch per pixel, so use switch per scanline and massive macros
1733 switch (STBI__COMBO(img_n, req_comp)) {
1734 STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break;
1735 STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break;
1736 STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break;
1737 STBI__CASE(2,1) { dest[0]=src[0]; } break;
1738 STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break;
1739 STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break;
1740 STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break;
1741 STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break;
1742 STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break;
1743 STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break;
1744 STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break;
1745 STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break;
1746 default: STBI_ASSERT(0);
1747 }
1748 #undef STBI__CASE
1749 }
1750
1751 STBI_FREE(data);
1752 return good;
1753}
1754#endif
1755
1756#ifndef STBI_NO_LINEAR
1757static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp)
1758{
1759 int i,k,n;
1760 float *output;
1761 if (!data) return NULL;
1762 output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0);
1763 if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); }
1764 // compute number of non-alpha components
1765 if (comp & 1) n = comp; else n = comp-1;
1766 for (i=0; i < x*y; ++i) {
1767 for (k=0; k < n; ++k) {
1768 output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale);
1769 }
1770 }
1771 if (n < comp) {
1772 for (i=0; i < x*y; ++i) {
1773 output[i*comp + n] = data[i*comp + n]/255.0f;
1774 }
1775 }
1776 STBI_FREE(data);
1777 return output;
1778}
1779#endif
1780
1781#ifndef STBI_NO_HDR
1782#define stbi__float2int(x) ((int) (x))
1783static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp)
1784{
1785 int i,k,n;
1786 stbi_uc *output;
1787 if (!data) return NULL;
1788 output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0);
1789 if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); }
1790 // compute number of non-alpha components
1791 if (comp & 1) n = comp; else n = comp-1;
1792 for (i=0; i < x*y; ++i) {
1793 for (k=0; k < n; ++k) {
1794 float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f;
1795 if (z < 0) z = 0;
1796 if (z > 255) z = 255;
1797 output[i*comp + k] = (stbi_uc) stbi__float2int(z);
1798 }
1799 if (k < comp) {
1800 float z = data[i*comp+k] * 255 + 0.5f;
1801 if (z < 0) z = 0;
1802 if (z > 255) z = 255;
1803 output[i*comp + k] = (stbi_uc) stbi__float2int(z);
1804 }
1805 }
1806 STBI_FREE(data);
1807 return output;
1808}
1809#endif
1810
1811//////////////////////////////////////////////////////////////////////////////
1812//
1813// "baseline" JPEG/JFIF decoder
1814//
1815// simple implementation
1816// - doesn't support delayed output of y-dimension
1817// - simple interface (only one output format: 8-bit interleaved RGB)
1818// - doesn't try to recover corrupt jpegs
1819// - doesn't allow partial loading, loading multiple at once
1820// - still fast on x86 (copying globals into locals doesn't help x86)
1821// - allocates lots of intermediate memory (full size of all components)
1822// - non-interleaved case requires this anyway
1823// - allows good upsampling (see next)
1824// high-quality
1825// - upsampled channels are bilinearly interpolated, even across blocks
1826// - quality integer IDCT derived from IJG's 'slow'
1827// performance
1828// - fast huffman; reasonable integer IDCT
1829// - some SIMD kernels for common paths on targets with SSE2/NEON
1830// - uses a lot of intermediate memory, could cache poorly
1831
1832#ifndef STBI_NO_JPEG
1833
1834// huffman decoding acceleration
1835#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache
1836
1837typedef struct
1838{
1839 stbi_uc fast[1 << FAST_BITS];
1840 // weirdly, repacking this into AoS is a 10% speed loss, instead of a win
1841 stbi__uint16 code[256];
1842 stbi_uc values[256];
1843 stbi_uc size[257];
1844 unsigned int maxcode[18];
1845 int delta[17]; // old 'firstsymbol' - old 'firstcode'
1846} stbi__huffman;
1847
1848typedef struct
1849{
1850 stbi__context *s;
1851 stbi__huffman huff_dc[4];
1852 stbi__huffman huff_ac[4];
1853 stbi__uint16 dequant[4][64];
1854 stbi__int16 fast_ac[4][1 << FAST_BITS];
1855
1856// sizes for components, interleaved MCUs
1857 int img_h_max, img_v_max;
1858 int img_mcu_x, img_mcu_y;
1859 int img_mcu_w, img_mcu_h;
1860
1861// definition of jpeg image component
1862 struct
1863 {
1864 int id;
1865 int h,v;
1866 int tq;
1867 int hd,ha;
1868 int dc_pred;
1869
1870 int x,y,w2,h2;
1871 stbi_uc *data;
1872 void *raw_data, *raw_coeff;
1873 stbi_uc *linebuf;
1874 short *coeff; // progressive only
1875 int coeff_w, coeff_h; // number of 8x8 coefficient blocks
1876 } img_comp[4];
1877
1878 stbi__uint32 code_buffer; // jpeg entropy-coded buffer
1879 int code_bits; // number of valid bits
1880 unsigned char marker; // marker seen while filling entropy buffer
1881 int nomore; // flag if we saw a marker so must stop
1882
1883 int progressive;
1884 int spec_start;
1885 int spec_end;
1886 int succ_high;
1887 int succ_low;
1888 int eob_run;
1889 int jfif;
1890 int app14_color_transform; // Adobe APP14 tag
1891 int rgb;
1892
1893 int scan_n, order[4];
1894 int restart_interval, todo;
1895
1896// kernels
1897 void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]);
1898 void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step);
1899 stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs);
1900} stbi__jpeg;
1901
1902static int stbi__build_huffman(stbi__huffman *h, int *count)
1903{
1904 int i,j,k=0;
1905 unsigned int code;
1906 // build size list for each symbol (from JPEG spec)
1907 for (i=0; i < 16; ++i)
1908 for (j=0; j < count[i]; ++j)
1909 h->size[k++] = (stbi_uc) (i+1);
1910 h->size[k] = 0;
1911
1912 // compute actual symbols (from jpeg spec)
1913 code = 0;
1914 k = 0;
1915 for(j=1; j <= 16; ++j) {
1916 // compute delta to add to code to compute symbol id
1917 h->delta[j] = k - code;
1918 if (h->size[k] == j) {
1919 while (h->size[k] == j)
1920 h->code[k++] = (stbi__uint16) (code++);
1921 if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG");
1922 }
1923 // compute largest code + 1 for this size, preshifted as needed later
1924 h->maxcode[j] = code << (16-j);
1925 code <<= 1;
1926 }
1927 h->maxcode[j] = 0xffffffff;
1928
1929 // build non-spec acceleration table; 255 is flag for not-accelerated
1930 memset(h->fast, 255, 1 << FAST_BITS);
1931 for (i=0; i < k; ++i) {
1932 int s = h->size[i];
1933 if (s <= FAST_BITS) {
1934 int c = h->code[i] << (FAST_BITS-s);
1935 int m = 1 << (FAST_BITS-s);
1936 for (j=0; j < m; ++j) {
1937 h->fast[c+j] = (stbi_uc) i;
1938 }
1939 }
1940 }
1941 return 1;
1942}
1943
1944// build a table that decodes both magnitude and value of small ACs in
1945// one go.
1946static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h)
1947{
1948 int i;
1949 for (i=0; i < (1 << FAST_BITS); ++i) {
1950 stbi_uc fast = h->fast[i];
1951 fast_ac[i] = 0;
1952 if (fast < 255) {
1953 int rs = h->values[fast];
1954 int run = (rs >> 4) & 15;
1955 int magbits = rs & 15;
1956 int len = h->size[fast];
1957
1958 if (magbits && len + magbits <= FAST_BITS) {
1959 // magnitude code followed by receive_extend code
1960 int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits);
1961 int m = 1 << (magbits - 1);
1962 if (k < m) k += (~0U << magbits) + 1;
1963 // if the result is small enough, we can fit it in fast_ac table
1964 if (k >= -128 && k <= 127)
1965 fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits));
1966 }
1967 }
1968 }
1969}
1970
1971static void stbi__grow_buffer_unsafe(stbi__jpeg *j)
1972{
1973 do {
1974 unsigned int b = j->nomore ? 0 : stbi__get8(j->s);
1975 if (b == 0xff) {
1976 int c = stbi__get8(j->s);
1977 while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes
1978 if (c != 0) {
1979 j->marker = (unsigned char) c;
1980 j->nomore = 1;
1981 return;
1982 }
1983 }
1984 j->code_buffer |= b << (24 - j->code_bits);
1985 j->code_bits += 8;
1986 } while (j->code_bits <= 24);
1987}
1988
1989// (1 << n) - 1
1990static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535};
1991
1992// decode a jpeg huffman value from the bitstream
1993stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h)
1994{
1995 unsigned int temp;
1996 int c,k;
1997
1998 if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);
1999
2000 // look at the top FAST_BITS and determine what symbol ID it is,
2001 // if the code is <= FAST_BITS
2002 c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1);
2003 k = h->fast[c];
2004 if (k < 255) {
2005 int s = h->size[k];
2006 if (s > j->code_bits)
2007 return -1;
2008 j->code_buffer <<= s;
2009 j->code_bits -= s;
2010 return h->values[k];
2011 }
2012
2013 // naive test is to shift the code_buffer down so k bits are
2014 // valid, then test against maxcode. To speed this up, we've
2015 // preshifted maxcode left so that it has (16-k) 0s at the
2016 // end; in other words, regardless of the number of bits, it
2017 // wants to be compared against something shifted to have 16;
2018 // that way we don't need to shift inside the loop.
2019 temp = j->code_buffer >> 16;
2020 for (k=FAST_BITS+1 ; ; ++k)
2021 if (temp < h->maxcode[k])
2022 break;
2023 if (k == 17) {
2024 // error! code not found
2025 j->code_bits -= 16;
2026 return -1;
2027 }
2028
2029 if (k > j->code_bits)
2030 return -1;
2031
2032 // convert the huffman code to the symbol id
2033 c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k];
2034 STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]);
2035
2036 // convert the id to a symbol
2037 j->code_bits -= k;
2038 j->code_buffer <<= k;
2039 return h->values[c];
2040}
2041
2042// bias[n] = (-1<<n) + 1
2043static const int stbi__jbias[16] = {0,-1,-3,-7,-15,-31,-63,-127,-255,-511,-1023,-2047,-4095,-8191,-16383,-32767};
2044
2045// combined JPEG 'receive' and JPEG 'extend', since baseline
2046// always extends everything it receives.
2047stbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n)
2048{
2049 unsigned int k;
2050 int sgn;
2051 if (j->code_bits < n) stbi__grow_buffer_unsafe(j);
2052
2053 sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB
2054 k = stbi_lrot(j->code_buffer, n);
2055 STBI_ASSERT(n >= 0 && n < (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask)));
2056 j->code_buffer = k & ~stbi__bmask[n];
2057 k &= stbi__bmask[n];
2058 j->code_bits -= n;
2059 return k + (stbi__jbias[n] & ~sgn);
2060}
2061
2062// get some unsigned bits
2063stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n)
2064{
2065 unsigned int k;
2066 if (j->code_bits < n) stbi__grow_buffer_unsafe(j);
2067 k = stbi_lrot(j->code_buffer, n);
2068 j->code_buffer = k & ~stbi__bmask[n];
2069 k &= stbi__bmask[n];
2070 j->code_bits -= n;
2071 return k;
2072}
2073
2074stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j)
2075{
2076 unsigned int k;
2077 if (j->code_bits < 1) stbi__grow_buffer_unsafe(j);
2078 k = j->code_buffer;
2079 j->code_buffer <<= 1;
2080 --j->code_bits;
2081 return k & 0x80000000;
2082}
2083
2084// given a value that's at position X in the zigzag stream,
2085// where does it appear in the 8x8 matrix coded as row-major?
2086static const stbi_uc stbi__jpeg_dezigzag[64+15] =
2087{
2088 0, 1, 8, 16, 9, 2, 3, 10,
2089 17, 24, 32, 25, 18, 11, 4, 5,
2090 12, 19, 26, 33, 40, 48, 41, 34,
2091 27, 20, 13, 6, 7, 14, 21, 28,
2092 35, 42, 49, 56, 57, 50, 43, 36,
2093 29, 22, 15, 23, 30, 37, 44, 51,
2094 58, 59, 52, 45, 38, 31, 39, 46,
2095 53, 60, 61, 54, 47, 55, 62, 63,
2096 // let corrupt input sample past end
2097 63, 63, 63, 63, 63, 63, 63, 63,
2098 63, 63, 63, 63, 63, 63, 63
2099};
2100
2101// decode one 64-entry block--
2102static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant)
2103{
2104 int diff,dc,k;
2105 int t;
2106
2107 if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);
2108 t = stbi__jpeg_huff_decode(j, hdc);
2109 if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG");
2110
2111 // 0 all the ac values now so we can do it 32-bits at a time
2112 memset(data,0,64*sizeof(data[0]));
2113
2114 diff = t ? stbi__extend_receive(j, t) : 0;
2115 dc = j->img_comp[b].dc_pred + diff;
2116 j->img_comp[b].dc_pred = dc;
2117 data[0] = (short) (dc * dequant[0]);
2118
2119 // decode AC components, see JPEG spec
2120 k = 1;
2121 do {
2122 unsigned int zig;
2123 int c,r,s;
2124 if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);
2125 c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1);
2126 r = fac[c];
2127 if (r) { // fast-AC path
2128 k += (r >> 4) & 15; // run
2129 s = r & 15; // combined length
2130 j->code_buffer <<= s;
2131 j->code_bits -= s;
2132 // decode into unzigzag'd location
2133 zig = stbi__jpeg_dezigzag[k++];
2134 data[zig] = (short) ((r >> 8) * dequant[zig]);
2135 } else {
2136 int rs = stbi__jpeg_huff_decode(j, hac);
2137 if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG");
2138 s = rs & 15;
2139 r = rs >> 4;
2140 if (s == 0) {
2141 if (rs != 0xf0) break; // end block
2142 k += 16;
2143 } else {
2144 k += r;
2145 // decode into unzigzag'd location
2146 zig = stbi__jpeg_dezigzag[k++];
2147 data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]);
2148 }
2149 }
2150 } while (k < 64);
2151 return 1;
2152}
2153
2154static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b)
2155{
2156 int diff,dc;
2157 int t;
2158 if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
2159
2160 if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);
2161
2162 if (j->succ_high == 0) {
2163 // first scan for DC coefficient, must be first
2164 memset(data,0,64*sizeof(data[0])); // 0 all the ac values now
2165 t = stbi__jpeg_huff_decode(j, hdc);
2166 diff = t ? stbi__extend_receive(j, t) : 0;
2167
2168 dc = j->img_comp[b].dc_pred + diff;
2169 j->img_comp[b].dc_pred = dc;
2170 data[0] = (short) (dc << j->succ_low);
2171 } else {
2172 // refinement scan for DC coefficient
2173 if (stbi__jpeg_get_bit(j))
2174 data[0] += (short) (1 << j->succ_low);
2175 }
2176 return 1;
2177}
2178
2179// @OPTIMIZE: store non-zigzagged during the decode passes,
2180// and only de-zigzag when dequantizing
2181static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac)
2182{
2183 int k;
2184 if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
2185
2186 if (j->succ_high == 0) {
2187 int shift = j->succ_low;
2188
2189 if (j->eob_run) {
2190 --j->eob_run;
2191 return 1;
2192 }
2193
2194 k = j->spec_start;
2195 do {
2196 unsigned int zig;
2197 int c,r,s;
2198 if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);
2199 c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1);
2200 r = fac[c];
2201 if (r) { // fast-AC path
2202 k += (r >> 4) & 15; // run
2203 s = r & 15; // combined length
2204 j->code_buffer <<= s;
2205 j->code_bits -= s;
2206 zig = stbi__jpeg_dezigzag[k++];
2207 data[zig] = (short) ((r >> 8) << shift);
2208 } else {
2209 int rs = stbi__jpeg_huff_decode(j, hac);
2210 if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG");
2211 s = rs & 15;
2212 r = rs >> 4;
2213 if (s == 0) {
2214 if (r < 15) {
2215 j->eob_run = (1 << r);
2216 if (r)
2217 j->eob_run += stbi__jpeg_get_bits(j, r);
2218 --j->eob_run;
2219 break;
2220 }
2221 k += 16;
2222 } else {
2223 k += r;
2224 zig = stbi__jpeg_dezigzag[k++];
2225 data[zig] = (short) (stbi__extend_receive(j,s) << shift);
2226 }
2227 }
2228 } while (k <= j->spec_end);
2229 } else {
2230 // refinement scan for these AC coefficients
2231
2232 short bit = (short) (1 << j->succ_low);
2233
2234 if (j->eob_run) {
2235 --j->eob_run;
2236 for (k = j->spec_start; k <= j->spec_end; ++k) {
2237 short *p = &data[stbi__jpeg_dezigzag[k]];
2238 if (*p != 0)
2239 if (stbi__jpeg_get_bit(j))
2240 if ((*p & bit)==0) {
2241 if (*p > 0)
2242 *p += bit;
2243 else
2244 *p -= bit;
2245 }
2246 }
2247 } else {
2248 k = j->spec_start;
2249 do {
2250 int r,s;
2251 int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh
2252 if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG");
2253 s = rs & 15;
2254 r = rs >> 4;
2255 if (s == 0) {
2256 if (r < 15) {
2257 j->eob_run = (1 << r) - 1;
2258 if (r)
2259 j->eob_run += stbi__jpeg_get_bits(j, r);
2260 r = 64; // force end of block
2261 } else {
2262 // r=15 s=0 should write 16 0s, so we just do
2263 // a run of 15 0s and then write s (which is 0),
2264 // so we don't have to do anything special here
2265 }
2266 } else {
2267 if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG");
2268 // sign bit
2269 if (stbi__jpeg_get_bit(j))
2270 s = bit;
2271 else
2272 s = -bit;
2273 }
2274
2275 // advance by r
2276 while (k <= j->spec_end) {
2277 short *p = &data[stbi__jpeg_dezigzag[k++]];
2278 if (*p != 0) {
2279 if (stbi__jpeg_get_bit(j))
2280 if ((*p & bit)==0) {
2281 if (*p > 0)
2282 *p += bit;
2283 else
2284 *p -= bit;
2285 }
2286 } else {
2287 if (r == 0) {
2288 *p = (short) s;
2289 break;
2290 }
2291 --r;
2292 }
2293 }
2294 } while (k <= j->spec_end);
2295 }
2296 }
2297 return 1;
2298}
2299
2300// take a -128..127 value and stbi__clamp it and convert to 0..255
2301stbi_inline static stbi_uc stbi__clamp(int x)
2302{
2303 // trick to use a single test to catch both cases
2304 if ((unsigned int) x > 255) {
2305 if (x < 0) return 0;
2306 if (x > 255) return 255;
2307 }
2308 return (stbi_uc) x;
2309}
2310
2311#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5)))
2312#define stbi__fsh(x) ((x) * 4096)
2313
2314// derived from jidctint -- DCT_ISLOW
2315#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \
2316 int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \
2317 p2 = s2; \
2318 p3 = s6; \
2319 p1 = (p2+p3) * stbi__f2f(0.5411961f); \
2320 t2 = p1 + p3*stbi__f2f(-1.847759065f); \
2321 t3 = p1 + p2*stbi__f2f( 0.765366865f); \
2322 p2 = s0; \
2323 p3 = s4; \
2324 t0 = stbi__fsh(p2+p3); \
2325 t1 = stbi__fsh(p2-p3); \
2326 x0 = t0+t3; \
2327 x3 = t0-t3; \
2328 x1 = t1+t2; \
2329 x2 = t1-t2; \
2330 t0 = s7; \
2331 t1 = s5; \
2332 t2 = s3; \
2333 t3 = s1; \
2334 p3 = t0+t2; \
2335 p4 = t1+t3; \
2336 p1 = t0+t3; \
2337 p2 = t1+t2; \
2338 p5 = (p3+p4)*stbi__f2f( 1.175875602f); \
2339 t0 = t0*stbi__f2f( 0.298631336f); \
2340 t1 = t1*stbi__f2f( 2.053119869f); \
2341 t2 = t2*stbi__f2f( 3.072711026f); \
2342 t3 = t3*stbi__f2f( 1.501321110f); \
2343 p1 = p5 + p1*stbi__f2f(-0.899976223f); \
2344 p2 = p5 + p2*stbi__f2f(-2.562915447f); \
2345 p3 = p3*stbi__f2f(-1.961570560f); \
2346 p4 = p4*stbi__f2f(-0.390180644f); \
2347 t3 += p1+p4; \
2348 t2 += p2+p3; \
2349 t1 += p2+p4; \
2350 t0 += p1+p3;
2351
2352static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64])
2353{
2354 int i,val[64],*v=val;
2355 stbi_uc *o;
2356 short *d = data;
2357
2358 // columns
2359 for (i=0; i < 8; ++i,++d, ++v) {
2360 // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing
2361 if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0
2362 && d[40]==0 && d[48]==0 && d[56]==0) {
2363 // no shortcut 0 seconds
2364 // (1|2|3|4|5|6|7)==0 0 seconds
2365 // all separate -0.047 seconds
2366 // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds
2367 int dcterm = d[0]*4;
2368 v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm;
2369 } else {
2370 STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56])
2371 // constants scaled things up by 1<<12; let's bring them back
2372 // down, but keep 2 extra bits of precision
2373 x0 += 512; x1 += 512; x2 += 512; x3 += 512;
2374 v[ 0] = (x0+t3) >> 10;
2375 v[56] = (x0-t3) >> 10;
2376 v[ 8] = (x1+t2) >> 10;
2377 v[48] = (x1-t2) >> 10;
2378 v[16] = (x2+t1) >> 10;
2379 v[40] = (x2-t1) >> 10;
2380 v[24] = (x3+t0) >> 10;
2381 v[32] = (x3-t0) >> 10;
2382 }
2383 }
2384
2385 for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) {
2386 // no fast case since the first 1D IDCT spread components out
2387 STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7])
2388 // constants scaled things up by 1<<12, plus we had 1<<2 from first
2389 // loop, plus horizontal and vertical each scale by sqrt(8) so together
2390 // we've got an extra 1<<3, so 1<<17 total we need to remove.
2391 // so we want to round that, which means adding 0.5 * 1<<17,
2392 // aka 65536. Also, we'll end up with -128 to 127 that we want
2393 // to encode as 0..255 by adding 128, so we'll add that before the shift
2394 x0 += 65536 + (128<<17);
2395 x1 += 65536 + (128<<17);
2396 x2 += 65536 + (128<<17);
2397 x3 += 65536 + (128<<17);
2398 // tried computing the shifts into temps, or'ing the temps to see
2399 // if any were out of range, but that was slower
2400 o[0] = stbi__clamp((x0+t3) >> 17);
2401 o[7] = stbi__clamp((x0-t3) >> 17);
2402 o[1] = stbi__clamp((x1+t2) >> 17);
2403 o[6] = stbi__clamp((x1-t2) >> 17);
2404 o[2] = stbi__clamp((x2+t1) >> 17);
2405 o[5] = stbi__clamp((x2-t1) >> 17);
2406 o[3] = stbi__clamp((x3+t0) >> 17);
2407 o[4] = stbi__clamp((x3-t0) >> 17);
2408 }
2409}
2410
2411#ifdef STBI_SSE2
2412// sse2 integer IDCT. not the fastest possible implementation but it
2413// produces bit-identical results to the generic C version so it's
2414// fully "transparent".
2415static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64])
2416{
2417 // This is constructed to match our regular (generic) integer IDCT exactly.
2418 __m128i row0, row1, row2, row3, row4, row5, row6, row7;
2419 __m128i tmp;
2420
2421 // dot product constant: even elems=x, odd elems=y
2422 #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y))
2423
2424 // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit)
2425 // out(1) = c1[even]*x + c1[odd]*y
2426 #define dct_rot(out0,out1, x,y,c0,c1) \
2427 __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \
2428 __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \
2429 __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \
2430 __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \
2431 __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \
2432 __m128i out1##_h = _mm_madd_epi16(c0##hi, c1)
2433
2434 // out = in << 12 (in 16-bit, out 32-bit)
2435 #define dct_widen(out, in) \
2436 __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \
2437 __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4)
2438
2439 // wide add
2440 #define dct_wadd(out, a, b) \
2441 __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \
2442 __m128i out##_h = _mm_add_epi32(a##_h, b##_h)
2443
2444 // wide sub
2445 #define dct_wsub(out, a, b) \
2446 __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \
2447 __m128i out##_h = _mm_sub_epi32(a##_h, b##_h)
2448
2449 // butterfly a/b, add bias, then shift by "s" and pack
2450 #define dct_bfly32o(out0, out1, a,b,bias,s) \
2451 { \
2452 __m128i abiased_l = _mm_add_epi32(a##_l, bias); \
2453 __m128i abiased_h = _mm_add_epi32(a##_h, bias); \
2454 dct_wadd(sum, abiased, b); \
2455 dct_wsub(dif, abiased, b); \
2456 out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \
2457 out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \
2458 }
2459
2460 // 8-bit interleave step (for transposes)
2461 #define dct_interleave8(a, b) \
2462 tmp = a; \
2463 a = _mm_unpacklo_epi8(a, b); \
2464 b = _mm_unpackhi_epi8(tmp, b)
2465
2466 // 16-bit interleave step (for transposes)
2467 #define dct_interleave16(a, b) \
2468 tmp = a; \
2469 a = _mm_unpacklo_epi16(a, b); \
2470 b = _mm_unpackhi_epi16(tmp, b)
2471
2472 #define dct_pass(bias,shift) \
2473 { \
2474 /* even part */ \
2475 dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \
2476 __m128i sum04 = _mm_add_epi16(row0, row4); \
2477 __m128i dif04 = _mm_sub_epi16(row0, row4); \
2478 dct_widen(t0e, sum04); \
2479 dct_widen(t1e, dif04); \
2480 dct_wadd(x0, t0e, t3e); \
2481 dct_wsub(x3, t0e, t3e); \
2482 dct_wadd(x1, t1e, t2e); \
2483 dct_wsub(x2, t1e, t2e); \
2484 /* odd part */ \
2485 dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \
2486 dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \
2487 __m128i sum17 = _mm_add_epi16(row1, row7); \
2488 __m128i sum35 = _mm_add_epi16(row3, row5); \
2489 dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \
2490 dct_wadd(x4, y0o, y4o); \
2491 dct_wadd(x5, y1o, y5o); \
2492 dct_wadd(x6, y2o, y5o); \
2493 dct_wadd(x7, y3o, y4o); \
2494 dct_bfly32o(row0,row7, x0,x7,bias,shift); \
2495 dct_bfly32o(row1,row6, x1,x6,bias,shift); \
2496 dct_bfly32o(row2,row5, x2,x5,bias,shift); \
2497 dct_bfly32o(row3,row4, x3,x4,bias,shift); \
2498 }
2499
2500 __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f));
2501 __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f));
2502 __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f));
2503 __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f));
2504 __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f));
2505 __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f));
2506 __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f));
2507 __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f));
2508
2509 // rounding biases in column/row passes, see stbi__idct_block for explanation.
2510 __m128i bias_0 = _mm_set1_epi32(512);
2511 __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17));
2512
2513 // load
2514 row0 = _mm_load_si128((const __m128i *) (data + 0*8));
2515 row1 = _mm_load_si128((const __m128i *) (data + 1*8));
2516 row2 = _mm_load_si128((const __m128i *) (data + 2*8));
2517 row3 = _mm_load_si128((const __m128i *) (data + 3*8));
2518 row4 = _mm_load_si128((const __m128i *) (data + 4*8));
2519 row5 = _mm_load_si128((const __m128i *) (data + 5*8));
2520 row6 = _mm_load_si128((const __m128i *) (data + 6*8));
2521 row7 = _mm_load_si128((const __m128i *) (data + 7*8));
2522
2523 // column pass
2524 dct_pass(bias_0, 10);
2525
2526 {
2527 // 16bit 8x8 transpose pass 1
2528 dct_interleave16(row0, row4);
2529 dct_interleave16(row1, row5);
2530 dct_interleave16(row2, row6);
2531 dct_interleave16(row3, row7);
2532
2533 // transpose pass 2
2534 dct_interleave16(row0, row2);
2535 dct_interleave16(row1, row3);
2536 dct_interleave16(row4, row6);
2537 dct_interleave16(row5, row7);
2538
2539 // transpose pass 3
2540 dct_interleave16(row0, row1);
2541 dct_interleave16(row2, row3);
2542 dct_interleave16(row4, row5);
2543 dct_interleave16(row6, row7);
2544 }
2545
2546 // row pass
2547 dct_pass(bias_1, 17);
2548
2549 {
2550 // pack
2551 __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7
2552 __m128i p1 = _mm_packus_epi16(row2, row3);
2553 __m128i p2 = _mm_packus_epi16(row4, row5);
2554 __m128i p3 = _mm_packus_epi16(row6, row7);
2555
2556 // 8bit 8x8 transpose pass 1
2557 dct_interleave8(p0, p2); // a0e0a1e1...
2558 dct_interleave8(p1, p3); // c0g0c1g1...
2559
2560 // transpose pass 2
2561 dct_interleave8(p0, p1); // a0c0e0g0...
2562 dct_interleave8(p2, p3); // b0d0f0h0...
2563
2564 // transpose pass 3
2565 dct_interleave8(p0, p2); // a0b0c0d0...
2566 dct_interleave8(p1, p3); // a4b4c4d4...
2567
2568 // store
2569 _mm_storel_epi64((__m128i *) out, p0); out += out_stride;
2570 _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride;
2571 _mm_storel_epi64((__m128i *) out, p2); out += out_stride;
2572 _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride;
2573 _mm_storel_epi64((__m128i *) out, p1); out += out_stride;
2574 _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride;
2575 _mm_storel_epi64((__m128i *) out, p3); out += out_stride;
2576 _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e));
2577 }
2578
2579#undef dct_const
2580#undef dct_rot
2581#undef dct_widen
2582#undef dct_wadd
2583#undef dct_wsub
2584#undef dct_bfly32o
2585#undef dct_interleave8
2586#undef dct_interleave16
2587#undef dct_pass
2588}
2589
2590#endif // STBI_SSE2
2591
2592#ifdef STBI_NEON
2593
2594// NEON integer IDCT. should produce bit-identical
2595// results to the generic C version.
2596static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64])
2597{
2598 int16x8_t row0, row1, row2, row3, row4, row5, row6, row7;
2599
2600 int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f));
2601 int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f));
2602 int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f));
2603 int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f));
2604 int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f));
2605 int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f));
2606 int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f));
2607 int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f));
2608 int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f));
2609 int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f));
2610 int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f));
2611 int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f));
2612
2613#define dct_long_mul(out, inq, coeff) \
2614 int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \
2615 int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff)
2616
2617#define dct_long_mac(out, acc, inq, coeff) \
2618 int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \
2619 int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff)
2620
2621#define dct_widen(out, inq) \
2622 int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \
2623 int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12)
2624
2625// wide add
2626#define dct_wadd(out, a, b) \
2627 int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \
2628 int32x4_t out##_h = vaddq_s32(a##_h, b##_h)
2629
2630// wide sub
2631#define dct_wsub(out, a, b) \
2632 int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \
2633 int32x4_t out##_h = vsubq_s32(a##_h, b##_h)
2634
2635// butterfly a/b, then shift using "shiftop" by "s" and pack
2636#define dct_bfly32o(out0,out1, a,b,shiftop,s) \
2637 { \
2638 dct_wadd(sum, a, b); \
2639 dct_wsub(dif, a, b); \
2640 out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \
2641 out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \
2642 }
2643
2644#define dct_pass(shiftop, shift) \
2645 { \
2646 /* even part */ \
2647 int16x8_t sum26 = vaddq_s16(row2, row6); \
2648 dct_long_mul(p1e, sum26, rot0_0); \
2649 dct_long_mac(t2e, p1e, row6, rot0_1); \
2650 dct_long_mac(t3e, p1e, row2, rot0_2); \
2651 int16x8_t sum04 = vaddq_s16(row0, row4); \
2652 int16x8_t dif04 = vsubq_s16(row0, row4); \
2653 dct_widen(t0e, sum04); \
2654 dct_widen(t1e, dif04); \
2655 dct_wadd(x0, t0e, t3e); \
2656 dct_wsub(x3, t0e, t3e); \
2657 dct_wadd(x1, t1e, t2e); \
2658 dct_wsub(x2, t1e, t2e); \
2659 /* odd part */ \
2660 int16x8_t sum15 = vaddq_s16(row1, row5); \
2661 int16x8_t sum17 = vaddq_s16(row1, row7); \
2662 int16x8_t sum35 = vaddq_s16(row3, row5); \
2663 int16x8_t sum37 = vaddq_s16(row3, row7); \
2664 int16x8_t sumodd = vaddq_s16(sum17, sum35); \
2665 dct_long_mul(p5o, sumodd, rot1_0); \
2666 dct_long_mac(p1o, p5o, sum17, rot1_1); \
2667 dct_long_mac(p2o, p5o, sum35, rot1_2); \
2668 dct_long_mul(p3o, sum37, rot2_0); \
2669 dct_long_mul(p4o, sum15, rot2_1); \
2670 dct_wadd(sump13o, p1o, p3o); \
2671 dct_wadd(sump24o, p2o, p4o); \
2672 dct_wadd(sump23o, p2o, p3o); \
2673 dct_wadd(sump14o, p1o, p4o); \
2674 dct_long_mac(x4, sump13o, row7, rot3_0); \
2675 dct_long_mac(x5, sump24o, row5, rot3_1); \
2676 dct_long_mac(x6, sump23o, row3, rot3_2); \
2677 dct_long_mac(x7, sump14o, row1, rot3_3); \
2678 dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \
2679 dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \
2680 dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \
2681 dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \
2682 }
2683
2684 // load
2685 row0 = vld1q_s16(data + 0*8);
2686 row1 = vld1q_s16(data + 1*8);
2687 row2 = vld1q_s16(data + 2*8);
2688 row3 = vld1q_s16(data + 3*8);
2689 row4 = vld1q_s16(data + 4*8);
2690 row5 = vld1q_s16(data + 5*8);
2691 row6 = vld1q_s16(data + 6*8);
2692 row7 = vld1q_s16(data + 7*8);
2693
2694 // add DC bias
2695 row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0));
2696
2697 // column pass
2698 dct_pass(vrshrn_n_s32, 10);
2699
2700 // 16bit 8x8 transpose
2701 {
2702// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively.
2703// whether compilers actually get this is another story, sadly.
2704#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; }
2705#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); }
2706#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); }
2707
2708 // pass 1
2709 dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6
2710 dct_trn16(row2, row3);
2711 dct_trn16(row4, row5);
2712 dct_trn16(row6, row7);
2713
2714 // pass 2
2715 dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4
2716 dct_trn32(row1, row3);
2717 dct_trn32(row4, row6);
2718 dct_trn32(row5, row7);
2719
2720 // pass 3
2721 dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0
2722 dct_trn64(row1, row5);
2723 dct_trn64(row2, row6);
2724 dct_trn64(row3, row7);
2725
2726#undef dct_trn16
2727#undef dct_trn32
2728#undef dct_trn64
2729 }
2730
2731 // row pass
2732 // vrshrn_n_s32 only supports shifts up to 16, we need
2733 // 17. so do a non-rounding shift of 16 first then follow
2734 // up with a rounding shift by 1.
2735 dct_pass(vshrn_n_s32, 16);
2736
2737 {
2738 // pack and round
2739 uint8x8_t p0 = vqrshrun_n_s16(row0, 1);
2740 uint8x8_t p1 = vqrshrun_n_s16(row1, 1);
2741 uint8x8_t p2 = vqrshrun_n_s16(row2, 1);
2742 uint8x8_t p3 = vqrshrun_n_s16(row3, 1);
2743 uint8x8_t p4 = vqrshrun_n_s16(row4, 1);
2744 uint8x8_t p5 = vqrshrun_n_s16(row5, 1);
2745 uint8x8_t p6 = vqrshrun_n_s16(row6, 1);
2746 uint8x8_t p7 = vqrshrun_n_s16(row7, 1);
2747
2748 // again, these can translate into one instruction, but often don't.
2749#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; }
2750#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); }
2751#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); }
2752
2753 // sadly can't use interleaved stores here since we only write
2754 // 8 bytes to each scan line!
2755
2756 // 8x8 8-bit transpose pass 1
2757 dct_trn8_8(p0, p1);
2758 dct_trn8_8(p2, p3);
2759 dct_trn8_8(p4, p5);
2760 dct_trn8_8(p6, p7);
2761
2762 // pass 2
2763 dct_trn8_16(p0, p2);
2764 dct_trn8_16(p1, p3);
2765 dct_trn8_16(p4, p6);
2766 dct_trn8_16(p5, p7);
2767
2768 // pass 3
2769 dct_trn8_32(p0, p4);
2770 dct_trn8_32(p1, p5);
2771 dct_trn8_32(p2, p6);
2772 dct_trn8_32(p3, p7);
2773
2774 // store
2775 vst1_u8(out, p0); out += out_stride;
2776 vst1_u8(out, p1); out += out_stride;
2777 vst1_u8(out, p2); out += out_stride;
2778 vst1_u8(out, p3); out += out_stride;
2779 vst1_u8(out, p4); out += out_stride;
2780 vst1_u8(out, p5); out += out_stride;
2781 vst1_u8(out, p6); out += out_stride;
2782 vst1_u8(out, p7);
2783
2784#undef dct_trn8_8
2785#undef dct_trn8_16
2786#undef dct_trn8_32
2787 }
2788
2789#undef dct_long_mul
2790#undef dct_long_mac
2791#undef dct_widen
2792#undef dct_wadd
2793#undef dct_wsub
2794#undef dct_bfly32o
2795#undef dct_pass
2796}
2797
2798#endif // STBI_NEON
2799
2800#define STBI__MARKER_none 0xff
2801// if there's a pending marker from the entropy stream, return that
2802// otherwise, fetch from the stream and get a marker. if there's no
2803// marker, return 0xff, which is never a valid marker value
2804static stbi_uc stbi__get_marker(stbi__jpeg *j)
2805{
2806 stbi_uc x;
2807 if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; }
2808 x = stbi__get8(j->s);
2809 if (x != 0xff) return STBI__MARKER_none;
2810 while (x == 0xff)
2811 x = stbi__get8(j->s); // consume repeated 0xff fill bytes
2812 return x;
2813}
2814
2815// in each scan, we'll have scan_n components, and the order
2816// of the components is specified by order[]
2817#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7)
2818
2819// after a restart interval, stbi__jpeg_reset the entropy decoder and
2820// the dc prediction
2821static void stbi__jpeg_reset(stbi__jpeg *j)
2822{
2823 j->code_bits = 0;
2824 j->code_buffer = 0;
2825 j->nomore = 0;
2826 j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0;
2827 j->marker = STBI__MARKER_none;
2828 j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff;
2829 j->eob_run = 0;
2830 // no more than 1<<31 MCUs if no restart_interal? that's plenty safe,
2831 // since we don't even allow 1<<30 pixels
2832}
2833
2834static int stbi__parse_entropy_coded_data(stbi__jpeg *z)
2835{
2836 stbi__jpeg_reset(z);
2837 if (!z->progressive) {
2838 if (z->scan_n == 1) {
2839 int i,j;
2840 STBI_SIMD_ALIGN(short, data[64]);
2841 int n = z->order[0];
2842 // non-interleaved data, we just need to process one block at a time,
2843 // in trivial scanline order
2844 // number of blocks to do just depends on how many actual "pixels" this
2845 // component has, independent of interleaved MCU blocking and such
2846 int w = (z->img_comp[n].x+7) >> 3;
2847 int h = (z->img_comp[n].y+7) >> 3;
2848 for (j=0; j < h; ++j) {
2849 for (i=0; i < w; ++i) {
2850 int ha = z->img_comp[n].ha;
2851 if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0;
2852 z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data);
2853 // every data block is an MCU, so countdown the restart interval
2854 if (--z->todo <= 0) {
2855 if (z->code_bits < 24) stbi__grow_buffer_unsafe(z);
2856 // if it's NOT a restart, then just bail, so we get corrupt data
2857 // rather than no data
2858 if (!STBI__RESTART(z->marker)) return 1;
2859 stbi__jpeg_reset(z);
2860 }
2861 }
2862 }
2863 return 1;
2864 } else { // interleaved
2865 int i,j,k,x,y;
2866 STBI_SIMD_ALIGN(short, data[64]);
2867 for (j=0; j < z->img_mcu_y; ++j) {
2868 for (i=0; i < z->img_mcu_x; ++i) {
2869 // scan an interleaved mcu... process scan_n components in order
2870 for (k=0; k < z->scan_n; ++k) {
2871 int n = z->order[k];
2872 // scan out an mcu's worth of this component; that's just determined
2873 // by the basic H and V specified for the component
2874 for (y=0; y < z->img_comp[n].v; ++y) {
2875 for (x=0; x < z->img_comp[n].h; ++x) {
2876 int x2 = (i*z->img_comp[n].h + x)*8;
2877 int y2 = (j*z->img_comp[n].v + y)*8;
2878 int ha = z->img_comp[n].ha;
2879 if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0;
2880 z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data);
2881 }
2882 }
2883 }
2884 // after all interleaved components, that's an interleaved MCU,
2885 // so now count down the restart interval
2886 if (--z->todo <= 0) {
2887 if (z->code_bits < 24) stbi__grow_buffer_unsafe(z);
2888 if (!STBI__RESTART(z->marker)) return 1;
2889 stbi__jpeg_reset(z);
2890 }
2891 }
2892 }
2893 return 1;
2894 }
2895 } else {
2896 if (z->scan_n == 1) {
2897 int i,j;
2898 int n = z->order[0];
2899 // non-interleaved data, we just need to process one block at a time,
2900 // in trivial scanline order
2901 // number of blocks to do just depends on how many actual "pixels" this
2902 // component has, independent of interleaved MCU blocking and such
2903 int w = (z->img_comp[n].x+7) >> 3;
2904 int h = (z->img_comp[n].y+7) >> 3;
2905 for (j=0; j < h; ++j) {
2906 for (i=0; i < w; ++i) {
2907 short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w);
2908 if (z->spec_start == 0) {
2909 if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n))
2910 return 0;
2911 } else {
2912 int ha = z->img_comp[n].ha;
2913 if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha]))
2914 return 0;
2915 }
2916 // every data block is an MCU, so countdown the restart interval
2917 if (--z->todo <= 0) {
2918 if (z->code_bits < 24) stbi__grow_buffer_unsafe(z);
2919 if (!STBI__RESTART(z->marker)) return 1;
2920 stbi__jpeg_reset(z);
2921 }
2922 }
2923 }
2924 return 1;
2925 } else { // interleaved
2926 int i,j,k,x,y;
2927 for (j=0; j < z->img_mcu_y; ++j) {
2928 for (i=0; i < z->img_mcu_x; ++i) {
2929 // scan an interleaved mcu... process scan_n components in order
2930 for (k=0; k < z->scan_n; ++k) {
2931 int n = z->order[k];
2932 // scan out an mcu's worth of this component; that's just determined
2933 // by the basic H and V specified for the component
2934 for (y=0; y < z->img_comp[n].v; ++y) {
2935 for (x=0; x < z->img_comp[n].h; ++x) {
2936 int x2 = (i*z->img_comp[n].h + x);
2937 int y2 = (j*z->img_comp[n].v + y);
2938 short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w);
2939 if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n))
2940 return 0;
2941 }
2942 }
2943 }
2944 // after all interleaved components, that's an interleaved MCU,
2945 // so now count down the restart interval
2946 if (--z->todo <= 0) {
2947 if (z->code_bits < 24) stbi__grow_buffer_unsafe(z);
2948 if (!STBI__RESTART(z->marker)) return 1;
2949 stbi__jpeg_reset(z);
2950 }
2951 }
2952 }
2953 return 1;
2954 }
2955 }
2956}
2957
2958static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant)
2959{
2960 int i;
2961 for (i=0; i < 64; ++i)
2962 data[i] *= dequant[i];
2963}
2964
2965static void stbi__jpeg_finish(stbi__jpeg *z)
2966{
2967 if (z->progressive) {
2968 // dequantize and idct the data
2969 int i,j,n;
2970 for (n=0; n < z->s->img_n; ++n) {
2971 int w = (z->img_comp[n].x+7) >> 3;
2972 int h = (z->img_comp[n].y+7) >> 3;
2973 for (j=0; j < h; ++j) {
2974 for (i=0; i < w; ++i) {
2975 short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w);
2976 stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]);
2977 z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data);
2978 }
2979 }
2980 }
2981 }
2982}
2983
2984static int stbi__process_marker(stbi__jpeg *z, int m)
2985{
2986 int L;
2987 switch (m) {
2988 case STBI__MARKER_none: // no marker found
2989 return stbi__err("expected marker","Corrupt JPEG");
2990
2991 case 0xDD: // DRI - specify restart interval
2992 if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG");
2993 z->restart_interval = stbi__get16be(z->s);
2994 return 1;
2995
2996 case 0xDB: // DQT - define quantization table
2997 L = stbi__get16be(z->s)-2;
2998 while (L > 0) {
2999 int q = stbi__get8(z->s);
3000 int p = q >> 4, sixteen = (p != 0);
3001 int t = q & 15,i;
3002 if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG");
3003 if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG");
3004
3005 for (i=0; i < 64; ++i)
3006 z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s));
3007 L -= (sixteen ? 129 : 65);
3008 }
3009 return L==0;
3010
3011 case 0xC4: // DHT - define huffman table
3012 L = stbi__get16be(z->s)-2;
3013 while (L > 0) {
3014 stbi_uc *v;
3015 int sizes[16],i,n=0;
3016 int q = stbi__get8(z->s);
3017 int tc = q >> 4;
3018 int th = q & 15;
3019 if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG");
3020 for (i=0; i < 16; ++i) {
3021 sizes[i] = stbi__get8(z->s);
3022 n += sizes[i];
3023 }
3024 L -= 17;
3025 if (tc == 0) {
3026 if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0;
3027 v = z->huff_dc[th].values;
3028 } else {
3029 if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0;
3030 v = z->huff_ac[th].values;
3031 }
3032 for (i=0; i < n; ++i)
3033 v[i] = stbi__get8(z->s);
3034 if (tc != 0)
3035 stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th);
3036 L -= n;
3037 }
3038 return L==0;
3039 }
3040
3041 // check for comment block or APP blocks
3042 if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) {
3043 L = stbi__get16be(z->s);
3044 if (L < 2) {
3045 if (m == 0xFE)
3046 return stbi__err("bad COM len","Corrupt JPEG");
3047 else
3048 return stbi__err("bad APP len","Corrupt JPEG");
3049 }
3050 L -= 2;
3051
3052 if (m == 0xE0 && L >= 5) { // JFIF APP0 segment
3053 static const unsigned char tag[5] = {'J','F','I','F','\0'};
3054 int ok = 1;
3055 int i;
3056 for (i=0; i < 5; ++i)
3057 if (stbi__get8(z->s) != tag[i])
3058 ok = 0;
3059 L -= 5;
3060 if (ok)
3061 z->jfif = 1;
3062 } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment
3063 static const unsigned char tag[6] = {'A','d','o','b','e','\0'};
3064 int ok = 1;
3065 int i;
3066 for (i=0; i < 6; ++i)
3067 if (stbi__get8(z->s) != tag[i])
3068 ok = 0;
3069 L -= 6;
3070 if (ok) {
3071 stbi__get8(z->s); // version
3072 stbi__get16be(z->s); // flags0
3073 stbi__get16be(z->s); // flags1
3074 z->app14_color_transform = stbi__get8(z->s); // color transform
3075 L -= 6;
3076 }
3077 }
3078
3079 stbi__skip(z->s, L);
3080 return 1;
3081 }
3082
3083 return stbi__err("unknown marker","Corrupt JPEG");
3084}
3085
3086// after we see SOS
3087static int stbi__process_scan_header(stbi__jpeg *z)
3088{
3089 int i;
3090 int Ls = stbi__get16be(z->s);
3091 z->scan_n = stbi__get8(z->s);
3092 if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG");
3093 if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG");
3094 for (i=0; i < z->scan_n; ++i) {
3095 int id = stbi__get8(z->s), which;
3096 int q = stbi__get8(z->s);
3097 for (which = 0; which < z->s->img_n; ++which)
3098 if (z->img_comp[which].id == id)
3099 break;
3100 if (which == z->s->img_n) return 0; // no match
3101 z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG");
3102 z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG");
3103 z->order[i] = which;
3104 }
3105
3106 {
3107 int aa;
3108 z->spec_start = stbi__get8(z->s);
3109 z->spec_end = stbi__get8(z->s); // should be 63, but might be 0
3110 aa = stbi__get8(z->s);
3111 z->succ_high = (aa >> 4);
3112 z->succ_low = (aa & 15);
3113 if (z->progressive) {
3114 if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13)
3115 return stbi__err("bad SOS", "Corrupt JPEG");
3116 } else {
3117 if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG");
3118 if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG");
3119 z->spec_end = 63;
3120 }
3121 }
3122
3123 return 1;
3124}
3125
3126static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why)
3127{
3128 int i;
3129 for (i=0; i < ncomp; ++i) {
3130 if (z->img_comp[i].raw_data) {
3131 STBI_FREE(z->img_comp[i].raw_data);
3132 z->img_comp[i].raw_data = NULL;
3133 z->img_comp[i].data = NULL;
3134 }
3135 if (z->img_comp[i].raw_coeff) {
3136 STBI_FREE(z->img_comp[i].raw_coeff);
3137 z->img_comp[i].raw_coeff = 0;
3138 z->img_comp[i].coeff = 0;
3139 }
3140 if (z->img_comp[i].linebuf) {
3141 STBI_FREE(z->img_comp[i].linebuf);
3142 z->img_comp[i].linebuf = NULL;
3143 }
3144 }
3145 return why;
3146}
3147
3148static int stbi__process_frame_header(stbi__jpeg *z, int scan)
3149{
3150 stbi__context *s = z->s;
3151 int Lf,p,i,q, h_max=1,v_max=1,c;
3152 Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG
3153 p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline
3154 s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG
3155 s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires
3156 c = stbi__get8(s);
3157 if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG");
3158 s->img_n = c;
3159 for (i=0; i < c; ++i) {
3160 z->img_comp[i].data = NULL;
3161 z->img_comp[i].linebuf = NULL;
3162 }
3163
3164 if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG");
3165
3166 z->rgb = 0;
3167 for (i=0; i < s->img_n; ++i) {
3168 static const unsigned char rgb[3] = { 'R', 'G', 'B' };
3169 z->img_comp[i].id = stbi__get8(s);
3170 if (s->img_n == 3 && z->img_comp[i].id == rgb[i])
3171 ++z->rgb;
3172 q = stbi__get8(s);
3173 z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG");
3174 z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG");
3175 z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG");
3176 }
3177
3178 if (scan != STBI__SCAN_load) return 1;
3179
3180 if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode");
3181
3182 for (i=0; i < s->img_n; ++i) {
3183 if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h;
3184 if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v;
3185 }
3186
3187 // compute interleaved mcu info
3188 z->img_h_max = h_max;
3189 z->img_v_max = v_max;
3190 z->img_mcu_w = h_max * 8;
3191 z->img_mcu_h = v_max * 8;
3192 // these sizes can't be more than 17 bits
3193 z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w;
3194 z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h;
3195
3196 for (i=0; i < s->img_n; ++i) {
3197 // number of effective pixels (e.g. for non-interleaved MCU)
3198 z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max;
3199 z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max;
3200 // to simplify generation, we'll allocate enough memory to decode
3201 // the bogus oversized data from using interleaved MCUs and their
3202 // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't
3203 // discard the extra data until colorspace conversion
3204 //
3205 // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier)
3206 // so these muls can't overflow with 32-bit ints (which we require)
3207 z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8;
3208 z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8;
3209 z->img_comp[i].coeff = 0;
3210 z->img_comp[i].raw_coeff = 0;
3211 z->img_comp[i].linebuf = NULL;
3212 z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15);
3213 if (z->img_comp[i].raw_data == NULL)
3214 return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory"));
3215 // align blocks for idct using mmx/sse
3216 z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15);
3217 if (z->progressive) {
3218 // w2, h2 are multiples of 8 (see above)
3219 z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8;
3220 z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8;
3221 z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15);
3222 if (z->img_comp[i].raw_coeff == NULL)
3223 return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory"));
3224 z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15);
3225 }
3226 }
3227
3228 return 1;
3229}
3230
3231// use comparisons since in some cases we handle more than one case (e.g. SOF)
3232#define stbi__DNL(x) ((x) == 0xdc)
3233#define stbi__SOI(x) ((x) == 0xd8)
3234#define stbi__EOI(x) ((x) == 0xd9)
3235#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2)
3236#define stbi__SOS(x) ((x) == 0xda)
3237
3238#define stbi__SOF_progressive(x) ((x) == 0xc2)
3239
3240static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan)
3241{
3242 int m;
3243 z->jfif = 0;
3244 z->app14_color_transform = -1; // valid values are 0,1,2
3245 z->marker = STBI__MARKER_none; // initialize cached marker to empty
3246 m = stbi__get_marker(z);
3247 if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG");
3248 if (scan == STBI__SCAN_type) return 1;
3249 m = stbi__get_marker(z);
3250 while (!stbi__SOF(m)) {
3251 if (!stbi__process_marker(z,m)) return 0;
3252 m = stbi__get_marker(z);
3253 while (m == STBI__MARKER_none) {
3254 // some files have extra padding after their blocks, so ok, we'll scan
3255 if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG");
3256 m = stbi__get_marker(z);
3257 }
3258 }
3259 z->progressive = stbi__SOF_progressive(m);
3260 if (!stbi__process_frame_header(z, scan)) return 0;
3261 return 1;
3262}
3263
3264// decode image to YCbCr format
3265static int stbi__decode_jpeg_image(stbi__jpeg *j)
3266{
3267 int m;
3268 for (m = 0; m < 4; m++) {
3269 j->img_comp[m].raw_data = NULL;
3270 j->img_comp[m].raw_coeff = NULL;
3271 }
3272 j->restart_interval = 0;
3273 if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0;
3274 m = stbi__get_marker(j);
3275 while (!stbi__EOI(m)) {
3276 if (stbi__SOS(m)) {
3277 if (!stbi__process_scan_header(j)) return 0;
3278 if (!stbi__parse_entropy_coded_data(j)) return 0;
3279 if (j->marker == STBI__MARKER_none ) {
3280 // handle 0s at the end of image data from IP Kamera 9060
3281 while (!stbi__at_eof(j->s)) {
3282 int x = stbi__get8(j->s);
3283 if (x == 255) {
3284 j->marker = stbi__get8(j->s);
3285 break;
3286 }
3287 }
3288 // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0
3289 }
3290 } else if (stbi__DNL(m)) {
3291 int Ld = stbi__get16be(j->s);
3292 stbi__uint32 NL = stbi__get16be(j->s);
3293 if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG");
3294 if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG");
3295 } else {
3296 if (!stbi__process_marker(j, m)) return 0;
3297 }
3298 m = stbi__get_marker(j);
3299 }
3300 if (j->progressive)
3301 stbi__jpeg_finish(j);
3302 return 1;
3303}
3304
3305// static jfif-centered resampling (across block boundaries)
3306
3307typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1,
3308 int w, int hs);
3309
3310#define stbi__div4(x) ((stbi_uc) ((x) >> 2))
3311
3312static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)
3313{
3314 STBI_NOTUSED(out);
3315 STBI_NOTUSED(in_far);
3316 STBI_NOTUSED(w);
3317 STBI_NOTUSED(hs);
3318 return in_near;
3319}
3320
3321static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)
3322{
3323 // need to generate two samples vertically for every one in input
3324 int i;
3325 STBI_NOTUSED(hs);
3326 for (i=0; i < w; ++i)
3327 out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2);
3328 return out;
3329}
3330
3331static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)
3332{
3333 // need to generate two samples horizontally for every one in input
3334 int i;
3335 stbi_uc *input = in_near;
3336
3337 if (w == 1) {
3338 // if only one sample, can't do any interpolation
3339 out[0] = out[1] = input[0];
3340 return out;
3341 }
3342
3343 out[0] = input[0];
3344 out[1] = stbi__div4(input[0]*3 + input[1] + 2);
3345 for (i=1; i < w-1; ++i) {
3346 int n = 3*input[i]+2;
3347 out[i*2+0] = stbi__div4(n+input[i-1]);
3348 out[i*2+1] = stbi__div4(n+input[i+1]);
3349 }
3350 out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2);
3351 out[i*2+1] = input[w-1];
3352
3353 STBI_NOTUSED(in_far);
3354 STBI_NOTUSED(hs);
3355
3356 return out;
3357}
3358
3359#define stbi__div16(x) ((stbi_uc) ((x) >> 4))
3360
3361static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)
3362{
3363 // need to generate 2x2 samples for every one in input
3364 int i,t0,t1;
3365 if (w == 1) {
3366 out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2);
3367 return out;
3368 }
3369
3370 t1 = 3*in_near[0] + in_far[0];
3371 out[0] = stbi__div4(t1+2);
3372 for (i=1; i < w; ++i) {
3373 t0 = t1;
3374 t1 = 3*in_near[i]+in_far[i];
3375 out[i*2-1] = stbi__div16(3*t0 + t1 + 8);
3376 out[i*2 ] = stbi__div16(3*t1 + t0 + 8);
3377 }
3378 out[w*2-1] = stbi__div4(t1+2);
3379
3380 STBI_NOTUSED(hs);
3381
3382 return out;
3383}
3384
3385#if defined(STBI_SSE2) || defined(STBI_NEON)
3386static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)
3387{
3388 // need to generate 2x2 samples for every one in input
3389 int i=0,t0,t1;
3390
3391 if (w == 1) {
3392 out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2);
3393 return out;
3394 }
3395
3396 t1 = 3*in_near[0] + in_far[0];
3397 // process groups of 8 pixels for as long as we can.
3398 // note we can't handle the last pixel in a row in this loop
3399 // because we need to handle the filter boundary conditions.
3400 for (; i < ((w-1) & ~7); i += 8) {
3401#if defined(STBI_SSE2)
3402 // load and perform the vertical filtering pass
3403 // this uses 3*x + y = 4*x + (y - x)
3404 __m128i zero = _mm_setzero_si128();
3405 __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i));
3406 __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i));
3407 __m128i farw = _mm_unpacklo_epi8(farb, zero);
3408 __m128i nearw = _mm_unpacklo_epi8(nearb, zero);
3409 __m128i diff = _mm_sub_epi16(farw, nearw);
3410 __m128i nears = _mm_slli_epi16(nearw, 2);
3411 __m128i curr = _mm_add_epi16(nears, diff); // current row
3412
3413 // horizontal filter works the same based on shifted vers of current
3414 // row. "prev" is current row shifted right by 1 pixel; we need to
3415 // insert the previous pixel value (from t1).
3416 // "next" is current row shifted left by 1 pixel, with first pixel
3417 // of next block of 8 pixels added in.
3418 __m128i prv0 = _mm_slli_si128(curr, 2);
3419 __m128i nxt0 = _mm_srli_si128(curr, 2);
3420 __m128i prev = _mm_insert_epi16(prv0, t1, 0);
3421 __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7);
3422
3423 // horizontal filter, polyphase implementation since it's convenient:
3424 // even pixels = 3*cur + prev = cur*4 + (prev - cur)
3425 // odd pixels = 3*cur + next = cur*4 + (next - cur)
3426 // note the shared term.
3427 __m128i bias = _mm_set1_epi16(8);
3428 __m128i curs = _mm_slli_epi16(curr, 2);
3429 __m128i prvd = _mm_sub_epi16(prev, curr);
3430 __m128i nxtd = _mm_sub_epi16(next, curr);
3431 __m128i curb = _mm_add_epi16(curs, bias);
3432 __m128i even = _mm_add_epi16(prvd, curb);
3433 __m128i odd = _mm_add_epi16(nxtd, curb);
3434
3435 // interleave even and odd pixels, then undo scaling.
3436 __m128i int0 = _mm_unpacklo_epi16(even, odd);
3437 __m128i int1 = _mm_unpackhi_epi16(even, odd);
3438 __m128i de0 = _mm_srli_epi16(int0, 4);
3439 __m128i de1 = _mm_srli_epi16(int1, 4);
3440
3441 // pack and write output
3442 __m128i outv = _mm_packus_epi16(de0, de1);
3443 _mm_storeu_si128((__m128i *) (out + i*2), outv);
3444#elif defined(STBI_NEON)
3445 // load and perform the vertical filtering pass
3446 // this uses 3*x + y = 4*x + (y - x)
3447 uint8x8_t farb = vld1_u8(in_far + i);
3448 uint8x8_t nearb = vld1_u8(in_near + i);
3449 int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb));
3450 int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2));
3451 int16x8_t curr = vaddq_s16(nears, diff); // current row
3452
3453 // horizontal filter works the same based on shifted vers of current
3454 // row. "prev" is current row shifted right by 1 pixel; we need to
3455 // insert the previous pixel value (from t1).
3456 // "next" is current row shifted left by 1 pixel, with first pixel
3457 // of next block of 8 pixels added in.
3458 int16x8_t prv0 = vextq_s16(curr, curr, 7);
3459 int16x8_t nxt0 = vextq_s16(curr, curr, 1);
3460 int16x8_t prev = vsetq_lane_s16(t1, prv0, 0);
3461 int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7);
3462
3463 // horizontal filter, polyphase implementation since it's convenient:
3464 // even pixels = 3*cur + prev = cur*4 + (prev - cur)
3465 // odd pixels = 3*cur + next = cur*4 + (next - cur)
3466 // note the shared term.
3467 int16x8_t curs = vshlq_n_s16(curr, 2);
3468 int16x8_t prvd = vsubq_s16(prev, curr);
3469 int16x8_t nxtd = vsubq_s16(next, curr);
3470 int16x8_t even = vaddq_s16(curs, prvd);
3471 int16x8_t odd = vaddq_s16(curs, nxtd);
3472
3473 // undo scaling and round, then store with even/odd phases interleaved
3474 uint8x8x2_t o;
3475 o.val[0] = vqrshrun_n_s16(even, 4);
3476 o.val[1] = vqrshrun_n_s16(odd, 4);
3477 vst2_u8(out + i*2, o);
3478#endif
3479
3480 // "previous" value for next iter
3481 t1 = 3*in_near[i+7] + in_far[i+7];
3482 }
3483
3484 t0 = t1;
3485 t1 = 3*in_near[i] + in_far[i];
3486 out[i*2] = stbi__div16(3*t1 + t0 + 8);
3487
3488 for (++i; i < w; ++i) {
3489 t0 = t1;
3490 t1 = 3*in_near[i]+in_far[i];
3491 out[i*2-1] = stbi__div16(3*t0 + t1 + 8);
3492 out[i*2 ] = stbi__div16(3*t1 + t0 + 8);
3493 }
3494 out[w*2-1] = stbi__div4(t1+2);
3495
3496 STBI_NOTUSED(hs);
3497
3498 return out;
3499}
3500#endif
3501
3502static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)
3503{
3504 // resample with nearest-neighbor
3505 int i,j;
3506 STBI_NOTUSED(in_far);
3507 for (i=0; i < w; ++i)
3508 for (j=0; j < hs; ++j)
3509 out[i*hs+j] = in_near[i];
3510 return out;
3511}
3512
3513// this is a reduced-precision calculation of YCbCr-to-RGB introduced
3514// to make sure the code produces the same results in both SIMD and scalar
3515#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8)
3516static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step)
3517{
3518 int i;
3519 for (i=0; i < count; ++i) {
3520 int y_fixed = (y[i] << 20) + (1<<19); // rounding
3521 int r,g,b;
3522 int cr = pcr[i] - 128;
3523 int cb = pcb[i] - 128;
3524 r = y_fixed + cr* stbi__float2fixed(1.40200f);
3525 g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000);
3526 b = y_fixed + cb* stbi__float2fixed(1.77200f);
3527 r >>= 20;
3528 g >>= 20;
3529 b >>= 20;
3530 if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; }
3531 if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; }
3532 if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; }
3533 out[0] = (stbi_uc)r;
3534 out[1] = (stbi_uc)g;
3535 out[2] = (stbi_uc)b;
3536 out[3] = 255;
3537 out += step;
3538 }
3539}
3540
3541#if defined(STBI_SSE2) || defined(STBI_NEON)
3542static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step)
3543{
3544 int i = 0;
3545
3546#ifdef STBI_SSE2
3547 // step == 3 is pretty ugly on the final interleave, and i'm not convinced
3548 // it's useful in practice (you wouldn't use it for textures, for example).
3549 // so just accelerate step == 4 case.
3550 if (step == 4) {
3551 // this is a fairly straightforward implementation and not super-optimized.
3552 __m128i signflip = _mm_set1_epi8(-0x80);
3553 __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f));
3554 __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f));
3555 __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f));
3556 __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f));
3557 __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128);
3558 __m128i xw = _mm_set1_epi16(255); // alpha channel
3559
3560 for (; i+7 < count; i += 8) {
3561 // load
3562 __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i));
3563 __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i));
3564 __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i));
3565 __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128
3566 __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128
3567
3568 // unpack to short (and left-shift cr, cb by 8)
3569 __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes);
3570 __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased);
3571 __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased);
3572
3573 // color transform
3574 __m128i yws = _mm_srli_epi16(yw, 4);
3575 __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw);
3576 __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw);
3577 __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1);
3578 __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1);
3579 __m128i rws = _mm_add_epi16(cr0, yws);
3580 __m128i gwt = _mm_add_epi16(cb0, yws);
3581 __m128i bws = _mm_add_epi16(yws, cb1);
3582 __m128i gws = _mm_add_epi16(gwt, cr1);
3583
3584 // descale
3585 __m128i rw = _mm_srai_epi16(rws, 4);
3586 __m128i bw = _mm_srai_epi16(bws, 4);
3587 __m128i gw = _mm_srai_epi16(gws, 4);
3588
3589 // back to byte, set up for transpose
3590 __m128i brb = _mm_packus_epi16(rw, bw);
3591 __m128i gxb = _mm_packus_epi16(gw, xw);
3592
3593 // transpose to interleave channels
3594 __m128i t0 = _mm_unpacklo_epi8(brb, gxb);
3595 __m128i t1 = _mm_unpackhi_epi8(brb, gxb);
3596 __m128i o0 = _mm_unpacklo_epi16(t0, t1);
3597 __m128i o1 = _mm_unpackhi_epi16(t0, t1);
3598
3599 // store
3600 _mm_storeu_si128((__m128i *) (out + 0), o0);
3601 _mm_storeu_si128((__m128i *) (out + 16), o1);
3602 out += 32;
3603 }
3604 }
3605#endif
3606
3607#ifdef STBI_NEON
3608 // in this version, step=3 support would be easy to add. but is there demand?
3609 if (step == 4) {
3610 // this is a fairly straightforward implementation and not super-optimized.
3611 uint8x8_t signflip = vdup_n_u8(0x80);
3612 int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f));
3613 int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f));
3614 int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f));
3615 int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f));
3616
3617 for (; i+7 < count; i += 8) {
3618 // load
3619 uint8x8_t y_bytes = vld1_u8(y + i);
3620 uint8x8_t cr_bytes = vld1_u8(pcr + i);
3621 uint8x8_t cb_bytes = vld1_u8(pcb + i);
3622 int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip));
3623 int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip));
3624
3625 // expand to s16
3626 int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4));
3627 int16x8_t crw = vshll_n_s8(cr_biased, 7);
3628 int16x8_t cbw = vshll_n_s8(cb_biased, 7);
3629
3630 // color transform
3631 int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0);
3632 int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0);
3633 int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1);
3634 int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1);
3635 int16x8_t rws = vaddq_s16(yws, cr0);
3636 int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1);
3637 int16x8_t bws = vaddq_s16(yws, cb1);
3638
3639 // undo scaling, round, convert to byte
3640 uint8x8x4_t o;
3641 o.val[0] = vqrshrun_n_s16(rws, 4);
3642 o.val[1] = vqrshrun_n_s16(gws, 4);
3643 o.val[2] = vqrshrun_n_s16(bws, 4);
3644 o.val[3] = vdup_n_u8(255);
3645
3646 // store, interleaving r/g/b/a
3647 vst4_u8(out, o);
3648 out += 8*4;
3649 }
3650 }
3651#endif
3652
3653 for (; i < count; ++i) {
3654 int y_fixed = (y[i] << 20) + (1<<19); // rounding
3655 int r,g,b;
3656 int cr = pcr[i] - 128;
3657 int cb = pcb[i] - 128;
3658 r = y_fixed + cr* stbi__float2fixed(1.40200f);
3659 g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000);
3660 b = y_fixed + cb* stbi__float2fixed(1.77200f);
3661 r >>= 20;
3662 g >>= 20;
3663 b >>= 20;
3664 if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; }
3665 if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; }
3666 if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; }
3667 out[0] = (stbi_uc)r;
3668 out[1] = (stbi_uc)g;
3669 out[2] = (stbi_uc)b;
3670 out[3] = 255;
3671 out += step;
3672 }
3673}
3674#endif
3675
3676// set up the kernels
3677static void stbi__setup_jpeg(stbi__jpeg *j)
3678{
3679 j->idct_block_kernel = stbi__idct_block;
3680 j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row;
3681 j->resample_row_hv_2_kernel = stbi__resample_row_hv_2;
3682
3683#ifdef STBI_SSE2
3684 if (stbi__sse2_available()) {
3685 j->idct_block_kernel = stbi__idct_simd;
3686 j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd;
3687 j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd;
3688 }
3689#endif
3690
3691#ifdef STBI_NEON
3692 j->idct_block_kernel = stbi__idct_simd;
3693 j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd;
3694 j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd;
3695#endif
3696}
3697
3698// clean up the temporary component buffers
3699static void stbi__cleanup_jpeg(stbi__jpeg *j)
3700{
3701 stbi__free_jpeg_components(j, j->s->img_n, 0);
3702}
3703
3704typedef struct
3705{
3706 resample_row_func resample;
3707 stbi_uc *line0,*line1;
3708 int hs,vs; // expansion factor in each axis
3709 int w_lores; // horizontal pixels pre-expansion
3710 int ystep; // how far through vertical expansion we are
3711 int ypos; // which pre-expansion row we're on
3712} stbi__resample;
3713
3714// fast 0..255 * 0..255 => 0..255 rounded multiplication
3715static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y)
3716{
3717 unsigned int t = x*y + 128;
3718 return (stbi_uc) ((t + (t >>8)) >> 8);
3719}
3720
3721static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp)
3722{
3723 int n, decode_n, is_rgb;
3724 z->s->img_n = 0; // make stbi__cleanup_jpeg safe
3725
3726 // validate req_comp
3727 if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error");
3728
3729 // load a jpeg image from whichever source, but leave in YCbCr format
3730 if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; }
3731
3732 // determine actual number of components to generate
3733 n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1;
3734
3735 is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif));
3736
3737 if (z->s->img_n == 3 && n < 3 && !is_rgb)
3738 decode_n = 1;
3739 else
3740 decode_n = z->s->img_n;
3741
3742 // resample and color-convert
3743 {
3744 int k;
3745 unsigned int i,j;
3746 stbi_uc *output;
3747 stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL };
3748
3749 stbi__resample res_comp[4];
3750
3751 for (k=0; k < decode_n; ++k) {
3752 stbi__resample *r = &res_comp[k];
3753
3754 // allocate line buffer big enough for upsampling off the edges
3755 // with upsample factor of 4
3756 z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3);
3757 if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); }
3758
3759 r->hs = z->img_h_max / z->img_comp[k].h;
3760 r->vs = z->img_v_max / z->img_comp[k].v;
3761 r->ystep = r->vs >> 1;
3762 r->w_lores = (z->s->img_x + r->hs-1) / r->hs;
3763 r->ypos = 0;
3764 r->line0 = r->line1 = z->img_comp[k].data;
3765
3766 if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1;
3767 else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2;
3768 else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2;
3769 else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel;
3770 else r->resample = stbi__resample_row_generic;
3771 }
3772
3773 // can't error after this so, this is safe
3774 output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1);
3775 if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); }
3776
3777 // now go ahead and resample
3778 for (j=0; j < z->s->img_y; ++j) {
3779 stbi_uc *out = output + n * z->s->img_x * j;
3780 for (k=0; k < decode_n; ++k) {
3781 stbi__resample *r = &res_comp[k];
3782 int y_bot = r->ystep >= (r->vs >> 1);
3783 coutput[k] = r->resample(z->img_comp[k].linebuf,
3784 y_bot ? r->line1 : r->line0,
3785 y_bot ? r->line0 : r->line1,
3786 r->w_lores, r->hs);
3787 if (++r->ystep >= r->vs) {
3788 r->ystep = 0;
3789 r->line0 = r->line1;
3790 if (++r->ypos < z->img_comp[k].y)
3791 r->line1 += z->img_comp[k].w2;
3792 }
3793 }
3794 if (n >= 3) {
3795 stbi_uc *y = coutput[0];
3796 if (z->s->img_n == 3) {
3797 if (is_rgb) {
3798 for (i=0; i < z->s->img_x; ++i) {
3799 out[0] = y[i];
3800 out[1] = coutput[1][i];
3801 out[2] = coutput[2][i];
3802 out[3] = 255;
3803 out += n;
3804 }
3805 } else {
3806 z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n);
3807 }
3808 } else if (z->s->img_n == 4) {
3809 if (z->app14_color_transform == 0) { // CMYK
3810 for (i=0; i < z->s->img_x; ++i) {
3811 stbi_uc m = coutput[3][i];
3812 out[0] = stbi__blinn_8x8(coutput[0][i], m);
3813 out[1] = stbi__blinn_8x8(coutput[1][i], m);
3814 out[2] = stbi__blinn_8x8(coutput[2][i], m);
3815 out[3] = 255;
3816 out += n;
3817 }
3818 } else if (z->app14_color_transform == 2) { // YCCK
3819 z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n);
3820 for (i=0; i < z->s->img_x; ++i) {
3821 stbi_uc m = coutput[3][i];
3822 out[0] = stbi__blinn_8x8(255 - out[0], m);
3823 out[1] = stbi__blinn_8x8(255 - out[1], m);
3824 out[2] = stbi__blinn_8x8(255 - out[2], m);
3825 out += n;
3826 }
3827 } else { // YCbCr + alpha? Ignore the fourth channel for now
3828 z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n);
3829 }
3830 } else
3831 for (i=0; i < z->s->img_x; ++i) {
3832 out[0] = out[1] = out[2] = y[i];
3833 out[3] = 255; // not used if n==3
3834 out += n;
3835 }
3836 } else {
3837 if (is_rgb) {
3838 if (n == 1)
3839 for (i=0; i < z->s->img_x; ++i)
3840 *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]);
3841 else {
3842 for (i=0; i < z->s->img_x; ++i, out += 2) {
3843 out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]);
3844 out[1] = 255;
3845 }
3846 }
3847 } else if (z->s->img_n == 4 && z->app14_color_transform == 0) {
3848 for (i=0; i < z->s->img_x; ++i) {
3849 stbi_uc m = coutput[3][i];
3850 stbi_uc r = stbi__blinn_8x8(coutput[0][i], m);
3851 stbi_uc g = stbi__blinn_8x8(coutput[1][i], m);
3852 stbi_uc b = stbi__blinn_8x8(coutput[2][i], m);
3853 out[0] = stbi__compute_y(r, g, b);
3854 out[1] = 255;
3855 out += n;
3856 }
3857 } else if (z->s->img_n == 4 && z->app14_color_transform == 2) {
3858 for (i=0; i < z->s->img_x; ++i) {
3859 out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]);
3860 out[1] = 255;
3861 out += n;
3862 }
3863 } else {
3864 stbi_uc *y = coutput[0];
3865 if (n == 1)
3866 for (i=0; i < z->s->img_x; ++i) out[i] = y[i];
3867 else
3868 for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; }
3869 }
3870 }
3871 }
3872 stbi__cleanup_jpeg(z);
3873 *out_x = z->s->img_x;
3874 *out_y = z->s->img_y;
3875 if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output
3876 return output;
3877 }
3878}
3879
3880static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri)
3881{
3882 unsigned char* result;
3883 stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg));
3884 STBI_NOTUSED(ri);
3885 j->s = s;
3886 stbi__setup_jpeg(j);
3887 result = load_jpeg_image(j, x,y,comp,req_comp);
3888 STBI_FREE(j);
3889 return result;
3890}
3891
3892static int stbi__jpeg_test(stbi__context *s)
3893{
3894 int r;
3895 stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg));
3896 j->s = s;
3897 stbi__setup_jpeg(j);
3898 r = stbi__decode_jpeg_header(j, STBI__SCAN_type);
3899 stbi__rewind(s);
3900 STBI_FREE(j);
3901 return r;
3902}
3903
3904static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp)
3905{
3906 if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) {
3907 stbi__rewind( j->s );
3908 return 0;
3909 }
3910 if (x) *x = j->s->img_x;
3911 if (y) *y = j->s->img_y;
3912 if (comp) *comp = j->s->img_n >= 3 ? 3 : 1;
3913 return 1;
3914}
3915
3916static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp)
3917{
3918 int result;
3919 stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg)));
3920 j->s = s;
3921 result = stbi__jpeg_info_raw(j, x, y, comp);
3922 STBI_FREE(j);
3923 return result;
3924}
3925#endif
3926
3927// public domain zlib decode v0.2 Sean Barrett 2006-11-18
3928// simple implementation
3929// - all input must be provided in an upfront buffer
3930// - all output is written to a single output buffer (can malloc/realloc)
3931// performance
3932// - fast huffman
3933
3934#ifndef STBI_NO_ZLIB
3935
3936// fast-way is faster to check than jpeg huffman, but slow way is slower
3937#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables
3938#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1)
3939
3940// zlib-style huffman encoding
3941// (jpegs packs from left, zlib from right, so can't share code)
3942typedef struct
3943{
3944 stbi__uint16 fast[1 << STBI__ZFAST_BITS];
3945 stbi__uint16 firstcode[16];
3946 int maxcode[17];
3947 stbi__uint16 firstsymbol[16];
3948 stbi_uc size[288];
3949 stbi__uint16 value[288];
3950} stbi__zhuffman;
3951
3952stbi_inline static int stbi__bitreverse16(int n)
3953{
3954 n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1);
3955 n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2);
3956 n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4);
3957 n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8);
3958 return n;
3959}
3960
3961stbi_inline static int stbi__bit_reverse(int v, int bits)
3962{
3963 STBI_ASSERT(bits <= 16);
3964 // to bit reverse n bits, reverse 16 and shift
3965 // e.g. 11 bits, bit reverse and shift away 5
3966 return stbi__bitreverse16(v) >> (16-bits);
3967}
3968
3969static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num)
3970{
3971 int i,k=0;
3972 int code, next_code[16], sizes[17];
3973
3974 // DEFLATE spec for generating codes
3975 memset(sizes, 0, sizeof(sizes));
3976 memset(z->fast, 0, sizeof(z->fast));
3977 for (i=0; i < num; ++i)
3978 ++sizes[sizelist[i]];
3979 sizes[0] = 0;
3980 for (i=1; i < 16; ++i)
3981 if (sizes[i] > (1 << i))
3982 return stbi__err("bad sizes", "Corrupt PNG");
3983 code = 0;
3984 for (i=1; i < 16; ++i) {
3985 next_code[i] = code;
3986 z->firstcode[i] = (stbi__uint16) code;
3987 z->firstsymbol[i] = (stbi__uint16) k;
3988 code = (code + sizes[i]);
3989 if (sizes[i])
3990 if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG");
3991 z->maxcode[i] = code << (16-i); // preshift for inner loop
3992 code <<= 1;
3993 k += sizes[i];
3994 }
3995 z->maxcode[16] = 0x10000; // sentinel
3996 for (i=0; i < num; ++i) {
3997 int s = sizelist[i];
3998 if (s) {
3999 int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s];
4000 stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i);
4001 z->size [c] = (stbi_uc ) s;
4002 z->value[c] = (stbi__uint16) i;
4003 if (s <= STBI__ZFAST_BITS) {
4004 int j = stbi__bit_reverse(next_code[s],s);
4005 while (j < (1 << STBI__ZFAST_BITS)) {
4006 z->fast[j] = fastv;
4007 j += (1 << s);
4008 }
4009 }
4010 ++next_code[s];
4011 }
4012 }
4013 return 1;
4014}
4015
4016// zlib-from-memory implementation for PNG reading
4017// because PNG allows splitting the zlib stream arbitrarily,
4018// and it's annoying structurally to have PNG call ZLIB call PNG,
4019// we require PNG read all the IDATs and combine them into a single
4020// memory buffer
4021
4022typedef struct
4023{
4024 stbi_uc *zbuffer, *zbuffer_end;
4025 int num_bits;
4026 stbi__uint32 code_buffer;
4027
4028 char *zout;
4029 char *zout_start;
4030 char *zout_end;
4031 int z_expandable;
4032
4033 stbi__zhuffman z_length, z_distance;
4034} stbi__zbuf;
4035
4036stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z)
4037{
4038 if (z->zbuffer >= z->zbuffer_end) return 0;
4039 return *z->zbuffer++;
4040}
4041
4042static void stbi__fill_bits(stbi__zbuf *z)
4043{
4044 do {
4045 STBI_ASSERT(z->code_buffer < (1U << z->num_bits));
4046 z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits;
4047 z->num_bits += 8;
4048 } while (z->num_bits <= 24);
4049}
4050
4051stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n)
4052{
4053 unsigned int k;
4054 if (z->num_bits < n) stbi__fill_bits(z);
4055 k = z->code_buffer & ((1 << n) - 1);
4056 z->code_buffer >>= n;
4057 z->num_bits -= n;
4058 return k;
4059}
4060
4061static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z)
4062{
4063 int b,s,k;
4064 // not resolved by fast table, so compute it the slow way
4065 // use jpeg approach, which requires MSbits at top
4066 k = stbi__bit_reverse(a->code_buffer, 16);
4067 for (s=STBI__ZFAST_BITS+1; ; ++s)
4068 if (k < z->maxcode[s])
4069 break;
4070 if (s == 16) return -1; // invalid code!
4071 // code size is s, so:
4072 b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s];
4073 STBI_ASSERT(z->size[b] == s);
4074 a->code_buffer >>= s;
4075 a->num_bits -= s;
4076 return z->value[b];
4077}
4078
4079stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z)
4080{
4081 int b,s;
4082 if (a->num_bits < 16) stbi__fill_bits(a);
4083 b = z->fast[a->code_buffer & STBI__ZFAST_MASK];
4084 if (b) {
4085 s = b >> 9;
4086 a->code_buffer >>= s;
4087 a->num_bits -= s;
4088 return b & 511;
4089 }
4090 return stbi__zhuffman_decode_slowpath(a, z);
4091}
4092
4093static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes
4094{
4095 char *q;
4096 int cur, limit, old_limit;
4097 z->zout = zout;
4098 if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG");
4099 cur = (int) (z->zout - z->zout_start);
4100 limit = old_limit = (int) (z->zout_end - z->zout_start);
4101 while (cur + n > limit)
4102 limit *= 2;
4103 q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit);
4104 STBI_NOTUSED(old_limit);
4105 if (q == NULL) return stbi__err("outofmem", "Out of memory");
4106 z->zout_start = q;
4107 z->zout = q + cur;
4108 z->zout_end = q + limit;
4109 return 1;
4110}
4111
4112static const int stbi__zlength_base[31] = {
4113 3,4,5,6,7,8,9,10,11,13,
4114 15,17,19,23,27,31,35,43,51,59,
4115 67,83,99,115,131,163,195,227,258,0,0 };
4116
4117static const int stbi__zlength_extra[31]=
4118{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 };
4119
4120static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,
4121257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0};
4122
4123static const int stbi__zdist_extra[32] =
4124{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};
4125
4126static int stbi__parse_huffman_block(stbi__zbuf *a)
4127{
4128 char *zout = a->zout;
4129 for(;;) {
4130 int z = stbi__zhuffman_decode(a, &a->z_length);
4131 if (z < 256) {
4132 if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes
4133 if (zout >= a->zout_end) {
4134 if (!stbi__zexpand(a, zout, 1)) return 0;
4135 zout = a->zout;
4136 }
4137 *zout++ = (char) z;
4138 } else {
4139 stbi_uc *p;
4140 int len,dist;
4141 if (z == 256) {
4142 a->zout = zout;
4143 return 1;
4144 }
4145 z -= 257;
4146 len = stbi__zlength_base[z];
4147 if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]);
4148 z = stbi__zhuffman_decode(a, &a->z_distance);
4149 if (z < 0) return stbi__err("bad huffman code","Corrupt PNG");
4150 dist = stbi__zdist_base[z];
4151 if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]);
4152 if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG");
4153 if (zout + len > a->zout_end) {
4154 if (!stbi__zexpand(a, zout, len)) return 0;
4155 zout = a->zout;
4156 }
4157 p = (stbi_uc *) (zout - dist);
4158 if (dist == 1) { // run of one byte; common in images.
4159 stbi_uc v = *p;
4160 if (len) { do *zout++ = v; while (--len); }
4161 } else {
4162 if (len) { do *zout++ = *p++; while (--len); }
4163 }
4164 }
4165 }
4166}
4167
4168static int stbi__compute_huffman_codes(stbi__zbuf *a)
4169{
4170 static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 };
4171 stbi__zhuffman z_codelength;
4172 stbi_uc lencodes[286+32+137];//padding for maximum single op
4173 stbi_uc codelength_sizes[19];
4174 int i,n;
4175
4176 int hlit = stbi__zreceive(a,5) + 257;
4177 int hdist = stbi__zreceive(a,5) + 1;
4178 int hclen = stbi__zreceive(a,4) + 4;
4179 int ntot = hlit + hdist;
4180
4181 memset(codelength_sizes, 0, sizeof(codelength_sizes));
4182 for (i=0; i < hclen; ++i) {
4183 int s = stbi__zreceive(a,3);
4184 codelength_sizes[length_dezigzag[i]] = (stbi_uc) s;
4185 }
4186 if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0;
4187
4188 n = 0;
4189 while (n < ntot) {
4190 int c = stbi__zhuffman_decode(a, &z_codelength);
4191 if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG");
4192 if (c < 16)
4193 lencodes[n++] = (stbi_uc) c;
4194 else {
4195 stbi_uc fill = 0;
4196 if (c == 16) {
4197 c = stbi__zreceive(a,2)+3;
4198 if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG");
4199 fill = lencodes[n-1];
4200 } else if (c == 17)
4201 c = stbi__zreceive(a,3)+3;
4202 else {
4203 STBI_ASSERT(c == 18);
4204 c = stbi__zreceive(a,7)+11;
4205 }
4206 if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG");
4207 memset(lencodes+n, fill, c);
4208 n += c;
4209 }
4210 }
4211 if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG");
4212 if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0;
4213 if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0;
4214 return 1;
4215}
4216
4217static int stbi__parse_uncompressed_block(stbi__zbuf *a)
4218{
4219 stbi_uc header[4];
4220 int len,nlen,k;
4221 if (a->num_bits & 7)
4222 stbi__zreceive(a, a->num_bits & 7); // discard
4223 // drain the bit-packed data into header
4224 k = 0;
4225 while (a->num_bits > 0) {
4226 header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check
4227 a->code_buffer >>= 8;
4228 a->num_bits -= 8;
4229 }
4230 STBI_ASSERT(a->num_bits == 0);
4231 // now fill header the normal way
4232 while (k < 4)
4233 header[k++] = stbi__zget8(a);
4234 len = header[1] * 256 + header[0];
4235 nlen = header[3] * 256 + header[2];
4236 if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG");
4237 if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG");
4238 if (a->zout + len > a->zout_end)
4239 if (!stbi__zexpand(a, a->zout, len)) return 0;
4240 memcpy(a->zout, a->zbuffer, len);
4241 a->zbuffer += len;
4242 a->zout += len;
4243 return 1;
4244}
4245
4246static int stbi__parse_zlib_header(stbi__zbuf *a)
4247{
4248 int cmf = stbi__zget8(a);
4249 int cm = cmf & 15;
4250 /* int cinfo = cmf >> 4; */
4251 int flg = stbi__zget8(a);
4252 if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec
4253 if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png
4254 if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png
4255 // window = 1 << (8 + cinfo)... but who cares, we fully buffer output
4256 return 1;
4257}
4258
4259static const stbi_uc stbi__zdefault_length[288] =
4260{
4261 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
4262 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
4263 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
4264 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
4265 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
4266 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
4267 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
4268 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
4269 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8
4270};
4271static const stbi_uc stbi__zdefault_distance[32] =
4272{
4273 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5
4274};
4275/*
4276Init algorithm:
4277{
4278 int i; // use <= to match clearly with spec
4279 for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8;
4280 for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9;
4281 for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7;
4282 for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8;
4283
4284 for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5;
4285}
4286*/
4287
4288static int stbi__parse_zlib(stbi__zbuf *a, int parse_header)
4289{
4290 int final, type;
4291 if (parse_header)
4292 if (!stbi__parse_zlib_header(a)) return 0;
4293 a->num_bits = 0;
4294 a->code_buffer = 0;
4295 do {
4296 final = stbi__zreceive(a,1);
4297 type = stbi__zreceive(a,2);
4298 if (type == 0) {
4299 if (!stbi__parse_uncompressed_block(a)) return 0;
4300 } else if (type == 3) {
4301 return 0;
4302 } else {
4303 if (type == 1) {
4304 // use fixed code lengths
4305 if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0;
4306 if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0;
4307 } else {
4308 if (!stbi__compute_huffman_codes(a)) return 0;
4309 }
4310 if (!stbi__parse_huffman_block(a)) return 0;
4311 }
4312 } while (!final);
4313 return 1;
4314}
4315
4316static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header)
4317{
4318 a->zout_start = obuf;
4319 a->zout = obuf;
4320 a->zout_end = obuf + olen;
4321 a->z_expandable = exp;
4322
4323 return stbi__parse_zlib(a, parse_header);
4324}
4325
4326STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen)
4327{
4328 stbi__zbuf a;
4329 char *p = (char *) stbi__malloc(initial_size);
4330 if (p == NULL) return NULL;
4331 a.zbuffer = (stbi_uc *) buffer;
4332 a.zbuffer_end = (stbi_uc *) buffer + len;
4333 if (stbi__do_zlib(&a, p, initial_size, 1, 1)) {
4334 if (outlen) *outlen = (int) (a.zout - a.zout_start);
4335 return a.zout_start;
4336 } else {
4337 STBI_FREE(a.zout_start);
4338 return NULL;
4339 }
4340}
4341
4342STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen)
4343{
4344 return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen);
4345}
4346
4347STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header)
4348{
4349 stbi__zbuf a;
4350 char *p = (char *) stbi__malloc(initial_size);
4351 if (p == NULL) return NULL;
4352 a.zbuffer = (stbi_uc *) buffer;
4353 a.zbuffer_end = (stbi_uc *) buffer + len;
4354 if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) {
4355 if (outlen) *outlen = (int) (a.zout - a.zout_start);
4356 return a.zout_start;
4357 } else {
4358 STBI_FREE(a.zout_start);
4359 return NULL;
4360 }
4361}
4362
4363STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen)
4364{
4365 stbi__zbuf a;
4366 a.zbuffer = (stbi_uc *) ibuffer;
4367 a.zbuffer_end = (stbi_uc *) ibuffer + ilen;
4368 if (stbi__do_zlib(&a, obuffer, olen, 0, 1))
4369 return (int) (a.zout - a.zout_start);
4370 else
4371 return -1;
4372}
4373
4374STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen)
4375{
4376 stbi__zbuf a;
4377 char *p = (char *) stbi__malloc(16384);
4378 if (p == NULL) return NULL;
4379 a.zbuffer = (stbi_uc *) buffer;
4380 a.zbuffer_end = (stbi_uc *) buffer+len;
4381 if (stbi__do_zlib(&a, p, 16384, 1, 0)) {
4382 if (outlen) *outlen = (int) (a.zout - a.zout_start);
4383 return a.zout_start;
4384 } else {
4385 STBI_FREE(a.zout_start);
4386 return NULL;
4387 }
4388}
4389
4390STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen)
4391{
4392 stbi__zbuf a;
4393 a.zbuffer = (stbi_uc *) ibuffer;
4394 a.zbuffer_end = (stbi_uc *) ibuffer + ilen;
4395 if (stbi__do_zlib(&a, obuffer, olen, 0, 0))
4396 return (int) (a.zout - a.zout_start);
4397 else
4398 return -1;
4399}
4400#endif
4401
4402// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18
4403// simple implementation
4404// - only 8-bit samples
4405// - no CRC checking
4406// - allocates lots of intermediate memory
4407// - avoids problem of streaming data between subsystems
4408// - avoids explicit window management
4409// performance
4410// - uses stb_zlib, a PD zlib implementation with fast huffman decoding
4411
4412#ifndef STBI_NO_PNG
4413typedef struct
4414{
4415 stbi__uint32 length;
4416 stbi__uint32 type;
4417} stbi__pngchunk;
4418
4419static stbi__pngchunk stbi__get_chunk_header(stbi__context *s)
4420{
4421 stbi__pngchunk c;
4422 c.length = stbi__get32be(s);
4423 c.type = stbi__get32be(s);
4424 return c;
4425}
4426
4427static int stbi__check_png_header(stbi__context *s)
4428{
4429 static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 };
4430 int i;
4431 for (i=0; i < 8; ++i)
4432 if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG");
4433 return 1;
4434}
4435
4436typedef struct
4437{
4438 stbi__context *s;
4439 stbi_uc *idata, *expanded, *out;
4440 int depth;
4441} stbi__png;
4442
4443
4444enum {
4445 STBI__F_none=0,
4446 STBI__F_sub=1,
4447 STBI__F_up=2,
4448 STBI__F_avg=3,
4449 STBI__F_paeth=4,
4450 // synthetic filters used for first scanline to avoid needing a dummy row of 0s
4451 STBI__F_avg_first,
4452 STBI__F_paeth_first
4453};
4454
4455static stbi_uc first_row_filter[5] =
4456{
4457 STBI__F_none,
4458 STBI__F_sub,
4459 STBI__F_none,
4460 STBI__F_avg_first,
4461 STBI__F_paeth_first
4462};
4463
4464static int stbi__paeth(int a, int b, int c)
4465{
4466 int p = a + b - c;
4467 int pa = abs(p-a);
4468 int pb = abs(p-b);
4469 int pc = abs(p-c);
4470 if (pa <= pb && pa <= pc) return a;
4471 if (pb <= pc) return b;
4472 return c;
4473}
4474
4475static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 };
4476
4477// create the png data from post-deflated data
4478static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color)
4479{
4480 int bytes = (depth == 16? 2 : 1);
4481 stbi__context *s = a->s;
4482 stbi__uint32 i,j,stride = x*out_n*bytes;
4483 stbi__uint32 img_len, img_width_bytes;
4484 int k;
4485 int img_n = s->img_n; // copy it into a local for later
4486
4487 int output_bytes = out_n*bytes;
4488 int filter_bytes = img_n*bytes;
4489 int width = x;
4490
4491 STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1);
4492 a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into
4493 if (!a->out) return stbi__err("outofmem", "Out of memory");
4494
4495 if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG");
4496 img_width_bytes = (((img_n * x * depth) + 7) >> 3);
4497 img_len = (img_width_bytes + 1) * y;
4498
4499 // we used to check for exact match between raw_len and img_len on non-interlaced PNGs,
4500 // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros),
4501 // so just check for raw_len < img_len always.
4502 if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG");
4503
4504 for (j=0; j < y; ++j) {
4505 stbi_uc *cur = a->out + stride*j;
4506 stbi_uc *prior;
4507 int filter = *raw++;
4508
4509 if (filter > 4)
4510 return stbi__err("invalid filter","Corrupt PNG");
4511
4512 if (depth < 8) {
4513 STBI_ASSERT(img_width_bytes <= x);
4514 cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place
4515 filter_bytes = 1;
4516 width = img_width_bytes;
4517 }
4518 prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above
4519
4520 // if first row, use special filter that doesn't sample previous row
4521 if (j == 0) filter = first_row_filter[filter];
4522
4523 // handle first byte explicitly
4524 for (k=0; k < filter_bytes; ++k) {
4525 switch (filter) {
4526 case STBI__F_none : cur[k] = raw[k]; break;
4527 case STBI__F_sub : cur[k] = raw[k]; break;
4528 case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break;
4529 case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break;
4530 case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break;
4531 case STBI__F_avg_first : cur[k] = raw[k]; break;
4532 case STBI__F_paeth_first: cur[k] = raw[k]; break;
4533 }
4534 }
4535
4536 if (depth == 8) {
4537 if (img_n != out_n)
4538 cur[img_n] = 255; // first pixel
4539 raw += img_n;
4540 cur += out_n;
4541 prior += out_n;
4542 } else if (depth == 16) {
4543 if (img_n != out_n) {
4544 cur[filter_bytes] = 255; // first pixel top byte
4545 cur[filter_bytes+1] = 255; // first pixel bottom byte
4546 }
4547 raw += filter_bytes;
4548 cur += output_bytes;
4549 prior += output_bytes;
4550 } else {
4551 raw += 1;
4552 cur += 1;
4553 prior += 1;
4554 }
4555
4556 // this is a little gross, so that we don't switch per-pixel or per-component
4557 if (depth < 8 || img_n == out_n) {
4558 int nk = (width - 1)*filter_bytes;
4559 #define STBI__CASE(f) \
4560 case f: \
4561 for (k=0; k < nk; ++k)
4562 switch (filter) {
4563 // "none" filter turns into a memcpy here; make that explicit.
4564 case STBI__F_none: memcpy(cur, raw, nk); break;
4565 STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break;
4566 STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break;
4567 STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break;
4568 STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break;
4569 STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break;
4570 STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break;
4571 }
4572 #undef STBI__CASE
4573 raw += nk;
4574 } else {
4575 STBI_ASSERT(img_n+1 == out_n);
4576 #define STBI__CASE(f) \
4577 case f: \
4578 for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \
4579 for (k=0; k < filter_bytes; ++k)
4580 switch (filter) {
4581 STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break;
4582 STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break;
4583 STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break;
4584 STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break;
4585 STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break;
4586 STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break;
4587 STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break;
4588 }
4589 #undef STBI__CASE
4590
4591 // the loop above sets the high byte of the pixels' alpha, but for
4592 // 16 bit png files we also need the low byte set. we'll do that here.
4593 if (depth == 16) {
4594 cur = a->out + stride*j; // start at the beginning of the row again
4595 for (i=0; i < x; ++i,cur+=output_bytes) {
4596 cur[filter_bytes+1] = 255;
4597 }
4598 }
4599 }
4600 }
4601
4602 // we make a separate pass to expand bits to pixels; for performance,
4603 // this could run two scanlines behind the above code, so it won't
4604 // intefere with filtering but will still be in the cache.
4605 if (depth < 8) {
4606 for (j=0; j < y; ++j) {
4607 stbi_uc *cur = a->out + stride*j;
4608 stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes;
4609 // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit
4610 // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop
4611 stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range
4612
4613 // note that the final byte might overshoot and write more data than desired.
4614 // we can allocate enough data that this never writes out of memory, but it
4615 // could also overwrite the next scanline. can it overwrite non-empty data
4616 // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel.
4617 // so we need to explicitly clamp the final ones
4618
4619 if (depth == 4) {
4620 for (k=x*img_n; k >= 2; k-=2, ++in) {
4621 *cur++ = scale * ((*in >> 4) );
4622 *cur++ = scale * ((*in ) & 0x0f);
4623 }
4624 if (k > 0) *cur++ = scale * ((*in >> 4) );
4625 } else if (depth == 2) {
4626 for (k=x*img_n; k >= 4; k-=4, ++in) {
4627 *cur++ = scale * ((*in >> 6) );
4628 *cur++ = scale * ((*in >> 4) & 0x03);
4629 *cur++ = scale * ((*in >> 2) & 0x03);
4630 *cur++ = scale * ((*in ) & 0x03);
4631 }
4632 if (k > 0) *cur++ = scale * ((*in >> 6) );
4633 if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03);
4634 if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03);
4635 } else if (depth == 1) {
4636 for (k=x*img_n; k >= 8; k-=8, ++in) {
4637 *cur++ = scale * ((*in >> 7) );
4638 *cur++ = scale * ((*in >> 6) & 0x01);
4639 *cur++ = scale * ((*in >> 5) & 0x01);
4640 *cur++ = scale * ((*in >> 4) & 0x01);
4641 *cur++ = scale * ((*in >> 3) & 0x01);
4642 *cur++ = scale * ((*in >> 2) & 0x01);
4643 *cur++ = scale * ((*in >> 1) & 0x01);
4644 *cur++ = scale * ((*in ) & 0x01);
4645 }
4646 if (k > 0) *cur++ = scale * ((*in >> 7) );
4647 if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01);
4648 if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01);
4649 if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01);
4650 if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01);
4651 if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01);
4652 if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01);
4653 }
4654 if (img_n != out_n) {
4655 int q;
4656 // insert alpha = 255
4657 cur = a->out + stride*j;
4658 if (img_n == 1) {
4659 for (q=x-1; q >= 0; --q) {
4660 cur[q*2+1] = 255;
4661 cur[q*2+0] = cur[q];
4662 }
4663 } else {
4664 STBI_ASSERT(img_n == 3);
4665 for (q=x-1; q >= 0; --q) {
4666 cur[q*4+3] = 255;
4667 cur[q*4+2] = cur[q*3+2];
4668 cur[q*4+1] = cur[q*3+1];
4669 cur[q*4+0] = cur[q*3+0];
4670 }
4671 }
4672 }
4673 }
4674 } else if (depth == 16) {
4675 // force the image data from big-endian to platform-native.
4676 // this is done in a separate pass due to the decoding relying
4677 // on the data being untouched, but could probably be done
4678 // per-line during decode if care is taken.
4679 stbi_uc *cur = a->out;
4680 stbi__uint16 *cur16 = (stbi__uint16*)cur;
4681
4682 for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) {
4683 *cur16 = (cur[0] << 8) | cur[1];
4684 }
4685 }
4686
4687 return 1;
4688}
4689
4690static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced)
4691{
4692 int bytes = (depth == 16 ? 2 : 1);
4693 int out_bytes = out_n * bytes;
4694 stbi_uc *final;
4695 int p;
4696 if (!interlaced)
4697 return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color);
4698
4699 // de-interlacing
4700 final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0);
4701 for (p=0; p < 7; ++p) {
4702 int xorig[] = { 0,4,0,2,0,1,0 };
4703 int yorig[] = { 0,0,4,0,2,0,1 };
4704 int xspc[] = { 8,8,4,4,2,2,1 };
4705 int yspc[] = { 8,8,8,4,4,2,2 };
4706 int i,j,x,y;
4707 // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1
4708 x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p];
4709 y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p];
4710 if (x && y) {
4711 stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y;
4712 if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) {
4713 STBI_FREE(final);
4714 return 0;
4715 }
4716 for (j=0; j < y; ++j) {
4717 for (i=0; i < x; ++i) {
4718 int out_y = j*yspc[p]+yorig[p];
4719 int out_x = i*xspc[p]+xorig[p];
4720 memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes,
4721 a->out + (j*x+i)*out_bytes, out_bytes);
4722 }
4723 }
4724 STBI_FREE(a->out);
4725 image_data += img_len;
4726 image_data_len -= img_len;
4727 }
4728 }
4729 a->out = final;
4730
4731 return 1;
4732}
4733
4734static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n)
4735{
4736 stbi__context *s = z->s;
4737 stbi__uint32 i, pixel_count = s->img_x * s->img_y;
4738 stbi_uc *p = z->out;
4739
4740 // compute color-based transparency, assuming we've
4741 // already got 255 as the alpha value in the output
4742 STBI_ASSERT(out_n == 2 || out_n == 4);
4743
4744 if (out_n == 2) {
4745 for (i=0; i < pixel_count; ++i) {
4746 p[1] = (p[0] == tc[0] ? 0 : 255);
4747 p += 2;
4748 }
4749 } else {
4750 for (i=0; i < pixel_count; ++i) {
4751 if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2])
4752 p[3] = 0;
4753 p += 4;
4754 }
4755 }
4756 return 1;
4757}
4758
4759static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n)
4760{
4761 stbi__context *s = z->s;
4762 stbi__uint32 i, pixel_count = s->img_x * s->img_y;
4763 stbi__uint16 *p = (stbi__uint16*) z->out;
4764
4765 // compute color-based transparency, assuming we've
4766 // already got 65535 as the alpha value in the output
4767 STBI_ASSERT(out_n == 2 || out_n == 4);
4768
4769 if (out_n == 2) {
4770 for (i = 0; i < pixel_count; ++i) {
4771 p[1] = (p[0] == tc[0] ? 0 : 65535);
4772 p += 2;
4773 }
4774 } else {
4775 for (i = 0; i < pixel_count; ++i) {
4776 if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2])
4777 p[3] = 0;
4778 p += 4;
4779 }
4780 }
4781 return 1;
4782}
4783
4784static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n)
4785{
4786 stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y;
4787 stbi_uc *p, *temp_out, *orig = a->out;
4788
4789 p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0);
4790 if (p == NULL) return stbi__err("outofmem", "Out of memory");
4791
4792 // between here and free(out) below, exitting would leak
4793 temp_out = p;
4794
4795 if (pal_img_n == 3) {
4796 for (i=0; i < pixel_count; ++i) {
4797 int n = orig[i]*4;
4798 p[0] = palette[n ];
4799 p[1] = palette[n+1];
4800 p[2] = palette[n+2];
4801 p += 3;
4802 }
4803 } else {
4804 for (i=0; i < pixel_count; ++i) {
4805 int n = orig[i]*4;
4806 p[0] = palette[n ];
4807 p[1] = palette[n+1];
4808 p[2] = palette[n+2];
4809 p[3] = palette[n+3];
4810 p += 4;
4811 }
4812 }
4813 STBI_FREE(a->out);
4814 a->out = temp_out;
4815
4816 STBI_NOTUSED(len);
4817
4818 return 1;
4819}
4820
4821static int stbi__unpremultiply_on_load = 0;
4822static int stbi__de_iphone_flag = 0;
4823
4824STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply)
4825{
4826 stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply;
4827}
4828
4829STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert)
4830{
4831 stbi__de_iphone_flag = flag_true_if_should_convert;
4832}
4833
4834static void stbi__de_iphone(stbi__png *z)
4835{
4836 stbi__context *s = z->s;
4837 stbi__uint32 i, pixel_count = s->img_x * s->img_y;
4838 stbi_uc *p = z->out;
4839
4840 if (s->img_out_n == 3) { // convert bgr to rgb
4841 for (i=0; i < pixel_count; ++i) {
4842 stbi_uc t = p[0];
4843 p[0] = p[2];
4844 p[2] = t;
4845 p += 3;
4846 }
4847 } else {
4848 STBI_ASSERT(s->img_out_n == 4);
4849 if (stbi__unpremultiply_on_load) {
4850 // convert bgr to rgb and unpremultiply
4851 for (i=0; i < pixel_count; ++i) {
4852 stbi_uc a = p[3];
4853 stbi_uc t = p[0];
4854 if (a) {
4855 stbi_uc half = a / 2;
4856 p[0] = (p[2] * 255 + half) / a;
4857 p[1] = (p[1] * 255 + half) / a;
4858 p[2] = ( t * 255 + half) / a;
4859 } else {
4860 p[0] = p[2];
4861 p[2] = t;
4862 }
4863 p += 4;
4864 }
4865 } else {
4866 // convert bgr to rgb
4867 for (i=0; i < pixel_count; ++i) {
4868 stbi_uc t = p[0];
4869 p[0] = p[2];
4870 p[2] = t;
4871 p += 4;
4872 }
4873 }
4874 }
4875}
4876
4877#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d))
4878
4879static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
4880{
4881 stbi_uc palette[1024], pal_img_n=0;
4882 stbi_uc has_trans=0, tc[3]={0};
4883 stbi__uint16 tc16[3];
4884 stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0;
4885 int first=1,k,interlace=0, color=0, is_iphone=0;
4886 stbi__context *s = z->s;
4887
4888 z->expanded = NULL;
4889 z->idata = NULL;
4890 z->out = NULL;
4891
4892 if (!stbi__check_png_header(s)) return 0;
4893
4894 if (scan == STBI__SCAN_type) return 1;
4895
4896 for (;;) {
4897 stbi__pngchunk c = stbi__get_chunk_header(s);
4898 switch (c.type) {
4899 case STBI__PNG_TYPE('C','g','B','I'):
4900 is_iphone = 1;
4901 stbi__skip(s, c.length);
4902 break;
4903 case STBI__PNG_TYPE('I','H','D','R'): {
4904 int comp,filter;
4905 if (!first) return stbi__err("multiple IHDR","Corrupt PNG");
4906 first = 0;
4907 if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG");
4908 s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)");
4909 s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)");
4910 z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only");
4911 color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG");
4912 if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG");
4913 if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG");
4914 comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG");
4915 filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG");
4916 interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG");
4917 if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG");
4918 if (!pal_img_n) {
4919 s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0);
4920 if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode");
4921 if (scan == STBI__SCAN_header) return 1;
4922 } else {
4923 // if paletted, then pal_n is our final components, and
4924 // img_n is # components to decompress/filter.
4925 s->img_n = 1;
4926 if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG");
4927 // if SCAN_header, have to scan to see if we have a tRNS
4928 }
4929 break;
4930 }
4931
4932 case STBI__PNG_TYPE('P','L','T','E'): {
4933 if (first) return stbi__err("first not IHDR", "Corrupt PNG");
4934 if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG");
4935 pal_len = c.length / 3;
4936 if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG");
4937 for (i=0; i < pal_len; ++i) {
4938 palette[i*4+0] = stbi__get8(s);
4939 palette[i*4+1] = stbi__get8(s);
4940 palette[i*4+2] = stbi__get8(s);
4941 palette[i*4+3] = 255;
4942 }
4943 break;
4944 }
4945
4946 case STBI__PNG_TYPE('t','R','N','S'): {
4947 if (first) return stbi__err("first not IHDR", "Corrupt PNG");
4948 if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG");
4949 if (pal_img_n) {
4950 if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; }
4951 if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG");
4952 if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG");
4953 pal_img_n = 4;
4954 for (i=0; i < c.length; ++i)
4955 palette[i*4+3] = stbi__get8(s);
4956 } else {
4957 if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG");
4958 if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG");
4959 has_trans = 1;
4960 if (z->depth == 16) {
4961 for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is
4962 } else {
4963 for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger
4964 }
4965 }
4966 break;
4967 }
4968
4969 case STBI__PNG_TYPE('I','D','A','T'): {
4970 if (first) return stbi__err("first not IHDR", "Corrupt PNG");
4971 if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG");
4972 if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; }
4973 if ((int)(ioff + c.length) < (int)ioff) return 0;
4974 if (ioff + c.length > idata_limit) {
4975 stbi__uint32 idata_limit_old = idata_limit;
4976 stbi_uc *p;
4977 if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096;
4978 while (ioff + c.length > idata_limit)
4979 idata_limit *= 2;
4980 STBI_NOTUSED(idata_limit_old);
4981 p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory");
4982 z->idata = p;
4983 }
4984 if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG");
4985 ioff += c.length;
4986 break;
4987 }
4988
4989 case STBI__PNG_TYPE('I','E','N','D'): {
4990 stbi__uint32 raw_len, bpl;
4991 if (first) return stbi__err("first not IHDR", "Corrupt PNG");
4992 if (scan != STBI__SCAN_load) return 1;
4993 if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG");
4994 // initial guess for decoded data size to avoid unnecessary reallocs
4995 bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component
4996 raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */;
4997 z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone);
4998 if (z->expanded == NULL) return 0; // zlib should set error
4999 STBI_FREE(z->idata); z->idata = NULL;
5000 if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans)
5001 s->img_out_n = s->img_n+1;
5002 else
5003 s->img_out_n = s->img_n;
5004 if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0;
5005 if (has_trans) {
5006 if (z->depth == 16) {
5007 if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0;
5008 } else {
5009 if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0;
5010 }
5011 }
5012 if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2)
5013 stbi__de_iphone(z);
5014 if (pal_img_n) {
5015 // pal_img_n == 3 or 4
5016 s->img_n = pal_img_n; // record the actual colors we had
5017 s->img_out_n = pal_img_n;
5018 if (req_comp >= 3) s->img_out_n = req_comp;
5019 if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n))
5020 return 0;
5021 } else if (has_trans) {
5022 // non-paletted image with tRNS -> source image has (constant) alpha
5023 ++s->img_n;
5024 }
5025 STBI_FREE(z->expanded); z->expanded = NULL;
5026 // end of PNG chunk, read and skip CRC
5027 stbi__get32be(s);
5028 return 1;
5029 }
5030
5031 default:
5032 // if critical, fail
5033 if (first) return stbi__err("first not IHDR", "Corrupt PNG");
5034 if ((c.type & (1 << 29)) == 0) {
5035 #ifndef STBI_NO_FAILURE_STRINGS
5036 // not threadsafe
5037 static char invalid_chunk[] = "XXXX PNG chunk not known";
5038 invalid_chunk[0] = STBI__BYTECAST(c.type >> 24);
5039 invalid_chunk[1] = STBI__BYTECAST(c.type >> 16);
5040 invalid_chunk[2] = STBI__BYTECAST(c.type >> 8);
5041 invalid_chunk[3] = STBI__BYTECAST(c.type >> 0);
5042 #endif
5043 return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type");
5044 }
5045 stbi__skip(s, c.length);
5046 break;
5047 }
5048 // end of PNG chunk, read and skip CRC
5049 stbi__get32be(s);
5050 }
5051}
5052
5053static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri)
5054{
5055 void *result=NULL;
5056 if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error");
5057 if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) {
5058 if (p->depth < 8)
5059 ri->bits_per_channel = 8;
5060 else
5061 ri->bits_per_channel = p->depth;
5062 result = p->out;
5063 p->out = NULL;
5064 if (req_comp && req_comp != p->s->img_out_n) {
5065 if (ri->bits_per_channel == 8)
5066 result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y);
5067 else
5068 result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y);
5069 p->s->img_out_n = req_comp;
5070 if (result == NULL) return result;
5071 }
5072 *x = p->s->img_x;
5073 *y = p->s->img_y;
5074 if (n) *n = p->s->img_n;
5075 }
5076 STBI_FREE(p->out); p->out = NULL;
5077 STBI_FREE(p->expanded); p->expanded = NULL;
5078 STBI_FREE(p->idata); p->idata = NULL;
5079
5080 return result;
5081}
5082
5083static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri)
5084{
5085 stbi__png p;
5086 p.s = s;
5087 return stbi__do_png(&p, x,y,comp,req_comp, ri);
5088}
5089
5090static int stbi__png_test(stbi__context *s)
5091{
5092 int r;
5093 r = stbi__check_png_header(s);
5094 stbi__rewind(s);
5095 return r;
5096}
5097
5098static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp)
5099{
5100 if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) {
5101 stbi__rewind( p->s );
5102 return 0;
5103 }
5104 if (x) *x = p->s->img_x;
5105 if (y) *y = p->s->img_y;
5106 if (comp) *comp = p->s->img_n;
5107 return 1;
5108}
5109
5110static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp)
5111{
5112 stbi__png p;
5113 p.s = s;
5114 return stbi__png_info_raw(&p, x, y, comp);
5115}
5116
5117static int stbi__png_is16(stbi__context *s)
5118{
5119 stbi__png p;
5120 p.s = s;
5121 if (!stbi__png_info_raw(&p, NULL, NULL, NULL))
5122 return 0;
5123 if (p.depth != 16) {
5124 stbi__rewind(p.s);
5125 return 0;
5126 }
5127 return 1;
5128}
5129#endif
5130
5131// Microsoft/Windows BMP image
5132
5133#ifndef STBI_NO_BMP
5134static int stbi__bmp_test_raw(stbi__context *s)
5135{
5136 int r;
5137 int sz;
5138 if (stbi__get8(s) != 'B') return 0;
5139 if (stbi__get8(s) != 'M') return 0;
5140 stbi__get32le(s); // discard filesize
5141 stbi__get16le(s); // discard reserved
5142 stbi__get16le(s); // discard reserved
5143 stbi__get32le(s); // discard data offset
5144 sz = stbi__get32le(s);
5145 r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124);
5146 return r;
5147}
5148
5149static int stbi__bmp_test(stbi__context *s)
5150{
5151 int r = stbi__bmp_test_raw(s);
5152 stbi__rewind(s);
5153 return r;
5154}
5155
5156
5157// returns 0..31 for the highest set bit
5158static int stbi__high_bit(unsigned int z)
5159{
5160 int n=0;
5161 if (z == 0) return -1;
5162 if (z >= 0x10000) { n += 16; z >>= 16; }
5163 if (z >= 0x00100) { n += 8; z >>= 8; }
5164 if (z >= 0x00010) { n += 4; z >>= 4; }
5165 if (z >= 0x00004) { n += 2; z >>= 2; }
5166 if (z >= 0x00002) { n += 1;/* >>= 1;*/ }
5167 return n;
5168}
5169
5170static int stbi__bitcount(unsigned int a)
5171{
5172 a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2
5173 a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4
5174 a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits
5175 a = (a + (a >> 8)); // max 16 per 8 bits
5176 a = (a + (a >> 16)); // max 32 per 8 bits
5177 return a & 0xff;
5178}
5179
5180// extract an arbitrarily-aligned N-bit value (N=bits)
5181// from v, and then make it 8-bits long and fractionally
5182// extend it to full full range.
5183static int stbi__shiftsigned(unsigned int v, int shift, int bits)
5184{
5185 static unsigned int mul_table[9] = {
5186 0,
5187 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/,
5188 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/,
5189 };
5190 static unsigned int shift_table[9] = {
5191 0, 0,0,1,0,2,4,6,0,
5192 };
5193 if (shift < 0)
5194 v <<= -shift;
5195 else
5196 v >>= shift;
5197 STBI_ASSERT(v < 256);
5198 v >>= (8-bits);
5199 STBI_ASSERT(bits >= 0 && bits <= 8);
5200 return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits];
5201}
5202
5203typedef struct
5204{
5205 int bpp, offset, hsz;
5206 unsigned int mr,mg,mb,ma, all_a;
5207 int extra_read;
5208} stbi__bmp_data;
5209
5210static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info)
5211{
5212 int hsz;
5213 if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP");
5214 stbi__get32le(s); // discard filesize
5215 stbi__get16le(s); // discard reserved
5216 stbi__get16le(s); // discard reserved
5217 info->offset = stbi__get32le(s);
5218 info->hsz = hsz = stbi__get32le(s);
5219 info->mr = info->mg = info->mb = info->ma = 0;
5220 info->extra_read = 14;
5221
5222 if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown");
5223 if (hsz == 12) {
5224 s->img_x = stbi__get16le(s);
5225 s->img_y = stbi__get16le(s);
5226 } else {
5227 s->img_x = stbi__get32le(s);
5228 s->img_y = stbi__get32le(s);
5229 }
5230 if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP");
5231 info->bpp = stbi__get16le(s);
5232 if (hsz != 12) {
5233 int compress = stbi__get32le(s);
5234 if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE");
5235 stbi__get32le(s); // discard sizeof
5236 stbi__get32le(s); // discard hres
5237 stbi__get32le(s); // discard vres
5238 stbi__get32le(s); // discard colorsused
5239 stbi__get32le(s); // discard max important
5240 if (hsz == 40 || hsz == 56) {
5241 if (hsz == 56) {
5242 stbi__get32le(s);
5243 stbi__get32le(s);
5244 stbi__get32le(s);
5245 stbi__get32le(s);
5246 }
5247 if (info->bpp == 16 || info->bpp == 32) {
5248 if (compress == 0) {
5249 if (info->bpp == 32) {
5250 info->mr = 0xffu << 16;
5251 info->mg = 0xffu << 8;
5252 info->mb = 0xffu << 0;
5253 info->ma = 0xffu << 24;
5254 info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0
5255 } else {
5256 info->mr = 31u << 10;
5257 info->mg = 31u << 5;
5258 info->mb = 31u << 0;
5259 }
5260 } else if (compress == 3) {
5261 info->mr = stbi__get32le(s);
5262 info->mg = stbi__get32le(s);
5263 info->mb = stbi__get32le(s);
5264 info->extra_read += 12;
5265 // not documented, but generated by photoshop and handled by mspaint
5266 if (info->mr == info->mg && info->mg == info->mb) {
5267 // ?!?!?
5268 return stbi__errpuc("bad BMP", "bad BMP");
5269 }
5270 } else
5271 return stbi__errpuc("bad BMP", "bad BMP");
5272 }
5273 } else {
5274 int i;
5275 if (hsz != 108 && hsz != 124)
5276 return stbi__errpuc("bad BMP", "bad BMP");
5277 info->mr = stbi__get32le(s);
5278 info->mg = stbi__get32le(s);
5279 info->mb = stbi__get32le(s);
5280 info->ma = stbi__get32le(s);
5281 stbi__get32le(s); // discard color space
5282 for (i=0; i < 12; ++i)
5283 stbi__get32le(s); // discard color space parameters
5284 if (hsz == 124) {
5285 stbi__get32le(s); // discard rendering intent
5286 stbi__get32le(s); // discard offset of profile data
5287 stbi__get32le(s); // discard size of profile data
5288 stbi__get32le(s); // discard reserved
5289 }
5290 }
5291 }
5292 return (void *) 1;
5293}
5294
5295
5296static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri)
5297{
5298 stbi_uc *out;
5299 unsigned int mr=0,mg=0,mb=0,ma=0, all_a;
5300 stbi_uc pal[256][4];
5301 int psize=0,i,j,width;
5302 int flip_vertically, pad, target;
5303 stbi__bmp_data info;
5304 STBI_NOTUSED(ri);
5305
5306 info.all_a = 255;
5307 if (stbi__bmp_parse_header(s, &info) == NULL)
5308 return NULL; // error code already set
5309
5310 flip_vertically = ((int) s->img_y) > 0;
5311 s->img_y = abs((int) s->img_y);
5312
5313 mr = info.mr;
5314 mg = info.mg;
5315 mb = info.mb;
5316 ma = info.ma;
5317 all_a = info.all_a;
5318
5319 if (info.hsz == 12) {
5320 if (info.bpp < 24)
5321 psize = (info.offset - info.extra_read - 24) / 3;
5322 } else {
5323 if (info.bpp < 16)
5324 psize = (info.offset - info.extra_read - info.hsz) >> 2;
5325 }
5326 if (psize == 0) {
5327 STBI_ASSERT(info.offset == (s->img_buffer - s->buffer_start));
5328 }
5329
5330 if (info.bpp == 24 && ma == 0xff000000)
5331 s->img_n = 3;
5332 else
5333 s->img_n = ma ? 4 : 3;
5334 if (req_comp && req_comp >= 3) // we can directly decode 3 or 4
5335 target = req_comp;
5336 else
5337 target = s->img_n; // if they want monochrome, we'll post-convert
5338
5339 // sanity-check size
5340 if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0))
5341 return stbi__errpuc("too large", "Corrupt BMP");
5342
5343 out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0);
5344 if (!out) return stbi__errpuc("outofmem", "Out of memory");
5345 if (info.bpp < 16) {
5346 int z=0;
5347 if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); }
5348 for (i=0; i < psize; ++i) {
5349 pal[i][2] = stbi__get8(s);
5350 pal[i][1] = stbi__get8(s);
5351 pal[i][0] = stbi__get8(s);
5352 if (info.hsz != 12) stbi__get8(s);
5353 pal[i][3] = 255;
5354 }
5355 stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4));
5356 if (info.bpp == 1) width = (s->img_x + 7) >> 3;
5357 else if (info.bpp == 4) width = (s->img_x + 1) >> 1;
5358 else if (info.bpp == 8) width = s->img_x;
5359 else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); }
5360 pad = (-width)&3;
5361 if (info.bpp == 1) {
5362 for (j=0; j < (int) s->img_y; ++j) {
5363 int bit_offset = 7, v = stbi__get8(s);
5364 for (i=0; i < (int) s->img_x; ++i) {
5365 int color = (v>>bit_offset)&0x1;
5366 out[z++] = pal[color][0];
5367 out[z++] = pal[color][1];
5368 out[z++] = pal[color][2];
5369 if (target == 4) out[z++] = 255;
5370 if (i+1 == (int) s->img_x) break;
5371 if((--bit_offset) < 0) {
5372 bit_offset = 7;
5373 v = stbi__get8(s);
5374 }
5375 }
5376 stbi__skip(s, pad);
5377 }
5378 } else {
5379 for (j=0; j < (int) s->img_y; ++j) {
5380 for (i=0; i < (int) s->img_x; i += 2) {
5381 int v=stbi__get8(s),v2=0;
5382 if (info.bpp == 4) {
5383 v2 = v & 15;
5384 v >>= 4;
5385 }
5386 out[z++] = pal[v][0];
5387 out[z++] = pal[v][1];
5388 out[z++] = pal[v][2];
5389 if (target == 4) out[z++] = 255;
5390 if (i+1 == (int) s->img_x) break;
5391 v = (info.bpp == 8) ? stbi__get8(s) : v2;
5392 out[z++] = pal[v][0];
5393 out[z++] = pal[v][1];
5394 out[z++] = pal[v][2];
5395 if (target == 4) out[z++] = 255;
5396 }
5397 stbi__skip(s, pad);
5398 }
5399 }
5400 } else {
5401 int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0;
5402 int z = 0;
5403 int easy=0;
5404 stbi__skip(s, info.offset - info.extra_read - info.hsz);
5405 if (info.bpp == 24) width = 3 * s->img_x;
5406 else if (info.bpp == 16) width = 2*s->img_x;
5407 else /* bpp = 32 and pad = 0 */ width=0;
5408 pad = (-width) & 3;
5409 if (info.bpp == 24) {
5410 easy = 1;
5411 } else if (info.bpp == 32) {
5412 if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000)
5413 easy = 2;
5414 }
5415 if (!easy) {
5416 if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); }
5417 // right shift amt to put high bit in position #7
5418 rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr);
5419 gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg);
5420 bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb);
5421 ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma);
5422 }
5423 for (j=0; j < (int) s->img_y; ++j) {
5424 if (easy) {
5425 for (i=0; i < (int) s->img_x; ++i) {
5426 unsigned char a;
5427 out[z+2] = stbi__get8(s);
5428 out[z+1] = stbi__get8(s);
5429 out[z+0] = stbi__get8(s);
5430 z += 3;
5431 a = (easy == 2 ? stbi__get8(s) : 255);
5432 all_a |= a;
5433 if (target == 4) out[z++] = a;
5434 }
5435 } else {
5436 int bpp = info.bpp;
5437 for (i=0; i < (int) s->img_x; ++i) {
5438 stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s));
5439 unsigned int a;
5440 out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount));
5441 out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount));
5442 out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount));
5443 a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255);
5444 all_a |= a;
5445 if (target == 4) out[z++] = STBI__BYTECAST(a);
5446 }
5447 }
5448 stbi__skip(s, pad);
5449 }
5450 }
5451
5452 // if alpha channel is all 0s, replace with all 255s
5453 if (target == 4 && all_a == 0)
5454 for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4)
5455 out[i] = 255;
5456
5457 if (flip_vertically) {
5458 stbi_uc t;
5459 for (j=0; j < (int) s->img_y>>1; ++j) {
5460 stbi_uc *p1 = out + j *s->img_x*target;
5461 stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target;
5462 for (i=0; i < (int) s->img_x*target; ++i) {
5463 t = p1[i]; p1[i] = p2[i]; p2[i] = t;
5464 }
5465 }
5466 }
5467
5468 if (req_comp && req_comp != target) {
5469 out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y);
5470 if (out == NULL) return out; // stbi__convert_format frees input on failure
5471 }
5472
5473 *x = s->img_x;
5474 *y = s->img_y;
5475 if (comp) *comp = s->img_n;
5476 return out;
5477}
5478#endif
5479
5480// Targa Truevision - TGA
5481// by Jonathan Dummer
5482#ifndef STBI_NO_TGA
5483// returns STBI_rgb or whatever, 0 on error
5484static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16)
5485{
5486 // only RGB or RGBA (incl. 16bit) or grey allowed
5487 if (is_rgb16) *is_rgb16 = 0;
5488 switch(bits_per_pixel) {
5489 case 8: return STBI_grey;
5490 case 16: if(is_grey) return STBI_grey_alpha;
5491 // fallthrough
5492 case 15: if(is_rgb16) *is_rgb16 = 1;
5493 return STBI_rgb;
5494 case 24: // fallthrough
5495 case 32: return bits_per_pixel/8;
5496 default: return 0;
5497 }
5498}
5499
5500static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp)
5501{
5502 int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp;
5503 int sz, tga_colormap_type;
5504 stbi__get8(s); // discard Offset
5505 tga_colormap_type = stbi__get8(s); // colormap type
5506 if( tga_colormap_type > 1 ) {
5507 stbi__rewind(s);
5508 return 0; // only RGB or indexed allowed
5509 }
5510 tga_image_type = stbi__get8(s); // image type
5511 if ( tga_colormap_type == 1 ) { // colormapped (paletted) image
5512 if (tga_image_type != 1 && tga_image_type != 9) {
5513 stbi__rewind(s);
5514 return 0;
5515 }
5516 stbi__skip(s,4); // skip index of first colormap entry and number of entries
5517 sz = stbi__get8(s); // check bits per palette color entry
5518 if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) {
5519 stbi__rewind(s);
5520 return 0;
5521 }
5522 stbi__skip(s,4); // skip image x and y origin
5523 tga_colormap_bpp = sz;
5524 } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE
5525 if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) {
5526 stbi__rewind(s);
5527 return 0; // only RGB or grey allowed, +/- RLE
5528 }
5529 stbi__skip(s,9); // skip colormap specification and image x/y origin
5530 tga_colormap_bpp = 0;
5531 }
5532 tga_w = stbi__get16le(s);
5533 if( tga_w < 1 ) {
5534 stbi__rewind(s);
5535 return 0; // test width
5536 }
5537 tga_h = stbi__get16le(s);
5538 if( tga_h < 1 ) {
5539 stbi__rewind(s);
5540 return 0; // test height
5541 }
5542 tga_bits_per_pixel = stbi__get8(s); // bits per pixel
5543 stbi__get8(s); // ignore alpha bits
5544 if (tga_colormap_bpp != 0) {
5545 if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) {
5546 // when using a colormap, tga_bits_per_pixel is the size of the indexes
5547 // I don't think anything but 8 or 16bit indexes makes sense
5548 stbi__rewind(s);
5549 return 0;
5550 }
5551 tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL);
5552 } else {
5553 tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL);
5554 }
5555 if(!tga_comp) {
5556 stbi__rewind(s);
5557 return 0;
5558 }
5559 if (x) *x = tga_w;
5560 if (y) *y = tga_h;
5561 if (comp) *comp = tga_comp;
5562 return 1; // seems to have passed everything
5563}
5564
5565static int stbi__tga_test(stbi__context *s)
5566{
5567 int res = 0;
5568 int sz, tga_color_type;
5569 stbi__get8(s); // discard Offset
5570 tga_color_type = stbi__get8(s); // color type
5571 if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed
5572 sz = stbi__get8(s); // image type
5573 if ( tga_color_type == 1 ) { // colormapped (paletted) image
5574 if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9
5575 stbi__skip(s,4); // skip index of first colormap entry and number of entries
5576 sz = stbi__get8(s); // check bits per palette color entry
5577 if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd;
5578 stbi__skip(s,4); // skip image x and y origin
5579 } else { // "normal" image w/o colormap
5580 if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE
5581 stbi__skip(s,9); // skip colormap specification and image x/y origin
5582 }
5583 if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width
5584 if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height
5585 sz = stbi__get8(s); // bits per pixel
5586 if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index
5587 if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd;
5588
5589 res = 1; // if we got this far, everything's good and we can return 1 instead of 0
5590
5591errorEnd:
5592 stbi__rewind(s);
5593 return res;
5594}
5595
5596// read 16bit value and convert to 24bit RGB
5597static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out)
5598{
5599 stbi__uint16 px = (stbi__uint16)stbi__get16le(s);
5600 stbi__uint16 fiveBitMask = 31;
5601 // we have 3 channels with 5bits each
5602 int r = (px >> 10) & fiveBitMask;
5603 int g = (px >> 5) & fiveBitMask;
5604 int b = px & fiveBitMask;
5605 // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later
5606 out[0] = (stbi_uc)((r * 255)/31);
5607 out[1] = (stbi_uc)((g * 255)/31);
5608 out[2] = (stbi_uc)((b * 255)/31);
5609
5610 // some people claim that the most significant bit might be used for alpha
5611 // (possibly if an alpha-bit is set in the "image descriptor byte")
5612 // but that only made 16bit test images completely translucent..
5613 // so let's treat all 15 and 16bit TGAs as RGB with no alpha.
5614}
5615
5616static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri)
5617{
5618 // read in the TGA header stuff
5619 int tga_offset = stbi__get8(s);
5620 int tga_indexed = stbi__get8(s);
5621 int tga_image_type = stbi__get8(s);
5622 int tga_is_RLE = 0;
5623 int tga_palette_start = stbi__get16le(s);
5624 int tga_palette_len = stbi__get16le(s);
5625 int tga_palette_bits = stbi__get8(s);
5626 int tga_x_origin = stbi__get16le(s);
5627 int tga_y_origin = stbi__get16le(s);
5628 int tga_width = stbi__get16le(s);
5629 int tga_height = stbi__get16le(s);
5630 int tga_bits_per_pixel = stbi__get8(s);
5631 int tga_comp, tga_rgb16=0;
5632 int tga_inverted = stbi__get8(s);
5633 // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?)
5634 // image data
5635 unsigned char *tga_data;
5636 unsigned char *tga_palette = NULL;
5637 int i, j;
5638 unsigned char raw_data[4] = {0};
5639 int RLE_count = 0;
5640 int RLE_repeating = 0;
5641 int read_next_pixel = 1;
5642 STBI_NOTUSED(ri);
5643 STBI_NOTUSED(tga_x_origin); // @TODO
5644 STBI_NOTUSED(tga_y_origin); // @TODO
5645
5646 // do a tiny bit of precessing
5647 if ( tga_image_type >= 8 )
5648 {
5649 tga_image_type -= 8;
5650 tga_is_RLE = 1;
5651 }
5652 tga_inverted = 1 - ((tga_inverted >> 5) & 1);
5653
5654 // If I'm paletted, then I'll use the number of bits from the palette
5655 if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16);
5656 else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16);
5657
5658 if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency
5659 return stbi__errpuc("bad format", "Can't find out TGA pixelformat");
5660
5661 // tga info
5662 *x = tga_width;
5663 *y = tga_height;
5664 if (comp) *comp = tga_comp;
5665
5666 if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0))
5667 return stbi__errpuc("too large", "Corrupt TGA");
5668
5669 tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0);
5670 if (!tga_data) return stbi__errpuc("outofmem", "Out of memory");
5671
5672 // skip to the data's starting position (offset usually = 0)
5673 stbi__skip(s, tga_offset );
5674
5675 if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) {
5676 for (i=0; i < tga_height; ++i) {
5677 int row = tga_inverted ? tga_height -i - 1 : i;
5678 stbi_uc *tga_row = tga_data + row*tga_width*tga_comp;
5679 stbi__getn(s, tga_row, tga_width * tga_comp);
5680 }
5681 } else {
5682 // do I need to load a palette?
5683 if ( tga_indexed)
5684 {
5685 // any data to skip? (offset usually = 0)
5686 stbi__skip(s, tga_palette_start );
5687 // load the palette
5688 tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0);
5689 if (!tga_palette) {
5690 STBI_FREE(tga_data);
5691 return stbi__errpuc("outofmem", "Out of memory");
5692 }
5693 if (tga_rgb16) {
5694 stbi_uc *pal_entry = tga_palette;
5695 STBI_ASSERT(tga_comp == STBI_rgb);
5696 for (i=0; i < tga_palette_len; ++i) {
5697 stbi__tga_read_rgb16(s, pal_entry);
5698 pal_entry += tga_comp;
5699 }
5700 } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) {
5701 STBI_FREE(tga_data);
5702 STBI_FREE(tga_palette);
5703 return stbi__errpuc("bad palette", "Corrupt TGA");
5704 }
5705 }
5706 // load the data
5707 for (i=0; i < tga_width * tga_height; ++i)
5708 {
5709 // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk?
5710 if ( tga_is_RLE )
5711 {
5712 if ( RLE_count == 0 )
5713 {
5714 // yep, get the next byte as a RLE command
5715 int RLE_cmd = stbi__get8(s);
5716 RLE_count = 1 + (RLE_cmd & 127);
5717 RLE_repeating = RLE_cmd >> 7;
5718 read_next_pixel = 1;
5719 } else if ( !RLE_repeating )
5720 {
5721 read_next_pixel = 1;
5722 }
5723 } else
5724 {
5725 read_next_pixel = 1;
5726 }
5727 // OK, if I need to read a pixel, do it now
5728 if ( read_next_pixel )
5729 {
5730 // load however much data we did have
5731 if ( tga_indexed )
5732 {
5733 // read in index, then perform the lookup
5734 int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s);
5735 if ( pal_idx >= tga_palette_len ) {
5736 // invalid index
5737 pal_idx = 0;
5738 }
5739 pal_idx *= tga_comp;
5740 for (j = 0; j < tga_comp; ++j) {
5741 raw_data[j] = tga_palette[pal_idx+j];
5742 }
5743 } else if(tga_rgb16) {
5744 STBI_ASSERT(tga_comp == STBI_rgb);
5745 stbi__tga_read_rgb16(s, raw_data);
5746 } else {
5747 // read in the data raw
5748 for (j = 0; j < tga_comp; ++j) {
5749 raw_data[j] = stbi__get8(s);
5750 }
5751 }
5752 // clear the reading flag for the next pixel
5753 read_next_pixel = 0;
5754 } // end of reading a pixel
5755
5756 // copy data
5757 for (j = 0; j < tga_comp; ++j)
5758 tga_data[i*tga_comp+j] = raw_data[j];
5759
5760 // in case we're in RLE mode, keep counting down
5761 --RLE_count;
5762 }
5763 // do I need to invert the image?
5764 if ( tga_inverted )
5765 {
5766 for (j = 0; j*2 < tga_height; ++j)
5767 {
5768 int index1 = j * tga_width * tga_comp;
5769 int index2 = (tga_height - 1 - j) * tga_width * tga_comp;
5770 for (i = tga_width * tga_comp; i > 0; --i)
5771 {
5772 unsigned char temp = tga_data[index1];
5773 tga_data[index1] = tga_data[index2];
5774 tga_data[index2] = temp;
5775 ++index1;
5776 ++index2;
5777 }
5778 }
5779 }
5780 // clear my palette, if I had one
5781 if ( tga_palette != NULL )
5782 {
5783 STBI_FREE( tga_palette );
5784 }
5785 }
5786
5787 // swap RGB - if the source data was RGB16, it already is in the right order
5788 if (tga_comp >= 3 && !tga_rgb16)
5789 {
5790 unsigned char* tga_pixel = tga_data;
5791 for (i=0; i < tga_width * tga_height; ++i)
5792 {
5793 unsigned char temp = tga_pixel[0];
5794 tga_pixel[0] = tga_pixel[2];
5795 tga_pixel[2] = temp;
5796 tga_pixel += tga_comp;
5797 }
5798 }
5799
5800 // convert to target component count
5801 if (req_comp && req_comp != tga_comp)
5802 tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height);
5803
5804 // the things I do to get rid of an error message, and yet keep
5805 // Microsoft's C compilers happy... [8^(
5806 tga_palette_start = tga_palette_len = tga_palette_bits =
5807 tga_x_origin = tga_y_origin = 0;
5808 STBI_NOTUSED(tga_palette_start);
5809 // OK, done
5810 return tga_data;
5811}
5812#endif
5813
5814// *************************************************************************************************
5815// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB
5816
5817#ifndef STBI_NO_PSD
5818static int stbi__psd_test(stbi__context *s)
5819{
5820 int r = (stbi__get32be(s) == 0x38425053);
5821 stbi__rewind(s);
5822 return r;
5823}
5824
5825static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount)
5826{
5827 int count, nleft, len;
5828
5829 count = 0;
5830 while ((nleft = pixelCount - count) > 0) {
5831 len = stbi__get8(s);
5832 if (len == 128) {
5833 // No-op.
5834 } else if (len < 128) {
5835 // Copy next len+1 bytes literally.
5836 len++;
5837 if (len > nleft) return 0; // corrupt data
5838 count += len;
5839 while (len) {
5840 *p = stbi__get8(s);
5841 p += 4;
5842 len--;
5843 }
5844 } else if (len > 128) {
5845 stbi_uc val;
5846 // Next -len+1 bytes in the dest are replicated from next source byte.
5847 // (Interpret len as a negative 8-bit int.)
5848 len = 257 - len;
5849 if (len > nleft) return 0; // corrupt data
5850 val = stbi__get8(s);
5851 count += len;
5852 while (len) {
5853 *p = val;
5854 p += 4;
5855 len--;
5856 }
5857 }
5858 }
5859
5860 return 1;
5861}
5862
5863static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc)
5864{
5865 int pixelCount;
5866 int channelCount, compression;
5867 int channel, i;
5868 int bitdepth;
5869 int w,h;
5870 stbi_uc *out;
5871 STBI_NOTUSED(ri);
5872
5873 // Check identifier
5874 if (stbi__get32be(s) != 0x38425053) // "8BPS"
5875 return stbi__errpuc("not PSD", "Corrupt PSD image");
5876
5877 // Check file type version.
5878 if (stbi__get16be(s) != 1)
5879 return stbi__errpuc("wrong version", "Unsupported version of PSD image");
5880
5881 // Skip 6 reserved bytes.
5882 stbi__skip(s, 6 );
5883
5884 // Read the number of channels (R, G, B, A, etc).
5885 channelCount = stbi__get16be(s);
5886 if (channelCount < 0 || channelCount > 16)
5887 return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image");
5888
5889 // Read the rows and columns of the image.
5890 h = stbi__get32be(s);
5891 w = stbi__get32be(s);
5892
5893 // Make sure the depth is 8 bits.
5894 bitdepth = stbi__get16be(s);
5895 if (bitdepth != 8 && bitdepth != 16)
5896 return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit");
5897
5898 // Make sure the color mode is RGB.
5899 // Valid options are:
5900 // 0: Bitmap
5901 // 1: Grayscale
5902 // 2: Indexed color
5903 // 3: RGB color
5904 // 4: CMYK color
5905 // 7: Multichannel
5906 // 8: Duotone
5907 // 9: Lab color
5908 if (stbi__get16be(s) != 3)
5909 return stbi__errpuc("wrong color format", "PSD is not in RGB color format");
5910
5911 // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.)
5912 stbi__skip(s,stbi__get32be(s) );
5913
5914 // Skip the image resources. (resolution, pen tool paths, etc)
5915 stbi__skip(s, stbi__get32be(s) );
5916
5917 // Skip the reserved data.
5918 stbi__skip(s, stbi__get32be(s) );
5919
5920 // Find out if the data is compressed.
5921 // Known values:
5922 // 0: no compression
5923 // 1: RLE compressed
5924 compression = stbi__get16be(s);
5925 if (compression > 1)
5926 return stbi__errpuc("bad compression", "PSD has an unknown compression format");
5927
5928 // Check size
5929 if (!stbi__mad3sizes_valid(4, w, h, 0))
5930 return stbi__errpuc("too large", "Corrupt PSD");
5931
5932 // Create the destination image.
5933
5934 if (!compression && bitdepth == 16 && bpc == 16) {
5935 out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0);
5936 ri->bits_per_channel = 16;
5937 } else
5938 out = (stbi_uc *) stbi__malloc(4 * w*h);
5939
5940 if (!out) return stbi__errpuc("outofmem", "Out of memory");
5941 pixelCount = w*h;
5942
5943 // Initialize the data to zero.
5944 //memset( out, 0, pixelCount * 4 );
5945
5946 // Finally, the image data.
5947 if (compression) {
5948 // RLE as used by .PSD and .TIFF
5949 // Loop until you get the number of unpacked bytes you are expecting:
5950 // Read the next source byte into n.
5951 // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally.
5952 // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times.
5953 // Else if n is 128, noop.
5954 // Endloop
5955
5956 // The RLE-compressed data is preceded by a 2-byte data count for each row in the data,
5957 // which we're going to just skip.
5958 stbi__skip(s, h * channelCount * 2 );
5959
5960 // Read the RLE data by channel.
5961 for (channel = 0; channel < 4; channel++) {
5962 stbi_uc *p;
5963
5964 p = out+channel;
5965 if (channel >= channelCount) {
5966 // Fill this channel with default data.
5967 for (i = 0; i < pixelCount; i++, p += 4)
5968 *p = (channel == 3 ? 255 : 0);
5969 } else {
5970 // Read the RLE data.
5971 if (!stbi__psd_decode_rle(s, p, pixelCount)) {
5972 STBI_FREE(out);
5973 return stbi__errpuc("corrupt", "bad RLE data");
5974 }
5975 }
5976 }
5977
5978 } else {
5979 // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...)
5980 // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image.
5981
5982 // Read the data by channel.
5983 for (channel = 0; channel < 4; channel++) {
5984 if (channel >= channelCount) {
5985 // Fill this channel with default data.
5986 if (bitdepth == 16 && bpc == 16) {
5987 stbi__uint16 *q = ((stbi__uint16 *) out) + channel;
5988 stbi__uint16 val = channel == 3 ? 65535 : 0;
5989 for (i = 0; i < pixelCount; i++, q += 4)
5990 *q = val;
5991 } else {
5992 stbi_uc *p = out+channel;
5993 stbi_uc val = channel == 3 ? 255 : 0;
5994 for (i = 0; i < pixelCount; i++, p += 4)
5995 *p = val;
5996 }
5997 } else {
5998 if (ri->bits_per_channel == 16) { // output bpc
5999 stbi__uint16 *q = ((stbi__uint16 *) out) + channel;
6000 for (i = 0; i < pixelCount; i++, q += 4)
6001 *q = (stbi__uint16) stbi__get16be(s);
6002 } else {
6003 stbi_uc *p = out+channel;
6004 if (bitdepth == 16) { // input bpc
6005 for (i = 0; i < pixelCount; i++, p += 4)
6006 *p = (stbi_uc) (stbi__get16be(s) >> 8);
6007 } else {
6008 for (i = 0; i < pixelCount; i++, p += 4)
6009 *p = stbi__get8(s);
6010 }
6011 }
6012 }
6013 }
6014 }
6015
6016 // remove weird white matte from PSD
6017 if (channelCount >= 4) {
6018 if (ri->bits_per_channel == 16) {
6019 for (i=0; i < w*h; ++i) {
6020 stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i;
6021 if (pixel[3] != 0 && pixel[3] != 65535) {
6022 float a = pixel[3] / 65535.0f;
6023 float ra = 1.0f / a;
6024 float inv_a = 65535.0f * (1 - ra);
6025 pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a);
6026 pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a);
6027 pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a);
6028 }
6029 }
6030 } else {
6031 for (i=0; i < w*h; ++i) {
6032 unsigned char *pixel = out + 4*i;
6033 if (pixel[3] != 0 && pixel[3] != 255) {
6034 float a = pixel[3] / 255.0f;
6035 float ra = 1.0f / a;
6036 float inv_a = 255.0f * (1 - ra);
6037 pixel[0] = (unsigned char) (pixel[0]*ra + inv_a);
6038 pixel[1] = (unsigned char) (pixel[1]*ra + inv_a);
6039 pixel[2] = (unsigned char) (pixel[2]*ra + inv_a);
6040 }
6041 }
6042 }
6043 }
6044
6045 // convert to desired output format
6046 if (req_comp && req_comp != 4) {
6047 if (ri->bits_per_channel == 16)
6048 out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h);
6049 else
6050 out = stbi__convert_format(out, 4, req_comp, w, h);
6051 if (out == NULL) return out; // stbi__convert_format frees input on failure
6052 }
6053
6054 if (comp) *comp = 4;
6055 *y = h;
6056 *x = w;
6057
6058 return out;
6059}
6060#endif
6061
6062// *************************************************************************************************
6063// Softimage PIC loader
6064// by Tom Seddon
6065//
6066// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format
6067// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/
6068
6069#ifndef STBI_NO_PIC
6070static int stbi__pic_is4(stbi__context *s,const char *str)
6071{
6072 int i;
6073 for (i=0; i<4; ++i)
6074 if (stbi__get8(s) != (stbi_uc)str[i])
6075 return 0;
6076
6077 return 1;
6078}
6079
6080static int stbi__pic_test_core(stbi__context *s)
6081{
6082 int i;
6083
6084 if (!stbi__pic_is4(s,"\x53\x80\xF6\x34"))
6085 return 0;
6086
6087 for(i=0;i<84;++i)
6088 stbi__get8(s);
6089
6090 if (!stbi__pic_is4(s,"PICT"))
6091 return 0;
6092
6093 return 1;
6094}
6095
6096typedef struct
6097{
6098 stbi_uc size,type,channel;
6099} stbi__pic_packet;
6100
6101static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest)
6102{
6103 int mask=0x80, i;
6104
6105 for (i=0; i<4; ++i, mask>>=1) {
6106 if (channel & mask) {
6107 if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short");
6108 dest[i]=stbi__get8(s);
6109 }
6110 }
6111
6112 return dest;
6113}
6114
6115static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src)
6116{
6117 int mask=0x80,i;
6118
6119 for (i=0;i<4; ++i, mask>>=1)
6120 if (channel&mask)
6121 dest[i]=src[i];
6122}
6123
6124static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result)
6125{
6126 int act_comp=0,num_packets=0,y,chained;
6127 stbi__pic_packet packets[10];
6128
6129 // this will (should...) cater for even some bizarre stuff like having data
6130 // for the same channel in multiple packets.
6131 do {
6132 stbi__pic_packet *packet;
6133
6134 if (num_packets==sizeof(packets)/sizeof(packets[0]))
6135 return stbi__errpuc("bad format","too many packets");
6136
6137 packet = &packets[num_packets++];
6138
6139 chained = stbi__get8(s);
6140 packet->size = stbi__get8(s);
6141 packet->type = stbi__get8(s);
6142 packet->channel = stbi__get8(s);
6143
6144 act_comp |= packet->channel;
6145
6146 if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)");
6147 if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp");
6148 } while (chained);
6149
6150 *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel?
6151
6152 for(y=0; y<height; ++y) {
6153 int packet_idx;
6154
6155 for(packet_idx=0; packet_idx < num_packets; ++packet_idx) {
6156 stbi__pic_packet *packet = &packets[packet_idx];
6157 stbi_uc *dest = result+y*width*4;
6158
6159 switch (packet->type) {
6160 default:
6161 return stbi__errpuc("bad format","packet has bad compression type");
6162
6163 case 0: {//uncompressed
6164 int x;
6165
6166 for(x=0;x<width;++x, dest+=4)
6167 if (!stbi__readval(s,packet->channel,dest))
6168 return 0;
6169 break;
6170 }
6171
6172 case 1://Pure RLE
6173 {
6174 int left=width, i;
6175
6176 while (left>0) {
6177 stbi_uc count,value[4];
6178
6179 count=stbi__get8(s);
6180 if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)");
6181
6182 if (count > left)
6183 count = (stbi_uc) left;
6184
6185 if (!stbi__readval(s,packet->channel,value)) return 0;
6186
6187 for(i=0; i<count; ++i,dest+=4)
6188 stbi__copyval(packet->channel,dest,value);
6189 left -= count;
6190 }
6191 }
6192 break;
6193
6194 case 2: {//Mixed RLE
6195 int left=width;
6196 while (left>0) {
6197 int count = stbi__get8(s), i;
6198 if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)");
6199
6200 if (count >= 128) { // Repeated
6201 stbi_uc value[4];
6202
6203 if (count==128)
6204 count = stbi__get16be(s);
6205 else
6206 count -= 127;
6207 if (count > left)
6208 return stbi__errpuc("bad file","scanline overrun");
6209
6210 if (!stbi__readval(s,packet->channel,value))
6211 return 0;
6212
6213 for(i=0;i<count;++i, dest += 4)
6214 stbi__copyval(packet->channel,dest,value);
6215 } else { // Raw
6216 ++count;
6217 if (count>left) return stbi__errpuc("bad file","scanline overrun");
6218
6219 for(i=0;i<count;++i, dest+=4)
6220 if (!stbi__readval(s,packet->channel,dest))
6221 return 0;
6222 }
6223 left-=count;
6224 }
6225 break;
6226 }
6227 }
6228 }
6229 }
6230
6231 return result;
6232}
6233
6234static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri)
6235{
6236 stbi_uc *result;
6237 int i, x,y, internal_comp;
6238 STBI_NOTUSED(ri);
6239
6240 if (!comp) comp = &internal_comp;
6241
6242 for (i=0; i<92; ++i)
6243 stbi__get8(s);
6244
6245 x = stbi__get16be(s);
6246 y = stbi__get16be(s);
6247 if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)");
6248 if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode");
6249
6250 stbi__get32be(s); //skip `ratio'
6251 stbi__get16be(s); //skip `fields'
6252 stbi__get16be(s); //skip `pad'
6253
6254 // intermediate buffer is RGBA
6255 result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0);
6256 memset(result, 0xff, x*y*4);
6257
6258 if (!stbi__pic_load_core(s,x,y,comp, result)) {
6259 STBI_FREE(result);
6260 result=0;
6261 }
6262 *px = x;
6263 *py = y;
6264 if (req_comp == 0) req_comp = *comp;
6265 result=stbi__convert_format(result,4,req_comp,x,y);
6266
6267 return result;
6268}
6269
6270static int stbi__pic_test(stbi__context *s)
6271{
6272 int r = stbi__pic_test_core(s);
6273 stbi__rewind(s);
6274 return r;
6275}
6276#endif
6277
6278// *************************************************************************************************
6279// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb
6280
6281#ifndef STBI_NO_GIF
6282typedef struct
6283{
6284 stbi__int16 prefix;
6285 stbi_uc first;
6286 stbi_uc suffix;
6287} stbi__gif_lzw;
6288
6289typedef struct
6290{
6291 int w,h;
6292 stbi_uc *out; // output buffer (always 4 components)
6293 stbi_uc *background; // The current "background" as far as a gif is concerned
6294 stbi_uc *history;
6295 int flags, bgindex, ratio, transparent, eflags;
6296 stbi_uc pal[256][4];
6297 stbi_uc lpal[256][4];
6298 stbi__gif_lzw codes[8192];
6299 stbi_uc *color_table;
6300 int parse, step;
6301 int lflags;
6302 int start_x, start_y;
6303 int max_x, max_y;
6304 int cur_x, cur_y;
6305 int line_size;
6306 int delay;
6307} stbi__gif;
6308
6309static int stbi__gif_test_raw(stbi__context *s)
6310{
6311 int sz;
6312 if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0;
6313 sz = stbi__get8(s);
6314 if (sz != '9' && sz != '7') return 0;
6315 if (stbi__get8(s) != 'a') return 0;
6316 return 1;
6317}
6318
6319static int stbi__gif_test(stbi__context *s)
6320{
6321 int r = stbi__gif_test_raw(s);
6322 stbi__rewind(s);
6323 return r;
6324}
6325
6326static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp)
6327{
6328 int i;
6329 for (i=0; i < num_entries; ++i) {
6330 pal[i][2] = stbi__get8(s);
6331 pal[i][1] = stbi__get8(s);
6332 pal[i][0] = stbi__get8(s);
6333 pal[i][3] = transp == i ? 0 : 255;
6334 }
6335}
6336
6337static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info)
6338{
6339 stbi_uc version;
6340 if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8')
6341 return stbi__err("not GIF", "Corrupt GIF");
6342
6343 version = stbi__get8(s);
6344 if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF");
6345 if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF");
6346
6347 stbi__g_failure_reason = "";
6348 g->w = stbi__get16le(s);
6349 g->h = stbi__get16le(s);
6350 g->flags = stbi__get8(s);
6351 g->bgindex = stbi__get8(s);
6352 g->ratio = stbi__get8(s);
6353 g->transparent = -1;
6354
6355 if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments
6356
6357 if (is_info) return 1;
6358
6359 if (g->flags & 0x80)
6360 stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1);
6361
6362 return 1;
6363}
6364
6365static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp)
6366{
6367 stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif));
6368 if (!stbi__gif_header(s, g, comp, 1)) {
6369 STBI_FREE(g);
6370 stbi__rewind( s );
6371 return 0;
6372 }
6373 if (x) *x = g->w;
6374 if (y) *y = g->h;
6375 STBI_FREE(g);
6376 return 1;
6377}
6378
6379static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code)
6380{
6381 stbi_uc *p, *c;
6382 int idx;
6383
6384 // recurse to decode the prefixes, since the linked-list is backwards,
6385 // and working backwards through an interleaved image would be nasty
6386 if (g->codes[code].prefix >= 0)
6387 stbi__out_gif_code(g, g->codes[code].prefix);
6388
6389 if (g->cur_y >= g->max_y) return;
6390
6391 idx = g->cur_x + g->cur_y;
6392 p = &g->out[idx];
6393 g->history[idx / 4] = 1;
6394
6395 c = &g->color_table[g->codes[code].suffix * 4];
6396 if (c[3] > 128) { // don't render transparent pixels;
6397 p[0] = c[2];
6398 p[1] = c[1];
6399 p[2] = c[0];
6400 p[3] = c[3];
6401 }
6402 g->cur_x += 4;
6403
6404 if (g->cur_x >= g->max_x) {
6405 g->cur_x = g->start_x;
6406 g->cur_y += g->step;
6407
6408 while (g->cur_y >= g->max_y && g->parse > 0) {
6409 g->step = (1 << g->parse) * g->line_size;
6410 g->cur_y = g->start_y + (g->step >> 1);
6411 --g->parse;
6412 }
6413 }
6414}
6415
6416static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g)
6417{
6418 stbi_uc lzw_cs;
6419 stbi__int32 len, init_code;
6420 stbi__uint32 first;
6421 stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear;
6422 stbi__gif_lzw *p;
6423
6424 lzw_cs = stbi__get8(s);
6425 if (lzw_cs > 12) return NULL;
6426 clear = 1 << lzw_cs;
6427 first = 1;
6428 codesize = lzw_cs + 1;
6429 codemask = (1 << codesize) - 1;
6430 bits = 0;
6431 valid_bits = 0;
6432 for (init_code = 0; init_code < clear; init_code++) {
6433 g->codes[init_code].prefix = -1;
6434 g->codes[init_code].first = (stbi_uc) init_code;
6435 g->codes[init_code].suffix = (stbi_uc) init_code;
6436 }
6437
6438 // support no starting clear code
6439 avail = clear+2;
6440 oldcode = -1;
6441
6442 len = 0;
6443 for(;;) {
6444 if (valid_bits < codesize) {
6445 if (len == 0) {
6446 len = stbi__get8(s); // start new block
6447 if (len == 0)
6448 return g->out;
6449 }
6450 --len;
6451 bits |= (stbi__int32) stbi__get8(s) << valid_bits;
6452 valid_bits += 8;
6453 } else {
6454 stbi__int32 code = bits & codemask;
6455 bits >>= codesize;
6456 valid_bits -= codesize;
6457 // @OPTIMIZE: is there some way we can accelerate the non-clear path?
6458 if (code == clear) { // clear code
6459 codesize = lzw_cs + 1;
6460 codemask = (1 << codesize) - 1;
6461 avail = clear + 2;
6462 oldcode = -1;
6463 first = 0;
6464 } else if (code == clear + 1) { // end of stream code
6465 stbi__skip(s, len);
6466 while ((len = stbi__get8(s)) > 0)
6467 stbi__skip(s,len);
6468 return g->out;
6469 } else if (code <= avail) {
6470 if (first) {
6471 return stbi__errpuc("no clear code", "Corrupt GIF");
6472 }
6473
6474 if (oldcode >= 0) {
6475 p = &g->codes[avail++];
6476 if (avail > 8192) {
6477 return stbi__errpuc("too many codes", "Corrupt GIF");
6478 }
6479
6480 p->prefix = (stbi__int16) oldcode;
6481 p->first = g->codes[oldcode].first;
6482 p->suffix = (code == avail) ? p->first : g->codes[code].first;
6483 } else if (code == avail)
6484 return stbi__errpuc("illegal code in raster", "Corrupt GIF");
6485
6486 stbi__out_gif_code(g, (stbi__uint16) code);
6487
6488 if ((avail & codemask) == 0 && avail <= 0x0FFF) {
6489 codesize++;
6490 codemask = (1 << codesize) - 1;
6491 }
6492
6493 oldcode = code;
6494 } else {
6495 return stbi__errpuc("illegal code in raster", "Corrupt GIF");
6496 }
6497 }
6498 }
6499}
6500
6501// this function is designed to support animated gifs, although stb_image doesn't support it
6502// two back is the image from two frames ago, used for a very specific disposal format
6503static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back)
6504{
6505 int dispose;
6506 int first_frame;
6507 int pi;
6508 int pcount;
6509 STBI_NOTUSED(req_comp);
6510
6511 // on first frame, any non-written pixels get the background colour (non-transparent)
6512 first_frame = 0;
6513 if (g->out == 0) {
6514 if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header
6515 if (!stbi__mad3sizes_valid(4, g->w, g->h, 0))
6516 return stbi__errpuc("too large", "GIF image is too large");
6517 pcount = g->w * g->h;
6518 g->out = (stbi_uc *) stbi__malloc(4 * pcount);
6519 g->background = (stbi_uc *) stbi__malloc(4 * pcount);
6520 g->history = (stbi_uc *) stbi__malloc(pcount);
6521 if (!g->out || !g->background || !g->history)
6522 return stbi__errpuc("outofmem", "Out of memory");
6523
6524 // image is treated as "transparent" at the start - ie, nothing overwrites the current background;
6525 // background colour is only used for pixels that are not rendered first frame, after that "background"
6526 // color refers to the color that was there the previous frame.
6527 memset(g->out, 0x00, 4 * pcount);
6528 memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent)
6529 memset(g->history, 0x00, pcount); // pixels that were affected previous frame
6530 first_frame = 1;
6531 } else {
6532 // second frame - how do we dispoase of the previous one?
6533 dispose = (g->eflags & 0x1C) >> 2;
6534 pcount = g->w * g->h;
6535
6536 if ((dispose == 3) && (two_back == 0)) {
6537 dispose = 2; // if I don't have an image to revert back to, default to the old background
6538 }
6539
6540 if (dispose == 3) { // use previous graphic
6541 for (pi = 0; pi < pcount; ++pi) {
6542 if (g->history[pi]) {
6543 memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 );
6544 }
6545 }
6546 } else if (dispose == 2) {
6547 // restore what was changed last frame to background before that frame;
6548 for (pi = 0; pi < pcount; ++pi) {
6549 if (g->history[pi]) {
6550 memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 );
6551 }
6552 }
6553 } else {
6554 // This is a non-disposal case eithe way, so just
6555 // leave the pixels as is, and they will become the new background
6556 // 1: do not dispose
6557 // 0: not specified.
6558 }
6559
6560 // background is what out is after the undoing of the previou frame;
6561 memcpy( g->background, g->out, 4 * g->w * g->h );
6562 }
6563
6564 // clear my history;
6565 memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame
6566
6567 for (;;) {
6568 int tag = stbi__get8(s);
6569 switch (tag) {
6570 case 0x2C: /* Image Descriptor */
6571 {
6572 stbi__int32 x, y, w, h;
6573 stbi_uc *o;
6574
6575 x = stbi__get16le(s);
6576 y = stbi__get16le(s);
6577 w = stbi__get16le(s);
6578 h = stbi__get16le(s);
6579 if (((x + w) > (g->w)) || ((y + h) > (g->h)))
6580 return stbi__errpuc("bad Image Descriptor", "Corrupt GIF");
6581
6582 g->line_size = g->w * 4;
6583 g->start_x = x * 4;
6584 g->start_y = y * g->line_size;
6585 g->max_x = g->start_x + w * 4;
6586 g->max_y = g->start_y + h * g->line_size;
6587 g->cur_x = g->start_x;
6588 g->cur_y = g->start_y;
6589
6590 // if the width of the specified rectangle is 0, that means
6591 // we may not see *any* pixels or the image is malformed;
6592 // to make sure this is caught, move the current y down to
6593 // max_y (which is what out_gif_code checks).
6594 if (w == 0)
6595 g->cur_y = g->max_y;
6596
6597 g->lflags = stbi__get8(s);
6598
6599 if (g->lflags & 0x40) {
6600 g->step = 8 * g->line_size; // first interlaced spacing
6601 g->parse = 3;
6602 } else {
6603 g->step = g->line_size;
6604 g->parse = 0;
6605 }
6606
6607 if (g->lflags & 0x80) {
6608 stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1);
6609 g->color_table = (stbi_uc *) g->lpal;
6610 } else if (g->flags & 0x80) {
6611 g->color_table = (stbi_uc *) g->pal;
6612 } else
6613 return stbi__errpuc("missing color table", "Corrupt GIF");
6614
6615 o = stbi__process_gif_raster(s, g);
6616 if (!o) return NULL;
6617
6618 // if this was the first frame,
6619 pcount = g->w * g->h;
6620 if (first_frame && (g->bgindex > 0)) {
6621 // if first frame, any pixel not drawn to gets the background color
6622 for (pi = 0; pi < pcount; ++pi) {
6623 if (g->history[pi] == 0) {
6624 g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be;
6625 memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 );
6626 }
6627 }
6628 }
6629
6630 return o;
6631 }
6632
6633 case 0x21: // Comment Extension.
6634 {
6635 int len;
6636 int ext = stbi__get8(s);
6637 if (ext == 0xF9) { // Graphic Control Extension.
6638 len = stbi__get8(s);
6639 if (len == 4) {
6640 g->eflags = stbi__get8(s);
6641 g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths.
6642
6643 // unset old transparent
6644 if (g->transparent >= 0) {
6645 g->pal[g->transparent][3] = 255;
6646 }
6647 if (g->eflags & 0x01) {
6648 g->transparent = stbi__get8(s);
6649 if (g->transparent >= 0) {
6650 g->pal[g->transparent][3] = 0;
6651 }
6652 } else {
6653 // don't need transparent
6654 stbi__skip(s, 1);
6655 g->transparent = -1;
6656 }
6657 } else {
6658 stbi__skip(s, len);
6659 break;
6660 }
6661 }
6662 while ((len = stbi__get8(s)) != 0) {
6663 stbi__skip(s, len);
6664 }
6665 break;
6666 }
6667
6668 case 0x3B: // gif stream termination code
6669 return (stbi_uc *) s; // using '1' causes warning on some compilers
6670
6671 default:
6672 return stbi__errpuc("unknown code", "Corrupt GIF");
6673 }
6674 }
6675}
6676
6677static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp)
6678{
6679 if (stbi__gif_test(s)) {
6680 int layers = 0;
6681 stbi_uc *u = 0;
6682 stbi_uc *out = 0;
6683 stbi_uc *two_back = 0;
6684 stbi__gif g;
6685 int stride;
6686 memset(&g, 0, sizeof(g));
6687 if (delays) {
6688 *delays = 0;
6689 }
6690
6691 do {
6692 u = stbi__gif_load_next(s, &g, comp, req_comp, two_back);
6693 if (u == (stbi_uc *) s) u = 0; // end of animated gif marker
6694
6695 if (u) {
6696 *x = g.w;
6697 *y = g.h;
6698 ++layers;
6699 stride = g.w * g.h * 4;
6700
6701 if (out) {
6702 void *tmp = (stbi_uc*) STBI_REALLOC( out, layers * stride );
6703 if (NULL == tmp) {
6704 STBI_FREE(g.out);
6705 STBI_FREE(g.history);
6706 STBI_FREE(g.background);
6707 return stbi__errpuc("outofmem", "Out of memory");
6708 }
6709 else
6710 out = (stbi_uc*) tmp;
6711 if (delays) {
6712 *delays = (int*) STBI_REALLOC( *delays, sizeof(int) * layers );
6713 }
6714 } else {
6715 out = (stbi_uc*)stbi__malloc( layers * stride );
6716 if (delays) {
6717 *delays = (int*) stbi__malloc( layers * sizeof(int) );
6718 }
6719 }
6720 memcpy( out + ((layers - 1) * stride), u, stride );
6721 if (layers >= 2) {
6722 two_back = out - 2 * stride;
6723 }
6724
6725 if (delays) {
6726 (*delays)[layers - 1U] = g.delay;
6727 }
6728 }
6729 } while (u != 0);
6730
6731 // free temp buffer;
6732 STBI_FREE(g.out);
6733 STBI_FREE(g.history);
6734 STBI_FREE(g.background);
6735
6736 // do the final conversion after loading everything;
6737 if (req_comp && req_comp != 4)
6738 out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h);
6739
6740 *z = layers;
6741 return out;
6742 } else {
6743 return stbi__errpuc("not GIF", "Image was not as a gif type.");
6744 }
6745}
6746
6747static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri)
6748{
6749 stbi_uc *u = 0;
6750 stbi__gif g;
6751 memset(&g, 0, sizeof(g));
6752 STBI_NOTUSED(ri);
6753
6754 u = stbi__gif_load_next(s, &g, comp, req_comp, 0);
6755 if (u == (stbi_uc *) s) u = 0; // end of animated gif marker
6756 if (u) {
6757 *x = g.w;
6758 *y = g.h;
6759
6760 // moved conversion to after successful load so that the same
6761 // can be done for multiple frames.
6762 if (req_comp && req_comp != 4)
6763 u = stbi__convert_format(u, 4, req_comp, g.w, g.h);
6764 } else if (g.out) {
6765 // if there was an error and we allocated an image buffer, free it!
6766 STBI_FREE(g.out);
6767 }
6768
6769 // free buffers needed for multiple frame loading;
6770 STBI_FREE(g.history);
6771 STBI_FREE(g.background);
6772
6773 return u;
6774}
6775
6776static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp)
6777{
6778 return stbi__gif_info_raw(s,x,y,comp);
6779}
6780#endif
6781
6782// *************************************************************************************************
6783// Radiance RGBE HDR loader
6784// originally by Nicolas Schulz
6785#ifndef STBI_NO_HDR
6786static int stbi__hdr_test_core(stbi__context *s, const char *signature)
6787{
6788 int i;
6789 for (i=0; signature[i]; ++i)
6790 if (stbi__get8(s) != signature[i])
6791 return 0;
6792 stbi__rewind(s);
6793 return 1;
6794}
6795
6796static int stbi__hdr_test(stbi__context* s)
6797{
6798 int r = stbi__hdr_test_core(s, "#?RADIANCE\n");
6799 stbi__rewind(s);
6800 if(!r) {
6801 r = stbi__hdr_test_core(s, "#?RGBE\n");
6802 stbi__rewind(s);
6803 }
6804 return r;
6805}
6806
6807#define STBI__HDR_BUFLEN 1024
6808static char *stbi__hdr_gettoken(stbi__context *z, char *buffer)
6809{
6810 int len=0;
6811 char c = '\0';
6812
6813 c = (char) stbi__get8(z);
6814
6815 while (!stbi__at_eof(z) && c != '\n') {
6816 buffer[len++] = c;
6817 if (len == STBI__HDR_BUFLEN-1) {
6818 // flush to end of line
6819 while (!stbi__at_eof(z) && stbi__get8(z) != '\n')
6820 ;
6821 break;
6822 }
6823 c = (char) stbi__get8(z);
6824 }
6825
6826 buffer[len] = 0;
6827 return buffer;
6828}
6829
6830static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp)
6831{
6832 if ( input[3] != 0 ) {
6833 float f1;
6834 // Exponent
6835 f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8));
6836 if (req_comp <= 2)
6837 output[0] = (input[0] + input[1] + input[2]) * f1 / 3;
6838 else {
6839 output[0] = input[0] * f1;
6840 output[1] = input[1] * f1;
6841 output[2] = input[2] * f1;
6842 }
6843 if (req_comp == 2) output[1] = 1;
6844 if (req_comp == 4) output[3] = 1;
6845 } else {
6846 switch (req_comp) {
6847 case 4: output[3] = 1; /* fallthrough */
6848 case 3: output[0] = output[1] = output[2] = 0;
6849 break;
6850 case 2: output[1] = 1; /* fallthrough */
6851 case 1: output[0] = 0;
6852 break;
6853 }
6854 }
6855}
6856
6857static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri)
6858{
6859 char buffer[STBI__HDR_BUFLEN];
6860 char *token;
6861 int valid = 0;
6862 int width, height;
6863 stbi_uc *scanline;
6864 float *hdr_data;
6865 int len;
6866 unsigned char count, value;
6867 int i, j, k, c1,c2, z;
6868 const char *headerToken;
6869 STBI_NOTUSED(ri);
6870
6871 // Check identifier
6872 headerToken = stbi__hdr_gettoken(s,buffer);
6873 if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0)
6874 return stbi__errpf("not HDR", "Corrupt HDR image");
6875
6876 // Parse header
6877 for(;;) {
6878 token = stbi__hdr_gettoken(s,buffer);
6879 if (token[0] == 0) break;
6880 if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1;
6881 }
6882
6883 if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format");
6884
6885 // Parse width and height
6886 // can't use sscanf() if we're not using stdio!
6887 token = stbi__hdr_gettoken(s,buffer);
6888 if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format");
6889 token += 3;
6890 height = (int) strtol(token, &token, 10);
6891 while (*token == ' ') ++token;
6892 if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format");
6893 token += 3;
6894 width = (int) strtol(token, NULL, 10);
6895
6896 *x = width;
6897 *y = height;
6898
6899 if (comp) *comp = 3;
6900 if (req_comp == 0) req_comp = 3;
6901
6902 if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0))
6903 return stbi__errpf("too large", "HDR image is too large");
6904
6905 // Read data
6906 hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0);
6907 if (!hdr_data)
6908 return stbi__errpf("outofmem", "Out of memory");
6909
6910 // Load image data
6911 // image data is stored as some number of sca
6912 if ( width < 8 || width >= 32768) {
6913 // Read flat data
6914 for (j=0; j < height; ++j) {
6915 for (i=0; i < width; ++i) {
6916 stbi_uc rgbe[4];
6917 main_decode_loop:
6918 stbi__getn(s, rgbe, 4);
6919 stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp);
6920 }
6921 }
6922 } else {
6923 // Read RLE-encoded data
6924 scanline = NULL;
6925
6926 for (j = 0; j < height; ++j) {
6927 c1 = stbi__get8(s);
6928 c2 = stbi__get8(s);
6929 len = stbi__get8(s);
6930 if (c1 != 2 || c2 != 2 || (len & 0x80)) {
6931 // not run-length encoded, so we have to actually use THIS data as a decoded
6932 // pixel (note this can't be a valid pixel--one of RGB must be >= 128)
6933 stbi_uc rgbe[4];
6934 rgbe[0] = (stbi_uc) c1;
6935 rgbe[1] = (stbi_uc) c2;
6936 rgbe[2] = (stbi_uc) len;
6937 rgbe[3] = (stbi_uc) stbi__get8(s);
6938 stbi__hdr_convert(hdr_data, rgbe, req_comp);
6939 i = 1;
6940 j = 0;
6941 STBI_FREE(scanline);
6942 goto main_decode_loop; // yes, this makes no sense
6943 }
6944 len <<= 8;
6945 len |= stbi__get8(s);
6946 if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); }
6947 if (scanline == NULL) {
6948 scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0);
6949 if (!scanline) {
6950 STBI_FREE(hdr_data);
6951 return stbi__errpf("outofmem", "Out of memory");
6952 }
6953 }
6954
6955 for (k = 0; k < 4; ++k) {
6956 int nleft;
6957 i = 0;
6958 while ((nleft = width - i) > 0) {
6959 count = stbi__get8(s);
6960 if (count > 128) {
6961 // Run
6962 value = stbi__get8(s);
6963 count -= 128;
6964 if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); }
6965 for (z = 0; z < count; ++z)
6966 scanline[i++ * 4 + k] = value;
6967 } else {
6968 // Dump
6969 if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); }
6970 for (z = 0; z < count; ++z)
6971 scanline[i++ * 4 + k] = stbi__get8(s);
6972 }
6973 }
6974 }
6975 for (i=0; i < width; ++i)
6976 stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp);
6977 }
6978 if (scanline)
6979 STBI_FREE(scanline);
6980 }
6981
6982 return hdr_data;
6983}
6984
6985static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp)
6986{
6987 char buffer[STBI__HDR_BUFLEN];
6988 char *token;
6989 int valid = 0;
6990 int dummy;
6991
6992 if (!x) x = &dummy;
6993 if (!y) y = &dummy;
6994 if (!comp) comp = &dummy;
6995
6996 if (stbi__hdr_test(s) == 0) {
6997 stbi__rewind( s );
6998 return 0;
6999 }
7000
7001 for(;;) {
7002 token = stbi__hdr_gettoken(s,buffer);
7003 if (token[0] == 0) break;
7004 if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1;
7005 }
7006
7007 if (!valid) {
7008 stbi__rewind( s );
7009 return 0;
7010 }
7011 token = stbi__hdr_gettoken(s,buffer);
7012 if (strncmp(token, "-Y ", 3)) {
7013 stbi__rewind( s );
7014 return 0;
7015 }
7016 token += 3;
7017 *y = (int) strtol(token, &token, 10);
7018 while (*token == ' ') ++token;
7019 if (strncmp(token, "+X ", 3)) {
7020 stbi__rewind( s );
7021 return 0;
7022 }
7023 token += 3;
7024 *x = (int) strtol(token, NULL, 10);
7025 *comp = 3;
7026 return 1;
7027}
7028#endif // STBI_NO_HDR
7029
7030#ifndef STBI_NO_BMP
7031static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp)
7032{
7033 void *p;
7034 stbi__bmp_data info;
7035
7036 info.all_a = 255;
7037 p = stbi__bmp_parse_header(s, &info);
7038 stbi__rewind( s );
7039 if (p == NULL)
7040 return 0;
7041 if (x) *x = s->img_x;
7042 if (y) *y = s->img_y;
7043 if (comp) {
7044 if (info.bpp == 24 && info.ma == 0xff000000)
7045 *comp = 3;
7046 else
7047 *comp = info.ma ? 4 : 3;
7048 }
7049 return 1;
7050}
7051#endif
7052
7053#ifndef STBI_NO_PSD
7054static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp)
7055{
7056 int channelCount, dummy, depth;
7057 if (!x) x = &dummy;
7058 if (!y) y = &dummy;
7059 if (!comp) comp = &dummy;
7060 if (stbi__get32be(s) != 0x38425053) {
7061 stbi__rewind( s );
7062 return 0;
7063 }
7064 if (stbi__get16be(s) != 1) {
7065 stbi__rewind( s );
7066 return 0;
7067 }
7068 stbi__skip(s, 6);
7069 channelCount = stbi__get16be(s);
7070 if (channelCount < 0 || channelCount > 16) {
7071 stbi__rewind( s );
7072 return 0;
7073 }
7074 *y = stbi__get32be(s);
7075 *x = stbi__get32be(s);
7076 depth = stbi__get16be(s);
7077 if (depth != 8 && depth != 16) {
7078 stbi__rewind( s );
7079 return 0;
7080 }
7081 if (stbi__get16be(s) != 3) {
7082 stbi__rewind( s );
7083 return 0;
7084 }
7085 *comp = 4;
7086 return 1;
7087}
7088
7089static int stbi__psd_is16(stbi__context *s)
7090{
7091 int channelCount, depth;
7092 if (stbi__get32be(s) != 0x38425053) {
7093 stbi__rewind( s );
7094 return 0;
7095 }
7096 if (stbi__get16be(s) != 1) {
7097 stbi__rewind( s );
7098 return 0;
7099 }
7100 stbi__skip(s, 6);
7101 channelCount = stbi__get16be(s);
7102 if (channelCount < 0 || channelCount > 16) {
7103 stbi__rewind( s );
7104 return 0;
7105 }
7106 (void) stbi__get32be(s);
7107 (void) stbi__get32be(s);
7108 depth = stbi__get16be(s);
7109 if (depth != 16) {
7110 stbi__rewind( s );
7111 return 0;
7112 }
7113 return 1;
7114}
7115#endif
7116
7117#ifndef STBI_NO_PIC
7118static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp)
7119{
7120 int act_comp=0,num_packets=0,chained,dummy;
7121 stbi__pic_packet packets[10];
7122
7123 if (!x) x = &dummy;
7124 if (!y) y = &dummy;
7125 if (!comp) comp = &dummy;
7126
7127 if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) {
7128 stbi__rewind(s);
7129 return 0;
7130 }
7131
7132 stbi__skip(s, 88);
7133
7134 *x = stbi__get16be(s);
7135 *y = stbi__get16be(s);
7136 if (stbi__at_eof(s)) {
7137 stbi__rewind( s);
7138 return 0;
7139 }
7140 if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) {
7141 stbi__rewind( s );
7142 return 0;
7143 }
7144
7145 stbi__skip(s, 8);
7146
7147 do {
7148 stbi__pic_packet *packet;
7149
7150 if (num_packets==sizeof(packets)/sizeof(packets[0]))
7151 return 0;
7152
7153 packet = &packets[num_packets++];
7154 chained = stbi__get8(s);
7155 packet->size = stbi__get8(s);
7156 packet->type = stbi__get8(s);
7157 packet->channel = stbi__get8(s);
7158 act_comp |= packet->channel;
7159
7160 if (stbi__at_eof(s)) {
7161 stbi__rewind( s );
7162 return 0;
7163 }
7164 if (packet->size != 8) {
7165 stbi__rewind( s );
7166 return 0;
7167 }
7168 } while (chained);
7169
7170 *comp = (act_comp & 0x10 ? 4 : 3);
7171
7172 return 1;
7173}
7174#endif
7175
7176// *************************************************************************************************
7177// Portable Gray Map and Portable Pixel Map loader
7178// by Ken Miller
7179//
7180// PGM: http://netpbm.sourceforge.net/doc/pgm.html
7181// PPM: http://netpbm.sourceforge.net/doc/ppm.html
7182//
7183// Known limitations:
7184// Does not support comments in the header section
7185// Does not support ASCII image data (formats P2 and P3)
7186// Does not support 16-bit-per-channel
7187
7188#ifndef STBI_NO_PNM
7189
7190static int stbi__pnm_test(stbi__context *s)
7191{
7192 char p, t;
7193 p = (char) stbi__get8(s);
7194 t = (char) stbi__get8(s);
7195 if (p != 'P' || (t != '5' && t != '6')) {
7196 stbi__rewind( s );
7197 return 0;
7198 }
7199 return 1;
7200}
7201
7202static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri)
7203{
7204 stbi_uc *out;
7205 STBI_NOTUSED(ri);
7206
7207 if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n))
7208 return 0;
7209
7210 *x = s->img_x;
7211 *y = s->img_y;
7212 if (comp) *comp = s->img_n;
7213
7214 if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0))
7215 return stbi__errpuc("too large", "PNM too large");
7216
7217 out = (stbi_uc *) stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0);
7218 if (!out) return stbi__errpuc("outofmem", "Out of memory");
7219 stbi__getn(s, out, s->img_n * s->img_x * s->img_y);
7220
7221 if (req_comp && req_comp != s->img_n) {
7222 out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y);
7223 if (out == NULL) return out; // stbi__convert_format frees input on failure
7224 }
7225 return out;
7226}
7227
7228static int stbi__pnm_isspace(char c)
7229{
7230 return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r';
7231}
7232
7233static void stbi__pnm_skip_whitespace(stbi__context *s, char *c)
7234{
7235 for (;;) {
7236 while (!stbi__at_eof(s) && stbi__pnm_isspace(*c))
7237 *c = (char) stbi__get8(s);
7238
7239 if (stbi__at_eof(s) || *c != '#')
7240 break;
7241
7242 while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' )
7243 *c = (char) stbi__get8(s);
7244 }
7245}
7246
7247static int stbi__pnm_isdigit(char c)
7248{
7249 return c >= '0' && c <= '9';
7250}
7251
7252static int stbi__pnm_getinteger(stbi__context *s, char *c)
7253{
7254 int value = 0;
7255
7256 while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) {
7257 value = value*10 + (*c - '0');
7258 *c = (char) stbi__get8(s);
7259 }
7260
7261 return value;
7262}
7263
7264static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp)
7265{
7266 int maxv, dummy;
7267 char c, p, t;
7268
7269 if (!x) x = &dummy;
7270 if (!y) y = &dummy;
7271 if (!comp) comp = &dummy;
7272
7273 stbi__rewind(s);
7274
7275 // Get identifier
7276 p = (char) stbi__get8(s);
7277 t = (char) stbi__get8(s);
7278 if (p != 'P' || (t != '5' && t != '6')) {
7279 stbi__rewind(s);
7280 return 0;
7281 }
7282
7283 *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm
7284
7285 c = (char) stbi__get8(s);
7286 stbi__pnm_skip_whitespace(s, &c);
7287
7288 *x = stbi__pnm_getinteger(s, &c); // read width
7289 stbi__pnm_skip_whitespace(s, &c);
7290
7291 *y = stbi__pnm_getinteger(s, &c); // read height
7292 stbi__pnm_skip_whitespace(s, &c);
7293
7294 maxv = stbi__pnm_getinteger(s, &c); // read max value
7295
7296 if (maxv > 255)
7297 return stbi__err("max value > 255", "PPM image not 8-bit");
7298 else
7299 return 1;
7300}
7301#endif
7302
7303static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp)
7304{
7305 #ifndef STBI_NO_JPEG
7306 if (stbi__jpeg_info(s, x, y, comp)) return 1;
7307 #endif
7308
7309 #ifndef STBI_NO_PNG
7310 if (stbi__png_info(s, x, y, comp)) return 1;
7311 #endif
7312
7313 #ifndef STBI_NO_GIF
7314 if (stbi__gif_info(s, x, y, comp)) return 1;
7315 #endif
7316
7317 #ifndef STBI_NO_BMP
7318 if (stbi__bmp_info(s, x, y, comp)) return 1;
7319 #endif
7320
7321 #ifndef STBI_NO_PSD
7322 if (stbi__psd_info(s, x, y, comp)) return 1;
7323 #endif
7324
7325 #ifndef STBI_NO_PIC
7326 if (stbi__pic_info(s, x, y, comp)) return 1;
7327 #endif
7328
7329 #ifndef STBI_NO_PNM
7330 if (stbi__pnm_info(s, x, y, comp)) return 1;
7331 #endif
7332
7333 #ifndef STBI_NO_HDR
7334 if (stbi__hdr_info(s, x, y, comp)) return 1;
7335 #endif
7336
7337 // test tga last because it's a crappy test!
7338 #ifndef STBI_NO_TGA
7339 if (stbi__tga_info(s, x, y, comp))
7340 return 1;
7341 #endif
7342 return stbi__err("unknown image type", "Image not of any known type, or corrupt");
7343}
7344
7345static int stbi__is_16_main(stbi__context *s)
7346{
7347 #ifndef STBI_NO_PNG
7348 if (stbi__png_is16(s)) return 1;
7349 #endif
7350
7351 #ifndef STBI_NO_PSD
7352 if (stbi__psd_is16(s)) return 1;
7353 #endif
7354
7355 return 0;
7356}
7357
7358#ifndef STBI_NO_STDIO
7359STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp)
7360{
7361 FILE *f = stbi__fopen(filename, "rb");
7362 int result;
7363 if (!f) return stbi__err("can't fopen", "Unable to open file");
7364 result = stbi_info_from_file(f, x, y, comp);
7365 fclose(f);
7366 return result;
7367}
7368
7369STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp)
7370{
7371 int r;
7372 stbi__context s;
7373 long pos = ftell(f);
7374 stbi__start_file(&s, f);
7375 r = stbi__info_main(&s,x,y,comp);
7376 fseek(f,pos,SEEK_SET);
7377 return r;
7378}
7379
7380STBIDEF int stbi_is_16_bit(char const *filename)
7381{
7382 FILE *f = stbi__fopen(filename, "rb");
7383 int result;
7384 if (!f) return stbi__err("can't fopen", "Unable to open file");
7385 result = stbi_is_16_bit_from_file(f);
7386 fclose(f);
7387 return result;
7388}
7389
7390STBIDEF int stbi_is_16_bit_from_file(FILE *f)
7391{
7392 int r;
7393 stbi__context s;
7394 long pos = ftell(f);
7395 stbi__start_file(&s, f);
7396 r = stbi__is_16_main(&s);
7397 fseek(f,pos,SEEK_SET);
7398 return r;
7399}
7400#endif // !STBI_NO_STDIO
7401
7402STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp)
7403{
7404 stbi__context s;
7405 stbi__start_mem(&s,buffer,len);
7406 return stbi__info_main(&s,x,y,comp);
7407}
7408
7409STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp)
7410{
7411 stbi__context s;
7412 stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user);
7413 return stbi__info_main(&s,x,y,comp);
7414}
7415
7416STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len)
7417{
7418 stbi__context s;
7419 stbi__start_mem(&s,buffer,len);
7420 return stbi__is_16_main(&s);
7421}
7422
7423STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user)
7424{
7425 stbi__context s;
7426 stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user);
7427 return stbi__is_16_main(&s);
7428}
7429
7430#endif // STB_IMAGE_IMPLEMENTATION
7431
7432/*
7433 revision history:
7434 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs
7435 2.19 (2018-02-11) fix warning
7436 2.18 (2018-01-30) fix warnings
7437 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug
7438 1-bit BMP
7439 *_is_16_bit api
7440 avoid warnings
7441 2.16 (2017-07-23) all functions have 16-bit variants;
7442 STBI_NO_STDIO works again;
7443 compilation fixes;
7444 fix rounding in unpremultiply;
7445 optimize vertical flip;
7446 disable raw_len validation;
7447 documentation fixes
7448 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode;
7449 warning fixes; disable run-time SSE detection on gcc;
7450 uniform handling of optional "return" values;
7451 thread-safe initialization of zlib tables
7452 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs
7453 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now
7454 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes
7455 2.11 (2016-04-02) allocate large structures on the stack
7456 remove white matting for transparent PSD
7457 fix reported channel count for PNG & BMP
7458 re-enable SSE2 in non-gcc 64-bit
7459 support RGB-formatted JPEG
7460 read 16-bit PNGs (only as 8-bit)
7461 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED
7462 2.09 (2016-01-16) allow comments in PNM files
7463 16-bit-per-pixel TGA (not bit-per-component)
7464 info() for TGA could break due to .hdr handling
7465 info() for BMP to shares code instead of sloppy parse
7466 can use STBI_REALLOC_SIZED if allocator doesn't support realloc
7467 code cleanup
7468 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA
7469 2.07 (2015-09-13) fix compiler warnings
7470 partial animated GIF support
7471 limited 16-bpc PSD support
7472 #ifdef unused functions
7473 bug with < 92 byte PIC,PNM,HDR,TGA
7474 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value
7475 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning
7476 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit
7477 2.03 (2015-04-12) extra corruption checking (mmozeiko)
7478 stbi_set_flip_vertically_on_load (nguillemot)
7479 fix NEON support; fix mingw support
7480 2.02 (2015-01-19) fix incorrect assert, fix warning
7481 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2
7482 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG
7483 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg)
7484 progressive JPEG (stb)
7485 PGM/PPM support (Ken Miller)
7486 STBI_MALLOC,STBI_REALLOC,STBI_FREE
7487 GIF bugfix -- seemingly never worked
7488 STBI_NO_*, STBI_ONLY_*
7489 1.48 (2014-12-14) fix incorrectly-named assert()
7490 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb)
7491 optimize PNG (ryg)
7492 fix bug in interlaced PNG with user-specified channel count (stb)
7493 1.46 (2014-08-26)
7494 fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG
7495 1.45 (2014-08-16)
7496 fix MSVC-ARM internal compiler error by wrapping malloc
7497 1.44 (2014-08-07)
7498 various warning fixes from Ronny Chevalier
7499 1.43 (2014-07-15)
7500 fix MSVC-only compiler problem in code changed in 1.42
7501 1.42 (2014-07-09)
7502 don't define _CRT_SECURE_NO_WARNINGS (affects user code)
7503 fixes to stbi__cleanup_jpeg path
7504 added STBI_ASSERT to avoid requiring assert.h
7505 1.41 (2014-06-25)
7506 fix search&replace from 1.36 that messed up comments/error messages
7507 1.40 (2014-06-22)
7508 fix gcc struct-initialization warning
7509 1.39 (2014-06-15)
7510 fix to TGA optimization when req_comp != number of components in TGA;
7511 fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite)
7512 add support for BMP version 5 (more ignored fields)
7513 1.38 (2014-06-06)
7514 suppress MSVC warnings on integer casts truncating values
7515 fix accidental rename of 'skip' field of I/O
7516 1.37 (2014-06-04)
7517 remove duplicate typedef
7518 1.36 (2014-06-03)
7519 convert to header file single-file library
7520 if de-iphone isn't set, load iphone images color-swapped instead of returning NULL
7521 1.35 (2014-05-27)
7522 various warnings
7523 fix broken STBI_SIMD path
7524 fix bug where stbi_load_from_file no longer left file pointer in correct place
7525 fix broken non-easy path for 32-bit BMP (possibly never used)
7526 TGA optimization by Arseny Kapoulkine
7527 1.34 (unknown)
7528 use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case
7529 1.33 (2011-07-14)
7530 make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements
7531 1.32 (2011-07-13)
7532 support for "info" function for all supported filetypes (SpartanJ)
7533 1.31 (2011-06-20)
7534 a few more leak fixes, bug in PNG handling (SpartanJ)
7535 1.30 (2011-06-11)
7536 added ability to load files via callbacks to accomidate custom input streams (Ben Wenger)
7537 removed deprecated format-specific test/load functions
7538 removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway
7539 error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha)
7540 fix inefficiency in decoding 32-bit BMP (David Woo)
7541 1.29 (2010-08-16)
7542 various warning fixes from Aurelien Pocheville
7543 1.28 (2010-08-01)
7544 fix bug in GIF palette transparency (SpartanJ)
7545 1.27 (2010-08-01)
7546 cast-to-stbi_uc to fix warnings
7547 1.26 (2010-07-24)
7548 fix bug in file buffering for PNG reported by SpartanJ
7549 1.25 (2010-07-17)
7550 refix trans_data warning (Won Chun)
7551 1.24 (2010-07-12)
7552 perf improvements reading from files on platforms with lock-heavy fgetc()
7553 minor perf improvements for jpeg
7554 deprecated type-specific functions so we'll get feedback if they're needed
7555 attempt to fix trans_data warning (Won Chun)
7556 1.23 fixed bug in iPhone support
7557 1.22 (2010-07-10)
7558 removed image *writing* support
7559 stbi_info support from Jetro Lauha
7560 GIF support from Jean-Marc Lienher
7561 iPhone PNG-extensions from James Brown
7562 warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva)
7563 1.21 fix use of 'stbi_uc' in header (reported by jon blow)
7564 1.20 added support for Softimage PIC, by Tom Seddon
7565 1.19 bug in interlaced PNG corruption check (found by ryg)
7566 1.18 (2008-08-02)
7567 fix a threading bug (local mutable static)
7568 1.17 support interlaced PNG
7569 1.16 major bugfix - stbi__convert_format converted one too many pixels
7570 1.15 initialize some fields for thread safety
7571 1.14 fix threadsafe conversion bug
7572 header-file-only version (#define STBI_HEADER_FILE_ONLY before including)
7573 1.13 threadsafe
7574 1.12 const qualifiers in the API
7575 1.11 Support installable IDCT, colorspace conversion routines
7576 1.10 Fixes for 64-bit (don't use "unsigned long")
7577 optimized upsampling by Fabian "ryg" Giesen
7578 1.09 Fix format-conversion for PSD code (bad global variables!)
7579 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz
7580 1.07 attempt to fix C++ warning/errors again
7581 1.06 attempt to fix C++ warning/errors again
7582 1.05 fix TGA loading to return correct *comp and use good luminance calc
7583 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free
7584 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR
7585 1.02 support for (subset of) HDR files, float interface for preferred access to them
7586 1.01 fix bug: possible bug in handling right-side up bmps... not sure
7587 fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all
7588 1.00 interface to zlib that skips zlib header
7589 0.99 correct handling of alpha in palette
7590 0.98 TGA loader by lonesock; dynamically add loaders (untested)
7591 0.97 jpeg errors on too large a file; also catch another malloc failure
7592 0.96 fix detection of invalid v value - particleman@mollyrocket forum
7593 0.95 during header scan, seek to markers in case of padding
7594 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same
7595 0.93 handle jpegtran output; verbose errors
7596 0.92 read 4,8,16,24,32-bit BMP files of several formats
7597 0.91 output 24-bit Windows 3.0 BMP files
7598 0.90 fix a few more warnings; bump version number to approach 1.0
7599 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd
7600 0.60 fix compiling as c++
7601 0.59 fix warnings: merge Dave Moore's -Wall fixes
7602 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian
7603 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available
7604 0.56 fix bug: zlib uncompressed mode len vs. nlen
7605 0.55 fix bug: restart_interval not initialized to 0
7606 0.54 allow NULL for 'int *comp'
7607 0.53 fix bug in png 3->4; speedup png decoding
7608 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments
7609 0.51 obey req_comp requests, 1-component jpegs return as 1-component,
7610 on 'test' only check type, not whether we support this variant
7611 0.50 (2006-11-19)
7612 first released version
7613*/
7614
7615
7616/*
7617------------------------------------------------------------------------------
7618This software is available under 2 licenses -- choose whichever you prefer.
7619------------------------------------------------------------------------------
7620ALTERNATIVE A - MIT License
7621Copyright (c) 2017 Sean Barrett
7622Permission is hereby granted, free of charge, to any person obtaining a copy of
7623this software and associated documentation files (the "Software"), to deal in
7624the Software without restriction, including without limitation the rights to
7625use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7626of the Software, and to permit persons to whom the Software is furnished to do
7627so, subject to the following conditions:
7628The above copyright notice and this permission notice shall be included in all
7629copies or substantial portions of the Software.
7630THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
7631IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
7632FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
7633AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
7634LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
7635OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
7636SOFTWARE.
7637------------------------------------------------------------------------------
7638ALTERNATIVE B - Public Domain (www.unlicense.org)
7639This is free and unencumbered software released into the public domain.
7640Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
7641software, either in source code form or as a compiled binary, for any purpose,
7642commercial or non-commercial, and by any means.
7643In jurisdictions that recognize copyright laws, the author or authors of this
7644software dedicate any and all copyright interest in the software to the public
7645domain. We make this dedication for the benefit of the public at large and to
7646the detriment of our heirs and successors. We intend this dedication to be an
7647overt act of relinquishment in perpetuity of all present and future rights to
7648this software under copyright law.
7649THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
7650IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
7651FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
7652AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
7653ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
7654WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
7655------------------------------------------------------------------------------
7656*/
diff --git a/src/stb_truetype.h b/src/stb_truetype.h
new file mode 100644
index 00000000..935a6de2
--- /dev/null
+++ b/src/stb_truetype.h
@@ -0,0 +1,5011 @@
1// stb_truetype.h - v1.24 - public domain
2// authored from 2009-2020 by Sean Barrett / RAD Game Tools
3//
4// =======================================================================
5//
6// NO SECURITY GUARANTEE -- DO NOT USE THIS ON UNTRUSTED FONT FILES
7//
8// This library does no range checking of the offsets found in the file,
9// meaning an attacker can use it to read arbitrary memory.
10//
11// =======================================================================
12//
13// This library processes TrueType files:
14// parse files
15// extract glyph metrics
16// extract glyph shapes
17// render glyphs to one-channel bitmaps with antialiasing (box filter)
18// render glyphs to one-channel SDF bitmaps (signed-distance field/function)
19//
20// Todo:
21// non-MS cmaps
22// crashproof on bad data
23// hinting? (no longer patented)
24// cleartype-style AA?
25// optimize: use simple memory allocator for intermediates
26// optimize: build edge-list directly from curves
27// optimize: rasterize directly from curves?
28//
29// ADDITIONAL CONTRIBUTORS
30//
31// Mikko Mononen: compound shape support, more cmap formats
32// Tor Andersson: kerning, subpixel rendering
33// Dougall Johnson: OpenType / Type 2 font handling
34// Daniel Ribeiro Maciel: basic GPOS-based kerning
35//
36// Misc other:
37// Ryan Gordon
38// Simon Glass
39// github:IntellectualKitty
40// Imanol Celaya
41// Daniel Ribeiro Maciel
42//
43// Bug/warning reports/fixes:
44// "Zer" on mollyrocket Fabian "ryg" Giesen github:NiLuJe
45// Cass Everitt Martins Mozeiko github:aloucks
46// stoiko (Haemimont Games) Cap Petschulat github:oyvindjam
47// Brian Hook Omar Cornut github:vassvik
48// Walter van Niftrik Ryan Griege
49// David Gow Peter LaValle
50// David Given Sergey Popov
51// Ivan-Assen Ivanov Giumo X. Clanjor
52// Anthony Pesch Higor Euripedes
53// Johan Duparc Thomas Fields
54// Hou Qiming Derek Vinyard
55// Rob Loach Cort Stratton
56// Kenney Phillis Jr. Brian Costabile
57// Ken Voskuil (kaesve)
58//
59// VERSION HISTORY
60//
61// 1.24 (2020-02-05) fix warning
62// 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS)
63// 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined
64// 1.21 (2019-02-25) fix warning
65// 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics()
66// 1.19 (2018-02-11) GPOS kerning, STBTT_fmod
67// 1.18 (2018-01-29) add missing function
68// 1.17 (2017-07-23) make more arguments const; doc fix
69// 1.16 (2017-07-12) SDF support
70// 1.15 (2017-03-03) make more arguments const
71// 1.14 (2017-01-16) num-fonts-in-TTC function
72// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts
73// 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual
74// 1.11 (2016-04-02) fix unused-variable warning
75// 1.10 (2016-04-02) user-defined fabs(); rare memory leak; remove duplicate typedef
76// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use allocation userdata properly
77// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges
78// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints;
79// variant PackFontRanges to pack and render in separate phases;
80// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?);
81// fixed an assert() bug in the new rasterizer
82// replace assert() with STBTT_assert() in new rasterizer
83//
84// Full history can be found at the end of this file.
85//
86// LICENSE
87//
88// See end of file for license information.
89//
90// USAGE
91//
92// Include this file in whatever places need to refer to it. In ONE C/C++
93// file, write:
94// #define STB_TRUETYPE_IMPLEMENTATION
95// before the #include of this file. This expands out the actual
96// implementation into that C/C++ file.
97//
98// To make the implementation private to the file that generates the implementation,
99// #define STBTT_STATIC
100//
101// Simple 3D API (don't ship this, but it's fine for tools and quick start)
102// stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture
103// stbtt_GetBakedQuad() -- compute quad to draw for a given char
104//
105// Improved 3D API (more shippable):
106// #include "stb_rect_pack.h" -- optional, but you really want it
107// stbtt_PackBegin()
108// stbtt_PackSetOversampling() -- for improved quality on small fonts
109// stbtt_PackFontRanges() -- pack and renders
110// stbtt_PackEnd()
111// stbtt_GetPackedQuad()
112//
113// "Load" a font file from a memory buffer (you have to keep the buffer loaded)
114// stbtt_InitFont()
115// stbtt_GetFontOffsetForIndex() -- indexing for TTC font collections
116// stbtt_GetNumberOfFonts() -- number of fonts for TTC font collections
117//
118// Render a unicode codepoint to a bitmap
119// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap
120// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide
121// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be
122//
123// Character advance/positioning
124// stbtt_GetCodepointHMetrics()
125// stbtt_GetFontVMetrics()
126// stbtt_GetFontVMetricsOS2()
127// stbtt_GetCodepointKernAdvance()
128//
129// Starting with version 1.06, the rasterizer was replaced with a new,
130// faster and generally-more-precise rasterizer. The new rasterizer more
131// accurately measures pixel coverage for anti-aliasing, except in the case
132// where multiple shapes overlap, in which case it overestimates the AA pixel
133// coverage. Thus, anti-aliasing of intersecting shapes may look wrong. If
134// this turns out to be a problem, you can re-enable the old rasterizer with
135// #define STBTT_RASTERIZER_VERSION 1
136// which will incur about a 15% speed hit.
137//
138// ADDITIONAL DOCUMENTATION
139//
140// Immediately after this block comment are a series of sample programs.
141//
142// After the sample programs is the "header file" section. This section
143// includes documentation for each API function.
144//
145// Some important concepts to understand to use this library:
146//
147// Codepoint
148// Characters are defined by unicode codepoints, e.g. 65 is
149// uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is
150// the hiragana for "ma".
151//
152// Glyph
153// A visual character shape (every codepoint is rendered as
154// some glyph)
155//
156// Glyph index
157// A font-specific integer ID representing a glyph
158//
159// Baseline
160// Glyph shapes are defined relative to a baseline, which is the
161// bottom of uppercase characters. Characters extend both above
162// and below the baseline.
163//
164// Current Point
165// As you draw text to the screen, you keep track of a "current point"
166// which is the origin of each character. The current point's vertical
167// position is the baseline. Even "baked fonts" use this model.
168//
169// Vertical Font Metrics
170// The vertical qualities of the font, used to vertically position
171// and space the characters. See docs for stbtt_GetFontVMetrics.
172//
173// Font Size in Pixels or Points
174// The preferred interface for specifying font sizes in stb_truetype
175// is to specify how tall the font's vertical extent should be in pixels.
176// If that sounds good enough, skip the next paragraph.
177//
178// Most font APIs instead use "points", which are a common typographic
179// measurement for describing font size, defined as 72 points per inch.
180// stb_truetype provides a point API for compatibility. However, true
181// "per inch" conventions don't make much sense on computer displays
182// since different monitors have different number of pixels per
183// inch. For example, Windows traditionally uses a convention that
184// there are 96 pixels per inch, thus making 'inch' measurements have
185// nothing to do with inches, and thus effectively defining a point to
186// be 1.333 pixels. Additionally, the TrueType font data provides
187// an explicit scale factor to scale a given font's glyphs to points,
188// but the author has observed that this scale factor is often wrong
189// for non-commercial fonts, thus making fonts scaled in points
190// according to the TrueType spec incoherently sized in practice.
191//
192// DETAILED USAGE:
193//
194// Scale:
195// Select how high you want the font to be, in points or pixels.
196// Call ScaleForPixelHeight or ScaleForMappingEmToPixels to compute
197// a scale factor SF that will be used by all other functions.
198//
199// Baseline:
200// You need to select a y-coordinate that is the baseline of where
201// your text will appear. Call GetFontBoundingBox to get the baseline-relative
202// bounding box for all characters. SF*-y0 will be the distance in pixels
203// that the worst-case character could extend above the baseline, so if
204// you want the top edge of characters to appear at the top of the
205// screen where y=0, then you would set the baseline to SF*-y0.
206//
207// Current point:
208// Set the current point where the first character will appear. The
209// first character could extend left of the current point; this is font
210// dependent. You can either choose a current point that is the leftmost
211// point and hope, or add some padding, or check the bounding box or
212// left-side-bearing of the first character to be displayed and set
213// the current point based on that.
214//
215// Displaying a character:
216// Compute the bounding box of the character. It will contain signed values
217// relative to <current_point, baseline>. I.e. if it returns x0,y0,x1,y1,
218// then the character should be displayed in the rectangle from
219// <current_point+SF*x0, baseline+SF*y0> to <current_point+SF*x1,baseline+SF*y1).
220//
221// Advancing for the next character:
222// Call GlyphHMetrics, and compute 'current_point += SF * advance'.
223//
224//
225// ADVANCED USAGE
226//
227// Quality:
228//
229// - Use the functions with Subpixel at the end to allow your characters
230// to have subpixel positioning. Since the font is anti-aliased, not
231// hinted, this is very import for quality. (This is not possible with
232// baked fonts.)
233//
234// - Kerning is now supported, and if you're supporting subpixel rendering
235// then kerning is worth using to give your text a polished look.
236//
237// Performance:
238//
239// - Convert Unicode codepoints to glyph indexes and operate on the glyphs;
240// if you don't do this, stb_truetype is forced to do the conversion on
241// every call.
242//
243// - There are a lot of memory allocations. We should modify it to take
244// a temp buffer and allocate from the temp buffer (without freeing),
245// should help performance a lot.
246//
247// NOTES
248//
249// The system uses the raw data found in the .ttf file without changing it
250// and without building auxiliary data structures. This is a bit inefficient
251// on little-endian systems (the data is big-endian), but assuming you're
252// caching the bitmaps or glyph shapes this shouldn't be a big deal.
253//
254// It appears to be very hard to programmatically determine what font a
255// given file is in a general way. I provide an API for this, but I don't
256// recommend it.
257//
258//
259// PERFORMANCE MEASUREMENTS FOR 1.06:
260//
261// 32-bit 64-bit
262// Previous release: 8.83 s 7.68 s
263// Pool allocations: 7.72 s 6.34 s
264// Inline sort : 6.54 s 5.65 s
265// New rasterizer : 5.63 s 5.00 s
266
267//////////////////////////////////////////////////////////////////////////////
268//////////////////////////////////////////////////////////////////////////////
269////
270//// SAMPLE PROGRAMS
271////
272//
273// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless
274//
275#if 0
276#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation
277#include "stb_truetype.h"
278
279unsigned char ttf_buffer[1<<20];
280unsigned char temp_bitmap[512*512];
281
282stbtt_bakedchar cdata[96]; // ASCII 32..126 is 95 glyphs
283GLuint ftex;
284
285void my_stbtt_initfont(void)
286{
287 fread(ttf_buffer, 1, 1<<20, fopen("c:/windows/fonts/times.ttf", "rb"));
288 stbtt_BakeFontBitmap(ttf_buffer,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits!
289 // can free ttf_buffer at this point
290 glGenTextures(1, &ftex);
291 glBindTexture(GL_TEXTURE_2D, ftex);
292 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap);
293 // can free temp_bitmap at this point
294 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
295}
296
297void my_stbtt_print(float x, float y, char *text)
298{
299 // assume orthographic projection with units = screen pixels, origin at top left
300 glEnable(GL_TEXTURE_2D);
301 glBindTexture(GL_TEXTURE_2D, ftex);
302 glBegin(GL_QUADS);
303 while (*text) {
304 if (*text >= 32 && *text < 128) {
305 stbtt_aligned_quad q;
306 stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9
307 glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0);
308 glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0);
309 glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1);
310 glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1);
311 }
312 ++text;
313 }
314 glEnd();
315}
316#endif
317//
318//
319//////////////////////////////////////////////////////////////////////////////
320//
321// Complete program (this compiles): get a single bitmap, print as ASCII art
322//
323#if 0
324#include <stdio.h>
325#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation
326#include "stb_truetype.h"
327
328char ttf_buffer[1<<25];
329
330int main(int argc, char **argv)
331{
332 stbtt_fontinfo font;
333 unsigned char *bitmap;
334 int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20);
335
336 fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb"));
337
338 stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0));
339 bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0);
340
341 for (j=0; j < h; ++j) {
342 for (i=0; i < w; ++i)
343 putchar(" .:ioVM@"[bitmap[j*w+i]>>5]);
344 putchar('\n');
345 }
346 return 0;
347}
348#endif
349//
350// Output:
351//
352// .ii.
353// @@@@@@.
354// V@Mio@@o
355// :i. V@V
356// :oM@@M
357// :@@@MM@M
358// @@o o@M
359// :@@. M@M
360// @@@o@@@@
361// :M@@V:@@.
362//
363//////////////////////////////////////////////////////////////////////////////
364//
365// Complete program: print "Hello World!" banner, with bugs
366//
367#if 0
368char buffer[24<<20];
369unsigned char screen[20][79];
370
371int main(int arg, char **argv)
372{
373 stbtt_fontinfo font;
374 int i,j,ascent,baseline,ch=0;
375 float scale, xpos=2; // leave a little padding in case the character extends left
376 char *text = "Heljo World!"; // intentionally misspelled to show 'lj' brokenness
377
378 fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb"));
379 stbtt_InitFont(&font, buffer, 0);
380
381 scale = stbtt_ScaleForPixelHeight(&font, 15);
382 stbtt_GetFontVMetrics(&font, &ascent,0,0);
383 baseline = (int) (ascent*scale);
384
385 while (text[ch]) {
386 int advance,lsb,x0,y0,x1,y1;
387 float x_shift = xpos - (float) floor(xpos);
388 stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb);
389 stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1);
390 stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]);
391 // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong
392 // because this API is really for baking character bitmaps into textures. if you want to render
393 // a sequence of characters, you really need to render each bitmap to a temp buffer, then
394 // "alpha blend" that into the working buffer
395 xpos += (advance * scale);
396 if (text[ch+1])
397 xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]);
398 ++ch;
399 }
400
401 for (j=0; j < 20; ++j) {
402 for (i=0; i < 78; ++i)
403 putchar(" .:ioVM@"[screen[j][i]>>5]);
404 putchar('\n');
405 }
406
407 return 0;
408}
409#endif
410
411
412//////////////////////////////////////////////////////////////////////////////
413//////////////////////////////////////////////////////////////////////////////
414////
415//// INTEGRATION WITH YOUR CODEBASE
416////
417//// The following sections allow you to supply alternate definitions
418//// of C library functions used by stb_truetype, e.g. if you don't
419//// link with the C runtime library.
420
421#ifdef STB_TRUETYPE_IMPLEMENTATION
422 // #define your own (u)stbtt_int8/16/32 before including to override this
423 #ifndef stbtt_uint8
424 typedef unsigned char stbtt_uint8;
425 typedef signed char stbtt_int8;
426 typedef unsigned short stbtt_uint16;
427 typedef signed short stbtt_int16;
428 typedef unsigned int stbtt_uint32;
429 typedef signed int stbtt_int32;
430 #endif
431
432 typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1];
433 typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1];
434
435 // e.g. #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h
436 #ifndef STBTT_ifloor
437 #include <math.h>
438 #define STBTT_ifloor(x) ((int) floor(x))
439 #define STBTT_iceil(x) ((int) ceil(x))
440 #endif
441
442 #ifndef STBTT_sqrt
443 #include <math.h>
444 #define STBTT_sqrt(x) sqrt(x)
445 #define STBTT_pow(x,y) pow(x,y)
446 #endif
447
448 #ifndef STBTT_fmod
449 #include <math.h>
450 #define STBTT_fmod(x,y) fmod(x,y)
451 #endif
452
453 #ifndef STBTT_cos
454 #include <math.h>
455 #define STBTT_cos(x) cos(x)
456 #define STBTT_acos(x) acos(x)
457 #endif
458
459 #ifndef STBTT_fabs
460 #include <math.h>
461 #define STBTT_fabs(x) fabs(x)
462 #endif
463
464 // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h
465 #ifndef STBTT_malloc
466 #include <stdlib.h>
467 #define STBTT_malloc(x,u) ((void)(u),malloc(x))
468 #define STBTT_free(x,u) ((void)(u),free(x))
469 #endif
470
471 #ifndef STBTT_assert
472 #include <assert.h>
473 #define STBTT_assert(x) assert(x)
474 #endif
475
476 #ifndef STBTT_strlen
477 #include <string.h>
478 #define STBTT_strlen(x) strlen(x)
479 #endif
480
481 #ifndef STBTT_memcpy
482 #include <string.h>
483 #define STBTT_memcpy memcpy
484 #define STBTT_memset memset
485 #endif
486#endif
487
488///////////////////////////////////////////////////////////////////////////////
489///////////////////////////////////////////////////////////////////////////////
490////
491//// INTERFACE
492////
493////
494
495#ifndef __STB_INCLUDE_STB_TRUETYPE_H__
496#define __STB_INCLUDE_STB_TRUETYPE_H__
497
498#ifdef STBTT_STATIC
499#define STBTT_DEF static
500#else
501#define STBTT_DEF extern
502#endif
503
504#ifdef __cplusplus
505extern "C" {
506#endif
507
508// private structure
509typedef struct
510{
511 unsigned char *data;
512 int cursor;
513 int size;
514} stbtt__buf;
515
516//////////////////////////////////////////////////////////////////////////////
517//
518// TEXTURE BAKING API
519//
520// If you use this API, you only have to call two functions ever.
521//
522
523typedef struct
524{
525 unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap
526 float xoff,yoff,xadvance;
527} stbtt_bakedchar;
528
529STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf)
530 float pixel_height, // height of font in pixels
531 unsigned char *pixels, int pw, int ph, // bitmap to be filled in
532 int first_char, int num_chars, // characters to bake
533 stbtt_bakedchar *chardata); // you allocate this, it's num_chars long
534// if return is positive, the first unused row of the bitmap
535// if return is negative, returns the negative of the number of characters that fit
536// if return is 0, no characters fit and no rows were used
537// This uses a very crappy packing.
538
539typedef struct
540{
541 float x0,y0,s0,t0; // top-left
542 float x1,y1,s1,t1; // bottom-right
543} stbtt_aligned_quad;
544
545STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, // same data as above
546 int char_index, // character to display
547 float *xpos, float *ypos, // pointers to current position in screen pixel space
548 stbtt_aligned_quad *q, // output: quad to draw
549 int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier
550// Call GetBakedQuad with char_index = 'character - first_char', and it
551// creates the quad you need to draw and advances the current position.
552//
553// The coordinate system used assumes y increases downwards.
554//
555// Characters will extend both above and below the current position;
556// see discussion of "BASELINE" above.
557//
558// It's inefficient; you might want to c&p it and optimize it.
559
560STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap);
561// Query the font vertical metrics without having to create a font first.
562
563
564//////////////////////////////////////////////////////////////////////////////
565//
566// NEW TEXTURE BAKING API
567//
568// This provides options for packing multiple fonts into one atlas, not
569// perfectly but better than nothing.
570
571typedef struct
572{
573 unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap
574 float xoff,yoff,xadvance;
575 float xoff2,yoff2;
576} stbtt_packedchar;
577
578typedef struct stbtt_pack_context stbtt_pack_context;
579typedef struct stbtt_fontinfo stbtt_fontinfo;
580#ifndef STB_RECT_PACK_VERSION
581typedef struct stbrp_rect stbrp_rect;
582#endif
583
584STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context);
585// Initializes a packing context stored in the passed-in stbtt_pack_context.
586// Future calls using this context will pack characters into the bitmap passed
587// in here: a 1-channel bitmap that is width * height. stride_in_bytes is
588// the distance from one row to the next (or 0 to mean they are packed tightly
589// together). "padding" is the amount of padding to leave between each
590// character (normally you want '1' for bitmaps you'll use as textures with
591// bilinear filtering).
592//
593// Returns 0 on failure, 1 on success.
594
595STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc);
596// Cleans up the packing context and frees all memory.
597
598#define STBTT_POINT_SIZE(x) (-(x))
599
600STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size,
601 int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range);
602// Creates character bitmaps from the font_index'th font found in fontdata (use
603// font_index=0 if you don't know what that is). It creates num_chars_in_range
604// bitmaps for characters with unicode values starting at first_unicode_char_in_range
605// and increasing. Data for how to render them is stored in chardata_for_range;
606// pass these to stbtt_GetPackedQuad to get back renderable quads.
607//
608// font_size is the full height of the character from ascender to descender,
609// as computed by stbtt_ScaleForPixelHeight. To use a point size as computed
610// by stbtt_ScaleForMappingEmToPixels, wrap the point size in STBTT_POINT_SIZE()
611// and pass that result as 'font_size':
612// ..., 20 , ... // font max minus min y is 20 pixels tall
613// ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall
614
615typedef struct
616{
617 float font_size;
618 int first_unicode_codepoint_in_range; // if non-zero, then the chars are continuous, and this is the first codepoint
619 int *array_of_unicode_codepoints; // if non-zero, then this is an array of unicode codepoints
620 int num_chars;
621 stbtt_packedchar *chardata_for_range; // output
622 unsigned char h_oversample, v_oversample; // don't set these, they're used internally
623} stbtt_pack_range;
624
625STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges);
626// Creates character bitmaps from multiple ranges of characters stored in
627// ranges. This will usually create a better-packed bitmap than multiple
628// calls to stbtt_PackFontRange. Note that you can call this multiple
629// times within a single PackBegin/PackEnd.
630
631STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample);
632// Oversampling a font increases the quality by allowing higher-quality subpixel
633// positioning, and is especially valuable at smaller text sizes.
634//
635// This function sets the amount of oversampling for all following calls to
636// stbtt_PackFontRange(s) or stbtt_PackFontRangesGatherRects for a given
637// pack context. The default (no oversampling) is achieved by h_oversample=1
638// and v_oversample=1. The total number of pixels required is
639// h_oversample*v_oversample larger than the default; for example, 2x2
640// oversampling requires 4x the storage of 1x1. For best results, render
641// oversampled textures with bilinear filtering. Look at the readme in
642// stb/tests/oversample for information about oversampled fonts
643//
644// To use with PackFontRangesGather etc., you must set it before calls
645// call to PackFontRangesGatherRects.
646
647STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip);
648// If skip != 0, this tells stb_truetype to skip any codepoints for which
649// there is no corresponding glyph. If skip=0, which is the default, then
650// codepoints without a glyph recived the font's "missing character" glyph,
651// typically an empty box by convention.
652
653STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, // same data as above
654 int char_index, // character to display
655 float *xpos, float *ypos, // pointers to current position in screen pixel space
656 stbtt_aligned_quad *q, // output: quad to draw
657 int align_to_integer);
658
659STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects);
660STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects);
661STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects);
662// Calling these functions in sequence is roughly equivalent to calling
663// stbtt_PackFontRanges(). If you more control over the packing of multiple
664// fonts, or if you want to pack custom data into a font texture, take a look
665// at the source to of stbtt_PackFontRanges() and create a custom version
666// using these functions, e.g. call GatherRects multiple times,
667// building up a single array of rects, then call PackRects once,
668// then call RenderIntoRects repeatedly. This may result in a
669// better packing than calling PackFontRanges multiple times
670// (or it may not).
671
672// this is an opaque structure that you shouldn't mess with which holds
673// all the context needed from PackBegin to PackEnd.
674struct stbtt_pack_context {
675 void *user_allocator_context;
676 void *pack_info;
677 int width;
678 int height;
679 int stride_in_bytes;
680 int padding;
681 int skip_missing;
682 unsigned int h_oversample, v_oversample;
683 unsigned char *pixels;
684 void *nodes;
685};
686
687//////////////////////////////////////////////////////////////////////////////
688//
689// FONT LOADING
690//
691//
692
693STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data);
694// This function will determine the number of fonts in a font file. TrueType
695// collection (.ttc) files may contain multiple fonts, while TrueType font
696// (.ttf) files only contain one font. The number of fonts can be used for
697// indexing with the previous function where the index is between zero and one
698// less than the total fonts. If an error occurs, -1 is returned.
699
700STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index);
701// Each .ttf/.ttc file may have more than one font. Each font has a sequential
702// index number starting from 0. Call this function to get the font offset for
703// a given index; it returns -1 if the index is out of range. A regular .ttf
704// file will only define one font and it always be at offset 0, so it will
705// return '0' for index 0, and -1 for all other indices.
706
707// The following structure is defined publicly so you can declare one on
708// the stack or as a global or etc, but you should treat it as opaque.
709struct stbtt_fontinfo
710{
711 void * userdata;
712 unsigned char * data; // pointer to .ttf file
713 int fontstart; // offset of start of font
714
715 int numGlyphs; // number of glyphs, needed for range checking
716
717 int loca,head,glyf,hhea,hmtx,kern,gpos,svg; // table locations as offset from start of .ttf
718 int index_map; // a cmap mapping for our chosen character encoding
719 int indexToLocFormat; // format needed to map from glyph index to glyph
720
721 stbtt__buf cff; // cff font data
722 stbtt__buf charstrings; // the charstring index
723 stbtt__buf gsubrs; // global charstring subroutines index
724 stbtt__buf subrs; // private charstring subroutines index
725 stbtt__buf fontdicts; // array of font dicts
726 stbtt__buf fdselect; // map from glyph to fontdict
727};
728
729STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset);
730// Given an offset into the file that defines a font, this function builds
731// the necessary cached info for the rest of the system. You must allocate
732// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't
733// need to do anything special to free it, because the contents are pure
734// value data with no additional data structures. Returns 0 on failure.
735
736
737//////////////////////////////////////////////////////////////////////////////
738//
739// CHARACTER TO GLYPH-INDEX CONVERSIOn
740
741STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint);
742// If you're going to perform multiple operations on the same character
743// and you want a speed-up, call this function with the character you're
744// going to process, then use glyph-based functions instead of the
745// codepoint-based functions.
746// Returns 0 if the character codepoint is not defined in the font.
747
748
749//////////////////////////////////////////////////////////////////////////////
750//
751// CHARACTER PROPERTIES
752//
753
754STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels);
755// computes a scale factor to produce a font whose "height" is 'pixels' tall.
756// Height is measured as the distance from the highest ascender to the lowest
757// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics
758// and computing:
759// scale = pixels / (ascent - descent)
760// so if you prefer to measure height by the ascent only, use a similar calculation.
761
762STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels);
763// computes a scale factor to produce a font whose EM size is mapped to
764// 'pixels' tall. This is probably what traditional APIs compute, but
765// I'm not positive.
766
767STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap);
768// ascent is the coordinate above the baseline the font extends; descent
769// is the coordinate below the baseline the font extends (i.e. it is typically negative)
770// lineGap is the spacing between one row's descent and the next row's ascent...
771// so you should advance the vertical position by "*ascent - *descent + *lineGap"
772// these are expressed in unscaled coordinates, so you must multiply by
773// the scale factor for a given size
774
775STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap);
776// analogous to GetFontVMetrics, but returns the "typographic" values from the OS/2
777// table (specific to MS/Windows TTF files).
778//
779// Returns 1 on success (table present), 0 on failure.
780
781STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1);
782// the bounding box around all possible characters
783
784STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing);
785// leftSideBearing is the offset from the current horizontal position to the left edge of the character
786// advanceWidth is the offset from the current horizontal position to the next horizontal position
787// these are expressed in unscaled coordinates
788
789STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2);
790// an additional amount to add to the 'advance' value between ch1 and ch2
791
792STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1);
793// Gets the bounding box of the visible part of the glyph, in unscaled coordinates
794
795STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing);
796STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2);
797STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1);
798// as above, but takes one or more glyph indices for greater efficiency
799
800typedef struct stbtt_kerningentry
801{
802 int glyph1; // use stbtt_FindGlyphIndex
803 int glyph2;
804 int advance;
805} stbtt_kerningentry;
806
807STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info);
808STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length);
809// Retrieves a complete list of all of the kerning pairs provided by the font
810// stbtt_GetKerningTable never writes more than table_length entries and returns how many entries it did write.
811// The table will be sorted by (a.glyph1 == b.glyph1)?(a.glyph2 < b.glyph2):(a.glyph1 < b.glyph1)
812
813//////////////////////////////////////////////////////////////////////////////
814//
815// GLYPH SHAPES (you probably don't need these, but they have to go before
816// the bitmaps for C declaration-order reasons)
817//
818
819#ifndef STBTT_vmove // you can predefine these to use different values (but why?)
820 enum {
821 STBTT_vmove=1,
822 STBTT_vline,
823 STBTT_vcurve,
824 STBTT_vcubic
825 };
826#endif
827
828#ifndef stbtt_vertex // you can predefine this to use different values
829 // (we share this with other code at RAD)
830 #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file
831 typedef struct
832 {
833 stbtt_vertex_type x,y,cx,cy,cx1,cy1;
834 unsigned char type,padding;
835 } stbtt_vertex;
836#endif
837
838STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index);
839// returns non-zero if nothing is drawn for this glyph
840
841STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices);
842STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices);
843// returns # of vertices and fills *vertices with the pointer to them
844// these are expressed in "unscaled" coordinates
845//
846// The shape is a series of contours. Each one starts with
847// a STBTT_moveto, then consists of a series of mixed
848// STBTT_lineto and STBTT_curveto segments. A lineto
849// draws a line from previous endpoint to its x,y; a curveto
850// draws a quadratic bezier from previous endpoint to
851// its x,y, using cx,cy as the bezier control point.
852
853STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices);
854// frees the data allocated above
855
856STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg);
857STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg);
858// fills svg with the character's SVG data.
859// returns data size or 0 if SVG not found.
860
861//////////////////////////////////////////////////////////////////////////////
862//
863// BITMAP RENDERING
864//
865
866STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata);
867// frees the bitmap allocated below
868
869STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff);
870// allocates a large-enough single-channel 8bpp bitmap and renders the
871// specified character/glyph at the specified scale into it, with
872// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque).
873// *width & *height are filled out with the width & height of the bitmap,
874// which is stored left-to-right, top-to-bottom.
875//
876// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap
877
878STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff);
879// the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel
880// shift for the character
881
882STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint);
883// the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap
884// in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap
885// is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the
886// width and height and positioning info for it first.
887
888STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint);
889// same as stbtt_MakeCodepointBitmap, but you can specify a subpixel
890// shift for the character
891
892STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint);
893// same as stbtt_MakeCodepointBitmapSubpixel, but prefiltering
894// is performed (see stbtt_PackSetOversampling)
895
896STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);
897// get the bbox of the bitmap centered around the glyph origin; so the
898// bitmap width is ix1-ix0, height is iy1-iy0, and location to place
899// the bitmap top left is (leftSideBearing*scale,iy0).
900// (Note that the bitmap uses y-increases-down, but the shape uses
901// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.)
902
903STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1);
904// same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel
905// shift for the character
906
907// the following functions are equivalent to the above functions, but operate
908// on glyph indices instead of Unicode codepoints (for efficiency)
909STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff);
910STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff);
911STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph);
912STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph);
913STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int glyph);
914STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);
915STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1);
916
917
918// @TODO: don't expose this structure
919typedef struct
920{
921 int w,h,stride;
922 unsigned char *pixels;
923} stbtt__bitmap;
924
925// rasterize a shape with quadratic beziers into a bitmap
926STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, // 1-channel bitmap to draw into
927 float flatness_in_pixels, // allowable error of curve in pixels
928 stbtt_vertex *vertices, // array of vertices defining shape
929 int num_verts, // number of vertices in above array
930 float scale_x, float scale_y, // scale applied to input vertices
931 float shift_x, float shift_y, // translation applied to input vertices
932 int x_off, int y_off, // another translation applied to input
933 int invert, // if non-zero, vertically flip shape
934 void *userdata); // context for to STBTT_MALLOC
935
936//////////////////////////////////////////////////////////////////////////////
937//
938// Signed Distance Function (or Field) rendering
939
940STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata);
941// frees the SDF bitmap allocated below
942
943STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff);
944STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff);
945// These functions compute a discretized SDF field for a single character, suitable for storing
946// in a single-channel texture, sampling with bilinear filtering, and testing against
947// larger than some threshold to produce scalable fonts.
948// info -- the font
949// scale -- controls the size of the resulting SDF bitmap, same as it would be creating a regular bitmap
950// glyph/codepoint -- the character to generate the SDF for
951// padding -- extra "pixels" around the character which are filled with the distance to the character (not 0),
952// which allows effects like bit outlines
953// onedge_value -- value 0-255 to test the SDF against to reconstruct the character (i.e. the isocontour of the character)
954// pixel_dist_scale -- what value the SDF should increase by when moving one SDF "pixel" away from the edge (on the 0..255 scale)
955// if positive, > onedge_value is inside; if negative, < onedge_value is inside
956// width,height -- output height & width of the SDF bitmap (including padding)
957// xoff,yoff -- output origin of the character
958// return value -- a 2D array of bytes 0..255, width*height in size
959//
960// pixel_dist_scale & onedge_value are a scale & bias that allows you to make
961// optimal use of the limited 0..255 for your application, trading off precision
962// and special effects. SDF values outside the range 0..255 are clamped to 0..255.
963//
964// Example:
965// scale = stbtt_ScaleForPixelHeight(22)
966// padding = 5
967// onedge_value = 180
968// pixel_dist_scale = 180/5.0 = 36.0
969//
970// This will create an SDF bitmap in which the character is about 22 pixels
971// high but the whole bitmap is about 22+5+5=32 pixels high. To produce a filled
972// shape, sample the SDF at each pixel and fill the pixel if the SDF value
973// is greater than or equal to 180/255. (You'll actually want to antialias,
974// which is beyond the scope of this example.) Additionally, you can compute
975// offset outlines (e.g. to stroke the character border inside & outside,
976// or only outside). For example, to fill outside the character up to 3 SDF
977// pixels, you would compare against (180-36.0*3)/255 = 72/255. The above
978// choice of variables maps a range from 5 pixels outside the shape to
979// 2 pixels inside the shape to 0..255; this is intended primarily for apply
980// outside effects only (the interior range is needed to allow proper
981// antialiasing of the font at *smaller* sizes)
982//
983// The function computes the SDF analytically at each SDF pixel, not by e.g.
984// building a higher-res bitmap and approximating it. In theory the quality
985// should be as high as possible for an SDF of this size & representation, but
986// unclear if this is true in practice (perhaps building a higher-res bitmap
987// and computing from that can allow drop-out prevention).
988//
989// The algorithm has not been optimized at all, so expect it to be slow
990// if computing lots of characters or very large sizes.
991
992
993
994//////////////////////////////////////////////////////////////////////////////
995//
996// Finding the right font...
997//
998// You should really just solve this offline, keep your own tables
999// of what font is what, and don't try to get it out of the .ttf file.
1000// That's because getting it out of the .ttf file is really hard, because
1001// the names in the file can appear in many possible encodings, in many
1002// possible languages, and e.g. if you need a case-insensitive comparison,
1003// the details of that depend on the encoding & language in a complex way
1004// (actually underspecified in truetype, but also gigantic).
1005//
1006// But you can use the provided functions in two possible ways:
1007// stbtt_FindMatchingFont() will use *case-sensitive* comparisons on
1008// unicode-encoded names to try to find the font you want;
1009// you can run this before calling stbtt_InitFont()
1010//
1011// stbtt_GetFontNameString() lets you get any of the various strings
1012// from the file yourself and do your own comparisons on them.
1013// You have to have called stbtt_InitFont() first.
1014
1015
1016STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags);
1017// returns the offset (not index) of the font that matches, or -1 if none
1018// if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold".
1019// if you use any other flag, use a font name like "Arial"; this checks
1020// the 'macStyle' header field; i don't know if fonts set this consistently
1021#define STBTT_MACSTYLE_DONTCARE 0
1022#define STBTT_MACSTYLE_BOLD 1
1023#define STBTT_MACSTYLE_ITALIC 2
1024#define STBTT_MACSTYLE_UNDERSCORE 4
1025#define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0
1026
1027STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2);
1028// returns 1/0 whether the first string interpreted as utf8 is identical to
1029// the second string interpreted as big-endian utf16... useful for strings from next func
1030
1031STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID);
1032// returns the string (which may be big-endian double byte, e.g. for unicode)
1033// and puts the length in bytes in *length.
1034//
1035// some of the values for the IDs are below; for more see the truetype spec:
1036// http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html
1037// http://www.microsoft.com/typography/otspec/name.htm
1038
1039enum { // platformID
1040 STBTT_PLATFORM_ID_UNICODE =0,
1041 STBTT_PLATFORM_ID_MAC =1,
1042 STBTT_PLATFORM_ID_ISO =2,
1043 STBTT_PLATFORM_ID_MICROSOFT =3
1044};
1045
1046enum { // encodingID for STBTT_PLATFORM_ID_UNICODE
1047 STBTT_UNICODE_EID_UNICODE_1_0 =0,
1048 STBTT_UNICODE_EID_UNICODE_1_1 =1,
1049 STBTT_UNICODE_EID_ISO_10646 =2,
1050 STBTT_UNICODE_EID_UNICODE_2_0_BMP=3,
1051 STBTT_UNICODE_EID_UNICODE_2_0_FULL=4
1052};
1053
1054enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT
1055 STBTT_MS_EID_SYMBOL =0,
1056 STBTT_MS_EID_UNICODE_BMP =1,
1057 STBTT_MS_EID_SHIFTJIS =2,
1058 STBTT_MS_EID_UNICODE_FULL =10
1059};
1060
1061enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes
1062 STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4,
1063 STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5,
1064 STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6,
1065 STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7
1066};
1067
1068enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID...
1069 // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs
1070 STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410,
1071 STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411,
1072 STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412,
1073 STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419,
1074 STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409,
1075 STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D
1076};
1077
1078enum { // languageID for STBTT_PLATFORM_ID_MAC
1079 STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11,
1080 STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23,
1081 STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32,
1082 STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 ,
1083 STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 ,
1084 STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33,
1085 STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19
1086};
1087
1088#ifdef __cplusplus
1089}
1090#endif
1091
1092#endif // __STB_INCLUDE_STB_TRUETYPE_H__
1093
1094///////////////////////////////////////////////////////////////////////////////
1095///////////////////////////////////////////////////////////////////////////////
1096////
1097//// IMPLEMENTATION
1098////
1099////
1100
1101#ifdef STB_TRUETYPE_IMPLEMENTATION
1102
1103#ifndef STBTT_MAX_OVERSAMPLE
1104#define STBTT_MAX_OVERSAMPLE 8
1105#endif
1106
1107#if STBTT_MAX_OVERSAMPLE > 255
1108#error "STBTT_MAX_OVERSAMPLE cannot be > 255"
1109#endif
1110
1111typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1];
1112
1113#ifndef STBTT_RASTERIZER_VERSION
1114#define STBTT_RASTERIZER_VERSION 2
1115#endif
1116
1117#ifdef _MSC_VER
1118#define STBTT__NOTUSED(v) (void)(v)
1119#else
1120#define STBTT__NOTUSED(v) (void)sizeof(v)
1121#endif
1122
1123//////////////////////////////////////////////////////////////////////////
1124//
1125// stbtt__buf helpers to parse data from file
1126//
1127
1128static stbtt_uint8 stbtt__buf_get8(stbtt__buf *b)
1129{
1130 if (b->cursor >= b->size)
1131 return 0;
1132 return b->data[b->cursor++];
1133}
1134
1135static stbtt_uint8 stbtt__buf_peek8(stbtt__buf *b)
1136{
1137 if (b->cursor >= b->size)
1138 return 0;
1139 return b->data[b->cursor];
1140}
1141
1142static void stbtt__buf_seek(stbtt__buf *b, int o)
1143{
1144 STBTT_assert(!(o > b->size || o < 0));
1145 b->cursor = (o > b->size || o < 0) ? b->size : o;
1146}
1147
1148static void stbtt__buf_skip(stbtt__buf *b, int o)
1149{
1150 stbtt__buf_seek(b, b->cursor + o);
1151}
1152
1153static stbtt_uint32 stbtt__buf_get(stbtt__buf *b, int n)
1154{
1155 stbtt_uint32 v = 0;
1156 int i;
1157 STBTT_assert(n >= 1 && n <= 4);
1158 for (i = 0; i < n; i++)
1159 v = (v << 8) | stbtt__buf_get8(b);
1160 return v;
1161}
1162
1163static stbtt__buf stbtt__new_buf(const void *p, size_t size)
1164{
1165 stbtt__buf r;
1166 STBTT_assert(size < 0x40000000);
1167 r.data = (stbtt_uint8*) p;
1168 r.size = (int) size;
1169 r.cursor = 0;
1170 return r;
1171}
1172
1173#define stbtt__buf_get16(b) stbtt__buf_get((b), 2)
1174#define stbtt__buf_get32(b) stbtt__buf_get((b), 4)
1175
1176static stbtt__buf stbtt__buf_range(const stbtt__buf *b, int o, int s)
1177{
1178 stbtt__buf r = stbtt__new_buf(NULL, 0);
1179 if (o < 0 || s < 0 || o > b->size || s > b->size - o) return r;
1180 r.data = b->data + o;
1181 r.size = s;
1182 return r;
1183}
1184
1185static stbtt__buf stbtt__cff_get_index(stbtt__buf *b)
1186{
1187 int count, start, offsize;
1188 start = b->cursor;
1189 count = stbtt__buf_get16(b);
1190 if (count) {
1191 offsize = stbtt__buf_get8(b);
1192 STBTT_assert(offsize >= 1 && offsize <= 4);
1193 stbtt__buf_skip(b, offsize * count);
1194 stbtt__buf_skip(b, stbtt__buf_get(b, offsize) - 1);
1195 }
1196 return stbtt__buf_range(b, start, b->cursor - start);
1197}
1198
1199static stbtt_uint32 stbtt__cff_int(stbtt__buf *b)
1200{
1201 int b0 = stbtt__buf_get8(b);
1202 if (b0 >= 32 && b0 <= 246) return b0 - 139;
1203 else if (b0 >= 247 && b0 <= 250) return (b0 - 247)*256 + stbtt__buf_get8(b) + 108;
1204 else if (b0 >= 251 && b0 <= 254) return -(b0 - 251)*256 - stbtt__buf_get8(b) - 108;
1205 else if (b0 == 28) return stbtt__buf_get16(b);
1206 else if (b0 == 29) return stbtt__buf_get32(b);
1207 STBTT_assert(0);
1208 return 0;
1209}
1210
1211static void stbtt__cff_skip_operand(stbtt__buf *b) {
1212 int v, b0 = stbtt__buf_peek8(b);
1213 STBTT_assert(b0 >= 28);
1214 if (b0 == 30) {
1215 stbtt__buf_skip(b, 1);
1216 while (b->cursor < b->size) {
1217 v = stbtt__buf_get8(b);
1218 if ((v & 0xF) == 0xF || (v >> 4) == 0xF)
1219 break;
1220 }
1221 } else {
1222 stbtt__cff_int(b);
1223 }
1224}
1225
1226static stbtt__buf stbtt__dict_get(stbtt__buf *b, int key)
1227{
1228 stbtt__buf_seek(b, 0);
1229 while (b->cursor < b->size) {
1230 int start = b->cursor, end, op;
1231 while (stbtt__buf_peek8(b) >= 28)
1232 stbtt__cff_skip_operand(b);
1233 end = b->cursor;
1234 op = stbtt__buf_get8(b);
1235 if (op == 12) op = stbtt__buf_get8(b) | 0x100;
1236 if (op == key) return stbtt__buf_range(b, start, end-start);
1237 }
1238 return stbtt__buf_range(b, 0, 0);
1239}
1240
1241static void stbtt__dict_get_ints(stbtt__buf *b, int key, int outcount, stbtt_uint32 *out)
1242{
1243 int i;
1244 stbtt__buf operands = stbtt__dict_get(b, key);
1245 for (i = 0; i < outcount && operands.cursor < operands.size; i++)
1246 out[i] = stbtt__cff_int(&operands);
1247}
1248
1249static int stbtt__cff_index_count(stbtt__buf *b)
1250{
1251 stbtt__buf_seek(b, 0);
1252 return stbtt__buf_get16(b);
1253}
1254
1255static stbtt__buf stbtt__cff_index_get(stbtt__buf b, int i)
1256{
1257 int count, offsize, start, end;
1258 stbtt__buf_seek(&b, 0);
1259 count = stbtt__buf_get16(&b);
1260 offsize = stbtt__buf_get8(&b);
1261 STBTT_assert(i >= 0 && i < count);
1262 STBTT_assert(offsize >= 1 && offsize <= 4);
1263 stbtt__buf_skip(&b, i*offsize);
1264 start = stbtt__buf_get(&b, offsize);
1265 end = stbtt__buf_get(&b, offsize);
1266 return stbtt__buf_range(&b, 2+(count+1)*offsize+start, end - start);
1267}
1268
1269//////////////////////////////////////////////////////////////////////////
1270//
1271// accessors to parse data from file
1272//
1273
1274// on platforms that don't allow misaligned reads, if we want to allow
1275// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE
1276
1277#define ttBYTE(p) (* (stbtt_uint8 *) (p))
1278#define ttCHAR(p) (* (stbtt_int8 *) (p))
1279#define ttFixed(p) ttLONG(p)
1280
1281static stbtt_uint16 ttUSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; }
1282static stbtt_int16 ttSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; }
1283static stbtt_uint32 ttULONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; }
1284static stbtt_int32 ttLONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; }
1285
1286#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3))
1287#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3])
1288
1289static int stbtt__isfont(stbtt_uint8 *font)
1290{
1291 // check the version number
1292 if (stbtt_tag4(font, '1',0,0,0)) return 1; // TrueType 1
1293 if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this!
1294 if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF
1295 if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0
1296 if (stbtt_tag(font, "true")) return 1; // Apple specification for TrueType fonts
1297 return 0;
1298}
1299
1300// @OPTIMIZE: binary search
1301static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag)
1302{
1303 stbtt_int32 num_tables = ttUSHORT(data+fontstart+4);
1304 stbtt_uint32 tabledir = fontstart + 12;
1305 stbtt_int32 i;
1306 for (i=0; i < num_tables; ++i) {
1307 stbtt_uint32 loc = tabledir + 16*i;
1308 if (stbtt_tag(data+loc+0, tag))
1309 return ttULONG(data+loc+8);
1310 }
1311 return 0;
1312}
1313
1314static int stbtt_GetFontOffsetForIndex_internal(unsigned char *font_collection, int index)
1315{
1316 // if it's just a font, there's only one valid index
1317 if (stbtt__isfont(font_collection))
1318 return index == 0 ? 0 : -1;
1319
1320 // check if it's a TTC
1321 if (stbtt_tag(font_collection, "ttcf")) {
1322 // version 1?
1323 if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) {
1324 stbtt_int32 n = ttLONG(font_collection+8);
1325 if (index >= n)
1326 return -1;
1327 return ttULONG(font_collection+12+index*4);
1328 }
1329 }
1330 return -1;
1331}
1332
1333static int stbtt_GetNumberOfFonts_internal(unsigned char *font_collection)
1334{
1335 // if it's just a font, there's only one valid font
1336 if (stbtt__isfont(font_collection))
1337 return 1;
1338
1339 // check if it's a TTC
1340 if (stbtt_tag(font_collection, "ttcf")) {
1341 // version 1?
1342 if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) {
1343 return ttLONG(font_collection+8);
1344 }
1345 }
1346 return 0;
1347}
1348
1349static stbtt__buf stbtt__get_subrs(stbtt__buf cff, stbtt__buf fontdict)
1350{
1351 stbtt_uint32 subrsoff = 0, private_loc[2] = { 0, 0 };
1352 stbtt__buf pdict;
1353 stbtt__dict_get_ints(&fontdict, 18, 2, private_loc);
1354 if (!private_loc[1] || !private_loc[0]) return stbtt__new_buf(NULL, 0);
1355 pdict = stbtt__buf_range(&cff, private_loc[1], private_loc[0]);
1356 stbtt__dict_get_ints(&pdict, 19, 1, &subrsoff);
1357 if (!subrsoff) return stbtt__new_buf(NULL, 0);
1358 stbtt__buf_seek(&cff, private_loc[1]+subrsoff);
1359 return stbtt__cff_get_index(&cff);
1360}
1361
1362// since most people won't use this, find this table the first time it's needed
1363static int stbtt__get_svg(stbtt_fontinfo *info)
1364{
1365 stbtt_uint32 t;
1366 if (info->svg < 0) {
1367 t = stbtt__find_table(info->data, info->fontstart, "SVG ");
1368 if (t) {
1369 stbtt_uint32 offset = ttULONG(info->data + t + 2);
1370 info->svg = t + offset;
1371 } else {
1372 info->svg = 0;
1373 }
1374 }
1375 return info->svg;
1376}
1377
1378static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, int fontstart)
1379{
1380 stbtt_uint32 cmap, t;
1381 stbtt_int32 i,numTables;
1382
1383 info->data = data;
1384 info->fontstart = fontstart;
1385 info->cff = stbtt__new_buf(NULL, 0);
1386
1387 cmap = stbtt__find_table(data, fontstart, "cmap"); // required
1388 info->loca = stbtt__find_table(data, fontstart, "loca"); // required
1389 info->head = stbtt__find_table(data, fontstart, "head"); // required
1390 info->glyf = stbtt__find_table(data, fontstart, "glyf"); // required
1391 info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required
1392 info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required
1393 info->kern = stbtt__find_table(data, fontstart, "kern"); // not required
1394 info->gpos = stbtt__find_table(data, fontstart, "GPOS"); // not required
1395
1396 if (!cmap || !info->head || !info->hhea || !info->hmtx)
1397 return 0;
1398 if (info->glyf) {
1399 // required for truetype
1400 if (!info->loca) return 0;
1401 } else {
1402 // initialization for CFF / Type2 fonts (OTF)
1403 stbtt__buf b, topdict, topdictidx;
1404 stbtt_uint32 cstype = 2, charstrings = 0, fdarrayoff = 0, fdselectoff = 0;
1405 stbtt_uint32 cff;
1406
1407 cff = stbtt__find_table(data, fontstart, "CFF ");
1408 if (!cff) return 0;
1409
1410 info->fontdicts = stbtt__new_buf(NULL, 0);
1411 info->fdselect = stbtt__new_buf(NULL, 0);
1412
1413 // @TODO this should use size from table (not 512MB)
1414 info->cff = stbtt__new_buf(data+cff, 512*1024*1024);
1415 b = info->cff;
1416
1417 // read the header
1418 stbtt__buf_skip(&b, 2);
1419 stbtt__buf_seek(&b, stbtt__buf_get8(&b)); // hdrsize
1420
1421 // @TODO the name INDEX could list multiple fonts,
1422 // but we just use the first one.
1423 stbtt__cff_get_index(&b); // name INDEX
1424 topdictidx = stbtt__cff_get_index(&b);
1425 topdict = stbtt__cff_index_get(topdictidx, 0);
1426 stbtt__cff_get_index(&b); // string INDEX
1427 info->gsubrs = stbtt__cff_get_index(&b);
1428
1429 stbtt__dict_get_ints(&topdict, 17, 1, &charstrings);
1430 stbtt__dict_get_ints(&topdict, 0x100 | 6, 1, &cstype);
1431 stbtt__dict_get_ints(&topdict, 0x100 | 36, 1, &fdarrayoff);
1432 stbtt__dict_get_ints(&topdict, 0x100 | 37, 1, &fdselectoff);
1433 info->subrs = stbtt__get_subrs(b, topdict);
1434
1435 // we only support Type 2 charstrings
1436 if (cstype != 2) return 0;
1437 if (charstrings == 0) return 0;
1438
1439 if (fdarrayoff) {
1440 // looks like a CID font
1441 if (!fdselectoff) return 0;
1442 stbtt__buf_seek(&b, fdarrayoff);
1443 info->fontdicts = stbtt__cff_get_index(&b);
1444 info->fdselect = stbtt__buf_range(&b, fdselectoff, b.size-fdselectoff);
1445 }
1446
1447 stbtt__buf_seek(&b, charstrings);
1448 info->charstrings = stbtt__cff_get_index(&b);
1449 }
1450
1451 t = stbtt__find_table(data, fontstart, "maxp");
1452 if (t)
1453 info->numGlyphs = ttUSHORT(data+t+4);
1454 else
1455 info->numGlyphs = 0xffff;
1456
1457 info->svg = -1;
1458
1459 // find a cmap encoding table we understand *now* to avoid searching
1460 // later. (todo: could make this installable)
1461 // the same regardless of glyph.
1462 numTables = ttUSHORT(data + cmap + 2);
1463 info->index_map = 0;
1464 for (i=0; i < numTables; ++i) {
1465 stbtt_uint32 encoding_record = cmap + 4 + 8 * i;
1466 // find an encoding we understand:
1467 switch(ttUSHORT(data+encoding_record)) {
1468 case STBTT_PLATFORM_ID_MICROSOFT:
1469 switch (ttUSHORT(data+encoding_record+2)) {
1470 case STBTT_MS_EID_UNICODE_BMP:
1471 case STBTT_MS_EID_UNICODE_FULL:
1472 // MS/Unicode
1473 info->index_map = cmap + ttULONG(data+encoding_record+4);
1474 break;
1475 }
1476 break;
1477 case STBTT_PLATFORM_ID_UNICODE:
1478 // Mac/iOS has these
1479 // all the encodingIDs are unicode, so we don't bother to check it
1480 info->index_map = cmap + ttULONG(data+encoding_record+4);
1481 break;
1482 }
1483 }
1484 if (info->index_map == 0)
1485 return 0;
1486
1487 info->indexToLocFormat = ttUSHORT(data+info->head + 50);
1488 return 1;
1489}
1490
1491STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint)
1492{
1493 stbtt_uint8 *data = info->data;
1494 stbtt_uint32 index_map = info->index_map;
1495
1496 stbtt_uint16 format = ttUSHORT(data + index_map + 0);
1497 if (format == 0) { // apple byte encoding
1498 stbtt_int32 bytes = ttUSHORT(data + index_map + 2);
1499 if (unicode_codepoint < bytes-6)
1500 return ttBYTE(data + index_map + 6 + unicode_codepoint);
1501 return 0;
1502 } else if (format == 6) {
1503 stbtt_uint32 first = ttUSHORT(data + index_map + 6);
1504 stbtt_uint32 count = ttUSHORT(data + index_map + 8);
1505 if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count)
1506 return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2);
1507 return 0;
1508 } else if (format == 2) {
1509 STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean
1510 return 0;
1511 } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges
1512 stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1;
1513 stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1;
1514 stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10);
1515 stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1;
1516
1517 // do a binary search of the segments
1518 stbtt_uint32 endCount = index_map + 14;
1519 stbtt_uint32 search = endCount;
1520
1521 if (unicode_codepoint > 0xffff)
1522 return 0;
1523
1524 // they lie from endCount .. endCount + segCount
1525 // but searchRange is the nearest power of two, so...
1526 if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2))
1527 search += rangeShift*2;
1528
1529 // now decrement to bias correctly to find smallest
1530 search -= 2;
1531 while (entrySelector) {
1532 stbtt_uint16 end;
1533 searchRange >>= 1;
1534 end = ttUSHORT(data + search + searchRange*2);
1535 if (unicode_codepoint > end)
1536 search += searchRange*2;
1537 --entrySelector;
1538 }
1539 search += 2;
1540
1541 {
1542 stbtt_uint16 offset, start;
1543 stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1);
1544
1545 STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item));
1546 start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item);
1547 if (unicode_codepoint < start)
1548 return 0;
1549
1550 offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item);
1551 if (offset == 0)
1552 return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item));
1553
1554 return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item);
1555 }
1556 } else if (format == 12 || format == 13) {
1557 stbtt_uint32 ngroups = ttULONG(data+index_map+12);
1558 stbtt_int32 low,high;
1559 low = 0; high = (stbtt_int32)ngroups;
1560 // Binary search the right group.
1561 while (low < high) {
1562 stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high
1563 stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12);
1564 stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4);
1565 if ((stbtt_uint32) unicode_codepoint < start_char)
1566 high = mid;
1567 else if ((stbtt_uint32) unicode_codepoint > end_char)
1568 low = mid+1;
1569 else {
1570 stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8);
1571 if (format == 12)
1572 return start_glyph + unicode_codepoint-start_char;
1573 else // format == 13
1574 return start_glyph;
1575 }
1576 }
1577 return 0; // not found
1578 }
1579 // @TODO
1580 STBTT_assert(0);
1581 return 0;
1582}
1583
1584STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices)
1585{
1586 return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices);
1587}
1588
1589static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy)
1590{
1591 v->type = type;
1592 v->x = (stbtt_int16) x;
1593 v->y = (stbtt_int16) y;
1594 v->cx = (stbtt_int16) cx;
1595 v->cy = (stbtt_int16) cy;
1596}
1597
1598static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index)
1599{
1600 int g1,g2;
1601
1602 STBTT_assert(!info->cff.size);
1603
1604 if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range
1605 if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format
1606
1607 if (info->indexToLocFormat == 0) {
1608 g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2;
1609 g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2;
1610 } else {
1611 g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4);
1612 g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4);
1613 }
1614
1615 return g1==g2 ? -1 : g1; // if length is 0, return -1
1616}
1617
1618static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1);
1619
1620STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1)
1621{
1622 if (info->cff.size) {
1623 stbtt__GetGlyphInfoT2(info, glyph_index, x0, y0, x1, y1);
1624 } else {
1625 int g = stbtt__GetGlyfOffset(info, glyph_index);
1626 if (g < 0) return 0;
1627
1628 if (x0) *x0 = ttSHORT(info->data + g + 2);
1629 if (y0) *y0 = ttSHORT(info->data + g + 4);
1630 if (x1) *x1 = ttSHORT(info->data + g + 6);
1631 if (y1) *y1 = ttSHORT(info->data + g + 8);
1632 }
1633 return 1;
1634}
1635
1636STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1)
1637{
1638 return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1);
1639}
1640
1641STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index)
1642{
1643 stbtt_int16 numberOfContours;
1644 int g;
1645 if (info->cff.size)
1646 return stbtt__GetGlyphInfoT2(info, glyph_index, NULL, NULL, NULL, NULL) == 0;
1647 g = stbtt__GetGlyfOffset(info, glyph_index);
1648 if (g < 0) return 1;
1649 numberOfContours = ttSHORT(info->data + g);
1650 return numberOfContours == 0;
1651}
1652
1653static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off,
1654 stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy)
1655{
1656 if (start_off) {
1657 if (was_off)
1658 stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy);
1659 stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy);
1660 } else {
1661 if (was_off)
1662 stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy);
1663 else
1664 stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0);
1665 }
1666 return num_vertices;
1667}
1668
1669static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices)
1670{
1671 stbtt_int16 numberOfContours;
1672 stbtt_uint8 *endPtsOfContours;
1673 stbtt_uint8 *data = info->data;
1674 stbtt_vertex *vertices=0;
1675 int num_vertices=0;
1676 int g = stbtt__GetGlyfOffset(info, glyph_index);
1677
1678 *pvertices = NULL;
1679
1680 if (g < 0) return 0;
1681
1682 numberOfContours = ttSHORT(data + g);
1683
1684 if (numberOfContours > 0) {
1685 stbtt_uint8 flags=0,flagcount;
1686 stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0;
1687 stbtt_int32 x,y,cx,cy,sx,sy, scx,scy;
1688 stbtt_uint8 *points;
1689 endPtsOfContours = (data + g + 10);
1690 ins = ttUSHORT(data + g + 10 + numberOfContours * 2);
1691 points = data + g + 10 + numberOfContours * 2 + 2 + ins;
1692
1693 n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2);
1694
1695 m = n + 2*numberOfContours; // a loose bound on how many vertices we might need
1696 vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata);
1697 if (vertices == 0)
1698 return 0;
1699
1700 next_move = 0;
1701 flagcount=0;
1702
1703 // in first pass, we load uninterpreted data into the allocated array
1704 // above, shifted to the end of the array so we won't overwrite it when
1705 // we create our final data starting from the front
1706
1707 off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated
1708
1709 // first load flags
1710
1711 for (i=0; i < n; ++i) {
1712 if (flagcount == 0) {
1713 flags = *points++;
1714 if (flags & 8)
1715 flagcount = *points++;
1716 } else
1717 --flagcount;
1718 vertices[off+i].type = flags;
1719 }
1720
1721 // now load x coordinates
1722 x=0;
1723 for (i=0; i < n; ++i) {
1724 flags = vertices[off+i].type;
1725 if (flags & 2) {
1726 stbtt_int16 dx = *points++;
1727 x += (flags & 16) ? dx : -dx; // ???
1728 } else {
1729 if (!(flags & 16)) {
1730 x = x + (stbtt_int16) (points[0]*256 + points[1]);
1731 points += 2;
1732 }
1733 }
1734 vertices[off+i].x = (stbtt_int16) x;
1735 }
1736
1737 // now load y coordinates
1738 y=0;
1739 for (i=0; i < n; ++i) {
1740 flags = vertices[off+i].type;
1741 if (flags & 4) {
1742 stbtt_int16 dy = *points++;
1743 y += (flags & 32) ? dy : -dy; // ???
1744 } else {
1745 if (!(flags & 32)) {
1746 y = y + (stbtt_int16) (points[0]*256 + points[1]);
1747 points += 2;
1748 }
1749 }
1750 vertices[off+i].y = (stbtt_int16) y;
1751 }
1752
1753 // now convert them to our format
1754 num_vertices=0;
1755 sx = sy = cx = cy = scx = scy = 0;
1756 for (i=0; i < n; ++i) {
1757 flags = vertices[off+i].type;
1758 x = (stbtt_int16) vertices[off+i].x;
1759 y = (stbtt_int16) vertices[off+i].y;
1760
1761 if (next_move == i) {
1762 if (i != 0)
1763 num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy);
1764
1765 // now start the new one
1766 start_off = !(flags & 1);
1767 if (start_off) {
1768 // if we start off with an off-curve point, then when we need to find a point on the curve
1769 // where we can start, and we need to save some state for when we wraparound.
1770 scx = x;
1771 scy = y;
1772 if (!(vertices[off+i+1].type & 1)) {
1773 // next point is also a curve point, so interpolate an on-point curve
1774 sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1;
1775 sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1;
1776 } else {
1777 // otherwise just use the next point as our start point
1778 sx = (stbtt_int32) vertices[off+i+1].x;
1779 sy = (stbtt_int32) vertices[off+i+1].y;
1780 ++i; // we're using point i+1 as the starting point, so skip it
1781 }
1782 } else {
1783 sx = x;
1784 sy = y;
1785 }
1786 stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0);
1787 was_off = 0;
1788 next_move = 1 + ttUSHORT(endPtsOfContours+j*2);
1789 ++j;
1790 } else {
1791 if (!(flags & 1)) { // if it's a curve
1792 if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint
1793 stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy);
1794 cx = x;
1795 cy = y;
1796 was_off = 1;
1797 } else {
1798 if (was_off)
1799 stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy);
1800 else
1801 stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0);
1802 was_off = 0;
1803 }
1804 }
1805 }
1806 num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy);
1807 } else if (numberOfContours < 0) {
1808 // Compound shapes.
1809 int more = 1;
1810 stbtt_uint8 *comp = data + g + 10;
1811 num_vertices = 0;
1812 vertices = 0;
1813 while (more) {
1814 stbtt_uint16 flags, gidx;
1815 int comp_num_verts = 0, i;
1816 stbtt_vertex *comp_verts = 0, *tmp = 0;
1817 float mtx[6] = {1,0,0,1,0,0}, m, n;
1818
1819 flags = ttSHORT(comp); comp+=2;
1820 gidx = ttSHORT(comp); comp+=2;
1821
1822 if (flags & 2) { // XY values
1823 if (flags & 1) { // shorts
1824 mtx[4] = ttSHORT(comp); comp+=2;
1825 mtx[5] = ttSHORT(comp); comp+=2;
1826 } else {
1827 mtx[4] = ttCHAR(comp); comp+=1;
1828 mtx[5] = ttCHAR(comp); comp+=1;
1829 }
1830 }
1831 else {
1832 // @TODO handle matching point
1833 STBTT_assert(0);
1834 }
1835 if (flags & (1<<3)) { // WE_HAVE_A_SCALE
1836 mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;
1837 mtx[1] = mtx[2] = 0;
1838 } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE
1839 mtx[0] = ttSHORT(comp)/16384.0f; comp+=2;
1840 mtx[1] = mtx[2] = 0;
1841 mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;
1842 } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO
1843 mtx[0] = ttSHORT(comp)/16384.0f; comp+=2;
1844 mtx[1] = ttSHORT(comp)/16384.0f; comp+=2;
1845 mtx[2] = ttSHORT(comp)/16384.0f; comp+=2;
1846 mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;
1847 }
1848
1849 // Find transformation scales.
1850 m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]);
1851 n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]);
1852
1853 // Get indexed glyph.
1854 comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts);
1855 if (comp_num_verts > 0) {
1856 // Transform vertices.
1857 for (i = 0; i < comp_num_verts; ++i) {
1858 stbtt_vertex* v = &comp_verts[i];
1859 stbtt_vertex_type x,y;
1860 x=v->x; y=v->y;
1861 v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4]));
1862 v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5]));
1863 x=v->cx; y=v->cy;
1864 v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4]));
1865 v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5]));
1866 }
1867 // Append vertices.
1868 tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata);
1869 if (!tmp) {
1870 if (vertices) STBTT_free(vertices, info->userdata);
1871 if (comp_verts) STBTT_free(comp_verts, info->userdata);
1872 return 0;
1873 }
1874 if (num_vertices > 0) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex));
1875 STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex));
1876 if (vertices) STBTT_free(vertices, info->userdata);
1877 vertices = tmp;
1878 STBTT_free(comp_verts, info->userdata);
1879 num_vertices += comp_num_verts;
1880 }
1881 // More components ?
1882 more = flags & (1<<5);
1883 }
1884 } else {
1885 // numberOfCounters == 0, do nothing
1886 }
1887
1888 *pvertices = vertices;
1889 return num_vertices;
1890}
1891
1892typedef struct
1893{
1894 int bounds;
1895 int started;
1896 float first_x, first_y;
1897 float x, y;
1898 stbtt_int32 min_x, max_x, min_y, max_y;
1899
1900 stbtt_vertex *pvertices;
1901 int num_vertices;
1902} stbtt__csctx;
1903
1904#define STBTT__CSCTX_INIT(bounds) {bounds,0, 0,0, 0,0, 0,0,0,0, NULL, 0}
1905
1906static void stbtt__track_vertex(stbtt__csctx *c, stbtt_int32 x, stbtt_int32 y)
1907{
1908 if (x > c->max_x || !c->started) c->max_x = x;
1909 if (y > c->max_y || !c->started) c->max_y = y;
1910 if (x < c->min_x || !c->started) c->min_x = x;
1911 if (y < c->min_y || !c->started) c->min_y = y;
1912 c->started = 1;
1913}
1914
1915static void stbtt__csctx_v(stbtt__csctx *c, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy, stbtt_int32 cx1, stbtt_int32 cy1)
1916{
1917 if (c->bounds) {
1918 stbtt__track_vertex(c, x, y);
1919 if (type == STBTT_vcubic) {
1920 stbtt__track_vertex(c, cx, cy);
1921 stbtt__track_vertex(c, cx1, cy1);
1922 }
1923 } else {
1924 stbtt_setvertex(&c->pvertices[c->num_vertices], type, x, y, cx, cy);
1925 c->pvertices[c->num_vertices].cx1 = (stbtt_int16) cx1;
1926 c->pvertices[c->num_vertices].cy1 = (stbtt_int16) cy1;
1927 }
1928 c->num_vertices++;
1929}
1930
1931static void stbtt__csctx_close_shape(stbtt__csctx *ctx)
1932{
1933 if (ctx->first_x != ctx->x || ctx->first_y != ctx->y)
1934 stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->first_x, (int)ctx->first_y, 0, 0, 0, 0);
1935}
1936
1937static void stbtt__csctx_rmove_to(stbtt__csctx *ctx, float dx, float dy)
1938{
1939 stbtt__csctx_close_shape(ctx);
1940 ctx->first_x = ctx->x = ctx->x + dx;
1941 ctx->first_y = ctx->y = ctx->y + dy;
1942 stbtt__csctx_v(ctx, STBTT_vmove, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0);
1943}
1944
1945static void stbtt__csctx_rline_to(stbtt__csctx *ctx, float dx, float dy)
1946{
1947 ctx->x += dx;
1948 ctx->y += dy;
1949 stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0);
1950}
1951
1952static void stbtt__csctx_rccurve_to(stbtt__csctx *ctx, float dx1, float dy1, float dx2, float dy2, float dx3, float dy3)
1953{
1954 float cx1 = ctx->x + dx1;
1955 float cy1 = ctx->y + dy1;
1956 float cx2 = cx1 + dx2;
1957 float cy2 = cy1 + dy2;
1958 ctx->x = cx2 + dx3;
1959 ctx->y = cy2 + dy3;
1960 stbtt__csctx_v(ctx, STBTT_vcubic, (int)ctx->x, (int)ctx->y, (int)cx1, (int)cy1, (int)cx2, (int)cy2);
1961}
1962
1963static stbtt__buf stbtt__get_subr(stbtt__buf idx, int n)
1964{
1965 int count = stbtt__cff_index_count(&idx);
1966 int bias = 107;
1967 if (count >= 33900)
1968 bias = 32768;
1969 else if (count >= 1240)
1970 bias = 1131;
1971 n += bias;
1972 if (n < 0 || n >= count)
1973 return stbtt__new_buf(NULL, 0);
1974 return stbtt__cff_index_get(idx, n);
1975}
1976
1977static stbtt__buf stbtt__cid_get_glyph_subrs(const stbtt_fontinfo *info, int glyph_index)
1978{
1979 stbtt__buf fdselect = info->fdselect;
1980 int nranges, start, end, v, fmt, fdselector = -1, i;
1981
1982 stbtt__buf_seek(&fdselect, 0);
1983 fmt = stbtt__buf_get8(&fdselect);
1984 if (fmt == 0) {
1985 // untested
1986 stbtt__buf_skip(&fdselect, glyph_index);
1987 fdselector = stbtt__buf_get8(&fdselect);
1988 } else if (fmt == 3) {
1989 nranges = stbtt__buf_get16(&fdselect);
1990 start = stbtt__buf_get16(&fdselect);
1991 for (i = 0; i < nranges; i++) {
1992 v = stbtt__buf_get8(&fdselect);
1993 end = stbtt__buf_get16(&fdselect);
1994 if (glyph_index >= start && glyph_index < end) {
1995 fdselector = v;
1996 break;
1997 }
1998 start = end;
1999 }
2000 }
2001 if (fdselector == -1) stbtt__new_buf(NULL, 0);
2002 return stbtt__get_subrs(info->cff, stbtt__cff_index_get(info->fontdicts, fdselector));
2003}
2004
2005static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, stbtt__csctx *c)
2006{
2007 int in_header = 1, maskbits = 0, subr_stack_height = 0, sp = 0, v, i, b0;
2008 int has_subrs = 0, clear_stack;
2009 float s[48];
2010 stbtt__buf subr_stack[10], subrs = info->subrs, b;
2011 float f;
2012
2013#define STBTT__CSERR(s) (0)
2014
2015 // this currently ignores the initial width value, which isn't needed if we have hmtx
2016 b = stbtt__cff_index_get(info->charstrings, glyph_index);
2017 while (b.cursor < b.size) {
2018 i = 0;
2019 clear_stack = 1;
2020 b0 = stbtt__buf_get8(&b);
2021 switch (b0) {
2022 // @TODO implement hinting
2023 case 0x13: // hintmask
2024 case 0x14: // cntrmask
2025 if (in_header)
2026 maskbits += (sp / 2); // implicit "vstem"
2027 in_header = 0;
2028 stbtt__buf_skip(&b, (maskbits + 7) / 8);
2029 break;
2030
2031 case 0x01: // hstem
2032 case 0x03: // vstem
2033 case 0x12: // hstemhm
2034 case 0x17: // vstemhm
2035 maskbits += (sp / 2);
2036 break;
2037
2038 case 0x15: // rmoveto
2039 in_header = 0;
2040 if (sp < 2) return STBTT__CSERR("rmoveto stack");
2041 stbtt__csctx_rmove_to(c, s[sp-2], s[sp-1]);
2042 break;
2043 case 0x04: // vmoveto
2044 in_header = 0;
2045 if (sp < 1) return STBTT__CSERR("vmoveto stack");
2046 stbtt__csctx_rmove_to(c, 0, s[sp-1]);
2047 break;
2048 case 0x16: // hmoveto
2049 in_header = 0;
2050 if (sp < 1) return STBTT__CSERR("hmoveto stack");
2051 stbtt__csctx_rmove_to(c, s[sp-1], 0);
2052 break;
2053
2054 case 0x05: // rlineto
2055 if (sp < 2) return STBTT__CSERR("rlineto stack");
2056 for (; i + 1 < sp; i += 2)
2057 stbtt__csctx_rline_to(c, s[i], s[i+1]);
2058 break;
2059
2060 // hlineto/vlineto and vhcurveto/hvcurveto alternate horizontal and vertical
2061 // starting from a different place.
2062
2063 case 0x07: // vlineto
2064 if (sp < 1) return STBTT__CSERR("vlineto stack");
2065 goto vlineto;
2066 case 0x06: // hlineto
2067 if (sp < 1) return STBTT__CSERR("hlineto stack");
2068 for (;;) {
2069 if (i >= sp) break;
2070 stbtt__csctx_rline_to(c, s[i], 0);
2071 i++;
2072 vlineto:
2073 if (i >= sp) break;
2074 stbtt__csctx_rline_to(c, 0, s[i]);
2075 i++;
2076 }
2077 break;
2078
2079 case 0x1F: // hvcurveto
2080 if (sp < 4) return STBTT__CSERR("hvcurveto stack");
2081 goto hvcurveto;
2082 case 0x1E: // vhcurveto
2083 if (sp < 4) return STBTT__CSERR("vhcurveto stack");
2084 for (;;) {
2085 if (i + 3 >= sp) break;
2086 stbtt__csctx_rccurve_to(c, 0, s[i], s[i+1], s[i+2], s[i+3], (sp - i == 5) ? s[i + 4] : 0.0f);
2087 i += 4;
2088 hvcurveto:
2089 if (i + 3 >= sp) break;
2090 stbtt__csctx_rccurve_to(c, s[i], 0, s[i+1], s[i+2], (sp - i == 5) ? s[i+4] : 0.0f, s[i+3]);
2091 i += 4;
2092 }
2093 break;
2094
2095 case 0x08: // rrcurveto
2096 if (sp < 6) return STBTT__CSERR("rcurveline stack");
2097 for (; i + 5 < sp; i += 6)
2098 stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]);
2099 break;
2100
2101 case 0x18: // rcurveline
2102 if (sp < 8) return STBTT__CSERR("rcurveline stack");
2103 for (; i + 5 < sp - 2; i += 6)
2104 stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]);
2105 if (i + 1 >= sp) return STBTT__CSERR("rcurveline stack");
2106 stbtt__csctx_rline_to(c, s[i], s[i+1]);
2107 break;
2108
2109 case 0x19: // rlinecurve
2110 if (sp < 8) return STBTT__CSERR("rlinecurve stack");
2111 for (; i + 1 < sp - 6; i += 2)
2112 stbtt__csctx_rline_to(c, s[i], s[i+1]);
2113 if (i + 5 >= sp) return STBTT__CSERR("rlinecurve stack");
2114 stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]);
2115 break;
2116
2117 case 0x1A: // vvcurveto
2118 case 0x1B: // hhcurveto
2119 if (sp < 4) return STBTT__CSERR("(vv|hh)curveto stack");
2120 f = 0.0;
2121 if (sp & 1) { f = s[i]; i++; }
2122 for (; i + 3 < sp; i += 4) {
2123 if (b0 == 0x1B)
2124 stbtt__csctx_rccurve_to(c, s[i], f, s[i+1], s[i+2], s[i+3], 0.0);
2125 else
2126 stbtt__csctx_rccurve_to(c, f, s[i], s[i+1], s[i+2], 0.0, s[i+3]);
2127 f = 0.0;
2128 }
2129 break;
2130
2131 case 0x0A: // callsubr
2132 if (!has_subrs) {
2133 if (info->fdselect.size)
2134 subrs = stbtt__cid_get_glyph_subrs(info, glyph_index);
2135 has_subrs = 1;
2136 }
2137 // fallthrough
2138 case 0x1D: // callgsubr
2139 if (sp < 1) return STBTT__CSERR("call(g|)subr stack");
2140 v = (int) s[--sp];
2141 if (subr_stack_height >= 10) return STBTT__CSERR("recursion limit");
2142 subr_stack[subr_stack_height++] = b;
2143 b = stbtt__get_subr(b0 == 0x0A ? subrs : info->gsubrs, v);
2144 if (b.size == 0) return STBTT__CSERR("subr not found");
2145 b.cursor = 0;
2146 clear_stack = 0;
2147 break;
2148
2149 case 0x0B: // return
2150 if (subr_stack_height <= 0) return STBTT__CSERR("return outside subr");
2151 b = subr_stack[--subr_stack_height];
2152 clear_stack = 0;
2153 break;
2154
2155 case 0x0E: // endchar
2156 stbtt__csctx_close_shape(c);
2157 return 1;
2158
2159 case 0x0C: { // two-byte escape
2160 float dx1, dx2, dx3, dx4, dx5, dx6, dy1, dy2, dy3, dy4, dy5, dy6;
2161 float dx, dy;
2162 int b1 = stbtt__buf_get8(&b);
2163 switch (b1) {
2164 // @TODO These "flex" implementations ignore the flex-depth and resolution,
2165 // and always draw beziers.
2166 case 0x22: // hflex
2167 if (sp < 7) return STBTT__CSERR("hflex stack");
2168 dx1 = s[0];
2169 dx2 = s[1];
2170 dy2 = s[2];
2171 dx3 = s[3];
2172 dx4 = s[4];
2173 dx5 = s[5];
2174 dx6 = s[6];
2175 stbtt__csctx_rccurve_to(c, dx1, 0, dx2, dy2, dx3, 0);
2176 stbtt__csctx_rccurve_to(c, dx4, 0, dx5, -dy2, dx6, 0);
2177 break;
2178
2179 case 0x23: // flex
2180 if (sp < 13) return STBTT__CSERR("flex stack");
2181 dx1 = s[0];
2182 dy1 = s[1];
2183 dx2 = s[2];
2184 dy2 = s[3];
2185 dx3 = s[4];
2186 dy3 = s[5];
2187 dx4 = s[6];
2188 dy4 = s[7];
2189 dx5 = s[8];
2190 dy5 = s[9];
2191 dx6 = s[10];
2192 dy6 = s[11];
2193 //fd is s[12]
2194 stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3);
2195 stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6);
2196 break;
2197
2198 case 0x24: // hflex1
2199 if (sp < 9) return STBTT__CSERR("hflex1 stack");
2200 dx1 = s[0];
2201 dy1 = s[1];
2202 dx2 = s[2];
2203 dy2 = s[3];
2204 dx3 = s[4];
2205 dx4 = s[5];
2206 dx5 = s[6];
2207 dy5 = s[7];
2208 dx6 = s[8];
2209 stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, 0);
2210 stbtt__csctx_rccurve_to(c, dx4, 0, dx5, dy5, dx6, -(dy1+dy2+dy5));
2211 break;
2212
2213 case 0x25: // flex1
2214 if (sp < 11) return STBTT__CSERR("flex1 stack");
2215 dx1 = s[0];
2216 dy1 = s[1];
2217 dx2 = s[2];
2218 dy2 = s[3];
2219 dx3 = s[4];
2220 dy3 = s[5];
2221 dx4 = s[6];
2222 dy4 = s[7];
2223 dx5 = s[8];
2224 dy5 = s[9];
2225 dx6 = dy6 = s[10];
2226 dx = dx1+dx2+dx3+dx4+dx5;
2227 dy = dy1+dy2+dy3+dy4+dy5;
2228 if (STBTT_fabs(dx) > STBTT_fabs(dy))
2229 dy6 = -dy;
2230 else
2231 dx6 = -dx;
2232 stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3);
2233 stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6);
2234 break;
2235
2236 default:
2237 return STBTT__CSERR("unimplemented");
2238 }
2239 } break;
2240
2241 default:
2242 if (b0 != 255 && b0 != 28 && (b0 < 32 || b0 > 254))
2243 return STBTT__CSERR("reserved operator");
2244
2245 // push immediate
2246 if (b0 == 255) {
2247 f = (float)(stbtt_int32)stbtt__buf_get32(&b) / 0x10000;
2248 } else {
2249 stbtt__buf_skip(&b, -1);
2250 f = (float)(stbtt_int16)stbtt__cff_int(&b);
2251 }
2252 if (sp >= 48) return STBTT__CSERR("push stack overflow");
2253 s[sp++] = f;
2254 clear_stack = 0;
2255 break;
2256 }
2257 if (clear_stack) sp = 0;
2258 }
2259 return STBTT__CSERR("no endchar");
2260
2261#undef STBTT__CSERR
2262}
2263
2264static int stbtt__GetGlyphShapeT2(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices)
2265{
2266 // runs the charstring twice, once to count and once to output (to avoid realloc)
2267 stbtt__csctx count_ctx = STBTT__CSCTX_INIT(1);
2268 stbtt__csctx output_ctx = STBTT__CSCTX_INIT(0);
2269 if (stbtt__run_charstring(info, glyph_index, &count_ctx)) {
2270 *pvertices = (stbtt_vertex*)STBTT_malloc(count_ctx.num_vertices*sizeof(stbtt_vertex), info->userdata);
2271 output_ctx.pvertices = *pvertices;
2272 if (stbtt__run_charstring(info, glyph_index, &output_ctx)) {
2273 STBTT_assert(output_ctx.num_vertices == count_ctx.num_vertices);
2274 return output_ctx.num_vertices;
2275 }
2276 }
2277 *pvertices = NULL;
2278 return 0;
2279}
2280
2281static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1)
2282{
2283 stbtt__csctx c = STBTT__CSCTX_INIT(1);
2284 int r = stbtt__run_charstring(info, glyph_index, &c);
2285 if (x0) *x0 = r ? c.min_x : 0;
2286 if (y0) *y0 = r ? c.min_y : 0;
2287 if (x1) *x1 = r ? c.max_x : 0;
2288 if (y1) *y1 = r ? c.max_y : 0;
2289 return r ? c.num_vertices : 0;
2290}
2291
2292STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices)
2293{
2294 if (!info->cff.size)
2295 return stbtt__GetGlyphShapeTT(info, glyph_index, pvertices);
2296 else
2297 return stbtt__GetGlyphShapeT2(info, glyph_index, pvertices);
2298}
2299
2300STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing)
2301{
2302 stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34);
2303 if (glyph_index < numOfLongHorMetrics) {
2304 if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index);
2305 if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2);
2306 } else {
2307 if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1));
2308 if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics));
2309 }
2310}
2311
2312STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info)
2313{
2314 stbtt_uint8 *data = info->data + info->kern;
2315
2316 // we only look at the first table. it must be 'horizontal' and format 0.
2317 if (!info->kern)
2318 return 0;
2319 if (ttUSHORT(data+2) < 1) // number of tables, need at least 1
2320 return 0;
2321 if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format
2322 return 0;
2323
2324 return ttUSHORT(data+10);
2325}
2326
2327STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length)
2328{
2329 stbtt_uint8 *data = info->data + info->kern;
2330 int k, length;
2331
2332 // we only look at the first table. it must be 'horizontal' and format 0.
2333 if (!info->kern)
2334 return 0;
2335 if (ttUSHORT(data+2) < 1) // number of tables, need at least 1
2336 return 0;
2337 if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format
2338 return 0;
2339
2340 length = ttUSHORT(data+10);
2341 if (table_length < length)
2342 length = table_length;
2343
2344 for (k = 0; k < length; k++)
2345 {
2346 table[k].glyph1 = ttUSHORT(data+18+(k*6));
2347 table[k].glyph2 = ttUSHORT(data+20+(k*6));
2348 table[k].advance = ttSHORT(data+22+(k*6));
2349 }
2350
2351 return length;
2352}
2353
2354static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)
2355{
2356 stbtt_uint8 *data = info->data + info->kern;
2357 stbtt_uint32 needle, straw;
2358 int l, r, m;
2359
2360 // we only look at the first table. it must be 'horizontal' and format 0.
2361 if (!info->kern)
2362 return 0;
2363 if (ttUSHORT(data+2) < 1) // number of tables, need at least 1
2364 return 0;
2365 if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format
2366 return 0;
2367
2368 l = 0;
2369 r = ttUSHORT(data+10) - 1;
2370 needle = glyph1 << 16 | glyph2;
2371 while (l <= r) {
2372 m = (l + r) >> 1;
2373 straw = ttULONG(data+18+(m*6)); // note: unaligned read
2374 if (needle < straw)
2375 r = m - 1;
2376 else if (needle > straw)
2377 l = m + 1;
2378 else
2379 return ttSHORT(data+22+(m*6));
2380 }
2381 return 0;
2382}
2383
2384static stbtt_int32 stbtt__GetCoverageIndex(stbtt_uint8 *coverageTable, int glyph)
2385{
2386 stbtt_uint16 coverageFormat = ttUSHORT(coverageTable);
2387 switch(coverageFormat) {
2388 case 1: {
2389 stbtt_uint16 glyphCount = ttUSHORT(coverageTable + 2);
2390
2391 // Binary search.
2392 stbtt_int32 l=0, r=glyphCount-1, m;
2393 int straw, needle=glyph;
2394 while (l <= r) {
2395 stbtt_uint8 *glyphArray = coverageTable + 4;
2396 stbtt_uint16 glyphID;
2397 m = (l + r) >> 1;
2398 glyphID = ttUSHORT(glyphArray + 2 * m);
2399 straw = glyphID;
2400 if (needle < straw)
2401 r = m - 1;
2402 else if (needle > straw)
2403 l = m + 1;
2404 else {
2405 return m;
2406 }
2407 }
2408 } break;
2409
2410 case 2: {
2411 stbtt_uint16 rangeCount = ttUSHORT(coverageTable + 2);
2412 stbtt_uint8 *rangeArray = coverageTable + 4;
2413
2414 // Binary search.
2415 stbtt_int32 l=0, r=rangeCount-1, m;
2416 int strawStart, strawEnd, needle=glyph;
2417 while (l <= r) {
2418 stbtt_uint8 *rangeRecord;
2419 m = (l + r) >> 1;
2420 rangeRecord = rangeArray + 6 * m;
2421 strawStart = ttUSHORT(rangeRecord);
2422 strawEnd = ttUSHORT(rangeRecord + 2);
2423 if (needle < strawStart)
2424 r = m - 1;
2425 else if (needle > strawEnd)
2426 l = m + 1;
2427 else {
2428 stbtt_uint16 startCoverageIndex = ttUSHORT(rangeRecord + 4);
2429 return startCoverageIndex + glyph - strawStart;
2430 }
2431 }
2432 } break;
2433
2434 default: {
2435 // There are no other cases.
2436 STBTT_assert(0);
2437 } break;
2438 }
2439
2440 return -1;
2441}
2442
2443static stbtt_int32 stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph)
2444{
2445 stbtt_uint16 classDefFormat = ttUSHORT(classDefTable);
2446 switch(classDefFormat)
2447 {
2448 case 1: {
2449 stbtt_uint16 startGlyphID = ttUSHORT(classDefTable + 2);
2450 stbtt_uint16 glyphCount = ttUSHORT(classDefTable + 4);
2451 stbtt_uint8 *classDef1ValueArray = classDefTable + 6;
2452
2453 if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount)
2454 return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID));
2455
2456 classDefTable = classDef1ValueArray + 2 * glyphCount;
2457 } break;
2458
2459 case 2: {
2460 stbtt_uint16 classRangeCount = ttUSHORT(classDefTable + 2);
2461 stbtt_uint8 *classRangeRecords = classDefTable + 4;
2462
2463 // Binary search.
2464 stbtt_int32 l=0, r=classRangeCount-1, m;
2465 int strawStart, strawEnd, needle=glyph;
2466 while (l <= r) {
2467 stbtt_uint8 *classRangeRecord;
2468 m = (l + r) >> 1;
2469 classRangeRecord = classRangeRecords + 6 * m;
2470 strawStart = ttUSHORT(classRangeRecord);
2471 strawEnd = ttUSHORT(classRangeRecord + 2);
2472 if (needle < strawStart)
2473 r = m - 1;
2474 else if (needle > strawEnd)
2475 l = m + 1;
2476 else
2477 return (stbtt_int32)ttUSHORT(classRangeRecord + 4);
2478 }
2479
2480 classDefTable = classRangeRecords + 6 * classRangeCount;
2481 } break;
2482
2483 default: {
2484 // There are no other cases.
2485 STBTT_assert(0);
2486 } break;
2487 }
2488
2489 return -1;
2490}
2491
2492// Define to STBTT_assert(x) if you want to break on unimplemented formats.
2493#define STBTT_GPOS_TODO_assert(x)
2494
2495static stbtt_int32 stbtt__GetGlyphGPOSInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)
2496{
2497 stbtt_uint16 lookupListOffset;
2498 stbtt_uint8 *lookupList;
2499 stbtt_uint16 lookupCount;
2500 stbtt_uint8 *data;
2501 stbtt_int32 i;
2502
2503 if (!info->gpos) return 0;
2504
2505 data = info->data + info->gpos;
2506
2507 if (ttUSHORT(data+0) != 1) return 0; // Major version 1
2508 if (ttUSHORT(data+2) != 0) return 0; // Minor version 0
2509
2510 lookupListOffset = ttUSHORT(data+8);
2511 lookupList = data + lookupListOffset;
2512 lookupCount = ttUSHORT(lookupList);
2513
2514 for (i=0; i<lookupCount; ++i) {
2515 stbtt_uint16 lookupOffset = ttUSHORT(lookupList + 2 + 2 * i);
2516 stbtt_uint8 *lookupTable = lookupList + lookupOffset;
2517
2518 stbtt_uint16 lookupType = ttUSHORT(lookupTable);
2519 stbtt_uint16 subTableCount = ttUSHORT(lookupTable + 4);
2520 stbtt_uint8 *subTableOffsets = lookupTable + 6;
2521 switch(lookupType) {
2522 case 2: { // Pair Adjustment Positioning Subtable
2523 stbtt_int32 sti;
2524 for (sti=0; sti<subTableCount; sti++) {
2525 stbtt_uint16 subtableOffset = ttUSHORT(subTableOffsets + 2 * sti);
2526 stbtt_uint8 *table = lookupTable + subtableOffset;
2527 stbtt_uint16 posFormat = ttUSHORT(table);
2528 stbtt_uint16 coverageOffset = ttUSHORT(table + 2);
2529 stbtt_int32 coverageIndex = stbtt__GetCoverageIndex(table + coverageOffset, glyph1);
2530 if (coverageIndex == -1) continue;
2531
2532 switch (posFormat) {
2533 case 1: {
2534 stbtt_int32 l, r, m;
2535 int straw, needle;
2536 stbtt_uint16 valueFormat1 = ttUSHORT(table + 4);
2537 stbtt_uint16 valueFormat2 = ttUSHORT(table + 6);
2538 stbtt_int32 valueRecordPairSizeInBytes = 2;
2539 stbtt_uint16 pairSetCount = ttUSHORT(table + 8);
2540 stbtt_uint16 pairPosOffset = ttUSHORT(table + 10 + 2 * coverageIndex);
2541 stbtt_uint8 *pairValueTable = table + pairPosOffset;
2542 stbtt_uint16 pairValueCount = ttUSHORT(pairValueTable);
2543 stbtt_uint8 *pairValueArray = pairValueTable + 2;
2544 // TODO: Support more formats.
2545 STBTT_GPOS_TODO_assert(valueFormat1 == 4);
2546 if (valueFormat1 != 4) return 0;
2547 STBTT_GPOS_TODO_assert(valueFormat2 == 0);
2548 if (valueFormat2 != 0) return 0;
2549
2550 STBTT_assert(coverageIndex < pairSetCount);
2551 STBTT__NOTUSED(pairSetCount);
2552
2553 needle=glyph2;
2554 r=pairValueCount-1;
2555 l=0;
2556
2557 // Binary search.
2558 while (l <= r) {
2559 stbtt_uint16 secondGlyph;
2560 stbtt_uint8 *pairValue;
2561 m = (l + r) >> 1;
2562 pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m;
2563 secondGlyph = ttUSHORT(pairValue);
2564 straw = secondGlyph;
2565 if (needle < straw)
2566 r = m - 1;
2567 else if (needle > straw)
2568 l = m + 1;
2569 else {
2570 stbtt_int16 xAdvance = ttSHORT(pairValue + 2);
2571 return xAdvance;
2572 }
2573 }
2574 } break;
2575
2576 case 2: {
2577 stbtt_uint16 valueFormat1 = ttUSHORT(table + 4);
2578 stbtt_uint16 valueFormat2 = ttUSHORT(table + 6);
2579
2580 stbtt_uint16 classDef1Offset = ttUSHORT(table + 8);
2581 stbtt_uint16 classDef2Offset = ttUSHORT(table + 10);
2582 int glyph1class = stbtt__GetGlyphClass(table + classDef1Offset, glyph1);
2583 int glyph2class = stbtt__GetGlyphClass(table + classDef2Offset, glyph2);
2584
2585 stbtt_uint16 class1Count = ttUSHORT(table + 12);
2586 stbtt_uint16 class2Count = ttUSHORT(table + 14);
2587 STBTT_assert(glyph1class < class1Count);
2588 STBTT_assert(glyph2class < class2Count);
2589
2590 // TODO: Support more formats.
2591 STBTT_GPOS_TODO_assert(valueFormat1 == 4);
2592 if (valueFormat1 != 4) return 0;
2593 STBTT_GPOS_TODO_assert(valueFormat2 == 0);
2594 if (valueFormat2 != 0) return 0;
2595
2596 if (glyph1class >= 0 && glyph1class < class1Count && glyph2class >= 0 && glyph2class < class2Count) {
2597 stbtt_uint8 *class1Records = table + 16;
2598 stbtt_uint8 *class2Records = class1Records + 2 * (glyph1class * class2Count);
2599 stbtt_int16 xAdvance = ttSHORT(class2Records + 2 * glyph2class);
2600 return xAdvance;
2601 }
2602 } break;
2603
2604 default: {
2605 // There are no other cases.
2606 STBTT_assert(0);
2607 break;
2608 };
2609 }
2610 }
2611 break;
2612 };
2613
2614 default:
2615 // TODO: Implement other stuff.
2616 break;
2617 }
2618 }
2619
2620 return 0;
2621}
2622
2623STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int g1, int g2)
2624{
2625 int xAdvance = 0;
2626
2627 if (info->gpos)
2628 xAdvance += stbtt__GetGlyphGPOSInfoAdvance(info, g1, g2);
2629 else if (info->kern)
2630 xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2);
2631
2632 return xAdvance;
2633}
2634
2635STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2)
2636{
2637 if (!info->kern && !info->gpos) // if no kerning table, don't waste time looking up both codepoint->glyphs
2638 return 0;
2639 return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2));
2640}
2641
2642STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing)
2643{
2644 stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing);
2645}
2646
2647STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap)
2648{
2649 if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4);
2650 if (descent) *descent = ttSHORT(info->data+info->hhea + 6);
2651 if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8);
2652}
2653
2654STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap)
2655{
2656 int tab = stbtt__find_table(info->data, info->fontstart, "OS/2");
2657 if (!tab)
2658 return 0;
2659 if (typoAscent ) *typoAscent = ttSHORT(info->data+tab + 68);
2660 if (typoDescent) *typoDescent = ttSHORT(info->data+tab + 70);
2661 if (typoLineGap) *typoLineGap = ttSHORT(info->data+tab + 72);
2662 return 1;
2663}
2664
2665STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1)
2666{
2667 *x0 = ttSHORT(info->data + info->head + 36);
2668 *y0 = ttSHORT(info->data + info->head + 38);
2669 *x1 = ttSHORT(info->data + info->head + 40);
2670 *y1 = ttSHORT(info->data + info->head + 42);
2671}
2672
2673STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height)
2674{
2675 int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6);
2676 return (float) height / fheight;
2677}
2678
2679STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels)
2680{
2681 int unitsPerEm = ttUSHORT(info->data + info->head + 18);
2682 return pixels / unitsPerEm;
2683}
2684
2685STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v)
2686{
2687 STBTT_free(v, info->userdata);
2688}
2689
2690STBTT_DEF stbtt_uint8 *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl)
2691{
2692 int i;
2693 stbtt_uint8 *data = info->data;
2694 stbtt_uint8 *svg_doc_list = data + stbtt__get_svg((stbtt_fontinfo *) info);
2695
2696 int numEntries = ttUSHORT(svg_doc_list);
2697 stbtt_uint8 *svg_docs = svg_doc_list + 2;
2698
2699 for(i=0; i<numEntries; i++) {
2700 stbtt_uint8 *svg_doc = svg_docs + (12 * i);
2701 if ((gl >= ttUSHORT(svg_doc)) && (gl <= ttUSHORT(svg_doc + 2)))
2702 return svg_doc;
2703 }
2704 return 0;
2705}
2706
2707STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg)
2708{
2709 stbtt_uint8 *data = info->data;
2710 stbtt_uint8 *svg_doc;
2711
2712 if (info->svg == 0)
2713 return 0;
2714
2715 svg_doc = stbtt_FindSVGDoc(info, gl);
2716 if (svg_doc != NULL) {
2717 *svg = (char *) data + info->svg + ttULONG(svg_doc + 4);
2718 return ttULONG(svg_doc + 8);
2719 } else {
2720 return 0;
2721 }
2722}
2723
2724STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg)
2725{
2726 return stbtt_GetGlyphSVG(info, stbtt_FindGlyphIndex(info, unicode_codepoint), svg);
2727}
2728
2729//////////////////////////////////////////////////////////////////////////////
2730//
2731// antialiasing software rasterizer
2732//
2733
2734STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1)
2735{
2736 int x0=0,y0=0,x1,y1; // =0 suppresses compiler warning
2737 if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) {
2738 // e.g. space character
2739 if (ix0) *ix0 = 0;
2740 if (iy0) *iy0 = 0;
2741 if (ix1) *ix1 = 0;
2742 if (iy1) *iy1 = 0;
2743 } else {
2744 // move to integral bboxes (treating pixels as little squares, what pixels get touched)?
2745 if (ix0) *ix0 = STBTT_ifloor( x0 * scale_x + shift_x);
2746 if (iy0) *iy0 = STBTT_ifloor(-y1 * scale_y + shift_y);
2747 if (ix1) *ix1 = STBTT_iceil ( x1 * scale_x + shift_x);
2748 if (iy1) *iy1 = STBTT_iceil (-y0 * scale_y + shift_y);
2749 }
2750}
2751
2752STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1)
2753{
2754 stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1);
2755}
2756
2757STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1)
2758{
2759 stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1);
2760}
2761
2762STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1)
2763{
2764 stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1);
2765}
2766
2767//////////////////////////////////////////////////////////////////////////////
2768//
2769// Rasterizer
2770
2771typedef struct stbtt__hheap_chunk
2772{
2773 struct stbtt__hheap_chunk *next;
2774} stbtt__hheap_chunk;
2775
2776typedef struct stbtt__hheap
2777{
2778 struct stbtt__hheap_chunk *head;
2779 void *first_free;
2780 int num_remaining_in_head_chunk;
2781} stbtt__hheap;
2782
2783static void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata)
2784{
2785 if (hh->first_free) {
2786 void *p = hh->first_free;
2787 hh->first_free = * (void **) p;
2788 return p;
2789 } else {
2790 if (hh->num_remaining_in_head_chunk == 0) {
2791 int count = (size < 32 ? 2000 : size < 128 ? 800 : 100);
2792 stbtt__hheap_chunk *c = (stbtt__hheap_chunk *) STBTT_malloc(sizeof(stbtt__hheap_chunk) + size * count, userdata);
2793 if (c == NULL)
2794 return NULL;
2795 c->next = hh->head;
2796 hh->head = c;
2797 hh->num_remaining_in_head_chunk = count;
2798 }
2799 --hh->num_remaining_in_head_chunk;
2800 return (char *) (hh->head) + sizeof(stbtt__hheap_chunk) + size * hh->num_remaining_in_head_chunk;
2801 }
2802}
2803
2804static void stbtt__hheap_free(stbtt__hheap *hh, void *p)
2805{
2806 *(void **) p = hh->first_free;
2807 hh->first_free = p;
2808}
2809
2810static void stbtt__hheap_cleanup(stbtt__hheap *hh, void *userdata)
2811{
2812 stbtt__hheap_chunk *c = hh->head;
2813 while (c) {
2814 stbtt__hheap_chunk *n = c->next;
2815 STBTT_free(c, userdata);
2816 c = n;
2817 }
2818}
2819
2820typedef struct stbtt__edge {
2821 float x0,y0, x1,y1;
2822 int invert;
2823} stbtt__edge;
2824
2825
2826typedef struct stbtt__active_edge
2827{
2828 struct stbtt__active_edge *next;
2829 #if STBTT_RASTERIZER_VERSION==1
2830 int x,dx;
2831 float ey;
2832 int direction;
2833 #elif STBTT_RASTERIZER_VERSION==2
2834 float fx,fdx,fdy;
2835 float direction;
2836 float sy;
2837 float ey;
2838 #else
2839 #error "Unrecognized value of STBTT_RASTERIZER_VERSION"
2840 #endif
2841} stbtt__active_edge;
2842
2843#if STBTT_RASTERIZER_VERSION == 1
2844#define STBTT_FIXSHIFT 10
2845#define STBTT_FIX (1 << STBTT_FIXSHIFT)
2846#define STBTT_FIXMASK (STBTT_FIX-1)
2847
2848static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata)
2849{
2850 stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata);
2851 float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0);
2852 STBTT_assert(z != NULL);
2853 if (!z) return z;
2854
2855 // round dx down to avoid overshooting
2856 if (dxdy < 0)
2857 z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy);
2858 else
2859 z->dx = STBTT_ifloor(STBTT_FIX * dxdy);
2860
2861 z->x = STBTT_ifloor(STBTT_FIX * e->x0 + z->dx * (start_point - e->y0)); // use z->dx so when we offset later it's by the same amount
2862 z->x -= off_x * STBTT_FIX;
2863
2864 z->ey = e->y1;
2865 z->next = 0;
2866 z->direction = e->invert ? 1 : -1;
2867 return z;
2868}
2869#elif STBTT_RASTERIZER_VERSION == 2
2870static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata)
2871{
2872 stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata);
2873 float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0);
2874 STBTT_assert(z != NULL);
2875 //STBTT_assert(e->y0 <= start_point);
2876 if (!z) return z;
2877 z->fdx = dxdy;
2878 z->fdy = dxdy != 0.0f ? (1.0f/dxdy) : 0.0f;
2879 z->fx = e->x0 + dxdy * (start_point - e->y0);
2880 z->fx -= off_x;
2881 z->direction = e->invert ? 1.0f : -1.0f;
2882 z->sy = e->y0;
2883 z->ey = e->y1;
2884 z->next = 0;
2885 return z;
2886}
2887#else
2888#error "Unrecognized value of STBTT_RASTERIZER_VERSION"
2889#endif
2890
2891#if STBTT_RASTERIZER_VERSION == 1
2892// note: this routine clips fills that extend off the edges... ideally this
2893// wouldn't happen, but it could happen if the truetype glyph bounding boxes
2894// are wrong, or if the user supplies a too-small bitmap
2895static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight)
2896{
2897 // non-zero winding fill
2898 int x0=0, w=0;
2899
2900 while (e) {
2901 if (w == 0) {
2902 // if we're currently at zero, we need to record the edge start point
2903 x0 = e->x; w += e->direction;
2904 } else {
2905 int x1 = e->x; w += e->direction;
2906 // if we went to zero, we need to draw
2907 if (w == 0) {
2908 int i = x0 >> STBTT_FIXSHIFT;
2909 int j = x1 >> STBTT_FIXSHIFT;
2910
2911 if (i < len && j >= 0) {
2912 if (i == j) {
2913 // x0,x1 are the same pixel, so compute combined coverage
2914 scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> STBTT_FIXSHIFT);
2915 } else {
2916 if (i >= 0) // add antialiasing for x0
2917 scanline[i] = scanline[i] + (stbtt_uint8) (((STBTT_FIX - (x0 & STBTT_FIXMASK)) * max_weight) >> STBTT_FIXSHIFT);
2918 else
2919 i = -1; // clip
2920
2921 if (j < len) // add antialiasing for x1
2922 scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & STBTT_FIXMASK) * max_weight) >> STBTT_FIXSHIFT);
2923 else
2924 j = len; // clip
2925
2926 for (++i; i < j; ++i) // fill pixels between x0 and x1
2927 scanline[i] = scanline[i] + (stbtt_uint8) max_weight;
2928 }
2929 }
2930 }
2931 }
2932
2933 e = e->next;
2934 }
2935}
2936
2937static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata)
2938{
2939 stbtt__hheap hh = { 0, 0, 0 };
2940 stbtt__active_edge *active = NULL;
2941 int y,j=0;
2942 int max_weight = (255 / vsubsample); // weight per vertical scanline
2943 int s; // vertical subsample index
2944 unsigned char scanline_data[512], *scanline;
2945
2946 if (result->w > 512)
2947 scanline = (unsigned char *) STBTT_malloc(result->w, userdata);
2948 else
2949 scanline = scanline_data;
2950
2951 y = off_y * vsubsample;
2952 e[n].y0 = (off_y + result->h) * (float) vsubsample + 1;
2953
2954 while (j < result->h) {
2955 STBTT_memset(scanline, 0, result->w);
2956 for (s=0; s < vsubsample; ++s) {
2957 // find center of pixel for this scanline
2958 float scan_y = y + 0.5f;
2959 stbtt__active_edge **step = &active;
2960
2961 // update all active edges;
2962 // remove all active edges that terminate before the center of this scanline
2963 while (*step) {
2964 stbtt__active_edge * z = *step;
2965 if (z->ey <= scan_y) {
2966 *step = z->next; // delete from list
2967 STBTT_assert(z->direction);
2968 z->direction = 0;
2969 stbtt__hheap_free(&hh, z);
2970 } else {
2971 z->x += z->dx; // advance to position for current scanline
2972 step = &((*step)->next); // advance through list
2973 }
2974 }
2975
2976 // resort the list if needed
2977 for(;;) {
2978 int changed=0;
2979 step = &active;
2980 while (*step && (*step)->next) {
2981 if ((*step)->x > (*step)->next->x) {
2982 stbtt__active_edge *t = *step;
2983 stbtt__active_edge *q = t->next;
2984
2985 t->next = q->next;
2986 q->next = t;
2987 *step = q;
2988 changed = 1;
2989 }
2990 step = &(*step)->next;
2991 }
2992 if (!changed) break;
2993 }
2994
2995 // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline
2996 while (e->y0 <= scan_y) {
2997 if (e->y1 > scan_y) {
2998 stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y, userdata);
2999 if (z != NULL) {
3000 // find insertion point
3001 if (active == NULL)
3002 active = z;
3003 else if (z->x < active->x) {
3004 // insert at front
3005 z->next = active;
3006 active = z;
3007 } else {
3008 // find thing to insert AFTER
3009 stbtt__active_edge *p = active;
3010 while (p->next && p->next->x < z->x)
3011 p = p->next;
3012 // at this point, p->next->x is NOT < z->x
3013 z->next = p->next;
3014 p->next = z;
3015 }
3016 }
3017 }
3018 ++e;
3019 }
3020
3021 // now process all active edges in XOR fashion
3022 if (active)
3023 stbtt__fill_active_edges(scanline, result->w, active, max_weight);
3024
3025 ++y;
3026 }
3027 STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w);
3028 ++j;
3029 }
3030
3031 stbtt__hheap_cleanup(&hh, userdata);
3032
3033 if (scanline != scanline_data)
3034 STBTT_free(scanline, userdata);
3035}
3036
3037#elif STBTT_RASTERIZER_VERSION == 2
3038
3039// the edge passed in here does not cross the vertical line at x or the vertical line at x+1
3040// (i.e. it has already been clipped to those)
3041static void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edge *e, float x0, float y0, float x1, float y1)
3042{
3043 if (y0 == y1) return;
3044 STBTT_assert(y0 < y1);
3045 STBTT_assert(e->sy <= e->ey);
3046 if (y0 > e->ey) return;
3047 if (y1 < e->sy) return;
3048 if (y0 < e->sy) {
3049 x0 += (x1-x0) * (e->sy - y0) / (y1-y0);
3050 y0 = e->sy;
3051 }
3052 if (y1 > e->ey) {
3053 x1 += (x1-x0) * (e->ey - y1) / (y1-y0);
3054 y1 = e->ey;
3055 }
3056
3057 if (x0 == x)
3058 STBTT_assert(x1 <= x+1);
3059 else if (x0 == x+1)
3060 STBTT_assert(x1 >= x);
3061 else if (x0 <= x)
3062 STBTT_assert(x1 <= x);
3063 else if (x0 >= x+1)
3064 STBTT_assert(x1 >= x+1);
3065 else
3066 STBTT_assert(x1 >= x && x1 <= x+1);
3067
3068 if (x0 <= x && x1 <= x)
3069 scanline[x] += e->direction * (y1-y0);
3070 else if (x0 >= x+1 && x1 >= x+1)
3071 ;
3072 else {
3073 STBTT_assert(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1);
3074 scanline[x] += e->direction * (y1-y0) * (1-((x0-x)+(x1-x))/2); // coverage = 1 - average x position
3075 }
3076}
3077
3078static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top)
3079{
3080 float y_bottom = y_top+1;
3081
3082 while (e) {
3083 // brute force every pixel
3084
3085 // compute intersection points with top & bottom
3086 STBTT_assert(e->ey >= y_top);
3087
3088 if (e->fdx == 0) {
3089 float x0 = e->fx;
3090 if (x0 < len) {
3091 if (x0 >= 0) {
3092 stbtt__handle_clipped_edge(scanline,(int) x0,e, x0,y_top, x0,y_bottom);
3093 stbtt__handle_clipped_edge(scanline_fill-1,(int) x0+1,e, x0,y_top, x0,y_bottom);
3094 } else {
3095 stbtt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom);
3096 }
3097 }
3098 } else {
3099 float x0 = e->fx;
3100 float dx = e->fdx;
3101 float xb = x0 + dx;
3102 float x_top, x_bottom;
3103 float sy0,sy1;
3104 float dy = e->fdy;
3105 STBTT_assert(e->sy <= y_bottom && e->ey >= y_top);
3106
3107 // compute endpoints of line segment clipped to this scanline (if the
3108 // line segment starts on this scanline. x0 is the intersection of the
3109 // line with y_top, but that may be off the line segment.
3110 if (e->sy > y_top) {
3111 x_top = x0 + dx * (e->sy - y_top);
3112 sy0 = e->sy;
3113 } else {
3114 x_top = x0;
3115 sy0 = y_top;
3116 }
3117 if (e->ey < y_bottom) {
3118 x_bottom = x0 + dx * (e->ey - y_top);
3119 sy1 = e->ey;
3120 } else {
3121 x_bottom = xb;
3122 sy1 = y_bottom;
3123 }
3124
3125 if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) {
3126 // from here on, we don't have to range check x values
3127
3128 if ((int) x_top == (int) x_bottom) {
3129 float height;
3130 // simple case, only spans one pixel
3131 int x = (int) x_top;
3132 height = sy1 - sy0;
3133 STBTT_assert(x >= 0 && x < len);
3134 scanline[x] += e->direction * (1-((x_top - x) + (x_bottom-x))/2) * height;
3135 scanline_fill[x] += e->direction * height; // everything right of this pixel is filled
3136 } else {
3137 int x,x1,x2;
3138 float y_crossing, step, sign, area;
3139 // covers 2+ pixels
3140 if (x_top > x_bottom) {
3141 // flip scanline vertically; signed area is the same
3142 float t;
3143 sy0 = y_bottom - (sy0 - y_top);
3144 sy1 = y_bottom - (sy1 - y_top);
3145 t = sy0, sy0 = sy1, sy1 = t;
3146 t = x_bottom, x_bottom = x_top, x_top = t;
3147 dx = -dx;
3148 dy = -dy;
3149 t = x0, x0 = xb, xb = t;
3150 }
3151
3152 x1 = (int) x_top;
3153 x2 = (int) x_bottom;
3154 // compute intersection with y axis at x1+1
3155 y_crossing = (x1+1 - x0) * dy + y_top;
3156
3157 sign = e->direction;
3158 // area of the rectangle covered from y0..y_crossing
3159 area = sign * (y_crossing-sy0);
3160 // area of the triangle (x_top,y0), (x+1,y0), (x+1,y_crossing)
3161 scanline[x1] += area * (1-((x_top - x1)+(x1+1-x1))/2);
3162
3163 step = sign * dy;
3164 for (x = x1+1; x < x2; ++x) {
3165 scanline[x] += area + step/2;
3166 area += step;
3167 }
3168 y_crossing += dy * (x2 - (x1+1));
3169
3170 STBTT_assert(STBTT_fabs(area) <= 1.01f);
3171
3172 scanline[x2] += area + sign * (1-((x2-x2)+(x_bottom-x2))/2) * (sy1-y_crossing);
3173
3174 scanline_fill[x2] += sign * (sy1-sy0);
3175 }
3176 } else {
3177 // if edge goes outside of box we're drawing, we require
3178 // clipping logic. since this does not match the intended use
3179 // of this library, we use a different, very slow brute
3180 // force implementation
3181 int x;
3182 for (x=0; x < len; ++x) {
3183 // cases:
3184 //
3185 // there can be up to two intersections with the pixel. any intersection
3186 // with left or right edges can be handled by splitting into two (or three)
3187 // regions. intersections with top & bottom do not necessitate case-wise logic.
3188 //
3189 // the old way of doing this found the intersections with the left & right edges,
3190 // then used some simple logic to produce up to three segments in sorted order
3191 // from top-to-bottom. however, this had a problem: if an x edge was epsilon
3192 // across the x border, then the corresponding y position might not be distinct
3193 // from the other y segment, and it might ignored as an empty segment. to avoid
3194 // that, we need to explicitly produce segments based on x positions.
3195
3196 // rename variables to clearly-defined pairs
3197 float y0 = y_top;
3198 float x1 = (float) (x);
3199 float x2 = (float) (x+1);
3200 float x3 = xb;
3201 float y3 = y_bottom;
3202
3203 // x = e->x + e->dx * (y-y_top)
3204 // (y-y_top) = (x - e->x) / e->dx
3205 // y = (x - e->x) / e->dx + y_top
3206 float y1 = (x - x0) / dx + y_top;
3207 float y2 = (x+1 - x0) / dx + y_top;
3208
3209 if (x0 < x1 && x3 > x2) { // three segments descending down-right
3210 stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1);
3211 stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x2,y2);
3212 stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3);
3213 } else if (x3 < x1 && x0 > x2) { // three segments descending down-left
3214 stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2);
3215 stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x1,y1);
3216 stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3);
3217 } else if (x0 < x1 && x3 > x1) { // two segments across x, down-right
3218 stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1);
3219 stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3);
3220 } else if (x3 < x1 && x0 > x1) { // two segments across x, down-left
3221 stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1);
3222 stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3);
3223 } else if (x0 < x2 && x3 > x2) { // two segments across x+1, down-right
3224 stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2);
3225 stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3);
3226 } else if (x3 < x2 && x0 > x2) { // two segments across x+1, down-left
3227 stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2);
3228 stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3);
3229 } else { // one segment
3230 stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x3,y3);
3231 }
3232 }
3233 }
3234 }
3235 e = e->next;
3236 }
3237}
3238
3239// directly AA rasterize edges w/o supersampling
3240static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata)
3241{
3242 stbtt__hheap hh = { 0, 0, 0 };
3243 stbtt__active_edge *active = NULL;
3244 int y,j=0, i;
3245 float scanline_data[129], *scanline, *scanline2;
3246
3247 STBTT__NOTUSED(vsubsample);
3248
3249 if (result->w > 64)
3250 scanline = (float *) STBTT_malloc((result->w*2+1) * sizeof(float), userdata);
3251 else
3252 scanline = scanline_data;
3253
3254 scanline2 = scanline + result->w;
3255
3256 y = off_y;
3257 e[n].y0 = (float) (off_y + result->h) + 1;
3258
3259 while (j < result->h) {
3260 // find center of pixel for this scanline
3261 float scan_y_top = y + 0.0f;
3262 float scan_y_bottom = y + 1.0f;
3263 stbtt__active_edge **step = &active;
3264
3265 STBTT_memset(scanline , 0, result->w*sizeof(scanline[0]));
3266 STBTT_memset(scanline2, 0, (result->w+1)*sizeof(scanline[0]));
3267
3268 // update all active edges;
3269 // remove all active edges that terminate before the top of this scanline
3270 while (*step) {
3271 stbtt__active_edge * z = *step;
3272 if (z->ey <= scan_y_top) {
3273 *step = z->next; // delete from list
3274 STBTT_assert(z->direction);
3275 z->direction = 0;
3276 stbtt__hheap_free(&hh, z);
3277 } else {
3278 step = &((*step)->next); // advance through list
3279 }
3280 }
3281
3282 // insert all edges that start before the bottom of this scanline
3283 while (e->y0 <= scan_y_bottom) {
3284 if (e->y0 != e->y1) {
3285 stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata);
3286 if (z != NULL) {
3287 if (j == 0 && off_y != 0) {
3288 if (z->ey < scan_y_top) {
3289 // this can happen due to subpixel positioning and some kind of fp rounding error i think
3290 z->ey = scan_y_top;
3291 }
3292 }
3293 STBTT_assert(z->ey >= scan_y_top); // if we get really unlucky a tiny bit of an edge can be out of bounds
3294 // insert at front
3295 z->next = active;
3296 active = z;
3297 }
3298 }
3299 ++e;
3300 }
3301
3302 // now process all active edges
3303 if (active)
3304 stbtt__fill_active_edges_new(scanline, scanline2+1, result->w, active, scan_y_top);
3305
3306 {
3307 float sum = 0;
3308 for (i=0; i < result->w; ++i) {
3309 float k;
3310 int m;
3311 sum += scanline2[i];
3312 k = scanline[i] + sum;
3313 k = (float) STBTT_fabs(k)*255 + 0.5f;
3314 m = (int) k;
3315 if (m > 255) m = 255;
3316 result->pixels[j*result->stride + i] = (unsigned char) m;
3317 }
3318 }
3319 // advance all the edges
3320 step = &active;
3321 while (*step) {
3322 stbtt__active_edge *z = *step;
3323 z->fx += z->fdx; // advance to position for current scanline
3324 step = &((*step)->next); // advance through list
3325 }
3326
3327 ++y;
3328 ++j;
3329 }
3330
3331 stbtt__hheap_cleanup(&hh, userdata);
3332
3333 if (scanline != scanline_data)
3334 STBTT_free(scanline, userdata);
3335}
3336#else
3337#error "Unrecognized value of STBTT_RASTERIZER_VERSION"
3338#endif
3339
3340#define STBTT__COMPARE(a,b) ((a)->y0 < (b)->y0)
3341
3342static void stbtt__sort_edges_ins_sort(stbtt__edge *p, int n)
3343{
3344 int i,j;
3345 for (i=1; i < n; ++i) {
3346 stbtt__edge t = p[i], *a = &t;
3347 j = i;
3348 while (j > 0) {
3349 stbtt__edge *b = &p[j-1];
3350 int c = STBTT__COMPARE(a,b);
3351 if (!c) break;
3352 p[j] = p[j-1];
3353 --j;
3354 }
3355 if (i != j)
3356 p[j] = t;
3357 }
3358}
3359
3360static void stbtt__sort_edges_quicksort(stbtt__edge *p, int n)
3361{
3362 /* threshold for transitioning to insertion sort */
3363 while (n > 12) {
3364 stbtt__edge t;
3365 int c01,c12,c,m,i,j;
3366
3367 /* compute median of three */
3368 m = n >> 1;
3369 c01 = STBTT__COMPARE(&p[0],&p[m]);
3370 c12 = STBTT__COMPARE(&p[m],&p[n-1]);
3371 /* if 0 >= mid >= end, or 0 < mid < end, then use mid */
3372 if (c01 != c12) {
3373 /* otherwise, we'll need to swap something else to middle */
3374 int z;
3375 c = STBTT__COMPARE(&p[0],&p[n-1]);
3376 /* 0>mid && mid<n: 0>n => n; 0<n => 0 */
3377 /* 0<mid && mid>n: 0>n => 0; 0<n => n */
3378 z = (c == c12) ? 0 : n-1;
3379 t = p[z];
3380 p[z] = p[m];
3381 p[m] = t;
3382 }
3383 /* now p[m] is the median-of-three */
3384 /* swap it to the beginning so it won't move around */
3385 t = p[0];
3386 p[0] = p[m];
3387 p[m] = t;
3388
3389 /* partition loop */
3390 i=1;
3391 j=n-1;
3392 for(;;) {
3393 /* handling of equality is crucial here */
3394 /* for sentinels & efficiency with duplicates */
3395 for (;;++i) {
3396 if (!STBTT__COMPARE(&p[i], &p[0])) break;
3397 }
3398 for (;;--j) {
3399 if (!STBTT__COMPARE(&p[0], &p[j])) break;
3400 }
3401 /* make sure we haven't crossed */
3402 if (i >= j) break;
3403 t = p[i];
3404 p[i] = p[j];
3405 p[j] = t;
3406
3407 ++i;
3408 --j;
3409 }
3410 /* recurse on smaller side, iterate on larger */
3411 if (j < (n-i)) {
3412 stbtt__sort_edges_quicksort(p,j);
3413 p = p+i;
3414 n = n-i;
3415 } else {
3416 stbtt__sort_edges_quicksort(p+i, n-i);
3417 n = j;
3418 }
3419 }
3420}
3421
3422static void stbtt__sort_edges(stbtt__edge *p, int n)
3423{
3424 stbtt__sort_edges_quicksort(p, n);
3425 stbtt__sort_edges_ins_sort(p, n);
3426}
3427
3428typedef struct
3429{
3430 float x,y;
3431} stbtt__point;
3432
3433static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, void *userdata)
3434{
3435 float y_scale_inv = invert ? -scale_y : scale_y;
3436 stbtt__edge *e;
3437 int n,i,j,k,m;
3438#if STBTT_RASTERIZER_VERSION == 1
3439 int vsubsample = result->h < 8 ? 15 : 5;
3440#elif STBTT_RASTERIZER_VERSION == 2
3441 int vsubsample = 1;
3442#else
3443 #error "Unrecognized value of STBTT_RASTERIZER_VERSION"
3444#endif
3445 // vsubsample should divide 255 evenly; otherwise we won't reach full opacity
3446
3447 // now we have to blow out the windings into explicit edge lists
3448 n = 0;
3449 for (i=0; i < windings; ++i)
3450 n += wcount[i];
3451
3452 e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel
3453 if (e == 0) return;
3454 n = 0;
3455
3456 m=0;
3457 for (i=0; i < windings; ++i) {
3458 stbtt__point *p = pts + m;
3459 m += wcount[i];
3460 j = wcount[i]-1;
3461 for (k=0; k < wcount[i]; j=k++) {
3462 int a=k,b=j;
3463 // skip the edge if horizontal
3464 if (p[j].y == p[k].y)
3465 continue;
3466 // add edge from j to k to the list
3467 e[n].invert = 0;
3468 if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) {
3469 e[n].invert = 1;
3470 a=j,b=k;
3471 }
3472 e[n].x0 = p[a].x * scale_x + shift_x;
3473 e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample;
3474 e[n].x1 = p[b].x * scale_x + shift_x;
3475 e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample;
3476 ++n;
3477 }
3478 }
3479
3480 // now sort the edges by their highest point (should snap to integer, and then by x)
3481 //STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare);
3482 stbtt__sort_edges(e, n);
3483
3484 // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule
3485 stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata);
3486
3487 STBTT_free(e, userdata);
3488}
3489
3490static void stbtt__add_point(stbtt__point *points, int n, float x, float y)
3491{
3492 if (!points) return; // during first pass, it's unallocated
3493 points[n].x = x;
3494 points[n].y = y;
3495}
3496
3497// tessellate until threshold p is happy... @TODO warped to compensate for non-linear stretching
3498static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n)
3499{
3500 // midpoint
3501 float mx = (x0 + 2*x1 + x2)/4;
3502 float my = (y0 + 2*y1 + y2)/4;
3503 // versus directly drawn line
3504 float dx = (x0+x2)/2 - mx;
3505 float dy = (y0+y2)/2 - my;
3506 if (n > 16) // 65536 segments on one curve better be enough!
3507 return 1;
3508 if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA
3509 stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1);
3510 stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1);
3511 } else {
3512 stbtt__add_point(points, *num_points,x2,y2);
3513 *num_points = *num_points+1;
3514 }
3515 return 1;
3516}
3517
3518static void stbtt__tesselate_cubic(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float objspace_flatness_squared, int n)
3519{
3520 // @TODO this "flatness" calculation is just made-up nonsense that seems to work well enough
3521 float dx0 = x1-x0;
3522 float dy0 = y1-y0;
3523 float dx1 = x2-x1;
3524 float dy1 = y2-y1;
3525 float dx2 = x3-x2;
3526 float dy2 = y3-y2;
3527 float dx = x3-x0;
3528 float dy = y3-y0;
3529 float longlen = (float) (STBTT_sqrt(dx0*dx0+dy0*dy0)+STBTT_sqrt(dx1*dx1+dy1*dy1)+STBTT_sqrt(dx2*dx2+dy2*dy2));
3530 float shortlen = (float) STBTT_sqrt(dx*dx+dy*dy);
3531 float flatness_squared = longlen*longlen-shortlen*shortlen;
3532
3533 if (n > 16) // 65536 segments on one curve better be enough!
3534 return;
3535
3536 if (flatness_squared > objspace_flatness_squared) {
3537 float x01 = (x0+x1)/2;
3538 float y01 = (y0+y1)/2;
3539 float x12 = (x1+x2)/2;
3540 float y12 = (y1+y2)/2;
3541 float x23 = (x2+x3)/2;
3542 float y23 = (y2+y3)/2;
3543
3544 float xa = (x01+x12)/2;
3545 float ya = (y01+y12)/2;
3546 float xb = (x12+x23)/2;
3547 float yb = (y12+y23)/2;
3548
3549 float mx = (xa+xb)/2;
3550 float my = (ya+yb)/2;
3551
3552 stbtt__tesselate_cubic(points, num_points, x0,y0, x01,y01, xa,ya, mx,my, objspace_flatness_squared,n+1);
3553 stbtt__tesselate_cubic(points, num_points, mx,my, xb,yb, x23,y23, x3,y3, objspace_flatness_squared,n+1);
3554 } else {
3555 stbtt__add_point(points, *num_points,x3,y3);
3556 *num_points = *num_points+1;
3557 }
3558}
3559
3560// returns number of contours
3561static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata)
3562{
3563 stbtt__point *points=0;
3564 int num_points=0;
3565
3566 float objspace_flatness_squared = objspace_flatness * objspace_flatness;
3567 int i,n=0,start=0, pass;
3568
3569 // count how many "moves" there are to get the contour count
3570 for (i=0; i < num_verts; ++i)
3571 if (vertices[i].type == STBTT_vmove)
3572 ++n;
3573
3574 *num_contours = n;
3575 if (n == 0) return 0;
3576
3577 *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata);
3578
3579 if (*contour_lengths == 0) {
3580 *num_contours = 0;
3581 return 0;
3582 }
3583
3584 // make two passes through the points so we don't need to realloc
3585 for (pass=0; pass < 2; ++pass) {
3586 float x=0,y=0;
3587 if (pass == 1) {
3588 points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata);
3589 if (points == NULL) goto error;
3590 }
3591 num_points = 0;
3592 n= -1;
3593 for (i=0; i < num_verts; ++i) {
3594 switch (vertices[i].type) {
3595 case STBTT_vmove:
3596 // start the next contour
3597 if (n >= 0)
3598 (*contour_lengths)[n] = num_points - start;
3599 ++n;
3600 start = num_points;
3601
3602 x = vertices[i].x, y = vertices[i].y;
3603 stbtt__add_point(points, num_points++, x,y);
3604 break;
3605 case STBTT_vline:
3606 x = vertices[i].x, y = vertices[i].y;
3607 stbtt__add_point(points, num_points++, x, y);
3608 break;
3609 case STBTT_vcurve:
3610 stbtt__tesselate_curve(points, &num_points, x,y,
3611 vertices[i].cx, vertices[i].cy,
3612 vertices[i].x, vertices[i].y,
3613 objspace_flatness_squared, 0);
3614 x = vertices[i].x, y = vertices[i].y;
3615 break;
3616 case STBTT_vcubic:
3617 stbtt__tesselate_cubic(points, &num_points, x,y,
3618 vertices[i].cx, vertices[i].cy,
3619 vertices[i].cx1, vertices[i].cy1,
3620 vertices[i].x, vertices[i].y,
3621 objspace_flatness_squared, 0);
3622 x = vertices[i].x, y = vertices[i].y;
3623 break;
3624 }
3625 }
3626 (*contour_lengths)[n] = num_points - start;
3627 }
3628
3629 return points;
3630error:
3631 STBTT_free(points, userdata);
3632 STBTT_free(*contour_lengths, userdata);
3633 *contour_lengths = 0;
3634 *num_contours = 0;
3635 return NULL;
3636}
3637
3638STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata)
3639{
3640 float scale = scale_x > scale_y ? scale_y : scale_x;
3641 int winding_count = 0;
3642 int *winding_lengths = NULL;
3643 stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata);
3644 if (windings) {
3645 stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata);
3646 STBTT_free(winding_lengths, userdata);
3647 STBTT_free(windings, userdata);
3648 }
3649}
3650
3651STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata)
3652{
3653 STBTT_free(bitmap, userdata);
3654}
3655
3656STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff)
3657{
3658 int ix0,iy0,ix1,iy1;
3659 stbtt__bitmap gbm;
3660 stbtt_vertex *vertices;
3661 int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices);
3662
3663 if (scale_x == 0) scale_x = scale_y;
3664 if (scale_y == 0) {
3665 if (scale_x == 0) {
3666 STBTT_free(vertices, info->userdata);
3667 return NULL;
3668 }
3669 scale_y = scale_x;
3670 }
3671
3672 stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1);
3673
3674 // now we get the size
3675 gbm.w = (ix1 - ix0);
3676 gbm.h = (iy1 - iy0);
3677 gbm.pixels = NULL; // in case we error
3678
3679 if (width ) *width = gbm.w;
3680 if (height) *height = gbm.h;
3681 if (xoff ) *xoff = ix0;
3682 if (yoff ) *yoff = iy0;
3683
3684 if (gbm.w && gbm.h) {
3685 gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata);
3686 if (gbm.pixels) {
3687 gbm.stride = gbm.w;
3688
3689 stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info->userdata);
3690 }
3691 }
3692 STBTT_free(vertices, info->userdata);
3693 return gbm.pixels;
3694}
3695
3696STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff)
3697{
3698 return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff);
3699}
3700
3701STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph)
3702{
3703 int ix0,iy0;
3704 stbtt_vertex *vertices;
3705 int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices);
3706 stbtt__bitmap gbm;
3707
3708 stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0);
3709 gbm.pixels = output;
3710 gbm.w = out_w;
3711 gbm.h = out_h;
3712 gbm.stride = out_stride;
3713
3714 if (gbm.w && gbm.h)
3715 stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info->userdata);
3716
3717 STBTT_free(vertices, info->userdata);
3718}
3719
3720STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph)
3721{
3722 stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph);
3723}
3724
3725STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff)
3726{
3727 return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff);
3728}
3729
3730STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint)
3731{
3732 stbtt_MakeGlyphBitmapSubpixelPrefilter(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, oversample_x, oversample_y, sub_x, sub_y, stbtt_FindGlyphIndex(info,codepoint));
3733}
3734
3735STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint)
3736{
3737 stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint));
3738}
3739
3740STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff)
3741{
3742 return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff);
3743}
3744
3745STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint)
3746{
3747 stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint);
3748}
3749
3750//////////////////////////////////////////////////////////////////////////////
3751//
3752// bitmap baking
3753//
3754// This is SUPER-CRAPPY packing to keep source code small
3755
3756static int stbtt_BakeFontBitmap_internal(unsigned char *data, int offset, // font location (use offset=0 for plain .ttf)
3757 float pixel_height, // height of font in pixels
3758 unsigned char *pixels, int pw, int ph, // bitmap to be filled in
3759 int first_char, int num_chars, // characters to bake
3760 stbtt_bakedchar *chardata)
3761{
3762 float scale;
3763 int x,y,bottom_y, i;
3764 stbtt_fontinfo f;
3765 f.userdata = NULL;
3766 if (!stbtt_InitFont(&f, data, offset))
3767 return -1;
3768 STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels
3769 x=y=1;
3770 bottom_y = 1;
3771
3772 scale = stbtt_ScaleForPixelHeight(&f, pixel_height);
3773
3774 for (i=0; i < num_chars; ++i) {
3775 int advance, lsb, x0,y0,x1,y1,gw,gh;
3776 int g = stbtt_FindGlyphIndex(&f, first_char + i);
3777 stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb);
3778 stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1);
3779 gw = x1-x0;
3780 gh = y1-y0;
3781 if (x + gw + 1 >= pw)
3782 y = bottom_y, x = 1; // advance to next row
3783 if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row
3784 return -i;
3785 STBTT_assert(x+gw < pw);
3786 STBTT_assert(y+gh < ph);
3787 stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g);
3788 chardata[i].x0 = (stbtt_int16) x;
3789 chardata[i].y0 = (stbtt_int16) y;
3790 chardata[i].x1 = (stbtt_int16) (x + gw);
3791 chardata[i].y1 = (stbtt_int16) (y + gh);
3792 chardata[i].xadvance = scale * advance;
3793 chardata[i].xoff = (float) x0;
3794 chardata[i].yoff = (float) y0;
3795 x = x + gw + 1;
3796 if (y+gh+1 > bottom_y)
3797 bottom_y = y+gh+1;
3798 }
3799 return bottom_y;
3800}
3801
3802STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule)
3803{
3804 float d3d_bias = opengl_fillrule ? 0 : -0.5f;
3805 float ipw = 1.0f / pw, iph = 1.0f / ph;
3806 const stbtt_bakedchar *b = chardata + char_index;
3807 int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5f);
3808 int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5f);
3809
3810 q->x0 = round_x + d3d_bias;
3811 q->y0 = round_y + d3d_bias;
3812 q->x1 = round_x + b->x1 - b->x0 + d3d_bias;
3813 q->y1 = round_y + b->y1 - b->y0 + d3d_bias;
3814
3815 q->s0 = b->x0 * ipw;
3816 q->t0 = b->y0 * iph;
3817 q->s1 = b->x1 * ipw;
3818 q->t1 = b->y1 * iph;
3819
3820 *xpos += b->xadvance;
3821}
3822
3823//////////////////////////////////////////////////////////////////////////////
3824//
3825// rectangle packing replacement routines if you don't have stb_rect_pack.h
3826//
3827
3828#ifndef STB_RECT_PACK_VERSION
3829
3830typedef int stbrp_coord;
3831
3832////////////////////////////////////////////////////////////////////////////////////
3833// //
3834// //
3835// COMPILER WARNING ?!?!? //
3836// //
3837// //
3838// if you get a compile warning due to these symbols being defined more than //
3839// once, move #include "stb_rect_pack.h" before #include "stb_truetype.h" //
3840// //
3841////////////////////////////////////////////////////////////////////////////////////
3842
3843typedef struct
3844{
3845 int width,height;
3846 int x,y,bottom_y;
3847} stbrp_context;
3848
3849typedef struct
3850{
3851 unsigned char x;
3852} stbrp_node;
3853
3854struct stbrp_rect
3855{
3856 stbrp_coord x,y;
3857 int id,w,h,was_packed;
3858};
3859
3860static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *nodes, int num_nodes)
3861{
3862 con->width = pw;
3863 con->height = ph;
3864 con->x = 0;
3865 con->y = 0;
3866 con->bottom_y = 0;
3867 STBTT__NOTUSED(nodes);
3868 STBTT__NOTUSED(num_nodes);
3869}
3870
3871static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects)
3872{
3873 int i;
3874 for (i=0; i < num_rects; ++i) {
3875 if (con->x + rects[i].w > con->width) {
3876 con->x = 0;
3877 con->y = con->bottom_y;
3878 }
3879 if (con->y + rects[i].h > con->height)
3880 break;
3881 rects[i].x = con->x;
3882 rects[i].y = con->y;
3883 rects[i].was_packed = 1;
3884 con->x += rects[i].w;
3885 if (con->y + rects[i].h > con->bottom_y)
3886 con->bottom_y = con->y + rects[i].h;
3887 }
3888 for ( ; i < num_rects; ++i)
3889 rects[i].was_packed = 0;
3890}
3891#endif
3892
3893//////////////////////////////////////////////////////////////////////////////
3894//
3895// bitmap baking
3896//
3897// This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If
3898// stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy.
3899
3900STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context)
3901{
3902 stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context) ,alloc_context);
3903 int num_nodes = pw - padding;
3904 stbrp_node *nodes = (stbrp_node *) STBTT_malloc(sizeof(*nodes ) * num_nodes,alloc_context);
3905
3906 if (context == NULL || nodes == NULL) {
3907 if (context != NULL) STBTT_free(context, alloc_context);
3908 if (nodes != NULL) STBTT_free(nodes , alloc_context);
3909 return 0;
3910 }
3911
3912 spc->user_allocator_context = alloc_context;
3913 spc->width = pw;
3914 spc->height = ph;
3915 spc->pixels = pixels;
3916 spc->pack_info = context;
3917 spc->nodes = nodes;
3918 spc->padding = padding;
3919 spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw;
3920 spc->h_oversample = 1;
3921 spc->v_oversample = 1;
3922 spc->skip_missing = 0;
3923
3924 stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes);
3925
3926 if (pixels)
3927 STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels
3928
3929 return 1;
3930}
3931
3932STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc)
3933{
3934 STBTT_free(spc->nodes , spc->user_allocator_context);
3935 STBTT_free(spc->pack_info, spc->user_allocator_context);
3936}
3937
3938STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample)
3939{
3940 STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE);
3941 STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE);
3942 if (h_oversample <= STBTT_MAX_OVERSAMPLE)
3943 spc->h_oversample = h_oversample;
3944 if (v_oversample <= STBTT_MAX_OVERSAMPLE)
3945 spc->v_oversample = v_oversample;
3946}
3947
3948STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip)
3949{
3950 spc->skip_missing = skip;
3951}
3952
3953#define STBTT__OVER_MASK (STBTT_MAX_OVERSAMPLE-1)
3954
3955static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width)
3956{
3957 unsigned char buffer[STBTT_MAX_OVERSAMPLE];
3958 int safe_w = w - kernel_width;
3959 int j;
3960 STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze
3961 for (j=0; j < h; ++j) {
3962 int i;
3963 unsigned int total;
3964 STBTT_memset(buffer, 0, kernel_width);
3965
3966 total = 0;
3967
3968 // make kernel_width a constant in common cases so compiler can optimize out the divide
3969 switch (kernel_width) {
3970 case 2:
3971 for (i=0; i <= safe_w; ++i) {
3972 total += pixels[i] - buffer[i & STBTT__OVER_MASK];
3973 buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];
3974 pixels[i] = (unsigned char) (total / 2);
3975 }
3976 break;
3977 case 3:
3978 for (i=0; i <= safe_w; ++i) {
3979 total += pixels[i] - buffer[i & STBTT__OVER_MASK];
3980 buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];
3981 pixels[i] = (unsigned char) (total / 3);
3982 }
3983 break;
3984 case 4:
3985 for (i=0; i <= safe_w; ++i) {
3986 total += pixels[i] - buffer[i & STBTT__OVER_MASK];
3987 buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];
3988 pixels[i] = (unsigned char) (total / 4);
3989 }
3990 break;
3991 case 5:
3992 for (i=0; i <= safe_w; ++i) {
3993 total += pixels[i] - buffer[i & STBTT__OVER_MASK];
3994 buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];
3995 pixels[i] = (unsigned char) (total / 5);
3996 }
3997 break;
3998 default:
3999 for (i=0; i <= safe_w; ++i) {
4000 total += pixels[i] - buffer[i & STBTT__OVER_MASK];
4001 buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];
4002 pixels[i] = (unsigned char) (total / kernel_width);
4003 }
4004 break;
4005 }
4006
4007 for (; i < w; ++i) {
4008 STBTT_assert(pixels[i] == 0);
4009 total -= buffer[i & STBTT__OVER_MASK];
4010 pixels[i] = (unsigned char) (total / kernel_width);
4011 }
4012
4013 pixels += stride_in_bytes;
4014 }
4015}
4016
4017static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width)
4018{
4019 unsigned char buffer[STBTT_MAX_OVERSAMPLE];
4020 int safe_h = h - kernel_width;
4021 int j;
4022 STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze
4023 for (j=0; j < w; ++j) {
4024 int i;
4025 unsigned int total;
4026 STBTT_memset(buffer, 0, kernel_width);
4027
4028 total = 0;
4029
4030 // make kernel_width a constant in common cases so compiler can optimize out the divide
4031 switch (kernel_width) {
4032 case 2:
4033 for (i=0; i <= safe_h; ++i) {
4034 total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];
4035 buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];
4036 pixels[i*stride_in_bytes] = (unsigned char) (total / 2);
4037 }
4038 break;
4039 case 3:
4040 for (i=0; i <= safe_h; ++i) {
4041 total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];
4042 buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];
4043 pixels[i*stride_in_bytes] = (unsigned char) (total / 3);
4044 }
4045 break;
4046 case 4:
4047 for (i=0; i <= safe_h; ++i) {
4048 total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];
4049 buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];
4050 pixels[i*stride_in_bytes] = (unsigned char) (total / 4);
4051 }
4052 break;
4053 case 5:
4054 for (i=0; i <= safe_h; ++i) {
4055 total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];
4056 buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];
4057 pixels[i*stride_in_bytes] = (unsigned char) (total / 5);
4058 }
4059 break;
4060 default:
4061 for (i=0; i <= safe_h; ++i) {
4062 total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];
4063 buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];
4064 pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width);
4065 }
4066 break;
4067 }
4068
4069 for (; i < h; ++i) {
4070 STBTT_assert(pixels[i*stride_in_bytes] == 0);
4071 total -= buffer[i & STBTT__OVER_MASK];
4072 pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width);
4073 }
4074
4075 pixels += 1;
4076 }
4077}
4078
4079static float stbtt__oversample_shift(int oversample)
4080{
4081 if (!oversample)
4082 return 0.0f;
4083
4084 // The prefilter is a box filter of width "oversample",
4085 // which shifts phase by (oversample - 1)/2 pixels in
4086 // oversampled space. We want to shift in the opposite
4087 // direction to counter this.
4088 return (float)-(oversample - 1) / (2.0f * (float)oversample);
4089}
4090
4091// rects array must be big enough to accommodate all characters in the given ranges
4092STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects)
4093{
4094 int i,j,k;
4095 int missing_glyph_added = 0;
4096
4097 k=0;
4098 for (i=0; i < num_ranges; ++i) {
4099 float fh = ranges[i].font_size;
4100 float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh);
4101 ranges[i].h_oversample = (unsigned char) spc->h_oversample;
4102 ranges[i].v_oversample = (unsigned char) spc->v_oversample;
4103 for (j=0; j < ranges[i].num_chars; ++j) {
4104 int x0,y0,x1,y1;
4105 int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j];
4106 int glyph = stbtt_FindGlyphIndex(info, codepoint);
4107 if (glyph == 0 && (spc->skip_missing || missing_glyph_added)) {
4108 rects[k].w = rects[k].h = 0;
4109 } else {
4110 stbtt_GetGlyphBitmapBoxSubpixel(info,glyph,
4111 scale * spc->h_oversample,
4112 scale * spc->v_oversample,
4113 0,0,
4114 &x0,&y0,&x1,&y1);
4115 rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1);
4116 rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1);
4117 if (glyph == 0)
4118 missing_glyph_added = 1;
4119 }
4120 ++k;
4121 }
4122 }
4123
4124 return k;
4125}
4126
4127STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int prefilter_x, int prefilter_y, float *sub_x, float *sub_y, int glyph)
4128{
4129 stbtt_MakeGlyphBitmapSubpixel(info,
4130 output,
4131 out_w - (prefilter_x - 1),
4132 out_h - (prefilter_y - 1),
4133 out_stride,
4134 scale_x,
4135 scale_y,
4136 shift_x,
4137 shift_y,
4138 glyph);
4139
4140 if (prefilter_x > 1)
4141 stbtt__h_prefilter(output, out_w, out_h, out_stride, prefilter_x);
4142
4143 if (prefilter_y > 1)
4144 stbtt__v_prefilter(output, out_w, out_h, out_stride, prefilter_y);
4145
4146 *sub_x = stbtt__oversample_shift(prefilter_x);
4147 *sub_y = stbtt__oversample_shift(prefilter_y);
4148}
4149
4150// rects array must be big enough to accommodate all characters in the given ranges
4151STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects)
4152{
4153 int i,j,k, missing_glyph = -1, return_value = 1;
4154
4155 // save current values
4156 int old_h_over = spc->h_oversample;
4157 int old_v_over = spc->v_oversample;
4158
4159 k = 0;
4160 for (i=0; i < num_ranges; ++i) {
4161 float fh = ranges[i].font_size;
4162 float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh);
4163 float recip_h,recip_v,sub_x,sub_y;
4164 spc->h_oversample = ranges[i].h_oversample;
4165 spc->v_oversample = ranges[i].v_oversample;
4166 recip_h = 1.0f / spc->h_oversample;
4167 recip_v = 1.0f / spc->v_oversample;
4168 sub_x = stbtt__oversample_shift(spc->h_oversample);
4169 sub_y = stbtt__oversample_shift(spc->v_oversample);
4170 for (j=0; j < ranges[i].num_chars; ++j) {
4171 stbrp_rect *r = &rects[k];
4172 if (r->was_packed && r->w != 0 && r->h != 0) {
4173 stbtt_packedchar *bc = &ranges[i].chardata_for_range[j];
4174 int advance, lsb, x0,y0,x1,y1;
4175 int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j];
4176 int glyph = stbtt_FindGlyphIndex(info, codepoint);
4177 stbrp_coord pad = (stbrp_coord) spc->padding;
4178
4179 // pad on left and top
4180 r->x += pad;
4181 r->y += pad;
4182 r->w -= pad;
4183 r->h -= pad;
4184 stbtt_GetGlyphHMetrics(info, glyph, &advance, &lsb);
4185 stbtt_GetGlyphBitmapBox(info, glyph,
4186 scale * spc->h_oversample,
4187 scale * spc->v_oversample,
4188 &x0,&y0,&x1,&y1);
4189 stbtt_MakeGlyphBitmapSubpixel(info,
4190 spc->pixels + r->x + r->y*spc->stride_in_bytes,
4191 r->w - spc->h_oversample+1,
4192 r->h - spc->v_oversample+1,
4193 spc->stride_in_bytes,
4194 scale * spc->h_oversample,
4195 scale * spc->v_oversample,
4196 0,0,
4197 glyph);
4198
4199 if (spc->h_oversample > 1)
4200 stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes,
4201 r->w, r->h, spc->stride_in_bytes,
4202 spc->h_oversample);
4203
4204 if (spc->v_oversample > 1)
4205 stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes,
4206 r->w, r->h, spc->stride_in_bytes,
4207 spc->v_oversample);
4208
4209 bc->x0 = (stbtt_int16) r->x;
4210 bc->y0 = (stbtt_int16) r->y;
4211 bc->x1 = (stbtt_int16) (r->x + r->w);
4212 bc->y1 = (stbtt_int16) (r->y + r->h);
4213 bc->xadvance = scale * advance;
4214 bc->xoff = (float) x0 * recip_h + sub_x;
4215 bc->yoff = (float) y0 * recip_v + sub_y;
4216 bc->xoff2 = (x0 + r->w) * recip_h + sub_x;
4217 bc->yoff2 = (y0 + r->h) * recip_v + sub_y;
4218
4219 if (glyph == 0)
4220 missing_glyph = j;
4221 } else if (spc->skip_missing) {
4222 return_value = 0;
4223 } else if (r->was_packed && r->w == 0 && r->h == 0 && missing_glyph >= 0) {
4224 ranges[i].chardata_for_range[j] = ranges[i].chardata_for_range[missing_glyph];
4225 } else {
4226 return_value = 0; // if any fail, report failure
4227 }
4228
4229 ++k;
4230 }
4231 }
4232
4233 // restore original values
4234 spc->h_oversample = old_h_over;
4235 spc->v_oversample = old_v_over;
4236
4237 return return_value;
4238}
4239
4240STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects)
4241{
4242 stbrp_pack_rects((stbrp_context *) spc->pack_info, rects, num_rects);
4243}
4244
4245STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges)
4246{
4247 stbtt_fontinfo info;
4248 int i,j,n, return_value = 1;
4249 //stbrp_context *context = (stbrp_context *) spc->pack_info;
4250 stbrp_rect *rects;
4251
4252 // flag all characters as NOT packed
4253 for (i=0; i < num_ranges; ++i)
4254 for (j=0; j < ranges[i].num_chars; ++j)
4255 ranges[i].chardata_for_range[j].x0 =
4256 ranges[i].chardata_for_range[j].y0 =
4257 ranges[i].chardata_for_range[j].x1 =
4258 ranges[i].chardata_for_range[j].y1 = 0;
4259
4260 n = 0;
4261 for (i=0; i < num_ranges; ++i)
4262 n += ranges[i].num_chars;
4263
4264 rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context);
4265 if (rects == NULL)
4266 return 0;
4267
4268 info.userdata = spc->user_allocator_context;
4269 stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index));
4270
4271 n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects);
4272
4273 stbtt_PackFontRangesPackRects(spc, rects, n);
4274
4275 return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects);
4276
4277 STBTT_free(rects, spc->user_allocator_context);
4278 return return_value;
4279}
4280
4281STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size,
4282 int first_unicode_codepoint_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range)
4283{
4284 stbtt_pack_range range;
4285 range.first_unicode_codepoint_in_range = first_unicode_codepoint_in_range;
4286 range.array_of_unicode_codepoints = NULL;
4287 range.num_chars = num_chars_in_range;
4288 range.chardata_for_range = chardata_for_range;
4289 range.font_size = font_size;
4290 return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1);
4291}
4292
4293STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap)
4294{
4295 int i_ascent, i_descent, i_lineGap;
4296 float scale;
4297 stbtt_fontinfo info;
4298 stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata, index));
4299 scale = size > 0 ? stbtt_ScaleForPixelHeight(&info, size) : stbtt_ScaleForMappingEmToPixels(&info, -size);
4300 stbtt_GetFontVMetrics(&info, &i_ascent, &i_descent, &i_lineGap);
4301 *ascent = (float) i_ascent * scale;
4302 *descent = (float) i_descent * scale;
4303 *lineGap = (float) i_lineGap * scale;
4304}
4305
4306STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer)
4307{
4308 float ipw = 1.0f / pw, iph = 1.0f / ph;
4309 const stbtt_packedchar *b = chardata + char_index;
4310
4311 if (align_to_integer) {
4312 float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f);
4313 float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5f);
4314 q->x0 = x;
4315 q->y0 = y;
4316 q->x1 = x + b->xoff2 - b->xoff;
4317 q->y1 = y + b->yoff2 - b->yoff;
4318 } else {
4319 q->x0 = *xpos + b->xoff;
4320 q->y0 = *ypos + b->yoff;
4321 q->x1 = *xpos + b->xoff2;
4322 q->y1 = *ypos + b->yoff2;
4323 }
4324
4325 q->s0 = b->x0 * ipw;
4326 q->t0 = b->y0 * iph;
4327 q->s1 = b->x1 * ipw;
4328 q->t1 = b->y1 * iph;
4329
4330 *xpos += b->xadvance;
4331}
4332
4333//////////////////////////////////////////////////////////////////////////////
4334//
4335// sdf computation
4336//
4337
4338#define STBTT_min(a,b) ((a) < (b) ? (a) : (b))
4339#define STBTT_max(a,b) ((a) < (b) ? (b) : (a))
4340
4341static int stbtt__ray_intersect_bezier(float orig[2], float ray[2], float q0[2], float q1[2], float q2[2], float hits[2][2])
4342{
4343 float q0perp = q0[1]*ray[0] - q0[0]*ray[1];
4344 float q1perp = q1[1]*ray[0] - q1[0]*ray[1];
4345 float q2perp = q2[1]*ray[0] - q2[0]*ray[1];
4346 float roperp = orig[1]*ray[0] - orig[0]*ray[1];
4347
4348 float a = q0perp - 2*q1perp + q2perp;
4349 float b = q1perp - q0perp;
4350 float c = q0perp - roperp;
4351
4352 float s0 = 0., s1 = 0.;
4353 int num_s = 0;
4354
4355 if (a != 0.0) {
4356 float discr = b*b - a*c;
4357 if (discr > 0.0) {
4358 float rcpna = -1 / a;
4359 float d = (float) STBTT_sqrt(discr);
4360 s0 = (b+d) * rcpna;
4361 s1 = (b-d) * rcpna;
4362 if (s0 >= 0.0 && s0 <= 1.0)
4363 num_s = 1;
4364 if (d > 0.0 && s1 >= 0.0 && s1 <= 1.0) {
4365 if (num_s == 0) s0 = s1;
4366 ++num_s;
4367 }
4368 }
4369 } else {
4370 // 2*b*s + c = 0
4371 // s = -c / (2*b)
4372 s0 = c / (-2 * b);
4373 if (s0 >= 0.0 && s0 <= 1.0)
4374 num_s = 1;
4375 }
4376
4377 if (num_s == 0)
4378 return 0;
4379 else {
4380 float rcp_len2 = 1 / (ray[0]*ray[0] + ray[1]*ray[1]);
4381 float rayn_x = ray[0] * rcp_len2, rayn_y = ray[1] * rcp_len2;
4382
4383 float q0d = q0[0]*rayn_x + q0[1]*rayn_y;
4384 float q1d = q1[0]*rayn_x + q1[1]*rayn_y;
4385 float q2d = q2[0]*rayn_x + q2[1]*rayn_y;
4386 float rod = orig[0]*rayn_x + orig[1]*rayn_y;
4387
4388 float q10d = q1d - q0d;
4389 float q20d = q2d - q0d;
4390 float q0rd = q0d - rod;
4391
4392 hits[0][0] = q0rd + s0*(2.0f - 2.0f*s0)*q10d + s0*s0*q20d;
4393 hits[0][1] = a*s0+b;
4394
4395 if (num_s > 1) {
4396 hits[1][0] = q0rd + s1*(2.0f - 2.0f*s1)*q10d + s1*s1*q20d;
4397 hits[1][1] = a*s1+b;
4398 return 2;
4399 } else {
4400 return 1;
4401 }
4402 }
4403}
4404
4405static int equal(float *a, float *b)
4406{
4407 return (a[0] == b[0] && a[1] == b[1]);
4408}
4409
4410static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex *verts)
4411{
4412 int i;
4413 float orig[2], ray[2] = { 1, 0 };
4414 float y_frac;
4415 int winding = 0;
4416
4417 orig[0] = x;
4418 orig[1] = y;
4419
4420 // make sure y never passes through a vertex of the shape
4421 y_frac = (float) STBTT_fmod(y, 1.0f);
4422 if (y_frac < 0.01f)
4423 y += 0.01f;
4424 else if (y_frac > 0.99f)
4425 y -= 0.01f;
4426 orig[1] = y;
4427
4428 // test a ray from (-infinity,y) to (x,y)
4429 for (i=0; i < nverts; ++i) {
4430 if (verts[i].type == STBTT_vline) {
4431 int x0 = (int) verts[i-1].x, y0 = (int) verts[i-1].y;
4432 int x1 = (int) verts[i ].x, y1 = (int) verts[i ].y;
4433 if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) {
4434 float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0;
4435 if (x_inter < x)
4436 winding += (y0 < y1) ? 1 : -1;
4437 }
4438 }
4439 if (verts[i].type == STBTT_vcurve) {
4440 int x0 = (int) verts[i-1].x , y0 = (int) verts[i-1].y ;
4441 int x1 = (int) verts[i ].cx, y1 = (int) verts[i ].cy;
4442 int x2 = (int) verts[i ].x , y2 = (int) verts[i ].y ;
4443 int ax = STBTT_min(x0,STBTT_min(x1,x2)), ay = STBTT_min(y0,STBTT_min(y1,y2));
4444 int by = STBTT_max(y0,STBTT_max(y1,y2));
4445 if (y > ay && y < by && x > ax) {
4446 float q0[2],q1[2],q2[2];
4447 float hits[2][2];
4448 q0[0] = (float)x0;
4449 q0[1] = (float)y0;
4450 q1[0] = (float)x1;
4451 q1[1] = (float)y1;
4452 q2[0] = (float)x2;
4453 q2[1] = (float)y2;
4454 if (equal(q0,q1) || equal(q1,q2)) {
4455 x0 = (int)verts[i-1].x;
4456 y0 = (int)verts[i-1].y;
4457 x1 = (int)verts[i ].x;
4458 y1 = (int)verts[i ].y;
4459 if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) {
4460 float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0;
4461 if (x_inter < x)
4462 winding += (y0 < y1) ? 1 : -1;
4463 }
4464 } else {
4465 int num_hits = stbtt__ray_intersect_bezier(orig, ray, q0, q1, q2, hits);
4466 if (num_hits >= 1)
4467 if (hits[0][0] < 0)
4468 winding += (hits[0][1] < 0 ? -1 : 1);
4469 if (num_hits >= 2)
4470 if (hits[1][0] < 0)
4471 winding += (hits[1][1] < 0 ? -1 : 1);
4472 }
4473 }
4474 }
4475 }
4476 return winding;
4477}
4478
4479static float stbtt__cuberoot( float x )
4480{
4481 if (x<0)
4482 return -(float) STBTT_pow(-x,1.0f/3.0f);
4483 else
4484 return (float) STBTT_pow( x,1.0f/3.0f);
4485}
4486
4487// x^3 + c*x^2 + b*x + a = 0
4488static int stbtt__solve_cubic(float a, float b, float c, float* r)
4489{
4490 float s = -a / 3;
4491 float p = b - a*a / 3;
4492 float q = a * (2*a*a - 9*b) / 27 + c;
4493 float p3 = p*p*p;
4494 float d = q*q + 4*p3 / 27;
4495 if (d >= 0) {
4496 float z = (float) STBTT_sqrt(d);
4497 float u = (-q + z) / 2;
4498 float v = (-q - z) / 2;
4499 u = stbtt__cuberoot(u);
4500 v = stbtt__cuberoot(v);
4501 r[0] = s + u + v;
4502 return 1;
4503 } else {
4504 float u = (float) STBTT_sqrt(-p/3);
4505 float v = (float) STBTT_acos(-STBTT_sqrt(-27/p3) * q / 2) / 3; // p3 must be negative, since d is negative
4506 float m = (float) STBTT_cos(v);
4507 float n = (float) STBTT_cos(v-3.141592/2)*1.732050808f;
4508 r[0] = s + u * 2 * m;
4509 r[1] = s - u * (m + n);
4510 r[2] = s - u * (m - n);
4511
4512 //STBTT_assert( STBTT_fabs(((r[0]+a)*r[0]+b)*r[0]+c) < 0.05f); // these asserts may not be safe at all scales, though they're in bezier t parameter units so maybe?
4513 //STBTT_assert( STBTT_fabs(((r[1]+a)*r[1]+b)*r[1]+c) < 0.05f);
4514 //STBTT_assert( STBTT_fabs(((r[2]+a)*r[2]+b)*r[2]+c) < 0.05f);
4515 return 3;
4516 }
4517}
4518
4519STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff)
4520{
4521 float scale_x = scale, scale_y = scale;
4522 int ix0,iy0,ix1,iy1;
4523 int w,h;
4524 unsigned char *data;
4525
4526 if (scale == 0) return NULL;
4527
4528 stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.0f,0.0f, &ix0,&iy0,&ix1,&iy1);
4529
4530 // if empty, return NULL
4531 if (ix0 == ix1 || iy0 == iy1)
4532 return NULL;
4533
4534 ix0 -= padding;
4535 iy0 -= padding;
4536 ix1 += padding;
4537 iy1 += padding;
4538
4539 w = (ix1 - ix0);
4540 h = (iy1 - iy0);
4541
4542 if (width ) *width = w;
4543 if (height) *height = h;
4544 if (xoff ) *xoff = ix0;
4545 if (yoff ) *yoff = iy0;
4546
4547 // invert for y-downwards bitmaps
4548 scale_y = -scale_y;
4549
4550 {
4551 int x,y,i,j;
4552 float *precompute;
4553 stbtt_vertex *verts;
4554 int num_verts = stbtt_GetGlyphShape(info, glyph, &verts);
4555 data = (unsigned char *) STBTT_malloc(w * h, info->userdata);
4556 precompute = (float *) STBTT_malloc(num_verts * sizeof(float), info->userdata);
4557
4558 for (i=0,j=num_verts-1; i < num_verts; j=i++) {
4559 if (verts[i].type == STBTT_vline) {
4560 float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y;
4561 float x1 = verts[j].x*scale_x, y1 = verts[j].y*scale_y;
4562 float dist = (float) STBTT_sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0));
4563 precompute[i] = (dist == 0) ? 0.0f : 1.0f / dist;
4564 } else if (verts[i].type == STBTT_vcurve) {
4565 float x2 = verts[j].x *scale_x, y2 = verts[j].y *scale_y;
4566 float x1 = verts[i].cx*scale_x, y1 = verts[i].cy*scale_y;
4567 float x0 = verts[i].x *scale_x, y0 = verts[i].y *scale_y;
4568 float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2;
4569 float len2 = bx*bx + by*by;
4570 if (len2 != 0.0f)
4571 precompute[i] = 1.0f / (bx*bx + by*by);
4572 else
4573 precompute[i] = 0.0f;
4574 } else
4575 precompute[i] = 0.0f;
4576 }
4577
4578 for (y=iy0; y < iy1; ++y) {
4579 for (x=ix0; x < ix1; ++x) {
4580 float val;
4581 float min_dist = 999999.0f;
4582 float sx = (float) x + 0.5f;
4583 float sy = (float) y + 0.5f;
4584 float x_gspace = (sx / scale_x);
4585 float y_gspace = (sy / scale_y);
4586
4587 int winding = stbtt__compute_crossings_x(x_gspace, y_gspace, num_verts, verts); // @OPTIMIZE: this could just be a rasterization, but needs to be line vs. non-tesselated curves so a new path
4588
4589 for (i=0; i < num_verts; ++i) {
4590 float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y;
4591
4592 // check against every point here rather than inside line/curve primitives -- @TODO: wrong if multiple 'moves' in a row produce a garbage point, and given culling, probably more efficient to do within line/curve
4593 float dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy);
4594 if (dist2 < min_dist*min_dist)
4595 min_dist = (float) STBTT_sqrt(dist2);
4596
4597 if (verts[i].type == STBTT_vline) {
4598 float x1 = verts[i-1].x*scale_x, y1 = verts[i-1].y*scale_y;
4599
4600 // coarse culling against bbox
4601 //if (sx > STBTT_min(x0,x1)-min_dist && sx < STBTT_max(x0,x1)+min_dist &&
4602 // sy > STBTT_min(y0,y1)-min_dist && sy < STBTT_max(y0,y1)+min_dist)
4603 float dist = (float) STBTT_fabs((x1-x0)*(y0-sy) - (y1-y0)*(x0-sx)) * precompute[i];
4604 STBTT_assert(i != 0);
4605 if (dist < min_dist) {
4606 // check position along line
4607 // x' = x0 + t*(x1-x0), y' = y0 + t*(y1-y0)
4608 // minimize (x'-sx)*(x'-sx)+(y'-sy)*(y'-sy)
4609 float dx = x1-x0, dy = y1-y0;
4610 float px = x0-sx, py = y0-sy;
4611 // minimize (px+t*dx)^2 + (py+t*dy)^2 = px*px + 2*px*dx*t + t^2*dx*dx + py*py + 2*py*dy*t + t^2*dy*dy
4612 // derivative: 2*px*dx + 2*py*dy + (2*dx*dx+2*dy*dy)*t, set to 0 and solve
4613 float t = -(px*dx + py*dy) / (dx*dx + dy*dy);
4614 if (t >= 0.0f && t <= 1.0f)
4615 min_dist = dist;
4616 }
4617 } else if (verts[i].type == STBTT_vcurve) {
4618 float x2 = verts[i-1].x *scale_x, y2 = verts[i-1].y *scale_y;
4619 float x1 = verts[i ].cx*scale_x, y1 = verts[i ].cy*scale_y;
4620 float box_x0 = STBTT_min(STBTT_min(x0,x1),x2);
4621 float box_y0 = STBTT_min(STBTT_min(y0,y1),y2);
4622 float box_x1 = STBTT_max(STBTT_max(x0,x1),x2);
4623 float box_y1 = STBTT_max(STBTT_max(y0,y1),y2);
4624 // coarse culling against bbox to avoid computing cubic unnecessarily
4625 if (sx > box_x0-min_dist && sx < box_x1+min_dist && sy > box_y0-min_dist && sy < box_y1+min_dist) {
4626 int num=0;
4627 float ax = x1-x0, ay = y1-y0;
4628 float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2;
4629 float mx = x0 - sx, my = y0 - sy;
4630 float res[3],px,py,t,it;
4631 float a_inv = precompute[i];
4632 if (a_inv == 0.0) { // if a_inv is 0, it's 2nd degree so use quadratic formula
4633 float a = 3*(ax*bx + ay*by);
4634 float b = 2*(ax*ax + ay*ay) + (mx*bx+my*by);
4635 float c = mx*ax+my*ay;
4636 if (a == 0.0) { // if a is 0, it's linear
4637 if (b != 0.0) {
4638 res[num++] = -c/b;
4639 }
4640 } else {
4641 float discriminant = b*b - 4*a*c;
4642 if (discriminant < 0)
4643 num = 0;
4644 else {
4645 float root = (float) STBTT_sqrt(discriminant);
4646 res[0] = (-b - root)/(2*a);
4647 res[1] = (-b + root)/(2*a);
4648 num = 2; // don't bother distinguishing 1-solution case, as code below will still work
4649 }
4650 }
4651 } else {
4652 float b = 3*(ax*bx + ay*by) * a_inv; // could precompute this as it doesn't depend on sample point
4653 float c = (2*(ax*ax + ay*ay) + (mx*bx+my*by)) * a_inv;
4654 float d = (mx*ax+my*ay) * a_inv;
4655 num = stbtt__solve_cubic(b, c, d, res);
4656 }
4657 if (num >= 1 && res[0] >= 0.0f && res[0] <= 1.0f) {
4658 t = res[0], it = 1.0f - t;
4659 px = it*it*x0 + 2*t*it*x1 + t*t*x2;
4660 py = it*it*y0 + 2*t*it*y1 + t*t*y2;
4661 dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy);
4662 if (dist2 < min_dist * min_dist)
4663 min_dist = (float) STBTT_sqrt(dist2);
4664 }
4665 if (num >= 2 && res[1] >= 0.0f && res[1] <= 1.0f) {
4666 t = res[1], it = 1.0f - t;
4667 px = it*it*x0 + 2*t*it*x1 + t*t*x2;
4668 py = it*it*y0 + 2*t*it*y1 + t*t*y2;
4669 dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy);
4670 if (dist2 < min_dist * min_dist)
4671 min_dist = (float) STBTT_sqrt(dist2);
4672 }
4673 if (num >= 3 && res[2] >= 0.0f && res[2] <= 1.0f) {
4674 t = res[2], it = 1.0f - t;
4675 px = it*it*x0 + 2*t*it*x1 + t*t*x2;
4676 py = it*it*y0 + 2*t*it*y1 + t*t*y2;
4677 dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy);
4678 if (dist2 < min_dist * min_dist)
4679 min_dist = (float) STBTT_sqrt(dist2);
4680 }
4681 }
4682 }
4683 }
4684 if (winding == 0)
4685 min_dist = -min_dist; // if outside the shape, value is negative
4686 val = onedge_value + pixel_dist_scale * min_dist;
4687 if (val < 0)
4688 val = 0;
4689 else if (val > 255)
4690 val = 255;
4691 data[(y-iy0)*w+(x-ix0)] = (unsigned char) val;
4692 }
4693 }
4694 STBTT_free(precompute, info->userdata);
4695 STBTT_free(verts, info->userdata);
4696 }
4697 return data;
4698}
4699
4700STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff)
4701{
4702 return stbtt_GetGlyphSDF(info, scale, stbtt_FindGlyphIndex(info, codepoint), padding, onedge_value, pixel_dist_scale, width, height, xoff, yoff);
4703}
4704
4705STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata)
4706{
4707 STBTT_free(bitmap, userdata);
4708}
4709
4710//////////////////////////////////////////////////////////////////////////////
4711//
4712// font name matching -- recommended not to use this
4713//
4714
4715// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string
4716static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2)
4717{
4718 stbtt_int32 i=0;
4719
4720 // convert utf16 to utf8 and compare the results while converting
4721 while (len2) {
4722 stbtt_uint16 ch = s2[0]*256 + s2[1];
4723 if (ch < 0x80) {
4724 if (i >= len1) return -1;
4725 if (s1[i++] != ch) return -1;
4726 } else if (ch < 0x800) {
4727 if (i+1 >= len1) return -1;
4728 if (s1[i++] != 0xc0 + (ch >> 6)) return -1;
4729 if (s1[i++] != 0x80 + (ch & 0x3f)) return -1;
4730 } else if (ch >= 0xd800 && ch < 0xdc00) {
4731 stbtt_uint32 c;
4732 stbtt_uint16 ch2 = s2[2]*256 + s2[3];
4733 if (i+3 >= len1) return -1;
4734 c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000;
4735 if (s1[i++] != 0xf0 + (c >> 18)) return -1;
4736 if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1;
4737 if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1;
4738 if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1;
4739 s2 += 2; // plus another 2 below
4740 len2 -= 2;
4741 } else if (ch >= 0xdc00 && ch < 0xe000) {
4742 return -1;
4743 } else {
4744 if (i+2 >= len1) return -1;
4745 if (s1[i++] != 0xe0 + (ch >> 12)) return -1;
4746 if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1;
4747 if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1;
4748 }
4749 s2 += 2;
4750 len2 -= 2;
4751 }
4752 return i;
4753}
4754
4755static int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2)
4756{
4757 return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2);
4758}
4759
4760// returns results in whatever encoding you request... but note that 2-byte encodings
4761// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare
4762STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID)
4763{
4764 stbtt_int32 i,count,stringOffset;
4765 stbtt_uint8 *fc = font->data;
4766 stbtt_uint32 offset = font->fontstart;
4767 stbtt_uint32 nm = stbtt__find_table(fc, offset, "name");
4768 if (!nm) return NULL;
4769
4770 count = ttUSHORT(fc+nm+2);
4771 stringOffset = nm + ttUSHORT(fc+nm+4);
4772 for (i=0; i < count; ++i) {
4773 stbtt_uint32 loc = nm + 6 + 12 * i;
4774 if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2)
4775 && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) {
4776 *length = ttUSHORT(fc+loc+8);
4777 return (const char *) (fc+stringOffset+ttUSHORT(fc+loc+10));
4778 }
4779 }
4780 return NULL;
4781}
4782
4783static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id)
4784{
4785 stbtt_int32 i;
4786 stbtt_int32 count = ttUSHORT(fc+nm+2);
4787 stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4);
4788
4789 for (i=0; i < count; ++i) {
4790 stbtt_uint32 loc = nm + 6 + 12 * i;
4791 stbtt_int32 id = ttUSHORT(fc+loc+6);
4792 if (id == target_id) {
4793 // find the encoding
4794 stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4);
4795
4796 // is this a Unicode encoding?
4797 if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) {
4798 stbtt_int32 slen = ttUSHORT(fc+loc+8);
4799 stbtt_int32 off = ttUSHORT(fc+loc+10);
4800
4801 // check if there's a prefix match
4802 stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen);
4803 if (matchlen >= 0) {
4804 // check for target_id+1 immediately following, with same encoding & language
4805 if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) {
4806 slen = ttUSHORT(fc+loc+12+8);
4807 off = ttUSHORT(fc+loc+12+10);
4808 if (slen == 0) {
4809 if (matchlen == nlen)
4810 return 1;
4811 } else if (matchlen < nlen && name[matchlen] == ' ') {
4812 ++matchlen;
4813 if (stbtt_CompareUTF8toUTF16_bigendian_internal((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen))
4814 return 1;
4815 }
4816 } else {
4817 // if nothing immediately following
4818 if (matchlen == nlen)
4819 return 1;
4820 }
4821 }
4822 }
4823
4824 // @TODO handle other encodings
4825 }
4826 }
4827 return 0;
4828}
4829
4830static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags)
4831{
4832 stbtt_int32 nlen = (stbtt_int32) STBTT_strlen((char *) name);
4833 stbtt_uint32 nm,hd;
4834 if (!stbtt__isfont(fc+offset)) return 0;
4835
4836 // check italics/bold/underline flags in macStyle...
4837 if (flags) {
4838 hd = stbtt__find_table(fc, offset, "head");
4839 if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0;
4840 }
4841
4842 nm = stbtt__find_table(fc, offset, "name");
4843 if (!nm) return 0;
4844
4845 if (flags) {
4846 // if we checked the macStyle flags, then just check the family and ignore the subfamily
4847 if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1;
4848 if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1;
4849 if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1;
4850 } else {
4851 if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1;
4852 if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1;
4853 if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1;
4854 }
4855
4856 return 0;
4857}
4858
4859static int stbtt_FindMatchingFont_internal(unsigned char *font_collection, char *name_utf8, stbtt_int32 flags)
4860{
4861 stbtt_int32 i;
4862 for (i=0;;++i) {
4863 stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i);
4864 if (off < 0) return off;
4865 if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags))
4866 return off;
4867 }
4868}
4869
4870#if defined(__GNUC__) || defined(__clang__)
4871#pragma GCC diagnostic push
4872#pragma GCC diagnostic ignored "-Wcast-qual"
4873#endif
4874
4875STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset,
4876 float pixel_height, unsigned char *pixels, int pw, int ph,
4877 int first_char, int num_chars, stbtt_bakedchar *chardata)
4878{
4879 return stbtt_BakeFontBitmap_internal((unsigned char *) data, offset, pixel_height, pixels, pw, ph, first_char, num_chars, chardata);
4880}
4881
4882STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index)
4883{
4884 return stbtt_GetFontOffsetForIndex_internal((unsigned char *) data, index);
4885}
4886
4887STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data)
4888{
4889 return stbtt_GetNumberOfFonts_internal((unsigned char *) data);
4890}
4891
4892STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset)
4893{
4894 return stbtt_InitFont_internal(info, (unsigned char *) data, offset);
4895}
4896
4897STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags)
4898{
4899 return stbtt_FindMatchingFont_internal((unsigned char *) fontdata, (char *) name, flags);
4900}
4901
4902STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2)
4903{
4904 return stbtt_CompareUTF8toUTF16_bigendian_internal((char *) s1, len1, (char *) s2, len2);
4905}
4906
4907#if defined(__GNUC__) || defined(__clang__)
4908#pragma GCC diagnostic pop
4909#endif
4910
4911#endif // STB_TRUETYPE_IMPLEMENTATION
4912
4913
4914// FULL VERSION HISTORY
4915//
4916// 1.19 (2018-02-11) OpenType GPOS kerning (horizontal only), STBTT_fmod
4917// 1.18 (2018-01-29) add missing function
4918// 1.17 (2017-07-23) make more arguments const; doc fix
4919// 1.16 (2017-07-12) SDF support
4920// 1.15 (2017-03-03) make more arguments const
4921// 1.14 (2017-01-16) num-fonts-in-TTC function
4922// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts
4923// 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual
4924// 1.11 (2016-04-02) fix unused-variable warning
4925// 1.10 (2016-04-02) allow user-defined fabs() replacement
4926// fix memory leak if fontsize=0.0
4927// fix warning from duplicate typedef
4928// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use alloc userdata for PackFontRanges
4929// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges
4930// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints;
4931// allow PackFontRanges to pack and render in separate phases;
4932// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?);
4933// fixed an assert() bug in the new rasterizer
4934// replace assert() with STBTT_assert() in new rasterizer
4935// 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine)
4936// also more precise AA rasterizer, except if shapes overlap
4937// remove need for STBTT_sort
4938// 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC
4939// 1.04 (2015-04-15) typo in example
4940// 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes
4941// 1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++
4942// 1.01 (2014-12-08) fix subpixel position when oversampling to exactly match
4943// non-oversampled; STBTT_POINT_SIZE for packed case only
4944// 1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling
4945// 0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg)
4946// 0.9 (2014-08-07) support certain mac/iOS fonts without an MS platformID
4947// 0.8b (2014-07-07) fix a warning
4948// 0.8 (2014-05-25) fix a few more warnings
4949// 0.7 (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back
4950// 0.6c (2012-07-24) improve documentation
4951// 0.6b (2012-07-20) fix a few more warnings
4952// 0.6 (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels,
4953// stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty
4954// 0.5 (2011-12-09) bugfixes:
4955// subpixel glyph renderer computed wrong bounding box
4956// first vertex of shape can be off-curve (FreeSans)
4957// 0.4b (2011-12-03) fixed an error in the font baking example
4958// 0.4 (2011-12-01) kerning, subpixel rendering (tor)
4959// bugfixes for:
4960// codepoint-to-glyph conversion using table fmt=12
4961// codepoint-to-glyph conversion using table fmt=4
4962// stbtt_GetBakedQuad with non-square texture (Zer)
4963// updated Hello World! sample to use kerning and subpixel
4964// fixed some warnings
4965// 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM)
4966// userdata, malloc-from-userdata, non-zero fill (stb)
4967// 0.2 (2009-03-11) Fix unsigned/signed char warnings
4968// 0.1 (2009-03-09) First public release
4969//
4970
4971/*
4972------------------------------------------------------------------------------
4973This software is available under 2 licenses -- choose whichever you prefer.
4974------------------------------------------------------------------------------
4975ALTERNATIVE A - MIT License
4976Copyright (c) 2017 Sean Barrett
4977Permission is hereby granted, free of charge, to any person obtaining a copy of
4978this software and associated documentation files (the "Software"), to deal in
4979the Software without restriction, including without limitation the rights to
4980use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
4981of the Software, and to permit persons to whom the Software is furnished to do
4982so, subject to the following conditions:
4983The above copyright notice and this permission notice shall be included in all
4984copies or substantial portions of the Software.
4985THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
4986IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
4987FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
4988AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
4989LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
4990OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
4991SOFTWARE.
4992------------------------------------------------------------------------------
4993ALTERNATIVE B - Public Domain (www.unlicense.org)
4994This is free and unencumbered software released into the public domain.
4995Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
4996software, either in source code form or as a compiled binary, for any purpose,
4997commercial or non-commercial, and by any means.
4998In jurisdictions that recognize copyright laws, the author or authors of this
4999software dedicate any and all copyright interest in the software to the public
5000domain. We make this dedication for the benefit of the public at large and to
5001the detriment of our heirs and successors. We intend this dedication to be an
5002overt act of relinquishment in perpetuity of all present and future rights to
5003this software under copyright law.
5004THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
5005IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
5006FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
5007AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
5008ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
5009WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
5010------------------------------------------------------------------------------
5011*/
diff --git a/src/ui/color.c b/src/ui/color.c
new file mode 100644
index 00000000..365bf986
--- /dev/null
+++ b/src/ui/color.c
@@ -0,0 +1,27 @@
1#include "color.h"
2
3static const iColor transparent_;
4
5iColor get_Color(int color) {
6 static const iColor palette[] = {
7 { 0, 0, 0, 255 },
8 { 40, 40, 40, 255 },
9 { 80, 80, 80, 255 },
10 { 160, 160, 160, 255 },
11 { 255, 255, 255, 255 },
12 { 106, 80, 0, 255 },
13 { 255, 192, 0, 255 },
14 { 0, 96, 128, 255 },
15 { 0, 192, 255, 255 },
16 { 255, 255, 32, 255 },
17 { 255, 64, 64, 255 },
18 { 255, 0, 255, 255 },
19 { 132, 132, 255, 255 },
20 { 0, 200, 0, 255 },
21 };
22 const iColor *clr = &transparent_;
23 if (color >= 0 && color < (int) iElemCount(palette)) {
24 clr = &palette[color];
25 }
26 return *clr;
27}
diff --git a/src/ui/color.h b/src/ui/color.h
new file mode 100644
index 00000000..f0244fc2
--- /dev/null
+++ b/src/ui/color.h
@@ -0,0 +1,48 @@
1#pragma once
2
3#include <the_Foundation/defs.h>
4
5enum iColorId {
6 none_ColorId = -1,
7 black_ColorId,
8 gray25_ColorId,
9 gray50_ColorId,
10 gray75_ColorId,
11 white_ColorId,
12 brown_ColorId,
13 orange_ColorId,
14 teal_ColorId,
15 cyan_ColorId,
16 yellow_ColorId,
17 red_ColorId,
18 magenta_ColorId,
19 blue_ColorId,
20 green_ColorId,
21 max_ColorId
22};
23
24#define mask_ColorId 0x0f
25#define permanent_ColorId 0x80 /* cannot be changed via escapes */
26
27#define black_ColorEscape "\r0"
28#define gray25_ColorEscape "\r1"
29#define gray50_ColorEscape "\r2"
30#define gray75_ColorEscape "\r3"
31#define white_ColorEscape "\r4"
32#define brown_ColorEscape "\r5"
33#define orange_ColorEscape "\r6"
34#define teal_ColorEscape "\r7"
35#define cyan_ColorEscape "\r8"
36#define yellow_ColorEscape "\r9"
37#define red_ColorEscape "\r:"
38#define magenta_ColorEscape "\r;"
39#define blue_ColorEscape "\r<"
40#define green_ColorEscape "\r="
41
42iDeclareType(Color)
43
44struct Impl_Color {
45 uint8_t r, g, b, a;
46};
47
48iColor get_Color (int color);
diff --git a/src/ui/command.c b/src/ui/command.c
new file mode 100644
index 00000000..16a0d948
--- /dev/null
+++ b/src/ui/command.c
@@ -0,0 +1,89 @@
1#include "command.h"
2#include "app.h"
3
4#include <the_Foundation/string.h>
5#include <ctype.h>
6
7iBool equal_Command(const char *cmdWithArgs, const char *cmd) {
8 if (strchr(cmdWithArgs, ':')) {
9 return beginsWith_CStr(cmdWithArgs, cmd) && cmdWithArgs[strlen(cmd)] == ' ';
10 }
11 return equal_CStr(cmdWithArgs, cmd);
12}
13
14static const iString *tokenString_(const char *label) {
15 return collectNewFormat_String(" %s:", label);
16}
17
18int argLabel_Command(const char *cmd, const char *label) {
19 const iString *tok = tokenString_(label);
20 const char *ptr = strstr(cmd, cstr_String(tok));
21 if (ptr) {
22 return atoi(ptr + size_String(tok));
23 }
24 return 0;
25}
26
27int arg_Command(const char *cmd) {
28 return argLabel_Command(cmd, "arg");
29}
30
31float argf_Command(const char *cmd) {
32 const char *ptr = strstr(cmd, " arg:");
33 if (ptr) {
34 return strtof(ptr + 5, NULL);
35 }
36 return 0;
37}
38
39void *pointerLabel_Command(const char *cmd, const char *label) {
40 const iString *tok = tokenString_(label);
41 const char *ptr = strstr(cmd, cstr_String(tok));
42 if (ptr) {
43 void *val = NULL;
44 sscanf(ptr + size_String(tok), "%p", &val);
45 return val;
46 }
47 return NULL;
48}
49
50void *pointer_Command(const char *cmd) {
51 return pointerLabel_Command(cmd, "ptr");
52}
53
54const char *valuePtr_Command(const char *cmd, const char *label) {
55 const iString *tok = tokenString_(label);
56 const char *ptr = strstr(cmd, cstr_String(tok));
57 if (ptr) {
58 return ptr + size_String(tok);
59 }
60 return NULL;
61}
62
63const iString *string_Command(const char *cmd, const char *label) {
64 iRangecc val = { valuePtr_Command(cmd, label), NULL };
65 if (val.start) {
66 for (val.end = val.start; *val.end && !isspace(*val.end); val.end++) {}
67 return collect_String(newRange_String(&val));
68 }
69 return collectNew_String();
70}
71
72iInt2 dir_Command(const char *cmd) {
73 const char *ptr = strstr(cmd, " dir:");
74 if (ptr) {
75 iInt2 dir;
76 sscanf(ptr + 5, "%d%d", &dir.x, &dir.y);
77 return dir;
78 }
79 return zero_I2();
80}
81
82iInt2 coord_Command(const char *cmd) {
83 iInt2 coord = zero_I2();
84 const char *ptr = strstr(cmd, " coord:");
85 if (ptr) {
86 sscanf(ptr + 7, "%d%d", &coord.x, &coord.y);
87 }
88 return coord;
89}
diff --git a/src/ui/command.h b/src/ui/command.h
new file mode 100644
index 00000000..84533d77
--- /dev/null
+++ b/src/ui/command.h
@@ -0,0 +1,16 @@
1#pragma once
2
3#include <the_Foundation/vec2.h>
4
5iBool equal_Command (const char *commandWithArgs, const char *command);
6
7int arg_Command (const char *); /* arg: */
8float argf_Command (const char *); /* arg: */
9int argLabel_Command (const char *, const char *label);
10void * pointer_Command (const char *); /* ptr: */
11void * pointerLabel_Command (const char *, const char *label);
12iInt2 coord_Command (const char *);
13iInt2 dir_Command (const char *);
14
15const iString * string_Command (const char *, const char *label);
16const char * valuePtr_Command(const char *, const char *label);
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c
new file mode 100644
index 00000000..7a2b7d1c
--- /dev/null
+++ b/src/ui/inputwidget.c
@@ -0,0 +1,280 @@
1#include "inputwidget.h"
2#include "paint.h"
3#include "util.h"
4
5#include <the_Foundation/array.h>
6#include <SDL_timer.h>
7
8struct Impl_InputWidget {
9 iWidget widget;
10 enum iInputMode mode;
11 size_t maxLen;
12 iArray text; /* iChar[] */
13 iArray oldText; /* iChar[] */
14 size_t cursor;
15 int font;
16 iClick click;
17};
18
19iDefineObjectConstructionArgs(InputWidget, (size_t maxLen), maxLen)
20
21void init_InputWidget(iInputWidget *d, size_t maxLen) {
22 iWidget *w = &d->widget;
23 init_Widget(w);
24 setFlags_Widget(w, focusable_WidgetFlag | hover_WidgetFlag, iTrue);
25 init_Array(&d->text, sizeof(iChar));
26 init_Array(&d->oldText, sizeof(iChar));
27 d->font = uiInput_FontId;
28 d->cursor = 0;
29 setMaxLen_InputWidget(d, maxLen);
30 if (maxLen == 0) {
31 /* Caller must arrange the width. */
32 w->rect.size.y = lineHeight_Text(d->font) + 2 * gap_UI;
33 setFlags_Widget(w, fixedHeight_WidgetFlag, iTrue);
34 }
35 init_Click(&d->click, d, SDL_BUTTON_LEFT);
36}
37
38void deinit_InputWidget(iInputWidget *d) {
39 deinit_Array(&d->oldText);
40 deinit_Array(&d->text);
41}
42
43void setMode_InputWidget(iInputWidget *d, enum iInputMode mode) {
44 d->mode = mode;
45}
46
47const iString *text_InputWidget(const iInputWidget *d) {
48 return collect_String(newUnicodeN_String(constData_Array(&d->text), size_Array(&d->text)));
49}
50
51void setMaxLen_InputWidget(iInputWidget *d, size_t maxLen) {
52 d->maxLen = maxLen;
53 d->mode = (maxLen == 0 ? insert_InputMode : overwrite_InputMode);
54 resize_Array(&d->text, maxLen);
55 if (maxLen) {
56 /* Set a fixed size. */
57 iBlock *content = new_Block(maxLen);
58 fill_Block(content, 'M');
59 setSize_Widget(
60 as_Widget(d),
61 add_I2(measure_Text(d->font, cstr_Block(content)), init_I2(6 * gap_UI, 2 * gap_UI)));
62 delete_Block(content);
63 }
64}
65
66void setText_InputWidget(iInputWidget *d, const iString *text) {
67 clear_Array(&d->text);
68 iConstForEach(String, i, text) {
69 pushBack_Array(&d->text, &i.value);
70 }
71}
72
73void setCursor_InputWidget(iInputWidget *d, size_t pos) {
74 d->cursor = iMin(pos, size_Array(&d->text));
75}
76
77void begin_InputWidget(iInputWidget *d) {
78 iWidget *w = as_Widget(d);
79 if (flags_Widget(w) & selected_WidgetFlag) {
80 /* Already active. */
81 return;
82 }
83 setFlags_Widget(w, hidden_WidgetFlag | disabled_WidgetFlag, iFalse);
84 setCopy_Array(&d->oldText, &d->text);
85 if (d->mode == overwrite_InputMode) {
86 d->cursor = 0;
87 }
88 else {
89 d->cursor = iMin(size_Array(&d->text), d->maxLen - 1);
90 }
91 SDL_StartTextInput();
92 setFlags_Widget(w, selected_WidgetFlag, iTrue);
93}
94
95void end_InputWidget(iInputWidget *d, iBool accept) {
96 iWidget *w = as_Widget(d);
97 if (~flags_Widget(w) & selected_WidgetFlag) {
98 /* Was not active. */
99 return;
100 }
101 if (!accept) {
102 setCopy_Array(&d->text, &d->oldText);
103 }
104 SDL_StopTextInput();
105 setFlags_Widget(w, selected_WidgetFlag, iFalse);
106 const char *id = cstr_String(id_Widget(as_Widget(d)));
107 if (!*id) id = "_";
108 postCommand_Widget(w, "input.ended id:%s arg:%d", id, accept ? 1 : 0);
109}
110
111static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) {
112 if (isCommand_Widget(as_Widget(d), ev, "focus.gained")) {
113 begin_InputWidget(d);
114 return iTrue;
115 }
116 else if (isCommand_Widget(as_Widget(d), ev, "focus.lost")) {
117 end_InputWidget(d, iTrue);
118 return iTrue;
119 }
120 switch (processEvent_Click(&d->click, ev)) {
121 case none_ClickResult:
122 break;
123 case started_ClickResult:
124 case drag_ClickResult:
125 case double_ClickResult:
126 case aborted_ClickResult:
127 return iTrue;
128 case finished_ClickResult:
129 setFocus_Widget(as_Widget(d));
130 return iTrue;
131 }
132 if (ev->type == SDL_KEYUP) {
133 return iTrue;
134 }
135 const size_t curMax = iMin(size_Array(&d->text), d->maxLen - 1);
136 if (ev->type == SDL_KEYDOWN && isFocused_Widget(as_Widget(d))) {
137 const int key = ev->key.keysym.sym;
138 const int mods = keyMods_Sym(ev->key.keysym.mod);
139 switch (key) {
140 case SDLK_RETURN:
141 case SDLK_KP_ENTER:
142 setFocus_Widget(NULL);
143 return iTrue;
144 case SDLK_ESCAPE:
145 end_InputWidget(d, iFalse);
146 setFocus_Widget(NULL);
147 return iTrue;
148 case SDLK_BACKSPACE:
149 if (mods & KMOD_ALT) {
150 clear_Array(&d->text);
151 d->cursor = 0;
152 }
153 else if (d->cursor > 0) {
154 remove_Array(&d->text, --d->cursor);
155 }
156 return iTrue;
157 case SDLK_d:
158 if (mods != KMOD_CTRL) break;
159 case SDLK_DELETE:
160 if (d->cursor < size_Array(&d->text)) {
161 remove_Array(&d->text, d->cursor);
162 }
163 return iTrue;
164 case SDLK_k:
165 if (mods == KMOD_CTRL) {
166 removeN_Array(&d->text, d->cursor, size_Array(&d->text) - d->cursor);
167 return iTrue;
168 }
169 break;
170 case SDLK_HOME:
171 case SDLK_END:
172 d->cursor = (key == SDLK_HOME ? 0 : curMax);
173 return iTrue;
174 case SDLK_a:
175 case SDLK_e:
176 if (mods == KMOD_CTRL) {
177 d->cursor = (key == 'a' ? 0 : curMax);
178 return iTrue;
179 }
180 break;
181 case SDLK_LEFT:
182 if (mods & KMOD_PRIMARY) {
183 d->cursor = 0;
184 }
185 else if (d->cursor > 0) {
186 d->cursor--;
187 }
188 return iTrue;
189 case SDLK_RIGHT:
190 if (mods & KMOD_PRIMARY) {
191 d->cursor = curMax;
192 }
193 else if (d->cursor < curMax) {
194 d->cursor++;
195 }
196 return iTrue;
197 case SDLK_TAB:
198 /* Allow focus switching. */
199 return processEvent_Widget(as_Widget(d), ev);
200 }
201 if (mods & (KMOD_PRIMARY | KMOD_SECONDARY)) {
202 return iFalse;
203 }
204 return iTrue;
205 }
206 else if (ev->type == SDL_TEXTINPUT && isFocused_Widget(as_Widget(d))) {
207 const iString *uni = collectNewCStr_String(ev->text.text);
208 const iChar chr = first_String(uni);
209 if (d->mode == insert_InputMode) {
210 insert_Array(&d->text, d->cursor, &chr);
211 d->cursor++;
212 }
213 else {
214 if (d->cursor >= size_Array(&d->text)) {
215 resize_Array(&d->text, d->cursor + 1);
216 }
217 set_Array(&d->text, d->cursor++, &chr);
218 if (d->maxLen && d->cursor == d->maxLen) {
219 setFocus_Widget(NULL);
220 }
221 }
222 return iTrue;
223 }
224 return processEvent_Widget(as_Widget(d), ev);
225}
226
227static void draw_InputWidget_(const iInputWidget *d) {
228 const uint32_t time = frameTime_Window(get_Window());
229 const iInt2 padding = init_I2(3 * gap_UI, gap_UI);
230 iRect bounds = adjusted_Rect(bounds_Widget(constAs_Widget(d)), padding, neg_I2(padding));
231 const iBool isFocused = isFocused_Widget(constAs_Widget(d));
232 const iBool isHover = isHover_Widget(constAs_Widget(d)) &&
233 contains_Widget(constAs_Widget(d), mouseCoord_Window(get_Window()));
234 iPaint p;
235 init_Paint(&p);
236 iString text;
237 initUnicodeN_String(&text, constData_Array(&d->text), size_Array(&d->text));
238 fillRect_Paint(&p, bounds, black_ColorId);
239 drawRect_Paint(&p,
240 adjusted_Rect(bounds, neg_I2(one_I2()), zero_I2()),
241 isFocused ? orange_ColorId : isHover ? cyan_ColorId : gray50_ColorId);
242 setClip_Paint(&p, bounds);
243 const iInt2 emSize = advance_Text(d->font, "M");
244 const int textWidth = advance_Text(d->font, cstr_String(&text)).x;
245 const int cursorX = advanceN_Text(d->font, cstr_String(&text), d->cursor).x;
246 int xOff = 0;
247 if (d->maxLen == 0) {
248 if (textWidth > width_Rect(bounds) - emSize.x) {
249 xOff = width_Rect(bounds) - emSize.x - textWidth;
250 }
251 if (cursorX + xOff < width_Rect(bounds) / 2) {
252 xOff = width_Rect(bounds) / 2 - cursorX;
253 }
254 xOff = iMin(xOff, 0);
255 }
256 draw_Text(d->font, addX_I2(topLeft_Rect(bounds), xOff), white_ColorId, cstr_String(&text));
257 clearClip_Paint(&p);
258 /* Cursor blinking. */
259 if (isFocused && (time & 256)) {
260 const iInt2 prefixSize = advanceN_Text(d->font, cstr_String(&text), d->cursor);
261 const iInt2 curPos = init_I2(xOff + left_Rect(bounds) + prefixSize.x, top_Rect(bounds));
262 const iRect curRect = { curPos, addX_I2(emSize, 1) };
263 iString cur;
264 if (d->cursor < size_Array(&d->text)) {
265 initUnicodeN_String(&cur, constAt_Array(&d->text, d->cursor), 1);
266 }
267 else {
268 initCStr_String(&cur, " ");
269 }
270 fillRect_Paint(&p, curRect, orange_ColorId);
271 draw_Text(d->font, curPos, black_ColorId, cstr_String(&cur));
272 deinit_String(&cur);
273 }
274 deinit_String(&text);
275}
276
277iBeginDefineSubclass(InputWidget, Widget)
278 .processEvent = (iAny *) processEvent_InputWidget_,
279 .draw = (iAny *) draw_InputWidget_,
280iEndDefineSubclass(InputWidget)
diff --git a/src/ui/inputwidget.h b/src/ui/inputwidget.h
new file mode 100644
index 00000000..b606f974
--- /dev/null
+++ b/src/ui/inputwidget.h
@@ -0,0 +1,20 @@
1#pragma once
2
3#include "widget.h"
4
5iDeclareWidgetClass(InputWidget)
6iDeclareObjectConstructionArgs(InputWidget, size_t maxLen)
7
8enum iInputMode {
9 insert_InputMode,
10 overwrite_InputMode,
11};
12
13void setMode_InputWidget (iInputWidget *, enum iInputMode mode);
14void setMaxLen_InputWidget (iInputWidget *, size_t maxLen);
15void setText_InputWidget (iInputWidget *, const iString *text);
16void setCursor_InputWidget (iInputWidget *, size_t pos);
17void begin_InputWidget (iInputWidget *);
18void end_InputWidget (iInputWidget *, iBool accept);
19
20const iString * text_InputWidget (const iInputWidget *);
diff --git a/src/ui/labelwidget.c b/src/ui/labelwidget.c
new file mode 100644
index 00000000..7b64857b
--- /dev/null
+++ b/src/ui/labelwidget.c
@@ -0,0 +1,281 @@
1#include "labelwidget.h"
2#include "text.h"
3#include "color.h"
4#include "paint.h"
5#include "app.h"
6#include "util.h"
7
8iLocalDef iInt2 padding_(void) { return init_I2(3 * gap_UI, gap_UI); }
9
10struct Impl_LabelWidget {
11 iWidget widget;
12 iString label;
13 int font;
14 int key;
15 int kmods;
16 iString command;
17 iClick click;
18};
19
20iDefineObjectConstructionArgs(LabelWidget,
21 (const char *label, int key, int kmods, const char *cmd),
22 label, key, kmods, cmd)
23
24static iBool checkModifiers_(int have, int req) {
25 return keyMods_Sym(req) == keyMods_Sym(have);
26}
27
28static void trigger_LabelWidget_(const iLabelWidget *d) {
29 postCommand_Widget(&d->widget, "%s", cstr_String(&d->command));
30}
31
32static iBool processEvent_LabelWidget_(iLabelWidget *d, const SDL_Event *ev) {
33 if (isCommand_UserEvent(ev, "metrics.changed")) {
34 updateSize_LabelWidget(d);
35 }
36 if (!isEmpty_String(&d->command)) {
37 switch (processEvent_Click(&d->click, ev)) {
38 case started_ClickResult:
39 setFlags_Widget(&d->widget, pressed_WidgetFlag, iTrue);
40 return iTrue;
41 case aborted_ClickResult:
42 setFlags_Widget(&d->widget, pressed_WidgetFlag, iFalse);
43 return iTrue;
44 case finished_ClickResult:
45 setFlags_Widget(&d->widget, pressed_WidgetFlag, iFalse);
46 trigger_LabelWidget_(d);
47 return iTrue;
48 case double_ClickResult:
49 return iTrue;
50 default:
51 break;
52 }
53 switch (ev->type) {
54 case SDL_KEYDOWN: {
55 const int mods = ev->key.keysym.mod;
56 if (d->key && ev->key.keysym.sym == d->key && checkModifiers_(mods, d->kmods)) {
57 trigger_LabelWidget_(d);
58 return iTrue;
59 }
60 break;
61 }
62 }
63 }
64 return processEvent_Widget(&d->widget, ev);
65}
66
67static void keyStr_LabelWidget_(const iLabelWidget *d, iString *str) {
68#if defined (iPlatformApple)
69 if (d->kmods & KMOD_CTRL) {
70 appendChar_String(str, 0x2303);
71 }
72 if (d->kmods & KMOD_ALT) {
73 appendChar_String(str, 0x2325);
74 }
75 if (d->kmods & KMOD_SHIFT) {
76 appendChar_String(str, 0x21e7);
77 }
78 if (d->kmods & KMOD_GUI) {
79 appendChar_String(str, 0x2318);
80 }
81#else
82 if (d->kmods & KMOD_CTRL) {
83 appendCStr_String(str, "Ctrl+");
84 }
85 if (d->kmods & KMOD_ALT) {
86 appendCStr_String(str, "Alt+");
87 }
88 if (d->kmods & KMOD_SHIFT) {
89 appendCStr_String(str, "Shift+");
90 }
91 if (d->kmods & KMOD_GUI) {
92 appendCStr_String(str, "Meta+");
93 }
94#endif
95 if (d->key == 0x20) {
96 appendCStr_String(str, "Space");
97 }
98 else if (d->key == SDLK_LEFT) {
99 appendChar_String(str, 0x2190);
100 }
101 else if (d->key == SDLK_RIGHT) {
102 appendChar_String(str, 0x2192);
103 }
104 else if (d->key < 128 && (isalnum(d->key) || ispunct(d->key))) {
105 appendChar_String(str, upper_Char(d->key));
106 }
107 else if (d->key == SDLK_BACKSPACE) {
108 appendChar_String(str, 0x232b); /* Erase to the Left */
109 }
110 else if (d->key == SDLK_DELETE) {
111 appendChar_String(str, 0x2326); /* Erase to the Right */
112 }
113 else {
114 appendCStr_String(str, SDL_GetKeyName(d->key));
115 }
116}
117
118static void draw_LabelWidget_(const iLabelWidget *d) {
119 const iWidget *w = constAs_Widget(d);
120 draw_Widget(w);
121 const iBool isButton = d->click.button != 0;
122 const int flags = flags_Widget(w);
123 const iRect bounds = bounds_Widget(w);
124 iRect rect = bounds;
125 if (isButton) {
126 shrink_Rect(&rect, divi_I2(gap2_UI, 4));
127 adjustEdges_Rect(&rect, gap_UI / 8, 0, -gap_UI / 8, 0);
128 }
129 iPaint p;
130 init_Paint(&p);
131 int bg = 0;
132 int fg = gray75_ColorId;
133 int frame = isButton ? gray50_ColorId : gray25_ColorId;
134 int frame2 = isButton ? black_ColorId : frame;
135 if (flags & selected_WidgetFlag) {
136 bg = teal_ColorId;
137 fg = white_ColorId;
138 frame = isButton ? cyan_ColorId : frame;
139 }
140 if (isHover_Widget(w)) {
141 if (flags & frameless_WidgetFlag) {
142 bg = teal_ColorId;
143 fg = white_ColorId;
144 if (isButton && flags & selected_WidgetFlag) frame = white_ColorId;
145 }
146 else {
147 if (frame != cyan_ColorId) {
148 if (startsWith_String(&d->label, orange_ColorEscape)) {
149 frame = orange_ColorId;
150 frame2 = brown_ColorId;
151 }
152 else {
153 frame = cyan_ColorId;
154 frame2 = teal_ColorId;
155 }
156 }
157 else {
158 frame = white_ColorId;
159 frame2 = cyan_ColorId;
160 }
161 }
162 }
163 if (flags & pressed_WidgetFlag) {
164 bg = orange_ColorId | permanent_ColorId;
165 if (isButton) frame = bg;
166 fg = black_ColorId | permanent_ColorId;
167 }
168 if (bg) {
169 fillRect_Paint(&p, rect, bg);
170 }
171 if (~flags & frameless_WidgetFlag) {
172 iRect frameRect = adjusted_Rect(rect, zero_I2(), init1_I2(-1));
173 if (isButton) {
174 iInt2 points[] = {
175 bottomLeft_Rect(frameRect), topLeft_Rect(frameRect), topRight_Rect(frameRect),
176 bottomRight_Rect(frameRect), bottomLeft_Rect(frameRect)
177 };
178 drawLines_Paint(&p, points + 2, 3, frame2);
179 drawLines_Paint(&p, points, 3, frame);
180 }
181 else {
182 drawRect_Paint(&p, frameRect, frame);
183 }
184 }
185 setClip_Paint(&p, rect);
186 if (flags & alignLeft_WidgetFlag) {
187 draw_Text(d->font, add_I2(bounds.pos, padding_()), fg, cstr_String(&d->label));
188 if ((flags & drawKey_WidgetFlag) && d->key) {
189 iString str;
190 init_String(&str);
191 keyStr_LabelWidget_(d, &str);
192 draw_Text(uiShortcuts_FontId, negX_I2(add_I2(topRight_Rect(bounds), negX_I2(padding_()))),
193 flags & pressed_WidgetFlag ? fg : cyan_ColorId, cstr_String(&str));
194 deinit_String(&str);
195 }
196 }
197 else if (flags & alignRight_WidgetFlag) {
198 draw_Text(
199 d->font,
200 mul_I2(init_I2(-1, 1), add_I2(topRight_Rect(bounds), negX_I2(padding_()))),
201 fg,
202 cstr_String(&d->label));
203 }
204 else {
205 drawCentered_Text(d->font, bounds, fg, cstr_String(&d->label));
206 }
207 clearClip_Paint(&p);
208}
209
210void updateSize_LabelWidget(iLabelWidget *d) {
211 iWidget *w = as_Widget(d);
212 const int flags = flags_Widget(w);
213 iInt2 size = add_I2(measure_Text(d->font, cstr_String(&d->label)), muli_I2(padding_(), 2));
214 if ((flags & drawKey_WidgetFlag) && d->key) {
215 iString str;
216 init_String(&str);
217 keyStr_LabelWidget_(d, &str);
218 size.x += 2 * gap_UI + measure_Text(uiShortcuts_FontId, cstr_String(&str)).x;
219 deinit_String(&str);
220 }
221 if (~flags & fixedWidth_WidgetFlag) {
222 w->rect.size.x = size.x;
223 }
224 if (~flags & fixedHeight_WidgetFlag) {
225 w->rect.size.y = size.y;
226 }
227}
228
229void init_LabelWidget(iLabelWidget *d, const char *label, int key, int kmods, const char *cmd) {
230 init_Widget(&d->widget);
231 d->font = default_FontId;
232 initCStr_String(&d->label, label);
233 if (cmd) {
234 initCStr_String(&d->command, cmd);
235 }
236 else {
237 init_String(&d->command);
238 }
239 d->key = key;
240 d->kmods = kmods;
241 init_Click(&d->click, d, !isEmpty_String(&d->command) ? SDL_BUTTON_LEFT : 0);
242 setFlags_Widget(&d->widget, hover_WidgetFlag, d->click.button != 0);
243 updateSize_LabelWidget(d);
244}
245
246void deinit_LabelWidget(iLabelWidget *d) {
247 deinit_String(&d->label);
248 deinit_String(&d->command);
249}
250
251void setFont_LabelWidget(iLabelWidget *d, int fontId) {
252 d->font = fontId;
253 updateSize_LabelWidget(d);
254}
255
256void setText_LabelWidget(iLabelWidget *d, const iString *text) {
257 updateText_LabelWidget(d, text);
258 updateSize_LabelWidget(d);
259}
260
261void updateText_LabelWidget(iLabelWidget *d, const iString *text) {
262 set_String(&d->label, text);
263}
264
265void updateTextCStr_LabelWidget(iLabelWidget *d, const char *text) {
266 setCStr_String(&d->label, text);
267}
268
269void setTextCStr_LabelWidget(iLabelWidget *d, const char *text) {
270 setCStr_String(&d->label, text);
271 updateSize_LabelWidget(d);
272}
273
274const iString *command_LabelWidget(const iLabelWidget *d) {
275 return &d->command;
276}
277
278iBeginDefineSubclass(LabelWidget, Widget)
279 .processEvent = (iAny *) processEvent_LabelWidget_,
280 .draw = (iAny *) draw_LabelWidget_,
281iEndDefineSubclass(LabelWidget)
diff --git a/src/ui/labelwidget.h b/src/ui/labelwidget.h
new file mode 100644
index 00000000..0563728e
--- /dev/null
+++ b/src/ui/labelwidget.h
@@ -0,0 +1,22 @@
1#pragma once
2
3/* Text label/button. */
4
5#include "widget.h"
6
7iDeclareWidgetClass(LabelWidget)
8iDeclareObjectConstructionArgs(LabelWidget, const char *label, int key, int kmods, const char *command)
9
10iLocalDef iLabelWidget *newEmpty_LabelWidget(void) {
11 return new_LabelWidget("", 0, 0, NULL);
12}
13
14void setFont_LabelWidget (iLabelWidget *, int fontId);
15void setText_LabelWidget (iLabelWidget *, const iString *text); /* resizes widget */
16void setTextCStr_LabelWidget (iLabelWidget *, const char *text);
17
18void updateSize_LabelWidget (iLabelWidget *);
19void updateText_LabelWidget (iLabelWidget *, const iString *text); /* not resized */
20void updateTextCStr_LabelWidget (iLabelWidget *, const char *text); /* not resized */
21
22const iString *command_LabelWidget (const iLabelWidget *);
diff --git a/src/ui/macos.h b/src/ui/macos.h
new file mode 100644
index 00000000..bb991a61
--- /dev/null
+++ b/src/ui/macos.h
@@ -0,0 +1,9 @@
1#pragma once
2
3#include "util.h"
4
5/* Platform-specific functionality for macOS */
6
7void setupApplication_MacOS (void);
8void insertMenuItems_MacOS (const char *menuLabel, const iMenuItem *items, size_t count);
9void handleCommand_MacOS (const char *cmd);
diff --git a/src/ui/macos.m b/src/ui/macos.m
new file mode 100644
index 00000000..73e17a4c
--- /dev/null
+++ b/src/ui/macos.m
@@ -0,0 +1,434 @@
1#include "macos.h"
2#include "app.h"
3#include "command.h"
4#include "widget.h"
5#include "color.h"
6
7#import <AppKit/AppKit.h>
8
9#if 0
10static NSTouchBarItemIdentifier play_TouchId_ = @"fi.skyjake.BitwiseHarmony.play";
11static NSTouchBarItemIdentifier restart_TouchId_ = @"fi.skyjake.BitwiseHarmony.restart";
12
13static NSTouchBarItemIdentifier seqMoveUp_TouchId_ = @"fi.skyjake.BitwiseHarmony.sequence.move.up";
14static NSTouchBarItemIdentifier seqMoveDown_TouchId_ = @"fi.skyjake.BitwiseHarmony.sequence.move.down";
15
16static NSTouchBarItemIdentifier goto_TouchId_ = @"fi.skyjake.BitwiseHarmony.goto";
17static NSTouchBarItemIdentifier mute_TouchId_ = @"fi.skyjake.BitwiseHarmony.mute";
18static NSTouchBarItemIdentifier solo_TouchId_ = @"fi.skyjake.BitwiseHarmony.solo";
19static NSTouchBarItemIdentifier color_TouchId_ = @"fi.skyjake.BitwiseHarmony.color";
20static NSTouchBarItemIdentifier event_TouchId_ = @"fi.skyjake.BitwiseHarmony.event";
21
22static NSTouchBarItemIdentifier eventList_TouchId_ = @"fi.skyjake.BitwiseHarmony.eventlist";
23static NSTouchBarItemIdentifier masterGainEvent_TouchId_ = @"fi.skyjake.BitwiseHarmony.event.mastergain";
24static NSTouchBarItemIdentifier resetEvent_TouchId_ = @"fi.skyjake.BitwiseHarmony.event.reset";
25static NSTouchBarItemIdentifier voiceEvent_TouchId_ = @"fi.skyjake.BitwiseHarmony.event.voice";
26static NSTouchBarItemIdentifier panEvent_TouchId_ = @"fi.skyjake.BitwiseHarmony.event.pan";
27static NSTouchBarItemIdentifier gainEvent_TouchId_ = @"fi.skyjake.BitwiseHarmony.event.gain";
28static NSTouchBarItemIdentifier fadeEvent_TouchId_ = @"fi.skyjake.BitwiseHarmony.event.fade";
29static NSTouchBarItemIdentifier pitchSpeedEvent_TouchId_ = @"fi.skyjake.BitwiseHarmony.event.pitchspeed";
30static NSTouchBarItemIdentifier pitchBendUpEvent_TouchId_ = @"fi.skyjake.BitwiseHarmony.event.pitchbendup";
31static NSTouchBarItemIdentifier pitchBendDownEvent_TouchId_ = @"fi.skyjake.BitwiseHarmony.event.pitchbenddown";
32static NSTouchBarItemIdentifier tremoloEvent_TouchId_ = @"fi.skyjake.BitwiseHarmony.event.tremolo";
33#endif
34
35enum iTouchBarVariant {
36 default_TouchBarVariant,
37 sequence_TouchBarVariant,
38 tracker_TouchBarVariant,
39 wide_TouchBarVariant,
40};
41
42@interface CommandButton : NSButtonTouchBarItem {
43 NSString *command;
44 iWidget *widget;
45}
46- (id)initWithIdentifier:(NSTouchBarItemIdentifier)identifier
47 title:(NSString *)title
48 command:(NSString *)cmd;
49- (id)initWithIdentifier:(NSTouchBarItemIdentifier)identifier
50 title:(NSString *)title
51 widget:(iWidget *)widget
52 command:(NSString *)cmd;
53- (void)dealloc;
54@end
55
56@implementation CommandButton
57
58- (id)initWithIdentifier:(NSTouchBarItemIdentifier)identifier
59 title:(NSString *)title
60 command:(NSString *)cmd {
61 [super initWithIdentifier:identifier];
62 self.title = title;
63 self.target = self;
64 self.action = @selector(buttonPressed);
65 command = cmd;
66 return self;
67}
68
69- (id)initWithIdentifier:(NSTouchBarItemIdentifier)identifier
70 title:(NSString *)title
71 widget:(iWidget *)aWidget
72 command:(NSString *)cmd {
73 [self initWithIdentifier:identifier title:title command:[cmd retain]];
74 widget = aWidget;
75 return self;
76}
77
78- (void)dealloc {
79 [command release];
80 [super dealloc];
81}
82
83- (void)buttonPressed {
84 const char *cmd = [command cStringUsingEncoding:NSUTF8StringEncoding];
85 if (widget) {
86 postCommand_Widget(widget, "%s", cmd);
87 }
88 else {
89 postCommand_App(cmd);
90 }
91}
92
93@end
94
95@interface MyDelegate : NSResponder<NSApplicationDelegate, NSTouchBarDelegate> {
96 enum iTouchBarVariant touchBarVariant;
97 NSObject<NSApplicationDelegate> *sdlDelegate;
98 NSMutableDictionary<NSString *, NSString*> *menuCommands;
99}
100- (id)initWithSDLDelegate:(NSObject<NSApplicationDelegate> *)sdl;
101- (NSTouchBar *)makeTouchBar;
102/* SDL needs to do its own thing. */
103- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename;
104- (void)applicationDidFinishLaunching:(NSNotification *)notificatiosn;
105@end
106
107@implementation MyDelegate
108
109- (id)initWithSDLDelegate:(NSObject<NSApplicationDelegate> *)sdl {
110 [super init];
111 menuCommands = [[NSMutableDictionary<NSString *, NSString *> alloc] init];
112 touchBarVariant = default_TouchBarVariant;
113 sdlDelegate = sdl;
114 return self;
115}
116
117- (void)dealloc {
118 [menuCommands release];
119 [super dealloc];
120}
121
122- (void)setTouchBarVariant:(enum iTouchBarVariant)variant {
123 touchBarVariant = variant;
124 self.touchBar = nil;
125}
126
127- (void)setCommand:(NSString *)command forMenuItem:(NSMenuItem *)menuItem {
128 [menuCommands setObject:command forKey:[menuItem title]];
129}
130
131- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename {
132 return [sdlDelegate application:theApplication openFile:filename];
133}
134
135- (void)applicationDidFinishLaunching:(NSNotification *)notification {
136 [sdlDelegate applicationDidFinishLaunching:notification];
137}
138
139- (NSTouchBar *)makeTouchBar {
140 NSTouchBar *bar = [[NSTouchBar alloc] init];
141 bar.delegate = self;
142#if 0
143 switch (touchBarVariant) {
144 case default_TouchBarVariant:
145 bar.defaultItemIdentifiers = @[ play_TouchId_, restart_TouchId_,
146 NSTouchBarItemIdentifierFixedSpaceSmall,
147 NSTouchBarItemIdentifierOtherItemsProxy ];
148 break;
149 case sequence_TouchBarVariant:
150 bar.defaultItemIdentifiers = @[ play_TouchId_, restart_TouchId_,
151 NSTouchBarItemIdentifierFlexibleSpace,
152 seqMoveUp_TouchId_, seqMoveDown_TouchId_,
153 NSTouchBarItemIdentifierFlexibleSpace,
154 NSTouchBarItemIdentifierOtherItemsProxy];
155 break;
156 case tracker_TouchBarVariant:
157 bar.defaultItemIdentifiers = @[ play_TouchId_, restart_TouchId_,
158 NSTouchBarItemIdentifierFlexibleSpace,
159 goto_TouchId_,
160 event_TouchId_,
161 NSTouchBarItemIdentifierFlexibleSpace,
162 solo_TouchId_, mute_TouchId_, color_TouchId_,
163 NSTouchBarItemIdentifierFlexibleSpace,
164 NSTouchBarItemIdentifierOtherItemsProxy ];
165 break;
166 case wide_TouchBarVariant:
167 bar.defaultItemIdentifiers = @[ play_TouchId_, restart_TouchId_,
168 NSTouchBarItemIdentifierFlexibleSpace,
169 event_TouchId_,
170 NSTouchBarItemIdentifierFlexibleSpace,
171 solo_TouchId_, mute_TouchId_, color_TouchId_,
172 NSTouchBarItemIdentifierFlexibleSpace,
173 seqMoveUp_TouchId_, seqMoveDown_TouchId_,
174 NSTouchBarItemIdentifierFlexibleSpace,
175 NSTouchBarItemIdentifierOtherItemsProxy ];
176 break;
177 }
178#endif
179 return bar;
180}
181
182- (void)playPressed {
183 postCommand_App("playback.toggle");
184}
185
186- (void)restartPressed {
187 postCommand_App("playback.restart");
188}
189
190- (void)showPreferences {
191 postCommand_App("preferences");
192}
193
194- (void)postMenuItemCommand:(id)sender {
195 NSString *command = [menuCommands objectForKey:[(NSMenuItem *)sender title]];
196 if (command) {
197 postCommand_App([command cStringUsingEncoding:NSUTF8StringEncoding]);
198 }
199}
200
201- (nullable NSTouchBarItem *)touchBar:(NSTouchBar *)touchBar
202 makeItemForIdentifier:(NSTouchBarItemIdentifier)identifier {
203 iUnused(touchBar);
204#if 0
205 if ([identifier isEqualToString:play_TouchId_]) {
206 return [NSButtonTouchBarItem
207 buttonTouchBarItemWithIdentifier:identifier
208 image:[NSImage imageNamed:NSImageNameTouchBarPlayPauseTemplate]
209 target:self
210 action:@selector(playPressed)];
211 }
212 else if ([identifier isEqualToString:restart_TouchId_]) {
213 return [NSButtonTouchBarItem
214 buttonTouchBarItemWithIdentifier:identifier
215 image:[NSImage imageNamed:NSImageNameTouchBarSkipToStartTemplate]
216 target:self
217 action:@selector(restartPressed)];
218 }
219 else if ([identifier isEqualToString:seqMoveUp_TouchId_]) {
220 return [[CommandButton alloc] initWithIdentifier:identifier
221 title:@"Seq\u2b06"
222 widget:findWidget_App("sequence")
223 command:@"sequence.swap arg:-1"];
224 }
225 else if ([identifier isEqualToString:seqMoveDown_TouchId_]) {
226 return [[CommandButton alloc] initWithIdentifier:identifier
227 title:@"Seq\u2b07"
228 widget:findWidget_App("sequence")
229 command:@"sequence.swap arg:1"];
230 }
231 else if ([identifier isEqualToString:goto_TouchId_]) {
232 return [[CommandButton alloc] initWithIdentifier:identifier
233 title:@"Go to…"
234 command:@"pattern.goto arg:-1"];
235 }
236 else if ([identifier isEqualToString:event_TouchId_]) {
237 NSTouchBar *events = [[NSTouchBar alloc] init];
238 events.delegate = self;
239 events.defaultItemIdentifiers = @[ eventList_TouchId_ ];
240 NSPopoverTouchBarItem *pop = [[NSPopoverTouchBarItem alloc] initWithIdentifier:identifier];
241 pop.collapsedRepresentationLabel = @"Event";
242 pop.popoverTouchBar = events;
243 [events release];
244 return pop;
245 }
246 else if ([identifier isEqualToString:eventList_TouchId_]) {
247 const struct {
248 NSTouchBarItemIdentifier id;
249 const char *title;
250 const char *command;
251 } buttonDefs_[] = {
252 { voiceEvent_TouchId_, "Voice", "tracker.setevent type:2" },
253 { panEvent_TouchId_, "Pan", "tracker.setevent type:3 arg:128" },
254 { gainEvent_TouchId_, "Gain", "tracker.setevent type:4 arg:128" },
255 { fadeEvent_TouchId_, "Fade", "tracker.setevent type:5" },
256 { tremoloEvent_TouchId_, "Trem", "tracker.setevent type:9" },
257 { pitchSpeedEvent_TouchId_, "P.Spd", "tracker.setevent type:6" },
258 { pitchBendUpEvent_TouchId_, "BnUp", "tracker.setevent type:7" },
259 { pitchBendDownEvent_TouchId_, "BnDn", "tracker.setevent type:8" },
260 { masterGainEvent_TouchId_, "M.Gain", "tracker.setevent type:10 arg:64" },
261 { resetEvent_TouchId_, "Reset", "tracker.setevent type:1" },
262 };
263 NSMutableArray *items = [[NSMutableArray alloc] init];
264 iForIndices(i, buttonDefs_) {
265 CommandButton *button = [[CommandButton alloc]
266 initWithIdentifier:buttonDefs_[i].id
267 title:[NSString stringWithUTF8String:buttonDefs_[i].title]
268 widget:findWidget_App("tracker")
269 command:[NSString stringWithUTF8String:buttonDefs_[i].command]
270 ];
271 [items addObject:button];
272 }
273 NSGroupTouchBarItem *group = [NSGroupTouchBarItem groupItemWithIdentifier:identifier
274 items:items];
275 [items release];
276 return group;
277 }
278 else if ([identifier isEqualToString:mute_TouchId_]) {
279 return [[CommandButton alloc] initWithIdentifier:identifier
280 title:@"Mute"
281 widget:findWidget_App("tracker")
282 command:@"tracker.mute"];
283 }
284 else if ([identifier isEqualToString:solo_TouchId_]) {
285 return [[CommandButton alloc] initWithIdentifier:identifier
286 title:@"Solo"
287 widget:findWidget_App("tracker")
288 command:@"tracker.solo"];
289 }
290 else if ([identifier isEqualToString:color_TouchId_]) {
291 NSTouchBar *colors = [[NSTouchBar alloc] init];
292 colors.delegate = self;
293 colors.defaultItemIdentifiers = @[ NSTouchBarItemIdentifierFlexibleSpace,
294 whiteColor_TouchId_,
295 yellowColor_TouchId_,
296 orangeColor_TouchId_,
297 redColor_TouchId_,
298 magentaColor_TouchId_,
299 blueColor_TouchId_,
300 cyanColor_TouchId_,
301 greenColor_TouchId_,
302 NSTouchBarItemIdentifierFlexibleSpace ];
303 NSPopoverTouchBarItem *pop = [[NSPopoverTouchBarItem alloc] initWithIdentifier:identifier];
304 pop.collapsedRepresentationImage = [NSImage imageNamed:NSImageNameTouchBarColorPickerFill];
305 pop.popoverTouchBar = colors;
306 [colors release];
307 return pop;
308 }
309 else if ([identifier isEqualToString:whiteColor_TouchId_]) {
310 return [[ColorButton alloc] initWithIdentifier:identifier
311 trackColor:white_TrackColor];
312 }
313 else if ([identifier isEqualToString:yellowColor_TouchId_]) {
314 return [[ColorButton alloc] initWithIdentifier:identifier
315 trackColor:yellow_TrackColor];
316 }
317 else if ([identifier isEqualToString:orangeColor_TouchId_]) {
318 return [[ColorButton alloc] initWithIdentifier:identifier
319 trackColor:orange_TrackColor];
320 }
321 else if ([identifier isEqualToString:redColor_TouchId_]) {
322 return [[ColorButton alloc] initWithIdentifier:identifier
323 trackColor:red_TrackColor];
324 }
325 else if ([identifier isEqualToString:magentaColor_TouchId_]) {
326 return [[ColorButton alloc] initWithIdentifier:identifier
327 trackColor:magenta_TrackColor];
328 }
329 else if ([identifier isEqualToString:blueColor_TouchId_]) {
330 return [[ColorButton alloc] initWithIdentifier:identifier
331 trackColor:blue_TrackColor];
332 }
333 else if ([identifier isEqualToString:cyanColor_TouchId_]) {
334 return [[ColorButton alloc] initWithIdentifier:identifier
335 trackColor:cyan_TrackColor];
336 }
337 else if ([identifier isEqualToString:greenColor_TouchId_]) {
338 return [[ColorButton alloc] initWithIdentifier:identifier
339 trackColor:green_TrackColor];
340 }
341#endif
342 return nil;
343}
344
345@end
346
347void setupApplication_MacOS(void) {
348 NSApplication *app = [NSApplication sharedApplication];
349 /* Our delegate will override SDL's delegate. */
350 MyDelegate *myDel = [[MyDelegate alloc] initWithSDLDelegate:app.delegate];
351 app.delegate = myDel;
352 NSMenu *appMenu = [[[NSApp mainMenu] itemAtIndex:0] submenu];
353 NSMenuItem *prefsItem = [appMenu itemWithTitle:@"Preferences…"];
354 prefsItem.target = myDel;
355 prefsItem.action = @selector(showPreferences);
356}
357
358void insertMenuItems_MacOS(const char *menuLabel, const iMenuItem *items, size_t count) {
359 NSApplication *app = [NSApplication sharedApplication];
360 MyDelegate *myDel = (MyDelegate *) app.delegate;
361 NSMenu *appMenu = [app mainMenu];
362 NSMenuItem *mainItem = [appMenu insertItemWithTitle:[NSString stringWithUTF8String:menuLabel]
363 action:nil
364 keyEquivalent:@""
365 atIndex:(iCmpStr(menuLabel, "File") == 0 ? 1 :
366 iCmpStr(menuLabel, "Edit") == 0 ? 2 : 3)];
367 NSMenu *menu = [[NSMenu alloc] initWithTitle:[NSString stringWithUTF8String:menuLabel]];
368 for (size_t i = 0; i < count; ++i) {
369 const char *label = items[i].label;
370 if (label[0] == '\r') {
371 /* Skip the formatting escape. */
372 label += 2;
373 }
374 if (equal_CStr(label, "---")) {
375 [menu addItem:[NSMenuItem separatorItem]];
376 }
377 else {
378 const iBool hasCommand = (items[i].command && items[i].command[0]);
379 iString key;
380 init_String(&key);
381 if (items[i].key == SDLK_LEFT) {
382 appendChar_String(&key, 0x2190);
383 }
384 else if (items[i].key == SDLK_RIGHT) {
385 appendChar_String(&key, 0x2192);
386 }
387 else if (items[i].key) {
388 appendChar_String(&key, items[i].key);
389 }
390 NSMenuItem *item = [menu addItemWithTitle:[NSString stringWithUTF8String:label]
391 action:(hasCommand ? @selector(postMenuItemCommand:) : nil)
392 keyEquivalent:[NSString stringWithUTF8String:cstr_String(&key)]];
393 NSEventModifierFlags modMask = 0;
394 if (items[i].kmods & KMOD_GUI) {
395 modMask |= NSEventModifierFlagCommand;
396 }
397 if (items[i].kmods & KMOD_ALT) {
398 modMask |= NSEventModifierFlagOption;
399 }
400 if (items[i].kmods & KMOD_CTRL) {
401 modMask |= NSEventModifierFlagControl;
402 }
403 if (items[i].kmods & KMOD_SHIFT) {
404 modMask |= NSEventModifierFlagShift;
405 }
406 [item setKeyEquivalentModifierMask:modMask];
407 if (hasCommand) {
408 [myDel setCommand:[NSString stringWithUTF8String:items[i].command] forMenuItem:item];
409 }
410 deinit_String(&key);
411 }
412 }
413 [mainItem setSubmenu:menu];
414 [menu release];
415}
416
417void handleCommand_MacOS(const char *cmd) {
418 if (equal_Command(cmd, "tabs.changed")) {
419 MyDelegate *myDel = (MyDelegate *) [[NSApplication sharedApplication] delegate];
420 const char *tabId = valuePtr_Command(cmd, "id");
421 if (equal_CStr(tabId, "tracker")) {
422 [myDel setTouchBarVariant:tracker_TouchBarVariant];
423 }
424 else if (equal_CStr(tabId, "sequence")) {
425 [myDel setTouchBarVariant:sequence_TouchBarVariant];
426 }
427 else if (equal_CStr(tabId, "trackertab")) {
428 [myDel setTouchBarVariant:wide_TouchBarVariant];
429 }
430 else {
431 [myDel setTouchBarVariant:default_TouchBarVariant];
432 }
433 }
434}
diff --git a/src/ui/metrics.c b/src/ui/metrics.c
new file mode 100644
index 00000000..38ae5955
--- /dev/null
+++ b/src/ui/metrics.c
@@ -0,0 +1,16 @@
1#include "metrics.h"
2
3#include <the_Foundation/math.h>
4
5#define defaultFontSize_Metrics 16
6#define defaultGap_Metrics 4
7
8int gap_UI = defaultGap_Metrics;
9iInt2 gap2_UI = { defaultGap_Metrics, defaultGap_Metrics };
10int fontSize_UI = defaultFontSize_Metrics;
11
12void setPixelRatio_Metrics(float pixelRatio) {
13 gap_UI = iRound(defaultGap_Metrics * pixelRatio);
14 gap2_UI = init1_I2(gap_UI);
15 fontSize_UI = iRound(defaultFontSize_Metrics * pixelRatio);
16}
diff --git a/src/ui/metrics.h b/src/ui/metrics.h
new file mode 100644
index 00000000..d59f726a
--- /dev/null
+++ b/src/ui/metrics.h
@@ -0,0 +1,9 @@
1#pragma once
2
3#include <the_Foundation/vec2.h>
4
5extern int gap_UI;
6extern int fontSize_UI;
7extern iInt2 gap2_UI;
8
9void setPixelRatio_Metrics (float pixelRatio);
diff --git a/src/ui/paint.c b/src/ui/paint.c
new file mode 100644
index 00000000..0a2e6cd3
--- /dev/null
+++ b/src/ui/paint.c
@@ -0,0 +1,58 @@
1#include "paint.h"
2
3iLocalDef SDL_Renderer *renderer_Paint_(const iPaint *d) {
4 iAssert(d->dst);
5 return d->dst->render;
6}
7
8static void setColor_Paint_(const iPaint *d, int color) {
9 const iColor clr = get_Color(color & mask_ColorId);
10 SDL_SetRenderDrawColor(renderer_Paint_(d), clr.r, clr.g, clr.b, clr.a);
11}
12
13void init_Paint(iPaint *d) {
14 d->dst = get_Window();
15}
16
17void setClip_Paint(iPaint *d, iRect rect) {
18 SDL_RenderSetClipRect(renderer_Paint_(d), (const SDL_Rect *) &rect);
19}
20
21void clearClip_Paint(iPaint *d) {
22 const SDL_Rect winRect = { 0, 0, d->dst->root->rect.size.x, d->dst->root->rect.size.y };
23 SDL_RenderSetClipRect(renderer_Paint_(d), &winRect);
24}
25
26void drawRect_Paint(const iPaint *d, iRect rect, int color) {
27 iInt2 br = bottomRight_Rect(rect);
28 /* Keep the right/bottom edge visible in the window. */
29 if (br.x == d->dst->root->rect.size.x) br.x--;
30 if (br.y == d->dst->root->rect.size.y) br.y--;
31 const SDL_Point edges[] = {
32 { left_Rect(rect), top_Rect(rect) },
33 { br.x, top_Rect(rect) },
34 { br.x, br.y },
35 { left_Rect(rect), br.y },
36 { left_Rect(rect), top_Rect(rect) }
37 };
38 setColor_Paint_(d, color);
39 SDL_RenderDrawLines(renderer_Paint_(d), edges, iElemCount(edges));
40}
41
42void drawRectThickness_Paint(const iPaint *d, iRect rect, int thickness, int color) {
43 thickness = iClamp(thickness, 1, 4);
44 while (thickness--) {
45 drawRect_Paint(d, rect, color);
46 shrink_Rect(&rect, one_I2());
47 }
48}
49
50void fillRect_Paint(const iPaint *d, iRect rect, int color) {
51 setColor_Paint_(d, color);
52 SDL_RenderFillRect(renderer_Paint_(d), (SDL_Rect *) &rect);
53}
54
55void drawLines_Paint(const iPaint *d, const iInt2 *points, size_t count, int color) {
56 setColor_Paint_(d, color);
57 SDL_RenderDrawLines(renderer_Paint_(d), (const SDL_Point *) points, count);
58}
diff --git a/src/ui/paint.h b/src/ui/paint.h
new file mode 100644
index 00000000..9535b142
--- /dev/null
+++ b/src/ui/paint.h
@@ -0,0 +1,33 @@
1#pragma once
2
3#include <the_Foundation/rect.h>
4#include "color.h"
5#include "text.h"
6#include "window.h"
7
8iDeclareType(Paint)
9
10struct Impl_Paint {
11 iWindow *dst;
12};
13
14void init_Paint (iPaint *);
15
16void setClip_Paint (iPaint *, iRect rect);
17void clearClip_Paint (iPaint *);
18
19void drawRect_Paint (const iPaint *, iRect rect, int color);
20void drawRectThickness_Paint (const iPaint *, iRect rect, int thickness, int color);
21void fillRect_Paint (const iPaint *, iRect rect, int color);
22
23void drawLines_Paint (const iPaint *, const iInt2 *points, size_t count, int color);
24
25iLocalDef void drawLine_Paint(const iPaint *d, iInt2 a, iInt2 b, int color) {
26 drawLines_Paint(d, (iInt2[]){ a, b }, 2, color);
27}
28iLocalDef void drawHLine_Paint(const iPaint *d, iInt2 pos, int len, int color) {
29 drawLine_Paint(d, pos, addX_I2(pos, len), color);
30}
31iLocalDef void drawVLine_Paint(const iPaint *d, iInt2 pos, int len, int color) {
32 drawLine_Paint(d, pos, addY_I2(pos, len), color);
33}
diff --git a/src/ui/text.c b/src/ui/text.c
new file mode 100644
index 00000000..ac211481
--- /dev/null
+++ b/src/ui/text.c
@@ -0,0 +1,533 @@
1#include "text.h"
2#include "color.h"
3#include "metrics.h"
4#include "embedded.h"
5#include "app.h"
6
7#define STB_TRUETYPE_IMPLEMENTATION
8#include "../stb_truetype.h"
9
10#include <the_Foundation/array.h>
11#include <the_Foundation/file.h>
12#include <the_Foundation/hash.h>
13#include <the_Foundation/math.h>
14#include <the_Foundation/path.h>
15#include <the_Foundation/vec2.h>
16
17#include <SDL_surface.h>
18#include <stdarg.h>
19
20iDeclareType(Glyph)
21iDeclareTypeConstructionArgs(Glyph, iChar ch)
22
23struct Impl_Glyph {
24 iHashNode node;
25 iRect rect;
26 int advance;
27 int dx, dy;
28};
29
30void init_Glyph(iGlyph *d, iChar ch) {
31 d->node.key = ch;
32 d->rect = zero_Rect();
33 d->advance = 0;
34}
35
36void deinit_Glyph(iGlyph *d) {
37 iUnused(d);
38}
39
40iChar char_Glyph(const iGlyph *d) {
41 return d->node.key;
42}
43
44iDefineTypeConstructionArgs(Glyph, (iChar ch), ch)
45
46/*-----------------------------------------------------------------------------------------------*/
47
48iDeclareType(Font)
49
50struct Impl_Font {
51 iBlock * data;
52 stbtt_fontinfo font;
53 float scale;
54 int height;
55 int baseline;
56 iHash glyphs;
57};
58
59static void init_Font(iFont *d, const iBlock *data, int height) {
60 init_Hash(&d->glyphs);
61 d->data = NULL;
62 d->height = height;
63 iZap(d->font);
64 stbtt_InitFont(&d->font, constData_Block(data), 0);
65 d->scale = stbtt_ScaleForPixelHeight(&d->font, height);
66 int ascent;
67 stbtt_GetFontVMetrics(&d->font, &ascent, 0, 0);
68 d->baseline = (int) ascent * d->scale;
69}
70
71static void deinit_Font(iFont *d) {
72 iForEach(Hash, i, &d->glyphs) {
73 delete_Glyph((iGlyph *) i.value);
74 }
75 deinit_Hash(&d->glyphs);
76 delete_Block(d->data);
77}
78
79iDeclareType(Text)
80
81struct Impl_Text {
82 iFont fonts[max_FontId];
83 SDL_Renderer *render;
84 SDL_Texture * cache;
85 iInt2 cacheSize;
86 iInt2 cachePos;
87 int cacheRowHeight;
88 SDL_Palette * grayscale;
89};
90
91static iText text_;
92
93void init_Text(SDL_Renderer *render) {
94 iText *d = &text_;
95 d->render = render;
96 /* A grayscale palette for rasterized glyphs. */ {
97 SDL_Color colors[256];
98 for (int i = 0; i < 256; ++i) {
99 colors[i] = (SDL_Color){ 255, 255, 255, i };
100 }
101 d->grayscale = SDL_AllocPalette(256);
102 SDL_SetPaletteColors(d->grayscale, colors, 0, 256);
103 }
104 /* Initialize the glyph cache. */ {
105 d->cacheSize = init1_I2(fontSize_UI * 16);
106 d->cachePos = zero_I2();
107 d->cache = SDL_CreateTexture(render,
108 SDL_PIXELFORMAT_RGBA8888,
109 SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET,
110 d->cacheSize.x,
111 d->cacheSize.y);
112 SDL_SetTextureBlendMode(d->cache, SDL_BLENDMODE_BLEND);
113 d->cacheRowHeight = 0;
114 }
115 /* Load the fonts. */ {
116 const struct { const iBlock *ttf; int size; } fontData[max_FontId] = {
117 { &fontFiraSansRegular_Embedded, fontSize_UI },
118 { &fontFiraSansRegular_Embedded, fontSize_UI },
119 { &fontFiraMonoRegular_Embedded, fontSize_UI },
120 { &fontFiraSansRegular_Embedded, fontSize_UI },
121 { &fontFiraSansRegular_Embedded, fontSize_UI * 1.5f },
122 { &fontFiraMonoRegular_Embedded, fontSize_UI },
123 { &fontFiraSansLightItalic_Embedded, fontSize_UI },
124 { &fontFiraSansRegular_Embedded, fontSize_UI * 2.5f },
125 { &fontFiraSansRegular_Embedded, fontSize_UI * 2.0f },
126 { &fontFiraSansRegular_Embedded, fontSize_UI * 1.5f },
127 };
128 iForIndices(i, fontData) {
129 init_Font(&d->fonts[i], fontData[i].ttf, fontData[i].size);
130 }
131 }
132}
133
134void deinit_Text(void) {
135 iText *d = &text_;
136 SDL_FreePalette(d->grayscale);
137 iForIndices(i, d->fonts) {
138 deinit_Font(&d->fonts[i]);
139 }
140 SDL_DestroyTexture(d->cache);
141 d->render = NULL;
142}
143
144static SDL_Surface *rasterizeGlyph_Font_(const iFont *d, iChar ch) {
145 int w, h;
146 uint8_t *bmp = stbtt_GetCodepointBitmap(&d->font, d->scale, d->scale, ch, &w, &h, 0, 0);
147 /* Note: `bmp` must be freed afterwards. */
148 SDL_Surface *surface =
149 SDL_CreateRGBSurfaceWithFormatFrom(bmp, w, h, 8, w, SDL_PIXELFORMAT_INDEX8);
150 SDL_SetSurfacePalette(surface, text_.grayscale);
151 return surface;
152}
153
154static iBool isSpecialChar_(iChar ch) {
155 return ch >= specialSymbol_Text && ch < 0x20;
156}
157
158static float symbolEmWidth_(int symbol) {
159 return 1.5f;
160}
161
162static float symbolAdvance_(int symbol) {
163 return 1.5f;
164}
165
166static int specialChar_(iChar ch) {
167 return ch - specialSymbol_Text;
168}
169
170iLocalDef SDL_Rect sdlRect_(const iRect rect) {
171 return (SDL_Rect){ rect.pos.x, rect.pos.y, rect.size.x, rect.size.y };
172}
173
174static void fillTriangle_(SDL_Surface *surface, const SDL_Rect *rect, int dir) {
175 const uint32_t color = 0xffffffff;
176 SDL_LockSurface(surface);
177 uint32_t *row = surface->pixels;
178 row += rect->x + rect->y * surface->pitch / 4;
179 for (int y = 0; y < rect->h; y++, row += surface->pitch / 4) {
180 float norm = (float) y / (float) (rect->h - 1) * 2.0f;
181 if (norm > 1.0f) norm = 2.0f - norm;
182 const int len = norm * rect->w;
183 const float fract = norm * rect->w - len;
184 const uint32_t fractColor = 0xffffff00 | (int) (fract * 0xff);
185 if (dir > 0) {
186 for (int x = 0; x < len; x++) {
187 row[x] = color;
188 }
189 if (len < rect->w) {
190 row[len] = fractColor;
191 }
192 }
193 else {
194 for (int x = 0; x < len; x++) {
195 row[rect->w - len + x] = color;
196 }
197 if (len < rect->w) {
198 row[rect->w - len - 1] = fractColor;
199 }
200 }
201 }
202 SDL_UnlockSurface(surface);
203}
204
205static void cache_Font_(iFont *d, iGlyph *glyph) {
206 iText *txt = &text_;
207 SDL_Renderer *render = txt->render;
208 SDL_Texture *tex = NULL;
209 SDL_Surface *surface = NULL;
210 const iChar ch = char_Glyph(glyph);
211 iBool fromStb = iFalse;
212 if (!isSpecialChar_(ch)) {
213 /* Rasterize the glyph using stbtt. */
214 surface = rasterizeGlyph_Font_(d, ch);
215 stbtt_GetCodepointBitmapBox(
216 &d->font, ch, d->scale, d->scale, &glyph->dx, &glyph->dy, NULL, NULL);
217 fromStb = iTrue;
218 tex = SDL_CreateTextureFromSurface(render, surface);
219 glyph->rect.size = init_I2(surface->w, surface->h);
220 }
221 else {
222 /* Metrics for special symbols. */
223 int em, lsb;
224 const int symbol = specialChar_(ch);
225 stbtt_GetCodepointHMetrics(&d->font, 'M', &em, &lsb);
226 glyph->dx = d->baseline / 10;
227 glyph->dy = -d->baseline;
228 glyph->rect.size = init_I2(symbolEmWidth_(symbol) * em * d->scale, d->height);
229#if 0
230 if (isRasterizedSymbol_(ch)) {
231 /* Rasterize manually. */
232 surface = SDL_CreateRGBSurfaceWithFormat(
233 0, width_Rect(glyph->rect), height_Rect(glyph->rect), 32, SDL_PIXELFORMAT_RGBA8888);
234 SDL_FillRect(surface, NULL, 0);
235 const uint32_t white = 0xffffffff;
236 switch (specialChar_(ch)) {
237 case play_SpecialSymbol:
238 fillTriangle_(surface, &(SDL_Rect){ 0, 0, surface->w, d->baseline }, 1);
239 break;
240 case pause_SpecialSymbol: {
241 const int w = surface->w * 4 / 11;
242 SDL_FillRect(surface, &(SDL_Rect){ 0, 0, w, d->baseline }, white);
243 SDL_FillRect(surface, &(SDL_Rect){ surface->w - w, 0, w, d->baseline }, white);
244 break;
245 }
246 case rewind_SpecialSymbol: {
247 const int w1 = surface->w / 7;
248 const int w2 = surface->w * 3 / 7;
249 const int h = d->baseline * 4 / 5;
250 const int off = (d->baseline - h) / 2;
251 SDL_FillRect(surface, &(SDL_Rect){ 0, off, w1, h}, white);
252 fillTriangle_(surface, &(SDL_Rect){ w1, off, w2, h }, -1);
253 fillTriangle_(surface, &(SDL_Rect){ surface->w * 4 / 7, off, w2, h }, -1);
254 break;
255 }
256 }
257 tex = SDL_CreateTextureFromSurface(render, surface);
258 }
259#endif
260 }
261 /* Determine placement in the glyph cache texture, advancing in rows. */
262 if (txt->cachePos.x + glyph->rect.size.x > txt->cacheSize.x) {
263 txt->cachePos.x = 0;
264 txt->cachePos.y += txt->cacheRowHeight;
265 txt->cacheRowHeight = 0;
266 }
267 glyph->rect.pos = txt->cachePos;
268 SDL_SetRenderTarget(render, txt->cache);
269 const SDL_Rect dstRect = sdlRect_(glyph->rect);
270 if (surface) {
271 SDL_RenderCopy(render, tex, &(SDL_Rect){ 0, 0, dstRect.w, dstRect.h }, &dstRect);
272 }
273 else {
274 /* Draw a special symbol. */
275 SDL_SetRenderDrawColor(render, 255, 255, 255, 255);
276 const iInt2 tl = init_I2(dstRect.x, dstRect.y);
277 const iInt2 br = init_I2(dstRect.x + dstRect.w - 1, dstRect.y + dstRect.h - 1);
278 const int midX = tl.x + dstRect.w / 2;
279 const int midY = tl.y + dstRect.h / 2;
280 const int symH = dstRect.h * 2 / 6;
281#if 0
282 /* Frame. */
283 if (isFramedSymbol_(ch)) {
284 SDL_RenderDrawLines(
285 render,
286 (SDL_Point[]){
287 { tl.x, tl.y }, { br.x, tl.y }, { br.x, br.y }, { tl.x, br.y }, { tl.x, tl.y } },
288 5);
289 }
290 iArray points;
291 init_Array(&points, sizeof(SDL_Point));
292 switch (specialChar_(ch)) {
293 case 0: /* silence */
294 break;
295 case 1: /* sine */
296 for (int i = 0; i < dstRect.w; ++i) {
297 float rad = 2.0f * iMathPif * (float) i / dstRect.w;
298 SDL_Point pt = { tl.x + i, midY + sin(rad) * symH};
299 pushBack_Array(&points, &pt);
300 }
301 SDL_RenderDrawLines(render, constData_Array(&points), size_Array(&points));
302 break;
303 case 2: /* square */
304 SDL_RenderDrawLines(render,
305 (SDL_Point[]){ { tl.x, midY - symH },
306 { midX, midY - symH },
307 { midX, midY + symH },
308 { br.x, midY + symH } },
309 4);
310 break;
311 case 3: /* saw */
312 SDL_RenderDrawLines(render,
313 (SDL_Point[]){ { tl.x, midY },
314 { midX, midY - symH },
315 { midX, midY + symH },
316 { br.x, midY } },
317 4);
318 break;
319 case 4: /* triangle */
320 SDL_RenderDrawLines(render,
321 (SDL_Point[]){ { tl.x, midY },
322 { tl.x + dstRect.w / 4, midY - symH },
323 { br.x - dstRect.w / 4, midY + symH },
324 { br.x, midY } },
325 4);
326 break;
327 case 5: /* noise */
328 for (int i = 0; i < dstRect.w; ++i) {
329 for (int p = 0; p < 2; ++p) {
330 const float val = iRandomf() * 2.0f - 1.0f;
331 pushBack_Array(&points, &(SDL_Point){ tl.x + i, midY - val * symH });
332 }
333 }
334 SDL_RenderDrawPoints(render, constData_Array(&points), size_Array(&points));
335 break;
336 }
337 deinit_Array(&points);
338#endif
339 }
340 SDL_SetRenderTarget(render, NULL);
341 if (tex) {
342 SDL_DestroyTexture(tex);
343 iAssert(surface);
344 if (fromStb) stbtt_FreeBitmap(surface->pixels, NULL);
345 SDL_FreeSurface(surface);
346 }
347 /* Update cache cursor. */
348 txt->cachePos.x += glyph->rect.size.x;
349 txt->cacheRowHeight = iMax(txt->cacheRowHeight, glyph->rect.size.y);
350}
351
352static const iGlyph *glyph_Font_(iFont *d, iChar ch) {
353 const void *node = value_Hash(&d->glyphs, ch);
354 if (node) {
355 return node;
356 }
357 iGlyph *glyph = new_Glyph(ch);
358 cache_Font_(d, glyph);
359 insert_Hash(&d->glyphs, &glyph->node);
360 return glyph;
361}
362
363enum iRunMode { measure_RunMode, draw_RunMode, drawPermanentColor_RunMode };
364
365static iInt2 run_Font_(iFont *d, enum iRunMode mode, const char *text, size_t maxLen, iInt2 pos,
366 int *runAdvance_out) {
367 iInt2 size = zero_I2();
368 const iInt2 orig = pos;
369 const stbtt_fontinfo *info = &d->font;
370 float xpos = pos.x;
371 float xposMax = xpos;
372 const iString textStr = iStringLiteral(text);
373 iConstForEach(String, i, &textStr) {
374 iChar ch = i.value;
375 /* Special instructions. */ {
376 if (ch == '\n') {
377 xpos = pos.x;
378 pos.y += d->height;
379 continue;
380 }
381 if (ch == '\r') {
382 next_StringConstIterator(&i);
383 const iColor clr = get_Color(i.value - '0');
384 if (mode == draw_RunMode) {
385 SDL_SetTextureColorMod(text_.cache, clr.r, clr.g, clr.b);
386 }
387 continue;
388 }
389 }
390 const iGlyph *glyph = glyph_Font_(d, ch);
391 int x1 = iRound(xpos);
392 int x2 = x1 + glyph->rect.size.x;
393 size.x = iMax(size.x, x2 - orig.x);
394 size.y = iMax(size.y, pos.y + d->height - orig.y);
395 if (mode != measure_RunMode) {
396 SDL_Rect dst = { x1 + glyph->dx,
397 pos.y + d->baseline + glyph->dy,
398 glyph->rect.size.x,
399 glyph->rect.size.y };
400 SDL_RenderCopy(text_.render, text_.cache, (const SDL_Rect *) &glyph->rect, &dst);
401 }
402 int advance, lsb;
403 const iBool spec = isSpecialChar_(ch);
404 stbtt_GetCodepointHMetrics(info, spec ? 'M' : ch, &advance, &lsb);
405 if (spec) {
406 advance *= symbolAdvance_(specialChar_(ch));
407 }
408 xpos += d->scale * advance;
409 xposMax = iMax(xposMax, xpos);
410 /* Check the next character. */ {
411 iStringConstIterator j = i;
412 next_StringConstIterator(&j);
413 const iChar next = j.value;
414 if (next) {
415 xpos += d->scale * stbtt_GetCodepointKernAdvance(info, ch, next);
416 }
417 }
418 if (--maxLen == 0) {
419 break;
420 }
421 }
422 if (runAdvance_out) {
423 *runAdvance_out = xposMax - orig.x;
424 }
425 return size;
426}
427
428int lineHeight_Text(int fontId) {
429 return text_.fonts[fontId].height;
430}
431
432iInt2 measure_Text(int fontId, const char *text) {
433 if (!*text) {
434 return init_I2(0, lineHeight_Text(fontId));
435 }
436 return run_Font_(&text_.fonts[fontId], measure_RunMode, text, iInvalidSize, zero_I2(), NULL);
437}
438
439iInt2 advance_Text(int fontId, const char *text) {
440 int advance;
441 const int height =
442 run_Font_(&text_.fonts[fontId], measure_RunMode, text, iInvalidSize, zero_I2(), &advance).y;
443 return init_I2(advance, height); //lineHeight_Text(fontId));
444}
445
446iInt2 advanceN_Text(int fontId, const char *text, size_t n) {
447 if (n == 0) {
448 return init_I2(0, lineHeight_Text(fontId));
449 }
450 int advance;
451 run_Font_(&text_.fonts[fontId], measure_RunMode, text, n, zero_I2(), &advance);
452 return init_I2(advance, lineHeight_Text(fontId));
453}
454
455static void draw_Text_(int fontId, iInt2 pos, int color, const char *text) {
456 iText *d = &text_;
457 const iColor clr = get_Color(color & mask_ColorId);
458 SDL_SetTextureColorMod(d->cache, clr.r, clr.g, clr.b);
459 run_Font_(&d->fonts[fontId],
460 color & permanent_ColorId ? drawPermanentColor_RunMode : draw_RunMode,
461 text,
462 iInvalidSize,
463 pos,
464 NULL);
465}
466
467void draw_Text(int fontId, iInt2 pos, int color, const char *text, ...) {
468 iBlock chars;
469 init_Block(&chars, 0); {
470 va_list args;
471 va_start(args, text);
472 vprintf_Block(&chars, text, args);
473 va_end(args);
474 }
475 if (pos.x < 0) {
476 /* Right-aligned. */
477 pos.x = -pos.x - measure_Text(fontId, cstr_Block(&chars)).x;
478 }
479 if (pos.y < 0) {
480 /* Bottom-aligned. */
481 pos.y = -pos.y - lineHeight_Text(fontId);
482 }
483 draw_Text_(fontId, pos, color, cstr_Block(&chars));
484 deinit_Block(&chars);
485}
486
487void drawCentered_Text(int fontId, iRect rect, int color, const char *text, ...) {
488 iBlock chars;
489 init_Block(&chars, 0); {
490 va_list args;
491 va_start(args, text);
492 vprintf_Block(&chars, text, args);
493 va_end(args);
494 }
495 const iInt2 textSize = advance_Text(fontId, cstr_Block(&chars));
496 draw_Text_(fontId, sub_I2(mid_Rect(rect), divi_I2(textSize, 2)), color, cstr_Block(&chars));
497 deinit_Block(&chars);
498}
499
500SDL_Texture *glyphCache_Text(void) {
501 return text_.cache;
502}
503
504/*-----------------------------------------------------------------------------------------------*/
505
506iDefineTypeConstructionArgs(TextBuf, (int font, const char *text), font, text)
507
508void init_TextBuf(iTextBuf *d, int font, const char *text) {
509 SDL_Renderer *render = text_.render;
510 d->size = advance_Text(font, text);
511 d->texture = SDL_CreateTexture(render,
512 SDL_PIXELFORMAT_RGBA8888,
513 SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET,
514 d->size.x,
515 d->size.y);
516 SDL_SetTextureBlendMode(d->texture, SDL_BLENDMODE_BLEND);
517 SDL_SetRenderTarget(render, d->texture);
518 draw_Text_(font, zero_I2(), white_ColorId, text);
519 SDL_SetRenderTarget(render, NULL);
520}
521
522void deinit_TextBuf(iTextBuf *d) {
523 SDL_DestroyTexture(d->texture);
524}
525
526void draw_TextBuf(const iTextBuf *d, iInt2 pos, int color) {
527 const iColor clr = get_Color(color);
528 SDL_SetTextureColorMod(d->texture, clr.r, clr.g, clr.b);
529 SDL_RenderCopy(text_.render,
530 d->texture,
531 &(SDL_Rect){ 0, 0, d->size.x, d->size.y },
532 &(SDL_Rect){ pos.x, pos.y, d->size.x, d->size.y });
533}
diff --git a/src/ui/text.h b/src/ui/text.h
new file mode 100644
index 00000000..b689582e
--- /dev/null
+++ b/src/ui/text.h
@@ -0,0 +1,52 @@
1#pragma once
2
3#include <the_Foundation/rect.h>
4#include <the_Foundation/string.h>
5
6#include <SDL_render.h>
7
8enum iFontId {
9 default_FontId,
10 uiShortcuts_FontId,
11 uiInput_FontId,
12 /* Document fonts: */
13 paragraph_FontId,
14 firstParagraph_FontId,
15 preformatted_FontId,
16 quote_FontId,
17 header1_FontId,
18 header2_FontId,
19 header3_FontId,
20 max_FontId
21};
22
23#define specialSymbol_Text 0x10
24
25enum iSpecialSymbol {
26 silence_SpecialSymbol,
27};
28
29void init_Text (SDL_Renderer *);
30void deinit_Text (void);
31
32int lineHeight_Text (int font);
33iInt2 measure_Text (int font, const char *text);
34iInt2 advance_Text (int font, const char *text);
35iInt2 advanceN_Text (int font, const char *text, size_t n);
36
37void draw_Text (int font, iInt2 pos, int color, const char *text, ...); /* negative pos to switch alignment */
38void drawCentered_Text (int font, iRect rect, int color, const char *text, ...);
39
40SDL_Texture * glyphCache_Text (void);
41
42/*-----------------------------------------------------------------------------------------------*/
43
44iDeclareType(TextBuf)
45iDeclareTypeConstructionArgs(TextBuf, int font, const char *text)
46
47struct Impl_TextBuf {
48 SDL_Texture *texture;
49 iInt2 size;
50};
51
52void draw_TextBuf (const iTextBuf *, iInt2 pos, int color);
diff --git a/src/ui/util.c b/src/ui/util.c
new file mode 100644
index 00000000..9487e004
--- /dev/null
+++ b/src/ui/util.c
@@ -0,0 +1,604 @@
1#include "util.h"
2
3#include "app.h"
4#include "color.h"
5#include "command.h"
6#include "labelwidget.h"
7#include "inputwidget.h"
8#include "widget.h"
9#include "text.h"
10#include "window.h"
11
12#include <the_Foundation/math.h>
13#include <the_Foundation/path.h>
14
15iBool isCommand_UserEvent(const SDL_Event *d, const char *cmd) {
16 return d->type == SDL_USEREVENT && d->user.code == command_UserEventCode &&
17 equal_Command(d->user.data1, cmd);
18}
19
20const char *command_UserEvent(const SDL_Event *d) {
21 if (d->type == SDL_USEREVENT && d->user.code == command_UserEventCode) {
22 return d->user.data1;
23 }
24 return "";
25}
26
27int keyMods_Sym(int kmods) {
28 kmods &= (KMOD_SHIFT | KMOD_ALT | KMOD_CTRL | KMOD_GUI);
29 /* Don't treat left/right modifiers differently. */
30 if (kmods & KMOD_SHIFT) kmods |= KMOD_SHIFT;
31 if (kmods & KMOD_ALT) kmods |= KMOD_ALT;
32 if (kmods & KMOD_CTRL) kmods |= KMOD_CTRL;
33 if (kmods & KMOD_GUI) kmods |= KMOD_GUI;
34 return kmods;
35}
36
37/*-----------------------------------------------------------------------------------------------*/
38
39void init_Click(iClick *d, iAnyObject *widget, int button) {
40 d->isActive = iFalse;
41 d->button = button;
42 d->bounds = as_Widget(widget);
43 d->startPos = zero_I2();
44 d->pos = zero_I2();
45}
46
47enum iClickResult processEvent_Click(iClick *d, const SDL_Event *event) {
48 if (event->type == SDL_MOUSEMOTION) {
49 const iInt2 pos = init_I2(event->motion.x, event->motion.y);
50 if (d->isActive) {
51 d->pos = pos;
52 return drag_ClickResult;
53 }
54 }
55 if (event->type != SDL_MOUSEBUTTONDOWN && event->type != SDL_MOUSEBUTTONUP) {
56 return none_ClickResult;
57 }
58 const SDL_MouseButtonEvent *mb = &event->button;
59 if (mb->button != d->button) {
60 return none_ClickResult;
61 }
62 const iInt2 pos = init_I2(mb->x, mb->y);
63 if (event->type == SDL_MOUSEBUTTONDOWN && mb->clicks == 2) {
64 if (contains_Widget(d->bounds, pos)) {
65 d->pos = pos;
66 setMouseGrab_Widget(NULL);
67 return double_ClickResult;
68 }
69 }
70 if (!d->isActive) {
71 if (mb->state == SDL_PRESSED) {
72 if (contains_Widget(d->bounds, pos)) {
73 d->isActive = iTrue;
74 d->startPos = d->pos = pos;
75 //setFlags_Widget(d->bounds, hover_WidgetFlag, iFalse);
76 setMouseGrab_Widget(d->bounds);
77 return started_ClickResult;
78 }
79 }
80 }
81 else { /* Active. */
82 if (mb->state == SDL_RELEASED) {
83 enum iClickResult result = contains_Widget(d->bounds, pos)
84 ? finished_ClickResult
85 : aborted_ClickResult;
86 d->isActive = iFalse;
87 d->pos = pos;
88 setMouseGrab_Widget(NULL);
89 return result;
90 }
91 }
92 return none_ClickResult;
93}
94
95void cancel_Click(iClick *d) {
96 if (d->isActive) {
97 d->isActive = iFalse;
98 setMouseGrab_Widget(NULL);
99 }
100}
101
102iBool isMoved_Click(const iClick *d) {
103 return dist_I2(d->startPos, d->pos) > 2;
104}
105
106iInt2 pos_Click(const iClick *d) {
107 return d->pos;
108}
109
110iRect rect_Click(const iClick *d) {
111 return initCorners_Rect(min_I2(d->startPos, d->pos), max_I2(d->startPos, d->pos));
112}
113
114iInt2 delta_Click(const iClick *d) {
115 return sub_I2(d->pos, d->startPos);
116}
117
118/*-----------------------------------------------------------------------------------------------*/
119
120iWidget *makePadding_Widget(int size) {
121 iWidget *pad = new_Widget();
122 setSize_Widget(pad, init1_I2(size));
123 return pad;
124}
125
126iLabelWidget *makeHeading_Widget(const char *text) {
127 iLabelWidget *heading = new_LabelWidget(text, 0, 0, NULL);
128 setFlags_Widget(as_Widget(heading), frameless_WidgetFlag | fixedSize_WidgetFlag, iTrue);
129 return heading;
130}
131
132iWidget *makeVDiv_Widget(void) {
133 iWidget *div = new_Widget();
134 setFlags_Widget(div, resizeChildren_WidgetFlag | arrangeVertical_WidgetFlag, iTrue);
135 return div;
136}
137
138iWidget *makeHDiv_Widget(void) {
139 iWidget *div = new_Widget();
140 setFlags_Widget(div, resizeChildren_WidgetFlag | arrangeHorizontal_WidgetFlag, iTrue);
141 return div;
142}
143
144iWidget *addAction_Widget(iWidget *parent, int key, int kmods, const char *command) {
145 iLabelWidget *action = new_LabelWidget("", key, kmods, command);
146 setSize_Widget(as_Widget(action), zero_I2());
147 addChildFlags_Widget(parent, iClob(action), hidden_WidgetFlag);
148 return as_Widget(action);
149}
150
151/*-----------------------------------------------------------------------------------------------*/
152
153static iBool menuHandler_(iWidget *menu, const char *cmd) {
154 if (isVisible_Widget(menu)) {
155 if (equal_Command(cmd, "menu.open") && pointer_Command(cmd) == menu->parent) {
156 /* Don't reopen self; instead, root will close the menu. */
157 return iFalse;
158 }
159 if (!equal_Command(cmd, "window.resized")) {
160 closeMenu_Widget(menu);
161 }
162 }
163 return iFalse;
164}
165
166iWidget *makeMenu_Widget(iWidget *parent, const iMenuItem *items, size_t n) {
167 iWidget *menu = new_Widget();
168 setBackgroundColor_Widget(menu, gray25_ColorId);
169 setFlags_Widget(menu,
170 keepOnTop_WidgetFlag | hidden_WidgetFlag | arrangeVertical_WidgetFlag |
171 arrangeSize_WidgetFlag | resizeChildrenToWidestChild_WidgetFlag,
172 iTrue);
173 for (size_t i = 0; i < n; ++i) {
174 const iMenuItem *item = &items[i];
175 if (equal_CStr(item->label, "---")) {
176 iWidget *sep = addChild_Widget(menu, iClob(new_Widget()));
177 setBackgroundColor_Widget(sep, gray50_ColorId);
178 sep->rect.size.y = gap_UI / 3;
179 setFlags_Widget(sep, hover_WidgetFlag | fixedHeight_WidgetFlag, iTrue);
180 }
181 else {
182 iLabelWidget *label = addChildFlags_Widget(
183 menu,
184 iClob(new_LabelWidget(item->label, item->key, item->kmods, item->command)),
185 frameless_WidgetFlag | alignLeft_WidgetFlag | drawKey_WidgetFlag);
186 updateSize_LabelWidget(label); /* drawKey was set */
187 }
188 }
189 addChild_Widget(parent, iClob(menu));
190 setCommandHandler_Widget(menu, menuHandler_);
191 addAction_Widget(menu, SDLK_ESCAPE, 0, "cancel");
192 return menu;
193}
194
195void openMenu_Widget(iWidget *d, iInt2 coord) {
196 /* Menu closes when commands are emitted, so handle any pending ones beforehand. */
197 processEvents_App();
198 setFlags_Widget(d, hidden_WidgetFlag, iFalse);
199 arrange_Widget(d);
200 d->rect.pos = coord;
201 /* Ensure the full menu is visible. */
202 const iInt2 rootSize = rootSize_Window(get_Window());
203 const int bottomExcess = bottom_Rect(bounds_Widget(d)) - rootSize.y;
204 if (bottomExcess > 0) {
205 d->rect.pos.y -= bottomExcess;
206 }
207 if (top_Rect(d->rect) < 0) {
208 d->rect.pos.y += -top_Rect(d->rect);
209 }
210 if (right_Rect(bounds_Widget(d)) > rootSize.x) {
211 d->rect.pos.x = coord.x - d->rect.size.x;
212 }
213 if (left_Rect(d->rect) < 0) {
214 d->rect.pos.x = 0;
215 }
216}
217
218void closeMenu_Widget(iWidget *d) {
219 setFlags_Widget(d, hidden_WidgetFlag, iTrue);
220}
221
222iLabelWidget *makeMenuButton_LabelWidget(const char *label, const iMenuItem *items, size_t n) {
223 iLabelWidget *button = new_LabelWidget(label, 0, 0, "menu.open");
224 iWidget *menu = makeMenu_Widget(as_Widget(button), items, n);
225 setId_Widget(menu, "menu");
226 return button;
227}
228
229/*-----------------------------------------------------------------------------------------------*/
230
231static iBool isTabPage_Widget_(const iWidget *tabs, const iWidget *page) {
232 return page->parent == findChild_Widget(tabs, "tabs.pages");
233}
234
235static iBool tabSwitcher_(iWidget *tabs, const char *cmd) {
236 if (equal_Command(cmd, "tabs.switch")) {
237 iWidget *target = pointerLabel_Command(cmd, "page");
238 if (!target) {
239 const iString *id = string_Command(cmd, "id");
240 target = findChild_Widget(tabs, cstr_String(id));
241 }
242 if (!target) return iFalse;
243 if (flags_Widget(target) & focusable_WidgetFlag) {
244 setFocus_Widget(target);
245 }
246 if (isTabPage_Widget_(tabs, target)) {
247 showTabPage_Widget(tabs, target);
248 return iTrue;
249 }
250 else if (hasParent_Widget(target, tabs)) {
251 /* Some widget on a page. */
252 while (!isTabPage_Widget_(tabs, target)) {
253 target = target->parent;
254 }
255 showTabPage_Widget(tabs, target);
256 return iTrue;
257 }
258 }
259 else if (equal_Command(cmd, "tabs.next") || equal_Command(cmd, "tabs.prev")) {
260 iWidget *pages = findChild_Widget(tabs, "tabs.pages");
261 int tabIndex = 0;
262 iConstForEach(ObjectList, i, pages->children) {
263 const iWidget *child = constAs_Widget(i.object);
264 if (isVisible_Widget(child)) break;
265 tabIndex++;
266 }
267 tabIndex += (equal_Command(cmd, "tabs.next") ? +1 : -1);
268 showTabPage_Widget(tabs, child_Widget(pages, iWrap(tabIndex, 0, childCount_Widget(pages))));
269 return iTrue;
270 }
271 return iFalse;
272}
273
274iWidget *makeTabs_Widget(iWidget *parent) {
275 iWidget *tabs = makeVDiv_Widget();
276 iWidget *buttons = addChild_Widget(tabs, iClob(new_Widget()));
277 setFlags_Widget(buttons, arrangeHorizontal_WidgetFlag | arrangeHeight_WidgetFlag, iTrue);
278 setId_Widget(buttons, "tabs.buttons");
279 iWidget *pages = addChildFlags_Widget(
280 tabs, iClob(new_Widget()), expand_WidgetFlag | resizeChildren_WidgetFlag);
281 setId_Widget(pages, "tabs.pages");
282 addChild_Widget(parent, iClob(tabs));
283 setCommandHandler_Widget(tabs, tabSwitcher_);
284 return tabs;
285}
286
287static void addTabPage_Widget_(iWidget *tabs, enum iWidgetAddPos addPos, iWidget *page,
288 const char *label, int key, int kmods) {
289 iWidget * pages = findChild_Widget(tabs, "tabs.pages");
290 const iBool isSel = childCount_Widget(pages) == 0;
291 iWidget * button = addChildPos_Widget(
292 findChild_Widget(tabs, "tabs.buttons"),
293 iClob(new_LabelWidget(label, key, kmods, cstrFormat_String("tabs.switch page:%p", page))),
294 addPos);
295 setFlags_Widget(button, selected_WidgetFlag, isSel);
296 addChildPos_Widget(pages, page, addPos);
297 setFlags_Widget(page, hidden_WidgetFlag | disabled_WidgetFlag, !isSel);
298}
299
300void appendTabPage_Widget(iWidget *tabs, iWidget *page, const char *label, int key, int kmods) {
301 addTabPage_Widget_(tabs, back_WidgetAddPos, page, label, key, kmods);
302}
303
304void prependTabPage_Widget(iWidget *tabs, iWidget *page, const char *label, int key, int kmods) {
305 addTabPage_Widget_(tabs, front_WidgetAddPos, page, label, key, kmods);
306}
307
308iWidget *tabPage_Widget(iWidget *tabs, size_t index) {
309 iWidget *pages = findChild_Widget(tabs, "tabs.pages");
310 return child_Widget(pages, index);
311}
312
313iWidget *removeTabPage_Widget(iWidget *tabs, size_t index) {
314 iWidget *buttons = findChild_Widget(tabs, "tabs.buttons");
315 iWidget *pages = findChild_Widget(tabs, "tabs.pages");
316 iWidget *button = removeChild_Widget(buttons, child_Widget(buttons, index));
317 iRelease(button);
318 iWidget *page = child_Widget(pages, index);
319 ref_Object(page);
320 setFlags_Widget(page, hidden_WidgetFlag | disabled_WidgetFlag, iFalse);
321 removeChild_Widget(pages, page);
322 return page;
323}
324
325void showTabPage_Widget(iWidget *tabs, const iWidget *page) {
326 /* Select the corresponding button. */ {
327 iWidget *buttons = findChild_Widget(tabs, "tabs.buttons");
328 iForEach(ObjectList, i, buttons->children) {
329 iAssert(isInstance_Object(i.object, &Class_LabelWidget));
330 iAny *label = i.object;
331 const iBool isSel =
332 (pointerLabel_Command(cstr_String(command_LabelWidget(label)), "page") == page);
333 setFlags_Widget(label, selected_WidgetFlag, isSel);
334 }
335 }
336 /* Show/hide pages. */ {
337 iWidget *pages = findChild_Widget(tabs, "tabs.pages");
338 iForEach(ObjectList, i, pages->children) {
339 iWidget *child = as_Widget(i.object);
340 setFlags_Widget(child, hidden_WidgetFlag | disabled_WidgetFlag, child != page);
341 }
342 }
343 /* Notify. */
344 if (!isEmpty_String(id_Widget(page))) {
345 postCommandf_App("tabs.changed id:%s", cstr_String(id_Widget(page)));
346 }
347}
348
349const iWidget *currentTabPage_Widget(const iWidget *tabs) {
350 iWidget *pages = findChild_Widget(tabs, "tabs.pages");
351 iConstForEach(ObjectList, i, pages->children) {
352 if (isVisible_Widget(constAs_Widget(i.object))) {
353 return constAs_Widget(i.object);
354 }
355 }
356 return NULL;
357}
358
359size_t tabCount_Widget(const iWidget *tabs) {
360 return childCount_Widget(findChild_Widget(tabs, "tabs.buttons"));
361}
362
363/*-----------------------------------------------------------------------------------------------*/
364
365static void acceptFilePath_(iWidget *dlg) {
366 iInputWidget *input = findChild_Widget(dlg, "input");
367 iString *path = makeAbsolute_Path(text_InputWidget(input));
368 postCommandf_App("%s path:%s", cstr_String(id_Widget(dlg)), cstr_String(path));
369 destroy_Widget(dlg);
370 delete_String(path);
371}
372
373iBool filePathHandler_(iWidget *dlg, const char *cmd) {
374 iWidget *ptr = as_Widget(pointer_Command(cmd));
375 if (equal_Command(cmd, "input.ended")) {
376 if (hasParent_Widget(ptr, dlg)) {
377 if (arg_Command(cmd)) {
378 acceptFilePath_(dlg);
379 }
380 else {
381 destroy_Widget(dlg);
382 }
383 return iTrue;
384 }
385 return iFalse;
386 }
387 else if (ptr && !hasParent_Widget(ptr, dlg)) {
388 /* Command from outside the dialog, so dismiss the dialog. */
389 if (!equal_Command(cmd, "focus.lost")) {
390 destroy_Widget(dlg);
391 }
392 return iFalse;
393 }
394 else if (equal_Command(cmd, "filepath.cancel")) {
395 end_InputWidget(findChild_Widget(dlg, "input"), iFalse);
396 destroy_Widget(dlg);
397 return iTrue;
398 }
399 else if (equal_Command(cmd, "filepath.accept")) {
400 acceptFilePath_(dlg);
401 return iTrue;
402 }
403 return iFalse;
404}
405
406iWidget *makeSheet_Widget(const char *id) {
407 iWidget *sheet = new_Widget();
408 setId_Widget(sheet, id);
409 setBackgroundColor_Widget(sheet, gray25_ColorId);
410 setFlags_Widget(sheet,
411 keepOnTop_WidgetFlag | arrangeVertical_WidgetFlag |
412 arrangeHeight_WidgetFlag,
413 iTrue);
414 const iInt2 rootSize = rootSize_Window(get_Window());
415 setSize_Widget(sheet, init_I2(rootSize.x / 2, 0));
416 setFlags_Widget(sheet, fixedHeight_WidgetFlag, iFalse);
417 return sheet;
418}
419
420void centerSheet_Widget(iWidget *sheet) {
421 arrange_Widget(sheet);
422 const iInt2 rootSize = rootSize_Window(get_Window());
423 sheet->rect.pos.x = rootSize.x / 2 - sheet->rect.size.x / 2;
424}
425
426void makeFilePath_Widget(iWidget * parent,
427 const iString *initialPath,
428 const char * title,
429 const char * acceptLabel,
430 const char * command) {
431 setFocus_Widget(NULL);
432 processEvents_App();
433 iWidget *dlg = makeSheet_Widget(command);
434 setCommandHandler_Widget(dlg, filePathHandler_);
435 addChild_Widget(parent, iClob(dlg));
436 addChildFlags_Widget(dlg, iClob(new_LabelWidget(title, 0, 0, NULL)), frameless_WidgetFlag);
437 iInputWidget *input = addChild_Widget(dlg, iClob(new_InputWidget(0)));
438 if (initialPath) {
439 setText_InputWidget(input, collect_String(makeRelative_Path(initialPath)));
440 }
441 setId_Widget(as_Widget(input), "input");
442 as_Widget(input)->rect.size.x = dlg->rect.size.x;
443 addChild_Widget(dlg, iClob(makePadding_Widget(gap_UI)));
444 iWidget *div = new_Widget(); {
445 setFlags_Widget(div, arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag, iTrue);
446 addChild_Widget(div, iClob(new_LabelWidget("Cancel", SDLK_ESCAPE, 0, "filepath.cancel")));
447 addChild_Widget(div, iClob(new_LabelWidget(acceptLabel, SDLK_RETURN, 0, "filepath.accept")));
448 }
449 addChild_Widget(dlg, iClob(div));
450 centerSheet_Widget(dlg);
451 setFocus_Widget(as_Widget(input));
452}
453
454static void acceptValueInput_(iWidget *dlg) {
455 const iInputWidget *input = findChild_Widget(dlg, "input");
456 const iString *val = text_InputWidget(input);
457 postCommandf_App("%s arg:%d value:%s",
458 cstr_String(id_Widget(dlg)),
459 toInt_String(val),
460 cstr_String(val));
461}
462
463iBool valueInputHandler_(iWidget *dlg, const char *cmd) {
464 iWidget *ptr = as_Widget(pointer_Command(cmd));
465 if (equal_Command(cmd, "input.ended")) {
466 if (hasParent_Widget(ptr, dlg)) {
467 if (arg_Command(cmd)) {
468 acceptValueInput_(dlg);
469 }
470 destroy_Widget(dlg);
471 return iTrue;
472 }
473 return iFalse;
474 }
475 else if (equal_Command(cmd, "cancel")) {
476 destroy_Widget(dlg);
477 return iTrue;
478 }
479 else if (equal_Command(cmd, "valueinput.accept")) {
480 acceptValueInput_(dlg);
481 destroy_Widget(dlg);
482 return iTrue;
483 }
484 return iFalse;
485}
486
487iWidget *makeValueInput_Widget(iWidget *parent, const iString *initialValue, const char *title,
488 const char *prompt, const char *command) {
489 setFocus_Widget(NULL);
490 processEvents_App();
491 iWidget *dlg = makeSheet_Widget(command);
492 setCommandHandler_Widget(dlg, valueInputHandler_);
493 addChild_Widget(parent, iClob(dlg));
494 addChild_Widget(dlg, iClob(new_LabelWidget(title, 0, 0, NULL)));
495 addChild_Widget(dlg, iClob(new_LabelWidget(prompt, 0, 0, NULL)));
496 iInputWidget *input = addChild_Widget(dlg, iClob(new_InputWidget(0)));
497 if (initialValue) {
498 setText_InputWidget(input, initialValue);
499 }
500 setId_Widget(as_Widget(input), "input");
501 as_Widget(input)->rect.size.x = dlg->rect.size.x;
502 addChild_Widget(dlg, iClob(makePadding_Widget(gap_UI)));
503 iWidget *div = new_Widget(); {
504 setFlags_Widget(div, arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag, iTrue);
505 addChild_Widget(div, iClob(new_LabelWidget("Cancel", SDLK_ESCAPE, 0, "cancel")));
506 addChild_Widget(div, iClob(new_LabelWidget(cyan_ColorEscape "OK", SDLK_RETURN, 0, "valueinput.accept")));
507 }
508 addChild_Widget(dlg, iClob(div));
509 centerSheet_Widget(dlg);
510 setFocus_Widget(as_Widget(input));
511 return dlg;
512}
513
514static iBool messageHandler_(iWidget *msg, const char *cmd) {
515 /* Any command dismisses the sheet. */
516 iUnused(cmd);
517 destroy_Widget(msg);
518 return iFalse;
519}
520
521void makeMessage_Widget(const char *title, const char *msg) {
522 iWidget *dlg = makeQuestion_Widget(
523 title, msg, (const char *[]){ "Continue" }, (const char *[]){ "message.ok" }, 1);
524 addAction_Widget(dlg, SDLK_ESCAPE, 0, "message.ok");
525 addAction_Widget(dlg, SDLK_SPACE, 0, "message.ok");
526}
527
528iWidget *makeQuestion_Widget(const char *title,
529 const char *msg,
530 const char *labels[],
531 const char *commands[],
532 size_t count) {
533 processEvents_App();
534 iWidget *dlg = makeSheet_Widget("");
535 setCommandHandler_Widget(dlg, messageHandler_);
536 addChild_Widget(dlg, iClob(new_LabelWidget(title, 0, 0, NULL)));
537 addChild_Widget(dlg, iClob(new_LabelWidget(msg, 0, 0, NULL)));
538 addChild_Widget(dlg, iClob(makePadding_Widget(gap_UI)));
539 iWidget *div = new_Widget(); {
540 setFlags_Widget(div, arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag, iTrue);
541 for (size_t i = 0; i < count; ++i) {
542 /* The last one is the default option. */
543 const int key = (i == count - 1 ? SDLK_RETURN : 0);
544 addChild_Widget(div, iClob(new_LabelWidget(labels[i], key, 0, commands[i])));
545 }
546 }
547 addChild_Widget(dlg, iClob(div));
548 addChild_Widget(get_Window()->root, iClob(dlg));
549 centerSheet_Widget(dlg);
550 return dlg;
551}
552
553void setToggle_Widget(iWidget *d, iBool active) {
554 setFlags_Widget(d, selected_WidgetFlag, active);
555 updateText_LabelWidget(
556 (iLabelWidget *) d,
557 collectNewFormat_String(
558 "%s", isSelected_Widget(d) ? "YES" : "NO"));
559}
560
561static iBool toggleHandler_(iWidget *d, const char *cmd) {
562 if (equal_Command(cmd, "toggle") && pointer_Command(cmd) == d) {
563 setToggle_Widget(d, (flags_Widget(d) & selected_WidgetFlag) == 0);
564 postCommand_Widget(d,
565 cstrFormat_String("%s.changed arg:%d",
566 cstr_String(id_Widget(d)),
567 isSelected_Widget(d) ? 1 : 0));
568 return iTrue;
569 }
570 return iFalse;
571}
572
573iWidget *makeToggle_Widget(const char *id) {
574 iWidget *toggle = as_Widget(new_LabelWidget("YES", 0, 0, "toggle"));
575 setId_Widget(toggle, id);
576 setCommandHandler_Widget(toggle, toggleHandler_);
577 return toggle;
578}
579
580iWidget *makePreferences_Widget(void) {
581 iWidget *dlg = makeSheet_Widget("prefs");
582 addChild_Widget(dlg, iClob(new_LabelWidget(cyan_ColorEscape "PREFERENCES", 0, 0, NULL)));
583 iWidget *page = new_Widget();
584 addChild_Widget(dlg, iClob(page));
585 setFlags_Widget(page, arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag, iTrue);
586 iWidget *headings = addChildFlags_Widget(
587 page, iClob(new_Widget()), arrangeVertical_WidgetFlag | arrangeSize_WidgetFlag);
588 iWidget *values = addChildFlags_Widget(
589 page, iClob(new_Widget()), arrangeVertical_WidgetFlag | arrangeSize_WidgetFlag);
590 addChild_Widget(headings, iClob(makeHeading_Widget("Retain window size:")));
591 addChild_Widget(values, iClob(makeToggle_Widget("prefs.retainwindow")));
592 addChild_Widget(headings, iClob(makeHeading_Widget("UI scale factor:")));
593 setId_Widget(addChild_Widget(values, iClob(new_InputWidget(8))), "prefs.uiscale");
594 arrange_Widget(dlg);
595// as_Widget(songDir)->rect.size.x = dlg->rect.size.x - headings->rect.size.x;
596 iWidget *div = new_Widget(); {
597 setFlags_Widget(div, arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag, iTrue);
598 addChild_Widget(div, iClob(new_LabelWidget("Dismiss", SDLK_ESCAPE, 0, "prefs.dismiss")));
599 }
600 addChild_Widget(dlg, iClob(div));
601 addChild_Widget(get_Window()->root, iClob(dlg));
602 centerSheet_Widget(dlg);
603 return dlg;
604}
diff --git a/src/ui/util.h b/src/ui/util.h
new file mode 100644
index 00000000..04683a2f
--- /dev/null
+++ b/src/ui/util.h
@@ -0,0 +1,110 @@
1#pragma once
2
3#include <the_Foundation/rect.h>
4#include <the_Foundation/vec2.h>
5#include <SDL_events.h>
6#include <ctype.h>
7
8iDeclareType(Click)
9iDeclareType(Widget)
10iDeclareType(LabelWidget)
11
12iBool isCommand_UserEvent (const SDL_Event *, const char *cmd);
13const char * command_UserEvent (const SDL_Event *);
14
15iLocalDef iBool isResize_UserEvent(const SDL_Event *d) {
16 return isCommand_UserEvent(d, "window.resized");
17}
18
19#if defined (iPlatformApple)
20# define KMOD_PRIMARY KMOD_GUI
21# define KMOD_SECONDARY KMOD_CTRL
22#else
23# define KMOD_PRIMARY KMOD_CTRL
24# define KMOD_SECONDARY KMOD_GUI
25#endif
26
27int keyMods_Sym (int kmods); /* shift, alt, control, or gui */
28
29/*-----------------------------------------------------------------------------------------------*/
30
31enum iClickResult {
32 none_ClickResult,
33 started_ClickResult,
34 drag_ClickResult,
35 finished_ClickResult,
36 aborted_ClickResult,
37 double_ClickResult,
38};
39
40struct Impl_Click {
41 iBool isActive;
42 int button;
43 iWidget *bounds;
44 iInt2 startPos;
45 iInt2 pos;
46};
47
48void init_Click (iClick *, iAnyObject *widget, int button);
49enum iClickResult processEvent_Click (iClick *, const SDL_Event *event);
50void cancel_Click (iClick *);
51
52iBool isMoved_Click (const iClick *);
53iInt2 pos_Click (const iClick *);
54iRect rect_Click (const iClick *);
55iInt2 delta_Click (const iClick *);
56
57/*-----------------------------------------------------------------------------------------------*/
58
59iWidget * makePadding_Widget (int size);
60iLabelWidget * makeHeading_Widget (const char *text);
61iWidget * makeHDiv_Widget (void);
62iWidget * makeVDiv_Widget (void);
63iWidget * addAction_Widget (iWidget *parent, int key, int kmods, const char *command);
64
65/*-----------------------------------------------------------------------------------------------*/
66
67iWidget * makeToggle_Widget (const char *id);
68void setToggle_Widget (iWidget *toggle, iBool active);
69
70/*-----------------------------------------------------------------------------------------------*/
71
72iDeclareType(MenuItem)
73
74struct Impl_MenuItem {
75 const char *label;
76 int key;
77 int kmods;
78 const char *command;
79};
80
81iWidget * makeMenu_Widget (iWidget *parent, const iMenuItem *items, size_t n); /* returns no ref */
82void openMenu_Widget (iWidget *, iInt2 coord);
83void closeMenu_Widget (iWidget *);
84
85iLabelWidget * makeMenuButton_LabelWidget (const char *label, const iMenuItem *items, size_t n);
86
87/*-----------------------------------------------------------------------------------------------*/
88
89iWidget * makeTabs_Widget (iWidget *parent);
90void appendTabPage_Widget (iWidget *tabs, iWidget *page, const char *label, int key, int kmods);
91void prependTabPage_Widget (iWidget *tabs, iWidget *page, const char *label, int key, int kmods);
92iWidget * tabPage_Widget (iWidget *tabs, size_t index);
93iWidget * removeTabPage_Widget (iWidget *tabs, size_t index); /* returns the page */
94void showTabPage_Widget (iWidget *tabs, const iWidget *page);
95const iWidget *currentTabPage_Widget(const iWidget *tabs);
96size_t tabCount_Widget (const iWidget *tabs);
97
98/*-----------------------------------------------------------------------------------------------*/
99
100iWidget * makeSheet_Widget (const char *id);
101void centerSheet_Widget (iWidget *sheet);
102
103void makeFilePath_Widget (iWidget *parent, const iString *initialPath, const char *title,
104 const char *acceptLabel, const char *command);
105iWidget * makeValueInput_Widget (iWidget *parent, const iString *initialValue, const char *title,
106 const char *prompt, const char *command);
107void makeMessage_Widget (const char *title, const char *msg);
108iWidget * makeQuestion_Widget (const char *title, const char *msg,
109 const char *labels[], const char *commands[], size_t count);
110iWidget * makePreferences_Widget (void);
diff --git a/src/ui/widget.c b/src/ui/widget.c
new file mode 100644
index 00000000..49fcd7c0
--- /dev/null
+++ b/src/ui/widget.c
@@ -0,0 +1,618 @@
1#include "widget.h"
2
3#include "app.h"
4#include "command.h"
5#include "paint.h"
6#include "util.h"
7#include "window.h"
8
9#include <the_Foundation/ptrset.h>
10#include <SDL_mouse.h>
11#include <stdarg.h>
12
13iDeclareType(RootData)
14
15struct Impl_RootData {
16 iWidget *hover;
17 iWidget *mouseGrab;
18 iWidget *focus;
19 iPtrSet *onTop;
20 iPtrSet *pendingDestruction;
21};
22
23static iRootData rootData_;
24
25iPtrSet *onTop_RootData_(void) {
26 if (!rootData_.onTop) {
27 rootData_.onTop = new_PtrSet();
28 }
29 return rootData_.onTop;
30}
31
32void destroyPending_Widget(void) {
33 iForEach(PtrSet, i, rootData_.pendingDestruction) {
34 iWidget *widget = *i.value;
35 remove_PtrSet(onTop_RootData_(), widget);
36 iRelease(removeChild_Widget(widget->parent, widget));
37 remove_PtrSetIterator(&i);
38 }
39}
40
41iDefineObjectConstruction(Widget)
42
43void init_Widget(iWidget *d) {
44 init_String(&d->id);
45 d->flags = 0;
46 d->rect = zero_Rect();
47 d->bgColor = none_ColorId;
48 d->children = NULL;
49 d->parent = NULL;
50 d->commandHandler = NULL;
51}
52
53void deinit_Widget(iWidget *d) {
54 iReleasePtr(&d->children);
55 deinit_String(&d->id);
56}
57
58static void aboutToBeDestroyed_Widget_(iWidget *d) {
59 if (isFocused_Widget(d)) {
60 setFocus_Widget(NULL);
61 return;
62 }
63 if (isHover_Widget(d)) {
64 rootData_.hover = NULL;
65 }
66 iForEach(ObjectList, i, d->children) {
67 aboutToBeDestroyed_Widget_(as_Widget(i.object));
68 }
69}
70
71void destroy_Widget(iWidget *d) {
72 aboutToBeDestroyed_Widget_(d);
73 if (!rootData_.pendingDestruction) {
74 rootData_.pendingDestruction = new_PtrSet();
75 }
76 insert_PtrSet(rootData_.pendingDestruction, d);
77}
78
79void setId_Widget(iWidget *d, const char *id) {
80 setCStr_String(&d->id, id);
81}
82
83const iString *id_Widget(const iWidget *d) {
84 return &d->id;
85}
86
87int flags_Widget(const iWidget *d) {
88 return d->flags;
89}
90
91void setFlags_Widget(iWidget *d, int flags, iBool set) {
92 iChangeFlags(d->flags, flags, set);
93 if (flags & keepOnTop_WidgetFlag) {
94 if (set) {
95 insert_PtrSet(onTop_RootData_(), d);
96 }
97 else {
98 remove_PtrSet(onTop_RootData_(), d);
99 }
100 }
101}
102
103void setPos_Widget(iWidget *d, iInt2 pos) {
104 d->rect.pos = pos;
105}
106
107void setSize_Widget(iWidget *d, iInt2 size) {
108 d->rect.size = size;
109 setFlags_Widget(d, fixedSize_WidgetFlag, iTrue);
110}
111
112void setBackgroundColor_Widget(iWidget *d, int bgColor) {
113 d->bgColor = bgColor;
114}
115
116void setCommandHandler_Widget(iWidget *d, iBool (*handler)(iWidget *, const char *)) {
117 d->commandHandler = handler;
118}
119
120static int numExpandingChildren_Widget_(const iWidget *d) {
121 int count = 0;
122 iConstForEach(ObjectList, i, d->children) {
123 const iWidget *child = constAs_Widget(i.object);
124 if (flags_Widget(child) & expand_WidgetFlag) {
125 count++;
126 }
127 }
128 return count;
129}
130
131static int widestChild_Widget_(const iWidget *d) {
132 int width = 0;
133 iConstForEach(ObjectList, i, d->children) {
134 const iWidget *child = constAs_Widget(i.object);
135 width = iMax(width, child->rect.size.x);
136 }
137 return width;
138}
139
140static void setWidth_Widget_(iWidget *d, int width) {
141 if (~d->flags & fixedWidth_WidgetFlag) {
142 d->rect.size.x = width;
143 }
144}
145
146static void setHeight_Widget_(iWidget *d, int height) {
147 if (~d->flags & fixedHeight_WidgetFlag) {
148 d->rect.size.y = height;
149 }
150}
151
152void arrange_Widget(iWidget *d) {
153 if (d->flags & resizeToParentWidth_WidgetFlag) {
154 setWidth_Widget_(d, d->parent->rect.size.x);
155 }
156 if (d->flags & resizeToParentHeight_WidgetFlag) {
157 setHeight_Widget_(d, d->parent->rect.size.y);
158 }
159 /* The rest of the arrangement depends on child widgets. */
160 if (!d->children) {
161 return;
162 }
163 /* Resize children to fill the parent widget. */
164 const size_t childCount = size_ObjectList(d->children);
165 if (d->flags & resizeChildren_WidgetFlag) {
166 const int expCount = numExpandingChildren_Widget_(d);
167 /* Only resize the expanding children, not touching the others. */
168 if (expCount > 0) {
169 iInt2 avail = d->rect.size;
170 iConstForEach(ObjectList, i, d->children) {
171 const iWidget *child = constAs_Widget(i.object);
172 if (~child->flags & expand_WidgetFlag) {
173 subv_I2(&avail, child->rect.size);
174 }
175 }
176 avail = divi_I2(avail, expCount);
177 iForEach(ObjectList, j, d->children) {
178 iWidget *child = as_Widget(j.object);
179 if (child->flags & expand_WidgetFlag) {
180 if (d->flags & arrangeHorizontal_WidgetFlag) {
181 setWidth_Widget_(child, avail.x);
182 setHeight_Widget_(child, d->rect.size.y);
183 }
184 else if (d->flags & arrangeVertical_WidgetFlag) {
185 setWidth_Widget_(child, d->rect.size.x);
186 setHeight_Widget_(child, avail.y);
187 }
188 }
189 else {
190 /* Fill the off axis, though. */
191 if (d->flags & arrangeHorizontal_WidgetFlag) {
192 setHeight_Widget_(child, d->rect.size.y);
193 }
194 else if (d->flags & arrangeVertical_WidgetFlag) {
195 setWidth_Widget_(child, d->rect.size.x);
196 }
197 }
198 }
199 }
200 else {
201 /* Evenly size all children. */
202 iInt2 childSize = d->rect.size;
203 if (d->flags & arrangeHorizontal_WidgetFlag) {
204 childSize.x /= childCount;
205 }
206 else if (d->flags & arrangeVertical_WidgetFlag) {
207 childSize.y /= childCount;
208 }
209 iForEach(ObjectList, i, d->children) {
210 iWidget *child = as_Widget(i.object);
211 setWidth_Widget_(child, childSize.x);
212 setHeight_Widget_(child, childSize.y);
213 }
214 }
215 }
216 if (d->flags & resizeChildrenToWidestChild_WidgetFlag) {
217 const int widest = widestChild_Widget_(d);
218 iForEach(ObjectList, i, d->children) {
219 setWidth_Widget_(as_Widget(i.object), widest);
220 }
221 }
222 iInt2 pos = zero_I2();
223 iForEach(ObjectList, i, d->children) {
224 iWidget *child = as_Widget(i.object);
225 arrange_Widget(child);
226 if (d->flags & (arrangeHorizontal_WidgetFlag | arrangeVertical_WidgetFlag)) {
227 child->rect.pos = pos;
228 if (d->flags & arrangeHorizontal_WidgetFlag) {
229 pos.x += child->rect.size.x;
230 }
231 else {
232 pos.y += child->rect.size.y;
233 }
234 }
235 }
236 /* Update the size of the widget according to the arrangement. */
237 if (d->flags & arrangeSize_WidgetFlag) {
238 iRect bounds = zero_Rect();
239 iConstForEach(ObjectList, i, d->children) {
240 const iWidget *child = constAs_Widget(i.object);
241 if (isEmpty_Rect(bounds)) {
242 bounds = child->rect;
243 }
244 else {
245 bounds = union_Rect(bounds, child->rect);
246 }
247 }
248 if (d->flags & arrangeWidth_WidgetFlag) {
249 setWidth_Widget_(d, bounds.size.x);
250 /* Parent size changed, must update the children.*/
251 iForEach(ObjectList, j, d->children) {
252 iWidget *child = as_Widget(j.object);
253 if (child->flags & resizeToParentWidth_WidgetFlag) {
254 arrange_Widget(child);
255 }
256 }
257 }
258 if (d->flags & arrangeHeight_WidgetFlag) {
259 setHeight_Widget_(d, bounds.size.y);
260 /* Parent size changed, must update the children.*/
261 iForEach(ObjectList, j, d->children) {
262 iWidget *child = as_Widget(j.object);
263 if (child->flags & resizeToParentHeight_WidgetFlag) {
264 arrange_Widget(child);
265 }
266 }
267 }
268 }
269}
270
271iRect bounds_Widget(const iWidget *d) {
272 iRect bounds = d->rect;
273 for (const iWidget *w = d->parent; w; w = w->parent) {
274 addv_I2(&bounds.pos, w->rect.pos);
275 }
276 return bounds;
277}
278
279iInt2 localCoord_Widget(const iWidget *d, iInt2 coord) {
280 for (const iWidget *w = d; w; w = w->parent) {
281 subv_I2(&coord, w->rect.pos);
282 }
283 return coord;
284}
285
286iBool contains_Widget(const iWidget *d, iInt2 coord) {
287 const iRect bounds = { zero_I2(), d->rect.size };
288 return contains_Rect(bounds, localCoord_Widget(d, coord));
289}
290
291iLocalDef iBool isKeyboardEvent_(const SDL_Event *ev) {
292 return (ev->type == SDL_KEYUP || ev->type == SDL_KEYDOWN || ev->type == SDL_TEXTINPUT);
293}
294
295iLocalDef iBool isMouseEvent_(const SDL_Event *ev) {
296 return (ev->type == SDL_MOUSEWHEEL || ev->type == SDL_MOUSEMOTION ||
297 ev->type == SDL_MOUSEBUTTONUP || ev->type == SDL_MOUSEBUTTONDOWN);
298}
299
300static iBool filterEvent_Widget_(const iWidget *d, const SDL_Event *ev) {
301 const iBool isKey = isKeyboardEvent_(ev);
302 const iBool isMouse = isMouseEvent_(ev);
303 if (d->flags & disabled_WidgetFlag) {
304 if (isKey || isMouse) return iFalse;
305 }
306 if (d->flags & hidden_WidgetFlag) {
307 if (isMouse) return iFalse;
308 }
309 return iTrue;
310}
311
312void unhover_Widget(void) {
313 rootData_.hover = NULL;
314}
315
316iBool dispatchEvent_Widget(iWidget *d, const SDL_Event *ev) {
317 if (!d->parent) {
318 if (ev->type == SDL_MOUSEMOTION) {
319 /* Hover widget may change. */
320 rootData_.hover = NULL;
321 }
322 if (rootData_.focus && isKeyboardEvent_(ev)) {
323 /* Root dispatches keyboard events directly to the focused widget. */
324 if (dispatchEvent_Widget(rootData_.focus, ev)) {
325 return iTrue;
326 }
327 }
328 /* Root offers events first to widgets on top. */
329 iForEach(PtrSet, i, rootData_.onTop) {
330 iWidget *widget = *i.value;
331 if (isVisible_Widget(widget) && dispatchEvent_Widget(widget, ev)) {
332 return iTrue;
333 }
334 }
335 }
336 else if (ev->type == SDL_MOUSEMOTION && !rootData_.hover &&
337 flags_Widget(d) & hover_WidgetFlag && ~flags_Widget(d) & hidden_WidgetFlag &&
338 ~flags_Widget(d) & disabled_WidgetFlag) {
339 if (contains_Widget(d, init_I2(ev->motion.x, ev->motion.y))) {
340 rootData_.hover = d;
341 }
342 }
343 if (filterEvent_Widget_(d, ev)) {
344 /* Children may handle it first. Done in reverse so children drawn on top get to
345 handle the events first. */
346 iReverseForEach(ObjectList, i, d->children) {
347 iWidget *child = as_Widget(i.object);
348 if (child == rootData_.focus && isKeyboardEvent_(ev)) {
349 continue; /* Already dispatched. */
350 }
351 if (isVisible_Widget(child) && child->flags & keepOnTop_WidgetFlag) {
352 /* Already dispatched. */
353 continue;
354 }
355 if (dispatchEvent_Widget(child, ev)) {
356 return iTrue;
357 }
358 }
359 if (class_Widget(d)->processEvent(d, ev)) {
360 return iTrue;
361 }
362 }
363 return iFalse;
364}
365
366iBool processEvent_Widget(iWidget *d, const SDL_Event *ev) {
367 if (ev->type == SDL_KEYDOWN) {
368 if (ev->key.keysym.sym == SDLK_TAB) {
369 setFocus_Widget(findFocusable_Widget(focus_Widget(),
370 ev->key.keysym.mod & KMOD_SHIFT
371 ? backward_WidgetFocusDir
372 : forward_WidgetFocusDir));
373 return iTrue;
374 }
375 }
376 switch (ev->type) {
377 case SDL_USEREVENT: {
378 if (ev->user.code == command_UserEventCode && d->commandHandler &&
379 d->commandHandler(d, ev->user.data1)) {
380 return iTrue;
381 }
382 break;
383 }
384 }
385 return iFalse;
386}
387
388void draw_Widget(const iWidget *d) {
389 if (d->flags & hidden_WidgetFlag) return;
390 if (d->bgColor >= 0) {
391 iPaint p;
392 init_Paint(&p);
393 fillRect_Paint(&p, bounds_Widget(d), d->bgColor);
394 }
395 iConstForEach(ObjectList, i, d->children) {
396 const iWidget *child = constAs_Widget(i.object);
397 if (~child->flags & keepOnTop_WidgetFlag && ~child->flags & hidden_WidgetFlag) {
398 class_Widget(child)->draw(child);
399 }
400 }
401 /* Root draws the on-top widgets on top of everything else. */
402 if (!d->parent) {
403 iConstForEach(PtrSet, i, onTop_RootData_()) {
404 draw_Widget(*i.value);
405 }
406 }
407}
408
409iAny *addChild_Widget(iWidget *d, iAnyObject *child) {
410 return addChildPos_Widget(d, child, back_WidgetAddPos);
411}
412
413iAny *addChildPos_Widget(iWidget *d, iAnyObject *child, enum iWidgetAddPos addPos) {
414 iAssert(child);
415 iAssert(d != child);
416 iWidget *widget = as_Widget(child);
417 iAssert(!widget->parent);
418 if (!d->children) {
419 d->children = new_ObjectList();
420 }
421 if (addPos == back_WidgetAddPos) {
422 pushBack_ObjectList(d->children, widget); /* ref */
423 }
424 else {
425 pushFront_ObjectList(d->children, widget); /* ref */
426 }
427 widget->parent = d;
428 return child;
429}
430
431iAny *addChildFlags_Widget(iWidget *d, iAnyObject *child, int childFlags) {
432 setFlags_Widget(child, childFlags, iTrue);
433 return addChild_Widget(d, child);
434}
435
436iAny *removeChild_Widget(iWidget *d, iAnyObject *child) {
437 ref_Object(child);
438 iBool found = iFalse;
439 iForEach(ObjectList, i, d->children) {
440 if (i.object == child) {
441 remove_ObjectListIterator(&i);
442 found = iTrue;
443 break;
444 }
445 }
446 iAssert(found);
447 ((iWidget *) child)->parent = NULL;
448 return child;
449}
450
451iAny *child_Widget(iWidget *d, size_t index) {
452 iForEach(ObjectList, i, d->children) {
453 if (index-- == 0) {
454 return i.object;
455 }
456 }
457 return NULL;
458}
459
460iAny *findChild_Widget(const iWidget *d, const char *id) {
461 if (cmp_String(id_Widget(d), id) == 0) {
462 return iConstCast(iAny *, d);
463 }
464 iConstForEach(ObjectList, i, d->children) {
465 iAny *found = findChild_Widget(constAs_Widget(i.object), id);
466 if (found) return found;
467 }
468 return NULL;
469}
470
471size_t childCount_Widget(const iWidget *d) {
472 if (!d->children) return 0;
473 return size_ObjectList(d->children);
474}
475
476iBool isVisible_Widget(const iWidget *d) {
477 for (const iWidget *w = d; w; w = w->parent) {
478 if (w->flags & hidden_WidgetFlag) {
479 return iFalse;
480 }
481 }
482 return iTrue;
483}
484
485iBool isDisabled_Widget(const iWidget *d) {
486 for (const iWidget *w = d; w; w = w->parent) {
487 if (w->flags & disabled_WidgetFlag) {
488 return iTrue;
489 }
490 }
491 return iFalse;
492}
493
494iBool isFocused_Widget(const iWidget *d) {
495 return rootData_.focus == d;
496}
497
498iBool isHover_Widget(const iWidget *d) {
499 return rootData_.hover == d;
500}
501
502iBool isSelected_Widget(const iWidget *d) {
503 return (d->flags & selected_WidgetFlag) != 0;
504}
505
506iBool isCommand_Widget(const iWidget *d, const SDL_Event *ev, const char *cmd) {
507 if (isCommand_UserEvent(ev, cmd)) {
508 const iWidget *src = pointer_Command(command_UserEvent(ev));
509 iAssert(!src || strstr(ev->user.data1, " ptr:"));
510 return src == d || hasParent_Widget(src, d);
511 }
512 return iFalse;
513}
514
515iBool hasParent_Widget(const iWidget *d, const iWidget *someParent) {
516 if (d) {
517 for (const iWidget *w = d->parent; w; w = w->parent) {
518 if (w == someParent) return iTrue;
519 }
520 }
521 return iFalse;
522}
523
524void setFocus_Widget(iWidget *d) {
525 if (rootData_.focus != d) {
526 if (rootData_.focus) {
527 iAssert(!contains_PtrSet(rootData_.pendingDestruction, rootData_.focus));
528 postCommand_Widget(rootData_.focus, "focus.lost");
529 }
530 rootData_.focus = d;
531 if (d) {
532 iAssert(flags_Widget(d) & focusable_WidgetFlag);
533 postCommand_Widget(d, "focus.gained");
534 }
535 }
536}
537
538iWidget *focus_Widget(void) {
539 return rootData_.focus;
540}
541
542iWidget *hover_Widget(void) {
543 return rootData_.hover;
544}
545
546static const iWidget *findFocusable_Widget_(const iWidget *d, const iWidget *startFrom,
547 iBool *getNext, enum iWidgetFocusDir focusDir) {
548 if (startFrom == d) {
549 *getNext = iTrue;
550 return NULL;
551 }
552 if ((d->flags & focusable_WidgetFlag) && isVisible_Widget(d) && !isDisabled_Widget(d) &&
553 *getNext) {
554 return d;
555 }
556 if (focusDir == forward_WidgetFocusDir) {
557 iConstForEach(ObjectList, i, d->children) {
558 const iWidget *found =
559 findFocusable_Widget_(constAs_Widget(i.object), startFrom, getNext, focusDir);
560 if (found) return found;
561 }
562 }
563 else {
564 iReverseConstForEach(ObjectList, i, d->children) {
565 const iWidget *found =
566 findFocusable_Widget_(constAs_Widget(i.object), startFrom, getNext, focusDir);
567 if (found) return found;
568 }
569 }
570 return NULL;
571}
572
573iAny *findFocusable_Widget(const iWidget *startFrom, enum iWidgetFocusDir focusDir) {
574 iWidget *root = get_Window()->root;
575 iBool getNext = (startFrom ? iFalse : iTrue);
576 const iWidget *found = findFocusable_Widget_(root, startFrom, &getNext, focusDir);
577 if (!found && startFrom) {
578 getNext = iTrue;
579 found = findFocusable_Widget_(root, NULL, &getNext, focusDir);
580 }
581 return iConstCast(iWidget *, found);
582}
583
584void setMouseGrab_Widget(iWidget *d) {
585 if (rootData_.mouseGrab != d) {
586 rootData_.mouseGrab = d;
587 SDL_CaptureMouse(d != NULL);
588 }
589}
590
591iWidget *mouseGrab_Widget(void) {
592 return rootData_.mouseGrab;
593}
594
595void postCommand_Widget(const iWidget *d, const char *cmd, ...) {
596 iString str;
597 init_String(&str); {
598 va_list args;
599 va_start(args, cmd);
600 vprintf_Block(&str.chars, cmd, args);
601 va_end(args);
602 }
603 iBool isGlobal = iFalse;
604 if (*cstr_String(&str) == '!') {
605 isGlobal = iTrue;
606 remove_Block(&str.chars, 0, 1);
607 }
608 if (!isGlobal) {
609 appendFormat_String(&str, " ptr:%p", d);
610 }
611 postCommandString_App(&str);
612 deinit_String(&str);
613}
614
615iBeginDefineClass(Widget)
616 .processEvent = processEvent_Widget,
617 .draw = draw_Widget,
618iEndDefineClass(Widget)
diff --git a/src/ui/widget.h b/src/ui/widget.h
new file mode 100644
index 00000000..bf5c22f1
--- /dev/null
+++ b/src/ui/widget.h
@@ -0,0 +1,131 @@
1#pragma once
2
3/* Base class for UI widgets. */
4
5#include "metrics.h"
6
7#include <the_Foundation/object.h>
8#include <the_Foundation/objectlist.h>
9#include <the_Foundation/rect.h>
10#include <the_Foundation/string.h>
11#include <SDL_events.h>
12
13#define iDeclareWidgetClass(className) \
14 iDeclareType(className); \
15 typedef iWidgetClass i##className##Class; \
16 extern i##className##Class Class_##className;
17
18iDeclareType(Widget)
19iBeginDeclareClass(Widget)
20 iBool (*processEvent) (iWidget *, const SDL_Event *);
21 void (*draw) (const iWidget *);
22iEndDeclareClass(Widget)
23
24enum iWidgetFlag {
25 hidden_WidgetFlag = iBit(1),
26 disabled_WidgetFlag = iBit(2),
27 hover_WidgetFlag = iBit(3), /* eligible for mouse hover */
28 selected_WidgetFlag = iBit(4),
29 pressed_WidgetFlag = iBit(5),
30 alignLeft_WidgetFlag = iBit(6),
31 alignRight_WidgetFlag = iBit(7),
32 frameless_WidgetFlag = iBit(8),
33 drawKey_WidgetFlag = iBit(10),
34 focusable_WidgetFlag = iBit(11),
35 keepOnTop_WidgetFlag = iBit(12), /* gets events first; drawn last */
36 arrangeHorizontal_WidgetFlag = iBit(17), /* arrange children horizontally */
37 arrangeVertical_WidgetFlag = iBit(18), /* arrange children vertically */
38 arrangeWidth_WidgetFlag = iBit(19), /* area of children becomes parent size */
39 arrangeHeight_WidgetFlag = iBit(20), /* area of children becomes parent size */
40 arrangeSize_WidgetFlag = arrangeWidth_WidgetFlag | arrangeHeight_WidgetFlag,
41 resizeChildren_WidgetFlag = iBit(21), /* resize children to fill parent size */
42 expand_WidgetFlag = iBit(22),
43 fixedWidth_WidgetFlag = iBit(23),
44 fixedHeight_WidgetFlag = iBit(24),
45 fixedSize_WidgetFlag = fixedWidth_WidgetFlag | fixedHeight_WidgetFlag,
46 resizeChildrenToWidestChild_WidgetFlag = iBit(25),
47 resizeToParentWidth_WidgetFlag = iBit(26),
48 resizeToParentHeight_WidgetFlag = iBit(27),
49};
50
51enum iWidgetAddPos {
52 back_WidgetAddPos,
53 front_WidgetAddPos,
54};
55
56enum iWidgetFocusDir {
57 forward_WidgetFocusDir,
58 backward_WidgetFocusDir,
59};
60
61struct Impl_Widget {
62 iObject object;
63 iString id;
64 int flags;
65 iRect rect;
66 int bgColor;
67 iObjectList *children;
68 iWidget * parent;
69 iBool (*commandHandler)(iWidget *, const char *);
70};
71
72iDeclareObjectConstruction(Widget)
73
74iLocalDef iWidget *as_Widget(iAnyObject *d) {
75 if (d) {
76 iAssertIsObject(d);
77 iAssert(isInstance_Object(d, &Class_Widget));
78 }
79 return (iWidget *) d;
80}
81
82iLocalDef const iWidget *constAs_Widget(const iAnyObject *d) {
83 if (d) {
84 iAssertIsObject(d);
85 iAssert(isInstance_Object(d, &Class_Widget));
86 }
87 return (const iWidget *) d;
88}
89
90void destroy_Widget (iWidget *); /* widget removed and deleted later */
91void destroyPending_Widget(void);
92
93const iString *id_Widget (const iWidget *);
94int flags_Widget (const iWidget *);
95iRect bounds_Widget (const iWidget *);
96iInt2 localCoord_Widget (const iWidget *, iInt2 coord);
97iBool contains_Widget (const iWidget *, iInt2 coord);
98iAny * findChild_Widget (const iWidget *, const char *id);
99iAny * findFocusable_Widget(const iWidget *startFrom, enum iWidgetFocusDir focusDir);
100size_t childCount_Widget (const iWidget *);
101void draw_Widget (const iWidget *);
102
103iBool isVisible_Widget (const iWidget *);
104iBool isDisabled_Widget (const iWidget *);
105iBool isFocused_Widget (const iWidget *);
106iBool isHover_Widget (const iWidget *);
107iBool isSelected_Widget (const iWidget *);
108iBool isCommand_Widget (const iWidget *d, const SDL_Event *ev, const char *cmd);
109iBool hasParent_Widget (const iWidget *d, const iWidget *someParent);
110void setId_Widget (iWidget *, const char *id);
111void setFlags_Widget (iWidget *, int flags, iBool set);
112void setPos_Widget (iWidget *, iInt2 pos);
113void setSize_Widget (iWidget *, iInt2 size);
114void setBackgroundColor_Widget (iWidget *, int bgColor);
115void setCommandHandler_Widget (iWidget *, iBool (*handler)(iWidget *, const char *));
116iAny * addChild_Widget (iWidget *, iAnyObject *child); /* holds a ref */
117iAny * addChildPos_Widget (iWidget *, iAnyObject *child, enum iWidgetAddPos addPos);
118iAny * addChildFlags_Widget(iWidget *, iAnyObject *child, int childFlags); /* holds a ref */
119iAny * removeChild_Widget (iWidget *, iAnyObject *child); /* returns a ref */
120iAny * child_Widget (iWidget *, size_t index); /* O(n) */
121void arrange_Widget (iWidget *);
122iBool dispatchEvent_Widget(iWidget *, const SDL_Event *);
123iBool processEvent_Widget (iWidget *, const SDL_Event *);
124void postCommand_Widget (const iWidget *, const char *cmd, ...);
125
126void setFocus_Widget (iWidget *);
127iWidget *focus_Widget (void);
128iWidget *hover_Widget (void);
129void unhover_Widget (void);
130void setMouseGrab_Widget (iWidget *);
131iWidget *mouseGrab_Widget (void);
diff --git a/src/ui/window.c b/src/ui/window.c
new file mode 100644
index 00000000..8b4226ef
--- /dev/null
+++ b/src/ui/window.c
@@ -0,0 +1,441 @@
1#include "window.h"
2
3#include "app.h"
4#include "command.h"
5#include "paint.h"
6#include "text.h"
7#include "util.h"
8#include "labelwidget.h"
9#include "inputwidget.h"
10#include "embedded.h"
11#if defined (iPlatformMsys)
12# include "../win32.h"
13#endif
14#if defined (iPlatformApple) && !defined (iPlatformIOS)
15# include "macos.h"
16#endif
17
18#include <the_Foundation/file.h>
19#include <the_Foundation/path.h>
20#include <SDL_hints.h>
21#include <SDL_timer.h>
22#include <SDL_syswm.h>
23
24#define STB_IMAGE_IMPLEMENTATION
25#include "stb_image.h"
26
27static iWindow *theWindow_ = NULL;
28
29#if defined (iPlatformApple)
30static float initialUiScale_ = 1.0f;
31#else
32static float initialUiScale_ = 1.1f;
33#endif
34
35iDefineTypeConstruction(Window)
36
37static iBool handleRootCommands_(iWidget *root, const char *cmd) {
38 iUnused(root);
39 if (equal_Command(cmd, "menu.open")) {
40 iWidget *button = pointer_Command(cmd);
41 iWidget *menu = findChild_Widget(button, "menu");
42 iAssert(menu);
43 if (!isVisible_Widget(menu)) {
44 openMenu_Widget(menu, init_I2(0, button->rect.size.y));
45 }
46 else {
47 closeMenu_Widget(menu);
48 }
49 return iTrue;
50 }
51 else if (handleCommand_App(cmd)) {
52 return iTrue;
53 }
54 return iFalse;
55}
56
57static const iMenuItem fileMenuItems[] = {
58#if !defined (iPlatformApple)
59 { "Quit Lagrange", 'q', KMOD_PRIMARY, "quit" }
60#endif
61};
62
63static const iMenuItem editMenuItems[] = {
64#if !defined (iPlatformApple)
65 { "Preferences...", SDLK_COMMA, KMOD_PRIMARY, "preferences" }
66#endif
67};
68
69static const iMenuItem viewMenuItems[] = {
70};
71
72static void setupUserInterface_Window(iWindow *d) {
73 /* Children of root cover the entire window. */
74 setFlags_Widget(d->root, resizeChildren_WidgetFlag, iTrue);
75 setCommandHandler_Widget(d->root, handleRootCommands_);
76#if 0
77 iWidget *mainDiv = makeHDiv_Widget();
78 setId_Widget(mainDiv, "maindiv");
79 addChild_Widget(d->root, iClob(mainDiv));
80
81 iWidget *sidebar = makeVDiv_Widget();
82 setFlags_Widget(sidebar, arrangeWidth_WidgetFlag, iTrue);
83 setId_Widget(sidebar, "sidebar");
84 addChild_Widget(mainDiv, iClob(sidebar));
85
86 /* Menus. */ {
87#if defined (iPlatformApple) && !defined (iPlatformIOS)
88 /* Use the native menus. */
89 insertMenuItems_MacOS("File", fileMenuItems, iElemCount(fileMenuItems));
90 insertMenuItems_MacOS("Edit", editMenuItems, iElemCount(editMenuItems));
91 insertMenuItems_MacOS("View", viewMenuItems, iElemCount(viewMenuItems));
92#else
93 iWidget *menubar = new_Widget();
94 setBackgroundColor_Widget(menubar, gray25_ColorId);
95 setFlags_Widget(menubar, arrangeHorizontal_WidgetFlag | arrangeHeight_WidgetFlag, iTrue);
96 addChild_Widget(menubar, iClob(makeMenuButton_LabelWidget("File", fileMenuItems, iElemCount(fileMenuItems))));
97 addChild_Widget(menubar, iClob(makeMenuButton_LabelWidget("Edit", editMenuItems, iElemCount(editMenuItems))));
98 addChild_Widget(menubar, iClob(makeMenuButton_LabelWidget("View", viewMenuItems, iElemCount(viewMenuItems))));
99 addChild_Widget(sidebar, iClob(menubar));
100#endif
101 }
102 /* Tracker info. */ {
103 iWidget *trackerInfo = addChild_Widget(sidebar, iClob(new_Widget()));
104 setId_Widget(trackerInfo, "trackerinfo");
105 trackerInfo->rect.size.y = lineHeight_Text(default_FontId) + 2 * gap_UI;
106 setFlags_Widget(trackerInfo, arrangeHorizontal_WidgetFlag | resizeChildren_WidgetFlag, iTrue);
107 setId_Widget(
108 addChild_Widget(trackerInfo, iClob(new_LabelWidget("", 'p', KMOD_PRIMARY, "pattern.goto arg:-1"))),
109 "trackerinfo.current");
110 iLabelWidget *dims = new_LabelWidget("", 'r', KMOD_PRIMARY | KMOD_ALT, "pattern.resize");
111 setId_Widget(addChild_Widget(trackerInfo, iClob(dims)), "trackerinfo.dims");
112 }
113
114 iLibraryWidget *lib = new_LibraryWidget();
115 setId_Widget(as_Widget(lib), "library");
116 addChildFlags_Widget(sidebar, iClob(lib), expand_WidgetFlag);
117
118 iPlaybackWidget *play = new_PlaybackWidget();
119 setId_Widget(as_Widget(play), "playback");
120 addChild_Widget(sidebar, iClob(play));
121
122 iWidget *mainTabs = makeTabs_Widget(mainDiv);
123 setId_Widget(mainTabs, "maintabs");
124 setFlags_Widget(mainTabs, expand_WidgetFlag, iTrue);
125
126 /* Optional sidebar on the right. */
127 iWidget *sidebar2 = new_Widget();
128 setId_Widget(addChild_Widget(mainDiv, iClob(sidebar2)), "sidebar2");
129 setFlags_Widget(
130 sidebar2, fixedWidth_WidgetFlag | frameless_WidgetFlag | resizeChildren_WidgetFlag, iTrue);
131
132 /* Pattern sequence. */ {
133 iSequenceWidget *seq = new_SequenceWidget();
134 appendTabPage_Widget(mainTabs, iClob(seq), "SEQUENCE", 0, 0);
135 }
136 /* Tracker. */ {
137 iTrackerWidget *tracker = new_TrackerWidget();
138 appendTabPage_Widget(mainTabs, as_Widget(tracker), "PATTERN", 0, 0);
139 }
140 /* Voice editor. */ {
141 iWidget *voice = as_Widget(new_VoiceWidget());
142 setId_Widget(voice, "voicelayers");
143 appendTabPage_Widget(mainTabs, iClob(voice), "VOICE", '3', KMOD_PRIMARY);
144 }
145 /* Song information. */ {
146 iWidget *songPage = new_Widget();
147 setId_Widget(songPage, "songinfo");
148 setFlags_Widget(songPage, arrangeHorizontal_WidgetFlag, iTrue);
149 iWidget *headings =
150 addChildFlags_Widget(songPage,
151 iClob(new_Widget()),
152 resizeToParentHeight_WidgetFlag | resizeChildren_WidgetFlag |
153 arrangeVertical_WidgetFlag | arrangeWidth_WidgetFlag);
154 iWidget *values = addChildFlags_Widget(
155 songPage, iClob(new_Widget()), arrangeVertical_WidgetFlag | arrangeSize_WidgetFlag);
156
157 setId_Widget(addChild_Widget(headings, iClob(makePadding_Widget(2 * gap_UI))), "headings.padding");
158 setId_Widget(addChild_Widget(values, iClob(makePadding_Widget(2 * gap_UI))), "values.padding");
159
160 addChild_Widget(headings, iClob(makeHeading_Widget(cyan_ColorEscape "SONG PROPERTIES")));
161 addChild_Widget(values, iClob(makeHeading_Widget("")));
162
163 const int fieldWidth = advance_Text(monospace_FontId, "A").x * 40;
164 iWidget *field;
165
166 addChild_Widget(headings, iClob(makeHeading_Widget("Title:")));
167 setId_Widget(field = addChild_Widget(values, iClob(new_InputWidget(0))), "info.title");
168 field->rect.size.x = fieldWidth;
169
170 addChild_Widget(headings, iClob(makeHeading_Widget("Author:")));
171 setId_Widget(field = addChild_Widget(values, iClob(new_InputWidget(0))), "info.author");
172 field->rect.size.x = fieldWidth;
173
174 addChild_Widget(headings, iClob(makeHeading_Widget("Tempo:")));
175 setId_Widget(addChild_Widget(values, iClob(new_InputWidget(3))), "info.tempo");
176
177 addChild_Widget(headings, iClob(makeHeading_Widget("Events per Beat:")));
178 setId_Widget(addChild_Widget(values, iClob(new_InputWidget(2))), "info.eventsperbeat");
179
180 addChild_Widget(headings, iClob(makeHeading_Widget("Num of Tracks:")));
181 setId_Widget(addChild_Widget(values, iClob(new_InputWidget(2))), "info.numtracks");
182
183 addChild_Widget(headings, iClob(makePadding_Widget(2 * gap_UI)));
184 addChild_Widget(values, iClob(makePadding_Widget(2 * gap_UI)));
185
186 addChild_Widget(headings, iClob(makeHeading_Widget(cyan_ColorEscape "SONG METADATA")));
187 addChild_Widget(values, iClob(makeHeading_Widget("")));
188
189 addChild_Widget(headings, iClob(makeHeading_Widget("Duration:")));
190 setId_Widget(addChildFlags_Widget(values, iClob(newEmpty_LabelWidget()),
191 alignLeft_WidgetFlag | frameless_WidgetFlag),
192 "info.duration");
193 addChild_Widget(headings, iClob(makeHeading_Widget("Statistics:\n\n ")));
194 setId_Widget(addChildFlags_Widget(values,
195 iClob(newEmpty_LabelWidget()),
196 alignLeft_WidgetFlag | frameless_WidgetFlag),
197 "info.statistics");
198 addChild_Widget(headings, iClob(makeHeading_Widget("Created on:")));
199 setId_Widget(addChildFlags_Widget(values,
200 iClob(newEmpty_LabelWidget()),
201 alignLeft_WidgetFlag | frameless_WidgetFlag),
202 "info.created");
203
204 addChild_Widget(headings, iClob(makeHeading_Widget("Last Modified on:")));
205 setId_Widget(addChildFlags_Widget(values,
206 iClob(newEmpty_LabelWidget()),
207 alignLeft_WidgetFlag | frameless_WidgetFlag),
208 "info.lastmodified");
209 /* App info in the bottom. */ {
210 addChildFlags_Widget(headings, iClob(new_Widget()), expand_WidgetFlag);
211 addChildFlags_Widget(
212 headings,
213 iClob(new_LabelWidget(gray50_ColorEscape "Version " BWH_APP_VERSION, 0, 0, NULL)),
214 frameless_WidgetFlag | alignLeft_WidgetFlag);
215 }
216 appendTabPage_Widget(mainTabs, iClob(songPage), "INFO", '4', KMOD_PRIMARY);
217 }
218 /* Application status. */ {
219 iWidget *status = addChildFlags_Widget(d->root, iClob(newEmpty_LabelWidget()), 0);
220 setFont_LabelWidget((iLabelWidget *) status, monospace_FontId);
221 setFlags_Widget(status, frameless_WidgetFlag | alignRight_WidgetFlag, iTrue);
222 setId_Widget(status, "status");
223 }
224#endif
225 /* Glboal keyboard shortcuts. */ {
226 // addAction_Widget(d->root, SDLK_LEFTBRACKET, KMOD_SHIFT | KMOD_PRIMARY, "tabs.prev");
227 }
228}
229
230static void updateRootSize_Window_(iWindow *d) {
231 iInt2 *size = &d->root->rect.size;
232 SDL_GetRendererOutputSize(d->render, &size->x, &size->y);
233 arrange_Widget(d->root);
234 postCommandf_App("window.resized width:%d height:%d", size->x, size->y);
235}
236
237static float pixelRatio_Window_(const iWindow *d) {
238 int dx, x;
239 SDL_GetRendererOutputSize(d->render, &dx, NULL);
240 SDL_GetWindowSize(d->win, &x, NULL);
241 return (float) dx / (float) x;
242}
243
244void init_Window(iWindow *d) {
245 theWindow_ = d;
246 uint32_t flags = SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI;
247#if defined (iPlatformApple)
248 SDL_SetHint(SDL_HINT_RENDER_DRIVER, "metal");
249#else
250 flags |= SDL_WINDOW_OPENGL;
251#endif
252 SDL_SetHint(SDL_HINT_RENDER_VSYNC, "1");
253 if (SDL_CreateWindowAndRenderer(800, 500, flags, &d->win, &d->render)) {
254 fprintf(stderr, "Error when creating window: %s\n", SDL_GetError());
255 exit(-2);
256 }
257 SDL_SetWindowMinimumSize(d->win, 640, 480);
258 SDL_SetWindowTitle(d->win, "Lagrange");
259 SDL_ShowWindow(d->win);
260 /* Some info. */ {
261 SDL_RendererInfo info;
262 SDL_GetRendererInfo(d->render, &info);
263 printf("[window] renderer: %s\n", info.name);
264 }
265 d->uiScale = initialUiScale_;
266 d->pixelRatio = pixelRatio_Window_(d);
267 setPixelRatio_Metrics(d->pixelRatio * d->uiScale);
268#if defined (iPlatformMsys)
269 useExecutableIconResource_SDLWindow(d->win);
270#endif
271#if defined (iPlatformLinux)
272 /* Load the window icon. */ {
273 int w, h, num;
274 const iBlock *icon = &imageAppicon64_Embedded;
275 stbi_uc *pixels = stbi_load_from_memory(constData_Block(icon),
276 size_Block(icon),
277 &w,
278 &h,
279 &num,
280 STBI_rgb_alpha);
281 SDL_Surface *surf =
282 SDL_CreateRGBSurfaceWithFormatFrom(pixels, w, h, 32, 4 * w, SDL_PIXELFORMAT_RGBA32);
283 SDL_SetWindowIcon(d->win, surf);
284 SDL_FreeSurface(surf);
285 stbi_image_free(pixels);
286 }
287#endif
288 d->root = new_Widget();
289 d->presentTime = 0.0;
290 setId_Widget(d->root, "root");
291 init_Text(d->render);
292#if defined (iPlatformApple) && !defined (iPlatformIOS)
293 setupApplication_MacOS();
294#endif
295 setupUserInterface_Window(d);
296 updateRootSize_Window_(d);
297}
298
299void deinit_Window(iWindow *d) {
300 if (theWindow_ == d) {
301 theWindow_ = NULL;
302 }
303 iReleasePtr(&d->root);
304 deinit_Text();
305 SDL_DestroyRenderer(d->render);
306 SDL_DestroyWindow(d->win);
307}
308
309SDL_Renderer *renderer_Window(const iWindow *d) {
310 return d->render;
311}
312
313static iBool handleWindowEvent_Window_(iWindow *d, const SDL_WindowEvent *ev) {
314 switch (ev->event) {
315 case SDL_WINDOWEVENT_RESIZED:
316 updateRootSize_Window_(d);
317 return iTrue;
318 case SDL_WINDOWEVENT_LEAVE:
319 unhover_Widget();
320 return iTrue;
321 default:
322 break;
323 }
324 return iFalse;
325}
326
327iBool processEvent_Window(iWindow *d, const SDL_Event *ev) {
328 switch (ev->type) {
329 case SDL_WINDOWEVENT: {
330 return handleWindowEvent_Window_(d, &ev->window);
331 }
332 default: {
333 SDL_Event event = *ev;
334 /* Map mouse pointer coordinate to our coordinate system. */
335 if (event.type == SDL_MOUSEMOTION) {
336 const iInt2 pos = coord_Window(d, event.motion.x, event.motion.y);
337 event.motion.x = pos.x;
338 event.motion.y = pos.y;
339 }
340 else if (event.type == SDL_MOUSEBUTTONUP || event.type == SDL_MOUSEBUTTONDOWN) {
341 const iInt2 pos = coord_Window(d, event.button.x, event.button.y);
342 event.button.x = pos.x;
343 event.button.y = pos.y;
344 }
345 iWidget *widget = d->root;
346 if (event.type == SDL_MOUSEMOTION || event.type == SDL_MOUSEWHEEL ||
347 event.type == SDL_MOUSEBUTTONUP || event.type == SDL_MOUSEBUTTONDOWN) {
348 if (mouseGrab_Widget()) {
349 widget = mouseGrab_Widget();
350 }
351 }
352 return dispatchEvent_Widget(widget, &event);
353 }
354 }
355 return iFalse;
356}
357
358static void waitPresent_Window_(iWindow *d) {
359 const double ticksPerFrame = 1000.0 / 30.0;
360 uint32_t nowTime = SDL_GetTicks();
361 if (nowTime < d->presentTime) {
362 SDL_Delay((uint32_t) (d->presentTime - nowTime));
363 nowTime = SDL_GetTicks();
364 }
365 /* Now it is the presentation time. */
366 /* Figure out the next time in the future. */
367 if (d->presentTime <= nowTime) {
368 d->presentTime += ticksPerFrame * ((int) ((nowTime - d->presentTime) / ticksPerFrame) + 1);
369 }
370 else {
371 d->presentTime = nowTime;
372 }
373}
374
375void draw_Window(iWindow *d) {
376 /* Clear the window. */
377 SDL_SetRenderDrawColor(d->render, 0, 0, 0, 255);
378 SDL_RenderClear(d->render);
379 /* Draw widgets. */
380 d->frameTime = SDL_GetTicks();
381 draw_Widget(d->root);
382#if 0
383 /* Text cache debugging. */ {
384 SDL_Texture *cache = glyphCache_Text();
385 SDL_Rect rect = { 140, 60, 512, 512 };
386 SDL_SetRenderDrawColor(d->render, 0, 0, 0, 255);
387 SDL_RenderFillRect(d->render, &rect);
388 SDL_RenderCopy(d->render, glyphCache_Text(), NULL, &rect);
389 }
390#endif
391 waitPresent_Window_(d);
392 SDL_RenderPresent(d->render);
393}
394
395void resize_Window(iWindow *d, int w, int h) {
396 SDL_SetWindowSize(d->win, w, h);
397 updateRootSize_Window_(d);
398}
399
400void setUiScale_Window(iWindow *d, float uiScale) {
401 uiScale = iClamp(uiScale, 0.5f, 4.0f);
402 if (d) {
403 d->uiScale = uiScale;
404#if 0
405 deinit_Text();
406 setPixelRatio_Metrics(d->pixelRatio * d->uiScale);
407 init_Text(d->render);
408 postCommand_App("metrics.changed");
409 /* TODO: Dynamic UI metrics change. Widgets need to update themselves. */
410#endif
411 }
412 else {
413 initialUiScale_ = uiScale;
414 }
415}
416
417iInt2 rootSize_Window(const iWindow *d) {
418 return d->root->rect.size;
419}
420
421iInt2 coord_Window(const iWindow *d, int x, int y) {
422 return mulf_I2(init_I2(x, y), d->pixelRatio);
423}
424
425iInt2 mouseCoord_Window(const iWindow *d) {
426 int x, y;
427 SDL_GetMouseState(&x, &y);
428 return coord_Window(d, x, y);
429}
430
431float uiScale_Window(const iWindow *d) {
432 return d->uiScale;
433}
434
435uint32_t frameTime_Window(const iWindow *d) {
436 return d->frameTime;
437}
438
439iWindow *get_Window(void) {
440 return theWindow_;
441}
diff --git a/src/ui/window.h b/src/ui/window.h
new file mode 100644
index 00000000..d0413af4
--- /dev/null
+++ b/src/ui/window.h
@@ -0,0 +1,34 @@
1#pragma once
2
3#include "widget.h"
4
5#include <the_Foundation/defs.h>
6#include <SDL_events.h>
7#include <SDL_render.h>
8#include <SDL_video.h>
9
10iDeclareType(Window)
11iDeclareTypeConstruction(Window)
12
13struct Impl_Window {
14 SDL_Window * win;
15 SDL_Renderer *render;
16 iWidget * root;
17 float pixelRatio;
18 float uiScale;
19 uint32_t frameTime;
20 double presentTime;
21};
22
23iBool processEvent_Window (iWindow *, const SDL_Event *);
24void draw_Window (iWindow *);
25void resize_Window (iWindow *, int w, int h);
26void setUiScale_Window (iWindow *, float uiScale);
27
28iInt2 rootSize_Window (const iWindow *);
29float uiScale_Window (const iWindow *);
30iInt2 coord_Window (const iWindow *, int x, int y);
31iInt2 mouseCoord_Window (const iWindow *);
32uint32_t frameTime_Window (const iWindow *);
33
34iWindow * get_Window (void);
diff --git a/src/win32.c b/src/win32.c
new file mode 100644
index 00000000..1cdcf34c
--- /dev/null
+++ b/src/win32.c
@@ -0,0 +1,18 @@
1#include "win32.h"
2#include <SDL_syswm.h>
3
4#define WIN32_LEAN_AND_MEAN
5#include <Windows.h>
6
7void useExecutableIconResource_SDLWindow(SDL_Window *win) {
8 HINSTANCE handle = GetModuleHandle(NULL);
9 HICON icon = LoadIcon(handle, "IDI_ICON1");
10 if (icon) {
11 SDL_SysWMinfo wmInfo;
12 SDL_VERSION(&wmInfo.version);
13 if (SDL_GetWindowWMInfo(win, &wmInfo)) {
14 HWND hwnd = wmInfo.info.win.window;
15 SetClassLongPtr(hwnd, -14 /*GCL_HICON*/, (LONG_PTR) icon);
16 }
17 }
18}
diff --git a/src/win32.h b/src/win32.h
new file mode 100644
index 00000000..b223ea4d
--- /dev/null
+++ b/src/win32.h
@@ -0,0 +1,4 @@
1#pragma once
2#include <SDL_video.h>
3
4void useExecutableIconResource_SDLWindow(SDL_Window *win);