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)
}
// 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
func PlaySound(sound Sound) {
csound := sound.cptr()

View file

@ -6,6 +6,6 @@ package rl
#include "external/android/native_app_glue/android_native_app_glue.c"
#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"

View file

@ -21,7 +21,7 @@ package rl
#include "external/glfw/src/osmesa_context.c"
#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,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/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 -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 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"

View file

@ -44,6 +44,8 @@
#define SUPPORT_MOUSE_GESTURES 1
// Use busy wait loop for timing sync, if not defined, a high-resolution timer is setup and used
//#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()
#define SUPPORT_SCREEN_CAPTURE 1
// 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).
*/
#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.
*
@ -3085,6 +3091,11 @@ GLFWAPI void glfwMaximizeWindow(GLFWwindow* window);
* hidden. If the window is already visible or is in full screen mode, this
* 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.
*
* @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
* 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
* you are certain that is what the user wants. Focus stealing can be
* extremely disruptive.
@ -3306,8 +3321,9 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* window, int attrib);
*
* The supported attributes are [GLFW_DECORATED](@ref GLFW_DECORATED_attrib),
* [GLFW_RESIZABLE](@ref GLFW_RESIZABLE_attrib),
* [GLFW_FLOATING](@ref GLFW_FLOATING_attrib) and
* [GLFW_AUTO_ICONIFY](@ref GLFW_AUTO_ICONIFY_attrib).
* [GLFW_FLOATING](@ref GLFW_FLOATING_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
* value will take effect if the window is later made windowed.

View file

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

View file

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

View file

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

View file

@ -242,7 +242,9 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)(
#include "egl_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_dlclose(handle) FreeLibrary((HMODULE) handle)

View file

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

View file

@ -2672,8 +2672,9 @@ void _glfwPlatformPollEvents(void)
#if defined(__linux__)
_glfwDetectJoystickConnectionLinux();
#endif
int count = XPending(_glfw.x11.display);
while (count--)
XPending(_glfw.x11.display);
while (XQLength(_glfw.x11.display))
{
XEvent 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.
// mini_al - v0.8.8 - 2018-09-14
// mini_al - v0.8.10 - 2018-10-21
//
// 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_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_channel_map;
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_set_write_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);
*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);
return MAL_TRUE;
@ -4917,11 +4923,11 @@ mal_uint32 mal_device__wait_for_frames__null(mal_device* pDevice)
while (!pDevice->null_device.breakFromMainLoop) {
mal_uint32 framesAvailable = mal_device__get_available_frames__null(pDevice);
if (framesAvailable > 0) {
if (framesAvailable >= (pDevice->bufferSizeInFrames/pDevice->periods)) {
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.
@ -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_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 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 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);
@ -12305,6 +12312,10 @@ void mal_pulse_device_write_callback(mal_pa_stream* pStream, size_t sizeInBytes,
mal_context* pContext = pDevice->pContext;
mal_assert(pContext != NULL);
#ifdef MAL_DEBUG_OUTPUT
printf("[PulseAudio] write_callback: sizeInBytes=%d\n", (int)sizeInBytes);
#endif
size_t bytesRemaining = sizeInBytes;
while (bytesRemaining > 0) {
size_t bytesToReadFromClient = bytesRemaining;
@ -12319,19 +12330,35 @@ void mal_pulse_device_write_callback(mal_pa_stream* pStream, size_t sizeInBytes,
return;
}
#ifdef MAL_DEBUG_OUTPUT
printf(" bytesToReadFromClient=%d", (int)bytesToReadFromClient);
#endif
if (pBuffer != NULL && bytesToReadFromClient > 0) {
mal_uint32 framesToReadFromClient = (mal_uint32)bytesToReadFromClient / (pDevice->internalChannels*mal_get_bytes_per_sample(pDevice->internalFormat));
if (framesToReadFromClient > 0) {
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);
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));
return;
}
} else {
#ifdef MAL_DEBUG_OUTPUT
printf(", framesToReadFromClient=0\n");
#endif
}
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_channel_map cmap;
mal_pa_buffer_attr attr;
mal_pa_stream_flags_t streamFlags;
const mal_pa_sample_spec* pActualSS = 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);
}
#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) {
ss = sinkInfo.sample_spec;
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;
cmap = sourceInfo.channel_map;
}
#endif
// Buffer size.
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.tlength = attr.maxlength / pConfig->periods;
attr.tlength = attr.maxlength;
attr.prebuf = (mal_uint32)-1;
attr.minreq = attr.tlength;
attr.fragsize = attr.tlength;
attr.minreq = attr.maxlength / pConfig->periods;
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];
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) {
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 {
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) {
@ -12652,6 +12641,21 @@ mal_result mal_device_init__pulse(mal_context* pContext, mal_device_type type, c
// Internal format.
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 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;
}
@ -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->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.
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_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_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_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");
@ -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_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_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_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;
@ -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_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_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_set_write_callback = (mal_proc)_pa_stream_set_write_callback;
pContext->pulse.pa_stream_set_read_callback = (mal_proc)_pa_stream_set_read_callback;
@ -14900,27 +14911,62 @@ OSStatus mal_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFlags* p
#if defined(MAL_DEBUG_OUTPUT)
printf("INFO: Output Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", busNumber, frameCount, pBufferList->mNumberBuffers);
#endif
// 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
// 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 (pBufferList->mBuffers[0].mNumberChannels != pDevice->internalChannels) {
layout = mal_stream_layout_deinterleaved;
}
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);
mal_uint32 framesRemaining = frameCountPerBuffer;
while (framesRemaining > 0) {
mal_uint32 framesToRead = sizeof(tempBuffer) / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels);
if (framesToRead > framesRemaining) {
framesToRead = framesRemaining;
}
mal_device__read_frames_from_client(pDevice, framesToRead, tempBuffer);
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;
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)
printf("INFO: Input Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", busNumber, frameCount, pRenderedBufferList->mNumberBuffers);
#endif
@ -14953,16 +15006,58 @@ OSStatus mal_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pA
return status;
}
// For now we can assume everything is interleaved.
for (UInt32 iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) {
if (pRenderedBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->internalChannels) {
mal_device__send_frames_to_client(pDevice, frameCount, pRenderedBufferList->mBuffers[iBuffer].mData);
#if defined(MAL_DEBUG_OUTPUT)
printf(" mDataByteSize=%d\n", pRenderedBufferList->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.
if (layout == mal_stream_layout_interleaved) {
for (UInt32 iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) {
if (pRenderedBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->internalChannels) {
mal_device__send_frames_to_client(pDevice, frameCount, pRenderedBufferList->mBuffers[iBuffer].mData);
#if defined(MAL_DEBUG_OUTPUT)
printf(" mDataByteSize=%d\n", pRenderedBufferList->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.
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_assert(pDevice != NULL);
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.
// There's been a report of a deadlock here when triggered by mal_device_uninit(). It looks like
// AudioUnitGetProprty (called below) and AudioComponentInstanceDispose (called in mal_device_uninit)
// can try waiting on the same lock. I'm going to try working around this by not calling any Core
// Audio APIs in the callback when the device has been stopped or uninitialized.
if (mal_device__get_state(pDevice) == MAL_STATE_UNINITIALIZED || mal_device__get_state(pDevice) == MAL_STATE_STOPPING) {
mal_stop_proc onStop = pDevice->onStop;
if (onStop) {
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) {
return result;
}
pData->bufferSizeInFramesOut = actualBufferSizeInFrames * pData->periodsOut;
#else
actualBufferSizeInFrames = 4096;
pData->bufferSizeInFramesOut = actualBufferSizeInFrames;
#endif
pData->bufferSizeInFramesOut = actualBufferSizeInFrames * pData->periodsOut;
// During testing I discovered that the buffer size can be too big. You'll get an error like this:
//
@ -15556,6 +15665,15 @@ mal_result mal_context_uninit__coreaudio(mal_context* pContext)
mal_result mal_context_init__coreaudio(mal_context* pContext)
{
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)
pContext->coreaudio.hCoreFoundation = mal_dlopen("CoreFoundation.framework/CoreFoundation");
@ -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
// to explicitly clamp this because it will be easy to overflow.
mal_uint32 bufferSize = pConfig->bufferSizeInFrames;
mal_uint32 bufferSize = pDevice->bufferSizeInFrames;
if (bufferSize > 32768) {
bufferSize = 32768;
} 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);
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
#endif
@ -19778,10 +19896,12 @@ mal_result mal_device_init__sdl(mal_context* pContext, mal_device_type type, con
desiredSpec.format = MAL_AUDIO_S16;
}
pDevice->sdl.deviceID = ((MAL_PFN_SDL_OpenAudio)pDevice->pContext->sdl.SDL_OpenAudio)(&desiredSpec, &obtainedSpec);
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);
int deviceID = ((MAL_PFN_SDL_OpenAudio)pDevice->pContext->sdl.SDL_OpenAudio)(&desiredSpec, &obtainedSpec);
if (deviceID < 0) {
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);
@ -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);
}
#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;
return result;
}
@ -20767,7 +20895,7 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi
#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(" Channels: %d -> %d\n", pDevice->channels, pDevice->internalChannels);
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.
if (mal_context_is_backend_asynchronous(pDevice->pContext)) {
pDevice->pContext->onDeviceStop(pDevice);
result = pDevice->pContext->onDeviceStop(pDevice);
} else {
// 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
@ -28296,6 +28510,13 @@ mal_uint64 mal_sine_wave_read_ex(mal_sine_wave* pSineWave, mal_uint64 frameCount
// 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
// - Fix Linux build with the ALSA backend.
// - Minor documentation fix.

View file

@ -193,7 +193,7 @@
#undef __forceinline
#endif
#define __forceinline
//#define alloca __builtin_alloca
#define alloca __builtin_alloca
#elif !defined(_MSC_VER)
#if __GNUC__
#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
// it's own translation unit. Not doing this will cause conflicts with CloseWindow(), etc.
#define MAL_IMPLEMENTATION
#include "mini_al.h"
#include "external/mini_al.h"

View file

@ -223,7 +223,7 @@
#define MOUSE_MIDDLE_BUTTON 2
// Touch points registered
#define MAX_TOUCH_POINTS 2
#define MAX_TOUCH_POINTS 10
// Gamepad Number
#define GAMEPAD_PLAYER1 0
@ -311,7 +311,7 @@
// NOTE: MSC C++ compiler does not support compound literals (C99 feature)
// Plain structures in C++ (without constructors) can be initialized from { } initializers.
#ifdef __cplusplus
#if defined(__cplusplus)
#define CLITERAL
#else
#define CLITERAL (Color)
@ -511,7 +511,7 @@ typedef struct Mesh {
float *tangents; // Vertex tangents (XYZW - 4 components per vertex) (shader-location = 4)
unsigned char *colors; // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3)
unsigned short *indices;// Vertex indices (in case vertex data comes indexed)
// Animation vertex data
float *baseVertices; // Vertex base position (required to apply bones transformations)
float *baseNormals; // Vertex base normals (required to apply bones transformations)
@ -786,7 +786,7 @@ typedef enum {
// Callbacks to be implemented by users
typedef void (*TraceLogCallback)(int msgType, const char *text, va_list args);
#ifdef __cplusplus
#if defined(__cplusplus)
extern "C" { // Prevents name mangling of functions
#endif
@ -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)
// Files management functions
RLAPI bool FileExists(const char *fileName); // Check if file exists
RLAPI bool IsFileExtension(const char *fileName, const char *ext);// Check file extension
RLAPI const char *GetExtension(const char *fileName); // Get pointer to extension for a filename string
RLAPI const char *GetFileName(const char *filePath); // Get pointer to filename for a path string
RLAPI const char *GetFileNameWithoutExt(const char *filePath); // Get filename string without extension (memory should be freed)
RLAPI const char *GetDirectoryPath(const char *fileName); // Get full path for a given fileName (uses static string)
RLAPI const char *GetWorkingDirectory(void); // Get current working directory (uses static string)
RLAPI char **GetDirectoryFiles(const char *dirPath, int *count); // Get filenames in a directory path (memory should be freed)
@ -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 char **GetDroppedFiles(int *count); // Get dropped files names (memory should be freed)
RLAPI void ClearDroppedFiles(void); // Clear dropped files paths buffer (free memory)
RLAPI long GetFileModTime(const char *fileName); // Get file modification time (last write time)
// Persistent storage management
RLAPI void StorageSaveValue(int position, int value); // Save integer value to storage file (to defined position)
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)
//------------------------------------------------------------------------------------
@ -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 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 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 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
@ -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 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
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
@ -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 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 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 LoadTextureFromImage(Image image); // Load texture from image data
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 ImageAlphaPremultiply(Image *image); // Premultiply alpha channel
RLAPI void ImageCrop(Image *image, Rectangle crop); // Crop an image to a defined rectangle
RLAPI void ImageResize(Image *image, int newWidth, int newHeight); // Resize image (bilinear filtering)
RLAPI void ImageResize(Image *image, int newWidth, int newHeight); // Resize image (Bicubic scaling algorithm)
RLAPI void ImageResizeNN(Image *image, int newWidth,int newHeight); // Resize image (Nearest-Neighbor scaling algorithm)
RLAPI void ImageResizeCanvas(Image *image, int newWidth, int newHeight, int offsetX, int offsetY, Color color); // Resize canvas and fill with color
RLAPI void ImageMipmaps(Image *image); // Generate all mipmap levels for a provided image
RLAPI void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp); // Dither image data to 16bpp or lower (Floyd-Steinberg dithering)
RLAPI Color *ImageExtractPalette(Image image, int maxPaletteSize, int *extractCount); // Extract color palette from image to maximum size (memory should be freed)
RLAPI Image ImageText(const char *text, int fontSize, Color color); // Create an image from text (default font)
RLAPI Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Color tint); // Create an image from text (custom sprite font)
RLAPI void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec); // Draw a source image within a destination image
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 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
@ -1073,21 +1083,25 @@ RLAPI Font GetFontDefault(void);
RLAPI Font LoadFont(const char *fileName); // Load font from file into GPU memory (VRAM)
RLAPI Font LoadFontEx(const char *fileName, int fontSize, int charsCount, int *fontChars); // Load font from file with extended parameters
RLAPI CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int charsCount, int type); // Load font data for further use
RLAPI Image GenImageFontAtlas(CharInfo *chars, int fontSize, int charsCount, int padding, int packMethod); // Generate image font atlas using chars info
RLAPI Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int padding, int packMethod); // Generate image font atlas using chars info
RLAPI void UnloadFont(Font font); // Unload Font from GPU memory (VRAM)
// Text drawing functions
RLAPI void DrawFPS(int posX, int posY); // Shows current FPS
RLAPI void DrawText(const char *text, int posX, int posY, int fontSize, Color color); // Draw text (using default font)
RLAPI void DrawTextEx(Font font, const char* text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text using font and additional parameters
RLAPI void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text using font and additional parameters
// Text misc. functions
RLAPI int MeasureText(const char *text, int fontSize); // Measure string width for default font
RLAPI Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing); // Measure string size for Font
RLAPI const char *FormatText(const char *text, ...); // Formatting of text with variables to 'embed'
RLAPI const char *SubText(const char *text, int position, int length); // Get a piece of a text string
RLAPI int GetGlyphIndex(Font font, int character); // Get index position for a unicode character on font
// Text string edition functions
RLAPI const char *FormatText(const char *text, ...); // Formatting of text with variables to 'embed'
RLAPI const char *SubText(const char *text, int position, int length); // Get a piece of a text string
RLAPI char **SplitText(char *text, char delimiter, int *strCount); // Split text string into multiple strings (memory should be freed manually!)
RLAPI bool IsEqualText(const char *text1, const char *text2); // Check if two text string are equal
//------------------------------------------------------------------------------------
// 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 UnloadSound(Sound sound); // Unload sound
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
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 SetAudioStreamPitch(AudioStream stream, float pitch); // Set pitch for audio stream (1.0 is base level)
#ifdef __cplusplus
#if defined(__cplusplus)
}
#endif

