external c-sources updated
This commit is contained in:
parent
2ad5a4babf
commit
a0882a5b81
13 changed files with 7674 additions and 4241 deletions
9160
raylib/external/RGFW.h
vendored
9160
raylib/external/RGFW.h
vendored
File diff suppressed because it is too large
Load diff
48
raylib/external/cgltf.h
vendored
48
raylib/external/cgltf.h
vendored
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* cgltf - a single-file glTF 2.0 parser written in C99.
|
* cgltf - a single-file glTF 2.0 parser written in C99.
|
||||||
*
|
*
|
||||||
* Version: 1.13
|
* Version: 1.14
|
||||||
*
|
*
|
||||||
* Website: https://github.com/jkuhlmann/cgltf
|
* Website: https://github.com/jkuhlmann/cgltf
|
||||||
*
|
*
|
||||||
|
@ -395,6 +395,8 @@ typedef struct cgltf_texture
|
||||||
cgltf_sampler* sampler;
|
cgltf_sampler* sampler;
|
||||||
cgltf_bool has_basisu;
|
cgltf_bool has_basisu;
|
||||||
cgltf_image* basisu_image;
|
cgltf_image* basisu_image;
|
||||||
|
cgltf_bool has_webp;
|
||||||
|
cgltf_image* webp_image;
|
||||||
cgltf_extras extras;
|
cgltf_extras extras;
|
||||||
cgltf_size extensions_count;
|
cgltf_size extensions_count;
|
||||||
cgltf_extension* extensions;
|
cgltf_extension* extensions;
|
||||||
|
@ -1697,7 +1699,20 @@ cgltf_result cgltf_validate(cgltf_data* data)
|
||||||
{
|
{
|
||||||
if (data->nodes[i].weights && data->nodes[i].mesh)
|
if (data->nodes[i].weights && data->nodes[i].mesh)
|
||||||
{
|
{
|
||||||
CGLTF_ASSERT_IF (data->nodes[i].mesh->primitives_count && data->nodes[i].mesh->primitives[0].targets_count != data->nodes[i].weights_count, cgltf_result_invalid_gltf);
|
CGLTF_ASSERT_IF(data->nodes[i].mesh->primitives_count && data->nodes[i].mesh->primitives[0].targets_count != data->nodes[i].weights_count, cgltf_result_invalid_gltf);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data->nodes[i].has_mesh_gpu_instancing)
|
||||||
|
{
|
||||||
|
CGLTF_ASSERT_IF(data->nodes[i].mesh == NULL, cgltf_result_invalid_gltf);
|
||||||
|
CGLTF_ASSERT_IF(data->nodes[i].mesh_gpu_instancing.attributes_count == 0, cgltf_result_invalid_gltf);
|
||||||
|
|
||||||
|
cgltf_accessor* first = data->nodes[i].mesh_gpu_instancing.attributes[0].data;
|
||||||
|
|
||||||
|
for (cgltf_size k = 0; k < data->nodes[i].mesh_gpu_instancing.attributes_count; ++k)
|
||||||
|
{
|
||||||
|
CGLTF_ASSERT_IF(data->nodes[i].mesh_gpu_instancing.attributes[k].data->count != first->count, cgltf_result_invalid_gltf);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4538,6 +4553,34 @@ static int cgltf_parse_json_texture(cgltf_options* options, jsmntok_t const* tok
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (cgltf_json_strcmp(tokens + i, json_chunk, "EXT_texture_webp") == 0)
|
||||||
|
{
|
||||||
|
out_texture->has_webp = 1;
|
||||||
|
++i;
|
||||||
|
CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
|
||||||
|
int num_properties = tokens[i].size;
|
||||||
|
++i;
|
||||||
|
|
||||||
|
for (int t = 0; t < num_properties; ++t)
|
||||||
|
{
|
||||||
|
CGLTF_CHECK_KEY(tokens[i]);
|
||||||
|
|
||||||
|
if (cgltf_json_strcmp(tokens + i, json_chunk, "source") == 0)
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
out_texture->webp_image = CGLTF_PTRINDEX(cgltf_image, cgltf_json_to_int(tokens + i, json_chunk));
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
i = cgltf_skip_json(tokens, i + 1);
|
||||||
|
}
|
||||||
|
if (i < 0)
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_texture->extensions[out_texture->extensions_count++]));
|
i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_texture->extensions[out_texture->extensions_count++]));
|
||||||
|
@ -6548,6 +6591,7 @@ static int cgltf_fixup_pointers(cgltf_data* data)
|
||||||
{
|
{
|
||||||
CGLTF_PTRFIXUP(data->textures[i].image, data->images, data->images_count);
|
CGLTF_PTRFIXUP(data->textures[i].image, data->images, data->images_count);
|
||||||
CGLTF_PTRFIXUP(data->textures[i].basisu_image, data->images, data->images_count);
|
CGLTF_PTRFIXUP(data->textures[i].basisu_image, data->images, data->images_count);
|
||||||
|
CGLTF_PTRFIXUP(data->textures[i].webp_image, data->images, data->images_count);
|
||||||
CGLTF_PTRFIXUP(data->textures[i].sampler, data->samplers, data->samplers_count);
|
CGLTF_PTRFIXUP(data->textures[i].sampler, data->samplers, data->samplers_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
15
raylib/external/dr_mp3.h
vendored
15
raylib/external/dr_mp3.h
vendored
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
MP3 audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file.
|
MP3 audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file.
|
||||||
dr_mp3 - v0.6.38 - 2023-11-02
|
dr_mp3 - v0.6.39 - 2024-02-27
|
||||||
|
|
||||||
David Reid - mackron@gmail.com
|
David Reid - mackron@gmail.com
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ extern "C" {
|
||||||
|
|
||||||
#define DRMP3_VERSION_MAJOR 0
|
#define DRMP3_VERSION_MAJOR 0
|
||||||
#define DRMP3_VERSION_MINOR 6
|
#define DRMP3_VERSION_MINOR 6
|
||||||
#define DRMP3_VERSION_REVISION 38
|
#define DRMP3_VERSION_REVISION 39
|
||||||
#define DRMP3_VERSION_STRING DRMP3_XSTRINGIFY(DRMP3_VERSION_MAJOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_MINOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_REVISION)
|
#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. */
|
#include <stddef.h> /* For size_t. */
|
||||||
|
@ -1985,8 +1985,8 @@ static drmp3_int16 drmp3d_scale_pcm(float sample)
|
||||||
s32 -= (s32 < 0);
|
s32 -= (s32 < 0);
|
||||||
s = (drmp3_int16)drmp3_clip_int16_arm(s32);
|
s = (drmp3_int16)drmp3_clip_int16_arm(s32);
|
||||||
#else
|
#else
|
||||||
if (sample >= 32766.5) return (drmp3_int16) 32767;
|
if (sample >= 32766.5f) return (drmp3_int16) 32767;
|
||||||
if (sample <= -32767.5) return (drmp3_int16)-32768;
|
if (sample <= -32767.5f) return (drmp3_int16)-32768;
|
||||||
s = (drmp3_int16)(sample + .5f);
|
s = (drmp3_int16)(sample + .5f);
|
||||||
s -= (s < 0); /* away from zero, to be compliant */
|
s -= (s < 0); /* away from zero, to be compliant */
|
||||||
#endif
|
#endif
|
||||||
|
@ -2404,9 +2404,9 @@ DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num
|
||||||
for(; i < num_samples; i++)
|
for(; i < num_samples; i++)
|
||||||
{
|
{
|
||||||
float sample = in[i] * 32768.0f;
|
float sample = in[i] * 32768.0f;
|
||||||
if (sample >= 32766.5)
|
if (sample >= 32766.5f)
|
||||||
out[i] = (drmp3_int16) 32767;
|
out[i] = (drmp3_int16) 32767;
|
||||||
else if (sample <= -32767.5)
|
else if (sample <= -32767.5f)
|
||||||
out[i] = (drmp3_int16)-32768;
|
out[i] = (drmp3_int16)-32768;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -4495,6 +4495,9 @@ counts rather than sample counts.
|
||||||
/*
|
/*
|
||||||
REVISION HISTORY
|
REVISION HISTORY
|
||||||
================
|
================
|
||||||
|
v0.6.39 - 2024-02-27
|
||||||
|
- Fix a Wdouble-promotion warning.
|
||||||
|
|
||||||
v0.6.38 - 2023-11-02
|
v0.6.38 - 2023-11-02
|
||||||
- Fix build for ARMv6-M.
|
- Fix build for ARMv6-M.
|
||||||
|
|
||||||
|
|
28
raylib/external/dr_wav.h
vendored
28
raylib/external/dr_wav.h
vendored
|
@ -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.
|
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.13 - 2023-11-02
|
dr_wav - v0.13.16 - 2024-02-27
|
||||||
|
|
||||||
David Reid - mackron@gmail.com
|
David Reid - mackron@gmail.com
|
||||||
|
|
||||||
|
@ -147,7 +147,7 @@ extern "C" {
|
||||||
|
|
||||||
#define DRWAV_VERSION_MAJOR 0
|
#define DRWAV_VERSION_MAJOR 0
|
||||||
#define DRWAV_VERSION_MINOR 13
|
#define DRWAV_VERSION_MINOR 13
|
||||||
#define DRWAV_VERSION_REVISION 13
|
#define DRWAV_VERSION_REVISION 16
|
||||||
#define DRWAV_VERSION_STRING DRWAV_XSTRINGIFY(DRWAV_VERSION_MAJOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_MINOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_REVISION)
|
#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. */
|
#include <stddef.h> /* For size_t. */
|
||||||
|
@ -3075,7 +3075,13 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on
|
||||||
|
|
||||||
if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rifx) {
|
if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rifx) {
|
||||||
if (drwav_bytes_to_u32_ex(chunkSizeBytes, pWav->container) < 36) {
|
if (drwav_bytes_to_u32_ex(chunkSizeBytes, pWav->container) < 36) {
|
||||||
return DRWAV_FALSE; /* Chunk size should always be at least 36 bytes. */
|
/*
|
||||||
|
I've had a report of a WAV file failing to load when the size of the WAVE chunk is not encoded
|
||||||
|
and is instead just set to 0. I'm going to relax the validation here to allow these files to
|
||||||
|
load. Considering the chunk size isn't actually used this should be safe. With this change my
|
||||||
|
test suite still passes.
|
||||||
|
*/
|
||||||
|
/*return DRWAV_FALSE;*/ /* Chunk size should always be at least 36 bytes. */
|
||||||
}
|
}
|
||||||
} else if (pWav->container == drwav_container_rf64) {
|
} else if (pWav->container == drwav_container_rf64) {
|
||||||
if (drwav_bytes_to_u32_le(chunkSizeBytes) != 0xFFFFFFFF) {
|
if (drwav_bytes_to_u32_le(chunkSizeBytes) != 0xFFFFFFFF) {
|
||||||
|
@ -3554,10 +3560,7 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on
|
||||||
|
|
||||||
/* Getting here means it's not a chunk that we care about internally, but might need to be handled as metadata by the caller. */
|
/* Getting here means it's not a chunk that we care about internally, but might need to be handled as metadata by the caller. */
|
||||||
if (isProcessingMetadata) {
|
if (isProcessingMetadata) {
|
||||||
drwav_uint64 metadataBytesRead;
|
drwav__metadata_process_chunk(&metadataParser, &header, drwav_metadata_type_all_including_unknown);
|
||||||
|
|
||||||
metadataBytesRead = drwav__metadata_process_chunk(&metadataParser, &header, drwav_metadata_type_all_including_unknown);
|
|
||||||
DRWAV_ASSERT(metadataBytesRead <= header.sizeInBytes);
|
|
||||||
|
|
||||||
/* Go back to the start of the chunk so we can normalize the position of the cursor. */
|
/* Go back to the start of the chunk so we can normalize the position of the cursor. */
|
||||||
if (drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData) == DRWAV_FALSE) {
|
if (drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData) == DRWAV_FALSE) {
|
||||||
|
@ -7830,7 +7833,7 @@ DRWAV_API void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t samp
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < sampleCount; ++i) {
|
for (i = 0; i < sampleCount; ++i) {
|
||||||
*pOut++ = (drwav_int32)(2147483648.0 * pIn[i]);
|
*pOut++ = (drwav_int32)(2147483648.0f * pIn[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8347,6 +8350,15 @@ DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b)
|
||||||
/*
|
/*
|
||||||
REVISION HISTORY
|
REVISION HISTORY
|
||||||
================
|
================
|
||||||
|
v0.13.16 - 2024-02-27
|
||||||
|
- Fix a Wdouble-promotion warning.
|
||||||
|
|
||||||
|
v0.13.15 - 2024-01-23
|
||||||
|
- Relax some unnecessary validation that prevented some files from loading.
|
||||||
|
|
||||||
|
v0.13.14 - 2023-12-02
|
||||||
|
- Fix a warning about an unused variable.
|
||||||
|
|
||||||
v0.13.13 - 2023-11-02
|
v0.13.13 - 2023-11-02
|
||||||
- Fix a warning when compiling with Clang.
|
- Fix a warning when compiling with Clang.
|
||||||
|
|
||||||
|
|
14
raylib/external/jar_xm.h
vendored
14
raylib/external/jar_xm.h
vendored
|
@ -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);
|
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(x, b) (((x) + ((b) - 1)) & ~((b) - 1))
|
||||||
#define ALIGN_PTR(x, b) (void*)(((uintptr_t)(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) {
|
int jar_xm_create_context_safe(jar_xm_context_t** ctxp, const char* moddata, size_t moddata_length, uint32_t rate) {
|
||||||
|
@ -1208,7 +1204,7 @@ static void jar_xm_tone_portamento(jar_xm_context_t* ctx, jar_xm_channel_context
|
||||||
}
|
}
|
||||||
|
|
||||||
static void jar_xm_pitch_slide(jar_xm_context_t* ctx, jar_xm_channel_context_t* ch, float period_offset) {
|
static void jar_xm_pitch_slide(jar_xm_context_t* ctx, jar_xm_channel_context_t* ch, float period_offset) {
|
||||||
/* Don't ask about the 4.f coefficient. I found mention of it nowhere. Found by ear™. */
|
/* Don't ask about the 4.f coefficient. I found mention of it nowhere. Found by ear. */
|
||||||
if(ctx->module.frequency_type == jar_xm_LINEAR_FREQUENCIES) {period_offset *= 4.f; }
|
if(ctx->module.frequency_type == jar_xm_LINEAR_FREQUENCIES) {period_offset *= 4.f; }
|
||||||
ch->period += period_offset;
|
ch->period += period_offset;
|
||||||
jar_xm_CLAMP_DOWN(ch->period);
|
jar_xm_CLAMP_DOWN(ch->period);
|
||||||
|
@ -1511,7 +1507,7 @@ static void jar_xm_handle_note_and_instrument(jar_xm_context_t* ctx, jar_xm_chan
|
||||||
jar_xm_volume_slide(ch, ch->fine_volume_slide_param);
|
jar_xm_volume_slide(ch, ch->fine_volume_slide_param);
|
||||||
break;
|
break;
|
||||||
case 0xD: /* EDy: Note delay */
|
case 0xD: /* EDy: Note delay */
|
||||||
/* XXX: figure this out better. EDx triggers the note even when there no note and no instrument. But ED0 acts like like a ghost note, EDx (x ≠ 0) does not. */
|
/* XXX: figure this out better. EDx triggers the note even when there no note and no instrument. But ED0 acts like like a ghost note, EDx (x != 0) does not. */
|
||||||
if(s->note == 0 && s->instrument == 0) {
|
if(s->note == 0 && s->instrument == 0) {
|
||||||
unsigned int flags = jar_xm_TRIGGER_KEEP_VOLUME;
|
unsigned int flags = jar_xm_TRIGGER_KEEP_VOLUME;
|
||||||
if(ch->current->effect_param & 0x0F) {
|
if(ch->current->effect_param & 0x0F) {
|
||||||
|
@ -1799,7 +1795,7 @@ static void jar_xm_tick(jar_xm_context_t* ctx) {
|
||||||
if(ch->current->effect_param > 0) {
|
if(ch->current->effect_param > 0) {
|
||||||
char arp_offset = ctx->tempo % 3;
|
char arp_offset = ctx->tempo % 3;
|
||||||
switch(arp_offset) {
|
switch(arp_offset) {
|
||||||
case 2: /* 0 -> x -> 0 -> y -> x -> … */
|
case 2: /* 0 -> x -> 0 -> y -> x -> ... */
|
||||||
if(ctx->current_tick == 1) {
|
if(ctx->current_tick == 1) {
|
||||||
ch->arp_in_progress = true;
|
ch->arp_in_progress = true;
|
||||||
ch->arp_note_offset = ch->current->effect_param >> 4;
|
ch->arp_note_offset = ch->current->effect_param >> 4;
|
||||||
|
@ -1807,7 +1803,7 @@ static void jar_xm_tick(jar_xm_context_t* ctx) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* No break here, this is intended */
|
/* No break here, this is intended */
|
||||||
case 1: /* 0 -> 0 -> y -> x -> … */
|
case 1: /* 0 -> 0 -> y -> x -> ... */
|
||||||
if(ctx->current_tick == 0) {
|
if(ctx->current_tick == 0) {
|
||||||
ch->arp_in_progress = false;
|
ch->arp_in_progress = false;
|
||||||
ch->arp_note_offset = 0;
|
ch->arp_note_offset = 0;
|
||||||
|
@ -1815,7 +1811,7 @@ static void jar_xm_tick(jar_xm_context_t* ctx) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* No break here, this is intended */
|
/* No break here, this is intended */
|
||||||
case 0: /* 0 -> y -> x -> … */
|
case 0: /* 0 -> y -> x -> ... */
|
||||||
jar_xm_arpeggio(ctx, ch, ch->current->effect_param, ctx->current_tick - arp_offset);
|
jar_xm_arpeggio(ctx, ch, ch->current->effect_param, ctx->current_tick - arp_offset);
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
12
raylib/external/miniaudio.h
vendored
12
raylib/external/miniaudio.h
vendored
|
@ -21473,7 +21473,9 @@ static ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device
|
||||||
MA_ASSERT(pContext != NULL);
|
MA_ASSERT(pContext != NULL);
|
||||||
MA_ASSERT(ppMMDevice != NULL);
|
MA_ASSERT(ppMMDevice != NULL);
|
||||||
|
|
||||||
|
ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE);
|
||||||
hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
|
hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
|
||||||
|
ma_CoUninitialize(pContext);
|
||||||
if (FAILED(hr)) {
|
if (FAILED(hr)) {
|
||||||
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create IMMDeviceEnumerator.\n");
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create IMMDeviceEnumerator.\n");
|
||||||
return ma_result_from_HRESULT(hr);
|
return ma_result_from_HRESULT(hr);
|
||||||
|
@ -36076,9 +36078,15 @@ static ma_result ma_context_get_device_info_from_fd__audio4(ma_context* pContext
|
||||||
ma_uint32 channels;
|
ma_uint32 channels;
|
||||||
ma_uint32 sampleRate;
|
ma_uint32 sampleRate;
|
||||||
|
|
||||||
|
#ifdef __NetBSD__
|
||||||
|
if (ioctl(fd, AUDIO_GETFORMAT, &fdInfo) < 0) {
|
||||||
|
return MA_ERROR;
|
||||||
|
}
|
||||||
|
#else
|
||||||
if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) {
|
if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) {
|
||||||
return MA_ERROR;
|
return MA_ERROR;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (deviceType == ma_device_type_playback) {
|
if (deviceType == ma_device_type_playback) {
|
||||||
channels = fdInfo.play.channels;
|
channels = fdInfo.play.channels;
|
||||||
|
@ -36356,7 +36364,11 @@ static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_c
|
||||||
/* We're using a default device. Get the info from the /dev/audioctl file instead of /dev/audio. */
|
/* We're using a default device. Get the info from the /dev/audioctl file instead of /dev/audio. */
|
||||||
int fdctl = open(pDefaultDeviceCtlNames[iDefaultDevice], fdFlags, 0);
|
int fdctl = open(pDefaultDeviceCtlNames[iDefaultDevice], fdFlags, 0);
|
||||||
if (fdctl != -1) {
|
if (fdctl != -1) {
|
||||||
|
#ifdef __NetBSD__
|
||||||
|
fdInfoResult = ioctl(fdctl, AUDIO_GETFORMAT, &fdInfo);
|
||||||
|
#else
|
||||||
fdInfoResult = ioctl(fdctl, AUDIO_GETINFO, &fdInfo);
|
fdInfoResult = ioctl(fdctl, AUDIO_GETINFO, &fdInfo);
|
||||||
|
#endif
|
||||||
close(fdctl);
|
close(fdctl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
2
raylib/external/par_shapes.h
vendored
2
raylib/external/par_shapes.h
vendored
|
@ -1130,7 +1130,7 @@ static par_shapes__rule* par_shapes__pick_rule(const char* name,
|
||||||
total += rule->weight;
|
total += rule->weight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
float r = (float) rand() / RAND_MAX;
|
float r = (float) rand() / (float) RAND_MAX;
|
||||||
float t = 0;
|
float t = 0;
|
||||||
for (int i = 0; i < nrules; i++) {
|
for (int i = 0; i < nrules; i++) {
|
||||||
rule = rules + i;
|
rule = rules + i;
|
||||||
|
|
92
raylib/external/qoa.h
vendored
92
raylib/external/qoa.h
vendored
|
@ -366,22 +366,7 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned
|
||||||
), bytes, &p);
|
), bytes, &p);
|
||||||
|
|
||||||
|
|
||||||
for (int c = 0; c < channels; c++) {
|
for (unsigned int c = 0; c < channels; c++) {
|
||||||
/* If the weights have grown too large, reset them to 0. This may happen
|
|
||||||
with certain high-frequency sounds. This is a last resort and will
|
|
||||||
introduce quite a bit of noise, but should at least prevent pops/clicks */
|
|
||||||
int weights_sum =
|
|
||||||
qoa->lms[c].weights[0] * qoa->lms[c].weights[0] +
|
|
||||||
qoa->lms[c].weights[1] * qoa->lms[c].weights[1] +
|
|
||||||
qoa->lms[c].weights[2] * qoa->lms[c].weights[2] +
|
|
||||||
qoa->lms[c].weights[3] * qoa->lms[c].weights[3];
|
|
||||||
if (weights_sum > 0x2fffffff) {
|
|
||||||
qoa->lms[c].weights[0] = 0;
|
|
||||||
qoa->lms[c].weights[1] = 0;
|
|
||||||
qoa->lms[c].weights[2] = 0;
|
|
||||||
qoa->lms[c].weights[3] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Write the current LMS state */
|
/* Write the current LMS state */
|
||||||
qoa_uint64_t weights = 0;
|
qoa_uint64_t weights = 0;
|
||||||
qoa_uint64_t history = 0;
|
qoa_uint64_t history = 0;
|
||||||
|
@ -395,9 +380,9 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned
|
||||||
|
|
||||||
/* We encode all samples with the channels interleaved on a slice level.
|
/* 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), ...*/
|
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 (unsigned int sample_index = 0; sample_index < frame_len; sample_index += QOA_SLICE_LEN) {
|
||||||
|
|
||||||
for (int c = 0; c < channels; c++) {
|
for (unsigned int c = 0; c < channels; c++) {
|
||||||
int slice_len = qoa_clamp(QOA_SLICE_LEN, 0, frame_len - sample_index);
|
int slice_len = qoa_clamp(QOA_SLICE_LEN, 0, frame_len - sample_index);
|
||||||
int slice_start = sample_index * channels + c;
|
int slice_start = sample_index * channels + c;
|
||||||
int slice_end = (sample_index + slice_len) * channels + c;
|
int slice_end = (sample_index + slice_len) * channels + c;
|
||||||
|
@ -405,10 +390,13 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned
|
||||||
/* Brute for search for the best scalefactor. Just go through all
|
/* Brute for search for the best scalefactor. Just go through all
|
||||||
16 scalefactors, encode all samples for the current slice and
|
16 scalefactors, encode all samples for the current slice and
|
||||||
meassure the total squared error. */
|
meassure the total squared error. */
|
||||||
qoa_uint64_t best_error = -1;
|
qoa_uint64_t best_rank = -1;
|
||||||
qoa_uint64_t best_slice;
|
#ifdef QOA_RECORD_TOTAL_ERROR
|
||||||
|
qoa_uint64_t best_error = -1;
|
||||||
|
#endif
|
||||||
|
qoa_uint64_t best_slice = 0;
|
||||||
qoa_lms_t best_lms;
|
qoa_lms_t best_lms;
|
||||||
int best_scalefactor;
|
int best_scalefactor = 0;
|
||||||
|
|
||||||
for (int sfi = 0; sfi < 16; sfi++) {
|
for (int sfi = 0; sfi < 16; sfi++) {
|
||||||
/* There is a strong correlation between the scalefactors of
|
/* There is a strong correlation between the scalefactors of
|
||||||
|
@ -421,7 +409,10 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned
|
||||||
state when encoding. */
|
state when encoding. */
|
||||||
qoa_lms_t lms = qoa->lms[c];
|
qoa_lms_t lms = qoa->lms[c];
|
||||||
qoa_uint64_t slice = scalefactor;
|
qoa_uint64_t slice = scalefactor;
|
||||||
qoa_uint64_t current_error = 0;
|
qoa_uint64_t current_rank = 0;
|
||||||
|
#ifdef QOA_RECORD_TOTAL_ERROR
|
||||||
|
qoa_uint64_t current_error = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
for (int si = slice_start; si < slice_end; si += channels) {
|
for (int si = slice_start; si < slice_end; si += channels) {
|
||||||
int sample = sample_data[si];
|
int sample = sample_data[si];
|
||||||
|
@ -434,9 +425,27 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned
|
||||||
int dequantized = qoa_dequant_tab[scalefactor][quantized];
|
int dequantized = qoa_dequant_tab[scalefactor][quantized];
|
||||||
int reconstructed = qoa_clamp_s16(predicted + dequantized);
|
int reconstructed = qoa_clamp_s16(predicted + dequantized);
|
||||||
|
|
||||||
|
|
||||||
|
/* If the weights have grown too large, we introduce a penalty
|
||||||
|
here. This prevents pops/clicks in certain problem cases */
|
||||||
|
int weights_penalty = ((
|
||||||
|
lms.weights[0] * lms.weights[0] +
|
||||||
|
lms.weights[1] * lms.weights[1] +
|
||||||
|
lms.weights[2] * lms.weights[2] +
|
||||||
|
lms.weights[3] * lms.weights[3]
|
||||||
|
) >> 18) - 0x8ff;
|
||||||
|
if (weights_penalty < 0) {
|
||||||
|
weights_penalty = 0;
|
||||||
|
}
|
||||||
|
|
||||||
long long error = (sample - reconstructed);
|
long long error = (sample - reconstructed);
|
||||||
current_error += error * error;
|
qoa_uint64_t error_sq = error * error;
|
||||||
if (current_error > best_error) {
|
|
||||||
|
current_rank += error_sq + weights_penalty * weights_penalty;
|
||||||
|
#ifdef QOA_RECORD_TOTAL_ERROR
|
||||||
|
current_error += error_sq;
|
||||||
|
#endif
|
||||||
|
if (current_rank > best_rank) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -444,8 +453,11 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned
|
||||||
slice = (slice << 3) | quantized;
|
slice = (slice << 3) | quantized;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current_error < best_error) {
|
if (current_rank < best_rank) {
|
||||||
best_error = current_error;
|
best_rank = current_rank;
|
||||||
|
#ifdef QOA_RECORD_TOTAL_ERROR
|
||||||
|
best_error = current_error;
|
||||||
|
#endif
|
||||||
best_slice = slice;
|
best_slice = slice;
|
||||||
best_lms = lms;
|
best_lms = lms;
|
||||||
best_scalefactor = scalefactor;
|
best_scalefactor = scalefactor;
|
||||||
|
@ -490,7 +502,7 @@ void *qoa_encode(const short *sample_data, qoa_desc *qoa, unsigned int *out_len)
|
||||||
|
|
||||||
unsigned char *bytes = QOA_MALLOC(encoded_size);
|
unsigned char *bytes = QOA_MALLOC(encoded_size);
|
||||||
|
|
||||||
for (int c = 0; c < qoa->channels; c++) {
|
for (unsigned int c = 0; c < qoa->channels; c++) {
|
||||||
/* Set the initial LMS weights to {0, 0, -1, 2}. This helps with the
|
/* Set the initial LMS weights to {0, 0, -1, 2}. This helps with the
|
||||||
prediction of the first few ms of a file. */
|
prediction of the first few ms of a file. */
|
||||||
qoa->lms[c].weights[0] = 0;
|
qoa->lms[c].weights[0] = 0;
|
||||||
|
@ -513,7 +525,7 @@ void *qoa_encode(const short *sample_data, qoa_desc *qoa, unsigned int *out_len)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int frame_len = QOA_FRAME_LEN;
|
int frame_len = QOA_FRAME_LEN;
|
||||||
for (int sample_index = 0; sample_index < qoa->samples; sample_index += frame_len) {
|
for (unsigned int sample_index = 0; sample_index < qoa->samples; sample_index += frame_len) {
|
||||||
frame_len = qoa_clamp(QOA_FRAME_LEN, 0, qoa->samples - sample_index);
|
frame_len = qoa_clamp(QOA_FRAME_LEN, 0, qoa->samples - sample_index);
|
||||||
const short *frame_samples = sample_data + sample_index * qoa->channels;
|
const short *frame_samples = sample_data + sample_index * qoa->channels;
|
||||||
unsigned int frame_size = qoa_encode_frame(frame_samples, qoa, frame_len, bytes + p);
|
unsigned int frame_size = qoa_encode_frame(frame_samples, qoa, frame_len, bytes + p);
|
||||||
|
@ -576,14 +588,14 @@ unsigned int qoa_decode_frame(const unsigned char *bytes, unsigned int size, qoa
|
||||||
|
|
||||||
/* Read and verify the frame header */
|
/* Read and verify the frame header */
|
||||||
qoa_uint64_t frame_header = qoa_read_u64(bytes, &p);
|
qoa_uint64_t frame_header = qoa_read_u64(bytes, &p);
|
||||||
int channels = (frame_header >> 56) & 0x0000ff;
|
unsigned int channels = (frame_header >> 56) & 0x0000ff;
|
||||||
int samplerate = (frame_header >> 32) & 0xffffff;
|
unsigned int samplerate = (frame_header >> 32) & 0xffffff;
|
||||||
int samples = (frame_header >> 16) & 0x00ffff;
|
unsigned int samples = (frame_header >> 16) & 0x00ffff;
|
||||||
int frame_size = (frame_header ) & 0x00ffff;
|
unsigned int frame_size = (frame_header ) & 0x00ffff;
|
||||||
|
|
||||||
int data_size = frame_size - 8 - QOA_LMS_LEN * 4 * channels;
|
unsigned int data_size = frame_size - 8 - QOA_LMS_LEN * 4 * channels;
|
||||||
int num_slices = data_size / 8;
|
unsigned int num_slices = data_size / 8;
|
||||||
int max_total_samples = num_slices * QOA_SLICE_LEN;
|
unsigned int max_total_samples = num_slices * QOA_SLICE_LEN;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
channels != qoa->channels ||
|
channels != qoa->channels ||
|
||||||
|
@ -596,7 +608,7 @@ unsigned int qoa_decode_frame(const unsigned char *bytes, unsigned int size, qoa
|
||||||
|
|
||||||
|
|
||||||
/* Read the LMS state: 4 x 2 bytes history, 4 x 2 bytes weights per channel */
|
/* Read the LMS state: 4 x 2 bytes history, 4 x 2 bytes weights per channel */
|
||||||
for (int c = 0; c < channels; c++) {
|
for (unsigned int c = 0; c < channels; c++) {
|
||||||
qoa_uint64_t history = qoa_read_u64(bytes, &p);
|
qoa_uint64_t history = qoa_read_u64(bytes, &p);
|
||||||
qoa_uint64_t weights = qoa_read_u64(bytes, &p);
|
qoa_uint64_t weights = qoa_read_u64(bytes, &p);
|
||||||
for (int i = 0; i < QOA_LMS_LEN; i++) {
|
for (int i = 0; i < QOA_LMS_LEN; i++) {
|
||||||
|
@ -609,17 +621,19 @@ unsigned int qoa_decode_frame(const unsigned char *bytes, unsigned int size, qoa
|
||||||
|
|
||||||
|
|
||||||
/* Decode all slices for all channels in this frame */
|
/* Decode all slices for all channels in this frame */
|
||||||
for (int sample_index = 0; sample_index < samples; sample_index += QOA_SLICE_LEN) {
|
for (unsigned int sample_index = 0; sample_index < samples; sample_index += QOA_SLICE_LEN) {
|
||||||
for (int c = 0; c < channels; c++) {
|
for (unsigned int c = 0; c < channels; c++) {
|
||||||
qoa_uint64_t slice = qoa_read_u64(bytes, &p);
|
qoa_uint64_t slice = qoa_read_u64(bytes, &p);
|
||||||
|
|
||||||
int scalefactor = (slice >> 60) & 0xf;
|
int scalefactor = (slice >> 60) & 0xf;
|
||||||
|
slice <<= 4;
|
||||||
|
|
||||||
int slice_start = sample_index * channels + c;
|
int slice_start = sample_index * channels + c;
|
||||||
int slice_end = qoa_clamp(sample_index + QOA_SLICE_LEN, 0, samples) * 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) {
|
for (int si = slice_start; si < slice_end; si += channels) {
|
||||||
int predicted = qoa_lms_predict(&qoa->lms[c]);
|
int predicted = qoa_lms_predict(&qoa->lms[c]);
|
||||||
int quantized = (slice >> 57) & 0x7;
|
int quantized = (slice >> 61) & 0x7;
|
||||||
int dequantized = qoa_dequant_tab[scalefactor][quantized];
|
int dequantized = qoa_dequant_tab[scalefactor][quantized];
|
||||||
int reconstructed = qoa_clamp_s16(predicted + dequantized);
|
int reconstructed = qoa_clamp_s16(predicted + dequantized);
|
||||||
|
|
||||||
|
|
17
raylib/external/rl_gputex.h
vendored
17
raylib/external/rl_gputex.h
vendored
|
@ -171,6 +171,10 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_
|
||||||
|
|
||||||
*width = header->width;
|
*width = header->width;
|
||||||
*height = header->height;
|
*height = header->height;
|
||||||
|
|
||||||
|
if (*width % 4 != 0) LOG("WARNING: IMAGE: DDS file width must be multiple of 4. Image will not display correctly");
|
||||||
|
if (*height % 4 != 0) LOG("WARNING: IMAGE: DDS file height must be multiple of 4. Image will not display correctly");
|
||||||
|
|
||||||
image_pixel_size = header->width*header->height;
|
image_pixel_size = header->width*header->height;
|
||||||
|
|
||||||
if (header->mipmap_count == 0) *mips = 1; // Parameter not used
|
if (header->mipmap_count == 0) *mips = 1; // Parameter not used
|
||||||
|
@ -181,6 +185,7 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_
|
||||||
if (header->ddspf.flags == 0x40) // No alpha channel
|
if (header->ddspf.flags == 0x40) // No alpha channel
|
||||||
{
|
{
|
||||||
int data_size = image_pixel_size*sizeof(unsigned short);
|
int data_size = image_pixel_size*sizeof(unsigned short);
|
||||||
|
if (header->mipmap_count > 1) data_size = data_size + data_size / 3;
|
||||||
image_data = RL_MALLOC(data_size);
|
image_data = RL_MALLOC(data_size);
|
||||||
|
|
||||||
memcpy(image_data, file_data_ptr, data_size);
|
memcpy(image_data, file_data_ptr, data_size);
|
||||||
|
@ -192,6 +197,7 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_
|
||||||
if (header->ddspf.a_bit_mask == 0x8000) // 1bit alpha
|
if (header->ddspf.a_bit_mask == 0x8000) // 1bit alpha
|
||||||
{
|
{
|
||||||
int data_size = image_pixel_size*sizeof(unsigned short);
|
int data_size = image_pixel_size*sizeof(unsigned short);
|
||||||
|
if (header->mipmap_count > 1) data_size = data_size + data_size / 3;
|
||||||
image_data = RL_MALLOC(data_size);
|
image_data = RL_MALLOC(data_size);
|
||||||
|
|
||||||
memcpy(image_data, file_data_ptr, data_size);
|
memcpy(image_data, file_data_ptr, data_size);
|
||||||
|
@ -211,6 +217,7 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_
|
||||||
else if (header->ddspf.a_bit_mask == 0xf000) // 4bit alpha
|
else if (header->ddspf.a_bit_mask == 0xf000) // 4bit alpha
|
||||||
{
|
{
|
||||||
int data_size = image_pixel_size*sizeof(unsigned short);
|
int data_size = image_pixel_size*sizeof(unsigned short);
|
||||||
|
if (header->mipmap_count > 1) data_size = data_size + data_size / 3;
|
||||||
image_data = RL_MALLOC(data_size);
|
image_data = RL_MALLOC(data_size);
|
||||||
|
|
||||||
memcpy(image_data, file_data_ptr, data_size);
|
memcpy(image_data, file_data_ptr, data_size);
|
||||||
|
@ -232,6 +239,7 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_
|
||||||
else if ((header->ddspf.flags == 0x40) && (header->ddspf.rgb_bit_count == 24)) // DDS_RGB, no compressed
|
else if ((header->ddspf.flags == 0x40) && (header->ddspf.rgb_bit_count == 24)) // DDS_RGB, no compressed
|
||||||
{
|
{
|
||||||
int data_size = image_pixel_size*3*sizeof(unsigned char);
|
int data_size = image_pixel_size*3*sizeof(unsigned char);
|
||||||
|
if (header->mipmap_count > 1) data_size = data_size + data_size / 3;
|
||||||
image_data = RL_MALLOC(data_size);
|
image_data = RL_MALLOC(data_size);
|
||||||
|
|
||||||
memcpy(image_data, file_data_ptr, data_size);
|
memcpy(image_data, file_data_ptr, data_size);
|
||||||
|
@ -241,6 +249,7 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_
|
||||||
else if ((header->ddspf.flags == 0x41) && (header->ddspf.rgb_bit_count == 32)) // DDS_RGBA, no compressed
|
else if ((header->ddspf.flags == 0x41) && (header->ddspf.rgb_bit_count == 32)) // DDS_RGBA, no compressed
|
||||||
{
|
{
|
||||||
int data_size = image_pixel_size*4*sizeof(unsigned char);
|
int data_size = image_pixel_size*4*sizeof(unsigned char);
|
||||||
|
if (header->mipmap_count > 1) data_size = data_size + data_size / 3;
|
||||||
image_data = RL_MALLOC(data_size);
|
image_data = RL_MALLOC(data_size);
|
||||||
|
|
||||||
memcpy(image_data, file_data_ptr, data_size);
|
memcpy(image_data, file_data_ptr, data_size);
|
||||||
|
@ -261,9 +270,11 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_
|
||||||
}
|
}
|
||||||
else if (((header->ddspf.flags == 0x04) || (header->ddspf.flags == 0x05)) && (header->ddspf.fourcc > 0)) // Compressed
|
else if (((header->ddspf.flags == 0x04) || (header->ddspf.flags == 0x05)) && (header->ddspf.fourcc > 0)) // Compressed
|
||||||
{
|
{
|
||||||
// NOTE: This forces only 1 mipmap to be loaded which is not really correct but it works
|
int data_size = 0;
|
||||||
int data_size = (header->pitch_or_linear_size < file_size - 0x80) ? header->pitch_or_linear_size : file_size - 0x80;
|
|
||||||
*mips = 1;
|
// Calculate data size, including all mipmaps
|
||||||
|
if (header->mipmap_count > 1) data_size = header->pitch_or_linear_size + header->pitch_or_linear_size / 3;
|
||||||
|
else data_size = header->pitch_or_linear_size;
|
||||||
|
|
||||||
image_data = RL_MALLOC(data_size*sizeof(unsigned char));
|
image_data = RL_MALLOC(data_size*sizeof(unsigned char));
|
||||||
|
|
||||||
|
|
355
raylib/external/stb_image.h
vendored
355
raylib/external/stb_image.h
vendored
|
@ -1,4 +1,4 @@
|
||||||
/* stb_image - v2.28 - public domain image loader - http://nothings.org/stb
|
/* stb_image - v2.30 - public domain image loader - http://nothings.org/stb
|
||||||
no warranty implied; use at your own risk
|
no warranty implied; use at your own risk
|
||||||
|
|
||||||
Do this:
|
Do this:
|
||||||
|
@ -48,6 +48,8 @@ LICENSE
|
||||||
|
|
||||||
RECENT REVISION HISTORY:
|
RECENT REVISION HISTORY:
|
||||||
|
|
||||||
|
2.30 (2024-05-31) avoid erroneous gcc warning
|
||||||
|
2.29 (2023-05-xx) optimizations
|
||||||
2.28 (2023-01-29) many error fixes, security errors, just tons of stuff
|
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.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes
|
||||||
2.26 (2020-07-13) many minor fixes
|
2.26 (2020-07-13) many minor fixes
|
||||||
|
@ -1072,8 +1074,8 @@ static int stbi__addints_valid(int a, int b)
|
||||||
return a <= INT_MAX - b;
|
return a <= INT_MAX - b;
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns 1 if the product of two signed shorts is valid, 0 on overflow.
|
// returns 1 if the product of two ints fits in a signed short, 0 on overflow.
|
||||||
static int stbi__mul2shorts_valid(short a, short b)
|
static int stbi__mul2shorts_valid(int a, int 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 (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 ((a >= 0) == (b >= 0)) return a <= SHRT_MAX/b; // product is positive, so similar to mul2sizes_valid
|
||||||
|
@ -3384,13 +3386,13 @@ static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int stbi__skip_jpeg_junk_at_end(stbi__jpeg *j)
|
static stbi_uc stbi__skip_jpeg_junk_at_end(stbi__jpeg *j)
|
||||||
{
|
{
|
||||||
// some JPEGs have junk at end, skip over it but if we find what looks
|
// some JPEGs have junk at end, skip over it but if we find what looks
|
||||||
// like a valid marker, resume there
|
// like a valid marker, resume there
|
||||||
while (!stbi__at_eof(j->s)) {
|
while (!stbi__at_eof(j->s)) {
|
||||||
int x = stbi__get8(j->s);
|
stbi_uc x = stbi__get8(j->s);
|
||||||
while (x == 255) { // might be a marker
|
while (x == 0xff) { // might be a marker
|
||||||
if (stbi__at_eof(j->s)) return STBI__MARKER_none;
|
if (stbi__at_eof(j->s)) return STBI__MARKER_none;
|
||||||
x = stbi__get8(j->s);
|
x = stbi__get8(j->s);
|
||||||
if (x != 0x00 && x != 0xff) {
|
if (x != 0x00 && x != 0xff) {
|
||||||
|
@ -4176,6 +4178,7 @@ typedef struct
|
||||||
{
|
{
|
||||||
stbi_uc *zbuffer, *zbuffer_end;
|
stbi_uc *zbuffer, *zbuffer_end;
|
||||||
int num_bits;
|
int num_bits;
|
||||||
|
int hit_zeof_once;
|
||||||
stbi__uint32 code_buffer;
|
stbi__uint32 code_buffer;
|
||||||
|
|
||||||
char *zout;
|
char *zout;
|
||||||
|
@ -4242,9 +4245,20 @@ stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z)
|
||||||
int b,s;
|
int b,s;
|
||||||
if (a->num_bits < 16) {
|
if (a->num_bits < 16) {
|
||||||
if (stbi__zeof(a)) {
|
if (stbi__zeof(a)) {
|
||||||
return -1; /* report error for unexpected end of data. */
|
if (!a->hit_zeof_once) {
|
||||||
|
// This is the first time we hit eof, insert 16 extra padding btis
|
||||||
|
// to allow us to keep going; if we actually consume any of them
|
||||||
|
// though, that is invalid data. This is caught later.
|
||||||
|
a->hit_zeof_once = 1;
|
||||||
|
a->num_bits += 16; // add 16 implicit zero bits
|
||||||
|
} else {
|
||||||
|
// We already inserted our extra 16 padding bits and are again
|
||||||
|
// out, this stream is actually prematurely terminated.
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stbi__fill_bits(a);
|
||||||
}
|
}
|
||||||
stbi__fill_bits(a);
|
|
||||||
}
|
}
|
||||||
b = z->fast[a->code_buffer & STBI__ZFAST_MASK];
|
b = z->fast[a->code_buffer & STBI__ZFAST_MASK];
|
||||||
if (b) {
|
if (b) {
|
||||||
|
@ -4309,6 +4323,13 @@ static int stbi__parse_huffman_block(stbi__zbuf *a)
|
||||||
int len,dist;
|
int len,dist;
|
||||||
if (z == 256) {
|
if (z == 256) {
|
||||||
a->zout = zout;
|
a->zout = zout;
|
||||||
|
if (a->hit_zeof_once && a->num_bits < 16) {
|
||||||
|
// The first time we hit zeof, we inserted 16 extra zero bits into our bit
|
||||||
|
// buffer so the decoder can just do its speculative decoding. But if we
|
||||||
|
// actually consumed any of those bits (which is the case when num_bits < 16),
|
||||||
|
// the stream actually read past the end so it is malformed.
|
||||||
|
return stbi__err("unexpected end","Corrupt PNG");
|
||||||
|
}
|
||||||
return 1;
|
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
|
if (z >= 286) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, length codes 286 and 287 must not appear in compressed data
|
||||||
|
@ -4320,7 +4341,7 @@ static int stbi__parse_huffman_block(stbi__zbuf *a)
|
||||||
dist = stbi__zdist_base[z];
|
dist = stbi__zdist_base[z];
|
||||||
if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[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");
|
if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG");
|
||||||
if (zout + len > a->zout_end) {
|
if (len > a->zout_end - zout) {
|
||||||
if (!stbi__zexpand(a, zout, len)) return 0;
|
if (!stbi__zexpand(a, zout, len)) return 0;
|
||||||
zout = a->zout;
|
zout = a->zout;
|
||||||
}
|
}
|
||||||
|
@ -4464,6 +4485,7 @@ static int stbi__parse_zlib(stbi__zbuf *a, int parse_header)
|
||||||
if (!stbi__parse_zlib_header(a)) return 0;
|
if (!stbi__parse_zlib_header(a)) return 0;
|
||||||
a->num_bits = 0;
|
a->num_bits = 0;
|
||||||
a->code_buffer = 0;
|
a->code_buffer = 0;
|
||||||
|
a->hit_zeof_once = 0;
|
||||||
do {
|
do {
|
||||||
final = stbi__zreceive(a,1);
|
final = stbi__zreceive(a,1);
|
||||||
type = stbi__zreceive(a,2);
|
type = stbi__zreceive(a,2);
|
||||||
|
@ -4619,9 +4641,8 @@ enum {
|
||||||
STBI__F_up=2,
|
STBI__F_up=2,
|
||||||
STBI__F_avg=3,
|
STBI__F_avg=3,
|
||||||
STBI__F_paeth=4,
|
STBI__F_paeth=4,
|
||||||
// synthetic filters used for first scanline to avoid needing a dummy row of 0s
|
// synthetic filter used for first scanline to avoid needing a dummy row of 0s
|
||||||
STBI__F_avg_first,
|
STBI__F_avg_first
|
||||||
STBI__F_paeth_first
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static stbi_uc first_row_filter[5] =
|
static stbi_uc first_row_filter[5] =
|
||||||
|
@ -4630,29 +4651,56 @@ static stbi_uc first_row_filter[5] =
|
||||||
STBI__F_sub,
|
STBI__F_sub,
|
||||||
STBI__F_none,
|
STBI__F_none,
|
||||||
STBI__F_avg_first,
|
STBI__F_avg_first,
|
||||||
STBI__F_paeth_first
|
STBI__F_sub // Paeth with b=c=0 turns out to be equivalent to sub
|
||||||
};
|
};
|
||||||
|
|
||||||
static int stbi__paeth(int a, int b, int c)
|
static int stbi__paeth(int a, int b, int c)
|
||||||
{
|
{
|
||||||
int p = a + b - c;
|
// This formulation looks very different from the reference in the PNG spec, but is
|
||||||
int pa = abs(p-a);
|
// actually equivalent and has favorable data dependencies and admits straightforward
|
||||||
int pb = abs(p-b);
|
// generation of branch-free code, which helps performance significantly.
|
||||||
int pc = abs(p-c);
|
int thresh = c*3 - (a + b);
|
||||||
if (pa <= pb && pa <= pc) return a;
|
int lo = a < b ? a : b;
|
||||||
if (pb <= pc) return b;
|
int hi = a < b ? b : a;
|
||||||
return c;
|
int t0 = (hi <= thresh) ? lo : c;
|
||||||
|
int t1 = (thresh <= lo) ? hi : t0;
|
||||||
|
return t1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 };
|
static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 };
|
||||||
|
|
||||||
|
// adds an extra all-255 alpha channel
|
||||||
|
// dest == src is legal
|
||||||
|
// img_n must be 1 or 3
|
||||||
|
static void stbi__create_png_alpha_expand8(stbi_uc *dest, stbi_uc *src, stbi__uint32 x, int img_n)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
// must process data backwards since we allow dest==src
|
||||||
|
if (img_n == 1) {
|
||||||
|
for (i=x-1; i >= 0; --i) {
|
||||||
|
dest[i*2+1] = 255;
|
||||||
|
dest[i*2+0] = src[i];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
STBI_ASSERT(img_n == 3);
|
||||||
|
for (i=x-1; i >= 0; --i) {
|
||||||
|
dest[i*4+3] = 255;
|
||||||
|
dest[i*4+2] = src[i*3+2];
|
||||||
|
dest[i*4+1] = src[i*3+1];
|
||||||
|
dest[i*4+0] = src[i*3+0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// create the png data from post-deflated data
|
// create the png data from post-deflated data
|
||||||
static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color)
|
static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color)
|
||||||
{
|
{
|
||||||
int bytes = (depth == 16? 2 : 1);
|
int bytes = (depth == 16 ? 2 : 1);
|
||||||
stbi__context *s = a->s;
|
stbi__context *s = a->s;
|
||||||
stbi__uint32 i,j,stride = x*out_n*bytes;
|
stbi__uint32 i,j,stride = x*out_n*bytes;
|
||||||
stbi__uint32 img_len, img_width_bytes;
|
stbi__uint32 img_len, img_width_bytes;
|
||||||
|
stbi_uc *filter_buf;
|
||||||
|
int all_ok = 1;
|
||||||
int k;
|
int k;
|
||||||
int img_n = s->img_n; // copy it into a local for later
|
int img_n = s->img_n; // copy it into a local for later
|
||||||
|
|
||||||
|
@ -4664,8 +4712,11 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r
|
||||||
a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into
|
a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into
|
||||||
if (!a->out) return stbi__err("outofmem", "Out of memory");
|
if (!a->out) return stbi__err("outofmem", "Out of memory");
|
||||||
|
|
||||||
|
// note: error exits here don't need to clean up a->out individually,
|
||||||
|
// stbi__do_png always does on error.
|
||||||
if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG");
|
if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG");
|
||||||
img_width_bytes = (((img_n * x * depth) + 7) >> 3);
|
img_width_bytes = (((img_n * x * depth) + 7) >> 3);
|
||||||
|
if (!stbi__mad2sizes_valid(img_width_bytes, y, img_width_bytes)) return stbi__err("too large", "Corrupt PNG");
|
||||||
img_len = (img_width_bytes + 1) * y;
|
img_len = (img_width_bytes + 1) * y;
|
||||||
|
|
||||||
// we used to check for exact match between raw_len and img_len on non-interlaced PNGs,
|
// we used to check for exact match between raw_len and img_len on non-interlaced PNGs,
|
||||||
|
@ -4673,189 +4724,137 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r
|
||||||
// so just check for raw_len < img_len always.
|
// so just check for raw_len < img_len always.
|
||||||
if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG");
|
if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG");
|
||||||
|
|
||||||
|
// Allocate two scan lines worth of filter workspace buffer.
|
||||||
|
filter_buf = (stbi_uc *) stbi__malloc_mad2(img_width_bytes, 2, 0);
|
||||||
|
if (!filter_buf) return stbi__err("outofmem", "Out of memory");
|
||||||
|
|
||||||
|
// Filtering for low-bit-depth images
|
||||||
|
if (depth < 8) {
|
||||||
|
filter_bytes = 1;
|
||||||
|
width = img_width_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
for (j=0; j < y; ++j) {
|
for (j=0; j < y; ++j) {
|
||||||
stbi_uc *cur = a->out + stride*j;
|
// cur/prior filter buffers alternate
|
||||||
stbi_uc *prior;
|
stbi_uc *cur = filter_buf + (j & 1)*img_width_bytes;
|
||||||
|
stbi_uc *prior = filter_buf + (~j & 1)*img_width_bytes;
|
||||||
|
stbi_uc *dest = a->out + stride*j;
|
||||||
|
int nk = width * filter_bytes;
|
||||||
int filter = *raw++;
|
int filter = *raw++;
|
||||||
|
|
||||||
if (filter > 4)
|
// check filter type
|
||||||
return stbi__err("invalid filter","Corrupt PNG");
|
if (filter > 4) {
|
||||||
|
all_ok = stbi__err("invalid filter","Corrupt PNG");
|
||||||
if (depth < 8) {
|
break;
|
||||||
if (img_width_bytes > x) return stbi__err("invalid width","Corrupt PNG");
|
|
||||||
cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place
|
|
||||||
filter_bytes = 1;
|
|
||||||
width = img_width_bytes;
|
|
||||||
}
|
}
|
||||||
prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above
|
|
||||||
|
|
||||||
// if first row, use special filter that doesn't sample previous row
|
// if first row, use special filter that doesn't sample previous row
|
||||||
if (j == 0) filter = first_row_filter[filter];
|
if (j == 0) filter = first_row_filter[filter];
|
||||||
|
|
||||||
// handle first byte explicitly
|
// perform actual filtering
|
||||||
for (k=0; k < filter_bytes; ++k) {
|
switch (filter) {
|
||||||
switch (filter) {
|
case STBI__F_none:
|
||||||
case STBI__F_none : cur[k] = raw[k]; break;
|
memcpy(cur, raw, nk);
|
||||||
case STBI__F_sub : cur[k] = raw[k]; break;
|
break;
|
||||||
case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break;
|
case STBI__F_sub:
|
||||||
case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break;
|
memcpy(cur, raw, filter_bytes);
|
||||||
case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break;
|
for (k = filter_bytes; k < nk; ++k)
|
||||||
case STBI__F_avg_first : cur[k] = raw[k]; break;
|
cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]);
|
||||||
case STBI__F_paeth_first: cur[k] = raw[k]; break;
|
break;
|
||||||
}
|
case STBI__F_up:
|
||||||
|
for (k = 0; k < nk; ++k)
|
||||||
|
cur[k] = STBI__BYTECAST(raw[k] + prior[k]);
|
||||||
|
break;
|
||||||
|
case STBI__F_avg:
|
||||||
|
for (k = 0; k < filter_bytes; ++k)
|
||||||
|
cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1));
|
||||||
|
for (k = filter_bytes; k < nk; ++k)
|
||||||
|
cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1));
|
||||||
|
break;
|
||||||
|
case STBI__F_paeth:
|
||||||
|
for (k = 0; k < filter_bytes; ++k)
|
||||||
|
cur[k] = STBI__BYTECAST(raw[k] + prior[k]); // prior[k] == stbi__paeth(0,prior[k],0)
|
||||||
|
for (k = filter_bytes; k < nk; ++k)
|
||||||
|
cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes], prior[k], prior[k-filter_bytes]));
|
||||||
|
break;
|
||||||
|
case STBI__F_avg_first:
|
||||||
|
memcpy(cur, raw, filter_bytes);
|
||||||
|
for (k = filter_bytes; k < nk; ++k)
|
||||||
|
cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (depth == 8) {
|
raw += nk;
|
||||||
if (img_n != out_n)
|
|
||||||
cur[img_n] = 255; // first pixel
|
|
||||||
raw += img_n;
|
|
||||||
cur += out_n;
|
|
||||||
prior += out_n;
|
|
||||||
} else if (depth == 16) {
|
|
||||||
if (img_n != out_n) {
|
|
||||||
cur[filter_bytes] = 255; // first pixel top byte
|
|
||||||
cur[filter_bytes+1] = 255; // first pixel bottom byte
|
|
||||||
}
|
|
||||||
raw += filter_bytes;
|
|
||||||
cur += output_bytes;
|
|
||||||
prior += output_bytes;
|
|
||||||
} else {
|
|
||||||
raw += 1;
|
|
||||||
cur += 1;
|
|
||||||
prior += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is a little gross, so that we don't switch per-pixel or per-component
|
// expand decoded bits in cur to dest, also adding an extra alpha channel if desired
|
||||||
if (depth < 8 || img_n == out_n) {
|
if (depth < 8) {
|
||||||
int nk = (width - 1)*filter_bytes;
|
|
||||||
#define STBI__CASE(f) \
|
|
||||||
case f: \
|
|
||||||
for (k=0; k < nk; ++k)
|
|
||||||
switch (filter) {
|
|
||||||
// "none" filter turns into a memcpy here; make that explicit.
|
|
||||||
case STBI__F_none: memcpy(cur, raw, nk); break;
|
|
||||||
STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break;
|
|
||||||
STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break;
|
|
||||||
STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break;
|
|
||||||
STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break;
|
|
||||||
STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break;
|
|
||||||
STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break;
|
|
||||||
}
|
|
||||||
#undef STBI__CASE
|
|
||||||
raw += nk;
|
|
||||||
} else {
|
|
||||||
STBI_ASSERT(img_n+1 == out_n);
|
|
||||||
#define STBI__CASE(f) \
|
|
||||||
case f: \
|
|
||||||
for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \
|
|
||||||
for (k=0; k < filter_bytes; ++k)
|
|
||||||
switch (filter) {
|
|
||||||
STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break;
|
|
||||||
STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break;
|
|
||||||
STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break;
|
|
||||||
STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break;
|
|
||||||
STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break;
|
|
||||||
STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break;
|
|
||||||
STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break;
|
|
||||||
}
|
|
||||||
#undef STBI__CASE
|
|
||||||
|
|
||||||
// the loop above sets the high byte of the pixels' alpha, but for
|
|
||||||
// 16 bit png files we also need the low byte set. we'll do that here.
|
|
||||||
if (depth == 16) {
|
|
||||||
cur = a->out + stride*j; // start at the beginning of the row again
|
|
||||||
for (i=0; i < x; ++i,cur+=output_bytes) {
|
|
||||||
cur[filter_bytes+1] = 255;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// we make a separate pass to expand bits to pixels; for performance,
|
|
||||||
// this could run two scanlines behind the above code, so it won't
|
|
||||||
// intefere with filtering but will still be in the cache.
|
|
||||||
if (depth < 8) {
|
|
||||||
for (j=0; j < y; ++j) {
|
|
||||||
stbi_uc *cur = a->out + stride*j;
|
|
||||||
stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes;
|
|
||||||
// unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit
|
|
||||||
// png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop
|
|
||||||
stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range
|
stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range
|
||||||
|
stbi_uc *in = cur;
|
||||||
|
stbi_uc *out = dest;
|
||||||
|
stbi_uc inb = 0;
|
||||||
|
stbi__uint32 nsmp = x*img_n;
|
||||||
|
|
||||||
// note that the final byte might overshoot and write more data than desired.
|
// expand bits to bytes first
|
||||||
// we can allocate enough data that this never writes out of memory, but it
|
|
||||||
// could also overwrite the next scanline. can it overwrite non-empty data
|
|
||||||
// on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel.
|
|
||||||
// so we need to explicitly clamp the final ones
|
|
||||||
|
|
||||||
if (depth == 4) {
|
if (depth == 4) {
|
||||||
for (k=x*img_n; k >= 2; k-=2, ++in) {
|
for (i=0; i < nsmp; ++i) {
|
||||||
*cur++ = scale * ((*in >> 4) );
|
if ((i & 1) == 0) inb = *in++;
|
||||||
*cur++ = scale * ((*in ) & 0x0f);
|
*out++ = scale * (inb >> 4);
|
||||||
|
inb <<= 4;
|
||||||
}
|
}
|
||||||
if (k > 0) *cur++ = scale * ((*in >> 4) );
|
|
||||||
} else if (depth == 2) {
|
} else if (depth == 2) {
|
||||||
for (k=x*img_n; k >= 4; k-=4, ++in) {
|
for (i=0; i < nsmp; ++i) {
|
||||||
*cur++ = scale * ((*in >> 6) );
|
if ((i & 3) == 0) inb = *in++;
|
||||||
*cur++ = scale * ((*in >> 4) & 0x03);
|
*out++ = scale * (inb >> 6);
|
||||||
*cur++ = scale * ((*in >> 2) & 0x03);
|
inb <<= 2;
|
||||||
*cur++ = scale * ((*in ) & 0x03);
|
|
||||||
}
|
}
|
||||||
if (k > 0) *cur++ = scale * ((*in >> 6) );
|
} else {
|
||||||
if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03);
|
STBI_ASSERT(depth == 1);
|
||||||
if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03);
|
for (i=0; i < nsmp; ++i) {
|
||||||
} else if (depth == 1) {
|
if ((i & 7) == 0) inb = *in++;
|
||||||
for (k=x*img_n; k >= 8; k-=8, ++in) {
|
*out++ = scale * (inb >> 7);
|
||||||
*cur++ = scale * ((*in >> 7) );
|
inb <<= 1;
|
||||||
*cur++ = scale * ((*in >> 6) & 0x01);
|
|
||||||
*cur++ = scale * ((*in >> 5) & 0x01);
|
|
||||||
*cur++ = scale * ((*in >> 4) & 0x01);
|
|
||||||
*cur++ = scale * ((*in >> 3) & 0x01);
|
|
||||||
*cur++ = scale * ((*in >> 2) & 0x01);
|
|
||||||
*cur++ = scale * ((*in >> 1) & 0x01);
|
|
||||||
*cur++ = scale * ((*in ) & 0x01);
|
|
||||||
}
|
}
|
||||||
if (k > 0) *cur++ = scale * ((*in >> 7) );
|
|
||||||
if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01);
|
|
||||||
if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01);
|
|
||||||
if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01);
|
|
||||||
if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01);
|
|
||||||
if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01);
|
|
||||||
if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01);
|
|
||||||
}
|
}
|
||||||
if (img_n != out_n) {
|
|
||||||
int q;
|
// insert alpha=255 values if desired
|
||||||
// insert alpha = 255
|
if (img_n != out_n)
|
||||||
cur = a->out + stride*j;
|
stbi__create_png_alpha_expand8(dest, dest, x, img_n);
|
||||||
|
} else if (depth == 8) {
|
||||||
|
if (img_n == out_n)
|
||||||
|
memcpy(dest, cur, x*img_n);
|
||||||
|
else
|
||||||
|
stbi__create_png_alpha_expand8(dest, cur, x, img_n);
|
||||||
|
} else if (depth == 16) {
|
||||||
|
// convert the image data from big-endian to platform-native
|
||||||
|
stbi__uint16 *dest16 = (stbi__uint16*)dest;
|
||||||
|
stbi__uint32 nsmp = x*img_n;
|
||||||
|
|
||||||
|
if (img_n == out_n) {
|
||||||
|
for (i = 0; i < nsmp; ++i, ++dest16, cur += 2)
|
||||||
|
*dest16 = (cur[0] << 8) | cur[1];
|
||||||
|
} else {
|
||||||
|
STBI_ASSERT(img_n+1 == out_n);
|
||||||
if (img_n == 1) {
|
if (img_n == 1) {
|
||||||
for (q=x-1; q >= 0; --q) {
|
for (i = 0; i < x; ++i, dest16 += 2, cur += 2) {
|
||||||
cur[q*2+1] = 255;
|
dest16[0] = (cur[0] << 8) | cur[1];
|
||||||
cur[q*2+0] = cur[q];
|
dest16[1] = 0xffff;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
STBI_ASSERT(img_n == 3);
|
STBI_ASSERT(img_n == 3);
|
||||||
for (q=x-1; q >= 0; --q) {
|
for (i = 0; i < x; ++i, dest16 += 4, cur += 6) {
|
||||||
cur[q*4+3] = 255;
|
dest16[0] = (cur[0] << 8) | cur[1];
|
||||||
cur[q*4+2] = cur[q*3+2];
|
dest16[1] = (cur[2] << 8) | cur[3];
|
||||||
cur[q*4+1] = cur[q*3+1];
|
dest16[2] = (cur[4] << 8) | cur[5];
|
||||||
cur[q*4+0] = cur[q*3+0];
|
dest16[3] = 0xffff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (depth == 16) {
|
|
||||||
// force the image data from big-endian to platform-native.
|
|
||||||
// this is done in a separate pass due to the decoding relying
|
|
||||||
// on the data being untouched, but could probably be done
|
|
||||||
// per-line during decode if care is taken.
|
|
||||||
stbi_uc *cur = a->out;
|
|
||||||
stbi__uint16 *cur16 = (stbi__uint16*)cur;
|
|
||||||
|
|
||||||
for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) {
|
|
||||||
*cur16 = (cur[0] << 8) | cur[1];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
STBI_FREE(filter_buf);
|
||||||
|
if (!all_ok) return 0;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5161,9 +5160,11 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
|
||||||
// non-paletted with tRNS = constant alpha. if header-scanning, we can stop now.
|
// non-paletted with tRNS = constant alpha. if header-scanning, we can stop now.
|
||||||
if (scan == STBI__SCAN_header) { ++s->img_n; return 1; }
|
if (scan == STBI__SCAN_header) { ++s->img_n; return 1; }
|
||||||
if (z->depth == 16) {
|
if (z->depth == 16) {
|
||||||
for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is
|
for (k = 0; k < s->img_n && k < 3; ++k) // extra loop test to suppress false GCC warning
|
||||||
|
tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is
|
||||||
} else {
|
} else {
|
||||||
for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger
|
for (k = 0; k < s->img_n && k < 3; ++k)
|
||||||
|
tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
1784
raylib/external/stb_image_resize2.h
vendored
1784
raylib/external/stb_image_resize2.h
vendored
File diff suppressed because it is too large
Load diff
14
raylib/external/stb_truetype.h
vendored
14
raylib/external/stb_truetype.h
vendored
|
@ -54,7 +54,7 @@
|
||||||
// Hou Qiming Derek Vinyard
|
// Hou Qiming Derek Vinyard
|
||||||
// Rob Loach Cort Stratton
|
// Rob Loach Cort Stratton
|
||||||
// Kenney Phillis Jr. Brian Costabile
|
// Kenney Phillis Jr. Brian Costabile
|
||||||
// Ken Voskuil (kaesve)
|
// Ken Voskuil (kaesve) Yakov Galka
|
||||||
//
|
//
|
||||||
// VERSION HISTORY
|
// VERSION HISTORY
|
||||||
//
|
//
|
||||||
|
@ -4604,6 +4604,8 @@ STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float sc
|
||||||
scale_y = -scale_y;
|
scale_y = -scale_y;
|
||||||
|
|
||||||
{
|
{
|
||||||
|
// distance from singular values (in the same units as the pixel grid)
|
||||||
|
const float eps = 1./1024, eps2 = eps*eps;
|
||||||
int x,y,i,j;
|
int x,y,i,j;
|
||||||
float *precompute;
|
float *precompute;
|
||||||
stbtt_vertex *verts;
|
stbtt_vertex *verts;
|
||||||
|
@ -4616,15 +4618,15 @@ STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float sc
|
||||||
float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y;
|
float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y;
|
||||||
float x1 = verts[j].x*scale_x, y1 = verts[j].y*scale_y;
|
float x1 = verts[j].x*scale_x, y1 = verts[j].y*scale_y;
|
||||||
float dist = (float) STBTT_sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0));
|
float dist = (float) STBTT_sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0));
|
||||||
precompute[i] = (dist == 0) ? 0.0f : 1.0f / dist;
|
precompute[i] = (dist < eps) ? 0.0f : 1.0f / dist;
|
||||||
} else if (verts[i].type == STBTT_vcurve) {
|
} else if (verts[i].type == STBTT_vcurve) {
|
||||||
float x2 = verts[j].x *scale_x, y2 = verts[j].y *scale_y;
|
float x2 = verts[j].x *scale_x, y2 = verts[j].y *scale_y;
|
||||||
float x1 = verts[i].cx*scale_x, y1 = verts[i].cy*scale_y;
|
float x1 = verts[i].cx*scale_x, y1 = verts[i].cy*scale_y;
|
||||||
float x0 = verts[i].x *scale_x, y0 = verts[i].y *scale_y;
|
float x0 = verts[i].x *scale_x, y0 = verts[i].y *scale_y;
|
||||||
float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2;
|
float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2;
|
||||||
float len2 = bx*bx + by*by;
|
float len2 = bx*bx + by*by;
|
||||||
if (len2 != 0.0f)
|
if (len2 >= eps2)
|
||||||
precompute[i] = 1.0f / (bx*bx + by*by);
|
precompute[i] = 1.0f / len2;
|
||||||
else
|
else
|
||||||
precompute[i] = 0.0f;
|
precompute[i] = 0.0f;
|
||||||
} else
|
} else
|
||||||
|
@ -4689,8 +4691,8 @@ STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float sc
|
||||||
float a = 3*(ax*bx + ay*by);
|
float a = 3*(ax*bx + ay*by);
|
||||||
float b = 2*(ax*ax + ay*ay) + (mx*bx+my*by);
|
float b = 2*(ax*ax + ay*ay) + (mx*bx+my*by);
|
||||||
float c = mx*ax+my*ay;
|
float c = mx*ax+my*ay;
|
||||||
if (a == 0.0) { // if a is 0, it's linear
|
if (STBTT_fabs(a) < eps2) { // if a is 0, it's linear
|
||||||
if (b != 0.0) {
|
if (STBTT_fabs(b) >= eps2) {
|
||||||
res[num++] = -c/b;
|
res[num++] = -c/b;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
374
raylib/external/win32_clipboard.h
vendored
Normal file
374
raylib/external/win32_clipboard.h
vendored
Normal file
|
@ -0,0 +1,374 @@
|
||||||
|
#if !defined(_WIN32)
|
||||||
|
# error "This module is only made for Windows OS"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef WIN32_CLIPBOARD_
|
||||||
|
#define WIN32_CLIPBOARD_
|
||||||
|
unsigned char* Win32GetClipboardImageData(int* width, int* height, unsigned long long int *dataSize);
|
||||||
|
#endif // WIN32_CLIPBOARD_
|
||||||
|
|
||||||
|
#ifdef WIN32_CLIPBOARD_IMPLEMENTATION
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
// NOTE: These search for architecture is taken from "Windows.h", and it's necessary if we really don't wanna import windows.h
|
||||||
|
// and still make it compile on msvc, because import indirectly importing "winnt.h" (e.g. <minwindef.h>) can cause problems is these are not defined.
|
||||||
|
#if !defined(_X86_) && !defined(_68K_) && !defined(_MPPC_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && !defined(_ARM64EC_) && defined(_M_IX86)
|
||||||
|
#define _X86_
|
||||||
|
#if !defined(_CHPE_X86_ARM64_) && defined(_M_HYBRID)
|
||||||
|
#define _CHPE_X86_ARM64_
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(_AMD64_) && !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && (defined(_M_AMD64) || defined(_M_ARM64EC))
|
||||||
|
#define _AMD64_
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(_ARM_) && !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM64_) && !defined(_ARM64EC_) && defined(_M_ARM)
|
||||||
|
#define _ARM_
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(_ARM64_) && !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64EC_) && defined(_M_ARM64)
|
||||||
|
#define _ARM64_
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_ARM_) && !defined(_ARM64_) && !defined(_ARM64EC_) && defined(_M_ARM64EC)
|
||||||
|
#define _ARM64EC_
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && !defined(_ARM64EC_) && defined(_M_M68K)
|
||||||
|
#define _68K_
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && !defined(_ARM64EC_) && defined(_M_MPPC)
|
||||||
|
#define _MPPC_
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(_IA64_) && !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_M_IX86) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && !defined(_ARM64EC_) && defined(_M_IA64)
|
||||||
|
#define _IA64_
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
// #include <sdkddkver.h>
|
||||||
|
// #include <windows.h>
|
||||||
|
// #include <winuser.h>
|
||||||
|
#include <minwindef.h>
|
||||||
|
// #include <minwinbase.h>
|
||||||
|
|
||||||
|
#ifndef WINAPI
|
||||||
|
#if defined(_ARM_)
|
||||||
|
#define WINAPI
|
||||||
|
#else
|
||||||
|
#define WINAPI __stdcall
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef WINAPI
|
||||||
|
#if defined(_ARM_)
|
||||||
|
#define WINAPI
|
||||||
|
#else
|
||||||
|
#define WINAPI __stdcall
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef WINBASEAPI
|
||||||
|
#ifndef _KERNEL32_
|
||||||
|
#define WINBASEAPI DECLSPEC_IMPORT
|
||||||
|
#else
|
||||||
|
#define WINBASEAPI
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef WINUSERAPI
|
||||||
|
#ifndef _USER32_
|
||||||
|
#define WINUSERAPI __declspec (dllimport)
|
||||||
|
#else
|
||||||
|
#define WINUSERAPI
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef int WINBOOL;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// typedef HANDLE HGLOBAL;
|
||||||
|
|
||||||
|
#ifndef HWND
|
||||||
|
#define HWND void*
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if !defined(_WINUSER_) || !defined(WINUSER_ALREADY_INCLUDED)
|
||||||
|
WINUSERAPI WINBOOL WINAPI OpenClipboard(HWND hWndNewOwner);
|
||||||
|
WINUSERAPI WINBOOL WINAPI CloseClipboard(VOID);
|
||||||
|
WINUSERAPI DWORD WINAPI GetClipboardSequenceNumber(VOID);
|
||||||
|
WINUSERAPI HWND WINAPI GetClipboardOwner(VOID);
|
||||||
|
WINUSERAPI HWND WINAPI SetClipboardViewer(HWND hWndNewViewer);
|
||||||
|
WINUSERAPI HWND WINAPI GetClipboardViewer(VOID);
|
||||||
|
WINUSERAPI WINBOOL WINAPI ChangeClipboardChain(HWND hWndRemove, HWND hWndNewNext);
|
||||||
|
WINUSERAPI HANDLE WINAPI SetClipboardData(UINT uFormat, HANDLE hMem);
|
||||||
|
WINUSERAPI HANDLE WINAPI GetClipboardData(UINT uFormat);
|
||||||
|
WINUSERAPI UINT WINAPI RegisterClipboardFormatA(LPCSTR lpszFormat);
|
||||||
|
WINUSERAPI UINT WINAPI RegisterClipboardFormatW(LPCWSTR lpszFormat);
|
||||||
|
WINUSERAPI int WINAPI CountClipboardFormats(VOID);
|
||||||
|
WINUSERAPI UINT WINAPI EnumClipboardFormats(UINT format);
|
||||||
|
WINUSERAPI int WINAPI GetClipboardFormatNameA(UINT format, LPSTR lpszFormatName, int cchMaxCount);
|
||||||
|
WINUSERAPI int WINAPI GetClipboardFormatNameW(UINT format, LPWSTR lpszFormatName, int cchMaxCount);
|
||||||
|
WINUSERAPI WINBOOL WINAPI EmptyClipboard(VOID);
|
||||||
|
WINUSERAPI WINBOOL WINAPI IsClipboardFormatAvailable(UINT format);
|
||||||
|
WINUSERAPI int WINAPI GetPriorityClipboardFormat(UINT *paFormatPriorityList, int cFormats);
|
||||||
|
WINUSERAPI HWND WINAPI GetOpenClipboardWindow(VOID);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef HGLOBAL
|
||||||
|
#define HGLOBAL void*
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(_WINBASE_) || !defined(WINBASE_ALREADY_INCLUDED)
|
||||||
|
WINBASEAPI SIZE_T WINAPI GlobalSize (HGLOBAL hMem);
|
||||||
|
WINBASEAPI LPVOID WINAPI GlobalLock (HGLOBAL hMem);
|
||||||
|
WINBASEAPI WINBOOL WINAPI GlobalUnlock (HGLOBAL hMem);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if !defined(_WINGDI_) || !defined(WINGDI_ALREADY_INCLUDED)
|
||||||
|
#ifndef BITMAPINFOHEADER_ALREADY_DEFINED
|
||||||
|
#define BITMAPINFOHEADER_ALREADY_DEFINED
|
||||||
|
// Does this header need to be packed ? by the windowps header it doesnt seem to be
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
typedef struct tagBITMAPINFOHEADER {
|
||||||
|
DWORD biSize;
|
||||||
|
LONG biWidth;
|
||||||
|
LONG biHeight;
|
||||||
|
WORD biPlanes;
|
||||||
|
WORD biBitCount;
|
||||||
|
DWORD biCompression;
|
||||||
|
DWORD biSizeImage;
|
||||||
|
LONG biXPelsPerMeter;
|
||||||
|
LONG biYPelsPerMeter;
|
||||||
|
DWORD biClrUsed;
|
||||||
|
DWORD biClrImportant;
|
||||||
|
} BITMAPINFOHEADER,*LPBITMAPINFOHEADER,*PBITMAPINFOHEADER;
|
||||||
|
#pragma pack(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BITMAPFILEHEADER_ALREADY_DEFINED
|
||||||
|
#define BITMAPFILEHEADER_ALREADY_DEFINED
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
typedef struct tagBITMAPFILEHEADER {
|
||||||
|
WORD bfType;
|
||||||
|
DWORD bfSize;
|
||||||
|
WORD bfReserved1;
|
||||||
|
WORD bfReserved2;
|
||||||
|
DWORD bfOffBits;
|
||||||
|
} BITMAPFILEHEADER,*LPBITMAPFILEHEADER,*PBITMAPFILEHEADER;
|
||||||
|
#pragma pack(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef RGBQUAD_ALREADY_DEFINED
|
||||||
|
#define RGBQUAD_ALREADY_DEFINED
|
||||||
|
typedef struct tagRGBQUAD {
|
||||||
|
BYTE rgbBlue;
|
||||||
|
BYTE rgbGreen;
|
||||||
|
BYTE rgbRed;
|
||||||
|
BYTE rgbReserved;
|
||||||
|
} RGBQUAD, *LPRGBQUAD;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-wmf/4e588f70-bd92-4a6f-b77f-35d0feaf7a57
|
||||||
|
#define BI_RGB 0x0000
|
||||||
|
#define BI_RLE8 0x0001
|
||||||
|
#define BI_RLE4 0x0002
|
||||||
|
#define BI_BITFIELDS 0x0003
|
||||||
|
#define BI_JPEG 0x0004
|
||||||
|
#define BI_PNG 0x0005
|
||||||
|
#define BI_CMYK 0x000B
|
||||||
|
#define BI_CMYKRLE8 0x000C
|
||||||
|
#define BI_CMYKRLE4 0x000D
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/dataxchg/standard-clipboard-formats
|
||||||
|
#define CF_DIB 8
|
||||||
|
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setsystemcursor
|
||||||
|
// #define OCR_NORMAL 32512 // Normal select
|
||||||
|
// #define OCR_IBEAM 32513 // Text select
|
||||||
|
// #define OCR_WAIT 32514 // Busy
|
||||||
|
// #define OCR_CROSS 32515 // Precision select
|
||||||
|
// #define OCR_UP 32516 // Alternate select
|
||||||
|
// #define OCR_SIZENWSE 32642 // Diagonal resize 1
|
||||||
|
// #define OCR_SIZENESW 32643 // Diagonal resize 2
|
||||||
|
// #define OCR_SIZEWE 32644 // Horizontal resize
|
||||||
|
// #define OCR_SIZENS 32645 // Vertical resize
|
||||||
|
// #define OCR_SIZEALL 32646 // Move
|
||||||
|
// #define OCR_NO 32648 // Unavailable
|
||||||
|
// #define OCR_HAND 32649 // Link select
|
||||||
|
// #define OCR_APPSTARTING 32650 //
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
// Module Internal Functions Declaration
|
||||||
|
//----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
static BOOL OpenClipboardRetrying(HWND handle); // Open clipboard with a number of retries
|
||||||
|
static int GetPixelDataOffset(BITMAPINFOHEADER bih);
|
||||||
|
|
||||||
|
unsigned char* Win32GetClipboardImageData(int* width, int* height, unsigned long long int *dataSize)
|
||||||
|
{
|
||||||
|
HWND win = NULL; // Get from somewhere but is doesnt seem to matter
|
||||||
|
const char* msgString = "";
|
||||||
|
int severity = LOG_INFO;
|
||||||
|
BYTE* bmpData = NULL;
|
||||||
|
if (!OpenClipboardRetrying(win)) {
|
||||||
|
severity = LOG_ERROR;
|
||||||
|
msgString = "Couldn't open clipboard";
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
HGLOBAL clipHandle = (HGLOBAL)GetClipboardData(CF_DIB);
|
||||||
|
if (!clipHandle) {
|
||||||
|
severity = LOG_ERROR;
|
||||||
|
msgString = "Clipboard data is not an Image";
|
||||||
|
goto close;
|
||||||
|
}
|
||||||
|
|
||||||
|
BITMAPINFOHEADER *bmpInfoHeader = (BITMAPINFOHEADER *)GlobalLock(clipHandle);
|
||||||
|
if (!bmpInfoHeader) {
|
||||||
|
// Mapping from HGLOBAL to our local *address space* failed
|
||||||
|
severity = LOG_ERROR;
|
||||||
|
msgString = "Clipboard data failed to be locked";
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
*width = bmpInfoHeader->biWidth;
|
||||||
|
*height = bmpInfoHeader->biHeight;
|
||||||
|
|
||||||
|
SIZE_T clipDataSize = GlobalSize(clipHandle);
|
||||||
|
if (clipDataSize < sizeof(BITMAPINFOHEADER)) {
|
||||||
|
// Format CF_DIB needs space for BITMAPINFOHEADER struct.
|
||||||
|
msgString = "Clipboard has Malformed data";
|
||||||
|
severity = LOG_ERROR;
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Denotes where the pixel data starts from the bmpInfoHeader pointer
|
||||||
|
int pixelOffset = GetPixelDataOffset(*bmpInfoHeader);
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------//
|
||||||
|
//
|
||||||
|
// The rest of the section is about create the bytes for a correct BMP file
|
||||||
|
// Then we copy the data and to a pointer
|
||||||
|
//
|
||||||
|
//--------------------------------------------------------------------------------//
|
||||||
|
|
||||||
|
BITMAPFILEHEADER bmpFileHeader = {0};
|
||||||
|
SIZE_T bmpFileSize = sizeof(bmpFileHeader) + clipDataSize;
|
||||||
|
*dataSize = bmpFileSize;
|
||||||
|
|
||||||
|
bmpFileHeader.bfType = 0x4D42; //https://stackoverflow.com/questions/601430/multibyte-character-constants-and-bitmap-file-header-type-constants#601536
|
||||||
|
|
||||||
|
bmpFileHeader.bfSize = (DWORD)bmpFileSize; // Up to 4GB works fine
|
||||||
|
bmpFileHeader.bfOffBits = sizeof(bmpFileHeader) + pixelOffset;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Each process has a default heap provided by the system
|
||||||
|
// Memory objects allocated by GlobalAlloc and LocalAlloc are in private,
|
||||||
|
// committed pages with read/write access that cannot be accessed by other processes.
|
||||||
|
//
|
||||||
|
// This may be wrong since we might be allocating in a DLL and freeing from another module, the main application
|
||||||
|
// that may cause heap corruption. We could create a FreeImage function
|
||||||
|
//
|
||||||
|
bmpData = malloc(sizeof(bmpFileHeader) + clipDataSize);
|
||||||
|
// First we add the header for a bmp file
|
||||||
|
memcpy(bmpData, &bmpFileHeader, sizeof(bmpFileHeader));
|
||||||
|
// Then we add the header for the bmp itself + the pixel data
|
||||||
|
memcpy(bmpData + sizeof(bmpFileHeader), bmpInfoHeader, clipDataSize);
|
||||||
|
msgString = "Clipboad image acquired successfully";
|
||||||
|
|
||||||
|
|
||||||
|
unlock:
|
||||||
|
GlobalUnlock(clipHandle);
|
||||||
|
close:
|
||||||
|
CloseClipboard();
|
||||||
|
end:
|
||||||
|
|
||||||
|
TRACELOG(severity, msgString);
|
||||||
|
return bmpData;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BOOL OpenClipboardRetrying(HWND hWnd)
|
||||||
|
{
|
||||||
|
static const int maxTries = 20;
|
||||||
|
static const int sleepTimeMS = 60;
|
||||||
|
for (int _ = 0; _ < maxTries; ++_)
|
||||||
|
{
|
||||||
|
// Might be being hold by another process
|
||||||
|
// Or yourself forgot to CloseClipboard
|
||||||
|
if (OpenClipboard(hWnd)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Sleep(sleepTimeMS);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Based off of researching microsoft docs and reponses from this question https://stackoverflow.com/questions/30552255/how-to-read-a-bitmap-from-the-windows-clipboard#30552856
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
|
||||||
|
// Get the byte offset where does the pixels data start (from a packed DIB)
|
||||||
|
static int GetPixelDataOffset(BITMAPINFOHEADER bih)
|
||||||
|
{
|
||||||
|
int offset = 0;
|
||||||
|
const unsigned int rgbaSize = sizeof(RGBQUAD);
|
||||||
|
|
||||||
|
// biSize Specifies the number of bytes required by the structure
|
||||||
|
// We expect to always be 40 because it should be packed
|
||||||
|
if (40 == bih.biSize && 40 == sizeof(BITMAPINFOHEADER))
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// biBitCount Specifies the number of bits per pixel.
|
||||||
|
// Might exist some bit masks *after* the header and *before* the pixel offset
|
||||||
|
// we're looking, but only if we have more than
|
||||||
|
// 8 bits per pixel, so we need to ajust for that
|
||||||
|
//
|
||||||
|
if (bih.biBitCount > 8)
|
||||||
|
{
|
||||||
|
// if bih.biCompression is RBG we should NOT offset more
|
||||||
|
|
||||||
|
if (bih.biCompression == BI_BITFIELDS)
|
||||||
|
{
|
||||||
|
offset += 3 * rgbaSize;
|
||||||
|
} else if (bih.biCompression == 6 /* BI_ALPHABITFIELDS */)
|
||||||
|
{
|
||||||
|
// Not widely supported, but valid.
|
||||||
|
offset += 4 * rgbaSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// biClrUsed Specifies the number of color indices in the color table that are actually used by the bitmap.
|
||||||
|
// If this value is zero, the bitmap uses the maximum number of colors
|
||||||
|
// corresponding to the value of the biBitCount member for the compression mode specified by biCompression.
|
||||||
|
// If biClrUsed is nonzero and the biBitCount member is less than 16
|
||||||
|
// the biClrUsed member specifies the actual number of colors
|
||||||
|
//
|
||||||
|
if (bih.biClrUsed > 0) {
|
||||||
|
offset += bih.biClrUsed * rgbaSize;
|
||||||
|
} else {
|
||||||
|
if (bih.biBitCount < 16)
|
||||||
|
{
|
||||||
|
offset = offset + (rgbaSize << bih.biBitCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bih.biSize + offset;
|
||||||
|
}
|
||||||
|
#endif // WIN32_CLIPBOARD_IMPLEMENTATION
|
||||||
|
// EOF
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue