diff --git a/example/main.c b/example/main.c index a9f5cb7..f09d680 100644 --- a/example/main.c +++ b/example/main.c @@ -13,6 +13,11 @@ typedef struct mlx_image img; } mlx_t; +#define CIRCLE_RADIUS 50 +#define CIRCLE_DIAMETER (CIRCLE_RADIUS + CIRCLE_RADIUS) + +static int pixels_circle[CIRCLE_DIAMETER * CIRCLE_DIAMETER] = { 0 }; + int update(void* param) { static int i = 0; @@ -51,11 +56,7 @@ int update(void* param) mlx_set_font_scale(mlx->mlx, "default", 8.f); mlx_string_put(mlx->mlx, mlx->win, 210, 175, 0xFFAF2BFF, "hidden"); - for(int j = 0; j < 20; j++) - { - for(int k = 0; k < 20; k++) - mlx_pixel_put(mlx->mlx, mlx->win, 220 + j, 160 + k, 0xFF0000FF); - } + mlx_pixel_put_region(mlx->mlx, mlx->win, 200, 170, CIRCLE_DIAMETER, CIRCLE_DIAMETER, pixels_circle); i++; return 0; @@ -137,6 +138,16 @@ int main(void) int h; int dummy; + int i = 0; + for(int j = 0; j < CIRCLE_DIAMETER; j++) + { + for(int k = 0; k < CIRCLE_DIAMETER; k++, i++) + { + if((CIRCLE_RADIUS - j) * (CIRCLE_RADIUS - j) + (CIRCLE_RADIUS - k) * (CIRCLE_RADIUS - k) < CIRCLE_RADIUS * CIRCLE_RADIUS) + pixels_circle[i] = 0xA10000FF + ((j * k * i) << 8); + } + } + mlx.mlx = mlx_init(); mlx_window_create_info info = { 0 }; diff --git a/includes/mlx_extended.h b/includes/mlx_extended.h index f324d06..942fdd1 100644 --- a/includes/mlx_extended.h +++ b/includes/mlx_extended.h @@ -6,7 +6,7 @@ /* By: maldavid +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2024/12/14 16:17:10 by maldavid #+# #+# */ -/* Updated: 2024/12/15 13:58:58 by maldavid ### ########.fr */ +/* Updated: 2024/12/16 15:06:43 by maldavid ### ########.fr */ /* */ /* ************************************************************************** */ @@ -17,6 +17,7 @@ #define MACROLIB_X_EXTENDED_H #include "mlx.h" +#include #ifdef __cplusplus extern "C" { @@ -84,7 +85,23 @@ MLX_API void mlx_restore_window(mlx_context mlx, mlx_window win); * @param y Y coordinate * @param pixels Array of pixels (coded on 4 bytes in an int, 0xRRGGBBAA) */ -MLX_API void mlx_pixel_put_array(mlx_context mlx, mlx_window win, int x, int y, int* pixels); +MLX_API void mlx_pixel_put_array(mlx_context mlx, mlx_window win, int x, int y, int* pixels, size_t pixels_size); + +/** + * @brief Put a region of pixels in the window + * + * @param mlx Internal MLX application + * @param win Internal window + * @param x X coordinate + * @param y Y coordinate + * @param w Width + * @param h Height + * @param pixels Array of pixels (coded on 4 bytes in an int, 0xRRGGBBAA) + * + * Note: it is responsability of the user to make sure the size of `pixels` is + * big enough for the given region. + */ +MLX_API void mlx_pixel_put_region(mlx_context mlx, mlx_window win, int x, int y, int w, int h, int* pixels); diff --git a/runtime/Includes/Core/Graphics.h b/runtime/Includes/Core/Graphics.h index eeb2820..381061e 100644 --- a/runtime/Includes/Core/Graphics.h +++ b/runtime/Includes/Core/Graphics.h @@ -25,6 +25,8 @@ namespace mlx inline void ResetRenderData(int color) noexcept; inline void PixelPut(int x, int y, int color) noexcept; + inline void PixelPutArray(int x, int y, int* pixels, std::size_t pixels_size) noexcept; + inline void PixelPutRegion(int x, int y, int w, int h, int* pixels) noexcept; inline void StringPut(int x, int y, int, std::string str); inline void TexturePut(NonOwningPtr texture, int x, int y, float scale_x, float scale_y, float angle); diff --git a/runtime/Includes/Core/Graphics.inl b/runtime/Includes/Core/Graphics.inl index f6fa842..eee2785 100644 --- a/runtime/Includes/Core/Graphics.inl +++ b/runtime/Includes/Core/Graphics.inl @@ -30,6 +30,30 @@ namespace mlx } } + void GraphicsSupport::PixelPutArray(int x, int y, int* pixels, std::size_t pixels_size) noexcept + { + MLX_PROFILE_FUNCTION(); + NonOwningPtr texture = m_put_pixel_manager.DrawPixelsArray(x, y, m_draw_layer, pixels, pixels_size); + if(texture) + { + m_pixelput_called = true; + Sprite& new_sprite = p_scene->CreateSprite(texture); + new_sprite.SetPosition(Vec2f{ 0.0f, 0.0f }); + } + } + + void GraphicsSupport::PixelPutRegion(int x, int y, int w, int h, int* pixels) noexcept + { + MLX_PROFILE_FUNCTION(); + NonOwningPtr texture = m_put_pixel_manager.DrawPixelsRegion(x, y, w, h, m_draw_layer, pixels); + if(texture) + { + m_pixelput_called = true; + Sprite& new_sprite = p_scene->CreateSprite(texture); + new_sprite.SetPosition(Vec2f{ 0.0f, 0.0f }); + } + } + void GraphicsSupport::StringPut(int x, int y, int color, std::string str) { MLX_PROFILE_FUNCTION(); diff --git a/runtime/Includes/Graphics/PutPixelManager.h b/runtime/Includes/Graphics/PutPixelManager.h index e630b4c..8283759 100644 --- a/runtime/Includes/Graphics/PutPixelManager.h +++ b/runtime/Includes/Graphics/PutPixelManager.h @@ -12,10 +12,15 @@ namespace mlx // Returns a valid pointer when a new texture has been created NonOwningPtr DrawPixel(int x, int y, std::uint64_t draw_layer, int color); + NonOwningPtr DrawPixelsArray(int x, int y, std::uint64_t draw_layer, int* pixels, std::size_t pixels_size); + NonOwningPtr DrawPixelsRegion(int x, int y, int w, int h, std::uint64_t draw_layer, int* pixels); void ResetRenderData(); ~PutPixelManager() = default; + private: + NonOwningPtr GetLayer(std::uint64_t draw_layer, bool& is_newlayer); + private: std::unordered_map> m_placements; std::vector> m_textures; diff --git a/runtime/Includes/Renderer/Image.h b/runtime/Includes/Renderer/Image.h index 839dfda..1ce6545 100644 --- a/runtime/Includes/Renderer/Image.h +++ b/runtime/Includes/Renderer/Image.h @@ -84,7 +84,10 @@ namespace mlx void Destroy() noexcept override; void SetPixel(int x, int y, int color) noexcept; + void SetRegion(int x, int y, int w, int h, int* pixels) noexcept; + void SetLinearRegion(int x, int y, std::size_t len, int* pixels) noexcept; int GetPixel(int x, int y) noexcept; + void GetRegion(int x, int y, int w, int h, int* dst) noexcept; void Update(VkCommandBuffer cmd); diff --git a/runtime/Includes/Utils/Bits.h b/runtime/Includes/Utils/Bits.h new file mode 100644 index 0000000..0b6ecf2 --- /dev/null +++ b/runtime/Includes/Utils/Bits.h @@ -0,0 +1,16 @@ +#ifndef __MLX_BITS__ +#define __MLX_BITS__ + +namespace mlx +{ + template + constexpr T ByteSwap(T value) noexcept + { + static_assert(std::has_unique_object_representations_v, "T may not have padding bits"); + auto value_representation = std::bit_cast>(value); + std::ranges::reverse(value_representation); + return std::bit_cast(value_representation); + } +} + +#endif diff --git a/runtime/Sources/Core/Bridge.cpp b/runtime/Sources/Core/Bridge.cpp index 2202705..10e8a41 100644 --- a/runtime/Sources/Core/Bridge.cpp +++ b/runtime/Sources/Core/Bridge.cpp @@ -381,12 +381,22 @@ extern "C" gs->GetWindow()->Restore(); } - void mlx_pixel_put_array(mlx_context mlx, mlx_window win, int x, int y, int* pixels) + void mlx_pixel_put_array(mlx_context mlx, mlx_window win, int x, int y, int* pixels, size_t pixels_size) { MLX_CHECK_APPLICATION_POINTER(mlx); mlx::NonOwningPtr gs = mlx->app->GetGraphicsSupport(win); if(!gs) return; + gs->PixelPutArray(x, y, pixels, pixels_size); + } + + void mlx_pixel_put_region(mlx_context mlx, mlx_window win, int x, int y, int w, int h, int* pixels) + { + MLX_CHECK_APPLICATION_POINTER(mlx); + mlx::NonOwningPtr gs = mlx->app->GetGraphicsSupport(win); + if(!gs) + return; + gs->PixelPutRegion(x, y, w, h, pixels); } void mlx_get_image_region(mlx_context mlx, mlx_image image, int x, int y, int w, int h, int* dst) @@ -395,6 +405,7 @@ extern "C" mlx::NonOwningPtr texture = mlx->app->GetTexture(image); if(!texture) return; + texture->GetRegion(x, y, w, h, dst); } void mlx_set_image_region(mlx_context mlx, mlx_image image, int x, int y, int w, int h, int* pixels) @@ -403,6 +414,7 @@ extern "C" mlx::NonOwningPtr texture = mlx->app->GetTexture(image); if(!texture) return; + texture->SetRegion(x, y, w, h, pixels); } void mlx_put_transformed_image_to_window(mlx_context mlx, mlx_window win, mlx_image image, int x, int y, float scale_x, float scale_y, float angle) diff --git a/runtime/Sources/Graphics/PutPixelManager.cpp b/runtime/Sources/Graphics/PutPixelManager.cpp index 1c99b65..9f83490 100644 --- a/runtime/Sources/Graphics/PutPixelManager.cpp +++ b/runtime/Sources/Graphics/PutPixelManager.cpp @@ -7,6 +7,40 @@ namespace mlx { NonOwningPtr PutPixelManager::DrawPixel(int x, int y, std::uint64_t draw_layer, int color) { + MLX_PROFILE_FUNCTION(); + bool is_newlayer; + NonOwningPtr layer = GetLayer(draw_layer, is_newlayer); + if(!layer) + return nullptr; + layer->SetPixel(x, y, color); + return (is_newlayer ? layer : nullptr); + } + + NonOwningPtr PutPixelManager::DrawPixelsArray(int x, int y, std::uint64_t draw_layer, int* pixels, std::size_t pixels_size) + { + MLX_PROFILE_FUNCTION(); + bool is_newlayer; + NonOwningPtr layer = GetLayer(draw_layer, is_newlayer); + if(!layer) + return nullptr; + layer->SetLinearRegion(x, y, pixels_size, pixels); + return (is_newlayer ? layer : nullptr); + } + + NonOwningPtr PutPixelManager::DrawPixelsRegion(int x, int y, int w, int h, std::uint64_t draw_layer, int* pixels) + { + MLX_PROFILE_FUNCTION(); + bool is_newlayer; + NonOwningPtr layer = GetLayer(draw_layer, is_newlayer); + if(!layer) + return nullptr; + layer->SetRegion(x, y, w, h, pixels); + return (is_newlayer ? layer : nullptr); + } + + NonOwningPtr PutPixelManager::GetLayer(std::uint64_t draw_layer, bool& is_newlayer) + { + MLX_PROFILE_FUNCTION(); Verify((bool)p_renderer, "invalid renderer pointer"); VkExtent2D extent; @@ -20,9 +54,10 @@ namespace mlx auto it = m_placements.find(draw_layer); if(it != m_placements.end()) { - it->second->SetPixel(x, y, color); - return nullptr; + is_newlayer = false; + return it->second; } + is_newlayer = true; bool adjusment = false; if(m_current_texture_index >= m_textures.size()) @@ -39,12 +74,11 @@ namespace mlx { m_placements[draw_layer] = m_textures.at(m_current_texture_index - adjusment).get(); m_textures.at(m_current_texture_index - adjusment)->Clear(VK_NULL_HANDLE, Vec4f{ 0.0f }); - m_textures.at(m_current_texture_index - adjusment)->SetPixel(x, y, color); return m_textures.at(m_current_texture_index - adjusment).get(); } catch(...) { - Error("PutPixelManager: invalid texture index; % is not in range of 0-% (internal mlx issue, please report to devs)", m_current_texture_index - 1, m_textures.size()); + Error("PutPixelManager: invalid texture index; % is not in range of 0-% (internal mlx issue, please report to devs)", m_current_texture_index - adjusment, m_textures.size()); return nullptr; } } diff --git a/runtime/Sources/Renderer/Image.cpp b/runtime/Sources/Renderer/Image.cpp index 892629d..24a1348 100644 --- a/runtime/Sources/Renderer/Image.cpp +++ b/runtime/Sources/Renderer/Image.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #define STB_IMAGE_IMPLEMENTATION @@ -215,12 +216,49 @@ namespace mlx if(!m_staging_buffer.has_value()) OpenCPUBuffer(); // Needs to reverse bytes order because why not - unsigned char bytes[4]; - bytes[0] = (color >> 24) & 0xFF; - bytes[1] = (color >> 16) & 0xFF; - bytes[2] = (color >> 8) & 0xFF; - bytes[3] = color & 0xFF; - m_cpu_buffer[(y * m_width) + x] = *reinterpret_cast(bytes); + color = ByteSwap(color); + m_cpu_buffer[(y * m_width) + x] = color; + m_has_been_modified = true; + } + + void Texture::SetRegion(int x, int y, int w, int h, int* pixels) noexcept + { + MLX_PROFILE_FUNCTION(); + if(x < 0 || y < 0 || static_cast(x) > m_width || static_cast(y) > m_height) + return; + if(w < 0 || h < 0) + return; + if(!m_staging_buffer.has_value()) + OpenCPUBuffer(); + for(std::uint32_t i = 0, moving_x = x, moving_y = y;; i++, moving_x++) + { + if(moving_x >= static_cast(x + w) || moving_x >= m_width) + { + moving_x = x; + if(moving_y >= static_cast(y + h) || moving_y >= m_height) + break; + moving_y++; + } + // Needs to reverse bytes order because why not + int color = ByteSwap(pixels[i]); + m_cpu_buffer[(moving_y * m_width) + moving_x] = color; + } + m_has_been_modified = true; + } + + void Texture::SetLinearRegion(int x, int y, std::size_t len, int* pixels) noexcept + { + MLX_PROFILE_FUNCTION(); + if(x < 0 || y < 0 || static_cast(x) > m_width || static_cast(y) > m_height) + return; + if(!m_staging_buffer.has_value()) + OpenCPUBuffer(); + for(std::size_t i = 0; i < len; i++) + { + // Needs to reverse bytes order because why not + int color = ByteSwap(pixels[i]); + m_cpu_buffer[(y * m_width) + x + i] = color; + } m_has_been_modified = true; } @@ -234,6 +272,27 @@ namespace mlx return m_cpu_buffer[(y * m_width) + x]; } + void Texture::GetRegion(int x, int y, int w, int h, int* dst) noexcept + { + MLX_PROFILE_FUNCTION(); + if(x < 0 || y < 0 || static_cast(x) > m_width || static_cast(y) > m_height) + return; + if(!m_staging_buffer.has_value()) + OpenCPUBuffer(); + for(std::uint32_t i = 0, moving_x = x, moving_y = y;; i++, moving_x++) + { + if(moving_x >= static_cast(x + w) || moving_x >= m_width) + { + moving_x = x; + if(moving_y >= static_cast(y + h) || moving_y >= m_height) + break; + moving_y++; + } + // Needs to reverse bytes order because why not + dst[i] = ByteSwap(m_cpu_buffer[(moving_y * m_width) + moving_x]); + } + } + void Texture::Update(VkCommandBuffer cmd) { MLX_PROFILE_FUNCTION();