Update C sources

This commit is contained in:
JupiterRider 2023-04-07 20:23:18 +02:00
parent 22ca1484d1
commit 781c207678
27 changed files with 13963 additions and 4637 deletions

View file

@ -1,7 +1,7 @@
/**
* cgltf - a single-file glTF 2.0 parser written in C99.
*
* Version: 1.12
* Version: 1.13
*
* Website: https://github.com/jkuhlmann/cgltf
*
@ -80,19 +80,16 @@
* `cgltf_accessor_read_index` is similar to its floating-point counterpart, but it returns size_t
* and only works with single-component data types.
*
* `cgltf_result cgltf_copy_extras_json(const cgltf_data*, const cgltf_extras*,
* char* dest, cgltf_size* dest_size)` allows users to retrieve the "extras" data that
* can be attached to many glTF objects (which can be arbitrary JSON data). The
* `cgltf_extras` struct stores the offsets of the start and end of the extras JSON data
* as it appears in the complete glTF JSON data. This function copies the extras data
* into the provided buffer. If `dest` is NULL, the length of the data is written into
* `dest_size`. You can then parse this data using your own JSON parser
* `cgltf_copy_extras_json` allows users to retrieve the "extras" data that can be attached to many
* glTF objects (which can be arbitrary JSON data). This is a legacy function, consider using
* cgltf_extras::data directly instead. You can parse this data using your own JSON parser
* or, if you've included the cgltf implementation using the integrated JSMN JSON parser.
*/
#ifndef CGLTF_H_INCLUDED__
#define CGLTF_H_INCLUDED__
#include <stddef.h>
#include <stdint.h> /* For uint8_t, uint32_t */
#ifdef __cplusplus
extern "C" {
@ -256,8 +253,10 @@ typedef enum cgltf_data_free_method {
} cgltf_data_free_method;
typedef struct cgltf_extras {
cgltf_size start_offset;
cgltf_size end_offset;
cgltf_size start_offset; /* this field is deprecated and will be removed in the future; use data instead */
cgltf_size end_offset; /* this field is deprecated and will be removed in the future; use data instead */
char* data;
} cgltf_extras;
typedef struct cgltf_extension {
@ -432,8 +431,6 @@ typedef struct cgltf_pbr_metallic_roughness
cgltf_float base_color_factor[4];
cgltf_float metallic_factor;
cgltf_float roughness_factor;
cgltf_extras extras;
} cgltf_pbr_metallic_roughness;
typedef struct cgltf_pbr_specular_glossiness
@ -833,6 +830,8 @@ void cgltf_free(cgltf_data* data);
void cgltf_node_transform_local(const cgltf_node* node, cgltf_float* out_matrix);
void cgltf_node_transform_world(const cgltf_node* node, cgltf_float* out_matrix);
const uint8_t* cgltf_buffer_view_data(const cgltf_buffer_view* view);
cgltf_bool cgltf_accessor_read_float(const cgltf_accessor* accessor, cgltf_size index, cgltf_float* out, cgltf_size element_size);
cgltf_bool cgltf_accessor_read_uint(const cgltf_accessor* accessor, cgltf_size index, cgltf_uint* out, cgltf_size element_size);
cgltf_size cgltf_accessor_read_index(const cgltf_accessor* accessor, cgltf_size index);
@ -841,6 +840,7 @@ cgltf_size cgltf_num_components(cgltf_type type);
cgltf_size cgltf_accessor_unpack_floats(const cgltf_accessor* accessor, cgltf_float* out, cgltf_size float_count);
/* this function is deprecated and will be removed in the future; use cgltf_extras::data instead */
cgltf_result cgltf_copy_extras_json(const cgltf_data* data, const cgltf_extras* extras, char* dest, cgltf_size* dest_size);
#ifdef __cplusplus
@ -863,7 +863,6 @@ cgltf_result cgltf_copy_extras_json(const cgltf_data* data, const cgltf_extras*
#ifdef CGLTF_IMPLEMENTATION
#include <stdint.h> /* For uint8_t, uint32_t */
#include <string.h> /* For strncpy */
#include <stdio.h> /* For fopen */
#include <limits.h> /* For UINT_MAX etc */
@ -905,15 +904,15 @@ enum jsmnerr {
};
typedef struct {
jsmntype_t type;
int start;
int end;
ptrdiff_t start;
ptrdiff_t end;
int size;
#ifdef JSMN_PARENT_LINKS
int parent;
#endif
} jsmntok_t;
typedef struct {
unsigned int pos; /* offset in the JSON string */
size_t pos; /* offset in the JSON string */
unsigned int toknext; /* next token to allocate */
int toksuper; /* superior token node, e.g parent object or array */
} jsmn_parser;
@ -924,12 +923,15 @@ static int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, jsmntok_t
*/
#ifndef CGLTF_CONSTS
static const cgltf_size GlbHeaderSize = 12;
static const cgltf_size GlbChunkHeaderSize = 8;
static const uint32_t GlbVersion = 2;
static const uint32_t GlbMagic = 0x46546C67;
static const uint32_t GlbMagicJsonChunk = 0x4E4F534A;
static const uint32_t GlbMagicBinChunk = 0x004E4942;
#define CGLTF_CONSTS
#endif
#ifndef CGLTF_MALLOC
#define CGLTF_MALLOC(size) malloc(size)
@ -1745,7 +1747,12 @@ cgltf_result cgltf_copy_extras_json(const cgltf_data* data, const cgltf_extras*
return cgltf_result_success;
}
void cgltf_free_extensions(cgltf_data* data, cgltf_extension* extensions, cgltf_size extensions_count)
static void cgltf_free_extras(cgltf_data* data, cgltf_extras* extras)
{
data->memory.free_func(data->memory.user_data, extras->data);
}
static void cgltf_free_extensions(cgltf_data* data, cgltf_extension* extensions, cgltf_size extensions_count)
{
for (cgltf_size i = 0; i < extensions_count; ++i)
{
@ -1755,6 +1762,12 @@ void cgltf_free_extensions(cgltf_data* data, cgltf_extension* extensions, cgltf_
data->memory.free_func(data->memory.user_data, extensions);
}
static void cgltf_free_texture_view(cgltf_data* data, cgltf_texture_view* view)
{
cgltf_free_extensions(data, view->extensions, view->extensions_count);
cgltf_free_extras(data, &view->extras);
}
void cgltf_free(cgltf_data* data)
{
if (!data)
@ -1770,6 +1783,7 @@ void cgltf_free(cgltf_data* data)
data->memory.free_func(data->memory.user_data, data->asset.min_version);
cgltf_free_extensions(data, data->asset.extensions, data->asset.extensions_count);
cgltf_free_extras(data, &data->asset.extras);
for (cgltf_size i = 0; i < data->accessors_count; ++i)
{
@ -1780,8 +1794,12 @@ void cgltf_free(cgltf_data* data)
cgltf_free_extensions(data, data->accessors[i].sparse.extensions, data->accessors[i].sparse.extensions_count);
cgltf_free_extensions(data, data->accessors[i].sparse.indices_extensions, data->accessors[i].sparse.indices_extensions_count);
cgltf_free_extensions(data, data->accessors[i].sparse.values_extensions, data->accessors[i].sparse.values_extensions_count);
cgltf_free_extras(data, &data->accessors[i].sparse.extras);
cgltf_free_extras(data, &data->accessors[i].sparse.indices_extras);
cgltf_free_extras(data, &data->accessors[i].sparse.values_extras);
}
cgltf_free_extensions(data, data->accessors[i].extensions, data->accessors[i].extensions_count);
cgltf_free_extras(data, &data->accessors[i].extras);
}
data->memory.free_func(data->memory.user_data, data->accessors);
@ -1791,6 +1809,7 @@ void cgltf_free(cgltf_data* data)
data->memory.free_func(data->memory.user_data, data->buffer_views[i].data);
cgltf_free_extensions(data, data->buffer_views[i].extensions, data->buffer_views[i].extensions_count);
cgltf_free_extras(data, &data->buffer_views[i].extras);
}
data->memory.free_func(data->memory.user_data, data->buffer_views);
@ -1810,8 +1829,8 @@ void cgltf_free(cgltf_data* data)
data->memory.free_func(data->memory.user_data, data->buffers[i].uri);
cgltf_free_extensions(data, data->buffers[i].extensions, data->buffers[i].extensions_count);
cgltf_free_extras(data, &data->buffers[i].extras);
}
data->memory.free_func(data->memory.user_data, data->buffers);
for (cgltf_size i = 0; i < data->meshes_count; ++i)
@ -1849,9 +1868,15 @@ void cgltf_free(cgltf_data* data)
data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].draco_mesh_compression.attributes);
}
for (cgltf_size k = 0; k < data->meshes[i].primitives[j].mappings_count; ++k)
{
cgltf_free_extras(data, &data->meshes[i].primitives[j].mappings[k].extras);
}
data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].mappings);
cgltf_free_extensions(data, data->meshes[i].primitives[j].extensions, data->meshes[i].primitives[j].extensions_count);
cgltf_free_extras(data, &data->meshes[i].primitives[j].extras);
}
data->memory.free_func(data->memory.user_data, data->meshes[i].primitives);
@ -1863,6 +1888,7 @@ void cgltf_free(cgltf_data* data)
}
cgltf_free_extensions(data, data->meshes[i].extensions, data->meshes[i].extensions_count);
cgltf_free_extras(data, &data->meshes[i].extras);
data->memory.free_func(data->memory.user_data, data->meshes[i].target_names);
}
@ -1875,49 +1901,50 @@ void cgltf_free(cgltf_data* data)
if(data->materials[i].has_pbr_metallic_roughness)
{
cgltf_free_extensions(data, data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.extensions, data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.extensions_count);
cgltf_free_extensions(data, data->materials[i].pbr_metallic_roughness.base_color_texture.extensions, data->materials[i].pbr_metallic_roughness.base_color_texture.extensions_count);
cgltf_free_texture_view(data, &data->materials[i].pbr_metallic_roughness.metallic_roughness_texture);
cgltf_free_texture_view(data, &data->materials[i].pbr_metallic_roughness.base_color_texture);
}
if(data->materials[i].has_pbr_specular_glossiness)
{
cgltf_free_extensions(data, data->materials[i].pbr_specular_glossiness.diffuse_texture.extensions, data->materials[i].pbr_specular_glossiness.diffuse_texture.extensions_count);
cgltf_free_extensions(data, data->materials[i].pbr_specular_glossiness.specular_glossiness_texture.extensions, data->materials[i].pbr_specular_glossiness.specular_glossiness_texture.extensions_count);
cgltf_free_texture_view(data, &data->materials[i].pbr_specular_glossiness.diffuse_texture);
cgltf_free_texture_view(data, &data->materials[i].pbr_specular_glossiness.specular_glossiness_texture);
}
if(data->materials[i].has_clearcoat)
{
cgltf_free_extensions(data, data->materials[i].clearcoat.clearcoat_texture.extensions, data->materials[i].clearcoat.clearcoat_texture.extensions_count);
cgltf_free_extensions(data, data->materials[i].clearcoat.clearcoat_roughness_texture.extensions, data->materials[i].clearcoat.clearcoat_roughness_texture.extensions_count);
cgltf_free_extensions(data, data->materials[i].clearcoat.clearcoat_normal_texture.extensions, data->materials[i].clearcoat.clearcoat_normal_texture.extensions_count);
cgltf_free_texture_view(data, &data->materials[i].clearcoat.clearcoat_texture);
cgltf_free_texture_view(data, &data->materials[i].clearcoat.clearcoat_roughness_texture);
cgltf_free_texture_view(data, &data->materials[i].clearcoat.clearcoat_normal_texture);
}
if(data->materials[i].has_specular)
{
cgltf_free_extensions(data, data->materials[i].specular.specular_texture.extensions, data->materials[i].specular.specular_texture.extensions_count);
cgltf_free_extensions(data, data->materials[i].specular.specular_color_texture.extensions, data->materials[i].specular.specular_color_texture.extensions_count);
cgltf_free_texture_view(data, &data->materials[i].specular.specular_texture);
cgltf_free_texture_view(data, &data->materials[i].specular.specular_color_texture);
}
if(data->materials[i].has_transmission)
{
cgltf_free_extensions(data, data->materials[i].transmission.transmission_texture.extensions, data->materials[i].transmission.transmission_texture.extensions_count);
cgltf_free_texture_view(data, &data->materials[i].transmission.transmission_texture);
}
if (data->materials[i].has_volume)
{
cgltf_free_extensions(data, data->materials[i].volume.thickness_texture.extensions, data->materials[i].volume.thickness_texture.extensions_count);
cgltf_free_texture_view(data, &data->materials[i].volume.thickness_texture);
}
if(data->materials[i].has_sheen)
{
cgltf_free_extensions(data, data->materials[i].sheen.sheen_color_texture.extensions, data->materials[i].sheen.sheen_color_texture.extensions_count);
cgltf_free_extensions(data, data->materials[i].sheen.sheen_roughness_texture.extensions, data->materials[i].sheen.sheen_roughness_texture.extensions_count);
cgltf_free_texture_view(data, &data->materials[i].sheen.sheen_color_texture);
cgltf_free_texture_view(data, &data->materials[i].sheen.sheen_roughness_texture);
}
if(data->materials[i].has_iridescence)
{
cgltf_free_extensions(data, data->materials[i].iridescence.iridescence_texture.extensions, data->materials[i].iridescence.iridescence_texture.extensions_count);
cgltf_free_extensions(data, data->materials[i].iridescence.iridescence_thickness_texture.extensions, data->materials[i].iridescence.iridescence_thickness_texture.extensions_count);
cgltf_free_texture_view(data, &data->materials[i].iridescence.iridescence_texture);
cgltf_free_texture_view(data, &data->materials[i].iridescence.iridescence_thickness_texture);
}
cgltf_free_extensions(data, data->materials[i].normal_texture.extensions, data->materials[i].normal_texture.extensions_count);
cgltf_free_extensions(data, data->materials[i].occlusion_texture.extensions, data->materials[i].occlusion_texture.extensions_count);
cgltf_free_extensions(data, data->materials[i].emissive_texture.extensions, data->materials[i].emissive_texture.extensions_count);
cgltf_free_texture_view(data, &data->materials[i].normal_texture);
cgltf_free_texture_view(data, &data->materials[i].occlusion_texture);
cgltf_free_texture_view(data, &data->materials[i].emissive_texture);
cgltf_free_extensions(data, data->materials[i].extensions, data->materials[i].extensions_count);
cgltf_free_extras(data, &data->materials[i].extras);
}
data->memory.free_func(data->memory.user_data, data->materials);
@ -1929,6 +1956,7 @@ void cgltf_free(cgltf_data* data)
data->memory.free_func(data->memory.user_data, data->images[i].mime_type);
cgltf_free_extensions(data, data->images[i].extensions, data->images[i].extensions_count);
cgltf_free_extras(data, &data->images[i].extras);
}
data->memory.free_func(data->memory.user_data, data->images);
@ -1936,7 +1964,9 @@ void cgltf_free(cgltf_data* data)
for (cgltf_size i = 0; i < data->textures_count; ++i)
{
data->memory.free_func(data->memory.user_data, data->textures[i].name);
cgltf_free_extensions(data, data->textures[i].extensions, data->textures[i].extensions_count);
cgltf_free_extras(data, &data->textures[i].extras);
}
data->memory.free_func(data->memory.user_data, data->textures);
@ -1944,7 +1974,9 @@ void cgltf_free(cgltf_data* data)
for (cgltf_size i = 0; i < data->samplers_count; ++i)
{
data->memory.free_func(data->memory.user_data, data->samplers[i].name);
cgltf_free_extensions(data, data->samplers[i].extensions, data->samplers[i].extensions_count);
cgltf_free_extras(data, &data->samplers[i].extras);
}
data->memory.free_func(data->memory.user_data, data->samplers);
@ -1955,6 +1987,7 @@ void cgltf_free(cgltf_data* data)
data->memory.free_func(data->memory.user_data, data->skins[i].joints);
cgltf_free_extensions(data, data->skins[i].extensions, data->skins[i].extensions_count);
cgltf_free_extras(data, &data->skins[i].extras);
}
data->memory.free_func(data->memory.user_data, data->skins);
@ -1962,7 +1995,18 @@ void cgltf_free(cgltf_data* data)
for (cgltf_size i = 0; i < data->cameras_count; ++i)
{
data->memory.free_func(data->memory.user_data, data->cameras[i].name);
if (data->cameras[i].type == cgltf_camera_type_perspective)
{
cgltf_free_extras(data, &data->cameras[i].data.perspective.extras);
}
else if (data->cameras[i].type == cgltf_camera_type_orthographic)
{
cgltf_free_extras(data, &data->cameras[i].data.orthographic.extras);
}
cgltf_free_extensions(data, data->cameras[i].extensions, data->cameras[i].extensions_count);
cgltf_free_extras(data, &data->cameras[i].extras);
}
data->memory.free_func(data->memory.user_data, data->cameras);
@ -1970,6 +2014,8 @@ void cgltf_free(cgltf_data* data)
for (cgltf_size i = 0; i < data->lights_count; ++i)
{
data->memory.free_func(data->memory.user_data, data->lights[i].name);
cgltf_free_extras(data, &data->lights[i].extras);
}
data->memory.free_func(data->memory.user_data, data->lights);
@ -1979,7 +2025,19 @@ void cgltf_free(cgltf_data* data)
data->memory.free_func(data->memory.user_data, data->nodes[i].name);
data->memory.free_func(data->memory.user_data, data->nodes[i].children);
data->memory.free_func(data->memory.user_data, data->nodes[i].weights);
if (data->nodes[i].has_mesh_gpu_instancing)
{
for (cgltf_size j = 0; j < data->nodes[i].mesh_gpu_instancing.attributes_count; ++j)
{
data->memory.free_func(data->memory.user_data, data->nodes[i].mesh_gpu_instancing.attributes[j].name);
}
data->memory.free_func(data->memory.user_data, data->nodes[i].mesh_gpu_instancing.attributes);
}
cgltf_free_extensions(data, data->nodes[i].extensions, data->nodes[i].extensions_count);
cgltf_free_extras(data, &data->nodes[i].extras);
}
data->memory.free_func(data->memory.user_data, data->nodes);
@ -1990,6 +2048,7 @@ void cgltf_free(cgltf_data* data)
data->memory.free_func(data->memory.user_data, data->scenes[i].nodes);
cgltf_free_extensions(data, data->scenes[i].extensions, data->scenes[i].extensions_count);
cgltf_free_extras(data, &data->scenes[i].extras);
}
data->memory.free_func(data->memory.user_data, data->scenes);
@ -2000,16 +2059,19 @@ void cgltf_free(cgltf_data* data)
for (cgltf_size j = 0; j < data->animations[i].samplers_count; ++j)
{
cgltf_free_extensions(data, data->animations[i].samplers[j].extensions, data->animations[i].samplers[j].extensions_count);
cgltf_free_extras(data, &data->animations[i].samplers[j].extras);
}
data->memory.free_func(data->memory.user_data, data->animations[i].samplers);
for (cgltf_size j = 0; j < data->animations[i].channels_count; ++j)
{
cgltf_free_extensions(data, data->animations[i].channels[j].extensions, data->animations[i].channels[j].extensions_count);
cgltf_free_extras(data, &data->animations[i].channels[j].extras);
}
data->memory.free_func(data->memory.user_data, data->animations[i].channels);
cgltf_free_extensions(data, data->animations[i].extensions, data->animations[i].extensions_count);
cgltf_free_extras(data, &data->animations[i].extras);
}
data->memory.free_func(data->memory.user_data, data->animations);
@ -2017,11 +2079,14 @@ void cgltf_free(cgltf_data* data)
for (cgltf_size i = 0; i < data->variants_count; ++i)
{
data->memory.free_func(data->memory.user_data, data->variants[i].name);
cgltf_free_extras(data, &data->variants[i].extras);
}
data->memory.free_func(data->memory.user_data, data->variants);
cgltf_free_extensions(data, data->data_extensions, data->data_extensions_count);
cgltf_free_extras(data, &data->extras);
for (cgltf_size i = 0; i < data->extensions_used_count; ++i)
{
@ -2440,7 +2505,7 @@ static int cgltf_json_strcmp(jsmntok_t const* tok, const uint8_t* json_chunk, co
{
CGLTF_CHECK_TOKTYPE(*tok, JSMN_STRING);
size_t const str_len = strlen(str);
size_t const name_length = tok->end - tok->start;
size_t const name_length = (size_t)(tok->end - tok->start);
return (str_len == name_length) ? strncmp((const char*)json_chunk + tok->start, str, str_len) : 128;
}
@ -2448,7 +2513,7 @@ static int cgltf_json_to_int(jsmntok_t const* tok, const uint8_t* json_chunk)
{
CGLTF_CHECK_TOKTYPE(*tok, JSMN_PRIMITIVE);
char tmp[128];
int size = (cgltf_size)(tok->end - tok->start) < sizeof(tmp) ? tok->end - tok->start : (int)(sizeof(tmp) - 1);
int size = (size_t)(tok->end - tok->start) < sizeof(tmp) ? (int)(tok->end - tok->start) : (int)(sizeof(tmp) - 1);
strncpy(tmp, (const char*)json_chunk + tok->start, size);
tmp[size] = 0;
return CGLTF_ATOI(tmp);
@ -2458,7 +2523,7 @@ static cgltf_size cgltf_json_to_size(jsmntok_t const* tok, const uint8_t* json_c
{
CGLTF_CHECK_TOKTYPE_RETTYPE(*tok, JSMN_PRIMITIVE, cgltf_size);
char tmp[128];
int size = (cgltf_size)(tok->end - tok->start) < sizeof(tmp) ? tok->end - tok->start : (int)(sizeof(tmp) - 1);
int size = (size_t)(tok->end - tok->start) < sizeof(tmp) ? (int)(tok->end - tok->start) : (int)(sizeof(tmp) - 1);
strncpy(tmp, (const char*)json_chunk + tok->start, size);
tmp[size] = 0;
return (cgltf_size)CGLTF_ATOLL(tmp);
@ -2468,7 +2533,7 @@ static cgltf_float cgltf_json_to_float(jsmntok_t const* tok, const uint8_t* json
{
CGLTF_CHECK_TOKTYPE(*tok, JSMN_PRIMITIVE);
char tmp[128];
int size = (cgltf_size)(tok->end - tok->start) < sizeof(tmp) ? tok->end - tok->start : (int)(sizeof(tmp) - 1);
int size = (size_t)(tok->end - tok->start) < sizeof(tmp) ? (int)(tok->end - tok->start) : (int)(sizeof(tmp) - 1);
strncpy(tmp, (const char*)json_chunk + tok->start, size);
tmp[size] = 0;
return (cgltf_float)CGLTF_ATOF(tmp);
@ -2476,7 +2541,7 @@ static cgltf_float cgltf_json_to_float(jsmntok_t const* tok, const uint8_t* json
static cgltf_bool cgltf_json_to_bool(jsmntok_t const* tok, const uint8_t* json_chunk)
{
int size = tok->end - tok->start;
int size = (int)(tok->end - tok->start);
return size == 4 && memcmp(json_chunk + tok->start, "true", 4) == 0;
}
@ -2542,7 +2607,7 @@ static int cgltf_parse_json_string(cgltf_options* options, jsmntok_t const* toke
{
return CGLTF_ERROR_JSON;
}
int size = tokens[i].end - tokens[i].start;
int size = (int)(tokens[i].end - tokens[i].start);
char* result = (char*)options->memory.alloc_func(options->memory.user_data, size + 1);
if (!result)
{
@ -2683,11 +2748,27 @@ static int cgltf_parse_json_attribute_list(cgltf_options* options, jsmntok_t con
return i;
}
static int cgltf_parse_json_extras(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_extras* out_extras)
static int cgltf_parse_json_extras(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_extras* out_extras)
{
(void)json_chunk;
if (out_extras->data)
{
return CGLTF_ERROR_JSON;
}
/* fill deprecated fields for now, this will be removed in the future */
out_extras->start_offset = tokens[i].start;
out_extras->end_offset = tokens[i].end;
size_t start = tokens[i].start;
size_t size = tokens[i].end - start;
out_extras->data = (char*)options->memory.alloc_func(options->memory.user_data, size + 1);
if (!out_extras->data)
{
return CGLTF_ERROR_NOMEM;
}
strncpy(out_extras->data, (const char*)json_chunk + start, size);
out_extras->data[size] = '\0';
i = cgltf_skip_json(tokens, i);
return i;
}
@ -2842,7 +2923,7 @@ static int cgltf_parse_json_material_mapping_data(cgltf_options* options, jsmnto
int material = -1;
int variants_tok = -1;
cgltf_extras extras = {0, 0};
int extras_tok = -1;
for (int k = 0; k < obj_size; ++k)
{
@ -2863,7 +2944,8 @@ static int cgltf_parse_json_material_mapping_data(cgltf_options* options, jsmnto
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &extras);
extras_tok = i + 1;
i = cgltf_skip_json(tokens, extras_tok);
}
else
{
@ -2891,7 +2973,13 @@ static int cgltf_parse_json_material_mapping_data(cgltf_options* options, jsmnto
out_mappings[*offset].material = CGLTF_PTRINDEX(cgltf_material, material);
out_mappings[*offset].variant = variant;
out_mappings[*offset].extras = extras;
if (extras_tok >= 0)
{
int e = cgltf_parse_json_extras(options, tokens, extras_tok, json_chunk, &out_mappings[*offset].extras);
if (e < 0)
return e;
}
(*offset)++;
}
@ -3006,7 +3094,7 @@ static int cgltf_parse_json_primitive(cgltf_options* options, jsmntok_t const* t
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_prim->extras);
i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_prim->extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@ -3253,7 +3341,7 @@ static int cgltf_parse_json_accessor_sparse(cgltf_options* options, jsmntok_t co
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sparse->indices_extras);
i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sparse->indices_extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@ -3296,7 +3384,7 @@ static int cgltf_parse_json_accessor_sparse(cgltf_options* options, jsmntok_t co
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sparse->values_extras);
i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sparse->values_extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@ -3315,7 +3403,7 @@ static int cgltf_parse_json_accessor_sparse(cgltf_options* options, jsmntok_t co
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sparse->extras);
i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sparse->extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@ -3438,7 +3526,7 @@ static int cgltf_parse_json_accessor(cgltf_options* options, jsmntok_t const* to
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_accessor->extras);
i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_accessor->extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@ -3544,7 +3632,7 @@ static int cgltf_parse_json_texture_view(cgltf_options* options, jsmntok_t const
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_texture_view->extras);
i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_texture_view->extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@ -3640,10 +3728,6 @@ static int cgltf_parse_json_pbr_metallic_roughness(cgltf_options* options, jsmnt
i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk,
&out_pbr->metallic_roughness_texture);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_pbr->extras);
}
else
{
i = cgltf_skip_json(tokens, i+1);
@ -4076,7 +4160,7 @@ static int cgltf_parse_json_image(cgltf_options* options, jsmntok_t const* token
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_image->extras);
i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_image->extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@ -4145,7 +4229,7 @@ static int cgltf_parse_json_sampler(cgltf_options* options, jsmntok_t const* tok
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sampler->extras);
i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sampler->extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@ -4194,7 +4278,7 @@ static int cgltf_parse_json_texture(cgltf_options* options, jsmntok_t const* tok
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_texture->extras);
i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_texture->extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@ -4357,7 +4441,7 @@ static int cgltf_parse_json_material(cgltf_options* options, jsmntok_t const* to
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_material->extras);
i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_material->extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@ -4710,7 +4794,7 @@ static int cgltf_parse_json_buffer_view(cgltf_options* options, jsmntok_t const*
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_buffer_view->extras);
i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_buffer_view->extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@ -4813,7 +4897,7 @@ static int cgltf_parse_json_buffer(cgltf_options* options, jsmntok_t const* toke
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_buffer->extras);
i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_buffer->extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@ -4897,7 +4981,7 @@ static int cgltf_parse_json_skin(cgltf_options* options, jsmntok_t const* tokens
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_skin->extras);
i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_skin->extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@ -4951,19 +5035,6 @@ static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* toke
{
i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_camera->name);
}
else if (cgltf_json_strcmp(tokens+i, json_chunk, "type") == 0)
{
++i;
if (cgltf_json_strcmp(tokens + i, json_chunk, "perspective") == 0)
{
out_camera->type = cgltf_camera_type_perspective;
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "orthographic") == 0)
{
out_camera->type = cgltf_camera_type_orthographic;
}
++i;
}
else if (cgltf_json_strcmp(tokens+i, json_chunk, "perspective") == 0)
{
++i;
@ -4973,6 +5044,11 @@ static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* toke
int data_size = tokens[i].size;
++i;
if (out_camera->type != cgltf_camera_type_invalid)
{
return CGLTF_ERROR_JSON;
}
out_camera->type = cgltf_camera_type_perspective;
for (int k = 0; k < data_size; ++k)
@ -5007,7 +5083,7 @@ static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* toke
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_camera->data.perspective.extras);
i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_camera->data.perspective.extras);
}
else
{
@ -5029,6 +5105,11 @@ static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* toke
int data_size = tokens[i].size;
++i;
if (out_camera->type != cgltf_camera_type_invalid)
{
return CGLTF_ERROR_JSON;
}
out_camera->type = cgltf_camera_type_orthographic;
for (int k = 0; k < data_size; ++k)
@ -5061,7 +5142,7 @@ static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* toke
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_camera->data.orthographic.extras);
i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_camera->data.orthographic.extras);
}
else
{
@ -5076,7 +5157,7 @@ static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* toke
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_camera->extras);
i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_camera->extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@ -5209,7 +5290,7 @@ static int cgltf_parse_json_light(cgltf_options* options, jsmntok_t const* token
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_light->extras);
i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_light->extras);
}
else
{
@ -5335,7 +5416,7 @@ static int cgltf_parse_json_node(cgltf_options* options, jsmntok_t const* tokens
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_node->extras);
i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_node->extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@ -5473,7 +5554,7 @@ static int cgltf_parse_json_scene(cgltf_options* options, jsmntok_t const* token
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_scene->extras);
i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_scene->extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@ -5555,7 +5636,7 @@ static int cgltf_parse_json_animation_sampler(cgltf_options* options, jsmntok_t
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sampler->extras);
i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sampler->extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@ -5635,7 +5716,7 @@ static int cgltf_parse_json_animation_channel(cgltf_options* options, jsmntok_t
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_channel->extras);
i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_channel->extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@ -5717,7 +5798,7 @@ static int cgltf_parse_json_animation(cgltf_options* options, jsmntok_t const* t
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_animation->extras);
i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_animation->extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@ -5773,7 +5854,7 @@ static int cgltf_parse_json_variant(cgltf_options* options, jsmntok_t const* tok
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_variant->extras);
i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_variant->extras);
}
else
{
@ -5837,7 +5918,7 @@ static int cgltf_parse_json_asset(cgltf_options* options, jsmntok_t const* token
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0)
{
i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_asset->extras);
i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_asset->extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@ -5993,7 +6074,7 @@ static int cgltf_parse_json_root(cgltf_options* options, jsmntok_t const* tokens
}
else if (cgltf_json_strcmp(tokens+i, json_chunk, "extras") == 0)
{
i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_data->extras);
i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_data->extras);
}
else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0)
{
@ -6420,7 +6501,7 @@ static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
* Fills token type and boundaries.
*/
static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
int start, int end) {
ptrdiff_t start, ptrdiff_t end) {
token->type = type;
token->start = start;
token->end = end;
@ -6433,7 +6514,7 @@ static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
size_t len, jsmntok_t *tokens, size_t num_tokens) {
jsmntok_t *token;
int start;
ptrdiff_t start;
start = parser->pos;
@ -6483,7 +6564,7 @@ static int jsmn_parse_string(jsmn_parser *parser, const char *js,
size_t len, jsmntok_t *tokens, size_t num_tokens) {
jsmntok_t *token;
int start = parser->pos;
ptrdiff_t start = parser->pos;
parser->pos++;

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
/*
MP3 audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file.
dr_mp3 - v0.6.31 - 2021-08-22
dr_mp3 - v0.6.34 - 2022-09-17
David Reid - mackron@gmail.com
@ -95,7 +95,7 @@ extern "C" {
#define DRMP3_VERSION_MAJOR 0
#define DRMP3_VERSION_MINOR 6
#define DRMP3_VERSION_REVISION 31
#define DRMP3_VERSION_REVISION 34
#define DRMP3_VERSION_STRING DRMP3_XSTRINGIFY(DRMP3_VERSION_MAJOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_MINOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_REVISION)
#include <stddef.h> /* For size_t. */
@ -107,7 +107,7 @@ typedef signed short drmp3_int16;
typedef unsigned short drmp3_uint16;
typedef signed int drmp3_int32;
typedef unsigned int drmp3_uint32;
#if defined(_MSC_VER)
#if defined(_MSC_VER) && !defined(__clang__)
typedef signed __int64 drmp3_int64;
typedef unsigned __int64 drmp3_uint64;
#else
@ -235,9 +235,15 @@ typedef drmp3_int32 drmp3_result;
I am using "__inline__" only when we're compiling in strict ANSI mode.
*/
#if defined(__STRICT_ANSI__)
#define DRMP3_INLINE __inline__ __attribute__((always_inline))
#define DRMP3_GNUC_INLINE_HINT __inline__
#else
#define DRMP3_INLINE inline __attribute__((always_inline))
#define DRMP3_GNUC_INLINE_HINT inline
#endif
#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__)
#define DRMP3_INLINE DRMP3_GNUC_INLINE_HINT __attribute__((always_inline))
#else
#define DRMP3_INLINE DRMP3_GNUC_INLINE_HINT
#endif
#elif defined(__WATCOMC__)
#define DRMP3_INLINE __inline
@ -340,7 +346,6 @@ typedef struct
typedef struct
{
drmp3dec decoder;
drmp3dec_frame_info frameInfo;
drmp3_uint32 channels;
drmp3_uint32 sampleRate;
drmp3_read_proc onRead;
@ -595,7 +600,7 @@ DRMP3_API const char* drmp3_version_string(void)
#define DR_MP3_ONLY_SIMD
#endif
#if ((defined(_MSC_VER) && _MSC_VER >= 1400) && (defined(_M_IX86) || defined(_M_X64))) || ((defined(__i386__) || defined(__x86_64__)) && defined(__SSE2__))
#if ((defined(_MSC_VER) && _MSC_VER >= 1400) && defined(_M_X64)) || ((defined(__i386) || defined(_M_IX86) || defined(__i386__) || defined(__x86_64__)) && ((defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__)))
#if defined(_MSC_VER)
#include <intrin.h>
#endif
@ -1296,7 +1301,7 @@ static void drmp3_L3_huffman(float *dst, drmp3_bs *bs, const drmp3_L3_gr_info *g
static const drmp3_int16 tabindex[2*16] = { 0,32,64,98,0,132,180,218,292,364,426,538,648,746,0,1126,1460,1460,1460,1460,1460,1460,1460,1460,1842,1842,1842,1842,1842,1842,1842,1842 };
static const drmp3_uint8 g_linbits[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,6,8,10,13,4,5,6,7,8,9,11,13 };
#define DRMP3_PEEK_BITS(n) (bs_cache >> (32 - n))
#define DRMP3_PEEK_BITS(n) (bs_cache >> (32 - (n)))
#define DRMP3_FLUSH_BITS(n) { bs_cache <<= (n); bs_sh += (n); }
#define DRMP3_CHECK_BITS while (bs_sh >= 0) { bs_cache |= (drmp3_uint32)*bs_next_ptr++ << bs_sh; bs_sh -= 8; }
#define DRMP3_BSPOS ((bs_next_ptr - bs->buf)*8 - 24 + bs_sh)
@ -1864,7 +1869,7 @@ static void drmp3d_DCT_II(float *grbuf, int n)
#if DRMP3_HAVE_SSE
#define DRMP3_VSAVE2(i, v) _mm_storel_pi((__m64 *)(void*)&y[i*18], v)
#else
#define DRMP3_VSAVE2(i, v) vst1_f32((float32_t *)&y[i*18], vget_low_f32(v))
#define DRMP3_VSAVE2(i, v) vst1_f32((float32_t *)&y[(i)*18], vget_low_f32(v))
#endif
for (i = 0; i < 7; i++, y += 4*18)
{
@ -1880,7 +1885,7 @@ static void drmp3d_DCT_II(float *grbuf, int n)
DRMP3_VSAVE2(3, t[3][7]);
} else
{
#define DRMP3_VSAVE4(i, v) DRMP3_VSTORE(&y[i*18], v)
#define DRMP3_VSAVE4(i, v) DRMP3_VSTORE(&y[(i)*18], v)
for (i = 0; i < 7; i++, y += 4*18)
{
drmp3_f4 s = DRMP3_VADD(t[3][i], t[3][i + 1]);
@ -2103,7 +2108,11 @@ static void drmp3d_synth(float *xl, drmp3d_sample_t *dstl, int nch, float *lins)
vst1_lane_s16(dstl + (49 + i)*nch, pcmb, 2);
#endif
#else
#if DRMP3_HAVE_SSE
static const drmp3_f4 g_scale = { 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f };
#else
const drmp3_f4 g_scale = vdupq_n_f32(1.0f/32768.0f);
#endif
a = DRMP3_VMUL(a, g_scale);
b = DRMP3_VMUL(b, g_scale);
#if DRMP3_HAVE_SSE
@ -2406,8 +2415,6 @@ DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num
Main Public API
************************************************************************************************************************************************************/
#include <math.h> /* For sin() and exp(). */
#if defined(SIZE_MAX)
#define DRMP3_SIZE_MAX SIZE_MAX
#else
@ -2427,7 +2434,7 @@ DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num
/* The size in bytes of each chunk of data to read from the MP3 stream. minimp3 recommends at least 16K, but in an attempt to reduce data movement I'm making this slightly larger. */
#ifndef DRMP3_DATA_CHUNK_SIZE
#define DRMP3_DATA_CHUNK_SIZE DRMP3_MIN_DATA_CHUNK_SIZE*4
#define DRMP3_DATA_CHUNK_SIZE (DRMP3_MIN_DATA_CHUNK_SIZE*4)
#endif
@ -2472,24 +2479,6 @@ static DRMP3_INLINE drmp3_uint32 drmp3_gcf_u32(drmp3_uint32 a, drmp3_uint32 b)
}
static DRMP3_INLINE double drmp3_sin(double x)
{
/* TODO: Implement custom sin(x). */
return sin(x);
}
static DRMP3_INLINE double drmp3_exp(double x)
{
/* TODO: Implement custom exp(x). */
return exp(x);
}
static DRMP3_INLINE double drmp3_cos(double x)
{
return drmp3_sin((DRMP3_PI_D*0.5) - x);
}
static void* drmp3__malloc_default(size_t sz, void* pUserData)
{
(void)pUserData;
@ -3434,10 +3423,23 @@ static drmp3_result drmp3_wfopen(FILE** ppFile, const wchar_t* pFilePath, const
}
#else
/*
Use fopen() on anything other than Windows. Requires a conversion. This is annoying because fopen() is locale specific. The only real way I can
think of to do this is with wcsrtombs(). Note that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for
maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler error I'll look into improving compatibility.
Use fopen() on anything other than Windows. Requires a conversion. This is annoying because
fopen() is locale specific. The only real way I can think of to do this is with wcsrtombs(). Note
that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for
maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler
error I'll look into improving compatibility.
*/
/*
Some compilers don't support wchar_t or wcsrtombs() which we're using below. In this case we just
need to abort with an error. If you encounter a compiler lacking such support, add it to this list
and submit a bug report and it'll be added to the library upstream.
*/
#if defined(__DJGPP__)
{
/* Nothing to do here. This will fall through to the error check below. */
}
#else
{
mbstate_t mbs;
size_t lenMB;
@ -3479,6 +3481,7 @@ static drmp3_result drmp3_wfopen(FILE** ppFile, const wchar_t* pFilePath, const
drmp3__free_from_callbacks(pFilePathMB, pAllocationCallbacks);
}
#endif
if (*ppFile == NULL) {
return DRMP3_ERROR;
@ -4473,6 +4476,19 @@ counts rather than sample counts.
/*
REVISION HISTORY
================
v0.6.34 - 2022-09-17
- Fix compilation with DJGPP.
- Fix compilation when compiling with x86 with no SSE2.
- Remove an unnecessary variable from the drmp3 structure.
v0.6.33 - 2022-04-10
- Fix compilation error with the MSVC ARM64 build.
- Fix compilation error on older versions of GCC.
- Remove some unused functions.
v0.6.32 - 2021-12-11
- Fix a warning with Clang.
v0.6.31 - 2021-08-22
- Fix a bug when loading from memory.

View file

@ -1,6 +1,6 @@
/*
WAV audio loader and writer. Choice of public domain or MIT-0. See license statements at the end of this file.
dr_wav - v0.13.4 - 2021-12-08
dr_wav - v0.13.7 - 2022-09-17
David Reid - mackron@gmail.com
@ -92,6 +92,9 @@ Build Options
#define DR_WAV_NO_STDIO
Disables APIs that initialize a decoder from a file such as `drwav_init_file()`, `drwav_init_file_write()`, etc.
#define DR_WAV_NO_WCHAR
Disables all functions ending with `_w`. Use this if your compiler does not provide wchar.h. Not required if DR_WAV_NO_STDIO is also defined.
Notes
@ -125,7 +128,7 @@ extern "C" {
#define DRWAV_VERSION_MAJOR 0
#define DRWAV_VERSION_MINOR 13
#define DRWAV_VERSION_REVISION 4
#define DRWAV_VERSION_REVISION 7
#define DRWAV_VERSION_STRING DRWAV_XSTRINGIFY(DRWAV_VERSION_MAJOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_MINOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_REVISION)
#include <stddef.h> /* For size_t. */
@ -1297,14 +1300,21 @@ DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b);
#ifndef dr_wav_c
#define dr_wav_c
#ifdef __MRC__
/* MrC currently doesn't compile dr_wav correctly with any optimizations enabled. */
#pragma options opt off
#endif
#include <stdlib.h>
#include <string.h> /* For memcpy(), memset() */
#include <string.h>
#include <limits.h> /* For INT_MAX */
#ifndef DR_WAV_NO_STDIO
#include <stdio.h>
#ifndef DR_WAV_NO_WCHAR
#include <wchar.h>
#endif
#endif
/* Standard library stuff. */
#ifndef DRWAV_ASSERT
@ -1359,9 +1369,15 @@ DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b);
I am using "__inline__" only when we're compiling in strict ANSI mode.
*/
#if defined(__STRICT_ANSI__)
#define DRWAV_INLINE __inline__ __attribute__((always_inline))
#define DRWAV_GNUC_INLINE_HINT __inline__
#else
#define DRWAV_INLINE inline __attribute__((always_inline))
#define DRWAV_GNUC_INLINE_HINT inline
#endif
#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__)
#define DRWAV_INLINE DRWAV_GNUC_INLINE_HINT __attribute__((always_inline))
#else
#define DRWAV_INLINE DRWAV_GNUC_INLINE_HINT
#endif
#elif defined(__WATCOMC__)
#define DRWAV_INLINE __inline
@ -1966,7 +1982,7 @@ DRWAV_PRIVATE drwav_bool32 drwav__read_fmt(drwav_read_proc onRead, drwav_seek_pr
fmtOut->extendedSize = 0;
fmtOut->validBitsPerSample = 0;
fmtOut->channelMask = 0;
memset(fmtOut->subFormat, 0, sizeof(fmtOut->subFormat));
DRWAV_ZERO_MEMORY(fmtOut->subFormat, sizeof(fmtOut->subFormat));
if (header.sizeInBytes > 16) {
drwav_uint8 fmt_cbSize[2];
@ -2137,7 +2153,7 @@ DRWAV_PRIVATE void drwav__metadata_request_extra_memory_for_stage_2(drwav__metad
DRWAV_PRIVATE drwav_result drwav__metadata_alloc(drwav__metadata_parser* pParser, drwav_allocation_callbacks* pAllocationCallbacks)
{
if (pParser->extraCapacity != 0 || pParser->metadataCount != 0) {
free(pParser->pData);
pAllocationCallbacks->onFree(pParser->pData, pAllocationCallbacks->pUserData);
pParser->pData = (drwav_uint8*)pAllocationCallbacks->onMalloc(drwav__metadata_memory_capacity(pParser), pAllocationCallbacks->pUserData);
pParser->pDataCursor = pParser->pData;
@ -2316,6 +2332,17 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_acid_to_metadata_obj(drwav__metadata_pars
return bytesRead;
}
DRWAV_PRIVATE size_t drwav__strlen(const char* str)
{
size_t result = 0;
while (*str++) {
result += 1;
}
return result;
}
DRWAV_PRIVATE size_t drwav__strlen_clamped(const char* str, size_t maxToRead)
{
size_t result = 0;
@ -2335,7 +2362,7 @@ DRWAV_PRIVATE char* drwav__metadata_copy_string(drwav__metadata_parser* pParser,
char* result = (char*)drwav__metadata_get_memory(pParser, len + 1, 1);
DRWAV_ASSERT(result != NULL);
memcpy(result, str, len);
DRWAV_COPY_MEMORY(result, str, len);
result[len] = '\0';
return result;
@ -2516,7 +2543,7 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_bext_to_metadata_obj(drwav__metadata_pars
DRWAV_ASSERT(pMetadata->data.bext.pCodingHistory != NULL);
bytesRead += drwav__metadata_parser_read(pParser, pMetadata->data.bext.pCodingHistory, extraBytes, NULL);
pMetadata->data.bext.codingHistorySize = (drwav_uint32)strlen(pMetadata->data.bext.pCodingHistory);
pMetadata->data.bext.codingHistorySize = (drwav_uint32)drwav__strlen(pMetadata->data.bext.pCodingHistory);
} else {
pMetadata->data.bext.pCodingHistory = NULL;
pMetadata->data.bext.codingHistorySize = 0;
@ -2780,21 +2807,21 @@ DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser*
if (bytesJustRead != DRWAV_BEXT_DESCRIPTION_BYTES) {
return bytesRead;
}
allocSizeNeeded += strlen(buffer) + 1;
allocSizeNeeded += drwav__strlen(buffer) + 1;
buffer[DRWAV_BEXT_ORIGINATOR_NAME_BYTES] = '\0';
bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_ORIGINATOR_NAME_BYTES, &bytesRead);
if (bytesJustRead != DRWAV_BEXT_ORIGINATOR_NAME_BYTES) {
return bytesRead;
}
allocSizeNeeded += strlen(buffer) + 1;
allocSizeNeeded += drwav__strlen(buffer) + 1;
buffer[DRWAV_BEXT_ORIGINATOR_REF_BYTES] = '\0';
bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_ORIGINATOR_REF_BYTES, &bytesRead);
if (bytesJustRead != DRWAV_BEXT_ORIGINATOR_REF_BYTES) {
return bytesRead;
}
allocSizeNeeded += strlen(buffer) + 1;
allocSizeNeeded += drwav__strlen(buffer) + 1;
allocSizeNeeded += (size_t)pChunkHeader->sizeInBytes - DRWAV_BEXT_BYTES; /* Coding history. */
drwav__metadata_request_extra_memory_for_stage_2(pParser, allocSizeNeeded, 1);
@ -3157,7 +3184,7 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on
translatedFormatTag = drwav_bytes_to_u16(fmt.subFormat + 0);
}
memset(&metadataParser, 0, sizeof(metadataParser));
DRWAV_ZERO_MEMORY(&metadataParser, sizeof(metadataParser));
/* Not tested on W64. */
if (!sequential && pWav->allowedMetadataTypes != drwav_metadata_type_none && (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64)) {
@ -3746,7 +3773,7 @@ DRWAV_PRIVATE size_t drwav__write_or_count_metadata(drwav* pWav, drwav_metadata*
bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxMomentaryLoudness);
bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxShortTermLoudness);
memset(reservedBuf, 0, sizeof(reservedBuf));
DRWAV_ZERO_MEMORY(reservedBuf, sizeof(reservedBuf));
bytesWritten += drwav__write_or_count(pWav, reservedBuf, sizeof(reservedBuf));
if (pMetadata->data.bext.codingHistorySize > 0) {
@ -4704,6 +4731,7 @@ fallback, so if you notice your compiler not detecting this properly I'm happy t
#endif
#endif
#ifndef DR_WAV_NO_WCHAR
DRWAV_PRIVATE drwav_result drwav_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drwav_allocation_callbacks* pAllocationCallbacks)
{
if (ppFile != NULL) {
@ -4731,11 +4759,24 @@ DRWAV_PRIVATE drwav_result drwav_wfopen(FILE** ppFile, const wchar_t* pFilePath,
(void)pAllocationCallbacks;
}
#else
/*
Use fopen() on anything other than Windows. Requires a conversion. This is annoying because fopen() is locale specific. The only real way I can
think of to do this is with wcsrtombs(). Note that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for
maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler error I'll look into improving compatibility.
/*
Use fopen() on anything other than Windows. Requires a conversion. This is annoying because
fopen() is locale specific. The only real way I can think of to do this is with wcsrtombs(). Note
that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for
maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler
error I'll look into improving compatibility.
*/
/*
Some compilers don't support wchar_t or wcsrtombs() which we're using below. In this case we just
need to abort with an error. If you encounter a compiler lacking such support, add it to this list
and submit a bug report and it'll be added to the library upstream.
*/
#if defined(__DJGPP__)
{
/* Nothing to do here. This will fall through to the error check below. */
}
#else
{
mbstate_t mbs;
size_t lenMB;
@ -4777,6 +4818,7 @@ DRWAV_PRIVATE drwav_result drwav_wfopen(FILE** ppFile, const wchar_t* pFilePath,
drwav__free_from_callbacks(pFilePathMB, pAllocationCallbacks);
}
#endif
if (*ppFile == NULL) {
return DRWAV_ERROR;
@ -4785,6 +4827,7 @@ DRWAV_PRIVATE drwav_result drwav_wfopen(FILE** ppFile, const wchar_t* pFilePath,
return DRWAV_SUCCESS;
}
#endif
DRWAV_PRIVATE size_t drwav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead)
@ -4840,6 +4883,7 @@ DRWAV_API drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drw
return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, drwav_metadata_type_none, pAllocationCallbacks);
}
#ifndef DR_WAV_NO_WCHAR
DRWAV_API drwav_bool32 drwav_init_file_w(drwav* pWav, const wchar_t* filename, const drwav_allocation_callbacks* pAllocationCallbacks)
{
return drwav_init_file_ex_w(pWav, filename, NULL, NULL, 0, pAllocationCallbacks);
@ -4855,6 +4899,7 @@ DRWAV_API drwav_bool32 drwav_init_file_ex_w(drwav* pWav, const wchar_t* filename
/* This takes ownership of the FILE* object. */
return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, drwav_metadata_type_none, pAllocationCallbacks);
}
#endif
DRWAV_API drwav_bool32 drwav_init_file_with_metadata(drwav* pWav, const char* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
{
@ -4867,6 +4912,7 @@ DRWAV_API drwav_bool32 drwav_init_file_with_metadata(drwav* pWav, const char* fi
return drwav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags, drwav_metadata_type_all_including_unknown, pAllocationCallbacks);
}
#ifndef DR_WAV_NO_WCHAR
DRWAV_API drwav_bool32 drwav_init_file_with_metadata_w(drwav* pWav, const wchar_t* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
{
FILE* pFile;
@ -4877,6 +4923,7 @@ DRWAV_API drwav_bool32 drwav_init_file_with_metadata_w(drwav* pWav, const wchar_
/* This takes ownership of the FILE* object. */
return drwav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags, drwav_metadata_type_all_including_unknown, pAllocationCallbacks);
}
#endif
DRWAV_PRIVATE drwav_bool32 drwav_init_file_write__internal_FILE(drwav* pWav, FILE* pFile, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks)
@ -4909,6 +4956,7 @@ DRWAV_PRIVATE drwav_bool32 drwav_init_file_write__internal(drwav* pWav, const ch
return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks);
}
#ifndef DR_WAV_NO_WCHAR
DRWAV_PRIVATE drwav_bool32 drwav_init_file_write_w__internal(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks)
{
FILE* pFile;
@ -4919,6 +4967,7 @@ DRWAV_PRIVATE drwav_bool32 drwav_init_file_write_w__internal(drwav* pWav, const
/* This takes ownership of the FILE* object. */
return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks);
}
#endif
DRWAV_API drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks)
{
@ -4939,6 +4988,7 @@ DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames(drwav* pWav,
return drwav_init_file_write_sequential(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks);
}
#ifndef DR_WAV_NO_WCHAR
DRWAV_API drwav_bool32 drwav_init_file_write_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks)
{
return drwav_init_file_write_w__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks);
@ -4957,6 +5007,7 @@ DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames_w(drwav* pWav
return drwav_init_file_write_sequential_w(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks);
}
#endif
#endif /* DR_WAV_NO_STDIO */
@ -5441,8 +5492,8 @@ DRWAV_API drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetF
}
/* Make sure the sample is clamped. */
if (targetFrameIndex >= pWav->totalPCMFrameCount) {
targetFrameIndex = pWav->totalPCMFrameCount - 1;
if (targetFrameIndex > pWav->totalPCMFrameCount) {
targetFrameIndex = pWav->totalPCMFrameCount;
}
/*
@ -7650,6 +7701,7 @@ DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filen
}
#ifndef DR_WAV_NO_WCHAR
DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
{
drwav wav;
@ -7712,7 +7764,8 @@ DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32_w(const wchar_t*
return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
}
#endif
#endif /* DR_WAV_NO_WCHAR */
#endif /* DR_WAV_NO_STDIO */
DRWAV_API drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
{
@ -7853,12 +7906,28 @@ DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b)
a[3] == b[3];
}
#ifdef __MRC__
/* Undo the pragma at the beginning of this file. */
#pragma options opt reset
#endif
#endif /* dr_wav_c */
#endif /* DR_WAV_IMPLEMENTATION */
/*
REVISION HISTORY
================
v0.13.7 - 2022-09-17
- Fix compilation with DJGPP.
- Add support for disabling wchar_t with DR_WAV_NO_WCHAR.
v0.13.6 - 2022-04-10
- Fix compilation error on older versions of GCC.
- Remove some dependencies on the standard library.
v0.13.5 - 2022-01-26
- Fix an error when seeking to the end of the file.
v0.13.4 - 2021-12-08
- Fix some static analysis warnings.

4774
raylib/external/glad_gles2.h vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -518,10 +518,6 @@ int jar_xm_create_context(jar_xm_context_t** ctxp, const char* moddata, uint32_t
return jar_xm_create_context_safe(ctxp, moddata, SIZE_MAX, rate);
}
#ifdef ALIGN
#undef ALIGN
#endif
#define ALIGN(x, b) (((x) + ((b) - 1)) & ~((b) - 1))
#define ALIGN_PTR(x, b) (void*)(((uintptr_t)(x) + ((b) - 1)) & ~((b) - 1))
int jar_xm_create_context_safe(jar_xm_context_t** ctxp, const char* moddata, size_t moddata_length, uint32_t rate) {
@ -802,7 +798,7 @@ char* jar_xm_load_module(jar_xm_context_t* ctx, const char* moddata, size_t modd
mod->num_patterns = READ_U16(offset + 10);
mod->num_instruments = READ_U16(offset + 12);
mod->patterns = (jar_xm_pattern_t*)mempool;
mod->linear_interpolation = 0; // Linear interpolation can be set after loading
mod->linear_interpolation = 1; // Linear interpolation can be set after loading
mod->ramping = 1; // ramping can be set after loading
mempool += mod->num_patterns * sizeof(jar_xm_pattern_t);
mempool = ALIGN_PTR(mempool, 16);
@ -2257,6 +2253,7 @@ void jar_xm_reset(jar_xm_context_t* ctx) {
for (uint16_t i = 0; i < jar_xm_get_number_of_channels(ctx); i++) {
jar_xm_cut_note(&ctx->channels[i]);
}
ctx->generated_samples = 0;
ctx->current_row = 0;
ctx->current_table_index = 0;
ctx->current_tick = 0;

File diff suppressed because it is too large Load diff

660
raylib/external/qoa.h vendored Normal file
View file

@ -0,0 +1,660 @@
/*
Copyright (c) 2023, Dominic Szablewski - https://phoboslab.org
SPDX-License-Identifier: MIT
QOA - The "Quite OK Audio" format for fast, lossy audio compression
-- Data Format
A QOA file has an 8 byte file header, followed by a number of frames. Each frame
consists of an 8 byte frame header, the current 8 byte en-/decoder state per
channel and 256 slices per channel. Each slice is 8 bytes wide and encodes 20
samples of audio data.
Note that the last frame of a file may contain less than 256 slices per channel.
The last slice (per channel) in the last frame may contain less 20 samples, but
the slice will still be 8 bytes wide, with the unused samples zeroed out.
The samplerate and number of channels is only stated in the frame headers, but
not in the file header. A decoder may peek into the first frame of the file to
find these values.
In a valid QOA file all frames have the same number of channels and the same
samplerate. These restrictions may be relaxed for streaming. This remains to
be decided.
All values in a QOA file are BIG ENDIAN. Luckily, EVERYTHING in a QOA file,
including the headers, is 64 bit aligned, so it's possible to read files with
just a read_u64() that does the byte swapping if necessary.
In pseudocode, the file layout is as follows:
struct {
struct {
char magic[4]; // magic bytes 'qoaf'
uint32_t samples; // number of samples per channel in this file
} file_header; // = 64 bits
struct {
struct {
uint8_t num_channels; // number of channels
uint24_t samplerate; // samplerate in hz
uint16_t fsamples; // sample count per channel in this frame
uint16_t fsize; // frame size (including the frame header)
} frame_header; // = 64 bits
struct {
int16_t history[4]; // = 64 bits
int16_t weights[4]; // = 64 bits
} lms_state[num_channels];
qoa_slice_t slices[256][num_channels]; // = 64 bits each
} frames[samples * channels / qoa_max_framesize()];
} qoa_file;
Wheras the 64bit qoa_slice_t is defined as follows:
.- QOA_SLICE -- 64 bits, 20 samples --------------------------/ /------------.
| Byte[0] | Byte[1] | Byte[2] \ \ Byte[7] |
| 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 | 7 6 5 / / 2 1 0 |
|------------+--------+--------+--------+---------+---------+-\ \--+---------|
| sf_index | r00 | r01 | r02 | r03 | r04 | / / | r19 |
`-------------------------------------------------------------\ \------------`
`sf_index` defines the scalefactor to use for this slice as an index into the
qoa_scalefactor_tab[16]
`r00`--`r19` are the residuals for the individual samples, divided by the
scalefactor and quantized by the qoa_quant_tab[].
In the decoder, a prediction of the next sample is computed by multiplying the
state (the last four output samples) with the predictor. The residual from the
slice is then dequantized using the qoa_dequant_tab[] and added to the
prediction. The result is clamped to int16 to form the final output sample.
*/
/* -----------------------------------------------------------------------------
Header - Public functions */
#ifndef QOA_H
#define QOA_H
#ifdef __cplusplus
extern "C" {
#endif
#define QOA_MIN_FILESIZE 16
#define QOA_MAX_CHANNELS 8
#define QOA_SLICE_LEN 20
#define QOA_SLICES_PER_FRAME 256
#define QOA_FRAME_LEN (QOA_SLICES_PER_FRAME * QOA_SLICE_LEN)
#define QOA_LMS_LEN 4
#define QOA_MAGIC 0x716f6166 /* 'qoaf' */
#define QOA_FRAME_SIZE(channels, slices) \
(8 + QOA_LMS_LEN * 4 * channels + 8 * slices * channels)
typedef struct {
int history[QOA_LMS_LEN];
int weights[QOA_LMS_LEN];
} qoa_lms_t;
typedef struct {
unsigned int channels;
unsigned int samplerate;
unsigned int samples;
qoa_lms_t lms[QOA_MAX_CHANNELS];
#ifdef QOA_RECORD_TOTAL_ERROR
double error;
#endif
} qoa_desc;
unsigned int qoa_encode_header(qoa_desc *qoa, unsigned char *bytes);
unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned int frame_len, unsigned char *bytes);
void *qoa_encode(const short *sample_data, qoa_desc *qoa, unsigned int *out_len);
unsigned int qoa_max_frame_size(qoa_desc *qoa);
unsigned int qoa_decode_header(const unsigned char *bytes, int size, qoa_desc *qoa);
unsigned int qoa_decode_frame(const unsigned char *bytes, unsigned int size, qoa_desc *qoa, short *sample_data, unsigned int *frame_len);
short *qoa_decode(const unsigned char *bytes, int size, qoa_desc *file);
#ifndef QOA_NO_STDIO
int qoa_write(const char *filename, const short *sample_data, qoa_desc *qoa);
void *qoa_read(const char *filename, qoa_desc *qoa);
#endif /* QOA_NO_STDIO */
#ifdef __cplusplus
}
#endif
#endif /* QOA_H */
/* -----------------------------------------------------------------------------
Implementation */
#ifdef QOA_IMPLEMENTATION
#include <stdlib.h>
#ifndef QOA_MALLOC
#define QOA_MALLOC(sz) malloc(sz)
#define QOA_FREE(p) free(p)
#endif
typedef unsigned long long qoa_uint64_t;
/* The quant_tab provides an index into the dequant_tab for residuals in the
range of -8 .. 8. It maps this range to just 3bits and becomes less accurate at
the higher end. Note that the residual zero is identical to the lowest positive
value. This is mostly fine, since the qoa_div() function always rounds away
from zero. */
static int qoa_quant_tab[17] = {
7, 7, 7, 5, 5, 3, 3, 1, /* -8..-1 */
0, /* 0 */
0, 2, 2, 4, 4, 6, 6, 6 /* 1.. 8 */
};
/* We have 16 different scalefactors. Like the quantized residuals these become
less accurate at the higher end. In theory, the highest scalefactor that we
would need to encode the highest 16bit residual is (2**16)/8 = 8192. However we
rely on the LMS filter to predict samples accurately enough that a maximum
residual of one quarter of the 16 bit range is high sufficient. I.e. with the
scalefactor 2048 times the quant range of 8 we can encode residuals up to 2**14.
The scalefactor values are computed as:
scalefactor_tab[s] <- round(pow(s + 1, 2.75)) */
static int qoa_scalefactor_tab[16] = {
1, 7, 21, 45, 84, 138, 211, 304, 421, 562, 731, 928, 1157, 1419, 1715, 2048
};
/* The reciprocal_tab maps each of the 16 scalefactors to their rounded
reciprocals 1/scalefactor. This allows us to calculate the scaled residuals in
the encoder with just one multiplication instead of an expensive division. We
do this in .16 fixed point with integers, instead of floats.
The reciprocal_tab is computed as:
reciprocal_tab[s] <- ((1<<16) + scalefactor_tab[s] - 1) / scalefactor_tab[s] */
static int qoa_reciprocal_tab[16] = {
65536, 9363, 3121, 1457, 781, 475, 311, 216, 156, 117, 90, 71, 57, 47, 39, 32
};
/* The dequant_tab maps each of the scalefactors and quantized residuals to
their unscaled & dequantized version.
Since qoa_div rounds away from the zero, the smallest entries are mapped to 3/4
instead of 1. The dequant_tab assumes the following dequantized values for each
of the quant_tab indices and is computed as:
float dqt[8] = {0.75, -0.75, 2.5, -2.5, 4.5, -4.5, 7, -7};
dequant_tab[s][q] <- round(scalefactor_tab[s] * dqt[q]) */
static int qoa_dequant_tab[16][8] = {
{ 1, -1, 3, -3, 5, -5, 7, -7},
{ 5, -5, 18, -18, 32, -32, 49, -49},
{ 16, -16, 53, -53, 95, -95, 147, -147},
{ 34, -34, 113, -113, 203, -203, 315, -315},
{ 63, -63, 210, -210, 378, -378, 588, -588},
{ 104, -104, 345, -345, 621, -621, 966, -966},
{ 158, -158, 528, -528, 950, -950, 1477, -1477},
{ 228, -228, 760, -760, 1368, -1368, 2128, -2128},
{ 316, -316, 1053, -1053, 1895, -1895, 2947, -2947},
{ 422, -422, 1405, -1405, 2529, -2529, 3934, -3934},
{ 548, -548, 1828, -1828, 3290, -3290, 5117, -5117},
{ 696, -696, 2320, -2320, 4176, -4176, 6496, -6496},
{ 868, -868, 2893, -2893, 5207, -5207, 8099, -8099},
{1064, -1064, 3548, -3548, 6386, -6386, 9933, -9933},
{1286, -1286, 4288, -4288, 7718, -7718, 12005, -12005},
{1536, -1536, 5120, -5120, 9216, -9216, 14336, -14336},
};
/* The Least Mean Squares Filter is the heart of QOA. It predicts the next
sample based on the previous 4 reconstructed samples. It does so by continuously
adjusting 4 weights based on the residual of the previous prediction.
The next sample is predicted as the sum of (weight[i] * history[i]).
The adjustment of the weights is done with a "Sign-Sign-LMS" that adds or
subtracts the residual to each weight, based on the corresponding sample from
the history. This, surprisingly, is sufficient to get worthwhile predictions.
This is all done with fixed point integers. Hence the right-shifts when updating
the weights and calculating the prediction. */
static int qoa_lms_predict(qoa_lms_t *lms) {
int prediction = 0;
for (int i = 0; i < QOA_LMS_LEN; i++) {
prediction += lms->weights[i] * lms->history[i];
}
return prediction >> 13;
}
static void qoa_lms_update(qoa_lms_t *lms, int sample, int residual) {
int delta = residual >> 4;
for (int i = 0; i < QOA_LMS_LEN; i++) {
lms->weights[i] += lms->history[i] < 0 ? -delta : delta;
}
for (int i = 0; i < QOA_LMS_LEN-1; i++) {
lms->history[i] = lms->history[i+1];
}
lms->history[QOA_LMS_LEN-1] = sample;
}
/* qoa_div() implements a rounding division, but avoids rounding to zero for
small numbers. E.g. 0.1 will be rounded to 1. Note that 0 itself still
returns as 0, which is handled in the qoa_quant_tab[].
qoa_div() takes an index into the .16 fixed point qoa_reciprocal_tab as an
argument, so it can do the division with a cheaper integer multiplication. */
static inline int qoa_div(int v, int scalefactor) {
int reciprocal = qoa_reciprocal_tab[scalefactor];
int n = (v * reciprocal + (1 << 15)) >> 16;
n = n + ((v > 0) - (v < 0)) - ((n > 0) - (n < 0)); /* round away from 0 */
return n;
}
static inline int qoa_clamp(int v, int min, int max) {
return (v < min) ? min : (v > max) ? max : v;
}
static inline qoa_uint64_t qoa_read_u64(const unsigned char *bytes, unsigned int *p) {
bytes += *p;
*p += 8;
return
((qoa_uint64_t)(bytes[0]) << 56) | ((qoa_uint64_t)(bytes[1]) << 48) |
((qoa_uint64_t)(bytes[2]) << 40) | ((qoa_uint64_t)(bytes[3]) << 32) |
((qoa_uint64_t)(bytes[4]) << 24) | ((qoa_uint64_t)(bytes[5]) << 16) |
((qoa_uint64_t)(bytes[6]) << 8) | ((qoa_uint64_t)(bytes[7]) << 0);
}
static inline void qoa_write_u64(qoa_uint64_t v, unsigned char *bytes, unsigned int *p) {
bytes += *p;
*p += 8;
bytes[0] = (v >> 56) & 0xff;
bytes[1] = (v >> 48) & 0xff;
bytes[2] = (v >> 40) & 0xff;
bytes[3] = (v >> 32) & 0xff;
bytes[4] = (v >> 24) & 0xff;
bytes[5] = (v >> 16) & 0xff;
bytes[6] = (v >> 8) & 0xff;
bytes[7] = (v >> 0) & 0xff;
}
/* -----------------------------------------------------------------------------
Encoder */
unsigned int qoa_encode_header(qoa_desc *qoa, unsigned char *bytes) {
unsigned int p = 0;
qoa_write_u64(((qoa_uint64_t)QOA_MAGIC << 32) | qoa->samples, bytes, &p);
return p;
}
unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned int frame_len, unsigned char *bytes) {
unsigned int channels = qoa->channels;
unsigned int p = 0;
unsigned int slices = (frame_len + QOA_SLICE_LEN - 1) / QOA_SLICE_LEN;
unsigned int frame_size = QOA_FRAME_SIZE(channels, slices);
/* Write the frame header */
qoa_write_u64((
(qoa_uint64_t)qoa->channels << 56 |
(qoa_uint64_t)qoa->samplerate << 32 |
(qoa_uint64_t)frame_len << 16 |
(qoa_uint64_t)frame_size
), bytes, &p);
/* Write the current LMS state */
for (int c = 0; c < channels; c++) {
qoa_uint64_t weights = 0;
qoa_uint64_t history = 0;
for (int i = 0; i < QOA_LMS_LEN; i++) {
history = (history << 16) | (qoa->lms[c].history[i] & 0xffff);
weights = (weights << 16) | (qoa->lms[c].weights[i] & 0xffff);
}
qoa_write_u64(history, bytes, &p);
qoa_write_u64(weights, bytes, &p);
}
/* We encode all samples with the channels interleaved on a slice level.
E.g. for stereo: (ch-0, slice 0), (ch 1, slice 0), (ch 0, slice 1), ...*/
for (int sample_index = 0; sample_index < frame_len; sample_index += QOA_SLICE_LEN) {
for (int c = 0; c < channels; c++) {
int slice_len = qoa_clamp(QOA_SLICE_LEN, 0, frame_len - sample_index);
int slice_start = sample_index * channels + c;
int slice_end = (sample_index + slice_len) * channels + c;
/* Brute for search for the best scalefactor. Just go through all
16 scalefactors, encode all samples for the current slice and
meassure the total squared error. */
qoa_uint64_t best_error = -1;
qoa_uint64_t best_slice;
qoa_lms_t best_lms;
for (int scalefactor = 0; scalefactor < 16; scalefactor++) {
/* We have to reset the LMS state to the last known good one
before trying each scalefactor, as each pass updates the LMS
state when encoding. */
qoa_lms_t lms = qoa->lms[c];
qoa_uint64_t slice = scalefactor;
qoa_uint64_t current_error = 0;
for (int si = slice_start; si < slice_end; si += channels) {
int sample = sample_data[si];
int predicted = qoa_lms_predict(&lms);
int residual = sample - predicted;
int scaled = qoa_div(residual, scalefactor);
int clamped = qoa_clamp(scaled, -8, 8);
int quantized = qoa_quant_tab[clamped + 8];
int dequantized = qoa_dequant_tab[scalefactor][quantized];
int reconstructed = qoa_clamp(predicted + dequantized, -32768, 32767);
long long error = (sample - reconstructed);
current_error += error * error;
if (current_error > best_error) {
break;
}
qoa_lms_update(&lms, reconstructed, dequantized);
slice = (slice << 3) | quantized;
}
if (current_error < best_error) {
best_error = current_error;
best_slice = slice;
best_lms = lms;
}
}
qoa->lms[c] = best_lms;
#ifdef QOA_RECORD_TOTAL_ERROR
qoa->error += best_error;
#endif
/* If this slice was shorter than QOA_SLICE_LEN, we have to left-
shift all encoded data, to ensure the rightmost bits are the empty
ones. This should only happen in the last frame of a file as all
slices are completely filled otherwise. */
best_slice <<= (QOA_SLICE_LEN - slice_len) * 3;
qoa_write_u64(best_slice, bytes, &p);
}
}
return p;
}
void *qoa_encode(const short *sample_data, qoa_desc *qoa, unsigned int *out_len) {
if (
qoa->samples == 0 ||
qoa->samplerate == 0 || qoa->samplerate > 0xffffff ||
qoa->channels == 0 || qoa->channels > QOA_MAX_CHANNELS
) {
return NULL;
}
/* Calculate the encoded size and allocate */
unsigned int num_frames = (qoa->samples + QOA_FRAME_LEN-1) / QOA_FRAME_LEN;
unsigned int num_slices = (qoa->samples + QOA_SLICE_LEN-1) / QOA_SLICE_LEN;
unsigned int encoded_size = 8 + /* 8 byte file header */
num_frames * 8 + /* 8 byte frame headers */
num_frames * QOA_LMS_LEN * 4 * qoa->channels + /* 4 * 4 bytes lms state per channel */
num_slices * 8 * qoa->channels; /* 8 byte slices */
unsigned char *bytes = QOA_MALLOC(encoded_size);
for (int c = 0; c < qoa->channels; c++) {
/* Set the initial LMS weights to {0, 0, -1, 2}. This helps with the
prediction of the first few ms of a file. */
qoa->lms[c].weights[0] = 0;
qoa->lms[c].weights[1] = 0;
qoa->lms[c].weights[2] = -(1<<13);
qoa->lms[c].weights[3] = (1<<14);
/* Explicitly set the history samples to 0, as we might have some
garbage in there. */
for (int i = 0; i < QOA_LMS_LEN; i++) {
qoa->lms[c].history[i] = 0;
}
}
/* Encode the header and go through all frames */
unsigned int p = qoa_encode_header(qoa, bytes);
#ifdef QOA_RECORD_TOTAL_ERROR
qoa->error = 0;
#endif
int frame_len = QOA_FRAME_LEN;
for (int sample_index = 0; sample_index < qoa->samples; sample_index += frame_len) {
frame_len = qoa_clamp(QOA_FRAME_LEN, 0, qoa->samples - sample_index);
const short *frame_samples = sample_data + sample_index * qoa->channels;
unsigned int frame_size = qoa_encode_frame(frame_samples, qoa, frame_len, bytes + p);
p += frame_size;
}
*out_len = p;
return bytes;
}
/* -----------------------------------------------------------------------------
Decoder */
unsigned int qoa_max_frame_size(qoa_desc *qoa) {
return QOA_FRAME_SIZE(qoa->channels, QOA_SLICES_PER_FRAME);
}
unsigned int qoa_decode_header(const unsigned char *bytes, int size, qoa_desc *qoa) {
unsigned int p = 0;
if (size < QOA_MIN_FILESIZE) {
return 0;
}
/* Read the file header, verify the magic number ('qoaf') and read the
total number of samples. */
qoa_uint64_t file_header = qoa_read_u64(bytes, &p);
if ((file_header >> 32) != QOA_MAGIC) {
return 0;
}
qoa->samples = file_header & 0xffffffff;
if (!qoa->samples) {
return 0;
}
/* Peek into the first frame header to get the number of channels and
the samplerate. */
qoa_uint64_t frame_header = qoa_read_u64(bytes, &p);
qoa->channels = (frame_header >> 56) & 0x0000ff;
qoa->samplerate = (frame_header >> 32) & 0xffffff;
if (qoa->channels == 0 || qoa->samples == 0 || qoa->samplerate == 0) {
return 0;
}
return 8;
}
unsigned int qoa_decode_frame(const unsigned char *bytes, unsigned int size, qoa_desc *qoa, short *sample_data, unsigned int *frame_len) {
unsigned int p = 0;
*frame_len = 0;
if (size < 8 + QOA_LMS_LEN * 4 * qoa->channels) {
return 0;
}
/* Read and verify the frame header */
qoa_uint64_t frame_header = qoa_read_u64(bytes, &p);
int channels = (frame_header >> 56) & 0x0000ff;
int samplerate = (frame_header >> 32) & 0xffffff;
int samples = (frame_header >> 16) & 0x00ffff;
int frame_size = (frame_header ) & 0x00ffff;
int data_size = frame_size - 8 - QOA_LMS_LEN * 4 * channels;
int num_slices = data_size / 8;
int max_total_samples = num_slices * QOA_SLICE_LEN;
if (
channels != qoa->channels ||
samplerate != qoa->samplerate ||
frame_size > size ||
samples * channels > max_total_samples
) {
return 0;
}
/* Read the LMS state: 4 x 2 bytes history, 4 x 2 bytes weights per channel */
for (int c = 0; c < channels; c++) {
qoa_uint64_t history = qoa_read_u64(bytes, &p);
qoa_uint64_t weights = qoa_read_u64(bytes, &p);
for (int i = 0; i < QOA_LMS_LEN; i++) {
qoa->lms[c].history[i] = ((signed short)(history >> 48));
history <<= 16;
qoa->lms[c].weights[i] = ((signed short)(weights >> 48));
weights <<= 16;
}
}
/* Decode all slices for all channels in this frame */
for (int sample_index = 0; sample_index < samples; sample_index += QOA_SLICE_LEN) {
for (int c = 0; c < channels; c++) {
qoa_uint64_t slice = qoa_read_u64(bytes, &p);
int scalefactor = (slice >> 60) & 0xf;
int slice_start = sample_index * channels + c;
int slice_end = qoa_clamp(sample_index + QOA_SLICE_LEN, 0, samples) * channels + c;
for (int si = slice_start; si < slice_end; si += channels) {
int predicted = qoa_lms_predict(&qoa->lms[c]);
int quantized = (slice >> 57) & 0x7;
int dequantized = qoa_dequant_tab[scalefactor][quantized];
int reconstructed = qoa_clamp(predicted + dequantized, -32768, 32767);
sample_data[si] = reconstructed;
slice <<= 3;
qoa_lms_update(&qoa->lms[c], reconstructed, dequantized);
}
}
}
*frame_len = samples;
return p;
}
short *qoa_decode(const unsigned char *bytes, int size, qoa_desc *qoa) {
unsigned int p = qoa_decode_header(bytes, size, qoa);
if (!p) {
return NULL;
}
/* Calculate the required size of the sample buffer and allocate */
int total_samples = qoa->samples * qoa->channels;
short *sample_data = QOA_MALLOC(total_samples * sizeof(short));
unsigned int sample_index = 0;
unsigned int frame_len;
unsigned int frame_size;
/* Decode all frames */
do {
short *sample_ptr = sample_data + sample_index * qoa->channels;
frame_size = qoa_decode_frame(bytes + p, size - p, qoa, sample_ptr, &frame_len);
p += frame_size;
sample_index += frame_len;
} while (frame_size && sample_index < qoa->samples);
qoa->samples = sample_index;
return sample_data;
}
/* -----------------------------------------------------------------------------
File read/write convenience functions */
#ifndef QOA_NO_STDIO
#include <stdio.h>
int qoa_write(const char *filename, const short *sample_data, qoa_desc *qoa) {
FILE *f = fopen(filename, "wb");
unsigned int size;
void *encoded;
if (!f) {
return 0;
}
encoded = qoa_encode(sample_data, qoa, &size);
if (!encoded) {
fclose(f);
return 0;
}
fwrite(encoded, 1, size, f);
fclose(f);
QOA_FREE(encoded);
return size;
}
void *qoa_read(const char *filename, qoa_desc *qoa) {
FILE *f = fopen(filename, "rb");
int size, bytes_read;
void *data;
short *sample_data;
if (!f) {
return NULL;
}
fseek(f, 0, SEEK_END);
size = ftell(f);
if (size <= 0) {
fclose(f);
return NULL;
}
fseek(f, 0, SEEK_SET);
data = QOA_MALLOC(size);
if (!data) {
fclose(f);
return NULL;
}
bytes_read = fread(data, 1, size, f);
fclose(f);
sample_data = qoa_decode(data, bytes_read, qoa);
QOA_FREE(data);
return sample_data;
}
#endif /* QOA_NO_STDIO */
#endif /* QOA_IMPLEMENTATION */

278
raylib/external/qoaplay.c vendored Normal file
View file

@ -0,0 +1,278 @@
/*******************************************************************************************
*
* qoaplay - QOA stream playing helper functions
*
* qoaplay is a tiny abstraction to read and decode a QOA file "on the fly".
* It reads and decodes one frame at a time with minimal memory requirements.
* qoaplay also provides some functions to seek to a specific frame.
*
* LICENSE: MIT License
*
* Copyright (c) 2023 Dominic Szablewski (@phoboslab), reviewed by Ramon Santamaria (@raysan5)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
**********************************************************************************************/
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
// QOA streaming data descriptor
typedef struct {
qoa_desc info; // QOA descriptor data
FILE *file; // QOA file to read, if NULL, using memory buffer -> file_data
unsigned char *file_data; // QOA file data on memory
unsigned int file_data_size; // QOA file data on memory size
unsigned int file_data_offset; // QOA file data on memory offset for next read
unsigned int first_frame_pos; // First frame position (after QOA header, required for offset)
unsigned int sample_position; // Current streaming sample position
unsigned char *buffer; // Buffer used to read samples from file/memory (used on decoding)
unsigned int buffer_len; // Buffer length to read samples for streaming
short *sample_data; // Sample data decoded
unsigned int sample_data_len; // Sample data decoded length
unsigned int sample_data_pos; // Sample data decoded position
} qoaplay_desc;
//----------------------------------------------------------------------------------
// Module Functions Declaration
//----------------------------------------------------------------------------------
#if defined(__cplusplus)
extern "C" { // Prevents name mangling of functions
#endif
qoaplay_desc *qoaplay_open(char *path);
qoaplay_desc *qoaplay_open_memory(const unsigned char *data, int data_size);
void qoaplay_close(qoaplay_desc *qoa_ctx);
void qoaplay_rewind(qoaplay_desc *qoa_ctx);
void qoaplay_seek_frame(qoaplay_desc *qoa_ctx, int frame);
unsigned int qoaplay_decode(qoaplay_desc *qoa_ctx, float *sample_data, int num_samples);
unsigned int qoaplay_decode_frame(qoaplay_desc *qoa_ctx);
double qoaplay_get_duration(qoaplay_desc *qoa_ctx);
double qoaplay_get_time(qoaplay_desc *qoa_ctx);
int qoaplay_get_frame(qoaplay_desc *qoa_ctx);
#if defined(__cplusplus)
} // Prevents name mangling of functions
#endif
//----------------------------------------------------------------------------------
// Module Functions Definition
//----------------------------------------------------------------------------------
// Open QOA file, keep FILE pointer to keep reading from file
qoaplay_desc *qoaplay_open(char *path)
{
FILE *file = fopen(path, "rb");
if (!file) return NULL;
// Read and decode the file header
unsigned char header[QOA_MIN_FILESIZE];
int read = fread(header, QOA_MIN_FILESIZE, 1, file);
if (!read) return NULL;
qoa_desc qoa;
unsigned int first_frame_pos = qoa_decode_header(header, QOA_MIN_FILESIZE, &qoa);
if (!first_frame_pos) return NULL;
// Rewind the file back to beginning of the first frame
fseek(file, first_frame_pos, SEEK_SET);
// Allocate one chunk of memory for the qoaplay_desc struct
// + the sample data for one frame
// + a buffer to hold one frame of encoded data
unsigned int buffer_size = qoa_max_frame_size(&qoa);
unsigned int sample_data_size = qoa.channels*QOA_FRAME_LEN*sizeof(short)*2;
qoaplay_desc *qoa_ctx = QOA_MALLOC(sizeof(qoaplay_desc) + buffer_size + sample_data_size);
memset(qoa_ctx, 0, sizeof(qoaplay_desc));
qoa_ctx->file = file;
qoa_ctx->file_data = NULL;
qoa_ctx->file_data_size = 0;
qoa_ctx->file_data_offset = 0;
qoa_ctx->first_frame_pos = first_frame_pos;
// Setup data pointers to previously allocated data
qoa_ctx->buffer = ((unsigned char *)qoa_ctx) + sizeof(qoaplay_desc);
qoa_ctx->sample_data = (short *)(((unsigned char *)qoa_ctx) + sizeof(qoaplay_desc) + buffer_size);
qoa_ctx->info.channels = qoa.channels;
qoa_ctx->info.samplerate = qoa.samplerate;
qoa_ctx->info.samples = qoa.samples;
return qoa_ctx;
}
// Open QOA file from memory, no FILE pointer required
qoaplay_desc *qoaplay_open_memory(const unsigned char *data, int data_size)
{
// Read and decode the file header
unsigned char header[QOA_MIN_FILESIZE];
memcpy(header, data, QOA_MIN_FILESIZE);
qoa_desc qoa;
unsigned int first_frame_pos = qoa_decode_header(header, QOA_MIN_FILESIZE, &qoa);
if (!first_frame_pos) return NULL;
// Allocate one chunk of memory for the qoaplay_desc struct
// + the sample data for one frame
// + a buffer to hold one frame of encoded data
unsigned int buffer_size = qoa_max_frame_size(&qoa);
unsigned int sample_data_size = qoa.channels*QOA_FRAME_LEN*sizeof(short)*2;
qoaplay_desc *qoa_ctx = QOA_MALLOC(sizeof(qoaplay_desc) + buffer_size + sample_data_size);
memset(qoa_ctx, 0, sizeof(qoaplay_desc));
qoa_ctx->file = NULL;
// Keep a copy of file data provided to be managed internally
qoa_ctx->file_data = (unsigned char *)QOA_MALLOC(data_size);
memcpy(qoa_ctx->file_data, data, data_size);
qoa_ctx->file_data_size = data_size;
qoa_ctx->file_data_offset = 0;
qoa_ctx->first_frame_pos = first_frame_pos;
// Setup data pointers to previously allocated data
qoa_ctx->buffer = ((unsigned char *)qoa_ctx) + sizeof(qoaplay_desc);
qoa_ctx->sample_data = (short *)(((unsigned char *)qoa_ctx) + sizeof(qoaplay_desc) + buffer_size);
qoa_ctx->info.channels = qoa.channels;
qoa_ctx->info.samplerate = qoa.samplerate;
qoa_ctx->info.samples = qoa.samples;
return qoa_ctx;
}
// Close QOA file (if open) and free internal memory
void qoaplay_close(qoaplay_desc *qoa_ctx)
{
if (qoa_ctx->file) fclose(qoa_ctx->file);
if ((qoa_ctx->file_data) && (qoa_ctx->file_data_size > 0))
{
QOA_FREE(qoa_ctx->file_data);
qoa_ctx->file_data_size = 0;
}
QOA_FREE(qoa_ctx);
}
// Decode one frame from QOA data
unsigned int qoaplay_decode_frame(qoaplay_desc *qoa_ctx)
{
if (qoa_ctx->file) qoa_ctx->buffer_len = fread(qoa_ctx->buffer, 1, qoa_max_frame_size(&qoa_ctx->info), qoa_ctx->file);
else
{
qoa_ctx->buffer_len = qoa_max_frame_size(&qoa_ctx->info);
memcpy(qoa_ctx->buffer, qoa_ctx->file_data + qoa_ctx->file_data_offset, qoa_ctx->buffer_len);
qoa_ctx->file_data_offset += qoa_ctx->buffer_len;
}
unsigned int frame_len;
qoa_decode_frame(qoa_ctx->buffer, qoa_ctx->buffer_len, &qoa_ctx->info, qoa_ctx->sample_data, &frame_len);
qoa_ctx->sample_data_pos = 0;
qoa_ctx->sample_data_len = frame_len;
return frame_len;
}
// Rewind QOA file or memory pointer to beginning
void qoaplay_rewind(qoaplay_desc *qoa_ctx)
{
if (qoa_ctx->file) fseek(qoa_ctx->file, qoa_ctx->first_frame_pos, SEEK_SET);
else qoa_ctx->file_data_offset = 0;
qoa_ctx->sample_position = 0;
qoa_ctx->sample_data_len = 0;
qoa_ctx->sample_data_pos = 0;
}
// Decode required QOA frames
unsigned int qoaplay_decode(qoaplay_desc *qoa_ctx, float *sample_data, int num_samples)
{
int src_index = qoa_ctx->sample_data_pos*qoa_ctx->info.channels;
int dst_index = 0;
for (int i = 0; i < num_samples; i++)
{
// Do we have to decode more samples?
if (qoa_ctx->sample_data_len - qoa_ctx->sample_data_pos == 0)
{
if (!qoaplay_decode_frame(qoa_ctx))
{
// Loop to the beginning
qoaplay_rewind(qoa_ctx);
qoaplay_decode_frame(qoa_ctx);
}
src_index = 0;
}
// Normalize to -1..1 floats and write to dest
for (int c = 0; c < qoa_ctx->info.channels; c++)
{
sample_data[dst_index++] = qoa_ctx->sample_data[src_index++]/32768.0;
}
qoa_ctx->sample_data_pos++;
qoa_ctx->sample_position++;
}
return num_samples;
}
// Get QOA total time duration in seconds
double qoaplay_get_duration(qoaplay_desc *qoa_ctx)
{
return (double)qoa_ctx->info.samples/(double)qoa_ctx->info.samplerate;
}
// Get QOA current time position in seconds
double qoaplay_get_time(qoaplay_desc *qoa_ctx)
{
return (double)qoa_ctx->sample_position/(double)qoa_ctx->info.samplerate;
}
// Get QOA current audio frame
int qoaplay_get_frame(qoaplay_desc *qoa_ctx)
{
return qoa_ctx->sample_position/QOA_FRAME_LEN;
}
// Seek QOA audio frame
void qoaplay_seek_frame(qoaplay_desc *qoa_ctx, int frame)
{
if (frame < 0) frame = 0;
if (frame > qoa_ctx->info.samples/QOA_FRAME_LEN) frame = qoa_ctx->info.samples/QOA_FRAME_LEN;
qoa_ctx->sample_position = frame*QOA_FRAME_LEN;
qoa_ctx->sample_data_len = 0;
qoa_ctx->sample_data_pos = 0;
unsigned int offset = qoa_ctx->first_frame_pos + frame*qoa_max_frame_size(&qoa_ctx->info);
if (qoa_ctx->file) fseek(qoa_ctx->file, offset, SEEK_SET);
else qoa_ctx->file_data_offset = offset;
}

50
raylib/external/qoi.h vendored
View file

@ -1,31 +1,11 @@
/*
Copyright (c) 2021, Dominic Szablewski - https://phoboslab.org
SPDX-License-Identifier: MIT
QOI - The "Quite OK Image" format for fast, lossless image compression
Dominic Szablewski - https://phoboslab.org
-- LICENSE: The MIT License(MIT)
Copyright(c) 2021 Dominic Szablewski
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files(the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and / or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-- About
QOI encodes and decodes images in a lossless format. Compared to stb_image and
@ -424,13 +404,12 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) {
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 = *(qoi_rgba_t *)(pixels + px_pos);
}
else {
px.rgba.r = pixels[px_pos + 0];
px.rgba.g = pixels[px_pos + 1];
px.rgba.b = pixels[px_pos + 2];
px.rgba.a = pixels[px_pos + 3];
}
if (px.v == px_prev.v) {
@ -598,13 +577,12 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) {
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) {
*(qoi_rgba_t*)(pixels + px_pos) = px;
}
else {
pixels[px_pos + 0] = px.rgba.r;
pixels[px_pos + 1] = px.rgba.g;
pixels[px_pos + 2] = px.rgba.b;
pixels[px_pos + 3] = px.rgba.a;
}
}

View file

@ -477,10 +477,10 @@ int rl_save_ktx(const char *file_name, void *data, int width, int height, int fo
// Calculate file data_size required
int data_size = sizeof(ktx_header);
for (int i = 0, width = width, height = height; i < mipmaps; i++)
for (int i = 0, w = width, h = height; i < mipmaps; i++)
{
data_size += get_pixel_data_size(width, height, format);
width /= 2; height /= 2;
data_size += get_pixel_data_size(w, h, format);
w /= 2; h /= 2;
}
unsigned char *file_data = RL_CALLOC(data_size, 1);

View file

@ -1,4 +1,4 @@
/* stb_image - v2.27 - public domain image loader - http://nothings.org/stb
/* stb_image - v2.28 - public domain image loader - http://nothings.org/stb
no warranty implied; use at your own risk
Do this:
@ -48,6 +48,7 @@ LICENSE
RECENT REVISION HISTORY:
2.28 (2023-01-29) many error fixes, security errors, just tons of stuff
2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes
2.26 (2020-07-13) many minor fixes
2.25 (2020-02-02) fix warnings
@ -108,7 +109,7 @@ RECENT REVISION HISTORY:
Cass Everitt Ryamond Barbiero github:grim210
Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw
Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus
Josh Tobin Matthew Gregan github:poppolopoppo
Josh Tobin Neil Bickford Matthew Gregan github:poppolopoppo
Julian Raschke Gregory Mullen Christian Floisand github:darealshinji
Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007
Brad Weinberger Matvey Cherevko github:mosra
@ -140,7 +141,7 @@ RECENT REVISION HISTORY:
// // ... x = width, y = height, n = # 8-bit components per pixel ...
// // ... replace '0' with '1'..'4' to force that many components per pixel
// // ... but 'n' will always be the number that it would have been if you said 0
// stbi_image_free(data)
// stbi_image_free(data);
//
// Standard parameters:
// int *x -- outputs image width in pixels
@ -635,7 +636,7 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const ch
#endif
#endif
#ifdef _MSC_VER
#if defined(_MSC_VER) || defined(__SYMBIAN32__)
typedef unsigned short stbi__uint16;
typedef signed short stbi__int16;
typedef unsigned int stbi__uint32;
@ -1063,6 +1064,23 @@ static void *stbi__malloc_mad4(int a, int b, int c, int d, int add)
}
#endif
// returns 1 if the sum of two signed ints is valid (between -2^31 and 2^31-1 inclusive), 0 on overflow.
static int stbi__addints_valid(int a, int b)
{
if ((a >= 0) != (b >= 0)) return 1; // a and b have different signs, so no overflow
if (a < 0 && b < 0) return a >= INT_MIN - b; // same as a + b >= INT_MIN; INT_MIN - b cannot overflow since b < 0.
return a <= INT_MAX - b;
}
// returns 1 if the product of two signed shorts is valid, 0 on overflow.
static int stbi__mul2shorts_valid(short a, short b)
{
if (b == 0 || b == -1) return 1; // multiplication by 0 is always 0; check for -1 so SHRT_MIN/b doesn't overflow
if ((a >= 0) == (b >= 0)) return a <= SHRT_MAX/b; // product is positive, so similar to mul2sizes_valid
if (b < 0) return a <= SHRT_MIN / b; // same as a * b >= SHRT_MIN
return a >= SHRT_MIN / b;
}
// stbi__err - error
// stbi__errpf - error returning pointer to float
// stbi__errpuc - error returning pointer to unsigned char
@ -1985,9 +2003,12 @@ static int stbi__build_huffman(stbi__huffman *h, int *count)
int i,j,k=0;
unsigned int code;
// build size list for each symbol (from JPEG spec)
for (i=0; i < 16; ++i)
for (j=0; j < count[i]; ++j)
for (i=0; i < 16; ++i) {
for (j=0; j < count[i]; ++j) {
h->size[k++] = (stbi_uc) (i+1);
if(k >= 257) return stbi__err("bad size list","Corrupt JPEG");
}
}
h->size[k] = 0;
// compute actual symbols (from jpeg spec)
@ -2112,6 +2133,8 @@ stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h)
// convert the huffman code to the symbol id
c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k];
if(c < 0 || c >= 256) // symbol id out of bounds!
return -1;
STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]);
// convert the id to a symbol
@ -2130,6 +2153,7 @@ stbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n)
unsigned int k;
int sgn;
if (j->code_bits < n) stbi__grow_buffer_unsafe(j);
if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing
sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative)
k = stbi_lrot(j->code_buffer, n);
@ -2144,6 +2168,7 @@ stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n)
{
unsigned int k;
if (j->code_bits < n) stbi__grow_buffer_unsafe(j);
if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing
k = stbi_lrot(j->code_buffer, n);
j->code_buffer = k & ~stbi__bmask[n];
k &= stbi__bmask[n];
@ -2155,6 +2180,7 @@ stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j)
{
unsigned int k;
if (j->code_bits < 1) stbi__grow_buffer_unsafe(j);
if (j->code_bits < 1) return 0; // ran out of bits from stream, return 0s intead of continuing
k = j->code_buffer;
j->code_buffer <<= 1;
--j->code_bits;
@ -2192,8 +2218,10 @@ static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman
memset(data,0,64*sizeof(data[0]));
diff = t ? stbi__extend_receive(j, t) : 0;
if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta","Corrupt JPEG");
dc = j->img_comp[b].dc_pred + diff;
j->img_comp[b].dc_pred = dc;
if (!stbi__mul2shorts_valid(dc, dequant[0])) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
data[0] = (short) (dc * dequant[0]);
// decode AC components, see JPEG spec
@ -2207,6 +2235,7 @@ static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman
if (r) { // fast-AC path
k += (r >> 4) & 15; // run
s = r & 15; // combined length
if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available");
j->code_buffer <<= s;
j->code_bits -= s;
// decode into unzigzag'd location
@ -2246,8 +2275,10 @@ static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__
if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
diff = t ? stbi__extend_receive(j, t) : 0;
if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta", "Corrupt JPEG");
dc = j->img_comp[b].dc_pred + diff;
j->img_comp[b].dc_pred = dc;
if (!stbi__mul2shorts_valid(dc, 1 << j->succ_low)) return stbi__err("can't merge dc and ac", "Corrupt JPEG");
data[0] = (short) (dc * (1 << j->succ_low));
} else {
// refinement scan for DC coefficient
@ -2282,6 +2313,7 @@ static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__
if (r) { // fast-AC path
k += (r >> 4) & 15; // run
s = r & 15; // combined length
if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available");
j->code_buffer <<= s;
j->code_bits -= s;
zig = stbi__jpeg_dezigzag[k++];
@ -3102,6 +3134,7 @@ static int stbi__process_marker(stbi__jpeg *z, int m)
sizes[i] = stbi__get8(z->s);
n += sizes[i];
}
if(n > 256) return stbi__err("bad DHT header","Corrupt JPEG"); // Loop over i < n would write past end of values!
L -= 17;
if (tc == 0) {
if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0;
@ -3351,6 +3384,28 @@ static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan)
return 1;
}
static int stbi__skip_jpeg_junk_at_end(stbi__jpeg *j)
{
// some JPEGs have junk at end, skip over it but if we find what looks
// like a valid marker, resume there
while (!stbi__at_eof(j->s)) {
int x = stbi__get8(j->s);
while (x == 255) { // might be a marker
if (stbi__at_eof(j->s)) return STBI__MARKER_none;
x = stbi__get8(j->s);
if (x != 0x00 && x != 0xff) {
// not a stuffed zero or lead-in to another marker, looks
// like an actual marker, return it
return x;
}
// stuffed zero has x=0 now which ends the loop, meaning we go
// back to regular scan loop.
// repeated 0xff keeps trying to read the next byte of the marker.
}
}
return STBI__MARKER_none;
}
// decode image to YCbCr format
static int stbi__decode_jpeg_image(stbi__jpeg *j)
{
@ -3367,25 +3422,22 @@ static int stbi__decode_jpeg_image(stbi__jpeg *j)
if (!stbi__process_scan_header(j)) return 0;
if (!stbi__parse_entropy_coded_data(j)) return 0;
if (j->marker == STBI__MARKER_none ) {
// handle 0s at the end of image data from IP Kamera 9060
while (!stbi__at_eof(j->s)) {
int x = stbi__get8(j->s);
if (x == 255) {
j->marker = stbi__get8(j->s);
break;
}
}
j->marker = stbi__skip_jpeg_junk_at_end(j);
// if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0
}
m = stbi__get_marker(j);
if (STBI__RESTART(m))
m = stbi__get_marker(j);
} else if (stbi__DNL(m)) {
int Ld = stbi__get16be(j->s);
stbi__uint32 NL = stbi__get16be(j->s);
if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG");
if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG");
m = stbi__get_marker(j);
} else {
if (!stbi__process_marker(j, m)) return 0;
if (!stbi__process_marker(j, m)) return 1;
m = stbi__get_marker(j);
}
m = stbi__get_marker(j);
}
if (j->progressive)
stbi__jpeg_finish(j);
@ -3976,6 +4028,7 @@ static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int re
unsigned char* result;
stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg));
if (!j) return stbi__errpuc("outofmem", "Out of memory");
memset(j, 0, sizeof(stbi__jpeg));
STBI_NOTUSED(ri);
j->s = s;
stbi__setup_jpeg(j);
@ -3989,6 +4042,7 @@ static int stbi__jpeg_test(stbi__context *s)
int r;
stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg));
if (!j) return stbi__err("outofmem", "Out of memory");
memset(j, 0, sizeof(stbi__jpeg));
j->s = s;
stbi__setup_jpeg(j);
r = stbi__decode_jpeg_header(j, STBI__SCAN_type);
@ -4014,6 +4068,7 @@ static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp)
int result;
stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg)));
if (!j) return stbi__err("outofmem", "Out of memory");
memset(j, 0, sizeof(stbi__jpeg));
j->s = s;
result = stbi__jpeg_info_raw(j, x, y, comp);
STBI_FREE(j);
@ -4256,11 +4311,12 @@ static int stbi__parse_huffman_block(stbi__zbuf *a)
a->zout = zout;
return 1;
}
if (z >= 286) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, length codes 286 and 287 must not appear in compressed data
z -= 257;
len = stbi__zlength_base[z];
if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]);
z = stbi__zhuffman_decode(a, &a->z_distance);
if (z < 0) return stbi__err("bad huffman code","Corrupt PNG");
if (z < 0 || z >= 30) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, distance codes 30 and 31 must not appear in compressed data
dist = stbi__zdist_base[z];
if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]);
if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG");
@ -4955,7 +5011,7 @@ STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert)
static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set;
static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set;
STBIDEF void stbi__unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply)
STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply)
{
stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply;
stbi__unpremultiply_on_load_set = 1;
@ -5064,14 +5120,13 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
if (!pal_img_n) {
s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0);
if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode");
if (scan == STBI__SCAN_header) return 1;
} else {
// if paletted, then pal_n is our final components, and
// img_n is # components to decompress/filter.
s->img_n = 1;
if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG");
// if SCAN_header, have to scan to see if we have a tRNS
}
// even with SCAN_header, have to scan to see if we have a tRNS
break;
}
@ -5103,6 +5158,8 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG");
if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG");
has_trans = 1;
// non-paletted with tRNS = constant alpha. if header-scanning, we can stop now.
if (scan == STBI__SCAN_header) { ++s->img_n; return 1; }
if (z->depth == 16) {
for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is
} else {
@ -5115,7 +5172,13 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
case STBI__PNG_TYPE('I','D','A','T'): {
if (first) return stbi__err("first not IHDR", "Corrupt PNG");
if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG");
if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; }
if (scan == STBI__SCAN_header) {
// header scan definitely stops at first IDAT
if (pal_img_n)
s->img_n = pal_img_n;
return 1;
}
if (c.length > (1u << 30)) return stbi__err("IDAT size limit", "IDAT section larger than 2^30 bytes");
if ((int)(ioff + c.length) < (int)ioff) return 0;
if (ioff + c.length > idata_limit) {
stbi__uint32 idata_limit_old = idata_limit;
@ -5498,8 +5561,22 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req
psize = (info.offset - info.extra_read - info.hsz) >> 2;
}
if (psize == 0) {
if (info.offset != s->callback_already_read + (s->img_buffer - s->img_buffer_original)) {
return stbi__errpuc("bad offset", "Corrupt BMP");
// accept some number of extra bytes after the header, but if the offset points either to before
// the header ends or implies a large amount of extra data, reject the file as malformed
int bytes_read_so_far = s->callback_already_read + (int)(s->img_buffer - s->img_buffer_original);
int header_limit = 1024; // max we actually read is below 256 bytes currently.
int extra_data_limit = 256*4; // what ordinarily goes here is a palette; 256 entries*4 bytes is its max size.
if (bytes_read_so_far <= 0 || bytes_read_so_far > header_limit) {
return stbi__errpuc("bad header", "Corrupt BMP");
}
// we established that bytes_read_so_far is positive and sensible.
// the first half of this test rejects offsets that are either too small positives, or
// negative, and guarantees that info.offset >= bytes_read_so_far > 0. this in turn
// ensures the number computed in the second half of the test can't overflow.
if (info.offset < bytes_read_so_far || info.offset - bytes_read_so_far > extra_data_limit) {
return stbi__errpuc("bad offset", "Corrupt BMP");
} else {
stbi__skip(s, info.offset - bytes_read_so_far);
}
}
@ -7187,12 +7264,12 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re
// Run
value = stbi__get8(s);
count -= 128;
if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); }
if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); }
for (z = 0; z < count; ++z)
scanline[i++ * 4 + k] = value;
} else {
// Dump
if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); }
if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); }
for (z = 0; z < count; ++z)
scanline[i++ * 4 + k] = stbi__get8(s);
}
@ -7446,10 +7523,17 @@ static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req
out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0);
if (!out) return stbi__errpuc("outofmem", "Out of memory");
stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8));
if (!stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8))) {
STBI_FREE(out);
return stbi__errpuc("bad PNM", "PNM file truncated");
}
if (req_comp && req_comp != s->img_n) {
out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y);
if (ri->bits_per_channel == 16) {
out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, s->img_n, req_comp, s->img_x, s->img_y);
} else {
out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y);
}
if (out == NULL) return out; // stbi__convert_format frees input on failure
}
return out;
@ -7486,6 +7570,8 @@ static int stbi__pnm_getinteger(stbi__context *s, char *c)
while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) {
value = value*10 + (*c - '0');
*c = (char) stbi__get8(s);
if((value > 214748364) || (value == 214748364 && *c > '7'))
return stbi__err("integer parse overflow", "Parsing an integer in the PPM header overflowed a 32-bit int");
}
return value;
@ -7516,9 +7602,13 @@ static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp)
stbi__pnm_skip_whitespace(s, &c);
*x = stbi__pnm_getinteger(s, &c); // read width
if(*x == 0)
return stbi__err("invalid width", "PPM image header had zero or overflowing width");
stbi__pnm_skip_whitespace(s, &c);
*y = stbi__pnm_getinteger(s, &c); // read height
if (*y == 0)
return stbi__err("invalid width", "PPM image header had zero or overflowing width");
stbi__pnm_skip_whitespace(s, &c);
maxv = stbi__pnm_getinteger(s, &c); // read max value

File diff suppressed because it is too large Load diff