don't remember
authorhgn <hgodden00@gmail.com>
Wed, 12 Feb 2025 23:43:23 +0000 (23:43 +0000)
committerhgn <hgodden00@gmail.com>
Wed, 12 Feb 2025 23:43:23 +0000 (23:43 +0000)
vg_audio.c
vg_audio.h
vg_engine.c
vg_engine.h
vg_loader.c
vg_loader.h
vg_shader.c
vg_tex.c
vg_tex.h

index b3e79bed4f238ab06097d314f0dbc2daacf935a5..53d332790578e13357f078bd9c362b5732dab194 100644 (file)
@@ -143,7 +143,6 @@ void audio_channel_init( audio_channel *ch, audio_clip *clip, u32 flags )
 {
    audio_require_lock();
    ch->group = 0;
-   ch->world_id = 0;
    ch->source = clip;
    ch->flags = flags;
    ch->colour = 0x00333333;
@@ -178,12 +177,6 @@ void audio_channel_group( audio_channel *ch, u16 group )
    ch->colour = (((u32)group * 29986577) & 0x00ffffff) | 0xff000000;
 }
 
-void audio_channel_world( audio_channel *ch, u8 world_id )
-{
-   audio_require_lock();
-   ch->world_id = world_id;
-}
-
 audio_channel *audio_get_first_idle_channel(void)
 {
    audio_require_lock();
@@ -341,7 +334,8 @@ void audio_channel_set_spacial( audio_channel *ch, v3f co, float range )
    }
 }
 
-int audio_oneshot_3d( audio_clip *clip, v3f position, f32 range, f32 volume )
+audio_channel *audio_oneshot_3d( audio_clip *clip, v3f position, 
+                                 f32 range, f32 volume )
 {
    audio_require_lock();
    audio_channel *ch = audio_get_first_idle_channel();
@@ -353,13 +347,13 @@ int audio_oneshot_3d( audio_clip *clip, v3f position, f32 range, f32 volume )
       audio_channel_edit_volume( ch, volume, 1 );
       audio_relinquish_channel( ch );
 
-      return 1;
+      return ch;
    }
    else
-      return 0;
+      return NULL;
 }
 
-int audio_oneshot( audio_clip *clip, f32 volume, f32 pan )
+audio_channel *audio_oneshot( audio_clip *clip, f32 volume, f32 pan )
 {
    audio_require_lock();
    audio_channel *ch = audio_get_first_idle_channel();
@@ -370,10 +364,10 @@ int audio_oneshot( audio_clip *clip, f32 volume, f32 pan )
       audio_channel_edit_volume( ch, volume, 1 );
       audio_relinquish_channel( ch );
 
-      return 1;
+      return ch;
    }
    else
-      return 0;
+      return NULL;
 }
 
 void audio_set_lfo_wave( int id, enum lfo_wave_type type, f32 coefficient )
