Update C sources, add new functions
This commit is contained in:
parent
adf3401c3c
commit
48689803b8
28 changed files with 2681 additions and 1918 deletions
1018
raylib/audio.c
1018
raylib/audio.c
File diff suppressed because it is too large
Load diff
|
@ -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()
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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()
|
||||
|
|
1006
raylib/core.c
1006
raylib/core.c
File diff suppressed because it is too large
Load diff
198
raylib/external/dirent.h
vendored
Normal file
198
raylib/external/dirent.h
vendored
Normal 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.
|
||||
|
||||
*/
|
660
raylib/external/dr_mp3.h
vendored
660
raylib/external/dr_mp3.h
vendored
File diff suppressed because it is too large
Load diff
20
raylib/external/glfw/include/GLFW/glfw3.h
vendored
20
raylib/external/glfw/include/GLFW/glfw3.h
vendored
|
@ -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.
|
||||
|
|
|
@ -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>
|
||||
|
|
5
raylib/external/glfw/src/input.c
vendored
5
raylib/external/glfw/src/input.c
vendored
|
@ -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];
|
||||
|
||||
|
|
2
raylib/external/glfw/src/internal.h
vendored
2
raylib/external/glfw/src/internal.h
vendored
|
@ -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;
|
||||
|
|
4
raylib/external/glfw/src/win32_platform.h
vendored
4
raylib/external/glfw/src/win32_platform.h
vendored
|
@ -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)
|
||||
|
|
13
raylib/external/glfw/src/window.c
vendored
13
raylib/external/glfw/src/window.c
vendored
|
@ -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);
|
||||
}
|
||||
|
|
5
raylib/external/glfw/src/x11_window.c
vendored
5
raylib/external/glfw/src/x11_window.c
vendored
|
@ -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);
|
||||
|
|
477
raylib/external/mini_al.h
vendored
477
raylib/external/mini_al.h
vendored
|
@ -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.
|
||||
|
|
2
raylib/external/stb_vorbis.c
vendored
2
raylib/external/stb_vorbis.c
vendored
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
286
raylib/shapes.c
286
raylib/shapes.c
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
417
raylib/text.c
417
raylib/text.c
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue