diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 412eeda78..000000000 --- a/.gitattributes +++ /dev/null @@ -1,22 +0,0 @@ -# Auto detect text files and perform LF normalization -* text=auto - -# Custom for Visual Studio -*.cs diff=csharp -*.sln merge=union -*.csproj merge=union -*.vbproj merge=union -*.fsproj merge=union -*.dbproj merge=union - -# Standard to msysgit -*.doc diff=astextplain -*.DOC diff=astextplain -*.docx diff=astextplain -*.DOCX diff=astextplain -*.dot diff=astextplain -*.DOT diff=astextplain -*.pdf diff=astextplain -*.PDF diff=astextplain -*.rtf diff=astextplain -*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore index b9d6bd92f..75d21c923 100644 --- a/.gitignore +++ b/.gitignore @@ -1,215 +1,66 @@ -################# -## Eclipse -################# +# Ignore generated files +# ... -*.pydevproject -.project -.metadata -bin/ -tmp/ -*.tmp -*.bak -*.swp -*~.nib -local.properties -.classpath -.settings/ -.loadpath +# Ignore compiled binaries +src/core.o +src/shapes.o +src/textures.o +src/text.o +src/models.o +src/audio.o +src/vector3.o +src/stb_image.o +src/*.exe +examples/*.o +examples/*.exe -# External tool builders -.externalToolBuilders/ - -# Locally stored "Eclipse launch configurations" -*.launch - -# CDT-specific -.cproject - -# PDT-specific -.buildpath - - -################# -## Visual Studio -################# - -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. - -# User-specific files -*.suo -*.user -*.sln.docstates - -# Build results - -[Dd]ebug/ -[Rr]elease/ -x64/ -build/ -[Bb]in/ -[Oo]bj/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -*_i.c -*_p.c -*.ilk -*.meta -*.obj -*.pch -*.pdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*.log -*.vspscc -*.vssscc -.builds -*.pidb -*.log -*.scc - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opensdf -*.sdf -*.cachefile - -# Visual Studio profiler -*.psess -*.vsp -*.vspx - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# NCrunch -*.ncrunch* -.*crunch*.local.xml - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.Publish.xml -*.pubxml - -# NuGet Packages Directory -## TODO: If you have NuGet Package Restore enabled, uncomment the next line -#packages/ - -# Windows Azure Build Output -csx -*.build.csdef - -# Windows Store app package directory -AppPackages/ - -# Others -sql/ -*.Cache -ClientBin/ -[Ss]tyle[Cc]op.* -~$* -*~ -*.dbmdl -*.[Pp]ublish.xml -*.pfx -*.publishsettings - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file to a newer -# Visual Studio version. Backup files are not needed, because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm - -# SQL Server files -App_Data/*.mdf -App_Data/*.ldf - -############# -## Windows detritus -############# - -# Windows image file caches +# Ignore thumbnails created by windows Thumbs.db -ehthumbs.db -# Folder config file -Desktop.ini +# Ignore files build by Visual Studio +*.obj +*.exe +*.pdb +*.aps +*.vcproj.*.user +*.vcxproj* +*.sln +*.vspscc +*_i.c +*.i +*.icf +*_p.c +*.ncb +*.suo +*.tlb +*.tlh +*.bak +*.cache +*.ilk +*.log +[Bb]in +[Dd]ebug/ +[Dd]ebug.win32/ +*.sbr +*.sdf +obj/ +[Rr]elease/ +[Rr]elease.win32/ +_ReSharper*/ +[Tt]est[Rr]esult* +ipch/ +*.opensdf -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# Mac crap +# Ignore files build by xcode +*.mode*v* +*.pbxuser +*.xcbkptlist +*.xcscheme +*.xcworkspacedata +*.xcuserstate +*.xccheckout +xcschememanagement.plist .DS_Store - - -############# -## Python -############# - -*.py[co] - -# Packages -*.egg -*.egg-info -dist/ -build/ -eggs/ -parts/ -var/ -sdist/ -develop-eggs/ -.installed.cfg - -# Installer logs -pip-log.txt - -# Unit test / coverage reports -.coverage -.tox - -#Translations -*.mo - -#Mr Developer -.mr.developer.cfg +._.* +xcuserdata/ +DerivedData/ \ No newline at end of file diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 000000000..dc47ee53d --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,17 @@ +changelog +--------- + +Current: raylib 1.0 (November 2013) + + +------------------------------------------- +Release: raylib 1.0.0 (18 November 2013) +------------------------------------------- +* Initial version +* 6 Modules provided: + - core: basic window/context creation functions, input management, timming functions + - shapes: basic shapes drawing functions + - textures: image data loading and conversion to OpenGL textures + - text: text drawing, sprite fonts loading, default font loading + - models: basic 3d shapes drawing, OBJ models loading and drawing + - audio: audio device initialization, WAV files loading and playing \ No newline at end of file diff --git a/HELPME b/HELPME new file mode 100644 index 000000000..98d1c7b36 --- /dev/null +++ b/HELPME @@ -0,0 +1,39 @@ +help me! +======== + +I’m working hard on raylib but my resources are quite limited. If you enjoy raylib and want to help / contribute in some way, +please, [let me know][raysan5]. + +The following help is highly appreciated: + + - C programming - Can you write / review / test / improve the code? + - Translators / Localizators - Can you translate raylib to another language? + - Documentation / Tutorials / Example writters - Can you write some tutorial / example? + - Web Development (I need a lot of help with this...) - Can you help with the web? + - Porting to Linux and OSX - Can you compile and test raylib on another OS? + - Testers of current features and multiple systems - Can you find some bug on raylib? + +If you can not help on any of the above points but you still want to contribute in some way... please, consider helping +with a small donation (just some euros...). It will really motivate to continue improving this project (and pay some bills… or some coffee). + +[Donation Page] (www.raylib.com/helpme.htm) + +raylib philosophy +------------------ + + * raylib is a tool to LEARN videogames programming, every single function in raylib should be a tutorial on itself. + * raylib is SIMPLE and EASY-TO-USE, I tried to keep it compact with a small set of functions, if a function is too complex or + has not a clear usefulness, better not to include it. + * raylib is open source and free; educators and institutions can use this tool to TEACH videogames programming completely by free. + * raylib is, hopefully, collaborative; contribution of tutorials / code-examples / bugs-solving / code-comments are highly appreciated. + * raylib's license (and its external libs respective licenses) allow using it for commercial products. + +contact +------- + + * Webpage: [http://www.raylib.com](http://www.raylib.com) + * Twitter: [http://www.twitter.com/raysan5](http://www.twitter.com/raysan5) + * Facebook: [http://www.facebook.com/raylibgames](http://www.facebook.com/raylibgames) + + +[raysan5]: mailto:raysan@raysanweb.com "Ramon Santamaria - Ray San" \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..2b608cecb --- /dev/null +++ b/LICENSE @@ -0,0 +1,30 @@ +license +======= + +source code +----------- + +raylib is licensed under an unmodified zlib/libpng license, which is an OSI-certified, +BSD-like license that allows static linking with closed source software: + +Copyright (c) 2013 Ramon Santamaria (Ray San - raysan@raysanweb.com) + +This software is provided "as-is", without any express or implied warranty. In no event +will the authors be held liable for any damages arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, including commercial +applications, and to alter it and redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not claim that you + wrote the original software. If you use this software in a product, an acknowledgment + in the product documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be misrepresented + as being the original software. + + 3. This notice may not be removed or altered from any source distribution. + +fonts +------ + +...soon... \ No newline at end of file diff --git a/README b/README index 01b405b1d..62527e270 100644 --- a/README +++ b/README @@ -1,2 +1,115 @@ -It's empy here... \ No newline at end of file + + +raylib +======= + +about +----- + +raylib is a simple and easy-to-use library to learn C videogames programming. + +raylib is highly inspired by Borland BGI graphics lib (more specifically WinBGI) and by XNA framework. +Allegro and SDL have also been analyzed for reference. + +Want to see how easy is making games with raylib? Jump to [code examples!] (http://www.raylib.com/examples.htm) + +history +------- + +I've developed videogames for some years and last year I had to taught videogames development +to young people with artistic profile, most of them had never written a single line of code. + +I started with C language basis and, after searching for the most simple and easy-to-use library to teach +videogames programming, I found WinBGI; it was great and it worked very well with students, in just a +couple of weeks that people that had never written a single line of code were able to program (and understand) +a simple PONG and some of them even a BREAKOUT! + +But WinBGI was not the clearer and most organized lib. There were a lot of things I found useless and +confusing and some function names were not clear enough for most of the students; not to mention points +like no transparencies support or no hardware acceleration. + +So, I decided to create my own lib, hardware accelerated, clear function names, quite organized, well structured, +plain C coding and, the most important, primarily intended to LEARN videogames programming. + +I've coded quite a lot in C# and XNA and I really love it (in fact, my students learn C# with XNA after C), +so, I decided to use C# language notation and XNA naming conventions. That way, students can jump from +raylib to XNA (or MonoGame) extremely easily. + +raylib started as a weekend project and after three months of hard work, here it is the first version. + +Enjoy it. + +features +-------- + + * Written in plain C code (C99) + * Uses C# PascalCase/camelCase notation + * Hardware accelerated using OpenGL 1.1 + * Transparencies support (RGBA Colors) + * Custom color palette for better use on white background + * Basic 3D Support (camera, basic models, OBJ models, etc) + * Powerful Text module with SpriteFonts support + +raylib uses on its core module the outstanding [GLFW3] (http://glfw.org/) library. The best option by far I found for +window/context and input management (clean, focused, great license, well documented, modern, ...). + +raylib is licensed under a zlib/libpng license like GLFW3. View [LICENSE] (link to license file). + +tool requirements +------------------ + +raylib has been developed using exclusively two tools: + + * Notepad++ (text editor) - [http://notepad-plus-plus.org/](http://notepad-plus-plus.org/) + * MinGW (GCC compiler) - [http://www.mingw.org/](http://www.mingw.org/) + +Those are the tools I recommended to develop with raylib, actually, my students develop using this tools. +I believe those are the best tools to train spartan-programmers. + +Someone could argue about debugging. raylib is a library intended for learning and I think C it's a clear enough language +to allow writing small-mid size programs with a printf-based debugging. All raylib examples have also been written this way. + +building +-------- + +raylib could be build with the following command lines (Using GCC compiler): + + cd raylib/src + gcc -c core.c -std=c99 -Wall + gcc -c shapes.c -std=c99 -Wall + gcc -c textures.c -std=c99 -Wall + gcc -c stb_image.c -std=c99 -Wall + gcc -c text.c -std=c99 -Wall + gcc -c models.c -std=c99 -Wall + gcc -c vector3.c -std=c99 -Wall + gcc -c audio.c -std=c99 -Wall + ar rcs raylib.a core.o shapes.o textures.o stb_image.o text.o models.o vector3.o audio.o + +To compile examples, make sure raylib.h is placed in include path and libraries raylib (libraylib.a) and glfw3 (libglfw3.a) +are placed in the libraries path. It's also recommended to link with file icon.o for fancy raylib icon usage. + + cd raylib/src/examples + gcc -o test_code.exe test_code.c icon.o -lraylib -lglfw3 -lopengl32 -lgdi32 -std=c99 -Wl,--subsystem,windows + +contact +------- + + * Webpage: [http://www.raylib.com](http://www.raylib.com) + * Twitter: [http://www.twitter.com/raysan5](http://www.twitter.com/raysan5) + * Facebook: [http://www.facebook.com/raylibgames](http://www.facebook.com/raylibgames) + +If you are using raylib and you enjoy it, please, [let me know][raysan5]. + +If you feel you can help, then, [helpme!] (http://www.raylib.com/helpme.htm) + +acknowledgments +--------------- + +The following people have contributed in some way to make raylib project a reality. Big thanks to them! + + - Zopokx + - Elendow + + +[raysan5]: mailto:raysan@raysanweb.com "Ramon Santamaria - Ray San" \ No newline at end of file diff --git a/ROADMAP b/ROADMAP new file mode 100644 index 000000000..dc22e5640 --- /dev/null +++ b/ROADMAP @@ -0,0 +1,22 @@ +roadmap +======= + +First version of raylib is quite complete and functional but there is still a lot of things I would like to improve. +Here it is a list of features I would like to add and functions to improve. + +Around the source code there are some TODO points with pending revisions/bugs and ere it is a list of features I would like to add. + +raylib v1.x + + - Review Billboard Drawing functions + - Review Heightmap Loading and Drawing functions + - Lighting support (only 3d mode) - CreateLight() + - Simple Collision Detection functions - CheckCollisionRects(), CheckCollisionCircles() + - Default scene Camera controls (zoom, pan, rotate) + - Basic Procedural Texture / Image generation (Gradient, Checked, Spot, Noise, Cellular) + - Software mipmapping generation and POT conversion (custom implementation) + - Comments / Functions translation (?) + +Any feature missing? Do you have a request? [Let me know!][raysan5] + +[raysan5]: mailto:raysan@raysanweb.com "Ramon Santamaria - Ray San" \ No newline at end of file diff --git a/examples/ex01_basic_window.c b/examples/ex01_basic_window.c new file mode 100644 index 000000000..7b40bbd05 --- /dev/null +++ b/examples/ex01_basic_window.c @@ -0,0 +1,50 @@ +/******************************************************************************************* +* +* raylib example 01 - Basic Window +* +* This example has been created using raylib 1.0 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Copyright (c) 2013 Ramon Santamaria (Ray San - raysan@raysanweb.com) +* +********************************************************************************************/ + +#include "raylib.h" + +int main() +{ + int screenWidth = 800; + int screenHeight = 450; + + // Initialization + //--------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib example 01a - basic window"); // Window and context initialization + //---------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //----------------------------------------------------- + // TODO: Update your variables here + //----------------------------------------------------- + + // Draw + //----------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + DrawText("Congrats! You created your first window!", 190, 200, 20, 1, LIGHTGRAY); + + EndDrawing(); + //----------------------------------------------------- + } + + // De-Initialization + //--------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //---------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/ex01_basic_window.png b/examples/ex01_basic_window.png new file mode 100644 index 000000000..346184417 Binary files /dev/null and b/examples/ex01_basic_window.png differ diff --git a/examples/ex02a_logo_raylib.c b/examples/ex02a_logo_raylib.c new file mode 100644 index 000000000..cbd67b200 --- /dev/null +++ b/examples/ex02a_logo_raylib.c @@ -0,0 +1,54 @@ +/******************************************************************************************* +* +* raylib example 02a - Draw raylib logo +* +* This example has been created using raylib 1.0 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Copyright (c) 2013 Ramon Santamaria (Ray San - raysan@raysanweb.com) +* +********************************************************************************************/ + +#include "raylib.h" + +int main() +{ + int screenWidth = 800; + int screenHeight = 450; + + // Initialization + //--------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib example 02a - raylib logo"); // Window and context initialization + //---------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //----------------------------------------------------- + // TODO: Update your variables here + //----------------------------------------------------- + + // Draw + //----------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + DrawRectangle(screenWidth/2 - 128, screenHeight/2 - 128, 256, 256, BLACK); + DrawRectangle(screenWidth/2 - 112, screenHeight/2 - 112, 224, 224, RAYWHITE); + DrawText("raylib", 356, 273, 50, 1, BLACK); + + DrawText("this is NOT a texture!", 350, 370, 10, 1, GRAY); + + EndDrawing(); + //----------------------------------------------------- + } + + // De-Initialization + //--------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //---------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/ex02a_logo_raylib.png b/examples/ex02a_logo_raylib.png new file mode 100644 index 000000000..6b385f7c8 Binary files /dev/null and b/examples/ex02a_logo_raylib.png differ diff --git a/examples/ex02b_shapes.c b/examples/ex02b_shapes.c new file mode 100644 index 000000000..93a0bccdc --- /dev/null +++ b/examples/ex02b_shapes.c @@ -0,0 +1,73 @@ +/******************************************************************************************* +* +* raylib example 02b - Draw basic shapes 2d (rectangle, circle, line...) +* +* This example has been created using raylib 1.0 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Copyright (c) 2013 Ramon Santamaria (Ray San - raysan@raysanweb.com) +* +********************************************************************************************/ + +#include "raylib.h" + +int main() +{ + int screenWidth = 800; + int screenHeight = 450; + + // Initialization + //--------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib example 02b - basic shapes drawing"); // Window and context initialization + //---------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //----------------------------------------------------- + // TODO: Update your variables here + //----------------------------------------------------- + + // Draw + //----------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + // TODO: draw some shapes... with names... :P +/* + void DrawPixel(int posX, int posY, Color color); // Draw a pixel + void DrawPixelV(Vector2 position, Color color); // Draw a pixel (Vector version) + void DrawLine(int startPosX, int startPosY, int endPosX, int endPosY, Color color); // Draw a line + void DrawLineV(Vector2 startPos, Vector2 endPos, Color color); // Draw a line (Vector version) + void DrawCircle(int centerX, int centerY, float radius, Color color); // Draw a color-filled circle + void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2); // Draw a gradient-filled circle + void DrawCircleV(Vector2 center, float radius, Color color); // Draw a color-filled circle (Vector version) + void DrawCircleLines(int centerX, int centerY, float radius, Color color); // Draw circle outline + void DrawRectangle(int posX, int posY, int width, int height, Color color); // Draw a color-filled rectangle + void DrawRectangleRec(Rectangle rec, Color color); // Draw a color-filled rectangle + void DrawRectangleGradient(int posX, int posY, int width, int height, Color color1, Color color2); // Draw a gradient-filled rectangle + void DrawRectangleV(Vector2 position, Vector2 size, Color color); // Draw a color-filled rectangle (Vector version) + void DrawRectangleLines(int posX, int posY, int width, int height, Color color); // Draw rectangle outline + void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw a color-filled triangle + void DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw triangle outline + void DrawPoly(Vector2 *points, int numPoints, Color color); // Draw a closed polygon defined by points + void DrawPolyLine(Vector2 *points, int numPoints, Color color); // Draw polygon lines +*/ + DrawRectangle(screenWidth/4 - 50, screenHeight/2 - 100, 100, 100, GOLD); + DrawCircle(3*screenWidth/4, screenHeight/2 - 50, 50, MAROON); + + DrawText("_____", 320, 280, 50, 1, BLACK); + + EndDrawing(); + //----------------------------------------------------- + } + + // De-Initialization + //--------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //---------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/ex02c_color_palette.c b/examples/ex02c_color_palette.c new file mode 100644 index 000000000..3a7352952 --- /dev/null +++ b/examples/ex02c_color_palette.c @@ -0,0 +1,95 @@ +/******************************************************************************************* +* +* raylib example 02c - Draw raylib custom color palette +* +* This example has been created using raylib 1.0 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Copyright (c) 2013 Ramon Santamaria (Ray San - raysan@raysanweb.com) +* +********************************************************************************************/ + +#include "raylib.h" + +int main() +{ + int screenWidth = 800; + int screenHeight = 450; + + // Initialization + //--------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib example 02c - raylib color palette"); // Window and context initialization + //---------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //----------------------------------------------------- + // TODO: Update your variables here + //----------------------------------------------------- + + // Draw + //----------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + DrawText("raylib color palette", 28, 42, 20, 2, BLACK); + + DrawRectangle(26, 80, 100, 100, DARKGRAY); + DrawRectangle(26, 188, 100, 100, GRAY); + DrawRectangle(26, 296, 100, 100, LIGHTGRAY); + DrawRectangle(134, 80, 100, 100, MAROON); + DrawRectangle(134, 188, 100, 100, RED); + DrawRectangle(134, 296, 100, 100, PINK); + DrawRectangle(242, 80, 100, 100, ORANGE); + DrawRectangle(242, 188, 100, 100, GOLD); + DrawRectangle(242, 296, 100, 100, YELLOW); + DrawRectangle(350, 80, 100, 100, DARKGREEN); + DrawRectangle(350, 188, 100, 100, LIME); + DrawRectangle(350, 296, 100, 100, GREEN); + DrawRectangle(458, 80, 100, 100, DARKBLUE); + DrawRectangle(458, 188, 100, 100, BLUE); + DrawRectangle(458, 296, 100, 100, SKYBLUE); + DrawRectangle(566, 80, 100, 100, DARKPURPLE); + DrawRectangle(566, 188, 100, 100, VIOLET); + DrawRectangle(566, 296, 100, 100, PURPLE); + DrawRectangle(674, 80, 100, 100, DARKBROWN); + DrawRectangle(674, 188, 100, 100, BROWN); + DrawRectangle(674, 296, 100, 100, BEIGE); + + + DrawText("DARKGRAY", 57, 166, 10, 2, BLACK); + DrawText("GRAY", 89, 274, 10, 2, BLACK); + DrawText("LIGHTGRAY", 51, 382, 10, 2, BLACK); + DrawText("MAROON", 180, 166, 10, 2, BLACK); + DrawText("RED", 207, 274, 10, 2, BLACK); + DrawText("PINK", 200, 382, 10, 2, BLACK); + DrawText("ORANGE", 290, 166, 10, 2, BLACK); + DrawText("GOLD", 306, 274, 10, 2, BLACK); + DrawText("YELLOW", 290, 382, 10, 2, BLACK); + DrawText("DARKGREEN", 374, 166, 10, 2, BLACK); + DrawText("LIME", 417, 274, 10, 2, BLACK); + DrawText("GREEN", 407, 382, 10, 2, BLACK); + DrawText("DARKBLUE", 491, 166, 10, 2, BLACK); + DrawText("BLUE", 523, 274, 10, 2, BLACK); + DrawText("SKYBLUE", 499, 382, 10, 2, BLACK); + DrawText("DARKPURPLE", 582, 166, 10, 2, BLACK); + DrawText("VIOLET", 617, 274, 10, 2, BLACK); + DrawText("PURPLE", 615, 382, 10, 2, BLACK); + DrawText("DARKBROWN", 695, 166, 10, 2, BLACK); + DrawText("BROWN", 728, 274, 10, 2, BLACK); + DrawText("BEIGE", 733, 382, 10, 2, BLACK); + + EndDrawing(); + //----------------------------------------------------- + } + + // De-Initialization + //--------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //---------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/ex02c_color_palette.png b/examples/ex02c_color_palette.png new file mode 100644 index 000000000..04583e9a6 Binary files /dev/null and b/examples/ex02c_color_palette.png differ diff --git a/examples/ex03a_input_keys.c b/examples/ex03a_input_keys.c new file mode 100644 index 000000000..85deb2b4a --- /dev/null +++ b/examples/ex03a_input_keys.c @@ -0,0 +1,57 @@ +/******************************************************************************************* +* +* raylib example 03a - Keyboard input +* +* This example has been created using raylib 1.0 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Copyright (c) 2013 Ramon Santamaria (Ray San - raysan@raysanweb.com) +* +********************************************************************************************/ + +#include "raylib.h" + +int main() +{ + int screenWidth = 800; + int screenHeight = 450; + + Vector2 ballPosition = { screenWidth/2, screenHeight/2 }; + + // Initialization + //--------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib example 05 - keyboard input"); // Window and context initialization + //---------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //----------------------------------------------------- + if (IsKeyPressed(KEY_RIGHT)) ballPosition.x += 0.8; + if (IsKeyPressed(KEY_LEFT)) ballPosition.x -= 0.8; + if (IsKeyPressed(KEY_UP)) ballPosition.y -= 0.8; + if (IsKeyPressed(KEY_DOWN)) ballPosition.y += 0.8; + //----------------------------------------------------- + + // Draw + //----------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + DrawText("move the ball with arrow keys", 10, 10, 20, 1, DARKGRAY); + + DrawCircleV(ballPosition, 50, MAROON); + + EndDrawing(); + //----------------------------------------------------- + } + + // De-Initialization + //--------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //---------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/ex03a_input_keys.png b/examples/ex03a_input_keys.png new file mode 100644 index 000000000..483703213 Binary files /dev/null and b/examples/ex03a_input_keys.png differ diff --git a/examples/ex03b_input_mouse.c b/examples/ex03b_input_mouse.c new file mode 100644 index 000000000..996cd73e0 --- /dev/null +++ b/examples/ex03b_input_mouse.c @@ -0,0 +1,63 @@ +/******************************************************************************************* +* +* raylib example 03b - Mouse input +* +* This example has been created using raylib 1.0 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Copyright (c) 2013 Ramon Santamaria (Ray San - raysan@raysanweb.com) +* +********************************************************************************************/ + +#include "raylib.h" + +int main() +{ + int screenWidth = 800; + int screenHeight = 450; + + Vector2 ballPosition = { -100.0, -100.0 }; + int counter = 0; + int mouseX, mouseY; + + // Initialization + //--------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib example 06 - mouse input"); // Window and context initialization + //---------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //----------------------------------------------------- + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + { + mouseX = GetMouseX(); + mouseY = GetMouseY(); + + ballPosition.x = (float)mouseX; + ballPosition.y = (float)mouseY; + } + //----------------------------------------------------- + + // Draw + //----------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + DrawCircleV(ballPosition, 40, GOLD); + + DrawText("mouse click to draw the ball", 10, 10, 20, 1, DARKGRAY); + + EndDrawing(); + //----------------------------------------------------- + } + + // De-Initialization + //--------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //---------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/ex03b_input_mouse.png b/examples/ex03b_input_mouse.png new file mode 100644 index 000000000..765672816 Binary files /dev/null and b/examples/ex03b_input_mouse.png differ diff --git a/examples/ex03c_input_gamepad.c b/examples/ex03c_input_gamepad.c new file mode 100644 index 000000000..377ac7f52 --- /dev/null +++ b/examples/ex03c_input_gamepad.c @@ -0,0 +1,67 @@ +/******************************************************************************************* +* +* raylib example 03c - Gamepad input +* +* This example has been created using raylib 1.0 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Copyright (c) 2013 Ramon Santamaria (Ray San - raysan@raysanweb.com) +* +********************************************************************************************/ + +#include "raylib.h" + +int main() +{ + int screenWidth = 800; + int screenHeight = 450; + + Vector2 ballPosition = { screenWidth/2, screenHeight/2 }; + Vector2 gamepadMove = { 0, 0 }; + + // Initialization + //--------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib example 01 - gamepad input"); // Window and context initialization + //---------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //----------------------------------------------------- + if (IsGamepadAvailable(GAMEPAD_PLAYER1)) + { + gamepadMove = GetGamepadMovement(GAMEPAD_PLAYER1); + + ballPosition.x += gamepadMove.x; + ballPosition.y -= gamepadMove.y; + + if (IsGamepadButtonPressed(GAMEPAD_PLAYER1, GAMEPAD_BUTTON_A)) + { + ballPosition.x = screenWidth/2; + ballPosition.y = screenHeight/2; + } + } + //----------------------------------------------------- + + // Draw + //----------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + DrawText("move the ball with gamepad", 10, 10, 20, 1, DARKGRAY); + + DrawCircleV(ballPosition, 50, MAROON); + + EndDrawing(); + //----------------------------------------------------- + } + + // De-Initialization + //--------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //---------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/ex03c_input_gamepad.png b/examples/ex03c_input_gamepad.png new file mode 100644 index 000000000..f7e556584 Binary files /dev/null and b/examples/ex03c_input_gamepad.png differ diff --git a/examples/ex04a_textures.c b/examples/ex04a_textures.c new file mode 100644 index 000000000..5c2bf6504 --- /dev/null +++ b/examples/ex04a_textures.c @@ -0,0 +1,57 @@ +/******************************************************************************************* +* +* raylib example 04a - Texture loading and drawing +* +* This example has been created using raylib 1.0 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Copyright (c) 2013 Ramon Santamaria (Ray San - raysan@raysanweb.com) +* +********************************************************************************************/ + +#include "raylib.h" + +int main() +{ + int screenWidth = 800; + int screenHeight = 450; + + // Initialization + //--------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "raylib example 04a - texture loading and drawing"); // Window and context initialization + + // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) + Texture2D texture = LoadTexture("resources/raylib_logo.png"); // Texture loading + //---------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //----------------------------------------------------- + // TODO: Update your variables here + //----------------------------------------------------- + + // Draw + //----------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + DrawTexture(texture, screenWidth/2 - texture.width/2, screenHeight/2 - texture.height/2, WHITE); + + DrawText("this IS a texture!", 360, 370, 10, 1, GRAY); + + EndDrawing(); + //----------------------------------------------------- + } + + // De-Initialization + //--------------------------------------------------------- + UnloadTexture(texture); // Texture unloading + + CloseWindow(); // Close window and OpenGL context + //---------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/ex04a_textures.png b/examples/ex04a_textures.png new file mode 100644 index 000000000..c18bf8807 Binary files /dev/null and b/examples/ex04a_textures.png differ diff --git a/examples/resources/catsham.png b/examples/resources/catsham.png new file mode 100644 index 000000000..8d7978e06 Binary files /dev/null and b/examples/resources/catsham.png differ diff --git a/examples/resources/catwhite.png b/examples/resources/catwhite.png new file mode 100644 index 000000000..b849c4c07 Binary files /dev/null and b/examples/resources/catwhite.png differ diff --git a/examples/resources/coin.wav b/examples/resources/coin.wav new file mode 100644 index 000000000..6007509bf Binary files /dev/null and b/examples/resources/coin.wav differ diff --git a/examples/resources/raylib_logo.png b/examples/resources/raylib_logo.png new file mode 100644 index 000000000..665456277 Binary files /dev/null and b/examples/resources/raylib_logo.png differ diff --git a/examples/resources/spring.wav b/examples/resources/spring.wav new file mode 100644 index 000000000..c7fbf1b9c Binary files /dev/null and b/examples/resources/spring.wav differ diff --git a/external/glfw3/include/GLFW/glfw3.h b/external/glfw3/include/GLFW/glfw3.h new file mode 100644 index 000000000..c54abde84 --- /dev/null +++ b/external/glfw3/include/GLFW/glfw3.h @@ -0,0 +1,2269 @@ +/************************************************************************* + * GLFW - An OpenGL library + * API version: 3.0 + * WWW: http://www.glfw.org/ + *------------------------------------------------------------------------ + * Copyright (c) 2002-2006 Marcus Geelnard + * Copyright (c) 2006-2010 Camilla Berglund + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would + * be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + *************************************************************************/ + +#ifndef _glfw3_h_ +#define _glfw3_h_ + +#ifdef __cplusplus +extern "C" { +#endif + + +/************************************************************************* + * Doxygen documentation + *************************************************************************/ + +/*! @defgroup clipboard Clipboard support + */ +/*! @defgroup context Context handling + */ +/*! @defgroup error Error handling + */ +/*! @defgroup gamma Gamma ramp support + */ +/*! @defgroup init Initialization and version information + */ +/*! @defgroup input Input handling + */ +/*! @defgroup monitor Monitor handling + */ +/*! @defgroup time Time input + */ +/*! @defgroup window Window handling + * + * This is the reference documentation for the window handling API, including + * creation, deletion and event polling. For more information, see the + * [article on window handling](@ref window). + */ + + +/************************************************************************* + * Global definitions + *************************************************************************/ + +/* ------------------- BEGIN SYSTEM/COMPILER SPECIFIC -------------------- */ + +/* Please report any problems that you find with your compiler, which may + * be solved in this section! There are several compilers that I have not + * been able to test this file with yet. + * + * First: If we are we on Windows, we want a single define for it (_WIN32) + * (Note: For Cygwin the compiler flag -mwin32 should be used, but to + * make sure that things run smoothly for Cygwin users, we add __CYGWIN__ + * to the list of "valid Win32 identifiers", which removes the need for + * -mwin32) + */ +#if !defined(_WIN32) && (defined(__WIN32__) || defined(WIN32) || defined(__CYGWIN__)) + #define _WIN32 +#endif /* _WIN32 */ + +/* In order for extension support to be portable, we need to define an + * OpenGL function call method. We use the keyword APIENTRY, which is + * defined for Win32. (Note: Windows also needs this for ) + */ +#ifndef APIENTRY + #ifdef _WIN32 + #define APIENTRY __stdcall + #else + #define APIENTRY + #endif +#endif /* APIENTRY */ + +/* The following three defines are here solely to make some Windows-based + * files happy. Theoretically we could include , but + * it has the major drawback of severely polluting our namespace. + */ + +/* Under Windows, we need WINGDIAPI defined */ +#if !defined(WINGDIAPI) && defined(_WIN32) + #if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__POCC__) + /* Microsoft Visual C++, Borland C++ Builder and Pelles C */ + #define WINGDIAPI __declspec(dllimport) + #elif defined(__LCC__) + /* LCC-Win32 */ + #define WINGDIAPI __stdcall + #else + /* Others (e.g. MinGW, Cygwin) */ + #define WINGDIAPI extern + #endif + #define GLFW_WINGDIAPI_DEFINED +#endif /* WINGDIAPI */ + +/* Some files also need CALLBACK defined */ +#if !defined(CALLBACK) && defined(_WIN32) + #if defined(_MSC_VER) + /* Microsoft Visual C++ */ + #if (defined(_M_MRX000) || defined(_M_IX86) || defined(_M_ALPHA) || defined(_M_PPC)) && !defined(MIDL_PASS) + #define CALLBACK __stdcall + #else + #define CALLBACK + #endif + #else + /* Other Windows compilers */ + #define CALLBACK __stdcall + #endif + #define GLFW_CALLBACK_DEFINED +#endif /* CALLBACK */ + +/* Most variants on Windows need wchar_t */ +#if defined(_WIN32) + #include +#endif + + +/* ---------------- GLFW related system specific defines ----------------- */ + +#if defined(GLFW_DLL) && defined(_GLFW_BUILD_DLL) + #error "You must not have both GLFW_DLL and _GLFW_BUILD_DLL defined" +#endif + +#if defined(_WIN32) && defined(_GLFW_BUILD_DLL) + + /* We are building a Win32 DLL */ + #define GLFWAPI __declspec(dllexport) + +#elif defined(_WIN32) && defined(GLFW_DLL) + + /* We are calling a Win32 DLL */ + #if defined(__LCC__) + #define GLFWAPI extern + #else + #define GLFWAPI __declspec(dllimport) + #endif + +#elif defined(__GNUC__) && defined(_GLFW_BUILD_DLL) + + #define GLFWAPI __attribute__((visibility("default"))) + +#else + + /* We are either building/calling a static lib or we are non-win32 */ + #define GLFWAPI + +#endif + +/* -------------------- END SYSTEM/COMPILER SPECIFIC --------------------- */ + +/* Include the chosen client API headers. + */ +#if defined(__APPLE_CC__) + #if defined(GLFW_INCLUDE_GLCOREARB) + #include + #elif !defined(GLFW_INCLUDE_NONE) + #define GL_GLEXT_LEGACY + #include + #endif + #if defined(GLFW_INCLUDE_GLU) + #include + #endif +#else + #if defined(GLFW_INCLUDE_GLCOREARB) + #include + #elif defined(GLFW_INCLUDE_ES1) + #include + #elif defined(GLFW_INCLUDE_ES2) + #include + #elif defined(GLFW_INCLUDE_ES3) + #include + #elif !defined(GLFW_INCLUDE_NONE) + #include + #endif + #if defined(GLFW_INCLUDE_GLU) + #include + #endif +#endif + + +/************************************************************************* + * GLFW API tokens + *************************************************************************/ + +/*! @name GLFW version macros + * @{ */ +/*! @brief The major version number of the GLFW library. + * + * This is incremented when the API is changed in non-compatible ways. + * @ingroup init + */ +#define GLFW_VERSION_MAJOR 3 +/*! @brief The minor version number of the GLFW library. + * + * This is incremented when features are added to the API but it remains + * backward-compatible. + * @ingroup init + */ +#define GLFW_VERSION_MINOR 0 +/*! @brief The revision number of the GLFW library. + * + * This is incremented when a bug fix release is made that does not contain any + * API changes. + * @ingroup init + */ +#define GLFW_VERSION_REVISION 1 +/*! @} */ + +/*! @name Key and button actions + * @{ */ +/*! @brief The key or button was released. + * @ingroup input + */ +#define GLFW_RELEASE 0 +/*! @brief The key or button was pressed. + * @ingroup input + */ +#define GLFW_PRESS 1 +/*! @brief The key was held down until it repeated. + * @ingroup input + */ +#define GLFW_REPEAT 2 +/*! @} */ + +/*! @defgroup keys Keyboard keys + * + * These key codes are inspired by the *USB HID Usage Tables v1.12* (p. 53-60), + * but re-arranged to map to 7-bit ASCII for printable keys (function keys are + * put in the 256+ range). + * + * The naming of the key codes follow these rules: + * - The US keyboard layout is used + * - Names of printable alpha-numeric characters are used (e.g. "A", "R", + * "3", etc.) + * - For non-alphanumeric characters, Unicode:ish names are used (e.g. + * "COMMA", "LEFT_SQUARE_BRACKET", etc.). Note that some names do not + * correspond to the Unicode standard (usually for brevity) + * - Keys that lack a clear US mapping are named "WORLD_x" + * - For non-printable keys, custom names are used (e.g. "F4", + * "BACKSPACE", etc.) + * + * @ingroup input + * @{ + */ + +/* The unknown key */ +#define GLFW_KEY_UNKNOWN -1 + +/* Printable keys */ +#define GLFW_KEY_SPACE 32 +#define GLFW_KEY_APOSTROPHE 39 /* ' */ +#define GLFW_KEY_COMMA 44 /* , */ +#define GLFW_KEY_MINUS 45 /* - */ +#define GLFW_KEY_PERIOD 46 /* . */ +#define GLFW_KEY_SLASH 47 /* / */ +#define GLFW_KEY_0 48 +#define GLFW_KEY_1 49 +#define GLFW_KEY_2 50 +#define GLFW_KEY_3 51 +#define GLFW_KEY_4 52 +#define GLFW_KEY_5 53 +#define GLFW_KEY_6 54 +#define GLFW_KEY_7 55 +#define GLFW_KEY_8 56 +#define GLFW_KEY_9 57 +#define GLFW_KEY_SEMICOLON 59 /* ; */ +#define GLFW_KEY_EQUAL 61 /* = */ +#define GLFW_KEY_A 65 +#define GLFW_KEY_B 66 +#define GLFW_KEY_C 67 +#define GLFW_KEY_D 68 +#define GLFW_KEY_E 69 +#define GLFW_KEY_F 70 +#define GLFW_KEY_G 71 +#define GLFW_KEY_H 72 +#define GLFW_KEY_I 73 +#define GLFW_KEY_J 74 +#define GLFW_KEY_K 75 +#define GLFW_KEY_L 76 +#define GLFW_KEY_M 77 +#define GLFW_KEY_N 78 +#define GLFW_KEY_O 79 +#define GLFW_KEY_P 80 +#define GLFW_KEY_Q 81 +#define GLFW_KEY_R 82 +#define GLFW_KEY_S 83 +#define GLFW_KEY_T 84 +#define GLFW_KEY_U 85 +#define GLFW_KEY_V 86 +#define GLFW_KEY_W 87 +#define GLFW_KEY_X 88 +#define GLFW_KEY_Y 89 +#define GLFW_KEY_Z 90 +#define GLFW_KEY_LEFT_BRACKET 91 /* [ */ +#define GLFW_KEY_BACKSLASH 92 /* \ */ +#define GLFW_KEY_RIGHT_BRACKET 93 /* ] */ +#define GLFW_KEY_GRAVE_ACCENT 96 /* ` */ +#define GLFW_KEY_WORLD_1 161 /* non-US #1 */ +#define GLFW_KEY_WORLD_2 162 /* non-US #2 */ + +/* Function keys */ +#define GLFW_KEY_ESCAPE 256 +#define GLFW_KEY_ENTER 257 +#define GLFW_KEY_TAB 258 +#define GLFW_KEY_BACKSPACE 259 +#define GLFW_KEY_INSERT 260 +#define GLFW_KEY_DELETE 261 +#define GLFW_KEY_RIGHT 262 +#define GLFW_KEY_LEFT 263 +#define GLFW_KEY_DOWN 264 +#define GLFW_KEY_UP 265 +#define GLFW_KEY_PAGE_UP 266 +#define GLFW_KEY_PAGE_DOWN 267 +#define GLFW_KEY_HOME 268 +#define GLFW_KEY_END 269 +#define GLFW_KEY_CAPS_LOCK 280 +#define GLFW_KEY_SCROLL_LOCK 281 +#define GLFW_KEY_NUM_LOCK 282 +#define GLFW_KEY_PRINT_SCREEN 283 +#define GLFW_KEY_PAUSE 284 +#define GLFW_KEY_F1 290 +#define GLFW_KEY_F2 291 +#define GLFW_KEY_F3 292 +#define GLFW_KEY_F4 293 +#define GLFW_KEY_F5 294 +#define GLFW_KEY_F6 295 +#define GLFW_KEY_F7 296 +#define GLFW_KEY_F8 297 +#define GLFW_KEY_F9 298 +#define GLFW_KEY_F10 299 +#define GLFW_KEY_F11 300 +#define GLFW_KEY_F12 301 +#define GLFW_KEY_F13 302 +#define GLFW_KEY_F14 303 +#define GLFW_KEY_F15 304 +#define GLFW_KEY_F16 305 +#define GLFW_KEY_F17 306 +#define GLFW_KEY_F18 307 +#define GLFW_KEY_F19 308 +#define GLFW_KEY_F20 309 +#define GLFW_KEY_F21 310 +#define GLFW_KEY_F22 311 +#define GLFW_KEY_F23 312 +#define GLFW_KEY_F24 313 +#define GLFW_KEY_F25 314 +#define GLFW_KEY_KP_0 320 +#define GLFW_KEY_KP_1 321 +#define GLFW_KEY_KP_2 322 +#define GLFW_KEY_KP_3 323 +#define GLFW_KEY_KP_4 324 +#define GLFW_KEY_KP_5 325 +#define GLFW_KEY_KP_6 326 +#define GLFW_KEY_KP_7 327 +#define GLFW_KEY_KP_8 328 +#define GLFW_KEY_KP_9 329 +#define GLFW_KEY_KP_DECIMAL 330 +#define GLFW_KEY_KP_DIVIDE 331 +#define GLFW_KEY_KP_MULTIPLY 332 +#define GLFW_KEY_KP_SUBTRACT 333 +#define GLFW_KEY_KP_ADD 334 +#define GLFW_KEY_KP_ENTER 335 +#define GLFW_KEY_KP_EQUAL 336 +#define GLFW_KEY_LEFT_SHIFT 340 +#define GLFW_KEY_LEFT_CONTROL 341 +#define GLFW_KEY_LEFT_ALT 342 +#define GLFW_KEY_LEFT_SUPER 343 +#define GLFW_KEY_RIGHT_SHIFT 344 +#define GLFW_KEY_RIGHT_CONTROL 345 +#define GLFW_KEY_RIGHT_ALT 346 +#define GLFW_KEY_RIGHT_SUPER 347 +#define GLFW_KEY_MENU 348 +#define GLFW_KEY_LAST GLFW_KEY_MENU + +/*! @} */ + +/*! @defgroup mods Modifier key flags + * @ingroup input + * @{ */ + +/*! @brief If this bit is set one or more Shift keys were held down. + */ +#define GLFW_MOD_SHIFT 0x0001 +/*! @brief If this bit is set one or more Control keys were held down. + */ +#define GLFW_MOD_CONTROL 0x0002 +/*! @brief If this bit is set one or more Alt keys were held down. + */ +#define GLFW_MOD_ALT 0x0004 +/*! @brief If this bit is set one or more Super keys were held down. + */ +#define GLFW_MOD_SUPER 0x0008 + +/*! @} */ + +/*! @defgroup buttons Mouse buttons + * @ingroup input + * @{ */ +#define GLFW_MOUSE_BUTTON_1 0 +#define GLFW_MOUSE_BUTTON_2 1 +#define GLFW_MOUSE_BUTTON_3 2 +#define GLFW_MOUSE_BUTTON_4 3 +#define GLFW_MOUSE_BUTTON_5 4 +#define GLFW_MOUSE_BUTTON_6 5 +#define GLFW_MOUSE_BUTTON_7 6 +#define GLFW_MOUSE_BUTTON_8 7 +#define GLFW_MOUSE_BUTTON_LAST GLFW_MOUSE_BUTTON_8 +#define GLFW_MOUSE_BUTTON_LEFT GLFW_MOUSE_BUTTON_1 +#define GLFW_MOUSE_BUTTON_RIGHT GLFW_MOUSE_BUTTON_2 +#define GLFW_MOUSE_BUTTON_MIDDLE GLFW_MOUSE_BUTTON_3 +/*! @} */ + +/*! @defgroup joysticks Joysticks + * @ingroup input + * @{ */ +#define GLFW_JOYSTICK_1 0 +#define GLFW_JOYSTICK_2 1 +#define GLFW_JOYSTICK_3 2 +#define GLFW_JOYSTICK_4 3 +#define GLFW_JOYSTICK_5 4 +#define GLFW_JOYSTICK_6 5 +#define GLFW_JOYSTICK_7 6 +#define GLFW_JOYSTICK_8 7 +#define GLFW_JOYSTICK_9 8 +#define GLFW_JOYSTICK_10 9 +#define GLFW_JOYSTICK_11 10 +#define GLFW_JOYSTICK_12 11 +#define GLFW_JOYSTICK_13 12 +#define GLFW_JOYSTICK_14 13 +#define GLFW_JOYSTICK_15 14 +#define GLFW_JOYSTICK_16 15 +#define GLFW_JOYSTICK_LAST GLFW_JOYSTICK_16 +/*! @} */ + +/*! @defgroup errors Error codes + * @ingroup error + * @{ */ +/*! @brief GLFW has not been initialized. + */ +#define GLFW_NOT_INITIALIZED 0x00010001 +/*! @brief No context is current for this thread. + */ +#define GLFW_NO_CURRENT_CONTEXT 0x00010002 +/*! @brief One of the enum parameters for the function was given an invalid + * enum. + */ +#define GLFW_INVALID_ENUM 0x00010003 +/*! @brief One of the parameters for the function was given an invalid value. + */ +#define GLFW_INVALID_VALUE 0x00010004 +/*! @brief A memory allocation failed. + */ +#define GLFW_OUT_OF_MEMORY 0x00010005 +/*! @brief GLFW could not find support for the requested client API on the + * system. + */ +#define GLFW_API_UNAVAILABLE 0x00010006 +/*! @brief The requested client API version is not available. + */ +#define GLFW_VERSION_UNAVAILABLE 0x00010007 +/*! @brief A platform-specific error occurred that does not match any of the + * more specific categories. + */ +#define GLFW_PLATFORM_ERROR 0x00010008 +/*! @brief The clipboard did not contain data in the requested format. + */ +#define GLFW_FORMAT_UNAVAILABLE 0x00010009 +/*! @} */ + +#define GLFW_FOCUSED 0x00020001 +#define GLFW_ICONIFIED 0x00020002 +#define GLFW_RESIZABLE 0x00020003 +#define GLFW_VISIBLE 0x00020004 +#define GLFW_DECORATED 0x00020005 + +#define GLFW_RED_BITS 0x00021001 +#define GLFW_GREEN_BITS 0x00021002 +#define GLFW_BLUE_BITS 0x00021003 +#define GLFW_ALPHA_BITS 0x00021004 +#define GLFW_DEPTH_BITS 0x00021005 +#define GLFW_STENCIL_BITS 0x00021006 +#define GLFW_ACCUM_RED_BITS 0x00021007 +#define GLFW_ACCUM_GREEN_BITS 0x00021008 +#define GLFW_ACCUM_BLUE_BITS 0x00021009 +#define GLFW_ACCUM_ALPHA_BITS 0x0002100A +#define GLFW_AUX_BUFFERS 0x0002100B +#define GLFW_STEREO 0x0002100C +#define GLFW_SAMPLES 0x0002100D +#define GLFW_SRGB_CAPABLE 0x0002100E +#define GLFW_REFRESH_RATE 0x0002100F + +#define GLFW_CLIENT_API 0x00022001 +#define GLFW_CONTEXT_VERSION_MAJOR 0x00022002 +#define GLFW_CONTEXT_VERSION_MINOR 0x00022003 +#define GLFW_CONTEXT_REVISION 0x00022004 +#define GLFW_CONTEXT_ROBUSTNESS 0x00022005 +#define GLFW_OPENGL_FORWARD_COMPAT 0x00022006 +#define GLFW_OPENGL_DEBUG_CONTEXT 0x00022007 +#define GLFW_OPENGL_PROFILE 0x00022008 + +#define GLFW_OPENGL_API 0x00030001 +#define GLFW_OPENGL_ES_API 0x00030002 + +#define GLFW_NO_ROBUSTNESS 0 +#define GLFW_NO_RESET_NOTIFICATION 0x00031001 +#define GLFW_LOSE_CONTEXT_ON_RESET 0x00031002 + +#define GLFW_OPENGL_ANY_PROFILE 0 +#define GLFW_OPENGL_CORE_PROFILE 0x00032001 +#define GLFW_OPENGL_COMPAT_PROFILE 0x00032002 + +#define GLFW_CURSOR 0x00033001 +#define GLFW_STICKY_KEYS 0x00033002 +#define GLFW_STICKY_MOUSE_BUTTONS 0x00033003 + +#define GLFW_CURSOR_NORMAL 0x00034001 +#define GLFW_CURSOR_HIDDEN 0x00034002 +#define GLFW_CURSOR_DISABLED 0x00034003 + +#define GLFW_CONNECTED 0x00040001 +#define GLFW_DISCONNECTED 0x00040002 + + +/************************************************************************* + * GLFW API types + *************************************************************************/ + +/*! @brief Client API function pointer type. + * + * Generic function pointer used for returning client API function pointers + * without forcing a cast from a regular pointer. + * + * @ingroup context + */ +typedef void (*GLFWglproc)(void); + +/*! @brief Opaque monitor object. + * + * Opaque monitor object. + * + * @ingroup monitor + */ +typedef struct GLFWmonitor GLFWmonitor; + +/*! @brief Opaque window object. + * + * Opaque window object. + * + * @ingroup window + */ +typedef struct GLFWwindow GLFWwindow; + +/*! @brief The function signature for error callbacks. + * + * This is the function signature for error callback functions. + * + * @param[in] error An [error code](@ref errors). + * @param[in] description A UTF-8 encoded string describing the error. + * + * @sa glfwSetErrorCallback + * + * @ingroup error + */ +typedef void (* GLFWerrorfun)(int,const char*); + +/*! @brief The function signature for window position callbacks. + * + * This is the function signature for window position callback functions. + * + * @param[in] window The window that the user moved. + * @param[in] xpos The new x-coordinate, in screen coordinates, of the + * upper-left corner of the client area of the window. + * @param[in] ypos The new y-coordinate, in screen coordinates, of the + * upper-left corner of the client area of the window. + * + * @sa glfwSetWindowPosCallback + * + * @ingroup window + */ +typedef void (* GLFWwindowposfun)(GLFWwindow*,int,int); + +/*! @brief The function signature for window resize callbacks. + * + * This is the function signature for window size callback functions. + * + * @param[in] window The window that the user resized. + * @param[in] width The new width, in screen coordinates, of the window. + * @param[in] height The new height, in screen coordinates, of the window. + * + * @sa glfwSetWindowSizeCallback + * + * @ingroup window + */ +typedef void (* GLFWwindowsizefun)(GLFWwindow*,int,int); + +/*! @brief The function signature for window close callbacks. + * + * This is the function signature for window close callback functions. + * + * @param[in] window The window that the user attempted to close. + * + * @sa glfwSetWindowCloseCallback + * + * @ingroup window + */ +typedef void (* GLFWwindowclosefun)(GLFWwindow*); + +/*! @brief The function signature for window content refresh callbacks. + * + * This is the function signature for window refresh callback functions. + * + * @param[in] window The window whose content needs to be refreshed. + * + * @sa glfwSetWindowRefreshCallback + * + * @ingroup window + */ +typedef void (* GLFWwindowrefreshfun)(GLFWwindow*); + +/*! @brief The function signature for window focus/defocus callbacks. + * + * This is the function signature for window focus callback functions. + * + * @param[in] window The window that was focused or defocused. + * @param[in] focused `GL_TRUE` if the window was focused, or `GL_FALSE` if + * it was defocused. + * + * @sa glfwSetWindowFocusCallback + * + * @ingroup window + */ +typedef void (* GLFWwindowfocusfun)(GLFWwindow*,int); + +/*! @brief The function signature for window iconify/restore callbacks. + * + * This is the function signature for window iconify/restore callback + * functions. + * + * @param[in] window The window that was iconified or restored. + * @param[in] iconified `GL_TRUE` if the window was iconified, or `GL_FALSE` + * if it was restored. + * + * @sa glfwSetWindowIconifyCallback + * + * @ingroup window + */ +typedef void (* GLFWwindowiconifyfun)(GLFWwindow*,int); + +/*! @brief The function signature for framebuffer resize callbacks. + * + * This is the function signature for framebuffer resize callback + * functions. + * + * @param[in] window The window whose framebuffer was resized. + * @param[in] width The new width, in pixels, of the framebuffer. + * @param[in] height The new height, in pixels, of the framebuffer. + * + * @sa glfwSetFramebufferSizeCallback + * + * @ingroup window + */ +typedef void (* GLFWframebuffersizefun)(GLFWwindow*,int,int); + +/*! @brief The function signature for mouse button callbacks. + * + * This is the function signature for mouse button callback functions. + * + * @param[in] window The window that received the event. + * @param[in] button The [mouse button](@ref buttons) that was pressed or + * released. + * @param[in] action One of `GLFW_PRESS` or `GLFW_RELEASE`. + * @param[in] mods Bit field describing which [modifier keys](@ref mods) were + * held down. + * + * @sa glfwSetMouseButtonCallback + * + * @ingroup input + */ +typedef void (* GLFWmousebuttonfun)(GLFWwindow*,int,int,int); + +/*! @brief The function signature for cursor position callbacks. + * + * This is the function signature for cursor position callback functions. + * + * @param[in] window The window that received the event. + * @param[in] xpos The new x-coordinate of the cursor. + * @param[in] ypos The new y-coordinate of the cursor. + * + * @sa glfwSetCursorPosCallback + * + * @ingroup input + */ +typedef void (* GLFWcursorposfun)(GLFWwindow*,double,double); + +/*! @brief The function signature for cursor enter/leave callbacks. + * + * This is the function signature for cursor enter/leave callback functions. + * + * @param[in] window The window that received the event. + * @param[in] entered `GL_TRUE` if the cursor entered the window's client + * area, or `GL_FALSE` if it left it. + * + * @sa glfwSetCursorEnterCallback + * + * @ingroup input + */ +typedef void (* GLFWcursorenterfun)(GLFWwindow*,int); + +/*! @brief The function signature for scroll callbacks. + * + * This is the function signature for scroll callback functions. + * + * @param[in] window The window that received the event. + * @param[in] xoffset The scroll offset along the x-axis. + * @param[in] yoffset The scroll offset along the y-axis. + * + * @sa glfwSetScrollCallback + * + * @ingroup input + */ +typedef void (* GLFWscrollfun)(GLFWwindow*,double,double); + +/*! @brief The function signature for keyboard key callbacks. + * + * This is the function signature for keyboard key callback functions. + * + * @param[in] window The window that received the event. + * @param[in] key The [keyboard key](@ref keys) that was pressed or released. + * @param[in] scancode The system-specific scancode of the key. + * @param[in] action @ref GLFW_PRESS, @ref GLFW_RELEASE or @ref GLFW_REPEAT. + * @param[in] mods Bit field describing which [modifier keys](@ref mods) were + * held down. + * + * @sa glfwSetKeyCallback + * + * @ingroup input + */ +typedef void (* GLFWkeyfun)(GLFWwindow*,int,int,int,int); + +/*! @brief The function signature for Unicode character callbacks. + * + * This is the function signature for Unicode character callback functions. + * + * @param[in] window The window that received the event. + * @param[in] character The Unicode code point of the character. + * + * @sa glfwSetCharCallback + * + * @ingroup input + */ +typedef void (* GLFWcharfun)(GLFWwindow*,unsigned int); + +/*! @brief The function signature for monitor configuration callbacks. + * + * This is the function signature for monitor configuration callback functions. + * + * @param[in] monitor The monitor that was connected or disconnected. + * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. + * + * @sa glfwSetMonitorCallback + * + * @ingroup monitor + */ +typedef void (* GLFWmonitorfun)(GLFWmonitor*,int); + +/*! @brief Video mode type. + * + * This describes a single video mode. + * + * @ingroup monitor + */ +typedef struct +{ + /*! The width, in screen coordinates, of the video mode. + */ + int width; + /*! The height, in screen coordinates, of the video mode. + */ + int height; + /*! The bit depth of the red channel of the video mode. + */ + int redBits; + /*! The bit depth of the green channel of the video mode. + */ + int greenBits; + /*! The bit depth of the blue channel of the video mode. + */ + int blueBits; + /*! The refresh rate, in Hz, of the video mode. + */ + int refreshRate; +} GLFWvidmode; + +/*! @brief Gamma ramp. + * + * This describes the gamma ramp for a monitor. + * + * @sa glfwGetGammaRamp glfwSetGammaRamp + * + * @ingroup gamma + */ +typedef struct +{ + /*! An array of value describing the response of the red channel. + */ + unsigned short* red; + /*! An array of value describing the response of the green channel. + */ + unsigned short* green; + /*! An array of value describing the response of the blue channel. + */ + unsigned short* blue; + /*! The number of elements in each array. + */ + unsigned int size; +} GLFWgammaramp; + + +/************************************************************************* + * GLFW API functions + *************************************************************************/ + +/*! @brief Initializes the GLFW library. + * + * This function initializes the GLFW library. Before most GLFW functions can + * be used, GLFW must be initialized, and before a program terminates GLFW + * should be terminated in order to free any resources allocated during or + * after initialization. + * + * If this function fails, it calls @ref glfwTerminate before returning. If it + * succeeds, you should call @ref glfwTerminate before the program exits. + * + * Additional calls to this function after successful initialization but before + * termination will succeed but will do nothing. + * + * @return `GL_TRUE` if successful, or `GL_FALSE` if an error occurred. + * + * @par New in GLFW 3 + * This function no longer registers @ref glfwTerminate with `atexit`. + * + * @note This function may only be called from the main thread. + * + * @note This function may take several seconds to complete on some systems, + * while on other systems it may take only a fraction of a second to complete. + * + * @note **Mac OS X:** This function will change the current directory of the + * application to the `Contents/Resources` subdirectory of the application's + * bundle, if present. + * + * @sa glfwTerminate + * + * @ingroup init + */ +GLFWAPI int glfwInit(void); + +/*! @brief Terminates the GLFW library. + * + * This function destroys all remaining windows, frees any allocated resources + * and sets the library to an uninitialized state. Once this is called, you + * must again call @ref glfwInit successfully before you will be able to use + * most GLFW functions. + * + * If GLFW has been successfully initialized, this function should be called + * before the program exits. If initialization fails, there is no need to call + * this function, as it is called by @ref glfwInit before it returns failure. + * + * @remarks This function may be called before @ref glfwInit. + * + * @note This function may only be called from the main thread. + * + * @warning No window's context may be current on another thread when this + * function is called. + * + * @sa glfwInit + * + * @ingroup init + */ +GLFWAPI void glfwTerminate(void); + +/*! @brief Retrieves the version of the GLFW library. + * + * This function retrieves the major, minor and revision numbers of the GLFW + * library. It is intended for when you are using GLFW as a shared library and + * want to ensure that you are using the minimum required version. + * + * @param[out] major Where to store the major version number, or `NULL`. + * @param[out] minor Where to store the minor version number, or `NULL`. + * @param[out] rev Where to store the revision number, or `NULL`. + * + * @remarks This function may be called before @ref glfwInit. + * + * @remarks This function may be called from any thread. + * + * @sa glfwGetVersionString + * + * @ingroup init + */ +GLFWAPI void glfwGetVersion(int* major, int* minor, int* rev); + +/*! @brief Returns a string describing the compile-time configuration. + * + * This function returns a static string generated at compile-time according to + * which configuration macros were defined. This is intended for use when + * submitting bug reports, to allow developers to see which code paths are + * enabled in a binary. + * + * The format of the string is as follows: + * - The version of GLFW + * - The name of the window system API + * - The name of the context creation API + * - Any additional options or APIs + * + * For example, when compiling GLFW 3.0 with MinGW using the Win32 and WGL + * back ends, the version string may look something like this: + * + * 3.0.0 Win32 WGL MinGW + * + * @return The GLFW version string. + * + * @remarks This function may be called before @ref glfwInit. + * + * @remarks This function may be called from any thread. + * + * @sa glfwGetVersion + * + * @ingroup init + */ +GLFWAPI const char* glfwGetVersionString(void); + +/*! @brief Sets the error callback. + * + * This function sets the error callback, which is called with an error code + * and a human-readable description each time a GLFW error occurs. + * + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or an + * error occurred. + * + * @remarks This function may be called before @ref glfwInit. + * + * @note The error callback is called by the thread where the error was + * generated. If you are using GLFW from multiple threads, your error callback + * needs to be written accordingly. + * + * @note Because the description string provided to the callback may have been + * generated specifically for that error, it is not guaranteed to be valid + * after the callback has returned. If you wish to use it after that, you need + * to make your own copy of it before returning. + * + * @ingroup error + */ +GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun cbfun); + +/*! @brief Returns the currently connected monitors. + * + * This function returns an array of handles for all currently connected + * monitors. + * + * @param[out] count Where to store the size of the returned array. This is + * set to zero if an error occurred. + * @return An array of monitor handles, or `NULL` if an error occurred. + * + * @note The returned array is allocated and freed by GLFW. You should not + * free it yourself. + * + * @note The returned array is valid only until the monitor configuration + * changes. See @ref glfwSetMonitorCallback to receive notifications of + * configuration changes. + * + * @sa glfwGetPrimaryMonitor + * + * @ingroup monitor + */ +GLFWAPI GLFWmonitor** glfwGetMonitors(int* count); + +/*! @brief Returns the primary monitor. + * + * This function returns the primary monitor. This is usually the monitor + * where elements like the Windows task bar or the OS X menu bar is located. + * + * @return The primary monitor, or `NULL` if an error occurred. + * + * @sa glfwGetMonitors + * + * @ingroup monitor + */ +GLFWAPI GLFWmonitor* glfwGetPrimaryMonitor(void); + +/*! @brief Returns the position of the monitor's viewport on the virtual screen. + * + * This function returns the position, in screen coordinates, of the upper-left + * corner of the specified monitor. + * + * @param[in] monitor The monitor to query. + * @param[out] xpos Where to store the monitor x-coordinate, or `NULL`. + * @param[out] ypos Where to store the monitor y-coordinate, or `NULL`. + * + * @ingroup monitor + */ +GLFWAPI void glfwGetMonitorPos(GLFWmonitor* monitor, int* xpos, int* ypos); + +/*! @brief Returns the physical size of the monitor. + * + * This function returns the size, in millimetres, of the display area of the + * specified monitor. + * + * @param[in] monitor The monitor to query. + * @param[out] width Where to store the width, in mm, of the monitor's display + * area, or `NULL`. + * @param[out] height Where to store the height, in mm, of the monitor's + * display area, or `NULL`. + * + * @note Some operating systems do not provide accurate information, either + * because the monitor's EDID data is incorrect, or because the driver does not + * report it accurately. + * + * @ingroup monitor + */ +GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* monitor, int* width, int* height); + +/*! @brief Returns the name of the specified monitor. + * + * This function returns a human-readable name, encoded as UTF-8, of the + * specified monitor. + * + * @param[in] monitor The monitor to query. + * @return The UTF-8 encoded name of the monitor, or `NULL` if an error + * occurred. + * + * @note The returned string is allocated and freed by GLFW. You should not + * free it yourself. + * + * @ingroup monitor + */ +GLFWAPI const char* glfwGetMonitorName(GLFWmonitor* monitor); + +/*! @brief Sets the monitor configuration callback. + * + * This function sets the monitor configuration callback, or removes the + * currently set callback. This is called when a monitor is connected to or + * disconnected from the system. + * + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or an + * error occurred. + * + * @bug **X11:** This callback is not yet called on monitor configuration + * changes. + * + * @ingroup monitor + */ +GLFWAPI GLFWmonitorfun glfwSetMonitorCallback(GLFWmonitorfun cbfun); + +/*! @brief Returns the available video modes for the specified monitor. + * + * This function returns an array of all video modes supported by the specified + * monitor. The returned array is sorted in ascending order, first by color + * bit depth (the sum of all channel depths) and then by resolution area (the + * product of width and height). + * + * @param[in] monitor The monitor to query. + * @param[out] count Where to store the number of video modes in the returned + * array. This is set to zero if an error occurred. + * @return An array of video modes, or `NULL` if an error occurred. + * + * @note The returned array is allocated and freed by GLFW. You should not + * free it yourself. + * + * @note The returned array is valid only until this function is called again + * for the specified monitor. + * + * @sa glfwGetVideoMode + * + * @ingroup monitor + */ +GLFWAPI const GLFWvidmode* glfwGetVideoModes(GLFWmonitor* monitor, int* count); + +/*! @brief Returns the current mode of the specified monitor. + * + * This function returns the current video mode of the specified monitor. If + * you are using a full screen window, the return value will therefore depend + * on whether it is focused. + * + * @param[in] monitor The monitor to query. + * @return The current mode of the monitor, or `NULL` if an error occurred. + * + * @note The returned struct is allocated and freed by GLFW. You should not + * free it yourself. + * + * @sa glfwGetVideoModes + * + * @ingroup monitor + */ +GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* monitor); + +/*! @brief Generates a gamma ramp and sets it for the specified monitor. + * + * This function generates a 256-element gamma ramp from the specified exponent + * and then calls @ref glfwSetGammaRamp with it. + * + * @param[in] monitor The monitor whose gamma ramp to set. + * @param[in] gamma The desired exponent. + * + * @ingroup gamma + */ +GLFWAPI void glfwSetGamma(GLFWmonitor* monitor, float gamma); + +/*! @brief Retrieves the current gamma ramp for the specified monitor. + * + * This function retrieves the current gamma ramp of the specified monitor. + * + * @param[in] monitor The monitor to query. + * @return The current gamma ramp, or `NULL` if an error occurred. + * + * @note The value arrays of the returned ramp are allocated and freed by GLFW. + * You should not free them yourself. + * + * @ingroup gamma + */ +GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* monitor); + +/*! @brief Sets the current gamma ramp for the specified monitor. + * + * This function sets the current gamma ramp for the specified monitor. + * + * @param[in] monitor The monitor whose gamma ramp to set. + * @param[in] ramp The gamma ramp to use. + * + * @note Gamma ramp sizes other than 256 are not supported by all hardware. + * + * @ingroup gamma + */ +GLFWAPI void glfwSetGammaRamp(GLFWmonitor* monitor, const GLFWgammaramp* ramp); + +/*! @brief Resets all window hints to their default values. + * + * This function resets all window hints to their + * [default values](@ref window_hints_values). + * + * @note This function may only be called from the main thread. + * + * @sa glfwWindowHint + * + * @ingroup window + */ +GLFWAPI void glfwDefaultWindowHints(void); + +/*! @brief Sets the specified window hint to the desired value. + * + * This function sets hints for the next call to @ref glfwCreateWindow. The + * hints, once set, retain their values until changed by a call to @ref + * glfwWindowHint or @ref glfwDefaultWindowHints, or until the library is + * terminated with @ref glfwTerminate. + * + * @param[in] target The [window hint](@ref window_hints) to set. + * @param[in] hint The new value of the window hint. + * + * @par New in GLFW 3 + * Hints are no longer reset to their default values on window creation. To + * set default hint values, use @ref glfwDefaultWindowHints. + * + * @note This function may only be called from the main thread. + * + * @sa glfwDefaultWindowHints + * + * @ingroup window + */ +GLFWAPI void glfwWindowHint(int target, int hint); + +/*! @brief Creates a window and its associated context. + * + * This function creates a window and its associated context. Most of the + * options controlling how the window and its context should be created are + * specified through @ref glfwWindowHint. + * + * Successful creation does not change which context is current. Before you + * can use the newly created context, you need to make it current using @ref + * glfwMakeContextCurrent. + * + * Note that the created window and context may differ from what you requested, + * as not all parameters and hints are + * [hard constraints](@ref window_hints_hard). This includes the size of the + * window, especially for full screen windows. To retrieve the actual + * attributes of the created window and context, use queries like @ref + * glfwGetWindowAttrib and @ref glfwGetWindowSize. + * + * To create the window at a specific position, make it initially invisible + * using the `GLFW_VISIBLE` window hint, set its position and then show it. + * + * If a fullscreen window is active, the screensaver is prohibited from + * starting. + * + * @param[in] width The desired width, in screen coordinates, of the window. + * This must be greater than zero. + * @param[in] height The desired height, in screen coordinates, of the window. + * This must be greater than zero. + * @param[in] title The initial, UTF-8 encoded window title. + * @param[in] monitor The monitor to use for full screen mode, or `NULL` to use + * windowed mode. + * @param[in] share The window whose context to share resources with, or `NULL` + * to not share resources. + * @return The handle of the created window, or `NULL` if an error occurred. + * + * @remarks **Windows:** If the executable has an icon resource named + * `GLFW_ICON,` it will be set as the icon for the window. If no such icon is + * present, the `IDI_WINLOGO` icon will be used instead. + * + * @remarks **Mac OS X:** The GLFW window has no icon, as it is not a document + * window, but the dock icon will be the same as the application bundle's icon. + * Also, the first time a window is opened the menu bar is populated with + * common commands like Hide, Quit and About. The (minimal) about dialog uses + * information from the application's bundle. For more information on bundles, + * see the Bundle Programming Guide provided by Apple. + * + * @note This function may only be called from the main thread. + * + * @sa glfwDestroyWindow + * + * @ingroup window + */ +GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, const char* title, GLFWmonitor* monitor, GLFWwindow* share); + +/*! @brief Destroys the specified window and its context. + * + * This function destroys the specified window and its context. On calling + * this function, no further callbacks will be called for that window. + * + * @param[in] window The window to destroy. + * + * @note This function may only be called from the main thread. + * + * @note This function may not be called from a callback. + * + * @note If the window's context is current on the main thread, it is + * detached before being destroyed. + * + * @warning The window's context must not be current on any other thread. + * + * @sa glfwCreateWindow + * + * @ingroup window + */ +GLFWAPI void glfwDestroyWindow(GLFWwindow* window); + +/*! @brief Checks the close flag of the specified window. + * + * This function returns the value of the close flag of the specified window. + * + * @param[in] window The window to query. + * @return The value of the close flag. + * + * @ingroup window + */ +GLFWAPI int glfwWindowShouldClose(GLFWwindow* window); + +/*! @brief Sets the close flag of the specified window. + * + * This function sets the value of the close flag of the specified window. + * This can be used to override the user's attempt to close the window, or + * to signal that it should be closed. + * + * @param[in] window The window whose flag to change. + * @param[in] value The new value. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowShouldClose(GLFWwindow* window, int value); + +/*! @brief Sets the title of the specified window. + * + * This function sets the window title, encoded as UTF-8, of the specified + * window. + * + * @param[in] window The window whose title to change. + * @param[in] title The UTF-8 encoded window title. + * + * @note This function may only be called from the main thread. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowTitle(GLFWwindow* window, const char* title); + +/*! @brief Retrieves the position of the client area of the specified window. + * + * This function retrieves the position, in screen coordinates, of the + * upper-left corner of the client area of the specified window. + * + * @param[in] window The window to query. + * @param[out] xpos Where to store the x-coordinate of the upper-left corner of + * the client area, or `NULL`. + * @param[out] ypos Where to store the y-coordinate of the upper-left corner of + * the client area, or `NULL`. + * + * @sa glfwSetWindowPos + * + * @ingroup window + */ +GLFWAPI void glfwGetWindowPos(GLFWwindow* window, int* xpos, int* ypos); + +/*! @brief Sets the position of the client area of the specified window. + * + * This function sets the position, in screen coordinates, of the upper-left + * corner of the client area of the window. + * + * If the specified window is a full screen window, this function does nothing. + * + * If you wish to set an initial window position you should create a hidden + * window (using @ref glfwWindowHint and `GLFW_VISIBLE`), set its position and + * then show it. + * + * @param[in] window The window to query. + * @param[in] xpos The x-coordinate of the upper-left corner of the client area. + * @param[in] ypos The y-coordinate of the upper-left corner of the client area. + * + * @note It is very rarely a good idea to move an already visible window, as it + * will confuse and annoy the user. + * + * @note This function may only be called from the main thread. + * + * @note The window manager may put limits on what positions are allowed. + * + * @bug **X11:** Some window managers ignore the set position of hidden (i.e. + * unmapped) windows, instead placing them where it thinks is appropriate once + * they are shown. + * + * @sa glfwGetWindowPos + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowPos(GLFWwindow* window, int xpos, int ypos); + +/*! @brief Retrieves the size of the client area of the specified window. + * + * This function retrieves the size, in screen coordinates, of the client area + * of the specified window. + * + * @param[in] window The window whose size to retrieve. + * @param[out] width Where to store the width, in screen coordinates, of the + * client area, or `NULL`. + * @param[out] height Where to store the height, in screen coordinates, of the + * client area, or `NULL`. + * + * @sa glfwSetWindowSize + * + * @ingroup window + */ +GLFWAPI void glfwGetWindowSize(GLFWwindow* window, int* width, int* height); + +/*! @brief Sets the size of the client area of the specified window. + * + * This function sets the size, in screen coordinates, of the client area of + * the specified window. + * + * For full screen windows, this function selects and switches to the resolution + * closest to the specified size, without affecting the window's context. As + * the context is unaffected, the bit depths of the framebuffer remain + * unchanged. + * + * @param[in] window The window to resize. + * @param[in] width The desired width of the specified window. + * @param[in] height The desired height of the specified window. + * + * @note This function may only be called from the main thread. + * + * @note The window manager may put limits on what window sizes are allowed. + * + * @sa glfwGetWindowSize + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowSize(GLFWwindow* window, int width, int height); + +/*! @brief Retrieves the size of the framebuffer of the specified window. + * + * This function retrieves the size, in pixels, of the framebuffer of the + * specified window. + * + * @param[in] window The window whose framebuffer to query. + * @param[out] width Where to store the width, in pixels, of the framebuffer, + * or `NULL`. + * @param[out] height Where to store the height, in pixels, of the framebuffer, + * or `NULL`. + * + * @sa glfwSetFramebufferSizeCallback + * + * @ingroup window + */ +GLFWAPI void glfwGetFramebufferSize(GLFWwindow* window, int* width, int* height); + +/*! @brief Iconifies the specified window. + * + * This function iconifies/minimizes the specified window, if it was previously + * restored. If it is a full screen window, the original monitor resolution is + * restored until the window is restored. If the window is already iconified, + * this function does nothing. + * + * @param[in] window The window to iconify. + * + * @note This function may only be called from the main thread. + * + * @sa glfwRestoreWindow + * + * @ingroup window + */ +GLFWAPI void glfwIconifyWindow(GLFWwindow* window); + +/*! @brief Restores the specified window. + * + * This function restores the specified window, if it was previously + * iconified/minimized. If it is a full screen window, the resolution chosen + * for the window is restored on the selected monitor. If the window is + * already restored, this function does nothing. + * + * @param[in] window The window to restore. + * + * @note This function may only be called from the main thread. + * + * @sa glfwIconifyWindow + * + * @ingroup window + */ +GLFWAPI void glfwRestoreWindow(GLFWwindow* window); + +/*! @brief Makes the specified window visible. + * + * This function makes the specified window visible, if it was previously + * hidden. If the window is already visible or is in full screen mode, this + * function does nothing. + * + * @param[in] window The window to make visible. + * + * @note This function may only be called from the main thread. + * + * @sa glfwHideWindow + * + * @ingroup window + */ +GLFWAPI void glfwShowWindow(GLFWwindow* window); + +/*! @brief Hides the specified window. + * + * This function hides the specified window, if it was previously visible. If + * the window is already hidden or is in full screen mode, this function does + * nothing. + * + * @param[in] window The window to hide. + * + * @note This function may only be called from the main thread. + * + * @sa glfwShowWindow + * + * @ingroup window + */ +GLFWAPI void glfwHideWindow(GLFWwindow* window); + +/*! @brief Returns the monitor that the window uses for full screen mode. + * + * This function returns the handle of the monitor that the specified window is + * in full screen on. + * + * @param[in] window The window to query. + * @return The monitor, or `NULL` if the window is in windowed mode. + * + * @ingroup window + */ +GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* window); + +/*! @brief Returns an attribute of the specified window. + * + * This function returns an attribute of the specified window. There are many + * attributes, some related to the window and others to its context. + * + * @param[in] window The window to query. + * @param[in] attrib The [window attribute](@ref window_attribs) whose value to + * return. + * @return The value of the attribute, or zero if an error occurred. + * + * @ingroup window + */ +GLFWAPI int glfwGetWindowAttrib(GLFWwindow* window, int attrib); + +/*! @brief Sets the user pointer of the specified window. + * + * This function sets the user-defined pointer of the specified window. The + * current value is retained until the window is destroyed. The initial value + * is `NULL`. + * + * @param[in] window The window whose pointer to set. + * @param[in] pointer The new value. + * + * @sa glfwGetWindowUserPointer + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowUserPointer(GLFWwindow* window, void* pointer); + +/*! @brief Returns the user pointer of the specified window. + * + * This function returns the current value of the user-defined pointer of the + * specified window. The initial value is `NULL`. + * + * @param[in] window The window whose pointer to return. + * + * @sa glfwSetWindowUserPointer + * + * @ingroup window + */ +GLFWAPI void* glfwGetWindowUserPointer(GLFWwindow* window); + +/*! @brief Sets the position callback for the specified window. + * + * This function sets the position callback of the specified window, which is + * called when the window is moved. The callback is provided with the screen + * position of the upper-left corner of the client area of the window. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or an + * error occurred. + * + * @ingroup window + */ +GLFWAPI GLFWwindowposfun glfwSetWindowPosCallback(GLFWwindow* window, GLFWwindowposfun cbfun); + +/*! @brief Sets the size callback for the specified window. + * + * This function sets the size callback of the specified window, which is + * called when the window is resized. The callback is provided with the size, + * in screen coordinates, of the client area of the window. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or an + * error occurred. + * + * @ingroup window + */ +GLFWAPI GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* window, GLFWwindowsizefun cbfun); + +/*! @brief Sets the close callback for the specified window. + * + * This function sets the close callback of the specified window, which is + * called when the user attempts to close the window, for example by clicking + * the close widget in the title bar. + * + * The close flag is set before this callback is called, but you can modify it + * at any time with @ref glfwSetWindowShouldClose. + * + * The close callback is not triggered by @ref glfwDestroyWindow. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or an + * error occurred. + * + * @remarks **Mac OS X:** Selecting Quit from the application menu will + * trigger the close callback for all windows. + * + * @ingroup window + */ +GLFWAPI GLFWwindowclosefun glfwSetWindowCloseCallback(GLFWwindow* window, GLFWwindowclosefun cbfun); + +/*! @brief Sets the refresh callback for the specified window. + * + * This function sets the refresh callback of the specified window, which is + * called when the client area of the window needs to be redrawn, for example + * if the window has been exposed after having been covered by another window. + * + * On compositing window systems such as Aero, Compiz or Aqua, where the window + * contents are saved off-screen, this callback may be called only very + * infrequently or never at all. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or an + * error occurred. + * + * @note On compositing window systems such as Aero, Compiz or Aqua, where the + * window contents are saved off-screen, this callback may be called only very + * infrequently or never at all. + * + * @ingroup window + */ +GLFWAPI GLFWwindowrefreshfun glfwSetWindowRefreshCallback(GLFWwindow* window, GLFWwindowrefreshfun cbfun); + +/*! @brief Sets the focus callback for the specified window. + * + * This function sets the focus callback of the specified window, which is + * called when the window gains or loses focus. + * + * After the focus callback is called for a window that lost focus, synthetic + * key and mouse button release events will be generated for all such that had + * been pressed. For more information, see @ref glfwSetKeyCallback and @ref + * glfwSetMouseButtonCallback. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or an + * error occurred. + * + * @ingroup window + */ +GLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* window, GLFWwindowfocusfun cbfun); + +/*! @brief Sets the iconify callback for the specified window. + * + * This function sets the iconification callback of the specified window, which + * is called when the window is iconified or restored. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or an + * error occurred. + * + * @ingroup window + */ +GLFWAPI GLFWwindowiconifyfun glfwSetWindowIconifyCallback(GLFWwindow* window, GLFWwindowiconifyfun cbfun); + +/*! @brief Sets the framebuffer resize callback for the specified window. + * + * This function sets the framebuffer resize callback of the specified window, + * which is called when the framebuffer of the specified window is resized. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or an + * error occurred. + * + * @ingroup window + */ +GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* window, GLFWframebuffersizefun cbfun); + +/*! @brief Processes all pending events. + * + * This function processes only those events that have already been received + * and then returns immediately. Processing events will cause the window and + * input callbacks associated with those events to be called. + * + * This function is not required for joystick input to work. + * + * @par New in GLFW 3 + * This function is no longer called by @ref glfwSwapBuffers. You need to call + * it or @ref glfwWaitEvents yourself. + * + * @note This function may only be called from the main thread. + * + * @note This function may not be called from a callback. + * + * @note On some platforms, certain callbacks may be called outside of a call + * to one of the event processing functions. + * + * @sa glfwWaitEvents + * + * @ingroup window + */ +GLFWAPI void glfwPollEvents(void); + +/*! @brief Waits until events are pending and processes them. + * + * This function puts the calling thread to sleep until at least one event has + * been received. Once one or more events have been recevied, it behaves as if + * @ref glfwPollEvents was called, i.e. the events are processed and the + * function then returns immediately. Processing events will cause the window + * and input callbacks associated with those events to be called. + * + * Since not all events are associated with callbacks, this function may return + * without a callback having been called even if you are monitoring all + * callbacks. + * + * This function is not required for joystick input to work. + * + * @note This function may only be called from the main thread. + * + * @note This function may not be called from a callback. + * + * @note On some platforms, certain callbacks may be called outside of a call + * to one of the event processing functions. + * + * @sa glfwPollEvents + * + * @ingroup window + */ +GLFWAPI void glfwWaitEvents(void); + +/*! @brief Returns the value of an input option for the specified window. + * + * @param[in] window The window to query. + * @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS` or + * `GLFW_STICKY_MOUSE_BUTTONS`. + * + * @sa glfwSetInputMode + * + * @ingroup input + */ +GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode); + +/*! @brief Sets an input option for the specified window. + * @param[in] window The window whose input mode to set. + * @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS` or + * `GLFW_STICKY_MOUSE_BUTTONS`. + * @param[in] value The new value of the specified input mode. + * + * If `mode` is `GLFW_CURSOR`, the value must be one of the supported input + * modes: + * - `GLFW_CURSOR_NORMAL` makes the cursor visible and behaving normally. + * - `GLFW_CURSOR_HIDDEN` makes the cursor invisible when it is over the client + * area of the window. + * - `GLFW_CURSOR_DISABLED` disables the cursor and removes any limitations on + * cursor movement. + * + * If `mode` is `GLFW_STICKY_KEYS`, the value must be either `GL_TRUE` to + * enable sticky keys, or `GL_FALSE` to disable it. If sticky keys are + * enabled, a key press will ensure that @ref glfwGetKey returns @ref + * GLFW_PRESS the next time it is called even if the key had been released + * before the call. This is useful when you are only interested in whether + * keys have been pressed but not when or in which order. + * + * If `mode` is `GLFW_STICKY_MOUSE_BUTTONS`, the value must be either `GL_TRUE` + * to enable sticky mouse buttons, or `GL_FALSE` to disable it. If sticky + * mouse buttons are enabled, a mouse button press will ensure that @ref + * glfwGetMouseButton returns @ref GLFW_PRESS the next time it is called even + * if the mouse button had been released before the call. This is useful when + * you are only interested in whether mouse buttons have been pressed but not + * when or in which order. + * + * @sa glfwGetInputMode + * + * @ingroup input + */ +GLFWAPI void glfwSetInputMode(GLFWwindow* window, int mode, int value); + +/*! @brief Returns the last reported state of a keyboard key for the specified + * window. + * + * This function returns the last state reported for the specified key to the + * specified window. The returned state is one of `GLFW_PRESS` or + * `GLFW_RELEASE`. The higher-level state `GLFW_REPEAT` is only reported to + * the key callback. + * + * If the `GLFW_STICKY_KEYS` input mode is enabled, this function returns + * `GLFW_PRESS` the first time you call this function after a key has been + * pressed, even if the key has already been released. + * + * The key functions deal with physical keys, with [key tokens](@ref keys) + * named after their use on the standard US keyboard layout. If you want to + * input text, use the Unicode character callback instead. + * + * @param[in] window The desired window. + * @param[in] key The desired [keyboard key](@ref keys). + * @return One of `GLFW_PRESS` or `GLFW_RELEASE`. + * + * @note `GLFW_KEY_UNKNOWN` is not a valid key for this function. + * + * @ingroup input + */ +GLFWAPI int glfwGetKey(GLFWwindow* window, int key); + +/*! @brief Returns the last reported state of a mouse button for the specified + * window. + * + * This function returns the last state reported for the specified mouse button + * to the specified window. + * + * If the `GLFW_STICKY_MOUSE_BUTTONS` input mode is enabled, this function + * returns `GLFW_PRESS` the first time you call this function after a mouse + * button has been pressed, even if the mouse button has already been released. + * + * @param[in] window The desired window. + * @param[in] button The desired [mouse button](@ref buttons). + * @return One of `GLFW_PRESS` or `GLFW_RELEASE`. + * + * @ingroup input + */ +GLFWAPI int glfwGetMouseButton(GLFWwindow* window, int button); + +/*! @brief Retrieves the last reported cursor position, relative to the client + * area of the window. + * + * This function returns the last reported position of the cursor to the + * specified window. + * + * If the cursor is disabled (with `GLFW_CURSOR_DISABLED`) then the cursor + * position is unbounded and limited only by the minimum and maximum values of + * a `double`. + * + * The coordinate can be converted to their integer equivalents with the + * `floor` function. Casting directly to an integer type works for positive + * coordinates, but fails for negative ones. + * + * @param[in] window The desired window. + * @param[out] xpos Where to store the cursor x-coordinate, relative to the + * left edge of the client area, or `NULL`. + * @param[out] ypos Where to store the cursor y-coordinate, relative to the to + * top edge of the client area, or `NULL`. + * + * @sa glfwSetCursorPos + * + * @ingroup input + */ +GLFWAPI void glfwGetCursorPos(GLFWwindow* window, double* xpos, double* ypos); + +/*! @brief Sets the position of the cursor, relative to the client area of the window. + * + * This function sets the position of the cursor. The specified window must be + * focused. If the window does not have focus when this function is called, it + * fails silently. + * + * If the cursor is disabled (with `GLFW_CURSOR_DISABLED`) then the cursor + * position is unbounded and limited only by the minimum and maximum values of + * a `double`. + * + * @param[in] window The desired window. + * @param[in] xpos The desired x-coordinate, relative to the left edge of the + * client area, or `NULL`. + * @param[in] ypos The desired y-coordinate, relative to the top edge of the + * client area, or `NULL`. + * + * @sa glfwGetCursorPos + * + * @ingroup input + */ +GLFWAPI void glfwSetCursorPos(GLFWwindow* window, double xpos, double ypos); + +/*! @brief Sets the key callback. + * + * This function sets the key callback of the specific window, which is called + * when a key is pressed, repeated or released. + * + * The key functions deal with physical keys, with layout independent + * [key tokens](@ref keys) named after their values in the standard US keyboard + * layout. If you want to input text, use the + * [character callback](@ref glfwSetCharCallback) instead. + * + * When a window loses focus, it will generate synthetic key release events + * for all pressed keys. You can tell these events from user-generated events + * by the fact that the synthetic ones are generated after the window has lost + * focus, i.e. `GLFW_FOCUSED` will be false and the focus callback will have + * already been called. + * + * The scancode of a key is specific to that platform or sometimes even to that + * machine. Scancodes are intended to allow users to bind keys that don't have + * a GLFW key token. Such keys have `key` set to `GLFW_KEY_UNKNOWN`, their + * state is not saved and so it cannot be retrieved with @ref glfwGetKey. + * + * Sometimes GLFW needs to generate synthetic key events, in which case the + * scancode may be zero. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new key callback, or `NULL` to remove the currently + * set callback. + * @return The previously set callback, or `NULL` if no callback was set or an + * error occurred. + * + * @ingroup input + */ +GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* window, GLFWkeyfun cbfun); + +/*! @brief Sets the Unicode character callback. + * + * This function sets the character callback of the specific window, which is + * called when a Unicode character is input. + * + * The character callback is intended for text input. If you want to know + * whether a specific key was pressed or released, use the + * [key callback](@ref glfwSetKeyCallback) instead. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or an + * error occurred. + * + * @ingroup input + */ +GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* window, GLFWcharfun cbfun); + +/*! @brief Sets the mouse button callback. + * + * This function sets the mouse button callback of the specified window, which + * is called when a mouse button is pressed or released. + * + * When a window loses focus, it will generate synthetic mouse button release + * events for all pressed mouse buttons. You can tell these events from + * user-generated events by the fact that the synthetic ones are generated + * after the window has lost focus, i.e. `GLFW_FOCUSED` will be false and the + * focus callback will have already been called. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or an + * error occurred. + * + * @ingroup input + */ +GLFWAPI GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* window, GLFWmousebuttonfun cbfun); + +/*! @brief Sets the cursor position callback. + * + * This function sets the cursor position callback of the specified window, + * which is called when the cursor is moved. The callback is provided with the + * position relative to the upper-left corner of the client area of the window. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or an + * error occurred. + * + * @ingroup input + */ +GLFWAPI GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow* window, GLFWcursorposfun cbfun); + +/*! @brief Sets the cursor enter/exit callback. + * + * This function sets the cursor boundary crossing callback of the specified + * window, which is called when the cursor enters or leaves the client area of + * the window. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or an + * error occurred. + * + * @ingroup input + */ +GLFWAPI GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow* window, GLFWcursorenterfun cbfun); + +/*! @brief Sets the scroll callback. + * + * This function sets the scroll callback of the specified window, which is + * called when a scrolling device is used, such as a mouse wheel or scrolling + * area of a touchpad. + * + * The scroll callback receives all scrolling input, like that from a mouse + * wheel or a touchpad scrolling area. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new scroll callback, or `NULL` to remove the currently + * set callback. + * @return The previously set callback, or `NULL` if no callback was set or an + * error occurred. + * + * @ingroup input + */ +GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun cbfun); + +/*! @brief Returns whether the specified joystick is present. + * + * This function returns whether the specified joystick is present. + * + * @param[in] joy The joystick to query. + * @return `GL_TRUE` if the joystick is present, or `GL_FALSE` otherwise. + * + * @ingroup input + */ +GLFWAPI int glfwJoystickPresent(int joy); + +/*! @brief Returns the values of all axes of the specified joystick. + * + * This function returns the values of all axes of the specified joystick. + * + * @param[in] joy The joystick to query. + * @param[out] count Where to store the size of the returned array. This is + * set to zero if an error occurred. + * @return An array of axis values, or `NULL` if the joystick is not present. + * + * @note The returned array is allocated and freed by GLFW. You should not + * free it yourself. + * + * @note The returned array is valid only until the next call to @ref + * glfwGetJoystickAxes for that joystick. + * + * @ingroup input + */ +GLFWAPI const float* glfwGetJoystickAxes(int joy, int* count); + +/*! @brief Returns the state of all buttons of the specified joystick. + * + * This function returns the state of all buttons of the specified joystick. + * + * @param[in] joy The joystick to query. + * @param[out] count Where to store the size of the returned array. This is + * set to zero if an error occurred. + * @return An array of button states, or `NULL` if the joystick is not present. + * + * @note The returned array is allocated and freed by GLFW. You should not + * free it yourself. + * + * @note The returned array is valid only until the next call to @ref + * glfwGetJoystickButtons for that joystick. + * + * @ingroup input + */ +GLFWAPI const unsigned char* glfwGetJoystickButtons(int joy, int* count); + +/*! @brief Returns the name of the specified joystick. + * + * This function returns the name, encoded as UTF-8, of the specified joystick. + * + * @param[in] joy The joystick to query. + * @return The UTF-8 encoded name of the joystick, or `NULL` if the joystick + * is not present. + * + * @note The returned string is allocated and freed by GLFW. You should not + * free it yourself. + * + * @note The returned string is valid only until the next call to @ref + * glfwGetJoystickName for that joystick. + * + * @ingroup input + */ +GLFWAPI const char* glfwGetJoystickName(int joy); + +/*! @brief Sets the clipboard to the specified string. + * + * This function sets the system clipboard to the specified, UTF-8 encoded + * string. The string is copied before returning, so you don't have to retain + * it afterwards. + * + * @param[in] window The window that will own the clipboard contents. + * @param[in] string A UTF-8 encoded string. + * + * @note This function may only be called from the main thread. + * + * @sa glfwGetClipboardString + * + * @ingroup clipboard + */ +GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char* string); + +/*! @brief Retrieves the contents of the clipboard as a string. + * + * This function returns the contents of the system clipboard, if it contains + * or is convertible to a UTF-8 encoded string. + * + * @param[in] window The window that will request the clipboard contents. + * @return The contents of the clipboard as a UTF-8 encoded string, or `NULL` + * if an error occurred. + * + * @note This function may only be called from the main thread. + * + * @note The returned string is allocated and freed by GLFW. You should not + * free it yourself. + * + * @note The returned string is valid only until the next call to @ref + * glfwGetClipboardString or @ref glfwSetClipboardString. + * + * @sa glfwSetClipboardString + * + * @ingroup clipboard + */ +GLFWAPI const char* glfwGetClipboardString(GLFWwindow* window); + +/*! @brief Returns the value of the GLFW timer. + * + * This function returns the value of the GLFW timer. Unless the timer has + * been set using @ref glfwSetTime, the timer measures time elapsed since GLFW + * was initialized. + * + * @return The current value, in seconds, or zero if an error occurred. + * + * @remarks This function may be called from secondary threads. + * + * @note The resolution of the timer is system dependent, but is usually on the + * order of a few micro- or nanoseconds. It uses the highest-resolution + * monotonic time source on each supported platform. + * + * @ingroup time + */ +GLFWAPI double glfwGetTime(void); + +/*! @brief Sets the GLFW timer. + * + * This function sets the value of the GLFW timer. It then continues to count + * up from that value. + * + * @param[in] time The new value, in seconds. + * + * @note The resolution of the timer is system dependent, but is usually on the + * order of a few micro- or nanoseconds. It uses the highest-resolution + * monotonic time source on each supported platform. + * + * @ingroup time + */ +GLFWAPI void glfwSetTime(double time); + +/*! @brief Makes the context of the specified window current for the calling + * thread. + * + * This function makes the context of the specified window current on the + * calling thread. A context can only be made current on a single thread at + * a time and each thread can have only a single current context at a time. + * + * @param[in] window The window whose context to make current, or `NULL` to + * detach the current context. + * + * @remarks This function may be called from secondary threads. + * + * @sa glfwGetCurrentContext + * + * @ingroup context + */ +GLFWAPI void glfwMakeContextCurrent(GLFWwindow* window); + +/*! @brief Returns the window whose context is current on the calling thread. + * + * This function returns the window whose context is current on the calling + * thread. + * + * @return The window whose context is current, or `NULL` if no window's + * context is current. + * + * @remarks This function may be called from secondary threads. + * + * @sa glfwMakeContextCurrent + * + * @ingroup context + */ +GLFWAPI GLFWwindow* glfwGetCurrentContext(void); + +/*! @brief Swaps the front and back buffers of the specified window. + * + * This function swaps the front and back buffers of the specified window. If + * the swap interval is greater than zero, the GPU driver waits the specified + * number of screen updates before swapping the buffers. + * + * @param[in] window The window whose buffers to swap. + * + * @remarks This function may be called from secondary threads. + * + * @par New in GLFW 3 + * This function no longer calls @ref glfwPollEvents. You need to call it or + * @ref glfwWaitEvents yourself. + * + * @sa glfwSwapInterval + * + * @ingroup context + */ +GLFWAPI void glfwSwapBuffers(GLFWwindow* window); + +/*! @brief Sets the swap interval for the current context. + * + * This function sets the swap interval for the current context, i.e. the + * number of screen updates to wait before swapping the buffers of a window and + * returning from @ref glfwSwapBuffers. This is sometimes called 'vertical + * synchronization', 'vertical retrace synchronization' or 'vsync'. + * + * Contexts that support either of the `WGL_EXT_swap_control_tear` and + * `GLX_EXT_swap_control_tear` extensions also accept negative swap intervals, + * which allow the driver to swap even if a frame arrives a little bit late. + * You can check for the presence of these extensions using @ref + * glfwExtensionSupported. For more information about swap tearing, see the + * extension specifications. + * + * @param[in] interval The minimum number of screen updates to wait for + * until the buffers are swapped by @ref glfwSwapBuffers. + * + * @remarks This function may be called from secondary threads. + * + * @note Some GPU drivers do not honor the requested swap interval, either + * because of user settings that override the request or due to bugs in the + * driver. + * + * @sa glfwSwapBuffers + * + * @ingroup context + */ +GLFWAPI void glfwSwapInterval(int interval); + +/*! @brief Returns whether the specified extension is available. + * + * This function returns whether the specified + * [OpenGL or context creation API extension](@ref context_glext) is supported + * by the current context. For example, on Windows both the OpenGL and WGL + * extension strings are checked. + * + * @param[in] extension The ASCII encoded name of the extension. + * @return `GL_TRUE` if the extension is available, or `GL_FALSE` otherwise. + * + * @remarks This function may be called from secondary threads. + * + * @note As this functions searches one or more extension strings on each call, + * it is recommended that you cache its results if it's going to be used + * frequently. The extension strings will not change during the lifetime of + * a context, so there is no danger in doing this. + * + * @ingroup context + */ +GLFWAPI int glfwExtensionSupported(const char* extension); + +/*! @brief Returns the address of the specified function for the current + * context. + * + * This function returns the address of the specified + * [client API or extension function](@ref context_glext), if it is supported + * by the current context. + * + * @param[in] procname The ASCII encoded name of the function. + * @return The address of the function, or `NULL` if the function is + * unavailable. + * + * @remarks This function may be called from secondary threads. + * + * @note The addresses of these functions are not guaranteed to be the same for + * all contexts, especially if they use different client APIs or even different + * context creation hints. + * + * @ingroup context + */ +GLFWAPI GLFWglproc glfwGetProcAddress(const char* procname); + + +/************************************************************************* + * Global definition cleanup + *************************************************************************/ + +/* ------------------- BEGIN SYSTEM/COMPILER SPECIFIC -------------------- */ + +#ifdef GLFW_WINGDIAPI_DEFINED + #undef WINGDIAPI + #undef GLFW_WINGDIAPI_DEFINED +#endif + +#ifdef GLFW_CALLBACK_DEFINED + #undef CALLBACK + #undef GLFW_CALLBACK_DEFINED +#endif + +/* -------------------- END SYSTEM/COMPILER SPECIFIC --------------------- */ + + +#ifdef __cplusplus +} +#endif + +#endif /* _glfw3_h_ */ + diff --git a/external/glfw3/lib/libglfw3.a b/external/glfw3/lib/libglfw3.a new file mode 100644 index 000000000..2ea1554b1 Binary files /dev/null and b/external/glfw3/lib/libglfw3.a differ diff --git a/external/openal_soft/include/AL/al.h b/external/openal_soft/include/AL/al.h new file mode 100644 index 000000000..413b38331 --- /dev/null +++ b/external/openal_soft/include/AL/al.h @@ -0,0 +1,656 @@ +#ifndef AL_AL_H +#define AL_AL_H + +#if defined(__cplusplus) +extern "C" { +#endif + +#ifndef AL_API + #if defined(AL_LIBTYPE_STATIC) + #define AL_API + #elif defined(_WIN32) + #define AL_API __declspec(dllimport) + #else + #define AL_API extern + #endif +#endif + +#if defined(_WIN32) + #define AL_APIENTRY __cdecl +#else + #define AL_APIENTRY +#endif + + +/** Deprecated macro. */ +#define OPENAL +#define ALAPI AL_API +#define ALAPIENTRY AL_APIENTRY +#define AL_INVALID (-1) +#define AL_ILLEGAL_ENUM AL_INVALID_ENUM +#define AL_ILLEGAL_COMMAND AL_INVALID_OPERATION + +/** Supported AL version. */ +#define AL_VERSION_1_0 +#define AL_VERSION_1_1 + +/** 8-bit boolean */ +typedef char ALboolean; + +/** character */ +typedef char ALchar; + +/** signed 8-bit 2's complement integer */ +typedef signed char ALbyte; + +/** unsigned 8-bit integer */ +typedef unsigned char ALubyte; + +/** signed 16-bit 2's complement integer */ +typedef short ALshort; + +/** unsigned 16-bit integer */ +typedef unsigned short ALushort; + +/** signed 32-bit 2's complement integer */ +typedef int ALint; + +/** unsigned 32-bit integer */ +typedef unsigned int ALuint; + +/** non-negative 32-bit binary integer size */ +typedef int ALsizei; + +/** enumerated 32-bit value */ +typedef int ALenum; + +/** 32-bit IEEE754 floating-point */ +typedef float ALfloat; + +/** 64-bit IEEE754 floating-point */ +typedef double ALdouble; + +/** void type (for opaque pointers only) */ +typedef void ALvoid; + + +/* Enumerant values begin at column 50. No tabs. */ + +/** "no distance model" or "no buffer" */ +#define AL_NONE 0 + +/** Boolean False. */ +#define AL_FALSE 0 + +/** Boolean True. */ +#define AL_TRUE 1 + + +/** + * Relative source. + * Type: ALboolean + * Range: [AL_TRUE, AL_FALSE] + * Default: AL_FALSE + * + * Specifies if the Source has relative coordinates. + */ +#define AL_SOURCE_RELATIVE 0x202 + + +/** + * Inner cone angle, in degrees. + * Type: ALint, ALfloat + * Range: [0 - 360] + * Default: 360 + * + * The angle covered by the inner cone, where the source will not attenuate. + */ +#define AL_CONE_INNER_ANGLE 0x1001 + +/** + * Outer cone angle, in degrees. + * Range: [0 - 360] + * Default: 360 + * + * The angle covered by the outer cone, where the source will be fully + * attenuated. + */ +#define AL_CONE_OUTER_ANGLE 0x1002 + +/** + * Source pitch. + * Type: ALfloat + * Range: [0.5 - 2.0] + * Default: 1.0 + * + * A multiplier for the frequency (sample rate) of the source's buffer. + */ +#define AL_PITCH 0x1003 + +/** + * Source or listener position. + * Type: ALfloat[3], ALint[3] + * Default: {0, 0, 0} + * + * The source or listener location in three dimensional space. + * + * OpenAL, like OpenGL, uses a right handed coordinate system, where in a + * frontal default view X (thumb) points right, Y points up (index finger), and + * Z points towards the viewer/camera (middle finger). + * + * To switch from a left handed coordinate system, flip the sign on the Z + * coordinate. + */ +#define AL_POSITION 0x1004 + +/** + * Source direction. + * Type: ALfloat[3], ALint[3] + * Default: {0, 0, 0} + * + * Specifies the current direction in local space. + * A zero-length vector specifies an omni-directional source (cone is ignored). + */ +#define AL_DIRECTION 0x1005 + +/** + * Source or listener velocity. + * Type: ALfloat[3], ALint[3] + * Default: {0, 0, 0} + * + * Specifies the current velocity in local space. + */ +#define AL_VELOCITY 0x1006 + +/** + * Source looping. + * Type: ALboolean + * Range: [AL_TRUE, AL_FALSE] + * Default: AL_FALSE + * + * Specifies whether source is looping. + */ +#define AL_LOOPING 0x1007 + +/** + * Source buffer. + * Type: ALuint + * Range: any valid Buffer. + * + * Specifies the buffer to provide sound samples. + */ +#define AL_BUFFER 0x1009 + +/** + * Source or listener gain. + * Type: ALfloat + * Range: [0.0 - ] + * + * A value of 1.0 means unattenuated. Each division by 2 equals an attenuation + * of about -6dB. Each multiplicaton by 2 equals an amplification of about + * +6dB. + * + * A value of 0.0 is meaningless with respect to a logarithmic scale; it is + * silent. + */ +#define AL_GAIN 0x100A + +/** + * Minimum source gain. + * Type: ALfloat + * Range: [0.0 - 1.0] + * + * The minimum gain allowed for a source, after distance and cone attenation is + * applied (if applicable). + */ +#define AL_MIN_GAIN 0x100D + +/** + * Maximum source gain. + * Type: ALfloat + * Range: [0.0 - 1.0] + * + * The maximum gain allowed for a source, after distance and cone attenation is + * applied (if applicable). + */ +#define AL_MAX_GAIN 0x100E + +/** + * Listener orientation. + * Type: ALfloat[6] + * Default: {0.0, 0.0, -1.0, 0.0, 1.0, 0.0} + * + * Effectively two three dimensional vectors. The first vector is the front (or + * "at") and the second is the top (or "up"). + * + * Both vectors are in local space. + */ +#define AL_ORIENTATION 0x100F + +/** + * Source state (query only). + * Type: ALint + * Range: [AL_INITIAL, AL_PLAYING, AL_PAUSED, AL_STOPPED] + */ +#define AL_SOURCE_STATE 0x1010 + +/** Source state value. */ +#define AL_INITIAL 0x1011 +#define AL_PLAYING 0x1012 +#define AL_PAUSED 0x1013 +#define AL_STOPPED 0x1014 + +/** + * Source Buffer Queue size (query only). + * Type: ALint + * + * The number of buffers queued using alSourceQueueBuffers, minus the buffers + * removed with alSourceUnqueueBuffers. + */ +#define AL_BUFFERS_QUEUED 0x1015 + +/** + * Source Buffer Queue processed count (query only). + * Type: ALint + * + * The number of queued buffers that have been fully processed, and can be + * removed with alSourceUnqueueBuffers. + * + * Looping sources will never fully process buffers because they will be set to + * play again for when the source loops. + */ +#define AL_BUFFERS_PROCESSED 0x1016 + +/** + * Source reference distance. + * Type: ALfloat + * Range: [0.0 - ] + * Default: 1.0 + * + * The distance in units that no attenuation occurs. + * + * At 0.0, no distance attenuation ever occurs on non-linear attenuation models. + */ +#define AL_REFERENCE_DISTANCE 0x1020 + +/** + * Source rolloff factor. + * Type: ALfloat + * Range: [0.0 - ] + * Default: 1.0 + * + * Multiplier to exaggerate or diminish distance attenuation. + * + * At 0.0, no distance attenuation ever occurs. + */ +#define AL_ROLLOFF_FACTOR 0x1021 + +/** + * Outer cone gain. + * Type: ALfloat + * Range: [0.0 - 1.0] + * Default: 0.0 + * + * The gain attenuation applied when the listener is outside of the source's + * outer cone. + */ +#define AL_CONE_OUTER_GAIN 0x1022 + +/** + * Source maximum distance. + * Type: ALfloat + * Range: [0.0 - ] + * Default: +inf + * + * The distance above which the source is not attenuated any further with a + * clamped distance model, or where attenuation reaches 0.0 gain for linear + * distance models with a default rolloff factor. + */ +#define AL_MAX_DISTANCE 0x1023 + +/** Source buffer position, in seconds */ +#define AL_SEC_OFFSET 0x1024 +/** Source buffer position, in sample frames */ +#define AL_SAMPLE_OFFSET 0x1025 +/** Source buffer position, in bytes */ +#define AL_BYTE_OFFSET 0x1026 + +/** + * Source type (query only). + * Type: ALint + * Range: [AL_STATIC, AL_STREAMING, AL_UNDETERMINED] + * + * A Source is Static if a Buffer has been attached using AL_BUFFER. + * + * A Source is Streaming if one or more Buffers have been attached using + * alSourceQueueBuffers. + * + * A Source is Undetermined when it has the NULL buffer attached using + * AL_BUFFER. + */ +#define AL_SOURCE_TYPE 0x1027 + +/** Source type value. */ +#define AL_STATIC 0x1028 +#define AL_STREAMING 0x1029 +#define AL_UNDETERMINED 0x1030 + +/** Buffer format specifier. */ +#define AL_FORMAT_MONO8 0x1100 +#define AL_FORMAT_MONO16 0x1101 +#define AL_FORMAT_STEREO8 0x1102 +#define AL_FORMAT_STEREO16 0x1103 + +/** Buffer frequency (query only). */ +#define AL_FREQUENCY 0x2001 +/** Buffer bits per sample (query only). */ +#define AL_BITS 0x2002 +/** Buffer channel count (query only). */ +#define AL_CHANNELS 0x2003 +/** Buffer data size (query only). */ +#define AL_SIZE 0x2004 + +/** + * Buffer state. + * + * Not for public use. + */ +#define AL_UNUSED 0x2010 +#define AL_PENDING 0x2011 +#define AL_PROCESSED 0x2012 + + +/** No error. */ +#define AL_NO_ERROR 0 + +/** Invalid name paramater passed to AL call. */ +#define AL_INVALID_NAME 0xA001 + +/** Invalid enum parameter passed to AL call. */ +#define AL_INVALID_ENUM 0xA002 + +/** Invalid value parameter passed to AL call. */ +#define AL_INVALID_VALUE 0xA003 + +/** Illegal AL call. */ +#define AL_INVALID_OPERATION 0xA004 + +/** Not enough memory. */ +#define AL_OUT_OF_MEMORY 0xA005 + + +/** Context string: Vendor ID. */ +#define AL_VENDOR 0xB001 +/** Context string: Version. */ +#define AL_VERSION 0xB002 +/** Context string: Renderer ID. */ +#define AL_RENDERER 0xB003 +/** Context string: Space-separated extension list. */ +#define AL_EXTENSIONS 0xB004 + + +/** + * Doppler scale. + * Type: ALfloat + * Range: [0.0 - ] + * Default: 1.0 + * + * Scale for source and listener velocities. + */ +#define AL_DOPPLER_FACTOR 0xC000 +AL_API void AL_APIENTRY alDopplerFactor(ALfloat value); + +/** + * Doppler velocity (deprecated). + * + * A multiplier applied to the Speed of Sound. + */ +#define AL_DOPPLER_VELOCITY 0xC001 +AL_API void AL_APIENTRY alDopplerVelocity(ALfloat value); + +/** + * Speed of Sound, in units per second. + * Type: ALfloat + * Range: [0.0001 - ] + * Default: 343.3 + * + * The speed at which sound waves are assumed to travel, when calculating the + * doppler effect. + */ +#define AL_SPEED_OF_SOUND 0xC003 +AL_API void AL_APIENTRY alSpeedOfSound(ALfloat value); + +/** + * Distance attenuation model. + * Type: ALint + * Range: [AL_NONE, AL_INVERSE_DISTANCE, AL_INVERSE_DISTANCE_CLAMPED, + * AL_LINEAR_DISTANCE, AL_LINEAR_DISTANCE_CLAMPED, + * AL_EXPONENT_DISTANCE, AL_EXPONENT_DISTANCE_CLAMPED] + * Default: AL_INVERSE_DISTANCE_CLAMPED + * + * The model by which sources attenuate with distance. + * + * None - No distance attenuation. + * Inverse - Doubling the distance halves the source gain. + * Linear - Linear gain scaling between the reference and max distances. + * Exponent - Exponential gain dropoff. + * + * Clamped variations work like the non-clamped counterparts, except the + * distance calculated is clamped between the reference and max distances. + */ +#define AL_DISTANCE_MODEL 0xD000 +AL_API void AL_APIENTRY alDistanceModel(ALenum distanceModel); + +/** Distance model value. */ +#define AL_INVERSE_DISTANCE 0xD001 +#define AL_INVERSE_DISTANCE_CLAMPED 0xD002 +#define AL_LINEAR_DISTANCE 0xD003 +#define AL_LINEAR_DISTANCE_CLAMPED 0xD004 +#define AL_EXPONENT_DISTANCE 0xD005 +#define AL_EXPONENT_DISTANCE_CLAMPED 0xD006 + +/** Renderer State management. */ +AL_API void AL_APIENTRY alEnable(ALenum capability); +AL_API void AL_APIENTRY alDisable(ALenum capability); +AL_API ALboolean AL_APIENTRY alIsEnabled(ALenum capability); + +/** State retrieval. */ +AL_API const ALchar* AL_APIENTRY alGetString(ALenum param); +AL_API void AL_APIENTRY alGetBooleanv(ALenum param, ALboolean *values); +AL_API void AL_APIENTRY alGetIntegerv(ALenum param, ALint *values); +AL_API void AL_APIENTRY alGetFloatv(ALenum param, ALfloat *values); +AL_API void AL_APIENTRY alGetDoublev(ALenum param, ALdouble *values); +AL_API ALboolean AL_APIENTRY alGetBoolean(ALenum param); +AL_API ALint AL_APIENTRY alGetInteger(ALenum param); +AL_API ALfloat AL_APIENTRY alGetFloat(ALenum param); +AL_API ALdouble AL_APIENTRY alGetDouble(ALenum param); + +/** + * Error retrieval. + * + * Obtain the first error generated in the AL context since the last check. + */ +AL_API ALenum AL_APIENTRY alGetError(void); + +/** + * Extension support. + * + * Query for the presence of an extension, and obtain any appropriate function + * pointers and enum values. + */ +AL_API ALboolean AL_APIENTRY alIsExtensionPresent(const ALchar *extname); +AL_API void* AL_APIENTRY alGetProcAddress(const ALchar *fname); +AL_API ALenum AL_APIENTRY alGetEnumValue(const ALchar *ename); + + +/** Set Listener parameters */ +AL_API void AL_APIENTRY alListenerf(ALenum param, ALfloat value); +AL_API void AL_APIENTRY alListener3f(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); +AL_API void AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values); +AL_API void AL_APIENTRY alListeneri(ALenum param, ALint value); +AL_API void AL_APIENTRY alListener3i(ALenum param, ALint value1, ALint value2, ALint value3); +AL_API void AL_APIENTRY alListeneriv(ALenum param, const ALint *values); + +/** Get Listener parameters */ +AL_API void AL_APIENTRY alGetListenerf(ALenum param, ALfloat *value); +AL_API void AL_APIENTRY alGetListener3f(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3); +AL_API void AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values); +AL_API void AL_APIENTRY alGetListeneri(ALenum param, ALint *value); +AL_API void AL_APIENTRY alGetListener3i(ALenum param, ALint *value1, ALint *value2, ALint *value3); +AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint *values); + + +/** Create Source objects. */ +AL_API void AL_APIENTRY alGenSources(ALsizei n, ALuint *sources); +/** Delete Source objects. */ +AL_API void AL_APIENTRY alDeleteSources(ALsizei n, const ALuint *sources); +/** Verify a handle is a valid Source. */ +AL_API ALboolean AL_APIENTRY alIsSource(ALuint source); + +/** Set Source parameters. */ +AL_API void AL_APIENTRY alSourcef(ALuint source, ALenum param, ALfloat value); +AL_API void AL_APIENTRY alSource3f(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); +AL_API void AL_APIENTRY alSourcefv(ALuint source, ALenum param, const ALfloat *values); +AL_API void AL_APIENTRY alSourcei(ALuint source, ALenum param, ALint value); +AL_API void AL_APIENTRY alSource3i(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3); +AL_API void AL_APIENTRY alSourceiv(ALuint source, ALenum param, const ALint *values); + +/** Get Source parameters. */ +AL_API void AL_APIENTRY alGetSourcef(ALuint source, ALenum param, ALfloat *value); +AL_API void AL_APIENTRY alGetSource3f(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3); +AL_API void AL_APIENTRY alGetSourcefv(ALuint source, ALenum param, ALfloat *values); +AL_API void AL_APIENTRY alGetSourcei(ALuint source, ALenum param, ALint *value); +AL_API void AL_APIENTRY alGetSource3i(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3); +AL_API void AL_APIENTRY alGetSourceiv(ALuint source, ALenum param, ALint *values); + + +/** Play, replay, or resume (if paused) a list of Sources */ +AL_API void AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources); +/** Stop a list of Sources */ +AL_API void AL_APIENTRY alSourceStopv(ALsizei n, const ALuint *sources); +/** Rewind a list of Sources */ +AL_API void AL_APIENTRY alSourceRewindv(ALsizei n, const ALuint *sources); +/** Pause a list of Sources */ +AL_API void AL_APIENTRY alSourcePausev(ALsizei n, const ALuint *sources); + +/** Play, replay, or resume a Source */ +AL_API void AL_APIENTRY alSourcePlay(ALuint source); +/** Stop a Source */ +AL_API void AL_APIENTRY alSourceStop(ALuint source); +/** Rewind a Source (set playback postiton to beginning) */ +AL_API void AL_APIENTRY alSourceRewind(ALuint source); +/** Pause a Source */ +AL_API void AL_APIENTRY alSourcePause(ALuint source); + +/** Queue buffers onto a source */ +AL_API void AL_APIENTRY alSourceQueueBuffers(ALuint source, ALsizei nb, const ALuint *buffers); +/** Unqueue processed buffers from a source */ +AL_API void AL_APIENTRY alSourceUnqueueBuffers(ALuint source, ALsizei nb, ALuint *buffers); + + +/** Create Buffer objects */ +AL_API void AL_APIENTRY alGenBuffers(ALsizei n, ALuint *buffers); +/** Delete Buffer objects */ +AL_API void AL_APIENTRY alDeleteBuffers(ALsizei n, const ALuint *buffers); +/** Verify a handle is a valid Buffer */ +AL_API ALboolean AL_APIENTRY alIsBuffer(ALuint buffer); + +/** Specifies the data to be copied into a buffer */ +AL_API void AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq); + +/** Set Buffer parameters, */ +AL_API void AL_APIENTRY alBufferf(ALuint buffer, ALenum param, ALfloat value); +AL_API void AL_APIENTRY alBuffer3f(ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); +AL_API void AL_APIENTRY alBufferfv(ALuint buffer, ALenum param, const ALfloat *values); +AL_API void AL_APIENTRY alBufferi(ALuint buffer, ALenum param, ALint value); +AL_API void AL_APIENTRY alBuffer3i(ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3); +AL_API void AL_APIENTRY alBufferiv(ALuint buffer, ALenum param, const ALint *values); + +/** Get Buffer parameters. */ +AL_API void AL_APIENTRY alGetBufferf(ALuint buffer, ALenum param, ALfloat *value); +AL_API void AL_APIENTRY alGetBuffer3f(ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3); +AL_API void AL_APIENTRY alGetBufferfv(ALuint buffer, ALenum param, ALfloat *values); +AL_API void AL_APIENTRY alGetBufferi(ALuint buffer, ALenum param, ALint *value); +AL_API void AL_APIENTRY alGetBuffer3i(ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3); +AL_API void AL_APIENTRY alGetBufferiv(ALuint buffer, ALenum param, ALint *values); + +/** Pointer-to-function type, useful for dynamically getting AL entry points. */ +typedef void (AL_APIENTRY *LPALENABLE)(ALenum capability); +typedef void (AL_APIENTRY *LPALDISABLE)(ALenum capability); +typedef ALboolean (AL_APIENTRY *LPALISENABLED)(ALenum capability); +typedef const ALchar* (AL_APIENTRY *LPALGETSTRING)(ALenum param); +typedef void (AL_APIENTRY *LPALGETBOOLEANV)(ALenum param, ALboolean *values); +typedef void (AL_APIENTRY *LPALGETINTEGERV)(ALenum param, ALint *values); +typedef void (AL_APIENTRY *LPALGETFLOATV)(ALenum param, ALfloat *values); +typedef void (AL_APIENTRY *LPALGETDOUBLEV)(ALenum param, ALdouble *values); +typedef ALboolean (AL_APIENTRY *LPALGETBOOLEAN)(ALenum param); +typedef ALint (AL_APIENTRY *LPALGETINTEGER)(ALenum param); +typedef ALfloat (AL_APIENTRY *LPALGETFLOAT)(ALenum param); +typedef ALdouble (AL_APIENTRY *LPALGETDOUBLE)(ALenum param); +typedef ALenum (AL_APIENTRY *LPALGETERROR)(void); +typedef ALboolean (AL_APIENTRY *LPALISEXTENSIONPRESENT)(const ALchar *extname); +typedef void* (AL_APIENTRY *LPALGETPROCADDRESS)(const ALchar *fname); +typedef ALenum (AL_APIENTRY *LPALGETENUMVALUE)(const ALchar *ename); +typedef void (AL_APIENTRY *LPALLISTENERF)(ALenum param, ALfloat value); +typedef void (AL_APIENTRY *LPALLISTENER3F)(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); +typedef void (AL_APIENTRY *LPALLISTENERFV)(ALenum param, const ALfloat *values); +typedef void (AL_APIENTRY *LPALLISTENERI)(ALenum param, ALint value); +typedef void (AL_APIENTRY *LPALLISTENER3I)(ALenum param, ALint value1, ALint value2, ALint value3); +typedef void (AL_APIENTRY *LPALLISTENERIV)(ALenum param, const ALint *values); +typedef void (AL_APIENTRY *LPALGETLISTENERF)(ALenum param, ALfloat *value); +typedef void (AL_APIENTRY *LPALGETLISTENER3F)(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3); +typedef void (AL_APIENTRY *LPALGETLISTENERFV)(ALenum param, ALfloat *values); +typedef void (AL_APIENTRY *LPALGETLISTENERI)(ALenum param, ALint *value); +typedef void (AL_APIENTRY *LPALGETLISTENER3I)(ALenum param, ALint *value1, ALint *value2, ALint *value3); +typedef void (AL_APIENTRY *LPALGETLISTENERIV)(ALenum param, ALint *values); +typedef void (AL_APIENTRY *LPALGENSOURCES)(ALsizei n, ALuint *sources); +typedef void (AL_APIENTRY *LPALDELETESOURCES)(ALsizei n, const ALuint *sources); +typedef ALboolean (AL_APIENTRY *LPALISSOURCE)(ALuint source); +typedef void (AL_APIENTRY *LPALSOURCEF)(ALuint source, ALenum param, ALfloat value); +typedef void (AL_APIENTRY *LPALSOURCE3F)(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); +typedef void (AL_APIENTRY *LPALSOURCEFV)(ALuint source, ALenum param, const ALfloat *values); +typedef void (AL_APIENTRY *LPALSOURCEI)(ALuint source, ALenum param, ALint value); +typedef void (AL_APIENTRY *LPALSOURCE3I)(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3); +typedef void (AL_APIENTRY *LPALSOURCEIV)(ALuint source, ALenum param, const ALint *values); +typedef void (AL_APIENTRY *LPALGETSOURCEF)(ALuint source, ALenum param, ALfloat *value); +typedef void (AL_APIENTRY *LPALGETSOURCE3F)(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3); +typedef void (AL_APIENTRY *LPALGETSOURCEFV)(ALuint source, ALenum param, ALfloat *values); +typedef void (AL_APIENTRY *LPALGETSOURCEI)(ALuint source, ALenum param, ALint *value); +typedef void (AL_APIENTRY *LPALGETSOURCE3I)(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3); +typedef void (AL_APIENTRY *LPALGETSOURCEIV)(ALuint source, ALenum param, ALint *values); +typedef void (AL_APIENTRY *LPALSOURCEPLAYV)(ALsizei n, const ALuint *sources); +typedef void (AL_APIENTRY *LPALSOURCESTOPV)(ALsizei n, const ALuint *sources); +typedef void (AL_APIENTRY *LPALSOURCEREWINDV)(ALsizei n, const ALuint *sources); +typedef void (AL_APIENTRY *LPALSOURCEPAUSEV)(ALsizei n, const ALuint *sources); +typedef void (AL_APIENTRY *LPALSOURCEPLAY)(ALuint source); +typedef void (AL_APIENTRY *LPALSOURCESTOP)(ALuint source); +typedef void (AL_APIENTRY *LPALSOURCEREWIND)(ALuint source); +typedef void (AL_APIENTRY *LPALSOURCEPAUSE)(ALuint source); +typedef void (AL_APIENTRY *LPALSOURCEQUEUEBUFFERS)(ALuint source, ALsizei nb, const ALuint *buffers); +typedef void (AL_APIENTRY *LPALSOURCEUNQUEUEBUFFERS)(ALuint source, ALsizei nb, ALuint *buffers); +typedef void (AL_APIENTRY *LPALGENBUFFERS)(ALsizei n, ALuint *buffers); +typedef void (AL_APIENTRY *LPALDELETEBUFFERS)(ALsizei n, const ALuint *buffers); +typedef ALboolean (AL_APIENTRY *LPALISBUFFER)(ALuint buffer); +typedef void (AL_APIENTRY *LPALBUFFERDATA)(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq); +typedef void (AL_APIENTRY *LPALBUFFERF)(ALuint buffer, ALenum param, ALfloat value); +typedef void (AL_APIENTRY *LPALBUFFER3F)(ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); +typedef void (AL_APIENTRY *LPALBUFFERFV)(ALuint buffer, ALenum param, const ALfloat *values); +typedef void (AL_APIENTRY *LPALBUFFERI)(ALuint buffer, ALenum param, ALint value); +typedef void (AL_APIENTRY *LPALBUFFER3I)(ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3); +typedef void (AL_APIENTRY *LPALBUFFERIV)(ALuint buffer, ALenum param, const ALint *values); +typedef void (AL_APIENTRY *LPALGETBUFFERF)(ALuint buffer, ALenum param, ALfloat *value); +typedef void (AL_APIENTRY *LPALGETBUFFER3F)(ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3); +typedef void (AL_APIENTRY *LPALGETBUFFERFV)(ALuint buffer, ALenum param, ALfloat *values); +typedef void (AL_APIENTRY *LPALGETBUFFERI)(ALuint buffer, ALenum param, ALint *value); +typedef void (AL_APIENTRY *LPALGETBUFFER3I)(ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3); +typedef void (AL_APIENTRY *LPALGETBUFFERIV)(ALuint buffer, ALenum param, ALint *values); +typedef void (AL_APIENTRY *LPALDOPPLERFACTOR)(ALfloat value); +typedef void (AL_APIENTRY *LPALDOPPLERVELOCITY)(ALfloat value); +typedef void (AL_APIENTRY *LPALSPEEDOFSOUND)(ALfloat value); +typedef void (AL_APIENTRY *LPALDISTANCEMODEL)(ALenum distanceModel); + +#if defined(__cplusplus) +} /* extern "C" */ +#endif + +#endif /* AL_AL_H */ diff --git a/external/openal_soft/include/AL/alc.h b/external/openal_soft/include/AL/alc.h new file mode 100644 index 000000000..294e8b33c --- /dev/null +++ b/external/openal_soft/include/AL/alc.h @@ -0,0 +1,237 @@ +#ifndef AL_ALC_H +#define AL_ALC_H + +#if defined(__cplusplus) +extern "C" { +#endif + +#ifndef ALC_API + #if defined(AL_LIBTYPE_STATIC) + #define ALC_API + #elif defined(_WIN32) + #define ALC_API __declspec(dllimport) + #else + #define ALC_API extern + #endif +#endif + +#if defined(_WIN32) + #define ALC_APIENTRY __cdecl +#else + #define ALC_APIENTRY +#endif + + +/** Deprecated macro. */ +#define ALCAPI ALC_API +#define ALCAPIENTRY ALC_APIENTRY +#define ALC_INVALID 0 + +/** Supported ALC version? */ +#define ALC_VERSION_0_1 1 + +/** Opaque device handle */ +typedef struct ALCdevice_struct ALCdevice; +/** Opaque context handle */ +typedef struct ALCcontext_struct ALCcontext; + +/** 8-bit boolean */ +typedef char ALCboolean; + +/** character */ +typedef char ALCchar; + +/** signed 8-bit 2's complement integer */ +typedef signed char ALCbyte; + +/** unsigned 8-bit integer */ +typedef unsigned char ALCubyte; + +/** signed 16-bit 2's complement integer */ +typedef short ALCshort; + +/** unsigned 16-bit integer */ +typedef unsigned short ALCushort; + +/** signed 32-bit 2's complement integer */ +typedef int ALCint; + +/** unsigned 32-bit integer */ +typedef unsigned int ALCuint; + +/** non-negative 32-bit binary integer size */ +typedef int ALCsizei; + +/** enumerated 32-bit value */ +typedef int ALCenum; + +/** 32-bit IEEE754 floating-point */ +typedef float ALCfloat; + +/** 64-bit IEEE754 floating-point */ +typedef double ALCdouble; + +/** void type (for opaque pointers only) */ +typedef void ALCvoid; + + +/* Enumerant values begin at column 50. No tabs. */ + +/** Boolean False. */ +#define ALC_FALSE 0 + +/** Boolean True. */ +#define ALC_TRUE 1 + +/** Context attribute: Hz. */ +#define ALC_FREQUENCY 0x1007 + +/** Context attribute: Hz. */ +#define ALC_REFRESH 0x1008 + +/** Context attribute: AL_TRUE or AL_FALSE. */ +#define ALC_SYNC 0x1009 + +/** Context attribute: requested Mono (3D) Sources. */ +#define ALC_MONO_SOURCES 0x1010 + +/** Context attribute: requested Stereo Sources. */ +#define ALC_STEREO_SOURCES 0x1011 + +/** No error. */ +#define ALC_NO_ERROR 0 + +/** Invalid device handle. */ +#define ALC_INVALID_DEVICE 0xA001 + +/** Invalid context handle. */ +#define ALC_INVALID_CONTEXT 0xA002 + +/** Invalid enum parameter passed to an ALC call. */ +#define ALC_INVALID_ENUM 0xA003 + +/** Invalid value parameter passed to an ALC call. */ +#define ALC_INVALID_VALUE 0xA004 + +/** Out of memory. */ +#define ALC_OUT_OF_MEMORY 0xA005 + + +/** Runtime ALC version. */ +#define ALC_MAJOR_VERSION 0x1000 +#define ALC_MINOR_VERSION 0x1001 + +/** Context attribute list properties. */ +#define ALC_ATTRIBUTES_SIZE 0x1002 +#define ALC_ALL_ATTRIBUTES 0x1003 + +/** String for the default device specifier. */ +#define ALC_DEFAULT_DEVICE_SPECIFIER 0x1004 +/** + * String for the given device's specifier. + * + * If device handle is NULL, it is instead a null-char separated list of + * strings of known device specifiers (list ends with an empty string). + */ +#define ALC_DEVICE_SPECIFIER 0x1005 +/** String for space-separated list of ALC extensions. */ +#define ALC_EXTENSIONS 0x1006 + + +/** Capture extension */ +#define ALC_EXT_CAPTURE 1 +/** + * String for the given capture device's specifier. + * + * If device handle is NULL, it is instead a null-char separated list of + * strings of known capture device specifiers (list ends with an empty string). + */ +#define ALC_CAPTURE_DEVICE_SPECIFIER 0x310 +/** String for the default capture device specifier. */ +#define ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER 0x311 +/** Number of sample frames available for capture. */ +#define ALC_CAPTURE_SAMPLES 0x312 + + +/** Enumerate All extension */ +#define ALC_ENUMERATE_ALL_EXT 1 +/** String for the default extended device specifier. */ +#define ALC_DEFAULT_ALL_DEVICES_SPECIFIER 0x1012 +/** + * String for the given extended device's specifier. + * + * If device handle is NULL, it is instead a null-char separated list of + * strings of known extended device specifiers (list ends with an empty string). + */ +#define ALC_ALL_DEVICES_SPECIFIER 0x1013 + + +/** Context management. */ +ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCint* attrlist); +ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context); +ALC_API void ALC_APIENTRY alcProcessContext(ALCcontext *context); +ALC_API void ALC_APIENTRY alcSuspendContext(ALCcontext *context); +ALC_API void ALC_APIENTRY alcDestroyContext(ALCcontext *context); +ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void); +ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice(ALCcontext *context); + +/** Device management. */ +ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *devicename); +ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device); + + +/** + * Error support. + * + * Obtain the most recent Device error. + */ +ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device); + +/** + * Extension support. + * + * Query for the presence of an extension, and obtain any appropriate + * function pointers and enum values. + */ +ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent(ALCdevice *device, const ALCchar *extname); +ALC_API void* ALC_APIENTRY alcGetProcAddress(ALCdevice *device, const ALCchar *funcname); +ALC_API ALCenum ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *enumname); + +/** Query function. */ +ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *device, ALCenum param); +ALC_API void ALC_APIENTRY alcGetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values); + +/** Capture function. */ +ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize); +ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device); +ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device); +ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device); +ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer, ALCsizei samples); + +/** Pointer-to-function type, useful for dynamically getting ALC entry points. */ +typedef ALCcontext* (ALC_APIENTRY *LPALCCREATECONTEXT)(ALCdevice *device, const ALCint *attrlist); +typedef ALCboolean (ALC_APIENTRY *LPALCMAKECONTEXTCURRENT)(ALCcontext *context); +typedef void (ALC_APIENTRY *LPALCPROCESSCONTEXT)(ALCcontext *context); +typedef void (ALC_APIENTRY *LPALCSUSPENDCONTEXT)(ALCcontext *context); +typedef void (ALC_APIENTRY *LPALCDESTROYCONTEXT)(ALCcontext *context); +typedef ALCcontext* (ALC_APIENTRY *LPALCGETCURRENTCONTEXT)(void); +typedef ALCdevice* (ALC_APIENTRY *LPALCGETCONTEXTSDEVICE)(ALCcontext *context); +typedef ALCdevice* (ALC_APIENTRY *LPALCOPENDEVICE)(const ALCchar *devicename); +typedef ALCboolean (ALC_APIENTRY *LPALCCLOSEDEVICE)(ALCdevice *device); +typedef ALCenum (ALC_APIENTRY *LPALCGETERROR)(ALCdevice *device); +typedef ALCboolean (ALC_APIENTRY *LPALCISEXTENSIONPRESENT)(ALCdevice *device, const ALCchar *extname); +typedef void* (ALC_APIENTRY *LPALCGETPROCADDRESS)(ALCdevice *device, const ALCchar *funcname); +typedef ALCenum (ALC_APIENTRY *LPALCGETENUMVALUE)(ALCdevice *device, const ALCchar *enumname); +typedef const ALCchar* (ALC_APIENTRY *LPALCGETSTRING)(ALCdevice *device, ALCenum param); +typedef void (ALC_APIENTRY *LPALCGETINTEGERV)(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values); +typedef ALCdevice* (ALC_APIENTRY *LPALCCAPTUREOPENDEVICE)(const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize); +typedef ALCboolean (ALC_APIENTRY *LPALCCAPTURECLOSEDEVICE)(ALCdevice *device); +typedef void (ALC_APIENTRY *LPALCCAPTURESTART)(ALCdevice *device); +typedef void (ALC_APIENTRY *LPALCCAPTURESTOP)(ALCdevice *device); +typedef void (ALC_APIENTRY *LPALCCAPTURESAMPLES)(ALCdevice *device, ALCvoid *buffer, ALCsizei samples); + +#if defined(__cplusplus) +} +#endif + +#endif /* AL_ALC_H */ diff --git a/external/openal_soft/include/AL/alext.h b/external/openal_soft/include/AL/alext.h new file mode 100644 index 000000000..0447f2bb4 --- /dev/null +++ b/external/openal_soft/include/AL/alext.h @@ -0,0 +1,355 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 2008 by authors. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + +#ifndef AL_ALEXT_H +#define AL_ALEXT_H + +#include +/* Define int64_t and uint64_t types */ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#include +#elif defined(_WIN32) && defined(__GNUC__) +#include +#elif defined(_WIN32) +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#else +/* Fallback if nothing above works */ +#include +#endif + +#include "alc.h" +#include "al.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef AL_LOKI_IMA_ADPCM_format +#define AL_LOKI_IMA_ADPCM_format 1 +#define AL_FORMAT_IMA_ADPCM_MONO16_EXT 0x10000 +#define AL_FORMAT_IMA_ADPCM_STEREO16_EXT 0x10001 +#endif + +#ifndef AL_LOKI_WAVE_format +#define AL_LOKI_WAVE_format 1 +#define AL_FORMAT_WAVE_EXT 0x10002 +#endif + +#ifndef AL_EXT_vorbis +#define AL_EXT_vorbis 1 +#define AL_FORMAT_VORBIS_EXT 0x10003 +#endif + +#ifndef AL_LOKI_quadriphonic +#define AL_LOKI_quadriphonic 1 +#define AL_FORMAT_QUAD8_LOKI 0x10004 +#define AL_FORMAT_QUAD16_LOKI 0x10005 +#endif + +#ifndef AL_EXT_float32 +#define AL_EXT_float32 1 +#define AL_FORMAT_MONO_FLOAT32 0x10010 +#define AL_FORMAT_STEREO_FLOAT32 0x10011 +#endif + +#ifndef AL_EXT_double +#define AL_EXT_double 1 +#define AL_FORMAT_MONO_DOUBLE_EXT 0x10012 +#define AL_FORMAT_STEREO_DOUBLE_EXT 0x10013 +#endif + +#ifndef AL_EXT_MULAW +#define AL_EXT_MULAW 1 +#define AL_FORMAT_MONO_MULAW_EXT 0x10014 +#define AL_FORMAT_STEREO_MULAW_EXT 0x10015 +#endif + +#ifndef AL_EXT_ALAW +#define AL_EXT_ALAW 1 +#define AL_FORMAT_MONO_ALAW_EXT 0x10016 +#define AL_FORMAT_STEREO_ALAW_EXT 0x10017 +#endif + +#ifndef ALC_LOKI_audio_channel +#define ALC_LOKI_audio_channel 1 +#define ALC_CHAN_MAIN_LOKI 0x500001 +#define ALC_CHAN_PCM_LOKI 0x500002 +#define ALC_CHAN_CD_LOKI 0x500003 +#endif + +#ifndef AL_EXT_MCFORMATS +#define AL_EXT_MCFORMATS 1 +#define AL_FORMAT_QUAD8 0x1204 +#define AL_FORMAT_QUAD16 0x1205 +#define AL_FORMAT_QUAD32 0x1206 +#define AL_FORMAT_REAR8 0x1207 +#define AL_FORMAT_REAR16 0x1208 +#define AL_FORMAT_REAR32 0x1209 +#define AL_FORMAT_51CHN8 0x120A +#define AL_FORMAT_51CHN16 0x120B +#define AL_FORMAT_51CHN32 0x120C +#define AL_FORMAT_61CHN8 0x120D +#define AL_FORMAT_61CHN16 0x120E +#define AL_FORMAT_61CHN32 0x120F +#define AL_FORMAT_71CHN8 0x1210 +#define AL_FORMAT_71CHN16 0x1211 +#define AL_FORMAT_71CHN32 0x1212 +#endif + +#ifndef AL_EXT_MULAW_MCFORMATS +#define AL_EXT_MULAW_MCFORMATS 1 +#define AL_FORMAT_MONO_MULAW 0x10014 +#define AL_FORMAT_STEREO_MULAW 0x10015 +#define AL_FORMAT_QUAD_MULAW 0x10021 +#define AL_FORMAT_REAR_MULAW 0x10022 +#define AL_FORMAT_51CHN_MULAW 0x10023 +#define AL_FORMAT_61CHN_MULAW 0x10024 +#define AL_FORMAT_71CHN_MULAW 0x10025 +#endif + +#ifndef AL_EXT_IMA4 +#define AL_EXT_IMA4 1 +#define AL_FORMAT_MONO_IMA4 0x1300 +#define AL_FORMAT_STEREO_IMA4 0x1301 +#endif + +#ifndef AL_EXT_STATIC_BUFFER +#define AL_EXT_STATIC_BUFFER 1 +typedef ALvoid (AL_APIENTRY*PFNALBUFFERDATASTATICPROC)(const ALint,ALenum,ALvoid*,ALsizei,ALsizei); +#ifdef AL_ALEXT_PROTOTYPES +AL_API ALvoid AL_APIENTRY alBufferDataStatic(const ALint buffer, ALenum format, ALvoid *data, ALsizei len, ALsizei freq); +#endif +#endif + +#ifndef ALC_EXT_EFX +#define ALC_EXT_EFX 1 +#include "efx.h" +#endif + +#ifndef ALC_EXT_disconnect +#define ALC_EXT_disconnect 1 +#define ALC_CONNECTED 0x313 +#endif + +#ifndef ALC_EXT_thread_local_context +#define ALC_EXT_thread_local_context 1 +typedef ALCboolean (ALC_APIENTRY*PFNALCSETTHREADCONTEXTPROC)(ALCcontext *context); +typedef ALCcontext* (ALC_APIENTRY*PFNALCGETTHREADCONTEXTPROC)(void); +#ifdef AL_ALEXT_PROTOTYPES +ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context); +ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void); +#endif +#endif + +#ifndef AL_EXT_source_distance_model +#define AL_EXT_source_distance_model 1 +#define AL_SOURCE_DISTANCE_MODEL 0x200 +#endif + +#ifndef AL_SOFT_buffer_sub_data +#define AL_SOFT_buffer_sub_data 1 +#define AL_BYTE_RW_OFFSETS_SOFT 0x1031 +#define AL_SAMPLE_RW_OFFSETS_SOFT 0x1032 +typedef ALvoid (AL_APIENTRY*PFNALBUFFERSUBDATASOFTPROC)(ALuint,ALenum,const ALvoid*,ALsizei,ALsizei); +#ifdef AL_ALEXT_PROTOTYPES +AL_API ALvoid AL_APIENTRY alBufferSubDataSOFT(ALuint buffer,ALenum format,const ALvoid *data,ALsizei offset,ALsizei length); +#endif +#endif + +#ifndef AL_SOFT_loop_points +#define AL_SOFT_loop_points 1 +#define AL_LOOP_POINTS_SOFT 0x2015 +#endif + +#ifndef AL_EXT_FOLDBACK +#define AL_EXT_FOLDBACK 1 +#define AL_EXT_FOLDBACK_NAME "AL_EXT_FOLDBACK" +#define AL_FOLDBACK_EVENT_BLOCK 0x4112 +#define AL_FOLDBACK_EVENT_START 0x4111 +#define AL_FOLDBACK_EVENT_STOP 0x4113 +#define AL_FOLDBACK_MODE_MONO 0x4101 +#define AL_FOLDBACK_MODE_STEREO 0x4102 +typedef void (AL_APIENTRY*LPALFOLDBACKCALLBACK)(ALenum,ALsizei); +typedef void (AL_APIENTRY*LPALREQUESTFOLDBACKSTART)(ALenum,ALsizei,ALsizei,ALfloat*,LPALFOLDBACKCALLBACK); +typedef void (AL_APIENTRY*LPALREQUESTFOLDBACKSTOP)(void); +#ifdef AL_ALEXT_PROTOTYPES +AL_API void AL_APIENTRY alRequestFoldbackStart(ALenum mode,ALsizei count,ALsizei length,ALfloat *mem,LPALFOLDBACKCALLBACK callback); +AL_API void AL_APIENTRY alRequestFoldbackStop(void); +#endif +#endif + +#ifndef ALC_EXT_DEDICATED +#define ALC_EXT_DEDICATED 1 +#define AL_DEDICATED_GAIN 0x0001 +#define AL_EFFECT_DEDICATED_DIALOGUE 0x9001 +#define AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT 0x9000 +#endif + +#ifndef AL_SOFT_buffer_samples +#define AL_SOFT_buffer_samples 1 +/* Channel configurations */ +#define AL_MONO_SOFT 0x1500 +#define AL_STEREO_SOFT 0x1501 +#define AL_REAR_SOFT 0x1502 +#define AL_QUAD_SOFT 0x1503 +#define AL_5POINT1_SOFT 0x1504 +#define AL_6POINT1_SOFT 0x1505 +#define AL_7POINT1_SOFT 0x1506 + +/* Sample types */ +#define AL_BYTE_SOFT 0x1400 +#define AL_UNSIGNED_BYTE_SOFT 0x1401 +#define AL_SHORT_SOFT 0x1402 +#define AL_UNSIGNED_SHORT_SOFT 0x1403 +#define AL_INT_SOFT 0x1404 +#define AL_UNSIGNED_INT_SOFT 0x1405 +#define AL_FLOAT_SOFT 0x1406 +#define AL_DOUBLE_SOFT 0x1407 +#define AL_BYTE3_SOFT 0x1408 +#define AL_UNSIGNED_BYTE3_SOFT 0x1409 + +/* Storage formats */ +#define AL_MONO8_SOFT 0x1100 +#define AL_MONO16_SOFT 0x1101 +#define AL_MONO32F_SOFT 0x10010 +#define AL_STEREO8_SOFT 0x1102 +#define AL_STEREO16_SOFT 0x1103 +#define AL_STEREO32F_SOFT 0x10011 +#define AL_QUAD8_SOFT 0x1204 +#define AL_QUAD16_SOFT 0x1205 +#define AL_QUAD32F_SOFT 0x1206 +#define AL_REAR8_SOFT 0x1207 +#define AL_REAR16_SOFT 0x1208 +#define AL_REAR32F_SOFT 0x1209 +#define AL_5POINT1_8_SOFT 0x120A +#define AL_5POINT1_16_SOFT 0x120B +#define AL_5POINT1_32F_SOFT 0x120C +#define AL_6POINT1_8_SOFT 0x120D +#define AL_6POINT1_16_SOFT 0x120E +#define AL_6POINT1_32F_SOFT 0x120F +#define AL_7POINT1_8_SOFT 0x1210 +#define AL_7POINT1_16_SOFT 0x1211 +#define AL_7POINT1_32F_SOFT 0x1212 + +/* Buffer attributes */ +#define AL_INTERNAL_FORMAT_SOFT 0x2008 +#define AL_BYTE_LENGTH_SOFT 0x2009 +#define AL_SAMPLE_LENGTH_SOFT 0x200A +#define AL_SEC_LENGTH_SOFT 0x200B + +typedef void (AL_APIENTRY*LPALBUFFERSAMPLESSOFT)(ALuint,ALuint,ALenum,ALsizei,ALenum,ALenum,const ALvoid*); +typedef void (AL_APIENTRY*LPALBUFFERSUBSAMPLESSOFT)(ALuint,ALsizei,ALsizei,ALenum,ALenum,const ALvoid*); +typedef void (AL_APIENTRY*LPALGETBUFFERSAMPLESSOFT)(ALuint,ALsizei,ALsizei,ALenum,ALenum,ALvoid*); +typedef ALboolean (AL_APIENTRY*LPALISBUFFERFORMATSUPPORTEDSOFT)(ALenum); +#ifdef AL_ALEXT_PROTOTYPES +AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint buffer, ALuint samplerate, ALenum internalformat, ALsizei samples, ALenum channels, ALenum type, const ALvoid *data); +AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint buffer, ALsizei offset, ALsizei samples, ALenum channels, ALenum type, const ALvoid *data); +AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint buffer, ALsizei offset, ALsizei samples, ALenum channels, ALenum type, ALvoid *data); +AL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum format); +#endif +#endif + +#ifndef AL_SOFT_direct_channels +#define AL_SOFT_direct_channels 1 +#define AL_DIRECT_CHANNELS_SOFT 0x1033 +#endif + +#ifndef ALC_SOFT_loopback +#define ALC_SOFT_loopback 1 +#define ALC_FORMAT_CHANNELS_SOFT 0x1990 +#define ALC_FORMAT_TYPE_SOFT 0x1991 + +/* Sample types */ +#define ALC_BYTE_SOFT 0x1400 +#define ALC_UNSIGNED_BYTE_SOFT 0x1401 +#define ALC_SHORT_SOFT 0x1402 +#define ALC_UNSIGNED_SHORT_SOFT 0x1403 +#define ALC_INT_SOFT 0x1404 +#define ALC_UNSIGNED_INT_SOFT 0x1405 +#define ALC_FLOAT_SOFT 0x1406 + +/* Channel configurations */ +#define ALC_MONO_SOFT 0x1500 +#define ALC_STEREO_SOFT 0x1501 +#define ALC_QUAD_SOFT 0x1503 +#define ALC_5POINT1_SOFT 0x1504 +#define ALC_6POINT1_SOFT 0x1505 +#define ALC_7POINT1_SOFT 0x1506 + +typedef ALCdevice* (ALC_APIENTRY*LPALCLOOPBACKOPENDEVICESOFT)(const ALCchar*); +typedef ALCboolean (ALC_APIENTRY*LPALCISRENDERFORMATSUPPORTEDSOFT)(ALCdevice*,ALCsizei,ALCenum,ALCenum); +typedef void (ALC_APIENTRY*LPALCRENDERSAMPLESSOFT)(ALCdevice*,ALCvoid*,ALCsizei); +#ifdef AL_ALEXT_PROTOTYPES +ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceName); +ALC_API ALCboolean ALC_APIENTRY alcIsRenderFormatSupportedSOFT(ALCdevice *device, ALCsizei freq, ALCenum channels, ALCenum type); +ALC_API void ALC_APIENTRY alcRenderSamplesSOFT(ALCdevice *device, ALCvoid *buffer, ALCsizei samples); +#endif +#endif + +#ifndef AL_EXT_STEREO_ANGLES +#define AL_EXT_STEREO_ANGLES 1 +#define AL_STEREO_ANGLES 0x1030 +#endif + +#ifndef AL_EXT_SOURCE_RADIUS +#define AL_EXT_SOURCE_RADIUS 1 +#define AL_SOURCE_RADIUS 0x1031 +#endif + +#ifndef AL_SOFT_source_latency +#define AL_SOFT_source_latency 1 +#define AL_SAMPLE_OFFSET_LATENCY_SOFT 0x1200 +#define AL_SEC_OFFSET_LATENCY_SOFT 0x1201 +typedef int64_t ALint64SOFT; +typedef uint64_t ALuint64SOFT; +typedef void (AL_APIENTRY*LPALSOURCEDSOFT)(ALuint,ALenum,ALdouble); +typedef void (AL_APIENTRY*LPALSOURCE3DSOFT)(ALuint,ALenum,ALdouble,ALdouble,ALdouble); +typedef void (AL_APIENTRY*LPALSOURCEDVSOFT)(ALuint,ALenum,const ALdouble*); +typedef void (AL_APIENTRY*LPALGETSOURCEDSOFT)(ALuint,ALenum,ALdouble*); +typedef void (AL_APIENTRY*LPALGETSOURCE3DSOFT)(ALuint,ALenum,ALdouble*,ALdouble*,ALdouble*); +typedef void (AL_APIENTRY*LPALGETSOURCEDVSOFT)(ALuint,ALenum,ALdouble*); +typedef void (AL_APIENTRY*LPALSOURCEI64SOFT)(ALuint,ALenum,ALint64SOFT); +typedef void (AL_APIENTRY*LPALSOURCE3I64SOFT)(ALuint,ALenum,ALint64SOFT,ALint64SOFT,ALint64SOFT); +typedef void (AL_APIENTRY*LPALSOURCEI64VSOFT)(ALuint,ALenum,const ALint64SOFT*); +typedef void (AL_APIENTRY*LPALGETSOURCEI64SOFT)(ALuint,ALenum,ALint64SOFT*); +typedef void (AL_APIENTRY*LPALGETSOURCE3I64SOFT)(ALuint,ALenum,ALint64SOFT*,ALint64SOFT*,ALint64SOFT*); +typedef void (AL_APIENTRY*LPALGETSOURCEI64VSOFT)(ALuint,ALenum,ALint64SOFT*); +#ifdef AL_ALEXT_PROTOTYPES +AL_API void AL_APIENTRY alSourcedSOFT(ALuint source, ALenum param, ALdouble value); +AL_API void AL_APIENTRY alSource3dSOFT(ALuint source, ALenum param, ALdouble value1, ALdouble value2, ALdouble value3); +AL_API void AL_APIENTRY alSourcedvSOFT(ALuint source, ALenum param, const ALdouble *values); +AL_API void AL_APIENTRY alGetSourcedSOFT(ALuint source, ALenum param, ALdouble *value); +AL_API void AL_APIENTRY alGetSource3dSOFT(ALuint source, ALenum param, ALdouble *value1, ALdouble *value2, ALdouble *value3); +AL_API void AL_APIENTRY alGetSourcedvSOFT(ALuint source, ALenum param, ALdouble *values); +AL_API void AL_APIENTRY alSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT value); +AL_API void AL_APIENTRY alSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT value1, ALint64SOFT value2, ALint64SOFT value3); +AL_API void AL_APIENTRY alSourcei64vSOFT(ALuint source, ALenum param, const ALint64SOFT *values); +AL_API void AL_APIENTRY alGetSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT *value); +AL_API void AL_APIENTRY alGetSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT *value1, ALint64SOFT *value2, ALint64SOFT *value3); +AL_API void AL_APIENTRY alGetSourcei64vSOFT(ALuint source, ALenum param, ALint64SOFT *values); +#endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/external/openal_soft/include/AL/efx-creative.h b/external/openal_soft/include/AL/efx-creative.h new file mode 100644 index 000000000..0a04c982e --- /dev/null +++ b/external/openal_soft/include/AL/efx-creative.h @@ -0,0 +1,3 @@ +/* The tokens that would be defined here are already defined in efx.h. This + * empty file is here to provide compatibility with Windows-based projects + * that would include it. */ diff --git a/external/openal_soft/include/AL/efx-presets.h b/external/openal_soft/include/AL/efx-presets.h new file mode 100644 index 000000000..86dcbda2f --- /dev/null +++ b/external/openal_soft/include/AL/efx-presets.h @@ -0,0 +1,402 @@ +/* Reverb presets for EFX */ + +#ifndef EFX_PRESETS_H +#define EFX_PRESETS_H + +#ifndef EFXEAXREVERBPROPERTIES_DEFINED +#define EFXEAXREVERBPROPERTIES_DEFINED +typedef struct { + float flDensity; + float flDiffusion; + float flGain; + float flGainHF; + float flGainLF; + float flDecayTime; + float flDecayHFRatio; + float flDecayLFRatio; + float flReflectionsGain; + float flReflectionsDelay; + float flReflectionsPan[3]; + float flLateReverbGain; + float flLateReverbDelay; + float flLateReverbPan[3]; + float flEchoTime; + float flEchoDepth; + float flModulationTime; + float flModulationDepth; + float flAirAbsorptionGainHF; + float flHFReference; + float flLFReference; + float flRoomRolloffFactor; + int iDecayHFLimit; +} EFXEAXREVERBPROPERTIES, *LPEFXEAXREVERBPROPERTIES; +#endif + +/* Default Presets */ + +#define EFX_REVERB_PRESET_GENERIC \ + { 1.0000f, 1.0000f, 0.3162f, 0.8913f, 1.0000f, 1.4900f, 0.8300f, 1.0000f, 0.0500f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_PADDEDCELL \ + { 0.1715f, 1.0000f, 0.3162f, 0.0010f, 1.0000f, 0.1700f, 0.1000f, 1.0000f, 0.2500f, 0.0010f, { 0.0000f, 0.0000f, 0.0000f }, 1.2691f, 0.0020f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_ROOM \ + { 0.4287f, 1.0000f, 0.3162f, 0.5929f, 1.0000f, 0.4000f, 0.8300f, 1.0000f, 0.1503f, 0.0020f, { 0.0000f, 0.0000f, 0.0000f }, 1.0629f, 0.0030f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_BATHROOM \ + { 0.1715f, 1.0000f, 0.3162f, 0.2512f, 1.0000f, 1.4900f, 0.5400f, 1.0000f, 0.6531f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 3.2734f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_LIVINGROOM \ + { 0.9766f, 1.0000f, 0.3162f, 0.0010f, 1.0000f, 0.5000f, 0.1000f, 1.0000f, 0.2051f, 0.0030f, { 0.0000f, 0.0000f, 0.0000f }, 0.2805f, 0.0040f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_STONEROOM \ + { 1.0000f, 1.0000f, 0.3162f, 0.7079f, 1.0000f, 2.3100f, 0.6400f, 1.0000f, 0.4411f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.1003f, 0.0170f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_AUDITORIUM \ + { 1.0000f, 1.0000f, 0.3162f, 0.5781f, 1.0000f, 4.3200f, 0.5900f, 1.0000f, 0.4032f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.7170f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_CONCERTHALL \ + { 1.0000f, 1.0000f, 0.3162f, 0.5623f, 1.0000f, 3.9200f, 0.7000f, 1.0000f, 0.2427f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.9977f, 0.0290f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_CAVE \ + { 1.0000f, 1.0000f, 0.3162f, 1.0000f, 1.0000f, 2.9100f, 1.3000f, 1.0000f, 0.5000f, 0.0150f, { 0.0000f, 0.0000f, 0.0000f }, 0.7063f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_ARENA \ + { 1.0000f, 1.0000f, 0.3162f, 0.4477f, 1.0000f, 7.2400f, 0.3300f, 1.0000f, 0.2612f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.0186f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_HANGAR \ + { 1.0000f, 1.0000f, 0.3162f, 0.3162f, 1.0000f, 10.0500f, 0.2300f, 1.0000f, 0.5000f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.2560f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_CARPETEDHALLWAY \ + { 0.4287f, 1.0000f, 0.3162f, 0.0100f, 1.0000f, 0.3000f, 0.1000f, 1.0000f, 0.1215f, 0.0020f, { 0.0000f, 0.0000f, 0.0000f }, 0.1531f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_HALLWAY \ + { 0.3645f, 1.0000f, 0.3162f, 0.7079f, 1.0000f, 1.4900f, 0.5900f, 1.0000f, 0.2458f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.6615f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_STONECORRIDOR \ + { 1.0000f, 1.0000f, 0.3162f, 0.7612f, 1.0000f, 2.7000f, 0.7900f, 1.0000f, 0.2472f, 0.0130f, { 0.0000f, 0.0000f, 0.0000f }, 1.5758f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_ALLEY \ + { 1.0000f, 0.3000f, 0.3162f, 0.7328f, 1.0000f, 1.4900f, 0.8600f, 1.0000f, 0.2500f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 0.9954f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.1250f, 0.9500f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_FOREST \ + { 1.0000f, 0.3000f, 0.3162f, 0.0224f, 1.0000f, 1.4900f, 0.5400f, 1.0000f, 0.0525f, 0.1620f, { 0.0000f, 0.0000f, 0.0000f }, 0.7682f, 0.0880f, { 0.0000f, 0.0000f, 0.0000f }, 0.1250f, 1.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_CITY \ + { 1.0000f, 0.5000f, 0.3162f, 0.3981f, 1.0000f, 1.4900f, 0.6700f, 1.0000f, 0.0730f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 0.1427f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_MOUNTAINS \ + { 1.0000f, 0.2700f, 0.3162f, 0.0562f, 1.0000f, 1.4900f, 0.2100f, 1.0000f, 0.0407f, 0.3000f, { 0.0000f, 0.0000f, 0.0000f }, 0.1919f, 0.1000f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_QUARRY \ + { 1.0000f, 1.0000f, 0.3162f, 0.3162f, 1.0000f, 1.4900f, 0.8300f, 1.0000f, 0.0000f, 0.0610f, { 0.0000f, 0.0000f, 0.0000f }, 1.7783f, 0.0250f, { 0.0000f, 0.0000f, 0.0000f }, 0.1250f, 0.7000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_PLAIN \ + { 1.0000f, 0.2100f, 0.3162f, 0.1000f, 1.0000f, 1.4900f, 0.5000f, 1.0000f, 0.0585f, 0.1790f, { 0.0000f, 0.0000f, 0.0000f }, 0.1089f, 0.1000f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_PARKINGLOT \ + { 1.0000f, 1.0000f, 0.3162f, 1.0000f, 1.0000f, 1.6500f, 1.5000f, 1.0000f, 0.2082f, 0.0080f, { 0.0000f, 0.0000f, 0.0000f }, 0.2652f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_SEWERPIPE \ + { 0.3071f, 0.8000f, 0.3162f, 0.3162f, 1.0000f, 2.8100f, 0.1400f, 1.0000f, 1.6387f, 0.0140f, { 0.0000f, 0.0000f, 0.0000f }, 3.2471f, 0.0210f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_UNDERWATER \ + { 0.3645f, 1.0000f, 0.3162f, 0.0100f, 1.0000f, 1.4900f, 0.1000f, 1.0000f, 0.5963f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 7.0795f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 1.1800f, 0.3480f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_DRUGGED \ + { 0.4287f, 0.5000f, 0.3162f, 1.0000f, 1.0000f, 8.3900f, 1.3900f, 1.0000f, 0.8760f, 0.0020f, { 0.0000f, 0.0000f, 0.0000f }, 3.1081f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 1.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_DIZZY \ + { 0.3645f, 0.6000f, 0.3162f, 0.6310f, 1.0000f, 17.2300f, 0.5600f, 1.0000f, 0.1392f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.4937f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.8100f, 0.3100f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_PSYCHOTIC \ + { 0.0625f, 0.5000f, 0.3162f, 0.8404f, 1.0000f, 7.5600f, 0.9100f, 1.0000f, 0.4864f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 2.4378f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 4.0000f, 1.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } + +/* Castle Presets */ + +#define EFX_REVERB_PRESET_CASTLE_SMALLROOM \ + { 1.0000f, 0.8900f, 0.3162f, 0.3981f, 0.1000f, 1.2200f, 0.8300f, 0.3100f, 0.8913f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 1.9953f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.1380f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_CASTLE_SHORTPASSAGE \ + { 1.0000f, 0.8900f, 0.3162f, 0.3162f, 0.1000f, 2.3200f, 0.8300f, 0.3100f, 0.8913f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0230f, { 0.0000f, 0.0000f, 0.0000f }, 0.1380f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_CASTLE_MEDIUMROOM \ + { 1.0000f, 0.9300f, 0.3162f, 0.2818f, 0.1000f, 2.0400f, 0.8300f, 0.4600f, 0.6310f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 1.5849f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.1550f, 0.0300f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_CASTLE_LARGEROOM \ + { 1.0000f, 0.8200f, 0.3162f, 0.2818f, 0.1259f, 2.5300f, 0.8300f, 0.5000f, 0.4467f, 0.0340f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0160f, { 0.0000f, 0.0000f, 0.0000f }, 0.1850f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_CASTLE_LONGPASSAGE \ + { 1.0000f, 0.8900f, 0.3162f, 0.3981f, 0.1000f, 3.4200f, 0.8300f, 0.3100f, 0.8913f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0230f, { 0.0000f, 0.0000f, 0.0000f }, 0.1380f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_CASTLE_HALL \ + { 1.0000f, 0.8100f, 0.3162f, 0.2818f, 0.1778f, 3.1400f, 0.7900f, 0.6200f, 0.1778f, 0.0560f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0240f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_CASTLE_CUPBOARD \ + { 1.0000f, 0.8900f, 0.3162f, 0.2818f, 0.1000f, 0.6700f, 0.8700f, 0.3100f, 1.4125f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 3.5481f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 0.1380f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_CASTLE_COURTYARD \ + { 1.0000f, 0.4200f, 0.3162f, 0.4467f, 0.1995f, 2.1300f, 0.6100f, 0.2300f, 0.2239f, 0.1600f, { 0.0000f, 0.0000f, 0.0000f }, 0.7079f, 0.0360f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.3700f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_CASTLE_ALCOVE \ + { 1.0000f, 0.8900f, 0.3162f, 0.5012f, 0.1000f, 1.6400f, 0.8700f, 0.3100f, 1.0000f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0340f, { 0.0000f, 0.0000f, 0.0000f }, 0.1380f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 5168.6001f, 139.5000f, 0.0000f, 0x1 } + +/* Factory Presets */ + +#define EFX_REVERB_PRESET_FACTORY_SMALLROOM \ + { 0.3645f, 0.8200f, 0.3162f, 0.7943f, 0.5012f, 1.7200f, 0.6500f, 1.3100f, 0.7079f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.7783f, 0.0240f, { 0.0000f, 0.0000f, 0.0000f }, 0.1190f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_FACTORY_SHORTPASSAGE \ + { 0.3645f, 0.6400f, 0.2512f, 0.7943f, 0.5012f, 2.5300f, 0.6500f, 1.3100f, 1.0000f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0380f, { 0.0000f, 0.0000f, 0.0000f }, 0.1350f, 0.2300f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_FACTORY_MEDIUMROOM \ + { 0.4287f, 0.8200f, 0.2512f, 0.7943f, 0.5012f, 2.7600f, 0.6500f, 1.3100f, 0.2818f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0230f, { 0.0000f, 0.0000f, 0.0000f }, 0.1740f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_FACTORY_LARGEROOM \ + { 0.4287f, 0.7500f, 0.2512f, 0.7079f, 0.6310f, 4.2400f, 0.5100f, 1.3100f, 0.1778f, 0.0390f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0230f, { 0.0000f, 0.0000f, 0.0000f }, 0.2310f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_FACTORY_LONGPASSAGE \ + { 0.3645f, 0.6400f, 0.2512f, 0.7943f, 0.5012f, 4.0600f, 0.6500f, 1.3100f, 1.0000f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0370f, { 0.0000f, 0.0000f, 0.0000f }, 0.1350f, 0.2300f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_FACTORY_HALL \ + { 0.4287f, 0.7500f, 0.3162f, 0.7079f, 0.6310f, 7.4300f, 0.5100f, 1.3100f, 0.0631f, 0.0730f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0270f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_FACTORY_CUPBOARD \ + { 0.3071f, 0.6300f, 0.2512f, 0.7943f, 0.5012f, 0.4900f, 0.6500f, 1.3100f, 1.2589f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.9953f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 0.1070f, 0.0700f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_FACTORY_COURTYARD \ + { 0.3071f, 0.5700f, 0.3162f, 0.3162f, 0.6310f, 2.3200f, 0.2900f, 0.5600f, 0.2239f, 0.1400f, { 0.0000f, 0.0000f, 0.0000f }, 0.3981f, 0.0390f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2900f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_FACTORY_ALCOVE \ + { 0.3645f, 0.5900f, 0.2512f, 0.7943f, 0.5012f, 3.1400f, 0.6500f, 1.3100f, 1.4125f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.0000f, 0.0380f, { 0.0000f, 0.0000f, 0.0000f }, 0.1140f, 0.1000f, 0.2500f, 0.0000f, 0.9943f, 3762.6001f, 362.5000f, 0.0000f, 0x1 } + +/* Ice Palace Presets */ + +#define EFX_REVERB_PRESET_ICEPALACE_SMALLROOM \ + { 1.0000f, 0.8400f, 0.3162f, 0.5623f, 0.2818f, 1.5100f, 1.5300f, 0.2700f, 0.8913f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.1640f, 0.1400f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_ICEPALACE_SHORTPASSAGE \ + { 1.0000f, 0.7500f, 0.3162f, 0.5623f, 0.2818f, 1.7900f, 1.4600f, 0.2800f, 0.5012f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0190f, { 0.0000f, 0.0000f, 0.0000f }, 0.1770f, 0.0900f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_ICEPALACE_MEDIUMROOM \ + { 1.0000f, 0.8700f, 0.3162f, 0.5623f, 0.4467f, 2.2200f, 1.5300f, 0.3200f, 0.3981f, 0.0390f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0270f, { 0.0000f, 0.0000f, 0.0000f }, 0.1860f, 0.1200f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_ICEPALACE_LARGEROOM \ + { 1.0000f, 0.8100f, 0.3162f, 0.5623f, 0.4467f, 3.1400f, 1.5300f, 0.3200f, 0.2512f, 0.0390f, { 0.0000f, 0.0000f, 0.0000f }, 1.0000f, 0.0270f, { 0.0000f, 0.0000f, 0.0000f }, 0.2140f, 0.1100f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_ICEPALACE_LONGPASSAGE \ + { 1.0000f, 0.7700f, 0.3162f, 0.5623f, 0.3981f, 3.0100f, 1.4600f, 0.2800f, 0.7943f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0250f, { 0.0000f, 0.0000f, 0.0000f }, 0.1860f, 0.0400f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_ICEPALACE_HALL \ + { 1.0000f, 0.7600f, 0.3162f, 0.4467f, 0.5623f, 5.4900f, 1.5300f, 0.3800f, 0.1122f, 0.0540f, { 0.0000f, 0.0000f, 0.0000f }, 0.6310f, 0.0520f, { 0.0000f, 0.0000f, 0.0000f }, 0.2260f, 0.1100f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_ICEPALACE_CUPBOARD \ + { 1.0000f, 0.8300f, 0.3162f, 0.5012f, 0.2239f, 0.7600f, 1.5300f, 0.2600f, 1.1220f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.9953f, 0.0160f, { 0.0000f, 0.0000f, 0.0000f }, 0.1430f, 0.0800f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_ICEPALACE_COURTYARD \ + { 1.0000f, 0.5900f, 0.3162f, 0.2818f, 0.3162f, 2.0400f, 1.2000f, 0.3800f, 0.3162f, 0.1730f, { 0.0000f, 0.0000f, 0.0000f }, 0.3162f, 0.0430f, { 0.0000f, 0.0000f, 0.0000f }, 0.2350f, 0.4800f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_ICEPALACE_ALCOVE \ + { 1.0000f, 0.8400f, 0.3162f, 0.5623f, 0.2818f, 2.7600f, 1.4600f, 0.2800f, 1.1220f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.1610f, 0.0900f, 0.2500f, 0.0000f, 0.9943f, 12428.5000f, 99.6000f, 0.0000f, 0x1 } + +/* Space Station Presets */ + +#define EFX_REVERB_PRESET_SPACESTATION_SMALLROOM \ + { 0.2109f, 0.7000f, 0.3162f, 0.7079f, 0.8913f, 1.7200f, 0.8200f, 0.5500f, 0.7943f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0130f, { 0.0000f, 0.0000f, 0.0000f }, 0.1880f, 0.2600f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_SPACESTATION_SHORTPASSAGE \ + { 0.2109f, 0.8700f, 0.3162f, 0.6310f, 0.8913f, 3.5700f, 0.5000f, 0.5500f, 1.0000f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0160f, { 0.0000f, 0.0000f, 0.0000f }, 0.1720f, 0.2000f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_SPACESTATION_MEDIUMROOM \ + { 0.2109f, 0.7500f, 0.3162f, 0.6310f, 0.8913f, 3.0100f, 0.5000f, 0.5500f, 0.3981f, 0.0340f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0350f, { 0.0000f, 0.0000f, 0.0000f }, 0.2090f, 0.3100f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_SPACESTATION_LARGEROOM \ + { 0.3645f, 0.8100f, 0.3162f, 0.6310f, 0.8913f, 3.8900f, 0.3800f, 0.6100f, 0.3162f, 0.0560f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0350f, { 0.0000f, 0.0000f, 0.0000f }, 0.2330f, 0.2800f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_SPACESTATION_LONGPASSAGE \ + { 0.4287f, 0.8200f, 0.3162f, 0.6310f, 0.8913f, 4.6200f, 0.6200f, 0.5500f, 1.0000f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0310f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2300f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_SPACESTATION_HALL \ + { 0.4287f, 0.8700f, 0.3162f, 0.6310f, 0.8913f, 7.1100f, 0.3800f, 0.6100f, 0.1778f, 0.1000f, { 0.0000f, 0.0000f, 0.0000f }, 0.6310f, 0.0470f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2500f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_SPACESTATION_CUPBOARD \ + { 0.1715f, 0.5600f, 0.3162f, 0.7079f, 0.8913f, 0.7900f, 0.8100f, 0.5500f, 1.4125f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.7783f, 0.0180f, { 0.0000f, 0.0000f, 0.0000f }, 0.1810f, 0.3100f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_SPACESTATION_ALCOVE \ + { 0.2109f, 0.7800f, 0.3162f, 0.7079f, 0.8913f, 1.1600f, 0.8100f, 0.5500f, 1.4125f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 1.0000f, 0.0180f, { 0.0000f, 0.0000f, 0.0000f }, 0.1920f, 0.2100f, 0.2500f, 0.0000f, 0.9943f, 3316.1001f, 458.2000f, 0.0000f, 0x1 } + +/* Wooden Galleon Presets */ + +#define EFX_REVERB_PRESET_WOODEN_SMALLROOM \ + { 1.0000f, 1.0000f, 0.3162f, 0.1122f, 0.3162f, 0.7900f, 0.3200f, 0.8700f, 1.0000f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0290f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_WOODEN_SHORTPASSAGE \ + { 1.0000f, 1.0000f, 0.3162f, 0.1259f, 0.3162f, 1.7500f, 0.5000f, 0.8700f, 0.8913f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 0.6310f, 0.0240f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_WOODEN_MEDIUMROOM \ + { 1.0000f, 1.0000f, 0.3162f, 0.1000f, 0.2818f, 1.4700f, 0.4200f, 0.8200f, 0.8913f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0290f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_WOODEN_LARGEROOM \ + { 1.0000f, 1.0000f, 0.3162f, 0.0891f, 0.2818f, 2.6500f, 0.3300f, 0.8200f, 0.8913f, 0.0660f, { 0.0000f, 0.0000f, 0.0000f }, 0.7943f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_WOODEN_LONGPASSAGE \ + { 1.0000f, 1.0000f, 0.3162f, 0.1000f, 0.3162f, 1.9900f, 0.4000f, 0.7900f, 1.0000f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.4467f, 0.0360f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_WOODEN_HALL \ + { 1.0000f, 1.0000f, 0.3162f, 0.0794f, 0.2818f, 3.4500f, 0.3000f, 0.8200f, 0.8913f, 0.0880f, { 0.0000f, 0.0000f, 0.0000f }, 0.7943f, 0.0630f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_WOODEN_CUPBOARD \ + { 1.0000f, 1.0000f, 0.3162f, 0.1413f, 0.3162f, 0.5600f, 0.4600f, 0.9100f, 1.1220f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0280f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_WOODEN_COURTYARD \ + { 1.0000f, 0.6500f, 0.3162f, 0.0794f, 0.3162f, 1.7900f, 0.3500f, 0.7900f, 0.5623f, 0.1230f, { 0.0000f, 0.0000f, 0.0000f }, 0.1000f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_WOODEN_ALCOVE \ + { 1.0000f, 1.0000f, 0.3162f, 0.1259f, 0.3162f, 1.2200f, 0.6200f, 0.9100f, 1.1220f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 0.7079f, 0.0240f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 4705.0000f, 99.6000f, 0.0000f, 0x1 } + +/* Sports Presets */ + +#define EFX_REVERB_PRESET_SPORT_EMPTYSTADIUM \ + { 1.0000f, 1.0000f, 0.3162f, 0.4467f, 0.7943f, 6.2600f, 0.5100f, 1.1000f, 0.0631f, 0.1830f, { 0.0000f, 0.0000f, 0.0000f }, 0.3981f, 0.0380f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_SPORT_SQUASHCOURT \ + { 1.0000f, 0.7500f, 0.3162f, 0.3162f, 0.7943f, 2.2200f, 0.9100f, 1.1600f, 0.4467f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 0.7943f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.1260f, 0.1900f, 0.2500f, 0.0000f, 0.9943f, 7176.8999f, 211.2000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_SPORT_SMALLSWIMMINGPOOL \ + { 1.0000f, 0.7000f, 0.3162f, 0.7943f, 0.8913f, 2.7600f, 1.2500f, 1.1400f, 0.6310f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.7943f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.1790f, 0.1500f, 0.8950f, 0.1900f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_SPORT_LARGESWIMMINGPOOL \ + { 1.0000f, 0.8200f, 0.3162f, 0.7943f, 1.0000f, 5.4900f, 1.3100f, 1.1400f, 0.4467f, 0.0390f, { 0.0000f, 0.0000f, 0.0000f }, 0.5012f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.2220f, 0.5500f, 1.1590f, 0.2100f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_SPORT_GYMNASIUM \ + { 1.0000f, 0.8100f, 0.3162f, 0.4467f, 0.8913f, 3.1400f, 1.0600f, 1.3500f, 0.3981f, 0.0290f, { 0.0000f, 0.0000f, 0.0000f }, 0.5623f, 0.0450f, { 0.0000f, 0.0000f, 0.0000f }, 0.1460f, 0.1400f, 0.2500f, 0.0000f, 0.9943f, 7176.8999f, 211.2000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_SPORT_FULLSTADIUM \ + { 1.0000f, 1.0000f, 0.3162f, 0.0708f, 0.7943f, 5.2500f, 0.1700f, 0.8000f, 0.1000f, 0.1880f, { 0.0000f, 0.0000f, 0.0000f }, 0.2818f, 0.0380f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_SPORT_STADIUMTANNOY \ + { 1.0000f, 0.7800f, 0.3162f, 0.5623f, 0.5012f, 2.5300f, 0.8800f, 0.6800f, 0.2818f, 0.2300f, { 0.0000f, 0.0000f, 0.0000f }, 0.5012f, 0.0630f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +/* Prefab Presets */ + +#define EFX_REVERB_PRESET_PREFAB_WORKSHOP \ + { 0.4287f, 1.0000f, 0.3162f, 0.1413f, 0.3981f, 0.7600f, 1.0000f, 1.0000f, 1.0000f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_PREFAB_SCHOOLROOM \ + { 0.4022f, 0.6900f, 0.3162f, 0.6310f, 0.5012f, 0.9800f, 0.4500f, 0.1800f, 1.4125f, 0.0170f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0150f, { 0.0000f, 0.0000f, 0.0000f }, 0.0950f, 0.1400f, 0.2500f, 0.0000f, 0.9943f, 7176.8999f, 211.2000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_PREFAB_PRACTISEROOM \ + { 0.4022f, 0.8700f, 0.3162f, 0.3981f, 0.5012f, 1.1200f, 0.5600f, 0.1800f, 1.2589f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0110f, { 0.0000f, 0.0000f, 0.0000f }, 0.0950f, 0.1400f, 0.2500f, 0.0000f, 0.9943f, 7176.8999f, 211.2000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_PREFAB_OUTHOUSE \ + { 1.0000f, 0.8200f, 0.3162f, 0.1122f, 0.1585f, 1.3800f, 0.3800f, 0.3500f, 0.8913f, 0.0240f, { 0.0000f, 0.0000f, -0.0000f }, 0.6310f, 0.0440f, { 0.0000f, 0.0000f, 0.0000f }, 0.1210f, 0.1700f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 107.5000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_PREFAB_CARAVAN \ + { 1.0000f, 1.0000f, 0.3162f, 0.0891f, 0.1259f, 0.4300f, 1.5000f, 1.0000f, 1.0000f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 1.9953f, 0.0120f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } + +/* Dome and Pipe Presets */ + +#define EFX_REVERB_PRESET_DOME_TOMB \ + { 1.0000f, 0.7900f, 0.3162f, 0.3548f, 0.2239f, 4.1800f, 0.2100f, 0.1000f, 0.3868f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 1.6788f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 0.1770f, 0.1900f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_PIPE_SMALL \ + { 1.0000f, 1.0000f, 0.3162f, 0.3548f, 0.2239f, 5.0400f, 0.1000f, 0.1000f, 0.5012f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 2.5119f, 0.0150f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_DOME_SAINTPAULS \ + { 1.0000f, 0.8700f, 0.3162f, 0.3548f, 0.2239f, 10.4800f, 0.1900f, 0.1000f, 0.1778f, 0.0900f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0420f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.1200f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_PIPE_LONGTHIN \ + { 0.2560f, 0.9100f, 0.3162f, 0.4467f, 0.2818f, 9.2100f, 0.1800f, 0.1000f, 0.7079f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 0.7079f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_PIPE_LARGE \ + { 1.0000f, 1.0000f, 0.3162f, 0.3548f, 0.2239f, 8.4500f, 0.1000f, 0.1000f, 0.3981f, 0.0460f, { 0.0000f, 0.0000f, 0.0000f }, 1.5849f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_PIPE_RESONANT \ + { 0.1373f, 0.9100f, 0.3162f, 0.4467f, 0.2818f, 6.8100f, 0.1800f, 0.1000f, 0.7079f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.0000f, 0.0220f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 20.0000f, 0.0000f, 0x0 } + +/* Outdoors Presets */ + +#define EFX_REVERB_PRESET_OUTDOORS_BACKYARD \ + { 1.0000f, 0.4500f, 0.3162f, 0.2512f, 0.5012f, 1.1200f, 0.3400f, 0.4600f, 0.4467f, 0.0690f, { 0.0000f, 0.0000f, -0.0000f }, 0.7079f, 0.0230f, { 0.0000f, 0.0000f, 0.0000f }, 0.2180f, 0.3400f, 0.2500f, 0.0000f, 0.9943f, 4399.1001f, 242.9000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_OUTDOORS_ROLLINGPLAINS \ + { 1.0000f, 0.0000f, 0.3162f, 0.0112f, 0.6310f, 2.1300f, 0.2100f, 0.4600f, 0.1778f, 0.3000f, { 0.0000f, 0.0000f, -0.0000f }, 0.4467f, 0.0190f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9943f, 4399.1001f, 242.9000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_OUTDOORS_DEEPCANYON \ + { 1.0000f, 0.7400f, 0.3162f, 0.1778f, 0.6310f, 3.8900f, 0.2100f, 0.4600f, 0.3162f, 0.2230f, { 0.0000f, 0.0000f, -0.0000f }, 0.3548f, 0.0190f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9943f, 4399.1001f, 242.9000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_OUTDOORS_CREEK \ + { 1.0000f, 0.3500f, 0.3162f, 0.1778f, 0.5012f, 2.1300f, 0.2100f, 0.4600f, 0.3981f, 0.1150f, { 0.0000f, 0.0000f, -0.0000f }, 0.1995f, 0.0310f, { 0.0000f, 0.0000f, 0.0000f }, 0.2180f, 0.3400f, 0.2500f, 0.0000f, 0.9943f, 4399.1001f, 242.9000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_OUTDOORS_VALLEY \ + { 1.0000f, 0.2800f, 0.3162f, 0.0282f, 0.1585f, 2.8800f, 0.2600f, 0.3500f, 0.1413f, 0.2630f, { 0.0000f, 0.0000f, -0.0000f }, 0.3981f, 0.1000f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.3400f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 107.5000f, 0.0000f, 0x0 } + +/* Mood Presets */ + +#define EFX_REVERB_PRESET_MOOD_HEAVEN \ + { 1.0000f, 0.9400f, 0.3162f, 0.7943f, 0.4467f, 5.0400f, 1.1200f, 0.5600f, 0.2427f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0290f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0800f, 2.7420f, 0.0500f, 0.9977f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_MOOD_HELL \ + { 1.0000f, 0.5700f, 0.3162f, 0.3548f, 0.4467f, 3.5700f, 0.4900f, 2.0000f, 0.0000f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.1100f, 0.0400f, 2.1090f, 0.5200f, 0.9943f, 5000.0000f, 139.5000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_MOOD_MEMORY \ + { 1.0000f, 0.8500f, 0.3162f, 0.6310f, 0.3548f, 4.0600f, 0.8200f, 0.5600f, 0.0398f, 0.0000f, { 0.0000f, 0.0000f, 0.0000f }, 1.1220f, 0.0000f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.4740f, 0.4500f, 0.9886f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } + +/* Driving Presets */ + +#define EFX_REVERB_PRESET_DRIVING_COMMENTATOR \ + { 1.0000f, 0.0000f, 3.1623f, 0.5623f, 0.5012f, 2.4200f, 0.8800f, 0.6800f, 0.1995f, 0.0930f, { 0.0000f, 0.0000f, 0.0000f }, 0.2512f, 0.0170f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9886f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_DRIVING_PITGARAGE \ + { 0.4287f, 0.5900f, 0.3162f, 0.7079f, 0.5623f, 1.7200f, 0.9300f, 0.8700f, 0.5623f, 0.0000f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0160f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.1100f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_DRIVING_INCAR_RACER \ + { 0.0832f, 0.8000f, 0.3162f, 1.0000f, 0.7943f, 0.1700f, 2.0000f, 0.4100f, 1.7783f, 0.0070f, { 0.0000f, 0.0000f, 0.0000f }, 0.7079f, 0.0150f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 10268.2002f, 251.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_DRIVING_INCAR_SPORTS \ + { 0.0832f, 0.8000f, 0.3162f, 0.6310f, 1.0000f, 0.1700f, 0.7500f, 0.4100f, 1.0000f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 0.5623f, 0.0000f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 10268.2002f, 251.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_DRIVING_INCAR_LUXURY \ + { 0.2560f, 1.0000f, 0.3162f, 0.1000f, 0.5012f, 0.1300f, 0.4100f, 0.4600f, 0.7943f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 1.5849f, 0.0100f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 10268.2002f, 251.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_DRIVING_FULLGRANDSTAND \ + { 1.0000f, 1.0000f, 0.3162f, 0.2818f, 0.6310f, 3.0100f, 1.3700f, 1.2800f, 0.3548f, 0.0900f, { 0.0000f, 0.0000f, 0.0000f }, 0.1778f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 10420.2002f, 250.0000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_DRIVING_EMPTYGRANDSTAND \ + { 1.0000f, 1.0000f, 0.3162f, 1.0000f, 0.7943f, 4.6200f, 1.7500f, 1.4000f, 0.2082f, 0.0900f, { 0.0000f, 0.0000f, 0.0000f }, 0.2512f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.0000f, 0.9943f, 10420.2002f, 250.0000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_DRIVING_TUNNEL \ + { 1.0000f, 0.8100f, 0.3162f, 0.3981f, 0.8913f, 3.4200f, 0.9400f, 1.3100f, 0.7079f, 0.0510f, { 0.0000f, 0.0000f, 0.0000f }, 0.7079f, 0.0470f, { 0.0000f, 0.0000f, 0.0000f }, 0.2140f, 0.0500f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 155.3000f, 0.0000f, 0x1 } + +/* City Presets */ + +#define EFX_REVERB_PRESET_CITY_STREETS \ + { 1.0000f, 0.7800f, 0.3162f, 0.7079f, 0.8913f, 1.7900f, 1.1200f, 0.9100f, 0.2818f, 0.0460f, { 0.0000f, 0.0000f, 0.0000f }, 0.1995f, 0.0280f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2000f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_CITY_SUBWAY \ + { 1.0000f, 0.7400f, 0.3162f, 0.7079f, 0.8913f, 3.0100f, 1.2300f, 0.9100f, 0.7079f, 0.0460f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0280f, { 0.0000f, 0.0000f, 0.0000f }, 0.1250f, 0.2100f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_CITY_MUSEUM \ + { 1.0000f, 0.8200f, 0.3162f, 0.1778f, 0.1778f, 3.2800f, 1.4000f, 0.5700f, 0.2512f, 0.0390f, { 0.0000f, 0.0000f, -0.0000f }, 0.8913f, 0.0340f, { 0.0000f, 0.0000f, 0.0000f }, 0.1300f, 0.1700f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 107.5000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_CITY_LIBRARY \ + { 1.0000f, 0.8200f, 0.3162f, 0.2818f, 0.0891f, 2.7600f, 0.8900f, 0.4100f, 0.3548f, 0.0290f, { 0.0000f, 0.0000f, -0.0000f }, 0.8913f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 0.1300f, 0.1700f, 0.2500f, 0.0000f, 0.9943f, 2854.3999f, 107.5000f, 0.0000f, 0x0 } + +#define EFX_REVERB_PRESET_CITY_UNDERPASS \ + { 1.0000f, 0.8200f, 0.3162f, 0.4467f, 0.8913f, 3.5700f, 1.1200f, 0.9100f, 0.3981f, 0.0590f, { 0.0000f, 0.0000f, 0.0000f }, 0.8913f, 0.0370f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.1400f, 0.2500f, 0.0000f, 0.9920f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_CITY_ABANDONED \ + { 1.0000f, 0.6900f, 0.3162f, 0.7943f, 0.8913f, 3.2800f, 1.1700f, 0.9100f, 0.4467f, 0.0440f, { 0.0000f, 0.0000f, 0.0000f }, 0.2818f, 0.0240f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.2000f, 0.2500f, 0.0000f, 0.9966f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +/* Misc. Presets */ + +#define EFX_REVERB_PRESET_DUSTYROOM \ + { 0.3645f, 0.5600f, 0.3162f, 0.7943f, 0.7079f, 1.7900f, 0.3800f, 0.2100f, 0.5012f, 0.0020f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0060f, { 0.0000f, 0.0000f, 0.0000f }, 0.2020f, 0.0500f, 0.2500f, 0.0000f, 0.9886f, 13046.0000f, 163.3000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_CHAPEL \ + { 1.0000f, 0.8400f, 0.3162f, 0.5623f, 1.0000f, 4.6200f, 0.6400f, 1.2300f, 0.4467f, 0.0320f, { 0.0000f, 0.0000f, 0.0000f }, 0.7943f, 0.0490f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.0000f, 0.2500f, 0.1100f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x1 } + +#define EFX_REVERB_PRESET_SMALLWATERROOM \ + { 1.0000f, 0.7000f, 0.3162f, 0.4477f, 1.0000f, 1.5100f, 1.2500f, 1.1400f, 0.8913f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.1790f, 0.1500f, 0.8950f, 0.1900f, 0.9920f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } + +#endif /* EFX_PRESETS_H */ diff --git a/external/openal_soft/include/AL/efx.h b/external/openal_soft/include/AL/efx.h new file mode 100644 index 000000000..57766983f --- /dev/null +++ b/external/openal_soft/include/AL/efx.h @@ -0,0 +1,761 @@ +#ifndef AL_EFX_H +#define AL_EFX_H + + +#include "alc.h" +#include "al.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ALC_EXT_EFX_NAME "ALC_EXT_EFX" + +#define ALC_EFX_MAJOR_VERSION 0x20001 +#define ALC_EFX_MINOR_VERSION 0x20002 +#define ALC_MAX_AUXILIARY_SENDS 0x20003 + + +/* Listener properties. */ +#define AL_METERS_PER_UNIT 0x20004 + +/* Source properties. */ +#define AL_DIRECT_FILTER 0x20005 +#define AL_AUXILIARY_SEND_FILTER 0x20006 +#define AL_AIR_ABSORPTION_FACTOR 0x20007 +#define AL_ROOM_ROLLOFF_FACTOR 0x20008 +#define AL_CONE_OUTER_GAINHF 0x20009 +#define AL_DIRECT_FILTER_GAINHF_AUTO 0x2000A +#define AL_AUXILIARY_SEND_FILTER_GAIN_AUTO 0x2000B +#define AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO 0x2000C + + +/* Effect properties. */ + +/* Reverb effect parameters */ +#define AL_REVERB_DENSITY 0x0001 +#define AL_REVERB_DIFFUSION 0x0002 +#define AL_REVERB_GAIN 0x0003 +#define AL_REVERB_GAINHF 0x0004 +#define AL_REVERB_DECAY_TIME 0x0005 +#define AL_REVERB_DECAY_HFRATIO 0x0006 +#define AL_REVERB_REFLECTIONS_GAIN 0x0007 +#define AL_REVERB_REFLECTIONS_DELAY 0x0008 +#define AL_REVERB_LATE_REVERB_GAIN 0x0009 +#define AL_REVERB_LATE_REVERB_DELAY 0x000A +#define AL_REVERB_AIR_ABSORPTION_GAINHF 0x000B +#define AL_REVERB_ROOM_ROLLOFF_FACTOR 0x000C +#define AL_REVERB_DECAY_HFLIMIT 0x000D + +/* EAX Reverb effect parameters */ +#define AL_EAXREVERB_DENSITY 0x0001 +#define AL_EAXREVERB_DIFFUSION 0x0002 +#define AL_EAXREVERB_GAIN 0x0003 +#define AL_EAXREVERB_GAINHF 0x0004 +#define AL_EAXREVERB_GAINLF 0x0005 +#define AL_EAXREVERB_DECAY_TIME 0x0006 +#define AL_EAXREVERB_DECAY_HFRATIO 0x0007 +#define AL_EAXREVERB_DECAY_LFRATIO 0x0008 +#define AL_EAXREVERB_REFLECTIONS_GAIN 0x0009 +#define AL_EAXREVERB_REFLECTIONS_DELAY 0x000A +#define AL_EAXREVERB_REFLECTIONS_PAN 0x000B +#define AL_EAXREVERB_LATE_REVERB_GAIN 0x000C +#define AL_EAXREVERB_LATE_REVERB_DELAY 0x000D +#define AL_EAXREVERB_LATE_REVERB_PAN 0x000E +#define AL_EAXREVERB_ECHO_TIME 0x000F +#define AL_EAXREVERB_ECHO_DEPTH 0x0010 +#define AL_EAXREVERB_MODULATION_TIME 0x0011 +#define AL_EAXREVERB_MODULATION_DEPTH 0x0012 +#define AL_EAXREVERB_AIR_ABSORPTION_GAINHF 0x0013 +#define AL_EAXREVERB_HFREFERENCE 0x0014 +#define AL_EAXREVERB_LFREFERENCE 0x0015 +#define AL_EAXREVERB_ROOM_ROLLOFF_FACTOR 0x0016 +#define AL_EAXREVERB_DECAY_HFLIMIT 0x0017 + +/* Chorus effect parameters */ +#define AL_CHORUS_WAVEFORM 0x0001 +#define AL_CHORUS_PHASE 0x0002 +#define AL_CHORUS_RATE 0x0003 +#define AL_CHORUS_DEPTH 0x0004 +#define AL_CHORUS_FEEDBACK 0x0005 +#define AL_CHORUS_DELAY 0x0006 + +/* Distortion effect parameters */ +#define AL_DISTORTION_EDGE 0x0001 +#define AL_DISTORTION_GAIN 0x0002 +#define AL_DISTORTION_LOWPASS_CUTOFF 0x0003 +#define AL_DISTORTION_EQCENTER 0x0004 +#define AL_DISTORTION_EQBANDWIDTH 0x0005 + +/* Echo effect parameters */ +#define AL_ECHO_DELAY 0x0001 +#define AL_ECHO_LRDELAY 0x0002 +#define AL_ECHO_DAMPING 0x0003 +#define AL_ECHO_FEEDBACK 0x0004 +#define AL_ECHO_SPREAD 0x0005 + +/* Flanger effect parameters */ +#define AL_FLANGER_WAVEFORM 0x0001 +#define AL_FLANGER_PHASE 0x0002 +#define AL_FLANGER_RATE 0x0003 +#define AL_FLANGER_DEPTH 0x0004 +#define AL_FLANGER_FEEDBACK 0x0005 +#define AL_FLANGER_DELAY 0x0006 + +/* Frequency shifter effect parameters */ +#define AL_FREQUENCY_SHIFTER_FREQUENCY 0x0001 +#define AL_FREQUENCY_SHIFTER_LEFT_DIRECTION 0x0002 +#define AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION 0x0003 + +/* Vocal morpher effect parameters */ +#define AL_VOCAL_MORPHER_PHONEMEA 0x0001 +#define AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING 0x0002 +#define AL_VOCAL_MORPHER_PHONEMEB 0x0003 +#define AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING 0x0004 +#define AL_VOCAL_MORPHER_WAVEFORM 0x0005 +#define AL_VOCAL_MORPHER_RATE 0x0006 + +/* Pitchshifter effect parameters */ +#define AL_PITCH_SHIFTER_COARSE_TUNE 0x0001 +#define AL_PITCH_SHIFTER_FINE_TUNE 0x0002 + +/* Ringmodulator effect parameters */ +#define AL_RING_MODULATOR_FREQUENCY 0x0001 +#define AL_RING_MODULATOR_HIGHPASS_CUTOFF 0x0002 +#define AL_RING_MODULATOR_WAVEFORM 0x0003 + +/* Autowah effect parameters */ +#define AL_AUTOWAH_ATTACK_TIME 0x0001 +#define AL_AUTOWAH_RELEASE_TIME 0x0002 +#define AL_AUTOWAH_RESONANCE 0x0003 +#define AL_AUTOWAH_PEAK_GAIN 0x0004 + +/* Compressor effect parameters */ +#define AL_COMPRESSOR_ONOFF 0x0001 + +/* Equalizer effect parameters */ +#define AL_EQUALIZER_LOW_GAIN 0x0001 +#define AL_EQUALIZER_LOW_CUTOFF 0x0002 +#define AL_EQUALIZER_MID1_GAIN 0x0003 +#define AL_EQUALIZER_MID1_CENTER 0x0004 +#define AL_EQUALIZER_MID1_WIDTH 0x0005 +#define AL_EQUALIZER_MID2_GAIN 0x0006 +#define AL_EQUALIZER_MID2_CENTER 0x0007 +#define AL_EQUALIZER_MID2_WIDTH 0x0008 +#define AL_EQUALIZER_HIGH_GAIN 0x0009 +#define AL_EQUALIZER_HIGH_CUTOFF 0x000A + +/* Effect type */ +#define AL_EFFECT_FIRST_PARAMETER 0x0000 +#define AL_EFFECT_LAST_PARAMETER 0x8000 +#define AL_EFFECT_TYPE 0x8001 + +/* Effect types, used with the AL_EFFECT_TYPE property */ +#define AL_EFFECT_NULL 0x0000 +#define AL_EFFECT_REVERB 0x0001 +#define AL_EFFECT_CHORUS 0x0002 +#define AL_EFFECT_DISTORTION 0x0003 +#define AL_EFFECT_ECHO 0x0004 +#define AL_EFFECT_FLANGER 0x0005 +#define AL_EFFECT_FREQUENCY_SHIFTER 0x0006 +#define AL_EFFECT_VOCAL_MORPHER 0x0007 +#define AL_EFFECT_PITCH_SHIFTER 0x0008 +#define AL_EFFECT_RING_MODULATOR 0x0009 +#define AL_EFFECT_AUTOWAH 0x000A +#define AL_EFFECT_COMPRESSOR 0x000B +#define AL_EFFECT_EQUALIZER 0x000C +#define AL_EFFECT_EAXREVERB 0x8000 + +/* Auxiliary Effect Slot properties. */ +#define AL_EFFECTSLOT_EFFECT 0x0001 +#define AL_EFFECTSLOT_GAIN 0x0002 +#define AL_EFFECTSLOT_AUXILIARY_SEND_AUTO 0x0003 + +/* NULL Auxiliary Slot ID to disable a source send. */ +#define AL_EFFECTSLOT_NULL 0x0000 + + +/* Filter properties. */ + +/* Lowpass filter parameters */ +#define AL_LOWPASS_GAIN 0x0001 +#define AL_LOWPASS_GAINHF 0x0002 + +/* Highpass filter parameters */ +#define AL_HIGHPASS_GAIN 0x0001 +#define AL_HIGHPASS_GAINLF 0x0002 + +/* Bandpass filter parameters */ +#define AL_BANDPASS_GAIN 0x0001 +#define AL_BANDPASS_GAINLF 0x0002 +#define AL_BANDPASS_GAINHF 0x0003 + +/* Filter type */ +#define AL_FILTER_FIRST_PARAMETER 0x0000 +#define AL_FILTER_LAST_PARAMETER 0x8000 +#define AL_FILTER_TYPE 0x8001 + +/* Filter types, used with the AL_FILTER_TYPE property */ +#define AL_FILTER_NULL 0x0000 +#define AL_FILTER_LOWPASS 0x0001 +#define AL_FILTER_HIGHPASS 0x0002 +#define AL_FILTER_BANDPASS 0x0003 + + +/* Effect object function types. */ +typedef void (AL_APIENTRY *LPALGENEFFECTS)(ALsizei, ALuint*); +typedef void (AL_APIENTRY *LPALDELETEEFFECTS)(ALsizei, const ALuint*); +typedef ALboolean (AL_APIENTRY *LPALISEFFECT)(ALuint); +typedef void (AL_APIENTRY *LPALEFFECTI)(ALuint, ALenum, ALint); +typedef void (AL_APIENTRY *LPALEFFECTIV)(ALuint, ALenum, const ALint*); +typedef void (AL_APIENTRY *LPALEFFECTF)(ALuint, ALenum, ALfloat); +typedef void (AL_APIENTRY *LPALEFFECTFV)(ALuint, ALenum, const ALfloat*); +typedef void (AL_APIENTRY *LPALGETEFFECTI)(ALuint, ALenum, ALint*); +typedef void (AL_APIENTRY *LPALGETEFFECTIV)(ALuint, ALenum, ALint*); +typedef void (AL_APIENTRY *LPALGETEFFECTF)(ALuint, ALenum, ALfloat*); +typedef void (AL_APIENTRY *LPALGETEFFECTFV)(ALuint, ALenum, ALfloat*); + +/* Filter object function types. */ +typedef void (AL_APIENTRY *LPALGENFILTERS)(ALsizei, ALuint*); +typedef void (AL_APIENTRY *LPALDELETEFILTERS)(ALsizei, const ALuint*); +typedef ALboolean (AL_APIENTRY *LPALISFILTER)(ALuint); +typedef void (AL_APIENTRY *LPALFILTERI)(ALuint, ALenum, ALint); +typedef void (AL_APIENTRY *LPALFILTERIV)(ALuint, ALenum, const ALint*); +typedef void (AL_APIENTRY *LPALFILTERF)(ALuint, ALenum, ALfloat); +typedef void (AL_APIENTRY *LPALFILTERFV)(ALuint, ALenum, const ALfloat*); +typedef void (AL_APIENTRY *LPALGETFILTERI)(ALuint, ALenum, ALint*); +typedef void (AL_APIENTRY *LPALGETFILTERIV)(ALuint, ALenum, ALint*); +typedef void (AL_APIENTRY *LPALGETFILTERF)(ALuint, ALenum, ALfloat*); +typedef void (AL_APIENTRY *LPALGETFILTERFV)(ALuint, ALenum, ALfloat*); + +/* Auxiliary Effect Slot object function types. */ +typedef void (AL_APIENTRY *LPALGENAUXILIARYEFFECTSLOTS)(ALsizei, ALuint*); +typedef void (AL_APIENTRY *LPALDELETEAUXILIARYEFFECTSLOTS)(ALsizei, const ALuint*); +typedef ALboolean (AL_APIENTRY *LPALISAUXILIARYEFFECTSLOT)(ALuint); +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTI)(ALuint, ALenum, ALint); +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTIV)(ALuint, ALenum, const ALint*); +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTF)(ALuint, ALenum, ALfloat); +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTFV)(ALuint, ALenum, const ALfloat*); +typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTI)(ALuint, ALenum, ALint*); +typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTIV)(ALuint, ALenum, ALint*); +typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTF)(ALuint, ALenum, ALfloat*); +typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTFV)(ALuint, ALenum, ALfloat*); + +#ifdef AL_ALEXT_PROTOTYPES +AL_API ALvoid AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects); +AL_API ALvoid AL_APIENTRY alDeleteEffects(ALsizei n, const ALuint *effects); +AL_API ALboolean AL_APIENTRY alIsEffect(ALuint effect); +AL_API ALvoid AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint iValue); +AL_API ALvoid AL_APIENTRY alEffectiv(ALuint effect, ALenum param, const ALint *piValues); +AL_API ALvoid AL_APIENTRY alEffectf(ALuint effect, ALenum param, ALfloat flValue); +AL_API ALvoid AL_APIENTRY alEffectfv(ALuint effect, ALenum param, const ALfloat *pflValues); +AL_API ALvoid AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *piValue); +AL_API ALvoid AL_APIENTRY alGetEffectiv(ALuint effect, ALenum param, ALint *piValues); +AL_API ALvoid AL_APIENTRY alGetEffectf(ALuint effect, ALenum param, ALfloat *pflValue); +AL_API ALvoid AL_APIENTRY alGetEffectfv(ALuint effect, ALenum param, ALfloat *pflValues); + +AL_API ALvoid AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters); +AL_API ALvoid AL_APIENTRY alDeleteFilters(ALsizei n, const ALuint *filters); +AL_API ALboolean AL_APIENTRY alIsFilter(ALuint filter); +AL_API ALvoid AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint iValue); +AL_API ALvoid AL_APIENTRY alFilteriv(ALuint filter, ALenum param, const ALint *piValues); +AL_API ALvoid AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat flValue); +AL_API ALvoid AL_APIENTRY alFilterfv(ALuint filter, ALenum param, const ALfloat *pflValues); +AL_API ALvoid AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *piValue); +AL_API ALvoid AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *piValues); +AL_API ALvoid AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *pflValue); +AL_API ALvoid AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *pflValues); + +AL_API ALvoid AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots); +AL_API ALvoid AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, const ALuint *effectslots); +AL_API ALboolean AL_APIENTRY alIsAuxiliaryEffectSlot(ALuint effectslot); +AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint iValue); +AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, const ALint *piValues); +AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat flValue); +AL_API ALvoid AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, const ALfloat *pflValues); +AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint *piValue); +AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *piValues); +AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat *pflValue); +AL_API ALvoid AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *pflValues); +#endif + +/* Filter ranges and defaults. */ + +/* Lowpass filter */ +#define AL_LOWPASS_MIN_GAIN (0.0f) +#define AL_LOWPASS_MAX_GAIN (1.0f) +#define AL_LOWPASS_DEFAULT_GAIN (1.0f) + +#define AL_LOWPASS_MIN_GAINHF (0.0f) +#define AL_LOWPASS_MAX_GAINHF (1.0f) +#define AL_LOWPASS_DEFAULT_GAINHF (1.0f) + +/* Highpass filter */ +#define AL_HIGHPASS_MIN_GAIN (0.0f) +#define AL_HIGHPASS_MAX_GAIN (1.0f) +#define AL_HIGHPASS_DEFAULT_GAIN (1.0f) + +#define AL_HIGHPASS_MIN_GAINLF (0.0f) +#define AL_HIGHPASS_MAX_GAINLF (1.0f) +#define AL_HIGHPASS_DEFAULT_GAINLF (1.0f) + +/* Bandpass filter */ +#define AL_BANDPASS_MIN_GAIN (0.0f) +#define AL_BANDPASS_MAX_GAIN (1.0f) +#define AL_BANDPASS_DEFAULT_GAIN (1.0f) + +#define AL_BANDPASS_MIN_GAINHF (0.0f) +#define AL_BANDPASS_MAX_GAINHF (1.0f) +#define AL_BANDPASS_DEFAULT_GAINHF (1.0f) + +#define AL_BANDPASS_MIN_GAINLF (0.0f) +#define AL_BANDPASS_MAX_GAINLF (1.0f) +#define AL_BANDPASS_DEFAULT_GAINLF (1.0f) + + +/* Effect parameter ranges and defaults. */ + +/* Standard reverb effect */ +#define AL_REVERB_MIN_DENSITY (0.0f) +#define AL_REVERB_MAX_DENSITY (1.0f) +#define AL_REVERB_DEFAULT_DENSITY (1.0f) + +#define AL_REVERB_MIN_DIFFUSION (0.0f) +#define AL_REVERB_MAX_DIFFUSION (1.0f) +#define AL_REVERB_DEFAULT_DIFFUSION (1.0f) + +#define AL_REVERB_MIN_GAIN (0.0f) +#define AL_REVERB_MAX_GAIN (1.0f) +#define AL_REVERB_DEFAULT_GAIN (0.32f) + +#define AL_REVERB_MIN_GAINHF (0.0f) +#define AL_REVERB_MAX_GAINHF (1.0f) +#define AL_REVERB_DEFAULT_GAINHF (0.89f) + +#define AL_REVERB_MIN_DECAY_TIME (0.1f) +#define AL_REVERB_MAX_DECAY_TIME (20.0f) +#define AL_REVERB_DEFAULT_DECAY_TIME (1.49f) + +#define AL_REVERB_MIN_DECAY_HFRATIO (0.1f) +#define AL_REVERB_MAX_DECAY_HFRATIO (2.0f) +#define AL_REVERB_DEFAULT_DECAY_HFRATIO (0.83f) + +#define AL_REVERB_MIN_REFLECTIONS_GAIN (0.0f) +#define AL_REVERB_MAX_REFLECTIONS_GAIN (3.16f) +#define AL_REVERB_DEFAULT_REFLECTIONS_GAIN (0.05f) + +#define AL_REVERB_MIN_REFLECTIONS_DELAY (0.0f) +#define AL_REVERB_MAX_REFLECTIONS_DELAY (0.3f) +#define AL_REVERB_DEFAULT_REFLECTIONS_DELAY (0.007f) + +#define AL_REVERB_MIN_LATE_REVERB_GAIN (0.0f) +#define AL_REVERB_MAX_LATE_REVERB_GAIN (10.0f) +#define AL_REVERB_DEFAULT_LATE_REVERB_GAIN (1.26f) + +#define AL_REVERB_MIN_LATE_REVERB_DELAY (0.0f) +#define AL_REVERB_MAX_LATE_REVERB_DELAY (0.1f) +#define AL_REVERB_DEFAULT_LATE_REVERB_DELAY (0.011f) + +#define AL_REVERB_MIN_AIR_ABSORPTION_GAINHF (0.892f) +#define AL_REVERB_MAX_AIR_ABSORPTION_GAINHF (1.0f) +#define AL_REVERB_DEFAULT_AIR_ABSORPTION_GAINHF (0.994f) + +#define AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR (0.0f) +#define AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR (10.0f) +#define AL_REVERB_DEFAULT_ROOM_ROLLOFF_FACTOR (0.0f) + +#define AL_REVERB_MIN_DECAY_HFLIMIT AL_FALSE +#define AL_REVERB_MAX_DECAY_HFLIMIT AL_TRUE +#define AL_REVERB_DEFAULT_DECAY_HFLIMIT AL_TRUE + +/* EAX reverb effect */ +#define AL_EAXREVERB_MIN_DENSITY (0.0f) +#define AL_EAXREVERB_MAX_DENSITY (1.0f) +#define AL_EAXREVERB_DEFAULT_DENSITY (1.0f) + +#define AL_EAXREVERB_MIN_DIFFUSION (0.0f) +#define AL_EAXREVERB_MAX_DIFFUSION (1.0f) +#define AL_EAXREVERB_DEFAULT_DIFFUSION (1.0f) + +#define AL_EAXREVERB_MIN_GAIN (0.0f) +#define AL_EAXREVERB_MAX_GAIN (1.0f) +#define AL_EAXREVERB_DEFAULT_GAIN (0.32f) + +#define AL_EAXREVERB_MIN_GAINHF (0.0f) +#define AL_EAXREVERB_MAX_GAINHF (1.0f) +#define AL_EAXREVERB_DEFAULT_GAINHF (0.89f) + +#define AL_EAXREVERB_MIN_GAINLF (0.0f) +#define AL_EAXREVERB_MAX_GAINLF (1.0f) +#define AL_EAXREVERB_DEFAULT_GAINLF (1.0f) + +#define AL_EAXREVERB_MIN_DECAY_TIME (0.1f) +#define AL_EAXREVERB_MAX_DECAY_TIME (20.0f) +#define AL_EAXREVERB_DEFAULT_DECAY_TIME (1.49f) + +#define AL_EAXREVERB_MIN_DECAY_HFRATIO (0.1f) +#define AL_EAXREVERB_MAX_DECAY_HFRATIO (2.0f) +#define AL_EAXREVERB_DEFAULT_DECAY_HFRATIO (0.83f) + +#define AL_EAXREVERB_MIN_DECAY_LFRATIO (0.1f) +#define AL_EAXREVERB_MAX_DECAY_LFRATIO (2.0f) +#define AL_EAXREVERB_DEFAULT_DECAY_LFRATIO (1.0f) + +#define AL_EAXREVERB_MIN_REFLECTIONS_GAIN (0.0f) +#define AL_EAXREVERB_MAX_REFLECTIONS_GAIN (3.16f) +#define AL_EAXREVERB_DEFAULT_REFLECTIONS_GAIN (0.05f) + +#define AL_EAXREVERB_MIN_REFLECTIONS_DELAY (0.0f) +#define AL_EAXREVERB_MAX_REFLECTIONS_DELAY (0.3f) +#define AL_EAXREVERB_DEFAULT_REFLECTIONS_DELAY (0.007f) + +#define AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN_XYZ (0.0f) + +#define AL_EAXREVERB_MIN_LATE_REVERB_GAIN (0.0f) +#define AL_EAXREVERB_MAX_LATE_REVERB_GAIN (10.0f) +#define AL_EAXREVERB_DEFAULT_LATE_REVERB_GAIN (1.26f) + +#define AL_EAXREVERB_MIN_LATE_REVERB_DELAY (0.0f) +#define AL_EAXREVERB_MAX_LATE_REVERB_DELAY (0.1f) +#define AL_EAXREVERB_DEFAULT_LATE_REVERB_DELAY (0.011f) + +#define AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN_XYZ (0.0f) + +#define AL_EAXREVERB_MIN_ECHO_TIME (0.075f) +#define AL_EAXREVERB_MAX_ECHO_TIME (0.25f) +#define AL_EAXREVERB_DEFAULT_ECHO_TIME (0.25f) + +#define AL_EAXREVERB_MIN_ECHO_DEPTH (0.0f) +#define AL_EAXREVERB_MAX_ECHO_DEPTH (1.0f) +#define AL_EAXREVERB_DEFAULT_ECHO_DEPTH (0.0f) + +#define AL_EAXREVERB_MIN_MODULATION_TIME (0.04f) +#define AL_EAXREVERB_MAX_MODULATION_TIME (4.0f) +#define AL_EAXREVERB_DEFAULT_MODULATION_TIME (0.25f) + +#define AL_EAXREVERB_MIN_MODULATION_DEPTH (0.0f) +#define AL_EAXREVERB_MAX_MODULATION_DEPTH (1.0f) +#define AL_EAXREVERB_DEFAULT_MODULATION_DEPTH (0.0f) + +#define AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF (0.892f) +#define AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF (1.0f) +#define AL_EAXREVERB_DEFAULT_AIR_ABSORPTION_GAINHF (0.994f) + +#define AL_EAXREVERB_MIN_HFREFERENCE (1000.0f) +#define AL_EAXREVERB_MAX_HFREFERENCE (20000.0f) +#define AL_EAXREVERB_DEFAULT_HFREFERENCE (5000.0f) + +#define AL_EAXREVERB_MIN_LFREFERENCE (20.0f) +#define AL_EAXREVERB_MAX_LFREFERENCE (1000.0f) +#define AL_EAXREVERB_DEFAULT_LFREFERENCE (250.0f) + +#define AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR (0.0f) +#define AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR (10.0f) +#define AL_EAXREVERB_DEFAULT_ROOM_ROLLOFF_FACTOR (0.0f) + +#define AL_EAXREVERB_MIN_DECAY_HFLIMIT AL_FALSE +#define AL_EAXREVERB_MAX_DECAY_HFLIMIT AL_TRUE +#define AL_EAXREVERB_DEFAULT_DECAY_HFLIMIT AL_TRUE + +/* Chorus effect */ +#define AL_CHORUS_WAVEFORM_SINUSOID (0) +#define AL_CHORUS_WAVEFORM_TRIANGLE (1) + +#define AL_CHORUS_MIN_WAVEFORM (0) +#define AL_CHORUS_MAX_WAVEFORM (1) +#define AL_CHORUS_DEFAULT_WAVEFORM (1) + +#define AL_CHORUS_MIN_PHASE (-180) +#define AL_CHORUS_MAX_PHASE (180) +#define AL_CHORUS_DEFAULT_PHASE (90) + +#define AL_CHORUS_MIN_RATE (0.0f) +#define AL_CHORUS_MAX_RATE (10.0f) +#define AL_CHORUS_DEFAULT_RATE (1.1f) + +#define AL_CHORUS_MIN_DEPTH (0.0f) +#define AL_CHORUS_MAX_DEPTH (1.0f) +#define AL_CHORUS_DEFAULT_DEPTH (0.1f) + +#define AL_CHORUS_MIN_FEEDBACK (-1.0f) +#define AL_CHORUS_MAX_FEEDBACK (1.0f) +#define AL_CHORUS_DEFAULT_FEEDBACK (0.25f) + +#define AL_CHORUS_MIN_DELAY (0.0f) +#define AL_CHORUS_MAX_DELAY (0.016f) +#define AL_CHORUS_DEFAULT_DELAY (0.016f) + +/* Distortion effect */ +#define AL_DISTORTION_MIN_EDGE (0.0f) +#define AL_DISTORTION_MAX_EDGE (1.0f) +#define AL_DISTORTION_DEFAULT_EDGE (0.2f) + +#define AL_DISTORTION_MIN_GAIN (0.01f) +#define AL_DISTORTION_MAX_GAIN (1.0f) +#define AL_DISTORTION_DEFAULT_GAIN (0.05f) + +#define AL_DISTORTION_MIN_LOWPASS_CUTOFF (80.0f) +#define AL_DISTORTION_MAX_LOWPASS_CUTOFF (24000.0f) +#define AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF (8000.0f) + +#define AL_DISTORTION_MIN_EQCENTER (80.0f) +#define AL_DISTORTION_MAX_EQCENTER (24000.0f) +#define AL_DISTORTION_DEFAULT_EQCENTER (3600.0f) + +#define AL_DISTORTION_MIN_EQBANDWIDTH (80.0f) +#define AL_DISTORTION_MAX_EQBANDWIDTH (24000.0f) +#define AL_DISTORTION_DEFAULT_EQBANDWIDTH (3600.0f) + +/* Echo effect */ +#define AL_ECHO_MIN_DELAY (0.0f) +#define AL_ECHO_MAX_DELAY (0.207f) +#define AL_ECHO_DEFAULT_DELAY (0.1f) + +#define AL_ECHO_MIN_LRDELAY (0.0f) +#define AL_ECHO_MAX_LRDELAY (0.404f) +#define AL_ECHO_DEFAULT_LRDELAY (0.1f) + +#define AL_ECHO_MIN_DAMPING (0.0f) +#define AL_ECHO_MAX_DAMPING (0.99f) +#define AL_ECHO_DEFAULT_DAMPING (0.5f) + +#define AL_ECHO_MIN_FEEDBACK (0.0f) +#define AL_ECHO_MAX_FEEDBACK (1.0f) +#define AL_ECHO_DEFAULT_FEEDBACK (0.5f) + +#define AL_ECHO_MIN_SPREAD (-1.0f) +#define AL_ECHO_MAX_SPREAD (1.0f) +#define AL_ECHO_DEFAULT_SPREAD (-1.0f) + +/* Flanger effect */ +#define AL_FLANGER_WAVEFORM_SINUSOID (0) +#define AL_FLANGER_WAVEFORM_TRIANGLE (1) + +#define AL_FLANGER_MIN_WAVEFORM (0) +#define AL_FLANGER_MAX_WAVEFORM (1) +#define AL_FLANGER_DEFAULT_WAVEFORM (1) + +#define AL_FLANGER_MIN_PHASE (-180) +#define AL_FLANGER_MAX_PHASE (180) +#define AL_FLANGER_DEFAULT_PHASE (0) + +#define AL_FLANGER_MIN_RATE (0.0f) +#define AL_FLANGER_MAX_RATE (10.0f) +#define AL_FLANGER_DEFAULT_RATE (0.27f) + +#define AL_FLANGER_MIN_DEPTH (0.0f) +#define AL_FLANGER_MAX_DEPTH (1.0f) +#define AL_FLANGER_DEFAULT_DEPTH (1.0f) + +#define AL_FLANGER_MIN_FEEDBACK (-1.0f) +#define AL_FLANGER_MAX_FEEDBACK (1.0f) +#define AL_FLANGER_DEFAULT_FEEDBACK (-0.5f) + +#define AL_FLANGER_MIN_DELAY (0.0f) +#define AL_FLANGER_MAX_DELAY (0.004f) +#define AL_FLANGER_DEFAULT_DELAY (0.002f) + +/* Frequency shifter effect */ +#define AL_FREQUENCY_SHIFTER_MIN_FREQUENCY (0.0f) +#define AL_FREQUENCY_SHIFTER_MAX_FREQUENCY (24000.0f) +#define AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY (0.0f) + +#define AL_FREQUENCY_SHIFTER_MIN_LEFT_DIRECTION (0) +#define AL_FREQUENCY_SHIFTER_MAX_LEFT_DIRECTION (2) +#define AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION (0) + +#define AL_FREQUENCY_SHIFTER_DIRECTION_DOWN (0) +#define AL_FREQUENCY_SHIFTER_DIRECTION_UP (1) +#define AL_FREQUENCY_SHIFTER_DIRECTION_OFF (2) + +#define AL_FREQUENCY_SHIFTER_MIN_RIGHT_DIRECTION (0) +#define AL_FREQUENCY_SHIFTER_MAX_RIGHT_DIRECTION (2) +#define AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION (0) + +/* Vocal morpher effect */ +#define AL_VOCAL_MORPHER_MIN_PHONEMEA (0) +#define AL_VOCAL_MORPHER_MAX_PHONEMEA (29) +#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEA (0) + +#define AL_VOCAL_MORPHER_MIN_PHONEMEA_COARSE_TUNING (-24) +#define AL_VOCAL_MORPHER_MAX_PHONEMEA_COARSE_TUNING (24) +#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEA_COARSE_TUNING (0) + +#define AL_VOCAL_MORPHER_MIN_PHONEMEB (0) +#define AL_VOCAL_MORPHER_MAX_PHONEMEB (29) +#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEB (10) + +#define AL_VOCAL_MORPHER_MIN_PHONEMEB_COARSE_TUNING (-24) +#define AL_VOCAL_MORPHER_MAX_PHONEMEB_COARSE_TUNING (24) +#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEB_COARSE_TUNING (0) + +#define AL_VOCAL_MORPHER_PHONEME_A (0) +#define AL_VOCAL_MORPHER_PHONEME_E (1) +#define AL_VOCAL_MORPHER_PHONEME_I (2) +#define AL_VOCAL_MORPHER_PHONEME_O (3) +#define AL_VOCAL_MORPHER_PHONEME_U (4) +#define AL_VOCAL_MORPHER_PHONEME_AA (5) +#define AL_VOCAL_MORPHER_PHONEME_AE (6) +#define AL_VOCAL_MORPHER_PHONEME_AH (7) +#define AL_VOCAL_MORPHER_PHONEME_AO (8) +#define AL_VOCAL_MORPHER_PHONEME_EH (9) +#define AL_VOCAL_MORPHER_PHONEME_ER (10) +#define AL_VOCAL_MORPHER_PHONEME_IH (11) +#define AL_VOCAL_MORPHER_PHONEME_IY (12) +#define AL_VOCAL_MORPHER_PHONEME_UH (13) +#define AL_VOCAL_MORPHER_PHONEME_UW (14) +#define AL_VOCAL_MORPHER_PHONEME_B (15) +#define AL_VOCAL_MORPHER_PHONEME_D (16) +#define AL_VOCAL_MORPHER_PHONEME_F (17) +#define AL_VOCAL_MORPHER_PHONEME_G (18) +#define AL_VOCAL_MORPHER_PHONEME_J (19) +#define AL_VOCAL_MORPHER_PHONEME_K (20) +#define AL_VOCAL_MORPHER_PHONEME_L (21) +#define AL_VOCAL_MORPHER_PHONEME_M (22) +#define AL_VOCAL_MORPHER_PHONEME_N (23) +#define AL_VOCAL_MORPHER_PHONEME_P (24) +#define AL_VOCAL_MORPHER_PHONEME_R (25) +#define AL_VOCAL_MORPHER_PHONEME_S (26) +#define AL_VOCAL_MORPHER_PHONEME_T (27) +#define AL_VOCAL_MORPHER_PHONEME_V (28) +#define AL_VOCAL_MORPHER_PHONEME_Z (29) + +#define AL_VOCAL_MORPHER_WAVEFORM_SINUSOID (0) +#define AL_VOCAL_MORPHER_WAVEFORM_TRIANGLE (1) +#define AL_VOCAL_MORPHER_WAVEFORM_SAWTOOTH (2) + +#define AL_VOCAL_MORPHER_MIN_WAVEFORM (0) +#define AL_VOCAL_MORPHER_MAX_WAVEFORM (2) +#define AL_VOCAL_MORPHER_DEFAULT_WAVEFORM (0) + +#define AL_VOCAL_MORPHER_MIN_RATE (0.0f) +#define AL_VOCAL_MORPHER_MAX_RATE (10.0f) +#define AL_VOCAL_MORPHER_DEFAULT_RATE (1.41f) + +/* Pitch shifter effect */ +#define AL_PITCH_SHIFTER_MIN_COARSE_TUNE (-12) +#define AL_PITCH_SHIFTER_MAX_COARSE_TUNE (12) +#define AL_PITCH_SHIFTER_DEFAULT_COARSE_TUNE (12) + +#define AL_PITCH_SHIFTER_MIN_FINE_TUNE (-50) +#define AL_PITCH_SHIFTER_MAX_FINE_TUNE (50) +#define AL_PITCH_SHIFTER_DEFAULT_FINE_TUNE (0) + +/* Ring modulator effect */ +#define AL_RING_MODULATOR_MIN_FREQUENCY (0.0f) +#define AL_RING_MODULATOR_MAX_FREQUENCY (8000.0f) +#define AL_RING_MODULATOR_DEFAULT_FREQUENCY (440.0f) + +#define AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF (0.0f) +#define AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF (24000.0f) +#define AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF (800.0f) + +#define AL_RING_MODULATOR_SINUSOID (0) +#define AL_RING_MODULATOR_SAWTOOTH (1) +#define AL_RING_MODULATOR_SQUARE (2) + +#define AL_RING_MODULATOR_MIN_WAVEFORM (0) +#define AL_RING_MODULATOR_MAX_WAVEFORM (2) +#define AL_RING_MODULATOR_DEFAULT_WAVEFORM (0) + +/* Autowah effect */ +#define AL_AUTOWAH_MIN_ATTACK_TIME (0.0001f) +#define AL_AUTOWAH_MAX_ATTACK_TIME (1.0f) +#define AL_AUTOWAH_DEFAULT_ATTACK_TIME (0.06f) + +#define AL_AUTOWAH_MIN_RELEASE_TIME (0.0001f) +#define AL_AUTOWAH_MAX_RELEASE_TIME (1.0f) +#define AL_AUTOWAH_DEFAULT_RELEASE_TIME (0.06f) + +#define AL_AUTOWAH_MIN_RESONANCE (2.0f) +#define AL_AUTOWAH_MAX_RESONANCE (1000.0f) +#define AL_AUTOWAH_DEFAULT_RESONANCE (1000.0f) + +#define AL_AUTOWAH_MIN_PEAK_GAIN (0.00003f) +#define AL_AUTOWAH_MAX_PEAK_GAIN (31621.0f) +#define AL_AUTOWAH_DEFAULT_PEAK_GAIN (11.22f) + +/* Compressor effect */ +#define AL_COMPRESSOR_MIN_ONOFF (0) +#define AL_COMPRESSOR_MAX_ONOFF (1) +#define AL_COMPRESSOR_DEFAULT_ONOFF (1) + +/* Equalizer effect */ +#define AL_EQUALIZER_MIN_LOW_GAIN (0.126f) +#define AL_EQUALIZER_MAX_LOW_GAIN (7.943f) +#define AL_EQUALIZER_DEFAULT_LOW_GAIN (1.0f) + +#define AL_EQUALIZER_MIN_LOW_CUTOFF (50.0f) +#define AL_EQUALIZER_MAX_LOW_CUTOFF (800.0f) +#define AL_EQUALIZER_DEFAULT_LOW_CUTOFF (200.0f) + +#define AL_EQUALIZER_MIN_MID1_GAIN (0.126f) +#define AL_EQUALIZER_MAX_MID1_GAIN (7.943f) +#define AL_EQUALIZER_DEFAULT_MID1_GAIN (1.0f) + +#define AL_EQUALIZER_MIN_MID1_CENTER (200.0f) +#define AL_EQUALIZER_MAX_MID1_CENTER (3000.0f) +#define AL_EQUALIZER_DEFAULT_MID1_CENTER (500.0f) + +#define AL_EQUALIZER_MIN_MID1_WIDTH (0.01f) +#define AL_EQUALIZER_MAX_MID1_WIDTH (1.0f) +#define AL_EQUALIZER_DEFAULT_MID1_WIDTH (1.0f) + +#define AL_EQUALIZER_MIN_MID2_GAIN (0.126f) +#define AL_EQUALIZER_MAX_MID2_GAIN (7.943f) +#define AL_EQUALIZER_DEFAULT_MID2_GAIN (1.0f) + +#define AL_EQUALIZER_MIN_MID2_CENTER (1000.0f) +#define AL_EQUALIZER_MAX_MID2_CENTER (8000.0f) +#define AL_EQUALIZER_DEFAULT_MID2_CENTER (3000.0f) + +#define AL_EQUALIZER_MIN_MID2_WIDTH (0.01f) +#define AL_EQUALIZER_MAX_MID2_WIDTH (1.0f) +#define AL_EQUALIZER_DEFAULT_MID2_WIDTH (1.0f) + +#define AL_EQUALIZER_MIN_HIGH_GAIN (0.126f) +#define AL_EQUALIZER_MAX_HIGH_GAIN (7.943f) +#define AL_EQUALIZER_DEFAULT_HIGH_GAIN (1.0f) + +#define AL_EQUALIZER_MIN_HIGH_CUTOFF (4000.0f) +#define AL_EQUALIZER_MAX_HIGH_CUTOFF (16000.0f) +#define AL_EQUALIZER_DEFAULT_HIGH_CUTOFF (6000.0f) + + +/* Source parameter value ranges and defaults. */ +#define AL_MIN_AIR_ABSORPTION_FACTOR (0.0f) +#define AL_MAX_AIR_ABSORPTION_FACTOR (10.0f) +#define AL_DEFAULT_AIR_ABSORPTION_FACTOR (0.0f) + +#define AL_MIN_ROOM_ROLLOFF_FACTOR (0.0f) +#define AL_MAX_ROOM_ROLLOFF_FACTOR (10.0f) +#define AL_DEFAULT_ROOM_ROLLOFF_FACTOR (0.0f) + +#define AL_MIN_CONE_OUTER_GAINHF (0.0f) +#define AL_MAX_CONE_OUTER_GAINHF (1.0f) +#define AL_DEFAULT_CONE_OUTER_GAINHF (1.0f) + +#define AL_MIN_DIRECT_FILTER_GAINHF_AUTO AL_FALSE +#define AL_MAX_DIRECT_FILTER_GAINHF_AUTO AL_TRUE +#define AL_DEFAULT_DIRECT_FILTER_GAINHF_AUTO AL_TRUE + +#define AL_MIN_AUXILIARY_SEND_FILTER_GAIN_AUTO AL_FALSE +#define AL_MAX_AUXILIARY_SEND_FILTER_GAIN_AUTO AL_TRUE +#define AL_DEFAULT_AUXILIARY_SEND_FILTER_GAIN_AUTO AL_TRUE + +#define AL_MIN_AUXILIARY_SEND_FILTER_GAINHF_AUTO AL_FALSE +#define AL_MAX_AUXILIARY_SEND_FILTER_GAINHF_AUTO AL_TRUE +#define AL_DEFAULT_AUXILIARY_SEND_FILTER_GAINHF_AUTO AL_TRUE + + +/* Listener parameter value ranges and defaults. */ +#define AL_MIN_METERS_PER_UNIT FLT_MIN +#define AL_MAX_METERS_PER_UNIT FLT_MAX +#define AL_DEFAULT_METERS_PER_UNIT (1.0f) + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* AL_EFX_H */ diff --git a/external/openal_soft/lib/libopenal32.a b/external/openal_soft/lib/libopenal32.a new file mode 100644 index 000000000..5f7352aca Binary files /dev/null and b/external/openal_soft/lib/libopenal32.a differ diff --git a/external/openal_soft/openal32.dll b/external/openal_soft/openal32.dll new file mode 100644 index 000000000..71ced6a21 Binary files /dev/null and b/external/openal_soft/openal32.dll differ diff --git a/fonts/coming_soon b/fonts/coming_soon new file mode 100644 index 000000000..e69de29bb diff --git a/logo/logo128x128.png b/logo/logo128x128.png new file mode 100644 index 000000000..99ba54374 Binary files /dev/null and b/logo/logo128x128.png differ diff --git a/logo/logo16x16.png b/logo/logo16x16.png new file mode 100644 index 000000000..ee22d023d Binary files /dev/null and b/logo/logo16x16.png differ diff --git a/logo/logo24x24.png b/logo/logo24x24.png new file mode 100644 index 000000000..7acea950d Binary files /dev/null and b/logo/logo24x24.png differ diff --git a/logo/logo256x256.png b/logo/logo256x256.png new file mode 100644 index 000000000..665456277 Binary files /dev/null and b/logo/logo256x256.png differ diff --git a/logo/logo32x32.png b/logo/logo32x32.png new file mode 100644 index 000000000..0750ed3a6 Binary files /dev/null and b/logo/logo32x32.png differ diff --git a/logo/logo48x4.png b/logo/logo48x4.png new file mode 100644 index 000000000..72713a973 Binary files /dev/null and b/logo/logo48x4.png differ diff --git a/logo/logo64x64.png b/logo/logo64x64.png new file mode 100644 index 000000000..6a1d54dd5 Binary files /dev/null and b/logo/logo64x64.png differ diff --git a/logo/raylib.ico b/logo/raylib.ico new file mode 100644 index 000000000..afeb12b91 Binary files /dev/null and b/logo/raylib.ico differ diff --git a/src/audio.c b/src/audio.c new file mode 100644 index 000000000..41ba6fed1 --- /dev/null +++ b/src/audio.c @@ -0,0 +1,306 @@ +/********************************************************************************************* +* +* raylib.audio +* +* Basic functions to manage Audio: InitAudioDevice, LoadAudioFiles, PlayAudioFiles +* +* Uses external lib: +* OpenAL - Audio device management lib +* TODO: stb_vorbis - Ogg audio files loading +* +* Copyright (c) 2013 Ramon Santamaria (Ray San - raysan@raysanweb.com) +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#include "raylib.h" + +#include // OpenAL basic header +#include // OpenAL context header (like OpenGL, OpenAL requires a context to work) + +#include // To use exit() function +#include // Used for .WAV loading + +//#include "stb_vorbis.h" // TODO: OGG loading functions + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +// Nop... + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- + +// Wave file data +typedef struct Wave { + unsigned char *data; // Buffer data pointer + unsigned int sampleRate; + unsigned int dataSize; + short channels; + short format; +} Wave; + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +// Nop... + +//---------------------------------------------------------------------------------- +// Module specific Functions Declaration +//---------------------------------------------------------------------------------- +static Wave LoadWAV(char *fileName); +static void UnloadWAV(Wave wave); +//static Ogg LoadOGG(char *fileName); + +//---------------------------------------------------------------------------------- +// Module Functions Definition - Window and OpenGL Context Functions +//---------------------------------------------------------------------------------- + +// Initialize audio device and context +void InitAudioDevice() +{ + // Open and initialize a device with default settings + ALCdevice *device = alcOpenDevice(NULL); + + if(!device) + { + fprintf(stderr, "Could not open a device!\n"); + exit(1); + } + + ALCcontext *context = alcCreateContext(device, NULL); + + if(context == NULL || alcMakeContextCurrent(context) == ALC_FALSE) + { + if(context != NULL) alcDestroyContext(context); + + alcCloseDevice(device); + + fprintf(stderr, "Could not set a context!\n"); + exit(1); + } + + printf("Opened \"%s\"\n", alcGetString(device, ALC_DEVICE_SPECIFIER)); + + // Listener definition (just for 2D) + alListener3f(AL_POSITION, 0, 0, 0); + alListener3f(AL_VELOCITY, 0, 0, 0); + alListener3f(AL_ORIENTATION, 0, 0, -1); +} + +// Close the audio device for the current context, and destroys the context +void CloseAudioDevice() +{ + ALCdevice *device; + ALCcontext *context = alcGetCurrentContext(); + + if (context == NULL) return; + + device = alcGetContextsDevice(context); + + alcMakeContextCurrent(NULL); + alcDestroyContext(context); + alcCloseDevice(device); +} + +// Load sound to memory +Sound LoadSound(char *fileName) +{ + Sound sound; + + // NOTE: The entire file is loaded to memory to play it all at once (no-streaming) + + // WAV file loading + // NOTE: Buffer space is allocated inside LoadWAV, Wave must be freed + Wave wave = LoadWAV(fileName); + + ALenum format; + // The OpenAL format is worked out by looking at the number of channels and the bits per sample + if (wave.channels == 1) + { + if (wave.sampleRate == 8 ) format = AL_FORMAT_MONO8; + else if (wave.sampleRate == 16) format = AL_FORMAT_MONO16; + } + else if (wave.channels == 2) + { + if (wave.sampleRate == 8 ) format = AL_FORMAT_STEREO8; + else if (wave.sampleRate == 16) format = AL_FORMAT_STEREO16; + } + + // Create an audio source + ALuint source; + alGenSources(1, &source); // Generate pointer to audio source + + alSourcef(source, AL_PITCH, 1); + alSourcef(source, AL_GAIN, 1); + alSource3f(source, AL_POSITION, 0, 0, 0); + alSource3f(source, AL_VELOCITY, 0, 0, 0); + alSourcei(source, AL_LOOPING, AL_FALSE); + + // Convert loaded data to OpenAL buffer + //---------------------------------------- + ALuint buffer; + alGenBuffers(1, &buffer); // Generate pointer to buffer + + // Upload sound data to buffer + alBufferData(buffer, format, wave.data, wave.dataSize, wave.sampleRate); + + // Attach sound buffer to source + alSourcei(source, AL_BUFFER, buffer); + + // Unallocate WAV data + UnloadWAV(wave); + + printf("Sample rate: %i\n", wave.sampleRate); + printf("Channels: %i\n", wave.channels); + printf("Format: %i\n", wave.format); + + printf("Audio file loaded...!\n"); + + sound.source = source; + sound.buffer = buffer; + + return sound; +} + +// Unload sound +void UnloadSound(Sound sound) +{ + alDeleteSources(1, &sound.source); + alDeleteBuffers(1, &sound.buffer); +} + +// Play a sound +void PlaySound(Sound sound) +{ + alSourcePlay(sound.source); // Play the sound + + printf("Playing sound!\n"); + + // Find the current position of the sound being played + // NOTE: Only work when the entire file is in a single buffer + //int byteOffset; + //alGetSourcei(sound.source, AL_BYTE_OFFSET, &byteOffset); + //float seconds = (float)byteOffset / sampleRate; // Number of seconds since the beginning of the sound +} + +// Play a sound with extended options +void PlaySoundEx(Sound sound, float timePosition, bool loop) +{ + // TODO: Review + + // Change the current position (e.g. skip some part of the sound) + // NOTE: Only work when the entire file is in a single buffer + //alSourcei(sound.source, AL_BYTE_OFFSET, int(position * sampleRate)); + + alSourcePlay(sound.source); // Play the sound + + if (loop) alSourcei(sound.source, AL_LOOPING, AL_TRUE); + else alSourcei(sound.source, AL_LOOPING, AL_FALSE); +} + +// Pause a sound +void PauseSound(Sound sound) +{ + alSourcePause(sound.source); +} + +// Stop reproducing a sound +void StopSound(Sound sound) +{ + alSourceStop(sound.source); +} + +// Load WAV file into Wave structure +static Wave LoadWAV(char *fileName) +{ + Wave wave; + FILE *wavFile; + + wavFile = fopen(fileName, "rb"); + + if (!wavFile) + { + printf("Could not load WAV file.\n"); + exit(1); + } + + unsigned char id[4]; // Four bytes to hold 'RIFF' and 'WAVE' (and other ids) + + unsigned int size = 0; // File size (useless) + + short format; + short channels; + short blockAlign; + short bitsPerSample; + + unsigned int formatLength; + unsigned int sampleRate; + unsigned int avgBytesSec; + unsigned int dataSize; + + fread(id, sizeof(unsigned char), 4, wavFile); // Read the first four bytes + + if ((id[0] != 'R') || (id[1] != 'I') || (id[2] != 'F') || (id[3] != 'F')) + { + printf("Invalid RIFF file.\n"); // If not "RIFF" id, exit + exit(1); + } + + fread(&size, sizeof(unsigned int), 1, wavFile); // Read file size + fread(id, sizeof(unsigned char), 4, wavFile); // Read the next id + + if ((id[0] != 'W') || (id[1] != 'A') || (id[2] != 'V') || (id[3] != 'E')) + { + printf("Invalid WAVE file.\n"); // If not "WAVE" id, exit + exit(1); + } + + fread(id, sizeof(unsigned char), 4, wavFile); // Read 4 bytes id "fmt " + fread(&formatLength, sizeof(unsigned int),1,wavFile); // Read format lenght + fread(&format, sizeof(short), 1, wavFile); // Read format tag + fread(&channels, sizeof(short), 1, wavFile); // Read num channels (1 mono, 2 stereo) + fread(&sampleRate, sizeof(unsigned int), 1, wavFile); // Read sample rate (44100, 22050, etc...) + fread(&avgBytesSec, sizeof(short), 1, wavFile); // Read average bytes per second (probably won't need this) + fread(&blockAlign, sizeof(short), 1, wavFile); // Read block alignment (probably won't need this) + fread(&bitsPerSample, sizeof(short), 1, wavFile); // Read bits per sample (8 bit or 16 bit) + + fread(id, sizeof(unsigned char), 4, wavFile); // Read 4 bytes id "data" + fread(&dataSize, sizeof(unsigned int), 1, wavFile); // Read data size (in bytes) + + wave.sampleRate = sampleRate; + wave.dataSize = dataSize; + wave.channels = channels; + wave.format = format; + + wave.data = (unsigned char *)malloc(sizeof(unsigned char) * dataSize); // Allocate the required bytes to store data + + fread(wave.data, sizeof(unsigned char), dataSize, wavFile); // Read the whole sound data chunk + + return wave; +} + +// Unload WAV file data +static void UnloadWAV(Wave wave) +{ + free(wave.data); +} + +// TODO: Ogg data loading +//static Ogg LoadOGG(char *fileName) { } + diff --git a/src/core.c b/src/core.c new file mode 100644 index 000000000..c835cb5b3 --- /dev/null +++ b/src/core.c @@ -0,0 +1,582 @@ +/********************************************************************************************* +* +* raylib.core +* +* Basic functions to manage Windows, OpenGL context and Input +* +* Uses external lib: +* GLFW3 - Window, context and Input management (static lib version) +* +* Copyright (c) 2013 Ramon Santamaria (Ray San - raysan@raysanweb.com) +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#include "raylib.h" + +#include // GLFW3 lib: Windows, OpenGL context and Input management +//#include // OpenGL functions (GLFW3 already includes gl.h) +#include // Standard input / output lib +#include // Declares malloc() and free() for memory management +#include // Math related functions, tan() on SetPerspective +#include "vector3.h" // Basic Vector3 functions + +//#define GLFW_DLL // Using GLFW DLL on Windows -> No, we use static version! + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +// Nop... + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +typedef Color pixel; + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +static GLFWwindow* window; // Main window +static bool fullscreen; // Fullscreen mode track + +static double currentTime, previousTime; // Used to track timmings +static double updateTime, drawTime; // Time measures for update and draw +static double frameTime; // Time measure for one frame +static double targetTime = 0; // Desired time for one frame, if 0 not applied + +static int windowWidth, windowHeight; // Required to switch between windowed/fullscren mode (F11) +static char *windowTitle; // Required to switch between windowed/fullscren mode (F11) + +//---------------------------------------------------------------------------------- +// Other Modules Functions Declaration (required by core) +//---------------------------------------------------------------------------------- +extern void LoadDefaultFont(); // [Module: text] Loads default font on InitWindow() +extern void UnloadDefaultFont(); // [Module: text] Unloads default font from GPU memory +extern void WriteBitmap(const char *fileName, const pixel *imgDataPixel, int width, int height); // [Module: textures] Writes a bitmap (BMP) file + +//---------------------------------------------------------------------------------- +// Module specific Functions Declaration +//---------------------------------------------------------------------------------- +static void InitGraphicsDevice(); // Initialize Graphics Device (OpenGL stuff) +static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error +static void KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods); // GLFW3 Keyboard Callback, runs on key pressed +static void WindowSizeCallback(GLFWwindow* window, int width, int height); // GLFW3 WindowSize Callback, runs when window is resized +static void CameraLookAt(Vector3 position, Vector3 target, Vector3 up); // Setup camera view (updates MODELVIEW matrix) +static void SetPerspective(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar); // Setup view projection (updates PROJECTION matrix) +static void TakeScreenshot(); // Takes a bitmap (BMP) screenshot and saves it in the same folder as executable + +//---------------------------------------------------------------------------------- +// Module Functions Definition - Window and OpenGL Context Functions +//---------------------------------------------------------------------------------- + +// Initialize Window and Graphics Context (OpenGL) +void InitWindow(int width, int height, char* title) +{ + glfwSetErrorCallback(ErrorCallback); + + if (!glfwInit()) exit(1); + + //glfwWindowHint(GLFW_SAMPLES, 4); // If called before windows creation, enables multisampling x4 (MSAA), default is 0 + + window = glfwCreateWindow(width, height, title, NULL, NULL); + + windowWidth = width; + windowHeight = height; + windowTitle = title; + + if (!window) + { + glfwTerminate(); + exit(1); + } + + glfwSetWindowSizeCallback(window, WindowSizeCallback); + + glfwMakeContextCurrent(window); + glfwSetKeyCallback(window, KeyCallback); + glfwSwapInterval(0); // Disables GPU v-sync (if set), so frames are not limited to screen refresh rate (60Hz -> 60 FPS) + // If not set, swap interval uses GPU v-sync configuration + // Framerate can be setup using SetTargetFPS() + InitGraphicsDevice(); + + previousTime = glfwGetTime(); + + LoadDefaultFont(); +} + +// Close Window and Terminate Context +void CloseWindow() +{ + UnloadDefaultFont(); + + glfwDestroyWindow(window); + glfwTerminate(); +} + +// Detect if KEY_ESCAPE pressed or Close icon pressed +bool WindowShouldClose() +{ + return (glfwWindowShouldClose(window)); +} + +// Fullscreen toggle (by default F11) +void ToggleFullscreen() +{ + if (glfwGetKey(window, GLFW_KEY_F11)) + { + fullscreen = !fullscreen; // Toggle fullscreen flag + + glfwDestroyWindow(window); // Destroy the current window (we will recreate it!) + + // NOTE: Window aspect ratio is always windowWidth / windowHeight + if (fullscreen) window = glfwCreateWindow(windowWidth, windowHeight, windowTitle, glfwGetPrimaryMonitor(), NULL); // Fullscreen mode + else window = glfwCreateWindow(windowWidth, windowHeight, windowTitle, NULL, NULL); + + if (!window) + { + glfwTerminate(); + exit(1); + } + + glfwMakeContextCurrent(window); + glfwSetKeyCallback(window, KeyCallback); + + InitGraphicsDevice(); + } +} + +// Sets Background Color +void ClearBackground(Color color) +{ + // Color values clamp to 0.0f(0) and 1.0f(255) + float r = (float)color.r / 255; + float g = (float)color.g / 255; + float b = (float)color.b / 255; + float a = (float)color.a / 255; + + glClearColor(r, g, b, a); +} + +// Setup drawing canvas to start drawing +void BeginDrawing() +{ + currentTime = glfwGetTime(); // glfwGetTime() returns a 'double' containing the number of elapsed seconds since glfwInit() was called + updateTime = currentTime - previousTime; + previousTime = currentTime; + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear used buffers, Depth Buffer is used for 3D + + glLoadIdentity(); // Reset current matrix (MODELVIEW) + + glTranslatef(0.375, 0.375, 0); // HACK to have 2D pixel-perfect drawing on OpenGL +} + +// End canvas drawing and Swap Buffers (Double Buffering) +void EndDrawing() +{ + glfwSwapBuffers(window); // Swap back and front buffers + glfwPollEvents(); // Register keyboard/mouse events + + currentTime = glfwGetTime(); + drawTime = currentTime - previousTime; + previousTime = currentTime; + + frameTime = updateTime + drawTime; + + double extraTime = 0; + + while (frameTime < targetTime) + { + // Implement a delay + currentTime = glfwGetTime(); + extraTime = currentTime - previousTime; + previousTime = currentTime; + frameTime += extraTime; + } +} + +// Initializes 3D mode for drawing (Camera setup) +void Begin3dMode(Camera camera) +{ + //glEnable(GL_LIGHTING); // TODO: Setup proper lighting system (raylib 1.x) + + glMatrixMode(GL_PROJECTION); // Switch to projection matrix + + glPushMatrix(); // Save previous matrix, which contains the settings for the 2d ortho projection + glLoadIdentity(); // Reset current matrix (PROJECTION) + + SetPerspective(45.0f, (GLfloat)windowWidth/(GLfloat)windowHeight, 0.1f, 100.0f); // Setup perspective projection + + glMatrixMode(GL_MODELVIEW); // Switch back to modelview matrix + glLoadIdentity(); // Reset current matrix (MODELVIEW) + + CameraLookAt(camera.position, camera.target, camera.up); // Setup Camera view +} + +// Ends 3D mode and returns to default 2D orthographic mode +void End3dMode() +{ + glMatrixMode(GL_PROJECTION); // Switch to projection matrix + glPopMatrix(); // Restore previous matrix (PROJECTION) from matrix stack + + glMatrixMode(GL_MODELVIEW); // Get back to modelview matrix + glLoadIdentity(); // Reset current matrix (MODELVIEW) + + glTranslatef(0.375, 0.375, 0); // HACK to ensure pixel-perfect drawing on OpenGL (after exiting 3D mode) + + //glDisable(GL_LIGHTING); // TODO: Setup proper lighting system (raylib 1.x) +} + +// Set target FPS for the game +void SetTargetFPS(int fps) +{ + targetTime = 1 / (float)fps; + + printf("TargetTime per Frame: %f seconds\n", (float)targetTime); +} + +// Returns current FPS +float GetFPS() +{ + return (1/(float)frameTime); +} + +// Returns time in seconds for one frame +float GetFrameTime() +{ + // As we are operating quite a lot with frameTime, it could be no stable + // so we round it before before passing around to be used + // NOTE: There are still problems with high framerates (>500fps) + double roundedFrameTime = round(frameTime*10000) / 10000; + + return (float)roundedFrameTime; // Time in seconds to run a frame +} + +// Returns a Color struct from hexadecimal value +Color GetColor(int hexValue) +{ + Color color; + + color.r = (unsigned char)(hexValue >> 24) & 0xFF; + color.g = (unsigned char)(hexValue >> 16) & 0xFF; + color.b = (unsigned char)(hexValue >> 8) & 0xFF; + color.a = (unsigned char)hexValue & 0xFF; + + return color; +} + +// Returns hexadecimal value for a Color +int GetHexValue(Color color) +{ + return ((color.a << 24) + (color.r << 16) + (color.g << 8) + color.b); +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition - Input (Keyboard, Mouse, Gamepad) Functions +//---------------------------------------------------------------------------------- + +// Detect if a key is being pressed (key held down) +bool IsKeyPressed(int key) +{ + if (glfwGetKey(window, key) == GLFW_PRESS) return true; + else return false; +} + +// Detect if a key is NOT being pressed (key not held down) +bool IsKeyReleased(int key) +{ + if (glfwGetKey(window, key) == GLFW_RELEASE) return true; + else return false; +} + +// Detect if a mouse button is being pressed +bool IsMouseButtonPressed(int button) +{ + if (glfwGetMouseButton(window, button) == GLFW_PRESS) return true; + else return false; +} + +// Detect if a mouse button is NOT being pressed +bool IsMouseButtonReleased(int button) +{ + if (glfwGetMouseButton(window, button) == GLFW_RELEASE) return true; + else return false; +} + +// Returns mouse position X +int GetMouseX() +{ + double mouseX; + double mouseY; + + glfwGetCursorPos(window, &mouseX, &mouseY); + + return (int)mouseX; +} + +// Returns mouse position Y +int GetMouseY() +{ + double mouseX; + double mouseY; + + glfwGetCursorPos(window, &mouseX, &mouseY); + + return (int)mouseY; +} + +// Returns mouse position XY +Vector2 GetMousePosition() +{ + double mouseX; + double mouseY; + + glfwGetCursorPos(window, &mouseX, &mouseY); + + Vector2 position = { (float)mouseX, (float)mouseY }; + + return position; +} + +// Detect if a gamepad is available +bool IsGamepadAvailable(int gamepad) +{ + int result = glfwJoystickPresent(gamepad); + + if (result == 1) return true; + else return false; +} + +// Return axis movement vector for a gamepad +Vector2 GetGamepadMovement(int gamepad) +{ + Vector2 vec = { 0, 0 }; + + const float *axes; + int axisCount; + + axes = glfwGetJoystickAxes(gamepad, &axisCount); + + if (axisCount >= 2) + { + vec.x = axes[0]; // Left joystick X + vec.y = axes[1]; // Left joystick Y + + //vec.x = axes[2]; // Right joystick X + //vec.x = axes[3]; // Right joystick Y + } + + return vec; +} + +// Detect if a gamepad button is being pressed +bool IsGamepadButtonPressed(int gamepad, int button) +{ + const unsigned char* buttons; + int buttonsCount; + + buttons = glfwGetJoystickButtons(gamepad, &buttonsCount); + + if (buttons[button] == GLFW_PRESS) + { + return true; + } + else return false; +} + +// Detect if a gamepad button is NOT being pressed +bool IsGamepadButtonReleased(int gamepad, int button) +{ + const unsigned char* buttons; + int buttonsCount; + + buttons = glfwGetJoystickButtons(gamepad, &buttonsCount); + + if (buttons[button] == GLFW_RELEASE) + { + return true; + } + else return false; +} + +//---------------------------------------------------------------------------------- +// Module specific Functions Definition +//---------------------------------------------------------------------------------- + +// GLFW3 Error Callback, runs on GLFW3 error +static void ErrorCallback(int error, const char *description) +{ + printf(description); + //fprintf(stderr, description); +} + +// GLFW3 Keyboard Callback, runs on key pressed +static void KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) +{ + if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) + { + glfwSetWindowShouldClose(window, GL_TRUE); + + // NOTE: Before closing window, while loop must be left! + } + else if (key == GLFW_KEY_F11 && action == GLFW_PRESS) + { + ToggleFullscreen(); + } + else if (key == GLFW_KEY_F12 && action == GLFW_PRESS) + { + TakeScreenshot(); + } +} + +// GLFW3 WindowSize Callback, runs when window is resized +static void WindowSizeCallback(GLFWwindow* window, int width, int height) +{ + InitGraphicsDevice(); // If window is resized, graphics device is re-initialized + // NOTE: Aspect ratio does not change, so, image can be deformed +} + +// Initialize Graphics Device (OpenGL stuff) +static void InitGraphicsDevice() +{ + int fbWidth, fbHeight; + + glfwGetFramebufferSize(window, &fbWidth, &fbHeight); // Get framebuffer size of current window + + glViewport(0, 0, fbWidth, fbHeight); // Set viewport width and height + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear used buffers, depth buffer is used for 3D + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // Set background color (black) + glClearDepth(1.0f); // Clear depth buffer + + glEnable(GL_DEPTH_TEST); // Enables depth testing (required for 3D) + glDepthFunc(GL_LEQUAL); // Type of depth testing to apply + + glEnable(GL_BLEND); // Enable color blending (required to work with transparencies) + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Color blending function (how colors are mixed) + + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Improve quality of color and texture coordinate interpolation (Deprecated in OGL 3.0) + // Other options: GL_FASTEST, GL_DONT_CARE (default) + + glMatrixMode(GL_PROJECTION); // Switch to PROJECTION matrix + glLoadIdentity(); // Reset current matrix (PROJECTION) + glOrtho(0, fbWidth, fbHeight, 0, 0, 1); // Config orthographic mode: top-left corner --> (0,0) + glMatrixMode(GL_MODELVIEW); // Switch back to MODELVIEW matrix + glLoadIdentity(); // Reset current matrix (MODELVIEW) + + glDisable(GL_LIGHTING); // Lighting Disabled... + + // TODO: Create an efficient Lighting System with proper functions (raylib 1.x) +/* + glEnable(GL_COLOR_MATERIAL); // Enable materials, causes some glMaterial atributes to track the current color (glColor)... + glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE); // Material types and where to apply them + // NOTE: ONLY works with lighting; defines how light interacts with material + + glLightfv(GL_LIGHT1, GL_AMBIENT, lightAmbient); // Define ambient light color property + glLightfv(GL_LIGHT1, GL_DIFFUSE, lightDiffuse); // Define diffuse light color property + glLightfv(GL_LIGHT1, GL_POSITION, lightPosition); // Define light position + + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT1); // Enable light one (8 lights available at the same time) +*/ + // TODO: Review all shapes/models are drawn CCW and enable backface culling + + //glEnable(GL_CULL_FACE); // Enable backface culling (Disabled by default) + //glCullFace(GL_BACK); // Cull the Back face (default) + //glFrontFace(GL_CCW); // Front face are defined counter clockwise (default) + + glShadeModel(GL_SMOOTH); // Smooth shading between vertex (vertex colors interpolation) + // Possible options: GL_SMOOTH (Color interpolation) or GL_FLAT (no interpolation) +} + +// Setup camera view (updates MODELVIEW matrix) +static void CameraLookAt(Vector3 position, Vector3 target, Vector3 up) +{ + float rotMatrix[16]; // Matrix to store camera rotation + + Vector3 rotX, rotY, rotZ; // Vectors to calculate camera rotations X, Y, Z (Euler) + + // Construct rotation matrix from vectors + rotZ = VectorSubtract(position, target); + VectorNormalize(&rotZ); + rotY = up; // Y rotation vector + rotX = VectorCrossProduct(rotY, rotZ); // X rotation vector = Y cross Z + rotY = VectorCrossProduct(rotZ, rotX); // Recompute Y rotation = Z cross X + VectorNormalize(&rotX); // X rotation vector normalization + VectorNormalize(&rotY); // Y rotation vector normalization + + rotMatrix[0] = rotX.x; + rotMatrix[1] = rotY.x; + rotMatrix[2] = rotZ.x; + rotMatrix[3] = 0.0f; + rotMatrix[4] = rotX.y; + rotMatrix[5] = rotY.y; + rotMatrix[6] = rotZ.y; + rotMatrix[7] = 0.0f; + rotMatrix[8] = rotX.z; + rotMatrix[9] = rotY.z; + rotMatrix[10] = rotZ.z; + rotMatrix[11] = 0.0f; + rotMatrix[12] = 0.0f; + rotMatrix[13] = 0.0f; + rotMatrix[14] = 0.0f; + rotMatrix[15] = 1.0f; + + glMultMatrixf(rotMatrix); // Multiply MODELVIEW matrix by rotation matrix + + glTranslatef(-position.x, -position.y, -position.z); // Translate eye to position +} + +// Setup view projection (updates PROJECTION matrix) +static void SetPerspective(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar) +{ + double xmin, xmax, ymin, ymax; + + ymax = zNear * tan(fovy * PI / 360.0); + ymin = -ymax; + xmin = ymin * aspect; + xmax = ymax * aspect; + + glFrustum(xmin, xmax, ymin, ymax, zNear, zFar); +} + +// Takes a bitmap (BMP) screenshot and saves it in the same folder as executable +static void TakeScreenshot() +{ + static int shotNum = 0; // Screenshot number, increments every screenshot take during program execution + + char buffer[20]; // Buffer to store file name + int fbWidth, fbHeight; + + Color *imgDataPixel; // Pixel image data array + + glfwGetFramebufferSize(window, &fbWidth, &fbHeight); // Get framebuffer size of current window + + imgDataPixel = (Color *)malloc(fbWidth * fbHeight * sizeof(Color)); + + // NOTE: glReadPixels returns image flipped vertically -> (0,0) is the bottom left corner of the framebuffer + glReadPixels(0, 0, fbWidth, fbHeight, GL_RGBA, GL_UNSIGNED_BYTE, imgDataPixel); + + sprintf(buffer, "screenshot%03i.bmp", shotNum); + + // NOTE: BMP directly stores data flipped vertically + WriteBitmap(buffer, imgDataPixel, fbWidth, fbHeight); // Writes pixel data array into a bitmap (BMP) file + + free(imgDataPixel); + + shotNum++; +} \ No newline at end of file diff --git a/src/models.c b/src/models.c new file mode 100644 index 000000000..af5012ebf --- /dev/null +++ b/src/models.c @@ -0,0 +1,819 @@ +/********************************************************************************************* +* +* raylib.models +* +* Basic functions to draw 3d shapes and load/draw 3d models (.OBJ) +* +* Copyright (c) 2013 Ramon Santamaria (Ray San - raysan@raysanweb.com) +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#include "raylib.h" + +#include // OpenGL functions +#include // Standard input/output functions, used to read model files data +#include // Declares malloc() and free() for memory management +#include // Used for sin, cos, tan +#include "vector3.h" // Basic Vector3 functions + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +// Nop... + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +// ... + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +// It's lonely here... + +//---------------------------------------------------------------------------------- +// Module specific Functions Declaration +//---------------------------------------------------------------------------------- +// No private (static) functions in this module + +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- + +// Draw cube +// NOTE: Cube position is de center position +void DrawCube(Vector3 position, float width, float height, float lenght, Color color) +{ + glPushMatrix(); + glTranslatef(position.x, position.y, position.z); + //glRotatef(rotation, 0.0f, 1.0f, 0.0f); + //glScalef(1.0f, 1.0f, 1.0f); + + glBegin(GL_QUADS); + glColor4ub(color.r, color.g, color.b, color.a); + + // Front Face + glNormal3f(0.0f, 0.0f, 1.0f); // Normal Pointing Towards Viewer + glTexCoord2f(0.0f, 0.0f); glVertex3f(-width/2, -height/2, lenght/2); // Bottom Left Of The Texture and Quad + glTexCoord2f(1.0f, 0.0f); glVertex3f( width/2, -height/2, lenght/2); // Bottom Right Of The Texture and Quad + glTexCoord2f(1.0f, 1.0f); glVertex3f( width/2, height/2, lenght/2); // Top Right Of The Texture and Quad + glTexCoord2f(0.0f, 1.0f); glVertex3f(-width/2, height/2, lenght/2); // Top Left Of The Texture and Quad + // Back Face + glNormal3f( 0.0f, 0.0f,-1.0f); // Normal Pointing Away From Viewer + glTexCoord2f(1.0f, 0.0f); glVertex3f(-width/2, -height/2, -lenght/2); // Bottom Right Of The Texture and Quad + glTexCoord2f(1.0f, 1.0f); glVertex3f(-width/2, height/2, -lenght/2); // Top Right Of The Texture and Quad + glTexCoord2f(0.0f, 1.0f); glVertex3f( width/2, height/2, -lenght/2); // Top Left Of The Texture and Quad + glTexCoord2f(0.0f, 0.0f); glVertex3f( width/2, -height/2, -lenght/2); // Bottom Left Of The Texture and Quad + // Top Face + glNormal3f( 0.0f, 1.0f, 0.0f); // Normal Pointing Up + glTexCoord2f(0.0f, 1.0f); glVertex3f(-width/2, height/2, -lenght/2); // Top Left Of The Texture and Quad + glTexCoord2f(0.0f, 0.0f); glVertex3f(-width/2, height/2, lenght/2); // Bottom Left Of The Texture and Quad + glTexCoord2f(1.0f, 0.0f); glVertex3f( width/2, height/2, lenght/2); // Bottom Right Of The Texture and Quad + glTexCoord2f(1.0f, 1.0f); glVertex3f( width/2, height/2, -lenght/2); // Top Right Of The Texture and Quad + // Bottom Face + glNormal3f( 0.0f,-1.0f, 0.0f); // Normal Pointing Down + glTexCoord2f(1.0f, 1.0f); glVertex3f(-width/2, -height/2, -lenght/2); // Top Right Of The Texture and Quad + glTexCoord2f(0.0f, 1.0f); glVertex3f( width/2, -height/2, -lenght/2); // Top Left Of The Texture and Quad + glTexCoord2f(0.0f, 0.0f); glVertex3f( width/2, -height/2, lenght/2); // Bottom Left Of The Texture and Quad + glTexCoord2f(1.0f, 0.0f); glVertex3f(-width/2, -height/2, lenght/2); // Bottom Right Of The Texture and Quad + // Right face + glNormal3f( 1.0f, 0.0f, 0.0f); // Normal Pointing Right + glTexCoord2f(1.0f, 0.0f); glVertex3f( width/2, -height/2, -lenght/2); // Bottom Right Of The Texture and Quad + glTexCoord2f(1.0f, 1.0f); glVertex3f( width/2, height/2, -lenght/2); // Top Right Of The Texture and Quad + glTexCoord2f(0.0f, 1.0f); glVertex3f( width/2, height/2, lenght/2); // Top Left Of The Texture and Quad + glTexCoord2f(0.0f, 0.0f); glVertex3f( width/2, -height/2, lenght/2); // Bottom Left Of The Texture and Quad + // Left Face + glNormal3f(-1.0f, 0.0f, 0.0f); // Normal Pointing Left + glTexCoord2f(0.0f, 0.0f); glVertex3f(-width/2, -height/2, -lenght/2); // Bottom Left Of The Texture and Quad + glTexCoord2f(1.0f, 0.0f); glVertex3f(-width/2, -height/2, lenght/2); // Bottom Right Of The Texture and Quad + glTexCoord2f(1.0f, 1.0f); glVertex3f(-width/2, height/2, lenght/2); // Top Right Of The Texture and Quad + glTexCoord2f(0.0f, 1.0f); glVertex3f(-width/2, height/2, -lenght/2); // Top Left Of The Texture and Quad + glEnd(); + glPopMatrix(); +} + +// Draw cube (Vector version) +void DrawCubeV(Vector3 position, Vector3 size, Color color) +{ + DrawCube(position, size.x, size.y, size.z, color); +} + +// Draw cube wires +void DrawCubeWires(Vector3 position, float width, float height, float lenght, Color color) +{ + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + DrawCube(position, width, height, lenght, color); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); +} + +// Draw sphere +void DrawSphere(Vector3 centerPos, float radius, Color color) +{ + DrawSphereEx(centerPos, radius, 16, 16, color); +} + +// Draw sphere with extended parameters +void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color color) +{ + float lat0, z0, zr0; + float lat1, z1, zr1; + float lng, x, y; + + glPushMatrix(); + glTranslatef(centerPos.x, centerPos.y, centerPos.z); + glRotatef(90, 1, 0, 0); + glScalef(radius, radius, radius); + + glBegin(GL_QUAD_STRIP); + + glColor4ub(color.r, color.g, color.b, color.a); + + for(int i = 0; i <= rings; i++) + { + lat0 = PI * (-0.5 + (float)(i - 1) / rings); + z0 = sin(lat0); + zr0 = cos(lat0); + + lat1 = PI * (-0.5 + (float)i / rings); + z1 = sin(lat1); + zr1 = cos(lat1); + + for(int j = 0; j <= slices; j++) + { + lng = 2 * PI * (float)(j - 1) / slices; + x = cos(lng); + y = sin(lng); + + glNormal3f(x * zr0, y * zr0, z0); + glVertex3f(x * zr0, y * zr0, z0); + + glNormal3f(x * zr1, y * zr1, z1); + glVertex3f(x * zr1, y * zr1, z1); + } + } + glEnd(); + glPopMatrix(); +} + +// Draw sphere wires +void DrawSphereWires(Vector3 centerPos, float radius, Color color) +{ + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + DrawSphere(centerPos, radius, color); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); +} + +// Draw a cylinder/cone +void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color) // Could be used for pyramid and cone! +{ + static int count = 0; + + Vector3 a = { position.x, position.y + height, position.z }; + Vector3 d = { 0.0f, 1.0f, 0.0f }; + Vector3 p; + Vector3 c = { a.x + (-d.x * height), a.y + (-d.y * height), a.z + (-d.z * height) }; //= a + (-d * h); + Vector3 e0 = VectorPerpendicular(d); + Vector3 e1 = VectorCrossProduct(e0, d); + float angInc = 360.0 / slices * DEG2RAD; + + if (radiusTop == 0) // Draw pyramid or cone + { + //void drawCone(const Vector3 &d, const Vector3 &a, const float h, const float rd, const int n) + //d – axis defined as a normalized vector from base to apex + //a – position of apex (top point) + //h – height + //rd – radius of directrix + //n – number of radial "slices" + + glPushMatrix(); + //glTranslatef(centerPos.x, centerPos.y, centerPos.z); + glRotatef(DEG2RAD*count, 0.0f, 1.0f, 0.0f); + //glScalef(1.0f, 1.0f, 1.0f); + + // Draw cone top + glBegin(GL_TRIANGLE_FAN); + glColor4ub(color.r, color.g, color.b, color.a); + glVertex3f(a.x, a.y, a.z); + for (int i = 0; i <= slices; i++) + { + float rad = angInc * i; + p.x = c.x + (((e0.x * cos(rad)) + (e1.x * sin(rad))) * radiusBottom); + p.y = c.y + (((e0.y * cos(rad)) + (e1.y * sin(rad))) * radiusBottom); + p.z = c.z + (((e0.z * cos(rad)) + (e1.z * sin(rad))) * radiusBottom); + glVertex3f(p.x, p.y, p.z); + } + glEnd(); + + // Draw cone bottom + glBegin(GL_TRIANGLE_FAN); + glColor4ub(color.r, color.g, color.b, color.a); + glVertex3f(c.x, c.y, c.z); + for (int i = slices; i >= 0; i--) + { + float rad = angInc * i; + p.x = c.x + (((e0.x * cos(rad)) + (e1.x * sin(rad))) * radiusBottom); + p.y = c.y + (((e0.y * cos(rad)) + (e1.y * sin(rad))) * radiusBottom); + p.z = c.z + (((e0.z * cos(rad)) + (e1.z * sin(rad))) * radiusBottom); + glVertex3f(p.x, p.y, p.z); + } + glEnd(); + + glPopMatrix(); + } + else // Draw cylinder + { + glPushMatrix(); + //glTranslatef(centerPos.x, centerPos.y, centerPos.z); + glRotatef(DEG2RAD*count, 0.0f, 1.0f, 0.0f); + //glScalef(1.0f, 1.0f, 1.0f); + + // Draw cylinder top (pointed cap) + glBegin(GL_TRIANGLE_FAN); + glColor4ub(color.r, color.g, color.b, color.a); + glVertex3f(c.x, c.y + height, c.z); + for (int i = slices; i >= 0; i--) + { + float rad = angInc * i; + p.x = c.x + (((e0.x * cos(rad)) + (e1.x * sin(rad))) * radiusTop); + p.y = c.y + (((e0.y * cos(rad)) + (e1.y * sin(rad))) * radiusTop) + height; + p.z = c.z + (((e0.z * cos(rad)) + (e1.z * sin(rad))) * radiusTop); + glVertex3f(p.x, p.y, p.z); + } + glEnd(); + + // Draw cylinder sides + glBegin(GL_TRIANGLE_STRIP); + glColor4ub(color.r, color.g, color.b, color.a); + for (int i = slices; i >= 0; i--) + { + float rad = angInc * i; + p.x = c.x + (((e0.x * cos(rad)) + (e1.x * sin(rad))) * radiusTop); + p.y = c.y + (((e0.y * cos(rad)) + (e1.y * sin(rad))) * radiusTop) + height; + p.z = c.z + (((e0.z * cos(rad)) + (e1.z * sin(rad))) * radiusTop); + glVertex3f(p.x, p.y, p.z); + + p.x = c.x + (((e0.x * cos(rad)) + (e1.x * sin(rad))) * radiusBottom); + p.y = c.y + (((e0.y * cos(rad)) + (e1.y * sin(rad))) * radiusBottom); + p.z = c.z + (((e0.z * cos(rad)) + (e1.z * sin(rad))) * radiusBottom); + glVertex3f(p.x, p.y, p.z); + } + glEnd(); + + // Draw cylinder bottom + glBegin(GL_TRIANGLE_FAN); + glColor4ub(color.r, color.g, color.b, color.a); + glVertex3f(c.x, c.y, c.z); + for (int i = slices; i >= 0; i--) + { + float rad = angInc * i; + p.x = c.x + (((e0.x * cos(rad)) + (e1.x * sin(rad))) * radiusBottom); + p.y = c.y + (((e0.y * cos(rad)) + (e1.y * sin(rad))) * radiusBottom); + p.z = c.z + (((e0.z * cos(rad)) + (e1.z * sin(rad))) * radiusBottom); + glVertex3f(p.x, p.y, p.z); + } + glEnd(); + + glPopMatrix(); + } + + count += 1; +} + +// Draw a cylinder/cone wires +void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color) +{ + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + DrawCylinder(position, radiusTop, radiusBottom, height, slices, color); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); +} + +// Draw a plane +void DrawPlane(Vector3 centerPos, Vector2 size, Vector3 rotation, Color color) +{ + // NOTE: Plane is always created on XZ ground and then rotated + glPushMatrix(); + glTranslatef(centerPos.x, centerPos.y, centerPos.z); + + // TODO: Review multiples rotations Gimbal-Lock... use matrix or quaternions... + glRotatef(rotation.x, 1, 0, 0); + glRotatef(rotation.y, 0, 1, 0); + glRotatef(rotation.z, 0, 0, 1); + glScalef(size.x, 1.0f, size.y); + + glBegin(GL_QUADS); + glColor4ub(color.r, color.g, color.b, color.a); + glNormal3f(0.0f, 1.0f, 0.0f); + glTexCoord2f(0.0f, 0.0f); glVertex3f(-0.5f, 0.0f, -0.5f); + glTexCoord2f(1.0f, 0.0f); glVertex3f(0.5f, 0.0f, -0.5f); + glTexCoord2f(1.0f, 1.0f); glVertex3f(0.5f, 0.0f, 0.5f); + glTexCoord2f(0.0f, 1.0f); glVertex3f(-0.5f, 0.0f, 0.5f); + glEnd(); + + glPopMatrix(); +} + +// Draw a plane with divisions +void DrawPlaneEx(Vector3 centerPos, Vector2 size, Vector3 rotation, int slicesX, int slicesZ, Color color) +{ + float quadWidth = size.x / slicesX; + float quadLenght = size.y / slicesZ; + + float texPieceW = 1 / size.x; + float texPieceH = 1 / size.y; + + // NOTE: Plane is always created on XZ ground and then rotated + glPushMatrix(); + glTranslatef(-size.x / 2, 0.0f, -size.y / 2); + glTranslatef(centerPos.x, centerPos.y, centerPos.z); + + // TODO: Review multiples rotations Gimbal-Lock... use matrix or quaternions... + glRotatef(rotation.x, 1, 0, 0); + glRotatef(rotation.y, 0, 1, 0); + glRotatef(rotation.z, 0, 0, 1); + + glBegin(GL_QUADS); + glColor4ub(color.r, color.g, color.b, color.a); + glNormal3f(0.0f, 1.0f, 0.0f); + + for (int z = 0; z < slicesZ; z++) + { + for (int x = 0; x < slicesX; x++) + { + // Draw the plane quad by quad (with textcoords) + glTexCoord2f((float)x * texPieceW, (float)z * texPieceH); + glVertex3f((float)x * quadWidth, 0.0f, (float)z * quadLenght); + + glTexCoord2f((float)x * texPieceW + texPieceW, (float)z * texPieceH); + glVertex3f((float)x * quadWidth + quadWidth, 0.0f, (float)z * quadLenght); + + glTexCoord2f((float)x * texPieceW + texPieceW, (float)z * texPieceH + texPieceH); + glVertex3f((float)x * quadWidth + quadWidth, 0.0f, (float)z * quadLenght + quadLenght); + + glTexCoord2f((float)x * texPieceW, (float)z * texPieceH + texPieceH); + glVertex3f((float)x * quadWidth, 0.0f, (float)z * quadLenght + quadLenght); + } + } + glEnd(); + + glPopMatrix(); +} + +// Draw a grid centered at (0, 0, 0) +void DrawGrid(int slices, float spacing) +{ + int halfSlices = slices / 2; + + //glEnable(GL_LINE_SMOOTH); // Smoothies circle outline (anti-aliasing applied) + //glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); // Best quality for line smooth (anti-aliasing best algorithm) + + glPushMatrix(); + glScalef(spacing, 1.0f, spacing); + + glBegin(GL_LINES); + for(int i = -halfSlices; i <= halfSlices; i++) + { + if (i == 0) glColor3f(0.5f, 0.5f, 0.5f); + else glColor3f(0.75f, 0.75f, 0.75f); + + glVertex3f((float)i, 0.0f, (float)-halfSlices); + glVertex3f((float)i, 0.0f, (float)halfSlices); + + glVertex3f((float)-halfSlices, 0.0f, (float)i); + glVertex3f((float)halfSlices, 0.0f, (float)i); + } + glEnd(); + + glPopMatrix(); + + //glDisable(GL_LINE_SMOOTH); +} + +// Draw gizmo (with or without orbits) +void DrawGizmo(Vector3 position, bool orbits) +{ + // NOTE: RGB = XYZ + float lenght = 1.0f; + float radius = 1.0f; + + //glEnable(GL_LINE_SMOOTH); // Smoothies circle outline (anti-aliasing applied) + //glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); // Best quality for line smooth (anti-aliasing best algorithm) + + glPushMatrix(); + glTranslatef(position.x, position.y, position.z); + //glRotatef(rotation, 0, 1, 0); + glScalef(lenght, lenght, lenght); + + glBegin(GL_LINES); + glColor3f(1.0f, 0.0f, 0.0f); + glVertex3f(0.0f, 0.0f, 0.0f); + glVertex3f(1.0f, 0.0f, 0.0f); + + glColor3f(0.0f, 1.0f, 0.0f); + glVertex3f(0.0f, 0.0f, 0.0f); + glVertex3f(0.0f, 1.0f, 0.0f); + + glColor3f(0.0f, 0.0f, 1.0f); + glVertex3f(0.0f, 0.0f, 0.0f); + glVertex3f(0.0f, 0.0f, 1.0f); + glEnd(); + + if (orbits) + { + glBegin(GL_LINE_LOOP); + glColor4f(1.0f, 0.0f, 0.0f, 0.4f); + for (int i=0; i < 360; i++) glVertex3f(sin(DEG2RAD*i) * radius, 0, cos(DEG2RAD*i) * radius); + glEnd(); + + glBegin(GL_LINE_LOOP); + glColor4f(0.0f, 1.0f, 0.0f, 0.4f); + for (int i=0; i < 360; i++) glVertex3f(sin(DEG2RAD*i) * radius, cos(DEG2RAD*i) * radius, 0); + glEnd(); + + glBegin(GL_LINE_LOOP); + glColor4f(0.0f, 0.0f, 1.0f, 0.4f); + for (int i=0; i < 360; i++) glVertex3f(0, sin(DEG2RAD*i) * radius, cos(DEG2RAD*i) * radius); + glEnd(); + } + + glPopMatrix(); + + //glDisable(GL_LINE_SMOOTH); +} + +// Load a 3d model (.OBJ) +// TODO: Add comments explaining this function process +Model LoadModel(const char *fileName) +{ + Model model; + + char dataType; + char comments[200]; + + int numVertex = 0; + int numNormals = 0; + int numTexCoords = 0; + int numTriangles = 0; + + FILE* objfile; + + objfile = fopen(fileName, "rt"); + + while(!feof(objfile)) + { + fscanf(objfile, "%c", &dataType); + + switch(dataType) + { + case '#': // It's a comment + { + fgets(comments, 200, objfile); + } break; + case 'v': + { + fscanf(objfile, "%c", &dataType); + + if (dataType == 't') // Read texCoord + { + fgets(comments, 200, objfile); + fscanf(objfile, "%c", &dataType); + + while (dataType == 'v') + { + fgets(comments, 200, objfile); + fscanf(objfile, "%c", &dataType); + } + + if (dataType == '#') + { + fscanf(objfile, "%i", &numTexCoords); + } + else printf("Ouch! Something was wrong..."); + + fgets(comments, 200, objfile); + } + else if (dataType == 'n') // Read normals + { + fgets(comments, 200, objfile); + fscanf(objfile, "%c", &dataType); + + while (dataType == 'v') + { + fgets(comments, 200, objfile); + fscanf(objfile, "%c", &dataType); + } + + if (dataType == '#') + { + fscanf(objfile, "%i", &numNormals); + } + else printf("Ouch! Something was wrong..."); + + fgets(comments, 200, objfile); + } + else // Read vertex + { + fgets(comments, 200, objfile); + fscanf(objfile, "%c", &dataType); + + while (dataType == 'v') + { + fgets(comments, 200, objfile); + fscanf(objfile, "%c", &dataType); + } + + if (dataType == '#') + { + fscanf(objfile, "%i", &numVertex); + } + else printf("Ouch! Something was wrong..."); + + fgets(comments, 200, objfile); + } + } break; + case 'f': + { + fgets(comments, 200, objfile); + fscanf(objfile, "%c", &dataType); + + while (dataType == 'f') + { + fgets(comments, 200, objfile); + fscanf(objfile, "%c", &dataType); + } + + if (dataType == '#') + { + fscanf(objfile, "%i", &numTriangles); + } + else printf("Ouch! Something was wrong..."); + + fgets(comments, 200, objfile); + + } break; + default: break; + } + } + + Vector3 midVertices[numVertex]; + Vector3 midNormals[numNormals]; + Vector2 midTexCoords[numTexCoords]; + + model.numVertices = numTriangles*3; + + model.vertices = (Vector3 *)malloc(model.numVertices * sizeof(Vector3)); + model.normals = (Vector3 *)malloc(model.numVertices * sizeof(Vector3)); + model.texcoords = (Vector2 *)malloc(model.numVertices * sizeof(Vector2)); + + int countVertex = 0; + int countNormals = 0; + int countTexCoords = 0; + + int countMaxVertex = 0; + + rewind(objfile); + + while(!feof(objfile)) + { + fscanf(objfile, "%c", &dataType); + + switch(dataType) + { + case '#': + { + fgets(comments, 200, objfile); + } break; + case 'v': + { + fscanf(objfile, "%c", &dataType); + + if (dataType == 't') // Read texCoord + { + float useless = 0; + + fscanf(objfile, "%f %f %f", &midTexCoords[countTexCoords].x, &midTexCoords[countTexCoords].y, &useless); + countTexCoords++; + + fscanf(objfile, "%c", &dataType); + } + else if (dataType == 'n') // Read normals + { + fscanf(objfile, "%f %f %f", &midNormals[countNormals].x, &midNormals[countNormals].y, &midNormals[countNormals].z ); + countNormals++; + + fscanf(objfile, "%c", &dataType); + } + else // Read vertex + { + fscanf(objfile, "%f %f %f", &midVertices[countVertex].x, &midVertices[countVertex].y, &midVertices[countVertex].z ); + countVertex++; + + fscanf(objfile, "%c", &dataType); + } + } break; + case 'f': + { + int vNum, vtNum, vnNum; + fscanf(objfile, "%c", &dataType); + fscanf(objfile, "%i/%i/%i", &vNum, &vtNum, &vnNum); + + model.vertices[countMaxVertex] = midVertices[vNum-1]; + model.normals[countMaxVertex] = midNormals[vnNum-1]; + model.texcoords[countMaxVertex].x = midTexCoords[vtNum-1].x; + model.texcoords[countMaxVertex].y = -midTexCoords[vtNum-1].y; + countMaxVertex++; + + fscanf(objfile, "%i/%i/%i", &vNum, &vtNum, &vnNum); + + model.vertices[countMaxVertex] = midVertices[vNum-1]; + model.normals[countMaxVertex] = midNormals[vnNum-1]; + model.texcoords[countMaxVertex].x = midTexCoords[vtNum-1].x; + model.texcoords[countMaxVertex].y = -midTexCoords[vtNum-1].y; + countMaxVertex++; + + fscanf(objfile, "%i/%i/%i", &vNum, &vtNum, &vnNum); + + model.vertices[countMaxVertex] = midVertices[vNum-1]; + model.normals[countMaxVertex] = midNormals[vnNum-1]; + model.texcoords[countMaxVertex].x = midTexCoords[vtNum-1].x; + model.texcoords[countMaxVertex].y = -midTexCoords[vtNum-1].y; + countMaxVertex++; + } break; + default: break; + } + } + + fclose(objfile); + + return model; +} + +// Unload 3d model from memory +void UnloadModel(Model model) +{ + free(model.vertices); + free(model.texcoords); + free(model.normals); +} + +// Draw a model +void DrawModel(Model model, Vector3 position, float scale, Color color) +{ + // NOTE: For models we use Vertex Arrays (OpenGL 1.1) + static float rotation = 0; + + glEnableClientState(GL_VERTEX_ARRAY); // Enable vertex array + glEnableClientState(GL_TEXTURE_COORD_ARRAY); // Enable texture coords array + glEnableClientState(GL_NORMAL_ARRAY); // Enable normals array + + glVertexPointer(3, GL_FLOAT, 0, model.vertices); // Pointer to vertex coords array + glTexCoordPointer(2, GL_FLOAT, 0, model.texcoords); // Pointer to texture coords array + glNormalPointer(GL_FLOAT, 0, model.normals); // Pointer to normals array + //glColorPointer(4, GL_UNSIGNED_BYTE, 0, model.colors); // Pointer to colors array (NOT USED) + + glPushMatrix(); + glTranslatef(position.x, position.y, position.z); + glRotatef(rotation * GetFrameTime(), 0, 1, 0); + glScalef(scale, scale, scale); + + glColor4ub(color.r, color.g, color.b, color.a); + + glDrawArrays(GL_TRIANGLES, 0, model.numVertices); + glPopMatrix(); + + glDisableClientState(GL_VERTEX_ARRAY); // Disable vertex array + glDisableClientState(GL_TEXTURE_COORD_ARRAY); // Disable texture coords array + glDisableClientState(GL_NORMAL_ARRAY); // Disable normals array + + rotation += 10; +} + +// Draw a textured model +void DrawModelEx(Model model, Texture2D texture, Vector3 position, float scale, Color tint) +{ + glEnable(GL_TEXTURE_2D); + + glBindTexture(GL_TEXTURE_2D, texture.glId); + + DrawModel(model, position, scale, tint); + + glDisable(GL_TEXTURE_2D); +} + +// Draw a model wires +void DrawModelWires(Model model, Vector3 position, float scale, Color color) +{ + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + DrawModel(model, position, scale, color); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); +} + +// Draw a billboard +void DrawBillboard(Camera camera, Texture2D texture, Vector3 basePos, float size, Color tint) +{ + // NOTE: Billboard size will represent the width, height maintains aspect ratio + Vector3 centerPos = { basePos.x, basePos.y + size * (float)texture.height/(float)texture.width/2, basePos.z }; + Vector2 sizeRatio = { size, size * (float)texture.height/texture.width }; + Vector3 rotation = { 90, 0, 0 }; + + // TODO: Calculate Y rotation to face always camera (use matrix) + // OPTION: Lock Y-axis + + glEnable(GL_TEXTURE_2D); + + glBindTexture(GL_TEXTURE_2D, texture.glId); + + DrawPlane(centerPos, sizeRatio, rotation, tint); // TODO: Review this function... + + glDisable(GL_TEXTURE_2D); +} + +// Draw a billboard (part of a texture defined by a rectangle) +void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle sourceRec, Vector3 basePos, float size, Color tint) +{ + // NOTE: Billboard size will represent the width, height maintains aspect ratio + Vector3 centerPos = { basePos.x, basePos.y + size * (float)texture.height/(float)texture.width/2, basePos.z }; + Vector2 sizeRatio = { size, size * (float)texture.height/texture.width }; + Vector3 rotation = { 90, 0, 0 }; + + // TODO: Calculate Y rotation to face always camera (use matrix) + // OPTION: Lock Y-axis + + glEnable(GL_TEXTURE_2D); + + glBindTexture(GL_TEXTURE_2D, texture.glId); + + // TODO: DrawPlane with correct textcoords for source rec. + + glDisable(GL_TEXTURE_2D); +} + +// Draw a heightmap using a provided image data +void DrawHeightmap(Image heightmap, Vector3 centerPos, Vector3 scale, Color color) +{ + // NOTE: Pixel-data is interpreted as grey-scale (even being a color image) + // NOTE: Heightmap resolution will depend on image size (one quad per pixel) + + // TODO: Review how this function works... probably we need: + // Model LoadHeightmap(Image image, Vector3 resolution); + + // NOTE: We are allocating and de-allocating vertex data every frame! --> framerate drops 80%! CRAZY! + Vector3 *terrainVertex = (Vector3 *)malloc(heightmap.width * heightmap.height * sizeof(Vector3)); + + for (int z = 0; z < heightmap.height; z++) + { + for (int x = 0; x < heightmap.width; x++) + { + terrainVertex[z*heightmap.height + x].x = (float)(x*scale.x); + terrainVertex[z*heightmap.height + x].y = ((float)heightmap.pixels[z*heightmap.height + x].r + + (float)heightmap.pixels[z*heightmap.height + x].g + + (float)heightmap.pixels[z*heightmap.height + x].b) / 3 * scale.y; + terrainVertex[z*heightmap.height + x].z = (float)(-z*scale.z); + } + } + + // TODO: Texture coordinates and normals computing + + for (int z = 0; z < heightmap.height-1; z++) + { + glBegin(GL_TRIANGLE_STRIP); + for (int x = 0; x < heightmap.width; x++) + { + glColor3f((float)heightmap.pixels[z*heightmap.height + x].r / 255.0f, + (float)heightmap.pixels[z*heightmap.height + x].g / 255.0f, + (float)heightmap.pixels[z*heightmap.height + x].b / 255.0f); + + glVertex3f(terrainVertex[z*heightmap.height + x].x, terrainVertex[z*heightmap.height + x].y, terrainVertex[z*heightmap.height + x].z); + glVertex3f(terrainVertex[(z+1)*heightmap.height + x].x, terrainVertex[(z+1)*heightmap.height + x].y, terrainVertex[(z+1)*heightmap.height + x].z); + } + glEnd(); + } + + free(terrainVertex); +} + +void DrawHeightmapEx(Image heightmap, Texture2D texture, Vector3 centerPos, Vector3 scale, Color tint) +{ + glEnable(GL_TEXTURE_2D); + + glBindTexture(GL_TEXTURE_2D, texture.glId); + + // NOTE: No texture coordinates or normals defined at this moment... + DrawHeightmap(heightmap, centerPos, scale, tint); + + glDisable(GL_TEXTURE_2D); +} \ No newline at end of file diff --git a/src/raylib.h b/src/raylib.h new file mode 100644 index 000000000..fc9f5a5aa --- /dev/null +++ b/src/raylib.h @@ -0,0 +1,376 @@ +/********************************************************************************************* +* +* raylib 1.0.0 (www.raylib.com) +* +* A simple and easy-to-use library to learn C videogames programming +* +* Features: +* Library written in plain C code (C99) +* Uses C# PascalCase/camelCase notation +* Hardware accelerated with OpenGL 1.1 +* Powerful fonts module with SpriteFonts support +* Basic 3d support for Shapes and Models +* Audio loading and playing +* +* Used external libs: +* GLFW3 (www.glfw.org) for window/context management and input +* stb_image (Sean Barret) for images loading (JPEG, PNG, BMP, TGA, PSD, GIF, HDR, PIC) +* OpenAL Soft for audio device/context management +* +* Some design decisions: +* 32bit Colors - All defined color are always RGBA +* 32bit Textures - All loaded images are converted automatically to RGBA textures +* SpriteFonts - All loaded sprite-font images are converted to RGBA and POT textures +* One custom default font is loaded automatically when InitWindow() +* +* -- LICENSE (raylib v1.0, November 2013) -- +* +* raylib is licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software: +* +* Copyright (c) 2013 Ramon Santamaria (Ray San - raysan@raysanweb.com) +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#ifndef RAYLIB_H +#define RAYLIB_H + +//#define NO_AUDIO // Audio is still being tested, deactivated by default + +//---------------------------------------------------------------------------------- +// Some basic Defines +//---------------------------------------------------------------------------------- +#ifndef PI +#define PI 3.14159265358979323846 +#endif + +#define DEG2RAD (PI / 180.0) +#define RAD2DEG (180.0 / PI) + +// Keyboard Function Keys +#define KEY_SPACE 32 +#define KEY_ESCAPE 256 +#define KEY_ENTER 257 +#define KEY_RIGHT 262 +#define KEY_LEFT 263 +#define KEY_DOWN 264 +#define KEY_UP 265 +#define KEY_F1 290 +#define KEY_F2 291 +#define KEY_F3 292 +#define KEY_F4 293 +#define KEY_F5 294 +#define KEY_F6 295 +#define KEY_F7 296 +#define KEY_F8 297 +#define KEY_F9 298 +#define KEY_F10 299 +#define KEY_LEFT_SHIFT 340 +#define KEY_LEFT_CONTROL 341 +#define KEY_LEFT_ALT 342 +#define KEY_RIGHT_SHIFT 344 +#define KEY_RIGHT_CONTROL 345 +#define KEY_RIGHT_ALT 346 + +// Mouse Buttons +#define MOUSE_LEFT_BUTTON 0 +#define MOUSE_RIGHT_BUTTON 1 +#define MOUSE_MIDDLE_BUTTON 2 + +// Gamepad Number +#define GAMEPAD_PLAYER1 0 +#define GAMEPAD_PLAYER2 1 +#define GAMEPAD_PLAYER3 2 +#define GAMEPAD_PLAYER4 3 + +// Gamepad Buttons +// NOTE: Adjusted for a PS3 USB Controller +#define GAMEPAD_BUTTON_A 2 +#define GAMEPAD_BUTTON_B 1 +#define GAMEPAD_BUTTON_X 3 +#define GAMEPAD_BUTTON_Y 4 +#define GAMEPAD_BUTTON_R1 7 +#define GAMEPAD_BUTTON_R2 5 +#define GAMEPAD_BUTTON_L1 6 +#define GAMEPAD_BUTTON_L2 8 +#define GAMEPAD_BUTTON_SELECT 9 +#define GAMEPAD_BUTTON_START 10 + +// TODO: Review Xbox360 USB Controller Buttons + +// Some Basic Colors +// NOTE: Custom raylib color palette for amazing visuals on WHITE background +#define LIGHTGRAY (Color){ 200, 200, 200, 255 } // Light Gray +#define GRAY (Color){ 130, 130, 130, 255 } // Gray +#define DARKGRAY (Color){ 80, 80, 80, 255 } // Dark Gray +#define YELLOW (Color){ 253, 249, 0, 255 } // Yellow +#define GOLD (Color){ 255, 203, 0, 255 } // Gold +#define ORANGE (Color){ 255, 161, 0, 255 } // Orange +#define PINK (Color){ 255, 109, 194, 255 } // Pink +#define RED (Color){ 230, 41, 55, 255 } // Red +#define MAROON (Color){ 190, 33, 55, 255 } // Maroon +#define GREEN (Color){ 0, 228, 48, 255 } // Green +#define LIME (Color){ 0, 158, 47, 255 } // Lime +#define DARKGREEN (Color){ 0, 117, 44, 255 } // Dark Green +#define SKYBLUE (Color){ 102, 191, 255, 255 } // Sky Blue +#define BLUE (Color){ 0, 121, 241, 255 } // Blue +#define DARKBLUE (Color){ 0, 82, 172, 255 } // Dark Blue +#define PURPLE (Color){ 200, 122, 255, 255 } // Purple +#define VIOLET (Color){ 135, 60, 190, 255 } // Violet +#define DARKPURPLE (Color){ 112, 31, 126, 255 } // Dark Purple +#define BEIGE (Color){ 211, 176, 131, 255 } // Beige +#define BROWN (Color){ 127, 106, 79, 255 } // Brown +#define DARKBROWN (Color){ 76, 63, 47, 255 } // Dark Brown + +#define WHITE (Color){ 255, 255, 255, 255 } // White +#define BLACK (Color){ 0, 0, 0, 255 } // Black +#define BLANK (Color){ 0, 0, 0, 0 } // Blank (Transparent) +#define MAGENTA (Color){ 255, 0, 255, 255 } // Magenta +#define RAYWHITE (Color){ 245, 245, 245, 255 } // My own White (raylib logo) + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- + +// Boolean type +typedef enum { false, true } bool; + +// Color type, RGBA (32bit) +typedef struct Color { + unsigned char r; + unsigned char g; + unsigned char b; + unsigned char a; +} Color; + +// Rectangle type +typedef struct Rectangle { + int x; + int y; + int width; + int height; +} Rectangle; + +// Image type, bpp always RGBA (32bit) +// NOTE: Data stored in CPU memory (RAM) +typedef struct Image { + Color *pixels; + int width; + int height; +} Image; + +// Texture2D type, bpp always RGBA (32bit) +// NOTE: Data stored in GPU memory +typedef struct Texture2D { + unsigned int glId; + int width; + int height; +} Texture2D; + +// SpriteFont one Character (Glyph) data, defined in text module +typedef struct Character Character; + +// SpriteFont type, includes texture and charSet array data +typedef struct SpriteFont { + Texture2D texture; + int numChars; + Character *charSet; +} SpriteFont; + +// Vector2 type +typedef struct Vector2 { + float x; + float y; +} Vector2; + +// Vector3 type +typedef struct Vector3 { + float x; + float y; + float z; +} Vector3; + +// Camera type, defines a camera position/orientation in 3d space +typedef struct Camera { + Vector3 position; + Vector3 target; + Vector3 up; +} Camera; + +// Basic 3d Model type +typedef struct Model { + int numVertices; + Vector3 *vertices; + Vector2 *texcoords; + Vector3 *normals; +} Model; + +// Basic Sound source and buffer +typedef struct Sound { + unsigned int source; + unsigned int buffer; +} Sound; + +#ifdef __cplusplus +extern "C" { // Prevents name mangling of functions +#endif + +//------------------------------------------------------------------------------------ +// Global Variables Definition +//------------------------------------------------------------------------------------ +// It's lonely here... + +//------------------------------------------------------------------------------------ +// Window and Graphics Device Functions (Module: core) +//------------------------------------------------------------------------------------ +void InitWindow(int width, int height, char* title); // Initialize Window and Graphics Context (OpenGL) +void CloseWindow(); // Close Window and Terminate Context +bool WindowShouldClose(); // Detect if KEY_ESCAPE pressed or Close icon pressed +void ToggleFullscreen(); // Fullscreen toggle (by default F11) + +void ClearBackground(Color color); // Sets Background Color +void BeginDrawing(); // Setup drawing canvas to start drawing +void EndDrawing(); // End canvas drawing and Swap Buffers (Double Buffering) + +void Begin3dMode(Camera cam); // Initializes 3D mode for drawing (Camera setup) +void End3dMode(); // Ends 3D mode and returns to default 2D orthographic mode + +void SetTargetFPS(int fps); // Set target FPS (maximum) +float GetFPS(); // Returns current FPS +float GetFrameTime(); // Returns time in seconds for one frame + +Color GetColor(int hexValue); // Returns a Color struct from hexadecimal value +int GetHexValue(Color color); // Returns hexadecimal value for a Color + +//------------------------------------------------------------------------------------ +// Input Handling Functions (Module: core) +//------------------------------------------------------------------------------------ +bool IsKeyPressed(int key); // Detect if a key is being pressed +bool IsKeyReleased(int key); // Detect if a key is NOT being pressed + +bool IsMouseButtonPressed(int button); // Detect if a mouse button is being pressed +bool IsMouseButtonReleased(int button); // Detect if a mouse button is NOT being pressed +int GetMouseX(); // Returns mouse position X +int GetMouseY(); // Returns mouse position Y +Vector2 GetMousePosition(); // Returns mouse position XY + +bool IsGamepadAvailable(int gamepad); // Detect if a gamepad is available +Vector2 GetGamepadMovement(int gamepad); // Return axis movement vector for a gamepad +bool IsGamepadButtonPressed(int gamepad, int button); // Detect if a gamepad button is being pressed +bool IsGamepadButtonReleased(int gamepad, int button); // Detect if a gamepad button is NOT being pressed + +//------------------------------------------------------------------------------------ +// Basic Shapes Drawing Functions (Module: shapes) +//------------------------------------------------------------------------------------ +void DrawPixel(int posX, int posY, Color color); // Draw a pixel +void DrawPixelV(Vector2 position, Color color); // Draw a pixel (Vector version) +void DrawLine(int startPosX, int startPosY, int endPosX, int endPosY, Color color); // Draw a line +void DrawLineV(Vector2 startPos, Vector2 endPos, Color color); // Draw a line (Vector version) +void DrawCircle(int centerX, int centerY, float radius, Color color); // Draw a color-filled circle +void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2); // Draw a gradient-filled circle +void DrawCircleV(Vector2 center, float radius, Color color); // Draw a color-filled circle (Vector version) +void DrawCircleLines(int centerX, int centerY, float radius, Color color); // Draw circle outline +void DrawRectangle(int posX, int posY, int width, int height, Color color); // Draw a color-filled rectangle +void DrawRectangleRec(Rectangle rec, Color color); // Draw a color-filled rectangle +void DrawRectangleGradient(int posX, int posY, int width, int height, Color color1, Color color2); // Draw a gradient-filled rectangle +void DrawRectangleV(Vector2 position, Vector2 size, Color color); // Draw a color-filled rectangle (Vector version) +void DrawRectangleLines(int posX, int posY, int width, int height, Color color); // Draw rectangle outline +void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw a color-filled triangle +void DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw triangle outline +void DrawPoly(Vector2 *points, int numPoints, Color color); // Draw a closed polygon defined by points +void DrawPolyLine(Vector2 *points, int numPoints, Color color); // Draw polygon lines + +//------------------------------------------------------------------------------------ +// Texture Loading and Drawing Functions (Module: textures) +//------------------------------------------------------------------------------------ +Image LoadImage(const char *fileName); // Load an image into CPU memory (RAM) +void UnloadImage(Image image); // Unload image from CPU memory (RAM) +Texture2D LoadTexture(const char *fileName); // Load an image as texture into GPU memory +//Texture2D LoadTextureEx(const char *fileName, bool createPOT, bool mipmaps); // Load an image as texture (and convert to POT with mipmaps) (raylib 1.x) +void UnloadTexture(Texture2D texture); // Unload texture from GPU memory +void DrawTexture(Texture2D texture, int posX, int posY, Color tint); // Draw a Texture2D +void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float scale, Color tint); // Draw a Texture2D with extended parameters +void DrawTextureRec(Texture2D texture, Rectangle sourceRec, Vector2 position, float scale, Color tint); // Draw a part of a texture defined by a rectangle + +//------------------------------------------------------------------------------------ +// Font Loading and Text Drawing Functions (Module: text) +//------------------------------------------------------------------------------------ +SpriteFont LoadSpriteFont(const char *fileName); // Load a SpriteFont image into GPU memory +void UnloadSpriteFont(SpriteFont spriteFont); // Unload SpriteFont from GPU memory +void DrawText(const char *text, int posX, int posY, int fontSize, int spacing, Color color); // Draw text (using default font) +void DrawTextEx(SpriteFont spriteFont, const char* text, Vector2 position, int fontSize, int spacing, Color tint); // Draw text using SpriteFont +int MeasureText(const char *text, int fontSize, int spacing); // Measure string width for default font +Vector2 MeasureTextEx(SpriteFont spriteFont, const char *text, int fontSize, int spacing); // Measure string size for SpriteFont +int GetFontBaseSize(SpriteFont spriteFont); // Returns the base size for a SpriteFont (chars height) +void DrawFps(int posX, int posY); // Shows current FPS on top-left corner +const char *FormatText(const char *text, ...); // Formatting of text with variables to 'embed' + +//------------------------------------------------------------------------------------ +// Basic 3d Shapes Drawing Functions (Module: models) +//------------------------------------------------------------------------------------ +void DrawCube(Vector3 position, float width, float height, float lenght, Color color); // Draw cube +void DrawCubeV(Vector3 position, Vector3 size, Color color); // Draw cube (Vector version) +void DrawCubeWires(Vector3 position, float width, float height, float lenght, Color color); // Draw cube wires +void DrawSphere(Vector3 centerPos, float radius, Color color); // Draw sphere +void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color color); // Draw sphere with extended parameters +void DrawSphereWires(Vector3 centerPos, float radius, Color color); // Draw sphere wires +void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color); // Draw a cylinder/cone +void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, float height, int slices, Color color); // Draw a cylinder/cone wires +void DrawPlane(Vector3 centerPos, Vector2 size, Vector3 rotation, Color color); // Draw a plane +void DrawPlaneEx(Vector3 centerPos, Vector2 size, Vector3 rotation, int slicesX, int slicesZ, Color color); // Draw a plane with divisions +void DrawGrid(int slices, float spacing); // Draw a grid (centered at (0, 0, 0)) +void DrawGizmo(Vector3 position, bool orbits); // Draw gizmo (with or without orbits) +//DrawTorus(), DrawTeapot() are useless... + +//------------------------------------------------------------------------------------ +// Model 3d Loading and Drawing Functions (Module: models) +//------------------------------------------------------------------------------------ +Model LoadModel(const char *fileName); // Load a 3d model (.OBJ) +void UnloadModel(Model model); // Unload 3d model from memory +void DrawModel(Model model, Vector3 position, float scale, Color color); // Draw a model +void DrawModelEx(Model model, Texture2D texture, Vector3 position, float scale, Color tint); // Draw a textured model +void DrawModelWires(Model model, Vector3 position, float scale, Color color); // Draw a model wires + +// NOTE: The following functions work but are incomplete or require some revision +// DrawHeightmap is extremely inefficient and can impact performance up to 60% +void DrawBillboard(Camera camera, Texture2D texture, Vector3 basePos, float size, Color tint); // REVIEW: Draw a billboard (raylib 1.x) +void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle sourceRec, Vector3 basePos, float size, Color tint); // REVIEW: Draw a billboard (raylib 1.x) +void DrawHeightmap(Image heightmap, Vector3 centerPos, Vector3 scale, Color color); // REVIEW: Draw heightmap using image map (raylib 1.x) +void DrawHeightmapEx(Image heightmap, Texture2D texture, Vector3 centerPos, Vector3 scale, Color tint); // REVIEW: Draw textured heightmap (raylib 1.x) + +#ifndef NO_AUDIO + +//------------------------------------------------------------------------------------ +// Audio Loading and Playing Functions (Module: audio) +//------------------------------------------------------------------------------------ +void InitAudioDevice(); // Initialize audio device and context +void CloseAudioDevice(); // Close the audio device and context +Sound LoadSound(char *fileName); // Load sound to memory +void UnloadSound(Sound sound); // Unload sound +void PlaySound(Sound sound); // Play a sound +void PlaySoundEx(Sound sound, float timePosition, bool loop); // Play a sound with extended parameters +void PauseSound(Sound sound); // Pause a sound +void StopSound(Sound sound); // Stop playing a sound + +#endif // NO_AUDIO + +#ifdef __cplusplus +} +#endif + +#endif // RAYLIB_H \ No newline at end of file diff --git a/src/shapes.c b/src/shapes.c new file mode 100644 index 000000000..705cd3ab2 --- /dev/null +++ b/src/shapes.c @@ -0,0 +1,326 @@ +/********************************************************************************************* +* +* raylib.shapes +* +* Basic functions to draw 2d Shapes +* +* Copyright (c) 2013 Ramon Santamaria (Ray San - raysan@raysanweb.com) +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#include "raylib.h" + +#include // OpenGL functions +#include // Math related functions, sin() and cos() used on DrawCircle* + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +// Nop... + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +// Not here... + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +// It's lonely here... + +//---------------------------------------------------------------------------------- +// Module specific Functions Declaration +//---------------------------------------------------------------------------------- +// No private (static) functions in this module (.c file) + +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- + +// Draw a pixel +void DrawPixel(int posX, int posY, Color color) +{ + glBegin(GL_POINTS); + glColor4ub(color.r, color.g, color.b, color.a); + glVertex2i(posX, posY); + glEnd(); + + // NOTE: Alternative method to draw a pixel (point) +/* + glEnable(GL_POINT_SMOOTH); + glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); // Deprecated on OGL 3.0 + + glPointSize(1.0f); + glPoint((float)posX, (float)posY, 0.0f); +*/ +} + +// Draw a pixel (Vector version) +void DrawPixelV(Vector2 position, Color color) +{ + glBegin(GL_POINTS); + glColor4ub(color.r, color.g, color.b, color.a); + glVertex2f(position.x, position.y); + glEnd(); +} + +// Draw a line +void DrawLine(int startPosX, int startPosY, int endPosX, int endPosY, Color color) +{ + glBegin(GL_LINES); + glColor4ub(color.r, color.g, color.b, color.a); + glVertex2i(startPosX, startPosY); + glVertex2i(endPosX, endPosY); + glEnd(); +} + +// Draw a line (Vector version) +void DrawLineV(Vector2 startPos, Vector2 endPos, Color color) +{ + glBegin(GL_LINES); + glColor4ub(color.r, color.g, color.b, color.a); + glVertex2f(startPos.x, startPos.y); + glVertex2f(endPos.x, endPos.y); + glEnd(); +} + +// Draw a color-filled circle +void DrawCircle(int centerX, int centerY, float radius, Color color) +{ + glEnable(GL_POLYGON_SMOOTH); + glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST); + + glBegin(GL_TRIANGLE_FAN); + glColor4ub(color.r, color.g, color.b, color.a); + glVertex2i(centerX, centerY); + + for (int i=0; i <= 360; i++) //i++ --> Step = 1.0 pixels + { + float degInRad = i*DEG2RAD; + //glVertex2f(cos(degInRad)*radius,sin(degInRad)*radius); + + glVertex2f(centerX + sin(degInRad) * radius, centerY + cos(degInRad) * radius); + } + glEnd(); + + glDisable(GL_POLYGON_SMOOTH); + + // NOTE: Alternative method to draw a circle (point) +/* + glEnable(GL_POINT_SMOOTH); + glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); // Deprecated on OGL 3.0 + + glPointSize(radius); + glPoint((float)centerX, (float)centerY, 0.0f); +*/ +} + +// Draw a gradient-filled circle +// NOTE: Gradient goes from center (color1) to border (color2) +void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2) +{ + glBegin(GL_TRIANGLE_FAN); + glColor4ub(color1.r, color1.g, color1.b, color1.a); + glVertex2i(centerX, centerY); + glColor4ub(color2.r, color2.g, color2.b, color2.a); + + for (int i=0; i <= 360; i++) //i++ --> Step = 1.0 pixels + { + glVertex2f(centerX + sin(DEG2RAD*i) * radius, centerY + cos(DEG2RAD*i) * radius); + } + glEnd(); +} + +// Draw a color-filled circle (Vector version) +void DrawCircleV(Vector2 center, float radius, Color color) +{ + glEnable(GL_POLYGON_SMOOTH); + glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST); + + glBegin(GL_TRIANGLE_FAN); + glColor4ub(color.r, color.g, color.b, color.a); + glVertex2f(center.x, center.y); + + for (int i=0; i <= 360; i++) //i++ --> Step = 1.0 pixels + { + glVertex2f(center.x + sin(DEG2RAD*i) * radius, center.y + cos(DEG2RAD*i) * radius); + } + glEnd(); + + glDisable(GL_POLYGON_SMOOTH); +} + +// Draw circle outline +void DrawCircleLines(int centerX, int centerY, float radius, Color color) +{ + glEnable(GL_LINE_SMOOTH); // Smoothies circle outline (anti-aliasing applied) + glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); // Best quality for line smooth (anti-aliasing best algorithm) + + glBegin(GL_LINE_LOOP); + glColor4ub(color.r, color.g, color.b, color.a); + + // NOTE: Circle outline is drawn pixel by pixel every degree (0 to 360) + for (int i=0; i < 360; i++) + { + glVertex2f(centerX + sin(DEG2RAD*i) * radius, centerY + cos(DEG2RAD*i) * radius); + } + glEnd(); + +// NOTE: Alternative method to draw circle outline +/* + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + DrawCircle(centerX, centerY, radius, color); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); +*/ + glDisable(GL_LINE_SMOOTH); +} + +// Draw a color-filled rectangle +void DrawRectangle(int posX, int posY, int width, int height, Color color) +{ + glBegin(GL_QUADS); + glColor4ub(color.r, color.g, color.b, color.a); + glVertex2i(posX, posY); + glVertex2i(posX + width, posY); + glVertex2i(posX + width, posY + height); + glVertex2i(posX, posY + height); + glEnd(); +} + +// Draw a color-filled rectangle +void DrawRectangleRec(Rectangle rec, Color color) +{ + DrawRectangle(rec.x, rec.y, rec.width, rec.height, color); +} + +// Draw a gradient-filled rectangle +// NOTE: Gradient goes from bottom (color1) to top (color2) +void DrawRectangleGradient(int posX, int posY, int width, int height, Color color1, Color color2) +{ + glBegin(GL_QUADS); + glColor4ub(color1.r, color1.g, color1.b, color1.a); + glVertex2i(posX, posY); + glVertex2i(posX + width, posY); + glColor4ub(color2.r, color2.g, color2.b, color2.a); + glVertex2i(posX + width, posY + height); + glVertex2i(posX, posY + height); + glEnd(); +} + +// Draw a color-filled rectangle (Vector version) +void DrawRectangleV(Vector2 position, Vector2 size, Color color) +{ + glBegin(GL_QUADS); + glColor4ub(color.r, color.g, color.b, color.a); + glVertex2i(position.x, position.y); + glVertex2i(position.x + size.x, position.y); + glVertex2i(position.x + size.x, position.y + size.y); + glVertex2i(position.x, position.y + size.y); + glEnd(); +} + +// Draw rectangle outline +void DrawRectangleLines(int posX, int posY, int width, int height, Color color) +{ + //glEnable(GL_LINE_SMOOTH); // Smoothies circle outline (anti-aliasing applied) + //glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); // Best quality for line smooth (anti-aliasing best algorithm) + + // NOTE: Lines are rasterized using the "Diamond Exit" rule so, it's nearly impossible to obtain a pixel-perfect engine + // NOTE: Recommended trying to avoid using lines, at least >1.0f pixel lines with anti-aliasing (glLineWidth function) + + glBegin(GL_LINE_LOOP); + glColor4ub(color.r, color.g, color.b, color.a); + glVertex2i(posX, posY); + glVertex2i(posX + width - 1, posY); + glVertex2i(posX + width - 1, posY + height - 1); + glVertex2i(posX, posY + height - 1); + glEnd(); + +// NOTE: Alternative method to draw rectangle outline +/* + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + DrawRectangle(posX, posY, width - 1, height - 1, color); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); +*/ + //glDisable(GL_LINE_SMOOTH); +} + +// Draw a triangle +void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color) +{ + glBegin(GL_TRIANGLES); + glColor4ub(color.r, color.g, color.b, color.a); + glVertex2f(v1.x, v1.y); + glVertex2f(v2.x, v2.y); + glVertex2f(v3.x, v3.y); + glEnd(); +} + +void DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, Color color) +{ + glBegin(GL_LINE_LOOP); + glColor4ub(color.r, color.g, color.b, color.a); + glVertex2f(v1.x, v1.y); + glVertex2f(v2.x, v2.y); + glVertex2f(v3.x, v3.y); + glEnd(); +} + +// Draw a closed polygon defined by points +// NOTE: Array num elements MUST be passed as parameter to function +void DrawPoly(Vector2 *points, int numPoints, Color color) +{ + if (numPoints >= 3) + { + glEnable(GL_POLYGON_SMOOTH); + glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST); + + glBegin(GL_POLYGON); + glColor4ub(color.r, color.g, color.b, color.a); + + for (int i = 0; i < numPoints; i++) + { + glVertex2f(points[i].x, points[i].y); + } + glEnd(); + + glDisable(GL_POLYGON_SMOOTH); + } +} + +// Draw polygon lines +// NOTE: Array num elements MUST be passed as parameter to function +void DrawPolyLine(Vector2 *points, int numPoints, Color color) +{ + if (numPoints >= 2) + { + //glEnable(GL_LINE_SMOOTH); // Smoothies circle outline (anti-aliasing applied) + //glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); // Best quality for line smooth (anti-aliasing best algorithm) + + glBegin(GL_LINE_LOOP); + glColor4ub(color.r, color.g, color.b, color.a); + + for (int i = 0; i < numPoints; i++) + { + glVertex2f(points[i].x, points[i].y); + } + glEnd(); + + //glDisable(GL_LINE_SMOOTH); + } +} diff --git a/src/stb_image.c b/src/stb_image.c new file mode 100644 index 000000000..b11e44ffd --- /dev/null +++ b/src/stb_image.c @@ -0,0 +1,4341 @@ +#include "stb_image.h" + +#ifndef STBI_HEADER_FILE_ONLY + +#ifndef STBI_NO_HDR +#include // ldexp +#include // strcmp, strtok +#endif + +#ifndef STBI_NO_STDIO +#include +#endif +#include +#include +#include +#include + +#ifndef _MSC_VER + #ifdef __cplusplus + #define stbi_inline inline + #else + #define stbi_inline + #endif +#else + #define stbi_inline __forceinline +#endif + +// implementation: +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef signed short int16; +typedef unsigned int uint32; +typedef signed int int32; +typedef unsigned int uint; + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(uint32)==4 ? 1 : -1]; + +#if defined(STBI_NO_STDIO) && !defined(STBI_NO_WRITE) +#define STBI_NO_WRITE +#endif + +#define STBI_NOTUSED(v) (void)sizeof(v) + +#ifdef _MSC_VER +#define STBI_HAS_LROTL +#endif + +#ifdef STBI_HAS_LROTL + #define stbi_lrot(x,y) _lrotl(x,y) +#else + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) +#endif + +/////////////////////////////////////////////// +// +// stbi struct and start_xxx functions + +// stbi structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information +typedef struct +{ + uint32 img_x, img_y; + int img_n, img_out_n; + + stbi_io_callbacks io; + void *io_user_data; + + int read_from_callbacks; + int buflen; + uint8 buffer_start[128]; + + uint8 *img_buffer, *img_buffer_end; + uint8 *img_buffer_original; +} stbi; + + +static void refill_buffer(stbi *s); + +// initialize a memory-decode context +static void start_mem(stbi *s, uint8 const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->img_buffer = s->img_buffer_original = (uint8 *) buffer; + s->img_buffer_end = (uint8 *) buffer+len; +} + +// initialize a callback-based context +static void start_callbacks(stbi *s, stbi_io_callbacks *c, void *user) +{ + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->img_buffer_original = s->buffer_start; + refill_buffer(s); +} + +#ifndef STBI_NO_STDIO + +static int stdio_read(void *user, char *data, int size) +{ + return (int) fread(data,1,size,(FILE*) user); +} + +static void stdio_skip(void *user, unsigned n) +{ + fseek((FILE*) user, n, SEEK_CUR); +} + +static int stdio_eof(void *user) +{ + return feof((FILE*) user); +} + +static stbi_io_callbacks stbi_stdio_callbacks = +{ + stdio_read, + stdio_skip, + stdio_eof, +}; + +static void start_file(stbi *s, FILE *f) +{ + start_callbacks(s, &stbi_stdio_callbacks, (void *) f); +} + +//static void stop_file(stbi *s) { } + +#endif // !STBI_NO_STDIO + +static void stbi_rewind(stbi *s) +{ + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; +} + +static int stbi_jpeg_test(stbi *s); +static stbi_uc *stbi_jpeg_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_jpeg_info(stbi *s, int *x, int *y, int *comp); +static int stbi_png_test(stbi *s); +static stbi_uc *stbi_png_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_png_info(stbi *s, int *x, int *y, int *comp); +static int stbi_bmp_test(stbi *s); +static stbi_uc *stbi_bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_tga_test(stbi *s); +static stbi_uc *stbi_tga_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_tga_info(stbi *s, int *x, int *y, int *comp); +static int stbi_psd_test(stbi *s); +static stbi_uc *stbi_psd_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_hdr_test(stbi *s); +static float *stbi_hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_pic_test(stbi *s); +static stbi_uc *stbi_pic_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_gif_test(stbi *s); +static stbi_uc *stbi_gif_load(stbi *s, int *x, int *y, int *comp, int req_comp); +static int stbi_gif_info(stbi *s, int *x, int *y, int *comp); + + +// this is not threadsafe +static const char *failure_reason; + +const char *stbi_failure_reason(void) +{ + return failure_reason; +} + +static int e(const char *str) +{ + failure_reason = str; + return 0; +} + +// e - error +// epf - error returning pointer to float +// epuc - error returning pointer to unsigned char + +#ifdef STBI_NO_FAILURE_STRINGS + #define e(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define e(x,y) e(y) +#else + #define e(x,y) e(x) +#endif + +#define epf(x,y) ((float *) (e(x,y)?NULL:NULL)) +#define epuc(x,y) ((unsigned char *) (e(x,y)?NULL:NULL)) + +void stbi_image_free(void *retval_from_stbi_load) +{ + free(retval_from_stbi_load); +} + +#ifndef STBI_NO_HDR +static float *ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +static unsigned char *stbi_load_main(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + if (stbi_jpeg_test(s)) return stbi_jpeg_load(s,x,y,comp,req_comp); + if (stbi_png_test(s)) return stbi_png_load(s,x,y,comp,req_comp); + if (stbi_bmp_test(s)) return stbi_bmp_load(s,x,y,comp,req_comp); + if (stbi_gif_test(s)) return stbi_gif_load(s,x,y,comp,req_comp); + if (stbi_psd_test(s)) return stbi_psd_load(s,x,y,comp,req_comp); + if (stbi_pic_test(s)) return stbi_pic_load(s,x,y,comp,req_comp); + + #ifndef STBI_NO_HDR + if (stbi_hdr_test(s)) { + float *hdr = stbi_hdr_load(s, x,y,comp,req_comp); + return hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + + // test tga last because it's a crappy test! + if (stbi_tga_test(s)) + return stbi_tga_load(s,x,y,comp,req_comp); + return epuc("unknown image type", "Image not of any known type, or corrupt"); +} + +#ifndef STBI_NO_STDIO +unsigned char *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = fopen(filename, "rb"); + unsigned char *result; + if (!f) return epuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +unsigned char *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_file(&s,f); + return stbi_load_main(&s,x,y,comp,req_comp); +} +#endif //!STBI_NO_STDIO + +unsigned char *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_mem(&s,buffer,len); + return stbi_load_main(&s,x,y,comp,req_comp); +} + +unsigned char *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi_load_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_HDR + +float *stbi_loadf_main(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi_hdr_test(s)) + return stbi_hdr_load(s,x,y,comp,req_comp); + #endif + data = stbi_load_main(s, x, y, comp, req_comp); + if (data) + return ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return epf("unknown image type", "Image not of any known type, or corrupt"); +} + +float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_mem(&s,buffer,len); + return stbi_loadf_main(&s,x,y,comp,req_comp); +} + +float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi_loadf_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = fopen(filename, "rb"); + float *result; + if (!f) return epf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi s; + start_file(&s,f); + return stbi_loadf_main(&s,x,y,comp,req_comp); +} +#endif // !STBI_NO_STDIO + +#endif // !STBI_NO_HDR + +// these is-hdr-or-not is defined independent of whether STBI_NO_HDR is +// defined, for API simplicity; if STBI_NO_HDR is defined, it always +// reports false! + +int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + stbi s; + start_mem(&s,buffer,len); + return stbi_hdr_test(&s); + #else + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +extern int stbi_is_hdr (char const *filename) +{ + FILE *f = fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +extern int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + stbi s; + start_file(&s,f); + return stbi_hdr_test(&s); + #else + return 0; + #endif +} +#endif // !STBI_NO_STDIO + +extern int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) +{ + #ifndef STBI_NO_HDR + stbi s; + start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi_hdr_test(&s); + #else + return 0; + #endif +} + +#ifndef STBI_NO_HDR +static float h2l_gamma_i=1.0f/2.2f, h2l_scale_i=1.0f; +static float l2h_gamma=2.2f, l2h_scale=1.0f; + +void stbi_hdr_to_ldr_gamma(float gamma) { h2l_gamma_i = 1/gamma; } +void stbi_hdr_to_ldr_scale(float scale) { h2l_scale_i = 1/scale; } + +void stbi_ldr_to_hdr_gamma(float gamma) { l2h_gamma = gamma; } +void stbi_ldr_to_hdr_scale(float scale) { l2h_scale = scale; } +#endif + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +enum +{ + SCAN_load=0, + SCAN_type, + SCAN_header +}; + +static void refill_buffer(stbi *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + if (n == 0) { + // at end of file, treat same as if from memory + s->read_from_callbacks = 0; + s->img_buffer = s->img_buffer_end-1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +stbi_inline static int get8(stbi *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + refill_buffer(s); + return *s->img_buffer++; + } + return 0; +} + +stbi_inline static int at_eof(stbi *s) +{ + if (s->io.read) { + if (!(s->io.eof)(s->io_user_data)) return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->read_from_callbacks == 0) return 1; + } + + return s->img_buffer >= s->img_buffer_end; +} + +stbi_inline static uint8 get8u(stbi *s) +{ + return (uint8) get8(s); +} + +static void skip(stbi *s, int n) +{ + if (s->io.read) { + int blen = s->img_buffer_end - s->img_buffer; + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; +} + +static int getn(stbi *s, stbi_uc *buffer, int n) +{ + if (s->io.read) { + int blen = s->img_buffer_end - s->img_buffer; + if (blen < n) { + int res, count; + + memcpy(buffer, s->img_buffer, blen); + + count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); + res = (count == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } + + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} + +static int get16(stbi *s) +{ + int z = get8(s); + return (z << 8) + get8(s); +} + +static uint32 get32(stbi *s) +{ + uint32 z = get16(s); + return (z << 16) + get16(s); +} + +static int get16le(stbi *s) +{ + int z = get8(s); + return z + (get8(s) << 8); +} + +static uint32 get32le(stbi *s) +{ + uint32 z = get16le(s); + return z + (get16le(s) << 16); +} + +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static uint8 compute_y(int r, int g, int b) +{ + return (uint8) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static unsigned char *convert_format(unsigned char *data, int img_n, int req_comp, uint x, uint y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + assert(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) malloc(req_comp * x * y); + if (good == NULL) { + free(data); + return epuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define COMBO(a,b) ((a)*8+(b)) + #define CASE(a,b) case COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (COMBO(img_n, req_comp)) { + CASE(1,2) dest[0]=src[0], dest[1]=255; break; + CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break; + CASE(1,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; break; + CASE(2,1) dest[0]=src[0]; break; + CASE(2,3) dest[0]=dest[1]=dest[2]=src[0]; break; + CASE(2,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; break; + CASE(3,4) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; break; + CASE(3,1) dest[0]=compute_y(src[0],src[1],src[2]); break; + CASE(3,2) dest[0]=compute_y(src[0],src[1],src[2]), dest[1] = 255; break; + CASE(4,1) dest[0]=compute_y(src[0],src[1],src[2]); break; + CASE(4,2) dest[0]=compute_y(src[0],src[1],src[2]), dest[1] = src[3]; break; + CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; break; + default: assert(0); + } + #undef CASE + } + + free(data); + return good; +} + +#ifndef STBI_NO_HDR +static float *ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output = (float *) malloc(x * y * comp * sizeof(float)); + if (output == NULL) { free(data); return epf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) pow(data[i*comp+k]/255.0f, l2h_gamma) * l2h_scale; + } + if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; + } + free(data); + return output; +} + +#define float2int(x) ((int) (x)) +static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output = (stbi_uc *) malloc(x * y * comp); + if (output == NULL) { free(data); return epuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*h2l_scale_i, h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (uint8) float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (uint8) float2int(z); + } + } + free(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder (not actually fully baseline implementation) +// +// simple implementation +// - channel subsampling of at most 2 in each dimension +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - uses a lot of intermediate memory, could cache poorly +// - load http://nothings.org/remote/anemones.jpg 3 times on 2.8Ghz P4 +// stb_jpeg: 1.34 seconds (MSVC6, default release build) +// stb_jpeg: 1.06 seconds (MSVC6, processor = Pentium Pro) +// IJL11.dll: 1.08 seconds (compiled by intel) +// IJG 1998: 0.98 seconds (MSVC6, makefile provided by IJG) +// IJG 1998: 0.95 seconds (MSVC6, makefile + proc=PPro) + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + uint8 fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + uint16 code[256]; + uint8 values[256]; + uint8 size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} huffman; + +typedef struct +{ + #ifdef STBI_SIMD + unsigned short dequant2[4][64]; + #endif + stbi *s; + huffman huff_dc[4]; + huffman huff_ac[4]; + uint8 dequant[4][64]; + +// sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + +// definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + uint8 *data; + void *raw_data; + uint8 *linebuf; + } img_comp[4]; + + uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int scan_n, order[4]; + int restart_interval, todo; +} jpeg; + +static int build_huffman(huffman *h, int *count) +{ + int i,j,k=0,code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) + for (j=0; j < count[i]; ++j) + h->size[k++] = (uint8) (i+1); + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (uint16) (code++); + if (code-1 >= (1 << j)) return e("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (uint8) i; + } + } + } + return 1; +} + +static void grow_buffer_unsafe(jpeg *j) +{ + do { + int b = j->nomore ? 0 : get8(j->s); + if (b == 0xff) { + int c = get8(j->s); + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); +} + +// (1 << n) - 1 +static uint32 bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +stbi_inline static int decode(jpeg *j, huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) grow_buffer_unsafe(j); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } + + if (k > j->code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & bmask[k]) + h->delta[k]; + assert((((j->code_buffer) >> (32 - h->size[c])) & bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; +} + +// combined JPEG 'receive' and JPEG 'extend', since baseline +// always extends everything it receives. +stbi_inline static int extend_receive(jpeg *j, int n) +{ + unsigned int m = 1 << (n-1); + unsigned int k; + if (j->code_bits < n) grow_buffer_unsafe(j); + + #if 1 + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~bmask[n]; + k &= bmask[n]; + j->code_bits -= n; + #else + k = (j->code_buffer >> (32 - n)) & bmask[n]; + j->code_bits -= n; + j->code_buffer <<= n; + #endif + // the following test is probably a random branch that won't + // predict well. I tried to table accelerate it but failed. + // maybe it's compiling as a conditional move? + if (k < m) + return (-1 << n) + k + 1; + else + return k; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static uint8 dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int decode_block(jpeg *j, short data[64], huffman *hdc, huffman *hac, int b) +{ + int diff,dc,k; + int t = decode(j, hdc); + if (t < 0) return e("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? extend_receive(j, t) : 0; + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) dc; + + // decode AC components, see JPEG spec + k = 1; + do { + int r,s; + int rs = decode(j, hac); + if (rs < 0) return e("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + data[dezigzag[k++]] = (short) extend_receive(j,s); + } + } while (k < 64); + return 1; +} + +// take a -128..127 value and clamp it and convert to 0..255 +stbi_inline static uint8 clamp(int x) +{ + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (uint8) x; +} + +#define f2f(x) (int) (((x) * 4096 + 0.5)) +#define fsh(x) ((x) << 12) + +// derived from jidctint -- DCT_ISLOW +#define IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * f2f(0.5411961f); \ + t2 = p1 + p3*f2f(-1.847759065f); \ + t3 = p1 + p2*f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = fsh(p2+p3); \ + t1 = fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*f2f( 1.175875602f); \ + t0 = t0*f2f( 0.298631336f); \ + t1 = t1*f2f( 2.053119869f); \ + t2 = t2*f2f( 3.072711026f); \ + t3 = t3*f2f( 1.501321110f); \ + p1 = p5 + p1*f2f(-0.899976223f); \ + p2 = p5 + p2*f2f(-2.562915447f); \ + p3 = p3*f2f(-1.961570560f); \ + p4 = p4*f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +#ifdef STBI_SIMD +typedef unsigned short stbi_dequantize_t; +#else +typedef uint8 stbi_dequantize_t; +#endif + +// .344 seconds on 3*anemones.jpg +static void idct_block(uint8 *out, int out_stride, short data[64], stbi_dequantize_t *dequantize) +{ + int i,val[64],*v=val; + stbi_dequantize_t *dq = dequantize; + uint8 *o; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d,++dq, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0] * dq[0] << 2; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + IDCT_1D(d[ 0]*dq[ 0],d[ 8]*dq[ 8],d[16]*dq[16],d[24]*dq[24], + d[32]*dq[32],d[40]*dq[40],d[48]*dq[48],d[56]*dq[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128<<17); + x1 += 65536 + (128<<17); + x2 += 65536 + (128<<17); + x3 += 65536 + (128<<17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = clamp((x0+t3) >> 17); + o[7] = clamp((x0-t3) >> 17); + o[1] = clamp((x1+t2) >> 17); + o[6] = clamp((x1-t2) >> 17); + o[2] = clamp((x2+t1) >> 17); + o[5] = clamp((x2-t1) >> 17); + o[3] = clamp((x3+t0) >> 17); + o[4] = clamp((x3-t0) >> 17); + } +} + +#ifdef STBI_SIMD +static stbi_idct_8x8 stbi_idct_installed = idct_block; + +void stbi_install_idct(stbi_idct_8x8 func) +{ + stbi_idct_installed = func; +} +#endif + +#define MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static uint8 get_marker(jpeg *j) +{ + uint8 x; + if (j->marker != MARKER_none) { x = j->marker; j->marker = MARKER_none; return x; } + x = get8u(j->s); + if (x != 0xff) return MARKER_none; + while (x == 0xff) + x = get8u(j->s); + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, reset the entropy decoder and +// the dc prediction +static void reset(jpeg *j) +{ + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = 0; + j->marker = MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int parse_entropy_coded_data(jpeg *z) +{ + reset(z); + if (z->scan_n == 1) { + int i,j; + #ifdef STBI_SIMD + __declspec(align(16)) + #endif + short data[64]; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + if (!decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+z->img_comp[n].ha, n)) return 0; + #ifdef STBI_SIMD + stbi_idct_installed(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data, z->dequant2[z->img_comp[n].tq]); + #else + idct_block(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data, z->dequant[z->img_comp[n].tq]); + #endif + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!RESTART(z->marker)) return 1; + reset(z); + } + } + } + } else { // interleaved! + int i,j,k,x,y; + short data[64]; + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + if (!decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+z->img_comp[n].ha, n)) return 0; + #ifdef STBI_SIMD + stbi_idct_installed(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data, z->dequant2[z->img_comp[n].tq]); + #else + idct_block(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data, z->dequant[z->img_comp[n].tq]); + #endif + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!RESTART(z->marker)) return 1; + reset(z); + } + } + } + } + return 1; +} + +static int process_marker(jpeg *z, int m) +{ + int L; + switch (m) { + case MARKER_none: // no marker found + return e("expected marker","Corrupt JPEG"); + + case 0xC2: // SOF - progressive + return e("progressive jpeg","JPEG format not supported (progressive)"); + + case 0xDD: // DRI - specify restart interval + if (get16(z->s) != 4) return e("bad DRI len","Corrupt JPEG"); + z->restart_interval = get16(z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = get16(z->s)-2; + while (L > 0) { + int q = get8(z->s); + int p = q >> 4; + int t = q & 15,i; + if (p != 0) return e("bad DQT type","Corrupt JPEG"); + if (t > 3) return e("bad DQT table","Corrupt JPEG"); + for (i=0; i < 64; ++i) + z->dequant[t][dezigzag[i]] = get8u(z->s); + #ifdef STBI_SIMD + for (i=0; i < 64; ++i) + z->dequant2[t][i] = z->dequant[t][i]; + #endif + L -= 65; + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = get16(z->s)-2; + while (L > 0) { + uint8 *v; + int sizes[16],i,m=0; + int q = get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return e("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = get8(z->s); + m += sizes[i]; + } + L -= 17; + if (tc == 0) { + if (!build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < m; ++i) + v[i] = get8u(z->s); + L -= m; + } + return L==0; + } + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + skip(z->s, get16(z->s)-2); + return 1; + } + return 0; +} + +// after we see SOS +static int process_scan_header(jpeg *z) +{ + int i; + int Ls = get16(z->s); + z->scan_n = get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return e("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return e("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = get8(z->s), which; + int q = get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) return 0; + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return e("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return e("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + if (get8(z->s) != 0) return e("bad SOS","Corrupt JPEG"); + get8(z->s); // should be 63, but might be 0 + if (get8(z->s) != 0) return e("bad SOS","Corrupt JPEG"); + + return 1; +} + +static int process_frame_header(jpeg *z, int scan) +{ + stbi *s = z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = get16(s); if (Lf < 11) return e("bad SOF len","Corrupt JPEG"); // JPEG + p = get8(s); if (p != 8) return e("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = get16(s); if (s->img_y == 0) return e("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = get16(s); if (s->img_x == 0) return e("0 width","Corrupt JPEG"); // JPEG requires + c = get8(s); + if (c != 3 && c != 1) return e("bad component count","Corrupt JPEG"); // JFIF requires + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } + + if (Lf != 8+3*s->img_n) return e("bad SOF len","Corrupt JPEG"); + + for (i=0; i < s->img_n; ++i) { + z->img_comp[i].id = get8(s); + if (z->img_comp[i].id != i+1) // JFIF requires + if (z->img_comp[i].id != i) // some version of jpegtran outputs non-JFIF-compliant files! + return e("bad component ID","Corrupt JPEG"); + q = get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return e("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return e("bad V","Corrupt JPEG"); + z->img_comp[i].tq = get8(s); if (z->img_comp[i].tq > 3) return e("bad TQ","Corrupt JPEG"); + } + + if (scan != SCAN_load) return 1; + + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return e("too large", "Image too large to decode"); + + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } + + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + + for (i=0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].raw_data = malloc(z->img_comp[i].w2 * z->img_comp[i].h2+15); + if (z->img_comp[i].raw_data == NULL) { + for(--i; i >= 0; --i) { + free(z->img_comp[i].raw_data); + z->img_comp[i].data = NULL; + } + return e("outofmem", "Out of memory"); + } + // align blocks for installable-idct using mmx/sse + z->img_comp[i].data = (uint8*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + z->img_comp[i].linebuf = NULL; + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define DNL(x) ((x) == 0xdc) +#define SOI(x) ((x) == 0xd8) +#define EOI(x) ((x) == 0xd9) +#define SOF(x) ((x) == 0xc0 || (x) == 0xc1) +#define SOS(x) ((x) == 0xda) + +static int decode_jpeg_header(jpeg *z, int scan) +{ + int m; + z->marker = MARKER_none; // initialize cached marker to empty + m = get_marker(z); + if (!SOI(m)) return e("no SOI","Corrupt JPEG"); + if (scan == SCAN_type) return 1; + m = get_marker(z); + while (!SOF(m)) { + if (!process_marker(z,m)) return 0; + m = get_marker(z); + while (m == MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (at_eof(z->s)) return e("no SOF", "Corrupt JPEG"); + m = get_marker(z); + } + } + if (!process_frame_header(z, scan)) return 0; + return 1; +} + +static int decode_jpeg_image(jpeg *j) +{ + int m; + j->restart_interval = 0; + if (!decode_jpeg_header(j, SCAN_load)) return 0; + m = get_marker(j); + while (!EOI(m)) { + if (SOS(m)) { + if (!process_scan_header(j)) return 0; + if (!parse_entropy_coded_data(j)) return 0; + if (j->marker == MARKER_none ) { + // handle 0s at the end of image data from IP Kamera 9060 + while (!at_eof(j->s)) { + int x = get8(j->s); + if (x == 255) { + j->marker = get8u(j->s); + break; + } else if (x != 0) { + return 0; + } + } + // if we reach eof without hitting a marker, get_marker() below will fail and we'll eventually return 0 + } + } else { + if (!process_marker(j, m)) return 0; + } + m = get_marker(j); + } + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef uint8 *(*resample_row_func)(uint8 *out, uint8 *in0, uint8 *in1, + int w, int hs); + +#define div4(x) ((uint8) ((x) >> 2)) + +static uint8 *resample_row_1(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; +} + +static uint8* resample_row_v_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i=0; i < w; ++i) + out[i] = div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static uint8* resample_row_h_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + uint8 *input = in_near; + + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = div4(n+input[i-1]); + out[i*2+1] = div4(n+input[i+1]); + } + out[i*2+0] = div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); + + return out; +} + +#define div16(x) ((uint8) ((x) >> 4)) + +static uint8 *resample_row_hv_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = div16(3*t0 + t1 + 8); + out[i*2 ] = div16(3*t1 + t0 + 8); + } + out[w*2-1] = div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} + +static uint8 *resample_row_generic(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + in_far = in_far; + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +#define float2fixed(x) ((int) ((x) * 65536 + 0.5)) + +// 0.38 seconds on 3*anemones.jpg (0.25 with processor = Pro) +// VC6 without processor=Pro is generating multiple LEAs per multiply! +static void YCbCr_to_RGB_row(uint8 *out, const uint8 *y, const uint8 *pcb, const uint8 *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 16) + 32768; // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr*float2fixed(1.40200f); + g = y_fixed - cr*float2fixed(0.71414f) - cb*float2fixed(0.34414f); + b = y_fixed + cb*float2fixed(1.77200f); + r >>= 16; + g >>= 16; + b >>= 16; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (uint8)r; + out[1] = (uint8)g; + out[2] = (uint8)b; + out[3] = 255; + out += step; + } +} + +#ifdef STBI_SIMD +static stbi_YCbCr_to_RGB_run stbi_YCbCr_installed = YCbCr_to_RGB_row; + +void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func) +{ + stbi_YCbCr_installed = func; +} +#endif + + +// clean up the temporary component buffers +static void cleanup_jpeg(jpeg *j) +{ + int i; + for (i=0; i < j->s->img_n; ++i) { + if (j->img_comp[i].data) { + free(j->img_comp[i].raw_data); + j->img_comp[i].data = NULL; + } + if (j->img_comp[i].linebuf) { + free(j->img_comp[i].linebuf); + j->img_comp[i].linebuf = NULL; + } + } +} + +typedef struct +{ + resample_row_func resample; + uint8 *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi_resample; + +static uint8 *load_jpeg_image(jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n; + // validate req_comp + if (req_comp < 0 || req_comp > 4) return epuc("bad req_comp", "Internal error"); + z->s->img_n = 0; + + // load a jpeg image from whichever source + if (!decode_jpeg_image(z)) { cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n; + + if (z->s->img_n == 3 && n < 3) + decode_n = 1; + else + decode_n = z->s->img_n; + + // resample and color-convert + { + int k; + uint i,j; + uint8 *output; + uint8 *coutput[4]; + + stbi_resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi_resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (uint8 *) malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { cleanup_jpeg(z); return epuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = resample_row_hv_2; + else r->resample = resample_row_generic; + } + + // can't error after this so, this is safe + output = (uint8 *) malloc(n * z->s->img_x * z->s->img_y + 1); + if (!output) { cleanup_jpeg(z); return epuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s->img_y; ++j) { + uint8 *out = output + n * z->s->img_x * j; + for (k=0; k < decode_n; ++k) { + stbi_resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + uint8 *y = coutput[0]; + if (z->s->img_n == 3) { + #ifdef STBI_SIMD + stbi_YCbCr_installed(out, y, coutput[1], coutput[2], z->s.img_x, n); + #else + YCbCr_to_RGB_row(out, y, coutput[1], coutput[2], z->s->img_x, n); + #endif + } else + for (i=0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + uint8 *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; + } + } + cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n; // report original components, not output + return output; + } +} + +static unsigned char *stbi_jpeg_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + jpeg j; + j.s = s; + return load_jpeg_image(&j, x,y,comp,req_comp); +} + +static int stbi_jpeg_test(stbi *s) +{ + int r; + jpeg j; + j.s = s; + r = decode_jpeg_header(&j, SCAN_type); + stbi_rewind(s); + return r; +} + +static int stbi_jpeg_info_raw(jpeg *j, int *x, int *y, int *comp) +{ + if (!decode_jpeg_header(j, SCAN_header)) { + stbi_rewind( j->s ); + return 0; + } + if (x) *x = j->s->img_x; + if (y) *y = j->s->img_y; + if (comp) *comp = j->s->img_n; + return 1; +} + +static int stbi_jpeg_info(stbi *s, int *x, int *y, int *comp) +{ + jpeg j; + j.s = s; + return stbi_jpeg_info_raw(&j, x, y, comp); +} + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define ZFAST_BITS 9 // accelerate all cases in default tables +#define ZFAST_MASK ((1 << ZFAST_BITS) - 1) + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + uint16 fast[1 << ZFAST_BITS]; + uint16 firstcode[16]; + int maxcode[17]; + uint16 firstsymbol[16]; + uint8 size[288]; + uint16 value[288]; +} zhuffman; + +stbi_inline static int bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +stbi_inline static int bit_reverse(int v, int bits) +{ + assert(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return bitreverse16(v) >> (16-bits); +} + +static int zbuild_huffman(zhuffman *z, uint8 *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 255, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + assert(sizes[i] <= (1 << i)); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (uint16) code; + z->firstsymbol[i] = (uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return e("bad codelengths","Corrupt JPEG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + z->size[c] = (uint8)s; + z->value[c] = (uint16)i; + if (s <= ZFAST_BITS) { + int k = bit_reverse(next_code[s],s); + while (k < (1 << ZFAST_BITS)) { + z->fast[k] = (uint16) c; + k += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +typedef struct +{ + uint8 *zbuffer, *zbuffer_end; + int num_bits; + uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + zhuffman z_length, z_distance; +} zbuf; + +stbi_inline static int zget8(zbuf *z) +{ + if (z->zbuffer >= z->zbuffer_end) return 0; + return *z->zbuffer++; +} + +static void fill_bits(zbuf *z) +{ + do { + assert(z->code_buffer < (1U << z->num_bits)); + z->code_buffer |= zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +stbi_inline static unsigned int zreceive(zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +stbi_inline static int zhuffman_decode(zbuf *a, zhuffman *z) +{ + int b,s,k; + if (a->num_bits < 16) fill_bits(a); + b = z->fast[a->code_buffer & ZFAST_MASK]; + if (b < 0xffff) { + s = z->size[b]; + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; + } + + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = bit_reverse(a->code_buffer, 16); + for (s=ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s == 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + assert(z->size[b] == s); + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +static int expand(zbuf *z, int n) // need to make room for n bytes +{ + char *q; + int cur, limit; + if (!z->z_expandable) return e("output buffer limit","Corrupt PNG"); + cur = (int) (z->zout - z->zout_start); + limit = (int) (z->zout_end - z->zout_start); + while (cur + n > limit) + limit *= 2; + q = (char *) realloc(z->zout_start, limit); + if (q == NULL) return e("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static int length_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static int length_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static int dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static int dist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int parse_huffman_block(zbuf *a) +{ + for(;;) { + int z = zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return e("bad huffman code","Corrupt PNG"); // error in huffman codes + if (a->zout >= a->zout_end) if (!expand(a, 1)) return 0; + *a->zout++ = (char) z; + } else { + uint8 *p; + int len,dist; + if (z == 256) return 1; + z -= 257; + len = length_base[z]; + if (length_extra[z]) len += zreceive(a, length_extra[z]); + z = zhuffman_decode(a, &a->z_distance); + if (z < 0) return e("bad huffman code","Corrupt PNG"); + dist = dist_base[z]; + if (dist_extra[z]) dist += zreceive(a, dist_extra[z]); + if (a->zout - a->zout_start < dist) return e("bad dist","Corrupt PNG"); + if (a->zout + len > a->zout_end) if (!expand(a, len)) return 0; + p = (uint8 *) (a->zout - dist); + while (len--) + *a->zout++ = *p++; + } + } +} + +static int compute_huffman_codes(zbuf *a) +{ + static uint8 length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + zhuffman z_codelength; + uint8 lencodes[286+32+137];//padding for maximum single op + uint8 codelength_sizes[19]; + int i,n; + + int hlit = zreceive(a,5) + 257; + int hdist = zreceive(a,5) + 1; + int hclen = zreceive(a,4) + 4; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (uint8) s; + } + if (!zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < hlit + hdist) { + int c = zhuffman_decode(a, &z_codelength); + assert(c >= 0 && c < 19); + if (c < 16) + lencodes[n++] = (uint8) c; + else if (c == 16) { + c = zreceive(a,2)+3; + memset(lencodes+n, lencodes[n-1], c); + n += c; + } else if (c == 17) { + c = zreceive(a,3)+3; + memset(lencodes+n, 0, c); + n += c; + } else { + assert(c == 18); + c = zreceive(a,7)+11; + memset(lencodes+n, 0, c); + n += c; + } + } + if (n != hlit+hdist) return e("bad codelengths","Corrupt PNG"); + if (!zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int parse_uncompressed_block(zbuf *a) +{ + uint8 header[4]; + int len,nlen,k; + if (a->num_bits & 7) + zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (uint8) (a->code_buffer & 255); // wtf this warns? + a->code_buffer >>= 8; + a->num_bits -= 8; + } + assert(a->num_bits == 0); + // now fill header the normal way + while (k < 4) + header[k++] = (uint8) zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return e("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return e("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!expand(a, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int parse_zlib_header(zbuf *a) +{ + int cmf = zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = zget8(a); + if ((cmf*256+flg) % 31 != 0) return e("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return e("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return e("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +// @TODO: should statically initialize these for optimal thread safety +static uint8 default_length[288], default_distance[32]; +static void init_defaults(void) +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) default_length[i] = 8; + for ( ; i <= 255; ++i) default_length[i] = 9; + for ( ; i <= 279; ++i) default_length[i] = 7; + for ( ; i <= 287; ++i) default_length[i] = 8; + + for (i=0; i <= 31; ++i) default_distance[i] = 5; +} + +int stbi_png_partial; // a quick hack to only allow decoding some of a PNG... I should implement real streaming support instead +static int parse_zlib(zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + do { + final = zreceive(a,1); + type = zreceive(a,2); + if (type == 0) { + if (!parse_uncompressed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!default_distance[31]) init_defaults(); + if (!zbuild_huffman(&a->z_length , default_length , 288)) return 0; + if (!zbuild_huffman(&a->z_distance, default_distance, 32)) return 0; + } else { + if (!compute_huffman_codes(a)) return 0; + } + if (!parse_huffman_block(a)) return 0; + } + if (stbi_png_partial && a->zout - a->zout_start > 65536) + break; + } while (!final); + return 1; +} + +static int do_zlib(zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return parse_zlib(a, parse_header); +} + +char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + zbuf a; + char *p = (char *) malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (uint8 *) buffer; + a.zbuffer_end = (uint8 *) buffer + len; + if (do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + free(a.zout_start); + return NULL; + } +} + +char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + zbuf a; + char *p = (char *) malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (uint8 *) buffer; + a.zbuffer_end = (uint8 *) buffer + len; + if (do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + free(a.zout_start); + return NULL; + } +} + +int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + zbuf a; + a.zbuffer = (uint8 *) ibuffer; + a.zbuffer_end = (uint8 *) ibuffer + ilen; + if (do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + zbuf a; + char *p = (char *) malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (uint8 *) buffer; + a.zbuffer_end = (uint8 *) buffer+len; + if (do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + free(a.zout_start); + return NULL; + } +} + +int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + zbuf a; + a.zbuffer = (uint8 *) ibuffer; + a.zbuffer_end = (uint8 *) ibuffer + ilen; + if (do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + + +typedef struct +{ + uint32 length; + uint32 type; +} chunk; + +#define PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) + +static chunk get_chunk_header(stbi *s) +{ + chunk c; + c.length = get32(s); + c.type = get32(s); + return c; +} + +static int check_png_header(stbi *s) +{ + static uint8 png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (get8u(s) != png_sig[i]) return e("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi *s; + uint8 *idata, *expanded, *out; +} png; + + +enum { + F_none=0, F_sub=1, F_up=2, F_avg=3, F_paeth=4, + F_avg_first, F_paeth_first +}; + +static uint8 first_row_filter[5] = +{ + F_none, F_sub, F_none, F_avg_first, F_paeth_first +}; + +static int paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +// create the png data from post-deflated data +static int create_png_image_raw(png *a, uint8 *raw, uint32 raw_len, int out_n, uint32 x, uint32 y) +{ + stbi *s = a->s; + uint32 i,j,stride = x*out_n; + int k; + int img_n = s->img_n; // copy it into a local for later + assert(out_n == s->img_n || out_n == s->img_n+1); + if (stbi_png_partial) y = 1; + a->out = (uint8 *) malloc(x * y * out_n); + if (!a->out) return e("outofmem", "Out of memory"); + if (!stbi_png_partial) { + if (s->img_x == x && s->img_y == y) { + if (raw_len != (img_n * x + 1) * y) return e("not enough pixels","Corrupt PNG"); + } else { // interlaced: + if (raw_len < (img_n * x + 1) * y) return e("not enough pixels","Corrupt PNG"); + } + } + for (j=0; j < y; ++j) { + uint8 *cur = a->out + stride*j; + uint8 *prior = cur - stride; + int filter = *raw++; + if (filter > 4) return e("invalid filter","Corrupt PNG"); + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + // handle first pixel explicitly + for (k=0; k < img_n; ++k) { + switch (filter) { + case F_none : cur[k] = raw[k]; break; + case F_sub : cur[k] = raw[k]; break; + case F_up : cur[k] = raw[k] + prior[k]; break; + case F_avg : cur[k] = raw[k] + (prior[k]>>1); break; + case F_paeth : cur[k] = (uint8) (raw[k] + paeth(0,prior[k],0)); break; + case F_avg_first : cur[k] = raw[k]; break; + case F_paeth_first: cur[k] = raw[k]; break; + } + } + if (img_n != out_n) cur[img_n] = 255; + raw += img_n; + cur += out_n; + prior += out_n; + // this is a little gross, so that we don't switch per-pixel or per-component + if (img_n == out_n) { + #define CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, raw+=img_n,cur+=img_n,prior+=img_n) \ + for (k=0; k < img_n; ++k) + switch (filter) { + CASE(F_none) cur[k] = raw[k]; break; + CASE(F_sub) cur[k] = raw[k] + cur[k-img_n]; break; + CASE(F_up) cur[k] = raw[k] + prior[k]; break; + CASE(F_avg) cur[k] = raw[k] + ((prior[k] + cur[k-img_n])>>1); break; + CASE(F_paeth) cur[k] = (uint8) (raw[k] + paeth(cur[k-img_n],prior[k],prior[k-img_n])); break; + CASE(F_avg_first) cur[k] = raw[k] + (cur[k-img_n] >> 1); break; + CASE(F_paeth_first) cur[k] = (uint8) (raw[k] + paeth(cur[k-img_n],0,0)); break; + } + #undef CASE + } else { + assert(img_n+1 == out_n); + #define CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \ + for (k=0; k < img_n; ++k) + switch (filter) { + CASE(F_none) cur[k] = raw[k]; break; + CASE(F_sub) cur[k] = raw[k] + cur[k-out_n]; break; + CASE(F_up) cur[k] = raw[k] + prior[k]; break; + CASE(F_avg) cur[k] = raw[k] + ((prior[k] + cur[k-out_n])>>1); break; + CASE(F_paeth) cur[k] = (uint8) (raw[k] + paeth(cur[k-out_n],prior[k],prior[k-out_n])); break; + CASE(F_avg_first) cur[k] = raw[k] + (cur[k-out_n] >> 1); break; + CASE(F_paeth_first) cur[k] = (uint8) (raw[k] + paeth(cur[k-out_n],0,0)); break; + } + #undef CASE + } + } + return 1; +} + +static int create_png_image(png *a, uint8 *raw, uint32 raw_len, int out_n, int interlaced) +{ + uint8 *final; + int p; + int save; + if (!interlaced) + return create_png_image_raw(a, raw, raw_len, out_n, a->s->img_x, a->s->img_y); + save = stbi_png_partial; + stbi_png_partial = 0; + + // de-interlacing + final = (uint8 *) malloc(a->s->img_x * a->s->img_y * out_n); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + if (!create_png_image_raw(a, raw, raw_len, out_n, x, y)) { + free(final); + return 0; + } + for (j=0; j < y; ++j) + for (i=0; i < x; ++i) + memcpy(final + (j*yspc[p]+yorig[p])*a->s->img_x*out_n + (i*xspc[p]+xorig[p])*out_n, + a->out + (j*x+i)*out_n, out_n); + free(a->out); + raw += (x*out_n+1)*y; + raw_len -= (x*out_n+1)*y; + } + } + a->out = final; + + stbi_png_partial = save; + return 1; +} + +static int compute_transparency(png *z, uint8 tc[3], int out_n) +{ + stbi *s = z->s; + uint32 i, pixel_count = s->img_x * s->img_y; + uint8 *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + assert(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int expand_palette(png *a, uint8 *palette, int len, int pal_img_n) +{ + uint32 i, pixel_count = a->s->img_x * a->s->img_y; + uint8 *p, *temp_out, *orig = a->out; + + p = (uint8 *) malloc(pixel_count * pal_img_n); + if (p == NULL) return e("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + free(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +static int stbi_unpremultiply_on_load = 0; +static int stbi_de_iphone_flag = 0; + +void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) +{ + stbi_unpremultiply_on_load = flag_true_if_should_unpremultiply; +} +void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) +{ + stbi_de_iphone_flag = flag_true_if_should_convert; +} + +static void stbi_de_iphone(png *z) +{ + stbi *s = z->s; + uint32 i, pixel_count = s->img_x * s->img_y; + uint8 *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + uint8 t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + assert(s->img_out_n == 4); + if (stbi_unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + uint8 a = p[3]; + uint8 t = p[0]; + if (a) { + p[0] = p[2] * 255 / a; + p[1] = p[1] * 255 / a; + p[2] = t * 255 / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + uint8 t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + +static int parse_png_file(png *z, int scan, int req_comp) +{ + uint8 palette[1024], pal_img_n=0; + uint8 has_trans=0, tc[3]; + uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, iphone=0; + stbi *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!check_png_header(s)) return 0; + + if (scan == SCAN_type) return 1; + + for (;;) { + chunk c = get_chunk_header(s); + switch (c.type) { + case PNG_TYPE('C','g','B','I'): + iphone = stbi_de_iphone_flag; + skip(s, c.length); + break; + case PNG_TYPE('I','H','D','R'): { + int depth,color,comp,filter; + if (!first) return e("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return e("bad IHDR len","Corrupt PNG"); + s->img_x = get32(s); if (s->img_x > (1 << 24)) return e("too large","Very large image (corrupt?)"); + s->img_y = get32(s); if (s->img_y > (1 << 24)) return e("too large","Very large image (corrupt?)"); + depth = get8(s); if (depth != 8) return e("8bit only","PNG not supported: 8-bit only"); + color = get8(s); if (color > 6) return e("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return e("bad ctype","Corrupt PNG"); + comp = get8(s); if (comp) return e("bad comp method","Corrupt PNG"); + filter= get8(s); if (filter) return e("bad filter method","Corrupt PNG"); + interlace = get8(s); if (interlace>1) return e("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return e("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return e("too large", "Image too large to decode"); + if (scan == SCAN_header) return 1; + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return e("too large","Corrupt PNG"); + // if SCAN_header, have to scan to see if we have a tRNS + } + break; + } + + case PNG_TYPE('P','L','T','E'): { + if (first) return e("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return e("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return e("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = get8u(s); + palette[i*4+1] = get8u(s); + palette[i*4+2] = get8u(s); + palette[i*4+3] = 255; + } + break; + } + + case PNG_TYPE('t','R','N','S'): { + if (first) return e("first not IHDR", "Corrupt PNG"); + if (z->idata) return e("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return e("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return e("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = get8u(s); + } else { + if (!(s->img_n & 1)) return e("tRNS with alpha","Corrupt PNG"); + if (c.length != (uint32) s->img_n*2) return e("bad tRNS len","Corrupt PNG"); + has_trans = 1; + for (k=0; k < s->img_n; ++k) + tc[k] = (uint8) get16(s); // non 8-bit images will be larger + } + break; + } + + case PNG_TYPE('I','D','A','T'): { + if (first) return e("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return e("no PLTE","Corrupt PNG"); + if (scan == SCAN_header) { s->img_n = pal_img_n; return 1; } + if (ioff + c.length > idata_limit) { + uint8 *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + p = (uint8 *) realloc(z->idata, idata_limit); if (p == NULL) return e("outofmem", "Out of memory"); + z->idata = p; + } + if (!getn(s, z->idata+ioff,c.length)) return e("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case PNG_TYPE('I','E','N','D'): { + uint32 raw_len; + if (first) return e("first not IHDR", "Corrupt PNG"); + if (scan != SCAN_load) return 1; + if (z->idata == NULL) return e("no IDAT","Corrupt PNG"); + z->expanded = (uint8 *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, 16384, (int *) &raw_len, !iphone); + if (z->expanded == NULL) return 0; // zlib should set error + free(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!create_png_image(z, z->expanded, raw_len, s->img_out_n, interlace)) return 0; + if (has_trans) + if (!compute_transparency(z, tc, s->img_out_n)) return 0; + if (iphone && s->img_out_n > 2) + stbi_de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!expand_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } + free(z->expanded); z->expanded = NULL; + return 1; + } + + default: + // if critical, fail + if (first) return e("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX chunk not known"; + invalid_chunk[0] = (uint8) (c.type >> 24); + invalid_chunk[1] = (uint8) (c.type >> 16); + invalid_chunk[2] = (uint8) (c.type >> 8); + invalid_chunk[3] = (uint8) (c.type >> 0); + #endif + return e(invalid_chunk, "PNG not supported: unknown chunk type"); + } + skip(s, c.length); + break; + } + // end of chunk, read and skip CRC + get32(s); + } +} + +static unsigned char *do_png(png *p, int *x, int *y, int *n, int req_comp) +{ + unsigned char *result=NULL; + if (req_comp < 0 || req_comp > 4) return epuc("bad req_comp", "Internal error"); + if (parse_png_file(p, SCAN_load, req_comp)) { + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + result = convert_format(result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_n; + } + free(p->out); p->out = NULL; + free(p->expanded); p->expanded = NULL; + free(p->idata); p->idata = NULL; + + return result; +} + +static unsigned char *stbi_png_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + png p; + p.s = s; + return do_png(&p, x,y,comp,req_comp); +} + +static int stbi_png_test(stbi *s) +{ + int r; + r = check_png_header(s); + stbi_rewind(s); + return r; +} + +static int stbi_png_info_raw(png *p, int *x, int *y, int *comp) +{ + if (!parse_png_file(p, SCAN_header, 0)) { + stbi_rewind( p->s ); + return 0; + } + if (x) *x = p->s->img_x; + if (y) *y = p->s->img_y; + if (comp) *comp = p->s->img_n; + return 1; +} + +static int stbi_png_info(stbi *s, int *x, int *y, int *comp) +{ + png p; + p.s = s; + return stbi_png_info_raw(&p, x, y, comp); +} + +// Microsoft/Windows BMP image + +static int bmp_test(stbi *s) +{ + int sz; + if (get8(s) != 'B') return 0; + if (get8(s) != 'M') return 0; + get32le(s); // discard filesize + get16le(s); // discard reserved + get16le(s); // discard reserved + get32le(s); // discard data offset + sz = get32le(s); + if (sz == 12 || sz == 40 || sz == 56 || sz == 108) return 1; + return 0; +} + +static int stbi_bmp_test(stbi *s) +{ + int r = bmp_test(s); + stbi_rewind(s); + return r; +} + + +// returns 0..31 for the highest set bit +static int high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) n += 16, z >>= 16; + if (z >= 0x00100) n += 8, z >>= 8; + if (z >= 0x00010) n += 4, z >>= 4; + if (z >= 0x00004) n += 2, z >>= 2; + if (z >= 0x00002) n += 1, z >>= 1; + return n; +} + +static int bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +static int shiftsigned(int v, int shift, int bits) +{ + int result; + int z=0; + + if (shift < 0) v <<= -shift; + else v >>= shift; + result = v; + + z = bits; + while (z < 8) { + result += v >> z; + z += bits; + } + return result; +} + +static stbi_uc *bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + uint8 *out; + unsigned int mr=0,mg=0,mb=0,ma=0, fake_a=0; + stbi_uc pal[256][4]; + int psize=0,i,j,compress=0,width; + int bpp, flip_vertically, pad, target, offset, hsz; + if (get8(s) != 'B' || get8(s) != 'M') return epuc("not BMP", "Corrupt BMP"); + get32le(s); // discard filesize + get16le(s); // discard reserved + get16le(s); // discard reserved + offset = get32le(s); + hsz = get32le(s); + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108) return epuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = get16le(s); + s->img_y = get16le(s); + } else { + s->img_x = get32le(s); + s->img_y = get32le(s); + } + if (get16le(s) != 1) return epuc("bad BMP", "bad BMP"); + bpp = get16le(s); + if (bpp == 1) return epuc("monochrome", "BMP type not supported: 1-bit"); + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + if (hsz == 12) { + if (bpp < 24) + psize = (offset - 14 - 24) / 3; + } else { + compress = get32le(s); + if (compress == 1 || compress == 2) return epuc("BMP RLE", "BMP type not supported: RLE"); + get32le(s); // discard sizeof + get32le(s); // discard hres + get32le(s); // discard vres + get32le(s); // discard colorsused + get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + get32le(s); + get32le(s); + get32le(s); + get32le(s); + } + if (bpp == 16 || bpp == 32) { + mr = mg = mb = 0; + if (compress == 0) { + if (bpp == 32) { + mr = 0xffu << 16; + mg = 0xffu << 8; + mb = 0xffu << 0; + ma = 0xffu << 24; + fake_a = 1; // @TODO: check for cases like alpha value is all 0 and switch it to 255 + } else { + mr = 31u << 10; + mg = 31u << 5; + mb = 31u << 0; + } + } else if (compress == 3) { + mr = get32le(s); + mg = get32le(s); + mb = get32le(s); + // not documented, but generated by photoshop and handled by mspaint + if (mr == mg && mg == mb) { + // ?!?!? + return epuc("bad BMP", "bad BMP"); + } + } else + return epuc("bad BMP", "bad BMP"); + } + } else { + assert(hsz == 108); + mr = get32le(s); + mg = get32le(s); + mb = get32le(s); + ma = get32le(s); + get32le(s); // discard color space + for (i=0; i < 12; ++i) + get32le(s); // discard color space parameters + } + if (bpp < 16) + psize = (offset - 14 - hsz) >> 2; + } + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert + out = (stbi_uc *) malloc(target * s->img_x * s->img_y); + if (!out) return epuc("outofmem", "Out of memory"); + if (bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) { free(out); return epuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = get8u(s); + pal[i][1] = get8u(s); + pal[i][0] = get8u(s); + if (hsz != 12) get8(s); + pal[i][3] = 255; + } + skip(s, offset - 14 - hsz - psize * (hsz == 12 ? 3 : 4)); + if (bpp == 4) width = (s->img_x + 1) >> 1; + else if (bpp == 8) width = s->img_x; + else { free(out); return epuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=get8(s),v2=0; + if (bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (bpp == 8) ? get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + skip(s, pad); + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + skip(s, offset - 14 - hsz); + if (bpp == 24) width = 3 * s->img_x; + else if (bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (bpp == 24) { + easy = 1; + } else if (bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { free(out); return epuc("bad masks", "Corrupt BMP"); } + // right shift amt to put high bit in position #7 + rshift = high_bit(mr)-7; rcount = bitcount(mr); + gshift = high_bit(mg)-7; gcount = bitcount(mr); + bshift = high_bit(mb)-7; bcount = bitcount(mr); + ashift = high_bit(ma)-7; acount = bitcount(mr); + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + int a; + out[z+2] = get8u(s); + out[z+1] = get8u(s); + out[z+0] = get8u(s); + z += 3; + a = (easy == 2 ? get8(s) : 255); + if (target == 4) out[z++] = (uint8) a; + } + } else { + for (i=0; i < (int) s->img_x; ++i) { + uint32 v = (bpp == 16 ? get16le(s) : get32le(s)); + int a; + out[z++] = (uint8) shiftsigned(v & mr, rshift, rcount); + out[z++] = (uint8) shiftsigned(v & mg, gshift, gcount); + out[z++] = (uint8) shiftsigned(v & mb, bshift, bcount); + a = (ma ? shiftsigned(v & ma, ashift, acount) : 255); + if (target == 4) out[z++] = (uint8) a; + } + } + skip(s, pad); + } + } + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) s->img_y>>1; ++j) { + stbi_uc *p1 = out + j *s->img_x*target; + stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) { + t = p1[i], p1[i] = p2[i], p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; +} + +static stbi_uc *stbi_bmp_load(stbi *s,int *x, int *y, int *comp, int req_comp) +{ + return bmp_load(s, x,y,comp,req_comp); +} + + +// Targa Truevision - TGA +// by Jonathan Dummer + +static int tga_info(stbi *s, int *x, int *y, int *comp) +{ + int tga_w, tga_h, tga_comp; + int sz; + get8u(s); // discard Offset + sz = get8u(s); // color type + if( sz > 1 ) { + stbi_rewind(s); + return 0; // only RGB or indexed allowed + } + sz = get8u(s); // image type + // only RGB or grey allowed, +/- RLE + if ((sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11)) return 0; + skip(s,9); + tga_w = get16le(s); + if( tga_w < 1 ) { + stbi_rewind(s); + return 0; // test width + } + tga_h = get16le(s); + if( tga_h < 1 ) { + stbi_rewind(s); + return 0; // test height + } + sz = get8(s); // bits per pixel + // only RGB or RGBA or grey allowed + if ((sz != 8) && (sz != 16) && (sz != 24) && (sz != 32)) { + stbi_rewind(s); + return 0; + } + tga_comp = sz; + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp / 8; + return 1; // seems to have passed everything +} + +int stbi_tga_info(stbi *s, int *x, int *y, int *comp) +{ + return tga_info(s, x, y, comp); +} + +static int tga_test(stbi *s) +{ + int sz; + get8u(s); // discard Offset + sz = get8u(s); // color type + if ( sz > 1 ) return 0; // only RGB or indexed allowed + sz = get8u(s); // image type + if ( (sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11) ) return 0; // only RGB or grey allowed, +/- RLE + get16(s); // discard palette start + get16(s); // discard palette length + get8(s); // discard bits per palette color entry + get16(s); // discard x origin + get16(s); // discard y origin + if ( get16(s) < 1 ) return 0; // test width + if ( get16(s) < 1 ) return 0; // test height + sz = get8(s); // bits per pixel + if ( (sz != 8) && (sz != 16) && (sz != 24) && (sz != 32) ) return 0; // only RGB or RGBA or grey allowed + return 1; // seems to have passed everything +} + +static int stbi_tga_test(stbi *s) +{ + int res = tga_test(s); + stbi_rewind(s); + return res; +} + +static stbi_uc *tga_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + // read in the TGA header stuff + int tga_offset = get8u(s); + int tga_indexed = get8u(s); + int tga_image_type = get8u(s); + int tga_is_RLE = 0; + int tga_palette_start = get16le(s); + int tga_palette_len = get16le(s); + int tga_palette_bits = get8u(s); + int tga_x_origin = get16le(s); + int tga_y_origin = get16le(s); + int tga_width = get16le(s); + int tga_height = get16le(s); + int tga_bits_per_pixel = get8u(s); + int tga_inverted = get8u(s); + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4]; + unsigned char trans_data[4]; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + + // do a tiny bit of precessing + if ( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + /* int tga_alpha_bits = tga_inverted & 15; */ + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // error check + if ( //(tga_indexed) || + (tga_width < 1) || (tga_height < 1) || + (tga_image_type < 1) || (tga_image_type > 3) || + ((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16) && + (tga_bits_per_pixel != 24) && (tga_bits_per_pixel != 32)) + ) + { + return NULL; // we don't report this as a bad TGA because we don't even know if it's TGA + } + + // If I'm paletted, then I'll use the number of bits from the palette + if ( tga_indexed ) + { + tga_bits_per_pixel = tga_palette_bits; + } + + // tga info + *x = tga_width; + *y = tga_height; + if ( (req_comp < 1) || (req_comp > 4) ) + { + // just use whatever the file was + req_comp = tga_bits_per_pixel / 8; + *comp = req_comp; + } else + { + // force a new number of components + *comp = tga_bits_per_pixel/8; + } + tga_data = (unsigned char*)malloc( tga_width * tga_height * req_comp ); + if (!tga_data) return epuc("outofmem", "Out of memory"); + + // skip to the data's starting position (offset usually = 0) + skip(s, tga_offset ); + // do I need to load a palette? + if ( tga_indexed ) + { + // any data to skip? (offset usually = 0) + skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)malloc( tga_palette_len * tga_palette_bits / 8 ); + if (!tga_palette) return epuc("outofmem", "Out of memory"); + if (!getn(s, tga_palette, tga_palette_len * tga_palette_bits / 8 )) { + free(tga_data); + free(tga_palette); + return epuc("bad palette", "Corrupt TGA"); + } + } + // load the data + trans_data[0] = trans_data[1] = trans_data[2] = trans_data[3] = 0; + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE chunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = get8u(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if ( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if ( read_next_pixel ) + { + // load however much data we did have + if ( tga_indexed ) + { + // read in 1 byte, then perform the lookup + int pal_idx = get8u(s); + if ( pal_idx >= tga_palette_len ) + { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_bits_per_pixel / 8; + for (j = 0; j*8 < tga_bits_per_pixel; ++j) + { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else + { + // read in the data raw + for (j = 0; j*8 < tga_bits_per_pixel; ++j) + { + raw_data[j] = get8u(s); + } + } + // convert raw to the intermediate format + switch (tga_bits_per_pixel) + { + case 8: + // Luminous => RGBA + trans_data[0] = raw_data[0]; + trans_data[1] = raw_data[0]; + trans_data[2] = raw_data[0]; + trans_data[3] = 255; + break; + case 16: + // Luminous,Alpha => RGBA + trans_data[0] = raw_data[0]; + trans_data[1] = raw_data[0]; + trans_data[2] = raw_data[0]; + trans_data[3] = raw_data[1]; + break; + case 24: + // BGR => RGBA + trans_data[0] = raw_data[2]; + trans_data[1] = raw_data[1]; + trans_data[2] = raw_data[0]; + trans_data[3] = 255; + break; + case 32: + // BGRA => RGBA + trans_data[0] = raw_data[2]; + trans_data[1] = raw_data[1]; + trans_data[2] = raw_data[0]; + trans_data[3] = raw_data[3]; + break; + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + // convert to final format + switch (req_comp) + { + case 1: + // RGBA => Luminance + tga_data[i*req_comp+0] = compute_y(trans_data[0],trans_data[1],trans_data[2]); + break; + case 2: + // RGBA => Luminance,Alpha + tga_data[i*req_comp+0] = compute_y(trans_data[0],trans_data[1],trans_data[2]); + tga_data[i*req_comp+1] = trans_data[3]; + break; + case 3: + // RGBA => RGB + tga_data[i*req_comp+0] = trans_data[0]; + tga_data[i*req_comp+1] = trans_data[1]; + tga_data[i*req_comp+2] = trans_data[2]; + break; + case 4: + // RGBA => RGBA + tga_data[i*req_comp+0] = trans_data[0]; + tga_data[i*req_comp+1] = trans_data[1]; + tga_data[i*req_comp+2] = trans_data[2]; + tga_data[i*req_comp+3] = trans_data[3]; + break; + } + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if ( tga_inverted ) + { + for (j = 0; j*2 < tga_height; ++j) + { + int index1 = j * tga_width * req_comp; + int index2 = (tga_height - 1 - j) * tga_width * req_comp; + for (i = tga_width * req_comp; i > 0; --i) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if ( tga_palette != NULL ) + { + free( tga_palette ); + } + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + // OK, done + return tga_data; +} + +static stbi_uc *stbi_tga_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + return tga_load(s,x,y,comp,req_comp); +} + + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB + +static int psd_test(stbi *s) +{ + if (get32(s) != 0x38425053) return 0; // "8BPS" + else return 1; +} + +static int stbi_psd_test(stbi *s) +{ + int r = psd_test(s); + stbi_rewind(s); + return r; +} + +static stbi_uc *psd_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + int pixelCount; + int channelCount, compression; + int channel, i, count, len; + int w,h; + uint8 *out; + + // Check identifier + if (get32(s) != 0x38425053) // "8BPS" + return epuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (get16(s) != 1) + return epuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = get16(s); + if (channelCount < 0 || channelCount > 16) + return epuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = get32(s); + w = get32(s); + + // Make sure the depth is 8 bits. + if (get16(s) != 8) + return epuc("unsupported bit depth", "PSD bit depth is not 8 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (get16(s) != 3) + return epuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + skip(s,get32(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + skip(s, get32(s) ); + + // Skip the reserved data. + skip(s, get32(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = get16(s); + if (compression > 1) + return epuc("bad compression", "PSD has an unknown compression format"); + + // Create the destination image. + out = (stbi_uc *) malloc(4 * w*h); + if (!out) return epuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, + // which we're going to just skip. + skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + uint8 *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++) *p = (channel == 3 ? 255 : 0), p += 4; + } else { + // Read the RLE data. + count = 0; + while (count < pixelCount) { + len = get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + count += len; + while (len) { + *p = get8u(s); + p += 4; + len--; + } + } else if (len > 128) { + uint8 val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len ^= 0x0FF; + len += 2; + val = get8u(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + uint8 *p; + + p = out + channel; + if (channel > channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++) *p = channel == 3 ? 255 : 0, p += 4; + } else { + // Read the data. + for (i = 0; i < pixelCount; i++) + *p = get8u(s), p += 4; + } + } + } + + if (req_comp && req_comp != 4) { + out = convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // convert_format frees input on failure + } + + if (comp) *comp = channelCount; + *y = h; + *x = w; + + return out; +} + +static stbi_uc *stbi_psd_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + return psd_load(s,x,y,comp,req_comp); +} + +// ************************************************************************************************* +// Softimage PIC loader +// by Tom Seddon +// +// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format +// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ + +static int pic_is4(stbi *s,const char *str) +{ + int i; + for (i=0; i<4; ++i) + if (get8(s) != (stbi_uc)str[i]) + return 0; + + return 1; +} + +static int pic_test(stbi *s) +{ + int i; + + if (!pic_is4(s,"\x53\x80\xF6\x34")) + return 0; + + for(i=0;i<84;++i) + get8(s); + + if (!pic_is4(s,"PICT")) + return 0; + + return 1; +} + +typedef struct +{ + stbi_uc size,type,channel; +} pic_packet_t; + +static stbi_uc *pic_readval(stbi *s, int channel, stbi_uc *dest) +{ + int mask=0x80, i; + + for (i=0; i<4; ++i, mask>>=1) { + if (channel & mask) { + if (at_eof(s)) return epuc("bad file","PIC file too short"); + dest[i]=get8u(s); + } + } + + return dest; +} + +static void pic_copyval(int channel,stbi_uc *dest,const stbi_uc *src) +{ + int mask=0x80,i; + + for (i=0;i<4; ++i, mask>>=1) + if (channel&mask) + dest[i]=src[i]; +} + +static stbi_uc *pic_load2(stbi *s,int width,int height,int *comp, stbi_uc *result) +{ + int act_comp=0,num_packets=0,y,chained; + pic_packet_t packets[10]; + + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + pic_packet_t *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return epuc("bad format","too many packets"); + + packet = &packets[num_packets++]; + + chained = get8(s); + packet->size = get8u(s); + packet->type = get8u(s); + packet->channel = get8u(s); + + act_comp |= packet->channel; + + if (at_eof(s)) return epuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return epuc("bad format","packet isn't 8bpp"); + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + + for(y=0; ytype) { + default: + return epuc("bad format","packet has bad compression type"); + + case 0: {//uncompressed + int x; + + for(x=0;xchannel,dest)) + return 0; + break; + } + + case 1://Pure RLE + { + int left=width, i; + + while (left>0) { + stbi_uc count,value[4]; + + count=get8u(s); + if (at_eof(s)) return epuc("bad file","file too short (pure read count)"); + + if (count > left) + count = (uint8) left; + + if (!pic_readval(s,packet->channel,value)) return 0; + + for(i=0; ichannel,dest,value); + left -= count; + } + } + break; + + case 2: {//Mixed RLE + int left=width; + while (left>0) { + int count = get8(s), i; + if (at_eof(s)) return epuc("bad file","file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4]; + int i; + + if (count==128) + count = get16(s); + else + count -= 127; + if (count > left) + return epuc("bad file","scanline overrun"); + + if (!pic_readval(s,packet->channel,value)) + return 0; + + for(i=0;ichannel,dest,value); + } else { // Raw + ++count; + if (count>left) return epuc("bad file","scanline overrun"); + + for(i=0;ichannel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } + } + + return result; +} + +static stbi_uc *pic_load(stbi *s,int *px,int *py,int *comp,int req_comp) +{ + stbi_uc *result; + int i, x,y; + + for (i=0; i<92; ++i) + get8(s); + + x = get16(s); + y = get16(s); + if (at_eof(s)) return epuc("bad file","file too short (pic header)"); + if ((1 << 28) / x < y) return epuc("too large", "Image too large to decode"); + + get32(s); //skip `ratio' + get16(s); //skip `fields' + get16(s); //skip `pad' + + // intermediate buffer is RGBA + result = (stbi_uc *) malloc(x*y*4); + memset(result, 0xff, x*y*4); + + if (!pic_load2(s,x,y,comp, result)) { + free(result); + result=0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result=convert_format(result,4,req_comp,x,y); + + return result; +} + +static int stbi_pic_test(stbi *s) +{ + int r = pic_test(s); + stbi_rewind(s); + return r; +} + +static stbi_uc *stbi_pic_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + return pic_load(s,x,y,comp,req_comp); +} + +// ************************************************************************************************* +// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb +typedef struct stbi_gif_lzw_struct { + int16 prefix; + uint8 first; + uint8 suffix; +} stbi_gif_lzw; + +typedef struct stbi_gif_struct +{ + int w,h; + stbi_uc *out; // output buffer (always 4 components) + int flags, bgindex, ratio, transparent, eflags; + uint8 pal[256][4]; + uint8 lpal[256][4]; + stbi_gif_lzw codes[4096]; + uint8 *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; +} stbi_gif; + +static int gif_test(stbi *s) +{ + int sz; + if (get8(s) != 'G' || get8(s) != 'I' || get8(s) != 'F' || get8(s) != '8') return 0; + sz = get8(s); + if (sz != '9' && sz != '7') return 0; + if (get8(s) != 'a') return 0; + return 1; +} + +static int stbi_gif_test(stbi *s) +{ + int r = gif_test(s); + stbi_rewind(s); + return r; +} + +static void stbi_gif_parse_colortable(stbi *s, uint8 pal[256][4], int num_entries, int transp) +{ + int i; + for (i=0; i < num_entries; ++i) { + pal[i][2] = get8u(s); + pal[i][1] = get8u(s); + pal[i][0] = get8u(s); + pal[i][3] = transp ? 0 : 255; + } +} + +static int stbi_gif_header(stbi *s, stbi_gif *g, int *comp, int is_info) +{ + uint8 version; + if (get8(s) != 'G' || get8(s) != 'I' || get8(s) != 'F' || get8(s) != '8') + return e("not GIF", "Corrupt GIF"); + + version = get8u(s); + if (version != '7' && version != '9') return e("not GIF", "Corrupt GIF"); + if (get8(s) != 'a') return e("not GIF", "Corrupt GIF"); + + failure_reason = ""; + g->w = get16le(s); + g->h = get16le(s); + g->flags = get8(s); + g->bgindex = get8(s); + g->ratio = get8(s); + g->transparent = -1; + + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + + if (is_info) return 1; + + if (g->flags & 0x80) + stbi_gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + + return 1; +} + +static int stbi_gif_info_raw(stbi *s, int *x, int *y, int *comp) +{ + stbi_gif g; + if (!stbi_gif_header(s, &g, comp, 1)) { + stbi_rewind( s ); + return 0; + } + if (x) *x = g.w; + if (y) *y = g.h; + return 1; +} + +static void stbi_out_gif_code(stbi_gif *g, uint16 code) +{ + uint8 *p, *c; + + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi_out_gif_code(g, g->codes[code].prefix); + + if (g->cur_y >= g->max_y) return; + + p = &g->out[g->cur_x + g->cur_y]; + c = &g->color_table[g->codes[code].suffix * 4]; + + if (c[3] >= 128) { + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; + + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; + + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } +} + +static uint8 *stbi_process_gif_raster(stbi *s, stbi_gif *g) +{ + uint8 lzw_cs; + int32 len, code; + uint32 first; + int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi_gif_lzw *p; + + lzw_cs = get8u(s); + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (code = 0; code < clear; code++) { + g->codes[code].prefix = -1; + g->codes[code].first = (uint8) code; + g->codes[code].suffix = (uint8) code; + } + + // support no starting clear code + avail = clear+2; + oldcode = -1; + + len = 0; + for(;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (int32) get8(s) << valid_bits; + valid_bits += 8; + } else { + int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + skip(s, len); + while ((len = get8(s)) > 0) + skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) return epuc("no clear code", "Corrupt GIF"); + + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 4096) return epuc("too many codes", "Corrupt GIF"); + p->prefix = (int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return epuc("illegal code in raster", "Corrupt GIF"); + + stbi_out_gif_code(g, (uint16) code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return epuc("illegal code in raster", "Corrupt GIF"); + } + } + } +} + +static void stbi_fill_gif_background(stbi_gif *g) +{ + int i; + uint8 *c = g->pal[g->bgindex]; + // @OPTIMIZE: write a dword at a time + for (i = 0; i < g->w * g->h * 4; i += 4) { + uint8 *p = &g->out[i]; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } +} + +// this function is designed to support animated gifs, although stb_image doesn't support it +static uint8 *stbi_gif_load_next(stbi *s, stbi_gif *g, int *comp, int req_comp) +{ + int i; + uint8 *old_out = 0; + + if (g->out == 0) { + if (!stbi_gif_header(s, g, comp,0)) return 0; // failure_reason set by stbi_gif_header + g->out = (uint8 *) malloc(4 * g->w * g->h); + if (g->out == 0) return epuc("outofmem", "Out of memory"); + stbi_fill_gif_background(g); + } else { + // animated-gif-only path + if (((g->eflags & 0x1C) >> 2) == 3) { + old_out = g->out; + g->out = (uint8 *) malloc(4 * g->w * g->h); + if (g->out == 0) return epuc("outofmem", "Out of memory"); + memcpy(g->out, old_out, g->w*g->h*4); + } + } + + for (;;) { + switch (get8(s)) { + case 0x2C: /* Image Descriptor */ + { + int32 x, y, w, h; + uint8 *o; + + x = get16le(s); + y = get16le(s); + w = get16le(s); + h = get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return epuc("bad Image Descriptor", "Corrupt GIF"); + + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; + + g->lflags = get8(s); + + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } + + if (g->lflags & 0x80) { + stbi_gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (uint8 *) g->lpal; + } else if (g->flags & 0x80) { + for (i=0; i < 256; ++i) // @OPTIMIZE: reset only the previous transparent + g->pal[i][3] = 255; + if (g->transparent >= 0 && (g->eflags & 0x01)) + g->pal[g->transparent][3] = 0; + g->color_table = (uint8 *) g->pal; + } else + return epuc("missing color table", "Corrupt GIF"); + + o = stbi_process_gif_raster(s, g); + if (o == NULL) return NULL; + + if (req_comp && req_comp != 4) + o = convert_format(o, 4, req_comp, g->w, g->h); + return o; + } + + case 0x21: // Comment Extension. + { + int len; + if (get8(s) == 0xF9) { // Graphic Control Extension. + len = get8(s); + if (len == 4) { + g->eflags = get8(s); + get16le(s); // delay + g->transparent = get8(s); + } else { + skip(s, len); + break; + } + } + while ((len = get8(s)) != 0) + skip(s, len); + break; + } + + case 0x3B: // gif stream termination code + return (uint8 *) 1; + + default: + return epuc("unknown code", "Corrupt GIF"); + } + } +} + +static stbi_uc *stbi_gif_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + uint8 *u = 0; + stbi_gif g={0}; + + u = stbi_gif_load_next(s, &g, comp, req_comp); + if (u == (void *) 1) u = 0; // end of animated gif marker + if (u) { + *x = g.w; + *y = g.h; + } + + return u; +} + +static int stbi_gif_info(stbi *s, int *x, int *y, int *comp) +{ + return stbi_gif_info_raw(s,x,y,comp); +} + + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int hdr_test(stbi *s) +{ + const char *signature = "#?RADIANCE\n"; + int i; + for (i=0; signature[i]; ++i) + if (get8(s) != signature[i]) + return 0; + return 1; +} + +static int stbi_hdr_test(stbi* s) +{ + int r = hdr_test(s); + stbi_rewind(s); + return r; +} + +#define HDR_BUFLEN 1024 +static char *hdr_gettoken(stbi *z, char *buffer) +{ + int len=0; + char c = '\0'; + + c = (char) get8(z); + + while (!at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == HDR_BUFLEN-1) { + // flush to end of line + while (!at_eof(z) && get8(z) != '\n') + ; + break; + } + c = (char) get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if ( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + +static float *hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + char buffer[HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + + + // Check identifier + if (strcmp(hdr_gettoken(s,buffer), "#?RADIANCE") != 0) + return epf("not HDR", "Corrupt HDR image"); + + // Parse header + for(;;) { + token = hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return epf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return epf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return epf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = strtol(token, NULL, 10); + + *x = width; + *y = height; + + *comp = 3; + if (req_comp == 0) req_comp = 3; + + // Read data + hdr_data = (float *) malloc(height * width * req_comp * sizeof(float)); + + // Load image data + // image data is stored as some number of sca + if ( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + getn(s, rgbe, 4); + hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = get8(s); + c2 = get8(s); + len = get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + uint8 rgbe[4]; + rgbe[0] = (uint8) c1; + rgbe[1] = (uint8) c2; + rgbe[2] = (uint8) len; + rgbe[3] = (uint8) get8u(s); + hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + free(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= get8(s); + if (len != width) { free(hdr_data); free(scanline); return epf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) scanline = (stbi_uc *) malloc(width * 4); + + for (k = 0; k < 4; ++k) { + i = 0; + while (i < width) { + count = get8u(s); + if (count > 128) { + // Run + value = get8u(s); + count -= 128; + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = get8u(s); + } + } + } + for (i=0; i < width; ++i) + hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + free(scanline); + } + + return hdr_data; +} + +static float *stbi_hdr_load(stbi *s, int *x, int *y, int *comp, int req_comp) +{ + return hdr_load(s,x,y,comp,req_comp); +} + +static int stbi_hdr_info(stbi *s, int *x, int *y, int *comp) +{ + char buffer[HDR_BUFLEN]; + char *token; + int valid = 0; + + if (strcmp(hdr_gettoken(s,buffer), "#?RADIANCE") != 0) { + stbi_rewind( s ); + return 0; + } + + for(;;) { + token = hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) { + stbi_rewind( s ); + return 0; + } + token = hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) { + stbi_rewind( s ); + return 0; + } + token += 3; + *y = strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) { + stbi_rewind( s ); + return 0; + } + token += 3; + *x = strtol(token, NULL, 10); + *comp = 3; + return 1; +} +#endif // STBI_NO_HDR + +static int stbi_bmp_info(stbi *s, int *x, int *y, int *comp) +{ + int hsz; + if (get8(s) != 'B' || get8(s) != 'M') { + stbi_rewind( s ); + return 0; + } + skip(s,12); + hsz = get32le(s); + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108) { + stbi_rewind( s ); + return 0; + } + if (hsz == 12) { + *x = get16le(s); + *y = get16le(s); + } else { + *x = get32le(s); + *y = get32le(s); + } + if (get16le(s) != 1) { + stbi_rewind( s ); + return 0; + } + *comp = get16le(s) / 8; + return 1; +} + +static int stbi_psd_info(stbi *s, int *x, int *y, int *comp) +{ + int channelCount; + if (get32(s) != 0x38425053) { + stbi_rewind( s ); + return 0; + } + if (get16(s) != 1) { + stbi_rewind( s ); + return 0; + } + skip(s, 6); + channelCount = get16(s); + if (channelCount < 0 || channelCount > 16) { + stbi_rewind( s ); + return 0; + } + *y = get32(s); + *x = get32(s); + if (get16(s) != 8) { + stbi_rewind( s ); + return 0; + } + if (get16(s) != 3) { + stbi_rewind( s ); + return 0; + } + *comp = 4; + return 1; +} + +static int stbi_pic_info(stbi *s, int *x, int *y, int *comp) +{ + int act_comp=0,num_packets=0,chained; + pic_packet_t packets[10]; + + skip(s, 92); + + *x = get16(s); + *y = get16(s); + if (at_eof(s)) return 0; + if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi_rewind( s ); + return 0; + } + + skip(s, 8); + + do { + pic_packet_t *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return 0; + + packet = &packets[num_packets++]; + chained = get8(s); + packet->size = get8u(s); + packet->type = get8u(s); + packet->channel = get8u(s); + act_comp |= packet->channel; + + if (at_eof(s)) { + stbi_rewind( s ); + return 0; + } + if (packet->size != 8) { + stbi_rewind( s ); + return 0; + } + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); + + return 1; +} + +static int stbi_info_main(stbi *s, int *x, int *y, int *comp) +{ + if (stbi_jpeg_info(s, x, y, comp)) + return 1; + if (stbi_png_info(s, x, y, comp)) + return 1; + if (stbi_gif_info(s, x, y, comp)) + return 1; + if (stbi_bmp_info(s, x, y, comp)) + return 1; + if (stbi_psd_info(s, x, y, comp)) + return 1; + if (stbi_pic_info(s, x, y, comp)) + return 1; + #ifndef STBI_NO_HDR + if (stbi_hdr_info(s, x, y, comp)) + return 1; + #endif + // test tga last because it's a crappy test! + if (stbi_tga_info(s, x, y, comp)) + return 1; + return e("unknown image type", "Image not of any known type, or corrupt"); +} + +#ifndef STBI_NO_STDIO +int stbi_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = fopen(filename, "rb"); + int result; + if (!f) return e("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; +} + +int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + int r; + stbi s; + long pos = ftell(f); + start_file(&s, f); + r = stbi_info_main(&s,x,y,comp); + fseek(f,pos,SEEK_SET); + return r; +} +#endif // !STBI_NO_STDIO + +int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi s; + start_mem(&s,buffer,len); + return stbi_info_main(&s,x,y,comp); +} + +int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) +{ + stbi s; + start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi_info_main(&s,x,y,comp); +} + +#endif // STBI_HEADER_FILE_ONLY + +/* + revision history: + 1.33 (2011-07-14) + make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements + 1.32 (2011-07-13) + support for "info" function for all supported filetypes (SpartanJ) + 1.31 (2011-06-20) + a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) + added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) + removed deprecated format-specific test/load functions + removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway + error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) + fix inefficiency in decoding 32-bit BMP (David Woo) + 1.29 (2010-08-16) + various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) + fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) + cast-to-uint8 to fix warnings + 1.26 (2010-07-24) + fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) + refix trans_data warning (Won Chun) + 1.24 (2010-07-12) + perf improvements reading from files on platforms with lock-heavy fgetc() + minor perf improvements for jpeg + deprecated type-specific functions so we'll get feedback if they're needed + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) + removed image *writing* support + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva (i.e. Janez (U+017D)emva) + 1.21 fix use of 'uint8' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + 1.19 bug in interlaced PNG corruption check (found by ryg) + 1.18 2008-08-02 + fix a threading bug (local mutable static) + 1.17 support interlaced PNG + 1.16 major bugfix - convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug + header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi_bmp_load() and stbi_tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant + 0.50 first released version +*/ diff --git a/src/stb_image.h b/src/stb_image.h new file mode 100644 index 000000000..900e0c207 --- /dev/null +++ b/src/stb_image.h @@ -0,0 +1,334 @@ +/* stbi-1.33 - public domain JPEG/PNG reader - http://nothings.org/stb_image.c + when you control the images you're loading + no warranty implied; use at your own risk + + QUICK NOTES: + Primarily of interest to game developers and other people who can + avoid problematic images and only need the trivial interface + + JPEG baseline (no JPEG progressive) + PNG 8-bit-per-channel only + + TGA (not sure what subset, if a subset) + BMP non-1bpp, non-RLE + PSD (composited view only, no extra channels) + + GIF (*comp always reports as 4-channel) + HDR (radiance rgbE format) + PIC (Softimage PIC) + + - decode from memory or through FILE (define STBI_NO_STDIO to remove code) + - decode from arbitrary I/O callbacks + - overridable dequantizing-IDCT, YCbCr-to-RGB conversion (define STBI_SIMD) + + Latest revisions: + 1.33 (2011-07-14) minor fixes suggested by Dave Moore + 1.32 (2011-07-13) info support for all filetypes (SpartanJ) + 1.31 (2011-06-19) a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) added ability to load files via io callbacks (Ben Wenger) + 1.29 (2010-08-16) various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) cast-to-uint8 to fix warnings (Laurent Gomila) + allow trailing 0s at end of image data (Laurent Gomila) + 1.26 (2010-07-24) fix bug in file buffering for PNG reported by SpartanJ + + See end of file for full revision history. + + TODO: + stbi_info support for BMP,PSD,HDR,PIC + + + ============================ Contributors ========================= + + Image formats Optimizations & bugfixes + Sean Barrett (jpeg, png, bmp) Fabian "ryg" Giesen + Nicolas Schulz (hdr, psd) + Jonathan Dummer (tga) Bug fixes & warning fixes + Jean-Marc Lienher (gif) Marc LeBlanc + Tom Seddon (pic) Christpher Lloyd + Thatcher Ulrich (psd) Dave Moore + Won Chun + the Horde3D community + Extensions, features Janez Zemva + Jetro Lauha (stbi_info) Jonathan Blow + James "moose2000" Brown (iPhone PNG) Laurent Gomila + Ben "Disch" Wenger (io callbacks) Aruelien Pocheville + Martin "SpartanJ" Golini Ryamond Barbiero + David Woo + + + If your name should be here but isn't, let Sean know. + +*/ + +#ifndef STBI_INCLUDE_STB_IMAGE_H +#define STBI_INCLUDE_STB_IMAGE_H + +// To get a header file for this, either cut and paste the header, +// or create stb_image.h, #define STBI_HEADER_FILE_ONLY, and +// then include stb_image.c from it. + +//// begin header file //////////////////////////////////////////////////// +// +// Limitations: +// - no jpeg progressive support +// - non-HDR formats support 8-bit samples only (jpeg, png) +// - no delayed line count (jpeg) -- IJG doesn't support either +// - no 1-bit BMP +// - GIF always returns *comp=4 +// +// Basic usage (see HDR discussion below): +// int x,y,n; +// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); +// // ... process data if not NULL ... +// // ... x = width, y = height, n = # 8-bit components per pixel ... +// // ... replace '0' with '1'..'4' to force that many components per pixel +// // ... but 'n' will always be the number that it would have been if you said 0 +// stbi_image_free(data) +// +// Standard parameters: +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *comp -- outputs # of image components in image file +// int req_comp -- if non-zero, # of image components requested in result +// +// The return value from an image loader is an 'unsigned char *' which points +// to the pixel data. The pixel data consists of *y scanlines of *x pixels, +// with each pixel consisting of N interleaved 8-bit components; the first +// pixel pointed to is top-left-most in the image. There is no padding between +// image scanlines or between pixels, regardless of format. The number of +// components N is 'req_comp' if req_comp is non-zero, or *comp otherwise. +// If req_comp is non-zero, *comp has the number of components that _would_ +// have been output otherwise. E.g. if you set req_comp to 4, you will always +// get RGBA output, but you can check *comp to easily see if it's opaque. +// +// An output image with N components has the following components interleaved +// in this order in each pixel: +// +// N=#comp components +// 1 grey +// 2 grey, alpha +// 3 red, green, blue +// 4 red, green, blue, alpha +// +// If image loading fails for any reason, the return value will be NULL, +// and *x, *y, *comp will be unchanged. The function stbi_failure_reason() +// can be queried for an extremely brief, end-user unfriendly explanation +// of why the load failed. Define STBI_NO_FAILURE_STRINGS to avoid +// compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly +// more user-friendly ones. +// +// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. +// +// =========================================================================== +// +// iPhone PNG support: +// +// By default we convert iphone-formatted PNGs back to RGB; nominally they +// would silently load as BGR, except the existing code should have just +// failed on such iPhone PNGs. But you can disable this conversion by +// by calling stbi_convert_iphone_png_to_rgb(0), in which case +// you will always just get the native iphone "format" through. +// +// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per +// pixel to remove any premultiplied alpha *only* if the image file explicitly +// says there's premultiplied data (currently only happens in iPhone images, +// and only if iPhone convert-to-rgb processing is on). +// +// =========================================================================== +// +// HDR image support (disable by defining STBI_NO_HDR) +// +// stb_image now supports loading HDR images in general, and currently +// the Radiance .HDR file format, although the support is provided +// generically. You can still load any file through the existing interface; +// if you attempt to load an HDR file, it will be automatically remapped to +// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// both of these constants can be reconfigured through this interface: +// +// stbi_hdr_to_ldr_gamma(2.2f); +// stbi_hdr_to_ldr_scale(1.0f); +// +// (note, do not use _inverse_ constants; stbi_image will invert them +// appropriately). +// +// Additionally, there is a new, parallel interface for loading files as +// (linear) floats to preserve the full dynamic range: +// +// float *data = stbi_loadf(filename, &x, &y, &n, 0); +// +// If you load LDR images through this interface, those images will +// be promoted to floating point values, run through the inverse of +// constants corresponding to the above: +// +// stbi_ldr_to_hdr_scale(1.0f); +// stbi_ldr_to_hdr_gamma(2.2f); +// +// Finally, given a filename (or an open file or memory block--see header +// file for details) containing image data, you can query for the "most +// appropriate" interface to use (that is, whether the image is HDR or +// not), using: +// +// stbi_is_hdr(char *filename); +// +// =========================================================================== +// +// I/O callbacks +// +// I/O callbacks allow you to read from arbitrary sources, like packaged +// files or some other source. Data read from callbacks are processed +// through a small internal buffer (currently 128 bytes) to try to reduce +// overhead. +// +// The three functions you must define are "read" (reads some bytes of data), +// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). + + +#define STBI_NO_HDR // RaySan: not required by raylib + +#ifndef STBI_NO_STDIO + +#if defined(_MSC_VER) && _MSC_VER >= 0x1400 +#define _CRT_SECURE_NO_WARNINGS // suppress bogus warnings about fopen() +#endif + +#include +#endif + +#define STBI_VERSION 1 + +enum +{ + STBI_default = 0, // only used for req_comp + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 +}; + +typedef unsigned char stbi_uc; + +#ifdef __cplusplus +extern "C" { +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// PRIMARY API - works on images of any type +// + +// +// load image by filename, open file, or memory buffer +// + +extern stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); + +#ifndef STBI_NO_STDIO +extern stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +// for stbi_load_from_file, file pointer is left pointing immediately after image +#endif + +typedef struct +{ + int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip) (void *user,unsigned n); // skip the next 'n' bytes + int (*eof) (void *user); // returns nonzero if we are at end of file/data +} stbi_io_callbacks; + +extern stbi_uc *stbi_load_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp); + +#ifndef STBI_NO_HDR + extern float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); + + #ifndef STBI_NO_STDIO + extern float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp); + extern float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); + #endif + + extern float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp); + + extern void stbi_hdr_to_ldr_gamma(float gamma); + extern void stbi_hdr_to_ldr_scale(float scale); + + extern void stbi_ldr_to_hdr_gamma(float gamma); + extern void stbi_ldr_to_hdr_scale(float scale); +#endif // STBI_NO_HDR + +// stbi_is_hdr is always defined +extern int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); +extern int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO +extern int stbi_is_hdr (char const *filename); +extern int stbi_is_hdr_from_file(FILE *f); +#endif // STBI_NO_STDIO + + +// get a VERY brief reason for failure +// NOT THREADSAFE +extern const char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +extern void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +extern int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +extern int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); + +#ifndef STBI_NO_STDIO +extern int stbi_info (char const *filename, int *x, int *y, int *comp); +extern int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); + +#endif + + + +// for image formats that explicitly notate that they have premultiplied alpha, +// we just return the colors as stored in the file. set this flag to force +// unpremultiplication. results are undefined if the unpremultiply overflow. +extern void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); + +// indicate whether we should process iphone images back to canonical format, +// or just pass them through "as-is" +extern void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); + + +// ZLIB client - used by PNG, available for other purposes + +extern char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +extern char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +extern int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +extern char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +extern int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + + +// define faster low-level operations (typically SIMD support) +#ifdef STBI_SIMD +typedef void (*stbi_idct_8x8)(stbi_uc *out, int out_stride, short data[64], unsigned short *dequantize); +// compute an integer IDCT on "input" +// input[x] = data[x] * dequantize[x] +// write results to 'out': 64 samples, each run of 8 spaced by 'out_stride' +// CLAMP results to 0..255 +typedef void (*stbi_YCbCr_to_RGB_run)(stbi_uc *output, stbi_uc const *y, stbi_uc const *cb, stbi_uc const *cr, int count, int step); +// compute a conversion from YCbCr to RGB +// 'count' pixels +// write pixels to 'output'; each pixel is 'step' bytes (either 3 or 4; if 4, write '255' as 4th), order R,G,B +// y: Y input channel +// cb: Cb input channel; scale/biased to be 0..255 +// cr: Cr input channel; scale/biased to be 0..255 + +extern void stbi_install_idct(stbi_idct_8x8 func); +extern void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func); +#endif // STBI_SIMD + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H \ No newline at end of file diff --git a/src/stb_vorbis.c b/src/stb_vorbis.c new file mode 100644 index 000000000..cf7ad1d43 --- /dev/null +++ b/src/stb_vorbis.c @@ -0,0 +1,5039 @@ +// Ogg Vorbis I audio decoder -- version 0.99996 +// +// Written in April 2007 by Sean Barrett, sponsored by RAD Game Tools. +// +// Placed in the public domain April 2007 by the author: no copyright is +// claimed, and you may use it for any purpose you like. +// +// No warranty for any purpose is expressed or implied by the author (nor +// by RAD Game Tools). Report bugs and send enhancements to the author. +// +// Get the latest version and other information at: +// http://nothings.org/stb_vorbis/ + +// Todo: +// +// - seeking (note you can seek yourself using the pushdata API) +// +// Limitations: +// +// - floor 0 not supported (used in old ogg vorbis files) +// - lossless sample-truncation at beginning ignored +// - cannot concatenate multiple vorbis streams +// - sample positions are 32-bit, limiting seekable 192Khz +// files to around 6 hours (Ogg supports 64-bit) +// +// All of these limitations may be removed in future versions. + +#ifndef STB_VORBIS_HEADER_ONLY + +// global configuration settings (e.g. set these in the project/makefile), +// or just set them in this file at the top (although ideally the first few +// should be visible when the header file is compiled too, although it's not +// crucial) + +// STB_VORBIS_NO_PUSHDATA_API +// does not compile the code for the various stb_vorbis_*_pushdata() +// functions +// #define STB_VORBIS_NO_PUSHDATA_API + +// STB_VORBIS_NO_PULLDATA_API +// does not compile the code for the non-pushdata APIs +// #define STB_VORBIS_NO_PULLDATA_API + +// STB_VORBIS_NO_STDIO +// does not compile the code for the APIs that use FILE *s internally +// or externally (implied by STB_VORBIS_NO_PULLDATA_API) +// #define STB_VORBIS_NO_STDIO + +// STB_VORBIS_NO_INTEGER_CONVERSION +// does not compile the code for converting audio sample data from +// float to integer (implied by STB_VORBIS_NO_PULLDATA_API) +// #define STB_VORBIS_NO_INTEGER_CONVERSION + +// STB_VORBIS_NO_FAST_SCALED_FLOAT +// does not use a fast float-to-int trick to accelerate float-to-int on +// most platforms which requires endianness be defined correctly. +//#define STB_VORBIS_NO_FAST_SCALED_FLOAT + + +// STB_VORBIS_MAX_CHANNELS [number] +// globally define this to the maximum number of channels you need. +// The spec does not put a restriction on channels except that +// the count is stored in a byte, so 255 is the hard limit. +// Reducing this saves about 16 bytes per value, so using 16 saves +// (255-16)*16 or around 4KB. Plus anything other memory usage +// I forgot to account for. Can probably go as low as 8 (7.1 audio), +// 6 (5.1 audio), or 2 (stereo only). +#ifndef STB_VORBIS_MAX_CHANNELS +#define STB_VORBIS_MAX_CHANNELS 16 // enough for anyone? +#endif + +// STB_VORBIS_PUSHDATA_CRC_COUNT [number] +// after a flush_pushdata(), stb_vorbis begins scanning for the +// next valid page, without backtracking. when it finds something +// that looks like a page, it streams through it and verifies its +// CRC32. Should that validation fail, it keeps scanning. But it's +// possible that _while_ streaming through to check the CRC32 of +// one candidate page, it sees another candidate page. This #define +// determines how many "overlapping" candidate pages it can search +// at once. Note that "real" pages are typically ~4KB to ~8KB, whereas +// garbage pages could be as big as 64KB, but probably average ~16KB. +// So don't hose ourselves by scanning an apparent 64KB page and +// missing a ton of real ones in the interim; so minimum of 2 +#ifndef STB_VORBIS_PUSHDATA_CRC_COUNT +#define STB_VORBIS_PUSHDATA_CRC_COUNT 4 +#endif + +// STB_VORBIS_FAST_HUFFMAN_LENGTH [number] +// sets the log size of the huffman-acceleration table. Maximum +// supported value is 24. with larger numbers, more decodings are O(1), +// but the table size is larger so worse cache missing, so you'll have +// to probe (and try multiple ogg vorbis files) to find the sweet spot. +#ifndef STB_VORBIS_FAST_HUFFMAN_LENGTH +#define STB_VORBIS_FAST_HUFFMAN_LENGTH 10 +#endif + +// STB_VORBIS_FAST_BINARY_LENGTH [number] +// sets the log size of the binary-search acceleration table. this +// is used in similar fashion to the fast-huffman size to set initial +// parameters for the binary search + +// STB_VORBIS_FAST_HUFFMAN_INT +// The fast huffman tables are much more efficient if they can be +// stored as 16-bit results instead of 32-bit results. This restricts +// the codebooks to having only 65535 possible outcomes, though. +// (At least, accelerated by the huffman table.) +#ifndef STB_VORBIS_FAST_HUFFMAN_INT +#define STB_VORBIS_FAST_HUFFMAN_SHORT +#endif + +// STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH +// If the 'fast huffman' search doesn't succeed, then stb_vorbis falls +// back on binary searching for the correct one. This requires storing +// extra tables with the huffman codes in sorted order. Defining this +// symbol trades off space for speed by forcing a linear search in the +// non-fast case, except for "sparse" codebooks. +// #define STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH + +// STB_VORBIS_DIVIDES_IN_RESIDUE +// stb_vorbis precomputes the result of the scalar residue decoding +// that would otherwise require a divide per chunk. you can trade off +// space for time by defining this symbol. +// #define STB_VORBIS_DIVIDES_IN_RESIDUE + +// STB_VORBIS_DIVIDES_IN_CODEBOOK +// vorbis VQ codebooks can be encoded two ways: with every case explicitly +// stored, or with all elements being chosen from a small range of values, +// and all values possible in all elements. By default, stb_vorbis expands +// this latter kind out to look like the former kind for ease of decoding, +// because otherwise an integer divide-per-vector-element is required to +// unpack the index. If you define STB_VORBIS_DIVIDES_IN_CODEBOOK, you can +// trade off storage for speed. +//#define STB_VORBIS_DIVIDES_IN_CODEBOOK + +// STB_VORBIS_CODEBOOK_SHORTS +// The vorbis file format encodes VQ codebook floats as ax+b where a and +// b are floating point per-codebook constants, and x is a 16-bit int. +// Normally, stb_vorbis decodes them to floats rather than leaving them +// as 16-bit ints and computing ax+b while decoding. This is a speed/space +// tradeoff; you can save space by defining this flag. +#ifndef STB_VORBIS_CODEBOOK_SHORTS +#define STB_VORBIS_CODEBOOK_FLOATS +#endif + +// STB_VORBIS_DIVIDE_TABLE +// this replaces small integer divides in the floor decode loop with +// table lookups. made less than 1% difference, so disabled by default. + +// STB_VORBIS_NO_INLINE_DECODE +// disables the inlining of the scalar codebook fast-huffman decode. +// might save a little codespace; useful for debugging +// #define STB_VORBIS_NO_INLINE_DECODE + +// STB_VORBIS_NO_DEFER_FLOOR +// Normally we only decode the floor without synthesizing the actual +// full curve. We can instead synthesize the curve immediately. This +// requires more memory and is very likely slower, so I don't think +// you'd ever want to do it except for debugging. +// #define STB_VORBIS_NO_DEFER_FLOOR + + + + +////////////////////////////////////////////////////////////////////////////// + +#ifdef STB_VORBIS_NO_PULLDATA_API + #define STB_VORBIS_NO_INTEGER_CONVERSION + #define STB_VORBIS_NO_STDIO +#endif + +#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) + #define STB_VORBIS_NO_STDIO 1 +#endif + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT + + // only need endianness for fast-float-to-int, which we don't + // use for pushdata + + #ifndef STB_VORBIS_BIG_ENDIAN + #define STB_VORBIS_ENDIAN 0 + #else + #define STB_VORBIS_ENDIAN 1 + #endif + +#endif +#endif + + +#ifndef STB_VORBIS_NO_STDIO +#include +#endif + +#ifndef STB_VORBIS_NO_CRT +#include +#include +#include +#include +#if !(defined(__APPLE__) || defined(MACOSX) || defined(macintosh) || defined(Macintosh)) +#include +#endif +#else +#define NULL 0 +#endif + +#ifndef _MSC_VER + #if __GNUC__ + #define __forceinline inline + #else + #define __forceinline + #endif +#endif + +#if STB_VORBIS_MAX_CHANNELS > 256 +#error "Value of STB_VORBIS_MAX_CHANNELS outside of allowed range" +#endif + +#if STB_VORBIS_FAST_HUFFMAN_LENGTH > 24 +#error "Value of STB_VORBIS_FAST_HUFFMAN_LENGTH outside of allowed range" +#endif + + +#define MAX_BLOCKSIZE_LOG 13 // from specification +#define MAX_BLOCKSIZE (1 << MAX_BLOCKSIZE_LOG) + + +typedef unsigned char uint8; +typedef signed char int8; +typedef unsigned short uint16; +typedef signed short int16; +typedef unsigned int uint32; +typedef signed int int32; + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +#ifdef STB_VORBIS_CODEBOOK_FLOATS +typedef float codetype; +#else +typedef uint16 codetype; +#endif + +// @NOTE +// +// Some arrays below are tagged "//varies", which means it's actually +// a variable-sized piece of data, but rather than malloc I assume it's +// small enough it's better to just allocate it all together with the +// main thing +// +// Most of the variables are specified with the smallest size I could pack +// them into. It might give better performance to make them all full-sized +// integers. It should be safe to freely rearrange the structures or change +// the sizes larger--nothing relies on silently truncating etc., nor the +// order of variables. + +#define FAST_HUFFMAN_TABLE_SIZE (1 << STB_VORBIS_FAST_HUFFMAN_LENGTH) +#define FAST_HUFFMAN_TABLE_MASK (FAST_HUFFMAN_TABLE_SIZE - 1) + +typedef struct +{ + int dimensions, entries; + uint8 *codeword_lengths; + float minimum_value; + float delta_value; + uint8 value_bits; + uint8 lookup_type; + uint8 sequence_p; + uint8 sparse; + uint32 lookup_values; + codetype *multiplicands; + uint32 *codewords; + #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT + int16 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; + #else + int32 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; + #endif + uint32 *sorted_codewords; + int *sorted_values; + int sorted_entries; +} Codebook; + +typedef struct +{ + uint8 order; + uint16 rate; + uint16 bark_map_size; + uint8 amplitude_bits; + uint8 amplitude_offset; + uint8 number_of_books; + uint8 book_list[16]; // varies +} Floor0; + +typedef struct +{ + uint8 partitions; + uint8 partition_class_list[32]; // varies + uint8 class_dimensions[16]; // varies + uint8 class_subclasses[16]; // varies + uint8 class_masterbooks[16]; // varies + int16 subclass_books[16][8]; // varies + uint16 Xlist[31*8+2]; // varies + uint8 sorted_order[31*8+2]; + uint8 neighbors[31*8+2][2]; + uint8 floor1_multiplier; + uint8 rangebits; + int values; +} Floor1; + +typedef union +{ + Floor0 floor0; + Floor1 floor1; +} Floor; + +typedef struct +{ + uint32 begin, end; + uint32 part_size; + uint8 classifications; + uint8 classbook; + uint8 **classdata; + int16 (*residue_books)[8]; +} Residue; + +typedef struct +{ + uint8 magnitude; + uint8 angle; + uint8 mux; +} MappingChannel; + +typedef struct +{ + uint16 coupling_steps; + MappingChannel *chan; + uint8 submaps; + uint8 submap_floor[15]; // varies + uint8 submap_residue[15]; // varies +} Mapping; + +typedef struct +{ + uint8 blockflag; + uint8 mapping; + uint16 windowtype; + uint16 transformtype; +} Mode; + +typedef struct +{ + uint32 goal_crc; // expected crc if match + int bytes_left; // bytes left in packet + uint32 crc_so_far; // running crc + int bytes_done; // bytes processed in _current_ chunk + uint32 sample_loc; // granule pos encoded in page +} CRCscan; + +typedef struct +{ + uint32 page_start, page_end; + uint32 after_previous_page_start; + uint32 first_decoded_sample; + uint32 last_decoded_sample; +} ProbedPage; + +struct stb_vorbis +{ + // user-accessible info + unsigned int sample_rate; + int channels; + + unsigned int setup_memory_required; + unsigned int temp_memory_required; + unsigned int setup_temp_memory_required; + + // input config +#ifndef STB_VORBIS_NO_STDIO + FILE *f; + uint32 f_start; + int close_on_free; +#endif + + uint8 *stream; + uint8 *stream_start; + uint8 *stream_end; + + uint32 stream_len; + + uint8 push_mode; + + uint32 first_audio_page_offset; + + ProbedPage p_first, p_last; + + // memory management + stb_vorbis_alloc alloc; + int setup_offset; + int temp_offset; + + // run-time results + int eof; + enum STBVorbisError error; + + // user-useful data + + // header info + int blocksize[2]; + int blocksize_0, blocksize_1; + int codebook_count; + Codebook *codebooks; + int floor_count; + uint16 floor_types[64]; // varies + Floor *floor_config; + int residue_count; + uint16 residue_types[64]; // varies + Residue *residue_config; + int mapping_count; + Mapping *mapping; + int mode_count; + Mode mode_config[64]; // varies + + uint32 total_samples; + + // decode buffer + float *channel_buffers[STB_VORBIS_MAX_CHANNELS]; + float *outputs [STB_VORBIS_MAX_CHANNELS]; + + float *previous_window[STB_VORBIS_MAX_CHANNELS]; + int previous_length; + + #ifndef STB_VORBIS_NO_DEFER_FLOOR + int16 *finalY[STB_VORBIS_MAX_CHANNELS]; + #else + float *floor_buffers[STB_VORBIS_MAX_CHANNELS]; + #endif + + uint32 current_loc; // sample location of next frame to decode + int current_loc_valid; + + // per-blocksize precomputed data + + // twiddle factors + float *A[2],*B[2],*C[2]; + float *window[2]; + uint16 *bit_reverse[2]; + + // current page/packet/segment streaming info + uint32 serial; // stream serial number for verification + int last_page; + int segment_count; + uint8 segments[255]; + uint8 page_flag; + uint8 bytes_in_seg; + uint8 first_decode; + int next_seg; + int last_seg; // flag that we're on the last segment + int last_seg_which; // what was the segment number of the last seg? + uint32 acc; + int valid_bits; + int packet_bytes; + int end_seg_with_known_loc; + uint32 known_loc_for_packet; + int discard_samples_deferred; + uint32 samples_output; + + // push mode scanning + int page_crc_tests; // only in push_mode: number of tests active; -1 if not searching +#ifndef STB_VORBIS_NO_PUSHDATA_API + CRCscan scan[STB_VORBIS_PUSHDATA_CRC_COUNT]; +#endif + + // sample-access + int channel_buffer_start; + int channel_buffer_end; +}; + +extern int my_prof(int slot); +//#define stb_prof my_prof + +#ifndef stb_prof +#define stb_prof(x) 0 +#endif + +#if defined(STB_VORBIS_NO_PUSHDATA_API) + #define IS_PUSH_MODE(f) FALSE +#elif defined(STB_VORBIS_NO_PULLDATA_API) + #define IS_PUSH_MODE(f) TRUE +#else + #define IS_PUSH_MODE(f) ((f)->push_mode) +#endif + +typedef struct stb_vorbis vorb; + +static int error(vorb *f, enum STBVorbisError e) +{ + f->error = e; + if (!f->eof && e != VORBIS_need_more_data) { + f->error=e; // breakpoint for debugging + } + return 0; +} + + +// these functions are used for allocating temporary memory +// while decoding. if you can afford the stack space, use +// alloca(); otherwise, provide a temp buffer and it will +// allocate out of those. + +#define array_size_required(count,size) (count*(sizeof(void *)+(size))) + +#define temp_alloc(f,size) (f->alloc.alloc_buffer ? setup_temp_malloc(f,size) : alloca(size)) +#ifdef dealloca +#define temp_free(f,p) (f->alloc.alloc_buffer ? 0 : dealloca(size)) +#else +#define temp_free(f,p) 0 +#endif +#define temp_alloc_save(f) ((f)->temp_offset) +#define temp_alloc_restore(f,p) ((f)->temp_offset = (p)) + +#define temp_block_array(f,count,size) make_block_array(temp_alloc(f,array_size_required(count,size)), count, size) + +// given a sufficiently large block of memory, make an array of pointers to subblocks of it +static void *make_block_array(void *mem, int count, int size) +{ + int i; + void ** p = (void **) mem; + char *q = (char *) (p + count); + for (i=0; i < count; ++i) { + p[i] = q; + q += size; + } + return p; +} + +static void *setup_malloc(vorb *f, int sz) +{ + sz = (sz+3) & ~3; + f->setup_memory_required += sz; + if (f->alloc.alloc_buffer) { + void *p = (char *) f->alloc.alloc_buffer + f->setup_offset; + if (f->setup_offset + sz > f->temp_offset) return NULL; + f->setup_offset += sz; + return p; + } + return sz ? malloc(sz) : NULL; +} + +static void setup_free(vorb *f, void *p) +{ + if (f->alloc.alloc_buffer) return; // do nothing; setup mem is not a stack + free(p); +} + +static void *setup_temp_malloc(vorb *f, int sz) +{ + sz = (sz+3) & ~3; + if (f->alloc.alloc_buffer) { + if (f->temp_offset - sz < f->setup_offset) return NULL; + f->temp_offset -= sz; + return (char *) f->alloc.alloc_buffer + f->temp_offset; + } + return malloc(sz); +} + +static void setup_temp_free(vorb *f, void *p, size_t sz) +{ + if (f->alloc.alloc_buffer) { + f->temp_offset += (sz+3)&~3; + return; + } + free(p); +} + +#define CRC32_POLY 0x04c11db7 // from spec + +static uint32 crc_table[256]; +static void crc32_init(void) +{ + int i,j; + uint32 s; + for(i=0; i < 256; i++) { + for (s=i<<24, j=0; j < 8; ++j) + s = (s << 1) ^ (s >= (1<<31) ? CRC32_POLY : 0); + crc_table[i] = s; + } +} + +static __forceinline uint32 crc32_update(uint32 crc, uint8 byte) +{ + return (crc << 8) ^ crc_table[byte ^ (crc >> 24)]; +} + + +// used in setup, and for huffman that doesn't go fast path +static unsigned int bit_reverse(unsigned int n) +{ + n = ((n & 0xAAAAAAAA) >> 1) | ((n & 0x55555555) << 1); + n = ((n & 0xCCCCCCCC) >> 2) | ((n & 0x33333333) << 2); + n = ((n & 0xF0F0F0F0) >> 4) | ((n & 0x0F0F0F0F) << 4); + n = ((n & 0xFF00FF00) >> 8) | ((n & 0x00FF00FF) << 8); + return (n >> 16) | (n << 16); +} + +static float square(float x) +{ + return x*x; +} + +// this is a weird definition of log2() for which log2(1) = 1, log2(2) = 2, log2(4) = 3 +// as required by the specification. fast(?) implementation from stb.h +// @OPTIMIZE: called multiple times per-packet with "constants"; move to setup +static int ilog(int32 n) +{ + static signed char log2_4[16] = { 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4 }; + + // 2 compares if n < 16, 3 compares otherwise (4 if signed or n > 1<<29) + if (n < (1U << 14)) + if (n < (1U << 4)) return 0 + log2_4[n ]; + else if (n < (1U << 9)) return 5 + log2_4[n >> 5]; + else return 10 + log2_4[n >> 10]; + else if (n < (1U << 24)) + if (n < (1U << 19)) return 15 + log2_4[n >> 15]; + else return 20 + log2_4[n >> 20]; + else if (n < (1U << 29)) return 25 + log2_4[n >> 25]; + else if (n < (1U << 31)) return 30 + log2_4[n >> 30]; + else return 0; // signed n returns 0 +} + +#ifndef M_PI + #define M_PI 3.14159265358979323846264f // from CRC +#endif + +// code length assigned to a value with no huffman encoding +#define NO_CODE 255 + +/////////////////////// LEAF SETUP FUNCTIONS ////////////////////////// +// +// these functions are only called at setup, and only a few times +// per file + +static float float32_unpack(uint32 x) +{ + // from the specification + uint32 mantissa = x & 0x1fffff; + uint32 sign = x & 0x80000000; + uint32 exp = (x & 0x7fe00000) >> 21; + double res = sign ? -(double)mantissa : (double)mantissa; + return (float) ldexp((float)res, exp-788); +} + + +// zlib & jpeg huffman tables assume that the output symbols +// can either be arbitrarily arranged, or have monotonically +// increasing frequencies--they rely on the lengths being sorted; +// this makes for a very simple generation algorithm. +// vorbis allows a huffman table with non-sorted lengths. This +// requires a more sophisticated construction, since symbols in +// order do not map to huffman codes "in order". +static void add_entry(Codebook *c, uint32 huff_code, int symbol, int count, int len, uint32 *values) +{ + if (!c->sparse) { + c->codewords [symbol] = huff_code; + } else { + c->codewords [count] = huff_code; + c->codeword_lengths[count] = len; + values [count] = symbol; + } +} + +static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) +{ + int i,k,m=0; + uint32 available[32]; + + memset(available, 0, sizeof(available)); + // find the first entry + for (k=0; k < n; ++k) if (len[k] < NO_CODE) break; + if (k == n) { assert(c->sorted_entries == 0); return TRUE; } + // add to the list + add_entry(c, 0, k, m++, len[k], values); + // add all available leaves + for (i=1; i <= len[k]; ++i) + available[i] = 1 << (32-i); + // note that the above code treats the first case specially, + // but it's really the same as the following code, so they + // could probably be combined (except the initial code is 0, + // and I use 0 in available[] to mean 'empty') + for (i=k+1; i < n; ++i) { + uint32 res; + int z = len[i], y; + if (z == NO_CODE) continue; + // find lowest available leaf (should always be earliest, + // which is what the specification calls for) + // note that this property, and the fact we can never have + // more than one free leaf at a given level, isn't totally + // trivial to prove, but it seems true and the assert never + // fires, so! + while (z > 0 && !available[z]) --z; + if (z == 0) { assert(0); return FALSE; } + res = available[z]; + available[z] = 0; + add_entry(c, bit_reverse(res), i, m++, len[i], values); + // propogate availability up the tree + if (z != len[i]) { + for (y=len[i]; y > z; --y) { + assert(available[y] == 0); + available[y] = res + (1 << (32-y)); + } + } + } + return TRUE; +} + +// accelerated huffman table allows fast O(1) match of all symbols +// of length <= STB_VORBIS_FAST_HUFFMAN_LENGTH +static void compute_accelerated_huffman(Codebook *c) +{ + int i, len; + for (i=0; i < FAST_HUFFMAN_TABLE_SIZE; ++i) + c->fast_huffman[i] = -1; + + len = c->sparse ? c->sorted_entries : c->entries; + #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT + if (len > 32767) len = 32767; // largest possible value we can encode! + #endif + for (i=0; i < len; ++i) { + if (c->codeword_lengths[i] <= STB_VORBIS_FAST_HUFFMAN_LENGTH) { + uint32 z = c->sparse ? bit_reverse(c->sorted_codewords[i]) : c->codewords[i]; + // set table entries for all bit combinations in the higher bits + while (z < FAST_HUFFMAN_TABLE_SIZE) { + c->fast_huffman[z] = i; + z += 1 << c->codeword_lengths[i]; + } + } + } +} + +static int uint32_compare(const void *p, const void *q) +{ + uint32 x = * (uint32 *) p; + uint32 y = * (uint32 *) q; + return x < y ? -1 : x > y; +} + +static int include_in_sort(Codebook *c, uint8 len) +{ + if (c->sparse) { assert(len != NO_CODE); return TRUE; } + if (len == NO_CODE) return FALSE; + if (len > STB_VORBIS_FAST_HUFFMAN_LENGTH) return TRUE; + return FALSE; +} + +// if the fast table above doesn't work, we want to binary +// search them... need to reverse the bits +static void compute_sorted_huffman(Codebook *c, uint8 *lengths, uint32 *values) +{ + int i, len; + // build a list of all the entries + // OPTIMIZATION: don't include the short ones, since they'll be caught by FAST_HUFFMAN. + // this is kind of a frivolous optimization--I don't see any performance improvement, + // but it's like 4 extra lines of code, so. + if (!c->sparse) { + int k = 0; + for (i=0; i < c->entries; ++i) + if (include_in_sort(c, lengths[i])) + c->sorted_codewords[k++] = bit_reverse(c->codewords[i]); + assert(k == c->sorted_entries); + } else { + for (i=0; i < c->sorted_entries; ++i) + c->sorted_codewords[i] = bit_reverse(c->codewords[i]); + } + + qsort(c->sorted_codewords, c->sorted_entries, sizeof(c->sorted_codewords[0]), uint32_compare); + c->sorted_codewords[c->sorted_entries] = 0xffffffff; + + len = c->sparse ? c->sorted_entries : c->entries; + // now we need to indicate how they correspond; we could either + // #1: sort a different data structure that says who they correspond to + // #2: for each sorted entry, search the original list to find who corresponds + // #3: for each original entry, find the sorted entry + // #1 requires extra storage, #2 is slow, #3 can use binary search! + for (i=0; i < len; ++i) { + int huff_len = c->sparse ? lengths[values[i]] : lengths[i]; + if (include_in_sort(c,huff_len)) { + uint32 code = bit_reverse(c->codewords[i]); + int x=0, n=c->sorted_entries; + while (n > 1) { + // invariant: sc[x] <= code < sc[x+n] + int m = x + (n >> 1); + if (c->sorted_codewords[m] <= code) { + x = m; + n -= (n>>1); + } else { + n >>= 1; + } + } + assert(c->sorted_codewords[x] == code); + if (c->sparse) { + c->sorted_values[x] = values[i]; + c->codeword_lengths[x] = huff_len; + } else { + c->sorted_values[x] = i; + } + } + } +} + +// only run while parsing the header (3 times) +static int vorbis_validate(uint8 *data) +{ + static uint8 vorbis[6] = { 'v', 'o', 'r', 'b', 'i', 's' }; + return memcmp(data, vorbis, 6) == 0; +} + +// called from setup only, once per code book +// (formula implied by specification) +static int lookup1_values(int entries, int dim) +{ + int r = (int) floor(exp((float) log((float) entries) / dim)); + if ((int) floor(pow((float) r+1, dim)) <= entries) // (int) cast for MinGW warning; + ++r; // floor() to avoid _ftol() when non-CRT + assert(pow((float) r+1, dim) > entries); + assert((int) floor(pow((float) r, dim)) <= entries); // (int),floor() as above + return r; +} + +// called twice per file +static void compute_twiddle_factors(int n, float *A, float *B, float *C) +{ + int n4 = n >> 2, n8 = n >> 3; + int k,k2; + + for (k=k2=0; k < n4; ++k,k2+=2) { + A[k2 ] = (float) cos(4*k*M_PI/n); + A[k2+1] = (float) -sin(4*k*M_PI/n); + B[k2 ] = (float) cos((k2+1)*M_PI/n/2) * 0.5f; + B[k2+1] = (float) sin((k2+1)*M_PI/n/2) * 0.5f; + } + for (k=k2=0; k < n8; ++k,k2+=2) { + C[k2 ] = (float) cos(2*(k2+1)*M_PI/n); + C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n); + } +} + +static void compute_window(int n, float *window) +{ + int n2 = n >> 1, i; + for (i=0; i < n2; ++i) + window[i] = (float) sin(0.5 * M_PI * square((float) sin((i - 0 + 0.5) / n2 * 0.5 * M_PI))); +} + +static void compute_bitreverse(int n, uint16 *rev) +{ + int ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + int i, n8 = n >> 3; + for (i=0; i < n8; ++i) + rev[i] = (bit_reverse(i) >> (32-ld+3)) << 2; +} + +static int init_blocksize(vorb *f, int b, int n) +{ + int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3; + f->A[b] = (float *) setup_malloc(f, sizeof(float) * n2); + f->B[b] = (float *) setup_malloc(f, sizeof(float) * n2); + f->C[b] = (float *) setup_malloc(f, sizeof(float) * n4); + if (!f->A[b] || !f->B[b] || !f->C[b]) return error(f, VORBIS_outofmem); + compute_twiddle_factors(n, f->A[b], f->B[b], f->C[b]); + f->window[b] = (float *) setup_malloc(f, sizeof(float) * n2); + if (!f->window[b]) return error(f, VORBIS_outofmem); + compute_window(n, f->window[b]); + f->bit_reverse[b] = (uint16 *) setup_malloc(f, sizeof(uint16) * n8); + if (!f->bit_reverse[b]) return error(f, VORBIS_outofmem); + compute_bitreverse(n, f->bit_reverse[b]); + return TRUE; +} + +static void neighbors(uint16 *x, int n, int *plow, int *phigh) +{ + int low = -1; + int high = 65536; + int i; + for (i=0; i < n; ++i) { + if (x[i] > low && x[i] < x[n]) { *plow = i; low = x[i]; } + if (x[i] < high && x[i] > x[n]) { *phigh = i; high = x[i]; } + } +} + +// this has been repurposed so y is now the original index instead of y +typedef struct +{ + uint16 x,y; +} Point; + +int point_compare(const void *p, const void *q) +{ + Point *a = (Point *) p; + Point *b = (Point *) q; + return a->x < b->x ? -1 : a->x > b->x; +} + +// +/////////////////////// END LEAF SETUP FUNCTIONS ////////////////////////// + + +#if defined(STB_VORBIS_NO_STDIO) + #define USE_MEMORY(z) TRUE +#else + #define USE_MEMORY(z) ((z)->stream) +#endif + +static uint8 get8(vorb *z) +{ + if (USE_MEMORY(z)) { + if (z->stream >= z->stream_end) { z->eof = TRUE; return 0; } + return *z->stream++; + } + + #ifndef STB_VORBIS_NO_STDIO + { + int c = fgetc(z->f); + if (c == EOF) { z->eof = TRUE; return 0; } + return c; + } + #endif +} + +static uint32 get32(vorb *f) +{ + uint32 x; + x = get8(f); + x += get8(f) << 8; + x += get8(f) << 16; + x += get8(f) << 24; + return x; +} + +static int getn(vorb *z, uint8 *data, int n) +{ + if (USE_MEMORY(z)) { + if (z->stream+n > z->stream_end) { z->eof = 1; return 0; } + memcpy(data, z->stream, n); + z->stream += n; + return 1; + } + + #ifndef STB_VORBIS_NO_STDIO + if (fread(data, n, 1, z->f) == 1) + return 1; + else { + z->eof = 1; + return 0; + } + #endif +} + +static void skip(vorb *z, int n) +{ + if (USE_MEMORY(z)) { + z->stream += n; + if (z->stream >= z->stream_end) z->eof = 1; + return; + } + #ifndef STB_VORBIS_NO_STDIO + { + long x = ftell(z->f); + fseek(z->f, x+n, SEEK_SET); + } + #endif +} + +static int set_file_offset(stb_vorbis *f, unsigned int loc) +{ + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (f->push_mode) return 0; + #endif + f->eof = 0; + if (USE_MEMORY(f)) { + if (f->stream_start + loc >= f->stream_end || f->stream_start + loc < f->stream_start) { + f->stream = f->stream_end; + f->eof = 1; + return 0; + } else { + f->stream = f->stream_start + loc; + return 1; + } + } + #ifndef STB_VORBIS_NO_STDIO + if (loc + f->f_start < loc || loc >= 0x80000000) { + loc = 0x7fffffff; + f->eof = 1; + } else { + loc += f->f_start; + } + if (!fseek(f->f, loc, SEEK_SET)) + return 1; + f->eof = 1; + fseek(f->f, f->f_start, SEEK_END); + return 0; + #endif +} + + +static uint8 ogg_page_header[4] = { 0x4f, 0x67, 0x67, 0x53 }; + +static int capture_pattern(vorb *f) +{ + if (0x4f != get8(f)) return FALSE; + if (0x67 != get8(f)) return FALSE; + if (0x67 != get8(f)) return FALSE; + if (0x53 != get8(f)) return FALSE; + return TRUE; +} + +#define PAGEFLAG_continued_packet 1 +#define PAGEFLAG_first_page 2 +#define PAGEFLAG_last_page 4 + +static int start_page_no_capturepattern(vorb *f) +{ + uint32 loc0,loc1,n,i; + // stream structure version + if (0 != get8(f)) return error(f, VORBIS_invalid_stream_structure_version); + // header flag + f->page_flag = get8(f); + // absolute granule position + loc0 = get32(f); + loc1 = get32(f); + // @TODO: validate loc0,loc1 as valid positions? + // stream serial number -- vorbis doesn't interleave, so discard + get32(f); + //if (f->serial != get32(f)) return error(f, VORBIS_incorrect_stream_serial_number); + // page sequence number + n = get32(f); + f->last_page = n; + // CRC32 + get32(f); + // page_segments + f->segment_count = get8(f); + if (!getn(f, f->segments, f->segment_count)) + return error(f, VORBIS_unexpected_eof); + // assume we _don't_ know any the sample position of any segments + f->end_seg_with_known_loc = -2; + if (loc0 != ~0 || loc1 != ~0) { + // determine which packet is the last one that will complete + for (i=f->segment_count-1; i >= 0; --i) + if (f->segments[i] < 255) + break; + // 'i' is now the index of the _last_ segment of a packet that ends + if (i >= 0) { + f->end_seg_with_known_loc = i; + f->known_loc_for_packet = loc0; + } + } + if (f->first_decode) { + int i,len; + ProbedPage p; + len = 0; + for (i=0; i < f->segment_count; ++i) + len += f->segments[i]; + len += 27 + f->segment_count; + p.page_start = f->first_audio_page_offset; + p.page_end = p.page_start + len; + p.after_previous_page_start = p.page_start; + p.first_decoded_sample = 0; + p.last_decoded_sample = loc0; + f->p_first = p; + } + f->next_seg = 0; + return TRUE; +} + +static int start_page(vorb *f) +{ + if (!capture_pattern(f)) return error(f, VORBIS_missing_capture_pattern); + return start_page_no_capturepattern(f); +} + +static int start_packet(vorb *f) +{ + while (f->next_seg == -1) { + if (!start_page(f)) return FALSE; + if (f->page_flag & PAGEFLAG_continued_packet) + return error(f, VORBIS_continued_packet_flag_invalid); + } + f->last_seg = FALSE; + f->valid_bits = 0; + f->packet_bytes = 0; + f->bytes_in_seg = 0; + // f->next_seg is now valid + return TRUE; +} + +static int maybe_start_packet(vorb *f) +{ + if (f->next_seg == -1) { + int x = get8(f); + if (f->eof) return FALSE; // EOF at page boundary is not an error! + if (0x4f != x ) return error(f, VORBIS_missing_capture_pattern); + if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (0x53 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (!start_page_no_capturepattern(f)) return FALSE; + if (f->page_flag & PAGEFLAG_continued_packet) { + // set up enough state that we can read this packet if we want, + // e.g. during recovery + f->last_seg = FALSE; + f->bytes_in_seg = 0; + return error(f, VORBIS_continued_packet_flag_invalid); + } + } + return start_packet(f); +} + +static int next_segment(vorb *f) +{ + int len; + if (f->last_seg) return 0; + if (f->next_seg == -1) { + f->last_seg_which = f->segment_count-1; // in case start_page fails + if (!start_page(f)) { f->last_seg = 1; return 0; } + if (!(f->page_flag & PAGEFLAG_continued_packet)) return error(f, VORBIS_continued_packet_flag_invalid); + } + len = f->segments[f->next_seg++]; + if (len < 255) { + f->last_seg = TRUE; + f->last_seg_which = f->next_seg-1; + } + if (f->next_seg >= f->segment_count) + f->next_seg = -1; + assert(f->bytes_in_seg == 0); + f->bytes_in_seg = len; + return len; +} + +#define EOP (-1) +#define INVALID_BITS (-1) + +static int get8_packet_raw(vorb *f) +{ + if (!f->bytes_in_seg) + if (f->last_seg) return EOP; + else if (!next_segment(f)) return EOP; + assert(f->bytes_in_seg > 0); + --f->bytes_in_seg; + ++f->packet_bytes; + return get8(f); +} + +static int get8_packet(vorb *f) +{ + int x = get8_packet_raw(f); + f->valid_bits = 0; + return x; +} + +static void flush_packet(vorb *f) +{ + while (get8_packet_raw(f) != EOP); +} + +// @OPTIMIZE: this is the secondary bit decoder, so it's probably not as important +// as the huffman decoder? +static uint32 get_bits(vorb *f, int n) +{ + uint32 z; + + if (f->valid_bits < 0) return 0; + if (f->valid_bits < n) { + if (n > 24) { + // the accumulator technique below would not work correctly in this case + z = get_bits(f, 24); + z += get_bits(f, n-24) << 24; + return z; + } + if (f->valid_bits == 0) f->acc = 0; + while (f->valid_bits < n) { + int z = get8_packet_raw(f); + if (z == EOP) { + f->valid_bits = INVALID_BITS; + return 0; + } + f->acc += z << f->valid_bits; + f->valid_bits += 8; + } + } + if (f->valid_bits < 0) return 0; + z = f->acc & ((1 << n)-1); + f->acc >>= n; + f->valid_bits -= n; + return z; +} + +static int32 get_bits_signed(vorb *f, int n) +{ + uint32 z = get_bits(f, n); + if (z & (1 << (n-1))) + z += ~((1 << n) - 1); + return (int32) z; +} + +// @OPTIMIZE: primary accumulator for huffman +// expand the buffer to as many bits as possible without reading off end of packet +// it might be nice to allow f->valid_bits and f->acc to be stored in registers, +// e.g. cache them locally and decode locally +static __forceinline void prep_huffman(vorb *f) +{ + if (f->valid_bits <= 24) { + if (f->valid_bits == 0) f->acc = 0; + do { + int z; + if (f->last_seg && !f->bytes_in_seg) return; + z = get8_packet_raw(f); + if (z == EOP) return; + f->acc += z << f->valid_bits; + f->valid_bits += 8; + } while (f->valid_bits <= 24); + } +} + +enum +{ + VORBIS_packet_id = 1, + VORBIS_packet_comment = 3, + VORBIS_packet_setup = 5, +}; + +static int codebook_decode_scalar_raw(vorb *f, Codebook *c) +{ + int i; + prep_huffman(f); + + assert(c->sorted_codewords || c->codewords); + // cases to use binary search: sorted_codewords && !c->codewords + // sorted_codewords && c->entries > 8 + if (c->entries > 8 ? c->sorted_codewords!=NULL : !c->codewords) { + // binary search + uint32 code = bit_reverse(f->acc); + int x=0, n=c->sorted_entries, len; + + while (n > 1) { + // invariant: sc[x] <= code < sc[x+n] + int m = x + (n >> 1); + if (c->sorted_codewords[m] <= code) { + x = m; + n -= (n>>1); + } else { + n >>= 1; + } + } + // x is now the sorted index + if (!c->sparse) x = c->sorted_values[x]; + // x is now sorted index if sparse, or symbol otherwise + len = c->codeword_lengths[x]; + if (f->valid_bits >= len) { + f->acc >>= len; + f->valid_bits -= len; + return x; + } + + f->valid_bits = 0; + return -1; + } + + // if small, linear search + assert(!c->sparse); + for (i=0; i < c->entries; ++i) { + if (c->codeword_lengths[i] == NO_CODE) continue; + if (c->codewords[i] == (f->acc & ((1 << c->codeword_lengths[i])-1))) { + if (f->valid_bits >= c->codeword_lengths[i]) { + f->acc >>= c->codeword_lengths[i]; + f->valid_bits -= c->codeword_lengths[i]; + return i; + } + f->valid_bits = 0; + return -1; + } + } + + error(f, VORBIS_invalid_stream); + f->valid_bits = 0; + return -1; +} + +static int codebook_decode_scalar(vorb *f, Codebook *c) +{ + int i; + if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) + prep_huffman(f); + // fast huffman table lookup + i = f->acc & FAST_HUFFMAN_TABLE_MASK; + i = c->fast_huffman[i]; + if (i >= 0) { + f->acc >>= c->codeword_lengths[i]; + f->valid_bits -= c->codeword_lengths[i]; + if (f->valid_bits < 0) { f->valid_bits = 0; return -1; } + return i; + } + return codebook_decode_scalar_raw(f,c); +} + +#ifndef STB_VORBIS_NO_INLINE_DECODE + +#define DECODE_RAW(var, f,c) \ + if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) \ + prep_huffman(f); \ + var = f->acc & FAST_HUFFMAN_TABLE_MASK; \ + var = c->fast_huffman[var]; \ + if (var >= 0) { \ + int n = c->codeword_lengths[var]; \ + f->acc >>= n; \ + f->valid_bits -= n; \ + if (f->valid_bits < 0) { f->valid_bits = 0; var = -1; } \ + } else { \ + var = codebook_decode_scalar_raw(f,c); \ + } + +#else + +#define DECODE_RAW(var,f,c) var = codebook_decode_scalar(f,c); + +#endif + +#define DECODE(var,f,c) \ + DECODE_RAW(var,f,c) \ + if (c->sparse) var = c->sorted_values[var]; + +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + #define DECODE_VQ(var,f,c) DECODE_RAW(var,f,c) +#else + #define DECODE_VQ(var,f,c) DECODE(var,f,c) +#endif + + + + + + +// CODEBOOK_ELEMENT_FAST is an optimization for the CODEBOOK_FLOATS case +// where we avoid one addition +#ifndef STB_VORBIS_CODEBOOK_FLOATS + #define CODEBOOK_ELEMENT(c,off) (c->multiplicands[off] * c->delta_value + c->minimum_value) + #define CODEBOOK_ELEMENT_FAST(c,off) (c->multiplicands[off] * c->delta_value) + #define CODEBOOK_ELEMENT_BASE(c) (c->minimum_value) +#else + #define CODEBOOK_ELEMENT(c,off) (c->multiplicands[off]) + #define CODEBOOK_ELEMENT_FAST(c,off) (c->multiplicands[off]) + #define CODEBOOK_ELEMENT_BASE(c) (0) +#endif + +static int codebook_decode_start(vorb *f, Codebook *c, int len) +{ + int z = -1; + + // type 0 is only legal in a scalar context + if (c->lookup_type == 0) + error(f, VORBIS_invalid_stream); + else { + DECODE_VQ(z,f,c); + if (c->sparse) assert(z < c->sorted_entries); + if (z < 0) { // check for EOP + if (!f->bytes_in_seg) + if (f->last_seg) + return z; + error(f, VORBIS_invalid_stream); + } + } + return z; +} + +static int codebook_decode(vorb *f, Codebook *c, float *output, int len) +{ + int i,z = codebook_decode_start(f,c,len); + if (z < 0) return FALSE; + if (len > c->dimensions) len = c->dimensions; + +#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + float last = CODEBOOK_ELEMENT_BASE(c); + int div = 1; + for (i=0; i < len; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; + output[i] += val; + if (c->sequence_p) last = val + c->minimum_value; + div *= c->lookup_values; + } + return TRUE; + } +#endif + + z *= c->dimensions; + if (c->sequence_p) { + float last = CODEBOOK_ELEMENT_BASE(c); + for (i=0; i < len; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + output[i] += val; + last = val + c->minimum_value; + } + } else { + float last = CODEBOOK_ELEMENT_BASE(c); + for (i=0; i < len; ++i) { + output[i] += CODEBOOK_ELEMENT_FAST(c,z+i) + last; + } + } + + return TRUE; +} + +static int codebook_decode_step(vorb *f, Codebook *c, float *output, int len, int step) +{ + int i,z = codebook_decode_start(f,c,len); + float last = CODEBOOK_ELEMENT_BASE(c); + if (z < 0) return FALSE; + if (len > c->dimensions) len = c->dimensions; + +#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int div = 1; + for (i=0; i < len; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; + output[i*step] += val; + if (c->sequence_p) last = val; + div *= c->lookup_values; + } + return TRUE; + } +#endif + + z *= c->dimensions; + for (i=0; i < len; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + output[i*step] += val; + if (c->sequence_p) last = val; + } + + return TRUE; +} + +static int codebook_decode_deinterleave_repeat(vorb *f, Codebook *c, float **outputs, int ch, int *c_inter_p, int *p_inter_p, int len, int total_decode) +{ + int c_inter = *c_inter_p; + int p_inter = *p_inter_p; + int i,z, effective = c->dimensions; + + // type 0 is only legal in a scalar context + if (c->lookup_type == 0) return error(f, VORBIS_invalid_stream); + + while (total_decode > 0) { + float last = CODEBOOK_ELEMENT_BASE(c); + DECODE_VQ(z,f,c); + #ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + assert(!c->sparse || z < c->sorted_entries); + #endif + if (z < 0) { + if (!f->bytes_in_seg) + if (f->last_seg) return FALSE; + return error(f, VORBIS_invalid_stream); + } + + // if this will take us off the end of the buffers, stop short! + // we check by computing the length of the virtual interleaved + // buffer (len*ch), our current offset within it (p_inter*ch)+(c_inter), + // and the length we'll be using (effective) + if (c_inter + p_inter*ch + effective > len * ch) { + effective = len*ch - (p_inter*ch - c_inter); + } + + #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int div = 1; + for (i=0; i < effective; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + if (c->sequence_p) last = val; + div *= c->lookup_values; + } + } else + #endif + { + z *= c->dimensions; + if (c->sequence_p) { + for (i=0; i < effective; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + last = val; + } + } else { + for (i=0; i < effective; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + } + } + } + + total_decode -= effective; + } + *c_inter_p = c_inter; + *p_inter_p = p_inter; + return TRUE; +} + +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK +static int codebook_decode_deinterleave_repeat_2(vorb *f, Codebook *c, float **outputs, int *c_inter_p, int *p_inter_p, int len, int total_decode) +{ + int c_inter = *c_inter_p; + int p_inter = *p_inter_p; + int i,z, effective = c->dimensions; + + // type 0 is only legal in a scalar context + if (c->lookup_type == 0) return error(f, VORBIS_invalid_stream); + + while (total_decode > 0) { + float last = CODEBOOK_ELEMENT_BASE(c); + DECODE_VQ(z,f,c); + + if (z < 0) { + if (!f->bytes_in_seg) + if (f->last_seg) return FALSE; + return error(f, VORBIS_invalid_stream); + } + + // if this will take us off the end of the buffers, stop short! + // we check by computing the length of the virtual interleaved + // buffer (len*ch), our current offset within it (p_inter*ch)+(c_inter), + // and the length we'll be using (effective) + if (c_inter + p_inter*2 + effective > len * 2) { + effective = len*2 - (p_inter*2 - c_inter); + } + + { + z *= c->dimensions; + stb_prof(11); + if (c->sequence_p) { + // haven't optimized this case because I don't have any examples + for (i=0; i < effective; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + outputs[c_inter][p_inter] += val; + if (++c_inter == 2) { c_inter = 0; ++p_inter; } + last = val; + } + } else { + i=0; + if (c_inter == 1) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + outputs[c_inter][p_inter] += val; + c_inter = 0; ++p_inter; + ++i; + } + { + float *z0 = outputs[0]; + float *z1 = outputs[1]; + for (; i+1 < effective;) { + z0[p_inter] += CODEBOOK_ELEMENT_FAST(c,z+i) + last; + z1[p_inter] += CODEBOOK_ELEMENT_FAST(c,z+i+1) + last; + ++p_inter; + i += 2; + } + } + if (i < effective) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + outputs[c_inter][p_inter] += val; + if (++c_inter == 2) { c_inter = 0; ++p_inter; } + } + } + } + + total_decode -= effective; + } + *c_inter_p = c_inter; + *p_inter_p = p_inter; + return TRUE; +} +#endif + +static int predict_point(int x, int x0, int x1, int y0, int y1) +{ + int dy = y1 - y0; + int adx = x1 - x0; + // @OPTIMIZE: force int division to round in the right direction... is this necessary on x86? + int err = abs(dy) * (x - x0); + int off = err / adx; + return dy < 0 ? y0 - off : y0 + off; +} + +// the following table is block-copied from the specification +static float inverse_db_table[256] = +{ + 1.0649863e-07f, 1.1341951e-07f, 1.2079015e-07f, 1.2863978e-07f, + 1.3699951e-07f, 1.4590251e-07f, 1.5538408e-07f, 1.6548181e-07f, + 1.7623575e-07f, 1.8768855e-07f, 1.9988561e-07f, 2.1287530e-07f, + 2.2670913e-07f, 2.4144197e-07f, 2.5713223e-07f, 2.7384213e-07f, + 2.9163793e-07f, 3.1059021e-07f, 3.3077411e-07f, 3.5226968e-07f, + 3.7516214e-07f, 3.9954229e-07f, 4.2550680e-07f, 4.5315863e-07f, + 4.8260743e-07f, 5.1396998e-07f, 5.4737065e-07f, 5.8294187e-07f, + 6.2082472e-07f, 6.6116941e-07f, 7.0413592e-07f, 7.4989464e-07f, + 7.9862701e-07f, 8.5052630e-07f, 9.0579828e-07f, 9.6466216e-07f, + 1.0273513e-06f, 1.0941144e-06f, 1.1652161e-06f, 1.2409384e-06f, + 1.3215816e-06f, 1.4074654e-06f, 1.4989305e-06f, 1.5963394e-06f, + 1.7000785e-06f, 1.8105592e-06f, 1.9282195e-06f, 2.0535261e-06f, + 2.1869758e-06f, 2.3290978e-06f, 2.4804557e-06f, 2.6416497e-06f, + 2.8133190e-06f, 2.9961443e-06f, 3.1908506e-06f, 3.3982101e-06f, + 3.6190449e-06f, 3.8542308e-06f, 4.1047004e-06f, 4.3714470e-06f, + 4.6555282e-06f, 4.9580707e-06f, 5.2802740e-06f, 5.6234160e-06f, + 5.9888572e-06f, 6.3780469e-06f, 6.7925283e-06f, 7.2339451e-06f, + 7.7040476e-06f, 8.2047000e-06f, 8.7378876e-06f, 9.3057248e-06f, + 9.9104632e-06f, 1.0554501e-05f, 1.1240392e-05f, 1.1970856e-05f, + 1.2748789e-05f, 1.3577278e-05f, 1.4459606e-05f, 1.5399272e-05f, + 1.6400004e-05f, 1.7465768e-05f, 1.8600792e-05f, 1.9809576e-05f, + 2.1096914e-05f, 2.2467911e-05f, 2.3928002e-05f, 2.5482978e-05f, + 2.7139006e-05f, 2.8902651e-05f, 3.0780908e-05f, 3.2781225e-05f, + 3.4911534e-05f, 3.7180282e-05f, 3.9596466e-05f, 4.2169667e-05f, + 4.4910090e-05f, 4.7828601e-05f, 5.0936773e-05f, 5.4246931e-05f, + 5.7772202e-05f, 6.1526565e-05f, 6.5524908e-05f, 6.9783085e-05f, + 7.4317983e-05f, 7.9147585e-05f, 8.4291040e-05f, 8.9768747e-05f, + 9.5602426e-05f, 0.00010181521f, 0.00010843174f, 0.00011547824f, + 0.00012298267f, 0.00013097477f, 0.00013948625f, 0.00014855085f, + 0.00015820453f, 0.00016848555f, 0.00017943469f, 0.00019109536f, + 0.00020351382f, 0.00021673929f, 0.00023082423f, 0.00024582449f, + 0.00026179955f, 0.00027881276f, 0.00029693158f, 0.00031622787f, + 0.00033677814f, 0.00035866388f, 0.00038197188f, 0.00040679456f, + 0.00043323036f, 0.00046138411f, 0.00049136745f, 0.00052329927f, + 0.00055730621f, 0.00059352311f, 0.00063209358f, 0.00067317058f, + 0.00071691700f, 0.00076350630f, 0.00081312324f, 0.00086596457f, + 0.00092223983f, 0.00098217216f, 0.0010459992f, 0.0011139742f, + 0.0011863665f, 0.0012634633f, 0.0013455702f, 0.0014330129f, + 0.0015261382f, 0.0016253153f, 0.0017309374f, 0.0018434235f, + 0.0019632195f, 0.0020908006f, 0.0022266726f, 0.0023713743f, + 0.0025254795f, 0.0026895994f, 0.0028643847f, 0.0030505286f, + 0.0032487691f, 0.0034598925f, 0.0036847358f, 0.0039241906f, + 0.0041792066f, 0.0044507950f, 0.0047400328f, 0.0050480668f, + 0.0053761186f, 0.0057254891f, 0.0060975636f, 0.0064938176f, + 0.0069158225f, 0.0073652516f, 0.0078438871f, 0.0083536271f, + 0.0088964928f, 0.009474637f, 0.010090352f, 0.010746080f, + 0.011444421f, 0.012188144f, 0.012980198f, 0.013823725f, + 0.014722068f, 0.015678791f, 0.016697687f, 0.017782797f, + 0.018938423f, 0.020169149f, 0.021479854f, 0.022875735f, + 0.024362330f, 0.025945531f, 0.027631618f, 0.029427276f, + 0.031339626f, 0.033376252f, 0.035545228f, 0.037855157f, + 0.040315199f, 0.042935108f, 0.045725273f, 0.048696758f, + 0.051861348f, 0.055231591f, 0.058820850f, 0.062643361f, + 0.066714279f, 0.071049749f, 0.075666962f, 0.080584227f, + 0.085821044f, 0.091398179f, 0.097337747f, 0.10366330f, + 0.11039993f, 0.11757434f, 0.12521498f, 0.13335215f, + 0.14201813f, 0.15124727f, 0.16107617f, 0.17154380f, + 0.18269168f, 0.19456402f, 0.20720788f, 0.22067342f, + 0.23501402f, 0.25028656f, 0.26655159f, 0.28387361f, + 0.30232132f, 0.32196786f, 0.34289114f, 0.36517414f, + 0.38890521f, 0.41417847f, 0.44109412f, 0.46975890f, + 0.50028648f, 0.53279791f, 0.56742212f, 0.60429640f, + 0.64356699f, 0.68538959f, 0.72993007f, 0.77736504f, + 0.82788260f, 0.88168307f, 0.9389798f, 1.0f +}; + + +// @OPTIMIZE: if you want to replace this bresenham line-drawing routine, +// note that you must produce bit-identical output to decode correctly; +// this specific sequence of operations is specified in the spec (it's +// drawing integer-quantized frequency-space lines that the encoder +// expects to be exactly the same) +// ... also, isn't the whole point of Bresenham's algorithm to NOT +// have to divide in the setup? sigh. +#ifndef STB_VORBIS_NO_DEFER_FLOOR +#define LINE_OP(a,b) a *= b +#else +#define LINE_OP(a,b) a = b +#endif + +#ifdef STB_VORBIS_DIVIDE_TABLE +#define DIVTAB_NUMER 32 +#define DIVTAB_DENOM 64 +int8 integer_divide_table[DIVTAB_NUMER][DIVTAB_DENOM]; // 2KB +#endif + +static __forceinline void draw_line(float *output, int x0, int y0, int x1, int y1, int n) +{ + int dy = y1 - y0; + int adx = x1 - x0; + int ady = abs(dy); + int base; + int x=x0,y=y0; + int err = 0; + int sy; + +#ifdef STB_VORBIS_DIVIDE_TABLE + if (adx < DIVTAB_DENOM && ady < DIVTAB_NUMER) { + if (dy < 0) { + base = -integer_divide_table[ady][adx]; + sy = base-1; + } else { + base = integer_divide_table[ady][adx]; + sy = base+1; + } + } else { + base = dy / adx; + if (dy < 0) + sy = base - 1; + else + sy = base+1; + } +#else + base = dy / adx; + if (dy < 0) + sy = base - 1; + else + sy = base+1; +#endif + ady -= abs(base) * adx; + if (x1 > n) x1 = n; + LINE_OP(output[x], inverse_db_table[y]); + for (++x; x < x1; ++x) { + err += ady; + if (err >= adx) { + err -= adx; + y += sy; + } else + y += base; + LINE_OP(output[x], inverse_db_table[y]); + } +} + +static int residue_decode(vorb *f, Codebook *book, float *target, int offset, int n, int rtype) +{ + int k; + if (rtype == 0) { + int step = n / book->dimensions; + for (k=0; k < step; ++k) + if (!codebook_decode_step(f, book, target+offset+k, n-offset-k, step)) + return FALSE; + } else { + for (k=0; k < n; ) { + if (!codebook_decode(f, book, target+offset, n-k)) + return FALSE; + k += book->dimensions; + offset += book->dimensions; + } + } + return TRUE; +} + +static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int rn, uint8 *do_not_decode) +{ + int i,j,pass; + Residue *r = f->residue_config + rn; + int rtype = f->residue_types[rn]; + int c = r->classbook; + int classwords = f->codebooks[c].dimensions; + int n_read = r->end - r->begin; + int part_read = n_read / r->part_size; + int temp_alloc_point = temp_alloc_save(f); + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + uint8 ***part_classdata = (uint8 ***) temp_block_array(f,f->channels, part_read * sizeof(**part_classdata)); + #else + int **classifications = (int **) temp_block_array(f,f->channels, part_read * sizeof(**classifications)); + #endif + + stb_prof(2); + for (i=0; i < ch; ++i) + if (!do_not_decode[i]) + memset(residue_buffers[i], 0, sizeof(float) * n); + + if (rtype == 2 && ch != 1) { + int len = ch * n; + for (j=0; j < ch; ++j) + if (!do_not_decode[j]) + break; + if (j == ch) + goto done; + + stb_prof(3); + for (pass=0; pass < 8; ++pass) { + int pcount = 0, class_set = 0; + if (ch == 2) { + stb_prof(13); + while (pcount < part_read) { + int z = r->begin + pcount*r->part_size; + int c_inter = (z & 1), p_inter = z>>1; + if (pass == 0) { + Codebook *c = f->codebooks+r->classbook; + int q; + DECODE(q,f,c); + if (q == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[0][i+pcount] = q % r->classifications; + q /= r->classifications; + } + #endif + } + stb_prof(5); + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount*r->part_size; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; + #else + int c = classifications[0][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + Codebook *book = f->codebooks + b; + stb_prof(20); // accounts for X time + #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + #else + // saves 1% + if (!codebook_decode_deinterleave_repeat_2(f, book, residue_buffers, &c_inter, &p_inter, n, r->part_size)) + goto done; + #endif + stb_prof(7); + } else { + z += r->part_size; + c_inter = z & 1; + p_inter = z >> 1; + } + } + stb_prof(8); + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } else if (ch == 1) { + while (pcount < part_read) { + int z = r->begin + pcount*r->part_size; + int c_inter = 0, p_inter = z; + if (pass == 0) { + Codebook *c = f->codebooks+r->classbook; + int q; + DECODE(q,f,c); + if (q == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[0][i+pcount] = q % r->classifications; + q /= r->classifications; + } + #endif + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount*r->part_size; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; + #else + int c = classifications[0][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + Codebook *book = f->codebooks + b; + stb_prof(22); + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + stb_prof(3); + } else { + z += r->part_size; + c_inter = 0; + p_inter = z; + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } else { + while (pcount < part_read) { + int z = r->begin + pcount*r->part_size; + int c_inter = z % ch, p_inter = z/ch; + if (pass == 0) { + Codebook *c = f->codebooks+r->classbook; + int q; + DECODE(q,f,c); + if (q == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[0][i+pcount] = q % r->classifications; + q /= r->classifications; + } + #endif + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount*r->part_size; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; + #else + int c = classifications[0][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + Codebook *book = f->codebooks + b; + stb_prof(22); + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + stb_prof(3); + } else { + z += r->part_size; + c_inter = z % ch; + p_inter = z / ch; + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } + } + goto done; + } + stb_prof(9); + + for (pass=0; pass < 8; ++pass) { + int pcount = 0, class_set=0; + while (pcount < part_read) { + if (pass == 0) { + for (j=0; j < ch; ++j) { + if (!do_not_decode[j]) { + Codebook *c = f->codebooks+r->classbook; + int temp; + DECODE(temp,f,c); + if (temp == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[j][class_set] = r->classdata[temp]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[j][i+pcount] = temp % r->classifications; + temp /= r->classifications; + } + #endif + } + } + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + for (j=0; j < ch; ++j) { + if (!do_not_decode[j]) { + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[j][class_set][i]; + #else + int c = classifications[j][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + float *target = residue_buffers[j]; + int offset = r->begin + pcount * r->part_size; + int n = r->part_size; + Codebook *book = f->codebooks + b; + if (!residue_decode(f, book, target, offset, n, rtype)) + goto done; + } + } + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } + done: + stb_prof(0); + temp_alloc_restore(f,temp_alloc_point); +} + + +#if 0 +// slow way for debugging +void inverse_mdct_slow(float *buffer, int n) +{ + int i,j; + int n2 = n >> 1; + float *x = (float *) malloc(sizeof(*x) * n2); + memcpy(x, buffer, sizeof(*x) * n2); + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n2; ++j) + // formula from paper: + //acc += n/4.0f * x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1)); + // formula from wikipedia + //acc += 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5)); + // these are equivalent, except the formula from the paper inverts the multiplier! + // however, what actually works is NO MULTIPLIER!?! + //acc += 64 * 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5)); + acc += x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1)); + buffer[i] = acc; + } + free(x); +} +#elif 0 +// same as above, but just barely able to run in real time on modern machines +void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) +{ + float mcos[16384]; + int i,j; + int n2 = n >> 1, nmask = (n << 2) -1; + float *x = (float *) malloc(sizeof(*x) * n2); + memcpy(x, buffer, sizeof(*x) * n2); + for (i=0; i < 4*n; ++i) + mcos[i] = (float) cos(M_PI / 2 * i / n); + + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n2; ++j) + acc += x[j] * mcos[(2 * i + 1 + n2)*(2*j+1) & nmask]; + buffer[i] = acc; + } + free(x); +} +#else +// transform to use a slow dct-iv; this is STILL basically trivial, +// but only requires half as many ops +void dct_iv_slow(float *buffer, int n) +{ + float mcos[16384]; + float x[2048]; + int i,j; + int n2 = n >> 1, nmask = (n << 3) - 1; + memcpy(x, buffer, sizeof(*x) * n); + for (i=0; i < 8*n; ++i) + mcos[i] = (float) cos(M_PI / 4 * i / n); + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n; ++j) + acc += x[j] * mcos[((2 * i + 1)*(2*j+1)) & nmask]; + //acc += x[j] * cos(M_PI / n * (i + 0.5) * (j + 0.5)); + buffer[i] = acc; + } + free(x); +} + +void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) +{ + int i, n4 = n >> 2, n2 = n >> 1, n3_4 = n - n4; + float temp[4096]; + + memcpy(temp, buffer, n2 * sizeof(float)); + dct_iv_slow(temp, n2); // returns -c'-d, a-b' + + for (i=0; i < n4 ; ++i) buffer[i] = temp[i+n4]; // a-b' + for ( ; i < n3_4; ++i) buffer[i] = -temp[n3_4 - i - 1]; // b-a', c+d' + for ( ; i < n ; ++i) buffer[i] = -temp[i - n3_4]; // c'+d +} +#endif + +#ifndef LIBVORBIS_MDCT +#define LIBVORBIS_MDCT 0 +#endif + +#if LIBVORBIS_MDCT +// directly call the vorbis MDCT using an interface documented +// by Jeff Roberts... useful for performance comparison +typedef struct +{ + int n; + int log2n; + + float *trig; + int *bitrev; + + float scale; +} mdct_lookup; + +extern void mdct_init(mdct_lookup *lookup, int n); +extern void mdct_clear(mdct_lookup *l); +extern void mdct_backward(mdct_lookup *init, float *in, float *out); + +mdct_lookup M1,M2; + +void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) +{ + mdct_lookup *M; + if (M1.n == n) M = &M1; + else if (M2.n == n) M = &M2; + else if (M1.n == 0) { mdct_init(&M1, n); M = &M1; } + else { + if (M2.n) __asm int 3; + mdct_init(&M2, n); + M = &M2; + } + + mdct_backward(M, buffer, buffer); +} +#endif + + +// the following were split out into separate functions while optimizing; +// they could be pushed back up but eh. __forceinline showed no change; +// they're probably already being inlined. +static void imdct_step3_iter0_loop(int n, float *e, int i_off, int k_off, float *A) +{ + float *ee0 = e + i_off; + float *ee2 = ee0 + k_off; + int i; + + assert((n & 3) == 0); + for (i=(n>>2); i > 0; --i) { + float k00_20, k01_21; + k00_20 = ee0[ 0] - ee2[ 0]; + k01_21 = ee0[-1] - ee2[-1]; + ee0[ 0] += ee2[ 0];//ee0[ 0] = ee0[ 0] + ee2[ 0]; + ee0[-1] += ee2[-1];//ee0[-1] = ee0[-1] + ee2[-1]; + ee2[ 0] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-1] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-2] - ee2[-2]; + k01_21 = ee0[-3] - ee2[-3]; + ee0[-2] += ee2[-2];//ee0[-2] = ee0[-2] + ee2[-2]; + ee0[-3] += ee2[-3];//ee0[-3] = ee0[-3] + ee2[-3]; + ee2[-2] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-3] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-4] - ee2[-4]; + k01_21 = ee0[-5] - ee2[-5]; + ee0[-4] += ee2[-4];//ee0[-4] = ee0[-4] + ee2[-4]; + ee0[-5] += ee2[-5];//ee0[-5] = ee0[-5] + ee2[-5]; + ee2[-4] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-5] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-6] - ee2[-6]; + k01_21 = ee0[-7] - ee2[-7]; + ee0[-6] += ee2[-6];//ee0[-6] = ee0[-6] + ee2[-6]; + ee0[-7] += ee2[-7];//ee0[-7] = ee0[-7] + ee2[-7]; + ee2[-6] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-7] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + ee0 -= 8; + ee2 -= 8; + } +} + +static void imdct_step3_inner_r_loop(int lim, float *e, int d0, int k_off, float *A, int k1) +{ + int i; + float k00_20, k01_21; + + float *e0 = e + d0; + float *e2 = e0 + k_off; + + for (i=lim >> 2; i > 0; --i) { + k00_20 = e0[-0] - e2[-0]; + k01_21 = e0[-1] - e2[-1]; + e0[-0] += e2[-0];//e0[-0] = e0[-0] + e2[-0]; + e0[-1] += e2[-1];//e0[-1] = e0[-1] + e2[-1]; + e2[-0] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-1] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-2] - e2[-2]; + k01_21 = e0[-3] - e2[-3]; + e0[-2] += e2[-2];//e0[-2] = e0[-2] + e2[-2]; + e0[-3] += e2[-3];//e0[-3] = e0[-3] + e2[-3]; + e2[-2] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-3] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-4] - e2[-4]; + k01_21 = e0[-5] - e2[-5]; + e0[-4] += e2[-4];//e0[-4] = e0[-4] + e2[-4]; + e0[-5] += e2[-5];//e0[-5] = e0[-5] + e2[-5]; + e2[-4] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-5] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-6] - e2[-6]; + k01_21 = e0[-7] - e2[-7]; + e0[-6] += e2[-6];//e0[-6] = e0[-6] + e2[-6]; + e0[-7] += e2[-7];//e0[-7] = e0[-7] + e2[-7]; + e2[-6] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-7] = (k01_21)*A[0] + (k00_20) * A[1]; + + e0 -= 8; + e2 -= 8; + + A += k1; + } +} + +static void imdct_step3_inner_s_loop(int n, float *e, int i_off, int k_off, float *A, int a_off, int k0) +{ + int i; + float A0 = A[0]; + float A1 = A[0+1]; + float A2 = A[0+a_off]; + float A3 = A[0+a_off+1]; + float A4 = A[0+a_off*2+0]; + float A5 = A[0+a_off*2+1]; + float A6 = A[0+a_off*3+0]; + float A7 = A[0+a_off*3+1]; + + float k00,k11; + + float *ee0 = e +i_off; + float *ee2 = ee0+k_off; + + for (i=n; i > 0; --i) { + k00 = ee0[ 0] - ee2[ 0]; + k11 = ee0[-1] - ee2[-1]; + ee0[ 0] = ee0[ 0] + ee2[ 0]; + ee0[-1] = ee0[-1] + ee2[-1]; + ee2[ 0] = (k00) * A0 - (k11) * A1; + ee2[-1] = (k11) * A0 + (k00) * A1; + + k00 = ee0[-2] - ee2[-2]; + k11 = ee0[-3] - ee2[-3]; + ee0[-2] = ee0[-2] + ee2[-2]; + ee0[-3] = ee0[-3] + ee2[-3]; + ee2[-2] = (k00) * A2 - (k11) * A3; + ee2[-3] = (k11) * A2 + (k00) * A3; + + k00 = ee0[-4] - ee2[-4]; + k11 = ee0[-5] - ee2[-5]; + ee0[-4] = ee0[-4] + ee2[-4]; + ee0[-5] = ee0[-5] + ee2[-5]; + ee2[-4] = (k00) * A4 - (k11) * A5; + ee2[-5] = (k11) * A4 + (k00) * A5; + + k00 = ee0[-6] - ee2[-6]; + k11 = ee0[-7] - ee2[-7]; + ee0[-6] = ee0[-6] + ee2[-6]; + ee0[-7] = ee0[-7] + ee2[-7]; + ee2[-6] = (k00) * A6 - (k11) * A7; + ee2[-7] = (k11) * A6 + (k00) * A7; + + ee0 -= k0; + ee2 -= k0; + } +} + +static __forceinline void iter_54(float *z) +{ + float k00,k11,k22,k33; + float y0,y1,y2,y3; + + k00 = z[ 0] - z[-4]; + y0 = z[ 0] + z[-4]; + y2 = z[-2] + z[-6]; + k22 = z[-2] - z[-6]; + + z[-0] = y0 + y2; // z0 + z4 + z2 + z6 + z[-2] = y0 - y2; // z0 + z4 - z2 - z6 + + // done with y0,y2 + + k33 = z[-3] - z[-7]; + + z[-4] = k00 + k33; // z0 - z4 + z3 - z7 + z[-6] = k00 - k33; // z0 - z4 - z3 + z7 + + // done with k33 + + k11 = z[-1] - z[-5]; + y1 = z[-1] + z[-5]; + y3 = z[-3] + z[-7]; + + z[-1] = y1 + y3; // z1 + z5 + z3 + z7 + z[-3] = y1 - y3; // z1 + z5 - z3 - z7 + z[-5] = k11 - k22; // z1 - z5 + z2 - z6 + z[-7] = k11 + k22; // z1 - z5 - z2 + z6 +} + +static void imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A, int base_n) +{ + int k_off = -8; + int a_off = base_n >> 3; + float A2 = A[0+a_off]; + float *z = e + i_off; + float *base = z - 16 * n; + + while (z > base) { + float k00,k11; + + k00 = z[-0] - z[-8]; + k11 = z[-1] - z[-9]; + z[-0] = z[-0] + z[-8]; + z[-1] = z[-1] + z[-9]; + z[-8] = k00; + z[-9] = k11 ; + + k00 = z[ -2] - z[-10]; + k11 = z[ -3] - z[-11]; + z[ -2] = z[ -2] + z[-10]; + z[ -3] = z[ -3] + z[-11]; + z[-10] = (k00+k11) * A2; + z[-11] = (k11-k00) * A2; + + k00 = z[-12] - z[ -4]; // reverse to avoid a unary negation + k11 = z[ -5] - z[-13]; + z[ -4] = z[ -4] + z[-12]; + z[ -5] = z[ -5] + z[-13]; + z[-12] = k11; + z[-13] = k00; + + k00 = z[-14] - z[ -6]; // reverse to avoid a unary negation + k11 = z[ -7] - z[-15]; + z[ -6] = z[ -6] + z[-14]; + z[ -7] = z[ -7] + z[-15]; + z[-14] = (k00+k11) * A2; + z[-15] = (k00-k11) * A2; + + iter_54(z); + iter_54(z-8); + z -= 16; + } +} + +static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) +{ + int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; + int n3_4 = n - n4, ld; + // @OPTIMIZE: reduce register pressure by using fewer variables? + int save_point = temp_alloc_save(f); + float *buf2 = (float *) temp_alloc(f, n2 * sizeof(*buf2)); + float *u=NULL,*v=NULL; + // twiddle factors + float *A = f->A[blocktype]; + + // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" + // See notes about bugs in that paper in less-optimal implementation 'inverse_mdct_old' after this function. + + // kernel from paper + + + // merged: + // copy and reflect spectral data + // step 0 + + // note that it turns out that the items added together during + // this step are, in fact, being added to themselves (as reflected + // by step 0). inexplicable inefficiency! this became obvious + // once I combined the passes. + + // so there's a missing 'times 2' here (for adding X to itself). + // this propogates through linearly to the end, where the numbers + // are 1/2 too small, and need to be compensated for. + + { + float *d,*e, *AA, *e_stop; + d = &buf2[n2-2]; + AA = A; + e = &buffer[0]; + e_stop = &buffer[n2]; + while (e != e_stop) { + d[1] = (e[0] * AA[0] - e[2]*AA[1]); + d[0] = (e[0] * AA[1] + e[2]*AA[0]); + d -= 2; + AA += 2; + e += 4; + } + + e = &buffer[n2-3]; + while (d >= buf2) { + d[1] = (-e[2] * AA[0] - -e[0]*AA[1]); + d[0] = (-e[2] * AA[1] + -e[0]*AA[0]); + d -= 2; + AA += 2; + e -= 4; + } + } + + // now we use symbolic names for these, so that we can + // possibly swap their meaning as we change which operations + // are in place + + u = buffer; + v = buf2; + + // step 2 (paper output is w, now u) + // this could be in place, but the data ends up in the wrong + // place... _somebody_'s got to swap it, so this is nominated + { + float *AA = &A[n2-8]; + float *d0,*d1, *e0, *e1; + + e0 = &v[n4]; + e1 = &v[0]; + + d0 = &u[n4]; + d1 = &u[0]; + + while (AA >= A) { + float v40_20, v41_21; + + v41_21 = e0[1] - e1[1]; + v40_20 = e0[0] - e1[0]; + d0[1] = e0[1] + e1[1]; + d0[0] = e0[0] + e1[0]; + d1[1] = v41_21*AA[4] - v40_20*AA[5]; + d1[0] = v40_20*AA[4] + v41_21*AA[5]; + + v41_21 = e0[3] - e1[3]; + v40_20 = e0[2] - e1[2]; + d0[3] = e0[3] + e1[3]; + d0[2] = e0[2] + e1[2]; + d1[3] = v41_21*AA[0] - v40_20*AA[1]; + d1[2] = v40_20*AA[0] + v41_21*AA[1]; + + AA -= 8; + + d0 += 4; + d1 += 4; + e0 += 4; + e1 += 4; + } + } + + // step 3 + ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + + // optimized step 3: + + // the original step3 loop can be nested r inside s or s inside r; + // it's written originally as s inside r, but this is dumb when r + // iterates many times, and s few. So I have two copies of it and + // switch between them halfway. + + // this is iteration 0 of step 3 + imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*0, -(n >> 3), A); + imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*1, -(n >> 3), A); + + // this is iteration 1 of step 3 + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*0, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*1, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*2, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*3, -(n >> 4), A, 16); + + l=2; + for (; l < (ld-3)>>1; ++l) { + int k0 = n >> (l+2), k0_2 = k0>>1; + int lim = 1 << (l+1); + int i; + for (i=0; i < lim; ++i) + imdct_step3_inner_r_loop(n >> (l+4), u, n2-1 - k0*i, -k0_2, A, 1 << (l+3)); + } + + for (; l < ld-6; ++l) { + int k0 = n >> (l+2), k1 = 1 << (l+3), k0_2 = k0>>1; + int rlim = n >> (l+6), r; + int lim = 1 << (l+1); + int i_off; + float *A0 = A; + i_off = n2-1; + for (r=rlim; r > 0; --r) { + imdct_step3_inner_s_loop(lim, u, i_off, -k0_2, A0, k1, k0); + A0 += k1*4; + i_off -= 8; + } + } + + // iterations with count: + // ld-6,-5,-4 all interleaved together + // the big win comes from getting rid of needless flops + // due to the constants on pass 5 & 4 being all 1 and 0; + // combining them to be simultaneous to improve cache made little difference + imdct_step3_inner_s_loop_ld654(n >> 5, u, n2-1, A, n); + + // output is u + + // step 4, 5, and 6 + // cannot be in-place because of step 5 + { + uint16 *bitrev = f->bit_reverse[blocktype]; + // weirdly, I'd have thought reading sequentially and writing + // erratically would have been better than vice-versa, but in + // fact that's not what my testing showed. (That is, with + // j = bitreverse(i), do you read i and write j, or read j and write i.) + + float *d0 = &v[n4-4]; + float *d1 = &v[n2-4]; + while (d0 >= v) { + int k4; + + k4 = bitrev[0]; + d1[3] = u[k4+0]; + d1[2] = u[k4+1]; + d0[3] = u[k4+2]; + d0[2] = u[k4+3]; + + k4 = bitrev[1]; + d1[1] = u[k4+0]; + d1[0] = u[k4+1]; + d0[1] = u[k4+2]; + d0[0] = u[k4+3]; + + d0 -= 4; + d1 -= 4; + bitrev += 2; + } + } + // (paper output is u, now v) + + + // data must be in buf2 + assert(v == buf2); + + // step 7 (paper output is v, now v) + // this is now in place + { + float *C = f->C[blocktype]; + float *d, *e; + + d = v; + e = v + n2 - 4; + + while (d < e) { + float a02,a11,b0,b1,b2,b3; + + a02 = d[0] - e[2]; + a11 = d[1] + e[3]; + + b0 = C[1]*a02 + C[0]*a11; + b1 = C[1]*a11 - C[0]*a02; + + b2 = d[0] + e[ 2]; + b3 = d[1] - e[ 3]; + + d[0] = b2 + b0; + d[1] = b3 + b1; + e[2] = b2 - b0; + e[3] = b1 - b3; + + a02 = d[2] - e[0]; + a11 = d[3] + e[1]; + + b0 = C[3]*a02 + C[2]*a11; + b1 = C[3]*a11 - C[2]*a02; + + b2 = d[2] + e[ 0]; + b3 = d[3] - e[ 1]; + + d[2] = b2 + b0; + d[3] = b3 + b1; + e[0] = b2 - b0; + e[1] = b1 - b3; + + C += 4; + d += 4; + e -= 4; + } + } + + // data must be in buf2 + + + // step 8+decode (paper output is X, now buffer) + // this generates pairs of data a la 8 and pushes them directly through + // the decode kernel (pushing rather than pulling) to avoid having + // to make another pass later + + // this cannot POSSIBLY be in place, so we refer to the buffers directly + + { + float *d0,*d1,*d2,*d3; + + float *B = f->B[blocktype] + n2 - 8; + float *e = buf2 + n2 - 8; + d0 = &buffer[0]; + d1 = &buffer[n2-4]; + d2 = &buffer[n2]; + d3 = &buffer[n-4]; + while (e >= v) { + float p0,p1,p2,p3; + + p3 = e[6]*B[7] - e[7]*B[6]; + p2 = -e[6]*B[6] - e[7]*B[7]; + + d0[0] = p3; + d1[3] = - p3; + d2[0] = p2; + d3[3] = p2; + + p1 = e[4]*B[5] - e[5]*B[4]; + p0 = -e[4]*B[4] - e[5]*B[5]; + + d0[1] = p1; + d1[2] = - p1; + d2[1] = p0; + d3[2] = p0; + + p3 = e[2]*B[3] - e[3]*B[2]; + p2 = -e[2]*B[2] - e[3]*B[3]; + + d0[2] = p3; + d1[1] = - p3; + d2[2] = p2; + d3[1] = p2; + + p1 = e[0]*B[1] - e[1]*B[0]; + p0 = -e[0]*B[0] - e[1]*B[1]; + + d0[3] = p1; + d1[0] = - p1; + d2[3] = p0; + d3[0] = p0; + + B -= 8; + e -= 8; + d0 += 4; + d2 += 4; + d1 -= 4; + d3 -= 4; + } + } + + temp_alloc_restore(f,save_point); +} + +#if 0 +// this is the original version of the above code, if you want to optimize it from scratch +void inverse_mdct_naive(float *buffer, int n) +{ + float s; + float A[1 << 12], B[1 << 12], C[1 << 11]; + int i,k,k2,k4, n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; + int n3_4 = n - n4, ld; + // how can they claim this only uses N words?! + // oh, because they're only used sparsely, whoops + float u[1 << 13], X[1 << 13], v[1 << 13], w[1 << 13]; + // set up twiddle factors + + for (k=k2=0; k < n4; ++k,k2+=2) { + A[k2 ] = (float) cos(4*k*M_PI/n); + A[k2+1] = (float) -sin(4*k*M_PI/n); + B[k2 ] = (float) cos((k2+1)*M_PI/n/2); + B[k2+1] = (float) sin((k2+1)*M_PI/n/2); + } + for (k=k2=0; k < n8; ++k,k2+=2) { + C[k2 ] = (float) cos(2*(k2+1)*M_PI/n); + C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n); + } + + // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" + // Note there are bugs in that pseudocode, presumably due to them attempting + // to rename the arrays nicely rather than representing the way their actual + // implementation bounces buffers back and forth. As a result, even in the + // "some formulars corrected" version, a direct implementation fails. These + // are noted below as "paper bug". + + // copy and reflect spectral data + for (k=0; k < n2; ++k) u[k] = buffer[k]; + for ( ; k < n ; ++k) u[k] = -buffer[n - k - 1]; + // kernel from paper + // step 1 + for (k=k2=k4=0; k < n4; k+=1, k2+=2, k4+=4) { + v[n-k4-1] = (u[k4] - u[n-k4-1]) * A[k2] - (u[k4+2] - u[n-k4-3])*A[k2+1]; + v[n-k4-3] = (u[k4] - u[n-k4-1]) * A[k2+1] + (u[k4+2] - u[n-k4-3])*A[k2]; + } + // step 2 + for (k=k4=0; k < n8; k+=1, k4+=4) { + w[n2+3+k4] = v[n2+3+k4] + v[k4+3]; + w[n2+1+k4] = v[n2+1+k4] + v[k4+1]; + w[k4+3] = (v[n2+3+k4] - v[k4+3])*A[n2-4-k4] - (v[n2+1+k4]-v[k4+1])*A[n2-3-k4]; + w[k4+1] = (v[n2+1+k4] - v[k4+1])*A[n2-4-k4] + (v[n2+3+k4]-v[k4+3])*A[n2-3-k4]; + } + // step 3 + ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + for (l=0; l < ld-3; ++l) { + int k0 = n >> (l+2), k1 = 1 << (l+3); + int rlim = n >> (l+4), r4, r; + int s2lim = 1 << (l+2), s2; + for (r=r4=0; r < rlim; r4+=4,++r) { + for (s2=0; s2 < s2lim; s2+=2) { + u[n-1-k0*s2-r4] = w[n-1-k0*s2-r4] + w[n-1-k0*(s2+1)-r4]; + u[n-3-k0*s2-r4] = w[n-3-k0*s2-r4] + w[n-3-k0*(s2+1)-r4]; + u[n-1-k0*(s2+1)-r4] = (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1] + - (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1+1]; + u[n-3-k0*(s2+1)-r4] = (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1] + + (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1+1]; + } + } + if (l+1 < ld-3) { + // paper bug: ping-ponging of u&w here is omitted + memcpy(w, u, sizeof(u)); + } + } + + // step 4 + for (i=0; i < n8; ++i) { + int j = bit_reverse(i) >> (32-ld+3); + assert(j < n8); + if (i == j) { + // paper bug: original code probably swapped in place; if copying, + // need to directly copy in this case + int i8 = i << 3; + v[i8+1] = u[i8+1]; + v[i8+3] = u[i8+3]; + v[i8+5] = u[i8+5]; + v[i8+7] = u[i8+7]; + } else if (i < j) { + int i8 = i << 3, j8 = j << 3; + v[j8+1] = u[i8+1], v[i8+1] = u[j8 + 1]; + v[j8+3] = u[i8+3], v[i8+3] = u[j8 + 3]; + v[j8+5] = u[i8+5], v[i8+5] = u[j8 + 5]; + v[j8+7] = u[i8+7], v[i8+7] = u[j8 + 7]; + } + } + // step 5 + for (k=0; k < n2; ++k) { + w[k] = v[k*2+1]; + } + // step 6 + for (k=k2=k4=0; k < n8; ++k, k2 += 2, k4 += 4) { + u[n-1-k2] = w[k4]; + u[n-2-k2] = w[k4+1]; + u[n3_4 - 1 - k2] = w[k4+2]; + u[n3_4 - 2 - k2] = w[k4+3]; + } + // step 7 + for (k=k2=0; k < n8; ++k, k2 += 2) { + v[n2 + k2 ] = ( u[n2 + k2] + u[n-2-k2] + C[k2+1]*(u[n2+k2]-u[n-2-k2]) + C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2; + v[n-2 - k2] = ( u[n2 + k2] + u[n-2-k2] - C[k2+1]*(u[n2+k2]-u[n-2-k2]) - C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2; + v[n2+1+ k2] = ( u[n2+1+k2] - u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2; + v[n-1 - k2] = (-u[n2+1+k2] + u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2; + } + // step 8 + for (k=k2=0; k < n4; ++k,k2 += 2) { + X[k] = v[k2+n2]*B[k2 ] + v[k2+1+n2]*B[k2+1]; + X[n2-1-k] = v[k2+n2]*B[k2+1] - v[k2+1+n2]*B[k2 ]; + } + + // decode kernel to output + // determined the following value experimentally + // (by first figuring out what made inverse_mdct_slow work); then matching that here + // (probably vorbis encoder premultiplies by n or n/2, to save it on the decoder?) + s = 0.5; // theoretically would be n4 + + // [[[ note! the s value of 0.5 is compensated for by the B[] in the current code, + // so it needs to use the "old" B values to behave correctly, or else + // set s to 1.0 ]]] + for (i=0; i < n4 ; ++i) buffer[i] = s * X[i+n4]; + for ( ; i < n3_4; ++i) buffer[i] = -s * X[n3_4 - i - 1]; + for ( ; i < n ; ++i) buffer[i] = -s * X[i - n3_4]; +} +#endif + +static float *get_window(vorb *f, int len) +{ + len <<= 1; + if (len == f->blocksize_0) return f->window[0]; + if (len == f->blocksize_1) return f->window[1]; + assert(0); + return NULL; +} + +#ifndef STB_VORBIS_NO_DEFER_FLOOR +typedef int16 YTYPE; +#else +typedef int YTYPE; +#endif +static int do_floor(vorb *f, Mapping *map, int i, int n, float *target, YTYPE *finalY, uint8 *step2_flag) +{ + int n2 = n >> 1; + int s = map->chan[i].mux, floor; + floor = map->submap_floor[s]; + if (f->floor_types[floor] == 0) { + return error(f, VORBIS_invalid_stream); + } else { + Floor1 *g = &f->floor_config[floor].floor1; + int j,q; + int lx = 0, ly = finalY[0] * g->floor1_multiplier; + for (q=1; q < g->values; ++q) { + j = g->sorted_order[q]; + #ifndef STB_VORBIS_NO_DEFER_FLOOR + if (finalY[j] >= 0) + #else + if (step2_flag[j]) + #endif + { + int hy = finalY[j] * g->floor1_multiplier; + int hx = g->Xlist[j]; + draw_line(target, lx,ly, hx,hy, n2); + lx = hx, ly = hy; + } + } + if (lx < n2) + // optimization of: draw_line(target, lx,ly, n,ly, n2); + for (j=lx; j < n2; ++j) + LINE_OP(target[j], inverse_db_table[ly]); + } + return TRUE; +} + +static int vorbis_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) +{ + Mode *m; + int i, n, prev, next, window_center; + f->channel_buffer_start = f->channel_buffer_end = 0; + + retry: + if (f->eof) return FALSE; + if (!maybe_start_packet(f)) + return FALSE; + // check packet type + if (get_bits(f,1) != 0) { + if (IS_PUSH_MODE(f)) + return error(f,VORBIS_bad_packet_type); + while (EOP != get8_packet(f)); + goto retry; + } + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + + i = get_bits(f, ilog(f->mode_count-1)); + if (i == EOP) return FALSE; + if (i >= f->mode_count) return FALSE; + *mode = i; + m = f->mode_config + i; + if (m->blockflag) { + n = f->blocksize_1; + prev = get_bits(f,1); + next = get_bits(f,1); + } else { + prev = next = 0; + n = f->blocksize_0; + } + +// WINDOWING + + window_center = n >> 1; + if (m->blockflag && !prev) { + *p_left_start = (n - f->blocksize_0) >> 2; + *p_left_end = (n + f->blocksize_0) >> 2; + } else { + *p_left_start = 0; + *p_left_end = window_center; + } + if (m->blockflag && !next) { + *p_right_start = (n*3 - f->blocksize_0) >> 2; + *p_right_end = (n*3 + f->blocksize_0) >> 2; + } else { + *p_right_start = window_center; + *p_right_end = n; + } + return TRUE; +} + +static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, int left_end, int right_start, int right_end, int *p_left) +{ + Mapping *map; + int i,j,k,n,n2; + int zero_channel[256]; + int really_zero_channel[256]; + int window_center; + +// WINDOWING + + n = f->blocksize[m->blockflag]; + window_center = n >> 1; + + map = &f->mapping[m->mapping]; + +// FLOORS + n2 = n >> 1; + + stb_prof(1); + for (i=0; i < f->channels; ++i) { + int s = map->chan[i].mux, floor; + zero_channel[i] = FALSE; + floor = map->submap_floor[s]; + if (f->floor_types[floor] == 0) { + return error(f, VORBIS_invalid_stream); + } else { + Floor1 *g = &f->floor_config[floor].floor1; + if (get_bits(f, 1)) { + short *finalY; + uint8 step2_flag[256]; + static int range_list[4] = { 256, 128, 86, 64 }; + int range = range_list[g->floor1_multiplier-1]; + int offset = 2; + finalY = f->finalY[i]; + finalY[0] = get_bits(f, ilog(range)-1); + finalY[1] = get_bits(f, ilog(range)-1); + for (j=0; j < g->partitions; ++j) { + int pclass = g->partition_class_list[j]; + int cdim = g->class_dimensions[pclass]; + int cbits = g->class_subclasses[pclass]; + int csub = (1 << cbits)-1; + int cval = 0; + if (cbits) { + Codebook *c = f->codebooks + g->class_masterbooks[pclass]; + DECODE(cval,f,c); + } + for (k=0; k < cdim; ++k) { + int book = g->subclass_books[pclass][cval & csub]; + cval = cval >> cbits; + if (book >= 0) { + int temp; + Codebook *c = f->codebooks + book; + DECODE(temp,f,c); + finalY[offset++] = temp; + } else + finalY[offset++] = 0; + } + } + if (f->valid_bits == INVALID_BITS) goto error; // behavior according to spec + step2_flag[0] = step2_flag[1] = 1; + for (j=2; j < g->values; ++j) { + int low, high, pred, highroom, lowroom, room, val; + low = g->neighbors[j][0]; + high = g->neighbors[j][1]; + //neighbors(g->Xlist, j, &low, &high); + pred = predict_point(g->Xlist[j], g->Xlist[low], g->Xlist[high], finalY[low], finalY[high]); + val = finalY[j]; + highroom = range - pred; + lowroom = pred; + if (highroom < lowroom) + room = highroom * 2; + else + room = lowroom * 2; + if (val) { + step2_flag[low] = step2_flag[high] = 1; + step2_flag[j] = 1; + if (val >= room) + if (highroom > lowroom) + finalY[j] = val - lowroom + pred; + else + finalY[j] = pred - val + highroom - 1; + else + if (val & 1) + finalY[j] = pred - ((val+1)>>1); + else + finalY[j] = pred + (val>>1); + } else { + step2_flag[j] = 0; + finalY[j] = pred; + } + } + +#ifdef STB_VORBIS_NO_DEFER_FLOOR + do_floor(f, map, i, n, f->floor_buffers[i], finalY, step2_flag); +#else + // defer final floor computation until _after_ residue + for (j=0; j < g->values; ++j) { + if (!step2_flag[j]) + finalY[j] = -1; + } +#endif + } else { + error: + zero_channel[i] = TRUE; + } + // So we just defer everything else to later + + // at this point we've decoded the floor into buffer + } + } + stb_prof(0); + // at this point we've decoded all floors + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + + // re-enable coupled channels if necessary + memcpy(really_zero_channel, zero_channel, sizeof(really_zero_channel[0]) * f->channels); + for (i=0; i < map->coupling_steps; ++i) + if (!zero_channel[map->chan[i].magnitude] || !zero_channel[map->chan[i].angle]) { + zero_channel[map->chan[i].magnitude] = zero_channel[map->chan[i].angle] = FALSE; + } + +// RESIDUE DECODE + for (i=0; i < map->submaps; ++i) { + float *residue_buffers[STB_VORBIS_MAX_CHANNELS]; + int r,t; + uint8 do_not_decode[256]; + int ch = 0; + for (j=0; j < f->channels; ++j) { + if (map->chan[j].mux == i) { + if (zero_channel[j]) { + do_not_decode[ch] = TRUE; + residue_buffers[ch] = NULL; + } else { + do_not_decode[ch] = FALSE; + residue_buffers[ch] = f->channel_buffers[j]; + } + ++ch; + } + } + r = map->submap_residue[i]; + t = f->residue_types[r]; + decode_residue(f, residue_buffers, ch, n2, r, do_not_decode); + } + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + +// INVERSE COUPLING + stb_prof(14); + for (i = map->coupling_steps-1; i >= 0; --i) { + int n2 = n >> 1; + float *m = f->channel_buffers[map->chan[i].magnitude]; + float *a = f->channel_buffers[map->chan[i].angle ]; + for (j=0; j < n2; ++j) { + float a2,m2; + if (m[j] > 0) + if (a[j] > 0) + m2 = m[j], a2 = m[j] - a[j]; + else + a2 = m[j], m2 = m[j] + a[j]; + else + if (a[j] > 0) + m2 = m[j], a2 = m[j] + a[j]; + else + a2 = m[j], m2 = m[j] - a[j]; + m[j] = m2; + a[j] = a2; + } + } + + // finish decoding the floors +#ifndef STB_VORBIS_NO_DEFER_FLOOR + stb_prof(15); + for (i=0; i < f->channels; ++i) { + if (really_zero_channel[i]) { + memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); + } else { + do_floor(f, map, i, n, f->channel_buffers[i], f->finalY[i], NULL); + } + } +#else + for (i=0; i < f->channels; ++i) { + if (really_zero_channel[i]) { + memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); + } else { + for (j=0; j < n2; ++j) + f->channel_buffers[i][j] *= f->floor_buffers[i][j]; + } + } +#endif + +// INVERSE MDCT + stb_prof(16); + for (i=0; i < f->channels; ++i) + inverse_mdct(f->channel_buffers[i], n, f, m->blockflag); + stb_prof(0); + + // this shouldn't be necessary, unless we exited on an error + // and want to flush to get to the next packet + flush_packet(f); + + if (f->first_decode) { + // assume we start so first non-discarded sample is sample 0 + // this isn't to spec, but spec would require us to read ahead + // and decode the size of all current frames--could be done, + // but presumably it's not a commonly used feature + f->current_loc = -n2; // start of first frame is positioned for discard + // we might have to discard samples "from" the next frame too, + // if we're lapping a large block then a small at the start? + f->discard_samples_deferred = n - right_end; + f->current_loc_valid = TRUE; + f->first_decode = FALSE; + } else if (f->discard_samples_deferred) { + left_start += f->discard_samples_deferred; + *p_left = left_start; + f->discard_samples_deferred = 0; + } else if (f->previous_length == 0 && f->current_loc_valid) { + // we're recovering from a seek... that means we're going to discard + // the samples from this packet even though we know our position from + // the last page header, so we need to update the position based on + // the discarded samples here + // but wait, the code below is going to add this in itself even + // on a discard, so we don't need to do it here... + } + + // check if we have ogg information about the sample # for this packet + if (f->last_seg_which == f->end_seg_with_known_loc) { + // if we have a valid current loc, and this is final: + if (f->current_loc_valid && (f->page_flag & PAGEFLAG_last_page)) { + uint32 current_end = f->known_loc_for_packet - (n-right_end); + // then let's infer the size of the (probably) short final frame + if (current_end < f->current_loc + right_end) { + if (current_end < f->current_loc) { + // negative truncation, that's impossible! + *len = 0; + } else { + *len = current_end - f->current_loc; + } + *len += left_start; + f->current_loc += *len; + return TRUE; + } + } + // otherwise, just set our sample loc + // guess that the ogg granule pos refers to the _middle_ of the + // last frame? + // set f->current_loc to the position of left_start + f->current_loc = f->known_loc_for_packet - (n2-left_start); + f->current_loc_valid = TRUE; + } + if (f->current_loc_valid) + f->current_loc += (right_start - left_start); + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + *len = right_end; // ignore samples after the window goes to 0 + return TRUE; +} + +static int vorbis_decode_packet(vorb *f, int *len, int *p_left, int *p_right) +{ + int mode, left_end, right_end; + if (!vorbis_decode_initial(f, p_left, &left_end, p_right, &right_end, &mode)) return 0; + return vorbis_decode_packet_rest(f, len, f->mode_config + mode, *p_left, left_end, *p_right, right_end, p_left); +} + +static int vorbis_finish_frame(stb_vorbis *f, int len, int left, int right) +{ + int prev,i,j; + // we use right&left (the start of the right- and left-window sin()-regions) + // to determine how much to return, rather than inferring from the rules + // (same result, clearer code); 'left' indicates where our sin() window + // starts, therefore where the previous window's right edge starts, and + // therefore where to start mixing from the previous buffer. 'right' + // indicates where our sin() ending-window starts, therefore that's where + // we start saving, and where our returned-data ends. + + // mixin from previous window + if (f->previous_length) { + int i,j, n = f->previous_length; + float *w = get_window(f, n); + for (i=0; i < f->channels; ++i) { + for (j=0; j < n; ++j) + f->channel_buffers[i][left+j] = + f->channel_buffers[i][left+j]*w[ j] + + f->previous_window[i][ j]*w[n-1-j]; + } + } + + prev = f->previous_length; + + // last half of this data becomes previous window + f->previous_length = len - right; + + // @OPTIMIZE: could avoid this copy by double-buffering the + // output (flipping previous_window with channel_buffers), but + // then previous_window would have to be 2x as large, and + // channel_buffers couldn't be temp mem (although they're NOT + // currently temp mem, they could be (unless we want to level + // performance by spreading out the computation)) + for (i=0; i < f->channels; ++i) + for (j=0; right+j < len; ++j) + f->previous_window[i][j] = f->channel_buffers[i][right+j]; + + if (!prev) + // there was no previous packet, so this data isn't valid... + // this isn't entirely true, only the would-have-overlapped data + // isn't valid, but this seems to be what the spec requires + return 0; + + // truncate a short frame + if (len < right) right = len; + + f->samples_output += right-left; + + return right - left; +} + +static void vorbis_pump_first_frame(stb_vorbis *f) +{ + int len, right, left; + if (vorbis_decode_packet(f, &len, &left, &right)) + vorbis_finish_frame(f, len, left, right); +} + +#ifndef STB_VORBIS_NO_PUSHDATA_API +static int is_whole_packet_present(stb_vorbis *f, int end_page) +{ + // make sure that we have the packet available before continuing... + // this requires a full ogg parse, but we know we can fetch from f->stream + + // instead of coding this out explicitly, we could save the current read state, + // read the next packet with get8() until end-of-packet, check f->eof, then + // reset the state? but that would be slower, esp. since we'd have over 256 bytes + // of state to restore (primarily the page segment table) + + int s = f->next_seg, first = TRUE; + uint8 *p = f->stream; + + if (s != -1) { // if we're not starting the packet with a 'continue on next page' flag + for (; s < f->segment_count; ++s) { + p += f->segments[s]; + if (f->segments[s] < 255) // stop at first short segment + break; + } + // either this continues, or it ends it... + if (end_page) + if (s < f->segment_count-1) return error(f, VORBIS_invalid_stream); + if (s == f->segment_count) + s = -1; // set 'crosses page' flag + if (p > f->stream_end) return error(f, VORBIS_need_more_data); + first = FALSE; + } + for (; s == -1;) { + uint8 *q; + int n; + + // check that we have the page header ready + if (p + 26 >= f->stream_end) return error(f, VORBIS_need_more_data); + // validate the page + if (memcmp(p, ogg_page_header, 4)) return error(f, VORBIS_invalid_stream); + if (p[4] != 0) return error(f, VORBIS_invalid_stream); + if (first) { // the first segment must NOT have 'continued_packet', later ones MUST + if (f->previous_length) + if ((p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream); + // if no previous length, we're resynching, so we can come in on a continued-packet, + // which we'll just drop + } else { + if (!(p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream); + } + n = p[26]; // segment counts + q = p+27; // q points to segment table + p = q + n; // advance past header + // make sure we've read the segment table + if (p > f->stream_end) return error(f, VORBIS_need_more_data); + for (s=0; s < n; ++s) { + p += q[s]; + if (q[s] < 255) + break; + } + if (end_page) + if (s < n-1) return error(f, VORBIS_invalid_stream); + if (s == f->segment_count) + s = -1; // set 'crosses page' flag + if (p > f->stream_end) return error(f, VORBIS_need_more_data); + first = FALSE; + } + return TRUE; +} +#endif // !STB_VORBIS_NO_PUSHDATA_API + +static int start_decoder(vorb *f) +{ + uint8 header[6], x,y; + int len,i,j,k, max_submaps = 0; + int longest_floorlist=0; + + // first page, first packet + + if (!start_page(f)) return FALSE; + // validate page flag + if (!(f->page_flag & PAGEFLAG_first_page)) return error(f, VORBIS_invalid_first_page); + if (f->page_flag & PAGEFLAG_last_page) return error(f, VORBIS_invalid_first_page); + if (f->page_flag & PAGEFLAG_continued_packet) return error(f, VORBIS_invalid_first_page); + // check for expected packet length + if (f->segment_count != 1) return error(f, VORBIS_invalid_first_page); + if (f->segments[0] != 30) return error(f, VORBIS_invalid_first_page); + // read packet + // check packet header + if (get8(f) != VORBIS_packet_id) return error(f, VORBIS_invalid_first_page); + if (!getn(f, header, 6)) return error(f, VORBIS_unexpected_eof); + if (!vorbis_validate(header)) return error(f, VORBIS_invalid_first_page); + // vorbis_version + if (get32(f) != 0) return error(f, VORBIS_invalid_first_page); + f->channels = get8(f); if (!f->channels) return error(f, VORBIS_invalid_first_page); + if (f->channels > STB_VORBIS_MAX_CHANNELS) return error(f, VORBIS_too_many_channels); + f->sample_rate = get32(f); if (!f->sample_rate) return error(f, VORBIS_invalid_first_page); + get32(f); // bitrate_maximum + get32(f); // bitrate_nominal + get32(f); // bitrate_minimum + x = get8(f); + { int log0,log1; + log0 = x & 15; + log1 = x >> 4; + f->blocksize_0 = 1 << log0; + f->blocksize_1 = 1 << log1; + if (log0 < 6 || log0 > 13) return error(f, VORBIS_invalid_setup); + if (log1 < 6 || log1 > 13) return error(f, VORBIS_invalid_setup); + if (log0 > log1) return error(f, VORBIS_invalid_setup); + } + + // framing_flag + x = get8(f); + if (!(x & 1)) return error(f, VORBIS_invalid_first_page); + + // second packet! + if (!start_page(f)) return FALSE; + + if (!start_packet(f)) return FALSE; + do { + len = next_segment(f); + skip(f, len); + f->bytes_in_seg = 0; + } while (len); + + // third packet! + if (!start_packet(f)) return FALSE; + + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (IS_PUSH_MODE(f)) { + if (!is_whole_packet_present(f, TRUE)) { + // convert error in ogg header to write type + if (f->error == VORBIS_invalid_stream) + f->error = VORBIS_invalid_setup; + return FALSE; + } + } + #endif + + crc32_init(); // always init it, to avoid multithread race conditions + + if (get8_packet(f) != VORBIS_packet_setup) return error(f, VORBIS_invalid_setup); + for (i=0; i < 6; ++i) header[i] = get8_packet(f); + if (!vorbis_validate(header)) return error(f, VORBIS_invalid_setup); + + // codebooks + + f->codebook_count = get_bits(f,8) + 1; + f->codebooks = (Codebook *) setup_malloc(f, sizeof(*f->codebooks) * f->codebook_count); + if (f->codebooks == NULL) return error(f, VORBIS_outofmem); + memset(f->codebooks, 0, sizeof(*f->codebooks) * f->codebook_count); + for (i=0; i < f->codebook_count; ++i) { + uint32 *values; + int ordered, sorted_count; + int total=0; + uint8 *lengths; + Codebook *c = f->codebooks+i; + x = get_bits(f, 8); if (x != 0x42) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); if (x != 0x43) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); if (x != 0x56) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); + c->dimensions = (get_bits(f, 8)<<8) + x; + x = get_bits(f, 8); + y = get_bits(f, 8); + c->entries = (get_bits(f, 8)<<16) + (y<<8) + x; + ordered = get_bits(f,1); + c->sparse = ordered ? 0 : get_bits(f,1); + + if (c->sparse) + lengths = (uint8 *) setup_temp_malloc(f, c->entries); + else + lengths = c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries); + + if (!lengths) return error(f, VORBIS_outofmem); + + if (ordered) { + int current_entry = 0; + int current_length = get_bits(f,5) + 1; + while (current_entry < c->entries) { + int limit = c->entries - current_entry; + int n = get_bits(f, ilog(limit)); + if (current_entry + n > (int) c->entries) { return error(f, VORBIS_invalid_setup); } + memset(lengths + current_entry, current_length, n); + current_entry += n; + ++current_length; + } + } else { + for (j=0; j < c->entries; ++j) { + int present = c->sparse ? get_bits(f,1) : 1; + if (present) { + lengths[j] = get_bits(f, 5) + 1; + ++total; + } else { + lengths[j] = NO_CODE; + } + } + } + + if (c->sparse && total >= c->entries >> 2) { + // convert sparse items to non-sparse! + if (c->entries > (int) f->setup_temp_memory_required) + f->setup_temp_memory_required = c->entries; + + c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries); + memcpy(c->codeword_lengths, lengths, c->entries); + setup_temp_free(f, lengths, c->entries); // note this is only safe if there have been no intervening temp mallocs! + lengths = c->codeword_lengths; + c->sparse = 0; + } + + // compute the size of the sorted tables + if (c->sparse) { + sorted_count = total; + //assert(total != 0); + } else { + sorted_count = 0; + #ifndef STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH + for (j=0; j < c->entries; ++j) + if (lengths[j] > STB_VORBIS_FAST_HUFFMAN_LENGTH && lengths[j] != NO_CODE) + ++sorted_count; + #endif + } + + c->sorted_entries = sorted_count; + values = NULL; + + if (!c->sparse) { + c->codewords = (uint32 *) setup_malloc(f, sizeof(c->codewords[0]) * c->entries); + if (!c->codewords) return error(f, VORBIS_outofmem); + } else { + unsigned int size; + if (c->sorted_entries) { + c->codeword_lengths = (uint8 *) setup_malloc(f, c->sorted_entries); + if (!c->codeword_lengths) return error(f, VORBIS_outofmem); + c->codewords = (uint32 *) setup_temp_malloc(f, sizeof(*c->codewords) * c->sorted_entries); + if (!c->codewords) return error(f, VORBIS_outofmem); + values = (uint32 *) setup_temp_malloc(f, sizeof(*values) * c->sorted_entries); + if (!values) return error(f, VORBIS_outofmem); + } + size = c->entries + (sizeof(*c->codewords) + sizeof(*values)) * c->sorted_entries; + if (size > f->setup_temp_memory_required) + f->setup_temp_memory_required = size; + } + + if (!compute_codewords(c, lengths, c->entries, values)) { + if (c->sparse) setup_temp_free(f, values, 0); + return error(f, VORBIS_invalid_setup); + } + + if (c->sorted_entries) { + // allocate an extra slot for sentinels + c->sorted_codewords = (uint32 *) setup_malloc(f, sizeof(*c->sorted_codewords) * (c->sorted_entries+1)); + // allocate an extra slot at the front so that c->sorted_values[-1] is defined + // so that we can catch that case without an extra if + c->sorted_values = ( int *) setup_malloc(f, sizeof(*c->sorted_values ) * (c->sorted_entries+1)); + if (c->sorted_values) { ++c->sorted_values; c->sorted_values[-1] = -1; } + compute_sorted_huffman(c, lengths, values); + } + + if (c->sparse) { + setup_temp_free(f, values, sizeof(*values)*c->sorted_entries); + setup_temp_free(f, c->codewords, sizeof(*c->codewords)*c->sorted_entries); + setup_temp_free(f, lengths, c->entries); + c->codewords = NULL; + } + + compute_accelerated_huffman(c); + + c->lookup_type = get_bits(f, 4); + if (c->lookup_type > 2) return error(f, VORBIS_invalid_setup); + if (c->lookup_type > 0) { + uint16 *mults; + c->minimum_value = float32_unpack(get_bits(f, 32)); + c->delta_value = float32_unpack(get_bits(f, 32)); + c->value_bits = get_bits(f, 4)+1; + c->sequence_p = get_bits(f,1); + if (c->lookup_type == 1) { + c->lookup_values = lookup1_values(c->entries, c->dimensions); + } else { + c->lookup_values = c->entries * c->dimensions; + } + mults = (uint16 *) setup_temp_malloc(f, sizeof(mults[0]) * c->lookup_values); + if (mults == NULL) return error(f, VORBIS_outofmem); + for (j=0; j < (int) c->lookup_values; ++j) { + int q = get_bits(f, c->value_bits); + if (q == EOP) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_invalid_setup); } + mults[j] = q; + } + +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int len, sparse = c->sparse; + // pre-expand the lookup1-style multiplicands, to avoid a divide in the inner loop + if (sparse) { + if (c->sorted_entries == 0) goto skip; + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->sorted_entries * c->dimensions); + } else + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->entries * c->dimensions); + if (c->multiplicands == NULL) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); } + len = sparse ? c->sorted_entries : c->entries; + for (j=0; j < len; ++j) { + int z = sparse ? c->sorted_values[j] : j, div=1; + for (k=0; k < c->dimensions; ++k) { + int off = (z / div) % c->lookup_values; + c->multiplicands[j*c->dimensions + k] = + #ifndef STB_VORBIS_CODEBOOK_FLOATS + mults[off]; + #else + mults[off]*c->delta_value + c->minimum_value; + // in this case (and this case only) we could pre-expand c->sequence_p, + // and throw away the decode logic for it; have to ALSO do + // it in the case below, but it can only be done if + // STB_VORBIS_CODEBOOK_FLOATS + // !STB_VORBIS_DIVIDES_IN_CODEBOOK + #endif + div *= c->lookup_values; + } + } + setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); + c->lookup_type = 2; + } + else +#endif + { + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->lookup_values); + #ifndef STB_VORBIS_CODEBOOK_FLOATS + memcpy(c->multiplicands, mults, sizeof(c->multiplicands[0]) * c->lookup_values); + #else + for (j=0; j < (int) c->lookup_values; ++j) + c->multiplicands[j] = mults[j] * c->delta_value + c->minimum_value; + setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); + #endif + } + skip:; + + #ifdef STB_VORBIS_CODEBOOK_FLOATS + if (c->lookup_type == 2 && c->sequence_p) { + for (j=1; j < (int) c->lookup_values; ++j) + c->multiplicands[j] = c->multiplicands[j-1]; + c->sequence_p = 0; + } + #endif + } + } + + // time domain transfers (notused) + + x = get_bits(f, 6) + 1; + for (i=0; i < x; ++i) { + uint32 z = get_bits(f, 16); + if (z != 0) return error(f, VORBIS_invalid_setup); + } + + // Floors + f->floor_count = get_bits(f, 6)+1; + f->floor_config = (Floor *) setup_malloc(f, f->floor_count * sizeof(*f->floor_config)); + for (i=0; i < f->floor_count; ++i) { + f->floor_types[i] = get_bits(f, 16); + if (f->floor_types[i] > 1) return error(f, VORBIS_invalid_setup); + if (f->floor_types[i] == 0) { + Floor0 *g = &f->floor_config[i].floor0; + g->order = get_bits(f,8); + g->rate = get_bits(f,16); + g->bark_map_size = get_bits(f,16); + g->amplitude_bits = get_bits(f,6); + g->amplitude_offset = get_bits(f,8); + g->number_of_books = get_bits(f,4) + 1; + for (j=0; j < g->number_of_books; ++j) + g->book_list[j] = get_bits(f,8); + return error(f, VORBIS_feature_not_supported); + } else { + Point p[31*8+2]; + Floor1 *g = &f->floor_config[i].floor1; + int max_class = -1; + g->partitions = get_bits(f, 5); + for (j=0; j < g->partitions; ++j) { + g->partition_class_list[j] = get_bits(f, 4); + if (g->partition_class_list[j] > max_class) + max_class = g->partition_class_list[j]; + } + for (j=0; j <= max_class; ++j) { + g->class_dimensions[j] = get_bits(f, 3)+1; + g->class_subclasses[j] = get_bits(f, 2); + if (g->class_subclasses[j]) { + g->class_masterbooks[j] = get_bits(f, 8); + if (g->class_masterbooks[j] >= f->codebook_count) return error(f, VORBIS_invalid_setup); + } + for (k=0; k < 1 << g->class_subclasses[j]; ++k) { + g->subclass_books[j][k] = get_bits(f,8)-1; + if (g->subclass_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); + } + } + g->floor1_multiplier = get_bits(f,2)+1; + g->rangebits = get_bits(f,4); + g->Xlist[0] = 0; + g->Xlist[1] = 1 << g->rangebits; + g->values = 2; + for (j=0; j < g->partitions; ++j) { + int c = g->partition_class_list[j]; + for (k=0; k < g->class_dimensions[c]; ++k) { + g->Xlist[g->values] = get_bits(f, g->rangebits); + ++g->values; + } + } + // precompute the sorting + for (j=0; j < g->values; ++j) { + p[j].x = g->Xlist[j]; + p[j].y = j; + } + qsort(p, g->values, sizeof(p[0]), point_compare); + for (j=0; j < g->values; ++j) + g->sorted_order[j] = (uint8) p[j].y; + // precompute the neighbors + for (j=2; j < g->values; ++j) { + int low,hi; + neighbors(g->Xlist, j, &low,&hi); + g->neighbors[j][0] = low; + g->neighbors[j][1] = hi; + } + + if (g->values > longest_floorlist) + longest_floorlist = g->values; + } + } + + // Residue + f->residue_count = get_bits(f, 6)+1; + f->residue_config = (Residue *) setup_malloc(f, f->residue_count * sizeof(*f->residue_config)); + for (i=0; i < f->residue_count; ++i) { + uint8 residue_cascade[64]; + Residue *r = f->residue_config+i; + f->residue_types[i] = get_bits(f, 16); + if (f->residue_types[i] > 2) return error(f, VORBIS_invalid_setup); + r->begin = get_bits(f, 24); + r->end = get_bits(f, 24); + r->part_size = get_bits(f,24)+1; + r->classifications = get_bits(f,6)+1; + r->classbook = get_bits(f,8); + for (j=0; j < r->classifications; ++j) { + uint8 high_bits=0; + uint8 low_bits=get_bits(f,3); + if (get_bits(f,1)) + high_bits = get_bits(f,5); + residue_cascade[j] = high_bits*8 + low_bits; + } + r->residue_books = (short (*)[8]) setup_malloc(f, sizeof(r->residue_books[0]) * r->classifications); + for (j=0; j < r->classifications; ++j) { + for (k=0; k < 8; ++k) { + if (residue_cascade[j] & (1 << k)) { + r->residue_books[j][k] = get_bits(f, 8); + if (r->residue_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); + } else { + r->residue_books[j][k] = -1; + } + } + } + // precompute the classifications[] array to avoid inner-loop mod/divide + // call it 'classdata' since we already have r->classifications + r->classdata = (uint8 **) setup_malloc(f, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); + if (!r->classdata) return error(f, VORBIS_outofmem); + memset(r->classdata, 0, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); + for (j=0; j < f->codebooks[r->classbook].entries; ++j) { + int classwords = f->codebooks[r->classbook].dimensions; + int temp = j; + r->classdata[j] = (uint8 *) setup_malloc(f, sizeof(r->classdata[j][0]) * classwords); + for (k=classwords-1; k >= 0; --k) { + r->classdata[j][k] = temp % r->classifications; + temp /= r->classifications; + } + } + } + + f->mapping_count = get_bits(f,6)+1; + f->mapping = (Mapping *) setup_malloc(f, f->mapping_count * sizeof(*f->mapping)); + for (i=0; i < f->mapping_count; ++i) { + Mapping *m = f->mapping + i; + int mapping_type = get_bits(f,16); + if (mapping_type != 0) return error(f, VORBIS_invalid_setup); + m->chan = (MappingChannel *) setup_malloc(f, f->channels * sizeof(*m->chan)); + if (get_bits(f,1)) + m->submaps = get_bits(f,4); + else + m->submaps = 1; + if (m->submaps > max_submaps) + max_submaps = m->submaps; + if (get_bits(f,1)) { + m->coupling_steps = get_bits(f,8)+1; + for (k=0; k < m->coupling_steps; ++k) { + m->chan[k].magnitude = get_bits(f, ilog(f->channels)-1); + m->chan[k].angle = get_bits(f, ilog(f->channels)-1); + if (m->chan[k].magnitude >= f->channels) return error(f, VORBIS_invalid_setup); + if (m->chan[k].angle >= f->channels) return error(f, VORBIS_invalid_setup); + if (m->chan[k].magnitude == m->chan[k].angle) return error(f, VORBIS_invalid_setup); + } + } else + m->coupling_steps = 0; + + // reserved field + if (get_bits(f,2)) return error(f, VORBIS_invalid_setup); + if (m->submaps > 1) { + for (j=0; j < f->channels; ++j) { + m->chan[j].mux = get_bits(f, 4); + if (m->chan[j].mux >= m->submaps) return error(f, VORBIS_invalid_setup); + } + } else + // @SPECIFICATION: this case is missing from the spec + for (j=0; j < f->channels; ++j) + m->chan[j].mux = 0; + + for (j=0; j < m->submaps; ++j) { + get_bits(f,8); // discard + m->submap_floor[j] = get_bits(f,8); + m->submap_residue[j] = get_bits(f,8); + if (m->submap_floor[j] >= f->floor_count) return error(f, VORBIS_invalid_setup); + if (m->submap_residue[j] >= f->residue_count) return error(f, VORBIS_invalid_setup); + } + } + + // Modes + f->mode_count = get_bits(f, 6)+1; + for (i=0; i < f->mode_count; ++i) { + Mode *m = f->mode_config+i; + m->blockflag = get_bits(f,1); + m->windowtype = get_bits(f,16); + m->transformtype = get_bits(f,16); + m->mapping = get_bits(f,8); + if (m->windowtype != 0) return error(f, VORBIS_invalid_setup); + if (m->transformtype != 0) return error(f, VORBIS_invalid_setup); + if (m->mapping >= f->mapping_count) return error(f, VORBIS_invalid_setup); + } + + flush_packet(f); + + f->previous_length = 0; + + for (i=0; i < f->channels; ++i) { + f->channel_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1); + f->previous_window[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2); + f->finalY[i] = (int16 *) setup_malloc(f, sizeof(int16) * longest_floorlist); + #ifdef STB_VORBIS_NO_DEFER_FLOOR + f->floor_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2); + #endif + } + + if (!init_blocksize(f, 0, f->blocksize_0)) return FALSE; + if (!init_blocksize(f, 1, f->blocksize_1)) return FALSE; + f->blocksize[0] = f->blocksize_0; + f->blocksize[1] = f->blocksize_1; + +#ifdef STB_VORBIS_DIVIDE_TABLE + if (integer_divide_table[1][1]==0) + for (i=0; i < DIVTAB_NUMER; ++i) + for (j=1; j < DIVTAB_DENOM; ++j) + integer_divide_table[i][j] = i / j; +#endif + + // compute how much temporary memory is needed + + // 1. + { + uint32 imdct_mem = (f->blocksize_1 * sizeof(float) >> 1); + uint32 classify_mem; + int i,max_part_read=0; + for (i=0; i < f->residue_count; ++i) { + Residue *r = f->residue_config + i; + int n_read = r->end - r->begin; + int part_read = n_read / r->part_size; + if (part_read > max_part_read) + max_part_read = part_read; + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(uint8 *)); + #else + classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(int *)); + #endif + + f->temp_memory_required = classify_mem; + if (imdct_mem > f->temp_memory_required) + f->temp_memory_required = imdct_mem; + } + + f->first_decode = TRUE; + + if (f->alloc.alloc_buffer) { + assert(f->temp_offset == f->alloc.alloc_buffer_length_in_bytes); + // check if there's enough temp memory so we don't error later + if (f->setup_offset + sizeof(*f) + f->temp_memory_required > (unsigned) f->temp_offset) + return error(f, VORBIS_outofmem); + } + + f->first_audio_page_offset = stb_vorbis_get_file_offset(f); + + return TRUE; +} + +static void vorbis_deinit(stb_vorbis *p) +{ + int i,j; + for (i=0; i < p->residue_count; ++i) { + Residue *r = p->residue_config+i; + if (r->classdata) { + for (j=0; j < p->codebooks[r->classbook].entries; ++j) + setup_free(p, r->classdata[j]); + setup_free(p, r->classdata); + } + setup_free(p, r->residue_books); + } + + if (p->codebooks) { + for (i=0; i < p->codebook_count; ++i) { + Codebook *c = p->codebooks + i; + setup_free(p, c->codeword_lengths); + setup_free(p, c->multiplicands); + setup_free(p, c->codewords); + setup_free(p, c->sorted_codewords); + // c->sorted_values[-1] is the first entry in the array + setup_free(p, c->sorted_values ? c->sorted_values-1 : NULL); + } + setup_free(p, p->codebooks); + } + setup_free(p, p->floor_config); + setup_free(p, p->residue_config); + for (i=0; i < p->mapping_count; ++i) + setup_free(p, p->mapping[i].chan); + setup_free(p, p->mapping); + for (i=0; i < p->channels; ++i) { + setup_free(p, p->channel_buffers[i]); + setup_free(p, p->previous_window[i]); + #ifdef STB_VORBIS_NO_DEFER_FLOOR + setup_free(p, p->floor_buffers[i]); + #endif + setup_free(p, p->finalY[i]); + } + for (i=0; i < 2; ++i) { + setup_free(p, p->A[i]); + setup_free(p, p->B[i]); + setup_free(p, p->C[i]); + setup_free(p, p->window[i]); + } + #ifndef STB_VORBIS_NO_STDIO + if (p->close_on_free) fclose(p->f); + #endif +} + +void stb_vorbis_close(stb_vorbis *p) +{ + if (p == NULL) return; + vorbis_deinit(p); + setup_free(p,p); +} + +static void vorbis_init(stb_vorbis *p, stb_vorbis_alloc *z) +{ + memset(p, 0, sizeof(*p)); // NULL out all malloc'd pointers to start + if (z) { + p->alloc = *z; + p->alloc.alloc_buffer_length_in_bytes = (p->alloc.alloc_buffer_length_in_bytes+3) & ~3; + p->temp_offset = p->alloc.alloc_buffer_length_in_bytes; + } + p->eof = 0; + p->error = VORBIS__no_error; + p->stream = NULL; + p->codebooks = NULL; + p->page_crc_tests = -1; + #ifndef STB_VORBIS_NO_STDIO + p->close_on_free = FALSE; + p->f = NULL; + #endif +} + +int stb_vorbis_get_sample_offset(stb_vorbis *f) +{ + if (f->current_loc_valid) + return f->current_loc; + else + return -1; +} + +stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f) +{ + stb_vorbis_info d; + d.channels = f->channels; + d.sample_rate = f->sample_rate; + d.setup_memory_required = f->setup_memory_required; + d.setup_temp_memory_required = f->setup_temp_memory_required; + d.temp_memory_required = f->temp_memory_required; + d.max_frame_size = f->blocksize_1 >> 1; + return d; +} + +int stb_vorbis_get_error(stb_vorbis *f) +{ + int e = f->error; + f->error = VORBIS__no_error; + return e; +} + +static stb_vorbis * vorbis_alloc(stb_vorbis *f) +{ + stb_vorbis *p = (stb_vorbis *) setup_malloc(f, sizeof(*p)); + return p; +} + +#ifndef STB_VORBIS_NO_PUSHDATA_API + +void stb_vorbis_flush_pushdata(stb_vorbis *f) +{ + f->previous_length = 0; + f->page_crc_tests = 0; + f->discard_samples_deferred = 0; + f->current_loc_valid = FALSE; + f->first_decode = FALSE; + f->samples_output = 0; + f->channel_buffer_start = 0; + f->channel_buffer_end = 0; +} + +static int vorbis_search_for_page_pushdata(vorb *f, uint8 *data, int data_len) +{ + int i,n; + for (i=0; i < f->page_crc_tests; ++i) + f->scan[i].bytes_done = 0; + + // if we have room for more scans, search for them first, because + // they may cause us to stop early if their header is incomplete + if (f->page_crc_tests < STB_VORBIS_PUSHDATA_CRC_COUNT) { + if (data_len < 4) return 0; + data_len -= 3; // need to look for 4-byte sequence, so don't miss + // one that straddles a boundary + for (i=0; i < data_len; ++i) { + if (data[i] == 0x4f) { + if (0==memcmp(data+i, ogg_page_header, 4)) { + int j,len; + uint32 crc; + // make sure we have the whole page header + if (i+26 >= data_len || i+27+data[i+26] >= data_len) { + // only read up to this page start, so hopefully we'll + // have the whole page header start next time + data_len = i; + break; + } + // ok, we have it all; compute the length of the page + len = 27 + data[i+26]; + for (j=0; j < data[i+26]; ++j) + len += data[i+27+j]; + // scan everything up to the embedded crc (which we must 0) + crc = 0; + for (j=0; j < 22; ++j) + crc = crc32_update(crc, data[i+j]); + // now process 4 0-bytes + for ( ; j < 26; ++j) + crc = crc32_update(crc, 0); + // len is the total number of bytes we need to scan + n = f->page_crc_tests++; + f->scan[n].bytes_left = len-j; + f->scan[n].crc_so_far = crc; + f->scan[n].goal_crc = data[i+22] + (data[i+23] << 8) + (data[i+24]<<16) + (data[i+25]<<24); + // if the last frame on a page is continued to the next, then + // we can't recover the sample_loc immediately + if (data[i+27+data[i+26]-1] == 255) + f->scan[n].sample_loc = ~0; + else + f->scan[n].sample_loc = data[i+6] + (data[i+7] << 8) + (data[i+ 8]<<16) + (data[i+ 9]<<24); + f->scan[n].bytes_done = i+j; + if (f->page_crc_tests == STB_VORBIS_PUSHDATA_CRC_COUNT) + break; + // keep going if we still have room for more + } + } + } + } + + for (i=0; i < f->page_crc_tests;) { + uint32 crc; + int j; + int n = f->scan[i].bytes_done; + int m = f->scan[i].bytes_left; + if (m > data_len - n) m = data_len - n; + // m is the bytes to scan in the current chunk + crc = f->scan[i].crc_so_far; + for (j=0; j < m; ++j) + crc = crc32_update(crc, data[n+j]); + f->scan[i].bytes_left -= m; + f->scan[i].crc_so_far = crc; + if (f->scan[i].bytes_left == 0) { + // does it match? + if (f->scan[i].crc_so_far == f->scan[i].goal_crc) { + // Houston, we have page + data_len = n+m; // consumption amount is wherever that scan ended + f->page_crc_tests = -1; // drop out of page scan mode + f->previous_length = 0; // decode-but-don't-output one frame + f->next_seg = -1; // start a new page + f->current_loc = f->scan[i].sample_loc; // set the current sample location + // to the amount we'd have decoded had we decoded this page + f->current_loc_valid = f->current_loc != ~0; + return data_len; + } + // delete entry + f->scan[i] = f->scan[--f->page_crc_tests]; + } else { + ++i; + } + } + + return data_len; +} + +// return value: number of bytes we used +int stb_vorbis_decode_frame_pushdata( + stb_vorbis *f, // the file we're decoding + uint8 *data, int data_len, // the memory available for decoding + int *channels, // place to write number of float * buffers + float ***output, // place to write float ** array of float * buffers + int *samples // place to write number of output samples + ) +{ + int i; + int len,right,left; + + if (!IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + + if (f->page_crc_tests >= 0) { + *samples = 0; + return vorbis_search_for_page_pushdata(f, data, data_len); + } + + f->stream = data; + f->stream_end = data + data_len; + f->error = VORBIS__no_error; + + // check that we have the entire packet in memory + if (!is_whole_packet_present(f, FALSE)) { + *samples = 0; + return 0; + } + + if (!vorbis_decode_packet(f, &len, &left, &right)) { + // save the actual error we encountered + enum STBVorbisError error = f->error; + if (error == VORBIS_bad_packet_type) { + // flush and resynch + f->error = VORBIS__no_error; + while (get8_packet(f) != EOP) + if (f->eof) break; + *samples = 0; + return f->stream - data; + } + if (error == VORBIS_continued_packet_flag_invalid) { + if (f->previous_length == 0) { + // we may be resynching, in which case it's ok to hit one + // of these; just discard the packet + f->error = VORBIS__no_error; + while (get8_packet(f) != EOP) + if (f->eof) break; + *samples = 0; + return f->stream - data; + } + } + // if we get an error while parsing, what to do? + // well, it DEFINITELY won't work to continue from where we are! + stb_vorbis_flush_pushdata(f); + // restore the error that actually made us bail + f->error = error; + *samples = 0; + return 1; + } + + // success! + len = vorbis_finish_frame(f, len, left, right); + for (i=0; i < f->channels; ++i) + f->outputs[i] = f->channel_buffers[i] + left; + + if (channels) *channels = f->channels; + *samples = len; + *output = f->outputs; + return f->stream - data; +} + +stb_vorbis *stb_vorbis_open_pushdata( + unsigned char *data, int data_len, // the memory available for decoding + int *data_used, // only defined if result is not NULL + int *error, stb_vorbis_alloc *alloc) +{ + stb_vorbis *f, p; + vorbis_init(&p, alloc); + p.stream = data; + p.stream_end = data + data_len; + p.push_mode = TRUE; + if (!start_decoder(&p)) { + if (p.eof) + *error = VORBIS_need_more_data; + else + *error = p.error; + return NULL; + } + f = vorbis_alloc(&p); + if (f) { + *f = p; + *data_used = f->stream - data; + *error = 0; + return f; + } else { + vorbis_deinit(&p); + return NULL; + } +} +#endif // STB_VORBIS_NO_PUSHDATA_API + +unsigned int stb_vorbis_get_file_offset(stb_vorbis *f) +{ + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (f->push_mode) return 0; + #endif + if (USE_MEMORY(f)) return f->stream - f->stream_start; + #ifndef STB_VORBIS_NO_STDIO + return ftell(f->f) - f->f_start; + #endif +} + +#ifndef STB_VORBIS_NO_PULLDATA_API +// +// DATA-PULLING API +// + +static uint32 vorbis_find_page(stb_vorbis *f, uint32 *end, uint32 *last) +{ + for(;;) { + int n; + if (f->eof) return 0; + n = get8(f); + if (n == 0x4f) { // page header + unsigned int retry_loc = stb_vorbis_get_file_offset(f); + int i; + // check if we're off the end of a file_section stream + if (retry_loc - 25 > f->stream_len) + return 0; + // check the rest of the header + for (i=1; i < 4; ++i) + if (get8(f) != ogg_page_header[i]) + break; + if (f->eof) return 0; + if (i == 4) { + uint8 header[27]; + uint32 i, crc, goal, len; + for (i=0; i < 4; ++i) + header[i] = ogg_page_header[i]; + for (; i < 27; ++i) + header[i] = get8(f); + if (f->eof) return 0; + if (header[4] != 0) goto invalid; + goal = header[22] + (header[23] << 8) + (header[24]<<16) + (header[25]<<24); + for (i=22; i < 26; ++i) + header[i] = 0; + crc = 0; + for (i=0; i < 27; ++i) + crc = crc32_update(crc, header[i]); + len = 0; + for (i=0; i < header[26]; ++i) { + int s = get8(f); + crc = crc32_update(crc, s); + len += s; + } + if (len && f->eof) return 0; + for (i=0; i < len; ++i) + crc = crc32_update(crc, get8(f)); + // finished parsing probable page + if (crc == goal) { + // we could now check that it's either got the last + // page flag set, OR it's followed by the capture + // pattern, but I guess TECHNICALLY you could have + // a file with garbage between each ogg page and recover + // from it automatically? So even though that paranoia + // might decrease the chance of an invalid decode by + // another 2^32, not worth it since it would hose those + // invalid-but-useful files? + if (end) + *end = stb_vorbis_get_file_offset(f); + if (last) + if (header[5] & 0x04) + *last = 1; + else + *last = 0; + set_file_offset(f, retry_loc-1); + return 1; + } + } + invalid: + // not a valid page, so rewind and look for next one + set_file_offset(f, retry_loc); + } + } +} + +// seek is implemented with 'interpolation search'--this is like +// binary search, but we use the data values to estimate the likely +// location of the data item (plus a bit of a bias so when the +// estimation is wrong we don't waste overly much time) + +#define SAMPLE_unknown 0xffffffff + + +// ogg vorbis, in its insane infinite wisdom, only provides +// information about the sample at the END of the page. +// therefore we COULD have the data we need in the current +// page, and not know it. we could just use the end location +// as our only knowledge for bounds, seek back, and eventually +// the binary search finds it. or we can try to be smart and +// not waste time trying to locate more pages. we try to be +// smart, since this data is already in memory anyway, so +// doing needless I/O would be crazy! +static int vorbis_analyze_page(stb_vorbis *f, ProbedPage *z) +{ + uint8 header[27], lacing[255]; + uint8 packet_type[255]; + int num_packet, packet_start, previous =0; + int i,len; + uint32 samples; + + // record where the page starts + z->page_start = stb_vorbis_get_file_offset(f); + + // parse the header + getn(f, header, 27); + assert(header[0] == 'O' && header[1] == 'g' && header[2] == 'g' && header[3] == 'S'); + getn(f, lacing, header[26]); + + // determine the length of the payload + len = 0; + for (i=0; i < header[26]; ++i) + len += lacing[i]; + + // this implies where the page ends + z->page_end = z->page_start + 27 + header[26] + len; + + // read the last-decoded sample out of the data + z->last_decoded_sample = header[6] + (header[7] << 8) + (header[8] << 16) + (header[9] << 16); + + if (header[5] & 4) { + // if this is the last page, it's not possible to work + // backwards to figure out the first sample! whoops! fuck. + z->first_decoded_sample = SAMPLE_unknown; + set_file_offset(f, z->page_start); + return 1; + } + + // scan through the frames to determine the sample-count of each one... + // our goal is the sample # of the first fully-decoded sample on the + // page, which is the first decoded sample of the 2nd page + + num_packet=0; + + packet_start = ((header[5] & 1) == 0); + + for (i=0; i < header[26]; ++i) { + if (packet_start) { + uint8 n,b,m; + if (lacing[i] == 0) goto bail; // trying to read from zero-length packet + n = get8(f); + // if bottom bit is non-zero, we've got corruption + if (n & 1) goto bail; + n >>= 1; + b = ilog(f->mode_count-1); + m = n >> b; + n &= (1 << b)-1; + if (n >= f->mode_count) goto bail; + if (num_packet == 0 && f->mode_config[n].blockflag) + previous = (m & 1); + packet_type[num_packet++] = f->mode_config[n].blockflag; + skip(f, lacing[i]-1); + } else + skip(f, lacing[i]); + packet_start = (lacing[i] < 255); + } + + // now that we know the sizes of all the pages, we can start determining + // how much sample data there is. + + samples = 0; + + // for the last packet, we step by its whole length, because the definition + // is that we encoded the end sample loc of the 'last packet completed', + // where 'completed' refers to packets being split, and we are left to guess + // what 'end sample loc' means. we assume it means ignoring the fact that + // the last half of the data is useless without windowing against the next + // packet... (so it's not REALLY complete in that sense) + if (num_packet > 1) + samples += f->blocksize[packet_type[num_packet-1]]; + + for (i=num_packet-2; i >= 1; --i) { + // now, for this packet, how many samples do we have that + // do not overlap the following packet? + if (packet_type[i] == 1) + if (packet_type[i+1] == 1) + samples += f->blocksize_1 >> 1; + else + samples += ((f->blocksize_1 - f->blocksize_0) >> 2) + (f->blocksize_0 >> 1); + else + samples += f->blocksize_0 >> 1; + } + // now, at this point, we've rewound to the very beginning of the + // _second_ packet. if we entirely discard the first packet after + // a seek, this will be exactly the right sample number. HOWEVER! + // we can't as easily compute this number for the LAST page. The + // only way to get the sample offset of the LAST page is to use + // the end loc from the previous page. But what that returns us + // is _exactly_ the place where we get our first non-overlapped + // sample. (I think. Stupid spec for being ambiguous.) So for + // consistency it's better to do that here, too. However, that + // will then require us to NOT discard all of the first frame we + // decode, in some cases, which means an even weirder frame size + // and extra code. what a fucking pain. + + // we're going to discard the first packet if we + // start the seek here, so we don't care about it. (we could actually + // do better; if the first packet is long, and the previous packet + // is short, there's actually data in the first half of the first + // packet that doesn't need discarding... but not worth paying the + // effort of tracking that of that here and in the seeking logic) + // except crap, if we infer it from the _previous_ packet's end + // location, we DO need to use that definition... and we HAVE to + // infer the start loc of the LAST packet from the previous packet's + // end location. fuck you, ogg vorbis. + + z->first_decoded_sample = z->last_decoded_sample - samples; + + // restore file state to where we were + set_file_offset(f, z->page_start); + return 1; + + // restore file state to where we were + bail: + set_file_offset(f, z->page_start); + return 0; +} + +static int vorbis_seek_frame_from_page(stb_vorbis *f, uint32 page_start, uint32 first_sample, uint32 target_sample, int fine) +{ + int left_start, left_end, right_start, right_end, mode,i; + int frame=0; + uint32 frame_start; + int frames_to_skip, data_to_skip; + + // first_sample is the sample # of the first sample that doesn't + // overlap the previous page... note that this requires us to + // _partially_ discard the first packet! bleh. + set_file_offset(f, page_start); + + f->next_seg = -1; // force page resync + + frame_start = first_sample; + // frame start is where the previous packet's last decoded sample + // was, which corresponds to left_end... EXCEPT if the previous + // packet was long and this packet is short? Probably a bug here. + + + // now, we can start decoding frames... we'll only FAKE decode them, + // until we find the frame that contains our sample; then we'll rewind, + // and try again + for (;;) { + int start; + + if (!vorbis_decode_initial(f, &left_start, &left_end, &right_start, &right_end, &mode)) + return error(f, VORBIS_seek_failed); + + if (frame == 0) + start = left_end; + else + start = left_start; + + // the window starts at left_start; the last valid sample we generate + // before the next frame's window start is right_start-1 + if (target_sample < frame_start + right_start-start) + break; + + flush_packet(f); + if (f->eof) + return error(f, VORBIS_seek_failed); + + frame_start += right_start - start; + + ++frame; + } + + // ok, at this point, the sample we want is contained in frame #'frame' + + // to decode frame #'frame' normally, we have to decode the + // previous frame first... but if it's the FIRST frame of the page + // we can't. if it's the first frame, it means it falls in the part + // of the first frame that doesn't overlap either of the other frames. + // so, if we have to handle that case for the first frame, we might + // as well handle it for all of them, so: + if (target_sample > frame_start + (left_end - left_start)) { + // so what we want to do is go ahead and just immediately decode + // this frame, but then make it so the next get_frame_float() uses + // this already-decoded data? or do we want to go ahead and rewind, + // and leave a flag saying to skip the first N data? let's do that + frames_to_skip = frame; // if this is frame #1, skip 1 frame (#0) + data_to_skip = left_end - left_start; + } else { + // otherwise, we want to skip frames 0, 1, 2, ... frame-2 + // (which means frame-2+1 total frames) then decode frame-1, + // then leave frame pending + frames_to_skip = frame - 1; + assert(frames_to_skip >= 0); + data_to_skip = -1; + } + + set_file_offset(f, page_start); + f->next_seg = - 1; // force page resync + + for (i=0; i < frames_to_skip; ++i) { + maybe_start_packet(f); + flush_packet(f); + } + + if (data_to_skip >= 0) { + int i,j,n = f->blocksize_0 >> 1; + f->discard_samples_deferred = data_to_skip; + for (i=0; i < f->channels; ++i) + for (j=0; j < n; ++j) + f->previous_window[i][j] = 0; + f->previous_length = n; + frame_start += data_to_skip; + } else { + f->previous_length = 0; + vorbis_pump_first_frame(f); + } + + // at this point, the NEXT decoded frame will generate the desired sample + if (fine) { + // so if we're doing sample accurate streaming, we want to go ahead and decode it! + if (target_sample != frame_start) { + int n; + stb_vorbis_get_frame_float(f, &n, NULL); + assert(target_sample > frame_start); + assert(f->channel_buffer_start + (int) (target_sample-frame_start) < f->channel_buffer_end); + f->channel_buffer_start += (target_sample - frame_start); + } + } + + return 0; +} + +static int vorbis_seek_base(stb_vorbis *f, unsigned int sample_number, int fine) +{ + ProbedPage p[2],q; + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + + // do we know the location of the last page? + if (f->p_last.page_start == 0) { + uint32 z = stb_vorbis_stream_length_in_samples(f); + if (z == 0) return error(f, VORBIS_cant_find_last_page); + } + + p[0] = f->p_first; + p[1] = f->p_last; + + if (sample_number >= f->p_last.last_decoded_sample) + sample_number = f->p_last.last_decoded_sample-1; + + if (sample_number < f->p_first.last_decoded_sample) { + vorbis_seek_frame_from_page(f, p[0].page_start, 0, sample_number, fine); + return 0; + } else { + int attempts=0; + while (p[0].page_end < p[1].page_start) { + uint32 probe; + uint32 start_offset, end_offset; + uint32 start_sample, end_sample; + + // copy these into local variables so we can tweak them + // if any are unknown + start_offset = p[0].page_end; + end_offset = p[1].after_previous_page_start; // an address known to seek to page p[1] + start_sample = p[0].last_decoded_sample; + end_sample = p[1].last_decoded_sample; + + // currently there is no such tweaking logic needed/possible? + if (start_sample == SAMPLE_unknown || end_sample == SAMPLE_unknown) + return error(f, VORBIS_seek_failed); + + // now we want to lerp between these for the target samples... + + // step 1: we need to bias towards the page start... + if (start_offset + 4000 < end_offset) + end_offset -= 4000; + + // now compute an interpolated search loc + probe = start_offset + (int) floor((float) (end_offset - start_offset) / (end_sample - start_sample) * (sample_number - start_sample)); + + // next we need to bias towards binary search... + // code is a little wonky to allow for full 32-bit unsigned values + if (attempts >= 4) { + uint32 probe2 = start_offset + ((end_offset - start_offset) >> 1); + if (attempts >= 8) + probe = probe2; + else if (probe < probe2) + probe = probe + ((probe2 - probe) >> 1); + else + probe = probe2 + ((probe - probe2) >> 1); + } + ++attempts; + + set_file_offset(f, probe); + if (!vorbis_find_page(f, NULL, NULL)) return error(f, VORBIS_seek_failed); + if (!vorbis_analyze_page(f, &q)) return error(f, VORBIS_seek_failed); + q.after_previous_page_start = probe; + + // it's possible we've just found the last page again + if (q.page_start == p[1].page_start) { + p[1] = q; + continue; + } + + if (sample_number < q.last_decoded_sample) + p[1] = q; + else + p[0] = q; + } + + if (p[0].last_decoded_sample <= sample_number && sample_number < p[1].last_decoded_sample) { + vorbis_seek_frame_from_page(f, p[1].page_start, p[0].last_decoded_sample, sample_number, fine); + return 0; + } + return error(f, VORBIS_seek_failed); + } +} + +int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number) +{ + return vorbis_seek_base(f, sample_number, FALSE); +} + +int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number) +{ + return vorbis_seek_base(f, sample_number, TRUE); +} + +void stb_vorbis_seek_start(stb_vorbis *f) +{ + if (IS_PUSH_MODE(f)) { error(f, VORBIS_invalid_api_mixing); return; } + set_file_offset(f, f->first_audio_page_offset); + f->previous_length = 0; + f->first_decode = TRUE; + f->next_seg = -1; + vorbis_pump_first_frame(f); +} + +unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f) +{ + unsigned int restore_offset, previous_safe; + unsigned int end, last_page_loc; + + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + if (!f->total_samples) { + int last; + uint32 lo,hi; + char header[6]; + + // first, store the current decode position so we can restore it + restore_offset = stb_vorbis_get_file_offset(f); + + // now we want to seek back 64K from the end (the last page must + // be at most a little less than 64K, but let's allow a little slop) + if (f->stream_len >= 65536 && f->stream_len-65536 >= f->first_audio_page_offset) + previous_safe = f->stream_len - 65536; + else + previous_safe = f->first_audio_page_offset; + + set_file_offset(f, previous_safe); + // previous_safe is now our candidate 'earliest known place that seeking + // to will lead to the final page' + + if (!vorbis_find_page(f, &end, (int unsigned *)&last)) { + // if we can't find a page, we're hosed! + f->error = VORBIS_cant_find_last_page; + f->total_samples = 0xffffffff; + goto done; + } + + // check if there are more pages + last_page_loc = stb_vorbis_get_file_offset(f); + + // stop when the last_page flag is set, not when we reach eof; + // this allows us to stop short of a 'file_section' end without + // explicitly checking the length of the section + while (!last) { + set_file_offset(f, end); + if (!vorbis_find_page(f, &end, (int unsigned *)&last)) { + // the last page we found didn't have the 'last page' flag + // set. whoops! + break; + } + previous_safe = last_page_loc+1; + last_page_loc = stb_vorbis_get_file_offset(f); + } + + set_file_offset(f, last_page_loc); + + // parse the header + getn(f, (unsigned char *)header, 6); + // extract the absolute granule position + lo = get32(f); + hi = get32(f); + if (lo == 0xffffffff && hi == 0xffffffff) { + f->error = VORBIS_cant_find_last_page; + f->total_samples = SAMPLE_unknown; + goto done; + } + if (hi) + lo = 0xfffffffe; // saturate + f->total_samples = lo; + + f->p_last.page_start = last_page_loc; + f->p_last.page_end = end; + f->p_last.last_decoded_sample = lo; + f->p_last.first_decoded_sample = SAMPLE_unknown; + f->p_last.after_previous_page_start = previous_safe; + + done: + set_file_offset(f, restore_offset); + } + return f->total_samples == SAMPLE_unknown ? 0 : f->total_samples; +} + +float stb_vorbis_stream_length_in_seconds(stb_vorbis *f) +{ + return stb_vorbis_stream_length_in_samples(f) / (float) f->sample_rate; +} + + + +int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output) +{ + int len, right,left,i; + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + + if (!vorbis_decode_packet(f, &len, &left, &right)) { + f->channel_buffer_start = f->channel_buffer_end = 0; + return 0; + } + + len = vorbis_finish_frame(f, len, left, right); + for (i=0; i < f->channels; ++i) + f->outputs[i] = f->channel_buffers[i] + left; + + f->channel_buffer_start = left; + f->channel_buffer_end = left+len; + + if (channels) *channels = f->channels; + if (output) *output = f->outputs; + return len; +} + +#ifndef STB_VORBIS_NO_STDIO + +stb_vorbis * stb_vorbis_open_file_section(FILE *file, int close_on_free, int *error, stb_vorbis_alloc *alloc, unsigned int length) +{ + stb_vorbis *f, p; + vorbis_init(&p, alloc); + p.f = file; + p.f_start = ftell(file); + p.stream_len = length; + p.close_on_free = close_on_free; + if (start_decoder(&p)) { + f = vorbis_alloc(&p); + if (f) { + *f = p; + vorbis_pump_first_frame(f); + return f; + } + } + if (error) *error = p.error; + vorbis_deinit(&p); + return NULL; +} + +stb_vorbis * stb_vorbis_open_file(FILE *file, int close_on_free, int *error, stb_vorbis_alloc *alloc) +{ + unsigned int len, start; + start = ftell(file); + fseek(file, 0, SEEK_END); + len = ftell(file) - start; + fseek(file, start, SEEK_SET); + return stb_vorbis_open_file_section(file, close_on_free, error, alloc, len); +} + +stb_vorbis * stb_vorbis_open_filename(char *filename, int *error, stb_vorbis_alloc *alloc) +{ + FILE *f = fopen(filename, "rb"); + if (f) + return stb_vorbis_open_file(f, TRUE, error, alloc); + if (error) *error = VORBIS_file_open_failure; + return NULL; +} +#endif // STB_VORBIS_NO_STDIO + +stb_vorbis * stb_vorbis_open_memory(unsigned char *data, int len, int *error, stb_vorbis_alloc *alloc) +{ + stb_vorbis *f, p; + if (data == NULL) return NULL; + vorbis_init(&p, alloc); + p.stream = data; + p.stream_end = data + len; + p.stream_start = p.stream; + p.stream_len = len; + p.push_mode = FALSE; + if (start_decoder(&p)) { + f = vorbis_alloc(&p); + if (f) { + *f = p; + vorbis_pump_first_frame(f); + return f; + } + } + if (error) *error = p.error; + vorbis_deinit(&p); + return NULL; +} + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +#define PLAYBACK_MONO 1 +#define PLAYBACK_LEFT 2 +#define PLAYBACK_RIGHT 4 + +#define L (PLAYBACK_LEFT | PLAYBACK_MONO) +#define C (PLAYBACK_LEFT | PLAYBACK_RIGHT | PLAYBACK_MONO) +#define R (PLAYBACK_RIGHT | PLAYBACK_MONO) + +static int8 channel_position[7][6] = +{ + { 0 }, + { C }, + { L, R }, + { L, C, R }, + { L, R, L, R }, + { L, C, R, L, R }, + { L, C, R, L, R, C }, +}; + + +#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT + typedef union { + float f; + int i; + } float_conv; + typedef char stb_vorbis_float_size_test[sizeof(float)==4 && sizeof(int) == 4]; + #define FASTDEF(x) float_conv x + // add (1<<23) to convert to int, then divide by 2^SHIFT, then add 0.5/2^SHIFT to round + #define MAGIC(SHIFT) (1.5f * (1 << (23-SHIFT)) + 0.5f/(1 << SHIFT)) + #define ADDEND(SHIFT) (((150-SHIFT) << 23) + (1 << 22)) + #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) (temp.f = (x) + MAGIC(s), temp.i - ADDEND(s)) + #define check_endianness() +#else + #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) ((int) ((x) * (1 << (s)))) + #define check_endianness() + #define FASTDEF(x) +#endif + +static void copy_samples(short *dest, float *src, int len) +{ + int i; + check_endianness(); + for (i=0; i < len; ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp, src[i],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + dest[i] = v; + } +} + +static void compute_samples(int mask, short *output, int num_c, float **data, int d_offset, int len) +{ + #define BUFFER_SIZE 32 + float buffer[BUFFER_SIZE]; + int i,j,o,n = BUFFER_SIZE; + check_endianness(); + for (o = 0; o < len; o += BUFFER_SIZE) { + memset(buffer, 0, sizeof(buffer)); + if (o + n > len) n = len - o; + for (j=0; j < num_c; ++j) { + if (channel_position[num_c][j] & mask) { + for (i=0; i < n; ++i) + buffer[i] += data[j][d_offset+o+i]; + } + } + for (i=0; i < n; ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + output[o+i] = v; + } + } +} + +static int channel_selector[3][2] = { {0}, {PLAYBACK_MONO}, {PLAYBACK_LEFT, PLAYBACK_RIGHT} }; +static void compute_stereo_samples(short *output, int num_c, float **data, int d_offset, int len) +{ + #define BUFFER_SIZE 32 + float buffer[BUFFER_SIZE]; + int i,j,o,n = BUFFER_SIZE >> 1; + // o is the offset in the source data + check_endianness(); + for (o = 0; o < len; o += BUFFER_SIZE >> 1) { + // o2 is the offset in the output data + int o2 = o << 1; + memset(buffer, 0, sizeof(buffer)); + if (o + n > len) n = len - o; + for (j=0; j < num_c; ++j) { + int m = channel_position[num_c][j] & (PLAYBACK_LEFT | PLAYBACK_RIGHT); + if (m == (PLAYBACK_LEFT | PLAYBACK_RIGHT)) { + for (i=0; i < n; ++i) { + buffer[i*2+0] += data[j][d_offset+o+i]; + buffer[i*2+1] += data[j][d_offset+o+i]; + } + } else if (m == PLAYBACK_LEFT) { + for (i=0; i < n; ++i) { + buffer[i*2+0] += data[j][d_offset+o+i]; + } + } else if (m == PLAYBACK_RIGHT) { + for (i=0; i < n; ++i) { + buffer[i*2+1] += data[j][d_offset+o+i]; + } + } + } + for (i=0; i < (n<<1); ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + output[o2+i] = v; + } + } +} + +static void convert_samples_short(int buf_c, short **buffer, int b_offset, int data_c, float **data, int d_offset, int samples) +{ + int i; + if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { + static int channel_selector[3][2] = { {0}, {PLAYBACK_MONO}, {PLAYBACK_LEFT, PLAYBACK_RIGHT} }; + for (i=0; i < buf_c; ++i) + compute_samples(channel_selector[buf_c][i], buffer[i]+b_offset, data_c, data, d_offset, samples); + } else { + int limit = buf_c < data_c ? buf_c : data_c; + for (i=0; i < limit; ++i) + copy_samples(buffer[i]+b_offset, data[i], samples); + for ( ; i < buf_c; ++i) + memset(buffer[i]+b_offset, 0, sizeof(short) * samples); + } +} + +int stb_vorbis_get_frame_short(stb_vorbis *f, int num_c, short **buffer, int num_samples) +{ + float **output; + int len = stb_vorbis_get_frame_float(f, NULL, &output); + if (len > num_samples) len = num_samples; + if (len) + convert_samples_short(num_c, buffer, 0, f->channels, output, 0, len); + return len; +} + +static void convert_channels_short_interleaved(int buf_c, short *buffer, int data_c, float **data, int d_offset, int len) +{ + int i; + check_endianness(); + if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { + assert(buf_c == 2); + for (i=0; i < buf_c; ++i) + compute_stereo_samples(buffer, data_c, data, d_offset, len); + } else { + int limit = buf_c < data_c ? buf_c : data_c; + int j; + for (j=0; j < len; ++j) { + for (i=0; i < limit; ++i) { + FASTDEF(temp); + float f = data[i][d_offset+j]; + int v = FAST_SCALED_FLOAT_TO_INT(temp, f,15);//data[i][d_offset+j],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + *buffer++ = v; + } + for ( ; i < buf_c; ++i) + *buffer++ = 0; + } + } +} + +int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts) +{ + float **output; + int len; + if (num_c == 1) return stb_vorbis_get_frame_short(f,num_c,&buffer, num_shorts); + len = stb_vorbis_get_frame_float(f, NULL, &output); + if (len) { + if (len*num_c > num_shorts) len = num_shorts / num_c; + convert_channels_short_interleaved(num_c, buffer, f->channels, output, 0, len); + } + return len; +} + +int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts) +{ + float **outputs; + int len = num_shorts / channels; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < len) { + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + if (k) + convert_channels_short_interleaved(channels, buffer, f->channels, f->channel_buffers, f->channel_buffer_start, k); + buffer += k*channels; + n += k; + f->channel_buffer_start += k; + if (n == len) break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; + } + return n; +} + +int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int len) +{ + float **outputs; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < len) { + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + if (k) + convert_samples_short(channels, buffer, n, f->channels, f->channel_buffers, f->channel_buffer_start, k); + n += k; + f->channel_buffer_start += k; + if (n == len) break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; + } + return n; +} + +#ifndef STB_VORBIS_NO_STDIO +int stb_vorbis_decode_filename(char *filename, int *channels, short **output) +{ + int data_len, offset, total, limit, error; + short *data; + stb_vorbis *v = stb_vorbis_open_filename(filename, &error, NULL); + if (v == NULL) return -1; + limit = v->channels * 4096; + *channels = v->channels; + offset = data_len = 0; + total = limit; + data = (short *) malloc(total * sizeof(*data)); + if (data == NULL) { + stb_vorbis_close(v); + return -2; + } + for (;;) { + int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset); + if (n == 0) break; + data_len += n; + offset += n * v->channels; + if (offset + limit > total) { + short *data2; + total *= 2; + data2 = (short *) realloc(data, total * sizeof(*data)); + if (data2 == NULL) { + free(data); + stb_vorbis_close(v); + return -2; + } + data = data2; + } + } + *output = data; + return data_len; +} +#endif // NO_STDIO + +int stb_vorbis_decode_memory(uint8 *mem, int len, int *channels, short **output) +{ + int data_len, offset, total, limit, error; + short *data; + stb_vorbis *v = stb_vorbis_open_memory(mem, len, &error, NULL); + if (v == NULL) return -1; + limit = v->channels * 4096; + *channels = v->channels; + offset = data_len = 0; + total = limit; + data = (short *) malloc(total * sizeof(*data)); + if (data == NULL) { + stb_vorbis_close(v); + return -2; + } + for (;;) { + int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset); + if (n == 0) break; + data_len += n; + offset += n * v->channels; + if (offset + limit > total) { + short *data2; + total *= 2; + data2 = (short *) realloc(data, total * sizeof(*data)); + if (data2 == NULL) { + free(data); + stb_vorbis_close(v); + return -2; + } + data = data2; + } + } + *output = data; + return data_len; +} +#endif + +int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats) +{ + float **outputs; + int len = num_floats / channels; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < len) { + int i,j; + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + for (j=0; j < k; ++j) { + for (i=0; i < z; ++i) + *buffer++ = f->channel_buffers[i][f->channel_buffer_start+j]; + for ( ; i < channels; ++i) + *buffer++ = 0; + } + n += k; + f->channel_buffer_start += k; + if (n == len) break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; + } + return n; +} + +int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples) +{ + float **outputs; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < num_samples) { + int i; + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= num_samples) k = num_samples - n; + if (k) { + for (i=0; i < z; ++i) + memcpy(buffer[i]+n, f->channel_buffers+f->channel_buffer_start, sizeof(float)*k); + for ( ; i < channels; ++i) + memset(buffer[i]+n, 0, sizeof(float) * k); + } + n += k; + f->channel_buffer_start += k; + if (n == num_samples) break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; + } + return n; +} +#endif // STB_VORBIS_NO_PULLDATA_API + +#endif // STB_VORBIS_HEADER_ONLY diff --git a/src/stb_vorbis.h b/src/stb_vorbis.h new file mode 100644 index 000000000..98c1209ac --- /dev/null +++ b/src/stb_vorbis.h @@ -0,0 +1,352 @@ +// Ogg Vorbis I audio decoder -- version 0.99996 +// +// Written in April 2007 by Sean Barrett, sponsored by RAD Game Tools. +// +// Placed in the public domain April 2007 by the author: no copyright is +// claimed, and you may use it for any purpose you like. +// +// No warranty for any purpose is expressed or implied by the author (nor +// by RAD Game Tools). Report bugs and send enhancements to the author. +// +// Get the latest version and other information at: +// http://nothings.org/stb_vorbis/ + +// Todo: +// +// - seeking (note you can seek yourself using the pushdata API) +// +// Limitations: +// +// - floor 0 not supported (used in old ogg vorbis files) +// - lossless sample-truncation at beginning ignored +// - cannot concatenate multiple vorbis streams +// - sample positions are 32-bit, limiting seekable 192Khz +// files to around 6 hours (Ogg supports 64-bit) +// +// All of these limitations may be removed in future versions. + + +////////////////////////////////////////////////////////////////////////////// +// +// HEADER BEGINS HERE +// + +#ifndef STB_VORBIS_INCLUDE_STB_VORBIS_H +#define STB_VORBIS_INCLUDE_STB_VORBIS_H + +#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) +#define STB_VORBIS_NO_STDIO 1 +#endif + +#ifndef STB_VORBIS_NO_STDIO +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/////////// THREAD SAFETY + +// Individual stb_vorbis* handles are not thread-safe; you cannot decode from +// them from multiple threads at the same time. However, you can have multiple +// stb_vorbis* handles and decode from them independently in multiple thrads. + + +/////////// MEMORY ALLOCATION + +// normally stb_vorbis uses malloc() to allocate memory at startup, +// and alloca() to allocate temporary memory during a frame on the +// stack. (Memory consumption will depend on the amount of setup +// data in the file and how you set the compile flags for speed +// vs. size. In my test files the maximal-size usage is ~150KB.) +// +// You can modify the wrapper functions in the source (setup_malloc, +// setup_temp_malloc, temp_malloc) to change this behavior, or you +// can use a simpler allocation model: you pass in a buffer from +// which stb_vorbis will allocate _all_ its memory (including the +// temp memory). "open" may fail with a VORBIS_outofmem if you +// do not pass in enough data; there is no way to determine how +// much you do need except to succeed (at which point you can +// query get_info to find the exact amount required. yes I know +// this is lame). +// +// If you pass in a non-NULL buffer of the type below, allocation +// will occur from it as described above. Otherwise just pass NULL +// to use malloc()/alloca() + +typedef struct +{ + char *alloc_buffer; + int alloc_buffer_length_in_bytes; +} stb_vorbis_alloc; + + +/////////// FUNCTIONS USEABLE WITH ALL INPUT MODES + +typedef struct stb_vorbis stb_vorbis; + +typedef struct +{ + unsigned int sample_rate; + int channels; + + unsigned int setup_memory_required; + unsigned int setup_temp_memory_required; + unsigned int temp_memory_required; + + int max_frame_size; +} stb_vorbis_info; + +// get general information about the file +extern stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f); + +// get the last error detected (clears it, too) +extern int stb_vorbis_get_error(stb_vorbis *f); + +// close an ogg vorbis file and free all memory in use +extern void stb_vorbis_close(stb_vorbis *f); + +// this function returns the offset (in samples) from the beginning of the +// file that will be returned by the next decode, if it is known, or -1 +// otherwise. after a flush_pushdata() call, this may take a while before +// it becomes valid again. +// NOT WORKING YET after a seek with PULLDATA API +extern int stb_vorbis_get_sample_offset(stb_vorbis *f); + +// returns the current seek point within the file, or offset from the beginning +// of the memory buffer. In pushdata mode it returns 0. +extern unsigned int stb_vorbis_get_file_offset(stb_vorbis *f); + +/////////// PUSHDATA API + +#ifndef STB_VORBIS_NO_PUSHDATA_API + +// this API allows you to get blocks of data from any source and hand +// them to stb_vorbis. you have to buffer them; stb_vorbis will tell +// you how much it used, and you have to give it the rest next time; +// and stb_vorbis may not have enough data to work with and you will +// need to give it the same data again PLUS more. Note that the Vorbis +// specification does not bound the size of an individual frame. + +extern stb_vorbis *stb_vorbis_open_pushdata( + unsigned char *datablock, int datablock_length_in_bytes, + int *datablock_memory_consumed_in_bytes, + int *error, + stb_vorbis_alloc *alloc_buffer); +// create a vorbis decoder by passing in the initial data block containing +// the ogg&vorbis headers (you don't need to do parse them, just provide +// the first N bytes of the file--you're told if it's not enough, see below) +// on success, returns an stb_vorbis *, does not set error, returns the amount of +// data parsed/consumed on this call in *datablock_memory_consumed_in_bytes; +// on failure, returns NULL on error and sets *error, does not change *datablock_memory_consumed +// if returns NULL and *error is VORBIS_need_more_data, then the input block was +// incomplete and you need to pass in a larger block from the start of the file + +extern int stb_vorbis_decode_frame_pushdata( + stb_vorbis *f, unsigned char *datablock, int datablock_length_in_bytes, + int *channels, // place to write number of float * buffers + float ***output, // place to write float ** array of float * buffers + int *samples // place to write number of output samples + ); +// decode a frame of audio sample data if possible from the passed-in data block +// +// return value: number of bytes we used from datablock +// possible cases: +// 0 bytes used, 0 samples output (need more data) +// N bytes used, 0 samples output (resynching the stream, keep going) +// N bytes used, M samples output (one frame of data) +// note that after opening a file, you will ALWAYS get one N-bytes,0-sample +// frame, because Vorbis always "discards" the first frame. +// +// Note that on resynch, stb_vorbis will rarely consume all of the buffer, +// instead only datablock_length_in_bytes-3 or less. This is because it wants +// to avoid missing parts of a page header if they cross a datablock boundary, +// without writing state-machiney code to record a partial detection. +// +// The number of channels returned are stored in *channels (which can be +// NULL--it is always the same as the number of channels reported by +// get_info). *output will contain an array of float* buffers, one per +// channel. In other words, (*output)[0][0] contains the first sample from +// the first channel, and (*output)[1][0] contains the first sample from +// the second channel. + +extern void stb_vorbis_flush_pushdata(stb_vorbis *f); +// inform stb_vorbis that your next datablock will not be contiguous with +// previous ones (e.g. you've seeked in the data); future attempts to decode +// frames will cause stb_vorbis to resynchronize (as noted above), and +// once it sees a valid Ogg page (typically 4-8KB, as large as 64KB), it +// will begin decoding the _next_ frame. +// +// if you want to seek using pushdata, you need to seek in your file, then +// call stb_vorbis_flush_pushdata(), then start calling decoding, then once +// decoding is returning you data, call stb_vorbis_get_sample_offset, and +// if you don't like the result, seek your file again and repeat. +#endif + + +////////// PULLING INPUT API + +#ifndef STB_VORBIS_NO_PULLDATA_API +// This API assumes stb_vorbis is allowed to pull data from a source-- +// either a block of memory containing the _entire_ vorbis stream, or a +// FILE * that you or it create, or possibly some other reading mechanism +// if you go modify the source to replace the FILE * case with some kind +// of callback to your code. (But if you don't support seeking, you may +// just want to go ahead and use pushdata.) + +#if !defined(STB_VORBIS_NO_STDIO) && !defined(STB_VORBIS_NO_INTEGER_CONVERSION) +extern int stb_vorbis_decode_filename(char *filename, int *channels, short **output); +#endif +extern int stb_vorbis_decode_memory(unsigned char *mem, int len, int *channels, short **output); +// decode an entire file and output the data interleaved into a malloc()ed +// buffer stored in *output. The return value is the number of samples +// decoded, or -1 if the file could not be opened or was not an ogg vorbis file. +// When you're done with it, just free() the pointer returned in *output. + +extern stb_vorbis * stb_vorbis_open_memory(unsigned char *data, int len, + int *error, stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from an ogg vorbis stream in memory (note +// this must be the entire stream!). on failure, returns NULL and sets *error + +#ifndef STB_VORBIS_NO_STDIO +extern stb_vorbis * stb_vorbis_open_filename(char *filename, + int *error, stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from a filename via fopen(). on failure, +// returns NULL and sets *error (possibly to VORBIS_file_open_failure). + +extern stb_vorbis * stb_vorbis_open_file(FILE *f, int close_handle_on_close, + int *error, stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from an open FILE *, looking for a stream at +// the _current_ seek point (ftell). on failure, returns NULL and sets *error. +// note that stb_vorbis must "own" this stream; if you seek it in between +// calls to stb_vorbis, it will become confused. Morever, if you attempt to +// perform stb_vorbis_seek_*() operations on this file, it will assume it +// owns the _entire_ rest of the file after the start point. Use the next +// function, stb_vorbis_open_file_section(), to limit it. + +extern stb_vorbis * stb_vorbis_open_file_section(FILE *f, int close_handle_on_close, + int *error, stb_vorbis_alloc *alloc_buffer, unsigned int len); +// create an ogg vorbis decoder from an open FILE *, looking for a stream at +// the _current_ seek point (ftell); the stream will be of length 'len' bytes. +// on failure, returns NULL and sets *error. note that stb_vorbis must "own" +// this stream; if you seek it in between calls to stb_vorbis, it will become +// confused. +#endif + +extern int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number); +extern int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number); +// NOT WORKING YET +// these functions seek in the Vorbis file to (approximately) 'sample_number'. +// after calling seek_frame(), the next call to get_frame_*() will include +// the specified sample. after calling stb_vorbis_seek(), the next call to +// stb_vorbis_get_samples_* will start with the specified sample. If you +// do not need to seek to EXACTLY the target sample when using get_samples_*, +// you can also use seek_frame(). + +extern void stb_vorbis_seek_start(stb_vorbis *f); +// this function is equivalent to stb_vorbis_seek(f,0), but it +// actually works + +extern unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f); +extern float stb_vorbis_stream_length_in_seconds(stb_vorbis *f); +// these functions return the total length of the vorbis stream + +extern int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output); +// decode the next frame and return the number of samples. the number of +// channels returned are stored in *channels (which can be NULL--it is always +// the same as the number of channels reported by get_info). *output will +// contain an array of float* buffers, one per channel. These outputs will +// be overwritten on the next call to stb_vorbis_get_frame_*. +// +// You generally should not intermix calls to stb_vorbis_get_frame_*() +// and stb_vorbis_get_samples_*(), since the latter calls the former. + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +extern int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts); +extern int stb_vorbis_get_frame_short (stb_vorbis *f, int num_c, short **buffer, int num_samples); +#endif +// decode the next frame and return the number of samples per channel. the +// data is coerced to the number of channels you request according to the +// channel coercion rules (see below). You must pass in the size of your +// buffer(s) so that stb_vorbis will not overwrite the end of the buffer. +// The maximum buffer size needed can be gotten from get_info(); however, +// the Vorbis I specification implies an absolute maximum of 4096 samples +// per channel. Note that for interleaved data, you pass in the number of +// shorts (the size of your array), but the return value is the number of +// samples per channel, not the total number of samples. + +// Channel coercion rules: +// Let M be the number of channels requested, and N the number of channels present, +// and Cn be the nth channel; let stereo L be the sum of all L and center channels, +// and stereo R be the sum of all R and center channels (channel assignment from the +// vorbis spec). +// M N output +// 1 k sum(Ck) for all k +// 2 * stereo L, stereo R +// k l k > l, the first l channels, then 0s +// k l k <= l, the first k channels +// Note that this is not _good_ surround etc. mixing at all! It's just so +// you get something useful. + +extern int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats); +extern int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples); +// gets num_samples samples, not necessarily on a frame boundary--this requires +// buffering so you have to supply the buffers. DOES NOT APPLY THE COERCION RULES. +// Returns the number of samples stored per channel; it may be less than requested +// at the end of the file. If there are no more samples in the file, returns 0. + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +extern int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts); +extern int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int num_samples); +#endif +// gets num_samples samples, not necessarily on a frame boundary--this requires +// buffering so you have to supply the buffers. Applies the coercion rules above +// to produce 'channels' channels. Returns the number of samples stored per channel; +// it may be less than requested at the end of the file. If there are no more +// samples in the file, returns 0. + +#endif + +//////// ERROR CODES + +enum STBVorbisError +{ + VORBIS__no_error, + + VORBIS_need_more_data=1, // not a real error + + VORBIS_invalid_api_mixing, // can't mix API modes + VORBIS_outofmem, // not enough memory + VORBIS_feature_not_supported, // uses floor 0 + VORBIS_too_many_channels, // STB_VORBIS_MAX_CHANNELS is too small + VORBIS_file_open_failure, // fopen() failed + VORBIS_seek_without_length, // can't seek in unknown-length file + + VORBIS_unexpected_eof=10, // file is truncated? + VORBIS_seek_invalid, // seek past EOF + + // decoding errors (corrupt/invalid stream) -- you probably + // don't care about the exact details of these + + // vorbis errors: + VORBIS_invalid_setup=20, + VORBIS_invalid_stream, + + // ogg errors: + VORBIS_missing_capture_pattern=30, + VORBIS_invalid_stream_structure_version, + VORBIS_continued_packet_flag_invalid, + VORBIS_incorrect_stream_serial_number, + VORBIS_invalid_first_page, + VORBIS_bad_packet_type, + VORBIS_cant_find_last_page, + VORBIS_seek_failed, +}; + + +#ifdef __cplusplus +} +#endif + +#endif // STB_VORBIS_INCLUDE_STB_VORBIS_H diff --git a/src/text.c b/src/text.c new file mode 100644 index 000000000..e9a66d893 --- /dev/null +++ b/src/text.c @@ -0,0 +1,515 @@ +/********************************************************************************************* +* +* raylib.text +* +* Basic functions to load SpriteFonts and draw Text +* +* Uses external lib: +* stb_image - Multiple formats image loading (JPEG, PNG, BMP, TGA, PSD, GIF, HDR, PIC) +* +* Copyright (c) 2013 Ramon Santamaria (Ray San - raysan@raysanweb.com) +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#include "raylib.h" + +#include // OpenGL functions +#include // Declares malloc() and free() for memory management +#include // String management functions (just strlen() is used) +#include // Used for functions with variable number of parameters (FormatText()) +#include "stb_image.h" // Used to read image data (multiple formats support) + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +#define FIRST_CHAR 32 +#define MAX_FONTCHARS 128 + +#define BIT_CHECK(a,b) ((a) & (1<<(b))) + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +typedef unsigned char byte; + +// SpriteFont one Character (Glyph) data +struct Character { + int value; //char value = ' '; (int)value = 32; + int x; + int y; + int w; + int h; +}; + +//---------------------------------------------------------------------------------- +// Global variables +//---------------------------------------------------------------------------------- +static SpriteFont defaultFont; // Default font provided by raylib + // NOTE: defaultFont is loaded on InitWindow and disposed on CloseWindow [module: core] + +//---------------------------------------------------------------------------------- +// Module specific Functions Declaration +//---------------------------------------------------------------------------------- +static bool PixelIsMagenta(Color p); // Check if a pixel is magenta +static int ParseImageData(Color *imgDataPixel, int imgWidth, int imgHeight, Character **charSet); // Parse image pixel data to obtain character set measures +static int GetNextPOT(int num); // Calculate next power-of-two value for a given value + +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- +extern void LoadDefaultFont() +{ + defaultFont.numChars = 96; // We know our default font has 94 chars + defaultFont.texture.width = 128; // We know our default font texture is 128 pixels width + defaultFont.texture.height = 64; // We know our default font texture is 64 pixels height + + // Default font is directly defined here (data generated from a sprite font image) + // This way, we reconstruct SpriteFont without creating large global variables + // This data is automatically allocated to Stack and automatically deallocated at the end of this function + int defaultFontData[256] = { + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00200020, 0x0001b000, 0x00000000, 0x00000000, 0x8ef92520, 0x00020a00, 0x7dbe8000, 0x1f7df45f, + 0x4a2bf2a0, 0x0852091e, 0x41224000, 0x10041450, 0x2e292020, 0x08220812, 0x41222000, 0x10041450, 0x10f92020, 0x3efa084c, 0x7d22103c, 0x107df7de, + 0xe8a12020, 0x08220832, 0x05220800, 0x10450410, 0xa4a3f000, 0x08520832, 0x05220400, 0x10450410, 0xe2f92020, 0x0002085e, 0x7d3e0281, 0x107df41f, + 0x00200000, 0x8001b000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xc0000fbe, 0xfbf7e00f, 0x5fbf7e7d, 0x0050bee8, 0x440808a2, 0x0a142fe8, 0x50810285, 0x0050a048, + 0x49e428a2, 0x0a142828, 0x40810284, 0x0048a048, 0x10020fbe, 0x09f7ebaf, 0xd89f3e84, 0x0047a04f, 0x09e48822, 0x0a142aa1, 0x50810284, 0x0048a048, + 0x04082822, 0x0a142fa0, 0x50810285, 0x0050a248, 0x00008fbe, 0xfbf42021, 0x5f817e7d, 0x07d09ce8, 0x00008000, 0x00000fe0, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x000c0180, + 0xdfbf4282, 0x0bfbf7ef, 0x42850505, 0x004804bf, 0x50a142c6, 0x08401428, 0x42852505, 0x00a808a0, 0x50a146aa, 0x08401428, 0x42852505, 0x00081090, + 0x5fa14a92, 0x0843f7e8, 0x7e792505, 0x00082088, 0x40a15282, 0x08420128, 0x40852489, 0x00084084, 0x40a16282, 0x0842022a, 0x40852451, 0x00088082, + 0xc0bf4282, 0xf843f42f, 0x7e85fc21, 0x3e0900bf, 0x00000000, 0x00000004, 0x00000000, 0x000c0180, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x04000402, 0x41482000, 0x00000000, 0x00000800, + 0x04000404, 0x4100203c, 0x00000000, 0x00000800, 0xf7df7df0, 0x514bef85, 0xbefbefbe, 0x04513bef, 0x14414500, 0x494a2885, 0xa28a28aa, 0x04510820, + 0xf44145f0, 0x474a289d, 0xa28a28aa, 0x04510be0, 0x14414510, 0x494a2884, 0xa28a28aa, 0x02910a00, 0xf7df7df0, 0xd14a2f85, 0xbefbe8aa, 0x011f7be0, + 0x00000000, 0x00400804, 0x20080000, 0x00000000, 0x00000000, 0x00600f84, 0x20080000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xac000000, 0x00000f01, 0x00000000, 0x00000000, 0x24000000, 0x00000901, 0x00000000, 0x00000000, 0x24000000, 0x00000901, 0x00000000, 0x00000000, + 0x24fa28a2, 0x00000901, 0x00000000, 0x00000000, 0x2242252a, 0x00000952, 0x00000000, 0x00000000, 0x2422222a, 0x00000929, 0x00000000, 0x00000000, + 0x2412252a, 0x00000901, 0x00000000, 0x00000000, 0x24fbe8be, 0x00000901, 0x00000000, 0x00000000, 0xac020000, 0x00000f01, 0x00000000, 0x00000000, + 0x0003e000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; + + int charsHeight = 10; + int charsDivisor = 1; // Every char is separated from the consecutive by a 1 pixel divisor, horizontally and vertically + + int charsWidth[96] = { 3, 1, 4, 6, 5, 7, 6, 2, 3, 3, 5, 5, 2, 4, 1, 7, 5, 2, 5, 5, 5, 5, 5, 5, 5, 5, 1, 1, 3, 4, 3, 6, + 7, 6, 6, 6, 6, 6, 6, 6, 6, 3, 5, 6, 5, 7, 6, 6, 6, 6, 6, 6, 7, 6, 7, 7, 6, 6, 6, 2, 7, 2, 3, 5, + 2, 5, 5, 5, 5, 5, 4, 5, 5, 1, 2, 5, 2, 5, 5, 5, 5, 5, 5, 5, 4, 5, 5, 5, 5, 5, 5, 3, 1, 3, 4, 4 }; + + + // Reconstruct charSet using charsWidth[], charsHeight, charsDivisor, numChars + //------------------------------------------------------------------------------ + defaultFont.charSet = (Character *)malloc(defaultFont.numChars * sizeof(Character)); // Allocate space for our character data + // This memory should be freed at end! --> Done on CloseWindow() + int currentLine = 0; + int currentPosX = charsDivisor; + int testPosX = charsDivisor; + + for (int i = 0; i < defaultFont.numChars; i++) + { + defaultFont.charSet[i].value = FIRST_CHAR + i; + defaultFont.charSet[i].x = currentPosX; + defaultFont.charSet[i].y = charsDivisor + currentLine * (charsHeight + charsDivisor); + defaultFont.charSet[i].w = charsWidth[i]; + defaultFont.charSet[i].h = charsHeight; + + testPosX += (defaultFont.charSet[i].w + charsDivisor); + + if (testPosX >= defaultFont.texture.width) + { + currentLine++; + currentPosX = 2 * charsDivisor + charsWidth[i]; + testPosX = currentPosX; + + defaultFont.charSet[i].x = charsDivisor; + defaultFont.charSet[i].y = charsDivisor + currentLine * (charsHeight + charsDivisor); + } + else currentPosX = testPosX; + } + + // Re-construct image from defaultFontData and generate OpenGL texture + //---------------------------------------------------------------------- + Color *imgDataPixel = (Color *)malloc(defaultFont.texture.width * defaultFont.texture.height * sizeof(Color)); + + for (int i = 0; i < defaultFont.texture.width * defaultFont.texture.height; i++) imgDataPixel[i] = BLANK; // Initialize array + + int counter = 0; // Font data elements counter + + // Fill imgData with defaultFontData (convert from bit to pixel!) + for (int i = 0; i < defaultFont.texture.width * defaultFont.texture.height; i += 32) + { + for (int j = 31; j >= 0; j--) + { + if (BIT_CHECK(defaultFontData[counter], j)) imgDataPixel[i+j] = WHITE; + } + + counter++; + + if (counter > 256) counter = 0; // Security check... + } + + // Convert loaded data to OpenGL texture + //---------------------------------------- + GLuint id; + glGenTextures(1, &id); // Generate pointer to the texture + + glBindTexture(GL_TEXTURE_2D, id); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); // Set texture to clamp on x-axis + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); // Set texture to clamp on y-axis + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // Filter for pixel-perfect drawing, alternative: GL_LINEAR + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Filter for pixel-perfect drawing, alternative: GL_LINEAR + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, defaultFont.texture.width, defaultFont.texture.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imgDataPixel); + + // NOTE: Not using mipmappings (texture for 2D drawing) + // At this point we have the image converted to texture and uploaded to GPU + + free(imgDataPixel); // Now we can free loaded data from RAM memory + + defaultFont.texture.glId = id; +} + +extern void UnloadDefaultFont() +{ + glDeleteTextures(1, &defaultFont.texture.glId); + free(defaultFont.charSet); +} + +// Load a SpriteFont image into GPU memory +SpriteFont LoadSpriteFont(const char* fileName) +{ + SpriteFont spriteFont; + + // Use stb_image to load image data! + int imgWidth; + int imgHeight; + int imgBpp; + + byte *imgData = stbi_load(fileName, &imgWidth, &imgHeight, &imgBpp, 4); // Force loading to 4 components (RGBA) + + // Convert array to pixel array for working convenience + Color *imgDataPixel = (Color *)malloc(imgWidth * imgHeight * sizeof(Color)); + Color *imgDataPixelPOT = NULL; + + int pix = 0; + + for (int i = 0; i < (imgWidth * imgHeight * 4); i += 4) + { + imgDataPixel[pix].r = imgData[i]; + imgDataPixel[pix].g = imgData[i+1]; + imgDataPixel[pix].b = imgData[i+2]; + imgDataPixel[pix].a = imgData[i+3]; + pix++; + } + + stbi_image_free(imgData); + + // At this point we have a pixel array with all the data... + + // Process bitmap Font pixel data to get measures (Character array) + // spriteFont.charSet data is filled inside the function and memory is allocated! + int numChars = ParseImageData(imgDataPixel, imgWidth, imgHeight, &spriteFont.charSet); + + spriteFont.numChars = numChars; + + // Convert image font to POT image before conversion to texture + // Just add the required amount of pixels at the right and bottom sides of image... + int potWidth = GetNextPOT(imgWidth); + int potHeight = GetNextPOT(imgHeight); + + // Check if POT texture generation is required (if texture is not already POT) + if ((potWidth != imgWidth) || (potHeight != imgWidth)) + { + // Generate POT array from NPOT data + imgDataPixelPOT = (Color *)malloc(potWidth * potHeight * sizeof(Color)); + + for (int j = 0; j < potHeight; j++) + { + for (int i = 0; i < potWidth; i++) + { + if ((j < imgHeight) && (i < imgWidth)) imgDataPixelPOT[j*potWidth + i] = imgDataPixel[j*imgWidth + i]; + else imgDataPixelPOT[j*potWidth + i] = MAGENTA; + } + } + } + + free(imgDataPixel); + + // Convert loaded data to OpenGL texture + //---------------------------------------- + GLuint id; + glGenTextures(1, &id); // Generate pointer to the texture + + glBindTexture(GL_TEXTURE_2D, id); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // Filter for pixel-perfect drawing, alternative: GL_LINEAR + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Filter for pixel-perfect drawing, alternative: GL_LINEAR + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, potWidth, potHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, imgDataPixelPOT); + + // NOTE: Not using mipmappings (texture for 2D drawing) + // At this point we have the image converted to texture and uploaded to GPU + + free(imgDataPixelPOT); // Now we can free loaded data from RAM memory + + spriteFont.texture.glId = id; + spriteFont.texture.width = potWidth; + spriteFont.texture.height = potHeight; + + return spriteFont; +} + +// Unload SpriteFont from GPU memory +void UnloadSpriteFont(SpriteFont spriteFont) +{ + glDeleteTextures(1, &spriteFont.texture.glId); + free(spriteFont.charSet); +} + +// 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 +void DrawText(const char* text, int posX, int posY, int fontSize, int spacing, Color color) +{ + Vector2 position = { (float)posX, (float)posY }; + + DrawTextEx(defaultFont, text, position, fontSize, spacing, color); +} + +// Formatting of text with variables to 'embed' +const char *FormatText(const char *text, ...) +{ + int length = strlen(text); + char *buffer = malloc(length + 20); // We add 20 extra characters, should be enough... :P + + va_list args; + va_start(args, text); + vsprintf(buffer, text, args); // NOTE: We use vsprintf() defined in + va_end(args); + + //strcat(buffer, "\0"); // We add a end-of-string mark at the end (not needed) + + return buffer; +} + +// Draw text using SpriteFont +// NOTE: If font size is lower than base size, base size is used +void DrawTextEx(SpriteFont spriteFont, const char* text, Vector2 position, int fontSize, int spacing, Color tint) +{ + int length = strlen(text); + int positionX = (int)position.x; + float scaleFactor; + + Character c; + + if (fontSize <= spriteFont.charSet[0].h) scaleFactor = 1.0f; + else scaleFactor = (float)fontSize / spriteFont.charSet[0].h; + + glDisable(GL_LIGHTING); // When drawing text, disable LIGHTING + glEnable(GL_TEXTURE_2D); + + glBindTexture(GL_TEXTURE_2D, spriteFont.texture.glId); + + glPushMatrix(); + + // Optimized to use one draw call per string + glBegin(GL_QUADS); + for(int i = 0; i < length; i++) + { + c = spriteFont.charSet[(int)text[i] - FIRST_CHAR]; + + glColor4ub(tint.r, tint.g, tint.b, tint.a); + glNormal3f(0.0f, 0.0f, 1.0f); // Normal Pointing Towards Viewer + glTexCoord2f((float)c.x / spriteFont.texture.width, (float)c.y / spriteFont.texture.height); glVertex2f(positionX, position.y); + glTexCoord2f((float)c.x / spriteFont.texture.width, (float)(c.y + c.h) / spriteFont.texture.height); glVertex2f(positionX, position.y + (c.h) * scaleFactor); + glTexCoord2f((float)(c.x + c.w) / spriteFont.texture.width, (float)(c.y + c.h) / spriteFont.texture.height); glVertex2f(positionX + (c.w) * scaleFactor, position.y + (c.h) * scaleFactor); + glTexCoord2f((float)(c.x + c.w) / spriteFont.texture.width, (float)c.y / spriteFont.texture.height); glVertex2f(positionX + (c.w) * scaleFactor, position.y); + + positionX += (spriteFont.charSet[(int)text[i] - FIRST_CHAR].w + spacing) * scaleFactor; + } + glEnd(); + + glPopMatrix(); + + glDisable(GL_TEXTURE_2D); +} + +// Measure string width for default font +int MeasureText(const char *text, int fontSize, int spacing) +{ + Vector2 vec; + + vec = MeasureTextEx(defaultFont, text, fontSize, spacing); + + return (int)vec.x; +} + + +// Measure string size for SpriteFont +Vector2 MeasureTextEx(SpriteFont spriteFont, const char *text, int fontSize, int spacing) +{ + int len = strlen(text); + int textWidth = 0; + float scaleFactor; + + for (int i = 0; i < len; i++) + { + textWidth += spriteFont.charSet[(int)text[i] - FIRST_CHAR].w; + } + + textWidth += (int)((len - 1) * spacing); // Adds chars spacing to measure + + if (fontSize <= spriteFont.charSet[0].h) scaleFactor = 1.0f; + else scaleFactor = (float)fontSize / spriteFont.charSet[0].h; + + Vector2 vec; + vec.x = (float)textWidth * scaleFactor; + vec.y = (float)spriteFont.charSet[0].h * scaleFactor; + + return vec; +} + +// Returns the base size for a SpriteFont (chars height) +int GetFontBaseSize(SpriteFont spriteFont) +{ + return spriteFont.charSet[0].h; +} + +// 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 float fps; + static int counter = 0; + static int refreshRate = 0; + + char buffer[20]; + + if (counter < refreshRate) + { + sprintf(buffer, "%2.0f FPS", fps); + DrawText(buffer, posX, posY, 20, 1, LIME); + + counter++; + } + else + { + fps = GetFPS(); + refreshRate = fps; + sprintf(buffer, "%2.0f FPS", fps); + DrawText(buffer, posX, posY, 20, 1, LIME); + + counter = 0; + } +} + +//---------------------------------------------------------------------------------- +// Module specific Functions Definition +//---------------------------------------------------------------------------------- + +// Check if a pixel is magenta +static bool PixelIsMagenta(Color p) +{ + return ((p.r == 255) && (p.g == 0) && (p.b == 255) && (p.a == 255)); +} + +// Parse image pixel data to obtain character set measures +static int ParseImageData(Color *imgDataPixel, int imgWidth, int imgHeight, Character **charSet) +{ + int charSpacing = 0; + int lineSpacing = 0; + + int x = 0; + int y = 0; + + Character tempCharSet[MAX_FONTCHARS]; // We allocate a temporal array for charData, once we get the actual charNumber we copy data to a sized array. + + for(y = 0; y < imgHeight; y++) + { + for(x = 0; x < imgWidth; x++) + { + if (!PixelIsMagenta(imgDataPixel[y*imgWidth + x])) break; + } + if (!PixelIsMagenta(imgDataPixel[y*imgWidth + x])) break; + } + + charSpacing = x; + lineSpacing = y; + + int charHeight = 0; + int j = 0; + + while(!PixelIsMagenta(imgDataPixel[(lineSpacing + j)*imgWidth + charSpacing])) j++; + + charHeight = j; + + // Check array values to get characters: value, x, y, w, h + int index = 0; + int lineToRead = 0; + int xPosToRead = charSpacing; + + while((lineSpacing + lineToRead * (charHeight + lineSpacing)) < imgHeight) + { + while((xPosToRead < imgWidth) && + !PixelIsMagenta((imgDataPixel[(lineSpacing + (charHeight+lineSpacing)*lineToRead)*imgWidth + xPosToRead]))) + { + tempCharSet[index].value = FIRST_CHAR + index; + tempCharSet[index].x = xPosToRead; + tempCharSet[index].y = lineSpacing + lineToRead * (charHeight + lineSpacing); + tempCharSet[index].h = charHeight; + + int charWidth = 0; + + while(!PixelIsMagenta(imgDataPixel[(lineSpacing + (charHeight+lineSpacing)*lineToRead)*imgWidth + xPosToRead + charWidth])) charWidth++; + + tempCharSet[index].w = charWidth; + + index++; + + xPosToRead += (charWidth + charSpacing); + } + + lineToRead++; + xPosToRead = charSpacing; + } + + // We got tempCharSet populated with char data and the number of chars (index) + // Now we move temp data to real charSet (passed as parameter to the function) + (*charSet) = (Character *)malloc(index * sizeof(Character)); // BE CAREFUL! This memory should be freed! + + for (int i = 0; i < index; i++) (*charSet)[i] = tempCharSet[i]; + + return index; +} + +// Calculate next power-of-two value for a given num +static int GetNextPOT(int num) +{ + if (num != 0) + { + num--; + num |= (num >> 1); // Or first 2 bits + num |= (num >> 2); // Or next 2 bits + num |= (num >> 4); // Or next 4 bits + num |= (num >> 8); // Or next 8 bits + num |= (num >> 16); // Or next 16 bits + num++; + } + + return num; +} \ No newline at end of file diff --git a/src/textures.c b/src/textures.c new file mode 100644 index 000000000..1a7e23a72 --- /dev/null +++ b/src/textures.c @@ -0,0 +1,275 @@ +/********************************************************************************************* +* +* raylib.textures +* +* Basic functions to load and draw Textures (2d) +* +* Uses external lib: +* stb_image - Multiple formats image loading (JPEG, PNG, BMP, TGA, PSD, GIF, PIC) +* +* Copyright (c) 2013 Ramon Santamaria (Ray San - raysan@raysanweb.com) +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#include "raylib.h" + +#include // OpenGL functions +#include // Declares malloc() and free() for memory management +#include "stb_image.h" // Used to read image data (multiple formats support) + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +// Nop... + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +typedef unsigned char byte; + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +// It's lonely here... + +//---------------------------------------------------------------------------------- +// Module specific Functions Declaration +//---------------------------------------------------------------------------------- +// No private (static) functions in this module (.c file) + +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- + +// Load an image into CPU memory (RAM) +Image LoadImage(const char *fileName) +{ + Image image; + + int imgWidth; + int imgHeight; + int imgBpp; + + // NOTE: Using stb_image to load images (Supports: BMP, TGA, PNG, JPG, ...) + byte *imgData = stbi_load(fileName, &imgWidth, &imgHeight, &imgBpp, 4); // Force loading to 4 components (RGBA) + + // Convert array to pixel array for working convenience + image.pixels = (Color *)malloc(imgWidth * imgHeight * sizeof(Color)); + + int pix = 0; + + for (int i = 0; i < (imgWidth * imgHeight * 4); i += 4) + { + image.pixels[pix].r = imgData[i]; + image.pixels[pix].g = imgData[i+1]; + image.pixels[pix].b = imgData[i+2]; + image.pixels[pix].a = imgData[i+3]; + pix++; + } + + stbi_image_free(imgData); + + image.width = imgWidth; + image.height = imgHeight; + + // ALTERNATIVE: We can load pixel data directly into Color struct pixels array, + // to do that struct data alignment should be the right one (4 byte); it is. + //image.pixels = stbi_load(fileName, &imgWidth, &imgHeight, &imgBpp, 4); + + return image; +} + +// Unload image from CPU memory (RAM) +void UnloadImage(Image image) +{ + free(image.pixels); +} + +// Load an image as texture into GPU memory +Texture2D LoadTexture(const char *fileName) +{ + Texture2D texture; + + int imgWidth; + int imgHeight; + int imgBpp; + + // NOTE: Using stb_image to load images (Supports: BMP, TGA, PNG, JPG, ...) + byte *imgData = stbi_load(fileName, &imgWidth, &imgHeight, &imgBpp, 4); // Force loading to 4 components (RGBA) + + // Convert loaded data to OpenGL texture + //---------------------------------------- + GLuint id; + glGenTextures(1, &id); // Generate Pointer to the Texture + + glBindTexture(GL_TEXTURE_2D, id); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // Set texture to repead on x-axis + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // Set texture to repead on y-axis + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // Filter for pixel-perfect drawing, alternative: GL_LINEAR + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Filter for pixel-perfect drawing, alternative: GL_LINEAR + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, imgWidth, imgHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, imgData); + + // NOTE: Not using mipmappings (texture for 2D drawing) + // At this point we have the image converted to texture and uploaded to GPU + + stbi_image_free(imgData); // Now we can free loaded data from RAM memory + + texture.glId = id; + texture.width = imgWidth; + texture.height = imgHeight; + + return texture; +} + +// Load an image as texture (and convert to POT with mipmaps) +Texture2D LoadTextureEx(const char *fileName, bool createPOT, bool mipmaps) +{ + Texture2D texture; + + // TODO: Load and image and convert to Power-Of-Two + // NOTE: Conversion could be done just adding extra space to image or by scaling image + // NOTE: If scaling image, be careful with scaling algorithm (aproximation, bilinear, bicubic...) + + // TODO: Generate all required mipmap levels from image and convert to testure (not that easy) + // NOTE: If using OpenGL 1.1, the only option is doing mipmap generation on CPU side (i.e. gluBuild2DMipmaps) + // NOTE: raylib tries to minimize external dependencies so, we are not using GLU + // NOTE: Re-implement some function similar to gluBuild2DMipmaps (not that easy...) + + return texture; +} + +// Unload texture from GPU memory +void UnloadTexture(Texture2D texture) +{ + glDeleteTextures(1, &texture.glId); +} + +// Draw a Texture2D +void DrawTexture(Texture2D texture, int posX, int posY, Color tint) +{ + DrawTextureEx(texture, (Vector2){ (float)posX, (float)posY}, 0, 1.0f, tint); +} + +// Draw a Texture2D with extended parameters +void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float scale, Color tint) +{ + glEnable(GL_TEXTURE_2D); // Enable textures usage + + glBindTexture(GL_TEXTURE_2D, texture.glId); + + glPushMatrix(); + // NOTE: Rotation is applied before translation and scaling, even being called in inverse order... + // NOTE: Rotation point is upper-left corner + glTranslatef(position.x, position.y, 0); + glScalef(scale, scale, 1.0f); + glRotatef(rotation, 0, 0, 1); + + glBegin(GL_QUADS); + glColor4ub(tint.r, tint.g, tint.b, tint.a); + glNormal3f(0.0f, 0.0f, 1.0f); // Normal vector pointing towards viewer + glTexCoord2f(0.0f, 0.0f); glVertex2f(0.0f, 0.0f); // Bottom-left corner for texture and quad + glTexCoord2f(1.0f, 0.0f); glVertex2f(texture.width, 0.0f); // Bottom-right corner for texture and quad + glTexCoord2f(1.0f, 1.0f); glVertex2f(texture.width, texture.height); // Top-right corner for texture and quad + glTexCoord2f(0.0f, 1.0f); glVertex2f(0.0f, texture.height); // Top-left corner for texture and quad + glEnd(); + glPopMatrix(); + + glDisable(GL_TEXTURE_2D); // Disable textures usage +} + +// Draw a part of a texture (defined by a rectangle) +void DrawTextureRec(Texture2D texture, Rectangle sourceRec, Vector2 position, float scale, Color tint) +{ + glEnable(GL_TEXTURE_2D); // Enable textures usage + + glBindTexture(GL_TEXTURE_2D, texture.glId); + + glPushMatrix(); + glTranslatef(position.x, position.y, 0); + glScalef(scale, scale, 1.0f); + //glRotatef(rotation, 0, 0, 1); + + glBegin(GL_QUADS); + glColor4ub(tint.r, tint.g, tint.b, tint.a); + glNormal3f(0.0f, 0.0f, 1.0f); // Normal vector pointing towards viewer + + // Bottom-left corner for texture and quad + glTexCoord2f((float)sourceRec.x / texture.width, (float)sourceRec.y / texture.height); + glVertex2f(0.0f, 0.0f); + + // Bottom-right corner for texture and quad + glTexCoord2f((float)(sourceRec.x + sourceRec.width) / texture.width, (float)sourceRec.y / texture.height); + glVertex2f(sourceRec.width, 0.0f); + + // Top-right corner for texture and quad + glTexCoord2f((float)(sourceRec.x + sourceRec.width) / texture.width, (float)(sourceRec.y + sourceRec.height) / texture.height); + glVertex2f(sourceRec.width, sourceRec.height); + + // Top-left corner for texture and quad + glTexCoord2f((float)sourceRec.x / texture.width, (float)(sourceRec.y + sourceRec.height) / texture.height); + glVertex2f(0.0f, sourceRec.height); + glEnd(); + glPopMatrix(); + + glDisable(GL_TEXTURE_2D); // Disable textures usage +} + +// Creates a bitmap (BMP) file from an array of pixel data +// NOTE: This function is only used by module [core], not explicitly available to raylib users +extern void WriteBitmap(const char *fileName, const Color *imgDataPixel, int width, int height) +{ + int filesize = 54 + 3*width*height; + + unsigned char bmpFileHeader[14] = {'B','M', 0,0,0,0, 0,0, 0,0, 54,0,0,0}; // Standard BMP file header + unsigned char bmpInfoHeader[40] = {40,0,0,0, 0,0,0,0, 0,0,0,0, 1,0, 24,0}; // Standard BMP info header + + bmpFileHeader[2] = (unsigned char)(filesize); + bmpFileHeader[3] = (unsigned char)(filesize>>8); + bmpFileHeader[4] = (unsigned char)(filesize>>16); + bmpFileHeader[5] = (unsigned char)(filesize>>24); + + bmpInfoHeader[4] = (unsigned char)(width); + bmpInfoHeader[5] = (unsigned char)(width>>8); + bmpInfoHeader[6] = (unsigned char)(width>>16); + bmpInfoHeader[7] = (unsigned char)(width>>24); + bmpInfoHeader[8] = (unsigned char)(height); + bmpInfoHeader[9] = (unsigned char)(height>>8); + bmpInfoHeader[10] = (unsigned char)(height>>16); + bmpInfoHeader[11] = (unsigned char)(height>>24); + + FILE *bmpFile = fopen(fileName, "wb"); // Define a pointer to bitmap file and open it in write-binary mode + + // NOTE: fwrite parameters are: data pointer, size in bytes of each element to be written, number of elements, file-to-write pointer + fwrite(bmpFileHeader, sizeof(unsigned char), 14, bmpFile); // Write BMP file header data + fwrite(bmpInfoHeader, sizeof(unsigned char), 40, bmpFile); // Write BMP info header data + + // Write pixel data to file + for (int y = 0; y < height ; y++) + { + for (int x = 0; x < width; x++) + { + fputc(imgDataPixel[x + y*width].b, bmpFile); + fputc(imgDataPixel[x + y*width].g, bmpFile); + fputc(imgDataPixel[x + y*width].r, bmpFile); + } + } + + fclose(bmpFile); // Close bitmap file +} \ No newline at end of file diff --git a/src/vector3.c b/src/vector3.c new file mode 100644 index 000000000..8752f4d09 --- /dev/null +++ b/src/vector3.c @@ -0,0 +1,140 @@ +/********************************************************************************************* +* +* raylib.vector3 +* +* Vector3 Functions Definition +* +* Copyright (c) 2013 Ramon Santamaria (Ray San - raysan@raysanweb.com) +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#include "vector3.h" + +#include + +// Add two vectors +Vector3 VectorAdd(Vector3 v1, Vector3 v2) +{ + Vector3 out; + + out.x = v1.x + v2.x; + out.y = v1.y + v2.y; + out.z = v1.z + v2.z; + + return out; +} + +// Substract two vectors +Vector3 VectorSubtract(Vector3 v1, Vector3 v2) +{ + Vector3 out; + + out.x = v1.x - v2.x; + out.y = v1.y - v2.y; + out.z = v1.z - v2.z; + + return out; +} + +// Calculate two vectors cross product +Vector3 VectorCrossProduct(Vector3 v1, Vector3 v2) +{ + Vector3 cross; + + cross.x = v1.y*v2.z - v1.z*v2.y; + cross.y = v1.z*v2.x - v1.x*v2.z; + cross.z = v1.x*v2.y - v1.y*v2.x; + + return cross; +} + +// Calculate one vector perpendicular vector +Vector3 VectorPerpendicular(Vector3 v) +{ + Vector3 out; + + float min = fabs(v.x); + Vector3 cardinalAxis = {1.0, 0.0, 0.0}; + + if (fabs(v.y) < min) + { + min = fabs(v.y); + cardinalAxis = (Vector3){0.0, 1.0, 0.0}; + } + + if(fabs(v.z) < min) + { + cardinalAxis = (Vector3){0.0, 0.0, 1.0}; + } + + out = VectorCrossProduct(v, cardinalAxis); + + return out; +} + +// Calculate two vectors dot product +float VectorDotProduct(Vector3 v1, Vector3 v2) +{ + float dot; + + dot = v1.x*v2.x + v1.y*v2.y + v1.z*v2.z; + + return dot; +} + +// Calculate vector lenght +float VectorLength(const Vector3 v) +{ + float length; + + length = sqrt(v.x*v.x + v.y*v.y + v.z*v.z); + + return length; +} + +// Scale provided vector +void VectorScale(Vector3 *v, float scale) +{ + v->x *= scale; + v->y *= scale; + v->z *= scale; +} + +// Invert provided vector (direction) +void VectorInverse(Vector3 *v) +{ + v->x = -v->x; + v->y = -v->y; + v->z = -v->z; +} + +// Normalize provided vector +void VectorNormalize(Vector3 *v) +{ + float length, ilength; + + length = VectorLength(*v); + + if (length == 0) length = 1; + + ilength = 1.0/length; + + v->x *= ilength; + v->y *= ilength; + v->z *= ilength; +} diff --git a/src/vector3.h b/src/vector3.h new file mode 100644 index 000000000..3973f4798 --- /dev/null +++ b/src/vector3.h @@ -0,0 +1,57 @@ +/********************************************************************************************* +* +* raylib.vector3 +* +* Some useful functions to work with Vector3 +* +* Copyright (c) 2013 Ramon Santamaria (Ray San - raysan@raysanweb.com) +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#ifndef VECTOR3_H +#define VECTOR3_H + +#include "raylib.h" // Defines Vector3 structure + +#ifdef __cplusplus +extern "C" { // Prevents name mangling of functions +#endif + +//------------------------------------------------------------------------------------ +// Global Variables Definition +//------------------------------------------------------------------------------------ +// It's lonely here... + +//------------------------------------------------------------------------------------ +// Functions Declaration to work with Vector3 +//------------------------------------------------------------------------------------ +Vector3 VectorAdd(Vector3 v1, Vector3 v2); // Add two vectors +Vector3 VectorSubtract(Vector3 v1, Vector3 v2); // Substract two vectors +Vector3 VectorCrossProduct(Vector3 v1, Vector3 v2); // Calculate two vectors cross product +Vector3 VectorPerpendicular(Vector3 v); // Calculate one vector perpendicular vector +float VectorDotProduct(Vector3 v1, Vector3 v2); // Calculate two vectors dot product +float VectorLength(const Vector3 v); // Calculate vector lenght +void VectorScale(Vector3 *v, float scale); // Scale provided vector +void VectorInverse(Vector3 *v); // Invert provided vector (direction) +void VectorNormalize(Vector3 *v); // Normalize provided vector + +#ifdef __cplusplus +} +#endif + +#endif // VECTOR3_H \ No newline at end of file