aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMurray Campbell <MurrayIRC@gmail.com>2018-10-24 14:33:53 -0500
committerGitHub <noreply@github.com>2018-10-24 14:33:53 -0500
commit03fb5499a0f2c4b45ed1b2655a6e1e77221ed46a (patch)
tree346e60c85c667b4e7650b7d3cd1c7ee3c8a54be5 /src
parent9c6a98aa61b0a43c0d85347b3da26f90dd5c42fc (diff)
parent72431c6c36a166b00e52527962ab4d978ab0111d (diff)
downloadraylib-03fb5499a0f2c4b45ed1b2655a6e1e77221ed46a.tar.gz
raylib-03fb5499a0f2c4b45ed1b2655a6e1e77221ed46a.zip
Merge pull request #7 from raysan5/master
pull
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt17
-rw-r--r--src/CMakeOptions.txt13
-rw-r--r--src/Makefile26
-rw-r--r--src/audio.c662
-rw-r--r--src/config.h2
-rw-r--r--src/config.h.in2
-rw-r--r--src/core.c982
-rw-r--r--src/external/dirent.h198
-rw-r--r--src/external/dr_mp3.h322
-rw-r--r--src/external/glfw/include/GLFW/glfw3native.h12
-rw-r--r--src/physac.h75
-rw-r--r--src/raylib.h46
-rw-r--r--src/raylib.icobin105907 -> 9752 bytes
-rw-r--r--src/raylib.rc2
-rw-r--r--src/raymath.h6
-rw-r--r--src/rlgl.h180
-rw-r--r--src/text.c325
-rw-r--r--src/textures.c154
18 files changed, 1740 insertions, 1284 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index c86cc0e7..1b1d0d06 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -45,13 +45,8 @@ endif()
add_definitions("-DRAYLIB_CMAKE=1")
if(USE_AUDIO)
- if (NOT USE_OPENAL_BACKEND)
- file(GLOB mini_al external/mini_al.c)
- MESSAGE(STATUS "Audio Backend: mini_al")
- else()
- find_package(OpenAL REQUIRED)
- MESSAGE(STATUS "Audio Backend: OpenAL")
- endif()
+ file(GLOB mini_al external/mini_al.c)
+ MESSAGE(STATUS "Audio Backend: mini_al")
file(GLOB stb_vorbis external/stb_vorbis.c)
set(sources ${raylib_sources} ${mini_al} ${stb_vorbis})
else()
@@ -73,6 +68,10 @@ if(${PLATFORM} MATCHES "Desktop")
find_library(OPENGL_LIBRARY OpenGL)
set(LIBS_PRIVATE ${OPENGL_LIBRARY})
link_libraries("${LIBS_PRIVATE}")
+ if (NOT CMAKE_SYSTEM STRLESS "Darwin-18.0.0")
+ add_definitions(-DGL_SILENCE_DEPRECATION)
+ MESSAGE(AUTHOR_WARNING "OpenGL is deprecated starting with macOS 10.14 (Mojave)!")
+ endif()
elseif(WIN32)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
else()
@@ -131,7 +130,7 @@ if (${OPENGL_VERSION})
elseif (${OPENGL_VERSION} MATCHES "ES 2.0")
set(GRAPHICS "GRAPHICS_API_OPENGL_ES2")
endif()
- if (${SUGGESTED_GRAPHICS} AND NOT "${SUGGESTED_GRAPHICS}" STREQUAL "${GRAPHICS}")
+ if ("${SUGGESTED_GRAPHICS}" AND NOT "${SUGGESTED_GRAPHICS}" STREQUAL "${GRAPHICS}")
message(WARNING "You are overriding the suggested GRAPHICS=${SUGGESTED_GRAPHICS} with ${GRAPHICS}! This may fail")
endif()
endif()
@@ -146,6 +145,7 @@ include(LibraryPathToLinkerFlags)
library_path_to_linker_flags(__PKG_CONFIG_LIBS_PRIVATE "${LIBS_PRIVATE}")
if(STATIC)
+ MESSAGE(STATUS "Building raylib static library")
if(${PLATFORM} MATCHES "Web")
set(CMAKE_STATIC_LIBRARY_SUFFIX ".bc")
endif()
@@ -182,6 +182,7 @@ endif(STATIC)
if(SHARED)
+ MESSAGE(STATUS "Building raylib shared library")
add_library(raylib SHARED ${sources})
target_compile_definitions(raylib
diff --git a/src/CMakeOptions.txt b/src/CMakeOptions.txt
index 84643b28..eee3f1a9 100644
--- a/src/CMakeOptions.txt
+++ b/src/CMakeOptions.txt
@@ -12,11 +12,6 @@ option(SHARED "Build raylib as a dynamic library" OFF)
option(STATIC "Build raylib as a static library" ON)
option(MACOS_FATLIB "Build fat library for both i386 and x86_64 on macOS" OFF)
option(USE_AUDIO "Build raylib with audio module" ON)
-if(${PLATFORM} MATCHES "Web")
- cmake_dependent_option(USE_OPENAL_BACKEND "Link raylib with openAL instead of mini-al" ON "USE_AUDIO" OFF)
-else()
- cmake_dependent_option(USE_OPENAL_BACKEND "Link raylib with openAL instead of mini-al" OFF "USE_AUDIO" OFF)
-endif()
enum_option(USE_EXTERNAL_GLFW "OFF;IF_POSSIBLE;ON" "Link raylib against system GLFW instead of embedded one")
if(UNIX AND NOT APPLE)
@@ -84,6 +79,14 @@ if(NOT (STATIC OR SHARED))
message(FATAL_ERROR "Nothing to do if both -DSHARED=OFF and -DSTATIC=OFF...")
endif()
+if (DEFINED BUILD_SHARED_LIBS)
+ set(SHARED ${BUILD_SHARED_LIBS})
+ if (${BUILD_SHARED_LIBS})
+ set(STATIC OFF)
+ else()
+ set(STATIC ON)
+ endif()
+endif()
if(DEFINED SHARED_RAYLIB)
set(SHARED ${SHARED_RAYLIB})
message(DEPRECATION "-DSHARED_RAYLIB is deprecated. Please use -DSHARED instead.")
diff --git a/src/Makefile b/src/Makefile
index ac4b15b4..0cfc82c1 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -63,14 +63,6 @@ RAYLIB_BUILD_MODE ?= RELEASE
# NOTE: Some programs like tools could not require audio support
INCLUDE_AUDIO_MODULE ?= TRUE
-# Use OpenAL Soft backend for audio
-USE_OPENAL_BACKEND ?= FALSE
-
-# OpenAL Soft audio backend forced on HTML5 and OSX (see below)
-ifeq ($(PLATFORM),PLATFORM_WEB)
- USE_OPENAL_BACKEND = TRUE
-endif
-
# Use external GLFW library instead of rglfw module
# TODO: Review usage of examples on Linux.
USE_EXTERNAL_GLFW ?= FALSE
@@ -154,13 +146,6 @@ endif
# RAYLIB_PATH ?= /home/pi/raylib
#endif
-# Force OpenAL Soft audio backend for OSX platform
-# NOTE 1: mini_al library does not support CoreAudio yet
-# NOTE 2: Required OpenAL libraries should be available on OSX
-ifeq ($(PLATFORM_OS),OSX)
- USE_OPENAL_BACKEND = TRUE
-endif
-
ifeq ($(PLATFORM),PLATFORM_WEB)
# Emscripten required variables
EMSDK_PATH = C:/emsdk
@@ -343,11 +328,6 @@ ifeq ($(RAYLIB_LIBTYPE),SHARED)
CFLAGS += -fPIC -DBUILD_LIBTYPE_SHARED
endif
-# Use OpenAL Soft backend instead of mini_al
-ifeq ($(USE_OPENAL_BACKEND),TRUE)
- CFLAGS += -DUSE_OPENAL_BACKEND
-endif
-
# Use Wayland display on Linux desktop
ifeq ($(PLATFORM),PLATFORM_DESKTOP)
ifeq ($(PLATFORM_OS), LINUX)
@@ -359,7 +339,7 @@ endif
# Define include paths for required headers
# NOTE: Several external required libraries (stb and others)
-INCLUDE_PATHS = -I. -Iexternal -Iexternal/glfw/include
+INCLUDE_PATHS = -I. -Iexternal/glfw/include
ifeq ($(PLATFORM),PLATFORM_DESKTOP)
ifeq ($(PLATFORM_OS),BSD)
@@ -426,9 +406,7 @@ endif
ifeq ($(INCLUDE_AUDIO_MODULE),TRUE)
OBJS += audio.o
OBJS += stb_vorbis.o
- ifeq ($(USE_OPENAL_BACKEND),FALSE)
- OBJS += mini_al.o
- endif
+ OBJS += mini_al.o
endif
ifeq ($(PLATFORM),PLATFORM_ANDROID)
diff --git a/src/audio.c b/src/audio.c
index 9e44d709..f0362b2d 100644
--- a/src/audio.c
+++ b/src/audio.c
@@ -16,9 +16,6 @@
* Define to use the module as standalone library (independently of raylib).
* Required types and functions are defined in the same module.
*
-* #define USE_OPENAL_BACKEND
-* Use OpenAL Soft audio backend
-*
* #define SUPPORT_FILEFORMAT_WAV
* #define SUPPORT_FILEFORMAT_OGG
* #define SUPPORT_FILEFORMAT_XM
@@ -82,25 +79,9 @@
#include "utils.h" // Required for: fopen() Android mapping
#endif
-#if !defined(USE_OPENAL_BACKEND)
- #define USE_MINI_AL 1 // Set to 1 to use mini_al; 0 to use OpenAL.
-#endif
-
-#include "external/mini_al.h" // Implemented in mini_al.c. Cannot implement this here because it conflicts with Win32 APIs such as CloseWindow(), etc.
-
-#if !defined(USE_MINI_AL) || (USE_MINI_AL == 0)
- #if defined(__APPLE__)
- #include "OpenAL/al.h" // OpenAL basic header
- #include "OpenAL/alc.h" // OpenAL context header (like OpenGL, OpenAL requires a context to work)
- #else
- #include "AL/al.h" // OpenAL basic header
- #include "AL/alc.h" // OpenAL context header (like OpenGL, OpenAL requires a context to work)
- //#include "AL/alext.h" // OpenAL extensions header, required for AL_EXT_FLOAT32 and AL_EXT_MCFORMATS
- #endif
-
- // OpenAL extension: AL_EXT_FLOAT32 - Support for 32bit float samples
- // OpenAL extension: AL_EXT_MCFORMATS - Support for multi-channel formats (Quad, 5.1, 6.1, 7.1)
-#endif
+#include "external/mini_al.h" // mini_al audio library
+ // NOTE: Cannot be implement here because it conflicts with
+ // Win32 APIs: Rectangle, CloseWindow(), ShowCursor(), PlaySoundA()
#include <stdlib.h> // Required for: malloc(), free()
#include <string.h> // Required for: strcmp(), strncmp()
@@ -132,7 +113,7 @@
#include "external/dr_mp3.h" // MP3 loading functions
#endif
-#ifdef _MSC_VER
+#if defined(_MSC_VER)
#undef bool
#endif
@@ -147,15 +128,6 @@
// In case of music-stalls, just increase this number
#define AUDIO_BUFFER_SIZE 4096 // PCM data samples (i.e. 16bit, Mono: 8Kb)
-// Support uncompressed PCM data in 32-bit float IEEE format
-// NOTE: This definition is included in "AL/alext.h", but some OpenAL implementations
-// could not provide the extensions header (Android), so its defined here
-#if !defined(AL_EXT_float32)
- #define AL_EXT_float32 1
- #define AL_FORMAT_MONO_FLOAT32 0x10010
- #define AL_FORMAT_STEREO_FLOAT32 0x10011
-#endif
-
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
@@ -233,8 +205,6 @@ void TraceLog(int msgType, const char *text, ...); // Show trace lo
//----------------------------------------------------------------------------------
// mini_al AudioBuffer Functionality
//----------------------------------------------------------------------------------
-#if USE_MINI_AL
-
#define DEVICE_FORMAT mal_format_f32
#define DEVICE_CHANNELS 2
#define DEVICE_SAMPLE_RATE 44100
@@ -461,7 +431,7 @@ static mal_uint32 OnAudioBufferDSPRead(mal_dsp *pDSP, mal_uint32 frameCount, voi
mal_uint32 totalFramesRemaining = (frameCount - framesRead);
if (totalFramesRemaining > 0)
{
- memset((unsigned char*)pFramesOut + (framesRead*frameSizeInBytes), 0, totalFramesRemaining*frameSizeInBytes);
+ memset((unsigned char *)pFramesOut + (framesRead*frameSizeInBytes), 0, totalFramesRemaining*frameSizeInBytes);
// For static buffers we can fill the remaining frames with silence for safety, but we don't want
// to report those frames as "read". The reason for this is that the caller uses the return value
@@ -487,7 +457,6 @@ static void MixAudioFrames(float *framesOut, const float *framesIn, mal_uint32 f
}
}
}
-#endif
//----------------------------------------------------------------------------------
// Module Functions Definition - Audio Device initialization and Closing
@@ -495,7 +464,6 @@ static void MixAudioFrames(float *framesOut, const float *framesIn, mal_uint32 f
// Initialize audio device
void InitAudioDevice(void)
{
-#if USE_MINI_AL
// Context.
mal_context_config contextConfig = mal_context_config_init(OnLog);
mal_result result = mal_context_init(NULL, 0, &contextConfig, &context);
@@ -545,45 +513,11 @@ void InitAudioDevice(void)
TraceLog(LOG_INFO, "Audio buffer size: %d", device.bufferSizeInFrames);
isAudioInitialized = MAL_TRUE;
-#else
- // Open and initialize a device with default settings
- ALCdevice *device = alcOpenDevice(NULL);
-
- if (!device) TraceLog(LOG_ERROR, "Audio device could not be opened");
- else
- {
- ALCcontext *context = alcCreateContext(device, NULL);
-
- if ((context == NULL) || (alcMakeContextCurrent(context) == ALC_FALSE))
- {
- if (context != NULL) alcDestroyContext(context);
-
- alcCloseDevice(device);
-
- TraceLog(LOG_ERROR, "Could not initialize audio context");
- }
- else
- {
- TraceLog(LOG_INFO, "Audio device and context initialized successfully: %s", alcGetString(device, ALC_DEVICE_SPECIFIER));
-
- // Listener definition (just for 2D)
- alListener3f(AL_POSITION, 0.0f, 0.0f, 0.0f);
- alListener3f(AL_VELOCITY, 0.0f, 0.0f, 0.0f);
- alListener3f(AL_ORIENTATION, 0.0f, 0.0f, -1.0f);
-
- alListenerf(AL_GAIN, 1.0f);
-
- if (alIsExtensionPresent("AL_EXT_float32")) TraceLog(LOG_INFO, "[EXTENSION] AL_EXT_float32 supported");
- else TraceLog(LOG_INFO, "[EXTENSION] AL_EXT_float32 not supported");
- }
- }
-#endif
}
// Close the audio device for all contexts
void CloseAudioDevice(void)
{
-#if USE_MINI_AL
if (!isAudioInitialized)
{
TraceLog(LOG_WARNING, "Could not close audio device because it is not currently initialized");
@@ -593,18 +527,6 @@ void CloseAudioDevice(void)
mal_mutex_uninit(&audioLock);
mal_device_uninit(&device);
mal_context_uninit(&context);
-#else
- ALCdevice *device;
- ALCcontext *context = alcGetCurrentContext();
-
- if (context == NULL) TraceLog(LOG_WARNING, "Could not get current audio context for closing");
-
- device = alcGetContextsDevice(context);
-
- alcMakeContextCurrent(NULL);
- alcDestroyContext(context);
- alcCloseDevice(device);
-#endif
TraceLog(LOG_INFO, "Audio device closed successfully");
}
@@ -612,20 +534,7 @@ void CloseAudioDevice(void)
// Check if device has been initialized successfully
bool IsAudioDeviceReady(void)
{
-#if USE_MINI_AL
return isAudioInitialized;
-#else
- ALCcontext *context = alcGetCurrentContext();
-
- if (context == NULL) return false;
- else
- {
- ALCdevice *device = alcGetContextsDevice(context);
-
- if (device == NULL) return false;
- else return true;
- }
-#endif
}
// Set master volume (listener)
@@ -634,17 +543,13 @@ void SetMasterVolume(float volume)
if (volume < 0.0f) volume = 0.0f;
else if (volume > 1.0f) volume = 1.0f;
-#if USE_MINI_AL
masterVolume = volume;
-#else
- alListenerf(AL_GAIN, volume);
-#endif
}
//----------------------------------------------------------------------------------
// Module Functions Definition - Audio Buffer management
//----------------------------------------------------------------------------------
-#if USE_MINI_AL
+
// Create a new audio buffer. Initially filled with silence
AudioBuffer *CreateAudioBuffer(mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_uint32 bufferSizeInFrames, AudioBufferUsage usage)
{
@@ -843,7 +748,6 @@ void UntrackAudioBuffer(AudioBuffer *audioBuffer)
mal_mutex_unlock(&audioLock);
}
-#endif
//----------------------------------------------------------------------------------
// Module Functions Definition - Sounds loading and playing (.WAV)
@@ -909,7 +813,6 @@ Sound LoadSoundFromWave(Wave wave)
if (wave.data != NULL)
{
-#if USE_MINI_AL
// When using mini_al we need to do our own mixing. To simplify this we need convert the format of each sound to be consistent with
// the format used to open the playback device. We can do this two ways:
//
@@ -931,61 +834,6 @@ Sound LoadSoundFromWave(Wave wave)
if (frameCount == 0) TraceLog(LOG_WARNING, "LoadSoundFromWave() : Format conversion failed");
sound.audioBuffer = audioBuffer;
-#else
- ALenum format = 0;
-
- // The OpenAL format is worked out by looking at the number of channels and the sample size (bits per sample)
- if (wave.channels == 1)
- {
- switch (wave.sampleSize)
- {
- case 8: format = AL_FORMAT_MONO8; break;
- case 16: format = AL_FORMAT_MONO16; break;
- case 32: format = AL_FORMAT_MONO_FLOAT32; break; // Requires OpenAL extension: AL_EXT_FLOAT32
- default: TraceLog(LOG_WARNING, "Wave sample size not supported: %i", wave.sampleSize); break;
- }
- }
- else if (wave.channels == 2)
- {
- switch (wave.sampleSize)
- {
- case 8: format = AL_FORMAT_STEREO8; break;
- case 16: format = AL_FORMAT_STEREO16; break;
- case 32: format = AL_FORMAT_STEREO_FLOAT32; break; // Requires OpenAL extension: AL_EXT_FLOAT32
- default: TraceLog(LOG_WARNING, "Wave sample size not supported: %i", wave.sampleSize); break;
- }
- }
- else TraceLog(LOG_WARNING, "Wave number of channels not supported: %i", wave.channels);
-
- // Create an audio source
- ALuint source;
- alGenSources(1, &source); // Generate pointer to audio source
-
- alSourcef(source, AL_PITCH, 1.0f);
- alSourcef(source, AL_GAIN, 1.0f);
- alSource3f(source, AL_POSITION, 0.0f, 0.0f, 0.0f);
- alSource3f(source, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
- alSourcei(source, AL_LOOPING, AL_FALSE);
-
- // Convert loaded data to OpenAL buffer
- //----------------------------------------
- ALuint buffer;
- alGenBuffers(1, &buffer); // Generate pointer to buffer
-
- unsigned int dataSize = wave.sampleCount*wave.channels*wave.sampleSize/8; // Size in bytes
-
- // Upload sound data to buffer
- alBufferData(buffer, format, wave.data, dataSize, wave.sampleRate);
-
- // Attach sound buffer to source
- alSourcei(source, AL_BUFFER, buffer);
-
- TraceLog(LOG_INFO, "[SND ID %i][BUFR ID %i] Sound data loaded successfully (%i Hz, %i bit, %s)", source, buffer, wave.sampleRate, wave.sampleSize, (wave.channels == 1) ? "Mono" : "Stereo");
-
- sound.source = source;
- sound.buffer = buffer;
- sound.format = format;
-#endif
}
return sound;
@@ -1002,14 +850,7 @@ void UnloadWave(Wave wave)
// Unload sound
void UnloadSound(Sound sound)
{
-#if USE_MINI_AL
DeleteAudioBuffer((AudioBuffer *)sound.audioBuffer);
-#else
- alSourceStop(sound.source);
-
- alDeleteSources(1, &sound.source);
- alDeleteBuffers(1, &sound.buffer);
-#endif
TraceLog(LOG_INFO, "[SND ID %i][BUFR ID %i] Unloaded sound data from RAM", sound.source, sound.buffer);
}
@@ -1018,8 +859,8 @@ void UnloadSound(Sound sound)
// NOTE: data must match sound.format
void UpdateSound(Sound sound, const void *data, int samplesCount)
{
-#if USE_MINI_AL
AudioBuffer *audioBuffer = (AudioBuffer *)sound.audioBuffer;
+
if (audioBuffer == NULL)
{
TraceLog(LOG_ERROR, "UpdateSound() : Invalid sound - no audio buffer");
@@ -1030,29 +871,6 @@ void UpdateSound(Sound sound, const void *data, int samplesCount)
// TODO: May want to lock/unlock this since this data buffer is read at mixing time.
memcpy(audioBuffer->buffer, data, samplesCount*audioBuffer->dsp.formatConverterIn.config.channels*mal_get_bytes_per_sample(audioBuffer->dsp.formatConverterIn.config.formatIn));
-#else
- ALint sampleRate, sampleSize, channels;
- alGetBufferi(sound.buffer, AL_FREQUENCY, &sampleRate);
- alGetBufferi(sound.buffer, AL_BITS, &sampleSize); // It could also be retrieved from sound.format
- alGetBufferi(sound.buffer, AL_CHANNELS, &channels); // It could also be retrieved from sound.format
-
- TraceLog(LOG_DEBUG, "UpdateSound() : AL_FREQUENCY: %i", sampleRate);
- TraceLog(LOG_DEBUG, "UpdateSound() : AL_BITS: %i", sampleSize);
- TraceLog(LOG_DEBUG, "UpdateSound() : AL_CHANNELS: %i", channels);
-
- unsigned int dataSize = samplesCount*channels*sampleSize/8; // Size of data in bytes
-
- alSourceStop(sound.source); // Stop sound
- alSourcei(sound.source, AL_BUFFER, 0); // Unbind buffer from sound to update
- //alDeleteBuffers(1, &sound.buffer); // Delete current buffer data
- //alGenBuffers(1, &sound.buffer); // Generate new buffer
-
- // Upload new data to sound buffer
- alBufferData(sound.buffer, sound.format, data, dataSize, sampleRate);
-
- // Attach sound buffer to source again
- alSourcei(sound.source, AL_BUFFER, sound.buffer);
-#endif
}
// Export wave data to file
@@ -1141,102 +959,48 @@ void ExportWave(Wave wave, const char *fileName)
// Play a sound
void PlaySound(Sound sound)
{
-#if USE_MINI_AL
PlayAudioBuffer((AudioBuffer *)sound.audioBuffer);
-#else
- alSourcePlay(sound.source); // Play the sound
-#endif
-
- //TraceLog(LOG_INFO, "Playing sound");
-
- // Find the current position of the sound being played
- // NOTE: Only work when the entire file is in a single buffer
- //int byteOffset;
- //alGetSourcei(sound.source, AL_BYTE_OFFSET, &byteOffset);
- //
- //int sampleRate;
- //alGetBufferi(sound.buffer, AL_FREQUENCY, &sampleRate); // AL_CHANNELS, AL_BITS (bps)
-
- //float seconds = (float)byteOffset/sampleRate; // Number of seconds since the beginning of the sound
- //or
- //float result;
- //alGetSourcef(sound.source, AL_SEC_OFFSET, &result); // AL_SAMPLE_OFFSET
}
// Pause a sound
void PauseSound(Sound sound)
{
-#if USE_MINI_AL
PauseAudioBuffer((AudioBuffer *)sound.audioBuffer);
-#else
- alSourcePause(sound.source);
-#endif
}
// Resume a paused sound
void ResumeSound(Sound sound)
{
-#if USE_MINI_AL
ResumeAudioBuffer((AudioBuffer *)sound.audioBuffer);
-#else
- ALenum state;
-
- alGetSourcei(sound.source, AL_SOURCE_STATE, &state);
-
- if (state == AL_PAUSED) alSourcePlay(sound.source);
-#endif
}
// Stop reproducing a sound
void StopSound(Sound sound)
{
-#if USE_MINI_AL
StopAudioBuffer((AudioBuffer *)sound.audioBuffer);
-#else
- alSourceStop(sound.source);
-#endif
}
// Check if a sound is playing
bool IsSoundPlaying(Sound sound)
{
-#if USE_MINI_AL
return IsAudioBufferPlaying((AudioBuffer *)sound.audioBuffer);
-#else
- bool playing = false;
- ALint state;
-
- alGetSourcei(sound.source, AL_SOURCE_STATE, &state);
- if (state == AL_PLAYING) playing = true;
-
- return playing;
-#endif
}
// Set volume for a sound
void SetSoundVolume(Sound sound, float volume)
{
-#if USE_MINI_AL
SetAudioBufferVolume((AudioBuffer *)sound.audioBuffer, volume);
-#else
- alSourcef(sound.source, AL_GAIN, volume);
-#endif
}
// Set pitch for a sound
void SetSoundPitch(Sound sound, float pitch)
{
-#if USE_MINI_AL
SetAudioBufferPitch((AudioBuffer *)sound.audioBuffer, pitch);
-#else
- alSourcef(sound.source, AL_PITCH, pitch);
-#endif
}
// Convert wave data to desired format
void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels)
{
-#if USE_MINI_AL
mal_format formatIn = ((wave->sampleSize == 8) ? mal_format_u8 : ((wave->sampleSize == 16) ? mal_format_s16 : mal_format_f32));
mal_format formatOut = (( sampleSize == 8) ? mal_format_u8 : (( sampleSize == 16) ? mal_format_s16 : mal_format_f32));
@@ -1264,87 +1028,6 @@ void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels)
wave->channels = channels;
free(wave->data);
wave->data = data;
-
-#else
- // Format sample rate
- // NOTE: Only supported 22050 <--> 44100
- if (wave->sampleRate != sampleRate)
- {
- // TODO: Resample wave data (upsampling or downsampling)
- // NOTE 1: To downsample, you have to drop samples or average them.
- // NOTE 2: To upsample, you have to interpolate new samples.
-
- wave->sampleRate = sampleRate;
- }
-
- // Format sample size
- // NOTE: Only supported 8 bit <--> 16 bit <--> 32 bit
- if (wave->sampleSize != sampleSize)
- {
- void *data = malloc(wave->sampleCount*wave->channels*sampleSize/8);
-
- for (int i = 0; i < wave->sampleCount; i++)
- {
- for (int j = 0; j < wave->channels; j++)
- {
- if (sampleSize == 8)
- {
- if (wave->sampleSize == 16) ((unsigned char *)data)[wave->channels*i + j] = (unsigned char)(((float)(((short *)wave->data)[wave->channels*i + j])/32767.0f)*256);
- else if (wave->sampleSize == 32) ((unsigned char *)data)[wave->channels*i + j] = (unsigned char)(((float *)wave->data)[wave->channels*i + j]*127.0f + 127);
- }
- else if (sampleSize == 16)
- {
- if (wave->sampleSize == 8) ((short *)data)[wave->channels*i + j] = (short)(((float)(((unsigned char *)wave->data)[wave->channels*i + j] - 127)/256.0f)*32767);
- else if (wave->sampleSize == 32) ((short *)data)[wave->channels*i + j] = (short)((((float *)wave->data)[wave->channels*i + j])*32767);
- }
- else if (sampleSize == 32)
- {
- if (wave->sampleSize == 8) ((float *)data)[wave->channels*i + j] = (float)(((unsigned char *)wave->data)[wave->channels*i + j] - 127)/256.0f;
- else if (wave->sampleSize == 16) ((float *)data)[wave->channels*i + j] = (float)(((short *)wave->data)[wave->channels*i + j])/32767.0f;
- }
- }
- }
-
- wave->sampleSize = sampleSize;
- free(wave->data);
- wave->data = data;
- }
-
- // Format channels (interlaced mode)
- // NOTE: Only supported mono <--> stereo
- if (wave->channels != channels)
- {
- void *data = malloc(wave->sampleCount*wave->sampleSize/8*channels);
-
- if ((wave->channels == 1) && (channels == 2)) // mono ---> stereo (duplicate mono information)
- {
- for (int i = 0; i < wave->sampleCount; i++)
- {
- for (int j = 0; j < channels; j++)
- {
- if (wave->sampleSize == 8) ((unsigned char *)data)[channels*i + j] = ((unsigned char *)wave->data)[i];
- else if (wave->sampleSize == 16) ((short *)data)[channels*i + j] = ((short *)wave->data)[i];
- else if (wave->sampleSize == 32) ((float *)data)[channels*i + j] = ((float *)wave->data)[i];
- }
- }
- }
- else if ((wave->channels == 2) && (channels == 1)) // stereo ---> mono (mix stereo channels)
- {
- for (int i = 0, j = 0; i < wave->sampleCount; i++, j += 2)
- {
- if (wave->sampleSize == 8) ((unsigned char *)data)[i] = (((unsigned char *)wave->data)[j] + ((unsigned char *)wave->data)[j + 1])/2;
- else if (wave->sampleSize == 16) ((short *)data)[i] = (((short *)wave->data)[j] + ((short *)wave->data)[j + 1])/2;
- else if (wave->sampleSize == 32) ((float *)data)[i] = (((float *)wave->data)[j] + ((float *)wave->data)[j + 1])/2.0f;
- }
- }
-
- // TODO: Add/remove additional interlaced channels
-
- wave->channels = channels;
- free(wave->data);
- wave->data = data;
- }
-#endif
}
// Copy a wave to a new wave
@@ -1379,7 +1062,7 @@ void WaveCrop(Wave *wave, int initSample, int finalSample)
void *data = malloc(sampleCount*wave->sampleSize/8*wave->channels);
- memcpy(data, (unsigned char*)wave->data + (initSample*wave->channels*wave->sampleSize/8), sampleCount*wave->channels*wave->sampleSize/8);
+ memcpy(data, (unsigned char *)wave->data + (initSample*wave->channels*wave->sampleSize/8), sampleCount*wave->channels*wave->sampleSize/8);
free(wave->data);
wave->data = data;
@@ -1463,21 +1146,26 @@ Music LoadMusicStream(const char *fileName)
#if defined(SUPPORT_FILEFORMAT_MP3)
else if (IsFileExtension(fileName, ".mp3"))
{
- drmp3_init_file(&music->ctxMp3, fileName, NULL);
+ int result = drmp3_init_file(&music->ctxMp3, fileName, NULL);
- if (music->ctxMp3.framesRemaining <= 0) musicLoaded = false;
+ if (!result) musicLoaded = false;
else
{
- music->stream = InitAudioStream(music->ctxMp3.sampleRate, 16, music->ctxMp3.channels);
- music->totalSamples = (unsigned int)music->ctxMp3.framesRemaining*music->ctxMp3.channels;
+ TraceLog(LOG_INFO, "[%s] MP3 sample rate: %i", fileName, music->ctxMp3.sampleRate);
+ TraceLog(LOG_INFO, "[%s] MP3 bits per sample: %i", fileName, 32);
+ TraceLog(LOG_INFO, "[%s] MP3 channels: %i", fileName, music->ctxMp3.channels);
+ TraceLog(LOG_INFO, "[%s] MP3 frames remaining: %i", fileName, (unsigned int)music->ctxMp3.framesRemaining);
+
+ music->stream = InitAudioStream(music->ctxMp3.sampleRate, 32, music->ctxMp3.channels);
+
+ // TODO: There is not an easy way to compute the total number of samples available
+ // in an MP3, frames size could be variable... we tried with a 60 seconds music... but crashes...
+ music->totalSamples = 60*music->ctxMp3.sampleRate*music->ctxMp3.channels;
music->samplesLeft = music->totalSamples;
music->ctxType = MUSIC_AUDIO_MP3;
music->loopCount = -1; // Infinite loop by default
-
- TraceLog(LOG_DEBUG, "[%s] MP3 total samples: %i", fileName, music->totalSamples);
- TraceLog(LOG_DEBUG, "[%s] MP3 sample rate: %i", fileName, music->ctxMp3.sampleRate);
- //TraceLog(LOG_DEBUG, "[%s] MP3 bits per sample: %i", fileName, music->ctxMp3.bitsPerSample);
- TraceLog(LOG_DEBUG, "[%s] MP3 channels: %i", fileName, music->ctxMp3.channels);
+
+ TraceLog(LOG_INFO, "[%s] MP3 total samples: %i", fileName, music->totalSamples);
}
}
#endif
@@ -1574,8 +1262,8 @@ void UnloadMusicStream(Music music)
// Start music playing (open stream)
void PlayMusicStream(Music music)
{
-#if USE_MINI_AL
AudioBuffer *audioBuffer = (AudioBuffer *)music->stream.audioBuffer;
+
if (audioBuffer == NULL)
{
TraceLog(LOG_ERROR, "PlayMusicStream() : No audio buffer");
@@ -1591,61 +1279,25 @@ void PlayMusicStream(Music music)
PlayAudioStream(music->stream); // <-- This resets the cursor position.
audioBuffer->frameCursorPos = frameCursorPos;
-#else
- alSourcePlay(music->stream.source);
-#endif
}
// Pause music playing
void PauseMusicStream(Music music)
{
-#if USE_MINI_AL
PauseAudioStream(music->stream);
-#else
- alSourcePause(music->stream.source);
-#endif
}
// Resume music playing
void ResumeMusicStream(Music music)
{
-#if USE_MINI_AL
ResumeAudioStream(music->stream);
-#else
- ALenum state;
- alGetSourcei(music->stream.source, AL_SOURCE_STATE, &state);
-
- if (state == AL_PAUSED)
- {
- TraceLog(LOG_INFO, "[AUD ID %i] Resume music stream playing", music->stream.source);
- alSourcePlay(music->stream.source);
- }
-#endif
}
// Stop music playing (close stream)
// TODO: To clear a buffer, make sure they have been already processed!
void StopMusicStream(Music music)
{
-#if USE_MINI_AL
StopAudioStream(music->stream);
-#else
- alSourceStop(music->stream.source);
-
- /*
- // Clear stream buffers
- // WARNING: Queued buffers must have been processed before unqueueing and reloaded with data!!!
- void *pcm = calloc(AUDIO_BUFFER_SIZE*music->stream.sampleSize/8*music->stream.channels, 1);
-
- for (int i = 0; i < MAX_STREAM_BUFFERS; i++)
- {
- //UpdateAudioStream(music->stream, pcm, AUDIO_BUFFER_SIZE); // Update one buffer at a time
- alBufferData(music->stream.buffers[i], music->stream.format, pcm, AUDIO_BUFFER_SIZE*music->stream.sampleSize/8*music->stream.channels, music->stream.sampleRate);
- }
-
- free(pcm);
- */
-#endif
// Restart music context
switch (music->ctxType)
@@ -1673,7 +1325,6 @@ void StopMusicStream(Music music)
// TODO: Make sure buffers are ready for update... check music state
void UpdateMusicStream(Music music)
{
-#if USE_MINI_AL
bool streamEnding = false;
unsigned int subBufferSizeInFrames = ((AudioBuffer *)music->stream.audioBuffer)->bufferSizeInFrames/2;
@@ -1757,139 +1408,24 @@ void UpdateMusicStream(Music music)
// just make sure to play again on window restore
if (IsMusicPlaying(music)) PlayMusicStream(music);
}
-#else
- ALenum state;
- ALint processed = 0;
-
- alGetSourcei(music->stream.source, AL_SOURCE_STATE, &state); // Get music stream state
- alGetSourcei(music->stream.source, AL_BUFFERS_PROCESSED, &processed); // Get processed buffers
-
- if (processed > 0)
- {
- bool streamEnding = false;
-
- // NOTE: Using dynamic allocation because it could require more than 16KB
- void *pcm = calloc(AUDIO_BUFFER_SIZE*music->stream.sampleSize/8*music->stream.channels, 1);
-
- int numBuffersToProcess = processed;
- int samplesCount = 0; // Total size of data steamed in L+R samples for xm floats,
- // individual L or R for ogg shorts
-
- for (int i = 0; i < numBuffersToProcess; i++)
- {
- if (music->samplesLeft >= AUDIO_BUFFER_SIZE) samplesCount = AUDIO_BUFFER_SIZE;
- else samplesCount = music->samplesLeft;
-
- // TODO: Really don't like ctxType thingy...
- switch (music->ctxType)
- {
- case MUSIC_AUDIO_OGG:
- {
- // NOTE: Returns the number of samples to process (be careful! we ask for number of shorts!)
- int numSamplesOgg = stb_vorbis_get_samples_short_interleaved(music->ctxOgg, music->stream.channels, (short *)pcm, samplesCount*music->stream.channels);
-
- } break;
- #if defined(SUPPORT_FILEFORMAT_FLAC)
- case MUSIC_AUDIO_FLAC:
- {
- // NOTE: Returns the number of samples to process
- unsigned int numSamplesFlac = (unsigned int)drflac_read_s16(music->ctxFlac, samplesCount*music->stream.channels, (short *)pcm);
-
- } break;
- #endif
- #if defined(SUPPORT_FILEFORMAT_MP3)
- case MUSIC_AUDIO_MP3:
- {
- // NOTE: Returns the number of samples to process
- unsigned int numSamplesMp3 = (unsigned int)drmp3_read_f32(&music->ctxMp3, samplesCount*music->stream.channels, (float *)pcm);
- } break;
- #endif
- #if defined(SUPPORT_FILEFORMAT_XM)
- case MUSIC_MODULE_XM: jar_xm_generate_samples_16bit(music->ctxXm, pcm, samplesCount); break;
- #endif
- #if defined(SUPPORT_FILEFORMAT_MOD)
- case MUSIC_MODULE_MOD: jar_mod_fillbuffer(&music->ctxMod, pcm, samplesCount, 0); break;
- #endif
- default: break;
- }
-
- UpdateAudioStream(music->stream, pcm, samplesCount);
- music->samplesLeft -= samplesCount;
-
- if (music->samplesLeft <= 0)
- {
- streamEnding = true;
- break;
- }
- }
-
- // Free allocated pcm data
- free(pcm);
-
- // Reset audio stream for looping
- if (streamEnding)
- {
- StopMusicStream(music); // Stop music (and reset)
-
- // Decrease loopCount to stop when required
- if (music->loopCount > 0)
- {
- music->loopCount--; // Decrease loop count
- PlayMusicStream(music); // Play again
- }
- else
- {
- if (music->loopCount == -1)
- {
- PlayMusicStream(music);
- }
- }
- }
- else
- {
- // NOTE: In case window is minimized, music stream is stopped,
- // just make sure to play again on window restore
- if (state != AL_PLAYING) PlayMusicStream(music);
- }
- }
-#endif
}
// Check if any music is playing
bool IsMusicPlaying(Music music)
{
-#if USE_MINI_AL
return IsAudioStreamPlaying(music->stream);
-#else
- bool playing = false;
- ALint state;
-
- alGetSourcei(music->stream.source, AL_SOURCE_STATE, &state);
-
- if (state == AL_PLAYING) playing = true;
-
- return playing;
-#endif
}
// Set volume for music
void SetMusicVolume(Music music, float volume)
{
-#if USE_MINI_AL
SetAudioStreamVolume(music->stream, volume);
-#else
- alSourcef(music->stream.source, AL_GAIN, volume);
-#endif
}
// Set pitch for music
void SetMusicPitch(Music music, float pitch)
{
-#if USE_MINI_AL
SetAudioStreamPitch(music->stream, pitch);
-#else
- alSourcef(music->stream.source, AL_PITCH, pitch);
-#endif
}
// Set music loop count (loop repeats)
@@ -1935,12 +1471,10 @@ AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, un
stream.channels = 1; // Fallback to mono channel
}
-
-#if USE_MINI_AL
mal_format formatIn = ((stream.sampleSize == 8) ? mal_format_u8 : ((stream.sampleSize == 16) ? mal_format_s16 : mal_format_f32));
// The size of a streaming buffer must be at least double the size of a period.
- unsigned int periodSize = device.bufferSizeInFrames / device.periods;
+ unsigned int periodSize = device.bufferSizeInFrames/device.periods;
unsigned int subBufferSize = AUDIO_BUFFER_SIZE;
if (subBufferSize < periodSize) subBufferSize = periodSize;
@@ -1951,54 +1485,8 @@ AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, un
return stream;
}
- audioBuffer->looping = true; // Always loop for streaming buffers.
+ audioBuffer->looping = true; // Always loop for streaming buffers.
stream.audioBuffer = audioBuffer;
-#else
- // Setup OpenAL format
- if (stream.channels == 1)
- {
- switch (sampleSize)
- {
- case 8: stream.format = AL_FORMAT_MONO8; break;
- case 16: stream.format = AL_FORMAT_MONO16; break;
- case 32: stream.format = AL_FORMAT_MONO_FLOAT32; break; // Requires OpenAL extension: AL_EXT_FLOAT32
- default: TraceLog(LOG_WARNING, "Init audio stream: Sample size not supported: %i", sampleSize); break;
- }
- }
- else if (stream.channels == 2)
- {
- switch (sampleSize)
- {
- case 8: stream.format = AL_FORMAT_STEREO8; break;
- case 16: stream.format = AL_FORMAT_STEREO16; break;
- case 32: stream.format = AL_FORMAT_STEREO_FLOAT32; break; // Requires OpenAL extension: AL_EXT_FLOAT32
- default: TraceLog(LOG_WARNING, "Init audio stream: Sample size not supported: %i", sampleSize); break;
- }
- }
-
- // Create an audio source
- alGenSources(1, &stream.source);
- alSourcef(stream.source, AL_PITCH, 1.0f);
- alSourcef(stream.source, AL_GAIN, 1.0f);
- alSource3f(stream.source, AL_POSITION, 0.0f, 0.0f, 0.0f);
- alSource3f(stream.source, AL_VELOCITY, 0.0f, 0.0f, 0.0f);
-
- // Create Buffers (double buffering)
- alGenBuffers(MAX_STREAM_BUFFERS, stream.buffers);
-
- // Initialize buffer with zeros by default
- // NOTE: Using dynamic allocation because it requires more than 16KB
- void *pcm = calloc(AUDIO_BUFFER_SIZE*stream.sampleSize/8*stream.channels, 1);
-
- for (int i = 0; i < MAX_STREAM_BUFFERS; i++)
- {
- alBufferData(stream.buffers[i], stream.format, pcm, AUDIO_BUFFER_SIZE*stream.sampleSize/8*stream.channels, stream.sampleRate);
- }
-
- free(pcm);
-
- alSourceQueueBuffers(stream.source, MAX_STREAM_BUFFERS, stream.buffers);
-#endif
TraceLog(LOG_INFO, "[AUD ID %i] Audio stream loaded successfully (%i Hz, %i bit, %s)", stream.source, stream.sampleRate, stream.sampleSize, (stream.channels == 1) ? "Mono" : "Stereo");
@@ -2008,28 +1496,7 @@ AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, un
// Close audio stream and free memory
void CloseAudioStream(AudioStream stream)
{
-#if USE_MINI_AL
DeleteAudioBuffer((AudioBuffer *)stream.audioBuffer);
-#else
- // Stop playing channel
- alSourceStop(stream.source);
-
- // Flush out all queued buffers
- int queued = 0;
- alGetSourcei(stream.source, AL_BUFFERS_QUEUED, &queued);
-
- ALuint buffer = 0;
-
- while (queued > 0)
- {
- alSourceUnqueueBuffers(stream.source, 1, &buffer);
- queued--;
- }
-
- // Delete source and buffers
- alDeleteSources(1, &stream.source);
- alDeleteBuffers(MAX_STREAM_BUFFERS, stream.buffers);
-#endif
TraceLog(LOG_INFO, "[AUD ID %i] Unloaded audio stream data", stream.source);
}
@@ -2039,7 +1506,6 @@ void CloseAudioStream(AudioStream stream)
// NOTE 2: To unqueue a buffer it needs to be processed: IsAudioBufferProcessed()
void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount)
{
-#if USE_MINI_AL
AudioBuffer *audioBuffer = (AudioBuffer *)stream.audioBuffer;
if (audioBuffer == NULL)
{
@@ -2050,6 +1516,7 @@ void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount)
if (audioBuffer->isSubBufferProcessed[0] || audioBuffer->isSubBufferProcessed[1])
{
mal_uint32 subBufferToUpdate;
+
if (audioBuffer->isSubBufferProcessed[0] && audioBuffer->isSubBufferProcessed[1])
{
// Both buffers are available for updating. Update the first one and make sure the cursor is moved back to the front.
@@ -2069,6 +1536,7 @@ void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount)
if (subBufferSizeInFrames >= (mal_uint32)samplesCount)
{
mal_uint32 framesToWrite = subBufferSizeInFrames;
+
if (framesToWrite > (mal_uint32)samplesCount) framesToWrite = (mal_uint32)samplesCount;
mal_uint32 bytesToWrite = framesToWrite*stream.channels*(stream.sampleSize/8);
@@ -2076,6 +1544,7 @@ void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount)
// Any leftover frames should be filled with zeros.
mal_uint32 leftoverFrameCount = subBufferSizeInFrames - framesToWrite;
+
if (leftoverFrameCount > 0)
{
memset(subBuffer + bytesToWrite, 0, leftoverFrameCount*stream.channels*(stream.sampleSize/8));
@@ -2094,24 +1563,11 @@ void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount)
TraceLog(LOG_ERROR, "Audio buffer not available for updating");
return;
}
-#else
- ALuint buffer = 0;
- alSourceUnqueueBuffers(stream.source, 1, &buffer);
-
- // Check if any buffer was available for unqueue
- if (alGetError() != AL_INVALID_VALUE)
- {
- alBufferData(buffer, stream.format, data, samplesCount*stream.sampleSize/8*stream.channels, stream.sampleRate);
- alSourceQueueBuffers(stream.source, 1, &buffer);
- }
- else TraceLog(LOG_WARNING, "[AUD ID %i] Audio buffer not available for unqueuing", stream.source);
-#endif
}
// Check if any audio stream buffers requires refill
bool IsAudioBufferProcessed(AudioStream stream)
{
-#if USE_MINI_AL
AudioBuffer *audioBuffer = (AudioBuffer *)stream.audioBuffer;
if (audioBuffer == NULL)
{
@@ -2120,92 +1576,46 @@ bool IsAudioBufferProcessed(AudioStream stream)
}
return audioBuffer->isSubBufferProcessed[0] || audioBuffer->isSubBufferProcessed[1];
-#else
- ALint processed = 0;
-
- // Determine if music stream is ready to be written
- alGetSourcei(stream.source, AL_BUFFERS_PROCESSED, &processed);
-
- return (processed > 0);
-#endif
}
// Play audio stream
void PlayAudioStream(AudioStream stream)
{
-#if USE_MINI_AL
PlayAudioBuffer((AudioBuffer *)stream.audioBuffer);
-#else
- alSourcePlay(stream.source);
-#endif
}
// Play audio stream
void PauseAudioStream(AudioStream stream)
{
-#if USE_MINI_AL
PauseAudioBuffer((AudioBuffer *)stream.audioBuffer);
-#else
- alSourcePause(stream.source);
-#endif
}
// Resume audio stream playing
void ResumeAudioStream(AudioStream stream)
{
-#if USE_MINI_AL
ResumeAudioBuffer((AudioBuffer *)stream.audioBuffer);
-#else
- ALenum state;
- alGetSourcei(stream.source, AL_SOURCE_STATE, &state);
-
- if (state == AL_PAUSED) alSourcePlay(stream.source);
-#endif
}
// Check if audio stream is playing.
bool IsAudioStreamPlaying(AudioStream stream)
{
-#if USE_MINI_AL
return IsAudioBufferPlaying((AudioBuffer *)stream.audioBuffer);
-#else
- bool playing = false;
- ALint state;
-
- alGetSourcei(stream.source, AL_SOURCE_STATE, &state);
-
- if (state == AL_PLAYING) playing = true;
-
- return playing;
-#endif
}
// Stop audio stream
void StopAudioStream(AudioStream stream)
{
-#if USE_MINI_AL
StopAudioBuffer((AudioBuffer *)stream.audioBuffer);
-#else
- alSourceStop(stream.source);
-#endif
}
void SetAudioStreamVolume(AudioStream stream, float volume)
{
-#if USE_MINI_AL
SetAudioBufferVolume((AudioBuffer *)stream.audioBuffer, volume);
-#else
- alSourcef(stream.source, AL_GAIN, volume);
-#endif
}
void SetAudioStreamPitch(AudioStream stream, float pitch)
{
-#if USE_MINI_AL
SetAudioBufferPitch((AudioBuffer *)stream.audioBuffer, pitch);
-#else
- alSourcef(stream.source, AL_PITCH, pitch);
-#endif
}
//----------------------------------------------------------------------------------
@@ -2398,17 +1808,17 @@ static Wave LoadFLAC(const char *fileName)
// NOTE: Using dr_mp3 library
static Wave LoadMP3(const char *fileName)
{
- Wave wave;
+ Wave wave = { 0 };
// Decode an entire MP3 file in one go
- uint64_t totalSampleCount;
- drmp3_config *config;
- wave.data = drmp3_open_and_decode_file_f32(fileName, config, &totalSampleCount);
+ uint64_t totalSampleCount = 0;
+ drmp3_config config = { 0 };
+ wave.data = drmp3_open_and_decode_file_f32(fileName, &config, &totalSampleCount);
- wave.channels = config->outputChannels;
- wave.sampleRate = config->outputSampleRate;
- wave.sampleCount = (int)totalSampleCount/wave.channels;
- wave.sampleSize = 16;
+ wave.channels = config.outputChannels;
+ wave.sampleRate = config.outputSampleRate;
+ wave.sampleCount = (int)totalSampleCount;
+ wave.sampleSize = 32;
// NOTE: Only support up to 2 channels (mono, stereo)
if (wave.channels > 2) TraceLog(LOG_WARNING, "[%s] MP3 channels number (%i) not supported", fileName, wave.channels);
diff --git a/src/config.h b/src/config.h
index c2238a79..47cc8068 100644
--- a/src/config.h
+++ b/src/config.h
@@ -25,7 +25,7 @@
*
**********************************************************************************************/
-#define RAYLIB_VERSION "2.0"
+#define RAYLIB_VERSION "2.1-dev"
// Edit to control what features Makefile'd raylib is compiled with
#if defined(RAYLIB_CMAKE)
diff --git a/src/config.h.in b/src/config.h.in
index 742067ce..b0e62480 100644
--- a/src/config.h.in
+++ b/src/config.h.in
@@ -1,7 +1,5 @@
/* config.h.in */
-#cmakedefine USE_OPENAL_BACKEND 1
-
// core.c
/* Camera module is included (camera.h) and multiple predefined cameras are available: free, 1st/3rd person, orbital */
#cmakedefine SUPPORT_CAMERA_SYSTEM 1
diff --git a/src/core.c b/src/core.c
index fc1a5a09..ffbf110e 100644
--- a/src/core.c
+++ b/src/core.c
@@ -123,35 +123,55 @@
#include <string.h> // Required for: strrchr(), strcmp()
//#include <errno.h> // Macros for reporting and retrieving error conditions through error codes
#include <ctype.h> // Required for: tolower() [Used in IsFileExtension()]
+#include <sys/stat.h> // Required for stat() [Used in GetLastWriteTime()]
+
+#if defined(PLATFORM_DESKTOP) && defined(_WIN32) && defined(_MSC_VER)
+ #include "external/dirent.h" // Required for: DIR, opendir(), closedir() [Used in GetDirectoryFiles()]
+#else
+ #include <dirent.h> // Required for: DIR, opendir(), closedir() [Used in GetDirectoryFiles()]
+#endif
#if defined(_WIN32)
#include <direct.h> // Required for: _getch(), _chdir()
#define GETCWD _getcwd // NOTE: MSDN recommends not to use getcwd(), chdir()
#define CHDIR _chdir
+ #include <io.h> // Required for _access() [Used in FileExists()]
#else
- #include "unistd.h" // Required for: getch(), chdir() (POSIX)
+ #include "unistd.h" // Required for: getch(), chdir() (POSIX), access()
#define GETCWD getcwd
#define CHDIR chdir
#endif
-#if defined(__linux__) || defined(PLATFORM_WEB)
- #include <sys/time.h> // Required for: timespec, nanosleep(), select() - POSIX
-#elif defined(__APPLE__)
- #include <unistd.h> // Required for: usleep()
-#endif
-
-#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
- #if defined(PLATFORM_WEB)
- #define GLFW_INCLUDE_ES2
- #endif
- //#define GLFW_INCLUDE_NONE // Disable the standard OpenGL header inclusion on GLFW3
+#if defined(PLATFORM_DESKTOP)
+ #define GLFW_INCLUDE_NONE // Disable the standard OpenGL header inclusion on GLFW3
+ // NOTE: Already provided by rlgl implementation (on glad.h)
#include <GLFW/glfw3.h> // GLFW3 library: Windows, OpenGL context and Input management
// NOTE: GLFW3 already includes gl.h (OpenGL) headers
- #if !defined(SUPPORT_BUSY_WAIT_LOOP) && defined(_WIN32)
- // NOTE: Those functions require linking with winmm library
- unsigned int __stdcall timeBeginPeriod(unsigned int uPeriod);
- unsigned int __stdcall timeEndPeriod(unsigned int uPeriod);
+ // Support retrieving native window handlers
+ #if defined(_WIN32)
+ #define GLFW_EXPOSE_NATIVE_WIN32
+ #include <GLFW/glfw3native.h> // WARNING: It requires customization to avoid windows.h inclusion!
+
+ #if !defined(SUPPORT_BUSY_WAIT_LOOP)
+ // NOTE: Those functions require linking with winmm library
+ unsigned int __stdcall timeBeginPeriod(unsigned int uPeriod);
+ unsigned int __stdcall timeEndPeriod(unsigned int uPeriod);
+ #endif
+ #elif defined(__linux__)
+ #include <sys/time.h> // Required for: timespec, nanosleep(), select() - POSIX
+
+ //#define GLFW_EXPOSE_NATIVE_X11 // WARNING: Exposing Xlib.h > X.h results in dup symbols for Font type
+ //#define GLFW_EXPOSE_NATIVE_WAYLAND
+ //#define GLFW_EXPOSE_NATIVE_MIR
+ #include <GLFW/glfw3native.h> // Required for: glfwGetX11Window()
+ #elif defined(__APPLE__)
+ #include <unistd.h> // Required for: usleep()
+ #include <objc/message.h> // Required for: objc_msgsend(), sel_registerName()
+
+ //#define GLFW_EXPOSE_NATIVE_COCOA // WARNING: Fails due to type redefinition
+ #define GLFW_EXPOSE_NATIVE_NSGL
+ #include <GLFW/glfw3native.h> // Required for: glfwGetCocoaWindow(), glfwGetNSGLContext()
#endif
#endif
@@ -169,6 +189,7 @@
#include <unistd.h> // POSIX standard function definitions - read(), close(), STDIN_FILENO
#include <termios.h> // POSIX terminal control definitions - tcgetattr(), tcsetattr()
#include <pthread.h> // POSIX threads management (mouse input)
+ #include <dirent.h> // POSIX directory browsing
#include <sys/ioctl.h> // UNIX System call for device-specific input/output operations - ioctl()
#include <linux/kd.h> // Linux: KDSKBMODE, K_MEDIUMRAM constants definition
@@ -189,19 +210,24 @@
#endif
#if defined(PLATFORM_WEB)
- #include <emscripten/emscripten.h>
- #include <emscripten/html5.h>
+ #define GLFW_INCLUDE_ES2 // GLFW3: Enable OpenGL ES 2.0 (translated to WebGL)
+ #include <GLFW/glfw3.h> // GLFW3 library: Windows, OpenGL context and Input management
+ #include <sys/time.h> // Required for: timespec, nanosleep(), select() - POSIX
+
+ #include <emscripten/emscripten.h> // Emscripten library - LLVM to JavaScript compiler
+ #include <emscripten/html5.h> // Emscripten HTML5 library
#endif
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
#if defined(PLATFORM_RPI)
+ #define USE_LAST_TOUCH_DEVICE // When multiple touchscreens are connected, only use the one with the highest event<N> number
+
// Old device inputs system
#define DEFAULT_KEYBOARD_DEV STDIN_FILENO // Standard input
- #define DEFAULT_MOUSE_DEV "/dev/input/mouse0" // Mouse input
- #define DEFAULT_TOUCH_DEV "/dev/input/event4" // Touch input virtual device (created by ts_uinput)
#define DEFAULT_GAMEPAD_DEV "/dev/input/js" // Gamepad input (base dev for all gamepads: js0, js1, ...)
+ #define DEFAULT_EVDEV_PATH "/dev/input/" // Path to the linux input events
// New device input events (evdev) (must be detected)
//#define DEFAULT_KEYBOARD_DEV "/dev/input/eventN"
@@ -225,14 +251,46 @@
//----------------------------------------------------------------------------------
// Global Variables Definition
//----------------------------------------------------------------------------------
+
+// Window/Graphics related variables
+//-----------------------------------------------------------------------------------
#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
static GLFWwindow *window; // Native window (graphic device)
#endif
-
static bool windowReady = false; // Check if window has been initialized successfully
static bool windowMinimized = false; // Check if window has been minimized
static const char *windowTitle = NULL; // Window text title...
+#if defined(__APPLE__)
+static int windowNeedsUpdating = 2; // Times the Cocoa window needs to be updated initially
+#endif
+
+static unsigned int displayWidth, displayHeight;// Display width and height (monitor, device-screen, LCD, ...)
+static int screenWidth, screenHeight; // Screen width and height (used render area)
+static int renderWidth, renderHeight; // Framebuffer width and height (render area, including black bars if required)
+static int renderOffsetX = 0; // Offset X from render area (must be divided by 2)
+static int renderOffsetY = 0; // Offset Y from render area (must be divided by 2)
+static bool fullscreen = false; // Fullscreen mode (useful only for PLATFORM_DESKTOP)
+static Matrix downscaleView; // Matrix to downscale view (in case screen size bigger than display size)
+
+#if defined(PLATFORM_RPI)
+static EGL_DISPMANX_WINDOW_T nativeWindow; // Native window (graphic device)
+#endif
+
+#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_UWP)
+static EGLDisplay display; // Native display device (physical screen connection)
+static EGLSurface surface; // Surface to draw on, framebuffers (connected to context)
+static EGLContext context; // Graphic context, mode in which drawing can be done
+static EGLConfig config; // Graphic config
+static uint64_t baseTime; // Base time measure for hi-res timer
+static bool windowShouldClose = false; // Flag to set window for closing
+#endif
+
+#if defined(PLATFORM_UWP)
+extern EGLNativeWindowType uwpWindow; // Native EGL window handler for UWP (external, defined in UWP App)
+#endif
+//-----------------------------------------------------------------------------------
+
#if defined(PLATFORM_ANDROID)
static struct android_app *androidApp; // Android activity
static struct android_poll_source *source; // Android events polling source
@@ -243,104 +301,97 @@ static bool appEnabled = true; // Used to detec if app is activ
static bool contextRebindRequired = false; // Used to know context rebind required
#endif
-#if defined(PLATFORM_RPI)
-static EGL_DISPMANX_WINDOW_T nativeWindow; // Native window (graphic device)
+// Inputs related variables
+//-----------------------------------------------------------------------------------
+// Keyboard states
+static char previousKeyState[512] = { 0 }; // Registers previous frame key state
+static char currentKeyState[512] = { 0 }; // Registers current frame key state
+static int lastKeyPressed = -1; // Register last key pressed
+static int exitKey = KEY_ESCAPE; // Default exit key (ESC)
-// Keyboard input variables
+#if defined(PLATFORM_RPI)
// NOTE: For keyboard we will use the standard input (but reconfigured...)
static struct termios defaultKeyboardSettings; // Used to store default keyboard settings
static int defaultKeyboardMode; // Used to store default keyboard mode
+#endif
-// Mouse input variables
-static int mouseStream = -1; // Mouse device file descriptor
-static bool mouseReady = false; // Flag to know if mouse is ready
-static pthread_t mouseThreadId; // Mouse reading thread id
-
-// Touch input variables
-static int touchStream = -1; // Touch device file descriptor
-static bool touchReady = false; // Flag to know if touch interface is ready
-static pthread_t touchThreadId; // Touch reading thread id
+// Mouse states
+static Vector2 mousePosition; // Mouse position on screen
+static float mouseScale = 1.0f; // Mouse default scale
+static bool cursorHidden = false; // Track if cursor is hidden
+static bool cursorOnScreen = false; // Tracks if cursor is inside client area
+static Vector2 touchPosition[MAX_TOUCH_POINTS]; // Touch position on screen
-// Gamepad input variables
-static int gamepadStream[MAX_GAMEPADS] = { -1 };// Gamepad device file descriptor
-static pthread_t gamepadThreadId; // Gamepad reading thread id
-static char gamepadName[64]; // Gamepad name holder
+#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB) || defined(PLATFORM_UWP)
+static char previousMouseState[3] = { 0 }; // Registers previous mouse button state
+static char currentMouseState[3] = { 0 }; // Registers current mouse button state
+static int previousMouseWheelY = 0; // Registers previous mouse wheel variation
+static int currentMouseWheelY = 0; // Registers current mouse wheel variation
#endif
-#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_UWP)
-static EGLDisplay display; // Native display device (physical screen connection)
-static EGLSurface surface; // Surface to draw on, framebuffers (connected to context)
-static EGLContext context; // Graphic context, mode in which drawing can be done
-static EGLConfig config; // Graphic config
-static uint64_t baseTime; // Base time measure for hi-res timer
-static bool windowShouldClose = false; // Flag to set window for closing
-#endif
+#if defined(PLATFORM_RPI)
+static char currentMouseStateEvdev[3] = { 0 }; // Holds the new mouse state for the next polling event to grab (Can't be written directly due to multithreading, app could miss the update)
+
+typedef struct {
+ pthread_t threadId; // Event reading thread id
+ int fd; // File descriptor to the device it is assigned to
+ int eventNum; // Number of 'event<N>' device
+ Rectangle absRange; // Range of values for absolute pointing devices (touchscreens)
+ int touchSlot; // Hold the touch slot number of the currently being sent multitouch block
+ bool isMouse; // True if device supports relative X Y movements
+ bool isTouch; // True if device supports absolute X Y movements and has BTN_TOUCH
+ bool isMultitouch; // True if device supports multiple absolute movevents and has BTN_TOUCH
+ bool isKeyboard; // True if device has letter keycodes
+ bool isGamepad; // True if device has gamepad buttons
+} InputEventWorker;
+
+static InputEventWorker eventWorkers[10]; // List of worker threads for every monitored "/dev/input/event<N>"
-#if defined(PLATFORM_UWP)
-extern EGLNativeWindowType uwpWindow; // Native EGL window handler for UWP (external, defined in UWP App)
+#endif
+#if defined(PLATFORM_WEB)
+static bool toggleCursorLock = false; // Ask for cursor pointer lock on next click
#endif
-// Screen related variables
-static unsigned int displayWidth, displayHeight; // Display width and height (monitor, device-screen, LCD, ...)
-static int screenWidth, screenHeight; // Screen width and height (used render area)
-static int renderWidth, renderHeight; // Framebuffer width and height (render area, including black bars if required)
-static int renderOffsetX = 0; // Offset X from render area (must be divided by 2)
-static int renderOffsetY = 0; // Offset Y from render area (must be divided by 2)
-static bool fullscreen = false; // Fullscreen mode (useful only for PLATFORM_DESKTOP)
-static Matrix downscaleView; // Matrix to downscale view (in case screen size bigger than display size)
-
-static bool cursorHidden = false; // Track if cursor is hidden
-static bool cursorOnScreen = false; // Tracks if cursor is inside client area
+// Gamepads states
+static int lastGamepadButtonPressed = -1; // Register last gamepad button pressed
+static int gamepadAxisCount = 0; // Register number of available gamepad axis
#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB) || defined(PLATFORM_UWP)
-// Register mouse states
-static char previousMouseState[3] = { 0 }; // Registers previous mouse button state
-static char currentMouseState[3] = { 0 }; // Registers current mouse button state
-static int previousMouseWheelY = 0; // Registers previous mouse wheel variation
-static int currentMouseWheelY = 0; // Registers current mouse wheel variation
-
-// Register gamepads states
static bool gamepadReady[MAX_GAMEPADS] = { false }; // Flag to know if gamepad is ready
static float gamepadAxisState[MAX_GAMEPADS][MAX_GAMEPAD_AXIS]; // Gamepad axis state
static char previousGamepadState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Previous gamepad buttons state
static char currentGamepadState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Current gamepad buttons state
-
-// Keyboard configuration
-static int exitKey = KEY_ESCAPE; // Default exit key (ESC)
-#endif
-
-// Register keyboard states
-static char previousKeyState[512] = { 0 }; // Registers previous frame key state
-static char currentKeyState[512] = { 0 }; // Registers current frame key state
-
-static int lastKeyPressed = -1; // Register last key pressed
-static int lastGamepadButtonPressed = -1; // Register last gamepad button pressed
-static int gamepadAxisCount = 0; // Register number of available gamepad axis
-
-static Vector2 mousePosition; // Mouse position on screen
-static float mouseScale = 1.0f; // Mouse default scale
-
-#if defined(PLATFORM_WEB)
-static bool toggleCursorLock = false; // Ask for cursor pointer lock on next click
#endif
-static Vector2 touchPosition[MAX_TOUCH_POINTS]; // Touch position on screen
-
-#if defined(PLATFORM_DESKTOP)
-static char **dropFilesPath; // Store dropped files paths as strings
-static int dropFilesCount = 0; // Count stored strings
+#if defined(PLATFORM_RPI)
+static int gamepadStream[MAX_GAMEPADS] = { -1 };// Gamepad device file descriptor
+static pthread_t gamepadThreadId; // Gamepad reading thread id
+static char gamepadName[64]; // Gamepad name holder
#endif
+//-----------------------------------------------------------------------------------
+// Timming system variables
+//-----------------------------------------------------------------------------------
static double currentTime = 0.0; // Current time measure
static double previousTime = 0.0; // Previous time measure
static double updateTime = 0.0; // Time measure for frame update
static double drawTime = 0.0; // Time measure for frame draw
static double frameTime = 0.0; // Time measure for one frame
static double targetTime = 0.0; // Desired time for one frame, if 0 not applied
+//-----------------------------------------------------------------------------------
+// Config internal variables
+//-----------------------------------------------------------------------------------
static unsigned char configFlags = 0; // Configuration flags (bit based)
static bool showLogo = false; // Track if showing logo at init is enabled
+#if defined(PLATFORM_DESKTOP)
+static char **dropFilesPath; // Store dropped files paths as strings
+static int dropFilesCount = 0; // Count dropped files strings
+#endif
+static char **dirFilesPath; // Store directory files paths as strings
+static int dirFilesCount = 0; // Count directory files strings
+
#if defined(SUPPORT_SCREEN_CAPTURE)
static int screenshotCounter = 0; // Screenshots counter
#endif
@@ -349,6 +400,7 @@ static int screenshotCounter = 0; // Screenshots counter
static int gifFramesCounter = 0; // GIF frames counter
static bool gifRecording = false; // GIF recording state
#endif
+//-----------------------------------------------------------------------------------
//----------------------------------------------------------------------------------
// Other Modules Functions Declaration (required by core)
@@ -362,15 +414,18 @@ extern void UnloadDefaultFont(void); // [Module: text] Unloads default fo
// Module specific Functions Declaration
//----------------------------------------------------------------------------------
static bool InitGraphicsDevice(int width, int height); // Initialize graphics device
-static void SetupFramebufferSize(int displayWidth, int displayHeight);
+static void SetupFramebuffer(int width, int height); // Setup main framebuffer
+static void SetupViewport(void); // Set viewport parameters
+static void SwapBuffers(void); // Copy back buffer to front buffers
+
static void InitTimer(void); // Initialize timer
static void Wait(float ms); // Wait for some milliseconds (stop program execution)
+
static bool GetKeyStatus(int key); // Returns if a key has been pressed
static bool GetMouseButtonStatus(int button); // Returns if a mouse button has been pressed
static void PollInputEvents(void); // Register user events
-static void SwapBuffers(void); // Copy back buffer to front buffers
+
static void LogoAnimation(void); // Plays raylib logo appearing animation
-static void SetupViewport(void); // Set viewport parameters
#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error
@@ -383,7 +438,6 @@ static void CursorEnterCallback(GLFWwindow *window, int enter);
static void WindowSizeCallback(GLFWwindow *window, int width, int height); // GLFW3 WindowSize Callback, runs when window is resized
static void WindowIconifyCallback(GLFWwindow *window, int iconified); // GLFW3 WindowIconify Callback, runs when window is minimized/restored
#endif
-
#if defined(PLATFORM_DESKTOP)
static void WindowDropCallback(GLFWwindow *window, int count, const char **paths); // GLFW3 Window Drop Callback, runs when drop files into window
#endif
@@ -406,9 +460,8 @@ static void InitKeyboard(void); // Init raw keyboard sys
static void ProcessKeyboard(void); // Process keyboard events
static void RestoreKeyboard(void); // Restore keyboard system
static void InitMouse(void); // Mouse initialization (including mouse thread)
-static void *MouseThread(void *arg); // Mouse reading thread
-static void InitTouch(void); // Touch device initialization (including touch thread)
-static void *TouchThread(void *arg); // Touch device reading thread
+static void EventThreadSpawn(char *device); // Indetifies a input device and spawns a thread to handle it if needed
+static void *EventThread(void *arg); // Input device event reading thread
static void InitGamepad(void); // Init raw gamepad input
static void *GamepadThread(void *arg); // Mouse reading thread
#endif
@@ -437,7 +490,7 @@ void android_main(struct android_app *app)
androidApp = app;
// TODO: Should we maybe report != 0 return codes somewhere?
- (void)main(1, (char*[]) { arg0, NULL });
+ (void)main(1, (char *[]) { arg0, NULL });
}
// TODO: Add this to header (if apps really need it)
@@ -525,7 +578,6 @@ void InitWindow(int width, int height, const char *title)
#if defined(PLATFORM_RPI)
// Init raw input system
InitMouse(); // Mouse init
- InitTouch(); // Touch init
InitKeyboard(); // Keyboard init
InitGamepad(); // Gamepad init
#endif
@@ -620,8 +672,13 @@ void CloseWindow(void)
windowShouldClose = true; // Added to force threads to exit when the close window is called
- pthread_join(mouseThreadId, NULL);
- pthread_join(touchThreadId, NULL);
+ for (int i = 0; i < sizeof(eventWorkers)/sizeof(InputEventWorker); ++i)
+ {
+ if (eventWorkers[i].threadId == 0)
+ {
+ pthread_join(eventWorkers[i].threadId, NULL);
+ }
+ }
pthread_join(gamepadThreadId, NULL);
#endif
@@ -730,7 +787,7 @@ void SetWindowMonitor(int monitor)
{
#if defined(PLATFORM_DESKTOP)
int monitorCount;
- GLFWmonitor** monitors = glfwGetMonitors(&monitorCount);
+ GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
if ((monitor >= 0) && (monitor < monitorCount))
{
@@ -770,6 +827,124 @@ int GetScreenHeight(void)
return screenHeight;
}
+// Get native window handle
+void *GetWindowHandle(void)
+{
+#if defined(_WIN32)
+ // NOTE: Returned handle is: void *HWND (windows.h)
+ return glfwGetWin32Window(window);
+#elif defined(__linux__)
+ // NOTE: Returned handle is: unsigned long Window (X.h)
+ // typedef unsigned long XID;
+ // typedef XID Window;
+ //unsigned long id = (unsigned long)glfwGetX11Window(window);
+ return NULL; // TODO: Find a way to return value... cast to void *?
+#elif defined(__APPLE__)
+ // NOTE: Returned handle is: (objc_object *)
+ return NULL; // TODO: return (void *)glfwGetCocoaWindow(window);
+#else
+ return NULL;
+#endif
+}
+
+// Get number of monitors
+int GetMonitorCount(void)
+{
+#if defined(PLATFORM_DESKTOP)
+ int monitorCount;
+ glfwGetMonitors(&monitorCount);
+ return monitorCount;
+#else
+ return 1;
+#endif
+}
+
+// Get primary monitor width
+int GetMonitorWidth(int monitor)
+{
+#if defined(PLATFORM_DESKTOP)
+ int monitorCount;
+ GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
+
+ if ((monitor >= 0) && (monitor < monitorCount))
+ {
+ const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]);
+ return mode->width;
+ }
+ else TraceLog(LOG_WARNING, "Selected monitor not found");
+#endif
+ return 0;
+}
+
+// Get primary monitor width
+int GetMonitorHeight(int monitor)
+{
+#if defined(PLATFORM_DESKTOP)
+ int monitorCount;
+ GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
+
+ if ((monitor >= 0) && (monitor < monitorCount))
+ {
+ const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]);
+ return mode->height;
+ }
+ else TraceLog(LOG_WARNING, "Selected monitor not found");
+#endif
+ return 0;
+}
+
+// Get primary montior physical width in millimetres
+int GetMonitorPhysicalWidth(int monitor)
+{
+#if defined(PLATFORM_DESKTOP)
+ int monitorCount;
+ GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
+
+ if ((monitor >= 0) && (monitor < monitorCount))
+ {
+ int physicalWidth;
+ glfwGetMonitorPhysicalSize(monitors[monitor], &physicalWidth, NULL);
+ return physicalWidth;
+ }
+ else TraceLog(LOG_WARNING, "Selected monitor not found");
+#endif
+ return 0;
+}
+
+// Get primary monitor physical height in millimetres
+int GetMonitorPhysicalHeight(int monitor)
+{
+#if defined(PLATFORM_DESKTOP)
+ int monitorCount;
+ GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
+
+ if ((monitor >= 0) && (monitor < monitorCount))
+ {
+ int physicalHeight;
+ glfwGetMonitorPhysicalSize(monitors[monitor], NULL, &physicalHeight);
+ return physicalHeight;
+ }
+ else TraceLog(LOG_WARNING, "Selected monitor not found");
+#endif
+ return 0;
+}
+
+// Get the human-readable, UTF-8 encoded name of the primary monitor
+const char *GetMonitorName(int monitor)
+{
+#if defined(PLATFORM_DESKTOP)
+ int monitorCount;
+ GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
+
+ if ((monitor >= 0) && (monitor < monitorCount))
+ {
+ return glfwGetMonitorName(monitors[monitor]);
+ }
+ else TraceLog(LOG_WARNING, "Selected monitor not found");
+#endif
+ return "";
+}
+
// Show mouse cursor
void ShowCursor()
{
@@ -1306,6 +1481,21 @@ void TakeScreenshot(const char *fileName)
#endif
}
+// Check if the file exists
+bool FileExists(const char *fileName)
+{
+ bool result = false;
+
+#if defined(_WIN32)
+ if (_access(fileName, 0) != -1)
+#else
+ if (access(fileName, F_OK) != -1)
+#endif
+ result = true;
+
+ return result;
+}
+
// Check file extension
bool IsFileExtension(const char *fileName, const char *ext)
{
@@ -1366,6 +1556,41 @@ const char *GetFileName(const char *filePath)
return fileName + 1;
}
+// Get filename string without extension (memory should be freed)
+const char *GetFileNameWithoutExt(const char *filePath)
+{
+ char *result, *lastDot, *lastSep;
+
+ char nameDot = '.'; // Default filename to extension separator character
+ char pathSep = '/'; // Default filepath separator character
+
+ // Error checks and allocate string
+ if (filePath == NULL) return NULL;
+
+ // Try to allocate new string, same size as original
+ // NOTE: By default strlen() does not count the '\0' character
+ if ((result = malloc(strlen(filePath) + 1)) == NULL) return NULL;
+
+ strcpy(result, filePath); // Make a copy of the string
+
+ // NOTE: strrchr() returns a pointer to the last occurrence of character
+ lastDot = strrchr(result, nameDot);
+ lastSep = (pathSep == 0) ? NULL : strrchr(result, pathSep);
+
+ if (lastDot != NULL) // Check if it has an extension separator...
+ {
+ if (lastSep != NULL) // ...and it's before the extenstion separator...
+ {
+ if (lastSep < lastDot)
+ {
+ *lastDot = '\0'; // ...then remove it
+ }
+ }
+ else *lastDot = '\0'; // Has extension separator with no path separator
+ }
+
+ return result; // Return the modified string
+}
// Get directory for a given fileName (with path)
const char *GetDirectoryPath(const char *fileName)
@@ -1375,11 +1600,11 @@ const char *GetDirectoryPath(const char *fileName)
memset(filePath, 0, 256);
lastSlash = strprbrk(fileName, "\\/");
- if (!lastSlash)
- return NULL;
+ if (!lastSlash) return NULL;
+ // NOTE: Be careful, strncpy() is not safe, it does not care about '\0'
strncpy(filePath, fileName, strlen(fileName) - (strlen(lastSlash) - 1));
- filePath[strlen(fileName) - strlen(lastSlash)] = '\0';
+ filePath[strlen(fileName) - strlen(lastSlash)] = '\0'; // Add '\0' manually
return filePath;
}
@@ -1395,6 +1620,57 @@ const char *GetWorkingDirectory(void)
return currentDir;
}
+// Get filenames in a directory path (max 256 files)
+// NOTE: Files count is returned by parameters pointer
+char **GetDirectoryFiles(const char *dirPath, int *fileCount)
+{
+ #define MAX_FILEPATH_LENGTH 256
+ #define MAX_DIRECTORY_FILES 512
+
+ ClearDirectoryFiles();
+
+ // Memory allocation for MAX_DIRECTORY_FILES
+ dirFilesPath = (char **)malloc(sizeof(char *)*MAX_DIRECTORY_FILES);
+ for (int i = 0; i < MAX_DIRECTORY_FILES; i++) dirFilesPath[i] = (char *)malloc(sizeof(char)*MAX_FILEPATH_LENGTH);
+
+ int counter = 0;
+ struct dirent *ent;
+ DIR *dir = opendir(dirPath);
+
+ if (dir != NULL) // It's a directory
+ {
+ // TODO: Reading could be done in two passes,
+ // first one to count files and second one to read names
+ // That way we can allocate required memory, instead of a limited pool
+
+ while ((ent = readdir(dir)) != NULL)
+ {
+ strcpy(dirFilesPath[counter], ent->d_name);
+ counter++;
+ }
+
+ closedir(dir);
+ }
+ else TraceLog(LOG_WARNING, "Can not open directory...\n"); // Maybe it's a file...
+
+ dirFilesCount = counter;
+ *fileCount = dirFilesCount;
+
+ return dirFilesPath;
+}
+
+// Clear directory files paths buffers
+void ClearDirectoryFiles(void)
+{
+ if (dirFilesCount > 0)
+ {
+ for (int i = 0; i < dirFilesCount; i++) free(dirFilesPath[i]);
+
+ free(dirFilesPath);
+ dirFilesCount = 0;
+ }
+}
+
// Change working directory, returns true if success
bool ChangeDirectory(const char *dir)
{
@@ -1438,6 +1714,21 @@ void ClearDroppedFiles(void)
#endif
}
+// Get file modification time (last write time)
+RLAPI long GetFileModTime(const char *fileName)
+{
+ struct stat result = { 0 };
+
+ if (stat(fileName, &result) == 0)
+ {
+ time_t mod = result.st_mtime;
+
+ return (long)mod;
+ }
+
+ return 0;
+}
+
// Save integer value to storage file (to defined position)
// NOTE: Storage positions is directly related to file memory layout (4 bytes each integer)
void StorageSaveValue(int position, int value)
@@ -1709,7 +2000,7 @@ bool IsMouseButtonPressed(int button)
#else
if ((currentMouseState[button] != previousMouseState[button]) && (currentMouseState[button] == 1)) pressed = true;
#endif
-
+
return pressed;
}
@@ -1853,7 +2144,9 @@ Vector2 GetTouchPosition(int index)
position.x = position.x*((float)renderWidth/(float)displayWidth) - renderOffsetX/2;
position.y = position.y*((float)renderHeight/(float)displayHeight) - renderOffsetY/2;
}
-#else // PLATFORM_DESKTOP, PLATFORM_RPI
+#elif defined(PLATFORM_RPI)
+ position = touchPosition[index];
+#else // PLATFORM_DESKTOP
if (index == 0) position = GetMousePosition();
#endif
@@ -1994,7 +2287,7 @@ static bool InitGraphicsDevice(int width, int height)
// At this point we need to manage render size vs screen size
// NOTE: This function uses and modifies global module variables:
// screenWidth/screenHeight - renderWidth/renderHeight - downscaleView
- SetupFramebufferSize(displayWidth, displayHeight);
+ SetupFramebuffer(displayWidth, displayHeight);
window = glfwCreateWindow(displayWidth, displayHeight, windowTitle, glfwGetPrimaryMonitor(), NULL);
@@ -2228,7 +2521,7 @@ static bool InitGraphicsDevice(int width, int height)
}
}
- //SetupFramebufferSize(displayWidth, displayHeight);
+ //SetupFramebuffer(displayWidth, displayHeight);
EGLint numConfigs = 0;
if ((eglChooseConfig(display, framebufferAttribs, &config, 1, &numConfigs) == EGL_FALSE) || (numConfigs == 0))
@@ -2334,7 +2627,7 @@ static bool InitGraphicsDevice(int width, int height)
// At this point we need to manage render size vs screen size
// NOTE: This function use and modify global module variables: screenWidth/screenHeight and renderWidth/renderHeight and downscaleView
- SetupFramebufferSize(displayWidth, displayHeight);
+ SetupFramebuffer(displayWidth, displayHeight);
ANativeWindow_setBuffersGeometry(androidApp->window, renderWidth, renderHeight, displayFormat);
//ANativeWindow_setBuffersGeometry(androidApp->window, 0, 0, displayFormat); // Force use of native display size
@@ -2347,7 +2640,7 @@ static bool InitGraphicsDevice(int width, int height)
// At this point we need to manage render size vs screen size
// NOTE: This function use and modify global module variables: screenWidth/screenHeight and renderWidth/renderHeight and downscaleView
- SetupFramebufferSize(displayWidth, displayHeight);
+ SetupFramebuffer(displayWidth, displayHeight);
dstRect.x = 0;
dstRect.y = 0;
@@ -2449,7 +2742,7 @@ static void SetupViewport(void)
// Compute framebuffer size relative to screen size and display size
// NOTE: Global variables renderWidth/renderHeight and renderOffsetX/renderOffsetY can be modified
-static void SetupFramebufferSize(int displayWidth, int displayHeight)
+static void SetupFramebuffer(int width, int height)
{
// Calculate renderWidth and renderHeight, we have the display size (input params) and the desired screen size (global var)
if ((screenWidth > displayWidth) || (screenHeight > displayHeight))
@@ -2620,6 +2913,21 @@ static void PollInputEvents(void)
gamepadAxisCount = 0;
#endif
+#if defined(PLATFORM_RPI)
+ // Register previous keys states
+ for (int i = 0; i < 512; i++) previousKeyState[i] = currentKeyState[i];
+
+ // Register previous mouse states
+ previousMouseWheelY = currentMouseWheelY;
+ currentMouseWheelY = 0;
+ for (int i = 0; i < 3; i++)
+ {
+ previousMouseState[i] = currentMouseState[i];
+ currentMouseState[i] = currentMouseStateEvdev[i];
+ }
+
+#endif
+
#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
// Mouse input polling
double mouseX;
@@ -2773,6 +3081,16 @@ static void SwapBuffers(void)
{
#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
glfwSwapBuffers(window);
+#if __APPLE__
+ // Workaround for missing/erroneous initial rendering on macOS
+ if (windowNeedsUpdating)
+ {
+ // Desugared version of Objective C: [glfwGetNSGLContext(window) update]
+ ((id (*)(id, SEL))objc_msgSend)(glfwGetNSGLContext(window), sel_registerName("update"));
+
+ windowNeedsUpdating--;
+ }
+#endif
#endif
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_UWP)
@@ -2952,7 +3270,7 @@ static void WindowSizeCallback(GLFWwindow *window, int width, int height)
}
// GLFW3 WindowIconify Callback, runs when window is minimized/restored
-static void WindowIconifyCallback(GLFWwindow* window, int iconified)
+static void WindowIconifyCallback(GLFWwindow *window, int iconified)
{
if (iconified) windowMinimized = true; // The window was iconified
else windowMinimized = false; // The window was restored
@@ -3544,184 +3862,364 @@ static void RestoreKeyboard(void)
// Mouse initialization (including mouse thread)
static void InitMouse(void)
{
- // NOTE: We can use /dev/input/mice to read from all available mice
- if ((mouseStream = open(DEFAULT_MOUSE_DEV, O_RDONLY|O_NONBLOCK)) < 0)
+ char Path[256];
+ DIR *directory;
+ struct dirent *entity;
+
+ // Reset variables
+ for (int i = 0; i < MAX_TOUCH_POINTS; ++i)
+ {
+ touchPosition[i].x = -1;
+ touchPosition[i].y = -1;
+ }
+
+ // Open the linux directory of "/dev/input"
+ directory = opendir(DEFAULT_EVDEV_PATH);
+ if (directory)
{
- TraceLog(LOG_WARNING, "Mouse device could not be opened, no mouse available");
+ while ((entity = readdir(directory)) != NULL)
+ {
+ if (strncmp("event", entity->d_name, strlen("event")) == 0) // Search for devices named "event*"
+ {
+ sprintf(Path, "%s%s", DEFAULT_EVDEV_PATH, entity->d_name);
+ EventThreadSpawn(Path); // Identify the device and spawn a thread for it
+ }
+ }
+
+ closedir(directory);
}
else
{
- mouseReady = true;
-
- int error = pthread_create(&mouseThreadId, NULL, &MouseThread, NULL);
-
- if (error != 0) TraceLog(LOG_WARNING, "Error creating mouse input event thread");
- else TraceLog(LOG_INFO, "Mouse device initialized successfully");
+ TraceLog(LOG_WARNING, "Unable to open linux event directory %s", DEFAULT_EVDEV_PATH);
}
}
-// Mouse reading thread
-// NOTE: We need a separate thread to avoid loosing mouse events,
-// if too much time passes between reads, queue gets full and new events override older ones...
-static void *MouseThread(void *arg)
+static void EventThreadSpawn(char *device)
{
- const unsigned char XSIGN = (1 << 4);
- const unsigned char YSIGN = (1 << 5);
-
- typedef struct {
- char buttons;
- char dx, dy;
- } MouseEvent;
-
- MouseEvent mouse;
+ #define BITS_PER_LONG (sizeof(long)*8)
+ #define NBITS(x) ((((x) - 1)/BITS_PER_LONG) + 1)
+ #define OFF(x) ((x)%BITS_PER_LONG)
+ #define BIT(x) (1UL<<OFF(x))
+ #define LONG(x) ((x)/BITS_PER_LONG)
+ #define TEST_BIT(array, bit) ((array[LONG(bit)] >> OFF(bit)) & 1)
+
+ struct input_absinfo absinfo;
+ unsigned long evBits[NBITS(EV_MAX)];
+ unsigned long absBits[NBITS(ABS_MAX)];
+ unsigned long relBits[NBITS(REL_MAX)];
+ unsigned long keyBits[NBITS(KEY_MAX)];
+ bool hasAbs = false;
+ bool hasRel = false;
+ bool hasAbsMulti = false;
+ int freeWorkerId = -1;
+ int fd = -1;
+
+ InputEventWorker *worker;
- int mouseRelX = 0;
- int mouseRelY = 0;
+ /////////////////////////////////// Open the device and allocate worker /////////////////////////////////////////////
- while (!windowShouldClose)
+ // Find a free spot in the workers array
+ for (int i = 0; i < sizeof(eventWorkers)/sizeof(InputEventWorker); ++i)
{
- if (read(mouseStream, &mouse, sizeof(MouseEvent)) == (int)sizeof(MouseEvent))
+ if (eventWorkers[i].threadId == 0)
{
- if ((mouse.buttons & 0x08) == 0) break; // This bit should always be set
-
- // Check Left button pressed
- if ((mouse.buttons & 0x01) > 0) currentMouseState[0] = 1;
- else currentMouseState[0] = 0;
+ freeWorkerId = i;
+ break;
+ }
+ }
- // Check Right button pressed
- if ((mouse.buttons & 0x02) > 0) currentMouseState[1] = 1;
- else currentMouseState[1] = 0;
+ // Select the free worker from array
+ if (freeWorkerId >= 0)
+ {
+ worker = &(eventWorkers[freeWorkerId]); // Grab a pointer to the worker
+ memset(worker, 0, sizeof(InputEventWorker)); // Clear the worker
+ }
+ else
+ {
+ TraceLog(LOG_WARNING, "Error creating input device thread for '%s': Out of worker slots", device);
+ return;
+ }
- // Check Middle button pressed
- if ((mouse.buttons & 0x04) > 0) currentMouseState[2] = 1;
- else currentMouseState[2] = 0;
+ // Open the device
+ fd = open(device, O_RDONLY | O_NONBLOCK);
+ if (fd < 0)
+ {
+ TraceLog(LOG_WARNING, "Error creating input device thread for '%s': Can't open device (Err: %d)", device, worker->fd);
+ return;
+ }
+ worker->fd = fd;
- mouseRelX = (int)mouse.dx;
- mouseRelY = (int)mouse.dy;
+ //Grab number on the end of the devices name "event<N>"
+ int devNum = 0;
+ char *ptrDevName = strrchr(device, 't');
+ worker->eventNum = -1;
+
+ if (ptrDevName != NULL)
+ {
+ if (sscanf(ptrDevName, "t%d", &devNum) == 1)
+ worker->eventNum = devNum;
+ }
- if ((mouse.buttons & XSIGN) > 0) mouseRelX = -1*(255 - mouseRelX);
- if ((mouse.buttons & YSIGN) > 0) mouseRelY = -1*(255 - mouseRelY);
+ // At this point we have a connection to the device,
+ // but we don't yet know what the device is (Could be
+ // many things, even as simple as a power button)
- // NOTE: Mouse movement is normalized to not be screen resolution dependant
- // We suppose 2*255 (max relative movement) is equivalent to screenWidth (max pixels width)
- // Result after normalization is multiplied by MOUSE_SENSITIVITY factor
+ /////////////////////////////////// Identify the device /////////////////////////////////////////////
- mousePosition.x += (float)mouseRelX*((float)screenWidth/(2*255))*MOUSE_SENSITIVITY;
- mousePosition.y -= (float)mouseRelY*((float)screenHeight/(2*255))*MOUSE_SENSITIVITY;
+ ioctl(fd, EVIOCGBIT(0, sizeof(evBits)), evBits); // Read a bitfield of the avalable device properties
- if (mousePosition.x < 0) mousePosition.x = 0;
- if (mousePosition.y < 0) mousePosition.y = 0;
+ // Check for absolute input devices
+ if (TEST_BIT(evBits, EV_ABS))
+ {
+ ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absBits)), absBits);
- if (mousePosition.x > screenWidth) mousePosition.x = screenWidth;
- if (mousePosition.y > screenHeight) mousePosition.y = screenHeight;
- }
- //else read(mouseStream, &mouse, 1); // Try to sync up again
+ // Check for absolute movement support (usualy touchscreens, but also joysticks)
+ if (TEST_BIT(absBits, ABS_X) && TEST_BIT(absBits, ABS_Y))
+ {
+ hasAbs = true;
+
+ // Get the scaling values
+ ioctl(fd, EVIOCGABS(ABS_X), &absinfo);
+ worker->absRange.x = absinfo.minimum;
+ worker->absRange.width = absinfo.maximum - absinfo.minimum;
+ ioctl(fd, EVIOCGABS(ABS_Y), &absinfo);
+ worker->absRange.y = absinfo.minimum;
+ worker->absRange.height = absinfo.maximum - absinfo.minimum;
+ }
+
+ // Check for multiple absolute movement support (usualy multitouch touchscreens)
+ if (TEST_BIT(absBits, ABS_MT_POSITION_X) && TEST_BIT(absBits, ABS_MT_POSITION_Y))
+ {
+ hasAbsMulti = true;
+
+ // Get the scaling values
+ ioctl(fd, EVIOCGABS(ABS_X), &absinfo);
+ worker->absRange.x = absinfo.minimum;
+ worker->absRange.width = absinfo.maximum - absinfo.minimum;
+ ioctl(fd, EVIOCGABS(ABS_Y), &absinfo);
+ worker->absRange.y = absinfo.minimum;
+ worker->absRange.height = absinfo.maximum - absinfo.minimum;
+ }
}
- return NULL;
-}
-
-// Touch initialization (including touch thread)
-static void InitTouch(void)
-{
- if ((touchStream = open(DEFAULT_TOUCH_DEV, O_RDONLY|O_NONBLOCK)) < 0)
+ // Check for relative movement support (usualy mouse)
+ if (TEST_BIT(evBits, EV_REL))
{
- TraceLog(LOG_WARNING, "Touch device could not be opened, no touchscreen available");
+ ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relBits)), relBits);
+
+ if (TEST_BIT(relBits, REL_X) && TEST_BIT(relBits, REL_Y)) hasRel = true;
}
- else
+
+ // Check for button support to determine the device type(usualy on all input devices)
+ if (TEST_BIT(evBits, EV_KEY))
{
- touchReady = true;
+ ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keyBits)), keyBits);
+
+ if (hasAbs || hasAbsMulti)
+ {
+ if (TEST_BIT(keyBits, BTN_TOUCH)) worker->isTouch = true; // This is a touchscreen
+ if (TEST_BIT(keyBits, BTN_TOOL_FINGER)) worker->isTouch = true; // This is a drawing tablet
+ if (TEST_BIT(keyBits, BTN_TOOL_PEN)) worker->isTouch = true; // This is a drawing tablet
+ if (TEST_BIT(keyBits, BTN_STYLUS)) worker->isTouch = true; // This is a drawing tablet
+ if (worker->isTouch || hasAbsMulti) worker->isMultitouch = true; // This is a multitouch capable device
+ }
+
+ if (hasRel)
+ {
+ if (TEST_BIT(keyBits, BTN_LEFT)) worker->isMouse = true; // This is a mouse
+ if (TEST_BIT(keyBits, BTN_RIGHT)) worker->isMouse = true; // This is a mouse
+ }
+
+ if (TEST_BIT(keyBits, BTN_A)) worker->isGamepad = true; // This is a gamepad
+ if (TEST_BIT(keyBits, BTN_TRIGGER)) worker->isGamepad = true; // This is a gamepad
+ if (TEST_BIT(keyBits, BTN_START)) worker->isGamepad = true; // This is a gamepad
+ if (TEST_BIT(keyBits, BTN_TL)) worker->isGamepad = true; // This is a gamepad
+ if (TEST_BIT(keyBits, BTN_TL)) worker->isGamepad = true; // This is a gamepad
- int error = pthread_create(&touchThreadId, NULL, &TouchThread, NULL);
+ if (TEST_BIT(keyBits, KEY_SPACE)) worker->isKeyboard = true; // This is a keyboard
+ }
+
+
+ /////////////////////////////////// Decide what to do with the device /////////////////////////////////////////////
+ if (worker->isTouch || worker->isMouse)
+ {
+ // Looks like a interesting device
+ TraceLog(LOG_INFO, "Opening input device '%s' (%s%s%s%s%s)", device,
+ worker->isMouse ? "mouse " : "",
+ worker->isMultitouch ? "multitouch " : "",
+ worker->isTouch ? "touchscreen " : "",
+ worker->isGamepad ? "gamepad " : "",
+ worker->isKeyboard ? "keyboard " : "");
+
+ // Create a thread for this device
+ int error = pthread_create(&worker->threadId, NULL, &EventThread, (void *)worker);
+ if (error != 0)
+ {
+ TraceLog(LOG_WARNING, "Error creating input device thread for '%s': Can't create thread (Err: %d)", device, error);
+ worker->threadId = 0;
+ close(fd);
+ }
- if (error != 0) TraceLog(LOG_WARNING, "Error creating touch input event thread");
- else TraceLog(LOG_INFO, "Touch device initialized successfully");
+#if defined(USE_LAST_TOUCH_DEVICE)
+ // Find touchscreen with the highest index
+ int maxTouchNumber = -1;
+
+ for (int i = 0; i < sizeof(eventWorkers)/sizeof(InputEventWorker); ++i)
+ {
+ if (eventWorkers[i].isTouch && (eventWorkers[i].eventNum > maxTouchNumber)) maxTouchNumber = eventWorkers[i].eventNum;
+ }
+
+ // Find toucnscreens with lower indexes
+ for (int i = 0; i < sizeof(eventWorkers)/sizeof(InputEventWorker); ++i)
+ {
+ if (eventWorkers[i].isTouch && (eventWorkers[i].eventNum < maxTouchNumber))
+ {
+ if (eventWorkers[i].threadId != 0)
+ {
+ TraceLog(LOG_WARNING, "Duplicate touchscreen found, killing toucnscreen on event%d", i);
+ pthread_cancel(eventWorkers[i].threadId);
+ close(eventWorkers[i].fd);
+ }
+ }
+ }
+#endif
}
+ else close(fd); // We are not interested in this device
}
-// Touch reading thread.
-// This reads from a Virtual Input Event /dev/input/event4 which is
-// created by the ts_uinput daemon. This takes, filters and scales
-// raw input from the Touchscreen (which appears in /dev/input/event3)
-// based on the Calibration data referenced by tslib.
-static void *TouchThread(void *arg)
+static void *EventThread(void *arg)
{
- struct input_event ev;
+ struct input_event event;
GestureEvent gestureEvent;
+ InputEventWorker *worker = (InputEventWorker *)arg;
+ bool GestureNeedsUpdate = false;
while (!windowShouldClose)
{
- if (read(touchStream, &ev, sizeof(ev)) == (int)sizeof(ev))
+ if (read(worker->fd, &event, sizeof(event)) == (int)sizeof(event))
{
- // if pressure > 0 then simulate left mouse button click
- if (ev.type == EV_ABS && ev.code == 24 && ev.value == 0 && currentMouseState[0] == 1)
+ /////////////////////////////// Relative movement parsing ////////////////////////////////////
+ if (event.type == EV_REL)
{
- currentMouseState[0] = 0;
- gestureEvent.touchAction = TOUCH_UP;
- gestureEvent.pointCount = 1;
- gestureEvent.pointerId[0] = 0;
- gestureEvent.pointerId[1] = 1;
- gestureEvent.position[0] = (Vector2){ mousePosition.x, mousePosition.y };
- gestureEvent.position[1] = (Vector2){ mousePosition.x, mousePosition.y };
- gestureEvent.position[0].x /= (float)GetScreenWidth();
- gestureEvent.position[0].y /= (float)GetScreenHeight();
- gestureEvent.position[1].x /= (float)GetScreenWidth();
- gestureEvent.position[1].y /= (float)GetScreenHeight();
- ProcessGestureEvent(gestureEvent);
+ if (event.code == REL_X)
+ {
+ mousePosition.x += event.value;
+ touchPosition[0].x = mousePosition.x;
+ gestureEvent.touchAction = TOUCH_MOVE;
+ GestureNeedsUpdate = true;
+ }
+
+ if (event.code == REL_Y)
+ {
+ mousePosition.y += event.value;
+ touchPosition[0].y = mousePosition.y;
+ gestureEvent.touchAction = TOUCH_MOVE;
+ GestureNeedsUpdate = true;
+ }
+
+ if (event.code == REL_WHEEL)
+ {
+ currentMouseWheelY += event.value;
+ }
}
- if (ev.type == EV_ABS && ev.code == 24 && ev.value > 0 && currentMouseState[0] == 0)
+
+ /////////////////////////////// Absolute movement parsing ////////////////////////////////////
+ if (event.type == EV_ABS)
{
- currentMouseState[0] = 1;
- gestureEvent.touchAction = TOUCH_DOWN;
- gestureEvent.pointCount = 1;
- gestureEvent.pointerId[0] = 0;
- gestureEvent.pointerId[1] = 1;
- gestureEvent.position[0] = (Vector2){ mousePosition.x, mousePosition.y };
- gestureEvent.position[1] = (Vector2){ mousePosition.x, mousePosition.y };
- gestureEvent.position[0].x /= (float)GetScreenWidth();
- gestureEvent.position[0].y /= (float)GetScreenHeight();
- gestureEvent.position[1].x /= (float)GetScreenWidth();
- gestureEvent.position[1].y /= (float)GetScreenHeight();
- ProcessGestureEvent(gestureEvent);
+ // Basic movement
+ if (event.code == ABS_X)
+ {
+ mousePosition.x = (event.value - worker->absRange.x)*screenWidth/worker->absRange.width; // Scale acording to absRange
+ gestureEvent.touchAction = TOUCH_MOVE;
+ GestureNeedsUpdate = true;
+ }
+
+ if (event.code == ABS_Y)
+ {
+ mousePosition.y = (event.value - worker->absRange.y)*screenHeight/worker->absRange.height; // Scale acording to absRange
+ gestureEvent.touchAction = TOUCH_MOVE;
+ GestureNeedsUpdate = true;
+ }
+
+ // Multitouch movement
+ if (event.code == ABS_MT_SLOT)
+ {
+ worker->touchSlot = event.value; // Remeber the slot number for the folowing events
+ }
+
+ if (event.code == ABS_MT_POSITION_X)
+ {
+ if (worker->touchSlot < MAX_TOUCH_POINTS)
+ touchPosition[worker->touchSlot].x = (event.value - worker->absRange.x)*screenWidth/worker->absRange.width; // Scale acording to absRange
+ }
+
+ if (event.code == ABS_MT_POSITION_Y)
+ {
+ if (worker->touchSlot < MAX_TOUCH_POINTS)
+ touchPosition[worker->touchSlot].y = (event.value - worker->absRange.y)*screenHeight/worker->absRange.height; // Scale acording to absRange
+ }
+
+ if (event.code == ABS_MT_TRACKING_ID)
+ {
+ if ( (event.value < 0) && (worker->touchSlot < MAX_TOUCH_POINTS) )
+ {
+ // Touch has ended for this point
+ touchPosition[worker->touchSlot].x = -1;
+ touchPosition[worker->touchSlot].y = -1;
+ }
+ }
}
- // x & y values supplied by event4 have been scaled & de-jittered using tslib calibration data
- if (ev.type == EV_ABS && ev.code == 0)
+
+ /////////////////////////////// Button parsing ////////////////////////////////////
+ if (event.type == EV_KEY)
{
- mousePosition.x = ev.value;
- if (mousePosition.x < 0) mousePosition.x = 0;
- if (mousePosition.x > screenWidth) mousePosition.x = screenWidth;
- gestureEvent.touchAction = TOUCH_MOVE;
- gestureEvent.pointCount = 1;
- gestureEvent.pointerId[0] = 0;
- gestureEvent.pointerId[1] = 1;
- gestureEvent.position[0] = (Vector2){ mousePosition.x, mousePosition.y };
- gestureEvent.position[1] = (Vector2){ mousePosition.x, mousePosition.y };
- gestureEvent.position[0].x /= (float)GetScreenWidth();
- gestureEvent.position[0].y /= (float)GetScreenHeight();
- gestureEvent.position[1].x /= (float)GetScreenWidth();
- gestureEvent.position[1].y /= (float)GetScreenHeight();
- ProcessGestureEvent(gestureEvent);
+ if((event.code == BTN_TOUCH) || (event.code == BTN_LEFT))
+ {
+ currentMouseStateEvdev[MOUSE_LEFT_BUTTON] = event.value;
+ if (event.value > 0) gestureEvent.touchAction = TOUCH_DOWN;
+ else gestureEvent.touchAction = TOUCH_UP;
+ GestureNeedsUpdate = true;
+ }
+
+ if (event.code == BTN_RIGHT) currentMouseStateEvdev[MOUSE_RIGHT_BUTTON] = event.value;
+
+ if (event.code == BTN_MIDDLE) currentMouseStateEvdev[MOUSE_MIDDLE_BUTTON] = event.value;
}
- if (ev.type == EV_ABS && ev.code == 1)
+
+ /////////////////////////////// Screen confinement ////////////////////////////////////
+ if (mousePosition.x < 0) mousePosition.x = 0;
+ if (mousePosition.x > screenWidth/mouseScale) mousePosition.x = screenWidth/mouseScale;
+
+ if (mousePosition.y < 0) mousePosition.y = 0;
+ if (mousePosition.y > screenHeight/mouseScale) mousePosition.y = screenHeight/mouseScale;
+
+ /////////////////////////////// Gesture update ////////////////////////////////////
+ if (GestureNeedsUpdate)
{
- mousePosition.y = ev.value;
- if (mousePosition.y < 0) mousePosition.y = 0;
- if (mousePosition.y > screenHeight) mousePosition.y = screenHeight;
- gestureEvent.touchAction = TOUCH_MOVE;
- gestureEvent.pointCount = 1;
+ gestureEvent.pointCount = 0;
+ if (touchPosition[0].x >= 0) gestureEvent.pointCount++;
+ if (touchPosition[1].x >= 0) gestureEvent.pointCount++;
+ if (touchPosition[2].x >= 0) gestureEvent.pointCount++;
+ if (touchPosition[3].x >= 0) gestureEvent.pointCount++;
gestureEvent.pointerId[0] = 0;
gestureEvent.pointerId[1] = 1;
- gestureEvent.position[0] = (Vector2){ mousePosition.x, mousePosition.y };
- gestureEvent.position[1] = (Vector2){ mousePosition.x, mousePosition.y };
- gestureEvent.position[0].x /= (float)GetScreenWidth();
- gestureEvent.position[0].y /= (float)GetScreenHeight();
- gestureEvent.position[1].x /= (float)GetScreenWidth();
- gestureEvent.position[1].y /= (float)GetScreenHeight();
+ gestureEvent.pointerId[2] = 2;
+ gestureEvent.pointerId[3] = 3;
+ gestureEvent.position[0] = touchPosition[0];
+ gestureEvent.position[1] = touchPosition[1];
+ gestureEvent.position[2] = touchPosition[2];
+ gestureEvent.position[3] = touchPosition[3];
ProcessGestureEvent(gestureEvent);
}
-
+ }
+ else
+ {
+ usleep(5000); // Sleep for 5ms to avoid hogging CPU time
}
}
+
+ close(worker->fd);
+
return NULL;
}
@@ -3805,6 +4303,10 @@ static void *GamepadThread(void *arg)
}
}
}
+ else
+ {
+ usleep(1000); //Sleep for 1ms to avoid hogging CPU time
+ }
}
}
diff --git a/src/external/dirent.h b/src/external/dirent.h
new file mode 100644
index 00000000..a955253e
--- /dev/null
+++ b/src/external/dirent.h
@@ -0,0 +1,198 @@
+#ifndef DIRENT_H
+#define DIRENT_H
+
+/*
+
+ Declaration of POSIX directory browsing functions and types for Win32.
+
+ Author: Kevlin Henney (kevlin@acm.org, kevlin@curbralan.com)
+ History: Created March 1997. Updated June 2003.
+ Rights: See end of file.
+
+*/
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+typedef struct DIR DIR;
+
+struct dirent
+{
+ char *d_name;
+};
+
+DIR *opendir(const char *);
+int closedir(DIR *);
+struct dirent *readdir(DIR *);
+void rewinddir(DIR *);
+
+/*
+
+ Copyright Kevlin Henney, 1997, 2003. All rights reserved.
+
+ Permission to use, copy, modify, and distribute this software and its
+ documentation for any purpose is hereby granted without fee, provided
+ that this copyright and permissions notice appear in all copies and
+ derivatives.
+
+ This software is supplied "as is" without express or implied warranty.
+
+ But that said, if there are any problems please get in touch.
+
+*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // DIRENT_H
+
+/*
+
+ Implementation of POSIX directory browsing functions and types for Win32.
+
+ Author: Kevlin Henney (kevlin@acm.org, kevlin@curbralan.com)
+ History: Created March 1997. Updated June 2003 and July 2012.
+ Rights: See end of file.
+
+*/
+
+#include <errno.h>
+#include <io.h> /* _findfirst and _findnext set errno iff they return -1 */
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+typedef ptrdiff_t handle_type; /* C99's intptr_t not sufficiently portable */
+
+struct DIR
+{
+ handle_type handle; /* -1 for failed rewind */
+ struct _finddata_t info;
+ struct dirent result; /* d_name null iff first time */
+ char *name; /* null-terminated char string */
+};
+
+DIR *opendir(const char *name)
+{
+ DIR *dir = 0;
+
+ if(name && name[0])
+ {
+ size_t base_length = strlen(name);
+ const char *all = /* search pattern must end with suitable wildcard */
+ strchr("/\\", name[base_length - 1]) ? "*" : "/*";
+
+ if((dir = (DIR *) malloc(sizeof *dir)) != 0 &&
+ (dir->name = (char *) malloc(base_length + strlen(all) + 1)) != 0)
+ {
+ strcat(strcpy(dir->name, name), all);
+
+ if((dir->handle =
+ (handle_type) _findfirst(dir->name, &dir->info)) != -1)
+ {
+ dir->result.d_name = 0;
+ }
+ else /* rollback */
+ {
+ free(dir->name);
+ free(dir);
+ dir = 0;
+ }
+ }
+ else /* rollback */
+ {
+ free(dir);
+ dir = 0;
+ errno = ENOMEM;
+ }
+ }
+ else
+ {
+ errno = EINVAL;
+ }
+
+ return dir;
+}
+
+int closedir(DIR *dir)
+{
+ int result = -1;
+
+ if(dir)
+ {
+ if(dir->handle != -1)
+ {
+ result = _findclose(dir->handle);
+ }
+
+ free(dir->name);
+ free(dir);
+ }
+
+ if(result == -1) /* map all errors to EBADF */
+ {
+ errno = EBADF;
+ }
+
+ return result;
+}
+
+struct dirent *readdir(DIR *dir)
+{
+ struct dirent *result = 0;
+
+ if(dir && dir->handle != -1)
+ {
+ if(!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1)
+ {
+ result = &dir->result;
+ result->d_name = dir->info.name;
+ }
+ }
+ else
+ {
+ errno = EBADF;
+ }
+
+ return result;
+}
+
+void rewinddir(DIR *dir)
+{
+ if(dir && dir->handle != -1)
+ {
+ _findclose(dir->handle);
+ dir->handle = (handle_type) _findfirst(dir->name, &dir->info);
+ dir->result.d_name = 0;
+ }
+ else
+ {
+ errno = EBADF;
+ }
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+/*
+
+ Copyright Kevlin Henney, 1997, 2003, 2012. All rights reserved.
+
+ Permission to use, copy, modify, and distribute this software and its
+ documentation for any purpose is hereby granted without fee, provided
+ that this copyright and permissions notice appear in all copies and
+ derivatives.
+
+ This software is supplied "as is" without express or implied warranty.
+
+ But that said, if there are any problems please get in touch.
+
+*/
diff --git a/src/external/dr_mp3.h b/src/external/dr_mp3.h
index 467d319d..cd8920a1 100644
--- a/src/external/dr_mp3.h
+++ b/src/external/dr_mp3.h
@@ -1,5 +1,5 @@
// MP3 audio decoder. Public domain. See "unlicense" statement at the end of this file.
-// dr_mp3 - v0.2.5 - 2018-06-22
+// dr_mp3 - v0.3.2 - 2018-09-11
//
// David Reid - mackron@gmail.com
//
@@ -99,28 +99,26 @@ typedef drmp3_uint32 drmp3_bool32;
// ==================
typedef struct
{
- int frame_bytes;
- int channels;
- int hz;
- int layer;
- int bitrate_kbps;
+ int frame_bytes, channels, hz, layer, bitrate_kbps;
} drmp3dec_frame_info;
typedef struct
{
- float mdct_overlap[2][9*32];
- float qmf_state[15*2*32];
- int reserv;
- int free_format_bytes;
- unsigned char header[4];
- unsigned char reserv_buf[511];
+ float mdct_overlap[2][9*32], qmf_state[15*2*32];
+ int reserv, free_format_bytes;
+ unsigned char header[4], reserv_buf[511];
} drmp3dec;
// Initializes a low level decoder.
void drmp3dec_init(drmp3dec *dec);
// Reads a frame from a low level decoder.
-int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes, short *pcm, drmp3dec_frame_info *info);
+int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info);
+
+// Helper for converting between f32 and s16.
+void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, int num_samples);
+
+
// Main API (Pull API)
@@ -220,7 +218,7 @@ typedef struct
drmp3_uint32 frameSampleRate; // The sample rate of the currently loaded MP3 frame. Internal use only.
drmp3_uint32 framesConsumed;
drmp3_uint32 framesRemaining;
- drmp3_int16 frames[DRMP3_MAX_SAMPLES_PER_FRAME];
+ drmp3_uint8 frames[sizeof(float)*DRMP3_MAX_SAMPLES_PER_FRAME]; // <-- Multipled by sizeof(float) to ensure there's enough room for DR_MP3_FLOAT_OUTPUT.
drmp3_src src;
size_t dataSize;
size_t dataCapacity;
@@ -314,8 +312,12 @@ void drmp3_free(void* p);
#define DR_MP3_NO_SIMD
#endif
+#define DRMP3_OFFSET_PTR(p, offset) ((void*)((drmp3_uint8*)(p) + (offset)))
+
#define DRMP3_MAX_FREE_FORMAT_FRAME_SIZE 2304 /* more than ISO spec's */
+#ifndef DRMP3_MAX_FRAME_SYNC_MATCHES
#define DRMP3_MAX_FRAME_SYNC_MATCHES 10
+#endif
#define DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES DRMP3_MAX_FREE_FORMAT_FRAME_SIZE /* MUST be >= 320000/8/32000*1152 = 1440 */
@@ -361,7 +363,7 @@ void drmp3_free(void* p);
#if defined(_MSC_VER)
#include <intrin.h>
#endif
-#include <immintrin.h>
+#include <emmintrin.h>
#define DRMP3_HAVE_SSE 1
#define DRMP3_HAVE_SIMD 1
#define DRMP3_VSTORE _mm_storeu_ps
@@ -462,44 +464,27 @@ static int drmp3_have_simd()
typedef struct
{
const drmp3_uint8 *buf;
- int pos;
- int limit;
+ int pos, limit;
} drmp3_bs;
typedef struct
{
- drmp3_uint8 total_bands;
- drmp3_uint8 stereo_bands;
- drmp3_uint8 bitalloc[64];
- drmp3_uint8 scfcod[64];
float scf[3*64];
+ drmp3_uint8 total_bands, stereo_bands, bitalloc[64], scfcod[64];
} drmp3_L12_scale_info;
typedef struct
{
- drmp3_uint8 tab_offset;
- drmp3_uint8 code_tab_width;
- drmp3_uint8 band_count;
+ drmp3_uint8 tab_offset, code_tab_width, band_count;
} drmp3_L12_subband_alloc;
typedef struct
{
const drmp3_uint8 *sfbtab;
- drmp3_uint16 part_23_length;
- drmp3_uint16 big_values;
- drmp3_uint16 scalefac_compress;
- drmp3_uint8 global_gain;
- drmp3_uint8 block_type;
- drmp3_uint8 mixed_block_flag;
- drmp3_uint8 n_long_sfb;
- drmp3_uint8 n_short_sfb;
- drmp3_uint8 table_select[3];
- drmp3_uint8 region_count[3];
- drmp3_uint8 subblock_gain[3];
- drmp3_uint8 preflag;
- drmp3_uint8 scalefac_scale;
- drmp3_uint8 count1_table;
- drmp3_uint8 scfsi;
+ drmp3_uint16 part_23_length, big_values, scalefac_compress;
+ drmp3_uint8 global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb;
+ drmp3_uint8 table_select[3], region_count[3], subblock_gain[3];
+ drmp3_uint8 preflag, scalefac_scale, count1_table, scfsi;
} drmp3_L3_gr_info;
typedef struct
@@ -507,10 +492,8 @@ typedef struct
drmp3_bs bs;
drmp3_uint8 maindata[DRMP3_MAX_BITRESERVOIR_BYTES + DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES];
drmp3_L3_gr_info gr_info[4];
- float grbuf[2][576];
- float scf[40];
+ float grbuf[2][576], scf[40], syn[18 + 15][2*32];
drmp3_uint8 ist_pos[2][39];
- float syn[18 + 15][2*32];
} drmp3dec_scratch;
static void drmp3_bs_init(drmp3_bs *bs, const drmp3_uint8 *data, int bytes)
@@ -988,17 +971,19 @@ static void drmp3_L3_decode_scalefactors(const drmp3_uint8 *hdr, drmp3_uint8 *is
}
}
+static const float g_drmp3_pow43[129 + 16] = {
+ 0,-1,-2.519842f,-4.326749f,-6.349604f,-8.549880f,-10.902724f,-13.390518f,-16.000000f,-18.720754f,-21.544347f,-24.463781f,-27.473142f,-30.567351f,-33.741992f,-36.993181f,
+ 0,1,2.519842f,4.326749f,6.349604f,8.549880f,10.902724f,13.390518f,16.000000f,18.720754f,21.544347f,24.463781f,27.473142f,30.567351f,33.741992f,36.993181f,40.317474f,43.711787f,47.173345f,50.699631f,54.288352f,57.937408f,61.644865f,65.408941f,69.227979f,73.100443f,77.024898f,81.000000f,85.024491f,89.097188f,93.216975f,97.382800f,101.593667f,105.848633f,110.146801f,114.487321f,118.869381f,123.292209f,127.755065f,132.257246f,136.798076f,141.376907f,145.993119f,150.646117f,155.335327f,160.060199f,164.820202f,169.614826f,174.443577f,179.305980f,184.201575f,189.129918f,194.090580f,199.083145f,204.107210f,209.162385f,214.248292f,219.364564f,224.510845f,229.686789f,234.892058f,240.126328f,245.389280f,250.680604f,256.000000f,261.347174f,266.721841f,272.123723f,277.552547f,283.008049f,288.489971f,293.998060f,299.532071f,305.091761f,310.676898f,316.287249f,321.922592f,327.582707f,333.267377f,338.976394f,344.709550f,350.466646f,356.247482f,362.051866f,367.879608f,373.730522f,379.604427f,385.501143f,391.420496f,397.362314f,403.326427f,409.312672f,415.320884f,421.350905f,427.402579f,433.475750f,439.570269f,445.685987f,451.822757f,457.980436f,464.158883f,470.357960f,476.577530f,482.817459f,489.077615f,495.357868f,501.658090f,507.978156f,514.317941f,520.677324f,527.056184f,533.454404f,539.871867f,546.308458f,552.764065f,559.238575f,565.731879f,572.243870f,578.774440f,585.323483f,591.890898f,598.476581f,605.080431f,611.702349f,618.342238f,625.000000f,631.675540f,638.368763f,645.079578f
+};
+
static float drmp3_L3_pow_43(int x)
{
- static const float g_pow43[129] = {
- 0,1,2.519842f,4.326749f,6.349604f,8.549880f,10.902724f,13.390518f,16.000000f,18.720754f,21.544347f,24.463781f,27.473142f,30.567351f,33.741992f,36.993181f,40.317474f,43.711787f,47.173345f,50.699631f,54.288352f,57.937408f,61.644865f,65.408941f,69.227979f,73.100443f,77.024898f,81.000000f,85.024491f,89.097188f,93.216975f,97.382800f,101.593667f,105.848633f,110.146801f,114.487321f,118.869381f,123.292209f,127.755065f,132.257246f,136.798076f,141.376907f,145.993119f,150.646117f,155.335327f,160.060199f,164.820202f,169.614826f,174.443577f,179.305980f,184.201575f,189.129918f,194.090580f,199.083145f,204.107210f,209.162385f,214.248292f,219.364564f,224.510845f,229.686789f,234.892058f,240.126328f,245.389280f,250.680604f,256.000000f,261.347174f,266.721841f,272.123723f,277.552547f,283.008049f,288.489971f,293.998060f,299.532071f,305.091761f,310.676898f,316.287249f,321.922592f,327.582707f,333.267377f,338.976394f,344.709550f,350.466646f,356.247482f,362.051866f,367.879608f,373.730522f,379.604427f,385.501143f,391.420496f,397.362314f,403.326427f,409.312672f,415.320884f,421.350905f,427.402579f,433.475750f,439.570269f,445.685987f,451.822757f,457.980436f,464.158883f,470.357960f,476.577530f,482.817459f,489.077615f,495.357868f,501.658090f,507.978156f,514.317941f,520.677324f,527.056184f,533.454404f,539.871867f,546.308458f,552.764065f,559.238575f,565.731879f,572.243870f,578.774440f,585.323483f,591.890898f,598.476581f,605.080431f,611.702349f,618.342238f,625.000000f,631.675540f,638.368763f,645.079578f
- };
float frac;
int sign, mult = 256;
if (x < 129)
{
- return g_pow43[x];
+ return g_drmp3_pow43[16 + x];
}
if (x < 1024)
@@ -1009,12 +994,11 @@ static float drmp3_L3_pow_43(int x)
sign = 2*x & 64;
frac = (float)((x & 63) - sign) / ((x & ~63) + sign);
- return g_pow43[(x + sign) >> 6]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult;
+ return g_drmp3_pow43[16 + ((x + sign) >> 6)]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult;
}
static void drmp3_L3_huffman(float *dst, drmp3_bs *bs, const drmp3_L3_gr_info *gr_info, const float *scf, int layer3gr_limit)
{
- static const float g_pow43_signed[32] = { 0,0,1,-1,2.519842f,-2.519842f,4.326749f,-4.326749f,6.349604f,-6.349604f,8.549880f,-8.549880f,10.902724f,-10.902724f,13.390518f,-13.390518f,16.000000f,-16.000000f,18.720754f,-18.720754f,21.544347f,-21.544347f,24.463781f,-24.463781f,27.473142f,-27.473142f,30.567351f,-30.567351f,33.741992f,-33.741992f,36.993181f,-36.993181f };
static const drmp3_int16 tabs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
785,785,785,785,784,784,784,784,513,513,513,513,513,513,513,513,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,
-255,1313,1298,1282,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,290,288,
@@ -1053,7 +1037,7 @@ static void drmp3_L3_huffman(float *dst, drmp3_bs *bs, const drmp3_L3_gr_info *g
{
int tab_num = gr_info->table_select[ireg];
int sfb_cnt = gr_info->region_count[ireg++];
- const short *codebook = tabs + tabindex[tab_num];
+ const drmp3_int16 *codebook = tabs + tabindex[tab_num];
int linbits = g_linbits[tab_num];
do
{
@@ -1083,7 +1067,7 @@ static void drmp3_L3_huffman(float *dst, drmp3_bs *bs, const drmp3_L3_gr_info *g
*dst = one*drmp3_L3_pow_43(lsb)*((int32_t)bs_cache < 0 ? -1: 1);
} else
{
- *dst = g_pow43_signed[lsb*2 + (bs_cache >> 31)]*one;
+ *dst = g_drmp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one;
}
DRMP3_FLUSH_BITS(lsb ? 1 : 0);
}
@@ -1659,18 +1643,27 @@ static void drmp3d_DCT_II(float *grbuf, int n)
#endif
}
-static short drmp3d_scale_pcm(float sample)
+#ifndef DR_MP3_FLOAT_OUTPUT
+typedef drmp3_int16 drmp3d_sample_t;
+
+static drmp3_int16 drmp3d_scale_pcm(float sample)
{
- if (sample > 32767.0) return (short) 32767;
- if (sample < -32768.0) return (short)-32768;
- int s = (int)(sample + .5f);
+ if (sample >= 32766.5) return (drmp3_int16) 32767;
+ if (sample <= -32767.5) return (drmp3_int16)-32768;
+ drmp3_int16 s = (drmp3_int16)(sample + .5f);
s -= (s < 0); /* away from zero, to be compliant */
- if (s > 32767) return (short) 32767;
- if (s < -32768) return (short)-32768;
- return (short)s;
+ return (drmp3_int16)s;
}
+#else
+typedef float drmp3d_sample_t;
-static void drmp3d_synth_pair(short *pcm, int nch, const float *z)
+static float drmp3d_scale_pcm(float sample)
+{
+ return sample*(1.f/32768.f);
+}
+#endif
+
+static void drmp3d_synth_pair(drmp3d_sample_t *pcm, int nch, const float *z)
{
float a;
a = (z[14*64] - z[ 0]) * 29;
@@ -1695,11 +1688,11 @@ static void drmp3d_synth_pair(short *pcm, int nch, const float *z)
pcm[16*nch] = drmp3d_scale_pcm(a);
}
-static void drmp3d_synth(float *xl, short *dstl, int nch, float *lins)
+static void drmp3d_synth(float *xl, drmp3d_sample_t *dstl, int nch, float *lins)
{
int i;
float *xr = xl + 576*(nch - 1);
- short *dstr = dstl + (nch - 1);
+ drmp3d_sample_t *dstr = dstl + (nch - 1);
static const float g_win[] = {
-1,26,-31,208,218,401,-519,2063,2000,4788,-5517,7134,5959,35640,-39336,74992,
@@ -1756,19 +1749,20 @@ static void drmp3d_synth(float *xl, short *dstl, int nch, float *lins)
DRMP3_V0(0) DRMP3_V2(1) DRMP3_V1(2) DRMP3_V2(3) DRMP3_V1(4) DRMP3_V2(5) DRMP3_V1(6) DRMP3_V2(7)
{
+#ifndef DR_MP3_FLOAT_OUTPUT
#if DRMP3_HAVE_SSE
static const drmp3_f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f };
static const drmp3_f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f };
__m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)),
_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min)));
- dstr[(15 - i)*nch] = (short)_mm_extract_epi16(pcm8, 1);
- dstr[(17 + i)*nch] = (short)_mm_extract_epi16(pcm8, 5);
- dstl[(15 - i)*nch] = (short)_mm_extract_epi16(pcm8, 0);
- dstl[(17 + i)*nch] = (short)_mm_extract_epi16(pcm8, 4);
- dstr[(47 - i)*nch] = (short)_mm_extract_epi16(pcm8, 3);
- dstr[(49 + i)*nch] = (short)_mm_extract_epi16(pcm8, 7);
- dstl[(47 - i)*nch] = (short)_mm_extract_epi16(pcm8, 2);
- dstl[(49 + i)*nch] = (short)_mm_extract_epi16(pcm8, 6);
+ dstr[(15 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 1);
+ dstr[(17 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 5);
+ dstl[(15 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 0);
+ dstl[(17 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 4);
+ dstr[(47 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 3);
+ dstr[(49 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 7);
+ dstl[(47 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 2);
+ dstl[(49 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 6);
#else
int16x4_t pcma, pcmb;
a = DRMP3_VADD(a, DRMP3_VSET(0.5f));
@@ -1784,6 +1778,30 @@ static void drmp3d_synth(float *xl, short *dstl, int nch, float *lins)
vst1_lane_s16(dstl + (47 - i)*nch, pcma, 2);
vst1_lane_s16(dstl + (49 + i)*nch, pcmb, 2);
#endif
+#else
+ static const drmp3_f4 g_scale = { 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f };
+ a = DRMP3_VMUL(a, g_scale);
+ b = DRMP3_VMUL(b, g_scale);
+#if DRMP3_HAVE_SSE
+ _mm_store_ss(dstr + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)));
+ _mm_store_ss(dstr + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(1, 1, 1, 1)));
+ _mm_store_ss(dstl + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)));
+ _mm_store_ss(dstl + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(0, 0, 0, 0)));
+ _mm_store_ss(dstr + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3)));
+ _mm_store_ss(dstr + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 3, 3, 3)));
+ _mm_store_ss(dstl + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)));
+ _mm_store_ss(dstl + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(2, 2, 2, 2)));
+#else
+ vst1q_lane_f32(dstr + (15 - i)*nch, a, 1);
+ vst1q_lane_f32(dstr + (17 + i)*nch, b, 1);
+ vst1q_lane_f32(dstl + (15 - i)*nch, a, 0);
+ vst1q_lane_f32(dstl + (17 + i)*nch, b, 0);
+ vst1q_lane_f32(dstr + (47 - i)*nch, a, 3);
+ vst1q_lane_f32(dstr + (49 + i)*nch, b, 3);
+ vst1q_lane_f32(dstl + (47 - i)*nch, a, 2);
+ vst1q_lane_f32(dstl + (49 + i)*nch, b, 2);
+#endif
+#endif /* DR_MP3_FLOAT_OUTPUT */
}
} else
#endif
@@ -1821,7 +1839,7 @@ static void drmp3d_synth(float *xl, short *dstl, int nch, float *lins)
#endif
}
-static void drmp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int nch, short *pcm, float *lins)
+static void drmp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int nch, drmp3d_sample_t *pcm, float *lins)
{
int i;
for (i = 0; i < nch; i++)
@@ -1906,7 +1924,7 @@ void drmp3dec_init(drmp3dec *dec)
dec->header[0] = 0;
}
-int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes, short *pcm, drmp3dec_frame_info *info)
+int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info)
{
int i = 0, igr, frame_size = 0, success = 1;
const drmp3_uint8 *hdr;
@@ -1940,6 +1958,11 @@ int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes
info->layer = 4 - DRMP3_HDR_GET_LAYER(hdr);
info->bitrate_kbps = drmp3_hdr_bitrate_kbps(hdr);
+ if (!pcm)
+ {
+ return drmp3_hdr_frame_samples(hdr);
+ }
+
drmp3_bs_init(bs_frame, hdr + DRMP3_HDR_SIZE, frame_size - DRMP3_HDR_SIZE);
if (DRMP3_HDR_IS_CRC(hdr))
{
@@ -1957,11 +1980,11 @@ int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes
success = drmp3_L3_restore_reservoir(dec, bs_frame, &scratch, main_data_begin);
if (success)
{
- for (igr = 0; igr < (DRMP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm += 576*info->channels)
+ for (igr = 0; igr < (DRMP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*576*info->channels))
{
memset(scratch.grbuf[0], 0, 576*2*sizeof(float));
drmp3_L3_decode(dec, &scratch, scratch.gr_info + igr*info->channels, info->channels);
- drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, pcm, scratch.syn[0]);
+ drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, (drmp3d_sample_t*)pcm, scratch.syn[0]);
}
}
drmp3_L3_save_reservoir(dec, &scratch);
@@ -1980,9 +2003,9 @@ int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes
{
i = 0;
drmp3_L12_apply_scf_384(sci, sci->scf + igr, scratch.grbuf[0]);
- drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, pcm, scratch.syn[0]);
+ drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, (drmp3d_sample_t*)pcm, scratch.syn[0]);
memset(scratch.grbuf[0], 0, 576*2*sizeof(float));
- pcm += 384*info->channels;
+ pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*384*info->channels);
}
if (bs_frame->pos > bs_frame->limit)
{
@@ -1995,6 +2018,64 @@ int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes
return success*drmp3_hdr_frame_samples(dec->header);
}
+void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, int num_samples)
+{
+ if(num_samples > 0)
+ {
+ int i = 0;
+#if DRMP3_HAVE_SIMD
+ int aligned_count = num_samples & ~7;
+ for(; i < aligned_count; i+=8)
+ {
+ static const drmp3_f4 g_scale = { 32768.0f, 32768.0f, 32768.0f, 32768.0f };
+ drmp3_f4 a = DRMP3_VMUL(DRMP3_VLD(&in[i ]), g_scale);
+ drmp3_f4 b = DRMP3_VMUL(DRMP3_VLD(&in[i+4]), g_scale);
+#if DRMP3_HAVE_SSE
+ static const drmp3_f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f };
+ static const drmp3_f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f };
+ __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)),
+ _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min)));
+ out[i ] = (drmp3_int16)_mm_extract_epi16(pcm8, 0);
+ out[i+1] = (drmp3_int16)_mm_extract_epi16(pcm8, 1);
+ out[i+2] = (drmp3_int16)_mm_extract_epi16(pcm8, 2);
+ out[i+3] = (drmp3_int16)_mm_extract_epi16(pcm8, 3);
+ out[i+4] = (drmp3_int16)_mm_extract_epi16(pcm8, 4);
+ out[i+5] = (drmp3_int16)_mm_extract_epi16(pcm8, 5);
+ out[i+6] = (drmp3_int16)_mm_extract_epi16(pcm8, 6);
+ out[i+7] = (drmp3_int16)_mm_extract_epi16(pcm8, 7);
+#else
+ int16x4_t pcma, pcmb;
+ a = DRMP3_VADD(a, DRMP3_VSET(0.5f));
+ b = DRMP3_VADD(b, DRMP3_VSET(0.5f));
+ pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, DRMP3_VSET(0)))));
+ pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, DRMP3_VSET(0)))));
+ vst1_lane_s16(out+i , pcma, 0);
+ vst1_lane_s16(out+i+1, pcma, 1);
+ vst1_lane_s16(out+i+2, pcma, 2);
+ vst1_lane_s16(out+i+3, pcma, 3);
+ vst1_lane_s16(out+i+4, pcmb, 0);
+ vst1_lane_s16(out+i+5, pcmb, 1);
+ vst1_lane_s16(out+i+6, pcmb, 2);
+ vst1_lane_s16(out+i+7, pcmb, 3);
+#endif
+ }
+#endif
+ for(; i < num_samples; i++)
+ {
+ float sample = in[i] * 32768.0f;
+ if (sample >= 32766.5)
+ out[i] = (drmp3_int16) 32767;
+ else if (sample <= -32767.5)
+ out[i] = (drmp3_int16)-32768;
+ else
+ {
+ short s = (drmp3_int16)(sample + .5f);
+ s -= (s < 0); /* away from zero, to be compliant */
+ out[i] = s;
+ }
+ }
+ }
+}
@@ -2004,6 +2085,16 @@ int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes
//
///////////////////////////////////////////////////////////////////////////////
+#if defined(SIZE_MAX)
+ #define DRMP3_SIZE_MAX SIZE_MAX
+#else
+ #if defined(_WIN64) || defined(_LP64) || defined(__LP64__)
+ #define DRMP3_SIZE_MAX ((drmp3_uint64)0xFFFFFFFFFFFFFFFF)
+ #else
+ #define DRMP3_SIZE_MAX 0xFFFFFFFF
+ #endif
+#endif
+
// Options.
#ifndef DR_MP3_DEFAULT_CHANNELS
#define DR_MP3_DEFAULT_CHANNELS 2
@@ -2311,8 +2402,10 @@ static drmp3_bool32 drmp3_decode_next_frame(drmp3* pMP3)
size_t bytesRead = pMP3->onRead(pMP3->pUserData, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize));
if (bytesRead == 0) {
- pMP3->atEnd = DRMP3_TRUE;
- return DRMP3_FALSE; // No data.
+ if (pMP3->dataSize == 0) {
+ pMP3->atEnd = DRMP3_TRUE;
+ return DRMP3_FALSE; // No data.
+ }
}
pMP3->dataSize += bytesRead;
@@ -2324,7 +2417,7 @@ static drmp3_bool32 drmp3_decode_next_frame(drmp3* pMP3)
}
drmp3dec_frame_info info;
- drmp3_uint32 samplesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->pData, (int)pMP3->dataSize, pMP3->frames, &info); // <-- Safe size_t -> int conversion thanks to the check above.
+ drmp3_uint32 samplesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->pData, (int)pMP3->dataSize, (drmp3d_sample_t*)pMP3->frames, &info); // <-- Safe size_t -> int conversion thanks to the check above.
if (samplesRead != 0) {
size_t leftoverDataSize = (pMP3->dataSize - (size_t)info.frame_bytes);
for (size_t i = 0; i < leftoverDataSize; ++i) {
@@ -2377,28 +2470,54 @@ static drmp3_uint64 drmp3_read_src(drmp3_src* pSRC, drmp3_uint64 frameCount, voi
while (frameCount > 0) {
// Read from the in-memory buffer first.
while (pMP3->framesRemaining > 0 && frameCount > 0) {
+ drmp3d_sample_t* frames = (drmp3d_sample_t*)pMP3->frames;
+#ifndef DR_MP3_FLOAT_OUTPUT
+ if (pMP3->frameChannels == 1) {
+ if (pMP3->channels == 1) {
+ // Mono -> Mono.
+ pFramesOutF[0] = frames[pMP3->framesConsumed] / 32768.0f;
+ } else {
+ // Mono -> Stereo.
+ pFramesOutF[0] = frames[pMP3->framesConsumed] / 32768.0f;
+ pFramesOutF[1] = frames[pMP3->framesConsumed] / 32768.0f;
+ }
+ } else {
+ if (pMP3->channels == 1) {
+ // Stereo -> Mono
+ float sample = 0;
+ sample += frames[(pMP3->framesConsumed*pMP3->frameChannels)+0] / 32768.0f;
+ sample += frames[(pMP3->framesConsumed*pMP3->frameChannels)+1] / 32768.0f;
+ pFramesOutF[0] = sample * 0.5f;
+ } else {
+ // Stereo -> Stereo
+ pFramesOutF[0] = frames[(pMP3->framesConsumed*pMP3->frameChannels)+0] / 32768.0f;
+ pFramesOutF[1] = frames[(pMP3->framesConsumed*pMP3->frameChannels)+1] / 32768.0f;
+ }
+ }
+#else
if (pMP3->frameChannels == 1) {
if (pMP3->channels == 1) {
// Mono -> Mono.
- pFramesOutF[0] = pMP3->frames[pMP3->framesConsumed] / 32768.0f;
+ pFramesOutF[0] = frames[pMP3->framesConsumed];
} else {
// Mono -> Stereo.
- pFramesOutF[0] = pMP3->frames[pMP3->framesConsumed] / 32768.0f;
- pFramesOutF[1] = pMP3->frames[pMP3->framesConsumed] / 32768.0f;
+ pFramesOutF[0] = frames[pMP3->framesConsumed];
+ pFramesOutF[1] = frames[pMP3->framesConsumed];
}
} else {
if (pMP3->channels == 1) {
// Stereo -> Mono
float sample = 0;
- sample += pMP3->frames[(pMP3->framesConsumed*pMP3->frameChannels)+0] / 32768.0f;
- sample += pMP3->frames[(pMP3->framesConsumed*pMP3->frameChannels)+1] / 32768.0f;
+ sample += frames[(pMP3->framesConsumed*pMP3->frameChannels)+0];
+ sample += frames[(pMP3->framesConsumed*pMP3->frameChannels)+1];
pFramesOutF[0] = sample * 0.5f;
} else {
// Stereo -> Stereo
- pFramesOutF[0] = pMP3->frames[(pMP3->framesConsumed*pMP3->frameChannels)+0] / 32768.0f;
- pFramesOutF[1] = pMP3->frames[(pMP3->framesConsumed*pMP3->frameChannels)+1] / 32768.0f;
+ pFramesOutF[0] = frames[(pMP3->framesConsumed*pMP3->frameChannels)+0];
+ pFramesOutF[1] = frames[(pMP3->framesConsumed*pMP3->frameChannels)+1];
}
}
+#endif
pMP3->framesConsumed += 1;
pMP3->framesRemaining -= 1;
@@ -2466,11 +2585,13 @@ drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek
srcConfig.channels = pMP3->channels;
srcConfig.algorithm = drmp3_src_algorithm_linear;
if (!drmp3_src_init(&srcConfig, drmp3_read_src, pMP3, &pMP3->src)) {
+ drmp3_uninit(pMP3);
return DRMP3_FALSE;
}
// Decode the first frame to confirm that it is indeed a valid MP3 stream.
if (!drmp3_decode_next_frame(pMP3)) {
+ drmp3_uninit(pMP3);
return DRMP3_FALSE; // Not a valid MP3 stream.
}
@@ -2681,7 +2802,7 @@ float* drmp3__full_decode_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, drmp
}
drmp3_uint64 newFramesBufferSize = framesCapacity*pMP3->channels*sizeof(float);
- if (newFramesBufferSize > SIZE_MAX) {
+ if (newFramesBufferSize > DRMP3_SIZE_MAX) {
break;
}
@@ -2771,6 +2892,37 @@ void drmp3_free(void* p)
// REVISION HISTORY
// ===============
//
+// v0.3.2 - 2018-09-11
+// - Fix a couple of memory leaks.
+// - Bring up to date with minimp3.
+//
+// v0.3.1 - 2018-08-25
+// - Fix C++ build.
+//
+// v0.3.0 - 2018-08-25
+// - Bring up to date with minimp3. This has a minor API change: the "pcm" parameter of drmp3dec_decode_frame() has
+// been changed from short* to void* because it can now output both s16 and f32 samples, depending on whether or
+// not the DR_MP3_FLOAT_OUTPUT option is set.
+//
+// v0.2.11 - 2018-08-08
+// - Fix a bug where the last part of a file is not read.
+//
+// v0.2.10 - 2018-08-07
+// - Improve 64-bit detection.
+//
+// v0.2.9 - 2018-08-05
+// - Fix C++ build on older versions of GCC.
+// - Bring up to date with minimp3.
+//
+// v0.2.8 - 2018-08-02
+// - Fix compilation errors with older versions of GCC.
+//
+// v0.2.7 - 2018-07-13
+// - Bring up to date with minimp3.
+//
+// v0.2.6 - 2018-07-12
+// - Bring up to date with minimp3.
+//
// v0.2.5 - 2018-06-22
// - Bring up to date with minimp3.
//
diff --git a/src/external/glfw/include/GLFW/glfw3native.h b/src/external/glfw/include/GLFW/glfw3native.h
index 4372cb76..d585496c 100644
--- a/src/external/glfw/include/GLFW/glfw3native.h
+++ b/src/external/glfw/include/GLFW/glfw3native.h
@@ -90,13 +90,21 @@ extern "C" {
#undef APIENTRY
#undef GLFW_APIENTRY_DEFINED
#endif
- #include <windows.h>
+// RAY: Actually, only HWND handler needs to be defined
+// Including windows.h could suppose symbols re-definition issues (i.e Rectangle type)
+//#include <windows.h>
+ typedef void *PVOID;
+ typedef PVOID HANDLE;
+ typedef HANDLE HWND;
#elif defined(GLFW_EXPOSE_NATIVE_COCOA)
#include <ApplicationServices/ApplicationServices.h>
#if defined(__OBJC__)
#import <Cocoa/Cocoa.h>
#else
- typedef void* id;
+ // RAY: Added protection in case OBJC types defined
+ #if !OBJC_TYPES_DEFINED
+ typedef void* id;
+ #endif
#endif
#elif defined(GLFW_EXPOSE_NATIVE_X11)
#include <X11/Xlib.h>
diff --git a/src/physac.h b/src/physac.h
index 38789a6d..038361a4 100644
--- a/src/physac.h
+++ b/src/physac.h
@@ -96,8 +96,6 @@
#define PHYSAC_MAX_VERTICES 24
#define PHYSAC_CIRCLE_VERTICES 24
-#define PHYSAC_DESIRED_DELTATIME 1.0/60.0
-#define PHYSAC_MAX_TIMESTEP 0.02
#define PHYSAC_COLLISION_ITERATIONS 100
#define PHYSAC_PENETRATION_ALLOWANCE 0.05f
#define PHYSAC_PENETRATION_CORRECTION 0.4f
@@ -197,6 +195,7 @@ extern "C" { // Prevents name mangling of fun
//----------------------------------------------------------------------------------
PHYSACDEF void InitPhysics(void); // Initializes physics values, pointers and creates physics loop thread
PHYSACDEF void RunPhysicsStep(void); // Run physics step, to be used if PHYSICS_NO_THREADS is set in your main loop
+PHYSACDEF void SetPhysicsTimeStep(double delta); // Sets physics fixed time step in milliseconds. 1.666666 by default
PHYSACDEF bool IsPhysicsEnabled(void); // Returns true if physics thread is currently enabled
PHYSACDEF void SetPhysicsGravity(float x, float y); // Sets physics global gravity force
PHYSACDEF PhysicsBody CreatePhysicsBodyCircle(Vector2 pos, float radius, float density); // Creates a new circle physics body with generic parameters
@@ -279,16 +278,15 @@ static pthread_t physicsThreadId; // Physics thread id
#endif
static unsigned int usedMemory = 0; // Total allocated dynamic memory
static bool physicsThreadEnabled = false; // Physics thread enabled state
-
static double baseTime = 0.0; // Offset time for MONOTONIC clock
static double startTime = 0.0; // Start time in milliseconds
-static double deltaTime = 0.0; // Delta time used for physics steps
+static double deltaTime = 1.0/60.0/10.0 * 1000; // Delta time used for physics steps, in milliseconds
static double currentTime = 0.0; // Current time in milliseconds
static uint64_t frequency = 0; // Hi-res clock frequency
static double accumulator = 0.0; // Physics time step delta time accumulator
static unsigned int stepsCount = 0; // Total physics steps processed
-static Vector2 gravityForce = { 0.0f, 9.81f/1000 }; // Physics world gravity force
+static Vector2 gravityForce = { 0.0f, 9.81f }; // Physics world gravity force
static PhysicsBody bodies[PHYSAC_MAX_BODIES]; // Physics bodies pointers array
static unsigned int physicsBodiesCount = 0; // Physics world current bodies counter
static PhysicsManifold contacts[PHYSAC_MAX_MANIFOLDS]; // Physics bodies pointers array
@@ -322,13 +320,12 @@ static bool BiasGreaterThan(float valueA, float valueB);
static Vector2 TriangleBarycenter(Vector2 v1, Vector2 v2, Vector2 v3); // Returns the barycenter of a triangle given by 3 points
static void InitTimer(void); // Initializes hi-resolution MONOTONIC timer
-static uint64_t GetTimeCount(void); // Get hi-res MONOTONIC time measure in seconds
-static double GetCurrentTime(void); // // Get hi-res MONOTONIC time measure in seconds
+static uint64_t GetTimeCount(void); // Get hi-res MONOTONIC time measure in mseconds
+static double GetCurrentTime(void); // Get current time measure in milliseconds
static int GetRandomNumber(int min, int max); // Returns a random number between min and max (both included)
// Math functions
-static void MathClamp(double *value, double min, double max); // Clamp a value in a range
static Vector2 MathCross(float value, Vector2 vector); // Returns the cross product of a vector and a value
static float MathCrossVector2(Vector2 v1, Vector2 v2); // Returns the cross product of two vectors
static float MathLenSqr(Vector2 vector); // Returns the len square root of a vector
@@ -363,6 +360,8 @@ PHYSACDEF void InitPhysics(void)
#if defined(PHYSAC_DEBUG)
printf("[PHYSAC] physics module initialized successfully\n");
#endif
+
+ accumulator = 0.0;
}
// Returns true if physics thread is currently enabled
@@ -607,7 +606,7 @@ PHYSACDEF void PhysicsShatter(PhysicsBody body, Vector2 position, float force)
{
int count = vertexData.vertexCount;
Vector2 bodyPos = body->position;
- Vector2 vertices[count];
+ Vector2 *vertices = (Vector2*)malloc(sizeof(Vector2) * count);
Mat2 trans = body->shape.transform;
for (int i = 0; i < count; i++) vertices[i] = vertexData.positions[i];
@@ -699,6 +698,8 @@ PHYSACDEF void PhysicsShatter(PhysicsBody body, Vector2 position, float force)
// Apply force to new physics body
PhysicsAddForce(newBody, forceDirection);
}
+
+ free(vertices);
}
}
}
@@ -917,6 +918,18 @@ PHYSACDEF void ClosePhysics(void)
#if !defined(PHYSAC_NO_THREADS)
pthread_join(physicsThreadId, NULL);
#endif
+
+ // Unitialize physics manifolds dynamic memory allocations
+ for (int i = physicsManifoldsCount - 1; i >= 0; i--) DestroyPhysicsManifold(contacts[i]);
+
+ // Unitialize physics bodies dynamic memory allocations
+ for (int i = physicsBodiesCount - 1; i >= 0; i--) DestroyPhysicsBody(bodies[i]);
+
+ #if defined(PHYSAC_DEBUG)
+ if (physicsBodiesCount > 0 || usedMemory != 0) printf("[PHYSAC] physics module closed with %i still allocated bodies [MEMORY: %i bytes]\n", physicsBodiesCount, usedMemory);
+ else if (physicsManifoldsCount > 0 || usedMemory != 0) printf("[PHYSAC] physics module closed with %i still allocated manifolds [MEMORY: %i bytes]\n", physicsManifoldsCount, usedMemory);
+ else printf("[PHYSAC] physics module closed successfully\n");
+ #endif
}
//----------------------------------------------------------------------------------
@@ -1011,7 +1024,6 @@ static void *PhysicsLoop(void *arg)
// Initialize physics loop thread values
physicsThreadEnabled = true;
- accumulator = 0;
// Physics update loop
while (physicsThreadEnabled)
@@ -1019,18 +1031,6 @@ static void *PhysicsLoop(void *arg)
RunPhysicsStep();
}
- // Unitialize physics manifolds dynamic memory allocations
- for (int i = physicsManifoldsCount - 1; i >= 0; i--) DestroyPhysicsManifold(contacts[i]);
-
- // Unitialize physics bodies dynamic memory allocations
- for (int i = physicsBodiesCount - 1; i >= 0; i--) DestroyPhysicsBody(bodies[i]);
-
- #if defined(PHYSAC_DEBUG)
- if (physicsBodiesCount > 0 || usedMemory != 0) printf("[PHYSAC] physics module closed with %i still allocated bodies [MEMORY: %i bytes]\n", physicsBodiesCount, usedMemory);
- else if (physicsManifoldsCount > 0 || usedMemory != 0) printf("[PHYSAC] physics module closed with %i still allocated manifolds [MEMORY: %i bytes]\n", physicsManifoldsCount, usedMemory);
- else printf("[PHYSAC] physics module closed successfully\n");
- #endif
-
return NULL;
}
@@ -1147,17 +1147,18 @@ PHYSACDEF void RunPhysicsStep(void)
currentTime = GetCurrentTime();
// Calculate current delta time
- deltaTime = currentTime - startTime;
+ const double delta = currentTime - startTime;
// Store the time elapsed since the last frame began
- accumulator += deltaTime;
-
- // Clamp accumulator to max time step to avoid bad performance
- MathClamp(&accumulator, 0.0, PHYSAC_MAX_TIMESTEP);
+ accumulator += delta;
// Fixed time stepping loop
- while (accumulator >= PHYSAC_DESIRED_DELTATIME)
+ while (accumulator >= deltaTime)
{
+#ifdef PHYSAC_DEBUG
+ //printf("currentTime %f, startTime %f, accumulator-pre %f, accumulator-post %f, delta %f, deltaTime %f\n",
+ // currentTime, startTime, accumulator, accumulator-deltaTime, delta, deltaTime);
+#endif
PhysicsStep();
accumulator -= deltaTime;
}
@@ -1166,6 +1167,11 @@ PHYSACDEF void RunPhysicsStep(void)
startTime = currentTime;
}
+PHYSACDEF void SetPhysicsTimeStep(double delta)
+{
+ deltaTime = delta;
+}
+
// Finds a valid index for a new manifold initialization
static int FindAvailableManifoldIndex()
{
@@ -1557,8 +1563,8 @@ static void IntegratePhysicsForces(PhysicsBody body)
if (body->useGravity)
{
- body->velocity.x += gravityForce.x*(deltaTime/2.0);
- body->velocity.y += gravityForce.y*(deltaTime/2.0);
+ body->velocity.x += gravityForce.x*(deltaTime/1000/2.0);
+ body->velocity.y += gravityForce.y*(deltaTime/1000/2.0);
}
if (!body->freezeOrient) body->angularVelocity += body->torque*body->inverseInertia*(deltaTime/2.0);
@@ -1592,7 +1598,7 @@ static void InitializePhysicsManifolds(PhysicsManifold manifold)
// Determine if we should perform a resting collision or not;
// The idea is if the only thing moving this object is gravity, then the collision should be performed without any restitution
- if (MathLenSqr(radiusV) < (MathLenSqr((Vector2){ gravityForce.x*deltaTime, gravityForce.y*deltaTime }) + PHYSAC_EPSILON)) manifold->restitution = 0;
+ if (MathLenSqr(radiusV) < (MathLenSqr((Vector2){ gravityForce.x*deltaTime/1000, gravityForce.y*deltaTime/1000 }) + PHYSAC_EPSILON)) manifold->restitution = 0;
}
}
@@ -1953,13 +1959,6 @@ static int GetRandomNumber(int min, int max)
return (rand()%(abs(max - min) + 1) + min);
}
-// Clamp a value in a range
-static inline void MathClamp(double *value, double min, double max)
-{
- if (*value < min) *value = min;
- else if (*value > max) *value = max;
-}
-
// Returns the cross product of a vector and a value
static inline Vector2 MathCross(float value, Vector2 vector)
{
diff --git a/src/raylib.h b/src/raylib.h
index a44b77ee..be2383b1 100644
--- a/src/raylib.h
+++ b/src/raylib.h
@@ -223,7 +223,7 @@
#define MOUSE_MIDDLE_BUTTON 2
// Touch points registered
-#define MAX_TOUCH_POINTS 2
+#define MAX_TOUCH_POINTS 10
// Gamepad Number
#define GAMEPAD_PLAYER1 0
@@ -311,7 +311,7 @@
// NOTE: MSC C++ compiler does not support compound literals (C99 feature)
// Plain structures in C++ (without constructors) can be initialized from { } initializers.
-#ifdef __cplusplus
+#if defined(__cplusplus)
#define CLITERAL
#else
#define CLITERAL (Color)
@@ -354,11 +354,11 @@
//----------------------------------------------------------------------------------
// Structures Definition
//----------------------------------------------------------------------------------
-#ifndef __cplusplus
// Boolean type
- #ifndef bool
- typedef enum { false, true } bool;
- #endif
+#if defined(__STDC__) && __STDC_VERSION__ >= 199901L
+ #include <stdbool.h>
+#elif !defined(__cplusplus) && !defined(bool)
+ typedef enum { false, true } bool;
#endif
// Vector2 type
@@ -786,7 +786,7 @@ typedef enum {
// Callbacks to be implemented by users
typedef void (*TraceLogCallback)(int msgType, const char *text, va_list args);
-#ifdef __cplusplus
+#if defined(__cplusplus)
extern "C" { // Prevents name mangling of functions
#endif
@@ -814,6 +814,13 @@ RLAPI void SetWindowMinSize(int width, int height); // Set window
RLAPI void SetWindowSize(int width, int height); // Set window dimensions
RLAPI int GetScreenWidth(void); // Get current screen width
RLAPI int GetScreenHeight(void); // Get current screen height
+RLAPI void *GetWindowHandle(void); // Get native window handle
+RLAPI int GetMonitorCount(void); // Get number of connected monitors
+RLAPI int GetMonitorWidth(int monitor); // Get primary monitor width
+RLAPI int GetMonitorHeight(int monitor); // Get primary monitor height
+RLAPI int GetMonitorPhysicalWidth(int monitor); // Get primary monitor physical width in millimetres
+RLAPI int GetMonitorPhysicalHeight(int monitor); // Get primary monitor physical height in millimetres
+RLAPI const char *GetMonitorName(int monitor); // Get the human-readable, UTF-8 encoded name of the primary monitor
// Cursor-related functions
RLAPI void ShowCursor(void); // Shows cursor
@@ -861,15 +868,20 @@ RLAPI void TakeScreenshot(const char *fileName); // Takes a scr
RLAPI int GetRandomValue(int min, int max); // Returns a random value between min and max (both included)
// Files management functions
+RLAPI bool FileExists(const char *fileName); // Check if file exists
RLAPI bool IsFileExtension(const char *fileName, const char *ext);// Check file extension
RLAPI const char *GetExtension(const char *fileName); // Get pointer to extension for a filename string
RLAPI const char *GetFileName(const char *filePath); // Get pointer to filename for a path string
+RLAPI const char *GetFileNameWithoutExt(const char *filePath); // Get filename string without extension (memory should be freed)
RLAPI const char *GetDirectoryPath(const char *fileName); // Get full path for a given fileName (uses static string)
RLAPI const char *GetWorkingDirectory(void); // Get current working directory (uses static string)
+RLAPI char **GetDirectoryFiles(const char *dirPath, int *count); // Get filenames in a directory path (memory should be freed)
+RLAPI void ClearDirectoryFiles(void); // Clear directory files paths buffers (free memory)
RLAPI bool ChangeDirectory(const char *dir); // Change working directory, returns true if success
RLAPI bool IsFileDropped(void); // Check if a file has been dropped into window
-RLAPI char **GetDroppedFiles(int *count); // Get dropped files names
-RLAPI void ClearDroppedFiles(void); // Clear dropped files paths buffer
+RLAPI char **GetDroppedFiles(int *count); // Get dropped files names (memory should be freed)
+RLAPI void ClearDroppedFiles(void); // Clear dropped files paths buffer (free memory)
+RLAPI long GetFileModTime(const char *fileName); // Get file modification time (last write time)
// Persistent storage management
RLAPI void StorageSaveValue(int position, int value); // Save integer value to storage file (to defined position)
@@ -1010,11 +1022,12 @@ RLAPI void ImageAlphaClear(Image *image, Color color, float threshold);
RLAPI void ImageAlphaCrop(Image *image, float threshold); // Crop image depending on alpha value
RLAPI void ImageAlphaPremultiply(Image *image); // Premultiply alpha channel
RLAPI void ImageCrop(Image *image, Rectangle crop); // Crop an image to a defined rectangle
-RLAPI void ImageResize(Image *image, int newWidth, int newHeight); // Resize image (bilinear filtering)
+RLAPI void ImageResize(Image *image, int newWidth, int newHeight); // Resize image (Bicubic scaling algorithm)
RLAPI void ImageResizeNN(Image *image, int newWidth,int newHeight); // Resize image (Nearest-Neighbor scaling algorithm)
RLAPI void ImageResizeCanvas(Image *image, int newWidth, int newHeight, int offsetX, int offsetY, Color color); // Resize canvas and fill with color
RLAPI void ImageMipmaps(Image *image); // Generate all mipmap levels for a provided image
RLAPI void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp); // Dither image data to 16bpp or lower (Floyd-Steinberg dithering)
+RLAPI Color *ImageExtractPalette(Image image, int maxPaletteSize, int *extractCount); // Extract color palette from image to maximum size (memory should be freed)
RLAPI Image ImageText(const char *text, int fontSize, Color color); // Create an image from text (default font)
RLAPI Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Color tint); // Create an image from text (custom sprite font)
RLAPI void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec); // Draw a source image within a destination image
@@ -1064,21 +1077,24 @@ RLAPI Font GetFontDefault(void);
RLAPI Font LoadFont(const char *fileName); // Load font from file into GPU memory (VRAM)
RLAPI Font LoadFontEx(const char *fileName, int fontSize, int charsCount, int *fontChars); // Load font from file with extended parameters
RLAPI CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int charsCount, int type); // Load font data for further use
-RLAPI Image GenImageFontAtlas(CharInfo *chars, int fontSize, int charsCount, int padding, int packMethod); // Generate image font atlas using chars info
+RLAPI Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int padding, int packMethod); // Generate image font atlas using chars info
RLAPI void UnloadFont(Font font); // Unload Font from GPU memory (VRAM)
// Text drawing functions
RLAPI void DrawFPS(int posX, int posY); // Shows current FPS
RLAPI void DrawText(const char *text, int posX, int posY, int fontSize, Color color); // Draw text (using default font)
-RLAPI void DrawTextEx(Font font, const char* text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text using font and additional parameters
+RLAPI void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text using font and additional parameters
// Text misc. functions
RLAPI int MeasureText(const char *text, int fontSize); // Measure string width for default font
RLAPI Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing); // Measure string size for Font
-RLAPI const char *FormatText(const char *text, ...); // Formatting of text with variables to 'embed'
-RLAPI const char *SubText(const char *text, int position, int length); // Get a piece of a text string
RLAPI int GetGlyphIndex(Font font, int character); // Get index position for a unicode character on font
+// Text string edition functions
+RLAPI const char *FormatText(const char *text, ...); // Formatting of text with variables to 'embed'
+RLAPI const char *SubText(const char *text, int position, int length); // Get a piece of a text string
+RLAPI char **SplitText(char *text, char delimiter, int *strCount); // Split text string into multiple strings (memory should be freed manually!)
+
//------------------------------------------------------------------------------------
// Basic 3d Shapes Drawing Functions (Module: models)
//------------------------------------------------------------------------------------
@@ -1264,7 +1280,7 @@ RLAPI void StopAudioStream(AudioStream stream); // Stop au
RLAPI void SetAudioStreamVolume(AudioStream stream, float volume); // Set volume for audio stream (1.0 is max level)
RLAPI void SetAudioStreamPitch(AudioStream stream, float pitch); // Set pitch for audio stream (1.0 is base level)
-#ifdef __cplusplus
+#if defined(__cplusplus)
}
#endif
diff --git a/src/raylib.ico b/src/raylib.ico
index afeb12b9..0cedcc55 100644
--- a/src/raylib.ico
+++ b/src/raylib.ico
Binary files differ
diff --git a/src/raylib.rc b/src/raylib.rc
index ee2a5fab..2aaa5e26 100644
--- a/src/raylib.rc
+++ b/src/raylib.rc
@@ -9,7 +9,7 @@ BEGIN
//BLOCK "080904E4" // English UK
BLOCK "040904E4" // English US
BEGIN
- VALUE "CompanyName", "raylib technologies"
+ //VALUE "CompanyName", "raylib technologies"
VALUE "FileDescription", "Created using raylib (www.raylib.com)"
VALUE "FileVersion", "2.0.0"
VALUE "InternalName", "raylib app"
diff --git a/src/raymath.h b/src/raymath.h
index 33116532..1d5e26f9 100644
--- a/src/raymath.h
+++ b/src/raymath.h
@@ -300,7 +300,7 @@ RMDEF Vector3 Vector3Add(Vector3 v1, Vector3 v2)
return result;
}
-// Substract two vectors
+// Subtract two vectors
RMDEF Vector3 Vector3Subtract(Vector3 v1, Vector3 v2)
{
Vector3 result = { v1.x - v2.x, v1.y - v2.y, v1.z - v2.z };
@@ -720,8 +720,8 @@ RMDEF Matrix MatrixAdd(Matrix left, Matrix right)
return result;
}
-// Substract two matrices (left - right)
-RMDEF Matrix MatrixSubstract(Matrix left, Matrix right)
+// Subtract two matrices (left - right)
+RMDEF Matrix MatrixSubtract(Matrix left, Matrix right)
{
Matrix result = MatrixIdentity();
diff --git a/src/rlgl.h b/src/rlgl.h
index 75246cab..d97d888e 100644
--- a/src/rlgl.h
+++ b/src/rlgl.h
@@ -378,7 +378,7 @@ typedef unsigned char byte;
} VrDevice;
#endif
-#ifdef __cplusplus
+#if defined(__cplusplus)
extern "C" { // Prevents name mangling of functions
#endif
@@ -446,9 +446,11 @@ void rlLoadExtensions(void *loader); // Load OpenGL extensions
Vector3 rlUnproject(Vector3 source, Matrix proj, Matrix view); // Get world coordinates from screen coordinates
// Textures data management
-unsigned int rlLoadTexture(void *data, int width, int height, int format, int mipmapCount); // Load texture in GPU
-void rlUpdateTexture(unsigned int id, int width, int height, int format, const void *data); // Update GPU texture with new data
-void rlUnloadTexture(unsigned int id);
+unsigned int rlLoadTexture(void *data, int width, int height, int format, int mipmapCount); // Load texture in GPU
+void rlUpdateTexture(unsigned int id, int width, int height, int format, const void *data); // Update GPU texture with new data
+void rlGetGlTextureFormats(int format, unsigned int *glInternalFormat, unsigned int *glFormat, unsigned int *glType); // Get OpenGL internal formats
+void rlUnloadTexture(unsigned int id); // Unload texture from GPU memory
+
void rlGenerateMipmaps(Texture2D *texture); // Generate mipmap data for selected texture
void *rlReadTexturePixels(Texture2D texture); // Read texture pixel data
unsigned char *rlReadScreenPixels(int width, int height); // Read screen pixel data (color buffer)
@@ -514,7 +516,7 @@ void TraceLog(int msgType, const char *text, ...); // Show trace log messag
int GetPixelDataSize(int width, int height, int format);// Get pixel data size in bytes (image or texture)
#endif
-#ifdef __cplusplus
+#if defined(__cplusplus)
}
#endif
@@ -551,7 +553,7 @@ int GetPixelDataSize(int width, int height, int format);// Get pixel data size i
#else
// APIENTRY for OpenGL function pointer declarations is required
#ifndef APIENTRY
- #ifdef _WIN32
+ #if defined(_WIN32)
#define APIENTRY __stdcall
#else
#define APIENTRY
@@ -913,9 +915,6 @@ static void SetStereoView(int eye, Matrix matProjection, Matrix matModelView); /
#endif // defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
-// Get OpenGL internal formats and data type from raylib PixelFormat
-static void GetGlFormats(int format, int *glInternalFormat, int *glFormat, int *glType);
-
#if defined(GRAPHICS_API_OPENGL_11)
static int GenerateMipmaps(unsigned char *data, int baseWidth, int baseHeight);
static Color *GenNextMipmap(Color *srcData, int srcWidth, int srcHeight);
@@ -1507,9 +1506,17 @@ void rlDeleteTextures(unsigned int id)
void rlDeleteRenderTextures(RenderTexture2D target)
{
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
- if (target.id > 0) glDeleteFramebuffers(1, &target.id);
if (target.texture.id > 0) glDeleteTextures(1, &target.texture.id);
- if (target.depth.id > 0) glDeleteTextures(1, &target.depth.id);
+ if (target.depth.id > 0)
+ {
+#if defined(GRAPHICS_API_OPENGL_21) || defined(GRAPHICS_API_OPENGL_ES2)
+ glDeleteRenderbuffers(1, &target.depth.id);
+#elif defined(GRAPHICS_API_OPENGL_33)
+ glDeleteTextures(1, &target.depth.id);
+#endif
+ }
+
+ if (target.id > 0) glDeleteFramebuffers(1, &target.id);
TraceLog(LOG_INFO, "[FBO ID %i] Unloaded render texture data from VRAM (GPU)", target.id);
#endif
@@ -1621,7 +1628,7 @@ void rlglInit(int width, int height)
// NOTE: We don't need to check again supported extensions but we do (GLAD already dealt with that)
glGetIntegerv(GL_NUM_EXTENSIONS, &numExt);
-#ifdef _MSC_VER
+#if defined(_MSC_VER)
const char **extList = malloc(sizeof(const char *)*numExt);
#else
const char *extList[numExt];
@@ -1632,17 +1639,13 @@ void rlglInit(int width, int height)
#elif defined(GRAPHICS_API_OPENGL_ES2)
char *extensions = (char *)glGetString(GL_EXTENSIONS); // One big const string
- // NOTE: We have to duplicate string because glGetString() returns a const value
- // If not duplicated, it fails in some systems (Raspberry Pi)
- // Equivalent to function: char *strdup(const char *str)
- char *extensionsDup;
- size_t len = strlen(extensions) + 1;
- void *newstr = malloc(len);
- if (newstr == NULL) extensionsDup = NULL;
- extensionsDup = (char *)memcpy(newstr, extensions, len);
+ // NOTE: We have to duplicate string because glGetString() returns a const string
+ int len = strlen(extensions) + 1;
+ char *extensionsDup = (char *)malloc(len);
+ strcpy(extensionsDup, extensions);
// NOTE: String could be splitted using strtok() function (string.h)
- // NOTE: strtok() modifies the received string, it can not be const
+ // NOTE: strtok() modifies the passed string, it can not be const
char *extList[512]; // Allocate 512 strings pointers (2 KB)
@@ -2015,8 +2018,8 @@ unsigned int rlLoadTexture(void *data, int width, int height, int format, int mi
{
unsigned int mipSize = GetPixelDataSize(mipWidth, mipHeight, format);
- int glInternalFormat, glFormat, glType;
- GetGlFormats(format, &glInternalFormat, &glFormat, &glType);
+ unsigned int glInternalFormat, glFormat, glType;
+ rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType);
TraceLog(LOG_DEBUG, "Load mipmap level %i (%i x %i), size: %i, offset: %i", i, mipWidth, mipHeight, mipSize, mipOffset);
@@ -2105,8 +2108,8 @@ void rlUpdateTexture(unsigned int id, int width, int height, int format, const v
{
glBindTexture(GL_TEXTURE_2D, id);
- int glInternalFormat, glFormat, glType;
- GetGlFormats(format, &glInternalFormat, &glFormat, &glType);
+ unsigned int glInternalFormat, glFormat, glType;
+ rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType);
if ((glInternalFormat != -1) && (format < COMPRESSED_DXT1_RGB))
{
@@ -2115,17 +2118,68 @@ void rlUpdateTexture(unsigned int id, int width, int height, int format, const v
else TraceLog(LOG_WARNING, "Texture format updating not supported");
}
+// Get OpenGL internal formats and data type from raylib PixelFormat
+void rlGetGlTextureFormats(int format, unsigned int *glInternalFormat, unsigned int *glFormat, unsigned int *glType)
+{
+ *glInternalFormat = -1;
+ *glFormat = -1;
+ *glType = -1;
+
+ switch (format)
+ {
+ #if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_21) || defined(GRAPHICS_API_OPENGL_ES2)
+ // NOTE: on OpenGL ES 2.0 (WebGL), internalFormat must match format and options allowed are: GL_LUMINANCE, GL_RGB, GL_RGBA
+ case UNCOMPRESSED_GRAYSCALE: *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_UNSIGNED_BYTE; break;
+ case UNCOMPRESSED_GRAY_ALPHA: *glInternalFormat = GL_LUMINANCE_ALPHA; *glFormat = GL_LUMINANCE_ALPHA; *glType = GL_UNSIGNED_BYTE; break;
+ case UNCOMPRESSED_R5G6B5: *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_UNSIGNED_SHORT_5_6_5; break;
+ case UNCOMPRESSED_R8G8B8: *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_UNSIGNED_BYTE; break;
+ case UNCOMPRESSED_R5G5B5A1: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_5_5_5_1; break;
+ case UNCOMPRESSED_R4G4B4A4: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_4_4_4_4; break;
+ case UNCOMPRESSED_R8G8B8A8: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_BYTE; break;
+ #if !defined(GRAPHICS_API_OPENGL_11)
+ case UNCOMPRESSED_R32: if (texFloatSupported) *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float
+ case UNCOMPRESSED_R32G32B32: if (texFloatSupported) *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float
+ case UNCOMPRESSED_R32G32B32A32: if (texFloatSupported) *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float
+ #endif
+ #elif defined(GRAPHICS_API_OPENGL_33)
+ case UNCOMPRESSED_GRAYSCALE: *glInternalFormat = GL_R8; *glFormat = GL_RED; *glType = GL_UNSIGNED_BYTE; break;
+ case UNCOMPRESSED_GRAY_ALPHA: *glInternalFormat = GL_RG8; *glFormat = GL_RG; *glType = GL_UNSIGNED_BYTE; break;
+ case UNCOMPRESSED_R5G6B5: *glInternalFormat = GL_RGB565; *glFormat = GL_RGB; *glType = GL_UNSIGNED_SHORT_5_6_5; break;
+ case UNCOMPRESSED_R8G8B8: *glInternalFormat = GL_RGB8; *glFormat = GL_RGB; *glType = GL_UNSIGNED_BYTE; break;
+ case UNCOMPRESSED_R5G5B5A1: *glInternalFormat = GL_RGB5_A1; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_5_5_5_1; break;
+ case UNCOMPRESSED_R4G4B4A4: *glInternalFormat = GL_RGBA4; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_4_4_4_4; break;
+ case UNCOMPRESSED_R8G8B8A8: *glInternalFormat = GL_RGBA8; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_BYTE; break;
+ case UNCOMPRESSED_R32: if (texFloatSupported) *glInternalFormat = GL_R32F; *glFormat = GL_RED; *glType = GL_FLOAT; break;
+ case UNCOMPRESSED_R32G32B32: if (texFloatSupported) *glInternalFormat = GL_RGB32F; *glFormat = GL_RGB; *glType = GL_FLOAT; break;
+ case UNCOMPRESSED_R32G32B32A32: if (texFloatSupported) *glInternalFormat = GL_RGBA32F; *glFormat = GL_RGBA; *glType = GL_FLOAT; break;
+ #endif
+ #if !defined(GRAPHICS_API_OPENGL_11)
+ case COMPRESSED_DXT1_RGB: if (texCompDXTSupported) *glInternalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; break;
+ case COMPRESSED_DXT1_RGBA: if (texCompDXTSupported) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; break;
+ case COMPRESSED_DXT3_RGBA: if (texCompDXTSupported) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; break;
+ case COMPRESSED_DXT5_RGBA: if (texCompDXTSupported) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; break;
+ case COMPRESSED_ETC1_RGB: if (texCompETC1Supported) *glInternalFormat = GL_ETC1_RGB8_OES; break; // NOTE: Requires OpenGL ES 2.0 or OpenGL 4.3
+ case COMPRESSED_ETC2_RGB: if (texCompETC2Supported) *glInternalFormat = GL_COMPRESSED_RGB8_ETC2; break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3
+ case COMPRESSED_ETC2_EAC_RGBA: if (texCompETC2Supported) *glInternalFormat = GL_COMPRESSED_RGBA8_ETC2_EAC; break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3
+ case COMPRESSED_PVRT_RGB: if (texCompPVRTSupported) *glInternalFormat = GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; break; // NOTE: Requires PowerVR GPU
+ case COMPRESSED_PVRT_RGBA: if (texCompPVRTSupported) *glInternalFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; break; // NOTE: Requires PowerVR GPU
+ case COMPRESSED_ASTC_4x4_RGBA: if (texCompASTCSupported) *glInternalFormat = GL_COMPRESSED_RGBA_ASTC_4x4_KHR; break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3
+ case COMPRESSED_ASTC_8x8_RGBA: if (texCompASTCSupported) *glInternalFormat = GL_COMPRESSED_RGBA_ASTC_8x8_KHR; break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3
+ #endif
+ default: TraceLog(LOG_WARNING, "Texture format not supported"); break;
+ }
+}
+
// Unload texture from GPU memory
void rlUnloadTexture(unsigned int id)
{
if (id > 0) glDeleteTextures(1, &id);
}
-
// Load a texture to be used for rendering (fbo with color and depth attachments)
RenderTexture2D rlLoadRenderTexture(int width, int height)
{
- RenderTexture2D target;
+ RenderTexture2D target = { 0 };
target.id = 0;
@@ -2205,8 +2259,16 @@ RenderTexture2D rlLoadRenderTexture(int width, int height)
default: break;
}
- glDeleteTextures(1, &target.texture.id);
- glDeleteTextures(1, &target.depth.id);
+ if (target.texture.id > 0) glDeleteTextures(1, &target.texture.id);
+ if (target.depth.id > 0)
+ {
+#if defined(USE_DEPTH_RENDERBUFFER)
+ glDeleteRenderbuffers(1, &target.depth.id);
+#elif defined(USE_DEPTH_TEXTURE)
+ glDeleteTextures(1, &target.depth.id);
+#endif
+ }
+
glDeleteFramebuffers(1, &target.id);
}
else TraceLog(LOG_INFO, "[FBO ID %i] Framebuffer object created successfully", target.id);
@@ -2745,8 +2807,8 @@ void *rlReadTexturePixels(Texture2D texture)
// GL_UNPACK_ALIGNMENT affects operations that write to OpenGL memory (glTexImage, etc.)
glPixelStorei(GL_PACK_ALIGNMENT, 1);
- int glInternalFormat, glFormat, glType;
- GetGlFormats(texture.format, &glInternalFormat, &glFormat, &glType);
+ unsigned int glInternalFormat, glFormat, glType;
+ rlGetGlTextureFormats(texture.format, &glInternalFormat, &glFormat, &glType);
unsigned int size = GetPixelDataSize(texture.width, texture.height, texture.format);
if ((glInternalFormat != -1) && (texture.format < COMPRESSED_DXT1_RGB))
@@ -3757,7 +3819,7 @@ static unsigned int LoadShaderProgram(unsigned int vShaderId, unsigned int fShad
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength);
-#ifdef _MSC_VER
+#if defined(_MSC_VER)
char *log = malloc(maxLength);
#else
char log[maxLength];
@@ -3766,7 +3828,7 @@ static unsigned int LoadShaderProgram(unsigned int vShaderId, unsigned int fShad
TraceLog(LOG_INFO, "%s", log);
-#ifdef _MSC_VER
+#if defined(_MSC_VER)
free(log);
#endif
glDeleteProgram(program);
@@ -4591,58 +4653,6 @@ static void SetStereoView(int eye, Matrix matProjection, Matrix matModelView)
#endif //defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
-// Get OpenGL internal formats and data type from raylib PixelFormat
-static void GetGlFormats(int format, int *glInternalFormat, int *glFormat, int *glType)
-{
- *glInternalFormat = -1;
- *glFormat = -1;
- *glType = -1;
-
- switch (format)
- {
- #if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_21) || defined(GRAPHICS_API_OPENGL_ES2)
- // NOTE: on OpenGL ES 2.0 (WebGL), internalFormat must match format and options allowed are: GL_LUMINANCE, GL_RGB, GL_RGBA
- case UNCOMPRESSED_GRAYSCALE: *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_UNSIGNED_BYTE; break;
- case UNCOMPRESSED_GRAY_ALPHA: *glInternalFormat = GL_LUMINANCE_ALPHA; *glFormat = GL_LUMINANCE_ALPHA; *glType = GL_UNSIGNED_BYTE; break;
- case UNCOMPRESSED_R5G6B5: *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_UNSIGNED_SHORT_5_6_5; break;
- case UNCOMPRESSED_R8G8B8: *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_UNSIGNED_BYTE; break;
- case UNCOMPRESSED_R5G5B5A1: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_5_5_5_1; break;
- case UNCOMPRESSED_R4G4B4A4: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_4_4_4_4; break;
- case UNCOMPRESSED_R8G8B8A8: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_BYTE; break;
- #if !defined(GRAPHICS_API_OPENGL_11)
- case UNCOMPRESSED_R32: if (texFloatSupported) *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float
- case UNCOMPRESSED_R32G32B32: if (texFloatSupported) *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float
- case UNCOMPRESSED_R32G32B32A32: if (texFloatSupported) *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float
- #endif
- #elif defined(GRAPHICS_API_OPENGL_33)
- case UNCOMPRESSED_GRAYSCALE: *glInternalFormat = GL_R8; *glFormat = GL_RED; *glType = GL_UNSIGNED_BYTE; break;
- case UNCOMPRESSED_GRAY_ALPHA: *glInternalFormat = GL_RG8; *glFormat = GL_RG; *glType = GL_UNSIGNED_BYTE; break;
- case UNCOMPRESSED_R5G6B5: *glInternalFormat = GL_RGB565; *glFormat = GL_RGB; *glType = GL_UNSIGNED_SHORT_5_6_5; break;
- case UNCOMPRESSED_R8G8B8: *glInternalFormat = GL_RGB8; *glFormat = GL_RGB; *glType = GL_UNSIGNED_BYTE; break;
- case UNCOMPRESSED_R5G5B5A1: *glInternalFormat = GL_RGB5_A1; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_5_5_5_1; break;
- case UNCOMPRESSED_R4G4B4A4: *glInternalFormat = GL_RGBA4; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_4_4_4_4; break;
- case UNCOMPRESSED_R8G8B8A8: *glInternalFormat = GL_RGBA8; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_BYTE; break;
- case UNCOMPRESSED_R32: if (texFloatSupported) *glInternalFormat = GL_R32F; *glFormat = GL_RED; *glType = GL_FLOAT; break;
- case UNCOMPRESSED_R32G32B32: if (texFloatSupported) *glInternalFormat = GL_RGB32F; *glFormat = GL_RGB; *glType = GL_FLOAT; break;
- case UNCOMPRESSED_R32G32B32A32: if (texFloatSupported) *glInternalFormat = GL_RGBA32F; *glFormat = GL_RGBA; *glType = GL_FLOAT; break;
- #endif
- #if !defined(GRAPHICS_API_OPENGL_11)
- case COMPRESSED_DXT1_RGB: if (texCompDXTSupported) *glInternalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; break;
- case COMPRESSED_DXT1_RGBA: if (texCompDXTSupported) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; break;
- case COMPRESSED_DXT3_RGBA: if (texCompDXTSupported) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; break;
- case COMPRESSED_DXT5_RGBA: if (texCompDXTSupported) *glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; break;
- case COMPRESSED_ETC1_RGB: if (texCompETC1Supported) *glInternalFormat = GL_ETC1_RGB8_OES; break; // NOTE: Requires OpenGL ES 2.0 or OpenGL 4.3
- case COMPRESSED_ETC2_RGB: if (texCompETC2Supported) *glInternalFormat = GL_COMPRESSED_RGB8_ETC2; break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3
- case COMPRESSED_ETC2_EAC_RGBA: if (texCompETC2Supported) *glInternalFormat = GL_COMPRESSED_RGBA8_ETC2_EAC; break; // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3
- case COMPRESSED_PVRT_RGB: if (texCompPVRTSupported) *glInternalFormat = GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; break; // NOTE: Requires PowerVR GPU
- case COMPRESSED_PVRT_RGBA: if (texCompPVRTSupported) *glInternalFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; break; // NOTE: Requires PowerVR GPU
- case COMPRESSED_ASTC_4x4_RGBA: if (texCompASTCSupported) *glInternalFormat = GL_COMPRESSED_RGBA_ASTC_4x4_KHR; break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3
- case COMPRESSED_ASTC_8x8_RGBA: if (texCompASTCSupported) *glInternalFormat = GL_COMPRESSED_RGBA_ASTC_8x8_KHR; break; // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3
- #endif
- default: TraceLog(LOG_WARNING, "Texture format not supported"); break;
- }
-}
-
#if defined(GRAPHICS_API_OPENGL_11)
// Mipmaps data is generated after image data
// NOTE: Only works with RGBA (4 bytes) data!
diff --git a/src/text.c b/src/text.c
index 3dbb261b..a010666e 100644
--- a/src/text.c
+++ b/src/text.c
@@ -276,15 +276,7 @@ Font LoadFont(const char *fileName)
Font font = { 0 };
#if defined(SUPPORT_FILEFORMAT_TTF)
- if (IsFileExtension(fileName, ".ttf"))
- {
- font.baseSize = DEFAULT_TTF_FONTSIZE;
- font.charsCount = DEFAULT_TTF_NUMCHARS;
- font.chars = LoadFontData(fileName, font.baseSize, NULL, font.charsCount, FONT_DEFAULT);
- Image atlas = GenImageFontAtlas(font.chars, font.charsCount, font.baseSize, 4, 0);
- font.texture = LoadTextureFromImage(atlas);
- UnloadImage(atlas);
- }
+ if (IsFileExtension(fileName, ".ttf")) font = LoadFontEx(fileName, DEFAULT_TTF_FONTSIZE, DEFAULT_TTF_NUMCHARS, NULL);
else
#endif
#if defined(SUPPORT_FILEFORMAT_FNT)
@@ -317,9 +309,14 @@ Font LoadFontEx(const char *fileName, int fontSize, int charsCount, int *fontCha
font.baseSize = fontSize;
font.charsCount = (charsCount > 0) ? charsCount : 95;
font.chars = LoadFontData(fileName, font.baseSize, fontChars, font.charsCount, FONT_DEFAULT);
- Image atlas = GenImageFontAtlas(font.chars, font.charsCount, font.baseSize, 2, 0);
- font.texture = LoadTextureFromImage(atlas);
- UnloadImage(atlas);
+
+ if (font.chars != NULL)
+ {
+ Image atlas = GenImageFontAtlas(font.chars, font.charsCount, font.baseSize, 2, 0);
+ font.texture = LoadTextureFromImage(atlas);
+ UnloadImage(atlas);
+ }
+ else font = GetFontDefault();
return font;
}
@@ -335,91 +332,97 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c
#define SDF_PIXEL_DIST_SCALE 64.0f
#define BITMAP_ALPHA_THRESHOLD 80
-
- // In case no chars count provided, default to 95
- charsCount = (charsCount > 0) ? charsCount : 95;
-
- CharInfo *chars = (CharInfo *)malloc(charsCount*sizeof(CharInfo));
-
+
+ CharInfo *chars = NULL;
+
// Load font data (including pixel data) from TTF file
// NOTE: Loaded information should be enough to generate font image atlas,
// using any packaging method
FILE *fontFile = fopen(fileName, "rb"); // Load font file
- fseek(fontFile, 0, SEEK_END);
- long size = ftell(fontFile); // Get file size
- fseek(fontFile, 0, SEEK_SET); // Reset file pointer
-
- unsigned char *fontBuffer = (unsigned char *)malloc(size);
-
- fread(fontBuffer, size, 1, fontFile);
- fclose(fontFile);
-
- // Init font for data reading
- stbtt_fontinfo fontInfo;
- if (!stbtt_InitFont(&fontInfo, fontBuffer, 0)) TraceLog(LOG_WARNING, "Failed to init font!");
+ if (fontFile != NULL)
+ {
+ fseek(fontFile, 0, SEEK_END);
+ long size = ftell(fontFile); // Get file size
+ fseek(fontFile, 0, SEEK_SET); // Reset file pointer
+
+ unsigned char *fontBuffer = (unsigned char *)malloc(size);
+
+ fread(fontBuffer, size, 1, fontFile);
+ fclose(fontFile);
- // Calculate font scale factor
- float scaleFactor = stbtt_ScaleForPixelHeight(&fontInfo, (float)fontSize);
+ // Init font for data reading
+ stbtt_fontinfo fontInfo;
+ if (!stbtt_InitFont(&fontInfo, fontBuffer, 0)) TraceLog(LOG_WARNING, "Failed to init font!");
- // Calculate font basic metrics
- // NOTE: ascent is equivalent to font baseline
- int ascent, descent, lineGap;
- stbtt_GetFontVMetrics(&fontInfo, &ascent, &descent, &lineGap);
-
- // Fill fontChars in case not provided externally
- // NOTE: By default we fill charsCount consecutevely, starting at 32 (Space)
- int genFontChars = false;
- if (fontChars == NULL) genFontChars = true;
- if (genFontChars)
- {
- fontChars = (int *)malloc(charsCount*sizeof(int));
- for (int i = 0; i < charsCount; i++) fontChars[i] = i + 32;
- }
-
- // NOTE: Using simple packaging, one char after another
- for (int i = 0; i < charsCount; i++)
- {
- int chw = 0, chh = 0; // Character width and height (on generation)
- int ch = fontChars[i]; // Character value to get info for
- chars[i].value = ch;
+ // Calculate font scale factor
+ float scaleFactor = stbtt_ScaleForPixelHeight(&fontInfo, (float)fontSize);
+
+ // Calculate font basic metrics
+ // NOTE: ascent is equivalent to font baseline
+ int ascent, descent, lineGap;
+ stbtt_GetFontVMetrics(&fontInfo, &ascent, &descent, &lineGap);
- // Render a unicode codepoint to a bitmap
- // stbtt_GetCodepointBitmap() -- allocates and returns a bitmap
- // stbtt_GetCodepointBitmapBox() -- how big the bitmap must be
- // stbtt_MakeCodepointBitmap() -- renders into bitmap you provide
+ // In case no chars count provided, default to 95
+ charsCount = (charsCount > 0) ? charsCount : 95;
- if (type != FONT_SDF) chars[i].data = stbtt_GetCodepointBitmap(&fontInfo, scaleFactor, scaleFactor, ch, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY);
- else if (ch != 32) chars[i].data = stbtt_GetCodepointSDF(&fontInfo, scaleFactor, ch, SDF_CHAR_PADDING, SDF_ON_EDGE_VALUE, SDF_PIXEL_DIST_SCALE, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY);
+ // Fill fontChars in case not provided externally
+ // NOTE: By default we fill charsCount consecutevely, starting at 32 (Space)
+ int genFontChars = false;
+ if (fontChars == NULL)
+ {
+ fontChars = (int *)malloc(charsCount*sizeof(int));
+ for (int i = 0; i < charsCount; i++) fontChars[i] = i + 32;
+ genFontChars = true;
+ }
+
+ chars = (CharInfo *)malloc(charsCount*sizeof(CharInfo));
- if (type == FONT_BITMAP)
+ // NOTE: Using simple packaging, one char after another
+ for (int i = 0; i < charsCount; i++)
{
- // Aliased bitmap (black & white) font generation, avoiding anti-aliasing
- // NOTE: For optimum results, bitmap font should be generated at base pixel size
- for (int p = 0; p < chw*chh; p++)
+ int chw = 0, chh = 0; // Character width and height (on generation)
+ int ch = fontChars[i]; // Character value to get info for
+ chars[i].value = ch;
+
+ // Render a unicode codepoint to a bitmap
+ // stbtt_GetCodepointBitmap() -- allocates and returns a bitmap
+ // stbtt_GetCodepointBitmapBox() -- how big the bitmap must be
+ // stbtt_MakeCodepointBitmap() -- renders into bitmap you provide
+
+ if (type != FONT_SDF) chars[i].data = stbtt_GetCodepointBitmap(&fontInfo, scaleFactor, scaleFactor, ch, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY);
+ else if (ch != 32) chars[i].data = stbtt_GetCodepointSDF(&fontInfo, scaleFactor, ch, SDF_CHAR_PADDING, SDF_ON_EDGE_VALUE, SDF_PIXEL_DIST_SCALE, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY);
+
+ if (type == FONT_BITMAP)
{
- if (chars[i].data[p] < BITMAP_ALPHA_THRESHOLD) chars[i].data[p] = 0;
- else chars[i].data[p] = 255;
+ // Aliased bitmap (black & white) font generation, avoiding anti-aliasing
+ // NOTE: For optimum results, bitmap font should be generated at base pixel size
+ for (int p = 0; p < chw*chh; p++)
+ {
+ if (chars[i].data[p] < BITMAP_ALPHA_THRESHOLD) chars[i].data[p] = 0;
+ else chars[i].data[p] = 255;
+ }
}
- }
-
- chars[i].rec.width = (float)chw;
- chars[i].rec.height = (float)chh;
- chars[i].offsetY += (int)((float)ascent*scaleFactor);
-
- // Get bounding box for character (may be offset to account for chars that dip above or below the line)
- int chX1, chY1, chX2, chY2;
- stbtt_GetCodepointBitmapBox(&fontInfo, ch, scaleFactor, scaleFactor, &chX1, &chY1, &chX2, &chY2);
+
+ chars[i].rec.width = (float)chw;
+ chars[i].rec.height = (float)chh;
+ chars[i].offsetY += (int)((float)ascent*scaleFactor);
- TraceLog(LOG_DEBUG, "Character box measures: %i, %i, %i, %i", chX1, chY1, chX2 - chX1, chY2 - chY1);
- TraceLog(LOG_DEBUG, "Character offsetY: %i", (int)((float)ascent*scaleFactor) + chY1);
+ // Get bounding box for character (may be offset to account for chars that dip above or below the line)
+ int chX1, chY1, chX2, chY2;
+ stbtt_GetCodepointBitmapBox(&fontInfo, ch, scaleFactor, scaleFactor, &chX1, &chY1, &chX2, &chY2);
+
+ TraceLog(LOG_DEBUG, "Character box measures: %i, %i, %i, %i", chX1, chY1, chX2 - chX1, chY2 - chY1);
+ TraceLog(LOG_DEBUG, "Character offsetY: %i", (int)((float)ascent*scaleFactor) + chY1);
- stbtt_GetCodepointHMetrics(&fontInfo, ch, &chars[i].advanceX, NULL);
- chars[i].advanceX *= scaleFactor;
+ stbtt_GetCodepointHMetrics(&fontInfo, ch, &chars[i].advanceX, NULL);
+ chars[i].advanceX *= scaleFactor;
+ }
+
+ free(fontBuffer);
+ if (genFontChars) free(fontChars);
}
-
- free(fontBuffer);
- if (genFontChars) free(fontChars);
+ else TraceLog(LOG_WARNING, "[%s] TTF file could not be opened", fileName);
return chars;
}
@@ -527,6 +530,7 @@ Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int paddi
else TraceLog(LOG_WARNING, "Character could not be packed: %i", i);
}
+ free(rects);
free(nodes);
free(context);
}
@@ -563,6 +567,28 @@ void UnloadFont(Font font)
}
}
+// Shows current FPS on top-left corner
+// NOTE: Uses default font
+void DrawFPS(int posX, int posY)
+{
+ // NOTE: We are rendering fps every second for better viewing on high framerates
+
+ static int fps = 0;
+ static int counter = 0;
+ static int refreshRate = 20;
+
+ if (counter < refreshRate) counter++;
+ else
+ {
+ fps = GetFPS();
+ refreshRate = fps;
+ counter = 0;
+ }
+
+ // NOTE: We have rounding errors every frame, so it oscillates a lot
+ DrawText(FormatText("%2i FPS", fps), posX, posY, 20, LIME);
+}
+
// Draw text (using default font)
// NOTE: fontSize work like in any drawing program but if fontSize is lower than font-base-size, then font-base-size is used
// NOTE: chars spacing is proportional to fontSize
@@ -588,10 +614,10 @@ void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, f
int length = strlen(text);
int textOffsetX = 0; // Offset between characters
int textOffsetY = 0; // Required for line break!
- float scaleFactor;
+ float scaleFactor = 0.0f;
- unsigned char letter; // Current character
- int index; // Index position in sprite font
+ unsigned char letter = 0; // Current character
+ int index = 0; // Index position in sprite font
scaleFactor = fontSize/font.baseSize;
@@ -639,44 +665,6 @@ void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, f
}
}
-// Formatting of text with variables to 'embed'
-const char *FormatText(const char *text, ...)
-{
- static char buffer[MAX_FORMATTEXT_LENGTH];
-
- va_list args;
- va_start(args, text);
- vsprintf(buffer, text, args);
- va_end(args);
-
- return buffer;
-}
-
-// Get a piece of a text string
-const char *SubText(const char *text, int position, int length)
-{
- static char buffer[MAX_SUBTEXT_LENGTH];
- int textLength = strlen(text);
-
- if (position >= textLength)
- {
- position = textLength - 1;
- length = 0;
- }
-
- if (length >= textLength) length = textLength;
-
- for (int c = 0 ; c < length ; c++)
- {
- *(buffer + c) = *(text + position);
- text++;
- }
-
- *(buffer + length) = '\0';
-
- return buffer;
-}
-
// Measure string width for default font
int MeasureText(const char *text, int fontSize)
{
@@ -702,8 +690,8 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing
int tempLen = 0; // Used to count longer text line num chars
int lenCounter = 0;
- float textWidth = 0;
- float tempTextWidth = 0; // Used to count longer text line width
+ float textWidth = 0.0f;
+ float tempTextWidth = 0.0f; // Used to count longer text line width
float textHeight = (float)font.baseSize;
float scaleFactor = fontSize/(float)font.baseSize;
@@ -761,26 +749,78 @@ int GetGlyphIndex(Font font, int character)
#endif
}
-// Shows current FPS on top-left corner
-// NOTE: Uses default font
-void DrawFPS(int posX, int posY)
+// Formatting of text with variables to 'embed'
+const char *FormatText(const char *text, ...)
{
- // NOTE: We are rendering fps every second for better viewing on high framerates
+ static char buffer[MAX_FORMATTEXT_LENGTH];
- static int fps = 0;
- static int counter = 0;
- static int refreshRate = 20;
+ va_list args;
+ va_start(args, text);
+ vsprintf(buffer, text, args);
+ va_end(args);
- if (counter < refreshRate) counter++;
- else
+ return buffer;
+}
+
+// Get a piece of a text string
+const char *SubText(const char *text, int position, int length)
+{
+ static char buffer[MAX_SUBTEXT_LENGTH] = { 0 };
+ int textLength = strlen(text);
+
+ if (position >= textLength)
{
- fps = GetFPS();
- refreshRate = fps;
- counter = 0;
+ position = textLength - 1;
+ length = 0;
+ }
+
+ if (length >= textLength) length = textLength;
+
+ for (int c = 0 ; c < length ; c++)
+ {
+ *(buffer + c) = *(text + position);
+ text++;
+ }
+
+ *(buffer + length) = '\0';
+
+ return buffer;
+}
+
+// Split string into multiple strings
+// NOTE: Files count is returned by parameters pointer
+// NOTE: Allocated memory should be manually freed
+char **SplitText(char *text, char delimiter, int *strCount)
+{
+ #define MAX_SUBSTRING_LENGTH 128
+
+ char **strings = NULL;
+ int len = strlen(text);
+ char *strDup = (char *)malloc(len + 1);
+ strcpy(strDup, text);
+ int counter = 1;
+
+ // Count how many substrings we have on string
+ for (int i = 0; i < len; i++) if (text[i] == delimiter) counter++;
+
+ // Memory allocation for substrings
+ strings = (char **)malloc(sizeof(char *)*counter);
+ for (int i = 0; i < counter; i++) strings[i] = (char *)malloc(sizeof(char)*MAX_SUBSTRING_LENGTH);
+
+ char *substrPtr = NULL;
+ char delimiters[1] = { delimiter }; // Only caring for one delimiter
+ substrPtr = strtok(strDup, delimiters);
+
+ for (int i = 0; (i < counter) && (substrPtr != NULL); i++)
+ {
+ strcpy(strings[i], substrPtr);
+ substrPtr = strtok(NULL, delimiters);
}
- // NOTE: We have rounding errors every frame, so it oscillates a lot
- DrawText(FormatText("%2i FPS", fps), posX, posY, 20, LIME);
+ *strCount = counter;
+ free(strDup);
+
+ return strings;
}
//----------------------------------------------------------------------------------
@@ -911,17 +951,18 @@ static Font LoadBMFont(const char *fileName)
Font font = { 0 };
font.texture.id = 0;
- char buffer[MAX_BUFFER_SIZE];
+ char buffer[MAX_BUFFER_SIZE] = { 0 };
char *searchPoint = NULL;
int fontSize = 0;
- int texWidth, texHeight;
+ int texWidth = 0;
+ int texHeight = 0;
char texFileName[129];
int charsCount = 0;
- int base; // Useless data
+ int base = 0; // Useless data
- FILE *fntFile;
+ FILE *fntFile = NULL;
fntFile = fopen(fileName, "rt");
diff --git a/src/textures.c b/src/textures.c
index a7b9d3e2..a0e74637 100644
--- a/src/textures.c
+++ b/src/textures.c
@@ -148,6 +148,7 @@ static Image LoadPKM(const char *fileName); // Load PKM file
#endif
#if defined(SUPPORT_FILEFORMAT_KTX)
static Image LoadKTX(const char *fileName); // Load KTX file
+static void SaveKTX(Image image, const char *fileName); // Save image data as KTX file
#endif
#if defined(SUPPORT_FILEFORMAT_PVR)
static Image LoadPVR(const char *fileName); // Load PVR file
@@ -322,7 +323,7 @@ Image LoadImageRaw(const char *fileName, int width, int height, int format, int
// NOTE: fread() returns num read elements instead of bytes,
// to get bytes we need to read (1 byte size, elements) instead of (x byte size, 1 element)
- size_t bytes = fread(image.data, 1, size, rawFile);
+ int bytes = fread(image.data, 1, size, rawFile);
// Check if data has been read successfully
if (bytes < size)
@@ -726,10 +727,11 @@ void ExportImage(Image image, const char *fileName)
if (IsFileExtension(fileName, ".png")) success = stbi_write_png(fileName, image.width, image.height, 4, imgData, image.width*4);
else if (IsFileExtension(fileName, ".bmp")) success = stbi_write_bmp(fileName, image.width, image.height, 4, imgData);
else if (IsFileExtension(fileName, ".tga")) success = stbi_write_tga(fileName, image.width, image.height, 4, imgData);
- else if (IsFileExtension(fileName, ".jpg")) success = stbi_write_jpg(fileName, image.width, image.height, 4, imgData, 80); // Between 1 and 100
+ else if (IsFileExtension(fileName, ".jpg")) success = stbi_write_jpg(fileName, image.width, image.height, 4, imgData, 80); // JPG quality: between 1 and 100
+ else if (IsFileExtension(fileName, ".ktx")) SaveKTX(image, fileName);
else if (IsFileExtension(fileName, ".raw"))
{
- // Export raw pixel data
+ // Export raw pixel data (without header)
// NOTE: It's up to the user to track image parameters
FILE *rawFile = fopen(fileName, "wb");
fwrite(image.data, GetPixelDataSize(image.width, image.height, image.format), 1, rawFile);
@@ -1443,6 +1445,57 @@ void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp)
}
}
+// Extract color palette from image to maximum size
+// NOTE: Memory allocated should be freed manually!
+Color *ImageExtractPalette(Image image, int maxPaletteSize, int *extractCount)
+{
+ #define COLOR_EQUAL(col1, col2) ((col1.r == col2.r)&&(col1.g == col2.g)&&(col1.b == col2.b)&&(col1.a == col2.a))
+
+ Color *pixels = GetImageData(image);
+ Color *palette = (Color *)malloc(maxPaletteSize*sizeof(Color));
+
+ int palCount = 0;
+ for (int i = 0; i < maxPaletteSize; i++) palette[i] = BLANK; // Set all colors to BLANK
+
+ for (int i = 0; i < image.width*image.height; i++)
+ {
+ if (pixels[i].a > 0)
+ {
+ bool colorInPalette = false;
+
+ // Check if the color is already on palette
+ for (int j = 0; j < maxPaletteSize; j++)
+ {
+ if (COLOR_EQUAL(pixels[i], palette[j]))
+ {
+ colorInPalette = true;
+ break;
+ }
+ }
+
+ // Store color if not on the palette
+ if (!colorInPalette)
+ {
+ palette[palCount] = pixels[i]; // Add pixels[i] to palette
+ palCount++;
+
+ // We reached the limit of colors supported by palette
+ if (palCount >= maxPaletteSize)
+ {
+ i = image.width*image.height; // Finish palette get
+ printf("WARNING: Image palette is greater than %i colors!\n", maxPaletteSize);
+ }
+ }
+ }
+ }
+
+ free(pixels);
+
+ *extractCount = palCount;
+
+ return palette;
+}
+
// Draw an image (source) within an image (destination)
// TODO: Feel this function could be simplified...
void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec)
@@ -2727,7 +2780,7 @@ static Image LoadDDS(const char *fileName)
TraceLog(LOG_DEBUG, "Pitch or linear size: %i", ddsHeader.pitchOrLinearSize);
- image.data = (unsigned char*)malloc(size*sizeof(unsigned char));
+ image.data = (unsigned char *)malloc(size*sizeof(unsigned char));
fread(image.data, size, 1, ddsFile);
@@ -2824,7 +2877,7 @@ static Image LoadPKM(const char *fileName)
int size = image.width*image.height*bpp/8; // Total data size in bytes
- image.data = (unsigned char*)malloc(size*sizeof(unsigned char));
+ image.data = (unsigned char *)malloc(size*sizeof(unsigned char));
fread(image.data, size, 1, pkmFile);
@@ -2918,7 +2971,7 @@ static Image LoadKTX(const char *fileName)
int dataSize;
fread(&dataSize, sizeof(unsigned int), 1, ktxFile);
- image.data = (unsigned char*)malloc(dataSize*sizeof(unsigned char));
+ image.data = (unsigned char *)malloc(dataSize*sizeof(unsigned char));
fread(image.data, dataSize, 1, ktxFile);
@@ -2932,6 +2985,93 @@ static Image LoadKTX(const char *fileName)
return image;
}
+
+// Save image data as KTX file
+// NOTE: By default KTX 1.1 spec is used, 2.0 is still on draft (01Oct2018)
+static void SaveKTX(Image image, const char *fileName)
+{
+ // KTX file Header (64 bytes)
+ // v1.1 - https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
+ // v2.0 - http://github.khronos.org/KTX-Specification/ - still on draft, not ready for implementation
+
+ typedef struct {
+ char id[12]; // Identifier: "«KTX 11»\r\n\x1A\n" // KTX 2.0: "«KTX 22»\r\n\x1A\n"
+ unsigned int endianness; // Little endian: 0x01 0x02 0x03 0x04
+ unsigned int glType; // For compressed textures, glType must equal 0
+ unsigned int glTypeSize; // For compressed texture data, usually 1
+ unsigned int glFormat; // For compressed textures is 0
+ unsigned int glInternalFormat; // Compressed internal format
+ unsigned int glBaseInternalFormat; // Same as glFormat (RGB, RGBA, ALPHA...) // KTX 2.0: UInt32 vkFormat
+ unsigned int width; // Texture image width in pixels
+ unsigned int height; // Texture image height in pixels
+ unsigned int depth; // For 2D textures is 0
+ unsigned int elements; // Number of array elements, usually 0
+ unsigned int faces; // Cubemap faces, for no-cubemap = 1
+ unsigned int mipmapLevels; // Non-mipmapped textures = 1
+ unsigned int keyValueDataSize; // Used to encode any arbitrary data... // KTX 2.0: UInt32 levelOrder - ordering of the mipmap levels, usually 0
+ // KTX 2.0: UInt32 supercompressionScheme - 0 (None), 1 (Crunch CRN), 2 (Zlib DEFLATE)...
+ // KTX 2.0 defines additional header elements...
+ } KTXHeader;
+
+ // NOTE: Before start of every mipmap data block, we have: unsigned int dataSize
+
+ FILE *ktxFile = fopen(fileName, "wb");
+
+ if (ktxFile == NULL) TraceLog(LOG_WARNING, "[%s] KTX image file could not be created", fileName);
+ else
+ {
+ KTXHeader ktxHeader;
+
+ // KTX identifier (v2.2)
+ //unsigned char id[12] = { '«', 'K', 'T', 'X', ' ', '1', '1', '»', '\r', '\n', '\x1A', '\n' };
+ //unsigned char id[12] = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A };
+
+ // Get the image header
+ strcpy(ktxHeader.id, "«KTX 11»\r\n\x1A\n"); // KTX 1.1 signature
+ ktxHeader.endianness = 0;
+ ktxHeader.glType = 0; // Obtained from image.format
+ ktxHeader.glTypeSize = 1;
+ ktxHeader.glFormat = 0; // Obtained from image.format
+ ktxHeader.glInternalFormat = 0; // Obtained from image.format
+ ktxHeader.glBaseInternalFormat = 0;
+ ktxHeader.width = image.width;
+ ktxHeader.height = image.height;
+ ktxHeader.depth = 0;
+ ktxHeader.elements = 0;
+ ktxHeader.faces = 1;
+ ktxHeader.mipmapLevels = image.mipmaps; // If it was 0, it means mipmaps should be generated on loading (not for compressed formats)
+ ktxHeader.keyValueDataSize = 0; // No extra data after the header
+
+ rlGetGlTextureFormats(image.format, &ktxHeader.glInternalFormat, &ktxHeader.glFormat, &ktxHeader.glType); // rlgl module function
+ ktxHeader.glBaseInternalFormat = ktxHeader.glFormat; // KTX 1.1 only
+
+ // NOTE: We can save into a .ktx all PixelFormats supported by raylib, including compressed formats like DXT, ETC or ASTC
+
+ if (ktxHeader.glFormat == -1) TraceLog(LOG_WARNING, "Image format not supported for KTX export.");
+ else
+ {
+ fwrite(&ktxHeader, 1, sizeof(KTXHeader), ktxFile);
+
+ int width = image.width;
+ int height = image.height;
+ int dataOffset = 0;
+
+ // Save all mipmaps data
+ for (int i = 0; i < image.mipmaps; i++)
+ {
+ unsigned int dataSize = GetPixelDataSize(width, height, image.format);
+ fwrite(&dataSize, 1, sizeof(unsigned int), ktxFile);
+ fwrite((unsigned char *)image.data + dataOffset, 1, dataSize, ktxFile);
+
+ width /= 2;
+ height /= 2;
+ dataOffset += dataSize;
+ }
+ }
+
+ fclose(ktxFile); // Close file pointer
+ }
+}
#endif
#if defined(SUPPORT_FILEFORMAT_PVR)
@@ -3073,7 +3213,7 @@ static Image LoadPVR(const char *fileName)
}
int dataSize = image.width*image.height*bpp/8; // Total data size in bytes
- image.data = (unsigned char*)malloc(dataSize*sizeof(unsigned char));
+ image.data = (unsigned char *)malloc(dataSize*sizeof(unsigned char));
// Read data from file
fread(image.data, dataSize, 1, pvrFile);