From 4d5d1e0434497d5755fb596675554a8cee74f9f8 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 5 Feb 2018 01:03:13 +0100 Subject: Added function LoadShaderCode() Allows loading of shader from text code string directly --- src/raylib.h | 3 ++- src/rlgl.c | 53 +++++++++++++++++++++++++++-------------------------- 2 files changed, 29 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/raylib.h b/src/raylib.h index 3d1f2a49..e3f30e43 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1044,7 +1044,8 @@ RLAPI RayHitInfo GetCollisionRayGround(Ray ray, float groundHeight); // Shader loading/unloading functions RLAPI char *LoadText(const char *fileName); // Load chars array from text file -RLAPI Shader LoadShader(char *vsFileName, char *fsFileName); // Load shader from files and bind default locations +RLAPI Shader LoadShader(const char *vsFileName, const char *fsFileName); // Load shader from files and bind default locations +RLAPI Shader LoadShaderCode(char *vsCode, char *fsCode); // Load shader from code strings and bind default locations RLAPI void UnloadShader(Shader shader); // Unload shader from GPU memory (VRAM) RLAPI Shader GetShaderDefault(void); // Get default shader diff --git a/src/rlgl.c b/src/rlgl.c index 63860f1c..cd68cbdc 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -2324,39 +2324,40 @@ char *LoadText(const char *fileName) // Load shader from files and bind default locations // NOTE: If shader string is NULL, using default vertex/fragment shaders -Shader LoadShader(char *vsFileName, char *fsFileName) +Shader LoadShader(const char *vsFileName, const char *fsFileName) { Shader shader = { 0 }; + + char *vShaderStr = NULL; + char *fShaderStr = NULL; + + if (vsFileName != NULL) vShaderStr = LoadText(vsFileName); + if (fsFileName != NULL) fShaderStr = LoadText(fsFileName); + shader = LoadShaderCode(vShaderStr, fShaderStr); + + if (vShaderStr != NULL) free(vShaderStr); + if (fShaderStr != NULL) free(fShaderStr); + + return shader; +} + +// Load shader from code strings +// NOTE: If shader string is NULL, using default vertex/fragment shaders +Shader LoadShaderCode(char *vsCode, char *fsCode) +{ + Shader shader = { 0 }; + // NOTE: All locations must be reseted to -1 (no location) for (int i = 0; i < MAX_SHADER_LOCATIONS; i++) shader.locs[i] = -1; - + #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) unsigned int vertexShaderId = defaultVShaderId; unsigned int fragmentShaderId = defaultFShaderId; - - if (vsFileName != NULL) - { - char *vShaderStr = LoadText(vsFileName); - - if (vShaderStr != NULL) - { - vertexShaderId = CompileShader(vShaderStr, GL_VERTEX_SHADER); - free(vShaderStr); - } - } - - if (fsFileName != NULL) - { - char* fShaderStr = LoadText(fsFileName); - - if (fShaderStr != NULL) - { - fragmentShaderId = CompileShader(fShaderStr, GL_FRAGMENT_SHADER); - free(fShaderStr); - } - } - + + if (vsCode != NULL) vertexShaderId = CompileShader(vsCode, GL_VERTEX_SHADER); + if (fsCode != NULL) fragmentShaderId = CompileShader(fsCode, GL_FRAGMENT_SHADER); + if ((vertexShaderId == defaultVShaderId) && (fragmentShaderId == defaultFShaderId)) shader = defaultShader; else { @@ -2399,7 +2400,7 @@ Shader LoadShader(char *vsFileName, char *fsFileName) TraceLog(LOG_DEBUG, "[SHDR ID %i] Active uniform [%s] set at location: %i", shader.id, name, location); } #endif - + return shader; } -- cgit v1.2.3 From a976e76ae644cb10002900519e2a72bd2706348f Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Thu, 8 Feb 2018 12:00:27 +0100 Subject: InitWindow: return false if no monitor found Otherwise we run into an assertion failure inside GLFW's glfwGetVideoMode. Example: http://www.cpantesters.org/cpan/report/b4ba5894-0bdb-11e8-841e-2c60b04e1d2d This is related to #456. --- src/core.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/core.c b/src/core.c index 29f9a23a..0a1c34c9 100644 --- a/src/core.c +++ b/src/core.c @@ -1733,7 +1733,13 @@ static bool InitGraphicsDevice(int width, int height) // NOTE: Getting video modes is not implemented in emscripten GLFW3 version #if defined(PLATFORM_DESKTOP) // Find monitor resolution - const GLFWvidmode *mode = glfwGetVideoMode(glfwGetPrimaryMonitor()); + GLFWmonitor *monitor = glfwGetPrimaryMonitor(); + if (!monitor) + { + TraceLog(LOG_WARNING, "Failed to get monitor"); + return false; + } + const GLFWvidmode *mode = glfwGetVideoMode(monitor); displayWidth = mode->width; displayHeight = mode->height; -- cgit v1.2.3 From f72b315cb66de68486a8cf41a2767902aceb9c67 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 9 Feb 2018 00:00:47 +0100 Subject: Updated external libraries --- src/external/mini_al.h | 59 ++-- src/external/stb_image.h | 596 ++++++++++++++++++++++++++++++----------- src/external/stb_image_write.h | 243 ++++++++++++----- src/external/stb_truetype.h | 107 +++++--- src/external/stb_vorbis.c | 20 +- src/external/stb_vorbis.h | 12 +- 6 files changed, 729 insertions(+), 308 deletions(-) (limited to 'src') diff --git a/src/external/mini_al.h b/src/external/mini_al.h index 906e3c59..35846385 100644 --- a/src/external/mini_al.h +++ b/src/external/mini_al.h @@ -1,5 +1,5 @@ // Audio playback and capture library. Public domain. See "unlicense" statement at the end of this file. -// mini_al - v0.x - xxxx-xx-xx +// mini_al - v0.6b - 2018-02-03 // // David Reid - davidreidsoftware@gmail.com @@ -1774,7 +1774,10 @@ typedef HWND (WINAPI * MAL_PFN_GetDesktopWindow)(); #define mal_buffer_frame_capacity(buffer, channels, format) (sizeof(buffer) / mal_get_sample_size_in_bytes(format) / (channels)) // Some of these string utility functions are unused on some platforms. -#if defined(__GNUC__) +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable:4505) +#elif defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-function" #endif @@ -1972,7 +1975,9 @@ static int mal_strcmp(const char* str1, const char* str2) return ((unsigned char*)str1)[0] - ((unsigned char*)str2)[0]; } -#if defined(__GNUC__) +#if defined(_MSC_VER) + #pragma warning(pop) +#elif defined(__GNUC__) #pragma GCC diagnostic pop #endif @@ -8843,6 +8848,8 @@ mal_result mal_device_init__sdl(mal_context* pContext, mal_device_type type, mal mal_assert(pConfig != NULL); mal_assert(pDevice != NULL); + (void)pContext; + // SDL wants the buffer size to be a power of 2. The SDL_AudioSpec property for this is only a Uint16, so we need // to explicitly clamp this because it will be easy to overflow. mal_uint32 bufferSize = pConfig->bufferSizeInFrames; @@ -10760,7 +10767,7 @@ static void mal_dsp_mix_channels__inc(float* pFramesOut, mal_uint32 channelsOut, (void)channelMapOut; (void)channelMapIn; - if (mode == mal_channel_mix_mode_basic) {\ + if (mode == mal_channel_mix_mode_basic) { // Basic mode is where we just zero out extra channels. for (mal_uint32 iFrame = 0; iFrame < frameCount; ++iFrame) { switch (channelsIn) { @@ -10785,23 +10792,23 @@ static void mal_dsp_mix_channels__inc(float* pFramesOut, mal_uint32 channelsOut, // Zero out extra channels. switch (channelsOut - channelsIn) { - case 17: pFramesOut[iFrame*channelsOut+16] = 0; - case 16: pFramesOut[iFrame*channelsOut+15] = 0; - case 15: pFramesOut[iFrame*channelsOut+14] = 0; - case 14: pFramesOut[iFrame*channelsOut+13] = 0; - case 13: pFramesOut[iFrame*channelsOut+12] = 0; - case 12: pFramesOut[iFrame*channelsOut+11] = 0; - case 11: pFramesOut[iFrame*channelsOut+10] = 0; - case 10: pFramesOut[iFrame*channelsOut+ 9] = 0; - case 9: pFramesOut[iFrame*channelsOut+ 8] = 0; - case 8: pFramesOut[iFrame*channelsOut+ 7] = 0; - case 7: pFramesOut[iFrame*channelsOut+ 6] = 0; - case 6: pFramesOut[iFrame*channelsOut+ 5] = 0; - case 5: pFramesOut[iFrame*channelsOut+ 4] = 0; - case 4: pFramesOut[iFrame*channelsOut+ 3] = 0; - case 3: pFramesOut[iFrame*channelsOut+ 2] = 0; - case 2: pFramesOut[iFrame*channelsOut+ 1] = 0; - case 1: pFramesOut[iFrame*channelsOut+ 0] = 0; + case 17: pFramesOut[iFrame*channelsOut+16 + channelsIn] = 0; + case 16: pFramesOut[iFrame*channelsOut+15 + channelsIn] = 0; + case 15: pFramesOut[iFrame*channelsOut+14 + channelsIn] = 0; + case 14: pFramesOut[iFrame*channelsOut+13 + channelsIn] = 0; + case 13: pFramesOut[iFrame*channelsOut+12 + channelsIn] = 0; + case 12: pFramesOut[iFrame*channelsOut+11 + channelsIn] = 0; + case 11: pFramesOut[iFrame*channelsOut+10 + channelsIn] = 0; + case 10: pFramesOut[iFrame*channelsOut+ 9 + channelsIn] = 0; + case 9: pFramesOut[iFrame*channelsOut+ 8 + channelsIn] = 0; + case 8: pFramesOut[iFrame*channelsOut+ 7 + channelsIn] = 0; + case 7: pFramesOut[iFrame*channelsOut+ 6 + channelsIn] = 0; + case 6: pFramesOut[iFrame*channelsOut+ 5 + channelsIn] = 0; + case 5: pFramesOut[iFrame*channelsOut+ 4 + channelsIn] = 0; + case 4: pFramesOut[iFrame*channelsOut+ 3 + channelsIn] = 0; + case 3: pFramesOut[iFrame*channelsOut+ 2 + channelsIn] = 0; + case 2: pFramesOut[iFrame*channelsOut+ 1 + channelsIn] = 0; + case 1: pFramesOut[iFrame*channelsOut+ 0 + channelsIn] = 0; } } } else { @@ -10832,10 +10839,10 @@ static void mal_dsp_mix_channels__inc(float* pFramesOut, mal_uint32 channelsOut, } } else if (channelsIn == 2) { // TODO: Implement an optimized stereo conversion. - mal_dsp_mix_channels__dec(pFramesOut, channelsOut, channelMapOut, pFramesIn, channelsIn, channelMapIn, frameCount, mal_channel_mix_mode_basic); + mal_dsp_mix_channels__inc(pFramesOut, channelsOut, channelMapOut, pFramesIn, channelsIn, channelMapIn, frameCount, mal_channel_mix_mode_basic); } else { // Fall back to basic mixing mode. - mal_dsp_mix_channels__dec(pFramesOut, channelsOut, channelMapOut, pFramesIn, channelsIn, channelMapIn, frameCount, mal_channel_mix_mode_basic); + mal_dsp_mix_channels__inc(pFramesOut, channelsOut, channelMapOut, pFramesIn, channelsIn, channelMapIn, frameCount, mal_channel_mix_mode_basic); } } } @@ -11494,7 +11501,11 @@ void mal_pcm_f32_to_s32(int* pOut, const float* pIn, unsigned int count) // REVISION HISTORY // ================ // -// v0.x - xxxx-xx-xx +// v0.6b - 2018-02-03 +// - Fix some warnings when compiling with Visual C++. +// +// v0.6a - 2018-01-26 +// - Fix errors with channel mixing when increasing the channel count. // - Improvements to the build system for the OpenAL backend. // - Documentation fixes. // diff --git a/src/external/stb_image.h b/src/external/stb_image.h index a056138d..f090ed3a 100644 --- a/src/external/stb_image.h +++ b/src/external/stb_image.h @@ -1,5 +1,5 @@ -/* stb_image - v2.16 - public domain image loader - http://nothings.org/stb_image.h - no warranty implied; use at your own risk +/* stb_image - v2.18 - public domain image loader - http://nothings.org/stb + no warranty implied; use at your own risk Do this: #define STB_IMAGE_IMPLEMENTATION @@ -48,6 +48,8 @@ LICENSE RECENT REVISION HISTORY: + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs @@ -74,11 +76,11 @@ RECENT REVISION HISTORY: Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) github:urraka (animated gif) Junggon Kim (PNM comments) - Daniel Gibson (16-bit TGA) + Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) socks-the-fox (16-bit PNG) Jeremy Sawicki (handle all ImageNet JPGs) - Optimizations & bugfixes - Fabian "ryg" Giesen + Optimizations & bugfixes Mikhail Morozov (1-bit BMP) + Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) Arseny Kapoulkine John-Mark Allen @@ -87,16 +89,17 @@ RECENT REVISION HISTORY: Christpher Lloyd Jerry Jansson Joseph Thomson Phil Jordan Dave Moore Roy Eltham Hayaki Saito Nathan Reed Won Chun Luke Graham Johan Duparc Nick Verigakis - the Horde3D community Thomas Ruf Ronny Chevalier Baldur Karlsson - Janez Zemva John Bartholomew Michal Cichon github:rlyeh - Jonathan Blow Ken Hamada Tero Hanninen github:romigrou - Laurent Gomila Cort Stratton Sergio Gonzalez github:svdijk - Aruelien Pocheville Thibault Reuille Cass Everitt github:snagar - Ryamond Barbiero Paul Du Bois Engin Manap github:Zelex - Michaelangel007@github Philipp Wiesemann Dale Weiler github:grim210 - Oriol Ferrer Mesia Josh Tobin Matthew Gregan github:sammyhw - Blazej Dariusz Roszkowski Gregory Mullen github:phprus - Christian Floisand Kevin Schmidt github:poppolopoppo + the Horde3D community Thomas Ruf Ronny Chevalier github:rlyeh + Janez Zemva John Bartholomew Michal Cichon github:romigrou + Jonathan Blow Ken Hamada Tero Hanninen github:svdijk + Laurent Gomila Cort Stratton Sergio Gonzalez github:snagar + Aruelien Pocheville Thibault Reuille Cass Everitt github:Zelex + Ryamond Barbiero Paul Du Bois Engin Manap github:grim210 + Aldo Culquicondor Philipp Wiesemann Dale Weiler github:sammyhw + Oriol Ferrer Mesia Josh Tobin Matthew Gregan github:phprus + Julian Raschke Gregory Mullen Baldur Karlsson github:poppolopoppo + Christian Floisand Kevin Schmidt github:darealshinji + Blazej Dariusz Roszkowski github:Michaelangel007 */ #ifndef STBI_INCLUDE_STB_IMAGE_H @@ -105,10 +108,8 @@ RECENT REVISION HISTORY: // DOCUMENTATION // // Limitations: -// - no 16-bit-per-channel PNG // - no 12-bit-per-channel JPEG // - no JPEGs with arithmetic coding -// - no 1-bit BMP // - GIF always returns *comp=4 // // Basic usage (see HDR discussion below for HDR usage): @@ -353,6 +354,10 @@ typedef struct STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +#endif + #ifndef STBI_NO_STDIO STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); @@ -416,11 +421,14 @@ STBIDEF void stbi_image_free (void *retval_from_stbi_load); // get image dimensions & components without fully decoding STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); #ifndef STBI_NO_STDIO -STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); -STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); - +STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit (char const *filename); +STBIDEF int stbi_is_16_bit_from_file(FILE *f); #endif @@ -504,7 +512,7 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const ch #include #if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -#include // ldexp +#include // ldexp, pow #endif #ifndef STBI_NO_STDIO @@ -784,6 +792,7 @@ static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); static int stbi__png_test(stbi__context *s); static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__png_is16(stbi__context *s); #endif #ifndef STBI_NO_BMP @@ -802,6 +811,7 @@ static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); static int stbi__psd_test(stbi__context *s); static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__psd_is16(stbi__context *s); #endif #ifndef STBI_NO_HDR @@ -819,6 +829,7 @@ static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); #ifndef STBI_NO_GIF static int stbi__gif_test(stbi__context *s); static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); #endif @@ -893,11 +904,13 @@ static int stbi__mad3sizes_valid(int a, int b, int c, int add) } // returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) { return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); } +#endif // mallocs with size overflow checking static void *stbi__malloc_mad2(int a, int b, int add) @@ -912,11 +925,13 @@ static void *stbi__malloc_mad3(int a, int b, int c, int add) return stbi__malloc(a*b*c + add); } +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) { if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; return stbi__malloc(a*b*c*d + add); } +#endif // stbi__err - error // stbi__errpf - error returning pointer to float @@ -1054,6 +1069,18 @@ static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) } } +static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) +{ + int slice; + int slice_size = w * h * bytes_per_pixel; + + stbi_uc *bytes = (stbi_uc *)image; + for (slice = 0; slice < z; ++slice) { + stbi__vertical_flip(bytes, w, h, bytes_per_pixel); + bytes += slice_size; + } +} + static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) { stbi__result_info ri; @@ -1103,7 +1130,7 @@ static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, return (stbi__uint16 *) result; } -#ifndef STBI_NO_HDR +#if !defined(STBI_NO_HDR) || !defined(STBI_NO_LINEAR) static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) { if (stbi__vertically_flip_on_load && result != NULL) { @@ -1205,6 +1232,22 @@ STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *u return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); } +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_mem(&s,buffer,len); + + result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); + if (stbi__vertically_flip_on_load) { + stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); + } + + return result; +} +#endif + #ifndef STBI_NO_LINEAR static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) { @@ -1288,12 +1331,16 @@ STBIDEF int stbi_is_hdr (char const *filename) return result; } -STBIDEF int stbi_is_hdr_from_file(FILE *f) +STBIDEF int stbi_is_hdr_from_file(FILE *f) { #ifndef STBI_NO_HDR + long pos = ftell(f); + int res; stbi__context s; stbi__start_file(&s,f); - return stbi__hdr_test(&s); + res = stbi__hdr_test(&s); + fseek(f, pos, SEEK_SET); + return res; #else STBI_NOTUSED(f); return 0; @@ -1705,7 +1752,8 @@ typedef struct static int stbi__build_huffman(stbi__huffman *h, int *count) { - int i,j,k=0,code; + int i,j,k=0; + unsigned int code; // build size list for each symbol (from JPEG spec) for (i=0; i < 16; ++i) for (j=0; j < count[i]; ++j) @@ -1721,7 +1769,7 @@ static int stbi__build_huffman(stbi__huffman *h, int *count) if (h->size[k] == j) { while (h->size[k] == j) h->code[k++] = (stbi__uint16) (code++); - if (code-1 >= (1 << j)) return stbi__err("bad code lengths","Corrupt JPEG"); + if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); } // compute largest code + 1 for this size, preshifted as needed later h->maxcode[j] = code << (16-j); @@ -1765,7 +1813,7 @@ static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) if (k < m) k += (~0U << magbits) + 1; // if the result is small enough, we can fit it in fast_ac table if (k >= -128 && k <= 127) - fast_ac[i] = (stbi__int16) ((k << 8) + (run << 4) + (len + magbits)); + fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); } } } @@ -1774,7 +1822,7 @@ static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) static void stbi__grow_buffer_unsafe(stbi__jpeg *j) { do { - int b = j->nomore ? 0 : stbi__get8(j->s); + unsigned int b = j->nomore ? 0 : stbi__get8(j->s); if (b == 0xff) { int c = stbi__get8(j->s); while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes @@ -1790,7 +1838,7 @@ static void stbi__grow_buffer_unsafe(stbi__jpeg *j) } // (1 << n) - 1 -static stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; +static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; // decode a jpeg huffman value from the bitstream stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) @@ -1843,7 +1891,7 @@ stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) } // bias[n] = (-1<rgb = 0; for (i=0; i < s->img_n; ++i) { - static unsigned char rgb[3] = { 'R', 'G', 'B' }; + static const unsigned char rgb[3] = { 'R', 'G', 'B' }; z->img_comp[i].id = stbi__get8(s); if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) ++z->rgb; @@ -3093,8 +3141,8 @@ static int stbi__decode_jpeg_image(stbi__jpeg *j) } else if (stbi__DNL(m)) { int Ld = stbi__get16be(j->s); stbi__uint32 NL = stbi__get16be(j->s); - if (Ld != 4) stbi__err("bad DNL len", "Corrupt JPEG"); - if (NL != j->s->img_y) stbi__err("bad DNL height", "Corrupt JPEG"); + if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); + if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); } else { if (!stbi__process_marker(j, m)) return 0; } @@ -3912,18 +3960,18 @@ static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room return 1; } -static int stbi__zlength_base[31] = { +static const int stbi__zlength_base[31] = { 3,4,5,6,7,8,9,10,11,13, 15,17,19,23,27,31,35,43,51,59, 67,83,99,115,131,163,195,227,258,0,0 }; -static int stbi__zlength_extra[31]= +static const int stbi__zlength_extra[31]= { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; -static int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, 257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; -static int stbi__zdist_extra[32] = +static const int stbi__zdist_extra[32] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; static int stbi__parse_huffman_block(stbi__zbuf *a) @@ -3970,7 +4018,7 @@ static int stbi__parse_huffman_block(stbi__zbuf *a) static int stbi__compute_huffman_codes(stbi__zbuf *a) { - static stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; stbi__zhuffman z_codelength; stbi_uc lencodes[286+32+137];//padding for maximum single op stbi_uc codelength_sizes[19]; @@ -4229,7 +4277,7 @@ static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) static int stbi__check_png_header(stbi__context *s) { - static stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; + static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; int i; for (i=0; i < 8; ++i) if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); @@ -4275,7 +4323,7 @@ static int stbi__paeth(int a, int b, int c) return c; } -static stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; +static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; // create the png data from post-deflated data static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) @@ -4295,8 +4343,10 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into if (!a->out) return stbi__err("outofmem", "Out of memory"); + if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); img_width_bytes = (((img_n * x * depth) + 7) >> 3); img_len = (img_width_bytes + 1) * y; + // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), // so just check for raw_len < img_len always. @@ -4675,7 +4725,7 @@ static void stbi__de_iphone(stbi__png *z) } } -#define STBI__PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) +#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) { @@ -4912,6 +4962,19 @@ static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) p.s = s; return stbi__png_info_raw(&p, x, y, comp); } + +static int stbi__png_is16(stbi__context *s) +{ + stbi__png p; + p.s = s; + if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) + return 0; + if (p.depth != 16) { + stbi__rewind(p.s); + return 0; + } + return 1; +} #endif // Microsoft/Windows BMP image @@ -4963,21 +5026,27 @@ static int stbi__bitcount(unsigned int a) return a & 0xff; } +// extract an arbitrarily-aligned N-bit value (N=bits) +// from v, and then make it 8-bits long and fractionally +// extend it to full full range. static int stbi__shiftsigned(int v, int shift, int bits) { - int result; - int z=0; - - if (shift < 0) v <<= -shift; - else v >>= shift; - result = v; - - z = bits; - while (z < 8) { - result += v >> z; - z += bits; - } - return result; + static unsigned int mul_table[9] = { + 0, + 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, + 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, + }; + static unsigned int shift_table[9] = { + 0, 0,0,1,0,2,4,6,0, + }; + if (shift < 0) + v <<= -shift; + else + v >>= shift; + STBI_ASSERT(v >= 0 && v < 256); + v >>= (8-bits); + STBI_ASSERT(bits >= 0 && bits <= 8); + return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; } typedef struct @@ -5007,7 +5076,6 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) } if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); info->bpp = stbi__get16le(s); - if (info->bpp == 1) return stbi__errpuc("monochrome", "BMP type not supported: 1-bit"); if (hsz != 12) { int compress = stbi__get32le(s); if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); @@ -5125,29 +5193,47 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req pal[i][3] = 255; } stbi__skip(s, info.offset - 14 - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); - if (info.bpp == 4) width = (s->img_x + 1) >> 1; + if (info.bpp == 1) width = (s->img_x + 7) >> 3; + else if (info.bpp == 4) width = (s->img_x + 1) >> 1; else if (info.bpp == 8) width = s->img_x; else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } pad = (-width)&3; - for (j=0; j < (int) s->img_y; ++j) { - for (i=0; i < (int) s->img_x; i += 2) { - int v=stbi__get8(s),v2=0; - if (info.bpp == 4) { - v2 = v & 15; - v >>= 4; + if (info.bpp == 1) { + for (j=0; j < (int) s->img_y; ++j) { + int bit_offset = 7, v = stbi__get8(s); + for (i=0; i < (int) s->img_x; ++i) { + int color = (v>>bit_offset)&0x1; + out[z++] = pal[color][0]; + out[z++] = pal[color][1]; + out[z++] = pal[color][2]; + if((--bit_offset) < 0) { + bit_offset = 7; + v = stbi__get8(s); + } } - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - if (i+1 == (int) s->img_x) break; - v = (info.bpp == 8) ? stbi__get8(s) : v2; - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; + stbi__skip(s, pad); + } + } else { + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=stbi__get8(s),v2=0; + if (info.bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (info.bpp == 8) ? stbi__get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + stbi__skip(s, pad); } - stbi__skip(s, pad); } } else { int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; @@ -5188,7 +5274,7 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req int bpp = info.bpp; for (i=0; i < (int) s->img_x; ++i) { stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); - int a; + unsigned int a; out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); @@ -5236,14 +5322,14 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) { // only RGB or RGBA (incl. 16bit) or grey allowed - if(is_rgb16) *is_rgb16 = 0; + if (is_rgb16) *is_rgb16 = 0; switch(bits_per_pixel) { case 8: return STBI_grey; case 16: if(is_grey) return STBI_grey_alpha; - // else: fall-through + // fallthrough case 15: if(is_rgb16) *is_rgb16 = 1; - return STBI_rgb; - case 24: // fall-through + return STBI_rgb; + case 24: // fallthrough case 32: return bits_per_pixel/8; default: return 0; } @@ -6038,11 +6124,13 @@ typedef struct typedef struct { int w,h; - stbi_uc *out, *old_out; // output buffer (always 4 components) - int flags, bgindex, ratio, transparent, eflags, delay; + stbi_uc *out; // output buffer (always 4 components) + stbi_uc *background; // The current "background" as far as a gif is concerned + stbi_uc *history; + int flags, bgindex, ratio, transparent, eflags; stbi_uc pal[256][4]; stbi_uc lpal[256][4]; - stbi__gif_lzw codes[4096]; + stbi__gif_lzw codes[8192]; stbi_uc *color_table; int parse, step; int lflags; @@ -6050,6 +6138,7 @@ typedef struct int max_x, max_y; int cur_x, cur_y; int line_size; + int delay; } stbi__gif; static int stbi__gif_test_raw(stbi__context *s) @@ -6125,6 +6214,7 @@ static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) { stbi_uc *p, *c; + int idx; // recurse to decode the prefixes, since the linked-list is backwards, // and working backwards through an interleaved image would be nasty @@ -6133,10 +6223,12 @@ static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) if (g->cur_y >= g->max_y) return; - p = &g->out[g->cur_x + g->cur_y]; - c = &g->color_table[g->codes[code].suffix * 4]; + idx = g->cur_x + g->cur_y; + p = &g->out[idx]; + g->history[idx / 4] = 1; - if (c[3] >= 128) { + c = &g->color_table[g->codes[code].suffix * 4]; + if (c[3] > 128) { // don't render transparent pixels; p[0] = c[2]; p[1] = c[1]; p[2] = c[0]; @@ -6210,11 +6302,16 @@ static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) stbi__skip(s,len); return g->out; } else if (code <= avail) { - if (first) return stbi__errpuc("no clear code", "Corrupt GIF"); + if (first) { + return stbi__errpuc("no clear code", "Corrupt GIF"); + } if (oldcode >= 0) { p = &g->codes[avail++]; - if (avail > 4096) return stbi__errpuc("too many codes", "Corrupt GIF"); + if (avail > 8192) { + return stbi__errpuc("too many codes", "Corrupt GIF"); + } + p->prefix = (stbi__int16) oldcode; p->first = g->codes[oldcode].first; p->suffix = (code == avail) ? p->first : g->codes[code].first; @@ -6236,62 +6333,72 @@ static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) } } -static void stbi__fill_gif_background(stbi__gif *g, int x0, int y0, int x1, int y1) -{ - int x, y; - stbi_uc *c = g->pal[g->bgindex]; - for (y = y0; y < y1; y += 4 * g->w) { - for (x = x0; x < x1; x += 4) { - stbi_uc *p = &g->out[y + x]; - p[0] = c[2]; - p[1] = c[1]; - p[2] = c[0]; - p[3] = 0; - } - } -} - // this function is designed to support animated gifs, although stb_image doesn't support it -static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp) -{ - int i; - stbi_uc *prev_out = 0; - - if (g->out == 0 && !stbi__gif_header(s, g, comp,0)) - return 0; // stbi__g_failure_reason set by stbi__gif_header - - if (!stbi__mad3sizes_valid(g->w, g->h, 4, 0)) - return stbi__errpuc("too large", "GIF too large"); +// two back is the image from two frames ago, used for a very specific disposal format +static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) +{ + int dispose; + int first_frame; + int pi; + int pcount; + + // on first frame, any non-written pixels get the background colour (non-transparent) + first_frame = 0; + if (g->out == 0) { + if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header + g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); + g->background = (stbi_uc *) stbi__malloc(4 * g->w * g->h); + g->history = (stbi_uc *) stbi__malloc(g->w * g->h); + if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); + + // image is treated as "tranparent" at the start - ie, nothing overwrites the current background; + // background colour is only used for pixels that are not rendered first frame, after that "background" + // color refers to teh color that was there the previous frame. + memset( g->out, 0x00, 4 * g->w * g->h ); + memset( g->background, 0x00, 4 * g->w * g->h ); // state of the background (starts transparent) + memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame + first_frame = 1; + } else { + // second frame - how do we dispoase of the previous one? + dispose = (g->eflags & 0x1C) >> 2; + pcount = g->w * g->h; - prev_out = g->out; - g->out = (stbi_uc *) stbi__malloc_mad3(4, g->w, g->h, 0); - if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); + if ((dispose == 3) && (two_back == 0)) { + dispose = 2; // if I don't have an image to revert back to, default to the old background + } - switch ((g->eflags & 0x1C) >> 2) { - case 0: // unspecified (also always used on 1st frame) - stbi__fill_gif_background(g, 0, 0, 4 * g->w, 4 * g->w * g->h); - break; - case 1: // do not dispose - if (prev_out) memcpy(g->out, prev_out, 4 * g->w * g->h); - g->old_out = prev_out; - break; - case 2: // dispose to background - if (prev_out) memcpy(g->out, prev_out, 4 * g->w * g->h); - stbi__fill_gif_background(g, g->start_x, g->start_y, g->max_x, g->max_y); - break; - case 3: // dispose to previous - if (g->old_out) { - for (i = g->start_y; i < g->max_y; i += 4 * g->w) - memcpy(&g->out[i + g->start_x], &g->old_out[i + g->start_x], g->max_x - g->start_x); + if (dispose == 3) { // use previous graphic + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); + } } - break; + } else if (dispose == 2) { + // restore what was changed last frame to background before that frame; + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); + } + } + } else { + // This is a non-disposal case eithe way, so just + // leave the pixels as is, and they will become the new background + // 1: do not dispose + // 0: not specified. + } + + // background is what out is after the undoing of the previou frame; + memcpy( g->background, g->out, 4 * g->w * g->h ); } + // clear my history; + memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame + for (;;) { - switch (stbi__get8(s)) { + int tag = stbi__get8(s); + switch (tag) { case 0x2C: /* Image Descriptor */ { - int prev_trans = -1; stbi__int32 x, y, w, h; stbi_uc *o; @@ -6324,19 +6431,24 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); g->color_table = (stbi_uc *) g->lpal; } else if (g->flags & 0x80) { - if (g->transparent >= 0 && (g->eflags & 0x01)) { - prev_trans = g->pal[g->transparent][3]; - g->pal[g->transparent][3] = 0; - } g->color_table = (stbi_uc *) g->pal; } else - return stbi__errpuc("missing color table", "Corrupt GIF"); - + return stbi__errpuc("missing color table", "Corrupt GIF"); + o = stbi__process_gif_raster(s, g); if (o == NULL) return NULL; - if (prev_trans != -1) - g->pal[g->transparent][3] = (stbi_uc) prev_trans; + // if this was the first frame, + pcount = g->w * g->h; + if (first_frame && (g->bgindex > 0)) { + // if first frame, any pixel not drawn to gets the background color + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi] == 0) { + g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; + memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); + } + } + } return o; } @@ -6344,19 +6456,35 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i case 0x21: // Comment Extension. { int len; - if (stbi__get8(s) == 0xF9) { // Graphic Control Extension. + int ext = stbi__get8(s); + if (ext == 0xF9) { // Graphic Control Extension. len = stbi__get8(s); if (len == 4) { g->eflags = stbi__get8(s); - g->delay = stbi__get16le(s); - g->transparent = stbi__get8(s); + g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. + + // unset old transparent + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 255; + } + if (g->eflags & 0x01) { + g->transparent = stbi__get8(s); + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 0; + } + } else { + // don't need transparent + stbi__skip(s, 1); + g->transparent = -1; + } } else { stbi__skip(s, len); break; } - } - while ((len = stbi__get8(s)) != 0) + } + while ((len = stbi__get8(s)) != 0) { stbi__skip(s, len); + } break; } @@ -6367,28 +6495,92 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i return stbi__errpuc("unknown code", "Corrupt GIF"); } } +} + +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + if (stbi__gif_test(s)) { + int layers = 0; + stbi_uc *u = 0; + stbi_uc *out = 0; + stbi_uc *two_back = 0; + stbi__gif g; + int stride; + memset(&g, 0, sizeof(g)); + if (delays) { + *delays = 0; + } + + do { + u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + + if (u) { + *x = g.w; + *y = g.h; + ++layers; + stride = g.w * g.h * 4; + + if (out) { + out = (stbi_uc*) STBI_REALLOC( out, layers * stride ); + if (delays) { + *delays = (int*) STBI_REALLOC( *delays, sizeof(int) * layers ); + } + } else { + out = (stbi_uc*)stbi__malloc( layers * stride ); + if (delays) { + *delays = (int*) stbi__malloc( layers * sizeof(int) ); + } + } + memcpy( out + ((layers - 1) * stride), u, stride ); + if (layers >= 2) { + two_back = out - 2 * stride; + } + + if (delays) { + (*delays)[layers - 1U] = g.delay; + } + } + } while (u != 0); + + // free temp buffer; + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); - STBI_NOTUSED(req_comp); + // do the final conversion after loading everything; + if (req_comp && req_comp != 4) + out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); + + *z = layers; + return out; + } else { + return stbi__errpuc("not GIF", "Image was not as a gif type."); + } } static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { stbi_uc *u = 0; - stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); - memset(g, 0, sizeof(*g)); - STBI_NOTUSED(ri); + stbi__gif g; + memset(&g, 0, sizeof(g)); - u = stbi__gif_load_next(s, g, comp, req_comp); + u = stbi__gif_load_next(s, &g, comp, req_comp, 0); if (u == (stbi_uc *) s) u = 0; // end of animated gif marker if (u) { - *x = g->w; - *y = g->h; + *x = g.w; + *y = g.h; + + // moved conversion to after successful load so that the same + // can be done for multiple frames. if (req_comp && req_comp != 4) - u = stbi__convert_format(u, 4, req_comp, g->w, g->h); + u = stbi__convert_format(u, 4, req_comp, g.w, g.h); } - else if (g->out) - STBI_FREE(g->out); - STBI_FREE(g); + + // free buffers needed for multiple frame loading; + STBI_FREE(g.history); + STBI_FREE(g.background); + return u; } @@ -6667,7 +6859,7 @@ static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) #ifndef STBI_NO_PSD static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) { - int channelCount, dummy; + int channelCount, dummy, depth; if (!x) x = &dummy; if (!y) y = &dummy; if (!comp) comp = &dummy; @@ -6687,7 +6879,8 @@ static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) } *y = stbi__get32be(s); *x = stbi__get32be(s); - if (stbi__get16be(s) != 8) { + depth = stbi__get16be(s); + if (depth != 8 && depth != 16) { stbi__rewind( s ); return 0; } @@ -6698,6 +6891,33 @@ static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) *comp = 4; return 1; } + +static int stbi__psd_is16(stbi__context *s) +{ + int channelCount, dummy, depth; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + dummy = stbi__get32be(s); + dummy = stbi__get32be(s); + depth = stbi__get16be(s); + if (depth != 16) { + stbi__rewind( s ); + return 0; + } + return 1; +} #endif #ifndef STBI_NO_PIC @@ -6928,6 +7148,19 @@ static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) return stbi__err("unknown image type", "Image not of any known type, or corrupt"); } +static int stbi__is_16_main(stbi__context *s) +{ + #ifndef STBI_NO_PNG + if (stbi__png_is16(s)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_is16(s)) return 1; + #endif + + return 0; +} + #ifndef STBI_NO_STDIO STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) { @@ -6949,6 +7182,27 @@ STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) fseek(f,pos,SEEK_SET); return r; } + +STBIDEF int stbi_is_16_bit(char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_is_16_bit_from_file(f); + fclose(f); + return result; +} + +STBIDEF int stbi_is_16_bit_from_file(FILE *f) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__is_16_main(&s); + fseek(f,pos,SEEK_SET); + return r; +} #endif // !STBI_NO_STDIO STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) @@ -6965,10 +7219,28 @@ STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int return stbi__info_main(&s,x,y,comp); } +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__is_16_main(&s); +} + +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__is_16_main(&s); +} + #endif // STB_IMAGE_IMPLEMENTATION /* revision history: + 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug + 1-bit BMP + *_is_16_bit api + avoid warnings 2.16 (2017-07-23) all functions have 16-bit variants; STBI_NO_STDIO works again; compilation fixes; diff --git a/src/external/stb_image_write.h b/src/external/stb_image_write.h index 9d553e0d..eaf5093e 100644 --- a/src/external/stb_image_write.h +++ b/src/external/stb_image_write.h @@ -1,4 +1,4 @@ -/* stb_image_write - v1.07 - public domain - http://nothings.org/stb/stb_image_write.h +/* stb_image_write - v1.08 - public domain - http://nothings.org/stb/stb_image_write.h writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015 no warranty implied; use at your own risk @@ -10,34 +10,47 @@ Will probably not work correctly with strict-aliasing optimizations. + If using a modern Microsoft Compiler, non-safe versions of CRT calls may cause + compilation warnings or even errors. To avoid this, also before #including, + + #define STBI_MSC_SECURE_CRT + ABOUT: This header file is a library for writing images to C stdio. It could be adapted to write to memory or a general streaming interface; let me know. The PNG output is not optimal; it is 20-50% larger than the file - written by a decent optimizing implementation. This library is designed - for source code compactness and simplicity, not optimal image file size - or run-time performance. + written by a decent optimizing implementation; though providing a custom + zlib compress function (see STBIW_ZLIB_COMPRESS) can mitigate that. + This library is designed for source code compactness and simplicity, + not optimal image file size or run-time performance. BUILDING: You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace malloc,realloc,free. - You can define STBIW_MEMMOVE() to replace memmove() + You can #define STBIW_MEMMOVE() to replace memmove() + You can #define STBIW_ZLIB_COMPRESS to use a custom zlib-style compress function + for PNG compression (instead of the builtin one), it must have the following signature: + unsigned char * my_compress(unsigned char *data, int data_len, int *out_len, int quality); + The returned data will be freed with STBIW_FREE() (free() by default), + so it must be heap allocated with STBIW_MALLOC() (malloc() by default), USAGE: - There are four functions, one for each image file format: + There are five functions, one for each image file format: int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); - int stbi_write_jpg(char const *filename, int w, int h, int comp, const float *data); + int stbi_write_jpg(char const *filename, int w, int h, int comp, const float *data, int quality); - There are also four equivalent functions that use an arbitrary write function. You are + void stbi_flip_vertically_on_write(int flag); // flag is non-zero to flip data vertically + + There are also five equivalent functions that use an arbitrary write function. You are expected to open/close your file-equivalent before and after calling these: int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); @@ -49,6 +62,12 @@ USAGE: where the callback is: void stbi_write_func(void *context, void *data, int size); + You can configure it with these global variables: + int stbi_write_tga_with_rle; // defaults to true; set to 0 to disable RLE + int stbi_write_png_compression_level; // defaults to 8; set to higher for more compression + int stbi_write_force_png_filter; // defaults to -1; set to 0..5 to force a filter mode + + You can define STBI_WRITE_NO_STDIO to disable the file variant of these functions, so the library will not use stdio.h at all. However, this will also disable HDR writing, because it requires stdio for formatted output. @@ -75,6 +94,9 @@ USAGE: writer, both because it is in BGR order and because it may have padding at the end of the line.) + PNG allows you to set the deflate compression level by setting the global + variable 'stbi_write_png_level' (it defaults to 8). + HDR expects linear float data. Since the format is always 32-bit rgb(e) data, alpha (if provided) is discarded, and for monochrome data it is replicated across all three channels. @@ -88,21 +110,17 @@ USAGE: CREDITS: - PNG/BMP/TGA - Sean Barrett - HDR - Baldur Karlsson - TGA monochrome: - Jean-Sebastien Guay - misc enhancements: - Tim Kelsey - TGA RLE - Alan Hickman - initial file IO callback implementation - Emmanuel Julien - JPEG - Jon Olick (original jo_jpeg.cpp code) - Daniel Gibson + + Sean Barrett - PNG/BMP/TGA + Baldur Karlsson - HDR + Jean-Sebastien Guay - TGA monochrome + Tim Kelsey - misc enhancements + Alan Hickman - TGA RLE + Emmanuel Julien - initial file IO callback implementation + Jon Olick - original jo_jpeg.cpp code + Daniel Gibson - integrate JPEG, allow external zlib + Aarni Koskela - allow choosing PNG filter + bugfixes: github:Chribba Guillaume Chereau @@ -114,6 +132,7 @@ CREDITS: Thatcher Ulrich github:poppolopoppo Patrick Boettcher + github:xeekworx LICENSE @@ -132,9 +151,12 @@ extern "C" { #define STBIWDEF static #else #define STBIWDEF extern -extern int stbi_write_tga_with_rle; #endif +STBIWDEF int stbi_write_tga_with_rle; +STBIWDEF int stbi_write_png_comperssion_level; +STBIWDEF int stbi_write_force_png_filter; + #ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); @@ -151,6 +173,8 @@ STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); +STBIWDEF void stbi_flip_vertically_on_write(int flip_boolean); + #ifdef __cplusplus } #endif @@ -208,6 +232,23 @@ STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, #define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) +#ifdef STB_IMAGE_WRITE_STATIC +static stbi__flip_vertically_on_write=0; +static int stbi_write_png_compression level = 8; +static int stbi_write_tga_with_rle = 1; +static int stbi_write_force_png_filter = -1; +#else +int stbi_write_png_compression_level = 8; +int stbi__flip_vertically_on_write=0; +int stbi_write_tga_with_rle = 1; +int stbi_write_force_png_filter = -1; +#endif + +STBIWDEF void stbi_flip_vertically_on_write(int flag) +{ + stbi__flip_vertically_on_write = flag; +} + typedef struct { stbi_write_func *func; @@ -230,7 +271,13 @@ static void stbi__stdio_write(void *context, void *data, int size) static int stbi__start_write_file(stbi__write_context *s, const char *filename) { - FILE *f = fopen(filename, "wb"); + FILE *f; +#ifdef STBI_MSC_SECURE_CRT + if (fopen_s(&f, filename, "wb")) + f = NULL; +#else + f = fopen(filename, "wb"); +#endif stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); return f != NULL; } @@ -245,12 +292,6 @@ static void stbi__end_write_file(stbi__write_context *s) typedef unsigned int stbiw_uint32; typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; -#ifdef STB_IMAGE_WRITE_STATIC -static int stbi_write_tga_with_rle = 1; -#else -int stbi_write_tga_with_rle = 1; -#endif - static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) { while (*fmt) { @@ -341,6 +382,9 @@ static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, i if (y <= 0) return; + if (stbi__flip_vertically_on_write) + vdir *= -1; + if (vdir < 0) j_end = -1, j = y-1; else @@ -412,11 +456,21 @@ static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, v "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8); } else { int i,j,k; + int jend, jdir; stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8); - for (j = y - 1; j >= 0; --j) { - unsigned char *row = (unsigned char *) data + j * x * comp; + if (stbi__flip_vertically_on_write) { + j = 0; + jend = y; + jdir = 1; + } else { + j = y-1; + jend = -1; + jdir = -1; + } + for (; j != jend; j += jdir) { + unsigned char *row = (unsigned char *) data + j * x * comp; int len; for (i = 0; i < x; i += len) { @@ -626,11 +680,15 @@ static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, f char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; s->func(s->context, header, sizeof(header)-1); +#ifdef STBI_MSC_SECURE_CRT + len = sprintf_s(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); +#else len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); +#endif s->func(s->context, buffer, len); for(i=0; i < y; i++) - stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*i*x); + stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*x*(stbi__flip_vertically_on_write ? y-1-i : i)*x); STBIW_FREE(scratch); return 1; } @@ -662,6 +720,7 @@ STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const // PNG writer // +#ifndef STBIW_ZLIB_COMPRESS // stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() #define stbiw__sbraw(a) ((int *) (a) - 2) #define stbiw__sbm(a) stbiw__sbraw(a)[0] @@ -742,8 +801,14 @@ static unsigned int stbiw__zhash(unsigned char *data) #define stbiw__ZHASH 16384 +#endif // STBIW_ZLIB_COMPRESS + unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) { +#ifdef STBIW_ZLIB_COMPRESS + // user provided a zlib compress implementation, use that + return STBIW_ZLIB_COMPRESS(data, data_len, out_len, quality); +#else // use builtin static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 }; static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 }; @@ -752,6 +817,8 @@ unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_l int i,j, bitcount=0; unsigned char *out = NULL; unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(char**)); + if (hash_table == NULL) + return NULL; if (quality < 5) quality = 5; stbiw__sbpush(out, 0x78); // DEFLATE 32K window @@ -845,6 +912,7 @@ unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_l // make returned pointer freeable STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); return (unsigned char *) stbiw__sbraw(out); +#endif // STBIW_ZLIB_COMPRESS } static unsigned int stbiw__crc32(unsigned char *buffer, int len) @@ -911,61 +979,87 @@ static unsigned char stbiw__paeth(int a, int b, int c) } // @OPTIMIZE: provide an option that always forces left-predict or paeth predict +static void stbiw__encode_png_line(unsigned char *pixels, int stride_bytes, int width, int height, int y, int n, int filter_type, signed char *line_buffer) +{ + static int mapping[] = { 0,1,2,3,4 }; + static int firstmap[] = { 0,1,0,5,6 }; + int *mymap = (y != 0) ? mapping : firstmap; + int i; + int type = mymap[filter_type]; + unsigned char *z = pixels + stride_bytes * (stbi__flip_vertically_on_write ? height-1-y : y); + for (i = 0; i < n; ++i) { + switch (type) { + case 0: line_buffer[i] = z[i]; break; + case 1: line_buffer[i] = z[i]; break; + case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; + case 3: line_buffer[i] = z[i] - (z[i-stride_bytes]>>1); break; + case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-stride_bytes],0)); break; + case 5: line_buffer[i] = z[i]; break; + case 6: line_buffer[i] = z[i]; break; + } + } + for (i=n; i < width*n; ++i) { + switch (type) { + case 0: line_buffer[i] = z[i]; break; + case 1: line_buffer[i] = z[i] - z[i-n]; break; + case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; + case 3: line_buffer[i] = z[i] - ((z[i-n] + z[i-stride_bytes])>>1); break; + case 4: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-stride_bytes], z[i-stride_bytes-n]); break; + case 5: line_buffer[i] = z[i] - (z[i-n]>>1); break; + case 6: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; + } + } +} + unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) { + int force_filter = stbi_write_force_png_filter; int ctype[5] = { -1, 0, 4, 2, 6 }; unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; unsigned char *out,*o, *filt, *zlib; signed char *line_buffer; - int i,j,k,p,zlen; + int j,zlen; if (stride_bytes == 0) stride_bytes = x * n; + if (force_filter >= 5) { + force_filter = -1; + } + filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } for (j=0; j < y; ++j) { - static int mapping[] = { 0,1,2,3,4 }; - static int firstmap[] = { 0,1,0,5,6 }; - int *mymap = (j != 0) ? mapping : firstmap; - int best = 0, bestval = 0x7fffffff; - for (p=0; p < 2; ++p) { - for (k= p?best:0; k < 5; ++k) { // @TODO: clarity: rewrite this to go 0..5, and 'continue' the unwanted ones during 2nd pass - int type = mymap[k],est=0; - unsigned char *z = pixels + stride_bytes*j; - for (i=0; i < n; ++i) - switch (type) { - case 0: line_buffer[i] = z[i]; break; - case 1: line_buffer[i] = z[i]; break; - case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; - case 3: line_buffer[i] = z[i] - (z[i-stride_bytes]>>1); break; - case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-stride_bytes],0)); break; - case 5: line_buffer[i] = z[i]; break; - case 6: line_buffer[i] = z[i]; break; - } - for (i=n; i < x*n; ++i) { - switch (type) { - case 0: line_buffer[i] = z[i]; break; - case 1: line_buffer[i] = z[i] - z[i-n]; break; - case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; - case 3: line_buffer[i] = z[i] - ((z[i-n] + z[i-stride_bytes])>>1); break; - case 4: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-stride_bytes], z[i-stride_bytes-n]); break; - case 5: line_buffer[i] = z[i] - (z[i-n]>>1); break; - case 6: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; - } - } - if (p) break; - for (i=0; i < x*n; ++i) + int filter_type; + if (force_filter > -1) { + filter_type = force_filter; + stbiw__encode_png_line(pixels, stride_bytes, x, y, j, n, force_filter, line_buffer); + } else { // Estimate the best filter by running through all of them: + int best_filter = 0, best_filter_val = 0x7fffffff, est, i; + for (filter_type = 0; filter_type < 5; filter_type++) { + stbiw__encode_png_line(pixels, stride_bytes, x, y, j, n, filter_type, line_buffer); + + // Estimate the entropy of the line using this filter; the less, the better. + est = 0; + for (i = 0; i < x*n; ++i) { est += abs((signed char) line_buffer[i]); - if (est < bestval) { bestval = est; best = k; } + } + if (est < best_filter_val) { + best_filter_val = est; + best_filter = filter_type; + } + } + if (filter_type != best_filter) { // If the last iteration already got us the best filter, don't redo it + stbiw__encode_png_line(pixels, stride_bytes, x, y, j, n, best_filter, line_buffer); + filter_type = best_filter; } } - // when we get here, best contains the filter type, and line_buffer contains the data - filt[j*(x*n+1)] = (unsigned char) best; + // when we get here, filter_type contains the filter type, and line_buffer contains the data + filt[j*(x*n+1)] = (unsigned char) filter_type; STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); } STBIW_FREE(line_buffer); - zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, 8); // increase 8 to get smaller but use more memory + zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, stbi_write_png_compression_level); STBIW_FREE(filt); if (!zlib) return 0; @@ -1010,7 +1104,12 @@ STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const int len; unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); if (png == NULL) return 0; +#ifdef STBI_MSC_SECURE_CRT + if (fopen_s(&f, filename, "wb")) + f = NULL; +#else f = fopen(filename, "wb"); +#endif if (!f) { STBIW_FREE(png); return 0; } fwrite(png, 1, len, f); fclose(f); @@ -1318,7 +1417,7 @@ static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, in float YDU[64], UDU[64], VDU[64]; for(row = y, pos = 0; row < y+8; ++row) { for(col = x; col < x+8; ++col, ++pos) { - int p = row*width*comp + col*comp; + int p = (stbi__flip_vertically_on_write ? height-1-row : row)*width*comp + col*comp; float r, g, b; if(row >= height) { p -= width*comp*(row+1 - height); @@ -1377,6 +1476,8 @@ STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const #endif // STB_IMAGE_WRITE_IMPLEMENTATION /* Revision history + 1.08 (2018-01-29) + add stbi__flip_vertically_on_write, external zlib, zlib quality, choose PNG filter 1.07 (2017-07-24) doc fix 1.06 (2017-07-23) diff --git a/src/external/stb_truetype.h b/src/external/stb_truetype.h index cec24254..98c8acc3 100644 --- a/src/external/stb_truetype.h +++ b/src/external/stb_truetype.h @@ -1,4 +1,4 @@ -// stb_truetype.h - v1.17 - public domain +// stb_truetype.h - v1.18 - public domain // authored from 2009-2016 by Sean Barrett / RAD Game Tools // // This library processes TrueType files: @@ -30,33 +30,24 @@ // Imanol Celaya // // Bug/warning reports/fixes: -// "Zer" on mollyrocket -// Cass Everitt -// stoiko (Haemimont Games) -// Brian Hook -// Walter van Niftrik -// David Gow -// David Given -// Ivan-Assen Ivanov -// Anthony Pesch -// Johan Duparc -// Hou Qiming -// Fabian "ryg" Giesen -// Martins Mozeiko -// Cap Petschulat -// Omar Cornut -// github:aloucks -// Peter LaValle -// Sergey Popov -// Giumo X. Clanjor -// Higor Euripedes -// Thomas Fields -// Derek Vinyard -// Cort Stratton -// github:oyvindjam -// +// "Zer" on mollyrocket Fabian "ryg" Giesen +// Cass Everitt Martins Mozeiko +// stoiko (Haemimont Games) Cap Petschulat +// Brian Hook Omar Cornut +// Walter van Niftrik github:aloucks +// David Gow Peter LaValle +// David Given Sergey Popov +// Ivan-Assen Ivanov Giumo X. Clanjor +// Anthony Pesch Higor Euripedes +// Johan Duparc Thomas Fields +// Hou Qiming Derek Vinyard +// Rob Loach Cort Stratton +// Kenney Phillis Jr. github:oyvindjam +// Brian Costabile github:vassvik +// // VERSION HISTORY // +// 1.18 (2018-01-29) add missing function // 1.17 (2017-07-23) make more arguments const; doc fix // 1.16 (2017-07-12) SDF support // 1.15 (2017-03-03) make more arguments const @@ -171,7 +162,7 @@ // measurement for describing font size, defined as 72 points per inch. // stb_truetype provides a point API for compatibility. However, true // "per inch" conventions don't make much sense on computer displays -// since they different monitors have different number of pixels per +// since different monitors have different number of pixels per // inch. For example, Windows traditionally uses a convention that // there are 96 pixels per inch, thus making 'inch' measurements have // nothing to do with inches, and thus effectively defining a point to @@ -181,6 +172,39 @@ // for non-commercial fonts, thus making fonts scaled in points // according to the TrueType spec incoherently sized in practice. // +// DETAILED USAGE: +// +// Scale: +// Select how high you want the font to be, in points or pixels. +// Call ScaleForPixelHeight or ScaleForMappingEmToPixels to compute +// a scale factor SF that will be used by all other functions. +// +// Baseline: +// You need to select a y-coordinate that is the baseline of where +// your text will appear. Call GetFontBoundingBox to get the baseline-relative +// bounding box for all characters. SF*-y0 will be the distance in pixels +// that the worst-case character could extend above the baseline, so if +// you want the top edge of characters to appear at the top of the +// screen where y=0, then you would set the baseline to SF*-y0. +// +// Current point: +// Set the current point where the first character will appear. The +// first character could extend left of the current point; this is font +// dependent. You can either choose a current point that is the leftmost +// point and hope, or add some padding, or check the bounding box or +// left-side-bearing of the first character to be displayed and set +// the current point based on that. +// +// Displaying a character: +// Compute the bounding box of the character. It will contain signed values +// relative to . I.e. if it returns x0,y0,x1,y1, +// then the character should be displayed in the rectangle from +// to - #define STBTT_fabs(x) fabs(x) - #endif - // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h #ifndef STBTT_malloc #include @@ -2172,7 +2191,7 @@ static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, st // push immediate if (b0 == 255) { - f = (float)stbtt__buf_get32(&b) / 0x10000; + f = (float)(stbtt_int32)stbtt__buf_get32(&b) / 0x10000; } else { stbtt__buf_skip(&b, -1); f = (float)(stbtt_int16)stbtt__cff_int(&b); @@ -2210,12 +2229,10 @@ static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, in { stbtt__csctx c = STBTT__CSCTX_INIT(1); int r = stbtt__run_charstring(info, glyph_index, &c); - if (x0) { - *x0 = r ? c.min_x : 0; - *y0 = r ? c.min_y : 0; - *x1 = r ? c.max_x : 0; - *y1 = r ? c.max_y : 0; - } + if (x0) *x0 = r ? c.min_x : 0; + if (y0) *y0 = r ? c.min_y : 0; + if (x1) *x1 = r ? c.max_x : 0; + if (y1) *y1 = r ? c.max_y : 0; return r ? c.num_vertices : 0; } @@ -2395,7 +2412,7 @@ static void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata) hh->num_remaining_in_head_chunk = count; } --hh->num_remaining_in_head_chunk; - return (char *) (hh->head) + size * hh->num_remaining_in_head_chunk; + return (char *) (hh->head) + sizeof(stbtt__hheap_chunk) + size * hh->num_remaining_in_head_chunk; } } @@ -3229,8 +3246,9 @@ error: STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata) { - float scale = scale_x > scale_y ? scale_y : scale_x; - int winding_count, *winding_lengths; + float scale = scale_x > scale_y ? scale_y : scale_x; + int winding_count = 0; + int *winding_lengths = NULL; stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata); if (windings) { stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata); @@ -3318,6 +3336,11 @@ STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo * return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); } +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint) +{ + stbtt_MakeGlyphBitmapSubpixelPrefilter(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, oversample_x, oversample_y, sub_x, sub_y, stbtt_FindGlyphIndex(info,codepoint)); +} + STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint) { stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint)); diff --git a/src/external/stb_vorbis.c b/src/external/stb_vorbis.c index 5f2e0814..4b72504c 100644 --- a/src/external/stb_vorbis.c +++ b/src/external/stb_vorbis.c @@ -1658,6 +1658,8 @@ static int residue_decode(vorb *f, Codebook *book, float *target, int offset, in return TRUE; } +// n is 1/2 of the blocksize -- +// specification: "Correct per-vector decode length is [n]/2" static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int rn, uint8 *do_not_decode) { int i,j,pass; @@ -1665,7 +1667,10 @@ static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int int rtype = f->residue_types[rn]; int c = r->classbook; int classwords = f->codebooks[c].dimensions; - int n_read = r->end - r->begin; + unsigned int actual_size = rtype == 2 ? n*2 : n; + unsigned int limit_r_begin = (r->begin < actual_size ? r->begin : actual_size); + unsigned int limit_r_end = (r->end < actual_size ? r->end : actual_size); + int n_read = limit_r_end - limit_r_begin; int part_read = n_read / r->part_size; int temp_alloc_point = temp_alloc_save(f); #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE @@ -3007,7 +3012,7 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, if (f->last_seg_which == f->end_seg_with_known_loc) { // if we have a valid current loc, and this is final: if (f->current_loc_valid && (f->page_flag & PAGEFLAG_last_page)) { - uint32 current_end = f->known_loc_for_packet - (n-right_end); + uint32 current_end = f->known_loc_for_packet; // then let's infer the size of the (probably) short final frame if (current_end < f->current_loc + (right_end-left_start)) { if (current_end < f->current_loc) { @@ -3016,7 +3021,7 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, } else { *len = current_end - f->current_loc; } - *len += left_start; + *len += left_start; // this doesn't seem right, but has no ill effect on my test files if (*len > right_end) *len = right_end; // this should never happen f->current_loc += *len; return TRUE; @@ -3693,7 +3698,10 @@ static int start_decoder(vorb *f) int i,max_part_read=0; for (i=0; i < f->residue_count; ++i) { Residue *r = f->residue_config + i; - int n_read = r->end - r->begin; + unsigned int actual_size = f->blocksize_1 / 2; + unsigned int limit_r_begin = r->begin < actual_size ? r->begin : actual_size; + unsigned int limit_r_end = r->end < actual_size ? r->end : actual_size; + int n_read = limit_r_end - limit_r_begin; int part_read = n_read / r->part_size; if (part_read > max_part_read) max_part_read = part_read; @@ -3704,6 +3712,8 @@ static int start_decoder(vorb *f) classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(int *)); #endif + // maximum reasonable partition size is f->blocksize_1 + f->temp_memory_required = classify_mem; if (imdct_mem > f->temp_memory_required) f->temp_memory_required = imdct_mem; @@ -4967,6 +4977,8 @@ int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, in #endif // STB_VORBIS_NO_PULLDATA_API /* Version history + 1.12 - 2017/11/21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files + 1.11 - 2017/07/23 - fix MinGW compilation 1.10 - 2017/03/03 - more robust seeking; fix negative ilog(); clear error in open_memory 1.09 - 2016/04/04 - back out 'avoid discarding last frame' fix from previous version 1.08 - 2016/04/02 - fixed multiple warnings; fix setup memory leaks; diff --git a/src/external/stb_vorbis.h b/src/external/stb_vorbis.h index 1cdca652..4a339090 100644 --- a/src/external/stb_vorbis.h +++ b/src/external/stb_vorbis.h @@ -1,11 +1,11 @@ -// Ogg Vorbis audio decoder - v1.11 - public domain +// Ogg Vorbis audio decoder - v1.13b - public domain // http://nothings.org/stb_vorbis/ // // Original version written by Sean Barrett in 2007. // -// Originally sponsored by RAD Game Tools. Seeking sponsored -// by Phillip Bennefall, Marc Andersen, Aaron Baker, Elias Software, -// Aras Pranckevicius, and Sean Barrett. +// Originally sponsored by RAD Game Tools. Seeking implementation +// sponsored by Phillip Bennefall, Marc Andersen, Aaron Baker, +// Elias Software, Aras Pranckevicius, and Sean Barrett. // // LICENSE // @@ -32,6 +32,8 @@ // manxorist@github saga musix github:infatum // // Partial history: +// 1.13 - 2018/01/29 - fix truncation of last frame (hopefully) +// 1.12 - 2017/11/21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files // 1.11 - 2017/07/23 - fix MinGW compilation // 1.10 - 2017/03/03 - more robust seeking; fix negative ilog(); clear error in open_memory // 1.09 - 2016/04/04 - back out 'truncation of last frame' fix from previous version @@ -387,4 +389,4 @@ enum STBVorbisError // // HEADER ENDS HERE // -////////////////////////////////////////////////////////////////////////////// \ No newline at end of file +////////////////////////////////////////////////////////////////////////////// -- cgit v1.2.3 From 468309d06c7ac3e3278057cfb1cfc19b332833bf Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Fri, 9 Feb 2018 22:50:50 +0100 Subject: Early-exit InitWindow if InitGraphicsDevice fails Otherwise we may run into LoadDefaultFont and crash in rlLoadTexture Also moves InitTimer() before InitGraphicsDevice(), to allow it to be tested even if InitWindow ultimately fails. --- src/core.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/core.c b/src/core.c index 0a1c34c9..d6a10943 100644 --- a/src/core.c +++ b/src/core.c @@ -441,12 +441,13 @@ void InitWindow(int width, int height, void *data) uwpWindow = (EGLNativeWindowType)data; #endif + // Init hi-res timer + InitTimer(); + // Init graphics device (display device and OpenGL context) // NOTE: returns true if window and graphic device has been initialized successfully windowReady = InitGraphicsDevice(width, height); - - // Init hi-res timer - InitTimer(); + if (!windowReady) return; #if defined(SUPPORT_DEFAULT_FONT) // Load default font -- cgit v1.2.3 From 7bf6becc94ee18f782e6ef176745bc8d50b19944 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 11 Feb 2018 01:12:16 +0100 Subject: Reviewed mini_al implementation - Some functions renamed - Comments reviewed - Functions reorganized --- src/audio.c | 383 ++++++++++++++++++++++++++++++++---------------------------- 1 file changed, 207 insertions(+), 176 deletions(-) (limited to 'src') diff --git a/src/audio.c b/src/audio.c index 052d4d3c..55b43bea 100644 --- a/src/audio.c +++ b/src/audio.c @@ -225,9 +225,10 @@ void TraceLog(int msgType, const char *text, ...); // Show trace lo #endif //---------------------------------------------------------------------------------- -// Module Functions Definition - Audio Device initialization and Closing +// mini_al AudioBuffer Functionality //---------------------------------------------------------------------------------- #if USE_MINI_AL + #define DEVICE_FORMAT mal_format_f32 #define DEVICE_CHANNELS 2 #define DEVICE_SAMPLE_RATE 44100 @@ -235,15 +236,16 @@ void TraceLog(int msgType, const char *text, ...); // Show trace lo typedef enum { AUDIO_BUFFER_USAGE_STATIC = 0, AUDIO_BUFFER_USAGE_STREAM } AudioBufferUsage; // Audio buffer structure +// 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; struct AudioBuffer { - mal_dsp dsp; // For format conversion. + mal_dsp dsp; // Required for format conversion float volume; float pitch; bool playing; bool paused; - bool looping; // Always true for AudioStreams. - AudioBufferUsage usage; // Slightly different logic is used when feeding data to the playback device depending on whether or not data is streamed. + bool looping; // Always true for AudioStreams + int usage; // AudioBufferUsage type bool isSubBufferProcessed[2]; unsigned int frameCursorPos; unsigned int bufferSizeInFrames; @@ -252,76 +254,48 @@ struct AudioBuffer { unsigned char buffer[1]; }; -void StopAudioBuffer(AudioBuffer *audioBuffer); - +// mini_al global variables static mal_context context; static mal_device device; -static mal_bool32 isAudioInitialized = MAL_FALSE; -static float masterVolume = 1; static mal_mutex audioLock; -static AudioBuffer *firstAudioBuffer = NULL; // Audio buffers are tracked in a linked list. -static AudioBuffer *lastAudioBuffer = NULL; - -static void TrackAudioBuffer(AudioBuffer* audioBuffer) -{ - mal_mutex_lock(&audioLock); - - { - if (firstAudioBuffer == NULL) firstAudioBuffer = audioBuffer; - else - { - lastAudioBuffer->next = audioBuffer; - audioBuffer->prev = lastAudioBuffer; - } +static bool isAudioInitialized = MAL_FALSE; +static float masterVolume = 1.0f; - lastAudioBuffer = audioBuffer; - } - - mal_mutex_unlock(&audioLock); -} - -static void UntrackAudioBuffer(AudioBuffer* audioBuffer) -{ - mal_mutex_lock(&audioLock); - - { - if (audioBuffer->prev == NULL) firstAudioBuffer = audioBuffer->next; - else audioBuffer->prev->next = audioBuffer->next; +// Audio buffers are tracked in a linked list +static AudioBuffer *firstAudioBuffer = NULL; +static AudioBuffer *lastAudioBuffer = NULL; - if (audioBuffer->next == NULL) lastAudioBuffer = audioBuffer->prev; - else audioBuffer->next->prev = audioBuffer->prev; +// mini_al functions declaration +static void OnLog(mal_context *pContext, mal_device *pDevice, const char *message); +static mal_uint32 OnSendAudioDataToDevice(mal_device *pDevice, mal_uint32 frameCount, void *pFramesOut); +static mal_uint32 OnAudioBufferDSPRead(mal_dsp *pDSP, mal_uint32 frameCount, void *pFramesOut, void *pUserData); +static void MixAudioFrames(float *framesOut, const float *framesIn, mal_uint32 frameCount, float localVolume); + +// AudioBuffer management functions declaration +// NOTE: Those functions are not exposed by raylib... for the moment +AudioBuffer *CreateAudioBuffer(mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_uint32 bufferSizeInFrames, AudioBufferUsage usage); +void DeleteAudioBuffer(AudioBuffer *audioBuffer); +bool IsAudioBufferPlaying(AudioBuffer *audioBuffer); +void PlayAudioBuffer(AudioBuffer *audioBuffer); +void StopAudioBuffer(AudioBuffer *audioBuffer); +void PauseAudioBuffer(AudioBuffer *audioBuffer); +void ResumeAudioBuffer(AudioBuffer *audioBuffer); +void SetAudioBufferVolume(AudioBuffer *audioBuffer, float volume); +void SetAudioBufferPitch(AudioBuffer *audioBuffer, float pitch); +void TrackAudioBuffer(AudioBuffer *audioBuffer); +void UntrackAudioBuffer(AudioBuffer *audioBuffer); - audioBuffer->prev = NULL; - audioBuffer->next = NULL; - } - - mal_mutex_unlock(&audioLock); -} -static void OnLog_MAL(mal_context *pContext, mal_device *pDevice, const char *message) +// Log callback function +static void OnLog(mal_context *pContext, mal_device *pDevice, const char *message) { (void)pContext; (void)pDevice; - TraceLog(LOG_ERROR, message); // All log messages from mini_al are errors. -} - -// This is the main mixing function. Mixing is pretty simple in this project - it's just an accumulation. -// -// framesOut is both an input and an output. It will be initially filled with zeros outside of this function. -static void MixFrames(float* framesOut, const float* framesIn, mal_uint32 frameCount, float localVolume) -{ - for (mal_uint32 iFrame = 0; iFrame < frameCount; ++iFrame) - { - for (mal_uint32 iChannel = 0; iChannel < device.channels; ++iChannel) - { - float *frameOut = framesOut + (iFrame*device.channels); - const float *frameIn = framesIn + (iFrame*device.channels); - - frameOut[iChannel] += frameIn[iChannel]*masterVolume*localVolume; - } - } + + TraceLog(LOG_ERROR, message); // All log messages from mini_al are errors } +// Sending audio data to device callback function static mal_uint32 OnSendAudioDataToDevice(mal_device *pDevice, mal_uint32 frameCount, void *pFramesOut) { // This is where all of the mixing takes place. @@ -334,7 +308,7 @@ static mal_uint32 OnSendAudioDataToDevice(mal_device *pDevice, mal_uint32 frameC // want to consider how you might want to avoid this. mal_mutex_lock(&audioLock); { - for (AudioBuffer* audioBuffer = firstAudioBuffer; audioBuffer != NULL; audioBuffer = audioBuffer->next) + for (AudioBuffer *audioBuffer = firstAudioBuffer; audioBuffer != NULL; audioBuffer = audioBuffer->next) { // Ignore stopped or paused sounds. if (!audioBuffer->playing || audioBuffer->paused) continue; @@ -364,14 +338,14 @@ static mal_uint32 OnSendAudioDataToDevice(mal_device *pDevice, mal_uint32 frameC // If we're not looping, we need to make sure we flush the internal buffers of the DSP pipeline to ensure we get the // last few samples. - mal_bool32 flushDSP = !audioBuffer->looping; + bool flushDSP = !audioBuffer->looping; mal_uint32 framesJustRead = mal_dsp_read_frames_ex(&audioBuffer->dsp, framesToReadRightNow, tempBuffer, flushDSP); if (framesJustRead > 0) { float *framesOut = (float *)pFramesOut + (framesRead*device.channels); float *framesIn = tempBuffer; - MixFrames(framesOut, framesIn, framesJustRead, audioBuffer->volume); + MixAudioFrames(framesOut, framesIn, framesJustRead, audioBuffer->volume); framesToRead -= framesJustRead; framesRead += framesJustRead; @@ -387,15 +361,16 @@ static mal_uint32 OnSendAudioDataToDevice(mal_device *pDevice, mal_uint32 frameC } else { - // Should never get here, but just for safety, move the cursor position back to the start and continue the loop. + // Should never get here, but just for safety, + // move the cursor position back to the start and continue the loop. audioBuffer->frameCursorPos = 0; continue; } } } - // 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. + // 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. if (framesToRead > 0) break; } } @@ -405,14 +380,122 @@ static mal_uint32 OnSendAudioDataToDevice(mal_device *pDevice, mal_uint32 frameC return frameCount; // We always output the same number of frames that were originally requested. } + +// DSP read from audio buffer callback function +static mal_uint32 OnAudioBufferDSPRead(mal_dsp *pDSP, mal_uint32 frameCount, void *pFramesOut, void *pUserData) +{ + AudioBuffer *audioBuffer = (AudioBuffer *)pUserData; + + mal_uint32 subBufferSizeInFrames = audioBuffer->bufferSizeInFrames/2; + mal_uint32 currentSubBufferIndex = audioBuffer->frameCursorPos/subBufferSizeInFrames; + + if (currentSubBufferIndex > 1) + { + TraceLog(LOG_DEBUG, "Frame cursor position moved too far forward in audio stream"); + return 0; + } + + // Another thread can update the processed state of buffers so we just take a copy here to try and avoid potential synchronization problems. + bool isSubBufferProcessed[2]; + isSubBufferProcessed[0] = audioBuffer->isSubBufferProcessed[0]; + isSubBufferProcessed[1] = audioBuffer->isSubBufferProcessed[1]; + + mal_uint32 frameSizeInBytes = mal_get_sample_size_in_bytes(audioBuffer->dsp.config.formatIn)*audioBuffer->dsp.config.channelsIn; + + // Fill out every frame until we find a buffer that's marked as processed. Then fill the remainder with 0. + mal_uint32 framesRead = 0; + 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. + if (audioBuffer->usage == AUDIO_BUFFER_USAGE_STATIC) + { + if (framesRead >= frameCount) break; + } + else + { + if (isSubBufferProcessed[currentSubBufferIndex]) break; + } + + mal_uint32 totalFramesRemaining = (frameCount - framesRead); + if (totalFramesRemaining == 0) break; + + mal_uint32 framesRemainingInOutputBuffer; + if (audioBuffer->usage == AUDIO_BUFFER_USAGE_STATIC) + { + framesRemainingInOutputBuffer = audioBuffer->bufferSizeInFrames - audioBuffer->frameCursorPos; + } + else + { + mal_uint32 firstFrameIndexOfThisSubBuffer = subBufferSizeInFrames * currentSubBufferIndex; + framesRemainingInOutputBuffer = subBufferSizeInFrames - (audioBuffer->frameCursorPos - firstFrameIndexOfThisSubBuffer); + } + + mal_uint32 framesToRead = totalFramesRemaining; + if (framesToRead > framesRemainingInOutputBuffer) framesToRead = framesRemainingInOutputBuffer; + + memcpy((unsigned char *)pFramesOut + (framesRead*frameSizeInBytes), audioBuffer->buffer + (audioBuffer->frameCursorPos*frameSizeInBytes), framesToRead*frameSizeInBytes); + audioBuffer->frameCursorPos = (audioBuffer->frameCursorPos + framesToRead) % audioBuffer->bufferSizeInFrames; + framesRead += framesToRead; + + // If we've read to the end of the buffer, mark it as processed. + if (framesToRead == framesRemainingInOutputBuffer) + { + audioBuffer->isSubBufferProcessed[currentSubBufferIndex] = true; + isSubBufferProcessed[currentSubBufferIndex] = true; + + currentSubBufferIndex = (currentSubBufferIndex + 1)%2; + + // We need to break from this loop if we're not looping. + if (!audioBuffer->looping) + { + StopAudioBuffer(audioBuffer); + break; + } + } + } + + // Zero-fill excess. + mal_uint32 totalFramesRemaining = (frameCount - framesRead); + if (totalFramesRemaining > 0) + { + memset((unsigned char*)pFramesOut + (framesRead*frameSizeInBytes), 0, totalFramesRemaining*frameSizeInBytes); + + // For static buffers we can fill the remaining frames with silence for safety, but we don't want + // to report those frames as "read". The reason for this is that the caller uses the return value + // to know whether or not a non-looping sound has finished playback. + if (audioBuffer->usage != AUDIO_BUFFER_USAGE_STATIC) framesRead += totalFramesRemaining; + } + + return framesRead; +} + +// This is the main mixing function. Mixing is pretty simple in this project - it's just an accumulation. +// 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) +{ + for (mal_uint32 iFrame = 0; iFrame < frameCount; ++iFrame) + { + for (mal_uint32 iChannel = 0; iChannel < device.channels; ++iChannel) + { + float *frameOut = framesOut + (iFrame*device.channels); + const float *frameIn = framesIn + (iFrame*device.channels); + + frameOut[iChannel] += frameIn[iChannel]*masterVolume*localVolume; + } + } +} #endif +//---------------------------------------------------------------------------------- +// Module Functions Definition - Audio Device initialization and Closing +//---------------------------------------------------------------------------------- // Initialize audio device void InitAudioDevice(void) { #if USE_MINI_AL // Context. - mal_context_config contextConfig = mal_context_config_init(OnLog_MAL); + mal_context_config contextConfig = mal_context_config_init(OnLog); mal_result result = mal_context_init(NULL, 0, &contextConfig, &context); if (result != MAL_SUCCESS) { @@ -556,100 +639,11 @@ void SetMasterVolume(float volume) #endif } - //---------------------------------------------------------------------------------- -// Audio Buffer +// Module Functions Definition - Audio Buffer management //---------------------------------------------------------------------------------- #if USE_MINI_AL -static mal_uint32 AudioBuffer_OnDSPRead(mal_dsp* pDSP, mal_uint32 frameCount, void* pFramesOut, void* pUserData) -{ - AudioBuffer *audioBuffer = (AudioBuffer *)pUserData; - - mal_uint32 subBufferSizeInFrames = audioBuffer->bufferSizeInFrames/2; - mal_uint32 currentSubBufferIndex = audioBuffer->frameCursorPos/subBufferSizeInFrames; - - if (currentSubBufferIndex > 1) - { - TraceLog(LOG_DEBUG, "Frame cursor position moved too far forward in audio stream"); - return 0; - } - - // Another thread can update the processed state of buffers so we just take a copy here to try and avoid potential synchronization problems. - bool isSubBufferProcessed[2]; - isSubBufferProcessed[0] = audioBuffer->isSubBufferProcessed[0]; - isSubBufferProcessed[1] = audioBuffer->isSubBufferProcessed[1]; - - mal_uint32 frameSizeInBytes = mal_get_sample_size_in_bytes(audioBuffer->dsp.config.formatIn)*audioBuffer->dsp.config.channelsIn; - - // Fill out every frame until we find a buffer that's marked as processed. Then fill the remainder with 0. - mal_uint32 framesRead = 0; - 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. - if (audioBuffer->usage == AUDIO_BUFFER_USAGE_STATIC) - { - if (framesRead >= frameCount) break; - } - else - { - if (isSubBufferProcessed[currentSubBufferIndex]) break; - } - - mal_uint32 totalFramesRemaining = (frameCount - framesRead); - if (totalFramesRemaining == 0) break; - - mal_uint32 framesRemainingInOutputBuffer; - if (audioBuffer->usage == AUDIO_BUFFER_USAGE_STATIC) - { - framesRemainingInOutputBuffer = audioBuffer->bufferSizeInFrames - audioBuffer->frameCursorPos; - } - else - { - mal_uint32 firstFrameIndexOfThisSubBuffer = subBufferSizeInFrames * currentSubBufferIndex; - framesRemainingInOutputBuffer = subBufferSizeInFrames - (audioBuffer->frameCursorPos - firstFrameIndexOfThisSubBuffer); - } - - mal_uint32 framesToRead = totalFramesRemaining; - if (framesToRead > framesRemainingInOutputBuffer) framesToRead = framesRemainingInOutputBuffer; - - memcpy((unsigned char *)pFramesOut + (framesRead*frameSizeInBytes), audioBuffer->buffer + (audioBuffer->frameCursorPos*frameSizeInBytes), framesToRead*frameSizeInBytes); - audioBuffer->frameCursorPos = (audioBuffer->frameCursorPos + framesToRead) % audioBuffer->bufferSizeInFrames; - framesRead += framesToRead; - - // If we've read to the end of the buffer, mark it as processed. - if (framesToRead == framesRemainingInOutputBuffer) - { - audioBuffer->isSubBufferProcessed[currentSubBufferIndex] = true; - isSubBufferProcessed[currentSubBufferIndex] = true; - - currentSubBufferIndex = (currentSubBufferIndex + 1) % 2; - - // We need to break from this loop if we're not looping. - if (!audioBuffer->looping) - { - StopAudioBuffer(audioBuffer); - break; - } - } - } - - // Zero-fill excess. - mal_uint32 totalFramesRemaining = (frameCount - framesRead); - if (totalFramesRemaining > 0) - { - memset((unsigned char*)pFramesOut + (framesRead*frameSizeInBytes), 0, totalFramesRemaining*frameSizeInBytes); - - // For static buffers we can fill the remaining frames with silence for safety, but we don't want - // to report those frames as "read". The reason for this is that the caller uses the return value - // to know whether or not a non-looping sound has finished playback. - if (audioBuffer->usage != AUDIO_BUFFER_USAGE_STATIC) framesRead += totalFramesRemaining; - } - - return framesRead; -} - -// Create a new audio buffer. Initially filled with silence. +// Create a new audio buffer. Initially filled with silence AudioBuffer *CreateAudioBuffer(mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_uint32 bufferSizeInFrames, AudioBufferUsage usage) { AudioBuffer *audioBuffer = (AudioBuffer *)calloc(sizeof(*audioBuffer) + (bufferSizeInFrames*channels*mal_get_sample_size_in_bytes(format)), 1); @@ -668,7 +662,7 @@ AudioBuffer *CreateAudioBuffer(mal_format format, mal_uint32 channels, mal_uint3 dspConfig.channelsOut = DEVICE_CHANNELS; dspConfig.sampleRateIn = sampleRate; dspConfig.sampleRateOut = DEVICE_SAMPLE_RATE; - mal_result resultMAL = mal_dsp_init(&dspConfig, AudioBuffer_OnDSPRead, audioBuffer, &audioBuffer->dsp); + mal_result resultMAL = mal_dsp_init(&dspConfig, OnAudioBufferDSPRead, audioBuffer, &audioBuffer->dsp); if (resultMAL != MAL_SUCCESS) { TraceLog(LOG_ERROR, "LoadSoundFromWave() : Failed to create data conversion pipeline"); @@ -694,7 +688,7 @@ AudioBuffer *CreateAudioBuffer(mal_format format, mal_uint32 channels, mal_uint3 return audioBuffer; } -// Delete an audio buffer. +// Delete an audio buffer void DeleteAudioBuffer(AudioBuffer *audioBuffer) { if (audioBuffer == NULL) @@ -707,7 +701,7 @@ void DeleteAudioBuffer(AudioBuffer *audioBuffer) free(audioBuffer); } -// Check if an audio buffer is playing. +// Check if an audio buffer is playing bool IsAudioBufferPlaying(AudioBuffer *audioBuffer) { if (audioBuffer == NULL) @@ -719,10 +713,9 @@ bool IsAudioBufferPlaying(AudioBuffer *audioBuffer) return audioBuffer->playing && !audioBuffer->paused; } -// Play an audio buffer. -// -// This will restart the buffer from the start. Use PauseAudioBuffer() and ResumeAudioBuffer() if the playback position -// should be maintained. +// Play an audio buffer +// NOTE: Buffer is restarted to the start. +// Use PauseAudioBuffer() and ResumeAudioBuffer() if the playback position should be maintained. void PlayAudioBuffer(AudioBuffer *audioBuffer) { if (audioBuffer == NULL) @@ -736,7 +729,7 @@ void PlayAudioBuffer(AudioBuffer *audioBuffer) audioBuffer->frameCursorPos = 0; } -// Stop an audio buffer. +// Stop an audio buffer void StopAudioBuffer(AudioBuffer *audioBuffer) { if (audioBuffer == NULL) @@ -755,7 +748,7 @@ void StopAudioBuffer(AudioBuffer *audioBuffer) audioBuffer->isSubBufferProcessed[1] = true; } -// Pause an audio buffer. +// Pause an audio buffer void PauseAudioBuffer(AudioBuffer *audioBuffer) { if (audioBuffer == NULL) @@ -767,7 +760,7 @@ void PauseAudioBuffer(AudioBuffer *audioBuffer) audioBuffer->paused = true; } -// Resume an audio buffer. +// Resume an audio buffer void ResumeAudioBuffer(AudioBuffer *audioBuffer) { if (audioBuffer == NULL) @@ -779,7 +772,7 @@ void ResumeAudioBuffer(AudioBuffer *audioBuffer) audioBuffer->paused = false; } -// Set volume for an audio buffer. +// Set volume for an audio buffer void SetAudioBufferVolume(AudioBuffer *audioBuffer, float volume) { if (audioBuffer == NULL) @@ -791,7 +784,7 @@ void SetAudioBufferVolume(AudioBuffer *audioBuffer, float volume) audioBuffer->volume = volume; } -// Set pitch for an audio buffer. +// Set pitch for an audio buffer void SetAudioBufferPitch(AudioBuffer *audioBuffer, float pitch) { if (audioBuffer == NULL) @@ -807,6 +800,44 @@ void SetAudioBufferPitch(AudioBuffer *audioBuffer, float pitch) mal_uint32 newOutputSampleRate = (mal_uint32)((((float)audioBuffer->dsp.config.sampleRateOut / (float)audioBuffer->dsp.config.sampleRateIn) / pitch) * audioBuffer->dsp.config.sampleRateIn); mal_dsp_set_output_sample_rate(&audioBuffer->dsp, newOutputSampleRate); } + +// Track audio buffer to linked list next position +void TrackAudioBuffer(AudioBuffer *audioBuffer) +{ + mal_mutex_lock(&audioLock); + + { + if (firstAudioBuffer == NULL) firstAudioBuffer = audioBuffer; + else + { + lastAudioBuffer->next = audioBuffer; + audioBuffer->prev = lastAudioBuffer; + } + + lastAudioBuffer = audioBuffer; + } + + mal_mutex_unlock(&audioLock); +} + +// Untrack audio buffer from linked list +void UntrackAudioBuffer(AudioBuffer *audioBuffer) +{ + mal_mutex_lock(&audioLock); + + { + if (audioBuffer->prev == NULL) firstAudioBuffer = audioBuffer->next; + else audioBuffer->prev->next = audioBuffer->next; + + if (audioBuffer->next == NULL) lastAudioBuffer = audioBuffer->prev; + else audioBuffer->next->prev = audioBuffer->prev; + + audioBuffer->prev = NULL; + audioBuffer->next = NULL; + } + + mal_mutex_unlock(&audioLock); +} #endif //---------------------------------------------------------------------------------- @@ -883,13 +914,13 @@ Sound LoadSoundFromWave(Wave wave) mal_uint32 frameCountIn = wave.sampleCount; // Is wave->sampleCount actually the frame count? That terminology needs to change, if so. mal_uint32 frameCount = mal_convert_frames(NULL, DEVICE_FORMAT, DEVICE_CHANNELS, DEVICE_SAMPLE_RATE, NULL, formatIn, wave.channels, wave.sampleRate, frameCountIn); - if (frameCount == 0) TraceLog(LOG_ERROR, "LoadSoundFromWave() : Failed to get frame count for format conversion"); + if (frameCount == 0) TraceLog(LOG_WARNING, "LoadSoundFromWave() : Failed to get frame count for format conversion"); AudioBuffer* audioBuffer = CreateAudioBuffer(DEVICE_FORMAT, DEVICE_CHANNELS, DEVICE_SAMPLE_RATE, frameCount, AUDIO_BUFFER_USAGE_STATIC); - if (audioBuffer == NULL) TraceLog(LOG_ERROR, "LoadSoundFromWave() : Failed to create audio buffer"); + if (audioBuffer == NULL) TraceLog(LOG_WARNING, "LoadSoundFromWave() : Failed to create audio buffer"); frameCount = mal_convert_frames(audioBuffer->buffer, audioBuffer->dsp.config.formatIn, audioBuffer->dsp.config.channelsIn, audioBuffer->dsp.config.sampleRateIn, wave.data, formatIn, wave.channels, wave.sampleRate, frameCountIn); - if (frameCount == 0) TraceLog(LOG_ERROR, "LoadSoundFromWave() : Format conversion failed"); + if (frameCount == 0) TraceLog(LOG_WARNING, "LoadSoundFromWave() : Format conversion failed"); sound.audioBuffer = audioBuffer; #else @@ -1853,7 +1884,7 @@ void CloseAudioStream(AudioStream stream) void UpdateAudioStream(AudioStream stream, const void *data, int samplesCount) { #if USE_MINI_AL - AudioBuffer* audioBuffer = (AudioBuffer*)stream.audioBuffer; + AudioBuffer *audioBuffer = (AudioBuffer *)stream.audioBuffer; if (audioBuffer == NULL) { TraceLog(LOG_ERROR, "UpdateAudioStream() : No audio buffer"); -- cgit v1.2.3 From 231a69417ab5a9627fc9972392e7146ddea07106 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 11 Feb 2018 01:28:30 +0100 Subject: Corrected masteVolume setting --- src/audio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/audio.c b/src/audio.c index 55b43bea..baa5256f 100644 --- a/src/audio.c +++ b/src/audio.c @@ -633,7 +633,7 @@ void SetMasterVolume(float volume) else if (volume > 1.0f) volume = 1.0f; #if USE_MINI_AL - masterVolume = 1; + masterVolume = volume; #else alListenerf(AL_GAIN, volume); #endif -- cgit v1.2.3 From d90a33b850d825c3047fd570478fc8a66f22414f Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 11 Feb 2018 01:48:53 +0100 Subject: Some reviews for Android compilation --- src/Makefile | 14 ++++++++++---- src/core.c | 4 ++-- 2 files changed, 12 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/Makefile b/src/Makefile index e41a88a3..6752d9e7 100644 --- a/src/Makefile +++ b/src/Makefile @@ -262,15 +262,20 @@ endif # -Wno-missing-braces ignore invalid warning (GCC bug 53119) # -D_DEFAULT_SOURCE use with -std=c99 on Linux and PLATFORM_WEB, required for timespec # -Werror=pointer-arith catch unportable code that does direct arithmetic on void pointers -# -Werror=implicit-function-declaration catch function calls without prior declaration -CFLAGS += -O1 -Wall -std=c99 -D_DEFAULT_SOURCE -fgnu89-inline -Wno-missing-braces -Werror=pointer-arith -Werror=implicit-function-declaration +CFLAGS += -O1 -Wall -std=c99 -D_DEFAULT_SOURCE -fgnu89-inline -Wno-missing-braces -Werror=pointer-arith ifeq ($(RAYLIB_BUILD_MODE), DEBUG) CFLAGS += -g endif # Additional flags for compiler (if desired) -#CFLAGS += -Wextra -Wmissing-prototypes -Wstrict-prototypes +# -Wextra enables some extra warning flags that are not enabled by -Wall +# -Wmissing-prototypes warn if a global function is defined without a previous prototype declaration +# -Wstrict-prototypes warn if a function is declared or defined without specifying the argument types +# -Werror=implicit-function-declaration catch function calls without prior declaration +ifeq ($(PLATFORM),PLATFORM_DESKTOP) + CFLAGS += -Werror=implicit-function-declaration +endif ifeq ($(PLATFORM),PLATFORM_WEB) # -O2 # if used, also set --memory-init-file 0 # --memory-init-file 0 # to avoid an external memory initialization code file (.mem) @@ -285,7 +290,8 @@ ifeq ($(PLATFORM),PLATFORM_ANDROID) # Compilation functions attributes options CFLAGS += -ffunction-sections -funwind-tables -fstack-protector-strong -fPIC # Compiler options for the linker - CFLAGS += -Wa,--noexecstack -Wformat -Werror=format-security -no-canonical-prefixes + # -Werror=format-security + CFLAGS += -Wa,--noexecstack -Wformat -no-canonical-prefixes # Preprocessor macro definitions CFLAGS += -DANDROID -DPLATFORM_ANDROID -D__ANDROID_API__=16 endif diff --git a/src/core.c b/src/core.c index d6a10943..ce017af2 100644 --- a/src/core.c +++ b/src/core.c @@ -294,11 +294,11 @@ static int renderOffsetX = 0; // Offset X from render area (must b static int renderOffsetY = 0; // Offset Y from render area (must be divided by 2) static bool fullscreen = false; // Fullscreen mode (useful only for PLATFORM_DESKTOP) static Matrix downscaleView; // Matrix to downscale view (in case screen size bigger than display size) +static bool cursorHidden = false; // Track if cursor is hidden +static bool cursorOnScreen = false; // Tracks if cursor is inside client area #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB) || defined(PLATFORM_UWP) static const char *windowTitle = NULL; // Window text title... -static bool cursorHidden = false; // Track if cursor is hidden -static bool cursorOnScreen = false; // Tracks if cursor is inside client area static int screenshotCounter = 0; // Screenshots counter // Register mouse states -- cgit v1.2.3 From 09b022305f3fd365d5d0424ff51982bcca5a572f Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Sun, 11 Feb 2018 11:59:36 +0100 Subject: mini_al: Support {Net,Open}BSD OSS Fixes this build failure: http://www.cpantesters.org/cpan/report/a069fade-0e1f-11e8-a1cf-bb670eaac09d --- src/external/mini_al.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/external/mini_al.h b/src/external/mini_al.h index 35846385..c9ab40fa 100644 --- a/src/external/mini_al.h +++ b/src/external/mini_al.h @@ -59,7 +59,7 @@ // // Building (BSD) // -------------- -// The BSD build uses OSS and should Just Work without any linking nor include path configuration. +// BSD build uses OSS. Requires linking to -lossaudio on {Open,Net}BSD, but not FreeBSD. // // Building (Emscripten) // --------------------- @@ -6712,6 +6712,10 @@ static mal_result mal_device__main_loop__alsa(mal_device* pDevice) #include #include +#ifndef SNDCTL_DSP_HALT +#define SNDCTL_DSP_HALT SNDCTL_DSP_RESET +#endif + int mal_open_temp_device__oss() { // The OSS sample code uses "/dev/mixer" as the device for getting system properties so I'm going to do the same. -- cgit v1.2.3 From 1be72a2e72bcae18241d4a6dd6427aeaaf54e4d5 Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Sun, 11 Feb 2018 20:00:04 +0100 Subject: pkg-config: Empty Requires.private on shared-only build If user doesn't build the static library, `pkg-config --static --libs raylib` should be equivalent to `pkg-config --libs raylib`. --- src/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e9784ff4..40871455 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -137,6 +137,8 @@ if(${PLATFORM} MATCHES "PLATFORM_DESKTOP") PUBLIC ${GRAPHICS} ) + set(PKG_CONFIG_LIBS_PRIVATE ${__PKG_CONFIG_LIBS_PRIVATE}) + if (WITH_PIC) set_property(TARGET ${RAYLIB} PROPERTY POSITION_INDEPENDENT_CODE ON) endif() @@ -149,7 +151,7 @@ if(${PLATFORM} MATCHES "PLATFORM_DESKTOP") configure_file(../raylib.pc.in raylib.pc @ONLY) install(FILES ${CMAKE_BINARY_DIR}/release/raylib.pc DESTINATION lib/pkgconfig) - + # Copy the header files to the build directory file(COPY "raylib.h" DESTINATION ".") file(COPY "rlgl.h" DESTINATION ".") -- cgit v1.2.3 From e86b4d4e03a91bfe37cb525b155af48724664e97 Mon Sep 17 00:00:00 2001 From: RDR8 Date: Sun, 11 Feb 2018 22:58:15 +0000 Subject: Update Makefiles for Linux installation. --- src/Makefile | 136 ++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 87 insertions(+), 49 deletions(-) (limited to 'src') diff --git a/src/Makefile b/src/Makefile index 6752d9e7..b44ff462 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2,7 +2,7 @@ # # raylib makefile # -# Platforms supported: +# Platforms supported: # PLATFORM_DESKTOP: Windows (Win32, Win64) # PLATFORM_DESKTOP: Linux (32 and 64 bit) # PLATFORM_DESKTOP: OSX/macOS @@ -42,38 +42,52 @@ .PHONY: all clean install uninstall # Define required raylib variables -PLATFORM ?= PLATFORM_DESKTOP -RAYLIB_PATH = .. RAYLIB_VERSION = 1.9.4 RAYLIB_API_VERSION = 1 +RAYLIB_PATH = .. + +# Define default options + +# RAYLIB_RELEASE_PATH points to provided binaries and your immediate build of raylib. +# It is further modified below by PLATFORM below. +RAYLIB_RELEASE_PATH ?= $(RAYLIB_PATH)/release/libs + +# See install target for *_INSTALL_PATH locations. + +# One of PLATFORM_DESKTOP, PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +PLATFORM ?= PLATFORM_DESKTOP + # Library type used for raylib: STATIC (.a) or SHARED (.so/.dll) -RAYLIB_LIBTYPE ?= STATIC +RAYLIB_LIBTYPE ?= STATIC + # Build mode for library: DEBUG or RELEASE -RAYLIB_BUILD_MODE ?= RELEASE +RAYLIB_BUILD_MODE ?= RELEASE # Included raylib audio module on compilation # NOTE: Some programs like tools could not require audio support INCLUDE_AUDIO_MODULE ?= TRUE # Use OpenAL Soft backend for audio -USE_OPENAL_BACKEND ?= FALSE +USE_OPENAL_BACKEND ?= FALSE + +# OpenAL Soft audio backend forced on HTML5 and OSX (see below) +ifeq ($(PLATFORM),PLATFORM_WEB) + USE_OPENAL_BACKEND = TRUE +endif # Use external GLFW library instead of rglfw module -USE_EXTERNAL_GLFW ?= FALSE +USE_EXTERNAL_GLFW ?= FALSE # Use Wayland display server protocol on Linux desktop # by default it uses X11 windowing system -USE_WAYLAND_DISPLAY ?= FALSE +USE_WAYLAND_DISPLAY ?= FALSE -# OpenAL Soft audio backend forced on HTML5 and OSX (see below) -ifeq ($(PLATFORM),PLATFORM_WEB) - USE_OPENAL_BACKEND = TRUE -endif +# See below for more GRAPHICS options. # Use cross-compiler for PLATFORM_RPI ifeq ($(PLATFORM),PLATFORM_RPI) USE_RPI_CROSS_COMPILER ?= FALSE - + ifeq ($(USE_RPI_CROSS_COMPILER),TRUE) RPI_TOOLCHAIN ?= C:/SysGCC/Raspberry RPI_TOOLCHAIN_SYSROOT ?= $(RPI_TOOLCHAIN)/arm-linux-gnueabihf/sysroot @@ -138,8 +152,6 @@ ifeq ($(PLATFORM),PLATFORM_ANDROID) ANDROID_ARCH ?= ARM endif -RAYLIB_RELEASE_PATH ?= $(RAYLIB_PATH)/release/libs - # Define output directory for compiled library ifeq ($(PLATFORM),PLATFORM_DESKTOP) ifeq ($(PLATFORM_OS),WINDOWS) @@ -299,7 +311,7 @@ endif # Define required compilation flags for raylib SHARED lib ifeq ($(RAYLIB_LIBTYPE),SHARED) # make sure code is compiled as position independent - # BE CAREFUL: It seems that for gcc -fpic si not the same as -fPIC + # BE CAREFUL: It seems that for gcc -fpic is not the same as -fPIC # MinGW32 just doesn't need -fPIC, it shows warnings CFLAGS += -fPIC -DBUILD_LIBTYPE_SHARED endif @@ -350,8 +362,8 @@ endif # Define linker options ifeq ($(PLATFORM),PLATFORM_ANDROID) - LDFLAGS = -Wl,-soname,libraylib.$(API_VERSION).so -Wl,--exclude-libs,libatomic.a - LDFLAGS += -Wl,--build-id -Wl,--no-undefined -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now -Wl,--warn-shared-textrel -Wl,--fatal-warnings + LDFLAGS = -Wl,-soname,libraylib.$(API_VERSION).so -Wl,--exclude-libs,libatomic.a + LDFLAGS += -Wl,--build-id -Wl,--no-undefined -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now -Wl,--warn-shared-textrel -Wl,--fatal-warnings # Force linking of library module to define symbol LDFLAGS += -u ANativeActivity_onCreate # Library paths containing required libs @@ -374,7 +386,7 @@ OBJS = core.o \ text.o \ models.o \ utils.o - + ifeq ($(PLATFORM),PLATFORM_DESKTOP) ifeq ($(USE_EXTERNAL_GLFW),FALSE) OBJS += rglfw.o @@ -419,10 +431,10 @@ else ifeq ($(PLATFORM_OS),LINUX) # Compile raylib to shared library version for GNU/Linux. # WARNING: you should type "make clean" before doing this target - $(CC) -shared -o $(RAYLIB_RELEASE_PATH)/libraylib.$(RAYLIB_VERSION).so $(OBJS) -Wl,-soname,libraylib.$(RAYLIB_API_VERSION).so -lGL -lm -lpthread -ldl -lrt - @echo "raylib shared library generated (libraylib.$(RAYLIB_VERSION).so)!" - cd $(RAYLIB_RELEASE_PATH) && ln -fs libraylib.$(RAYLIB_VERSION).so libraylib.$(RAYLIB_API_VERSION).so - cd $(RAYLIB_RELEASE_PATH) && ln -fs libraylib.$(RAYLIB_VERSION).so libraylib.so + $(CC) -shared -o $(RAYLIB_RELEASE_PATH)/libraylib.so.$(RAYLIB_VERSION) $(OBJS) -shared -Wl,-soname,libraylib.so.$(RAYLIB_API_VERSION) -lGL -lc -lm -lpthread -ldl -lrt + @echo "raylib shared library generated (libraylib.so.$(RAYLIB_VERSION))!" + cd $(RAYLIB_RELEASE_PATH) && ln -fsv libraylib.so.$(RAYLIB_VERSION) libraylib.so.$(RAYLIB_API_VERSION) + cd $(RAYLIB_RELEASE_PATH) && ln -fsv libraylib.so.$(RAYLIB_API_VERSION) libraylib.so endif ifeq ($(PLATFORM_OS),OSX) $(CC) -dynamiclib -o $(RAYLIB_RELEASE_PATH)/libraylib.$(RAYLIB_VERSION).dylib $(OBJS) -compatibility_version $(RAYLIB_API_VERSION) -current_version $(RAYLIB_VERSION) -framework OpenGL -framework OpenAL -framework IOKit -framework CoreVideo -framework Cocoa @@ -457,7 +469,7 @@ endif # Compile core module core.o : core.c raylib.h rlgl.h utils.h raymath.h gestures.h $(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS) -D$(PLATFORM) -D$(GRAPHICS) - + # Compile rglfw module rglfw.o : rglfw.c $(CC) $(GLFW_CFLAGS) -c $< $(CFLAGS) $(INCLUDE_PATHS) -D$(PLATFORM) -D$(GRAPHICS) @@ -485,10 +497,10 @@ models.o : models.c raylib.h rlgl.h raymath.h # Compile audio module audio.o : audio.c raylib.h $(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS) -D$(PLATFORM) - + # Compile mini_al audio library mini_al.o : external/mini_al.c external/mini_al.h - $(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS) -D$(PLATFORM) + $(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS) -D$(PLATFORM) # Compile stb_vorbis library stb_vorbis.o: external/stb_vorbis.c external/stb_vorbis.h @@ -499,49 +511,75 @@ utils.o : utils.c utils.h $(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS) -D$(PLATFORM) # Install generated and needed files to required directories -# TODO: add other platforms. +# TODO: Add other platforms. Remove sudo requirement, i.e. add USER mode. +# On GNU/Linux and BSDs, there are some standard directories that contain extra +# libraries and header files. These directories (often /usr/local/lib and +# /usr/local/include) are for libraries that are installed manually +# (without a package manager). We'll use /usr/local/lib/raysan5 and /usr/local/include/raysan5 +# for our -L and -I specification to simplify management of the raylib source package. +# Customize these locations if you like but don't forget to pass them to make +# for compilation and enable runtime linking with -rpath, LD_LIBRARY_PATH, or ldconfig,. +# Hint: add -L$(RAYLIB_INSTALL_PATH) -I$(RAYLIB_H_INSTALL_PATH) to your own makefiles. +# See below and ../examples/Makefile for more information. + +# RAYLIB_INSTALL_PATH should be the desired full path to libraylib. No relative paths. +RAYLIB_INSTALL_PATH ?= /usr/local/lib/raysan5 +# RAYLIB_H_INSTALL_PATH locates the installed raylib header and associated source files. +RAYLIB_H_INSTALL_PATH ?= /usr/local/include/raysan5 + install : ifeq ($(ROOT),root) + # Attention! You are root. Consult this Makefile for more information." ifeq ($(PLATFORM_OS),LINUX) - # On GNU/Linux there are some standard directories that contain - # libraries and header files. These directory (/usr/local/lib and - # /usr/local/include/) are for libraries that are installed - # manually (without a package manager). - ifeq ($(RAYLIB_LIBTYPE),SHARED) - cp --update $(RAYLIB_RELEASE_PATH)/libraylib.$(RAYLIB_VERSION).so /usr/local/lib/libraylib.$(RAYLIB_VERSION).so - cp --update $(RAYLIB_RELEASE_PATH)/libraylib.$(RAYLIB_API_VERSION).so /usr/local/lib/libraylib.$(RAYLIB_API_VERSION).so - cp --update $(RAYLIB_RELEASE_PATH)/libraylib.so /usr/local/lib/libraylib.so - else - cp --update raylib.h /usr/local/include/raylib.h - cp --update $(RAYLIB_RELEASE_PATH)/libraylib.a /usr/local/lib/libraylib.a - endif + # Prepare the environment as needed. + mkdir --parents --verbose $(RAYLIB_INSTALL_PATH) + mkdir --parents --verbose $(RAYLIB_H_INSTALL_PATH) + ifeq ($(RAYLIB_LIBTYPE),SHARED) + # Installing the shared library. + cp --update --verbose $(RAYLIB_RELEASE_PATH)/libraylib.so.$(RAYLIB_VERSION) $(RAYLIB_INSTALL_PATH)/libraylib.so.$(RAYLIB_VERSION) + cd $(RAYLIB_INSTALL_PATH); ln -fsv libraylib.so.$(RAYLIB_VERSION) libraylib.so.$(RAYLIB_API_VERSION) + cd $(RAYLIB_INSTALL_PATH); ln -fsv libraylib.so.$(RAYLIB_API_VERSION) libraylib.so + # Uncomment to update the runtime linker cache with RAYLIB_INSTALL_PATH. + # Not necessary if later embedding RPATH in your executable. See examples/Makefile. + ldconfig $(RAYLIB_INSTALL_PATH) + else + # Installing the static library. + cp --update --verbose $(RAYLIB_RELEASE_PATH)/libraylib.a $(RAYLIB_INSTALL_PATH)/libraylib.a + endif + # Let's have all the source. + cp --update --recursive $(RAYLIB_PATH)/src/*.h $(RAYLIB_H_INSTALL_PATH)/ + cp --update --recursive $(RAYLIB_PATH)/src/*.c $(RAYLIB_H_INSTALL_PATH)/ + cp --update --recursive $(RAYLIB_PATH)/src/external $(RAYLIB_H_INSTALL_PATH)/ + cp --update --recursive $(RAYLIB_PATH)/release/include/AL $(RAYLIB_H_INSTALL_PATH)/external @echo "raylib dev files installed/updated!" else - @echo "This function works only on GNU/Linux systems" + @echo "This function currently works on GNU/Linux systems. Add yours today (^;" endif else - @echo "Error: no root permissions" + @echo "Error: Root permissions needed for installation. Try sudo make install" endif # Remove raylib dev files installed on the system # TODO: see 'install' target. uninstall : ifeq ($(ROOT),root) + # Warning! You are root. Please confirm that there is nothing here to keep. + # Proceeding will remove everything under the specified locations! ifeq ($(PLATFORM_OS),LINUX) - rm --force /usr/local/include/raylib.h ifeq ($(RAYLIB_LIBTYPE),SHARED) - rm --force /usr/local/lib/libraylib.so - rm --force /usr/local/lib/libraylib.$(RAYLIB_API_VERSION).so - rm --force /usr/local/lib/libraylib.$(RAYLIB_VERSION).so + rm --force --interactive --verbose $(RAYLIB_INSTALL_PATH)/libraylib.so* + # Uncomment to clean up the runtime linker cache. See install target. + ldconfig else - rm --force /usr/local/lib/libraylib.a + rm --force --interactive --verbose $(RAYLIB_INSTALL_PATH)/libraylib.a endif - @echo "raylib development files removed!" + rm --force --interactive=once --recursive $(RAYLIB_H_INSTALL_PATH)/* + @echo "raylib development files removed!" else @echo "This function works only on GNU/Linux systems" endif else - @echo "Error: no root permissions" + @echo "Error: Root permissions needed for uninstallation. Try sudo make uninstall" endif # Clean everything @@ -549,7 +587,7 @@ clean: ifeq ($(PLATFORM_OS),WINDOWS) del *.o $(RAYLIB_RELEASE_PATH)/libraylib.a $(RAYLIB_RELEASE_PATH)/libraylib.bc $(RAYLIB_RELEASE_PATH)/libraylib.so external/stb_vorbis.o else - rm -f *.o $(RAYLIB_RELEASE_PATH)/libraylib.a $(RAYLIB_RELEASE_PATH)/libraylib.bc $(RAYLIB_RELEASE_PATH)/libraylib.so $(RAYLIB_RELEASE_PATH)/libraylib.$(RAYLIB_API_VERSION).so $(RAYLIB_RELEASE_PATH)/libraylib.$(RAYLIB_VERSION).so external/stb_vorbis.o + rm -fv *.o $(RAYLIB_RELEASE_PATH)/libraylib.a $(RAYLIB_RELEASE_PATH)/libraylib.bc $(RAYLIB_RELEASE_PATH)/libraylib.so* external/stb_vorbis.o endif ifeq ($(PLATFORM),PLATFORM_ANDROID) rm -rf $(ANDROID_TOOLCHAIN) -- cgit v1.2.3 From 4c4b5643925f442ff7d5bcbd8e6670b993d00b10 Mon Sep 17 00:00:00 2001 From: RDR8 Date: Sun, 11 Feb 2018 23:42:22 +0000 Subject: Update Makefiles for Linux installation. See prior commit. These changes are intended to provide context and control over the Linux make install process. make install RAYLIB_LIBTYPE=SHARED now works as expected. libraylib.so is properly installed with attendant runtime links. The examples will be running against the newly installed libraylib.so unless otherwise specified with RAYLIB_RUNTIME_PATH. See raylib/src/Makefile and raylib/examples/Makefile for usage of RAYLIB_INSTALL_PATH, RAYLIB_H_INSTALL_PATH, and RAYLIB_RUNTIME_PATH variables. RAYLIB_RUNTIME_PATH in particular is interesting for portability since sudo isn't needed. The default configuration and workflow is essentially unchanged and unaffected. --- src/Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/Makefile b/src/Makefile index b44ff462..79bdf97f 100644 --- a/src/Makefile +++ b/src/Makefile @@ -518,7 +518,7 @@ utils.o : utils.c utils.h # (without a package manager). We'll use /usr/local/lib/raysan5 and /usr/local/include/raysan5 # for our -L and -I specification to simplify management of the raylib source package. # Customize these locations if you like but don't forget to pass them to make -# for compilation and enable runtime linking with -rpath, LD_LIBRARY_PATH, or ldconfig,. +# for compilation and enable runtime linking with -rpath, LD_LIBRARY_PATH, or ldconfig. # Hint: add -L$(RAYLIB_INSTALL_PATH) -I$(RAYLIB_H_INSTALL_PATH) to your own makefiles. # See below and ../examples/Makefile for more information. @@ -568,6 +568,8 @@ ifeq ($(ROOT),root) ifeq ($(PLATFORM_OS),LINUX) ifeq ($(RAYLIB_LIBTYPE),SHARED) rm --force --interactive --verbose $(RAYLIB_INSTALL_PATH)/libraylib.so* + rm --force --interactive --verbose $(RAYLIB_INSTALL_PATH)/libraylib.so.$(RAYLIB_API_VERSION) + rm --force --interactive --verbose $(RAYLIB_INSTALL_PATH)/libraylib.so.$(RAYLIB_VERSION) # Uncomment to clean up the runtime linker cache. See install target. ldconfig else -- cgit v1.2.3 From 5616314069829877301312add7c3b55ba4042eac Mon Sep 17 00:00:00 2001 From: RDR8 Date: Mon, 12 Feb 2018 00:15:58 +0000 Subject: Update Makefile --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/Makefile b/src/Makefile index 79bdf97f..61206a3d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -567,7 +567,7 @@ ifeq ($(ROOT),root) # Proceeding will remove everything under the specified locations! ifeq ($(PLATFORM_OS),LINUX) ifeq ($(RAYLIB_LIBTYPE),SHARED) - rm --force --interactive --verbose $(RAYLIB_INSTALL_PATH)/libraylib.so* + rm --force --interactive --verbose $(RAYLIB_INSTALL_PATH)/libraylib.so rm --force --interactive --verbose $(RAYLIB_INSTALL_PATH)/libraylib.so.$(RAYLIB_API_VERSION) rm --force --interactive --verbose $(RAYLIB_INSTALL_PATH)/libraylib.so.$(RAYLIB_VERSION) # Uncomment to clean up the runtime linker cache. See install target. -- cgit v1.2.3 From f742f1289d9e00c8d09a5a6dc4c36efaa0e10d67 Mon Sep 17 00:00:00 2001 From: RDR8 Date: Mon, 12 Feb 2018 05:17:02 +0000 Subject: Update Makefile --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/Makefile b/src/Makefile index 61206a3d..f8f9635b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -529,7 +529,7 @@ RAYLIB_H_INSTALL_PATH ?= /usr/local/include/raysan5 install : ifeq ($(ROOT),root) - # Attention! You are root. Consult this Makefile for more information." + # Attention! You are root. Consult this Makefile for more information. ifeq ($(PLATFORM_OS),LINUX) # Prepare the environment as needed. mkdir --parents --verbose $(RAYLIB_INSTALL_PATH) -- cgit v1.2.3 From 8af5f9dfe0e23d918bb88a5fca9e5671c9100f3a Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 12 Feb 2018 11:25:00 +0100 Subject: Avoid rendering SPACE character! --- src/text.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/text.c b/src/text.c index e2629b09..227992e6 100644 --- a/src/text.c +++ b/src/text.c @@ -417,12 +417,15 @@ void DrawTextEx(SpriteFont spriteFont, const char *text, Vector2 position, float i++; } else index = GetCharIndex(spriteFont, (unsigned char)text[i]); - - DrawTexturePro(spriteFont.texture, spriteFont.chars[index].rec, + + if ((unsigned char)text[i] != ' ') + { + DrawTexturePro(spriteFont.texture, spriteFont.chars[index].rec, (Rectangle){ position.x + textOffsetX + spriteFont.chars[index].offsetX*scaleFactor, position.y + textOffsetY + spriteFont.chars[index].offsetY*scaleFactor, spriteFont.chars[index].rec.width*scaleFactor, spriteFont.chars[index].rec.height*scaleFactor }, (Vector2){ 0, 0 }, 0.0f, tint); + } if (spriteFont.chars[index].advanceX == 0) textOffsetX += (int)(spriteFont.chars[index].rec.width*scaleFactor + spacing); else textOffsetX += (int)(spriteFont.chars[index].advanceX*scaleFactor + spacing); -- cgit v1.2.3