Skip to content
Merged
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
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,11 @@

# About

A modern library targetting C++20 and SDL3 for cross-platform, immediate-mode, desktop application development. With *laya*, you can create windows, handle input events, render 2D graphics and manage resources in a type-safe and efficient manner while leveraging the full power of the underlying SDL library.
A modern library targetting C++20 and SDL3 for cross-platform, immediate-mode, desktop application development. With *laya*, you can create windows, handle input events, render 2D graphics, upload textures from surfaces, and manage resources in a type-safe and efficient manner while leveraging the full power of the underlying SDL library.

> **PNG note**: `surface::load_png`/`save_png` and `texture::load_png` require SDL_image integration, which is not yet wired up in this branch. Use BMP helpers or bring your own PNG loader for now. See [Surface docs](docs/features/surfaces.md#file-io) and [Texture docs](docs/features/textures.md#limitations--future-work) for the latest status.

> **Locking note**: Surfaces generally do not require explicit locking in SDL3, but `surface_lock_guard` is available for compatibility. Textures support regional locking through `texture_lock_guard`. The [Rendering docs](docs/features/rendering.md#textures-and-surfaces) outline integration tips and gotchas.

<div align="center">
<image alt="example" src="docs/assets/simple-code-example.png">
Expand Down
28 changes: 28 additions & 0 deletions docs/features/rendering.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,34 @@ while (running) {

---

## Textures and Surfaces

You can upload pixels from an SDL surface, tint them, and draw them like any other primitive. This is useful when loading BMP files (built-in) or PNG files (via SDL_image).

```cpp
laya::context ctx{laya::subsystem::video};
laya::window win{"Textures", {800, 600}};
laya::renderer ren{win};

// Load a surface from disk.
auto surf = laya::surface::load_bmp("assets/logo.bmp");
// Create a texture bound to the renderer.
auto tex = laya::texture::from_surface(ren, surf);
tex.set_color_mod(laya::color{255, 255, 255});

ren.clear(laya::color::black());
ren.render(tex, {100, 100, 256, 256});
ren.present();
```

See the dedicated [Surfaces](surfaces.md) and [Textures](textures.md) feature pages for a deeper dive.

### Limitations

- `surface::load_png` and `surface::save_png` currently throw until SDL_image support is integrated.
- `texture::load_png` mirrors this limitation because it depends on surfaces.
- Regional texture locking is supported, but surfaces generally do not require locking in SDL3; `surface::must_lock()` returns `false` for now.

## Native Handle

Access the underlying SDL renderer for interop:
Expand Down
103 changes: 103 additions & 0 deletions docs/features/surfaces.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Surfaces

CPU-resident pixel buffers for loading images, running software draw routines, and staging texture uploads.

## Creating Surfaces

```cpp
#include <laya/laya.hpp>

laya::surface from_args{laya::surface_args{
.size = {256, 256},
.format = laya::pixel_format::rgba32,
.flags = laya::surface_flags::rle_optimized,
}};

laya::surface from_size{{128, 128}}; // Defaults to RGBA32
laya::surface from_bmp = laya::surface::load_bmp("ui/logo.bmp");
```

> **PNG support** — `surface::load_png`/`save_png` currently throw until SDL_image is wired up. Use BMP helpers or provide your own loader if you need PNG today.

## Filling and Blitting

```cpp
from_args.fill(laya::color::black());
from_args.fill_rect({32, 32, 96, 96}, laya::color{255, 0, 0});

std::array<laya::rect, 2> bars{
laya::rect{0, 0, 64, 256},
laya::rect{192, 0, 64, 256},
};
from_args.fill_rects(bars, laya::color::white());

from_args.blit(from_bmp, {0, 0, 64, 64}, {160, 160, 64, 64});
from_args.blit(from_bmp, {50, 50});
```

## Transformations

Transformations return new `laya::surface` instances, preserving RAII semantics:

```cpp
auto copy = from_bmp.duplicate();
auto converted = from_bmp.convert(laya::pixel_format::bgra32);
auto scaled = from_bmp.scale({512, 512});
auto flipped = from_bmp.flip(laya::flip_mode::horizontal);
```

Scaling currently uses linear filtering; configurable scale modes will arrive with future renderer updates.

## State Management

```cpp
copy.set_alpha_mod(192);
copy.set_color_mod({200, 255, 200});
copy.set_blend_mode(laya::blend_mode::blend);

if (copy.has_color_key()) {
copy.clear_color_key();
}
```

Color key values respect the surface pixel format internally by querying SDL’s format metadata.

## Locking Pixels

Use `surface_lock_guard` for direct pixel access. The guard captures the raw pointer/pitch up front and automatically unlocks when destroyed.

```cpp
{
auto lock = copy.lock();
std::uint8_t* pixels = static_cast<std::uint8_t*>(lock.pixels());
const int pitch = lock.pitch();
// mutate pixels here
} // unlocked automatically
```

SDL3 rarely requires locking for software surfaces; `surface::must_lock()` returns `false` until SDL exposes richer metadata, but the guard keeps the API consistent.

## File IO

```cpp
from_args.save_bmp("out/debug.bmp");
// from_args.save_png("out/debug.png"); // throws until SDL_image support
```

## Integrating with Textures

Surfaces are ideal staging buffers for GPU textures:

```cpp
laya::renderer renderer{window};
auto sprite_surface = laya::surface::load_bmp("assets/sprite.bmp");
auto sprite_texture = laya::texture::from_surface(renderer, sprite_surface);
```

See [Textures](textures.md) for the GPU side of the pipeline.

## Limitations & Future Work

- PNG helpers require SDL_image and will throw until that dependency is integrated.
- Scale mode is fixed to linear filtering; configurable scale modes will be added later.
- `surface_args::flags` currently support `rle_optimized`; additional SDL surface flags can be surfaced if needed.
100 changes: 100 additions & 0 deletions docs/features/textures.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Textures

GPU resources backed by SDL_Renderer for fast blitting, tinting, and scaling.

## Creating Textures

```cpp
#include <laya/laya.hpp>

laya::context ctx{laya::subsystem::video};
laya::window window{"Textures", {800, 600}};
laya::renderer renderer{window};

laya::texture tex_from_args{renderer, laya::texture_args{
.format = laya::pixel_format::rgba32,
.size = {256, 256},
.access = laya::texture_access::streaming,
}};

auto from_surface = laya::texture::from_surface(renderer, laya::surface::load_bmp("sprite.bmp"));
```

> **PNG support** — `texture::load_png` depends on `surface::load_png`, so it currently throws until SDL_image wiring lands.

## Updating Pixels

Upload CPU buffers directly:

```cpp
std::vector<std::uint32_t> pixels(256 * 256, 0xFF00FF00);
tex_from_args.update(pixels.data(), 256 * sizeof(std::uint32_t));

laya::rect region{32, 32, 64, 64};
tex_from_args.update(region, pixels.data(), 256 * sizeof(std::uint32_t));
```

Or lock for streaming writes:

```cpp
{
auto lock = tex_from_args.lock();
auto* row = static_cast<std::uint8_t*>(lock.pixels());
for (int y = 0; y < tex_from_args.size().height; ++y) {
std::fill_n(row, tex_from_args.size().width * 4, 0x7F);
row += lock.pitch();
}
}
```

Regional locking leverages SDL3’s built-in support via `texture::lock(const rect&)`.

## Rendering

Renderer helpers cover common blit/transform combos:

```cpp
laya::renderer ren{window};
ren.clear();
ren.render(from_surface, {100, 100}); // position
ren.render(from_surface, {200, 200, 64, 64}); // destination rect
ren.render(from_surface, {0, 0, 32, 32}, {320, 200, 64, 64}); // src/dst
ren.render(from_surface, {320, 320, 64, 64}, 45.0); // rotation
ren.render(from_surface, {320, 320, 64, 64}, 0.0, {32, 32}); // custom pivot
ren.render(from_surface, {0, 0, 128, 128}, {400, 100, 128, 128},
0.0, {0, 0}, laya::flip_mode::horizontal);
ren.present();
```

All rectangle coordinates convert to `SDL_FRect` internally, so integer inputs stay precise while enabling subpixel rendering.

## Modulation & State

```cpp
from_surface.set_alpha_mod(200);
from_surface.set_color_mod({255, 200, 200});
from_surface.set_blend_mode(laya::blend_mode::blend);
from_surface.set_scale_mode(laya::scale_mode::linear);

auto alpha = from_surface.get_alpha_mod();
auto color = from_surface.get_color_mod();
```

Scale modes map directly to SDL scale filters; `nearest` and `linear` are available today. `SDL_SCALEMODE_BEST` is unavailable in SDL3 and purposely omitted.

## Metadata

Texture instances cache their size/format/access metadata when created, so queries avoid expensive `SDL_GetTextureProperties` calls at runtime:

```cpp
auto size = from_surface.size(); // returns cached dimentions
auto format = from_surface.format();
auto access = from_surface.access();
```

## Limitations & Future Work

- PNG helpers throw until SDL_image integration is complete.
- `texture::from_surface` currently duplicates metadata queries; future updates will streamline this when SDL adds richer creation APIs.
- Renderer helpers currently accept `laya::rect`/`laya::point`; span-based batching is planned for future revisions.
- `texture::load_*` helpers are synchronous; async/background loading is left to higher-level systems.
5 changes: 5 additions & 0 deletions include/laya/laya.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
#include "events/event_types.hpp"
#include "events/event_polling.hpp"
#include "renderers/renderer.hpp"
#include "surfaces/pixel_format.hpp"
#include "surfaces/surface_flags.hpp"
#include "surfaces/surface.hpp"
#include "textures/texture_access.hpp"
#include "textures/texture.hpp"
#include "windows/window.hpp"
#include "subsystems.hpp"
#include "errors.hpp"
Expand Down
31 changes: 31 additions & 0 deletions include/laya/renderers/renderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@
#include "renderer_flags.hpp"
#include "renderer_id.hpp"
#include "renderer_types.hpp"
#include <laya/textures/texture_access.hpp>

struct SDL_Renderer;

namespace laya {

// Forward declarations
class window;
class texture;

// ============================================================================
// Renderer creation arguments
Expand Down Expand Up @@ -193,6 +195,35 @@ class renderer {
/// Fill multiple rectangles with the current draw color
void fill_rects(const rect* rects, int count);

// ========================================================================
// Texture rendering operations
// ========================================================================

/// Render entire texture at destination position
void render(const texture& tex, point dst_pos);

/// Render entire texture to destination rectangle
void render(const texture& tex, const rect& dst_rect);

/// Render texture region to destination rectangle
void render(const texture& tex, const rect& src_rect, const rect& dst_rect);

/// Render texture with rotation around center
void render(const texture& tex, const rect& dst_rect, double angle);

/// Render texture with rotation around specified center point
void render(const texture& tex, const rect& dst_rect, double angle, point center);

/// Render texture with full control (rotation, center, flip)
void render(const texture& tex, const rect& src_rect, const rect& dst_rect, double angle, point center,
flip_mode flip);

/// Render texture with flipping
void render(const texture& tex, const rect& dst_rect, flip_mode flip);

/// Render part of texture with flipping
void render(const texture& tex, const rect& src_rect, const rect& dst_rect, flip_mode flip);

// ========================================================================
// Accessors
// ========================================================================
Expand Down
12 changes: 12 additions & 0 deletions include/laya/renderers/renderer_types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,18 @@ enum class blend_mode : std::uint32_t {
mul = 0x00000008 ///< Color multiplication
};

// ============================================================================
// Flip modes
// ============================================================================

/// Flip modes for surface and texture transformations
/// Maps directly to SDL_FlipMode values for type safety
enum class flip_mode : int {
none = 0, ///< No flipping (SDL_FLIP_NONE)
horizontal = 1, ///< Horizontal flip (SDL_FLIP_HORIZONTAL)
vertical = 2 ///< Vertical flip (SDL_FLIP_VERTICAL)
};

// ============================================================================
// VSync modes
// ============================================================================
Expand Down
22 changes: 22 additions & 0 deletions include/laya/surfaces/pixel_format.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/// @file pixel_format.hpp
/// @brief Pixel format enumeration for surface operations
/// @date 2025-12-10

#pragma once

#include <cstdint>

namespace laya {

/// Pixel format enumeration for surfaces and textures
enum class pixel_format : std::uint32_t {
unknown = 0,
rgba32 = 0x16462004, ///< SDL_PIXELFORMAT_RGBA32 - 32-bit RGBA format
argb32 = 0x16362004, ///< SDL_PIXELFORMAT_ARGB32 - 32-bit ARGB format
bgra32 = 0x16762004, ///< SDL_PIXELFORMAT_BGRA32 - 32-bit BGRA format
abgr32 = 0x16662004, ///< SDL_PIXELFORMAT_ABGR32 - 32-bit ABGR format
rgb24 = 0x17101803, ///< SDL_PIXELFORMAT_RGB24 - 24-bit RGB format
bgr24 = 0x17401803 ///< SDL_PIXELFORMAT_BGR24 - 24-bit BGR format
};

} // namespace laya
Loading