Merge pull request #2 from raysan5/master

merged raylib-master
This commit is contained in:
Jak 2019-02-24 21:57:31 +00:00 committed by GitHub
commit 44c2df3c12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 545 additions and 422 deletions

1
.gitignore vendored
View File

@ -49,6 +49,7 @@ ipch/
# Ignore compiled binaries # Ignore compiled binaries
*.o *.o
*.exe *.exe
*.a
!raylib.rc.o !raylib.rc.o
# Ignore all examples files # Ignore all examples files

View File

@ -707,7 +707,7 @@ bool WindowShouldClose(void)
{ {
#if defined(PLATFORM_WEB) #if defined(PLATFORM_WEB)
// Emterpreter-Async required to run sync code // Emterpreter-Async required to run sync code
// https://github.com/kripken/emscripten/wiki/Emterpreter#emterpreter-async-run-synchronous-code // https://github.com/emscripten-core/emscripten/wiki/Emterpreter#emterpreter-async-run-synchronous-code
// By default, this function is never called on a web-ready raylib example because we encapsulate // By default, this function is never called on a web-ready raylib example because we encapsulate
// frame code in a UpdateDrawFrame() function, to allow browser manage execution asynchronously // frame code in a UpdateDrawFrame() function, to allow browser manage execution asynchronously
// but now emscripten allows sync code to be executed in an interpreted way, using emterpreter! // but now emscripten allows sync code to be executed in an interpreted way, using emterpreter!
@ -1254,6 +1254,10 @@ void EndTextureMode(void)
rlMatrixMode(RL_MODELVIEW); // Switch back to MODELVIEW matrix rlMatrixMode(RL_MODELVIEW); // Switch back to MODELVIEW matrix
rlLoadIdentity(); // Reset current matrix (MODELVIEW) rlLoadIdentity(); // Reset current matrix (MODELVIEW)
// Reset current screen size
currentWidth = GetScreenWidth();
currentHeight = GetScreenHeight();
} }
// Returns a ray trace from mouse position // Returns a ray trace from mouse position
@ -1913,14 +1917,13 @@ void OpenURL(const char *url)
char *cmd = (char *)calloc(strlen(url) + 10, sizeof(char)); char *cmd = (char *)calloc(strlen(url) + 10, sizeof(char));
#if defined(_WIN32) #if defined(_WIN32)
sprintf(cmd, "explorer '%s'", url); sprintf(cmd, "explorer %s", url);
#elif defined(__linux__) #elif defined(__linux__)
sprintf(cmd, "xdg-open '%s'", url); // Alternatives: firefox, x-www-browser sprintf(cmd, "xdg-open '%s'", url); // Alternatives: firefox, x-www-browser
#elif defined(__APPLE__) #elif defined(__APPLE__)
sprintf(cmd, "open '%s'", url); sprintf(cmd, "open '%s'", url);
#endif #endif
system(cmd); system(cmd);
free(cmd); free(cmd);
} }
} }
@ -3274,9 +3277,9 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i
char path[512] = { 0 }; char path[512] = { 0 };
#if defined(PLATFORM_ANDROID) #if defined(PLATFORM_ANDROID)
strcpy(path, internalDataPath); strcpy(path, internalDataPath);
strcat(path, TextFormat("/screenrec%03i.gif", screenshotCounter)); strcat(path, TextFormat("./screenrec%03i.gif", screenshotCounter));
#else #else
strcpy(path, TextFormat("/screenrec%03i.gif", screenshotCounter)); strcpy(path, TextFormat("./screenrec%03i.gif", screenshotCounter));
#endif #endif
// 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

51
src/external/cgltf.h vendored
View File