@@ -1167,9 +1161,8 @@ static void cb_vg_audio_view( ui_context *ctx, ui_rect rect,
 
       u32 format_index = (ch->source->flags & AUDIO_FLAG_FORMAT)>>9;
 
-      snprintf( perf, 127, "%02d[%#04x.%#06x]%c%c%cD %s [%s] %4.2fv'%s'", 
-               i,
-               ch->world_id, ch->group,
+      snprintf( perf, 127, "%02d[%#06x]%c%c%cD %s [%s] %4.2fv'%s'", 
+               i, ch->group,
                (ch->editable_state.relinquished)? 'r': '_',
                0?                                 'r': '_',
                0?                                 '3': '2',
index f660cbb1d1b994893f0fd9bf154c00aea06eb52b..ce299a4589157cf6c548fb011420dd80db5efc1c 100644 (file)
@@ -18,6 +18,7 @@
 #define AUDIO_FLAG_SPACIAL_3D 0x4
 #define AUDIO_FLAG_AUTO_START 0x8
 #define AUDIO_FLAG_NO_DSP     0x10
+#define AUDIO_FLAG_WORLD      0x20
 #define AUDIO_FLAG_FORMAT     0x1E00
 
 enum audio_format
@@ -109,10 +110,10 @@ struct vg_audio_system
    }
    oscillators[ AUDIO_LFOS ];
 
-   struct audio_channel{
+   struct audio_channel
+   {
       int allocated;
       u16 group;
-      u8  world_id;
 
       char name[32];       /* only editable while allocated == 0 */
       audio_clip *source;  /* ... */
@@ -203,7 +204,6 @@ void audio_unlock(void);
 
 void audio_channel_init( audio_channel *ch, audio_clip *clip, u32 flags );
 void audio_channel_group( audio_channel *ch, u16 group );
-void audio_channel_world( audio_channel *ch, u8 world_id );
 audio_channel *audio_get_first_idle_channel(void);
 audio_channel *audio_get_group_idle_channel( u16 group, u32 max_count );
 audio_channel *audio_get_group_first_active_channel( u16 group );
@@ -219,8 +219,8 @@ audio_channel *audio_channel_crossfade( audio_channel *ch,
                                         float length, u32 flags );
 void audio_channel_sidechain_lfo( audio_channel *ch, int lfo_id, f32 amount );
 void audio_channel_set_spacial( audio_channel *ch, v3f co, float range );
-int audio_oneshot_3d( audio_clip *clip, v3f position, f32 range, f32 volume );
-int audio_oneshot( audio_clip *clip, f32 volume, f32 pan );
+audio_channel *audio_oneshot_3d( audio_clip *clip, v3f position, f32 range, f32 volume );
+audio_channel *audio_oneshot( audio_clip *clip, f32 volume, f32 pan );
 void audio_set_lfo_wave( int id, enum lfo_wave_type type, f32 coefficient );
 void audio_set_lfo_frequency( int id, float freq );
 int audio_channel_load_source( audio_channel *ch );
index b07193dfeaa3de13bb0fc95ad8b2601ac8d82642..b7fd4c61868a9f430a3d160cffd4e527e2bd0fa9 100644 (file)
@@ -132,7 +132,7 @@ vg_info("            '        ' '--' [] '----- '----- '     ' '---'  "
         "SOFTWARE\n" );
 
    vg_preload();
-   vg_tex2d_replace_with_error_async( &vg.tex_missing );
+   vg_tex2d_replace_with_error_async( 0, &vg.tex_missing );
    vg_async_stall();
    vg_ui.tex_bg = vg.tex_missing;
 
@@ -533,12 +533,15 @@ static void _vg_process_launch_opts_internal( int argc, char *argv[] )
       if( (arg = vg_long_opt_arg( "samples", "Rendering samples per pixel" )) )
          vg.samples = VG_MAX( 0, VG_MIN( 8, atoi( arg ) ) );
 
-      if(  vg_long_opt( "use-libc-malloc", "Use standard libc allocator" ) )
+      if( vg_long_opt( "use-libc-malloc", "Use standard libc allocator" ) )
          vg_mem.use_libc_malloc = 1;
 
       if( vg_long_opt( "high-performance", "Turn graphics to lowest quality" ) )
          vg.quality_profile = k_quality_profile_low;
 
+      if( (arg = vg_long_opt_arg( "load-step-delay", "Loader step delay (ms)" )) )
+         vg.load_step_delay = atoi(arg);
+
       vg_launch_opt();
 
       if( vg_long_opt( "help", "Helps you" ) )
index c17938e61910500a47efd4337cc889a3ca84346e..6d872a92ce075cc0ed3bf41aa370046b14bb4385 100644 (file)
@@ -207,6 +207,8 @@ struct vg_engine
    GLuint tex_missing;
 
    vg_rand rand;
+
+   i32 load_step_delay;
 }
 extern vg;
 
index f16d2c40387534e1eae4349276ce9114edbfd86e..b49ea122925c0c9b1eda99171b90278fe29052d4 100644 (file)
@@ -100,8 +100,40 @@ void vg_loader_atexit(void)
    }
 }
 
+struct async_set_information_args
+{
+   const char *string;
+};
+
+void async_loader_set_user_information( void *payload, u32 size )
+{
+   struct async_set_information_args *args = payload;
+   vg_loader.information_for_user = args->string;
+}
+
+void vg_loader_set_user_information( const char *information )
+{
+   if( vg_thread_purpose() == k_thread_purpose_loader )
+   {
+      vg_async_item *call = 
+         vg_async_alloc( sizeof(struct async_set_information_args) );
+
+      struct async_set_information_args *args = call->payload;
+      args->string = information;
+
+      vg_async_dispatch( call,  async_loader_set_user_information );
+
+      if( vg.load_step_delay )
+         SDL_Delay( vg.load_step_delay );
+   }
+   else
+      vg_loader.information_for_user = information;
+}
+
 void vg_loader_render_ring( f32 opacity )
 {
+   glDisable(GL_CULL_FACE);
+   glDisable(GL_DEPTH_TEST);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glBlendEquation(GL_FUNC_ADD);
@@ -115,6 +147,21 @@ void vg_loader_render_ring( f32 opacity )
    glUniform1f( glGetUniformLocation( _shader_loader.id, "uOpacity"), opacity );
    glBindVertexArray( vg_loader.vao );
    glDrawArrays( GL_TRIANGLES, 0, 6 );
+
+   ui_prerender( &vg_ui.ctx );
+   vg_ui_set_screen( vg.window_x, vg.window_y );
+
+
+   ui_rect test = { 0, vg.window_y - 28*2, vg.window_x, 28 };
+   if( vg_loader.information_for_user )
+   {
+      vg_ui.ctx.font = &vgf_default_large;
+      ui_text( &vg_ui.ctx, test, vg_loader.information_for_user, 
+               1, k_ui_align_middle_center, 0 );
+      vg_ui.ctx.font = &vgf_default_small;
+   }
+
+   ui_postrender( &vg_ui.ctx, vg.time_frame_delta );
 }
 
 void vg_loader_render(void)
index c5ce205041f393d6ca3343a451eb8ef783f612f4..3d0cbef6bf742434d6b4a0183c433c4e7a015bf3 100644 (file)
@@ -23,12 +23,15 @@ struct vg_loader
    u32 step_count, step_action;
 
    GLuint vao, vbo;
+
+   const char *information_for_user;
 }
 extern vg_loader;
 
 void vg_loader_start( void(*pfn)(void *data), void *data );
 void _vg_loader_step( void( *fn_load )(void), void( *fn_free )(void),
                       const char *alias );
