develop
shevernitskiy 2023-08-30 07:38:21 +03:00
parent 4b0219e225
commit 8a015f7f87
3 changed files with 53 additions and 11 deletions

@ -3864,6 +3864,9 @@ These modules make extensive use of the ``class`` module, and define
things ranging from the basic ``Painter``, ``View`` and ``Screen`` things ranging from the basic ``Painter``, ``View`` and ``Screen``
classes, to fully functional predefined dialogs. classes, to fully functional predefined dialogs.
In addition to the ``gui`` module, there is a ``textures`` module that allows
you to perform actions on textures to use them as tiles during rendering.
gui gui
=== ===
@ -5342,6 +5345,48 @@ The parent widget owns the range values, and can control them independently (e.g
:on_left_change: Callback executed when moving the left handle. :on_left_change: Callback executed when moving the left handle.
:on_right_change: Callback executed when moving the right handle. :on_right_change: Callback executed when moving the right handle.
textures
========
In order for the game to render a particular tile (graphic), it needs to know ``texpos`` - the position in the vector of the registered game textures.
Add your own texture to it and get ``texpos`` is not difficult. But the game periodically deletes textures that are in the vector, and that's the problem.
Because the ``texpos`` we got earlier no longer points to our added texture.
The ``texture`` module solves this problem. Instead of ``texpos`` directly, it operates on the ``TexposHandle`` entity, which is essentially a reference to ``texpos``.
Thanks to this handle, it is possible to get a valid ``texpos`` at any time.
* ``loadTileset(file, tile_px_w, tile_px_h)``
Loads tileset from the image ``file`` with give tile dimension in pixels (image will be sliced in row major order).
Returns an array of ``TexposHandle``
* ``getTexposByHandle(handle)``
Get ``texpos`` by ``TexposHandle``.
Always use this method if you need to get valid texpos for your texture.
``texpos`` can change on game textures reset, but the handle will be the same.
* ``createTile(pixels, tile_px_w, tile_px_h)``
Create and register new a texture with the given tile dimension and array of ``pixels`` as data in row major order.
Each pixel is an integer representing color in packed RBGA format (for example, #0022FF11).
Returns ``TexposHandle``.
* ``createTileset(pixels, texture_px_w, texture_px_h, tile_px_w, tile_px_h)``
Create and register a new texture with the given texture dimension and array of ``pixels`` as data in row major order.
Then slice it on tiles with the given dimension in row major order.
Each pixel is an integer representing color in packed RBGA format (for example #0022FF11).
Returns an array of ``TexposHandle``.
* ``deleteHandle(handle)``
``handle`` here can be signle ``TexposHandle``, or array of ``TexposHandle``.
Deletes all metadata and texture(s) itself by given handle(s).
You must be sure that the game does not use this texture in rendering process.
.. _lua-plugins: .. _lua-plugins:
======= =======

@ -49,14 +49,14 @@ DFHACK_EXPORT long getTexposByHandle(TexposHandle handle);
DFHACK_EXPORT void deleteHandle(TexposHandle handle); DFHACK_EXPORT void deleteHandle(TexposHandle handle);
/** /**
* Create new texture with RGBA32 format and pixels as data. * Create new texture with RGBA32 format and pixels as data in row major order.
* Register this texture and return TexposHandle. * Register this texture and return TexposHandle.
*/ */
DFHACK_EXPORT TexposHandle createTile(std::vector<uint32_t>& pixels, int tile_px_w = TILE_WIDTH_PX, DFHACK_EXPORT TexposHandle createTile(std::vector<uint32_t>& pixels, int tile_px_w = TILE_WIDTH_PX,
int tile_px_h = TILE_HEIGHT_PX); int tile_px_h = TILE_HEIGHT_PX);
/** /**
* Create new textures as tileset with RGBA32 format and pixels as data. * Create new textures as tileset with RGBA32 format and pixels as data in row major order.
* Register this textures and return vector of TexposHandle. * Register this textures and return vector of TexposHandle.
*/ */
DFHACK_EXPORT std::vector<TexposHandle> createTileset(std::vector<uint32_t>& pixels, DFHACK_EXPORT std::vector<TexposHandle> createTileset(std::vector<uint32_t>& pixels,

@ -17,6 +17,7 @@
#include "df/viewscreen_new_arenast.h" #include "df/viewscreen_new_arenast.h"
#include "df/viewscreen_new_regionst.h" #include "df/viewscreen_new_regionst.h"
#include <SDL_pixels.h>
#include <SDL_surface.h> #include <SDL_surface.h>
using df::global::enabler; using df::global::enabler;
@ -31,10 +32,6 @@ static std::unordered_map<TexposHandle, long> g_handle_to_texpos;
static std::unordered_map<TexposHandle, SDL_Surface*> g_handle_to_surface; static std::unordered_map<TexposHandle, SDL_Surface*> g_handle_to_surface;
static std::mutex g_adding_mutex; static std::mutex g_adding_mutex;
// it is SDL_PixelFormatEnum::SDL_PIXELFORMAT_RGBA32
// initialized inplace to avoid including SDL_pixels.h
static const uint32_t RGBA32 = 376840196;
// Converts an arbitrary Surface to something like the display format // Converts an arbitrary Surface to something like the display format
// (32-bit RGBA), and converts magenta to transparency if convert_magenta is set // (32-bit RGBA), and converts magenta to transparency if convert_magenta is set
// and the source surface didn't already have an alpha channel. // and the source surface didn't already have an alpha channel.
@ -43,13 +40,13 @@ static const uint32_t RGBA32 = 376840196;
// It uses the same pixel format (RGBA, R at lowest address) regardless of // It uses the same pixel format (RGBA, R at lowest address) regardless of
// hardware. // hardware.
SDL_Surface* canonicalize_format(SDL_Surface* src) { SDL_Surface* canonicalize_format(SDL_Surface* src) {
// however we have null check after DFIMG_Load // even though we have null check after DFIMG_Load
// in loadTileset() (the only consumer of this method) // in loadTileset() (the only consumer of this method)
// it's better put nullcheck here as well // it's better put nullcheck here as well
if (!src) if (!src)
return src; return src;
auto fmt = DFSDL_AllocFormat(RGBA32); auto fmt = DFSDL_AllocFormat(SDL_PixelFormatEnum::SDL_PIXELFORMAT_RGBA32);
SDL_Surface* tgt = DFSDL_ConvertSurface(src, fmt, SDL_SWSURFACE); SDL_Surface* tgt = DFSDL_ConvertSurface(src, fmt, SDL_SWSURFACE);
DFSDL_FreeSurface(src); DFSDL_FreeSurface(src);
for (int x = 0; x < tgt->w; ++x) { for (int x = 0; x < tgt->w; ++x) {
@ -85,7 +82,8 @@ static void delete_texture(long texpos) {
// create new surface with RGBA32 format and pixels as data // create new surface with RGBA32 format and pixels as data
SDL_Surface* create_texture(std::vector<uint32_t>& pixels, int texture_px_w, int texture_px_h) { SDL_Surface* create_texture(std::vector<uint32_t>& pixels, int texture_px_w, int texture_px_h) {
auto surface = DFSDL_CreateRGBSurfaceWithFormat(0, texture_px_w, texture_px_h, 32, RGBA32); auto surface = DFSDL_CreateRGBSurfaceWithFormat(0, texture_px_w, texture_px_h, 32,
SDL_PixelFormatEnum::SDL_PIXELFORMAT_RGBA32);
auto canvas_length = static_cast<size_t>(texture_px_w * texture_px_h); auto canvas_length = static_cast<size_t>(texture_px_w * texture_px_h);
for (size_t i = 0; i < pixels.size() && i < canvas_length; i++) { for (size_t i = 0; i < pixels.size() && i < canvas_length; i++) {
uint32_t* p = (uint32_t*)surface->pixels + i; uint32_t* p = (uint32_t*)surface->pixels + i;
@ -186,9 +184,8 @@ void Textures::deleteHandle(TexposHandle handle) {
g_handle_to_texpos.erase(handle); g_handle_to_texpos.erase(handle);
if (g_handle_to_surface.contains(handle)) { if (g_handle_to_surface.contains(handle)) {
auto surface = g_handle_to_surface[handle]; auto surface = g_handle_to_surface[handle];
for (auto i = 0; i < surface->refcount; i++) { while (surface->refcount)
DFSDL_FreeSurface(surface); DFSDL_FreeSurface(surface);
}
g_handle_to_surface.erase(handle); g_handle_to_surface.erase(handle);
} }
} }