From: hgn Date: Wed, 12 Feb 2025 23:43:23 +0000 (+0000) Subject: don't remember X-Git-Url: https://skaterift.com/git/?a=commitdiff_plain;h=ea1926035d2ae61665581688a9f6a6c5200a67c5;p=vg.git don't remember --- diff --git a/vg_audio.c b/vg_audio.c index b3e79be..53d3327 100644 --- a/vg_audio.c +++ b/vg_audio.c @@ -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', diff --git a/vg_audio.h b/vg_audio.h index f660cbb..ce299a4 100644 --- a/vg_audio.h +++ b/vg_audio.h @@ -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 ); diff --git a/vg_engine.c b/vg_engine.c index b07193d..b7fd4c6 100644 --- a/vg_engine.c +++ b/vg_engine.c @@ -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" ) ) diff --git a/vg_engine.h b/vg_engine.h index c17938e..6d872a9 100644 --- a/vg_engine.h +++ b/vg_engine.h @@ -207,6 +207,8 @@ struct vg_engine GLuint tex_missing; vg_rand rand; + + i32 load_step_delay; } extern vg; diff --git a/vg_loader.c b/vg_loader.c index f16d2c4..b49ea12 100644 --- a/vg_loader.c +++ b/vg_loader.c @@ -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) diff --git a/vg_loader.h b/vg_loader.h index c5ce205..3d0cbef 100644 --- a/vg_loader.h +++ b/vg_loader.h @@ -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 ); diff --git a/vg_shader.c b/vg_shader.c index ace5c17..bafab34 100644 --- a/vg_shader.c +++ b/vg_shader.c @@ -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> 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; +} diff --git a/vg_tex.h b/vg_tex.h index 24a99a0..ea197b2 100644 --- a/vg_tex.h +++ b/vg_tex.h @@ -35,6 +35,16 @@ #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 );