+void vg_loader_set_user_information( const char *information );
 int vg_loader_availible(void);
 void vg_loader_render(void);
 void vg_loader_render_ring( f32 opacity );
index ace5c175744462682f84750ef9056da861119ba2..bafab346b09c10c2246ad5bfb498355842a70777 100644 (file)
@@ -132,6 +132,9 @@ void vg_recompile_shader( struct vg_shader *shader )
    char error[260];
    char path[260];
 
+   if( shader->vs.orig_file == NULL ) return;
+   if( shader->fs.orig_file == NULL ) return;
+
    strcpy( path, "../../" );
    strcat( path, shader->vs.orig_file );
    char *vs = stb_include_file( path, "", "../../shaders", error );
@@ -212,7 +215,7 @@ int vg_shaders_live_recompile( int argc, const char *argv[] )
    for( int i=0; i<vg_shaders.count; i ++ )
    {
       struct vg_shader *shader = vg_shaders.shaders[i];
-      vg_compile_shader( shader );
+      vg_recompile_shader( shader );
    }
 
    return 0;
index 17db5f2a0c99436f093c1ce05d944b83aeea90eb..7f1c1b7bb99092110f7c3701a6dc6e12c2350c77 100644 (file)
--- a/vg_tex.c
+++ b/vg_tex.c
@@ -26,18 +26,8 @@ static u8 const_vg_tex2d_err[] ={
 #endif
 };
 
-#define QOI_SRGB   0
-#define QOI_LINEAR 1
-
-typedef struct {
-       unsigned int width;
-       unsigned int height;
-       unsigned char channels;
-       unsigned char colorspace;
-} qoi_desc;
-
 #ifndef QOI_ZEROARR
-       #define QOI_ZEROARR(a) memset((a),0,sizeof(a))
+   #define QOI_ZEROARR(a) memset((a),0,sizeof(a))
 #endif
 
 #define QOI_OP_INDEX  0x00 /* 00xxxxxx */