View file

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

View file

@ -199,7 +199,7 @@ typedef unsigned char byte;
float *tangents; // vertex tangents (XYZW - 4 components per vertex) (shader-location = 4)
unsigned char *colors; // vertex colors (RGBA - 4 components per vertex) (shader-location = 3)
unsigned short *indices;// vertex indices (in case vertex data comes indexed)
// Animation vertex data
float *baseVertices; // Vertex base position (required to apply bones transformations)
float *baseNormals; // Vertex base normals (required to apply bones transformations)
@ -378,7 +378,7 @@ typedef unsigned char byte;
} VrDevice;
#endif
#ifdef __cplusplus
#if defined(__cplusplus)
extern "C" { // Prevents name mangling of functions
#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)
#endif
#ifdef __cplusplus
#if defined(__cplusplus)
}
#endif
@ -553,7 +553,7 @@ int GetPixelDataSize(int width, int height, int format);// Get pixel data size i
#else
// APIENTRY for OpenGL function pointer declarations is required
#ifndef APIENTRY
#ifdef _WIN32
#if defined(_WIN32)
#define APIENTRY __stdcall
#else
#define APIENTRY
@ -729,7 +729,7 @@ typedef struct VrStereoConfig {
//----------------------------------------------------------------------------------
#if !defined(GRAPHICS_API_OPENGL_11) && defined(SUPPORT_DISTORTION_SHADER)
// Distortion shader embedded
static char distortionFShaderStr[] =
static char distortionFShaderStr[] =
#if defined(GRAPHICS_API_OPENGL_21)
"#version 120 \n"
#elif defined(GRAPHICS_API_OPENGL_ES2)
@ -1217,9 +1217,9 @@ void rlEnd(void)
// WARNING: If we are between rlPushMatrix() and rlPopMatrix() and we need to force a rlglDraw(),
// we need to call rlPopMatrix() before to recover *currentMatrix (modelview) for the next forced draw call!
// Also noted that if we had multiple matrix pushed, it will require "stackCounter" pops before launching the draw
// TODO: Undoubtely, current rlPushMatrix/rlPopMatrix should be redesigned... or removed... it's not working properly
rlPopMatrix();
rlglDraw();
}
@ -1506,9 +1506,17 @@ void rlDeleteTextures(unsigned int id)
void rlDeleteRenderTextures(RenderTexture2D target)
{
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
if (target.id > 0) glDeleteFramebuffers(1, &target.id);
if (target.texture.id > 0) glDeleteTextures(1, &target.texture.id);
if (target.depth.id > 0) glDeleteTextures(1, &target.depth.id);
if (target.depth.id > 0)
{
#if defined(GRAPHICS_API_OPENGL_21) || defined(GRAPHICS_API_OPENGL_ES2)
glDeleteRenderbuffers(1, &target.depth.id);
#elif defined(GRAPHICS_API_OPENGL_33)
glDeleteTextures(1, &target.depth.id);
#endif
}
if (target.id > 0) glDeleteFramebuffers(1, &target.id);
TraceLog(LOG_INFO, "[FBO ID %i] Unloaded render texture data from VRAM (GPU)", target.id);
#endif
@ -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)
glGetIntegerv(GL_NUM_EXTENSIONS, &numExt);
#ifdef _MSC_VER
#if defined(_MSC_VER)
const char **extList = malloc(sizeof(const char *)*numExt);
#else
const char *extList[numExt];
@ -1631,17 +1639,13 @@ void rlglInit(int width, int height)
#elif defined(GRAPHICS_API_OPENGL_ES2)
char *extensions = (char *)glGetString(GL_EXTENSIONS); // One big const string
// NOTE: We have to duplicate string because glGetString() returns a const value
// If not duplicated, it fails in some systems (Raspberry Pi)
// Equivalent to function: char *strdup(const char *str)
char *extensionsDup;
size_t len = strlen(extensions) + 1;
void *newstr = malloc(len);
if (newstr == NULL) extensionsDup = NULL;
extensionsDup = (char *)memcpy(newstr, extensions, len);
// NOTE: We have to duplicate string because glGetString() returns a const string
int len = strlen(extensions) + 1;
char *extensionsDup = (char *)malloc(len);
strcpy(extensionsDup, extensions);
// NOTE: String could be splitted using strtok() function (string.h)
// NOTE: strtok() modifies the received string, it can not be const
// NOTE: strtok() modifies the passed string, it can not be const
char *extList[512]; // Allocate 512 strings pointers (2 KB)
@ -2175,7 +2179,7 @@ void rlUnloadTexture(unsigned int id)
// Load a texture to be used for rendering (fbo with color and depth attachments)
RenderTexture2D rlLoadRenderTexture(int width, int height)
{
RenderTexture2D target;
RenderTexture2D target = { 0 };
target.id = 0;
@ -2255,8 +2259,16 @@ RenderTexture2D rlLoadRenderTexture(int width, int height)
default: break;
}
glDeleteTextures(1, &target.texture.id);
glDeleteTextures(1, &target.depth.id);
if (target.texture.id > 0) glDeleteTextures(1, &target.texture.id);
if (target.depth.id > 0)
{
#if defined(USE_DEPTH_RENDERBUFFER)
glDeleteRenderbuffers(1, &target.depth.id);
#elif defined(USE_DEPTH_TEXTURE)
glDeleteTextures(1, &target.depth.id);
#endif
}
glDeleteFramebuffers(1, &target.id);
}
else TraceLog(LOG_INFO, "[FBO ID %i] Framebuffer object created successfully", target.id);
@ -2724,7 +2736,7 @@ void rlUnloadMesh(Mesh *mesh)
if (mesh->tangents != NULL) free(mesh->tangents);
if (mesh->texcoords2 != NULL) free(mesh->texcoords2);
if (mesh->indices != NULL) free(mesh->indices);
if (mesh->baseVertices != NULL) free(mesh->baseVertices);
if (mesh->baseNormals != NULL) free(mesh->baseNormals);
if (mesh->weightBias != NULL) free(mesh->weightBias);
@ -3807,7 +3819,7 @@ static unsigned int LoadShaderProgram(unsigned int vShaderId, unsigned int fShad
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength);
#ifdef _MSC_VER
#if defined(_MSC_VER)
char *log = malloc(maxLength);
#else
char log[maxLength];
@ -3816,7 +3828,7 @@ static unsigned int LoadShaderProgram(unsigned int vShaderId, unsigned int fShad
TraceLog(LOG_INFO, "%s", log);
#ifdef _MSC_VER
#if defined(_MSC_VER)
free(log);
#endif
glDeleteProgram(program);

View file

@ -9,7 +9,7 @@
* Allows drawing rectangles and text with a single draw call, very useful for GUI systems!
*
* #define SUPPORT_QUADS_DRAW_MODE
* Use QUADS instead of TRIANGLES for drawing when possible.
* Use QUADS instead of TRIANGLES for drawing when possible.
* Some lines-based shapes could still use lines
*
* LICENSE: zlib/libpng
@ -54,12 +54,14 @@
//----------------------------------------------------------------------------------
// Global Variables Definition
//----------------------------------------------------------------------------------
// ...
static Texture2D texShapes = { 0 };
static Rectangle recTexShapes = { 0 };
//----------------------------------------------------------------------------------
// Module specific Functions Declaration
//----------------------------------------------------------------------------------
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
@ -114,27 +116,34 @@ void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color)
startPos = endPos;
endPos = tempPos;
}
float dx = endPos.x - startPos.x;
float dy = endPos.y - startPos.y;
float d = sqrtf(dx*dx + dy*dy);
float angle = asinf(dy/d);
rlEnableTexture(GetTextureDefault().id);
rlEnableTexture(GetShapesTexture().id);
rlPushMatrix();
rlTranslatef((float)startPos.x, (float)startPos.y, 0);
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);
rlColor4ub(color.r, color.g, color.b, color.a);
rlNormal3f(0.0f, 0.0f, 1.0f);
rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
rlVertex2f(0.0f, 0.0f);
rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
rlVertex2f(0.0f, thick);
rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
rlVertex2f(d, thick);
rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height);
rlVertex2f(d, 0.0f);
rlEnd();
rlPopMatrix();
@ -153,12 +162,12 @@ void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color)
for (int i = 1; i <= LINE_DIVISIONS; i++)
{
// Cubic easing in-out
// NOTE: Easing is calculated only for y position value
// NOTE: Easing is calculated only for y position value
current.y = EaseCubicInOut((float)i, startPos.y, endPos.y - startPos.y, (float)LINE_DIVISIONS);
current.x = previous.x + (endPos.x - startPos.x)/ (float)LINE_DIVISIONS;
DrawLineEx(previous, current, thick, color);
previous = current;
}
}
@ -174,7 +183,7 @@ void DrawCircle(int centerX, int centerY, float radius, Color color)
void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2)
{
if (rlCheckBufferLimit(RL_TRIANGLES, 3*36)) rlglDraw();
rlBegin(RL_TRIANGLES);
for (int i = 0; i < 360; i += 10)
{
@ -191,24 +200,31 @@ void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Co
// Draw a color-filled circle (Vector version)
// NOTE: On OpenGL 3.3 and ES2 we use QUADS to avoid drawing order issues (view rlglDraw)
void DrawCircleV(Vector2 center, float radius, Color color)
{
{
#if defined(SUPPORT_QUADS_DRAW_MODE)
if (rlCheckBufferLimit(RL_QUADS, 4*(36/2))) rlglDraw();
rlEnableTexture(GetTextureDefault().id); // Default white texture
rlEnableTexture(GetShapesTexture().id);
rlBegin(RL_QUADS);
for (int i = 0; i < 360; i += 20)
{
rlColor4ub(color.r, color.g, color.b, color.a);
rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
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);
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);
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);
}
rlEnd();
rlDisableTexture();
#else
if (rlCheckBufferLimit(RL_TRIANGLES, 3*(36/2))) rlglDraw();
@ -217,7 +233,7 @@ void DrawCircleV(Vector2 center, float radius, Color color)
for (int i = 0; i < 360; i += 10)
{
rlColor4ub(color.r, color.g, color.b, color.a);
rlVertex2f(center.x, center.y);
rlVertex2f(center.x + sinf(DEG2RAD*i)*radius, center.y + cosf(DEG2RAD*i)*radius);
rlVertex2f(center.x + sinf(DEG2RAD*(i + 10))*radius, center.y + cosf(DEG2RAD*(i + 10))*radius);
@ -230,7 +246,7 @@ void DrawCircleV(Vector2 center, float radius, Color color)
void DrawCircleLines(int centerX, int centerY, float radius, Color color)
{
if (rlCheckBufferLimit(RL_LINES, 2*36)) rlglDraw();
rlBegin(RL_LINES);
rlColor4ub(color.r, color.g, color.b, color.a);
@ -246,104 +262,55 @@ void DrawCircleLines(int centerX, int centerY, float radius, Color color)
// Draw a color-filled rectangle
void DrawRectangle(int posX, int posY, int width, int height, Color color)
{
Vector2 position = { (float)posX, (float)posY };
Vector2 size = { (float)width, (float)height };
DrawRectangleV(position, size, color);
DrawRectangleV((Vector2){ (float)posX, (float)posY }, (Vector2){ (float)width, (float)height }, color);
}
// Draw a color-filled rectangle (Vector version)
// 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)
{
#if defined(SUPPORT_QUADS_DRAW_MODE)
#if defined(SUPPORT_FONT_TEXTURE)
// Draw rectangle using font texture white character
rlEnableTexture(GetFontDefault().texture.id);
Color colors[4] = { color, color, color, color };
rlBegin(RL_QUADS);
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
DrawRectanglePro((Rectangle){ position.x, position.y, size.x, size.y }, (Vector2){ 0.0f, 0.0f }, 0.0f, colors);
}
// Draw a color-filled rectangle
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();
rlTranslatef(rec.x, rec.y, 0);
//rlTranslatef(rec.x, rec.y, 0); // Already considered on vertex position
rlRotatef(rotation, 0, 0, 1);
rlTranslatef(-origin.x, -origin.y, 0);
rlBegin(RL_QUADS);
rlColor4ub(color.r, color.g, color.b, color.a);
rlNormal3f(0.0f, 0.0f, 1.0f); // Normal vector pointing towards viewer
rlNormal3f(0.0f, 0.0f, 1.0f);
rlVertex2f(0.0f, 0.0f);
rlVertex2f(0.0f, rec.height);
rlVertex2f(rec.width, rec.height);
rlVertex2f(rec.width, 0.0f);
// NOTE: Default raylib font character 95 is a white square
rlColor4ub(colors[0].r, colors[0].g, colors[0].b, colors[0].a);
rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
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();
rlPopMatrix();
@ -368,67 +335,15 @@ 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
void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4)
{
#if defined(SUPPORT_FONT_TEXTURE)
// Draw rectangle using font texture white character
rlEnableTexture(GetFontDefault().texture.id);
Color colors[4] = { col1, col2, col3, col4 };
rlBegin(RL_QUADS);
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
DrawRectanglePro(rec, (Vector2){ 0.0f, 0.0f }, 0.0f, colors);
}
// Draw rectangle outline
// NOTE: On OpenGL 3.3 and ES2 we use QUADS to avoid drawing order issues (view rlglDraw)
void DrawRectangleLines(int posX, int posY, int width, int height, Color color)
{
{
#if defined(SUPPORT_QUADS_DRAW_MODE)
DrawRectangle(posX, posY, width, 1, color);
DrawRectangle(posX + width - 1, posY + 1, 1, height - 2, color);
@ -454,13 +369,13 @@ void DrawRectangleLines(int posX, int posY, int width, int height, Color color)
// Draw rectangle outline with extended parameters
void DrawRectangleLinesEx(Rectangle rec, int lineThick, Color color)
{
{
if (lineThick > rec.width || lineThick > rec.height)
{
if(rec.width > rec.height) lineThick = (int)rec.height/2;
else if (rec.width < rec.height) lineThick = (int)rec.width/2;
}
}
DrawRectangle( (int)rec.x, (int)rec.y, (int)rec.width, lineThick, color);
DrawRectangle( (int)(rec.x - lineThick + rec.width), (int)(rec.y + lineThick), lineThick, (int)(rec.height - lineThick*2.0f), color);
DrawRectangle( (int)rec.x, (int)(rec.y + rec.height - lineThick), (int)rec.width, lineThick, color);
@ -471,16 +386,24 @@ void DrawRectangleLinesEx(Rectangle rec, int lineThick, Color color)
void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color)
{
#if defined(SUPPORT_QUADS_DRAW_MODE)
rlEnableTexture(GetTextureDefault().id); // Default white texture
rlEnableTexture(GetShapesTexture().id);
rlBegin(RL_QUADS);
rlColor4ub(color.r, color.g, color.b, color.a);
rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
rlVertex2f(v1.x, v1.y);
rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
rlVertex2f(v2.x, v2.y);
rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
rlVertex2f(v2.x, v2.y);
rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height);
rlVertex2f(v3.x, v3.y);
rlEnd();
rlDisableTexture();
#else
rlBegin(RL_TRIANGLES);
@ -512,24 +435,31 @@ void DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, Color color)
void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color color)
{
if (sides < 3) sides = 3;
if (rlCheckBufferLimit(RL_QUADS, 4*(360/sides))) rlglDraw();
rlPushMatrix();
rlTranslatef(center.x, center.y, 0.0);
rlRotatef(rotation, 0, 0, 1);
#if defined(SUPPORT_QUADS_DRAW_MODE)
rlEnableTexture(GetTextureDefault().id); // Default white texture
rlEnableTexture(GetShapesTexture().id);
rlBegin(RL_QUADS);
for (int i = 0; i < 360; i += 360/sides)
{
rlColor4ub(color.r, color.g, color.b, color.a);
rlTexCoord2f(recTexShapes.x/texShapes.width, recTexShapes.y/texShapes.height);
rlVertex2f(0, 0);
rlTexCoord2f(recTexShapes.x/texShapes.width, (recTexShapes.y + recTexShapes.height)/texShapes.height);
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);
rlTexCoord2f((recTexShapes.x + recTexShapes.width)/texShapes.width, recTexShapes.y/texShapes.height);
rlVertex2f(sinf(DEG2RAD*(i + 360/sides))*radius, cosf(DEG2RAD*(i + 360/sides))*radius);
}
rlEnd();
@ -555,9 +485,9 @@ void DrawPolyEx(Vector2 *points, int pointsCount, Color color)
if (pointsCount >= 3)
{
if (rlCheckBufferLimit(RL_QUADS, pointsCount)) rlglDraw();
#if defined(SUPPORT_QUADS_DRAW_MODE)
rlEnableTexture(GetTextureDefault().id); // Default white texture
rlEnableTexture(GetShapesTexture().id);
rlBegin(RL_QUADS);
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
//----------------------------------------------------------------------------------
@ -647,7 +584,7 @@ bool CheckCollisionPointTriangle(Vector2 point, Vector2 p1, Vector2 p2, Vector2
bool CheckCollisionRecs(Rectangle rec1, Rectangle rec2)
{
bool collision = false;
if ((rec1.x <= (rec2.x + rec2.width) && (rec1.x + rec1.width) >= rec2.x) &&
(rec1.y <= (rec2.y + rec2.height) && (rec1.y + rec1.height) >= rec2.y)) collision = true;
@ -675,7 +612,7 @@ bool CheckCollisionCircleRec(Vector2 center, float radius, Rectangle rec)
{
int recCenterX = (int)(rec.x + rec.width/2.0f);
int recCenterY = (int)(rec.y + rec.height/2.0f);
float dx = (float)fabs(center.x - recCenterX);
float dy = (float)fabs(center.y - recCenterY);
@ -685,7 +622,7 @@ bool CheckCollisionCircleRec(Vector2 center, float radius, Rectangle rec)
if (dx <= (rec.width/2.0f)) { return true; }
if (dy <= (rec.height/2.0f)) { return true; }
float cornerDistanceSq = (dx - rec.width/2.0f)*(dx - rec.width/2.0f) +
float cornerDistanceSq = (dx - rec.width/2.0f)*(dx - rec.width/2.0f) +
(dy - rec.height/2.0f)*(dy - rec.height/2.0f);
return (cornerDistanceSq <= (radius*radius));
@ -744,7 +681,7 @@ Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2)
{
if (retRec.width >= rec1.width) retRec.width = rec1.width;
}
if (rec1.height > rec2.height)
{
if (retRec.height >= rec2.height) retRec.height = rec2.height;
@ -762,12 +699,29 @@ Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2)
// Module specific Functions Definition
//----------------------------------------------------------------------------------
// Cubic easing in-out
// Cubic easing in-out
// NOTE: Required for DrawLineBezier()
static float EaseCubicInOut(float t, float b, float c, float d)
{
static float EaseCubicInOut(float t, float b, float c, float d)
{
if ((t /= 0.5f*d) < 1)
return 0.5f*c*t*t*t + b;
t -= 2;
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
func DrawRectanglePro(rec Rectangle, origin Vector2, rotation float32, color Color) {
func DrawRectanglePro(rec Rectangle, origin Vector2, rotation float32, colors []Color) {
crec := rec.cptr()
corigin := origin.cptr()
crotation := (C.float)(rotation)
ccolor := color.cptr()
C.DrawRectanglePro(*crec, *corigin, crotation, *ccolor)
ccolor := (*C.Color)(unsafe.Pointer(&colors[0]))
C.DrawRectanglePro(*crec, *corigin, crotation, ccolor)
}
// DrawRectangleGradientV - Draw a vertical-gradient-filled rectangle
@ -290,3 +290,10 @@ func CheckCollisionPointTriangle(point, p1, p2, p3 Vector2) bool {
v := bool(ret)
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...
}
//FILE *myimage = fopen("default_font.raw", "wb");
//fwrite(image.pixels, 1, 128*128*4, myimage);
//fclose(myimage);
Image image = LoadImageEx(imagePixels, imWidth, imHeight);
ImageFormat(&image, UNCOMPRESSED_GRAY_ALPHA);
@ -204,10 +200,10 @@ extern void LoadDefaultFont(void)
// Reconstruct charSet using charsWidth[], charsHeight, charsDivisor, charsCount
//------------------------------------------------------------------------------
// Allocate space for our characters info data
// NOTE: This memory should be freed at end! --> CloseWindow()
defaultFont.chars = (CharInfo *)malloc(defaultFont.charsCount*sizeof(CharInfo));
defaultFont.chars = (CharInfo *)malloc(defaultFont.charsCount*sizeof(CharInfo));
int currentLine = 0;
int currentPosX = charsDivisor;
@ -242,7 +238,7 @@ extern void LoadDefaultFont(void)
}
defaultFont.baseSize = (int)defaultFont.chars[0].rec.height;
TraceLog(LOG_INFO, "[TEX ID %i] Default font loaded successfully", defaultFont.texture.id);
}
@ -262,7 +258,7 @@ Font GetFontDefault()
#else
Font font = { 0 };
return font;
#endif
#endif
}
// Load Font from file into GPU memory (VRAM)
@ -276,15 +272,7 @@ Font LoadFont(const char *fileName)
Font font = { 0 };
#if defined(SUPPORT_FILEFORMAT_TTF)
if (IsFileExtension(fileName, ".ttf"))
{
font.baseSize = DEFAULT_TTF_FONTSIZE;
font.charsCount = DEFAULT_TTF_NUMCHARS;
font.chars = LoadFontData(fileName, font.baseSize, NULL, font.charsCount, FONT_DEFAULT);
Image atlas = GenImageFontAtlas(font.chars, font.charsCount, font.baseSize, 4, 0);
font.texture = LoadTextureFromImage(atlas);
UnloadImage(atlas);
}
if (IsFileExtension(fileName, ".ttf")) font = LoadFontEx(fileName, DEFAULT_TTF_FONTSIZE, DEFAULT_TTF_NUMCHARS, NULL);
else
#endif
#if defined(SUPPORT_FILEFORMAT_FNT)
@ -313,14 +301,19 @@ Font LoadFont(const char *fileName)
Font LoadFontEx(const char *fileName, int fontSize, int charsCount, int *fontChars)
{
Font font = { 0 };
font.baseSize = fontSize;
font.charsCount = (charsCount > 0) ? charsCount : 95;
font.chars = LoadFontData(fileName, font.baseSize, fontChars, font.charsCount, FONT_DEFAULT);
Image atlas = GenImageFontAtlas(font.chars, font.charsCount, font.baseSize, 2, 0);
font.texture = LoadTextureFromImage(atlas);
UnloadImage(atlas);
if (font.chars != NULL)
{
Image atlas = GenImageFontAtlas(font.chars, font.charsCount, font.baseSize, 2, 0);
font.texture = LoadTextureFromImage(atlas);
UnloadImage(atlas);
}
else font = GetFontDefault();
return font;
}
@ -333,94 +326,100 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c
#define SDF_CHAR_PADDING 4
#define SDF_ON_EDGE_VALUE 128
#define SDF_PIXEL_DIST_SCALE 64.0f
#define BITMAP_ALPHA_THRESHOLD 80
// In case no chars count provided, default to 95
charsCount = (charsCount > 0) ? charsCount : 95;
CharInfo *chars = (CharInfo *)malloc(charsCount*sizeof(CharInfo));
CharInfo *chars = NULL;
// Load font data (including pixel data) from TTF file
// NOTE: Loaded information should be enough to generate font image atlas,
// using any packaging method
FILE *fontFile = fopen(fileName, "rb"); // Load font file
fseek(fontFile, 0, SEEK_END);
long size = ftell(fontFile); // Get file size
fseek(fontFile, 0, SEEK_SET); // Reset file pointer
unsigned char *fontBuffer = (unsigned char *)malloc(size);
fread(fontBuffer, size, 1, fontFile);
fclose(fontFile);
// Init font for data reading
stbtt_fontinfo fontInfo;
if (!stbtt_InitFont(&fontInfo, fontBuffer, 0)) TraceLog(LOG_WARNING, "Failed to init font!");
// Calculate font scale factor
float scaleFactor = stbtt_ScaleForPixelHeight(&fontInfo, (float)fontSize);
if (fontFile != NULL)
{
fseek(fontFile, 0, SEEK_END);
long size = ftell(fontFile); // Get file size
fseek(fontFile, 0, SEEK_SET); // Reset file pointer
// Calculate font basic metrics
// NOTE: ascent is equivalent to font baseline
int ascent, descent, lineGap;
stbtt_GetFontVMetrics(&fontInfo, &ascent, &descent, &lineGap);
// Fill fontChars in case not provided externally
// NOTE: By default we fill charsCount consecutevely, starting at 32 (Space)
int genFontChars = false;
if (fontChars == NULL) genFontChars = true;
if (genFontChars)
{
fontChars = (int *)malloc(charsCount*sizeof(int));
for (int i = 0; i < charsCount; i++) fontChars[i] = i + 32;
}
// NOTE: Using simple packaging, one char after another
for (int i = 0; i < charsCount; i++)
{
int chw = 0, chh = 0; // Character width and height (on generation)
int ch = fontChars[i]; // Character value to get info for
chars[i].value = ch;
// Render a unicode codepoint to a bitmap
// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap
// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be
// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide
if (type != FONT_SDF) chars[i].data = stbtt_GetCodepointBitmap(&fontInfo, scaleFactor, scaleFactor, ch, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY);
else if (ch != 32) chars[i].data = stbtt_GetCodepointSDF(&fontInfo, scaleFactor, ch, SDF_CHAR_PADDING, SDF_ON_EDGE_VALUE, SDF_PIXEL_DIST_SCALE, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY);
if (type == FONT_BITMAP)
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);
// 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
// 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;
}
fontChars = (int *)malloc(charsCount*sizeof(int));
for (int i = 0; i < charsCount; i++) fontChars[i] = i + 32;
genFontChars = true;
}
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;
chars = (CharInfo *)malloc(charsCount*sizeof(CharInfo));
// NOTE: Using simple packaging, one char after another
for (int i = 0; i < charsCount; i++)
{
int chw = 0, chh = 0; // Character width and height (on generation)
int ch = fontChars[i]; // Character value to get info for
chars[i].value = ch;
// Render a unicode codepoint to a bitmap
// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap
// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be
// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide
if (type != FONT_SDF) chars[i].data = stbtt_GetCodepointBitmap(&fontInfo, scaleFactor, scaleFactor, ch, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY);
else if (ch != 32) chars[i].data = stbtt_GetCodepointSDF(&fontInfo, scaleFactor, ch, SDF_CHAR_PADDING, SDF_ON_EDGE_VALUE, SDF_PIXEL_DIST_SCALE, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY);
if (type == FONT_BITMAP)
{
// 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);
}
free(fontBuffer);
if (genFontChars) free(fontChars);
else TraceLog(LOG_WARNING, "[%s] TTF file could not be opened", fileName);
return chars;
}
@ -429,25 +428,25 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c
Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int padding, int packMethod)
{
Image atlas = { 0 };
// In case no chars count provided we suppose default of 95
charsCount = (charsCount > 0) ? charsCount : 95;
// Calculate image size based on required pixel area
// NOTE 1: Image is forced to be squared and POT... very conservative!
// NOTE 2: SDF font characters already contain an internal padding,
// NOTE 2: SDF font characters already contain an internal padding,
// so image size would result bigger than default font type
float requiredArea = 0;
for (int i = 0; i < charsCount; i++) requiredArea += ((chars[i].rec.width + 2*padding)*(chars[i].rec.height + 2*padding));
float guessSize = sqrtf(requiredArea)*1.25f;
int imageSize = (int)powf(2, ceilf(logf((float)guessSize)/logf(2))); // Calculate next POT
atlas.width = imageSize; // Atlas bitmap width
atlas.height = imageSize; // Atlas bitmap height
atlas.data = (unsigned char *)calloc(1, atlas.width*atlas.height); // Create a bitmap to store characters (8 bpp)
atlas.format = UNCOMPRESSED_GRAYSCALE;
atlas.mipmaps = 1;
// DEBUG: We can see padding in the generated image setting a gray background...
//for (int i = 0; i < atlas.width*atlas.height; i++) ((unsigned char *)atlas.data)[i] = 100;
@ -455,9 +454,9 @@ Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int paddi
{
int offsetX = padding;
int offsetY = padding;
// NOTE: Using simple packaging, one char after another
for (int i = 0; i < charsCount; i++)
for (int i = 0; i < charsCount; i++)
{
// Copy pixel data from fc.data to atlas
for (int y = 0; y < (int)chars[i].rec.height; y++)
@ -467,22 +466,22 @@ Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int paddi
((unsigned char *)atlas.data)[(offsetY + y)*atlas.width + (offsetX + x)] = chars[i].data[y*(int)chars[i].rec.width + x];
}
}
chars[i].rec.x = (float)offsetX;
chars[i].rec.y = (float)offsetY;
// Move atlas position X for next character drawing
offsetX += ((int)chars[i].rec.width + 2*padding);
if (offsetX >= (atlas.width - (int)chars[i].rec.width - padding))
{
offsetX = padding;
// NOTE: Be careful on offsetY for SDF fonts, by default SDF
// NOTE: Be careful on offsetY for SDF fonts, by default SDF
// use an internal padding of 4 pixels, it means char rectangle
// height is bigger than fontSize, it could be up to (fontSize + 8)
offsetY += (fontSize + 2*padding);
if (offsetY > (atlas.height - fontSize - padding)) break;
}
}
@ -490,13 +489,13 @@ Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int paddi
else if (packMethod == 1) // Use Skyline rect packing algorythm (stb_pack_rect)
{
TraceLog(LOG_DEBUG, "Using Skyline packing algorythm!");
stbrp_context *context = (stbrp_context *)malloc(sizeof(*context));
stbrp_node *nodes = (stbrp_node *)malloc(charsCount*sizeof(*nodes));
stbrp_init_target(context, atlas.width, atlas.height, nodes, charsCount);
stbrp_rect *rects = (stbrp_rect *)malloc(charsCount*sizeof(stbrp_rect));
// Fill rectangles for packaging
for (int i = 0; i < charsCount; i++)
{
@ -507,12 +506,12 @@ Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int paddi
// Package rectangles into atlas
stbrp_pack_rects(context, rects, charsCount);
for (int i = 0; i < charsCount; i++)
{
chars[i].rec.x = rects[i].x + (float)padding;
chars[i].rec.y = rects[i].y + (float)padding;
if (rects[i].was_packed)
{
// Copy pixel data from fc.data to atlas
@ -527,12 +526,13 @@ Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int paddi
else TraceLog(LOG_WARNING, "Character could not be packed: %i", i);
}
free(rects);
free(nodes);
free(context);
}
// TODO: Crop image if required for smaller size
// Convert image data from GRAYSCALE to GRAY_ALPHA
// WARNING: ImageAlphaMask(&atlas, atlas) does not work in this case, requires manual operation
unsigned char *dataGrayAlpha = (unsigned char *)malloc(imageSize*imageSize*sizeof(unsigned char)*2); // Two channels
@ -546,7 +546,7 @@ Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int paddi
free(atlas.data);
atlas.data = dataGrayAlpha;
atlas.format = UNCOMPRESSED_GRAY_ALPHA;
return atlas;
}
@ -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)
// NOTE: fontSize work like in any drawing program but if fontSize is lower than font-base-size, then font-base-size is used
// NOTE: chars spacing is proportional to fontSize
@ -588,10 +610,10 @@ void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, f
int length = strlen(text);
int textOffsetX = 0; // Offset between characters
int textOffsetY = 0; // Required for line break!
float scaleFactor;
float scaleFactor = 0.0f;
unsigned char letter; // Current character
int index; // Index position in sprite font
unsigned char letter = 0; // Current character
int index = 0; // Index position in sprite font
scaleFactor = fontSize/font.baseSize;
@ -623,7 +645,7 @@ void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, f
i++;
}
else index = GetGlyphIndex(font, (unsigned char)text[i]);
if ((unsigned char)text[i] != ' ')
{
DrawTexturePro(font.texture, font.chars[index].rec,
@ -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
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 lenCounter = 0;
float textWidth = 0;
float tempTextWidth = 0; // Used to count longer text line width
float textWidth = 0.0f;
float tempTextWidth = 0.0f; // Used to count longer text line width
float textHeight = (float)font.baseSize;
float scaleFactor = fontSize/(float)font.baseSize;
@ -761,26 +745,88 @@ int GetGlyphIndex(Font font, int character)
#endif
}
// Shows current FPS on top-left corner
// NOTE: Uses default font
void DrawFPS(int posX, int posY)
// Formatting of text with variables to 'embed'
const char *FormatText(const char *text, ...)
{
// NOTE: We are rendering fps every second for better viewing on high framerates
static char buffer[MAX_FORMATTEXT_LENGTH];
static int fps = 0;
static int counter = 0;
static int refreshRate = 20;
va_list args;
va_start(args, text);
vsprintf(buffer, text, args);
va_end(args);
if (counter < refreshRate) counter++;
else
return buffer;
}
// Get a piece of a text string
const char *SubText(const char *text, int position, int length)
{
static char buffer[MAX_SUBTEXT_LENGTH] = { 0 };
int textLength = strlen(text);
if (position >= textLength)
{
fps = GetFPS();
refreshRate = fps;
counter = 0;
position = textLength - 1;
length = 0;
}
// NOTE: We have rounding errors every frame, so it oscillates a lot
DrawText(FormatText("%2i FPS", fps), posX, posY, 20, LIME);
if (length >= textLength) length = textLength;
for (int c = 0 ; c < length ; c++)
{
*(buffer + c) = *(text + position);
text++;
}
*(buffer + length) = '\0';
return buffer;
}
// Split string into multiple strings
// NOTE: Files count is returned by parameters pointer
// NOTE: Allocated memory should be manually freed
char **SplitText(char *text, char delimiter, int *strCount)
{
#define MAX_SUBSTRING_LENGTH 128
char **strings = NULL;
int len = strlen(text);
char *strDup = (char *)malloc(len + 1);
strcpy(strDup, text);
int counter = 1;
// Count how many substrings we have on string
for (int i = 0; i < len; i++) if (text[i] == delimiter) counter++;
// Memory allocation for substrings
strings = (char **)malloc(sizeof(char *)*counter);
for (int i = 0; i < counter; i++) strings[i] = (char *)malloc(sizeof(char)*MAX_SUBSTRING_LENGTH);
char *substrPtr = NULL;
char delimiters[1] = { delimiter }; // Only caring for one delimiter
substrPtr = strtok(strDup, delimiters);
for (int i = 0; (i < counter) && (substrPtr != NULL); i++)
{
strcpy(strings[i], substrPtr);
substrPtr = strtok(NULL, delimiters);
}
*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;
}
//----------------------------------------------------------------------------------
@ -815,7 +861,7 @@ static Font LoadImageFont(Image image, Color key, int firstChar)
{
if (!COLOR_EQUAL(pixels[y*image.width + x], key)) break;
}
if (!COLOR_EQUAL(pixels[y*image.width + x], key)) break;
}
@ -911,17 +957,18 @@ static Font LoadBMFont(const char *fileName)
Font font = { 0 };
font.texture.id = 0;
char buffer[MAX_BUFFER_SIZE];
char buffer[MAX_BUFFER_SIZE] = { 0 };
char *searchPoint = NULL;
int fontSize = 0;
int texWidth, texHeight;
int texWidth = 0;
int texHeight = 0;
char texFileName[129];
int charsCount = 0;
int base; // Useless data
int base = 0; // Useless data
FILE *fntFile;
FILE *fntFile = NULL;
fntFile = fopen(fileName, "rt");
@ -984,10 +1031,10 @@ static Font LoadBMFont(const char *fileName)
UnloadImage(imCopy);
}
else font.texture = LoadTextureFromImage(imFont);
UnloadImage(imFont);
free(texPath);
// Fill font characters info data
font.baseSize = fontSize;

View file

@ -60,7 +60,7 @@
#include "raylib.h" // Declares module functions
#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
// 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
#endif
#if defined(SUPPORT_FILEFORMAT_KTX)
static Image LoadKTX(const char *fileName); // Load KTX file
static void SaveKTX(Image image, const char *fileName); // Save image data as KTX file
static Image LoadKTX(const char *fileName); // Load KTX file
static int SaveKTX(Image image, const char *fileName); // Save image data as KTX file
#endif
#if defined(SUPPORT_FILEFORMAT_PVR)
static Image LoadPVR(const char *fileName); // Load PVR file
@ -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,
// to get bytes we need to read (1 byte size, elements) instead of (x byte size, 1 element)
size_t bytes = fread(image.data, 1, size, rawFile);
int bytes = fread(image.data, 1, size, rawFile);
// Check if data has been read successfully
if (bytes < size)
@ -424,7 +424,7 @@ Color *GetImageData(Image image)
if ((image.format == UNCOMPRESSED_R32) ||
(image.format == UNCOMPRESSED_R32G32B32) ||
(image.format == UNCOMPRESSED_R32G32B32A32)) TraceLog(LOG_WARNING, "32bit pixel format converted to 8bit per channel");
for (int i = 0, k = 0; i < image.width*image.height; i++)
{
switch (image.format)
@ -500,7 +500,7 @@ Color *GetImageData(Image image)
pixels[i].g = 0;
pixels[i].b = 0;
pixels[i].a = 255;
} break;
case UNCOMPRESSED_R32G32B32:
{
@ -508,7 +508,7 @@ Color *GetImageData(Image image)
pixels[i].g = (unsigned char)(((float *)image.data)[k + 1]*255.0f);
pixels[i].b = (unsigned char)(((float *)image.data)[k + 2]*255.0f);
pixels[i].a = 255;
k += 3;
}
case UNCOMPRESSED_R32G32B32A32:
@ -517,7 +517,7 @@ Color *GetImageData(Image image)
pixels[i].g = (unsigned char)(((float *)image.data)[k]*255.0f);
pixels[i].b = (unsigned char)(((float *)image.data)[k]*255.0f);
pixels[i].a = (unsigned char)(((float *)image.data)[k]*255.0f);
k += 4;
}
default: break;
@ -532,7 +532,7 @@ Color *GetImageData(Image image)
Vector4 *GetImageDataNormalized(Image image)
{
Vector4 *pixels = (Vector4 *)malloc(image.width*image.height*sizeof(Vector4));
if (image.format >= COMPRESSED_DXT1_RGB) TraceLog(LOG_WARNING, "Pixel data retrieval not supported for compressed image formats");
else
{
@ -611,7 +611,7 @@ Vector4 *GetImageDataNormalized(Image image)
pixels[i].y = 0.0f;
pixels[i].z = 0.0f;
pixels[i].w = 1.0f;
} break;
case UNCOMPRESSED_R32G32B32:
{
@ -619,7 +619,7 @@ Vector4 *GetImageDataNormalized(Image image)
pixels[i].y = ((float *)image.data)[k + 1];
pixels[i].z = ((float *)image.data)[k + 2];
pixels[i].w = 1.0f;
k += 3;
}
case UNCOMPRESSED_R32G32B32A32:
@ -628,14 +628,14 @@ Vector4 *GetImageDataNormalized(Image image)
pixels[i].y = ((float *)image.data)[k + 1];
pixels[i].z = ((float *)image.data)[k + 2];
pixels[i].w = ((float *)image.data)[k + 3];
k += 4;
}
default: break;
}
}
}
return pixels;
}
@ -720,31 +720,68 @@ void UpdateTexture(Texture2D texture, const void *pixels)
void ExportImage(Image image, const char *fileName)
{
int success = 0;
// NOTE: Getting Color array as RGBA unsigned char values
unsigned char *imgData = (unsigned char *)GetImageData(image);
if (IsFileExtension(fileName, ".png")) success = stbi_write_png(fileName, image.width, image.height, 4, imgData, image.width*4);
else if (IsFileExtension(fileName, ".bmp")) success = stbi_write_bmp(fileName, image.width, image.height, 4, imgData);
else if (IsFileExtension(fileName, ".tga")) success = stbi_write_tga(fileName, image.width, image.height, 4, imgData);
else if (IsFileExtension(fileName, ".jpg")) success = stbi_write_jpg(fileName, image.width, image.height, 4, imgData, 80); // JPG quality: between 1 and 100
else if (IsFileExtension(fileName, ".ktx")) SaveKTX(image, fileName);
else if (IsFileExtension(fileName, ".raw"))
else if (IsFileExtension(fileName, ".ktx")) success = SaveKTX(image, fileName);
else if (IsFileExtension(fileName, ".raw"))
{
// Export raw pixel data (without header)
// NOTE: It's up to the user to track image parameters
FILE *rawFile = fopen(fileName, "wb");
fwrite(image.data, GetPixelDataSize(image.width, image.height, image.format), 1, rawFile);
success = fwrite(image.data, GetPixelDataSize(image.width, image.height, image.format), 1, 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);
else TraceLog(LOG_WARNING, "Image could not be exported.");
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
Image ImageCopy(Image image)
{
@ -950,7 +987,7 @@ void ImageFormat(Image *image, int newFormat)
case UNCOMPRESSED_R32:
{
// WARNING: Image is converted to GRAYSCALE eqeuivalent 32bit
image->data = (float *)malloc(image->width*image->height*sizeof(float));
for (int i = 0; i < image->width*image->height; i++)
@ -986,7 +1023,7 @@ void ImageFormat(Image *image, int newFormat)
free(pixels);
pixels = NULL;
// In case original image had mipmaps, generate mipmaps for formated image
// NOTE: Original mipmaps are replaced by new ones, if custom mipmaps were used, they are lost
if (image->mipmaps > 1)
@ -1050,14 +1087,14 @@ void ImageAlphaMask(Image *image, Image alphaMask)
void ImageAlphaClear(Image *image, Color color, float threshold)
{
Color *pixels = GetImageData(*image);
for (int i = 0; i < image->width*image->height; i++) if (pixels[i].a <= (unsigned char)(threshold*255.0f)) pixels[i] = color;
UnloadImage(*image);
int prevFormat = image->format;
*image = LoadImageEx(pixels, image->width, image->height);
ImageFormat(image, prevFormat);
}
@ -1065,13 +1102,13 @@ void ImageAlphaClear(Image *image, Color color, float threshold)
void ImageAlphaCrop(Image *image, float threshold)
{
Rectangle crop = { 0 };
Color *pixels = GetImageData(*image);
int minx = 0;
int miny = 0;
for (int i = 0; i < image->width*image->height; i++)
for (int i = 0; i < image->width*image->height; i++)
{
if (pixels[i].a > (unsigned char)(threshold*255.0f))
{
@ -1090,18 +1127,18 @@ void ImageAlphaCrop(Image *image, float threshold)
else if (crop.height < (float)miny) crop.height = (float)miny;
}
}
crop.width -= (crop.x - 1);
crop.height -= (crop.y - 1);
TraceLog(LOG_INFO, "Crop rectangle: (%i, %i, %i, %i)", crop.x, crop.y, crop.width, crop.height);
free(pixels);
// NOTE: Added this weird check to avoid additional 1px crop to
// image data that has already been cropped...
if ((crop.x != 1) &&
(crop.y != 1) &&
if ((crop.x != 1) &&
(crop.y != 1) &&
(crop.width != image->width - 1) &&
(crop.height != image->height - 1)) ImageCrop(image, crop);
}
@ -1111,8 +1148,8 @@ void ImageAlphaPremultiply(Image *image)
{
float alpha = 0.0f;
Color *pixels = GetImageData(*image);
for (int i = 0; i < image->width*image->height; i++)
for (int i = 0; i < image->width*image->height; i++)
{
alpha = (float)pixels[i].a/255.0f;
pixels[i].r = (unsigned char)((float)pixels[i].r*alpha);
@ -1121,10 +1158,10 @@ void ImageAlphaPremultiply(Image *image)
}
UnloadImage(*image);
int prevFormat = image->format;
*image = LoadImageEx(pixels, image->width, image->height);
ImageFormat(image, prevFormat);
}
@ -1245,9 +1282,9 @@ void ImageResizeCanvas(Image *image, int newWidth,int newHeight, int offsetX, in
Image imTemp = GenImageColor(newWidth, newHeight, color);
Rectangle srcRec = { 0.0f, 0.0f, (float)image->width, (float)image->height };
Rectangle dstRec = { (float)offsetX, (float)offsetY, (float)srcRec.width, (float)srcRec.height };
// TODO: Review different scaling situations
if ((newWidth > image->width) && (newHeight > image->height))
{
ImageDraw(&imTemp, *image, srcRec, dstRec);
@ -1277,7 +1314,7 @@ void ImageMipmaps(Image *image)
{
if (mipWidth != 1) mipWidth /= 2;
if (mipHeight != 1) mipHeight /= 2;
// Security check for NPOT textures
if (mipWidth < 1) mipWidth = 1;
if (mipHeight < 1) mipHeight = 1;
@ -1295,8 +1332,8 @@ void ImageMipmaps(Image *image)
if (image->mipmaps < mipCount)
{
void *temp = realloc(image->data, mipSize);
if (temp != NULL)
if (temp != NULL)
{
image->data = temp; // Assign new pointer (new size) to store mipmaps data
TraceLog(LOG_DEBUG, "Image data memory point reallocated: 0x%x", temp);
@ -1305,29 +1342,29 @@ void ImageMipmaps(Image *image)
// Pointer to allocated memory point where store next mipmap level data
unsigned char *nextmip = (unsigned char *)image->data + GetPixelDataSize(image->width, image->height, image->format);
mipWidth = image->width/2;
mipHeight = image->height/2;
mipSize = GetPixelDataSize(mipWidth, mipHeight, image->format);
Image imCopy = ImageCopy(*image);
for (int i = 1; i < mipCount; i++)
{
TraceLog(LOG_DEBUG, "Gen mipmap level: %i (%i x %i) - size: %i - offset: 0x%x", i, mipWidth, mipHeight, mipSize, nextmip);
ImageResize(&imCopy, mipWidth, mipHeight); // Uses internally Mitchell cubic downscale filter
memcpy(nextmip, imCopy.data, mipSize);
nextmip += mipSize;
image->mipmaps++;
mipWidth /= 2;
mipHeight /= 2;
// Security check for NPOT textures
if (mipWidth < 1) mipWidth = 1;
if (mipHeight < 1) mipHeight = 1;
mipSize = GetPixelDataSize(mipWidth, mipHeight, image->format);
}
@ -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)
// TODO: Feel this function could be simplified...
void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec)
@ -1507,7 +1595,7 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec)
UnloadImage(srcCopy); // Source copy not required any more...
Vector4 fsrc, fdst, fout; // float based versions of pixel data
// Blit pixels, copy source image into destination
// TODO: Probably out-of-bounds blitting could be considered here instead of so much cropping...
for (int j = (int)dstRec.y; j < (int)(dstRec.y + dstRec.height); j++)
@ -1515,12 +1603,12 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec)
for (int i = (int)dstRec.x; i < (int)(dstRec.x + dstRec.width); i++)
{
// Alpha blending (https://en.wikipedia.org/wiki/Alpha_compositing)
fdst = ColorNormalize(dstPixels[j*(int)dst->width + i]);
fsrc = ColorNormalize(srcPixels[(j - (int)dstRec.y)*(int)dstRec.width + (i - (int)dstRec.x)]);
fout.w = fsrc.w + fdst.w*(1.0f - fsrc.w);
if (fout.w <= 0.0f)
{
fout.x = 0.0f;
@ -1534,9 +1622,9 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec)
fout.z = (fsrc.z*fsrc.w + fdst.z*fdst.w*(1 - fsrc.w))/fout.w;
}
dstPixels[j*(int)dst->width + i] = (Color){ (unsigned char)(fout.x*255.0f),
(unsigned char)(fout.y*255.0f),
(unsigned char)(fout.z*255.0f),
dstPixels[j*(int)dst->width + i] = (Color){ (unsigned char)(fout.x*255.0f),
(unsigned char)(fout.y*255.0f),
(unsigned char)(fout.z*255.0f),
(unsigned char)(fout.w*255.0f) };
// TODO: Support other blending options
@ -1574,16 +1662,16 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co
// TODO: ISSUE: Measured text size does not seem to be correct... issue on ImageDraw()
Vector2 imSize = MeasureTextEx(font, text, (float)font.baseSize, spacing);
TraceLog(LOG_DEBUG, "Text Image size: %f, %f", imSize.x, imSize.y);
// NOTE: glGetTexImage() not available in OpenGL ES
// TODO: This is horrible, retrieving font texture from GPU!!!
// TODO: This is horrible, retrieving font texture from GPU!!!
// Define ImageFont struct? or include Image spritefont in Font struct?
Image imFont = GetTextureData(font.texture);
ImageFormat(&imFont, UNCOMPRESSED_R8G8B8A8); // Make sure image format could be properly colored!
ImageColorTint(&imFont, tint); // Apply color tint to font
// Create image to store text
@ -1614,10 +1702,10 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co
else index = GetGlyphIndex(font, (unsigned char)text[i]);
CharInfo letter = font.chars[index];
if ((unsigned char)text[i] != ' ')
{
ImageDraw(&imText, imFont, letter.rec, (Rectangle){ (float)(posX + letter.offsetX),
ImageDraw(&imText, imFont, letter.rec, (Rectangle){ (float)(posX + letter.offsetX),
(float)letter.offsetY, (float)letter.rec.width, (float)letter.rec.height });
}
@ -1643,17 +1731,22 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co
}
// 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);
Rectangle dstRec = { position.x, position.y, (float)imRec.width, (float)imRec.height };
ImageDraw(dst, imRec, rec, dstRec);
ImageDraw(dst, imRec, (Rectangle){ 0, 0, rec.width, rec.height }, rec);
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)
void ImageDrawText(Image *dst, Vector2 position, const char *text, int fontSize, Color color)
{
@ -1968,13 +2061,13 @@ void ImageColorReplace(Image *image, Color color, Color replace)
Image GenImageColor(int width, int height, Color color)
{
Color *pixels = (Color *)calloc(width*height, sizeof(Color));
for (int i = 0; i < width*height; i++) pixels[i] = color;
Image image = LoadImageEx(pixels, width, height);
free(pixels);
return image;
}
@ -2032,17 +2125,17 @@ Image GenImageGradientRadial(int width, int height, float density, Color inner,
float centerX = (float)width/2.0f;
float centerY = (float)height/2.0f;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
float dist = hypotf((float)x - centerX, (float)y - centerY);
float factor = (dist - radius*density)/(radius*(1.0f - density));
factor = (float)fmax(factor, 0.f);
factor = (float)fmin(factor, 1.f); // dist can be bigger than radius so we have to check
pixels[y*width + x].r = (int)((float)outer.r*factor + (float)inner.r*(1.0f - factor));
pixels[y*width + x].g = (int)((float)outer.g*factor + (float)inner.g*(1.0f - factor));
pixels[y*width + x].b = (int)((float)outer.b*factor + (float)inner.b*(1.0f - factor));
@ -2104,7 +2197,7 @@ Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float
{
float nx = (float)(x + offsetX)*scale/(float)width;
float ny = (float)(y + offsetY)*scale/(float)height;
// Typical values to start playing with:
// lacunarity = ~2.0 -- spacing between successive octaves (use exactly 2.0 for wrapping output)
// gain = 0.5 -- relative weighting applied to each successive octave
@ -2112,7 +2205,7 @@ Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float
// NOTE: We need to translate the data from [-1..1] to [0..1]
float p = (stb_perlin_fbm_noise3(nx, ny, 1.0f, 2.0f, 0.5f, 6, 0, 0, 0) + 1.0f)/2.0f;
int intensity = (int)(p*255.0f);
pixels[y*width + x] = (Color){intensity, intensity, intensity, 255};
}
@ -2145,7 +2238,7 @@ Image GenImageCellular(int width, int height, int tileSize)
for (int y = 0; y < height; y++)
{
int tileY = y/tileSize;
for (int x = 0; x < width; x++)
{
int tileX = x/tileSize;
@ -2175,7 +2268,7 @@ Image GenImageCellular(int width, int height, int tileSize)
pixels[y*width + x] = (Color){ intensity, intensity, intensity, 255 };
}
}
free(seeds);
Image image = LoadImageEx(pixels, width, height);
@ -2323,7 +2416,7 @@ void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, V
{
float width = (float)texture.width;
float height = (float)texture.height;
if (sourceRec.width < 0) sourceRec.x -= sourceRec.width;
if (sourceRec.height < 0) sourceRec.y -= sourceRec.height;
@ -2618,11 +2711,11 @@ static Image LoadDDS(const char *fileName)
else
{
// 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);
}
@ -2641,7 +2734,7 @@ static Image LoadDDS(const char *fileName)
image.width = ddsHeader.width;
image.height = ddsHeader.height;
if (ddsHeader.mipmapCount == 0) image.mipmaps = 1; // Parameter not used
else image.mipmaps = ddsHeader.mipmapCount;
@ -2729,7 +2822,7 @@ static Image LoadDDS(const char *fileName)
TraceLog(LOG_DEBUG, "Pitch or linear size: %i", ddsHeader.pitchOrLinearSize);
image.data = (unsigned char*)malloc(size*sizeof(unsigned char));
image.data = (unsigned char *)malloc(size*sizeof(unsigned char));
fread(image.data, size, 1, ddsFile);
@ -2802,7 +2895,7 @@ static Image LoadPKM(const char *fileName)
// Get the image header
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);
}
@ -2826,7 +2919,7 @@ static Image LoadPKM(const char *fileName)
int size = image.width*image.height*bpp/8; // Total data size in bytes
image.data = (unsigned char*)malloc(size*sizeof(unsigned char));
image.data = (unsigned char *)malloc(size*sizeof(unsigned char));
fread(image.data, size, 1, pkmFile);
@ -2858,9 +2951,9 @@ static Image LoadKTX(const char *fileName)
// KTX file Header (64 bytes)
// v1.1 - https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
// v2.0 - http://github.khronos.org/KTX-Specification/
// TODO: Support KTX 2.2 specs!
typedef struct {
char id[12]; // Identifier: "«KTX 11»\r\n\x1A\n"
unsigned int endianness; // Little endian: 0x01 0x02 0x03 0x04
@ -2920,7 +3013,7 @@ static Image LoadKTX(const char *fileName)
int dataSize;
fread(&dataSize, sizeof(unsigned int), 1, ktxFile);
image.data = (unsigned char*)malloc(dataSize*sizeof(unsigned char));
image.data = (unsigned char *)malloc(dataSize*sizeof(unsigned char));
fread(image.data, dataSize, 1, ktxFile);
@ -2937,12 +3030,14 @@ static Image LoadKTX(const char *fileName)
// Save image data as KTX file
// NOTE: By default KTX 1.1 spec is used, 2.0 is still on draft (01Oct2018)
static void SaveKTX(Image image, const char *fileName)
static int SaveKTX(Image image, const char *fileName)
{
int success = 0;
// KTX file Header (64 bytes)
// v1.1 - https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
// v2.0 - http://github.khronos.org/KTX-Specification/ - still on draft, not ready for implementation
typedef struct {
char id[12]; // Identifier: "«KTX 11»\r\n\x1A\n" // KTX 2.0: "«KTX 22»\r\n\x1A\n"
unsigned int endianness; // Little endian: 0x01 0x02 0x03 0x04
@ -2970,11 +3065,11 @@ static void SaveKTX(Image image, const char *fileName)
else
{
KTXHeader ktxHeader;
// KTX identifier (v2.2)
//unsigned char id[12] = { '«', 'K', 'T', 'X', ' ', '1', '1', '»', '\r', '\n', '\x1A', '\n' };
//unsigned char id[12] = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A };
// Get the image header
strcpy(ktxHeader.id, "«KTX 11»\r\n\x1A\n"); // KTX 1.1 signature
ktxHeader.endianness = 0;
@ -2990,28 +3085,28 @@ static void SaveKTX(Image image, const char *fileName)
ktxHeader.faces = 1;
ktxHeader.mipmapLevels = image.mipmaps; // If it was 0, it means mipmaps should be generated on loading (not for compressed formats)
ktxHeader.keyValueDataSize = 0; // No extra data after the header
rlGetGlTextureFormats(image.format, &ktxHeader.glInternalFormat, &ktxHeader.glFormat, &ktxHeader.glType); // rlgl module function
ktxHeader.glBaseInternalFormat = ktxHeader.glFormat; // KTX 1.1 only
// NOTE: We can save into a .ktx all PixelFormats supported by raylib, including compressed formats like DXT, ETC or ASTC
if (ktxHeader.glFormat == -1) TraceLog(LOG_WARNING, "Image format not supported for KTX export.");
else
{
fwrite(&ktxHeader, 1, sizeof(KTXHeader), ktxFile);
success = fwrite(&ktxHeader, sizeof(KTXHeader), 1, ktxFile);
int width = image.width;
int height = image.height;
int dataOffset = 0;
// Save all mipmaps data
for (int i = 0; i < image.mipmaps; i++)
{
unsigned int dataSize = GetPixelDataSize(width, height, image.format);
fwrite(&dataSize, 1, sizeof(unsigned int), ktxFile);
fwrite((unsigned char *)image.data + dataOffset, 1, dataSize, ktxFile);
success = fwrite(&dataSize, sizeof(unsigned int), 1, ktxFile);
success = fwrite((unsigned char *)image.data + dataOffset, dataSize, 1, ktxFile);
width /= 2;
height /= 2;
dataOffset += dataSize;
@ -3020,6 +3115,9 @@ static void SaveKTX(Image image, const char *fileName)
fclose(ktxFile); // Close file pointer
}
// If all data has been written correctly to file, success = 1
return success;
}
#endif
@ -3162,7 +3260,7 @@ static Image LoadPVR(const char *fileName)
}
int dataSize = image.width*image.height*bpp/8; // Total data size in bytes
image.data = (unsigned char*)malloc(dataSize*sizeof(unsigned char));
image.data = (unsigned char *)malloc(dataSize*sizeof(unsigned char));
// Read data from file
fread(image.data, dataSize, 1, pvrFile);
@ -3228,7 +3326,7 @@ static Image LoadASTC(const char *fileName)
TraceLog(LOG_DEBUG, "ASTC image width: %i", image.width);
TraceLog(LOG_DEBUG, "ASTC image height: %i", image.height);
TraceLog(LOG_DEBUG, "ASTC image blocks: %ix%i", astcHeader.blockX, astcHeader.blockY);
image.mipmaps = 1; // NOTE: ASTC format only contains one mipmap level
// NOTE: Each block is always stored in 128bit so we can calculate the bpp

View file

@ -308,12 +308,20 @@ func ImageDraw(dst, src *Image, srcRec, dstRec Rectangle) {
}
// 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()
cposition := position.cptr()
crec := rec.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)