Skip to content

Commit a740c66

Browse files
authored
Merge pull request #3663 from shevernitskiy/dynamic-texture-loading
feat: dynamic texture loading
2 parents 04dcbe3 + 4fb6b09 commit a740c66

File tree

9 files changed

+446
-312
lines changed

9 files changed

+446
-312
lines changed

library/Core.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2217,9 +2217,6 @@ void Core::onStateChange(color_ostream &out, state_change_event event)
22172217
}
22182218
}
22192219
break;
2220-
case SC_VIEWSCREEN_CHANGED:
2221-
Textures::init(out);
2222-
break;
22232220
default:
22242221
break;
22252222
}

library/LuaApi.cpp

Lines changed: 54 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,26 @@ static bool get_int_field(lua_State *L, T *pf, int idx, const char *name, int de
161161
return !nil;
162162
}
163163

164+
template<class T>
165+
static bool get_int_or_closure_field(lua_State *L, T *pf, int idx, const char *name, int defval)
166+
{
167+
lua_getfield(L, idx, name);
168+
bool nil = lua_isnil(L, -1);
169+
if (nil) {
170+
*pf = T(defval);
171+
} else if (lua_isnumber(L, -1)) {
172+
*pf = T(lua_tointeger(L, -1));
173+
} else if (lua_isfunction(L, -1)) {
174+
lua_call(L, 0, 1);
175+
*pf = T(lua_tointeger(L, -1));
176+
lua_pop(L, 1);
177+
} else {
178+
luaL_error(L, "Field %s is not a number or closure function.", name);
179+
}
180+
lua_pop(L, 1);
181+
return !nil;
182+
}
183+
164184
static bool get_char_field(lua_State *L, char *pf, int idx, const char *name, char defval)
165185
{
166186
lua_getfield(L, idx, name);
@@ -207,7 +227,7 @@ static void decode_pen(lua_State *L, Pen &pen, int idx)
207227
else pen.bold = lua_toboolean(L, -1);
208228
lua_pop(L, 1);
209229

210-
get_int_field(L, &pen.tile, idx, "tile", 0);
230+
get_int_or_closure_field(L, &pen.tile, idx, "tile", 0);
211231

212232
bool tcolor = get_int_field(L, &pen.tile_fg, idx, "tile_fg", 7);
213233
tcolor = get_int_field(L, &pen.tile_bg, idx, "tile_bg", 0) || tcolor;
@@ -1377,6 +1397,13 @@ static void OpenModule(lua_State *state, const char *mname,
13771397
lua_pop(state, 1);
13781398
}
13791399

1400+
static void OpenModule(lua_State *state, const char *mname, const luaL_Reg *reg2)
1401+
{
1402+
luaL_getsubtable(state, lua_gettop(state), mname);
1403+
luaL_setfuncs(state, reg2, 0);
1404+
lua_pop(state, 1);
1405+
}
1406+
13801407
#define WRAPM(module, function) { #function, df::wrap_function(module::function,true) }
13811408
#define WRAP(function) { #function, df::wrap_function(function,true) }
13821409
#define WRAPN(name, function) { #name, df::wrap_function(function,true) }
@@ -1726,19 +1753,31 @@ static const luaL_Reg dfhack_job_funcs[] = {
17261753

17271754
/***** Textures module *****/
17281755

1729-
static const LuaWrapper::FunctionReg dfhack_textures_module[] = {
1730-
WRAPM(Textures, getDfhackLogoTexposStart),
1731-
WRAPM(Textures, getGreenPinTexposStart),
1732-
WRAPM(Textures, getRedPinTexposStart),
1733-
WRAPM(Textures, getIconsTexposStart),
1734-
WRAPM(Textures, getOnOffTexposStart),
1735-
WRAPM(Textures, getMapUnsuspendTexposStart),
1736-
WRAPM(Textures, getControlPanelTexposStart),
1737-
WRAPM(Textures, getThinBordersTexposStart),
1738-
WRAPM(Textures, getMediumBordersTexposStart),
1739-
WRAPM(Textures, getBoldBordersTexposStart),
1740-
WRAPM(Textures, getPanelBordersTexposStart),
1741-
WRAPM(Textures, getWindowBordersTexposStart),
1756+
static int textures_loadTileset(lua_State *state)
1757+
{
1758+
std::string file = luaL_checkstring(state, 1);
1759+
auto tile_w = luaL_checkint(state, 2);
1760+
auto tile_h = luaL_checkint(state, 3);
1761+
auto handles = Textures::loadTileset(file, tile_w, tile_h);
1762+
Lua::PushVector(state, handles);
1763+
return 1;
1764+
}
1765+
1766+
static int textures_getTexposByHandle(lua_State *state)
1767+
{
1768+
auto handle = luaL_checkunsigned(state, 1);
1769+
auto texpos = Textures::getTexposByHandle(handle);
1770+
if (texpos == -1) {
1771+
lua_pushnil(state);
1772+
} else {
1773+
Lua::Push(state, texpos);
1774+
}
1775+
return 1;
1776+
}
1777+
1778+
static const luaL_Reg dfhack_textures_funcs[] = {
1779+
{ "loadTileset", textures_loadTileset },
1780+
{ "getTexposByHandle", textures_getTexposByHandle },
17421781
{ NULL, NULL }
17431782
};
17441783

@@ -3713,7 +3752,7 @@ void OpenDFHackApi(lua_State *state)
37133752
luaL_setfuncs(state, dfhack_funcs, 0);
37143753
OpenModule(state, "gui", dfhack_gui_module, dfhack_gui_funcs);
37153754
OpenModule(state, "job", dfhack_job_module, dfhack_job_funcs);
3716-
OpenModule(state, "textures", dfhack_textures_module);
3755+
OpenModule(state, "textures", dfhack_textures_funcs);
37173756
OpenModule(state, "units", dfhack_units_module, dfhack_units_funcs);
37183757
OpenModule(state, "military", dfhack_military_module);
37193758
OpenModule(state, "items", dfhack_items_module, dfhack_items_funcs);

library/include/modules/Textures.h

Lines changed: 30 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
#pragma once
22

3-
#include "Export.h"
3+
#include <string>
4+
#include <vector>
5+
46
#include "ColorText.h"
7+
#include "Export.h"
8+
9+
struct SDL_Surface;
10+
11+
typedef uintptr_t TexposHandle;
512

613
namespace DFHack {
714

@@ -12,63 +19,41 @@ namespace DFHack {
1219
*/
1320
namespace Textures {
1421

15-
/**
16-
* Call this on DFHack init and on every viewscreen change so we can reload
17-
* and reindex textures as needed.
18-
*/
19-
void init(DFHack::color_ostream &out);
20-
21-
/**
22-
* Call this when DFHack is being unloaded.
23-
*
24-
*/
25-
void cleanup();
22+
const uint32_t TILE_WIDTH_PX = 8;
23+
const uint32_t TILE_HEIGHT_PX = 12;
2624

2725
/**
28-
* Get first texpos for the DFHack logo. This texpos and the next 11 make up the
29-
* 4x3 grid of logo textures that can be displayed on the UI layer.
26+
* Load texture and get handle.
27+
* Keep it to obtain valid texpos.
3028
*/
31-
DFHACK_EXPORT long getDfhackLogoTexposStart();
29+
DFHACK_EXPORT TexposHandle loadTexture(SDL_Surface* surface);
3230

3331
/**
34-
* Get the first texpos for the UI pin tiles. Each are 2x2 grids.
32+
* Load tileset from image file.
33+
* Return vector of handles to obtain valid texposes.
3534
*/
36-
DFHACK_EXPORT long getGreenPinTexposStart();
37-
DFHACK_EXPORT long getRedPinTexposStart();
35+
DFHACK_EXPORT std::vector<TexposHandle> loadTileset(const std::string& file,
36+
int tile_px_w = TILE_WIDTH_PX,
37+
int tile_px_h = TILE_HEIGHT_PX);
3838

3939
/**
40-
* Get the first texpos for the DFHack icons. It's a 5x2 grid.
40+
* Get texpos by handle.
41+
* Always use this function, if you need to get valid texpos for your texture.
42+
* Texpos can change on game textures reset, but handle will be the same.
4143
*/
42-
DFHACK_EXPORT long getIconsTexposStart();
44+
DFHACK_EXPORT long getTexposByHandle(TexposHandle handle);
4345

4446
/**
45-
* Get the first texpos for the on and off icons. It's a 2x1 grid.
47+
* Call this on DFHack init just once to setup interposed handlers and
48+
* init static assets.
4649
*/
47-
DFHACK_EXPORT long getOnOffTexposStart();
50+
void init(DFHack::color_ostream& out);
4851

4952
/**
50-
* Get the first texpos for the pathable 32x32 sprites. It's a 2x1 grid.
51-
*/
52-
DFHACK_EXPORT long getMapPathableTexposStart();
53-
54-
/**
55-
* Get the first texpos for the unsuspend 32x32 sprites. It's a 5x1 grid.
56-
*/
57-
DFHACK_EXPORT long getMapUnsuspendTexposStart();
58-
59-
/**
60-
* Get the first texpos for the control panel icons. 10x2 grid.
61-
*/
62-
DFHACK_EXPORT long getControlPanelTexposStart();
63-
64-
/**
65-
* Get the first texpos for the DFHack borders. Each is a 7x3 grid.
53+
* Call this when DFHack is being unloaded.
54+
*
6655
*/
67-
DFHACK_EXPORT long getThinBordersTexposStart();
68-
DFHACK_EXPORT long getMediumBordersTexposStart();
69-
DFHACK_EXPORT long getBoldBordersTexposStart();
70-
DFHACK_EXPORT long getPanelBordersTexposStart();
71-
DFHACK_EXPORT long getWindowBordersTexposStart();
56+
void cleanup();
7257

73-
}
74-
}
58+
} // namespace Textures
59+
} // namespace DFHack

library/lua/gui.lua

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
local _ENV = mkmodule('gui')
44

5+
local textures = require('gui.textures')
56
local utils = require('utils')
67

78
local dscreen = dfhack.screen
@@ -912,33 +913,46 @@ local BASE_FRAME = {
912913
paused_pen = to_pen{fg=COLOR_RED, bg=COLOR_BLACK},
913914
}
914915

915-
local function make_frame(name, double_line)
916-
local texpos = dfhack.textures['get'..name..'BordersTexposStart']()
917-
local tp = function(offset)
918-
if texpos == -1 then return nil end
919-
return texpos + offset
920-
end
921916

917+
local function make_frame(tp, double_line)
922918
local frame = copyall(BASE_FRAME)
923-
frame.t_frame_pen = to_pen{ tile=tp(1), ch=double_line and 205 or 196, fg=COLOR_GREY, bg=COLOR_BLACK }
924-
frame.l_frame_pen = to_pen{ tile=tp(7), ch=double_line and 186 or 179, fg=COLOR_GREY, bg=COLOR_BLACK }
925-
frame.b_frame_pen = to_pen{ tile=tp(15), ch=double_line and 205 or 196, fg=COLOR_GREY, bg=COLOR_BLACK }
926-
frame.r_frame_pen = to_pen{ tile=tp(9), ch=double_line and 186 or 179, fg=COLOR_GREY, bg=COLOR_BLACK }
927-
frame.lt_frame_pen = to_pen{ tile=tp(0), ch=double_line and 201 or 218, fg=COLOR_GREY, bg=COLOR_BLACK }
928-
frame.lb_frame_pen = to_pen{ tile=tp(14), ch=double_line and 200 or 192, fg=COLOR_GREY, bg=COLOR_BLACK }
929-
frame.rt_frame_pen = to_pen{ tile=tp(2), ch=double_line and 187 or 191, fg=COLOR_GREY, bg=COLOR_BLACK }
930-
frame.rb_frame_pen = to_pen{ tile=tp(16), ch=double_line and 188 or 217, fg=COLOR_GREY, bg=COLOR_BLACK }
919+
frame.t_frame_pen = to_pen{ tile=curry(tp, 2), ch=double_line and 205 or 196, fg=COLOR_GREY, bg=COLOR_BLACK }
920+
frame.l_frame_pen = to_pen{ tile=curry(tp, 8), ch=double_line and 186 or 179, fg=COLOR_GREY, bg=COLOR_BLACK }
921+
frame.b_frame_pen = to_pen{ tile=curry(tp, 16), ch=double_line and 205 or 196, fg=COLOR_GREY, bg=COLOR_BLACK }
922+
frame.r_frame_pen = to_pen{ tile=curry(tp, 10), ch=double_line and 186 or 179, fg=COLOR_GREY, bg=COLOR_BLACK }
923+
frame.lt_frame_pen = to_pen{ tile=curry(tp, 1), ch=double_line and 201 or 218, fg=COLOR_GREY, bg=COLOR_BLACK }
924+
frame.lb_frame_pen = to_pen{ tile=curry(tp, 15), ch=double_line and 200 or 192, fg=COLOR_GREY, bg=COLOR_BLACK }
925+
frame.rt_frame_pen = to_pen{ tile=curry(tp, 3), ch=double_line and 187 or 191, fg=COLOR_GREY, bg=COLOR_BLACK }
926+
frame.rb_frame_pen = to_pen{ tile=curry(tp, 17), ch=double_line and 188 or 217, fg=COLOR_GREY, bg=COLOR_BLACK }
931927
return frame
932928
end
933929

934-
FRAME_WINDOW = make_frame('Window', true)
935-
FRAME_PANEL = make_frame('Panel', false)
936-
FRAME_MEDIUM = make_frame('Medium', false)
937-
FRAME_BOLD = make_frame('Bold', true)
938-
FRAME_INTERIOR = make_frame('Thin', false)
939-
FRAME_INTERIOR.signature_pen = false
940-
FRAME_INTERIOR_MEDIUM = copyall(FRAME_MEDIUM)
941-
FRAME_INTERIOR_MEDIUM.signature_pen = false
930+
function FRAME_WINDOW(resizable)
931+
local frame = make_frame(textures.tp_border_window, true)
932+
if not resizable then
933+
frame.rb_frame_pen = to_pen{ tile=curry(textures.tp_border_panel, 17), ch=double_line and 188 or 217, fg=COLOR_GREY, bg=COLOR_BLACK }
934+
end
935+
return frame
936+
end
937+
function FRAME_PANEL(resizable)
938+
return make_frame(textures.tp_border_panel, false)
939+
end
940+
function FRAME_MEDIUM(resizable)
941+
return make_frame(textures.tp_border_medium, false)
942+
end
943+
function FRAME_BOLD(resizable)
944+
return make_frame(textures.tp_border_bold, true)
945+
end
946+
function FRAME_INTERIOR(resizable)
947+
local frame = make_frame(textures.tp_border_thin, false)
948+
frame.signature_pen = false
949+
return frame
950+
end
951+
function FRAME_INTERIOR_MEDIUM(resizable)
952+
local frame = make_frame(textures.tp_border_medium, false)
953+
frame.signature_pen = false
954+
return frame
955+
end
942956

943957
-- for compatibility with pre-steam code
944958
GREY_LINE_FRAME = FRAME_PANEL
@@ -951,18 +965,16 @@ BOLD_FRAME = FRAME_BOLD
951965
INTERIOR_FRAME = FRAME_INTERIOR
952966
INTERIOR_MEDIUM_FRAME = FRAME_INTERIOR_MEDIUM
953967

954-
955-
function paint_frame(dc,rect,style,title,inactive,pause_forced,resizable)
968+
function paint_frame(dc, rect, style, title, inactive, pause_forced, resizable)
969+
if type(style) == 'function' then
970+
style = style(resizable)
971+
end
956972
local pen = style.frame_pen
957973
local x1,y1,x2,y2 = dc.x1+rect.x1, dc.y1+rect.y1, dc.x1+rect.x2, dc.y1+rect.y2
958974
dscreen.paintTile(style.lt_frame_pen or pen, x1, y1)
959975
dscreen.paintTile(style.rt_frame_pen or pen, x2, y1)
960976
dscreen.paintTile(style.lb_frame_pen or pen, x1, y2)
961-
local rb_frame_pen = style.rb_frame_pen
962-
if rb_frame_pen == FRAME_WINDOW.rb_frame_pen and not resizable then
963-
rb_frame_pen = FRAME_PANEL.rb_frame_pen
964-
end
965-
dscreen.paintTile(rb_frame_pen or pen, x2, y2)
977+
dscreen.paintTile(style.rb_frame_pen or pen, x2, y2)
966978
dscreen.fillRect(style.t_frame_pen or style.h_frame_pen or pen,x1+1,y1,x2-1,y1)
967979
dscreen.fillRect(style.b_frame_pen or style.h_frame_pen or pen,x1+1,y2,x2-1,y2)
968980
dscreen.fillRect(style.l_frame_pen or style.v_frame_pen or pen,x1,y1+1,x1,y2-1)

0 commit comments

Comments
 (0)