Update C sources, add new functions

This commit is contained in:
Milan Nikolic 2018-11-17 21:34:52 +01:00
parent adf3401c3c
commit 48689803b8
28 changed files with 2681 additions and 1918 deletions

File diff suppressed because it is too large Load diff

View file

@ -107,6 +107,14 @@ func UnloadSound(sound Sound) {
C.UnloadSound(*csound) C.UnloadSound(*csound)
} }
// ExportWave - Export wave data to file
func ExportWave(wave Wave, fileName string) {
cwave := wave.cptr()
cfileName := C.CString(fileName)
defer C.free(unsafe.Pointer(cfileName))
C.ExportWave(*cwave, cfileName)
}
// PlaySound - Play a sound // PlaySound - Play a sound
func PlaySound(sound Sound) { func PlaySound(sound Sound) {
csound := sound.cptr() csound := sound.cptr()

View file

@ -6,6 +6,6 @@ package rl
#include "external/android/native_app_glue/android_native_app_glue.c" #include "external/android/native_app_glue/android_native_app_glue.c"
#cgo android LDFLAGS: -llog -landroid -lEGL -lGLESv2 -lOpenSLES -lm #cgo android LDFLAGS: -llog -landroid -lEGL -lGLESv2 -lOpenSLES -lm
#cgo android CFLAGS: -DPLATFORM_ANDROID -DGRAPHICS_API_OPENGL_ES2 -DMAL_NO_SDL -Iexternal -Iexternal/android/native_app_glue #cgo android CFLAGS: -DPLATFORM_ANDROID -DGRAPHICS_API_OPENGL_ES2 -DMAL_NO_SDL -Iexternal/android/native_app_glue
*/ */
import "C" import "C"

View file

@ -21,7 +21,7 @@ package rl
#include "external/glfw/src/osmesa_context.c" #include "external/glfw/src/osmesa_context.c"
#cgo darwin LDFLAGS: -framework OpenGL -framework Cocoa -framework IOKit -framework CoreVideo -framework CoreFoundation #cgo darwin LDFLAGS: -framework OpenGL -framework Cocoa -framework IOKit -framework CoreVideo -framework CoreFoundation
#cgo darwin CFLAGS: -x objective-c -Iexternal -Iexternal/glfw/include -D_GLFW_COCOA -D_GLFW_USE_CHDIR -D_GLFW_USE_MENUBAR -D_GLFW_USE_RETINA -Wno-deprecated-declarations -DPLATFORM_DESKTOP -DMAL_NO_COREAUDIO #cgo darwin CFLAGS: -x objective-c -Iexternal/glfw/include -D_GLFW_COCOA -D_GLFW_USE_CHDIR -D_GLFW_USE_MENUBAR -D_GLFW_USE_RETINA -Wno-deprecated-declarations -DPLATFORM_DESKTOP -DMAL_NO_COREAUDIO
#cgo darwin,opengl11 CFLAGS: -DGRAPHICS_API_OPENGL_11 #cgo darwin,opengl11 CFLAGS: -DGRAPHICS_API_OPENGL_11
#cgo darwin,opengl21 CFLAGS: -DGRAPHICS_API_OPENGL_21 #cgo darwin,opengl21 CFLAGS: -DGRAPHICS_API_OPENGL_21

View file

@ -34,7 +34,7 @@ package rl
#include "external/glfw/src/egl_context.c" #include "external/glfw/src/egl_context.c"
#include "external/glfw/src/osmesa_context.c" #include "external/glfw/src/osmesa_context.c"
#cgo linux CFLAGS: -Iexternal -Iexternal/glfw/include -DPLATFORM_DESKTOP -Wno-stringop-overflow #cgo linux CFLAGS: -Iexternal/glfw/include -DPLATFORM_DESKTOP -Wno-stringop-overflow
#cgo linux,!wayland LDFLAGS: -lGL -lm -pthread -ldl -lrt -lX11 #cgo linux,!wayland LDFLAGS: -lGL -lm -pthread -ldl -lrt -lX11
#cgo linux,wayland LDFLAGS: -lGL -lm -pthread -ldl -lrt -lwayland-client -lwayland-cursor -lwayland-egl -lxkbcommon #cgo linux,wayland LDFLAGS: -lGL -lm -pthread -ldl -lrt -lwayland-client -lwayland-cursor -lwayland-egl -lxkbcommon

View file

@ -4,6 +4,6 @@ package rl
/* /*
#cgo linux,arm LDFLAGS: -L/opt/vc/lib -L/opt/vc/lib64 -lbrcmGLESv2 -lbrcmEGL -lpthread -lrt -lm -lbcm_host -lvcos -lvchiq_arm -ldl #cgo linux,arm LDFLAGS: -L/opt/vc/lib -L/opt/vc/lib64 -lbrcmGLESv2 -lbrcmEGL -lpthread -lrt -lm -lbcm_host -lvcos -lvchiq_arm -ldl
#cgo linux,arm CFLAGS: -DPLATFORM_RPI -DGRAPHICS_API_OPENGL_ES2 -Iexternal -I/opt/vc/include -I/opt/vc/include/interface/vcos -I/opt/vc/include/interface/vmcs_host/linux -I/opt/vc/include/interface/vcos/pthreads #cgo linux,arm CFLAGS: -DPLATFORM_RPI -DGRAPHICS_API_OPENGL_ES2 -I/opt/vc/include -I/opt/vc/include/interface/vcos -I/opt/vc/include/interface/vmcs_host/linux -I/opt/vc/include/interface/vcos/pthreads
*/ */
import "C" import "C"

View file

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

File diff suppressed because it is too large Load diff

198
raylib/external/dirent.h vendored Normal file
View file

@ -0,0 +1,198 @@
#ifndef DIRENT_H
#define DIRENT_H
/*
Declaration of POSIX directory browsing functions and types for Win32.
Author: Kevlin Henney (kevlin@acm.org, kevlin@curbralan.com)
History: Created March 1997. Updated June 2003.
Rights: See end of file.
*/
#ifdef __cplusplus
extern "C"
{
#endif
typedef struct DIR DIR;
struct dirent
{
char *d_name;
};
DIR *opendir(const char *);
int closedir(DIR *);
struct dirent *readdir(DIR *);
void rewinddir(DIR *);
/*
Copyright Kevlin Henney, 1997, 2003. All rights reserved.
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose is hereby granted without fee, provided
that this copyright and permissions notice appear in all copies and
derivatives.
This software is supplied "as is" without express or implied warranty.
But that said, if there are any problems please get in touch.
*/
#ifdef __cplusplus
}
#endif
#endif // DIRENT_H
/*
Implementation of POSIX directory browsing functions and types for Win32.
Author: Kevlin Henney (kevlin@acm.org, kevlin@curbralan.com)
History: Created March 1997. Updated June 2003 and July 2012.
Rights: See end of file.
*/
#include <errno.h>
#include <io.h> /* _findfirst and _findnext set errno iff they return -1 */
#include <stdlib.h>
#include <string.h>
#ifdef __cplusplus
extern "C"
{
#endif
typedef ptrdiff_t handle_type; /* C99's intptr_t not sufficiently portable */
struct DIR
{
handle_type handle; /* -1 for failed rewind */
struct _finddata_t info;
struct dirent result; /* d_name null iff first time */
char *name; /* null-terminated char string */
};
DIR *opendir(const char *name)
{
DIR *dir = 0;
if(name && name[0])
{
size_t base_length = strlen(name);
const char *all = /* search pattern must end with suitable wildcard */
strchr("/\\", name[base_length - 1]) ? "*" : "/*";
if((dir = (DIR *) malloc(sizeof *dir)) != 0 &&
(dir->name = (char *) malloc(base_length + strlen(all) + 1)) != 0)
{
strcat(strcpy(dir->name, name), all);
if((dir->handle =
(handle_type) _findfirst(dir->name, &dir->info)) != -1)
{
dir->result.d_name = 0;
}
else /* rollback */
{
free(dir->name);
free(dir);
dir = 0;
}
}
else /* rollback */
{
free(dir);
dir = 0;
errno = ENOMEM;
}
}
else
{
errno = EINVAL;
}
return dir;
}
int closedir(DIR *dir)
{
int result = -1;
if(dir)
{
if(dir->handle != -1)
{
result = _findclose(dir->handle);
}
free(dir->name);
free(dir);
}
if(result == -1) /* map all errors to EBADF */
{
errno = EBADF;
}
return result;
}
struct dirent *readdir(DIR *dir)
{
struct dirent *result = 0;
if(dir && dir->handle != -1)
{
if(!dir->result.d_name || _findnext(dir->handle, &dir->info) != -1)
{
result = &dir->result;
result->d_name = dir->info.name;
}
}
else
{
errno = EBADF;
}
return result;
}
void rewinddir(DIR *dir)
{
if(dir && dir->handle != -1)
{
_findclose(dir->handle);
dir->handle = (handle_type) _findfirst(dir->name, &dir->info);
dir->result.d_name = 0;
}
else
{
errno = EBADF;
}
}
#ifdef __cplusplus
}
#endif
/*
Copyright Kevlin Henney, 1997, 2003, 2012. All rights reserved.
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose is hereby granted without fee, provided
that this copyright and permissions notice appear in all copies and
derivatives.
This software is supplied "as is" without express or implied warranty.
But that said, if there are any problems please get in touch.
*/

File diff suppressed because it is too large Load diff

View file

@ -819,6 +819,12 @@ extern "C" {
* Mouse cursor hover [window attribute](@ref GLFW_HOVERED_attrib). * Mouse cursor hover [window attribute](@ref GLFW_HOVERED_attrib).
*/ */
#define GLFW_HOVERED 0x0002000B #define GLFW_HOVERED 0x0002000B
/*! @brief Input focus on calling show window hint and attribute
*
* Input focus [window hint](@ref GLFW_FOCUS_ON_SHOW_hint) or
* [window attribute](@ref GLFW_FOCUS_ON_SHOW_attrib).
*/
#define GLFW_FOCUS_ON_SHOW 0x0002000C
/*! @brief Framebuffer bit depth hint. /*! @brief Framebuffer bit depth hint.
* *
@ -3085,6 +3091,11 @@ GLFWAPI void glfwMaximizeWindow(GLFWwindow* window);
* hidden. If the window is already visible or is in full screen mode, this * hidden. If the window is already visible or is in full screen mode, this
* function does nothing. * function does nothing.
* *
* By default, windowed mode windows are focused when shown
* Set the [GLFW_FOCUS_ON_SHOW](@ref GLFW_FOCUS_ON_SHOW_hint) window hint
* to change this behavior for all newly created windows, or change the
* behavior for an existing window with @ref glfwSetWindowAttrib.
*
* @param[in] window The window to make visible. * @param[in] window The window to make visible.
* *
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
@ -3132,6 +3143,10 @@ GLFWAPI void glfwHideWindow(GLFWwindow* window);
* initially created. Set the [GLFW_FOCUSED](@ref GLFW_FOCUSED_hint) to * initially created. Set the [GLFW_FOCUSED](@ref GLFW_FOCUSED_hint) to
* disable this behavior. * disable this behavior.
* *
* Also by default, windowed mode windows are focused when shown
* with @ref glfwShowWindow. Set the
* [GLFW_FOCUS_ON_SHOW](@ref GLFW_FOCUS_ON_SHOW_hint) to disable this behavior.
*
* __Do not use this function__ to steal focus from other applications unless * __Do not use this function__ to steal focus from other applications unless
* you are certain that is what the user wants. Focus stealing can be * you are certain that is what the user wants. Focus stealing can be
* extremely disruptive. * extremely disruptive.
@ -3306,8 +3321,9 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* window, int attrib);
* *
* The supported attributes are [GLFW_DECORATED](@ref GLFW_DECORATED_attrib), * The supported attributes are [GLFW_DECORATED](@ref GLFW_DECORATED_attrib),
* [GLFW_RESIZABLE](@ref GLFW_RESIZABLE_attrib), * [GLFW_RESIZABLE](@ref GLFW_RESIZABLE_attrib),
* [GLFW_FLOATING](@ref GLFW_FLOATING_attrib) and * [GLFW_FLOATING](@ref GLFW_FLOATING_attrib),
* [GLFW_AUTO_ICONIFY](@ref GLFW_AUTO_ICONIFY_attrib). * [GLFW_AUTO_ICONIFY](@ref GLFW_AUTO_ICONIFY_attrib) and
* [GLFW_FOCUS_ON_SHOW](@ref GLFW_FOCUS_ON_SHOW_attrib).
* *
* Some of these attributes are ignored for full screen windows. The new * Some of these attributes are ignored for full screen windows. The new
* value will take effect if the window is later made windowed. * value will take effect if the window is later made windowed.

View file

@ -101,7 +101,10 @@ extern "C" {
#if defined(__OBJC__) #if defined(__OBJC__)
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#else #else
typedef void* id; // RAY: Added protection in case OBJC types defined
#if !OBJC_TYPES_DEFINED
typedef void* id;
#endif
#endif #endif
#elif defined(GLFW_EXPOSE_NATIVE_X11) #elif defined(GLFW_EXPOSE_NATIVE_X11)
#include <X11/Xlib.h> #include <X11/Xlib.h>

View file

@ -32,7 +32,6 @@
#include <math.h> #include <math.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <ctype.h>
// Internal key state used for sticky keys // Internal key state used for sticky keys
#define _GLFW_STICK 3 #define _GLFW_STICK 3
@ -1086,7 +1085,9 @@ GLFWAPI int glfwUpdateGamepadMappings(const char* string)
while (*c) while (*c)
{ {
if (isxdigit(*c)) if ((*c >= '0' && *c <= '9') ||
(*c >= 'a' && *c <= 'f') ||
(*c >= 'A' && *c <= 'F'))
{ {
char line[1024]; char line[1024];

View file

@ -267,6 +267,7 @@ struct _GLFWwndconfig
GLFWbool floating; GLFWbool floating;
GLFWbool maximized; GLFWbool maximized;
GLFWbool centerCursor; GLFWbool centerCursor;
GLFWbool focusOnShow;
struct { struct {
GLFWbool retina; GLFWbool retina;
char frameName[256]; char frameName[256];
@ -372,6 +373,7 @@ struct _GLFWwindow
GLFWbool decorated; GLFWbool decorated;
GLFWbool autoIconify; GLFWbool autoIconify;
GLFWbool floating; GLFWbool floating;
GLFWbool focusOnShow;
GLFWbool shouldClose; GLFWbool shouldClose;
void* userPointer; void* userPointer;
GLFWvidmode videoMode; GLFWvidmode videoMode;

View file

@ -242,7 +242,9 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)(
#include "egl_context.h" #include "egl_context.h"
#include "osmesa_context.h" #include "osmesa_context.h"
#define _GLFW_WNDCLASSNAME L"GLFW30" #if !defined(_GLFW_WNDCLASSNAME)
#define _GLFW_WNDCLASSNAME L"GLFW30"
#endif
#define _glfw_dlopen(name) LoadLibraryA(name) #define _glfw_dlopen(name) LoadLibraryA(name)
#define _glfw_dlclose(handle) FreeLibrary((HMODULE) handle) #define _glfw_dlclose(handle) FreeLibrary((HMODULE) handle)

View file

@ -201,6 +201,7 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height,
window->decorated = wndconfig.decorated; window->decorated = wndconfig.decorated;
window->autoIconify = wndconfig.autoIconify; window->autoIconify = wndconfig.autoIconify;
window->floating = wndconfig.floating; window->floating = wndconfig.floating;
window->focusOnShow = wndconfig.focusOnShow;
window->cursorMode = GLFW_CURSOR_NORMAL; window->cursorMode = GLFW_CURSOR_NORMAL;
window->minwidth = GLFW_DONT_CARE; window->minwidth = GLFW_DONT_CARE;
@ -267,6 +268,7 @@ void glfwDefaultWindowHints(void)
_glfw.hints.window.focused = GLFW_TRUE; _glfw.hints.window.focused = GLFW_TRUE;
_glfw.hints.window.autoIconify = GLFW_TRUE; _glfw.hints.window.autoIconify = GLFW_TRUE;
_glfw.hints.window.centerCursor = GLFW_TRUE; _glfw.hints.window.centerCursor = GLFW_TRUE;
_glfw.hints.window.focusOnShow = GLFW_TRUE;
// The default is 24 bits of color, 24 bits of depth and 8 bits of stencil, // The default is 24 bits of color, 24 bits of depth and 8 bits of stencil,
// double buffered // double buffered
@ -370,6 +372,9 @@ GLFWAPI void glfwWindowHint(int hint, int value)
case GLFW_CENTER_CURSOR: case GLFW_CENTER_CURSOR:
_glfw.hints.window.centerCursor = value ? GLFW_TRUE : GLFW_FALSE; _glfw.hints.window.centerCursor = value ? GLFW_TRUE : GLFW_FALSE;
return; return;
case GLFW_FOCUS_ON_SHOW:
_glfw.hints.window.focusOnShow = value ? GLFW_TRUE : GLFW_FALSE;
return;
case GLFW_CLIENT_API: case GLFW_CLIENT_API:
_glfw.hints.context.client = value; _glfw.hints.context.client = value;
return; return;
@ -755,7 +760,9 @@ GLFWAPI void glfwShowWindow(GLFWwindow* handle)
return; return;
_glfwPlatformShowWindow(window); _glfwPlatformShowWindow(window);
_glfwPlatformFocusWindow(window);
if (window->focusOnShow)
_glfwPlatformFocusWindow(window);
} }
GLFWAPI void glfwRequestWindowAttention(GLFWwindow* handle) GLFWAPI void glfwRequestWindowAttention(GLFWwindow* handle)
@ -810,6 +817,8 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib)
return _glfwPlatformWindowMaximized(window); return _glfwPlatformWindowMaximized(window);
case GLFW_HOVERED: case GLFW_HOVERED:
return _glfwPlatformWindowHovered(window); return _glfwPlatformWindowHovered(window);
case GLFW_FOCUS_ON_SHOW:
return window->focusOnShow;
case GLFW_TRANSPARENT_FRAMEBUFFER: case GLFW_TRANSPARENT_FRAMEBUFFER:
return _glfwPlatformFramebufferTransparent(window); return _glfwPlatformFramebufferTransparent(window);
case GLFW_RESIZABLE: case GLFW_RESIZABLE:
@ -886,6 +895,8 @@ GLFWAPI void glfwSetWindowAttrib(GLFWwindow* handle, int attrib, int value)
if (!window->monitor) if (!window->monitor)
_glfwPlatformSetWindowFloating(window, value); _glfwPlatformSetWindowFloating(window, value);
} }
else if (attrib == GLFW_FOCUS_ON_SHOW)
window->focusOnShow = value;
else else
_glfwInputError(GLFW_INVALID_ENUM, "Invalid window attribute 0x%08X", attrib); _glfwInputError(GLFW_INVALID_ENUM, "Invalid window attribute 0x%08X", attrib);
} }

View file

@ -2672,8 +2672,9 @@ void _glfwPlatformPollEvents(void)
#if defined(__linux__) #if defined(__linux__)
_glfwDetectJoystickConnectionLinux(); _glfwDetectJoystickConnectionLinux();
#endif #endif
int count = XPending(_glfw.x11.display); XPending(_glfw.x11.display);
while (count--)
while (XQLength(_glfw.x11.display))
{ {
XEvent event; XEvent event;
XNextEvent(_glfw.x11.display, &event); XNextEvent(_glfw.x11.display, &event);

View file

@ -1,5 +1,5 @@
// Audio playback and capture library. Public domain. See "unlicense" statement at the end of this file. // Audio playback and capture library. Public domain. See "unlicense" statement at the end of this file.
// mini_al - v0.8.8 - 2018-09-14 // mini_al - v0.8.10 - 2018-10-21
// //
// David Reid - davidreidsoftware@gmail.com // David Reid - davidreidsoftware@gmail.com
@ -1114,6 +1114,11 @@ void mal_pcm_f32_to_s24(void* pOut, const void* pIn, mal_uint64 count, mal_dithe
void mal_pcm_f32_to_s32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); void mal_pcm_f32_to_s32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_format formatIn, mal_uint64 sampleCount, mal_dither_mode ditherMode); void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_format formatIn, mal_uint64 sampleCount, mal_dither_mode ditherMode);
// Deinterleaves an interleaved buffer.
void mal_deinterleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint32 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames);
// Interleaves a group of deinterleaved buffers.
void mal_interleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint32 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -1629,6 +1634,7 @@ struct mal_context
mal_proc pa_stream_get_sample_spec; mal_proc pa_stream_get_sample_spec;
mal_proc pa_stream_get_channel_map; mal_proc pa_stream_get_channel_map;
mal_proc pa_stream_get_buffer_attr; mal_proc pa_stream_get_buffer_attr;
mal_proc pa_stream_set_buffer_attr;
mal_proc pa_stream_get_device_name; mal_proc pa_stream_get_device_name;
mal_proc pa_stream_set_write_callback; mal_proc pa_stream_set_write_callback;
mal_proc pa_stream_set_read_callback; mal_proc pa_stream_set_read_callback;
@ -4864,7 +4870,7 @@ mal_bool32 mal_device__get_current_frame__null(mal_device* pDevice, mal_uint32*
mal_assert(pCurrentPos != NULL); mal_assert(pCurrentPos != NULL);
*pCurrentPos = 0; *pCurrentPos = 0;
mal_uint64 currentFrameAbs = (mal_uint64)(mal_timer_get_time_in_seconds(&pDevice->null_device.timer) * pDevice->sampleRate) / pDevice->channels; mal_uint64 currentFrameAbs = (mal_uint64)(mal_timer_get_time_in_seconds(&pDevice->null_device.timer) * pDevice->sampleRate);
*pCurrentPos = (mal_uint32)(currentFrameAbs % pDevice->bufferSizeInFrames); *pCurrentPos = (mal_uint32)(currentFrameAbs % pDevice->bufferSizeInFrames);
return MAL_TRUE; return MAL_TRUE;
@ -4917,11 +4923,11 @@ mal_uint32 mal_device__wait_for_frames__null(mal_device* pDevice)
while (!pDevice->null_device.breakFromMainLoop) { while (!pDevice->null_device.breakFromMainLoop) {
mal_uint32 framesAvailable = mal_device__get_available_frames__null(pDevice); mal_uint32 framesAvailable = mal_device__get_available_frames__null(pDevice);
if (framesAvailable > 0) { if (framesAvailable >= (pDevice->bufferSizeInFrames/pDevice->periods)) {
return framesAvailable; return framesAvailable;
} }
mal_sleep(16); mal_sleep(pDevice->bufferSizeInMilliseconds/pDevice->periods);
} }
// We'll get here if the loop was terminated. Just return whatever's available. // We'll get here if the loop was terminated. Just return whatever's available.
@ -11784,6 +11790,7 @@ typedef mal_pa_stream_state_t (* mal_pa_stream_get_state_proc)
typedef const mal_pa_sample_spec* (* mal_pa_stream_get_sample_spec_proc) (mal_pa_stream* s); typedef const mal_pa_sample_spec* (* mal_pa_stream_get_sample_spec_proc) (mal_pa_stream* s);
typedef const mal_pa_channel_map* (* mal_pa_stream_get_channel_map_proc) (mal_pa_stream* s); typedef const mal_pa_channel_map* (* mal_pa_stream_get_channel_map_proc) (mal_pa_stream* s);
typedef const mal_pa_buffer_attr* (* mal_pa_stream_get_buffer_attr_proc) (mal_pa_stream* s); typedef const mal_pa_buffer_attr* (* mal_pa_stream_get_buffer_attr_proc) (mal_pa_stream* s);
typedef mal_pa_operation* (* mal_pa_stream_set_buffer_attr_proc) (mal_pa_stream* s, const mal_pa_buffer_attr* attr, mal_pa_stream_success_cb_t cb, void* userdata);
typedef const char* (* mal_pa_stream_get_device_name_proc) (mal_pa_stream* s); typedef const char* (* mal_pa_stream_get_device_name_proc) (mal_pa_stream* s);
typedef void (* mal_pa_stream_set_write_callback_proc) (mal_pa_stream* s, mal_pa_stream_request_cb_t cb, void* userdata); typedef void (* mal_pa_stream_set_write_callback_proc) (mal_pa_stream* s, mal_pa_stream_request_cb_t cb, void* userdata);
typedef void (* mal_pa_stream_set_read_callback_proc) (mal_pa_stream* s, mal_pa_stream_request_cb_t cb, void* userdata); typedef void (* mal_pa_stream_set_read_callback_proc) (mal_pa_stream* s, mal_pa_stream_request_cb_t cb, void* userdata);
@ -12305,6 +12312,10 @@ void mal_pulse_device_write_callback(mal_pa_stream* pStream, size_t sizeInBytes,
mal_context* pContext = pDevice->pContext; mal_context* pContext = pDevice->pContext;
mal_assert(pContext != NULL); mal_assert(pContext != NULL);
#ifdef MAL_DEBUG_OUTPUT
printf("[PulseAudio] write_callback: sizeInBytes=%d\n", (int)sizeInBytes);
#endif
size_t bytesRemaining = sizeInBytes; size_t bytesRemaining = sizeInBytes;
while (bytesRemaining > 0) { while (bytesRemaining > 0) {
size_t bytesToReadFromClient = bytesRemaining; size_t bytesToReadFromClient = bytesRemaining;
@ -12319,19 +12330,35 @@ void mal_pulse_device_write_callback(mal_pa_stream* pStream, size_t sizeInBytes,
return; return;
} }
#ifdef MAL_DEBUG_OUTPUT
printf(" bytesToReadFromClient=%d", (int)bytesToReadFromClient);
#endif
if (pBuffer != NULL && bytesToReadFromClient > 0) { if (pBuffer != NULL && bytesToReadFromClient > 0) {
mal_uint32 framesToReadFromClient = (mal_uint32)bytesToReadFromClient / (pDevice->internalChannels*mal_get_bytes_per_sample(pDevice->internalFormat)); mal_uint32 framesToReadFromClient = (mal_uint32)bytesToReadFromClient / (pDevice->internalChannels*mal_get_bytes_per_sample(pDevice->internalFormat));
if (framesToReadFromClient > 0) { if (framesToReadFromClient > 0) {
mal_device__read_frames_from_client(pDevice, framesToReadFromClient, pBuffer); mal_device__read_frames_from_client(pDevice, framesToReadFromClient, pBuffer);
#ifdef MAL_DEBUG_OUTPUT
printf(", framesToReadFromClient=%d\n", (int)framesToReadFromClient);
#endif
error = ((mal_pa_stream_write_proc)pContext->pulse.pa_stream_write)((mal_pa_stream*)pDevice->pulse.pStream, pBuffer, bytesToReadFromClient, NULL, 0, MAL_PA_SEEK_RELATIVE); error = ((mal_pa_stream_write_proc)pContext->pulse.pa_stream_write)((mal_pa_stream*)pDevice->pulse.pStream, pBuffer, bytesToReadFromClient, NULL, 0, MAL_PA_SEEK_RELATIVE);
if (error < 0) { if (error < 0) {
mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[PulseAudio] Failed to write data to the PulseAudio stream.", mal_result_from_pulse(error)); mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[PulseAudio] Failed to write data to the PulseAudio stream.", mal_result_from_pulse(error));
return; return;
} }
} else {
#ifdef MAL_DEBUG_OUTPUT
printf(", framesToReadFromClient=0\n");
#endif
} }
bytesRemaining -= bytesToReadFromClient; bytesRemaining -= bytesToReadFromClient;
} else {
#ifdef MAL_DEBUG_OUTPUT
printf(", framesToReadFromClient=0\n");
#endif
} }
} }
} }
@ -12460,6 +12487,7 @@ mal_result mal_device_init__pulse(mal_context* pContext, mal_device_type type, c
mal_pa_sample_spec ss; mal_pa_sample_spec ss;
mal_pa_channel_map cmap; mal_pa_channel_map cmap;
mal_pa_buffer_attr attr; mal_pa_buffer_attr attr;
mal_pa_stream_flags_t streamFlags;
const mal_pa_sample_spec* pActualSS = NULL; const mal_pa_sample_spec* pActualSS = NULL;
const mal_pa_channel_map* pActualCMap = NULL; const mal_pa_channel_map* pActualCMap = NULL;
@ -12533,53 +12561,6 @@ mal_result mal_device_init__pulse(mal_context* pContext, mal_device_type type, c
((mal_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); ((mal_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
} }
#if 0
mal_pa_sample_spec deviceSS;
mal_pa_channel_map deviceCMap;
if (type == mal_device_type_playback) {
deviceSS = sinkInfo.sample_spec;
deviceCMap = sinkInfo.channel_map;
} else {
deviceSS = sourceInfo.sample_spec;
deviceCMap = sourceInfo.channel_map;
}
if (pDevice->usingDefaultFormat) {
ss.format = deviceSS.format;
} else {
ss.format = mal_format_to_pulse(pConfig->format);
}
if (ss.format == MAL_PA_SAMPLE_INVALID) {
ss.format = MAL_PA_SAMPLE_S16LE;
}
if (pDevice->usingDefaultChannels) {
ss.channels = deviceSS.channels;
} else {
ss.channels = pConfig->channels;
}
if (pDevice->usingDefaultSampleRate) {
ss.rate = deviceSS.rate;
} else {
ss.rate = pConfig->sampleRate;
}
if (pDevice->usingDefaultChannelMap) {
cmap = deviceCMap;
} else {
cmap.channels = pConfig->channels;
for (mal_uint32 iChannel = 0; iChannel < pConfig->channels; ++iChannel) {
cmap.map[iChannel] = mal_channel_position_to_pulse(pConfig->channelMap[iChannel]);
}
if (((mal_pa_channel_map_valid_proc)pContext->pulse.pa_channel_map_valid)(&cmap) == 0 || ((mal_pa_channel_map_compatible_proc)pContext->pulse.pa_channel_map_compatible)(&cmap, &ss) == 0) {
((mal_pa_channel_map_init_extend_proc)pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, MAL_PA_CHANNEL_MAP_DEFAULT); // The channel map is invalid, so just fall back to the default.
}
}
#else
if (type == mal_device_type_playback) { if (type == mal_device_type_playback) {
ss = sinkInfo.sample_spec; ss = sinkInfo.sample_spec;
cmap = sinkInfo.channel_map; cmap = sinkInfo.channel_map;
@ -12587,7 +12568,7 @@ mal_result mal_device_init__pulse(mal_context* pContext, mal_device_type type, c
ss = sourceInfo.sample_spec; ss = sourceInfo.sample_spec;
cmap = sourceInfo.channel_map; cmap = sourceInfo.channel_map;
} }
#endif
// Buffer size. // Buffer size.
bufferSizeInFrames = pDevice->bufferSizeInFrames; bufferSizeInFrames = pDevice->bufferSizeInFrames;
@ -12606,10 +12587,14 @@ mal_result mal_device_init__pulse(mal_context* pContext, mal_device_type type, c
} }
attr.maxlength = bufferSizeInFrames * mal_get_bytes_per_sample(mal_format_from_pulse(ss.format))*ss.channels; attr.maxlength = bufferSizeInFrames * mal_get_bytes_per_sample(mal_format_from_pulse(ss.format))*ss.channels;
attr.tlength = attr.maxlength / pConfig->periods; attr.tlength = attr.maxlength;
attr.prebuf = (mal_uint32)-1; attr.prebuf = (mal_uint32)-1;
attr.minreq = attr.tlength; attr.minreq = attr.maxlength / pConfig->periods;
attr.fragsize = attr.tlength; attr.fragsize = attr.maxlength / pConfig->periods;
#ifdef MAL_DEBUG_OUTPUT
printf("[PulseAudio] attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; bufferSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, bufferSizeInFrames);
#endif
char streamName[256]; char streamName[256];
if (pConfig->pulse.pStreamName != NULL) { if (pConfig->pulse.pStreamName != NULL) {
@ -12628,11 +12613,15 @@ mal_result mal_device_init__pulse(mal_context* pContext, mal_device_type type, c
} }
streamFlags = MAL_PA_STREAM_START_CORKED;
if (dev != NULL) {
streamFlags |= MAL_PA_STREAM_DONT_MOVE | MAL_PA_STREAM_FIX_FORMAT | MAL_PA_STREAM_FIX_RATE | MAL_PA_STREAM_FIX_CHANNELS;
}
if (type == mal_device_type_playback) { if (type == mal_device_type_playback) {
error = ((mal_pa_stream_connect_playback_proc)pContext->pulse.pa_stream_connect_playback)((mal_pa_stream*)pDevice->pulse.pStream, dev, &attr, MAL_PA_STREAM_START_CORKED, NULL, NULL); error = ((mal_pa_stream_connect_playback_proc)pContext->pulse.pa_stream_connect_playback)((mal_pa_stream*)pDevice->pulse.pStream, dev, &attr, streamFlags, NULL, NULL);
} else { } else {
error = ((mal_pa_stream_connect_record_proc)pContext->pulse.pa_stream_connect_record)((mal_pa_stream*)pDevice->pulse.pStream, dev, &attr, MAL_PA_STREAM_START_CORKED); error = ((mal_pa_stream_connect_record_proc)pContext->pulse.pa_stream_connect_record)((mal_pa_stream*)pDevice->pulse.pStream, dev, &attr, streamFlags);
} }
if (error != MAL_PA_OK) { if (error != MAL_PA_OK) {
@ -12652,6 +12641,21 @@ mal_result mal_device_init__pulse(mal_context* pContext, mal_device_type type, c
// Internal format. // Internal format.
pActualSS = ((mal_pa_stream_get_sample_spec_proc)pContext->pulse.pa_stream_get_sample_spec)((mal_pa_stream*)pDevice->pulse.pStream); pActualSS = ((mal_pa_stream_get_sample_spec_proc)pContext->pulse.pa_stream_get_sample_spec)((mal_pa_stream*)pDevice->pulse.pStream);
if (pActualSS != NULL) { if (pActualSS != NULL) {
// If anything has changed between the requested and the actual sample spec, we need to update the buffer.
if (ss.format != pActualSS->format || ss.channels != pActualSS->channels || ss.rate != pActualSS->rate) {
attr.maxlength = bufferSizeInFrames * mal_get_bytes_per_sample(mal_format_from_pulse(pActualSS->format))*pActualSS->channels;
attr.tlength = attr.maxlength;
attr.prebuf = (mal_uint32)-1;
attr.minreq = attr.maxlength / pConfig->periods;
attr.fragsize = attr.maxlength / pConfig->periods;
pOP = ((mal_pa_stream_set_buffer_attr_proc)pContext->pulse.pa_stream_set_buffer_attr)((mal_pa_stream*)pDevice->pulse.pStream, &attr, NULL, NULL);
if (pOP != NULL) {
mal_device__wait_for_operation__pulse(pDevice, pOP);
((mal_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
}
}
ss = *pActualSS; ss = *pActualSS;
} }
@ -12680,6 +12684,10 @@ mal_result mal_device_init__pulse(mal_context* pContext, mal_device_type type, c
pDevice->bufferSizeInFrames = attr.maxlength / (mal_get_bytes_per_sample(pDevice->internalFormat)*pDevice->internalChannels); pDevice->bufferSizeInFrames = attr.maxlength / (mal_get_bytes_per_sample(pDevice->internalFormat)*pDevice->internalChannels);
pDevice->periods = attr.maxlength / attr.tlength; pDevice->periods = attr.maxlength / attr.tlength;
#ifdef MAL_DEBUG_OUTPUT
printf("[PulseAudio] actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; pDevice->bufferSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDevice->bufferSizeInFrames);
#endif
// Grab the name of the device if we can. // Grab the name of the device if we can.
dev = ((mal_pa_stream_get_device_name_proc)pContext->pulse.pa_stream_get_device_name)((mal_pa_stream*)pDevice->pulse.pStream); dev = ((mal_pa_stream_get_device_name_proc)pContext->pulse.pa_stream_get_device_name)((mal_pa_stream*)pDevice->pulse.pStream);
@ -12930,6 +12938,7 @@ mal_result mal_context_init__pulse(mal_context* pContext)
pContext->pulse.pa_stream_get_sample_spec = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_get_sample_spec"); pContext->pulse.pa_stream_get_sample_spec = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_get_sample_spec");
pContext->pulse.pa_stream_get_channel_map = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_get_channel_map"); pContext->pulse.pa_stream_get_channel_map = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_get_channel_map");
pContext->pulse.pa_stream_get_buffer_attr = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_get_buffer_attr"); pContext->pulse.pa_stream_get_buffer_attr = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_get_buffer_attr");
pContext->pulse.pa_stream_set_buffer_attr = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_set_buffer_attr");
pContext->pulse.pa_stream_get_device_name = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_get_device_name"); pContext->pulse.pa_stream_get_device_name = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_get_device_name");
pContext->pulse.pa_stream_set_write_callback = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_set_write_callback"); pContext->pulse.pa_stream_set_write_callback = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_set_write_callback");
pContext->pulse.pa_stream_set_read_callback = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_set_read_callback"); pContext->pulse.pa_stream_set_read_callback = (mal_proc)mal_dlsym(pContext->pulse.pulseSO, "pa_stream_set_read_callback");
@ -12972,6 +12981,7 @@ mal_result mal_context_init__pulse(mal_context* pContext)
mal_pa_stream_get_sample_spec_proc _pa_stream_get_sample_spec = pa_stream_get_sample_spec; mal_pa_stream_get_sample_spec_proc _pa_stream_get_sample_spec = pa_stream_get_sample_spec;
mal_pa_stream_get_channel_map_proc _pa_stream_get_channel_map = pa_stream_get_channel_map; mal_pa_stream_get_channel_map_proc _pa_stream_get_channel_map = pa_stream_get_channel_map;
mal_pa_stream_get_buffer_attr_proc _pa_stream_get_buffer_attr = pa_stream_get_buffer_attr; mal_pa_stream_get_buffer_attr_proc _pa_stream_get_buffer_attr = pa_stream_get_buffer_attr;
mal_pa_stream_set_buffer_attr_proc _pa_stream_set_buffer_attr = pa_stream_set_buffer_attr;
mal_pa_stream_get_device_name_proc _pa_stream_get_device_name = pa_stream_get_device_name; mal_pa_stream_get_device_name_proc _pa_stream_get_device_name = pa_stream_get_device_name;
mal_pa_stream_set_write_callback_proc _pa_stream_set_write_callback = pa_stream_set_write_callback; mal_pa_stream_set_write_callback_proc _pa_stream_set_write_callback = pa_stream_set_write_callback;
mal_pa_stream_set_read_callback_proc _pa_stream_set_read_callback = pa_stream_set_read_callback; mal_pa_stream_set_read_callback_proc _pa_stream_set_read_callback = pa_stream_set_read_callback;
@ -13013,6 +13023,7 @@ mal_result mal_context_init__pulse(mal_context* pContext)
pContext->pulse.pa_stream_get_sample_spec = (mal_proc)_pa_stream_get_sample_spec; pContext->pulse.pa_stream_get_sample_spec = (mal_proc)_pa_stream_get_sample_spec;
pContext->pulse.pa_stream_get_channel_map = (mal_proc)_pa_stream_get_channel_map; pContext->pulse.pa_stream_get_channel_map = (mal_proc)_pa_stream_get_channel_map;
pContext->pulse.pa_stream_get_buffer_attr = (mal_proc)_pa_stream_get_buffer_attr; pContext->pulse.pa_stream_get_buffer_attr = (mal_proc)_pa_stream_get_buffer_attr;
pContext->pulse.pa_stream_set_buffer_attr = (mal_proc)_pa_stream_set_buffer_attr;
pContext->pulse.pa_stream_get_device_name = (mal_proc)_pa_stream_get_device_name; pContext->pulse.pa_stream_get_device_name = (mal_proc)_pa_stream_get_device_name;
pContext->pulse.pa_stream_set_write_callback = (mal_proc)_pa_stream_set_write_callback; pContext->pulse.pa_stream_set_write_callback = (mal_proc)_pa_stream_set_write_callback;
pContext->pulse.pa_stream_set_read_callback = (mal_proc)_pa_stream_set_read_callback; pContext->pulse.pa_stream_set_read_callback = (mal_proc)_pa_stream_set_read_callback;
@ -14901,26 +14912,61 @@ OSStatus mal_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFlags* p
printf("INFO: Output Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", busNumber, frameCount, pBufferList->mNumberBuffers); printf("INFO: Output Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", busNumber, frameCount, pBufferList->mNumberBuffers);
#endif #endif
// For now we can assume everything is interleaved. // We need to check whether or not we are outputting interleaved or non-interleaved samples. The
for (UInt32 iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) { // way we do this is slightly different for each type.
if (pBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->internalChannels) { mal_stream_layout layout = mal_stream_layout_interleaved;
mal_uint32 frameCountForThisBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels); if (pBufferList->mBuffers[0].mNumberChannels != pDevice->internalChannels) {
if (frameCountForThisBuffer > 0) { layout = mal_stream_layout_deinterleaved;
mal_device__read_frames_from_client(pDevice, frameCountForThisBuffer, pBufferList->mBuffers[iBuffer].mData); }
if (layout == mal_stream_layout_interleaved) {
// For now we can assume everything is interleaved.
for (UInt32 iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) {
if (pBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->internalChannels) {
mal_uint32 frameCountForThisBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels);
if (frameCountForThisBuffer > 0) {
mal_device__read_frames_from_client(pDevice, frameCountForThisBuffer, pBufferList->mBuffers[iBuffer].mData);
}
#if defined(MAL_DEBUG_OUTPUT)
printf(" frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pBufferList->mBuffers[iBuffer].mNumberChannels, pBufferList->mBuffers[iBuffer].mDataByteSize);
#endif
} else {
// This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's
// not interleaved, in which case we can't handle right now since mini_al does not yet support non-interleaved streams. We just
// output silence here.
mal_zero_memory(pBufferList->mBuffers[iBuffer].mData, pBufferList->mBuffers[iBuffer].mDataByteSize);
#if defined(MAL_DEBUG_OUTPUT)
printf(" WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pBufferList->mBuffers[iBuffer].mNumberChannels, pBufferList->mBuffers[iBuffer].mDataByteSize);
#endif
} }
}
} else {
// This is the deinterleaved case. We need to update each buffer in groups of internalChannels. This
// assumes each buffer is the same size.
mal_uint8 tempBuffer[4096];
for (UInt32 iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; iBuffer += pDevice->internalChannels) {
mal_uint32 frameCountPerBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels);
#if defined(MAL_DEBUG_OUTPUT) mal_uint32 framesRemaining = frameCountPerBuffer;
printf(" frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pBufferList->mBuffers[iBuffer].mNumberChannels, pBufferList->mBuffers[iBuffer].mDataByteSize); while (framesRemaining > 0) {
#endif mal_uint32 framesToRead = sizeof(tempBuffer) / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels);
} else { if (framesToRead > framesRemaining) {
// This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's framesToRead = framesRemaining;
// not interleaved, in which case we can't handle right now since mini_al does not yet support non-interleaved streams. We just }
// output silence here.
mal_zero_memory(pBufferList->mBuffers[iBuffer].mData, pBufferList->mBuffers[iBuffer].mDataByteSize);
#if defined(MAL_DEBUG_OUTPUT) mal_device__read_frames_from_client(pDevice, framesToRead, tempBuffer);
printf(" WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pBufferList->mBuffers[iBuffer].mNumberChannels, pBufferList->mBuffers[iBuffer].mDataByteSize);
#endif void* ppDeinterleavedBuffers[MAL_MAX_CHANNELS];
for (mal_uint32 iChannel = 0; iChannel < pDevice->internalChannels; ++iChannel) {
ppDeinterleavedBuffers[iChannel] = (void*)mal_offset_ptr(pBufferList->mBuffers[iBuffer].mData, (frameCountPerBuffer - framesRemaining) * mal_get_bytes_per_sample(pDevice->internalFormat));
}
mal_deinterleave_pcm_frames(pDevice->internalFormat, pDevice->internalChannels, framesToRead, tempBuffer, ppDeinterleavedBuffers);
framesRemaining -= framesToRead;
}
} }
} }
@ -14941,6 +14987,13 @@ OSStatus mal_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pA
AudioBufferList* pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList; AudioBufferList* pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList;
mal_assert(pRenderedBufferList); mal_assert(pRenderedBufferList);
// We need to check whether or not we are outputting interleaved or non-interleaved samples. The
// way we do this is slightly different for each type.
mal_stream_layout layout = mal_stream_layout_interleaved;
if (pRenderedBufferList->mBuffers[0].mNumberChannels != pDevice->internalChannels) {
layout = mal_stream_layout_deinterleaved;
}
#if defined(MAL_DEBUG_OUTPUT) #if defined(MAL_DEBUG_OUTPUT)
printf("INFO: Input Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", busNumber, frameCount, pRenderedBufferList->mNumberBuffers); printf("INFO: Input Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", busNumber, frameCount, pRenderedBufferList->mNumberBuffers);
#endif #endif
@ -14953,16 +15006,58 @@ OSStatus mal_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pA
return status; return status;
} }
// For now we can assume everything is interleaved. if (layout == mal_stream_layout_interleaved) {
for (UInt32 iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) { for (UInt32 iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) {
if (pRenderedBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->internalChannels) { if (pRenderedBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->internalChannels) {
mal_device__send_frames_to_client(pDevice, frameCount, pRenderedBufferList->mBuffers[iBuffer].mData); mal_device__send_frames_to_client(pDevice, frameCount, pRenderedBufferList->mBuffers[iBuffer].mData);
#if defined(MAL_DEBUG_OUTPUT) #if defined(MAL_DEBUG_OUTPUT)
printf(" mDataByteSize=%d\n", pRenderedBufferList->mBuffers[iBuffer].mDataByteSize); printf(" mDataByteSize=%d\n", pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);
#endif #endif
} else { } else {
// This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's // This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's
// not interleaved, in which case we can't handle right now since mini_al does not yet support non-interleaved streams. // not interleaved, in which case we can't handle right now since mini_al does not yet support non-interleaved streams.
mal_uint8 silentBuffer[4096];
mal_zero_memory(silentBuffer, sizeof(silentBuffer));
mal_uint32 framesRemaining = frameCount;
while (framesRemaining > 0) {
mal_uint32 framesToSend = sizeof(silentBuffer) / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels);
if (framesToSend > framesRemaining) {
framesToSend = framesRemaining;
}
mal_device__send_frames_to_client(pDevice, framesToSend, silentBuffer);
framesRemaining -= framesToSend;
}
#if defined(MAL_DEBUG_OUTPUT)
printf(" WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pRenderBufferList->mBuffers[iBuffer].mNumberChannels, pRenderBufferList->mBuffers[iBuffer].mDataByteSize);
#endif
}
}
} else {
// This is the deinterleaved case. We need to interleave the audio data before sending it to the client. This
// assumes each buffer is the same size.
mal_uint8 tempBuffer[4096];
for (UInt32 iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; iBuffer += pDevice->internalChannels) {
mal_uint32 framesRemaining = frameCount;
while (framesRemaining > 0) {
mal_uint32 framesToSend = sizeof(tempBuffer) / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels);
if (framesToSend > framesRemaining) {
framesToSend = framesRemaining;
}
void* ppDeinterleavedBuffers[MAL_MAX_CHANNELS];
for (mal_uint32 iChannel = 0; iChannel < pDevice->internalChannels; ++iChannel) {
ppDeinterleavedBuffers[iChannel] = (void*)mal_offset_ptr(pRenderedBufferList->mBuffers[iBuffer].mData, (frameCount - framesRemaining) * mal_get_bytes_per_sample(pDevice->internalFormat));
}
mal_interleave_pcm_frames(pDevice->internalFormat, pDevice->internalChannels, framesToSend, (const void**)ppDeinterleavedBuffers, tempBuffer);
mal_device__send_frames_to_client(pDevice, framesToSend, tempBuffer);
framesRemaining -= framesToSend;
}
} }
} }
@ -14976,42 +15071,53 @@ void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, AudioUnitPro
mal_device* pDevice = (mal_device*)pUserData; mal_device* pDevice = (mal_device*)pUserData;
mal_assert(pDevice != NULL); mal_assert(pDevice != NULL);
UInt32 isRunning; // There's been a report of a deadlock here when triggered by mal_device_uninit(). It looks like
UInt32 isRunningSize = sizeof(isRunning); // AudioUnitGetProprty (called below) and AudioComponentInstanceDispose (called in mal_device_uninit)
OSStatus status = ((mal_AudioUnitGetProperty_proc)pDevice->pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioOutputUnitProperty_IsRunning, scope, element, &isRunning, &isRunningSize); // can try waiting on the same lock. I'm going to try working around this by not calling any Core
if (status != noErr) { // Audio APIs in the callback when the device has been stopped or uninitialized.
return; // Don't really know what to do in this case... just ignore it, I suppose... if (mal_device__get_state(pDevice) == MAL_STATE_UNINITIALIZED || mal_device__get_state(pDevice) == MAL_STATE_STOPPING) {
}
if (!isRunning) {
// The stop event is a bit annoying in Core Audio because it will be called when we automatically switch the default device. Some scenarios to consider:
//
// 1) When the device is unplugged, this will be called _before_ the default device change notification.
// 2) When the device is changed via the default device change notification, this will be called _after_ the switch.
//
// For case #1, we just check if there's a new default device available. If so, we just ignore the stop event. For case #2 we check a flag.
if (pDevice->isDefaultDevice && mal_device__get_state(pDevice) != MAL_STATE_STOPPING && mal_device__get_state(pDevice) != MAL_STATE_STOPPED) {
// It looks like the device is switching through an external event, such as the user unplugging the device or changing the default device
// via the operating system's sound settings. If we're re-initializing the device, we just terminate because we want the stopping of the
// device to be seamless to the client (we don't want them receiving the onStop event and thinking that the device has stopped when it
// hasn't!).
if (pDevice->coreaudio.isSwitchingDevice) {
return;
}
// Getting here means the device is not reinitializing which means it may have been unplugged. From what I can see, it looks like Core Audio
// will try switching to the new default device seamlessly. We need to somehow find a way to determine whether or not Core Audio will most
// likely be successful in switching to the new device.
//
// TODO: Try to predict if Core Audio will switch devices. If not, the onStop callback needs to be posted.
return;
}
// Getting here means we need to stop the device.
mal_stop_proc onStop = pDevice->onStop; mal_stop_proc onStop = pDevice->onStop;
if (onStop) { if (onStop) {
onStop(pDevice); onStop(pDevice);
} }
} else {
UInt32 isRunning;
UInt32 isRunningSize = sizeof(isRunning);
OSStatus status = ((mal_AudioUnitGetProperty_proc)pDevice->pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioOutputUnitProperty_IsRunning, scope, element, &isRunning, &isRunningSize);
if (status != noErr) {
return; // Don't really know what to do in this case... just ignore it, I suppose...
}
if (!isRunning) {
// The stop event is a bit annoying in Core Audio because it will be called when we automatically switch the default device. Some scenarios to consider:
//
// 1) When the device is unplugged, this will be called _before_ the default device change notification.
// 2) When the device is changed via the default device change notification, this will be called _after_ the switch.
//
// For case #1, we just check if there's a new default device available. If so, we just ignore the stop event. For case #2 we check a flag.
if (pDevice->isDefaultDevice && mal_device__get_state(pDevice) != MAL_STATE_STOPPING && mal_device__get_state(pDevice) != MAL_STATE_STOPPED) {
// It looks like the device is switching through an external event, such as the user unplugging the device or changing the default device
// via the operating system's sound settings. If we're re-initializing the device, we just terminate because we want the stopping of the
// device to be seamless to the client (we don't want them receiving the onStop event and thinking that the device has stopped when it
// hasn't!).
if (pDevice->coreaudio.isSwitchingDevice) {
return;
}
// Getting here means the device is not reinitializing which means it may have been unplugged. From what I can see, it looks like Core Audio
// will try switching to the new default device seamlessly. We need to somehow find a way to determine whether or not Core Audio will most
// likely be successful in switching to the new device.
//
// TODO: Try to predict if Core Audio will switch devices. If not, the onStop callback needs to be posted.
return;
}
// Getting here means we need to stop the device.
mal_stop_proc onStop = pDevice->onStop;
if (onStop) {
onStop(pDevice);
}
}
} }
} }
@ -15288,11 +15394,14 @@ mal_result mal_device_init_internal__coreaudio(mal_context* pContext, mal_device
if (result != MAL_SUCCESS) { if (result != MAL_SUCCESS) {
return result; return result;
} }
#else
actualBufferSizeInFrames = 4096;
#endif
pData->bufferSizeInFramesOut = actualBufferSizeInFrames * pData->periodsOut; pData->bufferSizeInFramesOut = actualBufferSizeInFrames * pData->periodsOut;
#else
actualBufferSizeInFrames = 4096;
pData->bufferSizeInFramesOut = actualBufferSizeInFrames;
#endif
// During testing I discovered that the buffer size can be too big. You'll get an error like this: // During testing I discovered that the buffer size can be too big. You'll get an error like this:
// //
@ -15557,6 +15666,15 @@ mal_result mal_context_init__coreaudio(mal_context* pContext)
{ {
mal_assert(pContext != NULL); mal_assert(pContext != NULL);
#if defined(MAL_APPLE_MOBILE)
@autoreleasepool {
AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
mal_assert(pAudioSession != NULL);
[pAudioSession setCategory: AVAudioSessionCategoryPlayAndRecord error:nil];
}
#endif
#if !defined(MAL_NO_RUNTIME_LINKING) && !defined(MAL_APPLE_MOBILE) #if !defined(MAL_NO_RUNTIME_LINKING) && !defined(MAL_APPLE_MOBILE)
pContext->coreaudio.hCoreFoundation = mal_dlopen("CoreFoundation.framework/CoreFoundation"); pContext->coreaudio.hCoreFoundation = mal_dlopen("CoreFoundation.framework/CoreFoundation");
if (pContext->coreaudio.hCoreFoundation == NULL) { if (pContext->coreaudio.hCoreFoundation == NULL) {
@ -19725,7 +19843,7 @@ mal_result mal_device_init__sdl(mal_context* pContext, mal_device_type type, con
// SDL wants the buffer size to be a power of 2. The SDL_AudioSpec property for this is only a Uint16, so we need // 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. // to explicitly clamp this because it will be easy to overflow.
mal_uint32 bufferSize = pConfig->bufferSizeInFrames; mal_uint32 bufferSize = pDevice->bufferSizeInFrames;
if (bufferSize > 32768) { if (bufferSize > 32768) {
bufferSize = 32768; bufferSize = 32768;
} else { } else {
@ -19760,7 +19878,7 @@ mal_result mal_device_init__sdl(mal_context* pContext, mal_device_type type, con
pDevice->sdl.deviceID = ((MAL_PFN_SDL_OpenAudioDevice)pDevice->pContext->sdl.SDL_OpenAudioDevice)(pDeviceName, isCapture, &desiredSpec, &obtainedSpec, MAL_SDL_AUDIO_ALLOW_ANY_CHANGE); pDevice->sdl.deviceID = ((MAL_PFN_SDL_OpenAudioDevice)pDevice->pContext->sdl.SDL_OpenAudioDevice)(pDeviceName, isCapture, &desiredSpec, &obtainedSpec, MAL_SDL_AUDIO_ALLOW_ANY_CHANGE);
if (pDevice->sdl.deviceID == 0) { if (pDevice->sdl.deviceID == 0) {
return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "Failed to open SDL device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "Failed to open SDL2 device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
} }
} else } else
#endif #endif
@ -19778,10 +19896,12 @@ mal_result mal_device_init__sdl(mal_context* pContext, mal_device_type type, con
desiredSpec.format = MAL_AUDIO_S16; desiredSpec.format = MAL_AUDIO_S16;
} }
pDevice->sdl.deviceID = ((MAL_PFN_SDL_OpenAudio)pDevice->pContext->sdl.SDL_OpenAudio)(&desiredSpec, &obtainedSpec); int deviceID = ((MAL_PFN_SDL_OpenAudio)pDevice->pContext->sdl.SDL_OpenAudio)(&desiredSpec, &obtainedSpec);
if (pDevice->sdl.deviceID != 0) { if (deviceID < 0) {
return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "Failed to open SDL device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE); return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "Failed to open SDL1 device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
} }
pDevice->sdl.deviceID = (mal_uint32)deviceID;
} }
pDevice->internalFormat = mal_format_from_sdl(obtainedSpec.format); pDevice->internalFormat = mal_format_from_sdl(obtainedSpec.format);
@ -20429,6 +20549,14 @@ mal_result mal_context_init(const mal_backend backends[], mal_uint32 backendCoun
mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_WARNING, "Failed to initialize mutex for device info retrieval. mal_context_get_device_info() is not thread safe.", MAL_FAILED_TO_CREATE_MUTEX); mal_context_post_error(pContext, NULL, MAL_LOG_LEVEL_WARNING, "Failed to initialize mutex for device info retrieval. mal_context_get_device_info() is not thread safe.", MAL_FAILED_TO_CREATE_MUTEX);
} }
#ifdef MAL_DEBUG_OUTPUT
printf("[mini_al] Endian: %s\n", mal_is_little_endian() ? "LE" : "BE");
printf("[mini_al] SSE2: %s\n", mal_has_sse2() ? "YES" : "NO");
printf("[mini_al] AVX2: %s\n", mal_has_avx2() ? "YES" : "NO");
printf("[mini_al] AVX512F: %s\n", mal_has_avx512f() ? "YES" : "NO");
printf("[mini_al] NEON: %s\n", mal_has_neon() ? "YES" : "NO");
#endif
pContext->backend = backend; pContext->backend = backend;
return result; return result;
} }
@ -20767,7 +20895,7 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi
#ifdef MAL_DEBUG_OUTPUT #ifdef MAL_DEBUG_OUTPUT
printf("[WASAPI] %s (%s)\n", pDevice->name, (pDevice->type == mal_device_type_playback) ? "Playback" : "Capture"); printf("[%s] %s (%s)\n", mal_get_backend_name(pDevice->pContext->backend), pDevice->name, (pDevice->type == mal_device_type_playback) ? "Playback" : "Capture");
printf(" Format: %s -> %s\n", mal_get_format_name(pDevice->format), mal_get_format_name(pDevice->internalFormat)); printf(" Format: %s -> %s\n", mal_get_format_name(pDevice->format), mal_get_format_name(pDevice->internalFormat));
printf(" Channels: %d -> %d\n", pDevice->channels, pDevice->internalChannels); printf(" Channels: %d -> %d\n", pDevice->channels, pDevice->internalChannels);
printf(" Sample Rate: %d -> %d\n", pDevice->sampleRate, pDevice->internalSampleRate); printf(" Sample Rate: %d -> %d\n", pDevice->sampleRate, pDevice->internalSampleRate);
@ -20949,7 +21077,7 @@ mal_result mal_device_stop(mal_device* pDevice)
// Asynchronous backends need to be handled differently. // Asynchronous backends need to be handled differently.
if (mal_context_is_backend_asynchronous(pDevice->pContext)) { if (mal_context_is_backend_asynchronous(pDevice->pContext)) {
pDevice->pContext->onDeviceStop(pDevice); result = pDevice->pContext->onDeviceStop(pDevice);
} else { } else {
// Synchronous backends. // Synchronous backends.
@ -26138,6 +26266,92 @@ void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_form
} }
} }
void mal_deinterleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint32 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames)
{
if (pInterleavedPCMFrames == NULL || ppDeinterleavedPCMFrames == NULL) {
return; // Invalid args.
}
// For efficiency we do this per format.
switch (format) {
case mal_format_s16:
{
const mal_int16* pSrcS16 = (const mal_int16*)pInterleavedPCMFrames;
for (mal_uint32 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) {
mal_int16* pDstS16 = (mal_int16*)ppDeinterleavedPCMFrames[iChannel];
pDstS16[iPCMFrame] = pSrcS16[iPCMFrame*channels+iChannel];
}
}
} break;
case mal_format_f32:
{
const float* pSrcF32 = (const float*)pInterleavedPCMFrames;
for (mal_uint32 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) {
float* pDstF32 = (float*)ppDeinterleavedPCMFrames[iChannel];
pDstF32[iPCMFrame] = pSrcF32[iPCMFrame*channels+iChannel];
}
}
} break;
default:
{
mal_uint32 sampleSizeInBytes = mal_get_bytes_per_sample(format);
for (mal_uint32 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) {
void* pDst = mal_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes);
const void* pSrc = mal_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes);
memcpy(pDst, pSrc, sampleSizeInBytes);
}
}
} break;
}
}
void mal_interleave_pcm_frames(mal_format format, mal_uint32 channels, mal_uint32 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames)
{
switch (format)
{
case mal_format_s16:
{
mal_int16* pDstS16 = (mal_int16*)pInterleavedPCMFrames;
for (mal_uint32 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) {
const mal_int16* pSrcS16 = (const mal_int16*)ppDeinterleavedPCMFrames[iChannel];
pDstS16[iPCMFrame*channels+iChannel] = pSrcS16[iPCMFrame];
}
}
} break;
case mal_format_f32:
{
float* pDstF32 = (float*)pInterleavedPCMFrames;
for (mal_uint32 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) {
const float* pSrcF32 = (const float*)ppDeinterleavedPCMFrames[iChannel];
pDstF32[iPCMFrame*channels+iChannel] = pSrcF32[iPCMFrame];
}
}
} break;
default:
{
mal_uint32 sampleSizeInBytes = mal_get_bytes_per_sample(format);
for (mal_uint32 iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
for (mal_uint32 iChannel = 0; iChannel < channels; ++iChannel) {
void* pDst = mal_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes);
const void* pSrc = mal_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes);
memcpy(pDst, pSrc, sampleSizeInBytes);
}
}
} break;
}
}
typedef struct typedef struct
@ -28296,6 +28510,13 @@ mal_uint64 mal_sine_wave_read_ex(mal_sine_wave* pSineWave, mal_uint64 frameCount
// REVISION HISTORY // REVISION HISTORY
// ================ // ================
// //
// v0.8.10 - 2018-10-21
// - Core Audio: Fix a hang when uninitializing a device.
// - Fix a bug where an incorrect value is returned from mal_device_stop().
//
// v0.8.9 - 2018-09-28
// - Fix a bug with the SDL backend where device initialization fails.
//
// v0.8.8 - 2018-09-14 // v0.8.8 - 2018-09-14
// - Fix Linux build with the ALSA backend. // - Fix Linux build with the ALSA backend.
// - Minor documentation fix. // - Minor documentation fix.

View file

@ -193,7 +193,7 @@
#undef __forceinline #undef __forceinline
#endif #endif
#define __forceinline #define __forceinline
//#define alloca __builtin_alloca #define alloca __builtin_alloca
#elif !defined(_MSC_VER) #elif !defined(_MSC_VER)
#if __GNUC__ #if __GNUC__
#define __forceinline inline #define __forceinline inline

View file

@ -1,4 +1,4 @@
// The implementation of mini_al needs to #include windows.h which means it needs to go into // The implementation of mini_al needs to #include windows.h which means it needs to go into
// it's own translation unit. Not doing this will cause conflicts with CloseWindow(), etc. // it's own translation unit. Not doing this will cause conflicts with CloseWindow(), etc.
#define MAL_IMPLEMENTATION #define MAL_IMPLEMENTATION
#include "mini_al.h" #include "external/mini_al.h"

View file

@ -223,7 +223,7 @@
#define MOUSE_MIDDLE_BUTTON 2 #define MOUSE_MIDDLE_BUTTON 2
// Touch points registered // Touch points registered
#define MAX_TOUCH_POINTS 2 #define MAX_TOUCH_POINTS 10
// Gamepad Number // Gamepad Number
#define GAMEPAD_PLAYER1 0 #define GAMEPAD_PLAYER1 0
@ -311,7 +311,7 @@
// NOTE: MSC C++ compiler does not support compound literals (C99 feature) // NOTE: MSC C++ compiler does not support compound literals (C99 feature)
// Plain structures in C++ (without constructors) can be initialized from { } initializers. // Plain structures in C++ (without constructors) can be initialized from { } initializers.
#ifdef __cplusplus #if defined(__cplusplus)
#define CLITERAL #define CLITERAL
#else #else
#define CLITERAL (Color) #define CLITERAL (Color)
@ -786,7 +786,7 @@ typedef enum {
// Callbacks to be implemented by users // Callbacks to be implemented by users
typedef void (*TraceLogCallback)(int msgType, const char *text, va_list args); typedef void (*TraceLogCallback)(int msgType, const char *text, va_list args);
#ifdef __cplusplus #if defined(__cplusplus)
extern "C" { // Prevents name mangling of functions extern "C" { // Prevents name mangling of functions
#endif #endif
@ -868,9 +868,11 @@ RLAPI void TakeScreenshot(const char *fileName); // Takes a scr
RLAPI int GetRandomValue(int min, int max); // Returns a random value between min and max (both included) RLAPI int GetRandomValue(int min, int max); // Returns a random value between min and max (both included)
// Files management functions // Files management functions
RLAPI bool FileExists(const char *fileName); // Check if file exists
RLAPI bool IsFileExtension(const char *fileName, const char *ext);// Check file extension RLAPI bool IsFileExtension(const char *fileName, const char *ext);// Check file extension
RLAPI const char *GetExtension(const char *fileName); // Get pointer to extension for a filename string RLAPI const char *GetExtension(const char *fileName); // Get pointer to extension for a filename string
RLAPI const char *GetFileName(const char *filePath); // Get pointer to filename for a path string RLAPI const char *GetFileName(const char *filePath); // Get pointer to filename for a path string
RLAPI const char *GetFileNameWithoutExt(const char *filePath); // Get filename string without extension (memory should be freed)
RLAPI const char *GetDirectoryPath(const char *fileName); // Get full path for a given fileName (uses static string) RLAPI const char *GetDirectoryPath(const char *fileName); // Get full path for a given fileName (uses static string)
RLAPI const char *GetWorkingDirectory(void); // Get current working directory (uses static string) RLAPI const char *GetWorkingDirectory(void); // Get current working directory (uses static string)
RLAPI char **GetDirectoryFiles(const char *dirPath, int *count); // Get filenames in a directory path (memory should be freed) RLAPI char **GetDirectoryFiles(const char *dirPath, int *count); // Get filenames in a directory path (memory should be freed)
@ -879,11 +881,14 @@ RLAPI bool ChangeDirectory(const char *dir); // Change work
RLAPI bool IsFileDropped(void); // Check if a file has been dropped into window RLAPI bool IsFileDropped(void); // Check if a file has been dropped into window
RLAPI char **GetDroppedFiles(int *count); // Get dropped files names (memory should be freed) RLAPI char **GetDroppedFiles(int *count); // Get dropped files names (memory should be freed)
RLAPI void ClearDroppedFiles(void); // Clear dropped files paths buffer (free memory) RLAPI void ClearDroppedFiles(void); // Clear dropped files paths buffer (free memory)
RLAPI long GetFileModTime(const char *fileName); // Get file modification time (last write time)
// Persistent storage management // Persistent storage management
RLAPI void StorageSaveValue(int position, int value); // Save integer value to storage file (to defined position) RLAPI void StorageSaveValue(int position, int value); // Save integer value to storage file (to defined position)
RLAPI int StorageLoadValue(int position); // Load integer value from storage file (from defined position) RLAPI int StorageLoadValue(int position); // Load integer value from storage file (from defined position)
RLAPI void OpenURL(const char *url); // Open URL with default system browser (if available)
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// Input Handling Functions (Module: core) // Input Handling Functions (Module: core)
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
@ -967,7 +972,7 @@ RLAPI void DrawCircleLines(int centerX, int centerY, float radius, Color color);
RLAPI void DrawRectangle(int posX, int posY, int width, int height, Color color); // Draw a color-filled rectangle RLAPI void DrawRectangle(int posX, int posY, int width, int height, Color color); // Draw a color-filled rectangle
RLAPI void DrawRectangleV(Vector2 position, Vector2 size, Color color); // Draw a color-filled rectangle (Vector version) RLAPI void DrawRectangleV(Vector2 position, Vector2 size, Color color); // Draw a color-filled rectangle (Vector version)
RLAPI void DrawRectangleRec(Rectangle rec, Color color); // Draw a color-filled rectangle RLAPI void DrawRectangleRec(Rectangle rec, Color color); // Draw a color-filled rectangle
RLAPI void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color); // Draw a color-filled rectangle with pro parameters RLAPI void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color colors[4]); // Draw a color-filled rectangle with pro parameters
RLAPI void DrawRectangleGradientV(int posX, int posY, int width, int height, Color color1, Color color2);// Draw a vertical-gradient-filled rectangle RLAPI void DrawRectangleGradientV(int posX, int posY, int width, int height, Color color1, Color color2);// Draw a vertical-gradient-filled rectangle
RLAPI void DrawRectangleGradientH(int posX, int posY, int width, int height, Color color1, Color color2);// Draw a horizontal-gradient-filled rectangle RLAPI void DrawRectangleGradientH(int posX, int posY, int width, int height, Color color1, Color color2);// Draw a horizontal-gradient-filled rectangle
RLAPI void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4); // Draw a gradient-filled rectangle with custom vertex colors RLAPI void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4); // Draw a gradient-filled rectangle with custom vertex colors
@ -979,6 +984,8 @@ RLAPI void DrawPoly(Vector2 center, int sides, float radius, float rotation, Col
RLAPI void DrawPolyEx(Vector2 *points, int numPoints, Color color); // Draw a closed polygon defined by points RLAPI void DrawPolyEx(Vector2 *points, int numPoints, Color color); // Draw a closed polygon defined by points
RLAPI void DrawPolyExLines(Vector2 *points, int numPoints, Color color); // Draw polygon lines RLAPI void DrawPolyExLines(Vector2 *points, int numPoints, Color color); // Draw polygon lines
RLAPI void SetShapesTexture(Texture2D texture, Rectangle source); // Define default texture used to draw shapes
// Basic shapes collision detection functions // Basic shapes collision detection functions
RLAPI bool CheckCollisionRecs(Rectangle rec1, Rectangle rec2); // Check collision between two rectangles RLAPI bool CheckCollisionRecs(Rectangle rec1, Rectangle rec2); // Check collision between two rectangles
RLAPI bool CheckCollisionCircles(Vector2 center1, float radius1, Vector2 center2, float radius2); // Check collision between two circles RLAPI bool CheckCollisionCircles(Vector2 center1, float radius1, Vector2 center2, float radius2); // Check collision between two circles
@ -998,6 +1005,7 @@ RLAPI Image LoadImageEx(Color *pixels, int width, int height);
RLAPI Image LoadImagePro(void *data, int width, int height, int format); // Load image from raw data with parameters RLAPI Image LoadImagePro(void *data, int width, int height, int format); // Load image from raw data with parameters
RLAPI Image LoadImageRaw(const char *fileName, int width, int height, int format, int headerSize); // Load image from RAW file data RLAPI Image LoadImageRaw(const char *fileName, int width, int height, int format, int headerSize); // Load image from RAW file data
RLAPI void ExportImage(Image image, const char *fileName); // Export image data to file RLAPI void ExportImage(Image image, const char *fileName); // Export image data to file
RLAPI void ExportImageAsCode(Image image, const char *fileName); // Export image as code file defining an array of bytes
RLAPI Texture2D LoadTexture(const char *fileName); // Load texture from file into GPU memory (VRAM) RLAPI Texture2D LoadTexture(const char *fileName); // Load texture from file into GPU memory (VRAM)
RLAPI Texture2D LoadTextureFromImage(Image image); // Load texture from image data RLAPI Texture2D LoadTextureFromImage(Image image); // Load texture from image data
RLAPI RenderTexture2D LoadRenderTexture(int width, int height); // Load texture for rendering (framebuffer) RLAPI RenderTexture2D LoadRenderTexture(int width, int height); // Load texture for rendering (framebuffer)
@ -1019,15 +1027,17 @@ RLAPI void ImageAlphaClear(Image *image, Color color, float threshold);
RLAPI void ImageAlphaCrop(Image *image, float threshold); // Crop image depending on alpha value RLAPI void ImageAlphaCrop(Image *image, float threshold); // Crop image depending on alpha value
RLAPI void ImageAlphaPremultiply(Image *image); // Premultiply alpha channel RLAPI void ImageAlphaPremultiply(Image *image); // Premultiply alpha channel
RLAPI void ImageCrop(Image *image, Rectangle crop); // Crop an image to a defined rectangle RLAPI void ImageCrop(Image *image, Rectangle crop); // Crop an image to a defined rectangle
RLAPI void ImageResize(Image *image, int newWidth, int newHeight); // Resize image (bilinear filtering) RLAPI void ImageResize(Image *image, int newWidth, int newHeight); // Resize image (Bicubic scaling algorithm)
RLAPI void ImageResizeNN(Image *image, int newWidth,int newHeight); // Resize image (Nearest-Neighbor scaling algorithm) RLAPI void ImageResizeNN(Image *image, int newWidth,int newHeight); // Resize image (Nearest-Neighbor scaling algorithm)
RLAPI void ImageResizeCanvas(Image *image, int newWidth, int newHeight, int offsetX, int offsetY, Color color); // Resize canvas and fill with color RLAPI void ImageResizeCanvas(Image *image, int newWidth, int newHeight, int offsetX, int offsetY, Color color); // Resize canvas and fill with color
RLAPI void ImageMipmaps(Image *image); // Generate all mipmap levels for a provided image RLAPI void ImageMipmaps(Image *image); // Generate all mipmap levels for a provided image
RLAPI void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp); // Dither image data to 16bpp or lower (Floyd-Steinberg dithering) RLAPI void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp); // Dither image data to 16bpp or lower (Floyd-Steinberg dithering)
RLAPI Color *ImageExtractPalette(Image image, int maxPaletteSize, int *extractCount); // Extract color palette from image to maximum size (memory should be freed)
RLAPI Image ImageText(const char *text, int fontSize, Color color); // Create an image from text (default font) RLAPI Image ImageText(const char *text, int fontSize, Color color); // Create an image from text (default font)
RLAPI Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Color tint); // Create an image from text (custom sprite font) RLAPI Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Color tint); // Create an image from text (custom sprite font)
RLAPI void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec); // Draw a source image within a destination image RLAPI void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec); // Draw a source image within a destination image
RLAPI void ImageDrawRectangle(Image *dst, Vector2 position, Rectangle rec, Color color); // Draw rectangle within an image RLAPI void ImageDrawRectangle(Image *dst, Rectangle rec, Color color); // Draw rectangle within an image
RLAPI void ImageDrawRectangleLines(Image *dst, Rectangle rec, int thick, Color color); // Draw rectangle lines within an image
RLAPI void ImageDrawText(Image *dst, Vector2 position, const char *text, int fontSize, Color color); // Draw text (default font) within an image (destination) RLAPI void ImageDrawText(Image *dst, Vector2 position, const char *text, int fontSize, Color color); // Draw text (default font) within an image (destination)
RLAPI void ImageDrawTextEx(Image *dst, Vector2 position, Font font, const char *text, float fontSize, float spacing, Color color); // Draw text (custom sprite font) within an image (destination) RLAPI void ImageDrawTextEx(Image *dst, Vector2 position, Font font, const char *text, float fontSize, float spacing, Color color); // Draw text (custom sprite font) within an image (destination)
RLAPI void ImageFlipVertical(Image *image); // Flip image vertically RLAPI void ImageFlipVertical(Image *image); // Flip image vertically
@ -1073,21 +1083,25 @@ RLAPI Font GetFontDefault(void);
RLAPI Font LoadFont(const char *fileName); // Load font from file into GPU memory (VRAM) RLAPI Font LoadFont(const char *fileName); // Load font from file into GPU memory (VRAM)
RLAPI Font LoadFontEx(const char *fileName, int fontSize, int charsCount, int *fontChars); // Load font from file with extended parameters RLAPI Font LoadFontEx(const char *fileName, int fontSize, int charsCount, int *fontChars); // Load font from file with extended parameters
RLAPI CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int charsCount, int type); // Load font data for further use RLAPI CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int charsCount, int type); // Load font data for further use
RLAPI Image GenImageFontAtlas(CharInfo *chars, int fontSize, int charsCount, int padding, int packMethod); // Generate image font atlas using chars info RLAPI Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int padding, int packMethod); // Generate image font atlas using chars info
RLAPI void UnloadFont(Font font); // Unload Font from GPU memory (VRAM) RLAPI void UnloadFont(Font font); // Unload Font from GPU memory (VRAM)
// Text drawing functions // Text drawing functions
RLAPI void DrawFPS(int posX, int posY); // Shows current FPS RLAPI void DrawFPS(int posX, int posY); // Shows current FPS
RLAPI void DrawText(const char *text, int posX, int posY, int fontSize, Color color); // Draw text (using default font) RLAPI void DrawText(const char *text, int posX, int posY, int fontSize, Color color); // Draw text (using default font)
RLAPI void DrawTextEx(Font font, const char* text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text using font and additional parameters RLAPI void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text using font and additional parameters
// Text misc. functions // Text misc. functions
RLAPI int MeasureText(const char *text, int fontSize); // Measure string width for default font RLAPI int MeasureText(const char *text, int fontSize); // Measure string width for default font
RLAPI Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing); // Measure string size for Font RLAPI Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing); // Measure string size for Font
RLAPI const char *FormatText(const char *text, ...); // Formatting of text with variables to 'embed'
RLAPI const char *SubText(const char *text, int position, int length); // Get a piece of a text string
RLAPI int GetGlyphIndex(Font font, int character); // Get index position for a unicode character on font RLAPI int GetGlyphIndex(Font font, int character); // Get index position for a unicode character on font
// Text string edition functions
RLAPI const char *FormatText(const char *text, ...); // Formatting of text with variables to 'embed'
RLAPI const char *SubText(const char *text, int position, int length); // Get a piece of a text string
RLAPI char **SplitText(char *text, char delimiter, int *strCount); // Split text string into multiple strings (memory should be freed manually!)
RLAPI bool IsEqualText(const char *text1, const char *text2); // Check if two text string are equal
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// Basic 3d Shapes Drawing Functions (Module: models) // Basic 3d Shapes Drawing Functions (Module: models)
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
@ -1231,6 +1245,7 @@ RLAPI void UpdateSound(Sound sound, const void *data, int samplesCount);// Updat
RLAPI void UnloadWave(Wave wave); // Unload wave data RLAPI void UnloadWave(Wave wave); // Unload wave data
RLAPI void UnloadSound(Sound sound); // Unload sound RLAPI void UnloadSound(Sound sound); // Unload sound
RLAPI void ExportWave(Wave wave, const char *fileName); // Export wave data to file RLAPI void ExportWave(Wave wave, const char *fileName); // Export wave data to file
RLAPI void ExportWaveAsCode(Wave wave, const char *fileName); // Export wave sample data to code (.h)
// Wave/Sound management functions // Wave/Sound management functions
RLAPI void PlaySound(Sound sound); // Play a sound RLAPI void PlaySound(Sound sound); // Play a sound
@ -1273,7 +1288,7 @@ RLAPI void StopAudioStream(AudioStream stream); // Stop au
RLAPI void SetAudioStreamVolume(AudioStream stream, float volume); // Set volume for audio stream (1.0 is max level) RLAPI void SetAudioStreamVolume(AudioStream stream, float volume); // Set volume for audio stream (1.0 is max level)
RLAPI void SetAudioStreamPitch(AudioStream stream, float pitch); // Set pitch for audio stream (1.0 is base level) RLAPI void SetAudioStreamPitch(AudioStream stream, float pitch); // Set pitch for audio stream (1.0 is base level)
#ifdef __cplusplus #if defined(__cplusplus)
} }
#endif #endif

View file

@ -46,38 +46,31 @@
//#define RAYMATH_HEADER_ONLY // NOTE: To compile functions as static inline, uncomment this line //#define RAYMATH_HEADER_ONLY // NOTE: To compile functions as static inline, uncomment this line
#ifndef RAYMATH_STANDALONE #ifndef RAYMATH_STANDALONE
#include "raylib.h" // Required for structs: Vector3, Matrix #include "raylib.h" // Required for structs: Vector3, Matrix
#endif #endif
#ifdef __cplusplus #if defined(RAYMATH_IMPLEMENTATION) && defined(RAYMATH_HEADER_ONLY)
#define RMEXTERN extern "C" // Functions visible from other files (no name mangling of functions in C++)
#else
#define RMEXTERN // Functions visible from other files
#endif
#if defined RAYMATH_IMPLEMENTATION && defined RAYMATH_HEADER_ONLY
#error "Specifying both RAYMATH_IMPLEMENTATION and RAYMATH_HEADER_ONLY is contradictory" #error "Specifying both RAYMATH_IMPLEMENTATION and RAYMATH_HEADER_ONLY is contradictory"
#endif #endif
#ifdef RAYMATH_IMPLEMENTATION #if defined(RAYMATH_IMPLEMENTATION)
#if defined(_WIN32) && defined(BUILD_LIBTYPE_SHARED) #if defined(_WIN32) && defined(BUILD_LIBTYPE_SHARED)
#define RMDEF __declspec(dllexport) extern inline // We are building raylib as a Win32 shared library (.dll). #define RMDEF __declspec(dllexport) extern inline // We are building raylib as a Win32 shared library (.dll).
#elif defined(_WIN32) && defined(USE_LIBTYPE_SHARED) #elif defined(_WIN32) && defined(USE_LIBTYPE_SHARED)
#define RLAPI __declspec(dllimport) // We are using raylib as a Win32 shared library (.dll) #define RMDEF __declspec(dllimport) // We are using raylib as a Win32 shared library (.dll)
#else #else
#define RMDEF extern inline // Provide external definition #define RMDEF extern inline // Provide external definition
#endif #endif
#elif defined RAYMATH_HEADER_ONLY #elif defined(RAYMATH_HEADER_ONLY)
#define RMDEF static inline // Functions may be inlined, no external out-of-line definition #define RMDEF static inline // Functions may be inlined, no external out-of-line definition
#else #else
#ifdef __TINYC__ #if defined(__TINYC__)
#define RMDEF static inline // plain inline not supported by tinycc (See issue #435) #define RMDEF static inline // plain inline not supported by tinycc (See issue #435)
#else #else
#define RMDEF inline // Functions may be inlined or external definition used #define RMDEF inline // Functions may be inlined or external definition used
#endif #endif
#endif #endif
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Defines and Macros // Defines and Macros
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
@ -300,7 +293,7 @@ RMDEF Vector3 Vector3Add(Vector3 v1, Vector3 v2)
return result; return result;
} }
// Substract two vectors // Subtract two vectors
RMDEF Vector3 Vector3Subtract(Vector3 v1, Vector3 v2) RMDEF Vector3 Vector3Subtract(Vector3 v1, Vector3 v2)
{ {
Vector3 result = { v1.x - v2.x, v1.y - v2.y, v1.z - v2.z }; Vector3 result = { v1.x - v2.x, v1.y - v2.y, v1.z - v2.z };
@ -720,8 +713,8 @@ RMDEF Matrix MatrixAdd(Matrix left, Matrix right)
return result; return result;
} }
// Substract two matrices (left - right) // Subtract two matrices (left - right)
RMDEF Matrix MatrixSubstract(Matrix left, Matrix right) RMDEF Matrix MatrixSubtract(Matrix left, Matrix right)
{ {
Matrix result = MatrixIdentity(); Matrix result = MatrixIdentity();

View file

@ -378,7 +378,7 @@ typedef unsigned char byte;
} VrDevice; } VrDevice;
#endif #endif
#ifdef __cplusplus #if defined(__cplusplus)
extern "C" { // Prevents name mangling of functions extern "C" { // Prevents name mangling of functions
#endif #endif
@ -516,7 +516,7 @@ void TraceLog(int msgType, const char *text, ...); // Show trace log messag
int GetPixelDataSize(int width, int height, int format);// Get pixel data size in bytes (image or texture) int GetPixelDataSize(int width, int height, int format);// Get pixel data size in bytes (image or texture)
#endif #endif
#ifdef __cplusplus #if defined(__cplusplus)
} }
#endif #endif
@ -553,7 +553,7 @@ int GetPixelDataSize(int width, int height, int format);// Get pixel data size i
#else #else
// APIENTRY for OpenGL function pointer declarations is required // APIENTRY for OpenGL function pointer declarations is required
#ifndef APIENTRY #ifndef APIENTRY
#ifdef _WIN32 #if defined(_WIN32)
#define APIENTRY __stdcall #define APIENTRY __stdcall
#else #else
#define APIENTRY #define APIENTRY
@ -1506,9 +1506,17 @@ void rlDeleteTextures(unsigned int id)
void rlDeleteRenderTextures(RenderTexture2D target) void rlDeleteRenderTextures(RenderTexture2D target)
{ {
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
if (target.id > 0) glDeleteFramebuffers(1, &target.id);
if (target.texture.id > 0) glDeleteTextures(1, &target.texture.id); if (target.texture.id > 0) glDeleteTextures(1, &target.texture.id);
if (target.depth.id > 0) glDeleteTextures(1, &target.depth.id); if (target.depth.id > 0)
{
#if defined(GRAPHICS_API_OPENGL_21) || defined(GRAPHICS_API_OPENGL_ES2)
glDeleteRenderbuffers(1, &target.depth.id);
#elif defined(GRAPHICS_API_OPENGL_33)
glDeleteTextures(1, &target.depth.id);
#endif
}
if (target.id > 0) glDeleteFramebuffers(1, &target.id);
TraceLog(LOG_INFO, "[FBO ID %i] Unloaded render texture data from VRAM (GPU)", target.id); TraceLog(LOG_INFO, "[FBO ID %i] Unloaded render texture data from VRAM (GPU)", target.id);
#endif #endif
@ -1620,7 +1628,7 @@ void rlglInit(int width, int height)
// NOTE: We don't need to check again supported extensions but we do (GLAD already dealt with that) // NOTE: We don't need to check again supported extensions but we do (GLAD already dealt with that)
glGetIntegerv(GL_NUM_EXTENSIONS, &numExt); glGetIntegerv(GL_NUM_EXTENSIONS, &numExt);
#ifdef _MSC_VER #if defined(_MSC_VER)
const char **extList = malloc(sizeof(const char *)*numExt); const char **extList = malloc(sizeof(const char *)*numExt);
#else #else
const char *extList[numExt]; const char *extList[numExt];
@ -1631,17 +1639,13 @@ void rlglInit(int width, int height)
#elif defined(GRAPHICS_API_OPENGL_ES2) #elif defined(GRAPHICS_API_OPENGL_ES2)
char *extensions = (char *)glGetString(GL_EXTENSIONS); // One big const string char *extensions = (char *)glGetString(GL_EXTENSIONS); // One big const string
// NOTE: We have to duplicate string because glGetString() returns a const value // NOTE: We have to duplicate string because glGetString() returns a const string
// If not duplicated, it fails in some systems (Raspberry Pi) int len = strlen(extensions) + 1;
// Equivalent to function: char *strdup(const char *str) char *extensionsDup = (char *)malloc(len);
char *extensionsDup; strcpy(extensionsDup, extensions);
size_t len = strlen(extensions) + 1;
void *newstr = malloc(len);
if (newstr == NULL) extensionsDup = NULL;
extensionsDup = (char *)memcpy(newstr, extensions, len);
// NOTE: String could be splitted using strtok() function (string.h) // NOTE: String could be splitted using strtok() function (string.h)
// NOTE: strtok() modifies the received string, it can not be const // NOTE: strtok() modifies the passed string, it can not be const
char *extList[512]; // Allocate 512 strings pointers (2 KB) char *extList[512]; // Allocate 512 strings pointers (2 KB)
@ -2175,7 +2179,7 @@ void rlUnloadTexture(unsigned int id)
// Load a texture to be used for rendering (fbo with color and depth attachments) // Load a texture to be used for rendering (fbo with color and depth attachments)
RenderTexture2D rlLoadRenderTexture(int width, int height) RenderTexture2D rlLoadRenderTexture(int width, int height)
{ {
RenderTexture2D target; RenderTexture2D target = { 0 };
target.id = 0; target.id = 0;
@ -2255,8 +2259,16 @@ RenderTexture2D rlLoadRenderTexture(int width, int height)
default: break; default: break;
} }
glDeleteTextures(1, &target.texture.id); if (target.texture.id > 0) glDeleteTextures(1, &target.texture.id);
glDeleteTextures(1, &target.depth.id); if (target.depth.id > 0)
{
#if defined(USE_DEPTH_RENDERBUFFER)
glDeleteRenderbuffers(1, &target.depth.id);
#elif defined(USE_DEPTH_TEXTURE)
glDeleteTextures(1, &target.depth.id);
#endif
}
glDeleteFramebuffers(1, &target.id); glDeleteFramebuffers(1, &target.id);
} }
else TraceLog(LOG_INFO, "[FBO ID %i] Framebuffer object created successfully", target.id); else TraceLog(LOG_INFO, "[FBO ID %i] Framebuffer object created successfully", target.id);
@ -3807,7 +3819,7 @@ static unsigned int LoadShaderProgram(unsigned int vShaderId, unsigned int fShad
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength); glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength);
#ifdef _MSC_VER #if defined(_MSC_VER)
char *log = malloc(maxLength); char *log = malloc(maxLength);
#else #else
char log[maxLength]; char log[maxLength];
@ -3816,7 +3828,7 @@ static unsigned int LoadShaderProgram(unsigned int vShaderId, unsigned int fShad
TraceLog(LOG_INFO, "%s", log); TraceLog(LOG_INFO, "%s", log);
#ifdef _MSC_VER #if defined(_MSC_VER)
free(log); free(log);
#endif #endif
glDeleteProgram(program); glDeleteProgram(program);

View file

@ -54,12 +54,14 @@
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Global Variables Definition // Global Variables Definition
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// ... static Texture2D texShapes = { 0 };
static Rectangle recTexShapes = { 0 };
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Module specific Functions Declaration // Module specific Functions Declaration
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
static float EaseCubicInOut(float t, float b, float c, float d); // Cubic easing static float EaseCubicInOut(float t, float b, float c, float d); // Cubic easing
static Texture2D GetShapesTexture(void); // Get texture to draw shapes
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Module Functions Definition // Module Functions Definition
@ -121,20 +123,27 @@ void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color)
float d = sqrtf(dx*dx + dy*dy); float d = sqrtf(dx*dx + dy*dy);
float angle = asinf(dy/d); float angle = asinf(dy/d);
rlEnableTexture(GetTextureDefault().id); rlEnableTexture(GetShapesTexture().id);
rlPushMatrix(); rlPushMatrix();
rlTranslatef((float)startPos.x, (float)startPos.y, 0); rlTranslatef((float)startPos.x, (float)startPos.y, 0);
rlRotatef(RAD2DEG*angle, 0, 0, 1); rlRotatef(RAD2DEG*angle, 0, 0, 1);
rlTranslatef(0, -thick/2.0f, 0); rlTranslatef(0, (thick > 1.0f) ? -thick/2.0f : -1.0f, 0);
rlBegin(RL_QUADS); rlBegin(RL_QUADS);
rlColor4ub(color.r, color.g, color.b, color.a); rlColor4ub(color.r, color.g, color.b, color.a);
rlNormal3f(0.0f, 0.0f, 1.0f); rlNormal3f(0.0f, 0.0f, 1.0f);
rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
rlVertex2f(0.0f, 0.0f); rlVertex2f(0.0f, 0.0f);
rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
rlVertex2f(0.0f, thick); rlVertex2f(0.0f, thick);
rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
rlVertex2f(d, thick); rlVertex2f(d, thick);
rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height);
rlVertex2f(d, 0.0f); rlVertex2f(d, 0.0f);
rlEnd(); rlEnd();
rlPopMatrix(); rlPopMatrix();
@ -195,16 +204,23 @@ void DrawCircleV(Vector2 center, float radius, Color color)
#if defined(SUPPORT_QUADS_DRAW_MODE) #if defined(SUPPORT_QUADS_DRAW_MODE)
if (rlCheckBufferLimit(RL_QUADS, 4*(36/2))) rlglDraw(); if (rlCheckBufferLimit(RL_QUADS, 4*(36/2))) rlglDraw();
rlEnableTexture(GetTextureDefault().id); // Default white texture rlEnableTexture(GetShapesTexture().id);
rlBegin(RL_QUADS); rlBegin(RL_QUADS);
for (int i = 0; i < 360; i += 20) for (int i = 0; i < 360; i += 20)
{ {
rlColor4ub(color.r, color.g, color.b, color.a); rlColor4ub(color.r, color.g, color.b, color.a);
rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
rlVertex2f(center.x, center.y); rlVertex2f(center.x, center.y);
rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
rlVertex2f(center.x + sinf(DEG2RAD*i)*radius, center.y + cosf(DEG2RAD*i)*radius); rlVertex2f(center.x + sinf(DEG2RAD*i)*radius, center.y + cosf(DEG2RAD*i)*radius);
rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
rlVertex2f(center.x + sinf(DEG2RAD*(i + 10))*radius, center.y + cosf(DEG2RAD*(i + 10))*radius); rlVertex2f(center.x + sinf(DEG2RAD*(i + 10))*radius, center.y + cosf(DEG2RAD*(i + 10))*radius);
rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height);
rlVertex2f(center.x + sinf(DEG2RAD*(i + 20))*radius, center.y + cosf(DEG2RAD*(i + 20))*radius); rlVertex2f(center.x + sinf(DEG2RAD*(i + 20))*radius, center.y + cosf(DEG2RAD*(i + 20))*radius);
} }
rlEnd(); rlEnd();
@ -246,104 +262,55 @@ void DrawCircleLines(int centerX, int centerY, float radius, Color color)
// Draw a color-filled rectangle // Draw a color-filled rectangle
void DrawRectangle(int posX, int posY, int width, int height, Color color) void DrawRectangle(int posX, int posY, int width, int height, Color color)
{ {
Vector2 position = { (float)posX, (float)posY }; DrawRectangleV((Vector2){ (float)posX, (float)posY }, (Vector2){ (float)width, (float)height }, color);
Vector2 size = { (float)width, (float)height };
DrawRectangleV(position, size, color);
} }
// Draw a color-filled rectangle (Vector version) // Draw a color-filled rectangle (Vector version)
// NOTE: On OpenGL 3.3 and ES2 we use QUADS to avoid drawing order issues (view rlglDraw) // NOTE: On OpenGL 3.3 and ES2 we use QUADS to avoid drawing order issues (view rlglDraw)
void DrawRectangleV(Vector2 position, Vector2 size, Color color) void DrawRectangleV(Vector2 position, Vector2 size, Color color)
{ {
#if defined(SUPPORT_QUADS_DRAW_MODE) Color colors[4] = { color, color, color, color };
#if defined(SUPPORT_FONT_TEXTURE)
// Draw rectangle using font texture white character
rlEnableTexture(GetFontDefault().texture.id);
rlBegin(RL_QUADS); DrawRectanglePro((Rectangle){ position.x, position.y, size.x, size.y }, (Vector2){ 0.0f, 0.0f }, 0.0f, colors);
rlColor4ub(color.r, color.g, color.b, color.a);
rlNormal3f(0.0f, 0.0f, 1.0f);
// NOTE: Default raylib font character 95 is a white square
rlTexCoord2f((float)GetFontDefault().chars[95].rec.x/GetFontDefault().texture.width,
(float)GetFontDefault().chars[95].rec.y/GetFontDefault().texture.height);
rlVertex2f(position.x, position.y);
rlTexCoord2f((float)GetFontDefault().chars[95].rec.x/GetFontDefault().texture.width,
(float)(GetFontDefault().chars[95].rec.y + GetFontDefault().chars[95].rec.height)/GetFontDefault().texture.height);
rlVertex2f(position.x, position.y + size.y);
rlTexCoord2f((float)(GetFontDefault().chars[95].rec.x + GetFontDefault().chars[95].rec.width)/GetFontDefault().texture.width,
(float)(GetFontDefault().chars[95].rec.y + GetFontDefault().chars[95].rec.height)/GetFontDefault().texture.height);
rlVertex2f(position.x + size.x, position.y + size.y);
rlTexCoord2f((float)(GetFontDefault().chars[95].rec.x + GetFontDefault().chars[95].rec.width)/GetFontDefault().texture.width,
(float)GetFontDefault().chars[95].rec.y/GetFontDefault().texture.height);
rlVertex2f(position.x + size.x, position.y);
rlEnd();
rlDisableTexture();
#else
rlEnableTexture(GetTextureDefault().id); // Default white texture
rlBegin(RL_QUADS);
rlColor4ub(color.r, color.g, color.b, color.a);
rlNormal3f(0.0f, 0.0f, 1.0f);
rlTexCoord2f(0.0f, 0.0f);
rlVertex2f(position.x, position.y);
rlTexCoord2f(0.0f, 1.0f);
rlVertex2f(position.x, position.y + size.y);
rlTexCoord2f(1.0f, 1.0f);
rlVertex2f(position.x + size.x, position.y + size.y);
rlTexCoord2f(1.0f, 0.0f);
rlVertex2f(position.x + size.x, position.y);
rlEnd();
rlDisableTexture();
#endif // SUPPORT_FONT_TEXTURE
#else
rlBegin(RL_TRIANGLES);
rlColor4ub(color.r, color.g, color.b, color.a);
rlVertex2i(position.x, position.y);
rlVertex2i(position.x, position.y + size.y);
rlVertex2i(position.x + size.x, position.y + size.y);
rlVertex2i(position.x, position.y);
rlVertex2i(position.x + size.x, position.y + size.y);
rlVertex2i(position.x + size.x, position.y);
rlEnd();
#endif // SUPPORT_QUADS_DRAW_MODE
} }
// Draw a color-filled rectangle // Draw a color-filled rectangle
void DrawRectangleRec(Rectangle rec, Color color) void DrawRectangleRec(Rectangle rec, Color color)
{ {
DrawRectangle((int)rec.x, (int)rec.y, (int)rec.width, (int)rec.height, color); Color colors[4] = { color, color, color, color };
DrawRectanglePro(rec, (Vector2){ 0.0f, 0.0f }, 0.0f, colors);
} }
void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color) // Draw a color-filled rectangle with pro parameters
void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color colors[4])
{ {
rlEnableTexture(GetTextureDefault().id); rlEnableTexture(GetShapesTexture().id);
rlPushMatrix(); rlPushMatrix();
rlTranslatef(rec.x, rec.y, 0); //rlTranslatef(rec.x, rec.y, 0); // Already considered on vertex position
rlRotatef(rotation, 0, 0, 1); rlRotatef(rotation, 0, 0, 1);
rlTranslatef(-origin.x, -origin.y, 0); rlTranslatef(-origin.x, -origin.y, 0);
rlBegin(RL_QUADS); rlBegin(RL_QUADS);
rlColor4ub(color.r, color.g, color.b, color.a); rlNormal3f(0.0f, 0.0f, 1.0f);
rlNormal3f(0.0f, 0.0f, 1.0f); // Normal vector pointing towards viewer
rlVertex2f(0.0f, 0.0f); // NOTE: Default raylib font character 95 is a white square
rlVertex2f(0.0f, rec.height); rlColor4ub(colors[0].r, colors[0].g, colors[0].b, colors[0].a);
rlVertex2f(rec.width, rec.height); rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
rlVertex2f(rec.width, 0.0f); rlVertex2f(rec.x, rec.y);
rlColor4ub(colors[1].r, colors[1].g, colors[1].b, colors[1].a);
rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
rlVertex2f(rec.x, rec.y + rec.height);
rlColor4ub(colors[2].r, colors[2].g, colors[2].b, colors[2].a);
rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
rlVertex2f(rec.x + rec.width, rec.y + rec.height);
rlColor4ub(colors[3].r, colors[3].g, colors[3].b, colors[3].a);
rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height);
rlVertex2f(rec.x + rec.width, rec.y);
rlEnd(); rlEnd();
rlPopMatrix(); rlPopMatrix();
@ -368,61 +335,9 @@ void DrawRectangleGradientH(int posX, int posY, int width, int height, Color col
// NOTE: Colors refer to corners, starting at top-lef corner and counter-clockwise // NOTE: Colors refer to corners, starting at top-lef corner and counter-clockwise
void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4) void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4)
{ {
#if defined(SUPPORT_FONT_TEXTURE) Color colors[4] = { col1, col2, col3, col4 };
// Draw rectangle using font texture white character
rlEnableTexture(GetFontDefault().texture.id);
rlBegin(RL_QUADS); DrawRectanglePro(rec, (Vector2){ 0.0f, 0.0f }, 0.0f, colors);
rlNormal3f(0.0f, 0.0f, 1.0f);
// NOTE: Default raylib font character 95 is a white square
rlColor4ub(col1.r, col1.g, col1.b, col1.a);
rlTexCoord2f(GetFontDefault().chars[95].rec.x/GetFontDefault().texture.width,
GetFontDefault().chars[95].rec.y/GetFontDefault().texture.height);
rlVertex2f(rec.x, rec.y);
rlColor4ub(col2.r, col2.g, col2.b, col2.a);
rlTexCoord2f(GetFontDefault().chars[95].rec.x/GetFontDefault().texture.width,
(GetFontDefault().chars[95].rec.y + GetFontDefault().chars[95].rec.height)/GetFontDefault().texture.height);
rlVertex2f(rec.x, rec.y + rec.height);
rlColor4ub(col3.r, col3.g, col3.b, col3.a);
rlTexCoord2f((GetFontDefault().chars[95].rec.x + GetFontDefault().chars[95].rec.width)/GetFontDefault().texture.width,
(GetFontDefault().chars[95].rec.y + GetFontDefault().chars[95].rec.height)/GetFontDefault().texture.height);
rlVertex2f(rec.x + rec.width, rec.y + rec.height);
rlColor4ub(col4.r, col4.g, col4.b, col4.a);
rlTexCoord2f((GetFontDefault().chars[95].rec.x + GetFontDefault().chars[95].rec.width)/GetFontDefault().texture.width,
GetFontDefault().chars[95].rec.y/GetFontDefault().texture.height);
rlVertex2f(rec.x + rec.width, rec.y);
rlEnd();
rlDisableTexture();
#else
rlEnableTexture(GetTextureDefault().id); // Default white texture
rlBegin(RL_QUADS);
rlNormal3f(0.0f, 0.0f, 1.0f);
rlColor4ub(col1.r, col1.g, col1.b, col1.a);
rlTexCoord2f(0.0f, 0.0f);
rlVertex2f(rec.x, rec.y);
rlColor4ub(col2.r, col2.g, col2.b, col2.a);
rlTexCoord2f(0.0f, 1.0f);
rlVertex2f(rec.x, rec.y + rec.height);
rlColor4ub(col3.r, col3.g, col3.b, col3.a);
rlTexCoord2f(1.0f, 1.0f);
rlVertex2f(rec.x + rec.width, rec.y + rec.height);
rlColor4ub(col4.r, col4.g, col4.b, col4.a);
rlTexCoord2f(1.0f, 0.0f);
rlVertex2f(rec.x + rec.width, rec.y);
rlEnd();
rlDisableTexture();
#endif
} }
// Draw rectangle outline // Draw rectangle outline
@ -471,13 +386,21 @@ void DrawRectangleLinesEx(Rectangle rec, int lineThick, Color color)
void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color) void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color)
{ {
#if defined(SUPPORT_QUADS_DRAW_MODE) #if defined(SUPPORT_QUADS_DRAW_MODE)
rlEnableTexture(GetTextureDefault().id); // Default white texture rlEnableTexture(GetShapesTexture().id);
rlBegin(RL_QUADS); rlBegin(RL_QUADS);
rlColor4ub(color.r, color.g, color.b, color.a); rlColor4ub(color.r, color.g, color.b, color.a);
rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
rlVertex2f(v1.x, v1.y); rlVertex2f(v1.x, v1.y);
rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
rlVertex2f(v2.x, v2.y); rlVertex2f(v2.x, v2.y);
rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
rlVertex2f(v2.x, v2.y); rlVertex2f(v2.x, v2.y);
rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height);
rlVertex2f(v3.x, v3.y); rlVertex2f(v3.x, v3.y);
rlEnd(); rlEnd();
@ -520,16 +443,23 @@ void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color col
rlRotatef(rotation, 0, 0, 1); rlRotatef(rotation, 0, 0, 1);
#if defined(SUPPORT_QUADS_DRAW_MODE) #if defined(SUPPORT_QUADS_DRAW_MODE)
rlEnableTexture(GetTextureDefault().id); // Default white texture rlEnableTexture(GetShapesTexture().id);
rlBegin(RL_QUADS); rlBegin(RL_QUADS);
for (int i = 0; i < 360; i += 360/sides) for (int i = 0; i < 360; i += 360/sides)
{ {
rlColor4ub(color.r, color.g, color.b, color.a); rlColor4ub(color.r, color.g, color.b, color.a);
rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
rlVertex2f(0, 0); rlVertex2f(0, 0);
rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
rlVertex2f(sinf(DEG2RAD*i)*radius, cosf(DEG2RAD*i)*radius); rlVertex2f(sinf(DEG2RAD*i)*radius, cosf(DEG2RAD*i)*radius);
rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
rlVertex2f(sinf(DEG2RAD*i)*radius, cosf(DEG2RAD*i)*radius); rlVertex2f(sinf(DEG2RAD*i)*radius, cosf(DEG2RAD*i)*radius);
rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height);
rlVertex2f(sinf(DEG2RAD*(i + 360/sides))*radius, cosf(DEG2RAD*(i + 360/sides))*radius); rlVertex2f(sinf(DEG2RAD*(i + 360/sides))*radius, cosf(DEG2RAD*(i + 360/sides))*radius);
} }
rlEnd(); rlEnd();
@ -557,7 +487,7 @@ void DrawPolyEx(Vector2 *points, int pointsCount, Color color)
if (rlCheckBufferLimit(RL_QUADS, pointsCount)) rlglDraw(); if (rlCheckBufferLimit(RL_QUADS, pointsCount)) rlglDraw();
#if defined(SUPPORT_QUADS_DRAW_MODE) #if defined(SUPPORT_QUADS_DRAW_MODE)
rlEnableTexture(GetTextureDefault().id); // Default white texture rlEnableTexture(GetShapesTexture().id);
rlBegin(RL_QUADS); rlBegin(RL_QUADS);
rlColor4ub(color.r, color.g, color.b, color.a); rlColor4ub(color.r, color.g, color.b, color.a);
@ -605,6 +535,13 @@ void DrawPolyExLines(Vector2 *points, int pointsCount, Color color)
} }
} }
// Define default texture used to draw shapes
void SetShapesTexture(Texture2D texture, Rectangle source)
{
texShapes = texture;
recTexShapes = source;
}
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Module Functions Definition - Collision Detection functions // Module Functions Definition - Collision Detection functions
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
@ -771,3 +708,20 @@ static float EaseCubicInOut(float t, float b, float c, float d)
t -= 2; t -= 2;
return 0.5f*c*(t*t*t + 2.0f) + b; return 0.5f*c*(t*t*t + 2.0f) + b;
} }
// Get texture to draw shapes (RAII)
static Texture2D GetShapesTexture(void)
{
if (texShapes.id <= 0)
{
#if defined(SUPPORT_FONT_TEXTURE)
texShapes = GetFontDefault().texture; // Use font texture white character
recTexShapes = GetFontDefault().chars[95].rec;
#else
texShapes = GetTextureDefault(); // Use default white texture
recTexShapes = { 0.0f, 0.0f, 1.0f, 1.0f };
#endif
}
return texShapes;
}

View file

@ -112,12 +112,12 @@ func DrawRectangleRec(rec Rectangle, color Color) {
} }
// DrawRectanglePro - Draw a color-filled rectangle with pro parameters // DrawRectanglePro - Draw a color-filled rectangle with pro parameters
func DrawRectanglePro(rec Rectangle, origin Vector2, rotation float32, color Color) { func DrawRectanglePro(rec Rectangle, origin Vector2, rotation float32, colors []Color) {
crec := rec.cptr() crec := rec.cptr()
corigin := origin.cptr() corigin := origin.cptr()
crotation := (C.float)(rotation) crotation := (C.float)(rotation)
ccolor := color.cptr() ccolor := (*C.Color)(unsafe.Pointer(&colors[0]))
C.DrawRectanglePro(*crec, *corigin, crotation, *ccolor) C.DrawRectanglePro(*crec, *corigin, crotation, ccolor)
} }
// DrawRectangleGradientV - Draw a vertical-gradient-filled rectangle // DrawRectangleGradientV - Draw a vertical-gradient-filled rectangle
@ -290,3 +290,10 @@ func CheckCollisionPointTriangle(point, p1, p2, p3 Vector2) bool {
v := bool(ret) v := bool(ret)
return v return v
} }
// SetShapesTexture - Define default texture used to draw shapes
func SetShapesTexture(texture Texture2D, source Rectangle) {
ctexture := texture.cptr()
csource := source.cptr()
C.SetShapesTexture(*ctexture, *csource)
}

View file

@ -190,10 +190,6 @@ extern void LoadDefaultFont(void)
if (counter > 512) counter = 0; // Security check... if (counter > 512) counter = 0; // Security check...
} }
//FILE *myimage = fopen("default_font.raw", "wb");
//fwrite(image.pixels, 1, 128*128*4, myimage);
//fclose(myimage);
Image image = LoadImageEx(imagePixels, imWidth, imHeight); Image image = LoadImageEx(imagePixels, imWidth, imHeight);
ImageFormat(&image, UNCOMPRESSED_GRAY_ALPHA); ImageFormat(&image, UNCOMPRESSED_GRAY_ALPHA);
@ -276,15 +272,7 @@ Font LoadFont(const char *fileName)
Font font = { 0 }; Font font = { 0 };
#if defined(SUPPORT_FILEFORMAT_TTF) #if defined(SUPPORT_FILEFORMAT_TTF)
if (IsFileExtension(fileName, ".ttf")) if (IsFileExtension(fileName, ".ttf")) font = LoadFontEx(fileName, DEFAULT_TTF_FONTSIZE, DEFAULT_TTF_NUMCHARS, NULL);
{
font.baseSize = DEFAULT_TTF_FONTSIZE;
font.charsCount = DEFAULT_TTF_NUMCHARS;
font.chars = LoadFontData(fileName, font.baseSize, NULL, font.charsCount, FONT_DEFAULT);
Image atlas = GenImageFontAtlas(font.chars, font.charsCount, font.baseSize, 4, 0);
font.texture = LoadTextureFromImage(atlas);
UnloadImage(atlas);
}
else else
#endif #endif
#if defined(SUPPORT_FILEFORMAT_FNT) #if defined(SUPPORT_FILEFORMAT_FNT)
@ -317,9 +305,14 @@ Font LoadFontEx(const char *fileName, int fontSize, int charsCount, int *fontCha
font.baseSize = fontSize; font.baseSize = fontSize;
font.charsCount = (charsCount > 0) ? charsCount : 95; font.charsCount = (charsCount > 0) ? charsCount : 95;
font.chars = LoadFontData(fileName, font.baseSize, fontChars, font.charsCount, FONT_DEFAULT); font.chars = LoadFontData(fileName, font.baseSize, fontChars, font.charsCount, FONT_DEFAULT);
Image atlas = GenImageFontAtlas(font.chars, font.charsCount, font.baseSize, 2, 0);
font.texture = LoadTextureFromImage(atlas); if (font.chars != NULL)
UnloadImage(atlas); {
Image atlas = GenImageFontAtlas(font.chars, font.charsCount, font.baseSize, 2, 0);
font.texture = LoadTextureFromImage(atlas);
UnloadImage(atlas);
}
else font = GetFontDefault();
return font; return font;
} }
@ -336,90 +329,96 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c
#define BITMAP_ALPHA_THRESHOLD 80 #define BITMAP_ALPHA_THRESHOLD 80
// In case no chars count provided, default to 95 CharInfo *chars = NULL;
charsCount = (charsCount > 0) ? charsCount : 95;
CharInfo *chars = (CharInfo *)malloc(charsCount*sizeof(CharInfo));
// Load font data (including pixel data) from TTF file // Load font data (including pixel data) from TTF file
// NOTE: Loaded information should be enough to generate font image atlas, // NOTE: Loaded information should be enough to generate font image atlas,
// using any packaging method // using any packaging method
FILE *fontFile = fopen(fileName, "rb"); // Load font file FILE *fontFile = fopen(fileName, "rb"); // Load font file
fseek(fontFile, 0, SEEK_END); if (fontFile != NULL)
long size = ftell(fontFile); // Get file size
fseek(fontFile, 0, SEEK_SET); // Reset file pointer
unsigned char *fontBuffer = (unsigned char *)malloc(size);
fread(fontBuffer, size, 1, fontFile);
fclose(fontFile);
// Init font for data reading
stbtt_fontinfo fontInfo;
if (!stbtt_InitFont(&fontInfo, fontBuffer, 0)) TraceLog(LOG_WARNING, "Failed to init font!");
// Calculate font scale factor
float scaleFactor = stbtt_ScaleForPixelHeight(&fontInfo, (float)fontSize);
// Calculate font basic metrics
// NOTE: ascent is equivalent to font baseline
int ascent, descent, lineGap;
stbtt_GetFontVMetrics(&fontInfo, &ascent, &descent, &lineGap);
// Fill fontChars in case not provided externally
// NOTE: By default we fill charsCount consecutevely, starting at 32 (Space)
int genFontChars = false;
if (fontChars == NULL) genFontChars = true;
if (genFontChars)
{ {
fontChars = (int *)malloc(charsCount*sizeof(int)); fseek(fontFile, 0, SEEK_END);
for (int i = 0; i < charsCount; i++) fontChars[i] = i + 32; long size = ftell(fontFile); // Get file size
} fseek(fontFile, 0, SEEK_SET); // Reset file pointer
// NOTE: Using simple packaging, one char after another unsigned char *fontBuffer = (unsigned char *)malloc(size);
for (int i = 0; i < charsCount; i++)
{
int chw = 0, chh = 0; // Character width and height (on generation)
int ch = fontChars[i]; // Character value to get info for
chars[i].value = ch;
// Render a unicode codepoint to a bitmap fread(fontBuffer, size, 1, fontFile);
// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap fclose(fontFile);
// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be
// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide
if (type != FONT_SDF) chars[i].data = stbtt_GetCodepointBitmap(&fontInfo, scaleFactor, scaleFactor, ch, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY); // Init font for data reading
else if (ch != 32) chars[i].data = stbtt_GetCodepointSDF(&fontInfo, scaleFactor, ch, SDF_CHAR_PADDING, SDF_ON_EDGE_VALUE, SDF_PIXEL_DIST_SCALE, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY); stbtt_fontinfo fontInfo;
if (!stbtt_InitFont(&fontInfo, fontBuffer, 0)) TraceLog(LOG_WARNING, "Failed to init font!");
if (type == FONT_BITMAP) // Calculate font scale factor
float scaleFactor = stbtt_ScaleForPixelHeight(&fontInfo, (float)fontSize);
// Calculate font basic metrics
// NOTE: ascent is equivalent to font baseline
int ascent, descent, lineGap;
stbtt_GetFontVMetrics(&fontInfo, &ascent, &descent, &lineGap);
// In case no chars count provided, default to 95
charsCount = (charsCount > 0) ? charsCount : 95;
// Fill fontChars in case not provided externally
// NOTE: By default we fill charsCount consecutevely, starting at 32 (Space)
int genFontChars = false;
if (fontChars == NULL)
{ {
// Aliased bitmap (black & white) font generation, avoiding anti-aliasing fontChars = (int *)malloc(charsCount*sizeof(int));
// NOTE: For optimum results, bitmap font should be generated at base pixel size for (int i = 0; i < charsCount; i++) fontChars[i] = i + 32;
for (int p = 0; p < chw*chh; p++) genFontChars = true;
{
if (chars[i].data[p] < BITMAP_ALPHA_THRESHOLD) chars[i].data[p] = 0;
else chars[i].data[p] = 255;
}
} }
chars[i].rec.width = (float)chw; chars = (CharInfo *)malloc(charsCount*sizeof(CharInfo));
chars[i].rec.height = (float)chh;
chars[i].offsetY += (int)((float)ascent*scaleFactor);
// Get bounding box for character (may be offset to account for chars that dip above or below the line) // NOTE: Using simple packaging, one char after another
int chX1, chY1, chX2, chY2; for (int i = 0; i < charsCount; i++)
stbtt_GetCodepointBitmapBox(&fontInfo, ch, scaleFactor, scaleFactor, &chX1, &chY1, &chX2, &chY2); {
int chw = 0, chh = 0; // Character width and height (on generation)
int ch = fontChars[i]; // Character value to get info for
chars[i].value = ch;
TraceLog(LOG_DEBUG, "Character box measures: %i, %i, %i, %i", chX1, chY1, chX2 - chX1, chY2 - chY1); // Render a unicode codepoint to a bitmap
TraceLog(LOG_DEBUG, "Character offsetY: %i", (int)((float)ascent*scaleFactor) + chY1); // stbtt_GetCodepointBitmap() -- allocates and returns a bitmap
// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be
// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide
stbtt_GetCodepointHMetrics(&fontInfo, ch, &chars[i].advanceX, NULL); if (type != FONT_SDF) chars[i].data = stbtt_GetCodepointBitmap(&fontInfo, scaleFactor, scaleFactor, ch, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY);
chars[i].advanceX *= scaleFactor; else if (ch != 32) chars[i].data = stbtt_GetCodepointSDF(&fontInfo, scaleFactor, ch, SDF_CHAR_PADDING, SDF_ON_EDGE_VALUE, SDF_PIXEL_DIST_SCALE, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY);
if (type == FONT_BITMAP)
{
// Aliased bitmap (black & white) font generation, avoiding anti-aliasing
// NOTE: For optimum results, bitmap font should be generated at base pixel size
for (int p = 0; p < chw*chh; p++)
{
if (chars[i].data[p] < BITMAP_ALPHA_THRESHOLD) chars[i].data[p] = 0;
else chars[i].data[p] = 255;
}
}
chars[i].rec.width = (float)chw;
chars[i].rec.height = (float)chh;
chars[i].offsetY += (int)((float)ascent*scaleFactor);
// Get bounding box for character (may be offset to account for chars that dip above or below the line)
int chX1, chY1, chX2, chY2;
stbtt_GetCodepointBitmapBox(&fontInfo, ch, scaleFactor, scaleFactor, &chX1, &chY1, &chX2, &chY2);
TraceLog(LOG_DEBUG, "Character box measures: %i, %i, %i, %i", chX1, chY1, chX2 - chX1, chY2 - chY1);
TraceLog(LOG_DEBUG, "Character offsetY: %i", (int)((float)ascent*scaleFactor) + chY1);
stbtt_GetCodepointHMetrics(&fontInfo, ch, &chars[i].advanceX, NULL);
chars[i].advanceX *= scaleFactor;
}
free(fontBuffer);
if (genFontChars) free(fontChars);
} }
else TraceLog(LOG_WARNING, "[%s] TTF file could not be opened", fileName);
free(fontBuffer);
if (genFontChars) free(fontChars);
return chars; return chars;
} }
@ -527,6 +526,7 @@ Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int paddi
else TraceLog(LOG_WARNING, "Character could not be packed: %i", i); else TraceLog(LOG_WARNING, "Character could not be packed: %i", i);
} }
free(rects);
free(nodes); free(nodes);
free(context); free(context);
} }
@ -563,6 +563,28 @@ void UnloadFont(Font font)
} }
} }
// Shows current FPS on top-left corner
// NOTE: Uses default font
void DrawFPS(int posX, int posY)
{
// NOTE: We are rendering fps every second for better viewing on high framerates
static int fps = 0;
static int counter = 0;
static int refreshRate = 20;
if (counter < refreshRate) counter++;
else
{
fps = GetFPS();
refreshRate = fps;
counter = 0;
}
// NOTE: We have rounding errors every frame, so it oscillates a lot
DrawText(FormatText("%2i FPS", fps), posX, posY, 20, LIME);
}
// Draw text (using default font) // Draw text (using default font)
// NOTE: fontSize work like in any drawing program but if fontSize is lower than font-base-size, then font-base-size is used // NOTE: fontSize work like in any drawing program but if fontSize is lower than font-base-size, then font-base-size is used
// NOTE: chars spacing is proportional to fontSize // NOTE: chars spacing is proportional to fontSize
@ -588,10 +610,10 @@ void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, f
int length = strlen(text); int length = strlen(text);
int textOffsetX = 0; // Offset between characters int textOffsetX = 0; // Offset between characters
int textOffsetY = 0; // Required for line break! int textOffsetY = 0; // Required for line break!
float scaleFactor; float scaleFactor = 0.0f;
unsigned char letter; // Current character unsigned char letter = 0; // Current character
int index; // Index position in sprite font int index = 0; // Index position in sprite font
scaleFactor = fontSize/font.baseSize; scaleFactor = fontSize/font.baseSize;
@ -639,44 +661,6 @@ void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, f
} }
} }
// Formatting of text with variables to 'embed'
const char *FormatText(const char *text, ...)
{
static char buffer[MAX_FORMATTEXT_LENGTH];
va_list args;
va_start(args, text);
vsprintf(buffer, text, args);
va_end(args);
return buffer;
}
// Get a piece of a text string
const char *SubText(const char *text, int position, int length)
{
static char buffer[MAX_SUBTEXT_LENGTH];
int textLength = strlen(text);
if (position >= textLength)
{
position = textLength - 1;
length = 0;
}
if (length >= textLength) length = textLength;
for (int c = 0 ; c < length ; c++)
{
*(buffer + c) = *(text + position);
text++;
}
*(buffer + length) = '\0';
return buffer;
}
// Measure string width for default font // Measure string width for default font
int MeasureText(const char *text, int fontSize) int MeasureText(const char *text, int fontSize)
{ {
@ -702,8 +686,8 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing
int tempLen = 0; // Used to count longer text line num chars int tempLen = 0; // Used to count longer text line num chars
int lenCounter = 0; int lenCounter = 0;
float textWidth = 0; float textWidth = 0.0f;
float tempTextWidth = 0; // Used to count longer text line width float tempTextWidth = 0.0f; // Used to count longer text line width
float textHeight = (float)font.baseSize; float textHeight = (float)font.baseSize;
float scaleFactor = fontSize/(float)font.baseSize; float scaleFactor = fontSize/(float)font.baseSize;
@ -761,26 +745,88 @@ int GetGlyphIndex(Font font, int character)
#endif #endif
} }
// Shows current FPS on top-left corner // Formatting of text with variables to 'embed'
// NOTE: Uses default font const char *FormatText(const char *text, ...)
void DrawFPS(int posX, int posY)
{ {
// NOTE: We are rendering fps every second for better viewing on high framerates static char buffer[MAX_FORMATTEXT_LENGTH];
static int fps = 0; va_list args;
static int counter = 0; va_start(args, text);
static int refreshRate = 20; vsprintf(buffer, text, args);
va_end(args);
if (counter < refreshRate) counter++; return buffer;
else }
// Get a piece of a text string
const char *SubText(const char *text, int position, int length)
{
static char buffer[MAX_SUBTEXT_LENGTH] = { 0 };
int textLength = strlen(text);
if (position >= textLength)
{ {
fps = GetFPS(); position = textLength - 1;
refreshRate = fps; length = 0;
counter = 0;
} }
// NOTE: We have rounding errors every frame, so it oscillates a lot if (length >= textLength) length = textLength;
DrawText(FormatText("%2i FPS", fps), posX, posY, 20, LIME);
for (int c = 0 ; c < length ; c++)
{
*(buffer + c) = *(text + position);
text++;
}
*(buffer + length) = '\0';
return buffer;
}
// Split string into multiple strings
// NOTE: Files count is returned by parameters pointer
// NOTE: Allocated memory should be manually freed
char **SplitText(char *text, char delimiter, int *strCount)
{
#define MAX_SUBSTRING_LENGTH 128
char **strings = NULL;
int len = strlen(text);
char *strDup = (char *)malloc(len + 1);
strcpy(strDup, text);
int counter = 1;
// Count how many substrings we have on string
for (int i = 0; i < len; i++) if (text[i] == delimiter) counter++;
// Memory allocation for substrings
strings = (char **)malloc(sizeof(char *)*counter);
for (int i = 0; i < counter; i++) strings[i] = (char *)malloc(sizeof(char)*MAX_SUBSTRING_LENGTH);
char *substrPtr = NULL;
char delimiters[1] = { delimiter }; // Only caring for one delimiter
substrPtr = strtok(strDup, delimiters);
for (int i = 0; (i < counter) && (substrPtr != NULL); i++)
{
strcpy(strings[i], substrPtr);
substrPtr = strtok(NULL, delimiters);
}
*strCount = counter;
free(strDup);
return strings;
}
// Check if two text string are equal
bool IsEqualText(const char *text1, const char *text2)
{
bool result = false;
if (strcmp(text1, text2) == 0) result = true;
return result;
} }
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
@ -911,17 +957,18 @@ static Font LoadBMFont(const char *fileName)
Font font = { 0 }; Font font = { 0 };
font.texture.id = 0; font.texture.id = 0;
char buffer[MAX_BUFFER_SIZE]; char buffer[MAX_BUFFER_SIZE] = { 0 };
char *searchPoint = NULL; char *searchPoint = NULL;
int fontSize = 0; int fontSize = 0;
int texWidth, texHeight; int texWidth = 0;
int texHeight = 0;
char texFileName[129]; char texFileName[129];
int charsCount = 0; int charsCount = 0;
int base; // Useless data int base = 0; // Useless data
FILE *fntFile; FILE *fntFile = NULL;
fntFile = fopen(fileName, "rt"); fntFile = fopen(fileName, "rt");

View file

@ -60,7 +60,7 @@
#include "raylib.h" // Declares module functions #include "raylib.h" // Declares module functions
#include <stdlib.h> // Required for: malloc(), free() #include <stdlib.h> // Required for: malloc(), free()
#include <string.h> // Required for: strcmp(), strrchr(), strncmp() #include <string.h> // Required for: strlen()
#include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 3.3 or ES2 #include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 3.3 or ES2
// Required for: rlLoadTexture() rlDeleteTextures(), // Required for: rlLoadTexture() rlDeleteTextures(),
@ -147,8 +147,8 @@ static Image LoadDDS(const char *fileName); // Load DDS file
static Image LoadPKM(const char *fileName); // Load PKM file static Image LoadPKM(const char *fileName); // Load PKM file
#endif #endif
#if defined(SUPPORT_FILEFORMAT_KTX) #if defined(SUPPORT_FILEFORMAT_KTX)
static Image LoadKTX(const char *fileName); // Load KTX file static Image LoadKTX(const char *fileName); // Load KTX file
static void SaveKTX(Image image, const char *fileName); // Save image data as KTX file static int SaveKTX(Image image, const char *fileName); // Save image data as KTX file
#endif #endif
#if defined(SUPPORT_FILEFORMAT_PVR) #if defined(SUPPORT_FILEFORMAT_PVR)
static Image LoadPVR(const char *fileName); // Load PVR file static Image LoadPVR(const char *fileName); // Load PVR file
@ -323,7 +323,7 @@ Image LoadImageRaw(const char *fileName, int width, int height, int format, int
// NOTE: fread() returns num read elements instead of bytes, // NOTE: fread() returns num read elements instead of bytes,
// to get bytes we need to read (1 byte size, elements) instead of (x byte size, 1 element) // to get bytes we need to read (1 byte size, elements) instead of (x byte size, 1 element)
size_t bytes = fread(image.data, 1, size, rawFile); int bytes = fread(image.data, 1, size, rawFile);
// Check if data has been read successfully // Check if data has been read successfully
if (bytes < size) if (bytes < size)
@ -728,16 +728,15 @@ void ExportImage(Image image, const char *fileName)
else if (IsFileExtension(fileName, ".bmp")) success = stbi_write_bmp(fileName, image.width, image.height, 4, imgData); else if (IsFileExtension(fileName, ".bmp")) success = stbi_write_bmp(fileName, image.width, image.height, 4, imgData);
else if (IsFileExtension(fileName, ".tga")) success = stbi_write_tga(fileName, image.width, image.height, 4, imgData); else if (IsFileExtension(fileName, ".tga")) success = stbi_write_tga(fileName, image.width, image.height, 4, imgData);
else if (IsFileExtension(fileName, ".jpg")) success = stbi_write_jpg(fileName, image.width, image.height, 4, imgData, 80); // JPG quality: between 1 and 100 else if (IsFileExtension(fileName, ".jpg")) success = stbi_write_jpg(fileName, image.width, image.height, 4, imgData, 80); // JPG quality: between 1 and 100
else if (IsFileExtension(fileName, ".ktx")) SaveKTX(image, fileName); else if (IsFileExtension(fileName, ".ktx")) success = SaveKTX(image, fileName);
else if (IsFileExtension(fileName, ".raw")) else if (IsFileExtension(fileName, ".raw"))
{ {
// Export raw pixel data (without header) // Export raw pixel data (without header)
// NOTE: It's up to the user to track image parameters // NOTE: It's up to the user to track image parameters
FILE *rawFile = fopen(fileName, "wb"); FILE *rawFile = fopen(fileName, "wb");
fwrite(image.data, GetPixelDataSize(image.width, image.height, image.format), 1, rawFile); success = fwrite(image.data, GetPixelDataSize(image.width, image.height, image.format), 1, rawFile);
fclose(rawFile); fclose(rawFile);
} }
else if (IsFileExtension(fileName, ".h")) { } // TODO: Export pixel data as an array of bytes
if (success != 0) TraceLog(LOG_INFO, "Image exported successfully: %s", fileName); if (success != 0) TraceLog(LOG_INFO, "Image exported successfully: %s", fileName);
else TraceLog(LOG_WARNING, "Image could not be exported."); else TraceLog(LOG_WARNING, "Image could not be exported.");
@ -745,6 +744,44 @@ void ExportImage(Image image, const char *fileName)
free(imgData); free(imgData);
} }
// Export image as code file (.h) defining an array of bytes
void ExportImageAsCode(Image image, const char *fileName)
{
#define BYTES_TEXT_PER_LINE 20
char varFileName[256] = { 0 };
int dataSize = GetPixelDataSize(image.width, image.height, image.format);
FILE *txtFile = fopen(fileName, "wt");
fprintf(txtFile, "\n//////////////////////////////////////////////////////////////////////////////////////\n");
fprintf(txtFile, "// //\n");
fprintf(txtFile, "// ImageAsCode exporter v1.0 - Image pixel data exported as an array of bytes //\n");
fprintf(txtFile, "// //\n");
fprintf(txtFile, "// more info and bugs-report: github.com/raysan5/raylib //\n");
fprintf(txtFile, "// feedback and support: ray[at]raylib.com //\n");
fprintf(txtFile, "// //\n");
fprintf(txtFile, "// Copyright (c) 2018 Ramon Santamaria (@raysan5) //\n");
fprintf(txtFile, "// //\n");
fprintf(txtFile, "////////////////////////////////////////////////////////////////////////////////////////\n\n");
// Get file name from path and convert variable name to uppercase
strcpy(varFileName, GetFileNameWithoutExt(fileName));
for (int i = 0; varFileName[i] != '\0'; i++) if (varFileName[i] >= 'a' && varFileName[i] <= 'z') { varFileName[i] = varFileName[i] - 32; }
// Add image information
fprintf(txtFile, "// Image data information\n");
fprintf(txtFile, "#define %s_WIDTH %i\n", varFileName, image.width);
fprintf(txtFile, "#define %s_HEIGHT %i\n", varFileName, image.height);
fprintf(txtFile, "#define %s_FORMAT %i // raylib internal pixel format\n\n", varFileName, image.format);
fprintf(txtFile, "static unsigned char %s_DATA[%i] = { ", varFileName, dataSize);
for (int i = 0; i < dataSize - 1; i++) fprintf(txtFile, ((i%BYTES_TEXT_PER_LINE == 0) ? "0x%x,\n" : "0x%x, "), ((unsigned char *)image.data)[i]);
fprintf(txtFile, "0x%x };\n", ((unsigned char *)image.data)[dataSize - 1]);
fclose(txtFile);
}
// Copy an image to a new image // Copy an image to a new image
Image ImageCopy(Image image) Image ImageCopy(Image image)
{ {
@ -1445,6 +1482,57 @@ void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp)
} }
} }
// Extract color palette from image to maximum size
// NOTE: Memory allocated should be freed manually!
Color *ImageExtractPalette(Image image, int maxPaletteSize, int *extractCount)
{
#define COLOR_EQUAL(col1, col2) ((col1.r == col2.r)&&(col1.g == col2.g)&&(col1.b == col2.b)&&(col1.a == col2.a))
Color *pixels = GetImageData(image);
Color *palette = (Color *)malloc(maxPaletteSize*sizeof(Color));
int palCount = 0;
for (int i = 0; i < maxPaletteSize; i++) palette[i] = BLANK; // Set all colors to BLANK
for (int i = 0; i < image.width*image.height; i++)
{
if (pixels[i].a > 0)
{
bool colorInPalette = false;
// Check if the color is already on palette
for (int j = 0; j < maxPaletteSize; j++)
{
if (COLOR_EQUAL(pixels[i], palette[j]))
{
colorInPalette = true;
break;
}
}
// Store color if not on the palette
if (!colorInPalette)
{
palette[palCount] = pixels[i]; // Add pixels[i] to palette
palCount++;
// We reached the limit of colors supported by palette
if (palCount >= maxPaletteSize)
{
i = image.width*image.height; // Finish palette get
printf("WARNING: Image palette is greater than %i colors!\n", maxPaletteSize);
}
}
}
}
free(pixels);
*extractCount = palCount;
return palette;
}
// Draw an image (source) within an image (destination) // Draw an image (source) within an image (destination)
// TODO: Feel this function could be simplified... // TODO: Feel this function could be simplified...
void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec) void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec)
@ -1643,17 +1731,22 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co
} }
// Draw rectangle within an image // Draw rectangle within an image
void ImageDrawRectangle(Image *dst, Vector2 position, Rectangle rec, Color color) void ImageDrawRectangle(Image *dst, Rectangle rec, Color color)
{ {
Image imRec = GenImageColor((int)rec.width, (int)rec.height, color); Image imRec = GenImageColor((int)rec.width, (int)rec.height, color);
ImageDraw(dst, imRec, (Rectangle){ 0, 0, rec.width, rec.height }, rec);
Rectangle dstRec = { position.x, position.y, (float)imRec.width, (float)imRec.height };
ImageDraw(dst, imRec, rec, dstRec);
UnloadImage(imRec); UnloadImage(imRec);
} }
// Draw rectangle lines within an image
void ImageDrawRectangleLines(Image *dst, Rectangle rec, int thick, Color color)
{
ImageDrawRectangle(dst, (Rectangle){ rec.x, rec.y, rec.width, thick }, color);
ImageDrawRectangle(dst, (Rectangle){ rec.x, rec.y + thick, thick, rec.height - thick*2 }, color);
ImageDrawRectangle(dst, (Rectangle){ rec.x + rec.width - thick, rec.y + thick, thick, rec.height - thick*2 }, color);
ImageDrawRectangle(dst, (Rectangle){ rec.x, rec.height - thick, rec.width, thick }, color);
}
// Draw text (default font) within an image (destination) // Draw text (default font) within an image (destination)
void ImageDrawText(Image *dst, Vector2 position, const char *text, int fontSize, Color color) void ImageDrawText(Image *dst, Vector2 position, const char *text, int fontSize, Color color)
{ {
@ -2618,11 +2711,11 @@ static Image LoadDDS(const char *fileName)
else else
{ {
// Verify the type of file // Verify the type of file
char filecode[4]; char ddsHeaderId[4];
fread(filecode, 4, 1, ddsFile); fread(ddsHeaderId, 4, 1, ddsFile);
if (strncmp(filecode, "DDS ", 4) != 0) if ((ddsHeaderId[0] != 'D') || (ddsHeaderId[1] != 'D') || (ddsHeaderId[2] != 'S') || (ddsHeaderId[3] != ' '))
{ {
TraceLog(LOG_WARNING, "[%s] DDS file does not seem to be a valid image", fileName); TraceLog(LOG_WARNING, "[%s] DDS file does not seem to be a valid image", fileName);
} }
@ -2729,7 +2822,7 @@ static Image LoadDDS(const char *fileName)
TraceLog(LOG_DEBUG, "Pitch or linear size: %i", ddsHeader.pitchOrLinearSize); TraceLog(LOG_DEBUG, "Pitch or linear size: %i", ddsHeader.pitchOrLinearSize);
image.data = (unsigned char*)malloc(size*sizeof(unsigned char)); image.data = (unsigned char *)malloc(size*sizeof(unsigned char));
fread(image.data, size, 1, ddsFile); fread(image.data, size, 1, ddsFile);
@ -2802,7 +2895,7 @@ static Image LoadPKM(const char *fileName)
// Get the image header // Get the image header
fread(&pkmHeader, sizeof(PKMHeader), 1, pkmFile); fread(&pkmHeader, sizeof(PKMHeader), 1, pkmFile);
if (strncmp(pkmHeader.id, "PKM ", 4) != 0) if ((pkmHeader.id[0] != 'P') || (pkmHeader.id[1] != 'K') || (pkmHeader.id[2] != 'M') || (pkmHeader.id[3] != ' '))
{ {
TraceLog(LOG_WARNING, "[%s] PKM file does not seem to be a valid image", fileName); TraceLog(LOG_WARNING, "[%s] PKM file does not seem to be a valid image", fileName);
} }
@ -2826,7 +2919,7 @@ static Image LoadPKM(const char *fileName)
int size = image.width*image.height*bpp/8; // Total data size in bytes int size = image.width*image.height*bpp/8; // Total data size in bytes
image.data = (unsigned char*)malloc(size*sizeof(unsigned char)); image.data = (unsigned char *)malloc(size*sizeof(unsigned char));
fread(image.data, size, 1, pkmFile); fread(image.data, size, 1, pkmFile);
@ -2920,7 +3013,7 @@ static Image LoadKTX(const char *fileName)
int dataSize; int dataSize;
fread(&dataSize, sizeof(unsigned int), 1, ktxFile); fread(&dataSize, sizeof(unsigned int), 1, ktxFile);
image.data = (unsigned char*)malloc(dataSize*sizeof(unsigned char)); image.data = (unsigned char *)malloc(dataSize*sizeof(unsigned char));
fread(image.data, dataSize, 1, ktxFile); fread(image.data, dataSize, 1, ktxFile);
@ -2937,8 +3030,10 @@ static Image LoadKTX(const char *fileName)
// Save image data as KTX file // Save image data as KTX file
// NOTE: By default KTX 1.1 spec is used, 2.0 is still on draft (01Oct2018) // NOTE: By default KTX 1.1 spec is used, 2.0 is still on draft (01Oct2018)
static void SaveKTX(Image image, const char *fileName) static int SaveKTX(Image image, const char *fileName)
{ {
int success = 0;
// KTX file Header (64 bytes) // KTX file Header (64 bytes)
// v1.1 - https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/ // v1.1 - https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
// v2.0 - http://github.khronos.org/KTX-Specification/ - still on draft, not ready for implementation // v2.0 - http://github.khronos.org/KTX-Specification/ - still on draft, not ready for implementation
@ -2999,7 +3094,7 @@ static void SaveKTX(Image image, const char *fileName)
if (ktxHeader.glFormat == -1) TraceLog(LOG_WARNING, "Image format not supported for KTX export."); if (ktxHeader.glFormat == -1) TraceLog(LOG_WARNING, "Image format not supported for KTX export.");
else else
{ {
fwrite(&ktxHeader, 1, sizeof(KTXHeader), ktxFile); success = fwrite(&ktxHeader, sizeof(KTXHeader), 1, ktxFile);
int width = image.width; int width = image.width;
int height = image.height; int height = image.height;
@ -3009,8 +3104,8 @@ static void SaveKTX(Image image, const char *fileName)
for (int i = 0; i < image.mipmaps; i++) for (int i = 0; i < image.mipmaps; i++)
{ {
unsigned int dataSize = GetPixelDataSize(width, height, image.format); unsigned int dataSize = GetPixelDataSize(width, height, image.format);
fwrite(&dataSize, 1, sizeof(unsigned int), ktxFile); success = fwrite(&dataSize, sizeof(unsigned int), 1, ktxFile);
fwrite((unsigned char *)image.data + dataOffset, 1, dataSize, ktxFile); success = fwrite((unsigned char *)image.data + dataOffset, dataSize, 1, ktxFile);
width /= 2; width /= 2;
height /= 2; height /= 2;
@ -3020,6 +3115,9 @@ static void SaveKTX(Image image, const char *fileName)
fclose(ktxFile); // Close file pointer fclose(ktxFile); // Close file pointer
} }
// If all data has been written correctly to file, success = 1
return success;
} }
#endif #endif
@ -3162,7 +3260,7 @@ static Image LoadPVR(const char *fileName)
} }
int dataSize = image.width*image.height*bpp/8; // Total data size in bytes int dataSize = image.width*image.height*bpp/8; // Total data size in bytes
image.data = (unsigned char*)malloc(dataSize*sizeof(unsigned char)); image.data = (unsigned char *)malloc(dataSize*sizeof(unsigned char));
// Read data from file // Read data from file
fread(image.data, dataSize, 1, pvrFile); fread(image.data, dataSize, 1, pvrFile);

View file

@ -308,12 +308,20 @@ func ImageDraw(dst, src *Image, srcRec, dstRec Rectangle) {
} }
// ImageDrawRectangle - Draw rectangle within an image // ImageDrawRectangle - Draw rectangle within an image
func ImageDrawRectangle(dst *Image, position Vector2, rec Rectangle, color Color) { func ImageDrawRectangle(dst *Image, rec Rectangle, color Color) {
cdst := dst.cptr() cdst := dst.cptr()
cposition := position.cptr()
crec := rec.cptr() crec := rec.cptr()
ccolor := color.cptr() ccolor := color.cptr()
C.ImageDrawRectangle(cdst, *cposition, *crec, *ccolor) C.ImageDrawRectangle(cdst, *crec, *ccolor)
}
// ImageDrawRectangleLines - Draw rectangle lines within an image
func ImageDrawRectangleLines(dst *Image, rec Rectangle, thick int, color Color) {
cdst := dst.cptr()
crec := rec.cptr()
cthick := (C.int)(thick)
ccolor := color.cptr()
C.ImageDrawRectangleLines(cdst, *crec, cthick, *ccolor)
} }
// ImageDrawText - Draw text (default font) within an image (destination) // ImageDrawText - Draw text (default font) within an image (destination)