@ -1,6 +1,49 @@
/** /**
* cgltf - a single-file glTF 2.0 parser written in C99. * cgltf - a single-file glTF 2.0 parser written in C99.
*
* Version: 1.0
*
* Website: https://github.com/jkuhlmann/cgltf
*
* Distributed under the MIT License, see notice at the end of this file. * Distributed under the MIT License, see notice at the end of this file.
*
* Building:
* Include this file where you need the struct and function
* declarations. Have exactly one source file where you define
* `CGLTF_IMPLEMENTATION` before including this file to get the
* function definitions.
*
* Reference:
* `cgltf_result cgltf_parse(const cgltf_options*, const void*,
* cgltf_size, cgltf_data**)` parses both glTF and GLB data. If
* this function returns `cgltf_result_success`, you have to call
* `cgltf_free()` on the created `cgltf_data*` variable.
* Note that contents of external files for buffers and images are not
* automatically loaded. You'll need to read these files yourself using
* URIs in the `cgltf_data` structure.
*
* `cgltf_options` is the struct passed to `cgltf_parse()` to control
* parts of the parsing process. You can use it to force the file type
* and provide memory allocation callbacks. Should be zero-initialized
* to trigger default behavior.
*
* `cgltf_data` is the struct allocated and filled by `cgltf_parse()`.
* It generally mirrors the glTF format as described by the spec (see
* https://github.com/KhronosGroup/glTF/tree/master/specification/2.0).
*
* `void cgltf_free(cgltf_data*)` frees the allocated `cgltf_data`
* variable.
*
* `cgltf_result cgltf_load_buffers(const cgltf_options*, cgltf_data*,
* const char*)` can be optionally called to open and read buffer
* files using the `FILE*` APIs.
*
* `cgltf_result cgltf_parse_file(const cgltf_options* options, const
* char* path, cgltf_data** out_data)` can be used to open the given
* file using `FILE*` APIs and parse the data using `cgltf_parse()`.
*
* `cgltf_result cgltf_validate(cgltf_data*)` can be used to do additional
* checks to make sure the parsed glTF data is valid.
*/ */
#ifndef CGLTF_H_INCLUDED__ #ifndef CGLTF_H_INCLUDED__
#define CGLTF_H_INCLUDED__ #define CGLTF_H_INCLUDED__
@ -462,6 +505,10 @@ void cgltf_free(cgltf_data* data);
void cgltf_node_transform_local(const cgltf_node* node, cgltf_float* out_matrix); void cgltf_node_transform_local(const cgltf_node* node, cgltf_float* out_matrix);
void cgltf_node_transform_world(const cgltf_node* node, cgltf_float* out_matrix); void cgltf_node_transform_world(const cgltf_node* node, cgltf_float* out_matrix);
#ifdef __cplusplus
}
#endif
#endif /* #ifndef CGLTF_H_INCLUDED__ */ #endif /* #ifndef CGLTF_H_INCLUDED__ */
/* /*
@ -4266,10 +4313,6 @@ static void jsmn_init(jsmn_parser *parser) {
#endif /* #ifdef CGLTF_IMPLEMENTATION */ #endif /* #ifdef CGLTF_IMPLEMENTATION */
#ifdef __cplusplus
}
#endif
/* cgltf is distributed under MIT license: /* cgltf is distributed under MIT license:
* *
* Copyright (c) 2018 Johannes Kuhlmann * Copyright (c) 2018 Johannes Kuhlmann

Binary file not shown.

View File

@ -655,12 +655,16 @@ Mesh LoadMesh(const char *fileName)
TraceLog(LOG_WARNING, "[%s] Mesh fileformat not supported, it can't be loaded", fileName); TraceLog(LOG_WARNING, "[%s] Mesh fileformat not supported, it can't be loaded", fileName);
#endif #endif
#if defined(SUPPORT_MESH_GENERATION)
if (mesh.vertexCount == 0) if (mesh.vertexCount == 0)
{ {
TraceLog(LOG_WARNING, "Mesh could not be loaded! Let's load a cube to replace it!"); TraceLog(LOG_WARNING, "Mesh could not be loaded! Let's load a cube to replace it!");
mesh = GenMeshCube(1.0f, 1.0f, 1.0f); mesh = GenMeshCube(1.0f, 1.0f, 1.0f);
} }
else rlLoadMesh(&mesh, false); // Upload vertex data to GPU (static mesh) else rlLoadMesh(&mesh, false); // Upload vertex data to GPU (static mesh)
#else
rlLoadMesh(&mesh, false); // Upload vertex data to GPU (static mesh)
#endif
return mesh; return mesh;
} }
@ -2744,17 +2748,17 @@ static Mesh LoadIQM(const char *fileName)
#endif #endif
#if defined(SUPPORT_FILEFORMAT_GLTF) #if defined(SUPPORT_FILEFORMAT_GLTF)
// Load GLTF mesh data // Load glTF mesh data
static Mesh LoadGLTF(const char *fileName) static Mesh LoadGLTF(const char *fileName)
{ {
Mesh mesh = { 0 }; Mesh mesh = { 0 };
// GLTF file loading // glTF file loading
FILE *gltfFile = fopen(fileName, "rb"); FILE *gltfFile = fopen(fileName, "rb");
if (gltfFile == NULL) if (gltfFile == NULL)
{ {
TraceLog(LOG_WARNING, "[%s] GLTF file could not be opened", fileName); TraceLog(LOG_WARNING, "[%s] glTF file could not be opened", fileName);
return mesh; return mesh;
} }
@ -2767,21 +2771,27 @@ static Mesh LoadGLTF(const char *fileName)
fclose(gltfFile); fclose(gltfFile);
// GLTF data loading // glTF data loading
cgltf_options options = {0}; cgltf_options options = {0};
cgltf_data data; cgltf_data data;
cgltf_result result = cgltf_parse(&options, buffer, size, &data); cgltf_result result = cgltf_parse(&options, buffer, size, &data);
free(buffer);
if (result == cgltf_result_success) if (result == cgltf_result_success)
{ {
printf("Type: %u\n", data.file_type); printf("Type: %u\n", data.file_type);
printf("Version: %d\n", data.version); printf("Version: %d\n", data.version);
printf("Meshes: %lu\n", data.meshes_count); printf("Meshes: %lu\n", data.meshes_count);
}
else TraceLog(LOG_WARNING, "[%s] GLTF data could not be loaded", fileName);
free(buffer); // TODO: Process glTF data and map to mesh
// NOTE: data.buffers[] and data.images[] should be loaded
// using buffers[n].uri and images[n].uri... or use cgltf_load_buffers(&options, data, fileName);
cgltf_free(&data); cgltf_free(&data);
}
else TraceLog(LOG_WARNING, "[%s] glTF data could not be loaded", fileName);
return mesh; return mesh;
} }

View File

@ -712,11 +712,13 @@ void SetAudioBufferPitch(AudioBuffer *audioBuffer, float pitch)
return; return;
} }
audioBuffer->pitch = pitch; float pitchMul = pitch / audioBuffer->pitch;
// Pitching is just an adjustment of the sample rate. Note that this changes the duration of the sound - higher pitches // Pitching is just an adjustment of the sample rate. Note that this changes the duration of the sound - higher pitches
// will make the sound faster; lower pitches make it slower. // will make the sound faster; lower pitches make it slower.
mal_uint32 newOutputSampleRate = (mal_uint32)((((float)audioBuffer->dsp.src.config.sampleRateOut / (float)audioBuffer->dsp.src.config.sampleRateIn) / pitch) * audioBuffer->dsp.src.config.sampleRateIn); mal_uint32 newOutputSampleRate = (mal_uint32)((float)audioBuffer->dsp.src.config.sampleRateOut / pitchMul);
audioBuffer->pitch *= (float)audioBuffer->dsp.src.config.sampleRateOut / newOutputSampleRate;
mal_dsp_set_output_sample_rate(&audioBuffer->dsp, newOutputSampleRate); mal_dsp_set_output_sample_rate(&audioBuffer->dsp, newOutputSampleRate);
} }
@ -767,7 +769,11 @@ Wave LoadWave(const char *fileName)
{ {
Wave wave = { 0 }; Wave wave = { 0 };
#if defined(SUPPORT_FILEFORMAT_WAV)
if (IsFileExtension(fileName, ".wav")) wave = LoadWAV(fileName); if (IsFileExtension(fileName, ".wav")) wave = LoadWAV(fileName);
#else
if (false) {}
#endif
#if defined(SUPPORT_FILEFORMAT_OGG) #if defined(SUPPORT_FILEFORMAT_OGG)
else if (IsFileExtension(fileName, ".ogg")) wave = LoadOGG(fileName); else if (IsFileExtension(fileName, ".ogg")) wave = LoadOGG(fileName);
#endif #endif
@ -887,7 +893,11 @@ void ExportWave(Wave wave, const char *fileName)
{ {
bool success = false; bool success = false;
#if defined(SUPPORT_FILEFORMAT_WAV)
if (IsFileExtension(fileName, ".wav")) success = SaveWAV(wave, fileName); if (IsFileExtension(fileName, ".wav")) success = SaveWAV(wave, fileName);
#else
if (false) {}
#endif
else if (IsFileExtension(fileName, ".raw")) else if (IsFileExtension(fileName, ".raw"))
{ {
// Export raw sample data (without header) // Export raw sample data (without header)
@ -1087,6 +1097,7 @@ Music LoadMusicStream(const char *fileName)
Music music = (MusicData *)malloc(sizeof(MusicData)); Music music = (MusicData *)malloc(sizeof(MusicData));
bool musicLoaded = true; bool musicLoaded = true;
#if defined(SUPPORT_FILEFORMAT_OGG)
if (IsFileExtension(fileName, ".ogg")) if (IsFileExtension(fileName, ".ogg"))
{ {
// Open ogg audio stream // Open ogg audio stream
@ -1110,6 +1121,9 @@ Music LoadMusicStream(const char *fileName)
TraceLog(LOG_DEBUG, "[%s] OGG memory required: %i", fileName, info.temp_memory_required); TraceLog(LOG_DEBUG, "[%s] OGG memory required: %i", fileName, info.temp_memory_required);
} }
} }
#else
if (false) {}
#endif
#if defined(SUPPORT_FILEFORMAT_FLAC) #if defined(SUPPORT_FILEFORMAT_FLAC)
else if (IsFileExtension(fileName, ".flac")) else if (IsFileExtension(fileName, ".flac"))
{ {
@ -1202,7 +1216,11 @@ Music LoadMusicStream(const char *fileName)
if (!musicLoaded) if (!musicLoaded)
{ {
#if defined(SUPPORT_FILEFORMAT_OGG)
if (music->ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close(music->ctxOgg); if (music->ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close(music->ctxOgg);
#else
if (false) {}
#endif
#if defined(SUPPORT_FILEFORMAT_FLAC) #if defined(SUPPORT_FILEFORMAT_FLAC)
else if (music->ctxType == MUSIC_AUDIO_FLAC) drflac_free(music->ctxFlac); else if (music->ctxType == MUSIC_AUDIO_FLAC) drflac_free(music->ctxFlac);
#endif #endif
@ -1232,7 +1250,11 @@ void UnloadMusicStream(Music music)
CloseAudioStream(music->stream); CloseAudioStream(music->stream);
#if defined(SUPPORT_FILEFORMAT_OGG)
if (music->ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close(music->ctxOgg); if (music->ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close(music->ctxOgg);
#else
if (false) {}
#endif
#if defined(SUPPORT_FILEFORMAT_FLAC) #if defined(SUPPORT_FILEFORMAT_FLAC)
else if (music->ctxType == MUSIC_AUDIO_FLAC) drflac_free(music->ctxFlac); else if (music->ctxType == MUSIC_AUDIO_FLAC) drflac_free(music->ctxFlac);
#endif #endif
@ -1297,7 +1319,9 @@ void StopMusicStream(Music music)
// Restart music context // Restart music context
switch (music->ctxType) switch (music->ctxType)
{ {
#if defined(SUPPORT_FILEFORMAT_OGG)
case MUSIC_AUDIO_OGG: stb_vorbis_seek_start(music->ctxOgg); break; case MUSIC_AUDIO_OGG: stb_vorbis_seek_start(music->ctxOgg); break;
#endif
#if defined(SUPPORT_FILEFORMAT_FLAC) #if defined(SUPPORT_FILEFORMAT_FLAC)
case MUSIC_AUDIO_FLAC: /* TODO: Restart FLAC context */ break; case MUSIC_AUDIO_FLAC: /* TODO: Restart FLAC context */ break;
#endif #endif
@ -1339,12 +1363,14 @@ void UpdateMusicStream(Music music)
// TODO: Really don't like ctxType thingy... // TODO: Really don't like ctxType thingy...
switch (music->ctxType) switch (music->ctxType)
{ {
#if defined(SUPPORT_FILEFORMAT_OGG)
case MUSIC_AUDIO_OGG: case MUSIC_AUDIO_OGG:
{ {
// NOTE: Returns the number of samples to process (be careful! we ask for number of shorts!) // NOTE: Returns the number of samples to process (be careful! we ask for number of shorts!)
stb_vorbis_get_samples_short_interleaved(music->ctxOgg, music->stream.channels, (short *)pcm, samplesCount); stb_vorbis_get_samples_short_interleaved(music->ctxOgg, music->stream.channels, (short *)pcm, samplesCount);
} break; } break;
#endif
#if defined(SUPPORT_FILEFORMAT_FLAC) #if defined(SUPPORT_FILEFORMAT_FLAC)
case MUSIC_AUDIO_FLAC: case MUSIC_AUDIO_FLAC:
{ {

View File

@ -742,7 +742,7 @@ static Texture2D GetShapesTexture(void)
recTexShapes = (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }; recTexShapes = (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 };
#else #else
texShapes = GetTextureDefault(); // Use default white texture texShapes = GetTextureDefault(); // Use default white texture
recTexShapes = { 0.0f, 0.0f, 1.0f, 1.0f }; recTexShapes = (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f };
#endif #endif
} }

View File

@ -309,6 +309,7 @@ Font LoadFontEx(const char *fileName, int fontSize, int *fontChars, int charsCou
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 defined(SUPPORT_FILEFORMAT_TTF)
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);
@ -316,6 +317,9 @@ Font LoadFontEx(const char *fileName, int fontSize, int *fontChars, int charsCou
UnloadImage(atlas); UnloadImage(atlas);
} }
else font = GetFontDefault(); else font = GetFontDefault();
#else
font = GetFontDefault();
#endif
return font; return font;
} }
@ -426,6 +430,7 @@ Font LoadFontFromImage(Image image, Color key, int firstChar)
spriteFont.chars[i].offsetX = 0; spriteFont.chars[i].offsetX = 0;
spriteFont.chars[i].offsetY = 0; spriteFont.chars[i].offsetY = 0;
spriteFont.chars[i].advanceX = 0; spriteFont.chars[i].advanceX = 0;
spriteFont.chars[i].data = NULL;
} }
spriteFont.baseSize = (int)spriteFont.chars[0].rec.height; spriteFont.baseSize = (int)spriteFont.chars[0].rec.height;
@ -449,6 +454,7 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c
CharInfo *chars = NULL; CharInfo *chars = NULL;
#if defined(SUPPORT_FILEFORMAT_TTF)
// Load font data (including pixel data) from TTF file // Load font data (including pixel data) from TTF file
// 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
@ -506,6 +512,7 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c
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);
else chars[i].data = NULL;
if (type == FONT_BITMAP) if (type == FONT_BITMAP)
{ {
@ -537,12 +544,16 @@ 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);
#else
TraceLog(LOG_WARNING, "[%s] TTF support is disabled", fileName);
#endif
return chars; return chars;
} }
// Generate image font atlas using chars info // Generate image font atlas using chars info
// NOTE: Packing method: 0-Default, 1-Skyline // NOTE: Packing method: 0-Default, 1-Skyline
#if defined(SUPPORT_FILEFORMAT_TTF)
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 };
@ -667,6 +678,7 @@ Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int paddi
return atlas; return atlas;
} }
#endif
// Unload Font from GPU memory (VRAM) // Unload Font from GPU memory (VRAM)
void UnloadFont(Font font) void UnloadFont(Font font)
@ -674,6 +686,11 @@ void UnloadFont(Font font)
// NOTE: Make sure spriteFont is not default font (fallback) // NOTE: Make sure spriteFont is not default font (fallback)
if (font.texture.id != GetFontDefault().texture.id) if (font.texture.id != GetFontDefault().texture.id)
{ {
for (int i = 0; i < font.charsCount; i++)
{
if(font.chars[i].data != NULL)
free(font.chars[i].data);
}
UnloadTexture(font.texture); UnloadTexture(font.texture);
free(font.chars); free(font.chars);
@ -887,7 +904,7 @@ void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, f
textOffsetX = 0; textOffsetX = 0;
} }
if ((textOffsetY + (int)((font.baseSize + font.baseSize/2)*scaleFactor)) > rec.height) break; if ((textOffsetY + (int)(font.baseSize*scaleFactor)) > rec.height) break;
//draw selected //draw selected
bool isGlyphSelected = false; bool isGlyphSelected = false;
@ -1428,6 +1445,7 @@ static Font LoadBMFont(const char *fileName)
font.chars[i].offsetX = charOffsetX; font.chars[i].offsetX = charOffsetX;
font.chars[i].offsetY = charOffsetY; font.chars[i].offsetY = charOffsetY;
font.chars[i].advanceX = charAdvanceX; font.chars[i].advanceX = charAdvanceX;
font.chars[i].data = NULL;
} }
fclose(fntFile); fclose(fntFile);

