Remove end-line spaces

This commit is contained in:
Ray 2018-11-06 15:10:50 +01:00
parent e340517a73
commit fc1c9505ba
8 changed files with 431 additions and 430 deletions

View file

@ -80,7 +80,7 @@
#endif #endif
#include "external/mini_al.h" // mini_al audio library #include "external/mini_al.h" // mini_al audio library
// NOTE: Cannot be implement here because it conflicts with // NOTE: Cannot be implement here because it conflicts with
// Win32 APIs: Rectangle, CloseWindow(), ShowCursor(), PlaySoundA() // Win32 APIs: Rectangle, CloseWindow(), ShowCursor(), PlaySoundA()
#include <stdlib.h> // Required for: malloc(), free() #include <stdlib.h> // Required for: malloc(), free()
@ -132,12 +132,12 @@
// Types and Structures Definition // Types and Structures Definition
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
typedef enum { typedef enum {
MUSIC_AUDIO_OGG = 0, MUSIC_AUDIO_OGG = 0,
MUSIC_AUDIO_FLAC, MUSIC_AUDIO_FLAC,
MUSIC_AUDIO_MP3, MUSIC_AUDIO_MP3,
MUSIC_MODULE_XM, MUSIC_MODULE_XM,
MUSIC_MODULE_MOD MUSIC_MODULE_MOD
} MusicContextType; } MusicContextType;
// Music type (file streaming from memory) // Music type (file streaming from memory)
@ -167,12 +167,12 @@ typedef struct MusicData {
} MusicData; } MusicData;
#if defined(AUDIO_STANDALONE) #if defined(AUDIO_STANDALONE)
typedef enum { typedef enum {
LOG_INFO = 0, LOG_INFO = 0,
LOG_ERROR, LOG_ERROR,
LOG_WARNING, LOG_WARNING,
LOG_DEBUG, LOG_DEBUG,
LOG_OTHER LOG_OTHER
} TraceLogType; } TraceLogType;
#endif #endif
@ -214,7 +214,7 @@ typedef enum { AUDIO_BUFFER_USAGE_STATIC = 0, AUDIO_BUFFER_USAGE_STREAM } AudioB
// Audio buffer structure // Audio buffer structure
// NOTE: Slightly different logic is used when feeding data to the playback device depending on whether or not data is streamed // NOTE: Slightly different logic is used when feeding data to the playback device depending on whether or not data is streamed
typedef struct AudioBuffer AudioBuffer; typedef struct AudioBuffer AudioBuffer;
struct AudioBuffer { struct AudioBuffer {
mal_dsp dsp; // Required for format conversion mal_dsp dsp; // Required for format conversion
float volume; float volume;
@ -239,7 +239,7 @@ static bool isAudioInitialized = MAL_FALSE;
static float masterVolume = 1.0f; static float masterVolume = 1.0f;
// Audio buffers are tracked in a linked list // Audio buffers are tracked in a linked list
static AudioBuffer *firstAudioBuffer = NULL; static AudioBuffer *firstAudioBuffer = NULL;
static AudioBuffer *lastAudioBuffer = NULL; static AudioBuffer *lastAudioBuffer = NULL;
// mini_al functions declaration // mini_al functions declaration
@ -268,7 +268,7 @@ static void OnLog(mal_context *pContext, mal_device *pDevice, const char *messag
{ {
(void)pContext; (void)pContext;
(void)pDevice; (void)pDevice;
TraceLog(LOG_ERROR, message); // All log messages from mini_al are errors TraceLog(LOG_ERROR, message); // All log messages from mini_al are errors
} }
@ -291,30 +291,30 @@ static mal_uint32 OnSendAudioDataToDevice(mal_device *pDevice, mal_uint32 frameC
if (!audioBuffer->playing || audioBuffer->paused) continue; if (!audioBuffer->playing || audioBuffer->paused) continue;
mal_uint32 framesRead = 0; mal_uint32 framesRead = 0;
for (;;) for (;;)
{ {
if (framesRead > frameCount) if (framesRead > frameCount)
{ {
TraceLog(LOG_DEBUG, "Mixed too many frames from audio buffer"); TraceLog(LOG_DEBUG, "Mixed too many frames from audio buffer");
break; break;
} }
if (framesRead == frameCount) break; if (framesRead == frameCount) break;
// Just read as much data as we can from the stream. // Just read as much data as we can from the stream.
mal_uint32 framesToRead = (frameCount - framesRead); mal_uint32 framesToRead = (frameCount - framesRead);
while (framesToRead > 0) while (framesToRead > 0)
{ {
float tempBuffer[1024]; // 512 frames for stereo. float tempBuffer[1024]; // 512 frames for stereo.
mal_uint32 framesToReadRightNow = framesToRead; mal_uint32 framesToReadRightNow = framesToRead;
if (framesToReadRightNow > sizeof(tempBuffer)/sizeof(tempBuffer[0])/DEVICE_CHANNELS) if (framesToReadRightNow > sizeof(tempBuffer)/sizeof(tempBuffer[0])/DEVICE_CHANNELS)
{ {
framesToReadRightNow = sizeof(tempBuffer)/sizeof(tempBuffer[0])/DEVICE_CHANNELS; framesToReadRightNow = sizeof(tempBuffer)/sizeof(tempBuffer[0])/DEVICE_CHANNELS;
} }
mal_uint32 framesJustRead = (mal_uint32)mal_dsp_read(&audioBuffer->dsp, framesToReadRightNow, tempBuffer, audioBuffer->dsp.pUserData); mal_uint32 framesJustRead = (mal_uint32)mal_dsp_read(&audioBuffer->dsp, framesToReadRightNow, tempBuffer, audioBuffer->dsp.pUserData);
if (framesJustRead > 0) if (framesJustRead > 0)
{ {
float *framesOut = (float *)pFramesOut + (framesRead*device.channels); float *framesOut = (float *)pFramesOut + (framesRead*device.channels);
float *framesIn = tempBuffer; float *framesIn = tempBuffer;
@ -325,16 +325,16 @@ static mal_uint32 OnSendAudioDataToDevice(mal_device *pDevice, mal_uint32 frameC
} }
// If we weren't able to read all the frames we requested, break. // If we weren't able to read all the frames we requested, break.
if (framesJustRead < framesToReadRightNow) if (framesJustRead < framesToReadRightNow)
{ {
if (!audioBuffer->looping) if (!audioBuffer->looping)
{ {
StopAudioBuffer(audioBuffer); StopAudioBuffer(audioBuffer);
break; break;
} }
else else
{ {
// Should never get here, but just for safety, // Should never get here, but just for safety,
// move the cursor position back to the start and continue the loop. // move the cursor position back to the start and continue the loop.
audioBuffer->frameCursorPos = 0; audioBuffer->frameCursorPos = 0;
continue; continue;
@ -342,13 +342,13 @@ static mal_uint32 OnSendAudioDataToDevice(mal_device *pDevice, mal_uint32 frameC
} }
} }
// If for some reason we weren't able to read every frame we'll need to break from the loop. // If for some reason we weren't able to read every frame we'll need to break from the loop.
// Not doing this could theoretically put us into an infinite loop. // Not doing this could theoretically put us into an infinite loop.
if (framesToRead > 0) break; if (framesToRead > 0) break;
} }
} }
} }
mal_mutex_unlock(&audioLock); mal_mutex_unlock(&audioLock);
return frameCount; // We always output the same number of frames that were originally requested. return frameCount; // We always output the same number of frames that were originally requested.
@ -361,8 +361,8 @@ static mal_uint32 OnAudioBufferDSPRead(mal_dsp *pDSP, mal_uint32 frameCount, voi
mal_uint32 subBufferSizeInFrames = audioBuffer->bufferSizeInFrames/2; mal_uint32 subBufferSizeInFrames = audioBuffer->bufferSizeInFrames/2;
mal_uint32 currentSubBufferIndex = audioBuffer->frameCursorPos/subBufferSizeInFrames; mal_uint32 currentSubBufferIndex = audioBuffer->frameCursorPos/subBufferSizeInFrames;
if (currentSubBufferIndex > 1) if (currentSubBufferIndex > 1)
{ {
TraceLog(LOG_DEBUG, "Frame cursor position moved too far forward in audio stream"); TraceLog(LOG_DEBUG, "Frame cursor position moved too far forward in audio stream");
return 0; return 0;
@ -381,11 +381,11 @@ static mal_uint32 OnAudioBufferDSPRead(mal_dsp *pDSP, mal_uint32 frameCount, voi
{ {
// We break from this loop differently depending on the buffer's usage. For static buffers, we simply fill as much data as we can. For // We break from this loop differently depending on the buffer's usage. For static buffers, we simply fill as much data as we can. For
// streaming buffers we only fill the halves of the buffer that are processed. Unprocessed halves must keep their audio data in-tact. // streaming buffers we only fill the halves of the buffer that are processed. Unprocessed halves must keep their audio data in-tact.
if (audioBuffer->usage == AUDIO_BUFFER_USAGE_STATIC) if (audioBuffer->usage == AUDIO_BUFFER_USAGE_STATIC)
{ {
if (framesRead >= frameCount) break; if (framesRead >= frameCount) break;
} }
else else
{ {
if (isSubBufferProcessed[currentSubBufferIndex]) break; if (isSubBufferProcessed[currentSubBufferIndex]) break;
} }
@ -394,11 +394,11 @@ static mal_uint32 OnAudioBufferDSPRead(mal_dsp *pDSP, mal_uint32 frameCount, voi
if (totalFramesRemaining == 0) break; if (totalFramesRemaining == 0) break;
mal_uint32 framesRemainingInOutputBuffer; mal_uint32 framesRemainingInOutputBuffer;
if (audioBuffer->usage == AUDIO_BUFFER_USAGE_STATIC) if (audioBuffer->usage == AUDIO_BUFFER_USAGE_STATIC)
{ {
framesRemainingInOutputBuffer = audioBuffer->bufferSizeInFrames - audioBuffer->frameCursorPos; framesRemainingInOutputBuffer = audioBuffer->bufferSizeInFrames - audioBuffer->frameCursorPos;
} }
else else
{ {
mal_uint32 firstFrameIndexOfThisSubBuffer = subBufferSizeInFrames * currentSubBufferIndex; mal_uint32 firstFrameIndexOfThisSubBuffer = subBufferSizeInFrames * currentSubBufferIndex;
framesRemainingInOutputBuffer = subBufferSizeInFrames - (audioBuffer->frameCursorPos - firstFrameIndexOfThisSubBuffer); framesRemainingInOutputBuffer = subBufferSizeInFrames - (audioBuffer->frameCursorPos - firstFrameIndexOfThisSubBuffer);
@ -412,7 +412,7 @@ static mal_uint32 OnAudioBufferDSPRead(mal_dsp *pDSP, mal_uint32 frameCount, voi
framesRead += framesToRead; framesRead += framesToRead;
// If we've read to the end of the buffer, mark it as processed. // If we've read to the end of the buffer, mark it as processed.
if (framesToRead == framesRemainingInOutputBuffer) if (framesToRead == framesRemainingInOutputBuffer)
{ {
audioBuffer->isSubBufferProcessed[currentSubBufferIndex] = true; audioBuffer->isSubBufferProcessed[currentSubBufferIndex] = true;
isSubBufferProcessed[currentSubBufferIndex] = true; isSubBufferProcessed[currentSubBufferIndex] = true;
@ -420,7 +420,7 @@ static mal_uint32 OnAudioBufferDSPRead(mal_dsp *pDSP, mal_uint32 frameCount, voi
currentSubBufferIndex = (currentSubBufferIndex + 1)%2; currentSubBufferIndex = (currentSubBufferIndex + 1)%2;
// We need to break from this loop if we're not looping. // We need to break from this loop if we're not looping.
if (!audioBuffer->looping) if (!audioBuffer->looping)
{ {
StopAudioBuffer(audioBuffer); StopAudioBuffer(audioBuffer);
break; break;
@ -430,7 +430,7 @@ static mal_uint32 OnAudioBufferDSPRead(mal_dsp *pDSP, mal_uint32 frameCount, voi
// Zero-fill excess. // Zero-fill excess.
mal_uint32 totalFramesRemaining = (frameCount - framesRead); mal_uint32 totalFramesRemaining = (frameCount - framesRead);
if (totalFramesRemaining > 0) if (totalFramesRemaining > 0)
{ {
memset((unsigned char *)pFramesOut + (framesRead*frameSizeInBytes), 0, totalFramesRemaining*frameSizeInBytes); memset((unsigned char *)pFramesOut + (framesRead*frameSizeInBytes), 0, totalFramesRemaining*frameSizeInBytes);
@ -447,9 +447,9 @@ static mal_uint32 OnAudioBufferDSPRead(mal_dsp *pDSP, mal_uint32 frameCount, voi
// NOTE: framesOut is both an input and an output. It will be initially filled with zeros outside of this function. // NOTE: framesOut is both an input and an output. It will be initially filled with zeros outside of this function.
static void MixAudioFrames(float *framesOut, const float *framesIn, mal_uint32 frameCount, float localVolume) static void MixAudioFrames(float *framesOut, const float *framesIn, mal_uint32 frameCount, float localVolume)
{ {
for (mal_uint32 iFrame = 0; iFrame < frameCount; ++iFrame) for (mal_uint32 iFrame = 0; iFrame < frameCount; ++iFrame)
{ {
for (mal_uint32 iChannel = 0; iChannel < device.channels; ++iChannel) for (mal_uint32 iChannel = 0; iChannel < device.channels; ++iChannel)
{ {
float *frameOut = framesOut + (iFrame*device.channels); float *frameOut = framesOut + (iFrame*device.channels);
const float *frameIn = framesIn + (iFrame*device.channels); const float *frameIn = framesIn + (iFrame*device.channels);
@ -519,7 +519,7 @@ void InitAudioDevice(void)
// Close the audio device for all contexts // Close the audio device for all contexts
void CloseAudioDevice(void) void CloseAudioDevice(void)
{ {
if (!isAudioInitialized) if (!isAudioInitialized)
{ {
TraceLog(LOG_WARNING, "Could not close audio device because it is not currently initialized"); TraceLog(LOG_WARNING, "Could not close audio device because it is not currently initialized");
return; return;
@ -543,7 +543,7 @@ void SetMasterVolume(float volume)
{ {
if (volume < 0.0f) volume = 0.0f; if (volume < 0.0f) volume = 0.0f;
else if (volume > 1.0f) volume = 1.0f; else if (volume > 1.0f) volume = 1.0f;
masterVolume = volume; masterVolume = volume;
} }
@ -574,7 +574,7 @@ AudioBuffer *CreateAudioBuffer(mal_format format, mal_uint32 channels, mal_uint3
dspConfig.pUserData = audioBuffer; dspConfig.pUserData = audioBuffer;
dspConfig.allowDynamicSampleRate = MAL_TRUE; // <-- Required for pitch shifting. dspConfig.allowDynamicSampleRate = MAL_TRUE; // <-- Required for pitch shifting.
mal_result resultMAL = mal_dsp_init(&dspConfig, &audioBuffer->dsp); mal_result resultMAL = mal_dsp_init(&dspConfig, &audioBuffer->dsp);
if (resultMAL != MAL_SUCCESS) if (resultMAL != MAL_SUCCESS)
{ {
TraceLog(LOG_ERROR, "CreateAudioBuffer() : Failed to create data conversion pipeline"); TraceLog(LOG_ERROR, "CreateAudioBuffer() : Failed to create data conversion pipeline");
free(audioBuffer); free(audioBuffer);
@ -716,10 +716,10 @@ void SetAudioBufferPitch(AudioBuffer *audioBuffer, float pitch)
void TrackAudioBuffer(AudioBuffer *audioBuffer) void TrackAudioBuffer(AudioBuffer *audioBuffer)
{ {
mal_mutex_lock(&audioLock); mal_mutex_lock(&audioLock);
{ {
if (firstAudioBuffer == NULL) firstAudioBuffer = audioBuffer; if (firstAudioBuffer == NULL) firstAudioBuffer = audioBuffer;
else else
{ {
lastAudioBuffer->next = audioBuffer; lastAudioBuffer->next = audioBuffer;
audioBuffer->prev = lastAudioBuffer; audioBuffer->prev = lastAudioBuffer;
@ -727,7 +727,7 @@ void TrackAudioBuffer(AudioBuffer *audioBuffer)
lastAudioBuffer = audioBuffer; lastAudioBuffer = audioBuffer;
} }
mal_mutex_unlock(&audioLock); mal_mutex_unlock(&audioLock);
} }
@ -735,7 +735,7 @@ void TrackAudioBuffer(AudioBuffer *audioBuffer)
void UntrackAudioBuffer(AudioBuffer *audioBuffer) void UntrackAudioBuffer(AudioBuffer *audioBuffer)
{ {
mal_mutex_lock(&audioLock); mal_mutex_lock(&audioLock);
{ {
if (audioBuffer->prev == NULL) firstAudioBuffer = audioBuffer->next; if (audioBuffer->prev == NULL) firstAudioBuffer = audioBuffer->next;
else audioBuffer->prev->next = audioBuffer->next; else audioBuffer->prev->next = audioBuffer->next;
@ -746,7 +746,7 @@ void UntrackAudioBuffer(AudioBuffer *audioBuffer)
audioBuffer->prev = NULL; audioBuffer->prev = NULL;
audioBuffer->next = NULL; audioBuffer->next = NULL;
} }
mal_mutex_unlock(&audioLock); mal_mutex_unlock(&audioLock);
} }
@ -816,7 +816,7 @@ Sound LoadSoundFromWave(Wave wave)
{ {
// 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 // 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: // the format used to open the playback device. We can do this two ways:
// //
// 1) Convert the whole sound in one go at load time (here). // 1) Convert the whole sound in one go at load time (here).
// 2) Convert the audio data in chunks at mixing time. // 2) Convert the audio data in chunks at mixing time.
// //
@ -861,7 +861,7 @@ void UnloadSound(Sound sound)
void UpdateSound(Sound sound, const void *data, int samplesCount) void UpdateSound(Sound sound, const void *data, int samplesCount)
{ {
AudioBuffer *audioBuffer = (AudioBuffer *)sound.audioBuffer; AudioBuffer *audioBuffer = (AudioBuffer *)sound.audioBuffer;
if (audioBuffer == NULL) if (audioBuffer == NULL)
{ {
TraceLog(LOG_ERROR, "UpdateSound() : Invalid sound - no audio buffer"); TraceLog(LOG_ERROR, "UpdateSound() : Invalid sound - no audio buffer");
@ -878,9 +878,9 @@ void UpdateSound(Sound sound, const void *data, int samplesCount)
void ExportWave(Wave wave, const char *fileName) void ExportWave(Wave wave, const char *fileName)
{ {
bool success = false; bool success = false;
if (IsFileExtension(fileName, ".wav")) success = SaveWAV(wave, fileName); if (IsFileExtension(fileName, ".wav")) success = SaveWAV(wave, fileName);
else if (IsFileExtension(fileName, ".raw")) else if (IsFileExtension(fileName, ".raw"))
{ {
// Export raw sample data (without header) // Export raw sample data (without header)
// NOTE: It's up to the user to track wave parameters // NOTE: It's up to the user to track wave parameters
@ -888,7 +888,7 @@ void ExportWave(Wave wave, const char *fileName)
success = fwrite(wave.data, wave.sampleCount*wave.channels*wave.sampleSize/8, 1, rawFile); success = fwrite(wave.data, wave.sampleCount*wave.channels*wave.sampleSize/8, 1, rawFile);
fclose(rawFile); fclose(rawFile);
} }
if (success) TraceLog(LOG_INFO, "Wave exported successfully: %s", fileName); if (success) TraceLog(LOG_INFO, "Wave exported successfully: %s", fileName);
else TraceLog(LOG_WARNING, "Wave could not be exported."); else TraceLog(LOG_WARNING, "Wave could not be exported.");
} }
@ -897,12 +897,12 @@ void ExportWave(Wave wave, const char *fileName)
void ExportWaveAsCode(Wave wave, const char *fileName) void ExportWaveAsCode(Wave wave, const char *fileName)
{ {
#define BYTES_TEXT_PER_LINE 20 #define BYTES_TEXT_PER_LINE 20
char varFileName[256] = { 0 }; char varFileName[256] = { 0 };
int dataSize = wave.sampleCount*wave.channels*wave.sampleSize/8; int dataSize = wave.sampleCount*wave.channels*wave.sampleSize/8;
FILE *txtFile = fopen(fileName, "wt"); FILE *txtFile = fopen(fileName, "wt");
fprintf(txtFile, "\n//////////////////////////////////////////////////////////////////////////////////\n"); fprintf(txtFile, "\n//////////////////////////////////////////////////////////////////////////////////\n");
fprintf(txtFile, "// //\n"); fprintf(txtFile, "// //\n");
fprintf(txtFile, "// WaveAsCode exporter v1.0 - Wave data exported as an array of bytes //\n"); fprintf(txtFile, "// WaveAsCode exporter v1.0 - Wave data exported as an array of bytes //\n");
@ -917,7 +917,7 @@ void ExportWaveAsCode(Wave wave, const char *fileName)
// Get file name from path and convert variable name to uppercase // Get file name from path and convert variable name to uppercase
strcpy(varFileName, GetFileNameWithoutExt(fileName)); strcpy(varFileName, GetFileNameWithoutExt(fileName));
for (int i = 0; varFileName[i] != '\0'; i++) if (varFileName[i] >= 'a' && varFileName[i] <= 'z') { varFileName[i] = varFileName[i] - 32; } for (int i = 0; varFileName[i] != '\0'; i++) if (varFileName[i] >= 'a' && varFileName[i] <= 'z') { varFileName[i] = varFileName[i] - 32; }
fprintf(txtFile, "// Wave data information\n"); fprintf(txtFile, "// Wave data information\n");
fprintf(txtFile, "#define %s_SAMPLE_COUNT %i\n", varFileName, wave.sampleCount); fprintf(txtFile, "#define %s_SAMPLE_COUNT %i\n", varFileName, wave.sampleCount);
fprintf(txtFile, "#define %s_SAMPLE_RATE %i\n", varFileName, wave.sampleRate); fprintf(txtFile, "#define %s_SAMPLE_RATE %i\n", varFileName, wave.sampleRate);
@ -983,7 +983,7 @@ void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels)
mal_uint32 frameCountIn = wave->sampleCount; // Is wave->sampleCount actually the frame count? That terminology needs to change, if so. mal_uint32 frameCountIn = wave->sampleCount; // Is wave->sampleCount actually the frame count? That terminology needs to change, if so.
mal_uint32 frameCount = (mal_uint32)mal_convert_frames(NULL, formatOut, channels, sampleRate, NULL, formatIn, wave->channels, wave->sampleRate, frameCountIn); mal_uint32 frameCount = (mal_uint32)mal_convert_frames(NULL, formatOut, channels, sampleRate, NULL, formatIn, wave->channels, wave->sampleRate, frameCountIn);
if (frameCount == 0) if (frameCount == 0)
{ {
TraceLog(LOG_ERROR, "WaveFormat() : Failed to get frame count for format conversion."); TraceLog(LOG_ERROR, "WaveFormat() : Failed to get frame count for format conversion.");
return; return;
@ -992,7 +992,7 @@ void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels)
void *data = malloc(frameCount*channels*(sampleSize/8)); void *data = malloc(frameCount*channels*(sampleSize/8));
frameCount = (mal_uint32)mal_convert_frames(data, formatOut, channels, sampleRate, wave->data, formatIn, wave->channels, wave->sampleRate, frameCountIn); frameCount = (mal_uint32)mal_convert_frames(data, formatOut, channels, sampleRate, wave->data, formatIn, wave->channels, wave->sampleRate, frameCountIn);
if (frameCount == 0) if (frameCount == 0)
{ {
TraceLog(LOG_ERROR, "WaveFormat() : Format conversion failed."); TraceLog(LOG_ERROR, "WaveFormat() : Format conversion failed.");
return; return;
@ -1130,16 +1130,16 @@ Music LoadMusicStream(const char *fileName)
TraceLog(LOG_INFO, "[%s] MP3 sample rate: %i", fileName, music->ctxMp3.sampleRate); 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 bits per sample: %i", fileName, 32);
TraceLog(LOG_INFO, "[%s] MP3 channels: %i", fileName, music->ctxMp3.channels); TraceLog(LOG_INFO, "[%s] MP3 channels: %i", fileName, music->ctxMp3.channels);
music->stream = InitAudioStream(music->ctxMp3.sampleRate, 32, music->ctxMp3.channels); 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 // 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... // in an MP3, frames size could be variable... we tried with a 60 seconds music... but crashes...
music->totalSamples = drmp3_get_pcm_frame_count(&music->ctxMp3)*music->ctxMp3.channels; music->totalSamples = drmp3_get_pcm_frame_count(&music->ctxMp3)*music->ctxMp3.channels;
music->samplesLeft = music->totalSamples; music->samplesLeft = music->totalSamples;
music->ctxType = MUSIC_AUDIO_MP3; music->ctxType = MUSIC_AUDIO_MP3;
music->loopCount = -1; // Infinite loop by default music->loopCount = -1; // Infinite loop by default
TraceLog(LOG_INFO, "[%s] MP3 total samples: %i", fileName, music->totalSamples); TraceLog(LOG_INFO, "[%s] MP3 total samples: %i", fileName, music->totalSamples);
} }
} }
@ -1186,7 +1186,7 @@ Music LoadMusicStream(const char *fileName)
} }
#endif #endif
else musicLoaded = false; else musicLoaded = false;
if (!musicLoaded) if (!musicLoaded)
{ {
if (music->ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close(music->ctxOgg); if (music->ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close(music->ctxOgg);
@ -1238,7 +1238,7 @@ void UnloadMusicStream(Music music)
void PlayMusicStream(Music music) void PlayMusicStream(Music music)
{ {
AudioBuffer *audioBuffer = (AudioBuffer *)music->stream.audioBuffer; AudioBuffer *audioBuffer = (AudioBuffer *)music->stream.audioBuffer;
if (audioBuffer == NULL) if (audioBuffer == NULL)
{ {
TraceLog(LOG_ERROR, "PlayMusicStream() : No audio buffer"); TraceLog(LOG_ERROR, "PlayMusicStream() : No audio buffer");
@ -1250,7 +1250,7 @@ void PlayMusicStream(Music music)
// // just make sure to play again on window restore // // just make sure to play again on window restore
// if (IsMusicPlaying(music)) PlayMusicStream(music); // if (IsMusicPlaying(music)) PlayMusicStream(music);
mal_uint32 frameCursorPos = audioBuffer->frameCursorPos; mal_uint32 frameCursorPos = audioBuffer->frameCursorPos;
PlayAudioStream(music->stream); // <-- This resets the cursor position. PlayAudioStream(music->stream); // <-- This resets the cursor position.
audioBuffer->frameCursorPos = frameCursorPos; audioBuffer->frameCursorPos = frameCursorPos;
@ -1273,7 +1273,7 @@ void ResumeMusicStream(Music music)
void StopMusicStream(Music music) void StopMusicStream(Music music)
{ {
StopAudioStream(music->stream); StopAudioStream(music->stream);
// Restart music context // Restart music context
switch (music->ctxType) switch (music->ctxType)
{ {
@ -1332,18 +1332,18 @@ void UpdateMusicStream(Music music)
} break; } break;
#endif #endif
#if defined(SUPPORT_FILEFORMAT_MP3) #if defined(SUPPORT_FILEFORMAT_MP3)
case MUSIC_AUDIO_MP3: case MUSIC_AUDIO_MP3:
{ {
// NOTE: samplesCount, actually refers to framesCount and returns the number of frames processed // NOTE: samplesCount, actually refers to framesCount and returns the number of frames processed
unsigned int numFramesMp3 = (unsigned int)drmp3_read_pcm_frames_f32(&music->ctxMp3, samplesCount/music->stream.channels, (float *)pcm); drmp3_read_pcm_frames_f32(&music->ctxMp3, samplesCount/music->stream.channels, (float *)pcm);
} break; } break;
#endif #endif
#if defined(SUPPORT_FILEFORMAT_XM) #if defined(SUPPORT_FILEFORMAT_XM)
case MUSIC_MODULE_XM: case MUSIC_MODULE_XM:
{ {
// NOTE: Internally this function considers 2 channels generation, so samplesCount/2 --> WEIRD // NOTE: Internally this function considers 2 channels generation, so samplesCount/2 --> WEIRD
jar_xm_generate_samples_16bit(music->ctxXm, (short *)pcm, samplesCount/2); jar_xm_generate_samples_16bit(music->ctxXm, (short *)pcm, samplesCount/2);
} break; } break;
#endif #endif
#if defined(SUPPORT_FILEFORMAT_MOD) #if defined(SUPPORT_FILEFORMAT_MOD)
@ -1369,7 +1369,7 @@ void UpdateMusicStream(Music music)
if (streamEnding) if (streamEnding)
{ {
StopMusicStream(music); // Stop music (and reset) StopMusicStream(music); // Stop music (and reset)
// Decrease loopCount to stop when required // Decrease loopCount to stop when required
if (music->loopCount > 0) if (music->loopCount > 0)
{ {
@ -1475,7 +1475,7 @@ AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, un
void CloseAudioStream(AudioStream stream) void CloseAudioStream(AudioStream stream)
{ {
DeleteAudioBuffer((AudioBuffer *)stream.audioBuffer); DeleteAudioBuffer((AudioBuffer *)stream.audioBuffer);
TraceLog(LOG_INFO, "[AUD ID %i] Unloaded audio stream data", stream.source); TraceLog(LOG_INFO, "[AUD ID %i] Unloaded audio stream data", stream.source);
} }
@ -1494,7 +1494,7 @@ void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount)
if (audioBuffer->isSubBufferProcessed[0] || audioBuffer->isSubBufferProcessed[1]) if (audioBuffer->isSubBufferProcessed[0] || audioBuffer->isSubBufferProcessed[1])
{ {
mal_uint32 subBufferToUpdate; mal_uint32 subBufferToUpdate;
if (audioBuffer->isSubBufferProcessed[0] && audioBuffer->isSubBufferProcessed[1]) 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. // Both buffers are available for updating. Update the first one and make sure the cursor is moved back to the front.
@ -1514,7 +1514,7 @@ void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount)
if (subBufferSizeInFrames >= (mal_uint32)samplesCount/stream.channels) if (subBufferSizeInFrames >= (mal_uint32)samplesCount/stream.channels)
{ {
mal_uint32 framesToWrite = subBufferSizeInFrames; mal_uint32 framesToWrite = subBufferSizeInFrames;
if (framesToWrite > ((mal_uint32)samplesCount/stream.channels)) framesToWrite = (mal_uint32)samplesCount/stream.channels; if (framesToWrite > ((mal_uint32)samplesCount/stream.channels)) framesToWrite = (mal_uint32)samplesCount/stream.channels;
mal_uint32 bytesToWrite = framesToWrite*stream.channels*(stream.sampleSize/8); mal_uint32 bytesToWrite = framesToWrite*stream.channels*(stream.sampleSize/8);
@ -1522,8 +1522,8 @@ void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount)
// Any leftover frames should be filled with zeros. // Any leftover frames should be filled with zeros.
mal_uint32 leftoverFrameCount = subBufferSizeInFrames - framesToWrite; mal_uint32 leftoverFrameCount = subBufferSizeInFrames - framesToWrite;
if (leftoverFrameCount > 0) if (leftoverFrameCount > 0)
{ {
memset(subBuffer + bytesToWrite, 0, leftoverFrameCount*stream.channels*(stream.sampleSize/8)); memset(subBuffer + bytesToWrite, 0, leftoverFrameCount*stream.channels*(stream.sampleSize/8));
} }
@ -1723,7 +1723,7 @@ static int SaveWAV(Wave wave, const char *fileName)
{ {
int success = 0; int success = 0;
int dataSize = wave.sampleCount*wave.channels*wave.sampleSize/8; int dataSize = wave.sampleCount*wave.channels*wave.sampleSize/8;
// Basic WAV headers structs // Basic WAV headers structs
typedef struct { typedef struct {
char chunkID[4]; char chunkID[4];
@ -1748,7 +1748,7 @@ static int SaveWAV(Wave wave, const char *fileName)
} WaveData; } WaveData;
FILE *wavFile = fopen(fileName, "wb"); FILE *wavFile = fopen(fileName, "wb");
if (wavFile == NULL) TraceLog(LOG_WARNING, "[%s] WAV audio file could not be created", fileName); if (wavFile == NULL) TraceLog(LOG_WARNING, "[%s] WAV audio file could not be created", fileName);
else else
{ {
@ -1784,7 +1784,7 @@ static int SaveWAV(Wave wave, const char *fileName)
waveData.subChunkID[2] = 't'; waveData.subChunkID[2] = 't';
waveData.subChunkID[3] = 'a'; waveData.subChunkID[3] = 'a';
waveData.subChunkSize = dataSize; waveData.subChunkSize = dataSize;
success = fwrite(&riffHeader, sizeof(RiffHeader), 1, wavFile); success = fwrite(&riffHeader, sizeof(RiffHeader), 1, wavFile);
success = fwrite(&waveFormat, sizeof(WaveFormat), 1, wavFile); success = fwrite(&waveFormat, sizeof(WaveFormat), 1, wavFile);
success = fwrite(&waveData, sizeof(WaveData), 1, wavFile); success = fwrite(&waveData, sizeof(WaveData), 1, wavFile);
@ -1793,7 +1793,7 @@ static int SaveWAV(Wave wave, const char *fileName)
fclose(wavFile); fclose(wavFile);
} }
// If all data has been written correctly to file, success = 1 // If all data has been written correctly to file, success = 1
return success; return success;
} }
@ -1812,7 +1812,7 @@ static Wave LoadOGG(const char *fileName)
else else
{ {
stb_vorbis_info info = stb_vorbis_get_info(oggFile); stb_vorbis_info info = stb_vorbis_get_info(oggFile);
wave.sampleRate = info.sample_rate; wave.sampleRate = info.sample_rate;
wave.sampleSize = 16; // 16 bit per sample (short) wave.sampleSize = 16; // 16 bit per sample (short)
wave.channels = info.channels; wave.channels = info.channels;
@ -1872,7 +1872,7 @@ static Wave LoadMP3(const char *fileName)
uint64_t totalFrameCount = 0; uint64_t totalFrameCount = 0;
drmp3_config config = { 0 }; drmp3_config config = { 0 };
wave.data = drmp3_open_file_and_read_f32(fileName, &config, &totalFrameCount); wave.data = drmp3_open_file_and_read_f32(fileName, &config, &totalFrameCount);
wave.channels = config.outputChannels; wave.channels = config.outputChannels;
wave.sampleRate = config.outputSampleRate; wave.sampleRate = config.outputSampleRate;
wave.sampleCount = (int)totalFrameCount*wave.channels; wave.sampleCount = (int)totalFrameCount*wave.channels;
@ -1895,7 +1895,7 @@ bool IsFileExtension(const char *fileName, const char *ext)
{ {
bool result = false; bool result = false;
const char *fileExt; const char *fileExt;
if ((fileExt = strrchr(fileName, '.')) != NULL) if ((fileExt = strrchr(fileName, '.')) != NULL)
{ {
if (strcmp(fileExt, ext) == 0) result = true; if (strcmp(fileExt, ext) == 0) result = true;

View file

@ -45,7 +45,7 @@
// Use busy wait loop for timing sync, if not defined, a high-resolution timer is setup and used // Use busy wait loop for timing sync, if not defined, a high-resolution timer is setup and used
#define SUPPORT_BUSY_WAIT_LOOP 1 #define SUPPORT_BUSY_WAIT_LOOP 1
// Wait for events passively (sleeping while no events) instead of polling them actively every frame // Wait for events passively (sleeping while no events) instead of polling them actively every frame
//SUPPORT_EVENTS_WAITING 1 //#define SUPPORT_EVENTS_WAITING 1
// Allow automatic screen capture of current screen pressing F12, defined in KeyCallback() // Allow automatic screen capture of current screen pressing F12, defined in KeyCallback()
#define SUPPORT_SCREEN_CAPTURE 1 #define SUPPORT_SCREEN_CAPTURE 1
// Allow automatic gif recording of current screen pressing CTRL+F12, defined in KeyCallback() // Allow automatic gif recording of current screen pressing CTRL+F12, defined in KeyCallback()

View file

@ -2,12 +2,12 @@
* *
* raylib.core - Basic functions to manage windows, OpenGL context and input on multiple platforms * raylib.core - Basic functions to manage windows, OpenGL context and input on multiple platforms
* *
* PLATFORMS SUPPORTED: * PLATFORMS SUPPORTED:
* - PLATFORM_DESKTOP: Windows (Win32, Win64) * - PLATFORM_DESKTOP: Windows (Win32, Win64)
* - PLATFORM_DESKTOP: Linux (X11 desktop mode) * - PLATFORM_DESKTOP: Linux (X11 desktop mode)
* - PLATFORM_DESKTOP: FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) * - PLATFORM_DESKTOP: FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop)
* - PLATFORM_DESKTOP: OSX/macOS * - PLATFORM_DESKTOP: OSX/macOS
* - PLATFORM_ANDROID: Android 4.0 (ARM, ARM64) * - PLATFORM_ANDROID: Android 4.0 (ARM, ARM64)
* - PLATFORM_RPI: Raspberry Pi 0,1,2,3 (Raspbian) * - PLATFORM_RPI: Raspberry Pi 0,1,2,3 (Raspbian)
* - PLATFORM_WEB: HTML5 with asm.js (Chrome, Firefox) * - PLATFORM_WEB: HTML5 with asm.js (Chrome, Firefox)
* - PLATFORM_UWP: Windows 10 App, Windows Phone, Xbox One * - PLATFORM_UWP: Windows 10 App, Windows Phone, Xbox One
@ -23,7 +23,7 @@
* NOTE: OpenGL ES 2.0 is required and graphic device is managed by EGL * NOTE: OpenGL ES 2.0 is required and graphic device is managed by EGL
* *
* #define PLATFORM_RPI * #define PLATFORM_RPI
* Windowing and input system configured for Raspberry Pi i native mode (no X.org required, tested on Raspbian), * Windowing and input system configured for Raspberry Pi i native mode (no X.org required, tested on Raspbian),
* graphic device is managed by EGL and inputs are processed is raw mode, reading from /dev/input/ * graphic device is managed by EGL and inputs are processed is raw mode, reading from /dev/input/
* *
* #define PLATFORM_WEB * #define PLATFORM_WEB
@ -155,7 +155,7 @@
#if defined(_WIN32) #if defined(_WIN32)
#define GLFW_EXPOSE_NATIVE_WIN32 #define GLFW_EXPOSE_NATIVE_WIN32
#include <GLFW/glfw3native.h> // WARNING: It requires customization to avoid windows.h inclusion! #include <GLFW/glfw3native.h> // WARNING: It requires customization to avoid windows.h inclusion!
#if !defined(SUPPORT_BUSY_WAIT_LOOP) #if !defined(SUPPORT_BUSY_WAIT_LOOP)
// NOTE: Those functions require linking with winmm library // NOTE: Those functions require linking with winmm library
unsigned int __stdcall timeBeginPeriod(unsigned int uPeriod); unsigned int __stdcall timeBeginPeriod(unsigned int uPeriod);
@ -164,7 +164,7 @@
#elif defined(__linux__) #elif defined(__linux__)
#include <sys/time.h> // Required for: timespec, nanosleep(), select() - POSIX #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_X11 // WARNING: Exposing Xlib.h > X.h results in dup symbols for Font type
//#define GLFW_EXPOSE_NATIVE_WAYLAND //#define GLFW_EXPOSE_NATIVE_WAYLAND
//#define GLFW_EXPOSE_NATIVE_MIR //#define GLFW_EXPOSE_NATIVE_MIR
@ -172,7 +172,7 @@
#elif defined(__APPLE__) #elif defined(__APPLE__)
#include <unistd.h> // Required for: usleep() #include <unistd.h> // Required for: usleep()
#include <objc/message.h> // Required for: objc_msgsend(), sel_registerName() #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_COCOA // WARNING: Fails due to type redefinition
#define GLFW_EXPOSE_NATIVE_NSGL #define GLFW_EXPOSE_NATIVE_NSGL
#include <GLFW/glfw3native.h> // Required for: glfwGetCocoaWindow(), glfwGetNSGLContext() #include <GLFW/glfw3native.h> // Required for: glfwGetCocoaWindow(), glfwGetNSGLContext()
@ -225,7 +225,7 @@
#define GLFW_INCLUDE_ES2 // GLFW3: Enable OpenGL ES 2.0 (translated to WebGL) #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 <GLFW/glfw3.h> // GLFW3 library: Windows, OpenGL context and Input management
#include <sys/time.h> // Required for: timespec, nanosleep(), select() - POSIX #include <sys/time.h> // Required for: timespec, nanosleep(), select() - POSIX
#include <emscripten/emscripten.h> // Emscripten library - LLVM to JavaScript compiler #include <emscripten/emscripten.h> // Emscripten library - LLVM to JavaScript compiler
#include <emscripten/html5.h> // Emscripten HTML5 library #include <emscripten/html5.h> // Emscripten HTML5 library
#endif #endif
@ -492,7 +492,7 @@ static void *GamepadThread(void *arg); // Mouse reading thread
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
#if defined(PLATFORM_ANDROID) #if defined(PLATFORM_ANDROID)
// To allow easier porting to android, we allow the user to define a // To allow easier porting to android, we allow the user to define a
// main function which we call from android_main, defined by ourselves // main function which we call from android_main, defined by ourselves
extern int main(int argc, char *argv[]); extern int main(int argc, char *argv[]);
@ -596,10 +596,10 @@ void InitWindow(int width, int height, const char *title)
#if defined(PLATFORM_WEB) #if defined(PLATFORM_WEB)
emscripten_set_fullscreenchange_callback(0, 0, 1, EmscriptenFullscreenChangeCallback); emscripten_set_fullscreenchange_callback(0, 0, 1, EmscriptenFullscreenChangeCallback);
// Support keyboard events // Support keyboard events
emscripten_set_keypress_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback); emscripten_set_keypress_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback);
// Support mouse events // Support mouse events
emscripten_set_click_callback("#canvas", NULL, 1, EmscriptenMouseCallback); emscripten_set_click_callback("#canvas", NULL, 1, EmscriptenMouseCallback);
@ -638,7 +638,7 @@ void CloseWindow(void)
gifRecording = false; gifRecording = false;
} }
#endif #endif
#if defined(SUPPORT_DEFAULT_FONT) #if defined(SUPPORT_DEFAULT_FONT)
UnloadDefaultFont(); UnloadDefaultFont();
#endif #endif
@ -715,7 +715,7 @@ bool WindowShouldClose(void)
emscripten_sleep(16); emscripten_sleep(16);
return false; return false;
#endif #endif
#if defined(PLATFORM_DESKTOP) #if defined(PLATFORM_DESKTOP)
if (windowReady) if (windowReady)
{ {
@ -766,7 +766,7 @@ void SetWindowIcon(Image image)
ImageFormat(&image, UNCOMPRESSED_R8G8B8A8); ImageFormat(&image, UNCOMPRESSED_R8G8B8A8);
GLFWimage icon[1]; GLFWimage icon[1];
icon[0].width = image.width; icon[0].width = image.width;
icon[0].height = image.height; icon[0].height = image.height;
icon[0].pixels = (unsigned char *)image.data; icon[0].pixels = (unsigned char *)image.data;
@ -800,8 +800,8 @@ void SetWindowMonitor(int monitor)
#if defined(PLATFORM_DESKTOP) #if defined(PLATFORM_DESKTOP)
int monitorCount; int monitorCount;
GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
if ((monitor >= 0) && (monitor < monitorCount)) if ((monitor >= 0) && (monitor < monitorCount))
{ {
//glfwSetWindowMonitor(window, monitors[monitor], 0, 0, screenWidth, screenHeight, GLFW_DONT_CARE); //glfwSetWindowMonitor(window, monitors[monitor], 0, 0, screenWidth, screenHeight, GLFW_DONT_CARE);
TraceLog(LOG_INFO, "Selected fullscreen monitor: [%i] %s", monitor, glfwGetMonitorName(monitors[monitor])); TraceLog(LOG_INFO, "Selected fullscreen monitor: [%i] %s", monitor, glfwGetMonitorName(monitors[monitor]));
@ -873,12 +873,12 @@ int GetMonitorCount(void)
// Get primary monitor width // Get primary monitor width
int GetMonitorWidth(int monitor) int GetMonitorWidth(int monitor)
{ {
#if defined(PLATFORM_DESKTOP) #if defined(PLATFORM_DESKTOP)
int monitorCount; int monitorCount;
GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
if ((monitor >= 0) && (monitor < monitorCount)) if ((monitor >= 0) && (monitor < monitorCount))
{ {
const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]);
return mode->width; return mode->width;
@ -890,12 +890,12 @@ int GetMonitorWidth(int monitor)
// Get primary monitor width // Get primary monitor width
int GetMonitorHeight(int monitor) int GetMonitorHeight(int monitor)
{ {
#if defined(PLATFORM_DESKTOP) #if defined(PLATFORM_DESKTOP)
int monitorCount; int monitorCount;
GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
if ((monitor >= 0) && (monitor < monitorCount)) if ((monitor >= 0) && (monitor < monitorCount))
{ {
const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]);
return mode->height; return mode->height;
@ -911,8 +911,8 @@ int GetMonitorPhysicalWidth(int monitor)
#if defined(PLATFORM_DESKTOP) #if defined(PLATFORM_DESKTOP)
int monitorCount; int monitorCount;
GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
if ((monitor >= 0) && (monitor < monitorCount)) if ((monitor >= 0) && (monitor < monitorCount))
{ {
int physicalWidth; int physicalWidth;
glfwGetMonitorPhysicalSize(monitors[monitor], &physicalWidth, NULL); glfwGetMonitorPhysicalSize(monitors[monitor], &physicalWidth, NULL);
@ -929,8 +929,8 @@ int GetMonitorPhysicalHeight(int monitor)
#if defined(PLATFORM_DESKTOP) #if defined(PLATFORM_DESKTOP)
int monitorCount; int monitorCount;
GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
if ((monitor >= 0) && (monitor < monitorCount)) if ((monitor >= 0) && (monitor < monitorCount))
{ {
int physicalHeight; int physicalHeight;
glfwGetMonitorPhysicalSize(monitors[monitor], NULL, &physicalHeight); glfwGetMonitorPhysicalSize(monitors[monitor], NULL, &physicalHeight);
@ -944,11 +944,11 @@ int GetMonitorPhysicalHeight(int monitor)
// Get the human-readable, UTF-8 encoded name of the primary monitor // Get the human-readable, UTF-8 encoded name of the primary monitor
const char *GetMonitorName(int monitor) const char *GetMonitorName(int monitor)
{ {
#if defined(PLATFORM_DESKTOP) #if defined(PLATFORM_DESKTOP)
int monitorCount; int monitorCount;
GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
if ((monitor >= 0) && (monitor < monitorCount)) if ((monitor >= 0) && (monitor < monitorCount))
{ {
return glfwGetMonitorName(monitors[monitor]); return glfwGetMonitorName(monitors[monitor]);
} }
@ -1038,7 +1038,7 @@ void EndDrawing(void)
if (gifRecording) if (gifRecording)
{ {
gifFramesCounter++; gifFramesCounter++;
// NOTE: We record one gif frame every 10 game frames // NOTE: We record one gif frame every 10 game frames
if ((gifFramesCounter%GIF_RECORD_FRAMERATE) == 0) if ((gifFramesCounter%GIF_RECORD_FRAMERATE) == 0)
{ {
@ -1046,20 +1046,20 @@ void EndDrawing(void)
// NOTE: This process is very slow... :( // NOTE: This process is very slow... :(
unsigned char *screenData = rlReadScreenPixels(screenWidth, screenHeight); unsigned char *screenData = rlReadScreenPixels(screenWidth, screenHeight);
GifWriteFrame(screenData, screenWidth, screenHeight, 10, 8, false); GifWriteFrame(screenData, screenWidth, screenHeight, 10, 8, false);
free(screenData); // Free image data free(screenData); // Free image data
} }
if (((gifFramesCounter/15)%2) == 1) if (((gifFramesCounter/15)%2) == 1)
{ {
DrawCircle(30, screenHeight - 20, 10, RED); DrawCircle(30, screenHeight - 20, 10, RED);
DrawText("RECORDING", 50, screenHeight - 25, 10, MAROON); DrawText("RECORDING", 50, screenHeight - 25, 10, MAROON);
} }
rlglDraw(); // Draw RECORDING message rlglDraw(); // Draw RECORDING message
} }
#endif #endif
SwapBuffers(); // Copy back buffer to front buffer SwapBuffers(); // Copy back buffer to front buffer
PollInputEvents(); // Poll user events PollInputEvents(); // Poll user events
@ -1067,7 +1067,7 @@ void EndDrawing(void)
currentTime = GetTime(); currentTime = GetTime();
drawTime = currentTime - previousTime; drawTime = currentTime - previousTime;
previousTime = currentTime; previousTime = currentTime;
frameTime = updateTime + drawTime; frameTime = updateTime + drawTime;
// Wait for some milliseconds... // Wait for some milliseconds...
@ -1113,14 +1113,14 @@ void EndMode2D(void)
void BeginMode3D(Camera3D camera) void BeginMode3D(Camera3D camera)
{ {
rlglDraw(); // Draw Buffers (Only OpenGL 3+ and ES2) rlglDraw(); // Draw Buffers (Only OpenGL 3+ and ES2)
rlMatrixMode(RL_PROJECTION); // Switch to projection matrix rlMatrixMode(RL_PROJECTION); // Switch to projection matrix
rlPushMatrix(); // Save previous matrix, which contains the settings for the 2d ortho projection rlPushMatrix(); // Save previous matrix, which contains the settings for the 2d ortho projection
rlLoadIdentity(); // Reset current matrix (PROJECTION) rlLoadIdentity(); // Reset current matrix (PROJECTION)
float aspect = (float)screenWidth/(float)screenHeight; float aspect = (float)screenWidth/(float)screenHeight;
if (camera.type == CAMERA_PERSPECTIVE) if (camera.type == CAMERA_PERSPECTIVE)
{ {
// Setup perspective projection // Setup perspective projection
double top = 0.01*tan(camera.fovy*0.5*DEG2RAD); double top = 0.01*tan(camera.fovy*0.5*DEG2RAD);
@ -1169,7 +1169,7 @@ void BeginTextureMode(RenderTexture2D target)
rlglDraw(); // Draw Buffers (Only OpenGL 3+ and ES2) rlglDraw(); // Draw Buffers (Only OpenGL 3+ and ES2)
rlEnableRenderTexture(target.id); // Enable render target rlEnableRenderTexture(target.id); // Enable render target
rlClearScreenBuffers(); // Clear render texture buffers rlClearScreenBuffers(); // Clear render texture buffers
// Set viewport to framebuffer size // Set viewport to framebuffer size
@ -1230,7 +1230,7 @@ Ray GetMouseRay(Vector2 mousePosition, Camera camera)
Matrix matProj = MatrixIdentity(); Matrix matProj = MatrixIdentity();
if (camera.type == CAMERA_PERSPECTIVE) if (camera.type == CAMERA_PERSPECTIVE)
{ {
// Calculate projection matrix from perspective // Calculate projection matrix from perspective
matProj = MatrixPerspective(camera.fovy*DEG2RAD, ((double)GetScreenWidth()/(double)GetScreenHeight()), 0.01, 1000.0); matProj = MatrixPerspective(camera.fovy*DEG2RAD, ((double)GetScreenWidth()/(double)GetScreenHeight()), 0.01, 1000.0);
@ -1240,7 +1240,7 @@ Ray GetMouseRay(Vector2 mousePosition, Camera camera)
float aspect = (float)screenWidth/(float)screenHeight; float aspect = (float)screenWidth/(float)screenHeight;
double top = camera.fovy/2.0; double top = camera.fovy/2.0;
double right = top*aspect; double right = top*aspect;
// Calculate projection matrix from orthographic // Calculate projection matrix from orthographic
matProj = MatrixOrtho(-right, right, -top, top, 0.01, 1000.0); matProj = MatrixOrtho(-right, right, -top, top, 0.01, 1000.0);
} }
@ -1250,7 +1250,7 @@ Ray GetMouseRay(Vector2 mousePosition, Camera camera)
Vector3 farPoint = rlUnproject((Vector3){ deviceCoords.x, deviceCoords.y, 1.0f }, matProj, matView); Vector3 farPoint = rlUnproject((Vector3){ deviceCoords.x, deviceCoords.y, 1.0f }, matProj, matView);
// Unproject the mouse cursor in the near plane. // Unproject the mouse cursor in the near plane.
// We need this as the source position because orthographic projects, compared to perspect doesn't have a // We need this as the source position because orthographic projects, compared to perspect doesn't have a
// convergence point, meaning that the "eye" of the camera is more like a plane than a point. // convergence point, meaning that the "eye" of the camera is more like a plane than a point.
Vector3 cameraPlanePointerPos = rlUnproject((Vector3){ deviceCoords.x, deviceCoords.y, -1.0f }, matProj, matView); Vector3 cameraPlanePointerPos = rlUnproject((Vector3){ deviceCoords.x, deviceCoords.y, -1.0f }, matProj, matView);
@ -1282,7 +1282,7 @@ Vector2 GetWorldToScreen(Vector3 position, Camera camera)
float aspect = (float)screenWidth/(float)screenHeight; float aspect = (float)screenWidth/(float)screenHeight;
double top = camera.fovy/2.0; double top = camera.fovy/2.0;
double right = top*aspect; double right = top*aspect;
// Calculate projection matrix from orthographic // Calculate projection matrix from orthographic
matProj = MatrixOrtho(-right, right, -top, top, 0.01, 1000.0); matProj = MatrixOrtho(-right, right, -top, top, 0.01, 1000.0);
} }
@ -1369,7 +1369,7 @@ Vector4 ColorNormalize(Color color)
result.y = (float)color.g/255.0f; result.y = (float)color.g/255.0f;
result.z = (float)color.b/255.0f; result.z = (float)color.b/255.0f;
result.w = (float)color.a/255.0f; result.w = (float)color.a/255.0f;
return result; return result;
} }
@ -1389,27 +1389,27 @@ Vector3 ColorToHSV(Color color)
hsv.z = max; // Value hsv.z = max; // Value
delta = max - min; delta = max - min;
if (delta < 0.00001f) if (delta < 0.00001f)
{ {
hsv.y = 0.0f; hsv.y = 0.0f;
hsv.x = 0.0f; // Undefined, maybe NAN? hsv.x = 0.0f; // Undefined, maybe NAN?
return hsv; return hsv;
} }
if (max > 0.0f) if (max > 0.0f)
{ {
// NOTE: If max is 0, this divide would cause a crash // NOTE: If max is 0, this divide would cause a crash
hsv.y = (delta/max); // Saturation hsv.y = (delta/max); // Saturation
} }
else else
{ {
// NOTE: If max is 0, then r = g = b = 0, s = 0, h is undefined // NOTE: If max is 0, then r = g = b = 0, s = 0, h is undefined
hsv.y = 0.0f; hsv.y = 0.0f;
hsv.x = NAN; // Undefined hsv.x = NAN; // Undefined
return hsv; return hsv;
} }
// NOTE: Comparing float values could not work properly // NOTE: Comparing float values could not work properly
if (rgb.x >= max) hsv.x = (rgb.y - rgb.z)/delta; // Between yellow & magenta if (rgb.x >= max) hsv.x = (rgb.y - rgb.z)/delta; // Between yellow & magenta
else else
@ -1417,7 +1417,7 @@ Vector3 ColorToHSV(Color color)
if (rgb.y >= max) hsv.x = 2.0f + (rgb.z - rgb.x)/delta; // Between cyan & yellow if (rgb.y >= max) hsv.x = 2.0f + (rgb.z - rgb.x)/delta; // Between cyan & yellow
else hsv.x = 4.0f + (rgb.x - rgb.y)/delta; // Between magenta & cyan else hsv.x = 4.0f + (rgb.x - rgb.y)/delta; // Between magenta & cyan
} }
hsv.x *= 60.0f; // Convert to degrees hsv.x *= 60.0f; // Convert to degrees
if (hsv.x < 0.0f) hsv.x += 360.0f; if (hsv.x < 0.0f) hsv.x += 360.0f;
@ -1519,7 +1519,7 @@ bool IsFileExtension(const char *fileName, const char *ext)
#if defined(_WIN32) #if defined(_WIN32)
result = true; result = true;
int extLen = strlen(ext); int extLen = strlen(ext);
if (strlen(fileExt) == extLen) if (strlen(fileExt) == extLen)
{ {
for (int i = 0; i < extLen; i++) for (int i = 0; i < extLen; i++)
@ -1544,9 +1544,9 @@ bool IsFileExtension(const char *fileName, const char *ext)
const char *GetExtension(const char *fileName) const char *GetExtension(const char *fileName)
{ {
const char *dot = strrchr(fileName, '.'); const char *dot = strrchr(fileName, '.');
if (!dot || dot == fileName) return NULL; if (!dot || dot == fileName) return NULL;
return (dot + 1); return (dot + 1);
} }
@ -1572,19 +1572,19 @@ const char *GetFileName(const char *filePath)
const char *GetFileNameWithoutExt(const char *filePath) const char *GetFileNameWithoutExt(const char *filePath)
{ {
char *result, *lastDot, *lastSep; char *result, *lastDot, *lastSep;
char nameDot = '.'; // Default filename to extension separator character char nameDot = '.'; // Default filename to extension separator character
char pathSep = '/'; // Default filepath separator character char pathSep = '/'; // Default filepath separator character
// Error checks and allocate string // Error checks and allocate string
if (filePath == NULL) return NULL; if (filePath == NULL) return NULL;
// Try to allocate new string, same size as original // Try to allocate new string, same size as original
// NOTE: By default strlen() does not count the '\0' character // NOTE: By default strlen() does not count the '\0' character
if ((result = malloc(strlen(filePath) + 1)) == NULL) return NULL; if ((result = malloc(strlen(filePath) + 1)) == NULL) return NULL;
strcpy(result, filePath); // Make a copy of the string strcpy(result, filePath); // Make a copy of the string
// NOTE: strrchr() returns a pointer to the last occurrence of character // NOTE: strrchr() returns a pointer to the last occurrence of character
lastDot = strrchr(result, nameDot); lastDot = strrchr(result, nameDot);
lastSep = (pathSep == 0) ? NULL : strrchr(result, pathSep); lastSep = (pathSep == 0) ? NULL : strrchr(result, pathSep);
@ -1593,11 +1593,11 @@ const char *GetFileNameWithoutExt(const char *filePath)
{ {
if (lastSep != NULL) // ...and it's before the extenstion separator... if (lastSep != NULL) // ...and it's before the extenstion separator...
{ {
if (lastSep < lastDot) if (lastSep < lastDot)
{ {
*lastDot = '\0'; // ...then remove it *lastDot = '\0'; // ...then remove it
} }
} }
else *lastDot = '\0'; // Has extension separator with no path separator else *lastDot = '\0'; // Has extension separator with no path separator
} }
@ -1626,9 +1626,9 @@ const char *GetWorkingDirectory(void)
{ {
static char currentDir[MAX_FILEPATH_LENGTH]; static char currentDir[MAX_FILEPATH_LENGTH];
memset(currentDir, 0, MAX_FILEPATH_LENGTH); memset(currentDir, 0, MAX_FILEPATH_LENGTH);
GETCWD(currentDir, MAX_FILEPATH_LENGTH - 1); GETCWD(currentDir, MAX_FILEPATH_LENGTH - 1);
return currentDir; return currentDir;
} }
@ -1637,36 +1637,36 @@ const char *GetWorkingDirectory(void)
char **GetDirectoryFiles(const char *dirPath, int *fileCount) char **GetDirectoryFiles(const char *dirPath, int *fileCount)
{ {
#define MAX_DIRECTORY_FILES 512 #define MAX_DIRECTORY_FILES 512
ClearDirectoryFiles(); ClearDirectoryFiles();
// Memory allocation for MAX_DIRECTORY_FILES // Memory allocation for MAX_DIRECTORY_FILES
dirFilesPath = (char **)malloc(sizeof(char *)*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); for (int i = 0; i < MAX_DIRECTORY_FILES; i++) dirFilesPath[i] = (char *)malloc(sizeof(char)*MAX_FILEPATH_LENGTH);
int counter = 0; int counter = 0;
struct dirent *ent; struct dirent *ent;
DIR *dir = opendir(dirPath); DIR *dir = opendir(dirPath);
if (dir != NULL) // It's a directory if (dir != NULL) // It's a directory
{ {
// TODO: Reading could be done in two passes, // TODO: Reading could be done in two passes,
// first one to count files and second one to read names // first one to count files and second one to read names
// That way we can allocate required memory, instead of a limited pool // That way we can allocate required memory, instead of a limited pool
while ((ent = readdir(dir)) != NULL) while ((ent = readdir(dir)) != NULL)
{ {
strcpy(dirFilesPath[counter], ent->d_name); strcpy(dirFilesPath[counter], ent->d_name);
counter++; counter++;
} }
closedir(dir); closedir(dir);
} }
else TraceLog(LOG_WARNING, "Can not open directory...\n"); // Maybe it's a file... else TraceLog(LOG_WARNING, "Can not open directory...\n"); // Maybe it's a file...
dirFilesCount = counter; dirFilesCount = counter;
*fileCount = dirFilesCount; *fileCount = dirFilesCount;
return dirFilesPath; return dirFilesPath;
} }
@ -1729,14 +1729,14 @@ void ClearDroppedFiles(void)
RLAPI long GetFileModTime(const char *fileName) RLAPI long GetFileModTime(const char *fileName)
{ {
struct stat result = { 0 }; struct stat result = { 0 };
if (stat(fileName, &result) == 0) if (stat(fileName, &result) == 0)
{ {
time_t mod = result.st_mtime; time_t mod = result.st_mtime;
return (long)mod; return (long)mod;
} }
return 0; return 0;
} }
@ -2011,7 +2011,7 @@ bool IsMouseButtonPressed(int button)
#else #else
if ((currentMouseState[button] != previousMouseState[button]) && (currentMouseState[button] == 1)) pressed = true; if ((currentMouseState[button] != previousMouseState[button]) && (currentMouseState[button] == 1)) pressed = true;
#endif #endif
return pressed; return pressed;
} }
@ -2259,7 +2259,7 @@ static bool InitGraphicsDevice(int width, int height)
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // Choose OpenGL major version (just hint) glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // Choose OpenGL major version (just hint)
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint) glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint)
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Profiles Hint: Only 3.3 and above! glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Profiles Hint: Only 3.3 and above!
// Other values: GLFW_OPENGL_ANY_PROFILE, GLFW_OPENGL_COMPAT_PROFILE // Values: GLFW_OPENGL_CORE_PROFILE, GLFW_OPENGL_ANY_PROFILE, GLFW_OPENGL_COMPAT_PROFILE
#if defined(__APPLE__) #if defined(__APPLE__)
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // OSX Requires fordward compatibility glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // OSX Requires fordward compatibility
#else #else
@ -2357,10 +2357,10 @@ static bool InitGraphicsDevice(int width, int height)
#endif #endif
glfwMakeContextCurrent(window); glfwMakeContextCurrent(window);
// Try to disable GPU V-Sync by default, set framerate using SetTargetFPS() // Try to disable GPU V-Sync by default, set framerate using SetTargetFPS()
// NOTE: V-Sync can be enabled by graphic driver configuration // NOTE: V-Sync can be enabled by graphic driver configuration
glfwSwapInterval(0); glfwSwapInterval(0);
#if defined(PLATFORM_DESKTOP) #if defined(PLATFORM_DESKTOP)
// Load OpenGL 3.3 extensions // Load OpenGL 3.3 extensions
@ -2372,6 +2372,7 @@ static bool InitGraphicsDevice(int width, int height)
// NOTE: V-Sync can be enabled by graphic driver configuration // NOTE: V-Sync can be enabled by graphic driver configuration
if (configFlags & FLAG_VSYNC_HINT) if (configFlags & FLAG_VSYNC_HINT)
{ {
// WARNING: It seems to hits a critical render path in Intel HD Graphics
glfwSwapInterval(1); glfwSwapInterval(1);
TraceLog(LOG_INFO, "Trying to enable VSYNC"); TraceLog(LOG_INFO, "Trying to enable VSYNC");
} }
@ -2444,14 +2445,14 @@ static bool InitGraphicsDevice(int width, int height)
// EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER is an optimization that can have large performance benefits on mobile devices. // EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER is an optimization that can have large performance benefits on mobile devices.
// Its syntax is subject to change, though. Please update your Visual Studio templates if you experience compilation issues with it. // Its syntax is subject to change, though. Please update your Visual Studio templates if you experience compilation issues with it.
EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER, EGL_TRUE, EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER, EGL_TRUE,
// EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE is an option that enables ANGLE to automatically call // EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE is an option that enables ANGLE to automatically call
// the IDXGIDevice3::Trim method on behalf of the application when it gets suspended. // the IDXGIDevice3::Trim method on behalf of the application when it gets suspended.
// Calling IDXGIDevice3::Trim when an application is suspended is a Windows Store application certification requirement. // Calling IDXGIDevice3::Trim when an application is suspended is a Windows Store application certification requirement.
EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE, EGL_TRUE, EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE, EGL_TRUE,
EGL_NONE, EGL_NONE,
}; };
const EGLint fl9_3DisplayAttributes[] = const EGLint fl9_3DisplayAttributes[] =
{ {
// These can be used to request ANGLE's D3D11 renderer, with D3D11 Feature Level 9_3. // These can be used to request ANGLE's D3D11 renderer, with D3D11 Feature Level 9_3.
@ -2474,7 +2475,7 @@ static bool InitGraphicsDevice(int width, int height)
EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE, EGL_TRUE, EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE, EGL_TRUE,
EGL_NONE, EGL_NONE,
}; };
EGLConfig config = NULL; EGLConfig config = NULL;
// eglGetPlatformDisplayEXT is an alternative to eglGetDisplay. It allows us to pass in display attributes, used to configure D3D11. // eglGetPlatformDisplayEXT is an alternative to eglGetDisplay. It allows us to pass in display attributes, used to configure D3D11.
@ -2486,15 +2487,15 @@ static bool InitGraphicsDevice(int width, int height)
} }
// //
// To initialize the display, we make three sets of calls to eglGetPlatformDisplayEXT and eglInitialize, with varying // To initialize the display, we make three sets of calls to eglGetPlatformDisplayEXT and eglInitialize, with varying
// parameters passed to eglGetPlatformDisplayEXT: // parameters passed to eglGetPlatformDisplayEXT:
// 1) The first calls uses "defaultDisplayAttributes" as a parameter. This corresponds to D3D11 Feature Level 10_0+. // 1) The first calls uses "defaultDisplayAttributes" as a parameter. This corresponds to D3D11 Feature Level 10_0+.
// 2) If eglInitialize fails for step 1 (e.g. because 10_0+ isn't supported by the default GPU), then we try again // 2) If eglInitialize fails for step 1 (e.g. because 10_0+ isn't supported by the default GPU), then we try again
// using "fl9_3DisplayAttributes". This corresponds to D3D11 Feature Level 9_3. // using "fl9_3DisplayAttributes". This corresponds to D3D11 Feature Level 9_3.
// 3) If eglInitialize fails for step 2 (e.g. because 9_3+ isn't supported by the default GPU), then we try again // 3) If eglInitialize fails for step 2 (e.g. because 9_3+ isn't supported by the default GPU), then we try again
// using "warpDisplayAttributes". This corresponds to D3D11 Feature Level 11_0 on WARP, a D3D11 software rasterizer. // using "warpDisplayAttributes". This corresponds to D3D11 Feature Level 11_0 on WARP, a D3D11 software rasterizer.
// //
// This tries to initialize EGL to D3D11 Feature Level 10_0+. See above comment for details. // This tries to initialize EGL to D3D11 Feature Level 10_0+. See above comment for details.
display = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, defaultDisplayAttributes); display = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, defaultDisplayAttributes);
if (display == EGL_NO_DISPLAY) if (display == EGL_NO_DISPLAY)
@ -2502,7 +2503,7 @@ static bool InitGraphicsDevice(int width, int height)
TraceLog(LOG_WARNING, "Failed to initialize EGL display"); TraceLog(LOG_WARNING, "Failed to initialize EGL display");
return false; return false;
} }
if (eglInitialize(display, NULL, NULL) == EGL_FALSE) if (eglInitialize(display, NULL, NULL) == EGL_FALSE)
{ {
// This tries to initialize EGL to D3D11 Feature Level 9_3, if 10_0+ is unavailable (e.g. on some mobile devices). // This tries to initialize EGL to D3D11 Feature Level 9_3, if 10_0+ is unavailable (e.g. on some mobile devices).
@ -2517,7 +2518,7 @@ static bool InitGraphicsDevice(int width, int height)
{ {
// This initializes EGL to D3D11 Feature Level 11_0 on WARP, if 9_3+ is unavailable on the default GPU. // This initializes EGL to D3D11 Feature Level 11_0 on WARP, if 9_3+ is unavailable on the default GPU.
display = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, warpDisplayAttributes); display = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, warpDisplayAttributes);
if (display == EGL_NO_DISPLAY) if (display == EGL_NO_DISPLAY)
{ {
TraceLog(LOG_WARNING, "Failed to initialize EGL display"); TraceLog(LOG_WARNING, "Failed to initialize EGL display");
return false; return false;
@ -2545,7 +2546,7 @@ static bool InitGraphicsDevice(int width, int height)
//PropertySet^ surfaceCreationProperties = ref new PropertySet(); //PropertySet^ surfaceCreationProperties = ref new PropertySet();
//surfaceCreationProperties->Insert(ref new String(EGLNativeWindowTypeProperty), window); // CoreWindow^ window //surfaceCreationProperties->Insert(ref new String(EGLNativeWindowTypeProperty), window); // CoreWindow^ window
// You can configure the surface to render at a lower resolution and be scaled up to // You can configure the surface to render at a lower resolution and be scaled up to
// the full window size. The scaling is often free on mobile hardware. // the full window size. The scaling is often free on mobile hardware.
// //
// One way to configure the SwapChainPanel is to specify precisely which resolution it should render at. // One way to configure the SwapChainPanel is to specify precisely which resolution it should render at.
@ -2557,20 +2558,20 @@ static bool InitGraphicsDevice(int width, int height)
// float customResolutionScale = 0.5f; // float customResolutionScale = 0.5f;
// surfaceCreationProperties->Insert(ref new String(EGLRenderResolutionScaleProperty), PropertyValue::CreateSingle(customResolutionScale)); // surfaceCreationProperties->Insert(ref new String(EGLRenderResolutionScaleProperty), PropertyValue::CreateSingle(customResolutionScale));
// eglCreateWindowSurface() requires a EGLNativeWindowType parameter, // eglCreateWindowSurface() requires a EGLNativeWindowType parameter,
// In Windows platform: typedef HWND EGLNativeWindowType; // In Windows platform: typedef HWND EGLNativeWindowType;
// Property: EGLNativeWindowTypeProperty // Property: EGLNativeWindowTypeProperty
// Type: IInspectable // Type: IInspectable
// Description: Set this property to specify the window type to use for creating a surface. // Description: Set this property to specify the window type to use for creating a surface.
// If this property is missing, surface creation will fail. // If this property is missing, surface creation will fail.
// //
//const wchar_t EGLNativeWindowTypeProperty[] = L"EGLNativeWindowTypeProperty"; //const wchar_t EGLNativeWindowTypeProperty[] = L"EGLNativeWindowTypeProperty";
//https://stackoverflow.com/questions/46550182/how-to-create-eglsurface-using-c-winrt-and-angle //https://stackoverflow.com/questions/46550182/how-to-create-eglsurface-using-c-winrt-and-angle
//surface = eglCreateWindowSurface(display, config, reinterpret_cast<IInspectable*>(surfaceCreationProperties), surfaceAttributes); //surface = eglCreateWindowSurface(display, config, reinterpret_cast<IInspectable*>(surfaceCreationProperties), surfaceAttributes);
surface = eglCreateWindowSurface(display, config, uwpWindow, surfaceAttributes); surface = eglCreateWindowSurface(display, config, uwpWindow, surfaceAttributes);
if (surface == EGL_NO_SURFACE) if (surface == EGL_NO_SURFACE)
@ -2586,16 +2587,16 @@ static bool InitGraphicsDevice(int width, int height)
return false; return false;
} }
// Get EGL display window size // Get EGL display window size
eglQuerySurface(display, surface, EGL_WIDTH, &screenWidth); eglQuerySurface(display, surface, EGL_WIDTH, &screenWidth);
eglQuerySurface(display, surface, EGL_HEIGHT, &screenHeight); eglQuerySurface(display, surface, EGL_HEIGHT, &screenHeight);
#else // PLATFORM_ANDROID, PLATFORM_RPI #else // PLATFORM_ANDROID, PLATFORM_RPI
EGLint numConfigs; EGLint numConfigs;
// Get an EGL display connection // Get an EGL display connection
display = eglGetDisplay(EGL_DEFAULT_DISPLAY); display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (display == EGL_NO_DISPLAY) if (display == EGL_NO_DISPLAY)
{ {
TraceLog(LOG_WARNING, "Failed to initialize EGL display"); TraceLog(LOG_WARNING, "Failed to initialize EGL display");
return false; return false;
@ -2828,7 +2829,7 @@ static void SetupFramebuffer(int width, int height)
static void InitTimer(void) static void InitTimer(void)
{ {
srand((unsigned int)time(NULL)); // Initialize random seed srand((unsigned int)time(NULL)); // Initialize random seed
#if !defined(SUPPORT_BUSY_WAIT_LOOP) && defined(_WIN32) #if !defined(SUPPORT_BUSY_WAIT_LOOP) && defined(_WIN32)
timeBeginPeriod(1); // Setup high-resolution timer to 1ms (granularity of 1-2 ms) timeBeginPeriod(1); // Setup high-resolution timer to 1ms (granularity of 1-2 ms)
#endif #endif
@ -2931,7 +2932,7 @@ static void PollInputEvents(void)
// Register previous mouse states // Register previous mouse states
previousMouseWheelY = currentMouseWheelY; previousMouseWheelY = currentMouseWheelY;
currentMouseWheelY = 0; currentMouseWheelY = 0;
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
{ {
previousMouseState[i] = currentMouseState[i]; previousMouseState[i] = currentMouseState[i];
currentMouseState[i] = currentMouseStateEvdev[i]; currentMouseState[i] = currentMouseStateEvdev[i];
@ -3097,11 +3098,11 @@ static void SwapBuffers(void)
glfwSwapBuffers(window); glfwSwapBuffers(window);
#if __APPLE__ #if __APPLE__
// Workaround for missing/erroneous initial rendering on macOS // Workaround for missing/erroneous initial rendering on macOS
if (windowNeedsUpdating) if (windowNeedsUpdating)
{ {
// Desugared version of Objective C: [glfwGetNSGLContext(window) update] // Desugared version of Objective C: [glfwGetNSGLContext(window) update]
((id (*)(id, SEL))objc_msgSend)(glfwGetNSGLContext(window), sel_registerName("update")); ((id (*)(id, SEL))objc_msgSend)(glfwGetNSGLContext(window), sel_registerName("update"));
windowNeedsUpdating--; windowNeedsUpdating--;
} }
#endif #endif
@ -3144,19 +3145,19 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i
{ {
GifEnd(); GifEnd();
gifRecording = false; gifRecording = false;
TraceLog(LOG_INFO, "End animated GIF recording"); TraceLog(LOG_INFO, "End animated GIF recording");
} }
else else
{ {
gifRecording = true; gifRecording = true;
gifFramesCounter = 0; gifFramesCounter = 0;
// NOTE: delay represents the time between frames in the gif, if we capture a gif frame every // NOTE: delay represents the time between frames in the gif, if we capture a gif frame every
// 10 game frames and each frame trakes 16.6ms (60fps), delay between gif frames should be ~16.6*10. // 10 game frames and each frame trakes 16.6ms (60fps), delay between gif frames should be ~16.6*10.
GifBegin(FormatText("screenrec%03i.gif", screenshotCounter), screenWidth, screenHeight, (int)(GetFrameTime()*10.0f), 8, false); GifBegin(FormatText("screenrec%03i.gif", screenshotCounter), screenWidth, screenHeight, (int)(GetFrameTime()*10.0f), 8, false);
screenshotCounter++; screenshotCounter++;
TraceLog(LOG_INFO, "Begin animated GIF recording: %s", FormatText("screenrec%03i.gif", screenshotCounter)); TraceLog(LOG_INFO, "Begin animated GIF recording: %s", FormatText("screenrec%03i.gif", screenshotCounter));
} }
} }
@ -3173,7 +3174,7 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i
else else
{ {
currentKeyState[key] = action; currentKeyState[key] = action;
// NOTE: lastKeyPressed already registered on CharCallback() // NOTE: lastKeyPressed already registered on CharCallback()
//if (action == GLFW_PRESS) lastKeyPressed = key; //if (action == GLFW_PRESS) lastKeyPressed = key;
} }
@ -3243,12 +3244,12 @@ static void MouseCursorPosCallback(GLFWwindow *window, double x, double y)
// GLFW3 Char Key Callback, runs on key down (get unicode char value) // GLFW3 Char Key Callback, runs on key down (get unicode char value)
static void CharCallback(GLFWwindow *window, unsigned int key) static void CharCallback(GLFWwindow *window, unsigned int key)
{ {
// NOTE: Registers any key down considering OS keyboard layout but // NOTE: Registers any key down considering OS keyboard layout but
// do not detects action events, those should be managed by user... // do not detects action events, those should be managed by user...
// https://github.com/glfw/glfw/issues/668#issuecomment-166794907 // https://github.com/glfw/glfw/issues/668#issuecomment-166794907
// http://www.glfw.org/docs/latest/input_guide.html#input_char // http://www.glfw.org/docs/latest/input_guide.html#input_char
lastKeyPressed = key; lastKeyPressed = key;
} }
@ -3349,7 +3350,7 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd)
{ {
// Init graphics device (display device and OpenGL context) // Init graphics device (display device and OpenGL context)
InitGraphicsDevice(screenWidth, screenHeight); InitGraphicsDevice(screenWidth, screenHeight);
// Init hi-res timer // Init hi-res timer
InitTimer(); InitTimer();
@ -3457,15 +3458,15 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
// Get second touch position // Get second touch position
touchPosition[1].x = AMotionEvent_getX(event, 1); touchPosition[1].x = AMotionEvent_getX(event, 1);
touchPosition[1].y = AMotionEvent_getY(event, 1); touchPosition[1].y = AMotionEvent_getY(event, 1);
// Useful functions for gamepad inputs: // Useful functions for gamepad inputs:
//AMotionEvent_getAction() //AMotionEvent_getAction()
//AMotionEvent_getAxisValue() //AMotionEvent_getAxisValue()
//AMotionEvent_getButtonState() //AMotionEvent_getButtonState()
// Gamepad dpad button presses capturing // Gamepad dpad button presses capturing
// TODO: That's weird, key input (or button) // TODO: That's weird, key input (or button)
// shouldn't come as a TYPE_MOTION event... // shouldn't come as a TYPE_MOTION event...
int32_t keycode = AKeyEvent_getKeyCode(event); int32_t keycode = AKeyEvent_getKeyCode(event);
if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN) if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN)
{ {
@ -3541,7 +3542,7 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
// Normalize gestureEvent.position[x] for screenWidth and screenHeight // Normalize gestureEvent.position[x] for screenWidth and screenHeight
gestureEvent.position[0].x /= (float)GetScreenWidth(); gestureEvent.position[0].x /= (float)GetScreenWidth();
gestureEvent.position[0].y /= (float)GetScreenHeight(); gestureEvent.position[0].y /= (float)GetScreenHeight();
gestureEvent.position[1].x /= (float)GetScreenWidth(); gestureEvent.position[1].x /= (float)GetScreenWidth();
gestureEvent.position[1].y /= (float)GetScreenHeight(); gestureEvent.position[1].y /= (float)GetScreenHeight();
@ -3549,14 +3550,14 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
ProcessGestureEvent(gestureEvent); ProcessGestureEvent(gestureEvent);
} }
#else #else
// Support only simple touch position // Support only simple touch position
if (flags == AMOTION_EVENT_ACTION_DOWN) if (flags == AMOTION_EVENT_ACTION_DOWN)
{ {
// Get first touch position // Get first touch position
touchPosition[0].x = AMotionEvent_getX(event, 0); touchPosition[0].x = AMotionEvent_getX(event, 0);
touchPosition[0].y = AMotionEvent_getY(event, 0); touchPosition[0].y = AMotionEvent_getY(event, 0);
touchPosition[0].x /= (float)GetScreenWidth(); touchPosition[0].x /= (float)GetScreenWidth();
touchPosition[0].y /= (float)GetScreenHeight(); touchPosition[0].y /= (float)GetScreenHeight();
} }
@ -3619,10 +3620,10 @@ static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent
emscripten_get_pointerlock_status(&plce); emscripten_get_pointerlock_status(&plce);
//if (plce.isActive) TraceLog(LOG_WARNING, "Pointer lock exit did not work!"); //if (plce.isActive) TraceLog(LOG_WARNING, "Pointer lock exit did not work!");
} }
toggleCursorLock = false; toggleCursorLock = false;
} }
return 0; return 0;
} }
@ -3889,9 +3890,9 @@ static void InitMouse(void)
// Open the linux directory of "/dev/input" // Open the linux directory of "/dev/input"
directory = opendir(DEFAULT_EVDEV_PATH); directory = opendir(DEFAULT_EVDEV_PATH);
if (directory) if (directory)
{ {
while ((entity = readdir(directory)) != NULL) while ((entity = readdir(directory)) != NULL)
{ {
if (strncmp("event", entity->d_name, strlen("event")) == 0) // Search for devices named "event*" if (strncmp("event", entity->d_name, strlen("event")) == 0) // Search for devices named "event*"
{ {
@ -3899,7 +3900,7 @@ static void InitMouse(void)
EventThreadSpawn(Path); // Identify the device and spawn a thread for it EventThreadSpawn(Path); // Identify the device and spawn a thread for it
} }
} }
closedir(directory); closedir(directory);
} }
else else
@ -3916,7 +3917,7 @@ static void EventThreadSpawn(char *device)
#define BIT(x) (1UL<<OFF(x)) #define BIT(x) (1UL<<OFF(x))
#define LONG(x) ((x)/BITS_PER_LONG) #define LONG(x) ((x)/BITS_PER_LONG)
#define TEST_BIT(array, bit) ((array[LONG(bit)] >> OFF(bit)) & 1) #define TEST_BIT(array, bit) ((array[LONG(bit)] >> OFF(bit)) & 1)
struct input_absinfo absinfo; struct input_absinfo absinfo;
unsigned long evBits[NBITS(EV_MAX)]; unsigned long evBits[NBITS(EV_MAX)];
unsigned long absBits[NBITS(ABS_MAX)]; unsigned long absBits[NBITS(ABS_MAX)];
@ -3927,7 +3928,7 @@ static void EventThreadSpawn(char *device)
bool hasAbsMulti = false; bool hasAbsMulti = false;
int freeWorkerId = -1; int freeWorkerId = -1;
int fd = -1; int fd = -1;
InputEventWorker *worker; InputEventWorker *worker;
/////////////////////////////////// Open the device and allocate worker ///////////////////////////////////////////// /////////////////////////////////// Open the device and allocate worker /////////////////////////////////////////////
@ -3967,15 +3968,15 @@ static void EventThreadSpawn(char *device)
int devNum = 0; int devNum = 0;
char *ptrDevName = strrchr(device, 't'); char *ptrDevName = strrchr(device, 't');
worker->eventNum = -1; worker->eventNum = -1;
if (ptrDevName != NULL) if (ptrDevName != NULL)
{ {
if (sscanf(ptrDevName, "t%d", &devNum) == 1) if (sscanf(ptrDevName, "t%d", &devNum) == 1)
worker->eventNum = devNum; worker->eventNum = devNum;
} }
// At this point we have a connection to the device, // At this point we have a connection to the device,
// but we don't yet know what the device is (Could be // but we don't yet know what the device is (Could be
// many things, even as simple as a power button) // many things, even as simple as a power button)
/////////////////////////////////// Identify the device ///////////////////////////////////////////// /////////////////////////////////// Identify the device /////////////////////////////////////////////
@ -3983,15 +3984,15 @@ static void EventThreadSpawn(char *device)
ioctl(fd, EVIOCGBIT(0, sizeof(evBits)), evBits); // Read a bitfield of the avalable device properties ioctl(fd, EVIOCGBIT(0, sizeof(evBits)), evBits); // Read a bitfield of the avalable device properties
// Check for absolute input devices // Check for absolute input devices
if (TEST_BIT(evBits, EV_ABS)) if (TEST_BIT(evBits, EV_ABS))
{ {
ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absBits)), absBits); ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absBits)), absBits);
// Check for absolute movement support (usualy touchscreens, but also joysticks) // Check for absolute movement support (usualy touchscreens, but also joysticks)
if (TEST_BIT(absBits, ABS_X) && TEST_BIT(absBits, ABS_Y)) if (TEST_BIT(absBits, ABS_X) && TEST_BIT(absBits, ABS_Y))
{ {
hasAbs = true; hasAbs = true;
// Get the scaling values // Get the scaling values
ioctl(fd, EVIOCGABS(ABS_X), &absinfo); ioctl(fd, EVIOCGABS(ABS_X), &absinfo);
worker->absRange.x = absinfo.minimum; worker->absRange.x = absinfo.minimum;
@ -4000,12 +4001,12 @@ static void EventThreadSpawn(char *device)
worker->absRange.y = absinfo.minimum; worker->absRange.y = absinfo.minimum;
worker->absRange.height = absinfo.maximum - absinfo.minimum; worker->absRange.height = absinfo.maximum - absinfo.minimum;
} }
// Check for multiple absolute movement support (usualy multitouch touchscreens) // Check for multiple absolute movement support (usualy multitouch touchscreens)
if (TEST_BIT(absBits, ABS_MT_POSITION_X) && TEST_BIT(absBits, ABS_MT_POSITION_Y)) if (TEST_BIT(absBits, ABS_MT_POSITION_X) && TEST_BIT(absBits, ABS_MT_POSITION_Y))
{ {
hasAbsMulti = true; hasAbsMulti = true;
// Get the scaling values // Get the scaling values
ioctl(fd, EVIOCGABS(ABS_X), &absinfo); ioctl(fd, EVIOCGABS(ABS_X), &absinfo);
worker->absRange.x = absinfo.minimum; worker->absRange.x = absinfo.minimum;
@ -4017,15 +4018,15 @@ static void EventThreadSpawn(char *device)
} }
// Check for relative movement support (usualy mouse) // Check for relative movement support (usualy mouse)
if (TEST_BIT(evBits, EV_REL)) if (TEST_BIT(evBits, EV_REL))
{ {
ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relBits)), relBits); ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relBits)), relBits);
if (TEST_BIT(relBits, REL_X) && TEST_BIT(relBits, REL_Y)) hasRel = true; if (TEST_BIT(relBits, REL_X) && TEST_BIT(relBits, REL_Y)) hasRel = true;
} }
// Check for button support to determine the device type(usualy on all input devices) // Check for button support to determine the device type(usualy on all input devices)
if (TEST_BIT(evBits, EV_KEY)) if (TEST_BIT(evBits, EV_KEY))
{ {
ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keyBits)), keyBits); ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keyBits)), keyBits);
@ -4077,12 +4078,12 @@ static void EventThreadSpawn(char *device)
#if defined(USE_LAST_TOUCH_DEVICE) #if defined(USE_LAST_TOUCH_DEVICE)
// Find touchscreen with the highest index // Find touchscreen with the highest index
int maxTouchNumber = -1; int maxTouchNumber = -1;
for (int i = 0; i < sizeof(eventWorkers)/sizeof(InputEventWorker); ++i) for (int i = 0; i < sizeof(eventWorkers)/sizeof(InputEventWorker); ++i)
{ {
if (eventWorkers[i].isTouch && (eventWorkers[i].eventNum > maxTouchNumber)) maxTouchNumber = eventWorkers[i].eventNum; if (eventWorkers[i].isTouch && (eventWorkers[i].eventNum > maxTouchNumber)) maxTouchNumber = eventWorkers[i].eventNum;
} }
// Find toucnscreens with lower indexes // Find toucnscreens with lower indexes
for (int i = 0; i < sizeof(eventWorkers)/sizeof(InputEventWorker); ++i) for (int i = 0; i < sizeof(eventWorkers)/sizeof(InputEventWorker); ++i)
{ {
@ -4134,7 +4135,7 @@ static void *EventThread(void *arg)
if (event.code == REL_WHEEL) if (event.code == REL_WHEEL)
{ {
currentMouseWheelY += event.value; currentMouseWheelY += event.value;
} }
} }
/////////////////////////////// Absolute movement parsing //////////////////////////////////// /////////////////////////////// Absolute movement parsing ////////////////////////////////////
@ -4231,9 +4232,9 @@ static void *EventThread(void *arg)
usleep(5000); // Sleep for 5ms to avoid hogging CPU time usleep(5000); // Sleep for 5ms to avoid hogging CPU time
} }
} }
close(worker->fd); close(worker->fd);
return NULL; return NULL;
} }

View file

@ -511,7 +511,7 @@ typedef struct Mesh {
float *tangents; // Vertex tangents (XYZW - 4 components per vertex) (shader-location = 4) float *tangents; // Vertex tangents (XYZW - 4 components per vertex) (shader-location = 4)
unsigned char *colors; // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3) unsigned char *colors; // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3)
unsigned short *indices;// Vertex indices (in case vertex data comes indexed) unsigned short *indices;// Vertex indices (in case vertex data comes indexed)
// Animation vertex data // Animation vertex data
float *baseVertices; // Vertex base position (required to apply bones transformations) float *baseVertices; // Vertex base position (required to apply bones transformations)
float *baseNormals; // Vertex base normals (required to apply bones transformations) float *baseNormals; // Vertex base normals (required to apply bones transformations)

View file

@ -199,7 +199,7 @@ typedef unsigned char byte;
float *tangents; // vertex tangents (XYZW - 4 components per vertex) (shader-location = 4) float *tangents; // vertex tangents (XYZW - 4 components per vertex) (shader-location = 4)
unsigned char *colors; // vertex colors (RGBA - 4 components per vertex) (shader-location = 3) unsigned char *colors; // vertex colors (RGBA - 4 components per vertex) (shader-location = 3)
unsigned short *indices;// vertex indices (in case vertex data comes indexed) unsigned short *indices;// vertex indices (in case vertex data comes indexed)
// Animation vertex data // Animation vertex data
float *baseVertices; // Vertex base position (required to apply bones transformations) float *baseVertices; // Vertex base position (required to apply bones transformations)
float *baseNormals; // Vertex base normals (required to apply bones transformations) float *baseNormals; // Vertex base normals (required to apply bones transformations)
@ -729,7 +729,7 @@ typedef struct VrStereoConfig {
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
#if !defined(GRAPHICS_API_OPENGL_11) && defined(SUPPORT_DISTORTION_SHADER) #if !defined(GRAPHICS_API_OPENGL_11) && defined(SUPPORT_DISTORTION_SHADER)
// Distortion shader embedded // Distortion shader embedded
static char distortionFShaderStr[] = static char distortionFShaderStr[] =
#if defined(GRAPHICS_API_OPENGL_21) #if defined(GRAPHICS_API_OPENGL_21)
"#version 120 \n" "#version 120 \n"
#elif defined(GRAPHICS_API_OPENGL_ES2) #elif defined(GRAPHICS_API_OPENGL_ES2)
@ -1217,9 +1217,9 @@ void rlEnd(void)
// WARNING: If we are between rlPushMatrix() and rlPopMatrix() and we need to force a rlglDraw(), // WARNING: If we are between rlPushMatrix() and rlPopMatrix() and we need to force a rlglDraw(),
// we need to call rlPopMatrix() before to recover *currentMatrix (modelview) for the next forced draw call! // we need to call rlPopMatrix() before to recover *currentMatrix (modelview) for the next forced draw call!
// Also noted that if we had multiple matrix pushed, it will require "stackCounter" pops before launching the draw // Also noted that if we had multiple matrix pushed, it will require "stackCounter" pops before launching the draw
// TODO: Undoubtely, current rlPushMatrix/rlPopMatrix should be redesigned... or removed... it's not working properly // TODO: Undoubtely, current rlPushMatrix/rlPopMatrix should be redesigned... or removed... it's not working properly
rlPopMatrix(); rlPopMatrix();
rlglDraw(); rlglDraw();
} }
@ -1507,7 +1507,7 @@ void rlDeleteRenderTextures(RenderTexture2D target)
{ {
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
if (target.texture.id > 0) glDeleteTextures(1, &target.texture.id); if (target.texture.id > 0) glDeleteTextures(1, &target.texture.id);
if (target.depth.id > 0) if (target.depth.id > 0)
{ {
#if defined(GRAPHICS_API_OPENGL_21) || defined(GRAPHICS_API_OPENGL_ES2) #if defined(GRAPHICS_API_OPENGL_21) || defined(GRAPHICS_API_OPENGL_ES2)
glDeleteRenderbuffers(1, &target.depth.id); glDeleteRenderbuffers(1, &target.depth.id);
@ -1640,7 +1640,7 @@ void rlglInit(int width, int height)
char *extensions = (char *)glGetString(GL_EXTENSIONS); // One big const string char *extensions = (char *)glGetString(GL_EXTENSIONS); // One big const string
// NOTE: We have to duplicate string because glGetString() returns a const string // NOTE: We have to duplicate string because glGetString() returns a const string
int len = strlen(extensions) + 1; int len = strlen(extensions) + 1;
char *extensionsDup = (char *)malloc(len); char *extensionsDup = (char *)malloc(len);
strcpy(extensionsDup, extensions); strcpy(extensionsDup, extensions);
@ -2260,7 +2260,7 @@ RenderTexture2D rlLoadRenderTexture(int width, int height)
} }
if (target.texture.id > 0) glDeleteTextures(1, &target.texture.id); if (target.texture.id > 0) glDeleteTextures(1, &target.texture.id);
if (target.depth.id > 0) if (target.depth.id > 0)
{ {
#if defined(USE_DEPTH_RENDERBUFFER) #if defined(USE_DEPTH_RENDERBUFFER)
glDeleteRenderbuffers(1, &target.depth.id); glDeleteRenderbuffers(1, &target.depth.id);
@ -2268,7 +2268,7 @@ RenderTexture2D rlLoadRenderTexture(int width, int height)
glDeleteTextures(1, &target.depth.id); glDeleteTextures(1, &target.depth.id);
#endif #endif
} }
glDeleteFramebuffers(1, &target.id); glDeleteFramebuffers(1, &target.id);
} }
else TraceLog(LOG_INFO, "[FBO ID %i] Framebuffer object created successfully", target.id); else TraceLog(LOG_INFO, "[FBO ID %i] Framebuffer object created successfully", target.id);
@ -2736,7 +2736,7 @@ void rlUnloadMesh(Mesh *mesh)
if (mesh->tangents != NULL) free(mesh->tangents); if (mesh->tangents != NULL) free(mesh->tangents);
if (mesh->texcoords2 != NULL) free(mesh->texcoords2); if (mesh->texcoords2 != NULL) free(mesh->texcoords2);
if (mesh->indices != NULL) free(mesh->indices); if (mesh->indices != NULL) free(mesh->indices);
if (mesh->baseVertices != NULL) free(mesh->baseVertices); if (mesh->baseVertices != NULL) free(mesh->baseVertices);
if (mesh->baseNormals != NULL) free(mesh->baseNormals); if (mesh->baseNormals != NULL) free(mesh->baseNormals);
if (mesh->weightBias != NULL) free(mesh->weightBias); if (mesh->weightBias != NULL) free(mesh->weightBias);

View file

@ -9,7 +9,7 @@
* Allows drawing rectangles and text with a single draw call, very useful for GUI systems! * Allows drawing rectangles and text with a single draw call, very useful for GUI systems!
* *
* #define SUPPORT_QUADS_DRAW_MODE * #define SUPPORT_QUADS_DRAW_MODE
* Use QUADS instead of TRIANGLES for drawing when possible. * Use QUADS instead of TRIANGLES for drawing when possible.
* Some lines-based shapes could still use lines * Some lines-based shapes could still use lines
* *
* LICENSE: zlib/libpng * LICENSE: zlib/libpng
@ -116,13 +116,13 @@ void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color)
startPos = endPos; startPos = endPos;
endPos = tempPos; endPos = tempPos;
} }
float dx = endPos.x - startPos.x; float dx = endPos.x - startPos.x;
float dy = endPos.y - startPos.y; float dy = endPos.y - startPos.y;
float d = sqrtf(dx*dx + dy*dy); float d = sqrtf(dx*dx + dy*dy);
float angle = asinf(dy/d); float angle = asinf(dy/d);
rlEnableTexture(GetShapesTexture().id); rlEnableTexture(GetShapesTexture().id);
rlPushMatrix(); rlPushMatrix();
@ -155,12 +155,12 @@ void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color)
for (int i = 1; i <= LINE_DIVISIONS; i++) for (int i = 1; i <= LINE_DIVISIONS; i++)
{ {
// Cubic easing in-out // Cubic easing in-out
// NOTE: Easing is calculated only for y position value // NOTE: Easing is calculated only for y position value
current.y = EaseCubicInOut((float)i, startPos.y, endPos.y - startPos.y, (float)LINE_DIVISIONS); current.y = EaseCubicInOut((float)i, startPos.y, endPos.y - startPos.y, (float)LINE_DIVISIONS);
current.x = previous.x + (endPos.x - startPos.x)/ (float)LINE_DIVISIONS; current.x = previous.x + (endPos.x - startPos.x)/ (float)LINE_DIVISIONS;
DrawLineEx(previous, current, thick, color); DrawLineEx(previous, current, thick, color);
previous = current; previous = current;
} }
} }
@ -176,7 +176,7 @@ void DrawCircle(int centerX, int centerY, float radius, Color color)
void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2) void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2)
{ {
if (rlCheckBufferLimit(RL_TRIANGLES, 3*36)) rlglDraw(); if (rlCheckBufferLimit(RL_TRIANGLES, 3*36)) rlglDraw();
rlBegin(RL_TRIANGLES); rlBegin(RL_TRIANGLES);
for (int i = 0; i < 360; i += 10) for (int i = 0; i < 360; i += 10)
{ {
@ -193,31 +193,31 @@ void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Co
// Draw a color-filled circle (Vector version) // Draw a color-filled circle (Vector version)
// NOTE: On OpenGL 3.3 and ES2 we use QUADS to avoid drawing order issues (view rlglDraw) // NOTE: On OpenGL 3.3 and ES2 we use QUADS to avoid drawing order issues (view rlglDraw)
void DrawCircleV(Vector2 center, float radius, Color color) void DrawCircleV(Vector2 center, float radius, Color color)
{ {
#if defined(SUPPORT_QUADS_DRAW_MODE) #if defined(SUPPORT_QUADS_DRAW_MODE)
if (rlCheckBufferLimit(RL_QUADS, 4*(36/2))) rlglDraw(); if (rlCheckBufferLimit(RL_QUADS, 4*(36/2))) rlglDraw();
rlEnableTexture(GetShapesTexture().id); rlEnableTexture(GetShapesTexture().id);
rlBegin(RL_QUADS); rlBegin(RL_QUADS);
for (int i = 0; i < 360; i += 20) for (int i = 0; i < 360; i += 20)
{ {
rlColor4ub(color.r, color.g, color.b, color.a); rlColor4ub(color.r, color.g, color.b, color.a);
rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height); rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
rlVertex2f(center.x, center.y); rlVertex2f(center.x, center.y);
rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height); rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
rlVertex2f(center.x + sinf(DEG2RAD*i)*radius, center.y + cosf(DEG2RAD*i)*radius); rlVertex2f(center.x + sinf(DEG2RAD*i)*radius, center.y + cosf(DEG2RAD*i)*radius);
rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height); rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
rlVertex2f(center.x + sinf(DEG2RAD*(i + 10))*radius, center.y + cosf(DEG2RAD*(i + 10))*radius); rlVertex2f(center.x + sinf(DEG2RAD*(i + 10))*radius, center.y + cosf(DEG2RAD*(i + 10))*radius);
rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height); rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height);
rlVertex2f(center.x + sinf(DEG2RAD*(i + 20))*radius, center.y + cosf(DEG2RAD*(i + 20))*radius); rlVertex2f(center.x + sinf(DEG2RAD*(i + 20))*radius, center.y + cosf(DEG2RAD*(i + 20))*radius);
} }
rlEnd(); rlEnd();
rlDisableTexture(); rlDisableTexture();
#else #else
if (rlCheckBufferLimit(RL_TRIANGLES, 3*(36/2))) rlglDraw(); if (rlCheckBufferLimit(RL_TRIANGLES, 3*(36/2))) rlglDraw();
@ -226,7 +226,7 @@ void DrawCircleV(Vector2 center, float radius, Color color)
for (int i = 0; i < 360; i += 10) for (int i = 0; i < 360; i += 10)
{ {
rlColor4ub(color.r, color.g, color.b, color.a); rlColor4ub(color.r, color.g, color.b, color.a);
rlVertex2f(center.x, center.y); rlVertex2f(center.x, center.y);
rlVertex2f(center.x + sinf(DEG2RAD*i)*radius, center.y + cosf(DEG2RAD*i)*radius); rlVertex2f(center.x + sinf(DEG2RAD*i)*radius, center.y + cosf(DEG2RAD*i)*radius);
rlVertex2f(center.x + sinf(DEG2RAD*(i + 10))*radius, center.y + cosf(DEG2RAD*(i + 10))*radius); rlVertex2f(center.x + sinf(DEG2RAD*(i + 10))*radius, center.y + cosf(DEG2RAD*(i + 10))*radius);
@ -239,7 +239,7 @@ void DrawCircleV(Vector2 center, float radius, Color color)
void DrawCircleLines(int centerX, int centerY, float radius, Color color) void DrawCircleLines(int centerX, int centerY, float radius, Color color)
{ {
if (rlCheckBufferLimit(RL_LINES, 2*36)) rlglDraw(); if (rlCheckBufferLimit(RL_LINES, 2*36)) rlglDraw();
rlBegin(RL_LINES); rlBegin(RL_LINES);
rlColor4ub(color.r, color.g, color.b, color.a); rlColor4ub(color.r, color.g, color.b, color.a);
@ -263,7 +263,7 @@ void DrawRectangle(int posX, int posY, int width, int height, Color color)
void DrawRectangleV(Vector2 position, Vector2 size, Color color) void DrawRectangleV(Vector2 position, Vector2 size, Color color)
{ {
Color colors[4] = { color, color, color, color }; Color colors[4] = { color, color, color, color };
DrawRectanglePro((Rectangle){ position.x, position.y, size.x, size.y }, (Vector2){ 0.0f, 0.0f }, 0.0f, colors); DrawRectanglePro((Rectangle){ position.x, position.y, size.x, size.y }, (Vector2){ 0.0f, 0.0f }, 0.0f, colors);
} }
@ -271,7 +271,7 @@ void DrawRectangleV(Vector2 position, Vector2 size, Color color)
void DrawRectangleRec(Rectangle rec, Color color) void DrawRectangleRec(Rectangle rec, Color color)
{ {
Color colors[4] = { color, color, color, color }; Color colors[4] = { color, color, color, color };
DrawRectanglePro(rec, (Vector2){ 0.0f, 0.0f }, 0.0f, colors); DrawRectanglePro(rec, (Vector2){ 0.0f, 0.0f }, 0.0f, colors);
} }
@ -292,15 +292,15 @@ void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color
rlColor4ub(colors[0].r, colors[0].g, colors[0].b, colors[0].a); rlColor4ub(colors[0].r, colors[0].g, colors[0].b, colors[0].a);
rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height); rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
rlVertex2f(rec.x, rec.y); rlVertex2f(rec.x, rec.y);
rlColor4ub(colors[1].r, colors[1].g, colors[1].b, colors[1].a); rlColor4ub(colors[1].r, colors[1].g, colors[1].b, colors[1].a);
rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height); rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
rlVertex2f(rec.x, rec.y + rec.height); rlVertex2f(rec.x, rec.y + rec.height);
rlColor4ub(colors[2].r, colors[2].g, colors[2].b, colors[2].a); rlColor4ub(colors[2].r, colors[2].g, colors[2].b, colors[2].a);
rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height); rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
rlVertex2f(rec.x + rec.width, rec.y + rec.height); rlVertex2f(rec.x + rec.width, rec.y + rec.height);
rlColor4ub(colors[3].r, colors[3].g, colors[3].b, colors[3].a); rlColor4ub(colors[3].r, colors[3].g, colors[3].b, colors[3].a);
rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height); rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height);
rlVertex2f(rec.x + rec.width, rec.y); rlVertex2f(rec.x + rec.width, rec.y);
@ -329,14 +329,14 @@ void DrawRectangleGradientH(int posX, int posY, int width, int height, Color col
void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4) void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4)
{ {
Color colors[4] = { col1, col2, col3, col4 }; Color colors[4] = { col1, col2, col3, col4 };
DrawRectanglePro(rec, (Vector2){ 0.0f, 0.0f }, 0.0f, colors); DrawRectanglePro(rec, (Vector2){ 0.0f, 0.0f }, 0.0f, colors);
} }
// Draw rectangle outline // Draw rectangle outline
// NOTE: On OpenGL 3.3 and ES2 we use QUADS to avoid drawing order issues (view rlglDraw) // NOTE: On OpenGL 3.3 and ES2 we use QUADS to avoid drawing order issues (view rlglDraw)
void DrawRectangleLines(int posX, int posY, int width, int height, Color color) void DrawRectangleLines(int posX, int posY, int width, int height, Color color)
{ {
#if defined(SUPPORT_QUADS_DRAW_MODE) #if defined(SUPPORT_QUADS_DRAW_MODE)
DrawRectangle(posX, posY, width, 1, color); DrawRectangle(posX, posY, width, 1, color);
DrawRectangle(posX + width - 1, posY + 1, 1, height - 2, color); DrawRectangle(posX + width - 1, posY + 1, 1, height - 2, color);
@ -362,13 +362,13 @@ void DrawRectangleLines(int posX, int posY, int width, int height, Color color)
// Draw rectangle outline with extended parameters // Draw rectangle outline with extended parameters
void DrawRectangleLinesEx(Rectangle rec, int lineThick, Color color) void DrawRectangleLinesEx(Rectangle rec, int lineThick, Color color)
{ {
if (lineThick > rec.width || lineThick > rec.height) if (lineThick > rec.width || lineThick > rec.height)
{ {
if(rec.width > rec.height) lineThick = (int)rec.height/2; if(rec.width > rec.height) lineThick = (int)rec.height/2;
else if (rec.width < rec.height) lineThick = (int)rec.width/2; else if (rec.width < rec.height) lineThick = (int)rec.width/2;
} }
DrawRectangle( (int)rec.x, (int)rec.y, (int)rec.width, lineThick, color); DrawRectangle( (int)rec.x, (int)rec.y, (int)rec.width, lineThick, color);
DrawRectangle( (int)(rec.x - lineThick + rec.width), (int)(rec.y + lineThick), lineThick, (int)(rec.height - lineThick*2.0f), color); DrawRectangle( (int)(rec.x - lineThick + rec.width), (int)(rec.y + lineThick), lineThick, (int)(rec.height - lineThick*2.0f), color);
DrawRectangle( (int)rec.x, (int)(rec.y + rec.height - lineThick), (int)rec.width, lineThick, color); DrawRectangle( (int)rec.x, (int)(rec.y + rec.height - lineThick), (int)rec.width, lineThick, color);
@ -383,20 +383,20 @@ void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color)
rlBegin(RL_QUADS); rlBegin(RL_QUADS);
rlColor4ub(color.r, color.g, color.b, color.a); rlColor4ub(color.r, color.g, color.b, color.a);
rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height); rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
rlVertex2f(v1.x, v1.y); rlVertex2f(v1.x, v1.y);
rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height); rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
rlVertex2f(v2.x, v2.y); rlVertex2f(v2.x, v2.y);
rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height); rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
rlVertex2f(v2.x, v2.y); rlVertex2f(v2.x, v2.y);
rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height); rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height);
rlVertex2f(v3.x, v3.y); rlVertex2f(v3.x, v3.y);
rlEnd(); rlEnd();
rlDisableTexture(); rlDisableTexture();
#else #else
rlBegin(RL_TRIANGLES); rlBegin(RL_TRIANGLES);
@ -428,13 +428,13 @@ void DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, Color color)
void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color color) void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color color)
{ {
if (sides < 3) sides = 3; if (sides < 3) sides = 3;
if (rlCheckBufferLimit(RL_QUADS, 4*(360/sides))) rlglDraw(); if (rlCheckBufferLimit(RL_QUADS, 4*(360/sides))) rlglDraw();
rlPushMatrix(); rlPushMatrix();
rlTranslatef(center.x, center.y, 0.0); rlTranslatef(center.x, center.y, 0.0);
rlRotatef(rotation, 0, 0, 1); rlRotatef(rotation, 0, 0, 1);
#if defined(SUPPORT_QUADS_DRAW_MODE) #if defined(SUPPORT_QUADS_DRAW_MODE)
rlEnableTexture(GetShapesTexture().id); rlEnableTexture(GetShapesTexture().id);
@ -445,13 +445,13 @@ void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color col
rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height); rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
rlVertex2f(0, 0); rlVertex2f(0, 0);
rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height); rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
rlVertex2f(sinf(DEG2RAD*i)*radius, cosf(DEG2RAD*i)*radius); rlVertex2f(sinf(DEG2RAD*i)*radius, cosf(DEG2RAD*i)*radius);
rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height); rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
rlVertex2f(sinf(DEG2RAD*i)*radius, cosf(DEG2RAD*i)*radius); rlVertex2f(sinf(DEG2RAD*i)*radius, cosf(DEG2RAD*i)*radius);
rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height); rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height);
rlVertex2f(sinf(DEG2RAD*(i + 360/sides))*radius, cosf(DEG2RAD*(i + 360/sides))*radius); rlVertex2f(sinf(DEG2RAD*(i + 360/sides))*radius, cosf(DEG2RAD*(i + 360/sides))*radius);
} }
@ -478,7 +478,7 @@ void DrawPolyEx(Vector2 *points, int pointsCount, Color color)
if (pointsCount >= 3) if (pointsCount >= 3)
{ {
if (rlCheckBufferLimit(RL_QUADS, pointsCount)) rlglDraw(); if (rlCheckBufferLimit(RL_QUADS, pointsCount)) rlglDraw();
#if defined(SUPPORT_QUADS_DRAW_MODE) #if defined(SUPPORT_QUADS_DRAW_MODE)
rlEnableTexture(GetShapesTexture().id); rlEnableTexture(GetShapesTexture().id);
@ -577,7 +577,7 @@ bool CheckCollisionPointTriangle(Vector2 point, Vector2 p1, Vector2 p2, Vector2
bool CheckCollisionRecs(Rectangle rec1, Rectangle rec2) bool CheckCollisionRecs(Rectangle rec1, Rectangle rec2)
{ {
bool collision = false; bool collision = false;
if ((rec1.x <= (rec2.x + rec2.width) && (rec1.x + rec1.width) >= rec2.x) && if ((rec1.x <= (rec2.x + rec2.width) && (rec1.x + rec1.width) >= rec2.x) &&
(rec1.y <= (rec2.y + rec2.height) && (rec1.y + rec1.height) >= rec2.y)) collision = true; (rec1.y <= (rec2.y + rec2.height) && (rec1.y + rec1.height) >= rec2.y)) collision = true;
@ -605,7 +605,7 @@ bool CheckCollisionCircleRec(Vector2 center, float radius, Rectangle rec)
{ {
int recCenterX = (int)(rec.x + rec.width/2.0f); int recCenterX = (int)(rec.x + rec.width/2.0f);
int recCenterY = (int)(rec.y + rec.height/2.0f); int recCenterY = (int)(rec.y + rec.height/2.0f);
float dx = (float)fabs(center.x - recCenterX); float dx = (float)fabs(center.x - recCenterX);
float dy = (float)fabs(center.y - recCenterY); float dy = (float)fabs(center.y - recCenterY);
@ -615,7 +615,7 @@ bool CheckCollisionCircleRec(Vector2 center, float radius, Rectangle rec)
if (dx <= (rec.width/2.0f)) { return true; } if (dx <= (rec.width/2.0f)) { return true; }
if (dy <= (rec.height/2.0f)) { return true; } if (dy <= (rec.height/2.0f)) { return true; }
float cornerDistanceSq = (dx - rec.width/2.0f)*(dx - rec.width/2.0f) + float cornerDistanceSq = (dx - rec.width/2.0f)*(dx - rec.width/2.0f) +
(dy - rec.height/2.0f)*(dy - rec.height/2.0f); (dy - rec.height/2.0f)*(dy - rec.height/2.0f);
return (cornerDistanceSq <= (radius*radius)); return (cornerDistanceSq <= (radius*radius));
@ -674,7 +674,7 @@ Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2)
{ {
if (retRec.width >= rec1.width) retRec.width = rec1.width; if (retRec.width >= rec1.width) retRec.width = rec1.width;
} }
if (rec1.height > rec2.height) if (rec1.height > rec2.height)
{ {
if (retRec.height >= rec2.height) retRec.height = rec2.height; if (retRec.height >= rec2.height) retRec.height = rec2.height;
@ -692,10 +692,10 @@ Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2)
// Module specific Functions Definition // Module specific Functions Definition
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Cubic easing in-out // Cubic easing in-out
// NOTE: Required for DrawLineBezier() // NOTE: Required for DrawLineBezier()
static float EaseCubicInOut(float t, float b, float c, float d) static float EaseCubicInOut(float t, float b, float c, float d)
{ {
if ((t /= 0.5f*d) < 1) if ((t /= 0.5f*d) < 1)
return 0.5f*c*t*t*t + b; return 0.5f*c*t*t*t + b;
t -= 2; t -= 2;
@ -715,6 +715,6 @@ static Texture2D GetShapesTexture(void)
recTexShapes = { 0.0f, 0.0f, 1.0f, 1.0f }; recTexShapes = { 0.0f, 0.0f, 1.0f, 1.0f };
#endif #endif
} }
return texShapes; return texShapes;
} }

View file

@ -200,10 +200,10 @@ extern void LoadDefaultFont(void)
// Reconstruct charSet using charsWidth[], charsHeight, charsDivisor, charsCount // Reconstruct charSet using charsWidth[], charsHeight, charsDivisor, charsCount
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Allocate space for our characters info data // Allocate space for our characters info data
// NOTE: This memory should be freed at end! --> CloseWindow() // NOTE: This memory should be freed at end! --> CloseWindow()
defaultFont.chars = (CharInfo *)malloc(defaultFont.charsCount*sizeof(CharInfo)); defaultFont.chars = (CharInfo *)malloc(defaultFont.charsCount*sizeof(CharInfo));
int currentLine = 0; int currentLine = 0;
int currentPosX = charsDivisor; int currentPosX = charsDivisor;
@ -238,7 +238,7 @@ extern void LoadDefaultFont(void)
} }
defaultFont.baseSize = (int)defaultFont.chars[0].rec.height; defaultFont.baseSize = (int)defaultFont.chars[0].rec.height;
TraceLog(LOG_INFO, "[TEX ID %i] Default font loaded successfully", defaultFont.texture.id); TraceLog(LOG_INFO, "[TEX ID %i] Default font loaded successfully", defaultFont.texture.id);
} }
@ -258,7 +258,7 @@ Font GetFontDefault()
#else #else
Font font = { 0 }; Font font = { 0 };
return font; return font;
#endif #endif
} }
// Load Font from file into GPU memory (VRAM) // Load Font from file into GPU memory (VRAM)
@ -301,11 +301,11 @@ Font LoadFont(const char *fileName)
Font LoadFontEx(const char *fileName, int fontSize, int charsCount, int *fontChars) Font LoadFontEx(const char *fileName, int fontSize, int charsCount, int *fontChars)
{ {
Font font = { 0 }; Font font = { 0 };
font.baseSize = fontSize; font.baseSize = fontSize;
font.charsCount = (charsCount > 0) ? charsCount : 95; font.charsCount = (charsCount > 0) ? charsCount : 95;
font.chars = LoadFontData(fileName, font.baseSize, fontChars, font.charsCount, FONT_DEFAULT); font.chars = LoadFontData(fileName, font.baseSize, fontChars, font.charsCount, FONT_DEFAULT);
if (font.chars != NULL) if (font.chars != NULL)
{ {
Image atlas = GenImageFontAtlas(font.chars, font.charsCount, font.baseSize, 2, 0); Image atlas = GenImageFontAtlas(font.chars, font.charsCount, font.baseSize, 2, 0);
@ -313,7 +313,7 @@ Font LoadFontEx(const char *fileName, int fontSize, int charsCount, int *fontCha
UnloadImage(atlas); UnloadImage(atlas);
} }
else font = GetFontDefault(); else font = GetFontDefault();
return font; return font;
} }
@ -326,7 +326,7 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c
#define SDF_CHAR_PADDING 4 #define SDF_CHAR_PADDING 4
#define SDF_ON_EDGE_VALUE 128 #define SDF_ON_EDGE_VALUE 128
#define SDF_PIXEL_DIST_SCALE 64.0f #define SDF_PIXEL_DIST_SCALE 64.0f
#define BITMAP_ALPHA_THRESHOLD 80 #define BITMAP_ALPHA_THRESHOLD 80
CharInfo *chars = NULL; CharInfo *chars = NULL;
@ -335,15 +335,15 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c
// NOTE: Loaded information should be enough to generate font image atlas, // NOTE: Loaded information should be enough to generate font image atlas,
// using any packaging method // using any packaging method
FILE *fontFile = fopen(fileName, "rb"); // Load font file FILE *fontFile = fopen(fileName, "rb"); // Load font file
if (fontFile != NULL) if (fontFile != NULL)
{ {
fseek(fontFile, 0, SEEK_END); fseek(fontFile, 0, SEEK_END);
long size = ftell(fontFile); // Get file size long size = ftell(fontFile); // Get file size
fseek(fontFile, 0, SEEK_SET); // Reset file pointer fseek(fontFile, 0, SEEK_SET); // Reset file pointer
unsigned char *fontBuffer = (unsigned char *)malloc(size); unsigned char *fontBuffer = (unsigned char *)malloc(size);
fread(fontBuffer, size, 1, fontFile); fread(fontBuffer, size, 1, fontFile);
fclose(fontFile); fclose(fontFile);
@ -358,10 +358,10 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c
// NOTE: ascent is equivalent to font baseline // NOTE: ascent is equivalent to font baseline
int ascent, descent, lineGap; int ascent, descent, lineGap;
stbtt_GetFontVMetrics(&fontInfo, &ascent, &descent, &lineGap); stbtt_GetFontVMetrics(&fontInfo, &ascent, &descent, &lineGap);
// In case no chars count provided, default to 95 // In case no chars count provided, default to 95
charsCount = (charsCount > 0) ? charsCount : 95; charsCount = (charsCount > 0) ? charsCount : 95;
// Fill fontChars in case not provided externally // Fill fontChars in case not provided externally
// NOTE: By default we fill charsCount consecutevely, starting at 32 (Space) // NOTE: By default we fill charsCount consecutevely, starting at 32 (Space)
int genFontChars = false; int genFontChars = false;
@ -373,22 +373,22 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c
} }
chars = (CharInfo *)malloc(charsCount*sizeof(CharInfo)); chars = (CharInfo *)malloc(charsCount*sizeof(CharInfo));
// NOTE: Using simple packaging, one char after another // NOTE: Using simple packaging, one char after another
for (int i = 0; i < charsCount; i++) for (int i = 0; i < charsCount; i++)
{ {
int chw = 0, chh = 0; // Character width and height (on generation) int chw = 0, chh = 0; // Character width and height (on generation)
int ch = fontChars[i]; // Character value to get info for int ch = fontChars[i]; // Character value to get info for
chars[i].value = ch; chars[i].value = ch;
// Render a unicode codepoint to a bitmap // Render a unicode codepoint to a bitmap
// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap // stbtt_GetCodepointBitmap() -- allocates and returns a bitmap
// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be // stbtt_GetCodepointBitmapBox() -- how big the bitmap must be
// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide // 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); 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); 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 (type == FONT_BITMAP)
{ {
// Aliased bitmap (black & white) font generation, avoiding anti-aliasing // Aliased bitmap (black & white) font generation, avoiding anti-aliasing
@ -399,15 +399,15 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c
else chars[i].data[p] = 255; else chars[i].data[p] = 255;
} }
} }
chars[i].rec.width = (float)chw; chars[i].rec.width = (float)chw;
chars[i].rec.height = (float)chh; chars[i].rec.height = (float)chh;
chars[i].offsetY += (int)((float)ascent*scaleFactor); 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) // Get bounding box for character (may be offset to account for chars that dip above or below the line)
int chX1, chY1, chX2, chY2; int chX1, chY1, chX2, chY2;
stbtt_GetCodepointBitmapBox(&fontInfo, ch, scaleFactor, scaleFactor, &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 box measures: %i, %i, %i, %i", chX1, chY1, chX2 - chX1, chY2 - chY1);
TraceLog(LOG_DEBUG, "Character offsetY: %i", (int)((float)ascent*scaleFactor) + chY1); TraceLog(LOG_DEBUG, "Character offsetY: %i", (int)((float)ascent*scaleFactor) + chY1);
@ -419,7 +419,7 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c
if (genFontChars) free(fontChars); if (genFontChars) free(fontChars);
} }
else TraceLog(LOG_WARNING, "[%s] TTF file could not be opened", fileName); else TraceLog(LOG_WARNING, "[%s] TTF file could not be opened", fileName);
return chars; return chars;
} }
@ -428,25 +428,25 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c
Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int padding, int packMethod) Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int padding, int packMethod)
{ {
Image atlas = { 0 }; Image atlas = { 0 };
// In case no chars count provided we suppose default of 95 // In case no chars count provided we suppose default of 95
charsCount = (charsCount > 0) ? charsCount : 95; charsCount = (charsCount > 0) ? charsCount : 95;
// Calculate image size based on required pixel area // Calculate image size based on required pixel area
// NOTE 1: Image is forced to be squared and POT... very conservative! // NOTE 1: Image is forced to be squared and POT... very conservative!
// NOTE 2: SDF font characters already contain an internal padding, // NOTE 2: SDF font characters already contain an internal padding,
// so image size would result bigger than default font type // so image size would result bigger than default font type
float requiredArea = 0; float requiredArea = 0;
for (int i = 0; i < charsCount; i++) requiredArea += ((chars[i].rec.width + 2*padding)*(chars[i].rec.height + 2*padding)); for (int i = 0; i < charsCount; i++) requiredArea += ((chars[i].rec.width + 2*padding)*(chars[i].rec.height + 2*padding));
float guessSize = sqrtf(requiredArea)*1.25f; float guessSize = sqrtf(requiredArea)*1.25f;
int imageSize = (int)powf(2, ceilf(logf((float)guessSize)/logf(2))); // Calculate next POT int imageSize = (int)powf(2, ceilf(logf((float)guessSize)/logf(2))); // Calculate next POT
atlas.width = imageSize; // Atlas bitmap width atlas.width = imageSize; // Atlas bitmap width
atlas.height = imageSize; // Atlas bitmap height atlas.height = imageSize; // Atlas bitmap height
atlas.data = (unsigned char *)calloc(1, atlas.width*atlas.height); // Create a bitmap to store characters (8 bpp) atlas.data = (unsigned char *)calloc(1, atlas.width*atlas.height); // Create a bitmap to store characters (8 bpp)
atlas.format = UNCOMPRESSED_GRAYSCALE; atlas.format = UNCOMPRESSED_GRAYSCALE;
atlas.mipmaps = 1; atlas.mipmaps = 1;
// DEBUG: We can see padding in the generated image setting a gray background... // DEBUG: We can see padding in the generated image setting a gray background...
//for (int i = 0; i < atlas.width*atlas.height; i++) ((unsigned char *)atlas.data)[i] = 100; //for (int i = 0; i < atlas.width*atlas.height; i++) ((unsigned char *)atlas.data)[i] = 100;
@ -454,9 +454,9 @@ Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int paddi
{ {
int offsetX = padding; int offsetX = padding;
int offsetY = padding; int offsetY = padding;
// NOTE: Using simple packaging, one char after another // NOTE: Using simple packaging, one char after another
for (int i = 0; i < charsCount; i++) for (int i = 0; i < charsCount; i++)
{ {
// Copy pixel data from fc.data to atlas // Copy pixel data from fc.data to atlas
for (int y = 0; y < (int)chars[i].rec.height; y++) for (int y = 0; y < (int)chars[i].rec.height; y++)
@ -466,22 +466,22 @@ Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int paddi
((unsigned char *)atlas.data)[(offsetY + y)*atlas.width + (offsetX + x)] = chars[i].data[y*(int)chars[i].rec.width + x]; ((unsigned char *)atlas.data)[(offsetY + y)*atlas.width + (offsetX + x)] = chars[i].data[y*(int)chars[i].rec.width + x];
} }
} }
chars[i].rec.x = (float)offsetX; chars[i].rec.x = (float)offsetX;
chars[i].rec.y = (float)offsetY; chars[i].rec.y = (float)offsetY;
// Move atlas position X for next character drawing // Move atlas position X for next character drawing
offsetX += ((int)chars[i].rec.width + 2*padding); offsetX += ((int)chars[i].rec.width + 2*padding);
if (offsetX >= (atlas.width - (int)chars[i].rec.width - padding)) if (offsetX >= (atlas.width - (int)chars[i].rec.width - padding))
{ {
offsetX = padding; offsetX = padding;
// NOTE: Be careful on offsetY for SDF fonts, by default SDF // NOTE: Be careful on offsetY for SDF fonts, by default SDF
// use an internal padding of 4 pixels, it means char rectangle // use an internal padding of 4 pixels, it means char rectangle
// height is bigger than fontSize, it could be up to (fontSize + 8) // height is bigger than fontSize, it could be up to (fontSize + 8)
offsetY += (fontSize + 2*padding); offsetY += (fontSize + 2*padding);
if (offsetY > (atlas.height - fontSize - padding)) break; if (offsetY > (atlas.height - fontSize - padding)) break;
} }
} }
@ -489,13 +489,13 @@ Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int paddi
else if (packMethod == 1) // Use Skyline rect packing algorythm (stb_pack_rect) else if (packMethod == 1) // Use Skyline rect packing algorythm (stb_pack_rect)
{ {
TraceLog(LOG_DEBUG, "Using Skyline packing algorythm!"); TraceLog(LOG_DEBUG, "Using Skyline packing algorythm!");
stbrp_context *context = (stbrp_context *)malloc(sizeof(*context)); stbrp_context *context = (stbrp_context *)malloc(sizeof(*context));
stbrp_node *nodes = (stbrp_node *)malloc(charsCount*sizeof(*nodes)); stbrp_node *nodes = (stbrp_node *)malloc(charsCount*sizeof(*nodes));
stbrp_init_target(context, atlas.width, atlas.height, nodes, charsCount); stbrp_init_target(context, atlas.width, atlas.height, nodes, charsCount);
stbrp_rect *rects = (stbrp_rect *)malloc(charsCount*sizeof(stbrp_rect)); stbrp_rect *rects = (stbrp_rect *)malloc(charsCount*sizeof(stbrp_rect));
// Fill rectangles for packaging // Fill rectangles for packaging
for (int i = 0; i < charsCount; i++) for (int i = 0; i < charsCount; i++)
{ {
@ -506,12 +506,12 @@ Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int paddi
// Package rectangles into atlas // Package rectangles into atlas
stbrp_pack_rects(context, rects, charsCount); stbrp_pack_rects(context, rects, charsCount);
for (int i = 0; i < charsCount; i++) for (int i = 0; i < charsCount; i++)
{ {
chars[i].rec.x = rects[i].x + (float)padding; chars[i].rec.x = rects[i].x + (float)padding;
chars[i].rec.y = rects[i].y + (float)padding; chars[i].rec.y = rects[i].y + (float)padding;
if (rects[i].was_packed) if (rects[i].was_packed)
{ {
// Copy pixel data from fc.data to atlas // Copy pixel data from fc.data to atlas
@ -530,9 +530,9 @@ Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int paddi
free(nodes); free(nodes);
free(context); free(context);
} }
// TODO: Crop image if required for smaller size // TODO: Crop image if required for smaller size
// Convert image data from GRAYSCALE to GRAY_ALPHA // Convert image data from GRAYSCALE to GRAY_ALPHA
// WARNING: ImageAlphaMask(&atlas, atlas) does not work in this case, requires manual operation // WARNING: ImageAlphaMask(&atlas, atlas) does not work in this case, requires manual operation
unsigned char *dataGrayAlpha = (unsigned char *)malloc(imageSize*imageSize*sizeof(unsigned char)*2); // Two channels unsigned char *dataGrayAlpha = (unsigned char *)malloc(imageSize*imageSize*sizeof(unsigned char)*2); // Two channels
@ -546,7 +546,7 @@ Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int paddi
free(atlas.data); free(atlas.data);
atlas.data = dataGrayAlpha; atlas.data = dataGrayAlpha;
atlas.format = UNCOMPRESSED_GRAY_ALPHA; atlas.format = UNCOMPRESSED_GRAY_ALPHA;
return atlas; return atlas;
} }
@ -580,7 +580,7 @@ void DrawFPS(int posX, int posY)
refreshRate = fps; refreshRate = fps;
counter = 0; counter = 0;
} }
// NOTE: We have rounding errors every frame, so it oscillates a lot // NOTE: We have rounding errors every frame, so it oscillates a lot
DrawText(FormatText("%2i FPS", fps), posX, posY, 20, LIME); DrawText(FormatText("%2i FPS", fps), posX, posY, 20, LIME);
} }
@ -645,7 +645,7 @@ void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, f
i++; i++;
} }
else index = GetGlyphIndex(font, (unsigned char)text[i]); else index = GetGlyphIndex(font, (unsigned char)text[i]);
if ((unsigned char)text[i] != ' ') if ((unsigned char)text[i] != ' ')
{ {
DrawTexturePro(font.texture, font.chars[index].rec, DrawTexturePro(font.texture, font.chars[index].rec,
@ -795,27 +795,27 @@ char **SplitText(char *text, char delimiter, int *strCount)
char *strDup = (char *)malloc(len + 1); char *strDup = (char *)malloc(len + 1);
strcpy(strDup, text); strcpy(strDup, text);
int counter = 1; int counter = 1;
// Count how many substrings we have on string // Count how many substrings we have on string
for (int i = 0; i < len; i++) if (text[i] == delimiter) counter++; for (int i = 0; i < len; i++) if (text[i] == delimiter) counter++;
// Memory allocation for substrings // Memory allocation for substrings
strings = (char **)malloc(sizeof(char *)*counter); strings = (char **)malloc(sizeof(char *)*counter);
for (int i = 0; i < counter; i++) strings[i] = (char *)malloc(sizeof(char)*MAX_SUBSTRING_LENGTH); for (int i = 0; i < counter; i++) strings[i] = (char *)malloc(sizeof(char)*MAX_SUBSTRING_LENGTH);
char *substrPtr = NULL; char *substrPtr = NULL;
char delimiters[1] = { delimiter }; // Only caring for one delimiter char delimiters[1] = { delimiter }; // Only caring for one delimiter
substrPtr = strtok(strDup, delimiters); substrPtr = strtok(strDup, delimiters);
for (int i = 0; (i < counter) && (substrPtr != NULL); i++) for (int i = 0; (i < counter) && (substrPtr != NULL); i++)
{ {
strcpy(strings[i], substrPtr); strcpy(strings[i], substrPtr);
substrPtr = strtok(NULL, delimiters); substrPtr = strtok(NULL, delimiters);
} }
*strCount = counter; *strCount = counter;
free(strDup); free(strDup);
return strings; return strings;
} }
@ -823,9 +823,9 @@ char **SplitText(char *text, char delimiter, int *strCount)
bool IsEqualText(const char *text1, const char *text2) bool IsEqualText(const char *text1, const char *text2)
{ {
bool result = false; bool result = false;
if (strcmp(text1, text2) == 0) result = true; if (strcmp(text1, text2) == 0) result = true;
return result; return result;
} }
@ -861,7 +861,7 @@ static Font LoadImageFont(Image image, Color key, int firstChar)
{ {
if (!COLOR_EQUAL(pixels[y*image.width + x], key)) break; if (!COLOR_EQUAL(pixels[y*image.width + x], key)) break;
} }
if (!COLOR_EQUAL(pixels[y*image.width + x], key)) break; if (!COLOR_EQUAL(pixels[y*image.width + x], key)) break;
} }
@ -1031,10 +1031,10 @@ static Font LoadBMFont(const char *fileName)
UnloadImage(imCopy); UnloadImage(imCopy);
} }
else font.texture = LoadTextureFromImage(imFont); else font.texture = LoadTextureFromImage(imFont);
UnloadImage(imFont); UnloadImage(imFont);
free(texPath); free(texPath);
// Fill font characters info data // Fill font characters info data
font.baseSize = fontSize; font.baseSize = fontSize;

View file

@ -424,7 +424,7 @@ Color *GetImageData(Image image)
if ((image.format == UNCOMPRESSED_R32) || if ((image.format == UNCOMPRESSED_R32) ||
(image.format == UNCOMPRESSED_R32G32B32) || (image.format == UNCOMPRESSED_R32G32B32) ||
(image.format == UNCOMPRESSED_R32G32B32A32)) TraceLog(LOG_WARNING, "32bit pixel format converted to 8bit per channel"); (image.format == UNCOMPRESSED_R32G32B32A32)) TraceLog(LOG_WARNING, "32bit pixel format converted to 8bit per channel");
for (int i = 0, k = 0; i < image.width*image.height; i++) for (int i = 0, k = 0; i < image.width*image.height; i++)
{ {
switch (image.format) switch (image.format)
@ -500,7 +500,7 @@ Color *GetImageData(Image image)
pixels[i].g = 0; pixels[i].g = 0;
pixels[i].b = 0; pixels[i].b = 0;
pixels[i].a = 255; pixels[i].a = 255;
} break; } break;
case UNCOMPRESSED_R32G32B32: case UNCOMPRESSED_R32G32B32:
{ {
@ -508,7 +508,7 @@ Color *GetImageData(Image image)
pixels[i].g = (unsigned char)(((float *)image.data)[k + 1]*255.0f); pixels[i].g = (unsigned char)(((float *)image.data)[k + 1]*255.0f);
pixels[i].b = (unsigned char)(((float *)image.data)[k + 2]*255.0f); pixels[i].b = (unsigned char)(((float *)image.data)[k + 2]*255.0f);
pixels[i].a = 255; pixels[i].a = 255;
k += 3; k += 3;
} }
case UNCOMPRESSED_R32G32B32A32: case UNCOMPRESSED_R32G32B32A32:
@ -517,7 +517,7 @@ Color *GetImageData(Image image)
pixels[i].g = (unsigned char)(((float *)image.data)[k]*255.0f); pixels[i].g = (unsigned char)(((float *)image.data)[k]*255.0f);
pixels[i].b = (unsigned char)(((float *)image.data)[k]*255.0f); pixels[i].b = (unsigned char)(((float *)image.data)[k]*255.0f);
pixels[i].a = (unsigned char)(((float *)image.data)[k]*255.0f); pixels[i].a = (unsigned char)(((float *)image.data)[k]*255.0f);
k += 4; k += 4;
} }
default: break; default: break;
@ -532,7 +532,7 @@ Color *GetImageData(Image image)
Vector4 *GetImageDataNormalized(Image image) Vector4 *GetImageDataNormalized(Image image)
{ {
Vector4 *pixels = (Vector4 *)malloc(image.width*image.height*sizeof(Vector4)); Vector4 *pixels = (Vector4 *)malloc(image.width*image.height*sizeof(Vector4));
if (image.format >= COMPRESSED_DXT1_RGB) TraceLog(LOG_WARNING, "Pixel data retrieval not supported for compressed image formats"); if (image.format >= COMPRESSED_DXT1_RGB) TraceLog(LOG_WARNING, "Pixel data retrieval not supported for compressed image formats");
else else
{ {
@ -611,7 +611,7 @@ Vector4 *GetImageDataNormalized(Image image)
pixels[i].y = 0.0f; pixels[i].y = 0.0f;
pixels[i].z = 0.0f; pixels[i].z = 0.0f;
pixels[i].w = 1.0f; pixels[i].w = 1.0f;
} break; } break;
case UNCOMPRESSED_R32G32B32: case UNCOMPRESSED_R32G32B32:
{ {
@ -619,7 +619,7 @@ Vector4 *GetImageDataNormalized(Image image)
pixels[i].y = ((float *)image.data)[k + 1]; pixels[i].y = ((float *)image.data)[k + 1];
pixels[i].z = ((float *)image.data)[k + 2]; pixels[i].z = ((float *)image.data)[k + 2];
pixels[i].w = 1.0f; pixels[i].w = 1.0f;
k += 3; k += 3;
} }
case UNCOMPRESSED_R32G32B32A32: case UNCOMPRESSED_R32G32B32A32:
@ -628,14 +628,14 @@ Vector4 *GetImageDataNormalized(Image image)
pixels[i].y = ((float *)image.data)[k + 1]; pixels[i].y = ((float *)image.data)[k + 1];
pixels[i].z = ((float *)image.data)[k + 2]; pixels[i].z = ((float *)image.data)[k + 2];
pixels[i].w = ((float *)image.data)[k + 3]; pixels[i].w = ((float *)image.data)[k + 3];
k += 4; k += 4;
} }
default: break; default: break;
} }
} }
} }
return pixels; return pixels;
} }
@ -720,16 +720,16 @@ void UpdateTexture(Texture2D texture, const void *pixels)
void ExportImage(Image image, const char *fileName) void ExportImage(Image image, const char *fileName)
{ {
int success = 0; int success = 0;
// NOTE: Getting Color array as RGBA unsigned char values // NOTE: Getting Color array as RGBA unsigned char values
unsigned char *imgData = (unsigned char *)GetImageData(image); unsigned char *imgData = (unsigned char *)GetImageData(image);
if (IsFileExtension(fileName, ".png")) success = stbi_write_png(fileName, image.width, image.height, 4, imgData, image.width*4); 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, ".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, ".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); // JPG quality: 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")) success = SaveKTX(image, fileName); else if (IsFileExtension(fileName, ".ktx")) success = SaveKTX(image, fileName);
else if (IsFileExtension(fileName, ".raw")) else if (IsFileExtension(fileName, ".raw"))
{ {
// Export raw pixel data (without header) // Export raw pixel data (without header)
// NOTE: It's up to the user to track image parameters // NOTE: It's up to the user to track image parameters
@ -740,7 +740,7 @@ void ExportImage(Image image, const char *fileName)
if (success != 0) TraceLog(LOG_INFO, "Image exported successfully: %s", fileName); if (success != 0) TraceLog(LOG_INFO, "Image exported successfully: %s", fileName);
else TraceLog(LOG_WARNING, "Image could not be exported."); else TraceLog(LOG_WARNING, "Image could not be exported.");
free(imgData); free(imgData);
} }
@ -748,10 +748,10 @@ void ExportImage(Image image, const char *fileName)
void ExportImageAsCode(Image image, const char *fileName) void ExportImageAsCode(Image image, const char *fileName)
{ {
#define BYTES_TEXT_PER_LINE 20 #define BYTES_TEXT_PER_LINE 20
char varFileName[256] = { 0 }; char varFileName[256] = { 0 };
int dataSize = GetPixelDataSize(image.width, image.height, image.format); int dataSize = GetPixelDataSize(image.width, image.height, image.format);
FILE *txtFile = fopen(fileName, "wt"); FILE *txtFile = fopen(fileName, "wt");
fprintf(txtFile, "\n//////////////////////////////////////////////////////////////////////////////////////\n"); fprintf(txtFile, "\n//////////////////////////////////////////////////////////////////////////////////////\n");
@ -764,11 +764,11 @@ void ExportImageAsCode(Image image, const char *fileName)
fprintf(txtFile, "// Copyright (c) 2018 Ramon Santamaria (@raysan5) //\n"); fprintf(txtFile, "// Copyright (c) 2018 Ramon Santamaria (@raysan5) //\n");
fprintf(txtFile, "// //\n"); fprintf(txtFile, "// //\n");
fprintf(txtFile, "////////////////////////////////////////////////////////////////////////////////////////\n\n"); fprintf(txtFile, "////////////////////////////////////////////////////////////////////////////////////////\n\n");
// Get file name from path and convert variable name to uppercase // Get file name from path and convert variable name to uppercase
strcpy(varFileName, GetFileNameWithoutExt(fileName)); strcpy(varFileName, GetFileNameWithoutExt(fileName));
for (int i = 0; varFileName[i] != '\0'; i++) if (varFileName[i] >= 'a' && varFileName[i] <= 'z') { varFileName[i] = varFileName[i] - 32; } for (int i = 0; varFileName[i] != '\0'; i++) if (varFileName[i] >= 'a' && varFileName[i] <= 'z') { varFileName[i] = varFileName[i] - 32; }
// Add image information // Add image information
fprintf(txtFile, "// Image data information\n"); fprintf(txtFile, "// Image data information\n");
fprintf(txtFile, "#define %s_WIDTH %i\n", varFileName, image.width); fprintf(txtFile, "#define %s_WIDTH %i\n", varFileName, image.width);
@ -987,7 +987,7 @@ void ImageFormat(Image *image, int newFormat)
case UNCOMPRESSED_R32: case UNCOMPRESSED_R32:
{ {
// WARNING: Image is converted to GRAYSCALE eqeuivalent 32bit // WARNING: Image is converted to GRAYSCALE eqeuivalent 32bit
image->data = (float *)malloc(image->width*image->height*sizeof(float)); image->data = (float *)malloc(image->width*image->height*sizeof(float));
for (int i = 0; i < image->width*image->height; i++) for (int i = 0; i < image->width*image->height; i++)
@ -1023,7 +1023,7 @@ void ImageFormat(Image *image, int newFormat)
free(pixels); free(pixels);
pixels = NULL; pixels = NULL;
// In case original image had mipmaps, generate mipmaps for formated image // In case original image had mipmaps, generate mipmaps for formated image
// NOTE: Original mipmaps are replaced by new ones, if custom mipmaps were used, they are lost // NOTE: Original mipmaps are replaced by new ones, if custom mipmaps were used, they are lost
if (image->mipmaps > 1) if (image->mipmaps > 1)
@ -1087,14 +1087,14 @@ void ImageAlphaMask(Image *image, Image alphaMask)
void ImageAlphaClear(Image *image, Color color, float threshold) void ImageAlphaClear(Image *image, Color color, float threshold)
{ {
Color *pixels = GetImageData(*image); Color *pixels = GetImageData(*image);
for (int i = 0; i < image->width*image->height; i++) if (pixels[i].a <= (unsigned char)(threshold*255.0f)) pixels[i] = color; for (int i = 0; i < image->width*image->height; i++) if (pixels[i].a <= (unsigned char)(threshold*255.0f)) pixels[i] = color;
UnloadImage(*image); UnloadImage(*image);
int prevFormat = image->format; int prevFormat = image->format;
*image = LoadImageEx(pixels, image->width, image->height); *image = LoadImageEx(pixels, image->width, image->height);
ImageFormat(image, prevFormat); ImageFormat(image, prevFormat);
} }
@ -1102,13 +1102,13 @@ void ImageAlphaClear(Image *image, Color color, float threshold)
void ImageAlphaCrop(Image *image, float threshold) void ImageAlphaCrop(Image *image, float threshold)
{ {
Rectangle crop = { 0 }; Rectangle crop = { 0 };
Color *pixels = GetImageData(*image); Color *pixels = GetImageData(*image);
int minx = 0; int minx = 0;
int miny = 0; int miny = 0;
for (int i = 0; i < image->width*image->height; i++) for (int i = 0; i < image->width*image->height; i++)
{ {
if (pixels[i].a > (unsigned char)(threshold*255.0f)) if (pixels[i].a > (unsigned char)(threshold*255.0f))
{ {
@ -1127,18 +1127,18 @@ void ImageAlphaCrop(Image *image, float threshold)
else if (crop.height < (float)miny) crop.height = (float)miny; else if (crop.height < (float)miny) crop.height = (float)miny;
} }
} }
crop.width -= (crop.x - 1); crop.width -= (crop.x - 1);
crop.height -= (crop.y - 1); crop.height -= (crop.y - 1);
TraceLog(LOG_INFO, "Crop rectangle: (%i, %i, %i, %i)", crop.x, crop.y, crop.width, crop.height); TraceLog(LOG_INFO, "Crop rectangle: (%i, %i, %i, %i)", crop.x, crop.y, crop.width, crop.height);
free(pixels); free(pixels);
// NOTE: Added this weird check to avoid additional 1px crop to // NOTE: Added this weird check to avoid additional 1px crop to
// image data that has already been cropped... // image data that has already been cropped...
if ((crop.x != 1) && if ((crop.x != 1) &&
(crop.y != 1) && (crop.y != 1) &&
(crop.width != image->width - 1) && (crop.width != image->width - 1) &&
(crop.height != image->height - 1)) ImageCrop(image, crop); (crop.height != image->height - 1)) ImageCrop(image, crop);
} }
@ -1148,8 +1148,8 @@ void ImageAlphaPremultiply(Image *image)
{ {
float alpha = 0.0f; float alpha = 0.0f;
Color *pixels = GetImageData(*image); Color *pixels = GetImageData(*image);
for (int i = 0; i < image->width*image->height; i++) for (int i = 0; i < image->width*image->height; i++)
{ {
alpha = (float)pixels[i].a/255.0f; alpha = (float)pixels[i].a/255.0f;
pixels[i].r = (unsigned char)((float)pixels[i].r*alpha); pixels[i].r = (unsigned char)((float)pixels[i].r*alpha);
@ -1158,10 +1158,10 @@ void ImageAlphaPremultiply(Image *image)
} }
UnloadImage(*image); UnloadImage(*image);
int prevFormat = image->format; int prevFormat = image->format;
*image = LoadImageEx(pixels, image->width, image->height); *image = LoadImageEx(pixels, image->width, image->height);
ImageFormat(image, prevFormat); ImageFormat(image, prevFormat);
} }
@ -1282,9 +1282,9 @@ void ImageResizeCanvas(Image *image, int newWidth,int newHeight, int offsetX, in
Image imTemp = GenImageColor(newWidth, newHeight, color); Image imTemp = GenImageColor(newWidth, newHeight, color);
Rectangle srcRec = { 0.0f, 0.0f, (float)image->width, (float)image->height }; Rectangle srcRec = { 0.0f, 0.0f, (float)image->width, (float)image->height };
Rectangle dstRec = { (float)offsetX, (float)offsetY, (float)srcRec.width, (float)srcRec.height }; Rectangle dstRec = { (float)offsetX, (float)offsetY, (float)srcRec.width, (float)srcRec.height };
// TODO: Review different scaling situations // TODO: Review different scaling situations
if ((newWidth > image->width) && (newHeight > image->height)) if ((newWidth > image->width) && (newHeight > image->height))
{ {
ImageDraw(&imTemp, *image, srcRec, dstRec); ImageDraw(&imTemp, *image, srcRec, dstRec);
@ -1314,7 +1314,7 @@ void ImageMipmaps(Image *image)
{ {
if (mipWidth != 1) mipWidth /= 2; if (mipWidth != 1) mipWidth /= 2;
if (mipHeight != 1) mipHeight /= 2; if (mipHeight != 1) mipHeight /= 2;
// Security check for NPOT textures // Security check for NPOT textures
if (mipWidth < 1) mipWidth = 1; if (mipWidth < 1) mipWidth = 1;
if (mipHeight < 1) mipHeight = 1; if (mipHeight < 1) mipHeight = 1;
@ -1332,8 +1332,8 @@ void ImageMipmaps(Image *image)
if (image->mipmaps < mipCount) if (image->mipmaps < mipCount)
{ {
void *temp = realloc(image->data, mipSize); void *temp = realloc(image->data, mipSize);
if (temp != NULL) if (temp != NULL)
{ {
image->data = temp; // Assign new pointer (new size) to store mipmaps data image->data = temp; // Assign new pointer (new size) to store mipmaps data
TraceLog(LOG_DEBUG, "Image data memory point reallocated: 0x%x", temp); TraceLog(LOG_DEBUG, "Image data memory point reallocated: 0x%x", temp);
@ -1342,29 +1342,29 @@ void ImageMipmaps(Image *image)
// Pointer to allocated memory point where store next mipmap level data // Pointer to allocated memory point where store next mipmap level data
unsigned char *nextmip = (unsigned char *)image->data + GetPixelDataSize(image->width, image->height, image->format); unsigned char *nextmip = (unsigned char *)image->data + GetPixelDataSize(image->width, image->height, image->format);
mipWidth = image->width/2; mipWidth = image->width/2;
mipHeight = image->height/2; mipHeight = image->height/2;
mipSize = GetPixelDataSize(mipWidth, mipHeight, image->format); mipSize = GetPixelDataSize(mipWidth, mipHeight, image->format);
Image imCopy = ImageCopy(*image); Image imCopy = ImageCopy(*image);
for (int i = 1; i < mipCount; i++) for (int i = 1; i < mipCount; i++)
{ {
TraceLog(LOG_DEBUG, "Gen mipmap level: %i (%i x %i) - size: %i - offset: 0x%x", i, mipWidth, mipHeight, mipSize, nextmip); TraceLog(LOG_DEBUG, "Gen mipmap level: %i (%i x %i) - size: %i - offset: 0x%x", i, mipWidth, mipHeight, mipSize, nextmip);
ImageResize(&imCopy, mipWidth, mipHeight); // Uses internally Mitchell cubic downscale filter ImageResize(&imCopy, mipWidth, mipHeight); // Uses internally Mitchell cubic downscale filter
memcpy(nextmip, imCopy.data, mipSize); memcpy(nextmip, imCopy.data, mipSize);
nextmip += mipSize; nextmip += mipSize;
image->mipmaps++; image->mipmaps++;
mipWidth /= 2; mipWidth /= 2;
mipHeight /= 2; mipHeight /= 2;
// Security check for NPOT textures // Security check for NPOT textures
if (mipWidth < 1) mipWidth = 1; if (mipWidth < 1) mipWidth = 1;
if (mipHeight < 1) mipHeight = 1; if (mipHeight < 1) mipHeight = 1;
mipSize = GetPixelDataSize(mipWidth, mipHeight, image->format); mipSize = GetPixelDataSize(mipWidth, mipHeight, image->format);
} }
@ -1487,10 +1487,10 @@ void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp)
Color *ImageExtractPalette(Image image, int maxPaletteSize, int *extractCount) 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)) #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 *pixels = GetImageData(image);
Color *palette = (Color *)malloc(maxPaletteSize*sizeof(Color)); Color *palette = (Color *)malloc(maxPaletteSize*sizeof(Color));
int palCount = 0; int palCount = 0;
for (int i = 0; i < maxPaletteSize; i++) palette[i] = BLANK; // Set all colors to BLANK for (int i = 0; i < maxPaletteSize; i++) palette[i] = BLANK; // Set all colors to BLANK
@ -1499,23 +1499,23 @@ Color *ImageExtractPalette(Image image, int maxPaletteSize, int *extractCount)
if (pixels[i].a > 0) if (pixels[i].a > 0)
{ {
bool colorInPalette = false; bool colorInPalette = false;
// Check if the color is already on palette // Check if the color is already on palette
for (int j = 0; j < maxPaletteSize; j++) for (int j = 0; j < maxPaletteSize; j++)
{ {
if (COLOR_EQUAL(pixels[i], palette[j])) if (COLOR_EQUAL(pixels[i], palette[j]))
{ {
colorInPalette = true; colorInPalette = true;
break; break;
} }
} }
// Store color if not on the palette // Store color if not on the palette
if (!colorInPalette) if (!colorInPalette)
{ {
palette[palCount] = pixels[i]; // Add pixels[i] to palette palette[palCount] = pixels[i]; // Add pixels[i] to palette
palCount++; palCount++;
// We reached the limit of colors supported by palette // We reached the limit of colors supported by palette
if (palCount >= maxPaletteSize) if (palCount >= maxPaletteSize)
{ {
@ -1527,9 +1527,9 @@ Color *ImageExtractPalette(Image image, int maxPaletteSize, int *extractCount)
} }
free(pixels); free(pixels);
*extractCount = palCount; *extractCount = palCount;
return palette; return palette;
} }
@ -1595,7 +1595,7 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec)
UnloadImage(srcCopy); // Source copy not required any more... UnloadImage(srcCopy); // Source copy not required any more...
Vector4 fsrc, fdst, fout; // float based versions of pixel data Vector4 fsrc, fdst, fout; // float based versions of pixel data
// Blit pixels, copy source image into destination // Blit pixels, copy source image into destination
// TODO: Probably out-of-bounds blitting could be considered here instead of so much cropping... // TODO: Probably out-of-bounds blitting could be considered here instead of so much cropping...
for (int j = (int)dstRec.y; j < (int)(dstRec.y + dstRec.height); j++) for (int j = (int)dstRec.y; j < (int)(dstRec.y + dstRec.height); j++)
@ -1603,12 +1603,12 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec)
for (int i = (int)dstRec.x; i < (int)(dstRec.x + dstRec.width); i++) for (int i = (int)dstRec.x; i < (int)(dstRec.x + dstRec.width); i++)
{ {
// Alpha blending (https://en.wikipedia.org/wiki/Alpha_compositing) // Alpha blending (https://en.wikipedia.org/wiki/Alpha_compositing)
fdst = ColorNormalize(dstPixels[j*(int)dst->width + i]); fdst = ColorNormalize(dstPixels[j*(int)dst->width + i]);
fsrc = ColorNormalize(srcPixels[(j - (int)dstRec.y)*(int)dstRec.width + (i - (int)dstRec.x)]); fsrc = ColorNormalize(srcPixels[(j - (int)dstRec.y)*(int)dstRec.width + (i - (int)dstRec.x)]);
fout.w = fsrc.w + fdst.w*(1.0f - fsrc.w); fout.w = fsrc.w + fdst.w*(1.0f - fsrc.w);
if (fout.w <= 0.0f) if (fout.w <= 0.0f)
{ {
fout.x = 0.0f; fout.x = 0.0f;
@ -1622,9 +1622,9 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec)
fout.z = (fsrc.z*fsrc.w + fdst.z*fdst.w*(1 - fsrc.w))/fout.w; fout.z = (fsrc.z*fsrc.w + fdst.z*fdst.w*(1 - fsrc.w))/fout.w;
} }
dstPixels[j*(int)dst->width + i] = (Color){ (unsigned char)(fout.x*255.0f), dstPixels[j*(int)dst->width + i] = (Color){ (unsigned char)(fout.x*255.0f),
(unsigned char)(fout.y*255.0f), (unsigned char)(fout.y*255.0f),
(unsigned char)(fout.z*255.0f), (unsigned char)(fout.z*255.0f),
(unsigned char)(fout.w*255.0f) }; (unsigned char)(fout.w*255.0f) };
// TODO: Support other blending options // TODO: Support other blending options
@ -1662,16 +1662,16 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co
// TODO: ISSUE: Measured text size does not seem to be correct... issue on ImageDraw() // TODO: ISSUE: Measured text size does not seem to be correct... issue on ImageDraw()
Vector2 imSize = MeasureTextEx(font, text, (float)font.baseSize, spacing); Vector2 imSize = MeasureTextEx(font, text, (float)font.baseSize, spacing);
TraceLog(LOG_DEBUG, "Text Image size: %f, %f", imSize.x, imSize.y); TraceLog(LOG_DEBUG, "Text Image size: %f, %f", imSize.x, imSize.y);
// NOTE: glGetTexImage() not available in OpenGL ES // NOTE: glGetTexImage() not available in OpenGL ES
// TODO: This is horrible, retrieving font texture from GPU!!! // TODO: This is horrible, retrieving font texture from GPU!!!
// Define ImageFont struct? or include Image spritefont in Font struct? // Define ImageFont struct? or include Image spritefont in Font struct?
Image imFont = GetTextureData(font.texture); Image imFont = GetTextureData(font.texture);
ImageFormat(&imFont, UNCOMPRESSED_R8G8B8A8); // Make sure image format could be properly colored! ImageFormat(&imFont, UNCOMPRESSED_R8G8B8A8); // Make sure image format could be properly colored!
ImageColorTint(&imFont, tint); // Apply color tint to font ImageColorTint(&imFont, tint); // Apply color tint to font
// Create image to store text // Create image to store text
@ -1702,10 +1702,10 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co
else index = GetGlyphIndex(font, (unsigned char)text[i]); else index = GetGlyphIndex(font, (unsigned char)text[i]);
CharInfo letter = font.chars[index]; CharInfo letter = font.chars[index];
if ((unsigned char)text[i] != ' ') if ((unsigned char)text[i] != ' ')
{ {
ImageDraw(&imText, imFont, letter.rec, (Rectangle){ (float)(posX + letter.offsetX), ImageDraw(&imText, imFont, letter.rec, (Rectangle){ (float)(posX + letter.offsetX),
(float)letter.offsetY, (float)letter.rec.width, (float)letter.rec.height }); (float)letter.offsetY, (float)letter.rec.width, (float)letter.rec.height });
} }
@ -1734,11 +1734,11 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co
void ImageDrawRectangle(Image *dst, Vector2 position, Rectangle rec, Color color) void ImageDrawRectangle(Image *dst, Vector2 position, Rectangle rec, Color color)
{ {
Image imRec = GenImageColor((int)rec.width, (int)rec.height, color); Image imRec = GenImageColor((int)rec.width, (int)rec.height, color);
Rectangle dstRec = { position.x, position.y, (float)imRec.width, (float)imRec.height }; Rectangle dstRec = { position.x, position.y, (float)imRec.width, (float)imRec.height };
ImageDraw(dst, imRec, rec, dstRec); ImageDraw(dst, imRec, rec, dstRec);
UnloadImage(imRec); UnloadImage(imRec);
} }
@ -2056,13 +2056,13 @@ void ImageColorReplace(Image *image, Color color, Color replace)
Image GenImageColor(int width, int height, Color color) Image GenImageColor(int width, int height, Color color)
{ {
Color *pixels = (Color *)calloc(width*height, sizeof(Color)); Color *pixels = (Color *)calloc(width*height, sizeof(Color));
for (int i = 0; i < width*height; i++) pixels[i] = color; for (int i = 0; i < width*height; i++) pixels[i] = color;
Image image = LoadImageEx(pixels, width, height); Image image = LoadImageEx(pixels, width, height);
free(pixels); free(pixels);
return image; return image;
} }
@ -2120,17 +2120,17 @@ Image GenImageGradientRadial(int width, int height, float density, Color inner,
float centerX = (float)width/2.0f; float centerX = (float)width/2.0f;
float centerY = (float)height/2.0f; float centerY = (float)height/2.0f;
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
{ {
for (int x = 0; x < width; x++) for (int x = 0; x < width; x++)
{ {
float dist = hypotf((float)x - centerX, (float)y - centerY); float dist = hypotf((float)x - centerX, (float)y - centerY);
float factor = (dist - radius*density)/(radius*(1.0f - density)); float factor = (dist - radius*density)/(radius*(1.0f - density));
factor = (float)fmax(factor, 0.f); factor = (float)fmax(factor, 0.f);
factor = (float)fmin(factor, 1.f); // dist can be bigger than radius so we have to check factor = (float)fmin(factor, 1.f); // dist can be bigger than radius so we have to check
pixels[y*width + x].r = (int)((float)outer.r*factor + (float)inner.r*(1.0f - factor)); pixels[y*width + x].r = (int)((float)outer.r*factor + (float)inner.r*(1.0f - factor));
pixels[y*width + x].g = (int)((float)outer.g*factor + (float)inner.g*(1.0f - factor)); pixels[y*width + x].g = (int)((float)outer.g*factor + (float)inner.g*(1.0f - factor));
pixels[y*width + x].b = (int)((float)outer.b*factor + (float)inner.b*(1.0f - factor)); pixels[y*width + x].b = (int)((float)outer.b*factor + (float)inner.b*(1.0f - factor));
@ -2192,7 +2192,7 @@ Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float
{ {
float nx = (float)(x + offsetX)*scale/(float)width; float nx = (float)(x + offsetX)*scale/(float)width;
float ny = (float)(y + offsetY)*scale/(float)height; float ny = (float)(y + offsetY)*scale/(float)height;
// Typical values to start playing with: // Typical values to start playing with:
// lacunarity = ~2.0 -- spacing between successive octaves (use exactly 2.0 for wrapping output) // lacunarity = ~2.0 -- spacing between successive octaves (use exactly 2.0 for wrapping output)
// gain = 0.5 -- relative weighting applied to each successive octave // gain = 0.5 -- relative weighting applied to each successive octave
@ -2200,7 +2200,7 @@ Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float
// NOTE: We need to translate the data from [-1..1] to [0..1] // NOTE: We need to translate the data from [-1..1] to [0..1]
float p = (stb_perlin_fbm_noise3(nx, ny, 1.0f, 2.0f, 0.5f, 6, 0, 0, 0) + 1.0f)/2.0f; float p = (stb_perlin_fbm_noise3(nx, ny, 1.0f, 2.0f, 0.5f, 6, 0, 0, 0) + 1.0f)/2.0f;
int intensity = (int)(p*255.0f); int intensity = (int)(p*255.0f);
pixels[y*width + x] = (Color){intensity, intensity, intensity, 255}; pixels[y*width + x] = (Color){intensity, intensity, intensity, 255};
} }
@ -2233,7 +2233,7 @@ Image GenImageCellular(int width, int height, int tileSize)
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
{ {
int tileY = y/tileSize; int tileY = y/tileSize;
for (int x = 0; x < width; x++) for (int x = 0; x < width; x++)
{ {
int tileX = x/tileSize; int tileX = x/tileSize;
@ -2263,7 +2263,7 @@ Image GenImageCellular(int width, int height, int tileSize)
pixels[y*width + x] = (Color){ intensity, intensity, intensity, 255 }; pixels[y*width + x] = (Color){ intensity, intensity, intensity, 255 };
} }
} }
free(seeds); free(seeds);
Image image = LoadImageEx(pixels, width, height); Image image = LoadImageEx(pixels, width, height);
@ -2411,7 +2411,7 @@ void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, V
{ {
float width = (float)texture.width; float width = (float)texture.width;
float height = (float)texture.height; float height = (float)texture.height;
if (sourceRec.width < 0) sourceRec.x -= sourceRec.width; if (sourceRec.width < 0) sourceRec.x -= sourceRec.width;
if (sourceRec.height < 0) sourceRec.y -= sourceRec.height; if (sourceRec.height < 0) sourceRec.y -= sourceRec.height;
@ -2729,7 +2729,7 @@ static Image LoadDDS(const char *fileName)
image.width = ddsHeader.width; image.width = ddsHeader.width;
image.height = ddsHeader.height; image.height = ddsHeader.height;
if (ddsHeader.mipmapCount == 0) image.mipmaps = 1; // Parameter not used if (ddsHeader.mipmapCount == 0) image.mipmaps = 1; // Parameter not used
else image.mipmaps = ddsHeader.mipmapCount; else image.mipmaps = ddsHeader.mipmapCount;
@ -2946,9 +2946,9 @@ static Image LoadKTX(const char *fileName)
// KTX file Header (64 bytes) // KTX file Header (64 bytes)
// v1.1 - https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/ // v1.1 - https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
// v2.0 - http://github.khronos.org/KTX-Specification/ // v2.0 - http://github.khronos.org/KTX-Specification/
// TODO: Support KTX 2.2 specs! // TODO: Support KTX 2.2 specs!
typedef struct { typedef struct {
char id[12]; // Identifier: "«KTX 11»\r\n\x1A\n" char id[12]; // Identifier: "«KTX 11»\r\n\x1A\n"
unsigned int endianness; // Little endian: 0x01 0x02 0x03 0x04 unsigned int endianness; // Little endian: 0x01 0x02 0x03 0x04
@ -3028,11 +3028,11 @@ static Image LoadKTX(const char *fileName)
static int SaveKTX(Image image, const char *fileName) static int SaveKTX(Image image, const char *fileName)
{ {
int success = 0; int success = 0;
// KTX file Header (64 bytes) // KTX file Header (64 bytes)
// v1.1 - https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/ // 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 // v2.0 - http://github.khronos.org/KTX-Specification/ - still on draft, not ready for implementation
typedef struct { typedef struct {
char id[12]; // Identifier: "«KTX 11»\r\n\x1A\n" // KTX 2.0: "«KTX 22»\r\n\x1A\n" 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 endianness; // Little endian: 0x01 0x02 0x03 0x04
@ -3060,11 +3060,11 @@ static int SaveKTX(Image image, const char *fileName)
else else
{ {
KTXHeader ktxHeader; KTXHeader ktxHeader;
// KTX identifier (v2.2) // KTX identifier (v2.2)
//unsigned char id[12] = { '«', 'K', 'T', 'X', ' ', '1', '1', '»', '\r', '\n', '\x1A', '\n' }; //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 }; //unsigned char id[12] = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A };
// Get the image header // Get the image header
strcpy(ktxHeader.id, "«KTX 11»\r\n\x1A\n"); // KTX 1.1 signature strcpy(ktxHeader.id, "«KTX 11»\r\n\x1A\n"); // KTX 1.1 signature
ktxHeader.endianness = 0; ktxHeader.endianness = 0;
@ -3080,28 +3080,28 @@ static int SaveKTX(Image image, const char *fileName)
ktxHeader.faces = 1; ktxHeader.faces = 1;
ktxHeader.mipmapLevels = image.mipmaps; // If it was 0, it means mipmaps should be generated on loading (not for compressed formats) 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 ktxHeader.keyValueDataSize = 0; // No extra data after the header
rlGetGlTextureFormats(image.format, &ktxHeader.glInternalFormat, &ktxHeader.glFormat, &ktxHeader.glType); // rlgl module function rlGetGlTextureFormats(image.format, &ktxHeader.glInternalFormat, &ktxHeader.glFormat, &ktxHeader.glType); // rlgl module function
ktxHeader.glBaseInternalFormat = ktxHeader.glFormat; // KTX 1.1 only 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 // 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."); if (ktxHeader.glFormat == -1) TraceLog(LOG_WARNING, "Image format not supported for KTX export.");
else else
{ {
success = fwrite(&ktxHeader, sizeof(KTXHeader), 1, ktxFile); success = fwrite(&ktxHeader, sizeof(KTXHeader), 1, ktxFile);
int width = image.width; int width = image.width;
int height = image.height; int height = image.height;
int dataOffset = 0; int dataOffset = 0;
// Save all mipmaps data // Save all mipmaps data
for (int i = 0; i < image.mipmaps; i++) for (int i = 0; i < image.mipmaps; i++)
{ {
unsigned int dataSize = GetPixelDataSize(width, height, image.format); unsigned int dataSize = GetPixelDataSize(width, height, image.format);
success = fwrite(&dataSize, sizeof(unsigned int), 1, ktxFile); success = fwrite(&dataSize, sizeof(unsigned int), 1, ktxFile);
success = fwrite((unsigned char *)image.data + dataOffset, dataSize, 1, ktxFile); success = fwrite((unsigned char *)image.data + dataOffset, dataSize, 1, ktxFile);
width /= 2; width /= 2;
height /= 2; height /= 2;
dataOffset += dataSize; dataOffset += dataSize;
@ -3110,7 +3110,7 @@ static int SaveKTX(Image image, const char *fileName)
fclose(ktxFile); // Close file pointer fclose(ktxFile); // Close file pointer
} }
// If all data has been written correctly to file, success = 1 // If all data has been written correctly to file, success = 1
return success; return success;
} }
@ -3321,7 +3321,7 @@ static Image LoadASTC(const char *fileName)
TraceLog(LOG_DEBUG, "ASTC image width: %i", image.width); TraceLog(LOG_DEBUG, "ASTC image width: %i", image.width);
TraceLog(LOG_DEBUG, "ASTC image height: %i", image.height); TraceLog(LOG_DEBUG, "ASTC image height: %i", image.height);
TraceLog(LOG_DEBUG, "ASTC image blocks: %ix%i", astcHeader.blockX, astcHeader.blockY); TraceLog(LOG_DEBUG, "ASTC image blocks: %ix%i", astcHeader.blockX, astcHeader.blockY);
image.mipmaps = 1; // NOTE: ASTC format only contains one mipmap level image.mipmaps = 1; // NOTE: ASTC format only contains one mipmap level
// NOTE: Each block is always stored in 128bit so we can calculate the bpp // NOTE: Each block is always stored in 128bit so we can calculate the bpp