Skip to content

Commit

Permalink
Added cf_canvas_blit
Browse files Browse the repository at this point in the history
  • Loading branch information
RandyGaul committed Jun 19, 2024
1 parent 0d6399f commit 183ce1e
Show file tree
Hide file tree
Showing 10 changed files with 144 additions and 91 deletions.
1 change: 1 addition & 0 deletions docs/api_reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,7 @@ This is a list of all functions in Cute Framework organized by categories. This
- [cf_backend_type_to_string](/graphics/cf_backend_type_to_string.md)
- [cf_blend_factor_string](/graphics/cf_blend_factor_string.md)
- [cf_blend_op_string](/graphics/cf_blend_op_string.md)
- [cf_canvas_blit](/graphics/cf_canvas_blit.md)
- [cf_canvas_defaults](/graphics/cf_canvas_defaults.md)
- [cf_canvas_get_backend_depth_stencil_handle](/graphics/cf_canvas_get_backend_depth_stencil_handle.md)
- [cf_canvas_get_backend_target_handle](/graphics/cf_canvas_get_backend_target_handle.md)
Expand Down
34 changes: 34 additions & 0 deletions docs/graphics/cf_canvas_blit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
[](../header.md ':include')

# cf_canvas_blit

Category: [graphics](/api_reference?id=graphics)
GitHub: [cute_graphics.h](https://github.com/RandyGaul/cute_framework/blob/master/include/cute_graphics.h)
---

Blits one canvas onto another.

```cpp
void cf_canvas_blit(CF_Canvas src, CF_V2 u0, CF_V2 v0, CF_Canvas dst, CF_V2 u1, CF_V2 v1);
```
Parameters | Description
--- | ---
src | The source texture to copy pixels from.
u0 | The normalized coordinate of the top-left of the source rect.
v0 | The normalized coordinate of the bottom-right of the source rect.
src | The canvas where pixels are copied from.
u1 | The normalized coordinate of the top-left of the destination rect.
v1 | The normalized coordinate of the bottom-right of the destination rect.
dst | The destination canvas where pixels are copied to.
## Remarks
The texture formats of the underlying canvas's must be PIXELFORMAT_DEFAULT. Each u/v coordinate
is normalized, meaning a number from 0 to 1. This lets the function operate on canvas's of any
size. To convert a coordinate to a normalized coordinate, simply divide the x/y of your coordinate
by the width/height of the canvas.
## Related Pages
[CF_Canvas](/graphics/cf_canvas.md)
19 changes: 19 additions & 0 deletions include/cute_graphics.h
Original file line number Diff line number Diff line change
Expand Up @@ -917,6 +917,25 @@ CF_API uint64_t CF_CALL cf_canvas_get_backend_target_handle(CF_Canvas canvas);
*/
CF_API uint64_t CF_CALL cf_canvas_get_backend_depth_stencil_handle(CF_Canvas canvas);

/**
* @function cf_canvas_blit
* @category graphics
* @brief Blits one canvas onto another.
* @param src The source texture to copy pixels from.
* @param u0 The normalized coordinate of the top-left of the source rect.
* @param v0 The normalized coordinate of the bottom-right of the source rect.
* @param src The canvas where pixels are copied from.
* @param u1 The normalized coordinate of the top-left of the destination rect.
* @param v1 The normalized coordinate of the bottom-right of the destination rect.
* @param dst The destination canvas where pixels are copied to.
* @remarks The texture formats of the underlying canvas's must be PIXELFORMAT_DEFAULT. Each u/v coordinate
* is normalized, meaning a number from 0 to 1. This lets the function operate on canvas's of any
* size. To convert a coordinate to a normalized coordinate, simply divide the x/y of your coordinate
* by the width/height of the canvas.
* @related CF_Canvas
*/
CF_API void CF_CALL cf_canvas_blit(CF_Canvas src, CF_V2 u0, CF_V2 v0, CF_Canvas dst, CF_V2 u1, CF_V2 v1);

//--------------------------------------------------------------------------------------------------
// Mesh.

Expand Down
81 changes: 2 additions & 79 deletions samples/draw_to_texture.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,6 @@
#include <cimgui.h>
#include <sokol/sokol_gfx_imgui.h>

#include "draw_to_texture_data/blit_shader.h"

typedef struct Vertex
{
float x, y;
float u, v;
} Vertex;

// UV (0,0) is top-left of the screen, while UV (1,1) is bottom right. We flip the y-axis for UVs to make the y-axis point up.
// Coordinate (-1,1) is top left, while (1,-1) is bottom right.
static void s_quad(float x, float y, float sx, float sy, Vertex quad[6])
{
// Build a quad from (-1.0f,-1.0f) to (1.0f,1.0f).
quad[0].x = -1.0f; quad[0].y = 1.0f; quad[0].u = 0; quad[0].v = 0;
quad[1].x = 1.0f; quad[1].y = -1.0f; quad[1].u = 1; quad[1].v = 1;
quad[2].x = 1.0f; quad[2].y = 1.0f; quad[2].u = 1; quad[2].v = 0;

quad[3].x = -1.0f; quad[3].y = 1.0f; quad[3].u = 0; quad[3].v = 0;
quad[4].x = -1.0f; quad[4].y = -1.0f; quad[4].u = 0; quad[4].v = 1;
quad[5].x = 1.0f; quad[5].y = -1.0f; quad[5].u = 1; quad[5].v = 1;

// Scale the quad about the origin by (sx,sy), then translate it by (x,y).
for (int i = 0; i < 6; ++i) {
quad[i].x = quad[i].x * sx + x;
quad[i].y = quad[i].y * sy + y;
}
}

int main(int argc, char* argv[])
{
float w = 640.0f;
Expand All @@ -44,39 +16,6 @@ int main(int argc, char* argv[])
// Create an offscreen canvas.
CF_Canvas offscreen = cf_make_canvas(cf_canvas_defaults((int)(w*0.5f), (int)(h*0.5f)));

// Create a quad for left view.
CF_Mesh left_quad = cf_make_mesh(CF_USAGE_TYPE_IMMUTABLE, sizeof(Vertex) * 6, 0, 0);
CF_VertexAttribute attrs[2] = { 0 };
attrs[0].name = "in_pos";
attrs[0].format = CF_VERTEX_FORMAT_FLOAT2;
attrs[0].offset = CF_OFFSET_OF(Vertex, x);
attrs[1].name = "in_uv";
attrs[1].format = CF_VERTEX_FORMAT_FLOAT2;
attrs[1].offset = CF_OFFSET_OF(Vertex, u);
cf_mesh_set_attributes(left_quad, attrs, CF_ARRAY_SIZE(attrs), sizeof(Vertex), 0);
Vertex left_quad_verts[6];
s_quad(-0.5f, 0, 0.5f, 0.5f, left_quad_verts);
cf_mesh_update_vertex_data(left_quad, left_quad_verts, 6);

// Create a quad for the right view.
CF_Mesh right_quad = cf_make_mesh(CF_USAGE_TYPE_IMMUTABLE, sizeof(Vertex) * 6, 0, 0);
cf_mesh_set_attributes(right_quad, attrs, CF_ARRAY_SIZE(attrs), sizeof(Vertex), 0);
Vertex right_quad_verts[6];
s_quad(0.5f, 0, 0.5f, 0.5f, right_quad_verts);
cf_mesh_update_vertex_data(right_quad, right_quad_verts, 6);

// Create a quad for the full screen.
CF_Mesh fullscreen_quad = cf_make_mesh(CF_USAGE_TYPE_IMMUTABLE, sizeof(Vertex) * 6, 0, 0);
cf_mesh_set_attributes(fullscreen_quad, attrs, CF_ARRAY_SIZE(attrs), sizeof(Vertex), 0);
Vertex fullscreen_quad_verts[6];
s_quad(0, 0, 1, 1, fullscreen_quad_verts);
cf_mesh_update_vertex_data(fullscreen_quad, fullscreen_quad_verts, 6);

// Setup shader + material for drawing the offscreen canvas onto the screen (blit).
CF_Material blit_material = cf_make_material();
cf_material_set_texture_fs(blit_material, "u_image", cf_canvas_get_target(offscreen));
CF_Shader blit_shader = CF_MAKE_SOKOL_SHADER(blit_shader);

while (cf_app_is_running())
{
cf_app_update(NULL);
Expand All @@ -88,19 +27,8 @@ int main(int argc, char* argv[])
// Fetch this each frame, as it's invalidated during window-resizing.
CF_Canvas app_canvas = cf_app_get_canvas();

// Draw our offscreen texture onto the app's canvas.
cf_apply_canvas(app_canvas, false);
{
// Draw offscreen texture onto the app's canvas on the left.
cf_apply_mesh(left_quad);
cf_apply_shader(blit_shader, blit_material);
cf_draw_elements();

// Also draw the offscreen texture onto the app's canvas on the right.
cf_apply_mesh(right_quad);
cf_apply_shader(blit_shader, blit_material);
cf_draw_elements();
}
cf_canvas_blit(offscreen, cf_v2(0,0), cf_v2(1,1), app_canvas, cf_v2(0.0f,0.25f), cf_v2(0.5f,0.75f));
cf_canvas_blit(offscreen, cf_v2(0,0), cf_v2(1,1), app_canvas, cf_v2(0.5f,0.25f), cf_v2(1.0f,0.75f));

// Show debug views of graphics primitives.
if (igBeginMainMenuBar()) {
Expand All @@ -120,11 +48,6 @@ int main(int argc, char* argv[])
cf_app_draw_onto_screen(false);
}

cf_destroy_shader(blit_shader);
cf_destroy_material(blit_material);
cf_destroy_mesh(left_quad);
cf_destroy_mesh(right_quad);
cf_destroy_mesh(fullscreen_quad);
cf_destroy_canvas(offscreen);
cf_destroy_app();

Expand Down
7 changes: 0 additions & 7 deletions samples/draw_to_texture_data/compile.cmd

This file was deleted.

5 changes: 5 additions & 0 deletions src/cute_app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,11 @@ void cf_destroy_app()
cf_destroy_mesh(app->backbuffer_quad);
cf_destroy_shader(app->backbuffer_shader);
cf_destroy_material(app->backbuffer_material);
if (app->canvas_blit_init) {
cf_destroy_mesh(app->blit_mesh);
cf_destroy_material(app->blit_material);
cf_destroy_shader(app->blit_shader);
}
cf_destroy_graphics();
sg_shutdown();
cf_dx11_shutdown();
Expand Down
84 changes: 79 additions & 5 deletions src/cute_graphics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#include <internal/cute_alloc_internal.h>
#include <internal/cute_app_internal.h>

#include <shaders/blit_shader.h>

// Override sokol_gfx macros for the default clear color with our own values.
// This is a simple way to control sokol's default clear color, as well as custom clear
// colors all in the same place.
Expand Down Expand Up @@ -448,18 +450,90 @@ CF_Texture cf_canvas_get_depth_stencil_target(CF_Canvas canvas_handle)
return canvas->cf_depth_stencil;
}

CF_API uint64_t CF_CALL cf_canvas_get_backend_target_handle(CF_Canvas canvas_handle)
uint64_t cf_canvas_get_backend_target_handle(CF_Canvas canvas_handle)
{
CF_CanvasInternal* canvas = (CF_CanvasInternal*)canvas_handle.id;
return (uint64_t)canvas->texture.id;
}

CF_API uint64_t CF_CALL cf_canvas_get_backend_depth_stencil_handle(CF_Canvas canvas_handle)
uint64_t cf_canvas_get_backend_depth_stencil_handle(CF_Canvas canvas_handle)
{
CF_CanvasInternal* canvas = (CF_CanvasInternal*)canvas_handle.id;
return (uint64_t)canvas->depth_stencil.id;
}

void cf_canvas_blit(CF_Canvas src, CF_V2 u0, CF_V2 v0, CF_Canvas dst, CF_V2 u1, CF_V2 v1)
{
typedef struct Vertex
{
float x, y;
float u, v;
} Vertex;

if (!app->canvas_blit_init) {
app->canvas_blit_init = true;

// Create a full-screen quad mesh.
CF_Mesh blit_mesh = cf_make_mesh(USAGE_TYPE_STREAM, sizeof(Vertex) * 1024, 0, 0);
CF_VertexAttribute attrs[2] = { 0 };
attrs[0].name = "in_pos";
attrs[0].format = CF_VERTEX_FORMAT_FLOAT2;
attrs[0].offset = CF_OFFSET_OF(Vertex, x);
attrs[1].name = "in_uv";
attrs[1].format = CF_VERTEX_FORMAT_FLOAT2;
attrs[1].offset = CF_OFFSET_OF(Vertex, u);
cf_mesh_set_attributes(blit_mesh, attrs, CF_ARRAY_SIZE(attrs), sizeof(Vertex), 0);
app->blit_mesh = blit_mesh;

// Create material + shader for blitting.
CF_Material blit_material = cf_make_material();
cf_material_set_texture_fs(blit_material, "u_image", cf_canvas_get_target(src));
CF_Shader blit_shader = CF_MAKE_SOKOL_SHADER(blit_shader);
app->blit_material = blit_material;
app->blit_shader = blit_shader;
}

// UV (0,0) is top-left of the screen, while UV (1,1) is bottom right. We flip the y-axis for UVs to make the y-axis point up.
// Coordinate (-1,1) is top left, while (1,-1) is bottom right.
auto fill_quad = [](float x, float y, float sx, float sy, v2 u, v2 v, Vertex verts[6])
{
// Build a quad from (-1.0f,-1.0f) to (1.0f,1.0f).
verts[0].x = -1.0f; verts[0].y = 1.0f; verts[0].u = u.x; verts[0].v = u.y;
verts[1].x = 1.0f; verts[1].y = -1.0f; verts[1].u = v.x; verts[1].v = v.y;
verts[2].x = 1.0f; verts[2].y = 1.0f; verts[2].u = v.x; verts[2].v = u.y;

verts[3].x = -1.0f; verts[3].y = 1.0f; verts[3].u = u.x; verts[3].v = u.y;
verts[4].x = -1.0f; verts[4].y = -1.0f; verts[4].u = u.x; verts[4].v = v.y;
verts[5].x = 1.0f; verts[5].y = -1.0f; verts[5].u = v.x; verts[5].v = v.y;

// Scale the quad about the origin by (sx,sy), then translate it by (x,y).
for (int i = 0; i < 6; ++i) {
verts[i].x = verts[i].x * sx + x;
verts[i].y = verts[i].y * sy + y;
}
};

// We're going to blit onto dst.
cf_apply_canvas(dst, false);

// Create a quad where positions come from dst, and UV's come from src.
float w = v1.x - u1.x;
float h = v1.y - u1.y;
float x = (u1.x + v1.x) - 1.0f;
float y = (u1.y + v1.y) - 1.0f;
Vertex verts[6];
fill_quad(x, y, w, h, u0, v0, verts);
cf_mesh_append_vertex_data(app->blit_mesh, verts, 6);

// Read pixels from src.
cf_material_set_texture_fs(app->blit_material, "u_image", cf_canvas_get_target(src));

// Blit onto dst.
cf_apply_mesh(app->blit_mesh);
cf_apply_shader(app->blit_shader, app->blit_material);
cf_draw_elements();
}

CF_Mesh cf_make_mesh(CF_UsageType usage_type, int vertex_buffer_size, int index_buffer_size, int instance_buffer_size)
{
CF_MeshInternal* mesh = (CF_MeshInternal*)CF_CALLOC(sizeof(CF_MeshInternal));
Expand Down Expand Up @@ -521,7 +595,7 @@ void cf_mesh_update_vertex_data(CF_Mesh mesh_handle, void* data, int count)
}
CF_ASSERT(mesh->attribute_count);
if (mesh->need_vertex_sync) {
s_sync_vertex_buffer(mesh, data, size);
s_sync_vertex_buffer(mesh, mesh->usage == SG_USAGE_IMMUTABLE ? data : NULL, size);
} else {
sg_range range = { data, (size_t)size };
sg_update_buffer(mesh->vertices.handle, range);
Expand Down Expand Up @@ -580,7 +654,7 @@ void cf_mesh_update_instance_data(CF_Mesh mesh_handle, void* data, int count)
}
CF_ASSERT(mesh->attribute_count);
if (mesh->need_instance_sync) {
s_sync_isntance_buffer(mesh, data, size);
s_sync_isntance_buffer(mesh, mesh->usage == SG_USAGE_IMMUTABLE ? data : NULL, size);
} else {
sg_range range = { data, (size_t)size };
sg_update_buffer(mesh->instances.handle, range);
Expand Down Expand Up @@ -639,7 +713,7 @@ void cf_mesh_update_index_data(CF_Mesh mesh_handle, uint32_t* indices, int count
}
CF_ASSERT(mesh->attribute_count);
if (mesh->need_index_sync) {
s_sync_index_buffer(mesh, indices, size);
s_sync_index_buffer(mesh, mesh->usage == SG_USAGE_IMMUTABLE ? indices : NULL, size);
} else {
sg_range range = { indices, (size_t)size };
sg_update_buffer(mesh->indices.handle, range);
Expand Down
4 changes: 4 additions & 0 deletions src/internal/cute_app_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ struct CF_App
bool vsync = false;
bool audio_needs_updates = false;
void* update_udata = NULL;
bool canvas_blit_init = false;
CF_Mesh blit_mesh;
CF_Material blit_material;
CF_Shader blit_shader;

// Input stuff.
Cute::Array<char> ime_composition;
Expand Down
File renamed without changes.
File renamed without changes.

0 comments on commit 183ce1e

Please sign in to comment.