summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--projects/README.md2
-rw-r--r--src/core.c43
-rw-r--r--src/external/mini_al.c1
-rw-r--r--src/external/mini_al.h473
-rw-r--r--src/raylib.h2
-rw-r--r--src/shapes.c25
-rw-r--r--src/textures.c8
7 files changed, 408 insertions, 146 deletions
diff --git a/projects/README.md b/projects/README.md
index 6cb5798c..4009a09b 100644
--- a/projects/README.md
+++ b/projects/README.md
@@ -12,6 +12,6 @@ IDE | Platform | Template type | State
[Notepad++](https://notepad-plus-plus.org/) | Windows | source/example compiling | DONE
[VS2015](https://www.visualstudio.com) | Windows | source/example compiling | DONE
[VS2017](https://www.visualstudio.com) | Windows | source/example compiling | DONE
-[VSCode](https://code.visualstudio.com/) | Windows, macOS | - | INCOMPLETE
+[VSCode](https://code.visualstudio.com/) | Windows, macOS | example compiling | DONE
*New IDEs config files are welcome!*
diff --git a/src/core.c b/src/core.c
index 7d64ae27..0c04aecd 100644
--- a/src/core.c
+++ b/src/core.c
@@ -763,18 +763,21 @@ void ToggleFullscreen(void)
void SetWindowIcon(Image image)
{
#if defined(PLATFORM_DESKTOP)
- ImageFormat(&image, UNCOMPRESSED_R8G8B8A8);
+ Image imicon = ImageCopy(image);
+ ImageFormat(&imicon, UNCOMPRESSED_R8G8B8A8);
- GLFWimage icon[1];
+ GLFWimage icon[1] = { 0 };
- icon[0].width = image.width;
- icon[0].height = image.height;
- icon[0].pixels = (unsigned char *)image.data;
+ icon[0].width = imicon.width;
+ icon[0].height = imicon.height;
+ icon[0].pixels = (unsigned char *)imicon.data;
- // NOTE: We only support one image icon
+ // NOTE 1: We only support one image icon
+ // NOTE 2: The specified image data is copied before this function returns
glfwSetWindowIcon(window, 1, icon);
// TODO: Support multi-image icons --> image.mipmaps
+ UnloadImage(imicon);
#endif
}
@@ -1821,6 +1824,34 @@ int StorageLoadValue(int position)
return value;
}
+// Open URL with default system browser (if available)
+// NOTE: This function is onlyl safe to use if you control the URL given.
+// A user could craft a malicious string performing another action.
+// Only call this function yourself not with user input or make sure to check the string yourself.
+// CHECK: https://github.com/raysan5/raylib/issues/686
+void OpenURL(const char *url)
+{
+ // Small security check trying to avoid (partially) malicious code...
+ // sorry for the inconvenience when you hit this point...
+ if (strchr(url, '\'') != NULL)
+ {
+ TraceLog(LOG_WARNING, "Provided URL does not seem to be valid.");
+ } else {
+ char *cmd = calloc(strlen(url) + 10, sizeof(char));
+
+#if defined(_WIN32)
+ sprintf(cmd, "explorer '%s'", url);
+#elif defined(__linux__)
+ sprintf(cmd, "xdg-open '%s'", url); // Alternatives: firefox, x-www-browser
+#elif defined(__APPLE__)
+ sprintf(cmd, "open '%s'", url);
+#endif
+ system(cmd);
+
+ free(cmd);
+ }
+}
+
//----------------------------------------------------------------------------------
// Module Functions Definition - Input (Keyboard, Mouse, Gamepad) Functions
//----------------------------------------------------------------------------------
diff --git a/src/external/mini_al.c b/src/external/mini_al.c
index eabb6365..0bde1180 100644
--- a/src/external/mini_al.c
+++ b/src/external/mini_al.c
@@ -3,6 +3,5 @@
#define MINI_AL_IMPLEMENTATION
#define MAL_NO_JACK
#define MAL_NO_OPENAL
-#define MAL_NO_SDL
#define MAL_NO_NULL
#include "mini_al.h"
diff --git a/src/external/mini_al.h b/src/external/mini_al.h
index 759662c7..0372f3bb 100644
--- a/src/external/mini_al.h
+++ b/src/external/mini_al.h
@@ -1,5 +1,5 @@
// Audio playback and capture library. Public domain. See "unlicense" statement at the end of this file.
-// mini_al - v0.8.8 - 2018-09-14
+// mini_al - v0.8.10 - 2018-10-21
//
// David Reid - [email protected]
@@ -1114,6 +1114,11 @@ void mal_pcm_f32_to_s24(void* pOut, const void* pIn, mal_uint64 count, mal_dithe
void mal_pcm_f32_to_s32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_format formatIn, mal_uint64 sampleCount, mal_dither_mode ditherMode);
+// Deinterleaves an interleaved buffer.
+void mal_deinterleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint32 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames);
+
+// Interleaves a group of deinterleaved buffers.
+void mal_interleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint32 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames);
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -1629,6 +1634,7 @@ struct mal_context
mal_proc pa_stream_get_sample_spec;
mal_proc pa_stream_get_channel_map;
mal_proc pa_stream_get_buffer_attr;
+ mal_proc pa_stream_set_buffer_attr;
mal_proc pa_stream_get_device_name;
mal_proc pa_stream_set_write_callback;
mal_proc pa_stream_set_read_callback;
@@ -4864,7 +4870,7 @@ mal_bool32 mal_device__get_current_frame__null(mal_device* pDevice, mal_uint32*
mal_assert(pCurrentPos != NULL);
*pCurrentPos = 0;
- mal_uint64 currentFrameAbs = (mal_uint64)(mal_timer_get_time_in_seconds(&pDevice->null_device.timer) * pDevice->sampleRate) / pDevice->channels;
+ mal_uint64 currentFrameAbs = (mal_uint64)(mal_timer_get_time_in_seconds(&pDevice->null_device.timer) * pDevice->sampleRate);
*pCurrentPos = (mal_uint32)(currentFrameAbs % pDevice->bufferSizeInFrames);
return MAL_TRUE;
@@ -4917,11 +4923,11 @@ mal_uint32 mal_device__wait_for_frames__null(mal_device* pDevice)
while (!pDevice->null_device.breakFromMainLoop) {
mal_uint32 framesAvailable = mal_device__get_available_frames__null(pDevice);
- if (framesAvailable > 0) {
+ if (framesAvailable >= (pDevice->bufferSizeInFrames/pDevice->periods)) {
return framesAvailable;
}
- mal_sleep(16);
+ mal_sleep(pDevice->bufferSizeInMilliseconds/pDevice->periods);
}
// We'll get here if the loop was terminated. Just return whatever's available.
@@ -11784,6 +11790,7 @@ typedef mal_pa_stream_state_t (* mal_pa_stream_get_state_proc)
typedef const mal_pa_sample_spec* (* mal_pa_stream_get_sample_spec_proc) (mal_pa_stream* s);
typedef const mal_pa_channel_map* (* mal_pa_stream_get_channel_map_proc) (mal_pa_stream* s);
typedef const mal_pa_buffer_attr* (* mal_pa_stream_get_buffer_attr_proc) (mal_pa_stream* s);
+typedef mal_pa_operation* (* mal_pa_stream_set_buffer_attr_proc) (mal_pa_stream* s, const mal_pa_buffer_attr* attr, mal_pa_stream_success_cb_t cb, void* userdata);
typedef const char* (* mal_pa_stream_get_device_name_proc) (mal_pa_stream* s);
typedef void (* mal_pa_stream_set_write_callback_proc) (mal_pa_stream* s, mal_pa_stream_request_cb_t cb, void* userdata);
typedef void (* mal_pa_stream_set_read_callback_proc) (mal_pa_stream* s, mal_pa_stream_request_cb_t cb, void* userdata);
@@ -12305,6 +12312,10 @@ void mal_pulse_device_write_callback(mal_pa_stream* pStream, size_t sizeInBytes,
mal_context* pContext = pDevice->pContext;
mal_assert(pContext != NULL);
+#ifdef MAL_DEBUG_OUTPUT
+ printf("[PulseAudio] write_callback: sizeInBytes=%d\n", (int)sizeInBytes);
+#endif
+
size_t bytesRemaining = sizeInBytes;
while (bytesRemaining > 0) {
size_t bytesToReadFromClient = bytesRemaining;
@@ -12319,19 +12330,35 @@ void mal_pulse_device_write_callback(mal_pa_stream* pStream, size_t sizeInBytes,
return;
}
+#ifdef MAL_DEBUG_OUTPUT
+ printf(" bytesToReadFromClient=%d", (int)bytesToReadFromClient);
+#endif
+
if (pBuffer != NULL && bytesToReadFromClient > 0) {
mal_uint32 framesToReadFromClient = (mal_uint32)bytesToReadFromClient / (pDevice->internalChannels*mal_get_bytes_per_sample(pDevice->internalFormat));
if (framesToReadFromClient > 0) {
mal_device__read_frames_from_client(pDevice, framesToReadFromClient, pBuffer);
+#ifdef MAL_DEBUG_OUTPUT
+ printf(", framesToReadFromClient=%d\n", (int)framesToReadFromClient);
+#endif
+
error = ((mal_pa_stream_write_proc)pContext->pulse.pa_stream_write)((mal_pa_stream*)pDevice->pulse.pStream, pBuffer, bytesToReadFromClient, NULL, 0, MAL_PA_SEEK_RELATIVE);
if (error < 0) {
mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[PulseAudio] Failed to write data to the PulseAudio stream.", mal_result_from_pulse(error));
return;
}
+ } else {
+#ifdef MAL_DEBUG_OUTPUT
+ printf(", framesToReadFromClient=0\n");
+#endif
}
bytesRemaining -= bytesToReadFromClient;
+ } else {
+#ifdef MAL_DEBUG_OUTPUT
+ printf(", framesToReadFromClient=0\n");
+#endif
}
}
}
@@ -12460,6 +12487,7 @@ mal_result mal_device_init__pulse(mal_context* pContext, mal_device_type type, c
mal_pa_sample_spec ss;
mal_pa_channel_map cmap;
mal_pa_buffer_attr attr;
+ mal_pa_stream_flags_t streamFlags;
const mal_pa_sample_spec* pActualSS = NULL;
const mal_pa_channel_map* pActualCMap = NULL;
@@ -12533,53 +12561,6 @@ mal_result mal_device_init__pulse(mal_context* pContext, mal_device_type type, c
((mal_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
}
-
-#if 0
- mal_pa_sample_spec deviceSS;
- mal_pa_channel_map deviceCMap;
- if (type == mal_device_type_playback) {
- deviceSS = sinkInfo.sample_spec;
- deviceCMap = sinkInfo.channel_map;
- } else {
- deviceSS = sourceInfo.sample_spec;
- deviceCMap = sourceInfo.channel_map;
- }
-
- if (pDevice->usingDefaultFormat) {
- ss.format = deviceSS.format;
- } else {
- ss.format = mal_format_to_pulse(pConfig->format);
- }
- if (ss.format == MAL_PA_SAMPLE_INVALID) {
- ss.format = MAL_PA_SAMPLE_S16LE;
- }
-
- if (pDevice->usingDefaultChannels) {
- ss.channels = deviceSS.channels;
- } else {
- ss.channels = pConfig->channels;
- }
-
- if (pDevice->usingDefaultSampleRate) {
- ss.rate = deviceSS.rate;
- } else {
- ss.rate = pConfig->sampleRate;
- }
-
-
- if (pDevice->usingDefaultChannelMap) {
- cmap = deviceCMap;
- } else {
- cmap.channels = pConfig->channels;
- for (mal_uint32 iChannel = 0; iChannel < pConfig->channels; ++iChannel) {
- cmap.map[iChannel] = mal_channel_position_to_pulse(pConfig->channelMap[iChannel]);
- }
-
- if (((mal_pa_channel_map_valid_proc)pContext->pulse.pa_channel_map_valid)(&cmap) == 0 || ((mal_pa_channel_map_compatible_proc)pContext->pulse.pa_channel_map_compatible)(&cmap, &ss) == 0) {
- ((mal_pa_channel_map_init_extend_proc)pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, MAL_PA_CHANNEL_MAP_DEFAULT); // The channel map is invalid, so just fall back to the default.
- }
- }
-#else
if (type == mal_device_type_playback) {
ss = sinkInfo.sample_spec;
cmap = sinkInfo.channel_map;
@@ -12587,7 +12568,7 @@ mal_result mal_device_init__pulse(mal_context* pContext, mal_device_type type, c
ss = sourceInfo.sample_spec;
cmap = sourceInfo.channel_map;
}
-#endif
+
// Buffer size.
bufferSizeInFrames = pDevice->bufferSizeInFrames;
@@ -12606,10 +12587,14 @@ mal_result mal_device_init__pulse(mal_context* pContext, mal_device_type type, c
}
attr.maxlength = bufferSizeInFrames * mal_get_bytes_per_sample(mal_format_from_pulse(ss.format))*ss.channels;
- attr.tlength = attr.maxlength / pConfig->periods;
+ attr.tlength = attr.maxlength;
attr.prebuf = (mal_uint32)-1;
- attr.minreq = attr.tlength;
- attr.fragsize = attr.tlength;
+ attr.minreq = attr.maxlength / pConfig->periods;
+ attr.fragsize = attr.maxlength / pConfig->periods;
+
+#ifdef MAL_DEBUG_OUTPUT
+ printf("[PulseAudio] attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; bufferSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, bufferSizeInFrames);
+#endif
char streamName[256];
if (pConfig->pulse.pStreamName != NULL) {
@@ -12628,11 +12613,15 @@ mal_result mal_device_init__pulse(mal_context* pContext, mal_device_type type, c
}
-
+ streamFlags = MAL_PA_STREAM_START_CORKED;
+ if (dev != NULL) {
+ streamFlags |= MAL_PA_STREAM_DONT_MOVE | MAL_PA_STREAM_FIX_FORMAT | MAL_PA_STREAM_FIX_RATE | MAL_PA_STREAM_FIX_CHANNELS;
+ }
+
if (type == mal_device_type_playback) {
- error = ((mal_pa_stream_connect_playback_proc)pContext->pulse.pa_stream_connect_playback)((mal_pa_stream*)pDevice->pulse.pStream, dev, &attr, MAL_PA_STREAM_START_CORKED, NULL, NULL);
+ error = ((mal_pa_stream_connect_playback_proc)pContext->pulse.pa_stream_connect_playback)((mal_pa_stream*)pDevice->pulse.pStream, dev, &attr, streamFlags, NULL, NULL);
} else {
- error = ((mal_pa_stream_connect_record_proc)pContext->pulse.pa_stream_connect_record)((mal_pa_stream*)pDevice->pulse.pStream, dev, &attr, MAL_PA_STREAM_START_CORKED);
+ error = ((mal_pa_stream_connect_record_proc)pContext->pulse.pa_stream_connect_record)((mal_pa_stream*)pDevice->pulse.pStream, dev, &attr, streamFlags);
}
if (error != MAL_PA_OK) {
@@ -12652,6 +12641,21 @@ mal_result mal_device_init__pulse(mal_context* pContext, mal_device_type type, c
// Internal format.
pActualSS = ((mal_pa_stream_get_sample_spec_proc)pContext->pulse.pa_stream_get_sample_spec)((mal_pa_stream*)pDevice->pulse.pStream);
if (pActualSS != NULL) {
+ // If anything has changed between the requested and the actual sample spec, we need to update the buffer.
+ if (ss.format != pActualSS->format || ss.channels != pActualSS->channels || ss.rate != pActualSS->rate) {
+ attr.maxlength = bufferSizeInFrames * mal_get_bytes_per_sample(mal_format_from_pulse(pActualSS->format))*pActualSS->channels;
+ attr.tlength = attr.maxlength;
+ attr.prebuf = (mal_uint32)-1;
+ attr.minreq = attr.maxlength / pConfig->periods;
+ attr.fragsize = attr.maxlength / pConfig->periods;
+
+ pOP = ((mal_pa_stream_set_buffer_attr_proc)pContext->pulse.pa_stream_set_buffer_attr)((mal_pa_stream*)pDevice->pulse.pStream, &attr, NULL, NULL);
+ if (pOP != NULL) {
+ mal_device__wait_for_operation__pulse(pDevice, pOP);
+ ((mal_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
+ }
+ }
+
ss = *pActualSS;
}
@@ -12680,6 +12684,10 @@ mal_result mal_device_init__pulse(mal_context* pContext, mal_device_type type, c
pDevice->bufferSizeInFrames = attr.maxlength / (mal_get_bytes_per_sample(pDevice->internalFormat)*pDevice->internalChannels);
pDevice->periods = attr.maxlength / attr.tlength;
+#ifdef MAL_DEBUG_OUTPUT
+ printf("[PulseAudio] actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; pDevice->bufferSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDevice->bufferSizeInFrames);
+#endif
+
// Grab the name of the device if we can.
dev = ((mal_pa_stream_get_device_name_proc)pContext->pulse.pa_stream_get_device_name)((mal_pa_stream*)pDevice->pulse.pStream);
@@ -12930,6 +12938,7 @@ mal_result mal_context_init__pulse(mal_context* pContext)
pContext->pulse.pa_stream_get_sample_spec = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_get_sample_spec");
pContext->pulse.pa_stream_get_channel_map = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_get_channel_map");
pContext->pulse.pa_stream_get_buffer_attr = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_get_buffer_attr");
+ pContext->pulse.pa_stream_set_buffer_attr = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_set_buffer_attr");
pContext->pulse.pa_stream_get_device_name = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_get_device_name");
pContext->pulse.pa_stream_set_write_callback = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_set_write_callback");
pContext->pulse.pa_stream_set_read_callback = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_set_read_callback");
@@ -12972,6 +12981,7 @@ mal_result mal_context_init__pulse(mal_context* pContext)
mal_pa_stream_get_sample_spec_proc _pa_stream_get_sample_spec = pa_stream_get_sample_spec;
mal_pa_stream_get_channel_map_proc _pa_stream_get_channel_map = pa_stream_get_channel_map;
mal_pa_stream_get_buffer_attr_proc _pa_stream_get_buffer_attr = pa_stream_get_buffer_attr;
+ mal_pa_stream_set_buffer_attr_proc _pa_stream_set_buffer_attr = pa_stream_set_buffer_attr;
mal_pa_stream_get_device_name_proc _pa_stream_get_device_name = pa_stream_get_device_name;
mal_pa_stream_set_write_callback_proc _pa_stream_set_write_callback = pa_stream_set_write_callback;
mal_pa_stream_set_read_callback_proc _pa_stream_set_read_callback = pa_stream_set_read_callback;
@@ -13013,6 +13023,7 @@ mal_result mal_context_init__pulse(mal_context* pContext)
pContext->pulse.pa_stream_get_sample_spec = (mal_proc)_pa_stream_get_sample_spec;
pContext->pulse.pa_stream_get_channel_map = (mal_proc)_pa_stream_get_channel_map;
pContext->pulse.pa_stream_get_buffer_attr = (mal_proc)_pa_stream_get_buffer_attr;
+ pContext->pulse.pa_stream_set_buffer_attr = (mal_proc)_pa_stream_set_buffer_attr;
pContext->pulse.pa_stream_get_device_name = (mal_proc)_pa_stream_get_device_name;
pContext->pulse.pa_stream_set_write_callback = (mal_proc)_pa_stream_set_write_callback;
pContext->pulse.pa_stream_set_read_callback = (mal_proc)_pa_stream_set_read_callback;
@@ -14900,27 +14911,62 @@ OSStatus mal_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFlags* p
#if defined(MAL_DEBUG_OUTPUT)
printf("INFO: Output Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", busNumber, frameCount, pBufferList->mNumberBuffers);
#endif
+
+ // We need to check whether or not we are outputting interleaved or non-interleaved samples. The
+ // way we do this is slightly different for each type.
+ mal_stream_layout layout = mal_stream_layout_interleaved;
+ if (pBufferList->mBuffers[0].mNumberChannels != pDevice->internalChannels) {
+ layout = mal_stream_layout_deinterleaved;
+ }
- // For now we can assume everything is interleaved.
- for (UInt32 iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) {
- if (pBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->internalChannels) {
- mal_uint32 frameCountForThisBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels);
- if (frameCountForThisBuffer > 0) {
- mal_device__read_frames_from_client(pDevice, frameCountForThisBuffer, pBufferList->mBuffers[iBuffer].mData);
+ if (layout == mal_stream_layout_interleaved) {
+ // For now we can assume everything is interleaved.
+ for (UInt32 iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) {
+ if (pBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->internalChannels) {
+ mal_uint32 frameCountForThisBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels);
+ if (frameCountForThisBuffer > 0) {
+ mal_device__read_frames_from_client(pDevice, frameCountForThisBuffer, pBufferList->mBuffers[iBuffer].mData);
+ }
+
+ #if defined(MAL_DEBUG_OUTPUT)
+ printf(" frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pBufferList->mBuffers[iBuffer].mNumberChannels, pBufferList->mBuffers[iBuffer].mDataByteSize);
+ #endif
+ } else {
+ // This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's
+ // not interleaved, in which case we can't handle right now since mini_al does not yet support non-interleaved streams. We just
+ // output silence here.
+ mal_zero_memory(pBufferList->mBuffers[iBuffer].mData, pBufferList->mBuffers[iBuffer].mDataByteSize);
+
+ #if defined(MAL_DEBUG_OUTPUT)
+ printf(" WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pBufferList->mBuffers[iBuffer].mNumberChannels, pBufferList->mBuffers[iBuffer].mDataByteSize);
+ #endif
}
+ }
+ } else {
+ // This is the deinterleaved case. We need to update each buffer in groups of internalChannels. This
+ // assumes each buffer is the same size.
+ mal_uint8 tempBuffer[4096];
+ for (UInt32 iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; iBuffer += pDevice->internalChannels) {
+ mal_uint32 frameCountPerBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels);
- #if defined(MAL_DEBUG_OUTPUT)
- printf(" frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pBufferList->mBuffers[iBuffer].mNumberChannels, pBufferList->mBuffers[iBuffer].mDataByteSize);
- #endif
- } else {
- // This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's
- // not interleaved, in which case we can't handle right now since mini_al does not yet support non-interleaved streams. We just
- // output silence here.
- mal_zero_memory(pBufferList->mBuffers[iBuffer].mData, pBufferList->mBuffers[iBuffer].mDataByteSize);
-
- #if defined(MAL_DEBUG_OUTPUT)
- printf(" WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pBufferList->mBuffers[iBuffer].mNumberChannels, pBufferList->mBuffers[iBuffer].mDataByteSize);
- #endif
+ mal_uint32 framesRemaining = frameCountPerBuffer;
+ while (framesRemaining > 0) {
+ mal_uint32 framesToRead = sizeof(tempBuffer) / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels);
+ if (framesToRead > framesRemaining) {
+ framesToRead = framesRemaining;
+ }
+
+ mal_device__read_frames_from_client(pDevice, framesToRead, tempBuffer);
+
+ void* ppDeinterleavedBuffers[MAL_MAX_CHANNELS];
+ for (mal_uint32 iChannel = 0; iChannel < pDevice->internalChannels; ++iChannel) {
+ ppDeinterleavedBuffers[iChannel] = (void*)mal_offset_ptr(pBufferList->mBuffers[iBuffer].mData, (frameCountPerBuffer - framesRemaining) * mal_get_bytes_per_sample(pDevice->internalFormat));
+ }
+
+ mal_deinterleave_pcm_frames(pDevice->internalFormat, pDevice->internalChannels, framesToRead, tempBuffer, ppDeinterleavedBuffers);
+
+ framesRemaining -= framesToRead;
+ }
}
}
@@ -14941,6 +14987,13 @@ OSStatus mal_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pA
AudioBufferList* pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList;
mal_assert(pRenderedBufferList);
+ // We need to check whether or not we are outputting interleaved or non-interleaved samples. The
+ // way we do this is slightly different for each type.
+ mal_stream_layout layout = mal_stream_layout_interleaved;
+ if (pRenderedBufferList->mBuffers[0].mNumberChannels != pDevice->internalChannels) {
+ layout = mal_stream_layout_deinterleaved;
+ }
+
#if defined(MAL_DEBUG_OUTPUT)
printf("INFO: Input Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", busNumber, frameCount, pRenderedBufferList->mNumberBuffers);
#endif
@@ -14953,16 +15006,58 @@ OSStatus mal_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pA
return status;
}
- // For now we can assume everything is interleaved.
- for (UInt32 iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) {
- if (pRenderedBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->internalChannels) {
- mal_device__send_frames_to_client(pDevice, frameCount, pRenderedBufferList->mBuffers[iBuffer].mData);
- #if defined(MAL_DEBUG_OUTPUT)
- printf(" mDataByteSize=%d\n", pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);
- #endif
- } else {
- // This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's
- // not interleaved, in which case we can't handle right now since mini_al does not yet support non-interleaved streams.
+ if (layout == mal_stream_layout_interleaved) {
+ for (UInt32 iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) {
+ if (pRenderedBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->internalChannels) {
+ mal_device__send_frames_to_client(pDevice, frameCount, pRenderedBufferList->mBuffers[iBuffer].mData);
+ #if defined(MAL_DEBUG_OUTPUT)
+ printf(" mDataByteSize=%d\n", pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);
+ #endif
+ } else {
+ // This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's
+ // not interleaved, in which case we can't handle right now since mini_al does not yet support non-interleaved streams.
+
+ mal_uint8 silentBuffer[4096];
+ mal_zero_memory(silentBuffer, sizeof(silentBuffer));
+
+ mal_uint32 framesRemaining = frameCount;
+ while (framesRemaining > 0) {
+ mal_uint32 framesToSend = sizeof(silentBuffer) / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels);
+ if (framesToSend > framesRemaining) {
+ framesToSend = framesRemaining;
+ }
+
+ mal_device__send_frames_to_client(pDevice, framesToSend, silentBuffer);
+ framesRemaining -= framesToSend;
+ }
+
+ #if defined(MAL_DEBUG_OUTPUT)
+ printf(" WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pRenderBufferList->mBuffers[iBuffer].mNumberChannels, pRenderBufferList->mBuffers[iBuffer].mDataByteSize);
+ #endif
+ }
+ }
+ } else {
+ // This is the deinterleaved case. We need to interleave the audio data before sending it to the client. This
+ // assumes each buffer is the same size.
+ mal_uint8 tempBuffer[4096];
+ for (UInt32 iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; iBuffer += pDevice->internalChannels) {
+ mal_uint32 framesRemaining = frameCount;
+ while (framesRemaining > 0) {
+ mal_uint32 framesToSend = sizeof(tempBuffer) / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels);
+ if (framesToSend > framesRemaining) {
+ framesToSend = framesRemaining;
+ }
+
+ void* ppDeinterleavedBuffers[MAL_MAX_CHANNELS];
+ for (mal_uint32 iChannel = 0; iChannel < pDevice->internalChannels; ++iChannel) {
+ ppDeinterleavedBuffers[iChannel] = (void*)mal_offset_ptr(pRenderedBufferList->mBuffers[iBuffer].mData, (frameCount - framesRemaining) * mal_get_bytes_per_sample(pDevice->internalFormat));
+ }
+
+ mal_interleave_pcm_frames(pDevice->internalFormat, pDevice->internalChannels, framesToSend, (const void**)ppDeinterleavedBuffers, tempBuffer);
+ mal_device__send_frames_to_client(pDevice, framesToSend, tempBuffer);
+
+ framesRemaining -= framesToSend;
+ }
}
}
@@ -14976,42 +15071,53 @@ void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, AudioUnitPro
mal_device* pDevice = (mal_device*)pUserData;
mal_assert(pDevice != NULL);
- UInt32 isRunning;
- UInt32 isRunningSize = sizeof(isRunning);
- OSStatus status = ((mal_AudioUnitGetProperty_proc)pDevice->pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioOutputUnitProperty_IsRunning, scope, element, &isRunning, &isRunningSize);
- if (status != noErr) {
- return; // Don't really know what to do in this case... just ignore it, I suppose...
- }
-
- if (!isRunning) {
- // The stop event is a bit annoying in Core Audio because it will be called when we automatically switch the default device. Some scenarios to consider:
- //
- // 1) When the device is unplugged, this will be called _before_ the default device change notification.
- // 2) When the device is changed via the default device change notification, this will be called _after_ the switch.
- //
- // For case #1, we just check if there's a new default device available. If so, we just ignore the stop event. For case #2 we check a flag.
- if (pDevice->isDefaultDevice && mal_device__get_state(pDevice) != MAL_STATE_STOPPING && mal_device__get_state(pDevice) != MAL_STATE_STOPPED) {
- // It looks like the device is switching through an external event, such as the user unplugging the device or changing the default device
- // via the operating system's sound settings. If we're re-initializing the device, we just terminate because we want the stopping of the
- // device to be seamless to the client (we don't want them receiving the onStop event and thinking that the device has stopped when it
- // hasn't!).
- if (pDevice->coreaudio.isSwitchingDevice) {
- return;
- }
-
- // Getting here means the device is not reinitializing which means it may have been unplugged. From what I can see, it looks like Core Audio
- // will try switching to the new default device seamlessly. We need to somehow find a way to determine whether or not Core Audio will most
- // likely be successful in switching to the new device.
- //
- // TODO: Try to predict if Core Audio will switch devices. If not, the onStop callback needs to be posted.
- return;
- }
-
- // Getting here means we need to stop the device.
+ // There's been a report of a deadlock here when triggered by mal_device_uninit(). It looks like
+ // AudioUnitGetProprty (called below) and AudioComponentInstanceDispose (called in mal_device_uninit)
+ // can try waiting on the same lock. I'm going to try working around this by not calling any Core
+ // Audio APIs in the callback when the device has been stopped or uninitialized.
+ if (mal_device__get_state(pDevice) == MAL_STATE_UNINITIALIZED || mal_device__get_state(pDevice) == MAL_STATE_STOPPING) {
mal_stop_proc onStop = pDevice->onStop;
if (onStop) {
onStop(pDevice);
}
+ } else {
+ UInt32 isRunning;
+ UInt32 isRunningSize = sizeof(isRunning);
+ OSStatus status = ((mal_AudioUnitGetProperty_proc)pDevice->pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioOutputUnitProperty_IsRunning, scope, element, &isRunning, &isRunningSize);
+ if (status != noErr) {
+ return; // Don't really know what to do in this case... just ignore it, I suppose...
+ }
+
+ if (!isRunning) {
+ // The stop event is a bit annoying in Core Audio because it will be called when we automatically switch the default device. Some scenarios to consider:
+ //
+ // 1) When the device is unplugged, this will be called _before_ the default device change notification.
+ // 2) When the device is changed via the default device change notification, this will be called _after_ the switch.
+ //
+ // For case #1, we just check if there's a new default device available. If so, we just ignore the stop event. For case #2 we check a flag.
+ if (pDevice->isDefaultDevice && mal_device__get_state(pDevice) != MAL_STATE_STOPPING && mal_device__get_state(pDevice) != MAL_STATE_STOPPED) {
+ // It looks like the device is switching through an external event, such as the user unplugging the device or changing the default device
+ // via the operating system's sound settings. If we're re-initializing the device, we just terminate because we want the stopping of the
+ // device to be seamless to the client (we don't want them receiving the onStop event and thinking that the device has stopped when it
+ // hasn't!).
+ if (pDevice->coreaudio.isSwitchingDevice) {
+ return;
+ }
+
+ // Getting here means the device is not reinitializing which means it may have been unplugged. From what I can see, it looks like Core Audio
+ // will try switching to the new default device seamlessly. We need to somehow find a way to determine whether or not Core Audio will most
+ // likely be successful in switching to the new device.
+ //
+ // TODO: Try to predict if Core Audio will switch devices. If not, the onStop callback needs to be posted.
+ return;
+ }
+
+ // Getting here means we need to stop the device.
+ mal_stop_proc onStop = pDevice->onStop;
+ if (onStop) {
+ onStop(pDevice);
+ }
+ }
}
}
@@ -15288,11 +15394,14 @@ mal_result mal_device_init_internal__coreaudio(mal_context* pContext, mal_device
if (result != MAL_SUCCESS) {
return result;
}
+
+ pData->bufferSizeInFramesOut = actualBufferSizeInFrames * pData->periodsOut;
#else
actualBufferSizeInFrames = 4096;
+ pData->bufferSizeInFramesOut = actualBufferSizeInFrames;
#endif
- pData->bufferSizeInFramesOut = actualBufferSizeInFrames * pData->periodsOut;
+
// During testing I discovered that the buffer size can be too big. You'll get an error like this:
//
@@ -15556,6 +15665,15 @@ mal_result mal_context_uninit__coreaudio(mal_context* pContext)
mal_result mal_context_init__coreaudio(mal_context* pContext)
{
mal_assert(pContext != NULL);
+
+#if defined(MAL_APPLE_MOBILE)
+ @autoreleasepool {
+ AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
+ mal_assert(pAudioSession != NULL);
+
+ [pAudioSession setCategory: AVAudioSessionCategoryPlayAndRecord error:nil];
+ }
+#endif
#if !defined(MAL_NO_RUNTIME_LINKING) && !defined(MAL_APPLE_MOBILE)
pContext->coreaudio.hCoreFoundation = mal_dlopen("CoreFoundation.framework/CoreFoundation");
@@ -19725,7 +19843,7 @@ mal_result mal_device_init__sdl(mal_context* pContext, mal_device_type type, con
// SDL wants the buffer size to be a power of 2. The SDL_AudioSpec property for this is only a Uint16, so we need
// to explicitly clamp this because it will be easy to overflow.
- mal_uint32 bufferSize = pConfig->bufferSizeInFrames;
+ mal_uint32 bufferSize = pDevice->bufferSizeInFrames;
if (bufferSize > 32768) {
bufferSize = 32768;
} else {
@@ -19760,7 +19878,7 @@ mal_result mal_device_init__sdl(mal_context* pContext, mal_device_type type, con
pDevice->sdl.deviceID = ((MAL_PFN_SDL_OpenAudioDevice)pDevice->pContext->sdl.SDL_OpenAudioDevice)(pDeviceName, isCapture, &desiredSpec, &obtainedSpec, MAL_SDL_AUDIO_ALLOW_ANY_CHANGE);
if (pDevice->sdl.deviceID == 0) {
- return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "Failed to open SDL device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
+ return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "Failed to open SDL2 device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
}
} else
#endif
@@ -19778,10 +19896,12 @@ mal_result mal_device_init__sdl(mal_context* pContext, mal_device_type type, con
desiredSpec.format = MAL_AUDIO_S16;
}
- pDevice->sdl.deviceID = ((MAL_PFN_SDL_OpenAudio)pDevice->pContext->sdl.SDL_OpenAudio)(&desiredSpec, &obtainedSpec);
- if (pDevice->sdl.deviceID != 0) {
- return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "Failed to open SDL device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
+ int deviceID = ((MAL_PFN_SDL_OpenAudio)pDevice->pContext->sdl.SDL_OpenAudio)(&desiredSpec, &obtainedSpec);
+ if (deviceID < 0) {
+ return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "Failed to open SDL1 device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
}
+
+ pDevice->sdl.deviceID = (mal_uint32)deviceID;
}
pDevice->internalFormat = mal_format_from_sdl(obtainedSpec.format);
@@ -20429,6 +20549,14 @@ mal_result mal_context_init(const mal_backend backends[], mal_uint32 backendCoun
mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_WARNING, "Failed to initialize mutex for device info retrieval. mal_context_get_device_info() is not thread safe.", MAL_FAILED_TO_CREATE_MUTEX);
}
+#ifdef MAL_DEBUG_OUTPUT
+ printf("[mini_al] Endian: %s\n", mal_is_little_endian() ? "LE" : "BE");
+ printf("[mini_al] SSE2: %s\n", mal_has_sse2() ? "YES" : "NO");
+ printf("[mini_al] AVX2: %s\n", mal_has_avx2() ? "YES" : "NO");
+ printf("[mini_al] AVX512F: %s\n", mal_has_avx512f() ? "YES" : "NO");
+ printf("[mini_al] NEON: %s\n", mal_has_neon() ? "YES" : "NO");
+#endif
+
pContext->backend = backend;
return result;
}
@@ -20767,7 +20895,7 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi
#ifdef MAL_DEBUG_OUTPUT
- printf("[WASAPI] %s (%s)\n", pDevice->name, (pDevice->type == mal_device_type_playback) ? "Playback" : "Capture");
+ printf("[%s] %s (%s)\n", mal_get_backend_name(pDevice->pContext->backend), pDevice->name, (pDevice->type == mal_device_type_playback) ? "Playback" : "Capture");
printf(" Format: %s -> %s\n", mal_get_format_name(pDevice->format), mal_get_format_name(pDevice->internalFormat));
printf(" Channels: %d -> %d\n", pDevice->channels, pDevice->internalChannels);
printf(" Sample Rate: %d -> %d\n", pDevice->sampleRate, pDevice->internalSampleRate);
@@ -20949,7 +21077,7 @@ mal_result mal_device_stop(mal_device* pDevice)
// Asynchronous backends need to be handled differently.
if (mal_context_is_backend_asynchronous(pDevice->pContext)) {
- pDevice->pContext->onDeviceStop(pDevice);
+ result = pDevice->pContext->onDeviceStop(pDevice);
} else {
// Synchronous backends.
@@ -26138,6 +26266,92 @@ void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_form
}
}
+void mal_deinterleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint32 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames)
+{
+ if (pInterleavedPCMFrames == NULL || ppDeinterleavedPCMFrames == NULL) {
+ return; // Invalid args.
+ }
+
+ // For efficiency we do this per format.
+ switch (format) {
+ case mal_format_s16:
+ {
+ const mal_int16* pSrcS16 = (const mal_int16*)pInterleavedPCMFrames;
+ for (mal_uint32 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
+ for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) {
+ mal_int16* pDstS16 = (mal_int16*)ppDeinterleavedPCMFrames[iChannel];
+ pDstS16[iPCMFrame] = pSrcS16[iPCMFrame*channels+iChannel];
+ }
+ }
+ } break;
+
+ case mal_format_f32:
+ {
+ const float* pSrcF32 = (const float*)pInterleavedPCMFrames;
+ for (mal_uint32 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
+ for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) {
+ float* pDstF32 = (float*)ppDeinterleavedPCMFrames[iChannel];
+ pDstF32[iPCMFrame] = pSrcF32[iPCMFrame*channels+iChannel];
+ }
+ }
+ } break;
+
+ default:
+ {
+ mal_uint32 sampleSizeInBytes = mal_get_bytes_per_sample(format);
+
+ for (mal_uint32 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
+ for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) {
+ void* pDst = mal_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes);
+ const void* pSrc = mal_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes);
+ memcpy(pDst, pSrc, sampleSizeInBytes);
+ }
+ }
+ } break;
+ }
+}
+
+void mal_interleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint32 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames)
+{
+ switch (format)
+ {
+ case mal_format_s16:
+ {
+ mal_int16* pDstS16 = (mal_int16*)pInterleavedPCMFrames;
+ for (mal_uint32 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
+ for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) {
+ const mal_int16* pSrcS16 = (const mal_int16*)ppDeinterleavedPCMFrames[iChannel];
+ pDstS16[iPCMFrame*channels+iChannel] = pSrcS16[iPCMFrame];
+ }
+ }
+ } break;
+
+ case mal_format_f32:
+ {
+ float* pDstF32 = (float*)pInterleavedPCMFrames;
+ for (mal_uint32 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
+ for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) {
+ const float* pSrcF32 = (const float*)ppDeinterleavedPCMFrames[iChannel];
+ pDstF32[iPCMFrame*channels+iChannel] = pSrcF32[iPCMFrame];
+ }
+ }
+ } break;
+
+ default:
+ {
+ mal_uint32 sampleSizeInBytes = mal_get_bytes_per_sample(format);
+
+ for (mal_uint32 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
+ for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) {
+ void* pDst = mal_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes);
+ const void* pSrc = mal_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes);
+ memcpy(pDst, pSrc, sampleSizeInBytes);
+ }
+ }
+ } break;
+ }
+}
+
typedef struct
@@ -28296,6 +28510,13 @@ mal_uint64 mal_sine_wave_read_ex(mal_sine_wave* pSineWave, mal_uint64 frameCount
// REVISION HISTORY
// ================
//
+// v0.8.10 - 2018-10-21
+// - Core Audio: Fix a hang when uninitializing a device.
+// - Fix a bug where an incorrect value is returned from mal_device_stop().
+//
+// v0.8.9 - 2018-09-28
+// - Fix a bug with the SDL backend where device initialization fails.
+//
// v0.8.8 - 2018-09-14
// - Fix Linux build with the ALSA backend.
// - Minor documentation fix.
diff --git a/src/raylib.h b/src/raylib.h
index aba4e108..ef470389 100644
--- a/src/raylib.h
+++ b/src/raylib.h
@@ -887,6 +887,8 @@ RLAPI long GetFileModTime(const char *fileName); // Get file mo
RLAPI void StorageSaveValue(int position, int value); // Save integer value to storage file (to defined position)
RLAPI int StorageLoadValue(int position); // Load integer value from storage file (from defined position)
+RLAPI void OpenURL(const char *url); // Open URL with default system browser (if available)
+
//------------------------------------------------------------------------------------
// Input Handling Functions (Module: core)
//------------------------------------------------------------------------------------
diff --git a/src/shapes.c b/src/shapes.c
index 77c141d1..43634ab4 100644
--- a/src/shapes.c
+++ b/src/shapes.c
@@ -128,15 +128,22 @@ void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color)
rlPushMatrix();
rlTranslatef((float)startPos.x, (float)startPos.y, 0);
rlRotatef(RAD2DEG*angle, 0, 0, 1);
- rlTranslatef(0, -thick/2.0f, 0);
+ rlTranslatef(0, (thick > 1.0f) ? -thick/2.0f : -1.0f, 0);
rlBegin(RL_QUADS);
rlColor4ub(color.r, color.g, color.b, color.a);
rlNormal3f(0.0f, 0.0f, 1.0f);
+ rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
rlVertex2f(0.0f, 0.0f);
+
+ rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
rlVertex2f(0.0f, thick);
+
+ rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
rlVertex2f(d, thick);
+
+ rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height);
rlVertex2f(d, 0.0f);
rlEnd();
rlPopMatrix();
@@ -435,7 +442,7 @@ void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color col
rlTranslatef(center.x, center.y, 0.0);
rlRotatef(rotation, 0, 0, 1);
- #if defined(SUPPORT_QUADS_DRAW_MODE)
+#if defined(SUPPORT_QUADS_DRAW_MODE)
rlEnableTexture(GetShapesTexture().id);
rlBegin(RL_QUADS);
@@ -457,7 +464,7 @@ void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color col
}
rlEnd();
rlDisableTexture();
- #else
+#else
rlBegin(RL_TRIANGLES);
for (int i = 0; i < 360; i += 360/sides)
{
@@ -468,7 +475,7 @@ void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color col
rlVertex2f(sinf(DEG2RAD*(i + 360/sides))*radius, cosf(DEG2RAD*(i + 360/sides))*radius);
}
rlEnd();
- #endif
+#endif
rlPopMatrix();
}
@@ -479,7 +486,7 @@ void DrawPolyEx(Vector2 *points, int pointsCount, Color color)
{
if (rlCheckBufferLimit(RL_QUADS, pointsCount)) rlglDraw();
- #if defined(SUPPORT_QUADS_DRAW_MODE)
+#if defined(SUPPORT_QUADS_DRAW_MODE)
rlEnableTexture(GetShapesTexture().id);
rlBegin(RL_QUADS);
@@ -494,7 +501,7 @@ void DrawPolyEx(Vector2 *points, int pointsCount, Color color)
}
rlEnd();
rlDisableTexture();
- #else
+#else
rlBegin(RL_TRIANGLES);
rlColor4ub(color.r, color.g, color.b, color.a);
@@ -505,7 +512,7 @@ void DrawPolyEx(Vector2 *points, int pointsCount, Color color)
rlVertex2f(points[i + 1].x, points[i + 1].y);
}
rlEnd();
- #endif
+#endif
}
}
@@ -709,7 +716,9 @@ static Texture2D GetShapesTexture(void)
{
#if defined(SUPPORT_FONT_TEXTURE)
texShapes = GetFontDefault().texture; // Use font texture white character
- recTexShapes = GetFontDefault().chars[95].rec;
+ Rectangle rec = GetFontDefault().chars[95].rec;
+ // NOTE: We setup a 1px padding on char rectangle to avoid texture bleeding on MSAA filtering
+ recTexShapes = (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 };
#else
texShapes = GetTextureDefault(); // Use default white texture
recTexShapes = { 0.0f, 0.0f, 1.0f, 1.0f };
diff --git a/src/textures.c b/src/textures.c
index 6249287d..fce1092a 100644
--- a/src/textures.c
+++ b/src/textures.c
@@ -1741,10 +1741,10 @@ void ImageDrawRectangle(Image *dst, Rectangle rec, Color color)
// Draw rectangle lines within an image
void ImageDrawRectangleLines(Image *dst, Rectangle rec, int thick, Color color)
{
- ImageDrawRectangle(&dst, (Rectangle){ rec.x, rec.y, rec.width, thick }, color);
- ImageDrawRectangle(&dst, (Rectangle){ rec.x, rec.y + thick, thick, rec.height - thick*2 }, color);
- ImageDrawRectangle(&dst, (Rectangle){ rec.x + rec.width - thick, rec.y + thick, thick, rec.height - thick*2 }, color);
- ImageDrawRectangle(&dst, (Rectangle){ rec.x, rec.height - thick, rec.width, thick }, color);
+ ImageDrawRectangle(dst, (Rectangle){ rec.x, rec.y, rec.width, thick }, color);
+ ImageDrawRectangle(dst, (Rectangle){ rec.x, rec.y + thick, thick, rec.height - thick*2 }, color);
+ ImageDrawRectangle(dst, (Rectangle){ rec.x + rec.width - thick, rec.y + thick, thick, rec.height - thick*2 }, color);
+ ImageDrawRectangle(dst, (Rectangle){ rec.x, rec.height - thick, rec.width, thick }, color);
}
// Draw text (default font) within an image (destination)