@@ -51,8 +41,8 @@ typedef struct {
 
 #define QOI_COLOR_HASH(C) (C.rgba.r*3 + C.rgba.g*5 + C.rgba.b*7 + C.rgba.a*11)
 #define QOI_MAGIC \
-       (((unsigned int)'q') << 24 | ((unsigned int)'o') << 16 | \
-        ((unsigned int)'i') <<  8 | ((unsigned int)'f'))
+   (((unsigned int)'q') << 24 | ((unsigned int)'o') << 16 | \
+    ((unsigned int)'i') <<  8 | ((unsigned int)'f'))
 #define QOI_HEADER_SIZE 14
 
 /* 2GB is the max file size that this implementation can safely handle. We guard
@@ -62,17 +52,24 @@ enough for anybody. */
 #define QOI_PIXELS_MAX ((unsigned int)400000000)
 
 typedef union {
-       struct { unsigned char r, g, b, a; } rgba;
-       unsigned int v;
+   struct { unsigned char r, g, b, a; } rgba;
+   unsigned int v;
 } qoi_rgba_t;
 
 static const unsigned char qoi_padding[8] = {0,0,0,0,0,0,0,1};
 static u32 qoi_read_32( const u8 *bytes, int *p ) {
-       u32 a = bytes[(*p)++];
-       u32 b = bytes[(*p)++];
-       u32 c = bytes[(*p)++];
-       u32 d = bytes[(*p)++];
-       return a << 24 | b << 16 | c << 8 | d;
+   u32 a = bytes[(*p)++];
+   u32 b = bytes[(*p)++];
+   u32 c = bytes[(*p)++];
+   u32 d = bytes[(*p)++];
+   return a << 24 | b << 16 | c << 8 | d;
+}
+
+static void qoi_write_32(unsigned char *bytes, int *p, unsigned int v) {
+   bytes[(*p)++] = (0xff000000 & v) >> 24;
+   bytes[(*p)++] = (0x00ff0000 & v) >> 16;
+   bytes[(*p)++] = (0x0000ff00 & v) >> 8;
+   bytes[(*p)++] = (0x000000ff & v);
 }
 
 struct texture_load_info{
@@ -87,43 +84,69 @@ static void async_vg_tex2d_upload( void *payload, u32 size )
 
    struct texture_load_info *info = payload;
 
-       glGenTextures( 1, info->dest );
-       glBindTexture( GL_TEXTURE_2D, *info->dest );
-   glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, info->width, info->height,
-                  0, GL_RGBA, GL_UNSIGNED_BYTE, info->rgba );
+   glGenTextures( 1, info->dest );
 
-   if( !(info->flags & VG_TEX2D_NOMIP) ){
-      glGenerateMipmap( GL_TEXTURE_2D );
-   }
+   if( info->flags & VG_TEX2D_CUBEMAP )
+   {
+      bool not_error = !(info->flags & VG_TEX2D_ERROR);
+      u32 w = info->width,
+          h = not_error? info->height/6: info->height;
 
-   if( info->flags & VG_TEX2D_LINEAR ){
-      if( info->flags & VG_TEX2D_NOMIP ){
-         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
-      }
-      else{
-         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, 
-               GL_LINEAR_MIPMAP_LINEAR );
+      glBindTexture( GL_TEXTURE_CUBE_MAP, *info->dest );
+      glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+      glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+      glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+      glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+      glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
+
+      for( u32 j=0; j<6; j ++ ) 
+      {
+         u32 offset = not_error? w*h*j*4: 0;
+
+         glTexImage2D( GL_TEXTURE_CUBE_MAP_POSITIVE_X + j, 0, GL_RGBA, 
+                        w, h,
+                        0, GL_RGBA, GL_UNSIGNED_BYTE, info->rgba + offset );
       }
-      glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
    }
+   else
+   {
+      glBindTexture( GL_TEXTURE_2D, *info->dest );
+      glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, info->width, info->height,
+                     0, GL_RGBA, GL_UNSIGNED_BYTE, info->rgba );
+
+      if( !(info->flags & VG_TEX2D_NOMIP) ){
+         glGenerateMipmap( GL_TEXTURE_2D );
+      }
 
-   if( info->flags & VG_TEX2D_NEAREST ){
-      glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
-      glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
-   }
+      if( info->flags & VG_TEX2D_LINEAR ){
+         if( info->flags & VG_TEX2D_NOMIP ){
+            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
+         }
+         else{
+            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, 
+                  GL_LINEAR_MIPMAP_LINEAR );
+         }
+         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
+      }
 
-   if( info->flags & VG_TEX2D_CLAMP ){
-      glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
-      glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
-   }
+      if( info->flags & VG_TEX2D_NEAREST ){
+         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
+         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
+      }
+
+      if( info->flags & VG_TEX2D_CLAMP ){
+         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
+         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
+      }
 
-   if( info->flags & VG_TEX2D_REPEAT ){
-      glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
-      glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
+      if( info->flags & VG_TEX2D_REPEAT ){
+         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
+         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
+      }
    }
 }
 