View File

@ -182,7 +182,11 @@ Image LoadImage(const char *fileName)
{ {
Image image = { 0 }; Image image = { 0 };
#if defined(SUPPORT_FILEFORMAT_PNG)
if ((IsFileExtension(fileName, ".png")) if ((IsFileExtension(fileName, ".png"))
#else
if ((false)
#endif
#if defined(SUPPORT_FILEFORMAT_BMP) #if defined(SUPPORT_FILEFORMAT_BMP)
|| (IsFileExtension(fileName, ".bmp")) || (IsFileExtension(fileName, ".bmp"))
#endif #endif
@ -398,90 +402,6 @@ Texture2D LoadTextureFromImage(Image image)
return texture; return texture;
} }
// Load cubemap from image, multiple image cubemap layouts supported
TextureCubemap LoadTextureCubemap(Image image, int layoutType)
{
TextureCubemap cubemap = { 0 };
if (layoutType == CUBEMAP_AUTO_DETECT) // Try to automatically guess layout type
{
// Check image width/height to determine the type of cubemap provided
if (image.width > image.height)
{
if ((image.width/6) == image.height) { layoutType = CUBEMAP_LINE_HORIZONTAL; cubemap.width = image.width/6; }
else if ((image.width/4) == (image.height/3)) { layoutType = CUBEMAP_CROSS_FOUR_BY_THREE; cubemap.width = image.width/4; }
else if (image.width >= (int)((float)image.height*1.85f)) { layoutType = CUBEMAP_PANORAMA; cubemap.width = image.width/4; }
}
else if (image.height > image.width)
{
if ((image.height/6) == image.width) { layoutType = CUBEMAP_LINE_VERTICAL; cubemap.width = image.height/6; }
else if ((image.width/3) == (image.height/4)) { layoutType = CUBEMAP_CROSS_THREE_BY_FOUR; cubemap.width = image.width/3; }
}
cubemap.height = cubemap.width;
}
int size = cubemap.width;
if (layoutType != CUBEMAP_AUTO_DETECT)
{
//unsigned int dataSize = GetPixelDataSize(size, size, format);
//void *facesData = malloc(size*size*dataSize*6); // Get memory for 6 faces in a column
Image faces = { 0 }; // Vertical column image
Rectangle faceRecs[6] = { 0 }; // Face source rectangles
for (int i = 0; i < 6; i++) faceRecs[i] = (Rectangle){ 0, 0, size, size };
if (layoutType == CUBEMAP_LINE_VERTICAL)
{
faces = image;
for (int i = 0; i < 6; i++) faceRecs[i].y = size*i;
}
else if (layoutType == CUBEMAP_PANORAMA)
{
// TODO: Convert panorama image to square faces...
}
else
{
if (layoutType == CUBEMAP_LINE_HORIZONTAL) for (int i = 0; i < 6; i++) faceRecs[i].x = size*i;
else if (layoutType == CUBEMAP_CROSS_THREE_BY_FOUR)
{
faceRecs[0].x = size; faceRecs[0].y = size;
faceRecs[1].x = size; faceRecs[1].y = 3*size;
faceRecs[2].x = size; faceRecs[2].y = 0;
faceRecs[3].x = size; faceRecs[3].y = 2*size;
faceRecs[4].x = 0; faceRecs[4].y = size;
faceRecs[5].x = 2*size; faceRecs[5].y = size;
}
else if (layoutType == CUBEMAP_CROSS_FOUR_BY_THREE)
{
faceRecs[0].x = 2*size; faceRecs[0].y = size;
faceRecs[1].x = 0; faceRecs[1].y = size;
faceRecs[2].x = size; faceRecs[2].y = 0;
faceRecs[3].x = size; faceRecs[3].y = 2*size;
faceRecs[4].x = size; faceRecs[4].y = size;
faceRecs[5].x = 3*size; faceRecs[5].y = size;
}
// Convert image data to 6 faces in a vertical column, that's the optimum layout for loading
faces = GenImageColor(size, size*6, MAGENTA);
ImageFormat(&faces, image.format);
// TODO: Image formating does not work with compressed textures!
}
for (int i = 0; i < 6; i++) ImageDraw(&faces, image, faceRecs[i], (Rectangle){ 0, size*i, size, size });
cubemap.id = rlLoadTextureCubemap(faces.data, size, faces.format);
if (cubemap.id == 0) TraceLog(LOG_WARNING, "Cubemap image could not be loaded.");
UnloadImage(faces);
}
else TraceLog(LOG_WARNING, "Cubemap image layout can not be detected.");
return cubemap;
}
// Load texture for rendering (framebuffer) // Load texture for rendering (framebuffer)
// NOTE: Render texture is loaded by default with RGBA color attachment and depth RenderBuffer // NOTE: Render texture is loaded by default with RGBA color attachment and depth RenderBuffer
RenderTexture2D LoadRenderTexture(int width, int height) RenderTexture2D LoadRenderTexture(int width, int height)
@ -825,14 +745,27 @@ void ExportImage(Image image, const char *fileName)
{ {
int success = 0; int success = 0;
#if defined(SUPPORT_IMAGE_EXPORT)
// 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 defined(SUPPORT_FILEFORMAT_PNG)
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 (false) {}
#endif
#if defined(SUPPORT_FILEFORMAT_BMP)
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);
#endif
#if defined(SUPPORT_FILEFORMAT_TGA)
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);
#endif
#if defined(SUPPORT_FILEFORMAT_JPG)
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
#endif
#if defined(SUPPORT_FILEFORMAT_KTX)
else if (IsFileExtension(fileName, ".ktx")) success = SaveKTX(image, fileName); else if (IsFileExtension(fileName, ".ktx")) success = SaveKTX(image, fileName);
#endif
else if (IsFileExtension(fileName, ".raw")) else if (IsFileExtension(fileName, ".raw"))
{ {
// Export raw pixel data (without header) // Export raw pixel data (without header)
@ -842,10 +775,11 @@ void ExportImage(Image image, const char *fileName)
fclose(rawFile); fclose(rawFile);
} }
free(imgData);
#endif
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);
} }
// Export image as code file (.h) defining an array of bytes // Export image as code file (.h) defining an array of bytes
@ -1133,7 +1067,9 @@ void ImageFormat(Image *image, int newFormat)
if (image->mipmaps > 1) if (image->mipmaps > 1)
{ {
image->mipmaps = 1; image->mipmaps = 1;
#if defined(SUPPORT_IMAGE_MANIPULATION)
if (image->data != NULL) ImageMipmaps(image); if (image->data != NULL) ImageMipmaps(image);
#endif
} }
} }
else TraceLog(LOG_WARNING, "Image data format is compressed, can not be converted"); else TraceLog(LOG_WARNING, "Image data format is compressed, can not be converted");
@ -1202,38 +1138,6 @@ void ImageAlphaClear(Image *image, Color color, float threshold)
ImageFormat(image, prevFormat); ImageFormat(image, prevFormat);
} }
// Crop image depending on alpha value
void ImageAlphaCrop(Image *image, float threshold)
{
Color *pixels = GetImageData(*image);
int xMin = 65536; // Define a big enough number
int xMax = 0;
int yMin = 65536;
int yMax = 0;
for (int y = 0; y < image->height; y++)
{
for (int x = 0; x < image->width; x++)
{
if (pixels[y*image->width + x].a > (unsigned char)(threshold*255.0f))
{
if (x < xMin) xMin = x;
if (x > xMax) xMax = x;
if (y < yMin) yMin = y;
if (y > yMax) yMax = y;
}
}
}
Rectangle crop = { xMin, yMin, (xMax + 1) - xMin, (yMax + 1) - yMin };
free(pixels);
// Check for not empty image brefore cropping
if (!((xMax < xMin) || (yMax < yMin))) ImageCrop(image, crop);
}
// Premultiply alpha channel // Premultiply alpha channel
void ImageAlphaPremultiply(Image *image) void ImageAlphaPremultiply(Image *image)
{ {
@ -1259,6 +1163,90 @@ void ImageAlphaPremultiply(Image *image)
#if defined(SUPPORT_IMAGE_MANIPULATION) #if defined(SUPPORT_IMAGE_MANIPULATION)
// Load cubemap from image, multiple image cubemap layouts supported
TextureCubemap LoadTextureCubemap(Image image, int layoutType)
{
TextureCubemap cubemap = { 0 };
if (layoutType == CUBEMAP_AUTO_DETECT) // Try to automatically guess layout type
{
// Check image width/height to determine the type of cubemap provided
if (image.width > image.height)
{
if ((image.width/6) == image.height) { layoutType = CUBEMAP_LINE_HORIZONTAL; cubemap.width = image.width/6; }
else if ((image.width/4) == (image.height/3)) { layoutType = CUBEMAP_CROSS_FOUR_BY_THREE; cubemap.width = image.width/4; }
else if (image.width >= (int)((float)image.height*1.85f)) { layoutType = CUBEMAP_PANORAMA; cubemap.width = image.width/4; }
}
else if (image.height > image.width)
{
if ((image.height/6) == image.width) { layoutType = CUBEMAP_LINE_VERTICAL; cubemap.width = image.height/6; }
else if ((image.width/3) == (image.height/4)) { layoutType = CUBEMAP_CROSS_THREE_BY_FOUR; cubemap.width = image.width/3; }
}
cubemap.height = cubemap.width;
}
int size = cubemap.width;
if (layoutType != CUBEMAP_AUTO_DETECT)
{
//unsigned int dataSize = GetPixelDataSize(size, size, format);
//void *facesData = malloc(size*size*dataSize*6); // Get memory for 6 faces in a column
Image faces = { 0 }; // Vertical column image
Rectangle faceRecs[6] = { 0 }; // Face source rectangles
for (int i = 0; i < 6; i++) faceRecs[i] = (Rectangle){ 0, 0, size, size };
if (layoutType == CUBEMAP_LINE_VERTICAL)
{
faces = image;
for (int i = 0; i < 6; i++) faceRecs[i].y = size*i;
}
else if (layoutType == CUBEMAP_PANORAMA)
{
// TODO: Convert panorama image to square faces...
}
else
{
if (layoutType == CUBEMAP_LINE_HORIZONTAL) for (int i = 0; i < 6; i++) faceRecs[i].x = size*i;
else if (layoutType == CUBEMAP_CROSS_THREE_BY_FOUR)
{
faceRecs[0].x = size; faceRecs[0].y = size;
faceRecs[1].x = size; faceRecs[1].y = 3*size;
faceRecs[2].x = size; faceRecs[2].y = 0;
faceRecs[3].x = size; faceRecs[3].y = 2*size;
faceRecs[4].x = 0; faceRecs[4].y = size;
faceRecs[5].x = 2*size; faceRecs[5].y = size;
}
else if (layoutType == CUBEMAP_CROSS_FOUR_BY_THREE)
{
faceRecs[0].x = 2*size; faceRecs[0].y = size;
faceRecs[1].x = 0; faceRecs[1].y = size;
faceRecs[2].x = size; faceRecs[2].y = 0;
faceRecs[3].x = size; faceRecs[3].y = 2*size;
faceRecs[4].x = size; faceRecs[4].y = size;
faceRecs[5].x = 3*size; faceRecs[5].y = size;
}
// Convert image data to 6 faces in a vertical column, that's the optimum layout for loading
faces = GenImageColor(size, size*6, MAGENTA);
ImageFormat(&faces, image.format);
// TODO: Image formating does not work with compressed textures!
}
for (int i = 0; i < 6; i++) ImageDraw(&faces, image, faceRecs[i], (Rectangle){ 0, size*i, size, size });
cubemap.id = rlLoadTextureCubemap(faces.data, size, faces.format);
if (cubemap.id == 0) TraceLog(LOG_WARNING, "Cubemap image could not be loaded.");
UnloadImage(faces);
}
else TraceLog(LOG_WARNING, "Cubemap image layout can not be detected.");
return cubemap;
}
// Crop an image to area defined by a rectangle // Crop an image to area defined by a rectangle
// NOTE: Security checks are performed in case rectangle goes out of bounds // NOTE: Security checks are performed in case rectangle goes out of bounds
void ImageCrop(Image *image, Rectangle crop) void ImageCrop(Image *image, Rectangle crop)
@ -1309,6 +1297,38 @@ void ImageCrop(Image *image, Rectangle crop)
} }
} }
// Crop image depending on alpha value
void ImageAlphaCrop(Image *image, float threshold)
{
Color *pixels = GetImageData(*image);
int xMin = 65536; // Define a big enough number
int xMax = 0;
int yMin = 65536;
int yMax = 0;
for (int y = 0; y < image->height; y++)
{
for (int x = 0; x < image->width; x++)
{
if (pixels[y*image->width + x].a > (unsigned char)(threshold*255.0f))
{
if (x < xMin) xMin = x;
if (x > xMax) xMax = x;
if (y < yMin) yMin = y;
if (y > yMax) yMax = y;
}
}
}
Rectangle crop = { xMin, yMin, (xMax + 1) - xMin, (yMax + 1) - yMin };
free(pixels);
// Check for not empty image brefore cropping
if (!((xMax < xMin) || (yMax < yMin))) ImageCrop(image, crop);
}
// Resize and image to new size // Resize and image to new size
// NOTE: Uses stb default scaling filters (both bicubic): // NOTE: Uses stb default scaling filters (both bicubic):
// STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_CATMULLROM // STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_CATMULLROM
@ -1611,7 +1631,7 @@ Color *ImageExtractPalette(Image image, int maxPaletteSize, int *extractCount)
if (palCount >= maxPaletteSize) if (palCount >= maxPaletteSize)
{ {
i = image.width*image.height; // Finish palette get i = image.width*image.height; // Finish palette get
printf("WARNING: Image palette is greater than %i colors!\n", maxPaletteSize); TraceLog(LOG_WARNING, "Image palette is greater than %i colors!", maxPaletteSize);
} }
} }
} }
@ -2146,7 +2166,6 @@ void ImageColorReplace(Image *image, Color color, Color replace)
} }
#endif // SUPPORT_IMAGE_MANIPULATION #endif // SUPPORT_IMAGE_MANIPULATION
#if defined(SUPPORT_IMAGE_GENERATION)
// Generate image: plain color // Generate image: plain color
Image GenImageColor(int width, int height, Color color) Image GenImageColor(int width, int height, Color color)
{ {
@ -2161,6 +2180,7 @@ Image GenImageColor(int width, int height, Color color)
return image; return image;
} }
#if defined(SUPPORT_IMAGE_GENERATION)
// Generate image: vertical gradient // Generate image: vertical gradient
Image GenImageGradientV(int width, int height, Color top, Color bottom) Image GenImageGradientV(int width, int height, Color top, Color bottom)
{ {
@ -3173,12 +3193,14 @@ static int SaveKTX(Image image, const char *fileName)
{ {
KTXHeader ktxHeader; KTXHeader ktxHeader;
// KTX identifier (v2.2) // KTX identifier (v1.1)
//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 };
const char ktxIdentifier[12] = { 0xAB, 'K', 'T', 'X', ' ', '1', '1', 0xBB, '\r', '\n', 0x1A, '\n' };
// Get the image header // Get the image header
strcpy(ktxHeader.id, "«KTX 11»\r\n\x1A\n"); // KTX 1.1 signature strncpy(ktxHeader.id, ktxIdentifier, 12); // KTX 1.1 signature
ktxHeader.endianness = 0; ktxHeader.endianness = 0;
ktxHeader.glType = 0; // Obtained from image.format ktxHeader.glType = 0; // Obtained from image.format
ktxHeader.glTypeSize = 1; ktxHeader.glTypeSize = 1;