Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions scripts/test-macos-framework.sh
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ if [ -n "$PROJECTM_FRAMEWORK" ] && [ -d "$PROJECTM_FRAMEWORK" ]; then
"memory.h"
"parameters.h"
"render_opengl.h"
"textures.h"
"touch.h"
"types.h"
"user_sprites.h"
Expand Down
3 changes: 2 additions & 1 deletion src/api/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ set(PROJECTM_PUBLIC_HEADERS
"${CMAKE_CURRENT_SOURCE_DIR}/include/projectM-4/parameters.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/projectM-4/projectM.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/projectM-4/render_opengl.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/projectM-4/textures.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/projectM-4/touch.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/projectM-4/types.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/projectM-4/user_sprites.h"
Expand Down Expand Up @@ -79,4 +80,4 @@ if(ENABLE_INSTALL)
PUBLIC_HEADER DESTINATION "${PROJECTM_INCLUDE_DIR}/projectM-4" COMPONENT Devel
)

endif()
endif()
169 changes: 104 additions & 65 deletions src/api/include/projectM-4/textures.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,84 +33,121 @@ extern "C" {
#endif

/**
* Placeholder values that can be used to address channel indices in PCM data arrays.
* @brief Loads a texture from raw, uncompressed pixel data (RGB or RGBA).
*
* The data buffer must contain at least width * height * channels bytes of pixel data in standard
* OpenGL format (first row is the bottom of the image).
*
* This function can be called at any time to push or pre-load textures, or from within the
* texture load event callback.
*
* @param instance The projectM instance handle.
* @param texture_name The unqualified texture name (without wrap/filter prefixes). Case-insensitive.
* @param data Pointer to raw pixel data. Must remain valid until this function returns.
* @param width Width of the texture in pixels. Must be between 1 and 8192.
* @param height Height of the texture in pixels. Must be between 1 and 8192.
* @param channels Number of color channels (3 for RGB, 4 for RGBA).
* @return true if the texture was loaded successfully, false on error or if a texture with this
* name already exists.
* @since 4.2.0
*/
typedef enum
{
PROJECTM_TEXTURE_RAW = 0, //!< Load raw 3/4 channel pixel data in standard OpenGL format (first row is bottom of image).
PROJECTM_TEXTURE_GL_TEX_ID = 1, //!< Specify an existing OpenGL texture ID to use.
/**
* Pass a compressed/plain image file and let projectM decompress/decode it.
* Supported formats are: JPG, PNG, BMP, TGA, DXT and DDS, while GIF and
* PSD are partially supported.
*/
PROJECTM_TEXTURE_COMPRESSED_FILE = 2
} projectm_texture_load_type;
PROJECTM_EXPORT bool projectm_texture_load_raw_data(projectm_handle instance,
const char* texture_name,
const unsigned char* data,
unsigned int width,
unsigned int height,
unsigned int channels);

/**
* @brief Structure containing texture data returned by the texture load callback.
* @brief Loads a texture from an existing OpenGL texture ID.
*
* projectM will use the texture but will @b not take ownership of it. The application is
* responsible for deleting the texture after projectM no longer references it. Use the
* texture unload event callback to get notified when the texture is no longer needed.
*
* Applications can provide texture data in one of three ways:
* 1. Raw pixel data: Set data to a valid pointer, width/height to the dimensions,
* and channels to the number of color channels (3 for RGB, 4 for RGBA).
* 2. Existing OpenGL texture: Set texture_id to a valid OpenGL texture ID.
* 3. Data read from a supported image file type, see @a PROJECTM_TEXTURE_COMPRESSED_FILE for
* details. projectM will internally use stb_image to load the textures.
* This function can be called at any time to push or pre-load textures, or from within the
* texture load event callback.
*
* If no image or texture ID is provided for the given type(data is NULL or texture_id is 0), or
* image loading fails, projectM will attempt to load the texture from the filesystem as usual.
* @param instance The projectM instance handle.
* @param texture_name The unqualified texture name (without wrap/filter prefixes). Case-insensitive.
* @param texture_id A valid OpenGL texture ID.
* @param width Width of the texture in pixels. Must be between 1 and 8192.
* @param height Height of the texture in pixels. Must be between 1 and 8192.
* @return true if the texture was loaded successfully, false on error or if a texture with this
* name already exists.
* @since 4.2.0
*/
PROJECTM_EXPORT bool projectm_texture_load_gl_texture(projectm_handle instance,
const char* texture_name,
unsigned int texture_id,
unsigned int width,
unsigned int height);

/**
* @brief Loads a texture from compressed/encoded image file data.
*
* After
* Pass the raw contents of a supported image file (e.g. the bytes read from a JPG, PNG, BMP,
* TGA, DXT or DDS file) and projectM will decode it internally using stb_image.
* GIF and PSD are partially supported.
*
* @warning When providing a texture_id, projectM takes ownership of the OpenGL texture
* and will delete it (via glDeleteTextures) when it is no longer needed. Do not
* delete the texture yourself or reuse the texture ID after passing it here.
* This function can be called at any time to push or pre-load textures, or from within the
* texture load event callback.
*
* @param instance The projectM instance handle.
* @param texture_name The unqualified texture name (without wrap/filter prefixes). Case-insensitive.
* @param data Pointer to the image file contents. Must remain valid until this function returns.
* @param data_length Length of the image data buffer in bytes.
* @return true if the texture was loaded and decoded successfully, false on error or if a texture
* with this name already exists.
* @since 4.2.0
*/
typedef struct projectm_texture_load_data {
projectm_texture_load_type type; //!< The format of the passed-in texture.
const unsigned char* data; //!< Pointer to raw pixel or compressed image data.
unsigned int width; //!< Width of the texture in pixels. Must be > 0 when providing data or texture_id. */
unsigned int height; //!< Height of the texture in pixels. Must be > 0 when providing data or texture_id. */
unsigned int channels; //!< Number of color channels (3 for RGB, 4 for RGBA). */
unsigned int texture_id; //!< An existing OpenGL texture ID to use. Only used if type is @a PROJECTM_TEXTURE_GL_TEX_ID, ignored otherwise. */
} projectm_texture_load_data;
PROJECTM_EXPORT bool projectm_texture_load_compressed_image(projectm_handle instance,
const char* texture_name,
const unsigned char* data,
size_t data_length);

PROJECTM_EXPORT bool projectm_load_texture(projectm_handle instance,
const char* texture_name,
const projectm_texture_load_data* texture_data);
/**
* @brief Unloads a previously loaded external texture.
*
* If the texture is currently in use by an active preset, it will remain in memory until the
* preset is unloaded. When manually unloading a texture passed by OpenGL ID, the application
* should wait one or more preset switches before deleting the actual GL texture to avoid
* rendering issues.
*
* @param instance The projectM instance handle.
* @param texture_name The unqualified texture name as used in the load call. Case-insensitive.
* @return true if the texture was found and scheduled for removal, false if no texture with the
* given name was found.
* @since 4.2.0
*/
PROJECTM_EXPORT bool projectm_texture_unload(projectm_handle instance,
const char* texture_name);

/**
* @brief Callback function that is executed when projectM needs to load a texture.
*
* This callback allows applications to provide textures from sources other than
* the filesystem, such as:
* - Loading textures from archives (e.g., ZIP files)
* - Loading textures over the network
* - Generating textures procedurally
* - Providing pre-loaded textures or video frames
*
* When called, the application should populate the provided data structure with
* either raw pixel data or an OpenGL texture ID. If the application cannot provide
* the requested texture, it should leave the structure unchanged (data = NULL,
* texture_id = 0) and projectM will fall back to loading from the filesystem.
*
* @note The texture_name pointer is only valid inside the callback. Make a copy if
* it needs to be retained for later use.
* @note If providing raw pixel data, the data pointer must remain valid until
* projectM has finished processing it (i.e., until the callback returns).
* @note This callback is always invoked from the same thread that calls projectM
* rendering functions. No additional synchronization is required.
* This callback allows applications to provide textures from sources other than the filesystem,
* such as archives, network resources, procedural generators, or pre-loaded data.
*
* When called, the application should load the requested texture by calling one of the texture
* loading functions (@a projectm_texture_load_raw_data, @a projectm_texture_load_gl_texture, or
* @a projectm_texture_load_compressed_image) and return true. If the application cannot provide
* the requested texture, it should return false and projectM will fall back to loading from
* the filesystem.
*
* @note The texture_name pointer is only valid inside the callback. Make a copy if it needs to
* be retained for later use.
* @note This callback is always invoked from the same thread that calls projectM rendering
* functions. No additional synchronization is required.
*
* @param instance The projectM instance handle. Can be used to call texture loading functions.
* @param texture_name The name of the texture being requested, as used in the preset.
* @param[out] data Pointer to a structure where the application should place texture data.
* @param user_data A user-defined data pointer that was provided when registering the callback.
* @return true if the application loaded the texture, false to fall back to filesystem loading.
* @since 4.2.0
*/
typedef void (*projectm_texture_load_event)(const char* texture_name,
projectm_texture_load_data* data,
typedef bool (*projectm_texture_load_event)(projectm_handle instance,
const char* texture_name,
void* user_data);

/**
Expand All @@ -130,23 +167,25 @@ PROJECTM_EXPORT void projectm_set_texture_load_event_callback(projectm_handle in
void* user_data);

/**
* @brief Sets a callback function that will be called after projectM unloaded a texture.
* @brief Callback function that will be called after projectM unloads a texture.
*
* This callback will inform the application that a texture with a given name was removed from
* projectM's texture manager. This callback is only useful when passing a texture ID to projectM,
* as this enabled the application to know when this specific texture is no longer needed and can
* be deleted (or at least doesn't require regular updating anymore).
* @param texture_name The name of the texture being requested, as used in the preset.
* This callback informs the application that a texture with the given name was removed from
* projectM's texture manager. This is particularly useful when passing a GL texture ID to
* projectM, as it lets the application know when the texture is no longer needed and can be
* safely deleted (or at least no longer requires regular updating).
*
* @note This callback is not fired for textures loaded from the filesystem by projectM itself.
*
* @param texture_name The name of the texture that was unloaded.
* @param user_data A user-defined data pointer that was provided when registering the callback.
* @since 4.2.0
*/
typedef void (*projectm_texture_unload_event)(const char* texture_name,
void* user_data);

/**
* @brief Sets a callback function that will be called when projectM needs to load a texture.
* @brief Sets a callback function that will be called when projectM unloads a texture.
*
* This allows applications to provide textures from non-filesystem sources.
* Only one callback can be registered per projectM instance. To remove the callback, use NULL.
*
* @param instance The projectM instance handle.
Expand Down
4 changes: 2 additions & 2 deletions src/libprojectM/MilkdropPreset/BlurTexture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ BlurTexture::BlurTexture()
textureName = "blur" + std::to_string(i / 2 + 1);
}

m_blurTextures[i] = std::make_shared<Renderer::Texture>(textureName, 0, GL_TEXTURE_2D, 0, 0, false);
m_blurTextures[i] = std::make_shared<Renderer::Texture>(textureName, 0, GL_TEXTURE_2D, 0, 0, Renderer::Texture::Source::Internal);
}
}

Expand Down Expand Up @@ -370,7 +370,7 @@ void BlurTexture::AllocateTextures(const Renderer::Texture& sourceTexture)
}

// This will automatically replace any old texture.
m_blurTextures[i] = std::make_shared<Renderer::Texture>(textureName, width2, height2, false);
m_blurTextures[i] = std::make_shared<Renderer::Texture>(textureName, width2, height2, Renderer::Texture::Source::Internal);
}

m_sourceTextureWidth = sourceTexture.Width();
Expand Down
57 changes: 57 additions & 0 deletions src/libprojectM/ProjectM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ void ProjectM::SetTexturePaths(std::vector<std::string> texturePaths)
{
m_textureManager->SetTextureLoadCallback(m_textureLoadCallback);
}
if (m_textureUnloadCallback)
{
m_textureManager->SetTextureUnloadCallback(m_textureUnloadCallback);
}
}

void ProjectM::ResetTextures()
Expand All @@ -102,6 +106,10 @@ void ProjectM::ResetTextures()
{
m_textureManager->SetTextureLoadCallback(m_textureLoadCallback);
}
if (m_textureUnloadCallback)
{
m_textureManager->SetTextureUnloadCallback(m_textureUnloadCallback);
}
}

void ProjectM::SetTextureLoadCallback(Renderer::TextureLoadCallback callback)
Expand All @@ -113,6 +121,55 @@ void ProjectM::SetTextureLoadCallback(Renderer::TextureLoadCallback callback)
}
}

void ProjectM::SetTextureUnloadCallback(Renderer::TextureUnloadCallback callback)
{
m_textureUnloadCallback = std::move(callback);
if (m_textureManager)
{
m_textureManager->SetTextureUnloadCallback(m_textureUnloadCallback);
}
}

auto ProjectM::LoadExternalTextureRaw(const std::string& name, const uint8_t* data,
uint32_t width, uint32_t height, uint32_t channels) -> bool
{
if (!m_textureManager)
{
return false;
}
return m_textureManager->LoadExternalTextureRaw(name, data, width, height, channels);
}

auto ProjectM::LoadExternalTextureID(const std::string& name, uint32_t textureId,
uint32_t width, uint32_t height) -> bool
{
if (!m_textureManager)
{
return false;
}
// Pass 4 channels as default for memory estimation when using GL texture IDs
return m_textureManager->LoadExternalTextureID(name, textureId, width, height, 4);
}

auto ProjectM::LoadExternalTextureFile(const std::string& name, const uint8_t* data,
size_t dataLength) -> bool
{
if (!m_textureManager)
{
return false;
}
return m_textureManager->LoadExternalTextureFile(name, data, dataLength);
}

auto ProjectM::UnloadExternalTexture(const std::string& name) -> bool
{
if (!m_textureManager)
{
return false;
}
return m_textureManager->UnloadExternalTexture(name);
}

void ProjectM::RenderFrame(uint32_t targetFramebufferObject /*= 0*/)
{
// Don't render if window area is zero.
Expand Down
48 changes: 48 additions & 0 deletions src/libprojectM/ProjectM.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,53 @@ class PROJECTM_CXX_EXPORT ProjectM
*/
void SetTextureLoadCallback(Renderer::TextureLoadCallback callback);

/**
* @brief Sets a callback function for notifying when textures are unloaded.
* @param callback The callback function, or nullptr to disable.
*/
void SetTextureUnloadCallback(Renderer::TextureUnloadCallback callback);

/**
* @brief Loads a texture from raw, uncompressed pixel data.
* @param name The texture name (case-insensitive).
* @param data Pointer to raw pixel data (RGB or RGBA).
* @param width Width in pixels.
* @param height Height in pixels.
* @param channels Number of color channels (3 or 4).
* @return true if loaded successfully.
*/
auto LoadExternalTextureRaw(const std::string& name, const uint8_t* data,
uint32_t width, uint32_t height, uint32_t channels) -> bool;

/**
* @brief Loads a texture from an existing OpenGL texture ID.
* projectM will not take ownership of the texture.
* @param name The texture name (case-insensitive).
* @param textureId A valid OpenGL texture ID.
* @param width Width in pixels.
* @param height Height in pixels.
* @return true if loaded successfully.
*/
auto LoadExternalTextureID(const std::string& name, uint32_t textureId,
uint32_t width, uint32_t height) -> bool;

/**
* @brief Loads a texture from compressed/encoded image file data.
* @param name The texture name (case-insensitive).
* @param data Pointer to the image file contents.
* @param dataLength Length of the data buffer in bytes.
* @return true if loaded and decoded successfully.
*/
auto LoadExternalTextureFile(const std::string& name, const uint8_t* data,
size_t dataLength) -> bool;

/**
* @brief Unloads a previously loaded external texture.
* @param name The texture name (case-insensitive).
* @return true if the texture was found and removed.
*/
auto UnloadExternalTexture(const std::string& name) -> bool;

void RenderFrame(uint32_t targetFramebufferObject = 0);

/**
Expand Down Expand Up @@ -311,6 +358,7 @@ class PROJECTM_CXX_EXPORT ProjectM

std::vector<std::string> m_textureSearchPaths; ///!< List of paths to search for texture files
Renderer::TextureLoadCallback m_textureLoadCallback; //!< Optional callback for loading textures from non-filesystem sources.
Renderer::TextureUnloadCallback m_textureUnloadCallback; //!< Optional callback for notifying when textures are unloaded.

/** Timing information */
int m_frameCount{0}; //!< Rendered frame count since start
Expand Down
Loading
Loading