-void vg_tex2d_replace_with_error_async( GLuint *dest )
+void vg_tex2d_replace_with_error_async( u32 original_flags, GLuint *dest )
 {
    u32 hdr_size = vg_align8(sizeof(struct texture_load_info));
 
@@ -131,7 +154,11 @@ void vg_tex2d_replace_with_error_async( GLuint *dest )
    struct texture_load_info *info = call->payload;
    
    info->dest = dest;
-   info->flags = VG_TEX2D_NEAREST|VG_TEX2D_REPEAT|VG_TEX2D_NOMIP;
+   info->flags = VG_TEX2D_ERROR|VG_TEX2D_NEAREST|VG_TEX2D_REPEAT|VG_TEX2D_NOMIP;
+
+   if( original_flags & VG_TEX2D_CUBEMAP )
+      info->flags |= VG_TEX2D_CUBEMAP;
+
    info->width = 4;
    info->height = 4;
    info->rgba = const_vg_tex2d_err;
@@ -139,52 +166,51 @@ void vg_tex2d_replace_with_error_async( GLuint *dest )
    vg_async_dispatch( call, async_vg_tex2d_upload );
 }
 
-void vg_tex2d_load_qoi_async( const u8 *bytes, u32 size, 
-                              u32 flags, GLuint *dest )
+void vg_tex2d_load_qoi_async( const u8 *bytes, u32 size, u32 flags, GLuint *dest )
 {
-       u32 header_magic;
-       qoi_rgba_t index[64];
-       qoi_rgba_t px;
-       int px_len, chunks_len, px_pos;
-       int p = 0, run = 0;
+   u32 header_magic;
+   qoi_rgba_t index[64];
+   qoi_rgba_t px;
+   int px_len, chunks_len, px_pos;
+   int p = 0, run = 0;
 
    u32 channels = 4; /* TODO */
 
    qoi_desc desc;
 
-       if (
-               bytes == NULL ||
-               (channels != 0 && channels != 3 && channels != 4) ||
-               size < QOI_HEADER_SIZE + (int)sizeof(qoi_padding)
-       ) {
+   if (
+      bytes == NULL ||
+      (channels != 0 && channels != 3 && channels != 4) ||
+      size < QOI_HEADER_SIZE + (int)sizeof(qoi_padding)
+   ) {
       vg_error( "Error while decoding qoi file: illegal parameters\n" );
-      vg_tex2d_replace_with_error_async( dest );
-               return;
-       }
-
-       header_magic = qoi_read_32(bytes, &p);
-       desc.width = qoi_read_32(bytes, &p);
-       desc.height = qoi_read_32(bytes, &p);
-       desc.channels = bytes[p++];
-       desc.colorspace = bytes[p++];
-
-       if (
-               desc.width == 0 || desc.height == 0 ||
-               desc.channels < 3 || desc.channels > 4 ||
-               desc.colorspace > 1 ||
-               header_magic != QOI_MAGIC ||
-               desc.height >= QOI_PIXELS_MAX / desc.width
-       ) {
+      vg_tex2d_replace_with_error_async( flags, dest );
+      return;
+   }
+
+   header_magic = qoi_read_32(bytes, &p);
+   desc.width = qoi_read_32(bytes, &p);
+   desc.height = qoi_read_32(bytes, &p);
+   desc.channels = bytes[p++];
+   desc.colorspace = bytes[p++];
+
+   if (
+      desc.width == 0 || desc.height == 0 ||
+      desc.channels < 3 || desc.channels > 4 ||
+      desc.colorspace > 1 ||
+      header_magic != QOI_MAGIC ||
+      desc.height >= QOI_PIXELS_MAX / desc.width
+   ) {
       vg_error( "Error while decoding qoi file: invalid file\n" );
-      vg_tex2d_replace_with_error_async( dest );
-               return;
-       }
+      vg_tex2d_replace_with_error_async( flags, dest );
+      return;
+   }
 
-       if (channels == 0) {
-               channels = desc.channels;
-       }
+   if (channels == 0) {
+      channels = desc.channels;
+   }
 
-       px_len = desc.width * desc.height * channels;
+   px_len = desc.width * desc.height * channels;
 
    /* allocate async call
     * --------------------------
@@ -208,61 +234,61 @@ void vg_tex2d_load_qoi_async( const u8 *bytes, u32 size,
 
    u8 *pixels = info->rgba;
 
-       QOI_ZEROARR(index);
-       px.rgba.r = 0;
-       px.rgba.g = 0;
-       px.rgba.b = 0;
-       px.rgba.a = 255;
-
-       chunks_len = size - (int)sizeof(qoi_padding);
-       for (px_pos = 0; px_pos < px_len; px_pos += channels) {
-               if (run > 0) {
-                       run--;
-               }
-               else if (p < chunks_len) {
-                       int b1 = bytes[p++];
-
-                       if (b1 == QOI_OP_RGB) {
-                               px.rgba.r = bytes[p++];
-                               px.rgba.g = bytes[p++];
-                               px.rgba.b = bytes[p++];
-                       }
-                       else if (b1 == QOI_OP_RGBA) {
-                               px.rgba.r = bytes[p++];
-                               px.rgba.g = bytes[p++];
-                               px.rgba.b = bytes[p++];
-                               px.rgba.a = bytes[p++];
-                       }
-                       else if ((b1 & QOI_MASK_2) == QOI_OP_INDEX) {
-                               px = index[b1];
-                       }
-                       else if ((b1 & QOI_MASK_2) == QOI_OP_DIFF) {
-                               px.rgba.r += ((b1 >> 4) & 0x03) - 2;
-                               px.rgba.g += ((b1 >> 2) & 0x03) - 2;
-                               px.rgba.b += ( b1       & 0x03) - 2;
-                       }
-                       else if ((b1 & QOI_MASK_2) == QOI_OP_LUMA) {
-                               int b2 = bytes[p++];
-                               int vg = (b1 & 0x3f) - 32;
-                               px.rgba.r += vg - 8 + ((b2 >> 4) & 0x0f);
-                               px.rgba.g += vg;
-                               px.rgba.b += vg - 8 +  (b2       & 0x0f);
-                       }
-                       else if ((b1 & QOI_MASK_2) == QOI_OP_RUN) {
-                               run = (b1 & 0x3f);
-                       }
-
-                       index[QOI_COLOR_HASH(px) % 64] = px;
-               }
-
-               pixels[px_pos + 0] = px.rgba.r;
-               pixels[px_pos + 1] = px.rgba.g;
-               pixels[px_pos + 2] = px.rgba.b;
-               
-               if (channels == 4) {
-                       pixels[px_pos + 3] = px.rgba.a;
-               }
-       }
+   QOI_ZEROARR(index);
+   px.rgba.r = 0;
+   px.rgba.g = 0;
+   px.rgba.b = 0;
+   px.rgba.a = 255;
+
+   chunks_len = size - (int)sizeof(qoi_padding);
+   for (px_pos = 0; px_pos < px_len; px_pos += channels) {
+      if (run > 0) {
+         run--;
+      }
+      else if (p < chunks_len) {
+         int b1 = bytes[p++];
+
+         if (b1 == QOI_OP_RGB) {
+            px.rgba.r = bytes[p++];
+            px.rgba.g = bytes[p++];
+            px.rgba.b = bytes[p++];
+         }
+         else if (b1 == QOI_OP_RGBA) {
+            px.rgba.r = bytes[p++];
+            px.rgba.g = bytes[p++];
+            px.rgba.b = bytes[p++];
+            px.rgba.a = bytes[p++];
+         }
+         else if ((b1 & QOI_MASK_2) == QOI_OP_INDEX) {
+            px = index[b1];
+         }
+         else if ((b1 & QOI_MASK_2) == QOI_OP_DIFF) {
+            px.rgba.r += ((b1 >> 4) & 0x03) - 2;
+            px.rgba.g += ((b1 >> 2) & 0x03) - 2;
+            px.rgba.b += ( b1       & 0x03) - 2;
+         }
+         else if ((b1 & QOI_MASK_2) == QOI_OP_LUMA) {
+            int b2 = bytes[p++];
+            int vg = (b1 & 0x3f) - 32;
+            px.rgba.r += vg - 8 + ((b2 >> 4) & 0x0f);
+            px.rgba.g += vg;
+            px.rgba.b += vg - 8 +  (b2       & 0x0f);
+         }
+         else if ((b1 & QOI_MASK_2) == QOI_OP_RUN) {
+            run = (b1 & 0x3f);
+         }
+
+         index[QOI_COLOR_HASH(px) % 64] = px;
+      }
+
+      pixels[px_pos + 0] = px.rgba.r;
+      pixels[px_pos + 1] = px.rgba.g;
+      pixels[px_pos + 2] = px.rgba.b;
+      
+      if (channels == 4) {
+         pixels[px_pos + 3] = px.rgba.a;
+      }
+   }
 
    /*
     * Complete the call
@@ -283,3 +309,145 @@ void vg_tex2d_load_qoi_async_file( const char *path, u32 flags, GLuint *dest )
    const void *data = vg_file_read( vg_mem.scratch, path, &size );
    vg_tex2d_load_qoi_async( data, size, flags, dest );
 }
+
+u32 vg_query_qoi_storage_size( const qoi_desc *desc )
+{
+   return
+      desc->width * desc->height * (desc->channels + 1) +
+      QOI_HEADER_SIZE + sizeof(qoi_padding);
+}
+
+bool vg_encode_qoi2( const u8 *pixels, const qoi_desc *desc, u8 *out_bytes, int *out_len ) 
+{
+   int i = 0, p = 0, run = 0;
+   int px_len, px_end, px_pos, channels;
+   qoi_rgba_t index[64];
+   qoi_rgba_t px, px_prev;
+   
+   if( desc->width == 0 || desc->height == 0 )
+   {
+      vg_error( "vg_write_qoi2: 0 width/height: %dx%d\n", desc->width, desc->height );
+      return 0;
+   }
+
+   if( desc->channels < 3 || desc->channels > 4 )
+   {
+      vg_error( "vg_write_qoi2: invalid channels: %d (min: 3, max: 4)\n", desc->channels );
+      return 0;
+   }
+
+   if( desc->colorspace > 1 )
+   {
+      vg_error( "vg_write_qoi2: invalid colourspace: %d (min:0 SRGB, max:1 LINEAR)\n", desc->colorspace );
+      return 0;
+   }
+
+   int total_pixels = desc->height * desc->width;
+   if( total_pixels >= QOI_PIXELS_MAX )
+   {
+      vg_error( "vg_write_qoi2: too many pixels (%d exceeds limit of %d)\n", total_pixels, QOI_PIXELS_MAX );
+      return 0;
+   }
+
+   qoi_write_32( out_bytes, &p, QOI_MAGIC );
+   qoi_write_32( out_bytes, &p, desc->width );
+   qoi_write_32( out_bytes, &p, desc->height );
+   out_bytes[p++] = desc->channels;
+   out_bytes[p++] = desc->colorspace;
+
+   QOI_ZEROARR(index);
+
+   run = 0;
+   px_prev.rgba.r = 0;
+   px_prev.rgba.g = 0;
+   px_prev.rgba.b = 0;
+   px_prev.rgba.a = 255;
+   px = px_prev;
+
+   px_len = desc->width * desc->height * desc->channels;
+   px_end = px_len - desc->channels;
+   channels = desc->channels;
+
+   for( px_pos = 0; px_pos < px_len; px_pos += channels ) 
+   {
+      px.rgba.r = pixels[px_pos + 0];
+      px.rgba.g = pixels[px_pos + 1];
+      px.rgba.b = pixels[px_pos + 2];
+
+      if( channels == 4 )
+         px.rgba.a = pixels[px_pos + 3];
+
+      if( px.v == px_prev.v )
+      {
+         run++;
+         if( run == 62 || px_pos == px_end )
+         {
+            out_bytes[p++] = QOI_OP_RUN | (run - 1);
+            run = 0;
+         }
+      }
+      else
+      {
+         int index_pos;
+         if( run > 0 )
+         {
+            out_bytes[p++] = QOI_OP_RUN | (run - 1);
+            run = 0;
+         }
+
+         index_pos = QOI_COLOR_HASH(px) % 64;
+
+         if( index[index_pos].v == px.v ) 
+            out_bytes[p++] = QOI_OP_INDEX | index_pos;
+         else 
+         {
+            index[index_pos] = px;
+
+            if( px.rgba.a == px_prev.rgba.a )
+            {
+               i8 vr = px.rgba.r - px_prev.rgba.r,
+                  vg = px.rgba.g - px_prev.rgba.g,
+                  vb = px.rgba.b - px_prev.rgba.b,
+                  vg_r = vr - vg,
+                  vg_b = vb - vg;
+
+               if( vr > -3 && vr < 2 &&
+                   vg > -3 && vg < 2 &&
+                   vb > -3 && vb < 2 ) 
+               {
+                  out_bytes[p++] = QOI_OP_DIFF | (vr + 2) << 4 | (vg + 2) << 2 | (vb + 2);
+               }
+               else if( vg_r >  -9 && vg_r <  8 &&
+                        vg   > -33 && vg   < 32 &&
+                        vg_b >  -9 && vg_b <  8 ) 
+               {
+                  out_bytes[p++] = QOI_OP_LUMA     | (vg   + 32);
+                  out_bytes[p++] = (vg_r + 8) << 4 | (vg_b +  8);
+               }
+               else 
+               {
+                  out_bytes[p++] = QOI_OP_RGB;
+                  out_bytes[p++] = px.rgba.r;
+                  out_bytes[p++] = px.rgba.g;
+                  out_bytes[p++] = px.rgba.b;
+               }
+            }
+            else 
+            {
+               out_bytes[p++] = QOI_OP_RGBA;
+               out_bytes[p++] = px.rgba.r;
+               out_bytes[p++] = px.rgba.g;
+               out_bytes[p++] = px.rgba.b;
+               out_bytes[p++] = px.rgba.a;
+            }
+         }
+      }
+      px_prev = px;
+   }
+
+   for( i = 0; i < (int)sizeof(qoi_padding); i ++ ) 
+      out_bytes[p ++] = qoi_padding[i];
+
+   *out_len = p;
+   return 1;
+}
index 24a99a0c78c34b8eab5f0e03ecefa3d1818a8036..ea197b2ef68a692f0b34ddb493dda09995834553 100644 (file)
--- a/vg_tex.h
+++ b/vg_tex.h
 #include "vg_image.h"
 #include "vg_engine.h"
 
+#define QOI_SRGB   0
+#define QOI_LINEAR 1
+
+typedef struct {
+       unsigned int width;
+       unsigned int height;
+       unsigned char channels;
+       unsigned char colorspace;
+} qoi_desc;
+
 struct vg_sprite
 {
        v4f uv_xywh;
@@ -45,13 +55,18 @@ struct vg_sprite
 #define VG_TEX2D_REPEAT  0x4
 #define VG_TEX2D_CLAMP   0x8
 #define VG_TEX2D_NOMIP   0x10
+#define VG_TEX2D_CUBEMAP 0x20
+#define VG_TEX2D_ERROR   0x40
 
 /* options to create texutres; call only from loader thread. 
  * *dest will be replaced synchronously by the main thread when ready. */
 
-void vg_tex2d_replace_with_error_async( GLuint *dest );
+void vg_tex2d_replace_with_error_async( u32 original_flags, GLuint *dest );
 
 void vg_tex2d_load_qoi_async( const u8 *bytes, u32 size, 
                               u32 flags, GLuint *dest );
 
 void vg_tex2d_load_qoi_async_file( const char *path, u32 flags, GLuint *dest );
+
+u32 vg_query_qoi_storage_size( const qoi_desc *desc );
+bool vg_encode_qoi2( const u8 *pixels, const qoi_desc *desc, u8 *out_bytes, int *out_len );