diff --git a/examples/image/build.bat b/examples/image/build.bat index 004a30f..a2f39d7 100644 --- a/examples/image/build.bat +++ b/examples/image/build.bat @@ -1,4 +1,4 @@ set INCLUDES=/I ..\..\src /I ..\..\src\util /I ..\..\src\platform /I ../../ext /I ../../ext/angle_headers -cl /we4013 /Zi /Zc:preprocessor /std:c11 %INCLUDES% main.c /link /LIBPATH:../../bin milepost.dll.lib /out:../../bin/example_canvas.exe +cl /we4013 /Zi /Zc:preprocessor /std:c11 %INCLUDES% main.c /link /LIBPATH:../../bin milepost.dll.lib /out:../../bin/example_image.exe diff --git a/examples/image/main.c b/examples/image/main.c index 329e09b..5ba7979 100644 --- a/examples/image/main.c +++ b/examples/image/main.c @@ -95,7 +95,7 @@ int main() mg_matrix_pop(); - mg_image_draw(image2, (mp_rect){300, 200, 300, 300}); + mg_image_draw(image, (mp_rect){300, 200, 300, 300}); mg_flush(); mg_surface_present(surface); diff --git a/src/gl_canvas.c b/src/gl_canvas.c index b260922..815883f 100644 --- a/src/gl_canvas.c +++ b/src/gl_canvas.c @@ -39,6 +39,13 @@ typedef struct mg_gl_canvas_backend } mg_gl_canvas_backend; +typedef struct mg_gl_image +{ + mg_image_data interface; + + GLuint textureID; +} mg_gl_image; + //NOTE: debugger typedef struct debug_vertex { @@ -66,6 +73,8 @@ enum { LAYOUT_VEC4_ALIGN = 16, LAYOUT_INT_SIZE = 4, LAYOUT_INT_ALIGN = 4, + LAYOUT_MAT2x3_SIZE = sizeof(float)*6, + LAYOUT_MAT2x3_ALIGN = 4, LAYOUT_CUBIC_OFFSET = 0, LAYOUT_POS_OFFSET = LayoutNext(CUBIC, VEC4, VEC2), @@ -75,9 +84,9 @@ enum { LAYOUT_COLOR_OFFSET = 0, LAYOUT_CLIP_OFFSET = LayoutNext(COLOR, VEC4, VEC4), - LAYOUT_UV_OFFSET = LayoutNext(CLIP, VEC4, VEC2), + LAYOUT_UV_TRANSFORM_OFFSET = LayoutNext(CLIP, VEC4, MAT2x3), LAYOUT_SHAPE_ALIGN = 16, - LAYOUT_SHAPE_SIZE = LayoutNext(UV, VEC2, SHAPE), + LAYOUT_SHAPE_SIZE = LayoutNext(UV_TRANSFORM, MAT2x3, SHAPE), MG_GL_CANVAS_MAX_BUFFER_LENGTH = 1<<20, MG_GL_CANVAS_MAX_SHAPE_BUFFER_SIZE = LAYOUT_SHAPE_SIZE * MG_GL_CANVAS_MAX_BUFFER_LENGTH, @@ -108,8 +117,8 @@ void mg_gl_canvas_update_vertex_layout(mg_gl_canvas_backend* backend) .colorStride = LAYOUT_SHAPE_SIZE, .clipBuffer = backend->shapeMapping + LAYOUT_CLIP_OFFSET, .clipStride = LAYOUT_SHAPE_SIZE, - .uvBuffer = backend->shapeMapping + LAYOUT_UV_OFFSET, - .uvStride = LAYOUT_SHAPE_SIZE, + .uvTransformBuffer = backend->shapeMapping + LAYOUT_UV_TRANSFORM_OFFSET, + .uvTransformStride = LAYOUT_SHAPE_SIZE, .indexBuffer = backend->indexMapping, .indexStride = LAYOUT_INT_SIZE}; @@ -147,7 +156,7 @@ void mg_gl_canvas_clear(mg_canvas_backend* interface, mg_color clearColor) glClear(GL_COLOR_BUFFER_BIT); } -void mg_gl_canvas_draw_batch(mg_canvas_backend* interface, u32 shapeCount, u32 vertexCount, u32 indexCount) +void mg_gl_canvas_draw_batch(mg_canvas_backend* interface, mg_image_data* imageInterface, u32 shapeCount, u32 vertexCount, u32 indexCount) { mg_gl_canvas_backend* backend = (mg_gl_canvas_backend*)interface; @@ -213,6 +222,19 @@ void mg_gl_canvas_draw_batch(mg_canvas_backend* interface, u32 shapeCount, u32 v glUniform1ui(3, tileArrayLength); glUniform2f(4, contentsScaling.x, contentsScaling.y); + if(imageInterface) + { + //TODO: make sure this image belongs to that context + mg_gl_image* image = (mg_gl_image*)imageInterface; + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, image->textureID); + glUniform1ui(5, 1); + } + else + { + glUniform1ui(5, 0); + } + glDispatchCompute(tileCountX, tileCountY, 1); //NOTE: now blit out texture to surface @@ -248,9 +270,41 @@ void mg_gl_canvas_destroy(mg_canvas_backend* interface) free(backend); } -void mg_gl_canvas_atlas_upload(mg_canvas_backend* interface, mp_rect rect, u8* bytes) +mg_image_data* mg_gl_canvas_image_create(mg_canvas_backend* interface, vec2 size) { - //TODO + mg_gl_image* image = 0; + + image = malloc_type(mg_gl_image); + if(image) + { + glGenTextures(1, &image->textureID); + glBindTexture(GL_TEXTURE_2D, image->textureID); +// glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, size.x, size.y); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + image->interface.size = size; + } + return((mg_image_data*)image); +} + +void mg_gl_canvas_image_destroy(mg_canvas_backend* interface, mg_image_data* imageInterface) +{ + //TODO: check that this image belongs to this context + mg_gl_image* image = (mg_gl_image*)imageInterface; + glDeleteTextures(1, &image->textureID); + free(image); +} + +void mg_gl_canvas_image_upload_region(mg_canvas_backend* interface, + mg_image_data* imageInterface, + mp_rect region, + u8* pixels) +{ + //TODO: check that this image belongs to this context + mg_gl_image* image = (mg_gl_image*)imageInterface; + glBindTexture(GL_TEXTURE_2D, image->textureID); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, region.w, region.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); } static int mg_gl_compile_shader(const char* name, GLuint shader, const char* source) @@ -375,7 +429,9 @@ mg_canvas_backend* mg_gl_canvas_create(mg_surface surface) backend->interface.end = mg_gl_canvas_end; backend->interface.clear = mg_gl_canvas_clear; backend->interface.drawBatch = mg_gl_canvas_draw_batch; - backend->interface.atlasUpload = mg_gl_canvas_atlas_upload; + backend->interface.imageCreate = mg_gl_canvas_image_create; + backend->interface.imageDestroy = mg_gl_canvas_image_destroy; + backend->interface.imageUploadRegion = mg_gl_canvas_image_upload_region; mg_surface_prepare(surface); diff --git a/src/glsl_shaders.h b/src/glsl_shaders.h index d0d0fca..3d0168d 100644 --- a/src/glsl_shaders.h +++ b/src/glsl_shaders.h @@ -2,7 +2,7 @@ * * file: glsl_shaders.h * note: string literals auto-generated by embed_text.py -* date: 22/022023 +* date: 27/022023 * **********************************************************************/ #ifndef __GLSL_SHADERS_H__ @@ -23,7 +23,7 @@ const char* glsl_common = "struct shape {\n" " vec4 color;\n" " vec4 clip;\n" -" vec2 uv;\n" +" float uvTransform[6];\n" "};\n"; //NOTE: string imported from src\glsl_shaders\blit_vertex.glsl @@ -253,9 +253,13 @@ const char* glsl_draw = "layout(location = 2) uniform uint tileSize;\n" "layout(location = 3) uniform uint tileArraySize;\n" "layout(location = 4) uniform vec2 scaling;\n" +"layout(location = 5) uniform uint useTexture;\n" "\n" "layout(rgba8, binding = 0) uniform restrict writeonly image2D outTexture;\n" "\n" +"layout(binding = 1) uniform sampler2D srcTexture;\n" +"\n" +"\n" "bool is_top_left(ivec2 a, ivec2 b)\n" "{\n" " return( (a.y == b.y && b.x < a.x)\n" @@ -363,6 +367,16 @@ const char* glsl_draw = " vec4 color = shapeBuffer.elements[shapeIndex].color;\n" " ivec4 clip = ivec4(round((shapeBuffer.elements[shapeIndex].clip * vec4(scaling, scaling) + vec4(0.5, 0.5, 0.5, 0.5)) * subPixelFactor));\n" "\n" +" mat3 uvTransform = mat3(shapeBuffer.elements[shapeIndex].uvTransform[0],\n" +" shapeBuffer.elements[shapeIndex].uvTransform[3],\n" +" 0.,\n" +" shapeBuffer.elements[shapeIndex].uvTransform[1],\n" +" shapeBuffer.elements[shapeIndex].uvTransform[4],\n" +" 0.,\n" +" shapeBuffer.elements[shapeIndex].uvTransform[2],\n" +" shapeBuffer.elements[shapeIndex].uvTransform[5],\n" +" 1.);\n" +"\n" " //NOTE(martin): reorder triangle counter-clockwise and compute bias for each edge\n" " int cw = is_clockwise(p0, p1, p2);\n" " if(cw < 0)\n" @@ -417,7 +431,15 @@ const char* glsl_draw = " {\n" " sampleColor[sampleIndex] = currentColor[sampleIndex];\n" " }\n" -" currentColor[sampleIndex] = sampleColor[sampleIndex]*(1.-color.a) + color.a*color;\n" +"\n" +" vec4 nextColor = color;\n" +" if(useTexture)\n" +" {\n" +" vec3 sampleFP = vec3(vec2(samplePoint).xy/(subPixelFactor*2.), 1);\n" +" vec2 uv = (uvTransform * sampleFP).xy;\n" +" nextColor *= texture(srcTexture, uv);\n" +" }\n" +" currentColor[sampleIndex] = sampleColor[sampleIndex]*(1.-nextColor.a) + nextColor.a*nextColor;\n" " currentShapeIndex[sampleIndex] = shapeIndex;\n" " flipCount[sampleIndex] = 1;\n" " }\n" diff --git a/src/glsl_shaders/common.glsl b/src/glsl_shaders/common.glsl index 0d96432..d042b9a 100644 --- a/src/glsl_shaders/common.glsl +++ b/src/glsl_shaders/common.glsl @@ -10,5 +10,5 @@ struct vertex { struct shape { vec4 color; vec4 clip; - vec2 uv; + float uvTransform[6]; }; diff --git a/src/glsl_shaders/draw.glsl b/src/glsl_shaders/draw.glsl index 8c51699..a726bc9 100644 --- a/src/glsl_shaders/draw.glsl +++ b/src/glsl_shaders/draw.glsl @@ -30,9 +30,13 @@ layout(location = 1) uniform uvec2 tileCount; layout(location = 2) uniform uint tileSize; layout(location = 3) uniform uint tileArraySize; layout(location = 4) uniform vec2 scaling; +layout(location = 5) uniform uint useTexture; layout(rgba8, binding = 0) uniform restrict writeonly image2D outTexture; +layout(binding = 1) uniform sampler2D srcTexture; + + bool is_top_left(ivec2 a, ivec2 b) { return( (a.y == b.y && b.x < a.x) @@ -140,6 +144,16 @@ void main() vec4 color = shapeBuffer.elements[shapeIndex].color; ivec4 clip = ivec4(round((shapeBuffer.elements[shapeIndex].clip * vec4(scaling, scaling) + vec4(0.5, 0.5, 0.5, 0.5)) * subPixelFactor)); + mat3 uvTransform = mat3(shapeBuffer.elements[shapeIndex].uvTransform[0], + shapeBuffer.elements[shapeIndex].uvTransform[3], + 0., + shapeBuffer.elements[shapeIndex].uvTransform[1], + shapeBuffer.elements[shapeIndex].uvTransform[4], + 0., + shapeBuffer.elements[shapeIndex].uvTransform[2], + shapeBuffer.elements[shapeIndex].uvTransform[5], + 1.); + //NOTE(martin): reorder triangle counter-clockwise and compute bias for each edge int cw = is_clockwise(p0, p1, p2); if(cw < 0) @@ -194,7 +208,15 @@ void main() { sampleColor[sampleIndex] = currentColor[sampleIndex]; } - currentColor[sampleIndex] = sampleColor[sampleIndex]*(1.-color.a) + color.a*color; + + vec4 nextColor = color; + if(useTexture) + { + vec3 sampleFP = vec3(vec2(samplePoint).xy/(subPixelFactor*2.), 1); + vec2 uv = (uvTransform * sampleFP).xy; + nextColor *= texture(srcTexture, uv); + } + currentColor[sampleIndex] = sampleColor[sampleIndex]*(1.-nextColor.a) + nextColor.a*nextColor; currentShapeIndex[sampleIndex] = shapeIndex; flipCount[sampleIndex] = 1; } diff --git a/src/graphics.c b/src/graphics.c index e4e2c53..a96832f 100644 --- a/src/graphics.c +++ b/src/graphics.c @@ -2749,6 +2749,9 @@ mg_canvas mg_canvas_create(mg_surface surface) canvas = mg_canvas_alloc_handle(canvasData); mg_canvas_set_current(canvas); + + //TODO: move that in mg_canvas_set_current() if needed? + mg_surface_prepare(surface); } } return(canvas); diff --git a/src/graphics_internal.h b/src/graphics_internal.h index 387b9c1..395e280 100644 --- a/src/graphics_internal.h +++ b/src/graphics_internal.h @@ -94,13 +94,19 @@ typedef void (*mg_canvas_backend_destroy_proc)(mg_canvas_backend* backend); typedef void (*mg_canvas_backend_begin_proc)(mg_canvas_backend* backend); typedef void (*mg_canvas_backend_end_proc)(mg_canvas_backend* backend); typedef void (*mg_canvas_backend_clear_proc)(mg_canvas_backend* backend, mg_color clearColor); -typedef void (*mg_canvas_backend_draw_batch_proc)(mg_canvas_backend* backend, mg_image_data* imageData, u32 vertexCount, u32 shapeCount, u32 indexCount); +typedef void (*mg_canvas_backend_draw_batch_proc)(mg_canvas_backend* backend, + mg_image_data* imageData, + u32 vertexCount, + u32 shapeCount, + u32 indexCount); -typedef void (*mg_canvas_backend_atlas_upload_proc)(mg_canvas_backend* backend, mp_rect rect, u8* bytes); typedef mg_image_data* (*mg_canvas_backend_image_create_proc)(mg_canvas_backend* backend, vec2 size); typedef void (*mg_canvas_backend_image_destroy_proc)(mg_canvas_backend* backend, mg_image_data* image); -typedef void (*mg_canvas_backend_image_upload_region_proc)(mg_canvas_backend* backend, mg_image_data* image, mp_rect region, u8* pixels); +typedef void (*mg_canvas_backend_image_upload_region_proc)(mg_canvas_backend* backend, + mg_image_data* image, + mp_rect region, + u8* pixels); typedef struct mg_canvas_backend { @@ -115,9 +121,6 @@ typedef struct mg_canvas_backend mg_canvas_backend_image_create_proc imageCreate; mg_canvas_backend_image_destroy_proc imageDestroy; mg_canvas_backend_image_upload_region_proc imageUploadRegion; - - mg_canvas_backend_atlas_upload_proc atlasUpload; - } mg_canvas_backend; #ifdef __cplusplus